Tdarr_Plugins
Tdarr_Plugins copied to clipboard
[request] Reorder (or Delete) Default Subtitle Track Based off Description (Sign & Song)
Is your feature request related to a problem? Please describe.
I often have anime that comes with Sign & Songs
subtitles as default. From what I can tell, this subtitle track only contains subtitles for the songs in the episode and do not provide dialogue subtitles. This is pretty useless to me personally as I don't really care about what the intro/outro songs are saying.
What makes it undesirable, is that based off user/system settings in Plex, it may choose the sign|song
as the default subtitle stream even if only Japanese/Foreign audio exists - causing the end user to have to change the subtitle track for each episode manually/script if they have subtitles always on/enabled.
Example:
Describe the solution you'd like I would like a way to do the following:
- Be able to set the default subtitle track based on language and description (or lack of), i.e. I want to be able to set as default another subtitle track that is in English but is not
Sign|Song
. - If above is not possible, optionally removing any subtitles with
Sign|Song
or similar (if other subtitle tracks in the target language exist - i.e. so you won't remove it if it's the only subtitle in the target language) and setting a new default subtitle track based on Language (I want english and it can auto-select the next subtitle track that is in English and make it default)
Describe alternatives you've considered So far from what I can tell, there is no community plugin to do this feature after my few hours of searching on google/online. Closest I found was this which is similar but opposite of what I want: https://www.reddit.com/r/Tdarr/comments/109yg5s/set_subtitles_as_forced_if_named_signs_songs/
- The links are dead in that thread so I am unable to tell what exactly was done
Additional context
Using Latest Docker Image of Tdarr, version: 2.00.20
For now, I ended up adding a few lines to the existing Migz4CleanSubs
plugin.
Below is nothing fancy, just quick for my use case. It will remove the subtitle stream if it has sign|sing
(sign or sing, some name them sing|song
instead of sign|song
) and song
in the title/description of the subtitle stream. Does not do any safety checks to see if it is the only subtitle stream remaining, so some caution.
This will work if you have your subtitle stream(s) properly labeled with sign
and song
like from my screenshot in the original post.
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
const details = () => ({
id: 'Tdarr_Plugin_MC93_Migz4CleanSubs_SIGN_SONG',
Stage: 'Pre-processing',
Name: 'Custom SignSong version of Migz-Clean subtitle streams',
Type: 'Subtitle',
Operation: 'Transcode',
Description: 'This plugin keeps only specified language tracks & can tag tracks with an unknown language. \n\n',
Version: '2.4',
Tags: 'pre-processing,ffmpeg,subtitle only,configurable',
Inputs: [{
name: 'language',
type: 'string',
defaultValue: 'eng',
inputUI: {
type: 'text',
},
tooltip: `Specify language tag/s here for the subtitle tracks you'd like to keep.
\\nMust follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
\\nExample:\\n
eng
\\nExample:\\n
eng,jpn`,
},
{
name: 'commentary',
type: 'boolean',
defaultValue: false,
inputUI: {
type: 'dropdown',
options: [
'false',
'true',
],
},
tooltip: `Specify if subtitle tracks that contain commentary/description should be removed.
\\nExample:\\n
true
\\nExample:\\n
false`,
},
{
name: 'signsong',
type: 'boolean',
defaultValue: false,
inputUI: {
type: 'dropdown',
options: [
'false',
'true',
],
},
tooltip: `Specify if subtitle tracks containing sign/song subtitles should be removed.
\\nExample:\\n
true
\\nExample:\\n
false`,
},
{
name: 'tag_language',
type: 'string',
defaultValue: '',
inputUI: {
type: 'text',
},
tooltip: `Specify a single language for subtitle tracks with no language or unknown language to be tagged with.
\\nMust follow ISO-639-2 3 letter format. https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes
\\nLeave empty to disable.
\\nExample:\\n
eng
\\nExample:\\n
por`,
},
],
});
// eslint-disable-next-line no-unused-vars
const plugin = (file, librarySettings, inputs, otherArguments) => {
const lib = require('../methods/lib')();
// eslint-disable-next-line no-unused-vars,no-param-reassign
inputs = lib.loadDefaultValues(inputs, details);
const response = {
processFile: false,
preset: '',
container: `.${file.container}`,
handBrakeMode: false,
FFmpegMode: true,
reQueueAfter: false,
infoLog: '',
};
// Check if file is a video. If it isn't then exit plugin.
if (file.fileMedium !== 'video') {
// eslint-disable-next-line no-console
console.log('File is not video');
response.infoLog += '☒File is not video \n';
response.processFile = false;
return response;
}
// Check if inputs.language has been configured. If it hasn't then exit plugin.
if (inputs.language === '') {
response.infoLog
+= '☒Language/s to keep have not been configured, '
+ 'please configure required options. Skipping this plugin. \n';
response.processFile = false;
return response;
}
// Set up required variables.
const language = inputs.language.split(',');
let ffmpegCommandInsert = '';
let subtitleIdx = 0;
let convert = false;
// look for both to be sure
const signRegex = /(sign|sing)/gi;
const songRegex = /song/gi
// Go through each stream in the file.
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Catch error here incase the language metadata is completely missing.
try {
// Check if stream is subtitle
// AND checks if the tracks language code does not match any of the languages entered in inputs.language.
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 0:s:${subtitleIdx} has unwanted language tag ${file.ffProbeData.streams[
i
].tags.language.toLowerCase()}, removing. \n`;
convert = true;
}
} catch (err) {
// Error
}
// Catch error here incase the title metadata is completely missing.
try {
// Check if inputs.commentary is set to true
// AND if stream is subtitle
// AND then checks for stream titles with the following "commentary, description, sdh".
// Removing any streams that are applicable.
if (
inputs.commentary === 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 0:s:${subtitleIdx} detected as being descriptive, removing. \n`;
convert = true;
}
} catch (err) {
// Error
}
// Catch error here incase the title metadata is completely missing.
try {
// Check if inputs.singsong is set to true
// AND if stream is subtitle
// AND then checks for sing|song
// Removing any streams that are applicable.
if (
inputs.signsong === true
&& file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle'
&& signRegex.test(file.ffProbeData.streams[i].tags.title.toLowerCase())
&& songRegex.test(file.ffProbeData.streams[i].tags.title.toLowerCase())
) {
ffmpegCommandInsert += `-map -0:s:${subtitleIdx} `;
response.infoLog += `☒Subtitle stream 0:s:${subtitleIdx} detected as being sign|song subtitle, removing. \n`;
convert = true;
}
} catch (err) {
// Error
}
// Check if inputs.tag_language has something entered.
// (Entered means user actually wants something to happen, empty would disable this)
// AND checks that stream is subtitle.
if (
inputs.tag_language !== ''
&& file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle'
) {
// Catch error here incase the metadata is completely missing.
try {
// Look for subtitle with "und" as metadata language.
if (
file.ffProbeData.streams[i].tags.language
.toLowerCase()
.includes('und')
) {
ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `;
response.infoLog
+= `☒Subtitle stream 0:s:${subtitleIdx} has no language, tagging as ${inputs.tag_language}. \n`;
convert = true;
}
} catch (err) {
// Error
}
// Checks if the tags metadata is completely missing.
// If so this would cause playback to show language as "undefined".
// No catch error here otherwise it would never detect the metadata as missing.
if (typeof file.ffProbeData.streams[i].tags === 'undefined') {
ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `;
response.infoLog
+= `☒Subtitle stream 0:s:${subtitleIdx} has no language, tagging as ${inputs.tag_language}. \n`;
convert = true;
} else if (typeof file.ffProbeData.streams[i].tags.language === 'undefined') {
// Checks if the tags.language metadata is completely missing.
// If so this would cause playback to show language as "undefined".
// No catch error here otherwise it would never detect the metadata as missing
ffmpegCommandInsert += `-metadata:s:s:${subtitleIdx} language=${inputs.tag_language} `;
response.infoLog
+= `☒Subtitle stream 0:s:${subtitleIdx} has no language, tagging as ${inputs.tag_language}. \n`;
convert = true;
}
}
// Check if stream type is subtitle and increment subtitleIdx if true.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') {
subtitleIdx += 1;
}
}
// Convert file if convert variable is set to true.
if (convert === true) {
response.processFile = true;
response.preset = `, -map 0 ${ffmpegCommandInsert} -c copy -max_muxing_queue_size 9999`;
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;
Okay assuming plex is reading the disposition of the sub stream
They look like this
"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 "captions":0 "descriptions":0 "metadata":0 "dependent":0 "still_image":0
I assume the streams offending you are marked default or forced or both and your plex setting is choosing them and playing them....
Lets assume you look at the probe data and see (EXAMPLE) Sub Stream 0 eng Sub Stream 1 eng forced/default signs and songs in the title
does this work in ffmepg? THREE VARIBALES
- input.mkv
- output.mkv
- iD this is in "-disposition:s:iD" <- Last part meaning in the example it would be this -> "-i input.mkv -map 0 -c copy -disposition:s:1 0 output.mkv" COMMAND in FFMPEG "-i input.mkv -map 0 -c copy -disposition:s:iD 0 output.mkv"
If this fixes your issue with ONE file let me know and I will produce a plugin that will do it for you
The substream dispositions should all be unset in the output file after that command is run AND you need to make sure Plex also does not auto play it... It could be that plex auto plays those in if the title they says signs which would be another issue
The theoretical plugin I would make would loop through the Subtitle Streams and look for this if (stream.disposition.lyrics || (title.includes('signs')) || (title.includes('songs')))
If a stream had the above we would run that ffmpeg command on it with its iD going in that field.
If I forget about this thread you could edit migz plugin with
if (stream.disposition.lyrics || (title.includes('signs')) || (title.includes('songs'))) {
ffmpegCommandInsert += -disposition:s:${subtitleIdx} 0
;
}
The above if syntax would need to fit is loop style so its not exact
Tdarr_Plugin_remove_disposition_sign_songs.zip
Was working on something similar so just changed it quick for you..... All this should do is remove the disposition forced and default from a sub track that is signs and songs.... This will leave the sign and songs track with NO DISPOSITION AT ALL, but the title tag remains in place
Simple 79 lines total, lines 50 through 73 handle everything
****This is a Prototype and is largely untested, use at your own risk
I missed that present needs a comma
preset: '',
to
preset: ' , ', <- like this