chore: add 05_Finalize_Move.js from live FileFlows export
This commit is contained in:
parent
b29cc860bb
commit
2eb34a4527
1 changed files with 164 additions and 0 deletions
164
fileflows/scripts/05_Finalize_Move.js
Normal file
164
fileflows/scripts/05_Finalize_Move.js
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
function Script(SourceRoot, DestRoot)
|
||||
{
|
||||
SourceRoot = SourceRoot || '/tdarr/complete/';
|
||||
DestRoot = DestRoot || '/tdarr/converted/';
|
||||
|
||||
let origPath = Variables['file.Orig.FullName'];
|
||||
let workingFile = Variables['file.FullName'];
|
||||
|
||||
if (!origPath || !workingFile) {
|
||||
Logger.ELog('Pfad-Variablen fehlen (orig=' + origPath + ' work=' + workingFile + ')');
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (origPath.indexOf(SourceRoot) !== 0) {
|
||||
Logger.ELog('Original "' + origPath + '" liegt nicht unter ' + SourceRoot + ' - abort');
|
||||
return 2;
|
||||
}
|
||||
|
||||
let relPath = origPath.substring(SourceRoot.length);
|
||||
let destAbs = DestRoot + relPath;
|
||||
let destDir = destAbs.substring(0, destAbs.lastIndexOf('/'));
|
||||
|
||||
Logger.ILog('orig: ' + origPath);
|
||||
Logger.ILog('working: ' + workingFile);
|
||||
Logger.ILog('dest: ' + destAbs);
|
||||
|
||||
Flow.Execute({ command: '/bin/mkdir', argumentList: ['-p', destDir] });
|
||||
|
||||
// Wenn Ziel schon existiert: explizit loggen und ueberschreiben (typisch bei Re-Encode
|
||||
// gleichen Films aus altem Run). Kein -n, sonst geht der frische Encode beim Cleanup verloren.
|
||||
let testExist = Flow.Execute({ command: '/usr/bin/test', argumentList: ['-f', destAbs] });
|
||||
if (testExist && testExist.exitCode === 0) {
|
||||
let oldSize = Flow.Execute({ command: '/usr/bin/stat', argumentList: ['-c', '%s', destAbs] });
|
||||
let oldSizeStr = (oldSize && oldSize.standardOutput) ? oldSize.standardOutput.trim() : '?';
|
||||
Logger.WLog('Ziel existiert bereits (size=' + oldSizeStr + ' bytes) - wird ueberschrieben: ' + destAbs);
|
||||
}
|
||||
|
||||
let mv = Flow.Execute({ command: '/bin/mv', argumentList: ['-f', workingFile, destAbs] });
|
||||
if (!mv || mv.exitCode !== 0) {
|
||||
Logger.ELog('mv failed (exit=' + (mv ? mv.exitCode : '?') + ')');
|
||||
if (mv && mv.standardError) Logger.ELog(mv.standardError);
|
||||
return 2;
|
||||
}
|
||||
|
||||
Flow.SetWorkingFile(destAbs);
|
||||
Logger.ILog('Moved -> ' + destAbs);
|
||||
|
||||
if (origPath !== destAbs) {
|
||||
Flow.Execute({ command: '/bin/rm', argumentList: ['-f', origPath] });
|
||||
Logger.ILog('Deleted original: ' + origPath);
|
||||
}
|
||||
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
// UNTERTITEL-HANDLING (greift fuer ALLE Zweige: AV1, HEVC, reiner Copy)
|
||||
// Ziel: Emby-Auto-Burn-Transcode wegen Untertiteln verhindern.
|
||||
// - Text-Subs (srt/ass): als externe .srt-Sidecar rausziehen UND aus MKV entfernen.
|
||||
// Radarr (importExtraFiles=.srt) zieht sie beim Import mit und benennt synchron um.
|
||||
// - Bild-Subs (PGS/VobSub): im MKV behalten, aber default/forced-Flags loeschen
|
||||
// -> Emby brennt nie automatisch ein (kein Transcode). Manuell waehlbar bleiben sie.
|
||||
// - Forced-Textspur -> eigene .forced.srt.
|
||||
// Remux (Container-only, -c copy) laeuft NUR wenn noetig (Text-Sub vorhanden oder
|
||||
// ein Bild-Sub traegt default/forced). Sonst wird das Video nicht angefasst.
|
||||
// ═════════════════════════════════════════════════════════════════════════
|
||||
try {
|
||||
handleSubtitles(destAbs);
|
||||
} catch (e) {
|
||||
Logger.WLog('Untertitel-Handling uebersprungen (Fehler, Film bleibt unveraendert): ' + e);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
// ── Helfer ───────────────────────────────────────────────────────────────
|
||||
function handleSubtitles(file) {
|
||||
var ffprobe = Flow.GetToolPath('ffprobe') || Variables['ffprobe'] || 'ffprobe';
|
||||
var ffmpeg = Flow.GetToolPath('ffmpeg') || Variables['ffmpeg'] || 'ffmpeg';
|
||||
|
||||
var res = Flow.Execute({
|
||||
command: ffprobe,
|
||||
argumentList: ['-v', 'quiet', '-print_format', 'json', '-show_streams', file]
|
||||
});
|
||||
if (!res || res.exitCode !== 0) { Logger.WLog('Sub: ffprobe fehlgeschlagen - ueberspringe'); return; }
|
||||
var j;
|
||||
try { j = JSON.parse(res.standardOutput || res.output || ''); } catch (e) { Logger.WLog('Sub: JSON-Fehler - ueberspringe'); return; }
|
||||
|
||||
// relative Subtitle-Streams sammeln (0:s:0, 0:s:1, ...)
|
||||
var subs = [], sidx = 0;
|
||||
var textCodecs = ['subrip', 'srt', 'ass', 'ssa', 'mov_text', 'webvtt', 'text', 'subviewer', 'eia_608'];
|
||||
(j.streams || []).forEach(function (s) {
|
||||
if (s.codec_type !== 'subtitle') return;
|
||||
var codec = ('' + (s.codec_name || '')).toLowerCase();
|
||||
var disp = s.disposition || {};
|
||||
var tags = s.tags || {};
|
||||
subs.push({
|
||||
sIdx: sidx, // relativer -map 0:s:N Index
|
||||
codec: codec,
|
||||
isText: textCodecs.indexOf(codec) !== -1,
|
||||
lang: ('' + (tags.language || 'und')).toLowerCase(),
|
||||
forced: (disp.forced === 1) || /forced/i.test('' + (tags.title || '')),
|
||||
isDefault: (disp.default === 1)
|
||||
});
|
||||
sidx++;
|
||||
});
|
||||
|
||||
if (subs.length === 0) { Logger.ILog('Sub: keine Untertitel-Spuren.'); return; }
|
||||
|
||||
var textSubs = subs.filter(function (s) { return s.isText; });
|
||||
var imgSubs = subs.filter(function (s) { return !s.isText; });
|
||||
|
||||
// 1) Text-Subs als .srt-Sidecar extrahieren (jeweils eigener ffmpeg-Call, Main unangetastet)
|
||||
var base = file.replace(/\.[^./]+$/, ''); // Pfad ohne Extension
|
||||
var seen = {}; // lang+forced -> Zaehler (Mehrfachspuren)
|
||||
textSubs.forEach(function (s) {
|
||||
var key = s.lang + (s.forced ? '.forced' : '');
|
||||
var n = seen[key] || 0; seen[key] = n + 1;
|
||||
var suffix = '.' + s.lang + (s.forced ? '.forced' : '') + (n > 0 ? '.' + n : '');
|
||||
var sidecar = base + suffix + '.srt';
|
||||
var ex = Flow.Execute({
|
||||
command: ffmpeg,
|
||||
argumentList: ['-hide_banner', '-y', '-i', file,
|
||||
'-map', '0:s:' + s.sIdx, '-c:s', 'srt', sidecar]
|
||||
});
|
||||
if (ex && ex.exitCode === 0) Logger.ILog('Sub: Sidecar extrahiert -> ' + sidecar);
|
||||
else Logger.WLog('Sub: Sidecar-Extraktion fehlgeschlagen (0:s:' + s.sIdx + ', ' + s.codec + ')');
|
||||
});
|
||||
|
||||
// 2) Remux nur wenn noetig: Text-Subs raus ODER ein Bild-Sub traegt default/forced.
|
||||
var badImgDisp = imgSubs.some(function (s) { return s.isDefault || s.forced; });
|
||||
if (textSubs.length === 0 && !badImgDisp) {
|
||||
Logger.ILog('Sub: nur Bild-Subs ohne default/forced -> kein Remux noetig.');
|
||||
return;
|
||||
}
|
||||
|
||||
var tmp = Flow.TempPath + '/' + Flow.NewGuid() + '.mkv';
|
||||
// Video + Audio + Attachments 1:1; Bild-Subs behalten (mit neutralisierter Disposition),
|
||||
// Text-Subs weglassen.
|
||||
var args = ['-hide_banner', '-y', '-i', file, '-map', '0:v?', '-map', '0:a?', '-map', '0:t?'];
|
||||
var outSubPos = 0;
|
||||
imgSubs.forEach(function (s) { args.push('-map', '0:s:' + s.sIdx); });
|
||||
args.push('-c', 'copy');
|
||||
// alle behaltenen Bild-Subs: default + forced weg (Emby brennt sonst automatisch ein)
|
||||
imgSubs.forEach(function () {
|
||||
args.push('-disposition:s:' + outSubPos, '0');
|
||||
outSubPos++;
|
||||
});
|
||||
args.push('-dn', '-max_muxing_queue_size', '9999', tmp);
|
||||
|
||||
Logger.ILog('Sub: Remux (Text raus: ' + textSubs.length + ', Bild behalten: ' + imgSubs.length +
|
||||
', Flags neutralisiert). FFmpeg: ' + args.join(' '));
|
||||
var rm = Flow.Execute({ command: ffmpeg, argumentList: args });
|
||||
if (!rm || rm.exitCode !== 0) {
|
||||
Logger.WLog('Sub: Remux fehlgeschlagen (exit=' + (rm ? rm.exitCode : '?') + ') - Film bleibt unveraendert.');
|
||||
if (rm && rm.standardError) Logger.WLog(rm.standardError);
|
||||
Flow.Execute({ command: '/bin/rm', argumentList: ['-f', tmp] });
|
||||
return;
|
||||
}
|
||||
var mv2 = Flow.Execute({ command: '/bin/mv', argumentList: ['-f', tmp, file] });
|
||||
if (!mv2 || mv2.exitCode !== 0) {
|
||||
Logger.WLog('Sub: Ersetzen der Datei fehlgeschlagen - Original bleibt.');
|
||||
Flow.Execute({ command: '/bin/rm', argumentList: ['-f', tmp] });
|
||||
return;
|
||||
}
|
||||
Logger.ILog('Sub: MKV neu gemuxt -> ' + file);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue