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>
114 lines
5.0 KiB
Markdown
114 lines
5.0 KiB
Markdown
# 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
|