diff --git a/Community/Tdarr_Plugin_ER01_Transcode audio and video with HW (PC and Mac).js b/Community/Tdarr_Plugin_ER01_Transcode audio and video with HW (PC and Mac).js index a0b048e..649c87a 100644 --- a/Community/Tdarr_Plugin_ER01_Transcode audio and video with HW (PC and Mac).js +++ b/Community/Tdarr_Plugin_ER01_Transcode audio and video with HW (PC and Mac).js @@ -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' ) { + 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' ) { + 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' ) { + 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 ) { + 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; } - + 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';} - + outputResolution = '720p'; + } + if (convertVideo === false) { - response.infoLog += `NOT converting video ${file.video_resolution}, ${file.video_codec_name}, bitrate = ${currentBitrate} \n`; + 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`; + 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`; + response.infoLog += `Converting audio, ${audioMessage} ${originalAudio}. \n`; } else { - response.infoLog += `Not converting audio. \n`;} + 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; } diff --git a/Community/Tdarr_Plugin_JB69_JBHEVCQSV_MinimalFile.js b/Community/Tdarr_Plugin_JB69_JBHEVCQSV_MinimalFile.js index 5becd9d..ba47e98 100644 --- a/Community/Tdarr_Plugin_JB69_JBHEVCQSV_MinimalFile.js +++ b/Community/Tdarr_Plugin_JB69_JBHEVCQSV_MinimalFile.js @@ -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�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�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; } } diff --git a/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js b/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js index 2a1abb4..4c44fd6 100644 --- a/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js +++ b/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js @@ -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; diff --git a/Community/Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js b/Community/Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js index cb13d26..3926797 100644 --- a/Community/Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js +++ b/Community/Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js @@ -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]; diff --git a/Community/Tdarr_Plugin_vdka_Tiered_CPU_CRF_Based_Configurable.js b/Community/Tdarr_Plugin_vdka_Tiered_CPU_CRF_Based_Configurable.js index 9334699..7647830 100644 --- a/Community/Tdarr_Plugin_vdka_Tiered_CPU_CRF_Based_Configurable.js +++ b/Community/Tdarr_Plugin_vdka_Tiered_CPU_CRF_Based_Configurable.js @@ -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) { diff --git a/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js b/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js index 730b884..8918a4e 100644 --- a/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js +++ b/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js @@ -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; diff --git a/Community/Tdarr_Plugin_z18s_rename_files_based_on_codec.js b/Community/Tdarr_Plugin_z18s_rename_files_based_on_codec.js index 4c195f9..1d83aed 100644 --- a/Community/Tdarr_Plugin_z18s_rename_files_based_on_codec.js +++ b/Community/Tdarr_Plugin_z18s_rename_files_based_on_codec.js @@ -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, }); diff --git a/Community/Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution.js b/Community/Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution.js index f66635f..6a729bd 100644 --- a/Community/Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution.js +++ b/Community/Tdarr_Plugin_z18t_rename_files_based_on_codec_and_resolution.js @@ -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, }); diff --git a/Tdarr_Plugin_aaaa_Pre_Proc_Example.js b/Tdarr_Plugin_aaaa_Pre_Proc_Example.js index 713db5e..f52129e 100644 --- a/Tdarr_Plugin_aaaa_Pre_Proc_Example.js +++ b/Tdarr_Plugin_aaaa_Pre_Proc_Example.js @@ -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', diff --git a/examples/Tdarr_Plugin_a9he_New_file_size_check.js b/examples/Tdarr_Plugin_a9he_New_file_size_check.js new file mode 100644 index 0000000..15bcee3 --- /dev/null +++ b/examples/Tdarr_Plugin_a9he_New_file_size_check.js @@ -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; +}; diff --git a/examples/filters/Tdarr_Plugin_bbbc_Filter_Example.js b/examples/filters/Tdarr_Plugin_bbbc_Filter_Example.js new file mode 100644 index 0000000..a40f945 --- /dev/null +++ b/examples/filters/Tdarr_Plugin_bbbc_Filter_Example.js @@ -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; +}; diff --git a/package.json b/package.json index 0cf53f2..677416f 100644 --- a/package.json +++ b/package.json @@ -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",