diff --git a/MILESTONES.md b/MILESTONES.md index 342ffe1..b914c26 100644 --- a/MILESTONES.md +++ b/MILESTONES.md @@ -48,11 +48,19 @@ - [ ] toast on skip + skip-counter in settings - [ ] category toggles in `settings.xml` -## M6 — install + cross-compile +## M6 — install + cross-compile [PARTIAL] -- [ ] crafting-table builds `torttube-sidecar.aarch64` + `.armv7` -- [ ] `addon.zip` ships with platform detect via `xbmc.getCondVisibility('system.platform.linux.raspberrypi')` -- [ ] one-shot install path documented for LibreELEC `/storage/.kodi/` +- [x] cross-compile sidecar for aarch64-musl static via throwaway + `messense/rust-musl-cross:aarch64-musl` container. 6.2MB stripped + static binary. Builds clean from `scripts/build-addon-zip.sh`. +- [x] bundle yt-dlp's `yt-dlp_linux_aarch64` release binary for Tier 2/3 +- [x] zip layout matches Kodi "install from zip" expectations +- [x] addon.zip dropped at `smb://lucy/downloads/torttube/` for Pi-side install +- [x] install + smoke recipe documented at `docs/install.md` +- [ ] **install on the actual Pi** + verify the JSON-RPC `Player.Open` + smoke against `192.168.0.158` — needs Cobb to either install via + Kodi UI or grant SSH to drop the zip directly +- [ ] armv7 build for older Pis (deferred — Cobb's TVs are all aarch64-capable) ## Upstream PR work (parallel lane — every bug evaluated for "fix it upstream?") diff --git a/addon/plugin.video.torttube/addon.xml b/addon/plugin.video.torttube/addon.xml index 0866235..d1b51ab 100644 --- a/addon/plugin.video.torttube/addon.xml +++ b/addon/plugin.video.torttube/addon.xml @@ -5,7 +5,6 @@ provider-name="Sulkta-Coop"> - diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 0000000..0ff36a1 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,65 @@ +# Installing torttube on the LibreELEC RPi + +The addon ships as a single `plugin.video.torttube-.zip` that +contains the Python addon, a static aarch64 sidecar binary, and yt-dlp's +aarch64 release binary (for Tier 2/3 fallback). Nothing else needs to be +installed on the Pi. + +## Build the zip (Sulkta-internal) + +```bash +bash scripts/build-addon-zip.sh +# → /mnt/user/downloads/torttube/plugin.video.torttube-0.0.1.zip on Lucy +``` + +The script cross-compiles the sidecar in a throwaway +`messense/rust-musl-cross:aarch64-musl` container, fetches yt-dlp's +official `yt-dlp_linux_aarch64` release binary, packages everything, +drops the result at `/mnt/user/downloads/torttube/` (Lucy SMB). + +## Install on the Pi (Kodi UI flow) + +1. On the Pi: Settings → File manager → Add source → enter + `smb://lucy/downloads/` → name it `lucy-downloads`. +2. Settings → Add-ons → Install from zip file → `lucy-downloads` → + `torttube/plugin.video.torttube-.zip`. +3. Kodi installs, the addon appears under Video add-ons. + +Unsigned addons need `Settings → System → Add-ons → Unknown sources` ON. + +## Verify + +After install, fire the smoke from any LAN client: + +```bash +curl -u kodi:pineapple -H "Content-Type: application/json" \ + -X POST http://192.168.0.158:8080/jsonrpc -d '{ + "jsonrpc": "2.0", "id": 1, "method": "Player.Open", + "params": {"item": {"file": + "plugin://plugin.video.torttube/?action=play&id=dQw4w9WgXcQ"}}}' +``` + +The TV should switch to playback of "Never Gonna Give You Up" within a +few seconds (rustypipe resolve takes ~1s, then Kodi starts the stream). + +## Troubleshooting + +- **Black screen + "no stream URL"** notification: rustypipe returned + separate audio + video streams (DASH-style), and we're picking the + video stream which may be video-only. This is the M3 known gap — + needs the audio+video merge work in M3+. Workaround: try a video that + yt-dlp's tier-2 path can resolve to a combined format; the sidecar + falls back automatically. +- **Sidecar crashes**: `cat /storage/.kodi/temp/kodi.log | grep torttube` + on the Pi. Sidecar logs to stderr; Kodi captures those. +- **yt-dlp permission denied**: addon's bin/ dir needs +x. The zip + preserves perms but if you copied files manually, `chmod +x bin/*`. +- **HTTPS errors from sidecar**: it bundles rustls + webpki roots, so + no system CA store needed. If you see "invalid certificate", your Pi + clock is wrong (LibreELEC's NTP failed). + +## Updating + +Just rebuild + bump the version in `addon/plugin.video.torttube/addon.xml`, +re-run `build-addon-zip.sh`, install the new zip from the same SMB +location. Kodi will treat it as an upgrade if the version is higher. diff --git a/scripts/build-addon-zip.sh b/scripts/build-addon-zip.sh new file mode 100755 index 0000000..3b4ef60 --- /dev/null +++ b/scripts/build-addon-zip.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Build addon.zip for torttube — runs from a host that can ssh lucy. +# +# Cross-compiles the sidecar via messense/rust-musl-cross:aarch64-musl +# (one-shot container, no crafting-table mutation), bundles yt-dlp's +# aarch64 release binary, packages with the Kodi addon dir layout, and +# drops the zip at /mnt/user/downloads/torttube/ on Lucy. +# +# Usage: bash scripts/build-addon-zip.sh +# +# Expected: ssh alias "lucy" works (per Sulkta SSH config), and the +# torttube source is at /mnt/cache/appdata/crafting-table/workspace/torttube/ +# on Lucy (rsync first if you're iterating locally). +set -euo pipefail + +VERSION="${VERSION:-0.0.1}" +SRC=/mnt/cache/appdata/crafting-table/workspace/torttube +CARGO_HOME_CACHE=/mnt/cache/appdata/crafting-table/workspace/.cargo-aarch64 +TARGET_DIR=/mnt/cache/appdata/crafting-table/workspace/.aarch64-target +STAGE=/tmp/torttube-stage +DEST_DIR=/mnt/user/downloads/torttube + +echo ">>> Cross-compile sidecar for aarch64-musl" +ssh lucy "docker run --rm \ + -v $SRC/sidecar:/src \ + -v $CARGO_HOME_CACHE:/cargo-home \ + -v $TARGET_DIR:/target \ + -e PATH=/root/.cargo/bin:/usr/local/musl/bin:/usr/local/bin:/usr/bin:/bin \ + -e CARGO_HOME=/cargo-home \ + -e CARGO_TARGET_DIR=/target \ + -e CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER=aarch64-unknown-linux-musl-gcc \ + -e CC_aarch64_unknown_linux_musl=aarch64-unknown-linux-musl-gcc \ + -w /src \ + messense/rust-musl-cross:aarch64-musl \ + cargo build --release" + +echo ">>> Stage addon tree" +ssh lucy "rm -rf $STAGE && mkdir -p $STAGE/plugin.video.torttube/bin" +ssh lucy "rsync -a $SRC/addon/plugin.video.torttube/ $STAGE/plugin.video.torttube/ --exclude bin" +ssh lucy "cp $TARGET_DIR/aarch64-unknown-linux-musl/release/torttube-sidecar $STAGE/plugin.video.torttube/bin/" + +echo ">>> Fetch yt-dlp aarch64 release binary" +ssh lucy "curl -sSL -o $STAGE/plugin.video.torttube/bin/yt-dlp \ + https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux_aarch64" +ssh lucy "chmod +x $STAGE/plugin.video.torttube/bin/*" + +echo ">>> Build addon.zip" +ssh lucy "cd $STAGE && rm -f plugin.video.torttube.zip && \ + zip -r plugin.video.torttube.zip plugin.video.torttube/ -x '*.DS_Store' > /dev/null" + +echo ">>> Drop at $DEST_DIR/plugin.video.torttube-$VERSION.zip" +ssh lucy "mkdir -p $DEST_DIR && cp $STAGE/plugin.video.torttube.zip \ + $DEST_DIR/plugin.video.torttube-$VERSION.zip" + +ssh lucy "ls -la $DEST_DIR/" +echo ">>> done — install via Kodi: Settings > Add-ons > Install from zip" +echo " SMB path: smb://lucy/downloads/torttube/plugin.video.torttube-$VERSION.zip" diff --git a/sidecar/crates/torttube-sidecar/Cargo.toml b/sidecar/crates/torttube-sidecar/Cargo.toml index 2e67476..017df7d 100644 --- a/sidecar/crates/torttube-sidecar/Cargo.toml +++ b/sidecar/crates/torttube-sidecar/Cargo.toml @@ -11,8 +11,11 @@ name = "torttube-sidecar" path = "src/main.rs" [dependencies] -# Tier 1 — native Rust Innertube -rustypipe = "0.11" +# Tier 1 — native Rust Innertube. +# default-features=false skips rustypipe's default-tls which pulls in +# native-tls / openssl — we use rustls so cross-compile to aarch64-musl +# stays openssl-free. +rustypipe = { version = "0.11", default-features = false, features = ["rustls-tls-webpki-roots"] } # Tier 2 + 3 — yt-dlp subprocess shell-out (no library, just std::process)