From 4f0d34fc937be0e4ab97236dc0c465bf038a5f62 Mon Sep 17 00:00:00 2001 From: s8n Date: Mon, 11 May 2026 03:16:23 +0100 Subject: [PATCH] fix(home-layout): write to client='Jellyfin Web' not just 'emby' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Jellyfin 10.10.3 web client reads DisplayPreferences with the client name 'Jellyfin Web'. The legacy 'emby' value is read by older SDKs only — so writing only to 'emby' (the original script) updates the DB but has no visible effect on the web UI. The empty 'Jellyfin Web' doc falls back to factory defaults that include Next Up. Now patches all four per-client docs ('Jellyfin Web', 'emby', 'emby-mobile', 'emby-web') and ensures both Continue Watching (resume) and Latest Media are present so blank users don't fall back to defaults. Applied 2026-05-11 across prod (13 users x 4 clients) and dev (1x4). --- bin/set-home-layout.py | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/bin/set-home-layout.py b/bin/set-home-layout.py index afa3ff3..e6a0e9a 100755 --- a/bin/set-home-layout.py +++ b/bin/set-home-layout.py @@ -25,6 +25,7 @@ Token retrieval (admin): import json import os import sys +import urllib.parse import urllib.request JF_URL = os.environ.get("JF_URL") @@ -33,6 +34,12 @@ JF_TOKEN = os.environ.get("JF_TOKEN") if not JF_URL or not JF_TOKEN: sys.exit("set JF_URL and JF_TOKEN env vars") +# Jellyfin 10.10.3 web client uses 'Jellyfin Web' as the DisplayPreferences +# client name. The older 'emby' name is read by legacy SDKs only — writing +# only to 'emby' has no effect on the web UI. Patch every per-client doc to +# keep all consumers in sync. +CLIENTS = ["Jellyfin Web", "emby", "emby-mobile", "emby-web"] + def http(method, url, body=None): req = urllib.request.Request(url, method=method) @@ -46,8 +53,9 @@ def http(method, url, body=None): return r.status, (json.loads(text) if text else None) -def patch_user(user_id, username): - url = f"{JF_URL}/DisplayPreferences/usersettings?userId={user_id}&client=emby" +def patch_user_client(user_id, client): + q = urllib.parse.urlencode({"userId": user_id, "client": client}) + url = f"{JF_URL}/DisplayPreferences/usersettings?{q}" _, prefs = http("GET", url) cp = prefs.get("CustomPrefs") or {} @@ -60,6 +68,11 @@ def patch_user(user_id, username): sections[sections.index("none")] = "resume" else: sections = ["resume"] + sections[:9] + if "latestmedia" not in sections: + for i, s in enumerate(sections): + if s == "none": + sections[i] = "latestmedia" + break for i, s in enumerate(sections): cp[f"homesection{i}"] = s @@ -75,9 +88,12 @@ def main(): users = http("GET", f"{JF_URL}/Users")[1] print(f"{JF_URL} — {len(users)} users") for u in users: - changed, before, after = patch_user(u["Id"], u["Name"]) - mark = "CHANGED" if changed else " ok " - print(f" [{mark}] {u['Name']:<14} {before} -> {after}") + for client in CLIENTS: + changed, before, after = patch_user_client(u["Id"], client) + if changed: + print(f" [CHANGED] {u['Name']:<14} client={client:<14} {before} -> {after}") + else: + print(f" [ ok ] {u['Name']:<14} client={client:<14} {after}") if __name__ == "__main__":