feat(v5): Etappe W-2 — Rippen: Parser, makemkvcon-Lauf, Werkzeug-Katalog
Ampel / ampel (push) Failing after 1m22s

rip/parser.ts: die reine Textverarbeitung mit allen bezahlten Fallen aus
dem Docker-Zweig — quelle() (dev:G: statt dev:\.\G:, der 404-Verwandte),
textVon() (UTF-8 streng, sonst cp1252 — nie wieder 'Öffnen'), parseMsg
(Regex statt split — Meldungen mit Komma), PRGV (-1 vs. echtes 0 %),
TINFO/SINFO (Sprache aus Attr 3/4, NICHT 28/29), laengsterTitel mit
Play-All-Schutz, episodenTitel, KRITISCHE_CODES (sprachfeste NUMMERN).

rip/makemkv.ts: Kommandobau + Lauf — ZeilenLeser (binaer, halbe Zeilen,
je Zeile dekodiert), RipAuswertung (pur), rippen() mit AbortSignal,
Lesefehler-Flag AUCH bei Rueckgabewert 0. werkzeuge/katalog.ts: die
Suchreihenfolge samt %VAR%-Gross/Klein-Falle und Registry-Rueckfall.
ablauf/rippen.ts: ein Rip je Laufwerk, Status-Push, 'erst nachsehen,
dann rippen' (unbekannt heisst trotzdem rippen). Fenster: Rippen-Knopf,
Fortschrittsbalken, Abbrechen; Auswurf waehrend des Rips abgelehnt.

Gemessen am echten System (30.08.2026): Katalog findet
makemkvcon64 (Program Files x86), HandBrakeCLI und flac (beide im
rc11-Werkzeugordner LOCALAPPDATA\Rippy\tools). NEUER BEFUND: Nach dem
abgebrochenen rc11-Rip vom Mittag sieht MakeMKV GAR KEIN Laufwerk mehr
(MSG 5042, leere DRV-Liste, mit und ohne --noscan), waehrend die
Storage-IOCTLs sauber antworten — MakeMKV spricht SCSI direkt. 5042 ist
jetzt ein KRITISCHER_CODE mit Klartext-Abhilfe. Der volle Rip-Beweis
(messung.rip.test.ts liegt bereit) braucht deshalb erst eine Hand am
Geraet: Disc neu einlegen oder USB ab-/anstecken.

96 Tests gruen, Smoke gruen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-08-30 18:12:44 +02:00
co-authored by Claude Fable 5
parent a45069af5e
commit 844bca37ff
13 changed files with 1495 additions and 23 deletions
+37 -2
View File
@@ -38,6 +38,20 @@ export type WacheEreignis =
export type AuswurfGrund = 'ok' | 'kein-zugriff' | 'abgelehnt' | 'bleibt-drin'
// ── Rippen (Etappe W-2) ─────────────────────────────────────────────────
export type RipPhase = 'liest-info' | 'rippt' | 'fertig' | 'fehler' | 'abgebrochen'
export interface RipStatus {
/** Laufwerks-Kennung, auf der der Rip läuft. */
id: string
phase: RipPhase
/** 0100; -1 heißt „noch keine Angabe" (ein echtes 0 % bleibt sichtbar). */
prozent: number
text: string
dateien: string[]
lesefehler: boolean
}
/** Nachrichten, die der Kern verschickt. */
export type KernNachricht =
| { art: 'kern-bereit'; pid: number; nodeVersion: string; datenbank: DbStatus }
@@ -45,9 +59,14 @@ export type KernNachricht =
| { art: 'kern-fehler'; text: string }
| { art: 'laufwerke'; liste: GeraeteInfo[]; ereignisse: WacheEreignis[] }
| { art: 'auswurf-ergebnis'; id: string; grund: AuswurfGrund; fehler: string | null }
| { art: 'rip-status'; status: RipStatus }
/** Nachrichten, die das Fenster über den MessagePort an den Kern schickt. */
export type FensterNachricht = { art: 'ping'; zeit: number } | { art: 'auswerfen'; id: string }
export type FensterNachricht =
| { art: 'ping'; zeit: number }
| { art: 'auswerfen'; id: string }
| { art: 'rip-start'; id: string }
| { art: 'rip-abbruch'; id: string }
/** Nachrichten, die das Haupt an den Kern schickt (Port-Übergabe). */
export type HauptNachricht = { art: 'fenster-port' }
@@ -85,11 +104,25 @@ export function istKernNachricht(wert: unknown): wert is KernNachricht {
typeof wert.grund === 'string' &&
(wert.fehler === null || typeof wert.fehler === 'string')
)
case 'rip-status':
return istRipStatus(wert.status)
default:
return false
}
}
export function istRipStatus(wert: unknown): wert is RipStatus {
if (!istObjekt(wert)) return false
return (
typeof wert.id === 'string' &&
typeof wert.phase === 'string' &&
typeof wert.prozent === 'number' &&
typeof wert.text === 'string' &&
Array.isArray(wert.dateien) &&
typeof wert.lesefehler === 'boolean'
)
}
export function istDbStatus(wert: unknown): wert is DbStatus {
if (!istObjekt(wert)) return false
if (wert.ok === true) return typeof wert.pfad === 'string'
@@ -100,7 +133,9 @@ export function istDbStatus(wert: unknown): wert is DbStatus {
export function istFensterNachricht(wert: unknown): wert is FensterNachricht {
if (!istObjekt(wert)) return false
if (wert.art === 'ping') return typeof wert.zeit === 'number'
if (wert.art === 'auswerfen') return typeof wert.id === 'string' && wert.id.length > 0
if (wert.art === 'auswerfen' || wert.art === 'rip-start' || wert.art === 'rip-abbruch') {
return typeof wert.id === 'string' && wert.id.length > 0
}
return false
}