diff --git a/fileflows/scripts/04b_Add_EAC3_if_DTS.js b/fileflows/scripts/04b_Add_EAC3_if_DTS.js new file mode 100644 index 0000000..d2c65ae --- /dev/null +++ b/fileflows/scripts/04b_Add_EAC3_if_DTS.js @@ -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: -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; +}