Rust port of NewPipeExtractor (YT-only). Plugs into Straw via UniFFI.
Lands the data shapes + the HTTP layer for stream extraction. The
extractor orchestrator + DASH manifest creator are deferred to the
next session — the parsing logic is dense enough to want a focused
pass.
src/stream/
* mod.rs — StreamInfo + StreamInfoItem (full + 'card' shapes)
mirroring NPE StreamInfo.java + StreamInfoItem.java
* delivery.rs — DeliveryMethod (Progressive/Dash/Hls/Torrent)
* audio.rs — AudioStream (itag, format, url, bitrate, codec,
audio_track_id, content_length, etc.)
* video.rs — VideoStream (itag, format, url, resolution, fps,
bandwidth, codec, video_only flag)
* subtitles.rs — SubtitlesStream (url, lang, auto_generated, mime)
src/youtube/stream_helper.rs
* generate_content_playback_nonce() — 16-char LCG-shuffled cpn
* get_web_metadata_player_response (microformat + thumbnails only)
* get_web_embedded_player_response (embed-url + signatureTimestamp)
* get_android_player_response (full Android /player + poToken)
* get_android_reel_player_response (no-poToken fallback)
* get_ios_player_response (iOS — flagged with 917 KiB cap
warning in the doc comment)
Per-helper headers + URL shapes match audit Track C §2.7 verbatim:
Android/iOS hit gapis endpoint with mobile UA; WEB family hits
www.youtube.com with the WEB headers.
Tests: 64 lib unit pass (up from 62 in Phase 3).
Next session: full stream_extractor.rs orchestrator + dash_manifest/
creator + Phase 4 done-when smoke (extract NCS Spektrem).
|
||
|---|---|---|
| src | ||
| tests | ||
| .gitignore | ||
| Cargo.lock | ||
| Cargo.toml | ||
| LICENSE | ||
| README.md | ||
strawcore
Rust port of NewPipeExtractor (v0.26.2), YouTube-only. Plugs into Straw via UniFFI.
Why this exists
rustypipe regex-parses YouTube's player.js and reimplements the signature deobfuscator in Rust. Every YT player rotation breaks it. NPE embeds Mozilla Rhino and executes the JS function live — resilient by design, and that's the architecture we're mirroring.
The rustypipe-backed Straw build (vc=15..17) also routed playback through iOS-progressive URLs, which hit a server-side ~917 KiB end-byte cap. NPE uses the Android client + po_token → DASH manifest path, which doesn't see the cap. Same fix, different layer.
See memory/npe-audit-2026-05-24/SPEC.md in the workspace repo for the full plan.
Status
| Phase | Subsystem | Status |
|---|---|---|
| 1 | Foundation (downloader + service spine) | in progress |
| 2 | JS engine (rquickjs + ress) | pending |
| 3 | InnerTube + itag table | pending |
| 4 | Stream extractor + DASH | pending |
| 5 | PoTokenProvider trait + Android JNI bridge | pending |
| 6 | Search + Channel + Playlist + Kiosks | pending |
| 7 | UniFFI surface swap | pending |
| 8 | Delete rustypipe everywhere | pending |
Build + test
cargo build
cargo test --lib # offline unit tests
cargo test --features online-tests # full smoke incl. live httpbin.org
License
GPL-3.0-or-later. NPE is GPL-3.0; this port inherits.