Open pchang388 opened 1 year ago
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
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
I modified @abelfodil's Tdarr_Plugin_bldl_Clean_Subtitle_Streams to accomplish this. The only thing that I changed is a fix to the code so that it operates on the correct subtitle track, and I made it so that the plugin only removes the disposition of the subtitle track instead of removing it entirely (sometimes I like to watch my anime dubbed).
Here is the file:
/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */
const details = () => ({
id: 'Tdarr_Plugin_bldl_Clean_Subtitle_Streams',
Stage: 'Pre-processing',
Name: 'bldl Clean Subtitle Streams',
Type: 'Subtitle',
Operation: 'Transcode',
Description: 'This plugin remove disposition of specified subtitle tracks. \n\n',
Version: '1.0',
Tags: 'pre-processing,ffmpeg,subtitle only,configurable',
Inputs: [{
name: 'titles_to_clean',
type: 'string',
defaultValue: 'Signs / Songs',
inputUI: {
type: 'text',
},
tooltip: 'Specify titles to clean, separated by a comma. Defaults to "Signs / Songs".',
}],
});
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const plugin = (file, librarySettings, inputs, otherArguments) => {
const lib = require('../methods/lib')();
// eslint-disable-next-line @typescript-eslint/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: '',
};
if (inputs.titles_to_clean === undefined) {
response.processFile = false;
response.infoLog += '☒ Inputs not entered! \n';
return response;
}
// 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;
}
// Set up required variables.
let ffmpegCommandInsert = '';
let subtitleIdx = 0;
let convert = false;
const titles_to_clean = inputs.titles_to_clean.split(',').map((s) => s.toLowerCase());
// Go through each stream in the file.
for (let i = 0; i < file.ffProbeData.streams.length; i++) {
// Catch error here incase the title metadata is completely missing.
try {
// Check if stream is subtitle
// AND then checks for stream titles with the specified titles.
// Removing any streams that are applicable.
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') {
if (titles_to_clean.some((s) => file.ffProbeData.streams[i].tags.title.toLowerCase().includes(s))) {
ffmpegCommandInsert += `-disposition:s:${subtitleIdx} 0 `;
response.infoLog += `☒Subtitle stream 0:s:${subtitleIdx} detected as being ${titles_to_clean}; removing disposition. \n`;
convert = true;
}
subtitleIdx++; // increment current subtitle id
}
} catch (err) {
// Error
}
}
// 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;
Do note that I did only very minimal testing so make sure to test it yourself to make sure it works.
@gabehf Thanks for the fix! I updated the PR to fix the bug :)
I also recently created https://github.com/HaveAGitGat/Tdarr_Plugins/pull/717, and I use it in conjunction with stream reordering plugins.
It might be interesting to create a subtitles reordering plugin based on titles.
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:
Sign|Song
.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/
Additional context Using Latest Docker Image of Tdarr, version:
2.00.20