fix: improve deobfuscator (support multiple nsig name matches, error if mapping all streams fails)

Since YouTube keeps changing the nsig function signature and a generic regex may match at multiple places, I changed the extraction logic to search for multiple matches if necessary and test the extracted deobfuscation functions.

I also found out that if the deobfuscation fails for all streams, fetching the player still returns a successful result with no streams, suggesting that the video is not available. So I changed the mapper to throw an ExtractionError if no streams are mapped successfully.
This commit is contained in:
ThetaDev 2024-07-29 14:45:52 +02:00
parent 11a0038350
commit 8152ce6b08
No known key found for this signature in database
GPG key ID: E319D3C5148D65B6
4 changed files with 414 additions and 331 deletions

View file

@ -551,6 +551,24 @@ impl<'a> Iterator for SplitTokens<'a> {
}
}
/// Applies function to the elements of iterator and returns the first successful result
/// or the last error if the function fails on all elements. If the iterator is empty, e_empty
/// is returned.
pub fn find_map_or_last_err<I, T, P, O, E>(mut iter: I, e_empty: E, mut f: P) -> Result<O, E>
where
I: Iterator<Item = T>,
P: FnMut(T) -> Result<O, E>,
{
let res = iter.try_fold(e_empty, |_, itm| match f(itm) {
Ok(o) => Err(o),
Err(e) => Ok(e),
});
match res {
Ok(e) => Err(e),
Err(o) => Ok(o),
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::{fs::File, io::BufReader, path::PathBuf};
@ -730,4 +748,27 @@ pub(crate) mod tests {
let res = country_from_name(name);
assert_eq!(res, expect);
}
#[test]
fn t_find_map_or_last_err() {
// Success
let res = find_map_or_last_err([1, 2, 3].into_iter(), 0, |x: i32| {
if x > 2 {
Ok(true)
} else {
Err(x)
}
});
assert_eq!(res, Ok(true));
// Error
let res = find_map_or_last_err([1, 2, 3].into_iter(), 0, |x: i32| Err::<(), _>(x));
assert_eq!(res, Err(3));
// Empty iterator
assert_eq!(
find_map_or_last_err(std::iter::empty(), 0, |_: i32| Ok(true)),
Err(0)
);
}
}