deobfuscate: soft-fail sig_fn/nsig_fn extraction
When YouTube rotates player.js to a shape our six sig/nsig regex patterns don't recognise (eg. c2f7551f, May 2026), the whole player path used to die at extract_fns even for clients that don't need the sig fn at all (iOS, Android, Tv all get pre-signed stream URLs). Now sig_fn / nsig_fn extraction is best-effort. Only the signature timestamp is required — every `needs_deobf` client needs sts in the request payload, but the actual deobfuscation functions are only consumed by map_url when a stream URL carries `&s=` or `&n=`. On failure we log a warning and store an empty string; Deobfuscator then skips the JS eval, and any deobfuscate_sig/deobfuscate_nsig call will fail loudly with "sig fn unavailable" instead of crashing the player. Keeps the Tv fallback alive even when sig deobf regex breaks.
This commit is contained in:
parent
a6df2ff7f4
commit
bda0fea193
1 changed files with 43 additions and 9 deletions
|
|
@ -61,10 +61,34 @@ impl DeobfData {
|
|||
}
|
||||
|
||||
pub fn extract_fns(js_url: &str, player_js: &str) -> Result<Self, Error> {
|
||||
let sig_fn = get_sig_fn(player_js)?;
|
||||
let nsig_fn = get_nsig_fn(player_js)?;
|
||||
// The signature timestamp is the only piece every "needs_deobf" client
|
||||
// actually requires in its request payload — without it, those clients
|
||||
// get an error back. So we hard-fail on sts extraction.
|
||||
let sts = get_sts(player_js)?;
|
||||
|
||||
// sig_fn and nsig_fn are needed only when YouTube returns stream URLs
|
||||
// containing the &s= cipher / &n= throttling params. Most clients
|
||||
// (iOS, Android, Tv) get pre-signed URLs and never touch these.
|
||||
// Tolerate extraction failures here so a single rotated player.js
|
||||
// shape doesn't bring down the whole player path for those clients.
|
||||
// The dead-code fallback is preserved: if a stream URL DOES need
|
||||
// deobfuscation, `Deobfuscator::deobfuscate_sig` will fail with a
|
||||
// clear "sig fn unavailable" error instead of crashing the player.
|
||||
let sig_fn = match get_sig_fn(player_js) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
tracing::warn!("could not extract sig deobf fn (sig deobfuscation disabled until YT rotates player.js again): {}", e);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
let nsig_fn = match get_nsig_fn(player_js) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
tracing::warn!("could not extract nsig deobf fn (throttling parameter deobf disabled until YT rotates player.js again): {}", e);
|
||||
String::new()
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
js_url: js_url.to_owned(),
|
||||
sig_fn,
|
||||
|
|
@ -79,13 +103,23 @@ impl Deobfuscator {
|
|||
pub fn new(data: &DeobfData) -> Result<Self, DeobfError> {
|
||||
let rt = Runtime::new()?;
|
||||
let ctx = Context::full(&rt)?;
|
||||
ctx.with(|ctx| {
|
||||
let mut opts = rquickjs::context::EvalOptions::default();
|
||||
opts.strict = false;
|
||||
ctx.eval_with_options::<(), _>(data.sig_fn.as_bytes(), opts)?;
|
||||
let mut opts = rquickjs::context::EvalOptions::default();
|
||||
opts.strict = false;
|
||||
ctx.eval_with_options::<(), _>(data.nsig_fn.as_bytes(), opts)
|
||||
ctx.with(|ctx| -> Result<(), rquickjs::Error> {
|
||||
// Skip JS eval for any deobf fn we couldn't extract. The matching
|
||||
// `deobfuscate_sig` / `deobfuscate_nsig` calls will then return an
|
||||
// Err naturally because the global won't be defined — and that
|
||||
// only matters if a stream actually has obfuscated params, which
|
||||
// shouldn't happen on the iOS/Android/Tv InnerTube paths.
|
||||
if !data.sig_fn.is_empty() {
|
||||
let mut opts = rquickjs::context::EvalOptions::default();
|
||||
opts.strict = false;
|
||||
ctx.eval_with_options::<(), _>(data.sig_fn.as_bytes(), opts)?;
|
||||
}
|
||||
if !data.nsig_fn.is_empty() {
|
||||
let mut opts = rquickjs::context::EvalOptions::default();
|
||||
opts.strict = false;
|
||||
ctx.eval_with_options::<(), _>(data.nsig_fn.as_bytes(), opts)?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
Ok(Self { ctx })
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue