Consistent formatting for MediaInfo in various locations

Fixed: Stream details for MP3 and EAC3 in Kodi metadata
Closes #1534
This commit is contained in:
Mark McDowall 2017-05-19 11:18:50 -07:00 committed by GitHub
parent edf549d0fd
commit 0d782e1cac
9 changed files with 218 additions and 124 deletions

View File

@ -1,11 +1,12 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormattedAudioChannelsFixture
public class FormatAudioChannelsFixture : TestBase
{
[Test]
public void should_subtract_one_from_AudioChannels_as_total_channels_if_LFE_in_AudioChannelPositionsText()
@ -17,7 +18,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
AudioChannelPositionsText = "Front: L C R, Side: L R, LFE"
};
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
}
[Test]
@ -30,7 +31,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
AudioChannelPositionsText = "Front: L R"
};
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
}
[Test]
@ -44,7 +45,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 2
};
mediaInfoModel.FormattedAudioChannels.Should().Be(0);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(0);
}
[Test]
@ -58,7 +59,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
}
[Test]
@ -72,7 +73,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(2);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
}
[Test]
@ -86,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(5.1m);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
}
[Test]
@ -100,7 +101,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
}
[Test]
@ -114,7 +115,7 @@ namespace NzbDrone.Core.Test.MediaFiles.MediaInfo
SchemaRevision = 3
};
mediaInfoModel.FormattedAudioChannels.Should().Be(7.1m);
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
}
}
}

View File

@ -0,0 +1,49 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormatAudioCodecFixture : TestBase
{
[TestCase("AC-3", "AC3")]
[TestCase("E-AC-3", "EAC3")]
[TestCase("MPEG Audio", "MPEG Audio")]
[TestCase("DTS", "DTS")]
public void should_format_audio_format(string audioFormat, string expectedFormat)
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = audioFormat
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be(expectedFormat);
}
[Test]
public void should_return_MP3_for_MPEG_Audio_with_Layer_3_for_the_profile()
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = "MPEG Audio",
AudioProfile = "Layer 3"
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be("MP3");
}
[Test]
public void should_return_AudioFormat_by_default()
{
var mediaInfoModel = new MediaInfoModel
{
AudioFormat = "Other Audio Format"
};
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel).Should().Be(mediaInfoModel.AudioFormat);
ExceptionVerification.ExpectedErrors(1);
}
}
}

View File

@ -0,0 +1,40 @@
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
{
[TestFixture]
public class FormatVideoCodecFixture : TestBase
{
[TestCase("AVC", null, "x264")]
[TestCase("AVC", "source.title.x264.720p-Sonarr", "x264")]
[TestCase("AVC", "source.title.h264.720p-Sonarr", "h264")]
[TestCase("V_MPEGH/ISO/HEVC", null, "x265")]
[TestCase("V_MPEGH/ISO/HEVC", "source.title.x265.720p-Sonarr", "x265")]
[TestCase("V_MPEGH/ISO/HEVC", "source.title.h265.720p-Sonarr", "h265")]
[TestCase("MPEG-2 Video", null, "MPEG2")]
public void should_format_video_codec_with_source_title(string videoCodec, string sceneName, string expectedFormat)
{
var mediaInfoModel = new MediaInfoModel
{
VideoCodec = videoCodec
};
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
}
[Test]
public void should_return_VideoCodec_by_default()
{
var mediaInfoModel = new MediaInfoModel
{
VideoCodec = "VideoCodec"
};
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, null).Should().Be(mediaInfoModel.VideoCodec);
ExceptionVerification.ExpectedErrors(1);
}
}
}

View File

@ -291,7 +291,9 @@
<Compile Include="MediaFiles\EpisodeImport\Specifications\UpgradeSpecificationFixture.cs" />
<Compile Include="MediaFiles\ImportApprovedEpisodesFixture.cs" />
<Compile Include="MediaFiles\MediaFileRepositoryFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\FormattedAudioChannelsFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioCodecFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatVideoCodecFixture.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatterTests\FormatAudioChannelsFixture.cs" />
<Compile Include="Messaging\Commands\CommandQueueManagerFixture.cs" />
<Compile Include="MetadataSource\SkyHook\SkyHookProxySearchFixture.cs" />
<Compile Include="MetadataSource\SearchSeriesComparerFixture.cs" />

View File

@ -11,6 +11,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Core.Extras.Metadata.Files;
using NzbDrone.Core.MediaCover;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Tv;
namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
@ -247,7 +248,7 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
var video = new XElement("video");
video.Add(new XElement("aspect", (float)episodeFile.MediaInfo.Width / (float)episodeFile.MediaInfo.Height));
video.Add(new XElement("bitrate", episodeFile.MediaInfo.VideoBitrate));
video.Add(new XElement("codec", episodeFile.MediaInfo.VideoCodec));
video.Add(new XElement("codec", MediaInfoFormatter.FormatVideoCodec(episodeFile.MediaInfo, episodeFile.SceneName)));
video.Add(new XElement("framerate", episodeFile.MediaInfo.VideoFps));
video.Add(new XElement("height", episodeFile.MediaInfo.Height));
video.Add(new XElement("scantype", episodeFile.MediaInfo.ScanType));
@ -264,11 +265,11 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
var audio = new XElement("audio");
audio.Add(new XElement("bitrate", episodeFile.MediaInfo.AudioBitrate));
audio.Add(new XElement("channels", episodeFile.MediaInfo.AudioChannels));
audio.Add(new XElement("codec", GetAudioCodec(episodeFile.MediaInfo.AudioFormat)));
audio.Add(new XElement("codec", MediaInfoFormatter.FormatAudioCodec(episodeFile.MediaInfo)));
audio.Add(new XElement("language", episodeFile.MediaInfo.AudioLanguages));
streamDetails.Add(audio);
if (episodeFile.MediaInfo.Subtitles != null && episodeFile.MediaInfo.Subtitles.Length > 0)
if (episodeFile.MediaInfo.Subtitles.IsNotNullOrWhiteSpace())
{
var subtitle = new XElement("subtitle");
subtitle.Add(new XElement("language", episodeFile.MediaInfo.Subtitles));
@ -379,15 +380,5 @@ namespace NzbDrone.Core.Extras.Metadata.Consumers.Xbmc
{
return Path.ChangeExtension(episodeFilePath, "").Trim('.') + "-thumb.jpg";
}
private string GetAudioCodec(string audioCodec)
{
if (audioCodec == "AC-3")
{
return "AC3";
}
return audioCodec;
}
}
}

View File

@ -0,0 +1,98 @@
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation;
namespace NzbDrone.Core.MediaFiles.MediaInfo
{
public static class MediaInfoFormatter
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(MediaInfoFormatter));
public static decimal FormatAudioChannels(MediaInfoModel mediaInfo)
{
var audioChannelPositions = mediaInfo.AudioChannelPositions;
var audioChannelPositionsText = mediaInfo.AudioChannelPositionsText;
var audioChannels = mediaInfo.AudioChannels;
if (audioChannelPositions.IsNullOrWhiteSpace())
{
if (audioChannelPositionsText.IsNullOrWhiteSpace())
{
if (mediaInfo.SchemaRevision >= 3)
{
return audioChannels;
}
return 0;
}
return mediaInfo.AudioChannelPositionsText.ContainsIgnoreCase("LFE") ? audioChannels - 1 + 0.1m : audioChannels;
}
return audioChannelPositions.Replace("Object Based / ", "")
.Split(new string[] { " / " }, StringSplitOptions.None)
.First()
.Split('/')
.Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture));
}
public static string FormatAudioCodec(MediaInfoModel mediaInfo)
{
var audioFormat = mediaInfo.AudioFormat;
if (audioFormat == "AC-3")
{
return "AC3";
}
if (audioFormat == "E-AC-3")
{
return "EAC3";
}
if (audioFormat == "MPEG Audio")
{
return mediaInfo.AudioProfile == "Layer 3" ? "MP3" : audioFormat;
}
if (audioFormat == "DTS")
{
return "DTS";
}
Logger.Error("Unknown audio format: {0}", audioFormat);
return audioFormat;
}
public static string FormatVideoCodec(MediaInfoModel mediaInfo, string sceneName)
{
var videoCodec = mediaInfo.VideoCodec;
if (videoCodec == "AVC")
{
return sceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(sceneName).Contains("h264")
? "h264"
: "x264";
}
if (videoCodec == "V_MPEGH/ISO/HEVC")
{
return sceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(sceneName).Contains("h265")
? "h265"
: "x265";
}
if (videoCodec == "MPEG-2 Video")
{
return "MPEG2";
}
Logger.Error("Unknown video codec: {0}", videoCodec);
return videoCodec;
}
}
}

View File

@ -27,33 +27,5 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
public string Subtitles { get; set; }
public string ScanType { get; set; }
public int SchemaRevision { get; set; }
[JsonIgnore]
public decimal FormattedAudioChannels
{
get
{
if (AudioChannelPositions.IsNullOrWhiteSpace())
{
if (AudioChannelPositionsText.IsNullOrWhiteSpace())
{
if (SchemaRevision >= 3)
{
return AudioChannels;
}
return 0;
}
return AudioChannelPositionsText.ContainsIgnoreCase("LFE") ? AudioChannels - 1 + 0.1m : AudioChannels;
}
return AudioChannelPositions.Replace("Object Based / ", "")
.Split(new string[] { " / " }, StringSplitOptions.None)
.First()
.Split('/')
.Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture));
}
}
}
}

View File

@ -783,6 +783,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="MediaFiles\MediaFileTableCleanupService.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoFormatter.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoLib.cs" />
<Compile Include="MediaFiles\MediaInfo\MediaInfoModel.cs" />
<Compile Include="MediaFiles\MediaInfo\UpdateMediaInfoService.cs" />

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
@ -9,6 +9,7 @@ using NzbDrone.Common.Cache;
using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Tv;
@ -445,75 +446,14 @@ namespace NzbDrone.Core.Organizer
{
if (episodeFile.MediaInfo == null) return;
string videoCodec;
switch (episodeFile.MediaInfo.VideoCodec)
{
case "AVC":
if (episodeFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(episodeFile.SceneName).Contains("h264"))
{
videoCodec = "h264";
}
else
{
videoCodec = "x264";
}
break;
case "V_MPEGH/ISO/HEVC":
if (episodeFile.SceneName.IsNotNullOrWhiteSpace() && Path.GetFileNameWithoutExtension(episodeFile.SceneName).Contains("h265"))
{
videoCodec = "h265";
}
else
{
videoCodec = "x265";
}
break;
case "MPEG-2 Video":
videoCodec = "MPEG2";
break;
default:
videoCodec = episodeFile.MediaInfo.VideoCodec;
break;
}
string audioCodec;
switch (episodeFile.MediaInfo.AudioFormat)
{
case "AC-3":
audioCodec = "AC3";
break;
case "E-AC-3":
audioCodec = "EAC3";
break;
case "MPEG Audio":
if (episodeFile.MediaInfo.AudioProfile == "Layer 3")
{
audioCodec = "MP3";
}
else
{
audioCodec = episodeFile.MediaInfo.AudioFormat;
}
break;
case "DTS":
audioCodec = episodeFile.MediaInfo.AudioFormat;
break;
default:
audioCodec = episodeFile.MediaInfo.AudioFormat;
break;
}
var videoCodec = MediaInfoFormatter.FormatVideoCodec(episodeFile.MediaInfo, episodeFile.SceneName);
var audioCodec = MediaInfoFormatter.FormatAudioCodec(episodeFile.MediaInfo);
var audioChannels = MediaInfoFormatter.FormatAudioChannels(episodeFile.MediaInfo);
var mediaInfoAudioLanguages = GetLanguagesToken(episodeFile.MediaInfo.AudioLanguages);
if (!mediaInfoAudioLanguages.IsNullOrWhiteSpace())
{
mediaInfoAudioLanguages = string.Format("[{0}]", mediaInfoAudioLanguages);
mediaInfoAudioLanguages = $"[{mediaInfoAudioLanguages}]";
}
if (mediaInfoAudioLanguages == "[EN]")
@ -524,12 +464,12 @@ namespace NzbDrone.Core.Organizer
var mediaInfoSubtitleLanguages = GetLanguagesToken(episodeFile.MediaInfo.Subtitles);
if (!mediaInfoSubtitleLanguages.IsNullOrWhiteSpace())
{
mediaInfoSubtitleLanguages = string.Format("[{0}]", mediaInfoSubtitleLanguages);
mediaInfoSubtitleLanguages = $"[{mediaInfoSubtitleLanguages}]";
}
var videoBitDepth = episodeFile.MediaInfo.VideoBitDepth > 0 ? episodeFile.MediaInfo.VideoBitDepth.ToString() : string.Empty;
var audioChannels = episodeFile.MediaInfo.FormattedAudioChannels > 0 ?
episodeFile.MediaInfo.FormattedAudioChannels.ToString("F1", CultureInfo.InvariantCulture) :
var audioChannelsFormatted = audioChannels > 0 ?
audioChannels.ToString("F1", CultureInfo.InvariantCulture) :
string.Empty;
tokenHandlers["{MediaInfo Video}"] = m => videoCodec;
@ -538,11 +478,11 @@ namespace NzbDrone.Core.Organizer
tokenHandlers["{MediaInfo Audio}"] = m => audioCodec;
tokenHandlers["{MediaInfo AudioCodec}"] = m => audioCodec;
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannels;
tokenHandlers["{MediaInfo AudioChannels}"] = m => audioChannelsFormatted;
tokenHandlers["{MediaInfo Simple}"] = m => string.Format("{0} {1}", videoCodec, audioCodec);
tokenHandlers["{MediaInfo Simple}"] = m => $"{videoCodec} {audioCodec}";
tokenHandlers["{MediaInfo Full}"] = m => string.Format("{0} {1}{2} {3}", videoCodec, audioCodec, mediaInfoAudioLanguages, mediaInfoSubtitleLanguages);
tokenHandlers["{MediaInfo Full}"] = m => $"{videoCodec} {audioCodec}{mediaInfoAudioLanguages} {mediaInfoSubtitleLanguages}";
}
private string GetLanguagesToken(string mediaInfoLanguages)