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>
104 lines
4.3 KiB
JavaScript
104 lines
4.3 KiB
JavaScript
// Nachgebautes HandBrakeCLI für den Klick-Beweis (bau/klick-smoke.mjs).
|
|
// Antwortet so, wie encoder.ts, scan.ts und handbrake.ts es am echten
|
|
// HandBrake 1.11 gemessen haben:
|
|
// --help → Encoder-Block „-e, --encoder" bis zur nächsten Option
|
|
// --preset-list → Preset-Namen mit genau vier Leerzeichen Einrückung
|
|
// --scan --json → „JSON Title Set:" + Objekt + „HandBrake has exited."
|
|
// Encode → „Encoding: task 1 of 1, x %" in Stufen, Datei kopiert;
|
|
// ab der zweiten Stufe mit der fps/ETA-Klammer, wie das
|
|
// Binary sie formatiert (5.6.0, Restzeit).
|
|
const fs = require('node:fs')
|
|
|
|
const args = process.argv.slice(2)
|
|
const out = (zeile) => process.stdout.write(zeile + '\n')
|
|
const wert = (schalter) => {
|
|
const i = args.indexOf(schalter)
|
|
return i >= 0 ? args[i + 1] : undefined
|
|
}
|
|
|
|
if (args.includes('--help') || args.includes('-h')) {
|
|
out('Usage: HandBrakeCLI [options] -i <source> -o <destination>')
|
|
out('')
|
|
out('Video Options -----------------------------------------------------------------')
|
|
out('')
|
|
out(' -e, --encoder <string> Select video encoder:')
|
|
out(' x264')
|
|
out(' x265')
|
|
out(' nvenc_h264')
|
|
out(' -q, --quality <float> Set video quality (e.g. 22.0)')
|
|
out('')
|
|
process.exit(0)
|
|
}
|
|
|
|
if (args.includes('--preset-list')) {
|
|
out('General/')
|
|
out(' Very Fast 1080p30')
|
|
out(' Fast 1080p30')
|
|
out(' HQ 1080p30 Surround')
|
|
out('')
|
|
process.exit(0)
|
|
}
|
|
|
|
if (args.includes('--scan')) {
|
|
const eingabe = wert('--input') ?? wert('-i') ?? 'quelle'
|
|
out('[12:00:00] hb_init: starting libhb thread')
|
|
out('Scanning title 1 of 1, preview 1, 100.00 %')
|
|
out('JSON Title Set: {')
|
|
out('"MainFeature": 0,')
|
|
out('"TitleList": [')
|
|
out('{')
|
|
// 5.7.0: CodecName und ChannelCount wie das echte 1.11.2 (gemessen
|
|
// 12.09.2026) — dieselben Spuren, die fake-makemkvcon meldet, ohne den
|
|
// DTS-Kern (den nimmt MakeMKVs Regel nicht mit).
|
|
out('"AudioList": [')
|
|
out('{ "ChannelCount": 6, "ChannelLayout": 1551, "CodecName": "dtshd", "Description": "Japanese (DTS-HD MA, 5.1 ch)", "Language": "Japanese", "LanguageCode": "jpn", "TrackNumber": 1 },')
|
|
out('{ "ChannelCount": 6, "ChannelLayout": 1551, "CodecName": "ac3", "Description": "Deutsch (AC3, 5.1 ch)", "Language": "Deutsch", "LanguageCode": "deu", "TrackNumber": 2 },')
|
|
out('{ "ChannelCount": 2, "ChannelLayout": 3, "CodecName": "ac3", "Description": "Deutsch (AC3, 2.0 ch)", "Language": "Deutsch", "LanguageCode": "deu", "TrackNumber": 3 }')
|
|
out('],')
|
|
out('"Duration": { "Hours": 0, "Minutes": 52, "Seconds": 47, "Ticks": 542070 },')
|
|
out('"Index": 1,')
|
|
out(`"Name": ${JSON.stringify(eingabe)},`)
|
|
out('"SubtitleList": [')
|
|
out('{ "Format": "bitmap", "Language": "Deutsch", "LanguageCode": "deu", "Source": 5, "SourceName": "PGS", "TrackNumber": 1 },')
|
|
out('{ "Format": "bitmap", "Language": "Deutsch", "LanguageCode": "deu", "Source": 5, "SourceName": "PGS", "TrackNumber": 2 },')
|
|
out('{ "Format": "bitmap", "Language": "English", "LanguageCode": "eng", "Source": 5, "SourceName": "PGS", "TrackNumber": 3 }')
|
|
out(']')
|
|
out('}')
|
|
out(']')
|
|
out('}')
|
|
out('HandBrake has exited.')
|
|
process.exit(0)
|
|
}
|
|
|
|
const eingabe = wert('--input') ?? wert('-i')
|
|
const ausgabe = wert('--output') ?? wert('-o')
|
|
if (eingabe === undefined || ausgabe === undefined) {
|
|
out('Missing input or output file name. Run HandBrakeCLI --help for syntax.')
|
|
process.exit(1)
|
|
}
|
|
out('[12:00:00] hb_init: starting libhb thread')
|
|
out('Scanning title 1 of 1, preview 10, 100.00 %')
|
|
let schritt = 0
|
|
const stufen = [5, 30, 55, 80, 100]
|
|
const tick = () => {
|
|
const prozent = stufen[schritt].toFixed(2)
|
|
if (schritt === 0) out(`Encoding: task 1 of 1, ${prozent} %`)
|
|
else {
|
|
// Formatstring aus dem Binary (1.11.2): „%.2f %% (%.2f fps, avg %.2f fps, ETA %02dh%02dm%02ds)"
|
|
const rest = (stufen.length - 1 - schritt) * 300
|
|
out(`Encoding: task 1 of 1, ${prozent} % (48.00 fps, avg 47.50 fps, ETA 00h${String(Math.floor(rest / 60)).padStart(2, '0')}m${String(rest % 60).padStart(2, '0')}s)`)
|
|
}
|
|
schritt += 1
|
|
if (schritt < stufen.length) {
|
|
setTimeout(tick, 500)
|
|
return
|
|
}
|
|
fs.copyFileSync(eingabe, ausgabe)
|
|
out('[12:00:05] libhb: work result = 0')
|
|
out('')
|
|
out('Encode done!')
|
|
out('HandBrake has exited.')
|
|
process.exit(0)
|
|
}
|
|
setTimeout(tick, 200)
|