From 69effb542def089c8553f6a1d4bb7faa8ea35191 Mon Sep 17 00:00:00 2001 From: Frank Date: Mon, 2 Nov 2026 04:47:16 -0800 Subject: [PATCH] docs(jellyfin): Shows is on providers, and the per-library door stays dead anyway MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `Shows` flip landed today (one field, proven in the server's own `LibraryOptions` answer), so the posture table no longer calls it a silent file index and `Music`/`Collections` are named as the remaining ones. Two corrections the same day's measurements force: - The per-library `POST /Items/{id}/Refresh` answered `204` and queued nothing both before the flip and after it — zero journal lines for ten minutes, and `Scan Media Library` kept `LastTaskResult=None, EndTimeUtc=None`, the never-run signature. The provider switch was never the reason that door is dead; only `POST /Library/Refresh` runs (204, then `Running` inside 15 s at 33 % CPU), and it walks every library, so a library-scoped pass has no door on this build. That paragraph also records the null-pair read that separates "never ran" from "just finished", and that query+body on the same call answers `400` with an empty body whatever the values. - The key law: a plugin owns its own config file under its own `` root and omits null settings, so TVDB's `` element never existed and `install` refused — which is what `MISSING key` had been reporting since October. PR #121 fixes the writer. The `Shows` art census replaces speculation with the split that matters: 2,908 of 3,558 series+season carry a primary, 624 have no provider id, 26 have an id and no poster. A `Download` campaign over them took 20 s and downloaded nothing, which is the measurement that says identification is the only lever left. --- serverAppConfigs-docs/Dankserver/Jellyfin.md | 60 +++++++++++++++----- 1 file changed, 45 insertions(+), 15 deletions(-) diff --git a/serverAppConfigs-docs/Dankserver/Jellyfin.md b/serverAppConfigs-docs/Dankserver/Jellyfin.md index 6f827ae..0a02a97 100644 --- a/serverAppConfigs-docs/Dankserver/Jellyfin.md +++ b/serverAppConfigs-docs/Dankserver/Jellyfin.md @@ -89,13 +89,16 @@ looks exactly like an unconfigured library. | Library | `CollectionType` | `EnableInternetProviders` | `EnableRealtimeMonitor` | |---|---|---|---| | `Movies` | movies | true (fixed 2026-11-02) | **false** | -| `Shows` | tvshows | **false** | true | +| `Shows` | tvshows | true (fixed 2026-11-02) | true | | `Music` | music | **false** | true | | `Collections` | boxsets | **false** | false | -`Shows` and `Music` are silent file indexes in the sense above: a refresh answers +`Music` and `Collections` are silent file indexes in the sense above: a refresh answers `204`, stores nothing, logs nothing. Any metadata or artwork work on either one -starts by flipping the switch, never by firing a scan. +starts by flipping the switch, never by firing a scan. `Shows` sat in that set +until 2026-11-02; the door is `POST /Library/VirtualFolders/LibraryOptions` with +the library's own `ItemId` (below), and `scripts/jellyfin-providers.sh verify` is +the one command that says which libraries are still in it. **The per-library option door on 12.x is `POST /Library/VirtualFolders/LibraryOptions`** — `GET` there answers `405`, which is how the route announces itself. The 10.x doors @@ -130,6 +133,15 @@ nothing to look up. Art coverage is capped by identification, not by the image d the items a campaign returns nothing for are the unidentified ones, and no image retry will change that. +**A `Download` campaign over a library that is already dressed does nothing in +seconds, and that is the measurement proving identification is the only lever +left.** Run over all 753 series and 2,805 seasons of `Shows` on 2026-11-02 it +classified 3,558 items in 20 s: 2,908 already carried a primary, 624 have no +provider id at all, 26 have an id and no poster in TMDb — zero downloads. The +artless remainder is not an image-door problem: it is the unnamed long tail (clip +collections, `(Warhammer TV)`-shaped directories), and the lever is folder names +or an identify pass, never another retry loop. + **Series and season art is fetched; episode stills are not, deliberately.** Every episode query is a fresh provider round-trip — nothing caches per episode — measured at ~11 s per item beside ~2 s per series on the same host, with one hit in five in a @@ -250,9 +262,17 @@ scripts/jellyfin-providers.sh verify # plugin + key LENGTHS, never key bytes - **The homelab TVDB key is a licensed key** (its JWT claims say `community_supported: false`), so it needs **no PIN** — the optional `TVDB_PIN=` line exists only for a subscriber-shaped key. -- **TVDB is installed and keyed but is not a metadata agent**: TMDb still - identifies everything. Enabling TVDB as a provider would re-fetch show - metadata across the library, so it is a deliberate, separate change. +- **TVDB is installed but is not a metadata agent**: TMDb still identifies + everything. Enabling TVDB as a provider would re-fetch show metadata across + the library, so it is a deliberate, separate change. +- **A plugin owns its own config file, under its own root, and omits null + settings** — so the element an installer wants to fill may simply not exist, + and its root is ``, not ``. TVDB's file had + no `` element at all, which is why `install` refused with + `no place to write ` from October until the anchor fix: the secret sat + valid in `provider-keys` the whole time, and `verify`'s `MISSING key` was this + fault, not a missing key. The writer now anchors on the file's own root and + still refuses by name when a file has none. - **The deployer places the keys before it restarts jellyfin** (`ensure_provider_keys` in `scripts/deploy-remote.sh`) and warns instead of failing when `provider-keys` is absent, so a host without it still deploys. @@ -302,15 +322,25 @@ anything. The fix is `systemctl --user restart jellyfin.service`, one POST after health answers, and then believe `ScheduledTasks` State at 130 % CPU, not the response code. -**The door that scans is `POST /Library/Refresh` (204), and `ScheduledTasks` is the -progress read.** Measured 2026-11-02 against a `Shows` library that had never been -identified: the per-library `POST /Items/{id}/Refresh?Recursive=true` answers `204` -and queues **nothing** — item census identical afterwards, `Scan Media Library` -idle, journal silent — and it is still an empty `204` after a service restart, so -that status code carries no information at all. `/Library/Refresh` walks every -library; within seconds the journal names folders (`…/TVShows/…`, -`EpisodeMetadataService`) and `GET /ScheduledTasks` reports -`Scan Media Library | Running | 75 %` while the process sits near 200 % CPU. +**The door that scans is `POST /Library/Refresh` (204), and `ScheduledTasks` is +the progress read.** A task that has never completed answers +`LastTaskResult=None, EndTimeUtc=None`, and that pair is what separates "never +ran" from "finished a moment ago" — the difference between a door that is dead +and one that merely answered `204`. + +**The per-library `POST /Items/{id}/Refresh` queues nothing, and the provider +switch is not the reason.** Measured twice on 2026-11-02 against `Shows`: before +the flip and again after it, the call answered `204`, the journal wrote zero lines +over the following ten minutes, and `Scan Media Library` kept its never-run +signature; a service restart changes nothing. `POST /Library/Refresh` does run — +`204`, then `Running` inside 15 s at 33 % CPU — but it walks every library, so on +this build a library-scoped metadata pass has no working door and the global one +is the only lever. + +**That endpoint takes its parameters from one source.** The documented query form +(empty body) and the JSON body form (no query) each parse; supplying both at once +answers `400` with an empty body whatever the values, and `ValidationFailure` is +rejected in the body while the query form accepts it. **`RefreshStatus` on the `GET /Library/VirtualFolders` record is not a progress read.** It stayed `Idle` through a scan that was verifiably running at 75 %. The