commit
e006c7731b
@ -0,0 +1,104 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_action_add_audio_stream_codec',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Standardise audio stream codecs',
|
||||
Type: 'Video',
|
||||
Operation: 'Transcode',
|
||||
Description: `
|
||||
This action has a built-in filter. Additional filters can be added above. \n\n
|
||||
|
||||
If the following audio track does not exist, Tdarr will try to add it using existing audio streams.
|
||||
Tdarr will try to create the specified audio stream from the highest channel count stream
|
||||
available in the specified language.
|
||||
If no specified language track exists, the best untagged/undefined stream will be used.
|
||||
`,
|
||||
Version: '1.00',
|
||||
Tags: 'action',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'audioCodec',
|
||||
type: 'string',
|
||||
defaultValue: 'aac',
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'aac',
|
||||
'ac3',
|
||||
'eac3',
|
||||
'dca',
|
||||
'flac',
|
||||
'mp2',
|
||||
'libmp3lame',
|
||||
'truehd',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired audio codec',
|
||||
},
|
||||
{
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
defaultValue: 'en',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Tdarr will check to see if the stream language tag includes the tag you specify.'
|
||||
+ ' Case-insensitive. One tag only',
|
||||
},
|
||||
{
|
||||
name: 'channels',
|
||||
type: 'number',
|
||||
defaultValue: 2,
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'1',
|
||||
'2',
|
||||
'6',
|
||||
'8',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired number of channels',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
const lib = require('../methods/lib')();
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
const response = {
|
||||
processFile: false,
|
||||
preset: '',
|
||||
container: '',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: false,
|
||||
reQueueAfter: false,
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
const { audioCodec, language, channels } = inputs;
|
||||
|
||||
const transcodeAddAudioStream = lib.actions.transcodeAddAudioStream(
|
||||
file,
|
||||
audioCodec,
|
||||
language,
|
||||
channels,
|
||||
);
|
||||
|
||||
response.preset = transcodeAddAudioStream.preset;
|
||||
response.container = `.${file.container}`;
|
||||
response.handbrakeMode = false;
|
||||
response.ffmpegMode = true;
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = transcodeAddAudioStream.processFile;
|
||||
response.infoLog += transcodeAddAudioStream.note;
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,185 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_action_handbrake_basic_options',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'HandBrake basic options',
|
||||
Type: 'Video',
|
||||
Operation: 'Transcode',
|
||||
Description: `
|
||||
Set basic HandBrake transcode options. This action has no built-in filter so be sure to set a codec filter
|
||||
to prevent a transcoding loop.
|
||||
`,
|
||||
Version: '1.00',
|
||||
Tags: 'action',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'handbrakePreset',
|
||||
type: 'string',
|
||||
defaultValue: 'Very Fast 1080p30',
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'Very Fast 1080p30',
|
||||
'Very Fast 720p30',
|
||||
'Very Fast 576p25',
|
||||
'Very Fast 480p30',
|
||||
'Fast 1080p30',
|
||||
'Fast 720p30',
|
||||
'Fast 576p25',
|
||||
'Fast 480p30',
|
||||
'HQ 1080p30 Surround',
|
||||
'HQ 720p30 Surround',
|
||||
'HQ 576p25 Surround',
|
||||
'HQ 480p30 Surround"',
|
||||
'Super HQ 1080p30 Surround',
|
||||
'Super HQ 720p30 Surround',
|
||||
'Super HQ 576p25 Surround',
|
||||
'Super HQ 480p30 Surround',
|
||||
'Gmail Large 3 Minutes 720p30',
|
||||
'Gmail Medium 5 Minutes 480p30',
|
||||
'Gmail Small 10 Minutes 288p30',
|
||||
'Vimeo YouTube HQ 2160p60 4K',
|
||||
'Vimeo YouTube HQ 1440p60 2.5K',
|
||||
'Vimeo YouTube HQ 1080p60',
|
||||
'Vimeo YouTube HQ 720p60',
|
||||
'Vimeo YouTube 720p30',
|
||||
'Android 1080p30',
|
||||
'Android 720p30',
|
||||
'Android 576p25',
|
||||
'Android 480p30',
|
||||
'Apple 2160p60 4K HEVC Surround',
|
||||
'Apple 1080p60 Surround',
|
||||
'Apple 1080p30 Surround',
|
||||
'Apple 720p30 Surround',
|
||||
'Apple 540p30 Surround',
|
||||
'Apple 240p30',
|
||||
'Chromecast 2160p60 4K HEVC Surround',
|
||||
'Chromecast 1080p60 Surround',
|
||||
'Chromecast 1080p30 Surround',
|
||||
'Amazon Fire 2160p60 4K HEVC Surround',
|
||||
'Amazon Fire 1080p30 Surround',
|
||||
'Amazon Fire 720p30',
|
||||
'Playstation 1080p30 Surround',
|
||||
'Playstation 720p30',
|
||||
'Playstation 540p30',
|
||||
'Roku 2160p60 4K HEVC Surround',
|
||||
'Roku 1080p30 Surround',
|
||||
'Roku 720p30 Surround',
|
||||
'Roku 576p25',
|
||||
'Roku 480p30',
|
||||
'Windows Mobile 1080p30',
|
||||
'Windows Mobile 720p30',
|
||||
'Windows Mobile 540p30',
|
||||
'Windows Mobile 480p30',
|
||||
'Xbox 1080p30 Surround',
|
||||
'Xbox Legacy 1080p30 Surround',
|
||||
'H.265 MKV 2160p60',
|
||||
'H.265 MKV 1080p30',
|
||||
'H.265 MKV 720p30',
|
||||
'H.265 MKV 576p25',
|
||||
'H.265 MKV 480p30',
|
||||
'H.264 MKV 2160p60',
|
||||
'H.264 MKV 1080p30',
|
||||
'H.264 MKV 720p30',
|
||||
'H.264 MKV 576p25',
|
||||
'H.264 MKV 480p30',
|
||||
'VP9 MKV 2160p60',
|
||||
'VP9 MKV 1080p30',
|
||||
'VP9 MKV 720p30',
|
||||
'VP9 MKV 576p25',
|
||||
'VP9 MKV 480p30',
|
||||
'VP8 MKV 1080p30',
|
||||
'VP8 MKV 720p30',
|
||||
'VP8 MKV 576p25',
|
||||
'VP8 MKV 480p30',
|
||||
'Production Max',
|
||||
'Production Standard',
|
||||
'Production Proxy 1080p',
|
||||
'Production Proxy 540p',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired HandBrake preset',
|
||||
},
|
||||
{
|
||||
name: 'videoEncoder',
|
||||
type: 'string',
|
||||
defaultValue: 'x265',
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'x264',
|
||||
'x264_10bit',
|
||||
'qsv_h264',
|
||||
'nvenc_h264',
|
||||
'x265',
|
||||
'x265_10bit',
|
||||
'x265_12bit',
|
||||
'qsv_h265',
|
||||
'nvenc_h265',
|
||||
'mpeg4',
|
||||
'mpeg2',
|
||||
'VP8',
|
||||
'VP9',
|
||||
'theora',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired video encoder',
|
||||
},
|
||||
{
|
||||
name: 'keepSubtitles',
|
||||
type: 'boolean',
|
||||
defaultValue: true,
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'true',
|
||||
'false',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Specify whether to keep subs or not',
|
||||
},
|
||||
{
|
||||
name: 'container',
|
||||
type: 'string',
|
||||
defaultValue: 'mkv',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired container',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
const lib = require('../methods/lib')();
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
const response = {
|
||||
processFile: false,
|
||||
preset: '',
|
||||
container: '',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: false,
|
||||
reQueueAfter: false,
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
const keepSubs = inputs.keepSubtitles === true ? '--all-subtitles' : '';
|
||||
|
||||
response.preset = `-Z "${inputs.handbrakePreset}" -e ${inputs.videoEncoder} ${keepSubs}`;
|
||||
response.container = `.${inputs.container}`;
|
||||
response.handbrakeMode = true;
|
||||
response.ffmpegMode = false;
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = true;
|
||||
response.infoLog += 'File is being transcoded using HandBrake \n';
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,105 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_action_handbrake_ffmpeg_custom',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'HandBrake or FFmpeg custom arguments',
|
||||
Type: 'Video',
|
||||
Operation: 'Transcode',
|
||||
Description: `
|
||||
Set HandBrake or FFmpeg arguments. This action has no built-in filter so be sure to set a codec filter
|
||||
to prevent a transcoding loop.
|
||||
`,
|
||||
Version: '1.00',
|
||||
Tags: 'action',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'cli',
|
||||
type: 'string',
|
||||
defaultValue: 'ffmpeg',
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'ffmpeg',
|
||||
'handbrake',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired video encoder',
|
||||
},
|
||||
{
|
||||
name: 'arguments',
|
||||
type: 'string',
|
||||
defaultValue: '<io> -map 0 -c copy',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
`
|
||||
When using FFmpeg, you need to separate the input and output args with <io>. FFmpeg Examples:
|
||||
\n\n
|
||||
-r 1<io>-r 24
|
||||
\n\n
|
||||
<io>-sn -c:v copy -c:a copy
|
||||
\n\n
|
||||
<io>-c:v libx265 -crf 23 -ac 6 -c:a aac -preset veryfast
|
||||
\n\n
|
||||
<io>-map 0 -c copy -c:v libx265 -c:a aac
|
||||
\n\n
|
||||
-c:v h264_cuvid<io>-c:v hevc_nvenc -preset slow -c:a copy
|
||||
|
||||
\n\n
|
||||
HandBrake examples:
|
||||
\n\n
|
||||
-e x264 -q 20 -B
|
||||
\n\n
|
||||
-Z "Very Fast 1080p30"
|
||||
\n\n
|
||||
-Z "Fast 1080p30" -e nvenc_h265
|
||||
\n\n
|
||||
-Z "Very Fast 1080p30" --all-subtitles --all-audio
|
||||
\n\n
|
||||
-Z "Very Fast 480p30"
|
||||
\n\n
|
||||
--preset-import-file "C:/Users/HaveAGitGat/Desktop/testpreset.json" -Z "My Preset"
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'container',
|
||||
type: 'string',
|
||||
defaultValue: 'mkv',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired container',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
const lib = require('../methods/lib')();
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
const response = {
|
||||
processFile: false,
|
||||
preset: '',
|
||||
container: '',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: false,
|
||||
reQueueAfter: false,
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
response.preset = inputs.arguments;
|
||||
response.container = `.${inputs.container}`;
|
||||
response.handbrakeMode = inputs.cli === 'handbrake';
|
||||
response.ffmpegMode = inputs.cli === 'ffmpeg';
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = true;
|
||||
response.infoLog += 'File is being transcoded using custom arguments \n';
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,103 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_action_keep_one_audio_stream',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Keep one audio stream',
|
||||
Type: 'Video',
|
||||
Operation: 'Transcode',
|
||||
Description: `
|
||||
This action has a built-in filter. Additional filters can be added.\n\n
|
||||
|
||||
Tdarr will try to keep the best audio track possible given the requirements specified below.
|
||||
If the specified stream does not exist, Tdarr will try to create it using the best stream available.
|
||||
If no specified language track exists, the best untagged/undefined stream will be used/kept.
|
||||
`,
|
||||
Version: '1.00',
|
||||
Tags: 'action',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'audioCodec',
|
||||
type: 'string',
|
||||
defaultValue: 'aac',
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'aac',
|
||||
'ac3',
|
||||
'eac3',
|
||||
'dca',
|
||||
'flac',
|
||||
'mp2',
|
||||
'libmp3lame',
|
||||
'truehd',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired audio codec',
|
||||
},
|
||||
{
|
||||
name: 'language',
|
||||
type: 'string',
|
||||
defaultValue: 'en',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Tdarr will check to see if the stream language tag includes the tag you specify.'
|
||||
+ ' Case-insensitive. One tag only',
|
||||
},
|
||||
{
|
||||
name: 'channels',
|
||||
type: 'number',
|
||||
defaultValue: 2,
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'1',
|
||||
'2',
|
||||
'6',
|
||||
'8',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired number of channels',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
const lib = require('../methods/lib')();
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
const response = {
|
||||
processFile: false,
|
||||
preset: '',
|
||||
container: `.${file.container}`,
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: false,
|
||||
reQueueAfter: false,
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
const { audioCodec, language, channels } = inputs;
|
||||
|
||||
const transcodeKeepOneAudioStream = lib.actions.transcodeKeepOneAudioStream(
|
||||
file,
|
||||
audioCodec,
|
||||
language,
|
||||
channels,
|
||||
);
|
||||
|
||||
response.preset = transcodeKeepOneAudioStream.preset;
|
||||
response.container = `.${file.container}`;
|
||||
response.handbrakeMode = false;
|
||||
response.FFmpegMode = true;
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = transcodeKeepOneAudioStream.processFile;
|
||||
response.infoLog += transcodeKeepOneAudioStream.note;
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,62 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_action_remux_container',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Remux container',
|
||||
Type: 'Video',
|
||||
Operation: 'Transcode',
|
||||
Description: `
|
||||
This action has a built-in filter. Additional filters can be added.\n\n
|
||||
|
||||
If not in the specified container, the file will be remuxed.
|
||||
`,
|
||||
Version: '1.00',
|
||||
Tags: 'action',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'container',
|
||||
type: 'string',
|
||||
defaultValue: 'mkv',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired container',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
const lib = require('../methods/lib')();
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
const response = {
|
||||
processFile: false,
|
||||
preset: '',
|
||||
container: '',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: false,
|
||||
reQueueAfter: false,
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
const { container } = inputs;
|
||||
|
||||
const remuxContainer = lib.actions.remuxContainer(
|
||||
file,
|
||||
container,
|
||||
);
|
||||
|
||||
response.preset = ', -map 0 -c copy';
|
||||
response.container = '.mkv';
|
||||
response.handbrakeMode = false;
|
||||
response.FFmpegMode = true;
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = remuxContainer.processFile;
|
||||
response.infoLog += remuxContainer.note;
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,71 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_action_standardise_audio_stream_codecs',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Standardise audio stream codecs',
|
||||
Type: 'Video',
|
||||
Operation: 'Transcode',
|
||||
Description: `
|
||||
This action has a built-in filter. Additional filters can be added.\n\n
|
||||
|
||||
All audio tracks which are not in the specified codec will be transcoded
|
||||
into the specified codec. Bitrate and channel count are kept the same.
|
||||
`,
|
||||
Version: '1.00',
|
||||
Tags: 'action',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'audioCodec',
|
||||
type: 'string',
|
||||
defaultValue: 'aac',
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'aac',
|
||||
'ac3',
|
||||
'eac3',
|
||||
'dca',
|
||||
'flac',
|
||||
'mp2',
|
||||
'libmp3lame',
|
||||
'truehd',
|
||||
],
|
||||
},
|
||||
tooltip:
|
||||
'Enter the desired audio codec',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
const lib = require('../methods/lib')();
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
const response = {
|
||||
processFile: false,
|
||||
preset: '',
|
||||
container: '',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: false,
|
||||
reQueueAfter: false,
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
const transcodeStandardiseAudioCodecs = lib.actions.transcodeStandardiseAudioCodecs(
|
||||
file,
|
||||
inputs.audioCodec,
|
||||
);
|
||||
|
||||
response.preset = transcodeStandardiseAudioCodecs.preset;
|
||||
response.container = `.${file.container}`;
|
||||
response.handbrakeMode = false;
|
||||
response.ffmpegMode = true;
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = transcodeStandardiseAudioCodecs.processFile;
|
||||
response.infoLog += transcodeStandardiseAudioCodecs.note;
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,60 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_filter_by_bitrate',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Filter by bitrate',
|
||||
Type: 'Video',
|
||||
Operation: 'Filter',
|
||||
Description: 'Only allow files to be transcoded which are within the lower and upper bounds (Kb) \n\n',
|
||||
Version: '1.00',
|
||||
Tags: 'filter',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'upperBound',
|
||||
type: 'number',
|
||||
defaultValue: 10000,
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter the upper bound bitrate in Kb for files which should be processed.',
|
||||
},
|
||||
{
|
||||
name: 'lowerBound',
|
||||
type: 'number',
|
||||
defaultValue: 0,
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter the lower bound bitrate in Kb for files which should be processed.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
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: '',
|
||||
};
|
||||
|
||||
if (
|
||||
file.bit_rate >= inputs.lowerBound * 1000
|
||||
&& file.bit_rate <= inputs.upperBound * 1000
|
||||
) {
|
||||
response.processFile = true;
|
||||
response.processFile += '☑File bitrate is within filter limits. Moving to next plugin.';
|
||||
} else {
|
||||
response.processFile = false;
|
||||
response.processFile += '☒File bitrate is not within filter limits. Breaking out of plugin stack.\n';
|
||||
return response;
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,73 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_filter_by_codec',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Filter by codec',
|
||||
Type: 'Video',
|
||||
Operation: 'Filter',
|
||||
Description: 'Only allow specified codecs to be processed \n\n',
|
||||
Version: '1.00',
|
||||
Tags: 'filter',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'codecsToProcess',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter a comma separated list of codecs to be processed. Leave blank if using codecsToNotProcess',
|
||||
},
|
||||
{
|
||||
name: 'codecsToNotProcess',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter a comma separated list of codecs to be not be processed. Leave blank if using codecsToProcess',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
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: '',
|
||||
};
|
||||
|
||||
const fileCodec = file.video_codec_name !== '' ? file.video_codec_name : file.audio_codec_name;
|
||||
|
||||
if (inputs.codecsToProcess !== '') {
|
||||
const codecs = inputs.codecsToProcess.split(',');
|
||||
if (codecs.includes(fileCodec)) {
|
||||
response.processFile = true;
|
||||
response.processFile += 'File is in codecsToProcess. Moving to next plugin.';
|
||||
} else {
|
||||
response.processFile = false;
|
||||
response.processFile += 'File is not in codecsToProcess. Breaking out of plugin stack.';
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.codecsToNotProcess !== '') {
|
||||
const codecs = inputs.codecsToNotProcess.split(',');
|
||||
if (codecs.includes(fileCodec)) {
|
||||
response.processFile = false;
|
||||
response.processFile += 'File is in codecsToNotProcess. Breaking out of plugin stack.';
|
||||
} else {
|
||||
response.processFile = true;
|
||||
response.processFile += 'File is not in codecsToNotProcess. Moving to next plugin.';
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,81 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_filter_by_resolution',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Filter by resolution',
|
||||
Type: 'Video',
|
||||
Operation: 'Filter',
|
||||
Description: 'Only allow specified resolutions to be processed \n\n',
|
||||
Version: '1.00',
|
||||
Tags: 'filter',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'resolutionsToProcess',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
`Enter a comma separated list of resolutions to be processed.
|
||||
Leave blank if using resolutionsToNotProcess.
|
||||
480p,576p,720p,1080p,4KUHD,DCI4K,8KUHD,Other
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: 'resolutionsToNotProcess',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter a comma separated list of resolutions to be not be processed.'
|
||||
+ ' Leave blank if using resolutionsToProcess',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
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: '',
|
||||
};
|
||||
|
||||
if (!file.video_resolution) {
|
||||
throw new Error('File has no resolution!');
|
||||
}
|
||||
|
||||
const fileResolution = file.video_resolution;
|
||||
|
||||
if (inputs.resolutionsToProcess !== '') {
|
||||
const resolutions = inputs.resolutionsToProcess.split(',');
|
||||
if (resolutions.includes(fileResolution)) {
|
||||
response.processFile = true;
|
||||
response.processFile += 'File is in resolutionsToProcess. Moving to next plugin.';
|
||||
} else {
|
||||
response.processFile = false;
|
||||
response.processFile += 'File is not in resolutionsToProcess. Breaking out of plugin stack.';
|
||||
}
|
||||
}
|
||||
|
||||
if (inputs.resolutionsToNotProcess !== '') {
|
||||
const resolutions = inputs.resolutionsToNotProcess.split(',');
|
||||
if (resolutions.includes(fileResolution)) {
|
||||
response.processFile = false;
|
||||
response.processFile += 'File is in resolutionsToNotProcess. Breaking out of plugin stack.';
|
||||
} else {
|
||||
response.processFile = true;
|
||||
response.processFile += 'File is not in resolutionsToNotProcess. Moving to next plugin.';
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -0,0 +1,57 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_00td_filter_by_size',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Filter by size',
|
||||
Type: 'Video',
|
||||
Operation: 'Filter',
|
||||
Description: 'Only allow files to be transcoded which are within the lower and upper bounds (MB) \n\n',
|
||||
Version: '1.00',
|
||||
Tags: 'filter',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'upperBound',
|
||||
type: 'number',
|
||||
defaultValue: 100000,
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter the upper bound size in MB for files which should be processed.',
|
||||
},
|
||||
{
|
||||
name: 'lowerBound',
|
||||
type: 'number',
|
||||
defaultValue: 0,
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Enter the lower bound size in MB for files which should be processed.',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
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: '',
|
||||
};
|
||||
|
||||
const fileSize = file.file_size;
|
||||
if (fileSize >= inputs.lowerBound && fileSize <= inputs.upperBound) {
|
||||
response.processFile = true;
|
||||
response.infoLog += 'File is within lower and upper bound size limits. Moving to next plugin.';
|
||||
} else {
|
||||
response.infoLog += 'File is not within lower and upper bound size limits. Breaking out of plugin stack.';
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -1,145 +1,145 @@
|
||||
const loadDefaultValues = require('../methods/loadDefaultValues');
|
||||
/* eslint-disable */
|
||||
const details = () => {
|
||||
return {
|
||||
id: "Tdarr_Plugin_MP01_MichPasCleanSubsAndAudioCodecs",
|
||||
Stage: "Pre-processing",
|
||||
Name: "MichPass-Remove subtitle and audio streams with certain codecs",
|
||||
Type: "Any",
|
||||
Operation: 'Transcode',
|
||||
Description: `This plugin removed specified codecs from subtitle and audio tracks. Helpful to remove bitmap subtitles (pgs,vobsub) or audio codec (truehd), which can cause Plex to start transcoding. Based on Migz4 Plugin. Thanks \n\n`,
|
||||
Version: "1.0",
|
||||
Tags: "pre-processing,ffmpeg,subtitle, audio,configurable",
|
||||
|
||||
Inputs: [
|
||||
{
|
||||
name: "tag_subtitle_codecs",
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Specify key words here for subtitle tracks you'd like to have removed.
|
||||
\\nExample:\\n
|
||||
hdmv_pgs_subtitle
|
||||
\\nExample:\\n
|
||||
hdmv_pgs_subtitle,dvd_subtitle`,
|
||||
},
|
||||
{
|
||||
|
||||
name: "tag_audio_codecs",
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Specify all audio codecs you'd like to have removed.
|
||||
\\nExample:\\n
|
||||
truehd
|
||||
\\nExample:\\n
|
||||
xxx,yyy`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = loadDefaultValues(inputs, details);
|
||||
var response = {
|
||||
processFile: false,
|
||||
preset: "",
|
||||
container: "." + file.container,
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: false,
|
||||
infoLog: "",
|
||||
|
||||
};
|
||||
|
||||
// Check if file is a video. If it isn't then exit plugin.
|
||||
|
||||
if (file.fileMedium !== "video") {
|
||||
console.log("File is not video");
|
||||
response.infoLog += "☒File is not video \n";
|
||||
response.processFile = false;
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
// Set up required variables.
|
||||
var tag_subtitle_codecs = inputs.tag_subtitle_codecs.split(",");
|
||||
var tag_audio_codecs = inputs.tag_audio_codecs.split(",");
|
||||
var ffmpegCommandInsert = "";
|
||||
var subtitleIdx = 0;
|
||||
var audioIdx = 0;
|
||||
var convert = false;
|
||||
|
||||
// Go through each stream in the file.
|
||||
for (var i = 0; i < file.ffProbeData.streams.length; i++) {
|
||||
// Catch error here incase the title metadata is completely missing.
|
||||
try {
|
||||
// Check stream is subtitle AND stream codec contains certain words, removing these streams .
|
||||
if (
|
||||
file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" &&
|
||||
tag_subtitle_codecs.indexOf(file.ffProbeData.streams[i].codec_name.toLowerCase()
|
||||
) > -1
|
||||
) {
|
||||
ffmpegCommandInsert += `-map -0:s:${subtitleIdx} `;
|
||||
response.infoLog += `☒Subtitle stream detected as unwanted. removing subtitle stream 0:s:${subtitleIdx} - ${file.ffProbeData.streams[i].tags.title} - ${file.ffProbeData.streams[i].codec_name}. \n`;
|
||||
convert = true;
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
|
||||
// For debugging
|
||||
// response.infoLog += `☒test tags codectype title - ${file.ffProbeData.streams[i].codec_type}. \n`;
|
||||
// response.infoLog += `☒test tag name - ${file.ffProbeData.streams[i].codec_name}. \n`;
|
||||
// response.infoLog += `☒test tags long name - ${file.ffProbeData.streams[i].codec_long_name}. \n`;
|
||||
|
||||
try {
|
||||
// Check if stream is audio .
|
||||
if (
|
||||
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" &&
|
||||
tag_audio_codecs.indexOf(file.ffProbeData.streams[i].codec_name.toLowerCase()
|
||||
) > -1
|
||||
) {
|
||||
ffmpegCommandInsert += `-map -0:a:${audioIdx} `;
|
||||
response.infoLog += `☒audio stream detected as unwanted. removing audio stream 0:a:${audioIdx} - ${file.ffProbeData.streams[i].tags.title} - ${file.ffProbeData.streams[i].codec_name} \n`;
|
||||
convert = true;
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
|
||||
// Check if stream type is audio and increment audioIdx if true.
|
||||
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") {
|
||||
audioIdx++;
|
||||
}
|
||||
|
||||
// Check if stream type is subtitle and increment subtitleIdx if true.
|
||||
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") {
|
||||
subtitleIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Convert file if convert variable is set to true.
|
||||
|
||||
if (convert === true) {
|
||||
response.processFile = true;
|
||||
response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096`;
|
||||
response.container = "." + file.container;
|
||||
response.reQueueAfter = true;
|
||||
} else {
|
||||
response.processFile = false;
|
||||
response.infoLog +=
|
||||
"☑File doesn't contain subtitle or audio codecs which were unwanted or that require tagging.\n";
|
||||
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/* eslint-disable */
|
||||
const details = () => {
|
||||
return {
|
||||
id: "Tdarr_Plugin_MP01_MichPasCleanSubsAndAudioCodecs",
|
||||
Stage: "Pre-processing",
|
||||
Name: "MichPass-Remove subtitle and audio streams with certain codecs",
|
||||
Type: "Any",
|
||||
Operation: 'Transcode',
|
||||
Description: `This plugin removed specified codecs from subtitle and audio tracks. Helpful to remove bitmap subtitles (pgs,vobsub) or audio codec (truehd), which can cause Plex to start transcoding. Based on Migz4 Plugin. Thanks \n\n`,
|
||||
Version: "1.0",
|
||||
Tags: "pre-processing,ffmpeg,subtitle, audio,configurable",
|
||||
|
||||
Inputs: [
|
||||
{
|
||||
name: "tag_subtitle_codecs",
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Specify key words here for subtitle tracks you'd like to have removed.
|
||||
\\nExample:\\n
|
||||
hdmv_pgs_subtitle
|
||||
\\nExample:\\n
|
||||
hdmv_pgs_subtitle,dvd_subtitle`,
|
||||
},
|
||||
{
|
||||
|
||||
name: "tag_audio_codecs",
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Specify all audio codecs you'd like to have removed.
|
||||
\\nExample:\\n
|
||||
truehd
|
||||
\\nExample:\\n
|
||||
xxx,yyy`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
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: "",
|
||||
container: "." + file.container,
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: false,
|
||||
infoLog: "",
|
||||
|
||||
};
|
||||
|
||||
// Check if file is a video. If it isn't then exit plugin.
|
||||
|
||||
if (file.fileMedium !== "video") {
|
||||
console.log("File is not video");
|
||||
response.infoLog += "☒File is not video \n";
|
||||
response.processFile = false;
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
// Set up required variables.
|
||||
var tag_subtitle_codecs = inputs.tag_subtitle_codecs.split(",");
|
||||
var tag_audio_codecs = inputs.tag_audio_codecs.split(",");
|
||||
var ffmpegCommandInsert = "";
|
||||
var subtitleIdx = 0;
|
||||
var audioIdx = 0;
|
||||
var convert = false;
|
||||
|
||||
// Go through each stream in the file.
|
||||
for (var i = 0; i < file.ffProbeData.streams.length; i++) {
|
||||
// Catch error here incase the title metadata is completely missing.
|
||||
try {
|
||||
// Check stream is subtitle AND stream codec contains certain words, removing these streams .
|
||||
if (
|
||||
file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" &&
|
||||
tag_subtitle_codecs.indexOf(file.ffProbeData.streams[i].codec_name.toLowerCase()
|
||||
) > -1
|
||||
) {
|
||||
ffmpegCommandInsert += `-map -0:s:${subtitleIdx} `;
|
||||
response.infoLog += `☒Subtitle stream detected as unwanted. removing subtitle stream 0:s:${subtitleIdx} - ${file.ffProbeData.streams[i].tags.title} - ${file.ffProbeData.streams[i].codec_name}. \n`;
|
||||
convert = true;
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
|
||||
// For debugging
|
||||
// response.infoLog += `☒test tags codectype title - ${file.ffProbeData.streams[i].codec_type}. \n`;
|
||||
// response.infoLog += `☒test tag name - ${file.ffProbeData.streams[i].codec_name}. \n`;
|
||||
// response.infoLog += `☒test tags long name - ${file.ffProbeData.streams[i].codec_long_name}. \n`;
|
||||
|
||||
try {
|
||||
// Check if stream is audio .
|
||||
if (
|
||||
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" &&
|
||||
tag_audio_codecs.indexOf(file.ffProbeData.streams[i].codec_name.toLowerCase()
|
||||
) > -1
|
||||
) {
|
||||
ffmpegCommandInsert += `-map -0:a:${audioIdx} `;
|
||||
response.infoLog += `☒audio stream detected as unwanted. removing audio stream 0:a:${audioIdx} - ${file.ffProbeData.streams[i].tags.title} - ${file.ffProbeData.streams[i].codec_name} \n`;
|
||||
convert = true;
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
|
||||
// Check if stream type is audio and increment audioIdx if true.
|
||||
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") {
|
||||
audioIdx++;
|
||||
}
|
||||
|
||||
// Check if stream type is subtitle and increment subtitleIdx if true.
|
||||
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") {
|
||||
subtitleIdx++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Convert file if convert variable is set to true.
|
||||
|
||||
if (convert === true) {
|
||||
response.processFile = true;
|
||||
response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096`;
|
||||
response.container = "." + file.container;
|
||||
response.reQueueAfter = true;
|
||||
} else {
|
||||
response.processFile = false;
|
||||
response.infoLog +=
|
||||
"☑File doesn't contain subtitle or audio codecs which were unwanted or that require tagging.\n";
|
||||
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
@ -1,232 +1,233 @@
|
||||
const loadDefaultValues = require('../methods/loadDefaultValues');
|
||||
/* eslint-disable */
|
||||
//PLugin runs multipass loudnorm filter
|
||||
//first run gets the required details and stores for the next pass
|
||||
//second pass applies the values
|
||||
|
||||
//stages
|
||||
// Determined Loudnorm Values
|
||||
// Applying Normalisation
|
||||
// Normalisation Complete
|
||||
|
||||
|
||||
//setup global vars
|
||||
|
||||
var secondPass = false;
|
||||
var logOutFile = '';
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
const details = () => {
|
||||
return {
|
||||
id: "Tdarr_Plugin_NIfPZuCLU_2_Pass_Loudnorm_Audio_Normalisation",
|
||||
Stage: 'Pre-processing',
|
||||
Name: "2 Pass Loudnorm Volume Normalisation",
|
||||
Type: "Video",
|
||||
Operation: "Transcode",
|
||||
Description: "PLEASE READ FULL DESCRIPTION BEFORE USE \n Uses multiple passes to normalise audio streams of videos using loudnorm.\n\n The first pass will create an log file in the same directory as the video.\nSecond pass will apply the values determined in the first pass to the file.\nOutput will be MKV to allow metadata to be added for tracking normalisation stage.",
|
||||
Version: "0.1",
|
||||
Tags: "pre-processing,ffmpeg,configurable",
|
||||
|
||||
Inputs: [
|
||||
//(Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI
|
||||
{
|
||||
name: "i",
|
||||
type: 'string',
|
||||
defaultValue:'-23.0',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `\"I\" value used in loudnorm pass \n
|
||||
defaults to -23.0`, //Each line following `Example:` will be clearly formatted. \\n used for line breaks
|
||||
},
|
||||
{
|
||||
name: "lra",
|
||||
type: 'string',
|
||||
defaultValue:'7.0',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Desired lra value. \n Defaults to 7.0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "tp",
|
||||
type: 'string',
|
||||
defaultValue:'-2.0',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Desired \"tp\" value. \n Defaults to -2.0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "offset",
|
||||
type: 'string',
|
||||
defaultValue:'0.0',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Desired "offset" value. \n Defaults to 0.0
|
||||
`,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = loadDefaultValues(inputs, details);
|
||||
|
||||
//Must return this object at some point
|
||||
var response = {
|
||||
processFile: false,
|
||||
preset: '',
|
||||
container: '.mkv',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '',
|
||||
|
||||
}
|
||||
|
||||
response.infoLog += ""
|
||||
//grab the current file being processed and make an out file for the ffmpeg log
|
||||
let currentfilename = file._id;
|
||||
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())
|
||||
|
||||
//setup required varibles
|
||||
var loudNorm_i = -23.0
|
||||
var lra = 7.0
|
||||
var tp = -2.0
|
||||
var offset = 0.0
|
||||
|
||||
//create local varibles for inputs
|
||||
if (inputs !== undefined) {
|
||||
if (inputs.i !== undefined) loudNorm_i = inputs.i
|
||||
if (inputs.lra !== undefined) lra = inputs.lra
|
||||
if (inputs.tp !== undefined) tp = inputs.tp
|
||||
if (inputs.offset !== undefined) offset = inputs.offset
|
||||
}
|
||||
|
||||
|
||||
//check for previous pass tags
|
||||
|
||||
if (typeof probeData.format === "undefined" || typeof probeData.format.tags.NORMALISATIONSTAGE === "undefined" || probeData.format.tags.NORMALISATIONSTAGE === "" || file.forceProcessing === true) {
|
||||
|
||||
//no metadata found first pass is required
|
||||
console.log("Searching for audio normailisation values")
|
||||
response.infoLog += "Searching for required normalisation values. \n"
|
||||
var loudNormInfo = "";
|
||||
|
||||
//Do the first pass, output the log to the out file and use a secondary output for an unchanged file to allow Tdarr to track, Set metadata stage
|
||||
response.preset = `<io>-af loudnorm=I=${loudNorm_i}:LRA=${lra}:TP=${tp}:print_format=json -f null NUL -map 0 -c copy -metadata NORMALISATIONSTAGE="FirstPassComplete" 2>"${logOutFile}"`
|
||||
response.container = '.mkv'
|
||||
response.handBrakeMode = false
|
||||
response.FFmpegMode = true
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = true
|
||||
response.infoLog += "Normalisation first pass processing \n"
|
||||
return response
|
||||
}
|
||||
if (probeData.format.tags.NORMALISATIONSTAGE === "FirstPassComplete") {
|
||||
|
||||
//ensure previous out file exists
|
||||
if (fs.existsSync(logOutFile)) {
|
||||
secondPass = true;
|
||||
loudNormInfo = fs.readFileSync(logOutFile).toString();
|
||||
|
||||
//grab the json from the out file
|
||||
var startIndex = loudNormInfo.lastIndexOf("{");
|
||||
var endIndex = loudNormInfo.lastIndexOf("}");
|
||||
|
||||
var outValues = loudNormInfo.toString().substr(startIndex, endIndex)
|
||||
|
||||
response.infoLog += "Loudnorm first pass values returned: \n" + outValues
|
||||
|
||||
//parse the JSON
|
||||
var loudNormValues = JSON.parse(outValues)
|
||||
|
||||
//use parsed values in second pass
|
||||
response.preset = `-y<io>-af loudnorm=print_format=summary:linear=true:I=${loudNorm_i}:LRA=${lra}:TP=${tp}:measured_i=${loudNormValues.input_i}:measured_lra=${loudNormValues.input_lra}:measured_tp=${loudNormValues.input_tp}:measured_thresh=${loudNormValues.input_thresh}:offset=${loudNormValues.target_offset} -c:a aac -b:a 192k -c:s copy -c:v copy -metadata NORMALISATIONSTAGE="Complete"`
|
||||
response.container = '.mkv'
|
||||
response.handBrakeMode = false
|
||||
response.FFmpegMode = true
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = true
|
||||
response.infoLog += "Normalisation pass processing \n"
|
||||
return response
|
||||
} else {
|
||||
response.infoLog += "Previous log output file is missing. Please rerun with force processing to regenerate."
|
||||
response.processFile = false;
|
||||
return response
|
||||
|
||||
}
|
||||
}
|
||||
if(probeData.format.tags.NORMALISATIONSTAGE === "Complete"){
|
||||
response.processFile = false;
|
||||
response.infoLog += "File is already marked as normalised \n"
|
||||
return response
|
||||
} else {
|
||||
//what is this tag?
|
||||
response.processFile = false;
|
||||
response.infoLog += "Unknown normalisation stage tag: \n" + probeData.format.tags.NORMALISATIONSTAGE
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports.onTranscodeSuccess = function onTranscodeSuccess(
|
||||
file,
|
||||
librarySettings,
|
||||
inputs
|
||||
) {
|
||||
|
||||
var response = {
|
||||
file,
|
||||
removeFromDB: false,
|
||||
updateDB: true,
|
||||
};
|
||||
if (secondPass) {
|
||||
response.infoLog += "Audio normalisation complete. \n"
|
||||
//remove old out file
|
||||
if (fs.existsSync(logOutFile)) {
|
||||
fs.unlinkSync(logOutFile);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
else {
|
||||
response.infoLog += "Audio normalisation first pass complete. \n"
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.onTranscodeError = function onTranscodeError(
|
||||
file,
|
||||
librarySettings,
|
||||
inputs
|
||||
) {
|
||||
console.log("Failed to normalise audio");
|
||||
|
||||
//Optional response if you need to modify database
|
||||
var response = {
|
||||
file,
|
||||
removeFromDB: false,
|
||||
updateDB: false,
|
||||
};
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* eslint-disable */
|
||||
//PLugin runs multipass loudnorm filter
|
||||
//first run gets the required details and stores for the next pass
|
||||
//second pass applies the values
|
||||
|
||||
//stages
|
||||
// Determined Loudnorm Values
|
||||
// Applying Normalisation
|
||||
// Normalisation Complete
|
||||
|
||||
|
||||
//setup global vars
|
||||
|
||||
var secondPass = false;
|
||||
var logOutFile = '';
|
||||
|
||||
var fs = require('fs');
|
||||
|
||||
const details = () => {
|
||||
return {
|
||||
id: "Tdarr_Plugin_NIfPZuCLU_2_Pass_Loudnorm_Audio_Normalisation",
|
||||
Stage: 'Pre-processing',
|
||||
Name: "2 Pass Loudnorm Volume Normalisation",
|
||||
Type: "Video",
|
||||
Operation: "Transcode",
|
||||
Description: "PLEASE READ FULL DESCRIPTION BEFORE USE \n Uses multiple passes to normalise audio streams of videos using loudnorm.\n\n The first pass will create an log file in the same directory as the video.\nSecond pass will apply the values determined in the first pass to the file.\nOutput will be MKV to allow metadata to be added for tracking normalisation stage.",
|
||||
Version: "0.1",
|
||||
Tags: "pre-processing,ffmpeg,configurable",
|
||||
|
||||
Inputs: [
|
||||
//(Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI
|
||||
{
|
||||
name: "i",
|
||||
type: 'string',
|
||||
defaultValue:'-23.0',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `\"I\" value used in loudnorm pass \n
|
||||
defaults to -23.0`, //Each line following `Example:` will be clearly formatted. \\n used for line breaks
|
||||
},
|
||||
{
|
||||
name: "lra",
|
||||
type: 'string',
|
||||
defaultValue:'7.0',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Desired lra value. \n Defaults to 7.0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "tp",
|
||||
type: 'string',
|
||||
defaultValue:'-2.0',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Desired \"tp\" value. \n Defaults to -2.0
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "offset",
|
||||
type: 'string',
|
||||
defaultValue:'0.0',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: `Desired "offset" value. \n Defaults to 0.0
|
||||
`,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// eslint-disable-next-line global-require
|
||||
const lib = require('../methods/lib')();
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
|
||||
//Must return this object at some point
|
||||
var response = {
|
||||
processFile: false,
|
||||
preset: '',
|
||||
container: '.mkv',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '',
|
||||
|
||||
}
|
||||
|
||||
response.infoLog += ""
|
||||
//grab the current file being processed and make an out file for the ffmpeg log
|
||||
let currentfilename = file._id;
|
||||
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())
|
||||
|
||||
//setup required varibles
|
||||
var loudNorm_i = -23.0
|
||||
var lra = 7.0
|
||||
var tp = -2.0
|
||||
var offset = 0.0
|
||||
|
||||
//create local varibles for inputs
|
||||
if (inputs !== undefined) {
|
||||
if (inputs.i !== undefined) loudNorm_i = inputs.i
|
||||
if (inputs.lra !== undefined) lra = inputs.lra
|
||||
if (inputs.tp !== undefined) tp = inputs.tp
|
||||
if (inputs.offset !== undefined) offset = inputs.offset
|
||||
}
|
||||
|
||||
|
||||
//check for previous pass tags
|
||||
|
||||
if (typeof probeData.format === "undefined" || typeof probeData.format.tags.NORMALISATIONSTAGE === "undefined" || probeData.format.tags.NORMALISATIONSTAGE === "" || file.forceProcessing === true) {
|
||||
|
||||
//no metadata found first pass is required
|
||||
console.log("Searching for audio normailisation values")
|
||||
response.infoLog += "Searching for required normalisation values. \n"
|
||||
var loudNormInfo = "";
|
||||
|
||||
//Do the first pass, output the log to the out file and use a secondary output for an unchanged file to allow Tdarr to track, Set metadata stage
|
||||
response.preset = `<io>-af loudnorm=I=${loudNorm_i}:LRA=${lra}:TP=${tp}:print_format=json -f null NUL -map 0 -c copy -metadata NORMALISATIONSTAGE="FirstPassComplete" 2>"${logOutFile}"`
|
||||
response.container = '.mkv'
|
||||
response.handBrakeMode = false
|
||||
response.FFmpegMode = true
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = true
|
||||
response.infoLog += "Normalisation first pass processing \n"
|
||||
return response
|
||||
}
|
||||
if (probeData.format.tags.NORMALISATIONSTAGE === "FirstPassComplete") {
|
||||
|
||||
//ensure previous out file exists
|
||||
if (fs.existsSync(logOutFile)) {
|
||||
secondPass = true;
|
||||
loudNormInfo = fs.readFileSync(logOutFile).toString();
|
||||
|
||||
//grab the json from the out file
|
||||
var startIndex = loudNormInfo.lastIndexOf("{");
|
||||
var endIndex = loudNormInfo.lastIndexOf("}");
|
||||
|
||||
var outValues = loudNormInfo.toString().substr(startIndex, endIndex)
|
||||
|
||||
response.infoLog += "Loudnorm first pass values returned: \n" + outValues
|
||||
|
||||
//parse the JSON
|
||||
var loudNormValues = JSON.parse(outValues)
|
||||
|
||||
//use parsed values in second pass
|
||||
response.preset = `-y<io>-af loudnorm=print_format=summary:linear=true:I=${loudNorm_i}:LRA=${lra}:TP=${tp}:measured_i=${loudNormValues.input_i}:measured_lra=${loudNormValues.input_lra}:measured_tp=${loudNormValues.input_tp}:measured_thresh=${loudNormValues.input_thresh}:offset=${loudNormValues.target_offset} -c:a aac -b:a 192k -c:s copy -c:v copy -metadata NORMALISATIONSTAGE="Complete"`
|
||||
response.container = '.mkv'
|
||||
response.handBrakeMode = false
|
||||
response.FFmpegMode = true
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = true
|
||||
response.infoLog += "Normalisation pass processing \n"
|
||||
return response
|
||||
} else {
|
||||
response.infoLog += "Previous log output file is missing. Please rerun with force processing to regenerate."
|
||||
response.processFile = false;
|
||||
return response
|
||||
|
||||
}
|
||||
}
|
||||
if(probeData.format.tags.NORMALISATIONSTAGE === "Complete"){
|
||||
response.processFile = false;
|
||||
response.infoLog += "File is already marked as normalised \n"
|
||||
return response
|
||||
} else {
|
||||
//what is this tag?
|
||||
response.processFile = false;
|
||||
response.infoLog += "Unknown normalisation stage tag: \n" + probeData.format.tags.NORMALISATIONSTAGE
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
module.exports.onTranscodeSuccess = function onTranscodeSuccess(
|
||||
file,
|
||||
librarySettings,
|
||||
inputs
|
||||
) {
|
||||
|
||||
var response = {
|
||||
file,
|
||||
removeFromDB: false,
|
||||
updateDB: true,
|
||||
};
|
||||
if (secondPass) {
|
||||
response.infoLog += "Audio normalisation complete. \n"
|
||||
//remove old out file
|
||||
if (fs.existsSync(logOutFile)) {
|
||||
fs.unlinkSync(logOutFile);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
else {
|
||||
response.infoLog += "Audio normalisation first pass complete. \n"
|
||||
return response;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.onTranscodeError = function onTranscodeError(
|
||||
file,
|
||||
librarySettings,
|
||||
inputs
|
||||
) {
|
||||
console.log("Failed to normalise audio");
|
||||
|
||||
//Optional response if you need to modify database
|
||||
var response = {
|
||||
file,
|
||||
removeFromDB: false,
|
||||
updateDB: false,
|
||||
};
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
Loading…
Reference in new issue