New: Refactor MediaInfo tokens (fixes old tokens adds new stuff) (#3058)
* Rename all 'episodeFile' variables to 'movieFile' * Improve media info extraction with more fields * Improve media info tokens extraction * Add missing fields to MediaInfoModel * Restore to previous implementation of null handling * Forgot to add MediaInfoFormatter to project * Add missing EqualsIgnoreCase extension method * Simplify Logger.Debug() invocations * Add missing StartsWithIgnoreCase extension method * This '.Value' shouldn't be required * Remove TODO comment * Upgrade MediaInfo from 17.10 to 18.08.1 * Use correct media info field for files listing * Replace media info "VideoCodec" (deprecated) with "VideoFormat" * Fix 'Formatiting' typos * Add support for media info Format_AdditionalFeatures' field * Add proper support for all DTS and TrueHD flavors * Add support for '3D' media info token * Remove deprecated media info video/audio profile fields * Add support for 'HDR' media info token * Add new video parameters to anime file name sample * Adapt tests for new media info fields * Revert "Remove deprecated media info video/audio profile fields" * Include missing test files in core test project * Fix small regression issue * Allow sample movie to be detected as HDR * Do not parse audio channel positions if there are no channels * Clean up extra blank line * Reuse already declared variable * Fix wrong audio channels detection on DTS:X streams * Fix all failing unit tests * Fix remaining failing unit testspull/3144/head
parent
4009852c35
commit
97f111bec8
Binary file not shown.
Binary file not shown.
@ -0,0 +1,177 @@
|
|||||||
|
using FluentAssertions;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.MediaFiles.MediaInfo;
|
||||||
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test.MediaFiles.MediaInfo.MediaInfoFormatterTests
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class FormatAudioChannelsFixture : TestBase
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void should_subtract_one_from_AudioChannels_as_total_channels_if_LFE_in_AudioChannelPositionsText()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 6,
|
||||||
|
AudioChannelPositions = null,
|
||||||
|
AudioChannelPositionsText = "Front: L C R, Side: L R, LFE"
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_use_AudioChannels_as_total_channels_if_LFE_not_in_AudioChannelPositionsText()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = null,
|
||||||
|
AudioChannelPositionsText = "Front: L R"
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_0_if_schema_revision_is_less_than_3_and_other_properties_are_null()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = null,
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_use_AudioChannels_if_schema_revision_is_3_and_other_properties_are_null()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = null,
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_sum_AudioChannelPositions()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = "2/0/0",
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_sum_AudioChannelPositions_including_decimal()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = "3/2/0.1",
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_cleanup_extraneous_text_from_AudioChannelPositions()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = "Object Based / 3/2/2.1",
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_skip_empty_groups_in_AudioChannelPositions()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = " / 2/0/0.0",
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_sum_first_series_of_numbers_from_AudioChannelPositions()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = "3/2/2.1 / 3/2/2.1",
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(7.1m);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_sum_dual_mono_representation_AudioChannelPositions()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = "1+1",
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(2.0m);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_use_AudioChannelPositionText_when_AudioChannelChannelPosition_is_invalid()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 6,
|
||||||
|
AudioChannelPositions = "15 objects",
|
||||||
|
AudioChannelPositionsText = "15 objects / Front: L C R, Side: L R, LFE",
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_remove_atmos_objects_from_AudioChannelPostions()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioChannels = 2,
|
||||||
|
AudioChannelPositions = "15 objects / 3/2.1",
|
||||||
|
AudioChannelPositionsText = null,
|
||||||
|
SchemaRevision = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioChannels(mediaInfoModel).Should().Be(5.1m);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,73 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
private static string sceneName = "My.Series.S01E01-Sonarr";
|
||||||
|
|
||||||
|
[TestCase("AC-3", "AC3")]
|
||||||
|
[TestCase("E-AC-3", "EAC3")]
|
||||||
|
[TestCase("MPEG Audio", "MPEG Audio")]
|
||||||
|
[TestCase("DTS", "DTS")]
|
||||||
|
public void should_format_audio_format_legacy(string audioFormat, string expectedFormat)
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioFormat = audioFormat
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("MPEG Audio, A_MPEG/L2, , ", "droned.s01e03.swedish.720p.hdtv.x264-prince", "MP2")]
|
||||||
|
[TestCase("Vorbis, A_VORBIS, , Xiph.Org libVorbis I 20101101 (Schaufenugget)", "DB Super HDTV", "Vorbis")]
|
||||||
|
[TestCase("PCM, 1, , ", "DW DVDRip XviD-idTV", "PCM")] // Dubbed most likely
|
||||||
|
[TestCase("TrueHD, A_TRUEHD, , ", "", "TrueHD")]
|
||||||
|
[TestCase("WMA, 161, , ", "Droned.wmv", "WMA")]
|
||||||
|
[TestCase("WMA, 162, Pro, ", "B.N.S04E18.720p.WEB-DL", "WMA")]
|
||||||
|
[TestCase("Opus, A_OPUS, , ", "Roadkill Ep3x11 - YouTube.webm", "Opus")]
|
||||||
|
[TestCase("mp3 , 0, , ", "climbing.mp4", "MP3")]
|
||||||
|
public void should_format_audio_format(string audioFormatPack, string sceneName, string expectedFormat)
|
||||||
|
{
|
||||||
|
var split = audioFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioFormat = split[0],
|
||||||
|
AudioCodecID = split[1],
|
||||||
|
AudioProfile = split[2],
|
||||||
|
AudioCodecLibrary = split[3]
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).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, sceneName).Should().Be("MP3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_AudioFormat_by_default()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
AudioFormat = "Other Audio Format",
|
||||||
|
AudioCodecID = "Other Audio Codec"
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatAudioCodec(mediaInfoModel, sceneName).Should().Be(mediaInfoModel.AudioFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,92 @@
|
|||||||
|
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, "h264")]
|
||||||
|
[TestCase("AVC", "source.title.x264.720p-Sonarr", "x264")]
|
||||||
|
[TestCase("AVC", "source.title.h264.720p-Sonarr", "h264")]
|
||||||
|
[TestCase("V_MPEGH/ISO/HEVC", null, "h265")]
|
||||||
|
[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_legacy(string videoCodec, string sceneName, string expectedFormat)
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
VideoFormat = videoCodec
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("MPEG Video, 2, Main@High, ", "Droned.S01E02.1080i.HDTV.DD5.1.MPEG2-NTb", "MPEG2")]
|
||||||
|
[TestCase("MPEG Video, V_MPEG2, Main@High, ", "", "MPEG2")]
|
||||||
|
[TestCase("MPEG Video, , , ", "The.Simpsons.S13E04.INTERNAL-ANiVCD.mpg", "MPEG")]
|
||||||
|
[TestCase("VC-1, WVC1, Advanced@L4, ", "B.N.S04E18.720p.WEB-DL", "VC1")]
|
||||||
|
[TestCase("VC-1, V_MS/VFW/FOURCC / WVC1, Advanced@L3, ", "", "VC1")]
|
||||||
|
[TestCase("VC-1, WMV3, MP@LL, ", "It's Always Sunny S07E13 The Gang's RevengeHDTV.XviD-2HD.avi", "VC1")]
|
||||||
|
[TestCase("V.MPEG4/ISO/AVC, V.MPEG4/ISO/AVC, , ", "pd.2015.S03E08.720p.iP.WEBRip.AAC2.0.H264-BTW", "h264")]
|
||||||
|
[TestCase("WMV2, WMV2, , ", "Droned.wmv", "WMV")]
|
||||||
|
[TestCase("xvid, xvid, , ", "", "XviD")]
|
||||||
|
[TestCase("div3, div3, , ", "spsm.dvdrip.divx.avi'.", "DivX")]
|
||||||
|
[TestCase("VP6, 4, , ", "Top Gear - S12E01 - Lorries - SD TV.flv", "VP6")]
|
||||||
|
[TestCase("VP7, VP70, General, ", "Sweet Seymour.avi", "VP7")]
|
||||||
|
[TestCase("VP8, V_VP8, , ", "Dick.mkv", "VP8")]
|
||||||
|
[TestCase("VP9, V_VP9, , ", "Roadkill Ep3x11 - YouTube.webm", "VP9")]
|
||||||
|
[TestCase("x264, x264, , ", "Ghost Advent - S04E05 - Stanley Hotel SDTV.avi", "x264")]
|
||||||
|
[TestCase("V_MPEGH/ISO/HEVC, V_MPEGH/ISO/HEVC, , ", "The BBT S11E12 The Matrimonial Metric 1080p 10bit AMZN WEB-DL", "h265")]
|
||||||
|
[TestCase("MPEG-4 Visual, 20, Simple@L1, Lavc52.29.0", "Will.And.Grace.S08E14.WS.DVDrip.XviD.I.Love.L.Gay-Obfuscated", "XviD")]
|
||||||
|
[TestCase("MPEG-4 Visual, 20, Advanced Simple@L5, XviD0046", "", "XviD")]
|
||||||
|
[TestCase("mp4v, mp4v, , ", "American.Chopper.S06E07.Mountain.Creek.Bike.DSR.XviD-KRS", "XviD")]
|
||||||
|
public void should_format_video_format(string videoFormatPack, string sceneName, string expectedFormat)
|
||||||
|
{
|
||||||
|
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
VideoFormat = split[0],
|
||||||
|
VideoCodecID = split[1],
|
||||||
|
VideoProfile = split[2],
|
||||||
|
VideoCodecLibrary = split[3]
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase("AVC, AVC, , x264", "Some.Video.S01E01.h264", "x264")] // Force mediainfo tag
|
||||||
|
[TestCase("HEVC, HEVC, , x265", "Some.Video.S01E01.h265", "x265")] // Force mediainfo tag
|
||||||
|
[TestCase("AVC, AVC, , ", "Some.Video.S01E01.x264", "x264")] // Not seen in practice, but honor tag if otherwise unknown
|
||||||
|
[TestCase("HEVC, HEVC, , ", "Some.Video.S01E01.x265", "x265")] // Not seen in practice, but honor tag if otherwise unknown
|
||||||
|
[TestCase("AVC, AVC, , ", "Some.Video.S01E01", "h264")] // Default value
|
||||||
|
[TestCase("HEVC, HEVC, , ", "Some.Video.S01E01", "h265")] // Default value
|
||||||
|
public void should_format_video_format_fallbacks(string videoFormatPack, string sceneName, string expectedFormat)
|
||||||
|
{
|
||||||
|
var split = videoFormatPack.Split(new string[] { ", " }, System.StringSplitOptions.None);
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
VideoFormat = split[0],
|
||||||
|
VideoCodecID = split[1],
|
||||||
|
VideoProfile = split[2],
|
||||||
|
VideoCodecLibrary = split[3]
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, sceneName).Should().Be(expectedFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_return_VideoFormat_by_default()
|
||||||
|
{
|
||||||
|
var mediaInfoModel = new MediaInfoModel
|
||||||
|
{
|
||||||
|
VideoFormat = "VideoCodec"
|
||||||
|
};
|
||||||
|
|
||||||
|
MediaInfoFormatter.FormatVideoCodec(mediaInfoModel, null).Should().Be(mediaInfoModel.VideoFormat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,470 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using NLog;
|
||||||
|
using NLog.Fluent;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Instrumentation;
|
||||||
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
|
||||||
|
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 audioChannels = FormatAudioChannelsFromAudioChannelPositions(mediaInfo);
|
||||||
|
|
||||||
|
if (audioChannels == null)
|
||||||
|
{
|
||||||
|
audioChannels = FormatAudioChannelsFromAudioChannelPositionsText(mediaInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioChannels == null)
|
||||||
|
{
|
||||||
|
audioChannels = FormatAudioChannelsFromAudioChannels(mediaInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioChannels ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatAudioCodec(MediaInfoModel mediaInfo, string sceneName)
|
||||||
|
{
|
||||||
|
if (mediaInfo.AudioCodecID == null)
|
||||||
|
{
|
||||||
|
return FormatAudioCodecLegacy(mediaInfo, sceneName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var audioFormat = mediaInfo.AudioFormat;
|
||||||
|
var audioCodecID = mediaInfo.AudioCodecID ?? string.Empty;
|
||||||
|
var audioProfile = mediaInfo.AudioProfile ?? string.Empty;
|
||||||
|
var audioAdditionalFeatures = mediaInfo.AudioAdditionalFeatures ?? string.Empty;
|
||||||
|
var audioCodecLibrary = mediaInfo.AudioCodecLibrary ?? string.Empty;
|
||||||
|
|
||||||
|
if (audioFormat.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("AC-3"))
|
||||||
|
{
|
||||||
|
return "AC3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("E-AC-3"))
|
||||||
|
{
|
||||||
|
return "EAC3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("AAC"))
|
||||||
|
{
|
||||||
|
if (audioCodecID == "A_AAC/MPEG4/LC/SBR")
|
||||||
|
{
|
||||||
|
return "HE-AAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "AAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("DTS"))
|
||||||
|
{
|
||||||
|
if (audioAdditionalFeatures.StartsWithIgnoreCase("XLL"))
|
||||||
|
{
|
||||||
|
if (audioAdditionalFeatures.EndsWithIgnoreCase("X"))
|
||||||
|
{
|
||||||
|
return "DTS-X";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "DTS-HD MA";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioAdditionalFeatures.EqualsIgnoreCase("ES"))
|
||||||
|
{
|
||||||
|
return "DTS-ES";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioAdditionalFeatures.EqualsIgnoreCase("XBR"))
|
||||||
|
{
|
||||||
|
return "DTS-HD HRA";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "DTS";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("FLAC"))
|
||||||
|
{
|
||||||
|
return "FLAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.Trim().EqualsIgnoreCase("mp3"))
|
||||||
|
{
|
||||||
|
return "MP3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("MPEG Audio"))
|
||||||
|
{
|
||||||
|
if (mediaInfo.AudioCodecID == "55" || mediaInfo.AudioCodecID == "A_MPEG/L3" || mediaInfo.AudioProfile == "Layer 3")
|
||||||
|
{
|
||||||
|
return "MP3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mediaInfo.AudioCodecID == "A_MPEG/L2" || mediaInfo.AudioProfile == "Layer 2")
|
||||||
|
{
|
||||||
|
return "MP2";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("Opus"))
|
||||||
|
{
|
||||||
|
return "Opus";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("PCM"))
|
||||||
|
{
|
||||||
|
return "PCM";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("MLP FBA"))
|
||||||
|
{
|
||||||
|
if (audioAdditionalFeatures == "16-ch")
|
||||||
|
{
|
||||||
|
return "TrueHD Atmos";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "TrueHD";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("Vorbis"))
|
||||||
|
{
|
||||||
|
return "Vorbis";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat == "WMA")
|
||||||
|
{
|
||||||
|
return "WMA";
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug("Unknown audio format: '{0}' in '{1}'.", string.Join(", ", audioFormat, audioCodecID, audioProfile, audioAdditionalFeatures, audioCodecLibrary), sceneName);
|
||||||
|
|
||||||
|
return audioFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatAudioCodecLegacy(MediaInfoModel mediaInfo, string sceneName)
|
||||||
|
{
|
||||||
|
var audioFormat = mediaInfo.AudioFormat;
|
||||||
|
|
||||||
|
if (audioFormat.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return audioFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("AC-3"))
|
||||||
|
{
|
||||||
|
return "AC3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("E-AC-3"))
|
||||||
|
{
|
||||||
|
return "EAC3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("AAC"))
|
||||||
|
{
|
||||||
|
return "AAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("MPEG Audio") && mediaInfo.AudioProfile == "Layer 3")
|
||||||
|
{
|
||||||
|
return "MP3";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("DTS"))
|
||||||
|
{
|
||||||
|
return "DTS";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("TrueHD"))
|
||||||
|
{
|
||||||
|
return "TrueHD";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("FLAC"))
|
||||||
|
{
|
||||||
|
return "FLAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("Vorbis"))
|
||||||
|
{
|
||||||
|
return "Vorbis";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioFormat.EqualsIgnoreCase("Opus"))
|
||||||
|
{
|
||||||
|
return "Opus";
|
||||||
|
}
|
||||||
|
|
||||||
|
return audioFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatVideoCodec(MediaInfoModel mediaInfo, string sceneName)
|
||||||
|
{
|
||||||
|
if (mediaInfo.VideoFormat == null)
|
||||||
|
{
|
||||||
|
return FormatVideoCodecLegacy(mediaInfo, sceneName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var videoFormat = mediaInfo.VideoFormat;
|
||||||
|
var videoCodecID = mediaInfo.VideoCodecID ?? string.Empty;
|
||||||
|
var videoProfile = mediaInfo.VideoProfile ?? string.Empty;
|
||||||
|
var videoCodecLibrary = mediaInfo.VideoCodecLibrary ?? string.Empty;
|
||||||
|
|
||||||
|
var result = videoFormat;
|
||||||
|
|
||||||
|
if (videoFormat.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "x264")
|
||||||
|
{
|
||||||
|
return "x264";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "AVC" || videoFormat == "V.MPEG4/ISO/AVC")
|
||||||
|
{
|
||||||
|
if (videoCodecLibrary.StartsWithIgnoreCase("x264"))
|
||||||
|
{
|
||||||
|
return "x264";
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSceneNameMatch(sceneName, "AVC", "x264", "h264");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "HEVC" || videoFormat == "V_MPEGH/ISO/HEVC")
|
||||||
|
{
|
||||||
|
if (videoCodecLibrary.StartsWithIgnoreCase("x265"))
|
||||||
|
{
|
||||||
|
return "x265";
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSceneNameMatch(sceneName, "HEVC", "x265", "h265");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "MPEG Video")
|
||||||
|
{
|
||||||
|
if (videoCodecID == "2" || videoCodecID == "V_MPEG2")
|
||||||
|
{
|
||||||
|
return "MPEG2";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodecID.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return "MPEG";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "MPEG-2 Video")
|
||||||
|
{
|
||||||
|
return "MPEG2";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "MPEG-4 Visual")
|
||||||
|
{
|
||||||
|
if (videoCodecID.ContainsIgnoreCase("XVID") ||
|
||||||
|
videoCodecLibrary.StartsWithIgnoreCase("XviD"))
|
||||||
|
{
|
||||||
|
return "XviD";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodecID.ContainsIgnoreCase("DIV3") ||
|
||||||
|
videoCodecID.ContainsIgnoreCase("DIVX") ||
|
||||||
|
videoCodecID.ContainsIgnoreCase("DX50") ||
|
||||||
|
videoCodecLibrary.StartsWithIgnoreCase("DivX"))
|
||||||
|
{
|
||||||
|
return "DivX";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "MPEG-4 Visual" || videoFormat == "mp4v")
|
||||||
|
{
|
||||||
|
result = GetSceneNameMatch(sceneName, "XviD", "DivX", "");
|
||||||
|
if (result.IsNotNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "VC-1")
|
||||||
|
{
|
||||||
|
return "VC1";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat.EqualsIgnoreCase("VP6") || videoFormat.EqualsIgnoreCase("VP7") ||
|
||||||
|
videoFormat.EqualsIgnoreCase("VP8") || videoFormat.EqualsIgnoreCase("VP9"))
|
||||||
|
{
|
||||||
|
return videoFormat.ToUpperInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat == "WMV2")
|
||||||
|
{
|
||||||
|
return "WMV";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat.EqualsIgnoreCase("DivX") || videoFormat.EqualsIgnoreCase("div3"))
|
||||||
|
{
|
||||||
|
return "DivX";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoFormat.EqualsIgnoreCase("XviD"))
|
||||||
|
{
|
||||||
|
return "XviD";
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug("Unknown video format: '{0}' in '{1}'.", string.Join(", ", videoFormat, videoCodecID, videoProfile, videoCodecLibrary), sceneName);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string FormatVideoCodecLegacy(MediaInfoModel mediaInfo, string sceneName)
|
||||||
|
{
|
||||||
|
var videoCodec = mediaInfo.VideoFormat;
|
||||||
|
|
||||||
|
if (videoCodec.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return videoCodec;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodec == "AVC")
|
||||||
|
{
|
||||||
|
return GetSceneNameMatch(sceneName, "AVC", "h264", "x264");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodec == "V_MPEGH/ISO/HEVC" || videoCodec == "HEVC")
|
||||||
|
{
|
||||||
|
return GetSceneNameMatch(sceneName, "HEVC", "h265", "x265");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodec == "MPEG-2 Video")
|
||||||
|
{
|
||||||
|
return "MPEG2";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodec == "MPEG-4 Visual")
|
||||||
|
{
|
||||||
|
return GetSceneNameMatch(sceneName, "DivX", "XviD");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodec.StartsWithIgnoreCase("XviD"))
|
||||||
|
{
|
||||||
|
return "XviD";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodec.StartsWithIgnoreCase("DivX"))
|
||||||
|
{
|
||||||
|
return "DivX";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (videoCodec.EqualsIgnoreCase("VC-1"))
|
||||||
|
{
|
||||||
|
return "VC1";
|
||||||
|
}
|
||||||
|
|
||||||
|
return videoCodec;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal? FormatAudioChannelsFromAudioChannelPositions(MediaInfoModel mediaInfo)
|
||||||
|
{
|
||||||
|
var audioChannelPositions = mediaInfo.AudioChannelPositions;
|
||||||
|
|
||||||
|
if (audioChannelPositions.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Debug("Formatting audio channels using 'AudioChannelPositions', with a value of: '{0}'", audioChannelPositions);
|
||||||
|
|
||||||
|
if (audioChannelPositions.Contains("+"))
|
||||||
|
{
|
||||||
|
return audioChannelPositions.Split('+')
|
||||||
|
.Sum(s => decimal.Parse(s.Trim(), CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audioChannelPositions.Contains("/"))
|
||||||
|
{
|
||||||
|
return Regex.Replace(audioChannelPositions, @"^\d+\sobjects", "", RegexOptions.Compiled | RegexOptions.IgnoreCase)
|
||||||
|
.Replace("Object Based / ", "")
|
||||||
|
.Split(new string[] { " / " }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.FirstOrDefault()
|
||||||
|
?.Split('/')
|
||||||
|
.Sum(s => decimal.Parse(s, CultureInfo.InvariantCulture));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn(e, "Unable to format audio channels using 'AudioChannelPositions'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal? FormatAudioChannelsFromAudioChannelPositionsText(MediaInfoModel mediaInfo)
|
||||||
|
{
|
||||||
|
var audioChannelPositionsText = mediaInfo.AudioChannelPositionsText;
|
||||||
|
var audioChannels = mediaInfo.AudioChannels;
|
||||||
|
|
||||||
|
if (audioChannelPositionsText.IsNullOrWhiteSpace())
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Debug("Formatting audio channels using 'AudioChannelPositionsText', with a value of: '{0}'", audioChannelPositionsText);
|
||||||
|
|
||||||
|
return audioChannelPositionsText.ContainsIgnoreCase("LFE") ? audioChannels - 1 + 0.1m : audioChannels;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Warn(e, "Unable to format audio channels using 'AudioChannelPositionsText'");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static decimal? FormatAudioChannelsFromAudioChannels(MediaInfoModel mediaInfo)
|
||||||
|
{
|
||||||
|
var audioChannels = mediaInfo.AudioChannels;
|
||||||
|
|
||||||
|
if (mediaInfo.SchemaRevision >= 3)
|
||||||
|
{
|
||||||
|
Logger.Debug("Formatting audio channels using 'AudioChannels', with a value of: '{0}'", audioChannels);
|
||||||
|
|
||||||
|
return audioChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetSceneNameMatch(string sceneName, params string[] tokens)
|
||||||
|
{
|
||||||
|
sceneName = sceneName.IsNotNullOrWhiteSpace() ? Parser.Parser.RemoveFileExtension(sceneName) : string.Empty;
|
||||||
|
|
||||||
|
foreach (var token in tokens)
|
||||||
|
{
|
||||||
|
if (sceneName.ContainsIgnoreCase(token))
|
||||||
|
{
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last token is the default.
|
||||||
|
return tokens.Last();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue