Floatplane channel plugin for Jellyfin 10.10.x: browse, HLS proxy, cookie session, CI release
Some checks failed
release / build-release (push) Has been cancelled

- IChannel: creators -> sub-channels -> video posts (public listing API, search mapped)
- IRequiresMediaInfoCallback -> signed proxy URLs; stream controller rewrites HLS so the
  AES-128 key is fetched server-side with the session cookie, segments stay on the CDN
- delivery/info cached per token lifetime and throttled (~2/min)
- config page: paste sails.sid + validate, extra creators, quality, search test
- scripts/package.sh -> plugin folder + zip + md5 + manifest.json; Gitea release workflow
- research docs, HAR analysis (HAR itself gitignored), install-pi5.sh

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-25 09:42:27 +01:00
commit 9e33a343ab
162 changed files with 52004 additions and 0 deletions

113
README.md Normal file
View File

@@ -0,0 +1,113 @@
# 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_<ver>/ (DLL + meta.json), build/floatplane_<ver>.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