From be15419882c17e9b6294ad628244c735abd69af6 Mon Sep 17 00:00:00 2001 From: Sucukdeluxe Date: Wed, 17 Jun 2026 11:30:32 +0200 Subject: [PATCH] Fix: deutsche Tonspur-Erkennung waehlt nicht mehr eine falsch-getaggte Spur per Titel (Datenverlust-Schutz, Runde-7-Audit) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit isGermanStream wertete die Titel-Regex /\b(german|deutsch)\b/ fuer JEDEN nicht deutsch-getaggten Audiostream aus — also auch fuer eine bereits anderssprachig getaggte Spur (z.B. {language:"eng", title:"German Commentary"}). Der Kommentar ueber dem Titel-Fallback sagt ausdruecklich, er sei "used when the language tag is missing", aber genau diese Gating-Bedingung fehlte. Folge: pickAudioTrack (der EINZIGE Caller) nimmt den ersten isGermanStream- Treffer. Lag eine eng-Spur mit "German"/"Deutsch" im Titel VOR der korrekt mit "ger" getaggten Spur, gewann die englische Spur. Der Remux behielt dann Englisch, verwarf die echte deutsche Spur, ersetzte das Original atomar in-place und strippte den .DL.-Marker — irreversibler Datenverlust mit falscher Sprache, vom System als Erfolg ("german-tag"/"remuxed") gemeldet. Single-Trigger, kein compound-Vorbedingung. Betrifft genau die vom Nutzer bestaetigte Tonspur-Funktion. Fix: Titel-Fallback nur noch ausfuehren, wenn KEIN Sprach-Tag vorhanden ist (`if (lang) return false;` vor der Titel-Pruefung) — deckt sich exakt mit der dokumentierten Absicht des bestehenden Kommentars. Korrekt getaggte deutsche Spuren gewinnen jetzt immer; eine anderssprachige Spur mit "German" im Titel wird nicht mehr faelschlich gewaehlt. Untagged-Spuren nutzen weiter den Titel-Fallback (unveraendert), und der German-Release-Mislabel-Pfad (German-first-Konvention) greift weiterhin. Runde-7-Audit (adversarisch verifiziert) hat ausserdem den deferred-Extraction- Cluster abgeschlossen: #10 CRC-Kleindatei-Delete und #12 7z-Exit-1-als-Erfolg sind BENIGN, #11 resume-empty-output not-confirmed (compound), #13 = das dokumentierte LOW Companion-Overwrite (PP-1, compound, nur Sekundaerdateien). Rot-bewiesen: zwei reine pickAudioTrack-Tests (eng-Titel-"German" vor echter ger → audioRelIndex 1; eng-Titel-"Deutsch entfernt" ohne echte dt. Spur → skip) failen ohne den Gate. Volle Suite 884 gruen, tsc unveraendert. --- src/main/video-processor.ts | 3 +++ tasks/audit-loop.md | 21 +++++++++++++++++++++ tasks/entscheidungen-offen.md | 10 ++++++++++ tests/video-processor.test.ts | 11 +++++++++++ 4 files changed, 45 insertions(+) diff --git a/src/main/video-processor.ts b/src/main/video-processor.ts index bb1b9f8..bbc63b1 100644 --- a/src/main/video-processor.ts +++ b/src/main/video-processor.ts @@ -104,6 +104,9 @@ function isGermanStream(stream: ProbedAudioStream): boolean { // Free-text title fallback (used when the language tag is missing). Full words // only — the 2-3 letter codes ger/deu are too ambiguous in a title and would // pick the wrong track to keep (which then deletes the real German one). + if (lang) { + return false; + } const title = (stream.title || "").toLowerCase(); return /\b(german|deutsch)\b/.test(title); } diff --git a/tasks/audit-loop.md b/tasks/audit-loop.md index 35570e4..f6b38a7 100644 --- a/tasks/audit-loop.md +++ b/tasks/audit-loop.md @@ -165,6 +165,27 @@ Multi-Agent-adversarisch, Routing-Fix frueher advisor-gesegnet, Praezedenz 215/2 Runde-5/6-Charakterisierungen (PP-SEM-1 benign, DISK-1 deferred, deferred-LOW-Cluster #10/#11/#13 benign) NICHT released — dokumentiert. +## Runde 7 (Post-Download: Extraction + Video-Processor + Companion/Orchestrierung) — Workflow wcxk08n5i +3 Finder + adversarisch verifizieren. 2 confirmed, 0 refuted. Schliesst den deferred-Extraction-Cluster ab. +- **CONFIRMED MED (GEFIXT) VP-1 Falsche Tonspur:** isGermanStream (video-processor.ts:99-108) wertet die + Titel-Regex /\b(german|deutsch)\b/ fuer JEDEN nicht-deutsch-getaggten Stream — NICHT auf "Sprach-Tag fehlt" + gegated, obwohl der Kommentar (104-106) genau das als Absicht nennt. pickAudioTrack (124, EINZIGER Caller) + nimmt den ERSTEN Treffer → eine eng-Spur mit Titel "...German..." vor der echten ger-Spur GEWINNT → Remux + behaelt Englisch, verwirft die korrekte dt. Spur, ersetzt das Original atomar in-place (500) und strippt + .DL. → irreversibler Datenverlust + falsche Sprache, als Erfolg gemeldet. Single-Trigger (nicht compound). + Betrifft genau die vom Nutzer bestaetigte dt.-Tonspur-Funktion [[project_german_audio_feature]]. Fix: + `if (lang) return false;` VOR dem Titel-Fallback (deckt sich mit der Kommentar-Absicht). Rot-bewiesen + (2 Tests: eng-Titel-"German" vs echte ger → audioRelIndex 1; eng-Titel-"Deutsch entfernt" → skip). tsc=6. +- **DOKUMENTIERT, nicht gefixt (LOW compound) PP-1/#13 Companion-Overwrite:** renameCompanionFiles/ + moveCompanionFiles (dl-mgr 3735/3791) ohne Uniqueness-Guard (anders als MKV via buildUniqueFlattenTargetPath); + cross-volume copyFile ohne COPYFILE_EXCL (3696) → ueberschreibt einen vorhandenen Orphan-Companion still + (nur .srt/.idx/.nfo, nie Archiv/Video). Compound (Orphan + Namenskollision), Sekundaerdateien, Integrations- + TDD noetig → dokumentiert. Fix bekannt (Uniqueness-Guard spiegeln). +- **Deferred-Extraction-Cluster re-klassifiziert (aus Runde 2):** #10 CRC-Kleindatei-Delete BENIGN (failed + Archive aus cleanupSources ausgeschlossen, nicht geloescht); #12 7z-Exit-1-als-Erfolg BENIGN (nur Erfolg + wenn KEINE Error-Marker — echte CRC/Korruption korrekt als Fehler); #11 resume-empty-output NOT-CONFIRMED + (compound, unbewiesene Hybrid-Collect-Ordering-Annahme); #13 = PP-1. + ## Runde 6 (Retry/Backoff/Error-Klassifikations-State-Machine + Disk/IO) — Workflow wqurq83ma 3 Finder + adversarisch verifizieren. 1 confirmed (2/3), 3 refuted. Schliesst den deferred-LOW-Cluster ab. - **CONFIRMED HIGH (GEFIXT) SHELVE-LOOP-RETRYLIMIT:** Bei FINITEM retryLimit>=5 sind alle drei Per-Klasse- diff --git a/tasks/entscheidungen-offen.md b/tasks/entscheidungen-offen.md index 3cc53b2..3d468e3 100644 --- a/tasks/entscheidungen-offen.md +++ b/tasks/entscheidungen-offen.md @@ -63,6 +63,16 @@ ODER zwei unabhängige Pro-Modus-Schalter (mehr Kontrolle, aber UI + Migration n ## Erledigt in dieser Runde (zur Info, kein Handlungsbedarf) +- **Deutsche Tonspur: falsche Spur-Auswahl behoben (Datenverlust-Schutz):** Die Erkennung der + deutschen Tonspur hat den Titel-Text einer Spur („...German...") auch dann ausgewertet, wenn die + Spur bereits ein anderssprachiges Tag hatte (z.B. eine englische Spur mit „German" im Titel, wie + „German Commentary"). Lag so eine Spur VOR der korrekt mit „ger" getaggten Spur, wurde die falsche + (englische) behalten und die echte deutsche Spur beim Remux unwiderruflich verworfen — das Ergebnis + wurde als Erfolg gemeldet. Jetzt wird der Titel nur noch dann herangezogen, wenn gar kein Sprach-Tag + vorhanden ist (so war es ohnehin gemeint). Korrekt getaggte deutsche Spuren gewinnen jetzt immer. + Rot-bewiesener Test. (Noch nicht released — wird mit der nächsten Runde gebündelt.) + + - **Endlos-Wiederholung bei festem Wiederholungslimit behoben:** Wenn du ein FESTES Retry-Limit (z.B. 5) eingestellt hattest UND ein Link sprunghaft verschiedene Fehlerarten produzierte (mal Umwandlungs-Timeout, mal Abbruch mitten im Download, mal allgemeiner Fehler), konnte ein diff --git a/tests/video-processor.test.ts b/tests/video-processor.test.ts index ccdbcb9..90a31f6 100644 --- a/tests/video-processor.test.ts +++ b/tests/video-processor.test.ts @@ -92,6 +92,17 @@ describe("pickAudioTrack", () => { expect(d).toMatchObject({ action: "remux", audioRelIndex: 0, reason: "fallback-first-untagged" }); }); + it("tag mode does NOT let an eng track titled 'German' beat a correctly-tagged German track", () => { + const engTitledGerman = { language: "eng", title: "German Commentary" }; + const d = pickAudioTrack([engTitledGerman, ger], "tag"); + expect(d).toMatchObject({ action: "remux", audioRelIndex: 1, reason: "german-tag" }); + }); + + it("tag mode does NOT pick a non-German-tagged track just because its title contains 'Deutsch'", () => { + const d = pickAudioTrack([{ language: "eng", title: "Deutsch entfernt" }, { language: "fre", title: "" }], "tag"); + expect(d).toMatchObject({ action: "skip", reason: "no-german-track" }); + }); + it("tag mode with single German -> single (no remux)", () => { expect(pickAudioTrack([ger], "tag")).toMatchObject({ action: "single" }); });