jellyfin, extracted from the platform as a plugin

Eighth plugin out, and the second resurrection: like soulseek, hono.ts carried
the router import and its mount commented out since 2026-08-13, with the
permission reduced to api: [], so the server booted clean while every panel
request 404'd. jellyfin_servers was also absent from the drizzle barrel, so
bun db:push would have dropped it.

Easier than soulseek in one specific way: mountPrefix derives /jellyfin from
the appName, which is exactly what the router already hardcoded, so no frontend
call site needed repointing. Soulseek needed 47 because its appName and its old
prefix differed. The frontend also already routed through one helper, so there
were five call sites rather than forty-seven.

Self-contained, unlike transmission and soulseek: jellyfin_servers is a
registry this plugin owns outright — label, URL, encrypted token, one active —
because more than one Jellyfin is a thing someone has, and more than one
Transmission is not.

Carries its own .prettierignore for the vendored hls.mjs, rather than leaving a
stale rule behind in the platform naming a path that no longer exists.
This commit is contained in:
officer
2026-08-19 03:17:32 +00:00
commit a7f1a71785
34 changed files with 41581 additions and 0 deletions
+72
View File
@@ -0,0 +1,72 @@
# Jellyfin — the extraction record
Extracted from the platform on **2026-08-19**, the eighth plugin out, immediately after soulseek.
`plugins/EXTRACTING-A-PLUGIN.md` is the runbook.
## Also a resurrection
Like soulseek, and for the same reason: `hono.ts` carried both the router import and its
`PROTECTED_MOUNTS` entry commented out (`// plugin — switched off 2026-08-13`), and the permission had
been reduced to `api: []`. So `assertPermissionTotality` was satisfied, the server booted clean, and
every request from the panels 404'd. It had been dead for six days.
The same data hazard applied: `schema.ts` had `export * from './jellyfin/schema'` commented out, so
`jellyfin_servers` was absent from the drizzle barrel and `bun db:push` would have dropped it.
## What moved
| Was | Is now |
| --------------------------------------------------------- | --------------- |
| `src/servers/sidecar/jellyfin/*` (5 files) | `sidecar/*` |
| `src/servers/api/jellyfin/router.ts` | `api/router.ts` |
| `src/databases/officer_db/src/jellyfin/*` (3 files) | `db/*` |
| `src/workspaces/officerdev/src/apps/Jellyfin/*` (20 files, incl. `vendor/`) | `web/*` |
| `src/apps/officer-web/Screens/Dashboard/Jellyfin/*` | `web/layout.ts` |
## Easier than soulseek, in one specific way
The mount prefix is unchanged. `mountPrefix` derives `/jellyfin` from the appName, which is what the
router was already hardcoding — so **no frontend call site had to be repointed**. Soulseek needed 47,
because its appName (`soulseek`) and its old prefix (`slskd`) differed.
The frontend also already routed everything through one helper (`useJellyfinData.ts`), so there were
five call sites in total rather than forty-seven spread across every panel.
## It owns its data, unlike transmission and soulseek
`jellyfin_servers` is a **registry** — label, URL, encrypted token, one active — and it moved into this
plugin whole. That is the right shape here for the same reason `service_connections` is the right shape
there: more than one Jellyfin is a thing someone actually has, and more than one Transmission is not.
So this plugin is self-contained. No core table, no platform union to extend.
## Fixtures: fixed properly this time
`jellyfin` was the canonical app-store test fixture, and extracting it broke **18 tests**. That is the
fourth extraction in a row to break the same files — photos and transmission on 2026-08-18, slskd
earlier on 2026-08-19, jellyfin an hour later.
`installer.test.ts` now builds **literal** fixtures and asks the catalogue nothing:
```ts
const fullInstall: CatalogueEntry = { id: 'test-full', members: 'accounts', modes: [...], ... };
```
Those tests pin `planSteps` and `runInstall` — installer *logic*. A service leaving for `plugins/` says
nothing about whether the installer still plans the right steps, so it should never have been able to
break them. `catalogue.test.ts` remains the place where real entries are asserted well-formed.
`members.test.ts`, `catalogue.test.ts` and `preflight.test.ts` were re-anchored to `memos`, which has
jellyfin's exact shape (`members: 'accounts'`, `modes: ['existing', 'provisioned']`).
## Carried its own ignore rule
`.prettierignore` in the platform named `src/workspaces/officerdev/src/apps/Jellyfin/vendor/hls.mjs`.
That path no longer exists, so the rule moved into this repo's own `.prettierignore` rather than being
left behind as a stale line matching nothing.
## Not carried over
`composeTemplate: 'jellyfin'` and `modes: ['existing', 'provisioned']` died with the catalogue entry.
Like every plugin so far, this is **point-at-your-own-server only** until plugin Docker provisioning
exists.