Merge branch 'master' into JB69Plugin

This commit is contained in:
Zach Gelnett 2021-04-14 17:01:48 -04:00 committed by GitHub
commit 2fce8abb1a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
131 changed files with 6173 additions and 156317 deletions

30
.eslintrc.json Normal file
View file

@ -0,0 +1,30 @@
{
"env": {
"commonjs": true,
"es6": true,
"node": true
},
"extends": [
"airbnb-base"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"class-methods-use-this": 0,
"no-case-declarations": 0,
"camelcase": 0,
"jsx-a11y/click-events-have-key-events": 0,
"no-underscore-dangle": ["error", { "allow": ["_id"] }],
"max-len": [
"error",
{
"code": 120
}
]
}
}

25
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,25 @@
name: Node.js CI
on:
push:
branches: ['**']
pull_request:
branches: ['**']
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [10.x, 12.x, 14.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 lint

10
.gitignore vendored Normal file
View file

@ -0,0 +1,10 @@
/node_modules
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### VS Code ###
.vscode/

View file

@ -1,4 +0,0 @@
node_modules/
.github/
# npx prettier@2.0.5 . --write

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_075a_FFMPEG_HEVC_Generic", id: "Tdarr_Plugin_075a_FFMPEG_HEVC_Generic",
@ -41,7 +42,7 @@ function plugin(file) {
} }
response.processFile = true; response.processFile = true;
response.preset = `,-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:v:0 libx265 `; response.preset = `,-map 0:v -map 0:a -map 0:s? -map 0:d? -c copy -c:v:0 libx265 -max_muxing_queue_size 9999`;
response.container = ".mkv"; response.container = ".mkv";
response.handBrakeMode = false; response.handBrakeMode = false;
response.FFmpegMode = true; response.FFmpegMode = true;

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_075a_Transcode_Customisable", id: "Tdarr_Plugin_075a_Transcode_Customisable",
@ -5,7 +6,7 @@ module.exports.details = function details() {
Name: "Video Transcode Customisable", Name: "Video Transcode Customisable",
Type: "", Type: "",
Operation: "Transcode", Operation: "Transcode",
Description: `[TESTING][Contains built-in filter] Specify codec filter and transcode arguments for HandBrake or FFmpeg \n\n`, Description: `[Contains built-in filter] Specify codec filter and transcode arguments for HandBrake or FFmpeg \n\n`,
Version: "1.00", Version: "1.00",
Link: "", Link: "",
Tags: "pre-processing,handbrake,ffmpeg,configurable", Tags: "pre-processing,handbrake,ffmpeg,configurable",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only", id: "Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only",
@ -41,7 +42,7 @@ function plugin(file) {
} }
response.processFile = true; response.processFile = true;
response.preset = `,-map 0:v -map 0:a -c copy -c:v:0 libx265`; response.preset = `,-map 0:v -map 0:a -c copy -c:v:0 libx265 -max_muxing_queue_size 9999`;
response.container = ".mkv"; response.container = ".mkv";
response.handBrakeMode = false; response.handBrakeMode = false;
response.FFmpegMode = true; response.FFmpegMode = true;

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20", id: "Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20", id: "Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_076a_re_order_audio_streams", id: "Tdarr_Plugin_076a_re_order_audio_streams",
@ -5,7 +6,7 @@ module.exports.details = function details() {
Name: "Re-order audio streams", Name: "Re-order audio streams",
Type: "", Type: "",
Operation: "Transcode", Operation: "Transcode",
Description: `[TESTING][Contains built-in filter] Specify a language tag for Tdarr to try and put as 1st audio track \n\n`, Description: `[Contains built-in filter] Specify a language tag for Tdarr to try and put as 1st audio track \n\n`,
Version: "1.00", Version: "1.00",
Link: "", Link: "",
Tags: "pre-processing,audio only,ffmpeg,configurable", Tags: "pre-processing,audio only,ffmpeg,configurable",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_076b_re_order_subtitle_streams", id: "Tdarr_Plugin_076b_re_order_subtitle_streams",
@ -5,7 +6,7 @@ module.exports.details = function details() {
Name: "Re-order subtitle streams", Name: "Re-order subtitle streams",
Type: "", Type: "",
Operation: "Transcode", Operation: "Transcode",
Description: `[TESTING][Contains built-in filter] Specify a language tag for Tdarr to try and put as 1st subtitle track \n\n`, Description: `[Contains built-in filter] Specify a language tag for Tdarr to try and put as 1st subtitle track \n\n`,
Version: "1.00", Version: "1.00",
Link: "", Link: "",
Tags: "pre-processing,subtitle only,ffmpeg,configurable", Tags: "pre-processing,subtitle only,ffmpeg,configurable",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable", id: "Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable",

View file

@ -0,0 +1,78 @@
/* eslint-disable */
module.exports.details = function details() {
return {
id: "Tdarr_Plugin_078d_Output_embedded_subs_to_SRT_and_remove",
Stage: "Pre-processing",
Name: "Output embedded subs to SRT and remove",
Type: "Video",
Operation: "Transcode",
Description: `This plugin outputs embedded subs to SRT and then removes them \n\n`,
Version: "1.00",
Link: "",
Tags: "ffmpeg",
};
};
module.exports.plugin = function plugin(file, librarySettings, inputs, otherArguments) {
//Must return this object at some point in the function else plugin will fail.
let response = {
processFile: false,
preset: "",
container: "",
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: "",
};
const ffmpegPath = otherArguments.ffmpegPath
const exec = require("child_process").exec;
let subsArr = file.ffProbeData.streams.filter(row => row.codec_name === 'subrip')
if (subsArr.length === 0) {
response.infoLog += "No subs in file to extract!";
return response
}
let subStream = subsArr[0]
let lang = ''
if (subStream.tags) {
lang = subStream.tags.language
}
const { originalLibraryFile } = otherArguments;
let subsFile = '';
// for Tdarr V2 (2.00.05+)
if (originalLibraryFile && originalLibraryFile.file) {
subsFile = originalLibraryFile.file;
} else {
// for Tdarr V1
subsFile = file.file;
}
subsFile = subsFile.split('.');
subsFile[subsFile.length - 2] += `.${lang}`;
subsFile[subsFile.length - 1] = 'srt';
subsFile = subsFile.join('.');
let index = subStream.index
let command = `${ffmpegPath} -i "${file.file}" -map 0:${index} "${subsFile}"`
exec(command);
response = {
processFile: true,
preset: `, -map 0 -map -0:${index} -c copy`,
container: "." + file.container,
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: "Found sub to extract!",
};
return response;
};

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_43az_add_to_radarr", id: "Tdarr_Plugin_43az_add_to_radarr",
@ -5,7 +6,7 @@ module.exports.details = function details() {
Name: "Add movie to Radarr after processing", Name: "Add movie to Radarr after processing",
Type: "Video", Type: "Video",
Operation: "", Operation: "",
Description: `[TESTING]Add movie to Radarr after processing \n\n`, Description: `Add movie to Radarr after processing \n\n`,
Version: "1.00", Version: "1.00",
Link: "", Link: "",
Tags: "3rd party,post-processing,configurable", Tags: "3rd party,post-processing,configurable",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_A47j_FFMPEG_NVENC_HEVC_Video_Only", id: "Tdarr_Plugin_A47j_FFMPEG_NVENC_HEVC_Video_Only",
@ -140,7 +141,6 @@ function plugin(file,librarySettings,inputs,otherArguments) {
return response; return response;
} }
// How much does HVEC compress the raw stream? // How much does HVEC compress the raw stream?
var compressionFactor = 0.07; var compressionFactor = 0.07;
if ( ! isNaN(Number(inputs.compressionFactor)) ) { if ( ! isNaN(Number(inputs.compressionFactor)) ) {
@ -176,7 +176,7 @@ function plugin(file,librarySettings,inputs,otherArguments) {
// -------------------------------- METADATA UPDATES -------------------------------- // -------------------------------- METADATA UPDATES --------------------------------
// If there is no _STATISTICS_WRITING_DATE_UTC-eng field, then we need to run mkvpropedit and // If there is no _STATISTICS_WRITING_DATE_UTC-eng field, then we need to run mkvpropedit and
// rerun mediainfo to load the stats. // rerun mediainfo to load the stats.
if (file.ffProbeData.streams[0].tags["_STATISTICS_WRITING_DATE_UTC-eng"] === undefined ) { if (file.ffProbeData.streams[0].tags === undefined || file.ffProbeData.streams[0].tags["_STATISTICS_WRITING_DATE_UTC-eng"] === undefined ) {
response.infoLog += "☑Track statistics are missing.\n"; response.infoLog += "☑Track statistics are missing.\n";
updateTrackStats(file); updateTrackStats(file);
getMediaInfo(file); getMediaInfo(file);
@ -200,20 +200,18 @@ function plugin(file,librarySettings,inputs,otherArguments) {
updateTrackStats(file); updateTrackStats(file);
getMediaInfo(file); getMediaInfo(file);
if ( isNaN(MediaInfo.videoBR) || isNaN(MediaInfo.videoBitDepth) ) { if ( isNaN(MediaInfo.videoBR) || isNaN(MediaInfo.videoBitDepth) ) {
response.infoLog += "videoBR or videoBitDepth still NaN, giving up.\n"; response.infoLog += "videoBR or videoBitDepth still NaN, using default.\n";
throw ("MediaInfo.videoBR or videoBitDepth still NaN, giving up.");
} }
} }
// If the overall bitrate is less than the videoBR, then something is wacky. // If the overall bitrate is less than the videoBR, then something is wacky.
if ( MediaInfo.videoBR > MediaInfo.overallBR ) { if ( MediaInfo.videoBR > MediaInfo.overallBR ) {
response.infoLog += `videoBR (${MediaInfo.videoBR} was greater than overallBR (${MediaInfo.overallBR}), response.infoLog += `videoBR (${MediaInfo.videoBR} was greater than overallBR (${MediaInfo.overallBR}),
which is impossible. Updating stats.\n`; which is impossible. Updating stats.\n`;
updateTrackStats(file); updateTrackStats(file);
getMediaInfo(file); getMediaInfo(file);
if ( MediaInfo.videoBR > MediaInfo.overallBR ) { if ( MediaInfo.videoBR > MediaInfo.overallBR ) {
response.infoLog += `videoBR and overallBR still inconsistent, giving up.\n`; response.infoLog += `videoBR and overallBR still inconsistent, using default.\n`;
throw (`videoBR (${MediaInfo.videoBR}) and overallBR (${MediaInfo.overallBR}) still inconsistent, giving up.`);
} }
} }
@ -263,10 +261,13 @@ response.infoLog += `Video details: ${file.ffProbeData.streams[0].codec_name}-${
var maxBitrate = Math.round(targetBitrate*1.3); var maxBitrate = Math.round(targetBitrate*1.3);
var minBitrate = Math.round(targetBitrate*0.7); var minBitrate = Math.round(targetBitrate*0.7);
var bufsize = Math.round(MediaInfo.videoBR); if ( isNaN(MediaInfo.videoBR) ) {
var bufsize = targetBitrate;
} else {
var bufsize = Math.round(MediaInfo.videoBR);
}
response.preset += `,-map 0:v -map 0:a -map 0:s? -map -:d? -c copy -c:v:0 hevc_nvenc -rc:v vbr_hq -preset medium -profile:v main10 -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -max_muxing_queue_size 4096 `;
response.preset += `,-map 0:v -map 0:a -map 0:s? -map -:d? -c copy -c:v:0 hevc_nvenc -rc:v vbr_hq -preset medium -profile:v main -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -max_muxing_queue_size 4096 `;
response.infoLog += `Video bitrate is ${Math.round(MediaInfo.videoBR/1000)}Kbps, overall is ${Math.round(MediaInfo.overallBR/1000)}Kbps. `; response.infoLog += `Video bitrate is ${Math.round(MediaInfo.videoBR/1000)}Kbps, overall is ${Math.round(MediaInfo.overallBR/1000)}Kbps. `;
response.infoLog += `Calculated target is ${Math.round(targetBitrate/1000)}Kbps.\n`; response.infoLog += `Calculated target is ${Math.round(targetBitrate/1000)}Kbps.\n`;
@ -274,7 +275,13 @@ response.infoLog += `Calculated target is ${Math.round(targetBitrate/1000)}Kbps.
// Adjust target bitrates by codec and bitrate // Adjust target bitrates by codec and bitrate
switch (file.ffProbeData.streams[0].codec_name) { switch (file.ffProbeData.streams[0].codec_name) {
case "hevc": case "hevc":
if ( (MediaInfo.videoBR > targetBitrate*1.5) || file.forceProcessing === true ) { if ( isNaN(MediaInfo.videoBR) ) {
response.processFile = true;
targetBitrate = Math.min(MediaInfo.overallBR, targetBitrate);
response.preset +=` -b:v ${targetBitrate} -maxrate ${maxBitrate} -minrate ${minBitrate} -bufsize ${bufsize} `;
response.infoLog += `☒HEVC Bitrate for ${file.video_resolution} could not be determined,
using sensible default of ${Math.round(targetBitrate/1000)}Kbps.\n`;
} else if ( (MediaInfo.videoBR > targetBitrate*1.5) || file.forceProcessing === true ) {
response.processFile = true; response.processFile = true;
response.preset +=` -b:v ${targetBitrate} -maxrate ${maxBitrate} -minrate ${minBitrate} -bufsize ${bufsize} `; response.preset +=` -b:v ${targetBitrate} -maxrate ${maxBitrate} -minrate ${minBitrate} -bufsize ${bufsize} `;
response.infoLog += `☒HEVC Bitrate for ${file.video_resolution} exceeds ${Math.round(targetBitrate*1.5/1000)}Kbps, response.infoLog += `☒HEVC Bitrate for ${file.video_resolution} exceeds ${Math.round(targetBitrate*1.5/1000)}Kbps,
@ -286,18 +293,22 @@ response.infoLog += `Calculated target is ${Math.round(targetBitrate/1000)}Kbps.
case "h264": case "h264":
response.processFile = true; response.processFile = true;
// We want the new bitrate to be 70% the h264 bitrate, but not higher than our target. // We want the new bitrate to be 70% the h264 bitrate, but not higher than our target.
if ( isNaN(MediaInfo.videoBR) ) {
new_bitrate = Math.min(MediaInfo.overallBR*0.7,targetBitrate);
} else {
new_bitrate = Math.min(Math.round(MediaInfo.videoBR*0.7),targetBitrate); new_bitrate = Math.min(Math.round(MediaInfo.videoBR*0.7),targetBitrate);
// New bitrate should not be lower than our 60% of our target. // New bitrate should not be lower than our 60% of our target.
new_bitrate = Math.max( new_bitrate, Math.min(MediaInfo.videoBR, targetBitrate*0.6) ); new_bitrate = Math.max( new_bitrate, Math.min(MediaInfo.videoBR, targetBitrate*0.6) );
}
response.preset +=` -b:v ${new_bitrate} -maxrate ${Math.round(new_bitrate*1.3)} -minrate ${Math.round(new_bitrate*0.7)} -bufsize ${bufsize}`; response.preset +=` -b:v ${new_bitrate} -maxrate ${Math.round(new_bitrate*1.3)} -minrate ${Math.round(new_bitrate*0.7)} -bufsize ${bufsize}`;
response.infoLog += `☒H264 Resolution is ${file.video_resolution}, bitrate was ${Math.round(MediaInfo.videoBR/1000)}Kbps. response.infoLog += `☒H264 Resolution is ${file.video_resolution}, bitrate was ${Math.round(MediaInfo.videoBR/1000)}Kbps.
HEVC target bitrate will be ${Math.round(new_bitrate/1000)}Kbps.\n`; HEVC target bitrate will be ${Math.round(new_bitrate/1000)}Kbps.\n`;
break; // case "h264" break; // case "h264"
default: default:
response.processFile = true; response.processFile = true;
response.preset +=` -b:v ${targetBitrate} -maxrate ${maxBitrate} -minrate ${minBitrate}K -bufsize ${bufsize} `; response.preset +=` -b:v ${targetBitrate} -maxrate ${maxBitrate} -minrate ${minBitrate} -bufsize ${bufsize} `;
response.infoLog += `${file.ffProbeData.streams[0].codec_name} resolution is ${file.video_resolution}, response.infoLog += `${file.ffProbeData.streams[0].codec_name} resolution is ${file.video_resolution},
bitrate was ${Math.round(MediaInfo.videoBR/1000)}Kbps. HEVC target bitrate will be ${Math.round(new_bitrate/1000)}Kbps.\n`; bitrate was ${Math.round(MediaInfo.videoBR/1000)}Kbps. HEVC target bitrate will be ${Math.round(targetBitrate/1000)}Kbps.\n`;
break; // default break; // default
} // switch (file.ffProbeData.streams[0].codec_name) } // switch (file.ffProbeData.streams[0].codec_name)

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_DOOM_NVENC_Tiered_MKV_CleanAll", id: "Tdarr_Plugin_DOOM_NVENC_Tiered_MKV_CleanAll",
@ -194,7 +195,7 @@ function loopOverStreamsOfType(file, type, method) {
} }
/** /**
* Converts all multi channel audio streams to AC3. * Removes audio tracks that aren't in the allowed languages or labeled as Commentary tracks.
*/ */
function buildAudioConfiguration(inputs, file, logger) { function buildAudioConfiguration(inputs, file, logger) {
var configuration = new Configurator(["-c:a copy"]); var configuration = new Configurator(["-c:a copy"]);
@ -202,7 +203,7 @@ function buildAudioConfiguration(inputs, file, logger) {
var streams_removing = 0; var streams_removing = 0;
var languages = inputs.audio_language.split(","); var languages = inputs.audio_language.split(",");
loopOverStreamsOfType(file, "audio", function (stream, id) { loopOverStreamsOfType(file, "audio", function (stream, id) {
stream_count++; stream_count++;
if ("tags" in stream && "title" in stream.tags && inputs.audio_commentary.toLowerCase() == "true") { if ("tags" in stream && "title" in stream.tags && inputs.audio_commentary.toLowerCase() == "true") {
if ( if (
stream.tags.title.toLowerCase().includes("commentary") || stream.tags.title.toLowerCase().includes("commentary") ||
@ -216,12 +217,12 @@ function buildAudioConfiguration(inputs, file, logger) {
); );
} }
} }
if ("tags" in stream) { if ("tags" in stream) {
// Remove unwated languages // Remove unwanted languages
if ("language" in stream.tags) { if ("language" in stream.tags) {
if (languages.indexOf(stream.tags.language.toLowerCase()) === -1) { if (languages.indexOf(stream.tags.language.toLowerCase()) === -1) {
configuration.AddOutputSetting(`-map -0:a:${id}`); configuration.AddOutputSetting(`-map -0:a:${id}`);
streams_removing++; streams_removing++;
logger.AddError( logger.AddError(
`Removing audio track in language ${stream.tags.language}` `Removing audio track in language ${stream.tags.language}`
); );

View file

@ -0,0 +1,394 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() {
return {
id: 'Tdarr_Plugin_ER01_Transcode audio and video with HW (PC and Mac)',
Stage: 'Pre-processing',
Name: 'Transcode Using QSV or VT & FFMPEG',
Type: 'Video',
Operation: 'Transcode',
Description: `Files not in H265 will be transcoded into H265 using hw with ffmpeg, assuming mkv container. Plugin uses QS if the node runs on a PC, or Videotoolbox if run on a Mac.
Much thanks to Migz for bulk of the important code.
Quality is controlled via bitrate adjustments - H264 to H265 assumes 0.5x bitrate. Resolution change from 1080p to 720p assumes 0.7x bitrate.
Audio conversion is either 2 channel ac3 or 6 channel ac3, for maximal compatibility and small file size. All subtitles removed.
The idea is to homogenize your collection to 1080p or higher movies with 5.1 audio, or 720p TV shows with 2.0 audio.`,
Tags: 'pre-processing,ffmpeg,video only,configurable,h265',
Inputs: [{
name: 'audio_channels',
tooltip: `Specify whether to modify audio channels.
\\n Leave empty to disable.
\\nExample:\\n
2 - produces single 2.0 channel ac3 audio file, in English, unless not possible.
\\nExample:\\n
6 - produces single 5.1 channel ac3 file, in English, unless not possible.`,
},
{
name: 'resize',
tooltip: `Specify if output file should be reduced to 720p from 1080p. Default is false.
\\nExample:\\n
yes
\\nExample:\\n
no`,
},
{
name: 'bitrate_cutoff',
tooltip: `Specify bitrate cutoff, files with a current bitrate lower then this will not be transcoded.
\\n Rate is in kbps.
\\n Leave empty to disable.
\\nExample:\\n
6000
\\nExample:\\n
4000`,
},
],
};
}
function plugin(file, librarySettings, inputs) {
const response = {
container: '.mkv',
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '',
};
let duration = '';
let convertAudio = false;
let convertVideo = false;
let extraArguments = '';
// Check if inputs.container has been configured. If it hasn't then exit plugin.
if (inputs.container === '') {
response.infoLog += 'Plugin has not been configured, please configure required options. Skipping this plugin. \n';
response.processFile = false;
return response;
}
// 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. \n';
return response;
}
const os = require('os');
// VIDEO SECTION
let bitRateMultiplier = 1.00;
let videoIdx = -1;
let willBeResized = false;
let videoOptions = `-map 0:v -c:v copy `;
// video options
// hevc, 1080, false - do nothing
// hevc, not 1080 - do nothing
// hevc, 1080, true - resize, mult 0.5
// not hevc, 1080, true - resize, mult 0.25
// not hevc, 1080, false - no resize, mult 0.5
// not hevc, not 1080 - no resize, mult 0.5
// Go through each stream in the file.
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Check if stream is a video.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'video') {
// Check if codec of stream is mjpeg/png. If so then remove this "video" stream.
// mjpeg/png are usually embedded pictures that can cause havoc with plugins.
if (file.ffProbeData.streams[i].codec_name === 'mjpeg' || file.ffProbeData.streams[i].codec_name === 'png' ) {
extraArguments += `-map -v:${videoIdx} `;
convertVideo = true; }
/* // no video conversion if: hevc, 1080, false OR hevc, not 1080
if (file.ffProbeData.streams[i].codec_name === 'hevc'
&& ((file.video_resolution === '1080p' && inputs.resize === 'no' ) || (file.video_resolution !== '1080p' ))) {
convertVideo = false; } */
// no video conversion if: hevc, 1080, false
if (file.ffProbeData.streams[i].codec_name === 'hevc' && file.ffProbeData.streams[i].width > 1800 && file.ffProbeData.streams[i].width < 2000 && inputs.resize === 'no' ) {
convertVideo = false; }
// no video conversion if: hevc, not 1080
if (file.ffProbeData.streams[i].codec_name === 'hevc' && (file.ffProbeData.streams[i].width < 1800 || file.ffProbeData.streams[i].width > 2000)) {
convertVideo = false; }
// resize video if: hevc, 1080, true
if (file.ffProbeData.streams[i].codec_name === 'hevc' && file.ffProbeData.streams[i].width > 1800 && file.ffProbeData.streams[i].width < 2000 && inputs.resize === 'yes' ) {
convertVideo = true;
willBeResized = true;
bitRateMultiplier = 0.7; }
// resize video if: not hevc, 1080, true
if (file.ffProbeData.streams[i].codec_name !== 'hevc' && file.ffProbeData.streams[i].width > 1800 && file.ffProbeData.streams[i].width < 2000 && inputs.resize === 'yes' ) {
convertVideo = true;
willBeResized = true;
bitRateMultiplier = 0.4; }
// no resize video if: not hevc, 1080, false
if (file.ffProbeData.streams[i].codec_name !== 'hevc' && file.ffProbeData.streams[i].width > 1800 && file.ffProbeData.streams[i].width < 2000 && inputs.resize === 'no' ) {
convertVideo = true;
bitRateMultiplier = 0.5; }
// no resize video if: not hevc, not 1080
if (file.ffProbeData.streams[i].codec_name !== 'hevc' && file.ffProbeData.streams[i].width < 1800 ) {
convertVideo = true;
bitRateMultiplier = 0.5; }
}
// Increment videoIdx.
videoIdx += 1;
}
// figure out final bitrate
// Check if duration info is filled, if so times it by 0.0166667 to get time in minutes.
// If not filled then get duration of stream 0 and do the same.
if (typeof file.meta.Duration !== 'undefined') {
duration = file.meta.Duration * 0.0166667;
} else {
duration = file.ffProbeData.streams[0].duration * 0.0166667;
}
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/
// eslint-disable-next-line no-bitwise
const currentBitrate = ~~(file.file_size / (duration * 0.0075));
// Use the same calculation used for currentBitrate but divide it in half to get targetBitrate.
// Logic of h265 can be half the bitrate as h264 without losing quality.
// eslint-disable-next-line no-bitwise
const targetBitrate = ~~(file.file_size / (duration * 0.0075) * bitRateMultiplier);
// Allow some leeway under and over the targetBitrate.
const minimumBitrate = ~~(targetBitrate * 0.7);
const maximumBitrate = ~~(targetBitrate * 1.3);
// If targetBitrate comes out as 0 then something has gone wrong and bitrates could not be calculcated.
if (targetBitrate === 0) {
response.processFile = false;
response.infoLog += 'Target bitrate could not be calculated. Skipping this plugin. \n';
return response;
}
// Check if inputs.bitrate cutoff has something entered.
// (Entered means user actually wants something to happen, empty would disable this).
if (inputs.bitrate_cutoff !== '') {
// Checks if currentBitrate is below inputs.bitrate_cutoff
// If so then don't convert video.
if (currentBitrate <= inputs.bitrate_cutoff) {
convertVideo = false; }
}
// AUDIO SECTION
// Set up required variables.
let audioOptions = `-map 0:a -c:a copy `;
let audioIdx = 0;
let numberofAudioChannels = 0;
let has2Channels = false;
let has6Channels = false;
let has8Channels = false;
let lang2Channels = '';
let lang6Channels = '';
let lang8Channels = '';
let type2Channels = '';
let type6Channels = '';
let type8Channels = '';
let keepAudioIdx = -1;
let keepIGuessAudioIdx = -1;
let encodeAudioIdx = -1;
let keepAudioStream = -1;
let encodeAudioStream = -1;
let originalAudio = '';
// Go through each stream in the file.
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try {
// Go through all audio streams and check if 2,6 & 8 channel tracks exist or not.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
numberofAudioChannels += 1;
if (file.ffProbeData.streams[i].channels === 2 && has2Channels === false) {
has2Channels = true;
lang2Channels = file.ffProbeData.streams[i].tags.language.toLowerCase();
type2Channels = file.ffProbeData.streams[i].codec_name.toLowerCase();
}
if (file.ffProbeData.streams[i].channels === 6 && has6Channels === false) {
has6Channels = true;
lang6Channels = file.ffProbeData.streams[i].tags.language.toLowerCase();
type6Channels = file.ffProbeData.streams[i].codec_name.toLowerCase();
}
if (file.ffProbeData.streams[i].channels === 8 && has8Channels === false) {
has8Channels = true;
lang8Channels = file.ffProbeData.streams[i].tags.language.toLowerCase();
type8Channels = file.ffProbeData.streams[i].codec_name.toLowerCase();
}
}
} catch (err) {
// Error
}
}
// Are we processing for 6 channels?
if (inputs.audio_channels == 6) {
audioIdx = -1;
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try {
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
audioIdx += 1;
if (file.ffProbeData.streams[i].tags.language.toLowerCase() === 'eng' || file.ffProbeData.streams[i].tags.language.toLowerCase() === 'und') {
if (file.ffProbeData.streams[i].channels == 6 ) {
if (file.ffProbeData.streams[i].codec_name.toLowerCase() === 'ac3') {
//response.infoLog += `Found 6 channel audio in proper language and codec, audio stream ${audioIdx}\n`;
if (keepAudioIdx === -1) {
keepAudioIdx = audioIdx;
keepAudioStream = i;}
} else {
//response.infoLog += `Found 6 channel audio in proper language, need to re-encode, audio stream ${audioIdx}\n`;
if (encodeAudioIdx === -1) {
encodeAudioIdx = audioIdx;
encodeAudioStream = i;}
}}
if (file.ffProbeData.streams[i].channels > 6 ) {
//response.infoLog += `Found existing multi-channel audio in proper language, need to re-encode, audio stream ${audioIdx}\n`;
if (encodeAudioIdx === -1) {
encodeAudioIdx = audioIdx;
encodeAudioStream = i;}
}
}
}
} catch (err) {
// Error
}
}
if (keepAudioIdx === -1 && encodeAudioIdx === -1) { // didn't find any 5.1 or better audio streams in proper language, defaulting to using 2 channels
inputs.audio_channels = '2';}
}
// Are we processing for 2 channels?
if (inputs.audio_channels == 2) {
audioIdx = -1;
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try {
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
audioIdx += 1;
if (file.ffProbeData.streams[i].tags.language.toLowerCase() === 'eng' || file.ffProbeData.streams[i].tags.language.toLowerCase() === 'und') {
if (file.ffProbeData.streams[i].channels == 2 ) {
if (file.ffProbeData.streams[i].codec_name.toLowerCase() === 'aac' || file.ffProbeData.streams[i].codec_name.toLowerCase() === 'ac3') {
//response.infoLog += `Found 2 channel audio in proper language and codec, audio stream ${audioIdx}\n`;
if (keepAudioIdx === -1) {
keepAudioIdx = audioIdx;
keepAudioStream = i;}
} else {
//response.infoLog += `Found 2 channel audio in proper language, need to re-encode, audio stream ${audioIdx}\n`;
if (encodeAudioIdx === -1) {
encodeAudioIdx = audioIdx;
encodeAudioStream = i;}
}
} else {
//response.infoLog += `Found existing multi-channel audio in proper language, need to re-encode, audio stream ${audioIdx}\n`;
if (encodeAudioIdx === -1) {
encodeAudioIdx = audioIdx;
encodeAudioStream = i;}
}
}
// response.infoLog += `a ${audioIdx}. k ${keepAudioIdx}. e ${encodeAudioIdx}\n `;
}
} catch (err) {
// Error
}
}
}
let audioMessage = '';
// selecting channels to keep, only if 2 or 6 channels processed
if (keepAudioIdx !== -1) {
//keep audio, exclude everything else
if (numberofAudioChannels !== 1) {
convertAudio = true;
audioMessage += `keeping audio stream ${keepAudioIdx}.`;
audioOptions = `-map 0:a:${keepAudioIdx} -c:a copy `;
originalAudio += `${file.ffProbeData.streams[keepAudioStream].channels} channel ${file.ffProbeData.streams[keepAudioStream].codec_name} --> ${inputs.audio_channels} channel ac3`;}
} else {
if (encodeAudioIdx !== -1) {
// encode this audio
convertAudio = true;
audioMessage += `encoding audio stream ${encodeAudioIdx}. `;
audioOptions = `-map 0:a:${encodeAudioIdx} -c:a ac3 -ac ${inputs.audio_channels} `; // 2 or 6 channels encoding
originalAudio += `${file.ffProbeData.streams[encodeAudioStream].channels} channel ${file.ffProbeData.streams[encodeAudioStream].codec_name} --> ${inputs.audio_channels} channel ac3`;
} else {
// do not encode audio
convertAudio = false;
audioMessage += `no audio to encode.`;
}
}
// test for whether the file needs to be processed - separate for video and audio convertAudio, convertVideo
if (convertAudio === false && convertVideo === false) { // if nothing to do, exit
response.infoLog += `File is processed already, nothing to do`;
response.processFile = false;
return response; }
// Generate ffmpeg command line arguments in total
// few defaults
response.preset = `, -sn `;
if (convertVideo === true) {
// Set bitrateSettings variable using bitrate information calculated earlier.
bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k `
+ `-maxrate ${maximumBitrate}k -bufsize ${currentBitrate}k`;
if (willBeResized === true) {
extraArguments += `-filter:v scale=1280:-1 `; }
if (os.platform() === 'darwin') {
videoOptions = `-map 0:v -c:v hevc_videotoolbox -profile main `;
}
if (os.platform() === 'win32') {
videoOptions = `-map 0:v -c:v hevc_qsv -load_plugin hevc_hw `;
}
}
response.preset += `${videoOptions} ${bitrateSettings} ${extraArguments} ${audioOptions} `;
let outputResolution = file.video_resolution;
if (willBeResized === true) {
outputResolution = '720p';}
if (convertVideo === false) {
response.infoLog += `NOT converting video ${file.video_resolution}, ${file.video_codec_name}, bitrate = ${currentBitrate} \n`;
} else {
response.infoLog += `Converting video, `;
if (willBeResized === false ) { response.infoLog += `NOT `; }
response.infoLog += `resizing. ${file.video_resolution}, ${file.video_codec_name} --> ${outputResolution}, hevc. bitrate = ${currentBitrate} --> ${targetBitrate}, multiplier ${bitRateMultiplier}. \n`;
}
if (convertAudio === true) {
response.infoLog += `Converting audio, ${audioMessage} ${originalAudio}. \n`;
} else {
response.infoLog += `Not converting audio. \n`;}
response.infoLog += `2 channels - ${lang2Channels} ${type2Channels} \n`;
response.infoLog += `6 channels - ${lang6Channels} ${type6Channels} \n`;
response.infoLog += `8 channels - ${lang8Channels} ${type8Channels} `;
response.processFile = true;
return response;
}
module.exports.details = details;
module.exports.plugin = plugin;

View file

@ -33,7 +33,7 @@
// If the source video is less than this rate the script will either: // If the source video is less than this rate the script will either:
// Copy the existing stream, if the codec is hevc // Copy the existing stream, if the codec is hevc
// Transcode the stream to hevc using 80% of the original streams bitrate // Transcode the stream to hevc using 80% of the original streams bitrate
// It could probably be less but if the source is of low bitrate we dont want to compromise too much on the transcode // It could probably be less but if the source is of low bitrate we don<EFBFBD>t want to compromise too much on the transcode
// //
// If the source media bitrate is close, within 10%, of the target bitrate and the codec is hevc, it will copy instead of transcode to preserve quality // If the source media bitrate is close, within 10%, of the target bitrate and the codec is hevc, it will copy instead of transcode to preserve quality
// //
@ -52,7 +52,7 @@
// If the source audio is less than this rate the script will either: // If the source audio is less than this rate the script will either:
// Copy the existing stream, if the codec is aac // Copy the existing stream, if the codec is aac
// Transcode the stream to aac using 100% of the original streams bitrate // 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 dont want to compromise too much on the transcode // 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: // Subtitles:
// All are removed?? (TODO: ensure this is correct and mention the flag to keep them if desired) // All are removed?? (TODO: ensure this is correct and mention the flag to keep them if desired)

View file

@ -1,3 +1,4 @@
/* eslint-disable */
////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////
// //
// Author: JarBinks, Zachg99, Jeff47 // Author: JarBinks, Zachg99, Jeff47

View file

@ -1,233 +1,292 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MC93_Migz1FFMPEG", id: 'Tdarr_Plugin_MC93_Migz1FFMPEG',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "Migz-Transcode Using Nvidia GPU & FFMPEG", Name: 'Migz-Transcode Using Nvidia GPU & FFMPEG',
Type: "Video", Type: 'Video',
Operation: "Transcode", Operation: 'Transcode',
Description: `Files not in H265 will be transcoded into H265 using Nvidia GPU with ffmpeg, settings are dependant on file bitrate, working by the logic that H265 can support the same ammount of data at half the bitrate of H264. NVDEC & NVENC compatable GPU required. \n\n`, Description: `Files not in H265 will be transcoded into H265 using Nvidia GPU with ffmpeg.
Version: "2.5", Settings are dependant on file bitrate
Link: Working by the logic that H265 can support the same ammount of data at half the bitrate of H264.
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz1FFMPEG.js", NVDEC & NVENC compatable GPU required.
Tags: "pre-processing,ffmpeg,video only,nvenc h265,configurable", This plugin will skip any files that are in the VP9 codec.`,
Inputs: [ Version: '3.0',
{ Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz1FFMPEG.js',
name: "container", Tags: 'pre-processing,ffmpeg,video only,nvenc h265,configurable',
tooltip: `Specify output container of file, ensure that all stream types you may have are supported by your chosen container. mkv is recommended. Inputs: [{
\\nExample:\\n name: 'container',
mkv tooltip: `Specify output container of file
\\n Ensure that all stream types you may have are supported by your chosen container.
\\n mkv is recommended.
\\nExample:\\n
mkv
\\nExample:\\n \\nExample:\\n
mp4`, mp4`,
}, },
{ {
name: "bitrate_cutoff", name: 'bitrate_cutoff',
tooltip: `Specify bitrate cutoff, files with a current bitrate lower then this will not be transcoded. Rate is in kbps. Leave empty to disable. tooltip: `Specify bitrate cutoff, files with a current bitrate lower then this will not be transcoded.
\\nExample:\\n \\n Rate is in kbps.
6000 \\n Leave empty to disable.
\\nExample:\\n
6000
\\nExample:\\n \\nExample:\\n
4000`, 4000`,
}, },
{ {
name: "enable_10bit", name: 'enable_10bit',
tooltip: `Specify if output file should be 10bit. Default is false. tooltip: `Specify if output file should be 10bit. Default is false.
\\nExample:\\n \\nExample:\\n
true true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
{ {
name: "force_conform", name: 'enable_bframes',
tooltip: `Make the file conform to output containers requirements. tooltip: `Specify if b frames should be used.
\\n Drop hdmv_pgs_subtitle/eia_608/subrip subtitles for MP4. \\n Using B frames should decrease file sizes but are only supported on newer GPUs.
\\n Drop data streams and mov_text/eia_608 subtitles for MKV. \\n Default is false.
\\nExample:\\n
true
\\nExample:\\n
false`,
},
{
name: 'force_conform',
tooltip: `Make the file conform to output containers requirements.
\\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. \\n Default is false.
\\nExample:\\n \\nExample:\\n
true true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
], ],
}; };
} }
function plugin(file, librarySettings, inputs) { function plugin(file, librarySettings, inputs) {
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: true, FFmpegMode: true,
reQueueAfter: true, reQueueAfter: true,
infoLog: "", infoLog: '',
}; };
let duration = '';
// Check if inputs.container has been configured. If it hasn't then exit plugin. // Check if inputs.container has been configured. If it hasn't then exit plugin.
if (inputs.container == "") { if (inputs.container === '') {
response.infoLog += response.infoLog += 'Plugin has not been configured, please configure required options. Skipping this plugin. \n';
"☒Container has not been configured within plugin settings, please configure required options. Skipping this plugin. \n";
response.processFile = false; response.processFile = false;
return response; return response;
} else {
response.container = "." + inputs.container;
} }
response.container = `.${inputs.container}`;
// Check if file is a video. If it isn't then exit plugin. // Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
response.processFile = false; response.processFile = false;
response.infoLog += "☒File is not a video. \n"; response.infoLog += 'File is not a video. \n';
return response; return response;
} }
// Check if duration info is filled, if so times it by 0.0166667 to get time in minutes. If not filled then get duration of stream 0 and do the same. // Check if duration info is filled, if so times it by 0.0166667 to get time in minutes.
if (typeof file.meta.Duration != "undefined") { // If not filled then get duration of stream 0 and do the same.
var duration = file.meta.Duration * 0.0166667; if (typeof file.meta.Duration !== 'undefined') {
duration = file.meta.Duration * 0.0166667;
} else { } else {
var duration = file.ffProbeData.streams[0].duration * 0.0166667; duration = file.ffProbeData.streams[0].duration * 0.0166667;
} }
// Set up required variables. // Set up required variables.
var videoIdx = 0; let videoIdx = 0;
var CPU10 = false; let CPU10 = false;
var extraArguments = ""; let extraArguments = '';
var bitrateSettings = ""; 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/ // Work out currentBitrate using "Bitrate = file size / (number of minutes * .0075)"
var currentBitrate = ~~(file.file_size / (duration * 0.0075)); // Used from here https://blog.frame.io/2017/03/06/calculate-video-bitrates/
// Use the same calculation used for currentBitrate but divide it in half to get targetBitrate. Logic of h265 can be half the bitrate as h264 without losing quality. // eslint-disable-next-line no-bitwise
var targetBitrate = ~~(file.file_size / (duration * 0.0075) / 2); const currentBitrate = ~~(file.file_size / (duration * 0.0075));
// Use the same calculation used for currentBitrate but divide it in half to get targetBitrate.
// Logic of h265 can be half the bitrate as h264 without losing quality.
// eslint-disable-next-line no-bitwise
const targetBitrate = ~~(file.file_size / (duration * 0.0075) / 2);
// Allow some leeway under and over the targetBitrate. // Allow some leeway under and over the targetBitrate.
var minimumBitrate = ~~(targetBitrate * 0.7); // eslint-disable-next-line no-bitwise
var maximumBitrate = ~~(targetBitrate * 1.3); const minimumBitrate = ~~(targetBitrate * 0.7);
// eslint-disable-next-line no-bitwise
const maximumBitrate = ~~(targetBitrate * 1.3);
// If targetBitrate comes out as 0 then something has gone wrong and bitrates could not be calculcated. Cancel plugin completely. // If targetBitrate comes out as 0 then something has gone wrong and bitrates could not be calculated.
if (targetBitrate == "0") { // Cancel plugin completely.
if (targetBitrate === 0) {
response.processFile = false; response.processFile = false;
response.infoLog += response.infoLog += 'Target bitrate could not be calculated. Skipping this plugin. \n';
"☒Target bitrate could not be calculated. Skipping this plugin. \n";
return response; return response;
} }
// Check if inputs.bitrate cutoff has something entered (Entered means user actually wants something to happen, empty would disable this). // Check if inputs.bitrate cutoff has something entered.
if (inputs.bitrate_cutoff != "") { // (Entered means user actually wants something to happen, empty would disable this).
// Checks if currentBitrate is below inputs.bitrate_cutoff, if so then cancel plugin without touching original files. if (inputs.bitrate_cutoff !== '') {
// Checks if currentBitrate is below inputs.bitrate_cutoff.
// If so then cancel plugin without touching original files.
if (currentBitrate <= inputs.bitrate_cutoff) { if (currentBitrate <= inputs.bitrate_cutoff) {
response.processFile = false; response.processFile = false;
response.infoLog += `☑Current bitrate is below configured bitrate cutoff of ${inputs.bitrate_cutoff}. Nothing to do, cancelling plugin. \n`; response.infoLog += `Current bitrate is below set cutoff of ${inputs.bitrate_cutoff}. Cancelling plugin. \n`;
return response; return response;
} }
} }
// Check if force_conform option is checked. If so then check streams and add any extra parameters required to make file conform with output format. // Check if force_conform option is checked.
if (inputs.force_conform == "true") { // If so then check streams and add any extra parameters required to make file conform with output format.
if (inputs.container.toLowerCase() == "mkv") { if (inputs.force_conform === 'true') {
extraArguments += `-map -0:d `; if (inputs.container.toLowerCase() === 'mkv') {
for (var i = 0; i < file.ffProbeData.streams.length; i++) { extraArguments += '-map -0:d ';
if ( for (let i = 0; i < file.ffProbeData.streams.length; i++) {
file.ffProbeData.streams[i].codec_name try {
.toLowerCase() == "mov_text" || if (
file.ffProbeData.streams[i].codec_name file.ffProbeData.streams[i].codec_name
.toLowerCase() == "eia_608" .toLowerCase() === 'mov_text'
) { || file.ffProbeData.streams[i].codec_name
extraArguments += `-map -0:${i} `; .toLowerCase() === 'eia_608'
} || file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'timed_id3'
) {
extraArguments += `-map -0:${i} `;
}
} catch (err) {
// Error
} }
}
} }
if (inputs.container.toLowerCase() == "mp4") { if (inputs.container.toLowerCase() === 'mp4') {
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
if ( try {
file.ffProbeData.streams[i].codec_name if (
.toLowerCase() == "hdmv_pgs_subtitle" || file.ffProbeData.streams[i].codec_name
file.ffProbeData.streams[i].codec_name .toLowerCase() === 'hdmv_pgs_subtitle'
.toLowerCase() == "eia_608" || || file.ffProbeData.streams[i].codec_name
file.ffProbeData.streams[i].codec_name .toLowerCase() === 'eia_608'
.toLowerCase() == "subrip" || file.ffProbeData.streams[i].codec_name
) { .toLowerCase() === 'subrip'
extraArguments += `-map -0:${i} `; || file.ffProbeData.streams[i].codec_name
} .toLowerCase() === 'timed_id3'
) {
extraArguments += `-map -0:${i} `;
}
} catch (err) {
// Error
} }
}
} }
} }
// Check if 10bit variable is true. // Check if 10bit variable is true.
if (inputs.enable_10bit == "true") { if (inputs.enable_10bit === 'true') {
// If set to true then add 10bit argument // If set to true then add 10bit argument
extraArguments += `-pix_fmt p010le `; extraArguments += '-pix_fmt p010le ';
}
// Check if b frame variable is true.
if (inputs.enable_bframes === 'true') {
// If set to true then add b frames argument
extraArguments += '-bf 5 ';
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Check if stream is a video. // Check if stream is a video.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'video') {
// Check if codec of stream is mjpeg/png, if so then remove this "video" stream. mjpeg/png are usually embedded pictures that can cause havoc with plugins. // Check if codec of stream is mjpeg/png, if so then remove this "video" stream.
if (file.ffProbeData.streams[i].codec_name == "mjpeg" || file.ffProbeData.streams[i].codec_name == "png") { // mjpeg/png are usually embedded pictures that can cause havoc with plugins.
if (file.ffProbeData.streams[i].codec_name === 'mjpeg' || file.ffProbeData.streams[i].codec_name === 'png') {
extraArguments += `-map -v:${videoIdx} `; extraArguments += `-map -v:${videoIdx} `;
} }
// Check if codec of stream is hevc AND check if file.container matches inputs.container. If so nothing for plugin to do. // Check if codec of stream is hevc or vp9 AND check if file.container matches inputs.container.
// If so nothing for plugin to do.
if ( if (
file.ffProbeData.streams[i].codec_name == "hevc" && (
file.container == inputs.container file.ffProbeData.streams[i].codec_name === 'hevc'
|| file.ffProbeData.streams[i].codec_name === 'vp9'
)
&& file.container === inputs.container
) { ) {
response.processFile = false; response.processFile = false;
response.infoLog += `☑File is already hevc & in ${inputs.container}. \n`; response.infoLog += `File is already hevc or vp9 & in ${inputs.container}. \n`;
return response; return response;
} }
// Check if codec of stream is hevc AND check if file.container does NOT match inputs.container. If so remux file. // Check if codec of stream is hevc or vp9
// AND check if file.container does NOT match inputs.container.
// If so remux file.
if ( if (
file.ffProbeData.streams[i].codec_name == "hevc" && (
file.container != "${inputs.container}" file.ffProbeData.streams[i].codec_name === 'hevc'
|| file.ffProbeData.streams[i].codec_name === 'vp9'
)
&& file.container !== inputs.container
) { ) {
response.infoLog += `☒File is hevc but is not in ${inputs.container} container. Remuxing. \n`; response.infoLog += `File is hevc or vp9 but is not in ${inputs.container} container. Remuxing. \n`;
response.preset = `, -map 0 -c copy ${extraArguments}`; response.preset = `, -map 0 -c copy ${extraArguments}`;
response.processFile = true; response.processFile = true;
return response; return response;
} }
// Check if video stream is HDR or 10bit // Check if video stream is HDR or 10bit
if (file.ffProbeData.streams[i].profile == "High 10" || file.ffProbeData.streams[i].bits_per_raw_sample == "10" ) { if (
CPU10 = true file.ffProbeData.streams[i].profile === 'High 10'
|| file.ffProbeData.streams[i].bits_per_raw_sample === '10'
) {
CPU10 = true;
} }
// Increment videoIdx. // Increment videoIdx.
videoIdx++; videoIdx += 1;
} }
} }
// Set bitrateSettings variable using bitrate information calulcated earlier. // Set bitrateSettings variable using bitrate information calulcated earlier.
bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k -maxrate ${maximumBitrate}k -bufsize ${currentBitrate}k`; bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k `
+ `-maxrate ${maximumBitrate}k -bufsize ${currentBitrate}k`;
// Print to infoLog information around file & bitrate settings. // Print to infoLog information around file & bitrate settings.
response.infoLog += `Container for output selected as ${ response.infoLog += `Container for output selected as ${inputs.container}. \n`;
inputs.container response.infoLog += `Current bitrate = ${currentBitrate} \n`;
}. \n Current bitrate = ${~~( response.infoLog += 'Bitrate settings: \n';
file.file_size / response.infoLog += `Target = ${targetBitrate} \n`;
(duration * 0.0075) response.infoLog += `Minimum = ${minimumBitrate} \n`;
)} \n Bitrate settings: \nTarget = ${targetBitrate} \nMinimum = ${minimumBitrate} \nMaximum = ${maximumBitrate} \n`; response.infoLog += `Maximum = ${maximumBitrate} \n`;
// Codec will be checked so it can be transcoded correctly // Codec will be checked so it can be transcoded correctly
if (file.video_codec_name == "h263") { if (file.video_codec_name === 'h263') {
response.preset = `-c:v h263_cuvid`; response.preset = '-c:v h263_cuvid';
} else if (file.video_codec_name == "h264") { } else if (file.video_codec_name === 'h264') {
if (CPU10 == false) { if (CPU10 === false) {
response.preset = `-c:v h264_cuvid`; response.preset = '-c:v h264_cuvid';
} }
} else if (file.video_codec_name == "mjpeg") { } else if (file.video_codec_name === 'mjpeg') {
response.preset = `c:v mjpeg_cuvid`; response.preset = '-c:v mjpeg_cuvid';
} else if (file.video_codec_name == "mpeg1") { } else if (file.video_codec_name === 'mpeg1') {
response.preset = `-c:v mpeg1_cuvid`; response.preset = '-c:v mpeg1_cuvid';
} else if (file.video_codec_name == "mpeg2") { } else if (file.video_codec_name === 'mpeg2') {
response.preset = `-c:v mpeg2_cuvid`; response.preset = '-c:v mpeg2_cuvid';
} else if (file.video_codec_name == "vc1") { } else if (file.video_codec_name === 'vc1') {
response.preset = `-c:v vc1_cuvid`; response.preset = '-c:v vc1_cuvid';
} else if (file.video_codec_name == "vp8") { } else if (file.video_codec_name === 'vp8') {
response.preset = `-c:v vp8_cuvid`; response.preset = '-c:v vp8_cuvid';
} else if (file.video_codec_name == "vp9") {
response.preset = `-c:v vp9_cuvid`;
} }
response.preset += `,-map 0 -c:v hevc_nvenc -rc:v vbr_hq -cq:v 19 ${bitrateSettings} -spatial_aq:v 1 -rc-lookahead:v 32 -c:a copy -c:s copy -max_muxing_queue_size 4096 ${extraArguments}`; response.preset += `,-map 0 -c:v hevc_nvenc -rc:v vbr_hq -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.processFile = true;
response.infoLog += `☒File is not hevc. Transcoding. \n`; response.infoLog += 'File is not hevc or vp9. Transcoding. \n';
return response; return response;
} }
module.exports.details = details; module.exports.details = details;

View file

@ -1,205 +1,242 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MC93_Migz1FFMPEG_CPU", id: 'Tdarr_Plugin_MC93_Migz1FFMPEG_CPU',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "Migz-Transcode Using CPU & FFMPEG", Name: 'Migz-Transcode Using CPU & FFMPEG',
Type: "Video", Type: 'Video',
Operation: "Transcode", Operation: 'Transcode',
Description: `Files not in H265 will be transcoded into H265 using CPU with ffmpeg, settings are dependant on file bitrate, working by the logic that H265 can support the same ammount of data at half the bitrate of H264. \n\n`, Description: `Files not in H265 will be transcoded into H265 using CPU with ffmpeg.
Version: "1.4", Settings are dependant on file bitrate
Link: Working by the logic that H265 can support the same ammount of data at half the bitrate of H264.
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz1FFMPEG_CPU.js", This plugin will skip any files that are in the VP9 codec.`,
Tags: "pre-processing,ffmpeg,video only,configurable,h265", Version: '1.9',
Inputs: [ Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz1FFMPEG_CPU.js',
{ Tags: 'pre-processing,ffmpeg,video only,configurable,h265',
name: "container", Inputs: [{
tooltip: `Specify output container of file, ensure that all stream types you may have are supported by your chosen container. mkv is recommended. name: 'container',
\\nExample:\\n tooltip: `Specify output container of file.
mkv \\n Ensure that all stream types you may have are supported by your chosen container.
\\n mkv is recommended.
\\nExample:\\n
mkv
\\nExample:\\n \\nExample:\\n
mp4`, mp4`,
}, },
{ {
name: "bitrate_cutoff", name: 'bitrate_cutoff',
tooltip: `Specify bitrate cutoff, files with a current bitrate lower then this will not be transcoded. Rate is in kbps. Leave empty to disable. tooltip: `Specify bitrate cutoff, files with a current bitrate lower then this will not be transcoded.
\\nExample:\\n \\n Rate is in kbps.
6000 \\n Leave empty to disable.
\\nExample:\\n
6000
\\nExample:\\n \\nExample:\\n
4000`, 4000`,
}, },
{ {
name: "enable_10bit", name: 'enable_10bit',
tooltip: `Specify if output file should be 10bit. Default is false. tooltip: `Specify if output file should be 10bit. Default is false.
\\nExample:\\n \\nExample:\\n
true true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
{ {
name: "force_conform", name: 'force_conform',
tooltip: `Make the file conform to output containers requirements. tooltip: `Make the file conform to output containers requirements.
\\n Drop hdmv_pgs_subtitle/eia_608/subrip subtitles for MP4. \\n Drop hdmv_pgs_subtitle/eia_608/subrip/timed_id3 for MP4.
\\n Drop data streams and mov_text/eia_608 subtitles for MKV. \\n Drop data streams/mov_text/eia_608/timed_id3 for MKV.
\\n Default is false. \\n Default is false.
\\nExample:\\n \\nExample:\\n
true true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
], ],
}; };
} }
function plugin(file, librarySettings, inputs) { function plugin(file, librarySettings, inputs) {
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: true, FFmpegMode: true,
reQueueAfter: true, reQueueAfter: true,
infoLog: "", infoLog: '',
}; };
let duration = '';
// Check if inputs.container has been configured. If it hasn't then exit plugin. // Check if inputs.container has been configured. If it hasn't then exit plugin.
if (!inputs || inputs.container == "") { if (inputs.container === '') {
response.infoLog += response.infoLog += 'Plugin has not been configured, please configure required options. Skipping this plugin. \n';
"☒Container has not been configured within plugin settings, please configure required options. Skipping this plugin. \n";
response.processFile = false; response.processFile = false;
return response; return response;
} else {
response.container = "." + inputs.container;
} }
response.container = `.${inputs.container}`;
// Check if file is a video. If it isn't then exit plugin. // Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
response.processFile = false; response.processFile = false;
response.infoLog += "☒File is not a video. \n"; response.infoLog += 'File is not a video. \n';
return response; return response;
} }
// Check if duration info is filled, if so times it by 0.0166667 to get time in minutes. If not filled then get duration of stream 0 and do the same. // Check if duration info is filled, if so times it by 0.0166667 to get time in minutes.
if (typeof file.meta.Duration != "undefined") { // If not filled then get duration of stream 0 and do the same.
var duration = file.meta.Duration * 0.0166667; if (typeof file.meta.Duration !== 'undefined') {
duration = file.meta.Duration * 0.0166667;
} else { } else {
var duration = file.ffProbeData.streams[0].duration * 0.0166667; duration = file.ffProbeData.streams[0].duration * 0.0166667;
} }
// Set up required variables. // Set up required variables.
var videoIdx = -1; let videoIdx = -1;
var extraArguments = ""; let extraArguments = '';
var bitrateSettings = ""; 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/ // Work out currentBitrate using "Bitrate = file size / (number of minutes * .0075)"
var currentBitrate = ~~(file.file_size / (duration * 0.0075)); // Used from here https://blog.frame.io/2017/03/06/calculate-video-bitrates/
// Use the same calculation used for currentBitrate but divide it in half to get targetBitrate. Logic of h265 can be half the bitrate as h264 without losing quality. // eslint-disable-next-line no-bitwise
var targetBitrate = ~~(file.file_size / (duration * 0.0075) / 2); const currentBitrate = ~~(file.file_size / (duration * 0.0075));
// Use the same calculation used for currentBitrate but divide it in half to get targetBitrate.
// Logic of h265 can be half the bitrate as h264 without losing quality.
// eslint-disable-next-line no-bitwise
const targetBitrate = ~~(file.file_size / (duration * 0.0075) / 2);
// Allow some leeway under and over the targetBitrate. // Allow some leeway under and over the targetBitrate.
var minimumBitrate = ~~(targetBitrate * 0.7); // eslint-disable-next-line no-bitwise
var maximumBitrate = ~~(targetBitrate * 1.3); const minimumBitrate = ~~(targetBitrate * 0.7);
// eslint-disable-next-line no-bitwise
const maximumBitrate = ~~(targetBitrate * 1.3);
// If targetBitrate comes out as 0 then something has gone wrong and bitrates could not be calculcated. Cancel plugin completely. // If targetBitrate comes out as 0 then something has gone wrong and bitrates could not be calculcated.
if (targetBitrate == "0") { // Cancel plugin completely.
if (targetBitrate === 0) {
response.processFile = false; response.processFile = false;
response.infoLog += response.infoLog += 'Target bitrate could not be calculated. Skipping this plugin. \n';
"☒Target bitrate could not be calculated. Skipping this plugin. \n";
return response; return response;
} }
// Check if inputs.bitrate cutoff has something entered (Entered means user actually wants something to happen, empty would disable this). // Check if inputs.bitrate cutoff has something entered.
if (inputs.bitrate_cutoff != "") { // (Entered means user actually wants something to happen, empty would disable this).
// Checks if currentBitrate is below inputs.bitrate_cutoff, if so then cancel plugin without touching original files. if (inputs.bitrate_cutoff !== '') {
// Checks if currentBitrate is below inputs.bitrate_cutoff
// If so then cancel plugin without touching original files.
if (currentBitrate <= inputs.bitrate_cutoff) { if (currentBitrate <= inputs.bitrate_cutoff) {
response.processFile = false; response.processFile = false;
response.infoLog += `☑Current bitrate is below configured bitrate cutoff of ${inputs.bitrate_cutoff}. Nothing to do, cancelling plugin. \n`; response.infoLog += 'Current bitrate is below set bitrate cutoff '
+ `of ${inputs.bitrate_cutoff}. Nothing to do, cancelling plugin. \n`;
return response; return response;
} }
} }
// Check if force_conform option is checked. If so then check streams and add any extra parameters required to make file conform with output format. // Check if force_conform option is checked.
if (inputs.force_conform == "true") { // If so then check streams and add any extra parameters required to make file conform with output format.
if (inputs.container.toLowerCase() == "mkv") { if (inputs.force_conform === 'true') {
extraArguments += `-map -0:d `; if (inputs.container.toLowerCase() === 'mkv') {
for (var i = 0; i < file.ffProbeData.streams.length; i++) { extraArguments += '-map -0:d ';
if ( for (let i = 0; i < file.ffProbeData.streams.length; i++) {
file.ffProbeData.streams[i].codec_name try {
.toLowerCase() == "mov_text" || if (
file.ffProbeData.streams[i].codec_name file.ffProbeData.streams[i].codec_name
.toLowerCase() == "eia_608" .toLowerCase() === 'mov_text'
) { || file.ffProbeData.streams[i].codec_name
extraArguments += `-map -0:${i} `; .toLowerCase() === 'eia_608'
} || file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'timed_id3'
) {
extraArguments += `-map -0:${i} `;
}
} catch (err) {
// Error
} }
}
} }
if (inputs.container.toLowerCase() == "mp4") { if (inputs.container.toLowerCase() === 'mp4') {
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
if ( try {
file.ffProbeData.streams[i].codec_name if (
.toLowerCase() == "hdmv_pgs_subtitle" || file.ffProbeData.streams[i].codec_name
file.ffProbeData.streams[i].codec_name .toLowerCase() === 'hdmv_pgs_subtitle'
.toLowerCase() == "eia_608" || || file.ffProbeData.streams[i].codec_name
file.ffProbeData.streams[i].codec_name .toLowerCase() === 'eia_608'
.toLowerCase() == "subrip" || file.ffProbeData.streams[i].codec_name
) { .toLowerCase() === 'subrip'
extraArguments += `-map -0:${i} `; || file.ffProbeData.streams[i].codec_name
} .toLowerCase() === 'timed_id3'
) {
extraArguments += `-map -0:${i} `;
}
} catch (err) {
// Error
} }
}
} }
} }
// Check if 10bit variable is true. // Check if 10bit variable is true.
if (inputs.enable_10bit == "true") { if (inputs.enable_10bit === 'true') {
// If set to true then add 10bit argument // If set to true then add 10bit argument
extraArguments += `-pix_fmt p010le `; extraArguments += '-pix_fmt p010le ';
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Check if stream is a video. // Check if stream is a video.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'video') {
// Check if codec of stream is mjpeg/png, if so then remove this "video" stream. mjpeg/png are usually embedded pictures that can cause havoc with plugins. // Check if codec of stream is mjpeg/png.
if (file.ffProbeData.streams[i].codec_name == "mjpeg" || file.ffProbeData.streams[i].codec_name == "png") { // If so then remove this "video" stream.
extraArguments += `-map -v:${videoIdx} `; // mjpeg/png are usually embedded pictures that can cause havoc with plugins.
} if (file.ffProbeData.streams[i].codec_name === 'mjpeg' || file.ffProbeData.streams[i].codec_name === 'png') {
// Check if codec of stream is hevc AND check if file.container matches inputs.container. If so nothing for plugin to do. extraArguments += `-map -v:${videoIdx} `;
}
// Check if codec of stream is hevc or vp9
// AND check if file.container matches inputs.container.
// If so nothing for plugin to do.
if ( if (
file.ffProbeData.streams[i].codec_name == "hevc" && (file.ffProbeData.streams[i].codec_name === 'hevc' || file.ffProbeData.streams[i].codec_name === 'vp9')
file.container == inputs.container && file.container === `${inputs.container}`
) { ) {
response.processFile = false; response.processFile = false;
response.infoLog += `File is already hevc & in ${inputs.container}. \n`; response.infoLog += `File is already hevc or vp9 & in ${inputs.container}. \n`;
return response; return response;
} }
// Check if codec of stream is hevc AND check if file.container does NOT match inputs.container. If so remux file. // Check if codec of stream is hevc or vp9
// AND check if file.container does NOT match inputs.container.
// If so remux file.
if ( if (
file.ffProbeData.streams[i].codec_name == "hevc" && (file.ffProbeData.streams[i].codec_name === 'hevc' || file.ffProbeData.streams[i].codec_name === 'vp9')
file.container != "${inputs.container}" && file.container !== `${inputs.container}`
) { ) {
response.infoLog += `File is hevc but is not in ${inputs.container} container. Remuxing. \n`; response.infoLog += `File is hevc or vp9 but is not in ${inputs.container} container. Remuxing. \n`;
response.preset = `, -map 0 -c copy ${extraArguments}`; response.preset = `, -map 0 -c copy ${extraArguments}`;
response.processFile = true; response.processFile = true;
return response; return response;
} }
// Increment videoIdx. // Increment videoIdx.
videoIdx++; videoIdx += 1;
} }
} }
// Set bitrateSettings variable using bitrate information calulcated earlier. // Set bitrateSettings variable using bitrate information calulcated earlier.
bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k -maxrate ${maximumBitrate}k -bufsize ${currentBitrate}k`; bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k `
+ `-maxrate ${maximumBitrate}k -bufsize ${currentBitrate}k`;
// Print to infoLog information around file & bitrate settings. // Print to infoLog information around file & bitrate settings.
response.infoLog += `Container for output selected as ${ response.infoLog += `Container for output selected as ${inputs.container}. \n`;
inputs.container response.infoLog += `Current bitrate = ${currentBitrate} \n`;
}. \n Current bitrate = ${~~( response.infoLog += 'Bitrate settings: \n';
file.file_size / response.infoLog += `Target = ${targetBitrate} \n`;
(duration * 0.0075) response.infoLog += `Minimum = ${minimumBitrate} \n`;
)} \n Bitrate settings: \nTarget = ${targetBitrate} \nMinimum = ${minimumBitrate} \nMaximum = ${maximumBitrate} \n`; response.infoLog += `Maximum = ${maximumBitrate} \n`;
response.preset += `,-map 0 -c:v libx265 ${bitrateSettings} -c:a copy -c:s copy -max_muxing_queue_size 4096 ${extraArguments}`; response.preset += `,-map 0 -c:v libx265 ${bitrateSettings} `
+ `-c:a copy -c:s copy -max_muxing_queue_size 9999 ${extraArguments}`;
response.processFile = true; response.processFile = true;
response.infoLog += `☒File is not hevc. Transcoding. \n`; response.infoLog += 'File is not hevc or vp9. Transcoding. \n';
return response; return response;
} }
module.exports.details = details; module.exports.details = details;

View file

@ -0,0 +1,132 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() {
return {
id: 'Tdarr_Plugin_MC93_Migz1Remux',
Stage: 'Pre-processing',
Name: 'Migz-Remux container',
Type: 'Video',
Operation: 'Remux',
Description: 'Files will be remuxed into either mkv or mp4. \n\n',
Version: '1.1',
Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz1Remux.js',
Tags: 'pre-processing,ffmpeg,video only,configurable',
Inputs: [{
name: 'container',
tooltip: `Specify output container of file
\\nEnsure that all stream types you may have are supported by your chosen container.
\\nmkv is recommended.
\\nExample:\\n
mkv
\\nExample:\\n
mp4`,
},
{
name: 'force_conform',
tooltip: `Make the file conform to output containers requirements.
\\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`,
},
],
};
}
function plugin(file, librarySettings, inputs) {
const response = {
processFile: false,
preset: '',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '',
};
// Check if inputs.container has been configured. If it hasn't then exit plugin.
if (inputs.container === '') {
response.infoLog
+= '☒Container has not been configured, please configure required options. Skipping this plugin. \n';
response.processFile = false;
return response;
}
response.container = `.${inputs.container}`;
// 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. \n';
return response;
}
// Set up required variables.
let extraArguments = '';
let convert = false;
// Check if force_conform option is checked.
// If so then check streams and add any extra parameters required to make file conform with output format.
if (inputs.force_conform === 'true') {
if (inputs.container.toLowerCase() === 'mkv') {
extraArguments += '-map -0:d ';
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try {
if (
file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'mov_text'
|| file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'eia_608'
|| file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'timed_id3'
) {
extraArguments += `-map -0:${i} `;
}
} catch (err) {
// Error
}
}
}
if (inputs.container.toLowerCase() === 'mp4') {
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try {
if (
file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'hdmv_pgs_subtitle'
|| file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'eia_608'
|| file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'subrip'
|| file.ffProbeData.streams[i].codec_name
.toLowerCase() === 'timed_id3'
) {
extraArguments += `-map -0:${i} `;
}
} catch (err) {
// Error
}
}
}
}
// Check if file.container does NOT match inputs.container. If so remux file.
if (file.container !== inputs.container) {
response.infoLog += `☒File is ${file.container} but requested to be ${inputs.container} container. Remuxing. \n`;
convert = true;
} else if (file.container === inputs.container) {
response.infoLog += `☑File is already in ${inputs.container} container. \n`;
return response;
}
if (convert === true) {
response.preset += `, -map 0 -c copy -max_muxing_queue_size 9999 ${extraArguments}`;
response.processFile = true;
return response;
}
return response;
}
module.exports.details = details;
module.exports.plugin = plugin;

View file

@ -1,123 +1,217 @@
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MC93_Migz2CleanTitle", id: 'Tdarr_Plugin_MC93_Migz2CleanTitle',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "Migz-Clean title metadata", Name: 'Migz-Clean title metadata',
Type: "Video", Type: 'Video',
Operation: "Clean", Operation: 'Clean',
Description: `This plugin removes title metadata from video/audio/subtitles, if it exists. Video checking is mandatory, audio and subtitles are optional.\n\n`, Description: 'This plugin removes title metadata from video/audio/subtitles.\n\n',
Version: "1.2", Version: '1.9',
Link: Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz2CleanTitle.js',
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz2CleanTitle.js", Tags: 'pre-processing,ffmpeg,configurable',
Tags: "pre-processing,ffmpeg,configurable", Inputs: [{
Inputs: [ name: 'clean_audio',
{ tooltip: `Specify if audio titles should be checked & cleaned. Optional.
name: "clean_audio", \\nExample:\\n
tooltip: `Specify if audio titles should be checked & cleaned. Optional. true
\\nExample:\\n
true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
{ {
name: "clean_subtitles", name: 'clean_subtitles',
tooltip: `Specify if subtitle titles should be checked & cleaned. Optional. tooltip: `Specify if subtitle titles should be checked & cleaned. Optional.
\\nExample:\\n \\nExample:\\n
true true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
{
name: 'custom_title_matching',
tooltip: `If you enable audio or subtitle cleaning the plugin only looks for titles with more then 3 full stops.
//nThis is one way to identify junk metadata without removing real metadata that you might want.
//nHere you can specify your own text for it to also search for to match and remove.
//nComma separated. Optional.
\\nExample:\\n
MiNX - Small HD episodes
\\nExample:\\n
MiNX - Small HD episodes,GalaxyTV - small excellence!`,
},
], ],
}; };
} }
function plugin(file, librarySettings, inputs) { function plugin(file, librarySettings, inputs) {
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
container: "." + file.container, container: `.${file.container}`,
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: true, FFmpegMode: true,
reQueueAfter: false, reQueueAfter: false,
infoLog: "", infoLog: '',
}; };
// Set up required variables. // Set up required variables.
var ffmpegCommandInsert = "";
var videoIdx = 0; let ffmpegCommandInsert = '';
var audioIdx = 0; let videoIdx = 0;
var subtitleIdx = 0; let audioIdx = 0;
var convert = false; let subtitleIdx = 0;
let convert = false;
let custom_title_matching = '';
// Check if inputs.custom_title_matching has been configured. If it has then set variable
if (typeof inputs.custom_title_matching !== 'undefined') {
custom_title_matching = inputs.custom_title_matching.toLowerCase().split(',');
}
// Check if file is a video. If it isn't then exit plugin. // Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
console.log("File is not video"); // eslint-disable-next-line no-console
response.infoLog += "☒File is not video \n"; console.log('File is not video');
response.infoLog += '☒File is not video \n';
response.processFile = false; response.processFile = false;
return response; return response;
} }
// Check if overall file metadata title is not empty, if it's not empty set to "". // Check if overall file metadata title is not empty, if it's not empty set to "".
if (typeof file.meta.Title != "undefined") if (
!(
typeof file.meta.Title === 'undefined'
|| file.meta.Title === '""'
|| file.meta.Title === ''
)
) {
try { try {
ffmpegCommandInsert += ` -metadata title="" `; ffmpegCommandInsert += ' -metadata title="" ';
convert = true; convert = true;
} catch (err) {} } catch (err) {
// Error
}
}
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
try { // Check if stream is a video.
// Check if stream is a video. if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'video') {
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { try {
// Check if stream title is not empty, if it's nto empty set to "". // Check if stream title is not empty, if it's not empty set to "".
if (typeof file.ffProbeData.streams[i].tags.title != "undefined") { if (
response.infoLog += `☒Video stream title is not empty, most likely junk metadata. Removing title from stream ${i} \n`; !(
typeof file.ffProbeData.streams[i].tags.title === 'undefined'
|| file.ffProbeData.streams[i].tags.title === '""'
|| file.ffProbeData.streams[i].tags.title === ''
)
) {
response.infoLog += `☒Video stream title is not empty. Removing title from stream ${i} \n`;
ffmpegCommandInsert += ` -metadata:s:v:${videoIdx} title="" `; ffmpegCommandInsert += ` -metadata:s:v:${videoIdx} title="" `;
convert = true; convert = true;
} }
// Increment videoIdx. // Increment videoIdx.
videoIdx++; videoIdx += 1;
} catch (err) {
// Error
} }
}
// Check if title metadata of audio stream has more then 3 full stops. If so then it's likely to be junk metadata so remove. // Check if title metadata of audio stream has more then 3 full stops.
if ( // If so then it's likely to be junk metadata so remove.
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && // Then check if any audio streams match with user input custom_title_matching variable, if so then remove.
inputs.clean_audio.toLowerCase() == "true" if (
) { file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
if (file.ffProbeData.streams[i].tags.title.split(".").length - 1 > 3) { && inputs.clean_audio.toLowerCase() === 'true'
response.infoLog += `☒More then 3 full stops detected in audio title, likely to be junk metadata. Removing title from stream ${i} \n`; ) {
ffmpegCommandInsert += ` -metadata:s:a:${audioIdx} title="" `; try {
convert = true; if (
!(
typeof file.ffProbeData.streams[i].tags.title === 'undefined'
|| file.ffProbeData.streams[i].tags.title === '""'
|| file.ffProbeData.streams[i].tags.title === ''
)
) {
if (file.ffProbeData.streams[i].tags.title.split('.').length - 1 > 3) {
try {
response.infoLog += `☒More then 3 full stops in audio title. Removing title from stream ${i} \n`;
ffmpegCommandInsert += ` -metadata:s:a:${audioIdx} title="" `;
convert = true;
} catch (err) {
// Error
}
}
if (typeof inputs.custom_title_matching !== 'undefined') {
try {
if (custom_title_matching.indexOf(file.ffProbeData.streams[i].tags.title.toLowerCase()) !== -1) {
response.infoLog += `☒Audio matched custom input. Removing title from stream ${i} \n`;
ffmpegCommandInsert += ` -metadata:s:a:${audioIdx} title="" `;
convert = true;
}
} catch (err) {
// Error
}
}
} }
// Increment audioIdx. // Increment audioIdx.
audioIdx++; audioIdx += 1;
} catch (err) {
// Error
} }
}
// Check if title metadata of subtitle stream has more then 3 full stops. If so then it's likely to be junk metadata so remove. // Check if title metadata of subtitle stream has more then 3 full stops.
if ( // If so then it's likely to be junk metadata so remove.
file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" && // Then check if any streams match with user input custom_title_matching variable, if so then remove.
inputs.clean_subtitles.toLowerCase() == "true" if (
) { file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle'
if (file.ffProbeData.streams[i].tags.title.split(".").length - 1 > 3) { && inputs.clean_subtitles.toLowerCase() === 'true'
response.infoLog += `☒More then 3 full stops detected in subtitle title, likely to be junk metadata. Removing title from stream ${i} \n`; ) {
ffmpegCommandInsert += ` -metadata:s:s:${subtitleIdx} title="" `; try {
convert = true; if (
!(
typeof file.ffProbeData.streams[i].tags.title === 'undefined'
|| file.ffProbeData.streams[i].tags.title === '""'
|| file.ffProbeData.streams[i].tags.title === ''
)
) {
if (file.ffProbeData.streams[i].tags.title.split('.').length - 1 > 3) {
try {
response.infoLog += `☒More then 3 full stops in subtitle title. Removing title from stream ${i} \n`;
ffmpegCommandInsert += ` -metadata:s:s:${subtitleIdx} title="" `;
convert = true;
} catch (err) {
// Error
}
}
if (typeof inputs.custom_title_matching !== 'undefined') {
try {
if (custom_title_matching.indexOf(file.ffProbeData.streams[i].tags.title.toLowerCase()) !== -1) {
response.infoLog += `☒Subtitle matched custom input. Removing title from stream ${i} \n`;
ffmpegCommandInsert += ` -metadata:s:s:${subtitleIdx} title="" `;
convert = true;
}
} catch (err) {
// Error
}
}
} }
// Increment subtitleIdx. // Increment subtitleIdx.
subtitleIdx++; subtitleIdx += 1;
} catch (err) {
// Error
} }
} catch (err) {} }
}
// Convert file if convert variable is set to true. // Convert file if convert variable is set to true.
if (convert == true) { if (convert === true) {
response.infoLog += "☒File has title metadata. Removing \n"; response.infoLog += '☒File has title metadata. Removing \n';
response.preset = `,${ffmpegCommandInsert} -c copy -map 0 -max_muxing_queue_size 4096`; response.preset = `,${ffmpegCommandInsert} -c copy -map 0 -max_muxing_queue_size 9999`;
response.reQueueAfter = true; response.reQueueAfter = true;
response.processFile = true; response.processFile = true;
} else { } else {
response.infoLog += "☑File has no title metadata \n"; response.infoLog += '☑File has no title metadata \n';
} }
return response; return response;
} }

View file

@ -1,140 +1,154 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MC93_Migz3CleanAudio", id: 'Tdarr_Plugin_MC93_Migz3CleanAudio',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "Migz-Clean audio streams", Name: 'Migz-Clean audio streams',
Type: "Audio", Type: 'Audio',
Operation: "Clean", Operation: 'Clean',
Description: `This plugin keeps only specified language audio tracks & can tags those that have an unknown language. \n\n`, Description: 'This plugin keeps only specified language tracks & can tags tracks with an unknown language. \n\n',
Version: "2.2", Version: '2.4',
Link: Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz3CleanAudio.js',
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz3CleanAudio.js", Tags: 'pre-processing,ffmpeg,audio only,configurable',
Tags: "pre-processing,ffmpeg,audio only,configurable", Inputs: [{
Inputs: [ name: 'language',
{ tooltip: `Specify language tag/s here for the audio tracks you'd like to keep
name: "language", \\nRecommended to keep "und" as this stands for undertermined
tooltip: `Specify language tag/s here for the audio tracks you'd like to keep, recommended to keep "und" as this stands for undertermined, some files may not have the language specified. Must follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes \\nSome files may not have the language specified.
\\nExample:\\n \\nMust follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
eng \\nExample:\\n
eng
\\nExample:\\n \\nExample:\\n
eng,und eng,und
\\nExample:\\n \\nExample:\\n
eng,und,jap`, eng,und,jap`,
}, },
{ {
name: "commentary", name: 'commentary',
tooltip: `Specify if audio tracks that contain commentary/description should be removed. tooltip: `Specify if audio tracks that contain commentary/description should be removed.
\\nExample:\\n \\nExample:\\n
true true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
{ {
name: "tag_language", name: 'tag_language',
tooltip: `Specify a single language for audio tracks with no language or unknown language to be tagged with, leave empty to disable, you must have "und" in your list of languages to keep for this to function. Must follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes tooltip: `Specify a single language for audio tracks with no language or unknown language to be tagged with.
\\nExample:\\n \\nYou must have "und" in your list of languages to keep for this to function.
eng \\nMust follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
\\nLeave empty to disable.
\\nExample:\\n
eng
\\nExample:\\n \\nExample:\\n
por`, por`,
}, },
{ {
name: "tag_title", name: 'tag_title',
tooltip: `Specify audio tracks with no title to be tagged with the number of channels they contain. Do NOT use this with mp4, as mp4 does not support title tags. tooltip: `Specify audio tracks with no title to be tagged with the number of channels they contain.
\\nExample:\\n \\nDo NOT use this with mp4, as mp4 does not support title tags.
true \\nExample:\\n
true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
], ],
}; };
} }
function plugin(file, librarySettings, inputs) { function plugin(file, librarySettings, inputs) {
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
container: "." + file.container, container: `.${file.container}`,
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: true, FFmpegMode: true,
reQueueAfter: false, reQueueAfter: false,
infoLog: "", infoLog: '',
}; };
// Check if file is a video. If it isn't then exit plugin. // Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
console.log("File is not video"); // eslint-disable-next-line no-console
response.infoLog += "☒File is not video \n"; console.log('File is not video');
response.infoLog += '☒File is not video \n';
response.processFile = false; response.processFile = false;
return response; return response;
} }
// Check if inputs.language has been configured. If it hasn't then exit plugin. // Check if inputs.language has been configured. If it hasn't then exit plugin.
if (inputs.language == "") { if (inputs.language === '') {
response.infoLog += response.infoLog += '☒Language/s options not set, please configure required options. Skipping this plugin. \n';
"☒Language/s keep have not been configured within plugin settings, please configure required options. Skipping this plugin. \n";
response.processFile = false; response.processFile = false;
return response; return response;
} }
// Set up required variables. // Set up required variables.
var language = inputs.language.split(","); const language = inputs.language.split(',');
var ffmpegCommandInsert = ""; let ffmpegCommandInsert = '';
var convert = false; let convert = false;
var audioIdx = 0; let audioIdx = 0;
var audioStreamsRemoved = 0; let audioStreamsRemoved = 0;
var audioStreamCount = file.ffProbeData.streams.filter( const audioStreamCount = file.ffProbeData.streams.filter(
(row) => row.codec_type.toLowerCase() == "audio" (row) => row.codec_type.toLowerCase() === 'audio',
).length; ).length;
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Catch error here incase the language metadata is completely missing. // Catch error here incase the language metadata is completely missing.
try { try {
// Check if stream is audio AND checks if the tracks language code does not match any of the languages entered in inputs.language. // Check if stream is audio
// AND checks if the tracks language code does not match any of the languages entered in inputs.language.
if ( if (
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
language.indexOf( && language.indexOf(
file.ffProbeData.streams[i].tags.language.toLowerCase() file.ffProbeData.streams[i].tags.language.toLowerCase(),
) === -1 ) === -1
) { ) {
audioStreamsRemoved++; audioStreamsRemoved += 1;
ffmpegCommandInsert += `-map -0:a:${audioIdx} `; ffmpegCommandInsert += `-map -0:a:${audioIdx} `;
response.infoLog += `☒Audio stream detected as being an unwanted language, removing. Audio stream 0:a:${audioIdx} - ${file.ffProbeData.streams[ response.infoLog += `☒Audio stream detected as being unwanted, removing. Audio stream 0:a:${audioIdx} \n`;
i
].tags.language.toLowerCase()} \n`;
convert = true; convert = true;
} }
} catch (err) {} } catch (err) {
// Error
}
// Catch error here incase the title metadata is completely missing. // Catch error here incase the title metadata is completely missing.
try { try {
// Check if inputs.commentary is set to true AND if stream is audio AND then checks for stream titles with the following "commentary, description, sdh". Removing any streams that are applicable. // Check if inputs.commentary is set to true
// AND if stream is audio
// AND then checks for stream titles with the following "commentary, description, sdh".
// Removing any streams that are applicable.
if ( if (
inputs.commentary.toLowerCase() == "true" && inputs.commentary.toLowerCase() === 'true'
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
(file.ffProbeData.streams[i].tags.title && (file.ffProbeData.streams[i].tags.title
.toLowerCase() .toLowerCase()
.includes("commentary") || .includes('commentary')
file.ffProbeData.streams[i].tags.title || file.ffProbeData.streams[i].tags.title
.toLowerCase() .toLowerCase()
.includes("description") || .includes('description')
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("sdh")) || file.ffProbeData.streams[i].tags.title.toLowerCase().includes('sdh'))
) { ) {
audioStreamsRemoved++; audioStreamsRemoved += 1;
ffmpegCommandInsert += `-map -0:a:${audioIdx} `; ffmpegCommandInsert += `-map -0:a:${audioIdx} `;
response.infoLog += `☒Audio stream detected as being Commentary or Description, removing. Audio stream 0:a:${audioIdx} - ${file.ffProbeData.streams[i].tags.title}. \n`; response.infoLog += `☒Audio stream detected as being descriptive, removing. Stream 0:a:${audioIdx} \n`;
convert = true; convert = true;
} }
} catch (err) {} } catch (err) {
// Error
}
// Check if inputs.tag_language has something entered (Entered means user actually wants something to happen, empty would disable this) AND checks that stream is audio. // Check if inputs.tag_language has something entered
// (Entered means user actually wants something to happen, empty would disable this)
// AND checks that stream is audio.
if ( if (
inputs.tag_language != "" && inputs.tag_language !== ''
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
) { ) {
// Catch error here incase the metadata is completely missing. // Catch error here incase the metadata is completely missing.
try { try {
@ -142,64 +156,69 @@ function plugin(file, librarySettings, inputs) {
if ( if (
file.ffProbeData.streams[i].tags.language file.ffProbeData.streams[i].tags.language
.toLowerCase() .toLowerCase()
.includes("und") .includes('und')
) { ) {
ffmpegCommandInsert += `-metadata:s:a:${audioIdx} language=${inputs.tag_language} `; ffmpegCommandInsert += `-metadata:s:a:${audioIdx} language=${inputs.tag_language} `;
response.infoLog += `☒Audio stream detected as having unknown language tagged, tagging as ${inputs.tag_language}. \n`; response.infoLog += `☒Audio stream detected as having no language, tagging as ${inputs.tag_language}. \n`;
convert = true; convert = true;
} }
} catch (err) {} } catch (err) {
// Error
// Checks if the tags metadata is completely missing, if so this would cause playback to show language as "undefined". No catch error here otherwise it would never detect the metadata as missing.
if (typeof file.ffProbeData.streams[i].tags == "undefined") {
ffmpegCommandInsert += `-metadata:s:a:${audioIdx} language=${inputs.tag_language} `;
response.infoLog += `☒Audio stream detected as having no language tagged, tagging as ${inputs.tag_language}. \n`;
convert = true;
} }
// Checks if the tags.language metadata is completely missing, if so this would cause playback to show language as "undefined". No catch error here otherwise it would never detect the metadata as missing.
else { // Checks if the tags metadata is completely missing.
if (typeof file.ffProbeData.streams[i].tags.language == "undefined") { // If so this would cause playback to show language as "undefined".
ffmpegCommandInsert += `-metadata:s:a:${audioIdx} language=${inputs.tag_language} `; // No catch error here otherwise it would never detect the metadata as missing.
response.infoLog += `☒Audio stream detected as having no language tagged, tagging as ${inputs.tag_language}. \n`; if (typeof file.ffProbeData.streams[i].tags === 'undefined') {
convert = true; ffmpegCommandInsert += `-metadata:s:a:${audioIdx} language=${inputs.tag_language} `;
} response.infoLog += `☒Audio stream detected as having no language, tagging as ${inputs.tag_language}. \n`;
convert = true;
} else if (typeof file.ffProbeData.streams[i].tags.language === 'undefined') {
// Checks if the tags.language metadata is completely missing.
// If so this would cause playback to show language as "undefined".
// No catch error here otherwise it would never detect the metadata as missing.
ffmpegCommandInsert += `-metadata:s:a:${audioIdx} language=${inputs.tag_language} `;
response.infoLog += `☒Audio stream detected as having no language, tagging as ${inputs.tag_language}. \n`;
convert = true;
} }
} }
try { try {
// Check if title metadata is missing from any streams AND inputs.tag_title set to true AND if stream type is audio. Add title to any applicable streams. // Check if title metadata is missing from any streams
// AND inputs.tag_title set to true AND if stream type is audio. Add title to any applicable streams.
if ( if (
typeof file.ffProbeData.streams[i].tags.title == "undefined" && typeof file.ffProbeData.streams[i].tags.title === 'undefined'
inputs.tag_title.toLowerCase() == "true" && && inputs.tag_title.toLowerCase() === 'true'
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
) { ) {
if (file.ffProbeData.streams[i].channels == "8") { if (file.ffProbeData.streams[i].channels === 8) {
ffmpegCommandInsert += `-metadata:s:a:${audioIdx} title="7.1" `; ffmpegCommandInsert += `-metadata:s:a:${audioIdx} title="7.1" `;
response.infoLog += `☒Audio stream detected as 8 channel audio track with no title, tagging title. Audio stream 0:a:${audioIdx} tagged as "7.1" \n`; response.infoLog += `☒Audio stream detected as 8 channel with no title, tagging. Stream 0:a:${audioIdx} \n`;
convert = true; convert = true;
} }
if (file.ffProbeData.streams[i].channels == "6") { if (file.ffProbeData.streams[i].channels === 6) {
ffmpegCommandInsert += `-metadata:s:a:${audioIdx} title="5.1" `; ffmpegCommandInsert += `-metadata:s:a:${audioIdx} title="5.1" `;
response.infoLog += `☒Audio stream detected as 6 channel audio track with no title, tagging title. Audio stream 0:a:${audioIdx} tagged as "5.1" \n`; response.infoLog += `☒Audio stream detected as 6 channel with no title, tagging. Stream 0:a:${audioIdx} \n`;
convert = true; convert = true;
} }
if (file.ffProbeData.streams[i].channels == "2") { if (file.ffProbeData.streams[i].channels === 2) {
ffmpegCommandInsert += `-metadata:s:a:${audioIdx} title="2.0" `; ffmpegCommandInsert += `-metadata:s:a:${audioIdx} title="2.0" `;
response.infoLog += `☒Audio stream detected as 2 channel audio track with no title, tagging title. Audio stream 0:a:${audioIdx} tagged as "2.0" \n`; response.infoLog += `☒Audio stream detected as 2 channel with no title, tagging. Stream 0:a:${audioIdx} \n`;
convert = true; convert = true;
} }
} }
} catch (err) {} } catch (err) {
// Error
}
// Check if stream type is audio and increment audioIdx if true. // Check if stream type is audio and increment audioIdx if true.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
audioIdx++; audioIdx += 1;
} }
} }
// Failsafe to cancel processing if all streams would be removed following this plugin. We don't want no audio. // Failsafe to cancel processing if all streams would be removed following this plugin. We don't want no audio.
if (audioStreamsRemoved == audioStreamCount) { if (audioStreamsRemoved === audioStreamCount) {
response.infoLog += response.infoLog += '☒Cancelling plugin otherwise all audio tracks would be removed. \n';
"☒Cancelling plugin otherwise all audio tracks would be removed. \n";
response.processFile = false; response.processFile = false;
return response; return response;
} }
@ -207,13 +226,12 @@ function plugin(file, librarySettings, inputs) {
// Convert file if convert variable is set to true. // Convert file if convert variable is set to true.
if (convert === true) { if (convert === true) {
response.processFile = true; response.processFile = true;
response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096`; response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 9999`;
response.container = "." + file.container; response.container = `.${file.container}`;
response.reQueueAfter = true; response.reQueueAfter = true;
} else { } else {
response.processFile = false; response.processFile = false;
response.infoLog += response.infoLog += "☑File doesn't contain audio tracks which are unwanted or that require tagging.\n";
"☑File doesn't contain audio tracks which are unwanted or that require tagging.\n";
} }
return response; return response;
} }

View file

@ -1,123 +1,133 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MC93_Migz4CleanSubs", id: 'Tdarr_Plugin_MC93_Migz4CleanSubs',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "Migz-Clean subtitle streams", Name: 'Migz-Clean subtitle streams',
Type: "subtitles", Type: 'subtitles',
Operation: "Clean", Operation: 'Clean',
Description: `This plugin keeps only specified language subtitle tracks & can tag those that have an unknown language. \n\n`, Description: 'This plugin keeps only specified language tracks & can tag tracks with an unknown language. \n\n',
Version: "2.2", Version: '2.4',
Link: Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz4CleanSubs.js',
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz4CleanSubs.js", Tags: 'pre-processing,ffmpeg,subtitle only,configurable',
Tags: "pre-processing,ffmpeg,subtitle only,configurable", Inputs: [{
Inputs: [ name: 'language',
{ tooltip: `Specify language tag/s here for the subtitle tracks you'd like to keep.
name: "language", \\nMust follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
tooltip: `Specify language tag/s here for the subtitle tracks you'd like to keep. Must follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes \\nExample:\\n
\\nExample:\\n eng
eng
\\nExample:\\n \\nExample:\\n
eng,jap`, eng,jpn`,
}, },
{ {
name: "commentary", name: 'commentary',
tooltip: `Specify if subtitle tracks that contain commentary/description should be removed. tooltip: `Specify if subtitle tracks that contain commentary/description should be removed.
\\nExample:\\n \\nExample:\\n
true true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
{ {
name: "tag_language", name: 'tag_language',
tooltip: `Specify a single language for subtitle tracks with no language or unknown language to be tagged with, leave empty to disable. Must follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes tooltip: `Specify a single language for subtitle tracks with no language or unknown language to be tagged with.
\\nExample:\\n \\nMust follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
eng \\nLeave empty to disable.
\\nExample:\\n
eng
\\nExample:\\n \\nExample:\\n
por`, por`,
}, },
], ],
}; };
} }
function plugin(file, librarySettings, inputs) { function plugin(file, librarySettings, inputs) {
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
container: "." + file.container, container: `.${file.container}`,
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: true, FFmpegMode: true,
reQueueAfter: false, reQueueAfter: false,
infoLog: "", infoLog: '',
}; };
// Check if file is a video. If it isn't then exit plugin. // Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
console.log("File is not video"); // eslint-disable-next-line no-console
response.infoLog += "☒File is not video \n"; console.log('File is not video');
response.infoLog += '☒File is not video \n';
response.processFile = false; response.processFile = false;
return response; return response;
} }
// Check if inputs.language has been configured. If it hasn't then exit plugin. // Check if inputs.language has been configured. If it hasn't then exit plugin.
if (inputs.language == "") { if (inputs.language === '') {
response.infoLog += response.infoLog += '☒Language/s to keep have not been configured, '
"☒Language/s keep have not been configured within plugin settings, please configure required options. Skipping this plugin. \n"; + 'please configure required options. Skipping this plugin. \n';
response.processFile = false; response.processFile = false;
return response; return response;
} }
// Set up required variables. // Set up required variables.
var language = inputs.language.split(","); const language = inputs.language.split(',');
var ffmpegCommandInsert = ""; let ffmpegCommandInsert = '';
var subtitleIdx = 0; let subtitleIdx = 0;
var convert = false; let convert = false;
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Catch error here incase the language metadata is completely missing. // Catch error here incase the language metadata is completely missing.
try { try {
// Check if stream is subtitle AND checks if the tracks language code does not match any of the languages entered in inputs.language. // Check if stream is subtitle
// AND checks if the tracks language code does not match any of the languages entered in inputs.language.
if ( if (
file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle'
language.indexOf( && language.indexOf(
file.ffProbeData.streams[i].tags.language.toLowerCase() file.ffProbeData.streams[i].tags.language.toLowerCase(),
) === -1 ) === -1
) { ) {
ffmpegCommandInsert += `-map -0:s:${subtitleIdx} `; ffmpegCommandInsert += `-map -0:s:${subtitleIdx} `;
response.infoLog += `☒Subtitle stream detected as being an unwanted language, removing. Subtitle stream 0:s:${subtitleIdx} - ${file.ffProbeData.streams[ response.infoLog += `☒Subtitle stream detected as being unwanted, removing. Stream 0:s:${subtitleIdx} \n`;
i
].tags.language.toLowerCase()} \n`;
convert = true; convert = true;
} }
} catch (err) {} } catch (err) {
// Error
}
// Catch error here incase the title metadata is completely missing. // Catch error here incase the title metadata is completely missing.
try { try {
// Check if inputs.commentary is set to true AND if stream is subtitle AND then checks for stream titles with the following "commentary, description, sdh". Removing any streams that are applicable. // Check if inputs.commentary is set to true
// AND if stream is subtitle
// AND then checks for stream titles with the following "commentary or description".
// Removing any streams that are applicable.
if ( if (
inputs.commentary.toLowerCase() == "true" && inputs.commentary.toLowerCase() === 'true'
file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" && && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle'
(file.ffProbeData.streams[i].tags.title && (file.ffProbeData.streams[i].tags.title
.toLowerCase() .toLowerCase()
.includes("commentary") || .includes('commentary')
file.ffProbeData.streams[i].tags.title || file.ffProbeData.streams[i].tags.title
.toLowerCase() .toLowerCase()
.includes("description") || .includes('description'))
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("sdh"))
) { ) {
ffmpegCommandInsert += `-map -0:s:${subtitleIdx} `; ffmpegCommandInsert += `-map -0:s:${subtitleIdx} `;
response.infoLog += `☒Subtitle stream detected as being Commentary or Description, removing. Subtitle stream 0:s:${subtitleIdx} - ${file.ffProbeData.streams[i].tags.title}. \n`; response.infoLog += `☒Subtitle stream detected as being descriptive, removing. Stream 0:s:${subtitleIdx} \n`;
convert = true; convert = true;
} }
} catch (err) {} } catch (err) {
// Error
}
// Check if inputs.tag_language has something entered (Entered means user actually wants something to happen, empty would disable this) AND checks that stream is subtitle. // Check if inputs.tag_language has something entered.
// (Entered means user actually wants something to happen, empty would disable this)
// AND checks that stream is subtitle.
if ( if (
inputs.tag_language != "" && inputs.tag_language !== ''
file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle'
) { ) {
// Catch error here incase the metadata is completely missing. // Catch error here incase the metadata is completely missing.
try { try {
@ -125,46 +135,48 @@ function plugin(file, librarySettings, inputs) {
if ( if (
file.ffProbeData.streams[i].tags.language file.ffProbeData.streams[i].tags.language
.toLowerCase() .toLowerCase()
.includes("und") .includes('und')
) { ) {
ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `; ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `;
response.infoLog += `☒Subtitle stream detected as having unknown language tagged, tagging as ${inputs.tag_language}. \n`; response.infoLog += `☒Subtitle stream detected as having no language, tagging as ${inputs.tag_language}. \n`;
convert = true; convert = true;
} }
} catch (err) {} } catch (err) {
// Error
// Checks if the tags metadata is completely missing, if so this would cause playback to show language as "undefined". No catch error here otherwise it would never detect the metadata as missing.
if (typeof file.ffProbeData.streams[i].tags == "undefined") {
ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `;
response.infoLog += `☒Subtitle stream detected as having no language tagged, tagging as ${inputs.tag_language}. \n`;
convert = true;
} }
// Checks if the tags.language metadata is completely missing, if so this would cause playback to show language as "undefined". No catch error here otherwise it would never detect the metadata as missing.
else { // Checks if the tags metadata is completely missing.
if (typeof file.ffProbeData.streams[i].tags.language == "undefined") { // If so this would cause playback to show language as "undefined".
ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `; // No catch error here otherwise it would never detect the metadata as missing.
response.infoLog += `☒Subtitle stream detected as having no language tagged, tagging as ${inputs.tag_language}. \n`; if (typeof file.ffProbeData.streams[i].tags === 'undefined') {
convert = true; ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `;
} response.infoLog += `☒Subtitle stream detected as having no language, tagging as ${inputs.tag_language}. \n`;
convert = true;
} else if (typeof file.ffProbeData.streams[i].tags.language === 'undefined') {
// Checks if the tags.language metadata is completely missing.
// If so this would cause playback to show language as "undefined".
// No catch error here otherwise it would never detect the metadata as missing
ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `;
response.infoLog += `☒Subtitle stream detected as having no language, tagging as ${inputs.tag_language}. \n`;
convert = true;
} }
} }
// Check if stream type is subtitle and increment subtitleIdx if true. // Check if stream type is subtitle and increment subtitleIdx if true.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') {
subtitleIdx++; subtitleIdx += 1;
} }
} }
// Convert file if convert variable is set to true. // Convert file if convert variable is set to true.
if (convert === true) { if (convert === true) {
response.processFile = true; response.processFile = true;
response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096`; response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 9999`;
response.container = "." + file.container; response.container = `.${file.container}`;
response.reQueueAfter = true; response.reQueueAfter = true;
} else { } else {
response.processFile = false; response.processFile = false;
response.infoLog += response.infoLog += "☑File doesn't contain subtitle tracks which are unwanted or that require tagging.\n";
"☑File doesn't contain subtitle tracks which are unwanted or that require tagging.\n";
} }
return response; return response;
} }

View file

@ -1,148 +1,155 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MC93_Migz5ConvertAudio", id: 'Tdarr_Plugin_MC93_Migz5ConvertAudio',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "Migz-Convert audio streams", Name: 'Migz-Convert audio streams',
Type: "Audio", Type: 'Audio',
Operation: "Transcode", Operation: 'Transcode',
Description: `This plugin can convert any 2.0 audio track/s to AAC and can create downmixed audio tracks. \n\n`, Description: 'This plugin can convert any 2.0 audio track/s to AAC and can create downmixed audio tracks. \n\n',
Version: "2.1", Version: '2.3',
Link: "", Link: '',
Tags: "pre-processing,ffmpeg,audio only,configurable", Tags: 'pre-processing,ffmpeg,audio only,configurable',
Inputs: [ Inputs: [{
{ name: 'aac_stereo',
name: "aac_stereo", tooltip: `Specify if any 2.0 audio tracks should be converted to aac for maximum compatability with devices.
tooltip: `Specify if any 2.0 audio tracks should be converted to aac for maximum compatability with devices. Optional. \\nOptional.
\\nExample:\\n \\nExample:\\n
true true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
{ {
name: "downmix", name: 'downmix',
tooltip: `Specify if downmixing should be used to create extra audio tracks. I.e if you have an 8ch but no 2ch or 6ch, create the missing audio tracks from the 8 ch. Likewise if you only have 6ch, create the missing 2ch from it. Optional. tooltip: `Specify if downmixing should be used to create extra audio tracks.
\\nExample:\\n \\nI.e if you have an 8ch but no 2ch or 6ch, create the missing audio tracks from the 8 ch.
true \\nLikewise if you only have 6ch, create the missing 2ch from it. Optional.
\\nExample:\\n
true
\\nExample:\\n \\nExample:\\n
false`, false`,
}, },
], ],
}; };
} }
function plugin(file, librarySettings, inputs) { function plugin(file, librarySettings, inputs) {
var response = { const response = {
processFile: false, processFile: false,
container: "." + file.container, container: `.${file.container}`,
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: true, FFmpegMode: true,
reQueueAfter: true, reQueueAfter: true,
infoLog: "", infoLog: '',
}; };
// Check if both inputs.aac_stereo AND inputs.downmix have been left empty. If they have then exit plugin. // Check if both inputs.aac_stereo AND inputs.downmix have been left empty. If they have then exit plugin.
if (inputs && inputs.aac_stereo == "" && inputs.downmix == "") { if (inputs && inputs.aac_stereo === '' && inputs.downmix === '') {
response.infoLog += response.infoLog += '☒Plugin has not been configured, please configure required options. Skipping this plugin. \n';
"☒Neither aac_stereo or downmix options have been configured within plugin settings, please configure required options. Skipping this plugin. \n";
response.processFile = false; response.processFile = false;
return response; return response;
} }
// Check if file is a video. If it isn't then exit plugin. // Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
console.log("File is not video"); // eslint-disable-next-line no-console
response.infoLog += "☒File is not video. \n"; console.log('File is not video');
response.infoLog += '☒File is not video. \n';
response.processFile = false; response.processFile = false;
return response; return response;
} }
// Set up required variables. // Set up required variables.
var ffmpegCommandInsert = ""; let ffmpegCommandInsert = '';
var audioIdx = 0; let audioIdx = 0;
var has2Channel = false; let has2Channel = false;
var has6Channel = false; let has6Channel = false;
var has8Channel = false; let has8Channel = false;
var convert = false; let convert = false;
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try { try {
// Go through all audio streams and check if 2,6 & 8 channel tracks exist or not. // Go through all audio streams and check if 2,6 & 8 channel tracks exist or not.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
if (file.ffProbeData.streams[i].channels == "2") { if (file.ffProbeData.streams[i].channels === 2) {
has2Channel = true; has2Channel = true;
} }
if (file.ffProbeData.streams[i].channels == "6") { if (file.ffProbeData.streams[i].channels === 6) {
has6Channel = true; has6Channel = true;
} }
if (file.ffProbeData.streams[i].channels == "8") { if (file.ffProbeData.streams[i].channels === 8) {
has8Channel = true; has8Channel = true;
} }
} }
} catch (err) {} } catch (err) {
// Error
}
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Check if stream is audio. // Check if stream is audio.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
// Catch error here incase user left inputs.downmix empty. // Catch error here incase user left inputs.downmix empty.
try { try {
// Check if inputs.downmix is set to true. // Check if inputs.downmix is set to true.
if (inputs.downmix.toLowerCase() == "true") { if (inputs.downmix.toLowerCase() === 'true') {
// Check if file has 8 channel audio but no 6 channel, if so then create extra downmix from the 8 channel. // Check if file has 8 channel audio but no 6 channel, if so then create extra downmix from the 8 channel.
if ( if (
has8Channel == true && has8Channel === true
has6Channel == false && && has6Channel === false
file.ffProbeData.streams[i].channels == "8" && 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 += response.infoLog += '☒Audio track is 8 channel, no 6 channel exists. Creating 6 channel from 8 channel. \n';
"☒Audio track is 8 channel, no 6 channel exists. Creating 6 channel from 8 channel. \n";
convert = true; convert = true;
} }
// Check if file has 6 channel audio but no 2 channel, if so then create extra downmix from the 6 channel. // Check if file has 6 channel audio but no 2 channel, if so then create extra downmix from the 6 channel.
if ( if (
has6Channel == true && has6Channel === true
has2Channel == false && && has2Channel === false
file.ffProbeData.streams[i].channels == "6" && 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 += response.infoLog += '☒Audio track is 6 channel, no 2 channel exists. Creating 2 channel from 6 channel. \n';
"☒Audio track is 6 channel, no 2 channel exists. Creating 2 channel from 6 channel. \n";
convert = true; convert = true;
} }
} }
} catch (err) {} } catch (err) {
// Error
}
// Catch error here incase user left inputs.downmix empty. // Catch error here incase user left inputs.downmix empty.
try { try {
// Check if inputs.aac_stereo is set to true. // Check if inputs.aac_stereo is set to true.
if (inputs.aac_stereo.toLowerCase() == "true") { if (inputs.aac_stereo.toLowerCase() === 'true') {
// Check if codec_name for stream is NOT aac AND check if channel ammount is 2. // Check if codec_name for stream is NOT aac AND check if channel ammount is 2.
if ( if (
file.ffProbeData.streams[i].codec_name != "aac" && file.ffProbeData.streams[i].codec_name !== 'aac'
file.ffProbeData.streams[i].channels == "2" && file.ffProbeData.streams[i].channels === 2
) { ) {
ffmpegCommandInsert += `-c:a:${audioIdx} aac `; ffmpegCommandInsert += `-c:a:${audioIdx} aac `;
response.infoLog += response.infoLog += '☒Audio track is 2 channel but is not AAC. Converting. \n';
"☒Audio track is 2 channel but is not AAC. Converting. \n";
convert = true; convert = true;
} }
} }
} catch (err) {} } catch (err) {
audioIdx++; // Error
}
audioIdx += 1;
} }
} }
// Convert file if convert variable is set to true. // Convert file if convert variable is set to true.
if (convert == true) { if (convert === true) {
response.processFile = true; response.processFile = true;
response.preset = `, -map 0 -c:v copy -c:a copy ${ffmpegCommandInsert} -strict -2 -c:s copy -max_muxing_queue_size 4096 `; response.preset = `, -map 0 -c:v copy -c:a copy ${ffmpegCommandInsert} `
+ '-strict -2 -c:s copy -max_muxing_queue_size 9999 ';
} else { } else {
response.infoLog += "☑File contains all required audio formats. \n"; response.infoLog += '☑File contains all required audio formats. \n';
response.processFile = false; response.processFile = false;
} }
return response; return response;

View file

@ -1,184 +1,201 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MC93_Migz6OrderStreams", id: 'Tdarr_Plugin_MC93_Migz6OrderStreams',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "Migz-Order Streams", Name: 'Migz-Order Streams',
Type: "Streams", Type: 'Streams',
Operation: "Order", Operation: 'Order',
Description: `Orders streams into Video first, then Audio (2ch, 6ch, 8ch) and finally Subtitles. \n\n`, Description: 'Orders streams into Video first, then Audio (2ch, 6ch, 8ch) and finally Subtitles. \n\n',
Version: "1.1", Version: '1.3',
Link: Link:
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz6OrderStreams.js", 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz6OrderStreams.js',
Tags: "pre-processing,ffmpeg,", Tags: 'pre-processing,ffmpeg,',
}; };
} }
function plugin(file) { function plugin(file) {
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
container: "." + file.container, container: `.${file.container}`,
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: true, FFmpegMode: true,
infoLog: "", infoLog: '',
}; };
// Set up required variables. // Set up required variables.
var ffmpegCommandInsert = ""; let ffmpegCommandInsert = '';
var videoIdx = 0; let audioIdx = 0;
var audioIdx = 0; let audio6Idx = 0;
var audio2Idx = 0; let audio8Idx = 0;
var audio6Idx = 0; let subtitleIdx = 0;
var audio8Idx = 0; let convert = false;
var subtitleIdx = 0;
var convert = false;
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try { try {
// Check if stream is video. // Check if stream is video.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'video') {
// Check if audioIdx or subtitleIdx do NOT equal 0, if they do then it means a audio or subtitle track has already appeared before the video track so file needs to be organized. // Check if audioIdx or subtitleIdx do NOT equal 0
if (audioIdx != "0" || subtitleIdx != "0") { // If so then it means a audio or subtitle track has already appeared before the video track
// So file needs to be organized.
if (audioIdx !== 0 || subtitleIdx !== 0) {
convert = true; convert = true;
response.infoLog += "☒ Video not first. \n"; response.infoLog += '☒ Video not first. \n';
} }
// Increment videoIdx.
videoIdx++;
} }
// Check if stream is audio. // Check if stream is audio.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
// Check if subtitleIdx does NOT equal 0, if it does then it means a subtitle track has already appeared before an audio track so file needs to be organized. // Check if subtitleIdx does NOT equal 0.
if (subtitleIdx != "0") { // If so then it means a subtitle track has already appeared before an audio track
// So file needs to be organized.
if (subtitleIdx !== 0) {
convert = true; convert = true;
response.infoLog += "☒ Audio not second. \n"; response.infoLog += '☒ Audio not second. \n';
} }
// Increment audioIdx. // Increment audioIdx.
audioIdx++; audioIdx += 1;
// Check if audio track is 2 channel. // Check if audio track is 2 channel.
if (file.ffProbeData.streams[i].channels == "2") { if (file.ffProbeData.streams[i].channels === '2') {
// Check if audio6Idx or audio8Idx do NOT equal 0, if they do then it means a 6 channel or 8 channel audio track has already appeared before the 2 channel audio track so file needs to be organized. // Check if audio6Idx or audio8Idx do NOT equal 0.
if (audio6Idx != "0" || audio8Idx != "0") { // If so then it means a 6 or 8 channel audio track has already appeared before the 2 channel audio track
// So file needs to be organized.
if (audio6Idx !== 0 || audio8Idx !== 0) {
convert = true; convert = true;
response.infoLog += "☒ Audio 2ch not first. \n"; response.infoLog += '☒ Audio 2ch not first. \n';
} }
// Increment audio2Idx.
audio2Idx++;
} }
// Check if audio track is 6 channel. // Check if audio track is 6 channel.
if (file.ffProbeData.streams[i].channels == "6") { if (file.ffProbeData.streams[i].channels === 6) {
// Check if audio8Idx does NOT equal 0, if it does then it means a 8 channel audio track has already appeared before the 6 channel audio track so file needs to be organized. // Check if audio8Idx does NOT equal 0.
if (audio8Idx != "0") { // If so then it means a 8 channel audio track has already appeared before the 6 channel audio track
// So file needs to be organized.
if (audio8Idx !== 0) {
convert = true; convert = true;
response.infoLog += "☒ Audio 6ch not second. \n"; response.infoLog += '☒ Audio 6ch not second. \n';
} }
// Increment audio6Idx. // Increment audio6Idx.
audio6Idx++; audio6Idx += 1;
} }
// Check if audio track is 8 channel. // Check if audio track is 8 channel.
if (file.ffProbeData.streams[i].channels == "8") { if (file.ffProbeData.streams[i].channels === 8) {
// Increment audio8Idx. // Increment audio8Idx.
audio8Idx++; audio8Idx += 1;
} }
} }
// Check if stream is subtitle. // Check if stream is subtitle.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') {
// Increment subtitleIdx // Increment subtitleIdx
subtitleIdx++; subtitleIdx += 1;
} }
} catch (err) {} } catch (err) {
// Error
}
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try { try {
// Check if stream is video AND is not a mjpeg. // Check if stream is video AND is not a mjpeg.
if ( if (
file.ffProbeData.streams[i].codec_type.toLowerCase() == "video" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'video'
file.ffProbeData.streams[i].codec_name.toLowerCase() != "mjpeg" && file.ffProbeData.streams[i].codec_name.toLowerCase() !== 'mjpeg'
) { ) {
ffmpegCommandInsert += `-map 0:${i} `; ffmpegCommandInsert += `-map 0:${i} `;
} }
} catch (err) {} } catch (err) {
// Error
}
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try { try {
// Check if stream is audio AND 2 channel. // Check if stream is audio AND 2 channel.
if ( if (
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
file.ffProbeData.streams[i].channels == "2" && file.ffProbeData.streams[i].channels === 2
) { ) {
ffmpegCommandInsert += `-map 0:${i} `; ffmpegCommandInsert += `-map 0:${i} `;
} }
} catch (err) {} } catch (err) {
// Error
}
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try { try {
// Check if stream is audio AND 6 channel. // Check if stream is audio AND 6 channel.
if ( if (
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
file.ffProbeData.streams[i].channels == "6" && file.ffProbeData.streams[i].channels === 6
) { ) {
ffmpegCommandInsert += `-map 0:${i} `; ffmpegCommandInsert += `-map 0:${i} `;
} }
} catch (err) {} } catch (err) {
// Error
}
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try { try {
// Check if stream is audio AND 8 channel. // Check if stream is audio AND 8 channel.
if ( if (
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
file.ffProbeData.streams[i].channels == "8" && file.ffProbeData.streams[i].channels === 8
) { ) {
ffmpegCommandInsert += `-map 0:${i} `; ffmpegCommandInsert += `-map 0:${i} `;
} }
} catch (err) {} } catch (err) {
// Error
}
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try { try {
// Check if stream is audio AND not 2, 6 or 8 channel. // Check if stream is audio AND not 2, 6 or 8 channel.
if ( if (
file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio'
file.ffProbeData.streams[i].channels != "2" && && file.ffProbeData.streams[i].channels !== 2
file.ffProbeData.streams[i].channels != "6" && && file.ffProbeData.streams[i].channels !== 6
file.ffProbeData.streams[i].channels != "8" && file.ffProbeData.streams[i].channels !== 8
) { ) {
ffmpegCommandInsert += `-map 0:${i} `; ffmpegCommandInsert += `-map 0:${i} `;
} }
} catch (err) {} } catch (err) {
// Error
}
} }
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
try { try {
// Check if stream is subtitle. // Check if stream is subtitle.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') {
ffmpegCommandInsert += `-map 0:${i} `; ffmpegCommandInsert += `-map 0:${i} `;
} }
} catch (err) {} } catch (err) {
// Error
}
} }
// Convert file if convert variable is set to true. // Convert file if convert variable is set to true.
if (convert == true) { if (convert === true) {
response.processFile = true; response.processFile = true;
response.preset = `,${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096`; response.preset = `,${ffmpegCommandInsert} -c copy -max_muxing_queue_size 9999`;
response.reQueueAfter = true; response.reQueueAfter = true;
response.infoLog += response.infoLog
"☒ Streams are out of order, reorganizing streams. Video, Audio, Subtitles. \n"; += '☒ Streams are out of order, reorganizing streams. Video, Audio, Subtitles. \n';
} else { } else {
response.infoLog += "☑ Streams are in expected order. \n "; response.infoLog += '☑ Streams are in expected order. \n ';
response.processFile = false; response.processFile = false;
} }
return response; return response;

View file

@ -1,67 +1,68 @@
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MC93_MigzImageRemoval", id: 'Tdarr_Plugin_MC93_MigzImageRemoval',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "Migz-Remove image formats from file", Name: 'Migz-Remove image formats from file',
Type: "Video", Type: 'Video',
Operation: "Clean", Operation: 'Clean',
Description: `Identify any unwanted image formats in the file and remove those streams. MJPEG & PNG \n\n`, Description: 'Identify any unwanted image formats in the file and remove those streams. MJPEG & PNG \n\n',
Version: "1.1", Version: '1.3',
Link: Link:
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_MigzImageRemoval.js", 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_MigzImageRemoval.js',
Tags: "pre-processing,ffmpeg,video only", Tags: 'pre-processing,ffmpeg,video only',
}; };
} }
function plugin(file, librarySettings, inputs) { function plugin(file) {
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
handBrakeMode: false, handBrakeMode: false,
container: "." + file.container, container: `.${file.container}`,
FFmpegMode: true, FFmpegMode: true,
reQueueAfter: true, reQueueAfter: true,
infoLog: "", infoLog: '',
}; };
// Check if file is a video. If it isn't then exit plugin. // Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
response.processFile = false; response.processFile = false;
response.infoLog += "☒File is not a video. \n"; response.infoLog += '☒File is not a video. \n';
return response; return response;
} }
// Set up required variables. // Set up required variables.
var videoIdx = 0; let videoIdx = 0;
var extraArguments = ""; let extraArguments = '';
var convert = false; let convert = false;
// Go through each stream in the file. // Go through each stream in the file.
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Check if stream is video. // Check if stream is video.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "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 or png. Remove if so.
if ( if (
file.ffProbeData.streams[i].codec_name == "mjpeg" || file.ffProbeData.streams[i].codec_name === 'mjpeg'
file.ffProbeData.streams[i].codec_name == "png" || file.ffProbeData.streams[i].codec_name === 'png'
) { ) {
convert = true; convert = true;
extraArguments += `-map -v:${videoIdx} `; extraArguments += `-map -v:${videoIdx} `;
} }
// Increment videoIdx. // Increment videoIdx.
videoIdx++; videoIdx += 1;
} }
} }
// Convert file if convert variable is set to true. // Convert file if convert variable is set to true.
if (convert === true) { if (convert === true) {
response.preset += `,-map 0 -c copy -max_muxing_queue_size 4096 ${extraArguments}`; response.preset += `,-map 0 -c copy -max_muxing_queue_size 9999 ${extraArguments}`;
response.infoLog += `☒File has image format stream, removing. \n`; response.infoLog += '☒File has image format stream, removing. \n';
response.processFile = true; response.processFile = true;
} else { } else {
response.processFile = false; response.processFile = false;
response.infoLog += response.infoLog
"☑File doesn't contain any unwanted image format streams.\n"; += "☑File doesn't contain any unwanted image format streams.\n";
} }
return response; return response;
} }

View file

@ -1,94 +1,102 @@
module.exports.dependencies = [
'request',
];
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_MC93_MigzPlex_Autoscan", id: 'Tdarr_Plugin_MC93_MigzPlex_Autoscan',
Stage: "Post-processing", Stage: 'Post-processing',
Name: "Send request for file to be scanned by plex_autoscan.", Name: 'Send request for file to be scanned by plex_autoscan.',
Type: "Video", Type: 'Video',
Operation: "", Operation: '',
Description: `Send request for file to be scanned by plex_autoscan. https://github.com/l3uddz/plex_autoscan \n\n`, Description: 'Send request for file to be scanned by plex_autoscan. https://github.com/l3uddz/plex_autoscan \n\n',
Version: "1.1", Version: '1.2',
Link: Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_MigzPlex_Autoscan.js',
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_MigzPlex_Autoscan.js", Tags: '3rd party,post-processing,configurable',
Tags: "3rd party,post-processing,configurable",
Inputs: [ Inputs: [{
{ name: 'autoscan_address',
name: "autoscan_address", tooltip: `
tooltip: ` Enter the IP address/URL for autoscan. Must include http(s)://
Enter the IP address/URL for autoscan. Must include http(s)://
\\nExample:\\n
http://192.168.0.10
\\nExample:\\n
https://subdomain.domain.tld`,
},
{
name: "autoscan_port",
tooltip: `
Enter the port Autoscan is using, default is 3468
\\nExample:\\n \\nExample:\\n
3468`, http://192.168.0.10
},
{ \\nExample:\\n
name: "autoscan_passkey", https://subdomain.domain.tld`,
tooltip: ` },
{
Enter the autoscan passkey. name: 'autoscan_port',
tooltip: `
\\nExample:\\n Enter the port Autoscan is using, default is 3468
9c4b81fe234e4d6eb9011cefe514d915`,
}, \\nExample:\\n
3468`,
},
{
name: 'autoscan_passkey',
tooltip: `
Enter the autoscan passkey.
\\nExample:\\n
9c4b81fe234e4d6eb9011cefe514d915`,
},
], ],
}; };
}; };
module.exports.plugin = function plugin(file, librarySettings, inputs) { module.exports.plugin = function plugin(file, librarySettings, inputs) {
// eslint-disable-next-line global-require,import/no-unresolved
const request = require('request');
// Set up required variables.
const ADDRESS = inputs.autoscan_address;
const PORT = inputs.autoscan_port;
const PASSKEY = inputs.autoscan_passkey;
let filepath = '';
const response = {};
filepath = `${file.file}`;
// Check if all inputs have been configured. If they haven't then exit plugin. // Check if all inputs have been configured. If they haven't then exit plugin.
if ( if (
inputs && inputs
inputs.autoscan_address == "" && && inputs.autoscan_address === ''
inputs.autoscan_port == "" && && inputs.autoscan_port === ''
inputs.autoscan_passkey == "" && inputs.autoscan_passkey === ''
) { ) {
response.infoLog += response.infoLog += '☒Plugin options have not been configured, please configure options. Skipping this plugin. \n';
"☒Autoscan options have not been configured, please configure all options. Skipping this plugin. \n";
response.processFile = false; response.processFile = false;
return response; return response;
} }
// Take variable inputs and turn them into read only variable
const request = require("request");
const ADDRESS = inputs.autoscan_address;
const PORT = inputs.autoscan_port;
const PASSKEY = inputs.autoscan_passkey;
// Set up required variables.
var response = "";
filepath = `${file.file}`;
// Set content of request/post. // Set content of request/post.
request.post( request.post({
{ headers: {
headers: { 'content-type': 'application/json',
"content-type": "application/json",
},
url: `${ADDRESS}:${PORT}/${PASSKEY}`,
form: {
eventType: "Manual",
filepath: `${filepath}`,
},
}, },
(error, res, body) => { url: `${ADDRESS}:${PORT}/${PASSKEY}`,
if (error) { form: {
console.error(error); eventType: 'Manual',
} filepath: `${filepath}`,
console.log(`statusCode: ${res.statusCode}`); },
console.log(body); },
(error, res, body) => {
if (error) {
// eslint-disable-next-line no-console
console.error(error);
} }
); // eslint-disable-next-line no-console
console.log(`statusCode: ${res.statusCode}`);
// eslint-disable-next-line no-console
console.log(body);
});
console.log("request next"); // eslint-disable-next-line no-console
console.log('request next');
// eslint-disable-next-line no-console
console.log(request.post); console.log(request.post);
return undefined;
}; };

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_MP01_MichPasCleanSubsAndAudioCodecs", id: "Tdarr_Plugin_MP01_MichPasCleanSubsAndAudioCodecs",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
const vaapiPrefix = ` -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi `; const vaapiPrefix = ` -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 -hwaccel_output_format vaapi `;
module.exports.details = function details() { module.exports.details = function details() {

View file

@ -0,0 +1,213 @@
/* 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');
var path = require('path');
if (fs.existsSync(path.join(process.cwd(), '/npm'))) {
var rootModules = path.join(process.cwd(), '/npm/node_modules/')
} else {
var rootModules = ''
}
const importFresh = require(rootModules + 'import-fresh');
const library = importFresh('../methods/library.js')
const ffprobePath = require(rootModules + 'ffprobe-static').path;
module.exports.details = function details() {
return {
id: "Tdarr_Plugin_NIfPZuCLU_2_Pass_Loudnorm_Audio_Normalisation",
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",
Link: "",
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",
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",
tooltip: `Desired lra value. \n Defaults to 7.0
`,
},
{
name: "tp",
tooltip: `Desired \"tp\" value. \n Defaults to -2.0
`,
},
{
name: "offset",
tooltip: `Desired "offset" value. \n Defaults to 0.0
`,
},
],
}
}
module.exports.plugin = function plugin(file, librarySettings, inputs) {
//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;
};

View file

@ -0,0 +1,54 @@
/* eslint-disable */
var fs = require('fs');
var path = require('path');
if (fs.existsSync(path.join(process.cwd(), '/npm'))) {
var rootModules = path.join(process.cwd(), '/npm/node_modules/')
} else {
var rootModules = ''
}
const importFresh = require(rootModules + 'import-fresh');
const library = importFresh('../methods/library.js')
module.exports.details = function details() {
return {
id: "Tdarr_Plugin_O8O0dCTlb_Set_File_Permissions_For_UnRaid",
Name: "Set file permissions for UnRaid",
Type: "Video",
Operation: "Transcode",
Description: "Sets file permissions using chown nobody:users to prevent lock from root. Use at end of stack. ",
Version: "",
Link: "",
Tags: "post-processing"
}
}
module.exports.plugin = function plugin(file) {
//Must return this object at some point
var response = {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: true,
infoLog: '',
}
response.infoLog += ""
if ((true) || file.forceProcessing === true) {
require("child_process").execSync(`chown nobody:users "${file._id}"`)
response.preset = ''
response.container = '.mkv'
response.handBrakeMode = false
response.FFmpegMode = true
response.reQueueAfter = true;
response.processFile = false
response.infoLog += "File permissions set \n"
return response
}
}

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta", id: "Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30", id: "Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30", id: "Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs", id: "Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs",
@ -5,7 +6,7 @@ module.exports.details = function details() {
Name: "Transcode Specific Audio Stream Codecs", Name: "Transcode Specific Audio Stream Codecs",
Type: "", Type: "",
Operation: "Transcode", Operation: "Transcode",
Description: `[TESTING][Contains built-in filter] Transcode audio streams with specific codecs into another codec. \n\n`, Description: `[Contains built-in filter] Transcode audio streams with specific codecs into another codec. \n\n`,
Version: "1.00", Version: "1.00",
Link: "", Link: "",
Tags: "pre-processing,audio only,ffmpeg,configurable", Tags: "pre-processing,audio only,ffmpeg,configurable",
@ -34,6 +35,16 @@ module.exports.details = function details() {
\\nExample:\\n \\nExample:\\n
eac3 eac3
`,
},
{
name: "bitrate",
tooltip: `Specify the transcoded audio bitrate (optional):
\\n 384k
\\n 640k
\\nExample:\\n
640k
`, `,
}, },
], ],
@ -53,7 +64,7 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
infoLog: "", infoLog: "",
}; };
if (inputs.codecs_to_transcode === undefined || inputs.codec === undefined) { if (inputs.codecs_to_transcode === undefined || inputs.codec === undefined ) {
response.processFile = false; response.processFile = false;
response.infoLog += "☒ Inputs not entered! \n"; response.infoLog += "☒ Inputs not entered! \n";
return response; return response;
@ -81,6 +92,9 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
) )
) { ) {
ffmpegCommand += ` -map 0:${i} -c:${i} ${encoder} `; ffmpegCommand += ` -map 0:${i} -c:${i} ${encoder} `;
if (inputs.bitrate != undefined) {
ffmpegCommand += `-b:a ${inputs.bitrate} `;
}
hasStreamsToTranscode = true; hasStreamsToTranscode = true;
} else if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { } else if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") {
ffmpegCommand += ` -map 0:${i}`; ffmpegCommand += ` -map 0:${i}`;
@ -104,4 +118,4 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
response.infoLog += `☒ File has streams which aren't in desired codec! \n`; response.infoLog += `☒ File has streams which aren't in desired codec! \n`;
return response; return response;
} }
}; };

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta", id: "Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3", id: "Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_c0r1_SetDefaultAudioStream", id: "Tdarr_Plugin_c0r1_SetDefaultAudioStream",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV", id: "Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix", id: "Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4", id: "Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_drdd_standardise_all_in_one", id: "Tdarr_Plugin_drdd_standardise_all_in_one",
@ -230,8 +231,8 @@ function buildAudioConfiguration(_inputs, file, logger) {
function buildSubtitleConfiguration(inputs, file, logger) { function buildSubtitleConfiguration(inputs, file, logger) {
var configuration = new Configurator(["-c:s copy"]); var configuration = new Configurator(["-c:s copy"]);
if (!inputs.wanted_subtitle_languages) return configuration;
var languages = inputs.wanted_subtitle_languages.split(","); var languages = inputs.wanted_subtitle_languages.split(",");
if (languages.length === 0) return configuration;
loopOverStreamsOfType(file, "subtitle", function (stream, id) { loopOverStreamsOfType(file, "subtitle", function (stream, id) {
if (stream.codec_name === "eia_608") { if (stream.codec_name === "eia_608") {
@ -346,6 +347,8 @@ function buildVideoConfiguration(inputs, file, logger) {
configuration.AddInputSetting("-c:v mpeg1_cuvid"); configuration.AddInputSetting("-c:v mpeg1_cuvid");
} else if (file.video_codec_name == "mpeg2") { } else if (file.video_codec_name == "mpeg2") {
configuration.AddInputSetting("-c:v mpeg2_cuvid"); configuration.AddInputSetting("-c:v mpeg2_cuvid");
} else if (file.video_codec_name == "mpeg4") {
configuration.AddInputSetting("-c:v mpeg4_cuvid");
} else if (file.video_codec_name == "vc1") { } else if (file.video_codec_name == "vc1") {
configuration.AddInputSetting("-c:v vc1_cuvid"); configuration.AddInputSetting("-c:v vc1_cuvid");
} else if (file.video_codec_name == "vp8") { } else if (file.video_codec_name == "vp8") {
@ -360,7 +363,7 @@ function buildVideoConfiguration(inputs, file, logger) {
if ( if (
(inputs.qsv !== "true" && inputs.nvenc !== "true") || (inputs.qsv !== "true" && inputs.nvenc !== "true") ||
stream.codec_name === "mpeg4" (inputs.qsv === "true" && stream.codec_name === "mpeg4")
) { ) {
configuration.RemoveOutputSetting("-c:v copy"); configuration.RemoveOutputSetting("-c:v copy");
configuration.AddOutputSetting(`-c:v libx265 ${bitrateSettings}`); configuration.AddOutputSetting(`-c:v libx265 ${bitrateSettings}`);

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta", id: "Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta", id: "Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta", id: "Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
const fs = require("fs"); const fs = require("fs");
const execSync = require("child_process").execSync; const execSync = require("child_process").execSync;

View file

@ -1,3 +1,4 @@
/* eslint-disable */
const exec = require("child_process").exec; const exec = require("child_process").exec;
const fs = require("fs"); const fs = require("fs");

View file

@ -1,3 +1,4 @@
/* eslint-disable */
const fs = require("fs"); const fs = require("fs");
const execSync = require("child_process").execSync; const execSync = require("child_process").execSync;

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs", id: "Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs",

View file

@ -0,0 +1,118 @@
function details() {
return {
id: 'Tdarr_Plugin_henk_Add_Specific_Audio_Codec',
Stage: 'Pre-processing',
Name: '[MKV ONLY] Transcode given codec to other given codec and keep original',
Type: 'Audio',
Operation: 'Transcode',
Description: 'Re-encodes all audio tracks in a given codec to another given codec and keeps original.',
Version: '1.01',
Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/'
+ 'Tdarr_Plugin_henk_Add_Specific_Audio_Codec.js',
Tags: 'post-processing,configurable',
Inputs: [{
name: 'input_codecs',
tooltip: 'Comma separated list of input codecs to be processed. Defaults to dts.'
+ '\\nExample:\\n'
+ 'dts,aac,ac3',
}, {
name: 'output_codec',
tooltip: 'FFMPEG encoder used for the output of the new tracks. Defaults to ac3.',
}, {
name: 'bitrate',
tooltip: 'Specifies the (stereo) bitrate for the new audio codec. Defaults to 128k. Only numbers.',
}, {
name: 'auto_adjust',
tooltip: '[true/false] Multi-channel audio requires a higher bitrate for the same quality, '
+ 'do you want the plugin to calculate this? (bitrate * (channels / 2))',
}, {
name: 'custom_bitrate_input',
tooltip: 'DIRECT ACCESS TO FFMPEG, USE WITH CAUTION. If filled, can be used for custom bitrate arguments.',
}],
};
}
function plugin(file, librarySettings, inputs) {
const response = {
processFile: false,
preset: ', -c copy -map 0:v ',
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') {
response.infoLog += '☒File is not video \n';
return response;
}
// Check if file is mkv. If it isn't then exit plugin.
if (file.container !== 'mkv') {
response.infoLog += '☒File is not mkv \n';
return response;
}
let convertCount = 0;
let streamCount = 0;
let indexCount = 0;
let killPlugin = false;
const inputCodecs = inputs.input_codecs ? inputs.input_codecs.split(',') : ['dts'];
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
const currStream = file.ffProbeData.streams[i];
if (currStream.tags.COPYRIGHT) {
if (currStream.tags.COPYRIGHT === 'henk_asac') {
killPlugin = true;
}
}
}
if (killPlugin) {
response.infoLog
+= '☑File has already been processed by this plugin.\n';
return response;
}
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
const currStream = file.ffProbeData.streams[i];
if (currStream.codec_type.toLowerCase() === 'audio') {
response.preset += ` -map 0:a:${indexCount}? -c:a:${streamCount} copy `;
streamCount += 1;
if (inputCodecs.includes(currStream.codec_name.toLowerCase())) {
convertCount += 1;
let bitrate = `-b:a:${streamCount} `;
if (inputs.custom_bitrate_input) {
bitrate += inputs.custom_bitrate_input;
} else if (inputs.bitrate) {
bitrate += `${inputs.auto_adjust ? (inputs.bitrate * (currStream.channels / 2)) : inputs.bitrate}k`;
} else {
bitrate = '128k';
}
response.preset += ` -map 0:a:${indexCount}? -c:a:${streamCount} ${inputs.output_codec || 'ac3'} ${bitrate} `
+ `-metadata:s:a:${streamCount} title="" -metadata:s:a:${streamCount} copyright="henk_asac" `
+ `-disposition:a:${streamCount} 0`;
streamCount += 1;
}
indexCount += 1;
}
}
if (convertCount > 0) {
response.processFile = true;
response.container = `.${file.container}`;
response.reQueueAfter = true;
response.preset += ' -map 0:s? ';
} else {
response.infoLog
+= '☑File doesn\'t contain audio tracks with the specified codec.\n';
}
return response;
}
module.exports.details = details;
module.exports.plugin = plugin;

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle", id: "Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle", id: "Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_lmg1_Reorder_Streams", id: "Tdarr_Plugin_lmg1_Reorder_Streams",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta", id: "Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation", id: "Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV", id: "Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV",

View file

@ -0,0 +1,124 @@
const fs = require('fs');
module.exports.details = function details() {
return {
id: 'Tdarr_Plugin_rr01_drpeppershaker_extract_subs_to_SRT',
Stage: 'Pre-processing',
Name: 'drpeppershaker Extract embedded subtitles and optionally remove them',
Type: 'Video',
Operation: 'Transcode',
Description: 'This plugin extracts embedded subs in one pass inside Tdarr and will optionally remove them. \n\n '
+ 'All processes happen within Tdarr without the use of any exec() functions, which lets the progress bar '
+ 'report the status correctly. AND all subtitles are extracted in one pass, which is much faster than '
+ 'other options.',
// Created by drpeppershaker with help from reddit user /u/jakejones48, lots of
// improvements made after looking at "Tdarr_Plugin_078d" by HaveAGitGat.
Version: '1.04',
Link: '',
Tags: 'pre-processing,subtitle only,ffmpeg,configurable',
Inputs: [
{
name: 'remove_subs',
tooltip: `Do you want to remove subtitles after they are extracted?
\\nExample:\\n
yes
\\nExample:\\n
no
`,
},
],
};
};
module.exports.plugin = function plugin(file, librarySettings, inputs, otherArguments) {
// Must return this object at some point in the function else plugin will fail.
const response = {
processFile: true,
preset: '',
container: `.${file.container}`,
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '',
};
if (inputs.remove_subs === undefined) {
response.processFile = false;
response.infoLog += '☒ Inputs not entered! \n';
return response;
}
const subsArr = file.ffProbeData.streams.filter((row) => row.codec_name === 'subrip');
if (subsArr.length === 0) {
response.infoLog += 'No subs in file to extract!\n';
response.processFile = false;
return response;
}
response.infoLog += 'Found subs to extract!\n';
let command = '-y,';
for (let i = 0; i < subsArr.length; i += 1) {
const subStream = subsArr[i];
let lang = '';
let title = 'none';
if (subStream.tags) {
lang = subStream.tags.language;
}
if (subStream.tags.title) {
title = subStream.tags.title;
}
const { originalLibraryFile } = otherArguments;
let subsFile = '';
// for Tdarr V2 (2.00.05+)
if (originalLibraryFile && originalLibraryFile.file) {
subsFile = originalLibraryFile.file;
} else {
// for Tdarr V1
subsFile = file.file;
}
subsFile = subsFile.split('.');
subsFile[subsFile.length - 2] += `.${lang}`;
subsFile[subsFile.length - 1] = 'srt';
subsFile = subsFile.join('.');
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')) {
response.infoLog += `Stream ${i} ${lang}.srt is a ${title} track. Skipping!\n`;
} else {
response.infoLog += `Extracting ${lang}.srt\n`;
command += ` -map 0:${index} "${subsFile}"`;
}
}
if (command === '-y,') {
response.infoLog += 'All subs already extracted!\n';
if (inputs.remove_subs === 'no') {
response.processFile = false;
return response;
}
}
response.preset = command;
if (inputs.remove_subs === 'yes') {
response.preset += ' -map 0 -map -0:s -c copy';
}
if (inputs.remove_subs === 'no') {
response.preset += ' -map 0 -c copy';
}
return response;
};

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_s710_nick_h265_nvenc_4K", id: "Tdarr_Plugin_s710_nick_h265_nvenc_4K",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_s7x8_winsome_h265", id: "Tdarr_Plugin_s7x8_winsome_h265",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_s7x9_winsome_h265_10bit", id: "Tdarr_Plugin_s7x9_winsome_h265_10bit",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_s7x9_winsome_h265_nvenc", id: "Tdarr_Plugin_s7x9_winsome_h265_nvenc",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_sdd3_Remove_Commentary_Tracks", id: "Tdarr_Plugin_sdd3_Remove_Commentary_Tracks",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio", id: "Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio",

View file

@ -0,0 +1,68 @@
/* eslint-disable */
function details() {
return {
id: "Tdarr_Plugin_vdka_Remove_DataStreams",
Stage: "Pre-processing",
Name: "Remove Data Streams ",
Type: "Video",
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`,
Version: "1.00",
Link:
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_vdka_Remove_DataStreams.js",
Tags: "pre-processing,ffmpeg",
};
}
function plugin(file) {
//Must return this object
var response = {
processFile: false,
preset: "",
container: ".mp4",
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: false,
infoLog: "",
};
if (file.fileMedium !== "video") {
console.log("File is not video");
response.infoLog += "☒File is not video \n";
response.processFile = false;
return response;
} else {
response.FFmpegMode = true;
response.container = ".mkv";
var hasData = false;
for (var i = 0; i < file.ffProbeData.streams.length; i++) {
try {
if (
file.ffProbeData.streams[i].codec_type.toLowerCase() == "data"
) {
hasData = true;
}
} catch (err) {}
}
if (hasData) {
response.infoLog += "☒File has data streams \n";
response.preset = ",-map 0 -c copy -dn -map_chapters -1";
response.reQueueAfter = true;
response.processFile = true;
return response;
} else {
response.infoLog += "☑File has no data streams! \n";
}
response.infoLog += "☑File meets conditions! \n";
return response;
}
}
module.exports.details = details;
module.exports.plugin = plugin;

View file

@ -0,0 +1,227 @@
/* eslint-disable */
function details () {
return {
id: 'Tdarr_Plugin_vdka_Tiered_NVENC_CQV_BASED_CONFIGURABLE',
Stage: 'Pre-processing',
Name: 'Tiered FFMPEG+NVENC CQ:V BASED CONFIGURABLE',
Type: 'Video',
Operation: 'Transcode',
Description: `[Contains built-in filter] This plugin uses different CQ:V values (similar to crf but for nvenc) depending on resolution,
the CQ:V value is configurable per resolution.
FFmpeg Preset can be configured, uses slow by default.
ALL OPTIONS MUST BE CONFIGURED UNLESS MARKED OPTIONAL!
If files are not in hevc they will be transcoded.
The output container is mkv. \n\n`,
Version: '1.00',
Link:
'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_vdka_Tiered_NVENC_CQV_BASED_CONFIGURABLE.js',
Tags: 'pre-processing,ffmpeg,video only,nvenc h265,configurable',
Inputs: [
{
name: 'sdCQV',
tooltip: `Enter the CQ:V value you want for 480p and 576p content.
\\nExample:\\n
21`
},
{
name: 'hdCQV',
tooltip: `Enter the CQ:V value you want for 720p content.
\\nExample:\\n
23`
},
{
name: 'fullhdCQV',
tooltip: `Enter the CQ:V value you want for 1080p content.
\\nExample:\\n
25`
},
{
name: 'uhdCQV',
tooltip: `Enter the CQ:V value you want for 4K/UHD/2160p content.
\\nExample:\\n
28`
},
{
name: 'bframe',
tooltip: `Specify amount of b-frames to use, 0-5. Use 0 to disable. (GPU must support this, turing and newer supports this, except for the 1650)
\\nExample:\\n
3`
},
{
name: 'ffmpeg_preset',
tooltip: `OPTIONAL, DEFAULTS TO SLOW IF NOT SET
\\n Enter the ffmpeg preset you want, leave blank for default (slow)
\\n This only applies if video is transcoded, video already in h264 will not be transcoded with this setting
\\nExample:\\n
slow
\\nExample:\\n
medium
\\nExample:\\n
fast
\\nExample:\\n
veryfast`
}
]
}
}
module.exports.plugin = function plugin (file, librarySettings, inputs) {
var transcode = 0 //if this var changes to 1 the file will be transcoded
var subcli = `-c:s copy`
var maxmux = ''
var map = '-map 0'
var cqvinuse = ''
//default values that will be returned
var response = {
processFile: false,
preset: '',
container: '.mkv',
handBrakeMode: false,
FFmpegMode: false,
reQueueAfter: true,
infoLog: '',
maxmux: false
}
//check if the file is a video, if not the function will be stopped immediately
if (file.fileMedium !== 'video') {
response.processFile = false
response.infoLog += '☒File is not a video! \n'
return response
} else {
// bitrateprobe = file.ffProbeData.streams[0].bit_rate;
response.infoLog += '☑File is a video! \n'
}
//check if the file is already hevc, it will not be transcoded if true and the function will be stopped immediately
if (file.ffProbeData.streams[0].codec_name == 'hevc') {
response.processFile = false
response.infoLog += '☑File is already in hevc! \n'
return response
}
// Check if preset is configured, default to slow if not
var ffmpeg_preset
if (inputs.ffmpeg_preset === undefined) {
ffmpeg_preset = `slow`
response.infoLog += '☑Preset not set, defaulting to slow\n'
} else {
ffmpeg_preset = `${inputs.ffmpeg_preset}`
response.infoLog += `☑Preset set as ${inputs.ffmpeg_preset}\n`
}
//codec will be checked so it can be transcoded correctly
if (file.video_codec_name == 'h263') {
response.preset = `-c:v h263_cuvid`
} else if (file.video_codec_name == 'h264') {
if (file.ffProbeData.streams[0].profile != 'High 10') {
//Remove HW Decoding for High 10 Profile
response.preset = `-c:v h264_cuvid`
}
} else if (file.video_codec_name == 'mjpeg') {
response.preset = `c:v mjpeg_cuvid`
} else if (file.video_codec_name == 'mpeg1') {
response.preset = `-c:v mpeg1_cuvid`
} else if (file.video_codec_name == 'mpeg2') {
response.preset = `-c:v mpeg2_cuvid`
}
// skipping this one because it's empty
// else if (file.video_codec_name == 'mpeg4') {
// response.preset = ``
// }
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`
} else if (file.video_codec_name == 'vp9') {
response.preset = `-c:v vp9_cuvid`
}
//Set Subtitle Var before adding encode cli
for (var i = 0; i < file.ffProbeData.streams.length; i++) {
try {
if (
file.ffProbeData.streams[i].codec_name.toLowerCase() == 'mov_text' &&
file.ffProbeData.streams[i].codec_type.toLowerCase() == 'subtitle'
) {
subcli = `-c:s srt`
}
} catch (err) {}
//mitigate TrueHD audio causing Too many packets error
try {
if (
file.ffProbeData.streams[i].codec_name.toLowerCase() == 'truehd' ||
(file.ffProbeData.streams[i].codec_name.toLowerCase() == 'dts' &&
file.ffProbeData.streams[i].profile.toLowerCase() == 'dts-hd ma') ||
(file.ffProbeData.streams[i].codec_name.toLowerCase() == 'aac' &&
file.ffProbeData.streams[i].sample_rate.toLowerCase() == '44100' &&
file.ffProbeData.streams[i].codec_type.toLowerCase() == 'audio')
) {
maxmux = ` -max_muxing_queue_size 9999`
}
} catch (err) {}
//mitigate errors due to embeded pictures
try {
if (
(file.ffProbeData.streams[i].codec_name.toLowerCase() == 'png' ||
file.ffProbeData.streams[i].codec_name.toLowerCase() == 'bmp' ||
file.ffProbeData.streams[i].codec_name.toLowerCase() == 'mjpeg') &&
file.ffProbeData.streams[i].codec_type.toLowerCase() == 'video'
) {
map = `-map 0:v:0 -map 0:a -map 0:s?`
}
} catch (err) {}
}
//file will be encoded if the resolution is 480p or 576p
//codec will be checked so it can be transcoded correctly
if (file.video_resolution === '480p' || file.video_resolution === '576p') {
cqvinuse = `${inputs.sdCQV}`
response.preset += `,${map} -dn -c:v hevc_nvenc -pix_fmt p010le -rc vbr_hq -b:v 0 -preset ${ffmpeg_preset} -cq ${inputs.sdCQV} -rc-lookahead 32 -bf ${inputs.bframe} -a53cc 0 -c:a copy ${subcli}${maxmux}`
transcode = 1
}
//file will be encoded if the resolution is 720p
//codec will be checked so it can be transcoded correctly
if (file.video_resolution === '720p') {
cqvinuse = `${inputs.hdCQV}`
response.preset += `,${map} -dn -c:v hevc_nvenc -pix_fmt p010le -rc vbr_hq -b:v 0 -preset ${ffmpeg_preset} -cq ${inputs.hdCQV} -rc-lookahead 32 -bf ${inputs.bframe} -a53cc 0 -c:a copy ${subcli}${maxmux}`
transcode = 1
}
//file will be encoded if the resolution is 1080p
//codec will be checked so it can be transcoded correctly
if (file.video_resolution === '1080p') {
cqvinuse = `${inputs.fullhdCQV}`
response.preset += `,${map} -dn -c:v hevc_nvenc -pix_fmt p010le -rc vbr_hq -b:v 0 -preset ${ffmpeg_preset} -cq ${inputs.fullhdCQV} -rc-lookahead 32 -bf ${inputs.bframe} -a53cc 0 -c:a copy ${subcli}${maxmux}`
transcode = 1
}
//file will be encoded if the resolution is 4K
//codec will be checked so it can be transcoded correctly
if (file.video_resolution === '4KUHD') {
cqvinuse = `${inputs.uhdCQV}`
response.preset += `,${map} -dn -c:v hevc_nvenc -pix_fmt p010le -rc vbr_hq -b:v 0 -preset ${ffmpeg_preset} -cq ${inputs.uhdCQV} -rc-lookahead 32 -bf ${inputs.bframe} -a53cc 0 -c:a copy ${subcli}${maxmux}`
transcode = 1
}
//check if the file is eligible for transcoding
//if true the neccessary response values will be changed
if (transcode == 1) {
response.processFile = true
response.FFmpegMode = true
response.reQueueAfter = true
response.infoLog += `☑File is ${file.video_resolution}, using CQ:V value of ${cqvinuse}!\n`
response.infoLog += `☒File is not hevc!\n`
response.infoLog += `File is being transcoded!\n`
}
return response
}
module.exports.details = details

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_x7ab_Remove_Subs", id: "Tdarr_Plugin_x7ab_Remove_Subs",

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function details() { function details() {
return { return {
id: "Tdarr_Plugin_x7ac_Remove_Closed_Captions", id: "Tdarr_Plugin_x7ac_Remove_Closed_Captions",

View file

@ -1,142 +1,137 @@
function details() { function details() {
return { return {
id: "Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium", id: 'Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "TheRealShadoh FFmpeg Subs Medium, video MP4, audio AAC, keep subs. ", Name: 'TheRealShadoh FFmpeg Subs Medium, video MP4, audio AAC, keep subs. ',
Type: "Video", Type: 'Video',
Description: `[Contains built-in filter] This plugin transcodes into H264 using FFmpeg's 'Medium' preset if the file is not in H264 already. It maintains all subtitles. It removes metadata (if a title exists), and maintains all audio tracks. The output container is MP4. \n\n Description: '[Contains built-in filter] This plugin transcodes into H264 using FFmpeg\'s '
+ '\'Medium\' preset if the file is not in H264 already. It maintains all subtitles. It removes metadata'
+ ` (if a title exists), and maintains all audio tracks. The output container is MP4. \n\n
`, `,
Version: "1.00", Version: '1.00',
Link: Link:
"https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js", 'https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/'
Tags: "pre-processing,ffmpeg,h264", + 'Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js',
Tags: 'pre-processing,ffmpeg,h264',
}; };
} }
function plugin(file) { function plugin(file) {
//Must return this object // Must return this object
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
container: ".mp4", container: '.mp4',
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: false, FFmpegMode: false,
reQueueAfter: false, reQueueAfter: false,
infoLog: "", infoLog: '',
}; };
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
console.log("File is not video"); // eslint-disable-next-line no-console
console.log('File is not video');
response.infoLog += "☒File is not video \n"; response.infoLog += '☒File is not video \n';
response.processFile = false; response.processFile = false;
return response; return response;
} else { }
var jsonString = JSON.stringify(file); const jsonString = JSON.stringify(file);
var hasSubs = false; let hasSubs = false;
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
try { try {
let streamData = file.ffProbeData.streams[i]; const streamData = file.ffProbeData.streams[i];
if ( if (
streamData.codec_type.toLowerCase() == "subtitle" && streamData.codec_type.toLowerCase() === 'subtitle'
streamData.codec_name != "mov_text" && streamData.codec_name !== 'mov_text'
) { ) {
hasSubs = true; hasSubs = true;
} }
} catch (err) {} } catch (err) {
// err
} }
}
if (file.ffProbeData.streams[0].codec_name != "h264") { if (file.ffProbeData.streams[0].codec_name !== 'h264') {
response.infoLog += "☒File is not in h264! \n"; response.infoLog += '☒File is not in h264! \n';
response.preset = response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a '
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v libx264 -preset medium -c:a aac -c:s mov_text"; + '-c:v libx264 -preset medium -c:a aac -c:s mov_text';
response.reQueueAfter = true; response.reQueueAfter = true;
response.processFile = true; response.processFile = true;
response.FFmpegMode = true; response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File is already in h264! \n";
}
///
if (
file.meta.Title != undefined &&
!jsonString.includes("aac") &&
hasSubs
) {
response.infoLog += "☒File has title metadata and no aac and subs \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (!jsonString.includes("aac") && hasSubs) {
response.infoLog += "☒File has no aac track and has subs \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (file.meta.Title != undefined && hasSubs) {
response.infoLog += "☒File has title and has subs \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
///
if (file.meta.Title != undefined) {
response.infoLog += "☒File has title metadata \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has no title metadata \n";
}
if (!jsonString.includes("aac")) {
response.infoLog += "☒File has no aac track \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has aac track \n";
}
if (hasSubs) {
response.infoLog += "☒File has incompatible subs \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has no/compatible subs \n";
}
response.infoLog += "☑File meets conditions! \n";
return response; return response;
} }
response.infoLog += '☑File is already in h264! \n';
///
if (
file.meta.Title !== undefined
&& !jsonString.includes('aac')
&& hasSubs
) {
response.infoLog += '☒File has title metadata and no aac and subs \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (!jsonString.includes('aac') && hasSubs) {
response.infoLog += '☒File has no aac track and has subs \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (file.meta.Title !== undefined && hasSubs) {
response.infoLog += '☒File has title and has subs \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
///
if (file.meta.Title !== undefined) {
response.infoLog += '☒File has title metadata \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has no title metadata \n';
if (!jsonString.includes('aac')) {
response.infoLog += '☒File has no aac track \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has aac track \n';
if (hasSubs) {
response.infoLog += '☒File has incompatible subs \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has no/compatible subs \n';
response.infoLog += '☑File meets conditions! \n';
return response;
} }
module.exports.details = details; module.exports.details = details;

View file

@ -1,3 +1,9 @@
/* eslint-disable */
module.exports.dependencies = [
'fs-extra',
];
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_z18s_rename_files_based_on_codec", id: "Tdarr_Plugin_z18s_rename_files_based_on_codec",
@ -5,7 +11,7 @@ module.exports.details = function details() {
Name: "Rename based on codec", Name: "Rename based on codec",
Type: "Video", Type: "Video",
Operation: "", Operation: "",
Description: `[TESTING][Contains built-in filter]This plugin renames 264 files to 265 or vice versa depending on codec. \n\n`, Description: `[Contains built-in filter]This plugin renames 264 files to 265 or vice versa depending on codec. \n\n`,
Version: "1.00", Version: "1.00",
Link: "", Link: "",
Tags: "post-processing", Tags: "post-processing",

View file

@ -1,33 +1,39 @@
module.exports.dependencies = [
'fs-extra',
];
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution", id: 'Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution',
Stage: "Post-processing", Stage: 'Post-processing',
Name: "Rename based on codec and resolution", Name: 'Rename based on codec and resolution',
Type: "Video", Type: 'Video',
Operation: "", Operation: '',
Description: `[TESTING][Contains built-in filter]This plugin renames files depending on codec and resolution\n\n`, Description: '[Contains built-in filter]This plugin renames files depending on codec and resolution\n\n',
Version: "1.00", Version: '1.00',
Link: "", Link: '',
Tags: "post-processing", Tags: 'post-processing',
}; };
}; };
module.exports.plugin = function plugin(file, librarySettings, inputs) { module.exports.plugin = function plugin(file) {
try { try {
const fs = require("fs"); // eslint-disable-next-line global-require
const path = require("path"); const fs = require('fs');
let rootModules // eslint-disable-next-line global-require
if (fs.existsSync(path.join(process.cwd(), "/npm"))) { const path = require('path');
rootModules = path.join(process.cwd(), "/npm/node_modules/"); let rootModules;
if (fs.existsSync(path.join(process.cwd(), '/npm'))) {
rootModules = path.join(process.cwd(), '/npm/node_modules/');
} else { } else {
rootModules = ""; rootModules = '';
} }
const fsextra = require(rootModules + "fs-extra"); // eslint-disable-next-line global-require,import/no-dynamic-require
let fileNameOld = file._id; const fsextra = require(`${rootModules}fs-extra`);
const fileNameOld = file._id;
let resolutions = { const resolutions = {
_480p: '480p', _480p: '480p',
_576p: '576p', _576p: '576p',
_720p: '720p', _720p: '720p',
@ -35,30 +41,29 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
_4KUHD: '4k', _4KUHD: '4k',
_DCI4K: '4k', _DCI4K: '4k',
_8KUHD: '8k', _8KUHD: '8k',
_Other: 'Other' _Other: 'Other',
} };
//only process if properties available // only process if properties available
if (file.ffProbeData.streams[0].codec_name && file.video_resolution) { if (file.ffProbeData.streams[0].codec_name && file.video_resolution) {
const resolution = `_${file.video_resolution}`;
const resShouldBe = resolutions[resolution];
const codecShouldBe = file.ffProbeData.streams[0].codec_name;
let resolution = '_' + file.video_resolution // Remove container from processing
let resShouldBe = resolutions[resolution]
let codecShouldBe = file.ffProbeData.streams[0].codec_name
//Remove container from processing
let fileName = file._id; let fileName = file._id;
let parts = fileName.split('/') let parts = fileName.split('/');
fileName = parts[parts.length - 1] fileName = parts[parts.length - 1];
parts.splice(parts.length - 1, 1) parts.splice(parts.length - 1, 1);
parts = parts.join('/') parts = parts.join('/');
fileName = fileName.split('.') fileName = fileName.split('.');
let container = fileName[fileName.length - 1] const container = fileName[fileName.length - 1];
fileName.splice(fileName.length - 1, 1) fileName.splice(fileName.length - 1, 1);
fileName = fileName.join('.') fileName = fileName.join('.');
//put term substrings below strings (i.e. '480' below '480p') // put term substrings below strings (i.e. '480' below '480p')
let terms = [ const terms = [
'480p', '480p',
'480', '480',
'576p', '576p',
@ -76,60 +81,63 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
'264', '264',
'h265', 'h265',
'265', '265',
'hevc' 'hevc',
] ];
//clean up res and codec terms from name // clean up res and codec terms from name
for (let i = 0; i < terms.length; i++) { for (let i = 0; i < terms.length; i += 1) {
// eslint-disable-next-line no-constant-condition
while (true) { while (true) {
let idx = fileName.indexOf(terms[i]); const idx = fileName.indexOf(terms[i]);
if (idx === -1) { if (idx === -1) {
break break;
} else { } else {
let length = terms[i].length const { length } = terms[i];
fileName = fileName.split('') fileName = fileName.split('');
fileName.splice(idx, length); fileName.splice(idx, length);
fileName = fileName.join('') fileName = fileName.join('');
} }
} }
} }
if (resShouldBe === 'Other') { if (resShouldBe === 'Other') {
fileName = parts + '/' + fileName + '_' + codecShouldBe + '.' + container fileName = `${parts}/${fileName}_${codecShouldBe}.${container}`;
} else { } else {
fileName = parts + '/' + fileName + '_' + resShouldBe + '_' + codecShouldBe + '.' + container fileName = `${parts}/${fileName}_${resShouldBe}_${codecShouldBe}.${container}`;
} }
//clean up word breakers // clean up word breakers
let breakers = [ const breakers = [
'.', '.',
'_', '_',
'-', '-',
] ];
fileName = fileName.split('') fileName = fileName.split('');
for (let i = 0; i < fileName.length; i++) { for (let i = 0; i < fileName.length; i += 1) {
for (let j = 0; j < breakers.length; j++) { for (let j = 0; j < breakers.length; j += 1) {
for (let k = 0; k < breakers.length; k++) { for (let k = 0; k < breakers.length; k += 1) {
if (fileName[i] === breakers[j] && fileName[i + 1] === breakers[k]) { if (fileName[i] === breakers[j] && fileName[i + 1] === breakers[k]) {
fileName.splice(i, 1); fileName.splice(i, 1);
i-- i -= 1;
} }
} }
} }
} }
fileName = fileName.join('') fileName = fileName.join('');
file._id = fileName // eslint-disable-next-line no-param-reassign
file.file = fileName file._id = fileName;
// eslint-disable-next-line no-param-reassign
file.file = fileName;
if (fileNameOld != file._id) { if (fileNameOld !== file._id) {
fsextra.moveSync(fileNameOld, file._id, { fsextra.moveSync(fileNameOld, file._id, {
overwrite: true, overwrite: true,
}); });
let response = { const response = {
file, file,
removeFromDB: false, removeFromDB: false,
updateDB: true, updateDB: true,
@ -139,6 +147,9 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
} }
} }
} catch (err) { } catch (err) {
// eslint-disable-next-line no-console
console.log(err); console.log(err);
} }
return undefined;
}; };

View file

@ -1,143 +1,137 @@
function details() { function details() {
return { return {
id: "Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast", id: 'Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "TheRealShadoh FFmpeg Subs Fast, video MP4, audio AAC, keep subs. ", Name: 'TheRealShadoh FFmpeg Subs Fast, video MP4, audio AAC, keep subs. ',
Type: "Video", Type: 'Video',
Description: `[Contains built-in filter] This plugin transcodes into H264 using FFmpeg's 'Fast' preset if the file is not in H264 already. It maintains all subtitles. It removes metadata (if a title exists), and maintains all audio tracks. The output container is MP4. \n\n Description: '[Contains built-in filter] This plugin transcodes into H264 using '
+ 'FFmpeg\'s \'Fast\' preset if the file is not in H264 already. It maintains all subtitles. '
+ `It removes metadata (if a title exists), and maintains all audio tracks. The output container is MP4. \n\n
`, `,
Version: "1.00", Version: '1.00',
Link: Link:
"https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js", 'https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/'
Tags: "pre-processing,ffmpeg,h264", + 'Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js',
Tags: 'pre-processing,ffmpeg,h264',
}; };
} }
function plugin(file) { function plugin(file) {
//Must return this object // Must return this object
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
container: ".mp4", container: '.mp4',
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: false, FFmpegMode: false,
reQueueAfter: false, reQueueAfter: false,
infoLog: "", infoLog: '',
}; };
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
console.log("File is not video"); // eslint-disable-next-line no-console
console.log('File is not video');
response.infoLog += "☒File is not video \n"; response.infoLog += '☒File is not video \n';
response.processFile = false; response.processFile = false;
return response; return response;
} else { }
var jsonString = JSON.stringify(file); const jsonString = JSON.stringify(file);
var hasSubs = false; let hasSubs = false;
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
try { try {
let streamData = file.ffProbeData.streams[i]; const streamData = file.ffProbeData.streams[i];
if ( if (
streamData.codec_type.toLowerCase() == "subtitle" && streamData.codec_type.toLowerCase() === 'subtitle'
streamData.codec_name != "mov_text" && streamData.codec_name !== 'mov_text'
) { ) {
hasSubs = true; hasSubs = true;
} }
} catch (err) {} } catch (err) {
// err
} }
}
if (file.ffProbeData.streams[0].codec_name != "h264") { if (file.ffProbeData.streams[0].codec_name !== 'h264') {
response.infoLog += "☒File is not in h264! \n"; response.infoLog += '☒File is not in h264! \n';
response.preset = response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v libx264 -preset fast -c:a aac -c:s mov_text';
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v libx264 -preset fast -c:a aac -c:s mov_text"; response.reQueueAfter = true;
response.reQueueAfter = true; response.processFile = true;
response.processFile = true; response.FFmpegMode = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File is already in h264! \n";
}
///
if (
file.meta.Title != undefined &&
!jsonString.includes("aac") &&
hasSubs
) {
response.infoLog += "☒File has title metadata and no aac and subs \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (!jsonString.includes("aac") && hasSubs) {
response.infoLog += "☒File has no aac track and has subs \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (file.meta.Title != undefined && hasSubs) {
response.infoLog += "☒File has title and has subs \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
///
if (file.meta.Title != undefined) {
response.infoLog += "☒File has title metadata \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has no title metadata \n";
}
if (!jsonString.includes("aac")) {
response.infoLog += "☒File has no aac track \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has aac track \n";
}
if (hasSubs) {
response.infoLog += "☒File has incompatible subs \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has no/compatible subs \n";
}
response.infoLog += "☑File meets conditions! \n";
return response; return response;
} }
response.infoLog += '☑File is already in h264! \n';
///
if (
file.meta.Title !== undefined
&& !jsonString.includes('aac')
&& hasSubs
) {
response.infoLog += '☒File has title metadata and no aac and subs \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (!jsonString.includes('aac') && hasSubs) {
response.infoLog += '☒File has no aac track and has subs \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (file.meta.Title !== undefined && hasSubs) {
response.infoLog += '☒File has title and has subs \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
///
if (file.meta.Title !== undefined) {
response.infoLog += '☒File has title metadata \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has no title metadata \n';
if (!jsonString.includes('aac')) {
response.infoLog += '☒File has no aac track \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has aac track \n';
if (hasSubs) {
response.infoLog += '☒File has incompatible subs \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has no/compatible subs \n';
response.infoLog += '☑File meets conditions! \n';
return response;
} }
module.exports.details = details; module.exports.details = details;

View file

@ -1,143 +1,137 @@
function details() { function details() {
return { return {
id: "Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow", id: 'Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: "TheRealShadoh FFmpeg Subs Slow, video MP4, audio AAC, keep subs. ", Name: 'TheRealShadoh FFmpeg Subs Slow, video MP4, audio AAC, keep subs. ',
Type: "Video", Type: 'Video',
Description: `[Contains built-in filter] This plugin transcodes into H264 using FFmpeg's 'Slow' preset if the file is not in H264 already. It maintains all subtitles. It removes metadata (if a title exists), and maintains all audio tracks. The output container is MP4. \n\n Description: '[Contains built-in filter] This plugin transcodes into H264 using FFmpeg\'s \'Slow\' preset'
+ ' if the file is not in H264 already. It maintains all subtitles. It removes metadata (if a title exists), '
+ `and maintains all audio tracks. The output container is MP4. \n\n
`, `,
Version: "1.00", Version: '1.00',
Link: Link:
"https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js", 'https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/'
Tags: "pre-processing,ffmpeg,h264", + 'Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js',
Tags: 'pre-processing,ffmpeg,h264',
}; };
} }
function plugin(file) { function plugin(file) {
//Must return this object // Must return this object
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
container: ".mp4", container: '.mp4',
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: false, FFmpegMode: false,
reQueueAfter: false, reQueueAfter: false,
infoLog: "", infoLog: '',
}; };
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
console.log("File is not video"); // eslint-disable-next-line no-console
console.log('File is not video');
response.infoLog += "☒File is not video \n"; response.infoLog += '☒File is not video \n';
response.processFile = false; response.processFile = false;
return response; return response;
} else { }
var jsonString = JSON.stringify(file); const jsonString = JSON.stringify(file);
var hasSubs = false; let hasSubs = false;
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
try { try {
let streamData = file.ffProbeData.streams[i]; const streamData = file.ffProbeData.streams[i];
if ( if (
streamData.codec_type.toLowerCase() == "subtitle" && streamData.codec_type.toLowerCase() === 'subtitle'
streamData.codec_name != "mov_text" && streamData.codec_name !== 'mov_text'
) { ) {
hasSubs = true; hasSubs = true;
} }
} catch (err) {} } catch (err) {
// err
} }
}
if (file.ffProbeData.streams[0].codec_name != "h264") { if (file.ffProbeData.streams[0].codec_name !== 'h264') {
response.infoLog += "☒File is not in h264! \n"; response.infoLog += '☒File is not in h264! \n';
response.preset = response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v libx264 -preset slow -c:a aac -c:s mov_text';
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v libx264 -preset slow -c:a aac -c:s mov_text"; response.reQueueAfter = true;
response.reQueueAfter = true; response.processFile = true;
response.processFile = true; response.FFmpegMode = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File is already in h264! \n";
}
///
if (
file.meta.Title != undefined &&
!jsonString.includes("aac") &&
hasSubs
) {
response.infoLog += "☒File has title metadata and no aac and subs \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (!jsonString.includes("aac") && hasSubs) {
response.infoLog += "☒File has no aac track and has subs \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (file.meta.Title != undefined && hasSubs) {
response.infoLog += "☒File has title and has subs \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
///
if (file.meta.Title != undefined) {
response.infoLog += "☒File has title metadata \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has no title metadata \n";
}
if (!jsonString.includes("aac")) {
response.infoLog += "☒File has no aac track \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has aac track \n";
}
if (hasSubs) {
response.infoLog += "☒File has incompatible subs \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has no/compatible subs \n";
}
response.infoLog += "☑File meets conditions! \n";
return response; return response;
} }
response.infoLog += '☑File is already in h264! \n';
///
if (
file.meta.Title !== undefined
&& !jsonString.includes('aac')
&& hasSubs
) {
response.infoLog += '☒File has title metadata and no aac and subs \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (!jsonString.includes('aac') && hasSubs) {
response.infoLog += '☒File has no aac track and has subs \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (file.meta.Title !== undefined && hasSubs) {
response.infoLog += '☒File has title and has subs \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
///
if (file.meta.Title !== undefined) {
response.infoLog += '☒File has title metadata \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has no title metadata \n';
if (!jsonString.includes('aac')) {
response.infoLog += '☒File has no aac track \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has aac track \n';
if (hasSubs) {
response.infoLog += '☒File has incompatible subs \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has no/compatible subs \n';
response.infoLog += '☑File meets conditions! \n';
return response;
} }
module.exports.details = details; module.exports.details = details;

View file

@ -1,144 +1,139 @@
function details() { function details() {
return { return {
id: "Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast", id: 'Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast',
Stage: "Pre-processing", Stage: 'Pre-processing',
Name: Name:
"TheRealShadoh FFmpeg Subs VeryFast, video MP4, audio AAC, keep subs. ", 'TheRealShadoh FFmpeg Subs VeryFast, video MP4, audio AAC, keep subs. ',
Type: "Video", Type: 'Video',
Description: `[Contains built-in filter] This plugin transcodes into H264 using FFmpeg's 'VeryFast' preset if the file is not in H264 already. It maintains all subtitles. It removes metadata (if a title exists), and maintains all audio tracks. The output container is MP4. \n\n Description: '[Contains built-in filter] This plugin transcodes into H264 using FFmpeg\'s \'VeryFast\' preset '
+ 'if the file is not in H264 already. It maintains all subtitles. It removes metadata (if a title exists), '
+ `and maintains all audio tracks. The output container is MP4. \n\n
`, `,
Version: "1.00", Version: '1.00',
Link: Link:
"https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_Veryfast.js", 'https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/'
Tags: "pre-processing,ffmpeg,h264", + 'Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_Veryfast.js',
Tags: 'pre-processing,ffmpeg,h264',
}; };
} }
function plugin(file) { function plugin(file) {
//Must return this object // Must return this object
var response = { const response = {
processFile: false, processFile: false,
preset: "", preset: '',
container: ".mp4", container: '.mp4',
handBrakeMode: false, handBrakeMode: false,
FFmpegMode: false, FFmpegMode: false,
reQueueAfter: false, reQueueAfter: false,
infoLog: "", infoLog: '',
}; };
if (file.fileMedium !== "video") { if (file.fileMedium !== 'video') {
console.log("File is not video"); // eslint-disable-next-line no-console
console.log('File is not video');
response.infoLog += "☒File is not video \n"; response.infoLog += '☒File is not video \n';
response.processFile = false; response.processFile = false;
return response; return response;
} else { }
var jsonString = JSON.stringify(file); const jsonString = JSON.stringify(file);
var hasSubs = false; let hasSubs = false;
for (var i = 0; i < file.ffProbeData.streams.length; i++) { for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
try { try {
let streamData = file.ffProbeData.streams[i]; const streamData = file.ffProbeData.streams[i];
if ( if (
streamData.codec_type.toLowerCase() == "subtitle" && streamData.codec_type.toLowerCase() === 'subtitle'
streamData.codec_name != "mov_text" && streamData.codec_name !== 'mov_text'
) { ) {
hasSubs = true; hasSubs = true;
} }
} catch (err) {} } catch (err) {
// err
} }
}
if (file.ffProbeData.streams[0].codec_name != "h264") { if (file.ffProbeData.streams[0].codec_name !== 'h264') {
response.infoLog += "☒File is not in h264! \n"; response.infoLog += '☒File is not in h264! \n';
response.preset = response.preset = ', -map_metadata -1 -map 0:v -map 0:s? '
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v libx264 -preset veryfast -c:a aac -c:s mov_text"; + '-map 0:a -c:v libx264 -preset veryfast -c:a aac -c:s mov_text';
response.reQueueAfter = true; response.reQueueAfter = true;
response.processFile = true; response.processFile = true;
response.FFmpegMode = true; response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File is already in h264! \n";
}
///
if (
file.meta.Title != undefined &&
!jsonString.includes("aac") &&
hasSubs
) {
response.infoLog += "☒File has title metadata and no aac and subs \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (!jsonString.includes("aac") && hasSubs) {
response.infoLog += "☒File has no aac track and has subs \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (file.meta.Title != undefined && hasSubs) {
response.infoLog += "☒File has title and has subs \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
///
if (file.meta.Title != undefined) {
response.infoLog += "☒File has title metadata \n";
response.preset =
", -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has no title metadata \n";
}
if (!jsonString.includes("aac")) {
response.infoLog += "☒File has no aac track \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has aac track \n";
}
if (hasSubs) {
response.infoLog += "☒File has incompatible subs \n";
response.preset =
", -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text";
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
} else {
response.infoLog += "☑File has no/compatible subs \n";
}
response.infoLog += "☑File meets conditions! \n";
return response; return response;
} }
response.infoLog += '☑File is already in h264! \n';
///
if (
file.meta.Title !== undefined
&& !jsonString.includes('aac')
&& hasSubs
) {
response.infoLog += '☒File has title metadata and no aac and subs \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (!jsonString.includes('aac') && hasSubs) {
response.infoLog += '☒File has no aac track and has subs \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
if (file.meta.Title !== undefined && hasSubs) {
response.infoLog += '☒File has title and has subs \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
///
if (file.meta.Title !== undefined) {
response.infoLog += '☒File has title metadata \n';
response.preset = ', -map_metadata -1 -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has no title metadata \n';
if (!jsonString.includes('aac')) {
response.infoLog += '☒File has no aac track \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a aac -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has aac track \n';
if (hasSubs) {
response.infoLog += '☒File has incompatible subs \n';
response.preset = ', -map 0:v -map 0:s? -map 0:a -c:v copy -c:a copy -c:s mov_text';
response.reQueueAfter = true;
response.processFile = true;
response.FFmpegMode = true;
return response;
}
response.infoLog += '☑File has no/compatible subs \n';
response.infoLog += '☑File meets conditions! \n';
return response;
} }
module.exports.details = details; module.exports.details = details;

648
README.md
View file

@ -89,243 +89,411 @@ Note, to access FFprobe inside a plugin, use this:
Example file object: Example file object:
let file = { let file = {
_id: 'C:/Users/H/Desktop/Test Input1/Sample.mp4', "meta": {
DB: 'ZRPDmnmpyuAEQi7nG', "SourceFile": "C:/Users/H/Desktop/Transcode/Source/SampleVideo_1280x720_30mb - Copy (5).mp4",
HealthCheck: 'Not attempted', "errors": [],
TranscodeDecisionMaker: 'Not attempted', "Duration": 170.902,
bit_rate: 1690430.4, "PreviewDuration": 0,
container: 'mp4', "SelectionDuration": 0,
createdAt: 2019-09-26T06:46:31.929Z, "TrackDuration": 170.861,
ffProbeData: "MediaDuration": 170.901333333333,
{ streams: "ExifToolVersion": 12.1,
[ { index: 0, "FileName": "SampleVideo_1280x720_30mb - Copy (5).mp4",
codec_name: 'h264', "Directory": "C:/Users/H/Desktop/Transcode/Source",
codec_long_name: 'H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10', "FileSize": "16 MB",
profile: 'Main', "FileModifyDate": {
codec_type: 'video', "year": 2020,
codec_time_base: '1/50', "month": 12,
codec_tag_string: 'avc1', "day": 26,
codec_tag: '0x31637661', "hour": 12,
width: 1280, "minute": 29,
height: 720, "second": 11,
coded_width: 1280, "millisecond": 0,
coded_height: 720, "tzoffsetMinutes": 60,
has_b_frames: 0, "rawValue": "2020:12:26 12:29:11+01:00"
sample_aspect_ratio: '1:1', },
display_aspect_ratio: '16:9', "FileAccessDate": {
pix_fmt: 'yuv420p', "year": 2020,
level: 31, "month": 12,
chroma_location: 'left', "day": 27,
refs: 1, "hour": 11,
is_avc: 'true', "minute": 42,
nal_length_size: '4', "second": 53,
r_frame_rate: '25/1', "millisecond": 0,
avg_frame_rate: '25/1', "tzoffsetMinutes": 60,
time_base: '1/12800', "rawValue": "2020:12:27 11:42:53+01:00"
start_pts: 0, },
start_time: '0.000000', "FileCreateDate": {
duration_ts: 67584, "year": 2020,
duration: '5.280000', "month": 12,
bit_rate: '1205959', "day": 26,
bits_per_raw_sample: '8', "hour": 12,
nb_frames: '132', "minute": 29,
disposition: "second": 22,
{ default: 1, "millisecond": 0,
dub: 0, "tzoffsetMinutes": 60,
original: 0, "rawValue": "2020:12:26 12:29:22+01:00"
comment: 0, },
lyrics: 0, "FilePermissions": "rw-rw-rw-",
karaoke: 0, "FileType": "MP4",
forced: 0, "FileTypeExtension": "mp4",
hearing_impaired: 0, "MIMEType": "video/mp4",
visual_impaired: 0, "MajorBrand": "MP4 v2 [ISO 14496-14]",
clean_effects: 0, "MinorVersion": "0.2.0",
attached_pic: 0, "CompatibleBrands": [
timed_thumbnails: 0 }, "isom",
tags: "iso2",
{ creation_time: '1970-01-01T00:00:00.000000Z', "avc1",
language: 'und', "mp41"
handler_name: 'VideoHandler' } }, ],
{ index: 1, "MediaDataSize": 16831682,
codec_name: 'aac', "MediaDataOffset": 48,
codec_long_name: 'AAC (Advanced Audio Coding)', "MovieHeaderVersion": 0,
profile: 'LC', "CreateDate": {
codec_type: 'audio', "year": 2020,
codec_time_base: '1/48000', "month": 12,
codec_tag_string: 'mp4a', "day": 26,
codec_tag: '0x6134706d', "hour": 11,
sample_fmt: 'fltp', "minute": 28,
sample_rate: '48000', "second": 53,
channels: 6, "millisecond": 0,
channel_layout: '5.1', "rawValue": "2020:12:26 11:28:53"
bits_per_sample: 0, },
r_frame_rate: '0/0', "ModifyDate": {
avg_frame_rate: '0/0', "year": 2020,
time_base: '1/48000', "month": 12,
start_pts: 0, "day": 26,
start_time: '0.000000', "hour": 11,
duration_ts: 254976, "minute": 28,
duration: '5.312000', "second": 53,
bit_rate: '384828', "millisecond": 0,
max_bit_rate: '400392', "rawValue": "2020:12:26 11:28:53"
nb_frames: '249', },
disposition: "TimeScale": 1000,
{ default: 1, "PreferredRate": 1,
dub: 0, "PreferredVolume": "100.00%",
original: 0, "PreviewTime": "0 s",
comment: 0, "PosterTime": "0 s",
lyrics: 0, "SelectionTime": "0 s",
karaoke: 0, "CurrentTime": "0 s",
forced: 0, "NextTrackID": 3,
hearing_impaired: 0, "TrackHeaderVersion": 0,
visual_impaired: 0, "TrackCreateDate": {
clean_effects: 0, "year": 2020,
attached_pic: 0, "month": 12,
timed_thumbnails: 0 }, "day": 26,
tags: "hour": 11,
{ creation_time: '1970-01-01T00:00:00.000000Z', "minute": 28,
language: 'und', "second": 53,
handler_name: 'SoundHandler' } } ] }, "millisecond": 0,
ffProbeRead: 'success', "rawValue": "2020:12:26 11:28:53"
file: 'C:/Users/H/Desktop/Test Input1/Sample.mp4', },
fileMedium: 'video', "TrackModifyDate": {
file_size: 1.056519, "year": 2020,
meta: "month": 12,
{ SourceFile: 'C:/Users/H/Desktop/Test Input1/Sample.mp4', "day": 26,
errors: [], "hour": 11,
Duration: 5.312, "minute": 28,
PreviewDuration: 0, "second": 53,
SelectionDuration: 0, "millisecond": 0,
TrackDuration: 5.28, "rawValue": "2020:12:26 11:28:53"
MediaDuration: 5.312, },
ExifToolVersion: 11.65, "TrackID": 1,
FileName: 'Sample.mp4', "TrackLayer": 0,
Directory: 'C:/Users/H/Desktop/Test Input1', "TrackVolume": "0.00%",
FileSize: '1032 kB', "ImageWidth": 1280,
FileModifyDate: "ImageHeight": 720,
{ year: 2019, "GraphicsMode": "srcCopy",
month: 9, "OpColor": "0 0 0",
day: 24, "CompressorID": "avc1",
hour: 7, "SourceImageWidth": 1280,
minute: 24, "SourceImageHeight": 720,
second: 22, "XResolution": 72,
millisecond: 0, "YResolution": 72,
tzoffsetMinutes: 60, "BitDepth": 24,
rawValue: '2019:09:24 07:24:22+01:00' }, "PixelAspectRatio": "1:1",
FileAccessDate: "VideoFrameRate": 24.997,
{ year: 2019, "MatrixStructure": "1 0 0 0 1 0 0 0 1",
month: 9, "MediaHeaderVersion": 0,
day: 26, "MediaCreateDate": {
hour: 7, "year": 2020,
minute: 44, "month": 12,
second: 30, "day": 26,
millisecond: 0, "hour": 11,
tzoffsetMinutes: 60, "minute": 28,
rawValue: '2019:09:26 07:44:30+01:00' }, "second": 53,
FileCreateDate: "millisecond": 0,
{ year: 2019, "rawValue": "2020:12:26 11:28:53"
month: 9, },
day: 26, "MediaModifyDate": {
hour: 7, "year": 2020,
minute: 44, "month": 12,
second: 30, "day": 26,
millisecond: 0, "hour": 11,
tzoffsetMinutes: 60, "minute": 28,
rawValue: '2019:09:26 07:44:30+01:00' }, "second": 53,
FilePermissions: 'rw-rw-rw-', "millisecond": 0,
FileType: 'MP4', "rawValue": "2020:12:26 11:28:53"
FileTypeExtension: 'mp4', },
MIMEType: 'video/mp4', "MediaTimeScale": 48000,
MajorBrand: 'MP4 Base Media v1 [IS0 14496-12:2003]', "MediaLanguageCode": "und",
MinorVersion: '0.2.0', "HandlerDescription": "Stereo",
CompatibleBrands: [ 'isom', 'iso2', 'avc1', 'mp41' ], "Balance": 0,
MovieDataSize: 0, "AudioFormat": "mp4a",
MovieDataOffset: 1051515, "AudioChannels": 2,
MovieHeaderVersion: 0, "AudioBitsPerSample": 16,
CreateDate: "AudioSampleRate": 48000,
{ year: 1970, "Track2Name": "Stereo",
month: 1, "Track2Title": "Stereo",
day: 8, "HandlerType": "Metadata",
hour: 0, "HandlerVendorID": "Apple",
minute: 0, "Encoder": "HandBrake 1.3.3 2020061300",
second: 0, "ImageSize": "1280x720",
millisecond: 0, "Megapixels": 0.922,
rawValue: '1970:01:08 00:00:00' }, "AvgBitrate": "788 kbps",
ModifyDate: "Rotation": 0
{ year: 2014, },
month: 7, "mediaInfo": {
day: 19, "@ref": "",
hour: 17, "track": [
minute: 15, {
second: 29, "@type": "General",
millisecond: 0, "VideoCount": "1",
rawValue: '2014:07:19 17:15:29' }, "AudioCount": "1",
TimeScale: 1000, "Format": "MPEG-4",
PreferredRate: 1, "Format_Profile": "Base Media",
PreferredVolume: '100.00%', "CodecID": "mp42",
PreviewTime: '0 s', "CodecID_Compatible": "isom/iso2/avc1/mp41",
PosterTime: '0 s', "FileSize": "16965336",
SelectionTime: '0 s', "Duration": "170.902",
CurrentTime: '0 s', "OverallBitRate": "794155",
NextTrackID: 3, "FrameRate": "25.000",
TrackHeaderVersion: 0, "FrameCount": "4271",
TrackCreateDate: '0000:00:00 00:00:00', "StreamSize": "133654",
TrackModifyDate: '0000:00:00 00:00:00', "HeaderSize": "40",
TrackID: 1, "DataSize": "16831690",
TrackLayer: 0, "FooterSize": "133606",
TrackVolume: '0.00%', "IsStreamable": "No",
ImageWidth: 1280, "Encoded_Date": "UTC 2020-12-26 11:28:53",
ImageHeight: 720, "Tagged_Date": "UTC 2020-12-26 11:28:53",
GraphicsMode: 'srcCopy', "Encoded_Application": "HandBrake 1.3.3 2020061300"
OpColor: '0 0 0', },
CompressorID: 'avc1', {
SourceImageWidth: 1280, "@type": "Video",
SourceImageHeight: 720, "StreamOrder": "0",
XResolution: 72, "ID": "1",
YResolution: 72, "Format": "AVC",
BitDepth: 24, "Format_Profile": "Main",
VideoFrameRate: 25, "Format_Level": "4",
MatrixStructure: '1 0 0 0 1 0 0 0 1', "Format_Settings_CABAC": "Yes",
MediaHeaderVersion: 0, "Format_Settings_RefFrames": "4",
MediaCreateDate: '0000:00:00 00:00:00', "CodecID": "avc1",
MediaModifyDate: '0000:00:00 00:00:00', "Duration": "170.861",
MediaTimeScale: 48000, "BitRate": "627225",
MediaLanguageCode: 'und', "Width": "1280",
HandlerDescription: 'SoundHandler', "Height": "720",
Balance: 0, "Sampled_Width": "1280",
AudioFormat: 'mp4a', "Sampled_Height": "720",
AudioChannels: 2, "PixelAspectRatio": "1.000",
AudioBitsPerSample: 16, "DisplayAspectRatio": "1.778",
AudioSampleRate: 48000, "Rotation": "0.000",
HandlerType: 'Metadata', "FrameRate_Mode": "VFR",
HandlerVendorID: 'Apple', "FrameRate": "25.000",
Encoder: 'Lavf53.24.2', "FrameRate_Minimum": "16.393",
Title: 'Sample title test', "FrameRate_Maximum": "25.000",
Composer: 'th', "FrameCount": "4271",
BeatsPerMinute: '', "ColorSpace": "YUV",
ContentCreateDate: 2018, "ChromaSubsampling": "4:2:0",
Genre: 'this', "BitDepth": "8",
Artist: 'hhj', "ScanType": "Progressive",
Comment: 'hhk', "StreamSize": "13394380",
Subtitle: 'jj', "Encoded_Library": "x264 - core 157 r2935 545de2f",
Mood: 'lik', "Encoded_Library_Name": "x264",
ContentDistributor: 'cont', "Encoded_Library_Version": "core 157 r2935 545de2f",
Conductor: 'jo', "Encoded_Library_Settings": "cabac=1 / ref=1 / deblock=1:0:0 / analyse=0x1:0x111 / me=hex / subme=2 / psy=1 / psy_rd=1.00:0.00 / mixed_ref=0 / me_range=16 / chroma_me=1 / trellis=0 / 8x8dct=0 / cqm=0 / deadzone=21,11 / fast_pskip=1 / chroma_qp_offset=0 / threads=22 / lookahead_threads=5 / sliced_threads=0 / nr=0 / decimate=1 / interlaced=0 / bluray_compat=0 / constrained_intra=0 / bframes=3 / b_pyramid=2 / b_adapt=1 / b_bias=0 / direct=1 / weightb=1 / open_gop=0 / weightp=1 / keyint=250 / keyint_min=25 / scenecut=40 / intra_refresh=0 / rc_lookahead=10 / rc=crf / mbtree=1 / crf=24.0 / qcomp=0.60 / qpmin=0 / qpmax=69 / qpstep=4 / vbv_maxrate=20000 / vbv_bufsize=25000 / crf_max=0.0 / nal_hrd=none / filler=0 / ip_ratio=1.40 / aq=1:1.00",
Writer: 'writ', "Encoded_Date": "UTC 2020-12-26 11:28:53",
InitialKey: 'ho', "Tagged_Date": "UTC 2020-12-26 11:28:53",
Producer: 'prod', "colour_description_present": "Yes",
ParentalRating: 'par', "colour_description_present_Source": "Stream",
Director: 'dir', "colour_range": "Limited",
Period: 'pol', "colour_range_Source": "Stream",
Publisher: 'pub', "colour_primaries": "BT.709",
PromotionURL: 'prom', "colour_primaries_Source": "Stream",
AuthorURL: 'auth', "transfer_characteristics": "BT.709",
EncodedBy: 'enc', "transfer_characteristics_Source": "Stream",
Category: 'h', "matrix_coefficients": "BT.709",
ImageSize: '1280x720', "matrix_coefficients_Source": "Stream",
Megapixels: 0.922, "extra": {
AvgBitrate: '1.58 Mbps', "CodecConfigurationBox": "avcC"
Rotation: 0 }, }
processingStatus: false, },
video_codec_name: 'h264', {
video_resolution: '720p' } "@type": "Audio",
"StreamOrder": "1",
"ID": "2",
"Format": "AAC",
"Format_Settings_SBR": "No (Explicit)",
"Format_AdditionalFeatures": "LC",
"CodecID": "mp4a-40-2",
"Duration": "170.902",
"BitRate_Mode": "CBR",
"BitRate": "160902",
"Channels": "2",
"ChannelPositions": "Front: L R",
"ChannelLayout": "L R",
"SamplesPerFrame": "1024",
"SamplingRate": "48000",
"SamplingCount": "8203296",
"FrameRate": "46.875",
"FrameCount": "8011",
"Compression_Mode": "Lossy",
"StreamSize": "3437302",
"StreamSize_Proportion": "0.20261",
"Title": "Stereo",
"Default": "Yes",
"AlternateGroup": "1",
"Encoded_Date": "UTC 2020-12-26 11:28:53",
"Tagged_Date": "UTC 2020-12-26 11:28:53"
}
]
},
"hasClosedCaptions": false,
"container": "mp4",
"ffProbeRead": "success",
"ffProbeData": {
"streams": [
{
"index": 0,
"codec_name": "h264",
"codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"profile": "Main",
"codec_type": "video",
"codec_time_base": "170861/8542000",
"codec_tag_string": "avc1",
"codec_tag": "0x31637661",
"width": 1280,
"height": 720,
"coded_width": 1280,
"coded_height": 720,
"has_b_frames": 2,
"sample_aspect_ratio": "1:1",
"display_aspect_ratio": "16:9",
"pix_fmt": "yuv420p",
"level": 40,
"color_range": "tv",
"color_space": "bt709",
"color_transfer": "bt709",
"color_primaries": "bt709",
"chroma_location": "left",
"refs": 1,
"is_avc": "true",
"nal_length_size": "4",
"r_frame_rate": "50/1",
"avg_frame_rate": "4271000/170861",
"time_base": "1/90000",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 15377490,
"duration": "170.861000",
"bit_rate": "627147",
"bits_per_raw_sample": "8",
"nb_frames": "4271",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0,
"timed_thumbnails": 0
},
"tags": {
"creation_time": "2020-12-26T11:28:53.000000Z",
"language": "und",
"handler_name": "VideoHandler"
}
},
{
"index": 1,
"codec_name": "aac",
"codec_long_name": "AAC (Advanced Audio Coding)",
"profile": "LC",
"codec_type": "audio",
"codec_time_base": "1/48000",
"codec_tag_string": "mp4a",
"codec_tag": "0x6134706d",
"sample_fmt": "fltp",
"sample_rate": "48000",
"channels": 2,
"channel_layout": "stereo",
"bits_per_sample": 0,
"r_frame_rate": "0/0",
"avg_frame_rate": "0/0",
"time_base": "1/48000",
"start_pts": 0,
"start_time": "0.000000",
"duration_ts": 8202240,
"duration": "170.880000",
"bit_rate": "160902",
"max_bit_rate": "160902",
"nb_frames": "8011",
"disposition": {
"default": 1,
"dub": 0,
"original": 0,
"comment": 0,
"lyrics": 0,
"karaoke": 0,
"forced": 0,
"hearing_impaired": 0,
"visual_impaired": 0,
"clean_effects": 0,
"attached_pic": 0,
"timed_thumbnails": 0
},
"tags": {
"creation_time": "2020-12-26T11:28:53.000000Z",
"language": "und",
"handler_name": "Stereo"
}
}
]
},
"file_size": 16.179405212402344,
"bit_rate": 794155.0596248143,
"video_resolution": "720p",
"fileMedium": "video",
"video_codec_name": "h264",
"_id": "C:/Users/H/Desktop/Transcode/Source/SampleVideo_1280x720_30mb - Copy (5).mp4",
"file": "C:/Users/H/Desktop/Transcode/Source/SampleVideo_1280x720_30mb - Copy (5).mp4",
"DB": "WratRWZpe",
"lastPluginDetails": "none",
"processingStatus": false,
"createdAt": "2020-12-27T10:42:55.642Z",
"statSync": {
"dev": 3832468976,
"mode": 33206,
"nlink": 1,
"uid": 0,
"gid": 0,
"rdev": 0,
"blksize": 4096,
"ino": 5066549580826442,
"size": 16965336,
"blocks": 33136,
"atimeMs": 1609065774191.6953,
"mtimeMs": 1608982151506.065,
"ctimeMs": 1608982164201.0798,
"birthtimeMs": 1608982162081.075,
"atime": "2020-12-27T10:42:54.192Z",
"mtime": "2020-12-26T11:29:11.506Z",
"ctime": "2020-12-26T11:29:24.201Z",
"birthtime": "2020-12-26T11:29:22.081Z"
},
"history": ""
}

View file

@ -1,19 +1,24 @@
// List any npm dependencies which the plugin needs, they will be auto installed when the plugin runs:
module.exports.dependencies = [
'import-fresh',
];
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_aaaa_Pre_Proc_Example", id: 'Tdarr_Plugin_aaaa_Pre_Proc_Example',
Stage: "Pre-processing", //Preprocessing or Post-processing. Determines when the plugin will be executed. Stage: 'Pre-processing', // Preprocessing or Post-processing. Determines when the plugin will be executed.
Name: "No title meta data ", Name: 'No title meta data ',
Type: "Video", Type: 'Video',
Operation: "Transcode", Operation: 'Transcode',
Description: `This plugin removes metadata (if a title exists). The output container is the same as the original. \n\n`, Description: 'This plugin removes metadata (if a title exists). The output container is the same as the original. \n\n',
Version: "1.00", Version: '1.00',
Link: "https://github.com/HaveAGitGat/Tdarr_Plugin_aaaa_Pre_Proc_Example", Link: 'https://github.com/HaveAGitGat/Tdarr_Plugin_aaaa_Pre_Proc_Example',
Tags: "ffmpeg,h265", //Provide tags to categorise your plugin in the plugin browser.Tag options: h265,hevc,h264,nvenc h265,nvenc h264,video only,audio only,subtitle only,handbrake,ffmpeg,radarr,sonarr,pre-processing,post-processing,configurable Tags: 'ffmpeg,h265', // Provide tags to categorise your plugin in the plugin browser.Tag options: h265,hevc,h264,nvenc h265,nvenc h264,video only,audio only,subtitle only,handbrake,ffmpeg,radarr,sonarr,pre-processing,post-processing,configurable
Inputs: [ Inputs: [
//(Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI // (Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI
{ {
name: "language", name: 'language',
tooltip: `Enter one language tag here for the language of the subtitles you'd like to keep. tooltip: `Enter one language tag here for the language of the subtitles you'd like to keep.
\\nExample:\\n \\nExample:\\n
@ -25,10 +30,10 @@ module.exports.details = function details() {
\\nExample:\\n \\nExample:\\n
de`, //Each line following `Example:` will be clearly formatted. \\n used for line breaks de`, // Each line following `Example:` will be clearly formatted. \\n used for line breaks
}, },
{ {
name: "channels", name: 'channels',
tooltip: `Desired audio channel number. tooltip: `Desired audio channel number.
\\nExample:\\n \\nExample:\\n
@ -39,67 +44,69 @@ module.exports.details = function details() {
}; };
module.exports.plugin = function plugin(file, librarySettings, inputs) { module.exports.plugin = function plugin(file, librarySettings, inputs) {
//Must return this object at some point in the function else plugin will fail. // Only 'require' dependencies within this function or other functions. Do not require in the top scope.
const importFresh = require('import-fresh');
var response = { // Must return this object at some point in the function else plugin will fail.
processFile: false, //If set to false, the file will be skipped. Set to true to have the file transcoded.
preset: "", //HandBrake/FFmpeg CLI arguments you'd like to use. const response = {
//For FFmpeg, the input arguments come first followed by <io>, followed by the output argument. processFile: false, // If set to false, the file will be skipped. Set to true to have the file transcoded.
preset: '', // HandBrake/FFmpeg CLI arguments you'd like to use.
// For FFmpeg, the input arguments come first followed by <io>, followed by the output argument.
// Examples // Examples
//HandBrake // HandBrake
// '-Z "Very Fast 1080p30"' // '-Z "Very Fast 1080p30"'
//FFmpeg // FFmpeg
// '-sn <io> -map_metadata -1 -c:v copy -c:a copy' // '-sn <io> -map_metadata -1 -c:v copy -c:a copy'
container: ".mp4", // The container of the transcoded output file. container: '.mp4', // The container of the transcoded output file.
handBrakeMode: false, //Set whether to use HandBrake or FFmpeg for transcoding handBrakeMode: false, // Set whether to use HandBrake or FFmpeg for transcoding
FFmpegMode: false, FFmpegMode: false,
reQueueAfter: true, //Leave as true. File will be re-qeued afterwards and pass through the plugin filter again to make sure it meets conditions. reQueueAfter: true, // Leave as true. File will be re-qeued afterwards and pass through the plugin filter again to make sure it meets conditions.
infoLog: "", //This will be shown when the user clicks the 'i' (info) button on a file in the output queue if infoLog: '', // This will be shown when the user clicks the 'i' (info) button on a file in the output queue if
//it has been skipped. // it has been skipped.
// Give reasons why it has been skipped ('File has no title metadata, File meets conditions!') // Give reasons why it has been skipped ('File has no title metadata, File meets conditions!')
//Optional (include together) // Optional (include together)
file, file,
removeFromDB: false, //Tell Tdarr to remove file from database if true removeFromDB: false, // Tell Tdarr to remove file from database if true
updateDB: false, //Change file object above and update database if true updateDB: false, // Change file object above and update database if true
}; };
console.log(inputs.language); //eng if user entered 'eng' in input box in Tdarr plugin UI console.log(inputs.language); // eng if user entered 'eng' in input box in Tdarr plugin UI
console.log(inputs.channels); //2 if user entered '2' in input box in Tdarr plugin UI console.log(inputs.channels); // 2 if user entered '2' in input box in Tdarr plugin UI
//Here we specify that we want the output file container to be the same as the current container. // Here we specify that we want the output file container to be the same as the current container.
response.container = "." + file.container; response.container = `.${file.container}`;
//We will use FFmpeg for this procedure. // We will use FFmpeg for this procedure.
response.FFmpegMode = true; response.FFmpegMode = true;
//Check if file has title metadata // Check if file has title metadata
if (file.meta.Title != undefined) { if (file.meta.Title != undefined) {
//if so, remove it // if so, remove it
response.infoLog += " File has title metadata"; response.infoLog += ' File has title metadata';
response.preset = ",-map_metadata -1 -c:v copy -c:a copy"; response.preset = ',-map_metadata -1 -c:v copy -c:a copy';
response.processFile = true; response.processFile = true;
return response; return response;
} else {
response.infoLog += " File has no title metadata";
} }
response.infoLog += ' File has no title metadata';
response.infoLog += " File meets conditions!"; response.infoLog += ' File meets conditions!';
return response; return response;
}; };
module.exports.onTranscodeSuccess = function onTranscodeSuccess( module.exports.onTranscodeSuccess = function onTranscodeSuccess(
file, file,
librarySettings, librarySettings,
inputs inputs,
) { ) {
console.log( console.log(
"Transcode success! Now do some stuff with the newly scanned file." 'Transcode success! Now do some stuff with the newly scanned file.',
); );
//Optional response if you need to modify database // Optional response if you need to modify database
var response = { const response = {
file, file,
removeFromDB: false, removeFromDB: false,
updateDB: false, updateDB: false,
@ -111,12 +118,12 @@ module.exports.onTranscodeSuccess = function onTranscodeSuccess(
module.exports.onTranscodeError = function onTranscodeError( module.exports.onTranscodeError = function onTranscodeError(
file, file,
librarySettings, librarySettings,
inputs inputs,
) { ) {
console.log("Transcode fail! Now do some stuff with the original file."); console.log('Transcode fail! Now do some stuff with the original file.');
//Optional response if you need to modify database // Optional response if you need to modify database
var response = { const response = {
file, file,
removeFromDB: false, removeFromDB: false,
updateDB: false, updateDB: false,
@ -125,7 +132,7 @@ module.exports.onTranscodeError = function onTranscodeError(
return response; return response;
}; };
//Example file object: // Example file object:
// { // {
// _id: 'C:/Users/H/Desktop/Test Input1/Sample.mp4', // _id: 'C:/Users/H/Desktop/Test Input1/Sample.mp4',
// DB: 'ZRPDmnmpyuAEQi7nG', // DB: 'ZRPDmnmpyuAEQi7nG',

View file

@ -0,0 +1,36 @@
module.exports.details = function details() {
return {
id: 'Tdarr_Plugin_bbbb_Filter_Example',
Stage: 'Pre-processing',
Name: 'Filter keywords ',
Type: 'Video',
Operation: 'Filter',
Description: 'This plugin prevents processing files which contain keywords \n\n',
Version: '1.00',
Link: 'https://github.com/HaveAGitGat/Tdarr_Plugin_bbbb_Filter_Example.js',
Tags: '',
};
};
module.exports.plugin = function plugin(file) {
// Must return this object at some point in the function else plugin will fail.
const response = {
processFile: true,
infoLog: '',
};
const keywords = [
'Low quality',
];
for (let i = 0; i < keywords.length; i += 1) {
if (file.file.includes(keywords[i])) {
response.processFile = false;
response.infoLog += `Filter preventing processing. File title contains keyword ${keywords[i]}`;
break;
}
}
return response;
};

View file

@ -1,19 +1,24 @@
// List any npm dependencies which the plugin needs, they will be auto installed when the plugin runs:
module.exports.dependencies = [
'import-fresh',
];
module.exports.details = function details() { module.exports.details = function details() {
return { return {
id: "Tdarr_Plugin_zzzz_Post_Proc_Example", id: 'Tdarr_Plugin_zzzz_Post_Proc_Example',
Stage: "Post-processing", //Preprocessing or Post-processing. Determines when the plugin will be executed. This plugin does some stuff after all plugins have been executed Stage: 'Post-processing', // Preprocessing or Post-processing. Determines when the plugin will be executed. This plugin does some stuff after all plugins have been executed
Name: "Post proc ", Name: 'Post proc ',
Type: "Video", Type: 'Video',
Operation: "", Operation: '',
Description: `This plugin does some stuff after all plugins have been executed. \n\n`, Description: 'This plugin does some stuff after all plugins have been executed. \n\n',
Version: "1.00", Version: '1.00',
Link: "https://github.com/HaveAGitGat/Tdarr_Plugin_aaaa_Post_Proc_Example", Link: 'https://github.com/HaveAGitGat/Tdarr_Plugin_aaaa_Post_Proc_Example',
Tags: "ffmpeg,h265", //Provide tags to categorise your plugin in the plugin browser.Tag options: h265,hevc,h264,nvenc h265,nvenc h264,video only,audio only,subtitle only,handbrake,ffmpeg,radarr,sonarr,pre-processing,post-processing,configurable Tags: 'ffmpeg,h265', // Provide tags to categorise your plugin in the plugin browser.Tag options: h265,hevc,h264,nvenc h265,nvenc h264,video only,audio only,subtitle only,handbrake,ffmpeg,radarr,sonarr,pre-processing,post-processing,configurable
Inputs: [ Inputs: [
//(Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI // (Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI
{ {
name: "language", name: 'language',
tooltip: `Enter one language tag here for the language of the subtitles you'd like to keep. tooltip: `Enter one language tag here for the language of the subtitles you'd like to keep.
\\nExample:\\n \\nExample:\\n
@ -23,10 +28,10 @@ module.exports.details = function details() {
fr fr
\\nExample:\\n \\nExample:\\n
de`, //Each line following `Example:` will be clearly formatted. \\n used for line breaks de`, // Each line following `Example:` will be clearly formatted. \\n used for line breaks
}, },
{ {
name: "channels", name: 'channels',
tooltip: `Desired audio channel number. tooltip: `Desired audio channel number.
\\nExample:\\n \\nExample:\\n
@ -37,12 +42,15 @@ module.exports.details = function details() {
}; };
module.exports.plugin = function plugin(file, librarySettings, inputs) { module.exports.plugin = function plugin(file, librarySettings, inputs) {
// Only 'require' dependencies within this function or other functions. Do not require in the top scope.
const importFresh = require('import-fresh');
console.log( console.log(
"Transcode success! Now do some stuff with the newly scanned file." 'Transcode success! Now do some stuff with the newly scanned file.',
); );
//Optional response if you need to modify database // Optional response if you need to modify database
var response = { const response = {
file, file,
removeFromDB: false, removeFromDB: false,
updateDB: false, updateDB: false,
@ -51,7 +59,7 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
return response; return response;
}; };
//Example file object: // Example file object:
// { // {
// _id: 'C:/Users/H/Desktop/Test Input1/Sample.mp4', // _id: 'C:/Users/H/Desktop/Test Input1/Sample.mp4',
// DB: 'ZRPDmnmpyuAEQi7nG', // DB: 'ZRPDmnmpyuAEQi7nG',

View file

@ -1,21 +1,25 @@
var fs = require("fs"); const fs = require('fs');
var path = require("path"); const path = require('path');
if (fs.existsSync(path.join(process.cwd(), "/npm"))) {
var rootModules = path.join(process.cwd(), "/npm/node_modules/"); let rootModules;
if (fs.existsSync(path.join(process.cwd(), '/npm'))) {
rootModules = path.join(process.cwd(), '/npm/node_modules/');
} else { } else {
var rootModules = ""; rootModules = '';
} }
const importFresh = require(rootModules + "import-fresh");
// eslint-disable-next-line import/no-dynamic-require
const importFresh = require(`${rootModules}import-fresh`);
module.exports.remuxContainer = importFresh( module.exports.remuxContainer = importFresh(
"./library/actions/remuxContainer.js" './library/actions/remuxContainer.js',
); );
module.exports.transcodeStandardiseAudioCodecs = importFresh( module.exports.transcodeStandardiseAudioCodecs = importFresh(
"./library/actions/transcodeStandardiseAudioCodecs.js" './library/actions/transcodeStandardiseAudioCodecs.js',
); );
module.exports.transcodeAddAudioStream = importFresh( module.exports.transcodeAddAudioStream = importFresh(
"./library/actions/transcodeAddAudioStream.js" './library/actions/transcodeAddAudioStream.js',
); );
module.exports.transcodeKeepOneAudioStream = importFresh( module.exports.transcodeKeepOneAudioStream = importFresh(
"./library/actions/transcodeKeepOneAudioStream.js" './library/actions/transcodeKeepOneAudioStream.js',
); );

View file

@ -1,3 +1,4 @@
/* eslint-disable */
var fs = require("fs"); var fs = require("fs");
var path = require("path"); var path = require("path");
if (fs.existsSync(path.join(process.cwd(), "/npm"))) { if (fs.existsSync(path.join(process.cwd(), "/npm"))) {

View file

@ -1,3 +1,4 @@
/* eslint-disable */
var fs = require("fs"); var fs = require("fs");
var path = require("path"); var path = require("path");
if (fs.existsSync(path.join(process.cwd(), "/npm"))) { if (fs.existsSync(path.join(process.cwd(), "/npm"))) {

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function remuxContainer(file, container) { function remuxContainer(file, container) {
try { try {
if (file.container != container) { if (file.container != container) {

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports = function transcodeAddAudioStream( module.exports = function transcodeAddAudioStream(
file, file,
audioEncoder, audioEncoder,

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports = function transcodeKeepOneAudioStream( module.exports = function transcodeKeepOneAudioStream(
file, file,
audioEncoder, audioEncoder,

View file

@ -1,3 +1,4 @@
/* eslint-disable */
module.exports = function transcodeStandardiseAudioCodecs(file, audioEncoder) { module.exports = function transcodeStandardiseAudioCodecs(file, audioEncoder) {
//Function required responses //Function required responses
// preset // preset

View file

@ -1,24 +1,24 @@
function filterByAge(file, ageCutOff_Seconds) { function filterByAge(file, ageCutOff_Seconds, type) {
try { try {
var timeNow = new Date(); const timeNow = new Date();
var dateCreated = new Date(file.statSync.birthtime); const dateCreated = new Date(file.statSync.birthtime);
var fileAge = Math.round((timeNow - dateCreated) / 1000); const fileAge = Math.round((timeNow - dateCreated) / 1000);
if (fileAge > ageCutOff_Seconds) { if ((type === 'exclude' && fileAge > ageCutOff_Seconds) || (type === 'include' && fileAge < ageCutOff_Seconds)) {
var response = { const response = {
outcome: false, outcome: false,
note: `☒File creation date is older than specified requirement. \n`, note: 'File creation date is not within specified requirement. Wont process. \n',
};
return response;
} else {
var response = {
outcome: true,
note: `☑File creation date is within specified requirement. \n`,
}; };
return response; return response;
} }
const response = {
outcome: true,
note: 'File creation date is within specified requirement. Will process. \n',
};
return response;
} catch (err) { } catch (err) {
var response = { const response = {
outcome: false, outcome: false,
note: `library.filters.filterByAge error: ${err} \n`, note: `library.filters.filterByAge error: ${err} \n`,
}; };

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function filterByCodec(file, mode, codecs) { function filterByCodec(file, mode, codecs) {
try { try {
// console.log(file,mode,codecs) // console.log(file,mode,codecs)

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function filterByMedium(file, medium) { function filterByMedium(file, medium) {
try { try {
if (file.fileMedium !== medium) { if (file.fileMedium !== medium) {

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function filterByResolution(file, mode, resolution) { function filterByResolution(file, mode, resolution) {
try { try {
if (mode === "exclude") { if (mode === "exclude") {

View file

@ -1,3 +1,4 @@
/* eslint-disable */
function filterBySize(file, lowerBound, upperBound) { function filterBySize(file, lowerBound, upperBound) {
try { try {
if ( if (

15
node_modules/.bin/prettier generated vendored
View file

@ -1,15 +0,0 @@
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*|*MINGW*|*MSYS*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../prettier/bin-prettier.js" "$@"
ret=$?
else
node "$basedir/../prettier/bin-prettier.js" "$@"
ret=$?
fi
exit $ret

17
node_modules/.bin/prettier.cmd generated vendored
View file

@ -1,17 +0,0 @@
@ECHO off
SETLOCAL
CALL :find_dp0
IF EXIST "%dp0%\node.exe" (
SET "_prog=%dp0%\node.exe"
) ELSE (
SET "_prog=node"
SET PATHEXT=%PATHEXT:;.JS;=;%
)
"%_prog%" "%dp0%\..\prettier\bin-prettier.js" %*
ENDLOCAL
EXIT /b %errorlevel%
:find_dp0
SET dp0=%~dp0
EXIT /b

18
node_modules/.bin/prettier.ps1 generated vendored
View file

@ -1,18 +0,0 @@
#!/usr/bin/env pwsh
$basedir=Split-Path $MyInvocation.MyCommand.Definition -Parent
$exe=""
if ($PSVersionTable.PSVersion -lt "6.0" -or $IsWindows) {
# Fix case when both the Windows and Linux builds of Node
# are installed in the same directory
$exe=".exe"
}
$ret=0
if (Test-Path "$basedir/node$exe") {
& "$basedir/node$exe" "$basedir/../prettier/bin-prettier.js" $args
$ret=$LASTEXITCODE
} else {
& "node$exe" "$basedir/../prettier/bin-prettier.js" $args
$ret=$LASTEXITCODE
}
exit $ret

96
node_modules/callsites/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,96 @@
declare namespace callsites {
interface CallSite {
/**
Returns the value of `this`.
*/
getThis(): unknown | undefined;
/**
Returns the type of `this` as a string. This is the name of the function stored in the constructor field of `this`, if available, otherwise the object's `[[Class]]` internal property.
*/
getTypeName(): string | null;
/**
Returns the current function.
*/
getFunction(): Function | undefined;
/**
Returns the name of the current function, typically its `name` property. If a name property is not available an attempt will be made to try to infer a name from the function's context.
*/
getFunctionName(): string | null;
/**
Returns the name of the property of `this` or one of its prototypes that holds the current function.
*/
getMethodName(): string | undefined;
/**
Returns the name of the script if this function was defined in a script.
*/
getFileName(): string | null;
/**
Returns the current line number if this function was defined in a script.
*/
getLineNumber(): number | null;
/**
Returns the current column number if this function was defined in a script.
*/
getColumnNumber(): number | null;
/**
Returns a string representing the location where `eval` was called if this function was created using a call to `eval`.
*/
getEvalOrigin(): string | undefined;
/**
Returns `true` if this is a top-level invocation, that is, if it's a global object.
*/
isToplevel(): boolean;
/**
Returns `true` if this call takes place in code defined by a call to `eval`.
*/
isEval(): boolean;
/**
Returns `true` if this call is in native V8 code.
*/
isNative(): boolean;
/**
Returns `true` if this is a constructor call.
*/
isConstructor(): boolean;
}
}
declare const callsites: {
/**
Get callsites from the V8 stack trace API.
@returns An array of `CallSite` objects.
@example
```
import callsites = require('callsites');
function unicorn() {
console.log(callsites()[0].getFileName());
//=> '/Users/sindresorhus/dev/callsites/test.js'
}
unicorn();
```
*/
(): callsites.CallSite[];
// TODO: Remove this for the next major release, refactor the whole definition to:
// declare function callsites(): callsites.CallSite[];
// export = callsites;
default: typeof callsites;
};
export = callsites;

13
node_modules/callsites/index.js generated vendored Normal file
View file

@ -0,0 +1,13 @@
'use strict';
const callsites = () => {
const _prepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = (_, stack) => stack;
const stack = new Error().stack.slice(1);
Error.prepareStackTrace = _prepareStackTrace;
return stack;
};
module.exports = callsites;
// TODO: Remove this for the next major release
module.exports.default = callsites;

View file

@ -1,4 +1,6 @@
Copyright © James Long and contributors MIT License
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

71
node_modules/callsites/package.json generated vendored Normal file
View file

@ -0,0 +1,71 @@
{
"_from": "callsites@^3.0.0",
"_id": "callsites@3.1.0",
"_inBundle": false,
"_integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"_location": "/callsites",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "callsites@^3.0.0",
"name": "callsites",
"escapedName": "callsites",
"rawSpec": "^3.0.0",
"saveSpec": null,
"fetchSpec": "^3.0.0"
},
"_requiredBy": [
"/parent-module"
],
"_resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
"_shasum": "b3630abd8943432f54b3f0519238e33cd7df2f73",
"_spec": "callsites@^3.0.0",
"_where": "C:\\Users\\H\\Documents\\GitHub\\tdarr_express_node\\assets\\app\\plugins\\node_modules\\parent-module",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"bugs": {
"url": "https://github.com/sindresorhus/callsites/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Get callsites from the V8 stack trace API",
"devDependencies": {
"ava": "^1.4.1",
"tsd": "^0.7.2",
"xo": "^0.24.0"
},
"engines": {
"node": ">=6"
},
"files": [
"index.js",
"index.d.ts"
],
"homepage": "https://github.com/sindresorhus/callsites#readme",
"keywords": [
"stacktrace",
"v8",
"callsite",
"callsites",
"stack",
"trace",
"function",
"file",
"line",
"debug"
],
"license": "MIT",
"name": "callsites",
"repository": {
"type": "git",
"url": "git+https://github.com/sindresorhus/callsites.git"
},
"scripts": {
"test": "xo && ava && tsd"
},
"version": "3.1.0"
}

48
node_modules/callsites/readme.md generated vendored Normal file
View file

@ -0,0 +1,48 @@
# callsites [![Build Status](https://travis-ci.org/sindresorhus/callsites.svg?branch=master)](https://travis-ci.org/sindresorhus/callsites)
> Get callsites from the [V8 stack trace API](https://v8.dev/docs/stack-trace-api)
## Install
```
$ npm install callsites
```
## Usage
```js
const callsites = require('callsites');
function unicorn() {
console.log(callsites()[0].getFileName());
//=> '/Users/sindresorhus/dev/callsites/test.js'
}
unicorn();
```
## API
Returns an array of callsite objects with the following methods:
- `getThis`: returns the value of `this`.
- `getTypeName`: returns the type of `this` as a string. This is the name of the function stored in the constructor field of `this`, if available, otherwise the object's `[[Class]]` internal property.
- `getFunction`: returns the current function.
- `getFunctionName`: returns the name of the current function, typically its `name` property. If a name property is not available an attempt will be made to try to infer a name from the function's context.
- `getMethodName`: returns the name of the property of `this` or one of its prototypes that holds the current function.
- `getFileName`: if this function was defined in a script returns the name of the script.
- `getLineNumber`: if this function was defined in a script returns the current line number.
- `getColumnNumber`: if this function was defined in a script returns the current column number
- `getEvalOrigin`: if this function was created using a call to `eval` returns a string representing the location where `eval` was called.
- `isToplevel`: is this a top-level invocation, that is, is this the global object?
- `isEval`: does this call take place in code defined by a call to `eval`?
- `isNative`: is this call in native V8 code?
- `isConstructor`: is this a constructor call?
## License
MIT © [Sindre Sorhus](https://sindresorhus.com)

30
node_modules/import-fresh/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,30 @@
/**
Import a module while bypassing the cache.
@example
```
// foo.js
let i = 0;
module.exports = () => ++i;
// index.js
import importFresh = require('import-fresh');
require('./foo')();
//=> 1
require('./foo')();
//=> 2
importFresh('./foo')();
//=> 1
importFresh('./foo')();
//=> 1
const foo = importFresh<typeof import('./foo')>('./foo');
```
*/
declare function importFresh<T>(moduleId: string): T;
export = importFresh;

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