fix(windows): Rippen ging nicht — drei Container-Annahmen im Rip-Pfad

Commander: „Das Rippen der disk funktioniert nicht. makemkvcon endete mit
Code 11 — letzte Meldung: Das Öffnen der Disk schlug fehl — keine MKV-Datei
entstanden"

In diesem einen Satz steckten drei Fehler. Alle drei an seinem Laufwerk
gemessen, nicht hergeleitet.

## 1. Die Quellenangabe — das war der Abbruch

    dev:\.\G:  MSG:2024 „Unknown device" -> MSG:5010 Öffnen schlug fehl
    dev:G:      68 TINFO-Zeilen, „Die Aufgabe wurde erfolgreich abgeschlossen"

`f"dev:{device_path}"` stand an VIER Stellen. Unter Linux richtig (/dev/sr0),
unter Windows nicht: `rippy.drives.windows` meldet den Geraete-Namensraum
`\.\G:`, den `CreateFileW` fuer die IOCTLs braucht — MakeMKV kennt ihn
nicht.

## 2. „Ö" statt „Ö"

`Ö` als UTF-8 (C3 96), gelesen als cp1252. `subprocess` stand auf `text=True`
ohne `encoding=`, nahm also die Gebietsschema-Kodierung; Rippy stellt seine
Konsole beim Start aber auf UTF-8 (sonst stirbt der Start an einer
Umlaut-Logzeile). Direkt gemessen kommen die Bytes je nach Umgebung als UTF-8
ODER cp1252 — deshalb wird jetzt bestimmt statt behauptet.

## 3. Die Ursache fehlte im Fehlertext

Gesucht wurde nach ENGLISCHEN Textbausteinen („evaluation period has
expired"). Auf einem deutschen Windows trifft keiner. Jetzt zaehlen die
Meldungs-NUMMERN, die sprachunabhaengig sind.

⚠️ Beim ersten Anlauf hatte ich 5021 aus dem Kopf mit „Volume-Key unbekannt"
beschriftet — falsch. Meine eigene Ausgabe zeigte nur meine Beschriftung
statt MakeMKVs Text, sodass es fast durchgegangen waere. Der echte Wortlaut
steht jetzt im Code.

## Nebenbefund: der Beta-Key lag zweimal am falschen Ort

`makemkv_daten.DATEN_DIR` war fest `/root/.MakeMKV`. Nach der Reparatur des
Ordners blieb MSG:5051 trotzdem stehen — weil MakeMKV unter Windows seine
Einstellungen in der REGISTRY haelt (HKCU\Software\MakeMKV), nicht in einer
settings.conf. Nachgesehen statt vermutet.

Und der wichtigste Teil davon: **Ein abgelehnter Schluessel ist schlimmer als
gar keiner.** Ohne Key las MakeMKV die Disc noch (68 TINFO), mit dem
abgelehnten verweigerte es alles (0 Titel). Deshalb wird jetzt geprueft und
bei Ablehnung zurueckgenommen.

## Beweis

Echter Rip ueber `ripping.run_makemkv`, kuerzester Titel:

    Status success, Code 0, „Evangelion 2.22_t03.mkv" 217,9 MB
    5036 „Das Kopieren wurde abgeschlossen. 1 Titel wurden gesichert."

828 Tests gruen, ruff sauber.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-29 13:52:06 +02:00
co-authored by Claude Opus 5
parent 73ed104bbc
commit 954f293871
9 changed files with 693 additions and 39 deletions
+18 -11
View File
@@ -47,7 +47,7 @@ import time
import requests
from rippy import store as db
from rippy.rip import makemkv_daten
from rippy.rip import makemkv_aufruf, makemkv_daten
import medien
from rippy.core import notify
@@ -243,17 +243,24 @@ def _makemkv_key_anwenden(einstellungen: dict) -> None:
key = (einstellungen.get("makemkvAppKey") or "").strip()
if not key:
return
pfad = os.path.join(makemkv_daten.DATEN_DIR, "settings.conf")
try:
os.makedirs(makemkv_daten.DATEN_DIR, exist_ok=True)
try:
with open(pfad, encoding="utf-8", errors="replace") as f:
alt = f.read()
except OSError:
alt = ""
with open(pfad, "w", encoding="utf-8", newline="\n") as f:
f.write(makemkv_daten.settings_conf_zusammenfuehren(alt, key))
except OSError as e:
# WOHIN, entscheidet der Rechner — unter Windows die Registry, sonst
# die settings.conf. Begruendung und Messung in
# `rippy.rip.makemkv_aufruf.key_ablegen`.
#
# UND nachsehen, ob MakeMKV ihn annimmt: Ein abgelehnter Schluessel
# ist schlimmer als gar keiner — ohne las MakeMKV die Disc noch, mit
# einem abgelehnten verweigerte es alles (gemessen 29.08.2026). Die
# paar Sekunden Rueckfrage sind nichts gegen einen Rip, der Stunden
# laeuft und sonst am ersten Titel scheitert.
from ripping import werkzeug
urteil = makemkv_aufruf.key_setzen_und_pruefen(key, werkzeug("makemkv"))
if not urteil["ok"]:
db.add_log("warning", "worker",
"MakeMKV nimmt den hinterlegten Beta-Key nicht an: %s"
% urteil["grund"])
except (OSError, ImportError) as e:
db.add_log("warning", "worker", f"MakeMKV-Key konnte nicht gesetzt werden: {e}")
+32 -11
View File
@@ -37,6 +37,8 @@ from rippy.drives.linux import ( # noqa: F401
)
from rippy.drives.linux import auswerfen_versuchen as wirf_disc_aus # noqa: F401
from rippy.platform.winlauf import OHNE_FENSTER
from rippy.rip.makemkv_aufruf import KRITISCHE_CODES, text_von
from rippy.rip.makemkv_aufruf import quelle as makemkv_quelle
from rippy.tools import katalog as werkzeuge
_NACKTER_NAME = {"makemkv": "makemkvcon", "handbrake": "HandBrakeCLI"}
@@ -102,7 +104,7 @@ def build_makemkv_cmd(device_path: str, output_dir: str, titel: str = "all") ->
"--noscan",
"--progress=-same",
"mkv",
f"dev:{device_path}",
makemkv_quelle(device_path),
str(titel),
output_dir,
]
@@ -257,11 +259,11 @@ def lies_titel_info(device_path: str, timeout: int = 300) -> tuple:
wäre eine Minute Wartezeit für nichts.
"""
ergebnis = subprocess.run(
[werkzeug("makemkv"), "-r", "--noscan", "info", f"dev:{device_path}"],
capture_output=True, text=True, timeout=timeout,
[werkzeug("makemkv"), "-r", "--noscan", "info", makemkv_quelle(device_path)],
capture_output=True, timeout=timeout, # binaer: siehe text_von
creationflags=OHNE_FENSTER,
)
ausgabe = ergebnis.stdout or ""
ausgabe = text_von(ergebnis.stdout)
return parse_titel_info(ausgabe), parse_stream_info(ausgabe)
@@ -330,13 +332,13 @@ def lies_titel_dauern(device_path: str, timeout: int = 300) -> dict:
"""Fragt die Titel-Laufzeiten der Disc ab (makemkvcon info, Robot-Mode)."""
try:
ergebnis = subprocess.run(
[werkzeug("makemkv"), "-r", "--noscan", "info", f"dev:{device_path}"],
capture_output=True, text=True, timeout=timeout,
[werkzeug("makemkv"), "-r", "--noscan", "info", makemkv_quelle(device_path)],
capture_output=True, timeout=timeout, # binaer: siehe text_von
creationflags=OHNE_FENSTER,
)
except (OSError, subprocess.TimeoutExpired):
return {}
return parse_tinfo_dauern(ergebnis.stdout or "")
return parse_tinfo_dauern(text_von(ergebnis.stdout))
def parse_scan_dauer(ausgabe: str) -> int:
@@ -730,8 +732,17 @@ def run_makemkv(device_path: str, output_dir: str, progress_cb=None, titel: str
build_makemkv_cmd(device_path, output_dir, titel),
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
# BINAER lesen und selbst dekodieren (siehe `text_von`):
# `text=True` nahm die Gebietsschema-Kodierung, makemkvcon
# schrieb aber UTF-8 — daraus wurde beim Commander
# „Das Öffnen der Disk schlug fehl".
#
# bufsize=0 statt 1: Zeilenpufferung gibt es im Binaermodus
# nicht (Python warnt und puffert doch). Ungepuffert kommt
# jede Fortschrittszeile sofort an — bei einem Rip, der
# Stunden laeuft, ist das der Unterschied zwischen einem
# Balken und einem eingefrorenen Fenster.
bufsize=0,
creationflags=OHNE_FENSTER,
)
@@ -741,6 +752,12 @@ def run_makemkv(device_path: str, output_dir: str, progress_cb=None, titel: str
# abgelaufen) steht Zeilen davor und ging im Fehlertext verloren
# (Befund 24.07., Summer-Wars-UHD).
kritische_meldungen = []
# Die Textmuster bleiben als Rueckfall fuer englische Installationen —
# die MELDUNGSNUMMERN sind aber die verlaessliche Quelle. Beim
# Commander meldet MakeMKV deutsch, also traf hier am 29.08.2026 kein
# einziges Muster: Im Fehler stand nur „Das Öffnen der Disk schlug
# fehl", waehrend die Ursache eine Zeile darueber („Der Testzeitraum
# ist abgelaufen", MSG 5051) unter den Tisch fiel.
KRITISCH = (
"volume key is unknown",
"evaluation period has expired",
@@ -748,7 +765,8 @@ def run_makemkv(device_path: str, output_dir: str, progress_cb=None, titel: str
"Too old version",
)
try:
for line in process.stdout:
for rohzeile in process.stdout:
line = text_von(rohzeile)
progress = get_progress_from_prgv(line)
if progress >= 0:
if progress_cb:
@@ -758,7 +776,10 @@ def run_makemkv(device_path: str, output_dir: str, progress_cb=None, titel: str
if meldung is None:
continue
code, letzte_meldung = meldung
if any(muster in letzte_meldung for muster in KRITISCH):
if code in KRITISCHE_CODES:
kritische_meldungen.append(
"%s (%s)" % (KRITISCHE_CODES[code], letzte_meldung.strip()))
elif any(muster in letzte_meldung for muster in KRITISCH):
kritische_meldungen.append(letzte_meldung)
if log_cb:
try: