# jellyfin-plugin-floatplane A Jellyfin server plugin that exposes your [Floatplane](https://www.floatplane.com) subscriptions (creators → channels → videos, plus live streams) inside Jellyfin and all its clients, so Floatplane content shows up next to the rest of the library on the Pi5 server and plays through the normal Jellyfin players. **Status (2026-10-25):** browsing works (verified against a local Jellyfin 10.10.7); playback proxy is implemented but still needs a real session cookie to be verified. See [`doc/plan.md`](doc/plan.md) for milestones and [`doc/floatplane-api-notes.md`](doc/floatplane-api-notes.md) for the API. The plugin is a pure managed .NET assembly (AnyCPU IL), so one build runs on both `linux-x64` (dev box) and `linux-arm64` (the Pi5). ## Prerequisites | Target | What to install | |--------|-----------------| | Arch / Garuda (dev box) | `sudo pacman -S dotnet-sdk-8.0` (Jellyfin 10.10.x = .NET 8) — for Jellyfin 12.x use `dotnet-sdk` (.NET 10) | | Debian / Ubuntu | `apt install dotnet-sdk-8.0` from the Microsoft repo | | Pi5 (runtime only) | nothing — the plugin runs inside the existing `linuxserver/jellyfin` container | Optional, for local testing: Docker (to run a throw-away Jellyfin of the same version as the Pi5 with the plugin folder mounted). ## Build ```bash git clone https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane.git cd jellyfin-plugin-floatplane scripts/package.sh # → build/Floatplane_/ (DLL + meta.json), build/floatplane_.zip (+ .md5) ``` The VS Code `build` task (`Ctrl+Shift+B`) runs the same script. Plain `dotnet build` works too (`jellyfin-plugin-floatplane.sln`). ## Install **From the plugin repository (recommended):** Dashboard → Plugins → Repositories → add ``` https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane/releases/download/latest/manifest.json ``` then Catalog → Channels → Floatplane → Install → restart Jellyfin. **Manual, on the Pi5 (linuxserver image):** ```bash curl -fsSL https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane/releases/download/latest/install-pi5.sh | sh -s -- --dry-run # then without --dry-run; it installs into /srv/docker/jellyfin/data/plugins/ and restarts the container ``` **Manual, from a local build:** ```bash # linuxserver image keeps data under /config/data (container path); # host path on the Pi5: /srv/docker/jellyfin/data/plugins/ (infra-Doc services/media-stack.md) cp -r build/Floatplane_0.1.0.0 /srv/docker/jellyfin/data/plugins/ docker restart jellyfin ``` > The Pi5 runs Jellyfin **10.10.7** but `lscr.io/linuxserver/jellyfin:latest` > is already **12.3** (.NET 10). Pin the tag (`10.10.7ubu2404-ls80`) before > any `docker pull`, or the plugin becomes `NotSupported` overnight. Then Dashboard → Plugins → Floatplane → paste your `sails.sid` cookie (the page explains where to find it) → Validate → Save. A "Floatplane" channel appears under My Media: creators → sub-channels → videos. Without a cookie you can still browse creators listed under "Extra creators" (public data); playback needs the cookie. ## How it works (short) - `FloatplaneChannel` (`IChannel`) lists creators → sub-channels → posts from Floatplane's public listing API; ids are `creator:…`, `channel:…`, `post:…`. - On play, `IRequiresMediaInfoCallback` fetches the post (login) and hands Jellyfin signed URLs to the plugin's own `Floatplane/Stream/…` endpoints. - The stream endpoint calls `delivery/info` (cached, throttled to ~2/min), fetches the HLS playlist, and rewrites it: the AES-128 key URI points back at the plugin (which fetches the key with the cookie), segments go straight to the CDN. ffmpeg then remuxes (stream copy) for the client. - A scheduled task refreshes the catalogue and bumps the channel cache key. ## Test ```bash dotnet test # unit tests (models, HLS rewriter, API client with fixtures from the HAR) ``` Local end-to-end test with a disposable Jellyfin matching the Pi5 version: ```bash mkdir -p .jellyfin-test/config/data/plugins cp -r build .jellyfin-test/config/data/plugins/Floatplane_0.1.0.0 # writable copy, not :ro docker run --rm -p 8096:8096 -v $PWD/.jellyfin-test/config:/config \ lscr.io/linuxserver/jellyfin:10.10.7ubu2404-ls80 # logs: .jellyfin-test/config/log/log_*.log ; plugin status: Dashboard → Plugins ``` ## Docs - `doc/plan.md` — goal, stack, milestones, roadmap - `doc/floatplane-api-notes.md` — endpoints, auth, HLS/AES-128 playback flow - `doc/research/jellyfin-plugin-research.md` — plugin API research (10.10 vs 12.x, channels, proxy pattern, packaging, paths) - `doc/research/floatplane-api-research.md` — community API docs, OAuth device flow, DPoP, rate limits, other clients - `doc/research/reference-sources/` — snapshots of other clients' source for study (chaosflix is the template to follow) - `doc/har/` — the captured browser session (**gitignored, contains credentials**) - Gitea wiki (once the repo exists) — architecture and design decisions