claude 9e33a343ab
Some checks failed
release / build-release (push) Has been cancelled
Floatplane channel plugin for Jellyfin 10.10.x: browse, HLS proxy, cookie session, CI release
- 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>
2026-10-25 09:42:27 +01:00

jellyfin-plugin-floatplane

A Jellyfin server plugin that exposes your Floatplane 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 for milestones and 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

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):

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:

# 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

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:

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
Description
No description provided
Readme 1.5 MiB
Languages
C# 84.8%
HTML 10%
Shell 5.2%