strawcore/examples/live_extract.rs
Cobb dd964407cc
Some checks failed
gitleaks / scan (push) Failing after 5s
youtube: fall back to VISIONOS primary when ANDROID is bot-walled
YouTube's 2026 poToken/BotGuard enforcement now rejects the pot-less ANDROID
client outright (playabilityStatus "sign in to confirm you're not a bot"), which
aborted the whole extraction since ANDROID was the required primary.

stream_info_with() now cascades ANDROID -> VISIONOS for the primary player
response: when ANDROID is unusable (fetch error, failed playability, or decoy),
retry with VISIONOS (client 101) as the primary. VISIONOS is pot-free,
visitorData-based, and needs no JS player -- the current survivor (yt-dlp
default; NewPipeExtractor #1508). Only if VISIONOS also fails do we surface the
ORIGINAL ANDROID error (keeps the real bot-wall reason visible).

The fallback drops the (poisoned) visitorData cache first so VISIONOS mints a
fresh one (the ingredient that passes attestation). On the VISIONOS-primary
path the primary source carries no poToken (null) and uses the VISIONOS cpn, so
no ANDROID pot/cpn is appended to a visionOS URL (would 403); the redundant
add-on VISIONOS fetch is skipped. Output shape is unchanged (still the muxed
itag-18 the app plays) -- no app or delivery-labeling change. 5 new unit tests.
2026-08-06 06:35:27 -07:00

50 lines
1.6 KiB
Rust

// Throwaway live diagnostic (playback-outage triage). Not shipped.
use std::sync::Arc;
use strawcore_core::downloader::ReqwestDownloader;
use strawcore_core::youtube::stream_extractor::{stream_info_with, ExtractOptions};
use strawcore_core::NewPipe;
fn client_of(url: &str) -> String {
url.split('&')
.find_map(|kv| kv.strip_prefix("c="))
.unwrap_or("?")
.to_string()
}
fn report(label: &str, vid: &str, visionos: bool) {
let opts = ExtractOptions {
fetch_visionos_client: visionos,
..Default::default()
};
match stream_info_with(vid, opts) {
Ok(i) => {
let a = i.audio_streams.first().map(|s| client_of(&s.url));
let v = i
.video_streams
.first()
.or_else(|| i.video_only_streams.first())
.map(|s| client_of(&s.url));
println!(
"[{label}] OK audio={} video={} video_only={} dash={} hls={} | first audio c={:?} first video c={:?}",
i.audio_streams.len(),
i.video_streams.len(),
i.video_only_streams.len(),
i.dash_manifest_url.is_some(),
i.hls_manifest_url.is_some(),
a,
v,
);
}
Err(e) => println!("[{label}] ERR {e:?}"),
}
}
fn main() {
let dl = Arc::new(ReqwestDownloader::new().expect("downloader"));
NewPipe::init(dl);
let vid = std::env::args()
.nth(1)
.unwrap_or_else(|| "dQw4w9WgXcQ".into());
report("visionOS ON (shipped)", &vid, true);
report("visionOS OFF (like 91) ", &vid, false);
}