fix(windows): Roh- und Zielordner landeten in einem Ordner namens „app"
Beim Nachstellen des leeren Bildschirms lief ein echter Test-Rip durch. Die
Rohdaten landeten in:
F:\app\temp\raw\<job>\Evangelion 2.22_t00.mkv (436 MB)
Also in einem Ordner namens `app` auf dem Laufwerk, von dem Rippy gerade lief.
## Zwei Container-Wurzeln im Worker
RAW_DIR = /app/temp/raw
MEDIA_ROOT = /app/media
Unter Windows sind das keine Pfade, sondern Unfaelle. Schlimmer: Die Pruefung
`unter_wurzel(wahl, MEDIA_ROOT)` verwarf auch eine AUSDRUECKLICHE Wahl — ein
Arbeitsordner wie `D:\Roh` liegt nicht unter `/app/media`, also fiel er still
auf den Container-Standard zurueck.
**Damit kam der Arbeitsordner, den der Commander am 28.08.2026 ausdruecklich
bestellt hat, unter Windows nie an.** Der Dialog zeigte ihn, das Setzen ging,
und der Worker ignorierte ihn — ohne ein Wort. Dasselbe galt fuer das Ziel:
Eine UNC-Freigabe liegt unter gar keiner lokalen Wurzel, also waere die
fertige Datei in `X:\app\media\bluray` gelandet.
## Die Wurzeln kommen jetzt aus dem Betrieb
Im Container aendert sich NICHTS: dort ist `/app/media` die Wurzel und `frei`
falsch. Nativ zaehlt die Wahl des Nutzers — dort IST sein Laufwerk die Grenze.
Die drei bestehenden Tests wurden rot, und zwar zu Recht: Sie pruefen die
Container-Regel, liefen aber unter Windows, wo `frei` gilt. Die Wurzeln sind
deshalb einspritzbar — beide Betriebsfaelle sind jetzt auf jedem Rechner
pruefbar, statt vom laufenden abzuhaengen.
837 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
27c9d9a4bb
commit
423374e65a
+70
-15
@@ -157,7 +157,7 @@ def unter_wurzel(pfad: str, wurzel: str) -> bool:
|
||||
return pfad == sauber or pfad.startswith(sauber + "/")
|
||||
|
||||
|
||||
def _zielbasis(target_dir, disc_type: str) -> str:
|
||||
def _zielbasis(target_dir, disc_type: str, wurzeln=None) -> str:
|
||||
"""Ablagebasis: vom Nutzer gewähltes Ziel (validiert) oder Standard.
|
||||
|
||||
posixpath statt os.path — aus demselben Grund wie in _arbeitsverzeichnis:
|
||||
@@ -167,16 +167,70 @@ def _zielbasis(target_dir, disc_type: str) -> str:
|
||||
als der Test dafür erstmals unter Windows lief. Live war es nie: aufgerufen
|
||||
wird nur aus rip_disc, und das ist auf Windows-Workern verriegelt.
|
||||
"""
|
||||
wurzel, _vorgabe, frei = wurzeln or _betriebs_wurzeln()
|
||||
if target_dir:
|
||||
normalisiert = posixpath.normpath(target_dir)
|
||||
if unter_wurzel(normalisiert, MEDIA_ROOT):
|
||||
normalisiert = _normalisiert(target_dir)
|
||||
if frei or unter_wurzel(normalisiert, wurzel):
|
||||
return normalisiert
|
||||
return posixpath.join(RIP_OUTPUT_DIR, disc_type)
|
||||
# Ohne Wahl: die Wurzel dieses Betriebs, nicht die des Containers. Sonst
|
||||
# landete die fertige Datei unter Windows in `X:\app\media\bluray` —
|
||||
# einem Ordner, den niemand gesucht hat (gemessen 29.08.2026).
|
||||
from rippy import pfade
|
||||
|
||||
return pfade.verbinden(wurzel if frei else RIP_OUTPUT_DIR, disc_type)
|
||||
|
||||
|
||||
def _arbeitsverzeichnis(einstellungen: dict, job_wahl: str = "") -> str:
|
||||
def _betriebs_wurzeln() -> tuple:
|
||||
"""`(medien_wurzel, arbeits_vorgabe, frei)` für DIESEN Betrieb.
|
||||
|
||||
## Warum das hier gebraucht wird (Befund 29.08.2026)
|
||||
|
||||
Beim Nachstellen des leeren Bildschirms lief ein echter Test-Rip durch —
|
||||
und die Rohdaten landeten in **`F:\\app\\temp\\raw`**. Also in einem
|
||||
Ordner namens `app` auf dem Laufwerk, von dem Rippy gerade lief.
|
||||
|
||||
`RAW_DIR` ist `/app/temp/raw` und `MEDIA_ROOT` ist `/app/media`; unter
|
||||
Windows sind das keine Pfade, sondern Unfälle. Schlimmer noch: Die
|
||||
Prüfung `unter_wurzel(wahl, MEDIA_ROOT)` verwarf **auch eine ausdrückliche
|
||||
Wahl** — ein Arbeitsordner wie `D:\\Roh` liegt nicht unter `/app/media`,
|
||||
also fiel er still auf den Container-Standard zurück.
|
||||
|
||||
Damit kam der Arbeitsordner, den der Commander am 28.08.2026 ausdrücklich
|
||||
bestellt hat („kannst du noch einbauen das man den arbeitsordner … setzen
|
||||
kann"), unter Windows nie an. Der Dialog zeigte ihn, das Setzen ging, und
|
||||
der Worker ignorierte ihn — ohne ein Wort.
|
||||
|
||||
Im Container ändert sich nichts: Dort ist `/app/media` die Wurzel, und
|
||||
`frei` ist falsch.
|
||||
"""
|
||||
from rippy import betrieb, config
|
||||
|
||||
try:
|
||||
werte = config.laden()
|
||||
except Exception: # noqa: BLE001
|
||||
werte = {}
|
||||
return (betrieb.medien_wurzel(werte) or MEDIA_ROOT,
|
||||
betrieb.arbeits_vorgabe(werte) or RAW_DIR,
|
||||
betrieb.frei_blaettern(werte))
|
||||
|
||||
|
||||
def _normalisiert(wert: str) -> str:
|
||||
"""Pfad säubern — nach seiner FORM, nicht nach dem laufenden Rechner.
|
||||
|
||||
`posixpath` für Container-Pfade (sonst macht Windows Backslashes daraus
|
||||
und die Wurzelprüfung greift nicht mehr), `os.path` für echte
|
||||
Windows-Pfade. Dieselbe Regel wie in `rippy.pfade`.
|
||||
"""
|
||||
from rippy import pfade
|
||||
|
||||
return os.path.normpath(wert) if pfade.ist_windows_pfad(wert) \
|
||||
else posixpath.normpath(wert)
|
||||
|
||||
|
||||
def _arbeitsverzeichnis(einstellungen: dict, job_wahl: str = "",
|
||||
wurzeln=None) -> str:
|
||||
"""Basis für Roh-Rips. Reihenfolge: Wahl DIESES Rips → UI-Setting
|
||||
`workDir` → Container-Default /app/temp/raw.
|
||||
`workDir` → Vorgabe dieses Betriebs (Container: /app/temp/raw).
|
||||
|
||||
Hintergrund (Commander 24.07.): Die VM-Platte (150 GB) reicht für BD-50,
|
||||
aber eine 4K-UHD (bis 100 GB roh + Kompression daneben) sprengt sie —
|
||||
@@ -187,17 +241,18 @@ def _arbeitsverzeichnis(einstellungen: dict, job_wahl: str = "") -> str:
|
||||
Standard — und ist damit der Wert, der bei Vollautomatik-Rips greift, bei
|
||||
denen niemand gefragt wird.
|
||||
"""
|
||||
# posixpath statt os.path: Das sind IMMER Container-Pfade (/app/media/...),
|
||||
# auch wenn ein nativer Windows-Worker dieses Modul lädt — der übersetzt
|
||||
# sie erst später mit pfad_lokal(). os.path.normpath macht unter Windows
|
||||
# Backslashes daraus, und dann greift die MEDIA_ROOT-Prüfung nicht mehr.
|
||||
wurzel, vorgabe, frei = wurzeln or _betriebs_wurzeln()
|
||||
for kandidat in (job_wahl, einstellungen.get("workDir")):
|
||||
wert = (kandidat or "").strip()
|
||||
if wert:
|
||||
normalisiert = posixpath.normpath(wert)
|
||||
if unter_wurzel(normalisiert, MEDIA_ROOT):
|
||||
return normalisiert
|
||||
return RAW_DIR
|
||||
if not wert:
|
||||
continue
|
||||
normalisiert = _normalisiert(wert)
|
||||
# Nativ zählt die Wahl des Nutzers — dort IST sein Laufwerk die
|
||||
# Grenze. Im Container bleibt die Wurzelprüfung: Ein Pfad ausserhalb
|
||||
# von /app/media wäre dort ein Pfad ins Nichts.
|
||||
if frei or unter_wurzel(normalisiert, wurzel):
|
||||
return normalisiert
|
||||
return vorgabe
|
||||
|
||||
|
||||
def _frei_bytes(pfad: str) -> int:
|
||||
|
||||
Reference in New Issue
Block a user