From 6898ead35d2dcffce717e710bb0431f43db2687b Mon Sep 17 00:00:00 2001 From: sharinganthief Date: Tue, 14 Apr 2026 10:56:34 -0400 Subject: [PATCH] Mka Support add mka support --- src/NzbDrone.Common/Lidarr.Common.csproj | 2 +- .../ParserTests/QualityParserFixture.cs | 1 + src/NzbDrone.Core/Lidarr.Core.csproj | 2 +- src/NzbDrone.Core/MediaFiles/AudioTag.cs | 2 +- .../MediaFiles/MediaFileExtensions.cs | 1 + src/NzbDrone.Core/Parser/QualityParser.cs | 64 ++++++++++++++++++- 6 files changed, 68 insertions(+), 4 deletions(-) diff --git a/src/NzbDrone.Common/Lidarr.Common.csproj b/src/NzbDrone.Common/Lidarr.Common.csproj index 21a822ea8..fb2096362 100644 --- a/src/NzbDrone.Common/Lidarr.Common.csproj +++ b/src/NzbDrone.Common/Lidarr.Common.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs index 5d22174ab..4dab3a8aa 100644 --- a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs @@ -297,6 +297,7 @@ public void should_parse_quality_from_name(string title) [TestCase("01. Kanye West - Ultralight Beam.mp3")] [TestCase("01. Kanye West - Ultralight Beam.ogg")] + [TestCase("01. Kanye West - Ultralight Beam.mka")] // These get detected by name as we are looking for the extensions as identifiers for release names // [TestCase("01. Kanye West - Ultralight Beam.m4a")] diff --git a/src/NzbDrone.Core/Lidarr.Core.csproj b/src/NzbDrone.Core/Lidarr.Core.csproj index d5eed7f10..ad9cce669 100644 --- a/src/NzbDrone.Core/Lidarr.Core.csproj +++ b/src/NzbDrone.Core/Lidarr.Core.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/NzbDrone.Core/MediaFiles/AudioTag.cs b/src/NzbDrone.Core/MediaFiles/AudioTag.cs index 500a94bed..e8a0cac39 100644 --- a/src/NzbDrone.Core/MediaFiles/AudioTag.cs +++ b/src/NzbDrone.Core/MediaFiles/AudioTag.cs @@ -177,7 +177,7 @@ public void Read(string path) Logger.Debug("Audio Properties: " + acodec.Description + ", Bitrate: " + bitrate + ", Sample Size: " + file.Properties.BitsPerSample + ", SampleRate: " + acodec.AudioSampleRate + ", Channels: " + acodec.AudioChannels); - Quality = QualityParser.ParseQuality(file.Name, acodec.Description, bitrate, file.Properties.BitsPerSample); + Quality = QualityParser.ParseQualityFromCodec(file.Name, acodec, bitrate, file.Properties.BitsPerSample); Logger.Debug($"Quality parsed: {Quality}, Source: {Quality.QualityDetectionSource}"); MediaInfo = new MediaInfoModel diff --git a/src/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs b/src/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs index b87fcc619..eef0523de 100644 --- a/src/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs +++ b/src/NzbDrone.Core/MediaFiles/MediaFileExtensions.cs @@ -17,6 +17,7 @@ static MediaFileExtensions() { ".m4a", Quality.Unknown }, { ".m4b", Quality.Unknown }, { ".m4p", Quality.Unknown }, + { ".mka", Quality.Unknown }, { ".ogg", Quality.Unknown }, { ".oga", Quality.Unknown }, { ".opus", Quality.Unknown }, diff --git a/src/NzbDrone.Core/Parser/QualityParser.cs b/src/NzbDrone.Core/Parser/QualityParser.cs index 1bfca5fea..43fb4f8f6 100644 --- a/src/NzbDrone.Core/Parser/QualityParser.cs +++ b/src/NzbDrone.Core/Parser/QualityParser.cs @@ -1,10 +1,13 @@ using System; +using System.Reflection; using System.Text.RegularExpressions; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Qualities; +using TagLib; +using TagLib.Matroska; namespace NzbDrone.Core.Parser { @@ -38,12 +41,46 @@ public class QualityParser private static readonly Regex SampleSizeRegex = new (@"\b(?:(?24[-._ ]?bit|flac24(?:[-._ ]?bit)?|tr24|24-(?:44|48|96|192)|[\[\(].*24bit.*[\]\)]))\b", RegexOptions.Compiled); - private static readonly Regex CodecRegex = new (@"\b(?:(?MPEG Version \d(.5)? Audio, Layer 1|MP1)|(?MPEG Version \d(.5)? Audio, Layer 2|MP2)|(?MP3.*VBR|MPEG Version \d(.5)? Audio, Layer 3 vbr)|(?MP3|MPEG Version \d(.5)? Audio, Layer 3)|(?(web)?flac(?:24(?:[-._ ]?bit)?)?|TR24)|(?wavpack|wv)|(?alac)|(?WMA\d?)|(?WAV|PCM)|(?M4A|M4P|M4B|AAC|mp4a|MPEG-4 Audio(?!.*alac))|(?OGG|OGA|Vorbis))\b|(?monkey's audio|[\[|\(].*\bape\b.*[\]|\)])|(?Opus Version \d(.5)? Audio|[\[|\(].*\bopus\b.*[\]|\)])", + private static readonly Regex CodecRegex = new (@"\b(?:(?MPEG Version \d(.5)? Audio, Layer 1|MP1)|(?MPEG Version \d(.5)? Audio, Layer 2|MP2)|(?MP3.*VBR|MPEG Version \d(.5)? Audio, Layer 3 vbr)|(?MP3|MPEG Version \d(.5)? Audio, Layer 3)|(?(web)?flac(?:24(?:[-._ ]?bit)?)?|TR24)|(?wavpack|wv)|(?alac)|(?WMA\d?)|(?WAV|PCM)|(?M4A|M4P|M4B|AAC|mp4a|MPEG-4 Audio(?!.*alac))|(?MKA|OGG|OGA|Vorbis))\b|(?monkey's audio|[\[|\(].*\bape\b.*[\]|\)])|(?Opus Version \d(.5)? Audio|[\[|\(].*\bopus\b.*[\]|\)])", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex WebRegex = new (@"\b(?WEB)(?:\b|$|[ .])", RegexOptions.Compiled | RegexOptions.IgnoreCase); + public static QualityModel ParseQualityFromCodec(string name, IAudioCodec audioCodec, int fileBitrate, int fileSampleSize = 0) + { + var normalizedName = name.Replace('_', ' ').Trim().ToLower(); + var result = ParseQualityModifiers(name, normalizedName); + + var codec = Codec.Unknown; + var bitrate = ParseBitRate(normalizedName); + var sampleSize = ParseSampleSize(normalizedName); + + if (audioCodec != null) + { + if (audioCodec is AudioTrack matroskaCodec) + { + codec = ParseCodec(StealCodecFromMatroskaTrack(matroskaCodec), string.Empty); + } + + if (codec == Codec.Unknown && audioCodec!.Description.IsNotNullOrWhiteSpace()) + { + var descCodec = ParseCodec(audioCodec.Description, string.Empty); + Logger.Trace($"Got codec {descCodec}"); + + result.Quality = FindQuality(descCodec, fileBitrate, fileSampleSize); + + if (result.Quality != Quality.Unknown) + { + result.QualityDetectionSource = QualityDetectionSource.TagLib; + return result; + } + } + } + + return ParseQuality(result, codec, name, normalizedName, bitrate, sampleSize); + } + public static QualityModel ParseQuality(string name, string desc, int fileBitrate, int fileSampleSize = 0) { Logger.Debug("Trying to parse quality for '{0}'", name); @@ -74,6 +111,17 @@ public static QualityModel ParseQuality(string name, string desc, int fileBitrat var bitrate = ParseBitRate(normalizedName); var sampleSize = ParseSampleSize(normalizedName); + return ParseQuality(result, codec, name, normalizedName, bitrate, sampleSize); + } + + public static QualityModel ParseQuality( + QualityModel result, + Codec codec, + string name, + string normalizedName, + BitRate bitrate, + SampleSize sampleSize) + { switch (codec) { case Codec.MP1: @@ -333,6 +381,20 @@ public static Codec ParseCodec(string name, string origName) return Codec.Unknown; } + private static string StealCodecFromMatroskaTrack(AudioTrack matroskaCodec) + { + var field = typeof(Track) + .GetField("track_codec_id", BindingFlags.NonPublic | BindingFlags.Instance); + + if (field == null) + { + return null; + } + + var val = ((string)field.GetValue(matroskaCodec) ?? string.Empty).Replace("A_", string.Empty); + return val; + } + private static BitRate ParseBitRate(string name) { // var nameWithNoSpaces = Regex.Replace(name, @"\s+", "");