"""Automatische Erneuerung des MakeMKV-Beta-Keys. Der kostenlose MakeMKV-Beta-Key (Forum-Thread "MakeMKV is free while in beta", t=1053) wechselt etwa monatlich und laeuft zum Monatsende ab. Ohne gueltigen Key faellt Blu-ray/UHD-Ripping in den 30-Tage-Testmodus; DVD-Ripping bleibt frei. Bisher musste der Key von Hand nachgetragen werden (UI: Einstellungen -> System). Dieses Modul holt den aktuellen Key vom Forum und schreibt ihn in die Settings (makemkvAppKey). Der Worker liest den Key VOR jedem Rip aus den Settings (docker/worker/tasks.py) und schreibt settings.conf -> die Erneuerung wirkt ohne Rebuild/Neustart, ab dem naechsten Rip. Wichtig: Das ist die kostenlose, oeffentliche Beta-LIZENZ der Software selbst — NICHT das Entschluesseln oder Verteilen von Disc-Schluesseln. Den AACS-Schluessel zieht MakeMKV via LibreDrive ohnehin selbst aus dem Laufwerk. Quelle/Format dokumentiert (AGENTS Regel D — nicht geraten): - Forum-Thread: https://forum.makemkv.com/forum/viewtopic.php?t=1053 - Key-Format: "T-" + langer String aus [A-Za-z0-9_] (typ. ~66 Zeichen; die exakte Laenge variiert je Key, darum kein fixes Limit, sondern mind. 50, greedy). """ import asyncio import re FORUM_URL = "https://forum.makemkv.com/forum/viewtopic.php?t=1053" KEY_RE = re.compile(r"T-[A-Za-z0-9_]{50,}") _HEADERS = {"User-Agent": "rippy-makemkv-key-refresh/1.0"} def extract_key(html: str) -> str | None: """Zieht den Beta-Key ("T-...") aus dem Forum-HTML. Pure Funktion -> testbar.""" treffer = KEY_RE.search(html or "") return treffer.group(0) if treffer else None def fetch_current_key(timeout: int = 15) -> str | None: """Holt den aktuellen Beta-Key vom MakeMKV-Forum. None bei Fehler/kein Treffer.""" import requests # lazy: haelt extract_key + den Test dependency-frei antwort = requests.get(FORUM_URL, headers=_HEADERS, timeout=timeout) antwort.raise_for_status() return extract_key(antwort.text) def _apply_key(key: str) -> bool: """Merge den Key in die Settings. save_settings ueberschreibt das GANZE JSON, darum erst lesen, dann setzen. Rueckgabe True, wenn sich der Key geaendert hat.""" import db einstellungen = db.get_settings() if (einstellungen.get("makemkvAppKey") or "").strip() == key: return False einstellungen["makemkvAppKey"] = key db.save_settings(einstellungen) return True async def refresh_once() -> bool: """Einmal holen + anwenden. Best-effort, loggt in die App-Logs. True = geaendert.""" import db key = await asyncio.to_thread(fetch_current_key) if not key: db.add_log("warning", "makemkv-key", "Aktueller Beta-Key nicht im Forum gefunden (Format geaendert?).") return False geaendert = await asyncio.to_thread(_apply_key, key) if geaendert: db.add_log("success", "makemkv-key", f"MakeMKV-Beta-Key automatisch erneuert (…{key[-6:]}).") return geaendert async def refresh_loop(intervall_stunden: int = 24, start_verzoegerung_s: int = 60) -> None: """Periodische Erneuerung (Default: taeglich, damit ein Monatswechsel nie verpasst wird). Fehler werden geloggt, aber nie geworfen -> die API laeuft weiter.""" import db await asyncio.sleep(start_verzoegerung_s) while True: try: await refresh_once() except Exception as exc: db.add_log("warning", "makemkv-key", f"Key-Refresh fehlgeschlagen: {exc!r}") await asyncio.sleep(max(3600, intervall_stunden * 3600))