mirror of
https://github.com/FliegendeWurst/KIT-ILIAS-downloader.git
synced 2024-08-28 04:04:18 +00:00
Support download of other personal desktop pages
This commit is contained in:
parent
9da1a9d2d7
commit
a003d27442
@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||||||
and this project loosely adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project loosely adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- `--sync-url https://ilias.studium.kit.edu/ilias.php?baseClass=ilPersonalDesktopGUI&cmd=jumpToMemberships` to download all courses (unless specified otherwise in the `.iliasignore`)
|
||||||
|
|
||||||
## [0.2.21] - 2021-05-18
|
## [0.2.21] - 2021-05-18
|
||||||
### Fixed
|
### Fixed
|
||||||
|
36
src/ilias.rs
36
src/ilias.rs
@ -167,12 +167,15 @@ impl ILIAS {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns subfolders and the main text on the course page.
|
/// Returns subfolders and the main text in a course/folder/personal desktop.
|
||||||
pub async fn get_course_content(&self, url: &URL) -> Result<(Vec<Result<Object>>, Option<String>)> {
|
pub async fn get_course_content(&self, url: &URL) -> Result<(Vec<Result<Object>>, Option<String>)> {
|
||||||
let html = self.get_html(&url.url).await?;
|
let html = self.get_html(&url.url).await?;
|
||||||
|
|
||||||
let main_text = if let Some(el) = html.select(&il_content_container).next() {
|
let main_text = if let Some(el) = html.select(&il_content_container).next() {
|
||||||
if !el.children().flat_map(|x| x.value().as_element()).next().map(|x|
|
|
||||||
x.attr("class").unwrap_or_default().contains("ilContainerBlock")).unwrap_or(false)
|
if !el.children().flat_map(|x| x.value().as_element()).next()
|
||||||
|
.map(|x| x.attr("class").unwrap_or_default()
|
||||||
|
.contains("ilContainerBlock")).unwrap_or(false)
|
||||||
&& el.inner_html().len() > 40 {
|
&& el.inner_html().len() > 40 {
|
||||||
// ^ minimum length of useful content?
|
// ^ minimum length of useful content?
|
||||||
Some(el.inner_html())
|
Some(el.inner_html())
|
||||||
@ -186,15 +189,6 @@ impl ILIAS {
|
|||||||
Ok((ILIAS::get_items(&html), main_text))
|
Ok((ILIAS::get_items(&html), main_text))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn personal_desktop(&self) -> Result<Dashboard> {
|
|
||||||
let html = self.get_html("https://ilias.studium.kit.edu/ilias.php?baseClass=ilPersonalDesktopGUI&cmd=jumpToSelectedItems").await?;
|
|
||||||
let items = ILIAS::get_items(&html)
|
|
||||||
.into_iter()
|
|
||||||
.flat_map(Result::ok)
|
|
||||||
.collect();
|
|
||||||
Ok(Dashboard { items })
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_course_content_tree(&self, ref_id: &str, cmd_node: &str) -> Result<Vec<Object>> {
|
pub async fn get_course_content_tree(&self, ref_id: &str, cmd_node: &str) -> Result<Vec<Object>> {
|
||||||
// TODO: this magically does not return sub-folders
|
// TODO: this magically does not return sub-folders
|
||||||
// opening the same url in browser does show sub-folders?!
|
// opening the same url in browser does show sub-folders?!
|
||||||
@ -213,15 +207,11 @@ impl ILIAS {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Dashboard {
|
|
||||||
pub items: Vec<Object>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Object {
|
pub enum Object {
|
||||||
Course { name: String, url: URL },
|
Course { name: String, url: URL },
|
||||||
Folder { name: String, url: URL },
|
Folder { name: String, url: URL },
|
||||||
|
PersonalDesktop { url: URL },
|
||||||
File { name: String, url: URL },
|
File { name: String, url: URL },
|
||||||
Forum { name: String, url: URL },
|
Forum { name: String, url: URL },
|
||||||
Thread { url: URL },
|
Thread { url: URL },
|
||||||
@ -253,6 +243,7 @@ impl Object {
|
|||||||
| Generic { name, .. } => &name,
|
| Generic { name, .. } => &name,
|
||||||
Thread { url } => &url.thr_pk.as_ref().unwrap(),
|
Thread { url } => &url.thr_pk.as_ref().unwrap(),
|
||||||
Video { url } => &url.url,
|
Video { url } => &url.url,
|
||||||
|
PersonalDesktop { url } => url.cmd.as_ref().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,6 +251,7 @@ impl Object {
|
|||||||
match self {
|
match self {
|
||||||
Course { url, .. }
|
Course { url, .. }
|
||||||
| Folder { url, .. }
|
| Folder { url, .. }
|
||||||
|
| PersonalDesktop { url }
|
||||||
| File { url, .. }
|
| File { url, .. }
|
||||||
| Forum { url, .. }
|
| Forum { url, .. }
|
||||||
| Thread { url }
|
| Thread { url }
|
||||||
@ -278,6 +270,7 @@ impl Object {
|
|||||||
match self {
|
match self {
|
||||||
Course { .. } => "course",
|
Course { .. } => "course",
|
||||||
Folder { .. } => "folder",
|
Folder { .. } => "folder",
|
||||||
|
PersonalDesktop { .. } => "personal desktop",
|
||||||
File { .. } => "file",
|
File { .. } => "file",
|
||||||
Forum { .. } => "forum",
|
Forum { .. } => "forum",
|
||||||
Thread { .. } => "thread",
|
Thread { .. } => "thread",
|
||||||
@ -293,16 +286,16 @@ impl Object {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_dir(&self) -> bool {
|
pub fn is_dir(&self) -> bool {
|
||||||
match self {
|
matches!(self,
|
||||||
Course { .. }
|
Course { .. }
|
||||||
| Folder { .. }
|
| Folder { .. }
|
||||||
|
| PersonalDesktop { .. }
|
||||||
| Forum { .. }
|
| Forum { .. }
|
||||||
| Thread { .. }
|
| Thread { .. }
|
||||||
| Wiki { .. }
|
| Wiki { .. }
|
||||||
| ExerciseHandler { .. }
|
| ExerciseHandler { .. }
|
||||||
| PluginDispatch { .. } => true,
|
| PluginDispatch { .. }
|
||||||
_ => false,
|
)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_link(item: ElementRef, link: ElementRef) -> Result<Self> {
|
pub fn from_link(item: ElementRef, link: ElementRef) -> Result<Self> {
|
||||||
@ -399,6 +392,7 @@ impl Object {
|
|||||||
None => Course { name, url },
|
None => Course { name, url },
|
||||||
},
|
},
|
||||||
"ilobjplugindispatchgui" => PluginDispatch { name, url },
|
"ilobjplugindispatchgui" => PluginDispatch { name, url },
|
||||||
|
"ilpersonaldesktopgui" => PersonalDesktop { url },
|
||||||
_ => Generic { name, url },
|
_ => Generic { name, url },
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
31
src/main.rs
31
src/main.rs
@ -120,23 +120,19 @@ async fn real_main(mut opt: Opt) -> Result<()> {
|
|||||||
if PROGRESS_BAR_ENABLED.load(Ordering::SeqCst) {
|
if PROGRESS_BAR_ENABLED.load(Ordering::SeqCst) {
|
||||||
PROGRESS_BAR.set_draw_target(ProgressDrawTarget::stderr_nohz());
|
PROGRESS_BAR.set_draw_target(ProgressDrawTarget::stderr_nohz());
|
||||||
PROGRESS_BAR.set_style(ProgressStyle::default_bar().template("[{pos}/{len}+] {wide_msg}"));
|
PROGRESS_BAR.set_style(ProgressStyle::default_bar().template("[{pos}/{len}+] {wide_msg}"));
|
||||||
PROGRESS_BAR.inc_length(1);
|
|
||||||
PROGRESS_BAR.set_message("initializing..");
|
PROGRESS_BAR.set_message("initializing..");
|
||||||
}
|
}
|
||||||
if let Some(url) = ilias.opt.sync_url.as_ref() {
|
|
||||||
// TODO: this should be unified with the download logic below
|
// default sync URL: main personal desktop
|
||||||
let obj = Object::from_url(URL::from_href(url).context("invalid sync URL")?, "Sync URL".to_owned(), None).context("invalid sync object")?; // name can be empty for first element
|
let sync_url = ilias.opt.sync_url.clone().unwrap_or_else(|| format!("{}ilias.php?baseClass=ilPersonalDesktopGUI&cmd=jumpToSelectedItems", ILIAS_URL));
|
||||||
|
let obj = Object::from_url(URL::from_href(&sync_url).context("invalid sync URL")?, String::new(), None).context("invalid sync object")?; // name can be empty for first element
|
||||||
spawn!(process_gracefully(ilias.clone(), ilias.opt.output.clone(), obj));
|
spawn!(process_gracefully(ilias.clone(), ilias.opt.output.clone(), obj));
|
||||||
} else {
|
|
||||||
let desktop = ilias.personal_desktop().await.context("Failed to load personal desktop")?;
|
|
||||||
for item in desktop.items {
|
|
||||||
let path = ilias.opt.output.join(file_escape(item.name()));
|
|
||||||
tx.unbounded_send(task::spawn(process_gracefully(ilias.clone(), path, item))).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while let Either::Left((task, _)) = future::select(rx.next(), future::ready(())).await {
|
while let Either::Left((task, _)) = future::select(rx.next(), future::ready(())).await {
|
||||||
if let Some(task) = task {
|
if let Some(task) = task {
|
||||||
let _ = task.await;
|
if let Err(e) = task.await {
|
||||||
|
error!(e)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -149,7 +145,6 @@ async fn real_main(mut opt: Opt) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if PROGRESS_BAR_ENABLED.load(Ordering::SeqCst) {
|
if PROGRESS_BAR_ENABLED.load(Ordering::SeqCst) {
|
||||||
PROGRESS_BAR.inc(1);
|
|
||||||
PROGRESS_BAR.set_style(ProgressStyle::default_bar().template("[{pos}/{len}] {wide_msg}"));
|
PROGRESS_BAR.set_style(ProgressStyle::default_bar().template("[{pos}/{len}] {wide_msg}"));
|
||||||
PROGRESS_BAR.finish_with_message("done");
|
PROGRESS_BAR.finish_with_message("done");
|
||||||
}
|
}
|
||||||
@ -220,9 +215,13 @@ async fn process(ilias: Arc<ILIAS>, path: PathBuf, obj: Object) -> Result<()> {
|
|||||||
let relative_path = path.strip_prefix(&ilias.opt.output).unwrap();
|
let relative_path = path.strip_prefix(&ilias.opt.output).unwrap();
|
||||||
if PROGRESS_BAR_ENABLED.load(Ordering::SeqCst) {
|
if PROGRESS_BAR_ENABLED.load(Ordering::SeqCst) {
|
||||||
PROGRESS_BAR.inc(1);
|
PROGRESS_BAR.inc(1);
|
||||||
PROGRESS_BAR.set_message(relative_path.display().to_string());
|
let path = relative_path.display().to_string();
|
||||||
|
if !path.is_empty() {
|
||||||
|
PROGRESS_BAR.set_message(path);
|
||||||
}
|
}
|
||||||
if ilias.ignore.matched(relative_path, obj.is_dir()).is_ignore() {
|
}
|
||||||
|
// root path should not be matched
|
||||||
|
if relative_path.parent().is_some() && ilias.ignore.matched(relative_path, obj.is_dir()).is_ignore() {
|
||||||
log!(1, "Ignored {}", relative_path.to_string_lossy());
|
log!(1, "Ignored {}", relative_path.to_string_lossy());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -263,7 +262,7 @@ async fn process(ilias: Arc<ILIAS>, path: PathBuf, obj: Object) -> Result<()> {
|
|||||||
spawn!(process_gracefully(ilias, path, item));
|
spawn!(process_gracefully(ilias, path, item));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Folder { url, .. } => {
|
Folder { url, .. } | PersonalDesktop { url } => {
|
||||||
let content = ilias.get_course_content(&url).await?;
|
let content = ilias.get_course_content(&url).await?;
|
||||||
if let Some(s) = content.1.as_ref() {
|
if let Some(s) = content.1.as_ref() {
|
||||||
let path = path.join("folder.html");
|
let path = path.join("folder.html");
|
||||||
|
Loading…
Reference in New Issue
Block a user