feat(transcode): ein HandBrake-Preset je Disc-Typ statt eines fuer alles
Ampel / ampel (push) Successful in 29s
Ampel / ampel (push) Successful in 29s
Rueckfrage des Commanders beim ersten echten UHD-Rip: "merkt Rippy, dass es
eine UHD ist, und nimmt direkt das 4K-Preset?" Antwort war nein. Die
Kompression fragte den Disc-Typ ueberhaupt nicht:
preset = einstellungen.get("transcodePreset") or DEFAULT_HB_PRESET
Live eingestellt war "HQ 1080p30 Surround". Der gerade laufende Akira-Rip
waere also verlustfrei in 4K gerippt und danach auf 1080p heruntergerechnet
worden - und mit keepOriginal=False waere der 4K-Rohschnitt danach geloescht
worden. Umgekehrt wurde eine DVD auf 1080p hochskaliert, was nichts bringt.
- preset_fuer(disc_type, einstellungen) in ripping.py, pure und getestet.
Reihenfolge: Preset des Disc-Typs -> allgemeines transcodePreset ->
DEFAULT_HB_PRESET. Bestandsinstallationen aendern ihr Verhalten NICHT,
solange die neuen Felder nicht gespeichert sind.
- Drei Einstellungen: transcodePresetDvd / transcodePresetBluray /
transcodePresetUhd. transcodePreset bleibt als Rueckfall bestehen.
- transcode_files holt den Disc-Typ aus dem Job-Datensatz und schreibt ihn
mit ins Log ("Disc-Typ 'uhd', Preset '...'").
- UI: drei Auswahlfelder statt einem, mit Klartext dazu, warum eine 4K-UHD
auf ein 2160p-Preset gehoert.
Preset-Namen stammen aus "HandBrakeCLI --preset-list" im Worker-Image
(HandBrake 1.6.1) - nicht aus dem Kopf (AGENTS Regel D):
H.265 MKV 2160p60 4K, HQ 2160p60 4K HEVC Surround,
Super HQ 2160p60 4K HEVC Surround, H.265 MKV 1080p30, HQ 1080p30 Surround,
Super HQ 1080p30 Surround, H.265 MKV 576p25, H.265 MKV 480p30,
HQ 576p25 Surround.
Sofortmassnahme am laufenden Job (auf Ansage des Commanders): keepOriginal
auf True gesetzt - nur dieses eine Feld, gegengeprueft dass kein anderer
Schluessel veraendert wurde. Damit ueberlebt der 4K-Rohschnitt die
Kompression.
ACHTUNG - Deploy bewusst NICHT ausgefuehrt: docker compose up -d --build
wuerde den Worker-Container neu erstellen und den laufenden Akira-Rip
abbrechen. Erst nach Abschluss des Jobs deployen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -225,6 +225,33 @@ def check_handbrake_installed() -> bool:
|
||||
|
||||
DEFAULT_HB_PRESET = "H.265 MKV 1080p30"
|
||||
|
||||
# Ein Preset je Disc-Typ (Befund 25.07.2026). Vorher galt EIN Preset für
|
||||
# alles: eine 4K-UHD wurde damit auf 1080p heruntergerechnet und eine DVD auf
|
||||
# 1080p hochskaliert — beides Unsinn, und beim ersten UHD-Rip wäre die 4K-
|
||||
# Auflösung still verlorengegangen. Den Disc-Typ kennt Rippy ohnehin.
|
||||
PRESET_SCHLUESSEL = {
|
||||
"dvd": "transcodePresetDvd",
|
||||
"bluray": "transcodePresetBluray",
|
||||
"uhd": "transcodePresetUhd",
|
||||
}
|
||||
|
||||
|
||||
def preset_fuer(disc_type: str, einstellungen: dict) -> str:
|
||||
"""Welches HandBrake-Preset gilt für diesen Disc-Typ? (pure, testbar)
|
||||
|
||||
Reihenfolge: Preset des Disc-Typs → allgemeines transcodePreset →
|
||||
DEFAULT_HB_PRESET. Diese Kette hält Bestandsinstallationen am Leben, in
|
||||
denen nur `transcodePreset` gesetzt ist: solange der Nutzer die neuen
|
||||
Felder nicht speichert, ändert sich sein Verhalten nicht.
|
||||
"""
|
||||
einstellungen = einstellungen or {}
|
||||
schluessel = PRESET_SCHLUESSEL.get(disc_type or "")
|
||||
if schluessel:
|
||||
eigen = (einstellungen.get(schluessel) or "").strip()
|
||||
if eigen:
|
||||
return eigen
|
||||
return (einstellungen.get("transcodePreset") or "").strip() or DEFAULT_HB_PRESET
|
||||
|
||||
|
||||
def build_handbrake_cmd(input_path: str, output_path: str, preset: str = DEFAULT_HB_PRESET) -> list:
|
||||
"""Baut das HandBrake-Kommando für die Kompressions-Stufe (pure Funktion).
|
||||
|
||||
@@ -35,11 +35,11 @@ except ImportError: # Windows: kein fcntl
|
||||
disc_size_bytes = None
|
||||
|
||||
from ripping import (
|
||||
DEFAULT_HB_PRESET,
|
||||
RIP_OUTPUT_DIR,
|
||||
RipAbbruch,
|
||||
lies_datei_dauer,
|
||||
lies_titel_info,
|
||||
preset_fuer,
|
||||
rip_cd,
|
||||
rip_video,
|
||||
run_handbrake,
|
||||
@@ -565,14 +565,19 @@ def transcode_files(self, job_id: str, raw_dir: str, final_dir: str):
|
||||
return ergebnis
|
||||
|
||||
einstellungen = db.get_settings()
|
||||
preset = einstellungen.get("transcodePreset") or DEFAULT_HB_PRESET
|
||||
# Preset nach Disc-Typ (Befund 25.07.2026): vorher lief JEDE Quelle durch
|
||||
# dasselbe Preset — eine 4K-UHD wurde damit auf 1080p heruntergerechnet.
|
||||
job = db.get_job(job_id) or {}
|
||||
disc_type = job.get("disc_type") or ""
|
||||
preset = preset_fuer(disc_type, einstellungen)
|
||||
original_behalten = einstellungen.get("keepOriginal", False)
|
||||
|
||||
os.makedirs(final_dir, exist_ok=True)
|
||||
db.update_job(job_id, status="transcoding", progress=0, error=None)
|
||||
db.add_log(
|
||||
"info", "worker",
|
||||
f"Job {job_id}: Kompression gestartet ({len(quellen)} Datei(en), Preset '{preset}')",
|
||||
f"Job {job_id}: Kompression gestartet ({len(quellen)} Datei(en), "
|
||||
f"Disc-Typ '{disc_type or 'unbekannt'}', Preset '{preset}')",
|
||||
)
|
||||
|
||||
anzahl = len(quellen)
|
||||
|
||||
@@ -138,3 +138,43 @@ def test_abcde_config_enthaelt_zielverzeichnis():
|
||||
assert "INTERACTIVE=n" in inhalt
|
||||
finally:
|
||||
os.unlink(pfad)
|
||||
|
||||
|
||||
def test_preset_fuer_nimmt_das_preset_des_disc_typs():
|
||||
# Befund 25.07.2026: vorher galt EIN Preset fuer alles — eine 4K-UHD wurde
|
||||
# damit auf 1080p heruntergerechnet, und beim ersten echten UHD-Rip waere
|
||||
# die 4K-Aufloesung still verlorengegangen.
|
||||
from ripping import preset_fuer
|
||||
|
||||
einstellungen = {
|
||||
"transcodePreset": "HQ 1080p30 Surround",
|
||||
"transcodePresetDvd": "H.265 MKV 576p25",
|
||||
"transcodePresetBluray": "H.265 MKV 1080p30",
|
||||
"transcodePresetUhd": "H.265 MKV 2160p60 4K",
|
||||
}
|
||||
assert preset_fuer("uhd", einstellungen) == "H.265 MKV 2160p60 4K"
|
||||
assert preset_fuer("bluray", einstellungen) == "H.265 MKV 1080p30"
|
||||
assert preset_fuer("dvd", einstellungen) == "H.265 MKV 576p25"
|
||||
|
||||
|
||||
def test_preset_fuer_faellt_auf_das_allgemeine_preset_zurueck():
|
||||
# Bestandsinstallationen kennen die drei neuen Felder nicht. Solange der
|
||||
# Nutzer sie nicht speichert, MUSS sich sein Verhalten nicht aendern.
|
||||
from ripping import preset_fuer
|
||||
|
||||
alt = {"transcodePreset": "HQ 1080p30 Surround"}
|
||||
assert preset_fuer("uhd", alt) == "HQ 1080p30 Surround"
|
||||
assert preset_fuer("dvd", alt) == "HQ 1080p30 Surround"
|
||||
# Leere Zeichenkette zaehlt als "nicht gesetzt" (leeres Select-Feld im UI)
|
||||
assert preset_fuer("uhd", {"transcodePresetUhd": " ", "transcodePreset": "X"}) == "X"
|
||||
|
||||
|
||||
def test_preset_fuer_ohne_einstellungen_nimmt_den_eingebauten_standard():
|
||||
# Unbekannter Disc-Typ, leere oder fehlende Einstellungen: nie None, nie
|
||||
# Absturz — sonst stirbt die Kompression an einem leeren --preset-Argument.
|
||||
from ripping import DEFAULT_HB_PRESET, preset_fuer
|
||||
|
||||
assert preset_fuer("uhd", {}) == DEFAULT_HB_PRESET
|
||||
assert preset_fuer("", None) == DEFAULT_HB_PRESET
|
||||
assert preset_fuer(None, {}) == DEFAULT_HB_PRESET
|
||||
assert preset_fuer("cd", {"transcodePresetUhd": "egal"}) == DEFAULT_HB_PRESET
|
||||
|
||||
Reference in New Issue
Block a user