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
@@ -19,13 +19,20 @@ def test_der_echte_fall_wird_gefunden():
|
||||
assert orte[0] == f"/app/temp/raw/{JOB}"
|
||||
|
||||
|
||||
# Die Wurzeln des CONTAINER-Betriebs, eingespritzt. Ohne sie hingen diese
|
||||
# Tests am laufenden Rechner: Unter Windows liefert `wurzeln()` echte
|
||||
# Windows-Pfade, und die Erwartungen hier gelten dort nicht (am 29.08.2026
|
||||
# prompt sieben Tests rot geworden).
|
||||
CONTAINER = ("/app/temp/raw", "/app/media", False)
|
||||
|
||||
|
||||
def test_suche_liefert_nur_was_existiert():
|
||||
vorhanden = {f"/app/media/rippy/{JOB}"}
|
||||
gefunden = rohdaten.suche(
|
||||
JOB, "",
|
||||
listdir=lambda p: ["movies", "rippy"],
|
||||
isdir=lambda p: p in vorhanden,
|
||||
)
|
||||
orte_wurzeln=CONTAINER)
|
||||
assert gefunden == [f"/app/media/rippy/{JOB}"]
|
||||
|
||||
|
||||
@@ -64,7 +71,7 @@ def test_kaputter_mount_reisst_die_suche_nicht_mit():
|
||||
return p == f"/app/temp/raw/{JOB}"
|
||||
|
||||
gefunden = rohdaten.suche(
|
||||
JOB, "", listdir=lambda p: ["totes-nas", "movies"], isdir=isdir_kaputt)
|
||||
JOB, "", listdir=lambda p: ["totes-nas", "movies"], isdir=isdir_kaputt, orte_wurzeln=CONTAINER)
|
||||
assert gefunden == [f"/app/temp/raw/{JOB}"]
|
||||
|
||||
|
||||
@@ -73,7 +80,7 @@ def test_listdir_kaputt_faellt_auf_den_standard_zurueck():
|
||||
raise OSError("kein /app/media")
|
||||
|
||||
gefunden = rohdaten.suche(
|
||||
JOB, "", listdir=listdir_kaputt, isdir=lambda p: True)
|
||||
JOB, "", listdir=listdir_kaputt, isdir=lambda p: True, orte_wurzeln=CONTAINER)
|
||||
assert gefunden == [f"/app/temp/raw/{JOB}"]
|
||||
|
||||
|
||||
@@ -177,7 +184,7 @@ def test_suche_mit_der_zeitgrenze_findet_den_echten_fall():
|
||||
JOB, "",
|
||||
listdir=lambda p: ["bluray", "movies", "rippy"],
|
||||
isdir=lambda p: rohdaten.verzeichnis_da(p, laufen),
|
||||
)
|
||||
orte_wurzeln=CONTAINER)
|
||||
assert gefunden == [f"/app/media/rippy/{JOB}"]
|
||||
|
||||
|
||||
@@ -206,7 +213,7 @@ def test_suche_mit_status_meldet_ungepruefte_orte():
|
||||
return "unklar" if pfad.startswith("/app/media/rippy/") else "weg"
|
||||
|
||||
e = rohdaten.suche_mit_status(
|
||||
JOB, "", listdir=lambda p: ["rippy"], pruefer=pruefe)
|
||||
JOB, "", listdir=lambda p: ["rippy"], pruefer=pruefe, orte_wurzeln=CONTAINER)
|
||||
assert e == {"pfade": [], "unklar": True}
|
||||
|
||||
|
||||
@@ -215,7 +222,7 @@ def test_suche_mit_status_ohne_zweifel():
|
||||
return "da" if pfad == f"/app/media/rippy/{JOB}" else "weg"
|
||||
|
||||
e = rohdaten.suche_mit_status(
|
||||
JOB, "", listdir=lambda p: ["movies", "rippy"], pruefer=pruefe)
|
||||
JOB, "", listdir=lambda p: ["movies", "rippy"], pruefer=pruefe, orte_wurzeln=CONTAINER)
|
||||
assert e == {"pfade": [f"/app/media/rippy/{JOB}"], "unklar": False}
|
||||
|
||||
|
||||
@@ -227,6 +234,6 @@ def test_suche_mit_status_findet_trotz_unklarem_anderen_ort():
|
||||
return "unklar" if "totes-nas" in pfad else "weg"
|
||||
|
||||
e = rohdaten.suche_mit_status(
|
||||
JOB, "", listdir=lambda p: ["rippy", "totes-nas"], pruefer=pruefe)
|
||||
JOB, "", listdir=lambda p: ["rippy", "totes-nas"], pruefer=pruefe, orte_wurzeln=CONTAINER)
|
||||
assert e["pfade"] == [f"/app/media/rippy/{JOB}"]
|
||||
assert e["unklar"] is True
|
||||
|
||||
Reference in New Issue
Block a user