Kodi addon for YouTube via rustypipe + SponsorBlock. Replaces the dead plugin.video.youtube on LibreELEC RPi TVs.
Find a file
Kayos 659e7cf613 Subscriptions — NewPipe-style offline subs + chronological feed
User-curated, no YouTube account. Same posture as Watch Later: you
decide what comes back, no algorithm involved.

Sidecar SubscriptionsFeed op:
  - Takes channel_ids: Vec<String>, per_channel + limit defaults 8/60
  - Caps fan-out at 200 channels per call + each id at 64 chars to
    keep a malicious caller from hammering YouTube via the sidecar
  - Spawns one tokio::task per channel against
    rustypipe::query().channel_videos(), merges results once all
    finish, sorts by publish_date string newest-first
  - A channel that 404s / region-blocks / rustypipe-errors is silently
    dropped — one dead subscription doesn't kill the whole feed; the
    failed list is returned in channels_failed for logging

Addon side:
  - subscriptions.json under addon_data, persisted via same
    _atomic_write_json + _with_lock helpers as Watch Later (no
    repeat of the race + torn-write hazards the audit caught)
  - Two new root menu entries (visible only when subscribed):
    * 'Subscriptions Feed  (N)' — chronological merge of latest uploads
    * 'Subscriptions (channel list)' — per-channel browse
  - Context menu on every video result toggles
    'Subscribe to <channel>' / 'Unsubscribe from <channel>' based on
    current sub state
  - Context menu on each entry in the channel list has its own
    'Unsubscribe from <channel>' for direct removal
  - _unsubscribe_action does Container.Refresh only when
    Container.FolderPath contains 'action=subs' (same guard pattern
    we used for wl_remove)

Live smoke (browse-only, no playback, Leia still safe):
  - Subscribed to LTT (UCXuqSBlHAE6Xw-yeJA0Tunw) + MKBHD
    (UCBJycsmduvYEL83R_U4JriQ) via RunPlugin
  - subscriptions.json correctly holds both
  - action=subs shows MKBHD + Linus Tech Tips as channel folders
  - action=subs_feed returns 16 merged items: MKBHD's recent uploads
    plus LTT's
  - Root menu now includes 'Subscriptions Feed  (2)' and
    'Subscriptions (channel list)'

Addon v0.0.17.
2026-05-23 12:59:16 -07:00
addon/plugin.video.torttube Subscriptions — NewPipe-style offline subs + chronological feed 2026-05-23 12:59:16 -07:00
docs Declare plugin.video.youtube as a Kodi addon dep 2026-05-23 12:15:06 -07:00
scripts M6 DONE — torttube ships, Rick Astley plays fullscreen on the Livingroom Pi 2026-05-23 10:18:26 -07:00
sidecar Subscriptions — NewPipe-style offline subs + chronological feed 2026-05-23 12:59:16 -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 DONE via delegation — pv.youtube plays HD with audio 2026-05-23 11:58:12 -07:00
README.md Declare plugin.video.youtube as a Kodi addon dep 2026-05-23 12:15:06 -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, SponsorBlock]
        ├── torttube-sidecar    [Rust binary — JSON-over-stdio]
        │     ├── rustypipe         [Native Rust Innertube for browse]
        │     ├── yt-dlp subprocess [Fallback resolve]
        │     └── sponsorblock      [REST client, SHA-256 prefix lookup]
        └── plugin.video.youtube    [DEPENDENCY — handles HD playback]
              └── inputstream.adaptive  [DASH demux + decode]

plugin.video.youtube is declared as a Kodi addon dependency in addon.xml. When a user installs torttube, Kodi auto-fetches pv.youtube from the official Kodi addon repository — user only manages torttube; the dep is transparent.

torttube does what it's faster at: rustypipe-backed search/channel/playlist browse, SponsorBlock auto-skip via a tight xbmc.Player() monitor loop, JSON-RPC remote-control for share-to-TV. Playback hands off to pv.youtube via plugin://plugin.video.youtube/play/?video_id=<id> — they've spent years getting the DASH-MPD + multi-client Innertube fallback right. Our SponsorBlock monitor runs in parallel because xbmc.Player() is a global accessor that works regardless of which addon initiated playback.

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.