Added: Monkey Audio and WavPack support (#455)

* Added: Monkey Audio and WavPack support

* fixup! Add test case, fix typo
This commit is contained in:
Qstick 2018-08-26 21:11:07 -04:00 committed by GitHub
parent 894385747e
commit 499af45566
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 190 additions and 6 deletions

View File

@ -144,6 +144,32 @@ namespace NzbDrone.Core.Test.ParserTests
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.ALAC);
}
[TestCase("Stevie Ray Vaughan Discography (1981-1987) [APE]", null, 0)]
[TestCase("Brain Ape - Rig it [2014][ape]", null, 0)]
[TestCase("", "Monkey's Audio", 0)]
public void should_parse_ape_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.APE);
}
[TestCase("Max Roach - Drums Unlimited (1966) [WavPack]", null, 0)]
[TestCase("Roxette - Charm School(2011) (2CD) [WV]", null, 0)]
[TestCase("", "WavPack", 0)]
public void should_parse_wavpack_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.WAVPACK);
}
[TestCase("Arctic Monkeys - AM {2013-Album}", null, 0)]
[TestCase("Audio Adrinaline - Audio Adrinaline", null, 0)]
[TestCase("Brain Ape - Rig it [2014][flac]", null, 0)]
[TestCase("Coil - The Ape Of Naples(2005) (FLAC)", null, 0)]
public void should_not_parse_ape_quality(string title, string desc, int bitrate)
{
var result = QualityParser.ParseQuality(title, desc, bitrate);
result.Quality.Should().NotBe(Quality.APE);
}
[TestCase("Milky Chance - Sadnecessary [256 Kbps] [M4A]", null, 0)]
[TestCase("Little Mix - Salute [Deluxe Edition] [2013] [M4A-256]-V3nom [GLT", null, 0)]

View File

@ -0,0 +1,135 @@
using System.Collections.Generic;
using System.Data;
using System.Linq;
using FluentMigrator;
using Newtonsoft.Json;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(19)]
public class add_ape_quality_in_profiles : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(ConvertProfile);
}
private void ConvertProfile(IDbConnection conn, IDbTransaction tran)
{
var updater = new ProfileUpdater19(conn, tran);
updater.SplitQualityAppend(6, 35); // APE after Flac
updater.SplitQualityAppend(6, 36); // WavPack after Flac
updater.Commit();
}
}
public class Profile19
{
public int Id { get; set; }
public string Name { get; set; }
public int Cutoff { get; set; }
public List<ProfileItem19> Items { get; set; }
}
public class ProfileItem19
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int Id { get; set; }
public string Name { get; set; }
public int? Quality { get; set; }
public bool Allowed { get; set; }
public List<ProfileItem19> Items { get; set; }
}
public class ProfileUpdater19
{
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
private List<Profile19> _profiles;
private HashSet<Profile19> _changedProfiles = new HashSet<Profile19>();
public ProfileUpdater19(IDbConnection conn, IDbTransaction tran)
{
_connection = conn;
_transaction = tran;
_profiles = GetProfiles();
}
public void Commit()
{
foreach (var profile in _changedProfiles)
{
using (var updateProfileCmd = _connection.CreateCommand())
{
updateProfileCmd.Transaction = _transaction;
updateProfileCmd.CommandText = "UPDATE Profiles SET Name = ?, Cutoff = ?, Items = ? WHERE Id = ?";
updateProfileCmd.AddParameter(profile.Name);
updateProfileCmd.AddParameter(profile.Cutoff);
updateProfileCmd.AddParameter(profile.Items.ToJson());
updateProfileCmd.AddParameter(profile.Id);
updateProfileCmd.ExecuteNonQuery();
}
}
_changedProfiles.Clear();
}
public void SplitQualityAppend(int find, int quality)
{
foreach (var profile in _profiles)
{
if (profile.Items.Any(v => v.Quality == quality)) continue;
var findIndex = profile.Items.FindIndex(v =>
{
return v.Quality == find || (v.Items != null && v.Items.Any(b => b.Quality == find));
});
profile.Items.Insert(findIndex + 1, new ProfileItem19
{
Quality = quality,
Allowed = false
});
_changedProfiles.Add(profile);
}
}
private List<Profile19> GetProfiles()
{
var profiles = new List<Profile19>();
using (var getProfilesCmd = _connection.CreateCommand())
{
getProfilesCmd.Transaction = _transaction;
getProfilesCmd.CommandText = @"SELECT Id, Name, Cutoff, Items FROM Profiles";
using (var profileReader = getProfilesCmd.ExecuteReader())
{
while (profileReader.Read())
{
profiles.Add(new Profile19
{
Id = profileReader.GetInt32(0),
Name = profileReader.GetString(1),
Cutoff = profileReader.GetInt32(2),
Items = Json.Deserialize<List<ProfileItem19>>(profileReader.GetString(3))
});
}
}
}
return profiles;
}
}
}

View File

@ -18,7 +18,9 @@ namespace NzbDrone.Core.MediaFiles
{ ".ogg", Quality.Unknown },
{ ".wma", Quality.WMA },
{ ".wav", Quality.WAV },
{ ".flac", Quality.FLAC }
{ ".wv" , Quality.WAVPACK },
{ ".flac", Quality.FLAC },
{ ".ape", Quality.APE }
};
}

View File

@ -190,6 +190,7 @@
<Compile Include="Datastore\Migration\016_update_artist_history_indexes.cs" />
<Compile Include="Datastore\Migration\017_remove_nma.cs" />
<Compile Include="Datastore\Migration\018_album_disambiguation.cs" />
<Compile Include="Datastore\Migration\019_add_ape_quality_in_profiles.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" />

View File

@ -54,7 +54,7 @@ namespace NzbDrone.Core.Parser
private static readonly Regex SampleSizeRegex = new Regex(@"\b(?:(?<S24>24[ ]bit|24bit|[\[\(].*24bit.*[\]\)]))");
private static readonly Regex CodecRegex = new Regex(@"\b(?:(?<MP3VBR>MP3.*VBR|MPEG Version 1 Audio, Layer 3 vbr)|(?<MP3CBR>MP3|MPEG Version \d+ Audio, Layer 3)|(?<FLAC>flac)|(?<ALAC>alac)|(?<WMA>WMA\d?)|(?<WAV>WAV|PCM)|(?<AAC>M4A|AAC|mp4a)|(?<OGG>OGG|Vorbis))\b",
private static readonly Regex CodecRegex = new Regex(@"\b(?:(?<MP3VBR>MP3.*VBR|MPEG Version 1 Audio, Layer 3 vbr)|(?<MP3CBR>MP3|MPEG Version \d+ Audio, Layer 3)|(?<FLAC>flac)|(?<WAVPACK>wavpack|wv)|(?<ALAC>alac)|(?<WMA>WMA\d?)|(?<WAV>WAV|PCM)|(?<AAC>M4A|AAC|mp4a)|(?<OGG>OGG|Vorbis))\b|(?<APE>monkey's audio|[\[|\(].*ape.*[\]|\)])",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static QualityModel ParseQuality(string name, string desc, int fileBitrate, int fileSampleSize = 0)
@ -66,14 +66,14 @@ namespace NzbDrone.Core.Parser
if (desc.IsNotNullOrWhiteSpace())
{
var descCodec = ParseCodec(desc);
var descCodec = ParseCodec(desc, "");
result.Quality = FindQuality(descCodec, fileBitrate, fileSampleSize);
if (result.Quality != Quality.Unknown) { return result; }
}
var codec = ParseCodec(normalizedName);
var codec = ParseCodec(normalizedName,name);
var bitrate = ParseBitRate(normalizedName);
var sampleSize = ParseSampleSize(normalizedName);
@ -100,6 +100,12 @@ namespace NzbDrone.Core.Parser
case Codec.ALAC:
result.Quality = Quality.ALAC;
break;
case Codec.WAVPACK:
result.Quality = Quality.WAVPACK;
break;
case Codec.APE:
result.Quality = Quality.APE;
break;
case Codec.WMA:
result.Quality = Quality.WMA;
break;
@ -154,7 +160,7 @@ namespace NzbDrone.Core.Parser
return result;
}
private static Codec ParseCodec(string name)
private static Codec ParseCodec(string name, string origName)
{
var match = CodecRegex.Match(name);
@ -167,6 +173,8 @@ namespace NzbDrone.Core.Parser
if (match.Groups["OGG"].Success) { return Codec.OGG; }
if (match.Groups["MP3VBR"].Success) { return Codec.MP3VBR; }
if (match.Groups["MP3CBR"].Success) { return Codec.MP3CBR; }
if (match.Groups["WAVPACK"].Success) { return Codec.WAVPACK; }
if (match.Groups["APE"].Success) { return Codec.APE; }
return Codec.Unknown;
}
@ -232,6 +240,10 @@ namespace NzbDrone.Core.Parser
return Quality.FLAC;
case Codec.ALAC:
return Quality.ALAC;
case Codec.WAVPACK:
return Quality.WAVPACK;
case Codec.APE:
return Quality.APE;
case Codec.WMA:
return Quality.WMA;
case Codec.WAV:
@ -289,12 +301,14 @@ namespace NzbDrone.Core.Parser
MP3VBR,
FLAC,
ALAC,
APE,
WAVPACK,
WMA,
AAC,
AACVBR,
OGG,
WAV,
Unknown,
Unknown
}
public enum BitRate

View File

@ -90,6 +90,8 @@ namespace NzbDrone.Core.Qualities
public static Quality MP3_008 => new Quality(32, "MP3-8"); // For Current Files Only
public static Quality MP3_112 => new Quality(33, "MP3-112"); // For Current Files Only
public static Quality MP3_224 => new Quality(34, "MP3-224"); // For Current Files Only
public static Quality APE => new Quality(35, "APE");
public static Quality WAVPACK => new Quality(36, "WavPack");
static Quality()
{
@ -128,6 +130,8 @@ namespace NzbDrone.Core.Qualities
VORBIS_Q5,
ALAC,
FLAC,
APE,
WAVPACK,
FLAC_24,
WAV
};
@ -173,6 +177,8 @@ namespace NzbDrone.Core.Qualities
new QualityDefinition(Quality.VORBIS_Q10) { Weight = 21, MinSize = 0, MaxSize = 550, GroupName = "High Quality Lossy", GroupWeight = 6 },
new QualityDefinition(Quality.ALAC) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
new QualityDefinition(Quality.FLAC) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
new QualityDefinition(Quality.APE) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
new QualityDefinition(Quality.WAVPACK) { Weight = 22, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
new QualityDefinition(Quality.FLAC_24) { Weight = 23, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
new QualityDefinition(Quality.WAV) { Weight = 24, MinSize = 0, MaxSize = null, GroupWeight = 8}
};