From 4399d272dca42891fcf585fd5fdeaf141803b808 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 3 Dec 2017 19:58:31 -0800 Subject: [PATCH] Fixed: Import failures when audio channels are in an unexpected format Fixes #2318 --- .../FormatAudioChannelsFixture.cs | 28 +++++ .../MediaInfo/MediaInfoFormatter.cs | 109 +++++++++++++----- 2 files changed, 108 insertions(+), 29 deletions(-) diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatAudioChannelsFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatAudioChannelsFixture.cs index 5de655cd5..7a80c9af4 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatAudioChannelsFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatAudioChannelsFixture.cs @@ -145,5 +145,33 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2.0m); } + + [Test] + public void should_use_AudioChannelPositionText_when_AudioChannelChannelPosition_is_invalid() + { + var mediaInfoModel = new MediaInfoModel + { + AudioChannels = 6, + AudioChannelPositions = "15 objects", + AudioChannelPositionsText = "15 objects / Front: L C R, Side: L R, LFE", + SchemaRevision = 3 + }; + + MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m); + } + + [Test] + public void should_remove_atmos_objects_from_AudioChannelPostions() + { + var mediaInfoModel = new MediaInfoModel + { + AudioChannels = 2, + AudioChannelPositions = "15 objects / 3/2.1", + AudioChannelPositionsText = null, + SchemaRevision = 3 + }; + + MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m); + } } } diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs index 52631b78c..af6f14475 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; using System.IO; using System.Linq; +using System.Text.RegularExpressions; using NLog; using NLog.Fluent; using NzbDrone.Common.Extensions; @@ -16,42 +17,19 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo public static decimal FormatAudioChannels(MediaInfoModel mediaInfo) { - var audioChannelPositions = mediaInfo.AudioChannelPositions; - var audioChannelPositionsText = mediaInfo.AudioChannelPositionsText; - var audioChannels = mediaInfo.AudioChannels; + var audioChannels = FormatAudioChannelsFromAudioChannelPositions(mediaInfo); - if (audioChannelPositions.IsNullOrWhiteSpace()) + if (audioChannels == null) { - if (audioChannelPositionsText.IsNullOrWhiteSpace()) - { - if (mediaInfo.SchemaRevision >= 3) - { - Logger.Debug("Formatiting audio channels using 'AudioChannels', with a value of: '{0}'", audioChannels); - - return audioChannels; - } - - return 0; - } - - Logger.Debug("Formatiting audio channels using 'AudioChannelPositionsText', with a value of: '{0}'", audioChannelPositionsText); - - return mediaInfo.AudioChannelPositionsText.ContainsIgnoreCase("LFE") ? audioChannels - 1 + 0.1m : audioChannels; + audioChannels = FormatAudioChannelsFromAudioChannelPositionsText(mediaInfo); } - Logger.Debug("Formatiting audio channels using 'AudioChannelPositions', with a value of: '{0}'", audioChannelPositions); - - if (audioChannelPositions.Contains("+")) + if (audioChannels == null) { - return audioChannelPositions.Split('+') - .Sum(s => decimal.Parse(s.Trim(), CultureInfo.InvariantCulture)); + audioChannels = FormatAudioChannelsFromAudioChannels(mediaInfo); } - return audioChannelPositions.Replace("Object Based / ", "") - .Split(new string[] { " / " }, StringSplitOptions.RemoveEmptyEntries) - .First() - .Split('/') - .Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture)); + return audioChannels ?? 0; } public static string FormatAudioCodec(MediaInfoModel mediaInfo, string sceneName) @@ -360,6 +338,79 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo return videoCodec; } + private static decimal? FormatAudioChannelsFromAudioChannelPositions(MediaInfoModel mediaInfo) + { + var audioChannelPositions = mediaInfo.AudioChannelPositions; + + if (audioChannelPositions.IsNullOrWhiteSpace()) + { + return null; + } + + try + { + Logger.Debug("Formatiting audio channels using 'AudioChannelPositions', with a value of: '{0}'", audioChannelPositions); + + if (audioChannelPositions.Contains("+")) + { + return audioChannelPositions.Split('+') + .Sum(s => decimal.Parse(s.Trim(), CultureInfo.InvariantCulture)); + } + + + return Regex.Replace(audioChannelPositions, @"^\d+\sobjects", "", RegexOptions.Compiled | RegexOptions.IgnoreCase) + .Replace("Object Based / ", "") + .Split(new string[] { " / " }, StringSplitOptions.RemoveEmptyEntries) + .FirstOrDefault() + ?.Split('/') + .Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture)); + } + catch (Exception e) + { + Logger.Warn(e, "Unable to format audio channels using 'AudioChannelPositions'"); + } + + return null; + } + + private static decimal? FormatAudioChannelsFromAudioChannelPositionsText(MediaInfoModel mediaInfo) + { + var audioChannelPositionsText = mediaInfo.AudioChannelPositionsText; + var audioChannels = mediaInfo.AudioChannels; + + if (audioChannelPositionsText.IsNullOrWhiteSpace()) + { + return null; + } + + try + { + Logger.Debug("Formatiting audio channels using 'AudioChannelPositionsText', with a value of: '{0}'", audioChannelPositionsText); + + return mediaInfo.AudioChannelPositionsText.ContainsIgnoreCase("LFE") ? audioChannels - 1 + 0.1m : audioChannels; + } + catch (Exception e) + { + Logger.Warn(e, "Unable to format audio channels using 'AudioChannelPositionsText'"); + } + + return null; + } + + private static decimal? FormatAudioChannelsFromAudioChannels(MediaInfoModel mediaInfo) + { + var audioChannels = mediaInfo.AudioChannels; + + if (mediaInfo.SchemaRevision >= 3) + { + Logger.Debug("Formatiting audio channels using 'AudioChannels', with a value of: '{0}'", audioChannels); + + return audioChannels; + } + + return null; + } + private static string GetSceneNameMatch(string sceneName, params string[] tokens) { sceneName = sceneName.IsNotNullOrWhiteSpace() ? Path.GetFileNameWithoutExtension(sceneName) : string.Empty;