fix(ui,transcode): Dialog-Falle, Arbeitsverzeichnis waehlbar, Original-Aufheben entschaerft
Ampel / ampel (push) Successful in 28s
Ampel / ampel (push) Successful in 28s
Drei Befunde aus dem ersten echten UHD-Durchlauf (Akira). Der Rip und die
Kompression liefen sauber durch - danach hat "Original behalten" die Platte
vollgeschrieben und den fertigen Job als fehlgeschlagen markiert.
1. ORIGINAL-AUFHEBEN DARF DEN JOB NICHT MEHR TOETEN (tasks.py)
Vorher stand dort ein nacktes shutil.move(raw_dir, ziel). Arbeits-
verzeichnis (/app/temp, Docker-Volume) und Ziel (/app/media, Bind-Mount)
sind VERSCHIEDENE Dateisysteme - os.rename scheitert mit EXDEV, shutil.move
faellt auf Kopieren zurueck. Ergebnis am 25.07.: 74-GB-Vollkopie auf
dieselbe Platte, Abbruch bei 41 GB mit ENOSPC, Platte 100 % voll, Worker-
Container startete nicht mehr ("failed to mount: no space left on device"),
und der Job galt als FEHLGESCHLAGEN - obwohl die komprimierte Datei
(4,8 GB) fertig und in Ordnung war. Der Nutzer sah nur eine leere Queue.
Jetzt: _original_aufheben() prueft erst, ob ueberhaupt kopiert werden muss
(gleiches Dateisystem -> reines Umhaengen), prueft sonst den freien Platz
VORHER, faengt jeden OSError ab, raeumt eine halbe Kopie weg und meldet das
als WARNUNG. Der Job bleibt erfolgreich, die Roh-Datei bleibt liegen.
Zwei Tests decken beide Wege ab.
2. ARBEITSVERZEICHNIS IST JETZT WAEHLBAR (Settings.tsx)
Es war ein freies Textfeld - man musste den Container-Pfad (/app/media/...)
KENNEN, um eine Netzwerk-Freigabe zu treffen. Genau daran ist es
gescheitert, weshalb der 74-GB-Rohschnitt ueberhaupt erst auf der VM-Platte
landete. Jetzt eine Auswahl aus /storage-targets (dieselbe Liste wie bei
den Speicherzielen), inklusive Kennzeichnung als Netzwerk-Freigabe und
freiem Platz je Ziel.
3. DIALOGE KLEBTEN IM PANEL (ui/Modal.tsx)
"Rippen starten" im Laufwerke-Tab oeffnete den Dialog INNERHALB des
Bereichs, teils abgeschnitten. Ursache: .glass-panel in index.css setzt
backdrop-filter: blur(16px), und ein Element mit backdrop-filter wird zum
Bezugsrahmen fuer position: fixed seiner Nachfahren - das Modal war damit
an der Card ausgerichtet statt am Fenster. Modal rendert jetzt per
createPortal an document.body. Behebt es fuer ALLE Dialoge auf einmal.
Aufgeraeumt: die abgebrochene 41-GB-Teilkopie unter
"/app/media/movies/Akira (1988)/original/" geloescht (nachweislich
unvollstaendig - 41 GB gegen 79,6 GB Quelle, Quelle intakt). Platte wieder
bei 78 %, Container laufen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+59
-3
@@ -172,6 +172,64 @@ def _makemkv_key_anwenden(einstellungen: dict) -> None:
|
||||
db.add_log("warning", "worker", f"MakeMKV-Key konnte nicht gesetzt werden: {e}")
|
||||
|
||||
|
||||
def _ordner_groesse(pfad: str) -> int:
|
||||
"""Belegter Platz eines Ordners in Bytes (0, wenn nicht lesbar)."""
|
||||
summe = 0
|
||||
for wurzel, _, dateien in os.walk(pfad):
|
||||
for name in dateien:
|
||||
try:
|
||||
summe += os.path.getsize(os.path.join(wurzel, name))
|
||||
except OSError:
|
||||
pass
|
||||
return summe
|
||||
|
||||
|
||||
def _original_aufheben(job_id: str, raw_dir: str, final_dir: str) -> None:
|
||||
"""Roh-Rip zusätzlich aufheben — darf den Job NIEMALS scheitern lassen.
|
||||
|
||||
Befund 25.07.2026 (Akira-UHD, echter Schaden): Hier stand ein nacktes
|
||||
shutil.move(). Arbeitsverzeichnis (/app/temp, Docker-Volume) und Ziel
|
||||
(/app/media, Bind-Mount) sind VERSCHIEDENE Dateisysteme — os.rename
|
||||
scheitert dort mit EXDEV, und shutil.move fällt auf Kopieren zurück. Damit
|
||||
lief eine 74-GB-Vollkopie auf dieselbe Platte an, bis sie mit ENOSPC voll
|
||||
war. Ergebnis: Platte 100 % voll, Worker-Container startete nicht mehr —
|
||||
und der Job wurde als FEHLGESCHLAGEN markiert, obwohl die komprimierte
|
||||
Datei längst fertig und in Ordnung war. Der Nutzer sah nur „nichts da".
|
||||
|
||||
Deshalb dreifach abgesichert: Platz vorher prüfen, Fehler abfangen, und im
|
||||
Zweifel die Rohdatei einfach liegen lassen, wo sie ist.
|
||||
"""
|
||||
ziel_original = os.path.join(final_dir, "original")
|
||||
try:
|
||||
# Liegt beides auf demselben Dateisystem, ist es ein reines Umhängen —
|
||||
# sofort fertig, kein zusätzlicher Platz nötig.
|
||||
gleiches_dateisystem = os.stat(raw_dir).st_dev == os.stat(final_dir).st_dev
|
||||
if not gleiches_dateisystem:
|
||||
benoetigt = _ordner_groesse(raw_dir)
|
||||
frei = _frei_bytes(final_dir)
|
||||
if frei < benoetigt * 1.05:
|
||||
db.add_log(
|
||||
"warning", "worker",
|
||||
f"Job {job_id}: Original NICHT aufgehoben — dafür wären "
|
||||
f"{benoetigt / 1024**3:.1f} GB nötig, frei sind nur "
|
||||
f"{frei / 1024**3:.1f} GB. Die Roh-Datei bleibt unter "
|
||||
f"{raw_dir} liegen. Abhilfe: Arbeitsverzeichnis "
|
||||
"(Einstellungen → Verarbeitung) auf dieselbe Freigabe legen "
|
||||
"wie das Ziel — dann wird nur umgehängt statt kopiert."
|
||||
)
|
||||
return
|
||||
shutil.move(raw_dir, ziel_original)
|
||||
db.add_log("info", "worker", f"Job {job_id}: Original behalten unter {ziel_original}")
|
||||
except OSError as e:
|
||||
# Halb geschriebene Kopie wegräumen, sonst belegt sie für immer Platz.
|
||||
shutil.rmtree(ziel_original, ignore_errors=True)
|
||||
db.add_log(
|
||||
"warning", "worker",
|
||||
f"Job {job_id}: Original konnte nicht aufgehoben werden ({e}). "
|
||||
f"Die komprimierte Datei ist fertig; die Roh-Datei bleibt unter {raw_dir}."
|
||||
)
|
||||
|
||||
|
||||
def _abbruch_angefordert(job_id: str) -> bool:
|
||||
"""Kooperativer Abbruch: hat der Nutzer über die API abgebrochen?"""
|
||||
return db.get_job_status(job_id) == "canceling"
|
||||
@@ -610,9 +668,7 @@ def transcode_files(self, job_id: str, raw_dir: str, final_dir: str):
|
||||
return ergebnis
|
||||
|
||||
if original_behalten:
|
||||
ziel_original = os.path.join(final_dir, "original")
|
||||
shutil.move(raw_dir, ziel_original)
|
||||
db.add_log("info", "worker", f"Job {job_id}: Original behalten unter {ziel_original}")
|
||||
_original_aufheben(job_id, raw_dir, final_dir)
|
||||
else:
|
||||
shutil.rmtree(raw_dir, ignore_errors=True)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user