"""Encoder-Fähigkeiten dieses Workers — ehrlich erkannt, nicht behauptet. Jeder Worker meldet beim Start, was auf SEINER Maschine wirklich verfügbar ist (Commander-Anforderung 23.07.: das UI zeigt an, WAS da ist). Ein Remote-GPU-Worker meldet sich hier genauso wie der eingebaute CPU-Worker. """ import os import re import shutil import subprocess def erkenne_encoder() -> list: """Liste der verfügbaren Encoder-Backends auf dieser Maschine.""" gefunden = ["cpu-x264", "cpu-x265"] # HandBrake-Software-Encoder, immer dabei # VAAPI: AMD (VCN) und Intel (QuickSync) melden sich über /dev/dri if os.path.exists("/dev/dri/renderD128"): gefunden.append("vaapi") # NVENC: NVIDIA-Treiber im Container sichtbar if shutil.which("nvidia-smi") or os.path.exists("/usr/lib/x86_64-linux-gnu/libnvidia-encode.so.1"): gefunden.append("nvenc") return gefunden def erkenne_ip() -> str: """Beste erratbare eigene IP (UDP-Route-Trick, KEIN echter Traffic). Ehrlicher Vorbehalt: im Docker-Bridge-Netz ist das die Container-IP — für die Maschinen-Zuordnung ist deshalb WORKER_NAME (Anzeigename in compose/.env) die verlässliche Größe; die IP ist Zusatz-Indiz. """ import socket try: s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect(("192.0.2.1", 9)) # TEST-NET — nur Routing, kein Paket nötig return s.getsockname()[0] finally: s.close() except OSError: return "" def werkzeug_versionen() -> dict: """Kern-Werkzeuge dieses Workers — fürs UI (Einstellungen → System). MakeMKV-Version kommt aus dem Build (ENV MAKEMKV_VERSION im Dockerfile) — makemkvcon hat keinen dokumentierten --version-Schalter (usage.txt). HandBrakeCLI --version ist dokumentiert (handbrake.fr/docs, CLI Options) und gibt z.B. "HandBrake 1.6.1" aus. """ import socket info = { # Zuordnung im UI: hostname (für den Celery-Online-Abgleich) + IP "hostname": socket.gethostname(), "ip": erkenne_ip(), } if shutil.which("makemkvcon"): info["makemkv"] = os.getenv("MAKEMKV_VERSION") or "installiert" if shutil.which("HandBrakeCLI"): try: aus = subprocess.run( ["HandBrakeCLI", "--version"], capture_output=True, text=True, timeout=15, ) treffer = re.search(r"HandBrake\s+([\w.]+)", (aus.stdout or "") + (aus.stderr or "")) info["handbrake"] = treffer.group(1) if treffer else "installiert" except (OSError, subprocess.TimeoutExpired): info["handbrake"] = "installiert" # Woher kommt der MakeMKV-Key? UI-Setting schlägt Env — ehrlich anzeigen. try: import db ui_key = (db.get_settings().get("makemkvAppKey") or "").strip() except Exception: ui_key = "" if ui_key: info["makemkv_key"] = "ui" elif os.getenv("MAKEMKV_APP_KEY"): info["makemkv_key"] = "env" else: info["makemkv_key"] = "keiner" # Sieht DIESER Worker eine KEYDB.cfg? Die API zeigt ihren eigenen Mount — # bei einem Remote-Worker kann das etwas ganz anderes sein, und nur das # Verzeichnis des rippenden Workers zählt (Befund 25.07.2026). # # Gemeldet wird die Angabe NUR, wenn das Datenverzeichnis hier wirklich # eingehängt ist (os.path.ismount). Ein Remote-Transcode-Worker benutzt # dasselbe Image — makemkvcon ist dort also vorhanden und der entrypoint # legt den Ordner an —, er bekommt den Mount aber nicht und rippt nie. # Ohne diese Prüfung trüge er im UI dauerhaft die Warnung "keine # KEYDB.cfg", obwohl ihn das gar nichts angeht. try: import makemkv_daten daten_dir = makemkv_daten.DATEN_DIR except Exception: daten_dir = "" if shutil.which("makemkvcon") and daten_dir and os.path.ismount(daten_dir): try: info["keydb"] = "ja" if makemkv_daten.keydb_status().get("vorhanden") else "nein" except Exception: info["keydb"] = "unbekannt" # Die entscheidende Zahl für 4K-UHD: wie viele Disc-Schlüssel kennt # dieser Worker? 0 heißt, dass jede unbekannte UHD-Disc scheitert — # makemkvcon holt sie unter Linux nie selbst (Befund 25.07.2026). try: info["schluessel"] = str(makemkv_daten.schluesselspeicher_status().get("schluessel", 0)) except Exception: info["schluessel"] = "unbekannt" return info