refactor: remove bail macros
This commit is contained in:
parent
6a99540ef5
commit
25025ef701
6 changed files with 108 additions and 141 deletions
|
|
@ -376,35 +376,26 @@ fn map_url(
|
||||||
deobf: &Deobfuscator,
|
deobf: &Deobfuscator,
|
||||||
last_nsig: &mut [String; 2],
|
last_nsig: &mut [String; 2],
|
||||||
) -> MapResult<Option<(String, bool)>> {
|
) -> MapResult<Option<(String, bool)>> {
|
||||||
let (url_base, mut url_params) = match url {
|
let x = match url {
|
||||||
Some(url) => ok_or_bail!(
|
Some(url) => util::url_to_params(url).map_err(|_| format!("Could not parse url `{url}`")),
|
||||||
util::url_to_params(url),
|
|
||||||
MapResult {
|
|
||||||
c: None,
|
|
||||||
warnings: vec![format!("Could not parse url `{url}`")]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
None => match signature_cipher {
|
None => match signature_cipher {
|
||||||
Some(signature_cipher) => match cipher_to_url_params(signature_cipher, deobf) {
|
Some(signature_cipher) => cipher_to_url_params(signature_cipher, deobf).map_err(|e| {
|
||||||
Ok(res) => res,
|
format!("Could not deobfuscate signatureCipher `{signature_cipher}`: {e}")
|
||||||
Err(e) => {
|
}),
|
||||||
return MapResult {
|
None => Err("stream contained neither url or cipher".to_owned()),
|
||||||
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()],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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 warnings = vec![];
|
||||||
let mut throttled = false;
|
let mut throttled = false;
|
||||||
deobf_nsig(&mut url_params, deobf, last_nsig).unwrap_or_else(|e| {
|
deobf_nsig(&mut url_params, deobf, last_nsig).unwrap_or_else(|e| {
|
||||||
|
|
@ -414,21 +405,17 @@ fn map_url(
|
||||||
throttled = true;
|
throttled = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
MapResult {
|
match Url::parse_with_params(url_base.as_str(), url_params.iter()) {
|
||||||
c: Some((
|
Ok(url) => MapResult {
|
||||||
ok_or_bail!(
|
c: Some((url.to_string(), throttled)),
|
||||||
Url::parse_with_params(url_base.as_str(), url_params.iter()),
|
warnings,
|
||||||
MapResult {
|
},
|
||||||
c: None,
|
Err(_) => MapResult {
|
||||||
warnings: vec![format!(
|
c: None,
|
||||||
"url could not be joined. url: `{url_base}` params: {url_params:?}"
|
warnings: vec![format!(
|
||||||
)],
|
"url could not be joined. url: `{url_base}` params: {url_params:?}"
|
||||||
}
|
)],
|
||||||
)
|
},
|
||||||
.to_string(),
|
|
||||||
throttled,
|
|
||||||
)),
|
|
||||||
warnings,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -437,16 +424,27 @@ fn map_video_stream(
|
||||||
deobf: &Deobfuscator,
|
deobf: &Deobfuscator,
|
||||||
last_nsig: &mut [String; 2],
|
last_nsig: &mut [String; 2],
|
||||||
) -> MapResult<Option<VideoStream>> {
|
) -> MapResult<Option<VideoStream>> {
|
||||||
let (mtype, codecs) = some_or_bail!(
|
let (mtype, codecs) = match parse_mime(&f.mime_type) {
|
||||||
parse_mime(&f.mime_type),
|
Some(x) => x,
|
||||||
MapResult {
|
None => {
|
||||||
c: None,
|
return MapResult {
|
||||||
warnings: vec![format!(
|
c: None,
|
||||||
"Invalid mime type `{}` in video format {:?}",
|
warnings: vec![format!(
|
||||||
&f.mime_type, &f
|
"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);
|
let map_res = map_url(&f.url, &f.signature_cipher, deobf, last_nsig);
|
||||||
|
|
||||||
match map_res.c {
|
match map_res.c {
|
||||||
|
|
@ -469,13 +467,7 @@ fn map_video_stream(
|
||||||
hdr: f.color_info.unwrap_or_default().primaries
|
hdr: f.color_info.unwrap_or_default().primaries
|
||||||
== player::Primaries::ColorPrimariesBt2020,
|
== player::Primaries::ColorPrimariesBt2020,
|
||||||
mime: f.mime_type.to_owned(),
|
mime: f.mime_type.to_owned(),
|
||||||
format: some_or_bail!(
|
format,
|
||||||
get_video_format(mtype),
|
|
||||||
MapResult {
|
|
||||||
c: None,
|
|
||||||
warnings: vec![format!("invalid video format. itag: {}", f.itag)]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
codec: get_video_codec(codecs),
|
codec: get_video_codec(codecs),
|
||||||
throttled,
|
throttled,
|
||||||
}),
|
}),
|
||||||
|
|
@ -495,16 +487,27 @@ fn map_audio_stream(
|
||||||
) -> MapResult<Option<AudioStream>> {
|
) -> MapResult<Option<AudioStream>> {
|
||||||
static LANG_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r#"^([a-z]{2,3})\."#).unwrap());
|
static LANG_PATTERN: Lazy<Regex> = Lazy::new(|| Regex::new(r#"^([a-z]{2,3})\."#).unwrap());
|
||||||
|
|
||||||
let (mtype, codecs) = some_or_bail!(
|
let (mtype, codecs) = match parse_mime(&f.mime_type) {
|
||||||
parse_mime(&f.mime_type),
|
Some(x) => x,
|
||||||
MapResult {
|
None => {
|
||||||
c: None,
|
return MapResult {
|
||||||
warnings: vec![format!(
|
c: None,
|
||||||
"Invalid mime type `{}` in video format {:?}",
|
warnings: vec![format!(
|
||||||
&f.mime_type, &f
|
"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);
|
let map_res = map_url(&f.url, &f.signature_cipher, deobf, last_nsig);
|
||||||
|
|
||||||
match map_res.c {
|
match map_res.c {
|
||||||
|
|
@ -519,13 +522,7 @@ fn map_audio_stream(
|
||||||
init_range: f.init_range,
|
init_range: f.init_range,
|
||||||
duration_ms: f.approx_duration_ms,
|
duration_ms: f.approx_duration_ms,
|
||||||
mime: f.mime_type.to_owned(),
|
mime: f.mime_type.to_owned(),
|
||||||
format: some_or_bail!(
|
format,
|
||||||
get_audio_format(mtype),
|
|
||||||
MapResult {
|
|
||||||
c: None,
|
|
||||||
warnings: vec![format!("invalid audio format. itag: {}", f.itag)]
|
|
||||||
}
|
|
||||||
),
|
|
||||||
codec: get_audio_codec(codecs),
|
codec: get_audio_codec(codecs),
|
||||||
channels: f.audio_channels,
|
channels: f.audio_channels,
|
||||||
loudness_db: f.loudness_db,
|
loudness_db: f.loudness_db,
|
||||||
|
|
@ -559,7 +556,7 @@ fn parse_mime(mime: &str) -> Option<(&str, Vec<&str>)> {
|
||||||
static PATTERN: Lazy<Regex> =
|
static PATTERN: Lazy<Regex> =
|
||||||
Lazy::new(|| Regex::new(r#"(\w+/\w+);\scodecs="([a-zA-Z-0-9.,\s]*)""#).unwrap());
|
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((
|
Some((
|
||||||
captures.get(1).unwrap().as_str(),
|
captures.get(1).unwrap().as_str(),
|
||||||
captures
|
captures
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
#![warn(missing_docs, clippy::todo, clippy::dbg_macro)]
|
#![warn(missing_docs, clippy::todo, clippy::dbg_macro)]
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
mod macros;
|
|
||||||
|
|
||||||
mod deobfuscate;
|
mod deobfuscate;
|
||||||
mod serializer;
|
mod serializer;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
@ -193,40 +193,43 @@ pub fn retry_delay(
|
||||||
/// Also strips google analytics tracking parameters
|
/// Also strips google analytics tracking parameters
|
||||||
/// (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`) because google analytics is bad.
|
/// (`utm_source`, `utm_medium`, `utm_campaign`, `utm_content`) because google analytics is bad.
|
||||||
pub fn sanitize_yt_url(url: &str) -> String {
|
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<String> {
|
||||||
|
let mut parsed_url = Url::parse(url).ok()?;
|
||||||
|
|
||||||
// Convert redirect url
|
// Convert redirect url
|
||||||
if parsed_url.host_str().unwrap_or_default() == "www.youtube.com"
|
if parsed_url.host_str().unwrap_or_default() == "www.youtube.com"
|
||||||
&& parsed_url.path() == "/redirect"
|
&& parsed_url.path() == "/redirect"
|
||||||
{
|
{
|
||||||
if let Some((_, url)) = parsed_url.query_pairs().find(|(k, _)| k == "q") {
|
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());
|
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::<Vec<_>>();
|
||||||
|
|
||||||
|
// 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
|
sanitize_yt_url_inner(url).unwrap_or_else(|| url.to_string())
|
||||||
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::<Vec<_>>();
|
|
||||||
|
|
||||||
// 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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait TryRemove<T> {
|
pub trait TryRemove<T> {
|
||||||
|
|
@ -298,7 +301,7 @@ where
|
||||||
filtered.push(c);
|
filtered.push(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(ok_or_bail!(buf.parse::<u64>(), None), exp, filtered)
|
(buf.parse::<u64>().ok()?, exp, filtered)
|
||||||
};
|
};
|
||||||
|
|
||||||
let lookup_token = |token: &str| match token {
|
let lookup_token = |token: &str| match token {
|
||||||
|
|
@ -318,14 +321,7 @@ where
|
||||||
.sum::<i32>();
|
.sum::<i32>();
|
||||||
}
|
}
|
||||||
|
|
||||||
F::try_from(some_or_bail!(
|
F::try_from(num.checked_mul((10_u64).checked_pow(exp.try_into().ok()?)?)?).ok()
|
||||||
num.checked_mul(some_or_bail!(
|
|
||||||
(10_u64).checked_pow(ok_or_bail!(exp.try_into(), None)),
|
|
||||||
None
|
|
||||||
)),
|
|
||||||
None
|
|
||||||
))
|
|
||||||
.ok()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace all html control characters to make a string safe for inserting into HTML.
|
/// Replace all html control characters to make a string safe for inserting into HTML.
|
||||||
|
|
|
||||||
|
|
@ -98,11 +98,11 @@ pub fn string_from_pb<P: IntoIterator<Item = u8>>(pb: P, field: u32) -> Option<S
|
||||||
5 => 4,
|
5 => 4,
|
||||||
// string
|
// string
|
||||||
2 => {
|
2 => {
|
||||||
let len = some_or_bail!(parse_varint(&mut pb), None);
|
let len = parse_varint(&mut pb)?;
|
||||||
if this_field == field {
|
if this_field == field {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
for _ in 0..len {
|
for _ in 0..len {
|
||||||
buf.push(some_or_bail!(pb.next(), None));
|
buf.push(pb.next()?);
|
||||||
}
|
}
|
||||||
return String::from_utf8(buf).ok();
|
return String::from_utf8(buf).ok();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
// This code is used to test the deobfuscation javascript extraction.
|
// 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.
|
// into my project.
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Reference in a new issue