diff --git a/Community/Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js b/Community/Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js new file mode 100644 index 0000000..0a878b5 --- /dev/null +++ b/Community/Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js @@ -0,0 +1,268 @@ +/* eslint-disable no-await-in-loop */ +module.exports.dependencies = ['axios', '@cospired/i18n-iso-languages', 'path']; +const details = () => ({ + id: 'Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng', + Stage: 'Pre-processing', + Name: 'Remove all langs except native and English', + Type: 'Audio', + Operation: 'Transcode', + Description: `This plugin will remove all language audio tracks except the 'native' + (requires TMDB api key) and English. + 'Native' languages are the ones that are listed on imdb. It does an API call to + Radarr, Sonarr to check if the movie/series exists and grabs the IMDB id. As a last resort it + falls back to '[imdb-ttDIGITS] in the filename.`, + Version: '1.00', + Link: 'https://github.com/HaveAGitGat/Tdarr_Plugins/blob/master/Community/' + + 'Tdarr_Plugin_henk_Keep_Native_Lang_Plus_Eng.js', + Tags: 'pre-processing,configurable', + + Inputs: [ + { + name: 'user_langs', + tooltip: 'Input a comma separated list of ISO-639-2 languages. It will still keep English and undefined tracks.' + + '(https://en.wikipedia.org/wiki/List_of_ISO_639-2_codes 639-2 column)' + + '\\nExample:\\n' + + 'nld,nor', + }, + { + name: 'priority', + tooltip: 'Priority for either Radarr or Sonarr. Leaving it empty defaults to Radarr first.' + + '\\nExample:\\n' + + 'sonarr', + }, + { + name: 'api_key', + tooltip: 'Input your TMDB api key here. (https://www.themoviedb.org/)', + }, + { + name: 'radarr_api_key', + tooltip: 'Input your Radarr api key here.', + }, + { + name: 'radarr_url', + tooltip: 'Input your Radarr url here. (Without http://). Do include the port.' + + '\\nExample:\\n' + + '192.168.1.2:7878', + }, + { + name: 'sonarr_api_key', + tooltip: 'Input your Sonarr api key here.', + }, + { + name: 'sonarr_url', + tooltip: 'Input your Sonarr url here. (Without http://). Do include the port.' + + '\\nExample:\\n' + + '192.168.1.2:8989', + }, + ], +}); +const response = { + processFile: false, + preset: ', -map 0 ', + container: '.', + handBrakeMode: false, + FFmpegMode: true, + reQueueAfter: false, + infoLog: '', +}; + +const processStreams = (result, file, user_langs) => { + // eslint-disable-next-line global-require,import/no-unresolved + const languages = require('@cospired/i18n-iso-languages'); + const tracks = { + keep: [], + remove: [], + remLangs: '', + }; + let streamIndex = 0; + + const langsTemp = result.original_language; + let langs = []; + + if (Array.isArray(langsTemp)) { + // For loop because I thought some imdb stuff returns multiple languages + // Translates 'en' to 'eng', because imdb uses a different format compared to ffmpeg + for (let i = 0; i < langsTemp.length; i += 1) { + langs.push(languages.alpha2ToAlpha3B(langsTemp)); + } + } else { + langs.push(languages.alpha2ToAlpha3B(langsTemp)); + } + + if (user_langs) { + langs = langs.concat(user_langs); + } + if (!langs.includes('eng')) langs.push('eng'); + if (!langs.includes('und')) langs.push('und'); + + response.infoLog += 'Keeping languages: '; + // Print languages to UI + langs.forEach((l) => { + response.infoLog += `${languages.getName(l, 'en')}, `; + }); + + response.infoLog = `${response.infoLog.slice(0, -2)}\n`; + + for (let i = 0; i < file.ffProbeData.streams.length; i += 1) { + const stream = file.ffProbeData.streams[i]; + + if (stream.codec_type === 'audio') { + if (!stream.tags) { + response.infoLog += `☒No tags found on audio track ${streamIndex}. Keeping it. \n`; + tracks.keep.push(streamIndex); + streamIndex += 1; + // eslint-disable-next-line no-continue + continue; + } + if (langs.includes(stream.tags.language)) { + tracks.keep.push(streamIndex); + } else { + tracks.remove.push(streamIndex); + response.preset += `-map -0:a:${streamIndex} `; + tracks.remLangs += `${languages.getName(stream.tags.language, 'en')}, `; + } + streamIndex += 1; + } + } + response.preset += ' -c copy -max_muxing_queue_size 9999'; + return tracks; +}; + +const tmdbApi = async (filename, api_key, axios) => { + let fileName; + // If filename begins with tt, it's already an imdb id + if (filename) { + if (filename.substr(0, 2) === 'tt') { + fileName = filename; + } else { + const idRegex = /\[imdb-(tt\d*)]/; + const fileMatch = filename.match(idRegex); + // eslint-disable-next-line prefer-destructuring + if (fileMatch) fileName = fileMatch[1]; + } + } + + if (fileName) { + const result = await axios.get(`https://api.themoviedb.org/3/find/${fileName}?api_key=` + + `${api_key}&language=en-US&external_source=imdb_id`) + .then((resp) => (resp.data.movie_results.length > 0 ? resp.data.movie_results[0] : resp.data.tv_results[0])); + + if (!result) { + response.infoLog += '☒No IMDB result was found. \n'; + } + return result; + } + return null; +}; + +// eslint-disable-next-line consistent-return +const parseArrResponse = async (body, filePath, arr) => { + // eslint-disable-next-line default-case + switch (arr) { + case 'radarr': + // filePath = file + for (let i = 0; i < body.length; i += 1) { + if (body[i].movieFile) { + if (body[i].movieFile.relativePath) { + if (body[i].movieFile.relativePath === filePath) { + return body[i]; + } + } + } + } + break; + case 'sonarr': + // filePath = directory the file is in + // eslint-disable-next-line global-require,import/no-unresolved + const path = require('path'); + for (let i = 0; i < body.length; i += 1) { + if (body[i].path) { + if (path.basename(body[i].path) === path.basename(path.dirname(filePath))) { + return body[i]; + } + } + } + } +}; + +const plugin = async (file, librarySettings, inputs) => { + // eslint-disable-next-line global-require,import/no-unresolved + const axios = require('axios').default; + response.container = `.${file.container}`; + let prio = ['radarr', 'sonarr']; + let radarrResult = null; + let sonarrResult = null; + let tmdbResult = null; + + if (inputs.priority) { + if (inputs.priority === 'sonarr') { + prio = ['sonarr', 'radarr']; + } + } + + for (let i = 0; i < prio.length; i += 1) { + let imdbId; + // eslint-disable-next-line default-case + switch (prio[i]) { + case 'radarr': + if (tmdbResult) break; + if (inputs.radarr_api_key) { + radarrResult = await parseArrResponse( + await axios.get(`http://${inputs.radarr_url}/api/v3/movie?apiKey=${inputs.radarr_api_key}`) + .then((resp) => resp.data), + file.meta.FileName, 'radarr', + ); + + if (radarrResult) { + imdbId = radarrResult.imdbId; + response.infoLog += `Grabbed ID (${imdbId}) from Radarr \n`; + } else { + response.infoLog += 'Couldn\'t grab ID from Radarr/Sonarr, grabbing it from file name \n'; + imdbId = file.meta.FileName; + } + tmdbResult = await tmdbApi(imdbId, inputs.api_key, axios); + } + break; + case 'sonarr': + if (tmdbResult) break; + if (inputs.sonarr_api_key) { + sonarrResult = await parseArrResponse( + await axios.get(`http://${inputs.sonarr_url}/api/series?apikey=${inputs.sonarr_api_key}`) + .then((resp) => resp.data), + file.meta.Directory, 'sonarr', + ); + + if (sonarrResult) { + imdbId = sonarrResult.imdbId; + response.infoLog += `Grabbed ID (${imdbId}) from Sonarr \n`; + } else { + response.infoLog += 'Couldn\'t grab ID from Radarr/Sonarr, grabbing it from file name \n'; + imdbId = file.meta.FileName; + } + tmdbResult = await tmdbApi(imdbId, inputs.api_key, axios); + } + } + } + + if (tmdbResult) { + const tracks = processStreams(tmdbResult, file, inputs.user_langs ? inputs.user_langs.split(',') : ''); + + if (tracks.remove.length > 0) { + if (tracks.keep.length > 0) { + response.infoLog += `☑Removing tracks with languages: ${tracks.remLangs.slice(0, -2)}. \n`; + response.processFile = true; + response.infoLog += '\n'; + } else { + response.infoLog += '☒Cancelling plugin otherwise all audio tracks would be removed. \n'; + } + } else { + response.infoLog += '☒No audio tracks to be removed. \n'; + } + } else { + response.infoLog += '☒Couldn\'t find the IMDB id of this file. Skipping. \n'; + } + return response; +}; + +module.exports.details = details; +module.exports.plugin = plugin;