From bbcd7adc5849af71f034f53805819d6dc8cb1ff5 Mon Sep 17 00:00:00 2001 From: tws101 <tws101@users.noreply.github.com> Date: Sat, 4 Jan 2025 10:20:13 -0600 Subject: [PATCH] Add files via upload --- .../1.0.0/index.ts | 91 ++++ .../1.0.0/index.ts | 218 ++++++++ .../1.0.0/index.ts | 476 ++++++++++++++++++ .../0.7.0/index.ts | 217 ++++++++ .../1.0.0/index.ts | 301 +++++++++++ .../1.0.0/index.ts | 138 +++++ .../1.0.0/index.ts | 69 +++ .../1.0.0/index.ts | 112 +++++ .../1.0.0/index.ts | 76 +++ 9 files changed, 1698 insertions(+) create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveCommentary/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveDuplicates/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveUnwantedLanuguages/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStandardizeAllAudio/0.7.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesAddtoMKV/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesExtractSubs/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemoveCommentary/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemovebyLanguage/1.0.0/index.ts create mode 100644 FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemovebyProperty/1.0.0/index.ts diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveCommentary/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveCommentary/1.0.0/index.ts new file mode 100644 index 000000000..f28bf3e6b --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveCommentary/1.0.0/index.ts @@ -0,0 +1,91 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; +import { Istreams } from '../../../../FlowHelpers/1.0.0/interfaces/synced/IFileObject'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Audio Remove Commentary', + description: 'Checks all Audio streams for commentary and removes them', + style: { + borderColor: '#6efefc', + }, + tags: 'audio', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + ], + outputs: [ + { + number: 1, + tooltip: 'Go to Next Plugin', + }, + ], +}); + +const noCommentary = (stream :Istreams) => { + if (!stream.tags || !stream.tags.title) { + return true; + } if ( + stream.tags.title.toLowerCase().includes('commentary') + || stream.tags.title.toLowerCase().includes('description') + || stream.tags.title.toLowerCase().includes('sdh') + ) { + return false; + } + return true; +}; + +const findNumberOfAudioStream = (args :IpluginInputArgs) => { + if (args.inputFileObj.ffProbeData.streams) { + const number = args.inputFileObj.ffProbeData.streams.filter( + (stream :Istreams) => stream.codec_type === 'audio', + ).length; + return number; + } + return 0; +}; + +const removeCommentary = (args :IpluginInputArgs) => { + const numberOfAudioStreams = Number(findNumberOfAudioStream(args)); + let audioStreamsRemoved = 0; + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type !== 'audio') { + return; + } + if (noCommentary(stream)) { + return; + } + args.jobLog(`Removing Stream ${stream.index} Commentray Detected`); + // eslint-disable-next-line no-param-reassign + stream.removed = true; + audioStreamsRemoved += 1; + }); + if (audioStreamsRemoved === numberOfAudioStreams) { + throw new Error('All audio streams would be removed.'); + } +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + removeCommentary(args); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveDuplicates/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveDuplicates/1.0.0/index.ts new file mode 100644 index 000000000..a3d596b05 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveDuplicates/1.0.0/index.ts @@ -0,0 +1,218 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; +import { Istreams } from '../../../../FlowHelpers/1.0.0/interfaces/synced/IFileObject'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Audio Remove Duplicate Streams', + description: 'Remove Duplicate Audio Streams of each Language.', + style: { + borderColor: '#6efefc', + }, + tags: 'audio', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +const findNumberOfAudioStream = (args :IpluginInputArgs) => { + if (args.inputFileObj.ffProbeData.streams) { + const number = args.inputFileObj.ffProbeData.streams.filter( + (stream :Istreams) => stream.codec_type === 'audio', + ).length; + return number; + } + return 0; +}; + +const getHighest = (first: Istreams, second: Istreams) => { + // @ts-expect-error channels + if (first?.channels > second?.channels) { + return first; + } + return second; +}; + +const noCommentary = (stream :Istreams) => { + if (!stream.tags || !stream.tags.title) { + return true; + } if ( + stream.tags.title.toLowerCase().includes('commentary') + || stream.tags.title.toLowerCase().includes('description') + || stream.tags.title.toLowerCase().includes('sdh') + ) { + return false; + } + return true; +}; + +const undstreams = (args :IpluginInputArgs) => { + if (args.inputFileObj.ffProbeData.streams) { + const ustreams = args.inputFileObj.ffProbeData.streams.filter((stream :Istreams) => { + if ( + stream.codec_type === 'audio' + && noCommentary(stream) + && (!stream.tags + || !stream.tags.language + || stream.tags.language.toLowerCase().includes('und')) + ) { + return true; + } return false; + }); + return ustreams; + } + throw new Error('Error finding undefined streams'); +}; + +const langStreams = (args :IpluginInputArgs, lang :string) => { + if (args.inputFileObj.ffProbeData.streams) { + const lStreams = args.inputFileObj.ffProbeData.streams.filter((stream :Istreams) => { + if ( + stream.codec_type === 'audio' + && stream.tags?.language?.toLowerCase().includes(lang) + && noCommentary(stream) + ) { + return true; + } return false; + }); + return lStreams; + } + throw new Error('Error finding duplicate streams'); +}; + +const removeDuplicates = (args :IpluginInputArgs) => { + const numberOfAudioStreams = Number(findNumberOfAudioStream(args)); + let hasDUPS = false; + let duplicates: Array<string> = []; + let audioStreamsRemoved = 0; + + if (numberOfAudioStreams >= 2 && args.inputFileObj.ffProbeData.streams) { + const tag: Array<string> = []; + const audioStreams = args.inputFileObj.ffProbeData.streams.filter((stream :Istreams) => { + if (stream.codec_type === 'audio') { + return true; + } return false; + }); + + audioStreams.forEach((stream :Istreams) => { + let lang = ''; + if (stream.tags !== undefined) { + if (stream.tags.language !== undefined) { + lang = stream.tags.language.toLowerCase(); + } else { + lang = 'und'; + } + } else { + lang = 'und'; + } + tag.push(lang); + }); + + duplicates = tag.filter((item, index) => tag.indexOf(item) !== index); + if (duplicates.length >= 1) { + hasDUPS = true; + } + } + + if (hasDUPS) { + const highestDUPS: Array<Istreams> = []; + const undhighestDUP: Array<Istreams> = []; + let undefIsDUP = false; + if (duplicates.includes('und')) { + undefIsDUP = true; + } + + if (undefIsDUP) { + const findundID = (element :string) => element === 'und'; + const iD = duplicates.findIndex(findundID); + duplicates.splice(iD, 1); + const undStreams = undstreams(args); + const streamwithhighestChannelCount = undStreams.reduce(getHighest); + undhighestDUP.push(streamwithhighestChannelCount); + } + + duplicates.forEach((dup) => { + const streamWithTag = langStreams(args, + dup); + const streamwithhighestChannelCount = streamWithTag.reduce(getHighest); + highestDUPS.push(streamwithhighestChannelCount); + }); + + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type !== 'audio') { + return; + } + if (!undefIsDUP) { + if (stream.tags === undefined + || stream.tags.language === undefined + || stream.tags.language.toLowerCase().includes('und') + ) { + return; + } + } + if (stream.tags === undefined + || stream.tags.language === undefined + || stream.tags.language.toLowerCase().includes('und') + ) { + if (stream.index === undhighestDUP[0].index) { + return; + } + args.jobLog(`Removing Stream ${stream.index} Duplicate Detected`); + // eslint-disable-next-line no-param-reassign + stream.removed = true; + audioStreamsRemoved += 1; + } + if (stream.tags && stream.tags.language && !(duplicates.includes(stream.tags?.language?.toLowerCase()))) { + return; + } + let chosenStream = false; + highestDUPS.forEach((element :Istreams) => { + if (element.index === stream.index) { + chosenStream = true; + } + }); + if (chosenStream) { + return; + } + args.jobLog(`Removing Stream ${stream.index} Duplicate Detected`); + // eslint-disable-next-line no-param-reassign + stream.removed = true; + audioStreamsRemoved += 1; + }); + } + if (audioStreamsRemoved === numberOfAudioStreams) { + throw new Error('All audio streams would be removed.'); + } +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + removeDuplicates(args); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveUnwantedLanuguages/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveUnwantedLanuguages/1.0.0/index.ts new file mode 100644 index 000000000..275a40863 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandAudioRemoveUnwantedLanuguages/1.0.0/index.ts @@ -0,0 +1,476 @@ +import { Istreams } from '../../../../FlowHelpers/1.0.0/interfaces/synced/IFileObject'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Audio Remove Unwanted Languages', + description: 'Audio Remove Unwanted Languages, Define the languages you want to keep.' + + ' All others will be removed. If there is only one audio stream plugin will be skipped.', + style: { + borderColor: '#6efefc', + }, + tags: 'audio', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + label: 'Keep Languages', + name: 'langTags', + type: 'string', + defaultValue: 'eng', + inputUI: { + type: 'text', + }, + tooltip: + 'Choose the languages you want to keep. Three letter format.' + + 'Seperate additional tags with commas eng,jpn,kor ', + }, + { + label: 'Keep Undefined', + name: 'keepUndefined', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: 'Keeps the Undefined Audio Streams', + }, + { + label: 'Keep Native, Requires API keys to check this if enabled.', + name: 'keepNative', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: 'Toggle whether to enable setting keep native. ', + }, + { + label: 'Priority, Check Radarr or Sonarr First', + name: 'priority', + type: 'string', + defaultValue: 'Radarr', + inputUI: { + type: 'dropdown', + options: [ + 'Radarr', + 'Sonarr', + ], + displayConditions: { + logic: 'AND', + sets: [ + { + logic: 'AND', + inputs: [ + { + name: 'keepNative', + value: 'true', + condition: '===', + }, + ], + }, + ], + }, + }, + tooltip: 'Specify the audio bitrate for newly added channels', + }, + { + label: 'TMDB api key, It is recomended to add this under Tools Global Variables as api_key', + name: 'api_key', + type: 'string', + defaultValue: '{{{args.userVariables.global.api_key}}}', + inputUI: { + type: 'text', + displayConditions: { + logic: 'AND', + sets: [ + { + logic: 'AND', + inputs: [ + { + name: 'keepNative', + value: 'true', + condition: '===', + }, + ], + }, + ], + }, + }, + tooltip: 'Input your TMDB api (v3) key here. (https://www.themoviedb.org/), or use api_key as a global variable.', + }, + { + label: 'Radarr api key, It is recomended to add this under Tools Global Variables as radarr_api_key', + name: 'radarr_api_key', + type: 'string', + defaultValue: '{{{args.userVariables.global.radarr_api_key}}}', + inputUI: { + type: 'text', + displayConditions: { + logic: 'AND', + sets: [ + { + logic: 'AND', + inputs: [ + { + name: 'keepNative', + value: 'true', + condition: '===', + }, + ], + }, + ], + }, + }, + tooltip: 'Input your Radarr api key here, or use radarr_api_key as a global variable.', + }, + { + label: 'Radarr url, It is recomended to add this under Tools Global Variables as radarr_url', + name: 'radarr_url', + type: 'string', + defaultValue: '{{{args.userVariables.global.radarr_url}}}', + inputUI: { + type: 'text', + displayConditions: { + logic: 'AND', + sets: [ + { + logic: 'AND', + inputs: [ + { + name: 'keepNative', + value: 'true', + condition: '===', + }, + ], + }, + ], + }, + }, + tooltip: + 'Input your Radarr url here. (With the http://), do include the port,' + + 'or use radarr_url as a global variable.', + }, + { + label: 'Sonarr api key, It is recomended to add this under Tools Global Variables as sonarr_api_key', + name: 'sonarr_api_key', + type: 'string', + defaultValue: '{{{args.userVariables.global.sonarr_api_key}}}', + inputUI: { + type: 'text', + displayConditions: { + logic: 'AND', + sets: [ + { + logic: 'AND', + inputs: [ + { + name: 'keepNative', + value: 'true', + condition: '===', + }, + ], + }, + ], + }, + }, + tooltip: 'Input your Sonarr api key here, or use sonarr_api_key as a global variable.', + }, + { + label: 'Sonarr url, It is recomended to add this under Tools Global Variables as sonarr_url', + name: 'sonarr_url', + type: 'string', + defaultValue: '{{{args.userVariables.global.sonarr_url}}}', + inputUI: { + type: 'text', + displayConditions: { + logic: 'AND', + sets: [ + { + logic: 'AND', + inputs: [ + { + name: 'keepNative', + value: 'true', + condition: '===', + }, + ], + }, + ], + }, + }, + tooltip: + 'Input your Sonarr url here. (With the http://), do include the port,' + + 'or use sonarr_url as a global variable.', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +const tmdbApi = async (filename :string, api_key :string, args :IpluginInputArgs) => { + let fileName = ''; + // If filename begins with tt, it's already an imdb id + if (filename) { + if (filename.slice(0, 2) === 'tt') { + fileName = filename; + } else { + const idRegex = /(tt\d{7,8})/; + const fileMatch = filename.match(idRegex); + // eslint-disable-next-line prefer-destructuring + if (fileMatch) fileName = fileMatch[1]; + } + } + if (fileName) { + const result = await args.deps.axios + .get( + `https://api.themoviedb.org/3/find/${fileName}?api_key=` + + `${api_key}&language=en-US&external_source=imdb_id`, + ) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + .then((resp :any) => (resp.data.movie_results.length > 0 + ? resp.data.movie_results[0] + : resp.data.tv_results[0])); + if (!result) { + return null; + } + return result; + } + return null; +}; +interface Body { + movie: { + imdbId: string, + } + series: { + imdbId: string, + } +} +const parseArrResponse = (body :Body, arr :string) => { + switch (arr) { + case 'radarr': + return body.movie; + case 'sonarr': + return body.series; + default: + throw new Error('This should never happen'); + } +}; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const tmdbFetchResult = async (args :IpluginInputArgs, languages :any) => { + const priority = String(args.inputs.priority); + const api_key = String(args.inputs.api_key); + const radarr_api_key = String(args.inputs.radarr_api_key); + const radarr_url = String(args.inputs.radarr_url); + const sonarr_api_key = String(args.inputs.sonarr_api_key); + const sonarr_url = String(args.inputs.sonarr_url); + interface tmdbResult { + original_language: string, + } + let tmdbResult :null|tmdbResult = null; + let prio = ['radarr', 'sonarr']; + if (priority === 'sonarr') { + prio = ['sonarr', 'radarr']; + } + interface arrResult { + imdbId: string, + originalLanguage?: { + name?: string, + } + } + let radarrResult :null|arrResult = null; + let sonarrResult :null|arrResult = null; + if (args.inputFileObj.meta?.FileName) { + const fileNameEncoded = encodeURIComponent(args.inputFileObj.meta.FileName); + // eslint-disable-next-line no-restricted-syntax + for (const arr of prio) { + let imdbId = ''; + switch (arr) { + case 'radarr': + if (tmdbResult) break; + if (radarr_api_key) { + // eslint-disable-next-line no-await-in-loop + radarrResult = await parseArrResponse( + // eslint-disable-next-line no-await-in-loop + await args.deps.axios + .get( + `${radarr_url}/api/v3/parse?apikey=${radarr_api_key}&title=${fileNameEncoded}`, + ).then((resp :unknown) => { + if (resp && typeof resp === 'object' && 'data' in resp && resp.data) { + return resp.data; + } + return null; + }), + 'radarr', + ); + if (radarrResult) { + imdbId = radarrResult.imdbId; + args.jobLog(`Grabbed ID (${imdbId}) from Radarr `); + tmdbResult = { original_language: languages.getAlpha2Code(radarrResult.originalLanguage?.name, 'en') }; + } else { + imdbId = fileNameEncoded; + } + } + break; + case 'sonarr': + if (tmdbResult) break; + if (sonarr_api_key) { + // eslint-disable-next-line no-await-in-loop + sonarrResult = await parseArrResponse( + // eslint-disable-next-line no-await-in-loop + await args.deps.axios.get( + `${sonarr_url}/api/v3/parse?apikey=${sonarr_api_key}&title=${fileNameEncoded}`, + ).then((resp :unknown) => { + if (resp && typeof resp === 'object' && 'data' in resp && resp.data) { + return resp.data; + } + return null; + }), + 'sonarr', + ); + if (sonarrResult) { + imdbId = sonarrResult.imdbId; + args.jobLog(`Grabbed ID (${imdbId}) from Sonarr `); + } else { + imdbId = fileNameEncoded; + } + // eslint-disable-next-line no-await-in-loop + tmdbResult = await tmdbApi(imdbId, api_key, args); + } + break; + default: + throw new Error('This should never happen'); + } + } + if (tmdbResult) { + return tmdbResult; + } + args.jobLog('Couldn\'t find the IMDB id of this file. I do not know what the native language is.'); + } + return null; +}; + +const findNumberOfAudioStream = (args :IpluginInputArgs) => { + if (args.inputFileObj.ffProbeData.streams) { + const number = args.inputFileObj.ffProbeData.streams.filter( + (stream :Istreams) => stream.codec_type === 'audio', + ).length; + return number; + } + return 0; +}; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const refineLangTags = (languages :any, langTags :string[]) => { + const master = langTags; + langTags.forEach((element) => { + const lang = languages.alpha3BToAlpha2(element); + master.push(lang); + }); + return master; +}; + +const removeUnwanted = ( + args :IpluginInputArgs, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + languages :any, + numberOfAudioStreams :number, + nativeLang :string, + nativeLangBool :boolean, +) => { + const langTagsUnTrimmed = String(args.inputs.langTags).toLowerCase().split(','); + const langTags: Array<string> = []; + langTagsUnTrimmed.forEach((element) => { + const trimedElement = element.trim(); + langTags.push(trimedElement); + }); + const keepUndefined = Boolean(args.inputs.keepUndefined); + + if (numberOfAudioStreams >= 2) { + const langTagsMaster = refineLangTags(languages, langTags); + let audioStreamsRemoved = 0; + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type !== 'audio') { + return; + } + if (keepUndefined) { + if ((!stream.tags || !stream.tags.language || stream.tags.language.toLowerCase().includes('und'))) { + return; + } + } + if (stream.tags && stream.tags.language && langTagsMaster.includes(stream.tags.language.toLowerCase())) { + return; + } + if (nativeLangBool) { + if (stream.tags?.language?.toLowerCase().includes(nativeLang)) { + return; + } + } + args.jobLog(`Removing Stream ${stream.index} is unwanted`); + // eslint-disable-next-line no-param-reassign + stream.removed = true; + audioStreamsRemoved += 1; + }); + if (audioStreamsRemoved === numberOfAudioStreams) { + throw new Error('All audio streams would be removed.'); + } + } +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + const dependencies = ['@cospired/i18n-iso-languages']; + await args.installClassicPluginDeps(dependencies); + // eslint-disable-next-line import/no-unresolved + const languages = require('@cospired/i18n-iso-languages'); + const keepNative = Boolean(args.inputs.keepNative); + let nativeLang = ''; + let nativeLangBool = false; + const numberOfAudioStreams = Number(findNumberOfAudioStream(args)); + + if (numberOfAudioStreams === 1) { + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; + } + + if (keepNative) { + if (args.inputFileObj.ffProbeData.streams) { + const tmdbResult = await tmdbFetchResult(args, languages); + if (tmdbResult) { + const langsTemp :string = tmdbResult.original_language === 'cn' ? 'zh' : tmdbResult.original_language; + const originalLang :string = (languages.alpha2ToAlpha3B(langsTemp)); + nativeLang = originalLang; + nativeLangBool = true; + args.jobLog(`Found ${langsTemp} using code ${nativeLang}`); + } + } + } + + removeUnwanted(args, languages, numberOfAudioStreams, nativeLang, nativeLangBool); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStandardizeAllAudio/0.7.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStandardizeAllAudio/0.7.0/index.ts new file mode 100644 index 000000000..808b02002 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandStandardizeAllAudio/0.7.0/index.ts @@ -0,0 +1,217 @@ +import { getFfType } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Standardize All Audio', + description: 'Standardize All Audio to the same codec. Command will be sent for all audio streams.' + + ' It is recomended to use Check All Audio Codecs or other verifiers before sending this command.' + + ' The following configurations are unsupported by FFmpeg.' + + ' FFmpeg does NOT support 1 channel truehd.' + + ' FFmpeg does NOT support 6 channel dca or libmp3lame.' + + ' FFmpeg does NOT support 8 channel dca, libmp3lame, truehd, ac3, or eac3.', + style: { + borderColor: '#6efefc', + }, + tags: 'audio', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + label: 'Audio Encoder', + name: 'audioEncoder', + type: 'string', + defaultValue: 'ac3', + inputUI: { + type: 'dropdown', + options: [ + 'aac', + 'ac3', + 'eac3', + 'dca', + 'flac', + 'libopus', + 'mp2', + 'libmp3lame', + 'truehd', + ], + }, + tooltip: 'Enter the desired audio code', + }, + { + label: 'Channels', + name: 'channels', + type: 'number', + defaultValue: '6', + inputUI: { + type: 'dropdown', + options: [ + '1', + '2', + '6', + '8', + ], + }, + tooltip: 'Enter the desired number of channel, certain channel counts are not supported with certain codec.', + }, + { + label: 'Enable Bitrate', + name: 'enableBitrate', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: 'Toggle whether to enable setting audio bitrate', + }, + { + label: 'Bitrate', + name: 'bitrate', + type: 'string', + defaultValue: '300k', + inputUI: { + type: 'text', + displayConditions: { + logic: 'AND', + sets: [ + { + logic: 'AND', + inputs: [ + { + name: 'enableBitrate', + value: 'true', + condition: '===', + }, + ], + }, + ], + }, + }, + tooltip: 'Specify the audio bitrate for newly added channels', + }, + { + label: 'Enable Samplerate', + name: 'enableSamplerate', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: 'Toggle whether to enable setting audio samplerate', + }, + { + label: 'Samplerate', + name: 'samplerate', + type: 'string', + defaultValue: '48k', + inputUI: { + type: 'text', + displayConditions: { + logic: 'AND', + sets: [ + { + logic: 'AND', + inputs: [ + { + name: 'enableSamplerate', + value: 'true', + condition: '===', + }, + ], + }, + ], + }, + }, + tooltip: 'Specify the audio bitrate for newly added channels', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +const checkAbort = (audioCodec :string, channelCount :number) => { + // channel count 1 not supported + if ((['truehd'].includes(audioCodec)) && channelCount === 1) { + throw new Error( + `Selected ${audioCodec} does not support the channel count of ${channelCount}. Reconfigure the Plugin`, + ); + } + // channel count 6 not supported + if ((['dca', 'libmp3lame'].includes(audioCodec)) && channelCount === 6) { + throw new Error( + `Selected ${audioCodec} does not support the channel count of ${channelCount}. Reconfigure the Plugin`, + ); + } + // channel count 8 not supported + if ((['dca', 'libmp3lame', 'truehd', 'ac3', 'eac3'].includes(audioCodec)) && channelCount === 8) { + throw new Error( + `Selected ${audioCodec} does not support the channel count of ${channelCount}. Reconfigure the Plugin`, + ); + } +}; + +const transcodeStreams = (args :IpluginInputArgs) => { + const enableBitrate = Boolean(args.inputs.enableBitrate); + const bitrate = String(args.inputs.bitrate); + const enableSamplerate = Boolean(args.inputs.enableSamplerate); + const samplerate = String(args.inputs.samplerate); + const audioEncoder = String(args.inputs.audioEncoder); + const wantedChannelCount = Number(args.inputs.channels); + + checkAbort(audioEncoder, wantedChannelCount); + + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type !== 'audio') { + return; + } + let targetChannels = wantedChannelCount; + if (stream.channels && stream.channels < wantedChannelCount) { + targetChannels = Number(stream.channels); + } + args.jobLog(`Processing Stream ${stream.index} Standardizing`); + stream.outputArgs.push('-c:{outputIndex}', audioEncoder); + stream.outputArgs.push('-ac:{outputIndex}', `${targetChannels}`); + if (enableBitrate) { + const ffType = getFfType(stream.codec_type); + stream.outputArgs.push(`-b:${ffType}:{outputTypeIndex}`, `${bitrate}`); + } + if (enableSamplerate) { + stream.outputArgs.push('-ar:{outputIndex}', `${samplerate}`); + } + if (['dca', 'truehd', 'flac'].includes(audioEncoder)) { + stream.outputArgs.push('-strict', '-2'); + } + }); + // eslint-disable-next-line no-param-reassign + args.variables.ffmpegCommand.shouldProcess = true; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + transcodeStreams(args); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesAddtoMKV/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesAddtoMKV/1.0.0/index.ts new file mode 100644 index 000000000..595b43955 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesAddtoMKV/1.0.0/index.ts @@ -0,0 +1,301 @@ +import { getFileName, getFileAbosluteDir } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { Istreams } from '../../../../FlowHelpers/1.0.0/interfaces/synced/IFileObject'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.1.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Subtitles Add to MKV', + description: + 'Add Subtitles in SRT to MKV,' + + ' You must you the Begin/Exectute Command made for Multi Input.', + style: { + borderColor: '#6efefc', + }, + tags: 'subtitle', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + label: 'ONE Language tag to Add to MKV', + name: 'langTag', + type: 'string', + defaultValue: 'eng', + inputUI: { + type: 'text', + }, + tooltip: 'Choose ONE three letter language tag to insert into the mkv.', + }, + { + label: 'Include Forced Subs', + name: 'include_forced', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: + 'Forced subtitles will also be added, required naming is source.eng.forced.srt,' + + 'this example assumes chosen tag is eng.', + }, + { + label: 'Include SDH Subs', + name: 'include_sdh', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: + 'Sdh subtitles will also be added, required naming is source.eng.sdh.srt,' + + 'this example assumes chosen tag is eng.', + }, + { + label: 'Include CC Subs', + name: 'include_cc', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: + 'Sdh subtitles will also be added, required naming is source.eng.cc.srt,' + + 'this example assumes chosen tag is eng.', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/ban-types +const loopOverStreamsOfType = (args :IpluginInputArgs, type :string, method :Function) => { + if (args.inputFileObj.ffProbeData.streams) { + for (let i = 0; i < args.inputFileObj.ffProbeData.streams.length; i++) { + if (args.inputFileObj.ffProbeData.streams[i].codec_type.toLowerCase() === type) { + method(args.inputFileObj.ffProbeData.streams[i]); + } + } + } +}; + +const buildSubtitleConfiguration = (args :IpluginInputArgs) => { + // eslint-disable-next-line import/no-unresolved + const languages = require('@cospired/i18n-iso-languages'); + const fs = require('fs'); + const processLanguage = String(args.inputs.langTag).trim(); + const processLanguage2 :string = languages.alpha3BToAlpha2(processLanguage); + const boolGetForced = Boolean(args.inputs.include_forced); + const boolGetSdh = Boolean(args.inputs.include_sdh); + const boolGetCc = Boolean(args.inputs.include_cc); + const subtitleSettings = { + processFile: <boolean> false, + subInput: [] as string[], + subOutput: [] as string[], + }; + + let embeddedSubs = 0; + let boolHaveMain = false; + let boolHaveForced = false; + let boolHaveSdh = false; + let boolHaveCc = false; + // Loop through the streams + const subProcess = (stream :Istreams) => { + // eslint-disable-next-line no-plusplus + embeddedSubs++; + let lang = ''; + let title = ''; + let codec = ''; + if (stream.tags !== undefined) { + if (stream.tags.language !== undefined) { + lang = stream.tags.language.toLowerCase(); + } + if (stream.tags.title !== undefined) { + title = stream.tags.title.toLowerCase(); + } + } + if (stream.codec_name !== undefined) { + codec = stream.codec_name.toLowerCase(); + } + + // Ignore these codecs + if (codec !== 'subrip' && codec !== 'ass' && codec !== 'mov_text' && codec !== 'ssa') { + return; + } + + // Ignore languages we dont want + if (processLanguage !== lang && processLanguage2 !== lang) { + return; + } + + // Check these titles and determine if we already have what we want + if (processLanguage === lang || processLanguage2 === lang) { + if (stream.disposition.forced || (title.includes('forced'))) { + boolHaveForced = true; + } else if (stream.disposition.sdh || (title.includes('sdh'))) { + boolHaveSdh = true; + } else if (stream.disposition.cc || (title.includes('cc'))) { + boolHaveCc = true; + } else { + boolHaveMain = true; + } + } + }; + + loopOverStreamsOfType(args, 'subtitle', subProcess); + + // Check if all Good or Determine if we are missing a sub we want in the MKV + let boolCheckForSrt = false; + if (!boolHaveMain) { + boolCheckForSrt = true; + } + if (boolGetForced) { + if (!boolHaveForced) { + boolCheckForSrt = true; + } + } + if (boolGetSdh) { + if (!boolHaveSdh) { + boolCheckForSrt = true; + } + } + if (boolGetCc) { + if (!boolHaveCc) { + boolCheckForSrt = true; + } + } + if (!boolCheckForSrt) { + return subtitleSettings; + } + + // Setup Variable to Check Disk for the files + const dispostionMain = ''; + const dispostionForced = '.forced'; + const dispostionSdh = '.sdh'; + const dispostionCc = '.cc'; + + const buildSrtFile = (lang :string, disposition :string) => { + const fileName = getFileName(args.originalLibraryFile._id); + const orignalFolder = getFileAbosluteDir(args.originalLibraryFile._id); + const tempsrtFile = [orignalFolder, '/', fileName]; + tempsrtFile.push(`.${lang}${disposition}.srt`); + const srtFile = tempsrtFile.join(''); + return srtFile; + }; + + const mainSubFile = buildSrtFile(processLanguage, dispostionMain); + const mainAltSubFile = buildSrtFile(processLanguage2, dispostionMain); + const forcedSubFile = buildSrtFile(processLanguage, dispostionForced); + const forcedAltSubFile = buildSrtFile(processLanguage2, dispostionForced); + const sdhSubFile = buildSrtFile(processLanguage, dispostionSdh); + const sdhAltSubFile = buildSrtFile(processLanguage2, dispostionSdh); + const ccSubFile = buildSrtFile(processLanguage, dispostionCc); + const ccAltSubFile = buildSrtFile(processLanguage2, dispostionCc); + + // Check for the SRT files names we want + const findSrtFile = (subFile :string, altSubFile :string) => { + if (fs.existsSync(`${subFile}`)) { + return subFile; + } + if (fs.existsSync(`${altSubFile}`)) { + return altSubFile; + } + return null; + }; + + const mainChosenSubsFile = findSrtFile(mainSubFile, mainAltSubFile); + const forcedChosenSubsFile = findSrtFile(forcedSubFile, forcedAltSubFile); + const sdhChosenSubsFile = findSrtFile(sdhSubFile, sdhAltSubFile); + const ccChosenSubsFile = findSrtFile(ccSubFile, ccAltSubFile); + + // Add Subs to MKV + let subIndex = 1; + const titlepMain = 'default'; + const titleForced = 'forced'; + const titleSdh = 'sdh'; + const titleCc = 'cc'; + + const transcode = (chosenSubsFile :string, title :string) => { + let disposition = title; + if (disposition === 'sdh' || disposition === 'cc') { + disposition = 'hearing_impaired'; + } + const mInput = ['-sub_charenc', 'UTF-8', '-f', 'srt', '-i', chosenSubsFile]; + mInput.forEach((element) => { + subtitleSettings.subInput.push(element); + }); + const output = [ + '-map', `${subIndex}:s`, `-codec:s:${embeddedSubs}`, 'srt', `-metadata:s:s:${embeddedSubs}`, + `language=${processLanguage}`, `-metadata:s:s:${embeddedSubs}`, + `title=${title}`, `-disposition:s:${embeddedSubs}`, disposition]; + output.forEach((element) => { + subtitleSettings.subOutput.push(element); + }); + // eslint-disable-next-line no-plusplus + embeddedSubs++; + // eslint-disable-next-line no-plusplus + subIndex++; + }; + + if (mainChosenSubsFile != null && !boolHaveMain) { + transcode(mainChosenSubsFile, titlepMain); + args.jobLog(`Adding ${args.inputs.langTag} SRT to MKV`); + subtitleSettings.processFile = true; + } + if (forcedChosenSubsFile != null && boolGetForced && !boolHaveForced) { + transcode(forcedChosenSubsFile, titleForced); + args.jobLog(`Adding ${args.inputs.langTag} SRT FORCED to MKV`); + subtitleSettings.processFile = true; + } + if (sdhChosenSubsFile != null && boolGetSdh && !boolHaveSdh) { + transcode(sdhChosenSubsFile, titleSdh); + args.jobLog(`Adding ${args.inputs.langTag} SRT SDH to MKV`); + subtitleSettings.processFile = true; + } + if (ccChosenSubsFile != null && boolGetCc && !boolHaveCc) { + transcode(ccChosenSubsFile, titleCc); + args.jobLog(`Adding ${args.inputs.langTag} SRT CC to MKV`); + subtitleSettings.processFile = true; + } + + return subtitleSettings; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + const dependencies = ['@cospired/i18n-iso-languages']; + await args.installClassicPluginDeps(dependencies); + + const subtitleSettings = buildSubtitleConfiguration(args); + + if (subtitleSettings.processFile) { + subtitleSettings.subInput.forEach((element) => { + args.variables.ffmpegCommand.multiInputArguments.push(element); + }); + subtitleSettings.subOutput.forEach((element) => { + args.variables.ffmpegCommand.overallOuputArguments.push(element); + }); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesExtractSubs/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesExtractSubs/1.0.0/index.ts new file mode 100644 index 000000000..8731d3104 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesExtractSubs/1.0.0/index.ts @@ -0,0 +1,138 @@ +import { getFileName, getFileAbosluteDir } from '../../../../FlowHelpers/1.0.0/fileUtils'; +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.1.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Subtitles Extract Subs', + description: + 'Extract Subtitles to SRT,' + + ' You must use the Begin/Exectute Command made for Multi Output.' + + 'This outputs the subs to the input folder', + style: { + borderColor: '#6efefc', + }, + tags: 'subtitle', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + label: 'Overwrite existing SRT Files', + name: 'overwrite', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: 'Overwrite with the extracted SRT files', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +const buildSubtitleConfiguration = (args :IpluginInputArgs) => { + const overwrite = Boolean(args.inputs.overwrite); + const fs = require('fs'); + let subIdx = -1; + const subtitleSettings = { + processFile: <boolean> false, + subOutput: [] as string[], + }; + + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type !== 'subtitle') { + return; + } + subIdx += 1; + if (stream.removed) { + return; + } + let lang = ''; + let title = ''; + let strDisposition = ''; + let boolTextSubs = false; + let codec = ''; + if (stream.tags?.language !== undefined) { + lang = stream.tags.language.toLowerCase(); + } + if (stream.tags?.title !== undefined) { + title = stream.tags.title.toLowerCase(); + } + if (stream.codec_name !== undefined) { + codec = stream.codec_name.toLowerCase(); + } + if (stream.disposition.forced || (title.includes('forced'))) { + strDisposition = '.forced'; + } else if (stream.disposition.sdh || (title.includes('sdh'))) { + strDisposition = '.sdh'; + } else if (stream.disposition.cc || (title.includes('cc'))) { + strDisposition = '.cc'; + } else if (stream.disposition.commentary || stream.disposition.description + || (title.includes('commentary')) || (title.includes('description'))) { + strDisposition = '.commentary'; + } else if (stream.disposition.lyrics + || (title.includes('signs')) || (title.includes('songs'))) { + strDisposition = '.signsandsongs'; + } + if (codec === 'ass' || codec === 'mov_text' || codec === 'ssa' || codec === 'subrip') { + boolTextSubs = true; + } + if (!boolTextSubs) { + return; + } + // Build subtitle file names. + const fileName = getFileName(args.originalLibraryFile._id); + const orignalFolder = getFileAbosluteDir(args.originalLibraryFile._id); + const tempsubsFile = [orignalFolder, '/', fileName]; + if (lang === '') { + tempsubsFile.push(`.und${strDisposition}.srt`); + } else { + tempsubsFile.push(`.${lang}${strDisposition}.srt`); + } + const subsFile = tempsubsFile.join(''); + // Send Commands. + if (fs.existsSync(subsFile) && !overwrite) { + return; + } + args.jobLog(`Extracting Subtitles at index ${stream.index}`); + subtitleSettings.processFile = true; + subtitleSettings.subOutput.push('-map', `0:s:${subIdx}`, subsFile); + }); + return subtitleSettings; +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + const subtitleSettings = buildSubtitleConfiguration(args); + + if (subtitleSettings.processFile) { + subtitleSettings.subOutput.forEach((element) => { + args.variables.ffmpegCommand.multiOutputArguments.push(element); + }); + } + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemoveCommentary/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemoveCommentary/1.0.0/index.ts new file mode 100644 index 000000000..5006d83d5 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemoveCommentary/1.0.0/index.ts @@ -0,0 +1,69 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Subtitles Remove Commentary', + description: 'Remove Commentary', + style: { + borderColor: '#6efefc', + }, + tags: 'subtitle', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +const removeCommentary = (args :IpluginInputArgs) => { + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type !== 'subtitle') { + return; + } + let title = ''; + if (stream.tags !== undefined) { + if (stream.tags.title !== undefined) { + title = stream.tags.title.toLowerCase(); + } + } + if (stream.disposition.commentary + || stream.disposition.description + || (title.includes('commentary')) + || (title.includes('description'))) { + args.jobLog(`Removing Subtitles at index ${stream.index} Commentary Detected`); + // eslint-disable-next-line no-param-reassign + stream.removed = true; + } + }); +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + removeCommentary(args); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemovebyLanguage/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemovebyLanguage/1.0.0/index.ts new file mode 100644 index 000000000..437c9df06 --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemovebyLanguage/1.0.0/index.ts @@ -0,0 +1,112 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Subtitles Remove by Language', + description: 'Remove by Language', + style: { + borderColor: '#6efefc', + }, + tags: 'subtitle', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + label: 'Keep Languages', + name: 'langTags', + type: 'string', + defaultValue: 'eng', + inputUI: { + type: 'text', + }, + tooltip: + 'Choose the subtitles languages you want to keep. Three letter format.' + + 'Seperate additional tags with commas eng,jpn,kor ', + }, + { + label: 'Keep Undefined', + name: 'keepUndefined', + type: 'boolean', + defaultValue: 'false', + inputUI: { + type: 'switch', + }, + tooltip: 'Keeps the Undefined Subtitles Streams', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const refineLangTags = (languages :any, langTags :string[]) => { + const master = langTags; + langTags.forEach((element :string) => { + const lang = languages.alpha3BToAlpha2(element); + master.push(lang); + }); + return master; +}; + +const removeNyLanguage = (args :IpluginInputArgs) => { + // eslint-disable-next-line import/no-unresolved + const languages = require('@cospired/i18n-iso-languages'); + const keepUndefined = Boolean(args.inputs.keepUndefined); + const langTagsUnTrimmed = String(args.inputs.langTags).toLowerCase().split(','); + const langTags: Array<string> = []; + langTagsUnTrimmed.forEach((element) => { + const trimedElement = element.trim(); + langTags.push(trimedElement); + }); + + const langTagsMaster = refineLangTags(languages, langTags); + + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type !== 'subtitle') { + return; + } + if (keepUndefined) { + if ((!stream.tags || !stream.tags.language || stream.tags.language.toLowerCase().includes('und'))) { + return; + } + } + if (stream.tags && stream.tags.language && langTagsMaster.includes(stream.tags.language.toLowerCase())) { + return; + } + args.jobLog(`Removing Subtitles at index ${stream.index} Unwanted Language`); + // eslint-disable-next-line no-param-reassign + stream.removed = true; + }); +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = async (args: IpluginInputArgs): Promise<IpluginOutputArgs> => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + const dependencies = ['@cospired/i18n-iso-languages']; + await args.installClassicPluginDeps(dependencies); + + removeNyLanguage(args); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +}; diff --git a/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemovebyProperty/1.0.0/index.ts b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemovebyProperty/1.0.0/index.ts new file mode 100644 index 000000000..f4c214f8a --- /dev/null +++ b/FlowPluginsTs/CommunityFlowPlugins/ffmpegCommand/ffmpegCommandSubtitlesRemovebyProperty/1.0.0/index.ts @@ -0,0 +1,76 @@ +import { + IpluginDetails, + IpluginInputArgs, + IpluginOutputArgs, +} from '../../../../FlowHelpers/1.0.0/interfaces/interfaces'; + +/* eslint no-plusplus: ["error", { "allowForLoopAfterthoughts": true }] */ +const details = () :IpluginDetails => ({ + name: 'Subtitles Remove by Title', + description: 'Remove by Title', + style: { + borderColor: '#6efefc', + }, + tags: 'subtitle', + isStartPlugin: false, + pType: '', + requiresVersion: '2.11.01', + sidebarPosition: -1, + icon: '', + inputs: [ + { + label: 'Title to Remove', + name: 'valuesToRemove', + type: 'string', + defaultValue: 'forced', + inputUI: { + type: 'text', + }, + tooltip: '\n Choose one Title to Remove ', + }, + ], + outputs: [ + { + number: 1, + tooltip: 'Continue to next plugin', + }, + ], +}); + +const removeByTitle = (args :IpluginInputArgs) => { + const valuesToRemove = String(args.inputs.valuesToRemove).toLowerCase().trim(); + args.variables.ffmpegCommand.streams.forEach((stream) => { + if (stream.codec_type !== 'subtitle') { + return; + } + let title = ''; + if (stream.tags !== undefined) { + if (stream.tags.title !== undefined) { + title = stream.tags.title.toLowerCase(); + } + } + if (title.includes(valuesToRemove)) { + // eslint-disable-next-line no-param-reassign + stream.removed = true; + } + }); +}; + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const plugin = (args:IpluginInputArgs):IpluginOutputArgs => { + const lib = require('../../../../../methods/lib')(); + // eslint-disable-next-line @typescript-eslint/no-unused-vars,no-param-reassign + args.inputs = lib.loadDefaultValues(args.inputs, details); + + removeByTitle(args); + + return { + outputFileObj: args.inputFileObj, + outputNumber: 1, + variables: args.variables, + }; +}; +export { + details, + plugin, +};