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.
In 5.2.0 und 5.3.0 blieb der Dialog ewig bei "Rippy liest die Titel der
Disc …" stehen. Der Grund war ein Fehler in Rippy selbst: Die Anfrage
"lies die Titel" war dem Kern unbekannt und wurde weggeworfen — still.
Behoben, und zwar auf drei Ebenen: Die Anfrage ist jetzt bekannt, eine
verworfene Nachricht sagt künftig laut warum, und ein neuer Prüf-Test
vergleicht bei jedem Bau die Nachrichten-Liste mit dem Prüfer. Derselbe
Fehler kann so nicht wiederkommen.
Die Titelliste bleibt jetzt stehen.
In 5.3.1 kam die Auswahl korrekt — und Minuten spaeter legte sich ein
"keine Antwort nach 300 s" darueber. Der Grund: Ein aelterer, laengst
ueberholter Lese-Vorgang meldete verspaetet seinen Abbruch. Jetzt gilt je
Laufwerk nur der NEUESTE Lauf; ein abgeloester gibt das Laufwerk sofort
frei und schweigt. Nebenbei bremsen sich damit nicht mehr zwei Lesevorgaenge
auf derselben Disc gegenseitig aus.
Rippy sagt jetzt, WARUM keine Titel kamen.
Findet MakeMKV das Laufwerk nicht (das passiert nach abgebrochenen
Rips), stand dort bisher nur "keine Titel gefunden" — als läge es an der
Disc. Jetzt steht die echte Ursache da, samt Abhilfe: Disc auswerfen und
neu einlegen, Laufwerk ab- und anstecken, notfalls neu starten.
Und wenn es wirklich zu lange dauert, steht da jetzt ein Satz statt einer
Fehlermeldung: Bei einer beschaedigten Disc kann das Lesen ueber fuenf
Minuten brauchen — Rippy beendet den Versuch dann und gibt das Laufwerk frei.
Aus 5.3.0: Serien landen unter Serien/<Titel>/Season NN, Filme bekommen
ihre Extras im Unterordner "extras".
Aus 5.3.1: Die Titel-Auswahl funktioniert (der Kern verwarf die Anfrage
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.
Unveraendert: Updates warten immer, bis kein Rip mehr laeuft.
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "rippy",
"version": "5.3.1",
"version": "5.3.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "rippy",
"version": "5.3.1",
"version": "5.3.2",
"license": "UNLICENSED",
"dependencies": {
"electron-updater": "^6.8.9",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "rippy",
"productName": "Rippy",
"version": "5.3.1",
"version": "5.3.2",
"description": "Rippy v5 — Disc-Ripping als eigenständiges Windows-Programm (KONZEPT-WINDOWS.md)",
"main": "out/haupt/index.js",
"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. */
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. */
export class RipAuswertung {
prozent = -1
@@ -153,6 +157,7 @@ export function titelInfoLesen(
programm: string,
geraetepfad: string,
timeoutMs = 300_000,
abbruch?: AbortSignal,
): Promise<DiscInfo> {
return new Promise((fertig, scheitert) => {
const kind = spawn(programm, buildInfoArgs(geraetepfad), {
@@ -166,12 +171,24 @@ export function titelInfoLesen(
kind.kill()
scheitert(new Error(`makemkvcon info: keine Antwort nach ${Math.round(timeoutMs / 1000)} s`))
}, 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) => {
clearTimeout(zeiger)
abbruch?.removeEventListener('abort', aufgeben)
scheitert(fehler)
})
kind.on('close', () => {
clearTimeout(zeiger)
abbruch?.removeEventListener('abort', aufgeben)
const ausgabe = textVon(Buffer.concat(stuecke))
const titel = parseTitelInfo(ausgabe)
const streams = parseStreamInfo(ausgabe)