Compare commits
8 Commits
543dd99587
...
7be5905cf9
Author | SHA1 | Date | |
---|---|---|---|
7be5905cf9 | |||
f3aca08aea | |||
afb22b5aef | |||
1b751e0f6e | |||
885000e21a | |||
9e9906ffed | |||
93ff21021c | |||
8c341c11fa |
@ -1,5 +1,5 @@
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_MM1_Full_Stack',
|
||||
id: 'Tdarr_Plugin_MM1_MrMeeb_Full_Stack',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'MrMeeb Full Stack Processing',
|
||||
Type: 'Video',
|
||||
@ -7,7 +7,56 @@ const details = () => ({
|
||||
Description: 'Automatically removes TrueHD and replaces with a suitable AC3 5.1 track, either pre-existing or created.\n Removes non-English and non-German subtitles.\n Transcodes x264 content to HEVC using NVENC, with a focus on quality',
|
||||
Version: '0.1',
|
||||
Tags: 'pre-processing',
|
||||
Inputs: [],
|
||||
Inputs: [
|
||||
{
|
||||
name: 'process_video',
|
||||
type: 'boolean',
|
||||
defaultValue: true,
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'true',
|
||||
'false',
|
||||
],
|
||||
},
|
||||
tooltip: `Select whether to process video, or just copy over`,
|
||||
},
|
||||
{
|
||||
name: 'process_audio',
|
||||
type: 'boolean',
|
||||
defaultValue: true,
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'true',
|
||||
'false',
|
||||
],
|
||||
},
|
||||
tooltip: `Select whether to process audio, or just copy over`,
|
||||
},
|
||||
{
|
||||
name: 'process_subs',
|
||||
type: 'boolean',
|
||||
defaultValue: true,
|
||||
inputUI: {
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
'true',
|
||||
'false',
|
||||
],
|
||||
},
|
||||
tooltip: `Select whether to process subs, or just copy over`,
|
||||
},
|
||||
{
|
||||
name: 'crf',
|
||||
type: 'string', // set the data type of the input ('string', 'number', 'boolean')
|
||||
defaultValue: '19', // set the default value of the input incase the user enters no input
|
||||
inputUI: {
|
||||
type: 'text', // specify how the input UI will appear to the user ('text' or 'dropdown')
|
||||
},
|
||||
tooltip: `Enter CRF value used for video transcoding.`,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
|
||||
@ -16,12 +65,12 @@ const details = () => ({
|
||||
// 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);
|
||||
const lib = require('../methods/lib')();
|
||||
// load default plugin inputs
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
|
||||
// Load response object
|
||||
const response = {
|
||||
// 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.
|
||||
@ -53,16 +102,28 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
const processAudio = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
if (inputs.process_audio == false) {
|
||||
|
||||
response.infoLog += `Processing audio set to skip. Copying all present tracks over.\n`
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
// Function variables
|
||||
let audioIdx = -1;
|
||||
let audioIdxTruehd = -1;
|
||||
let trueHD = -1;
|
||||
let trueHDi = -1;
|
||||
let audioIdxDtshdma = -1;
|
||||
let dtshdma = -1;
|
||||
let dtshdmai = -1;
|
||||
let ac3Count = 0;
|
||||
let dtsHandled = 0;
|
||||
|
||||
console.log(` === Running processAudio function === `)
|
||||
|
||||
// Go through all streams, hunting for TrueHD
|
||||
// Go through all streams, hunting for TrueHD/DTS-HD MA
|
||||
for (let i = 0; i < file.ffProbeData.streams.length; i ++) {
|
||||
|
||||
// Work with audio streams only
|
||||
@ -134,7 +195,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
if (trueHDReplacementCount <= 1) {
|
||||
|
||||
ffmpegAudioFirstTrack = ` -map 0:a:${audioIdxTruehd} -metadata:s:a:0 "title=${MMCodec} - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:${audioIdxTruehd} -metadata:s:a:0 "title=${MMCodec} - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
||||
|
||||
}
|
||||
else {
|
||||
@ -186,14 +247,14 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
response.infoLog += 'TrueHD track exists, but no AC3 compatibility track is present. Creating one, and downmixing to 6 channels.\n'
|
||||
|
||||
ffmpegAudioFirstTrack = `-map 0:a:${trueHD} -c:a:0 ac3 -b:a:0 640k -ac:a:0 6 -metadata:s:a:0 "title=AC3 - 5.1 - MM" -disposition:a:0 default`
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:${trueHD} -c:a:0 ac3 -b:a:0 640k -ac:a:0 6 -metadata:s:a:0 "title=AC3 - 5.1 - MM" -disposition:a:0 default`
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
response.infoLog += 'TrueHD track exists, but no AC3 compatibility track is present. Creating one.\n'
|
||||
|
||||
ffmpegAudioFirstTrack = `-map 0:a:${trueHD} -c:a:0 ac3 -b:a:0 640k -ac:a:0 ${file.ffProbeData.streams[trueHDi].channels} -metadata:s:a:0 "title=AC3 - 5.1 - MM" -disposition:a:0 default`
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:${trueHD} -c:a:0 ac3 -b:a:0 640k -ac:a:0 ${file.ffProbeData.streams[trueHDi].channels} -metadata:s:a:0 "title=AC3 - 5.1 - MM" -disposition:a:0 default`
|
||||
|
||||
}
|
||||
|
||||
@ -202,6 +263,145 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
}
|
||||
|
||||
// Identify if stream is DTS-HD MA
|
||||
if (file.ffProbeData.streams[i].codec_name.toLowerCase() === 'dts') {
|
||||
|
||||
// Check if DTS-HD MA has already been handled by a previous run
|
||||
// Assume that it will be the second stream (first audio stream) - it should be, if already handled
|
||||
MMChannelLayout = file.ffProbeData.streams[i].channel_layout.replace('(side)', ''); // Have to declare this here to make the if statement work. Leaving it in place further down as well
|
||||
|
||||
if (file.ffProbeData.streams[i].tags.title === `DTS-HD MA - ${MMChannelLayout} - MM`) {
|
||||
|
||||
console.log(`Stream ${i} (audio stream ${audioIdx}) is DTS-HD MA and has already been handled. Safe to assume this file has already been treated.`);
|
||||
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is DTS-HD MA and has already been handled. Safe to assume this file has already been treated.\n`
|
||||
|
||||
// Mark DTS-HD MA handling process as complete causing everything else audio-related to be skipped
|
||||
dtsHandled = 1
|
||||
|
||||
} else {
|
||||
|
||||
console.log(`Stream ${i} (audio stream ${audioIdx}) is DTS-HD MA and has not been handled. Commencing DTS-HD MA handling process`);
|
||||
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is DTS-HD MA. Commencing DTS-HD MA handling process.\n`
|
||||
|
||||
};
|
||||
|
||||
dtshdma = audioIdx; // Index of DTS-HD MA track relative to other audio tracks
|
||||
dtshdmai = i; // Index of DTS-HD MA track within all tracks (audio, video, subs, etc)
|
||||
let dtshdmaLang = file.ffProbeData.streams[i].tags.language // Language of DTS-HD MA track
|
||||
let dtshdmaReplacementCount = 0 // Number of DTS-HD MA replacement candidates
|
||||
|
||||
// Look if there is a suitable AC3 5.1 track to use alongside it
|
||||
|
||||
for (let i = 0; i < file.ffProbeData.streams.length; i ++) { // Iterate through all streams
|
||||
|
||||
if (dtsHandled == 0 && file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') { // If the stream is an audio stream
|
||||
|
||||
audioIdxDtshdma++; // Index of replacement track
|
||||
MMCodec = file.ffProbeData.streams[i].codec_name.toUpperCase();
|
||||
MMChannelLayout = file.ffProbeData.streams[i].channel_layout.replace('(side)', '');
|
||||
|
||||
try {
|
||||
|
||||
// Identify if stream is commentary track
|
||||
if (
|
||||
file.ffProbeData.streams[i].tags.title != undefined &&
|
||||
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
|
||||
file.ffProbeData.streams[i].disposition.comment == 1
|
||||
) {
|
||||
|
||||
console.log(`Stream ${i} (audio stream ${audioIdxDtshdma}) is probably a commentary track. Removing.`)
|
||||
response.infoLog += `Stream ${i} (audio stream ${audioIdxDtshdma}) is probably a commentary track. Removing.\n`
|
||||
|
||||
}
|
||||
|
||||
// Identify if stream is AC3 5.1
|
||||
else if (
|
||||
file.ffProbeData.streams[i].codec_name.toLowerCase() === 'ac3' && // is codec AC3
|
||||
file.ffProbeData.streams[i].channels === 6 && // does it have 6 channels (5.1)
|
||||
file.ffProbeData.streams[i].tags.language === dtshdmaLang // does it have the same language as the DTS-HD MA track
|
||||
) {
|
||||
|
||||
console.log(`Stream ${i} (audio stream ${audioIdxDtshdma}) is AC3 5.1 and in matching language. Keeping alongside the DTS-HD MA track.`);
|
||||
console.log(`${MMCodec} <- Should be AC3`)
|
||||
|
||||
response.infoLog += `Stream ${i} (audio stream ${audioIdxDtshdma}) is AC3 5.1 and in matching language. Keeping alongside the DTS-HD MA track.\n`
|
||||
response.infoLog +=`${MMCodec} <- Should be AC3\n`
|
||||
|
||||
dtshdmaReplacementCount++ // Add to the DTS-HD MA replacement count
|
||||
|
||||
if (dtshdmaReplacementCount <= 1) { // If we found a single DTS-HD MA replacement...
|
||||
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:${dtshdma} -metadata:s:a:0 "title=DTS-HD MA - ${MMChannelLayout} - MM" -disposition:a:0 default -map 0:a:${audioIdxDtshdma} -metadata:s:a:1 "title=${MMCodec} - ${MMChannelLayout} - MM"` // Keep both the DTS-HD MA and AC3 tracks, label them
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
console.log('More than 1 suitable AC3 5.1 track has been detected - something has probably gone wrong in detection of commentary tracks. The first found track will be kept')
|
||||
response.infoLog += 'More than 1 suitable AC3 5.1 track has been detected - something has probably gone wrong in detection of commentary tracks. The first found track will be kept\n'
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
else if (
|
||||
file.ffProbeData.streams[i].codec_name.toLowerCase() === 'ac3' && // is codec AC3
|
||||
file.ffProbeData.streams[i].channels === 6 && // does it have 6 channels (5.1)
|
||||
file.ffProbeData.streams[i].tags.language !== dtshdmaLang && // does it NOT match the language of the DTS-HD MA track
|
||||
file.ffProbeData.streams[i].tags.language.toLowerCase() == 'eng' // is the AC3 track in English
|
||||
) {
|
||||
|
||||
console.log(`Stream ${i} (audio stream ${audioIdxDtshdma}) is AC3 5.1. It does not match the native language of the movie, but is in English. Probably want to keep.`);
|
||||
response.infoLog += `Stream ${i} (audio stream ${audioIdxDtshdma}) is AC3 5.1. It does not match the native language of the movie, but is in English. Probably want to keep.\n`
|
||||
|
||||
ffmpegAudioOtherTracks += ` -map 0:a:${audioIdxDtshdma}` // Add the track to the list of additional tracks
|
||||
|
||||
}
|
||||
|
||||
else if (file.ffProbeData.streams[i].codec_name.toLowerCase() !== 'dts') { // Remove any track that isn't AC3 since we already have DTS-HD MA to work with
|
||||
|
||||
console.log(`Stream ${i} (audio stream ${audioIdxDtshdma}) isn't AC3 5.1, and DTS-HD MA is already available to create an AC3 5.1 track if not present. Removing.`)
|
||||
response.infoLog += `Stream ${i} (audio stream ${audioIdxDtshdma}) isn't AC3 5.1, and DTS-HD MA is already available to create an AC3 5.1 track if not present. Removing.\n`
|
||||
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
if (dtsHandled == 0 && dtshdmaReplacementCount == 0) { // If we didn't find a replacement candidate
|
||||
|
||||
console.log('A suitable DTS-HD MA replacement track is not available. Creating my own.')
|
||||
//console.log(`${MMCodec} <- Should be AC3`)
|
||||
|
||||
|
||||
if (file.ffProbeData.streams[dtshdmai].channels > 6) { // If the DTS-HD MA track has more than 6 channels, downmix to 6 channels
|
||||
|
||||
response.infoLog += 'DTS-HD MA track exists, but no AC3 compatibility track is present. Creating one, and downmixing to 6 channels.\n'
|
||||
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:${dtshdma} -metadata:s:a:0 "title=DTS-HD MA - ${MMChannelLayout} - MM" -disposition:a:0 default -map 0:a:${dtshdma} -c:a:1 ac3 -b:a:1 640k -ac:a:1 6 -metadata:s:a:1 "title=AC3 - 5.1 - MM"`
|
||||
|
||||
}
|
||||
else { // Otherwise, create an AC3 track with the same channel layout
|
||||
|
||||
response.infoLog += 'DTS-HD MA track exists, but no AC3 compatibility track is present. Creating one.\n'
|
||||
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:${dtshdma} -metadata:s:a:0 "title=DTS-HD MA - ${MMChannelLayout} - MM" -disposition:a:0 default -map 0:a:${dtshdma} -c:a:1 ac3 -b:a:1 640k -ac:a:1 ${file.ffProbeData.streams[dtshdmai].channels} -metadata:s:a:1 "title=AC3 - 5.1 - MM"`
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Mark DTS-HD MA handling process as complete, skipping the logic for no THD/DTS-HD MA. Was already set if the run had completed before.
|
||||
dtsHandled = 1
|
||||
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
@ -210,8 +410,8 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
};
|
||||
|
||||
// Logic for if no TrueHD track present
|
||||
if (trueHD < 0) {
|
||||
// Logic for if no TrueHD/DTS-HD MA track present
|
||||
if (trueHD < 0 && dtsHandled == 0) {
|
||||
|
||||
console.log('No TrueHD track is present in the file.')
|
||||
response.infoLog += `No TrueHD track is present.\n`
|
||||
@ -272,8 +472,8 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
//LOGIC NEEDS ADDING FOR IF AN AC3 TRACK HAS BEEN CREATED WITH FEWER THAN 6 CHANNELS
|
||||
|
||||
console.log(`Neither a TrueHD track or a desireable AC3 5.1 track exist. Selecting the first audio track and converting it to AC3.`)
|
||||
response.infoLog += 'Neither a TrueHD track or a desireable AC3 5.1 track exist. Selecting the first audio track and converting it to AC3.\n'
|
||||
console.log(`Neither a TrueHD track nor a desireable AC3 5.1 track exist. Selecting the first audio track and converting it to AC3.`)
|
||||
response.infoLog += 'Neither a TrueHD track nor a desireable AC3 5.1 track exist. Selecting the first audio track and converting it to AC3.\n'
|
||||
|
||||
if (file.ffProbeData.streams[1].codec_type.toLowerCase() === 'audio') {
|
||||
|
||||
@ -286,12 +486,12 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
if ( file.ffProbeData.streams[1].channels > 6 ) {
|
||||
|
||||
ffmpegAudioFirstTrack = ` -map 0:a:0 -c:a:0 ac3 -b:a:0 640k -ac:a:0 6 -metadata:s:a:0 "title=AC3 - 5.1 - MM" -disposition:a:0 default`
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:0 -c:a:0 ac3 -b:a:0 640k -ac:a:0 6 -metadata:s:a:0 "title=AC3 - 5.1 - MM" -disposition:a:0 default`
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
ffmpegAudioFirstTrack = ` -map 0:a:0 -c:a:0 ac3 -b:a:0 640k -ac:a:0 ${file.ffProbeData.streams[1].channels} -metadata:s:a:0 "title=AC3 - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:0 -c:a:0 ac3 -b:a:0 640k -ac:a:0 ${file.ffProbeData.streams[1].channels} -metadata:s:a:0 "title=AC3 - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
||||
|
||||
}
|
||||
|
||||
@ -355,7 +555,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
console.log(`Stream ${i} (audio stream ${audioIdx}) is AC3 5.1, but not labelled correctly. Correcting.`)
|
||||
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is AC3 5.1, but not labelled correctly. Correcting.\n`
|
||||
ffmpegAudioFirstTrack = ` -map 0:a:${audioIdx} -metadata:s:a:0 "title=${MMCodec} - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:${audioIdx} -metadata:s:a:0 "title=${MMCodec} - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
||||
|
||||
}
|
||||
|
||||
@ -394,7 +594,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
let MMCodec = file.ffProbeData.streams[1].codec_name.toUpperCase();
|
||||
let MMChannelLayout = file.ffProbeData.streams[1].channel_layout.replace('(side)', '');
|
||||
|
||||
ffmpegAudioFirstTrack = ` -map 0:a:0 -metadata:s:a:0 "title=${MMCodec} - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
||||
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:0 -metadata:s:a:0 "title=${MMCodec} - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
||||
|
||||
console.log(`Copying over other AC3 5.1 tracks`)
|
||||
|
||||
@ -429,8 +629,18 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
const processSubs = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
if (inputs.process_subs == false) {
|
||||
|
||||
response.infoLog += `Processing subs set to skip. Copying all present tracks over.\n`
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
// Function variables
|
||||
let subIdx = -1;
|
||||
|
||||
@ -439,11 +649,11 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
// Go through all subtitles, removing non-English/German subs and commentary subs
|
||||
for (let i = 0; i < file.ffProbeData.streams.length; i ++) {
|
||||
|
||||
console.log(file.ffProbeData.streams[i].codec_type)
|
||||
|
||||
// Work with subtitle streams only
|
||||
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') {
|
||||
|
||||
//console.log(`Stream ${i} is a subtitle stream`);
|
||||
|
||||
// Keep track of subtitle stream relative numbers
|
||||
try {
|
||||
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") {
|
||||
@ -451,10 +661,18 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
}
|
||||
} catch (err) {}
|
||||
|
||||
//console.log(`language = ${file.ffProbeData.streams[i].tags.language.toLowerCase()}`)
|
||||
// Remove any subtitles with no language set
|
||||
if (file.ffProbeData.streams[i].tags.language == undefined) {
|
||||
|
||||
console.log(`Stream ${i} (subtitle stream ${subIdx}) has no language. Removing.`)
|
||||
response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) has no language. Removing. \n`
|
||||
|
||||
ffmpegSubs += ` -map -0:s:${subIdx}`
|
||||
|
||||
}
|
||||
|
||||
// Remove any subtitles not in English or German
|
||||
if (
|
||||
else if (
|
||||
file.ffProbeData.streams[i].tags.language.toLowerCase() !== "eng" &&
|
||||
file.ffProbeData.streams[i].tags.language.toLowerCase() !== "ger"
|
||||
) {
|
||||
@ -494,8 +712,18 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
const processVideo = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
if (inputs.process_video == false) {
|
||||
|
||||
response.infoLog += `Processing video set to skip. Copying all present tracks over.\n`
|
||||
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
console.log(` === Running processVideo function === `)
|
||||
|
||||
// Check if video track is already HEVC
|
||||
@ -511,12 +739,14 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
console.log('File needs transcoding to HEVC. Converting now.')
|
||||
response.infoLog += `☒ File needs transcoding to HEVC.\n`
|
||||
|
||||
ffmpegVideo = ` -c:v hevc_nvenc -b:v 0 -preset p7 -cq 1 -rc-lookahead 32 -bf 0`
|
||||
ffmpegVideo = ` -c:v libx265 -pix_fmt yuv420p10le -preset slow -x265-params crf=${inputs.crf}:bframes=8:rc-lookahead=32:b-intra=1:aq-mode=3`
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
// Start running functions
|
||||
|
||||
processAudio(file, librarySettings, inputs, otherArguments);
|
||||
@ -533,7 +763,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
response.container = `.${file.container}`;
|
||||
|
||||
response.preset = `,-c copy -map 0:v?${ffmpegVideo}${ffmpegAudioFirstTrack}${ffmpegAudioOtherTracks} -map 0:s?${ffmpegSubs} -map 0:d? -max_muxing_queue_size 9999`;
|
||||
response.preset = `,-c copy -map 0:v?${ffmpegVideo} -map 0:a?${ffmpegAudioFirstTrack}${ffmpegAudioOtherTracks} -map 0:s?${ffmpegSubs} -map 0:d? -max_muxing_queue_size 9999`;
|
||||
|
||||
console.log(response.preset)
|
||||
|
||||
@ -553,7 +783,7 @@ const plugin = (file, librarySettings, inputs, otherArguments) => {
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
module.exports.details = details;
|
||||
module.exports.plugin = plugin;
|
323
Tdarr_Plugin_MM_Keep_Native_Lang_Plus_Eng.js
Normal file
323
Tdarr_Plugin_MM_Keep_Native_Lang_Plus_Eng.js
Normal file
@ -0,0 +1,323 @@
|
||||
/* eslint-disable no-await-in-loop */
|
||||
module.exports.dependencies = ['axios@0.27.2', '@cospired/i18n-iso-languages'];
|
||||
// tdarrSkipTest
|
||||
const details = () => ({
|
||||
id: 'Tdarr_Plugin_MM_Keep_Native_Lang_Plus_Eng',
|
||||
Stage: 'Pre-processing',
|
||||
Name: 'Remove all langs except native and English - MM',
|
||||
Type: 'Audio',
|
||||
Operation: 'Transcode',
|
||||
Description: `(Updated version of Henk's plugin, provided by them in Discord' 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 the IMDB id in the filename.`,
|
||||
Version: '1.2',
|
||||
Tags: 'pre-processing,configurable',
|
||||
Inputs: [
|
||||
{
|
||||
name: 'user_langs',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
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',
|
||||
type: 'string',
|
||||
defaultValue: 'Radarr',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Priority for either Radarr or Sonarr. Leaving it empty defaults to Radarr first.' +
|
||||
'\\nExample:\\n' +
|
||||
'sonarr',
|
||||
},
|
||||
{
|
||||
name: 'api_key',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Input your TMDB api (v3) key here. (https://www.themoviedb.org/)',
|
||||
},
|
||||
{
|
||||
name: 'radarr_api_key',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: 'Input your Radarr api key here.',
|
||||
},
|
||||
{
|
||||
name: 'radarr_url',
|
||||
type: 'string',
|
||||
defaultValue: '192.168.1.2:7878',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip:
|
||||
'Input your Radarr url here. (Without http://). Do include the port.' +
|
||||
'\\nExample:\\n' +
|
||||
'192.168.1.2:7878',
|
||||
},
|
||||
{
|
||||
name: 'sonarr_api_key',
|
||||
type: 'string',
|
||||
defaultValue: '',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
tooltip: 'Input your Sonarr api key here.',
|
||||
},
|
||||
{
|
||||
name: 'sonarr_url',
|
||||
type: 'string',
|
||||
defaultValue: '192.168.1.2:8989',
|
||||
inputUI: {
|
||||
type: 'text',
|
||||
},
|
||||
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 import/no-unresolved
|
||||
const languages = require('@cospired/i18n-iso-languages');
|
||||
const tracks = {
|
||||
keep: [],
|
||||
remove: [],
|
||||
remLangs: '',
|
||||
};
|
||||
let streamIndex = 0;
|
||||
|
||||
// If the original language is pulled as Chinese 'cn' is used. iso-language expects 'zh' for Chinese.
|
||||
const langsTemp =
|
||||
result.original_language === 'cn' ? 'zh' : result.original_language;
|
||||
|
||||
let langs = [];
|
||||
|
||||
langs.push(languages.alpha2ToAlpha3B(langsTemp));
|
||||
|
||||
// Some console reporting for clarification of what the plugin is using and reporting.
|
||||
response.infoLog += `Original language: ${langsTemp}, Using code: ${languages.alpha2ToAlpha3B(
|
||||
langsTemp,
|
||||
)}\n`;
|
||||
|
||||
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 (const stream of file.ffProbeData.streams) {
|
||||
|
||||
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 (stream.tags.language) {
|
||||
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;
|
||||
} else {
|
||||
response.infoLog += `☒No language tag found on audio track ${streamIndex}. Keeping it. \n`;
|
||||
}
|
||||
}
|
||||
}
|
||||
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.substring(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 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':
|
||||
return body.movie;
|
||||
case 'sonarr':
|
||||
return body.series;
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const plugin = async (file, librarySettings, inputs, otherArguments) => {
|
||||
const lib = require('../methods/lib')();
|
||||
// eslint-disable-next-line no-unused-vars,no-param-reassign
|
||||
inputs = lib.loadDefaultValues(inputs, details);
|
||||
// eslint-disable-next-line 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'];
|
||||
}
|
||||
}
|
||||
|
||||
const fileNameEncoded = encodeURIComponent(file.meta.FileName);
|
||||
|
||||
for (const arr of prio) {
|
||||
let imdbId;
|
||||
// eslint-disable-next-line default-case
|
||||
switch (arr) {
|
||||
case 'radarr':
|
||||
if (tmdbResult) break;
|
||||
if (inputs.radarr_api_key) {
|
||||
radarrResult = await parseArrResponse(
|
||||
await axios
|
||||
.get(
|
||||
`${inputs.radarr_url}/api/v3/parse?apikey=${inputs.radarr_api_key}&title=${fileNameEncoded}`,
|
||||
)
|
||||
.then((resp) => resp.data),
|
||||
fileNameEncoded,
|
||||
'radarr',
|
||||
);
|
||||
|
||||
if (radarrResult) {
|
||||
imdbId = radarrResult.imdbId;
|
||||
response.infoLog += `Grabbed ID (${imdbId}) from Radarr \n`;
|
||||
const languages = require('@cospired/i18n-iso-languages');
|
||||
tmdbResult = {original_language: languages.getAlpha2Code(radarrResult.originalLanguage.name, 'en')};
|
||||
} else {
|
||||
response.infoLog += "Couldn't grab ID from Radarr \n";
|
||||
imdbId = fileNameEncoded;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'sonarr':
|
||||
if (tmdbResult) break;
|
||||
if (inputs.sonarr_api_key) {
|
||||
sonarrResult = await parseArrResponse(
|
||||
await axios.get(
|
||||
`${inputs.sonarr_url}/api/v3/parse?apikey=${inputs.sonarr_api_key}&title=${fileNameEncoded}`
|
||||
)
|
||||
.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 Sonarr \n";
|
||||
imdbId = fileNameEncoded;
|
||||
}
|
||||
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;
|
Loading…
x
Reference in New Issue
Block a user