Rust port of NewPipeExtractor (YT-only). Plugs into Straw via UniFFI.
Found via emulator smoke that channelInfo was returning empty
recent_videos list, breaking the subscriptions feed.
Two root causes:
1. First browse of a channel by browseId lands on the HOME tab in
2026 YT, not Videos. Home uses sectionListRenderer, not the
richGridRenderer my parser expected. The Videos tab in the
response carries an empty content block (you need a SECOND
browse with the params token to populate it).
2. Channel video items on the Videos tab migrated from
videoRenderer to lockupViewModel (YT made the switch ~2024).
My old parser only handled videoRenderer.
Fix:
* fetch_channel_browse now does TWO browses — first for Home
(header + metadata), second with params='EgZ2aWRlb3PyBgQKAjoA'
for the Videos tab. Same magic constant NPE uses (audit Track
A §2.4).
* parse_videos_tab handles BOTH videoRenderer (legacy/fallback)
AND lockupViewModel (current). lockupViewModel parse extracts:
- contentId → video ID
- metadata.lockupMetadataViewModel.title.content → title
- metadataRows[].metadataParts[].text.content → view-count
('1.1m views') + relative-age ('2 years ago') + uploader
- contentImage.thumbnailViewModel.overlays[]
.thumbnailBottomOverlayViewModel.badges[]
.thumbnailBadgeViewModel.text → duration ('3:14:08')
- contentImage.thumbnailViewModel.image.sources[] → thumbnails
* parse_videos_continuation pulls the continuation token from the
Videos tab grid for pagination.
Second browse is best-effort: if it fails, recent_videos stays
empty and the channel header still populates from the first.
Verified the YT response shape by probing live channel
UCwwtUfy0-CqN50HfaFDzL0w (NCS Spektrem) — got 30+ lockup-style
video items with the expected fields.
|
||
|---|---|---|
| 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.