mirror of
https://github.com/gabehf/Tdarr_Plugins.git
synced 2026-03-09 07:29:04 -07:00
Merge branch 'master' into pr/612
This commit is contained in:
commit
65ef38696d
12 changed files with 1743 additions and 972 deletions
2
.github/workflows/lint_and_test.yml
vendored
2
.github/workflows/lint_and_test.yml
vendored
|
|
@ -57,7 +57,7 @@ jobs:
|
|||
with:
|
||||
node-version: '18.x'
|
||||
|
||||
- run: npm i && npm i -g typescript && rm -rdf ./FlowPlugins && tsc
|
||||
- run: npm i && npm i -g typescript && rm -rdf ./FlowPlugins && tsc -v && tsc
|
||||
|
||||
- uses: stefanzweifel/git-auto-commit-action@v5
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -26,14 +26,10 @@ const details = () => ({
|
|||
Settings are dependant on file bitrate working by the logic that H265 can support the same amount of data at half
|
||||
the bitrate of H264. This plugin will skip files already in HEVC, AV1 & VP9 unless "reconvert_hevc" is marked as
|
||||
true. If it is then these will be reconverted again if they exceed the bitrate specified in "hevc_max_bitrate".
|
||||
This plugin will also attempt to use mkvpropedit to generate accurate bitrate metadata in MKV files.
|
||||
It's not required to enable mkvpropedit but highly recommended to ensure accurate bitrates are used when
|
||||
encoding your media.
|
||||
\n\n==NOTE== Intel ARC cards are reportedly working successfully with this plugin, however please bare in mind that
|
||||
I've not officially tested with them yet and your results might vary. Don't just assume it will work and if it does
|
||||
ensure you properly test your files & workflow!`,
|
||||
Version: '1.2',
|
||||
Tags: 'pre-processing,ffmpeg,video only,qsv,h265,hevc,mkvpropedit,configurable',
|
||||
This plugin relies on understanding the accurate video bitrate of your files. It's highly recommended to remux
|
||||
into MKV & enable "Run mkvpropedit on files before running plugins" under Tdarr>Options.`,
|
||||
Version: '1.3',
|
||||
Tags: 'pre-processing,ffmpeg,video only,qsv,h265,hevc,configurable',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'container',
|
||||
|
|
@ -101,7 +97,7 @@ const details = () => ({
|
|||
==DESCRIPTION==
|
||||
\\nSpecify if we want to enable 10bit encoding.
|
||||
\\nIf this is enabled files will be processed and converted into 10bit
|
||||
HEVC using main10 profile and with p010le pixel format. \n
|
||||
HEVC using main10 profile and with p010le pixel format.\n
|
||||
If you just want to retain files that are already 10 bit then this can be left as false, as
|
||||
10bit to 10bit in ffmpeg should be automatic.
|
||||
\\n
|
||||
|
|
@ -161,7 +157,6 @@ const details = () => ({
|
|||
https://ffmpeg.org/ffmpeg-codecs.html#toc-HEVC-Options-1
|
||||
\\n
|
||||
==WARNING== \\n
|
||||
Just because a cmd is mentioned doesn't mean your installed version of ffmpeg supports it...
|
||||
Be certain to verify the cmds work before adding to your workflow. \\n
|
||||
Check Tdarr Help Tab. Enter ffmpeg cmd - "-h encoder=hevc_qsv". This will give a list of supported commands. \\n
|
||||
MAC SPECIFIC - This option is ignored on Mac because videotoolbox is used rather than qsv.
|
||||
|
|
@ -170,6 +165,7 @@ const details = () => ({
|
|||
\\nDefault is empty but the first example below has a suggested value. If unsure just leave empty.
|
||||
\\nEnsure to only use cmds valid to encoding QSV as the script handles other ffmpeg cmds relating to
|
||||
bitrate etc. Anything else entered here might be supported but could cause undesired results.
|
||||
\\nIf you are using a "-vf" cmd, please put it at the end to avoid issues!
|
||||
\\nExample:\\n
|
||||
-look_ahead 1 -look_ahead_depth 100 -extbrc 1 -rdo 1 -mbbrc 1 -b_strategy 1 -adaptive_i 1 -adaptive_b 1
|
||||
\\n Above enables look ahead, extended bitrate control, b-frames, etc.\\n
|
||||
|
|
@ -189,7 +185,7 @@ const details = () => ({
|
|||
},
|
||||
tooltip: `\\n
|
||||
==DESCRIPTION==
|
||||
\\nSpecify bitrate cutoff, files with a video bitrate lower then this will not be processed. \n
|
||||
\\nSpecify bitrate cutoff, files with a video bitrate lower then this will not be processed.\n
|
||||
\\n
|
||||
==INFO==
|
||||
\\nRate is in kbps.
|
||||
|
|
@ -330,15 +326,13 @@ let bitrateSettings = '';
|
|||
let inflatedCutoff = 0;
|
||||
let main10 = false;
|
||||
let high10 = false;
|
||||
let swDecode = false;
|
||||
let videoBR = 0;
|
||||
|
||||
// Finds the first video stream and get video bitrate
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
const lib = require('../methods/lib')();
|
||||
const os = require('os');
|
||||
const proc = require('child_process');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
const response = {
|
||||
|
|
@ -353,126 +347,71 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
|
||||
if (file.fileMedium !== 'video') {
|
||||
response.processFile = false;
|
||||
response.infoLog += `☒ File seems to be ${file.fileMedium} & not video. Exiting \n`;
|
||||
response.infoLog += `☒ File seems to be ${file.fileMedium} & not video. Exiting\n`;
|
||||
return response;
|
||||
}
|
||||
|
||||
// MKVPROPEDIT - Refresh video stats
|
||||
const intStatsDays = 7; // Use 1 week threshold for new stats
|
||||
let statsUptoDate = false;
|
||||
const currentFileName = file._id;
|
||||
let statsError = false;
|
||||
let metadataEncode = '';
|
||||
|
||||
// Only process MKV files
|
||||
if (file.container === 'mkv') {
|
||||
let datStats = Date.parse(new Date(70, 1).toISOString()); // Placeholder date
|
||||
metadataEncode = `-map_metadata:g -1 -metadata JBDONEDATE=${datStats}`;
|
||||
if (file.mediaInfo.track[0].extra !== undefined && file.mediaInfo.track[0].extra.JBDONEDATE !== undefined) {
|
||||
datStats = Date.parse(file.mediaInfo.track[0].extra.JBDONEDATE);
|
||||
} else {
|
||||
try {
|
||||
if (
|
||||
file.mediaInfo.track[0].extra !== undefined
|
||||
&& file.ffProbeData.streams[0].tags['_STATISTICS_WRITING_DATE_UTC-eng'] !== undefined
|
||||
) {
|
||||
// Set stats date to match info inside file
|
||||
datStats = Date.parse(`${file.ffProbeData.streams[0].tags['_STATISTICS_WRITING_DATE_UTC-eng']} GMT`);
|
||||
}
|
||||
} catch (err) {
|
||||
// Catch error - Ignore & carry on - If check can bomb out if the tag doesn't exist...
|
||||
}
|
||||
}
|
||||
|
||||
// Threshold for stats date
|
||||
const statsThres = Date.parse(new Date(new Date().setDate(new Date().getDate() - intStatsDays)).toISOString());
|
||||
|
||||
// Strings for easy to read dates in info log
|
||||
let statsThresString = new Date(statsThres);
|
||||
statsThresString = statsThresString.toUTCString();
|
||||
let datStatsString = new Date(datStats);
|
||||
datStatsString = datStatsString.toUTCString();
|
||||
response.infoLog += `Checking file stats - If stats are older than ${intStatsDays} days we'll grab new stats.\n
|
||||
Stats threshold: ${statsThresString}\n
|
||||
Current stats date: ${datStatsString}\n`;
|
||||
|
||||
// Are the stats out of date?
|
||||
if (datStats >= statsThres) {
|
||||
statsUptoDate = true;
|
||||
response.infoLog += '☑ File stats are upto date! - Continuing...\n';
|
||||
} else {
|
||||
response.infoLog += '☒ File stats are out of date! - Will attempt to use mkvpropedit to refresh stats\n';
|
||||
try {
|
||||
if (otherArguments.mkvpropeditPath !== '') { // Try to use mkvpropedit path if it is set
|
||||
proc.execSync(`"${otherArguments.mkvpropeditPath}" --add-track-statistics-tags "${currentFileName}"`);
|
||||
} else { // Otherwise just use standard mkvpropedit cmd
|
||||
proc.execSync(`mkvpropedit --add-track-statistics-tags "${currentFileName}"`);
|
||||
}
|
||||
} catch (err) {
|
||||
response.infoLog += '☒ Error updating file stats - Possible mkvpropedit failure or file issue - '
|
||||
+ ' Ensure mkvpropedit is set correctly in the node settings & check the filename for unusual characters.\n'
|
||||
+ ' Continuing but file stats will likely be inaccurate...\n';
|
||||
statsError = true;
|
||||
}
|
||||
if (statsError !== true) {
|
||||
// File now updated with new stats
|
||||
response.infoLog += 'Remuxing file to write in updated file stats! \n';
|
||||
response.preset += `-fflags +genpts <io> -map 0 -c copy -max_muxing_queue_size 9999 -map_metadata:g -1
|
||||
-metadata JBDONEDATE=${new Date().toISOString()}`;
|
||||
response.processFile = true;
|
||||
return response;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
response.infoLog += 'Input file is not MKV so cannot use mkvpropedit to get new file stats. '
|
||||
+ 'Continuing but file stats will likely be inaccurate...\n';
|
||||
}
|
||||
|
||||
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
|
||||
const strstreamType = file.ffProbeData.streams[i].codec_type.toLowerCase();
|
||||
videoIdx = -1;
|
||||
// Check if stream is a video.
|
||||
if (videoIdx === -1 && strstreamType === 'video') {
|
||||
videoIdx = i;
|
||||
videoBR = Number(file.mediaInfo.track[i + 1].BitRate) / 1000;
|
||||
|
||||
// If MediaInfo fails somehow fallback to ffprobe - Try two types of tags that might exist
|
||||
if (videoBR <= 0) {
|
||||
if (Number(file.ffProbeData.streams[i].tags.BPS) > 0) {
|
||||
videoBR = file.ffProbeData.streams[i].tags.BPS / 1000;
|
||||
} else {
|
||||
try {
|
||||
if (Number(file.ffProbeData.streams[i].tags.BPS['-eng']) > 0) {
|
||||
videoBR = file.ffProbeData.streams[i].tags.BPS['-eng'] / 1000;
|
||||
if (strstreamType === 'video') {
|
||||
if (file.ffProbeData.streams[i].codec_name !== 'mjpeg'
|
||||
&& file.ffProbeData.streams[i].codec_name !== 'png') {
|
||||
if (videoBR <= 0) { // Process if videoBR is not yet valid
|
||||
try { // Try checking file stats using Mediainfo first, then ffprobe.
|
||||
videoBR = Number(file.mediaInfo.track[i + 1].BitRate) / 1000;
|
||||
if (videoBR <= 0 || Number.isNaN(videoBR)) {
|
||||
if (Number(file.ffProbeData.streams[i].tags.BPS) > 0) {
|
||||
videoBR = file.ffProbeData.streams[i].tags.BPS / 1000;
|
||||
} else if (Number(file.ffProbeData.streams[i].tags.BPS['-eng']) > 0) {
|
||||
videoBR = file.ffProbeData.streams[i].tags.BPS['-eng'] / 1000;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Catch error - Ignore & carry on - If check can bomb out if tags don't exist...
|
||||
videoBR = 0; // Set videoBR to 0 for safety
|
||||
}
|
||||
}
|
||||
if (duration <= 0) { // Process if duration is not yet valid
|
||||
try { // Attempt to get duration info
|
||||
if (Number.isNaN(file.meta.Duration)) {
|
||||
duration = file.meta.Duration;
|
||||
duration = (new Date(`1970-01-01T${duration}Z`).getTime() / 1000) / 60;
|
||||
} else if (file.meta.Duration > 0) {
|
||||
duration = file.meta.Duration / 60;
|
||||
}
|
||||
if (duration <= 0 || Number.isNaN(duration)) {
|
||||
if (typeof file.mediaInfo.track[i + 1].Duration !== 'undefined') {
|
||||
duration = file.mediaInfo.track[i + 1].Duration;
|
||||
duration = (new Date(`1970-01-01T${duration}Z`).getTime() / 1000) / 60;
|
||||
} else if (typeof file.ffProbeData.streams[i].tags.DURATION !== 'undefined') {
|
||||
duration = file.ffProbeData.streams[i].tags.DURATION;
|
||||
duration = (new Date(`1970-01-01T${duration}Z`).getTime() / 1000) / 60;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Catch error - Ignore & carry on - If check can bomb out if tags don't exist...
|
||||
duration = 0; // Set duration to 0 for safety
|
||||
}
|
||||
}
|
||||
if ((videoBR <= 0 || Number.isNaN(videoBR)) || (duration <= 0 || Number.isNaN(duration))) {
|
||||
// videoBR or duration not yet valid so Loop
|
||||
} else {
|
||||
break;// Exit loop if both valid
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if duration info is filled, if so convert time format to minutes.
|
||||
// If not filled then get duration of video stream and do the same.
|
||||
if (typeof file.meta.Duration !== 'undefined') {
|
||||
duration = file.meta.Duration;
|
||||
// Get seconds by using a Date & then convert to minutes
|
||||
duration = (new Date(`1970-01-01T${duration}Z`).getTime() / 1000) / 60;
|
||||
} else {
|
||||
duration = file.ffProbeData.streams[videoIdx].tags.DURATION;
|
||||
duration = (new Date(`1970-01-01T${duration}Z`).getTime() / 1000) / 60;
|
||||
}
|
||||
|
||||
if (Number.isNaN(videoBR) || videoBR <= 0) {
|
||||
// Work out currentBitrate using "Bitrate = file size / (number of minutes * .0075)"
|
||||
currentBitrate = Math.round(file.file_size / (duration * 0.0075));
|
||||
response.infoLog += '==WARNING== Failed to get an accurate video bitrate, ';
|
||||
response.infoLog += `falling back to old method to get OVERALL file bitrate of ${currentBitrate}kbps. `;
|
||||
response.infoLog += 'Bitrate calculations for video encode will likely be inaccurate... \n';
|
||||
response.infoLog += '==WARNING== Failed to get an accurate video bitrate, '
|
||||
+ `falling back to old method to get OVERALL file bitrate of ${currentBitrate}kbps. `
|
||||
+ 'Bitrate calculations for video encode will likely be inaccurate...\n';
|
||||
} else {
|
||||
currentBitrate = Math.round(videoBR);
|
||||
response.infoLog += `☑ It looks like the current video bitrate is ${currentBitrate}kbps. \n`;
|
||||
response.infoLog += `☑ It looks like the current video bitrate is ${currentBitrate}kbps.\n`;
|
||||
}
|
||||
|
||||
// Get overall bitrate for use with HEVC reprocessing
|
||||
|
|
@ -486,8 +425,8 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
// If targetBitrate or currentBitrate comes out as 0 then something
|
||||
// has gone wrong and bitrates could not be calculated.
|
||||
// Cancel plugin completely.
|
||||
if (targetBitrate <= 0 || currentBitrate <= 0) {
|
||||
response.infoLog += '☒ Target bitrate could not be calculated. Skipping this plugin. \n';
|
||||
if (targetBitrate <= 0 || currentBitrate <= 0 || overallBitRate <= 0) {
|
||||
response.infoLog += '☒ Target bitrates could not be calculated. Skipping this plugin.\n';
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
@ -495,19 +434,18 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
// has gone wrong as that is not what we want.
|
||||
// Cancel plugin completely.
|
||||
if (targetBitrate >= currentBitrate) {
|
||||
response.infoLog += `☒ Target bitrate has been calculated as ${targetBitrate}kbps. This is equal or greater `;
|
||||
response.infoLog += "than the current bitrate... Something has gone wrong and this shouldn't happen! "
|
||||
+ 'Skipping this plugin. \n';
|
||||
response.infoLog += `☒ Target bitrate has been calculated as ${targetBitrate}kbps. This is equal or greater than `
|
||||
+ "the current bitrate... Something has gone wrong and this shouldn't happen! Skipping this plugin.\n";
|
||||
return response;
|
||||
}
|
||||
|
||||
// Ensure that bitrate_cutoff is set if reconvert_hevc is true since we need some protection against a loop
|
||||
// Cancel the plugin
|
||||
if (inputs.reconvert_hevc === true && inputs.bitrate_cutoff <= 0 && inputs.hevc_max_bitrate <= 0) {
|
||||
response.infoLog += `Reconvert HEVC is ${inputs.reconvert_hevc}, however there is no bitrate cutoff `;
|
||||
response.infoLog += 'or HEVC specific cutoff set so we have no way to know when to stop processing this file. \n'
|
||||
+ 'Either set reconvert_HEVC to false or set a bitrate cutoff and set a hevc_max_bitrate cutoff. \n'
|
||||
+ '☒ Skipping this plugin. \n';
|
||||
response.infoLog += `Reconvert HEVC is ${inputs.reconvert_hevc}, however there is no bitrate cutoff or HEVC `
|
||||
+ 'specific cutoff set so we have no way to know when to stop processing this file.\n'
|
||||
+ 'Either set reconvert_HEVC to false or set a bitrate cutoff and set a hevc_max_bitrate cutoff.\n'
|
||||
+ '☒ Skipping this plugin.\n';
|
||||
return response;
|
||||
}
|
||||
|
||||
|
|
@ -517,13 +455,13 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
// Checks if currentBitrate is below inputs.bitrate_cutoff.
|
||||
// If so then cancel plugin without touching original files.
|
||||
if (currentBitrate <= inputs.bitrate_cutoff) {
|
||||
response.infoLog += `☑ Current bitrate is below set cutoff of ${inputs.bitrate_cutoff}kbps. \n`
|
||||
+ 'Cancelling plugin. \n';
|
||||
response.infoLog += `☑ Current bitrate is below set cutoff of ${inputs.bitrate_cutoff}kbps.\n`
|
||||
+ 'Cancelling plugin.\n';
|
||||
return response;
|
||||
}
|
||||
// If above cutoff then carry on
|
||||
if (currentBitrate > inputs.bitrate_cutoff && inputs.reconvert_hevc === false) {
|
||||
response.infoLog += '☒ Current bitrate appears to be above the cutoff. Need to process \n';
|
||||
response.infoLog += '☒ Current bitrate appears to be above the cutoff. Need to process\n';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -531,8 +469,8 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
// Checks if targetBitrate is above inputs.max_average_bitrate.
|
||||
// If so then clamp target bitrate
|
||||
if (targetBitrate > inputs.max_average_bitrate) {
|
||||
response.infoLog += 'Our target bitrate is above the max_average_bitrate ';
|
||||
response.infoLog += `so clamping at max of ${inputs.max_average_bitrate}kbps. \n`;
|
||||
response.infoLog += 'Our target bitrate is above the max_average_bitrate so clamping at max of '
|
||||
+ `${inputs.max_average_bitrate}kbps.\n`;
|
||||
targetBitrate = Math.round(inputs.max_average_bitrate);
|
||||
minimumBitrate = Math.round(targetBitrate * 0.75);
|
||||
maximumBitrate = Math.round(targetBitrate * 1.25);
|
||||
|
|
@ -544,14 +482,14 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
if (inputs.min_average_bitrate > 0) {
|
||||
// Exit the plugin is the cutoff is less than the min average bitrate. Most likely user error
|
||||
if (inputs.bitrate_cutoff < inputs.min_average_bitrate) {
|
||||
response.infoLog += `☒ Bitrate cutoff ${inputs.bitrate_cutoff}k is less than the set minimum
|
||||
average bitrate set of ${inputs.min_average_bitrate}kbps. We don't want this. Cancelling plugin. \n`;
|
||||
response.infoLog += `☒ Bitrate cutoff ${inputs.bitrate_cutoff}k is less than the set minimum `
|
||||
+ `average bitrate set of ${inputs.min_average_bitrate}kbps. We don't want this. Cancelling plugin.\n`;
|
||||
return response;
|
||||
}
|
||||
// Checks if inputs.bitrate_cutoff is below inputs.min_average_bitrate.
|
||||
// If so then set currentBitrate to the minimum allowed.)
|
||||
if (targetBitrate < inputs.min_average_bitrate) {
|
||||
response.infoLog += `Target average bitrate clamped at min of ${inputs.min_average_bitrate}kbps. \n`;
|
||||
response.infoLog += `Target average bitrate clamped at min of ${inputs.min_average_bitrate}kbps.\n`;
|
||||
targetBitrate = Math.round(inputs.min_average_bitrate);
|
||||
minimumBitrate = Math.round(targetBitrate * 0.75);
|
||||
maximumBitrate = Math.round(targetBitrate * 1.25);
|
||||
|
|
@ -610,98 +548,92 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
// Check if codec of stream is mjpeg/png, if so then remove this "video" stream.
|
||||
// mjpeg/png are usually embedded pictures that can cause havoc with plugins.
|
||||
if (file.ffProbeData.streams[i].codec_name === 'mjpeg' || file.ffProbeData.streams[i].codec_name === 'png') {
|
||||
extraArguments += `-map -v:${videoIdx} `;
|
||||
}
|
||||
|
||||
// Check for HDR in files. If so exit plugin. We assume HDR files have bt2020 color spaces. HDR can be complicated
|
||||
// and some aspects are still unsupported in ffmpeg I believe. Likely we don't want to re-encode anything HDR.
|
||||
if (file.ffProbeData.streams[i].color_space === 'bt2020nc'
|
||||
&& file.ffProbeData.streams[i].color_transfer === 'smpte2084'
|
||||
&& file.ffProbeData.streams[i].color_primaries === 'bt2020') {
|
||||
response.infoLog += '☒ This looks to be a HDR file. HDR files are unfortunately '
|
||||
+ 'not supported by this plugin. Exiting plugin. \n\n';
|
||||
return response;
|
||||
}
|
||||
|
||||
// Now check if we're reprocessing HEVC files, if not then ensure we don't convert HEVC again
|
||||
if (inputs.reconvert_hevc === false && (file.ffProbeData.streams[i].codec_name === 'hevc'
|
||||
|| file.ffProbeData.streams[i].codec_name === 'vp9' || file.ffProbeData.streams[i].codec_name === 'av1')) {
|
||||
// Check if codec of stream is HEVC, VP9 or AV1 AND check if file.container matches inputs.container.
|
||||
// If so nothing for plugin to do.
|
||||
if ((file.ffProbeData.streams[i].codec_name === 'hevc' || file.ffProbeData.streams[i].codec_name === 'vp9'
|
||||
|| file.ffProbeData.streams[i].codec_name === 'av1') && file.container === inputs.container) {
|
||||
response.infoLog += `☑ File is already HEVC, VP9 or AV1 & in ${inputs.container}. \n`;
|
||||
return response;
|
||||
extraArguments += `-map -0:v:${videoIdx} `;
|
||||
} else { // Ensure to only do further checks if video stream is valid for use
|
||||
// Check for HDR in files. Attempt to use same color
|
||||
if ((file.ffProbeData.streams[i].color_space === 'bt2020nc'
|
||||
|| file.ffProbeData.streams[i].color_space === 'bt2020n')
|
||||
&& (file.ffProbeData.streams[i].color_transfer === 'smpte2084'
|
||||
|| file.ffProbeData.streams[i].color_transfer === 'arib-std-b67')
|
||||
&& file.ffProbeData.streams[i].color_primaries === 'bt2020') {
|
||||
response.infoLog += '==WARNING== This looks to be a HDR file. HDR is supported but '
|
||||
+ 'correct encoding is not guaranteed.\n';
|
||||
extraArguments += `-color_primaries ${file.ffProbeData.streams[i].color_primaries} `
|
||||
+ `-color_trc ${file.ffProbeData.streams[i].color_transfer} `
|
||||
+ `-colorspace ${file.ffProbeData.streams[i].color_space} `;
|
||||
}
|
||||
|
||||
// Check if codec of stream is HEVC, Vp9 or AV1
|
||||
// AND check if file.container does NOT match inputs.container.
|
||||
// If so remux file.
|
||||
if ((file.ffProbeData.streams[i].codec_name === 'hevc' || file.ffProbeData.streams[i].codec_name === 'vp9'
|
||||
// AND check if file.container does NOT match inputs.container. If so remux file.
|
||||
if ((file.ffProbeData.streams[i].codec_name === 'hevc'
|
||||
|| file.ffProbeData.streams[i].codec_name === 'vp9'
|
||||
|| file.ffProbeData.streams[i].codec_name === 'av1') && file.container !== inputs.container) {
|
||||
response.infoLog += `☒ File is HEVC, VP9 or AV1 but is not in ${inputs.container} container. Remuxing. \n`;
|
||||
response.infoLog += `☒ File is HEVC, VP9 or AV1 but is not in ${inputs.container} container. Remuxing.\n`;
|
||||
response.preset = `<io> -map 0 -c copy ${extraArguments}`;
|
||||
response.processFile = true;
|
||||
return response;
|
||||
}
|
||||
|
||||
// New logic for reprocessing HEVC. Mainly done for my own use.
|
||||
// We attempt to get accurate stats earlier - If we can't we fall back onto overall bitrate
|
||||
// which can be inaccurate. We may inflate the current bitrate check so we don't keep looping this logic.
|
||||
} else if (inputs.reconvert_hevc === true && (file.ffProbeData.streams[i].codec_name === 'hevc'
|
||||
|| file.ffProbeData.streams[i].codec_name === 'vp9' || file.ffProbeData.streams[i].codec_name === 'av1')) {
|
||||
if (statsUptoDate !== true) {
|
||||
currentBitrate = overallBitRate; // User overall bitrate if we don't have upto date stats
|
||||
response.infoLog += `☒ Unable to get accurate stats for HEVC so falling back to Overall file Bitrate.
|
||||
Remux to MKV to allow generation of accurate video bitrate statistics.
|
||||
File overall bitrate is ${overallBitRate}kbps.\n`;
|
||||
}
|
||||
if (inputs.hevc_max_bitrate > 0) {
|
||||
if (currentBitrate > inputs.hevc_max_bitrate) {
|
||||
// If bitrate is higher then hevc_max_bitrate then need to re-encode
|
||||
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, `
|
||||
+ `VP9 or AV1. Using HEVC specific cutoff of ${inputs.hevc_max_bitrate}kbps. \n`;
|
||||
response.infoLog += '☒ The file is still above this new cutoff! Reconverting. \n';
|
||||
} else {
|
||||
// Otherwise we're now below the hevc cutoff and we can exit
|
||||
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, `
|
||||
+ `VP9 or AV1. Using HEVC specific cutoff of ${inputs.hevc_max_bitrate}kbps. \n`;
|
||||
response.infoLog += '☑ The file is NOT above this new cutoff. Exiting plugin. \n';
|
||||
// Now check if we're reprocessing HEVC files, if not then ensure we don't convert HEVC again
|
||||
if (inputs.reconvert_hevc === false && (file.ffProbeData.streams[i].codec_name === 'hevc'
|
||||
|| file.ffProbeData.streams[i].codec_name === 'vp9' || file.ffProbeData.streams[i].codec_name === 'av1')) {
|
||||
// Check if codec of stream is HEVC, VP9 or AV1 AND check if file.container matches inputs.container.
|
||||
// If so nothing for plugin to do.
|
||||
if ((file.ffProbeData.streams[i].codec_name === 'hevc' || file.ffProbeData.streams[i].codec_name === 'vp9'
|
||||
|| file.ffProbeData.streams[i].codec_name === 'av1') && file.container === inputs.container) {
|
||||
response.infoLog += `☑ File is already HEVC, VP9 or AV1 & in ${inputs.container}.\n`;
|
||||
return response;
|
||||
}
|
||||
|
||||
// If we're not using the hevc max bitrate then we need a safety net to try and ensure we don't keep
|
||||
// looping this plugin. For maximum safety we simply multiply the cutoff by 2.
|
||||
} else if (currentBitrate > (inputs.bitrate_cutoff * 2)) {
|
||||
inflatedCutoff = Math.round(inputs.bitrate_cutoff * 2);
|
||||
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, `;
|
||||
response.infoLog += 'VP9 or AV1. Will use Overall file Bitrate for HEVC files as safety, ';
|
||||
response.infoLog += `bitrate is ${overallBitRate}kbps. \n`;
|
||||
response.infoLog += 'HEVC specific cutoff not set so bitrate_cutoff is multiplied by 2 for safety! \n';
|
||||
response.infoLog += `Cutoff now temporarily ${inflatedCutoff}kbps. \n`;
|
||||
response.infoLog += '☒ The file is still above this new cutoff! Reconverting. \n';
|
||||
} else {
|
||||
// File is below cutoff so we can exit
|
||||
inflatedCutoff = Math.round(inputs.bitrate_cutoff * 2);
|
||||
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, `;
|
||||
response.infoLog += 'VP9 or AV1. Will use Overall file Bitrate for HEVC files as safety, ';
|
||||
response.infoLog += `bitrate is ${overallBitRate}kbps. \n`;
|
||||
response.infoLog += 'HEVC specific cutoff not set so bitrate_cutoff is multiplied by 2 for safety! \n';
|
||||
response.infoLog += `Cutoff now temporarily ${inflatedCutoff}kbps. \n`;
|
||||
response.infoLog += '☑The file is NOT above this new cutoff. Exiting plugin. \n';
|
||||
return response;
|
||||
}
|
||||
}
|
||||
// New logic for reprocessing HEVC. Mainly done for my own use.
|
||||
// We attempt to get accurate stats earlier - If we can't we fall back onto overall bitrate
|
||||
// which can be inaccurate. We may inflate the current bitrate check so we don't keep looping this logic.
|
||||
} else if (inputs.reconvert_hevc === true && (file.ffProbeData.streams[i].codec_name === 'hevc'
|
||||
|| file.ffProbeData.streams[i].codec_name === 'vp9' || file.ffProbeData.streams[i].codec_name === 'av1')) {
|
||||
if (inputs.hevc_max_bitrate > 0) {
|
||||
if (currentBitrate > inputs.hevc_max_bitrate) {
|
||||
// If bitrate is higher then hevc_max_bitrate then need to re-encode
|
||||
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, VP9 or AV1. `
|
||||
+ `Using HEVC specific cutoff of ${inputs.hevc_max_bitrate}kbps.\n`
|
||||
+ '☒ The file is still above this new cutoff! Reconverting.\n';
|
||||
} else {
|
||||
// Otherwise we're now below the hevc cutoff and we can exit
|
||||
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, VP9 or AV1. `
|
||||
+ `Using HEVC specific cutoff of ${inputs.hevc_max_bitrate}kbps.\n`
|
||||
+ '☑ The file is NOT above this new cutoff. Exiting plugin.\n';
|
||||
return response;
|
||||
}
|
||||
|
||||
// On testing I've found files in the High10 profile don't play nice with hw decoding so mark these
|
||||
if (file.ffProbeData.streams[i].profile === 'High 10') {
|
||||
high10 = true;
|
||||
response.infoLog += 'Input file is 10bit using High10. Disabling hardware decoding to avoid problems. \n';
|
||||
}
|
||||
// If files are 10 bit or the enable_10bit setting is used mark to enable Main10.
|
||||
if (file.ffProbeData.streams[i].profile === 'Main 10' || file.ffProbeData.streams[i].bits_per_raw_sample === '10'
|
||||
|| inputs.enable_10bit === true) {
|
||||
main10 = true;
|
||||
// If we're not using the hevc max bitrate then we need a safety net to try and ensure we don't keep
|
||||
// looping this plugin. For maximum safety we simply multiply the cutoff by 2.
|
||||
} else if (currentBitrate > (inputs.bitrate_cutoff * 2)) {
|
||||
inflatedCutoff = Math.round(inputs.bitrate_cutoff * 2);
|
||||
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, VP9 or AV1. `
|
||||
+ `Will use Overall file Bitrate for HEVC files as safety, bitrate is ${overallBitRate}kbps.\n`
|
||||
+ 'HEVC specific cutoff not set so bitrate_cutoff is multiplied by 2 for safety!\n'
|
||||
+ `Cutoff now temporarily ${inflatedCutoff}kbps.\n`
|
||||
+ '☒ The file is still above this new cutoff! Reconverting.\n';
|
||||
} else {
|
||||
// File is below cutoff so we can exit
|
||||
inflatedCutoff = Math.round(inputs.bitrate_cutoff * 2);
|
||||
response.infoLog += `Reconvert_hevc is ${inputs.reconvert_hevc} & the file is already HEVC, VP9 or AV1. `
|
||||
+ `Will use Overall file Bitrate for HEVC files as safety, bitrate is ${overallBitRate}kbps.\n`
|
||||
+ 'HEVC specific cutoff not set so bitrate_cutoff is multiplied by 2 for safety!\n'
|
||||
+ `Cutoff now temporarily ${inflatedCutoff}kbps.\n`
|
||||
+ '☑The file is NOT above this new cutoff. Exiting plugin.\n';
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
// Files in the High10 profile are not supported for HW Decode
|
||||
if (file.ffProbeData.streams[i].profile === 'High 10') {
|
||||
high10 = true;
|
||||
main10 = true;
|
||||
// If files are 10 bit or the enable_10bit setting is used mark to enable Main10.
|
||||
} else if (file.ffProbeData.streams[i].profile === 'Main 10'
|
||||
|| file.ffProbeData.streams[i].bits_per_raw_sample === '10' || inputs.enable_10bit === true) {
|
||||
main10 = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Increment video index. Needed to keep track of video id in case there is more than one video track.
|
||||
|
|
@ -710,78 +642,145 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
}
|
||||
}
|
||||
|
||||
// Specify the output format
|
||||
switch (inputs.container) {
|
||||
case 'mkv':
|
||||
extraArguments += '-f matroska ';
|
||||
break;
|
||||
case 'mp4':
|
||||
extraArguments += '-f mp4 ';
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
||||
// Some video codecs don't support HW decode so mark these
|
||||
// VC1 & VP8 are no longer supported on new HW, add cases here if your HW does support
|
||||
switch (file.video_codec_name) {
|
||||
case 'mpeg2':
|
||||
break;
|
||||
case 'h264':
|
||||
if (high10 === true) {
|
||||
swDecode = true;
|
||||
response.infoLog += 'Input file is h264 High10. Hardware Decode not supported.\n';
|
||||
}
|
||||
break;
|
||||
case 'mjpeg':
|
||||
break;
|
||||
case 'hevc':
|
||||
break;
|
||||
case 'vp9':// Should be supported by 8th Gen +
|
||||
break;
|
||||
case 'av1':// Should be supported by 11th gen +
|
||||
break;
|
||||
default:
|
||||
swDecode = true;
|
||||
response.infoLog += `Input file is ${file.video_codec_name}. Hardware Decode not supported.\n`;
|
||||
}
|
||||
|
||||
// Are we encoding to 10 bit? If so enable correct profile & pixel format.
|
||||
if (high10 === true) { // This is used if we have High10 files. SW decode and use standard -pix_fmt p010le
|
||||
extraArguments += '-profile:v main10 -pix_fmt p010le ';
|
||||
response.infoLog += '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n';
|
||||
} else if (main10 === true) { // Pixel formate method when using HW decode
|
||||
extraArguments += '-profile:v main10 -vf scale_qsv=format=p010le ';
|
||||
response.infoLog += '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n';
|
||||
if (os.platform() !== 'darwin') {
|
||||
if (swDecode === true && main10 === true) {
|
||||
// This is used if we have High10 or Main10 is enabled & odd format files.
|
||||
// SW decode and use standard -pix_fmt p010le
|
||||
extraArguments += '-profile:v main10 -pix_fmt p010le ';
|
||||
response.infoLog += '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n';
|
||||
} else if (main10 === true) { // Pixel formate method when using HW decode
|
||||
if (inputs.extra_qsv_options.search('-vf scale_qsv') >= 0) {
|
||||
extraArguments += '-profile:v main10';
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputs.extra_qsv_options += ',format=p010le'; // Only add on the pixel format to existing scale_qsv cmd
|
||||
} else {
|
||||
extraArguments += '-profile:v main10 -vf scale_qsv=format=p010le';
|
||||
}
|
||||
response.infoLog += '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n';
|
||||
}
|
||||
} else {
|
||||
// Mac - Video toolbox profile & pixel format
|
||||
extraArguments += '-profile:v 2 -pix_fmt yuv420p10le ';
|
||||
response.infoLog += '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n';
|
||||
}
|
||||
|
||||
// Set bitrateSettings variable using bitrate information calculated earlier.
|
||||
bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k `
|
||||
+ `-maxrate ${maximumBitrate}k -bufsize ${currentBitrate}k`;
|
||||
// Print to infoLog information around file & bitrate settings.
|
||||
response.infoLog += `Container for output selected as ${inputs.container}. \n`;
|
||||
response.infoLog += 'Encode variable bitrate settings: \n';
|
||||
response.infoLog += `Target = ${targetBitrate}k \n`;
|
||||
response.infoLog += `Minimum = ${minimumBitrate}k \n`;
|
||||
response.infoLog += `Maximum = ${maximumBitrate}k \n`;
|
||||
response.infoLog += `Container for output selected as ${inputs.container}.\n`
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ `Target = ${targetBitrate}k\n`
|
||||
+ `Minimum = ${minimumBitrate}k\n`
|
||||
+ `Maximum = ${maximumBitrate}k\n`;
|
||||
|
||||
// START PRESET
|
||||
// -fflags +genpts should regenerate timestamps if they end up missing...
|
||||
response.preset = '-fflags +genpts ';
|
||||
|
||||
// HW ACCEL FLAGS - I think these are good practice but are they necessary?
|
||||
// HW ACCEL FLAGS
|
||||
// Account for different OS
|
||||
if (high10 === false) {
|
||||
// Seems incoming High10 files don't play nice decoding so use software decode
|
||||
if (swDecode !== true) {
|
||||
// Only enable hw decode for accepted formats
|
||||
switch (os.platform()) {
|
||||
case 'darwin': // Mac OS - Enable videotoolbox instead of QSV
|
||||
response.preset += '-hwaccel videotoolbox';
|
||||
break;
|
||||
case 'linux': // Linux - Full device, should fix child_device_type warnings
|
||||
response.preset += `-hwaccel qsv -hwaccel_output_format qsv
|
||||
-init_hw_device qsv:hw_any,child_device_type=vaapi `;
|
||||
response.preset += '-hwaccel qsv -hwaccel_output_format qsv '
|
||||
+ '-init_hw_device qsv:hw_any,child_device_type=vaapi ';
|
||||
break;
|
||||
case 'win32': // Windows - Full device, should fix child_device_type warnings
|
||||
response.preset += `-hwaccel qsv -hwaccel_output_format qsv
|
||||
-init_hw_device qsv:hw_any,child_device_type=d3d11va `;
|
||||
response.preset += '-hwaccel qsv -hwaccel_output_format qsv '
|
||||
+ '-init_hw_device qsv:hw,child_device_type=d3d11va ';
|
||||
break;
|
||||
default:
|
||||
response.preset += '-hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any ';
|
||||
}
|
||||
} else {
|
||||
switch (os.platform()) {
|
||||
case 'darwin': // Mac OS - Enable videotoolbox instead of QSV
|
||||
response.preset += '-hwaccel videotoolbox';
|
||||
break;
|
||||
case 'linux': // Linux - Full device, should fix child_device_type warnings
|
||||
response.preset += '-hwaccel_output_format qsv '
|
||||
+ '-init_hw_device qsv:hw_any,child_device_type=vaapi ';
|
||||
break;
|
||||
case 'win32': // Windows - Full device, should fix child_device_type warnings
|
||||
response.preset += '-hwaccel_output_format qsv '
|
||||
+ '-init_hw_device qsv:hw,child_device_type=d3d11va ';
|
||||
break;
|
||||
default:
|
||||
// Default to enabling hwaccel for output only
|
||||
response.preset += '-hwaccel_output_format qsv -init_hw_device qsv:hw_any ';
|
||||
}
|
||||
}
|
||||
|
||||
// DECODE FLAGS
|
||||
// VC1 & VP8 are no longer supported on new HW, add cases here if your HW does support
|
||||
if (os.platform() !== 'darwin') {
|
||||
if (high10 === false) { // Don't enable for High10
|
||||
switch (file.video_codec_name) {
|
||||
case 'mpeg2':
|
||||
response.preset += '-c:v mpeg2_qsv';
|
||||
break;
|
||||
case 'h264':
|
||||
switch (file.video_codec_name) {
|
||||
case 'mpeg2':
|
||||
response.preset += '-c:v mpeg2_qsv';
|
||||
break;
|
||||
case 'h264':
|
||||
if (high10 !== true) { // Don't enable for High10
|
||||
response.preset += '-c:v h264_qsv';
|
||||
break;
|
||||
case 'vc1':
|
||||
response.preset += '-c:v vc1_qsv';
|
||||
break;
|
||||
case 'mjpeg':
|
||||
response.preset += '-c:v mjpeg_qsv';
|
||||
break;
|
||||
case 'vp8':
|
||||
response.preset += '-c:v vp8_qsv';
|
||||
break;
|
||||
case 'hevc':
|
||||
response.preset += '-c:v hevc_qsv';
|
||||
break;
|
||||
case 'vp9': // Should be supported by 8th Gen +
|
||||
response.preset += '-c:v vp9_qsv';
|
||||
break;
|
||||
default:
|
||||
response.preset += '';
|
||||
}
|
||||
} else {
|
||||
response.preset += `-c:v ${file.video_codec_name}`;
|
||||
}
|
||||
break;
|
||||
case 'mjpeg':
|
||||
response.preset += '-c:v mjpeg_qsv';
|
||||
break;
|
||||
case 'hevc':
|
||||
response.preset += '-c:v hevc_qsv';
|
||||
break;
|
||||
case 'vp9': // Should be supported by 8th Gen +
|
||||
response.preset += '-c:v vp9_qsv';
|
||||
break;
|
||||
case 'av1': // Should be supported by 11th gen +
|
||||
response.preset += '-c:v av1_qsv';
|
||||
break;
|
||||
default:
|
||||
// Use incoming format for software decode
|
||||
response.preset += `-c:v ${file.video_codec_name}`;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -798,13 +797,94 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
response.preset += 'hevc_qsv';
|
||||
break;
|
||||
case 'win32':
|
||||
response.preset += 'hevc_qsv -load_plugin hevc_hw';
|
||||
// Windows needs the additional -load_plugin. Tested working on a Win 10 - i5-10505
|
||||
response.preset += 'hevc_qsv';
|
||||
// Tested working on a Win 10 - i5-10505
|
||||
break;
|
||||
default:
|
||||
response.preset += 'hevc_qsv'; // Default to QSV
|
||||
}
|
||||
|
||||
// Only add on for HW decoded formats
|
||||
// VC1 & VP8 are no longer supported on new HW, add cases here if your HW does support
|
||||
if (swDecode !== true && os.platform() !== 'darwin') {
|
||||
// Check if -vf cmd has already been used on user input
|
||||
if (inputs.extra_qsv_options.search('-vf scale_qsv') >= 0) {
|
||||
switch (file.video_codec_name) {
|
||||
case 'mpeg2':
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'h264':
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'mjpeg':
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'hevc':
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'vp9': // Should be supported by 8th Gen +
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'av1': // Should be supported by 11th gen +
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
inputs.extra_qsv_options += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
default:
|
||||
}
|
||||
} else if (extraArguments.search('-vf') === -1) {
|
||||
// Check if -vf cmd has been used on the other var instead, if not add it & rest of cmd
|
||||
switch (file.video_codec_name) {
|
||||
case 'mpeg2':
|
||||
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'h264':
|
||||
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'mjpeg':
|
||||
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'hevc':
|
||||
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'vp9': // Should be supported by 8th Gen +
|
||||
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'av1': // Should be supported by 11th gen +
|
||||
extraArguments += '-vf hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
default:
|
||||
}
|
||||
} else {
|
||||
// Otherwise add the cmd onto the end
|
||||
switch (file.video_codec_name) {
|
||||
case 'mpeg2':
|
||||
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'h264':
|
||||
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'mjpeg':
|
||||
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'hevc':
|
||||
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'vp9': // Should be supported by 8th Gen +
|
||||
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
case 'av1': // Should be supported by 11th gen +
|
||||
extraArguments += ',hwupload=extra_hw_frames=64,format=qsv ';
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add the rest of the ffmpeg command
|
||||
switch (os.platform()) {
|
||||
case 'darwin':
|
||||
|
|
@ -818,11 +898,11 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
|||
// Normal behavior
|
||||
response.preset += ` ${bitrateSettings} `
|
||||
+ `-preset ${inputs.encoder_speedpreset} ${inputs.extra_qsv_options} `
|
||||
+ `-c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments} ${metadataEncode}`;
|
||||
+ `-c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments}`;
|
||||
}
|
||||
|
||||
response.processFile = true;
|
||||
response.infoLog += 'File Transcoding... \n';
|
||||
response.infoLog += 'File Transcoding...\n';
|
||||
|
||||
return response;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -16,6 +16,66 @@ var details = function () { return ({
|
|||
sidebarPosition: -1,
|
||||
icon: '',
|
||||
inputs: [
|
||||
{
|
||||
label: 'Use % of Input Bitrate',
|
||||
name: 'useInputBitrate',
|
||||
type: 'boolean',
|
||||
defaultValue: 'false',
|
||||
inputUI: {
|
||||
type: 'switch',
|
||||
},
|
||||
tooltip: 'Specify whether to use a % of input bitrate as the output bitrate',
|
||||
},
|
||||
{
|
||||
label: 'Target Bitrate %',
|
||||
name: 'targetBitratePercent',
|
||||
type: 'string',
|
||||
defaultValue: '50',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
displayConditions: {
|
||||
logic: 'AND',
|
||||
sets: [
|
||||
{
|
||||
logic: 'AND',
|
||||
inputs: [
|
||||
{
|
||||
name: 'useInputBitrate',
|
||||
value: 'true',
|
||||
condition: '===',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: 'Specify the target bitrate as a % of the input bitrate',
|
||||
},
|
||||
{
|
||||
label: 'Fallback Bitrate',
|
||||
name: 'fallbackBitrate',
|
||||
type: 'string',
|
||||
defaultValue: '4000',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
displayConditions: {
|
||||
logic: 'AND',
|
||||
sets: [
|
||||
{
|
||||
logic: 'AND',
|
||||
inputs: [
|
||||
{
|
||||
name: 'useInputBitrate',
|
||||
value: 'true',
|
||||
condition: '===',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: 'Specify fallback bitrate in kbps if input bitrate is not available',
|
||||
},
|
||||
{
|
||||
label: 'Bitrate',
|
||||
name: 'bitrate',
|
||||
|
|
@ -23,6 +83,21 @@ var details = function () { return ({
|
|||
defaultValue: '5000',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
displayConditions: {
|
||||
logic: 'AND',
|
||||
sets: [
|
||||
{
|
||||
logic: 'AND',
|
||||
inputs: [
|
||||
{
|
||||
name: 'useInputBitrate',
|
||||
value: 'true',
|
||||
condition: '!==',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: 'Specify bitrate in kbps',
|
||||
},
|
||||
|
|
@ -40,10 +115,36 @@ var plugin = function (args) {
|
|||
var lib = require('../../../../../methods/lib')();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
|
||||
args.inputs = lib.loadDefaultValues(args.inputs, details);
|
||||
var useInputBitrate = args.inputs.useInputBitrate;
|
||||
var targetBitratePercent = String(args.inputs.targetBitratePercent);
|
||||
var fallbackBitrate = String(args.inputs.fallbackBitrate);
|
||||
var bitrate = String(args.inputs.bitrate);
|
||||
args.variables.ffmpegCommand.streams.forEach(function (stream) {
|
||||
var _a, _b, _c, _d;
|
||||
if (stream.codec_type === 'video') {
|
||||
var ffType = (0, fileUtils_1.getFfType)(stream.codec_type);
|
||||
stream.outputArgs.push("-b:".concat(ffType, ":{outputTypeIndex}"), "".concat(String(args.inputs.bitrate), "k"));
|
||||
if (useInputBitrate) {
|
||||
args.jobLog('Attempting to use % of input bitrate as output bitrate');
|
||||
// check if input bitrate is available
|
||||
var mediainfoIndex = stream.index + 1;
|
||||
var inputBitrate = (_d = (_c = (_b = (_a = args === null || args === void 0 ? void 0 : args.inputFileObj) === null || _a === void 0 ? void 0 : _a.mediaInfo) === null || _b === void 0 ? void 0 : _b.track) === null || _c === void 0 ? void 0 : _c[mediainfoIndex]) === null || _d === void 0 ? void 0 : _d.BitRate;
|
||||
if (inputBitrate) {
|
||||
args.jobLog("Found input bitrate: ".concat(inputBitrate));
|
||||
// @ts-expect-error type
|
||||
inputBitrate = parseInt(inputBitrate, 10) / 1000;
|
||||
var targetBitrate = (inputBitrate * (parseInt(targetBitratePercent, 10) / 100));
|
||||
args.jobLog("Setting video bitrate as ".concat(targetBitrate, "k"));
|
||||
stream.outputArgs.push("-b:".concat(ffType, ":{outputTypeIndex}"), "".concat(targetBitrate, "k"));
|
||||
}
|
||||
else {
|
||||
args.jobLog("Unable to find input bitrate, setting fallback bitrate as ".concat(fallbackBitrate, "k"));
|
||||
stream.outputArgs.push("-b:".concat(ffType, ":{outputTypeIndex}"), "".concat(fallbackBitrate, "k"));
|
||||
}
|
||||
}
|
||||
else {
|
||||
args.jobLog("Using fixed bitrate. Setting video bitrate as ".concat(bitrate, "k"));
|
||||
stream.outputArgs.push("-b:".concat(ffType, ":{outputTypeIndex}"), "".concat(bitrate, "k"));
|
||||
}
|
||||
}
|
||||
});
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ var plugin = function (args) {
|
|||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
|
||||
args.inputs = lib.loadDefaultValues(args.inputs, details);
|
||||
var extensions = String(args.inputs.extensions);
|
||||
var extensionArray = extensions.trim().split(',');
|
||||
var extension = (0, fileUtils_1.getContainer)(args.inputFileObj._id);
|
||||
var extensionArray = extensions.trim().split(',').map(function (row) { return row.toLowerCase(); });
|
||||
var extension = (0, fileUtils_1.getContainer)(args.inputFileObj._id).toLowerCase();
|
||||
var extensionMatch = false;
|
||||
if (extensionArray.includes(extension)) {
|
||||
extensionMatch = true;
|
||||
|
|
|
|||
|
|
@ -150,33 +150,31 @@ var details = function () { return ({
|
|||
],
|
||||
}); };
|
||||
exports.details = details;
|
||||
var doOperation = function (_a) {
|
||||
var args = _a.args, sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, operation = _a.operation;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
args.jobLog("Input path: ".concat(sourcePath));
|
||||
args.jobLog("Output path: ".concat(destinationPath));
|
||||
if (!(sourcePath === destinationPath)) return [3 /*break*/, 1];
|
||||
args.jobLog("Input and output path are the same, skipping ".concat(operation));
|
||||
return [3 /*break*/, 3];
|
||||
case 1:
|
||||
args.deps.fsextra.ensureDirSync((0, fileUtils_1.getFileAbosluteDir)(destinationPath));
|
||||
return [4 /*yield*/, (0, fileMoveOrCopy_1.default)({
|
||||
operation: operation,
|
||||
sourcePath: sourcePath,
|
||||
destinationPath: destinationPath,
|
||||
args: args,
|
||||
})];
|
||||
case 2:
|
||||
_b.sent();
|
||||
_b.label = 3;
|
||||
case 3: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
var doOperation = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var args = _b.args, sourcePath = _b.sourcePath, destinationPath = _b.destinationPath, operation = _b.operation;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
args.jobLog("Input path: ".concat(sourcePath));
|
||||
args.jobLog("Output path: ".concat(destinationPath));
|
||||
if (!(sourcePath === destinationPath)) return [3 /*break*/, 1];
|
||||
args.jobLog("Input and output path are the same, skipping ".concat(operation));
|
||||
return [3 /*break*/, 3];
|
||||
case 1:
|
||||
args.deps.fsextra.ensureDirSync((0, fileUtils_1.getFileAbosluteDir)(destinationPath));
|
||||
return [4 /*yield*/, (0, fileMoveOrCopy_1.default)({
|
||||
operation: operation,
|
||||
sourcePath: sourcePath,
|
||||
destinationPath: destinationPath,
|
||||
args: args,
|
||||
})];
|
||||
case 2:
|
||||
_c.sent();
|
||||
_c.label = 3;
|
||||
case 3: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
};
|
||||
}); };
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
var plugin = function (args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var lib, _a, keepRelativePath, allFiles, sourceDirectory, outputDirectory, copyOrMove, fileExtensions, outputPath, subStem, sourceDir, filesInDir, i;
|
||||
|
|
|
|||
|
|
@ -69,260 +69,248 @@ var compareOldNew = function (_a) {
|
|||
+ " cache file of size ".concat(sourceFileSize));
|
||||
}
|
||||
};
|
||||
var tryMove = function (_a) {
|
||||
var sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, sourceFileSize = _a.sourceFileSize, args = _a.args;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var error, err_2, destinationSize;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
args.jobLog("Attempting move from ".concat(sourcePath, " to ").concat(destinationPath, ", method 1"));
|
||||
error = false;
|
||||
_b.label = 1;
|
||||
case 1:
|
||||
_b.trys.push([1, 3, , 4]);
|
||||
return [4 /*yield*/, fs_1.promises.rename(sourcePath, destinationPath)];
|
||||
case 2:
|
||||
_b.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
err_2 = _b.sent();
|
||||
error = true;
|
||||
args.jobLog("File move error: ".concat(JSON.stringify(err_2)));
|
||||
return [3 /*break*/, 4];
|
||||
case 4: return [4 /*yield*/, getSizeBytes(destinationPath)];
|
||||
case 5:
|
||||
destinationSize = _b.sent();
|
||||
compareOldNew({
|
||||
sourceFileSize: sourceFileSize,
|
||||
destinationSize: destinationSize,
|
||||
args: args,
|
||||
});
|
||||
if (error || destinationSize !== sourceFileSize) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
});
|
||||
var tryMove = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var error, err_2, destinationSize;
|
||||
var sourcePath = _b.sourcePath, destinationPath = _b.destinationPath, sourceFileSize = _b.sourceFileSize, args = _b.args;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
args.jobLog("Attempting move from ".concat(sourcePath, " to ").concat(destinationPath, ", method 1"));
|
||||
error = false;
|
||||
_c.label = 1;
|
||||
case 1:
|
||||
_c.trys.push([1, 3, , 4]);
|
||||
return [4 /*yield*/, fs_1.promises.rename(sourcePath, destinationPath)];
|
||||
case 2:
|
||||
_c.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
err_2 = _c.sent();
|
||||
error = true;
|
||||
args.jobLog("File move error: ".concat(JSON.stringify(err_2)));
|
||||
return [3 /*break*/, 4];
|
||||
case 4: return [4 /*yield*/, getSizeBytes(destinationPath)];
|
||||
case 5:
|
||||
destinationSize = _c.sent();
|
||||
compareOldNew({
|
||||
sourceFileSize: sourceFileSize,
|
||||
destinationSize: destinationSize,
|
||||
args: args,
|
||||
});
|
||||
if (error || destinationSize !== sourceFileSize) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
});
|
||||
};
|
||||
}); };
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
var tryMvdir = function (_a) {
|
||||
var sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, sourceFileSize = _a.sourceFileSize, args = _a.args;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var error, destinationSize;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
args.jobLog("Attempting move from ".concat(sourcePath, " to ").concat(destinationPath, ", method 2"));
|
||||
error = false;
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
// fs-extra and move-file don't work when destination is on windows root of drive
|
||||
// mvdir will try to move else fall back to copy/unlink
|
||||
// potential bug on unraid
|
||||
args.deps.mvdir(sourcePath, destinationPath, { overwrite: true })
|
||||
.then(function () {
|
||||
resolve(true);
|
||||
}).catch(function (err) {
|
||||
error = true;
|
||||
args.jobLog("File move error: ".concat(err));
|
||||
resolve(err);
|
||||
});
|
||||
})];
|
||||
case 1:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, getSizeBytes(destinationPath)];
|
||||
case 2:
|
||||
destinationSize = _b.sent();
|
||||
compareOldNew({
|
||||
sourceFileSize: sourceFileSize,
|
||||
destinationSize: destinationSize,
|
||||
args: args,
|
||||
});
|
||||
if (error || destinationSize !== sourceFileSize) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
});
|
||||
var tryMvdir = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var error, destinationSize;
|
||||
var sourcePath = _b.sourcePath, destinationPath = _b.destinationPath, sourceFileSize = _b.sourceFileSize, args = _b.args;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
args.jobLog("Attempting move from ".concat(sourcePath, " to ").concat(destinationPath, ", method 2"));
|
||||
error = false;
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
// fs-extra and move-file don't work when destination is on windows root of drive
|
||||
// mvdir will try to move else fall back to copy/unlink
|
||||
// potential bug on unraid
|
||||
args.deps.mvdir(sourcePath, destinationPath, { overwrite: true })
|
||||
.then(function () {
|
||||
resolve(true);
|
||||
}).catch(function (err) {
|
||||
error = true;
|
||||
args.jobLog("File move error: ".concat(err));
|
||||
resolve(err);
|
||||
});
|
||||
})];
|
||||
case 1:
|
||||
_c.sent();
|
||||
return [4 /*yield*/, getSizeBytes(destinationPath)];
|
||||
case 2:
|
||||
destinationSize = _c.sent();
|
||||
compareOldNew({
|
||||
sourceFileSize: sourceFileSize,
|
||||
destinationSize: destinationSize,
|
||||
args: args,
|
||||
});
|
||||
if (error || destinationSize !== sourceFileSize) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
});
|
||||
};
|
||||
}); };
|
||||
// Keep in e.g. https://github.com/HaveAGitGat/Tdarr/issues/858
|
||||
var tyNcp = function (_a) {
|
||||
var sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, sourceFileSize = _a.sourceFileSize, args = _a.args;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var error_1, destinationSize;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
if (!args.deps.ncp) return [3 /*break*/, 3];
|
||||
args.jobLog("Attempting copy from ".concat(sourcePath, " to ").concat(destinationPath, " , method 1"));
|
||||
error_1 = false;
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
args.deps.ncp(sourcePath, destinationPath, function (err) {
|
||||
if (err) {
|
||||
error_1 = true;
|
||||
args.jobLog("File copy error: ".concat(err));
|
||||
resolve(err);
|
||||
}
|
||||
else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
})];
|
||||
case 1:
|
||||
_b.sent();
|
||||
return [4 /*yield*/, getSizeBytes(destinationPath)];
|
||||
case 2:
|
||||
destinationSize = _b.sent();
|
||||
compareOldNew({
|
||||
sourceFileSize: sourceFileSize,
|
||||
destinationSize: destinationSize,
|
||||
args: args,
|
||||
});
|
||||
if (error_1 || destinationSize !== sourceFileSize) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/, true];
|
||||
case 3: return [2 /*return*/, false];
|
||||
}
|
||||
});
|
||||
var tyNcp = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var error_1, destinationSize;
|
||||
var sourcePath = _b.sourcePath, destinationPath = _b.destinationPath, sourceFileSize = _b.sourceFileSize, args = _b.args;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
if (!args.deps.ncp) return [3 /*break*/, 3];
|
||||
args.jobLog("Attempting copy from ".concat(sourcePath, " to ").concat(destinationPath, " , method 1"));
|
||||
error_1 = false;
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
args.deps.ncp(sourcePath, destinationPath, function (err) {
|
||||
if (err) {
|
||||
error_1 = true;
|
||||
args.jobLog("File copy error: ".concat(err));
|
||||
resolve(err);
|
||||
}
|
||||
else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
})];
|
||||
case 1:
|
||||
_c.sent();
|
||||
return [4 /*yield*/, getSizeBytes(destinationPath)];
|
||||
case 2:
|
||||
destinationSize = _c.sent();
|
||||
compareOldNew({
|
||||
sourceFileSize: sourceFileSize,
|
||||
destinationSize: destinationSize,
|
||||
args: args,
|
||||
});
|
||||
if (error_1 || destinationSize !== sourceFileSize) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/, true];
|
||||
case 3: return [2 /*return*/, false];
|
||||
}
|
||||
});
|
||||
};
|
||||
var tryNormalCopy = function (_a) {
|
||||
var sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, sourceFileSize = _a.sourceFileSize, args = _a.args;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var error, err_3, destinationSize;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
args.jobLog("Attempting copy from ".concat(sourcePath, " to ").concat(destinationPath, " , method 2"));
|
||||
error = false;
|
||||
_b.label = 1;
|
||||
case 1:
|
||||
_b.trys.push([1, 3, , 4]);
|
||||
return [4 /*yield*/, fs_1.promises.copyFile(sourcePath, destinationPath)];
|
||||
case 2:
|
||||
_b.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
err_3 = _b.sent();
|
||||
error = true;
|
||||
args.jobLog("File copy error: ".concat(JSON.stringify(err_3)));
|
||||
return [3 /*break*/, 4];
|
||||
case 4: return [4 /*yield*/, getSizeBytes(destinationPath)];
|
||||
case 5:
|
||||
destinationSize = _b.sent();
|
||||
compareOldNew({
|
||||
sourceFileSize: sourceFileSize,
|
||||
destinationSize: destinationSize,
|
||||
args: args,
|
||||
});
|
||||
if (error || destinationSize !== sourceFileSize) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
});
|
||||
}); };
|
||||
var tryNormalCopy = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var error, err_3, destinationSize;
|
||||
var sourcePath = _b.sourcePath, destinationPath = _b.destinationPath, sourceFileSize = _b.sourceFileSize, args = _b.args;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
args.jobLog("Attempting copy from ".concat(sourcePath, " to ").concat(destinationPath, " , method 2"));
|
||||
error = false;
|
||||
_c.label = 1;
|
||||
case 1:
|
||||
_c.trys.push([1, 3, , 4]);
|
||||
return [4 /*yield*/, fs_1.promises.copyFile(sourcePath, destinationPath)];
|
||||
case 2:
|
||||
_c.sent();
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
err_3 = _c.sent();
|
||||
error = true;
|
||||
args.jobLog("File copy error: ".concat(JSON.stringify(err_3)));
|
||||
return [3 /*break*/, 4];
|
||||
case 4: return [4 /*yield*/, getSizeBytes(destinationPath)];
|
||||
case 5:
|
||||
destinationSize = _c.sent();
|
||||
compareOldNew({
|
||||
sourceFileSize: sourceFileSize,
|
||||
destinationSize: destinationSize,
|
||||
args: args,
|
||||
});
|
||||
if (error || destinationSize !== sourceFileSize) {
|
||||
return [2 /*return*/, false];
|
||||
}
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
});
|
||||
};
|
||||
var cleanSourceFile = function (_a) {
|
||||
var args = _a.args, sourcePath = _a.sourcePath;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var err_4;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
_b.trys.push([0, 2, , 3]);
|
||||
args.jobLog("Deleting source file ".concat(sourcePath));
|
||||
return [4 /*yield*/, fs_1.promises.unlink(sourcePath)];
|
||||
case 1:
|
||||
_b.sent();
|
||||
return [3 /*break*/, 3];
|
||||
case 2:
|
||||
err_4 = _b.sent();
|
||||
args.jobLog("Failed to delete source file ".concat(sourcePath, ": ").concat(JSON.stringify(err_4)));
|
||||
return [3 /*break*/, 3];
|
||||
case 3: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
}); };
|
||||
var cleanSourceFile = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var err_4;
|
||||
var args = _b.args, sourcePath = _b.sourcePath;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
_c.trys.push([0, 2, , 3]);
|
||||
args.jobLog("Deleting source file ".concat(sourcePath));
|
||||
return [4 /*yield*/, fs_1.promises.unlink(sourcePath)];
|
||||
case 1:
|
||||
_c.sent();
|
||||
return [3 /*break*/, 3];
|
||||
case 2:
|
||||
err_4 = _c.sent();
|
||||
args.jobLog("Failed to delete source file ".concat(sourcePath, ": ").concat(JSON.stringify(err_4)));
|
||||
return [3 /*break*/, 3];
|
||||
case 3: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
};
|
||||
var fileMoveOrCopy = function (_a) {
|
||||
var operation = _a.operation, sourcePath = _a.sourcePath, destinationPath = _a.destinationPath, args = _a.args;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var sourceFileSize, moved, ncpd, copied;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
args.jobLog('Calculating cache file size in bytes');
|
||||
return [4 /*yield*/, getSizeBytes(sourcePath)];
|
||||
case 1:
|
||||
sourceFileSize = _b.sent();
|
||||
args.jobLog("".concat(sourceFileSize));
|
||||
if (!(operation === 'move')) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, tryMove({
|
||||
sourcePath: sourcePath,
|
||||
destinationPath: destinationPath,
|
||||
args: args,
|
||||
sourceFileSize: sourceFileSize,
|
||||
})];
|
||||
case 2:
|
||||
moved = _b.sent();
|
||||
if (moved) {
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
// disable: https://github.com/HaveAGitGat/Tdarr/issues/885
|
||||
// const mvdird = await tryMvdir({
|
||||
// sourcePath,
|
||||
// destinationPath,
|
||||
// args,
|
||||
// sourceFileSize,
|
||||
// });
|
||||
// if (mvdird) {
|
||||
// return true;
|
||||
// }
|
||||
args.jobLog('Failed to move file, trying copy');
|
||||
_b.label = 3;
|
||||
case 3: return [4 /*yield*/, tyNcp({
|
||||
}); };
|
||||
var fileMoveOrCopy = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var sourceFileSize, moved, ncpd, copied;
|
||||
var operation = _b.operation, sourcePath = _b.sourcePath, destinationPath = _b.destinationPath, args = _b.args;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
args.jobLog('Calculating cache file size in bytes');
|
||||
return [4 /*yield*/, getSizeBytes(sourcePath)];
|
||||
case 1:
|
||||
sourceFileSize = _c.sent();
|
||||
args.jobLog("".concat(sourceFileSize));
|
||||
if (!(operation === 'move')) return [3 /*break*/, 3];
|
||||
return [4 /*yield*/, tryMove({
|
||||
sourcePath: sourcePath,
|
||||
destinationPath: destinationPath,
|
||||
args: args,
|
||||
sourceFileSize: sourceFileSize,
|
||||
})];
|
||||
case 4:
|
||||
ncpd = _b.sent();
|
||||
if (!ncpd) return [3 /*break*/, 7];
|
||||
if (!(operation === 'move')) return [3 /*break*/, 6];
|
||||
return [4 /*yield*/, cleanSourceFile({
|
||||
args: args,
|
||||
sourcePath: sourcePath,
|
||||
})];
|
||||
case 5:
|
||||
_b.sent();
|
||||
_b.label = 6;
|
||||
case 6: return [2 /*return*/, true];
|
||||
case 7: return [4 /*yield*/, tryNormalCopy({
|
||||
sourcePath: sourcePath,
|
||||
destinationPath: destinationPath,
|
||||
case 2:
|
||||
moved = _c.sent();
|
||||
if (moved) {
|
||||
return [2 /*return*/, true];
|
||||
}
|
||||
// disable: https://github.com/HaveAGitGat/Tdarr/issues/885
|
||||
// const mvdird = await tryMvdir({
|
||||
// sourcePath,
|
||||
// destinationPath,
|
||||
// args,
|
||||
// sourceFileSize,
|
||||
// });
|
||||
// if (mvdird) {
|
||||
// return true;
|
||||
// }
|
||||
args.jobLog('Failed to move file, trying copy');
|
||||
_c.label = 3;
|
||||
case 3: return [4 /*yield*/, tyNcp({
|
||||
sourcePath: sourcePath,
|
||||
destinationPath: destinationPath,
|
||||
args: args,
|
||||
sourceFileSize: sourceFileSize,
|
||||
})];
|
||||
case 4:
|
||||
ncpd = _c.sent();
|
||||
if (!ncpd) return [3 /*break*/, 7];
|
||||
if (!(operation === 'move')) return [3 /*break*/, 6];
|
||||
return [4 /*yield*/, cleanSourceFile({
|
||||
args: args,
|
||||
sourceFileSize: sourceFileSize,
|
||||
sourcePath: sourcePath,
|
||||
})];
|
||||
case 8:
|
||||
copied = _b.sent();
|
||||
if (!copied) return [3 /*break*/, 11];
|
||||
if (!(operation === 'move')) return [3 /*break*/, 10];
|
||||
return [4 /*yield*/, cleanSourceFile({
|
||||
args: args,
|
||||
sourcePath: sourcePath,
|
||||
})];
|
||||
case 9:
|
||||
_b.sent();
|
||||
_b.label = 10;
|
||||
case 10: return [2 /*return*/, true];
|
||||
case 11: throw new Error("Failed to ".concat(operation, " file"));
|
||||
}
|
||||
});
|
||||
case 5:
|
||||
_c.sent();
|
||||
_c.label = 6;
|
||||
case 6: return [2 /*return*/, true];
|
||||
case 7: return [4 /*yield*/, tryNormalCopy({
|
||||
sourcePath: sourcePath,
|
||||
destinationPath: destinationPath,
|
||||
args: args,
|
||||
sourceFileSize: sourceFileSize,
|
||||
})];
|
||||
case 8:
|
||||
copied = _c.sent();
|
||||
if (!copied) return [3 /*break*/, 11];
|
||||
if (!(operation === 'move')) return [3 /*break*/, 10];
|
||||
return [4 /*yield*/, cleanSourceFile({
|
||||
args: args,
|
||||
sourcePath: sourcePath,
|
||||
})];
|
||||
case 9:
|
||||
_c.sent();
|
||||
_c.label = 10;
|
||||
case 10: return [2 /*return*/, true];
|
||||
case 11: throw new Error("Failed to ".concat(operation, " file"));
|
||||
}
|
||||
});
|
||||
};
|
||||
}); };
|
||||
exports.default = fileMoveOrCopy;
|
||||
|
|
|
|||
|
|
@ -80,80 +80,78 @@ var getFileSize = function (file) { return __awaiter(void 0, void 0, void 0, fun
|
|||
});
|
||||
}); };
|
||||
exports.getFileSize = getFileSize;
|
||||
var moveFileAndValidate = function (_a) {
|
||||
var inputPath = _a.inputPath, outputPath = _a.outputPath, args = _a.args;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var inputSize, res1, outputSize, err_1, res2, errMessage;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0: return [4 /*yield*/, (0, exports.getFileSize)(inputPath)];
|
||||
case 1:
|
||||
inputSize = _b.sent();
|
||||
args.jobLog("Attempt 1: Moving file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
args.deps.gracefulfs.rename(inputPath, outputPath, function (err) {
|
||||
if (err) {
|
||||
args.jobLog("Failed to move file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
args.jobLog(JSON.stringify(err));
|
||||
resolve(false);
|
||||
}
|
||||
else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
})];
|
||||
case 2:
|
||||
res1 = _b.sent();
|
||||
outputSize = 0;
|
||||
_b.label = 3;
|
||||
case 3:
|
||||
_b.trys.push([3, 5, , 6]);
|
||||
return [4 /*yield*/, (0, exports.getFileSize)(outputPath)];
|
||||
case 4:
|
||||
outputSize = _b.sent();
|
||||
return [3 /*break*/, 6];
|
||||
case 5:
|
||||
err_1 = _b.sent();
|
||||
args.jobLog(JSON.stringify(err_1));
|
||||
return [3 /*break*/, 6];
|
||||
case 6:
|
||||
if (!(!res1 || inputSize !== outputSize)) return [3 /*break*/, 9];
|
||||
var moveFileAndValidate = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var inputSize, res1, outputSize, err_1, res2, errMessage;
|
||||
var inputPath = _b.inputPath, outputPath = _b.outputPath, args = _b.args;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0: return [4 /*yield*/, (0, exports.getFileSize)(inputPath)];
|
||||
case 1:
|
||||
inputSize = _c.sent();
|
||||
args.jobLog("Attempt 1: Moving file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
args.deps.gracefulfs.rename(inputPath, outputPath, function (err) {
|
||||
if (err) {
|
||||
args.jobLog("Failed to move file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
args.jobLog(JSON.stringify(err));
|
||||
resolve(false);
|
||||
}
|
||||
else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
})];
|
||||
case 2:
|
||||
res1 = _c.sent();
|
||||
outputSize = 0;
|
||||
_c.label = 3;
|
||||
case 3:
|
||||
_c.trys.push([3, 5, , 6]);
|
||||
return [4 /*yield*/, (0, exports.getFileSize)(outputPath)];
|
||||
case 4:
|
||||
outputSize = _c.sent();
|
||||
return [3 /*break*/, 6];
|
||||
case 5:
|
||||
err_1 = _c.sent();
|
||||
args.jobLog(JSON.stringify(err_1));
|
||||
return [3 /*break*/, 6];
|
||||
case 6:
|
||||
if (!(!res1 || inputSize !== outputSize)) return [3 /*break*/, 9];
|
||||
if (inputSize !== outputSize) {
|
||||
args.jobLog("File sizes do not match, input: ".concat(inputSize, " ")
|
||||
+ "does not equal output: ".concat(outputSize));
|
||||
}
|
||||
args.jobLog("Attempt 1 failed: Moving file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
args.jobLog("Attempt 2: Moving file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
args.deps.mvdir(inputPath, outputPath, { overwrite: true })
|
||||
.then(function () {
|
||||
resolve(true);
|
||||
}).catch(function (err) {
|
||||
args.jobLog("Failed to move file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
args.jobLog(JSON.stringify(err));
|
||||
resolve(false);
|
||||
});
|
||||
})];
|
||||
case 7:
|
||||
res2 = _c.sent();
|
||||
return [4 /*yield*/, (0, exports.getFileSize)(outputPath)];
|
||||
case 8:
|
||||
outputSize = _c.sent();
|
||||
if (!res2 || inputSize !== outputSize) {
|
||||
if (inputSize !== outputSize) {
|
||||
args.jobLog("File sizes do not match, input: ".concat(inputSize, " ")
|
||||
+ "does not equal output: ".concat(outputSize));
|
||||
}
|
||||
args.jobLog("Attempt 1 failed: Moving file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
args.jobLog("Attempt 2: Moving file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
args.deps.mvdir(inputPath, outputPath, { overwrite: true })
|
||||
.then(function () {
|
||||
resolve(true);
|
||||
}).catch(function (err) {
|
||||
args.jobLog("Failed to move file from ".concat(inputPath, " to ").concat(outputPath));
|
||||
args.jobLog(JSON.stringify(err));
|
||||
resolve(false);
|
||||
});
|
||||
})];
|
||||
case 7:
|
||||
res2 = _b.sent();
|
||||
return [4 /*yield*/, (0, exports.getFileSize)(outputPath)];
|
||||
case 8:
|
||||
outputSize = _b.sent();
|
||||
if (!res2 || inputSize !== outputSize) {
|
||||
if (inputSize !== outputSize) {
|
||||
args.jobLog("File sizes do not match, input: ".concat(inputSize, " ")
|
||||
+ "does not equal output: ".concat(outputSize));
|
||||
}
|
||||
errMessage = "Failed to move file from ".concat(inputPath, " to ").concat(outputPath, ", check errors above");
|
||||
args.jobLog(errMessage);
|
||||
throw new Error(errMessage);
|
||||
}
|
||||
_b.label = 9;
|
||||
case 9: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
errMessage = "Failed to move file from ".concat(inputPath, " to ").concat(outputPath, ", check errors above");
|
||||
args.jobLog(errMessage);
|
||||
throw new Error(errMessage);
|
||||
}
|
||||
_c.label = 9;
|
||||
case 9: return [2 /*return*/];
|
||||
}
|
||||
});
|
||||
};
|
||||
}); };
|
||||
exports.moveFileAndValidate = moveFileAndValidate;
|
||||
var getPluginWorkDir = function (args) {
|
||||
var pluginWorkDir = "".concat(args.workDir, "/").concat(new Date().getTime());
|
||||
|
|
|
|||
|
|
@ -61,80 +61,78 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getEncoder = exports.getBestNvencDevice = exports.hasEncoder = void 0;
|
||||
var os_1 = __importDefault(require("os"));
|
||||
var hasEncoder = function (_a) {
|
||||
var ffmpegPath = _a.ffmpegPath, encoder = _a.encoder, inputArgs = _a.inputArgs, outputArgs = _a.outputArgs, filter = _a.filter, args = _a.args;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var spawn, isEnabled, commandArr_1, err_1;
|
||||
return __generator(this, function (_b) {
|
||||
switch (_b.label) {
|
||||
case 0:
|
||||
spawn = require('child_process').spawn;
|
||||
isEnabled = false;
|
||||
_b.label = 1;
|
||||
case 1:
|
||||
_b.trys.push([1, 3, , 4]);
|
||||
commandArr_1 = __spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], inputArgs, true), [
|
||||
'-f',
|
||||
'lavfi',
|
||||
'-i',
|
||||
'color=c=black:s=256x256:d=1:r=30'
|
||||
], false), (filter ? filter.split(' ') : []), true), [
|
||||
'-c:v',
|
||||
encoder
|
||||
], false), outputArgs, true), [
|
||||
'-f',
|
||||
'null',
|
||||
'/dev/null',
|
||||
], false);
|
||||
args.jobLog("Checking for encoder ".concat(encoder, " with command:"));
|
||||
args.jobLog("".concat(ffmpegPath, " ").concat(commandArr_1.join(' ')));
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
var error = function () {
|
||||
resolve(false);
|
||||
};
|
||||
var stderr = '';
|
||||
try {
|
||||
var thread = spawn(ffmpegPath, commandArr_1);
|
||||
thread.on('error', function () {
|
||||
// catches execution error (bad file)
|
||||
error();
|
||||
});
|
||||
thread.stdout.on('data', function (data) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
stderr += data;
|
||||
});
|
||||
thread.stderr.on('data', function (data) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
stderr += data;
|
||||
});
|
||||
thread.on('close', function (code) {
|
||||
if (code !== 0) {
|
||||
error();
|
||||
}
|
||||
else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
// catches execution error (no file)
|
||||
var hasEncoder = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var spawn, isEnabled, commandArr_1, err_1;
|
||||
var ffmpegPath = _b.ffmpegPath, encoder = _b.encoder, inputArgs = _b.inputArgs, outputArgs = _b.outputArgs, filter = _b.filter, args = _b.args;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
spawn = require('child_process').spawn;
|
||||
isEnabled = false;
|
||||
_c.label = 1;
|
||||
case 1:
|
||||
_c.trys.push([1, 3, , 4]);
|
||||
commandArr_1 = __spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray(__spreadArray([], inputArgs, true), [
|
||||
'-f',
|
||||
'lavfi',
|
||||
'-i',
|
||||
'color=c=black:s=256x256:d=1:r=30'
|
||||
], false), (filter ? filter.split(' ') : []), true), [
|
||||
'-c:v',
|
||||
encoder
|
||||
], false), outputArgs, true), [
|
||||
'-f',
|
||||
'null',
|
||||
'/dev/null',
|
||||
], false);
|
||||
args.jobLog("Checking for encoder ".concat(encoder, " with command:"));
|
||||
args.jobLog("".concat(ffmpegPath, " ").concat(commandArr_1.join(' ')));
|
||||
return [4 /*yield*/, new Promise(function (resolve) {
|
||||
var error = function () {
|
||||
resolve(false);
|
||||
};
|
||||
var stderr = '';
|
||||
try {
|
||||
var thread = spawn(ffmpegPath, commandArr_1);
|
||||
thread.on('error', function () {
|
||||
// catches execution error (bad file)
|
||||
error();
|
||||
}
|
||||
})];
|
||||
case 2:
|
||||
isEnabled = _b.sent();
|
||||
args.jobLog("Encoder ".concat(encoder, " is ").concat(isEnabled ? 'enabled' : 'disabled'));
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
err_1 = _b.sent();
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err_1);
|
||||
return [3 /*break*/, 4];
|
||||
case 4: return [2 /*return*/, isEnabled];
|
||||
}
|
||||
});
|
||||
});
|
||||
thread.stdout.on('data', function (data) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
stderr += data;
|
||||
});
|
||||
thread.stderr.on('data', function (data) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
stderr += data;
|
||||
});
|
||||
thread.on('close', function (code) {
|
||||
if (code !== 0) {
|
||||
error();
|
||||
}
|
||||
else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
// catches execution error (no file)
|
||||
error();
|
||||
}
|
||||
})];
|
||||
case 2:
|
||||
isEnabled = _c.sent();
|
||||
args.jobLog("Encoder ".concat(encoder, " is ").concat(isEnabled ? 'enabled' : 'disabled'));
|
||||
return [3 /*break*/, 4];
|
||||
case 3:
|
||||
err_1 = _c.sent();
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(err_1);
|
||||
return [3 /*break*/, 4];
|
||||
case 4: return [2 /*return*/, isEnabled];
|
||||
}
|
||||
});
|
||||
};
|
||||
}); };
|
||||
exports.hasEncoder = hasEncoder;
|
||||
// credit to UNCode101 for this
|
||||
var getBestNvencDevice = function (_a) {
|
||||
|
|
@ -207,236 +205,234 @@ var encoderFilter = function (encoder, targetCodec) {
|
|||
}
|
||||
return false;
|
||||
};
|
||||
var getEncoder = function (_a) {
|
||||
var targetCodec = _a.targetCodec, hardwareEncoding = _a.hardwareEncoding, hardwareType = _a.hardwareType, args = _a.args;
|
||||
return __awaiter(void 0, void 0, void 0, function () {
|
||||
var supportedGpuEncoders, gpuEncoders, filteredGpuEncoders, idx, _i, filteredGpuEncoders_1, gpuEncoder, _b, enabledDevices, res;
|
||||
return __generator(this, function (_c) {
|
||||
switch (_c.label) {
|
||||
case 0:
|
||||
supportedGpuEncoders = ['hevc', 'h264', 'av1'];
|
||||
if (!(args.workerType
|
||||
&& args.workerType.includes('gpu')
|
||||
&& hardwareEncoding && (supportedGpuEncoders.includes(targetCodec)))) return [3 /*break*/, 5];
|
||||
gpuEncoders = [
|
||||
{
|
||||
encoder: 'hevc_nvenc',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'cuda',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'hevc_amf',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'hevc_qsv',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'qsv',
|
||||
],
|
||||
outputArgs: __spreadArray([], (os_1.default.platform() === 'win32' ? ['-load_plugin', 'hevc_hw'] : []), true),
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'hevc_vaapi',
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'vaapi',
|
||||
'-hwaccel_device',
|
||||
'/dev/dri/renderD128',
|
||||
'-hwaccel_output_format',
|
||||
'vaapi',
|
||||
],
|
||||
outputArgs: [],
|
||||
enabled: false,
|
||||
filter: '-vf format=nv12,hwupload',
|
||||
},
|
||||
{
|
||||
encoder: 'hevc_videotoolbox',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'videotoolbox',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
// h264
|
||||
{
|
||||
encoder: 'h264_nvenc',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'cuda',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'h264_amf',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'h264_qsv',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'qsv',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'h264_videotoolbox',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'videotoolbox',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
// av1
|
||||
{
|
||||
encoder: 'av1_nvenc',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'av1_amf',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'av1_qsv',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'av1_vaapi',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
];
|
||||
filteredGpuEncoders = gpuEncoders.filter(function (device) { return encoderFilter(device.encoder, targetCodec); });
|
||||
if (hardwareEncoding && hardwareType !== 'auto') {
|
||||
idx = filteredGpuEncoders.findIndex(function (device) { return device.encoder.includes(hardwareType); });
|
||||
if (idx === -1) {
|
||||
throw new Error("Could not find encoder ".concat(targetCodec, " for hardware ").concat(hardwareType));
|
||||
}
|
||||
return [2 /*return*/, __assign(__assign({}, filteredGpuEncoders[idx]), { isGpu: true, enabledDevices: [] })];
|
||||
var getEncoder = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
|
||||
var supportedGpuEncoders, gpuEncoders, filteredGpuEncoders, idx, _i, filteredGpuEncoders_1, gpuEncoder, _c, enabledDevices, res;
|
||||
var targetCodec = _b.targetCodec, hardwareEncoding = _b.hardwareEncoding, hardwareType = _b.hardwareType, args = _b.args;
|
||||
return __generator(this, function (_d) {
|
||||
switch (_d.label) {
|
||||
case 0:
|
||||
supportedGpuEncoders = ['hevc', 'h264', 'av1'];
|
||||
if (!(args.workerType
|
||||
&& args.workerType.includes('gpu')
|
||||
&& hardwareEncoding && (supportedGpuEncoders.includes(targetCodec)))) return [3 /*break*/, 5];
|
||||
gpuEncoders = [
|
||||
{
|
||||
encoder: 'hevc_nvenc',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'cuda',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'hevc_amf',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'hevc_qsv',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'qsv',
|
||||
],
|
||||
outputArgs: __spreadArray([], (os_1.default.platform() === 'win32' ? ['-load_plugin', 'hevc_hw'] : []), true),
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'hevc_vaapi',
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'vaapi',
|
||||
'-hwaccel_device',
|
||||
'/dev/dri/renderD128',
|
||||
'-hwaccel_output_format',
|
||||
'vaapi',
|
||||
],
|
||||
outputArgs: [],
|
||||
enabled: false,
|
||||
filter: '-vf format=nv12,hwupload',
|
||||
},
|
||||
{
|
||||
encoder: 'hevc_videotoolbox',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'videotoolbox',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
// h264
|
||||
{
|
||||
encoder: 'h264_nvenc',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'cuda',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'h264_amf',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'h264_qsv',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'qsv',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'h264_videotoolbox',
|
||||
enabled: false,
|
||||
inputArgs: [
|
||||
'-hwaccel',
|
||||
'videotoolbox',
|
||||
],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
// av1
|
||||
{
|
||||
encoder: 'av1_nvenc',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'av1_amf',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'av1_qsv',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
{
|
||||
encoder: 'av1_vaapi',
|
||||
enabled: false,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
filter: '',
|
||||
},
|
||||
];
|
||||
filteredGpuEncoders = gpuEncoders.filter(function (device) { return encoderFilter(device.encoder, targetCodec); });
|
||||
if (hardwareEncoding && hardwareType !== 'auto') {
|
||||
idx = filteredGpuEncoders.findIndex(function (device) { return device.encoder.includes(hardwareType); });
|
||||
if (idx === -1) {
|
||||
throw new Error("Could not find encoder ".concat(targetCodec, " for hardware ").concat(hardwareType));
|
||||
}
|
||||
args.jobLog(JSON.stringify({ filteredGpuEncoders: filteredGpuEncoders }));
|
||||
_i = 0, filteredGpuEncoders_1 = filteredGpuEncoders;
|
||||
_c.label = 1;
|
||||
case 1:
|
||||
if (!(_i < filteredGpuEncoders_1.length)) return [3 /*break*/, 4];
|
||||
gpuEncoder = filteredGpuEncoders_1[_i];
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
_b = gpuEncoder;
|
||||
return [4 /*yield*/, (0, exports.hasEncoder)({
|
||||
ffmpegPath: args.ffmpegPath,
|
||||
encoder: gpuEncoder.encoder,
|
||||
inputArgs: gpuEncoder.inputArgs,
|
||||
outputArgs: gpuEncoder.outputArgs,
|
||||
filter: gpuEncoder.filter,
|
||||
return [2 /*return*/, __assign(__assign({}, filteredGpuEncoders[idx]), { isGpu: true, enabledDevices: [] })];
|
||||
}
|
||||
args.jobLog(JSON.stringify({ filteredGpuEncoders: filteredGpuEncoders }));
|
||||
_i = 0, filteredGpuEncoders_1 = filteredGpuEncoders;
|
||||
_d.label = 1;
|
||||
case 1:
|
||||
if (!(_i < filteredGpuEncoders_1.length)) return [3 /*break*/, 4];
|
||||
gpuEncoder = filteredGpuEncoders_1[_i];
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
_c = gpuEncoder;
|
||||
return [4 /*yield*/, (0, exports.hasEncoder)({
|
||||
ffmpegPath: args.ffmpegPath,
|
||||
encoder: gpuEncoder.encoder,
|
||||
inputArgs: gpuEncoder.inputArgs,
|
||||
outputArgs: gpuEncoder.outputArgs,
|
||||
filter: gpuEncoder.filter,
|
||||
args: args,
|
||||
})];
|
||||
case 2:
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
_c.enabled = _d.sent();
|
||||
_d.label = 3;
|
||||
case 3:
|
||||
_i++;
|
||||
return [3 /*break*/, 1];
|
||||
case 4:
|
||||
enabledDevices = filteredGpuEncoders.filter(function (device) { return device.enabled === true; });
|
||||
args.jobLog(JSON.stringify({ enabledDevices: enabledDevices }));
|
||||
if (enabledDevices.length > 0) {
|
||||
if (enabledDevices[0].encoder.includes('nvenc')) {
|
||||
res = (0, exports.getBestNvencDevice)({
|
||||
args: args,
|
||||
})];
|
||||
case 2:
|
||||
// eslint-disable-next-line no-await-in-loop
|
||||
_b.enabled = _c.sent();
|
||||
_c.label = 3;
|
||||
case 3:
|
||||
_i++;
|
||||
return [3 /*break*/, 1];
|
||||
case 4:
|
||||
enabledDevices = filteredGpuEncoders.filter(function (device) { return device.enabled === true; });
|
||||
args.jobLog(JSON.stringify({ enabledDevices: enabledDevices }));
|
||||
if (enabledDevices.length > 0) {
|
||||
if (enabledDevices[0].encoder.includes('nvenc')) {
|
||||
res = (0, exports.getBestNvencDevice)({
|
||||
args: args,
|
||||
nvencDevice: enabledDevices[0],
|
||||
});
|
||||
return [2 /*return*/, __assign(__assign({}, res), { isGpu: true, enabledDevices: enabledDevices })];
|
||||
}
|
||||
return [2 /*return*/, {
|
||||
encoder: enabledDevices[0].encoder,
|
||||
inputArgs: enabledDevices[0].inputArgs,
|
||||
outputArgs: enabledDevices[0].outputArgs,
|
||||
isGpu: true,
|
||||
enabledDevices: enabledDevices,
|
||||
}];
|
||||
}
|
||||
return [3 /*break*/, 6];
|
||||
case 5:
|
||||
if (!hardwareEncoding) {
|
||||
args.jobLog('Hardware encoding is disabled in plugin input options');
|
||||
}
|
||||
if (!args.workerType || !args.workerType.includes('gpu')) {
|
||||
args.jobLog('Worker type is not GPU');
|
||||
}
|
||||
if (!supportedGpuEncoders.includes(targetCodec)) {
|
||||
args.jobLog("Target codec ".concat(targetCodec, " is not supported for GPU encoding"));
|
||||
}
|
||||
_c.label = 6;
|
||||
case 6:
|
||||
if (targetCodec === 'hevc') {
|
||||
return [2 /*return*/, {
|
||||
encoder: 'libx265',
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
isGpu: false,
|
||||
enabledDevices: [],
|
||||
}];
|
||||
}
|
||||
if (targetCodec === 'h264') {
|
||||
return [2 /*return*/, {
|
||||
encoder: 'libx264',
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
isGpu: false,
|
||||
enabledDevices: [],
|
||||
}];
|
||||
}
|
||||
if (targetCodec === 'av1') {
|
||||
return [2 /*return*/, {
|
||||
encoder: 'libsvtav1',
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
isGpu: false,
|
||||
enabledDevices: [],
|
||||
}];
|
||||
nvencDevice: enabledDevices[0],
|
||||
});
|
||||
return [2 /*return*/, __assign(__assign({}, res), { isGpu: true, enabledDevices: enabledDevices })];
|
||||
}
|
||||
return [2 /*return*/, {
|
||||
encoder: targetCodec,
|
||||
encoder: enabledDevices[0].encoder,
|
||||
inputArgs: enabledDevices[0].inputArgs,
|
||||
outputArgs: enabledDevices[0].outputArgs,
|
||||
isGpu: true,
|
||||
enabledDevices: enabledDevices,
|
||||
}];
|
||||
}
|
||||
return [3 /*break*/, 6];
|
||||
case 5:
|
||||
if (!hardwareEncoding) {
|
||||
args.jobLog('Hardware encoding is disabled in plugin input options');
|
||||
}
|
||||
if (!args.workerType || !args.workerType.includes('gpu')) {
|
||||
args.jobLog('Worker type is not GPU');
|
||||
}
|
||||
if (!supportedGpuEncoders.includes(targetCodec)) {
|
||||
args.jobLog("Target codec ".concat(targetCodec, " is not supported for GPU encoding"));
|
||||
}
|
||||
_d.label = 6;
|
||||
case 6:
|
||||
if (targetCodec === 'hevc') {
|
||||
return [2 /*return*/, {
|
||||
encoder: 'libx265',
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
isGpu: false,
|
||||
enabledDevices: [],
|
||||
}];
|
||||
}
|
||||
});
|
||||
}
|
||||
if (targetCodec === 'h264') {
|
||||
return [2 /*return*/, {
|
||||
encoder: 'libx264',
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
isGpu: false,
|
||||
enabledDevices: [],
|
||||
}];
|
||||
}
|
||||
if (targetCodec === 'av1') {
|
||||
return [2 /*return*/, {
|
||||
encoder: 'libsvtav1',
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
isGpu: false,
|
||||
enabledDevices: [],
|
||||
}];
|
||||
}
|
||||
return [2 /*return*/, {
|
||||
encoder: targetCodec,
|
||||
inputArgs: [],
|
||||
outputArgs: [],
|
||||
isGpu: false,
|
||||
enabledDevices: [],
|
||||
}];
|
||||
}
|
||||
});
|
||||
};
|
||||
}); };
|
||||
exports.getEncoder = getEncoder;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import {
|
|||
import { getFfType } from '../../../../FlowHelpers/1.0.0/fileUtils';
|
||||
|
||||
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
|
||||
const details = () :IpluginDetails => ({
|
||||
const details = (): IpluginDetails => ({
|
||||
name: 'Set Video Bitrate',
|
||||
description: 'Set Video Bitrate',
|
||||
style: {
|
||||
|
|
@ -19,6 +19,67 @@ const details = () :IpluginDetails => ({
|
|||
sidebarPosition: -1,
|
||||
icon: '',
|
||||
inputs: [
|
||||
{
|
||||
label: 'Use % of Input Bitrate',
|
||||
name: 'useInputBitrate',
|
||||
type: 'boolean',
|
||||
defaultValue: 'false',
|
||||
inputUI: {
|
||||
type: 'switch',
|
||||
},
|
||||
tooltip: 'Specify whether to use a % of input bitrate as the output bitrate',
|
||||
},
|
||||
|
||||
{
|
||||
label: 'Target Bitrate %',
|
||||
name: 'targetBitratePercent',
|
||||
type: 'string',
|
||||
defaultValue: '50',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
displayConditions: {
|
||||
logic: 'AND',
|
||||
sets: [
|
||||
{
|
||||
logic: 'AND',
|
||||
inputs: [
|
||||
{
|
||||
name: 'useInputBitrate',
|
||||
value: 'true',
|
||||
condition: '===',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: 'Specify the target bitrate as a % of the input bitrate',
|
||||
},
|
||||
{
|
||||
label: 'Fallback Bitrate',
|
||||
name: 'fallbackBitrate',
|
||||
type: 'string',
|
||||
defaultValue: '4000',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
displayConditions: {
|
||||
logic: 'AND',
|
||||
sets: [
|
||||
{
|
||||
logic: 'AND',
|
||||
inputs: [
|
||||
{
|
||||
name: 'useInputBitrate',
|
||||
value: 'true',
|
||||
condition: '===',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: 'Specify fallback bitrate in kbps if input bitrate is not available',
|
||||
},
|
||||
{
|
||||
label: 'Bitrate',
|
||||
name: 'bitrate',
|
||||
|
|
@ -26,6 +87,21 @@ const details = () :IpluginDetails => ({
|
|||
defaultValue: '5000',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
displayConditions: {
|
||||
logic: 'AND',
|
||||
sets: [
|
||||
{
|
||||
logic: 'AND',
|
||||
inputs: [
|
||||
{
|
||||
name: 'useInputBitrate',
|
||||
value: 'true',
|
||||
condition: '!==',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
tooltip: 'Specify bitrate in kbps',
|
||||
},
|
||||
|
|
@ -39,15 +115,40 @@ const details = () :IpluginDetails => ({
|
|||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const plugin = (args:IpluginInputArgs):IpluginOutputArgs => {
|
||||
const plugin = (args: IpluginInputArgs): IpluginOutputArgs => {
|
||||
const lib = require('../../../../../methods/lib')();
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign
|
||||
args.inputs = lib.loadDefaultValues(args.inputs, details);
|
||||
|
||||
const { useInputBitrate } = args.inputs;
|
||||
const targetBitratePercent = String(args.inputs.targetBitratePercent);
|
||||
const fallbackBitrate = String(args.inputs.fallbackBitrate);
|
||||
const bitrate = String(args.inputs.bitrate);
|
||||
|
||||
args.variables.ffmpegCommand.streams.forEach((stream) => {
|
||||
if (stream.codec_type === 'video') {
|
||||
const ffType = getFfType(stream.codec_type);
|
||||
stream.outputArgs.push(`-b:${ffType}:{outputTypeIndex}`, `${String(args.inputs.bitrate)}k`);
|
||||
if (useInputBitrate) {
|
||||
args.jobLog('Attempting to use % of input bitrate as output bitrate');
|
||||
// check if input bitrate is available
|
||||
const mediainfoIndex = stream.index + 1;
|
||||
|
||||
let inputBitrate = args?.inputFileObj?.mediaInfo?.track?.[mediainfoIndex]?.BitRate;
|
||||
if (inputBitrate) {
|
||||
args.jobLog(`Found input bitrate: ${inputBitrate}`);
|
||||
// @ts-expect-error type
|
||||
inputBitrate = parseInt(inputBitrate, 10) / 1000;
|
||||
const targetBitrate = (inputBitrate * (parseInt(targetBitratePercent, 10) / 100));
|
||||
args.jobLog(`Setting video bitrate as ${targetBitrate}k`);
|
||||
stream.outputArgs.push(`-b:${ffType}:{outputTypeIndex}`, `${targetBitrate}k`);
|
||||
} else {
|
||||
args.jobLog(`Unable to find input bitrate, setting fallback bitrate as ${fallbackBitrate}k`);
|
||||
stream.outputArgs.push(`-b:${ffType}:{outputTypeIndex}`, `${fallbackBitrate}k`);
|
||||
}
|
||||
} else {
|
||||
args.jobLog(`Using fixed bitrate. Setting video bitrate as ${bitrate}k`);
|
||||
stream.outputArgs.push(`-b:${ffType}:{outputTypeIndex}`, `${bitrate}k`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -49,9 +49,9 @@ const plugin = (args: IpluginInputArgs): IpluginOutputArgs => {
|
|||
args.inputs = lib.loadDefaultValues(args.inputs, details);
|
||||
|
||||
const extensions = String(args.inputs.extensions);
|
||||
const extensionArray = extensions.trim().split(',');
|
||||
const extensionArray = extensions.trim().split(',').map((row) => row.toLowerCase());
|
||||
|
||||
const extension = getContainer(args.inputFileObj._id);
|
||||
const extension = getContainer(args.inputFileObj._id).toLowerCase();
|
||||
|
||||
let extensionMatch = false;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ const _ = require('lodash');
|
|||
const run = require('../helpers/run');
|
||||
|
||||
const tests = [
|
||||
// Test 0
|
||||
{
|
||||
input: {
|
||||
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
|
||||
|
|
@ -13,58 +14,55 @@ const tests = [
|
|||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv \n'
|
||||
+ ' -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset slow -c:a copy -c:s copy -max_muxing_queue_size 9999 ',
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset slow -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -vf hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: 'Input file is not MKV so cannot use mkvpropedit to get new file stats. Continuing but file stats will likely be inaccurate...\n'
|
||||
+ '☑ It looks like the current video bitrate is 1206kbps. \n'
|
||||
+ 'Container for output selected as mkv. \n'
|
||||
+ 'Encode variable bitrate settings: \n'
|
||||
+ 'Target = 603k \n'
|
||||
+ 'Minimum = 452k \n'
|
||||
+ 'Maximum = 754k \n'
|
||||
+ 'File Transcoding... \n',
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv \n'
|
||||
+ ' -init_hw_device qsv:hw_any,child_device_type=d3d11va -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -load_plugin hevc_hw -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset slow -c:a copy -c:s copy -max_muxing_queue_size 9999 ',
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset slow -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -vf hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: 'Input file is not MKV so cannot use mkvpropedit to get new file stats. Continuing but file stats will likely be inaccurate...\n'
|
||||
+ '☑ It looks like the current video bitrate is 1206kbps. \n'
|
||||
+ 'Container for output selected as mkv. \n'
|
||||
+ 'Encode variable bitrate settings: \n'
|
||||
+ 'Target = 603k \n'
|
||||
+ 'Minimum = 452k \n'
|
||||
+ 'Maximum = 754k \n'
|
||||
+ 'File Transcoding... \n',
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset slow -c:a copy -c:s copy -max_muxing_queue_size 9999 ',
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset slow -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: 'Input file is not MKV so cannot use mkvpropedit to get new file stats. Continuing but file stats will likely be inaccurate...\n'
|
||||
+ '☑ It looks like the current video bitrate is 1206kbps. \n'
|
||||
+ 'Container for output selected as mkv. \n'
|
||||
+ 'Encode variable bitrate settings: \n'
|
||||
+ 'Target = 603k \n'
|
||||
+ 'Minimum = 452k \n'
|
||||
+ 'Maximum = 754k \n'
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding... \n',
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test 1
|
||||
{
|
||||
input: {
|
||||
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
|
||||
|
|
@ -79,70 +77,222 @@ const tests = [
|
|||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv \n'
|
||||
+ ' -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -vf scale_qsv=format=p010le ',
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f mp4 -profile:v main10 -vf scale_qsv=format=p010le,hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: 'Input file is not MKV so cannot use mkvpropedit to get new file stats. Continuing but file stats will likely be inaccurate...\n'
|
||||
+ '☑ It looks like the current video bitrate is 1206kbps. \n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n'
|
||||
+ 'Container for output selected as mp4. \n'
|
||||
+ 'Encode variable bitrate settings: \n'
|
||||
+ 'Target = 603k \n'
|
||||
+ 'Minimum = 452k \n'
|
||||
+ 'Maximum = 754k \n'
|
||||
+ 'File Transcoding... \n',
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mp4.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mp4',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv \n'
|
||||
+ ' -init_hw_device qsv:hw_any,child_device_type=d3d11va -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -load_plugin hevc_hw -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -vf scale_qsv=format=p010le ',
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f mp4 -profile:v main10 -vf scale_qsv=format=p010le,hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: 'Input file is not MKV so cannot use mkvpropedit to get new file stats. Continuing but file stats will likely be inaccurate...\n'
|
||||
+ '☑ It looks like the current video bitrate is 1206kbps. \n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n'
|
||||
+ 'Container for output selected as mp4. \n'
|
||||
+ 'Encode variable bitrate settings: \n'
|
||||
+ 'Target = 603k \n'
|
||||
+ 'Minimum = 452k \n'
|
||||
+ 'Maximum = 754k \n'
|
||||
+ 'File Transcoding... \n',
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mp4.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mp4',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -vf scale_qsv=format=p010le ',
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f mp4 -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: 'Input file is not MKV so cannot use mkvpropedit to get new file stats. Continuing but file stats will likely be inaccurate...\n'
|
||||
+ '☑ It looks like the current video bitrate is 1206kbps. \n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n'
|
||||
+ 'Container for output selected as mp4. \n'
|
||||
+ 'Encode variable bitrate settings: \n'
|
||||
+ 'Target = 603k \n'
|
||||
+ 'Minimum = 452k \n'
|
||||
+ 'Maximum = 754k \n'
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mp4.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding... \n',
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mp4',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test 2
|
||||
{
|
||||
input: {
|
||||
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
|
||||
file: (() => {
|
||||
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
|
||||
file.ffProbeData.streams[0].profile = 'High 10';
|
||||
return file;
|
||||
})(),
|
||||
librarySettings: {},
|
||||
inputs: {
|
||||
container: 'mp4',
|
||||
container: 'mkv',
|
||||
encoder_speedpreset: 'fast',
|
||||
enable_10bit: 'true',
|
||||
bitrate_cutoff: '2000',
|
||||
},
|
||||
otherArguments: {},
|
||||
},
|
||||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264<io> -map 0 -c:v hevc_qsv -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v main10 -pix_fmt p010le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ 'Input file is h264 High10. Hardware Decode not supported.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v h264<io> -map 0 -c:v hevc_qsv -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v main10 -pix_fmt p010le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ 'Input file is h264 High10. Hardware Decode not supported.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ 'Input file is h264 High10. Hardware Decode not supported.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test 3
|
||||
{
|
||||
input: {
|
||||
file: (() => {
|
||||
const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json'));
|
||||
file.mediaInfo.track[1].BitRate = 12000000;
|
||||
file.ffProbeData.streams[0].profile = 'Main 10';
|
||||
return file;
|
||||
})(),
|
||||
librarySettings: {},
|
||||
inputs: {
|
||||
container: 'mkv',
|
||||
encoder_speedpreset: 'fast',
|
||||
reconvert_hevc: 'true',
|
||||
hevc_max_bitrate: '6000',
|
||||
bitrate_cutoff: '4000',
|
||||
},
|
||||
otherArguments: {},
|
||||
},
|
||||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v hevc_qsv<io> -map 0 -c:v hevc_qsv -b:v 6000k -minrate 4500k -maxrate 7500k -bufsize 12000k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v main10 -vf scale_qsv=format=p010le,hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 12000kbps.\n'
|
||||
+ 'Reconvert_hevc is true & the file is already HEVC, VP9 or AV1. Using HEVC specific cutoff of 6000kbps.\n'
|
||||
+ '☒ The file is still above this new cutoff! Reconverting.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 6000k\n'
|
||||
+ 'Minimum = 4500k\n'
|
||||
+ 'Maximum = 7500k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v hevc_qsv<io> -map 0 -c:v hevc_qsv -b:v 6000k -minrate 4500k -maxrate 7500k -bufsize 12000k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v main10 -vf scale_qsv=format=p010le,hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 12000kbps.\n'
|
||||
+ 'Reconvert_hevc is true & the file is already HEVC, VP9 or AV1. Using HEVC specific cutoff of 6000kbps.\n'
|
||||
+ '☒ The file is still above this new cutoff! Reconverting.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 6000k\n'
|
||||
+ 'Minimum = 4500k\n'
|
||||
+ 'Maximum = 7500k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 6000k -minrate 4500k -maxrate 7500k -bufsize 12000k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 12000kbps.\n'
|
||||
+ 'Reconvert_hevc is true & the file is already HEVC, VP9 or AV1. Using HEVC specific cutoff of 6000kbps.\n'
|
||||
+ '☒ The file is still above this new cutoff! Reconverting.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 6000k\n'
|
||||
+ 'Minimum = 4500k\n'
|
||||
+ 'Maximum = 7500k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test 4
|
||||
{
|
||||
input: {
|
||||
file: (() => {
|
||||
const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json'));
|
||||
file.mediaInfo.track[1].BitRate = 5000000;
|
||||
file.ffProbeData.streams[0].profile = 'Main 10';
|
||||
file.mediaInfo.track[0].extra.JBDONEDATE = new Date().toISOString();
|
||||
return file;
|
||||
})(),
|
||||
librarySettings: {},
|
||||
inputs: {
|
||||
container: 'mkv',
|
||||
encoder_speedpreset: 'fast',
|
||||
reconvert_hevc: 'true',
|
||||
hevc_max_bitrate: '6000',
|
||||
bitrate_cutoff: '4000',
|
||||
},
|
||||
otherArguments: {},
|
||||
},
|
||||
|
|
@ -152,13 +302,366 @@ const tests = [
|
|||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: 'Input file is not MKV so cannot use mkvpropedit to get new file stats. Continuing but file stats will likely be inaccurate...\n'
|
||||
+ '☑ It looks like the current video bitrate is 1206kbps. \n'
|
||||
+ '☑ Current bitrate is below set cutoff of 2000kbps. \n'
|
||||
+ 'Cancelling plugin. \n',
|
||||
container: '.mp4',
|
||||
infoLog: '☑ It looks like the current video bitrate is 5000kbps.\n'
|
||||
+ 'Reconvert_hevc is true & the file is already HEVC, VP9 or AV1. Using HEVC specific cutoff of 6000kbps.\n'
|
||||
+ '☑ The file is NOT above this new cutoff. Exiting plugin.\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
},
|
||||
// Test 5
|
||||
{
|
||||
input: {
|
||||
file: (() => {
|
||||
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
|
||||
file.ffProbeData.streams[3].codec_name = 'hdmv_pgs_subtitle';
|
||||
file.ffProbeData.streams[4].codec_name = 'eia_608';
|
||||
file.ffProbeData.streams[5].codec_name = 'subrip';
|
||||
file.ffProbeData.streams[6].codec_name = 'timed_id3';
|
||||
return file;
|
||||
})(),
|
||||
librarySettings: {},
|
||||
inputs: {
|
||||
container: 'mp4',
|
||||
encoder_speedpreset: 'fast',
|
||||
force_conform: 'true',
|
||||
},
|
||||
otherArguments: {},
|
||||
},
|
||||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -map -0:3 -map -0:4 -map -0:5 -map -0:6 -f mp4 -vf hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ 'Container for output selected as mp4.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mp4',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -map -0:3 -map -0:4 -map -0:5 -map -0:6 -f mp4 -vf hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ 'Container for output selected as mp4.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mp4',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -map -0:3 -map -0:4 -map -0:5 -map -0:6 -f mp4 -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mp4.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mp4',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test 6
|
||||
{
|
||||
input: {
|
||||
file: (() => {
|
||||
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
|
||||
file.ffProbeData.streams[3].codec_name = 'mov_text';
|
||||
file.ffProbeData.streams[4].codec_name = 'eia_608';
|
||||
file.ffProbeData.streams[5].codec_name = 'timed_id3';
|
||||
return file;
|
||||
})(),
|
||||
librarySettings: {},
|
||||
inputs: {
|
||||
container: 'mkv',
|
||||
encoder_speedpreset: 'fast',
|
||||
force_conform: 'true',
|
||||
},
|
||||
otherArguments: {},
|
||||
},
|
||||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -map -0:d -map -0:3 -map -0:4 -map -0:5 -f matroska -vf hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -map -0:d -map -0:3 -map -0:4 -map -0:5 -f matroska -vf hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 3227k -minrate 2420k -maxrate 4034k -bufsize 6454k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -map -0:d -map -0:3 -map -0:4 -map -0:5 -f matroska -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 6454kbps.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 3227k\n'
|
||||
+ 'Minimum = 2420k\n'
|
||||
+ 'Maximum = 4034k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test 7
|
||||
{
|
||||
input: {
|
||||
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
|
||||
librarySettings: {},
|
||||
inputs: {
|
||||
container: 'mkv',
|
||||
encoder_speedpreset: 'fast',
|
||||
extra_qsv_options: '-look_ahead 1 -look_ahead_depth 100 -extbrc 1 -rdo 1 -mbbrc 1 -b_strategy 1 -adaptive_i 1 -adaptive_b 1 -vf scale_qsv=1280:-1',
|
||||
enable_10bit: 'true',
|
||||
},
|
||||
otherArguments: {},
|
||||
},
|
||||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -look_ahead 1 -look_ahead_depth 100 -extbrc 1 -rdo 1 -mbbrc 1 -b_strategy 1 -adaptive_i 1 -adaptive_b 1 -vf scale_qsv=1280:-1,format=p010le,hwupload=extra_hw_frames=64,format=qsv -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v main10',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -look_ahead 1 -look_ahead_depth 100 -extbrc 1 -rdo 1 -mbbrc 1 -b_strategy 1 -adaptive_i 1 -adaptive_b 1 -vf scale_qsv=1280:-1,format=p010le,hwupload=extra_hw_frames=64,format=qsv -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v main10',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test 8
|
||||
{
|
||||
input: {
|
||||
file: (() => {
|
||||
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
|
||||
file.ffProbeData.streams[0].bits_per_raw_sample = '10';
|
||||
file.video_codec_name = 'vc1';
|
||||
return file;
|
||||
})(),
|
||||
librarySettings: {},
|
||||
inputs: {
|
||||
container: 'mkv',
|
||||
encoder_speedpreset: 'fast',
|
||||
},
|
||||
otherArguments: {},
|
||||
},
|
||||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v vc1<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v main10 -pix_fmt p010le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ 'Input file is vc1. Hardware Decode not supported.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v vc1<io> -map 0 -c:v hevc_qsv -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v main10 -pix_fmt p010le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ 'Input file is vc1. Hardware Decode not supported.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 603k -minrate 452k -maxrate 754k -bufsize 1206k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -f matroska -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 1206kbps.\n'
|
||||
+ 'Input file is vc1. Hardware Decode not supported.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 603k\n'
|
||||
+ 'Minimum = 452k\n'
|
||||
+ 'Maximum = 754k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
},
|
||||
},
|
||||
// Test 9
|
||||
{
|
||||
input: {
|
||||
file: (() => {
|
||||
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
|
||||
file.ffProbeData.streams[0].color_space = 'bt2020nc';
|
||||
file.ffProbeData.streams[0].color_transfer = 'smpte2084';
|
||||
file.ffProbeData.streams[0].color_primaries = 'bt2020';
|
||||
file.mediaInfo.track[1].BitRate = 12000000;
|
||||
file.ffProbeData.streams[0].profile = 'Main 10';
|
||||
return file;
|
||||
})(),
|
||||
librarySettings: {},
|
||||
inputs: {
|
||||
container: 'mkv',
|
||||
encoder_speedpreset: 'fast',
|
||||
reconvert_hevc: 'true',
|
||||
hevc_max_bitrate: '6000',
|
||||
bitrate_cutoff: '4000',
|
||||
},
|
||||
otherArguments: {},
|
||||
},
|
||||
output: {
|
||||
linux: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw_any,child_device_type=vaapi -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 6000k -minrate 4500k -maxrate 7500k -bufsize 12000k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -color_primaries bt2020 -color_trc smpte2084 -colorspace bt2020nc -f matroska -profile:v main10 -vf scale_qsv=format=p010le,hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 12000kbps.\n'
|
||||
+ '==WARNING== This looks to be a HDR file. HDR is supported but correct encoding is not guaranteed.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 6000k\n'
|
||||
+ 'Minimum = 4500k\n'
|
||||
+ 'Maximum = 7500k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
win32: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel qsv -hwaccel_output_format qsv -init_hw_device qsv:hw,child_device_type=d3d11va -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 6000k -minrate 4500k -maxrate 7500k -bufsize 12000k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -color_primaries bt2020 -color_trc smpte2084 -colorspace bt2020nc -f matroska -profile:v main10 -vf scale_qsv=format=p010le,hwupload=extra_hw_frames=64,format=qsv ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 12000kbps.\n'
|
||||
+ '==WARNING== This looks to be a HDR file. HDR is supported but correct encoding is not guaranteed.\n'
|
||||
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 6000k\n'
|
||||
+ 'Minimum = 4500k\n'
|
||||
+ 'Maximum = 7500k\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
darwin: {
|
||||
processFile: true,
|
||||
preset: '-fflags +genpts -hwaccel videotoolbox<io> -map 0 -c:v hevc_videotoolbox -b:v 6000k -minrate 4500k -maxrate 7500k -bufsize 12000k -preset fast -c:a copy -c:s copy -max_muxing_queue_size 9999 -color_primaries bt2020 -color_trc smpte2084 -colorspace bt2020nc -f matroska -profile:v 2 -pix_fmt yuv420p10le ',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '☑ It looks like the current video bitrate is 12000kbps.\n'
|
||||
+ '==WARNING== This looks to be a HDR file. HDR is supported but correct encoding is not guaranteed.\n'
|
||||
+ '10 bit encode enabled. Setting VideoToolBox Profile v2 & 10 bit pixel format\n'
|
||||
+ 'Container for output selected as mkv.\n'
|
||||
+ 'Encode variable bitrate settings:\n'
|
||||
+ 'Target = 6000k\n'
|
||||
+ 'Minimum = 4500k\n'
|
||||
+ 'Maximum = 7500k\n'
|
||||
+ '==ALERT== OS detected as MAC - This will use VIDEOTOOLBOX to encode which is NOT QSV\n'
|
||||
+ 'cmds set in extra_qsv_options will be IGNORED!\n'
|
||||
+ 'File Transcoding...\n',
|
||||
container: '.mkv',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
void run(tests);
|
||||
|
|
|
|||
|
|
@ -16,8 +16,10 @@ const stackLog = (err) => {
|
|||
};
|
||||
|
||||
const run = async (tests) => {
|
||||
try {
|
||||
for (let i = 0; i < tests.length; i += 1) {
|
||||
let errorsEncountered = false;
|
||||
|
||||
for (let i = 0; i < tests.length; i += 1) {
|
||||
try {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`[${os.platform()}] ${scriptName}: test ${i}`);
|
||||
const test = tests[i];
|
||||
|
|
@ -69,10 +71,14 @@ const run = async (tests) => {
|
|||
chai.assert.deepEqual(testOutput, expectedOutput);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
stackLog(err);
|
||||
errorsEncountered = true;
|
||||
}
|
||||
} catch (err) {
|
||||
// eslint-disable-next-line no-console
|
||||
stackLog(err);
|
||||
}
|
||||
|
||||
if (errorsEncountered) {
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue