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.
3.7 KiB
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:
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.