/** * @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; }