Multiple audio tracks with different codecs and languages
Re: Multiple audio tracks with different codecs and languages
Re: Multiple audio tracks with different codecs and languages
I'm fairly confident it could be significantly simplified, will post some results.
Re: Multiple audio tracks with different codecs and languages
DTS:X
Before:
{ac}=DTS
{aco}= Binding "aco": undefined
{af}=6ch (Wrong)
{channels}=7.1
Format DTS
Format_Commercial DTS
Channel(s) Object Based / 8 / 6
ChannelPositions/String2 Object Based / 3/2/2.1 / 3/2/0.1
CodecID A_DTS
old: Codec DTS-HD
old: Format_Profile X / MA / Core
After:
{ac}=DTS
{aco}= Binding "aco": undefined
{channels}= Binding "channels": No value present
{af}=Binding "af": No value present
Format DTS
Format_Commercial DTS-HD Master Audio
Channel(s) 8
ChannelPositions/String2 Object Based
CodecID A_DTS
new: Format/String DTS XLL X
new: Format_AdditionalFeatures XLL X
****************************************************************************
TrueHD:
Before:
{ac}=TrueHD
{aco}= Binding "aco": undefined
{channels}=7.1
{af}=8ch
Format TrueHD
Format_Commercial TrueHD
ChannelPositions/String2 3/2/2.1
CodecID A_TRUEHD
After:
{ac}=MLPFBA
{aco}= Binding "aco": undefined
{channels}=7.1
{af}=8ch
Format MLP FBA
Format_Commercial Dolby TrueHD
ChannelPositions/String2 3/2/2.1
CodecID A_TRUEHD
****************************************************************************
TrueHD+Atmos:
Before:
{ac}=TrueHD
{aco}= TrueHD+Atmos
{channels}=7.1
{af}=8ch
Format TrueHD
Format_Commercial TrueHD
Format_Profile TrueHD+Atmos / TrueHD
ChannelPositions/String2 Object Based / 3/2/2.1
CodecID A_TRUEHD
After:
{ac}=MLPFBA
{aco}= Binding "aco": undefined
{channels}=7.1
{af}=8ch
Format MLP FBA
Format/String MLP FBA 16-ch
Format_Commercial Dolby TrueHD with Dolby Atmos
Format_AdditionalFeatures 16-ch
ChannelPositions/String2 3/2/2.1
CodecID A_TRUEHD
****************************************************************************
Re: Multiple audio tracks with different codecs and languages
Code: Select all
// somewhere at the top
import net.filebot.Language
{ // map Codec + Format Profile
def mCFP = [
"FLAC" : "FLAC",
"PCM" : "PCM",
"MP3": "MP3",
"E-AC-3 JOC": "E-AC-3",
"DTS ES XXCH": "DTS-ES Discrete",
"MLP FBA": "TrueHD",
"MLP FBA 16-ch": "TrueHD"
]
audio.collect { au ->
def ac1 = any{ au['CodecID/Hint'] }{au['Format/String']}{ au['Format'] } // extends _ac_ which strips spaces > "CodecID/Hint", "Format"
def ac2 = any{ au['CodecID/String'] }{ au['Codec/String'] }{ au['Codec'] }
def atmos = (aco =~ /(?i:atmos)/) ? 'Atmos' : null // _aco_ uses "Codec_Profile", "Format_Profile", "Format_Commercial"
def combined = allOf{ac1}{ac2}.join(' ')
def fallback = any{ac1}{ac2}{aco}
def stream = allOf
/* _channels_ as it uses "ChannelPositions/String2", "Channel(s)_Original", "Channel(s)"
compared to _af_ which uses "Channel(s)_Original", "Channel(s)" */
{ allOf{"${channels}"}{au['NumberOfDynamicObjects'] + "obj"}.join('+') }
{ allOf{ mCFP.get(combined, aco) }{atmos}.join('+') } /* bit risky keeping aco as default */
{ Language.findLanguage(au['Language']).ISO3.upperInitial() }
/* _cf_ not being used > "Codec/Extensions", "Format" */
return stream
}.sort{a, b -> a.first() <=> b.first() }*.join(" ").join(", ") }
aco =~ /(?i:atmos)/ is being used as aco.match(/atmos/) fails with Pattern not found: Dolby TrueHD when the string isn't present.
Expected output is:
7.1+15obj TrueHD+Atmos for Dolby Amaze Atmos trailer
5.1 AC-3 for the Amaze regular trailer
5.1 DTS-HD MA Eng, 5.1 PCM Eng, 5.1 AC-3 Eng for Dolby 3D Glass Return Trailer
5.1+11obj E-AC-3+Atmos Eng for Dolby Natures Fury Atmos trailer
I tested it on MacOS, latest version of FileBot available.
Code: Select all
/Applications/FileBot.app//Contents/MacOS/libmediainfo.dylib:
/usr/local/lib/libmediainfo.0.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.8)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2)
/usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 104.1.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 489.0.0)
Feedback welcome.
Re: Multiple audio tracks with different codecs and languages
All versions of FileBot come packaged with the latest MediaInfo release, which is currently Version 18.12, but on Linux / Synology / etc FileBot may not package libmediainfo and instead rely on the one provided by your distribution packaging system.
2.
Here's how {aco} is currently implemented:
https://www.filebot.net/docs/api/src-ht ... l#line.414
Re: Multiple audio tracks with different codecs and languages
These have shifted around it seems mainly with Codec bindings which now seem deprecated > Codec, Codec/String, Codec/Family, Codec/Info, Codec/Url and Codec_Profile https://github.com/MediaArea/MediaInfoLib/blob/29be23e7bc740e1ba39ce6a231ceb69c254b4401/Source/MediaInfo/MediaInfo_Config_Automatic.cpp#L4864
The nicest output is probably Format_Commercial when available, but it's usually quite verbose, for example in the Dolby Amaze trailer it outputs Dolby TrueHD with Dolby Atmos or something similar, and for DTS-HD MA it returns DTS-HD Master Audio.
I can't recall a single field consistently outputting nice info; all DTS have DTS as format, without distinguishing between variants, however DTS ES-Discrete is actually the commercial name for DTS ES with XCh/XXCh extension (vs DTS ES Matrix with no header).
First try, totally untested:
Code: Select all
af = getMediaInfo(StreamKind.Audio, 0, "Format/String", "Format"); // short version
ac = getMediaInfo(StreamKind.Audio, 0, "CodecID/Hint", "CodecID/String", "CodecID"); // not sure about Hint
afo = getMediaInfo(StreamKind.Audio, 0, "Format_AdditionalFeatures", "Matrix_Format"); // for additional format features (DTS Neural)
aco = getMediaInfo(StreamKind.Audio, 0, "Format_Profile", "Format_Commercial"); // nicelooking
NumberOfDynamicObjects in Atmos.
Re: Multiple audio tracks with different codecs and languages
I only have one test files for ATMOS, and none for DTS. So MediaInfo dumps + preferences for MI fields to base aco, channels, etc on are appreciated.
{aco} at least uses "Codec_Profile", "Format_Profile", "Format_Commercial" so at least the last one should work reasonably well with the latest release.
"Format_AdditionalFeatures", "Matrix_Format" I haven't really seen before. I guess I could add a binding if it's useful, are have af / channels check that field as well if that makes sense.
Re: Multiple audio tracks with different codecs and languages
By the way, the Dolby files are freely available on their website, in addition to those there are a few examples on https://streams.videolan.org/samples/ for all tastes.
I believe the most consistent across version is CodecID, which, however, isn't particularly pleasing (it usually has A_<codecname> and is allcaps)
Also which kind of output should I aim for to keep compatibility?
Re: Multiple audio tracks with different codecs and languages
viewtopic.php?f=5&t=4285
You can CTRL+A / CTRL+C to copy & paste a relevant table.
Re: Multiple audio tracks with different codecs and languages
DTS HD MA
Code: Select all
Format DTS
Format/String DTS XLL
Format/Info Digital Theater Systems
Format/Url https://en.wikipedia.org/wiki/DTS_(sound_system)
Format_Commercial DTS-HD Master Audio
Format_Commercial_IfAny DTS-HD Master Audio
Format_Settings_Mode 16
Format_Settings_Endianness Big
Format_AdditionalFeatures XLL
CodecID A_DTS
Duration 29536
Duration/String 29 s 536 ms
Duration/String1 29 s 536 ms
Duration/String2 29 s 536 ms
Duration/String3 00:00:29.536
Duration/String5 00:00:29.536
BitRate_Mode VBR
BitRate_Mode/String Variable
Channel(s) 6
Channel(s)/String 6 channels
ChannelPositions Front: L C R, Side: L R, LFE
ChannelPositions/String2 3/2/0.1
ChannelLayout C L R Ls Rs LFE
SamplesPerFrame 512
SamplingRate 48000
SamplingRate/String 48.0 kHz
FrameRate 93.750
FrameRate/String 93.750 FPS (512 SPF)
BitDepth 24
BitDepth/String 24 bits
Compression_Mode Lossless
Compression_Mode/String Lossless
Delay 0
Delay/String3 00:00:00.000
Delay_Source Container
Delay_Source/String Container
Video_Delay 0
Video_Delay/String3 00:00:00.000
Title DTS-HD MA 5.1
Language en
Language/String English
Language/String1 English
Language/String2 en
Language/String3 eng
Language/String4 en
Default Yes
Default/String Yes
Forced No
Forced/String No
Code: Select all
Format DTS
Format/String DTS XBR
Format/Info Digital Theater Systems
Format/Url https://en.wikipedia.org/wiki/DTS_(sound_system)
Format_Commercial DTS-HD High Resolution Audio
Format_Commercial_IfAny DTS-HD High Resolution Audio
Format_Settings_Mode 16
Format_Settings_Endianness Big
Format_AdditionalFeatures XBR
CodecID A_DTS
Duration 16246
Duration/String 16 s 246 ms
Duration/String1 16 s 246 ms
Duration/String2 16 s 246 ms
Duration/String3 00:00:16.246
Duration/String5 00:00:16.246
BitRate_Mode CBR
BitRate_Mode/String Constant
BitRate 3018000
BitRate/String 3 018 kb/s
Channel(s) 6
Channel(s)/String 6 channels
Channel(s)_Original 8
Channel(s)_Original/String 8 channels
ChannelPositions Front: L C R, Side: L R, Back: L R, LFE
ChannelPositions/String2 3/2/2.1
ChannelLayout C L R LFE Lb Rb Lss Rss
SamplesPerFrame 512
SamplingRate 96000
SamplingRate/String 96.0 kHz
FrameRate 187.500
FrameRate/String 187.500 FPS (512 SPF)
BitDepth 24
BitDepth/String 24 bits
Compression_Mode Lossy
Compression_Mode/String Lossy
Delay 0
Delay/String3 00:00:00.000
Delay_Source Container
Delay_Source/String Container
Video_Delay 0
Video_Delay/String3 00:00:00.000
StreamSize 6128803
StreamSize/String 5.84 MiB (25%)
StreamSize/String1 6 MiB
StreamSize/String2 5.8 MiB
StreamSize/String3 5.84 MiB
StreamSize/String4 5.845 MiB
StreamSize/String5 5.84 MiB (25%)
StreamSize_Proportion 0.24785
Language en
Language/String English
Language/String1 English
Language/String2 en
Language/String3 eng
Language/String4 en
Default Yes
Default/String Yes
Forced No
Forced/String No
Code: Select all
OriginalSourceMedium_ID 4352
OriginalSourceMedium_ID/String 4352 (0x1100)
Format DTS
Format/String DTS XLL X
Format/Info Digital Theater Systems
Format/Url https://en.wikipedia.org/wiki/DTS_(sound_system)
Format_Commercial DTS-HD Master Audio
Format_Commercial_IfAny DTS-HD Master Audio
Format_Settings_Mode 16
Format_Settings_Endianness Big
Format_AdditionalFeatures XLL X
CodecID A_DTS
Duration 18944.000000
Duration/String 18 s 944 ms
Duration/String1 18 s 944 ms
Duration/String2 18 s 944 ms
Duration/String3 00:00:18.944
Duration/String4 00:00:18:84
Duration/String5 00:00:18.944 (00:00:18:84)
BitRate_Mode VBR
BitRate_Mode/String Variable
BitRate 6089277
BitRate/String 6 089 kb/s
Channel(s) 8
Channel(s)/String 8 channels
Channel(s)_Original Object Based
Channel(s)_Original/String Object Based
ChannelPositions Object Based
ChannelPositions/String2 Object Based
ChannelLayout Object Based
SamplesPerFrame 512
SamplingRate 48000
SamplingRate/String 48.0 kHz
FrameRate 93.750
FrameRate/String 93.750 FPS (512 SPF)
BitDepth 24
BitDepth/String 24 bits
Delay 0
Delay/String3 00:00:00.000
Delay_Source Container
Delay_Source/String Container
Video_Delay 0
Video_Delay/String3 00:00:00.000
StreamSize 14419408
StreamSize/String 13.8 MiB (18%)
StreamSize/String1 14 MiB
StreamSize/String2 14 MiB
StreamSize/String3 13.8 MiB
StreamSize/String4 13.75 MiB
StreamSize/String5 13.8 MiB (18%)
StreamSize_Proportion 0.17777
Title Surround 7.1
Language en
Language/String English
Language/String1 English
Language/String2 en
Language/String3 eng
Language/String4 en
Default Yes
Default/String Yes
Forced No
Forced/String No
OriginalSourceMedium Blu-ray
Code: Select all
Format MLP FBA
Format/String MLP FBA 16-ch
Format/Info Meridian Lossless Packing FBA with 16-channel presentation
Format_Commercial Dolby TrueHD with Dolby Atmos
Format_Commercial_IfAny Dolby TrueHD with Dolby Atmos
Format_AdditionalFeatures 16-ch
CodecID A_TRUEHD
CodecID/Url http://www.dolby.com/consumer/technology/trueHD.html
Duration 63500.000000
Duration/String 1 min 3 s
Duration/String1 1 min 3 s 500 ms
Duration/String2 1 min 3 s
Duration/String3 00:01:03.500
Duration/String5 00:01:03.500
BitRate_Mode VBR
BitRate_Mode/String Variable
BitRate 6693961
BitRate/String 6 694 kb/s
BitRate_Maximum 9096000
BitRate_Maximum/String 9 096 kb/s
Channel(s) 8
Channel(s)/String 8 channels
ChannelPositions Front: L C R, Side: L R, Back: L R, LFE
ChannelPositions/String2 3/2/2.1
ChannelLayout L R C LFE Ls Rs Lb Rb
SamplesPerFrame 40
SamplingRate 48000
SamplingRate/String 48.0 kHz
FrameRate 1200.000
FrameRate/String 1 200.000 FPS (40 SPF)
Compression_Mode Lossless
Compression_Mode/String Lossless
Delay 0
Delay/String3 00:00:00.000
Delay_Source Container
Delay_Source/String Container
Video_Delay 0
Video_Delay/String3 00:00:00.000
StreamSize 53133320
StreamSize/String 50.7 MiB (36%)
StreamSize/String1 51 MiB
StreamSize/String2 51 MiB
StreamSize/String3 50.7 MiB
StreamSize/String4 50.67 MiB
StreamSize/String5 50.7 MiB (36%)
StreamSize_Proportion 0.36310
Default Yes
Default/String Yes
Forced No
Forced/String No
NumberOfDynamicObjects 15
BedChannelCount/String 1 channel
BedChannelConfiguration LFE
Code: Select all
Format E-AC-3
Format/String E-AC-3 JOC
Format/Info Enhanced AC-3 with Joint Object Coding
Format/Url https://en.wikipedia.org/wiki/Dolby_Digital_Plus
Format_Commercial Dolby Digital Plus with Dolby Atmos
Format_Commercial_IfAny Dolby Digital Plus with Dolby Atmos
Format_Settings_Endianness Big
Format_AdditionalFeatures JOC
InternetMediaType audio/eac3
CodecID A_EAC3
Duration 110944.000000
Duration/String 1 min 50 s
Duration/String1 1 min 50 s 944 ms
Duration/String2 1 min 50 s
Duration/String3 00:01:50.944
Duration/String4 00:01:51:26
Duration/String5 00:01:50.944 (00:01:51:26)
BitRate_Mode CBR
BitRate_Mode/String Constant
BitRate 448000
BitRate/String 448 kb/s
Channel(s) 6
Channel(s)/String 6 channels
ChannelPositions Front: L C R, Side: L R, LFE
ChannelPositions/String2 3/2/0.1
ChannelLayout L R C LFE Ls Rs
SamplesPerFrame 1536
SamplingRate 48000
SamplingRate/String 48.0 kHz
FrameRate 31.250
FrameRate/String 31.250 FPS (1536 SPF)
Compression_Mode Lossy
Compression_Mode/String Lossy
Delay 0
Delay/String3 00:00:00.000
Delay_Source Container
Delay_Source/String Container
Video_Delay -67
Video_Delay/String -67 ms
Video_Delay/String1 -67 ms
Video_Delay/String2 -67 ms
Video_Delay/String3 -00:00:00.067
StreamSize 6212864
StreamSize/String 5.93 MiB (5%)
StreamSize/String1 6 MiB
StreamSize/String2 5.9 MiB
StreamSize/String3 5.93 MiB
StreamSize/String4 5.925 MiB
StreamSize/String5 5.93 MiB (5%)
StreamSize_Proportion 0.04884
Language en
Language/String English
Language/String1 English
Language/String2 en
Language/String3 eng
Language/String4 en
ServiceKind CM
ServiceKind/String Complete Main
Default Yes
Default/String Yes
Forced No
Forced/String No
ComplexityIndex 12
NumberOfDynamicObjects 11
BedChannelCount/String 1 channel
BedChannelConfiguration LFE
bsid 16
dialnorm -31
dialnorm/String -31 dB
compr 0.53
compr/String 0.53 dB
acmod 7
lfeon 1
dialnorm_Average -31
dialnorm_Average/String -31 dB
dialnorm_Minimum -31
dialnorm_Minimum/String -31 dB
dialnorm_Maximum -31
dialnorm_Maximum/String -31 dB
compr_Average -2.02
compr_Average/String -2.02 dB
compr_Minimum -11.51
compr_Minimum/String -11.51 dB
compr_Maximum 1.02
compr_Maximum/String 1.02 dB
Code: Select all
OriginalSourceMedium_ID 4352
OriginalSourceMedium_ID/String 4352 (0x1100)
Format DTS
Format/String DTS XLL X
Format/Info Digital Theater Systems
Format/Url https://en.wikipedia.org/wiki/DTS_(sound_system)
Format_Commercial DTS-HD Master Audio
Format_Commercial_IfAny DTS-HD Master Audio
Format_Settings_Mode 16
Format_Settings_Endianness Big
Format_AdditionalFeatures XLL X
CodecID A_DTS
Duration 95018.666666
Duration/String 1 min 35 s
Duration/String1 1 min 35 s 19 ms
Duration/String2 1 min 35 s
Duration/String3 00:01:35.019
Duration/String4 00:01:34:72
Duration/String5 00:01:35.019 (00:01:34:72)
BitRate_Mode VBR
BitRate_Mode/String Variable
BitRate 4965852
BitRate/String 4 966 kb/s
Channel(s) 8
Channel(s)/String 8 channels
Channel(s)_Original Object Based
Channel(s)_Original/String Object Based
ChannelPositions Object Based
ChannelPositions/String2 Object Based
ChannelLayout Object Based
SamplesPerFrame 512
SamplingRate 48000
SamplingRate/String 48.0 kHz
FrameRate 93.750
FrameRate/String 93.750 FPS (512 SPF)
BitDepth 24
BitDepth/String 24 bits
Delay 0
Delay/String3 00:00:00.000
Delay_Source Container
Delay_Source/String Container
Video_Delay 0
Video_Delay/String3 00:00:00.000
StreamSize 58980676
StreamSize/String 56.2 MiB (14%)
StreamSize/String1 56 MiB
StreamSize/String2 56 MiB
StreamSize/String3 56.2 MiB
StreamSize/String4 56.25 MiB
StreamSize/String5 56.2 MiB (14%)
StreamSize_Proportion 0.13759
Title Surround 7.1
Language en
Language/String English
Language/String1 English
Language/String2 en
Language/String3 eng
Language/String4 en
Default Yes
Default/String Yes
Forced No
Forced/String No
OriginalSourceMedium Blu-ray
Code: Select all
Audio_Format_List DTS ES XXCH
Audio_Format_WithHint_List DTS ES XXCH
Audio_Codec_List DTS ES XXCH
CompleteName /Users/devster/Movies/Aquaman.2018.IMAX.Edition.1080p.BluRay.DDP7.1.x264-Geek.mkv
FolderName /Users/devster/Movies
FileNameExtension Aquaman.2018.IMAX.Edition.1080p.BluRay.DDP7.1.x264-Geek.mkv
FileName Aquaman.2018.IMAX.Edition.1080p.BluRay.DDP7.1.x264-Geek
FileExtension mkv
Format DTS
Format/String DTS ES XXCH
Format/Info Digital Theater Systems
Format/Url https://en.wikipedia.org/wiki/DTS_(sound_system)
Format/Extensions dts dtshd
Format_Commercial DTS-ES Discrete
Format_Commercial_IfAny DTS-ES Discrete
Format_AdditionalFeatures ES XXCH
FileSize 5233800
FileSize/String 4.99 MiB
FileSize/String1 5 MiB
FileSize/String2 5.0 MiB
FileSize/String3 4.99 MiB
FileSize/String4 4.991 MiB
Duration 27733
Duration/String 27 s 733 ms
Duration/String1 27 s 733 ms
Duration/String2 27 s 733 ms
Duration/String3 00:00:27.733
Duration/String5 00:00:27.733
OverallBitRate_Mode CBR
OverallBitRate_Mode/String Constant
OverallBitRate 1509750
OverallBitRate/String 1 510 kb/s
StreamSize 63
StreamSize/String 63.0 Bytes (0%)
StreamSize/String1 63 Bytes
StreamSize/String2 63 Bytes
StreamSize/String3 63.0 Bytes
StreamSize/String4 63.00 Bytes
StreamSize/String5 63.0 Bytes (0%)
StreamSize_Proportion 0.00001
File_Modified_Date UTC 2019-04-07 22:55:13
File_Modified_Date_Local 2019-04-08 00:55:13
FileExtension_Invalid dts dtshd
Code: Select all
Format DTS
Format/String DTS 96/24
Format/Info Digital Theater Systems
Format/Url https://en.wikipedia.org/wiki/DTS_(sound_system)
Format_Commercial DTS 96/24
Format_Commercial_IfAny DTS 96/24
Format_Settings_Mode 16
Format_Settings_Endianness Big
Format_AdditionalFeatures 96/24
Duration 295168
Duration/String 4 min 55 s
Duration/String1 4 min 55 s 168 ms
Duration/String2 4 min 55 s
Duration/String3 00:04:55.168
Duration/String5 00:04:55.168
BitRate_Mode CBR
BitRate_Mode/String Constant
BitRate 1509750
BitRate/String 1 510 kb/s
Channel(s) 5
Channel(s)/String 5 channels
ChannelPositions Front: L C R, Side: L R
ChannelPositions/String2 3/2/0.0
ChannelLayout C L R Ls Rs
SamplesPerFrame 512
SamplingRate 96000
SamplingRate/String 96.0 kHz
FrameRate 187.500
FrameRate/String 187.500 FPS (512 SPF)
BitDepth 24
BitDepth/String 24 bits
Compression_Mode Lossy
Compression_Mode/String Lossy
StreamSize 55703736
StreamSize/String 53.1 MiB (100%)
StreamSize/String1 53 MiB
StreamSize/String2 53 MiB
StreamSize/String3 53.1 MiB
StreamSize/String4 53.12 MiB
StreamSize/String5 53.1 MiB (100%)
StreamSize_Proportion 1.00000
Code: Select all
Format MLP FBA
Format/String MLP FBA
Format/Info Meridian Lossless Packing FBA
Format_Commercial Dolby TrueHD
Format_Commercial_IfAny Dolby TrueHD
CodecID A_TRUEHD
CodecID/Url http://www.dolby.com/consumer/technology/trueHD.html
Duration 16184
Duration/String 16 s 184 ms
Duration/String1 16 s 184 ms
Duration/String2 16 s 184 ms
Duration/String3 00:00:16.184
Duration/String5 00:00:16.184
BitRate_Mode VBR
BitRate_Mode/String Variable
BitRate_Maximum 3153000
BitRate_Maximum/String 3 153 kb/s
Channel(s) 6
Channel(s)/String 6 channels
ChannelPositions Front: L C R, Side: L R, LFE
ChannelPositions/String2 3/2/0.1
ChannelLayout L R C LFE Ls Rs
SamplesPerFrame 40
SamplingRate 48000
SamplingRate/String 48.0 kHz
FrameRate 1200.000
FrameRate/String 1 200.000 FPS (40 SPF)
Compression_Mode Lossless
Compression_Mode/String Lossless
Delay 0
Delay/String3 00:00:00.000
Delay_Source Container
Delay_Source/String Container
Video_Delay 0
Video_Delay/String3 00:00:00.000
Default Yes
Default/String Yes
Forced No
Forced/String No
Re: Multiple audio tracks with different codecs and languages
Hi devster, I want to use your current version but for the life of me can't get it to work. I just pasted it into filebot gui.devster wrote: 15 Apr 2019, 00:14 So, as MediaInfo changed a bit and I realized how I was needlessly trying to replicate the logic already present in MediaBindingBean.java this is a new version of the snippetUnfortunately most of the map is now a bit useless and I only left the ugly MLP FBA.Code: Select all
// somewhere at the top import net.filebot.Language { // map Codec + Format Profile def mCFP = [ "FLAC" : "FLAC", "PCM" : "PCM", "MP3": "MP3", "E-AC-3 JOC": "E-AC-3", "DTS ES XXCH": "DTS-ES Discrete", "MLP FBA": "TrueHD", "MLP FBA 16-ch": "TrueHD" ] audio.collect { au -> def ac1 = any{ au['CodecID/Hint'] }{au['Format/String']}{ au['Format'] } // extends _ac_ which strips spaces > "CodecID/Hint", "Format" def ac2 = any{ au['CodecID/String'] }{ au['Codec/String'] }{ au['Codec'] } def atmos = (aco =~ /(?i:atmos)/) ? 'Atmos' : null // _aco_ uses "Codec_Profile", "Format_Profile", "Format_Commercial" def combined = allOf{ac1}{ac2}.join(' ') def fallback = any{ac1}{ac2}{aco} def stream = allOf /* _channels_ as it uses "ChannelPositions/String2", "Channel(s)_Original", "Channel(s)" compared to _af_ which uses "Channel(s)_Original", "Channel(s)" */ { allOf{"${channels}"}{au['NumberOfDynamicObjects'] + "obj"}.join('+') } { allOf{ mCFP.get(combined, aco) }{atmos}.join('+') } /* bit risky keeping aco as default */ { Language.findLanguage(au['Language']).ISO3.upperInitial() } /* _cf_ not being used > "Codec/Extensions", "Format" */ return stream }.sort{a, b -> a.first() <=> b.first() }*.join(" ").join(", ") }
aco =~ /(?i:atmos)/ is being used as aco.match(/atmos/) fails with Pattern not found: Dolby TrueHD when the string isn't present.
Expected output is:
7.1+15obj TrueHD+Atmos for Dolby Amaze Atmos trailer
5.1 AC-3 for the Amaze regular trailer
5.1 DTS-HD MA Eng, 5.1 PCM Eng, 5.1 AC-3 Eng for Dolby 3D Glass Return Trailer
5.1+11obj E-AC-3+Atmos Eng for Dolby Natures Fury Atmos trailer
I tested it on MacOS, latest version of FileBot available.not very informative as version unfortunately.Code: Select all
/Applications/FileBot.app//Contents/MacOS/libmediainfo.dylib: /usr/local/lib/libmediainfo.0.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.8) /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1238.60.2) /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 104.1.0) /usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 489.0.0)
Feedback welcome.
I guess I have to add a { at the beginning so the "import net.filebot.Language" is not interpreted as text but that leaves me with:
SyntaxError: missing token: }
but I can't find where to put the }. Tried at the very back but that did not help.
not a programmer, sorry, help pls
Re: Multiple audio tracks with different codecs and languages
Code: Select all
{ import net.filebot.Language
def mCFP = [
"FLAC" : "FLAC",
"PCM" : "PCM",
"MP3": "MP3",
"E-AC-3 JOC": "E-AC-3",
"DTS ES XXCH": "DTS-ES Discrete",
"MLP FBA": "TrueHD",
"MLP FBA 16-ch": "TrueHD"
]
audio.collect { au ->
def ac1 = any{ au['CodecID/Hint'] }{au['Format/String']}{ au['Format'] }
def ac2 = any{ au['CodecID/String'] }{ au['Codec/String'] }{ au['Codec'] }
def atmos = (aco =~ /(?i:atmos)/) ? 'Atmos' : null
def combined = allOf{ac1}{ac2}.join(' ')
def fallback = any{ac1}{ac2}{aco}
def stream = allOf
{ allOf{"${channels}"}{au['NumberOfDynamicObjects'] + "obj"}.join('+') }
{ allOf{ mCFP.get(combined, aco) }{atmos}.join('+') }
{ Language.findLanguage(au['Language']).ISO3.upperInitial() }
return stream
}.sort{a, b -> a.first() <=> b.first() }*.join(" ").join(", ")
}
or you can use my version
Code: Select all
{
// map codec + format_profile
def mCFP = [ "MP3" : "MP3",
"AC 3" : "AC3",
"E AC 3" : "EAC3",
"E AC 3 JOC" : "EAC3.Atmos",
"MLP FBA" : "TrueHD",
"MLP FBA 16 ch" : "TrueHD.Atmos",
"DTS" : "DTS",
"DTS ES XXCH XBR" : "DTS-HD.HRA",
"DTS ES XBR" : "DTS-HD.HRA",
"DTS XBR" : "DTS-HD.HRA",
"DTS XLL" : "DTS-HD.MA",
"DTS ES XXCH" : "DTS-ES",
"DTS ES" : "DTS-ES",
"DTS XLL X" : "DTS.X",
"DTS 96 24" : "DTS 96-24",
"AAC LC" : "AAC"]
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6/,'5.1').replaceAll(/8/,'7.1') }
audio.collect { au ->
def channels = any{ channelClean(au['ChannelPositionsString2'])}{ channelClean(au['ChannelsOriginal'])}{ channelClean(au['Channels']) }
def ch = channels
.tokenize('\\/').take(3)*.toDouble()
.inject(0, { a, b -> a + b })
.findAll { it > 0 }.max().toString()
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = { ( au['Format_AdditionalFeatures'] != null) ? audioClean(au['Format_AdditionalFeatures']) : '' }
def combined = allOf{codec}{format_profile}.join(' ')
def stream = allOf
{ mCFP.get(combined, 'UNKNOWN_FORMAT--'+combined+'--') }
{ ch }
{ au.'LanguageString3'.upperInitial() }
println "MISSING_mCFP_FORMAT: "+combined
return stream
}*.join(".").unique().join(".&.")
}Re: Multiple audio tracks with different codecs and languages
thank you very much kim, I chose and adapted your versionkim wrote: 29 Apr 2019, 16:10sample: 7.1 DTS-HD Master Audio EngCode: Select all
{ import net.filebot.Language def mCFP = [ "FLAC" : "FLAC", "PCM" : "PCM", "MP3": "MP3", "E-AC-3 JOC": "E-AC-3", "DTS ES XXCH": "DTS-ES Discrete", "MLP FBA": "TrueHD", "MLP FBA 16-ch": "TrueHD" ] audio.collect { au -> def ac1 = any{ au['CodecID/Hint'] }{au['Format/String']}{ au['Format'] } def ac2 = any{ au['CodecID/String'] }{ au['Codec/String'] }{ au['Codec'] } def atmos = (aco =~ /(?i:atmos)/) ? 'Atmos' : null def combined = allOf{ac1}{ac2}.join(' ') def fallback = any{ac1}{ac2}{aco} def stream = allOf { allOf{"${channels}"}{au['NumberOfDynamicObjects'] + "obj"}.join('+') } { allOf{ mCFP.get(combined, aco) }{atmos}.join('+') } { Language.findLanguage(au['Language']).ISO3.upperInitial() } return stream }.sort{a, b -> a.first() <=> b.first() }*.join(" ").join(", ") }
or you can use my version
sample: DTS-HD.MA.7.1.EngCode: Select all
{ // map codec + format_profile def mCFP = [ "MP3" : "MP3", "AC 3" : "AC3", "E AC 3" : "EAC3", "E AC 3 JOC" : "EAC3.Atmos", "MLP FBA" : "TrueHD", "MLP FBA 16 ch" : "TrueHD.Atmos", "DTS" : "DTS", "DTS ES XXCH XBR" : "DTS-HD.HRA", "DTS ES XBR" : "DTS-HD.HRA", "DTS XBR" : "DTS-HD.HRA", "DTS XLL" : "DTS-HD.MA", "DTS ES XXCH" : "DTS-ES", "DTS ES" : "DTS-ES", "DTS XLL X" : "DTS.X", "DTS 96 24" : "DTS 96-24", "AAC LC" : "AAC"] def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6/,'5.1').replaceAll(/8/,'7.1') } audio.collect { au -> def channels = any{ channelClean(au['ChannelPositionsString2'])}{ channelClean(au['ChannelsOriginal'])}{ channelClean(au['Channels']) } def ch = channels .tokenize('\\/').take(3)*.toDouble() .inject(0, { a, b -> a + b }) .findAll { it > 0 }.max().toString() def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = { ( au['Format_AdditionalFeatures'] != null) ? audioClean(au['Format_AdditionalFeatures']) : '' } def combined = allOf{codec}{format_profile}.join(' ') def stream = allOf { mCFP.get(combined, 'UNKNOWN_FORMAT--'+combined+'--') } { ch } { au.'LanguageString3'.upperInitial() } println "MISSING_mCFP_FORMAT: "+combined return stream }*.join(".").unique().join(".&.") }
FYI, I added
"PCM" : "PCM",
"DTS ES XLL" : "DTS-HD MA",
"DTS ES XXCH XLL" : "DTS-HD MA",
"AC 3 Dep" : "E-AC3",
"AAC LC SBR" : "AAC",
which displayed UNKNOWN_FORMAT for some of my movies
Re: Multiple audio tracks with different codecs and languages
"DTS ES XXCH XLL" : "DTS-ES",
6.1 ch ?
because XLL is better then ES
should be
"DTS ES XXCH XLL" : "DTS-HD MA",
https://wiki.videolan.org/DTS/
you got more info on "AC 3 Dep" ?
Re: Multiple audio tracks with different codecs and languages
you are correct of course, XLL stands for lossless, which should therefore be DTS-HD MAkim wrote: 30 Apr 2019, 14:21 I think this is wrong
"DTS ES XXCH XLL" : "DTS-ES",
6.1 ch ?
because XLL is better then ES
should be
"DTS ES XXCH XLL" : "DTS-HD MA",
https://wiki.videolan.org/DTS/
you got more info on "AC 3 Dep" ?
"AC 3 Dep" was the german audio track from "finding dory" bluray. here is the mediainfo
Code: Select all
Audio #1
Count : 305
Count of stream of this kind : 2
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 0
Stream identifier : 1
StreamOrder : 1
ID : 2
ID : 2
Unique ID : 9548324243094778268
Format : AC-3
Format : E-AC-3
Format/Info : Enhanced AC-3
Format/Url : https://en.wikipedia.org/wiki/AC3
Commercial name : Dolby Digital Plus
Commercial name : Dolby Digital Plus
Format profile : Blu-ray Disc
Format settings, Endianness : Big
Format_AdditionalFeatures : Dep
Internet media type : audio/eac3
Codec ID : A_EAC3
Duration : 5823648.000000
Duration : 1 h 37 min
Duration : 1 h 37 min 3s 648 ms
Duration : 1 h 37 min
Duration : 01:37:03.648
Duration : 01:37:50:19
Duration : 01:37:03.648 (01:37:50:19)
Bit rate mode : CBR
Bit rate mode : konstant
Bit rate : 896000
Bit rate : 896 kb/s
Channel(s) : 6
Channel(s) : 6 Kanäle
Channel(s)_Original : 8
Channel(s)_Original : 8 Kanäle
Channel positions : Front: L C R, Side: L R, Back: L R, LFE
Channel positions : 3/2/0.1
Channel layout : L R C LFE Ls Rs Lb Rb
Samples per frame : 1536
Sampling rate : 48000
Sampling rate : 48,0 kHz
Samples count : 279535104
Frame rate : 31.250
Frame rate : 31,250 FPS (1536 SPF)
Frame count : 181989
Compression mode : Lossy
Delay : 0
Delay : 00:00:00.000
Delay, origin : Container
Delay relative to video : 0
Delay relative to video : 00:00:00.000
Stream size : 652248576
Stream size : 622 MiB (3%)
Stream size : 622 MiB
Stream size : 622 MiB
Stream size : 622 MiB
Stream size : 622,0 MiB
Stream size : 622 MiB (3%)
Proportion of this stream : 0.03370
Title : ger
Language : de
Language : Deutsch
Language : Deutsch
Language : de
Language : deu
Language : de
Service kind : CM
Service kind : Complete Main
Default : Yes
Default : Ja
Forced : No
Forced : Nein
bsid : 16
dialnorm : -31
dialnorm : -31 dB
compr : -4.53
compr : -4.53 dB
acmod : 7 / 5
lfeon : 1 / 0
dialnorm_Average : -31
dialnorm_Average : -31 dB
dialnorm_Minimum : -31
dialnorm_Minimum : -31 dB
dialnorm_Maximum : -31
dialnorm_Maximum : -31 dB
dialnorm_Count : 654
compr_Average : 2.01
compr_Average : 2.01 dB
compr_Minimum : -4.53
compr_Minimum : -4.53 dB
compr_Maximum : 4.22
compr_Maximum : 4.22 dB
compr_Count : 252
dynrng_Average : 0.63
dynrng_Average : 0.63 dB
dynrng_Minimum : -3.87
dynrng_Minimum : -3.87 dB
dynrng_Maximum : 4.54
dynrng_Maximum : 4.54 dB
dynrng_Count : 653Re: Multiple audio tracks with different codecs and languages
https://github.com/MediaArea/MediaInfoL ... l.cpp#L436
seems to be an additional format feature of E-AC-3
-
stephen147
- Donor
- Posts: 131
- Joined: 01 Sep 2015, 22:40
Re: Multiple audio tracks with different codecs and languages
Thanks to all concerned.
Code: Select all
{
def mCFP =
[
'AAC LC SBR' : 'AAC',
'AAC LC' : 'AAC',
'AC 3 Dep' : 'E-AC3',
'AC 3' : 'AC3',
'DTS 96 24' : 'DTS 96-24',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS ES XXCH' : 'DTS-ES',
'DTS ES' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS XLL X' : 'DTS X',
'DTS XLL' : 'DTS-HD MA',
'DTS' : 'DTS',
'E AC 3 JOC' : 'EAC3 Atmos',
'E AC 3' : 'EAC3',
'MLP FBA 16 ch' : 'TrueHD Atmos',
'MLP FBA' : 'TrueHD',
'MP3' : 'MP3',
'PCM' : 'PCM'
]
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6/,'5.1').replaceAll(/8/,'7.1') }
audio.collect { au ->
def channels = any{ channelClean(au['ChannelPositionsString2'])}{ channelClean(au['ChannelsOriginal'])}{ channelClean(au['Channels']) }
def dynChannel = {au['NumberOfDynamicObjects'] + 'Objs\''};
def ch = channels
.tokenize('\\/').take(3)*.toDouble()
.inject(0, { a, b -> a + b })
.findAll { it > 0 }.max().toString() + 'ch'
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = { ( au['Format_AdditionalFeatures'] != null) ? audioClean(au['Format_AdditionalFeatures']) : '' }
def combined = allOf{codec}{format_profile}.join(' ')
def stream = allOf
{ mCFP.get(combined, 'UNKNOWN_FORMAT--'+combined+'--') }
{ dynChannel }
{ ch }
//{ au.'LanguageString3'.upperInitial() }
println "MISSING_mCFP_FORMAT: "+combined
return stream
}[0].join( ' ' )
}
Re: Multiple audio tracks with different codecs and languages
All 1 one format, use:
Code: Select all
allStreams.join(' & ').space('.')Code: Select all
bestStream.space('.')(first to last order)
Code: Select all
{
def codecList =
[
'MP3' : 'MP3',
'PCM' : 'PCM',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6/,'5.1').replaceAll(/8/,'7.1') }
def combined
audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = { ( au['Format_AdditionalFeatures'] != null) ? audioClean(au['Format_AdditionalFeatures']) : '' }
combined = allOf{codec}{format_profile}.join(' ')
def channels = any{ channelClean(au['ChannelPositionsString2'])}{ channelClean(au['ChannelsOriginal'])}{ channelClean(au['Channels']) }
def ch = channels
.tokenize('\\/').take(3)*.toDouble()
.inject(0, { a, b -> a + b })
.findAll { it > 0 }.max().toString()
audioStreams << ['index' : codecList.findIndexOf {it.key == combined}, 'codec' : codecList.get(combined, 'UNKNOWN_FORMAT'), 'combined' : combined, 'ch' : ch, 'objects' : any{'(' + au['NumberOfDynamicObjects'] + ' Objs)'}{' '}, 'lang' : any{au.'LanguageString3'.upperInitial()}{' '} ]
return audioStreams
}
def allStreams = audioStreams.collect{ it.codec + ' ' + it.ch + ' ' + it.objects + ' ' + it.lang }.unique()
def bestStream = audioStreams.unique().findAll{ it.index == audioStreams.index.max() }.collect{ it.codec + ' ' + it.ch + ' ' + it.objects + ' ' + it.lang }.find{ it }
allStreams.join(' & ').space('.')
bestStream.space('.')
}-
stephen147
- Donor
- Posts: 131
- Joined: 01 Sep 2015, 22:40
Re: Multiple audio tracks with different codecs and languages
Cool, thanks for the example. I'll stick with grabbing the [0] stream as this will be the primary one.
Re: Multiple audio tracks with different codecs and languages
Code: Select all
allStreams.first()added
Code: Select all
[allStreams.first(), bestStream].unique().join(' & ').space('.')if you don't want some of the output just editDTS-HD.MA.7.1.Eng.&.TrueHD.Atmos.7.1.(11.Objs).Eng
Code: Select all
def filter = { it.codec + ' ' + it.ch + ' ' + it.objects + ' ' + it.lang }Code: Select all
allStreams.take(3).join(' & ').space('.')Code: Select all
{
def codecList =
[
'MP3' : 'MP3',
'PCM' : 'PCM',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6/,'5.1').replaceAll(/8/,'7.1') }
def filter = { it.codec + ' ' + it.ch + ' ' + it.objects + ' ' + it.lang }
def combined
audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def channels = any{ channelClean(au['ChannelPositionsString2'])}{ channelClean(au['ChannelsOriginal'])}{ channelClean(au['Channels']) }
def ch = channels.tokenize('\\/').take(3)*.toDouble().inject(0, { a, b -> a + b }).findAll { it > 0 }.max().toString()
combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf {it.key == combined}, 'codec' : codecList.get(combined, 'UNKNOWN_FORMAT'), 'combined' : combined, 'ch' : ch, 'objects' : any{'(' + au['NumberOfDynamicObjects'] + ' Objs)'}{' '}, 'lang' : any{au.'LanguageString3'.upperInitial()}{' '} ]
return audioStreams
}
def allStreams = audioStreams.collect{ filter(it) }.unique()
def bestStream = audioStreams.unique().findAll{ it.index == audioStreams.index.max() }.collect{ filter(it) }.find{ it }
allStreams.join(' & ').space('.')
bestStream.space('.')
[allStreams.first(), bestStream].unique().join(' & ').space('.')
}Re: Multiple audio tracks with different codecs and languages
added check for defaultStream
Code: Select all
{
def codecList =
[
'MP3' : 'MP3',
'PCM' : 'PCM',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6/,'5.1').replaceAll(/8/,'7.1') }
def filter = { it.codec + ' ' + it.ch + ' ' + it.objects + ' ' + it.lang }
audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def channels = any{ channelClean(au['ChannelPositionsString2'])}{ channelClean(au['ChannelsOriginal'])}{ channelClean(au['Channels']) }
def ch = channels.tokenize('\\/').take(3)*.toDouble().inject(0, { a, b -> a + b }).findAll { it > 0 }.max().toString()
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf {it.key == combined}, 'default' : au['default'][0].toBoolean(), 'codec' : codecList.get(combined, 'UNKNOWN_FORMAT'), 'combined' : combined, 'ch' : ch, 'objects' : any{'(' + au['NumberOfDynamicObjects'] + ' Objs)'}{' '}, 'lang' : any{au.'LanguageString3'.upperInitial()}{' '} ]
return audioStreams
}
def allStreams = audioStreams.collect{ filter(it) }.unique()
def bestStream = audioStreams.unique().findAll{ it.index == audioStreams.index.max() }.collect{ filter(it) }.find{ it }
def defaultStream = audioStreams.findAll{it.default == true}.collect{ filter(it) }.find{ it }
allStreams.join(' & ').space('.')
bestStream.space('.')
[defaultStream, bestStream].unique().join(' & ').space('.')
}Re: Multiple audio tracks with different codecs and languages
Code: Select all
{ import java.math.RoundingMode
// audio map, fill in with your preferred one
def mCFP = []
audio.collect { au ->
/* Format seems to be consistently defined and identical to Format/String
Format_Profile and Format_AdditionalFeatures instead
seem to be usually mutually exclusive
Format_Commercial (and _If_Any variant) seem to be defined
mainly for Dolby/DTS formats */
def _ac = any
{ allOf
{ au["Format"] }
{ au["Format_Profile"] }
{ au["Format_AdditionalFeatures"] }
.join(" ") }
{ au["Format_Commercial"] }
/* original _aco_ binding uses "Codec_Profile", "Format_Profile", "Format_Commercial" */
def _aco = any{ au["Codec_Profile"] }{ au["Format_Profile"] }{ au["Format_Commercial"] }
/* def atmos = (_aco =~ /(?i:atmos)/) ? "Atmos" : null */
def isAtmos = {
def _fAtmos = any{audio.FormatCommercial =~ /(?i)atmos/}{false}
def _oAtmos = any{audio.NumberOfDynamicObjects}{false}
if (_fAtmos || _oAtmos) { return "Atmos" }
}
/* _channels_ uses "ChannelPositions/String2", "Channel(s)_Original", "Channel(s)"
compared to _af_ which uses "Channel(s)_Original", "Channel(s)"
using another variable allows calculating the output for each audio stream */
String _channels = any
{ au["ChannelPositions/String2"] }
{ au["Channel(s)_Original"] }
{ au["Channel(s)"] }
String _ch
/* _channels can contain no numbers */
Object splitCh = _channels =~ /^(?i)object.based$/ ? "Object Based" :
_channels.tokenize("\\/\\.")
/* the below may be needed for 3/2/0.2.1/3/2/0.1 files, of
which I have no examples anymore since MediaInfo 18.12 */
// _channels.tokenize("\\/").take(3)*.tokenize("\\.")
// .flatten()*.toInteger()
switch (splitCh) {
case { it instanceof String }:
/* Object Based channels, which usually have also a numeric value */
def _chDeep = any{ au["Channel(s)"] }{ au["Channel(s)/String"].replaceAll("channels", "") }
_ch = allOf{ splitCh }{ _chDeep + "ch" }.join(" ")
break
case { it.size > 4 }:
/* format similar to 3/2/0.2.1, coercing the last value to Double fails */
def wide = splitCh.takeRight(1)
Double main = splitCh.take(4)*.toDouble().inject(0, { a, b -> a + b })
Double sub = Double.parseDouble("0." + wide.last())
_ch = (main + sub).toBigDecimal().setScale(1, RoundingMode.HALF_UP).toString()
break
case { it.size > 1 }:
/* original logic is _mostly_ unchanged if format is like 3/2/0.1 */
Double sub = Double.parseDouble(splitCh.takeRight(2).join("."))
_ch = splitCh.take(2)*.toDouble().plus(sub).inject(0, { a, b -> a + b })
.toBigDecimal().setScale(1, RoundingMode.HALF_UP).toString()
break
default:
_ch = splitCh.first().toDouble()
}
Re: Multiple audio tracks with different codecs and languages
changed: bestStream to preferredStream
added: bestBitRate
added: support for chFilter (change 'ch' : ch, to "'ch' : chFilter,")
Code: Select all
{
def codecList =
[
'MP3' : 'MP3',
'PCM' : 'PCM',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def filter = { [it.codec, it.ch, it.objects, it.lang] }
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1')}
def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = (( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null)
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf {it.key == combined}, 'default' : au['default'][0].toBoolean(),
'codec' : codecList.get(combined, 'UNKNOWN_FORMAT'), 'combined' : combined, 'ch' : ch,
'bitrate' : any{toInt(au.BitRate)}{toInt(au.BitRate_Maximum)}{au.FrameRate.toDouble()}{null},
'objects' : any{'[' + au['NumberOfDynamicObjects'] + ' Objs]'}{null}, 'lang' : any{au.'LanguageString3'.upperInitial()}{null} ]
return audioStreams
}
def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ')
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = oneStream(audioStreams.findAll{it.default == true})
allStreams.join(' & ').space('.')
preferredStream.space('.')
defaultStream.space('.')
bestBitRate.space('.')
[bestBitRate, preferredStream].unique().join(' & ').space('.')
[defaultStream, bestBitRate].unique().join(' & ').space('.')
}Re: Multiple audio tracks with different codecs and languages
https://get.filebot.net/filebot/BETA/
The new builds come with MediaInfo 19.04 which hopefully makes things work better again.
Re: Multiple audio tracks with different codecs and languages
With MediaInfo 18.12 and 19.04 the result is the following:
Code: Select all
Audio
Count : 298
StreamCount : 1
StreamKind : Audio
StreamKind/String : Audio
StreamKindID : 0
StreamOrder : 1
ID : 2
ID/String : 2
UniqueID : 8654701659007087417
Format : AC-3
Format/String : E-AC-3
Format/Info : Enhanced AC-3
Format/Url : https://en.wikipedia.org/wiki/AC3
Format_Commercial : Dolby Digital Plus
Format_Commercial_IfAny : Dolby Digital Plus
Format_Profile : Blu-ray Disc
Format_Settings_Endianness : Big
Format_AdditionalFeatures : Dep
InternetMediaType : audio/eac3
CodecID : A_EAC3
Duration : 8089088.000000
Duration/String : 2h 14mn
Duration/String1 : 2h 14mn 49s 88ms
Duration/String2 : 2h 14mn
Duration/String3 : 02:14:49.088
Duration/String4 : 02:15:54:10
Duration/String5 : 02:14:49.088 (02:15:54:10)
BitRate_Mode : CBR
BitRate_Mode/String : CBR
BitRate : 1280000
BitRate/String : 1280 Kbps
Channel(s) : 8
Channel(s)/String : 8 channel3
ChannelPositions : Front: L C R, Side: L R, Back: L R, LFE
ChannelPositions/String2 : 3/2/0.1
ChannelLayout : L R C LFE Ls Rs Lb Rb
SamplesPerFrame : 1536
SamplingRate : 48000
SamplingRate/String : 48.0 KHz
SamplingCount : 388276224
FrameRate : 31.250
FrameRate/String : 31.250 fps3 (1536 SPF)
FrameCount : 252784
Compression_Mode : Lossy
Compression_Mode/String : Lossy
Delay : 0
Delay/String3 : 00:00:00.000
Delay_Source : Container
Delay_Source/String : Container
Video_Delay : 0
Video_Delay/String3 : 00:00:00.000
StreamSize : 1294254080
StreamSize/String : 1.21 GiB (6%)
StreamSize/String1 : 1 GiB
StreamSize/String2 : 1.2 GiB
StreamSize/String3 : 1.21 GiB
StreamSize/String4 : 1.205 GiB
StreamSize/String5 : 1.21 GiB (6%)
StreamSize_Proportion : 0.06499
Title : Dolby Digital Plus Audio / 7.1 / 48 kHz / 1280 kbps
Language : en
Language/String : en
Language/String1 : en
Language/String2 : en
Language/String3 : eng
Language/String4 : en
ServiceKind : CM
ServiceKind/String : Complete Main
Default : Yes
Default/String : Yes
Forced : No
Forced/String : No
bsid : 16
dialnorm : -31
dialnorm : -31 dB
compr : -0.28
compr : -0.28 dB
acmod : 7 / 5
lfeon : 1 / 0
dialnorm_Average : -31
dialnorm_Average : -31 dB
dialnorm_Minimum : -31
dialnorm_Minimum : -31 dB
dialnorm_Maximum : -31
dialnorm_Maximum : -31 dB
dialnorm_Count : 3476
compr_Average : -2.76
compr_Average : -2.76 dB
compr_Minimum : -6.88
compr_Minimum : -6.88 dB
compr_Maximum : -0.56
compr_Maximum : -0.56 dB
compr_Count : 700
- Format/String and Format differ (AC-3 vs E-AC-3) for this I modified my last script by changing the Format into any{au["Format/String"] }{ au["Format"] }
- never seen Format_Profile: Blu-ray Disc or Format_AdditionalFeatures: Dep, any info on them?
- channels seem 8, which is consistent with ChannelPositions and ChannelLayout but not with ChannelPositions/String2 which reports 5.1, do I really need to parse raw ChannelPositions to fix this? any smarter ideas?
Re: Multiple audio tracks with different codecs and languages
Code: Select all
Title : Dolby Digital Plus Audio / 7.1 / 48 kHz / 1280 kbpsMight be useful, but probably won't be defined for the vast majority of files.
Re: Multiple audio tracks with different codecs and languages
For naming purposes I found out that the AdditionalFeatures bascically means a hybrid stream.
In this case there seem to be:
- "base" 5.1 AC-3 stream, decodable by an AC-3 decoder. (The Format field which represents the "minimal" decoder required)
- Dependant E-AC-3 stream with 2 additional channels, optional and apparently interleaved with the first one (The Format/String apparently)
The ChannelPositions/String2 seems a bug, a workaround in this specific case would be to tokenize by comma ChannelPositions (Front: L C R, Side: L R, Back: L R, LFE), strip whatever's before the colon, count number of words in the string (3,2,2,1) with the last one being the Low Frequency Effects.
Re: Multiple audio tracks with different codecs and languages
Looks like this: "ESP ac3 2.0 ENG flac 1.0"
./1961 - Homicidal - Homicidio - William Castle/1961 - Homicidal - Homicidio - William Castle BDR 1080p 35.0Mbps ESP ac3 2.0 ENG flac 1.0 SUB ENG ESP.mkv
Code: Select all
{
def codecList =
[
'MP3' : 'mp3',
'FLAC' : 'flac',
'PCM' : 'pcm',
'AAC LC' : 'aac',
'AAC LC SBR' : 'aac',
'AC 3' : 'ac3',
'AC 3 Dep' : 'eac3',
'E AC 3' : 'eac3',
'E AC 3 JOC' : 'eac3 Atmos',
'DTS' : 'dts',
'DTS 96 24' : 'dts',
'DTS ES' : 'dtses',
'DTS ES XXCH' : 'dtses',
'DTS XBR' : 'dts',
'DTS ES XBR' : 'dtses',
'DTS ES XXCH XBR' : 'dtses',
'DTS XLL' : 'dts',
'DTS ES XLL' : 'dtses',
'DTS ES XXCH XLL' : 'dtses',
'DTS XLL X' : 'dtsx',
'MLP FBA' : 'truehd',
'MLP FBA 16 ch' : 'truehd Atmos'
]
def filter = { [it.lang, it.codec, it.ch, it.objects] }
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1')}
def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = (( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null)
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf {it.key == combined}, 'default' : au['default'][0].toBoolean(),
'codec' : codecList.get(combined, 'UNKNOWN_FORMAT'), 'combined' : combined, 'ch' : ch,
'bitrate' : any{toInt(au.BitRate)}{toInt(au.BitRate_Maximum)}{au.FrameRate.toDouble()}{null},
'objects' : any{'[' + au['NumberOfDynamicObjects'] + ' Objs]'}{null}, 'lang' : any{au.'LanguageString3'.upper().replaceAll("SPA","ESP")}{null} ]
return audioStreams
}
def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ')
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = oneStream(audioStreams.findAll{it.default == true})
allStreams.join(' ').space(' ')
preferredStream.space(' ')
defaultStream.space(' ')
bestBitRate.space(' ')
[bestBitRate, preferredStream].unique().join(' ')
[defaultStream, bestBitRate].unique().join(' ')
}Re: Multiple audio tracks with different codecs and languages
e.g. German
Code: Select all
{
def preferredLang = 'Deu'
def codecList =
[
'MP3' : 'MP3',
'PCM' : 'PCM',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def filter = { [it.codec, it.ch, it.objects, it.lang] }
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1')}
def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = (( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null)
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf {it.key == combined}, 'default' : au['default'][0].toBoolean(),
'codec' : codecList.get(combined, 'UNKNOWN_FORMAT'), 'combined' : combined, 'ch' : ch,
'bitrate' : any{toInt(au.BitRate)}{toInt(au.BitRate_Maximum)}{au.FrameRate.toDouble()}{null},
'objects' : any{'[' + au['NumberOfDynamicObjects'] + ' Objs]'}{null}, 'lang' : any{au.'LanguageString3'.upperInitial()}{null} ]
return audioStreams
}
def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ')
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = oneStream(audioStreams.findAll{it.default == true})
def bestPreferredLang = any{audioStreams.findAll{it.lang == preferredLang }.sort{a, b -> b.bitrate <=> a.bitrate}.collect{ filter(it) }*.minus(null).unique().get(0).join(' ')}{defaultStream}
bestPreferredLang.space('.')
}Re: Multiple audio tracks with different codecs and languages
Added useChFilter = to make it more user friendly ( true or false, makes it more "scene" like )
Code: Select all
{
def preferredLang = 'Eng'
def useChFilter = false
def filter = { [it.codec, it.ch, it.objects, it.lang] }
def codecList =
[
'MP3' : 'MP3',
'PCM' : 'PCM',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ')
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = oneStream(audioStreams.findAll{ it.default == true })
def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{}
allStreams.join(' & ').space('.')
preferredStream.space('.')
defaultStream.space('.')
bestBitRate.space('.')
[defaultStream, bestBitRate].unique().join(' & ').space('.')
[bestBitRate, preferredStream].unique().join(' & ').space('.')
any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}Code: Select all
allStreams.join(' & ').space('.')
preferredStream.space('.')
defaultStream.space('.')
bestBitRate.space('.')
[defaultStream, bestBitRate].unique().join(' & ').space('.')
[bestBitRate, preferredStream].unique().join(' & ').space('.')
any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}Code: Select all
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )allStreams = All Audio Streams
preferredStream = The last (best) match from codecList (order matters, from low to high quality / or what you prefer)
defaultStream = The Audio Stream tagged "default=Yes" or if only one Stream
bestBitRate = The Audio Stream with the highest BitRate (with BitRate_Maximum/FrameRate as backup)
Re: Multiple audio tracks with different codecs and languages
I use CLI via SSH to rename media on my Synology (where I have FileBot installed.) It works great. I have a basic understanding of the expressions I've compiled. But is "codecList" a file in my FileBot package that's installed on my Syno? Do I edit that to add the many awesome codecs in the code Kim has evolved, here? Or is that long expression something I add to a CLI command each time I want to process files in my 'unsorted' folder?
Re: Multiple audio tracks with different codecs and languages
Code: Select all
MovieTitle (2019) 118mins [18.0 Mbps] (1920x800) Dolby Digital 5.1 [6ch]
Code: Select all
SameMovieTitle 118mins (18.0 Mbps) (1920x800) TrueHD 7.1 Atmos [8ch]
I would like to continue using CLI via Synology package - I’ve tried at least ten different expressions I’ve found on the forums, re. audio codec, but it always outputs 5.1 Dolby.
I know I'm not providing all the info you need, but if we know that all the modules are updated to latest, on the Syno server... is there a default reason why outputs differ?
Re: Multiple audio tracks with different codecs and languages
Code: Select all
filebot -rename -r /volume1/Media/UNSORTED/Movies/ --db TheMovieDB -non-strict --conflict auto --output /volume1/Media/SORTED/ --format "Movies/{certification}/{n} ({y})/{n} ({y}) {minutes}mins [{mbps}] ({resolution}) {aco} {channels} [{af}]/{ny} @{mbps} {tags.upper[]} {' CD'+pi}" --action copyRe: Multiple audio tracks with different codecs and languages
Re: Multiple audio tracks with different codecs and languages
The output is below — I assume the next step is for me to check the version I'm using vs. the newest version available.
Code: Select all
FileBot 4.8.5 (r6224)
JNA Native: 5.2.2
MediaInfo: 19.09
7-Zip-JBinding: 9.20
Chromaprint: fpcalc version 1.4.3
Extended Attributes: OK
Unicode Filesystem: OK
Script Bundle: 2019-05-15 (r565)
Groovy: 2.5.6
JRE: Java(TM) SE Runtime Environment 1.8.0_201
JVM: 64-bit Java HotSpot(TM) 64-Bit Server VM
CPU/MEM: 4 Core / 1 GB Max Memory / 40 MB Used Memory
OS: Linux (amd64)
HW: Linux ******** 3.10.105 #24922 SMP Wed Jul 3 16:37:24 CST 2019 x86_64 GNU/Linux synology_avoton_1815+
DATA: /volume1/@appstore/filebot/data/admin
Package: SPK
License: FileBot License P75********Re: Multiple audio tracks with different codecs and languages
MediaInfo: 19.09 is the latest one though, so that should work exactly the same as the latest beta, which bundles this version of MediaInfo as well:
viewtopic.php?t=1609
Re: Multiple audio tracks with different codecs and languages
I'll test results running FB on a MacMini that's connected to the Syno (to see if my results differ vs. running the Syno FB build.)
Since I'll be trying FB on a diff. platform, it's a good time to ask:
Is the GUI (app) version of FB the only way to use these long { def codecList... } definitions of audio codecs? Like in the "Filter" field of the GUI version?
Or, if there's a thread, or keywords that'll help me search how to use these w/ the CLI-based FB operations...?
I know it's a real huckleberry question. I'm brain-blocked on the next step with this.
Thanks for helping me better understand these diff results (Syno FB vs. Windows FB.)
Re: Multiple audio tracks with different codecs and languages
Re: Multiple audio tracks with different codecs and languages
And thank you for the tools. Time to learn about syntax and to compare the data FileBot is getting back from libmediainfo.
Even in the short year I've been using FileBot, it has been really cool to see how you continuously evolve it, based on input.
Cheers @rednoah.
-Erin
Re: Multiple audio tracks with different codecs and languages
Keep the good ideas coming. I might not always implement exactly what you want, but it might inspire me to implement something even more interesting.nartana wrote: 15 Mar 2020, 00:19 Even in the short year I've been using FileBot, it has been really cool to see how you continuously evolve it, based on input.
Re: Multiple audio tracks with different codecs and languages
this what i ' m looking for many days, now my search is over. <3kim wrote: 05 Mar 2020, 22:11 Added addToList = to make it more user friendly ( output e.g. [Add to "DTS XBR" codecList] )
Added useChFilter = to make it more user friendly ( true or false, makes it more "scene" like )
Only use one of these lines:Code: Select all
{ def preferredLang = 'Eng' def useChFilter = false def filter = { [it.codec, it.ch, it.objects, it.lang] } def codecList = [ 'MP3' : 'MP3', 'PCM' : 'PCM', 'AAC LC' : 'AAC', 'AAC LC SBR' : 'AAC', 'AAC LC SBR PS' : 'AAC', 'AC 3' : 'AC3', 'AC 3 Dep' : 'EAC3', 'E AC 3' : 'EAC3', 'E AC 3 JOC' : 'EAC3 Atmos', 'AC 3 Dep JOC' : 'EAC3 Atmos', 'DTS' : 'DTS', 'DTS 96 24' : 'DTS 96-24', 'DTS ES' : 'DTS-ES', 'DTS ES XXCH' : 'DTS-ES', 'DTS XBR' : 'DTS-HD HRA', 'DTS ES XBR' : 'DTS-HD HRA', 'DTS ES XXCH XBR' : 'DTS-HD HRA', 'DTS XLL' : 'DTS-HD MA', 'DTS ES XLL' : 'DTS-HD MA', 'DTS ES XXCH XLL' : 'DTS-HD MA', 'DTS XLL X' : 'DTS X', 'MLP FBA' : 'TrueHD', 'MLP FBA 16 ch' : 'TrueHD Atmos' ] def audioStreams = [] def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') } def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') } def dString = { it.toDouble().toString() } def toInt = { it.toInteger() } any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{} def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() } { channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) } def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ) def combined = allOf{codec}{format_profile}.join(' ') audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null}, 'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ] return audioStreams } def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort() def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ') def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }) def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }) def defaultStream = oneStream(audioStreams.findAll{ it.default == true }) def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{} allStreams.join(' & ').space('.') preferredStream.space('.') defaultStream.space('.') bestBitRate.space('.') [defaultStream, bestBitRate].unique().join(' & ').space('.') [bestBitRate, preferredStream].unique().join(' & ').space('.') any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream} }{'NO_AUDIO'} }useChFilter = this line:Code: Select all
allStreams.join(' & ').space('.') preferredStream.space('.') defaultStream.space('.') bestBitRate.space('.') [defaultStream, bestBitRate].unique().join(' & ').space('.') [bestBitRate, preferredStream].unique().join(' & ').space('.') any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}= if AAC/MP3 2.0 OR AC3/EAC3/DTS/TrueHD/MLPFBA 5.1, then don't show the 2.0/5.1 partCode: Select all
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
allStreams = All Audio Streams
preferredStream = The last (best) match from codecList (order matters, from low to high quality / or what you prefer)
defaultStream = The Audio Stream tagged "default=Yes" or if only one Stream
bestBitRate = The Audio Stream with the highest BitRate (with BitRate_Maximum/FrameRate as backup)
Thanks for this format, Really appreciate your work
-
AbedlaPaille
- Posts: 107
- Joined: 12 Apr 2020, 04:02
Re: Multiple audio tracks with different codecs and languages
Can this awesome bit of wizardry be tweaked to prefer the stream in the language matching the one from info.OriginalLanguage? Cheers for this snippet kim it's quite amazing.kim wrote: 05 Mar 2020, 22:11 Added addToList = to make it more user friendly ( output e.g. [Add to "DTS XBR" codecList] )
Added useChFilter = to make it more user friendly ( true or false, makes it more "scene" like )
Only use one of these lines:Code: Select all
{ def preferredLang = 'Eng' def useChFilter = false def filter = { [it.codec, it.ch, it.objects, it.lang] } def codecList = [ 'MP3' : 'MP3', 'PCM' : 'PCM', 'AAC LC' : 'AAC', 'AAC LC SBR' : 'AAC', 'AAC LC SBR PS' : 'AAC', 'AC 3' : 'AC3', 'AC 3 Dep' : 'EAC3', 'E AC 3' : 'EAC3', 'E AC 3 JOC' : 'EAC3 Atmos', 'AC 3 Dep JOC' : 'EAC3 Atmos', 'DTS' : 'DTS', 'DTS 96 24' : 'DTS 96-24', 'DTS ES' : 'DTS-ES', 'DTS ES XXCH' : 'DTS-ES', 'DTS XBR' : 'DTS-HD HRA', 'DTS ES XBR' : 'DTS-HD HRA', 'DTS ES XXCH XBR' : 'DTS-HD HRA', 'DTS XLL' : 'DTS-HD MA', 'DTS ES XLL' : 'DTS-HD MA', 'DTS ES XXCH XLL' : 'DTS-HD MA', 'DTS XLL X' : 'DTS X', 'MLP FBA' : 'TrueHD', 'MLP FBA 16 ch' : 'TrueHD Atmos' ] def audioStreams = [] def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') } def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') } def dString = { it.toDouble().toString() } def toInt = { it.toInteger() } any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{} def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() } { channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) } def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ) def combined = allOf{codec}{format_profile}.join(' ') audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null}, 'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ] return audioStreams } def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort() def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ') def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }) def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }) def defaultStream = oneStream(audioStreams.findAll{ it.default == true }) def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{} allStreams.join(' & ').space('.') preferredStream.space('.') defaultStream.space('.') bestBitRate.space('.') [defaultStream, bestBitRate].unique().join(' & ').space('.') [bestBitRate, preferredStream].unique().join(' & ').space('.') any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream} }{'NO_AUDIO'} }useChFilter = this line:Code: Select all
allStreams.join(' & ').space('.') preferredStream.space('.') defaultStream.space('.') bestBitRate.space('.') [defaultStream, bestBitRate].unique().join(' & ').space('.') [bestBitRate, preferredStream].unique().join(' & ').space('.') any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}= if AAC/MP3 2.0 OR AC3/EAC3/DTS/TrueHD/MLPFBA 5.1, then don't show the 2.0/5.1 partCode: Select all
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
allStreams = All Audio Streams
preferredStream = The last (best) match from codecList (order matters, from low to high quality / or what you prefer)
defaultStream = The Audio Stream tagged "default=Yes" or if only one Stream
bestBitRate = The Audio Stream with the highest BitRate (with BitRate_Maximum/FrameRate as backup)
Re: Multiple audio tracks with different codecs and languages
Code: Select all
{
import net.filebot.Language;
def preferredLang = Language.findLanguage(info.OriginalLanguage).ISO3.upperInitial()iso_639_1=nl iso_639_3=nld iso_639_2B=dut tag=nl-NL names=[Dutch]
Code: Select all
{
import net.filebot.Language;
def preferredLang = Language.findLanguage(info.OriginalLanguage).iso_639_2B.upperInitial()Code: Select all
{
import net.filebot.Language;
def preferredLang = Language.findLanguage(info.OriginalLanguage).ISO3B.upperInitial()-
haveabreak
- Posts: 6
- Joined: 05 Jul 2020, 21:35
Re: Multiple audio tracks with different codecs and languages
How could I add this to my existing naming convention?kim wrote: 05 Mar 2020, 22:11 Added addToList = to make it more user friendly ( output e.g. [Add to "DTS XBR" codecList] )
Added useChFilter = to make it more user friendly ( true or false, makes it more "scene" like )
Only use one of these lines:Code: Select all
{ def preferredLang = 'Eng' def useChFilter = false def filter = { [it.codec, it.ch, it.objects, it.lang] } def codecList = [ 'MP3' : 'MP3', 'PCM' : 'PCM', 'AAC LC' : 'AAC', 'AAC LC SBR' : 'AAC', 'AAC LC SBR PS' : 'AAC', 'AC 3' : 'AC3', 'AC 3 Dep' : 'EAC3', 'E AC 3' : 'EAC3', 'E AC 3 JOC' : 'EAC3 Atmos', 'AC 3 Dep JOC' : 'EAC3 Atmos', 'DTS' : 'DTS', 'DTS 96 24' : 'DTS 96-24', 'DTS ES' : 'DTS-ES', 'DTS ES XXCH' : 'DTS-ES', 'DTS XBR' : 'DTS-HD HRA', 'DTS ES XBR' : 'DTS-HD HRA', 'DTS ES XXCH XBR' : 'DTS-HD HRA', 'DTS XLL' : 'DTS-HD MA', 'DTS ES XLL' : 'DTS-HD MA', 'DTS ES XXCH XLL' : 'DTS-HD MA', 'DTS XLL X' : 'DTS X', 'MLP FBA' : 'TrueHD', 'MLP FBA 16 ch' : 'TrueHD Atmos' ] def audioStreams = [] def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') } def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') } def dString = { it.toDouble().toString() } def toInt = { it.toInteger() } any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{} def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() } { channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) } def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ) def combined = allOf{codec}{format_profile}.join(' ') audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null}, 'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ] return audioStreams } def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort() def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ') def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }) def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }) def defaultStream = oneStream(audioStreams.findAll{ it.default == true }) def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{} allStreams.join(' & ').space('.') preferredStream.space('.') defaultStream.space('.') bestBitRate.space('.') [defaultStream, bestBitRate].unique().join(' & ').space('.') [bestBitRate, preferredStream].unique().join(' & ').space('.') any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream} }{'NO_AUDIO'} }useChFilter = this line:Code: Select all
allStreams.join(' & ').space('.') preferredStream.space('.') defaultStream.space('.') bestBitRate.space('.') [defaultStream, bestBitRate].unique().join(' & ').space('.') [bestBitRate, preferredStream].unique().join(' & ').space('.') any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}= if AAC/MP3 2.0 OR AC3/EAC3/DTS/TrueHD/MLPFBA 5.1, then don't show the 2.0/5.1 partCode: Select all
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
allStreams = All Audio Streams
preferredStream = The last (best) match from codecList (order matters, from low to high quality / or what you prefer)
defaultStream = The Audio Stream tagged "default=Yes" or if only one Stream
bestBitRate = The Audio Stream with the highest BitRate (with BitRate_Maximum/FrameRate as backup)
Code: Select all
movieFormat={plex.derive{' ' + tags.join(' ')}{' [' + allOf{vs}{vf}{hdr}{ac}{channels}{vc.replace('AVC','x264').replace('ATEME', 'H.265').replace('Microsoft', 'VC-1').replace('HEVC','x265')}{fn.match(/REMUX/).upper()}.join(' ') + ']' + {'-' + group}}.tail}
seriesFormat={plex.derive{' ' + tags.join(' ')}{' [' + allOf{vs}{vf}{hdr}{ac}{channels}{vc.replace('AVC','x264').replace('ATEME', 'H.265').replace('Microsoft', 'VC-1').replace('HEVC','x265')}{fn.match(/REMUX/).upper()}.join(' ') + ']' + {'-' + group}}.tail}
animeFormat={plex.derive{' ' + tags.join(' ')}{' [' + allOf{vs}{vf}{hdr}{ac}{channels}{vc.replace('AVC','x264').replace('ATEME', 'H.265').replace('Microsoft', 'VC-1').replace('HEVC','x265')}{fn.match(/REMUX/).upper()}.join(' ') + ']' + {'-' + group}}.tail}Re: Multiple audio tracks with different codecs and languages
Code: Select all
{ac}{channels}Code: Select all
{plex.derive{' ' + tags.join(' ')}{' [' + allOf{vs}{vf}{hdr}{
def preferredLang = 'Eng'
def useChFilter = false
def filter = { [it.codec, it.ch, it.objects, it.lang] }
def codecList =
[
'MP3' : 'MP3',
'PCM' : 'PCM',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ')
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = oneStream(audioStreams.findAll{ it.default == true })
def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{}
any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}{vc.replace('AVC','x264').replace('ATEME', 'H.265').replace('Microsoft', 'VC-1').replace('HEVC','x265')}{fn.match(/REMUX/).upper()}.join(' ') + ']' + {'-' + group}}.tail}Code: Select all
{ def preferredLang = 'Eng'; def useChFilter = false; def filter = { [it.codec, it.ch, it.objects, it.lang] };Code: Select all
{plex.derive{' ' + tags.join(' ')}{' [' + allOf{vs}{vf}{hdr}{def preferredLang = 'Eng'; def useChFilter = false; def filter = { [it.codec, it.ch, it.objects, it.lang] }; def codecList = ['MP3' : 'MP3','PCM' : 'PCM','AAC LC' : 'AAC','AAC LC SBR' : 'AAC','AAC LC SBR PS' : 'AAC','AC 3' : 'AC3','AC 3 Dep' : 'EAC3','E AC 3' : 'EAC3','E AC 3 JOC' : 'EAC3 Atmos','AC 3 Dep JOC' : 'EAC3 Atmos','DTS' : 'DTS','DTS 96 24' : 'DTS 96-24','DTS ES' : 'DTS-ES','DTS ES XXCH' : 'DTS-ES','DTS XBR' : 'DTS-HD HRA','DTS ES XBR' : 'DTS-HD HRA','DTS ES XXCH XBR' : 'DTS-HD HRA','DTS XLL' : 'DTS-HD MA','DTS ES XLL' : 'DTS-HD MA','DTS ES XXCH XLL' : 'DTS-HD MA','DTS XLL X' : 'DTS X','MLP FBA' : 'TrueHD','MLP FBA 16 ch' : 'TrueHD Atmos']; def audioStreams = []; def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }; def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }; def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') }; def dString = { it.toDouble().toString() }; def toInt = { it.toInteger() }; any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }); def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}; def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }; def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ); def combined = allOf{codec}{format_profile}.join(' '); audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ]; return audioStreams}; def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort(); def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' '); def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }); def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }); def defaultStream = oneStream(audioStreams.findAll{ it.default == true }); def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{}; any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}}{'NO_AUDIO'}}{vc.replace('AVC','x264').replace('ATEME', 'H.265').replace('Microsoft', 'VC-1').replace('HEVC','x265')}{fn.match(/REMUX/).upper()}.join(' ') + ']' + {'-' + group}}.tail}Code: Select all
{vc}{ac}-
haveabreak
- Posts: 6
- Joined: 05 Jul 2020, 21:35
Re: Multiple audio tracks with different codecs and languages
Wow, thank you so much for the help! This helps a lot!kim wrote: 30 Jul 2020, 17:45 very easy just replace thelike so:Code: Select all
{ac}{channels}if you really want is on one line just add a ; after all the "def blocks"e.g.Code: Select all
{plex.derive{' ' + tags.join(' ')}{' [' + allOf{vs}{vf}{hdr}{ def preferredLang = 'Eng' def useChFilter = false def filter = { [it.codec, it.ch, it.objects, it.lang] } def codecList = [ 'MP3' : 'MP3', 'PCM' : 'PCM', 'AAC LC' : 'AAC', 'AAC LC SBR' : 'AAC', 'AAC LC SBR PS' : 'AAC', 'AC 3' : 'AC3', 'AC 3 Dep' : 'EAC3', 'E AC 3' : 'EAC3', 'E AC 3 JOC' : 'EAC3 Atmos', 'AC 3 Dep JOC' : 'EAC3 Atmos', 'DTS' : 'DTS', 'DTS 96 24' : 'DTS 96-24', 'DTS ES' : 'DTS-ES', 'DTS ES XXCH' : 'DTS-ES', 'DTS XBR' : 'DTS-HD HRA', 'DTS ES XBR' : 'DTS-HD HRA', 'DTS ES XXCH XBR' : 'DTS-HD HRA', 'DTS XLL' : 'DTS-HD MA', 'DTS ES XLL' : 'DTS-HD MA', 'DTS ES XXCH XLL' : 'DTS-HD MA', 'DTS XLL X' : 'DTS X', 'MLP FBA' : 'TrueHD', 'MLP FBA 16 ch' : 'TrueHD Atmos' ] def audioStreams = [] def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') } def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') } def dString = { it.toDouble().toString() } def toInt = { it.toInteger() } any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{} def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() } { channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) } def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ) def combined = allOf{codec}{format_profile}.join(' ') audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null}, 'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ] return audioStreams } def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort() def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' ') def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }) def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }) def defaultStream = oneStream(audioStreams.findAll{ it.default == true }) def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{} any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream} }{'NO_AUDIO'} }{vc.replace('AVC','x264').replace('ATEME', 'H.265').replace('Microsoft', 'VC-1').replace('HEVC','x265')}{fn.match(/REMUX/).upper()}.join(' ') + ']' + {'-' + group}}.tail}Code: Select all
{ def preferredLang = 'Eng'; def useChFilter = false; def filter = { [it.codec, it.ch, it.objects, it.lang] };btw: the normal way is video before audioCode: Select all
{plex.derive{' ' + tags.join(' ')}{' [' + allOf{vs}{vf}{hdr}{def preferredLang = 'Eng'; def useChFilter = false; def filter = { [it.codec, it.ch, it.objects, it.lang] }; def codecList = ['MP3' : 'MP3','PCM' : 'PCM','AAC LC' : 'AAC','AAC LC SBR' : 'AAC','AAC LC SBR PS' : 'AAC','AC 3' : 'AC3','AC 3 Dep' : 'EAC3','E AC 3' : 'EAC3','E AC 3 JOC' : 'EAC3 Atmos','AC 3 Dep JOC' : 'EAC3 Atmos','DTS' : 'DTS','DTS 96 24' : 'DTS 96-24','DTS ES' : 'DTS-ES','DTS ES XXCH' : 'DTS-ES','DTS XBR' : 'DTS-HD HRA','DTS ES XBR' : 'DTS-HD HRA','DTS ES XXCH XBR' : 'DTS-HD HRA','DTS XLL' : 'DTS-HD MA','DTS ES XLL' : 'DTS-HD MA','DTS ES XXCH XLL' : 'DTS-HD MA','DTS XLL X' : 'DTS X','MLP FBA' : 'TrueHD','MLP FBA 16 ch' : 'TrueHD Atmos']; def audioStreams = []; def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }; def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }; def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') }; def dString = { it.toDouble().toString() }; def toInt = { it.toInteger() }; any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }); def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}; def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }; def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ); def combined = allOf{codec}{format_profile}.join(' '); audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ]; return audioStreams}; def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort(); def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' '); def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }); def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }); def defaultStream = oneStream(audioStreams.findAll{ it.default == true }); def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{}; any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}}{'NO_AUDIO'}}{vc.replace('AVC','x264').replace('ATEME', 'H.265').replace('Microsoft', 'VC-1').replace('HEVC','x265')}{fn.match(/REMUX/).upper()}.join(' ') + ']' + {'-' + group}}.tail}Code: Select all
{vc}{ac}
Tried to edit it as you said. How does that look and how would you do it?btw: the normal way is video before audioCode: Select all
{vc}{ac}
Code: Select all
{plex.derive{' ' + tags.join(' ')}{' [' + allOf{vs}{vf}{hdr}{vc.replace('AVC','x264').replace('ATEME', 'H.265').replace('Microsoft', 'VC-1').replace('HEVC','x265')}{fn.match(/REMUX/).upper()}{def preferredLang = 'Eng'; def useChFilter = false; def filter = { [it.codec, it.ch, it.objects, it.lang] }; def codecList = ['MP3' : 'MP3','PCM' : 'PCM','AAC LC' : 'AAC','AAC LC SBR' : 'AAC','AAC LC SBR PS' : 'AAC','AC 3' : 'AC3','AC 3 Dep' : 'EAC3','E AC 3' : 'EAC3','E AC 3 JOC' : 'EAC3 Atmos','AC 3 Dep JOC' : 'EAC3 Atmos','DTS' : 'DTS','DTS 96 24' : 'DTS 96-24','DTS ES' : 'DTS-ES','DTS ES XXCH' : 'DTS-ES','DTS XBR' : 'DTS-HD HRA','DTS ES XBR' : 'DTS-HD HRA','DTS ES XXCH XBR' : 'DTS-HD HRA','DTS XLL' : 'DTS-HD MA','DTS ES XLL' : 'DTS-HD MA','DTS ES XXCH XLL' : 'DTS-HD MA','DTS XLL X' : 'DTS X','MLP FBA' : 'TrueHD','MLP FBA 16 ch' : 'TrueHD Atmos']; def audioStreams = []; def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }; def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }; def oneStream = { it.collect{ filter(it) }*.minus(null).unique().flatten().join(' ') }; def dString = { it.toDouble().toString() }; def toInt = { it.toInteger() }; any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }); def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}; def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }; def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ); def combined = allOf{codec}{format_profile}.join(' '); audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ]; return audioStreams}; def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort(); def allStreams = audioStreams.collect{ filter(it) }*.minus(null).unique()*.join(' '); def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }); def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }); def defaultStream = oneStream(audioStreams.findAll{ it.default == true }); def bestPreferredLang = any{ audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }*.minus(null).unique().get(0).join(' ') }{}; any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}}{'NO_AUDIO'}}.join(' ') + ']' + {'-' + group}}.tail}
Re: Multiple audio tracks with different codecs and languages
the only thing is choice one and maybe one more as backup
Code: Select all
{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}Re: Multiple audio tracks with different codecs and languages
I'm trying to include this complex piece of code but have some movies not matching (NO_AUDIO).
How can I troubleshoot this ?
Here are some exemples with "mediainfo -f" results of audio parts.
Exemple 1
Code: Select all
Audio #1
Count : 294
Count of stream of this kind : 3
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 0
Stream identifier : 1
StreamOrder : 1
ID : 2
ID : 2
Unique ID : 2
Format : AC-3
Format : AC-3
Format/Info : Audio Coding 3
Format/Url : https://en.wikipedia.org/wiki/AC3
Commercial name : Dolby Digital
Commercial name : Dolby Digital
Format settings, Endianness : Big
Codec ID : A_AC3
Duration : 6054117
Duration : 1 h 40 min
Duration : 1 h 40 min 54 s 117 ms
Duration : 1 h 40 min
Duration : 01:40:54.117
Duration : 01:40:54.117
Bit rate mode : CBR
Bit rate mode : Constant
Bit rate : 384000
Bit rate : 384 kb/s
Channel(s) : 6
Channel(s) : 6 channels
Channel positions : Front: L C R, Side: L R, LFE
Channel positions : 3/2/0.1
Channel layout : L R C LFE Ls Rs
Samples per frame : 1536
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 290597616
Frame rate : 31.250
Frame rate : 31.250 FPS (1536 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 78
Delay : 78 ms
Delay : 78 ms
Delay : 78 ms
Delay : 00:00:00.078
Delay, origin : Container
Delay, origin : Container
Delay relative to video : -5
Delay relative to video : -5 ms
Delay relative to video : -5 ms
Delay relative to video : -5 ms
Delay relative to video : -00:00:00.005
Stream size : 290597616
Stream size : 277 MiB (12%)
Stream size : 277 MiB
Stream size : 277 MiB
Stream size : 277 MiB
Stream size : 277.1 MiB
Stream size : 277 MiB (12%)
Proportion of this stream : 0.12196
Title : VFF AC3 5.1
Language : fr
Language : French
Language : French
Language : fr
Language : fra
Language : fr
Service kind : CM
Service kind : Complete Main
Default : Yes
Default : Yes
Forced : No
Forced : No
bsid : 8
Dialog Normalization : -31
Dialog Normalization : -31 dB
acmod : 7
lfeon : 1
dialnorm_Average : -31
dialnorm_Average : -31 dB
dialnorm_Minimum : -31
dialnorm_Minimum : -31 dB
dialnorm_Maximum : -31
dialnorm_Maximum : -31 dB
dialnorm_Count : 1002
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Audio #2
Count : 294
Count of stream of this kind : 3
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 1
Stream identifier : 2
StreamOrder : 2
ID : 3
ID : 3
Unique ID : 3
Format : AC-3
Format : AC-3
Format/Info : Audio Coding 3
Format/Url : https://en.wikipedia.org/wiki/AC3
Commercial name : Dolby Digital
Commercial name : Dolby Digital
Format settings, Endianness : Big
Codec ID : A_AC3
Duration : 6054117
Duration : 1 h 40 min
Duration : 1 h 40 min 54 s 117 ms
Duration : 1 h 40 min
Duration : 01:40:54.117
Duration : 01:40:54.117
Bit rate mode : CBR
Bit rate mode : Constant
Bit rate : 384000
Bit rate : 384 kb/s
Channel(s) : 6
Channel(s) : 6 channels
Channel positions : Front: L C R, Side: L R, LFE
Channel positions : 3/2/0.1
Channel layout : L R C LFE Ls Rs
Samples per frame : 1536
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 290597616
Frame rate : 31.250
Frame rate : 31.250 FPS (1536 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 78
Delay : 78 ms
Delay : 78 ms
Delay : 78 ms
Delay : 00:00:00.078
Delay, origin : Container
Delay, origin : Container
Delay relative to video : -5
Delay relative to video : -5 ms
Delay relative to video : -5 ms
Delay relative to video : -5 ms
Delay relative to video : -00:00:00.005
Stream size : 290597616
Stream size : 277 MiB (12%)
Stream size : 277 MiB
Stream size : 277 MiB
Stream size : 277 MiB
Stream size : 277.1 MiB
Stream size : 277 MiB (12%)
Proportion of this stream : 0.12196
Title : Anglais AC3 5.1
Language : en
Language : English
Language : English
Language : en
Language : eng
Language : en
Service kind : CM
Service kind : Complete Main
Default : No
Default : No
Forced : No
Forced : No
bsid : 8
Dialog Normalization : -31
Dialog Normalization : -31 dB
acmod : 7
lfeon : 1
dialnorm_Average : -31
dialnorm_Average : -31 dB
dialnorm_Minimum : -31
dialnorm_Minimum : -31 dB
dialnorm_Maximum : -31
dialnorm_Maximum : -31 dB
dialnorm_Count : 1002
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Audio #3
Count : 282
Count of stream of this kind : 3
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 2
Stream identifier : 3
StreamOrder : 3
ID : 4
ID : 4
Unique ID : 4
Format : AAC
Format : AAC LC
Format/Info : Advanced Audio Codec Low Complexity
Commercial name : AAC
Format_AdditionalFeatures : LC
Codec ID : A_AAC-2
Duration : 6054117
Duration : 1 h 40 min
Duration : 1 h 40 min 54 s 117 ms
Duration : 1 h 40 min
Duration : 01:40:54.117
Duration : 01:40:54.117
Channel(s) : 2
Channel(s) : 2 channels
Channel positions : Front: L R
Channel positions : 2/0/0
Channel layout : L R
Samples per frame : 1024
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 290597616
Frame rate : 46.875
Frame rate : 46.875 FPS (1024 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 41
Delay : 41 ms
Delay : 41 ms
Delay : 41 ms
Delay : 00:00:00.041
Delay, origin : Container
Delay, origin : Container
Delay relative to video : -42
Delay relative to video : -42 ms
Delay relative to video : -42 ms
Delay relative to video : -42 ms
Delay relative to video : -00:00:00.042
Title : VFF AAC 2.0
Language : fr
Language : French
Language : French
Language : fr
Language : fra
Language : fr
Default : No
Default : No
Forced : No
Forced : No
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Code: Select all
Audio #1
Count : 282
Count of stream of this kind : 2
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 0
Stream identifier : 1
StreamOrder : 1
ID : 2
ID : 2
Unique ID : 17475167537096227729
Format : AAC
Format : AAC LC
Format/Info : Advanced Audio Codec Low Complexity
Commercial name : AAC
Format_AdditionalFeatures : LC
Codec ID : A_AAC-2
Duration : 5115584
Duration : 1 h 25 min
Duration : 1 h 25 min 15 s 584 ms
Duration : 1 h 25 min
Duration : 01:25:15.584
Duration : 01:25:15.584
Channel(s) : 2
Channel(s) : 2 channels
Channel positions : Front: L R
Channel positions : 2/0/0
Channel layout : L R
Samples per frame : 1024
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 245548032
Frame rate : 46.875
Frame rate : 46.875 FPS (1024 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 0
Delay : 00:00:00.000
Delay, origin : Container
Delay, origin : Container
Delay relative to video : -83
Delay relative to video : -83 ms
Delay relative to video : -83 ms
Delay relative to video : -83 ms
Delay relative to video : -00:00:00.083
Title : VFF AAC 2.0
Language : fr
Language : French
Language : French
Language : fr
Language : fra
Language : fr
Default : Yes
Default : Yes
Forced : No
Forced : No
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Audio #2
Count : 294
Count of stream of this kind : 2
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 1
Stream identifier : 2
StreamOrder : 2
ID : 3
ID : 3
Unique ID : 3
Format : AC-3
Format : AC-3
Format/Info : Audio Coding 3
Format/Url : https://en.wikipedia.org/wiki/AC3
Commercial name : Dolby Digital
Commercial name : Dolby Digital
Format settings, Endianness : Big
Codec ID : A_AC3
Duration : 5115584
Duration : 1 h 25 min
Duration : 1 h 25 min 15 s 584 ms
Duration : 1 h 25 min
Duration : 01:25:15.584
Duration : 01:25:15.584
Bit rate mode : CBR
Bit rate mode : Constant
Bit rate : 384000
Bit rate : 384 kb/s
Channel(s) : 6
Channel(s) : 6 channels
Channel positions : Front: L C R, Side: L R, LFE
Channel positions : 3/2/0.1
Channel layout : L R C LFE Ls Rs
Samples per frame : 1536
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 245548032
Frame rate : 31.250
Frame rate : 31.250 FPS (1536 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 78
Delay : 78 ms
Delay : 78 ms
Delay : 78 ms
Delay : 00:00:00.078
Delay, origin : Container
Delay, origin : Container
Delay relative to video : -5
Delay relative to video : -5 ms
Delay relative to video : -5 ms
Delay relative to video : -5 ms
Delay relative to video : -00:00:00.005
Stream size : 245548032
Stream size : 234 MiB (14%)
Stream size : 234 MiB
Stream size : 234 MiB
Stream size : 234 MiB
Stream size : 234.2 MiB
Stream size : 234 MiB (14%)
Proportion of this stream : 0.13658
Title : Anglais AC3 5.1
Language : en
Language : English
Language : English
Language : en
Language : eng
Language : en
Service kind : CM
Service kind : Complete Main
Default : No
Default : No
Forced : No
Forced : No
bsid : 8
Dialog Normalization : -31
Dialog Normalization : -31 dB
acmod : 7
lfeon : 1
dialnorm_Average : -31
dialnorm_Average : -31 dB
dialnorm_Minimum : -31
dialnorm_Minimum : -31 dB
dialnorm_Maximum : -31
dialnorm_Maximum : -31 dB
dialnorm_Count : 1504
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Code: Select all
Audio #1
Count : 282
Count of stream of this kind : 6
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 0
Stream identifier : 1
StreamOrder : 1
ID : 2
ID : 2
Unique ID : 1810417575
Format : DTS
Format : DTS
Format/Info : Digital Theater Systems
Format/Url : https://en.wikipedia.org/wiki/DTS_(sound_system)
Commercial name : DTS
Mode : 16
Format settings, Endianness : Big
Codec ID : A_DTS
Duration : 7501909
Duration : 2 h 5 min
Duration : 2 h 5 min 1 s 909 ms
Duration : 2 h 5 min
Duration : 02:05:01.909
Duration : 02:05:01.909
Bit rate mode : CBR
Bit rate mode : Constant
Bit rate : 754500
Bit rate : 754 kb/s
Channel(s) : 6
Channel(s) : 6 channels
Channel positions : Front: L C R, Side: L R, LFE
Channel positions : 3/2/0.1
Channel layout : C L R Ls Rs LFE
Samples per frame : 512
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 360091632
Frame rate : 93.750
Frame rate : 93.750 FPS (512 SPF)
Bit depth : 24
Bit depth : 24 bits
Compression mode : Lossy
Compression mode : Lossy
Delay : 0
Delay : 00:00:00.000
Delay, origin : Container
Delay, origin : Container
Delay relative to video : 0
Delay relative to video : 00:00:00.000
Stream size : 707523792
Stream size : 675 MiB (9%)
Stream size : 675 MiB
Stream size : 675 MiB
Stream size : 675 MiB
Stream size : 674.7 MiB
Stream size : 675 MiB (9%)
Proportion of this stream : 0.09197
Language : en
Language : English
Language : English
Language : en
Language : eng
Language : en
Default : No
Default : No
Forced : No
Forced : No
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Audio #2
Count : 282
Count of stream of this kind : 6
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 1
Stream identifier : 2
StreamOrder : 2
ID : 3
ID : 3
Unique ID : 4936
Format : AAC
Format : AAC LC
Format/Info : Advanced Audio Codec Low Complexity
Commercial name : AAC
Format_AdditionalFeatures : LC
Codec ID : A_AAC-2
Duration : 7501909
Duration : 2 h 5 min
Duration : 2 h 5 min 1 s 909 ms
Duration : 2 h 5 min
Duration : 02:05:01.909
Duration : 02:05:01.909
Channel(s) : 2
Channel(s) : 2 channels
Channel positions : Front: L R
Channel positions : 2/0/0
Channel layout : L R
Samples per frame : 1024
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 360091632
Frame rate : 46.875
Frame rate : 46.875 FPS (1024 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 0
Delay : 00:00:00.000
Delay, origin : Container
Delay, origin : Container
Delay relative to video : 0
Delay relative to video : 00:00:00.000
Language : fr
Language : French
Language : French
Language : fr
Language : fra
Language : fr
Default : No
Default : No
Forced : No
Forced : No
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Audio #3
Count : 282
Count of stream of this kind : 6
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 2
Stream identifier : 3
StreamOrder : 3
ID : 4
ID : 4
Unique ID : 15050
Format : AAC
Format : AAC LC
Format/Info : Advanced Audio Codec Low Complexity
Commercial name : AAC
Format_AdditionalFeatures : LC
Codec ID : A_AAC-2
Duration : 7501909
Duration : 2 h 5 min
Duration : 2 h 5 min 1 s 909 ms
Duration : 2 h 5 min
Duration : 02:05:01.909
Duration : 02:05:01.909
Channel(s) : 2
Channel(s) : 2 channels
Channel positions : Front: L R
Channel positions : 2/0/0
Channel layout : L R
Samples per frame : 1024
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 360091632
Frame rate : 46.875
Frame rate : 46.875 FPS (1024 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 0
Delay : 00:00:00.000
Delay, origin : Container
Delay, origin : Container
Delay relative to video : 0
Delay relative to video : 00:00:00.000
Language : pt
Language : Portuguese
Language : Portuguese
Language : pt
Language : por
Language : pt
Default : No
Default : No
Forced : No
Forced : No
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Audio #4
Count : 282
Count of stream of this kind : 6
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 3
Stream identifier : 4
StreamOrder : 4
ID : 5
ID : 5
Unique ID : 25636
Format : AAC
Format : AAC LC
Format/Info : Advanced Audio Codec Low Complexity
Commercial name : AAC
Format_AdditionalFeatures : LC
Codec ID : A_AAC-2
Duration : 7501909
Duration : 2 h 5 min
Duration : 2 h 5 min 1 s 909 ms
Duration : 2 h 5 min
Duration : 02:05:01.909
Duration : 02:05:01.909
Channel(s) : 2
Channel(s) : 2 channels
Channel positions : Front: L R
Channel positions : 2/0/0
Channel layout : L R
Samples per frame : 1024
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 360091632
Frame rate : 46.875
Frame rate : 46.875 FPS (1024 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 0
Delay : 00:00:00.000
Delay, origin : Container
Delay, origin : Container
Delay relative to video : 0
Delay relative to video : 00:00:00.000
Language : ru
Language : Russian
Language : Russian
Language : ru
Language : rus
Language : ru
Default : No
Default : No
Forced : No
Forced : No
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Audio #5
Count : 282
Count of stream of this kind : 6
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 4
Stream identifier : 5
StreamOrder : 5
ID : 6
ID : 6
Unique ID : 384
Format : AAC
Format : AAC LC
Format/Info : Advanced Audio Codec Low Complexity
Commercial name : AAC
Format_AdditionalFeatures : LC
Codec ID : A_AAC-2
Duration : 7501909
Duration : 2 h 5 min
Duration : 2 h 5 min 1 s 909 ms
Duration : 2 h 5 min
Duration : 02:05:01.909
Duration : 02:05:01.909
Channel(s) : 2
Channel(s) : 2 channels
Channel positions : Front: L R
Channel positions : 2/0/0
Channel layout : L R
Samples per frame : 1024
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 360091632
Frame rate : 46.875
Frame rate : 46.875 FPS (1024 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 0
Delay : 00:00:00.000
Delay, origin : Container
Delay, origin : Container
Delay relative to video : 0
Delay relative to video : 00:00:00.000
Language : es
Language : Spanish
Language : Spanish
Language : es
Language : spa
Language : es
Default : No
Default : No
Forced : No
Forced : No
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Audio #6
Count : 282
Count of stream of this kind : 6
Kind of stream : Audio
Kind of stream : Audio
Stream identifier : 5
Stream identifier : 6
StreamOrder : 6
ID : 7
ID : 7
Unique ID : 9511
Format : AAC
Format : AAC LC
Format/Info : Advanced Audio Codec Low Complexity
Commercial name : AAC
Format_AdditionalFeatures : LC
Codec ID : A_AAC-2
Duration : 7501909
Duration : 2 h 5 min
Duration : 2 h 5 min 1 s 909 ms
Duration : 2 h 5 min
Duration : 02:05:01.909
Duration : 02:05:01.909
Channel(s) : 2
Channel(s) : 2 channels
Channel positions : Front: L R
Channel positions : 2/0/0
Channel layout : L R
Samples per frame : 1024
Sampling rate : 48000
Sampling rate : 48.0 kHz
Samples count : 360091632
Frame rate : 46.875
Frame rate : 46.875 FPS (1024 SPF)
Compression mode : Lossy
Compression mode : Lossy
Delay : 0
Delay : 00:00:00.000
Delay, origin : Container
Delay, origin : Container
Delay relative to video : 0
Delay relative to video : 00:00:00.000
Language : uk
Language : Ukrainian
Language : Ukrainian
Language : uk
Language : ukr
Language : uk
Default : No
Default : No
Forced : No
Forced : No
SamplingCount_Source : General_Duration
Duration_Source : General_Duration
Code: Select all
'objects' : any{ '[' + au['NumberOfDynamicObjects'] + ' Objs]' }{null}I don't understand the "AddToList" option ? What is it made for and how to use it.
Lastly, idealy, I would like to rename my file with this result :
Best French language available & Best quality if different ; So 2 results max but could be 1 if all the same (unique). Something like :
[AC3 5.1 Fra] // with hidden [AAC 2.0 Fra & AC3 5.1 Eng] (not better qualities)
[AC3 5.1 Fra & DTS-ES 7.0 Eng] ; Because Eng version is better
I tried this without success : [bestPreferredLang, bestBitRate].unique().join(' & ')
It shows more than 2 results for a lot of movies.
Thanks
Re: Multiple audio tracks with different codecs and languages
Code: Select all
{
def preferredLang = 'Fra'
def useChFilter = false
def filter = { [it.codec, it.ch, it.objects, it.lang].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/\p{Punct}/, ' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().sum() }
{ channelClean(au.ChannelLayout_Original).split().collect{ it == 'LFE' ? 0.1 : 1 }.sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ au.FrameRate.toDouble() }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString3'.upperInitial() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams.findAll{ !it.default }) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}e.g. replace
Code: Select all
{bestPreferredLang}Code: Select all
{[bestPreferredLang, bestBitRate].findAll().unique().join(' & ')}Re: Multiple audio tracks with different codecs and languages
I tried to change the last line :
Code: Select all
any{addToList}{bestPreferredLang}{defaultStream}{bestBitRate}{preferredStream}Code: Select all
{[bestPreferredLang, bestBitRate].findAll().unique().join(' & ')}But doing like this, as you told, is working :
Code: Select all
any{addToList}{[bestPreferredLang, bestBitRate].findAll().unique().join(' & ')}{defaultStream}{bestBitRate}{preferredStream}If I take attributes "one by one", like only "{bestBitRate}", I can't get it to work (it gives me a result but not the "best bitrate").
I'm surely missing something like dealing with lists & findAll...
Also, if I remove the last line, as if I don't want to get anything, except "NO_AUDIO", I still get a result... I don't know from where : The "return audiostream" ?! The "any{audio.collect..." ?!
Well, the good thing is that it seems to be working. The bad thing is that I don't understand... but the problem is... me
Re: Multiple audio tracks with different codecs and languages
What is the "best" and "NOT the Best" ?(it gives me a result but not the "best bitrate").
output with ?:
Code: Select all
audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }same result... to confirm remove unique partI only get 1 result even if there should be 2 independent results for some movies
e.g. "bestBitRate" is from line
Code: Select all
def bestBitRateFAIL =
Code: Select all
{[bestPreferredLang, bestBitRate]...}Code: Select all
[bestPreferredLang, bestBitRate]...Re: Multiple audio tracks with different codecs and languages
from
Code: Select all
{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }Code: Select all
{ toInt(au.BitRate_Maximum) }{ toInt(au.BitRate) }I'm not sure what is the "Best" ?
Re: Multiple audio tracks with different codecs and languages
My bad. I was just using "{}" and the results was not as expected... and I'm not dealing with limit cases where order can be discussed.
Exemple when comparing "FR AC3@384kb/s" vs "EN EAC3@1024kb/s" :

Strange result when using {bestBitRate}, the reason I was not understanding what was "BestBitRate" (how AC3@384 can be considered better than EAC3@1024) :

The fact is that {} shouldn't be there...
I just want to notice here that the result is the same if you remove everything (you choose nothing and you have a result...) :

I still don't understand why... and it didn't help me to troubleshoot myself (now we know the problem is myself
Perhaps there's a way to return null if no option is chosen at the end (and so switch to "no_audio") ? Well, it's not really important.
And the working test I was looking for, removing the {} (not sure it's the "good way" to do for someone wanting only the higher bitrate as result) :

To talk about "what is the best", I totally agree that it's not that simple to order things, because it could be sorted by :
- Codec from the codecList (but hard to organize) : MP2 < MP3 < PCM < AAC < AC3 < EAC3 < ... But which one is better from AAC/AC3 or TrueHD Atmos/DTS-X or ... and what between AC3 5.1 & EAC3 2.0 (don't know if it exists but you get the idea...)
- Bitrate (what is done actually). Must be the most relevant even if, in some rare cases, bitrate can be lower but quality higher due to better compression of the algorithm...
- Max bitrate, thanks for the idea... but average must be better
- Number of channels : 2.0 < 5.1 < 7.1 < 9.1
Now, it's working as expected with this (even if I don't know what is "addToList" which can be preferred by the "any" because it's at the front :
Code: Select all
any{addToList}{[bestPreferredLang, bestBitRate].findAll().unique().join(' & ')}{defaultStream}{bestBitRate}{preferredStream}Code: Select all
[bestPreferredLang, bestBitRate].findAll().unique().join(' & ')- For a movie, like above, with AC3@384 FR & EAC3@1024 EN => Show both (best FR then best "other/foreign").
- For a movie with AC3@384 FR & DTS@1500 FR & AC3@384 EN => Show only DTS FR (the best of all available) ;
Is it sure that "bestPreferredLang" will always take DTS@1500 FR over AC3@384 FR ? I mean select the highest bitrate of all FR, because it's only selecting against "preferedLang", not best bitrate. I guess it's because listStream is sorted by bitrate... (but if so, why not only taking the first of the list for bestBitRate ? ; bestBitRate = listStream(it)[0]) - For a movie with AC3@350 FR & AC3@340 EN => Show only AC3 FR (best of the best)
- For a movie with AC3@340 FR & AC3@350 EN => I would prefer to only display FR, but it's not working this way now (350 > 340...). Would it be possible/easy to display the alternative audio (other that French) IF bitrate is higher (already done) but also codec is different ?
bestPreferredLang.codec <> bestBitRate.codec ? Show both : show bestPreferredLang only.
Just not sure we can play with bestPreferredLang & bestBitRate like this because they are Strings as I understand... probably needed to take it from audioStreams like :
def betterForeign = ?!? I could probably try things but it will not probably be ideal...
seems working :
Code: Select all
audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }.codec == audioStreams.findAll{ it.lang == preferredLang }.codec ? bestPreferredLang:[bestPreferredLang, bestBitRate].join(' & ')Re: Multiple audio tracks with different codecs and languages
Because
"audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max()" can contain multiple results (if differents bitrates are equals)
And also because "audioStreams.findAll{ it.lang == preferredLang }" can contain multiple results and, not yet sorted by bitrate (to have the bestPreferrendLang...)
So comparing both may result of strange things ^^
The solution could be to :
1) construct the array : audioStreams << ... as it is actually
2) Sort the array by bitrate (but keep it as an array), to replace the all in one "listStream" who sort and convert as String list... loosing the ability to do things...
3) Next, we can use this sorted array to find things... The 1st in the array, without any filter, is bestBitRate ; The 1st in the array, with lang filter, is bestPreferredLang ; The first with default ... and so on.
What do you think ?
Re: Multiple audio tracks with different codecs and languages
Try it out:
Code: Select all
audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() || it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.sort{ it.lang != preferredLang }.unique{ it.lang }.unique{ it.bitrate }.collect{ filter(it) }*.join(' ').join(' & ')Code: Select all
audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() || it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate ?: preferredLang <=> b.lang}.unique{ it.lang }.unique{ it.bitrate }.collect{ filter(it) }*.join(' ').join(' & ')maybe you can reduce the unique's and sort's ?
do rednoah have any advanced info ?
Re: Multiple audio tracks with different codecs and languages
Looking at the lines, it's effectively not easy
Also, unfortunately, it's not working.
With 1st expression
Code: Select all
audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() || it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.sort{ it.lang != preferredLang }.unique{ it.lang }.unique{ it.bitrate }.collect{ filter(it) }*.join(' ').join(' & ')
OK on lines 1,2,3
KO on lines 4,5,6
With 2nd expression :
Code: Select all
audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() || it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate ?: preferredLang <=> b.lang}.unique{ it.lang }.unique{ it.bitrate }.collect{ filter(it) }*.join(' ').join(' & ')
OK on lines 1,2,3 (but I'd prefer FR first as previously)
KO on lines 4,5,6
Re: Multiple audio tracks with different codecs and languages
Code: Select all
( audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate } 999 audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }.sort{ a, b -> b.default <=> a.default }.unique{ it.bitrate } ).unique{ it.lang }.collect{ filter(it) }*.join(' ').join(' 777 ')999 to +
777 to &
@rednoah
PS: I'm super annoyed at the "WAF"
Re: Multiple audio tracks with different codecs and languages
Me too... Might need to move custom format talk to the Discord channel.
EDIT:
Created a dedicated channel for this kind of discussion:
https://discord.com/channels/2287231828 ... 5177269290
Discord will sharing code and upload screenshots easier, plus it allows for real-time messaging, so it should make the experience better in this regard as well.
Re: Multiple audio tracks with different codecs and languages
Code: Select all
( audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate } + audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }.sort{ a, b -> b.default <=> a.default }.unique{ it.bitrate } ).unique{ it.lang }.collect{ filter(it) }*.join(' ').join(' 777 ')
Thanks
Re: Multiple audio tracks with different codecs and languages
Code: Select all
.unique{ it.codec }Code: Select all
.unique{ it.lang }.unique{ it.codec }.collect{ filter(it) }*.join(' ').join(' & ')Re: Multiple audio tracks with different codecs and languages
As you told, I'm all set with this :
Code: Select all
audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() || it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.sort{ it.lang != preferredLang }.unique{ it.lang }.unique{ it.codec }.collect{ filter(it) }*.join(' ').join(' & ')Code: Select all
def preferredLang = 'FR'
def useChFilter = false
//def filter = { [it.codec, it.ch, it.objects, it.lang].findAll() }
def filter = { [it.codec, it.ch, it.lang].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ au.FrameRate.toDouble() }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() || it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate }.sort{ it.lang != preferredLang }.unique{ it.lang }.unique{ it.codec }.collect{ filter(it) }*.join(' ').join(' & ')
}{'NO_AUDIO'}- the useChFilter parts (cleaning a little bit the output, removing the nb of channels if it's "standard" values)
- I still didn't get the goal of "return audioStreams" & "Add To" parts, so keep it ^^
Thanks a lot to Kim for his time ^^
Re: Multiple audio tracks with different codecs and languages
This is the final result:
Code: Select all
{
def preferredLang = 'FR'
def filter = { [it.codec, it.ch, it.lang].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/\p{Punct}/, ' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().sum() }
{ channelClean(au.ChannelLayout_Original).split().collect{ it == 'LFE' ? 0.1 : 1 }.sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add "' + combined + '" to codecList'), 'combined' : combined, 'ch' : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ au.FrameRate.toDouble() }{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
}
def addToList = audioStreams.codec.findAll{ it.contains('to codecList') }.unique().sort()
any{addToList}{( audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate } + audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }.sort{ a, b -> b.default <=> a.default }.unique{ it.bitrate } ).unique{ it.lang }.unique{ it.codec }.collect{ filter(it) }*.join(' ').join(' & ')}
}{'NO_AUDIO'}
}Re: Multiple audio tracks with different codecs and languages
Hello, I would like you to add all the audios, but only the ones with the highest rate, that is, imagine that I have a video with these audios:kim wrote: 08 Feb 2021, 04:28 e.g. addToList = [Add "AC 3" to codecList, Add "DTS XLL" to codecList, Add "DTS" to codecList]
This is the final result:Code: Select all
{ def preferredLang = 'FR' def filter = { [it.codec, it.ch, it.lang].findAll() } def codecList = [ 'MPEG Audio' : 'MP2', 'MP3' : 'MP3', 'PCM' : 'PCM', 'FLAC' : 'FLAC', 'AAC LC' : 'AAC', 'AAC LC SBR' : 'AAC', 'AAC LC SBR PS' : 'AAC', 'AC 3' : 'AC3', 'AC 3 Dep' : 'EAC3', 'E AC 3' : 'EAC3', 'E AC 3 JOC' : 'EAC3 Atmos', 'AC 3 Dep JOC' : 'EAC3 Atmos', 'DTS' : 'DTS', 'DTS 96 24' : 'DTS 96-24', 'DTS ES' : 'DTS-ES', 'DTS ES XXCH' : 'DTS-ES', 'DTS XBR' : 'DTS-HD HRA', 'DTS ES XBR' : 'DTS-HD HRA', 'DTS ES XXCH XBR' : 'DTS-HD HRA', 'DTS XLL' : 'DTS-HD MA', 'DTS ES XLL' : 'DTS-HD MA', 'DTS ES XXCH XLL' : 'DTS-HD MA', 'DTS XLL X' : 'DTS X', 'MLP FBA' : 'TrueHD', 'MLP FBA 16 ch' : 'TrueHD Atmos' ] def audioStreams = [] def audioClean = { it.replaceAll(/\p{Punct}/, ' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') } def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') } def oneStream = { listStream(it)[0] } def dString = { it.toDouble().toString() } def toInt = { it.toInteger() } any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{} def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().sum() } { channelClean(au.ChannelLayout_Original).split().collect{ it == 'LFE' ? 0.1 : 1 }.sum() } { channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) } def combined = allOf{codec}{format_profile}.join(' ') audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'codec' : codecList.get(combined, 'Add "' + combined + '" to codecList'), 'combined' : combined, 'ch' : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ au.FrameRate.toDouble() }{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ] } def addToList = audioStreams.codec.findAll{ it.contains('to codecList') }.unique().sort() any{addToList}{( audioStreams.findAll{ it.lang == preferredLang }.sort{ a, b -> b.bitrate <=> a.bitrate } + audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }.sort{ a, b -> b.default <=> a.default }.unique{ it.bitrate } ).unique{ it.lang }.unique{ it.codec }.collect{ filter(it) }*.join(' ').join(' & ')} }{'NO_AUDIO'} }
Spanish AC3 5.1
Spanish DTS-HD 5.1
English AC3 5.1
English TrueHD 7.1
let it be like this:
[ES DTS-HD 5.1 - EN TrueHD 7.1]
I would only add the best audios of each language.
Is it possible to do this? What would I have to modify in the code?
Thank you.
Re: Multiple audio tracks with different codecs and languages
Code: Select all
{
def preferredLang = 'FR'
def useChFilter = false
def filter = { [it.lang, it.codec, it.ch, it.objects].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')
any{addToList}{bestBitRateAllLang}{[bestPreferredLang, bestBitRate].findAll().join(' & ')}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}EN DTS-HD MA 7.1 - FR AC3 5.1 - IT AC3 5.1 - ES AC3 5.1 - NL AC3 5.1 - CA AC3 5.1
EDIT:
if you really want with the [...]
you can replace
Code: Select all
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')Code: Select all
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).toString().split(', ').join(' - ')Code: Select all
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).joining(' - ', ' [', ']')[EN DTS-HD MA 7.1 - FR AC3 5.1 - IT AC3 5.1 - ES AC3 5.1 - NL AC3 5.1 - CA AC3 5.1]
Re: Multiple audio tracks with different codecs and languages
I would like to ask if there is an easy way to adapt it to group audio languages by audio codec and channel.
I think this would be interesting to shorten file names but keeping all the info.
Sample of how current code works:
The desired result:EN EAC Atmos 5.1 - ES EAC3 5.1 - FR EAC3 5.1 - EN EAC3 5.1
Another feature I find interesting would be to be able to choose a 'default' audio language in the same way you allow to define the preferreded language.EN EAC Atmos 5.1 - ES|FR|EN EAC3 5.1
This way, you could group in a folder all those movies or episodes ripped from VHS or DVB, that maybe don't contain language info but that you know they are in English or Spanish, and rename them all at the same time, knowing that if any of them do not have language information, the file will be renamed using the default desired language.
Sample of how current code works:
The desired result:MP3 2.0
Although this could be manually achieved by changing 'null' in this line for 'EN' or any other language string.EN MP3 2.0
Code: Select all
'lang' : any{ au.'LanguageString3'.upper() }{null} ]Thank you again!
Re: Multiple audio tracks with different codecs and languages
sample:
warning you cant use pipes in filename on windows... change "pipes" inEN DTS-HD MA 7.1 - EN TrueHD Atmos 7.1 - EN DTS 5.1 - EN|FR|IT|ES|NL|CA AC3 5.1
Code: Select all
it.unique{it.lang}.lang.join('|')Code: Select all
{
def preferredLang = 'FR'
def useChFilter = false
def filter = { [it.lang, it.codec, it.ch, it.objects].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')
def groupCodec = audioStreams.sort{ a, b -> b.bitrate <=> a.bitrate }.groupBy{ it.codec }.values().collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.collect{ "${it.lang.replace('null', 'EN')} ${it.codec} ${it.ch}" }.join(' - ')
any{addToList}{groupCodec}{bestBitRateAllLang}{[bestPreferredLang, bestBitRate].findAll().join(' & ')}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}Code: Select all
it.lang.replace('null', 'EN')Or maybe more like this ?
Code: Select all
{
def preferredLang = 'FR'
def fallbackLang = any{languages*.ISO2*.upper().join('-')}{'YourDefaultLandHere'}
def useChFilter = false
def filter = { [it.lang, it.codec, it.ch, it.objects].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')
def groupCodec = audioStreams.sort{ a, b -> b.bitrate <=> a.bitrate }.groupBy{ it.codec }.values().collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.collect{ "${it.lang.replace('null', fallbackLang)} ${it.codec} ${it.ch}" }.join(' - ')
any{addToList}{groupCodec}{bestBitRateAllLang}{[bestPreferredLang, bestBitRate].findAll().join(' & ')}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}Re: Multiple audio tracks with different codecs and languages
Thank you so much
Re: Multiple audio tracks with different codecs and languages
Meaning, which format, I don't believe MacOS version exports to anything else besides text, and a slew of XML formats.
By the way, the Dolby files are freely available on their website, in addition to those there are a few examples on https://streams.videolan.org/samples/ for all tastes.
I believe the most consistent across version is CodecID, which, however, isn't particularly pleasing (it usually has A_<codecname> and is allcaps)
Also which kind of output should I aim for to keep compatibility?
Re: Multiple audio tracks with different codecs and languages
viewtopic.php?t=4285

Re: Multiple audio tracks with different codecs and languages
I am trying to make your script you posted above go but I would like to make some changes if possible, keeping only the Italian language for the audio codec result.
I would like a result like this :
Code: Select all
Rocketman (2019) (UHD.Dolby.Vision.10.IT.AC3.5.1).mkvCode: Select all
Rocketman (2019) UHD Dolby Vision 10 EN TrueHD Atmos 7.1 - IT AC3 5.1.mkvWould it also be possible to change the name of the directory in this way?
Code: Select all
Rocketman (2019) DV UHD/Rocketman (2019) (UHD.Dolby.Vision.10.IT.AC3.5.1).mkvthe script I am trying to use is the one posted above:
Code: Select all
{plex} {vs} {hd} {hdr} {bitdepth}
{
def preferredLang = ‘IT’
def fallbackLang = any{languages*.ISO2*.upper().join('-')}{'YourDefaultLandHere'}
def useChFilter = false
def filter = { [it.lang, it.codec, it.ch, it.objects].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')
def groupCodec = audioStreams.sort{ a, b -> b.bitrate <=> a.bitrate }.groupBy{ it.codec }.values().collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.collect{ "${it.lang.replace('null', fallbackLang)} ${it.codec} ${it.ch}" }.join(' - ')
any{addToList}{groupCodec}{bestBitRateAllLang}{[bestPreferredLang, bestBitRate].findAll().join(' & ')}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}Re: Multiple audio tracks with different codecs and languages
Code: Select all
{plex.derive{" $hdr"}{" $hd"}.name}/{plex.name}{allOf{vs}{hd}{hdr}{bitdepth}.joining('.', ' (', '.') }
{
def preferredLang = 'IT'
def fallbackLang = any{languages*.ISO2*.upper().join('-')}{'YourDefaultLandHere'}
def useChFilter = false
def filter = { [it.lang, it.codec, it.ch].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')
def groupCodec = audioStreams.sort{ a, b -> b.bitrate <=> a.bitrate }.groupBy{ it.codec }.values().collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.collect{ "${it.lang.replace('null', fallbackLang)} ${it.codec} ${it.ch}" }.join(' - ')
any{addToList}{bestPreferredLang.space('.')}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}+')'
}Re: Multiple audio tracks with different codecs and languages
Thank you very much!
It is just what I was looking for and it works perfectly.
Thanks again!
Re: Multiple audio tracks with different codecs and languages
I see that post to improve my filebot expression, the current code is:
Code: Select all
{drive}/{n.colon(' - ')} ({y}) {" {tmdb-$id}"}/{n.colon(' - ')} ({y}) {any{fn.match(/Director.?s|director|dir(?=\s)|dir(?=\W)/) ? '[Version Director]' : ''} {fn.match(/Extended|extendida|ext(?=\s)|ext(?=\W)/)? '[V. Extendida]' : ''} {fn.match(/Cine|cinema|cinematográfica|cinematografica|cin(?=\s)|cin(?=\W)/)? '[V. Cine]' : ''} {fn.match(/unrated/)? '- UNRATED' : ''}{fn.match(/uncut/)? '- UNCUT' : ''} {'[Ed. ' + fn.match(/(\d+)(?i:th|º)?.(?i:aniversario|Anniversary|anniv(?=\s)|anniv(?=\W) aniv(?=\s)|aniv(?=\W))/) + " aniversario]"} {fn.match(/Criterion/)? '[Ed. Criterion]' : ''} {fn. match (/remastered|remasterizada|remast(?=\s)|remast(?=\W)/) ? '[Remasterizada]' : ''}} {fn.match(/rotulado castellano|Rotulado Castellano|Rotulado Español|rotulado castellano|ROTULADO CASTELLANO/) && vf=~ /1080|720/ ? '[Rotulado Castellano]': ''}{fn.match(/rotulado castellano|Rotulado Castellano|Rotulado Español|rotulado castellano|ROTULADO CASTELLANO/) && vf=~ 2160 ? '[Rotulado Castellano]': ''} [{ fn.match(/A3P/) + ' ' }{ fn.match(/ATVP/) + ' ' }{ fn.match(/ATVP[+]/) + ' ' }{ fn.match(/ATV/) + ' ' }{ fn.match(/AMZN/) + ' ' }{ fn.match(/DSNP[+]/) + ' ' }{ fn.match(/DSN[+]/) + ' ' }{ fn.match(/DSNP/) + ' ' }{ fn.match(/DSNY/) + ' ' }{ fn.match(/Disney[+]/) + ' ' }{ fn.match(/DisneyPlus/) + ' ' }{ fn.match(/STARZ/) + ' ' }{ fn.match(/HBO/) + ' ' }{ fn.match(/HMAX/) + ' ' }{ fn.match(/HULU/) + ' ' }{ fn.match(/M[+]/) + ' ' }{ fn.match(/NF/) + ' ' }{ fn.match(/IMAX/) + ' ' }{ fn.match(/FLMN/) + ' ' }{allOf {any{any{fn.match(/uhdremux|remux/) && vf=~ 2160 ? 'UHDRemux': ''}{if (megabytes>=26500 && bitrate >= 37000000 && vf=~ 2160) 'UHDRemux'}{fn.match(/uhdrip|UHDrip|mUHD|UHDrip|UHD BluRay/) && vf=~ /1080|720|2160/ ? 'UHDRip': ''}{fn.match(/bdremux|BDRemux|BDremux|BluRay Remux|remux/) && vf=~ /1080|720/ ? 'BDRemux': ''}{if (megabytes>= 915769.6 && bitrate >= 917301504 && vf=~ /1080|720/) 'BDRemux'}}{fn.match(/bluray|Bluray|BluRay|BLURAYRIP|BLURAY/) && vf=~ /1080|720/ ? 'BDRip': ''}{fn.match(/bluray|Bluray|BluRay|BLURAYRIP|BLURAY|BDRip|bdrip/) && vf=~ /2160/ ? 'UHDRip': ''}{fn.match(/microuhd|Microuhd|MICROUHD|microUHD|MICROuhd|MHD|micro/) && vf=~ /2160/ ? 'MicroUHD': ''}{any{fn=~ (/m1080|m720|m480|brm|mhd|M1080|M720|M480|BRM|MHD|MicroHD|micro|Micro|MICROHD/) ? /MicroHD/ : null} {vs.match (/BluRay|WEB-DL/) ? {source.replace ('WEBDL', 'WEB-DL') .replace('webdl', 'WEB-DL') .replace('Webdl', 'WEB-DL') .replace('Web-dl', 'WEB-DL') .replace('web-dl', 'WEB-DL') .replace('WEB-dl', 'WEB-DL') .replace ('WEBrip', 'WEBRip') .replace ('webrip', 'WEBRip') .replace ('WEBRIP', 'WEBRip') .replace ('WEB-rip', 'WEBRip') .replace ('web-rip', 'WEBRip') .replace ('WEB-RIP', 'WEBRip') .replace ('WEB-Rip', 'WEBRip') .replace ('BDRIP', 'BDRip').replace ('bdrip', 'BDRip') .replace ('BDrip', 'BDRip') .replace ('Bdrip', 'BDRip') .replace ('bd-rip', 'BDRip') .replace ('BDRIP', 'BDRip') .replace ('BD-rip', 'BDRip') .replace ('BD-Rip', 'BDRip') .replace ('brrip', 'BRRip') .replace ('Brrip', 'BRRip') .replace ('BRrip', 'BRRip') .replace ('br-rip', 'BRRip') .replace ('BR-rip', 'BRRip') .replace ('BR-Rip', 'BRRip')} : ''}{vs}}}{vf}{hdr.replace('Dolby Vision', 'DV').replace ('Dovi','DV') .replace ('DOVI','DV') .replace ('dovi','DV')} {vc.replace('Microsoft', 'VC-1').replace ('x264','AVC') .replace ('x265','HEVC') .replace ('ATEME','HEVC')} {audio[0].formatString =~ (/DTS XBR|AC-3|MLP FBA 16-ch|MLP FBA|DTS XLL|E-AC-3 JOC|DTS ES XLL|DTS ES XXCH/) ? {audio[0].formatString.replace ('DTS XBR', ' DTS-HD HR').replace ('AC-3', 'AC3') .replace ('DTS XLL', 'DTS-HD MA') .replace ('MLP FBA 16-ch', 'TrueHD(Atmos)') .replace ('MLP FBA', 'TrueHD').replace ('DTS ES XLL', 'DTS-HD MA').replace ('DTS XLL X', 'DTS X') .replace ('E-AC-3 JOC', 'EAC3(Atmos)') .replace ('DTS ES XXCH', 'DTS-ES Discrete')}: {ac}} {audio[0].channels.replace('1','Mono').replace('2','2.0').replace('6','5.1').replace('7','6.1').replace('8','7.1').replace('9','8.1').replace('10','9.1')} {audio[1].formatString =~ (/DTS XBR|AC-3|MLP FBA 16-ch|MLP FBA|DTS XLL|E-AC-3 JOC|DTS ES XLL|DTS ES XXCH/) ? {audio[1].formatString.replace ('DTS XBR', ' DTS-HD HR').replace ('AC-3', 'AC3') .replace ('DTS XLL', 'DTS-HD MA') .replace ('MLP FBA 16-ch', 'TrueHD(Atmos)') .replace ('MLP FBA', 'TrueHD').replace ('DTS ES XLL', 'DTS-HD MA').replace ('DTS XLL X', 'DTS X') .replace ('E-AC-3 JOC', 'EAC3(Atmos)') .replace ('DTS ES XXCH', 'DTS-ES Discrete')}: {ac}} {audio[1].channels.replace('1','Mono').replace('2','2.0').replace('6','5.1').replace('7','6.1').replace('8','7.1').replace('9','8.1').replace('10','9.1')} {allOf{audioLanguages}{textLanguages} =~ /spa/ ? null : ' VO'}{textLanguages =~ /spa/ && !(audioLanguages =~ /spa/) ? ' VOSE ' : null}{textLanguages.size() > 0 ? ' Subs' : 'null'}.join(' ')}] {" {tmdb-$id}"}Movie (2022) {tmdb-XXXXXX}/Movie (2022) [UHDRemux 2160p DV HEVC ES DTS-HD MA 7.1 - EN TrueHD Atmos 7.1 Subs] {tmdb-XXXXXX}
Where I choose the best audio in my example language ES and the best audio in the next available language only one option EN/FR...
Example: ES DTS-HD MA 7.1 - EN TrueHD Atmos 7.1
I have tried to adapt the code but I have not had any luck with this example post viewtopic.php?p=55273#p55273 but i want only best audio for ES audio and next audio only one audio language
I just need a little push to get it right, thanks
Re: Multiple audio tracks with different codecs and languages
Hi!çdeejayexe wrote: 03 Nov 2022, 01:29 Hi
I see that post to improve my filebot expression, the current code is:With this code I have verified that the audios only select [0] and [1], but I have seen this post and I would like to be able to adapt it to my code in the following way:Code: Select all
{drive}/{n.colon(' - ')} ({y}) {" {tmdb-$id}"}/{n.colon(' - ')} ({y}) {any{fn.match(/Director.?s|director|dir(?=\s)|dir(?=\W)/) ? '[Version Director]' : ''} {fn.match(/Extended|extendida|ext(?=\s)|ext(?=\W)/)? '[V. Extendida]' : ''} {fn.match(/Cine|cinema|cinematográfica|cinematografica|cin(?=\s)|cin(?=\W)/)? '[V. Cine]' : ''} {fn.match(/unrated/)? '- UNRATED' : ''}{fn.match(/uncut/)? '- UNCUT' : ''} {'[Ed. ' + fn.match(/(\d+)(?i:th|º)?.(?i:aniversario|Anniversary|anniv(?=\s)|anniv(?=\W) aniv(?=\s)|aniv(?=\W))/) + " aniversario]"} {fn.match(/Criterion/)? '[Ed. Criterion]' : ''} {fn. match (/remastered|remasterizada|remast(?=\s)|remast(?=\W)/) ? '[Remasterizada]' : ''}} {fn.match(/rotulado castellano|Rotulado Castellano|Rotulado Español|rotulado castellano|ROTULADO CASTELLANO/) && vf=~ /1080|720/ ? '[Rotulado Castellano]': ''}{fn.match(/rotulado castellano|Rotulado Castellano|Rotulado Español|rotulado castellano|ROTULADO CASTELLANO/) && vf=~ 2160 ? '[Rotulado Castellano]': ''} [{ fn.match(/A3P/) + ' ' }{ fn.match(/ATVP/) + ' ' }{ fn.match(/ATVP[+]/) + ' ' }{ fn.match(/ATV/) + ' ' }{ fn.match(/AMZN/) + ' ' }{ fn.match(/DSNP[+]/) + ' ' }{ fn.match(/DSN[+]/) + ' ' }{ fn.match(/DSNP/) + ' ' }{ fn.match(/DSNY/) + ' ' }{ fn.match(/Disney[+]/) + ' ' }{ fn.match(/DisneyPlus/) + ' ' }{ fn.match(/STARZ/) + ' ' }{ fn.match(/HBO/) + ' ' }{ fn.match(/HMAX/) + ' ' }{ fn.match(/HULU/) + ' ' }{ fn.match(/M[+]/) + ' ' }{ fn.match(/NF/) + ' ' }{ fn.match(/IMAX/) + ' ' }{ fn.match(/FLMN/) + ' ' }{allOf {any{any{fn.match(/uhdremux|remux/) && vf=~ 2160 ? 'UHDRemux': ''}{if (megabytes>=26500 && bitrate >= 37000000 && vf=~ 2160) 'UHDRemux'}{fn.match(/uhdrip|UHDrip|mUHD|UHDrip|UHD BluRay/) && vf=~ /1080|720|2160/ ? 'UHDRip': ''}{fn.match(/bdremux|BDRemux|BDremux|BluRay Remux|remux/) && vf=~ /1080|720/ ? 'BDRemux': ''}{if (megabytes>= 915769.6 && bitrate >= 917301504 && vf=~ /1080|720/) 'BDRemux'}}{fn.match(/bluray|Bluray|BluRay|BLURAYRIP|BLURAY/) && vf=~ /1080|720/ ? 'BDRip': ''}{fn.match(/bluray|Bluray|BluRay|BLURAYRIP|BLURAY|BDRip|bdrip/) && vf=~ /2160/ ? 'UHDRip': ''}{fn.match(/microuhd|Microuhd|MICROUHD|microUHD|MICROuhd|MHD|micro/) && vf=~ /2160/ ? 'MicroUHD': ''}{any{fn=~ (/m1080|m720|m480|brm|mhd|M1080|M720|M480|BRM|MHD|MicroHD|micro|Micro|MICROHD/) ? /MicroHD/ : null} {vs.match (/BluRay|WEB-DL/) ? {source.replace ('WEBDL', 'WEB-DL') .replace('webdl', 'WEB-DL') .replace('Webdl', 'WEB-DL') .replace('Web-dl', 'WEB-DL') .replace('web-dl', 'WEB-DL') .replace('WEB-dl', 'WEB-DL') .replace ('WEBrip', 'WEBRip') .replace ('webrip', 'WEBRip') .replace ('WEBRIP', 'WEBRip') .replace ('WEB-rip', 'WEBRip') .replace ('web-rip', 'WEBRip') .replace ('WEB-RIP', 'WEBRip') .replace ('WEB-Rip', 'WEBRip') .replace ('BDRIP', 'BDRip').replace ('bdrip', 'BDRip') .replace ('BDrip', 'BDRip') .replace ('Bdrip', 'BDRip') .replace ('bd-rip', 'BDRip') .replace ('BDRIP', 'BDRip') .replace ('BD-rip', 'BDRip') .replace ('BD-Rip', 'BDRip') .replace ('brrip', 'BRRip') .replace ('Brrip', 'BRRip') .replace ('BRrip', 'BRRip') .replace ('br-rip', 'BRRip') .replace ('BR-rip', 'BRRip') .replace ('BR-Rip', 'BRRip')} : ''}{vs}}}{vf}{hdr.replace('Dolby Vision', 'DV').replace ('Dovi','DV') .replace ('DOVI','DV') .replace ('dovi','DV')} {vc.replace('Microsoft', 'VC-1').replace ('x264','AVC') .replace ('x265','HEVC') .replace ('ATEME','HEVC')} {audio[0].formatString =~ (/DTS XBR|AC-3|MLP FBA 16-ch|MLP FBA|DTS XLL|E-AC-3 JOC|DTS ES XLL|DTS ES XXCH/) ? {audio[0].formatString.replace ('DTS XBR', ' DTS-HD HR').replace ('AC-3', 'AC3') .replace ('DTS XLL', 'DTS-HD MA') .replace ('MLP FBA 16-ch', 'TrueHD(Atmos)') .replace ('MLP FBA', 'TrueHD').replace ('DTS ES XLL', 'DTS-HD MA').replace ('DTS XLL X', 'DTS X') .replace ('E-AC-3 JOC', 'EAC3(Atmos)') .replace ('DTS ES XXCH', 'DTS-ES Discrete')}: {ac}} {audio[0].channels.replace('1','Mono').replace('2','2.0').replace('6','5.1').replace('7','6.1').replace('8','7.1').replace('9','8.1').replace('10','9.1')} {audio[1].formatString =~ (/DTS XBR|AC-3|MLP FBA 16-ch|MLP FBA|DTS XLL|E-AC-3 JOC|DTS ES XLL|DTS ES XXCH/) ? {audio[1].formatString.replace ('DTS XBR', ' DTS-HD HR').replace ('AC-3', 'AC3') .replace ('DTS XLL', 'DTS-HD MA') .replace ('MLP FBA 16-ch', 'TrueHD(Atmos)') .replace ('MLP FBA', 'TrueHD').replace ('DTS ES XLL', 'DTS-HD MA').replace ('DTS XLL X', 'DTS X') .replace ('E-AC-3 JOC', 'EAC3(Atmos)') .replace ('DTS ES XXCH', 'DTS-ES Discrete')}: {ac}} {audio[1].channels.replace('1','Mono').replace('2','2.0').replace('6','5.1').replace('7','6.1').replace('8','7.1').replace('9','8.1').replace('10','9.1')} {allOf{audioLanguages}{textLanguages} =~ /spa/ ? null : ' VO'}{textLanguages =~ /spa/ && !(audioLanguages =~ /spa/) ? ' VOSE ' : null}{textLanguages.size() > 0 ? ' Subs' : 'null'}.join(' ')}] {" {tmdb-$id}"}
Movie (2022) {tmdb-XXXXXX}/Movie (2022) [UHDRemux 2160p DV HEVC ES DTS-HD MA 7.1 - EN TrueHD Atmos 7.1 Subs] {tmdb-XXXXXX}
Where I choose the best audio in my example language ES and the best audio in the next available language only one option EN/FR...
Example: ES DTS-HD MA 7.1 - EN TrueHD Atmos 7.1
I have tried to adapt the code but I have not had any luck with this example post viewtopic.php?p=55273#p55273 but i want only best audio for ES audio and next audio only one audio language
I just need a little push to get it right, thanks
Maybe @rednoah @kim can help me?
Thanks
Re: Multiple audio tracks with different codecs and languages
ES AC3 5.1 - EN DTS-HD MA 7.1
Code: Select all
{
def preferredLang = 'ES'
def fallbackLang = any{languages*.ISO2*.upper().join('-')}{'YourDefaultLandHere'}
def useChFilter = false
def filter = { [it.lang, it.codec, it.ch].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')
def groupCodec = audioStreams.sort{ a, b -> b.bitrate <=> a.bitrate }.groupBy{ it.codec }.values().collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.collect{ "${it.lang.replace('null', fallbackLang)} ${it.codec} ${it.ch}" }.join(' - ')
any{addToList}{[bestPreferredLang, bestBitRate].unique().findAll().join(' - ')}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}Code: Select all
{audio[0].formatString =~ (/DTS XBR|AC-3|MLP FBA 16-ch|MLP FBA|DTS XLL|E-AC-3 JOC|DTS ES XLL|DTS ES XXCH/) ? {audio[0].formatString.replace ('DTS XBR', ' DTS-HD HR').replace ('AC-3', 'AC3') .replace ('DTS XLL', 'DTS-HD MA') .replace ('MLP FBA 16-ch', 'TrueHD(Atmos)') .replace ('MLP FBA', 'TrueHD').replace ('DTS ES XLL', 'DTS-HD MA').replace ('DTS XLL X', 'DTS X') .replace ('E-AC-3 JOC', 'EAC3(Atmos)') .replace ('DTS ES XXCH', 'DTS-ES Discrete')}: {ac}} {audio[0].channels.replace('1','Mono').replace('2','2.0').replace('6','5.1').replace('7','6.1').replace('8','7.1').replace('9','8.1').replace('10','9.1')} {audio[1].formatString =~ (/DTS XBR|AC-3|MLP FBA 16-ch|MLP FBA|DTS XLL|E-AC-3 JOC|DTS ES XLL|DTS ES XXCH/) ? {audio[1].formatString.replace ('DTS XBR', ' DTS-HD HR').replace ('AC-3', 'AC3') .replace ('DTS XLL', 'DTS-HD MA') .replace ('MLP FBA 16-ch', 'TrueHD(Atmos)') .replace ('MLP FBA', 'TrueHD').replace ('DTS ES XLL', 'DTS-HD MA').replace ('DTS XLL X', 'DTS X') .replace ('E-AC-3 JOC', 'EAC3(Atmos)') .replace ('DTS ES XXCH', 'DTS-ES Discrete')}: {ac}} {audio[1].channels.replace('1','Mono').replace('2','2.0').replace('6','5.1').replace('7','6.1').replace('8','7.1').replace('9','8.1').replace('10','9.1')}Re: Multiple audio tracks with different codecs and languages
I have been testing it and now it is correct not as I had it, but I see a detail, if the audio is the same in my language ES and in English EN for example it only shows it once like this ES EAC3 5.1 when it should be if the audio is the same same ES-EN EAC3 5.1 or ES|EN with def preferredLang = 'ES'kim wrote: 21 Nov 2022, 20:09 sampleES AC3 5.1 - EN DTS-HD MA 7.1replaceCode: Select all
{ def preferredLang = 'ES' def fallbackLang = any{languages*.ISO2*.upper().join('-')}{'YourDefaultLandHere'} def useChFilter = false def filter = { [it.lang, it.codec, it.ch].findAll() } def codecList = [ 'MPEG Audio' : 'MP2', 'MP3' : 'MP3', 'PCM' : 'PCM', 'FLAC' : 'FLAC', 'AAC LC' : 'AAC', 'AAC LC SBR' : 'AAC', 'AAC LC SBR PS' : 'AAC', 'AC 3' : 'AC3', 'AC 3 Dep' : 'EAC3', 'E AC 3' : 'EAC3', 'E AC 3 JOC' : 'EAC3 Atmos', 'AC 3 Dep JOC' : 'EAC3 Atmos', 'DTS' : 'DTS', 'DTS 96 24' : 'DTS 96-24', 'DTS ES' : 'DTS-ES', 'DTS ES XXCH' : 'DTS-ES', 'DTS XBR' : 'DTS-HD HRA', 'DTS ES XBR' : 'DTS-HD HRA', 'DTS ES XXCH XBR' : 'DTS-HD HRA', 'DTS XLL' : 'DTS-HD MA', 'DTS ES XLL' : 'DTS-HD MA', 'DTS ES XXCH XLL' : 'DTS-HD MA', 'DTS XLL X' : 'DTS X', 'MLP FBA' : 'TrueHD', 'MLP FBA 16 ch' : 'TrueHD Atmos' ] def audioStreams = [] def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') } def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') } def oneStream = { listStream(it)[0] } def dString = { it.toDouble().toString() } def toInt = { it.toInteger() } any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{} def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() } { channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) } def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ) def combined = allOf{codec}{format_profile}.join(' ') audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null}, 'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ] return audioStreams } def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort() def allStreams = listStream(audioStreams) def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }) def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }) def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) } def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{} def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ') def groupCodec = audioStreams.sort{ a, b -> b.bitrate <=> a.bitrate }.groupBy{ it.codec }.values().collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.collect{ "${it.lang.replace('null', fallbackLang)} ${it.codec} ${it.ch}" }.join(' - ') any{addToList}{[bestPreferredLang, bestBitRate].unique().findAll().join(' - ')}{defaultStream}{bestBitRate}{preferredStream} }{'NO_AUDIO'} }Code: Select all
{audio[0].formatString =~ (/DTS XBR|AC-3|MLP FBA 16-ch|MLP FBA|DTS XLL|E-AC-3 JOC|DTS ES XLL|DTS ES XXCH/) ? {audio[0].formatString.replace ('DTS XBR', ' DTS-HD HR').replace ('AC-3', 'AC3') .replace ('DTS XLL', 'DTS-HD MA') .replace ('MLP FBA 16-ch', 'TrueHD(Atmos)') .replace ('MLP FBA', 'TrueHD').replace ('DTS ES XLL', 'DTS-HD MA').replace ('DTS XLL X', 'DTS X') .replace ('E-AC-3 JOC', 'EAC3(Atmos)') .replace ('DTS ES XXCH', 'DTS-ES Discrete')}: {ac}} {audio[0].channels.replace('1','Mono').replace('2','2.0').replace('6','5.1').replace('7','6.1').replace('8','7.1').replace('9','8.1').replace('10','9.1')} {audio[1].formatString =~ (/DTS XBR|AC-3|MLP FBA 16-ch|MLP FBA|DTS XLL|E-AC-3 JOC|DTS ES XLL|DTS ES XXCH/) ? {audio[1].formatString.replace ('DTS XBR', ' DTS-HD HR').replace ('AC-3', 'AC3') .replace ('DTS XLL', 'DTS-HD MA') .replace ('MLP FBA 16-ch', 'TrueHD(Atmos)') .replace ('MLP FBA', 'TrueHD').replace ('DTS ES XLL', 'DTS-HD MA').replace ('DTS XLL X', 'DTS X') .replace ('E-AC-3 JOC', 'EAC3(Atmos)') .replace ('DTS ES XXCH', 'DTS-ES Discrete')}: {ac}} {audio[1].channels.replace('1','Mono').replace('2','2.0').replace('6','5.1').replace('7','6.1').replace('8','7.1').replace('9','8.1').replace('10','9.1')}
I tried the code a few posts back but it doesn't work for me, could you indicate what is missing?
Sample:
ES AC3 5.1 - EN DTS-HD MA 7.1 (better audio for both languages)
ES-EN AC3 5.1 if audio it is same
Thanks for your time
Re: Multiple audio tracks with different codecs and languages
Code: Select all
.unique()Code: Select all
{[bestPreferredLang, bestBitRate].unique().findAll().join(' - ')}Re: Multiple audio tracks with different codecs and languages
Ok I have eliminated that function from the code but now in many examples the same language appears repeated like this:kim wrote: 25 Nov 2022, 15:38 cant you just remove thefromCode: Select all
.unique()?Code: Select all
{[bestPreferredLang, bestBitRate].unique().findAll().join(' - ')}
ES EAC3 5.1 - ES EAC3 5.1
And checking the media info has ES and EN.
When what I get should be ES-EN EAC3 5.1
or ES|EN EAC3 5.1 Or if they are different audios ES EAC3 5.1 - EN AC3 5.1
Re: Multiple audio tracks with different codecs and languages
replace the last part
Code: Select all
def custom = audioStreams.groupBy{ it.lang }.values()*.findAll{ it.lang == preferredLang || it.bitrate == audioStreams.bitrate.max() }.findAll { it }.collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.sort{ it.lang != preferredLang }.collect{ "${it.lang.replace('null', '')} ${it.codec} ${it.ch}" }.join(' - ')
any{addToList}{custom}{[bestPreferredLang, bestBitRate].unique().findAll().join(' - ')}{defaultStream}{bestBitRate}{preferredStream}ES AC3 5.1 - EN DTS-HD MA 7.1
Re: Multiple audio tracks with different codecs and languages
Ok I have verified it by removing the last part and adding as it says, but it is not correct, you can see the image with few examples:kim wrote: 27 Nov 2022, 14:41 try this:
replace the last partsample:Code: Select all
def custom = audioStreams.groupBy{ it.lang }.values()*.findAll{ it.lang == preferredLang || it.bitrate == audioStreams.bitrate.max() }.findAll { it }.collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.sort{ it.lang != preferredLang }.collect{ "${it.lang.replace('null', '')} ${it.codec} ${it.ch}" }.join(' - ') any{addToList}{custom}{[bestPreferredLang, bestBitRate].unique().findAll().join(' - ')}{defaultStream}{bestBitRate}{preferredStream}ES AC3 5.1 - EN DTS-HD MA 7.1

This code that u said me with last changes in the end of code
Code: Select all
{
def preferredLang = 'ES'
def fallbackLang = any{languages*.ISO2*.upper().join('-')}{'YourDefaultLandHere'}
def useChFilter = false
def filter = { [it.lang, it.codec, it.ch].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')
def custom = audioStreams.groupBy{ it.lang }.values()*.findAll{ it.lang == preferredLang || it.bitrate == audioStreams.bitrate.max() }.findAll { it }.collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.sort{ it.lang != preferredLang }.collect{ "${it.lang.replace('null', '')} ${it.codec} ${it.ch}" }.join(' - ')
any{addToList}{custom}{[bestPreferredLang, bestBitRate].unique().findAll().join(' - ')}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}Code: Select all
{
def preferredLang = 'ES'
def fallbackLang = any{languages*.ISO2*.upper().join('-')}{'YourDefaultLandHere'}
def useChFilter = false
def filter = { [it.lang, it.codec, it.ch, it.objects].findAll() }
def codecList =
[
'MPEG Audio' : 'MP2',
'MP3' : 'MP3',
'PCM' : 'PCM',
'FLAC' : 'FLAC',
'AAC LC' : 'AAC',
'AAC LC SBR' : 'AAC',
'AAC LC SBR PS' : 'AAC',
'AC 3' : 'AC3',
'AC 3 Dep' : 'EAC3',
'E AC 3' : 'EAC3',
'E AC 3 JOC' : 'EAC3 Atmos',
'AC 3 Dep JOC' : 'EAC3 Atmos',
'DTS' : 'DTS',
'DTS 96 24' : 'DTS 96-24',
'DTS ES' : 'DTS-ES',
'DTS ES XXCH' : 'DTS-ES',
'DTS XBR' : 'DTS-HD HRA',
'DTS ES XBR' : 'DTS-HD HRA',
'DTS ES XXCH XBR' : 'DTS-HD HRA',
'DTS XLL' : 'DTS-HD MA',
'DTS ES XLL' : 'DTS-HD MA',
'DTS ES XXCH XLL' : 'DTS-HD MA',
'DTS XLL X' : 'DTS X',
'MLP FBA' : 'TrueHD',
'MLP FBA 16 ch' : 'TrueHD Atmos'
]
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') }
def oneStream = { listStream(it)[0] }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
any{audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
def combined = allOf{codec}{format_profile}.join(' ')
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' },
'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ]
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort()
def allStreams = listStream(audioStreams)
def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() })
def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() })
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{}
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')
def groupCodec = audioStreams.sort{ a, b -> b.bitrate <=> a.bitrate }.groupBy{ it.codec }.values().collect{ [lang: it.unique{it.lang}.lang.join('|'), codec: it.unique{it.codec}.codec.join(), ch: it.unique{it.ch}.ch.join()] }.collect{ "${it.lang.replace('null', fallbackLang)} ${it.codec} ${it.ch}" }.join(' - ')
any{addToList}{groupCodec}{bestBitRateAllLang}{[bestPreferredLang, bestBitRate].findAll().join(' & ')}{defaultStream}{bestBitRate}{preferredStream}
}{'NO_AUDIO'}
}
I have managed to see this, what I want is to choose the best audio in ES And the following best audio language and in the event that ES and for example EN are the same audio, express it like this, for example ES|EN EAC3 5.1
Re: Multiple audio tracks with different codecs and languages
It's not easy, even harder when I can't test it proper with sample files
I need small sample files (5 sec should be enough maybe less)
Re: Multiple audio tracks with different codecs and languages
I finally had the energy...
I have tried to make it easy to use and customize...
It should also be better to detect the language if missing tag in file
give it a try:
Code: Select all
{
types = ['lang', 'codec', 'ch'] /* ['lang','codec', 'ch', 'objects'] */
replaceSpaces = ' ' /* EN?DTS-HD?MA?7.1?-?EN?DTS?5.1 */
joinWithin = '.' /* ENG?DTS-HD?MA?7.1 */
joinStreams = ' - ' /* ENG DTS-HD MA 7.1 ? FRA AC3 5.1 */
joinLangs = '-' /* ENG?FRA */
objectsText = 'Objs' /* [11 Objs] */
typeLang = 'ISO2' /* ISO2, ISO3 or name (linked to preferredLang and fallbackLang) */
preferredLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' (linked to typeLang) */
secondLang = 'ES' /* 'EN', 'ENG' or 'ENGLISH' */
fallbackLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' */
prefOrder = 'bitrate' /* bitrate, index or default */
groupByType = 'codec' /* codec, lang or bitrate */
useChFilter = false /* false or true (hide default ch count e.g. AC3 5.1 vs AC3) */
excludeCommentary = false /* false or true */
CommentaryText = '(Commentary)'
def codecList = /* [Add "DTS XLL" to codecList] = 'DTS XLL':'DTS-HD MA' */
[ 'MPEG Audio':'MP2', 'MP3':'MP3',
'PCM':'PCM', 'FLAC':'FLAC',
'AAC LC':'AAC', 'AAC LC SBR':'AAC', 'AAC LC SBR PS':'AAC',
'AC 3':'AC3', 'AC 3 Dep':'EAC3', 'E AC 3':'EAC3', 'E AC 3 JOC':'EAC3 Atmos', 'AC 3 Dep JOC':'EAC3 Atmos',
'DTS':'DTS', 'DTS 96 24':'DTS 96-24',
'DTS ES':'DTS-ES', 'DTS ES XXCH':'DTS-ES',
'DTS XBR':'DTS-HD HRA', 'DTS ES XBR':'DTS-HD HRA', 'DTS ES XXCH XBR':'DTS-HD HRA',
'DTS XLL':'DTS-HD MA', 'DTS ES XLL':'DTS-HD MA', 'DTS ES XXCH XLL':'DTS-HD MA',
'DTS XLL X':'DTS X',
'MLP FBA':'TrueHD', 'MLP FBA 16 ch':'TrueHD Atmos' ]
// Collect Language
def getLangCode(lang) {
lang = lang[0].toString().upper()
def langDB = []
def languages = Locale.getISOLanguages()
for (String langCode : languages) {
Locale locale = new Locale(langCode,langCode)
langDB << [ISO2: locale.getCountry(), ISO3: locale.getISO3Language().upper(), name: locale.getDisplayLanguage().upper()]
}
return any{langDB.find{it.ISO2 == lang}}{langDB.find{it.ISO3 == lang}}{langDB.find{it.name == lang}}
}
def fetchLang = any{ getLangCode(omdb.SpokenLanguages)[typeLang]}{getLangCode(info.SpokenLanguages)[typeLang]}{getLangCode(languages)[typeLang]}{getLangCode([fallbackLang])[typeLang]}{' '}
def getLang = { !it.lang ? it.lang.toString().replace(/null/, fetchLang) : it.lang.toString() }
def joinSameLang = { it.toUnique{it.lang}.findResults{ getLang(it) }.join([joinLangs]) }
def auLang = { [ISO2: it.'LanguageString2', ISO3: it.'LanguageString3', name: it.'LanguageString'] }
def prefLang(lang, streams) { streams.findAll{ it.lang == getLangCode([lang])[typeLang] } }
// Filters and Sorters
getObjText = { '['+[it.num, it.text].flatten().join(joinWithin)+']' }
def onOff(type, types = types) { types.contains(type) }
def filter = { [it.lang, it.codec, it.ch, it.objects ? getObjText(it.objects) : null, it.commentary ? CommentaryText : null].findAll() }
def filterLang = { it.findAll{ it.lang == preferredLang || it.lang == secondLang }.sort{ it.lang != preferredLang } }
def listStream = { it.unique(false).findResults{ filter(it) }*.join(joinWithin) }
def oneStream = { listStream(it)[0] }
def sortByType(streams, type = 'bitrate'){ streams.sort{ a, b -> b[type] <=> a[type] } }
def findBest(streams, type = 'bitrate'){ streams.findAll{ it[type] == streams[type].max() } }
def noCom = { it.findAll{ !it.commentary } }
def groupType = { it.groupBy{ it[groupByType] }.findResults{ k,v ->
allOf{ types.contains('lang') ? joinSameLang(v) : null}{ v.codec[0] }{ v.ch[0] }{ v.objects[0] ? getObjText(v.objects[0]) : null }
.join(joinWithin) }.join(joinStreams) }
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{P}\p{C}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
def separator = { it.replaceAll(/\s/,replaceSpaces) }
any{ audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def combined = allOf{codec}{format_profile}.join(' ')
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().sum().toString() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'combined' : combined,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'codec' : onOff('codec') ? codecList.get(combined, 'Add "' + combined + '" to codecList').space(joinWithin) : null,
'ch' : onOff('ch') ? useChFilter ? chFilter : ch : null, 'lang' : onOff('lang') ? any{ auLang(au)[typeLang].upper() }{ au.StreamCount == '1' ? fetchLang : null }{null} : null,
'objects' : onOff('objects') ? (any{def objects = au['NumberOfDynamicObjects']; objects ? [num: toInt(objects), text: objectsText] : ''}{null}) : null, 'commentary' : any {au['Title'].lower() ==~ /.*commentary.*/} {false}]
excludeCommentary ? audioStreams = noCom(audioStreams) : null
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('codecList') }.unique().sort()
def allStreams = separator(listStream(sortByType(audioStreams, prefOrder)).join(joinStreams))
def groupAllStreams = separator(groupType(sortByType(audioStreams, prefOrder)))
def bestPrefLang = any{ separator(oneStream(prefLang(preferredLang, audioStreams))) }{}
def bestPrefSecLang = any{ separator(oneStream(prefLang(secondLang, audioStreams))) }{}
def customBestPrefOrder = any{ separator(oneStream(findBest(audioStreams, prefOrder))) }{}
def customGroupBestLang = separator(groupType(sortByType(audioStreams, prefOrder).toUnique{ it.lang }))
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def customGroupLangFilter = any{ groupType(filterLang(sortByType(audioStreams, prefOrder))) }{}
def custom = groupType([(prefLang(preferredLang, audioStreams)), prefLang(secondLang, audioStreams), findBest(audioStreams, prefOrder)].flatten().toUnique{it.lang})
any{addToList}{custom}{customBestPrefOrder}{defaultStream}
}{'NO_AUDIO'}
}Code: Select all
types = ['lang', 'codec', 'ch'] /* ['lang','codec', 'ch', 'objects'] */
replaceSpaces = ' ' /* EN?DTS-HD?MA?7.1?-?EN?DTS?5.1 */
joinWithin = '.' /* ENG?DTS-HD?MA?7.1 */
joinStreams = ' - ' /* ENG DTS-HD MA 7.1 ? FRA AC3 5.1 */
joinLangs = '-' /* ENG?FRA */
objectsText = 'Objs' /* [11 Objs] */
typeLang = 'ISO2' /* ISO2, ISO3 or name (linked to preferredLang and fallbackLang) */
preferredLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' (linked to typeLang) */
secondLang = 'ES' /* 'EN', 'ENG' or 'ENGLISH' */
fallbackLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' */
prefOrder = 'bitrate' /* bitrate, index or default */
groupByType = 'codec' /* codec, lang or bitrate */
useChFilter = false /* false or true (hide default ch count e.g. AC3 5.1 vs AC3) */
excludeCommentary = false /* false or true */
CommentaryText = '(Commentary)'add or remove (e.g. output)
Code: Select all
{allStreams} = EN.DTS-HD.MA.7.1 - EN.TrueHD.Atmos.7.1 - EN.DTS-HD.MA.5.1 - EN.DTS.5.1 - EN.AC3.5.1 - FR.AC3.5.1 - IT.AC3.5.1 - ES.AC3.5.1 - NL.AC3.5.1 - CA.AC3.5.1
{groupAllStreams} = EN.DTS-HD.MA.7.1 - EN.TrueHD.Atmos.7.1 - EN.DTS.5.1 - EN-FR-IT-ES-NL-CA.AC3.5.1
{bestPrefLang} = EN.DTS-HD.MA.7.1
{bestPrefSecLang} = ES.AC3.5.1
{customBestPrefOrder} = EN.DTS-HD.MA.7.1
{customGroupBestLang} = EN.DTS-HD.MA.7.1 - FR-IT-ES-NL-CA.AC3.5.1
{defaultStream} = EN.DTS-HD.MA.7.1
{customGroupLangFilter} = EN.DTS-HD.MA.7.1 - EN.TrueHD.Atmos.7.1 - EN.DTS.5.1 - EN-ES.AC3.5.1
{custom} = EN.DTS-HD.MA.7.1 - ES.AC3.5.1Code: Select all
any{addToList}{custom}{customBestPrefOrder}{defaultStream}Re: Multiple audio tracks with different codecs and languages
Good evening.kim wrote: 21 Dec 2022, 02:19 Hi All
I finally had the energy...
I have tried to make it easy to use and customize...
It should also be better to detect the language if missing tag in file
give it a try:
to customize edit top part:Code: Select all
{ types = ['lang', 'codec', 'ch'] /* ['lang','codec', 'ch', 'objects'] */ replaceSpaces = ' ' /* EN?DTS-HD?MA?7.1?-?EN?DTS?5.1 */ joinWithin = '.' /* ENG?DTS-HD?MA?7.1 */ joinStreams = ' - ' /* ENG DTS-HD MA 7.1 ? FRA AC3 5.1 */ joinLangs = '-' /* ENG?FRA */ objectsText = 'Objs' /* [11 Objs] */ typeLang = 'ISO2' /* ISO2, ISO3 or name (linked to preferredLang and fallbackLang) */ preferredLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' (linked to typeLang) */ secondLang = 'ES' /* 'EN', 'ENG' or 'ENGLISH' */ fallbackLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' */ prefOrder = 'bitrate' /* bitrate, index or default */ groupByType = 'codec' /* codec, lang or bitrate */ useChFilter = false /* false or true (hide default ch count e.g. AC3 5.1 vs AC3) */ excludeCommentary = false /* false or true */ CommentaryText = '(Commentary)' def codecList = /* [Add "DTS XLL" to codecList] = 'DTS XLL':'DTS-HD MA' */ [ 'MPEG Audio':'MP2', 'MP3':'MP3', 'PCM':'PCM', 'FLAC':'FLAC', 'AAC LC':'AAC', 'AAC LC SBR':'AAC', 'AAC LC SBR PS':'AAC', 'AC 3':'AC3', 'AC 3 Dep':'EAC3', 'E AC 3':'EAC3', 'E AC 3 JOC':'EAC3 Atmos', 'AC 3 Dep JOC':'EAC3 Atmos', 'DTS':'DTS', 'DTS 96 24':'DTS 96-24', 'DTS ES':'DTS-ES', 'DTS ES XXCH':'DTS-ES', 'DTS XBR':'DTS-HD HRA', 'DTS ES XBR':'DTS-HD HRA', 'DTS ES XXCH XBR':'DTS-HD HRA', 'DTS XLL':'DTS-HD MA', 'DTS ES XLL':'DTS-HD MA', 'DTS ES XXCH XLL':'DTS-HD MA', 'DTS XLL X':'DTS X', 'MLP FBA':'TrueHD', 'MLP FBA 16 ch':'TrueHD Atmos' ] // Collect Language def getLangCode(lang) { lang = lang[0].toString().upper() def langDB = [] def languages = Locale.getISOLanguages() for (String langCode : languages) { Locale locale = new Locale(langCode,langCode) langDB << [ISO2: locale.getCountry(), ISO3: locale.getISO3Language().upper(), name: locale.getDisplayLanguage().upper()] } return any{langDB.find{it.ISO2 == lang}}{langDB.find{it.ISO3 == lang}}{langDB.find{it.name == lang}} } def fetchLang = any{ getLangCode(omdb.SpokenLanguages)[typeLang]}{getLangCode(info.SpokenLanguages)[typeLang]}{getLangCode(languages)[typeLang]}{getLangCode([fallbackLang])[typeLang]}{' '} def getLang = { !it.lang ? it.lang.toString().replace(/null/, fetchLang) : it.lang.toString() } def joinSameLang = { it.toUnique{it.lang}.findResults{ getLang(it) }.join([joinLangs]) } def auLang = { [ISO2: it.'LanguageString2', ISO3: it.'LanguageString3', name: it.'LanguageString'] } def prefLang(lang, streams) { streams.findAll{ it.lang == getLangCode([lang])[typeLang] } } // Filters and Sorters getObjText = { '['+[it.num, it.text].flatten().join(joinWithin)+']' } def onOff(type, types = types) { types.contains(type) } def filter = { [it.lang, it.codec, it.ch, it.objects ? getObjText(it.objects) : null, it.commentary ? CommentaryText : null].findAll() } def filterLang = { it.findAll{ it.lang == preferredLang || it.lang == secondLang }.sort{ it.lang != preferredLang } } def listStream = { it.unique(false).findResults{ filter(it) }*.join(joinWithin) } def oneStream = { listStream(it)[0] } def sortByType(streams, type = 'bitrate'){ streams.sort{ a, b -> b[type] <=> a[type] } } def findBest(streams, type = 'bitrate'){ streams.findAll{ it[type] == streams[type].max() } } def noCom = { it.findAll{ !it.commentary } } def groupType = { it.groupBy{ it[groupByType] }.findResults{ k,v -> allOf{ types.contains('lang') ? joinSameLang(v) : null}{ v.codec[0] }{ v.ch[0] }{ v.objects[0] ? getObjText(v.objects[0]) : null } .join(joinWithin) }.join(joinStreams) } def audioStreams = [] def audioClean = { it.replaceAll(/[\p{P}\p{C}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') } def dString = { it.toDouble().toString() } def toInt = { it.toInteger() } def separator = { it.replaceAll(/\s/,replaceSpaces) } any{ audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{} def combined = allOf{codec}{format_profile}.join(' ') def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().sum().toString() } { channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) } def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ) audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'combined' : combined, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null}, 'codec' : onOff('codec') ? codecList.get(combined, 'Add "' + combined + '" to codecList').space(joinWithin) : null, 'ch' : onOff('ch') ? useChFilter ? chFilter : ch : null, 'lang' : onOff('lang') ? any{ auLang(au)[typeLang].upper() }{ au.StreamCount == '1' ? fetchLang : null }{null} : null, 'objects' : onOff('objects') ? (any{def objects = au['NumberOfDynamicObjects']; objects ? [num: toInt(objects), text: objectsText] : ''}{null}) : null, 'commentary' : any {au['Title'].lower() ==~ /.*commentary.*/} {false}] excludeCommentary ? audioStreams = noCom(audioStreams) : null return audioStreams } def addToList = audioStreams.codec.findAll{ it.contains('codecList') }.unique().sort() def allStreams = separator(listStream(sortByType(audioStreams, prefOrder)).join(joinStreams)) def groupAllStreams = separator(groupType(sortByType(audioStreams, prefOrder))) def bestPrefLang = any{ separator(oneStream(prefLang(preferredLang, audioStreams))) }{} def bestPrefSecLang = any{ separator(oneStream(prefLang(secondLang, audioStreams))) }{} def customBestPrefOrder = any{ separator(oneStream(findBest(audioStreams, prefOrder))) }{} def customGroupBestLang = separator(groupType(sortByType(audioStreams, prefOrder).toUnique{ it.lang })) def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) } def customGroupLangFilter = any{ groupType(filterLang(sortByType(audioStreams, prefOrder))) }{} def custom = groupType([(prefLang(preferredLang, audioStreams)), prefLang(secondLang, audioStreams), findBest(audioStreams, prefOrder)].flatten().toUnique{it.lang}) any{addToList}{custom}{customBestPrefOrder}{defaultStream} }{'NO_AUDIO'} }ANDCode: Select all
types = ['lang', 'codec', 'ch'] /* ['lang','codec', 'ch', 'objects'] */ replaceSpaces = ' ' /* EN?DTS-HD?MA?7.1?-?EN?DTS?5.1 */ joinWithin = '.' /* ENG?DTS-HD?MA?7.1 */ joinStreams = ' - ' /* ENG DTS-HD MA 7.1 ? FRA AC3 5.1 */ joinLangs = '-' /* ENG?FRA */ objectsText = 'Objs' /* [11 Objs] */ typeLang = 'ISO2' /* ISO2, ISO3 or name (linked to preferredLang and fallbackLang) */ preferredLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' (linked to typeLang) */ secondLang = 'ES' /* 'EN', 'ENG' or 'ENGLISH' */ fallbackLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' */ prefOrder = 'bitrate' /* bitrate, index or default */ groupByType = 'codec' /* codec, lang or bitrate */ useChFilter = false /* false or true (hide default ch count e.g. AC3 5.1 vs AC3) */ excludeCommentary = false /* false or true */ CommentaryText = '(Commentary)'
add or remove (e.g. output)on last part:Code: Select all
{allStreams} = EN.DTS-HD.MA.7.1 - EN.TrueHD.Atmos.7.1 - EN.DTS-HD.MA.5.1 - EN.DTS.5.1 - EN.AC3.5.1 - FR.AC3.5.1 - IT.AC3.5.1 - ES.AC3.5.1 - NL.AC3.5.1 - CA.AC3.5.1 {groupAllStreams} = EN.DTS-HD.MA.7.1 - EN.TrueHD.Atmos.7.1 - EN.DTS.5.1 - EN-FR-IT-ES-NL-CA.AC3.5.1 {bestPrefLang} = EN.DTS-HD.MA.7.1 {bestPrefSecLang} = ES.AC3.5.1 {customBestPrefOrder} = EN.DTS-HD.MA.7.1 {customGroupBestLang} = EN.DTS-HD.MA.7.1 - FR-IT-ES-NL-CA.AC3.5.1 {defaultStream} = EN.DTS-HD.MA.7.1 {customGroupLangFilter} = EN.DTS-HD.MA.7.1 - EN.TrueHD.Atmos.7.1 - EN.DTS.5.1 - EN-ES.AC3.5.1 {custom} = EN.DTS-HD.MA.7.1 - ES.AC3.5.1Code: Select all
any{addToList}{custom}{customBestPrefOrder}{defaultStream}
First of all, thank you for your time at Christmas to dedicate your effort and dedication.
I have seen that the code is now simpler and with the final part for customization so that each person who uses it can choose.
I have tried it and now it is very simple and intuitive. I have done a lot of tests and I think that it is already correct.
The only inconvenience has been when adapting it to my code that gives me an error -> Syntax Error: Method definition not expected here. Please define the method at an appropriate place or perhaps try using a block/Closure instead. at line: 31 column: 2. File: Script25.groovy
I put my code here next to yours and it fails in the block of line 31 -> def getLangCode(lang) { :
Code: Select all
{drive}/{n.colon(' - ')} ({y}) {" {tmdb-$id}"}/{n.colon(' - ')} ({y}) {any{fn.match(/Director.?s|director|dir(?=\s)|dir(?=\W)/) ? '[Version Director]' : ''} {fn.match(/Extended|extendida|ext(?=\s)|ext(?=\W)/)? '[V. Extendida]' : ''} {fn.match(/Cine|cinema|cinematográfica|cinematografica|cin(?=\s)|cin(?=\W)/)? '[V. Cine]' : ''} {fn.match(/unrated/)? '- UNRATED' : ''}{fn.match(/uncut/)? '- UNCUT' : ''} {'[Ed. ' + fn.match(/(\d+)(?i:th|º)?.(?i:aniversario|Anniversary|anniv(?=\s)|anniv(?=\W) aniv(?=\s)|aniv(?=\W))/) + " aniversario]"} {fn.match(/Criterion/)? '[Ed. Criterion]' : ''} {fn. match (/remastered|remasterizada|remast(?=\s)|remast(?=\W)/) ? '[Remasterizada]' : ''}} {fn.match(/rotulado castellano|Rotulado Castellano|Rotulado Español|rotulado castellano|ROTULADO CASTELLANO/) && vf=~ /1080|720/ ? '[Rotulado Castellano]': ''}{fn.match(/rotulado castellano|Rotulado Castellano|Rotulado Español|rotulado castellano|ROTULADO CASTELLANO/) && vf=~ 2160 ? '[Rotulado Castellano]': ''} [{ fn.match(/A3P/) + ' ' }{ fn.match(/ATVP/) + ' ' }{ fn.match(/ATVP[+]/) + ' ' }{ fn.match(/ATV/) + ' ' }{ fn.match(/AMZN/) + ' ' }{ fn.match(/DSNP[+]/) + ' ' }{ fn.match(/DSN[+]/) + ' ' }{ fn.match(/DSNP/) + ' ' }{ fn.match(/DSNY/) + ' ' }{ fn.match(/Disney[+]/) + ' ' }{ fn.match(/DisneyPlus/) + ' ' }{ fn.match(/STARZ/) + ' ' }{ fn.match(/HBO/) + ' ' }{ fn.match(/HMAX/) + ' ' }{ fn.match(/HULU/) + ' ' }{ fn.match(/M[+]/) + ' ' }{ fn.match(/NF/) + ' ' }{ fn.match(/IMAX/) + ' ' }{ fn.match(/FLMN/) + ' ' }{allOf {any{any{fn.match(/uhdremux|remux/) && vf=~ 2160 ? 'UHDRemux': ''}{if (megabytes>=26500 && bitrate >= 37000000 && vf=~ 2160) 'UHDRemux'}{fn.match(/uhdrip|UHDrip|mUHD|UHDrip|UHD BluRay/) && vf=~ /1080|720|2160/ ? 'UHDRip': ''}{fn.match(/bdremux|BDRemux|BDremux|BluRay Remux|remux/) && vf=~ /1080|720/ ? 'BDRemux': ''}{if (megabytes>= 915769.6 && bitrate >= 917301504 && vf=~ /1080|720/) 'BDRemux'}}{fn.match(/bluray|Bluray|BluRay|BLURAYRIP|BLURAY/) && vf=~ /1080|720/ ? 'BDRip': ''}{fn.match(/bluray|Bluray|BluRay|BLURAYRIP|BLURAY|BDRip|bdrip/) && vf=~ /2160/ ? 'UHDRip': ''}{fn.match(/microuhd|Microuhd|MICROUHD|microUHD|MICROuhd|MHD|micro/) && vf=~ /2160/ ? 'MicroUHD': ''}{any{fn=~ (/m1080|m720|m480|brm|mhd|M1080|M720|M480|BRM|MHD|MicroHD|micro|Micro|MICROHD/) ? /MicroHD/ : null} {vs.match (/BluRay|WEB-DL/) ? {source.replace ('WEBDL', 'WEB-DL') .replace('webdl', 'WEB-DL') .replace('Webdl', 'WEB-DL') .replace('Web-dl', 'WEB-DL') .replace('web-dl', 'WEB-DL') .replace('WEB-dl', 'WEB-DL') .replace ('WEBrip', 'WEBRip') .replace ('webrip', 'WEBRip') .replace ('WEBRIP', 'WEBRip') .replace ('WEB-rip', 'WEBRip') .replace ('web-rip', 'WEBRip') .replace ('WEB-RIP', 'WEBRip') .replace ('WEB-Rip', 'WEBRip') .replace ('BDRIP', 'BDRip').replace ('bdrip', 'BDRip') .replace ('BDrip', 'BDRip') .replace ('Bdrip', 'BDRip') .replace ('bd-rip', 'BDRip') .replace ('BDRIP', 'BDRip') .replace ('BD-rip', 'BDRip') .replace ('BD-Rip', 'BDRip') .replace ('brrip', 'BRRip') .replace ('Brrip', 'BRRip') .replace ('BRrip', 'BRRip') .replace ('br-rip', 'BRRip') .replace ('BR-rip', 'BRRip') .replace ('BR-Rip', 'BRRip')} : ''}{vs}}}{vf}{hdr.replace('Dolby Vision', 'DV').replace ('Dovi','DV') .replace ('DOVI','DV') .replace ('dovi','DV')} {vc.replace('Microsoft', 'VC-1').replace ('x264','AVC') .replace ('x265','HEVC') .replace ('ATEME','HEVC')} {
types = ['lang', 'codec', 'ch'] /* ['lang','codec', 'ch', 'objects'] */
replaceSpaces = ' ' /* EN?DTS-HD?MA?7.1?-?EN?DTS?5.1 */
joinWithin = ' ' /* ENG?DTS-HD?MA?7.1 */
joinStreams = ' - ' /* ENG DTS-HD MA 7.1 ? FRA AC3 5.1 */
joinLangs = '-' /* ENG?FRA */
objectsText = 'Objs' /* [11 Objs] */
typeLang = 'ISO2' /* ISO2, ISO3 or name (linked to preferredLang and fallbackLang) */
preferredLang = 'ES' /* 'EN', 'ENG' or 'ENGLISH' (linked to typeLang) */
secondLang = 'EN' /* 'EN', 'ENG' or 'ENGLISH' */
fallbackLang = 'ES' /* 'EN', 'ENG' or 'ENGLISH' */
prefOrder = 'bitrate' /* bitrate, index or default */
groupByType = 'codec' /* codec, lang or bitrate */
useChFilter = false /* false or true (hide default ch count e.g. AC3 5.1 vs AC3) */
excludeCommentary = false /* false or true */
CommentaryText = '(Commentary)'
def codecList = /* [Add "DTS XLL" to codecList] = 'DTS XLL':'DTS-HD MA' */
[ 'MPEG Audio':'MP2', 'MP3':'MP3',
'PCM':'PCM', 'FLAC':'FLAC',
'AAC LC':'AAC', 'AAC LC SBR':'AAC', 'AAC LC SBR PS':'AAC',
'AC 3':'AC3', 'AC 3 Dep':'EAC3', 'E AC 3':'EAC3', 'E AC 3 JOC':'EAC3 Atmos', 'AC 3 Dep JOC':'EAC3 Atmos',
'DTS':'DTS', 'DTS 96 24':'DTS 96-24',
'DTS ES':'DTS-ES', 'DTS ES XXCH':'DTS-ES',
'DTS XBR':'DTS-HD HRA', 'DTS ES XBR':'DTS-HD HRA', 'DTS ES XXCH XBR':'DTS-HD HRA',
'DTS XLL':'DTS-HD MA', 'DTS ES XLL':'DTS-HD MA', 'DTS ES XXCH XLL':'DTS-HD MA',
'DTS XLL X':'DTS X',
'MLP FBA':'TrueHD', 'MLP FBA 16 ch':'TrueHD Atmos' ]
// Collect Language
def getLangCode(lang) {
lang = lang[0].toString().upper()
def langDB = []
def languages = Locale.getISOLanguages()
for (String langCode : languages) {
Locale locale = new Locale(langCode,langCode)
langDB << [ISO2: locale.getCountry(), ISO3: locale.getISO3Language().upper(), name: locale.getDisplayLanguage().upper()]
}
return any{langDB.find{it.ISO2 == lang}}{langDB.find{it.ISO3 == lang}}{langDB.find{it.name == lang}}
}
def fetchLang = any{ getLangCode(omdb.SpokenLanguages)[typeLang]}{getLangCode(info.SpokenLanguages)[typeLang]}{getLangCode(languages)[typeLang]}{getLangCode([fallbackLang])[typeLang]}{' '}
def getLang = { !it.lang ? it.lang.toString().replace(/null/, fetchLang) : it.lang.toString() }
def joinSameLang = { it.toUnique{it.lang}.findResults{ getLang(it) }.join([joinLangs]) }
def auLang = { [ISO2: it.'LanguageString2', ISO3: it.'LanguageString3', name: it.'LanguageString'] }
def prefLang(lang, streams) { streams.findAll{ it.lang == getLangCode([lang])[typeLang] } }
// Filters and Sorters
getObjText = { '['+[it.num, it.text].flatten().join(joinWithin)+']' }
def onOff(type, types = types) { types.contains(type) }
def filter = { [it.lang, it.codec, it.ch, it.objects ? getObjText(it.objects) : null, it.commentary ? CommentaryText : null].findAll() }
def filterLang = { it.findAll{ it.lang == preferredLang || it.lang == secondLang }.sort{ it.lang != preferredLang } }
def listStream = { it.unique(false).findResults{ filter(it) }*.join(joinWithin) }
def oneStream = { listStream(it)[0] }
def sortByType(streams, type = 'bitrate'){ streams.sort{ a, b -> b[type] <=> a[type] } }
def findBest(streams, type = 'bitrate'){ streams.findAll{ it[type] == streams[type].max() } }
def noCom = { it.findAll{ !it.commentary } }
def groupType = { it.groupBy{ it[groupByType] }.findResults{ k,v ->
allOf{ types.contains('lang') ? joinSameLang(v) : null}{ v.codec[0] }{ v.ch[0] }{ v.objects[0] ? getObjText(v.objects[0]) : null }
.join(joinWithin) }.join(joinStreams) }
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{P}\p{C}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
def separator = { it.replaceAll(/\s/,replaceSpaces) }
any{ audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def combined = allOf{codec}{format_profile}.join(' ')
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().sum().toString() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'combined' : combined,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'codec' : onOff('codec') ? codecList.get(combined, 'Add "' + combined + '" to codecList').space(joinWithin) : null,
'ch' : onOff('ch') ? useChFilter ? chFilter : ch : null, 'lang' : onOff('lang') ? any{ auLang(au)[typeLang].upper() }{ au.StreamCount == '1' ? fetchLang : null }{null} : null,
'objects' : onOff('objects') ? (any{def objects = au['NumberOfDynamicObjects']; objects ? [num: toInt(objects), text: objectsText] : ''}{null}) : null, 'commentary' : any {au['Title'].lower() ==~ /.*commentary.*/} {false}]
excludeCommentary ? audioStreams = noCom(audioStreams) : null
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('codecList') }.unique().sort()
def allStreams = separator(listStream(sortByType(audioStreams, prefOrder)).join(joinStreams))
def groupAllStreams = separator(groupType(sortByType(audioStreams, prefOrder)))
def bestPrefLang = any{ separator(oneStream(prefLang(preferredLang, audioStreams))) }{}
def bestPrefSecLang = any{ separator(oneStream(prefLang(secondLang, audioStreams))) }{}
def customBestPrefOrder = any{ separator(oneStream(findBest(audioStreams, prefOrder))) }{}
def customGroupBestLang = separator(groupType(sortByType(audioStreams, prefOrder).toUnique{ it.lang }))
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def customGroupLangFilter = any{ groupType(filterLang(sortByType(audioStreams, prefOrder))) }{}
def custom = groupType([(prefLang(preferredLang, audioStreams)), prefLang(secondLang, audioStreams), findBest(audioStreams, prefOrder)].flatten().toUnique{it.lang})
any{addToList}{custom}{customBestPrefOrder}{defaultStream}
}{'NO_AUDIO'}
} {allOf{audioLanguages}{textLanguages} =~ /spa/ ? null : ' VO'}{textLanguages =~ /spa/ && !(audioLanguages =~ /spa/) ? ' VOSE ' : null}{textLanguages.size() > 0 ? ' Subs' : 'null'}.join(' ')}] {" {tmdb-$id}"}Re: Multiple audio tracks with different codecs and languages
I changed a lot so something may be broken... try it out:
Code: Select all
{drive}{'/' + n.colon(' - ')}{' (' + y + ')'}{'/' + n.colon(' - ')}{' (' + y + ')'}
{ any{ fn.match(/Director.?s|director|dir(?=\s)|dir(?=\W)/) ? '[Version Director]' : ''}
{ fn.match(/Extended|extendida|ext(?=\s)|ext(?=\W)/) ? '[V. Extendida]' : '' }
{ fn.match(/Cine|cinema|cinematográfica|cinematografica|cin(?=\s)|cin(?=\W)/) ? '[V. Cine]' : '' }
{ '- ' + fn.match(/unrated|uncut/).upper() }
{ '[Ed. ' + fn.match(/(\d+)(?i:th|º)?.(?i:aniversario|Anniversary|anniv(?=\s)|anniv(?=\W) aniv(?=\s)|aniv(?=\W))/) + ' aniversario]' }
{ fn.match(/Criterion/) ? '[Ed. Criterion]' : '' }
{ fn.match (/remastered|remasterizada|remast(?=\s)|remast(?=\W)/) ? '[Remasterizada]' : ''}
{ fn =~ /(?i)rotulado castellano|Rotulado Español/ && vf=~ /2160|1080|720/ ? '[Rotulado Castellano]' : '' }
}
{ allOf{ fn.match(/A3P|ATVP|ATVP[+]|ATV|AMZN|DSNP[+]|DSN[+]|DSNP|DSNY|Disney[+]|DisneyPlus|STARZ|HBO|HMAX|HULU|M[+]|NF|IMAX|FLMN/) }
{any{ fn =~ /(?i)uhdremux|remux/ && vf=~ 2160 ? 'UHDRemux': '' }
{ if (megabytes >= 26500 && bitrate >= 37000000 && vf=~ 2160) 'UHDRemux' }
{ fn =~ /(?i)UHDrip|mUHD|UHD BluRay/ && vf=~ /1080|720|2160/ ? 'UHDRip': '' }
{ fn =~ /(?i)BDRemux|BluRay Remux|remux/ && vf=~ /1080|720/ ? 'BDRemux': '' }
{ if (megabytes >= 915769.6 && bitrate >= 917301504 && vf=~ /1080|720/) 'BDRemux' }
{ fn =~ /(?i)BluRay|BLURAYRIP/ && vf=~ /1080|720/ ? 'BDRip': '' }
{ fn =~ /(?i)BluRay|BLURAYRIP|BDRip/ && vf=~ /2160/ ? 'UHDRip': '' }
{ fn =~ /(?i)microUHD|MHD|micro/ && vf=~ /2160/ ? 'MicroUHD': '' }
{ fn =~ (/m1080|m720|m480|BRM|MHD|MicroHD|Micro/) ? /MicroHD/ : null }
{ vs.match(/BluRay|WEB-DL/) ? source.replaceFirst(/(?i)WEB-?DL/, 'WEB-DL').replaceFirst(/(?i)WEB-?Rip/, 'WEB-DL').replaceFirst(/(?i)BD-?Rip/, 'BDRip').replaceFirst(/(?i)BR-?Rip/, 'BDRip') : '' }
{vs}
}
{vf}
{ hdr.replaceFirst(/(?i)Dolby Vision|Dovi/, 'DV') }
{ vc.replace('Microsoft', 'VC-1').replaceFirst(/(?i)x264|x265|ATEME/, 'HEVC') }
}
{
types = ['lang', 'codec', 'ch']
replaceSpaces = ' '
joinWithin = ' '
joinStreams = ' - '
joinLangs = '-'
objectsText = 'Objs'
typeLang = 'ISO2'
preferredLang = 'ES'
secondLang = 'EN'
fallbackLang = 'ES'
prefOrder = 'bitrate'
groupByType = 'codec'
useChFilter = false
excludeCommentary = false
CommentaryText = '(Commentary)'
def codecList =
[ 'MPEG Audio':'MP2', 'MP3':'MP3',
'PCM':'PCM', 'FLAC':'FLAC',
'AAC LC':'AAC', 'AAC LC SBR':'AAC', 'AAC LC SBR PS':'AAC',
'AC 3':'AC3', 'AC 3 Dep':'EAC3', 'E AC 3':'EAC3', 'E AC 3 JOC':'EAC3 Atmos', 'AC 3 Dep JOC':'EAC3 Atmos',
'DTS':'DTS', 'DTS 96 24':'DTS 96-24',
'DTS ES':'DTS-ES', 'DTS ES XXCH':'DTS-ES',
'DTS XBR':'DTS-HD HRA', 'DTS ES XBR':'DTS-HD HRA', 'DTS ES XXCH XBR':'DTS-HD HRA',
'DTS XLL':'DTS-HD MA', 'DTS ES XLL':'DTS-HD MA', 'DTS ES XXCH XLL':'DTS-HD MA',
'DTS XLL X':'DTS X',
'MLP FBA':'TrueHD', 'MLP FBA 16 ch':'TrueHD Atmos' ]
// Collect Language
def getLangCode(lang) {
lang = lang[0].toString().upper()
def langDB = []
def languages = Locale.getISOLanguages()
for (String langCode : languages) {
Locale locale = new Locale(langCode,langCode)
langDB << [ISO2: locale.getCountry(), ISO3: locale.getISO3Language().upper(), name: locale.getDisplayLanguage().upper()]
}
return any{langDB.find{it.ISO2 == lang}}{langDB.find{it.ISO3 == lang}}{langDB.find{it.name == lang}}
}
def fetchLang = any{ getLangCode(omdb.SpokenLanguages)[typeLang]}{getLangCode(info.SpokenLanguages)[typeLang]}{getLangCode(languages)[typeLang]}{getLangCode([fallbackLang])[typeLang]}{' '}
def getLang = { !it.lang ? it.lang.toString().replace(/null/, fetchLang) : it.lang.toString() }
def joinSameLang = { it.toUnique{it.lang}.findResults{ getLang(it) }.join([joinLangs]) }
def auLang = { [ISO2: it.'LanguageString2', ISO3: it.'LanguageString3', name: it.'LanguageString'] }
def prefLang(lang, streams) { streams.findAll{ it.lang == getLangCode([lang])[typeLang] } }
// Filters and Sorters
getObjText = { '['+[it.num, it.text].flatten().join(joinWithin)+']' }
def onOff(type, types = types) { types.contains(type) }
def filter = { [it.lang, it.codec, it.ch, it.objects ? getObjText(it.objects) : null, it.commentary ? CommentaryText : null].findAll() }
def filterLang = { it.findAll{ it.lang == preferredLang || it.lang == secondLang }.sort{ it.lang != preferredLang } }
def listStream = { it.unique(false).findResults{ filter(it) }*.join(joinWithin) }
def oneStream = { listStream(it)[0] }
def sortByType(streams, type = 'bitrate'){ streams.sort{ a, b -> b[type] <=> a[type] } }
def findBest(streams, type = 'bitrate'){ streams.findAll{ it[type] == streams[type].max() } }
def noCom = { it.findAll{ !it.commentary } }
def groupType = { it.groupBy{ it[groupByType] }.findResults{ k,v ->
allOf{ types.contains('lang') ? joinSameLang(v) : null}{ v.codec[0] }{ v.ch[0] }{ v.objects[0] ? getObjText(v.objects[0]) : null }
.join(joinWithin) }.join(joinStreams) }
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{P}\p{C}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
def separator = { it.replaceAll(/\s/,replaceSpaces) }
any{ audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def combined = allOf{codec}{format_profile}.join(' ')
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().sum().toString() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'combined' : combined,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'codec' : onOff('codec') ? codecList.get(combined, 'Add "' + combined + '" to codecList').space(joinWithin) : null,
'ch' : onOff('ch') ? useChFilter ? chFilter : ch : null, 'lang' : onOff('lang') ? any{ auLang(au)[typeLang].upper() }{ au.StreamCount == '1' ? fetchLang : null }{null} : null,
'objects' : onOff('objects') ? (any{def objects = au['NumberOfDynamicObjects']; objects ? [num: toInt(objects), text: objectsText] : ''}{null}) : null, 'commentary' : any {au['Title'].lower() ==~ /.*commentary.*/} {false}]
excludeCommentary ? audioStreams = noCom(audioStreams) : null
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('codecList') }.unique().sort()
def allStreams = separator(listStream(sortByType(audioStreams, prefOrder)).join(joinStreams))
def groupAllStreams = separator(groupType(sortByType(audioStreams, prefOrder)))
def bestPrefLang = any{ separator(oneStream(prefLang(preferredLang, audioStreams))) }{}
def bestPrefSecLang = any{ separator(oneStream(prefLang(secondLang, audioStreams))) }{}
def customBestPrefOrder = any{ separator(oneStream(findBest(audioStreams, prefOrder))) }{}
def customGroupBestLang = separator(groupType(sortByType(audioStreams, prefOrder).toUnique{ it.lang }))
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def customGroupLangFilter = any{ groupType(filterLang(sortByType(audioStreams, prefOrder))) }{}
def custom = groupType([(prefLang(preferredLang, audioStreams)), prefLang(secondLang, audioStreams), findBest(audioStreams, prefOrder)].flatten().toUnique{it.lang})
'[' + any{addToList}{bestPrefLang}{customBestPrefOrder}{defaultStream} + ']'
}{'NO_AUDIO'}
}{' ' + (allOf{textLanguages}
{textLanguages =~ /spa/ ? null : ' VO'}
{textLanguages =~ /spa/ && !(audioLanguages =~ /spa/) ? ' VOSE ' : null}
{textLanguages.size() > 0 ? ' Subs' : 'null'}.join(' '))
}Re: Multiple audio tracks with different codecs and languages
Thanks for the code, but when I try to rename I get [No_Audio]kim wrote: 11 Aug 2021, 18:08 this was not easy... but give it a try:
sample:Code: Select all
{ def preferredLang = 'FR' def useChFilter = false def filter = { [it.lang, it.codec, it.ch, it.objects].findAll() } def codecList = [ 'MPEG Audio' : 'MP2', 'MP3' : 'MP3', 'PCM' : 'PCM', 'FLAC' : 'FLAC', 'AAC LC' : 'AAC', 'AAC LC SBR' : 'AAC', 'AAC LC SBR PS' : 'AAC', 'AC 3' : 'AC3', 'AC 3 Dep' : 'EAC3', 'E AC 3' : 'EAC3', 'E AC 3 JOC' : 'EAC3 Atmos', 'AC 3 Dep JOC' : 'EAC3 Atmos', 'DTS' : 'DTS', 'DTS 96 24' : 'DTS 96-24', 'DTS ES' : 'DTS-ES', 'DTS ES XXCH' : 'DTS-ES', 'DTS XBR' : 'DTS-HD HRA', 'DTS ES XBR' : 'DTS-HD HRA', 'DTS ES XXCH XBR' : 'DTS-HD HRA', 'DTS XLL' : 'DTS-HD MA', 'DTS ES XLL' : 'DTS-HD MA', 'DTS ES XXCH XLL' : 'DTS-HD MA', 'DTS XLL X' : 'DTS X', 'MLP FBA' : 'TrueHD', 'MLP FBA 16 ch' : 'TrueHD Atmos' ] def audioStreams = [] def audioClean = { it.replaceAll(/[\p{Pd}\p{Space}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ').slash(' ') } def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') } def listStream = { it.sort{ a, b -> b.bitrate <=> a.bitrate }.collect{ filter(it) }.unique()*.join(' ') } def oneStream = { listStream(it)[0] } def dString = { it.toDouble().toString() } def toInt = { it.toInteger() } any{audio.collect{ au -> def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] }) def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{} def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().toString().sum() } { channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) } def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null ) def combined = allOf{codec}{format_profile}.join(' ') audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'codec' : codecList.get(combined, 'Add to "' + combined + '" codecList'), 'combined' : combined, 'ch' : useChFilter ? chFilter : ch, 'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null}, 'objects' : any{def objects = au['NumberOfDynamicObjects']; objects ? "[$objects Objs]" : ''}{null}, 'lang' : any{ au.'LanguageString2'.upper() }{null} ] return audioStreams } def addToList = audioStreams.codec.findAll{ it.contains('Add to') }.unique().sort() def allStreams = listStream(audioStreams) def preferredStream = oneStream(audioStreams.findAll{ it.index == audioStreams.index.max() }) def bestBitRate = oneStream(audioStreams.findAll{ it.bitrate == audioStreams.bitrate.max() }) def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) } def bestPreferredLang = any{ oneStream(audioStreams.findAll{ it.lang == preferredLang }) }{} def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ') any{addToList}{bestBitRateAllLang}{[bestPreferredLang, bestBitRate].findAll().join(' & ')}{defaultStream}{bestBitRate}{preferredStream} }{'NO_AUDIO'} }EN DTS-HD MA 7.1 - FR AC3 5.1 - IT AC3 5.1 - ES AC3 5.1 - NL AC3 5.1 - CA AC3 5.1
EDIT:
if you really want with the [...]
you can replacewithCode: Select all
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).join(' - ')orCode: Select all
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).toString().split(', ').join(' - ')sample:Code: Select all
def bestBitRateAllLang = listStream(audioStreams.groupBy{ it.lang }.values()*.sort{ a, b -> b.bitrate <=> a.bitrate }*.find { it }).joining(' - ', ' [', ']')[EN DTS-HD MA 7.1 - FR AC3 5.1 - IT AC3 5.1 - ES AC3 5.1 - NL AC3 5.1 - CA AC3 5.1]

Re: Multiple audio tracks with different codecs and languages
I definetly want to use this script:
Code: Select all
{
types = ['lang', 'codec', 'ch'] /* ['lang','codec', 'ch', 'objects'] */
replaceSpaces = ' ' /* EN?DTS-HD?MA?7.1?-?EN?DTS?5.1 */
joinWithin = '.' /* ENG?DTS-HD?MA?7.1 */
joinStreams = '.' /* ENG DTS-HD MA 7.1 ? FRA AC3 5.1 */
joinLangs = '.' /* ENG?FRA */
objectsText = 'Objs' /* [11 Objs] */
typeLang = 'ISO3' /* ISO2, ISO3 or name (linked to preferredLang and fallbackLang) */
preferredLang = 'iTA' /* 'EN', 'ENG' or 'ENGLISH' (linked to typeLang) */
secondLang = 'ENG' /* 'EN', 'ENG' or 'ENGLISH' */
fallbackLang = 'iTA' /* 'EN', 'ENG' or 'ENGLISH' */
prefOrder = 'bitrate' /* bitrate, index or default */
groupByType = 'codec' /* codec, lang or bitrate */
useChFilter = false /* false or true (hide default ch count e.g. AC3 5.1 vs AC3) */
excludeCommentary = false /* false or true */
CommentaryText = '(Commentary)'
def codecList = /* [Add "DTS XLL" to codecList] = 'DTS XLL':'DTS-HD MA' */
[ 'MPEG Audio':'MP2', 'MP3':'MP3',
'PCM':'PCM', 'FLAC':'FLAC',
'AAC LC':'AAC', 'AAC LC SBR':'AAC', 'AAC LC SBR PS':'AAC',
'AC 3':'AC3', 'AC 3 Dep':'EAC3', 'E AC 3':'EAC3', 'E AC 3 JOC':'EAC3 Atmos', 'AC 3 Dep JOC':'EAC3 Atmos',
'DTS':'DTS', 'DTS 96 24':'DTS 96-24',
'DTS ES':'DTS-ES', 'DTS ES XXCH':'DTS-ES',
'DTS XBR':'DTS-HD HRA', 'DTS ES XBR':'DTS-HD HRA', 'DTS ES XXCH XBR':'DTS-HD HRA',
'DTS XLL':'DTS-HD MA', 'DTS ES XLL':'DTS-HD MA', 'DTS ES XXCH XLL':'DTS-HD MA',
'DTS XLL X':'DTS X',
'MLP FBA':'TrueHD', 'MLP FBA 16 ch':'TrueHD Atmos' ]
// Collect Language
def getLangCode(lang) {
lang = lang[0].toString().upper()
def langDB = []
def languages = Locale.getISOLanguages()
for (String langCode : languages) {
Locale locale = new Locale(langCode,langCode)
langDB << [ISO2: locale.getCountry(), ISO3: locale.getISO3Language().upper(), name: locale.getDisplayLanguage().upper()]
}
return any{langDB.find{it.ISO2 == lang}}{langDB.find{it.ISO3 == lang}}{langDB.find{it.name == lang}}
}
def fetchLang = any{ getLangCode(omdb.SpokenLanguages)[typeLang]}{getLangCode(info.SpokenLanguages)[typeLang]}{getLangCode(languages)[typeLang]}{getLangCode([fallbackLang])[typeLang]}{' '}
def getLang = { !it.lang ? it.lang.toString().replace(/null/, fetchLang) : it.lang.toString() }
def joinSameLang = { it.toUnique{it.lang}.findResults{ getLang(it) }.join([joinLangs]) }
def auLang = { [ISO2: it.'LanguageString2', ISO3: it.'LanguageString3', name: it.'LanguageString'] }
def prefLang(lang, streams) { streams.findAll{ it.lang == getLangCode([lang])[typeLang] } }
// Filters and Sorters
getObjText = { '['+[it.num, it.text].flatten().join(joinWithin)+']' }
def onOff(type, types = types) { types.contains(type) }
def filter = { [it.lang, it.codec, it.ch, it.objects ? getObjText(it.objects) : null, it.commentary ? CommentaryText : null].findAll() }
def filterLang = { it.findAll{ it.lang == preferredLang || it.lang == secondLang }.sort{ it.lang != preferredLang } }
def listStream = { it.unique(false).findResults{ filter(it) }*.join(joinWithin) }
def oneStream = { listStream(it)[0] }
def sortByType(streams, type = 'bitrate'){ streams.sort{ a, b -> b[type] <=> a[type] } }
def findBest(streams, type = 'bitrate'){ streams.findAll{ it[type] == streams[type].max() } }
def noCom = { it.findAll{ !it.commentary } }
def groupType = { it.groupBy{ it[groupByType] }.findResults{ k,v ->
allOf{ types.contains('lang') ? joinSameLang(v) : null}{ v.codec[0] }{ v.ch[0] }{ v.objects[0] ? getObjText(v.objects[0]) : null }
.join(joinWithin) }.join(joinStreams) }
def audioStreams = []
def audioClean = { it.replaceAll(/[\p{P}\p{C}]/, ' ').replaceAll(/\p{Space}{2,}/, ' ') }
def channelClean = { it.replaceAll(/Debug.+|Object\sBased\s?\/?|(\d+)?\sobjects\s\/\s|0.(?=\d.\d)|20/).replaceAll(/6.0/,'5.1').replaceAll(/8.0/,'7.1') }
def dString = { it.toDouble().toString() }
def toInt = { it.toInteger() }
def separator = { it.replaceAll(/\s/,replaceSpaces) }
any{ audio.collect{ au ->
def codec = audioClean(any{ au['CodecID/Hint'] }{ au['Format'] })
def format_profile = any{ audioClean(au['Format_AdditionalFeatures'])}{}
def combined = allOf{codec}{format_profile}.join(' ')
def String ch = any{ channelClean(au.ChannelPositionsString2).tokenize('\\/')*.toDouble().sum().toString() }
{ channelClean(dString(au.ChannelsOriginal)) } { channelClean(dString(au.Channels)) }
def chFilter = ( ( ( (ac == 'AAC'||ac == 'MP3') && ch != '2.0') || ( (ac == 'AC3'||ac == 'EAC3'||ac == 'DTS'||ac == 'TrueHD'||ac == 'MLPFBA') && ch != '5.1' ) ) ? ch : null )
audioStreams << ['index' : codecList.findIndexOf { it.key == combined }, 'default' : any {au['default'][0].toBoolean() }{ audio.size == 1 ? true : '' }, 'combined' : combined,
'bitrate' : any{ toInt(au.BitRate) }{ toInt(au.BitRate_Maximum) }{ dString(au.FrameRate) }{null},
'codec' : onOff('codec') ? codecList.get(combined, 'Add "' + combined + '" to codecList').space(joinWithin) : null,
'ch' : onOff('ch') ? useChFilter ? chFilter : ch : null, 'lang' : onOff('lang') ? any{ auLang(au)[typeLang].upper() }{ au.StreamCount == '1' ? fetchLang : null }{null} : null,
'objects' : onOff('objects') ? (any{def objects = au['NumberOfDynamicObjects']; objects ? [num: toInt(objects), text: objectsText] : ''}{null}) : null, 'commentary' : any {au['Title'].lower() ==~ /.*commentary.*/} {false}]
excludeCommentary ? audioStreams = noCom(audioStreams) : null
return audioStreams
}
def addToList = audioStreams.codec.findAll{ it.contains('codecList') }.unique().sort()
def allStreams = separator(listStream(sortByType(audioStreams, prefOrder)).join(joinStreams))
def groupAllStreams = separator(groupType(sortByType(audioStreams, prefOrder)))
def bestPrefLang = any{ separator(oneStream(prefLang(preferredLang, audioStreams))) }{}
def bestPrefSecLang = any{ separator(oneStream(prefLang(secondLang, audioStreams))) }{}
def customBestPrefOrder = any{ separator(oneStream(findBest(audioStreams, prefOrder))) }{}
def customGroupBestLang = separator(groupType(sortByType(audioStreams, prefOrder).toUnique{ it.lang }))
def defaultStream = any{ oneStream(audioStreams.findAll{ it.default == true }) }{ oneStream(audioStreams) }
def customGroupLangFilter = any{ groupType(filterLang(sortByType(audioStreams, prefOrder))) }{}
def custom = groupType([(prefLang(preferredLang, audioStreams)), prefLang(secondLang, audioStreams), findBest(audioStreams, prefOrder)].flatten().toUnique{it.lang})
any{addToList}{custom}{customBestPrefOrder}{defaultStream}
}{'NO_AUDIO'}
}Thank you.