|
|
|
|
@ -1,296 +1,238 @@
|
|
|
|
|
/* eslint-disable */
|
|
|
|
|
const details = () => {
|
|
|
|
|
return {
|
|
|
|
|
id: "Tdarr_Plugin_A47j_FFMPEG_NVENC_HEVC_Video_Only",
|
|
|
|
|
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
|
|
|
|
|
/* eslint-disable no-restricted-globals */
|
|
|
|
|
|
|
|
|
|
const details = () => ({
|
|
|
|
|
id: 'Tdarr_Plugin_A47j_FFMPEG_NVENC_HEVC_Video_Only',
|
|
|
|
|
Stage: 'Pre-processing',
|
|
|
|
|
Name: "FFMPEG nvenc_H265 Video Only",
|
|
|
|
|
Type: "Video",
|
|
|
|
|
Stage: "Pre-processing",
|
|
|
|
|
Operation: "Transcode",
|
|
|
|
|
Description: `[Contains built-in filter] This plugin transcodes non-h265 files into h265 mkv using NVENC,
|
|
|
|
|
reducing resolution to 1920x1080 using nvenc. Audio/subtitles not affected. Bitrate is scaled based on input file quality.
|
|
|
|
|
== This plugin depends on mediainfo and mkvpropedit, which must be installed manually!
|
|
|
|
|
Check this gist for details: https://gist.github.com/jeff47/4ec428e329a485a102bab0398e6ac4be == `,
|
|
|
|
|
Version: "1.00",
|
|
|
|
|
Tags: "pre-processing,video only,ffmpeg,nvenc h265,h265",
|
|
|
|
|
Name: 'FFMPEG nvenc_H265 Video Only',
|
|
|
|
|
Type: 'Video',
|
|
|
|
|
Operation: 'Transcode',
|
|
|
|
|
Description: `
|
|
|
|
|
[Contains built-in filter] This plugin transcodes non-h265 files into h265 mkv using NVENC,
|
|
|
|
|
reducing resolution to 1920x1080 using nvenc. Audio/subtitles not affected. Bitrate is scaled based on
|
|
|
|
|
input file quality.`,
|
|
|
|
|
Version: '1.1',
|
|
|
|
|
Tags: 'pre-processing,video only,ffmpeg,nvenc h265,h265',
|
|
|
|
|
|
|
|
|
|
Inputs: [
|
|
|
|
|
{
|
|
|
|
|
name: "compressionFactor",
|
|
|
|
|
name: 'compressionFactor',
|
|
|
|
|
type: 'string',
|
|
|
|
|
defaultValue: '0.07',
|
|
|
|
|
inputUI: {
|
|
|
|
|
type: 'text',
|
|
|
|
|
},
|
|
|
|
|
tooltip: `== Compression Factor == \\n\\n
|
|
|
|
|
How much does HEVC compress raw video? I suggest something between 0.04-0.08. Remember that GPU encoding is not as
|
|
|
|
|
efficient as CPU encoding, so resulting file sizes will be larger.\\n\\n
|
|
|
|
|
0.07 will result in a 1080p@29.92fps having a target bitrate of 5.4mbps. This is the default.\\n`
|
|
|
|
|
tooltip: `
|
|
|
|
|
== Compression Factor == \\n\\n
|
|
|
|
|
How much does HEVC compress raw video? I suggest something between 0.04-0.08. Remember that GPU encoding is
|
|
|
|
|
faster but not as space efficient as CPU encoding, so resulting file sizes will be larger than if you used CPU
|
|
|
|
|
encoding.\\n\\n
|
|
|
|
|
0.07 will result in a 1080p@29.92fps having a target bitrate of 5.4mbps. This is the default.\\n`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "maxResolution",
|
|
|
|
|
name: 'maxResolution',
|
|
|
|
|
type: 'string',
|
|
|
|
|
defaultValue: 'false',
|
|
|
|
|
inputUI: {
|
|
|
|
|
type: 'text',
|
|
|
|
|
type: 'dropdown',
|
|
|
|
|
options: [
|
|
|
|
|
'false',
|
|
|
|
|
'8KUHD',
|
|
|
|
|
'4KUHD',
|
|
|
|
|
'1080p',
|
|
|
|
|
'720p',
|
|
|
|
|
'576p',
|
|
|
|
|
'480p',
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
tooltip: `== Maximum Resolution ==\\n\\n
|
|
|
|
|
Videos that exceed this resolution will be resized down to this resolution.\\n
|
|
|
|
|
Accepted options: 480p, 576p, 720p, 1080p, 4KUHD, 8KUHD. If false, no resizing will occur.\\n`
|
|
|
|
|
If false, no resizing will occur.\\n`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'ffmpegPreset',
|
|
|
|
|
type: 'string',
|
|
|
|
|
defaultValue: 'medium',
|
|
|
|
|
inputUI: {
|
|
|
|
|
type: 'dropdown',
|
|
|
|
|
options: [
|
|
|
|
|
'veryslow',
|
|
|
|
|
'slower',
|
|
|
|
|
'slow',
|
|
|
|
|
'medium',
|
|
|
|
|
'fast',
|
|
|
|
|
'faster',
|
|
|
|
|
'veryfast',
|
|
|
|
|
'superfast',
|
|
|
|
|
'ultrafast',
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
tooltip: `== FFmpeg Preset ==\\n\\n
|
|
|
|
|
Select the ffmpeg preset.\\n`,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'container',
|
|
|
|
|
type: 'string',
|
|
|
|
|
defaultValue: 'mp4',
|
|
|
|
|
inputUI: {
|
|
|
|
|
type: 'dropdown',
|
|
|
|
|
options: [
|
|
|
|
|
'mp4',
|
|
|
|
|
'mkv',
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
tooltip: `== Container ==\\n\\n
|
|
|
|
|
mkv or mp4.\\n`,
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var MediaInfo = {
|
|
|
|
|
videoHeight: "",
|
|
|
|
|
videoWidth: "",
|
|
|
|
|
videoFPS:"",
|
|
|
|
|
videoBR: "",
|
|
|
|
|
videoBitDepth: "",
|
|
|
|
|
overallBR: "",
|
|
|
|
|
JSRProcessed: false,
|
|
|
|
|
JSRVersion: 0,
|
|
|
|
|
JSRProcessedTime: 0,
|
|
|
|
|
const MediaInfo = {
|
|
|
|
|
videoHeight: '',
|
|
|
|
|
videoWidth: '',
|
|
|
|
|
videoFPS: '',
|
|
|
|
|
videoBR: '',
|
|
|
|
|
videoBitDepth: '',
|
|
|
|
|
overallBR: '',
|
|
|
|
|
}; // var MediaInfo
|
|
|
|
|
|
|
|
|
|
// Easier for our functions if response has global scope.
|
|
|
|
|
var response = {
|
|
|
|
|
const response = {
|
|
|
|
|
processFile: false,
|
|
|
|
|
preset: "",
|
|
|
|
|
container: ".mkv",
|
|
|
|
|
preset: '',
|
|
|
|
|
container: '.mp4',
|
|
|
|
|
handBrakeMode: false,
|
|
|
|
|
FFmpegMode: true,
|
|
|
|
|
reQueueAfter: true,
|
|
|
|
|
infoLog: "",
|
|
|
|
|
infoLog: '',
|
|
|
|
|
}; // var response
|
|
|
|
|
|
|
|
|
|
// Runs mkvpropedit --add-track-statistics on the file.
|
|
|
|
|
function updateTrackStats(file) {
|
|
|
|
|
response.infoLog += `☑Running mkvpropedit.\n`;
|
|
|
|
|
try {
|
|
|
|
|
const proc = require("child_process");
|
|
|
|
|
proc.execFile('mkvpropedit', [ '--delete-track-statistics-tags', file._id], (error,stdout,stderr) => {
|
|
|
|
|
if (error) throw `mkvpropedit failed: ${error}\n`;
|
|
|
|
|
});
|
|
|
|
|
proc.execFile('mkvpropedit', [ '--add-track-statistics-tags', file._id], (error,stdout,stderr) => {
|
|
|
|
|
if (error) throw `mkvpropedit failed: ${error}\n`;
|
|
|
|
|
});
|
|
|
|
|
} catch (err) {
|
|
|
|
|
response.infoLog += `mkvpropedit failed: ${err}.\n`;
|
|
|
|
|
throw `mkvpropedit failed: ${err}.\n`;
|
|
|
|
|
}; // end try/catch
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
} // end updateTrackStats()
|
|
|
|
|
|
|
|
|
|
// Runs mediainfo on the file, gets JSON output, finds the first video stream and returns the video bit rate and bit depth.
|
|
|
|
|
// Finds the first video stream and populates some useful variables
|
|
|
|
|
function getMediaInfo(file) {
|
|
|
|
|
var objMedInfo = "";
|
|
|
|
|
|
|
|
|
|
response.infoLog += `☑Running mediainfo.\n`;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const proc = require('child_process')
|
|
|
|
|
objMedInfo = JSON.parse(proc.execFileSync('mediainfo', [file._id,'--output=JSON']));
|
|
|
|
|
} catch (err) {
|
|
|
|
|
response.infoLog += `Mediainfo failed: ${err}.\n`;
|
|
|
|
|
throw `Mediainfo failed: ${err}.\n`;
|
|
|
|
|
}; // end try/catch
|
|
|
|
|
|
|
|
|
|
var videoIdx = -1;
|
|
|
|
|
var videoInxFirst = -1;
|
|
|
|
|
let videoIdx = -1;
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < file.ffProbeData.streams.length; i++) {
|
|
|
|
|
|
|
|
|
|
strstreamType = file.ffProbeData.streams[i].codec_type.toLowerCase();
|
|
|
|
|
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
|
|
|
|
|
const strstreamType = file.ffProbeData.streams[i].codec_type.toLowerCase();
|
|
|
|
|
|
|
|
|
|
// Looking For Video
|
|
|
|
|
// Check if stream is a video.
|
|
|
|
|
if (videoIdx == -1 && strstreamType == "video") {
|
|
|
|
|
if (videoIdx === -1 && strstreamType === 'video') {
|
|
|
|
|
videoIdx = i;
|
|
|
|
|
videoInxFirst = i;
|
|
|
|
|
|
|
|
|
|
MediaInfo.videoHeight = Number(file.ffProbeData.streams[i].height);
|
|
|
|
|
MediaInfo.videoWidth = Number(file.ffProbeData.streams[i].width);
|
|
|
|
|
MediaInfo.videoFPS = Number(objMedInfo.media.track[i + 1].FrameRate);
|
|
|
|
|
MediaInfo.videoBR = Number(objMedInfo.media.track[i + 1].BitRate);
|
|
|
|
|
MediaInfo.videoBitDepth = Number(objMedInfo.media.track[i + 1].BitDepth);
|
|
|
|
|
}
|
|
|
|
|
MediaInfo.videoFPS = Number(file.mediaInfo.track[i + 1].FrameRate);
|
|
|
|
|
MediaInfo.videoBR = Number(file.mediaInfo.track[i + 1].BitRate);
|
|
|
|
|
MediaInfo.videoBitDepth = Number(file.mediaInfo.track[i + 1].BitDepth);
|
|
|
|
|
}
|
|
|
|
|
MediaInfo.overallBR = objMedInfo.media.track[0].OverallBitRate;
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
MediaInfo.JSRVersion = Number(objMedInfo.media.track[0].extra.JSRVERSION);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
MediaInfo.JSRVersion = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
MediaInfo.JSRProcessed = Boolean(objMedInfo.media.track[0].extra.JSRPROCESSED);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
MediaInfo.JSRProcessed = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
MediaInfo.JSRProcessedTime = Number(objMedInfo.media.track[0].extra.JSRPROCESSEDTIME);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
MediaInfo.JSRProcessedTime = "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
MediaInfo.overallBR = file.mediaInfo.track[0].OverallBitRate;
|
|
|
|
|
} // end getMediaInfo()
|
|
|
|
|
|
|
|
|
|
// eslint-disable-next-line no-unused-vars
|
|
|
|
|
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|
|
|
|
|
|
|
|
|
const lib = require('../methods/lib')();
|
|
|
|
|
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
|
|
|
|
inputs = lib.loadDefaultValues(inputs, details);
|
|
|
|
|
|
|
|
|
|
if (file.fileMedium !== "video") {
|
|
|
|
|
if (file.fileMedium !== 'video') {
|
|
|
|
|
response.processFile = false;
|
|
|
|
|
response.infoLog += "☒File is not a video.\n";
|
|
|
|
|
response.infoLog += '☒File is not a video.\n';
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// How much does HVEC compress the raw stream?
|
|
|
|
|
var compressionFactor = 0.07;
|
|
|
|
|
let compressionFactor = 0.07;
|
|
|
|
|
if (!isNaN(Number(inputs.compressionFactor))) {
|
|
|
|
|
compressionFactor = inputs.compressionFactor;
|
|
|
|
|
} else {
|
|
|
|
|
response.infoLog += `No compression factor selected, defaulting to ${compressionFactor}.\n`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response.container = `.${inputs.container}`;
|
|
|
|
|
|
|
|
|
|
// Do we resize?
|
|
|
|
|
var resolutionOrder = [ "480p", "576p", "720p", "1080p", "4KUHD", "8KUHD" ];
|
|
|
|
|
|
|
|
|
|
const resolutionOrder = ['480p', '576p', '720p', '1080p', '4KUHD', '8KUHD'];
|
|
|
|
|
|
|
|
|
|
// Define the dimensions and the number of pixels (weightxheight) for each resolution.
|
|
|
|
|
var resolutions = {
|
|
|
|
|
"480p": { "dimensions": "640x480", "pixelCount": 307200 },
|
|
|
|
|
"576p": { "dimensions": "720x576", "pixelCount": 414720 },
|
|
|
|
|
"720p": { "dimensions": "1280x720", "pixelCount": 921600 },
|
|
|
|
|
"1080p": { "dimensions": "1920x1080", "pixelCount": 2073600 },
|
|
|
|
|
"4KUHD": { "dimensions": "3840x2160", "pixelCount": 8294400 },
|
|
|
|
|
"8KUHD": { "dimensions": "7680x4320", "pixelCount": 33177600 }
|
|
|
|
|
const resolutions = {
|
|
|
|
|
'480p': { dimensions: '640x480', pixelCount: 307200 },
|
|
|
|
|
'576p': { dimensions: '720x576', pixelCount: 414720 },
|
|
|
|
|
'720p': { dimensions: '1280x720', pixelCount: 921600 },
|
|
|
|
|
'1080p': { dimensions: '1920x1080', pixelCount: 2073600 },
|
|
|
|
|
'4KUHD': { dimensions: '3840x2160', pixelCount: 8294400 },
|
|
|
|
|
'8KUHD': { dimensions: '7680x4320', pixelCount: 33177600 },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var maxResolution = "8KUHD";
|
|
|
|
|
let maxResolution = '8KUHD';
|
|
|
|
|
if (resolutionOrder.indexOf(inputs.maxResolution) > 0) {
|
|
|
|
|
maxResolution = inputs.maxResolution;
|
|
|
|
|
} else {
|
|
|
|
|
response.infoLog += `No valid resolution selected, defaulting to ${maxResolution}.\n`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// -------------------------------- METADATA UPDATES --------------------------------
|
|
|
|
|
// If there is no _STATISTICS_WRITING_DATE_UTC-eng field, then we need to run mkvpropedit and
|
|
|
|
|
// rerun mediainfo to load the stats.
|
|
|
|
|
if (file.ffProbeData.streams[0].tags === undefined || file.ffProbeData.streams[0].tags["_STATISTICS_WRITING_DATE_UTC-eng"] === undefined ) {
|
|
|
|
|
response.infoLog += "☑Track statistics are missing.\n";
|
|
|
|
|
updateTrackStats(file);
|
|
|
|
|
getMediaInfo(file);
|
|
|
|
|
} else {
|
|
|
|
|
// mkvpropedit records the time the stats were written. Get it (specify it is in UTC) and add a 10 second buffer.
|
|
|
|
|
StatsWritingTime = Date.parse(`${file.ffProbeData.streams[0].tags["_STATISTICS_WRITING_DATE_UTC-eng"]} UTC`) + 10000;
|
|
|
|
|
|
|
|
|
|
// If the file's mtime is more than 60 seconds later than StatsWritingTime, then we should rerun mkvpropedit!
|
|
|
|
|
if ( file.statSync.mtimeMs > StatsWritingTime ) {
|
|
|
|
|
response.infoLog += "☑Track statistics are out of date.\n";
|
|
|
|
|
updateTrackStats(file);
|
|
|
|
|
getMediaInfo(file);
|
|
|
|
|
} else {
|
|
|
|
|
response.infoLog += "☑Track statistics are up to date.\n";
|
|
|
|
|
getMediaInfo(file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( isNaN(MediaInfo.videoBR) || isNaN(MediaInfo.videoBitDepth) ) {
|
|
|
|
|
response.infoLog += "videoBR or videoBitDepth was NaN, something went wrong with mediainfo.\n";
|
|
|
|
|
updateTrackStats(file);
|
|
|
|
|
getMediaInfo(file);
|
|
|
|
|
if ( isNaN(MediaInfo.videoBR) || isNaN(MediaInfo.videoBitDepth) ) {
|
|
|
|
|
response.infoLog += "videoBR or videoBitDepth still NaN, using default.\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the overall bitrate is less than the videoBR, then something is wacky.
|
|
|
|
|
if ( MediaInfo.videoBR > MediaInfo.overallBR ) {
|
|
|
|
|
response.infoLog += `videoBR (${MediaInfo.videoBR} was greater than overallBR (${MediaInfo.overallBR}),
|
|
|
|
|
which is impossible. Updating stats.\n`;
|
|
|
|
|
updateTrackStats(file);
|
|
|
|
|
getMediaInfo(file);
|
|
|
|
|
if ( MediaInfo.videoBR > MediaInfo.overallBR ) {
|
|
|
|
|
response.infoLog += `videoBR and overallBR still inconsistent, using default.\n`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( file.forceProcessing !== true ) {
|
|
|
|
|
if ( MediaInfo.JSRProcessed !== undefined && MediaInfo.JSRProcessed == true) {
|
|
|
|
|
response.infoLog += `JSRPROCESSED metadata tag was true. This file was already transcoded by this plugin. Exiting...\n`;
|
|
|
|
|
response.processFile = false;
|
|
|
|
|
return response;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set decoding options here
|
|
|
|
|
switch (file.ffProbeData.streams[0].codec_name) {
|
|
|
|
|
case "hevc":
|
|
|
|
|
response.preset = `-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v hevc_cuvid `;
|
|
|
|
|
case 'hevc':
|
|
|
|
|
response.preset = '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v hevc_cuvid ';
|
|
|
|
|
break;
|
|
|
|
|
case 'h264':
|
|
|
|
|
response.preset = '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v h264_cuvid ';
|
|
|
|
|
break;
|
|
|
|
|
case "h264":
|
|
|
|
|
response.preset = `-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v h264_cuvid `;
|
|
|
|
|
case 'vc1':
|
|
|
|
|
response.preset = '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v vc1_cuvid ';
|
|
|
|
|
break;
|
|
|
|
|
case "vc1":
|
|
|
|
|
response.preset = `-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v vc1_cuvid `;
|
|
|
|
|
case 'vp8':
|
|
|
|
|
response.preset = '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v vp8_cuvid ';
|
|
|
|
|
break;
|
|
|
|
|
case "vp8":
|
|
|
|
|
response.preset = `-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v vp8_cuvid `;
|
|
|
|
|
case 'vp9':
|
|
|
|
|
response.preset = '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v vp9_cuvid ';
|
|
|
|
|
break;
|
|
|
|
|
case "vp9":
|
|
|
|
|
response.preset = `-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v vp9_cuvid `;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
} // end switch(codec)
|
|
|
|
|
|
|
|
|
|
let targetBitrate;
|
|
|
|
|
|
|
|
|
|
// Resize high resolution videos to 1080p.
|
|
|
|
|
if (resolutionOrder.indexOf(file.video_resolution) > resolutionOrder.indexOf(maxResolution)) {
|
|
|
|
|
// File resolution exceeds limit, need to resize.
|
|
|
|
|
response.preset += ` -resize ${resolutions[maxResolution].dimensions} `;
|
|
|
|
|
response.infoLog += `Resizing to ${resolutions[maxResolution].dimensions}.\n`;
|
|
|
|
|
response.processFile = true;
|
|
|
|
|
var targetBitrate = Math.round((resolutions[maxResolution].pixelCount*MediaInfo.videoFPS)*compressionFactor);
|
|
|
|
|
targetBitrate = Math.round((resolutions[maxResolution].pixelCount * MediaInfo.videoFPS) * compressionFactor);
|
|
|
|
|
} else {
|
|
|
|
|
// No resize needed.
|
|
|
|
|
var targetBitrate = Math.round((MediaInfo.videoWidth*MediaInfo.videoHeight*MediaInfo.videoFPS)*compressionFactor);
|
|
|
|
|
targetBitrate = Math.round((MediaInfo.videoWidth * MediaInfo.videoHeight * MediaInfo.videoFPS) * compressionFactor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Calculate bitrates
|
|
|
|
|
response.infoLog += `Video details: ${file.ffProbeData.streams[0].codec_name}-${file.video_resolution}
|
|
|
|
|
${MediaInfo.videoWidth}x${MediaInfo.videoHeight}x${MediaInfo.videoFPS}@8 bits.\n`;
|
|
|
|
|
|
|
|
|
|
var maxBitrate = Math.round(targetBitrate*1.3);
|
|
|
|
|
var minBitrate = Math.round(targetBitrate*0.7);
|
|
|
|
|
const maxBitrate = Math.round(targetBitrate * 1.3);
|
|
|
|
|
const minBitrate = Math.round(targetBitrate * 0.7);
|
|
|
|
|
|
|
|
|
|
let bufsize;
|
|
|
|
|
if (isNaN(MediaInfo.videoBR)) {
|
|
|
|
|
var bufsize = targetBitrate;
|
|
|
|
|
bufsize = targetBitrate;
|
|
|
|
|
} else {
|
|
|
|
|
var bufsize = Math.round(MediaInfo.videoBR);
|
|
|
|
|
bufsize = Math.round(MediaInfo.videoBR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
response.preset += `,-map 0:v -map 0:a -map 0:s? -map -:d? -c copy -c:v:0 hevc_nvenc -rc:v vbr_hq -preset medium -profile:v main10 -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -max_muxing_queue_size 4096 `;
|
|
|
|
|
response.infoLog += `Video bitrate is ${Math.round(MediaInfo.videoBR/1000)}Kbps, overall is ${Math.round(MediaInfo.overallBR/1000)}Kbps. `;
|
|
|
|
|
response.preset += ',-map 0:v -map 0:a -map 0:s? -map -:d? -c copy -c:v:0 hevc_nvenc'
|
|
|
|
|
+ ` -rc:v vbr_hq -preset ${inputs.ffmpegPreset} -profile:v main10 -rc-lookahead 32 `
|
|
|
|
|
+ '-spatial_aq:v 1 -aq-strength:v 8 -max_muxing_queue_size 4096 ';
|
|
|
|
|
response.infoLog += `Video bitrate is ${Math.round(MediaInfo.videoBR / 1000)}Kbps,`
|
|
|
|
|
+ ` overall is ${Math.round(MediaInfo.overallBR / 1000)}Kbps. `;
|
|
|
|
|
response.infoLog += `Calculated target is ${Math.round(targetBitrate / 1000)}Kbps.\n`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Adjust target bitrates by codec and bitrate
|
|
|
|
|
switch (file.ffProbeData.streams[0].codec_name) {
|
|
|
|
|
case "hevc":
|
|
|
|
|
case 'hevc':
|
|
|
|
|
if (isNaN(MediaInfo.videoBR)) {
|
|
|
|
|
response.processFile = true;
|
|
|
|
|
targetBitrate = Math.min(MediaInfo.overallBR, targetBitrate);
|
|
|
|
|
@ -300,14 +242,16 @@ response.infoLog += `Calculated target is ${Math.round(targetBitrate/1000)}Kbps.
|
|
|
|
|
} else if ((MediaInfo.videoBR > targetBitrate * 1.5) || file.forceProcessing === true) {
|
|
|
|
|
response.processFile = true;
|
|
|
|
|
response.preset += ` -b:v ${targetBitrate} -maxrate ${maxBitrate} -minrate ${minBitrate} -bufsize ${bufsize} `;
|
|
|
|
|
response.infoLog += `☒HEVC Bitrate for ${file.video_resolution} exceeds ${Math.round(targetBitrate*1.5/1000)}Kbps,
|
|
|
|
|
downsampling to ${Math.round(targetBitrate/1000)}Kbps.\n`;
|
|
|
|
|
response.infoLog += `☒HEVC Bitrate for ${file.video_resolution}`
|
|
|
|
|
+ ` exceeds ${Math.round((targetBitrate * 1.5) / 1000)}Kbps,`
|
|
|
|
|
+ ` downsampling to ${Math.round(targetBitrate / 1000)}Kbps.\n`;
|
|
|
|
|
} else {
|
|
|
|
|
response.infoLog += `☑HEVC Bitrate is within limits.\n`
|
|
|
|
|
response.infoLog += '☑HEVC Bitrate is within limits.\n';
|
|
|
|
|
}
|
|
|
|
|
break; // case "hevc"
|
|
|
|
|
case "h264":
|
|
|
|
|
case 'h264':
|
|
|
|
|
response.processFile = true;
|
|
|
|
|
let new_bitrate;
|
|
|
|
|
// We want the new bitrate to be 70% the h264 bitrate, but not higher than our target.
|
|
|
|
|
if (isNaN(MediaInfo.videoBR)) {
|
|
|
|
|
new_bitrate = Math.min(MediaInfo.overallBR * 0.7, targetBitrate);
|
|
|
|
|
@ -316,35 +260,29 @@ response.infoLog += `Calculated target is ${Math.round(targetBitrate/1000)}Kbps.
|
|
|
|
|
// New bitrate should not be lower than our 60% of our target.
|
|
|
|
|
new_bitrate = Math.max(new_bitrate, Math.min(MediaInfo.videoBR, targetBitrate * 0.6));
|
|
|
|
|
}
|
|
|
|
|
response.preset +=` -b:v ${new_bitrate} -maxrate ${Math.round(new_bitrate*1.3)} -minrate ${Math.round(new_bitrate*0.7)} -bufsize ${bufsize}`;
|
|
|
|
|
response.infoLog += `☒H264 Resolution is ${file.video_resolution}, bitrate was ${Math.round(MediaInfo.videoBR/1000)}Kbps.
|
|
|
|
|
HEVC target bitrate will be ${Math.round(new_bitrate/1000)}Kbps.\n`;
|
|
|
|
|
response.preset += ` -b:v ${new_bitrate} -maxrate ${Math.round(new_bitrate * 1.3)}`
|
|
|
|
|
+ `-minrate ${Math.round(new_bitrate * 0.7)} -bufsize ${bufsize}`;
|
|
|
|
|
response.infoLog += `☒H264 Resolution is ${file.video_resolution},`
|
|
|
|
|
+ ` bitrate was ${Math.round(MediaInfo.videoBR / 1000)}Kbps.`
|
|
|
|
|
+ ` HEVC target bitrate will be ${Math.round(new_bitrate / 1000)}Kbps.\n`;
|
|
|
|
|
break; // case "h264"
|
|
|
|
|
default:
|
|
|
|
|
response.processFile = true;
|
|
|
|
|
response.preset += ` -b:v ${targetBitrate} -maxrate ${maxBitrate} -minrate ${minBitrate} -bufsize ${bufsize} `;
|
|
|
|
|
response.infoLog += `☒${file.ffProbeData.streams[0].codec_name} resolution is ${file.video_resolution},
|
|
|
|
|
bitrate was ${Math.round(MediaInfo.videoBR/1000)}Kbps. HEVC target bitrate will be ${Math.round(targetBitrate/1000)}Kbps.\n`;
|
|
|
|
|
response.infoLog += `☒${file.ffProbeData.streams[0].codec_name} resolution is ${file.video_resolution},`
|
|
|
|
|
+ ` bitrate was ${Math.round(MediaInfo.videoBR / 1000)}Kbps.`
|
|
|
|
|
+ ` HEVC target bitrate will be ${Math.round(targetBitrate / 1000)}Kbps.\n`;
|
|
|
|
|
break; // default
|
|
|
|
|
} // switch (file.ffProbeData.streams[0].codec_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (response.processFile == true) {
|
|
|
|
|
response.preset += ` -map_metadata:g -1 -metadata JSRVERSION=1 -metadata JSRPROCESSED=true -metadata JSRPROCESSEDTIME=${Date.now()} `;
|
|
|
|
|
if (response.processFile === true) {
|
|
|
|
|
response.preset += ' -map_metadata:g -1';
|
|
|
|
|
response.FFmpegMode = true;
|
|
|
|
|
response.infoLog += `☒Transcoding to HEVC.`;
|
|
|
|
|
} else {
|
|
|
|
|
if (file.container != "mkv") {
|
|
|
|
|
response_preset = ',-c copy -map 0';
|
|
|
|
|
response.processFile = true;
|
|
|
|
|
response.infoLog += `☒Remuxing to mkv.`;
|
|
|
|
|
response.infoLog += '☒Transcoding to HEVC.';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return response;
|
|
|
|
|
} // end plugin()
|
|
|
|
|
}; // end plugin()
|
|
|
|
|
|
|
|
|
|
module.exports.details = details;
|
|
|
|
|
module.exports.plugin = plugin;
|
|
|
|
|
|