diff --git a/src/client/player.rs b/src/client/player.rs index a9358f7..8ea30cd 100644 --- a/src/client/player.rs +++ b/src/client/player.rs @@ -376,35 +376,26 @@ fn map_url( deobf: &Deobfuscator, last_nsig: &mut [String; 2], ) -> MapResult> { - let (url_base, mut url_params) = match url { - Some(url) => ok_or_bail!( - util::url_to_params(url), - MapResult { - c: None, - warnings: vec![format!("Could not parse url `{url}`")] - } - ), + let x = match url { + Some(url) => util::url_to_params(url).map_err(|_| format!("Could not parse url `{url}`")), None => match signature_cipher { - Some(signature_cipher) => match cipher_to_url_params(signature_cipher, deobf) { - Ok(res) => res, - Err(e) => { - return MapResult { - c: None, - warnings: vec![format!( - "Could not deobfuscate signatureCipher `{signature_cipher}`: {e}" - )], - }; - } - }, - None => { - return MapResult { - c: None, - warnings: vec!["stream contained neither url nor cipher".to_owned()], - } - } + Some(signature_cipher) => cipher_to_url_params(signature_cipher, deobf).map_err(|e| { + format!("Could not deobfuscate signatureCipher `{signature_cipher}`: {e}") + }), + None => Err("stream contained neither url or cipher".to_owned()), }, }; + let (url_base, mut url_params) = match x { + Ok(x) => x, + Err(e) => { + return MapResult { + c: None, + warnings: vec![e], + } + } + }; + let mut warnings = vec![]; let mut throttled = false; deobf_nsig(&mut url_params, deobf, last_nsig).unwrap_or_else(|e| { @@ -414,21 +405,17 @@ fn map_url( throttled = true; }); - MapResult { - c: Some(( - ok_or_bail!( - Url::parse_with_params(url_base.as_str(), url_params.iter()), - MapResult { - c: None, - warnings: vec![format!( - "url could not be joined. url: `{url_base}` params: {url_params:?}" - )], - } - ) - .to_string(), - throttled, - )), - warnings, + match Url::parse_with_params(url_base.as_str(), url_params.iter()) { + Ok(url) => MapResult { + c: Some((url.to_string(), throttled)), + warnings, + }, + Err(_) => MapResult { + c: None, + warnings: vec![format!( + "url could not be joined. url: `{url_base}` params: {url_params:?}" + )], + }, } } @@ -437,16 +424,27 @@ fn map_video_stream( deobf: &Deobfuscator, last_nsig: &mut [String; 2], ) -> MapResult> { - let (mtype, codecs) = some_or_bail!( - parse_mime(&f.mime_type), - MapResult { - c: None, - warnings: vec![format!( - "Invalid mime type `{}` in video format {:?}", - &f.mime_type, &f - )] + let (mtype, codecs) = match parse_mime(&f.mime_type) { + Some(x) => x, + None => { + return MapResult { + c: None, + warnings: vec![format!( + "Invalid mime type `{}` in video format {:?}", + &f.mime_type, &f + )], + } } - ); + }; + let format = match get_video_format(mtype) { + Some(f) => f, + None => { + return MapResult { + c: None, + warnings: vec![format!("invalid video format. itag: {}", f.itag)], + } + } + }; let map_res = map_url(&f.url, &f.signature_cipher, deobf, last_nsig); match map_res.c { @@ -469,13 +467,7 @@ fn map_video_stream( hdr: f.color_info.unwrap_or_default().primaries == player::Primaries::ColorPrimariesBt2020, mime: f.mime_type.to_owned(), - format: some_or_bail!( - get_video_format(mtype), - MapResult { - c: None, - warnings: vec![format!("invalid video format. itag: {}", f.itag)] - } - ), + format, codec: get_video_codec(codecs), throttled, }), @@ -495,16 +487,27 @@ fn map_audio_stream( ) -> MapResult> { static LANG_PATTERN: Lazy = Lazy::new(|| Regex::new(r#"^([a-z]{2,3})\."#).unwrap()); - let (mtype, codecs) = some_or_bail!( - parse_mime(&f.mime_type), - MapResult { - c: None, - warnings: vec![format!( - "Invalid mime type `{}` in video format {:?}", - &f.mime_type, &f - )] + let (mtype, codecs) = match parse_mime(&f.mime_type) { + Some(x) => x, + None => { + return MapResult { + c: None, + warnings: vec![format!( + "Invalid mime type `{}` in video format {:?}", + &f.mime_type, &f + )], + } } - ); + }; + let format = match get_audio_format(mtype) { + Some(f) => f, + None => { + return MapResult { + c: None, + warnings: vec![format!("invalid audio format. itag: {}", f.itag)], + } + } + }; let map_res = map_url(&f.url, &f.signature_cipher, deobf, last_nsig); match map_res.c { @@ -519,13 +522,7 @@ fn map_audio_stream( init_range: f.init_range, duration_ms: f.approx_duration_ms, mime: f.mime_type.to_owned(), - format: some_or_bail!( - get_audio_format(mtype), - MapResult { - c: None, - warnings: vec![format!("invalid audio format. itag: {}", f.itag)] - } - ), + format, codec: get_audio_codec(codecs), channels: f.audio_channels, loudness_db: f.loudness_db, @@ -559,7 +556,7 @@ fn parse_mime(mime: &str) -> Option<(&str, Vec<&str>)> { static PATTERN: Lazy = Lazy::new(|| Regex::new(r#"(\w+/\w+);\scodecs="([a-zA-Z-0-9.,\s]*)""#).unwrap()); - let captures = some_or_bail!(PATTERN.captures(mime), None); + let captures = PATTERN.captures(mime)?; Some(( captures.get(1).unwrap().as_str(), captures diff --git a/src/lib.rs b/src/lib.rs index 534205b..ef46435 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,6 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs, clippy::todo, clippy::dbg_macro)] -#[macro_use] -mod macros; - mod deobfuscate; mod serializer; mod util; diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 7da2081..0000000 --- a/src/macros.rs +++ /dev/null @@ -1,23 +0,0 @@ -/// Returns an unwrapped Option if Some() otherwise returns the passed expression -macro_rules! some_or_bail { - ($opt:expr, $ret:expr $(,)?) => {{ - match $opt { - Some(stuff) => stuff, - None => { - return $ret; - } - } - }}; -} - -/// Returns an unwrapped Result if Ok() otherwise returns the passed expression -macro_rules! ok_or_bail { - ($result:expr, $ret:expr $(,)?) => {{ - match $result { - Ok(stuff) => stuff, - Err(_) => { - return $ret; - } - } - }}; -} diff --git a/src/util/mod.rs b/src/util/mod.rs index 274301c..b41f537 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -193,40 +193,43 @@ pub fn retry_delay( /// Also strips google analytics tracking parameters /// (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`) because google analytics is bad. pub fn sanitize_yt_url(url: &str) -> String { - let mut parsed_url = ok_or_bail!(Url::parse(url), url.to_owned()); + fn sanitize_yt_url_inner(url: &str) -> Option { + let mut parsed_url = Url::parse(url).ok()?; - // Convert redirect url - if parsed_url.host_str().unwrap_or_default() == "www.youtube.com" - && parsed_url.path() == "/redirect" - { - if let Some((_, url)) = parsed_url.query_pairs().find(|(k, _)| k == "q") { - parsed_url = ok_or_bail!(Url::parse(url.as_ref()), url.to_string()); + // Convert redirect url + if parsed_url.host_str().unwrap_or_default() == "www.youtube.com" + && parsed_url.path() == "/redirect" + { + if let Some((_, url)) = parsed_url.query_pairs().find(|(k, _)| k == "q") { + parsed_url = Url::parse(url.as_ref()).ok()?; + } } + + // Remove GA tracking params + if parsed_url.query().is_some() { + let params = parsed_url + .query_pairs() + .filter_map(|(k, v)| match k.borrow() { + "utm_source" | "utm_medium" | "utm_campaign" | "utm_content" => None, + _ => Some((k.to_string(), v.to_string())), + }) + .collect::>(); + + // Set empty query string if there are no parameters to prevent urls from ending with /? + if params.is_empty() { + parsed_url.set_query(None); + } else { + parsed_url + .query_pairs_mut() + .clear() + .extend_pairs(params) + .finish(); + } + } + Some(parsed_url.to_string()) } - // Remove GA tracking params - if parsed_url.query().is_some() { - let params = parsed_url - .query_pairs() - .filter_map(|(k, v)| match k.borrow() { - "utm_source" | "utm_medium" | "utm_campaign" | "utm_content" => None, - _ => Some((k.to_string(), v.to_string())), - }) - .collect::>(); - - // Set empty query string if there are no parameters to prevent urls from ending with /? - if params.is_empty() { - parsed_url.set_query(None); - } else { - parsed_url - .query_pairs_mut() - .clear() - .extend_pairs(params) - .finish(); - } - } - - parsed_url.to_string() + sanitize_yt_url_inner(url).unwrap_or_else(|| url.to_string()) } pub trait TryRemove { @@ -298,7 +301,7 @@ where filtered.push(c); } } - (ok_or_bail!(buf.parse::(), None), exp, filtered) + (buf.parse::().ok()?, exp, filtered) }; let lookup_token = |token: &str| match token { @@ -318,14 +321,7 @@ where .sum::(); } - F::try_from(some_or_bail!( - num.checked_mul(some_or_bail!( - (10_u64).checked_pow(ok_or_bail!(exp.try_into(), None)), - None - )), - None - )) - .ok() + F::try_from(num.checked_mul((10_u64).checked_pow(exp.try_into().ok()?)?)?).ok() } /// Replace all html control characters to make a string safe for inserting into HTML. diff --git a/src/util/protobuf.rs b/src/util/protobuf.rs index 5858a56..64488ac 100644 --- a/src/util/protobuf.rs +++ b/src/util/protobuf.rs @@ -98,11 +98,11 @@ pub fn string_from_pb>(pb: P, field: u32) -> Option 4, // string 2 => { - let len = some_or_bail!(parse_varint(&mut pb), None); + let len = parse_varint(&mut pb)?; if this_field == field { let mut buf = Vec::new(); for _ in 0..len { - buf.push(some_or_bail!(pb.next(), None)); + buf.push(pb.next()?); } return String::from_utf8(buf).ok(); } else { diff --git a/testfiles/deobf/dummy_player.js b/testfiles/deobf/dummy_player.js index 6b241f8..6fa8f7e 100644 --- a/testfiles/deobf/dummy_player.js +++ b/testfiles/deobf/dummy_player.js @@ -1,5 +1,5 @@ // This code is used to test the deobfuscation javascript extraction. -// Since YouTube's player code is copyrighted, I can just copy-paste it +// Since YouTube's player code is copyrighted, I can't just copy-paste it // into my project. /*