chore: add 01_Detect_4K_Remux.js from live FileFlows export
This commit is contained in:
parent
12cfca26d1
commit
7aa14490b3
1 changed files with 109 additions and 0 deletions
109
fileflows/scripts/01_Detect_4K_Remux.js
Normal file
109
fileflows/scripts/01_Detect_4K_Remux.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
function Script(MinWidth, MinSourceMbps)
|
||||
{
|
||||
MinWidth = MinWidth || 3000;
|
||||
MinSourceMbps = MinSourceMbps || 15;
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// WICHTIG (verifiziert 2026-07-06): Variables.vi.VideoInfo wird in dieser
|
||||
// FileFlows-Version im ScriptNode NICHT befuellt (0/7 Laeufe hatten vi-Daten,
|
||||
// die "loadVi"-Warteschleife war wirkungslos). Der native VideoInfo-Zugriff
|
||||
// laeuft nur ueber den FFmpeg-Builder-Node, nicht im Script.
|
||||
// -> Verlaesslicher Weg: ffprobe direkt aufrufen und JSON parsen.
|
||||
// Liefert echte Kanalzahlen (6/8) statt Layout-Strings ("5.1").
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
function probeInfo() {
|
||||
var probe = null;
|
||||
try { probe = Flow.GetToolPath('ffprobe'); } catch (e) {}
|
||||
if (!probe) probe = Variables['ffprobe'] || 'ffprobe';
|
||||
var input = Flow.WorkingFile;
|
||||
var res = Flow.Execute({
|
||||
command: probe,
|
||||
argumentList: ['-v', 'quiet', '-print_format', 'json',
|
||||
'-show_format', '-show_streams', input]
|
||||
});
|
||||
if (!res || res.exitCode !== 0) { Logger.WLog('ffprobe fehlgeschlagen (exit=' + (res ? res.exitCode : '?') + ')'); return null; }
|
||||
var raw = res.standardOutput || res.output || '';
|
||||
var j;
|
||||
try { j = JSON.parse(raw); } catch (e) { Logger.WLog('ffprobe JSON parse-Fehler: ' + e); return null; }
|
||||
|
||||
var streams = j.streams || [];
|
||||
var fmtBr = parseInt((j.format && j.format.bit_rate) || 0, 10) || 0;
|
||||
function tagBps(s) { var t = s.tags || {}; return parseInt(t.BPS || t['BPS-eng'] || t.bps || 0, 10) || 0; }
|
||||
|
||||
var vids = [], auds = [];
|
||||
for (var i = 0; i < streams.length; i++) {
|
||||
var s = streams[i], disp = s.disposition || {};
|
||||
if (s.codec_type === 'video') {
|
||||
// cover/poster (attached_pic, mjpeg, png) ignorieren
|
||||
if (disp.attached_pic === 1 || s.codec_name === 'mjpeg' || s.codec_name === 'png') continue;
|
||||
var dovi = (s.side_data_list || []).some(function (x) {
|
||||
var t = ('' + (x.side_data_type || '')).toLowerCase();
|
||||
return t.indexOf('dovi') !== -1 || t.indexOf('dolby vision') !== -1;
|
||||
});
|
||||
var trc = ('' + (s.color_transfer || ''));
|
||||
vids.push({
|
||||
Width: s.width || 0, Height: s.height || 0,
|
||||
Codec: (s.codec_name || ''),
|
||||
Bitrate: tagBps(s) || 0,
|
||||
ColorTransfer: trc, ColorPrimaries: s.color_primaries || '', ColorSpace: s.color_space || '',
|
||||
PixelFormat: s.pix_fmt || '',
|
||||
DolbyVision: dovi,
|
||||
// P7 hat HDR10-Base-Layer (smpte2084), P5 ist ICtCp (kein smpte2084)
|
||||
DolbyVisionProfile: dovi ? (trc.indexOf('smpte2084') !== -1 ? 7 : 5) : null
|
||||
});
|
||||
} else if (s.codec_type === 'audio') {
|
||||
var t = s.tags || {};
|
||||
auds.push({
|
||||
Language: t.language || '', Codec: (s.codec_name || ''),
|
||||
Channels: s.channels || 0, ChannelLayout: s.channel_layout || '',
|
||||
Title: t.title || '', Profile: s.profile || '',
|
||||
Default: (disp.default === 1), Bitrate: tagBps(s) || 0
|
||||
});
|
||||
}
|
||||
}
|
||||
return { Bitrate: fmtBr, VideoStreams: vids, AudioStreams: auds };
|
||||
}
|
||||
|
||||
let vi = probeInfo();
|
||||
let v = (vi && vi.VideoStreams && vi.VideoStreams.length > 0) ? vi.VideoStreams[0] : null;
|
||||
|
||||
if (!v) {
|
||||
Logger.ELog('Kein Video-Stream ermittelbar (ffprobe) - kann 4K-Remux nicht bewerten');
|
||||
return 2;
|
||||
}
|
||||
|
||||
let width = v.Width || 0;
|
||||
let codec = (v.Codec || '').toLowerCase();
|
||||
let bitrateBps = v.Bitrate || 0;
|
||||
// Video-Bitrate fehlt (kein BPS-Tag) -> aus Container-Gesamtbitrate schaetzen (~92%)
|
||||
if (bitrateBps <= 0 && vi.Bitrate) bitrateBps = Math.round(vi.Bitrate * 0.92);
|
||||
let bitrateMbps = bitrateBps > 0 ? (bitrateBps / 1000000) : 0;
|
||||
|
||||
// Dateiname aus den flachen Punkt-Keys (Variables.file als Objekt existiert NICHT in dieser Version)
|
||||
let fileName = Variables['file.Orig.FullName'] || Variables['file.FullName']
|
||||
|| Variables['file.Name'] || '';
|
||||
if (!fileName) { try { fileName = Flow.WorkingFile || ''; } catch (e) {} }
|
||||
|
||||
let is4K = width >= MinWidth;
|
||||
let isRemux = /remux/i.test(fileName);
|
||||
let isHEVC = (codec === 'hevc' || codec === 'h265');
|
||||
let isAV1 = (codec === 'av1');
|
||||
// Bei unbekannter Bitrate NICHT ablehnen: ein 4K-HEVC-Remux ist per Definition hochbitratig.
|
||||
let brKnown = bitrateMbps > 0;
|
||||
let highBR = brKnown ? (bitrateMbps >= MinSourceMbps) : true;
|
||||
|
||||
Logger.ILog('Datei: ' + fileName);
|
||||
Logger.ILog('Video: codec=' + codec + ' width=' + width + ' bitrate=' +
|
||||
(brKnown ? bitrateMbps.toFixed(1) + ' Mbit/s' : 'unbekannt'));
|
||||
Logger.ILog('Checks: 4K=' + is4K + ' Remux=' + isRemux + ' HEVC=' + isHEVC +
|
||||
' >=' + MinSourceMbps + 'Mbit=' + highBR + (brKnown ? '' : ' (Bitrate unbekannt->Remux gilt als HB)'));
|
||||
|
||||
if (isAV1) { Logger.ILog('Bereits AV1 - kein Re-Encode.'); return 2; }
|
||||
if (!is4K) { Logger.ILog('Nicht 4K (<' + MinWidth + 'px).'); return 2; }
|
||||
if (!isRemux) { Logger.ILog('Kein "Remux" im Dateinamen.'); return 2; }
|
||||
if (!isHEVC) { Logger.ILog('Codec ist nicht HEVC/H265.'); return 2; }
|
||||
if (!highBR) { Logger.ILog('Bitrate < ' + MinSourceMbps + ' Mbit/s - lohnt nicht.'); return 2; }
|
||||
|
||||
Logger.ILog('-> 4K Remux HEVC Kandidat fuer AV1 Encode');
|
||||
return 1;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue