From eb2a7b46c412d806765ce3183c8a46bc7414f397 Mon Sep 17 00:00:00 2001 From: MrMeeb Date: Sat, 27 Apr 2024 20:16:56 +0000 Subject: [PATCH] Add sub extractor job --- Tdarr_Plugin_MM1_MrMeeb_Extract_Subs.js | 187 ++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 Tdarr_Plugin_MM1_MrMeeb_Extract_Subs.js diff --git a/Tdarr_Plugin_MM1_MrMeeb_Extract_Subs.js b/Tdarr_Plugin_MM1_MrMeeb_Extract_Subs.js new file mode 100644 index 0000000..1490dc0 --- /dev/null +++ b/Tdarr_Plugin_MM1_MrMeeb_Extract_Subs.js @@ -0,0 +1,187 @@ +const details = () => ({ + id: 'Tdarr_Plugin_MM1_MrMeeb_Extract_Subs', + Stage: 'Pre-processing', + Name: 'MrMeeb Extract Subtitles', + Type: 'Subtitle', + Operation: 'Transcode', + Description: 'Automatically extracts SRT subtitles to external files and removes the embedded ones.', + Version: '0.1', + Tags: 'pre-processing', + Inputs: [], + }); + + // eslint-disable-next-line no-unused-vars + const plugin = (file, librarySettings, inputs, otherArguments) => { + + const lib = require('../methods/lib')(); + // load default plugin inputs + inputs = lib.loadDefaultValues(inputs, details); + + // Load response object + const 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 , 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, + 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 + }; + + // Required global variables for functions + let ffmpegSubs = ''; + + // Defining functions + const removeMKV = (filename) => { + + const re = filename.replace(/\.mkv/g, ''); + + return re + + } + + // Function variables + let subIdx = -1; // The stream index of the current subtitle stream, relative to all subtitle steams + + console.log(` === Running Extract Subs function === `) + + // Identify all subtitles + for (let i = 0; i < file.ffProbeData.streams.length; i ++) { + + // Work with subtitle streams only + if ( + file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle' && + file.ffProbeData.streams[i].codec_name.toLowerCase() === 'subrip' + ) { + + // Set language to variable + let subLang = file.ffProbeData.streams[i].tags.language.toLowerCase() + let subName = removeMKV(otherArguments.originalLibraryFile.meta.FileName) + + // Keep track of subtitle stream relative numbers + try { + if ( + file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle' + ) { + subIdx++; + } + } catch (err) {} + + // Extract forced and SDH subtitles + if ( + (file.ffProbeData.streams[i].disposition.hearing_impaired == 1 && + file.ffProbeData.streams[i].disposition.forced == 1) || + (file.ffProbeData.streams[i].tags.title != undefined && + file.ffProbeData.streams[i].tags.title.toLowerCase().includes("sdh") && + file.ffProbeData.streams[i].disposition.forced == 1) + ) { + + console.log(`Stream ${i} (subtitle stream ${subIdx}) is SDH and forced.`) + response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is SDH and forced. \n` + + ffmpegSubs += ` -map 0:s:${subIdx} -c copy "${otherArguments.originalLibraryFile.meta.Directory}/${subName}-${subIdx}.${subLang}.sdh.forced.srt"` + + } + + // Extract SDH subtitles + else if ( + file.ffProbeData.streams[i].disposition.hearing_impaired == 1 || + (file.ffProbeData.streams[i].tags.title != undefined && file.ffProbeData.streams[i].tags.title.toLowerCase().includes("sdh")) + ) { + + console.log(`Stream ${i} (subtitle stream ${subIdx}) is SDH.`) + response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is SDH. \n` + + ffmpegSubs += ` -map 0:s:${subIdx} -c copy "${otherArguments.originalLibraryFile.meta.Directory}/${subName}-${subIdx}.${subLang}.sdh.srt"` + + } + + // Extract forced subtitles + else if ( + file.ffProbeData.streams[i].disposition.forced == 1 + ) { + + console.log(`Stream ${i} (subtitle stream ${subIdx}) is forced.`) + response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is forced. \n` + + ffmpegSubs += ` -map 0:s:${subIdx} -c copy "${otherArguments.originalLibraryFile.meta.Directory}/${subName}-${subIdx}.${subLang}.forced.srt"` + + } + + // Extract forced and SDH subtitles + else { + + console.log(`Stream ${i} (subtitle stream ${subIdx}) is standard.`) + response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is standard. \n` + + ffmpegSubs += ` -map 0:s:${subIdx} -c copy "${otherArguments.originalLibraryFile.meta.Directory}/${subName}-${subIdx}.${subLang}.srt"` + + }; + + } + + // If subtitles aren't in SRT format + else if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') { + + subIdx++; + console.log(`Stream ${i} (subtitle stream ${subIdx}) is a subtitle but not SRT.`) + response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is a subtitle but not SRT. \n` + + } + + } + + // If no subs + if (subIdx == -1) { + + console.log('No SRT subtitles found to extract. No action needed.') + response.infoLog += `No SRT subtitles found to extract. No action needed. \n` + + } + + if (ffmpegSubs.length > 0) { + + response.infoLog += '☒ Changes are required! \n Extracting SRT files'; + + require("child_process").execSync(`rm -rf ${otherArguments.originalLibraryFile.meta.Directory}/*.srt`) + + response.FFmpegMode = true; + + response.container = `.${file.container}`; + + response.preset = `,${ffmpegSubs} -map 0:v? -map 0:a? -map 0:d? -c copy -max_muxing_queue_size 9999`; + + console.log(response.preset) + + response.processFile = true; + + return response; + + } + + if (ffmpegSubs.length == 0) { + + response.infoLog += '☑ No changes are required \n'; + + response.processFile = false; + + return response; + + } + +}; + +module.exports.details = details; +module.exports.plugin = plugin; \ No newline at end of file