Some checks failed
gitleaks / scan (push) Failing after 4s
The crate had zero logging — every failure, fallback, cache reset, and empty-parse was silent by construction, which made the 2026 bot-wall outage nearly undiagnosable. Add `log = "0.4"` and 52 statements across the extractor following a one-INFO-per-outcome / WARN-on-every-failure / DEBUG-for-internals policy, so a future break shows up in a log grep instead of a debugger. Highlights: nsig->throttled-URL fallback (aggregated one WARN per extraction, never per-format, via a threaded UrlProcessStats counter); the nsig identity-output trap now returns DeobfError::NsigIdentity and is NOT cached (previously it cached and permanently throttled); player.js build/eval/install/ StillBad lifecycle; the ANDROID->VISIONOS cascade outcome; visitorData decline + reset; HTTP 429 bot-flag; channel/search layout-change empties; per-request DEBUG (query stripped). Plus a `BotDetected` error variant (Display byte- identical to the old string) so the wall is greppable. No secrets: only videoId/channel ids, error Displays (already URL/token-scrubbed at the exceptions.rs choke points), query-stripped endpoints/player.js URLs, counts, and lengths are logged — never token/poToken/visitorData/signature values or response bodies.
69 lines
2.5 KiB
Rust
69 lines
2.5 KiB
Rust
// JS deobfuscator subsystem — mirrors NPE's player.js / sig / nsig pipeline.
|
|
//
|
|
// Production callers go through `player_manager` (mirrors NPE's
|
|
// YoutubeJavaScriptPlayerManager):
|
|
// * signature_timestamp(video_id)
|
|
// * deobfuscate_signature(video_id, obfuscated)
|
|
// * url_with_throttling_parameter_deobfuscated(video_id, url)
|
|
// * throttling_parameter_cache_size()
|
|
// * clear_all_caches()
|
|
// * clear_throttling_parameters_cache()
|
|
//
|
|
// The other submodules (runtime / lexer / extractor / signature / nsig) are
|
|
// kept `pub` because `tests/js_phase2_offline.rs` exercises them directly
|
|
// from outside the crate. If the integration test gets folded into the
|
|
// inline `#[cfg(test)]` blocks, these can drop to `pub(crate)`.
|
|
|
|
pub mod extractor;
|
|
pub mod lexer;
|
|
pub mod nsig;
|
|
pub mod player_manager;
|
|
pub mod runtime;
|
|
pub mod signature;
|
|
|
|
use thiserror::Error;
|
|
|
|
// Clone: PlayerManager's failure memo replays the original extraction
|
|
// error on calls suppressed by the cooldown (mirrors NPE's cached
|
|
// ParsingException fields).
|
|
#[derive(Debug, Clone, Error)]
|
|
pub enum DeobfError {
|
|
#[error("could not fetch iframe_api: {0}")]
|
|
FetchIframe(String),
|
|
#[error("could not fetch embed page: {0}")]
|
|
FetchEmbed(String),
|
|
#[error("could not extract player.js URL")]
|
|
PlayerUrlMissing,
|
|
#[error("could not fetch player.js: {0}")]
|
|
FetchPlayerCode(String),
|
|
#[error("invalid player.js URL: {0}")]
|
|
InvalidPlayerUrl(String),
|
|
#[error("could not find sig deobf function via any pattern")]
|
|
SigFuncNotFound,
|
|
#[error("could not parse sig deobf function body: {0}")]
|
|
SigBodyParseFailed(String),
|
|
#[error("could not find sig helper object")]
|
|
SigHelperMissing,
|
|
#[error("could not find sig global array")]
|
|
SigGlobalArrayMissing,
|
|
#[error("could not extract signature timestamp")]
|
|
SigTimestampMissing,
|
|
#[error("could not find nsig deobf function via any pattern")]
|
|
NsigFuncNotFound,
|
|
#[error("could not parse nsig deobf function body: {0}")]
|
|
NsigBodyParseFailed(String),
|
|
#[error("nsig array indirection failed: {0}")]
|
|
NsigArrayLookupFailed(String),
|
|
#[error("js compile failed: {0}")]
|
|
JsCompileFailed(String),
|
|
#[error("js runtime failed: {0}")]
|
|
JsRuntimeFailed(String),
|
|
#[error("nsig output was empty (function neutered?)")]
|
|
NsigEmpty,
|
|
#[error("nsig output equalled its input (identity — deobfuscator neutered?)")]
|
|
NsigIdentity,
|
|
#[error("downloader not initialized")]
|
|
DownloaderMissing,
|
|
}
|
|
|
|
pub use player_manager::PlayerManager;
|