Videoerne lå fladt i et Emby-hjemmevideobibliotek: ingen kanaler, ingen
billeder, og datoen var hentetidspunktet, så sortering efter premieredato gav
den rækkefølge de blev hentet i.
Nu henter yt-dlp til en skjult .staging-mappe med info-JSON og miniature, og
organiser() lægger videoen i <Kanal>/Season ÅÅÅÅ/ som
"<Kanal> - sÅÅÅÅeMMDDnn - <Titel> [<id>]" med NFO (titel, beskrivelse,
upload-dato som premiere), -thumb.jpg og undertekster. Kanalen får tvshow.nfo,
plakat (profilbillede) og baggrund (banner). Afsnitsnummeret bygger på
upload-datoen, så Emby sorterer som på YouTube.
Sletning (manuel, cyklus og karens) går nu gennem én funktion, der også tager
NFO og billede og rydder tomme sæson- og kanalmapper. Playlister springer
private/slettede videoer over (--ignore-errors), og et job med hentede videoer
regnes som gennemført, selvom yt-dlp sluttede med en fejl.
yt_migrer.py flytter de eksisterende videoer ind i strukturen og genopretter
Emby-biblioteket som "Serier" med kun lokale data; Emby-status (set, favorit,
position) gemmes før og sættes på igen bagefter.
Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
Når portalen genstarter midt i en hentning, bliver yt-dlp dræbt uden en
fejltekst, og jobbet endte som "ukendt fejl". Et negativt returkode (signal)
hedder nu "afbrudt" og prøves igen som de andre midlertidige fejl.
Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
Test mod en video der fik 403: kun standardklienten gav 1080p; android_vr
faldt til 360p, og tv, web_safari, mweb og tv_simply gav ingen brugbare
formater. Nye forsøg bruger derfor standardklienten med 30 s og 2 min pause.
Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
Tre fejl, der forstærkede hinanden:
- Hver tilføjelse startede sin egen kø-tømning, så flere hentninger kørte
samtidig (og natlig timer kunne køre oveni). Mange samtidige hentninger fra
samme IP er netop det YouTube svarer 403 på. Køen har nu en fil-lås
(yt_koe.lock), så kun én kørsel henter, også på tværs af processer.
- yt-dlp gentager ikke selv en 403. Midlertidige fejl (403/429/timeout) prøves
nu igen op til to gange med pause og en anden YouTube-klient
(tv,web_safari og mweb). Permanente fejl prøves ikke igen.
- Et job der stod som "running", da portalen genstartede, blev hængende for
altid. Med låsen ved vi, at et sådant job er afbrudt, og det lægges i kø
igen ved næste kørsel.
Fejlede job har en "Prøv igen"-knap (og "Prøv alle fejlede igen"), og den
natlige kørsel opdaterer yt-dlp, før den henter.
Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
Hver række under "Hentede videoer" på /admin/yt har nu en Slet-knap (med
bekræftelse; nævner det, hvis videoen er favorit). Sletningen bruger samme
sikkerhedsnet som oprydningen: kun filer portalen selv har hentet, og
undertekster/miniature slettes med. Er det jobbets sidste fil, markeres jobbet
som slettet, og Emby-biblioteket opdateres. Video-id'et bliver i
abonnementets "seen", så en overvåget kanal ikke henter den igen.
Claude-Session: https://claude.ai/code/session_012LCTmU2ewo8tnM1dByYAoG
Playliste-abonnement henter nye videoer efterhånden som de lægges i playlisten.
Kanal-abonnement holder en rullende buffer: cycle X = de X nyeste videoer ligger
klar, og kommer der en ny, ryger den ældste ud.
X tæller kun kanal-abonnementets egne hentninger. Manuelle hentninger,
playliste-abonnementer og favoritter tæller ikke med — og favoritter slettes
aldrig, så en favoritering frigør en plads i cyklussen til næste video.
Tre ting der måtte designes udenom:
1. `seen` husker hvert video-id abonnementet nogensinde har hentet, også efter
videoen er slettet igen. Uden det ville set-reglen slette en video efter 7
dage, hvorefter kanalen stadig har den blandt sine nyeste og henter den igen
— i en uendelig løkke.
2. Playliste og kanal primer forskelligt ved oprettelse. En playliste kan have
hundredvis af videoer, og ønsket var NYE videoer, så første tjek markerer det
eksisterende som kendt uden at hente. En kanal henter derimod de X nyeste med
det samme; det er hele pointen med cycle X. Resten af bagkataloget markeres
som kendt, så den aldrig begynder at hente gammelt indhold.
3. enforce_cycles rører intet hvis Emby ikke svarer. Uden favorit-data kunne den
komme til at slette noget fredet — samme konservative holdning som mediaclean.
Downloads gemmer nu video-id og upload-dato, så cyklussen kan sortere efter hvor
ny videoen ER, ikke hvornår vi tilfældigvis hentede den.
Timeren kører i rækkefølgen: tjek abonnementer -> hent -> håndhæv cyklus ->
slet sete. Cyklussen skal håndhæves efter hentningen, ellers ser den et halvt
bibliotek.
Claude-Session: https://claude.ai/code/session_011WxG8pbLj61jYU1MMaD7VC
Nyt admin-modul /admin/yt: indsæt en YouTube-URL, og videoen hentes med yt-dlp
til Emby-biblioteket "YouTube" (/media/yt-offline) — klar til offline-brug.
Oprydningen er bevidst modsat mediaclean.py: dér tæller selv delvis afspilning
som "set" for at frede alt der er rørt, her slettes kun videoer der er eksplicit
markeret som set (Emby UserData.Played). Halvt sete videoer bliver liggende.
Favoritter slettes aldrig, og en favoritering i karensperioden freder igen.
Sikkerhedsnet: oprydningen rører kun filer portalen selv har hentet (registreret
med fuld sti i yt_state.json). Det eksisterende YT-bibliotek er urørligt, og
download-træet er lagt uden for det, så Emby ikke indekserer dobbelt.
Standardvalg:
- H.264/AAC foretrækkes frem for AV1. Pascal-GPU'en i CT100 kan ikke afkode AV1,
og mange afspillere kan det slet ikke offline. Koster ~1,8x diskplads.
- SponsorBlock klipper sponsorater ud og beholder resten som kapitler.
- Undertekster hentes hvis de findes, men er aldrig et krav: YouTube rate-limiter
undertekst-endpointet uafhængigt af videoen, så en 429 dér ville ellers vælte
hele hentningen. Fald-tilbage henter videoen uden undertekster.
- Metadata, miniature og kapitler indlejres, så Emby har noget at vise.
Alt kan redigeres i UI'et. Natlig timer (yt-offline.timer) tømmer køen og rydder op.
Claude-Session: https://claude.ai/code/session_011WxG8pbLj61jYU1MMaD7VC