mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-20 18:02:44 +01:00
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>
This commit is contained in:
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 @@ public void get_info_unicode()
|
||||
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 @@ public enum HdrFormat
|
||||
Hdr10,
|
||||
Hdr10Plus,
|
||||
Hlg10,
|
||||
DolbyVision
|
||||
DolbyVision,
|
||||
DolbyVisionHdr10,
|
||||
DolbyVisionSdr,
|
||||
DolbyVisionHlg
|
||||
}
|
||||
}
|
||||
|
@ -303,5 +303,30 @@ public static string FormatVideoDynamicRange(MediaInfoModel 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 @@ public class VideoFileInfoReader : IVideoFileInfoReader
|
||||
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 @@ public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string
|
||||
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 @@ public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string
|
||||
|
||||
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 @@ public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string
|
||||
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 @@ public interface IBuildFileNames
|
||||
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 @@ private void AddQualityTokens(Dictionary<string, Func<TokenMatch, string>> token
|
||||
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 @@ private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tok
|
||||
|
||||
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 @@ public class MediaInfoResource : RestResource
|
||||
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 @@ public static MediaInfoResource ToResource(this MediaInfoModel model, string sce
|
||||
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…
Reference in New Issue
Block a user