856 lines
36 KiB
JavaScript
856 lines
36 KiB
JavaScript
const details = () => ({
|
|
id: 'Tdarr_Plugin_MM1_MrMeeb_Full_Stack',
|
|
Stage: 'Pre-processing',
|
|
Name: 'MrMeeb Full Stack Processing',
|
|
Type: 'Video',
|
|
Operation: 'Transcode',
|
|
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: [
|
|
{
|
|
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: 'remove_commentary',
|
|
type: 'boolean',
|
|
defaultValue: true,
|
|
inputUI: {
|
|
type: 'dropdown',
|
|
options: [
|
|
'true',
|
|
'false',
|
|
],
|
|
},
|
|
tooltip: `Select whether to remove commentary audio and subtitle tracks`,
|
|
},
|
|
{
|
|
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.`,
|
|
},
|
|
],
|
|
});
|
|
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
// 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.
|
|
// Examples
|
|
// HandBrake
|
|
// '-Z "Very Fast 1080p30"'
|
|
// FFmpeg
|
|
// '-sn <io> -map_metadata -1 -c:v copy -c:a copy'
|
|
container: '.mp4', // The container of the transcoded output file.
|
|
handBrakeMode: false, // Set whether to use HandBrake or FFmpeg for transcoding
|
|
FFmpegMode: false,
|
|
infoLog: '', // This will be shown when the user clicks the 'i' (info) button on a file in the output queue if
|
|
// it has been skipped.
|
|
// Give reasons why it has been skipped ('File has no title metadata, File meets conditions!')
|
|
|
|
// Optional (include together)
|
|
//file,
|
|
//removeFromDB: false, // Tell Tdarr to remove file from database if true
|
|
//updateDB: false, // Change file object above and update database if true
|
|
};
|
|
|
|
// Required global variables for functions
|
|
let ffmpegAudioFirstTrack = '';
|
|
let ffmpegAudioOtherTracks = '';
|
|
let ffmpegSubs = '';
|
|
let ffmpegVideo = '';
|
|
|
|
// Defining functions
|
|
|
|
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/DTS-HD MA
|
|
for (let i = 0; i < file.ffProbeData.streams.length; i ++) {
|
|
|
|
// Work with audio streams only
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
|
|
|
|
let MMCodec = file.ffProbeData.streams[i].codec_name.toUpperCase();
|
|
let MMChannelLayout = file.ffProbeData.streams[i].channel_layout.replace('(side)', '');
|
|
|
|
//console.log(`Stream ${i} is an audio stream`);
|
|
|
|
// Keep track of audio stream relative numbers
|
|
try {
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") {
|
|
audioIdx++;
|
|
}
|
|
} catch (err) {}
|
|
|
|
// Identify wanted tracks
|
|
try {
|
|
|
|
// Identify if stream is TrueHD
|
|
if (file.ffProbeData.streams[i].codec_name.toLowerCase() === 'truehd') {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdx}) is TrueHD. Commencing TrueHD handling process`);
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is TrueHD. Commencing TrueHD handling process.\n`
|
|
|
|
trueHD = audioIdx;
|
|
trueHDi = i;
|
|
let trueHDLang = file.ffProbeData.streams[i].tags.language
|
|
let trueHDReplacementCount = 0
|
|
|
|
// Look for a suitable AC3 5.1 track to replace it
|
|
|
|
for (let i = 0; i < file.ffProbeData.streams.length; i ++) {
|
|
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
|
|
|
|
audioIdxTruehd++;
|
|
MMCodec = file.ffProbeData.streams[i].codec_name.toUpperCase();
|
|
MMChannelLayout = file.ffProbeData.streams[i].channel_layout.replace('(side)', '');
|
|
|
|
try {
|
|
|
|
if (
|
|
(file.ffProbeData.streams[i].tags.title != undefined &&
|
|
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
|
|
file.ffProbeData.streams[i].disposition.comment == 1) &&
|
|
inputs.remove_commentary == true
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to remove.`)
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to remove.\n`
|
|
|
|
}
|
|
|
|
else if (
|
|
(file.ffProbeData.streams[i].tags.title != undefined &&
|
|
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
|
|
file.ffProbeData.streams[i].disposition.comment == 1) &&
|
|
inputs.remove_commentary == false
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to keep.`)
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to keep.\n`
|
|
ffmpegAudioOtherTracks += ` -map 0:a:${audioIdxTruehd}`
|
|
|
|
}
|
|
|
|
// Identify if stream is AC3 5.1
|
|
else if (
|
|
file.ffProbeData.streams[i].codec_name.toLowerCase() === 'ac3' &&
|
|
file.ffProbeData.streams[i].channels === 6 &&
|
|
file.ffProbeData.streams[i].tags.language === trueHDLang
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) is AC3 5.1 and in matching language. Suitable replacement.`);
|
|
console.log(`${MMCodec} <- Should be AC3`)
|
|
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) is AC3 5.1 and in matching language. Suitable replacement.\n`
|
|
response.infoLog +=`${MMCodec} <- Should be AC3\n`
|
|
|
|
trueHDReplacementCount++
|
|
|
|
if (trueHDReplacementCount <= 1) {
|
|
|
|
ffmpegAudioFirstTrack = ` -map -0:a -map 0:a:${audioIdxTruehd} -metadata:s:a:0 "title=${MMCodec} - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
|
|
|
}
|
|
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' &&
|
|
file.ffProbeData.streams[i].channels === 6 &&
|
|
file.ffProbeData.streams[i].tags.language !== trueHDLang &&
|
|
file.ffProbeData.streams[i].tags.language.toLowerCase() == 'eng'
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) 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 ${audioIdxTruehd}) 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:${audioIdxTruehd}`
|
|
|
|
}
|
|
|
|
else if (file.ffProbeData.streams[i].codec_name.toLowerCase() !== 'truehd') {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) isn't AC3 5.1, and TrueHD is already available to create an AC3 5.1 track if not present. Removing.`)
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) isn't AC3 5.1, and TrueHD is already available to create an AC3 5.1 track if not present. Removing.\n`
|
|
|
|
};
|
|
|
|
} catch (error) {
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
if (trueHDReplacementCount = 0) {
|
|
|
|
console.log('A suitable TrueHD replacement track is not available. Creating my own.')
|
|
//console.log(`${MMCodec} <- Should be AC3`)
|
|
|
|
|
|
if (file.ffProbeData.streams[trueHDi].channels > 6) {
|
|
|
|
response.infoLog += 'TrueHD track exists, but no AC3 compatibility track is present. Creating one, and downmixing to 6 channels.\n'
|
|
|
|
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 -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`
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Identify if stream is DTS-HD MA
|
|
if (file.ffProbeData.streams[i].profile != undefined && file.ffProbeData.streams[i].profile.toLowerCase() === 'dts-hd ma') {
|
|
|
|
// 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) &&
|
|
inputs.remove_commentary == true
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdxDtshdma}) is probably a commentary track. Selected to remove.`)
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdxDtshdma}) is probably a commentary track. Selected to remove.\n`
|
|
|
|
}
|
|
|
|
else if (
|
|
(file.ffProbeData.streams[i].tags.title != undefined &&
|
|
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
|
|
file.ffProbeData.streams[i].disposition.comment == 1) &&
|
|
inputs.remove_commentary == false
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to keep.`)
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdxTruehd}) is probably a commentary track. Selected to keep.\n`
|
|
ffmpegAudioOtherTracks += ` -map 0:a:${audioIdxTruehd}`
|
|
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
// 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`
|
|
audioIdx = -1
|
|
|
|
for (let i = 0; i < file.ffProbeData.streams.length; i ++) {
|
|
|
|
// Work with audio streams only
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
|
|
|
|
//console.log(`Stream ${i} is an audio stream`);
|
|
|
|
// Keep track of audio stream relative numbers
|
|
try {
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") {
|
|
audioIdx++;
|
|
}
|
|
} catch (err) {}
|
|
|
|
try {
|
|
// Try to identify commentary tracks
|
|
if (
|
|
(file.ffProbeData.streams[i].tags.title != undefined &&
|
|
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
|
|
file.ffProbeData.streams[i].disposition.comment == 1) &&
|
|
inputs.remove_commentary == true
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Selected to remove`)
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Selected to remove.\n`
|
|
|
|
}
|
|
|
|
else if (
|
|
(file.ffProbeData.streams[i].tags.title != undefined &&
|
|
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
|
|
file.ffProbeData.streams[i].disposition.comment == 1) &&
|
|
inputs.remove_commentary == false
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Selected to keep.`)
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is probably a commentary track. Selected to keep.\n`
|
|
ffmpegAudioOtherTracks += ` -map 0:a:${audioIdx}`
|
|
|
|
}
|
|
|
|
else if (
|
|
file.ffProbeData.streams[i].codec_name.toLowerCase() === 'ac3' &&
|
|
file.ffProbeData.streams[i].channels == 6
|
|
) {
|
|
|
|
console.log(`Stream ${i} (audio stream ${audioIdx}) is an AC3 5.1 track`)
|
|
response.infoLog += `Stream ${i} (audio stream ${audioIdx}) is an AC3 5.1 track.\n`
|
|
ac3Count++
|
|
//console.log(`ac3Count = ${ac3Count}`)
|
|
|
|
}
|
|
|
|
//console.log(`codec_type = ${file.ffProbeData.streams[i].codec_name.toLowerCase()}`)
|
|
//console.log(`channels = ${file.ffProbeData.streams[i].channels == 6}`)
|
|
//console.log(`ac3Count = ${ac3Count}`)
|
|
|
|
} catch (error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ac3Count == 0) {
|
|
|
|
//LOGIC NEEDS ADDING FOR IF AN AC3 TRACK HAS BEEN CREATED WITH FEWER THAN 6 CHANNELS
|
|
|
|
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') {
|
|
|
|
let MMCodec = file.ffProbeData.streams[1].codec_name.toUpperCase();
|
|
let MMChannelLayout = file.ffProbeData.streams[1].channel_layout.replace('(side)', '');
|
|
console.log(`Assuming stream 2 will be the best audio track available.`)
|
|
|
|
console.log(`Stream 2 is ${file.ffProbeData.streams[1].codec_name} with ${file.ffProbeData.streams[1].channels} channels.`)
|
|
response.infoLog += `Stream 2 is ${file.ffProbeData.streams[1].codec_name} with ${file.ffProbeData.streams[1].channels} channels.\n`
|
|
|
|
if ( file.ffProbeData.streams[1].channels > 6 ) {
|
|
|
|
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 -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`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
console.log(`Stream 2 isn't an audio track. Taking no action.`)
|
|
response.infoLog += `☒ Stream 2 isn't an audio track. Taking no action.\n`
|
|
|
|
};
|
|
|
|
}
|
|
|
|
if (ac3Count == 1) {
|
|
|
|
audioIdx = -1;
|
|
|
|
console.log(`Only 1 AC3 5.1 track present, checking to see if it's correctly labelled`)
|
|
response.infoLog += `Only 1 AC3 5.1 track present, checking to see if it's correctly labelled.\n`
|
|
|
|
for (let i = 0; i < file.ffProbeData.streams.length; i ++) {
|
|
|
|
// Work with audio streams only
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
|
|
|
|
//console.log(`Stream ${i} is an audio stream`);
|
|
let MMCodec = file.ffProbeData.streams[i].codec_name.toUpperCase();
|
|
let MMChannelLayout = file.ffProbeData.streams[i].channel_layout.replace('(side)', '');
|
|
|
|
// Keep track of audio stream relative numbers
|
|
try {
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") {
|
|
audioIdx++;
|
|
//console.log(`audioIdx = ${audioIdx}`)
|
|
}
|
|
} catch (err) {}
|
|
|
|
// Look only for AC3 5.1 streams (should only find 1)
|
|
if (
|
|
file.ffProbeData.streams[i].codec_name.toLowerCase() == "ac3" &&
|
|
file.ffProbeData.streams[i].channels == 6
|
|
) {
|
|
|
|
// Checking if AC3 5.1 already default and labelled - sign the file has probably already been processed
|
|
if (
|
|
file.ffProbeData.streams[i].disposition.default = 1 &&
|
|
file.ffProbeData.streams[i].tags.title === `${MMCodec} - ${MMChannelLayout} - MM`
|
|
) {
|
|
|
|
console.log('Track is labelled correctly. No further work required.')
|
|
response.infoLog += '☑ Track is labelled correctly. No further work required.\n'
|
|
|
|
}
|
|
|
|
// Checking if AC3 5.1 exists but not labelled correctly
|
|
else if (
|
|
file.ffProbeData.streams[i].disposition.default != 1 ||
|
|
file.ffProbeData.streams[i].tags.title !== `${MMCodec} - ${MMChannelLayout} - MM`
|
|
) {
|
|
|
|
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 -map 0:a:${audioIdx} -metadata:s:a:0 "title=${MMCodec} - ${MMChannelLayout} - MM" -disposition:a:0 default`
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ac3Count > 1) {
|
|
|
|
console.log(`There is more than 1 AC3 5.1 track. This is probably due to it being a foreign-language film with an additional English dub.`)
|
|
console.log(`Checking for signs that the first audio track has been processed`)
|
|
console.log(`Assuming stream 2 is the first audio track`)
|
|
response.infoLog += `There is more than 1 AC3 5.1 track. This is probably due to it being a foreign-language film with an additional English dub.\n Checking for signs that the first audio track has been processed.\n Assuming stream 2 is the first audio track.\n`
|
|
|
|
if (
|
|
file.ffProbeData.streams[1].codec_type.toLowerCase() === 'audio' &&
|
|
file.ffProbeData.streams[1].codec_name.toLowerCase() === 'ac3' &&
|
|
file.ffProbeData.streams[1].tags.title != undefined &&
|
|
file.ffProbeData.streams[1].tags.title.includes("MM")
|
|
) {
|
|
|
|
console.log(`First track is labelled by this plugin, and set as default - safe to assume this file has been processed. Taking no action.`)
|
|
response.infoLog += `☑ First track is labelled by this plugin, and set as default - safe to assume this file has been processed. Taking no action.\n`
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
console.log(`First audio stream is AC3 5.1, but not labelled as expected. Resolving.`)
|
|
response.infoLog += `First audio stream is AC3 5.1, but not labelled as expected. Resolving.\n`
|
|
|
|
let MMCodec = file.ffProbeData.streams[1].codec_name.toUpperCase();
|
|
let MMChannelLayout = file.ffProbeData.streams[1].channel_layout.replace('(side)', '');
|
|
|
|
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`)
|
|
|
|
audioIdx = 0 //Set to 0 since already accounted for track 1
|
|
|
|
for (let i = 2; i < file.ffProbeData.streams.length; i ++) {
|
|
|
|
// Work with audio streams only
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'audio') {
|
|
|
|
//console.log(`Stream ${i} is an audio stream`);
|
|
|
|
// Keep track of audio stream relative numbers
|
|
try {
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "audio") {
|
|
audioIdx++;
|
|
//console.log(`audioIdx = ${audioIdx}`)
|
|
}
|
|
} catch (err) {}
|
|
|
|
ffmpegAudioOtherTracks += ` -map 0:a:${audioIdxTruehd}`
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
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;
|
|
|
|
console.log(` === Running processSubs function === `)
|
|
|
|
// Go through all subtitles, removing non-English/German subs and commentary subs
|
|
for (let i = 0; i < file.ffProbeData.streams.length; i ++) {
|
|
|
|
// Work with subtitle streams only
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() === 'subtitle') {
|
|
|
|
// Keep track of subtitle stream relative numbers
|
|
try {
|
|
if (file.ffProbeData.streams[i].codec_type.toLowerCase() == "subtitle") {
|
|
subIdx++;
|
|
}
|
|
} catch (err) {}
|
|
|
|
// 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
|
|
else if (
|
|
file.ffProbeData.streams[i].tags.language.toLowerCase() !== "eng" &&
|
|
file.ffProbeData.streams[i].tags.language.toLowerCase() !== "ger"
|
|
) {
|
|
|
|
console.log(`Stream ${i} (subtitle stream ${subIdx}) is not in a desired language. Removing.`)
|
|
response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is not in a desired language. Removing. \n`
|
|
|
|
ffmpegSubs += ` -map -0:s:${subIdx}`
|
|
|
|
}
|
|
|
|
// Remove any subtitles that are for commentary
|
|
else if (
|
|
(file.ffProbeData.streams[i].tags.title != undefined &&
|
|
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
|
|
file.ffProbeData.streams[i].disposition.comment == 1) &&
|
|
inputs.remove_commentary == true
|
|
|
|
) {
|
|
|
|
console.log(`Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Selected to remove.`)
|
|
response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Selected to remove. \n`
|
|
|
|
ffmpegSubs += ` -map -0:s:${subIdx}`
|
|
|
|
}
|
|
|
|
else if (
|
|
(file.ffProbeData.streams[i].tags.title != undefined &&
|
|
file.ffProbeData.streams[i].tags.title.toLowerCase().includes("commentary") ||
|
|
file.ffProbeData.streams[i].disposition.comment == 1) &&
|
|
inputs.remove_commentary == false
|
|
|
|
) {
|
|
|
|
console.log(`Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Selected to keep.`)
|
|
response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is for a commentary track. Selected to keep. \n`
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
console.log(`Stream ${i} (subtitle stream ${subIdx}) is wanted. Keeping.`)
|
|
response.infoLog += `Stream ${i} (subtitle stream ${subIdx}) is wanted. Keeping. \n`
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
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
|
|
if (file.ffProbeData.streams[0].codec_name.toLowerCase() === 'hevc') {
|
|
|
|
console.log(`File is already HEVC. No action needed.`)
|
|
response.infoLog += `☑ File is already HEVC. No action needed.\n`
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
console.log('File needs transcoding to HEVC. Converting now.')
|
|
response.infoLog += `☒ File needs transcoding to HEVC.\n`
|
|
|
|
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);
|
|
|
|
processSubs(file, librarySettings, inputs, otherArguments);
|
|
|
|
processVideo(file, librarySettings, inputs, otherArguments);
|
|
|
|
if (ffmpegAudioFirstTrack.length > 0 || ffmpegAudioOtherTracks.length > 0 || ffmpegSubs.length > 0 || ffmpegVideo.length > 0) {
|
|
|
|
response.infoLog += '☒ Changes are required! \n';
|
|
|
|
response.FFmpegMode = true;
|
|
|
|
response.container = `.${file.container}`;
|
|
|
|
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)
|
|
|
|
response.processFile = true;
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
if (ffmpegAudioFirstTrack.length == 0 && ffmpegAudioOtherTracks.length == 0 && ffmpegSubs.length == 0 && ffmpegVideo.length == 0) {
|
|
|
|
response.infoLog += '☑ No changes are required \n';
|
|
|
|
response.processFile = false;
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
module.exports.details = details;
|
|
module.exports.plugin = plugin; |