From fd1feaaee3481ec01522d1da0cd8fde96fd0404d Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Sun, 26 Jul 2026 11:58:58 +0200 Subject: [PATCH] fix(ui): die Ablage war im UI ueberhaupt nicht einstellbar Der Grund, warum der erste Rip mit externem Encoder scheitern MUSSTE - und Commander-Vorgabe: "Wenn ein externes Ziel eingehaengt ist, soll Rippy das ausgewaehlte als Arbeitsziel verwenden. Das stellt man ja sowieso in den Einstellungen ein. Das muss auch fuer den Automatik-Modus so sein." Genau das ging nicht. outputDir stand in den Einstellungen, wurde von der Vollautomatik (main.py) und der Schnellwahl (RipTargetModal) gelesen - aber es gab NIRGENDS ein Eingabefeld. Die Ablage klebte auf /app/media, der Container-Platte. Das Arbeitsverzeichnis hatte eine Auswahlliste der eingehaengten Ziele, das Ziel nicht. Folge im Praxistest: Rohdaten auf der NAS (die der PC erreicht), Ziel auf der VM-Platte (die er nicht erreicht, kein Samba dort). Der externe Encoder konnte lesen, aber nicht schreiben - und das haette man mit den vorhandenen Bedienelementen gar nicht anders einstellen koennen. JETZT: Ablage-Auswahl in Einstellungen -> Ripping, mit derselben Liste eingehaengter Ziele wie das Arbeitsverzeichnis. Damit wirkt sie automatisch auch in der Vollautomatik und in der Schnellwahl, weil beide outputDir lesen - also genau so, wie der Commander es beschrieben hat. DAZU die Pruefung, die den Vorfall verhindert haette: Sobald ein EXTERNER Worker gemeldet ist, warnt das UI, wenn Ablage und Arbeitsverzeichnis nicht beide auf einer erreichbaren Freigabe liegen. Drei Faelle, jeweils mit der Konsequenz im Klartext: - Arbeitsverzeichnis auf der Container-Platte -> externer Encoder kann nicht lesen - Arbeit auf Freigabe, Ablage lokal -> kann lesen, nicht schreiben (der Vorfall) - beide auf VERSCHIEDENEN Freigaben -> laeuft, kostet aber eine Vollkopie "Extern" ist dabei keine Heuristik ueber IPs oder Namen: caps.py meldet es selbst (`extern`), weil /app im Rippy-Image immer existiert und ausserhalb nie. Zusaetzlich meldet der Worker jetzt seine `pfad_map` - damit kann das UI im naechsten Schritt sagen, ob die Uebersetzung ueberhaupt gesetzt ist. Co-Authored-By: Claude Opus 5 --- docker/ui/src/pages/Settings.tsx | 78 +++++++++++++++++++++++++++++++- docker/worker/caps.py | 10 ++++ 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/docker/ui/src/pages/Settings.tsx b/docker/ui/src/pages/Settings.tsx index 90d9d02..78bd373 100644 --- a/docker/ui/src/pages/Settings.tsx +++ b/docker/ui/src/pages/Settings.tsx @@ -77,10 +77,12 @@ interface WorkerInfo { // Angaben war am 25.07.2026 nicht zu sehen, dass ein 4K-Encode auf der // Rippy-VM Tage statt Stunden braucht (kein AVX2, 4 Kerne). // handbrake_encoder: die ungefilterte Encoder-Auskunft von HandBrake selbst. + // extern: "ja" = läuft AUSSERHALB des Rippy-Containers, sieht /app/... also + // nur über eine Freigabe plus pfad_map. Der Worker meldet das selbst. info?: { makemkv?: string, handbrake?: string, makemkv_key?: string, keydb?: string, cpu_modell?: string, cpu_kerne?: string, cpu_simd?: string, - handbrake_encoder?: string, + handbrake_encoder?: string, extern?: string, pfad_map?: string, } } @@ -345,6 +347,40 @@ export default function SettingsPage() { ) } + // Erreichbarkeit für EXTERNE Encoder. Ein Worker auf einem anderen Rechner + // sieht die Container-Platte nicht — nur eingehängte Netzwerk-Freigaben, die + // er selbst auch erreicht. Liegen Ablage und Arbeitsverzeichnis nicht BEIDE + // auf einer solchen Freigabe, scheitert die Kompression dort zwangsläufig. + // + // Genau das ist am 25.07.2026 passiert: Rohdaten auf der NAS (erreichbar), + // Ziel auf der VM-Platte (nicht erreichbar) — der Fehler kam erst nach einer + // Stunde Rippen. Diese Prüfung sagt es vorher. + // Der Worker sagt selbst, ob er außerhalb des Containers läuft (caps.py + // meldet `extern`) — keine Heuristik über IPs oder Namen nötig. + const externerWorkerVorhanden = workers.some(w => w.info?.extern === 'ja') + const istFreigabe = (pfad: string) => + ziele.some(z => z.path === pfad && z.is_mount) + const ablageWarnung = (() => { + const ablage = settings.outputDir || '/app/media' + const arbeit = settings.workDir + if (!arbeit) { + return 'Das Arbeitsverzeichnis steht auf der Container-Platte. Ein Encoder auf ' + + 'einem anderen Rechner kann die Rohdaten dort nicht lesen — lege beides auf ' + + 'dieselbe Netzwerk-Freigabe (Einstellungen → Verarbeitung).' + } + if (!istFreigabe(ablage) && istFreigabe(arbeit)) { + return 'Arbeitsverzeichnis liegt auf einer Freigabe, die Ablage aber auf der ' + + 'Container-Platte. Ein externer Encoder kann dann lesen, aber nicht schreiben — ' + + 'genau daran scheiterte der erste Versuch. Stelle die Ablage auf dieselbe Freigabe.' + } + if (istFreigabe(ablage) && istFreigabe(arbeit) && ablage !== arbeit) { + return 'Ablage und Arbeitsverzeichnis liegen auf VERSCHIEDENEN Freigaben. Das ' + + 'funktioniert, kostet am Ende aber eine Vollkopie statt eines Umhängens. ' + + 'Dieselbe Freigabe für beides ist deutlich schneller.' + } + return '' + })() + const tabs: { id: SettingsTab; label: string; icon: any }[] = [ { id: 'ripping', label: 'Ripping', icon: Disc }, { id: 'verarbeitung', label: 'Verarbeitung', icon: Cpu }, @@ -732,12 +768,50 @@ export default function SettingsPage() { Speicherziele + {/* ABLAGE — bis 26.07.2026 gab es dieses Feld im UI GAR NICHT. + outputDir stand in den Einstellungen und wurde von der + Vollautomatik und der Schnellwahl gelesen, ließ sich aber + nirgends ändern: die Ablage klebte auf /app/media, also der + Container-Platte. Deshalb konnte man das Arbeitsverzeichnis auf + eine NAS legen (dafür gab es eine Auswahl), das ZIEL aber nicht + — und genau daran scheiterte der erste Rip mit externem + Encoder: Rohdaten auf der erreichbaren NAS, Ziel auf der + VM-Platte, die der andere Rechner nicht sieht. */} +
+

+ Ablage — wohin die fertigen Filme kommen +

+ +

+ Gilt für die Schnellwahl im Rip-Dialog und + für die Vollautomatik — dort fragt niemand nach, also entscheidet dieser Wert. + Die Unterordner darunter kommen aus der Schnellwahl. +

+ {externerWorkerVorhanden && ablageWarnung && ( +

+ ⚠️ {ablageWarnung} +

+ )} +
+

Schnellwahl beim Rippen

- Name der Ordner im „Rippen starten"-Dialog (🎥/📺/🎵). + Name der Ordner im „Rippen starten"-Dialog (🎥/📺/🎵) — jeweils unterhalb der Ablage.

{([['movieDir', '🎥 Filme'], ['seriesDir', '📺 Serien'], ['musicDir', '🎵 Musik']] as const).map(([feld, label]) => ( diff --git a/docker/worker/caps.py b/docker/worker/caps.py index b6ba212..f405718 100644 --- a/docker/worker/caps.py +++ b/docker/worker/caps.py @@ -269,6 +269,16 @@ def werkzeug_versionen() -> dict: "cpu_modell": modell, "cpu_kerne": str(kerne), "cpu_simd": simd, + # Läuft dieser Worker INNERHALB des Rippy-Containers oder auf einer + # fremden Maschine? Entscheidend, weil ein externer Worker die + # 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", + # Ist die Pfad-Übersetzung gesetzt? Ohne sie kann ein externer Worker + # grundsätzlich nicht komprimieren. + "pfad_map": os.getenv("RIPPY_PATH_MAP", ""), } hb_encoder = parse_encoder_liste(hole_handbrake_hilfe()) if hb_encoder: