fix(windows): Cover, Arbeitsverzeichnis und die zurueckgelassenen _MEI-Ordner

Drei Befunde des Commanders vom 28.08.2026, alle drei gemessen.

## 1. „Was ist mit dem Cover auf Windows Rippy?"

Es gab keins, weil es keinen Treffer gab. `_scan_video` verlangte woertliche
Gleichheit:

    if movie.get("title", "").lower() == kandidat.lower():

An seiner Disc gemessen:

    'Evangelion 2.22'    1 Treffer   Evangelion: 2.0 You Can (Not) Advance
    'Evangelion'        20 Treffer   irgendein Evangelion

Der EINZIGE Treffer auf den vollen Disc-Titel war der richtige Film, mit
Poster — und wurde verworfen. Jetzt zaehlt die SPEZIFITAET der Anfrage: Wer
auf den vollen Disc-Titel hoechstens drei Treffer bekommt, hat gefragt wie
jemand, der weiss was er sucht. Ergebnis an derselben Disc:

    Evangelion: 2.0 You Can (Not) Advance / 2009 / 80 % / Poster + dt. Text

## 2. „Warum heisst das hier noch container platte? … Waere es moeglich das
##     Arbeitsverzeichnis zu aendern? momentan geht das nicht."

Beide Haelften gehen auf EINE Zeile zurueck: `MEDIA_ROOT = "/app/media"` war
zugleich Vorgabe UND Pfadgrenze. Auf Windows gibt es den Ordner nicht:

* `/storage-targets` fing den OSError und gab still [] zurueck — die Auswahl
  hatte genau einen Eintrag. Das ist „momentan geht das nicht".
* Dessen Text war fest verdrahtet „(Container-Platte)".
* `/browse` antwortete auf jeden Pfad mit 422.

Die Grenze faellt nicht weg, sie wird betriebsabhaengig: Container und
Kopflos-Betrieb bedienen ein Netz, die native App den Menschen davor.
Gemessen auf seinem PC: 7 Laufwerke zur Auswahl, X/Y/Z mit je 2,2 TB frei.

## 3. „Failed to remove temporary directory: …_MEI0000b0882"

GEMESSEN: 20 zurueckgelassene _MEI-Ordner mit 1,1 GB. Ursache: `Popen` ohne
`env=` reicht PyInstallers Auspack-Zeiger an die Kinder weiter (--dienst und
--oeffnen). Beide laufen dann im Ordner des Elternprozesses, der sich zuerst
beendet und ihn loeschen will. Waere das TEILWEISE geglueckt, haetten Dienst
und Fenster mitten im Betrieb ihre Dateien verloren.

Nebenbefund: test_setup_fenster scheiterte in jeder Umgebung ohne pywebview.

794 Tests gruen, ruff sauber.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-28 16:43:41 +02:00
co-authored by Claude Opus 5
parent 11001a443e
commit 35370fd552
15 changed files with 2728 additions and 1723 deletions
+49 -3
View File
@@ -940,15 +940,40 @@ export default function SettingsPage() {
/storage-targets — dieselbe Liste wie bei den Speicherzielen,
inklusive freiem Platz.
*/}
{!betrieb.kann.container_pfade ? (
/*
* Ein echtes Pfadfeld statt einer Auswahl — dieselbe
* Entscheidung wie bei der Ablage darüber, und aus demselben
* Grund.
*
* Commander am 28.08.2026: „Wäre es möglich das
* Arbeitsverzeichnis zu ändern? momentan geht das nicht."
*
* Es ging nicht, weil `ziele` auf Windows LEER war: Die Liste
* kommt aus /storage-targets, und das las `/app/media` ein
* Ordner, den es dort nicht gibt. Übrig blieb eine Auswahl mit
* genau einem Eintrag, und der hieß Container-Platte".
*
* Die Liste ist inzwischen repariert (sie bringt jetzt die
* Laufwerke mit freiem Platz). Aber die richtige Antwort auf
* „wohin mit 100 GB" ist auf Windows oft ein Ort, den keine
* Liste kennt D:\Rippy-Arbeit oder eine UNC-Freigabe.
* Deshalb hier ein Feld, in das man ihn schreiben kann.
*/
<Input
label="Arbeitsverzeichnis für Roh-Rips (Standard)"
value={settings.workDir}
placeholder={betrieb.arbeits_vorgabe}
onChange={(e) => handleChange('workDir', e.target.value)}
/>
) : (
<Select
label="Arbeitsverzeichnis für Roh-Rips (Standard)"
value={settings.workDir}
onChange={(e) => handleChange('workDir', e.target.value)}
>
<option value="">
{betrieb.kann.container_pfade
? 'Container-Platte (Standard) — klein, nur für DVD/Blu-ray'
: `Standard (${betrieb.ablage_vorgabe || 'neben der Ablage'})`}
Container-Platte (Standard) klein, nur für DVD/Blu-ray
</option>
{ziele.map(z => (
<option key={z.path} value={z.path}>
@@ -957,6 +982,27 @@ export default function SettingsPage() {
</option>
))}
</Select>
)}
{!betrieb.kann.container_pfade && !!ziele.length && (
/* Die Laufwerke als Ein-Klick-Wahl. Wer den Platz sieht,
wählt anders — genau daran ist am 25.07. eine Platte
vollgelaufen. */
<div className="flex flex-wrap gap-1.5">
{ziele.map(z => (
<button
key={z.path}
onClick={() => handleChange('workDir', z.path)}
className={`px-2.5 py-1 text-xs rounded-lg border transition-colors ${
settings.workDir === z.path
? 'border-amber-500 bg-amber-500/10 text-amber-700 dark:text-amber-300'
: 'border-slate-200 dark:border-slate-700 text-slate-500 dark:text-slate-400 hover:border-slate-400'
}`}
>
{z.name}{z.free_gb != null ? `${z.free_gb} GB frei` : ''}
</button>
))}
</div>
)}
<p className="text-xs text-slate-500 dark:text-slate-400">
<strong className="text-slate-700 dark:text-slate-300">Der Standard</strong> beim
Rippen starten" kannst du für jede Disc etwas anderes wählen. <strong className="text-slate-700 dark:text-slate-300">