v0.2: All videos folder, shared watched state, release-order sorting, official icon, OAuth plumbing, docs
Some checks failed
release / build-release (push) Has been cancelled
Some checks failed
release / build-release (push) Has been cancelled
- per-creator "All videos" folder (release order across sub-channels); sub-channel copies use cpost: ids and UserDataMirror keeps played/resume state in sync - SortNameUpdater forces release-order sort names so every client lists newest first by default - official Floatplane icon as channel image (Primary only — a channel Thumb hid video thumbnails in Continue Watching) and as 16:9 plugin thumbnail; one-time image replacement on upgrade - OAuth device-flow login (FloatplaneAuthService + settings UI); needs a Floatplane client id; Turnstile verified domain-bound so password login inside Jellyfin is impossible - generic install.sh (linuxserver / official image / native), thumb.png in package + manifest - README rewritten for a general audience, doc/usage.md with screenshots, 60 tests Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
230
README.md
230
README.md
@@ -1,116 +1,150 @@
|
||||
# jellyfin-plugin-floatplane
|
||||
# Floatplane for Jellyfin
|
||||
|
||||
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.
|
||||
A [Jellyfin](https://jellyfin.org) server plugin that brings your
|
||||
[Floatplane](https://www.floatplane.com) subscriptions into Jellyfin.
|
||||
Creators, their sub-channels and every video appear as a **Floatplane**
|
||||
channel under *My Media* in the web client and in all Jellyfin apps, and
|
||||
play through the normal Jellyfin player — with watched state, resume,
|
||||
"Continue Watching", "Recently Added" and search, like any other library.
|
||||
|
||||
**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).
|
||||
## Features
|
||||
|
||||
## Prerequisites
|
||||
- **Creators → All videos → sub-channels.** Each creator you subscribe to
|
||||
gets an *All videos* folder (every sub-channel in release order, newest
|
||||
first) plus one folder per sub-channel (TechLinked, ShortCircuit, …).
|
||||
- **Real metadata**: thumbnail, description, duration, release date,
|
||||
sub-channel as *Genre*, creator as *Studio*.
|
||||
- **Newest first by default** in every Jellyfin client, without touching
|
||||
the sort menu.
|
||||
- **Watched/resume state is shared** between the *All videos* copy and the
|
||||
sub-channel copy of a video.
|
||||
- **Low-CPU playback**: the server remuxes Floatplane's H.264/AAC stream
|
||||
(stream copy, no re-encode). Quality levels show up as *Versions*.
|
||||
- **Home screen integration**: "Recently Added in Floatplane" and
|
||||
"Continue Watching" work out of the box.
|
||||
- **Browse without logging in**: public creators (e.g. `linustechtips`)
|
||||
can be listed by url-name; playback needs a logged-in session.
|
||||
- **Search test tool** for Floatplane's own search API on the settings
|
||||
page, and a scheduled task that refreshes the catalogue.
|
||||
|
||||
| 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).
|
||||
## Requirements
|
||||
|
||||
## Build
|
||||
| | |
|
||||
|---|---|
|
||||
| Jellyfin | **10.10.x** (plugin targetAbi `10.10.0.0`, .NET 8). Jellyfin 12.x needs a separate build (not available yet). |
|
||||
| Architecture | one AnyCPU DLL — runs unchanged on x64 and arm64 (Raspberry Pi 4/5 etc.). |
|
||||
| Floatplane | an account with an active subscription for playback. |
|
||||
|
||||
## Install
|
||||
|
||||
**From the plugin repository (recommended)**
|
||||
|
||||
1. Dashboard → Plugins → **Repositories** → **+**. Name it anything and use
|
||||
this URL:
|
||||
|
||||
```
|
||||
https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane/releases/download/latest/manifest.json
|
||||
```
|
||||
|
||||
2. Dashboard → Plugins → **Catalog** → section *Channels* → **Floatplane**
|
||||
→ **Install**.
|
||||
3. Restart Jellyfin. The plugin shows as *Active* under *My Plugins*.
|
||||
|
||||
Updates then appear in the catalog like any other plugin.
|
||||
|
||||
**With the install script** (Docker — linuxserver.io or official image — or
|
||||
a native install; detects the right plugin directory, verifies the checksum,
|
||||
restarts Jellyfin):
|
||||
|
||||
```bash
|
||||
curl -fsSL https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane/releases/download/latest/install.sh | sh -s -- --dry-run
|
||||
curl -fsSL https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane/releases/download/latest/install.sh | sh
|
||||
```
|
||||
|
||||
Environment overrides: `TAG` (`latest` or `vX.Y.Z`), `JELLYFIN_CONTAINER`
|
||||
(default `jellyfin`, empty = native), `JELLYFIN_CONFIG_DIR`, `PLUGINS_DIR`,
|
||||
`PUID`/`PGID`.
|
||||
|
||||
**Manually**: download `floatplane_<version>.zip` from the
|
||||
[releases](https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane/releases),
|
||||
unzip it into `<jellyfin data>/plugins/Floatplane_<version>/` and restart.
|
||||
|
||||
## Log in
|
||||
|
||||
Floatplane's password login requires a Cloudflare captcha that only works on
|
||||
floatplane.com, so the plugin cannot ask for your username and password.
|
||||
Two options, both on **Dashboard → Plugins → Floatplane**:
|
||||
|
||||
1. **Session cookie** (works today): log in at floatplane.com in a browser,
|
||||
copy the value of the `sails.sid` cookie (DevTools → Application/Storage
|
||||
→ Cookies), paste it into the settings page, press **Validate cookie**
|
||||
(shows your username and subscriptions) and **Save**.
|
||||
2. **Login with Floatplane** (OAuth device flow — the way Floatplane's own
|
||||
apps and clients like Hydravion or Wasserflug log in). This needs a
|
||||
client id issued by Floatplane; without one the button reports
|
||||
`invalid_client`.
|
||||
|
||||
Step-by-step instructions, every setting explained and troubleshooting:
|
||||
**[doc/usage.md](doc/usage.md)**.
|
||||
|
||||
## How it works (short)
|
||||
|
||||
- The channel lists creators → *All videos* / sub-channels → posts from
|
||||
Floatplane's public listing API (ids `creator:…`, `all:…`, `channel:…`,
|
||||
`post:…`, `cpost:…`).
|
||||
- On play, the plugin fetches the post details with your session and hands
|
||||
Jellyfin signed URLs to its own `Floatplane/Stream/…` endpoints.
|
||||
- The stream endpoint asks Floatplane for the stream info (cached 6 h,
|
||||
throttled to the ~2/min the API allows), downloads the HLS playlist and
|
||||
rewrites it: the AES-128 key is fetched through the plugin with your
|
||||
session, the video segments go straight to Floatplane's CDN. Jellyfin's
|
||||
ffmpeg then stream-copies the result to the client.
|
||||
- A scheduled task refreshes the catalogue and bumps the channel cache key
|
||||
so Jellyfin re-lists the folders.
|
||||
|
||||
## Development
|
||||
|
||||
```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)
|
||||
dotnet test # 59 unit tests (models, HLS rewriter, API client, catalogue)
|
||||
scripts/package.sh # → build/Floatplane_<ver>/ (DLL, meta.json, thumb.png), build/floatplane_<ver>.zip (+ .md5)
|
||||
scripts/package.sh 1.2.3.0 https://example.com/dl # versioned package + build/manifest.json
|
||||
```
|
||||
|
||||
The VS Code `build` task (`Ctrl+Shift+B`) runs the same script. Plain
|
||||
`dotnet build` works too (`jellyfin-plugin-floatplane.sln`).
|
||||
- Needs the .NET 8 SDK; the test host also needs the ASP.NET Core 8 shared
|
||||
runtime (Arch: `sudo pacman -S dotnet-sdk-8.0 aspnet-runtime-8.0`).
|
||||
- The VS Code `build` task (`Ctrl+Shift+B`) runs `scripts/package.sh`.
|
||||
- Local end-to-end test against a throw-away Jellyfin of the right version:
|
||||
|
||||
## Install
|
||||
```bash
|
||||
mkdir -p .jellyfin-test/config/data/plugins
|
||||
cp -r build/Floatplane_0.2.0.0 .jellyfin-test/config/data/plugins/ # writable copy
|
||||
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
|
||||
```
|
||||
|
||||
**From the plugin repository (recommended):** Dashboard → Plugins →
|
||||
Repositories → add
|
||||
- Every push to `master` runs the tests and publishes the rolling `latest`
|
||||
release (zip, md5, `manifest.json`, `install.sh`); a `vX.Y.Z` tag publishes
|
||||
a versioned release.
|
||||
|
||||
```
|
||||
https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane/releases/download/latest/manifest.json
|
||||
```
|
||||
More: [`doc/plan.md`](doc/plan.md) (milestones, roadmap),
|
||||
[`doc/floatplane-api-notes.md`](doc/floatplane-api-notes.md) (the API as
|
||||
observed), [`doc/research/`](doc/research/) (Jellyfin plugin + Floatplane
|
||||
API research and reference sources), and the
|
||||
[project wiki](https://gitea.brasse-pc.eu/brasse/jellyfin-plugin-floatplane/wiki).
|
||||
|
||||
then Catalog → Channels → Floatplane → Install → restart Jellyfin.
|
||||
## Credits & disclaimer
|
||||
|
||||
**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)
|
||||
```
|
||||
|
||||
The test host needs the ASP.NET Core 8 shared runtime (the plugin references
|
||||
Jellyfin's controllers): on Arch `sudo pacman -S aspnet-runtime-8.0`.
|
||||
|
||||
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
|
||||
Unofficial, not affiliated with or endorsed by Floatplane Media Inc.
|
||||
The plugin uses Floatplane's unofficial API documented by the community
|
||||
([jamamp/FloatplaneAPI](https://github.com/jamamp/FloatplaneAPI)) and
|
||||
borrows the channel + signed-proxy pattern from
|
||||
[chaosflix-jellyfin](https://github.com/praetorianer777/chaosflix-jellyfin).
|
||||
Use it with your own subscription, for personal viewing. The Floatplane
|
||||
name and logo belong to Floatplane Media Inc.
|
||||
|
||||
Reference in New Issue
Block a user