diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..1b182fe --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,10 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..e46a4c0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,10 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + + diff --git a/Community/Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js b/Community/Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js index d50012c..cc69448 100644 --- a/Community/Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js +++ b/Community/Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_075a_FFMPEG_HEVC_Generic", + Stage: "Pre-processing", Name: "FFMPEG H265", Type: "Video", Operation:"Transcode", Description: `[Contains built-in filter] This plugin transcodes non h265 files into h265 mkv using default settings. Audio/subtitles not affected. \n\n`, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_075a_FFMPEG_HEVC_Generic.js", + Tags:'pre-processing,ffmpeg,h265,video only' } } diff --git a/Community/Tdarr_Plugin_075a_Transcode_Customisable.js b/Community/Tdarr_Plugin_075a_Transcode_Customisable.js new file mode 100644 index 0000000..1d8b7f8 --- /dev/null +++ b/Community/Tdarr_Plugin_075a_Transcode_Customisable.js @@ -0,0 +1,208 @@ + + + +module.exports.details = function details() { + + return { + id: "Tdarr_Plugin_075a_Transcode_Customisable", + Stage: "Pre-processing", + Name: "Video Transcode Customisable", + Type: "", + Operation: "Transcode", + Description: `[TESTING][Contains built-in filter] Specify codec filter and transcode arguments for HandBrake or FFmpeg \n\n`, + Version: "1.00", + Link: "", + Tags:'pre-processing,handbrake,ffmpeg,configurable', + Inputs: [ + { + name: 'codecs_to_exclude', + tooltip: `Input codecs, separated by a comma, that should be excluded when processing. + + \\nFor example, if you're transcoding into hevc (h265), then add a filter to prevent hevc being transcoded so your newly transcoded files won't be infinitely looped/processed. \\n + + + \\nCommon video codecs: + + \\nmpeg4 + \\nhevc + \\nh264 + \\nmpeg2video + \\ntheora + \\nvp8 + \\nvp9 + + + \\nExample:\\n + + hevc + + \\nYou can also enter multiple codecs: + + \\nExample:\\n + + mp3,aac,dts + + \\nExample:\\n + + h264,vp9 + + ` + }, + { + name: 'cli', + tooltip: `Enter the CLI to use. + + \\nExample:\\n + handbrake + + \\nExample:\\n + ffmpeg + + ` + }, + { + name: 'transcode_arguments', + tooltip: `\\nEnter HandBrake or FFmpeg transcode arguments. + + \\nHandBrake examples: + + \\nExample:\\n + -e x264 -q 20 -B + + \\nExample:\\n + -Z "Very Fast 1080p30" + + \\nExample:\\n + -Z "Fast 1080p30" -e nvenc_h265 + + \\nExample:\\n + -Z "Very Fast 1080p30" --all-subtitles --all-audio + + \\nExample:\\n + -Z "Very Fast 480p30" + + \\nExample:\\n + --preset-import-file "C:\Users\HaveAGitGat\Desktop\testpreset.json" -Z "My Preset" + + \\nYou can learn more about HandBrake presets here: + + \\nhttps://handbrake.fr/docs/en/latest/technical/official-presets.html + + + \\nWhen using FFmpeg, you need to separate the input and output parameters with a comma. FFmpeg Examples: + + \\nExample:\\n +-r 1,-r 24 + +\\nExample:\\n +,-sn -c:v copy -c:a copy + +\\nExample:\\n +,-c:v lib265 -crf 23 -ac 6 -c:a aac -preset veryfast + +\\nExample:\\n +,-map 0 -c copy -c:v libx265 -c:a aac + +\\nExample:\\n +-c:v h264_cuvid,-c:v hevc_nvenc -preset slow -c:a copy + +\\nPlease see the following for help with creating FFmpeg commands: + +\\nhttps://opensource.com/article/17/6/ffmpeg-convert-media-file-formats + + + + + ` + }, + { + name: 'output_container', + tooltip: ` + \\nEnter the output container of the new file + + \\nExample:\\n + .mp4 + + \\nExample:\\n + .mp3 + + \\nExample:\\n + .mkv + + ` + }, + ] + } + +} + +module.exports.plugin = function plugin(file, librarySettings, inputs) { + + + + //Must return this object + + var response = { + + processFile: false, + preset: '', + container: '.mp4', + handBrakeMode: false, + FFmpegMode: false, + reQueueAfter: false, + infoLog: '', + + } + + if (inputs.codecs_to_exclude === undefined + || inputs.cli === undefined + || inputs.transcode_arguments === undefined + || inputs.output_container === undefined) { + + response.processFile = false + response.infoLog += "☒ Inputs not entered! \n" + return response + + + } + + + if (inputs.codecs_to_exclude.includes(file.ffProbeData.streams[0].codec_name)) { + response.processFile = false + response.infoLog += `☑File is already in ${file.ffProbeData.streams[0].codec_name}! \n` + return response + } + + + + + //transcode settings + + if (inputs.cli == `handbrake`) { + response.handBrakeMode = true + response.FFmpegMode = false + } else if (inputs.cli == `ffmpeg`) { + response.handBrakeMode = false + response.FFmpegMode = true + + } else { + response.processFile = false + response.infoLog += "☒ CLI not input correctly! \n" + return response + + } + + + + response.processFile = true; + response.preset = inputs.transcode_arguments + response.container = inputs.output_container + + response.reQueueAfter = true; + response.infoLog += `☒File is not in desired codec! \n` + return response + + + +} + diff --git a/Community/Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js b/Community/Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js index 5212367..4a5d236 100644 --- a/Community/Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js +++ b/Community/Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only", + Stage: "Pre-processing", Name: "FFMPEG H265 Video + Audio Kept Only", Type: "Video", Operation:"Transcode", Description: `[Contains built-in filter] This plugin transcodes non h265 files into h265 mkv using default settings. Only video and audio streams are kept. Useful for if you're getting errors because of certain containers not being able to handle certain subtitle/data streams. \n\n`, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_075b_FFMPEG_HEVC_Generic_Video_Audio_Only.js", + Tags:'pre-processing,video only,ffmpeg,h265', } } diff --git a/Community/Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20.js b/Community/Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20.js new file mode 100644 index 0000000..955645f --- /dev/null +++ b/Community/Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20.js @@ -0,0 +1,69 @@ + + + +function details() { + + return { + id: "Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20", + Name: "FFMPEG H265 Video + Audio Kept Only With CRF 20", + Type: "Video", + Operation:"Transcode", + Description: `[Contains built-in filter] This plugin transcodes non h265 files into h265 mkv using default settings. Only video and audio streams are kept. Useful for if you're getting errors because of certain containers not being able to handle certain subtitle/data streams. A CRF value of 20 is used. \n\n`, + Version: "1.00", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_075c_FFMPEG_HEVC_Generic_Video_Audio_Only_CRF20.js", + Tags:'pre-processing,video only,ffmpeg,h265', + } + +} + +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") { + response.processFile = false + response.infoLog += "☒File is not a video! \n" + return response + } else { + response.infoLog += "☑File is a video! \n" + } + + + if (file.ffProbeData.streams[0].codec_name == 'hevc') { + response.processFile = false + response.infoLog += "☑File is already in hevc! \n" + return response + } + + + + response.processFile = true; + response.preset = `,-map 0:v -map 0:a -c copy -c:v:0 libx265 -crf 20` + response.container = '.mkv' + response.handBrakeMode = false + response.FFmpegMode = true + response.reQueueAfter = true; + response.infoLog += `☒File is not hevc! \n` + return response + + + +} + +module.exports.details = details; + +module.exports.plugin = plugin; + diff --git a/Community/Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20.js b/Community/Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20.js new file mode 100644 index 0000000..2b5a8d6 --- /dev/null +++ b/Community/Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20.js @@ -0,0 +1,69 @@ + + + +function details() { + + return { + id: "Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20", + Name: "FFMPEG H265 Video + Audio Kept Only With CRF 20 On The GPU", + Type: "Video", + Operation:"Transcode", + Description: `[Contains built-in filter] This plugin transcodes non h265 files into h265 mkv using the graphics card and a CRF value of 20. Only video and audio streams are kept. Useful for if you're getting errors because of certain containers not being able to handle certain subtitle/data streams. A CRF value of 20 is used. The GPU (graphics card) is used. \n\n`, + Version: "1.00", + Link: "https://github.com/moodiest/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_075d_FFMPEG_HEVC_GPU_Generic_Video_Audio_Only_CRF20.js", + Tags:'pre-processing,video only,ffmpeg,h265', + } + +} + +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") { + response.processFile = false + response.infoLog += "☒File is not a video! \n" + return response + } else { + response.infoLog += "☑File is a video! \n" + } + + + if (file.ffProbeData.streams[0].codec_name == 'hevc') { + response.processFile = false + response.infoLog += "☑File is already in hevc! \n" + return response + } + + + + response.processFile = true; + response.preset = `,-map 0:v -map 0:a -c copy -c:v:0 hevc_nvenc -crf 20` + response.container = '.mkv' + response.handBrakeMode = false + response.FFmpegMode = true + response.reQueueAfter = true; + response.infoLog += `☒File is not hevc! \n` + return response + + + +} + +module.exports.details = details; + +module.exports.plugin = plugin; + diff --git a/Community/Tdarr_Plugin_076a_re_order_audio_streams.js b/Community/Tdarr_Plugin_076a_re_order_audio_streams.js new file mode 100644 index 0000000..93cdc17 --- /dev/null +++ b/Community/Tdarr_Plugin_076a_re_order_audio_streams.js @@ -0,0 +1,155 @@ + + + +module.exports.details = function details() { + + return { + id: "Tdarr_Plugin_076a_re_order_audio_streams", + Stage: "Pre-processing", + Name: "Re-order audio streams", + Type: "", + Operation: "Transcode", + Description: `[TESTING][Contains built-in filter] Specify a language tag for Tdarr to try and put as 1st audio track \n\n`, + Version: "1.00", + Link: "", + Tags:'pre-processing,audio only,ffmpeg,configurable', + Inputs: [ + { + name: 'preferred_language', + tooltip: `Specify one language tag for Tdarr to try and put as 1st audio track + + \\nExample:\\n + + eng + + \\nExample:\\n + + en + + \\nExample:\\n + + fr + + \\nExample:\\n + + de + + ` + } + ] + } + +} + +module.exports.plugin = function plugin(file, librarySettings, inputs) { + + + + //Must return this object + + var response = { + + processFile: false, + preset: '', + container: '.mp4', + handBrakeMode: false, + FFmpegMode: false, + reQueueAfter: false, + infoLog: '', + + } + + + console.log(inputs.preferred_language) + + if (inputs.preferred_language === undefined) { + + response.processFile = false + response.infoLog += "☒ Inputs not entered! \n" + return response + + + } + + + var desiredTrackPosition = file.ffProbeData.streams.filter(stream => stream.codec_type.toLowerCase() == "video").length + + var audioInLang = file.ffProbeData.streams.filter(stream => { + + + if (stream.codec_type.toLowerCase() == "audio" + && stream.tags && stream.tags.language && inputs.preferred_language.includes(stream.tags.language.toLowerCase())) { + return true + } + + return false + + }) + + + if (audioInLang.length == 0) { + + response.processFile = false + response.infoLog += "☒ No audio tracks in desired language! \n" + return response + + } + + + var streamToMove = audioInLang[0] + + if (streamToMove.index == desiredTrackPosition) { + + response.processFile = false + response.infoLog += "☑ Preferred language is already first audio track! \n" + return response + + } + + + var ffmpegCommand = ', -c copy' + + if (file.ffProbeData.streams[0].codec_type.toLowerCase() == "video") { + ffmpegCommand += ` -map 0:v ` + } + + var allAudioTracks = file.ffProbeData.streams.filter(stream => stream.codec_type.toLowerCase() == "audio") + + + var streamIdx + + for (var i = 0; i < allAudioTracks.length; i++) { + + if (allAudioTracks[i].index == streamToMove.index) { + streamIdx = i + break + + } + } + + ffmpegCommand += ` -map 0:a:${streamIdx} -disposition:a:${streamIdx} default` + + for (var i = 0; i < allAudioTracks.length; i++) { + + if (i !== streamIdx) { + ffmpegCommand += ` -map 0:a:${i} -disposition:a:${i} none ` + } + } + + ffmpegCommand += ` -map 0:s? -map 0:d? ` + + + + response.processFile = true + response.preset = ffmpegCommand + response.container = `.` + file.container + response.handBrakeMode = false + response.FFmpegMode = true + response.reQueueAfter = true; + response.infoLog += `☒ Desired audio lang is not first audio stream, moving! \n` + return response + + + +} + diff --git a/Community/Tdarr_Plugin_076b_re_order_subtitle_streams.js b/Community/Tdarr_Plugin_076b_re_order_subtitle_streams.js new file mode 100644 index 0000000..8212212 --- /dev/null +++ b/Community/Tdarr_Plugin_076b_re_order_subtitle_streams.js @@ -0,0 +1,157 @@ + + + +module.exports.details = function details() { + + return { + id: "Tdarr_Plugin_076b_re_order_subtitle_streams", + Stage: "Pre-processing", + Name: "Re-order subtitle streams", + Type: "", + Operation: "Transcode", + Description: `[TESTING][Contains built-in filter] Specify a language tag for Tdarr to try and put as 1st subtitle track \n\n`, + Version: "1.00", + Link: "", + Tags:'pre-processing,subtitle only,ffmpeg,configurable', + Inputs: [ + { + name: 'preferred_language', + tooltip: `Specify one language tag for Tdarr to try and put as 1st subtitle track + + \\nExample:\\n + + eng + + \\nExample:\\n + + en + + \\nExample:\\n + + fr + + \\nExample:\\n + + de + + ` + } + ] + } + +} + +module.exports.plugin = function plugin(file, librarySettings, inputs) { + + + + //Must return this object + + var response = { + + processFile: false, + preset: '', + container: '.mp4', + handBrakeMode: false, + FFmpegMode: false, + reQueueAfter: false, + infoLog: '', + + } + + + console.log(inputs.preferred_language) + + if (inputs.preferred_language === undefined) { + + response.processFile = false + response.infoLog += "☒ Inputs not entered! \n" + return response + + + } + + + var desiredTrackPosition = file.ffProbeData.streams.filter(stream => stream.codec_type.toLowerCase() == "video" || stream.codec_type.toLowerCase() == "audio").length + + + + var subtitleInLang = file.ffProbeData.streams.filter(stream => { + + + if (stream.codec_type.toLowerCase() == "subtitle" + && stream.tags && stream.tags.language && inputs.preferred_language.includes(stream.tags.language.toLowerCase())) { + return true + } + + return false + + }) + + + if (subtitleInLang.length == 0) { + + response.processFile = false + response.infoLog += "☒ No subtitle tracks in desired language! \n" + return response + + } + + + var streamToMove = subtitleInLang[0] + + if (streamToMove.index == desiredTrackPosition) { + + response.processFile = false + response.infoLog += "☑ Preferred language is already first subtitle track! \n" + return response + + } + + + var ffmpegCommand = ', -c copy ' + + if (file.ffProbeData.streams[0].codec_type.toLowerCase() == "video") { + ffmpegCommand += ` -map 0:v -map 0:a ` + } + + var allSubtitleTracks = file.ffProbeData.streams.filter(stream => stream.codec_type.toLowerCase() == "subtitle") + + + var streamIdx + + for (var i = 0; i < allSubtitleTracks.length; i++) { + + if (allSubtitleTracks[i].index == streamToMove.index) { + streamIdx = i + break + + } + } + + ffmpegCommand += ` -map 0:s:${streamIdx} -disposition:s:${streamIdx} default` + + for (var i = 0; i < allSubtitleTracks.length; i++) { + + if (i !== streamIdx) { + ffmpegCommand += ` -map 0:s:${i} -disposition:a:${i} none ` + } + } + + ffmpegCommand += ` -map 0:d? ` + + + + response.processFile = true + response.preset = ffmpegCommand + response.container = `.` + file.container + response.handBrakeMode = false + response.FFmpegMode = true + response.reQueueAfter = true; + response.infoLog += `☒ Desired subtitle lang is not first subtitle stream, moving! \n` + return response + + + +} + diff --git a/Community/Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable.js b/Community/Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable.js new file mode 100644 index 0000000..9359bfe --- /dev/null +++ b/Community/Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable.js @@ -0,0 +1,112 @@ + + + +module.exports.details = function details() { + + return { + id: "Tdarr_Plugin_077b_HandBrake_NVENC_264_Configurable", + Stage: "Pre-processing", + Name: "HandBrake NVENC 264 Configurable", + Type: "Video", + Operation: "Transcode", + Description: `[Contains built-in filter] If files are not in H264, they will be transcoded into H264 using HandBrake NVENC H264. All audio and subtitles are kept. \n\n`, + Version: "1.00", + Link: "", + Tags: 'pre-processing,handbrake,nvenc h264,configurable', + Inputs: [ + { + name: 'handbrake_preset', + tooltip: `\\nEnter the name of a HandBrake preset. + + + \\nYou can learn more about HandBrake presets here: + + \\nhttps://handbrake.fr/docs/en/latest/technical/official-presets.html + + + \\nExample:\\n + Very Fast 1080p30 + + \\nExample:\\n + Fast 1080p30 + + ` + }, + { + name: 'output_container', + tooltip: ` + \\nEnter the output container of the new file + + \\nExample:\\n + .mp4 + + \\nExample:\\n + .mkv + + ` + }, + ] + } + +} + +module.exports.plugin = function plugin(file, librarySettings, inputs) { + + + + //Must return this object + + var response = { + + processFile: false, + preset: '', + container: '.mp4', + handBrakeMode: false, + FFmpegMode: false, + reQueueAfter: false, + infoLog: '', + + } + + if (inputs.handbrake_preset === undefined + || inputs.output_container === undefined) { + + response.processFile = false + response.infoLog += "☒ Inputs not entered! \n" + return response + + } + + if (file.ffProbeData.streams[0].codec_name == 'h264') { + + + response.processFile = false + response.infoLog += "☑ File is already in h264, no need to transcode! \n" + return response + + + } else { + + var container = inputs.output_container + + if (container.charAt(0) != '.') { + container = '.' + container + } + + var response = { + + processFile: true, + preset: `-Z "${inputs.handbrake_preset}" -e nvenc_h264 --all-audio --all-subtitles`, + container: container, + handBrakeMode: true, + FFmpegMode: false, + reQueueAfter: true, + infoLog: '☒ File is not in h264, transcoding! \n' + } + + return response + + } + +} + diff --git a/Community/Tdarr_Plugin_43az_add_to_radarr.js b/Community/Tdarr_Plugin_43az_add_to_radarr.js new file mode 100644 index 0000000..d0e184b --- /dev/null +++ b/Community/Tdarr_Plugin_43az_add_to_radarr.js @@ -0,0 +1,114 @@ + + + +module.exports.details = function details() { + + return { + id: "Tdarr_Plugin_43az_add_to_radarr", + Stage: "Post-processing", + Name: "Add movie to Radarr after processing", + Type: "Video", + Operation: "", + Description: `[TESTING]Add movie to Radarr after processing \n\n`, + Version: "1.00", + Link: "", + Tags:"3rd party,post-processing,configurable", + + Inputs: [ + { + name: 'server_ip', + tooltip: ` + Enter the server IP address + + \\nExample:\\n + 192.168.0.10 + ` + }, + { + name: 'port', + tooltip: ` + Enter the port Radarr is using + + \\nExample:\\n + 7878 + ` + }, + { + name: 'radarr_api_key', + tooltip: ` + + Enter the Radarr API key. You can find it on Radarr at /settings/general + + \\nExample:\\n + 3ff1ae1c39a2a2a397315e15266dea48 + ` + }, + ] + } +} + +module.exports.plugin = function plugin(file, librarySettings, inputs) { + + + + const request = require('request') + + const IP = inputs.server_ip + const port = inputs.port + const APIKey = inputs.radarr_api_key + + + var term = file.file.split("/") + term = term[term.length - 1] + term = term.split(".") + term = term[term.length - 2] + term = encodeURI(term) + + + console.log(IP) + console.log(term) + + request.get(`http://${IP}:${port}/api/movie/lookup?term=${term}&apikey=${APIKey}`, { + json: { + } + }, (error, res, body) => { + if (error) { + console.error(error) + } + // console.log(`statusCode: ${res.statusCode}`) + //console.log(body) + + + var response = body[0] + console.log(response.title) //Shrek + + + response.profileId = 6 + response.path = file.file + response.qualityProfile = 6 + + + request.post(`http://${IP}:${port}/api/movie?apikey=${APIKey}`, { + json: response + }, (error, res, body) => { + if (error) { + console.error(error) + } + console.log(`statusCode: ${res.statusCode}`) + // console.log(body) + }) + + }) + + + //Optional response if you need to modify database + var response = { + file, + removeFromDB: false, + updateDB: false, + } + + //return response + +} + diff --git a/Community/Tdarr_Plugin_MC93_Migz1FFMPEG.js b/Community/Tdarr_Plugin_MC93_Migz1FFMPEG.js new file mode 100644 index 0000000..05aac9b --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_Migz1FFMPEG.js @@ -0,0 +1,162 @@ +function details() { + return { + id: "Tdarr_Plugin_MC93_Migz1FFMPEG", + Stage: "Pre-processing", + Name: "Migz-Transcode Using Nvidia GPU & FFMPEG", + Type: "Video", + Operation:"Transcode", + Description: `Files will be transcoded 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`, + Version: "2.20", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz1FFMPEG.js", + Tags:'pre-processing,ffmpeg,video only,nvenc h265,configurable', + Inputs: [ + { + name: 'container', + tooltip: `Specify output container of file, ensure that all stream types you may have are supported by your chosen container. mkv is recommended. + \\nExample:\\n + mkv + + \\nExample:\\n + mp4` + + + }, + { + 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. + \\nExample:\\n + 6000 + + \\nExample:\\n + 4000` + + + }, + ] + } +} + +function plugin(file, librarySettings, inputs) { + var response = { + processFile: false, + preset: '', + handBrakeMode: false, + FFmpegMode: true, + reQueueAfter: true, + infoLog: '' + } + + if (inputs.container == "") { + response.infoLog += "☒Container has not been configured within plugin settings, please configure required options. Skipping this plugin. \n" + response.processFile = false + return response + } else { + response.container = '.' + inputs.container + } + + if (inputs.container == "mkv") { + extraArguments += "-map -0:d " + } + + if (file.fileMedium !== "video") { + response.processFile = false + response.infoLog += "☒File is not a video. \n" + return response + } + + if (typeof file.meta.Duration != 'undefined') { + var duration = (file.meta.Duration * 0.0166667) + } else { + var duration = (file.ffProbeData.streams[0].duration * 0.0166667) + } + + var videoIdx = 0 + var extraArguments = "" + var bitrateSettings = "" + var filesize = (file.file_size / 1000) + var currentBitrate = ~~(file.file_size / (duration * 0.0075)) + var targetBitrate = ~~((file.file_size / (duration * 0.0075)) / 2) + var minimumBitrate = ~~(targetBitrate * 0.7) + var maximumBitrate = ~~(targetBitrate * 1.3) + + + if (targetBitrate == "0") { + response.processFile = false + response.infoLog += "☒Target bitrate could not be calculated. Skipping this plugin. \n" + return response + } + + if (inputs.bitrate_cutoff != "") { + if (currentBitrate <= inputs.bitrate_cutoff) { + if (file.container == inputs.container) { + response.processFile = false + response.infoLog += `☑Current bitrate is below configured bitrate cutoff of ${inputs.bitrate_cutoff} & file container is already ${inputs.container}. Nothing to do, skipping. \n` + return response + } else { + response.processFile = true + response.preset += `, -c copy ${extraArguments}` + response.infoLog += `☒Current bitrate is below configured bitrate cutoff of ${inputs.bitrate_cutoff} but is not in correct container. Remuxing to ${inputs.container} but not transcoding. \n` + return response + } + } + } + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { + if (file.ffProbeData.streams[i].codec_name == 'mjpeg') { + extraArguments += `-map -v:${videoIdx} ` + } + if (file.ffProbeData.streams[i].codec_name == 'hevc' && file.container == inputs.container) { + response.processFile = false + response.infoLog += `☑File is already in ${inputs.container} & hevc. \n` + return response + } + if (file.ffProbeData.streams[i].codec_name == 'hevc' && file.container != '${inputs.container}') { + response.infoLog += `☒File is hevc but is not in ${inputs.container} container. Remuxing. \n` + response.preset = `, -map 0 -c copy ${extraArguments}` + response.processFile = true; + return response + } + videoIdx++ + } + } + + bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k -maxrate ${maximumBitrate}k` + response.infoLog += `Container for output selected as ${inputs.container}. \n Current bitrate = ${~~(file.file_size / (duration * 0.0075))} \n Bitrate settings: \nTarget = ${targetBitrate} \nMinimum = ${minimumBitrate} \nMaximum = ${maximumBitrate} \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') { //if a h264 coded video is not HDR + 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` + } + 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` + } + + + response.preset += `,-map 0 -c:v hevc_nvenc -rc:v vbr_hq ${bitrateSettings} -bufsize 2M -spatial_aq:v 1 -c:a copy -c:s copy -max_muxing_queue_size 4096 ${extraArguments}` + response.processFile = true + response.infoLog += `☒File is not hevc. Transcoding. \n` + return response +} + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_MC93_Migz1FFMPEG_CPU.js b/Community/Tdarr_Plugin_MC93_Migz1FFMPEG_CPU.js new file mode 100644 index 0000000..012825a --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_Migz1FFMPEG_CPU.js @@ -0,0 +1,133 @@ +function details() { + return { + id: "Tdarr_Plugin_MC93_Migz1FFMPEG_CPU", + Stage: "Pre-processing", + Name: "Migz-Transcode Using CPU & FFMPEG", + Type: "Video", + Operation:"Transcode", + Description: `Files will be transcoded 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`, + Version: "1.1", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz1FFMPEG_CPU.js", + Tags:'pre-processing,ffmpeg,video only,configurable,h265', + Inputs: [ + { + name: 'container', + tooltip: `Specify output container of file, ensure that all stream types you may have are supported by your chosen container. mkv is recommended. + \\nExample:\\n + mkv + + \\nExample:\\n + mp4` + + + }, + { + 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. + \\nExample:\\n + 6000 + + \\nExample:\\n + 4000` + + + }, + ] + } +} + +function plugin(file, librarySettings, inputs) { + var response = { + processFile: false, + preset: '', + handBrakeMode: false, + FFmpegMode: true, + reQueueAfter: true, + infoLog: '' + } + + if (inputs.container == "") { + response.infoLog += "☒Container has not been configured within plugin settings, please configure required options. Skipping this plugin. \n" + response.processFile = false + return response + } else { + response.container = '.' + inputs.container + } + + if (inputs.container == "mkv") { + extraArguments += "-map -0:d " + } + + if (file.fileMedium !== "video") { + response.processFile = false + response.infoLog += "☒File is not a video. \n" + return response + } + + if (typeof file.meta.Duration != 'undefined') { + var duration = (file.meta.Duration * 0.0166667) + } else { + var duration = (file.ffProbeData.streams[0].duration * 0.0166667) + } + + var videoIdx = 0 + var extraArguments = "" + var bitrateSettings = "" + var filesize = (file.file_size / 1000) + var currentBitrate = ~~(file.file_size / (duration * 0.0075)) + var targetBitrate = ~~((file.file_size / (duration * 0.0075)) / 2) + var minimumBitrate = ~~(targetBitrate * 0.7) + var maximumBitrate = ~~(targetBitrate * 1.3) + + if (targetBitrate == "0") { + response.processFile = false + response.infoLog += "☒Target bitrate could not be calculated. Skipping this plugin. \n" + return response + } + + if (inputs.bitrate_cutoff != "") { + if (currentBitrate <= inputs.bitrate_cutoff) { + if (file.container == inputs.container) { + response.processFile = false + response.infoLog += `☑Current bitrate is below configured bitrate cutoff of ${inputs.bitrate_cutoff} & file container is already ${inputs.container}. Nothing to do, skipping. \n` + return response + } else { + response.processFile = true + response.preset += `, -c copy ${extraArguments}` + response.infoLog += `☒Current bitrate is below configured bitrate cutoff of ${inputs.bitrate_cutoff} but is not in correct container. Remuxing to ${inputs.container} but not transcoding. \n` + return response + } + } + } + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { + if (file.ffProbeData.streams[i].codec_name == 'mjpeg') { + extraArguments += `-map -v:${videoIdx} ` + } + if (file.ffProbeData.streams[i].codec_name == 'hevc' && file.container == inputs.container) { + response.processFile = false + response.infoLog += `☑File is already in ${inputs.container} & hevc. \n` + return response + } + if (file.ffProbeData.streams[i].codec_name == 'hevc' && file.container != '${inputs.container}') { + response.infoLog += `☒File is hevc but is not in ${inputs.container} container. Remuxing. \n` + response.preset = `, -map 0 -c copy ${extraArguments}` + response.processFile = true; + return response + } + videoIdx++ + } + } + + bitrateSettings = `-b:v ${targetBitrate}k -minrate ${minimumBitrate}k -maxrate ${maximumBitrate}k` + response.infoLog += `Container for output selected as ${inputs.container}. \n Current bitrate = ${~~(file.file_size / (duration * 0.0075))} \n Bitrate settings: \nTarget = ${targetBitrate} \nMinimum = ${minimumBitrate} \nMaximum = ${maximumBitrate} \n` + + + response.preset += `,-map 0 -c:v libx265 ${bitrateSettings} -bufsize 2M -spatial_aq:v 1 -c:a copy -c:s copy -max_muxing_queue_size 4096 ${extraArguments}` + response.processFile = true + response.infoLog += `☒File is not hevc. Transcoding. \n` + return response +} + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_MC93_Migz2CleanTitle.js b/Community/Tdarr_Plugin_MC93_Migz2CleanTitle.js new file mode 100644 index 0000000..12a7aa6 --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_Migz2CleanTitle.js @@ -0,0 +1,106 @@ +function details() { + return { + id: "Tdarr_Plugin_MC93_Migz2CleanTitle", + Stage: "Pre-processing", + Name: "Migz-Clean title metadata", + Type: "Video", + 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`, + Version: "1.20", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz2CleanTitle.js", + Tags:'pre-processing,ffmpeg,configurable', + Inputs: [ + { + name: 'clean_audio', + tooltip: `Specify if audio titles should be checked & cleaned. Optional. + \\nExample:\\n + true + + \\nExample:\\n + false` + }, + { + name: 'clean_subtitles', + tooltip: `Specify if subtitle titles should be checked & cleaned. Optional. + \\nExample:\\n + true + + \\nExample:\\n + false` + }, + ] + } +} + +function plugin(file, librarySettings, inputs) { + var response = { + processFile : false, + preset : '', + container: '.' + file.container, + handBrakeMode : false, + FFmpegMode : true, + reQueueAfter : false, + infoLog : '', + + } + + var ffmpegCommandInsert = '' + var videoIdx = 0 + var audioIdx = 0 + var subtitleIdx = 0 + var convert = false + + if (file.fileMedium !== "video") { + console.log("File is not video") + response.infoLog += "☒File is not video \n" + response.processFile = false; + return response + } + + if (typeof file.meta.Title != 'undefined') try { + ffmpegCommandInsert += ` -metadata title="" ` + convert = true + } catch (err) { } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { + if (typeof file.ffProbeData.streams[i].tags.title != 'undefined') { + response.infoLog += `☒Video stream title is not empty, most likely junk metadata. Removing title from stream ${i} \n` + ffmpegCommandInsert += ` -metadata:s:v:${videoIdx} title="" ` + convert = true + } + videoIdx++ + } + + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && inputs.clean_audio.toLowerCase() == "true") { + if (file.ffProbeData.streams[i].tags.title.split('.').length-1 > 3) { + 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:a:${audioIdx} title="" ` + convert = true + } + audioIdx++ + } + + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" && inputs.clean_subtitles.toLowerCase() == "true") { + if (file.ffProbeData.streams[i].tags.title.split('.').length-1 > 3) { + 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="" ` + convert = true + } + subtitleIdx++ + } + } catch (err) { } + + if (convert == true) { + response.infoLog += "☒File has title metadata. Removing \n" + response.preset = `,${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096` + response.reQueueAfter = true; + response.processFile = true; + } else { + response.infoLog += "☑File has no title metadata \n" + } + return response +} + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_MC93_Migz3CleanAudio.js b/Community/Tdarr_Plugin_MC93_Migz3CleanAudio.js new file mode 100644 index 0000000..41bcfc3 --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_Migz3CleanAudio.js @@ -0,0 +1,172 @@ +function details() { + return { + id: "Tdarr_Plugin_MC93_Migz3CleanAudio", + Stage: "Pre-processing", + Name: "Migz-Clean audio streams", + Type: "Audio", + Operation: "Clean", + Description: `This plugin keeps only specified language audio tracks & can tags those that have an unknown language. \n\n`, + Version: "2.00", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz3CleanAudio.js", + Tags:'pre-processing,ffmpeg,audio only,configurable', + Inputs: [ + { + name: 'language', + 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 + \\nExample:\\n + eng + + \\nExample:\\n + eng,und + + \\nExample:\\n + eng,und,jap` + }, + { + name: 'commentary', + tooltip: `Specify if audio tracks that contain commentary/description should be removed. + \\nExample:\\n + true + + \\nExample:\\n + false` + }, + { + 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 + \\nExample:\\n + eng + + \\nExample:\\n + por + ` + }, + { + 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. + \\nExample:\\n + true + + \\nExample:\\n + false` + }, + ] + } +} + +function plugin(file, librarySettings, inputs) { + var response = { + processFile: false, + preset: '', + container: '.' + file.container, + handBrakeMode: false, + FFmpegMode: true, + 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 + } + + if (inputs.language == "") { + response.infoLog += "☒Language/s keep have not been configured within plugin settings, please configure required options. Skipping this plugin. \n" + response.processFile = false; + return response + } + + var language = inputs.language.split(",") + var ffmpegCommandInsert = '' + var convert = false + var audioIdx = -1 + var audioStreamsRemoved = 0 + var audioStreamCount = file.ffProbeData.streams.filter(row => (row.codec_type.toLowerCase() == "audio")).length; + + console.log("audioStreamCount:" + audioStreamCount) + response.infoLog += `Languages to keep are ${language} \n` + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { + audioIdx++ + } + } catch (err) { } + + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && language.indexOf(file.ffProbeData.streams[i].tags.language.toLowerCase()) === -1) { + audioStreamsRemoved++ + ffmpegCommandInsert += `-map -0:a:${audioIdx} ` + response.infoLog += `☒Audio stream detected as being an unwanted language, removing. Audio stream 0:a:${audioIdx} - ${file.ffProbeData.streams[i].tags.language.toLowerCase()} \n` + convert = true + } + } catch (err) { } + + try { + if (inputs.commentary.toLowerCase() == "true" && file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && (file.ffProbeData.streams[i].tags.title.toLowerCase().includes('commentary') || file.ffProbeData.streams[i].tags.title.toLowerCase().includes('description') || file.ffProbeData.streams[i].tags.title.toLowerCase().includes('sdh'))) { + audioStreamsRemoved++ + 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` + convert = true + } + } catch (err) { } + + try { + if (inputs.tag_language != "") { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].tags.language.toLowerCase().includes('und') && language.indexOf('und') !== -1) { + 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` + convert = true + } + + if (typeof file.ffProbeData.streams[i].tags.language === 'undefined' && file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { + 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 + } + } + } catch (err) { } + + try { + if (typeof file.ffProbeData.streams[i].tags.title == 'undefined' && inputs.tag_title.toLowerCase() == "true" && file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { + if (file.ffProbeData.streams[i].channels == "8") { + 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` + convert = true + } + if (file.ffProbeData.streams[i].channels == "6") { + 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` + convert = true + } + if (file.ffProbeData.streams[i].channels == "2") { + 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` + convert = true + } + } + } catch (err) { } + + } + + if (audioStreamsRemoved == audioStreamCount) { + response.infoLog += "☒Cancelling plugin otherwise all audio tracks would be removed. \n" + response.processFile = false + return response + } + + if (convert === true && (audioStreamsRemoved != audioStreamCount)) { + response.processFile = true + response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096` + response.container = '.' + file.container + response.reQueueAfter = true + } else { + response.processFile = false + response.infoLog += "☑File doesn't contain audio tracks which are unwanted or that require tagging.\n" + } + return response +} +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_MC93_Migz4CleanSubs.js b/Community/Tdarr_Plugin_MC93_Migz4CleanSubs.js new file mode 100644 index 0000000..3183e49 --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_Migz4CleanSubs.js @@ -0,0 +1,126 @@ +function details() { + return { + id: "Tdarr_Plugin_MC93_Migz4CleanSubs", + Stage: "Pre-processing", + Name: "Migz-Clean subtitle streams", + Type: "subtitless", + Operation: "Clean", + Description: `This plugin keeps only specified language subtitle tracks & can tag those that have an unknown language. \n\n`, + Version: "2.00", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz4CleanSubs.js", + Tags:'pre-processing,ffmpeg,subtitle only,configurable', + Inputs: [ + { + name: 'language', + 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 + eng + + \\nExample:\\n + eng,jap` + }, + { + name: 'commentary', + tooltip: `Specify if subtitle tracks that contain commentary/description should be removed. + \\nExample:\\n + true + + \\nExample:\\n + false` + }, + { + name: 'tag_title', + 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 + \\nExample:\\n + eng + + \\nExample:\\n + por` + }, + ] + } +} + +function plugin(file, librarySettings, inputs) { + var response = { + processFile: false, + preset: '', + container: '.' + file.container, + handBrakeMode: false, + FFmpegMode: true, + 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 + } + + if (inputs.language == "") { + response.infoLog += "☒Language/s keep have not been configured within plugin settings, please configure required options. Skipping this plugin. \n" + response.processFile = false; + return response + } + + var language = inputs.language.split(",") + var ffmpegCommandInsert = '' + var subtitleIdx = -1 + var convert = false + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") { + subtitleIdx++ + } + } catch (err) { } + + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" && language.indexOf(file.ffProbeData.streams[i].tags.language.toLowerCase()) === -1) { + ffmpegCommandInsert += `-map -0:s:${subtitleIdx} ` + response.infoLog += `☒Subtitle stream detected as being an unwanted language, removing. Subtitle stream 0:s:${subtitleIdx} - ${file.ffProbeData.streams[i].tags.language.toLowerCase()} \n` + convert = true + } + } catch (err) { } + + try { + if (inputs.commentary.toLowerCase() == "true" && file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" && (file.ffProbeData.streams[i].tags.title.toLowerCase().includes('commentary') || file.ffProbeData.streams[i].tags.title.toLowerCase().includes('description') || file.ffProbeData.streams[i].tags.title.toLowerCase().includes('sdh'))) { + 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` + convert = true + } + } catch (err) { } + + try { + if (inputs.tag_title != "") { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle" && file.ffProbeData.streams[i].tags.language.toLowerCase().includes('und')) { + ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_title} ` + response.infoLog += `☒Subtitle stream detected as having unknown language tagged, tagging as ${inputs.tag_title}. \n` + convert = true + } + } + } catch (err) { } + + try { + if (typeof file.ffProbeData.streams[i].tags.language == 'undefined' && file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") { + ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_title} ` + response.infoLog += `☒Subtitle stream detected as having no language tagged, tagging as ${inputs.tag_title}. \n` + convert = true + } + } catch (err) { } + } + if (convert === true ) { + response.processFile = true; + response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096` + response.container = '.' + file.container + response.reQueueAfter = true; + } else { + response.processFile = false; + response.infoLog += "☑File doesn't contain subtitle tracks which are unwanted or that require tagging.\n" + } + return response +} +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_MC93_Migz5ConvertAudio.js b/Community/Tdarr_Plugin_MC93_Migz5ConvertAudio.js new file mode 100644 index 0000000..4950a49 --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_Migz5ConvertAudio.js @@ -0,0 +1,120 @@ +function details() { + return { + id: "Tdarr_Plugin_MC93_Migz5ConvertAudio", + Stage: "Pre-processing", + Name: "Migz-Convert audio streams", + Type: "Audio", + Operation: "Transcode", + Description: `This plugin can convert any 2.0 audio track/s to AAC and can create downmixed audio tracks. \n\n`, + Version: "2.00", + Link: "", + Tags:'pre-processing,ffmpeg,audio only,configurable', + Inputs: [ + { + name: 'aac_stereo', + tooltip: `Specify if any 2.0 audio tracks should be converted to aac for maximum compatability with devices. + \\nExample:\\n + true + + \\nExample:\\n + false` + }, + { + 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. + \\nExample:\\n + true + + \\nExample:\\n + false` + }, + ] + } +} + +function plugin(file, librarySettings, inputs) { + var response = { + processFile: false, + container: '.' + file.container, + handBrakeMode: false, + FFmpegMode: true, + reQueueAfter: true, + infoLog: '', + } + + + if (file.fileMedium !== "video") { + console.log("File is not video") + response.infoLog += "☒File is not video. \n" + response.processFile = false; + return response + } + + if (inputs.aac_stereo == "" && inputs.downmix == "") { + response.infoLog += "☒Neither aac_stereo or downmix options have been configured within plugin settings, please configure required options. Skipping this plugin. \n" + response.processFile = false + return response + } + + var ffmpegCommandInsert = '' + var audioIdx = 0 + var has2Channel = false + var has6Channel = false + var has8Channel = false + var convert = false + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { + if (file.ffProbeData.streams[i].channels == "2") { + has2Channel = true + } + if (file.ffProbeData.streams[i].channels == "6") { + has6Channel = true + } + if (file.ffProbeData.streams[i].channels == "8") { + has8Channel = true + } + } + } catch (err) { } + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { + if (inputs.downmix.toLowerCase() == "true") { + if (has8Channel == true && has6Channel == false && file.ffProbeData.streams[i].channels == "8") { + ffmpegCommandInsert += `-map 0:${i} -c:a:${audioIdx} ac3 -ac 6 -metadata:s:a:${audioIdx} title="5.1 " ` + response.infoLog += "☒Audio track is 8 channel, no 6 channel exists. Creating 2 channel from 6 channel. \n" + convert = true + } + if (has6Channel == true && has2Channel == false && file.ffProbeData.streams[i].channels == "6") { + ffmpegCommandInsert += `-map 0:${i} -c:a:${audioIdx} aac -ac 2 -metadata:s:a:${audioIdx} title="2.0 " ` + response.infoLog += "☒Audio track is 6 channel, no 2 channel exists. Creating 2 channel from 6 channel. \n" + convert = true + } + } + if (inputs.aac_stereo.toLowerCase() == "true") { + if (file.ffProbeData.streams[i].codec_name != "aac" && file.ffProbeData.streams[i].channels == "2" ) { + ffmpegCommandInsert += `-c:a:${audioIdx} aac ` + response.infoLog += "☒Audio track is 2 channel but is not AAC. Converting. \n" + convert = true + } + } + audioIdx++ + } + + } + + if (convert == 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 ` + } else { + response.infoLog += "☑File contains all required audio formats. \n" + response.processFile = false; + } + return response + +} + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_MC93_Migz6OrderStreams.js b/Community/Tdarr_Plugin_MC93_Migz6OrderStreams.js new file mode 100644 index 0000000..1ee5d4a --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_Migz6OrderStreams.js @@ -0,0 +1,137 @@ +function details() { + return { + id: "Tdarr_Plugin_MC93_Migz6OrderStreams", + Stage: "Pre-processing", + Name: "Migz-Organize Streams", + Type: "Streams", + Operation: "Organize", + Description: `Organizes streams into Video first, then Audio (2ch, 6ch, 8ch) and finally Subtitles. \n\n`, + Version: "1.00", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_Migz6OrderStreams.js", + Tags:'pre-processing,ffmpeg,' + } +} + +function plugin(file) { + var response = { + processFile: false, + preset: '', + container: '.' + file.container, + handBrakeMode: false, + FFmpegMode: true, + infoLog: '', + } + + var ffmpegCommandInsert = '' + var videoIdx = 0 + var audioIdx = 0 + var audio2Idx = 0 + var audio6Idx = 0 + var audio8Idx = 0 + var subtitleIdx = 0 + var convert = false + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { + if (audioIdx != "0" || subtitleIdx != "0") { + convert = true + response.infoLog += "☒ Video not first. \n" + } + videoIdx++ + } + + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { + if (subtitleIdx != "0") { + convert = true + response.infoLog += "☒ Audio not second. \n" + } + audioIdx++ + if (file.ffProbeData.streams[i].channels == "2") { + if (audio6Idx != "0" || audio8Idx != "0") { + convert = true + response.infoLog += "☒ Audio 2ch not first. \n" + } + audio2Idx++ + } + if (file.ffProbeData.streams[i].channels == "6") { + if (audio8Idx != "0") { + convert = true + response.infoLog += "☒ Audio 6ch not second. \n" + } + audio6Idx++ + } + if (file.ffProbeData.streams[i].channels == "8") { + audio8Idx++ + response.infoLog += "☒ Audio 8ch not last. \n" + } + } + + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") { + subtitleIdx++ + } + } catch (err) { } + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video" && file.ffProbeData.streams[i].codec_name.toLowerCase() != "mjpeg") { + ffmpegCommandInsert += `-map 0:${i} ` + } + } catch (err) { } + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].channels == "2") { + ffmpegCommandInsert += `-map 0:${i} ` + } + } catch (err) { } + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].channels == "6") { + ffmpegCommandInsert += `-map 0:${i} ` + } + } catch (err) { } + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].channels == "8") { + ffmpegCommandInsert += `-map 0:${i} ` + } + } catch (err) { } + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].channels != "2" && file.ffProbeData.streams[i].channels != "6" && file.ffProbeData.streams[i].channels != "8") { + ffmpegCommandInsert += `-map 0:${i} ` + } + } catch (err) { } + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") { + ffmpegCommandInsert += `-map 0:${i} ` + } + } catch (err) { } + } + + if (convert == true) { + response.processFile = true; + response.preset = `,${ffmpegCommandInsert} -c copy -max_muxing_queue_size 4096` + response.reQueueAfter = true; + response.infoLog += "☒ Streams are out of order, reorganizing streams. Video, Audio, Subtitles. \n" + } else { + response.infoLog += "☑ Streams are in expected order. \n " + response.processFile = false; + } + return response + +} +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_MC93_MigzImageRemoval.js b/Community/Tdarr_Plugin_MC93_MigzImageRemoval.js new file mode 100644 index 0000000..2fd4094 --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_MigzImageRemoval.js @@ -0,0 +1,60 @@ +function details() { + return { + id: "Tdarr_Plugin_MC93_MigzImageRemoval", + Stage: "Pre-processing", + Name: "Migz-Remove image formats from file", + Type: "Video", + Operation:"Clean", + Description: `Identify any unwanted image formats in the file and remove those streams. MJPEG & PNG \n\n`, + Version: "1.0", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_MigzImageRemoval.js", + Tags:'pre-processing,ffmpeg,video only' + } +} + +function plugin(file, librarySettings, inputs) { + var response = { + processFile: false, + preset: '', + handBrakeMode: false, + container: '.' + file.container, + FFmpegMode: true, + reQueueAfter: true, + infoLog: '' + } + + if (file.fileMedium !== "video") { + response.processFile = false + response.infoLog += "☒File is not a video. \n" + return response + } + + var videoIdx = 0 + var extraArguments = "" + var convert = false + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "video") { + if (file.ffProbeData.streams[i].codec_name == 'mjpeg' || file.ffProbeData.streams[i].codec_name == 'png') { + convert = true + extraArguments += `-map -v:${videoIdx} ` + } + videoIdx++ + } + } + + if (convert === true ) { + response.preset += `,-map 0 -c copy -max_muxing_queue_size 4096 ${extraArguments}` + response.infoLog += `☒File has image format stream, removing. \n` + response.processFile = true; + } else { + response.processFile = false; + response.infoLog += "☑File doesn't contain any unwanted image format streams.\n" + } + return response +} + + + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_MC93_MigzPlex_Autoscan.js b/Community/Tdarr_Plugin_MC93_MigzPlex_Autoscan.js new file mode 100644 index 0000000..150bf63 --- /dev/null +++ b/Community/Tdarr_Plugin_MC93_MigzPlex_Autoscan.js @@ -0,0 +1,77 @@ +module.exports.details = function details() { + + return { + id: "Tdarr_Plugin_MC93_MigzPlex_Autoscan", + Stage: "Post-processing", + Name: "Send request for file to be scanned by plex_autoscan.", + Type: "Video", + Operation: "", + Description: `Send request for file to be scanned by plex_autoscan. https://github.com/l3uddz/plex_autoscan \n\n`, + Version: "1.00", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_MC93_MigzPlex_Autoscan.js", + Tags:"3rd party,post-processing,configurable", + + Inputs: [ + { + name: 'autoscan_address', + tooltip: ` + 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 + 3468 + ` + }, + { + name: 'autoscan_passkey', + tooltip: ` + + Enter the autoscan passkey. + + \\nExample:\\n + 9c4b81fe234e4d6eb9011cefe514d915 + ` + }, + ] + } +} + +module.exports.plugin = function plugin(file, librarySettings, inputs) { + + const request = require('request') + const ADDRESS = inputs.autoscan_address + const PORT = inputs.autoscan_port + const PASSKEY = inputs.autoscan_passkey + + var response = "" + filepath = `${file.file}` + + + request.post({ + headers: {'content-type': 'application/json'}, + url: `${ADDRESS}:${PORT}/${PASSKEY}`, + form: { "eventType" : "Manual", "filepath" : `${filepath}` } + }, (error, res, body) => { + if (error) { + console.error(error) + } + console.log(`statusCode: ${res.statusCode}`) + console.log(body) + }) + + console.log("request next") + console.log(request.post) + +} + diff --git a/Community/Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js b/Community/Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js index 4b7ebab..18cf629 100644 --- a/Community/Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js +++ b/Community/Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta", + Stage: "Pre-processing", Name: "Drawmonster MP4 No title meta data ", Type: "Video", Description: `[Contains built-in filter] This plugin removes metadata (if a title exists). The output container is mp4. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_a37x_Drawmonster_MP4_No_Title_Meta.js", + Tags:'pre-processing,ffmpeg', } } diff --git a/Community/Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js b/Community/Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js index df76687..6cc48f6 100644 --- a/Community/Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js +++ b/Community/Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30", + Stage: "Pre-processing", Name: "HaveAGitGat HandBrake VeryFast1080p30, No title meta, no subs, 192Kb AAC stereo,MP4 ", Type: "Video", Description: `[Contains built-in filter] This plugin transcodes into H264 using HandBrake's 'Very Fast 1080p30' preset if the file is not in H264 already. It removes subs, metadata (if a title exists) and adds a stereo 192kbit AAC track if an AAC track (any) doesn't exist. The output container is MP4. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_a8hc_HaveAGitGat_HandBrake_H264_VeryFast1080p30.js", + Tags:'pre-processing,handbrake,ffmpeg,h264', } } @@ -85,6 +87,19 @@ function plugin(file) { /// +if(hasSubs){ + + response.infoLog += "☒File has subs \n" + response.preset = ',-sn -map 0 -c copy' + response.reQueueAfter = true; + response.processFile = true; + response.FFmpegMode = true + return response + + }else{ + response.infoLog += "☑File has no subs \n" + } + if((file.meta.Title != "undefined") && !jsonString.includes("aac") && hasSubs){ response.infoLog += "☒File has title metadata and no aac and subs \n" @@ -144,19 +159,6 @@ function plugin(file) { response.infoLog += "☑File has aac track \n" } - if(hasSubs){ - - response.infoLog += "☒File has subs \n" - response.preset = ',-sn -map 0 -c copy' - response.reQueueAfter = true; - response.processFile = true; - response.FFmpegMode = true - return response - - }else{ - response.infoLog += "☑File has no subs \n" - } - response.infoLog += "☑File meets conditions! \n" return response diff --git a/Community/Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js b/Community/Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js index 9314ee1..03e7920 100644 --- a/Community/Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js +++ b/Community/Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30", + Stage: "Pre-processing", Name: "HaveAGitGat HandBrake Fast1080p30, No title meta, no subs, 192Kb AAC stereo,MP4 ", Type: "Video", Description: `[Contains built-in filter] This plugin transcodes into H264 using HandBrake's 'Fast 1080p30' preset if the file is not in H264 already. It removes subs, metadata (if a title exists) and adds a stereo 192kbit AAC track if an AAC track (any) doesn't exist. The output container is MP4. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_a9hc_HaveAGitGat_HandBrake_H264_Fast1080p30.js", + Tags:'pre-processing,handbrake,ffmpeg,h264', } } @@ -85,6 +87,19 @@ function plugin(file) { /// +if(hasSubs){ + + response.infoLog += "☒File has subs \n" + response.preset = ',-sn -map 0 -c copy' + response.reQueueAfter = true; + response.processFile = true; + response.FFmpegMode = true + return response + + }else{ + response.infoLog += "☑File has no subs \n" + } + if((file.meta.Title != "undefined") && !jsonString.includes("aac") && hasSubs){ response.infoLog += "☒File has title metadata and no aac and subs \n" @@ -144,18 +159,6 @@ function plugin(file) { response.infoLog += "☑File has aac track \n" } - if(hasSubs){ - - response.infoLog += "☒File has subs \n" - response.preset = ',-sn -map 0 -c copy' - response.reQueueAfter = true; - response.processFile = true; - response.FFmpegMode = true - return response - - }else{ - response.infoLog += "☑File has no subs \n" - } response.infoLog += "☑File meets conditions! \n" diff --git a/Community/Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs.js b/Community/Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs.js new file mode 100644 index 0000000..87a35b4 --- /dev/null +++ b/Community/Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs.js @@ -0,0 +1,149 @@ + + + +module.exports.details = function details() { + + return { + id: "Tdarr_Plugin_a9hd_FFMPEG_Transcode_Specific_Audio_Stream_Codecs", + Stage: "Pre-processing", + Name: "Transcode Specific Audio Stream Codecs", + Type: "", + Operation: "Transcode", + Description: `[TESTING][Contains built-in filter] Transcode audio streams with specific codecs into another codec. \n\n`, + Version: "1.00", + Link: "", + Tags:'pre-processing,audio only,ffmpeg,configurable', + Inputs: [ + { + name: 'codecs_to_transcode', + tooltip: `Specifiy the codecs which you'd like to transcode + + \\nExample:\\n + + ac3 + + \\nExample:\\n + + eac3,ac3,aac + + + + + + ` + }, + { + name: 'codec', + tooltip: `Specify the codec you'd like to transcode into: + + \\n aac + \\n ac3 + \\n eac3 + \\n dts + \\n flac + \\n mp2 + \\n mp3 + \\n truehd + + \\nExample:\\n + eac3 + + + + + + ` + } + ] + } + +} + +module.exports.plugin = function plugin(file, librarySettings, inputs) { + + + + //Must return this object + + var response = { + + processFile: false, + preset: '', + container: '.mp4', + handBrakeMode: false, + FFmpegMode: false, + reQueueAfter: false, + infoLog: '', + + } + + if (inputs.codecs_to_transcode === undefined + || inputs.codec === undefined + ) { + + response.processFile = false + response.infoLog += "☒ Inputs not entered! \n" + return response + } + + + var encoder = inputs.codec + + if (encoder == 'mp3') { + encoder = `libmp3lame` + } else if (encoder == 'dts') { + encoder = `dca` + } + + + + + var codecs_to_transcode = inputs.codecs_to_transcode.split(',') + var hasStreamsToTranscode = false + + + var ffmpegCommand = `, -c copy -map 0:v ` + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && + file.ffProbeData.streams[i].codec_name && + codecs_to_transcode.includes(file.ffProbeData.streams[i].codec_name.toLowerCase()) + ) { + + ffmpegCommand += ` -map 0:${i} -c:${i} ${encoder} ` + hasStreamsToTranscode = true + + } + } + + + ffmpegCommand += ` -map 0:s? -map 0:d? ` + + console.log + + + if (hasStreamsToTranscode == false) { + + response.processFile = false + response.infoLog += "☑ File does not have any streams that need to be transcoded! \n" + return response + + } else { + + response.processFile = true; + response.preset = ffmpegCommand + response.container = '.' + file.container + response.handBrakeMode = false + response.FFmpegMode = true + response.reQueueAfter = true; + response.infoLog += `☒ File has streams which aren't in desired codec! \n` + return response + + + + + + } +} + diff --git a/Community/Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js b/Community/Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js index bd66b85..4fc0d2f 100644 --- a/Community/Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js +++ b/Community/Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta", + Stage: "Pre-processing", Name: "Nosirus h265, aac, no meta, subs kept", Type: "Video", - Description: `[Contains built-in filter] If the file is not in h265 it will be trancoded into h265 with HandBrake using the following command '-e x265 -q 22 --encoder-preset slow --all-audio --all-subtitles copy:aac -E fdk_aac -Q 4 -x aq-mode=3'. If no aac, aac track will be added. Subtitles are kept. Metadata is removed.\n\n + Description: `[Contains built-in filter] If the file is not in h265 it will be trancoded into h265 with FFmpeg using the following command '-e x265 -q 22 --encoder-preset slow --all-audio --all-subtitles copy:aac -E fdk_aac -Q 4 -x aq-mode=3'. If no aac, aac track will be added. Subtitles are kept. Metadata is removed.\n\n `, Version: "1.01", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_b38x_Nosirus_h265_aac_no_meta.js", + Tags:'pre-processing,ffmpeg,h265,', } } diff --git a/Community/Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js b/Community/Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js index 348ba31..2c79048 100644 --- a/Community/Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js +++ b/Community/Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3", + Stage: "Pre-processing", Name: "the1poet Video surround sound to ac3", Type: "Video", Description: `[Contains built-in filter] If the file has surround sound tracks not in ac3, they will be converted to ac3. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_b39x_the1poet_surround_sound_to_ac3.js", + Tags:'pre-processing,ffmpeg,audio only,', } } @@ -60,7 +62,7 @@ function plugin(file) { try { - if ( file.ffProbeData.streams[1].channels >= 6 && file.ffProbeData.streams[i].codec_name !== 'ac3' && file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" ) { + if ( file.ffProbeData.streams[i].channels >= 6 && file.ffProbeData.streams[i].codec_name !== 'ac3' && file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" ) { ffmpegCommandInsert += ` -c:a:${audioIdx} ac3 ` hasnonAC3SurroundTrack = true diff --git a/Community/Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js b/Community/Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js index d24789d..3771a39 100644 --- a/Community/Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js +++ b/Community/Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js @@ -1,19 +1,26 @@ - function details() { return { id: "Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV", + Stage: "Pre-processing", Name: "Tiered FFMPEG NVENC settings depending on resolution", Type: "Video", Operation:"Transcode", - Description: `[Contains built-in filter] This plugin uses different FFMPEG NVENC transcoding settings for 480p,576p,720p and 1080p. If files are not in hevc they will be transcoded. The output container is mkv. \n\n`, - Version: "1.05", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js" + Description: `[Contains built-in filter] This plugin uses different FFMPEG NVENC transcoding settings for 480p,576p,720p,1080p and 4KUHD. If files are not in hevc they will be transcoded. The output container is mkv. \n\n`, + Version: "1.09", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_d5d3_iiDrakeii_FFMPEG_NVENC_Tiered_MKV.js", + Tags:'pre-processing,ffmpeg,video only,nvenc h265', } } - + function plugin(file) { var transcode = 0; //if this var changes to 1 the file will be transcoded - + var bitrateprobe = 0; //bitrate from ffprobe + var bitratetarget = 0; + var bitratemax = 0; + var bitratecheck = 0; + var subcli = `-c:s copy`; + var maxmux = ''; + var map = '-map 0' //default values that will be returned var response = { processFile: false, @@ -22,18 +29,21 @@ function plugin(file) { handBrakeMode: false, FFmpegMode: false, reQueueAfter: true, - infoLog: '' + 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 { + } + 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 @@ -41,139 +51,142 @@ function plugin(file) { return response } -//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" ) { - if (file.video_codec_name == 'h263') { - response.preset = `-c:v h263_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'h264') { - if (file.ffProbeData.streams[0].profile == 'High 10') {response.preset = `,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else {response.preset = `-c:v h264_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - } - else if (file.video_codec_name == 'mjpeg') { - response.preset = `-c:v mjpeg_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'mpeg1') { - response.preset = `-c:v mpeg1_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'mpeg2') { - response.preset = `-c:v mpeg2_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'mpeg4') { - response.preset = `,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'vc1') { - response.preset = `-c:v vc1_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + 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 == 'vp8') { - response.preset = `-c:v vp8_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + } + 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" ) { + bitratecheck = 1000000; + if(bitrateprobe != null && bitrateprobe < bitratecheck) { + bitratetarget = parseInt((bitrateprobe * .8) / 1000); // Lower Bitrate to 60% of original and convert to KB + bitratemax = bitratetarget + 500; // Set max bitrate to 6MB Higher } - else if (file.video_codec_name == 'vp9') { - response.preset = `-c:v vp9_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } else { - response.preset = `,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + bitratetarget = 1000; + bitratemax = 1500; } - + response.preset += `,${map} -dn -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v ${bitratetarget}k -maxrate:v 1500k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -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") { - if (file.video_codec_name == 'h263') { - response.preset = `-c:v h263_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'h264') { - if (file.ffProbeData.streams[0].profile == 'High 10') {response.preset = `,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 29 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else {response.preset = `-c:v h264_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - } - else if (file.video_codec_name == 'mjpeg') { - response.preset = `-c:v mjpeg_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'mpeg1') { - response.preset = `-c:v mpeg1_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'mpeg2') { - response.preset = `-c:v mpeg2_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'mpeg4') { - response.preset = `,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'vc1') { - response.preset = `-c:v vc1_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + bitratecheck = 2000000; + if(bitrateprobe != null && bitrateprobe < bitratecheck) { + bitratetarget = parseInt((bitrateprobe * .8) / 1000); // Lower Bitrate to 60% of original and convert to KB + bitratemax = bitratetarget + 2000; // Set max bitrate to 6MB Higher } - else if (file.video_codec_name == 'vp8') { - response.preset = `-c:v vp8_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'vp9') { - response.preset = `-c:v vp9_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } else { - response.preset = `,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + bitratetarget = 2000; + bitratemax = 4000; } - + response.preset += `,${map} -dn -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 30 -b:v ${bitratetarget}k -maxrate:v ${bitratemax}k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -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") { - if (file.video_codec_name == 'h263') { - response.preset = `-c:v h263_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'h264') { - if (file.ffProbeData.streams[0].profile == 'High 10') {response.preset = `,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else {response.preset = `-c:v h264_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - } - else if (file.video_codec_name == 'mjpeg') { - response.preset = `-c:v mjpeg_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'mpeg1') { - response.preset = `-c:v mpeg1_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + bitratecheck = 2500000; + if(bitrateprobe != null && bitrateprobe < bitratecheck) { + bitratetarget = parseInt((bitrateprobe * .8) / 1000); // Lower Bitrate to 60% of original and convert to KB + bitratemax = bitratetarget + 2500; // Set max bitrate to 6MB Higher } - else if (file.video_codec_name == 'mpeg2') { - response.preset = `-c:v mpeg2_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + else { + bitratetarget = 2500; + bitratemax = 5000; } - else if (file.video_codec_name == 'mpeg4') { - response.preset = `,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'vc1') { - response.preset = `-c:v vc1_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } - else if (file.video_codec_name == 'vp8') { - response.preset = `-c:v vp8_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + response.preset += `,${map} -dn -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:V 31 -b:v ${bitratetarget}k -maxrate:v ${bitratemax}k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -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") { + bitratecheck = 14000000; + if(bitrateprobe != null && bitrateprobe < bitratecheck) { + bitratetarget = parseInt((bitrateprobe * .7) / 1000); // Lower Bitrate to 60% of original and convert to KB + bitratemax = bitratetarget + 6000; // Set max bitrate to 6MB Higher } - else if (file.video_codec_name == 'vp9') { - response.preset = `-c:v vp9_cuvid,-map 0 -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` - } else { - response.preset = `, -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v 2500k -maxrate:v 5000k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -c:a copy -c:s copy` + bitratetarget = 14000; + bitratemax = 20000; } - - transcode = 1; + response.preset += `,${map} -dn -c:v hevc_nvenc -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 31 -b:v ${bitratetarget}k -maxrate:v ${bitratemax}k -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -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} but is not hevc!\n` + response.infoLog += `☒File is ${file.video_resolution}!\n` + response.infoLog += `☒File is not hevc!\n` + response.infoLog += `☒File bitrate is ${parseInt(bitrateprobe / 1000)}kb!\n` + if(bitrateprobe < bitratecheck) { + response.infoLog += `File bitrate is LOWER than the Default Target Bitrate!\n` + } + else { + response.infoLog += `File bitrate is HIGHER than the Default Target Bitrate!\n` + } + response.infoLog += `☒Target Bitrate set to ${bitratetarget}kb!\n` response.infoLog += `File is being transcoded!\n` } return response -} - + } module.exports.details = details; - module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix.js b/Community/Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix.js new file mode 100644 index 0000000..f226984 --- /dev/null +++ b/Community/Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix.js @@ -0,0 +1,78 @@ +function details() { + return { + id: "Tdarr_Plugin_d5d4_iiDrakeii_Not_A_Video_Mjpeg_Fix", + Stage: "Pre-processing", + Name: "Mjpeg Stream False Not A Video Fixer", + Type: "Video", + Operation:"", + Description: `Checks if file is not a video file due to Mjpeg stream. Removes Mjpeg Stream \n\n`, + Version: "1.00", + Tags:'pre-processing,ffmpeg,' + } +} + +function plugin(file) { + var transcode = 0; //if this var changes to 1 the file will be transcoded +//default values that will be returned + var response = { + processFile: false, + preset: '', + container: '.mp4', + handBrakeMode: false, + FFmpegMode: false, + reQueueAfter: true, + infoLog: '' + } + response.container = '.' + file.container + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { +//check for mjpeg streams and set the preset if mjpeg streams are found + try { + if ((file.ffProbeData.streams[i].codec_name.toLowerCase() == "mjpeg") && file.ffProbeData.streams[i].codec_type.toLowerCase() == "video" ) { + response.preset = `,-map 0 -map -0:v:1 -c:v copy -c:a copy -c:s copy` + response.infoLog = "☒File is not a video but has Mjpeg Stream! \n" + } + } + catch (err) { } + } + //If preset is not set check if file is video and stop (reque if it is a video) + if (response.preset != `,-map 0 -map -0:v:1 -c:v copy -c:a copy -c:s copy`) { + if (file.fileMedium !== "video") { + console.log("File is not video!") + response.infoLog += " File is not video\n" + response.processFile = false; + + return response + } + else { + response.infoLog += "☑File is a video Without Mjpeg! \n" + response.processFile = false + response.reQueueAfter = true + return response + } + } + //Process mjpeg removal if video found to not be a video and have mjpeg stream + else { + if (file.fileMedium !== "video") { + transcode = 1 + } + else { + response.infoLog += "☑File is a video With Mjpeg! \n" + response.processFile = false + response.reQueueAfter = true + return response + } + } +//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 += `Mjpeg Stream is being removed!\n` + } + + return response +} +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js b/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js new file mode 100644 index 0000000..b4b8b6e --- /dev/null +++ b/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js @@ -0,0 +1,194 @@ +function details() { + return { + id: "Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4", + Stage: "Pre-processing", + Name: "Dallas FFmpeg h264 mp4. Video: h264/mp4, Subs: Convert to mov_text or drop, Audio: aac", + Type: "Video", + Description: `This plugin transcodes into H264 with an MP4 container using the FFmpeg preset you select (slow,medium,fast,veryfast). It maintains all compatible subtitles and audio tracks. Drops picture tracks such as mjpeg\n\n`, + Version: "1.00", + Link: "https://github.com/JackDallas/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_da11_Dallas_FFmpeg_Presets_H264_MP4.js", + Tags:'pre-processing,ffmpeg,h264,video only,configurable', + Inputs: [ + { + name: 'FFmpeg_preset', + tooltip: `Select the FFmpeg preset you wish to use,(slow,medium,fast,veryfast). + + \\nExample:\\n + slow + + \\nExample:\\n + medium + + \\nExample:\\n + fast + + \\nExample:\\n + veryfast` + } + ] + }; +} + +const presets = [ + "slow", "medium", "fast", "veryfast" +]; + +// Normalizes the preset or if invalid returns null +function getPreset(preset) { + if (!preset) + return null; + + preset = preset.toLowerCase(); + // Strip Spaces + preset = preset.replace(/\s+/g, ''); + + if (presets.includes(preset)) { + return preset; + } + + return null; +} + +const GOOD = true; +const BAD = false; + +function plugin(file, librarySettings, inputs) { + var response = { + processFile: false, + preset: '', + container: '.mp4', + handBrakeMode: false, + FFmpegMode: false, + reQueueAfter: false, + infoLog: '', + addInfo(status, info) { + this.infoLog += (status ? "☑" : "☒") + " " + info + "\n"; + } + } + + // Check the file is a video + if (file.fileMedium !== "video") { + console.log("File is not video"); + response.addInfo(BAD, `File is not video`); + response.processFile = false; + return response; + } + + // Get and check the preset + let preset = getPreset(inputs.FFmpeg_preset); + + if (preset === null) { + response.addInfo(BAD, `Invalid Preset, \"${inputs.FFmpeg_preset}\" please select from (slow,medium,fast,veryfast)`); + + throw `Error: Invalid Preset, \"${inputs.FFmpeg_preset}\" please select from (slow,medium,fast,veryfast) \n` + } + + var jsonString = JSON.stringify(file) + + var hasSubs = false; + var hasBadSubs = false; + var subType = "-c:s mov_text"; + var subMap = ""; + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + try { + let streamData = file.ffProbeData.streams[i]; + if (streamData.codec_type.toLowerCase() == "subtitle") { + if (streamData.codec_name === "hdmv_pgs_subtitle" || streamData.codec_name === "dvd_subtitle") { + hasBadSubs = true; + // Drop incompatible subs + subMap += " -map -0:" + streamData.index + " "; + } else if (streamData.codec_name != "mov_text") { + hasSubs = true + // Keep compatible subs + subMap += " -map 0:" + streamData.index + " "; + } + } + } catch (err) { + console.log("Error reading stream: " + JSON.stringify(err)); + } + } + + if (hasBadSubs) + response.addInfo(BAD, "File contains unsupported sub(s), dropping these!"); + + if (file.ffProbeData.streams[0].codec_name != 'h264') { + response.addInfo(BAD, "File is not in h264!"); + response.preset = ', -map_metadata -1 -map 0:V ' + subMap + ' -map 0:a -c:v libx264 -preset medium -c:a aac -strict -2 ' + subType; + response.reQueueAfter = true; + response.processFile = true; + response.FFmpegMode = true; + return response; + } else { + response.addInfo(GOOD, "File is already in h264!"); + } + + if ((file.meta.Title != undefined) && !jsonString.includes("aac") && hasSubs) { + response.addInfo(BAD, "File has title metadata and no aac and subs"); + response.preset = ', -map_metadata -1 -map 0:v ' + subMap + ' -map 0:a -c:v copy -c:a aac -strict -2 ' + subType; + response.reQueueAfter = true; + response.processFile = true; + response.FFmpegMode = true; + return response + } + + if (!jsonString.includes("aac") && hasSubs) { + response.addInfo(BAD, "File has no aac track and has subs"); + response.preset = ', -map 0:v ' + subMap + ' -map 0:a -c:v copy -c:a aac -strict -2 ' + subType; + response.reQueueAfter = true; + response.processFile = true; + response.FFmpegMode = true; + return response; + } + + if (file.meta.Title != undefined && hasSubs) { + response.addInfo(BAD, "File has title and has subs"); + response.preset = ', -map_metadata -1 -map 0:v ' + subMap + ' -map 0:a -c:v copy -c:a copy ' + subType; + response.reQueueAfter = true; + response.processFile = true; + response.FFmpegMode = true; + return response; + } + + if (file.meta.Title != undefined) { + response.addInfo(BAD, "File has title metadata"); + response.preset = ', -map_metadata -1 -map 0:v ' + subMap + ' -map 0:a -c:v copy -c:a copy ' + subType; + response.reQueueAfter = true; + response.processFile = true; + response.FFmpegMode = true; + return response; + } else { + response.addInfo(GOOD, "File has no title metadata"); + } + + if (!jsonString.includes("aac")) { + response.addInfo(BAD, "File has no aac track"); + response.preset = ', -map 0:v ' + subMap + ' -map 0:a -c:v copy -c:a aac -strict -2 ' + subType; + response.reQueueAfter = true; + response.processFile = true; + response.FFmpegMode = true; + return response; + } else { + response.addInfo(GOOD, "File has aac track"); + } + + if (hasSubs) { + if (hasBadSubs) { + response.addInfo(BAD, "File has incompatible subs, dropping these..."); + } else { + response.addInfo(BAD, "File has compatible subs, copying..."); + } + response.preset = ', -map 0:v ' + subMap + ' -map 0:a -c:v copy -c:a copy ' + subType; + response.processFile = true; + response.FFmpegMode = true + return response + } else { + response.addInfo(GOOD, "File has no/compatible subs"); + } + + response.addInfo(GOOD, "File meets conditions!"); + return response +} + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta.js b/Community/Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta.js index adff2b4..0b25aa9 100644 --- a/Community/Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta.js +++ b/Community/Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta", + Stage: "Pre-processing", Name: "H.264 MKV 480p30, No Subs No, Title Meta", Type: "Video", Description: `[Contains built-in filter] This plugin removes subs, metadata (if a title exists) and makes sure the video is h264 480p mkv. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e3jc_Tharic_H.264_MKV_480p30_No_Subs_No_Title_Meta.js", + Tags:'pre-processing,handbrake,ffmpeg,h264' } } diff --git a/Community/Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta.js b/Community/Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta.js index a45e88d..6ad4c5f 100644 --- a/Community/Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta.js +++ b/Community/Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta", + Stage: "Pre-processing", Name: "H.264 MKV 720p30, No Subs No, Title Meta", Type: "Video", Description: `[Contains built-in filter] This plugin removes subs, metadata (if a title exists) and makes sure the video is h264 720p mkv. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e3jd_Tharic_H.264_MKV_720p30_No_Subs_No_Title_Meta.js", + Tags:'pre-processing,handbrake,ffmpeg,h264' } } diff --git a/Community/Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta.js b/Community/Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta.js index 819202f..956b23b 100644 --- a/Community/Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta.js +++ b/Community/Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta", + Stage: "Pre-processing", Name: "H.264 MKV 1080p30, No Subs No, Title Meta", Type: "Video", Description: `[Contains built-in filter] This plugin removes subs, metadata (if a title exists) and makes sure the video is h264 1080p mkv. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e3je_Tharic_H.264_MKV_1080p30_No_Subs_No_Title_Meta.js", + Tags:'pre-processing,handbrake,ffmpeg,h264' } } diff --git a/Community/Tdarr_Plugin_e5c3_CnT_Add_Subtitles.js b/Community/Tdarr_Plugin_e5c3_CnT_Add_Subtitles.js index dd3d0d9..0af11d7 100644 --- a/Community/Tdarr_Plugin_e5c3_CnT_Add_Subtitles.js +++ b/Community/Tdarr_Plugin_e5c3_CnT_Add_Subtitles.js @@ -1,5 +1,5 @@ const fs = require('fs'); -const iso6392 = require('/home/Tdarr/Documents/node_modules/iso-639-2'); +const execSync = require('child_process').execSync; function details() { return { @@ -8,24 +8,24 @@ function details() { Name: "Add subtitles to MKV files", Type: "Video", Operation:"Remux", - Description: `Add subtitles. READ THIS!! You must run "npm install iso-639-2" in the folder "/home/Tdarr/Documents" for this plugin to work. This is a plugin that will check for subtitles, they should be named according to the ISO 639-2 language code.`, - Version: "1.00", + Description: `This plugin will check for subtitles, they should be named according to the ISO 639-2 language code.\nA subtitle could look like this: eng.srt\n If there are subtitles found they will be added with FFMPEG, if there are no subs of that language found.\n On first run node module iso-639-2 will be installed in the documents folder.\n Created by @control#0405`, + Version: "1.3", Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e5c3_CnT_Add_Subtitles.js", + Tags:'pre-processing,ffmpeg,subtitle only,configurable', + Inputs: [ + { + name: 'install_packages', + tooltip: `Please change this to "yes", it allows the plugin to install the required nodemodule. (iso-639-2) \\nExample:\\n yes` + }, + { + name: 'container', + tooltip: `Enter the output container of the new file.\\n Default: .mkv\\nExample:\\n.mkv` + }, + ] } } -function plugin(file) { - var i = 0; //int for counting lang[position] - var sub = 0; //becomes first subtitle stream - var lang = iso6392; //languages to check against - var path = file.meta.Directory; //path of media folder - var exist = 0; //if the language exists should be added this becomes 1 - var new_subs = 0 //count the new subs - var added_subs = 0; //counts the amount of subs that have been mapped - var preset_import = ''; - var preset_meta = ''; - - +function plugin(file, librarySettings, inputs, otherArguments) { //default response var response = { processFile: false, @@ -34,50 +34,91 @@ function plugin(file) { handBrakeMode: false, FFmpegMode: false, reQueueAfter: false, - infoLog: `Testing for subtitles...\nPath: ${path}\n`, + infoLog: `Searching new subtitles...\n`, + } + + if (inputs.container !== undefined) { + response.container = inputs.container; + console.log(`Changed container to: ` + inputs.container); } - //find first subtitle stream - while (file.ffProbeData.streams[sub].codec_type.toLowerCase() != "subtitle") { - sub++ + if (inputs.install_packages == "yes") { + if (!fs.existsSync(`${otherArguments.homePath}/Tdarr/node_modules/iso-639-2`)) { + execSync(`cd ${otherArguments.homePath}/Tdarr \n npm install iso-639-2`); + } + } else { + response.infoLog = `Please take a look at the input options\n A extra nodemodule is required.` + return response; + } + + if (fs.existsSync(`/home/Tdarr/Documents/Tdarr/node_modules/iso-639-2`)) { + var iso6392 = require('/home/Tdarr/Documents/Tdarr/node_modules/iso-639-2'); + } else { + response.infoLog += `Nodemodule iso-639-2 isn't installed!\nTry Again` + return response; } - response.infoLog += `The first subtitle stream is ${sub}\n` + var i = 0; //int for counting lang[position] + var found_subtitle_stream = 0; + var sub = 0; //becomes first subtitle stream + var lang = iso6392; //languages to check against + var path = file.meta.Directory; //path of media folder + var exist = 0; //if the language exists should be added this becomes 1 + var new_subs = 0 //count the new subs + var added_subs = 0; //counts the amount of subs that have been mapped + var preset_import = ''; + var preset_meta = ''; + + //find first subtitle stream + while (found_subtitle_stream == 0 && sub < file.ffProbeData.streams.length) { + if (file.ffProbeData.streams[sub].codec_type.toLowerCase() == "subtitle") { + found_subtitle_stream = 1; + } else { + sub++; + } + } + + response.infoLog += `Path: ${path}\n`; for (i = 0; i < lang.length; i++) { - //check if srt exists in folder + //check if srt file exists in folder if (fs.existsSync(`${path}/${lang[i].iso6392B}.srt`)) { - response.infoLog += `Found subtitle ${lang[i].name}\n` + response.infoLog += `Found subtitle ${lang[i].name}.srt\n`; - //check if language already exists - for (sub_stream = sub; sub_stream < file.ffProbeData.streams.length; sub_stream++) { - response.infoLog += `does ${lang[i].name} exist in stream ${sub_stream}?\n` - if (file.ffProbeData.streams[sub_stream].tags.language.toLowerCase() == lang[i].iso6392B) { - response.infoLog += `YES\n` - exist = 1; - } else { - response.infoLog += `NO\n` + if (found_subtitle_stream == 1) { + //check if language already exists + for (sub_stream = sub; sub_stream < file.ffProbeData.streams.length; sub_stream++) { + //response.infoLog += `does ${lang[i].name} exist in stream ${sub_stream}?\n` + if (file.ffProbeData.streams[sub_stream].tags.language) { + if (file.ffProbeData.streams[sub_stream].tags.language.toLowerCase() == lang[i].iso6392B) { + //response.infoLog += `YES\n` + exist = 1; + response.infoLog += `Language already exists in stream ${sub_stream}\n It will not be added\n`; + } + } } + } else { + exist = 0; } //add if it hasn't found the language if (exist != 1) { - preset_import += ` -sub_charenc "UTF-8" -f srt -i "${path}/${lang[i].iso6392B}.srt"` - preset_meta += ` -metadata:s:s:${new_subs} language=${lang[i].iso6392B}` - new_subs++ + preset_import += ` -sub_charenc "UTF-8" -f srt -i "${path}/${lang[i].iso6392B}.srt"`; + preset_meta += ` -metadata:s:s:${new_subs} language=${lang[i].iso6392B}`; + new_subs++; } - } else { - response.infoLog += `did not find sub ${lang[i].iso6392B}.srt\n` - } + } + //else { + // response.infoLog += `did not find sub ${lang[i].iso6392B}.srt\n` + //} exist = 0; } - response.infoLog += `${new_subs} new subs will be added\n` response.preset += ` ${preset_import}${preset_meta} -map 0:v -map 0:a` //map new subs while (added_subs < new_subs) { - added_subs++ - response.preset += ` -map ${added_subs}:s` + added_subs++; + response.preset += ` -map ${added_subs}:s`; } //if new subs have been found they will be added @@ -85,12 +126,16 @@ function plugin(file) { response.FFmpegMode = true; response.processFile = true; response.reQueueAfter = true; - response.preset += ` -map 0:s -c copy` + if (found_subtitle_stream == 1) { + response.preset += ` -map 0:s `; + } + response.preset += ` -c copy`; + response.infoLog += `${new_subs} new subs will be added\n`; } else { - response.infoLog += `No new subtitle languages were found\n` + response.infoLog += `No new subtitle languages were found\n`; } - response.infoLog += `The ffmpeg string is: ${response.preset}\n` + //response.infoLog += `The ffmpeg string is: ${response.preset}\n` return response } diff --git a/Community/Tdarr_Plugin_e5c3_CnT_Keep_Preferred_Audio.js b/Community/Tdarr_Plugin_e5c3_CnT_Keep_Preferred_Audio.js new file mode 100644 index 0000000..113dde9 --- /dev/null +++ b/Community/Tdarr_Plugin_e5c3_CnT_Keep_Preferred_Audio.js @@ -0,0 +1,134 @@ +const exec = require('child_process').exec; +const fs = require('fs'); + +function details() { + return { + id: "Tdarr_Plugin_e5c3_CnT_Keep_Preferred_Audio", + Stage: "Pre-processing", + Name: "Keep Preffered Audio", + Type: "Audio", + Operation:"Remove Audio", + Description: "Plugin that checks for unwanted audio, per 1.104 beta you can change the languages yourself from within Tdarr!\nUntill you enter a value it keep english tracks by default.\nUndefined languages are kept to prevent videos without sound.\nIf you would like to keep track of the languages you have for each file you can use the 'special' option.\nCreated by @control#0405", + Version: "1.2", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e5c3_CnT_Keep_Preferred_Audio.js", + Inputs: [ + { + name: 'languages', + tooltip: `Desired Languages you would like to keep, language format has to be according to the iso-639-2 standard: https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes\\nExample:\\eng,dut` + }, + { + name: 'special', + tooltip: `This is if you want a specific language to be logged to a file in your Tdarr documents folder.\\nIt will add the name of the file that is being processed if this language(s) has been found.\\nThe file is created the first time it finds a file with the language.\\nThe languages don't have to be in "languages".\\nExample:\\eng,dut` + }, + { + name: 'container', + tooltip: `Enter the output container of the new file.\\n Default: .mkv\\nExample:\\n.mkv` + } + ] + } +} + +function plugin(file, librarySettings, inputs, otherArguments) { + if (inputs.languages == "" || inputs.special == 'undefined') { + var languages = ["eng", "en"]; //these languages should be kept, named according to ISO 639-2 language scheme + } else { + var languages = inputs.languages.toLowerCase().split(','); //these languages should be kept, named according to ISO 639-2 language scheme + } + if (inputs.special == "" || inputs.special == 'undefined') { + var special = ``; + } else { + var special = inputs.special.toLowerCase().split(','); + } + if (languages.length >= special.length) { + var length = languages.length; + } else { + var length = special.length; + } + console.log(languages); + var transcode = 0; //if this becomes '1' it will be transcoded + var sourcename = file.meta.FileName.substring(0, file.meta.FileName.lastIndexOf(".")); //filename without extension + var specialcheck = ``; //contains the txt string if special language was found + var wanted = 0; + var audio = 0; + + //default response + var response = { + processFile: false, + preset: `, -map 0:v`, + container: '.mkv', + handBrakeMode: false, + FFmpegMode: false, + reQueueAfter: false, + infoLog: 'Removing unwanted audio...\n', + }; + + if (inputs.container !== undefined) { + response.container = inputs.container; + console.log(`Changed container to: ` + inputs.container); + } + + for (i = 0; i < file.ffProbeData.streams.length; i++) { + if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { + //check for non-english tracks + if (file.ffProbeData.streams[i].tags.language) { + if (typeof file.ffProbeData.streams[i].tags.language !== 'undefined') { + for (l = 0; l < length; l++) { + if (file.ffProbeData.streams[i].tags.language == special[l]) { + if (!fs.existsSync(otherArguments.homePath + `/Tdarr/special_audio_${special[l]}.txt`)) { //create txt file if it doesn't exist yet + exec(`echo "${sourcename}" >> ${otherArguments.homePath}/Tdarr/special_audio_${special[l]}.txt`); //first file will be added and file is created + console.log(`added to txt: ` + sourcename); + } else { + specialcheck = fs.readFileSync(otherArguments.homePath + `/Tdarr/special_audio_${special[l]}.txt`).toString(); //create string from existing file + if (!specialcheck.includes(sourcename)) { //only add the filename if it wasn't added already + exec(`echo "${sourcename}" >> ${otherArguments.homePath}/Tdarr/special_audio_${special[l]}.txt`); + console.log(`added to txt: ` + sourcename); + } + } + + response.preset += ` -map 0:${i}`; + response.infoLog += `Found special ${special[l]}: ${i}\n`; + wanted++; + break; + } else if (file.ffProbeData.streams[i].tags.language == languages[l]) { + response.preset += ` -map 0:${i}`; + response.infoLog += `Found wanted ${languages[l]}: ${i}\n`; + wanted++; + break; + } else if (i == length-1) { + response.infoLog += `Found unwanted: ${file.ffProbeData.streams[i].tags.language}: ${i}\n`; + } + } + } else { + response.preset += ` -map 0:${i}`; + response.infoLog += `Added undefined: ${i}\n`; + wanted++; + } + } else { + response.preset += ` -map 0:${i}`; + response.infoLog += `Added undefined: ${i}\n`; + wanted++; + } + + audio++; + } + } + + if (audio > wanted && wanted > 0) { + transcode = 1; + } + + if (transcode == 1) { + response.infoLog += `Found unwanted audio\nIt will be removed\n`; + response.processFile = true; + response.FFmpegMode = true; + response.preset += ` -map 0:s? -c copy`; + response.reQueueAfter = true; + } else { + response.infoLog += `No unwanted audio found!\n`; + } + + return response +} + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_e5c3_CnT_Remove_Letterbox.js b/Community/Tdarr_Plugin_e5c3_CnT_Remove_Letterbox.js new file mode 100644 index 0000000..98bb3a2 --- /dev/null +++ b/Community/Tdarr_Plugin_e5c3_CnT_Remove_Letterbox.js @@ -0,0 +1,387 @@ +const fs = require('fs'); +const execSync = require('child_process').execSync; + +function details() { + return { + id: "Tdarr_Plugin_e5c3_CnT_Remove_Letterbox", + Stage: "Pre-processing", + Name: "Remove letterbox", + Type: "Video", + Operation:"Transcode", + Description: `Uses iiDrakeii's filter, and crops video files when letterboxing is detected.\nThis uses the FFMPEG NVENC transcoding(hw).\nIf a file is 4K it will be scaled down to 1080p.\nNow with user definable bitrates!(since 1.104 beta)\nCreated by @control#0405`, + Version: "1.4", + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_e5c3_CnT_Remove_Letterbox.js", + Inputs: [ + { + name: 'bitrate', + tooltip: `Desired bitrate for a 1080p video, minimum transcode size is based of this too!\\n 720p will be half of 1080p, 480p will be half of 720p.\\nThe default is '3000', this value is based of movies.\\nI would suggest 1500-2000 for series.\\nExample:\\n3000` + }, + { + name: 'container', + tooltip: `Enter the output container of the new file.\\n Default: .mkv\\nExample:\\n.mkv` + }, + ], + Tags:'pre-processing,video only,ffmpeg,configurable', + } +} + +function plugin(file, librarySettings, inputs, otherArguments) { + if (inputs.bitrate == "" || inputs.special == 'undefined') { + var min_bitrate = 6600; + var avg_rate = 3000; + var max_rate = 6000; + } else { + var min_bitrate = inputs.bitrate*2.2; + var avg_rate = inputs.bitrate; + var max_rate = inputs.bitrate*2; + } + + var source = file.meta.SourceFile; //source file + var stats = fs.statSync(source); + var size = stats["size"]/1000000000; + size = size.toFixed(2); + var decoder = decoder_string(file); //decoder, before the input + var encoder = encoder_string_full(file, highres(file), crop_decider(file, create_crop_values(file).crop_height).crop, avg_rate, max_rate); //encoder + var process = 0; //decides if it should be processed + + //default values that will be returned + var response = { + processFile: false, + preset: '', + container: '.mkv', + handBrakeMode: false, + FFmpegMode: true, + reQueueAfter: true, + infoLog: '' + } + + if (inputs.container !== undefined) { + response.container = inputs.container; + console.log(`Changed container to: ` + inputs.container); + } + + var returns = { + create_crop: create_crop_values(file), + crop: crop_decider(file, create_crop_values(file).crop_height), + size: size_check(file, min_bitrate) + } + + var log = { + size: returns.size.log, + hevc: ``, + resolution: ``, + crop: returns.crop.log, + createcrop: returns.create_crop.log + } + + //filters + if (size_check(file, min_bitrate).size == 1) { + if (hevc(file) == 1) { + log.hevc = `☑ - Video is not HEVC \n`; + process = 1; + } else { + log.hevc += "☒ - File is already in HEVC \n" + } + + if (highres(file) == 1) { + process = 1; + log.resolution += `☑ - Resolution > 1080p.\n File will be transcoded to 1080p \n`; + } else { + log.resolution += `☒ - Resolution <= 1080p \n`; + } + + if (crop_decider(file, create_crop_values(file).crop_height).crop != "0") { + process = 1; + } + } + + response.infoLog += log.createcrop + + log.crop + + log.resolution + + log.size + + log.hevc; + response.preset = `${decoder}, -map 0:v:0 -map 0:a -map 0:s? ${encoder}` + + //change response + if (process == 1) { + response.processFile = true; + response.infoLog += `File will be processed\n`; + } else if (file.forceProcessing === true) { + response.processFile = true; + response.infoLog += `Force processing!\n`; + } else if (response.container !== `.` + file.container) { + response.infoLog += `Container is not correct\nMuxing to ${response.container}!\n`; + response.preset = `${decoder}, -c copy`; + response.processFile = true; + } else { + response.infoLog += `Processing not necessary\n`; + } + + return response; +} + +function highres(file) { + //if file is larger than 1080p it should be transcoded + if (file.meta.ImageWidth > 1920) { + return 1; + } else { + return 0; + } +} + +function create_crop_values(file) { + var source = file.meta.SourceFile; //source file + var dir = file.meta.Directory; //source directory + var sourcename = file.meta.FileName.substring(0, file.meta.FileName.lastIndexOf(".")); //filename without extension + var cropfile = `${dir}/${sourcename}.txt`; //location and name of the crop file + var returns = { + crop_height: 0, //return value for this function, required for crop_decider + log: `` + } + + //create crop value + if (!fs.existsSync(`${cropfile}`)) { + returns.log += `Creating crop values...\n`; + execSync(otherArguments.ffmpegPath + ` -ss 300 -i \"${source}\" -frames:v 240 -vf cropdetect -f null - 2>&1 | awk \'/crop/ { print $NF }\' | tail -240 > \"${cropfile}\"`); + execSync(otherArguments.ffmpegPath + ` -ss 1200 -i \"${source}\" -frames:v 240 -vf cropdetect -f null - 2>&1 | awk \'/crop/ { print $NF }\' | tail -240 >> \"${cropfile}\"`); + } else { + returns.log += `Crop values already exist\n`; + } + + //get data from copvalue.txt + var data = fs.readFileSync(`${cropfile}`).toString().split("\n"); //full data from cropvalue.txt + + //get height of the supposed cropped video + //var crop_height = parseInt(data[0].substring(10, 14)); + for (var c = 0; c < data.length; c++) { + if (parseInt(data[c].substring(10, 14)) > returns.crop_height) { + returns.crop_height = parseInt(data[c].substring(10, 14)); + returns.log += `New cropheight: ${parseInt(data[c].substring(10, 14))}\n`; + } + } + return returns; +} + +function hevc(file) { + //check if the file is already hevc, it will not be transcoded if true + if (file.ffProbeData.streams[0].codec_name) { + if ("hevc".toLowerCase().includes(file.ffProbeData.streams[0].codec_name.toLowerCase())) { + return 0; + } else { + return 1; + } + } +} + +function decoder_string(file) { + var decoder = ``; //decoder, before the input + + //use the correct decoder + if (file.video_codec_name == 'h263') { + decoder = `-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 + decoder = `-c:v h264_cuvid` + } + } + else if (file.video_codec_name == 'mjpeg') { + decoder = `c:v mjpeg_cuvid` + } + else if (file.video_codec_name == 'mpeg1') { + decoder = `-c:v mpeg1_cuvid` + } + else if (file.video_codec_name == 'mpeg2') { + decoder = `-c:v mpeg2_cuvid` + } + else if (file.video_codec_name == 'vc1') { + decoder = `-c:v vc1_cuvid` + } + else if (file.video_codec_name == 'vp8') { + decoder = `-c:v vp8_cuvid` + } + else if (file.video_codec_name == 'vp9') { + decoder = `-c:v vp9_cuvid` + } + + return decoder; +} + +function crop_decider(file, crop_height) { + var returns = { + crop: `0`, //sets the crop filter + log: ``, + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + if (file.ffProbeData.streams[i].width !== undefined) { + var imageWidth = file.ffProbeData.streams[i].width; + var imageHeight = file.ffProbeData.streams[i].height; + break; + } + } + + var min_crop = parseInt(imageHeight*.98); //if the crop value is larger than this the file won't be cropped + + //tree for resolution : quality + if (imageWidth >= 1300) { //file will be encoded if the resolution is 1080p, or greater (it will be downscaled) + //crop only if it is a larger crop than 1%; + if (crop_height < min_crop) { + var crop_hdis = parseInt((imageHeight-crop_height)/2); + if (crop_height >= 790) { + returns.crop = `-filter:0 crop=${imageWidth}:${crop_height}:0:${crop_hdis}`; + returns.log += `☑ - crop is larger than 1%\n`; + } + } else { + returns.log += `☒ - Crop is not necessary\n`; + } + } else if(imageWidth < 1300 && file.meta.ImageWidth >= 770) { + //crop only if it is a larger crop than 1%; + if (crop_height < min_crop) { + var crop_hdis = parseInt((imageHeight-crop_height)/2); + if (crop_height >= 530) { + returns.crop = `-filter:0 crop=${imageWidth}:${crop_height}:0:${crop_hdis}`; + returns.log += `☑ - crop is larger than 1%\n`; + } + } else { + returns.log += `☒ - Crop is not necessary\n`; + } + } else if(imageWidth < 770) { //file won't be cropped at this resolution + returns.log += `No crop: Resolution < 720p\n`; + } + + return returns; +} + +function size_check(file, min_bitrate) { + var duration = file.meta.Duration; //duration of video in seconds + var source = file.meta.SourceFile; //source file + var stats = fs.statSync(source); + var size = stats["size"]/1000000000; + size = size.toFixed(2); + var returns = { + size: 0, + log: `` + } + + //tree for resolution : quality + if (file.video_resolution === "1080p" || file.video_resolution === "4KUHD") { //file will be encoded if the resolution is 1080p, or greater (it will be downscaled) + var min_transcode_size = (min_bitrate * duration * 0.125)/1000000; //minimum size in GB for transcode + min_transcode_size = min_transcode_size.toFixed(2); + + //check if file is large enough for transcode + if (size >= (min_bitrate * duration * 0.125)/1000000) { + returns.log += `☑ - ${size}GB > ${min_transcode_size}GB\n`; + returns.size = 1; + } else { + returns.log += `☒ - ${size}GB < ${min_transcode_size}GB\n`; + } + } else if(file.video_resolution === "720p") { //file will be encoded if the resolution is 720p + var min_transcode_size = ((min_bitrate/2) * duration * 0.125)/1000000; //minimum size in GB for transcode + min_transcode_size = min_transcode_size.toFixed(2); + + //check if file is large enough for transcode + if (size >= ((min_bitrate/2) * duration * 0.125)/1000000) { + returns.log += `☑ - ${size}GB > ${min_transcode_size}GB\n`; + returns.size = 1; + } else { + returns.log += `☒ - ${size}GB < ${min_transcode_size}GB\n`; + } + } else if(file.video_resolution === "480p" || file.video_resolution === "576p") { //file will be encoded if the resolution is 480p or 576p + var min_transcode_size = ((min_bitrate/4) * duration * 0.125)/1000000; //minimum size in GB for transcode + min_transcode_size = min_transcode_size.toFixed(2); + + //check if file is large enough for transcode + if (size >= ((min_bitrate/4) * duration * 0.125)/1000000) { + returns.log += `☑ - ${size}GB > ${min_transcode_size}GB\n`; + returns.size = 1; + } else { + returns.log += `☒ - ${size}GB < ${min_transcode_size}GB\n`; + } + } + + return returns; +} + +function error_fix(file) { + var fix = { + sub_codec: 0, //changes to 1 if unwanted codec is found + muxing: 0 + } + + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + + //these subtitle codecs don't fit in a mkv container + if (file.ffProbeData.streams[i].codec_name && file.ffProbeData.streams[i].codec_type) { + if (file.ffProbeData.streams[i].codec_name.toLowerCase() == "eia_608" || file.ffProbeData.streams[i].codec_name.toLowerCase() == "mov_text" && file.ffProbeData.streams[i].codec_type.toLowerCase.includes("sub") && response.container == '.mkv') { + fix.sub_codec = 1; + } + + //mitigate TrueHD audio causing Too many packets error + if (file.ffProbeData.streams[i].codec_name.toLowerCase() == "truehd" || file.ffProbeData.streams[i].codec_name.toLowerCase() == "dts" && file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" ) { + fix.muxing = 1; + } + } + } + + return fix; +} + +function encoder_string(file, avg_rate, max_rate) { + var encoder = ``; //encoder + var fix = error_fix(file); + var sub = ``; + + //tree for resolution : quality + if (file.video_resolution === "1080p" || file.video_resolution === "4KUHD") { //file will be encoded if the resolution is 1080p, or greater (it will be downscaled) + encoder += ` -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 26 -b:v ${avg_rate}k -maxrate:v ${max_rate}k`; //-qp 26 + } else if(file.video_resolution === "720p") { //file will be encoded if the resolution is 720p + encoder += ` -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 26 -b:v ${avg_rate/2}k -maxrate:v ${max_rate/2}k`; //-qp 28 + } else if(file.video_resolution === "480p" || file.video_resolution === "576p") { //file will be encoded if the resolution is 480p or 576p + encoder += ` -pix_fmt p010le -rc:v vbr_hq -qmin 0 -cq:v 26 -b:v ${avg_rate/4}k -maxrate:v ${max_rate/4}k`; //-qp 30 + } + encoder += ` -c:v hevc_nvenc -preset slow -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 -a53cc 0 -dn`; + + if (fix.sub_codec == 1) { + for (var i = 0; i < file.ffProbeData.streams.length; i++) { + if (file.ffProbeData.streams[i].codec_name) { + if (file.ffProbeData.streams[i].codec_name.toLowerCase() == "eia_608" || file.ffProbeData.streams[i].codec_name.toLowerCase() == "mov_text" && file.ffProbeData.streams[i].codec_type.toLowerCase().includes("sub")) { + sub += ` -c:${i} ass`; + } else { + if (file.ffProbeData.streams[i].codec_type) { + if (file.ffProbeData.streams[i].codec_type.toLowerCase().includes("sub")) { + sub += ` -c:${i} copy`; + } + } + } + } + } + } else { + sub = ` -c:s copy` + } + + if (fix.muxing == 1) { + encoder += ` -max_muxing_queue_size 2048`; + } + + return encoder + ` -c:a copy` + sub; +} + +function encoder_string_full(file, highres, crop, avg_rate, max_rate) { + var encoder = encoder_string(file, avg_rate, max_rate); + + console.log(`crop filter: ` + crop) + + if (highres == 1 && crop != "0") { + return crop + `,scale=-1:1920 ` + encoder; + } else if (highres == 1) { + return `-filter:0 scale=-1:1920 ` + encoder; + } else if (crop != "0") { + return crop + encoder; + } else { + return encoder; + } +} + +module.exports.details = details; +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js b/Community/Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js index 19df91b..d0167c9 100644 --- a/Community/Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js +++ b/Community/Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js @@ -5,12 +5,13 @@ function details() { return { id: "Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs", + Stage: "Pre-processing", Name: "Sparticus 4K +AC3 No Subs Original container", Type: "Video", - Description: `[Contains built-in filter] This plugin for 4K video removes subs. If no AC3 track exists, it adds one (max 5.1 channels). If only an AC3 commentary track exists, it adds a new AC3 main track (max 5.1 channels). The output container is the same as the original file. \n\n -`, + Description: `[Contains built-in filter] This plugin for 4K video removes subs. If no AC3 track exists, it adds one (max 5.1 channels). If only an AC3 commentary track exists, it adds a new AC3 main track (max 5.1 channels). The output container is the same as the original file. \n\n`, Version: "1.04", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_fd5T_Sparticus_4K_AC3_No_Subs.js", + Tags:'pre-processing,ffmpeg', } } diff --git a/Community/Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js b/Community/Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js index e8f1d5d..6f0e636 100644 --- a/Community/Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js +++ b/Community/Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle", + Stage: "Pre-processing", Name: "Drawmonster MP4 Stereo AAC, No Subs, No title meta data ", Type: "Video", Description: `[Contains built-in filter] This plugin removes subs, metadata (if a title exists) and adds a stereo 192kbit AAC track if an AAC track (English or any) doesn't exist. The output container is mp4. \n\n `, Version: "1.07", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_hk75_Drawmonster_MP4_AAC_No_Subs_No_metaTitle.js", + Tags:'pre-processing,ffmpeg', } } diff --git a/Community/Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js b/Community/Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js index 0b01b8b..b542b26 100644 --- a/Community/Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js +++ b/Community/Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle", + Stage: "Pre-processing", Name: "GilbN MP4 Stereo AAC, No title meta data ", Type: "Video", Description: `[Contains built-in filter] This plugin removes metadata (if a title exists) and adds a stereo 192kbit AAC track if an AAC track (any) doesn't exist. The output container is mp4. \n\n `, Version: "1.01", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_hk76_GilbN_MP4_AAC_No_metaTitle.js", + Tags:'pre-processing,ffmpeg', } } diff --git a/Community/Tdarr_Plugin_lmg1_Reorder_Streams.js b/Community/Tdarr_Plugin_lmg1_Reorder_Streams.js index d81312a..c338f73 100644 --- a/Community/Tdarr_Plugin_lmg1_Reorder_Streams.js +++ b/Community/Tdarr_Plugin_lmg1_Reorder_Streams.js @@ -2,11 +2,13 @@ function details() { return { id: "Tdarr_Plugin_lmg1_Reorder_Streams", + Stage: "Pre-processing", Name: "Tdarr_Plugin_lmg1_Reorder_Streams ", Type: "Video", Description: `[Contains built-in filter] This plugin will move the video stream to the front so Tdarr will recognize the codec correctly.\n\n`, Version: "1.00", - Link: "https://github.com/luigi311/Tdarr_Plugin_lmg1_Reorder_Streams" + Link: "https://github.com/luigi311/Tdarr_Plugin_lmg1_Reorder_Streams", + Tags:'pre-processing,ffmpeg', } } diff --git a/Community/Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js b/Community/Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js index 6c1c538..f27fbea 100644 --- a/Community/Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js +++ b/Community/Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta", + Stage: "Pre-processing", Name: "Drawmonster No title meta data ", Type: "Video", Description: `[Contains built-in filter] This plugin removes metadata (if a title exists). The output container is the same as the original. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_nc7x_Drawmonster_No_Title_Meta.js", + Tags:'pre-processing,ffmpeg', } } diff --git a/Community/Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation.js b/Community/Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation.js new file mode 100644 index 0000000..cbc49a5 --- /dev/null +++ b/Community/Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation.js @@ -0,0 +1,66 @@ +function details() { + return { + id: "Tdarr_Plugin_r002_rootuser_FFMPEG_HQ_HEVC_MKV_Animation", + Stage: "Pre-processing", + Name: "FFMPEG HQ 10-bit HEVC MKV for Animation", + Type: "Video", + Operation:"Transcode", + Description: `[Contains built-in filter] High Quality FFMPEG transcoding settings for Animation. Converts all audio to AAC 512K. Preserves track names, metadata and attachments/fonts. Proper use of x265-params. CRF 18. Preset medium. 10-Bit Video encoding. Skips h.265 encoded videos. The output container is mkv. \n\n`, + Version: "1.1", + Link: "", + Tags:"pre-processing,ffmpeg,h265,aac,10bit,anime," + } +} + +function plugin(file) { + var transcode = 0; //if this var changes to 1 the file will be transcoded + +//default values that will be returned + var response = { + processFile: false, + preset: '', + container: '.mkv', + handBrakeMode: false, + FFmpegMode: true, + reQueueAfter: true, + infoLog: '' + } + +//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 { + 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 + } + +//Transcoding options + { + response.preset = ',-map 0 -c:s copy -movflags use_metadata_tags -c:a aac -b:a 512k -c:v:0 libx265 -preset medium -x265-params crf=18:tune=animation:qcomp=0.7:aq-strength=1.1 -pix_fmt yuv420p10le -f matroska' + 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} but is not hevc!\n` + response.infoLog += `☒File will be transcoded!\n` + } + + return response +} + +module.exports.details = details; + +module.exports.plugin = plugin; diff --git a/Community/Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV.js b/Community/Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV.js new file mode 100644 index 0000000..edbc270 --- /dev/null +++ b/Community/Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV.js @@ -0,0 +1,171 @@ +function details() { + return { + id: "Tdarr_Plugin_raf4_Floorpie_FFmpeg_Tiered_HEVC_MKV", + Stage: "Pre-processing", + Name: "FFmpeg Tiered HEVC MKV", + Type: "Video", + Operation:"Transcode", + Description: `[Contains built-in filter] This plugin uses different FFmpeg transcoding settings for 480p,576p,720p and 1080p. If files are not in hevc they will be transcoded. The output container is mkv. \n\n`, + Version: "1.01", + Link: "", + Tags:"pre-processing,ffmpeg,h265,video only," + } +} + +function plugin(file) { + var transcode = 0; //if this var changes to 1 the file will be transcoded + +//default values that will be returned + var response = { + processFile: false, + preset: '', + container: '.mkv', + handBrakeMode: false, + FFmpegMode: true, + reQueueAfter: true, + infoLog: '' + } + +//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 { + 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 + } + +//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" ) { + if (file.video_codec_name == 'h263') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else if (file.video_codec_name == 'h264') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else if (file.video_codec_name == 'mjpeg') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else if (file.video_codec_name == 'mpeg1') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else if (file.video_codec_name == 'mpeg2') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else if (file.video_codec_name == 'mpeg4') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else if (file.video_codec_name == 'vc1') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else if (file.video_codec_name == 'vp8') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else if (file.video_codec_name == 'vp9') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + else { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 27' + } + + 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") { + if (file.video_codec_name == 'h263') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else if (file.video_codec_name == 'h264') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else if (file.video_codec_name == 'mjpeg') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else if (file.video_codec_name == 'mpeg1') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else if (file.video_codec_name == 'mpeg2') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else if (file.video_codec_name == 'mpeg4') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else if (file.video_codec_name == 'vc1') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else if (file.video_codec_name == 'vp8') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else if (file.video_codec_name == 'vp9') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + else { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 25' + } + + 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") { + if (file.video_codec_name == 'h263') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else if (file.video_codec_name == 'h264') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else if (file.video_codec_name == 'mjpeg') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else if (file.video_codec_name == 'mpeg1') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else if (file.video_codec_name == 'mpeg2') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else if (file.video_codec_name == 'mpeg4') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else if (file.video_codec_name == 'vc1') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else if (file.video_codec_name == 'vp8') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else if (file.video_codec_name == 'vp9') { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + else { + response.preset = ',-map 0:v -map 0:s? -c:s srt -map 0:a -c copy -c:v:0 libx265 -preset fast -crf 23' + } + + 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} but is not hevc!\n` + response.infoLog += `☒File will be transcoded!\n` + } + + return response +} + +module.exports.details = details; + +module.exports.plugin = plugin; \ No newline at end of file diff --git a/Community/Tdarr_Plugin_s710_nick_h265_nvenc_4K.js b/Community/Tdarr_Plugin_s710_nick_h265_nvenc_4K.js index 2206769..c66e845 100644 --- a/Community/Tdarr_Plugin_s710_nick_h265_nvenc_4K.js +++ b/Community/Tdarr_Plugin_s710_nick_h265_nvenc_4K.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_s710_nick_h265_nvenc_4K", + Stage: "Pre-processing", Name: "Nick H265 NVENC 4K", Type: "Video", Description: `[Contains built-in filter] This plugin transcodes all 4K videos to h265 using nvenc (if not in h265 already). For 4K and files in other resolutions: If not in mkv the file is remuxed into mkv. If the English language track is not in AC3,EAC3 or DTS then an AC3 track is added.\n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_s710_nick_h265_nvenc_4K.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_s710_nick_h265_nvenc_4K.js", + Tags:'pre-processing,ffmpeg,nvenc h265', } } diff --git a/Community/Tdarr_Plugin_s7x8_winsome_h265.js b/Community/Tdarr_Plugin_s7x8_winsome_h265.js index 2e6d7d1..379bbfc 100644 --- a/Community/Tdarr_Plugin_s7x8_winsome_h265.js +++ b/Community/Tdarr_Plugin_s7x8_winsome_h265.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_s7x8_winsome_h265", + Stage: "Pre-processing", Name: "Winsome H265 ", Type: "Video", Description: `[Contains built-in filter] This plugin transcodes all videos to h265 (if not in h265 already) and remuxes if not in mkv. If the English language track is not in AC3,EAC3 or DTS then an AC3 track is added.\n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_s7x8_winsome_h265.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_s7x8_winsome_h265.js", + Tags:'pre-processing,handbrake,ffmpeg,h265', } } diff --git a/Community/Tdarr_Plugin_s7x9_winsome_h265_10bit.js b/Community/Tdarr_Plugin_s7x9_winsome_h265_10bit.js index af34167..9216486 100644 --- a/Community/Tdarr_Plugin_s7x9_winsome_h265_10bit.js +++ b/Community/Tdarr_Plugin_s7x9_winsome_h265_10bit.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_s7x9_winsome_h265_10bit", + Stage: "Pre-processing", Name: "Winsome H265 10 bit ", Type: "Video", Description: `[Contains built-in filter] This plugin transcodes all videos to h265 10 bit (if not in h265 already) and remuxes if not in mkv. If the English language track is not in AC3,EAC3 or DTS then an AC3 track is added.\n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_s7x9_winsome_h265_10bit.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_s7x9_winsome_h265_10bit.js", + Tags:'pre-processing,handbrake,ffmpeg,h265', } } diff --git a/Community/Tdarr_Plugin_s7x9_winsome_h265_nvenc.js b/Community/Tdarr_Plugin_s7x9_winsome_h265_nvenc.js index 744a9ee..cbf0bdc 100644 --- a/Community/Tdarr_Plugin_s7x9_winsome_h265_nvenc.js +++ b/Community/Tdarr_Plugin_s7x9_winsome_h265_nvenc.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_s7x9_winsome_h265_nvenc", + Stage: "Pre-processing", Name: "Winsome H265 NVENC", Type: "Video", Description: `[Contains built-in filter] This plugin transcodes all videos to h265 using nvenc (if not in h265 already) and remuxes if not in mkv. If the English language track is not in AC3,EAC3 or DTS then an AC3 track is added.\n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_s7x9_winsome_h265_nvenc.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_s7x9_winsome_h265_nvenc.js", + Tags:'pre-processing,handbrake,ffmpeg,nvenc h265', } } diff --git a/Community/Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js b/Community/Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js index 429cb3e..95c2061 100644 --- a/Community/Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js +++ b/Community/Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_sdd3_Remove_Commentary_Tracks", + Stage: "Pre-processing", Name: "Remove video commentary tracks", Type: "Video", - Operation:"Remux", + Operation: "Remux", Description: `[Contains built-in filter] If commentary tracks are detected, they will be removed. \n\n`, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_sdd3_Remove_Commentary_Tracks.js", + Tags:'pre-processing,ffmpeg,audio only', } } @@ -44,6 +46,7 @@ function plugin(file) { var audioIdx = -1 var hasCommentaryTrack = false + var ffmpegCommandInsert = "" for (var i = 0; i < file.ffProbeData.streams.length; i++) { @@ -59,11 +62,14 @@ function plugin(file) { //check if commentary track and passing audio stream number try { if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary")) { + + ffmpegCommandInsert += ` -map -0:a:${audioIdx}` hasCommentaryTrack = true } - } catch (err) { } + } catch (err) { + } } diff --git a/Community/Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js b/Community/Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js index 2ecaf1e..b662055 100644 --- a/Community/Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js +++ b/Community/Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js @@ -5,12 +5,14 @@ function details() { return { id: "Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio", + Stage: "Pre-processing", Name: "Remove Non English Audio ", Type: "Video", Description: `[Contains built-in filter] This plugin removes audio tracks which are not English or are not undefined. It ensures at least 1 audio track is left in any language. \n\n `, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_sdf5_Thierrrrry_Remove_Non_English_Audio.js", + Tags:'pre-processing,ffmpeg,audio only', } } @@ -35,7 +37,7 @@ function plugin(file) { response.FFmpegMode = true - + //check if files is video if (file.fileMedium !== "video") { @@ -47,22 +49,25 @@ function plugin(file) { return response - } + } - var ffmpegCommandInsert = '' - var audioIdx = -1 - var hasNonEngTrack = false - var audioStreamsRemoved = 0 + var ffmpegCommandInsert = '' + var audioIdx = -1 + var hasNonEngTrack = false + var audioStreamsRemoved = 0 - var audioStreamCount = file.ffProbeData.streams.filter(row => (row.codec_type.toLowerCase() == "audio")).length; + //count number of audio streams + var audioStreamCount = file.ffProbeData.streams.filter(row => (row.codec_type.toLowerCase() == "audio")).length; - console.log("audioStreamCount:" + audioStreamCount) + console.log("audioStreamCount:" + audioStreamCount) for (var i = 0; i < file.ffProbeData.streams.length; i++) { + + //check if current stream is audio, update audioIdx if so try { if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") { audioIdx++ @@ -70,18 +75,15 @@ function plugin(file) { } catch (err) { } - - - try { if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && !(file.ffProbeData.streams[i].tags.language.toLowerCase().includes('eng') || file.ffProbeData.streams[i].tags.language.toLowerCase().includes('und'))) { audioStreamsRemoved++ - if(audioStreamsRemoved == audioStreamCount){ + if (audioStreamsRemoved == audioStreamCount) { break; } - + ffmpegCommandInsert += ` -map -0:a:${audioIdx}` hasNonEngTrack = true @@ -93,27 +95,27 @@ function plugin(file) { - if (hasNonEngTrack === true) { + if (hasNonEngTrack === true) { - response.processFile = true; - response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy` - response.container = '.' + file.container - response.handBrakeMode = false - response.FFmpegMode = true - response.reQueueAfter = true; - response.infoLog += "☒File contains tracks which are not english or undefined. Removing! \n" - return response + response.processFile = true; + response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy` + response.container = '.' + file.container + response.handBrakeMode = false + response.FFmpegMode = true + response.reQueueAfter = true; + response.infoLog += "☒File contains tracks which are not english or undefined. Removing! \n" + return response - } else { + } else { - response.infoLog += "☑File doesn't contain tracks which are not english or undefined! \n" + response.infoLog += "☑File doesn't contain tracks which are not english or undefined! \n" - } + } + + response.processFile = false; + return response - response.processFile = false; - return response - } module.exports.details = details; diff --git a/Community/Tdarr_Plugin_x7ab_Remove_Subs.js b/Community/Tdarr_Plugin_x7ab_Remove_Subs.js index f1f39a0..876f4d0 100644 --- a/Community/Tdarr_Plugin_x7ab_Remove_Subs.js +++ b/Community/Tdarr_Plugin_x7ab_Remove_Subs.js @@ -5,11 +5,13 @@ function details() { return { id: "Tdarr_Plugin_x7ab_Remove_Subs", + Stage: "Pre-processing", Name: "Remove subtitles ", Type: "Video", Description: `[Contains built-in filter] This plugin removes subtitles if detected. The output container is the same as the original. \n\n`, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_x7ab_Remove_Subs.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_x7ab_Remove_Subs.js", + Tags:'pre-processing,ffmpeg,subtitle only', } } diff --git a/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js b/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js index da8f3e9..a326a7b 100644 --- a/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js +++ b/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js @@ -4,12 +4,14 @@ function details() { return { id: "Tdarr_Plugin_x7ac_Remove_Closed_Captions", + Stage: "Pre-processing", Name: "Remove closed captions", Type: "Video", Operation: "Remux", Description: "[Contains built-in filter] If detected, closed captions (XDS,608,708) will be removed.", Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js" + Link: "https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_x7ac_Remove_Closed_Captions.js", + Tags:'pre-processing,ffmpeg,subtitle only', } diff --git a/Community/Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js b/Community/Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js index 85ab199..2bda67c 100644 --- a/Community/Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js +++ b/Community/Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js @@ -4,13 +4,15 @@ function details() { return { - id: "Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js", + id: "Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium", + Stage: "Pre-processing", Name: "TheRealShadoh FFmpeg Subs Medium, video MP4, audio AAC, keep subs. ", 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 `, Version: "1.00", - Link: "https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js" + Link: "https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z0ab_TheRealShadoh_FFmpeg_Subs_H264_Medium.js", + Tags:'pre-processing,ffmpeg,h264', } } @@ -53,11 +55,9 @@ function plugin(file) { for (var i = 0; i < file.ffProbeData.streams.length; i++) { try { - - if(file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle"){ - + let streamData = file.ffProbeData.streams[i]; + if(streamData.codec_type.toLowerCase() == "subtitle" && streamData.codec_name != "mov_text"){ hasSubs = true - } } catch (err) { } } @@ -122,7 +122,7 @@ function plugin(file) { response.FFmpegMode = true return response }else{ - response.infoLog += "☑File has no title metadata" + response.infoLog += "☑File has no title metadata \n" } if(!jsonString.includes("aac")){ @@ -140,15 +140,14 @@ function plugin(file) { if(hasSubs){ - response.infoLog += "☒File has subs \n" + 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 subs \n" + response.infoLog += "☑File has no/compatible subs \n" } response.infoLog += "☑File meets conditions! \n" diff --git a/Community/Tdarr_Plugin_z18s_rename_files_based_on_codec.js b/Community/Tdarr_Plugin_z18s_rename_files_based_on_codec.js new file mode 100644 index 0000000..4e9b3d0 --- /dev/null +++ b/Community/Tdarr_Plugin_z18s_rename_files_based_on_codec.js @@ -0,0 +1,72 @@ + + + +module.exports.details = function details() { + + return { + id: "Tdarr_Plugin_z18s_rename_files_based_on_codec", + Stage: "Post-processing", + Name: "Rename based on codec", + Type: "Video", + Operation: "", + Description: `[TESTING][Contains built-in filter]This plugin renames 264 files to 265 or vice versa depending on codec. \n\n`, + Version: "1.00", + Link: "", + Tags:"post-processing", + + } + +} + +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 (file.ffProbeData.streams[0].codec_name == 'hevc' && file._id.includes('264')) { + file._id = file._id.replace("264", "265"); + file.file = file.file.replace("264", "265"); + } + + + + if (file.ffProbeData.streams[0].codec_name == 'h264' && file._id.includes('265')) { + file._id = file._id.replace("265", "264"); + file.file = file.file.replace("265", "264"); + } + + if (file.ffProbeData.streams[0].codec_name == 'h264' && file._id.includes('hevc')) { + file._id = file._id.replace("hevc", "264"); + file.file = file.file.replace("hevc", "264"); + } + + if (fileNameOld != file._id) { + + fsextra.moveSync(fileNameOld, file._id, { + overwrite: true + }) + + var response = { + file, + removeFromDB: false, + updateDB: true, + } + + return response + + + } + + } catch (err) { console.log(err) } +} diff --git a/Community/Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js b/Community/Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js index a5bbdc0..2f9b6d6 100644 --- a/Community/Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js +++ b/Community/Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js @@ -4,13 +4,15 @@ function details() { return { - id: "Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js", + id: "Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast", + Stage: "Pre-processing", Name: "TheRealShadoh FFmpeg Subs Fast, video MP4, audio AAC, keep subs. ", 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 `, Version: "1.00", - Link: "https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js" + Link: "https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z1ab_TheRealShadoh_FFmpeg_Subs_H264_Fast.js", + Tags:'pre-processing,ffmpeg,h264', } } @@ -53,12 +55,10 @@ function plugin(file) { for (var i = 0; i < file.ffProbeData.streams.length; i++) { try { - - if(file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle"){ - - hasSubs = true - - } + let streamData = file.ffProbeData.streams[i]; + if(streamData.codec_type.toLowerCase() == "subtitle" && streamData.codec_name != "mov_text"){ + hasSubs = true + } } catch (err) { } } @@ -121,7 +121,7 @@ function plugin(file) { response.FFmpegMode = true return response }else{ - response.infoLog += "☑File has no title metadata" + response.infoLog += "☑File has no title metadata \n" } if(!jsonString.includes("aac")){ @@ -138,16 +138,16 @@ function plugin(file) { } if(hasSubs){ - - response.infoLog += "☒File has subs \n" + + 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 subs \n" + }else{ + response.infoLog += "☑File has no/compatible subs \n" } response.infoLog += "☑File meets conditions! \n" diff --git a/Community/Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js b/Community/Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js index f7f39ed..45ed83e 100644 --- a/Community/Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js +++ b/Community/Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js @@ -4,13 +4,15 @@ function details() { return { - id: "Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js", + id: "Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow", + Stage: "Pre-processing", Name: "TheRealShadoh FFmpeg Subs Slow, video MP4, audio AAC, keep subs. ", 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 `, Version: "1.00", - Link: "https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js" + Link: "https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z2ab_TheRealShadoh_FFmpeg_Subs_H264_Slow.js", + Tags:'pre-processing,ffmpeg,h264', } } @@ -53,12 +55,10 @@ function plugin(file) { for (var i = 0; i < file.ffProbeData.streams.length; i++) { try { - - if(file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle"){ - - hasSubs = true - - } + let streamData = file.ffProbeData.streams[i]; + if(streamData.codec_type.toLowerCase() == "subtitle" && streamData.codec_name != "mov_text"){ + hasSubs = true + } } catch (err) { } } @@ -122,7 +122,7 @@ function plugin(file) { response.FFmpegMode = true return response }else{ - response.infoLog += "☑File has no title metadata" + response.infoLog += "☑File has no title metadata \n" } if(!jsonString.includes("aac")){ @@ -140,15 +140,15 @@ function plugin(file) { if(hasSubs){ - response.infoLog += "☒File has subs \n" + 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 subs \n" + }else{ + response.infoLog += "☑File has no/compatible subs \n" } response.infoLog += "☑File meets conditions! \n" diff --git a/Community/Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast.js b/Community/Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast.js index 2211f6c..b02b45a 100644 --- a/Community/Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast.js +++ b/Community/Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast.js @@ -4,13 +4,15 @@ function details() { return { - id: "Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast.js", + id: "Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_VeryFast", + Stage: "Pre-processing", Name: "TheRealShadoh FFmpeg Subs VeryFast, video MP4, audio AAC, keep subs. ", 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 `, Version: "1.00", - Link: "https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_Veryfast.js" + Link: "https://github.com/TheRealShadoh/Tdarr_Plugins/blob/master/Community/Tdarr_Plugin_z3ab_TheRealShadoh_FFmpeg_Subs_H264_Veryfast.js", + Tags:'pre-processing,ffmpeg,h264', } } @@ -53,12 +55,10 @@ function plugin(file) { for (var i = 0; i < file.ffProbeData.streams.length; i++) { try { - - if(file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle"){ - - hasSubs = true - - } + let streamData = file.ffProbeData.streams[i]; + if(streamData.codec_type.toLowerCase() == "subtitle" && streamData.codec_name != "mov_text"){ + hasSubs = true + } } catch (err) { } } @@ -122,7 +122,7 @@ function plugin(file) { response.FFmpegMode = true return response }else{ - response.infoLog += "☑File has no title metadata" + response.infoLog += "☑File has no title metadata \n" } if(!jsonString.includes("aac")){ @@ -140,7 +140,7 @@ function plugin(file) { if(hasSubs){ - response.infoLog += "☒File has subs \n" + 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; @@ -148,7 +148,7 @@ function plugin(file) { return response }else{ - response.infoLog += "☑File has no subs \n" + response.infoLog += "☑File has no/compatible subs \n" } response.infoLog += "☑File meets conditions! \n" diff --git a/README.md b/README.md index 9083939..3528183 100644 --- a/README.md +++ b/README.md @@ -7,10 +7,11 @@ There are two types of plugin: Steps for creating a community or local plugin. -1.Download Tdarr_Plugin_nc7x_Example.js from the following repository: +1. Copy the following pre and post-processing plugin examples: -https://github.com/HaveAGitGat/Tdarr_Plugin_nc7x_Example +https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Tdarr_Plugin_aaaa_Pre_Proc_Example.js +https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Tdarr_Plugin_zzzz_Post_Proc_Example.js 2.Determine an id for your plugin. Every id must start with 'Tdarr_Plugin_xxxx' where xxxx is a random mini id containing the following: Numeric digits (0-9) diff --git a/Tdarr_Plugin_aaaa_Pre_Proc_Example.js b/Tdarr_Plugin_aaaa_Pre_Proc_Example.js new file mode 100644 index 0000000..a1c3d63 --- /dev/null +++ b/Tdarr_Plugin_aaaa_Pre_Proc_Example.js @@ -0,0 +1,384 @@ + + + +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. + Name: "No title meta data ", + Type: "Video", + Operation: "Transcode", + Description: `This plugin removes metadata (if a title exists). The output container is the same as the original. \n\n`, + Version: "1.00", + 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 + + Inputs: [ //(Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI + { + name: 'language', + tooltip: `Enter one language tag here for the language of the subtitles you'd like to keep. + + \\nExample:\\n + eng + + \\nExample:\\n + + fr + + \\nExample:\\n + + de` //Each line following `Example:` will be clearly formatted. \\n used for line breaks + }, + { + name: 'channels', + tooltip: `Desired audio channel number. + + \\nExample:\\n + 2` + }, + ] + + } + +} + +module.exports.plugin = function plugin(file, librarySettings, inputs) { + + + //Must return this object at some point in the function else plugin will fail. + + var response = { + + 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 a comma, followed by the output argument. + // Examples + //HandBrake + // '-Z "Very Fast 1080p30"' + //FFmpeg + // '-sn,-map_metadata -1 -c:v copy -c:a copy' + container: '.mp4', // The container of the transcoded output file. + handBrakeMode: false, //Set whether to use HandBrake or FFmpeg for transcoding + 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. + 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. + // Give reasons why it has been skipped ('File has no title metadata, File meets conditions!') + + //Optional (include together) + file, + removeFromDB: false, //Tell Tdarr to remove file from 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.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. + response.container = '.' + file.container + + //We will use FFmpeg for this procedure. + response.FFmpegMode = true + + + + //Check if file has title metadata + if (file.meta.Title != undefined) { + //if so, remove it + + response.infoLog += " File has title metadata" + response.preset = ',-map_metadata -1 -c:v copy -c:a copy' + response.processFile = true + return response + } else { + response.infoLog += " File has no title metadata" + } + + + response.infoLog += " File meets conditions!" + return response +} + + +module.exports.onTranscodeSuccess = function onTranscodeSuccess(file, librarySettings, inputs) { + + console.log('Transcode success! Now do some stuff with the newly scanned file.') + + + //Optional response if you need to modify database + var response = { + file, + removeFromDB: false, + updateDB: false, + } + + return response +} + +module.exports.onTranscodeError = function onTranscodeError(file, librarySettings, inputs) { + + console.log('Transcode fail! Now do some stuff with the original file.') + + + //Optional response if you need to modify database + var response = { + file, + removeFromDB: false, + updateDB: false, + } + + return response +} + + + + + + + +//Example file object: + // { + // _id: 'C:/Users/H/Desktop/Test Input1/Sample.mp4', + // DB: 'ZRPDmnmpyuAEQi7nG', + // HealthCheck: 'Not attempted', + // TranscodeDecisionMaker: 'Not attempted', + // bit_rate: 1690430.4, + // container: 'mp4', + // createdAt: 2019-09-26T06:46:31.929Z, + // 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: '1/50', + // codec_tag_string: 'avc1', + // codec_tag: '0x31637661', + // width: 1280, + // height: 720, + // coded_width: 1280, + // coded_height: 720, + // has_b_frames: 0, + // sample_aspect_ratio: '1:1', + // display_aspect_ratio: '16:9', + // pix_fmt: 'yuv420p', + // level: 31, + // chroma_location: 'left', + // refs: 1, + // is_avc: 'true', + // nal_length_size: '4', + // r_frame_rate: '25/1', + // avg_frame_rate: '25/1', + // time_base: '1/12800', + // start_pts: 0, + // start_time: '0.000000', + // duration_ts: 67584, + // duration: '5.280000', + // bit_rate: '1205959', + // bits_per_raw_sample: '8', + // nb_frames: '132', + // 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: '1970-01-01T00:00:00.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: 6, + // channel_layout: '5.1', + // 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: 254976, + // duration: '5.312000', + // bit_rate: '384828', + // max_bit_rate: '400392', + // nb_frames: '249', + // 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: '1970-01-01T00:00:00.000000Z', + // language: 'und', + // handler_name: 'SoundHandler' } } ] }, + // ffProbeRead: 'success', + // file: 'C:/Users/H/Desktop/Test Input1/Sample.mp4', + // fileMedium: 'video', + // file_size: 1.056519, + // meta: + // { SourceFile: 'C:/Users/H/Desktop/Test Input1/Sample.mp4', + // errors: [], + // Duration: 5.312, + // PreviewDuration: 0, + // SelectionDuration: 0, + // TrackDuration: 5.28, + // MediaDuration: 5.312, + // ExifToolVersion: 11.65, + // FileName: 'Sample.mp4', + // Directory: 'C:/Users/H/Desktop/Test Input1', + // FileSize: '1032 kB', + // FileModifyDate: + // { year: 2019, + // month: 9, + // day: 24, + // hour: 7, + // minute: 24, + // second: 22, + // millisecond: 0, + // tzoffsetMinutes: 60, + // rawValue: '2019:09:24 07:24:22+01:00' }, + // FileAccessDate: + // { year: 2019, + // month: 9, + // day: 26, + // hour: 7, + // minute: 44, + // second: 30, + // millisecond: 0, + // tzoffsetMinutes: 60, + // rawValue: '2019:09:26 07:44:30+01:00' }, + // FileCreateDate: + // { year: 2019, + // month: 9, + // day: 26, + // hour: 7, + // minute: 44, + // second: 30, + // millisecond: 0, + // tzoffsetMinutes: 60, + // rawValue: '2019:09:26 07:44:30+01:00' }, + // FilePermissions: 'rw-rw-rw-', + // FileType: 'MP4', + // FileTypeExtension: 'mp4', + // MIMEType: 'video/mp4', + // MajorBrand: 'MP4 Base Media v1 [IS0 14496-12:2003]', + // MinorVersion: '0.2.0', + // CompatibleBrands: [ 'isom', 'iso2', 'avc1', 'mp41' ], + // MovieDataSize: 0, + // MovieDataOffset: 1051515, + // MovieHeaderVersion: 0, + // CreateDate: + // { year: 1970, + // month: 1, + // day: 8, + // hour: 0, + // minute: 0, + // second: 0, + // millisecond: 0, + // rawValue: '1970:01:08 00:00:00' }, + // ModifyDate: + // { year: 2014, + // month: 7, + // day: 19, + // hour: 17, + // minute: 15, + // second: 29, + // millisecond: 0, + // rawValue: '2014:07:19 17:15:29' }, + // TimeScale: 1000, + // PreferredRate: 1, + // PreferredVolume: '100.00%', + // PreviewTime: '0 s', + // PosterTime: '0 s', + // SelectionTime: '0 s', + // CurrentTime: '0 s', + // NextTrackID: 3, + // TrackHeaderVersion: 0, + // TrackCreateDate: '0000:00:00 00:00:00', + // TrackModifyDate: '0000:00:00 00:00:00', + // TrackID: 1, + // TrackLayer: 0, + // TrackVolume: '0.00%', + // ImageWidth: 1280, + // ImageHeight: 720, + // GraphicsMode: 'srcCopy', + // OpColor: '0 0 0', + // CompressorID: 'avc1', + // SourceImageWidth: 1280, + // SourceImageHeight: 720, + // XResolution: 72, + // YResolution: 72, + // BitDepth: 24, + // VideoFrameRate: 25, + // MatrixStructure: '1 0 0 0 1 0 0 0 1', + // MediaHeaderVersion: 0, + // MediaCreateDate: '0000:00:00 00:00:00', + // MediaModifyDate: '0000:00:00 00:00:00', + // MediaTimeScale: 48000, + // MediaLanguageCode: 'und', + // HandlerDescription: 'SoundHandler', + // Balance: 0, + // AudioFormat: 'mp4a', + // AudioChannels: 2, + // AudioBitsPerSample: 16, + // AudioSampleRate: 48000, + // HandlerType: 'Metadata', + // HandlerVendorID: 'Apple', + // Encoder: 'Lavf53.24.2', + // Title: 'Sample title test', + // Composer: 'th', + // BeatsPerMinute: '', + // ContentCreateDate: 2018, + // Genre: 'this', + // Artist: 'hhj', + // Comment: 'hhk', + // Subtitle: 'jj', + // Mood: 'lik', + // ContentDistributor: 'cont', + // Conductor: 'jo', + // Writer: 'writ', + // InitialKey: 'ho', + // Producer: 'prod', + // ParentalRating: 'par', + // Director: 'dir', + // Period: 'pol', + // Publisher: 'pub', + // PromotionURL: 'prom', + // AuthorURL: 'auth', + // EncodedBy: 'enc', + // Category: 'h', + // ImageSize: '1280x720', + // Megapixels: 0.922, + // AvgBitrate: '1.58 Mbps', + // Rotation: 0 }, + // processingStatus: false, + // video_codec_name: 'h264', + // video_resolution: '720p' } diff --git a/Tdarr_Plugin_nc7x_Example.js b/Tdarr_Plugin_zzzz_Post_Proc_Example.js similarity index 80% rename from Tdarr_Plugin_nc7x_Example.js rename to Tdarr_Plugin_zzzz_Post_Proc_Example.js index 411ac2d..4bd2d34 100644 --- a/Tdarr_Plugin_nc7x_Example.js +++ b/Tdarr_Plugin_zzzz_Post_Proc_Example.js @@ -1,79 +1,62 @@ -function details() { +module.exports.details = function details() { return { - id: "Tdarr_Plugin_nc7x_Example", - Name: "No title meta data ", + 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 + Name: "Post proc ", Type: "Video", - Description: `This plugin removes metadata (if a title exists). The output container is the same as the original. \n\n`, + Operation: "", + Description: `This plugin does some stuff after all plugins have been executed. \n\n`, Version: "1.00", - Link: "https://github.com/HaveAGitGat/Tdarr_Plugin_nc7x_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 + + Inputs: [ //(Optional) Inputs you'd like the user to enter to allow your plugin to be easily configurable from the UI + { + name: 'language', + tooltip: `Enter one language tag here for the language of the subtitles you'd like to keep. + + \\nExample:\\n + eng + + \\nExample:\\n + fr + + \\nExample:\\n + de` //Each line following `Example:` will be clearly formatted. \\n used for line breaks + }, + { + name: 'channels', + tooltip: `Desired audio channel number. + + \\nExample:\\n + 2` + }, + ] + } } -function plugin(file) { +module.exports.plugin = function plugin(file, librarySettings, inputs) { + console.log('Transcode success! Now do some stuff with the newly scanned file.') - //Must return this object + //Optional response if you need to modify database var response = { - - 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 a comma, followed by the output argument. - // Examples - //HandBrake - // '-Z "Very Fast 1080p30"' - //FFmpeg - // '-sn,-map_metadata -1 -c:v copy -c:a copy' - container: '.mp4', // The container of the transcoded output file. - handBrakeMode: false, //Set whether to use HandBrake or FFmpeg for transcoding - 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. - 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. - // Give reasons why it has been skipped ('File has no title metadata, File meets conditions!') - + file, + removeFromDB: false, + updateDB: false, } - response.container = '.' + file.container - response.FFmpegMode = true - - - if (file.fileMedium !== "video") { - - - console.log("File is not video") + return response - response.infoLog += " File is not video" - response.processFile = false - - return response - - } else { - - if (file.meta.Title != undefined) { - - response.infoLog += " File has title metadata" - response.preset = ',-map_metadata -1 -c:v copy -c:a copy' - response.processFile = true - return response - } else { - response.infoLog += " File has no title metadata" - } - - - response.infoLog += " File meets conditions!" - return response - - } } -module.exports.details = details; -module.exports.plugin = plugin; //Example file object: // { diff --git a/methods/library/actions/transcodeKeepOneAudioStream.js b/methods/library/actions/transcodeKeepOneAudioStream.js index 8e17882..4feece5 100644 --- a/methods/library/actions/transcodeKeepOneAudioStream.js +++ b/methods/library/actions/transcodeKeepOneAudioStream.js @@ -60,7 +60,7 @@ module.exports = function transcodeKeepOneAudioStream(file, audioEncoder, langTa try { if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio" && i !== audioStreamToKeep) { - ffmpegCommandInsert += ` -map -0:a:${i}` + ffmpegCommandInsert += ` -map -0:${i}` } } catch (err) { } diff --git a/methods/library/filters/filterByCodec.js b/methods/library/filters/filterByCodec.js index 1930365..07a675f 100644 --- a/methods/library/filters/filterByCodec.js +++ b/methods/library/filters/filterByCodec.js @@ -5,11 +5,22 @@ function filterByCodec(file, mode, codecs) { try { - // console.log(file,mode,codecs) + // console.log(file,mode,codecs) + + var allCodecs = file.ffProbeData.streams.map(row => row.codec_name) + + var included = false + + for (var i = 0; i < allCodecs.length; i++) { + + if (codecs.toLowerCase().includes(allCodecs[i])) { + included = true + } + } if (mode === 'include') { - if (codecs.toLowerCase().includes(file.ffProbeData.streams[0].codec_name.toLowerCase())) { + if (included) { var response = { outcome: true, @@ -29,7 +40,7 @@ function filterByCodec(file, mode, codecs) { } else if (mode === 'exclude') { - if (codecs.toLowerCase().includes(file.ffProbeData.streams[0].codec_name.toLowerCase())) { + if (included) { var response = { outcome: false, @@ -50,10 +61,10 @@ function filterByCodec(file, mode, codecs) { var response = { outcome: false, - note: `library.filters.filterByCodec error: ${err} \n` + note: `library.filters.filterByCodec error: ${err} \n` } return response - + } catch (err) {