Emby-kanalen som førsteklasses kilde: set/favorit virker uden YouTube-bibliotek
CD / deploy (push) Successful in 16s
CI / check (push) Successful in 24s

Biblioteket er nu valgfrit. Findes hverken kanal eller bibliotek, er svaret
ok=False, og fejler opslaget for en bruger, kastes der, så cyklus-sletningen
aldrig kører uden alle brugeres favoritter.

Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
This commit is contained in:
root
2026-10-06 21:37:07 +02:00
parent 6ee54c1248
commit 9df3c31e43
2 changed files with 46 additions and 43 deletions
+1 -1
View File
@@ -54,7 +54,7 @@
<div class="setgrid" style="margin-top:.8rem"> <div class="setgrid" style="margin-top:.8rem">
<label class="fld"><span>Karens efter set (dage)</span><input type="number" name="grace_days" min="0" max="365" value="{{ settings.grace_days }}"></label> <label class="fld"><span>Karens efter set (dage)</span><input type="number" name="grace_days" min="0" max="365" value="{{ settings.grace_days }}"></label>
<label class="fld"><span>Undertekst-sprog</span><input name="sub_langs" value="{{ settings.sub_langs }}"></label> <label class="fld"><span>Undertekst-sprog</span><input name="sub_langs" value="{{ settings.sub_langs }}"></label>
<label class="fld"><span>Emby-bibliotek</span><input name="library_name" value="{{ settings.library_name }}"></label> <label class="fld"><span>Emby-kanal/bibliotek</span><input name="library_name" value="{{ settings.library_name }}"></label>
<label class="fld"><span>Kvalitet (yt-dlp -f)</span><input name="quality" value="{{ settings.quality }}"></label> <label class="fld"><span>Kvalitet (yt-dlp -f)</span><input name="quality" value="{{ settings.quality }}"></label>
</div> </div>
<p style="margin-top:1rem;display:flex;gap:.6rem;flex-wrap:wrap"> <p style="margin-top:1rem;display:flex;gap:.6rem;flex-wrap:wrap">
+45 -42
View File
@@ -1153,34 +1153,32 @@ def _marked_played(ud: dict) -> bool:
return bool((ud or {}).get("Played")) return bool((ud or {}).get("Played"))
async def _kanal_brugerdata(c, users: list, merged: dict) -> None: async def _kanal_brugerdata(c, users: list, merged: dict, navn: str) -> bool:
"""Læg set/favorit fra Emby-kanalen "YouTube" (kanal-plugin'et) oven i """Læg set/favorit fra Emby-kanalen (kanal-plugin'et) ind i `merged`, nøglet på
biblioteket for samme fil, så slettereglen og fredningen virker uanset hvor filens sti. Kanalens elementer bærer video-id'et i ProviderIds["youtube"]; stien
videoen blev set. Kanalens elementer bærer video-id'et i ProviderIds["youtube"]; findes via indekset. Samme video ligger i "Alle videoer" og i playlister, og
stien findes via indekset. Mangler kanalen eller indekset, sker der intet. kopierne deler brugerdata, så det er ligegyldigt hvilken vi læser.
(Plugin'et synkroniserer selv kanal og bibliotek; det her er et sikkerhedsnet.)"""
False hvis kanalen eller indekset mangler. Fejler opslaget for en bruger, kastes
der: uden alle brugeres favoritter må slettereglen ikke køre."""
try: try:
with open(indeks_sti(), encoding="utf-8") as f: with open(indeks_sti(), encoding="utf-8") as f:
stier = {vid: os.path.realpath(v["path"]) for vid, v in json.load(f)["videos"].items()} stier = {vid: os.path.realpath(v["path"]) for vid, v in json.load(f)["videos"].items()}
except (OSError, ValueError, KeyError): except (OSError, ValueError, KeyError):
return return False
try: if not users:
kanaler = await _emby(c, "/Channels", UserId=users[0]) if users else {} return False
except Exception: # noqa: BLE001 kanaler = await _emby(c, "/Channels", UserId=users[0])
return
kanal = next((k for k in (kanaler.get("Items") or []) kanal = next((k for k in (kanaler.get("Items") or [])
if (k.get("Name") or "").lower() == "youtube"), None) if (k.get("Name") or "").lower() == navn.lower()), None)
if not kanal: if not kanal:
return return False
for uid in users: for uid in users:
try: data = await _emby(c, f"/Users/{uid}/Items", ParentId=kanal["Id"],
data = await _emby(c, f"/Users/{uid}/Items", ParentId=kanal["Id"], Recursive="true", IncludeItemTypes="Video", Fields="ProviderIds,UserData")
Recursive="true", Fields="ProviderIds,UserData")
except Exception: # noqa: BLE001
continue
for it in data.get("Items", []): for it in data.get("Items", []):
pid = it.get("ProviderIds") or {} pid = it.get("ProviderIds") or {}
vid = pid.get("YouTube") or pid.get("Youtube") or pid.get("youtube") vid = pid.get("youtube") or pid.get("YouTube") or pid.get("Youtube")
sti = stier.get(vid or "") sti = stier.get(vid or "")
if not sti: if not sti:
continue continue
@@ -1191,38 +1189,43 @@ async def _kanal_brugerdata(c, users: list, merged: dict) -> None:
m["played"] = True m["played"] = True
if ud.get("IsFavorite"): if ud.get("IsFavorite"):
m["favorite"] = True m["favorite"] = True
return True
async def emby_yt_items() -> dict: async def emby_yt_items() -> dict:
"""Alle videoer i YouTube-biblioteket med set/favorit aggregeret over ALLE brugere.""" """Alle videoer i Emby med set/favorit aggregeret over ALLE brugere.
Kilden er kanalen fra kanal-plugin'et og/eller et bibliotek med samme navn
(standard "YouTube"). Findes ingen af dem, er svaret ok=False, så intet slettes."""
settings = get_settings() settings = get_settings()
navn = settings.get("library_name") or "YouTube"
out = {"ok": False, "items": [], "error": ""} out = {"ok": False, "items": [], "error": ""}
try: try:
async with httpx.AsyncClient(timeout=45) as c: async with httpx.AsyncClient(timeout=45) as c:
lib = await _library_id(c, settings.get("library_name") or "YouTube")
if not lib:
out["error"] = (f"Fandt ikke Emby-biblioteket "
f"'{settings.get('library_name')}'")
return out
users = [u["Id"] for u in await _emby(c, "/Users")] users = [u["Id"] for u in await _emby(c, "/Users")]
merged = {} merged = {}
for uid in users: lib = await _library_id(c, navn)
data = await _emby(c, f"/Users/{uid}/Items", ParentId=lib, if lib:
Recursive="true", Fields="Path,UserData") for uid in users:
for it in data.get("Items", []): data = await _emby(c, f"/Users/{uid}/Items", ParentId=lib,
path = it.get("Path") or "" Recursive="true", Fields="Path,UserData")
if not path: for it in data.get("Items", []):
continue path = it.get("Path") or ""
key = os.path.realpath(path) if not path:
m = merged.setdefault(key, { continue
"id": it["Id"], "name": it.get("Name") or os.path.basename(path), key = os.path.realpath(path)
"path": key, "played": False, "favorite": False}) m = merged.setdefault(key, {
ud = it.get("UserData") or {} "id": it["Id"], "name": it.get("Name") or os.path.basename(path),
if _marked_played(ud): "path": key, "played": False, "favorite": False})
m["played"] = True ud = it.get("UserData") or {}
if ud.get("IsFavorite"): if _marked_played(ud):
m["favorite"] = True m["played"] = True
await _kanal_brugerdata(c, users, merged) if ud.get("IsFavorite"):
m["favorite"] = True
kanal = await _kanal_brugerdata(c, users, merged, navn)
if not lib and not kanal:
out["error"] = f"Fandt hverken Emby-kanalen eller -biblioteket '{navn}'"
return out
out["items"] = list(merged.values()) out["items"] = list(merged.values())
out["ok"] = True out["ok"] = True
except Exception as e: # noqa: BLE001 except Exception as e: # noqa: BLE001