fix(v5): Nur der NEUESTE Titel-Lauf antwortet — ein alter legte sich darueber
Ampel / ampel (push) Successful in 1m49s

Commander: "Er liest es korrekt ein, ich bekomme auch die auswahl aber
kurz danach kommt dieser fehler" — "makemkvcon info: keine Antwort nach
300 s", MINUTEN nach der fertigen Titelliste.

Mit nur einem Lauf ist das unmoeglich: Der Timeout wird bei 'close'
geloescht. Es waren also zwei Laeufe unterwegs, und der aeltere meldete
verspaetet seinen Abbruch — auf die laengst gelieferte Liste drauf.

Behoben, je Laufwerk:
* Ein neuer Lauf LOEST den alten AB: AbortController im Kern,
  titelInfoLesen nimmt jetzt ein AbortSignal, killt das Kind und lehnt
  mit der Marke ABGELOEST ab. Das Laufwerk ist sofort frei, und es
  laufen nie zwei makemkvcon auf derselben Disc.
* Nur die NEUESTE Laufnummer darf antworten (titelLaufNummern) — eine
  verspaetete Antwort wird verworfen, nicht angezeigt.
* Ein abgeloester Lauf SCHWEIGT: abgeloest ist Absicht, kein Fehlschlag.
* Laeuft es wirklich in den Timeout, steht jetzt ein Satz statt einer
  Fehlermeldung: Bei einer beschaedigten Disc kann das Lesen ueber fuenf
  Minuten brauchen; Rippy beendet den Versuch und gibt das Laufwerk frei.

Am lebenden Objekt bestaetigt (SPARTACUS_GOTA_D1, Commander-Screenshot):
"Serie: 2 Folgen erwartet, 2 gefunden - passt." | Titel 0 52:47 min
16,28 GB 7 Kap. | Titel 1 54:09 min 16,67 GB 7 Kap. | beide Rolle Folge |
2 von 2 gewaehlt, zusammen 32,95 GB — und KEIN Timeout hinterher.

Die Laengen liegen 2,5 Prozent auseinander, also weit innerhalb von
AEHNLICH (15 Prozent) — deshalb "passt" ohne Unsicherheits-Warnung.

261 Tests gruen, Typpruefung sauber. Version 5.3.2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-01 20:29:25 +02:00
co-authored by Claude Opus 5
parent 9622f4e6ef
commit 6d149212c9
5 changed files with 803 additions and 759 deletions
+14 -16
View File
@@ -1,22 +1,20 @@
Version 5.3.1 Version 5.3.2
Die Titel-Auswahl funktioniert jetzt wirklich. Die Titelliste bleibt jetzt stehen.
In 5.2.0 und 5.3.0 blieb der Dialog ewig bei "Rippy liest die Titel der In 5.3.1 kam die Auswahl korrekt — und Minuten spaeter legte sich ein
Disc …" stehen. Der Grund war ein Fehler in Rippy selbst: Die Anfrage "keine Antwort nach 300 s" darueber. Der Grund: Ein aelterer, laengst
"lies die Titel" war dem Kern unbekannt und wurde weggeworfen — still. ueberholter Lese-Vorgang meldete verspaetet seinen Abbruch. Jetzt gilt je
Behoben, und zwar auf drei Ebenen: Die Anfrage ist jetzt bekannt, eine Laufwerk nur der NEUESTE Lauf; ein abgeloester gibt das Laufwerk sofort
verworfene Nachricht sagt künftig laut warum, und ein neuer Prüf-Test frei und schweigt. Nebenbei bremsen sich damit nicht mehr zwei Lesevorgaenge
vergleicht bei jedem Bau die Nachrichten-Liste mit dem Prüfer. Derselbe auf derselben Disc gegenseitig aus.
Fehler kann so nicht wiederkommen.
Rippy sagt jetzt, WARUM keine Titel kamen. Und wenn es wirklich zu lange dauert, steht da jetzt ein Satz statt einer
Findet MakeMKV das Laufwerk nicht (das passiert nach abgebrochenen Fehlermeldung: Bei einer beschaedigten Disc kann das Lesen ueber fuenf
Rips), stand dort bisher nur "keine Titel gefunden" — als läge es an der Minuten brauchen — Rippy beendet den Versuch dann und gibt das Laufwerk frei.
Disc. Jetzt steht die echte Ursache da, samt Abhilfe: Disc auswerfen und
neu einlegen, Laufwerk ab- und anstecken, notfalls neu starten.
Aus 5.3.0: Serien landen unter Serien/<Titel>/Season NN, Filme bekommen Aus 5.3.1: Die Titel-Auswahl funktioniert (der Kern verwarf die Anfrage
ihre Extras im Unterordner "extras". vorher still), und Rippy nennt die Ursache, wenn MakeMKV kein Laufwerk findet.
Aus 5.3.0: Serien unter Serien/<Titel>/Season NN, Film-Extras im Unterordner.
Aus 5.2.0: Auswahl, was gerippt wird — plus der Defekt-Waechter. Aus 5.2.0: Auswahl, was gerippt wird — plus der Defekt-Waechter.
Unveraendert: Updates warten immer, bis kein Rip mehr laeuft. Unveraendert: Updates warten immer, bis kein Rip mehr laeuft.
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "rippy", "name": "rippy",
"version": "5.3.1", "version": "5.3.2",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "rippy", "name": "rippy",
"version": "5.3.1", "version": "5.3.2",
"license": "UNLICENSED", "license": "UNLICENSED",
"dependencies": { "dependencies": {
"electron-updater": "^6.8.9", "electron-updater": "^6.8.9",
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "rippy", "name": "rippy",
"productName": "Rippy", "productName": "Rippy",
"version": "5.3.1", "version": "5.3.2",
"description": "Rippy v5 — Disc-Ripping als eigenständiges Windows-Programm (KONZEPT-WINDOWS.md)", "description": "Rippy v5 — Disc-Ripping als eigenständiges Windows-Programm (KONZEPT-WINDOWS.md)",
"main": "out/haupt/index.js", "main": "out/haupt/index.js",
"author": "KrBrZ", "author": "KrBrZ",
File diff suppressed because it is too large Load Diff
+17
View File
@@ -45,6 +45,10 @@ export function buildRipArgs(geraetepfad: string, zielOrdner: string, titel: str
* von einem echten Defekt zu unterscheiden. */ * von einem echten Defekt zu unterscheiden. */
export const HAENGT_AB = 12 export const HAENGT_AB = 12
/** Kennzeichnet einen Titel-Lauf, den ein NEUERER abgelöst hat. Der
* Aufrufer schweigt dazu — abgelöst ist kein Fehler, sondern Absicht. */
export const ABGELOEST = 'titel-lauf-abgeloest'
/** Auswertung der Rip-Ausgabe — pur, eine Zeile nach der anderen. */ /** Auswertung der Rip-Ausgabe — pur, eine Zeile nach der anderen. */
export class RipAuswertung { export class RipAuswertung {
prozent = -1 prozent = -1
@@ -153,6 +157,7 @@ export function titelInfoLesen(
programm: string, programm: string,
geraetepfad: string, geraetepfad: string,
timeoutMs = 300_000, timeoutMs = 300_000,
abbruch?: AbortSignal,
): Promise<DiscInfo> { ): Promise<DiscInfo> {
return new Promise((fertig, scheitert) => { return new Promise((fertig, scheitert) => {
const kind = spawn(programm, buildInfoArgs(geraetepfad), { const kind = spawn(programm, buildInfoArgs(geraetepfad), {
@@ -166,12 +171,24 @@ export function titelInfoLesen(
kind.kill() kind.kill()
scheitert(new Error(`makemkvcon info: keine Antwort nach ${Math.round(timeoutMs / 1000)} s`)) scheitert(new Error(`makemkvcon info: keine Antwort nach ${Math.round(timeoutMs / 1000)} s`))
}, timeoutMs) }, timeoutMs)
// Ein abgelöster Lauf lässt das Laufwerk sofort los — zwei makemkvcon
// auf derselben Disc machen beide langsamer (Commander-Fund 01.09.2026:
// Ein alter Lauf meldete 300 s später seinen Timeout und legte ihn über
// die längst gelieferte Titelliste).
const aufgeben = (): void => {
clearTimeout(zeiger)
kind.kill()
scheitert(new Error(ABGELOEST))
}
abbruch?.addEventListener('abort', aufgeben, { once: true })
kind.on('error', (fehler) => { kind.on('error', (fehler) => {
clearTimeout(zeiger) clearTimeout(zeiger)
abbruch?.removeEventListener('abort', aufgeben)
scheitert(fehler) scheitert(fehler)
}) })
kind.on('close', () => { kind.on('close', () => {
clearTimeout(zeiger) clearTimeout(zeiger)
abbruch?.removeEventListener('abort', aufgeben)
const ausgabe = textVon(Buffer.concat(stuecke)) const ausgabe = textVon(Buffer.concat(stuecke))
const titel = parseTitelInfo(ausgabe) const titel = parseTitelInfo(ausgabe)
const streams = parseStreamInfo(ausgabe) const streams = parseStreamInfo(ausgabe)