chore: add 04b_Add_EAC3_if_DTS.js from live FileFlows export

This commit is contained in:
sascha 2026-07-17 19:34:11 +02:00
parent 58f72719e1
commit b29cc860bb

View file

@ -0,0 +1,113 @@
/**
* @name 04b_Add_EAC3_if_DTS
* @description Haengt eine EAC3-Kompatibilitaetsspur an, OHNE das Video neu zu encoden
* (Video = copy). Trigger: die beste deutsche Tonspur ist eine DTS-Variante
* (dts, dts-hd, dts:x ...). Grund: DTS macht ueber HDMI bei manchen AVRs (Denon)
* A/V-Sync-Probleme; EAC3 ist robust. TrueHD/AC3/EAC3 sind unproblematisch ->
* dann passiert nichts (Datei laeuft unveraendert weiter).
* Einzuhaengen im "nur kopieren"-Zweig: 02_Detect_1080p Output 2 -> HIER -> 05_Finalize_Move
* @author Sascha
* @revision 2
* @output OK (mit oder ohne neue Spur) -> weiter zu Finalize_Move
* @output Fehler
*/
function Script()
{
// Variables.vi.VideoInfo ist im ScriptNode unzuverlaessig (leer oder verzoegert;
// verifiziert 2026-07-06). Audio-Info daher direkt per ffprobe holen -> echte Kanalzahlen.
function probeAudio() {
var probe = null;
try { probe = Flow.GetToolPath('ffprobe'); } catch (e) {}
if (!probe) probe = Variables['ffprobe'] || 'ffprobe';
var res = Flow.Execute({
command: probe,
argumentList: ['-v', 'quiet', '-print_format', 'json', '-show_streams', Flow.WorkingFile]
});
if (!res || res.exitCode !== 0) { Logger.WLog('ffprobe fehlgeschlagen'); return null; }
var j;
try { j = JSON.parse(res.standardOutput || res.output || ''); } catch (e) { Logger.WLog('ffprobe JSON-Fehler: ' + e); return null; }
var streams = j.streams || [], auds = [];
for (var i = 0; i < streams.length; i++) {
var s = streams[i];
if (s.codec_type !== 'audio') continue;
var t = s.tags || {}, disp = s.disposition || {};
auds.push({ Language: t.language || '', Codec: (s.codec_name || ''),
Channels: s.channels || 0, Title: t.title || '', Default: (disp.default === 1) });
}
return auds;
}
let audioStreams = probeAudio();
if (audioStreams === null) { Logger.WLog('Keine Audio-Info (ffprobe) - ueberspringe'); return 1; }
function langOf(a) { return ('' + (a.Language || '')).toLowerCase().trim(); }
function isGerman(a) { let l = langOf(a); return (l === 'ger' || l === 'deu' || l === 'de'); }
function acodec(a) { return ('' + (a.Codec || '')).toLowerCase(); }
function isDts(a) { return acodec(a).indexOf('dts') === 0; } // dts, dts-hd, dts:x ...
function channels(a) { return parseInt(a.Channels, 10) || 0; } // ffprobe liefert echte Kanalzahl
function atitle(a) { return '' + (a.Title || a.Name || ''); }
// von UNS frueher erzeugte EAC3-Kompatspur (Titel-Tag) -> beim Neu-Mux verwerfen (idempotent).
function isOldCompat(a) { return acodec(a).indexOf('eac3') !== -1 && atitle(a).indexOf('AVR-kompatibel') !== -1; }
// beste deutsche spur (meiste kanaele) + relativer audio-index (position im array, fuer -map 0:a:N)
let bestGer = null, bestGerAIdx = -1, excludeIdx = -1;
for (let i = 0; i < audioStreams.length; i++) {
if (isOldCompat(audioStreams[i])) { excludeIdx = i; continue; } // alte Kompatspur ignorieren
if (isGerman(audioStreams[i])) {
if (!bestGer || channels(audioStreams[i]) > channels(bestGer)) {
bestGer = audioStreams[i]; bestGerAIdx = i;
}
}
}
if (bestGer === null) {
Logger.ILog('EAC3-Kompatspur: keine deutsche Tonspur - Datei unveraendert weiter.');
return 1;
}
if (!isDts(bestGer)) {
Logger.ILog('EAC3-Kompatspur: beste DE-Spur ist ' + acodec(bestGer) +
' (kein DTS) - unproblematisch, Datei unveraendert weiter.');
return 1;
}
// -> beste DE-spur ist DTS: EAC3-kompatspur davor haengen, video + rest 1:1 kopieren
let ch = channels(bestGer) || 6;
let outCh = (ch >= 6) ? 6 : (ch >= 2 ? ch : 2); // EAC3 max 5.1 (ffmpeg-limit)
Logger.ILog('EAC3-Kompatspur: beste DE-Spur ist DTS (' + acodec(bestGer) + ' ' + ch +
'ch, Audio-Index ' + bestGerAIdx + ') -> haenge EAC3 ' + outCh + 'ch als Default an (Video COPY).');
let inFile = Flow.WorkingFile;
let outFile = Flow.TempPath + '/' + (Variables['file.Name'] || (Flow.NewGuid() + '.mkv'));
let ffmpeg = Flow.GetToolPath('ffmpeg') || Variables['ffmpeg'];
// verifizierte ffmpeg-syntax:
// -map 0:a:<de> -map 0:a -> EAC3-quelle als slot0, dann ALLE originale
// -c:a copy -c:a:0 eac3 -> alle copy AUSSER slot0
// -disposition:a 0 -disposition:a:0 default -> nur slot0 ist default
let args = ['-hide_banner', '-y', '-i', inFile,
'-map', '0:v', '-c:v', 'copy',
'-map', '0:a:' + bestGerAIdx, '-map', '0:a'];
if (excludeIdx >= 0) args.push('-map', '-0:a:' + excludeIdx); // alte Kompatspur raus
args.push('-c:a', 'copy', '-c:a:0', 'eac3', '-ac:a:0', '' + outCh,
'-b:a:0', (outCh >= 6 ? '640k' : '256k'),
'-metadata:s:a:0', 'title=Deutsch (EAC3 AVR-kompatibel)',
'-metadata:s:a:0', 'language=ger',
'-disposition:a', '0', '-disposition:a:0', 'default',
'-map', '0:s?', '-c:s', 'copy',
'-map', '0:t?', '-c:t', 'copy',
'-dn', '-max_muxing_queue_size', '9999',
outFile);
Logger.ILog('FFmpeg: ' + args.join(' '));
let result = Flow.Execute({ command: ffmpeg, argumentList: args });
if (!result || result.exitCode !== 0) {
Logger.ELog('EAC3-Anhaengen fehlgeschlagen (exit=' + (result ? result.exitCode : '?') + ')');
if (result && result.standardError) Logger.ELog(result.standardError);
return 2;
}
Flow.SetWorkingFile(outFile);
Logger.ILog('EAC3-Kompatspur angehaengt -> ' + outFile);
return 1;
}