feat(tools): Werkzeuge finden, holen und aktuell halten — Grundlage fuer Windows
Ampel / ampel (push) Failing after 47s

WAS: rippy/tools/ mit katalog.py (finden + Version) und beschaffen.py
(herunterladen, installieren, Update-Stand). ripping.py und caps.py fragen
den Katalog statt shutil.which.

DER BEFUND, DER DAS NOETIG MACHT: Der Bestand suchte AUSSCHLIESSLICH mit
shutil.which(). Im Container stimmt das — dort liegen beide Werkzeuge in
/usr/local/bin. Unter Windows findet es NICHTS, auch wenn alles installiert
ist: Windows-Programme liegen in Program Files, nicht im PATH. Am
28.08.2026 auf dem Commander-PC nachgesehen — MakeMKV lag unter
"C:\Program Files (x86)\MakeMKV\makemkvcon64.exe", which sah es nicht.

Fuer einen Rippy, der unter Windows eigenstaendig arbeiten soll, ist das der
Unterschied zwischen "laeuft" und "laeuft nicht". Vorher/nachher gemessen:

  vorher   check_makemkv_installed() -> False   (obwohl installiert)
  nachher  check_makemkv_installed() -> True
           build_makemkv_cmd()[0]    -> C:\Program Files (x86)\MakeMKV\makemkvcon64.exe

SUCHREIHENFOLGE, mit Begruendung im Modul: eingestellt > Rippys eigener
Werkzeug-Ordner > PATH > bekannte Installationsorte > Uninstall-Zweig der
Registry. Rippys eigener Ordner steht VOR dem System, damit eine selbst
gepflegte Fassung eine alte Systeminstallation schlaegt.

VERSION: HandBrakeCLI kann --version. makemkvcon KANN DAS NICHT (usage.txt
kennt keinen solchen Schalter) — die Version kommt aus dem Uninstall-Zweig.
Dort steht bei MakeMKV zwar eine Version, aber InstallLocation ist LEER
(nachgesehen); deshalb wird der Ordner ersatzweise aus DisplayIcon bzw.
UninstallString abgeleitet. Der Test dafuer hat sofort einen Fehler
gefunden: '"C:\...\uninstall.exe" /S' liess sich mit einem blossen
strip('"') nicht aufloesen.

BESCHAFFUNG, an echter Hardware gemessen:
  HandBrake  GitHub-Release-API -> 1.11.2, HandBrakeCLI-1.11.2-win-x86_64.zip
             geladen, entpackt, gestartet: meldet sich als 1.11.2. 2,8 s.
  MakeMKV    makemkv.com antwortete mit HTTP 525 (Cloudflare) — auch mit
             Browser-Kennung. Dieselbe Sperre, die im Projekt schon den
             Docker-Bau lahmlegt. Deshalb: Abruf wird versucht, ein
             Fehlschlag wird im Klartext gemeldet, und eine selbst geholte
             Datei laesst sich weiterhin verwenden. Ein Update-Server, der
             heute antwortet, darf keine Startbedingung fuer morgen sein.

DREI FALLEN, GEGEN DIE ES TESTS GIBT:
- Die .sig-Datei liegt im Release direkt neben dem ZIP und ist 566 Bytes
  gross. Wer nur nach "win" filtert, laedt sie.
- "1.9.2" ist als Zeichenkette GROESSER als "1.11.2". Ein Update-Hinweis, der
  ab Version zehn dauerhaft in die Irre fuehrt — und genau dort ist
  HandBrake gerade.
- Eine Fehlerseite kommt oft mit HTTP 200. Sie darf keine funktionierende
  Installation ersetzen: erst laden, pruefen, dann tauschen.

Und: Ist die neueste Version unbekannt (Quelle tot), steht NICHT "Update
verfuegbar" da. Eine Nichtauskunft ist keine Aussage.

GEMESSEN: ruff sauber, 489 Tests gruen + 15 uebersprungen (vorher 456).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-28 11:12:21 +02:00
co-authored by Claude Opus 5
parent 059183c651
commit 288f9eeb8d
8 changed files with 1005 additions and 15 deletions
+20 -7
View File
@@ -12,6 +12,19 @@ import shutil
import subprocess
from rippy.platform.winlauf import OHNE_FENSTER
from rippy.tools import katalog as werkzeuge
def _hb() -> str:
"""Pfad zu HandBrakeCLI — "" wenn es nicht da ist.
Bis V2-4 stand hier `shutil.which("HandBrakeCLI")`. Das findet unter
Windows nichts, auch wenn HandBrake installiert ist (Program Files statt
PATH). Die Folge waere gewesen: Rippy meldet "keine Encoder gefunden" auf
einem Rechner, auf dem alles da ist — und die Encoder-Auswahl im UI bliebe
leer, ohne dass jemand den Grund saehe.
"""
return werkzeuge.finden("handbrake")
HB_ENCODER_KOPF = re.compile(r"^-e,\s*--encoder\b")
@@ -148,11 +161,11 @@ def parse_preset_liste(text: str) -> list:
def hole_handbrake_presets() -> list:
"""`HandBrakeCLI --preset-list` einmal abrufen (leer, wenn nicht installiert)."""
if not shutil.which("HandBrakeCLI"):
if not _hb():
return []
try:
aus = subprocess.run(
["HandBrakeCLI", "--preset-list"],
[_hb(), "--preset-list"],
capture_output=True, text=True, timeout=30,
creationflags=OHNE_FENSTER,
)
@@ -262,11 +275,11 @@ def cpu_merkmale() -> tuple:
def hole_handbrake_hilfe() -> str:
"""`HandBrakeCLI --help` einmal abrufen (leer, wenn nicht installiert)."""
if not shutil.which("HandBrakeCLI"):
if not _hb():
return ""
try:
aus = subprocess.run(
["HandBrakeCLI", "--help"], capture_output=True, text=True, timeout=30,
[_hb(), "--help"], capture_output=True, text=True, timeout=30,
creationflags=OHNE_FENSTER
)
return (aus.stdout or "") + (aus.stderr or "")
@@ -349,12 +362,12 @@ def werkzeug_versionen() -> dict:
presets = hole_handbrake_presets()
if presets:
info["presets"] = presets
if shutil.which("makemkvcon"):
if werkzeuge.finden("makemkv"):
info["makemkv"] = os.getenv("MAKEMKV_VERSION") or "installiert"
if shutil.which("HandBrakeCLI"):
if _hb():
try:
aus = subprocess.run(
["HandBrakeCLI", "--version"],
[_hb(), "--version"],
capture_output=True, text=True, timeout=15,
creationflags=OHNE_FENSTER,
)