diff --git a/src/main.rs b/src/main.rs index 593b785..509786e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -26,8 +26,6 @@ use presentation::Presentation; // TODO: Old "Publish To Go" packages get sweeped from the server and you have to request a new one; // Implement this // TODO: sometimes you need to access the video listing at least once using moodle; emulate this -// TODO: Add aliases with semester postfix so other can contribute aliases; move to separate file -// split into files in general? const MAX_RETRIES: u8 = 10; @@ -54,26 +52,36 @@ fn main() { let matches = App::new("TumMediasiteDownloader") .author("Boris-Chengbiao Zhou ") .about("Downloads \'catalogs\' from the TUM's Mediasite lecture archive.") - .arg(Arg::with_name("CATALOG_NAME") - .help("name of the catalog e.g. from the URL:\n\ + .arg( + Arg::with_name("CATALOG_NAME") + .help( + "name of the catalog e.g. from the URL:\n\ https://streams.tum.de/Mediasite/Catalog/catalogs/era-2016 -> era-2016\n\ - special cases (WS16/17; login included): DS, EIDI, ERA") - .required(true) - .index(1)) - .arg(Arg::with_name("OUTPUT_DIRECTORY") - .help("where to output the downloaded files") - .required(true) - .index(2)) - .arg(Arg::with_name("username") - .short("u") - .help("username for login; can be omitted if the user from .env should be used",) - .requires("password") - .takes_value(true)) - .arg(Arg::with_name("password") - .short("p") - .help("password for login; can be omitted if the user from .env should be used",) - .requires("username") - .takes_value(true)) + special cases (WS16/17; login included): DS, EIDI, ERA, ..." + ) + .required(true) + .index(1) + ) + .arg( + Arg::with_name("OUTPUT_DIRECTORY") + .help("where to output the downloaded files") + .required(true) + .index(2) + ) + .arg( + Arg::with_name("username") + .short("u") + .help("username for login; can be omitted if the user from .env should be used") + .requires("password") + .takes_value(true) + ) + .arg( + Arg::with_name("password") + .short("p") + .help("password for login; can be omitted if the user from .env should be used") + .requires("username") + .takes_value(true) + ) .get_matches(); let catalog_name = matches.value_of("CATALOG_NAME").unwrap(); @@ -128,12 +136,14 @@ fn get_auth() { form_data.insert("UserName", &*username); form_data.insert("Password", &*password); - let res = try_to_get_valid_response(|client| { - client - .post("https://streams.tum.de/Mediasite/Login") - .form(&form_data) - }, - |res| res.headers().get::().is_some()); + let res = try_to_get_valid_response( + |client| { + client + .post("https://streams.tum.de/Mediasite/Login") + .form(&form_data) + }, + |res| res.headers().get::().is_some(), + ); // FIXME: We're somehow only getting "302 Object moved" instead of the actual response // => We can't determine if the login was successful // (we still get a MediasiteAuth cookie that is useless) @@ -156,14 +166,18 @@ fn download_catalog(catalog_name: &str, out_dir: &Path) { let catalog_id = get_catalog_id(catalog_name); let json = get_json(&catalog_id); let presentations = json_to_presentations(&json); - println!("Starting to download {} presentations!", - presentations.len()); + println!( + "Starting to download {} presentations!", + presentations.len() + ); for (i, presentation) in presentations.iter().enumerate() { - println!("\nDownloading {}/{}: {}", - i + 1, - presentations.len(), - presentation.name()); + println!( + "\nDownloading {}/{}: {}", + i + 1, + presentations.len(), + presentation.name() + ); for _ in 0..MAX_RETRIES { match presentation.download(out_dir) { Ok(()) => break, @@ -189,8 +203,10 @@ fn get_catalog_id(name: &str) -> String { let prefix = "CatalogId: '"; let idx = body.find(prefix) - .expect("Failed to find CatalogId on the catalog page! Perhaps you got the wrong catalog \ - name or an invalid login? Maybe you need to open the page in a browser once..."); + .expect( + "Failed to find CatalogId on the catalog page! Perhaps you got the wrong catalog \ + name or an invalid login? Maybe you need to open the page in a browser once..." + ); let pre_len = prefix.len(); // Assuming all catalog ids follow this pattern! let len = "a6fca0c1-0be4-4e66-83b7-bcdc4eb5e95e".len(); @@ -218,12 +234,14 @@ fn get_json(catalog_id: &str) -> String { data.insert("CurrentFolderId", catalog_id); data.insert("ItemsPerPage", "500"); - let mut res = try_to_get_response(|client| { - client - .post("https://streams.tum.de/Mediasite/Catalog/Data/GetPresentationsForFolder") - .header(construct_cookie()) - .json(&data) - }); + let mut res = try_to_get_response( + |client| { + client + .post("https://streams.tum.de/Mediasite/Catalog/Data/GetPresentationsForFolder") + .header(construct_cookie()) + .json(&data) + } + ); read_response_body(&mut res) } @@ -232,14 +250,16 @@ fn construct_cookie() -> Cookie { } fn try_to_get_response(f: F) -> Response - where F: Fn(&Client) -> RequestBuilder +where + F: Fn(&Client) -> RequestBuilder, { try_to_get_valid_response(f, |_| true) } fn try_to_get_valid_response(f1: F1, f2: F2) -> Response - where F1: Fn(&Client) -> RequestBuilder, - F2: Fn(&Response) -> bool +where + F1: Fn(&Client) -> RequestBuilder, + F2: Fn(&Response) -> bool, { for retries in 0..MAX_RETRIES { if retries > 0 { diff --git a/src/presentation.rs b/src/presentation.rs index 11cfb81..79cb874 100644 --- a/src/presentation.rs +++ b/src/presentation.rs @@ -32,10 +32,11 @@ impl Presentation { } pub(crate) fn download(&self, out_dir: &Path) -> Result<(), DownloadError> { - fn download_to_file_and_check bool>(response: &mut Response, - out_file: &Path, - check: F) - -> Result<(), io::Error> { + fn download_to_file_and_check bool>( + response: &mut Response, + out_file: &Path, + check: F, + ) -> Result<(), io::Error> { println!("Output file: {}", out_file.display()); if out_file.exists() { println!("File is present already. Checking validity..."); @@ -56,14 +57,10 @@ impl Presentation { match *self { Presentation::PublishToGo { ref download_url, .. } => { - let mut response = try_to_get_valid_response(|client| { - client - .get(download_url) - .header(construct_cookie()) - }, - |resp| { - resp.headers().get::().is_some() - }); + let mut response = try_to_get_valid_response( + |client| client.get(download_url).header(construct_cookie()), + |resp| resp.headers().get::().is_some(), + ); let filename = { let content_disposition = @@ -74,8 +71,10 @@ impl Presentation { match *param { DispositionParam::Filename(ref charset, _, ref vec) => { assert_eq!(&Charset::Ext("UTF-8".to_string()), charset); - name = Some(String::from_utf8(vec.to_vec()) - .expect("Suggested filename isn't valid UTF-8!",)); + name = Some( + String::from_utf8(vec.to_vec()) + .expect("Suggested filename isn't valid UTF-8!") + ); } _ => continue, } @@ -95,22 +94,26 @@ impl Presentation { .. } => { let videos = { - let payload = format!("{{\"getPlayerOptionsRequest\":{{\ + let payload = format!( + "{{\"getPlayerOptionsRequest\":{{\ \"ResourceId\":\"{}\",\ \"QueryString\":\"{}\"\ }}}}", - resource_id, - query_string); + resource_id, + query_string + ); let url = "https://streams.tum.de/Mediasite/PlayerService/\ PlayerService.svc/json/GetPlayerOptions"; - let mut res = try_to_get_response(|client| { - client - .post(url) - .header(construct_cookie()) - .header(ContentType::json()) - .body(&*payload) - }); + let mut res = try_to_get_response( + |client| { + client + .post(url) + .header(construct_cookie()) + .header(ContentType::json()) + .body(&*payload) + } + ); let json_text = read_response_body(&mut res); if json_text.contains("You are not authorized to view the requested content") { @@ -127,13 +130,11 @@ impl Presentation { vec }; for (i, download_url) in videos.iter().enumerate() { - let mut response = try_to_get_valid_response(|client| { - client.get(download_url).header(construct_cookie()) - }, - |resp| { - resp.status() == - &StatusCode::Ok - }); + let mut response = + try_to_get_valid_response( + |client| client.get(download_url).header(construct_cookie()), + |resp| resp.status() == &StatusCode::Ok, + ); // TODO: Support formats besides mp4 // extract extension from url or somewehre else... let filename = format!("{} - {}.mp4", fix_filename(self.name()), i + 1); @@ -211,9 +212,11 @@ fn download_to_file(response: &mut Response, path: &Path) -> Result<(), io::Erro let now = Instant::now(); if now - last > Duration::from_millis(update_duration_millis) { - print!("{:.*} kB/s \r", - 2, - done as f64 * 1000.0 / update_duration_millis as f64 / 1024.0); + print!( + "{:.*} kB/s \r", + 2, + done as f64 * 1000.0 / update_duration_millis as f64 / 1024.0 + ); ::std::io::stdout() .flush() .expect("Failed flushing the terminal!"); @@ -261,9 +264,11 @@ fn check_video(path: &Path) -> bool { match command { Ok(output) => output.stderr.is_empty(), Err(_) => { - println!("Failed to check {} with ffmpeg. Perhaps ffmpeg isn't in your path. \ + println!( + "Failed to check {} with ffmpeg. Perhaps ffmpeg isn't in your path. \ Skipping check...", - path.display()); + path.display() + ); true } }