From 6b72400fc00e0f5b0853755a2c02ae8a8cbdd739 Mon Sep 17 00:00:00 2001 From: Mate Herber Date: Wed, 4 Mar 2026 16:31:07 +0100 Subject: [PATCH 1/2] New: Add {MediaInfo BestAudioCodec} naming token Adds a new naming token that resolves to the highest quality audio codec across all streams in a file, rather than just the primary stream. This addresses the common issue where release groups mux the lowest quality codec first (e.g. AC3), causing Radarr to rename files with the wrong codec and triggering unnecessary upgrade loops. Centralizes audio codec identification into an AudioCodec enum and AudioCodecHelper class (following the HdrFormat pattern), eliminating the duplicated matching logic between MediaInfoFormatter and VideoFileInfoReader. Also adds the token to the naming modal so users can discover it. Fixes #6488 --- .../MediaManagement/Naming/NamingModal.tsx | 1 + .../FormatBestAudioCodecFixture.cs | 106 +++++++++++ .../FileNameBuilderFixture.cs | 24 +++ .../MediaFiles/MediaInfo/AudioCodec.cs | 176 ++++++++++++++++++ .../MediaInfo/MediaInfoFormatter.cs | 108 +---------- .../MediaFiles/MediaInfo/MediaInfoModel.cs | 6 + .../MediaInfo/VideoFileInfoReader.cs | 41 +++- .../Organizer/FileNameBuilder.cs | 13 +- .../Organizer/FileNameSampleService.cs | 3 + 9 files changed, 372 insertions(+), 106 deletions(-) create mode 100644 src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatBestAudioCodecFixture.cs create mode 100644 src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs diff --git a/frontend/src/Settings/MediaManagement/Naming/NamingModal.tsx b/frontend/src/Settings/MediaManagement/Naming/NamingModal.tsx index f80a2e4ba1..5126da7a54 100644 --- a/frontend/src/Settings/MediaManagement/Naming/NamingModal.tsx +++ b/frontend/src/Settings/MediaManagement/Naming/NamingModal.tsx @@ -143,6 +143,7 @@ const mediaInfoTokens = [ { token: '{MediaInfo Full}', example: 'x264 DTS [EN+DE]', footNotes: '1' }, { token: '{MediaInfo AudioCodec}', example: 'DTS' }, + { token: '{MediaInfo BestAudioCodec}', example: 'DTS-HD MA' }, { token: '{MediaInfo AudioChannels}', example: '5.1' }, { token: '{MediaInfo AudioLanguages}', diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatBestAudioCodecFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatBestAudioCodecFixture.cs new file mode 100644 index 0000000000..38fd91e940 --- /dev/null +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatBestAudioCodecFixture.cs @@ -0,0 +1,106 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles.MediaInfo; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests +{ + [TestFixture] + public class FormatBestAudioCodecFixture : TestBase + { + private static string sceneName = "My.Movie.2020-Group"; + + [Test] + public void should_pick_truehd_atmos_over_ac3() + { + var mediaInfo = new MediaInfoModel + { + BestAudioFormat = "truehd", + BestAudioCodecID = "thd+", + BestAudioProfile = string.Empty + }; + + MediaInfoFormatter.FormatAudioCodec( + new MediaInfoModel + { + AudioFormat = mediaInfo.BestAudioFormat, + AudioCodecID = mediaInfo.BestAudioCodecID, + AudioProfile = mediaInfo.BestAudioProfile + }, + sceneName).Should().Be("TrueHD Atmos"); + } + + [Test] + public void should_pick_dts_hd_ma_over_ac3() + { + var mediaInfo = new MediaInfoModel + { + BestAudioFormat = "dts", + BestAudioCodecID = string.Empty, + BestAudioProfile = "DTS-HD MA" + }; + + MediaInfoFormatter.FormatAudioCodec( + new MediaInfoModel + { + AudioFormat = mediaInfo.BestAudioFormat, + AudioCodecID = mediaInfo.BestAudioCodecID, + AudioProfile = mediaInfo.BestAudioProfile + }, + sceneName).Should().Be("DTS-HD MA"); + } + + [Test] + public void should_pick_dtsx_over_dts_hd_ma() + { + var mediaInfo = new MediaInfoModel + { + BestAudioFormat = "dts", + BestAudioCodecID = string.Empty, + BestAudioProfile = "DTS:X" + }; + + MediaInfoFormatter.FormatAudioCodec( + new MediaInfoModel + { + AudioFormat = mediaInfo.BestAudioFormat, + AudioCodecID = mediaInfo.BestAudioCodecID, + AudioProfile = mediaInfo.BestAudioProfile + }, + sceneName).Should().Be("DTS-X"); + } + + [Test] + public void should_return_single_stream_codec() + { + var mediaInfo = new MediaInfoModel + { + BestAudioFormat = "ac3", + BestAudioCodecID = string.Empty, + BestAudioProfile = string.Empty + }; + + MediaInfoFormatter.FormatAudioCodec( + new MediaInfoModel + { + AudioFormat = mediaInfo.BestAudioFormat, + AudioCodecID = mediaInfo.BestAudioCodecID, + AudioProfile = mediaInfo.BestAudioProfile + }, + sceneName).Should().Be("AC3"); + } + + [Test] + public void should_return_null_when_best_audio_format_is_null() + { + var mediaInfo = new MediaInfoModel + { + AudioFormat = null, + AudioCodecID = null, + AudioProfile = null + }; + + MediaInfoFormatter.FormatAudioCodec(mediaInfo, sceneName).Should().BeNull(); + } + } +} diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs index 7cf7ce4d66..e700e2b208 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderTests/FileNameBuilderFixture.cs @@ -775,6 +775,30 @@ public void should_not_update_media_info_if_token_configured_and_revision_is_new Mocker.GetMock().Verify(v => v.Update(_movieFile, _movie), Times.Never()); } + [Test] + public void should_format_best_audio_codec() + { + _movieFile.ReleaseGroup = null; + + _movieFile.MediaInfo = new MediaInfoModel + { + VideoFormat = "h264", + AudioFormat = "ac3", + AudioChannels = 6, + AudioLanguages = new List { "eng" }, + Subtitles = new List { "eng" }, + BestAudioFormat = "dts", + BestAudioCodecID = string.Empty, + BestAudioProfile = "DTS-HD MA", + SchemaRevision = 15 + }; + + _namingConfig.StandardMovieFormat = "{MediaInfo BestAudioCodec}"; + + Subject.BuildFileName(_movie, _movieFile) + .Should().Be("DTS-HD MA"); + } + private void GivenMediaInfoModel(string videoCodec = "h264", string audioCodec = "dts", int audioChannels = 6, diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs new file mode 100644 index 0000000000..ef0cad3bc9 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs @@ -0,0 +1,176 @@ +namespace NzbDrone.Core.MediaFiles.MediaInfo +{ + public enum AudioCodec + { + Unknown = 0, + MP2 = 1, + MP3 = 2, + PCM = 3, + Vorbis = 4, + WMA = 5, + Opus = 6, + AAC = 7, + HE_AAC = 8, + AC3 = 9, + FLAC = 10, + EAC3 = 11, + DTS = 12, + DTS_Express = 13, + DTS_9624 = 14, + DTS_ES = 15, + DTS_HD_HRA = 16, + EAC3Atmos = 17, + DTS_HD_MA = 18, + DTS_X = 19, + TrueHD = 20, + TrueHDAtmos = 21 + } + + public static class AudioCodecHelper + { + public static AudioCodec Resolve(string format, string codecID, string profile) + { + format = format ?? string.Empty; + codecID = codecID ?? string.Empty; + profile = profile ?? string.Empty; + + if (codecID == "thd+") + { + return AudioCodec.TrueHDAtmos; + } + + if (format == "truehd") + { + return AudioCodec.TrueHD; + } + + if (format == "flac") + { + return AudioCodec.FLAC; + } + + if (format == "dts") + { + if (profile == "DTS:X") + { + return AudioCodec.DTS_X; + } + + if (profile == "DTS-HD MA") + { + return AudioCodec.DTS_HD_MA; + } + + if (profile == "DTS-ES") + { + return AudioCodec.DTS_ES; + } + + if (profile == "DTS-HD HRA") + { + return AudioCodec.DTS_HD_HRA; + } + + if (profile == "DTS Express") + { + return AudioCodec.DTS_Express; + } + + if (profile == "DTS 96/24") + { + return AudioCodec.DTS_9624; + } + + return AudioCodec.DTS; + } + + if (codecID == "ec+3") + { + return AudioCodec.EAC3Atmos; + } + + if (format == "eac3") + { + return AudioCodec.EAC3; + } + + if (format == "ac3") + { + return AudioCodec.AC3; + } + + if (format == "aac") + { + if (codecID == "A_AAC/MPEG4/LC/SBR") + { + return AudioCodec.HE_AAC; + } + + return AudioCodec.AAC; + } + + if (format == "mp3") + { + return AudioCodec.MP3; + } + + if (format == "mp2") + { + return AudioCodec.MP2; + } + + if (format == "opus") + { + return AudioCodec.Opus; + } + + if (format.StartsWith("pcm_") || format.StartsWith("adpcm_")) + { + return AudioCodec.PCM; + } + + if (format == "vorbis") + { + return AudioCodec.Vorbis; + } + + if (format == "wmav1" || + format == "wmav2" || + format == "wmapro") + { + return AudioCodec.WMA; + } + + return AudioCodec.Unknown; + } + + public static string GetDisplayName(AudioCodec codec) + { + return codec switch + { + AudioCodec.TrueHDAtmos => "TrueHD Atmos", + AudioCodec.TrueHD => "TrueHD", + AudioCodec.DTS_X => "DTS-X", + AudioCodec.DTS_HD_MA => "DTS-HD MA", + AudioCodec.DTS_HD_HRA => "DTS-HD HRA", + AudioCodec.DTS_ES => "DTS-ES", + AudioCodec.DTS_Express => "DTS Express", + AudioCodec.DTS_9624 => "DTS 96/24", + AudioCodec.DTS => "DTS", + AudioCodec.EAC3Atmos => "EAC3 Atmos", + AudioCodec.EAC3 => "EAC3", + AudioCodec.FLAC => "FLAC", + AudioCodec.AC3 => "AC3", + AudioCodec.HE_AAC => "HE-AAC", + AudioCodec.AAC => "AAC", + AudioCodec.MP3 => "MP3", + AudioCodec.MP2 => "MP2", + AudioCodec.Opus => "Opus", + AudioCodec.PCM => "PCM", + AudioCodec.Vorbis => "Vorbis", + AudioCodec.WMA => "WMA", + _ => string.Empty + }; + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs index 6914519fd4..5bdad3deeb 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs @@ -45,111 +45,11 @@ public static string FormatAudioCodec(MediaInfoModel mediaInfo, string sceneName } // see definitions here https://github.com/FFmpeg/FFmpeg/blob/master/libavcodec/codec_desc.c - if (audioCodecID == "thd+") + var codec = AudioCodecHelper.Resolve(audioFormat, audioCodecID, audioProfile); + + if (codec != AudioCodec.Unknown) { - return "TrueHD Atmos"; - } - - if (audioFormat == "truehd") - { - return "TrueHD"; - } - - if (audioFormat == "flac") - { - return "FLAC"; - } - - if (audioFormat == "dts") - { - if (audioProfile == "DTS:X") - { - return "DTS-X"; - } - - if (audioProfile == "DTS-HD MA") - { - return "DTS-HD MA"; - } - - if (audioProfile == "DTS-ES") - { - return "DTS-ES"; - } - - if (audioProfile == "DTS-HD HRA") - { - return "DTS-HD HRA"; - } - - if (audioProfile == "DTS Express") - { - return "DTS Express"; - } - - if (audioProfile == "DTS 96/24") - { - return "DTS 96/24"; - } - - return "DTS"; - } - - if (audioCodecID == "ec+3") - { - return "EAC3 Atmos"; - } - - if (audioFormat == "eac3") - { - return "EAC3"; - } - - if (audioFormat == "ac3") - { - return "AC3"; - } - - if (audioFormat == "aac") - { - if (audioCodecID == "A_AAC/MPEG4/LC/SBR") - { - return "HE-AAC"; - } - - return "AAC"; - } - - if (audioFormat == "mp3") - { - return "MP3"; - } - - if (audioFormat == "mp2") - { - return "MP2"; - } - - if (audioFormat == "opus") - { - return "Opus"; - } - - if (audioFormat.StartsWith("pcm_") || audioFormat.StartsWith("adpcm_")) - { - return "PCM"; - } - - if (audioFormat == "vorbis") - { - return "Vorbis"; - } - - if (audioFormat == "wmav1" || - audioFormat == "wmav2" || - audioFormat == "wmapro") - { - return "WMA"; + return AudioCodecHelper.GetDisplayName(codec); } Logger.ForDebugEvent() diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs index 39647e2396..25d2548d6a 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModel.cs @@ -43,6 +43,12 @@ public class MediaInfoModel : IEmbeddedDocument public string AudioProfile { get; set; } + public string BestAudioFormat { get; set; } + + public string BestAudioCodecID { get; set; } + + public string BestAudioProfile { get; set; } + public long AudioBitrate { get; set; } public TimeSpan RunTime { get; set; } diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs index cbfb17e888..4b3d01bd79 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs @@ -22,7 +22,7 @@ public class VideoFileInfoReader : IVideoFileInfoReader private readonly List _pixelFormats; public const int MINIMUM_MEDIA_INFO_SCHEMA_REVISION = 14; - public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 14; + public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 15; private static readonly string[] ValidHdrColourPrimaries = { "bt2020" }; private static readonly string[] HlgTransferFunctions = { "arib-std-b67" }; @@ -92,6 +92,11 @@ public MediaInfoModel GetMediaInfo(string filename) mediaInfoModel.AudioCodecID = analysis.PrimaryAudioStream?.CodecTagString; mediaInfoModel.AudioProfile = analysis.PrimaryAudioStream?.Profile; mediaInfoModel.AudioBitrate = GetBitrate(analysis.PrimaryAudioStream); + + var bestAudioStream = GetBestAudioStream(analysis.AudioStreams); + mediaInfoModel.BestAudioFormat = bestAudioStream?.CodecName; + mediaInfoModel.BestAudioCodecID = bestAudioStream?.CodecTagString; + mediaInfoModel.BestAudioProfile = bestAudioStream?.Profile; mediaInfoModel.RunTime = GetBestRuntime(analysis.PrimaryAudioStream?.Duration, primaryVideoStream?.Duration, analysis.Format.Duration); mediaInfoModel.AudioStreamCount = analysis.AudioStreams.Count; mediaInfoModel.AudioChannels = analysis.PrimaryAudioStream?.Channels ?? 0; @@ -194,6 +199,40 @@ private FFProbePixelFormat GetPixelFormat(string format) return _pixelFormats.Find(x => x.Name == format); } + private static AudioStream GetBestAudioStream(List audioStreams) + { + if (audioStreams == null || audioStreams.Count == 0) + { + return null; + } + + if (audioStreams.Count == 1) + { + return audioStreams[0]; + } + + AudioStream best = null; + var bestRank = -1; + + foreach (var stream in audioStreams) + { + var rank = GetAudioCodecRank(stream.CodecName, stream.CodecTagString, stream.Profile); + + if (rank > bestRank) + { + bestRank = rank; + best = stream; + } + } + + return best; + } + + private static int GetAudioCodecRank(string format, string codecID, string profile) + { + return (int)AudioCodecHelper.Resolve(format, codecID, profile); + } + public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string transferFunction, List sideData) { if (bitDepth < 10) diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 24d69c7cb4..4765134195 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -30,6 +30,7 @@ public class FileNameBuilder : IBuildFileNames { private const string MediaInfoVideoDynamicRangeToken = "{MediaInfo VideoDynamicRange}"; private const string MediaInfoVideoDynamicRangeTypeToken = "{MediaInfo VideoDynamicRangeType}"; + private const string MediaInfoBestAudioCodecToken = "{MediaInfo BestAudioCodec}"; private readonly INamingConfigService _namingConfigService; private readonly IQualityDefinitionService _qualityDefinitionService; @@ -366,7 +367,8 @@ private void AddQualityTokens(Dictionary> token new Dictionary(FileNameBuilderTokenEqualityComparer.Instance) { { MediaInfoVideoDynamicRangeToken, 5 }, - { MediaInfoVideoDynamicRangeTypeToken, 13 } + { MediaInfoVideoDynamicRangeTypeToken, 13 }, + { MediaInfoBestAudioCodecToken, 15 } }; private void AddMediaInfoTokens(Dictionary> tokenHandlers, MovieFile movieFile) @@ -415,6 +417,15 @@ private void AddMediaInfoTokens(Dictionary> tok m => MediaInfoFormatter.FormatVideoDynamicRange(movieFile.MediaInfo); tokenHandlers[MediaInfoVideoDynamicRangeTypeToken] = m => MediaInfoFormatter.FormatVideoDynamicRangeType(movieFile.MediaInfo); + + var bestAudioMediaInfo = new MediaInfoModel + { + AudioFormat = movieFile.MediaInfo.BestAudioFormat, + AudioCodecID = movieFile.MediaInfo.BestAudioCodecID, + AudioProfile = movieFile.MediaInfo.BestAudioProfile + }; + var bestAudioCodec = MediaInfoFormatter.FormatAudioCodec(bestAudioMediaInfo, sceneName) ?? string.Empty; + tokenHandlers[MediaInfoBestAudioCodecToken] = m => bestAudioCodec; } private void AddCustomFormats(Dictionary> tokenHandlers, Movie movie, MovieFile movieFile, List customFormats = null) diff --git a/src/NzbDrone.Core/Organizer/FileNameSampleService.cs b/src/NzbDrone.Core/Organizer/FileNameSampleService.cs index c04d036056..cd80dbbfdb 100644 --- a/src/NzbDrone.Core/Organizer/FileNameSampleService.cs +++ b/src/NzbDrone.Core/Organizer/FileNameSampleService.cs @@ -35,6 +35,9 @@ public FileNameSampleService(IBuildFileNames buildFileNames) AudioFormat = "DTS", AudioChannels = 6, AudioChannelPositions = "5.1", + BestAudioFormat = "dts", + BestAudioCodecID = string.Empty, + BestAudioProfile = "DTS-HD MA", AudioLanguages = new List { "ger" }, Subtitles = new List { "eng", "ger" } }; From 964ad105414a72e06e7a217e1d8fea4870f2aa57 Mon Sep 17 00:00:00 2001 From: Mate Herber Date: Thu, 5 Mar 2026 11:42:35 +0100 Subject: [PATCH 2/2] Refactor: Simplify AudioCodecHelper and GetBestAudioStream - Replace nested if/else chain with flat tuple pattern match - Replace manual loop with MaxBy in GetBestAudioStream - Remove unnecessary GetAudioCodecRank wrapper --- .../MediaFiles/MediaInfo/AudioCodec.cs | 192 +++++------------- .../MediaInfo/VideoFileInfoReader.cs | 31 +-- 2 files changed, 55 insertions(+), 168 deletions(-) diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs index ef0cad3bc9..7656bd8b94 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs @@ -30,147 +30,63 @@ public static class AudioCodecHelper { public static AudioCodec Resolve(string format, string codecID, string profile) { - format = format ?? string.Empty; - codecID = codecID ?? string.Empty; - profile = profile ?? string.Empty; + format ??= string.Empty; + codecID ??= string.Empty; + profile ??= string.Empty; - if (codecID == "thd+") + return (codecID, format, profile) switch { - return AudioCodec.TrueHDAtmos; - } - - if (format == "truehd") - { - return AudioCodec.TrueHD; - } - - if (format == "flac") - { - return AudioCodec.FLAC; - } - - if (format == "dts") - { - if (profile == "DTS:X") - { - return AudioCodec.DTS_X; - } - - if (profile == "DTS-HD MA") - { - return AudioCodec.DTS_HD_MA; - } - - if (profile == "DTS-ES") - { - return AudioCodec.DTS_ES; - } - - if (profile == "DTS-HD HRA") - { - return AudioCodec.DTS_HD_HRA; - } - - if (profile == "DTS Express") - { - return AudioCodec.DTS_Express; - } - - if (profile == "DTS 96/24") - { - return AudioCodec.DTS_9624; - } - - return AudioCodec.DTS; - } - - if (codecID == "ec+3") - { - return AudioCodec.EAC3Atmos; - } - - if (format == "eac3") - { - return AudioCodec.EAC3; - } - - if (format == "ac3") - { - return AudioCodec.AC3; - } - - if (format == "aac") - { - if (codecID == "A_AAC/MPEG4/LC/SBR") - { - return AudioCodec.HE_AAC; - } - - return AudioCodec.AAC; - } - - if (format == "mp3") - { - return AudioCodec.MP3; - } - - if (format == "mp2") - { - return AudioCodec.MP2; - } - - if (format == "opus") - { - return AudioCodec.Opus; - } - - if (format.StartsWith("pcm_") || format.StartsWith("adpcm_")) - { - return AudioCodec.PCM; - } - - if (format == "vorbis") - { - return AudioCodec.Vorbis; - } - - if (format == "wmav1" || - format == "wmav2" || - format == "wmapro") - { - return AudioCodec.WMA; - } - - return AudioCodec.Unknown; - } - - public static string GetDisplayName(AudioCodec codec) - { - return codec switch - { - AudioCodec.TrueHDAtmos => "TrueHD Atmos", - AudioCodec.TrueHD => "TrueHD", - AudioCodec.DTS_X => "DTS-X", - AudioCodec.DTS_HD_MA => "DTS-HD MA", - AudioCodec.DTS_HD_HRA => "DTS-HD HRA", - AudioCodec.DTS_ES => "DTS-ES", - AudioCodec.DTS_Express => "DTS Express", - AudioCodec.DTS_9624 => "DTS 96/24", - AudioCodec.DTS => "DTS", - AudioCodec.EAC3Atmos => "EAC3 Atmos", - AudioCodec.EAC3 => "EAC3", - AudioCodec.FLAC => "FLAC", - AudioCodec.AC3 => "AC3", - AudioCodec.HE_AAC => "HE-AAC", - AudioCodec.AAC => "AAC", - AudioCodec.MP3 => "MP3", - AudioCodec.MP2 => "MP2", - AudioCodec.Opus => "Opus", - AudioCodec.PCM => "PCM", - AudioCodec.Vorbis => "Vorbis", - AudioCodec.WMA => "WMA", - _ => string.Empty + ("thd+", _, _) => AudioCodec.TrueHDAtmos, + ("ec+3", _, _) => AudioCodec.EAC3Atmos, + (_, "truehd", _) => AudioCodec.TrueHD, + (_, "flac", _) => AudioCodec.FLAC, + (_, "dts", "DTS:X") => AudioCodec.DTS_X, + (_, "dts", "DTS-HD MA") => AudioCodec.DTS_HD_MA, + (_, "dts", "DTS-HD HRA") => AudioCodec.DTS_HD_HRA, + (_, "dts", "DTS-ES") => AudioCodec.DTS_ES, + (_, "dts", "DTS Express") => AudioCodec.DTS_Express, + (_, "dts", "DTS 96/24") => AudioCodec.DTS_9624, + (_, "dts", _) => AudioCodec.DTS, + (_, "eac3", _) => AudioCodec.EAC3, + (_, "ac3", _) => AudioCodec.AC3, + ("A_AAC/MPEG4/LC/SBR", "aac", _) => AudioCodec.HE_AAC, + (_, "aac", _) => AudioCodec.AAC, + (_, "mp3", _) => AudioCodec.MP3, + (_, "mp2", _) => AudioCodec.MP2, + (_, "opus", _) => AudioCodec.Opus, + (_, "vorbis", _) => AudioCodec.Vorbis, + (_, "wmav1", _) => AudioCodec.WMA, + (_, "wmav2", _) => AudioCodec.WMA, + (_, "wmapro", _) => AudioCodec.WMA, + _ when format.StartsWith("pcm_") || format.StartsWith("adpcm_") => AudioCodec.PCM, + _ => AudioCodec.Unknown }; } + + public static string GetDisplayName(AudioCodec codec) => codec switch + { + AudioCodec.TrueHDAtmos => "TrueHD Atmos", + AudioCodec.TrueHD => "TrueHD", + AudioCodec.DTS_X => "DTS-X", + AudioCodec.DTS_HD_MA => "DTS-HD MA", + AudioCodec.DTS_HD_HRA => "DTS-HD HRA", + AudioCodec.DTS_ES => "DTS-ES", + AudioCodec.DTS_Express => "DTS Express", + AudioCodec.DTS_9624 => "DTS 96/24", + AudioCodec.DTS => "DTS", + AudioCodec.EAC3Atmos => "EAC3 Atmos", + AudioCodec.EAC3 => "EAC3", + AudioCodec.FLAC => "FLAC", + AudioCodec.AC3 => "AC3", + AudioCodec.HE_AAC => "HE-AAC", + AudioCodec.AAC => "AAC", + AudioCodec.MP3 => "MP3", + AudioCodec.MP2 => "MP2", + AudioCodec.Opus => "Opus", + AudioCodec.PCM => "PCM", + AudioCodec.Vorbis => "Vorbis", + AudioCodec.WMA => "WMA", + _ => string.Empty + }; } } diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs index 4b3d01bd79..aa4ee66bcb 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/VideoFileInfoReader.cs @@ -201,36 +201,7 @@ private FFProbePixelFormat GetPixelFormat(string format) private static AudioStream GetBestAudioStream(List audioStreams) { - if (audioStreams == null || audioStreams.Count == 0) - { - return null; - } - - if (audioStreams.Count == 1) - { - return audioStreams[0]; - } - - AudioStream best = null; - var bestRank = -1; - - foreach (var stream in audioStreams) - { - var rank = GetAudioCodecRank(stream.CodecName, stream.CodecTagString, stream.Profile); - - if (rank > bestRank) - { - bestRank = rank; - best = stream; - } - } - - return best; - } - - private static int GetAudioCodecRank(string format, string codecID, string profile) - { - return (int)AudioCodecHelper.Resolve(format, codecID, profile); + return audioStreams?.MaxBy(stream => AudioCodecHelper.Resolve(stream.CodecName, stream.CodecTagString, stream.Profile)); } public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string transferFunction, List sideData)