New: Add {MediaInfo VideoDynamicRangeType} token for renaming

New: Detect HDR Type
New: Display HDR Type in File Media Info Modal

Based on Sonarr 7b694ea71d7f78bad5c03393c4cf6f7a28ada1cb

Closes #6789
Fixes #4844

Co-authored-by: ta264 <ta264@users.noreply.github.com>
Co-authored-by: Qstick <qstick@gmail.com>
pull/6884/head
bakerboy448 3 years ago committed by Qstick
parent 9e4c94592d
commit 13e44ce19a

@ -147,7 +147,8 @@ class NamingModal extends Component {
{ token: '{MediaInfo VideoCodec}', example: 'x264' },
{ token: '{MediaInfo VideoBitDepth}', example: '10' },
{ token: '{MediaInfo VideoDynamicRange}', example: 'HDR' }
{ token: '{MediaInfo VideoDynamicRange}', example: 'HDR' },
{ token: '{MediaInfo VideoDynamicRangeType}', example: 'DV HDR10' }
];
const releaseGroupTokens = [

@ -0,0 +1,31 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormatVideoDynamicRangeTypeFixture : TestBase
{
[TestCase(HdrFormat.None, "")]
[TestCase(HdrFormat.Hlg10, "HLG")]
[TestCase(HdrFormat.Pq10, "PQ")]
[TestCase(HdrFormat.Hdr10, "HDR10")]
[TestCase(HdrFormat.Hdr10Plus, "HDR10Plus")]
[TestCase(HdrFormat.DolbyVision, "DV")]
[TestCase(HdrFormat.DolbyVisionHdr10, "DV HDR10")]
[TestCase(HdrFormat.DolbyVisionHlg, "DV HLG")]
[TestCase(HdrFormat.DolbyVisionSdr, "DV SDR")]
public void should_format_video_dynamic_range_type(HdrFormat format, string expectedVideoDynamicRangeType)
{
var mediaInfo = new MediaInfoModel
{
VideoHdrFormat = format,
SchemaRevision = 9
};
MediaInfoFormatter.FormatVideoDynamicRangeType(mediaInfo).Should().Be(expectedVideoDynamicRangeType);
}
}
}

@ -102,24 +102,38 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
info.VideoTransferCharacteristics.Should().Be("bt709");
}
[TestCase(8, "", "", "", HdrFormat.None)]
[TestCase(10, "", "", "", HdrFormat.None)]
[TestCase(10, "bt709", "bt709", "", HdrFormat.None)]
[TestCase(8, "bt2020", "smpte2084", "", HdrFormat.None)]
[TestCase(10, "bt2020", "bt2020-10", "", HdrFormat.Hlg10)]
[TestCase(10, "bt2020", "arib-std-b67", "", HdrFormat.Hlg10)]
[TestCase(10, "bt2020", "smpte2084", "", HdrFormat.Pq10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.SideData", HdrFormat.Pq10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.MasteringDisplayMetadata", HdrFormat.Hdr10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.ContentLightLevelMetadata", HdrFormat.Hdr10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.HdrDynamicMetadataSpmte2094", HdrFormat.Hdr10Plus)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", HdrFormat.DolbyVision)]
public void should_detect_hdr_correctly(int bitDepth, string colourPrimaries, string transferFunction, string sideDataTypes, HdrFormat expected)
[TestCase(8, "", "", "", null, HdrFormat.None)]
[TestCase(10, "", "", "", null, HdrFormat.None)]
[TestCase(10, "bt709", "bt709", "", null, HdrFormat.None)]
[TestCase(8, "bt2020", "smpte2084", "", null, HdrFormat.None)]
[TestCase(10, "bt2020", "bt2020-10", "", null, HdrFormat.Hlg10)]
[TestCase(10, "bt2020", "arib-std-b67", "", null, HdrFormat.Hlg10)]
[TestCase(10, "bt2020", "smpte2084", "", null, HdrFormat.Pq10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.SideData", null, HdrFormat.Pq10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.MasteringDisplayMetadata", null, HdrFormat.Hdr10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.ContentLightLevelMetadata", null, HdrFormat.Hdr10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.HdrDynamicMetadataSpmte2094", null, HdrFormat.Hdr10Plus)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", null, HdrFormat.DolbyVision)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", 1, HdrFormat.DolbyVisionHdr10)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", 2, HdrFormat.DolbyVisionSdr)]
[TestCase(10, "bt2020", "smpte2084", "FFMpegCore.DoviConfigurationRecordSideData", 4, HdrFormat.DolbyVisionHlg)]
public void should_detect_hdr_correctly(int bitDepth, string colourPrimaries, string transferFunction, string sideDataTypes, int? doviConfigId, HdrFormat expected)
{
var assembly = Assembly.GetAssembly(typeof(FFProbe));
var types = sideDataTypes.Split(",").Select(x => x.Trim()).ToList();
var sideData = types.Where(x => x.IsNotNullOrWhiteSpace()).Select(x => assembly.CreateInstance(x)).Cast<SideData>().ToList();
if (doviConfigId.HasValue)
{
sideData.ForEach(x =>
{
if (x.GetType().Name == "DoviConfigurationRecordSideData")
{
((DoviConfigurationRecordSideData)x).DvBlSignalCompatibilityId = doviConfigId.Value;
}
});
}
var result = VideoFileInfoReader.GetHdrFormat(bitDepth, colourPrimaries, transferFunction, sideData);
result.Should().Be(expected);

@ -7,6 +7,9 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
Hdr10,
Hdr10Plus,
Hlg10,
DolbyVision
DolbyVision,
DolbyVisionHdr10,
DolbyVisionSdr,
DolbyVisionHlg
}
}

@ -303,5 +303,30 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
{
return mediaInfo.VideoHdrFormat != HdrFormat.None ? VideoDynamicRangeHdr : "";
}
public static string FormatVideoDynamicRangeType(MediaInfoModel mediaInfo)
{
switch (mediaInfo.VideoHdrFormat)
{
case HdrFormat.DolbyVision:
return "DV";
case HdrFormat.DolbyVisionHdr10:
return "DV HDR10";
case HdrFormat.DolbyVisionHlg:
return "DV HLG";
case HdrFormat.DolbyVisionSdr:
return "DV SDR";
case HdrFormat.Hdr10:
return "HDR10";
case HdrFormat.Hdr10Plus:
return "HDR10Plus";
case HdrFormat.Hlg10:
return "HLG";
case HdrFormat.Pq10:
return "PQ";
}
return "";
}
}
}

@ -22,7 +22,7 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
private readonly List<FFProbePixelFormat> _pixelFormats;
public const int MINIMUM_MEDIA_INFO_SCHEMA_REVISION = 8;
public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 8;
public const int CURRENT_MEDIA_INFO_SCHEMA_REVISION = 9;
private static readonly string[] ValidHdrColourPrimaries = { "bt2020" };
private static readonly string[] HlgTransferFunctions = { "bt2020-10", "arib-std-b67" };
@ -162,9 +162,15 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
return HdrFormat.None;
}
if (TryFindSideData(sideData, nameof(DoviConfigurationRecordSideData)))
if (TryGetSideData<DoviConfigurationRecordSideData>(sideData, out var dovi))
{
return HdrFormat.DolbyVision;
return dovi.DvBlSignalCompatibilityId switch
{
1 => HdrFormat.DolbyVisionHdr10,
2 => HdrFormat.DolbyVisionSdr,
4 => HdrFormat.DolbyVisionHlg,
_ => HdrFormat.DolbyVision
};
}
if (!ValidHdrColourPrimaries.Contains(colorPrimaries) || !ValidHdrTransferFunctions.Contains(transferFunction))
@ -179,13 +185,13 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
if (PqTransferFunctions.Contains(transferFunction))
{
if (TryFindSideData(sideData, nameof(HdrDynamicMetadataSpmte2094)))
if (TryGetSideData<HdrDynamicMetadataSpmte2094>(sideData, out _))
{
return HdrFormat.Hdr10Plus;
}
if (TryFindSideData(sideData, nameof(MasteringDisplayMetadata)) ||
TryFindSideData(sideData, nameof(ContentLightLevelMetadata)))
if (TryGetSideData<MasteringDisplayMetadata>(sideData, out _) ||
TryGetSideData<ContentLightLevelMetadata>(sideData, out _))
{
return HdrFormat.Hdr10;
}
@ -196,9 +202,12 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
return HdrFormat.None;
}
private static bool TryFindSideData(List<SideData> list, string typeName)
private static bool TryGetSideData<T>(List<SideData> list, out T result)
where T : SideData
{
return list?.Find(x => x.GetType().Name == typeName) != null;
result = (T)list?.FirstOrDefault(x => x.GetType().Name == typeof(T).Name);
return result != null;
}
}
}

@ -30,6 +30,7 @@ namespace NzbDrone.Core.Organizer
public class FileNameBuilder : IBuildFileNames
{
private const string MediaInfoVideoDynamicRangeToken = "{MediaInfo VideoDynamicRange}";
private const string MediaInfoVideoDynamicRangeTypeToken = "{MediaInfo VideoDynamicRangeType}";
private readonly INamingConfigService _namingConfigService;
private readonly IQualityDefinitionService _qualityDefinitionService;
@ -353,7 +354,8 @@ namespace NzbDrone.Core.Organizer
private static readonly IReadOnlyDictionary<string, int> MinimumMediaInfoSchemaRevisions =
new Dictionary<string, int>(FileNameBuilderTokenEqualityComparer.Instance)
{
{ MediaInfoVideoDynamicRangeToken, 5 }
{ MediaInfoVideoDynamicRangeToken, 5 },
{ MediaInfoVideoDynamicRangeTypeToken, 9 }
};
private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, MovieFile movieFile)
@ -418,6 +420,8 @@ namespace NzbDrone.Core.Organizer
tokenHandlers[MediaInfoVideoDynamicRangeToken] =
m => MediaInfoFormatter.FormatVideoDynamicRange(movieFile.MediaInfo);
tokenHandlers[MediaInfoVideoDynamicRangeTypeToken] =
m => MediaInfoFormatter.FormatVideoDynamicRangeType(movieFile.MediaInfo);
}
private void AddCustomFormats(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, Movie movie, MovieFile movieFile, List<CustomFormat> customFormats = null)

@ -15,6 +15,7 @@ namespace Radarr.Api.V3.MovieFiles
public int VideoBitDepth { get; set; }
public int VideoBitrate { get; set; }
public string VideoCodec { get; set; }
public string VideoDynamicRangeType { get; set; }
public decimal VideoFps { get; set; }
public string Resolution { get; set; }
public string RunTime { get; set; }
@ -41,6 +42,7 @@ namespace Radarr.Api.V3.MovieFiles
VideoBitDepth = model.VideoBitDepth,
VideoBitrate = model.VideoBitrate,
VideoCodec = MediaInfoFormatter.FormatVideoCodec(model, sceneName),
VideoDynamicRangeType = MediaInfoFormatter.FormatVideoDynamicRangeType(model),
VideoFps = Math.Round(model.VideoFps, 3),
Resolution = $"{model.Width}x{model.Height}",
RunTime = FormatRuntime(model.RunTime),

Loading…
Cancel
Save