Extend Qualities and Setup Default Groups (#127)

* Extend Qualities and Setup Default Groups

* fixup! Extend Qualities

* fixup! Codacy

* fixup! One more
pull/131/head
Qstick 7 years ago committed by GitHub
parent ead0b7a2f4
commit 10b8174726
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -16,8 +16,13 @@
width: 50px;
}
.audio {
composes: cell from 'Components/Table/Cells/TableRowCell.css';
width: 200px;
}
.language,
.audio,
.video,
.status {
composes: cell from 'Components/Table/Cells/TableRowCell.css';

@ -167,7 +167,7 @@ class EditQualityProfileModalContent extends Component {
name="cutoff"
{...cutoff}
values={qualities}
helpText="Once this quality is reached Sonarr will no longer download episodes"
helpText="Once this quality is reached Lidarr will no longer download albums"
onChange={onCutoffChange}
/>
</FormGroup>
@ -200,7 +200,7 @@ class EditQualityProfileModalContent extends Component {
id &&
<div
className={styles.deleteButtonContainer}
title={isInUse && 'Can\'t delete a quality profile that is attached to a series'}
title={isInUse && 'Can\'t delete a quality profile that is attached to a artist'}
>
<Button
kind={kinds.DANGER}

@ -7,6 +7,7 @@ function MediaInfo(props) {
type,
audioChannels,
audioCodec,
audioBitRate,
videoCodec
} = props;
@ -27,6 +28,16 @@ function MediaInfo(props) {
!!audioChannels &&
audioChannels.toFixed(1)
}
{
((!!audioCodec && !!audioBitRate) || (!!audioChannels && !!audioBitRate)) &&
' - '
}
{
!!audioBitRate &&
audioBitRate
}
</span>
);
}
@ -46,6 +57,7 @@ MediaInfo.propTypes = {
type: PropTypes.string.isRequired,
audioChannels: PropTypes.number,
audioCodec: PropTypes.string,
audioBitRate: PropTypes.string,
videoCodec: PropTypes.string
};

@ -23,11 +23,11 @@ namespace Lidarr.Api.V1.Profiles.Quality
protected override bool IsValid(PropertyValidatorContext context)
{
var cutoff = (int)context.PropertyValue;
int cutoff = (int)context.PropertyValue;
dynamic instance = context.ParentContext.InstanceToValidate;
var items = instance.Items as IList<QualityProfileQualityItemResource>;
var cutoffItem = items.SingleOrDefault(i => i.Id == cutoff || (i.Quality != null && i.Quality.Id == cutoff));
QualityProfileQualityItemResource cutoffItem = items.SingleOrDefault(i => (i.Quality == null && i.Id == cutoff) || i.Quality?.Id == cutoff);
if (cutoffItem == null)
{

@ -1,5 +1,3 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Profiles.Qualities;
using Lidarr.Http;
@ -7,44 +5,19 @@ namespace Lidarr.Api.V1.Profiles.Quality
{
public class QualityProfileSchemaModule : LidarrRestModule<QualityProfileResource>
{
public QualityProfileSchemaModule()
private readonly IProfileService _profileService;
public QualityProfileSchemaModule(IProfileService profileService)
: base("/qualityprofile/schema")
{
_profileService = profileService;
GetResourceSingle = GetSchema;
}
private QualityProfileResource GetSchema()
{
var groupedQualites = NzbDrone.Core.Qualities.Quality.DefaultQualityDefinitions.GroupBy(q => q.Weight);
var items = new List<ProfileQualityItem>();
var groupId = 1000;
foreach (var group in groupedQualites)
{
if (group.Count() == 1)
{
items.Add(new ProfileQualityItem { Quality = group.First().Quality, Allowed = false });
continue;
}
items.Add(new ProfileQualityItem
{
Id = groupId,
Name = group.First().GroupName,
Items = group.Select(g => new ProfileQualityItem
{
Quality = g.Quality,
Allowed = false
}).ToList(),
Allowed = false
});
groupId++;
}
Profile qualityProfile = _profileService.GetDefaultProfile(string.Empty);
var qualityProfile = new Profile();
qualityProfile.Cutoff = NzbDrone.Core.Qualities.Quality.Unknown.Id;
qualityProfile.Items = items;
return qualityProfile.ToResource();
}

@ -6,6 +6,7 @@ namespace Lidarr.Api.V1.TrackFiles
public class MediaInfoResource : RestResource
{
public decimal AudioChannels { get; set; }
public string AudioBitRate { get; set; }
public string AudioCodec { get; set; }
}
@ -21,7 +22,8 @@ namespace Lidarr.Api.V1.TrackFiles
return new MediaInfoResource
{
AudioChannels = MediaInfoFormatter.FormatAudioChannels(model),
AudioCodec = MediaInfoFormatter.FormatAudioCodec(model)
AudioCodec = MediaInfoFormatter.FormatAudioCodec(model),
AudioBitRate = MediaInfoFormatter.FormatAudioBitrate(model)
};
}
}

@ -77,15 +77,15 @@ namespace NzbDrone.Core.Test.Datastore
.All().With(c => c.Id = 0)
.Build().ToList();
history[0].Quality = new QualityModel(Quality.MP3_512, new Revision(version: 2));
history[1].Quality = new QualityModel(Quality.MP3_320, new Revision(version: 2));
history[0].Quality = new QualityModel(Quality.MP3_320, new Revision(version: 2));
history[1].Quality = new QualityModel(Quality.MP3_256, new Revision(version: 2));
Db.InsertMany(history);
var returnedHistory = Db.All<History.History>();
returnedHistory[0].Quality.Quality.Should().Be(Quality.MP3_512);
returnedHistory[0].Quality.Quality.Should().Be(Quality.MP3_320);
}
}
}

@ -0,0 +1,64 @@
using System.Linq;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Datastore.Migration;
using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Datastore.Migration
{
[TestFixture]
public class add_various_qualites_in_profileFixture : MigrationTest<add_various_qualites_in_profile>
{
private string GenerateQualityJson(int quality, bool allowed)
{
return $"{{ \"quality\": {quality}, \"allowed\": {allowed.ToString().ToLowerInvariant()} }}";
}
[Test]
public void should_add_wav_quality()
{
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new
{
Id = 0,
Name = "Lossless",
Cutoff = 1,
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.MP3_320, false)}, {GenerateQualityJson((int)Quality.FLAC, true)}]"
});
});
var profiles = db.Query<Profile4>("SELECT Items FROM Profiles LIMIT 1");
var items = profiles.First().Items;
items.Should().HaveCount(7);
items.Select(v => v.Quality).Should().Contain(13);
items.Select(v => v.Items.Count).Should().BeEquivalentTo(9, 5, 6, 3, 0, 5, 5);
items.Select(v => v.Allowed).Should().BeEquivalentTo(false, true, false, true, false, false, false);
}
[Test]
public void should_add_trash_lossy_quality_group_and_qualities()
{
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new
{
Id = 0,
Name = "Lossless",
Cutoff = 1,
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.MP3_320, false)}, {GenerateQualityJson((int)Quality.FLAC, true)}]"
});
});
var profiles = db.Query<Profile4>("SELECT Items FROM Profiles LIMIT 1");
var items = profiles.First().Items;
items.Should().HaveCount(7);
items.Select(v => v.Name).Should().Contain("Trash Quality Lossy");
items.Select(v => v.Items.Count).Should().BeEquivalentTo(9, 5, 6, 3, 0, 5, 5);
items.Select(v => v.Allowed).Should().BeEquivalentTo(false, true, false, true, false, false, false);
}
}
}

@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
};
_fakeArtist = Builder<Artist>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.MP3_512.Id, Items = Qualities.QualityFixture.GetDefaultQualities() })
.With(c => c.Profile = new Profile { Cutoff = Quality.MP3_320.Id, Items = Qualities.QualityFixture.GetDefaultQualities() })
.With(l => l.LanguageProfile = new LanguageProfile { Cutoff = Language.Spanish, Languages = LanguageFixture.GetDefaultLanguages() })
.Build();
@ -66,7 +66,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
};
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_192, new Revision(version: 1)), Language.English);
_notupgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_512, new Revision(version: 2)), Language.English);
_notupgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_320, new Revision(version: 2)), Language.English);
Mocker.GetMock<IConfigService>()
.SetupGet(s => s.EnableCompletedDownloadHandling)
@ -162,9 +162,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_not_be_upgradable_if_album_is_of_same_quality_as_existing()
{
_fakeArtist.Profile = new Profile { Cutoff = Quality.MP3_512.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_512, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_512, new Revision(version: 1)), Language.English);
_fakeArtist.Profile = new Profile { Cutoff = Quality.MP3_320.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_320, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_320, new Revision(version: 1)), Language.English);
GivenMostRecentForAlbum(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed);
@ -174,9 +174,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_not_be_upgradable_if_cutoff_already_met()
{
_fakeArtist.Profile = new Profile { Cutoff = Quality.MP3_512.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_512, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_512, new Revision(version: 1)), Language.Spanish);
_fakeArtist.Profile = new Profile { Cutoff = Quality.MP3_320.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_320, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_320, new Revision(version: 1)), Language.Spanish);
GivenMostRecentForAlbum(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed);
@ -202,9 +202,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_return_false_if_cutoff_already_met_and_cdh_is_disabled()
{
GivenCdhDisabled();
_fakeArtist.Profile = new Profile { Cutoff = Quality.MP3_512.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_512, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_512, new Revision(version: 1)), Language.Spanish);
_fakeArtist.Profile = new Profile { Cutoff = Quality.MP3_320.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_320, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.MP3_320, new Revision(version: 1)), Language.Spanish);
GivenMostRecentForAlbum(FIRST_ALBUM_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed);

@ -332,7 +332,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_prefer_quality_over_the_number_of_peers()
{
var remoteAlbum1 = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_512), Language.English);
var remoteAlbum1 = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_320), Language.English);
var remoteAlbum2 = GivenRemoteAlbum(new List<Album> { GivenAlbum(1) }, new QualityModel(Quality.MP3_192), Language.English);
var torrentInfo1 = new TorrentInfo();

@ -21,7 +21,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{
new object[] { Quality.MP3_192 },
new object[] { Quality.MP3_256 },
new object[] { Quality.MP3_512 }
new object[] { Quality.MP3_320 }
};
public static object[] DeniedTestCases =
@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void Setup()
{
var fakeArtist = Builder<Artist>.CreateNew()
.With(c => c.Profile = (LazyLoaded<Profile>)new Profile { Cutoff = Quality.MP3_512.Id })
.With(c => c.Profile = (LazyLoaded<Profile>)new Profile { Cutoff = Quality.MP3_320.Id })
.Build();
remoteAlbum = new RemoteAlbum
@ -49,7 +49,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_allow_if_quality_is_defined_in_profile(Quality qualityType)
{
remoteAlbum.ParsedAlbumInfo.Quality.Quality = qualityType;
remoteAlbum.Artist.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.MP3_192, Quality.MP3_256, Quality.MP3_512);
remoteAlbum.Artist.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.MP3_192, Quality.MP3_256, Quality.MP3_320);
Subject.IsSatisfiedBy(remoteAlbum, null).Accepted.Should().BeTrue();
}
@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_not_allow_if_quality_is_not_defined_in_profile(Quality qualityType)
{
remoteAlbum.ParsedAlbumInfo.Quality.Quality = qualityType;
remoteAlbum.Artist.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.MP3_192, Quality.MP3_256, Quality.MP3_512);
remoteAlbum.Artist.Profile.Value.Items = Qualities.QualityFixture.GetDefaultQualities(Quality.MP3_192, Quality.MP3_256, Quality.MP3_320);
Subject.IsSatisfiedBy(remoteAlbum, null).Accepted.Should().BeFalse();
}

@ -22,8 +22,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
new object[] { Quality.MP3_192, 1, Quality.MP3_192, 1, Quality.MP3_192, false },
new object[] { Quality.MP3_320, 1, Quality.MP3_256, 2, Quality.MP3_320, false },
new object[] { Quality.MP3_320, 1, Quality.MP3_256, 2, Quality.MP3_320, false },
new object[] { Quality.MP3_320, 1, Quality.MP3_320, 1, Quality.MP3_320, false },
new object[] { Quality.MP3_512, 1, Quality.MP3_512, 1, Quality.MP3_512, false }
new object[] { Quality.MP3_320, 1, Quality.MP3_320, 1, Quality.MP3_320, false }
};
public static object[] IsUpgradeTestCasesLanguages =

@ -103,7 +103,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_return_true_when_quality_in_queue_is_lower()
{
_artist.Profile.Value.Cutoff = Quality.MP3_512.Id;
_artist.Profile.Value.Cutoff = Quality.MP3_320.Id;
_artist.LanguageProfile.Value.Cutoff = Language.Spanish;
var remoteAlbum = Builder<RemoteAlbum>.CreateNew()
@ -193,7 +193,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_return_false_when_quality_in_queue_is_better()
{
_artist.Profile.Value.Cutoff = Quality.MP3_512.Id;
_artist.Profile.Value.Cutoff = Quality.MP3_320.Id;
var remoteAlbum = Builder<RemoteAlbum>.CreateNew()
.With(r => r.Artist = _artist)

@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var languages = Languages.LanguageFixture.GetDefaultLanguages(Language.English, Language.Spanish);
var fakeArtist = Builder<Artist>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.MP3_512.Id, Items = Qualities.QualityFixture.GetDefaultQualities()})
.With(c => c.Profile = new Profile { Cutoff = Quality.MP3_320.Id, Items = Qualities.QualityFixture.GetDefaultQualities()})
.With(l => l.LanguageProfile = new LanguageProfile { Cutoff = Language.Spanish, Languages = languages })
.Build();
@ -102,8 +102,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test]
public void should_not_be_upgradable_if_qualities_are_the_same()
{
_firstFile.Quality = new QualityModel(Quality.MP3_512);
_parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_512);
_firstFile.Quality = new QualityModel(Quality.MP3_320);
_parseResultSingle.ParsedAlbumInfo.Quality = new QualityModel(Quality.MP3_320);
Subject.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
}

@ -46,7 +46,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
{
new ProfileQualityItem { Allowed = true, Quality = Quality.MP3_256 },
new ProfileQualityItem { Allowed = true, Quality = Quality.MP3_320 },
new ProfileQualityItem { Allowed = true, Quality = Quality.MP3_512 }
new ProfileQualityItem { Allowed = true, Quality = Quality.FLAC }
},
};
@ -130,7 +130,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
[Test]
public void should_not_delete_if_the_grabbed_quality_is_the_lower()
{
GivenHeldRelease(new QualityModel(Quality.MP3_512));
GivenHeldRelease(new QualityModel(Quality.FLAC));
Subject.Handle(new AlbumGrabbedEvent(_remoteAlbum));

@ -175,7 +175,7 @@ namespace NzbDrone.Core.Test.MediaFiles.TrackImport
public void should_use_file_quality_if_folder_quality_is_null()
{
GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = QualityParser.ParseQuality(_audioFiles.Single());
var expectedQuality = QualityParser.ParseQuality(_audioFiles.Single(), null, 0);
var result = Subject.GetImportDecisions(_audioFiles, _artist);
@ -197,7 +197,7 @@ namespace NzbDrone.Core.Test.MediaFiles.TrackImport
public void should_use_file_quality_if_file_quality_was_determined_by_name()
{
GivenSpecifications(_pass1, _pass2, _pass3);
var expectedQuality = QualityParser.ParseQuality(_audioFiles.Single());
var expectedQuality = QualityParser.ParseQuality(_audioFiles.Single(), null, 0);
var result = Subject.GetImportDecisions(_audioFiles, _artist, new ParsedTrackInfo{Quality = new QualityModel(Quality.MP3_256) });

@ -111,6 +111,7 @@
<Compile Include="Datastore\DatabaseRelationshipFixture.cs" />
<Compile Include="Datastore\MappingExtentionFixture.cs" />
<Compile Include="Datastore\MarrDataLazyLoadingFixture.cs" />
<Compile Include="Datastore\Migration\004_add_various_qualities_in_profileFixture.cs" />
<Compile Include="Datastore\ObjectDatabaseFixture.cs" />
<Compile Include="Datastore\PagingSpecExtensionsTests\PagingOffsetFixture.cs" />
<Compile Include="Datastore\PagingSpecExtensionsTests\ToSortDirectionFixture.cs" />
@ -534,7 +535,6 @@
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="Datastore\Migration\" />
<Folder Include="InstrumentationTests\" />
<Folder Include="Providers\" />
<Folder Include="ProviderTests\UpdateProviderTests\" />

@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Test.Framework;
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("The Real Housewives of Some Place - S01E01 - Why are we doing this?", 0)]
public void should_parse_reality_from_title(string title, int reality)
{
QualityParser.ParseQuality(title).Revision.Real.Should().Be(reality);
QualityParser.ParseQuality(title, null, 0).Revision.Real.Should().Be(reality);
}
[TestCase("Chuck.S04E05.HDTV.XviD-LOL", 1)]
@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("[Vivid-Asenshi] Akame ga Kill - 02v2 [1F67AB55]", 2)]
public void should_parse_version_from_title(string title, int version)
{
QualityParser.ParseQuality(title).Revision.Version.Should().Be(version);
QualityParser.ParseQuality(title, null, 0).Revision.Version.Should().Be(version);
}
}
}

@ -1,4 +1,4 @@
using FluentAssertions;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Qualities;
@ -16,80 +16,199 @@ namespace NzbDrone.Core.Test.ParserTests
new object[] {Quality.MP3_VBR},
new object[] {Quality.MP3_256},
new object[] {Quality.MP3_320},
new object[] {Quality.MP3_512},
new object[] {Quality.MP3_VBR_V2},
new object[] {Quality.WAV},
new object[] {Quality.WMA},
new object[] {Quality.AAC_192},
new object[] {Quality.AAC_256},
new object[] {Quality.AAC_320},
new object[] {Quality.AAC_VBR},
new object[] {Quality.ALAC},
new object[] {Quality.FLAC},
};
[TestCase("VA - The Best 101 Love Ballads (2017) MP3 [192 kbps]")]
[TestCase("ATCQ - The Love Movement 1998 2CD 192kbps RIP")]
[TestCase("A Tribe Called Quest - The Love Movement 1998 2CD [192kbps] RIP")]
[TestCase("Maula - Jism 2 [2012] Mp3 - 192Kbps [Extended]- TK")]
[TestCase("VA - Complete Clubland - The Ultimate Ride Of Your Lfe [2014][MP3][192 kbps]")]
[TestCase("Complete Clubland - The Ultimate Ride Of Your Lfe [2014][MP3](192kbps)")]
[TestCase("The Ultimate Ride Of Your Lfe [192 KBPS][2014][MP3]")]
[TestCase("Gary Clark Jr - Live North America 2016 (2017) MP3 192kbps")]
[TestCase("Some Song [192][2014][MP3]")]
[TestCase("Other Song (192)[2014][MP3]")]
public void should_parse_mp3_192_quality(string title)
[TestCase("", "MPEG Version 1 Audio, Layer 3", 96)]
public void should_parse_mp3_96_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, Quality.MP3_192);
ParseAndVerifyQuality(title, desc, bitrate, Quality.MP3_096);
}
[TestCase("Beyoncé Lemonade [320] 2016 Beyonce Lemonade [320] 2016")]
[TestCase("Childish Gambino - Awaken, My Love Album 2016 mp3 320 Kbps")]
[TestCase("Maluma Felices Los 4 MP3 320 Kbps 2017 Download")]
[TestCase("Ricardo Arjona - APNEA (Single 2014) (320 kbps)")]
[TestCase("Kehlani - SweetSexySavage (Deluxe Edition) (2017) 320")]
[TestCase("Anderson Paak - Malibu (320)(2016)")]
public void should_parse_mp3_320_quality(string title)
[TestCase("", "MPEG Version 1 Audio, Layer 3", 128)]
public void should_parse_mp3_128_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, Quality.MP3_320);
ParseAndVerifyQuality(title, desc, bitrate, Quality.MP3_128);
}
[TestCase("", "MPEG Version 1 Audio, Layer 3", 160)]
public void should_parse_mp3_160_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.MP3_160);
}
[TestCase("Caetano Veloso Discografia Completa MP3 @256")]
[TestCase("Little Mix - Salute [Deluxe Edition] [2013] [M4A-256]-V3nom [GLT")]
[TestCase("Ricky Martin - A Quien Quiera Escuchar (2015) 256 kbps [GloDLS]")]
[TestCase("Jake Bugg - Jake Bugg (Album) [2012] {MP3 256 kbps}")]
[TestCase("Milky Chance - Sadnecessary [256 Kbps] [M4A]")]
[TestCase("Clean Bandit - New Eyes [2014] [Mp3-256]-V3nom [GLT]")]
[TestCase("Armin van Buuren - A State Of Trance 810 (20.04.2017) 256 kbps")]
[TestCase("PJ Harvey - Let England Shake [mp3-256-2011][trfkad]")]
[TestCase("X-Men Soundtracks (2006-2014) AAC, 256 kbps")]
[TestCase("Walk the Line Soundtrack (2005) [AAC, 256 kbps]")]
public void should_parse_mp3_256_quality(string title)
[TestCase("VA - The Best 101 Love Ballads (2017) MP3 [192 kbps]", null, 0)]
[TestCase("ATCQ - The Love Movement 1998 2CD 192kbps RIP", null, 0)]
[TestCase("A Tribe Called Quest - The Love Movement 1998 2CD [192kbps] RIP", null, 0)]
[TestCase("Maula - Jism 2 [2012] Mp3 - 192Kbps [Extended]- TK", null, 0)]
[TestCase("VA - Complete Clubland - The Ultimate Ride Of Your Lfe [2014][MP3][192 kbps]", null, 0)]
[TestCase("Complete Clubland - The Ultimate Ride Of Your Lfe [2014][MP3](192kbps)", null, 0)]
[TestCase("The Ultimate Ride Of Your Lfe [192 KBPS][2014][MP3]", null, 0)]
[TestCase("Gary Clark Jr - Live North America 2016 (2017) MP3 192kbps", null, 0)]
[TestCase("Some Song [192][2014][MP3]", null, 0)]
[TestCase("Other Song (192)[2014][MP3]", null, 0)]
[TestCase("", "MPEG Version 1 Audio, Layer 3", 192)]
public void should_parse_mp3_192_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.MP3_192);
}
[TestCase("Caetano Veloso Discografia Completa MP3 @256", null, 0)]
[TestCase("Ricky Martin - A Quien Quiera Escuchar (2015) 256 kbps [GloDLS]", null, 0)]
[TestCase("Jake Bugg - Jake Bugg (Album) [2012] {MP3 256 kbps}", null, 0)]
[TestCase("Clean Bandit - New Eyes [2014] [Mp3-256]-V3nom [GLT]", null, 0)]
[TestCase("Armin van Buuren - A State Of Trance 810 (20.04.2017) 256 kbps", null, 0)]
[TestCase("PJ Harvey - Let England Shake [mp3-256-2011][trfkad]", null, 0)]
[TestCase("", "MPEG Version 1 Audio, Layer 3", 256)]
public void should_parse_mp3_256_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.MP3_256);
}
[TestCase("Beyoncé Lemonade [320] 2016 Beyonce Lemonade [320] 2016", null, 0)]
[TestCase("Childish Gambino - Awaken, My Love Album 2016 mp3 320 Kbps", null, 0)]
[TestCase("Maluma Felices Los 4 MP3 320 Kbps 2017 Download", null, 0)]
[TestCase("Ricardo Arjona - APNEA (Single 2014) (320 kbps)", null, 0)]
[TestCase("Kehlani - SweetSexySavage (Deluxe Edition) (2017) 320", null, 0)]
[TestCase("Anderson Paak - Malibu (320)(2016)", null, 0)]
[TestCase("", "MPEG Version 1 Audio, Layer 3", 320)]
public void should_parse_mp3_320_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.MP3_320);
}
[TestCase("Sia - This Is Acting (Standard Edition) [2016-Web-MP3-V0(VBR)]", null, 0)]
public void should_parse_mp3_vbr_v0_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.MP3_VBR);
}
[TestCase("", "MPEG Version 1 Audio, Layer 3 VBR", 298)]
public void should_parse_mp3_vbr_v2_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.MP3_VBR_V2);
}
[TestCase("Kendrick Lamar - DAMN (2017) FLAC", null, 0)]
[TestCase("Alicia Keys - Vault Playlist Vol. 1 (2017) [FLAC CD]", null, 0)]
[TestCase("Gorillaz - Humanz (Deluxe) - lossless FLAC Tracks - 2017 - CDrip", null, 0)]
[TestCase("David Bowie - Blackstar (2016) [FLAC]", null, 0)]
[TestCase("The Cure - Greatest Hits (2001) FLAC Soup", null, 0)]
[TestCase("Slowdive- Souvlaki (FLAC)", null, 0)]
[TestCase("John Coltrane - Kulu Se Mama (1965) [EAC-FLAC]", null, 0)]
[TestCase("The Rolling Stones - The Very Best Of '75-'94 (1995) {FLAC}", null, 0)]
[TestCase("Migos-No_Label_II-CD-FLAC-2014-FORSAKEN", null, 0)]
[TestCase("ADELE 25 CD FLAC 2015 PERFECT", null, 0)]
[TestCase("", "Flac Audio", 1057)]
public void should_parse_flac_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.FLAC);
}
[TestCase("Beck.-.Guero.2005.[2016.Remastered].24bit.96kHz.LOSSLESS.FLAC", null, 0, 0)]
[TestCase("[R.E.M - Lifes Rich Pageant(1986) [24bit192kHz 2016 Remaster]LOSSLESS FLAC]", null, 0, 0)]
[TestCase("", "Flac Audio", 5057, 24)]
public void should_parse_flac_24bit_quality(string title, string desc, int bitrate, int sampleSize)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.FLAC_24, sampleSize);
}
[TestCase("", "Microsoft WMA2 Audio", 218)]
public void should_parse_wma_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.WMA);
}
[TestCase("", "PCM Audio", 1411)]
public void should_parse_wav_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.WAV);
}
[TestCase("Chuck Berry Discography ALAC", null, 0)]
[TestCase("A$AP Rocky - LONG LIVE A$AP Deluxe asap[ALAC]", null, 0)]
[TestCase("", "MPEG-4 Audio (alac)", 0)]
public void should_parse_alac_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.ALAC);
}
[TestCase("Milky Chance - Sadnecessary [256 Kbps] [M4A]", null, 0)]
[TestCase("Little Mix - Salute [Deluxe Edition] [2013] [M4A-256]-V3nom [GLT", null, 0)]
[TestCase("X-Men Soundtracks (2006-2014) AAC, 256 kbps", null, 0)]
[TestCase("The Weeknd - The Hills - Single[iTunes Plus AAC M4A]", null, 0)]
[TestCase("Walk the Line Soundtrack (2005) [AAC, 256 kbps]", null, 0)]
[TestCase("Firefly Soundtrack(2007 (2002-2003)) [AAC, 256 kbps VBR]", null, 0)]
public void should_parse_aac_256_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.AAC_256);
}
[TestCase("", "MPEG-4 Audio (mp4a)", 320)]
public void should_parse_aac_320_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.AAC_320);
}
public void should_parse_aac_vbr_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, Quality.MP3_256);
ParseAndVerifyQuality(title, desc, bitrate, Quality.AAC_VBR);
}
[TestCase("Caetano Veloso Discografia Completa MP3 @512")]
[TestCase("Walk the Line Soundtrack (2005) [AAC, 512 kbps]")]
[TestCase("Emeli Sande Next To Me (512 Kbps)")]
public void should_parse_mp3_512_quality(string title)
[TestCase("Kirlian Camera - The Ice Curtain - Album 1998 - Ogg-Vorbis Q10", null, 0)]
[TestCase("", "Vorbis Version 0 Audio", 500)]
public void should_parse_vorbis_q10_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, Quality.MP3_512);
ParseAndVerifyQuality(title, desc, bitrate, Quality.VORBIS_Q10);
}
[TestCase("Kendrick Lamar - DAMN (2017) FLAC")]
[TestCase("Alicia Keys - Vault Playlist Vol. 1 (2017) [FLAC CD]")]
[TestCase("Gorillaz - Humanz (Deluxe) - lossless FLAC Tracks - 2017 - CDrip")]
[TestCase("David Bowie - Blackstar (2016) [FLAC]")]
[TestCase("The Cure - Greatest Hits (2001) FLAC Soup")]
[TestCase("Slowdive- Souvlaki (FLAC)")]
[TestCase("John Coltrane - Kulu Se Mama (1965) [EAC-FLAC]")]
[TestCase("The Rolling Stones - The Very Best Of '75-'94 (1995) {FLAC}")]
[TestCase("Migos-No_Label_II-CD-FLAC-2014-FORSAKEN")]
[TestCase("ADELE 25 CD FLAC 2015 PERFECT")]
public void should_parse_flac_quality(string title)
[TestCase("", "Vorbis Version 0 Audio", 320)]
public void should_parse_vorbis_q9_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, Quality.FLAC);
ParseAndVerifyQuality(title, desc, bitrate, Quality.VORBIS_Q9);
}
[TestCase("Various Artists - No New York [1978/Ogg/q8]", null, 0)]
[TestCase("", "Vorbis Version 0 Audio", 256)]
public void should_parse_vorbis_q8_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.VORBIS_Q8);
}
[TestCase("Masters_At_Work-Nuyorican_Soul-.Talkin_Loud.-1997-OGG.Q7", null, 0)]
[TestCase("", "Vorbis Version 0 Audio", 224)]
public void should_parse_vorbis_q7_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.VORBIS_Q7);
}
[TestCase("", "Vorbis Version 0 Audio", 192)]
public void should_parse_vorbis_q6_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.VORBIS_Q6);
}
[TestCase("", "Vorbis Version 0 Audio", 160)]
public void should_parse_vorbis_q5_quality(string title, string desc, int bitrate)
{
ParseAndVerifyQuality(title, desc, bitrate, Quality.VORBIS_Q5);
}
// Flack doesn't get match for 'FLAC' quality
[TestCase("Roberta Flack 2006 - The Very Best of")]
public void should_not_parse_flac_quality(string title)
{
ParseAndVerifyQuality(title, Quality.Unknown);
ParseAndVerifyQuality(title, null, 0, Quality.Unknown);
}
[TestCase("The Chainsmokers & Coldplay - Something Just Like This")]
@ -99,35 +218,38 @@ namespace NzbDrone.Core.Test.ParserTests
[TestCase("Maroon 5 Ft Kendrick Lamar -Dont Wanna Know MP3 2016")]
public void quality_parse(string title)
{
ParseAndVerifyQuality(title, Quality.Unknown);
ParseAndVerifyQuality(title, null, 0, Quality.Unknown);
}
[Test, TestCaseSource(nameof(SelfQualityParserCases))]
public void parsing_our_own_quality_enum_name(Quality quality)
{
var fileName = string.Format("Some album [{0}]", quality.Name);
var result = QualityParser.ParseQuality(fileName);
var result = QualityParser.ParseQuality(fileName, null, 0);
result.Quality.Should().Be(quality);
}
[TestCase("Little Mix - Salute [Deluxe Edition] [2013] [M4A-256]-V3nom [GLT")]
public void should_parse_quality_from_name(string title)
{
QualityParser.ParseQuality(title).QualitySource.Should().Be(QualitySource.Name);
QualityParser.ParseQuality(title, null, 0).QualitySource.Should().Be(QualitySource.Name);
}
[TestCase("01. Kanye West - Ultralight Beam.mp3")]
[TestCase("01. Kanye West - Ultralight Beam.ogg")]
[TestCase("01. Kanye West - Ultralight Beam.m4a")]
[TestCase("01. Kanye West - Ultralight Beam.wma")]
[TestCase("01. Kanye West - Ultralight Beam.wav")]
public void should_parse_quality_from_extension(string title)
{
QualityParser.ParseQuality(title).QualitySource.Should().Be(QualitySource.Extension);
QualityParser.ParseQuality(title, null, 0).QualitySource.Should().Be(QualitySource.Extension);
}
private void ParseAndVerifyQuality(string title, Quality quality)
private void ParseAndVerifyQuality(string name, string desc, int bitrate, Quality quality, int sampleSize = 0)
{
var result = QualityParser.ParseQuality(title);
var result = QualityParser.ParseQuality(name, desc, bitrate, sampleSize);
result.Quality.Should().Be(quality);
}
}
}

@ -18,7 +18,6 @@ namespace NzbDrone.Core.Test.Qualities
new object[] {2, Quality.MP3_VBR},
new object[] {3, Quality.MP3_256},
new object[] {4, Quality.MP3_320},
new object[] {5, Quality.MP3_512},
new object[] {6, Quality.FLAC},
};
@ -29,7 +28,6 @@ namespace NzbDrone.Core.Test.Qualities
new object[] {Quality.MP3_VBR, 2},
new object[] {Quality.MP3_256, 3},
new object[] {Quality.MP3_320, 4},
new object[] {Quality.MP3_512, 5},
new object[] {Quality.FLAC, 6},
};
@ -56,7 +54,6 @@ namespace NzbDrone.Core.Test.Qualities
Quality.MP3_VBR,
Quality.MP3_256,
Quality.MP3_320,
Quality.MP3_512,
Quality.FLAC,
};

@ -41,12 +41,12 @@ namespace NzbDrone.Core.Test.Qualities
new ProfileQualityItem
{
Allowed = true,
Quality = Quality.MP3_320
Quality = Quality.MP3_256
},
new ProfileQualityItem
{
Allowed = true,
Quality = Quality.MP3_512
Quality = Quality.MP3_320
}
}
},
@ -119,8 +119,8 @@ namespace NzbDrone.Core.Test.Qualities
{
GivenGroupedProfile();
var first = new QualityModel(Quality.MP3_320);
var second = new QualityModel(Quality.MP3_512);
var first = new QualityModel(Quality.MP3_256);
var second = new QualityModel(Quality.MP3_320);
var compare = Subject.Compare(first, second);
@ -132,8 +132,8 @@ namespace NzbDrone.Core.Test.Qualities
{
GivenGroupedProfile();
var first = new QualityModel(Quality.MP3_320);
var second = new QualityModel(Quality.MP3_512);
var first = new QualityModel(Quality.MP3_256);
var second = new QualityModel(Quality.MP3_320);
var compare = Subject.Compare(first, second, true);

@ -0,0 +1,335 @@
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(4)]
public class add_various_qualites_in_profile : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.Sql("UPDATE QualityDefinitions SET Title = 'MP3-160' WHERE Quality = 5"); // Change MP3-512 to MP3-160
Execute.WithConnection(ConvertProfile);
}
private void ConvertProfile(IDbConnection conn, IDbTransaction tran)
{
var updater = new ProfileUpdater3(conn, tran);
updater.AddQuality(Qualities4.WAV);
updater.MoveQuality(Qualities4.MP3_160, Qualities4.Unknown);
updater.CreateNewGroup(Qualities4.Unknown, 1000, "Trash Quality Lossy", new[] { Qualities4.MP3_080,
Qualities4.MP3_064,
Qualities4.MP3_056,
Qualities4.MP3_048,
Qualities4.MP3_040,
Qualities4.MP3_032,
Qualities4.MP3_024,
Qualities4.MP3_016,
Qualities4.MP3_008 });
updater.CreateGroupAt(Qualities4.MP3_160, 1001, "Poor Quality Lossy", new[] { Qualities4.MP3_160,
Qualities4.VORBIS_Q5,
Qualities4.MP3_128,
Qualities4.MP3_096,
Qualities4.MP3_112 }); // Group Vorbis-Q5 with MP3-160
updater.CreateGroupAt(Qualities4.MP3_192, 1002, "Low Quality Lossy", new[] { Qualities4.MP3_192,
Qualities4.AAC_192,
Qualities4.VORBIS_Q6,
Qualities4.WMA,
Qualities4.MP3_224 }); // Group Vorbis-Q6, AAC 192, WMA with MP3-190
updater.CreateGroupAt(Qualities4.MP3_256, 1003, "Mid Quality Lossy", new[] { Qualities4.MP3_256,
Qualities4.MP3_VBR_V2,
Qualities4.VORBIS_Q8,
Qualities4.VORBIS_Q7,
Qualities4.AAC_256 }); // Group Mp3-VBR-V2, Vorbis-Q7, Q8, AAC-256 with MP3-256
updater.CreateGroupAt(Qualities4.MP3_320, 1004, "High Quality Lossy", new[] { Qualities4.MP3_VBR,
Qualities4.MP3_320,
Qualities4.AAC_320,
Qualities4.AAC_VBR,
Qualities4.VORBIS_Q10,
Qualities4.VORBIS_Q9 }); // Group MP3-VBR-V0, AAC-VBR, Vorbis-Q10, Q9, AAC-320 with MP3-320
updater.CreateGroupAt(Qualities4.FLAC, 1005, "Lossless", new[] { Qualities4.FLAC,
Qualities4.ALAC,
Qualities4.FLAC_24 }); // Group ALAC with FLAC
updater.Commit();
}
}
public class Profile4
{
public int Id { get; set; }
public string Name { get; set; }
public int Cutoff { get; set; }
public List<ProfileItem4> Items { get; set; }
}
public class ProfileItem4
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int Id { get; set; }
public string Name { get; set; }
public int? Quality { get; set; }
public List<ProfileItem4> Items { get; set; }
public bool Allowed { get; set; }
public ProfileItem4()
{
Items = new List<ProfileItem4>();
}
}
public enum Qualities4
{
Unknown,
MP3_192,
MP3_VBR,
MP3_256,
MP3_320,
MP3_160,
FLAC,
ALAC,
MP3_VBR_V2,
AAC_192,
AAC_256,
AAC_320,
AAC_VBR,
WAV,
VORBIS_Q10,
VORBIS_Q9,
VORBIS_Q8,
VORBIS_Q7,
VORBIS_Q6,
VORBIS_Q5,
WMA,
FLAC_24,
MP3_128,
MP3_096,
MP3_080,
MP3_064,
MP3_056,
MP3_048,
MP3_040,
MP3_032,
MP3_024,
MP3_016,
MP3_008,
MP3_112,
MP3_224
}
public class ProfileUpdater3
{
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
private List<Profile4> _profiles;
private HashSet<Profile4> _changedProfiles = new HashSet<Profile4>();
public ProfileUpdater3(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 AddQuality(Qualities4 quality)
{
foreach (var profile in _profiles)
{
profile.Items.Add(new ProfileItem4
{
Quality = (int)quality,
Allowed = false
});
}
}
public void CreateGroupAt(Qualities4 find, int groupId, string name, Qualities4[] qualities)
{
foreach (var profile in _profiles)
{
var findIndex = profile.Items.FindIndex(v => v.Quality == (int)find);
if (findIndex > -1)
{
var findQuality = profile.Items[findIndex];
profile.Items.Insert(findIndex, new ProfileItem4
{
Id = groupId,
Name = name,
Quality = null,
Items = qualities.Select(q => new ProfileItem4
{
Quality = (int)q,
Allowed = findQuality.Allowed
}).ToList(),
Allowed = findQuality.Allowed
});
}
else
{
// If the ID isn't found for some reason (mangled migration 71?)
profile.Items.Add(new ProfileItem4
{
Id = groupId,
Name = name,
Quality = null,
Items = qualities.Select(q => new ProfileItem4
{
Quality = (int)q,
Allowed = false
}).ToList(),
Allowed = false
});
}
foreach (var quality in qualities)
{
var index = profile.Items.FindIndex(v => v.Quality == (int)quality);
if (index > -1)
{
profile.Items.RemoveAt(index);
}
if (profile.Cutoff == (int)quality)
{
profile.Cutoff = groupId;
}
}
_changedProfiles.Add(profile);
}
}
public void CreateNewGroup(Qualities4 createafter, int groupId, string name, Qualities4[] qualities)
{
foreach (var profile in _profiles)
{
var findIndex = profile.Items.FindIndex(v => v.Quality == (int)createafter) + 1;
if (findIndex > -1)
{
profile.Items.Insert(findIndex, new ProfileItem4
{
Id = groupId,
Name = name,
Quality = null,
Items = qualities.Select(q => new ProfileItem4
{
Quality = (int)q,
Allowed = false
}).ToList(),
Allowed = false
});
}
else
{
profile.Items.Add(new ProfileItem4
{
Id = groupId,
Name = name,
Quality = null,
Items = qualities.Select(q => new ProfileItem4
{
Quality = (int)q,
Allowed = false
}).ToList(),
Allowed = false
});
}
}
}
public void MoveQuality(Qualities4 quality, Qualities4 moveafter)
{
foreach (var profile in _profiles)
{
var findIndex = profile.Items.FindIndex(v => v.Quality == (int)quality);
if (findIndex > -1)
{
var allowed = profile.Items[findIndex].Allowed;
profile.Items.RemoveAt(findIndex);
var findMoveIndex = profile.Items.FindIndex(v => v.Quality == (int)moveafter) + 1;
profile.Items.Insert(findMoveIndex, new ProfileItem4
{
Quality = (int)quality,
Allowed = allowed
});
}
}
}
private List<Profile4> GetProfiles()
{
var profiles = new List<Profile4>();
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 Profile4
{
Id = profileReader.GetInt32(0),
Name = profileReader.GetString(1),
Cutoff = profileReader.GetInt32(2),
Items = Json.Deserialize<List<ProfileItem4>>(profileReader.GetString(3))
});
}
}
}
return profiles;
}
}
}

@ -99,6 +99,7 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<QualityDefinition>().RegisterModel("QualityDefinitions")
.Ignore(d => d.GroupName)
.Ignore(d => d.GroupWeight)
.Ignore(d => d.Weight);
Mapper.Entity<Profile>().RegisterModel("Profiles");

@ -110,15 +110,6 @@ namespace NzbDrone.Core.MediaFiles
_logger.Warn("Unable to parse file on import: [{0}]", audioFile);
return false;
}
var size = _diskProvider.GetFileSize(audioFile);
var quality = QualityParser.ParseQuality(audioFile);
//if (!_detectSample.IsSample(artist, quality, audioFile, size, albumParseResult.IsPossibleSpecialEpisode))
//{
// _logger.Warn("Non-sample file detected: [{0}]", audioFile);
// return false;
//}
}
if (rarFiles.Any(f => _diskProvider.GetFileSize(f) > 10.Megabytes()))

@ -16,7 +16,9 @@ namespace NzbDrone.Core.MediaFiles
{ ".mp3", Quality.Unknown },
{ ".m4a", Quality.Unknown },
{ ".ogg", Quality.Unknown },
{ ".flac", Quality.FLAC },
{ ".wma", Quality.WMA },
{ ".wav", Quality.WAV },
{ ".flac", Quality.FLAC }
};
}

@ -2,6 +2,7 @@ using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using NLog;
using NLog.Fluent;
using NzbDrone.Common.Extensions;
@ -14,6 +15,13 @@ namespace NzbDrone.Core.MediaFiles.MediaInfo
{
private static readonly Logger Logger = NzbDroneLogger.GetLogger(typeof(MediaInfoFormatter));
public static string FormatAudioBitrate(MediaInfoModel mediaInfo)
{
int audioBitrate = mediaInfo.AudioBitrate / 1000;
return audioBitrate + " kbps";
}
public static decimal FormatAudioChannels(MediaInfoModel mediaInfo)
{
var audioChannelPositions = mediaInfo.AudioChannelPositions;

@ -171,14 +171,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport
return musicFiles.Count(file =>
{
var size = _diskProvider.GetFileSize(file);
var fileQuality = QualityParser.ParseQuality(file);
//var sample = _detectSample.IsSample(artist, GetQuality(folderInfo, fileQuality, artist), file, size, folderInfo.IsPossibleSpecialEpisode);
//if (sample)
//{
// return false;
//}
if (SceneChecker.IsSceneTitle(Path.GetFileName(file)))
{

@ -146,7 +146,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual
{
var localTrack = new LocalTrack();
localTrack.Path = file;
localTrack.Quality = QualityParser.ParseQuality(file);
localTrack.Quality = QualityParser.ParseQuality(file, null, 0);
localTrack.Language = LanguageParser.ParseLanguage(file);
localTrack.Size = _diskProvider.GetFileSize(file);

@ -172,6 +172,7 @@
<Compile Include="Datastore\LogDatabase.cs" />
<Compile Include="Datastore\Migration\001_initial_setup.cs" />
<Compile Include="Datastore\Migration\002_add_reason_to_pending_releases.cs" />
<Compile Include="Datastore\Migration\004_add_various_qualities_in_profile.cs" />
<Compile Include="Datastore\Migration\003_add_medium_support.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" />

@ -9,6 +9,7 @@ using NzbDrone.Common.Instrumentation;
using NzbDrone.Core.MediaFiles.MediaInfo;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Languages;
using TagLib;
namespace NzbDrone.Core.Parser
{
@ -264,7 +265,7 @@ namespace NzbDrone.Core.Parser
//result.Language = LanguageParser.ParseLanguage(title);
//Logger.Debug("Language parsed: {0}", result.Language);
result.Quality = QualityParser.ParseQuality(title);
result.Quality = QualityParser.ParseQuality(title, null, 0);
Logger.Debug("Quality parsed: {0}", result.Quality);
// Majora: We don't currently need Release Group for Music.
@ -369,7 +370,7 @@ namespace NzbDrone.Core.Parser
result.Language = LanguageParser.ParseLanguage(releaseTitle);
Logger.Debug("Language parsed: {0}", result.Language);
result.Quality = QualityParser.ParseQuality(title);
result.Quality = QualityParser.ParseQuality(title, null, 0);
Logger.Debug("Quality parsed: {0}", result.Quality);
result.ReleaseGroup = ParseReleaseGroup(releaseTitle);
@ -492,6 +493,8 @@ namespace NzbDrone.Core.Parser
{
var fileInfo = new FileInfo(path);
var file = TagLib.File.Create(path);
Logger.Debug("Starting Tag Parse for {0}", file.Name);
var trackNumber = file.Tag.Track;
var trackTitle = file.Tag.Title;
var discNumber = (file.Tag.Disc > 0) ? Convert.ToInt32(file.Tag.Disc) : 1 ;
@ -532,12 +535,10 @@ namespace NzbDrone.Core.Parser
if (acodec != null && (acodec.MediaTypes & TagLib.MediaTypes.Audio) != TagLib.MediaTypes.None)
{
Logger.Debug("Audio Properties : " + acodec.Description);
Logger.Debug("Bitrate: " + acodec.AudioBitrate);
Logger.Debug("SampleRate: " + acodec.AudioSampleRate);
Logger.Debug("Channels: " + acodec.AudioChannels + "\n");
Logger.Debug("Audio Properties : " + acodec.Description + ", Bitrate: " + acodec.AudioBitrate + ", Sample Size: " +
file.Properties.BitsPerSample + ", SampleRate: " + acodec.AudioSampleRate + ", Channels: " + acodec.AudioChannels);
result.Quality = QualityParser.ParseQuality(acodec.Description, acodec.AudioBitrate, acodec.AudioSampleRate);
result.Quality = QualityParser.ParseQuality(file.Name, acodec.Description, acodec.AudioBitrate, file.Properties.BitsPerSample);
Logger.Debug("Quality parsed: {0}", result.Quality);
}
}

@ -187,7 +187,7 @@ namespace NzbDrone.Core.Parser
if (folderInfo != null)
{
parsedTrackInfo = folderInfo.JsonClone();
parsedTrackInfo.Quality = QualityParser.ParseQuality(Path.GetFileName(filename));
parsedTrackInfo.Quality = QualityParser.ParseQuality(Path.GetFileName(filename), null, 0);
}
else

@ -1,5 +1,6 @@
using System;
using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.RegularExpressions;
using NLog;
using NzbDrone.Common.Extensions;
@ -39,76 +40,98 @@ namespace NzbDrone.Core.Parser
private static readonly Regex RealRegex = new Regex(@"\b(?<real>REAL)\b",
RegexOptions.Compiled);
private static readonly Regex BitRateRegex = new Regex(@"\b(?:(?<B192>192[ ]?kbps|192|[\[\(].*192.*[\]\)])|
(?<B256>256[ ]?kbps|256|[\[\(].*256.*[\]\)])|
(?<B320>320[ ]?kbps|320|[\[\(].*320.*[\]\)])|
(?<B512>512[ ]?kbps|512|[\[\(].*512.*[\]\)])|
(?<VBR>VBR[ ]?kbps|VBR|[\[\(].*VBR.*[\]\)])|
(?<FLAC>FLAC))\b",
private static readonly Regex BitRateRegex = new Regex(@"\b(?:(?<B096>96[ ]?kbps|96|[\[\(].*96.*[\]\)])|
(?<B128>128[ ]?kbps|128|[\[\(].*128.*[\]\)])|
(?<B160>160[ ]?kbps|160|[\[\(].*160.*[\]\)]|q5)|
(?<B192>192[ ]?kbps|192|[\[\(].*192.*[\]\)]|q6)|
(?<B224>224[ ]?kbps|224|[\[\(].*224.*[\]\)]|q7)|
(?<B256>256[ ]?kbps|256|itunes\splus|[\[\(].*256.*[\]\)]|q8)|
(?<B320>320[ ]?kbps|320|[\[\(].*320.*[\]\)]|q9)|
(?<B500>500[ ]?kbps|500|[\[\(].*500.*[\]\)]|q10)|
(?<VBRV0>V0[ ]?kbps|V0|[\[\(].*V0.*[\]\)])|
(?<VBRV2>V2[ ]?kbps|V2|[\[\(].*V2.*[\]\)]))\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
private static readonly Regex CodecRegex = new Regex(@"\b(?:
(?<MP3>MPEG Version \d+ Audio, Layer 3)|
(?<FLAC>flac)|
(?<VBR>VBR|MPEG Version \d+ Audio, Layer 3 VBR$)
)\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
private static readonly Regex SampleSizeRegex = new Regex(@"\b(?:(?<S24>24[ ]bit|24bit|[\[\(].*24bit.*[\]\)]))");
private static readonly Regex CodecRegex = new Regex(@"\b(?:(?<MP3VBR>MP3\S*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",
RegexOptions.Compiled | RegexOptions.IgnoreCase);
public static QualityModel ParseQuality(string desc, int bitrate, int sampleRate)
public static QualityModel ParseQuality(string name, string desc, int fileBitrate, int fileSampleSize = 0)
{
var result = new QualityModel { Quality = Quality.Unknown };
Logger.Debug("Trying to parse quality for {0}", name);
switch (bitrate)
var normalizedName = name.Replace('_', ' ').Trim().ToLower();
var result = ParseQualityModifiers(name, normalizedName);
if (desc.IsNotNullOrWhiteSpace())
{
case 192:
result.Quality = Quality.MP3_192;
break;
case 256:
result.Quality = Quality.MP3_256;
break;
case 320:
result.Quality = Quality.MP3_320;
break;
default:
var match = CodecRegex.Match(desc); //TODO: BUG: Figure out why this always fails
if (!match.Success) result.Quality = Quality.Unknown;
if (match.Groups["VBR"].Success) result.Quality = Quality.MP3_VBR;
if (match.Groups["FLAC"].Success) result.Quality = Quality.FLAC;
break;
}
var descCodec = ParseCodec(desc);
result.Quality = FindQuality(descCodec, fileBitrate, fileSampleSize);
return result;
}
public static QualityModel ParseQuality(string name)
{
Logger.Debug("Trying to parse quality for {0}", name);
if (result.Quality != Quality.Unknown) { return result; }
}
var normalizedName = name.Replace('_', ' ').Trim().ToLower();
var result = ParseQualityModifiers(name, normalizedName);
var codec = ParseCodec(normalizedName);
var bitrate = ParseBitRate(normalizedName);
var sampleSize = ParseSampleSize(normalizedName);
switch(bitrate)
switch(codec)
{
case BitRate.B192:
result.Quality = Quality.MP3_192;
case Codec.MP3VBR:
if (bitrate == BitRate.VBRV0) { result.Quality = Quality.MP3_VBR; }
else if (bitrate == BitRate.VBRV2) { result.Quality = Quality.MP3_VBR_V2; }
else { result.Quality = Quality.Unknown; }
break;
case BitRate.B256:
result.Quality = Quality.MP3_256;
case Codec.MP3CBR:
if (bitrate == BitRate.B096) { result.Quality = Quality.MP3_096; }
else if (bitrate == BitRate.B128) { result.Quality = Quality.MP3_128; }
else if (bitrate == BitRate.B160) { result.Quality = Quality.MP3_160; }
else if (bitrate == BitRate.B192) { result.Quality = Quality.MP3_192; }
else if (bitrate == BitRate.B256) { result.Quality = Quality.MP3_256; }
else if (bitrate == BitRate.B320) { result.Quality = Quality.MP3_320; }
else { result.Quality = Quality.Unknown; }
break;
case BitRate.B320:
result.Quality = Quality.MP3_320;
case Codec.FLAC:
if (sampleSize == SampleSize.S24) {result.Quality = Quality.FLAC_24;}
else {result.Quality = Quality.FLAC;}
break;
case BitRate.B512:
result.Quality = Quality.MP3_512;
case Codec.ALAC:
result.Quality = Quality.ALAC;
break;
case BitRate.FLAC:
result.Quality = Quality.FLAC;
case Codec.WMA:
result.Quality = Quality.WMA;
break;
case BitRate.VBR:
result.Quality = Quality.MP3_VBR;
case Codec.WAV:
result.Quality = Quality.WAV;
break;
case Codec.AAC:
if (bitrate == BitRate.B192) { result.Quality = Quality.AAC_192; }
else if (bitrate == BitRate.B256) { result.Quality = Quality.AAC_256; }
else if (bitrate == BitRate.B320) { result.Quality = Quality.AAC_320; }
else { result.Quality = Quality.AAC_VBR; }
break;
case Codec.AACVBR:
result.Quality = Quality.AAC_VBR;
break;
case Codec.OGG:
if (bitrate == BitRate.B160) { result.Quality = Quality.VORBIS_Q5; }
else if (bitrate == BitRate.B192) { result.Quality = Quality.VORBIS_Q6; }
else if (bitrate == BitRate.B224) { result.Quality = Quality.VORBIS_Q7; }
else if (bitrate == BitRate.B256) { result.Quality = Quality.VORBIS_Q8; }
else if (bitrate == BitRate.B320) { result.Quality = Quality.VORBIS_Q9; }
else if (bitrate == BitRate.B500) { result.Quality = Quality.VORBIS_Q10; }
break;
case Codec.Unknown:
if (bitrate == BitRate.B192) { result.Quality = Quality.MP3_192; }
else if (bitrate == BitRate.B256) { result.Quality = Quality.MP3_256; }
else if (bitrate == BitRate.B320) { result.Quality = Quality.MP3_320; }
else if (bitrate == BitRate.VBR) { result.Quality = Quality.MP3_VBR_V2; }
else { result.Quality = Quality.Unknown; }
break;
default:
result.Quality = Quality.Unknown;
break;
}
@ -130,15 +153,21 @@ namespace NzbDrone.Core.Parser
return result;
}
private static BitRate ParseCodec(string name)
private static Codec ParseCodec(string name)
{
var match = BitRateRegex.Match(name);
if (!match.Success) return BitRate.Unknown;
if (match.Groups["FLAC"].Success) return BitRate.FLAC;
if (match.Groups["VBR"].Success) return BitRate.VBR;
return BitRate.Unknown;
var match = CodecRegex.Match(name);
if (!match.Success) { return Codec.Unknown; }
if (match.Groups["FLAC"].Success) { return Codec.FLAC; }
if (match.Groups["ALAC"].Success) { return Codec.ALAC; }
if (match.Groups["WMA"].Success) { return Codec.WMA; }
if (match.Groups["WAV"].Success) { return Codec.WAV; }
if (match.Groups["AAC"].Success) { return Codec.AAC; }
if (match.Groups["OGG"].Success) { return Codec.OGG; }
if (match.Groups["MP3VBR"].Success) { return Codec.MP3VBR; }
if (match.Groups["MP3CBR"].Success) { return Codec.MP3CBR; }
return Codec.Unknown;
}
private static BitRate ParseBitRate(string name)
@ -147,16 +176,83 @@ namespace NzbDrone.Core.Parser
var match = BitRateRegex.Match(name);
if (!match.Success) return BitRate.Unknown;
if (match.Groups["B192"].Success) return BitRate.B192;
if (match.Groups["B256"].Success) return BitRate.B256;
if (match.Groups["B320"].Success) return BitRate.B320;
if (match.Groups["B512"].Success) return BitRate.B512;
if (match.Groups["FLAC"].Success) return BitRate.FLAC;
if (match.Groups["VBR"].Success) return BitRate.VBR;
if (match.Groups["B096"].Success) { return BitRate.B096; }
if (match.Groups["B128"].Success) { return BitRate.B128; }
if (match.Groups["B160"].Success) { return BitRate.B160; }
if (match.Groups["B192"].Success) { return BitRate.B192; }
if (match.Groups["B224"].Success) { return BitRate.B224; }
if (match.Groups["B256"].Success) { return BitRate.B256; }
if (match.Groups["B320"].Success) { return BitRate.B320; }
if (match.Groups["B500"].Success) { return BitRate.B500; }
if (match.Groups["VBR"].Success) { return BitRate.VBR; }
if (match.Groups["VBRV0"].Success) { return BitRate.VBRV0; }
if (match.Groups["VBRV2"].Success) { return BitRate.VBRV2; }
return BitRate.Unknown;
}
private static SampleSize ParseSampleSize(string name)
{
var match = SampleSizeRegex.Match(name);
if (!match.Success) { return SampleSize.Unknown; }
if (match.Groups["S24"].Success) { return SampleSize.S24; }
return SampleSize.Unknown;
}
private static Quality FindQuality(Codec codec, int bitrate, int sampleSize = 0)
{
switch (codec)
{
case Codec.MP3VBR:
return Quality.MP3_VBR;
case Codec.MP3CBR:
if (bitrate == 8) { return Quality.MP3_008; }
if (bitrate == 16) { return Quality.MP3_016; }
if (bitrate == 24) { return Quality.MP3_024; }
if (bitrate == 32) { return Quality.MP3_032; }
if (bitrate == 40) { return Quality.MP3_040; }
if (bitrate == 48) { return Quality.MP3_048; }
if (bitrate == 56) { return Quality.MP3_056; }
if (bitrate == 64) { return Quality.MP3_064; }
if (bitrate == 80) { return Quality.MP3_080; }
if (bitrate == 96) { return Quality.MP3_096; }
if (bitrate == 112) { return Quality.MP3_112; }
if (bitrate == 128) { return Quality.MP3_128; }
if (bitrate == 160) { return Quality.MP3_160; }
if (bitrate == 192) { return Quality.MP3_192; }
if (bitrate == 224) { return Quality.MP3_224; }
if (bitrate == 256) { return Quality.MP3_256; }
if (bitrate == 320) { return Quality.MP3_320; }
return Quality.Unknown;
case Codec.FLAC:
if (sampleSize == 24) {return Quality.FLAC_24;}
return Quality.FLAC;
case Codec.ALAC:
return Quality.ALAC;
case Codec.WMA:
return Quality.WMA;
case Codec.WAV:
return Quality.WAV;
case Codec.AAC:
if (bitrate == 192) { return Quality.AAC_192; }
if (bitrate == 256) { return Quality.AAC_256; }
if (bitrate == 320) { return Quality.AAC_320; }
return Quality.AAC_VBR;
case Codec.OGG:
if (bitrate == 160) { return Quality.VORBIS_Q5; }
if (bitrate == 192) { return Quality.VORBIS_Q6; }
if (bitrate == 224) { return Quality.VORBIS_Q7; }
if (bitrate == 256) { return Quality.VORBIS_Q8; }
if (bitrate == 320) { return Quality.VORBIS_Q9; }
if (bitrate == 500) { return Quality.VORBIS_Q10; }
return Quality.Unknown;
default:
return Quality.Unknown;
}
}
private static QualityModel ParseQualityModifiers(string name, string normalizedName)
{
var result = new QualityModel { Quality = Quality.Unknown };
@ -166,7 +262,7 @@ namespace NzbDrone.Core.Parser
result.Revision.Version = 2;
}
var versionRegexResult = VersionRegex.Match(normalizedName);
Match versionRegexResult = VersionRegex.Match(normalizedName);
if (versionRegexResult.Success)
{
@ -175,7 +271,7 @@ namespace NzbDrone.Core.Parser
//TODO: re-enable this when we have a reliable way to determine real
//TODO: Only treat it as a real if it comes AFTER the season/epsiode number
var realRegexResult = RealRegex.Matches(name);
MatchCollection realRegexResult = RealRegex.Matches(name);
if (realRegexResult.Count > 0)
{
@ -186,14 +282,39 @@ namespace NzbDrone.Core.Parser
}
}
public enum Codec
{
MP3CBR,
MP3VBR,
FLAC,
ALAC,
WMA,
AAC,
AACVBR,
OGG,
WAV,
Unknown,
}
public enum BitRate
{
B096,
B128,
B160,
B192,
B224,
B256,
B320,
B512,
B500,
VBR,
FLAC,
VBRV0,
VBRV2,
Unknown,
}
public enum SampleSize
{
S24,
Unknown
}
}

@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http.Dispatchers;
using NzbDrone.Core.Lifecycle;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Qualities;
@ -16,6 +18,8 @@ namespace NzbDrone.Core.Profiles.Qualities
List<Profile> All();
Profile Get(int id);
bool Exists(int id);
Profile GetDefaultProfile(string name, Quality cutoff = null, params Quality[] allowed);
}
public class ProfileService : IProfileService, IHandle<ApplicationStartedEvent>
@ -66,45 +70,112 @@ namespace NzbDrone.Core.Profiles.Qualities
return _profileRepository.Exists(id);
}
private Profile AddDefaultProfile(string name, Quality cutoff, params Quality[] allowed)
{
var items = Quality.DefaultQualityDefinitions
.OrderBy(v => v.Weight)
.Select(v => new ProfileQualityItem { Quality = v.Quality, Allowed = allowed.Contains(v.Quality) })
.ToList();
var profile = new Profile { Name = name,
Cutoff = (int)cutoff,
Items = items};
return Add(profile);
}
public void Handle(ApplicationStartedEvent message)
{
if (All().Any()) return;
_logger.Info("Setting up default quality profiles");
AddDefaultProfile("Any",
Quality.Unknown,
AddDefaultProfile("Any", Quality.Unknown,
Quality.Unknown,
Quality.MP3_008,
Quality.MP3_016,
Quality.MP3_024,
Quality.MP3_032,
Quality.MP3_040,
Quality.MP3_048,
Quality.MP3_056,
Quality.MP3_064,
Quality.MP3_080,
Quality.MP3_096,
Quality.MP3_112,
Quality.MP3_128,
Quality.MP3_160,
Quality.MP3_192,
Quality.MP3_224,
Quality.MP3_256,
Quality.MP3_320,
Quality.MP3_512,
Quality.MP3_VBR,
Quality.FLAC);
Quality.MP3_VBR_V2,
Quality.AAC_192,
Quality.AAC_256,
Quality.AAC_320,
Quality.AAC_VBR,
Quality.VORBIS_Q5,
Quality.VORBIS_Q6,
Quality.VORBIS_Q7,
Quality.VORBIS_Q8,
Quality.VORBIS_Q9,
Quality.VORBIS_Q10,
Quality.WMA,
Quality.ALAC,
Quality.FLAC,
Quality.FLAC_24);
AddDefaultProfile("Lossless",
AddDefaultProfile("Lossless", Quality.FLAC,
Quality.FLAC,
Quality.FLAC);
Quality.ALAC,
Quality.FLAC_24);
AddDefaultProfile("Standard",
Quality.MP3_192,
AddDefaultProfile("Standard", Quality.MP3_192,
Quality.MP3_192,
Quality.MP3_256,
Quality.MP3_320);
}
public Profile GetDefaultProfile(string name, Quality cutoff = null, params Quality[] allowed)
{
var groupedQualites = Quality.DefaultQualityDefinitions.GroupBy(q => q.GroupWeight);
var items = new List<ProfileQualityItem>();
var groupId = 1000;
var profileCutoff = cutoff == null ? Quality.Unknown.Id : cutoff.Id;
foreach (var group in groupedQualites)
{
if (group.Count() == 1)
{
var quality = group.First().Quality;
items.Add(new ProfileQualityItem { Quality = quality, Allowed = allowed.Contains(quality) });
continue;
}
var groupAllowed = group.Any(g => allowed.Contains(g.Quality));
items.Add(new ProfileQualityItem
{
Id = groupId,
Name = group.First().GroupName,
Items = group.Select(g => new ProfileQualityItem
{
Quality = g.Quality,
Allowed = groupAllowed
}).ToList(),
Allowed = groupAllowed
});
if (group.Any(s => s.Quality.Id == profileCutoff))
{
profileCutoff = groupId;
}
groupId++;
}
var qualityProfile = new Profile
{
Name = name,
Cutoff = profileCutoff,
Items = items
};
return qualityProfile;
}
private Profile AddDefaultProfile(string name, Quality cutoff, params Quality[] allowed)
{
var profile = GetDefaultProfile(name, cutoff, allowed);
return Add(profile);
}
}
}

@ -57,23 +57,79 @@ namespace NzbDrone.Core.Qualities
public static Quality Unknown => new Quality(0, "Unknown");
public static Quality MP3_192 => new Quality(1, "MP3-192");
public static Quality MP3_VBR => new Quality(2, "MP3-VBR");
public static Quality MP3_VBR => new Quality(2, "MP3-VBR-V0");
public static Quality MP3_256 => new Quality(3, "MP3-256");
public static Quality MP3_320 => new Quality(4, "MP3-320");
public static Quality MP3_512 => new Quality(5, "MP3-512");
public static Quality MP3_160 => new Quality(5, "MP3-160");
public static Quality FLAC => new Quality(6, "FLAC");
public static Quality ALAC => new Quality(7, "ALAC");
public static Quality MP3_VBR_V2 => new Quality(8, "MP3-VBR-V2");
public static Quality AAC_192 => new Quality(9, "AAC-192");
public static Quality AAC_256 => new Quality(10, "AAC-256");
public static Quality AAC_320 => new Quality(11, "AAC-320");
public static Quality AAC_VBR => new Quality(12, "AAC-VBR");
public static Quality WAV => new Quality(13, "WAV");
public static Quality VORBIS_Q10 => new Quality(14, "OGG Vorbis Q10");
public static Quality VORBIS_Q9 => new Quality(15, "OGG Vorbis Q9");
public static Quality VORBIS_Q8 => new Quality(16, "OGG Vorbis Q8");
public static Quality VORBIS_Q7 => new Quality(17, "OGG Vorbis Q7");
public static Quality VORBIS_Q6 => new Quality(18, "OGG Vorbis Q6");
public static Quality VORBIS_Q5 => new Quality(19, "OGG Vorbis Q5");
public static Quality WMA => new Quality(20, "WMA");
public static Quality FLAC_24 => new Quality(21, "FLAC 24bit");
public static Quality MP3_128 => new Quality(22, "MP3-128");
public static Quality MP3_096 => new Quality(23, "MP3-96"); // For Current Files Only
public static Quality MP3_080 => new Quality(24, "MP3-80"); // For Current Files Only
public static Quality MP3_064 => new Quality(25, "MP3-64"); // For Current Files Only
public static Quality MP3_056 => new Quality(26, "MP3-56"); // For Current Files Only
public static Quality MP3_048 => new Quality(27, "MP3-48"); // For Current Files Only
public static Quality MP3_040 => new Quality(28, "MP3-40"); // For Current Files Only
public static Quality MP3_032 => new Quality(29, "MP3-32"); // For Current Files Only
public static Quality MP3_024 => new Quality(30, "MP3-24"); // For Current Files Only
public static Quality MP3_016 => new Quality(31, "MP3-16"); // For Current Files Only
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
static Quality()
{
All = new List<Quality>
{
Unknown,
MP3_008,
MP3_016,
MP3_024,
MP3_032,
MP3_040,
MP3_048,
MP3_056,
MP3_064,
MP3_080,
MP3_096,
MP3_112,
MP3_128,
MP3_160,
MP3_192,
MP3_224,
MP3_VBR,
MP3_256,
MP3_320,
MP3_512,
MP3_VBR_V2,
AAC_192,
AAC_256,
AAC_320,
AAC_VBR,
WMA,
VORBIS_Q10,
VORBIS_Q9,
VORBIS_Q8,
VORBIS_Q7,
VORBIS_Q6,
VORBIS_Q5,
ALAC,
FLAC,
FLAC_24,
WAV
};
AllLookup = new Quality[All.Select(v => v.Id).Max() + 1];
@ -84,13 +140,41 @@ namespace NzbDrone.Core.Qualities
DefaultQualityDefinitions = new HashSet<QualityDefinition>
{
new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.MP3_192) { Weight = 2, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.MP3_VBR) { Weight = 3, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.MP3_256) { Weight = 4, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.MP3_320) { Weight = 5, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.MP3_512) { Weight = 6, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.FLAC) { Weight = 7, MinSize = 0, MaxSize = null },
new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 100, GroupWeight = 1},
new QualityDefinition(Quality.MP3_008) { Weight = 2, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_016) { Weight = 3, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_024) { Weight = 4, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_032) { Weight = 5, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_040) { Weight = 6, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_048) { Weight = 7, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_056) { Weight = 8, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_064) { Weight = 9, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_080) { Weight = 10, MinSize = 0, MaxSize = 100, GroupName = "Trash Quality Lossy", GroupWeight = 2 },
new QualityDefinition(Quality.MP3_096) { Weight = 11, MinSize = 0, MaxSize = 100, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
new QualityDefinition(Quality.MP3_112) { Weight = 12, MinSize = 0, MaxSize = 100, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
new QualityDefinition(Quality.MP3_128) { Weight = 13, MinSize = 0, MaxSize = 100, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
new QualityDefinition(Quality.VORBIS_Q5) { Weight = 14, MinSize = 0, MaxSize = 100, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
new QualityDefinition(Quality.MP3_160) { Weight = 14, MinSize = 0, MaxSize = 100, GroupName = "Poor Quality Lossy", GroupWeight = 3 },
new QualityDefinition(Quality.MP3_192) { Weight = 15, MinSize = 0, MaxSize = 100, GroupName = "Low Quality Lossy", GroupWeight = 4 },
new QualityDefinition(Quality.VORBIS_Q6) { Weight = 15, MinSize = 0, MaxSize = 100, GroupName = "Low Quality Lossy", GroupWeight = 4 },
new QualityDefinition(Quality.AAC_192) { Weight = 15, MinSize = 0, MaxSize = 100, GroupName = "Low Quality Lossy", GroupWeight = 4 },
new QualityDefinition(Quality.WMA) { Weight = 15, MinSize = 0, MaxSize = 100, GroupName = "Low Quality Lossy", GroupWeight = 4 },
new QualityDefinition(Quality.MP3_224) { Weight = 16, MinSize = 0, MaxSize = 100, GroupName = "Low Quality Lossy", GroupWeight = 4 },
new QualityDefinition(Quality.VORBIS_Q7) { Weight = 17, MinSize = 0, MaxSize = 100, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
new QualityDefinition(Quality.MP3_VBR_V2) { Weight = 18, MinSize = 0, MaxSize = 100, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
new QualityDefinition(Quality.MP3_256) { Weight = 18, MinSize = 0, MaxSize = 100, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
new QualityDefinition(Quality.VORBIS_Q8) { Weight = 18, MinSize = 0, MaxSize = 100, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
new QualityDefinition(Quality.AAC_256) { Weight = 18, MinSize = 0, MaxSize = 100, GroupName = "Mid Quality Lossy", GroupWeight = 5 },
new QualityDefinition(Quality.MP3_VBR) { Weight = 19, MinSize = 0, MaxSize = 100, GroupName = "High Quality Lossy", GroupWeight = 6 },
new QualityDefinition(Quality.AAC_VBR) { Weight = 19, MinSize = 0, MaxSize = 100, GroupName = "High Quality Lossy", GroupWeight = 6 },
new QualityDefinition(Quality.MP3_320) { Weight = 20, MinSize = 0, MaxSize = 100, GroupName = "High Quality Lossy", GroupWeight = 6 },
new QualityDefinition(Quality.VORBIS_Q9) { Weight = 20, MinSize = 0, MaxSize = 100, GroupName = "High Quality Lossy", GroupWeight = 6 },
new QualityDefinition(Quality.AAC_320) { Weight = 20, MinSize = 0, MaxSize = 100, GroupName = "High Quality Lossy", GroupWeight = 6 },
new QualityDefinition(Quality.VORBIS_Q10) { Weight = 12, MinSize = 0, MaxSize = 100, 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.FLAC_24) { Weight = 23, MinSize = 0, MaxSize = null, GroupName = "Lossless", GroupWeight = 7 },
new QualityDefinition(Quality.WAV) { Weight = 24, MinSize = 0, MaxSize = null, GroupWeight = 8}
};
}

@ -10,6 +10,7 @@ namespace NzbDrone.Core.Qualities
public string Title { get; set; }
public string GroupName { get; set; }
public int GroupWeight { get; set; }
public int Weight { get; set; }
public double? MinSize { get; set; }

Loading…
Cancel
Save