mirror of
https://github.com/gabehf/Tdarr_Plugins.git
synced 2026-03-09 15:38:19 -07:00
Merge branch 'master' into patch-1
This commit is contained in:
commit
67e4f71b43
12 changed files with 358 additions and 281 deletions
|
|
@ -1,5 +1,10 @@
|
|||
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
|
||||
/* eslint-disable */
|
||||
/* eslint max-len: 0 */
|
||||
/* eslint no-bitwise: 0 */
|
||||
/* eslint no-mixed-operators: 0 */
|
||||
|
||||
const os = require('os');
|
||||
function details() {
|
||||
return {
|
||||
id: 'Tdarr_Plugin_ER01_Transcode audio and video with HW (PC and Mac)',
|
||||
|
|
@ -8,10 +13,10 @@ function details() {
|
|||
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.
|
||||
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.`,
|
||||
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: [{
|
||||
|
|
@ -33,7 +38,7 @@ function details() {
|
|||
\\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.
|
||||
|
|
@ -65,7 +70,7 @@ function plugin(file, librarySettings, inputs) {
|
|||
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';
|
||||
|
|
@ -80,23 +85,20 @@ function plugin(file, librarySettings, inputs) {
|
|||
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 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
|
||||
// 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++) {
|
||||
|
|
@ -104,44 +106,50 @@ let videoOptions = `-map 0:v -c:v copy `;
|
|||
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' ) {
|
||||
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 = 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; }
|
||||
|
||||
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;
|
||||
}
|
||||
// Increment videoIdx.
|
||||
videoIdx += 1;
|
||||
}
|
||||
|
||||
// figure out final bitrate
|
||||
// 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') {
|
||||
|
|
@ -169,21 +177,21 @@ let videoOptions = `-map 0:v -c:v copy `;
|
|||
response.infoLog += 'Target bitrate could not be calculated. Skipping this plugin. \n';
|
||||
return response;
|
||||
}
|
||||
|
||||
// Check if inputs.bitrate cutoff has something entered.
|
||||
|
||||
// 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; }
|
||||
convertVideo = false;
|
||||
}
|
||||
}
|
||||
|
||||
// AUDIO SECTION
|
||||
|
||||
// AUDIO SECTION
|
||||
|
||||
// Set up required variables.
|
||||
let audioOptions = `-map 0:a -c:a copy `;
|
||||
// Set up required variables.
|
||||
let audioOptions = '-map 0:a -c:a copy ';
|
||||
let audioIdx = 0;
|
||||
let numberofAudioChannels = 0;
|
||||
let has2Channels = false;
|
||||
|
|
@ -197,197 +205,194 @@ let videoOptions = `-map 0:v -c:v copy `;
|
|||
let type8Channels = '';
|
||||
|
||||
let keepAudioIdx = -1;
|
||||
let keepIGuessAudioIdx = -1;
|
||||
// const 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 === 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();
|
||||
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();
|
||||
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
|
||||
// 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 if (encodeAudioIdx === -1) {
|
||||
// response.infoLog += `Found 6 channel audio in proper language, need to re-encode, audio stream ${audioIdx}\n`;
|
||||
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
|
||||
if (keepAudioIdx === -1 && encodeAudioIdx === -1) { // didn't find any 5.1 or better audio streams in proper language, defaulting to using 2 channels
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
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 if (encodeAudioIdx === -1) {
|
||||
// response.infoLog += `Found 2 channel audio in proper language, need to re-encode, audio stream ${audioIdx}\n`;
|
||||
encodeAudioIdx = audioIdx;
|
||||
encodeAudioStream = i;
|
||||
}
|
||||
} else if (encodeAudioIdx === -1) {
|
||||
// response.infoLog += `Found existing multi-channel audio in proper language, need to re-encode, audio stream ${audioIdx}\n`;
|
||||
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
|
||||
// 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`;}
|
||||
// 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 {
|
||||
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.`;
|
||||
}
|
||||
// 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
|
||||
// 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; }
|
||||
response.infoLog += 'File is processed already, nothing to do';
|
||||
response.processFile = false;
|
||||
return response;
|
||||
}
|
||||
|
||||
// Generate ffmpeg command line arguments in total
|
||||
// Generate ffmpeg command line arguments in total
|
||||
|
||||
// few defaults
|
||||
|
||||
response.preset = `, -sn `;
|
||||
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`;
|
||||
// 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 (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() === '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 `;
|
||||
}
|
||||
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`;
|
||||
outputResolution = '720p';
|
||||
}
|
||||
|
||||
if (convertAudio === true) {
|
||||
response.infoLog += `Converting audio, ${audioMessage} ${originalAudio}. \n`;
|
||||
|
||||
if (convertVideo === false) {
|
||||
response.infoLog += `NOT converting video ${file.video_resolution}, ${file.video_codec_name}, bitrate = ${currentBitrate} \n`;
|
||||
} else {
|
||||
response.infoLog += `Not converting audio. \n`;}
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
// If the source video is less than this rate the script will either:
|
||||
// Copy the existing stream, if the codec is hevc
|
||||
// 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 don’t 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
|
||||
//
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
// If the source audio is less than this rate the script will either:
|
||||
// Copy the existing stream, if the codec is aac
|
||||
// Transcode the stream to aac using 100% of the original streams bitrate
|
||||
// It could probably be less but if the source is of low bitrate but, we don’t 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:
|
||||
// All are removed?? (TODO: ensure this is correct and mention the flag to keep them if desired)
|
||||
|
|
@ -761,7 +761,7 @@ function findMediaInfoItem(file, index) {
|
|||
currMIOrder = file.mediaInfo.track[i].ID - 1;
|
||||
}
|
||||
|
||||
if (currMIOrder == index || currMIOrder == "0-" + index) {
|
||||
if (currMIOrder == index|| currMIOrder == "0-" + index) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ function plugin(file, librarySettings, inputs) {
|
|||
response.preset =
|
||||
", -map_metadata -1 -map 0:V " +
|
||||
subMap +
|
||||
" -map 0:a -c:v libx264 -preset medium -c:a aac -strict -2 " +
|
||||
" -map 0:a -c:v libx264 -preset " + preset + " -c:a aac -strict -2 " +
|
||||
subType;
|
||||
response.reQueueAfter = true;
|
||||
response.processFile = true;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const details = () => ({
|
|||
(requires TMDB api key) and English.
|
||||
'Native' languages are the ones that are listed on imdb. It does an API call to
|
||||
Radarr, Sonarr to check if the movie/series exists and grabs the IMDB id. As a last resort it
|
||||
falls back to '[imdb-ttDIGITS] in the filename.`,
|
||||
falls back to the IMDB id in the filename.`,
|
||||
Version: '1.00',
|
||||
Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/'
|
||||
+ 'Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js',
|
||||
|
|
@ -32,7 +32,7 @@ const details = () => ({
|
|||
},
|
||||
{
|
||||
name: 'api_key',
|
||||
tooltip: 'Input your TMDB api key here. (https://www.themoviedb.org/)',
|
||||
tooltip: 'Input your TMDB api (v3) key here. (https://www.themoviedb.org/)',
|
||||
},
|
||||
{
|
||||
name: 'radarr_api_key',
|
||||
|
|
@ -139,7 +139,7 @@ const tmdbApi = async (filename, api_key, axios) => {
|
|||
if (filename.substr(0, 2) === 'tt') {
|
||||
fileName = filename;
|
||||
} else {
|
||||
const idRegex = /\[imdb-(tt\d*)]/;
|
||||
const idRegex = /(tt\d{7,8})/;
|
||||
const fileMatch = filename.match(idRegex);
|
||||
// eslint-disable-next-line prefer-destructuring
|
||||
if (fileMatch) fileName = fileMatch[1];
|
||||
|
|
|
|||
|
|
@ -72,6 +72,20 @@ function details() {
|
|||
\\nExample:\\n
|
||||
veryfast`,
|
||||
},
|
||||
{
|
||||
name: 'sdDisabled',
|
||||
tooltip: `Input "true" if you want to skip SD (480p and 576p) files
|
||||
|
||||
\\nExample:\\n
|
||||
true`,
|
||||
},
|
||||
{
|
||||
name: 'uhdDisabled',
|
||||
tooltip: `Input "true" if you want to skip 4k (UHD) files
|
||||
|
||||
\\nExample:\\n
|
||||
true`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
|
@ -97,6 +111,22 @@ function plugin(file, librarySettings, inputs) {
|
|||
}
|
||||
response.infoLog += '☑File is a video! \n';
|
||||
|
||||
// check if the file is SD and sdDisable is enabled
|
||||
// skip this plugin if so
|
||||
if (['480p', '576p'].includes(file.video_resolution) && inputs.sdDisabled) {
|
||||
response.processFile = false;
|
||||
response.infoLog += '☒File is SD, not processing\n';
|
||||
return response;
|
||||
}
|
||||
|
||||
// check if the file is 4k and 4kDisable is enabled
|
||||
// skip this plugin if so
|
||||
if (file.video_resolution === '4KUHD' && inputs.uhdDisabled) {
|
||||
response.processFile = false;
|
||||
response.infoLog += '☒File is 4k/UHD, not processing\n';
|
||||
return response;
|
||||
}
|
||||
|
||||
// check if the file is already hevc
|
||||
// it will not be transcoded if true and the plugin will be stopped immediately
|
||||
for (let i = 0; i < file.ffProbeData.streams.length; i += 1) {
|
||||
|
|
|
|||
|
|
@ -1,62 +1,51 @@
|
|||
/* eslint-disable */
|
||||
function details() {
|
||||
return {
|
||||
id: "Tdarr_Plugin_x7ac_Remove_Closed_Captions",
|
||||
Stage: "Pre-processing",
|
||||
Name: "Remove closed captions",
|
||||
Type: "Video",
|
||||
Operation: "Remux",
|
||||
id: 'Tdarr_Plugin_x7ac_Remove_Closed_Captions',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Remove burned closed captions',
|
||||
Type: 'Video',
|
||||
Operation: 'Remux',
|
||||
Description:
|
||||
"[Contains built-in filter] If detected, closed captions (XDS,608,708) will be removed.",
|
||||
Version: "1.00",
|
||||
'[Contains built-in filter] If detected, closed captions (XDS,608,708) will be removed from streams.',
|
||||
Version: '1.01',
|
||||
Link:
|
||||
"https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js",
|
||||
Tags: "pre-processing,ffmpeg,subtitle only",
|
||||
'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js',
|
||||
Tags: 'pre-processing,ffmpeg,subtitle only',
|
||||
};
|
||||
}
|
||||
|
||||
function plugin(file) {
|
||||
//Must return this object
|
||||
|
||||
var response = {
|
||||
const response = {
|
||||
processFile: false,
|
||||
preset: "",
|
||||
container: ".mp4",
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
preset: ',-map 0 -codec copy -bsf:v \"filter_units=remove_types=6\"',
|
||||
container: `.${file.container}`,
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: "",
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
if (file.fileMedium !== "video") {
|
||||
console.log("File is not video");
|
||||
|
||||
response.infoLog += "☒File is not video \n";
|
||||
response.processFile = false;
|
||||
|
||||
if (file.fileMedium !== 'video') {
|
||||
response.infoLog += '☒File is not video \n';
|
||||
return response;
|
||||
} else {
|
||||
if (file.hasClosedCaptions === true) {
|
||||
response = {
|
||||
processFile: true,
|
||||
preset: ',-map 0 -codec copy -bsf:v "filter_units=remove_types=6"',
|
||||
container: "." + file.container,
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: "☒This file has closed captions \n",
|
||||
};
|
||||
|
||||
return response;
|
||||
} else {
|
||||
response.infoLog +=
|
||||
"☑Closed captions have not been detected on this file \n";
|
||||
response.processFile = false;
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check if Closed Captions are set at file level
|
||||
if (file.hasClosedCaptions) {
|
||||
response.processFile = true;
|
||||
response.infoLog += '☒This file has closed captions \n';
|
||||
return response;
|
||||
}
|
||||
// If not, check for Closed Captions in the streams
|
||||
const { streams } = file.ffProbeData;
|
||||
streams.forEach((stream) => {
|
||||
if (stream.closed_captions) {
|
||||
response.processFile = true;
|
||||
}
|
||||
});
|
||||
|
||||
response.infoLog += response.processFile ? '☒This file has burnt closed captions \n'
|
||||
: '☑Closed captions have not been detected on this file \n';
|
||||
return response;
|
||||
}
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,5 @@
|
|||
/* eslint-disable */
|
||||
|
||||
module.exports.dependencies = [
|
||||
'fs-extra',
|
||||
];
|
||||
|
||||
module.exports.details = function details() {
|
||||
return {
|
||||
id: "Tdarr_Plugin_z18s_rename_files_based_on_codec",
|
||||
|
|
@ -11,7 +7,7 @@ module.exports.details = function details() {
|
|||
Name: "Rename based on codec",
|
||||
Type: "Video",
|
||||
Operation: "",
|
||||
Description: `[Contains built-in filter]This plugin renames 264 files to 265 or vice versa depending on codec. \n\n`,
|
||||
Description: `[Contains built-in filter] If the filename contains '264' or '265', this plugin renames 264 files to 265 or vice versa depending on codec. \n\n`,
|
||||
Version: "1.00",
|
||||
Link: "",
|
||||
Tags: "post-processing",
|
||||
|
|
@ -21,15 +17,6 @@ module.exports.details = function details() {
|
|||
module.exports.plugin = function plugin(file, librarySettings, inputs) {
|
||||
try {
|
||||
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 = "";
|
||||
}
|
||||
|
||||
var fsextra = require(rootModules + "fs-extra");
|
||||
|
||||
var fileNameOld = file._id;
|
||||
|
||||
if (
|
||||
|
|
@ -39,6 +26,15 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
|
|||
file._id = file._id.replace("264", "265");
|
||||
file.file = file.file.replace("264", "265");
|
||||
}
|
||||
|
||||
//added handling for files with AVC in the name instead of h264/x264
|
||||
if (
|
||||
file.ffProbeData.streams[0].codec_name == "hevc" &&
|
||||
file._id.includes("AVC")
|
||||
) {
|
||||
file._id = file._id.replace("AVC", "HEVC");
|
||||
file.file = file.file.replace("AVC", "HEVC");
|
||||
}
|
||||
|
||||
if (
|
||||
file.ffProbeData.streams[0].codec_name == "h264" &&
|
||||
|
|
@ -57,7 +53,7 @@ module.exports.plugin = function plugin(file, librarySettings, inputs) {
|
|||
}
|
||||
|
||||
if (fileNameOld != file._id) {
|
||||
fsextra.moveSync(fileNameOld, file._id, {
|
||||
fs.renameSync(fileNameOld, file._id, {
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,3 @@
|
|||
module.exports.dependencies = [
|
||||
'fs-extra',
|
||||
];
|
||||
|
||||
module.exports.details = function details() {
|
||||
return {
|
||||
id: 'Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution',
|
||||
|
|
@ -20,17 +16,6 @@ module.exports.plugin = function plugin(file) {
|
|||
try {
|
||||
// eslint-disable-next-line global-require
|
||||
const fs = require('fs');
|
||||
// eslint-disable-next-line global-require
|
||||
const path = require('path');
|
||||
let rootModules;
|
||||
if (fs.existsSync(path.join(process.cwd(), '/npm'))) {
|
||||
rootModules = path.join(process.cwd(), '/npm/node_modules/');
|
||||
} else {
|
||||
rootModules = '';
|
||||
}
|
||||
|
||||
// eslint-disable-next-line global-require,import/no-dynamic-require
|
||||
const fsextra = require(`${rootModules}fs-extra`);
|
||||
const fileNameOld = file._id;
|
||||
|
||||
const resolutions = {
|
||||
|
|
@ -133,7 +118,7 @@ module.exports.plugin = function plugin(file) {
|
|||
file.file = fileName;
|
||||
|
||||
if (fileNameOld !== file._id) {
|
||||
fsextra.moveSync(fileNameOld, file._id, {
|
||||
fs.renameSync(fileNameOld, file._id, {
|
||||
overwrite: true,
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ module.exports.dependencies = [
|
|||
module.exports.details = function details() {
|
||||
return {
|
||||
id: 'Tdarr_Plugin_aaaa_Pre_Proc_Example',
|
||||
Stage: 'Pre-processing', // Preprocessing or Post-processing. Determines when the plugin will be executed.
|
||||
Stage: 'Pre-processing', // Pre-processing or Post-processing. Determines when the plugin will be executed.
|
||||
Name: 'No title meta data ',
|
||||
Type: 'Video',
|
||||
Operation: 'Transcode',
|
||||
|
|
|
|||
37
examples/Tdarr_Plugin_a9he_New_file_size_check.js
Normal file
37
examples/Tdarr_Plugin_a9he_New_file_size_check.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
module.exports.details = function details() {
|
||||
return {
|
||||
id: 'Tdarr_Plugin_a9he_New_file_size_check',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'New file size check',
|
||||
Type: 'Video',
|
||||
Operation: 'Transcode',
|
||||
Description: 'Give an error if new file is larger than the original \n\n',
|
||||
Version: '1.00',
|
||||
Link: '',
|
||||
Tags: '',
|
||||
};
|
||||
};
|
||||
|
||||
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: false,
|
||||
preset: '',
|
||||
handBrakeMode: false,
|
||||
FFmpegMode: true,
|
||||
reQueueAfter: true,
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
const newSize = file.file_size;
|
||||
const oldSize = otherArguments.originalLibraryFile.file_size;
|
||||
if (newSize > oldSize) {
|
||||
// Item will be errored in UI
|
||||
throw new Error(`Error! New file has size ${newSize} which is larger than original file ${oldSize}`);
|
||||
} else if (newSize < oldSize) {
|
||||
response.infoLog += `New file has size ${newSize} which is smaller than original file ${oldSize}`;
|
||||
}
|
||||
// if file sizes are exactly the same then file has not been transcoded yet
|
||||
|
||||
return response;
|
||||
};
|
||||
35
examples/filters/Tdarr_Plugin_bbbc_Filter_Example.js
Normal file
35
examples/filters/Tdarr_Plugin_bbbc_Filter_Example.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
module.exports.details = function details() {
|
||||
return {
|
||||
id: 'Tdarr_Plugin_bbbc_Filter_Example',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Filter resolutions',
|
||||
Type: 'Video',
|
||||
Operation: 'Filter',
|
||||
Description: 'This plugin prevents processing files with specified resolutions \n\n',
|
||||
Version: '1.00',
|
||||
Link: '',
|
||||
Tags: '',
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.plugin = function plugin(file) {
|
||||
const response = {
|
||||
processFile: true,
|
||||
infoLog: '',
|
||||
};
|
||||
|
||||
const resolutionsToSkip = [
|
||||
'1080p',
|
||||
'4KUHD',
|
||||
];
|
||||
|
||||
for (let i = 0; i < resolutionsToSkip.length; i += 1) {
|
||||
if (file.video_resolution === resolutionsToSkip[i]) {
|
||||
response.processFile = false;
|
||||
response.infoLog += `Filter preventing processing. File has resolution ${resolutionsToSkip[i]}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
};
|
||||
|
|
@ -12,8 +12,8 @@
|
|||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"lint": "eslint Community methods --ext js",
|
||||
"lint:fix": "eslint Community methods --ext js --fix"
|
||||
"lint": "eslint Community methods examples --ext js",
|
||||
"lint:fix": "eslint Community methods examples --ext js --fix"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue