@ -8,6 +8,7 @@ function details() {
Operation : "Transcode" ,
Operation : "Transcode" ,
Description :
Description :
"In a single pass ensures all files are in MKV containers and where possible encoded in h265 (Tiered bitrate based on resolution), removes audio and subtitles that are not in the configured language or marked as commentary." ,
"In a single pass ensures all files are in MKV containers and where possible encoded in h265 (Tiered bitrate based on resolution), removes audio and subtitles that are not in the configured language or marked as commentary." ,
Version : "2.0" ,
Tags : "pre-processing,ffmpeg,nvenc h265" ,
Tags : "pre-processing,ffmpeg,nvenc h265" ,
Inputs : [
Inputs : [
{
{
@ -37,8 +38,8 @@ function details() {
{
{
name : "target_pct_reduction" ,
name : "target_pct_reduction" ,
tooltip : ` Specify the target reduction of bitrate, if current bitrate is less than resolution targets.
tooltip : ` Specify the target reduction of bitrate, if current bitrate is less than resolution targets.
\ \ nExample 6 0% : \ \ n
\ \ nExample 5 0% : \ \ n
. 6 0` ,
. 5 0` ,
} ,
} ,
{
{
name : "audio_language" ,
name : "audio_language" ,
@ -196,13 +197,15 @@ function loopOverStreamsOfType(file, type, method) {
/ * *
/ * *
* Removes audio tracks that aren ' t in the allowed languages or labeled as Commentary tracks .
* Removes audio tracks that aren ' t in the allowed languages or labeled as Commentary tracks .
* Transcode audio if specified .
* /
* /
function buildAudioConfiguration ( inputs , file , logger ) {
function buildAudioConfiguration ( inputs , file , logger ) {
var configuration = new Configurator ( [ "-c:a copy" ] ) ;
var configuration = new Configurator ( [ "-c:a copy" ] ) ;
var stream _count = 0 ;
var stream _count = 0 ;
var streams _removing = 0 ;
var streams _removing = 0 ;
var languages = inputs . audio _language . split ( "," ) ;
var languages = inputs . audio _language . split ( "," ) ;
loopOverStreamsOfType ( file , "audio" , function ( stream , id ) {
function audioProcess ( stream , id ) {
stream _count ++ ;
stream _count ++ ;
if ( "tags" in stream && "title" in stream . tags && inputs . audio _commentary . toLowerCase ( ) == "true" ) {
if ( "tags" in stream && "title" in stream . tags && inputs . audio _commentary . toLowerCase ( ) == "true" ) {
if (
if (
@ -228,7 +231,10 @@ function buildAudioConfiguration(inputs, file, logger) {
) ;
) ;
}
}
}
}
} } ) ;
}
}
loopOverStreamsOfType ( file , "audio" , audioProcess ) ;
if ( stream _count == streams _removing ) {
if ( stream _count == streams _removing ) {
logger . AddError (
logger . AddError (
@ -240,6 +246,7 @@ function buildAudioConfiguration(inputs, file, logger) {
return configuration ;
return configuration ;
}
}
/ * *
/ * *
* Removes subtitles that aren ' t in the allowed languages or labeled as Commentary tracks .
* Removes subtitles that aren ' t in the allowed languages or labeled as Commentary tracks .
* /
* /
@ -250,20 +257,34 @@ function buildSubtitleConfiguration(inputs, file, logger) {
if ( languages . length === 0 ) return configuration ;
if ( languages . length === 0 ) return configuration ;
loopOverStreamsOfType ( file , "subtitle" , function ( stream , id ) {
loopOverStreamsOfType ( file , "subtitle" , function ( stream , id ) {
if ( stream . codec _name === "eia_608" ) {
if ( ( stream . codec _name === "eia_608" ) ||
( stream . codec _tag _string === "mp4s" ) ) {
// unsupported subtitle codec?
// unsupported subtitle codec?
configuration . AddOutputSetting ( ` -map -0:s: ${ id } ` ) ;
configuration . AddOutputSetting ( ` -map -0:s: ${ id } ` ) ;
logger . AddError (
` Removing unsupported subtitle `
) ;
return ;
}
// Remove unknown sub streams
if ( ! ( "codec_name" in stream ) ) {
configuration . AddOutputSetting ( ` -map -0:s: ${ id } ` ) ;
logger . AddError (
` Removing unknown subtitle `
) ;
return ;
return ;
}
}
if ( "tags" in stream ) {
if ( "tags" in stream ) {
// Remove unwated languages
// Remove unwa n ted languages
if ( "language" in stream . tags ) {
if ( "language" in stream . tags ) {
if ( languages . indexOf ( stream . tags . language . toLowerCase ( ) ) === - 1 ) {
if ( languages . indexOf ( stream . tags . language . toLowerCase ( ) ) === - 1 ) {
configuration . AddOutputSetting ( ` -map -0:s: ${ id } ` ) ;
configuration . AddOutputSetting ( ` -map -0:s: ${ id } ` ) ;
logger . AddError (
logger . AddError (
` Removing subtitle in language ${ stream . tags . language } `
` Removing subtitle in language ${ stream . tags . language } `
) ;
) ;
return ;
}
}
}
}
@ -278,6 +299,7 @@ function buildSubtitleConfiguration(inputs, file, logger) {
logger . AddError (
logger . AddError (
` Removing Commentary or Description subtitle: ${ stream . tags . title } `
` Removing Commentary or Description subtitle: ${ stream . tags . title } `
) ;
) ;
return ;
}
}
}
}
}
}
@ -292,118 +314,104 @@ function buildSubtitleConfiguration(inputs, file, logger) {
/ * *
/ * *
* Attempts to ensure that video streams are h265 encoded and inside an
* Attempts to ensure that video streams are h265 encoded and inside an
* MKV container . Will use CPU , Intel Quick Sync or NVidia NVENC encoding
* MKV container .
* as configured in the plugin inputs .
* /
* /
function buildVideoConfiguration ( inputs , file , logger ) {
function buildVideoConfiguration ( inputs , file , logger ) {
var configuration = new Configurator ( [ "-map 0" , "-map -0:d" , "-c:v copy" ] ) ;
var configuration = new Configurator ( [ "-map 0" , "-map -0:d" , "-c:v copy" ] ) ;
loopOverStreamsOfType ( file , "video" , function ( stream , id ) {
var tiered = {
"480p" : { "bitrate" : inputs . target _bitrate _480p576p ,
"max_increase" : 500 ,
"cq" : 29
} ,
"576p" : { "bitrate" : inputs . target _bitrate _480p576p ,
"max_increase" : 500 ,
"cq" : 29
} ,
"720p" : { "bitrate" : inputs . target _bitrate _720p ,
"max_increase" : 2000 ,
"cq" : 30
} ,
"1080p" : { "bitrate" : inputs . target _bitrate _1080p ,
"max_increase" : 2500 ,
"cq" : 31
} ,
"4KUHD" : { "bitrate" : inputs . target _bitrate _4KUHD ,
"max_increase" : 6000 ,
"cq" : 31
} ,
"Other" : { "bitrate" : inputs . target _bitrate _1080p ,
"max_increase" : 2500 ,
"cq" : 31
}
} ;
var inputSettings = {
"h263" : "-c:v h263_cuvid" ,
"h264" : "" ,
"mjpeg" : "c:v mjpeg_cuvid" ,
"mpeg1" : "-c:v mpeg1_cuvid" ,
"mpeg2" : "-c:v mpeg2_cuvid" ,
"vc1" : "-c:v vc1_cuvid" ,
"vp8" : "-c:v vp8_cuvid" ,
"vp9" : "-c:v vp9_cuvid"
}
function videoProcess ( stream , id ) {
if ( stream . codec _name === "mjpeg" ) {
if ( stream . codec _name === "mjpeg" ) {
configuration . AddOutputSetting ( ` -map -v: ${ id } ` ) ;
configuration . AddOutputSetting ( ` -map -v: ${ id } ` ) ;
return ;
return ;
}
}
if ( stream . codec _name === "hevc" && file . container === "mkv" ) {
if ( ( stream . codec _name === "hevc" || stream . codec _name === "vp9" ) && file . container === "mkv" ) {
logger . AddSuccess ( "File is in HEVC codec and in MKV" ) ;
logger . AddSuccess ( "File is in HEVC codec and in MKV" ) ;
return ;
return ;
}
}
// Check if should Remux.
// Check if should Remux.
if ( stream . codec _name === "hevc" && file . container !== "mkv" ) {
if ( ( stream . codec _name === "hevc" || stream . codec _name === "vp9" ) && file . container !== "mkv" ) {
configuration . AddOutputSetting ( "-c:v copy" ) ;
configuration . AddOutputSetting ( "-c:v copy" ) ;
logger . AddError ( "File is in HEVC codec but not MKV. Will remux" ) ;
logger . AddError ( "File is in HEVC codec but not MKV. Will remux" ) ;
}
}
// Check if should Transcode.
// remove png streams.
if ( stream . codec _name !== "hevc" ) {
if ( stream . codec _name === "png" ) {
configuration . AddOutputSetting ( ` -map -0:v: ${ id } ` ) ;
} else if ( stream . codec _name !== "hevc" && stream . codec _name !== "vp9" ) { // Check if should Transcode.
var bitrateprobe = calculateBitrate ( file ) ;
var bitrateprobe = calculateBitrate ( file ) ;
var bitratetarget = 0 ;
var bitratetarget = 0 ;
var bitratemax = 0 ;
var bitratemax = 0 ;
var cq = 0 ;
var cq = 0 ;
var bitratecheck = 0 ;
var bitratecheck = 0 ;
/ * *
* NVENC Configuration
* /
/* Determine tiered bitrate variables */
/* Determine tiered bitrate variables */
if ( file . video _resolution === "480p" || file . video _resolution === "576p" ) {
var tier = tiered [ file . video _resolution ] ;
bitratecheck = parseInt ( inputs . target _bitrate _480p576p ) ;
if ( bitrateprobe !== null && bitrateprobe < bitratecheck ) {
bitratecheck = parseInt ( tier [ "bitrate" ] ) ;
bitratetarget = parseInt ( ( bitrateprobe * inputs . target _pct _reduction ) / 1000 ) ; // Lower Bitrate to 60% of original and convert to KB
if ( bitrateprobe !== null && bitrateprobe < bitratecheck ) {
bitratemax = bitratetarget + 500 ; // Set max bitrate to 0.5MB Higher
bitratetarget = parseInt ( ( bitrateprobe * inputs . target _pct _reduction ) / 1000 ) ;
cq = 29 ;
} else {
bitratetarget = parseInt ( inputs . target _bitrate _480p576p / 1000 ) ;
bitratemax = bitratetarget + 500 ;
cq = 29 ;
}
}
if ( file . video _resolution === "720p" ) {
bitratecheck = parseInt ( inputs . target _bitrate _720p ) ;
if ( bitrateprobe !== null && bitrateprobe < bitratecheck ) {
bitratetarget = parseInt ( ( bitrateprobe * inputs . target _pct _reduction ) / 1000 ) ; // Lower Bitrate to 60% of original and convert to KB
bitratemax = bitratetarget + 2000 ; // Set max bitrate to 2MB Higher
cq = 30 ;
} else {
bitratetarget = parseInt ( inputs . target _bitrate _720p / 1000 ) ;
bitratemax = bitratetarget + 2000 ;
cq = 30 ;
}
}
if ( file . video _resolution === "1080p" ) {
bitratecheck = parseInt ( inputs . target _bitrate _1080p ) ;
if ( bitrateprobe !== null && bitrateprobe < bitratecheck ) {
bitratetarget = parseInt ( ( bitrateprobe * inputs . target _pct _reduction ) / 1000 ) ; // Lower Bitrate to 60% of original and convert to KB
bitratemax = bitratetarget + 2500 ; // Set max bitrate to 2.5MB Higher
cq = 31 ;
} else {
bitratetarget = parseInt ( inputs . target _bitrate _1080p / 1000 ) ;
bitratemax = bitratetarget + 2500 ;
cq = 31 ;
}
}
if ( file . video _resolution === "4KUHD" ) {
bitratecheck = parseInt ( inputs . target _bitrate _4KUHD ) ;
if ( bitrateprobe !== null && bitrateprobe < bitratecheck ) {
bitratetarget = parseInt ( ( bitrateprobe * inputs . target _pct _reduction ) / 1000 ) ; // Lower Bitrate to 60% of original and convert to KB
bitratemax = bitratetarget + 6000 ; // Set max bitrate to 6MB Higher
cq = 31 ;
} else {
} else {
bitratetarget = parseInt ( inputs . target _bitrate _4KUHD / 1000 ) ;
bitratetarget = parseInt ( tier [ "bitrate" ] / 1000 ) ;
bitratemax = bitratetarget + 6000 ;
cq = 31 ;
}
}
}
bitratemax = bitratetarget + tier [ "max_increase" ] ;
cq = tier [ "cq" ] ;
configuration . RemoveOutputSetting ( "-c:v copy" ) ;
configuration . RemoveOutputSetting ( "-c:v copy" ) ;
configuration . AddOutputSetting (
configuration . AddOutputSetting (
` -c:v hevc_nvenc -rc:v vbr_hq -qmin 0 -cq:v ${ cq } -b:v ${ bitratetarget } k -maxrate:v ${ bitratemax } k -preset medium -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 `
` -c:v hevc_nvenc -rc:v vbr_hq -qmin 0 -cq:v ${ cq } -b:v ${ bitratetarget } k -maxrate:v ${ bitratemax } k -preset medium -rc-lookahead 32 -spatial_aq:v 1 -aq-strength:v 8 `
) ;
) ;
if ( file . video _codec _name === "h263" ) {
configuration . AddInputSetting ( inputSettings [ file . video _codec _name ] ) ;
configuration . AddInputSetting ( "-c:v h263_cuvid" ) ;
} else if ( file . video _codec _name === "h264" ) {
if ( file . video _codec _name === "h264" && file . ffProbeData . streams [ 0 ] . profile !== "High 10" ) {
if ( file . ffProbeData . streams [ 0 ] . profile !== "High 10" ) {
configuration . AddInputSetting ( "-c:v h264_cuvid" ) ;
configuration . AddInputSetting ( "-c:v h264_cuvid" ) ;
} else if ( file . video _codec _name === "mjpeg" ) {
configuration . AddInputSetting ( "c:v mjpeg_cuvid" ) ;
} else if ( file . video _codec _name == "mpeg1" ) {
configuration . AddInputSetting ( "-c:v mpeg1_cuvid" ) ;
} else if ( file . video _codec _name == "mpeg2" ) {
configuration . AddInputSetting ( "-c:v mpeg2_cuvid" ) ;
} else if ( file . video _codec _name == "vc1" ) {
configuration . AddInputSetting ( "-c:v vc1_cuvid" ) ;
} else if ( file . video _codec _name == "vp8" ) {
configuration . AddInputSetting ( "-c:v vp8_cuvid" ) ;
} else if ( file . video _codec _name == "vp9" ) {
configuration . AddInputSetting ( "-c:v vp9_cuvid" ) ;
}
}
}
logger . AddError ( "Transcoding to HEVC using NVidia NVENC" ) ;
logger . AddError ( "Transcoding to HEVC using NVidia NVENC" ) ;
}
}
} ) ;
}
loopOverStreamsOfType ( file , "video" , videoProcess ) ;
if ( ! configuration . shouldProcess ) {
if ( ! configuration . shouldProcess ) {
logger . AddSuccess ( "No video processing necessary" ) ;
logger . AddSuccess ( "No video processing necessary" ) ;
@ -430,7 +438,26 @@ function plugin(file, _librarySettings, inputs) {
var videoSettings = buildVideoConfiguration ( inputs , file , logger ) ;
var videoSettings = buildVideoConfiguration ( inputs , file , logger ) ;
var subtitleSettings = buildSubtitleConfiguration ( inputs , file , logger ) ;
var subtitleSettings = buildSubtitleConfiguration ( inputs , file , logger ) ;
response . preset = ` ${ videoSettings . GetInputSettings ( ) } , ${ videoSettings . GetOutputSettings ( ) } ${ audioSettings . GetOutputSettings ( ) } ${ subtitleSettings . GetOutputSettings ( ) } -max_muxing_queue_size 4096 ` ;
response . preset = ` ${ videoSettings . GetInputSettings ( ) } , ${ videoSettings . GetOutputSettings ( ) } `
response . preset += ` ${ audioSettings . GetOutputSettings ( ) } `
response . preset += ` ${ subtitleSettings . GetOutputSettings ( ) } `
response . preset += ` -max_muxing_queue_size 9999 ` ;
// Extra parameters
var id = 0 ;
var badTypes = [ 'mov_text' , 'eia_608' , 'timed_id3' , 'mp4s' ] ;
for ( var i = 0 ; i < file . ffProbeData . streams . length ; i ++ ) {
if ( badTypes . includes ( file . ffProbeData . streams [ i ] . codec _name ) ) {
response . preset += ` -map -0: ${ i } ` ;
} ;
id ++ ;
}
// b frames argument
response . preset += ` -bf 5 ` ;
// fix probe size errors
response . preset += ` -analyzeduration 2147483647 -probesize 2147483647 ` ;
response . processFile =
response . processFile =
audioSettings . shouldProcess ||
audioSettings . shouldProcess ||
videoSettings . shouldProcess ||
videoSettings . shouldProcess ||