// 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: → 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) }