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
32 lines
1.0 KiB
Python
32 lines
1.0 KiB
Python
"""Natlig YouTube-kørsel (systemd-timer): tøm download-køen og ryd op efter sete videoer.
|
|
|
|
Køen tømmes også her — ikke kun fra web-ruten — så et job overlever en genstart af
|
|
portalen midt i en hentning.
|
|
"""
|
|
import asyncio
|
|
|
|
import ytdl
|
|
|
|
|
|
async def main():
|
|
if not ytdl.configured():
|
|
print("yt_job: ikke konfigureret (mangler EMBY-env, yt-dlp eller download-mappe)")
|
|
return
|
|
q = await ytdl.process_queue()
|
|
if q["done"] or q["failed"]:
|
|
print(f"yt_job: hentet={q['done']} fejlet={q['failed']}")
|
|
res = await ytdl.run_cycle(dry_run=False)
|
|
if res.get("error"):
|
|
print("yt_job FEJL:", res["error"])
|
|
return
|
|
print(f"yt_job: vores videoer={res['seen']} nye_paa_vej_ud={len(res['new'])} "
|
|
f"fredet={len(res['spared'])} slettet={len(res['deleted'])} "
|
|
f"frigjort={ytdl._human(res['freed'])} "
|
|
f"sletning={'til' if res['enabled'] else 'fra'}")
|
|
for e in res.get("errors", []):
|
|
print(" fejl:", e)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
asyncio.run(main())
|