WAS: Der Auswahl-Dialog zeigt je Titel jede Ton- und Untertitelspur, die der Info-Lauf gemessen hat („German · DTS-HD MA · Surround 5.1", „German · DD · Stereo (Kommentar)", „German · PGS (nur erzwungene)"), vorbelegt wie bisher (je Wunschsprache die erste Tonspur, alle Untertitel der Wunsch- sprachen) — angehakt wird einzeln. Die Untertitel-Vorwahl je Extra ist jetzt eine Spur. Der Parser liest dazu SINFO-Attribute 2 (Anordnung), 22 (Flags) und markiert abgeleitete Spuren (Bit 0x800, an der Kizuna- Disc gemessen): DTS-Kerne werden nicht angeboten, „nur erzwungene"- Untertitel schon. Die Ablage scannt die Roh-Datei und findet die gewählten MakeMKV-Spuren gestuft wieder (Sprache + Codec-Familie + Kanäle → … → Sprache), HandBrake bekommt `--audio 1,3 --subtitle 2,1 --subtitle-default=1`; Fehlendes wird gemeldet, scheitert der Scan, gelten die Sprachen der gewählten Spuren als Liste. MakeMKVs Auswahl- regel lässt Stereo-Doppel jetzt stehen (nur -sel:havecore); Rippys alte Regel wird in der Registry ersetzt, eine fremde bleibt. Dazu F31 (Durchsicht): `--aencoder copy` ohne `--audio-copy-mask` verengt HandBrakeCLI 1.11.2 auf „copy:aac" — AC3/DTS/TrueHD wurden seit 5.0 in AAC umgerechnet. Beide Wege setzen die Maske jetzt ausdrücklich (gemessen 12.09.2026: AC3 bleibt AC3, Passthru-Zeile nennt alle Codecs). WARUM: Commander-Entscheid 12.09.2026 („Jede Spur einzeln anhaken") — bisher ließ sich je Sprache nur „alles oder nichts" wählen, und der Kommentar-Ton fehlte in der Roh-Datei (havemulti). Gemessen am mitgelieferten HandBrakeCLI mit ffmpeg-gebauter Quelle (test/messung.spuren.test.ts, RIPPY_MESSUNG=1): --audio 1,3 → jpn und deu-Kommentar, --subtitle 2,1 --subtitle-default=1 → eng (default) vor deu, --subtitle none → keine Spur. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
89 lines
3.8 KiB
JavaScript
89 lines
3.8 KiB
JavaScript
// Nachgebautes makemkvcon für den Klick-Beweis (bau/klick-smoke.mjs).
|
|
// Spricht den Robot-Modus so, wie der Parser (kern/rip/parser.ts) ihn an
|
|
// echten Discs gemessen hat: TINFO (8 Kapitel, 9 Dauer, 11 Bytes), SINFO
|
|
// (1 Typ, 3 Sprachcode, 4 Sprachname), PRGV (aktuell,gesamt,max), MSG.
|
|
//
|
|
// info dev:X: → drei Titel: zwei Folgen (52:47 / 54:09, jpn+deu
|
|
// Ton, deu+eng Untertitel) und ein Logo-Schnipsel
|
|
// mkv dev:X: <nr> <ziel> → Fortschritt in Stufen, dann title_t0N.mkv
|
|
//
|
|
// Läuft über die eigene Node-Laufzeit (kern/werkzeuge/aufruf.ts) — kein EXE.
|
|
const fs = require('node:fs')
|
|
const path = require('node:path')
|
|
|
|
const args = process.argv.slice(2)
|
|
const befehl = args.find((a) => a === 'info' || a === 'mkv')
|
|
const out = (zeile) => process.stdout.write(zeile + '\n')
|
|
|
|
out('MSG:1005,0,1,"MakeMKV v1.18.4 win(x86-release) gestartet","%1 gestartet","MakeMKV v1.18.4 win(x86-release)"')
|
|
|
|
const TITEL = [
|
|
{ nr: 0, dauer: '0:52:47', bytes: 16283000000, kapitel: 7 },
|
|
{ nr: 1, dauer: '0:54:09', bytes: 16670000000, kapitel: 7 },
|
|
{ nr: 2, dauer: '0:00:35', bytes: 20000000, kapitel: 1 },
|
|
]
|
|
|
|
if (befehl === 'info') {
|
|
out(`TCOUNT:${TITEL.length}`)
|
|
for (const t of TITEL) {
|
|
out(`TINFO:${t.nr},8,0,"${t.kapitel}"`)
|
|
out(`TINFO:${t.nr},9,0,"${t.dauer}"`)
|
|
out(`TINFO:${t.nr},11,0,"${t.bytes}"`)
|
|
out(`SINFO:${t.nr},0,1,6201,"Video"`)
|
|
if (t.nr < 2) {
|
|
// 5.7.0: jede Spur mit Codec (6), Kanälen (14), Anordnung (2), Flags
|
|
// (22) und Beschreibung (30), wie an der Kizuna-Disc gemessen — samt
|
|
// dem abgeleiteten DTS-Kern (Flag 0x800), den der Dialog NICHT zeigt,
|
|
// und der „nur erzwungene"-Fassung eines Untertitels, die er zeigt.
|
|
const spur = (nr, code, typ, lang, name, codec, kanaele, layout, flags, text) => {
|
|
out(`SINFO:${t.nr},${nr},1,${code},"${typ}"`)
|
|
if (layout) out(`SINFO:${t.nr},${nr},2,0,"${layout}"`)
|
|
out(`SINFO:${t.nr},${nr},3,0,"${lang}"`)
|
|
out(`SINFO:${t.nr},${nr},4,0,"${name}"`)
|
|
out(`SINFO:${t.nr},${nr},6,0,"${codec}"`)
|
|
if (kanaele) out(`SINFO:${t.nr},${nr},14,0,"${kanaele}"`)
|
|
out(`SINFO:${t.nr},${nr},22,0,"${flags}"`)
|
|
out(`SINFO:${t.nr},${nr},30,0,"${text}"`)
|
|
}
|
|
spur(1, 6202, 'Audio', 'jpn', 'Japanese', 'DTS-HD MA', '6', 'Surround 5.1', 1024, 'DTS-HD MA Surround 5.1 Japanese')
|
|
spur(2, 6202, 'Audio', 'jpn', 'Japanese', 'DTS', '6', 'Surround 5.1', 2304, 'DTS Surround 5.1 Japanese')
|
|
spur(3, 6202, 'Audio', 'deu', 'German', 'DD', '6', 'Surround 5.1', 0, 'DD Surround 5.1 German')
|
|
spur(4, 6202, 'Audio', 'deu', 'German', 'DD', '2', 'Stereo', 0, 'DD Stereo German (Kommentar)')
|
|
spur(5, 6203, 'Untertitel', 'deu', 'German', 'PGS', '', '', 0, 'PGS German')
|
|
spur(6, 6203, 'Untertitel', 'deu', 'German', 'PGS', '', '', 6144, 'PGS German (nur erzwungene)')
|
|
spur(7, 6203, 'Untertitel', 'eng', 'English', 'PGS', '', '', 0, 'PGS English')
|
|
}
|
|
}
|
|
process.exit(0)
|
|
}
|
|
|
|
if (befehl === 'mkv') {
|
|
const i = args.indexOf('mkv')
|
|
const nr = Number(args[i + 2])
|
|
const ziel = args[i + 3]
|
|
if (!Number.isInteger(nr) || typeof ziel !== 'string') {
|
|
out('MSG:5010,0,0,"Falscher Aufruf"')
|
|
process.exit(1)
|
|
}
|
|
let schritt = 0
|
|
const stufen = [10, 35, 60, 85, 100]
|
|
const tick = () => {
|
|
out(`PRGV:${stufen[schritt]},${stufen[schritt]},100`)
|
|
schritt += 1
|
|
if (schritt < stufen.length) {
|
|
setTimeout(tick, 250)
|
|
return
|
|
}
|
|
fs.mkdirSync(ziel, { recursive: true })
|
|
const datei = path.join(ziel, `title_t${String(nr).padStart(2, '0')}.mkv`)
|
|
// 1 MB „Roh-Material" — groß genug, dass Größen sichtbar sind.
|
|
fs.writeFileSync(datei, Buffer.alloc(1024 * 1024, nr + 1))
|
|
out(`MSG:5003,0,1,"1 Titel gespeichert","%1 Titel gespeichert","1"`)
|
|
process.exit(0)
|
|
}
|
|
setTimeout(tick, 200)
|
|
} else {
|
|
out('MSG:5010,0,0,"Unbekannter Befehl"')
|
|
process.exit(1)
|
|
}
|