Kodi addon for YouTube via rustypipe + SponsorBlock. Replaces the dead plugin.video.youtube on LibreELEC RPi TVs.
Find a file
Kayos 0a289fea3a M7 — DASH partial: MPD serves OK, segment alignment still WIP
Big strides today:
- Sidecar resolve_dash op works (verified live on Pi)
- MPD builder generates valid MPEG-DASH on-demand manifest with
  H.264 720p/1080p video reps + best AAC audio rep
- ThreadingHTTPServer serves the MPD over the LAN IP (not 127.0.0.1
  — curl in Kodi 20's inputstream.adaptive can't open that)
- inputstream.adaptive PARSES our manifest cleanly: 'Successfully
  parsed manifest file (Periods: 1, Streams in first period: 2)'
- Segment GETs work once we set stream_headers with User-Agent
  + Origin + Referer (otherwise googlevideo 403s the audio segments)

Remaining issue:
- Audio drifts -25s → -44s behind video within seconds of playback
  start. inputstream.adaptive needs explicit SegmentTimeline timing
  derived from each rep's sidx box to stay aligned. Plugin.video.youtube
  does this; we'd need to fetch+parse sidx ourselves or fork their
  MPD-builder. Documented as M7-blocking + upstream PR candidate.

Default remains the stable yt-dlp progressive 360p path. DASH is
behind dash_enabled setting OR a dash.on marker file in addon_data.
Toggle on via:
  ssh <kodi> 'touch /storage/.kodi/userdata/addon_data/plugin.video.torttube/dash.on'
Toggle off:
  ssh <kodi> 'rm /storage/.kodi/userdata/addon_data/plugin.video.torttube/dash.on'

Addon v0.0.10. docs/upstream.md has the full segment-timing analysis.
2026-05-23 11:46:56 -07:00
addon/plugin.video.torttube M7 — DASH partial: MPD serves OK, segment alignment still WIP 2026-05-23 11:46:56 -07:00
docs M7 — DASH partial: MPD serves OK, segment alignment still WIP 2026-05-23 11:46:56 -07:00
scripts M6 DONE — torttube ships, Rick Astley plays fullscreen on the Livingroom Pi 2026-05-23 10:18:26 -07:00
sidecar M4 wrap — playlist browse + addon settings + upstream PR-77 notes 2026-05-23 11:33:20 -07:00
.gitignore M0 scaffold — Python addon + Rust sidecar 2026-05-23 08:14:09 -07:00
LICENSE M0 scaffold — Python addon + Rust sidecar 2026-05-23 08:14:09 -07:00
MILESTONES.md M7 — DASH partial: MPD serves OK, segment alignment still WIP 2026-05-23 11:46:56 -07:00
README.md M0 docs — lock three-tier resolve + upstream-contribution posture 2026-05-23 08:20:50 -07:00

torttube

Kodi addon for YouTube via RustyPipe extraction + SponsorBlock segment skipping.

Replaces the dead plugin.video.youtube on LibreELEC RPi TVs after Google required account-linking for the upstream addon.

Architecture

Kodi (LibreELEC, RPi)
  └── plugin.video.torttube     [Python addon — UI, browse, settings]
        └── torttube-sidecar    [Rust binary — JSON-over-stdio]
              ├── rustypipe         [Tier 1: native Rust Innertube]
              ├── yt-dlp subprocess [Tier 2: fallback resolve]
              ├── yt-dlp rip-to-temp [Tier 3: download to /storage/.kodi/temp,
              │                      play local file when streams die mid-play]
              └── sponsorblock      [REST client, SHA-256 prefix lookup]

Kodi addons are Python — the engine layer (n-param sig decoding, Innertube, SponsorBlock hashing) lives in a Rust sidecar so we get a single maintained extraction surface and clean aarch64/armv7 cross-compiles.

Three-tier resolve because YouTube actively fights every extractor:

  1. rustypipe (Rust) — preferred. Fast, in-process, no Python dep on the RPi.
  2. yt-dlp subprocess — fallback when rustypipe sig-decoding falls behind YouTube's deobfuscator changes. yt-dlp updates weekly; we shell out, parse -j JSON.
  3. Rip-to-temp — last resort when stream URLs 403 mid-playback (poToken expiry, cookie session mismatch). yt-dlp downloads to /storage/.kodi/temp/torttube/<id>.<ext>, Kodi plays the local file. Temp dir has size cap + age cleanup.

Status

M0 scaffold. Nothing playable yet — see MILESTONES.md.

Upstream — we fight with the FOSS extractor ecosystem, not next to it

YouTube's anti-scraping changes hit every extractor: NewPipe, yt-dlp, Invidious, rustypipe. Every fix we make in our sidecar gets evaluated for "is this upstreamable?" — if yes, the fix lands at the upstream project, not just here.

Active lanes:

  • rustypipe (Rust, codeberg.org/ThetaDev/rustypipe) — maintenance has slowed. Open PR #77 "Some fixes" is unmerged as of 2026-05-23. We will either help land it (review + ping maintainer) or fork to Sulkta-Coop/rustypipe if upstream stays quiet. Forking is the worst case, not the first move.
  • NewPipeExtractor (Java, github.com/TeamNewPipe/NewPipeExtractor) — actively maintained, 177 open issues. We use it as the reference implementation for Innertube behaviour. PRs to NPE land in Rust here via rustypipe, and vice versa.
  • yt-dlp (Python, github.com/yt-dlp/yt-dlp) — the gold standard. We're more consumers than contributors here, but if our rip-to-temp tier surfaces a specific extractor bug we file it.

Issues we're watching:

  • NPE #1339 — n-parameter deobfuscation
  • NPE #1444 — distinguish unavailable vs unextractable
  • NPE #1360 — refactor link handlers (help wanted)
  • NPE #1357 — JDoc checks in PR pipeline (good first issue)
  • rustypipe PR #77 — open as of 2026-05-23, unmerged

Contribution log lives at docs/upstream.md — every PR we file lands there with its outcome.

License

GPL-3.0-or-later. Matches RustyPipe and NewPipeExtractor.