From 28d0e8313074ee598a1676e4f52df51ce33b8799 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 13 Aug 2026 11:05:28 +0100 Subject: [PATCH] Fix the Jellyfin rename call: it takes a name, not an id POST /Library/VirtualFolders/Name is the odd one out in that controller -- most of /Library/VirtualFolders/* takes an id, and passing one here returns a bare "HTTP 400: Error processing request." that says nothing about why. Verified against 10.11.4: name -> 204. Also records that renaming re-ids the library, because Jellyfin derives the ItemId from the name. ytstream is unaffected because find_library matches on path -- confirmed by a full refresh-metadata over 257 NFOs with 0 proxy requests straight after the rename. Co-Authored-By: Claude Opus 5 --- deploy/retire-jellyfin-library.py | 15 +++++++++++++-- plan.md | 20 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/deploy/retire-jellyfin-library.py b/deploy/retire-jellyfin-library.py index 98fb6bf..4bbe16a 100755 --- a/deploy/retire-jellyfin-library.py +++ b/deploy/retire-jellyfin-library.py @@ -143,9 +143,20 @@ def main() -> None: print(f"==> deleted {old['Name']!r}") if renaming: + # `name`, NOT `id`. This endpoint is the odd one out -- most of + # /Library/VirtualFolders/* takes an id, and passing one here returns a + # bare "HTTP 400: Error processing request." that says nothing about why. + # Verified against Jellyfin 10.11.4 on 2026-08-13: name -> 204. request(key, "POST", "/Library/VirtualFolders/Name", - {"id": new["ItemId"], "newName": FINAL_NAME}) - print(f"==> renamed to {FINAL_NAME!r}") + {"name": new["Name"], "newName": FINAL_NAME}) + print(f"==> renamed {new['Name']!r} to {FINAL_NAME!r}") + # A library's ItemId is derived from its name, so renaming re-ids it -- + # measured, 98e74a0c… became 34f331a8…, which was the *deleted* library's + # id, because that one had this name. Nothing here caches an ItemId, and + # ytstream's own find_library() matches on path, so both survive it. Any + # future caller that stores an ItemId will not. + print(" note: the library's ItemId changed (Jellyfin derives it from " + "the name)") after = folders(key) print() diff --git a/plan.md b/plan.md index 6cdda48..a316a7e 100644 --- a/plan.md +++ b/plan.md @@ -1225,3 +1225,23 @@ Still worth keeping out of `rm`: `/var/lib/youtube-automate` (170 MB) holds the venv and `subs.db`, and `subs.db` is *not* in the repo — it is state, not code. It is already copied to `/var/lib/ytstream/youtube-automate-subs.db.archived-20260813`, so that directory is now safe to delete too, just not before checking that copy exists. + +### Jellyfin's rename endpoint takes a name, not an id — and re-ids the library + +`POST /Library/VirtualFolders/Name` is the odd one out in that controller: most of +`/Library/VirtualFolders/*` takes an `id`, and this one takes `name`. Passing an id +returns a bare `HTTP 400: Error processing request.` with nothing to say why. Verified +against Jellyfin 10.11.4, 2026-08-13: `name=…&newName=…` → `204`. + +Renaming **changes the library's ItemId**, because Jellyfin derives it from the name: +`98e74a0c…` became `34f331a8…` — which was the *deleted* library's id, since that one +had the name we renamed to. Consequences, checked rather than assumed: + +* `find_library()` matches on **path**, so ytstream is unaffected. `doctor` reports + `library 'YouTube'` and `refresh-metadata` re-read all 257 NFOs with **0 proxy + requests** immediately after the rename. +* Anything that ever caches a Jellyfin ItemId across a rename will break. Nothing + does today. Do not add one. + +End state: one library, `YouTube`, at `/disks/Plex/_ytstream`, 11 series and 257 +episodes. `/disks/Plex/YouTube` is no longer a library; its 9 files are still on disk.