docs(media): Jellyfin 12.4 needs the MediaBrowser header; the scan key is on the box

X-Emby-Token is refused for every key on 12.4 -- which is why this page
carried the false claim that no credential on the box could trigger a scan and
that the two Seerr keys were dead. The documented snippet now authenticates
with Authorization: MediaBrowser Token=..., reads 204 from /Library/Refresh,
and points at ~/.config/secrets/jellyfin (mirrored as JELLYFIN_API_KEY in the
ignored dankserver/.env). Law: prove a 401 against the ApiKeys table before
blaming the key.
This commit is contained in:
Frank committed 2026-10-30 20:39:51 -07:00
1 parent 09532e5216
commit b7b39cdc92
1 file changed
+22 -11
@@ -1,9 +1,9 @@
--- ---
type: reference type: reference
title: Media Relayout title: Media Relayout
description: "Census, plan and apply the movie library into Jellyfin format: the gates to read, and the rollback." description: "Census, plan and apply the movie library into Jellyfin format and move TV out of it: the gates to read, the refusals, and the rollback."
tags: [dankserver, media-server, jellyfin, plex, procedure] tags: [dankserver, media-server, jellyfin, plex, procedure]
timestamp: 2026-10-30T00:00:00Z timestamp: 2026-10-31T03:40:00Z
--- ---
# Media Relayout # Media Relayout
@@ -91,19 +91,23 @@ podman exec -i plex sh -c 'read -r T
done' </tmp/px.tok done' </tmp/px.tok
``` ```
[[Jellyfin]] has no credential on this box that can trigger a scan: the two keys [[Jellyfin]]'s scan key lives in `~/.config/secrets/jellyfin` (mode 600; the
in its database are Seerr's, unused since 2026-09-01, and answer 401 to every same value is `JELLYFIN_API_KEY` in the ignored `dankserver/.env`). It rides a
call. Mint one (*Dashboard → Advanced → API Keys*, Administrator) and store it in mode-600 curl config, never a header argument, and it must use the
`~/.config/secrets/jellyfin`, or press *Library → Scan All Libraries* in the web `MediaBrowser` form — Jellyfin 12.4 refuses a bare `X-Emby-Token` for any key:
UI. A scripted scan passes the key in a mode-600 curl config, never in a header
argument:
```bash ```bash
umask 077; printf 'header = "X-Emby-Token: %s"\n' "$(cat ~/.config/secrets/jellyfin)" >/tmp/jf.hdr umask 077
curl -s -K /tmp/jf.hdr -o /dev/null -w "jellyfin scan: %{http_code}\n" \ { printf 'header = "Authorization: MediaBrowser Token=\\"%s\\""\n' \
-X POST http://127.0.0.1:8096/Library/Refresh "$(cat ~/.config/secrets/jellyfin)"
echo 'url = "http://127.0.0.1:8096/Library/Refresh"'
echo 'request = "POST"'; } >/tmp/jf.hdr
curl -s -K /tmp/jf.hdr -o /dev/null -w "jellyfin scan: %{http_code}\n"
shred -u /tmp/jf.hdr
``` ```
`204` is acceptance; the scan then runs on the server's own schedule.
## TV in the movie library ## TV in the movie library
A box set that arrives in the movie share stays there until someone moves it: A box set that arrives in the movie share stays there until someone moves it:
@@ -180,6 +184,13 @@ A sidecar that shares an episode's own name (`.srt`, `.nfo`) moves with it.
free space, and `rename(2)` refuses it anyway. Trash goes beside the item — free space, and `rename(2)` refuses it anyway. Trash goes beside the item —
`/mnt/entropy/.AGENTTRASH/<date>-<topic>/` — so it costs one inode change and `/mnt/entropy/.AGENTTRASH/<date>-<topic>/` — so it costs one inode change and
rolls back exactly like the move that made it. rolls back exactly like the move that made it.
- **A Jellyfin 401 is the header, not the key.** 12.4 authenticates only
`Authorization: MediaBrowser Token="…"` and refuses a bare `X-Emby-Token` for
every key, valid or not. This page carried "no credential on this box can
trigger a scan, the two Seerr keys answer 401" for weeks because of that
header — the keys were innocent. Before believing a 401, prove the key against
the server's own table: `select count(*) from ApiKeys where AccessToken = ?`
on `jellyfin.db` opened `mode=ro&immutable=1`.
- **A file whose name carries no words is bonus material, not a folder.** - **A file whose name carries no words is bonus material, not a folder.**
`.Extra 1 ( 1H 27min multisub ).mkv` parses to `1`; a movie folder named `1` `.Extra 1 ( 1H 27min multisub ).mkv` parses to `1`; a movie folder named `1`
is not a movie. It becomes a review line telling a human to name the file. is not a movie. It becomes a review line telling a human to name the file.