Compare commits

...

2 Commits

Author SHA1 Message Date
eb2a7b46c4 Add sub extractor job 2024-04-27 20:16:56 +00:00
ce72b90f68 Add ability to keep commentary audio/subtitles 2024-04-27 20:16:46 +00:00
2 changed files with 274 additions and 20 deletions

View File

@ -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 <io>, followed by the output argument.
// Examples
// HandBrake
// '-Z "Very Fast 1080p30"'
// FFmpeg
// '-sn <io> -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;

View File

@ -47,6 +47,19 @@ const details = () => ({
}, },
tooltip: `Select whether to process subs, or just copy over`, tooltip: `Select whether to process subs, or just copy over`,
}, },
{
name: 'remove_commentary',
type: 'boolean',
defaultValue: true,
inputUI: {
type: 'dropdown',
options: [
'true',
'false',
],
},
tooltip: `Select whether to remove commentary audio and subtitle tracks`,
},
{ {
name: 'crf', name: 'crf',
type: 'string', // set the data type of the input ('string', 'number', 'boolean') type: 'string', // set the data type of the input ('string', 'number', 'boolean')
@ -140,7 +153,7 @@ const response = {
audioIdx++; audioIdx++;
} }
} catch (err) {} } catch (err) {}
// Identify wanted tracks // Identify wanted tracks
try { try {
@ -161,20 +174,34 @@ const response = {
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
audioIdxTruehd++; audioIdxTruehd++;
MMCodec = file.ffProbeData.streams[i].codec_name.toUpperCase(); MMCodec = file.ffProbeData.streams[i].codec_name.toUpperCase();
MMChannelLayout = file.ffProbeData.streams[i].channel_layout.replace('(side)', ''); MMChannelLayout = file.ffProbeData.streams[i].channel_layout.replace('(side)', '');
try { try {
if ( if (
file.ffProbeData.streams[i].tags.title != undefined && (file.ffProbeData.streams[i].tags.title != undefined &&
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") || file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
file.ffProbeData.streams[i].disposition.comment == 1 file.ffProbeData.streams[i].disposition.comment == 1) &&
inputs.remove_commentary == true
) { ) {
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Removing.`) console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to remove.`)
response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Removing.\n` response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to remove.\n`
}
else if (
(file.ffProbeData.streams[i].tags.title != undefined &&
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
file.ffProbeData.streams[i].disposition.comment == 1) &&
inputs.remove_commentary == false
) {
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to keep.`)
response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to keep.\n`
ffmpegAudioOtherTracks += ` -map 0:a:${audioIdxTruehd}`
} }
@ -304,13 +331,27 @@ const response = {
// Identify if stream is commentary track // Identify if stream is commentary track
if ( if (
file.ffProbeData.streams[i].tags.title != undefined && (file.ffProbeData.streams[i].tags.title != undefined &&
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") || file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
file.ffProbeData.streams[i].disposition.comment == 1 file.ffProbeData.streams[i].disposition.comment == 1) &&
inputs.remove_commentary == true
) { ) {
console.log(`Stream ${i} (audio stream ${audioIdxDtshdma}) is probably a commentary track. Removing.`) console.log(`Stream ${i} (audio stream ${audioIdxDtshdma}) is probably a commentary track. Selected to remove.`)
response.infoLog += `Stream ${i} (audio stream ${audioIdxDtshdma}) is probably a commentary track. Removing.\n` response.infoLog += `Stream ${i} (audio stream ${audioIdxDtshdma}) is probably a commentary track. Selected to remove.\n`
}
else if (
(file.ffProbeData.streams[i].tags.title != undefined &&
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
file.ffProbeData.streams[i].disposition.comment == 1) &&
inputs.remove_commentary == false
) {
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to keep.`)
response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to keep.\n`
ffmpegAudioOtherTracks += ` -map 0:a:${audioIdxTruehd}`
} }
@ -434,15 +475,29 @@ const response = {
try { try {
// Try to identify commentary tracks // Try to identify commentary tracks
if ( if (
file.ffProbeData.streams[i].tags.title != undefined && (file.ffProbeData.streams[i].tags.title != undefined &&
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") || file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
file.ffProbeData.streams[i].disposition.comment == 1 file.ffProbeData.streams[i].disposition.comment == 1) &&
inputs.remove_commentary == true
) { ) {
console.log(`Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Leaving it out`) console.log(`Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Selected to remove`)
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Removing.\n` response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Selected to remove.\n`
} }
else if (
(file.ffProbeData.streams[i].tags.title != undefined &&
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
file.ffProbeData.streams[i].disposition.comment == 1) &&
inputs.remove_commentary == false
) {
console.log(`Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Selected to keep.`)
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Selected to keep.\n`
ffmpegAudioOtherTracks += ` -map 0:a:${audioIdx}`
}
else if ( else if (
file.ffProbeData.streams[i].codec_name.toLowerCase() === 'ac3' && file.ffProbeData.streams[i].codec_name.toLowerCase() === 'ac3' &&
@ -649,8 +704,6 @@ const response = {
// Go through all subtitles, removing non-English/German subs and commentary subs // Go through all subtitles, removing non-English/German subs and commentary subs
for (let i = 0; i < file.ffProbeData.streams.length; i ++) { for (let i = 0; i < file.ffProbeData.streams.length; i ++) {
console.log(file.ffProbeData.streams[i].codec_type)
// Work with subtitle streams only // Work with subtitle streams only
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') { if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') {
@ -686,19 +739,33 @@ const response = {
// Remove any subtitles that are for commentary // Remove any subtitles that are for commentary
else if ( else if (
file.ffProbeData.streams[i].tags.title != undefined && (file.ffProbeData.streams[i].tags.title != undefined &&
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") || file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
file.ffProbeData.streams[i].disposition.comment == 1 file.ffProbeData.streams[i].disposition.comment == 1) &&
inputs.remove_commentary == true
) { ) {
console.log(`Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Removing.`) console.log(`Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Selected to remove.`)
response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Removing. \n` response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Selected to remove. \n`
ffmpegSubs += ` -map -0:s:${subIdx}` ffmpegSubs += ` -map -0:s:${subIdx}`
} }
else if (
(file.ffProbeData.streams[i].tags.title != undefined &&
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
file.ffProbeData.streams[i].disposition.comment == 1) &&
inputs.remove_commentary == false
) {
console.log(`Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Selected to keep.`)
response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Selected to keep. \n`
}
else { else {
console.log(`Stream ${i} (subtitle stream ${subIdx}) is wanted. Keeping.`) console.log(`Stream ${i} (subtitle stream ${subIdx}) is wanted. Keeping.`)