mirror of
https://github.com/Radarr/Radarr
synced 2026-05-07 10:10:59 +02:00
Merge 964ad10541 into 9226876792
This commit is contained in:
commit
f532aaf749
9 changed files with 259 additions and 106 deletions
|
|
@ -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}',
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -775,6 +775,30 @@ public void should_not_update_media_info_if_token_configured_and_revision_is_new
|
|||
Mocker.GetMock<IUpdateMediaInfo>().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<string> { "eng" },
|
||||
Subtitles = new List<string> { "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,
|
||||
|
|
|
|||
92
src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs
Normal file
92
src/NzbDrone.Core/MediaFiles/MediaInfo/AudioCodec.cs
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
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 ??= string.Empty;
|
||||
codecID ??= string.Empty;
|
||||
profile ??= string.Empty;
|
||||
|
||||
return (codecID, format, profile) switch
|
||||
{
|
||||
("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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class VideoFileInfoReader : IVideoFileInfoReader
|
|||
private readonly List<FFProbePixelFormat> _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,11 @@ private FFProbePixelFormat GetPixelFormat(string format)
|
|||
return _pixelFormats.Find(x => x.Name == format);
|
||||
}
|
||||
|
||||
private static AudioStream GetBestAudioStream(List<AudioStream> audioStreams)
|
||||
{
|
||||
return audioStreams?.MaxBy(stream => AudioCodecHelper.Resolve(stream.CodecName, stream.CodecTagString, stream.Profile));
|
||||
}
|
||||
|
||||
public static HdrFormat GetHdrFormat(int bitDepth, string colorPrimaries, string transferFunction, List<SideData> sideData)
|
||||
{
|
||||
if (bitDepth < 10)
|
||||
|
|
|
|||
|
|
@ -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<string, Func<TokenMatch, string>> token
|
|||
new Dictionary<string, int>(FileNameBuilderTokenEqualityComparer.Instance)
|
||||
{
|
||||
{ MediaInfoVideoDynamicRangeToken, 5 },
|
||||
{ MediaInfoVideoDynamicRangeTypeToken, 13 }
|
||||
{ MediaInfoVideoDynamicRangeTypeToken, 13 },
|
||||
{ MediaInfoBestAudioCodecToken, 15 }
|
||||
};
|
||||
|
||||
private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> tokenHandlers, MovieFile movieFile)
|
||||
|
|
@ -415,6 +417,15 @@ private void AddMediaInfoTokens(Dictionary<string, Func<TokenMatch, string>> 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<string, Func<TokenMatch, string>> tokenHandlers, Movie movie, MovieFile movieFile, List<CustomFormat> customFormats = null)
|
||||
|
|
|
|||
|
|
@ -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<string> { "ger" },
|
||||
Subtitles = new List<string> { "eng", "ger" }
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue