Merge remote-tracking branch 'origin/master' into pr/285

make-only-subtitle-default
HaveAGitGat 4 years ago
commit e3d27da87f

@ -1,23 +0,0 @@
name: Node.js CI
on:
pull_request:
branches: ['**']
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm i
- run: npm run checkPlugins && npm run lint

@ -0,0 +1,34 @@
name: Node.js CI
on:
pull_request:
branches: ['**']
jobs:
build:
strategy:
matrix:
node-version: [16.x]
os:
[
["ubuntu-20.04"],
["windows-2019"],
["macos-11.0"],
]
runs-on: ${{ matrix.os }}
steps:
- name: Set git to use LF
run: |
git config --global core.autocrlf false
git config --global core.eol lf
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm i
- run: npm run checkPlugins
- run: npm run lint
- run: npm run test

@ -84,8 +84,8 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
processFile: false,
preset: '',
container: '',
handBrakeMode: false,
FFmpegMode: false,
handbrakeMode: false,
ffmpegMode: false,
reQueueAfter: false,
infoLog: '',
};

@ -21,6 +21,8 @@ const details = () => ({
tooltip:
`Specify the process order.
For example, if 'languages' is first, the streams will be ordered based on that first.
So put the most important properties last.
The default order is suitable for most people.
\\nExample:\\n
codecs,channels,languages,streamTypes
@ -95,7 +97,12 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
infoLog: '',
};
let { streams } = file.ffProbeData;
if (!Array.isArray(file.ffProbeData.streams)) {
throw new Error('FFprobe was unable to extract any streams info on this file.'
+ 'This may be due to a corrupt file or permissions issue when scanning the file.');
}
let { streams } = JSON.parse(JSON.stringify(file.ffProbeData));
streams.forEach((stream, index) => {
// eslint-disable-next-line no-param-reassign
@ -110,7 +117,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
for (let i = 0; i < items.length; i += 1) {
const matchedStreams = [];
for (let j = 0; j < streams.length; j += 1) {
if (String(sortType.getValue(streams[j])).includes(String(items[i]))) {
if (String(sortType.getValue(streams[j])) === String(items[i])) {
if (
streams[j].codec_long_name
&& (

@ -0,0 +1,86 @@
const details = () => ({
id: 'Tdarr_Plugin_00td_filter_by_codec_tag_string',
Stage: 'Pre-processing',
Name: 'Filter by codec tag string',
Type: 'Video',
Operation: 'Filter',
Description: 'Only allow files with specified codec tag strings to be processed \n\n',
Version: '1.00',
Tags: 'filter',
Inputs: [
{
name: 'codecTagStringsToProcess',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip: `
Enter a comma separated list of codec tag strings to be processed.
Leave blank if using codecTagStringsToNotProcess`,
},
{
name: 'codecTagStringsToNotProcess',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip: `
Enter a comma separated list of codec tag strings to be not be processed.
Leave blank if using codecTagStringsToProcess`,
},
],
});
// eslint-disable-next-line no-unused-vars
const plugin = (file, librarySettings, inputs, otherArguments) => {
const lib = require('../methods/lib')();
// eslint-disable-next-line no-unused-vars,no-param-reassign
inputs = lib.loadDefaultValues(inputs, details);
const response = {
processFile: false,
infoLog: '',
};
let fileCodecTagStrings = [];
if (file.ffProbeData && file.ffProbeData.streams) {
fileCodecTagStrings = file.ffProbeData.streams.map((row) => row.codec_tag_string);
}
const checkInclude = (inputArr) => {
for (let i = 0; i < fileCodecTagStrings.length; i += 1) {
if (inputArr.includes(fileCodecTagStrings[i])) {
return true;
}
}
return false;
};
if (inputs.codecTagStringsToProcess !== '') {
const codecTagStrings = inputs.codecTagStringsToProcess.split(',');
if (checkInclude(codecTagStrings)) {
response.processFile = true;
response.infoLog += 'File is in codecTagStringsToProcess. Moving to next plugin.';
} else {
response.processFile = false;
response.infoLog += 'File is not in codecTagStringsToProcess. Breaking out of plugin stack.';
}
}
if (inputs.codecTagStringsToNotProcess !== '') {
const codecTagStrings = inputs.codecTagStringsToNotProcess.split(',');
if (checkInclude(codecTagStrings)) {
response.processFile = false;
response.infoLog += 'File is in codecTagStringsToNotProcess. Breaking out of plugin stack.';
} else {
response.processFile = true;
response.infoLog += 'File is not in codecTagStringsToNotProcess. Moving to next plugin.';
}
}
return response;
};
module.exports.details = details;
module.exports.plugin = plugin;

@ -16,7 +16,8 @@ const details = () => ({
type: 'text',
},
tooltip:
'Enter the upper bound size in MB for files which should be processed.',
'Enter the upper bound size in MB for files which should be processed.'
+ ' Files above this size won\'t be processed.',
},
{
name: 'lowerBound',
@ -26,7 +27,8 @@ const details = () => ({
type: 'text',
},
tooltip:
'Enter the lower bound size in MB for files which should be processed.',
'Enter the lower bound size in MB for files which should be processed.'
+ ' Files below this size won\'t be processed.',
},
],
});

@ -1,4 +1,5 @@
/* eslint-disable */
// tdarrSkipTest
const details = () => {
return {
id: "Tdarr_Plugin_078d_Output_embedded_subs_to_SRT_and_remove",

@ -1,4 +1,5 @@
/* eslint-disable */
// tdarrSkipTest
const details = () => {
return {
id: 'Tdarr_Plugin_43az_add_to_radarr',

@ -203,6 +203,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
if (inputs.bitrate_cutoff !== '') {
// Checks if currentBitrate is below inputs.bitrate_cutoff
// If so then don't convert video.
console.log(currentBitrate)
if (currentBitrate <= inputs.bitrate_cutoff) {
convertVideo = false;
}

@ -7,7 +7,7 @@
/*
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
Author: JarBinks, Zachg99, Jeff47
Date: 01/20/2022
Date: 03/22/2022
This is my attempt to create an all in one routine that will maintain my library in optimal format
!!!!FOR MY REQUIREMENTS!!!! Chances are very good you will need to make some changes to this routine
and it's partner in order to make it work for you.
@ -61,10 +61,6 @@ Audio: (Only one audio stream is used!!)
Transcode the stream to aac using 100% of the original streams bitrate
It could probably be less but if the source is of low bitrate but, we don<EFBFBD>t want
to compromise too much on the transcode
Subtitles:
All are removed?? (TODO: ensure this is correct and mention the flag to keep them if desired)
All are copied (They usually take up little space so I keep them)
Any that are in mov_text will be converted to srt
Chapters:
If chapters are found the script keeps them unless...
Any chapter start time is a negative number (Yes I have seen it)
@ -86,71 +82,11 @@ Subtitles:
Tdarr_Plugin_JB69_JBHEVCQSV_MinimalFile (JB - H265, AAC, MKV, bitrate optimized)
Tdarr_Plugin_JB69_JBHEVCQSZ_PostFix (JB - MKV Stats, Chapters, Audio Language)
I am running the docker image provided for Tdarr
****To get the proper video bitrate you need to run this in the docker container:
apt install mkvtoolnix
If those tools are added that no longer needs to be run.
Here is my docker config (I am running compose so yours might be a little different)
tdarr_server:
container_name: tdarr_server
image: haveagitgat/tdarr:latest
privileged: true
restart: unless-stopped
environment:
- PUID=${PUID} # default user id, defined in .env
- PGID=${PGID} # default group id, defined in .env
- TZ=${TZ} # timezone, defined in .env
- serverIP=tdarr_server #using internal docker networking. This should at least work when the nodes are on
#the same docker compose as the server
- serverPort=8266
- webUIPort=8265
volumes:
- ${ROOT}/tdarr/server:/app/server/Tdarr # Tdarr server files
- ${ROOT}/tdarr/configs:/app/configs # config files - can be same as NODE (unless separate server)
- ${ROOT}/tdarr/logs:/app/logs # Tdarr log files
- ${ROOT}/tdarr/cache:/temp # Cache folder, Should be same path mapped on NODE
- ${ROOT}/tdarr/testmedia:/home/Tdarr/testmedia # Should be same path mapped on NODE if using a test folder
- ${ROOT}/tdarr/scripts:/home/Tdarr/scripts # my random way of saving script files
- /volume1/video:/media # video library Should be same path mapped on NODE
ports:
- 8265:8265 #Exposed to access webui externally
- 8266:8266 #Exposed to allow external nodes to reach the server
logging:
options:
max-size: "2m"
max-file: "3"
tdarr_node:
container_name: tdarr_node
image: haveagitgat/tdarr_node:latest
privileged: true
restart: unless-stopped
devices:
- /dev/dri:/dev/dri
environment:
- PUID=${PUID} # default user id, defined in .env
- PGID=${PGID} # default group id, defined in .env
- TZ=${TZ} # timezone, defined in .env
- serverIP=192.168.x.x #container name of the server, should be modified if server is on another machine
- serverPort=8266
- nodeID=TDARRNODE_1
- nodeIP=192.168.x.x #container name of the node
- nodePort=9267 #not exposed via a "ports: " setting as the server/node communication is done on the internal
#docker network and can communicate on all ports
volumes:
- ${ROOT}/tdarr/configs:/app/configs # config files - can be same as server (unless separate server)
- ${ROOT}/tdarr/logs:/app/logs # config files - can be same as server (unless separate server)
- ${ROOT}/tdarr/testmedia:/home/Tdarr/testmedia # Should be same path mapped on server if using a test folder
- ${ROOT}/tdarr/scripts:/home/Tdarr/scripts # my random way of saving script files
- ${ROOT}/tdarr/cache:/temp # Cache folder, Should be same path mapped on server
- /mnt/video:/media # video library Should be same path mapped on server
ports:
- 9267:9267
logging:
options:
max-size: "2m"
max-file: "3"
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
*/
// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_JB69_JBHEVCQSV_MinimalFile',
Stage: 'Pre-processing',
@ -159,7 +95,7 @@ const details = () => ({
Operation: 'Transcode',
Description: `***You should not use this*** until you read the comments at the top of the code and understand
how it works **this does a lot** and is 1 of 2 routines you should to run **Part 1** \n`,
Version: '2.2',
Version: '2.4',
Tags: 'pre-processing,ffmpeg,video,audio,qsv,h265,aac',
Inputs: [{
name: 'Stats_Days',
@ -348,21 +284,17 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
const targetAudioBitratePerChannel = inputs.Target_Audio_Bitrate_Per_Channel;
const targetAudioChannels = inputs.Target_Audio_Channels;
// Subtitles
// const bolIncludeSubs = true; //not currently used, it's possible to remove subs but not setup now
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
const proc = require('child_process');
let bolStatsAreCurrent = false;
// Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== 'video') {
response.processFile = false;
response.infoLog += 'File is not a video. Exiting \n';
return response;
}
// If the file has already been processed we dont need to do more
if (file.container === 'mkv' && (
file.mediaInfo.track[0].extra !== undefined
&& file.mediaInfo.track[0].extra.JBDONEVERSION !== undefined
@ -409,10 +341,6 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
} catch (err) {
response.infoLog += 'Error Updating Status Probably Bad file, A remux will probably fix, will continue\n';
}
response.infoLog += 'Getting Stats Objects, again!\n';
// objMedInfo = JSON.parse(proc.execSync('mediainfo "' + currentFileName + '" --output=JSON').toString());
// objFFProbeInfo = JSON.parse(proc.execSync('ffprobe -v error -print_format json' +
// ' -show_format -show_streams -show_chapters "' + currentFileName + '"').toString());
}
}
@ -440,7 +368,6 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
const bolDoChapters = true;
// Set up required variables
let videoIdx = -1;
let videoIdxFirst = -1;
let audioIdx = -1;
@ -449,14 +376,12 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
let strStreamType = '';
let MILoc = -1;
// Go through each stream in the file.
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
strStreamType = file.ffProbeData.streams[i].codec_type.toLowerCase();
// Looking For Video
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
if (strStreamType === 'video') {
// First we need to check if it is included in the MediaInfo struture, it might not be (mjpeg??, others??)
MILoc = findMediaInfoItem(file, i);
response.infoLog += `Index ${i} MediaInfo stream: ${MILoc} \n`;
@ -484,9 +409,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
videoIdx = i;
} else {
const MILocC = findMediaInfoItem(file, videoIdx);
// const curstreamheight = file.ffProbeData.streams[videoIdx].height * 1; //Not needed
const curStreamWidth = file.ffProbeData.streams[videoIdx].width * 1;
// const curstreamFPS = file.mediaInfo.track[MILocC].FrameRate * 1; //Not needed
let curStreamBR = file.mediaInfo.track[MILocC].BitRate * 1;
if (isNaN(curStreamBR)) {
@ -505,12 +428,6 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
// Looking For Audio
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
if (strStreamType === 'audio') {
// response.processFile = false;
// response.infoLog += i + ":" + objFFProbeInfo.streams[i].tags.language + " \n";
// audioIdxFirst = i;
// response.infoLog += JSON.stringify(objFFProbeInfo.streams[i]) + " \n";
audioChannels = file.ffProbeData.streams[i].channels * 1;
audioBitrate = file.mediaInfo.track[findMediaInfoItem(file, i)].BitRate * 1;
@ -564,7 +481,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Looking For Subtitles -- These are causing problems let's just exclude for now
// Looking For Subtitles
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
if (!bolForceNoSubs && !bolDoSubs && (strStreamType === 'text' || strStreamType === 'subtitle')) {
// A sub has an S_TEXT/WEBVTT codec, ffmpeg will fail with it
@ -581,33 +498,9 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
bolForceNoSubs = true;
}
}
// bolDoSubs = true;
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
}
// return response;
// Go through chapters in the file looking for badness
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
// Not processing chapters - fileobject doesn't seem to have the chapters section
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
// for (var i = 0; i < objFFProbeInfo.chapters.length; i+=1) {
// Bad start times
// if (objFFProbeInfo.chapters[i].start_time < 0) {
// bolDoChapters = false;
// break; //Dont need to continue because we know they are bad
// }
// Duplicate start times
// for (var x = 0; i < objFFProbeInfo.chapters.length; i+=1) {
// if (i != x && objFFProbeInfo.chapters[i].start_time == objFFProbeInfo.chapters[x].start_time) {
// bolDoChapters = false;
// break; //Dont need to continue because we know they are bad
// }
// }
// }
// Video Decision section
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
if (videoIdx === -1) {
@ -638,11 +531,11 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
// Source is Variable Frame rate but we will transcode to fixed
if (file.mediaInfo.track[MILoc].FrameRate_Mode === 'VFR') videoFPS = 9999;
if (videoFPS > targetFrameRate) {
if (videoFPS > targetFrameRate && file.container !== 'ts') {
bolChangeFrameRateVideo = true; // Need to fix this it does not work :-(
}
// Lets see if we need to scal down the video size
// Lets see if we need to scale down the video size
if (videoHeight > maxVideoHeight) {
bolScaleVideo = true;
videoNewWidth = Math.floor((maxVideoHeight / videoHeight) * videoWidth);
@ -723,7 +616,6 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
} else {
// We already know the existing bitrate has enough meat for a decent transcode
// bolTranscodeVideo = true;
response.infoLog += `Video existing Bitrate, ${videoBR}, is higher than target,`
+ ` ${optimalVideoBitrate}, transcoding \n`;
}
@ -759,12 +651,12 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
audioNewChannels = file.ffProbeData.streams[audioIdx].channels;
}
let optimalaudiobitrate = audioNewChannels * targetAudioBitratePerChannel;
let optimalAudioBitrate = audioNewChannels * targetAudioBitratePerChannel;
// Now what are we going todo with the audio part
if (audioBR > (optimalaudiobitrate * 1.1)) {
if (audioBR > (optimalAudioBitrate * 1.1)) {
bolTranscodeAudio = true;
response.infoLog += `Audio existing Bitrate, ${audioBR}, is higher than target, ${optimalaudiobitrate} \n`;
response.infoLog += `Audio existing Bitrate, ${audioBR}, is higher than target, ${optimalAudioBitrate} \n`;
}
// If the audio codec is not what we want then we should transcode
@ -776,10 +668,10 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
// If the source bitrate is less than out target bitrate we should not ever go up
if (audioBR < optimalaudiobitrate) {
if (audioBR < optimalAudioBitrate) {
response.infoLog += `Audio existing Bitrate, ${audioBR}, is lower than target,`
+ ` ${optimalaudiobitrate}, using existing `;
optimalaudiobitrate = audioBR;
+ ` ${optimalAudioBitrate}, using existing `;
optimalAudioBitrate = audioBR;
if (file.ffProbeData.streams[audioIdx].codec_name !== targetAudioCodec) {
response.infoLog += 'rate';
} else {
@ -791,57 +683,35 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
// lets assemble our ffmpeg command
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
const strTranCodeBaseHW = ' -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi ';
const strTranCodeBaseSW = ' -vaapi_device /dev/dri/renderD128 ';
const strTranscodeVideoMapping = ' <io> -max_muxing_queue_size 8000 -map 0:{0} ';
const strTranscodeVideoCopy = ' -c:v:0 copy ';
const strTranscodeVideoTranscoding = ' -c:v:0 hevc_vaapi ';
// Used to make the output 10bit, I think the quotes need to be this way for ffmpeg
const strTranscodeVideoOptions = ' -vf "{0}" ';
const strTranscodeVideoScaling = 'w=-1:h=1080'; // Used when video is above our target of 1080
const strTransCodeFrameRate = 'fps={0}'; // Used to change the framerate to the target framerate
const strTranscodeVideoFormatHW = 'scale_vaapi='; // Used to make the output 10bit
const strTranscodeVideoFormat = 'format={0}'; // Used to add filters to the hardware transcode
const strTranscodeVideo10bit = 'p010'; // Used to make the output 10bit
const strTranscodeVideo8bit = 'p008'; // Used to make the output 8bit
const strTranscodeVideoSWDecode = 'hwupload'; // Used to make it use software decode if necessary
// Used to make it sure the software decode is in the proper pixel format
const strTranscodeVideoSWDecode10bit = 'nv12|vaapi';
const strTranscodeVideoBitrate = ' -b:v {0} '; // Used when video is above our target of 1080
const strTranscodeAudioMapping = ' -map 0:{0} ';
const strTranscodeAudioCopy = ' -c:a:0 copy ';
const strTranscodeAudioTranscoding = ' -c:a:0 ${targetAudioCodec} -b:a {0} ';
const strTranscodeAudioDownMixing = ' -ac {0} ';
const strTranscodeSubs = ' -map 0:s -scodec copy ';
const strTranscodeSubsConvert = ' -map 0:s -c:s srt ';
const strTranscodeSubsNone = ' -map -0:s ';
const strTranscodeMetadata = ' -map_metadata:g -1 -metadata JBDONEVERSION=1 -metadata JBDONEDATE={0} ';
const strTranscodeChapters = ' -map_chapters {0} ';
const strTranscodeFileOptions = ' ';
let strFFcmd = '';
if (bolTranscodeVideo) {
if (bolTranscodeSoftwareDecode) {
strFFcmd += strTranCodeBaseSW;
strFFcmd += ' -vaapi_device /dev/dri/renderD128 ';
} else {
strFFcmd += strTranCodeBaseHW;
strFFcmd += ' -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi ';
}
}
strFFcmd += strTranscodeVideoMapping.replace('{0}', videoIdx);
strFFcmd += ` <io> -max_muxing_queue_size 8000 -map 0:${videoIdx} `;
if (bolTranscodeVideo) {
strFFcmd += strTranscodeVideoTranscoding;
// Used to make the output 10bit, I think the quotes need to be this way for ffmpeg
strFFcmd += ' -c:v:0 hevc_vaapi ';
if (bolScaleVideo || bolUse10bit || bolTranscodeSoftwareDecode || bolChangeFrameRateVideo) {
let strOptions = '';
let strFormat = '';
if (bolScaleVideo) {
strOptions += strTranscodeVideoScaling;
// Used when video is above our target
strOptions += `w=-1:h=${maxVideoHeight}`;
}
let strChangeVideoRateString = '';
if (bolChangeFrameRateVideo) {
strChangeVideoRateString = `${strTransCodeFrameRate.replace('{0}', targetFrameRate)},`;
// Used to change the framerate to the target framerate
strChangeVideoRateString = `fps=${targetFrameRate},`;
}
if (strFormat.length > 0) {
@ -849,11 +719,13 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
if (bolUse10bit && !bolSource10bit) {
strFormat += strTranscodeVideo10bit;
// Used to make the output 10bit
strFormat += 'p010';
}
if (!bolUse10bit && bolSource10bit) {
strFormat += strTranscodeVideo8bit;
// Used to make the output 8bit
strFormat += 'p008';
}
if (bolTranscodeSoftwareDecode) {
@ -861,66 +733,64 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
if (strFormat.length > 0) {
strFormat += ',';
}
strFormat += strTranscodeVideoSWDecode10bit;
// Used to make it sure the software decode is in the proper pixel format
strFormat += 'nv12|vaapi';
}
if (strFormat.length > 0) {
strFormat += ',';
}
strFormat += strTranscodeVideoSWDecode;
// Used to make it use software decode if necessary
strFormat += 'hwupload';
}
if (strFormat.length > 0) {
if (strOptions.length > 0) {
strOptions += ',';
}
strOptions += strTranscodeVideoFormat.replace('{0}', strFormat);
strOptions += `format=${strFormat}`;
}
if (bolTranscodeSoftwareDecode) {
strFFcmd += strTranscodeVideoOptions.replace('{0}', strChangeVideoRateString + strOptions);
strFFcmd += ` -vf "${strChangeVideoRateString} ${strOptions}" `;
} else {
strFFcmd += strTranscodeVideoOptions
.replace('{0}', strChangeVideoRateString + strTranscodeVideoFormatHW + strOptions);
strFFcmd += ` -vf "${strChangeVideoRateString} scale_vaapi=${strOptions}" `;
}
}
strFFcmd += strTranscodeVideoBitrate.replace('{0}', optimalVideoBitrate);
// Used when video is above our target
strFFcmd += ` -b:v ${optimalVideoBitrate} `;
} else {
strFFcmd += strTranscodeVideoCopy;
strFFcmd += ' -c:v:0 copy ';
}
strFFcmd += strTranscodeAudioMapping.replace('{0}', audioIdx);
strFFcmd += ` -map 0:${audioIdx} `;
if (bolTranscodeAudio) {
strFFcmd += strTranscodeAudioTranscoding
.replace('{0}', optimalaudiobitrate)
.replace('${targetAudioCodec}', targetAudioCodec);
strFFcmd += ` -c:a:0 ${targetAudioCodec} -b:a ${optimalAudioBitrate} `;
} else {
strFFcmd += strTranscodeAudioCopy;
strFFcmd += ' -c:a:0 copy ';
}
if (bolDownMixAudio) {
strFFcmd += strTranscodeAudioDownMixing.replace('{0}', audioNewChannels);
strFFcmd += ` -ac ${audioNewChannels} `;
}
if (bolForceNoSubs) {
strFFcmd += strTranscodeSubsNone;
strFFcmd += ' -map -0:s ';
} else if (bolDoSubs) {
if (bolDoSubsConvert) {
strFFcmd += strTranscodeSubsConvert;
strFFcmd += ' -map 0:s -c:s srt ';
} else {
strFFcmd += strTranscodeSubs;
strFFcmd += ' -map 0:s -scodec copy ';
}
}
strFFcmd += strTranscodeMetadata.replace('{0}', new Date().toISOString());
strFFcmd += ` -map_metadata:g -1 -metadata JBDONEVERSION=1 -metadata JBDONEDATE=${new Date().toISOString()} `;
if (bolDoChapters) {
strFFcmd += strTranscodeChapters.replace('{0}', '0');
strFFcmd += ' -map_chapters 0 ';
} else {
strFFcmd += strTranscodeChapters.replace('{0}', '-1');
strFFcmd += ' -map_chapters -1 ';
}
strFFcmd += strTranscodeFileOptions;
/// ///////////////////////////////////////////////////////////////////////////////////////////////////
// response.infoLog += strFFcmd + "\n";
response.preset += strFFcmd;
response.processFile = true;
response.infoLog += 'File needs work. Transcoding. \n';

@ -148,6 +148,7 @@
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
// tdarrSkipTest
const details = () => {
return {
id: "Tdarr_Plugin_JB69_JBHEVCQSZ_PostFix",

@ -10,7 +10,7 @@ const details = () => ({
Working by the logic that H265 can support the same ammount of data at half the bitrate of H264.
NVDEC & NVENC compatable GPU required.
This plugin will skip any files that are in the VP9 codec.`,
Version: '3.0',
Version: '3.1',
Tags: 'pre-processing,ffmpeg,video only,nvenc h265,configurable',
Inputs: [{
name: 'container',
@ -149,6 +149,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
let videoIdx = 0;
let CPU10 = false;
let extraArguments = '';
let genpts = '';
let bitrateSettings = '';
// Work out currentBitrate using "Bitrate = file size / (number of minutes * .0075)"
// Used from here https://blog.frame.io/2017/03/06/calculate-video-bitrates/
@ -164,6 +165,11 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
// eslint-disable-next-line no-bitwise
const maximumBitrate = ~~(targetBitrate * 1.3);
// If Container .ts or .avi set genpts to fix unknown timestamp
if (inputs.container.toLowerCase() === 'ts' || inputs.container.toLowerCase() === 'avi') {
genpts = '-fflags +genpts';
}
// If targetBitrate comes out as 0 then something has gone wrong and bitrates could not be calculated.
// Cancel plugin completely.
if (targetBitrate === 0) {
@ -315,13 +321,15 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
response.preset = '-c:v mpeg1_cuvid';
} else if (file.video_codec_name === 'mpeg2') {
response.preset = '-c:v mpeg2_cuvid';
} else if (file.video_codec_name === 'mpeg4') {
response.preset = '-c:v mpeg4_cuvid';
} else if (file.video_codec_name === 'vc1') {
response.preset = '-c:v vc1_cuvid';
} else if (file.video_codec_name === 'vp8') {
response.preset = '-c:v vp8_cuvid';
}
response.preset += `,-map 0 -c:v hevc_nvenc -cq:v 19 ${bitrateSettings} `
response.preset += `${genpts}, -map 0 -c:v hevc_nvenc -cq:v 19 ${bitrateSettings} `
+ `-spatial_aq:v 1 -rc-lookahead:v 32 -c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments}`;
response.processFile = true;
response.infoLog += 'File is not hevc or vp9. Transcoding. \n';

@ -6,7 +6,7 @@ const details = () => ({
Type: 'Audio',
Operation: 'Transcode',
Description: 'This plugin can convert any 2.0 audio track/s to AAC and can create downmixed audio tracks. \n\n',
Version: '2.3',
Version: '2.4',
Tags: 'pre-processing,ffmpeg,audio only,configurable',
Inputs: [{
name: 'aac_stereo',
@ -114,7 +114,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
&& has6Channel === false
&& file.ffProbeData.streams[i].channels === 8
) {
ffmpegCommandInsert += `-map 0:${i} -c:a:${audioIdx} ac3 -ac 6 -metadata:s:a:${audioIdx} title="5.1 " `;
ffmpegCommandInsert += `-map 0:${i} -c:a:${audioIdx} ac3 -ac 6 -metadata:s:a:${audioIdx} title="5.1" `;
response.infoLog += '☒Audio track is 8 channel, no 6 channel exists. Creating 6 channel from 8 channel. \n';
convert = true;
}
@ -124,7 +124,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
&& has2Channel === false
&& file.ffProbeData.streams[i].channels === 6
) {
ffmpegCommandInsert += `-map 0:${i} -c:a:${audioIdx} aac -ac 2 -metadata:s:a:${audioIdx} title="2.0 " `;
ffmpegCommandInsert += `-map 0:${i} -c:a:${audioIdx} aac -ac 2 -metadata:s:a:${audioIdx} title="2.0" `;
response.infoLog += '☒Audio track is 6 channel, no 2 channel exists. Creating 2 channel from 6 channel. \n';
convert = true;
}

@ -5,8 +5,8 @@ const details = () => ({
Name: 'Migz-Remove image formats from file',
Type: 'Video',
Operation: 'Transcode',
Description: 'Identify any unwanted image formats in the file and remove those streams. MJPEG & PNG \n\n',
Version: '1.3',
Description: 'Identify any unwanted image formats in the file and remove those streams. MJPEG, PNG & GIF \n\n',
Version: '1.4',
Tags: 'pre-processing,ffmpeg,video only',
Inputs: [],
});
@ -42,10 +42,11 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Check if stream is video.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'video') {
// Check if stream codec is mjpeg or png. Remove if so.
// Check if stream codec is mjpeg, png or gif. Remove if so.
if (
file.ffProbeData.streams[i].codec_name === 'mjpeg'
|| file.ffProbeData.streams[i].codec_name === 'png'
|| file.ffProbeData.streams[i].codec_name === 'gif'
) {
convert = true;
extraArguments += `-map -v:${videoIdx} `;

@ -3,6 +3,7 @@ module.exports.dependencies = [
];
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_MC93_MigzPlex_Autoscan',
Stage: 'Post-processing',

@ -14,6 +14,7 @@
var secondPass = false;
var logOutFile = '';
// tdarrSkipTest
const details = () => {
return {
id: "Tdarr_Plugin_NIfPZuCLU_2_Pass_Loudnorm_Audio_Normalisation",
@ -96,8 +97,13 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
logOutFile = currentfilename.substr(0, currentfilename.lastIndexOf(".")) + ".out"
console.log("Log out file: " + logOutFile)
//get an updated version of the file for checking metadata
var probeData = JSON.parse(require("child_process").execSync(`ffprobe -v quiet -print_format json -show_format -show_streams "${currentfilename}"`).toString())
let probeData;
if (file && file.ffProbeData && file.ffProbeData.format) {
probeData = file.ffProbeData;
} else {
//get an updated version of the file for checking metadata
probeData = JSON.parse(require("child_process").execSync(`ffprobe -v quiet -print_format json -show_format -show_streams "${currentfilename}"`).toString())
}
//setup required varibles
var loudNorm_i = -23.0

@ -1,6 +1,7 @@
/* eslint-disable */
// tdarrSkipTest
const details = () => {
return {
id: "Tdarr_Plugin_O8O0dCTlb_Set_File_Permissions_For_UnRaid",

@ -4,6 +4,7 @@ module.exports.dependencies = [
];
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_TD01_TOAD_Autoscan',
Stage: 'Post-processing',

@ -1,7 +1,11 @@
// All credit for original plugin logic goes to Migz.
// This Plugin is essentially just his NVENC/CPU plugin modified to work with QSV & with extra hevc logic.
// Extra logic is mainly to control encoder quality/speed & to allow HEVC files to be reprocessed to reduce file size
// NOTE - This does not use VAAPI, it is QSV only. So newer intel igpus only. 8th+ gen should work.
// NOTE - This does not use VAAPI, it is QSV only. So newer intel iGPUs only. 8th+ gen should work.
// Additionally this was designed and tested on UNRAID via docker though there is logic to support use on
// Windows, Linux & Mac
// White paper from intel regarding QSV performance on linux using FFMPEG here:
// eslint-disable-next-line max-len
// https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/cloud-computing-quicksync-video-ffmpeg-white-paper.pdf
@ -12,12 +16,14 @@ const details = () => ({
Name: 'Boosh-Transcode using QSV GPU & FFMPEG',
Type: 'Video',
Operation: 'Transcode',
Description: `This is a QSV specific plugin, VAAPI is NOT used. So an INTEL QSV enabled CPU is required.
8th+ gen CPUs should work. Files not in H265/HEVC will be transcoded into H265/HEVC using Quick Sync Video (QSV)
via Intel GPU with ffmpeg. Settings are dependant on file bitrate working by the logic that H265 can support
Description: `This is a QSV specific plugin. 8th+ gen INTEL QSV enabled CPUs are required. VAAPI is NOT used.
Files not in H265/HEVC will be transcoded into H265/HEVC using Quick Sync Video (QSV)
via Intel GPU using FFmpeg. 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 into HEVC if they
exceed the bitrate specified in "hevc_max_bitrate". Reminder! An INTEL QSV enabled CPU is required.`,
exceed the bitrate specified in "hevc_max_bitrate". Reminder! An INTEL QSV enabled CPU is required.
NOTE - Created for use with UNRAID Docker and while it should support Windows/Mac etc, it may require
a custom version of FFmpeg to work properly.`,
Version: '1.0',
Tags: 'pre-processing,ffmpeg,video only,qsv,h265,hevc,configurable',
Inputs: [
@ -32,9 +38,13 @@ const details = () => ({
'mp4',
],
},
tooltip: `Specifies the output container of the file.
tooltip: `\\n
==DESCRIPTION==
\\n Specifies the output container of the file.
\\n Ensure that all stream types you may have are supported by your chosen container.
\\n MKV is recommended.
\\n
==INFO==
\\n Only MP4 & MKV are supported and MKV is recommended.
\\nExample:\\n
mkv
\\nExample:\\n
@ -51,17 +61,22 @@ const details = () => ({
'true',
],
},
tooltip: `Make the file conform to output containers requirements.
Use if you need to ensure the encode works from mp4>mkv or mkv>mp4.
This will drop data of certain type so ensure you are happy with that,
or use another plugin to convert these data types first.
\\n Drop hdmv_pgs_subtitle/eia_608/subrip/timed_id3 for MP4.
\\n Drop data streams/mov_text/eia_608/timed_id3 for MKV.
\\n Default is false.
\\nExample:\\n
true
\\nExample:\\n
false`,
tooltip: `\\n
==DESCRIPTION==
\\n Make the file conform to output containers requirements.
Use if you need to ensure the encode works from mp4>mkv or mkv>mp4. \\n
==WARNING== \\n
This will remove data of certain types so ensure you are happy with that,
or use another plugin to convert these data types first!
\\n
==INFO==
\\n Drop hdmv_pgs_subtitle/eia_608/subrip/timed_id3 for MP4.
\\n Drop data streams/mov_text/eia_608/timed_id3 for MKV.
\\n Default is false.
\\nExample:\\n
true
\\nExample:\\n
false`,
},
{
name: 'encoder_speedpreset',
@ -79,17 +94,50 @@ const details = () => ({
'veryslow',
],
},
tooltip: `Specify the encoder speed/preset to use.
Slower options mean slower encode but better quality and faster have quicker encodes but worse quality.
For more information see intel white paper on ffmpeg results using qsv: \\n`
// eslint-disable-next-line max-len
+ `https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/cloud-computing-quicksync-video-ffmpeg-white-paper.pdf
tooltip: `\\n
==DESCRIPTION==
\\n Specify the encoder speed/preset to use.
Slower options mean a slower encode but better quality and faster options mean faster encodes but
worse quality.
\\n For more information see intel white paper on FFmpeg results using QSV: \\n`
// eslint-disable-next-line max-len
+ `https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/cloud-computing-quicksync-video-ffmpeg-white-paper.pdf
\\n
==INFO==
\\n Default is "slow".
\\nExample:\\n
medium
\\nExample:\\n
slower`,
},
{
name: 'extra_qsv_options',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip: `\\n
==DESCRIPTION==
\\n Here you can add extra options to the FFmpeg QSV ENCODE cmd.
This does not override the FFmpeg cmd, it just allows additions to it.
\\n
There are extra QSV options that can be
forced on/off as desired. See here for some possible cmds -
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
==INFO==
\\n Default is empty but a suggested value is below. If unsure just leave empty.
\\n Ensure 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.
\\nExample:\\n
-extbrc 1 -rdo 1 -mbbrc 1 -b_strategy 1 -adaptive_i 1 -adaptive_b 1`,
},
{
name: 'enable_10bit',
type: 'boolean',
@ -101,11 +149,15 @@ const details = () => ({
'true',
],
},
tooltip: `Specify if we want to enable 10bit encoding.
If this is enabled files will be processed and converted into 10bit
tooltip: `\\n
==DESCRIPTION==
\\n Specify if we want to enable 10bit encoding.
\\n If this is enabled files will be processed and converted into 10bit
HEVC using main10 profile and with p010le pixel format. \n
If you just want to retain 10 bit already in files then this can be left as false, as
10bit to 10bit in ffmpeg should be automatic.
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
==INFO==
\\n Default is "false".
\\nExample:\\n
true
@ -119,9 +171,13 @@ const details = () => ({
inputUI: {
type: 'text',
},
tooltip: `Specify bitrate cutoff, files with a total bitrate lower then this will not be processed.
tooltip: `\\n
==DESCRIPTION==
\\n Specify bitrate cutoff, files with a total bitrate lower then this will not be processed. \n
Since getting the bitrate of the video from files is unreliable, bitrate here refers to the total
bitrate of the file and not just the video steam.
\\n
==INFO==
\\n Rate is in kbps.
\\n Defaults to 0 which means this is disabled.
\\n Enter a valid number to enable.
@ -137,10 +193,14 @@ const details = () => ({
inputUI: {
type: 'text',
},
tooltip: `Specify a maximum average video bitrate. When encoding we take the current total bitrate and halve it
tooltip: `\\n
==DESCRIPTION==
\\n Specify a maximum average video bitrate. When encoding we take the current total bitrate and halve it
to get an average target. This option sets a upper limit to that average
(i.e if you have a video bitrate of 10000, half is 5000, if your maximum desired average bitrate is 4000
then we use that as the target instead of 5000).
\\n
==INFO==
\\n Bitrate here is referring to video bitrate as we want to set the video bitrate on encode.
\\n Rate is in kbps.
\\n Defaults to 0 which means this is disabled.
@ -157,11 +217,15 @@ const details = () => ({
inputUI: {
type: 'text',
},
tooltip: `Specify a minimum average video bitrate. When encoding we take the current total bitrate and halve
it to get an average target. This option sets a lower limit to that average (i.e if you have a video bitrate
of 3000, half is 1500, if your minimum desired average bitrate is 2000 then we use that as the target instead
of 1500).
\\nBitrate here is referring to video bitrate as we want to set the video bitrate on encode.
tooltip: `\\n
==DESCRIPTION==
\\n Specify a minimum average video bitrate. When encoding we take the current total bitrate and halve
it to get an average target. This option sets a lower limit to that average (i.e if you have a video bitrate
of 3000, half is 1500, if your minimum desired average bitrate is 2000 then we use that as the target instead
of 1500).
\\n
==INFO==
\\n Bitrate here is referring to video bitrate as we want to set the video bitrate on encode.
\\n Rate is in kbps.
\\n Defaults to 0 which means this is disabled.
\\n Enter a valid number to enable.
@ -181,9 +245,12 @@ const details = () => ({
'true',
],
},
tooltip: `Specify if we want to reprocess HEVC, VP9 or AV1 files
(i.e reduce bitrate of files already in those codecs). Since this uses the same logic as normal, halving the
current bitrate, this is NOT recommended unless you know what you are doing, so leave false if unsure.
tooltip: `\\n
==DESCRIPTION==
\\n Specify if we want to reprocess HEVC, VP9 or AV1 files
(i.e reduce bitrate of files already in those codecs).
\\n Since this uses the same logic as normal, halving the current bitrate, this is NOT recommended
unless you know what you are doing, so leave false if unsure.
NEEDS to be used in conjunction with "bitrate_cutoff" or "hevc_max_bitrate" otherwise is ignored.
This is useful in certain situations, perhaps you have a file which is HEVC but has an extremely high
bitrate and you'd like to reduce it.
@ -192,7 +259,9 @@ const details = () => ({
"hevc_max_bitrate" & "max_average_bitrate" to prevent the plugin looping. Also it is highly suggested
that you have your "hevc_max_bitrate" higher than "max_average_bitrate".
\\n Again if you are unsure, please leave this as false!
\\n\\n WARNING!! IF YOU HAVE VP9 OR AV1 FILES YOU WANT TO KEEP IN THOSE FORMATS THEN DO NOT USE THIS OPTION.
\\n
==WARNING== \\n
IF YOU HAVE VP9 OR AV1 FILES YOU WANT TO KEEP IN THOSE FORMATS THEN DO NOT USE THIS OPTION.
\\n
\\nExample:\\n
true
@ -206,7 +275,9 @@ const details = () => ({
inputUI: {
type: 'text',
},
tooltip: `Has no effect unless "reconvert_hevc" is set to true. This allows you to specify a maximum
tooltip: `\\n
==DESCRIPTION==
\\n Has no effect unless "reconvert_hevc" is set to true. This allows you to specify a maximum
allowed average bitrate for HEVC or similar files. Much like the "bitrate_cutoff" option, but
specifically for HEVC files. It should be set HIGHER then your standard cutoff for safety.
\\n Also, it's highly suggested you use the min & max average bitrate options in combination with this. You
@ -216,6 +287,8 @@ const details = () => ({
this is why it is NOT recommended!
\\n As with the cutoff, getting the bitrate of the video from files is unreliable, so bitrate
here refers to the total bitrate of the file and not just the video steam.
\\n
==INFO==
\\n Rate is in kbps.
\\n Defaults to 0 which means this is disabled.
\\n Enter a valid number to enable, otherwise we use "bitrate_cutoff" and multiply x2 for a safe limit.
@ -229,7 +302,7 @@ const details = () => ({
// eslint-disable-next-line no-unused-vars
const plugin = (file, librarySettings, inputs, otherArguments) => {
const lib = require('../methods/lib')();
const lib = require('../methods/lib')(); const os = require('os');
// eslint-disable-next-line no-unused-vars,no-param-reassign
inputs = lib.loadDefaultValues(inputs, details);
const response = {
@ -413,7 +486,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
// 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.
// 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') {
@ -502,57 +575,80 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
+ `-maxrate ${maximumBitrate}k -bufsize ${currentBitrate}k`;
// Print to infoLog information around file & bitrate settings.
response.infoLog += `\nContainer for output selected as ${inputs.container}. \n`;
response.infoLog += `The current file bitrate = ${currentBitrate}k \n`;
response.infoLog += 'Bitrate settings: \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`;
// Codec will be checked so it can be transcoded correctly
// (QSV doesn't support HW decode of all older codecs, h263 & mpeg1 are SW based currently)
// If source file is 10 bit then don't hardware decode at all as this can cause issues.
// Instead best just to cpu decode to ensure it works.
// START PRESET
// DECODE FLAGS
// -fflags +genpts should regenerate timestamps if they end up missing...
response.preset = '-fflags +genpts ';
// Attempt to enable HW Decoding...
// If source file is 10 bit then bail as this can cause issues. Think it's the -c:v option that can break during 10bit
if (main10 === false) {
// Currently supported HW decode types
switch (file.video_codec_name) {
case 'h263':
response.preset = '-hwaccel qsv -c:v h263';
case 'mpeg2':
response.preset += '-hwaccel qsv -c:v mpeg2_qsv';
break;
case 'h264':
response.preset = '-hwaccel qsv -c:v h264_qsv';
break;
case 'mjpeg':
response.preset = '-hwaccel qsv -c:v mjpeg_qsv';
break;
case 'hevc':
response.preset = '-hwaccel qsv -c:v hevc_qsv';
break;
case 'mpeg1':
response.preset = '-hwaccel qsv -c:v mpeg1';
break;
case 'mpeg2':
response.preset = '-hwaccel qsv -c:v mpeg2_qsv';
response.preset += '-hwaccel qsv -c:v h264_qsv';
break;
case 'vc1':
response.preset = '-hwaccel qsv -c:v vc1_qsv';
response.preset += '-hwaccel qsv -c:v vc1_qsv';
break;
case 'mjpeg':
response.preset += '-hwaccel qsv -c:v mjpeg_qsv';
break;
case 'vp8':
response.preset = '-hwaccel qsv -c:v vp8_qsv';
response.preset += '-hwaccel qsv -c:v vp8_qsv';
break;
case 'av1':
response.preset = '-hwaccel qsv -c:v av1';
case 'hevc':
response.preset += '-hwaccel qsv -c:v hevc_qsv';
break;
case 'vp9': // Should be supported by 8th Gen +
response.preset += '-hwaccel qsv -c:v vp9_qsv';
break;
default:
response.preset = '';
response.preset += '-hwaccel qsv';
}
} else {
response.preset += '-hwaccel qsv';
// Enable basic hwaccel regardless. Seems to work...
}
response.preset += `<io> -map 0 -c:v hevc_qsv ${bitrateSettings} `
+ `-preset ${inputs.encoder_speedpreset} -look_ahead 1
// ADD ENCODE FLAGS TO PRESET
response.preset += '<io> -map 0 -c:v ';
// Account for different OS setup for QSV.
// FYI Darwin is Mac OS
switch (os.platform()) {
case 'darwin':
response.preset += 'hevc_videotoolbox';
// hevc_videotoolbox is for Mac but that doesn't seem to be included in Tdarr current Jellyfin FFmpeg
// Likely needs custom FFmpeg installed
break;
case 'linux':
response.preset += 'hevc_qsv';
break;
case 'win32':
response.preset += 'hevc_qsv -load_plugin hevc_hw';
// Windows needs the additional -load_plugin plugin. Tested working on a Win 10 - i5-10505
break;
default:
response.preset += 'hevc_qsv';
}
// Add the rest of the FFmpeg command
response.preset += ` ${bitrateSettings} `
+ `-preset ${inputs.encoder_speedpreset} ${inputs.extra_qsv_options}
-c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments}`;
// Other settings to consider.
// -b_strategy 1 -adaptive_b 1 -adaptive_i 1 -async_depth 1 -look_ahead 1 -look_ahead_depth 100
response.processFile = true;
response.infoLog += 'File Transcoding. \n';
response.infoLog += 'File Transcoding... \n';
return response;
};
module.exports.details = details;

@ -62,6 +62,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
const lib = require('../methods/lib')();
// eslint-disable-next-line no-unused-vars,no-param-reassign
inputs = lib.loadDefaultValues(inputs, details);
var response = {
processFile: false,
preset: "",
@ -70,15 +71,17 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
FFmpegMode: false,
reQueueAfter: false,
infoLog: "",
addInfo(status, info) {
this.infoLog += (status ? "☑" : "☒") + " " + info + "\n";
},
};
const addInfo = (status, info) => {
response.infoLog += (status ? "☑" : "☒") + " " + info + "\n";
}
// Check the file is a video
if (file.fileMedium !== "video") {
console.log("File is not video");
response.addInfo(BAD, `File is not video`);
addInfo(BAD, `File is not video`);
response.processFile = false;
return response;
}
@ -87,7 +90,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
let preset = getPreset(inputs.FFmpeg_preset);
if (preset === null) {
response.addInfo(
addInfo(
BAD,
`Invalid Preset, \"${inputs.FFmpeg_preset}\" please select from (slow,medium,fast,veryfast)`
);
@ -125,10 +128,10 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
if (hasBadSubs)
response.addInfo(BAD, "File contains unsupported sub(s), dropping these!");
addInfo(BAD, "File contains unsupported sub(s), dropping these!");
if (file.ffProbeData.streams[0].codec_name != "h264") {
response.addInfo(BAD, "File is not in h264!");
addInfo(BAD, "File is not in h264!");
response.preset =
", -map_metadata -1 -map 0:V " +
subMap +
@ -139,11 +142,11 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
response.FFmpegMode = true;
return response;
} else {
response.addInfo(GOOD, "File is already in h264!");
addInfo(GOOD, "File is already in h264!");
}
if (file.meta.Title != undefined && !jsonString.includes("aac") && hasSubs) {
response.addInfo(BAD, "File has title metadata and no aac and subs");
addInfo(BAD, "File has title metadata and no aac and subs");
response.preset =
", -map_metadata -1 -map 0:v " +
subMap +
@ -156,7 +159,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
if (!jsonString.includes("aac") && hasSubs) {
response.addInfo(BAD, "File has no aac track and has subs");
addInfo(BAD, "File has no aac track and has subs");
response.preset =
", -map 0:v " +
subMap +
@ -169,7 +172,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
if (file.meta.Title != undefined && hasSubs) {
response.addInfo(BAD, "File has title and has subs");
addInfo(BAD, "File has title and has subs");
response.preset =
", -map_metadata -1 -map 0:v " +
subMap +
@ -182,7 +185,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
}
if (file.meta.Title != undefined) {
response.addInfo(BAD, "File has title metadata");
addInfo(BAD, "File has title metadata");
response.preset =
", -map_metadata -1 -map 0:v " +
subMap +
@ -193,11 +196,11 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
response.FFmpegMode = true;
return response;
} else {
response.addInfo(GOOD, "File has no title metadata");
addInfo(GOOD, "File has no title metadata");
}
if (!jsonString.includes("aac")) {
response.addInfo(BAD, "File has no aac track");
addInfo(BAD, "File has no aac track");
response.preset =
", -map 0:v " +
subMap +
@ -208,14 +211,14 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
response.FFmpegMode = true;
return response;
} else {
response.addInfo(GOOD, "File has aac track");
addInfo(GOOD, "File has aac track");
}
if (hasSubs) {
if (hasBadSubs) {
response.addInfo(BAD, "File has incompatible subs, dropping these...");
addInfo(BAD, "File has incompatible subs, dropping these...");
} else {
response.addInfo(BAD, "File has compatible subs, copying...");
addInfo(BAD, "File has compatible subs, copying...");
}
response.preset =
", -map 0:v " + subMap + " -map 0:a -c:v copy -c:a copy " + subType;
@ -223,10 +226,10 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
response.FFmpegMode = true;
return response;
} else {
response.addInfo(GOOD, "File has no/compatible subs");
addInfo(GOOD, "File has no/compatible subs");
}
response.addInfo(GOOD, "File meets conditions!");
addInfo(GOOD, "File meets conditions!");
return response;
}

@ -4,14 +4,14 @@ const details = () => {
return {
id: "Tdarr_Plugin_drdd_standardise_all_in_one",
Stage: "Pre-processing",
Name: "DrDD H265 MKV AC3 audio subtitles [QSV & NVENC]",
Name: "DrDD H265 MKV AC3 audio subtitles [VAAPI & NVENC]",
Stage: "Pre-processing",
Type: "Video",
Operation: "Transcode",
Description:
"In a single pass ensures all files are in MKV containers and where possible encoded in h265 (settings dependant on file bitrate), converts all multi channel audio to AC3, removes audio commentary and removes subtitles that are not in the configured language or marked as commentary. This plugin is opinionated based on how I like my library to be configured and based on the work done by Migz with his plugins (Thanks!).",
"[Non-Windows] In a single pass ensures all files are in MKV containers and where possible encoded in h265 (settings dependant on file bitrate), converts all multi channel audio to AC3, removes audio commentary and removes subtitles that are not in the configured language or marked as commentary. This plugin is opinionated based on how I like my library to be configured and based on the work done by Migz with his plugins (Thanks!).",
Version: "1.0",
Tags: "pre-processing,ffmpeg,qsv h265, nvenc h265",
Tags: "pre-processing,ffmpeg,vaapi,h265, nvenc h265",
Inputs: [
{
name: "nvenc",
@ -346,7 +346,7 @@ function buildVideoConfiguration(inputs, file, logger) {
configuration.RemoveOutputSetting("-c:v copy");
configuration.AddOutputSetting(`-c:v hevc_vaapi ${bitrateSettings}`);
logger.AddError("Transcoding to HEVC using QSV");
logger.AddError("Transcoding to HEVC using VAAPI");
}
/**

@ -1,5 +1,6 @@
/* eslint-disable */
// tdarrSkipTest
const details = () => {
return {
id: "Tdarr_Plugin_e5c3_CnT_Add_Subtitles",

@ -1,5 +1,6 @@
/* eslint-disable */
// tdarrSkipTest
const details = () => {
return {
id: "Tdarr_Plugin_e5c3_CnT_Remove_Letterbox",

@ -1,3 +1,4 @@
// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_goof1_URL_Plex_Refresh',
Stage: 'Post-processing',

@ -1,5 +1,6 @@
/* eslint-disable no-await-in-loop */
module.exports.dependencies = ['axios', '@cospired/i18n-iso-languages'];
// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng',
Stage: 'Pre-processing',

@ -23,9 +23,6 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
FFmpegMode: true,
reQueueAfter: true,
infoLog: '',
file,
removeFromDB: false,
updateDB: false,
container: `.${file.container}`,
};

@ -1,3 +1,4 @@
// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_rr01_drpeppershaker_extract_subs_to_SRT',
Stage: 'Pre-processing',
@ -71,11 +72,11 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
let lang = '';
let title = 'none';
if (subStream.tags) {
if (subStream && subStream.tags && subStream.tags.language) {
lang = subStream.tags.language;
}
if (subStream.tags.title) {
if (subStream && subStream.tags && subStream.tags.title) {
title = subStream.tags.title;
}
@ -98,7 +99,9 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
const { index } = subStream;
if (fs.existsSync(`${subsFile}`)) {
response.infoLog += `${lang}.srt already exists. Skipping!\n`;
} else if (title.toLowerCase().includes('commentary') || title.toLowerCase().includes('description')) {
} else if (typeof title === 'string'
&& (title.toLowerCase().includes('commentary')
|| title.toLowerCase().includes('description'))) {
response.infoLog += `Stream ${i} ${lang}.srt is a ${title} track. Skipping!\n`;
} else {
response.infoLog += `Extracting ${lang}.srt\n`;

@ -6,7 +6,7 @@ const details = () => {
Name: "Remove Data Streams ",
Type: "Video",
Operation: "Transcode",
Description: `[Contains built-in filter] This plugin removes data streams if detected. The output container is the same as the original. Helps with issues like bin_data making files impossible to process. \n\n`,
Description: `[Contains built-in filter] This plugin removes data streams if detected. The output container is mkv. Helps with issues like bin_data making files impossible to process. \n\n`,
Version: "1.00",
Tags: "pre-processing,ffmpeg",
Inputs:[],

@ -67,8 +67,8 @@ const details = () => ({
},
{
name: 'bframe',
type: 'string',
defaultValue: '8',
type: 'number',
defaultValue: 8,
inputUI: {
type: 'text',
},
@ -100,8 +100,8 @@ const details = () => ({
},
{
name: 'sdDisabled',
type: 'string',
defaultValue: 'false',
type: 'boolean',
defaultValue: false,
inputUI: {
type: 'dropdown',
options: [
@ -116,16 +116,38 @@ const details = () => ({
},
{
name: 'uhdDisabled',
type: 'string',
defaultValue: 'false',
type: 'boolean',
defaultValue: false,
inputUI: {
type: 'text',
type: 'dropdown',
options: [
'false',
'true',
],
},
tooltip: `Input "true" if you want to skip 4k (UHD) files
\\nExample:\\n
true`,
},
{
name: 'force10bit',
type: 'boolean',
defaultValue: false,
inputUI: {
type: 'dropdown',
options: [
'false',
'true',
],
},
tooltip: `Specify if output file should be forced to 10bit. Default is false (bit depth is same as source).
\\nExample:\\n
true
\\nExample:\\n
false`,
},
],
});
@ -137,7 +159,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
let crf;
// default values that will be returned
const response = {
processFile: true,
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
@ -146,86 +168,68 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
infoLog: '',
};
// check if the file is a video, if not the function will be stopped immediately
// check if the file is a video, if not the plugin will exit
if (file.fileMedium !== 'video') {
response.processFile = false;
response.infoLog += '☒File is not a video! \n';
return response;
}
response.infoLog += '☑File is a video! \n';
// check if the file is SD and sdDisable is enabled
// check if the file is SD and sdDisabled is true
// skip this plugin if so
if (['480p', '576p'].includes(file.video_resolution) && inputs.sdDisabled === 'true') {
response.processFile = false;
response.infoLog += '☒File is SD, not processing\n';
if (inputs.sdDisabled && ['480p', '576p'].includes(file.video_resolution)) {
response.infoLog += '☒File is SD and disabled, not processing\n';
return response;
}
// check if the file is 4k and 4kDisable is enabled
// check if the file is 4k and uhdDisabled is true
// skip this plugin if so
if (file.video_resolution === '4KUHD' && inputs.uhdDisabled) {
response.processFile = false;
response.infoLog += '☒File is 4k/UHD, not processing\n';
if (inputs.uhdDisabled && file.video_resolution === '4KUHD') {
response.infoLog += '☒File is 4k/UHD and disabled, not processing\n';
return response;
}
// check if the file is already hevc
// it will not be transcoded if true and the plugin will be stopped immediately
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
if (file.ffProbeData.streams[i].codec_name && file.ffProbeData.streams[i].codec_name.toLowerCase() === 'hevc') {
response.processFile = false;
response.infoLog += '☑File is already in hevc! \n';
return response;
}
// check if the file contains a hevc track
// it will not be transcoded if true and the plugin will exit
if (file.ffProbeData.streams.filter((x) => x.codec_name.toLowerCase() === 'hevc').length) {
response.infoLog += '☑File is already in hevc! \n';
return response;
}
// if we made it to this point it is safe to assume there is no hevc stream
response.infoLog += '☒File is not hevc!\n';
// set sane input defaults if not configured
const sdCRF = inputs.sdCRF ? inputs.sdCRF : 20;
const hdCRF = inputs.hdCRF ? inputs.hdCRF : 22;
const fullhdCRF = inputs.fullhdCRF ? inputs.fullhdCRF : 24;
const uhdCRF = inputs.uhdCRF ? inputs.uhdCRF : 28;
const bframe = inputs.bframe ? inputs.bframe : 8;
// set preset to slow if not configured
let ffmpegPreset = 'slow';
if (!inputs.ffmpegPreset) {
response.infoLog += '☑Preset not set, defaulting to slow\n';
} else {
ffmpegPreset = `${inputs.ffmpegPreset}`;
response.infoLog += `☑Preset set as ${inputs.ffmpegPreset}\n`;
}
response.infoLog += `☑Preset set as ${inputs.ffmpegPreset}\n`;
// set crf by resolution
switch (file.video_resolution) {
case '480p':
case '576p':
crf = sdCRF;
crf = inputs.sdCRF;
break;
case '720p':
crf = hdCRF;
crf = inputs.hdCRF;
break;
case '1080p':
crf = fullhdCRF;
crf = inputs.fullhdCRF;
break;
case '4KUHD':
crf = uhdCRF;
crf = inputs.uhdCRF;
break;
default:
response.infoLog += 'Could for some reason not detect resolution, plugin will not proceed. \n';
response.processFile = false;
return response;
}
const pixel10Bit = inputs.force10bit ? ' -pix_fmt p010le' : '';
// encoding settings
response.preset += `,-map 0 -dn -c:v libx265 -preset ${ffmpegPreset}`
+ ` -x265-params crf=${crf}:bframes=${bframe}:rc-lookahead=32:ref=6:b-intra=1:aq-mode=3`
+ ' -a53cc 0 -c:a copy -c:s copy -max_muxing_queue_size 9999';
response.preset += `<io> -map 0 -dn -c:v libx265 -preset ${inputs.ffmpegPreset}`
+ ` -x265-params crf=${crf}:bframes=${inputs.bframe}:rc-lookahead=32:ref=6:b-intra=1:aq-mode=3`
+ ` ${pixel10Bit} -a53cc 0 -c:a copy -c:s copy -max_muxing_queue_size 9999`;
response.infoLog += `☑File is ${file.video_resolution}, using CRF value of ${crf}!\n`;
response.infoLog += 'File is being transcoded!\n';
response.processFile = true;
return response;
};

@ -1,5 +1,6 @@
/* eslint-disable */
// tdarrSkipTest
const details = () => {
return {
id: "Tdarr_Plugin_z18s_rename_files_based_on_codec",

@ -1,3 +1,4 @@
// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution',
Stage: 'Post-processing',

@ -4,6 +4,7 @@ module.exports.dependencies = [
'touch',
];
// tdarrSkipTest
const details = () => ({
id: 'Tdarr_Plugin_z80t_keep_original_date',
Stage: 'Post-processing',

165
package-lock.json generated

@ -117,9 +117,9 @@
"dev": true
},
"ansi-regex": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true
},
"ansi-styles": {
@ -174,6 +174,12 @@
"es-abstract": "^1.18.0-next.1"
}
},
"assertion-error": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
"dev": true
},
"ast-types-flow": {
"version": "0.0.7",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
@ -227,14 +233,27 @@
"callsites": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
"chai": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz",
"integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==",
"dev": true,
"requires": {
"assertion-error": "^1.1.0",
"check-error": "^1.0.2",
"deep-eql": "^3.0.1",
"get-func-name": "^2.0.0",
"loupe": "^2.3.1",
"pathval": "^1.1.1",
"type-detect": "^4.0.5"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@ -244,7 +263,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@ -253,7 +271,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
@ -261,26 +278,29 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
"integrity": "sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA==",
"dev": true
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@ -346,6 +366,15 @@
"ms": "2.1.2"
}
},
"deep-eql": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
"integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
"dev": true,
"requires": {
"type-detect": "^4.0.0"
}
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@ -474,6 +503,57 @@
"table": "^5.2.3",
"text-table": "^0.2.0",
"v8-compile-cache": "^2.0.3"
},
"dependencies": {
"ansi-styles": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"requires": {
"color-convert": "^2.0.1"
}
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
"dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"requires": {
"color-name": "~1.1.4"
}
},
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
"dev": true
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
"dev": true,
"requires": {
"has-flag": "^4.0.0"
}
}
}
},
"eslint-config-airbnb-base": {
@ -790,6 +870,12 @@
"integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
"dev": true
},
"get-func-name": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
"integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
"dev": true
},
"get-intrinsic": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz",
@ -873,10 +959,9 @@
"dev": true
},
"import-fresh": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.2.tgz",
"integrity": "sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==",
"dev": true,
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
"integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
"requires": {
"parent-module": "^1.0.0",
"resolve-from": "^4.0.0"
@ -1091,8 +1176,16 @@
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"dev": true
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"loupe": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.4.tgz",
"integrity": "sha512-OvKfgCC2Ndby6aSTREl5aCCPTNIzlDfQZvZxNUrBrihDhL3xcrYegTblhmEiCrg2kKQz4XsFIaemE5BF4ybSaQ==",
"dev": true,
"requires": {
"get-func-name": "^2.0.0"
}
},
"lru-cache": {
"version": "6.0.0",
@ -1113,9 +1206,9 @@
}
},
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"version": "1.2.6",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz",
"integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==",
"dev": true
},
"ms": {
@ -1249,7 +1342,6 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
"dev": true,
"requires": {
"callsites": "^3.0.0"
}
@ -1296,6 +1388,12 @@
"pify": "^2.0.0"
}
},
"pathval": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
"dev": true
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
@ -1375,8 +1473,7 @@
"resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
"dev": true
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="
},
"rimraf": {
"version": "3.0.2",
@ -1472,9 +1569,9 @@
},
"dependencies": {
"ansi-regex": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
"integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz",
"integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==",
"dev": true
},
"strip-ansi": {
@ -1577,6 +1674,12 @@
"prelude-ls": "^1.2.1"
}
},
"type-detect": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
"integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
"dev": true
},
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",

@ -1,17 +1,22 @@
{
"name": "tdarr_plugins",
"version": "1.0.0",
"description": "There are two types of plugin:",
"main": "Tdarr_Plugin_aaaa_Pre_Proc_Example.js",
"dependencies": {},
"description": "Tdar Plugins Repo",
"main": "",
"dependencies": {
"chalk": "^4.1.2",
"import-fresh": "^3.3.0",
"lodash": "^4.17.21"
},
"devDependencies": {
"chai": "^4.3.6",
"eslint": "^7.14.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsx-a11y": "^6.4.1"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"test": "node ./tests/runTests.js",
"lint": "eslint Community methods examples tests --ext js",
"lint:fix": "eslint Community methods examples tests --ext js --fix",
"checkPlugins": "node ./tests/checkPlugins.js"

@ -0,0 +1,92 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:1 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 aac -ac 2',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: 'The required channel count 2 is lower than the highest available channel count (6). Adding! \n',
handbrakeMode: false,
ffmpegMode: true,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
audioCodec: 'eac3',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:1 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 eac3 -ac 2',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: 'The required channel count 2 is lower than the highest available channel count (6). Adding! \n',
handbrakeMode: false,
ffmpegMode: true,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
audioCodec: 'eac3',
channels: 6,
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:1 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 eac3 -ac 6',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: 'The required channel count 6 is lower than the highest available channel count (6). Adding! \n',
handbrakeMode: false,
ffmpegMode: true,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
audioCodec: 'eac3',
channels: 8,
language: 'fr',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:1 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 eac3 -ac 6',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: 'The required channel count (8) is higher than the highest channel available in specified lang tag (6). Adding lower channel track. \n',
handbrakeMode: false,
ffmpegMode: true,
},
},
];
run(tests);

@ -0,0 +1,74 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Very Fast 1080p30" -e x265 --all-subtitles',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: 'File is being transcoded using HandBrake \n',
handbrakeMode: true,
ffmpegMode: false,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
handbrakePreset: 'Fast 576p25',
videoEncoder: 'nvenc_h265',
keepSubtitles: 'true',
container: 'mp4',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Fast 576p25" -e nvenc_h265 --all-subtitles',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: 'File is being transcoded using HandBrake \n',
handbrakeMode: true,
ffmpegMode: false,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
handbrakePreset: 'Fast 576p25',
videoEncoder: 'nvenc_h265',
keepSubtitles: 'false',
container: 'mov',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Fast 576p25" -e nvenc_h265 ',
container: '.mov',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: 'File is being transcoded using HandBrake \n',
handbrakeMode: true,
ffmpegMode: false,
},
},
];
run(tests);

@ -0,0 +1,66 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '<io> -map 0 -c copy',
container: '.mkv',
handbrakeMode: false,
ffmpegMode: true,
reQueueAfter: true,
infoLog: 'File is being transcoded using custom arguments \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
cli: 'handbrake',
arguments: '-Z "Very Fast 1080p30" --all-subtitles --all-audio',
container: 'mp4',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Very Fast 1080p30" --all-subtitles --all-audio',
container: '.mp4',
handbrakeMode: true,
ffmpegMode: false,
reQueueAfter: true,
infoLog: 'File is being transcoded using custom arguments \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
cli: 'ffmpeg',
arguments: '<io>-c:v libx265 -crf 23 -ac 6 -c:a aac -preset veryfast',
container: 'mov',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '<io>-c:v libx265 -crf 23 -ac 6 -c:a aac -preset veryfast',
container: '.mov',
handbrakeMode: false,
ffmpegMode: true,
reQueueAfter: true,
infoLog: 'File is being transcoded using custom arguments \n',
},
},
];
run(tests);

@ -0,0 +1,89 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:1 -map 0:s? -map 0:d? -c copy -c:a:0 aac -ac 2',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'No en streams. The required channel count 2 is lower than the highest available channel count (6).Adding it and removing others! \n',
handbrakeMode: false,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
audioCodec: 'eac3',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:1 -map 0:s? -map 0:d? -c copy -c:a:0 eac3 -ac 2',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'No en streams. The required channel count 2 is lower than the highest available channel count (6).Adding it and removing others! \n',
handbrakeMode: false,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
audioCodec: 'eac3',
language: 'fr',
channels: '6',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:1 -map 0:s? -map 0:d? -c copy -c:a:0 eac3 -ac 6',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'No fr streams. The required channel count 6 is lower than the highest available channel count (6).Adding it and removing others! \n',
handbrakeMode: false,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
audioCodec: 'aac',
language: 'und',
channels: '8',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'The best und stream already exists. It is the only audio stream. \n',
handbrakeMode: false,
},
},
];
run(tests);

@ -0,0 +1,72 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
infoLog: 'Streams are in the correct order!',
},
},
{
// orig
// 0 vid h264
// 1 flac eng
// 2 ac3 eng
// 3 eac3 eng
// 4 aac fre
// 5 aac eng
// 6 sub fre
// expect
// 4 aac fre
// 2 ac3 eng
// 1 flac eng
// 3 eac3 eng
// 5 aac eng
// 6 sub fre
// 0 vid h264
// console.log(streams.map(stream => {
// return {
// "index": stream.index,
// codec_name: stream.codec_name,
// codec_type: stream.codec_type,
// language: stream.tags.language,
// }
// }))
input: {
file: require('../sampleData/media/sampleH264_2.json'),
librarySettings: {},
inputs: {
processOrder: 'codecs,channels,languages,streamTypes',
languages: 'fre,eng',
streamTypes: 'audio,subtitle,video',
codecs: 'ac3,flac,eac3,aac',
channels: '7.1,5.1,2,1',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '<io> -c copy -map 0:4 -map 0:2 -map 0:1 -map 0:3 -map 0:5 -map 0:6 -map 0:0',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
infoLog: 'Streams are not in the correct order!',
},
},
];
run(tests);

@ -0,0 +1,65 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -c copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is not in mkv \n',
handbrakeMode: false,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
container: 'mkv',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -c copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is not in mkv \n',
handbrakeMode: false,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
container: 'mp4',
},
otherArguments: {},
},
output: {
processFile: false,
preset: ', -map 0 -c copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is already in mp4 \n',
handbrakeMode: false,
},
},
];
run(tests);

@ -0,0 +1,66 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: "File does not have any audio streams which aren't in aac \n",
handbrakeMode: false,
ffmpegMode: true,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_2.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 aac -c:a:1 aac -c:a:2 aac',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: "File has audio streams which aren't in aac \n",
handbrakeMode: false,
ffmpegMode: true,
},
},
{
input: {
file: require('../sampleData/media/sampleH264_2.json'),
librarySettings: {},
inputs: {
audioCodec: 'eac3',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 eac3 -c:a:1 eac3 -c:a:3 eac3 -c:a:4 eac3',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: "File has audio streams which aren't in eac3 \n",
handbrakeMode: false,
ffmpegMode: true,
},
},
];
run(tests);

@ -0,0 +1,64 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_2.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
infoLog: '☑File bitrate is within filter limits. Moving to next plugin.',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
upperBound: 500,
lowerBound: 0,
},
otherArguments: {},
},
output: {
processFile: false,
infoLog: '☒File bitrate is not within filter limits. Breaking out of plugin stack.\n',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
upperBound: 10000,
lowerBound: 0,
},
otherArguments: {},
},
output: {
processFile: true,
infoLog: '☑File bitrate is within filter limits. Moving to next plugin.',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
upperBound: 10000,
lowerBound: 9000,
},
otherArguments: {},
},
output: {
processFile: false,
infoLog: '☒File bitrate is not within filter limits. Breaking out of plugin stack.\n',
},
},
];
run(tests);

@ -0,0 +1,59 @@
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: { processFile: false, infoLog: '' },
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecsToProcess: 'h264',
},
otherArguments: {},
},
output: { processFile: true, infoLog: 'File is in codecsToProcess. Moving to next plugin.' },
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecsToProcess: 'h265',
},
otherArguments: {},
},
output: { processFile: false, infoLog: 'File is not in codecsToProcess. Breaking out of plugin stack.' },
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecsToNotProcess: 'h264',
},
otherArguments: {},
},
output: { processFile: false, infoLog: 'File is in codecsToNotProcess. Breaking out of plugin stack.' },
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecsToNotProcess: 'h265',
},
otherArguments: {},
},
output: { processFile: true, infoLog: 'File is not in codecsToNotProcess. Moving to next plugin.' },
},
];
run(tests);

@ -0,0 +1,59 @@
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: { processFile: false, infoLog: '' },
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecTagStringsToProcess: 'avc1,rand',
},
otherArguments: {},
},
output: { processFile: true, infoLog: 'File is in codecTagStringsToProcess. Moving to next plugin.' },
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecTagStringsToNotProcess: 'avc1,rand',
},
otherArguments: {},
},
output: { processFile: false, infoLog: 'File is in codecTagStringsToNotProcess. Breaking out of plugin stack.' },
},
{
input: {
file: require('../sampleData/media/sampleH265_1.json'),
librarySettings: {},
inputs: {
codecTagStringsToProcess: 'avc1,rand',
},
otherArguments: {},
},
output: { processFile: false, infoLog: 'File is not in codecTagStringsToProcess. Breaking out of plugin stack.' },
},
{
input: {
file: require('../sampleData/media/sampleH265_1.json'),
librarySettings: {},
inputs: {
codecTagStringsToNotProcess: 'avc1,rand',
},
otherArguments: {},
},
output: { processFile: true, infoLog: 'File is not in codecTagStringsToNotProcess. Moving to next plugin.' },
},
];
run(tests);

@ -0,0 +1,74 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false, infoLog: '',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
resolutionsToProcess: '480p,720p',
},
otherArguments: {},
},
output: {
processFile: true,
infoLog: 'File is in resolutionsToProcess. Moving to next plugin.',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
resolutionsToProcess: '480p,1080p',
},
otherArguments: {},
},
output: {
processFile: false,
infoLog: 'File is not in resolutionsToProcess. Breaking out of plugin stack.',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
resolutionsToNotProcess: '480p,720p',
},
otherArguments: {},
},
output: {
processFile: false,
infoLog: 'File is in resolutionsToNotProcess. Breaking out of plugin stack.',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
resolutionsToNotProcess: '480p,1080p',
},
otherArguments: {},
},
output: {
processFile: true,
infoLog: 'File is not in resolutionsToNotProcess. Moving to next plugin.',
},
},
];
run(tests);

@ -0,0 +1,64 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
infoLog: 'File is within lower and upper bound size limits. Moving to next plugin.',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
upperBound: 0.5,
lowerBound: 0,
},
otherArguments: {},
},
output: {
processFile: false,
infoLog: 'File is not within lower and upper bound size limits. Breaking out of plugin stack.',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
upperBound: 2,
lowerBound: 0,
},
otherArguments: {},
},
output: {
processFile: true,
infoLog: 'File is within lower and upper bound size limits. Moving to next plugin.',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
upperBound: 4,
lowerBound: 2,
},
otherArguments: {},
},
output: {
processFile: false,
infoLog: 'File is not within lower and upper bound size limits. Breaking out of plugin stack.',
},
},
];
run(tests);

@ -0,0 +1,41 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:v:0 libx265 -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n☒File is not hevc! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH265_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is a video! \n☑File is already in hevc! \n',
},
},
];
run(tests);

@ -0,0 +1,81 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Very Fast 1080p30" --all-subtitles --all-audio',
container: '.mkv',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not in desired codec! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH265_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is already in hevc! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecs_to_exclude: 'h264',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is already in h264! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecs_to_exclude: 'hevc',
cli: 'handbrake',
transcode_arguments: '-Z "Very Fast 480p30"',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Very Fast 480p30"',
container: '.mkv',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not in desired codec! \n',
},
},
];
run(tests);

@ -0,0 +1,41 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a -c copy -c:v:0 libx265 -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n☒File is not hevc! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH265_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is a video! \n☑File is already in hevc! \n',
},
},
];
run(tests);

@ -0,0 +1,41 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a -c copy -c:v:0 libx265 -crf 20',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n☒File is not hevc! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH265_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is a video! \n☑File is already in hevc! \n',
},
},
];
run(tests);

@ -0,0 +1,41 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a -c copy -c:v:0 hevc_nvenc -crf 20',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n☒File is not hevc! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH265_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is a video! \n☑File is already in hevc! \n',
},
},
];
run(tests);

@ -0,0 +1,43 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_2.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑ Preferred language is already first audio track! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_2.json'),
librarySettings: {},
inputs: {
preferred_language: 'fre',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -c copy -map 0:v -map 0:a:3 -disposition:a:0 default -map 0:a:0 -map 0:a:1 -disposition:a:1 0 -map 0:a:2 -disposition:a:2 0 -disposition:a:3 0 -map 0:a:4 -disposition:a:4 0 -map 0:s? -map 0:d? ',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒ Desired audio lang is not first audio stream, moving! \n',
},
},
];
run(tests);

@ -0,0 +1,62 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_2.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☒ No subtitle tracks in desired language! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_2.json'),
librarySettings: {},
inputs: {
preferred_language: 'fre',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑ Preferred language is already first subtitle track! \n',
},
}, {
input: {
file: require('../sampleData/media/sampleH264_3.json'),
librarySettings: {},
inputs: {
preferred_language: 'fre',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -c copy -map 0:v -map 0:a -map 0:s:1 -disposition:s:0 default -map 0:s:0 -disposition:s:1 0 -map 0:d? ',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒ Desired subtitle lang is not first subtitle stream, moving! \n',
},
},
];
run(tests);

@ -0,0 +1,44 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑ File is already in h264, no need to transcode! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH265_1.json'),
librarySettings: {},
inputs: {
handbrake_preset: 'Very Fast 1080p30',
output_container: '.mp4',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Very Fast 1080p30" -e nvenc_h264 --all-audio --all-subtitles',
container: '.mp4',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒ File is not in h264, transcoding! \n',
},
},
];
run(tests);

@ -0,0 +1,112 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v h264_cuvid ,-map 0:v -map 0:a -map 0:s? -map -:d? -c copy -c:v:0 hevc_nvenc -preset medium -profile:v main10 -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -max_muxing_queue_size 4096 -b:v 967680 -maxrate 1257984 -minrate 677376 -bufsize 1205959 -map_metadata:g -1',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'No valid resolution selected, defaulting to 8KUHD.\n'
+ 'Video details: h264-720p \n'
+ ' 1280x720x25@8 bits.\n'
+ 'Video bitrate is 1206Kbps, overall is 1591Kbps. Calculated target is 1613Kbps.\n'
+ '☒H264 Resolution is 720p, bitrate was 1206Kbps. HEVC target bitrate will be 968Kbps.\n'
+ '☒Transcoding to HEVC.',
},
}, {
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {
ffmpegPreset: 'veryslow',
container: 'mkv',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v hevc_cuvid ,-map 0:v -map 0:a -map 0:s? -map -:d? -c copy -c:v:0 hevc_nvenc -preset veryslow -profile:v main10 -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -max_muxing_queue_size 4096 -b:v 3207442 -maxrate 4717440 -minrate 2540160 -bufsize 3628800 -map_metadata:g -1',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'No valid resolution selected, defaulting to 8KUHD.\n'
+ 'Video details: hevc-1080p \n'
+ ' 1920x1080x25@8 bits.\n'
+ 'Video bitrate is NaNKbps, overall is 3207Kbps. Calculated target is 3629Kbps.\n'
+ '☒HEVC Bitrate for 1080p could not be determined, \n'
+ ' using sensible default of 3207Kbps.\n'
+ '☒Transcoding to HEVC.',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {
maxResolution: '720p',
ffmpegPreset: 'veryslow',
container: 'mkv',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v hevc_cuvid -resize 1280x720 ,-map 0:v -map 0:a -map 0:s? -map -:d? -c copy -c:v:0 hevc_nvenc -preset veryslow -profile:v main10 -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -max_muxing_queue_size 4096 -b:v 1612800 -maxrate 2096640 -minrate 1128960 -bufsize 1612800 -map_metadata:g -1',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Resizing to 1280x720.\n'
+ 'Video details: hevc-1080p \n'
+ ' 1920x1080x25@8 bits.\n'
+ 'Video bitrate is NaNKbps, overall is 3207Kbps. Calculated target is 1613Kbps.\n'
+ '☒HEVC Bitrate for 1080p could not be determined, \n'
+ ' using sensible default of 1613Kbps.\n'
+ '☒Transcoding to HEVC.',
},
},
{
input: {
file: (() => {
// modify so no processing needed
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[0].codec_name = 'hevc';
return file;
})(),
librarySettings: {},
inputs: {
maxResolution: '1080p',
ffmpegPreset: 'veryslow',
container: 'mkv',
compressionFactor: '1',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '-vsync 0 -hwaccel cuda -hwaccel_output_format cuda -c:v hevc_cuvid ,-map 0:v -map 0:a -map 0:s? -map -:d? -c copy -c:v:0 hevc_nvenc -preset veryslow -profile:v main10 -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -max_muxing_queue_size 4096 ',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Video details: hevc-1080p \n'
+ ' 1918x1080x25@8 bits.\n'
+ 'Video bitrate is 6454Kbps, overall is 8249Kbps. Calculated target is 51786Kbps.\n'
+ '☑HEVC Bitrate is within limits.\n',
},
},
];
run(tests);

@ -0,0 +1,75 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.mkv',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Removing audio track in language und\n'
+ '☒ *** All audio tracks would have been removed. Defaulting to keeping all tracks for this file.\n'
+ '☒ Transcoding to HEVC using NVidia NVENC\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ' -c:v h264_cuvid,-map 0 -map -0:d -c:v hevc_nvenc -qmin 0 -cq:v 30 -b:v 602k -maxrate:v 2602k -preset medium -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy -max_muxing_queue_size 9999 -bf 5 -analyzeduration 2147483647 -probesize 2147483647',
reQueueAfter: true,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.mkv',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☑ File is in HEVC codec and in MKV\n'
+ '☑ No video processing necessary\n'
+ '☑ No subtitle processing necessary\n'
+ '☑ No need to process file',
processFile: false,
preset: ',-map 0 -map -0:d -c:v copy -c:a copy -c:s copy -max_muxing_queue_size 9999 -bf 5 -analyzeduration 2147483647 -probesize 2147483647',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].bit_rate = undefined;
return file;
})(),
librarySettings: {},
inputs: {
target_bitrate_720p: '1500000',
},
otherArguments: {},
},
output: {
container: '.mkv',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Removing audio track in language und\n'
+ '☒ *** All audio tracks would have been removed. Defaulting to keeping all tracks for this file.\n'
+ '☒ Transcoding to HEVC using NVidia NVENC\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ' -c:v h264_cuvid,-map 0 -map -0:d -c:v hevc_nvenc -qmin 0 -cq:v 30 -b:v 1500k -maxrate:v 3500k -preset medium -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy -max_muxing_queue_size 9999 -bf 5 -analyzeduration 2147483647 -probesize 2147483647',
reQueueAfter: true,
},
},
];
run(tests);

@ -0,0 +1,139 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
win32: {
container: '.mkv',
processFile: true,
preset: ', -sn -map 0:v -c:v hevc_qsv -load_plugin hevc_hw -b:v 758k -minrate 530k -maxrate 985k -bufsize 1517k -map 0:a -c:a copy ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Converting video, NOT resizing. 720p, h264 --> 720p, hevc. bitrate = 1517 --> 758, multiplier 0.5. \n'
+ 'Not converting audio. \n'
+ '2 channels - \n'
+ '6 channels - und aac \n'
+ '8 channels - ',
},
linux: false,
darwin: {
container: '.mkv',
processFile: true,
preset: ', -sn -map 0:v -c:v hevc_videotoolbox -profile main -b:v 758k -minrate 530k -maxrate 985k -bufsize 1517k -map 0:a -c:a copy ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Converting video, NOT resizing. 720p, h264 --> 720p, hevc. bitrate = 1517 --> 758, multiplier 0.5. \n'
+ 'Not converting audio. \n'
+ '2 channels - \n'
+ '6 channels - und aac \n'
+ '8 channels - ',
},
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.mkv',
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is processed already, nothing to do',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
resize: 'true',
},
otherArguments: {},
},
output: {
container: '.mkv',
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is processed already, nothing to do',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
bitrate_cutoff: '6000',
},
otherArguments: {},
},
output: {
win32: {
container: '.mkv',
processFile: true,
preset: ', -sn -map 0:v -c:v hevc_qsv -load_plugin hevc_hw -b:v 3933k -minrate 2753k -maxrate 5112k -bufsize 7866k -map 0:a -c:a copy ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Converting video, NOT resizing. 1080p, h264 --> 1080p, hevc. bitrate = 7866 --> 3933, multiplier 0.5. \n'
+ 'Not converting audio. \n'
+ '2 channels - eng flac \n'
+ '6 channels - \n'
+ '8 channels - ',
},
linux: false,
darwin: {
container: '.mkv',
processFile: true,
preset: ', -sn -map 0:v -c:v hevc_videotoolbox -profile main -b:v 3933k -minrate 2753k -maxrate 5112k -bufsize 7866k -map 0:a -c:a copy ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Converting video, NOT resizing. 1080p, h264 --> 1080p, hevc. bitrate = 7866 --> 3933, multiplier 0.5. \n'
+ 'Not converting audio. \n'
+ '2 channels - eng flac \n'
+ '6 channels - \n'
+ '8 channels - ',
},
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
bitrate_cutoff: '8000',
},
otherArguments: {},
},
output: {
container: '.mkv',
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is processed already, nothing to do',
},
},
];
run(tests);

@ -0,0 +1,59 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
preset: ', -map_metadata 0 -id3v2_version 3 -b:a 320k',
container: '.mp3',
handbrakeMode: false,
ffmpegMode: true,
processFile: false,
reQueueAfter: true,
infoLog: 'undefined☒Codec excluded \n ☑Codec not excluded \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleAAC_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
preset: ', -map_metadata 0 -id3v2_version 3 -b:a 320k',
container: '.mp3',
handbrakeMode: false,
ffmpegMode: true,
processFile: false,
reQueueAfter: true,
infoLog: 'undefined☒Codec excluded \n ☑Codec not excluded \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleMP3_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
preset: ', -map_metadata 0 -id3v2_version 3 -b:a 320k',
container: '.mp3',
handbrakeMode: false,
ffmpegMode: true,
processFile: false,
reQueueAfter: true,
infoLog: 'undefined☒Codec excluded \n ☒Codec excluded \n',
},
},
];
run(tests);

@ -0,0 +1,145 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-c:v h264_cuvid, -map 0 -c:v hevc_nvenc -cq:v 19 -b:v 758k -minrate 530k -maxrate 985k -bufsize 1517k -spatial_aq:v 1 -rc-lookahead:v 32 -c:a copy -c:s copy -max_muxing_queue_size 9999 ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Container for output selected as mkv. \n'
+ 'Current bitrate = 1517 \n'
+ 'Bitrate settings: \n'
+ 'Target = 758 \n'
+ 'Minimum = 530 \n'
+ 'Maximum = 985 \n'
+ 'File is not hevc or vp9. Transcoding. \n',
container: '.mkv',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is already hevc or vp9 & in mkv. \n',
container: '.mkv',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
enable_10bit: 'true',
force_conform: 'true',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-c:v h264_cuvid, -map 0 -c:v hevc_nvenc -cq:v 19 -b:v 758k -minrate 530k -maxrate 985k -bufsize 1517k -spatial_aq:v 1 -rc-lookahead:v 32 -c:a copy -c:s copy -max_muxing_queue_size 9999 -pix_fmt p010le ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Container for output selected as mp4. \n'
+ 'Current bitrate = 1517 \n'
+ 'Bitrate settings: \n'
+ 'Target = 758 \n'
+ 'Minimum = 530 \n'
+ 'Maximum = 985 \n'
+ 'File is not hevc or vp9. Transcoding. \n',
container: '.mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
enable_10bit: 'true',
force_conform: 'true',
bitrate_cutoff: '10000',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Current bitrate is below set cutoff of 10000. Cancelling plugin. \n',
container: '.mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
enable_10bit: 'true',
force_conform: 'true',
bitrate_cutoff: '1000',
},
otherArguments: {},
},
output: {
processFile: true,
preset: '-c:v h264_cuvid, -map 0 -c:v hevc_nvenc -cq:v 19 -b:v 758k -minrate 530k -maxrate 985k -bufsize 1517k -spatial_aq:v 1 -rc-lookahead:v 32 -c:a copy -c:s copy -max_muxing_queue_size 9999 -pix_fmt p010le ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Container for output selected as mp4. \n'
+ 'Current bitrate = 1517 \n'
+ 'Bitrate settings: \n'
+ 'Target = 758 \n'
+ 'Minimum = 530 \n'
+ 'Maximum = 985 \n'
+ 'File is not hevc or vp9. Transcoding. \n',
container: '.mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
force_conform: 'false',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -c copy ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is hevc or vp9 but is not in mp4 container. Remuxing. \n',
container: '.mp4',
},
},
];
run(tests);

@ -0,0 +1,145 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0 -c:v libx265 -b:v 758k -minrate 530k -maxrate 985k -bufsize 1517k -c:a copy -c:s copy -max_muxing_queue_size 9999 ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Container for output selected as mkv. \n'
+ 'Current bitrate = 1517 \n'
+ 'Bitrate settings: \n'
+ 'Target = 758 \n'
+ 'Minimum = 530 \n'
+ 'Maximum = 985 \n'
+ 'File is not hevc or vp9. Transcoding. \n',
container: '.mkv',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is already hevc or vp9 & in mkv. \n',
container: '.mkv',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
enable_10bit: 'true',
force_conform: 'true',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0 -c:v libx265 -b:v 758k -minrate 530k -maxrate 985k -bufsize 1517k -c:a copy -c:s copy -max_muxing_queue_size 9999 -pix_fmt p010le ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Container for output selected as mp4. \n'
+ 'Current bitrate = 1517 \n'
+ 'Bitrate settings: \n'
+ 'Target = 758 \n'
+ 'Minimum = 530 \n'
+ 'Maximum = 985 \n'
+ 'File is not hevc or vp9. Transcoding. \n',
container: '.mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
enable_10bit: 'true',
force_conform: 'true',
bitrate_cutoff: '10000',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Current bitrate is below set bitrate cutoff of 10000. Nothing to do, cancelling plugin. \n',
container: '.mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
enable_10bit: 'true',
force_conform: 'true',
bitrate_cutoff: '1000',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0 -c:v libx265 -b:v 758k -minrate 530k -maxrate 985k -bufsize 1517k -c:a copy -c:s copy -max_muxing_queue_size 9999 -pix_fmt p010le ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Container for output selected as mp4. \n'
+ 'Current bitrate = 1517 \n'
+ 'Bitrate settings: \n'
+ 'Target = 758 \n'
+ 'Minimum = 530 \n'
+ 'Maximum = 985 \n'
+ 'File is not hevc or vp9. Transcoding. \n',
container: '.mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
force_conform: 'false',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -c copy ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File is hevc or vp9 but is not in mp4 container. Remuxing. \n',
container: '.mp4',
},
},
];
run(tests);

@ -0,0 +1,45 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -c copy -max_muxing_queue_size 9999 ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File is mp4 but requested to be mkv container. Remuxing. \n',
container: '.mkv',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
force_conform: 'true',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is already in mp4 container. \n',
container: '.mp4',
},
},
];
run(tests);

@ -0,0 +1,46 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -metadata title= -c copy -map 0 -max_muxing_queue_size 9999',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File has title metadata. Removing \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.meta.Title = undefined;
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File has no title metadata \n',
},
},
];
run(tests);

@ -0,0 +1,98 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: "☑File doesn't contain audio tracks which are unwanted or that require tagging.\n",
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
language: 'eng',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -map -0:a:3 -c copy -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒Audio stream 0:a:3 has unwanted language tag fre, removing. \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[3].tags.title = 'description';
return file;
})(),
librarySettings: {},
inputs: {
language: 'eng',
commentary: 'true',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -map -0:a:2 -map -0:a:3 -c copy -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒Audio stream 0:a:2 detected as being descriptive, removing. \n'
+ '☒Audio stream 0:a:3 has unwanted language tag fre, removing. \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[2].tags.title = 'description';
file.ffProbeData.streams[3].tags.language = 'und';
return file;
})(),
librarySettings: {},
inputs: {
language: 'eng',
commentary: 'true',
tag_language: 'eng',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -map -0:a:1 -map -0:a:2 -metadata:s:a:2 language=eng -map -0:a:3 -c copy -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒Audio stream 0:a:1 detected as being descriptive, removing. \n'
+ '☒Audio stream 0:a:2 has unwanted language tag und, removing. \n'
+ '☒Audio stream 0:a:2 detected as having no language, tagging as eng. \n'
+ '☒Audio stream 0:a:3 has unwanted language tag fre, removing. \n',
},
},
];
run(tests);

@ -0,0 +1,72 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: "☑File doesn't contain subtitle tracks which are unwanted or that require tagging.\n",
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -map -0:s:0 -c copy -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒Subtitle stream 0:s:0 has unwanted language tag fre, removing. \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[7] = _.cloneDeep(file.ffProbeData.streams[6]);
file.ffProbeData.streams[6].tags.title = 'description';
file.ffProbeData.streams[7].tags.language = 'und';
return file;
})(),
librarySettings: {},
inputs: {
language: 'eng,und',
commentary: 'true',
tag_language: 'eng',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -map -0:s:0 -map -0:s:0 -metadata:s:s:1 language=eng -c copy -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒Subtitle stream 0:s:0 has unwanted language tag fre, removing. \n'
+ '☒Subtitle stream 0:s:0 detected as being descriptive, removing. \n'
+ '☒Subtitle stream 0:s:1 has no language, tagging as eng. \n',
},
},
];
run(tests);

@ -0,0 +1,90 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒Plugin has not been configured, please configure required options. Skipping this plugin. \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
aac_stereo: 'true',
},
otherArguments: {},
},
output: {
processFile: false,
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File contains all required audio formats. \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
aac_stereo: 'true',
},
otherArguments: {},
},
output: {
processFile: true,
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒Audio track is 2 channel but is not AAC. Converting. \n'
+ '☒Audio track is 2 channel but is not AAC. Converting. \n'
+ '☒Audio track is 2 channel but is not AAC. Converting. \n',
preset: ', -map 0 -c:v copy -c:a copy -c:a:0 aac -c:a:1 aac -c:a:2 aac -strict -2 -c:s copy -max_muxing_queue_size 9999 ',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[1].channels = 8;
return file;
})(),
librarySettings: {},
inputs: {
aac_stereo: 'false',
downmix: 'true',
},
otherArguments: {},
},
output: {
processFile: true,
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒Audio track is 8 channel, no 6 channel exists. Creating 6 channel from 8 channel. \n',
preset: ', -map 0 -c:v copy -c:a copy -map 0:1 -c:a:0 ac3 -ac 6 -metadata:s:a:0 title="5.1" -strict -2 -c:s copy -max_muxing_queue_size 9999 ',
},
},
];
run(tests);

@ -0,0 +1,103 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
infoLog: '☑ Streams are in expected order. \n ',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
infoLog: '☑ Streams are in expected order. \n ',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[1].channels = 8;
file.ffProbeData.streams[2].channels = 6;
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:0 -map 0:3 -map 0:4 -map 0:5 -map 0:2 -map 0:1 -map 0:6 -c copy -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
infoLog: '☒ Audio 6ch not second. \n'
+ '☒ Audio 2ch not first. \n'
+ '☒ Audio 2ch not first. \n'
+ '☒ Audio 2ch not first. \n'
+ '☒ Streams are out of order, reorganizing streams. Video, Audio, Subtitles. \n',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[1].channels = 8;
file.ffProbeData.streams[2].channels = 6;
const video = file.ffProbeData.streams.splice(0, 1)[0];
file.ffProbeData.streams.push(video);
const subs = file.ffProbeData.streams.splice(5, 1)[0];
file.ffProbeData.streams.unshift(subs);
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:6 -map 0:3 -map 0:4 -map 0:5 -map 0:2 -map 0:1 -map 0:0 -c copy -max_muxing_queue_size 9999',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
infoLog: '☒ Audio not second. \n'
+ '☒ Audio not second. \n'
+ '☒ Audio 6ch not second. \n'
+ '☒ Audio not second. \n'
+ '☒ Audio 2ch not first. \n'
+ '☒ Audio not second. \n'
+ '☒ Audio 2ch not first. \n'
+ '☒ Audio not second. \n'
+ '☒ Audio 2ch not first. \n'
+ '☒ Video not first. \n'
+ '☒ Streams are out of order, reorganizing streams. Video, Audio, Subtitles. \n',
reQueueAfter: true,
},
},
];
run(tests);

@ -0,0 +1,46 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
container: '.mp4',
FFmpegMode: true,
reQueueAfter: true,
infoLog: "☑File doesn't contain any unwanted image format streams.\n",
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[0].codec_name = 'mjpeg';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0 -c copy -max_muxing_queue_size 9999 -map -v:0 ',
handBrakeMode: false,
container: '.mkv',
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File has image format stream, removing. \n',
},
},
];
run(tests);

@ -0,0 +1,47 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: "☑File doesn't contain subtitle or audio codecs which were unwanted or that require tagging.\n",
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
tag_subtitle_codecs: 'subrip',
tag_audio_codecs: 'aac',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -map -0:a:3 -map -0:a:4 -map -0:s:0 -c copy -max_muxing_queue_size 4096',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒audio stream detected as unwanted. removing audio stream 0:a:3 - Français E-AC3 2.0 - aac \n'
+ '☒audio stream detected as unwanted. removing audio stream 0:a:4 - Anglais E-AC3 2.0 - aac \n'
+ '☒Subtitle stream detected as unwanted. removing subtitle stream 0:s:0 - Français - subrip. \n',
},
},
];
run(tests);

@ -0,0 +1,107 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ' -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi ,-map 0:v -map 0:a -map 0:s? -map 0:d? -map 0:t? -c copy -c:v:0 hevc_vaapi -b:v 758k -minrate 530k -maxrate 985k -bufsize 1M -max_muxing_queue_size 1024 ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☒ Video stream 0 is not HEVC, transcode required.\n'
+ ' ☑ Stream analysis complete, processing required.\n'
+ ' ',
container: 'mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑ Stream analysis complete, no processing required.\n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
minBitrate: '4000',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ' -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi ,-map 0:v -map 0:a -map 0:s? -map 0:d? -map 0:t? -c copy ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: "☒ Input file's bitrate 1517 is lower than the minimum bitrate threshold of 4000. Skipping this plugin.\n"
+ '☑ Stream analysis complete, processing required.\n'
+ ' ',
container: 'mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
remuxOnly: 'true',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☒ RemuxOnly is enabled and file is not a remux. Unable to process.\n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.file = `remux ${file.file}`;
return file;
})(),
librarySettings: {},
inputs: {
remuxOnly: 'true',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ' -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi ,-map 0:v -map 0:a -map 0:s? -map 0:d? -map 0:t? -c copy -c:v:0 hevc_vaapi -b:v 3933k -minrate 2753k -maxrate 5112k -bufsize 1M -max_muxing_queue_size 1024 ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☒ Video stream 0 is not HEVC, transcode required.\n'
+ ' ☑ Stream analysis complete, processing required.\n'
+ ' ',
container: 'mkv',
},
},
];
run(tests);

@ -0,0 +1,106 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
infoLog: 'File is already H264 but file is not in mkv. Remuxing \n',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
container: '.mkv',
preset: ', -map 0 -c copy ',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.container = 'mkv';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
infoLog: 'File is already H264 and in mkv \n',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
container: '.mkv',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
infoLog: 'Target bitrate could not be calculated. Skipping this plugin. \n',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
container: '.mkv',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].codec_name = 'hevc';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
infoLog: 'Container for output selected as mkv. \n'
+ 'Current bitrate = 1526 \n'
+ 'Bitrate settings: \n'
+ 'Target = 1526 \n'
+ 'Minimum = 1068 \n'
+ 'Maximum = 1983 \n'
+ 'File is not h264. Transcoding. \n',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
container: '.mkv',
preset: 'undefined,-map 0 -c:v h264_nvenc -preset fast -crf 23 -tune film -b:v 1526k -minrate 1068k -maxrate 1983k -bufsize 1526k -c:a copy -c:s copy -max_muxing_queue_size 9999 -pix_fmt yuv420p ',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
},
otherArguments: {},
},
output: {
processFile: false,
infoLog: 'File is already H264 and in mp4 \n',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
container: '.mp4',
},
},
];
run(tests);

@ -0,0 +1,505 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 27 -threads 64 -speed 2 \n'
+ ' -quality good -static-thresh 0 -tile-columns 2 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 26 -threads 64 -speed 2 \n'
+ ' -quality good -static-thresh 0 -tile-columns 2 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.container = 'webm';
file.ffProbeData.streams[0].codec_name = 'vp9';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☑ File is in proper video format\n'
+ '☑ No video processing necessary\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -c:v copy \n -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.container = 'webm';
file.ffProbeData.streams[0].codec_name = 'vp9';
file.ffProbeData.streams[1].codec_name = 'opus';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☑ No audio processing necessary\n'
+ '☑ File is in proper video format\n'
+ '☑ No video processing necessary\n'
+ '☑ No subtitle processing necessary\n'
+ '☑ No need to process file',
processFile: false,
preset: ',-map 0 -map -0:d -c:v copy \n -c:a copy -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '16',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 28 -threads 64 -speed 2 \n'
+ ' -quality good -static-thresh 0 -tile-columns 2 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.video_resolution = '240p';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '16',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 33 -threads 64 -speed 1 \n'
+ ' -quality good -static-thresh 0 -tile-columns 0 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.video_resolution = '360p';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '16',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 32 -threads 64 -speed 1 \n'
+ ' -quality good -static-thresh 0 -tile-columns 1 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.video_resolution = '480p';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '16',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 29 -threads 64 -speed 1 \n'
+ ' -quality good -static-thresh 0 -tile-columns 1 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.video_resolution = '1080p';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '16',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 27 -threads 64 -speed 2 \n'
+ ' -quality good -static-thresh 0 -tile-columns 2 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.video_resolution = '4KUHD';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '17',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 16 -threads 64 -speed 2 \n'
+ ' -quality good -static-thresh 0 -tile-columns 3 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.video_resolution = '8KUHD';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '17',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Audio is not in proper codec, will format\n'
+ '☒ Transcoding file to VP9\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -pix_fmt yuv420p10le -c:v libvpx-vp9 -b:v 0 -crf 17 -threads 64 -speed 2 \n'
+ ' -quality good -static-thresh 0 -tile-columns 3 -tile-rows 0 -frame-parallel 0 -row-mt 1 \n'
+ ' -aq-mode 0 -g 240 \n'
+ ' -c:a libopus -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.container = 'webm';
file.ffProbeData.streams[0].codec_name = 'vp9';
file.ffProbeData.streams[1].codec_name = 'opus';
file.ffProbeData.streams[1].tags.language = 'eng';
file.ffProbeData.streams[2] = _.cloneDeep(file.ffProbeData.streams[1]);
file.ffProbeData.streams[2].codec_type = 'subtitle';
file.ffProbeData.streams[2].tags.title = 'commentary';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '17',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng,und',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☑ No audio processing necessary\n'
+ '☑ File is in proper video format\n'
+ '☑ No video processing necessary\n'
+ '☒ Removing Commentary or Description subtitle: commentary',
processFile: true,
preset: ',-map 0 -map -0:d -c:v copy \n -c:a copy -c:s copy -map -0:s:0',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.container = 'webm';
file.ffProbeData.streams[0].codec_name = 'vp9';
file.ffProbeData.streams[1].codec_name = 'opus';
file.ffProbeData.streams[1].tags.language = 'eng';
file.ffProbeData.streams[2] = _.cloneDeep(file.ffProbeData.streams[0]);
file.ffProbeData.streams[2].codec_name = 'mjpeg';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '17',
audio_language: 'eng,und,fre',
audio_commentary: 'true',
subtitle_language: 'eng,und',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☑ No audio processing necessary\n'
+ '☑ File is in proper video format\n'
+ '☒ Removing mjpeg\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -c:v copy -map -0:v:1 \n -c:a copy -c:s copy',
reQueueAfter: true,
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.container = 'webm';
file.ffProbeData.streams[0].codec_name = 'vp9';
file.ffProbeData.streams[1].codec_name = 'opus';
file.ffProbeData.streams[1].tags.language = 'eng';
file.ffProbeData.streams[2] = _.cloneDeep(file.ffProbeData.streams[1]);
file.ffProbeData.streams[2].tags.language = 'fre';
return file;
})(),
librarySettings: {},
inputs: {
CQ_240p: '33',
CQ_360p: '32',
CQ_480p: '29',
CQ_720p: '28',
CQ_1080p: '27',
CQ_4KUHD: '16',
CQ_8KUHD: '17',
audio_language: 'eng,und',
audio_commentary: 'true',
subtitle_language: 'eng,und',
subtitle_commentary: 'true',
remove_mjpeg: 'true',
},
otherArguments: {},
},
output: {
container: '.webm',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Removing audio track in language fre\n'
+ '☑ File is in proper video format\n'
+ '☑ No video processing necessary\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -c:v copy \n -c:a copy -map -0:a:1 -c:s copy',
reQueueAfter: true,
},
},
];
run(tests);

@ -0,0 +1,42 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File has title metadata \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File has no title metadata \n☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,66 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is already in h264! \n☑File has no subs \n☒File has title metadata \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Very Fast 1080p30"',
container: '.mp4',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not in h264! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.meta.Title = undefined;
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is already in h264! \n'
+ '☑File has no subs \n'
+ '☑File has no title metadata☑File has aac track \n'
+ '☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,66 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is already in h264! \n☑File has no subs \n☒File has title metadata \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "Fast 1080p30"',
container: '.mp4',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not in h264! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.meta.Title = undefined;
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is already in h264! \n'
+ '☑File has no subs \n'
+ '☑File has no title metadata☑File has aac track \n'
+ '☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,45 @@
/* eslint max-len: 0 */
const run = require('../helpers/run');
const tests = [
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑ File does not have any streams that need to be transcoded! \n',
},
},
{
input: {
file: require('../sampleData/media/sampleH264_1.json'),
librarySettings: {},
inputs: {
codecs_to_transcode: 'aac',
codec: 'eac3',
bitrate: '640k',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -c copy -map 0:v -map 0:1 -c:1 eac3 -b:a 640k -map 0:s? -map 0:d? -max_muxing_queue_size 9999',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: "☒ File has streams which aren't in desired codec! \n",
},
},
];
run(tests);

@ -0,0 +1,68 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {
originalLibraryFile: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'New file has size 1.008 MB which is 100% of original file size: 1.008 MB',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
upperBound: '110',
lowerBound: '35',
},
otherArguments: {
originalLibraryFile: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.file_size = 3;
return file;
})(),
},
},
output: 'New file size not within limits. New file has size 1.008 MB which is 33% of original file size: 3.000 MB. lowerBound is 35%',
error: {
shouldThrow: true,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
upperBound: '120',
lowerBound: '35',
},
otherArguments: {
originalLibraryFile: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.file_size = 0.1;
return file;
})(),
},
},
output: 'New file size not within limits. New file has size 1.008 MB which is 1007% of original file size: 0.100 MB. upperBound is 120%',
error: {
shouldThrow: true,
},
},
];
run(tests);

@ -0,0 +1,68 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {
originalLibraryFile: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'New file has duration 5.312 s which is 100.000% of original file duration: 5.312 s',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
upperBound: '110',
lowerBound: '35',
},
otherArguments: {
originalLibraryFile: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.mediaInfo.track.filter((row) => row['@type'] === 'General')[0].Duration = 20;
return file;
})(),
},
},
output: 'New file duration not within limits. New file has duration 5.312 s which is 26.560% of original file duration: 20 s. lowerBound is 35%',
error: {
shouldThrow: true,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
upperBound: '110',
lowerBound: '35',
},
otherArguments: {
originalLibraryFile: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.mediaInfo.track.filter((row) => row['@type'] === 'General')[0].Duration = 1;
return file;
})(),
},
},
output: 'New file duration not within limits. New file has duration 5.312 s which is 531.200% of original file duration: 1 s. upperBound is 110%',
error: {
shouldThrow: true,
},
},
];
run(tests);

@ -0,0 +1,45 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -c copy -c:v:0 libx265 -preset:v slow -pix_fmt yuv420p10le -x265-params "crf=22:aq-mode=3"',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File is not in hevc! \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑File is already in hevc! \n'
+ '☑ All audio streams are in aac! \n'
+ '☑File has no title metadata \n'
+ '☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,59 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0 -c:v copy -c:a copy -c:a:0 ac3 -c:s copy -c:d copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒ File has surround audio which is NOT in ac3! \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑ All surround audio streams are in ac3! \n☑File meets conditions! \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: '☑ All surround audio streams are in ac3! \n☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,160 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
linux: {
processFile: true,
preset: '-fflags +genpts -hwaccel qsv -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -b:v 759k -minrate 569k -maxrate 949k -bufsize 1517k -preset slow \n'
+ ' -c:a copy -c:s copy -max_muxing_queue_size 9999 ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑ It looks like the current bitrate is 1517k. \n'
+ '\n'
+ 'Container for output selected as mkv. \n'
+ 'Encode variable bitrate settings: \n'
+ 'Target = 759k \n'
+ 'Minimum = 569k \n'
+ 'Maximum = 949k \n'
+ 'File Transcoding... \n',
container: '.mkv',
},
win32: {
processFile: true,
preset: '-fflags +genpts -hwaccel qsv -c:v h264_qsv<io> -map 0 -c:v hevc_qsv -load_plugin hevc_hw -b:v 759k -minrate 569k -maxrate 949k -bufsize 1517k -preset slow \n'
+ ' -c:a copy -c:s copy -max_muxing_queue_size 9999 ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑ It looks like the current bitrate is 1517k. \n'
+ '\n'
+ 'Container for output selected as mkv. \n'
+ 'Encode variable bitrate settings: \n'
+ 'Target = 759k \n'
+ 'Minimum = 569k \n'
+ 'Maximum = 949k \n'
+ 'File Transcoding... \n',
container: '.mkv',
},
darwin: {
processFile: true,
preset: '-fflags +genpts -hwaccel qsv -c:v h264_qsv<io> -map 0 -c:v hevc_videotoolbox -b:v 759k -minrate 569k -maxrate 949k -bufsize 1517k -preset slow \n'
+ ' -c:a copy -c:s copy -max_muxing_queue_size 9999 ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑ It looks like the current bitrate is 1517k. \n'
+ '\n'
+ 'Container for output selected as mkv. \n'
+ 'Encode variable bitrate settings: \n'
+ 'Target = 759k \n'
+ 'Minimum = 569k \n'
+ 'Maximum = 949k \n'
+ 'File Transcoding... \n',
container: '.mkv',
},
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
encoder_speedpreset: 'fast',
enable_10bit: 'true',
},
otherArguments: {},
},
output: {
linux: {
processFile: true,
preset: '-fflags +genpts -hwaccel qsv<io> -map 0 -c:v hevc_qsv -b:v 759k -minrate 569k -maxrate 949k -bufsize 1517k -preset fast \n'
+ ' -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -pix_fmt p010le ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑ It looks like the current bitrate is 1517k. \n'
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n'
+ '\n'
+ 'Container for output selected as mp4. \n'
+ 'Encode variable bitrate settings: \n'
+ 'Target = 759k \n'
+ 'Minimum = 569k \n'
+ 'Maximum = 949k \n'
+ 'File Transcoding... \n',
container: '.mp4',
},
win32: {
processFile: true,
preset: '-fflags +genpts -hwaccel qsv<io> -map 0 -c:v hevc_qsv -load_plugin hevc_hw -b:v 759k -minrate 569k -maxrate 949k -bufsize 1517k -preset fast \n'
+ ' -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -pix_fmt p010le ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑ It looks like the current bitrate is 1517k. \n'
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n'
+ '\n'
+ 'Container for output selected as mp4. \n'
+ 'Encode variable bitrate settings: \n'
+ 'Target = 759k \n'
+ 'Minimum = 569k \n'
+ 'Maximum = 949k \n'
+ 'File Transcoding... \n',
container: '.mp4',
},
darwin: {
processFile: true,
preset: '-fflags +genpts -hwaccel qsv<io> -map 0 -c:v hevc_videotoolbox -b:v 759k -minrate 569k -maxrate 949k -bufsize 1517k -preset fast \n'
+ ' -c:a copy -c:s copy -max_muxing_queue_size 9999 -profile:v main10 -pix_fmt p010le ',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑ It looks like the current bitrate is 1517k. \n'
+ '10 bit encode enabled. Setting Main10 Profile & 10 bit pixel format \n'
+ '\n'
+ 'Container for output selected as mp4. \n'
+ 'Encode variable bitrate settings: \n'
+ 'Target = 759k \n'
+ 'Minimum = 569k \n'
+ 'Maximum = 949k \n'
+ 'File Transcoding... \n',
container: '.mp4',
},
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
container: 'mp4',
encoder_speedpreset: 'fast',
enable_10bit: 'true',
bitrate_cutoff: '2000',
},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑ It looks like the current bitrate is 1517k. \n'
+ '☑ Current bitrate is below set cutoff of 2000k. Cancelling plugin. \n',
container: '.mp4',
},
},
];
run(tests);

@ -0,0 +1,49 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
infoLog: '☑ No 2 channel audio stream exists. \n ',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.ffProbeData.streams[1].channels = 6;
return file;
})(),
librarySettings: {},
inputs: {
channels: '6',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0 -c copy -disposition:1 default -disposition:2 0 -disposition:3 0 -disposition:4 0 -disposition:5 0 ',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
infoLog: '☒ Matching audio stream is not set to default. \n'
+ '☒ Setting 6 channel matching audio stream to default. Remove default from all other audio streams \n',
reQueueAfter: true,
},
},
];
run(tests);

@ -0,0 +1,50 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-c:v h264_cuvid,-map 0 -dn -c:v hevc_nvenc -pix_fmt p010le -qmin 0 -cq:v 30 -b:v 964k -maxrate:v 2964k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -a53cc 0 -c:a copy -c:s copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n'
+ '☒File is 720p!\n'
+ '☒File is not hevc!\n'
+ '☒File bitrate is 1205kb!\n'
+ 'File bitrate is LOWER than the Default Target Bitrate!\n'
+ '☒Target Bitrate set to 964kb!\n'
+ 'File is being transcoded!\n',
maxmux: false,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☑File is a video! \n☑File is already in hevc! \n',
maxmux: false,
},
},
];
run(tests);

@ -0,0 +1,47 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☑File is a video Without Mjpeg! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].codec_name = 'mjpeg';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: ',-map 0 -map -0:v:1 -c:v copy -c:a copy -c:s copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not a video but has Mjpeg Stream! \n'
+ '☑File is a video With Mjpeg! \n',
},
},
];
run(tests);

@ -0,0 +1,62 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map_metadata -1 -map 0:v -map 0:a -c:v copy -c:a copy -c:s mov_text',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑ File is already in h264!\n☒ File has title metadata\n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map_metadata -1 -map 0:V -map 0:a -c:v libx264 -preset medium -c:a aac -strict -2 -c:s mov_text',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒ File is not in h264!\n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {
FFmpeg_preset: 'fast',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map_metadata -1 -map 0:V -map 0:a -c:v libx264 -preset fast -c:a aac -strict -2 -c:s mov_text',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒ File is not in h264!\n',
},
},
];
run(tests);

@ -0,0 +1,110 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.mkv',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Will convert multi channel audio to AC3\n'
+ '☒ Transcoding to HEVC (software)\n'
+ 'Encoder configuration:\n'
+ '• Original Bitrate: 1517\n'
+ '• Target Bitrate: 1517\n'
+ '• Minimum Bitrate: 1061\n'
+ '• Maximum Bitrate: 1972\n'
+ '\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: ',-map 0 -map -0:d -c:v libx265 -b:v 1517k -minrate 1061k -maxrate 1972k -bufsize 1517k -c:a copy -c:a:0 ac3 -c:s copy -max_muxing_queue_size 4096',
reQueueAfter: false,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
nvenc: 'true',
},
otherArguments: {},
},
output: {
container: '.mkv',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Will convert multi channel audio to AC3\n'
+ '☒ Transcoding to HEVC using NVidia NVENC\n'
+ 'Encoder configuration:\n'
+ '• Original Bitrate: 1517\n'
+ '• Target Bitrate: 1517\n'
+ '• Minimum Bitrate: 1061\n'
+ '• Maximum Bitrate: 1972\n'
+ '\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: '-c:v h264_cuvid,-map 0 -map -0:d -c:v hevc_nvenc -cq:v 19 -b:v 1517k -minrate 1061k -maxrate 1972k -bufsize 1517k -spatial_aq:v 1 -rc-lookahead:v 32 -c:a copy -c:a:0 ac3 -c:s copy -max_muxing_queue_size 4096',
reQueueAfter: false,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {
nvenc: 'false',
qsv: 'true',
},
otherArguments: {},
},
output: {
container: '.mkv',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☒ Will convert multi channel audio to AC3\n'
+ '☒ Transcoding to HEVC using VAAPI\n'
+ 'Encoder configuration:\n'
+ '• Original Bitrate: 1517\n'
+ '• Target Bitrate: 1517\n'
+ '• Minimum Bitrate: 1061\n'
+ '• Maximum Bitrate: 1972\n'
+ '\n'
+ '☑ No subtitle processing necessary',
processFile: true,
preset: '-hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi,-map 0 -map -0:d -c:v hevc_vaapi -b:v 1517k -minrate 1061k -maxrate 1972k -bufsize 1517k -c:a copy -c:a:0 ac3 -c:s copy -max_muxing_queue_size 4096',
reQueueAfter: false,
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
container: '.mkv',
FFmpegMode: true,
handBrakeMode: false,
infoLog: '☑ No multi channel audio found\n'
+ '☑ No audio processing necessary\n'
+ '☑ File is in HEVC codec and in MKV\n'
+ '☑ No video processing necessary\n'
+ '☑ No subtitle processing necessary\n'
+ '☑ No need to process file',
processFile: false,
preset: ',-map 0 -map -0:d -c:v copy -c:a copy -c:s copy -max_muxing_queue_size 4096',
reQueueAfter: false,
},
},
];
run(tests);

@ -0,0 +1,97 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "H.264 MKV 480p30"',
container: '.mkv',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not h264 480p! \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "H.264 MKV 480p30"',
container: '.mkv',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not h264 480p! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].width = 720;
file.ffProbeData.streams[0].height = 480;
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is h264 480p! \n'
+ '☑File has no title and has no subs \n'
+ '☒File has title metadata \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].width = 720;
file.ffProbeData.streams[0].height = 480;
file.meta.Title = undefined;
file.container = 'mkv';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File is h264 480p! \n'
+ '☑File has no title and has no subs \n'
+ '☑File has no title metadata \n'
+ '☑File has no subs \n'
+ '☑File is in mkv container! \n'
+ '☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,99 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is h264 720p! \n'
+ '☑File has no title and has no subs \n'
+ '☒File has title metadata \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "H.264 MKV 720p30"',
container: '.mkv',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not h264 720p! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].width = 1280;
file.ffProbeData.streams[0].height = 720;
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is h264 720p! \n'
+ '☑File has no title and has no subs \n'
+ '☒File has title metadata \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].width = 1280;
file.ffProbeData.streams[0].height = 720;
file.meta.Title = undefined;
file.container = 'mkv';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File is h264 720p! \n'
+ '☑File has no title and has no subs \n'
+ '☑File has no title metadata \n'
+ '☑File has no subs \n'
+ '☑File is in mkv container! \n'
+ '☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,96 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is h264 1080p!☑File has no title and has no subs \n'
+ '☒File has title metadata \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "H.264 MKV 1080p30"',
container: '.mkv',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '☒File is not h264 1080p! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].width = 1920;
file.ffProbeData.streams[0].height = 1080;
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is h264 1080p!☑File has no title and has no subs \n'
+ '☒File has title metadata \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.ffProbeData.streams[0].width = 1920;
file.ffProbeData.streams[0].height = 1080;
file.meta.Title = undefined;
file.container = 'mkv';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File is h264 1080p!☑File has no title and has no subs \n'
+ '☑File has no title metadata \n'
+ '☑File has no subs \n'
+ '☑File is in mkv container! \n'
+ '☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,94 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: ', -map 0:v',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: 'Removing unwanted audio...\n'
+ 'Found unwanted: und: 1\n'
+ 'Found unwanted: und: 1\n'
+ 'No unwanted audio found!\n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: ', -map 0:v -map 0:1',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: 'Removing unwanted audio...\nAdded undefined: 1\nNo unwanted audio found!\n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
languages: 'fre',
container: 'mp4',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0:v -map 0:4 -map 0:s? -c copy',
container: 'mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Removing unwanted audio...\n'
+ 'Found wanted fre: 4\n'
+ 'Found unwanted audio\n'
+ 'It will be removed\n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
languages: 'eng',
container: 'mp4',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0:v -map 0:1 -map 0:2 -map 0:3 -map 0:5 -map 0:s? -c copy',
container: 'mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Removing unwanted audio...\n'
+ 'Found wanted eng: 1\n'
+ 'Found wanted eng: 2\n'
+ 'Found wanted eng: 3\n'
+ 'Found wanted eng: 5\n'
+ 'Found unwanted audio\n'
+ 'It will be removed\n',
},
},
];
run(tests);

@ -0,0 +1,48 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☒File is not a 4K video \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.video_resolution = '4KUHD';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-sn -map 0 -c copy',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File does not have only AC3 track commentaries! \n'
+ '☑File has AC3 track! \n'
+ '☒File has subs! \n',
},
},
];
run(tests);

@ -0,0 +1,85 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: ', -c copy -map 0:v ',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☒File is not mkv \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: ', -c copy -map 0:v -map 0:a:0? -c:a:0 copy -map 0:a:1? -c:a:1 copy -map 0:a:2? -c:a:2 copy -map 0:a:3? -c:a:3 copy -map 0:a:4? -c:a:4 copy ',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: "☑File doesn't contain audio tracks with the specified codec.\n",
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
input_codecs: 'aac',
output_codec: 'eac3',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -c copy -map 0:v -map 0:a:0? -c:a:0 copy -map 0:a:1? -c:a:1 copy -map 0:a:2? -c:a:2 copy -map 0:a:3? -c:a:3 copy -map 0:a:3? -c:a:4 eac3 -b:a:4 128k -metadata:s:a:4 title="" -metadata:s:a:4 copyright="henk_asac" -disposition:a:4 0 -map 0:a:4? -c:a:5 copy -map 0:a:4? -c:a:6 eac3 -b:a:6 128k -metadata:s:a:6 title="" -metadata:s:a:6 copyright="henk_asac" -disposition:a:6 0 -map 0:s? ',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {
input_codecs: 'aac',
output_codec: 'eac3',
bitrate: '256',
auto_adjust: 'false',
position_new_audio: 'before',
},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -c copy -map 0:v -map 0:a:0? -c:a:0 copy -map 0:a:1? -c:a:1 copy -map 0:a:2? -c:a:2 copy -map 0:a:3? -c:a:3 eac3 -b:a:3 256k -metadata:s:a:3 title="" -metadata:s:a:3 copyright="henk_asac" -disposition:a:3 0 -map 0:a:3? -c:a:4 copy -map 0:a:4? -c:a:5 eac3 -b:a:5 256k -metadata:s:a:5 title="" -metadata:s:a:5 copyright="henk_asac" -disposition:a:5 0 -map 0:a:4? -c:a:6 copy -map 0:s? ',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '',
},
},
];
run(tests);

@ -0,0 +1,68 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -c:v copy -c:a copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File has title metadata \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -c:v copy -c:a copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File has no title metadata \n'
+ '☑File has no subs \n'
+ '☒File is not in mp4 container! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json'));
file.container = 'mp4';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File has no title metadata \n'
+ '☑File has no subs \n'
+ '☑File is in mp4 container! \n'
+ '☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,66 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is in mp4 container! \n☒File has title metadata \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ', -map 0 -c copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File is not in mp4 container! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json'));
file.container = 'mp4';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File is in mp4 container! \n'
+ '☑File has no title metadata \n'
+ '☑File has aac track \n'
+ '☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,42 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-sn <io> -vcodec copy -scodec copy -acodec aac -filter:a "dynaudnorm,pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL|FR < 1.0*FR + 0.707*FC + 0.707*BR"',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File matches requirements for processing. Downmixing and applying DRC!',
container: '.mp4',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_2.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'File has more than 1 audio track - not processing',
container: '.mkv',
},
},
];
run(tests);

@ -0,0 +1,50 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: 'File has video in first stream\n File meets conditions!\n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
const audio = file.ffProbeData.streams[1];
// eslint-disable-next-line prefer-destructuring
file.ffProbeData.streams[1] = file.ffProbeData.streams[0];
file.ffProbeData.streams[0] = audio;
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v? -map 0:a? -map 0:s? -map 0:d? -map 0:t? -c copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: 'Video is not in the first stream',
},
},
];
run(tests);

@ -0,0 +1,42 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map_metadata -1 -map 0 -c copy',
container: '.mp4',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File has title metadata \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File has no title metadata \n☑File meets conditions! \n',
},
},
];
run(tests);

@ -0,0 +1,44 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0 -c:s copy -movflags use_metadata_tags -c:a aac -b:a 512k -c:v:0 libx265 -preset medium -x265-params crf=18:tune=animation:qcomp=0.7:aq-strength=1.1 -pix_fmt yuv420p10le -f matroska',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n'
+ '☒File is 720p but is not hevc!\n'
+ '☒File will be transcoded!\n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n☑File is already in hevc! \n',
},
},
];
run(tests);

@ -0,0 +1,67 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n'
+ '☒File is 720p but is not hevc!\n'
+ '☒File will be transcoded!\n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n☑File is already in hevc! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_2.json'));
file.video_resolution = '480p';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☑File is a video! \n'
+ '☒File is 480p but is not hevc!\n'
+ '☒File will be transcoded!\n',
},
},
];
run(tests);

@ -0,0 +1,84 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a:0 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 ac3 -b:a:0 192k -ac 2',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File has no language track in ac3,eac3,dts. No eng track marked so transcoding audio track 1 into ac3! \n',
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a:0 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 ac3 -b:a:0 192k -ac 2',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File has no language track in ac3,eac3,dts. No eng track marked so transcoding audio track 1 into ac3! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json'));
file.ffProbeData.streams[1].codec_name = 'ac3';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File is in mkv container! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH264_1.json'));
file.video_resolution = '4KUHD';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "H.265 MKV 2160p60" --all-audio --all-subtitles',
container: '.mkv',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: "☒ 4K file isn't in hevc! \n",
},
},
];
run(tests);

@ -0,0 +1,63 @@
/* eslint max-len: 0 */
const _ = require('lodash');
const run = require('../helpers/run');
const tests = [
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH264_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: '-Z "H.265 MKV 2160p60" --all-audio --all-subtitles',
container: '.mkv',
handBrakeMode: true,
FFmpegMode: false,
reQueueAfter: true,
infoLog: "☒File isn't in hevc! \n",
},
},
{
input: {
file: _.cloneDeep(require('../sampleData/media/sampleH265_1.json')),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: true,
preset: ',-map 0:v -map 0:a:0 -map 0:a -map 0:s? -map 0:d? -c copy -c:a:0 ac3 -b:a:0 192k -ac 2',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '☒File has no language track in ac3,eac3,dts. No eng track marked so transcoding audio track 1 into ac3! \n',
},
},
{
input: {
file: (() => {
const file = _.cloneDeep(require('../sampleData/media/sampleH265_1.json'));
file.ffProbeData.streams[1].codec_name = 'ac3';
return file;
})(),
librarySettings: {},
inputs: {},
otherArguments: {},
},
output: {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '☑File is in mkv container! \n',
},
},
];
run(tests);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save