From 7b694ea71d7f78bad5c03393c4cf6f7a28ada1cb Mon Sep 17 00:00:00 2001 From: Alan Collins Date: Sun, 5 Dec 2021 12:41:27 -0600 Subject: [PATCH] New: Add {MediaInfo VideoDynamicRangeType} token for renaming --- .../FormatVideoDynamicRangeFixture.cs | 36 ++++++++- .../MediaInfoModelExtensionsFixture.cs | 48 ++++++++++++ .../MediaFiles/MediaInfo/HdrFormat.cs | 20 +++++ .../MediaInfo/MediaInfoFormatter.cs | 35 +++++---- .../MediaInfo/MediaInfoModelExtensions.cs | 76 +++++++++++++++++++ .../Organizer/FileNameBuilder.cs | 6 +- 6 files changed, 202 insertions(+), 19 deletions(-) create mode 100644 src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoModelExtensionsFixture.cs create mode 100644 src/NzbDrone.Core/MediaFiles/MediaInfo/HdrFormat.cs create mode 100644 src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModelExtensions.cs diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatVideoDynamicRangeFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatVideoDynamicRangeFixture.cs index de5c22a15..06903a4ff 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatVideoDynamicRangeFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoFormatterTests/FormatVideoDynamicRangeFixture.cs @@ -17,8 +17,8 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests [TestCase(10, "BT.2020", "HLG", "", "", "HDR")] [TestCase(10, "", "", "Dolby Vision", "", "HDR")] [TestCase(10, "", "", "SMPTE ST 2086", "HDR10", "HDR")] - [TestCase(8, "", "", "Dolby Vision", "", "HDR")] - [TestCase(8, "", "", "SMPTE ST 2086", "HDR10", "HDR")] + [TestCase(8, "", "", "Dolby Vision", "", "")] + [TestCase(8, "", "", "SMPTE ST 2086", "HDR10", "")] [TestCase(10, "BT.2020", "PQ", "Dolby Vision / SMPTE ST 2086", "Blu-ray / HDR10", "HDR")] public void should_format_video_dynamic_range(int bitDepth, string colourPrimaries, string transferCharacteristics, string hdrFormat, string hdrFormatCompatibility, string expectedVideoDynamicRange) { @@ -34,5 +34,37 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests MediaInfoFormatter.FormatVideoDynamicRange(mediaInfo).Should().Be(expectedVideoDynamicRange); } + + [TestCase(8, "", "", "", "")] + [TestCase(8, "BT.601 NTSC", "BT.709", "", "")] + [TestCase(8, "BT.2020", "PQ", "", "")] + [TestCase(8, "", "", "Dolby Vision", "")] + [TestCase(8, "", "", "SMPTE ST 2086", "")] + [TestCase(10, "BT.601 NTSC", "PQ", "", "")] + [TestCase(10, "BT.2020", "BT.709", "", "")] + [TestCase(10, "BT.2020", "PQ", "", "PQ")] + [TestCase(10, "BT.2020", "HLG", "", "HLG")] + [TestCase(10, "", "", "SMPTE ST 2086", "HDR10")] + [TestCase(10, "", "", "HDR10", "HDR10")] + [TestCase(10, "", "", "SMPTE ST 2094 App 4", "HDR10Plus")] + [TestCase(10, "", "", "Dolby Vision", "DV")] + [TestCase(10, "BT.2020", "PQ", "Dolby Vision / SMPTE ST 2086", "DV HDR10")] + [TestCase(10, "", "", "SL-HDR1", "HDR")] + [TestCase(10, "", "", "SL-HDR2", "HDR")] + [TestCase(10, "", "", "SL-HDR3", "HDR")] + [TestCase(10, "", "", "Technicolor Advanced HDR", "HDR")] + public void should_format_video_dynamic_range_type(int bitDepth, string colourPrimaries, string transferCharacteristics, string hdrFormat, string expectedVideoDynamicRange) + { + var mediaInfo = new MediaInfoModel + { + VideoBitDepth = bitDepth, + VideoColourPrimaries = colourPrimaries, + VideoTransferCharacteristics = transferCharacteristics, + VideoHdrFormat = hdrFormat, + SchemaRevision = 7 + }; + + MediaInfoFormatter.FormatVideoDynamicRangeType(mediaInfo).Should().Be(expectedVideoDynamicRange); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoModelExtensionsFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoModelExtensionsFixture.cs new file mode 100644 index 000000000..86e423e21 --- /dev/null +++ b/src/NzbDrone.Core.Test/MediaFiles/MediaInfo/MediaInfoModelExtensionsFixture.cs @@ -0,0 +1,48 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.MediaFiles.MediaInfo; +using NzbDrone.Test.Common; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.Test.MediaFiles.MediaInfo +{ + [TestFixture] + public class MediaInfoModelExtensionsFixture : TestBase + { + [TestCase(8, "", "", "", HdrFormat.None)] + [TestCase(8, "BT.601 NTSC", "BT.709", "", HdrFormat.None)] + [TestCase(8, "BT.2020", "PQ", "", HdrFormat.None)] + [TestCase(8, "", "", "Dolby Vision", HdrFormat.None)] + [TestCase(8, "", "", "SMPTE ST 2086", HdrFormat.None)] + [TestCase(10, "BT.601 NTSC", "PQ", "", HdrFormat.None)] + [TestCase(10, "BT.2020", "BT.709", "", HdrFormat.None)] + [TestCase(10, "BT.2020", "PQ", "", HdrFormat.Pq10)] + [TestCase(10, "BT.2020", "HLG", "", HdrFormat.Hlg10)] + [TestCase(10, "", "", "SMPTE ST 2086", HdrFormat.Hdr10)] + [TestCase(10, "", "", "HDR10", HdrFormat.Hdr10)] + [TestCase(10, "", "", "SMPTE ST 2094 App 4", HdrFormat.Hdr10Plus)] + [TestCase(10, "", "", "Dolby Vision", HdrFormat.DolbyVision)] + [TestCase(10, "BT.2020", "PQ", "Dolby Vision / SMPTE ST 2086", HdrFormat.DolbyVisionHdr10)] + [TestCase(10, "", "", "SL-HDR1", HdrFormat.UnknownHdr)] + [TestCase(10, "", "", "SL-HDR2", HdrFormat.UnknownHdr)] + [TestCase(10, "", "", "SL-HDR3", HdrFormat.UnknownHdr)] + [TestCase(10, "", "", "Technicolor Advanced HDR", HdrFormat.UnknownHdr)] + public void should_get_hdr_format(int bitDepth, string colourPrimaries, string transferCharacteristics, string hdrFormat, HdrFormat expectedVideoDynamicRange) + { + var mediaInfo = new MediaInfoModel + { + VideoBitDepth = bitDepth, + VideoColourPrimaries = colourPrimaries, + VideoTransferCharacteristics = transferCharacteristics, + VideoHdrFormat = hdrFormat, + SchemaRevision = 7 + }; + + MediaInfoModelExtensions.GetHdrFormat(mediaInfo).Should().Be(expectedVideoDynamicRange); + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/HdrFormat.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/HdrFormat.cs new file mode 100644 index 000000000..aa67cdb26 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/HdrFormat.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.MediaFiles.MediaInfo +{ + public enum HdrFormat + { + None, + UnknownHdr, + Pq10, + Hdr10, + Hdr10Plus, + Hlg10, + DolbyVision, + DolbyVisionHdr10 + } +} diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs index 872097434..169e56eb7 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoFormatter.cs @@ -585,26 +585,29 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo return tokens.Last(); } - private static readonly string[] ValidHdrTransferFunctions = {"PQ", "HLG"}; - private const string ValidHdrColourPrimaries = "BT.2020"; - private const string VideoDynamicRangeHdr = "HDR"; - public static string FormatVideoDynamicRange(MediaInfoModel mediaInfo) { - if (mediaInfo.VideoHdrFormat.IsNotNullOrWhiteSpace()) - { - return VideoDynamicRangeHdr; - } + return mediaInfo.GetHdrFormat() == HdrFormat.None ? "" : "HDR"; + } - if (mediaInfo.VideoBitDepth >= 10 && - mediaInfo.VideoColourPrimaries.IsNotNullOrWhiteSpace() && - mediaInfo.VideoTransferCharacteristics.IsNotNullOrWhiteSpace()) + public static string FormatVideoDynamicRangeType(MediaInfoModel mediaInfo) + { + switch(mediaInfo.GetHdrFormat()) { - if (mediaInfo.VideoColourPrimaries.EqualsIgnoreCase(ValidHdrColourPrimaries) && - ValidHdrTransferFunctions.Any(mediaInfo.VideoTransferCharacteristics.Contains)) - { - return VideoDynamicRangeHdr; - } + case HdrFormat.DolbyVision: + return "DV"; + case HdrFormat.DolbyVisionHdr10: + return "DV HDR10"; + case HdrFormat.Hdr10: + return "HDR10"; + case HdrFormat.Hdr10Plus: + return "HDR10Plus"; + case HdrFormat.Hlg10: + return "HLG"; + case HdrFormat.Pq10: + return "PQ"; + case HdrFormat.UnknownHdr: + return "HDR"; } return ""; diff --git a/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModelExtensions.cs b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModelExtensions.cs new file mode 100644 index 000000000..9a7b97672 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/MediaInfo/MediaInfoModelExtensions.cs @@ -0,0 +1,76 @@ +using NzbDrone.Common.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NzbDrone.Core.MediaFiles.MediaInfo +{ + public static class MediaInfoModelExtensions + { + private const string HlgTransferFunction = "HLG"; + private const string PgTransferFunction = "PQ"; + private const string ValidHdrColourPrimaries = "BT.2020"; + + private static readonly string[] Hdr10Formats = { "SMPTE ST 2086", "HDR10" }; + private static readonly string[] Hdr10PlusFormats = { "SMPTE ST 2094 App 4" }; + private static readonly string[] DolbyVisionFormats = { "Dolby Vision" }; + + public static HdrFormat GetHdrFormat(this MediaInfoModel mediaInfo) + { + if (mediaInfo.VideoBitDepth < 10) + { + return HdrFormat.None; + } + + if (mediaInfo.VideoHdrFormat.IsNotNullOrWhiteSpace()) + { + if(DolbyVisionFormats.Any(mediaInfo.VideoHdrFormat.ContainsIgnoreCase)) + { + if(Hdr10Formats.Any(mediaInfo.VideoHdrFormat.ContainsIgnoreCase)) + { + return HdrFormat.DolbyVisionHdr10; + } + else + { + return HdrFormat.DolbyVision; + } + } + else if(Hdr10Formats.Any(mediaInfo.VideoHdrFormat.ContainsIgnoreCase)) + { + return HdrFormat.Hdr10; + } + else if(Hdr10PlusFormats.Any(mediaInfo.VideoHdrFormat.ContainsIgnoreCase)) + { + return HdrFormat.Hdr10Plus; + } + } + + // + // We didn't match straight from the format from MediaInfo, so try and match in ColourPrimaries and TransferCharacteristics + // + if (mediaInfo.VideoColourPrimaries.IsNotNullOrWhiteSpace() && mediaInfo.VideoTransferCharacteristics.IsNotNullOrWhiteSpace()) + { + if (mediaInfo.VideoColourPrimaries.EqualsIgnoreCase(ValidHdrColourPrimaries)) + { + if (mediaInfo.VideoTransferCharacteristics.EqualsIgnoreCase(PgTransferFunction)) + { + return HdrFormat.Pq10; + } + else if (mediaInfo.VideoTransferCharacteristics.EqualsIgnoreCase(HlgTransferFunction)) + { + return HdrFormat.Hlg10; + } + } + } + + if (mediaInfo.VideoHdrFormat.IsNotNullOrWhiteSpace()) + { + return HdrFormat.UnknownHdr; // MediaInfo reported Hdr information, but we're unsure of what type. + } + + return HdrFormat.None; + } + } +} diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 2af9a08b3..c6f8995b0 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -603,10 +603,12 @@ namespace NzbDrone.Core.Organizer } private const string MediaInfoVideoDynamicRangeToken = "{MediaInfo VideoDynamicRange}"; + private const string MediaInfoVideoDynamicRangeTypeToken = "{MediaInfo VideoDynamicRangeType}"; private static readonly IDictionary MinimumMediaInfoSchemaRevisions = new Dictionary(FileNameBuilderTokenEqualityComparer.Instance) { - {MediaInfoVideoDynamicRangeToken, 5} + {MediaInfoVideoDynamicRangeToken, 5}, + {MediaInfoVideoDynamicRangeTypeToken, 5} }; private void AddMediaInfoTokens(Dictionary> tokenHandlers, EpisodeFile episodeFile) @@ -650,6 +652,8 @@ namespace NzbDrone.Core.Organizer tokenHandlers[MediaInfoVideoDynamicRangeToken] = m => MediaInfoFormatter.FormatVideoDynamicRange(episodeFile.MediaInfo); + tokenHandlers[MediaInfoVideoDynamicRangeTypeToken] = + m => MediaInfoFormatter.FormatVideoDynamicRangeType(episodeFile.MediaInfo); } private void AddIdTokens(Dictionary> tokenHandlers, Series series)