fix(windows): Drueberinstallieren, Linux-Reste im Windows-Betrieb, Ordner-Waehler
Ampel / ampel (push) Successful in 1m27s
Ampel / ampel (push) Successful in 1m27s
Vier Fragen des Commanders vom 29.08.2026, drei davon mit Codefolge.
## 1. „Was passiert wenn man die Setup.exe einfach drueber installiert?"
Bis hierher: nicht zuverlaessig. Rippy startet mit Windows, laeuft also fast
immer — dann ist `Rippy.exe` gesperrt, `shutil.copy2` warf PermissionError,
und die neue Fassung landete als `Rippy.exe.neu` daneben. Dazu die Meldung
„wird beim naechsten Start uebernommen".
**Diese Zusage hat niemand eingeloest.** `.neu` kam im ganzen Projekt genau
einmal vor: an der Stelle, die es schrieb. Wer drueberinstallierte, behielt
still die alte Fassung, und das Setup meldete Erfolg.
Jetzt wird der laufende Rippy vorher beendet (`dienst_beenden` gibt es seit
der Deinstallation und wartet auch die zwei Sekunden ab, die Windows fuer die
Dateihandles braucht). Eine von einer aelteren Setup-Fassung liegengelassene
`.neu` wird dabei uebernommen. Bleibt die Datei DANN noch gesperrt, gibt es
einen klaren Fehler statt einer Zusage — Rippy im Infobereich beenden und das
Setup erneut starten.
Der Tausch laeuft bewusst im SETUP und nicht beim Dienststart: Windows sperrt
eine laufende .exe, und `Rippy.exe` waere genau die zu ersetzende Datei.
## 3. Linux-Reste im Windows-Betrieb (Docker/Headless unveraendert)
**`caps.py`: `os.path.isdir("/app")`.** Damit hielt sich der eigenstaendige
Windows-Rippy fuer einen FREMDEN Worker — und das UI warnte vor fehlender
Pfad-Uebersetzung auf einer Maschine ohne Container und ohne Freigabe.
„Extern" heisst jetzt, was es meint: Rippy laeuft woanders als dieser Worker.
**`caps.py`: `shutil.which("makemkvcon")` + `os.path.ismount(daten_dir)`.**
Beide unter Windows immer falsch (Programme liegen nicht im PATH, ein
normaler Ordner ist kein Mount). Die Schluessel-Auskunft blieb dauerhaft
„unbekannt", obwohl MakeMKV samt Datenverzeichnis da war. Der Mount-Test
bleibt fuer den Container, wo er einen Zweck hat.
**`rohdaten.py`: `/app/temp/raw` und `/app/media` fest.** Dieses Modul findet
die Rohdaten eines Jobs wieder — fuer den Wiederholen-Dialog und fuer
„Rohdaten mitloeschen". Unter Windows fand es NIE etwas: Der Dialog meldete
„keine Rohdaten", das Aufraeumen loeschte nichts, und die Bruchstuecke eines
abgebrochenen Rips blieben liegen (bei 4K-UHD bis 100 GB).
Sieben Tests wurden dabei rot, und zwar zu Recht: Sie pruefen Container-Regeln,
liefen aber unter Windows. Die Wurzeln sind jetzt einspritzbar — beide
Betriebsfaelle auf jedem Rechner pruefbar statt vom laufenden abhaengig.
## 4. „Der Durchsuchen button fehlt. Wie es der Installer auch macht"
Neu: `OrdnerWaehler` — Pfadfeld plus „Durchsuchen …", benutzt fuer Ablage und
Arbeitsverzeichnis. Es waere der DRITTE fest eingebaute Ordner-Browser
geworden (RipTargetModal, StorageMounts); dieser hier ist wiederverwendbar.
`/browse` weiss seit dem 28.08. selbst, in welchem Betrieb es laeuft.
⚠️ Beim Einbau fiel der Import unter den Tisch. `vite` pruefte das NICHT — das
Buendel blieb byte-gleich gross, und zur Laufzeit waere es der naechste leere
Bildschirm gewesen. Aufgefallen nur, weil die erwartete Anzahl Ersetzungen
nicht stimmte. Im Browser gegengeprueft: alle sieben Laufwerke, Navigation in
D:\, keine Konsolenfehler.
843 Tests gruen, ruff sauber.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
21b1757aa4
commit
b2acddbdfa
+53
-4
@@ -8,13 +8,38 @@ Remote-GPU-Worker meldet sich hier genauso wie der eingebaute CPU-Worker.
|
||||
import os
|
||||
import platform
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
|
||||
from rippy.platform.winlauf import OHNE_FENSTER
|
||||
from rippy.tools import katalog as werkzeuge
|
||||
|
||||
|
||||
def _extern(werte=None, container=None) -> str:
|
||||
"""Läuft Rippy woanders als dieser Worker? „ja"/„nein" (einspritzbar).
|
||||
|
||||
Drei Fälle, und nur einer davon ist „ja":
|
||||
|
||||
* **Im Container** — der Worker ist Teil von Rippy. Nein.
|
||||
* **Nativ und eigenständig** (Windows-App) — dieser Prozess IST Rippy.
|
||||
Nein. Es gibt hier weder Container-Pfade noch eine Freigabe, über die
|
||||
etwas zu übersetzen wäre.
|
||||
* **Nativ und verteilt** — ein Worker auf einem anderen Rechner, der sich
|
||||
bei einem Docker-Rippy meldet. Ja; er braucht RIPPY_PATH_MAP.
|
||||
"""
|
||||
from rippy import betrieb, config
|
||||
|
||||
if container is None:
|
||||
container = betrieb.im_container()
|
||||
if container:
|
||||
return "nein"
|
||||
if werte is None:
|
||||
try:
|
||||
werte = config.laden()
|
||||
except Exception: # noqa: BLE001
|
||||
werte = {}
|
||||
return "ja" if betrieb.modus(werte) == "verteilt" else "nein"
|
||||
|
||||
|
||||
def _hb() -> str:
|
||||
"""Pfad zu HandBrakeCLI — "" wenn es nicht da ist.
|
||||
|
||||
@@ -343,8 +368,18 @@ def werkzeug_versionen() -> dict:
|
||||
# Container-Pfade (/app/media, /app/temp) nur über eine Freigabe plus
|
||||
# RIPPY_PATH_MAP erreicht. Das UI kann damit VOR dem Rip warnen, statt
|
||||
# den Nutzer eine Stunde rippen zu lassen (Vorfall 25.07.2026).
|
||||
# /app ist im Rippy-Image immer vorhanden — kein Ratespiel.
|
||||
"extern": "nein" if os.path.isdir("/app") else "ja",
|
||||
#
|
||||
# ⚠️ Hier stand `os.path.isdir("/app")` (Befund 29.08.2026). Im Image
|
||||
# stimmt das. Auf einem Windows-PC gibt es `/app` nicht — und damit
|
||||
# hielt sich der eigenständige Windows-Rippy für einen FREMDEN Worker.
|
||||
# Folge: Das UI warnte vor fehlender Pfad-Übersetzung auf einer
|
||||
# Maschine, auf der es weder Container noch Freigabe gibt.
|
||||
#
|
||||
# „Extern" heißt: Rippy läuft woanders als dieser Worker. In der
|
||||
# eigenständigen Installation IST dieser Worker Rippy — dort ist die
|
||||
# Frage gegenstandslos. Deshalb entscheidet der Betrieb, nicht ein
|
||||
# Ordnername.
|
||||
"extern": _extern(),
|
||||
# Ist die Pfad-Übersetzung gesetzt? Ohne sie kann ein externer Worker
|
||||
# grundsätzlich nicht komprimieren.
|
||||
"pfad_map": os.getenv("RIPPY_PATH_MAP", ""),
|
||||
@@ -402,7 +437,21 @@ def werkzeug_versionen() -> dict:
|
||||
daten_dir = makemkv_daten.DATEN_DIR
|
||||
except Exception:
|
||||
daten_dir = ""
|
||||
if shutil.which("makemkvcon") and daten_dir and os.path.ismount(daten_dir):
|
||||
# ⚠️ `shutil.which` und `os.path.ismount` sind hier BEIDE Linux-Annahmen
|
||||
# (Befund 29.08.2026): Unter Windows liegt makemkvcon in „Programme" und
|
||||
# nicht im PATH, und ein normaler Ordner ist kein Mount. Beide Prüfungen
|
||||
# waren dort also immer falsch — die Schlüssel-Auskunft blieb dauerhaft
|
||||
# „unbekannt", obwohl MakeMKV samt Datenverzeichnis da war.
|
||||
#
|
||||
# Der Mount-Test bleibt für den Container: Dort teilen sich Rippy und ein
|
||||
# reiner Encoder-Worker dasselbe Image, aber nur einer bekommt den Mount.
|
||||
# Nativ zählt stattdessen, ob der Ordner überhaupt existiert.
|
||||
from rippy import betrieb
|
||||
|
||||
_im_container = betrieb.im_container()
|
||||
daten_da = bool(daten_dir) and (os.path.ismount(daten_dir) if _im_container
|
||||
else os.path.isdir(daten_dir))
|
||||
if werkzeuge.finden("makemkv") and daten_da:
|
||||
try:
|
||||
info["keydb"] = "ja" if makemkv_daten.keydb_status().get("vorhanden") else "nein"
|
||||
except Exception:
|
||||
|
||||
@@ -214,3 +214,33 @@ def test_hardware_presets_stehen_auch_ohne_hardware_in_der_liste():
|
||||
assert caps.leite_backends_ab(caps.parse_encoder_liste(HB_HILFE_ECHT)) == [
|
||||
"cpu-x264", "cpu-x265", "cpu-av1",
|
||||
]
|
||||
|
||||
|
||||
# ── „Extern" heisst: Rippy laeuft woanders (Befund 29.08.2026) ──────────
|
||||
#
|
||||
# Hier stand `os.path.isdir("/app")`. Im Image stimmt das. Auf einem
|
||||
# Windows-PC gibt es `/app` nicht — und damit hielt sich der eigenstaendige
|
||||
# Windows-Rippy fuer einen FREMDEN Worker. Folge: Das UI warnte vor fehlender
|
||||
# Pfad-Uebersetzung auf einer Maschine, auf der es weder Container noch
|
||||
# Freigabe gibt.
|
||||
|
||||
|
||||
def test_im_container_ist_nichts_extern():
|
||||
from caps import _extern
|
||||
|
||||
assert _extern({}, container=True) == "nein"
|
||||
|
||||
|
||||
def test_die_eigenstaendige_windows_app_ist_NICHT_extern():
|
||||
"""Dieser Prozess IST Rippy — die Frage ist dort gegenstandslos."""
|
||||
from caps import _extern
|
||||
|
||||
assert _extern({"profil": "standalone"}, container=False) == "nein"
|
||||
|
||||
|
||||
def test_ein_worker_bei_einem_docker_rippy_ist_extern():
|
||||
"""Der Fall, fuer den die Auskunft gemacht ist: Er braucht RIPPY_PATH_MAP."""
|
||||
from caps import _extern
|
||||
|
||||
assert _extern({"profil": "node", "queue": {"treiber": "celery"}},
|
||||
container=False) == "ja"
|
||||
|
||||
Reference in New Issue
Block a user