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>
69 lines
2.8 KiB
TypeScript
69 lines
2.8 KiB
TypeScript
// DER W-2-BEWEIS: ein voller, verlustfreier Rip der eingelegten Disc am
|
||
// echten Laufwerk — dauert 30–90 Minuten. Läuft nur auf Verlangen:
|
||
//
|
||
// RIPPY_MESSUNG_RIP=1 RIPPY_MESSUNG_ZIEL=<ordner> \
|
||
// npx vitest run test/messung.rip.test.ts --reporter=verbose
|
||
//
|
||
// Fortschritt wird in 5-%-Schritten geloggt; das Ergebnis nennt Dateien
|
||
// und Größen. Das Protokoll wandert in den SAVEPOINT (beweisen statt
|
||
// behaupten).
|
||
import { statSync } from 'node:fs'
|
||
import { describe, expect, it } from 'vitest'
|
||
|
||
const messen = process.env.RIPPY_MESSUNG_RIP === '1' && process.platform === 'win32'
|
||
const zielWurzel = process.env.RIPPY_MESSUNG_ZIEL ?? ''
|
||
|
||
describe.skipIf(!messen)('Voller Rip am echten Laufwerk', () => {
|
||
it('rippt die eingelegte Disc verlustfrei', { timeout: 3 * 60 * 60 * 1000 }, async () => {
|
||
expect(zielWurzel.length, 'RIPPY_MESSUNG_ZIEL fehlt').toBeGreaterThan(0)
|
||
const { finden } = await import('../src/kern/werkzeuge/katalog')
|
||
const { rippen } = await import('../src/kern/rip/makemkv')
|
||
const { win32Api } = await import('../src/kern/laufwerk/win32')
|
||
const { listOptischeLaufwerke } = await import('../src/kern/laufwerk/disc')
|
||
|
||
const programm = finden('makemkv')
|
||
expect(programm.length).toBeGreaterThan(0)
|
||
const geraete = listOptischeLaufwerke(win32Api())
|
||
expect(geraete.length).toBeGreaterThan(0)
|
||
|
||
const start = Date.now()
|
||
let letzteStufe = -5
|
||
const ergebnis = await rippen({
|
||
programm,
|
||
geraetepfad: geraete[0],
|
||
zielOrdner: zielWurzel,
|
||
titel: 'all',
|
||
aufFortschritt: (prozent) => {
|
||
if (prozent >= letzteStufe + 5) {
|
||
letzteStufe = prozent
|
||
const minuten = ((Date.now() - start) / 60000).toFixed(1)
|
||
console.log(`RIP-FORTSCHRITT ${prozent}% nach ${minuten}min`)
|
||
}
|
||
},
|
||
aufMeldung: (code, text) => {
|
||
// Nur die tragenden Meldungen — sonst wird das Protokoll unlesbar.
|
||
if (code >= 5000 || text.includes('errors') || text.includes('gesichert') || text.includes('saved')) {
|
||
console.log(`RIP-MSG ${code}: ${text}`)
|
||
}
|
||
},
|
||
})
|
||
|
||
const minuten = ((Date.now() - start) / 60000).toFixed(1)
|
||
if (ergebnis.status === 'success') {
|
||
console.log(`RIP-ERGEBNIS success nach ${minuten}min · lesefehler=${ergebnis.lesefehler}`)
|
||
for (const datei of ergebnis.dateien) {
|
||
console.log(`RIP-DATEI ${datei} · ${(statSync(datei).size / 1e9).toFixed(2)} GB`)
|
||
}
|
||
} else {
|
||
console.log(`RIP-ERGEBNIS ${ergebnis.status} nach ${minuten}min: ${'fehler' in ergebnis ? ergebnis.fehler : ''}`)
|
||
}
|
||
expect(ergebnis.status).toBe('success')
|
||
})
|
||
})
|
||
|
||
describe.skipIf(messen)('Rip-Messung übersprungen', () => {
|
||
it('läuft nur mit RIPPY_MESSUNG_RIP=1 unter Windows', () => {
|
||
expect(true).toBe(true)
|
||
})
|
||
})
|