Quality groups

This commit is contained in:
Mark McDowall 2017-06-18 10:02:20 -07:00 committed by Taloth Saldono
parent 068ea1e934
commit f31ac39e37
52 changed files with 1119 additions and 197 deletions

View File

@ -1,8 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using FluentValidation; using FluentValidation;
using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Profiles.Qualities;
using Sonarr.Http; using Sonarr.Http;
using Sonarr.Http.Mapping;
namespace NzbDrone.Api.Profiles namespace NzbDrone.Api.Profiles
{ {

View File

@ -1,8 +1,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Common.Extensions;
using Sonarr.Http.REST; using Sonarr.Http.REST;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Profiles;
using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -27,13 +26,41 @@ namespace NzbDrone.Api.Profiles
{ {
if (model == null) return null; if (model == null) return null;
var cutoffItem = model.Items.First(q =>
{
if (q.Id == model.Cutoff) return true;
if (q.Quality == null) return false;
return q.Quality.Id == model.Cutoff;
});
var cutoff = cutoffItem.Items == null || cutoffItem.Items.Empty()
? cutoffItem.Quality
: cutoffItem.Items.First().Quality;
return new ProfileResource return new ProfileResource
{ {
Id = model.Id, Id = model.Id,
Name = model.Name, Name = model.Name,
Cutoff = model.Cutoff, Cutoff = cutoff,
Items = model.Items.ConvertAll(ToResource)
// Flatten groups so things don't explode
Items = model.Items.SelectMany(i =>
{
if (i == null)
{
return null;
}
if (i.Items.Any())
{
return i.Items.ConvertAll(ToResource);
}
return new List<ProfileQualityItemResource> {ToResource(i)};
}).ToList()
}; };
} }
@ -57,7 +84,7 @@ namespace NzbDrone.Api.Profiles
Id = resource.Id, Id = resource.Id,
Name = resource.Name, Name = resource.Name,
Cutoff = (Quality)resource.Cutoff.Id, Cutoff = resource.Cutoff.Id,
Items = resource.Items.ConvertAll(ToModel) Items = resource.Items.ConvertAll(ToModel)
}; };
} }

View File

@ -27,7 +27,7 @@ namespace NzbDrone.Api.Profiles
.ToList(); .ToList();
var profile = new Profile(); var profile = new Profile();
profile.Cutoff = Quality.Unknown; profile.Cutoff = Quality.Unknown.Id;
profile.Items = items; profile.Items = items;
return new List<ProfileResource> { profile.ToResource() }; return new List<ProfileResource> { profile.ToResource() };

View File

@ -23,7 +23,7 @@ namespace NzbDrone.Core.Test.Datastore
var profile = new Profile var profile = new Profile
{ {
Name = "Test", Name = "Test",
Cutoff = Quality.WEBDL720p, Cutoff = Quality.WEBDL720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
}; };

View File

@ -0,0 +1,120 @@
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_webrip_and_br480_qualites_in_profileFixture : MigrationTest<add_webrip_and_br480_qualites_in_profile>
{
private string GenerateQualityJson(int quality, bool allowed)
{
return $"{{ \"quality\": {quality}, \"allowed\": {allowed.ToString().ToLowerInvariant()} }}";
}
[Test]
public void should_add_webrip_qualities_and_group_with_webdl()
{
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new
{
Id = 0,
Name = "SDTV",
Cutoff = 1,
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.WEBRip480p, false)}, {GenerateQualityJson((int)Quality.WEBRip720p, false)}, {GenerateQualityJson((int)Quality.WEBRip1080p, false)}, {GenerateQualityJson((int)Quality.WEBRip2160p, false)}]"
});
});
var profiles = db.Query<Profile117>("SELECT Items FROM Profiles LIMIT 1");
var items = profiles.First().Items;
items.Should().HaveCount(6);
items.Select(v => v.Quality).Should().BeEquivalentTo(1, null, null, null, null, null);
items.Select(v => v.Items.Count).Should().BeEquivalentTo(0, 2, 2, 2, 2, 2);
items.Select(v => v.Allowed).Should().BeEquivalentTo(true, false, false, false, false, false);
}
[Test]
public void should_add_bluray480p_quality_and_group_with_dvd()
{
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new
{
Id = 0,
Name = "SDTV",
Cutoff = 1,
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.DVD, false)}, {GenerateQualityJson((int)Quality.Bluray480p, false)}]"
});
});
var profiles = db.Query<Profile117>("SELECT Items FROM Profiles LIMIT 1");
var items = profiles.First().Items;
items.Should().HaveCount(6);
items.Select(v => v.Quality).Should().BeEquivalentTo(1, null, null, null, null, null);
items.Select(v => v.Items.Count).Should().BeEquivalentTo(0, 2, 2, 2, 2, 2);
items.Select(v => v.Allowed).Should().BeEquivalentTo(true, false, false, false, false, false);
}
[Test]
public void should_add_webrip_and_webdl_if_webdl_is_missing()
{
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new
{
Id = 0,
Name = "SDTV",
Cutoff = 1,
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.WEBRip480p, false)}, {GenerateQualityJson((int)Quality.WEBRip720p, false)}, {GenerateQualityJson((int)Quality.WEBRip1080p, false)}]"
});
});
var profiles = db.Query<Profile117>("SELECT Items FROM Profiles LIMIT 1");
var items = profiles.First().Items;
items.Should().HaveCount(6);
items.Select(v => v.Quality).Should().BeEquivalentTo(1, null, null, null, null, null);
items.Select(v => v.Items.Count).Should().BeEquivalentTo(0, 2, 2, 2, 2, 2);
items.Select(v => v.Allowed).Should().BeEquivalentTo(true, false, false, false, false, false);
}
[Test]
public void should_group_webrip_and_webdl_with_the_same_resolution()
{
var db = WithMigrationTestDb(c =>
{
c.Insert.IntoTable("Profiles").Row(new
{
Id = 0,
Name = "SDTV",
Cutoff = 1,
Items = $"[{GenerateQualityJson(1, true)}, {GenerateQualityJson((int)Quality.WEBRip480p, false)}, {GenerateQualityJson((int)Quality.WEBRip720p, false)}, {GenerateQualityJson((int)Quality.WEBRip1080p, false)}, {GenerateQualityJson((int)Quality.WEBRip2160p, false)}]"
});
});
var profiles = db.Query<Profile117>("SELECT Items FROM Profiles LIMIT 1");
var items = profiles.First().Items;
items[1].Items.First().Quality.Should().Be((int)Quality.WEBRip480p);
items[1].Items.Last().Quality.Should().Be((int)Quality.WEBDL480p);
items[2].Items.First().Quality.Should().Be((int)Quality.DVD);
items[2].Items.Last().Quality.Should().Be((int)Quality.Bluray480p);
items[3].Items.First().Quality.Should().Be((int)Quality.WEBRip720p);
items[3].Items.Last().Quality.Should().Be((int)Quality.WEBDL720p);
items[4].Items.First().Quality.Should().Be((int)Quality.WEBRip1080p);
items[4].Items.Last().Quality.Should().Be((int)Quality.WEBDL1080p);
items[5].Items.First().Quality.Should().Be((int)Quality.WEBRip2160p);
items[5].Items.Last().Quality.Should().Be((int)Quality.WEBDL2160p);
}
}
}

View File

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.CutoffNotMet( Subject.CutoffNotMet(
new Profile new Profile
{ {
Cutoff = Quality.Bluray1080p, Cutoff = Quality.Bluray1080p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities() Items = Qualities.QualityFixture.GetDefaultQualities()
}, },
new LanguageProfile new LanguageProfile
@ -36,7 +36,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.CutoffNotMet( Subject.CutoffNotMet(
new Profile new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities() Items = Qualities.QualityFixture.GetDefaultQualities()
}, },
new LanguageProfile new LanguageProfile
@ -53,7 +53,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.CutoffNotMet( Subject.CutoffNotMet(
new Profile new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities() Items = Qualities.QualityFixture.GetDefaultQualities()
}, },
new LanguageProfile new LanguageProfile
@ -70,7 +70,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.CutoffNotMet( Subject.CutoffNotMet(
new Profile new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities() Items = Qualities.QualityFixture.GetDefaultQualities()
}, },
new LanguageProfile new LanguageProfile
@ -89,7 +89,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Subject.CutoffNotMet( Subject.CutoffNotMet(
new Profile new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities() Items = Qualities.QualityFixture.GetDefaultQualities()
}, },
new LanguageProfile new LanguageProfile
@ -108,7 +108,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Profile _profile = new Profile Profile _profile = new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
}; };
@ -131,7 +131,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Profile _profile = new Profile Profile _profile = new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
}; };
@ -155,7 +155,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Profile _profile = new Profile Profile _profile = new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
}; };
@ -179,7 +179,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Profile _profile = new Profile Profile _profile = new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
}; };
@ -203,7 +203,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
Profile _profile = new Profile Profile _profile = new Profile
{ {
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
}; };

View File

@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void Setup() public void Setup()
{ {
var fakeSeries = Builder<Series>.CreateNew() var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Profile = (LazyLoaded<Profile>)new Profile { Cutoff = Quality.Bluray1080p }) .With(c => c.Profile = (LazyLoaded<Profile>)new Profile { Cutoff = Quality.Bluray1080p.Id })
.Build(); .Build();
remoteEpisode = new RemoteEpisode remoteEpisode = new RemoteEpisode

View File

@ -78,7 +78,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var profile = new Profile var profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(), Items = Qualities.QualityFixture.GetDefaultQualities(),
Cutoff = cutoff, Cutoff = cutoff.Id,
}; };
var langProfile = new LanguageProfile var langProfile = new LanguageProfile

View File

@ -106,7 +106,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_true_when_quality_in_queue_is_lower() public void should_return_true_when_quality_in_queue_is_lower()
{ {
_series.Profile.Value.Cutoff = Quality.Bluray1080p; _series.Profile.Value.Cutoff = Quality.Bluray1080p.Id;
_series.LanguageProfile.Value.Cutoff = Language.Spanish; _series.LanguageProfile.Value.Cutoff = Language.Spanish;
var remoteEpisode = Builder<RemoteEpisode>.CreateNew() var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
@ -126,7 +126,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_true_when_quality_in_queue_is_lower_but_language_is_higher() public void should_return_true_when_quality_in_queue_is_lower_but_language_is_higher()
{ {
_series.Profile.Value.Cutoff = Quality.Bluray1080p; _series.Profile.Value.Cutoff = Quality.Bluray1080p.Id;
_series.LanguageProfile.Value.Cutoff = Language.Spanish; _series.LanguageProfile.Value.Cutoff = Language.Spanish;
var remoteEpisode = Builder<RemoteEpisode>.CreateNew() var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
@ -196,7 +196,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_false_when_quality_in_queue_is_better() public void should_return_false_when_quality_in_queue_is_better()
{ {
_series.Profile.Value.Cutoff = Quality.Bluray1080p; _series.Profile.Value.Cutoff = Quality.Bluray1080p.Id;
var remoteEpisode = Builder<RemoteEpisode>.CreateNew() var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
.With(r => r.Series = _series) .With(r => r.Series = _series)
@ -294,7 +294,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_false_if_quality_and_language_in_queue_meets_cutoff() public void should_return_false_if_quality_and_language_in_queue_meets_cutoff()
{ {
_series.Profile.Value.Cutoff = _remoteEpisode.ParsedEpisodeInfo.Quality.Quality; _series.Profile.Value.Cutoff = _remoteEpisode.ParsedEpisodeInfo.Quality.Quality.Id;
var remoteEpisode = Builder<RemoteEpisode>.CreateNew() var remoteEpisode = Builder<RemoteEpisode>.CreateNew()
.With(r => r.Series = _series) .With(r => r.Series = _series)

View File

@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
_profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.WEBDL720p }); _profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.WEBDL720p });
_profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.Bluray720p }); _profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.Bluray720p });
_profile.Cutoff = Quality.WEBDL720p; _profile.Cutoff = Quality.WEBDL720p.Id;
_langProfile.Cutoff = Language.Spanish; _langProfile.Cutoff = Language.Spanish;
_langProfile.Languages = Languages.LanguageFixture.GetDefaultLanguages(); _langProfile.Languages = Languages.LanguageFixture.GetDefaultLanguages();

View File

@ -55,7 +55,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
}; };
var fakeSeries = Builder<Series>.CreateNew() var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p }) .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id })
.With(c => c.Path = @"C:\Series\My.Series".AsOsAgnostic()) .With(c => c.Path = @"C:\Series\My.Series".AsOsAgnostic())
.Build(); .Build();

View File

@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
}; };
_fakeSeries = Builder<Series>.CreateNew() _fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }) .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() })
.With(l => l.LanguageProfile = new LanguageProfile { Cutoff = Language.Spanish, Languages = LanguageFixture.GetDefaultLanguages() }) .With(l => l.LanguageProfile = new LanguageProfile { Cutoff = Language.Spanish, Languages = LanguageFixture.GetDefaultLanguages() })
.Build(); .Build();
@ -163,7 +163,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
[Test] [Test]
public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing() public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing()
{ {
_fakeSeries.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; _fakeSeries.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.English); _upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.English);
@ -175,7 +175,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
[Test] [Test]
public void should_be_upgradable_if_episode_is_of_same_quality_as_existing_but_new_has_better_language() public void should_be_upgradable_if_episode_is_of_same_quality_as_existing_but_new_has_better_language()
{ {
_fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; _fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
_parseResultSingle.ParsedEpisodeInfo.Language = Language.Spanish; _parseResultSingle.ParsedEpisodeInfo.Language = Language.Spanish;
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.English); _upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.English);
@ -188,7 +188,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
[Test] [Test]
public void should_not_be_upgradable_if_cutoff_already_met() public void should_not_be_upgradable_if_cutoff_already_met()
{ {
_fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; _fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.Spanish); _upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.Spanish);
@ -216,7 +216,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
public void should_return_false_if_cutoff_already_met_and_cdh_is_disabled() public void should_return_false_if_cutoff_already_met_and_cdh_is_disabled()
{ {
GivenCdhDisabled(); GivenCdhDisabled();
_fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; _fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities() };
_parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)); _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1));
_upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.Spanish); _upgradableQuality = new Tuple<QualityModel, Language>(new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)), Language.Spanish);

View File

@ -38,7 +38,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync
var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; var doubleEpisodeList = new List<Episode> { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } };
var fakeSeries = Builder<Series>.CreateNew() var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p }) .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id })
.Build(); .Build();
_parseResultMulti = new RemoteEpisode _parseResultMulti = new RemoteEpisode

View File

@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
var languages = Languages.LanguageFixture.GetDefaultLanguages(Language.English, Language.Spanish); var languages = Languages.LanguageFixture.GetDefaultLanguages(Language.English, Language.Spanish);
var fakeSeries = Builder<Series>.CreateNew() var fakeSeries = Builder<Series>.CreateNew()
.With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities()}) .With(c => c.Profile = new Profile { Cutoff = Quality.Bluray1080p.Id, Items = Qualities.QualityFixture.GetDefaultQualities()})
.With(l => l.LanguageProfile = new LanguageProfile { Cutoff = Language.Spanish, Languages = languages }) .With(l => l.LanguageProfile = new LanguageProfile { Cutoff = Language.Spanish, Languages = languages })
.Build(); .Build();

View File

@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
_profile = new Profile _profile = new Profile
{ {
Name = "Test", Name = "Test",
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = new List<ProfileQualityItem> Items = new List<ProfileQualityItem>
{ {
new ProfileQualityItem { Allowed = true, Quality = Quality.HDTV720p }, new ProfileQualityItem { Allowed = true, Quality = Quality.HDTV720p },

View File

@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
_profile = new Profile _profile = new Profile
{ {
Name = "Test", Name = "Test",
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = new List<ProfileQualityItem> Items = new List<ProfileQualityItem>
{ {
new ProfileQualityItem { Allowed = true, Quality = Quality.HDTV720p }, new ProfileQualityItem { Allowed = true, Quality = Quality.HDTV720p },

View File

@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.Download.Pending.PendingReleaseServiceTests
_profile = new Profile _profile = new Profile
{ {
Name = "Test", Name = "Test",
Cutoff = Quality.HDTV720p, Cutoff = Quality.HDTV720p.Id,
Items = new List<ProfileQualityItem> Items = new List<ProfileQualityItem>
{ {
new ProfileQualityItem { Allowed = true, Quality = Quality.HDTV720p }, new ProfileQualityItem { Allowed = true, Quality = Quality.HDTV720p },

View File

@ -30,13 +30,13 @@ namespace NzbDrone.Core.Test.HistoryTests
{ {
_profile = new Profile _profile = new Profile
{ {
Cutoff = Quality.WEBDL720p, Cutoff = Quality.WEBDL720p.Id,
Items = QualityFixture.GetDefaultQualities(), Items = QualityFixture.GetDefaultQualities(),
}; };
_profileCustom = new Profile _profileCustom = new Profile
{ {
Cutoff = Quality.WEBDL720p, Cutoff = Quality.WEBDL720p.Id,
Items = QualityFixture.GetDefaultQualities(Quality.DVD), Items = QualityFixture.GetDefaultQualities(Quality.DVD),
}; };

View File

@ -135,6 +135,7 @@
<Compile Include="Datastore\DatabaseRelationshipFixture.cs" /> <Compile Include="Datastore\DatabaseRelationshipFixture.cs" />
<Compile Include="Datastore\MappingExtentionFixture.cs" /> <Compile Include="Datastore\MappingExtentionFixture.cs" />
<Compile Include="Datastore\MarrDataLazyLoadingFixture.cs" /> <Compile Include="Datastore\MarrDataLazyLoadingFixture.cs" />
<Compile Include="Datastore\Migration\117_add_webrip_qualites_in_profileFixture.cs" />
<Compile Include="Datastore\Migration\108_fix_metadata_file_extensionsFixture.cs" /> <Compile Include="Datastore\Migration\108_fix_metadata_file_extensionsFixture.cs" />
<Compile Include="Datastore\Migration\109_import_extra_files_configFixture.cs" /> <Compile Include="Datastore\Migration\109_import_extra_files_configFixture.cs" />
<Compile Include="Datastore\Migration\110_fix_extra_files_configFixture.cs" /> <Compile Include="Datastore\Migration\110_fix_extra_files_configFixture.cs" />
@ -343,6 +344,7 @@
<Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" /> <Compile Include="ParserTests\MiniSeriesEpisodeParserFixture.cs" />
<Compile Include="ParserTests\ValidateParsedEpisodeInfoFixture.cs" /> <Compile Include="ParserTests\ValidateParsedEpisodeInfoFixture.cs" />
<Compile Include="Profiles\Delay\DelayProfileServiceFixture.cs" /> <Compile Include="Profiles\Delay\DelayProfileServiceFixture.cs" />
<Compile Include="Profiles\Qualities\QualityIndexCompareToFixture.cs" />
<Compile Include="Qualities\RevisionComparableFixture.cs" /> <Compile Include="Qualities\RevisionComparableFixture.cs" />
<Compile Include="QueueTests\QueueServiceFixture.cs" /> <Compile Include="QueueTests\QueueServiceFixture.cs" />
<Compile Include="RemotePathMappingsTests\RemotePathMappingServiceFixture.cs" /> <Compile Include="RemotePathMappingsTests\RemotePathMappingServiceFixture.cs" />

View File

@ -15,6 +15,7 @@ namespace NzbDrone.Core.Test.ParserTests
new object[] { Quality.SDTV }, new object[] { Quality.SDTV },
new object[] { Quality.DVD }, new object[] { Quality.DVD },
new object[] { Quality.WEBDL480p }, new object[] { Quality.WEBDL480p },
new object[] { Quality.Bluray480p },
new object[] { Quality.HDTV720p }, new object[] { Quality.HDTV720p },
new object[] { Quality.HDTV1080p }, new object[] { Quality.HDTV1080p },
new object[] { Quality.HDTV2160p }, new object[] { Quality.HDTV2160p },
@ -67,22 +68,12 @@ namespace NzbDrone.Core.Test.ParserTests
ParseAndVerifyQuality(title, Quality.SDTV, proper); ParseAndVerifyQuality(title, Quality.SDTV, proper);
} }
[TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", true)]
[TestCase("The.Shield.S01E13.NTSC.x264-CtrlSD", false)] [TestCase("The.Shield.S01E13.NTSC.x264-CtrlSD", false)]
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", false)]
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", false)]
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", false)]
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false)]
[TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", false)]
[TestCase("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", false)] [TestCase("The.Girls.Next.Door.S03E06.DVDRip.XviD-WiDE", false)]
[TestCase("The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", false)] [TestCase("The.Girls.Next.Door.S03E06.DVD.Rip.XviD-WiDE", false)]
[TestCase("the.shield.1x13.circles.ws.xvidvd-tns", false)] [TestCase("the.shield.1x13.circles.ws.xvidvd-tns", false)]
[TestCase("the_x-files.9x18.sunshine_days.ac3.ws_dvdrip_xvid-fov.avi", false)] [TestCase("the_x-files.9x18.sunshine_days.ac3.ws_dvdrip_xvid-fov.avi", false)]
[TestCase("[FroZen] Miyuki - 23 [DVD][7F6170E6]", false)] [TestCase("[FroZen] Miyuki - 23 [DVD][7F6170E6]", false)]
[TestCase("Hannibal.S01E05.576p.BluRay.DD5.1.x264-HiSD", false)]
[TestCase("Hannibal.S01E05.480p.BluRay.DD5.1.x264-HiSD", false)]
[TestCase("Heidi Girl of the Alps (BD)(640x480(RAW) (BATCH 1) (1-13)", false)]
[TestCase("[Doki] Clannad - 02 (848x480 XviD BD MP3) [95360783]", false)]
public void should_parse_dvd_quality(string title, bool proper) public void should_parse_dvd_quality(string title, bool proper)
{ {
ParseAndVerifyQuality(title, Quality.DVD, proper); ParseAndVerifyQuality(title, Quality.DVD, proper);
@ -99,6 +90,28 @@ namespace NzbDrone.Core.Test.ParserTests
ParseAndVerifyQuality(title, Quality.WEBDL480p, proper); ParseAndVerifyQuality(title, Quality.WEBDL480p, proper);
} }
[TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3-REPACK.-HELLYWOOD.avi", true)]
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD", false)]
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.X-viD.AC3.-HELLYWOOD", false)]
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.AC3.-HELLYWOOD", false)]
[TestCase("WEEDS.S03E01-06.DUAL.BDRip.XviD.AC3.-HELLYWOOD.avi", false)]
[TestCase("WEEDS.S03E01-06.DUAL.XviD.Bluray.AC3.-HELLYWOOD.avi", false)]
[TestCase("Hannibal.S01E05.576p.BluRay.DD5.1.x264-HiSD", false)]
[TestCase("Hannibal.S01E05.480p.BluRay.DD5.1.x264-HiSD", false)]
[TestCase("Heidi Girl of the Alps (BD)(640x480(RAW) (BATCH 1) (1-13)", false)]
[TestCase("[Doki] Clannad - 02 (848x480 XviD BD MP3) [95360783]", false)]
public void should_parse_bluray480p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.Bluray480p, proper);
}
[TestCase("Clarissa.Explains.It.All.S02E10.480p.HULU.WEBRip.x264-Puffin", false)]
[TestCase("Duck.Dynasty.S10E14.Techs.And.Balances.480p.AE.WEBRip.AAC2.0.x264-SEA", false)]
public void should_parse_webrip480p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.WEBRip480p, proper);
}
[TestCase("Dexter - S01E01 - Title [HDTV]", false)] [TestCase("Dexter - S01E01 - Title [HDTV]", false)]
[TestCase("Dexter - S01E01 - Title [HDTV-720p]", false)] [TestCase("Dexter - S01E01 - Title [HDTV-720p]", false)]
[TestCase("Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", true)] [TestCase("Pawn Stars S04E87 REPACK 720p HDTV x264 aAF", true)]
@ -152,7 +165,6 @@ namespace NzbDrone.Core.Test.ParserTests
ParseAndVerifyQuality(title, Quality.HDTV2160p, proper); ParseAndVerifyQuality(title, Quality.HDTV2160p, proper);
} }
[TestCase("Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false)]
[TestCase("Vanguard S01E04 Mexicos Death Train 720p WEB DL", false)] [TestCase("Vanguard S01E04 Mexicos Death Train 720p WEB DL", false)]
[TestCase("Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", false)] [TestCase("Hawaii Five 0 S02E21 720p WEB DL DD5 1 H 264", false)]
[TestCase("Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", false)] [TestCase("Castle S04E22 720p WEB DL DD5 1 H 264 NFHD", false)]
@ -176,7 +188,14 @@ namespace NzbDrone.Core.Test.ParserTests
ParseAndVerifyQuality(title, Quality.WEBDL720p, proper); ParseAndVerifyQuality(title, Quality.WEBDL720p, proper);
} }
[TestCase("Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false)] [TestCase("Arrested.Development.S04E01.720p.WEBRip.AAC2.0.x264-NFRiP", false)]
[TestCase("American.Gods.S01E07.A.Prayer.For.Mad.Sweeney.720p.AMZN.WEBRip.DD5.1.x264-NTb", false)]
[TestCase("LEGO.Star.Wars.The.Freemaker.Adventures.S07E01.A.New.Home.720p.DSNY.WEBRip.AAC2.0.x264-TVSmash", false)]
public void should_parse_webrip720p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.WEBRip720p, proper);
}
[TestCase("CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", false)] [TestCase("CSI NY S09E03 1080p WEB DL DD5 1 H264 NFHD", false)]
[TestCase("Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", false)] [TestCase("Two and a Half Men S10E03 1080p WEB DL DD5 1 H 264 NFHD", false)]
[TestCase("Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", false)] [TestCase("Criminal.Minds.S08E01.1080p.WEB-DL.DD5.1.H264-NFHD", false)]
@ -202,10 +221,15 @@ namespace NzbDrone.Core.Test.ParserTests
ParseAndVerifyQuality(title, Quality.WEBDL1080p, proper); ParseAndVerifyQuality(title, Quality.WEBDL1080p, proper);
} }
[TestCase("CASANOVA S01E01.2160P AMZN WEBRIP DD2.0 HI10P X264-TROLLUHD", false)] [TestCase("Arrested.Development.S04E01.iNTERNAL.1080p.WEBRip.x264-QRUS", false)]
[TestCase("JUST ADD MAGIC S01E01.2160P AMZN WEBRIP DD2.0 X264-TROLLUHD", false)] [TestCase("Blue.Bloods.S07E20.1080p.AMZN.WEBRip.DDP5.1.x264-ViSUM ac3.(NLsub)", false)]
[TestCase("The.Man.In.The.High.Castle.S01E01.2160p.AMZN.WEBRip.DD2.0.Hi10p.X264-TrollUHD", false)] [TestCase("Better.Call.Saul.S03E09.1080p.NF.WEBRip.DD5.1.x264-ViSUM", false)]
[TestCase("The Man In the High Castle S01E01 2160p AMZN WEBRip DD2.0 Hi10P x264-TrollUHD", false)] public void should_parse_webrip1080p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.WEBRip1080p, proper);
}
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.x264-spamTV", false)] [TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.x264-spamTV", false)]
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.h264-spamTV", false)] [TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.h264-spamTV", false)]
[TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.PROPER.h264-spamTV", true)] [TestCase("The.Nightly.Show.2016.03.14.2160p.WEB.PROPER.h264-spamTV", true)]
@ -216,6 +240,17 @@ namespace NzbDrone.Core.Test.ParserTests
ParseAndVerifyQuality(title, Quality.WEBDL2160p, proper); ParseAndVerifyQuality(title, Quality.WEBDL2160p, proper);
} }
[TestCase("CASANOVA S01E01.2160P AMZN WEBRIP DD2.0 HI10P X264-TROLLUHD", false)]
[TestCase("JUST ADD MAGIC S01E01.2160P AMZN WEBRIP DD2.0 X264-TROLLUHD", false)]
[TestCase("The.Man.In.The.High.Castle.S01E01.2160p.AMZN.WEBRip.DD2.0.Hi10p.X264-TrollUHD", false)]
[TestCase("The Man In the High Castle S01E01 2160p AMZN WEBRip DD2.0 Hi10P x264-TrollUHD", false)]
[TestCase("House.of.Cards.US.S05E08.Chapter.60.2160p.NF.WEBRip.DD5.1.x264-NTb.NLsubs", false)]
[TestCase("Bill Nye Saves the World S01 2160p Netflix WEBRip DD5.1 x264-TrollUHD", false)]
public void should_parse_webrip2160p_quality(string title, bool proper)
{
ParseAndVerifyQuality(title, Quality.WEBRip2160p, proper);
}
[TestCase("WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", false)] [TestCase("WEEDS.S03E01-06.DUAL.Bluray.AC3.-HELLYWOOD.avi", false)]
[TestCase("Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", false)] [TestCase("Chuck - S01E03 - Come Fly With Me - 720p BluRay.mkv", false)]
[TestCase("The Big Bang Theory.S03E01.The Electric Can Opener Fluctuation.m2ts", false)] [TestCase("The Big Bang Theory.S03E01.The Electric Can Opener Fluctuation.m2ts", false)]

View File

@ -1,4 +1,4 @@
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.Profiles
var profile = new Profile var profile = new Profile
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p), Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
Cutoff = Quality.Bluray1080p, Cutoff = Quality.Bluray1080p.Id,
Name = "TestProfile" Name = "TestProfile"
}; };

View File

@ -0,0 +1,36 @@
using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework;
using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Qualities
{
[TestFixture]
public class QualityIndexCompareToFixture : CoreTest
{
[TestCase(1, 0, 1, 0, 0)]
[TestCase(1, 1, 1, 0, 1)]
[TestCase(2, 0, 1, 0, 1)]
[TestCase(1, 0, 1, 1, -1)]
[TestCase(1, 0, 2, 0, -1)]
public void should_match_expected_when_respect_group_order_is_true(int leftIndex, int leftGroupIndex, int rightIndex, int rightGroupIndex, int expected)
{
var left = new QualityIndex(leftIndex, leftGroupIndex);
var right = new QualityIndex(rightIndex, rightGroupIndex);
left.CompareTo(right, true).Should().Be(expected);
}
[TestCase(1, 0, 1, 0, 0)]
[TestCase(1, 1, 1, 0, 0)]
[TestCase(2, 0, 1, 0, 1)]
[TestCase(1, 0, 1, 1, 0)]
[TestCase(1, 0, 2, 0, -1)]
public void should_match_expected_when_respect_group_order_is_false(int leftIndex, int leftGroupIndex, int rightIndex, int rightGroupIndex, int expected)
{
var left = new QualityIndex(leftIndex, leftGroupIndex);
var right = new QualityIndex(rightIndex, rightGroupIndex);
left.CompareTo(right, false).Should().Be(expected);
}
}
}

View File

@ -1,4 +1,5 @@
using FluentAssertions; using System.Collections.Generic;
using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@ -21,6 +22,50 @@ namespace NzbDrone.Core.Test.Qualities
Subject = new QualityModelComparer(new Profile { Items = QualityFixture.GetDefaultQualities(Quality.Bluray720p, Quality.DVD) }); Subject = new QualityModelComparer(new Profile { Items = QualityFixture.GetDefaultQualities(Quality.Bluray720p, Quality.DVD) });
} }
private void GivenGroupedProfile()
{
var profile = new Profile
{
Items = new List<ProfileQualityItem>
{
new ProfileQualityItem
{
Allowed = false,
Quality = Quality.SDTV
},
new ProfileQualityItem
{
Allowed = false,
Quality = Quality.DVD
},
new ProfileQualityItem
{
Allowed = true,
Items = new List<ProfileQualityItem>
{
new ProfileQualityItem
{
Allowed = true,
Quality = Quality.HDTV720p
},
new ProfileQualityItem
{
Allowed = true,
Quality = Quality.WEBDL720p
}
}
},
new ProfileQualityItem
{
Allowed = true,
Quality = Quality.Bluray720p
}
}
};
Subject = new QualityModelComparer(profile);
}
[Test] [Test]
public void should_be_greater_when_first_quality_is_greater_than_second() public void should_be_greater_when_first_quality_is_greater_than_second()
{ {
@ -72,5 +117,31 @@ namespace NzbDrone.Core.Test.Qualities
compare.Should().BeGreaterThan(0); compare.Should().BeGreaterThan(0);
} }
[Test]
public void should_ignore_group_order_by_default()
{
GivenGroupedProfile();
var first = new QualityModel(Quality.HDTV720p);
var second = new QualityModel(Quality.WEBDL720p);
var compare = Subject.Compare(first, second);
compare.Should().Be(0);
}
[Test]
public void should_respect_group_order()
{
GivenGroupedProfile();
var first = new QualityModel(Quality.HDTV720p);
var second = new QualityModel(Quality.WEBDL720p);
var compare = Subject.Compare(first, second, true);
compare.Should().BeLessThan(0);
}
} }
} }

View File

@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeRepositoryTests
var profile = new Profile var profile = new Profile
{ {
Id = 1, Id = 1,
Cutoff = Quality.WEBDL480p, Cutoff = Quality.WEBDL480p.Id,
Items = new List<ProfileQualityItem> Items = new List<ProfileQualityItem>
{ {
new ProfileQualityItem { Allowed = true, Quality = Quality.SDTV }, new ProfileQualityItem { Allowed = true, Quality = Quality.SDTV },

View File

@ -1,4 +1,4 @@
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using System.Linq; using System.Linq;
@ -22,7 +22,7 @@ namespace NzbDrone.Core.Test.TvTests.SeriesRepositoryTests
{ {
Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p), Items = Qualities.QualityFixture.GetDefaultQualities(Quality.Bluray1080p, Quality.DVD, Quality.HDTV720p),
Cutoff = Quality.Bluray1080p, Cutoff = Quality.Bluray1080p.Id,
Name = "TestProfile" Name = "TestProfile"
}; };

View File

@ -0,0 +1,181 @@
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(117)]
public class add_webrip_and_br480_qualites_in_profile : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Execute.WithConnection(ConvertProfile);
}
private void ConvertProfile(IDbConnection conn, IDbTransaction tran)
{
var updater = new ProfileUpdater116(conn, tran);
updater.CreateGroupAt(8, 1000, "WEB 480p", new[] {12, 8}); // Group WEBRip480p with WEBDL480p
updater.CreateGroupAt(2, 1001, "DVD", new[] {2, 13}); // Group Bluray480p with DVD
updater.CreateGroupAt(5, 1002, "WEB 720p", new[] {14, 5}); // Group WEBRip720p with WEBDL720p
updater.CreateGroupAt(3, 1003, "WEB 1080p", new[] {15, 3}); // Group WEBRip1080p with WEBDL1080p
updater.CreateGroupAt(18, 1004, "WEB 2160p", new[] {17, 18}); // Group WEBRip2160p with WEBDL2160p
updater.Commit();
}
}
public class Profile117
{
public int Id { get; set; }
public string Name { get; set; }
public int Cutoff { get; set; }
public List<ProfileItem117> Items { get; set; }
}
public class ProfileItem117
{
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int Id { get; set; }
public string Name { get; set; }
public int? Quality { get; set; }
public List<ProfileItem117> Items { get; set; }
public bool Allowed { get; set; }
public ProfileItem117()
{
Items = new List<ProfileItem117>();
}
}
public class ProfileUpdater116
{
private readonly IDbConnection _connection;
private readonly IDbTransaction _transaction;
private List<Profile117> _profiles;
private HashSet<Profile117> _changedProfiles = new HashSet<Profile117>();
public ProfileUpdater116(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 CreateGroupAt(int find, int groupId, string name, int[] qualities)
{
foreach (var profile in _profiles)
{
var findIndex = profile.Items.FindIndex(v => v.Quality == find);
if (findIndex > -1)
{
var findQuality = profile.Items[findIndex];
profile.Items.Insert(findIndex, new ProfileItem117
{
Id = groupId,
Name = name,
Quality = null,
Items = qualities.Select(q => new ProfileItem117
{
Quality = 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 ProfileItem117
{
Id = groupId,
Name = name,
Quality = null,
Items = qualities.Select(q => new ProfileItem117
{
Quality = q,
Allowed = false
}).ToList(),
Allowed = false
});
}
foreach (var quality in qualities)
{
var index = profile.Items.FindIndex(v => v.Quality == quality);
if (index > -1)
{
profile.Items.RemoveAt(index);
}
if (profile.Cutoff == quality)
{
profile.Cutoff = groupId;
}
}
_changedProfiles.Add(profile);
}
}
private List<Profile117> GetProfiles()
{
var profiles = new List<Profile117>();
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 Profile117
{
Id = profileReader.GetInt32(0),
Name = profileReader.GetString(1),
Cutoff = profileReader.GetInt32(2),
Items = Json.Deserialize<List<ProfileItem117>>(profileReader.GetString(3))
});
}
}
}
return profiles;
}
}
}

View File

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

View File

@ -58,7 +58,7 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareQuality(DownloadDecision x, DownloadDecision y) private int CompareQuality(DownloadDecision x, DownloadDecision y)
{ {
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedEpisodeInfo.Quality.Quality)), return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.Profile.Value.GetIndex(remoteEpisode.ParsedEpisodeInfo.Quality.Quality)),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real), CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version)); CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version));
} }

View File

@ -21,6 +21,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
var profile = subject.Series.Profile.Value;
foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value))
{ {
if (file == null) if (file == null)
@ -30,14 +32,18 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
} }
_logger.Debug("Comparing file quality and language with report. Existing file is {0} - {1}", file.Quality, file.Language); _logger.Debug("Comparing file quality and language with report. Existing file is {0} - {1}", file.Quality, file.Language);
if (!_upgradableSpecification.CutoffNotMet(subject.Series.Profile, if (!_upgradableSpecification.CutoffNotMet(profile,
subject.Series.LanguageProfile, subject.Series.LanguageProfile,
file.Quality, file.Quality,
file.Language, file.Language,
subject.ParsedEpisodeInfo.Quality)) subject.ParsedEpisodeInfo.Quality))
{ {
_logger.Debug("Cutoff already met, rejecting."); _logger.Debug("Cutoff already met, rejecting.");
return Decision.Reject("Existing file meets cutoff: {0} - {1}", subject.Series.Profile.Value.Cutoff, subject.Series.LanguageProfile.Value.Cutoff);
var qualityCutoffIndex = profile.GetIndex(profile.Cutoff);
var qualityCutoff = profile.Items[qualityCutoffIndex.Index];
return Decision.Reject("Existing file meets cutoff: {0} - {1}", qualityCutoff, subject.Series.LanguageProfile.Value.Cutoff);
} }
} }

View File

@ -1,3 +1,4 @@
using System;
using NLog; using NLog;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@ -19,7 +20,12 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
_logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedEpisodeInfo.Quality); _logger.Debug("Checking if report meets quality requirements. {0}", subject.ParsedEpisodeInfo.Quality);
if (!subject.Series.Profile.Value.Items.Exists(v => v.Allowed && v.Quality == subject.ParsedEpisodeInfo.Quality.Quality))
var profile = subject.Series.Profile.Value;
var qualityIndex = profile.GetIndex(subject.ParsedEpisodeInfo.Quality.Quality);
var qualityOrGroup = profile.Items[qualityIndex.Index];
if (!qualityOrGroup.Allowed)
{ {
_logger.Debug("Quality {0} rejected by Series' quality profile", subject.ParsedEpisodeInfo.Quality); _logger.Debug("Quality {0} rejected by Series' quality profile", subject.ParsedEpisodeInfo.Quality);
return Decision.Reject("{0} is not wanted in profile", subject.ParsedEpisodeInfo.Quality.Quality); return Decision.Reject("{0} is not wanted in profile", subject.ParsedEpisodeInfo.Quality.Quality);

View File

@ -1,4 +1,4 @@
using System.Linq; using System.Linq;
using NLog; using NLog;
using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.IndexerSearch.Definitions;
@ -73,8 +73,8 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
} }
// If quality meets or exceeds the best allowed quality in the profile accept it immediately // If quality meets or exceeds the best allowed quality in the profile accept it immediately
var bestQualityInProfile = new QualityModel(profile.LastAllowedQuality()); var bestQualityInProfile = profile.LastAllowedQuality();
var isBestInProfile = comparer.Compare(subject.ParsedEpisodeInfo.Quality, bestQualityInProfile) >= 0; var isBestInProfile = comparer.Compare(subject.ParsedEpisodeInfo.Quality.Quality, bestQualityInProfile) >= 0;
var isBestInProfileLanguage = comparerLanguage.Compare(subject.ParsedEpisodeInfo.Language, languageProfile.LastAllowedLanguage()) >= 0; var isBestInProfileLanguage = comparerLanguage.Compare(subject.ParsedEpisodeInfo.Language, languageProfile.LastAllowedLanguage()) >= 0;
if (isBestInProfile && isBestInProfileLanguage && isPreferredProtocol) if (isBestInProfile && isBestInProfileLanguage && isPreferredProtocol)

View File

@ -55,7 +55,7 @@ namespace NzbDrone.Core.DecisionEngine
public bool IsUpgradable(Profile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, QualityModel newQuality, Language newLanguage) public bool IsUpgradable(Profile profile, LanguageProfile languageProfile, QualityModel currentQuality, Language currentLanguage, QualityModel newQuality, Language newLanguage)
{ {
// If qualities are the same then check language // If qualities are the same then check language
if (newQuality != null && currentQuality == newQuality) if (newQuality != null && new QualityModelComparer(profile).Compare(newQuality, currentQuality) == 0)
{ {
return IsLanguageUpgradable(languageProfile, currentLanguage, newLanguage); return IsLanguageUpgradable(languageProfile, currentLanguage, newLanguage);
} }
@ -72,7 +72,7 @@ namespace NzbDrone.Core.DecisionEngine
public bool QualityCutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null) public bool QualityCutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null)
{ {
var qualityCompare = new QualityModelComparer(profile).Compare(currentQuality.Quality, profile.Cutoff); var qualityCompare = new QualityModelComparer(profile).Compare(currentQuality.Quality.Id, profile.Cutoff);
if (qualityCompare < 0) if (qualityCompare < 0)
{ {
@ -116,6 +116,7 @@ namespace NzbDrone.Core.DecisionEngine
{ {
var compare = newQuality.Revision.CompareTo(currentQuality.Revision); var compare = newQuality.Revision.CompareTo(currentQuality.Revision);
// Comparing the quality directly because we don't want to upgrade to a proper for a webrip from a webdl or vice versa
if (currentQuality.Quality == newQuality.Quality && compare > 0) if (currentQuality.Quality == newQuality.Quality && compare > 0)
{ {
_logger.Debug("New quality is a better revision for existing quality"); _logger.Debug("New quality is a better revision for existing quality");

View File

@ -5,8 +5,6 @@ using NzbDrone.Core.Download;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Languages; using NzbDrone.Core.Languages;
using NzbDrone.Core.Profiles.Qualities;
using System.Collections.Generic;
namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
{ {
@ -23,7 +21,6 @@ namespace NzbDrone.Core.MediaFiles.EpisodeImport.Specifications
{ {
var qualityComparer = new QualityModelComparer(localEpisode.Series.Profile); var qualityComparer = new QualityModelComparer(localEpisode.Series.Profile);
var languageComparer = new LanguageComparer(localEpisode.Series.LanguageProfile); var languageComparer = new LanguageComparer(localEpisode.Series.LanguageProfile);
var profile = localEpisode.Series.Profile.Value;
if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) > 0)) if (localEpisode.Episodes.Any(e => e.EpisodeFileId != 0 && qualityComparer.Compare(e.EpisodeFile.Value.Quality, localEpisode.Quality) > 0))
{ {

View File

@ -320,6 +320,7 @@
<Compile Include="Datastore\Migration\111_create_language_profiles.cs" /> <Compile Include="Datastore\Migration\111_create_language_profiles.cs" />
<Compile Include="Datastore\Migration\115_add_downloadclient_status.cs" /> <Compile Include="Datastore\Migration\115_add_downloadclient_status.cs" />
<Compile Include="Datastore\Migration\121_update_animetosho_url.cs" /> <Compile Include="Datastore\Migration\121_update_animetosho_url.cs" />
<Compile Include="Datastore\Migration\117_add_webrip_and_br480_qualites_in_profile.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationContext.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationContext.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationController.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationController.cs" />
<Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" /> <Compile Include="Datastore\Migration\Framework\MigrationDbFactory.cs" />
@ -1003,6 +1004,7 @@
<Compile Include="Profiles\Qualities\ProfileQualityItem.cs" /> <Compile Include="Profiles\Qualities\ProfileQualityItem.cs" />
<Compile Include="Profiles\Qualities\ProfileRepository.cs" /> <Compile Include="Profiles\Qualities\ProfileRepository.cs" />
<Compile Include="Profiles\Qualities\ProfileService.cs" /> <Compile Include="Profiles\Qualities\ProfileService.cs" />
<Compile Include="Profiles\Qualities\QualityIndex.cs" />
<Compile Include="ProgressMessaging\ProgressMessageContext.cs" /> <Compile Include="ProgressMessaging\ProgressMessageContext.cs" />
<Compile Include="Qualities\QualityDetectionSource.cs" /> <Compile Include="Qualities\QualityDetectionSource.cs" />
<Compile Include="Qualities\QualitySource.cs" /> <Compile Include="Qualities\QualitySource.cs" />

View File

@ -16,7 +16,8 @@ namespace NzbDrone.Core.Parser
private static readonly Regex SourceRegex = new Regex(@"\b(?: private static readonly Regex SourceRegex = new Regex(@"\b(?:
(?<bluray>BluRay|Blu-Ray|HD-?DVD|BD)| (?<bluray>BluRay|Blu-Ray|HD-?DVD|BD)|
(?<webdl>WEB[-_. ]DL|WEBDL|WebRip|AmazonHD|iTunesHD|NetflixU?HD|WebHD|[. ]WEB[. ](?:[xh]26[45]|DDP?5[. ]1)|\d+0p[. ]WEB[. ]|WEB-DLMux)| (?<webdl>WEB[-_. ]DL|WEBDL|AmazonHD|iTunesHD|NetflixU?HD|WebHD|[. ]WEB[. ](?:[xh]26[45]|DDP?5[. ]1)|\d+0p[. ]WEB[. ]|WEB-DLMux)|
(?<webrip>WebRip)|
(?<hdtv>HDTV)| (?<hdtv>HDTV)|
(?<bdrip>BDRip)| (?<bdrip>BDRip)|
(?<brrip>BRRip)| (?<brrip>BRRip)|
@ -99,7 +100,7 @@ namespace NzbDrone.Core.Parser
{ {
if (codecRegex.Groups["xvid"].Success || codecRegex.Groups["divx"].Success) if (codecRegex.Groups["xvid"].Success || codecRegex.Groups["divx"].Success)
{ {
result.Quality = Quality.DVD; result.Quality = Quality.Bluray480p;
return result; return result;
} }
@ -117,7 +118,7 @@ namespace NzbDrone.Core.Parser
if (resolution == Resolution.R480P || resolution == Resolution.R576p) if (resolution == Resolution.R480P || resolution == Resolution.R576p)
{ {
result.Quality = Quality.DVD; result.Quality = Quality.Bluray480p;
return result; return result;
} }
@ -155,6 +156,30 @@ namespace NzbDrone.Core.Parser
return result; return result;
} }
if (sourceMatch.Groups["webrip"].Success)
{
if (resolution == Resolution.R2160p)
{
result.Quality = Quality.WEBRip2160p;
return result;
}
if (resolution == Resolution.R1080p)
{
result.Quality = Quality.WEBRip1080p;
return result;
}
if (resolution == Resolution.R720p)
{
result.Quality = Quality.WEBRip720p;
return result;
}
result.Quality = Quality.WEBRip480p;
return result;
}
if (sourceMatch.Groups["hdtv"].Success) if (sourceMatch.Groups["hdtv"].Success)
{ {
if (resolution == Resolution.R2160p) if (resolution == Resolution.R2160p)
@ -197,7 +222,7 @@ namespace NzbDrone.Core.Parser
result.Quality = Quality.Bluray1080p; result.Quality = Quality.Bluray1080p;
return result; return result;
default: default:
result.Quality = Quality.DVD; result.Quality = Quality.Bluray480p;
return result; return result;
} }
} }

View File

@ -8,12 +8,59 @@ namespace NzbDrone.Core.Profiles.Qualities
public class Profile : ModelBase public class Profile : ModelBase
{ {
public string Name { get; set; } public string Name { get; set; }
public Quality Cutoff { get; set; } public int Cutoff { get; set; }
public List<ProfileQualityItem> Items { get; set; } public List<ProfileQualityItem> Items { get; set; }
public Quality LastAllowedQuality() public Quality LastAllowedQuality()
{ {
return Items.Last(q => q.Allowed).Quality; var lastAllowed = Items.Last(q => q.Allowed);
if (lastAllowed.Quality != null)
{
return lastAllowed.Quality;
}
// Returning any item from the group will work,
// returning the last because it's the true last quality.
return lastAllowed.Items.Last().Quality;
}
public QualityIndex GetIndex(Quality quality)
{
return GetIndex(quality.Id);
}
public QualityIndex GetIndex(int id)
{
for (var i = 0; i < Items.Count; i++)
{
var item = Items[i];
var quality = item.Quality;
// Quality matches by ID
if (quality != null && quality.Id == id)
{
return new QualityIndex(i);
}
// Group matches by ID
if (item.Id > 0 && item.Id == id)
{
return new QualityIndex(i);
}
for (var g = 0; g < item.Items.Count; g++)
{
var groupItem = item.Items[g];
if (groupItem.Quality.Id == id)
{
return new QualityIndex(i, g);
}
}
}
return new QualityIndex();
} }
} }
} }

View File

@ -1,11 +1,47 @@
using NzbDrone.Core.Datastore; using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.Profiles.Qualities namespace NzbDrone.Core.Profiles.Qualities
{ {
public class ProfileQualityItem : IEmbeddedDocument public class ProfileQualityItem : IEmbeddedDocument
{ {
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
public int Id { get; set; }
public string Name { get; set; }
public Quality Quality { get; set; } public Quality Quality { get; set; }
public List<ProfileQualityItem> Items { get; set; }
public bool Allowed { get; set; } public bool Allowed { get; set; }
public ProfileQualityItem()
{
Items = new List<ProfileQualityItem>();
}
public List<Quality> GetQualities()
{
if (Quality == null)
{
return Items.Select(s => s.Quality).ToList();
}
return new List<Quality>{ Quality };
}
public override string ToString()
{
var qualitiesString = string.Join(", ", GetQualities());
if (Name.IsNotNullOrWhiteSpace())
{
return $"{Name} ({qualitiesString})";
}
return qualitiesString;
}
} }
} }

View File

@ -16,6 +16,7 @@ namespace NzbDrone.Core.Profiles.Qualities
List<Profile> All(); List<Profile> All();
Profile Get(int id); Profile Get(int id);
bool Exists(int id); bool Exists(int id);
Profile GetDefaultProfile(string name, Quality cutoff = null, params Quality[] allowed);
} }
public class ProfileService : IProfileService, IHandle<ApplicationStartedEvent> public class ProfileService : IProfileService, IHandle<ApplicationStartedEvent>
@ -67,21 +68,6 @@ namespace NzbDrone.Core.Profiles.Qualities
return _profileRepository.Exists(id); 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 = cutoff,
Items = items,
};
return Add(profile);
}
public void Handle(ApplicationStartedEvent message) public void Handle(ApplicationStartedEvent message)
{ {
if (All().Any()) return; if (All().Any()) return;
@ -90,42 +76,107 @@ namespace NzbDrone.Core.Profiles.Qualities
AddDefaultProfile("Any", Quality.SDTV, AddDefaultProfile("Any", Quality.SDTV,
Quality.SDTV, Quality.SDTV,
Quality.WEBRip480p,
Quality.WEBDL480p, Quality.WEBDL480p,
Quality.DVD, Quality.DVD,
Quality.HDTV720p, Quality.HDTV720p,
Quality.HDTV1080p, Quality.HDTV1080p,
Quality.WEBRip720p,
Quality.WEBDL720p, Quality.WEBDL720p,
Quality.WEBRip1080p,
Quality.WEBDL1080p, Quality.WEBDL1080p,
Quality.Bluray720p, Quality.Bluray720p,
Quality.Bluray1080p); Quality.Bluray1080p);
AddDefaultProfile("SD", Quality.SDTV, AddDefaultProfile("SD", Quality.SDTV,
Quality.SDTV, Quality.SDTV,
Quality.WEBRip480p,
Quality.WEBDL480p, Quality.WEBDL480p,
Quality.DVD); Quality.DVD);
AddDefaultProfile("HD-720p", Quality.HDTV720p, AddDefaultProfile("HD-720p", Quality.HDTV720p,
Quality.HDTV720p, Quality.HDTV720p,
Quality.WEBRip720p,
Quality.WEBDL720p, Quality.WEBDL720p,
Quality.Bluray720p); Quality.Bluray720p);
AddDefaultProfile("HD-1080p", Quality.HDTV1080p, AddDefaultProfile("HD-1080p", Quality.HDTV1080p,
Quality.HDTV1080p, Quality.HDTV1080p,
Quality.WEBRip1080p,
Quality.WEBDL1080p, Quality.WEBDL1080p,
Quality.Bluray1080p); Quality.Bluray1080p);
AddDefaultProfile("Ultra-HD", Quality.HDTV2160p, AddDefaultProfile("Ultra-HD", Quality.HDTV2160p,
Quality.HDTV2160p, Quality.HDTV2160p,
Quality.WEBRip2160p,
Quality.WEBDL2160p, Quality.WEBDL2160p,
Quality.Bluray2160p); Quality.Bluray2160p);
AddDefaultProfile("HD - 720p/1080p", Quality.HDTV720p, AddDefaultProfile("HD - 720p/1080p", Quality.HDTV720p,
Quality.HDTV720p, Quality.HDTV720p,
Quality.HDTV1080p, Quality.HDTV1080p,
Quality.WEBRip720p,
Quality.WEBDL720p, Quality.WEBDL720p,
Quality.WEBRip1080p,
Quality.WEBDL1080p, Quality.WEBDL1080p,
Quality.Bluray720p, Quality.Bluray720p,
Quality.Bluray1080p); Quality.Bluray1080p);
} }
public Profile GetDefaultProfile(string name, Quality cutoff = null, params Quality[] allowed)
{
var groupedQualites = Quality.DefaultQualityDefinitions.GroupBy(q => q.Weight);
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 = group.First().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(g => g.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);
}
} }
} }

View File

@ -0,0 +1,55 @@
using System;
namespace NzbDrone.Core.Profiles.Qualities
{
public class QualityIndex : IComparable, IComparable<QualityIndex>
{
public int Index { get; set; }
public int GroupIndex { get; set; }
public QualityIndex()
{
Index = 0;
GroupIndex = 0;
}
public QualityIndex(int index)
{
Index = index;
GroupIndex = 0;
}
public QualityIndex(int index, int groupIndex)
{
Index = index;
GroupIndex = groupIndex;
}
public int CompareTo(object obj)
{
return CompareTo((QualityIndex)obj, true);
}
public int CompareTo(QualityIndex other)
{
return CompareTo(other, true);
}
public int CompareTo(QualityIndex right, bool respectGroupOrder)
{
if (right == null)
{
return 1;
}
var indexCompare = Index.CompareTo(right.Index);
if (respectGroupOrder && indexCompare == 0)
{
return GroupIndex.CompareTo(right.GroupIndex);
}
return indexCompare;;
}
}
}

View File

@ -71,12 +71,12 @@ namespace NzbDrone.Core.Qualities
public static Quality HDTV1080p => new Quality(9, "HDTV-1080p", QualitySource.Television, 1080); public static Quality HDTV1080p => new Quality(9, "HDTV-1080p", QualitySource.Television, 1080);
public static Quality RAWHD => new Quality(10, "Raw-HD", QualitySource.TelevisionRaw, 1080); public static Quality RAWHD => new Quality(10, "Raw-HD", QualitySource.TelevisionRaw, 1080);
//public static Quality HDTV480p { get { return new Quality(11, "HDTV-480p", QualitySource.Television, 480); } } //public static Quality HDTV480p { get { return new Quality(11, "HDTV-480p", QualitySource.Television, 480); } }
//public static Quality WEBRip480p { get { return new Quality(12, "WEBRip-480p", QualitySource.WebRip, 480); } } public static Quality WEBRip480p { get { return new Quality(12, "WEBRip-480p", QualitySource.WebRip, 480); } }
//public static Quality Bluray480p { get { return new Quality(13, "Bluray-480p", QualitySource.Bluray, 480); } } public static Quality Bluray480p { get { return new Quality(13, "Bluray-480p", QualitySource.Bluray, 480); } }
//public static Quality WEBRip720p { get { return new Quality(14, "WEBRip-720p", QualitySource.WebRip, 720); } } public static Quality WEBRip720p { get { return new Quality(14, "WEBRip-720p", QualitySource.WebRip, 720); } }
//public static Quality WEBRip1080p { get { return new Quality(15, "WEBRip-1080p", QualitySource.WebRip, 1080); } } public static Quality WEBRip1080p { get { return new Quality(15, "WEBRip-1080p", QualitySource.WebRip, 1080); } }
public static Quality HDTV2160p => new Quality(16, "HDTV-2160p", QualitySource.Television, 2160); public static Quality HDTV2160p => new Quality(16, "HDTV-2160p", QualitySource.Television, 2160);
//public static Quality WEBRip2160p { get { return new Quality(17, "WEBRip-2160p", QualitySource.WebRip, 2160); } } public static Quality WEBRip2160p { get { return new Quality(17, "WEBRip-2160p", QualitySource.WebRip, 2160); } }
public static Quality WEBDL2160p => new Quality(18, "WEBDL-2160p", QualitySource.Web, 2160); public static Quality WEBDL2160p => new Quality(18, "WEBDL-2160p", QualitySource.Web, 2160);
public static Quality Bluray2160p => new Quality(19, "Bluray-2160p", QualitySource.Bluray, 2160); public static Quality Bluray2160p => new Quality(19, "Bluray-2160p", QualitySource.Bluray, 2160);
@ -87,17 +87,22 @@ namespace NzbDrone.Core.Qualities
Unknown, Unknown,
SDTV, SDTV,
DVD, DVD,
WEBDL1080p, WEBRip480p,
WEBDL480p,
Bluray480p,
HDTV720p, HDTV720p,
WEBRip720p,
WEBDL720p, WEBDL720p,
Bluray720p, Bluray720p,
Bluray1080p, Bluray1080p,
WEBDL480p,
HDTV1080p, HDTV1080p,
WEBRip1080p,
WEBDL1080p,
RAWHD, RAWHD,
HDTV2160p, HDTV2160p,
WEBRip2160p,
WEBDL2160p, WEBDL2160p,
Bluray2160p, Bluray2160p
}; };
AllLookup = new Quality[All.Select(v => v.Id).Max() + 1]; AllLookup = new Quality[All.Select(v => v.Id).Max() + 1];
@ -110,18 +115,23 @@ namespace NzbDrone.Core.Qualities
{ {
new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.Unknown) { Weight = 1, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.SDTV) { Weight = 2, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.SDTV) { Weight = 2, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.WEBDL480p) { Weight = 3, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.WEBRip480p) { Weight = 3, MinSize = 0, MaxSize = 100, GroupName = "WEB 480p" },
new QualityDefinition(Quality.DVD) { Weight = 4, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.WEBDL480p) { Weight = 3, MinSize = 0, MaxSize = 100, GroupName = "WEB 480p" },
new QualityDefinition(Quality.HDTV720p) { Weight = 5, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.DVD) { Weight = 4, MinSize = 0, MaxSize = 100, GroupName = "DVD" },
new QualityDefinition(Quality.HDTV1080p) { Weight = 6, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.Bluray480p) { Weight = 5, MinSize = 0, MaxSize = 100, GroupName = "DVD" },
new QualityDefinition(Quality.RAWHD) { Weight = 7, MinSize = 0, MaxSize = null }, new QualityDefinition(Quality.HDTV720p) { Weight = 6, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.WEBDL720p) { Weight = 8, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.HDTV1080p) { Weight = 7, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.Bluray720p) { Weight = 9, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.RAWHD) { Weight = 8, MinSize = 0, MaxSize = null },
new QualityDefinition(Quality.WEBDL1080p) { Weight = 10, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.WEBRip720p) { Weight = 9, MinSize = 0, MaxSize = 100, GroupName = "WEB 720p" },
new QualityDefinition(Quality.Bluray1080p) { Weight = 11, MinSize = 0, MaxSize = 100 }, new QualityDefinition(Quality.WEBDL720p) { Weight = 9, MinSize = 0, MaxSize = 100, GroupName = "WEB 720p" },
new QualityDefinition(Quality.HDTV2160p) { Weight = 12, MinSize = 0, MaxSize = null }, new QualityDefinition(Quality.Bluray720p) { Weight = 10, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.WEBDL2160p) { Weight = 13, MinSize = 0, MaxSize = null }, new QualityDefinition(Quality.WEBRip1080p) { Weight = 11, MinSize = 0, MaxSize = 100, GroupName = "WEB 1080p" },
new QualityDefinition(Quality.Bluray2160p) { Weight = 14, MinSize = 0, MaxSize = null }, new QualityDefinition(Quality.WEBDL1080p) { Weight = 11, MinSize = 0, MaxSize = 100, GroupName = "WEB 1080p" },
new QualityDefinition(Quality.Bluray1080p) { Weight = 12, MinSize = 0, MaxSize = 100 },
new QualityDefinition(Quality.HDTV2160p) { Weight = 13, MinSize = 0, MaxSize = null },
new QualityDefinition(Quality.WEBRip2160p) { Weight = 14, MinSize = 0, MaxSize = null, GroupName = "WEB 2160p" },
new QualityDefinition(Quality.WEBDL2160p) { Weight = 14, MinSize = 0, MaxSize = null, GroupName = "WEB 2160p" },
new QualityDefinition(Quality.Bluray2160p) { Weight = 15, MinSize = 0, MaxSize = null }
}; };
} }

View File

@ -1,4 +1,5 @@
using NzbDrone.Core.Datastore; using Newtonsoft.Json;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Qualities namespace NzbDrone.Core.Qualities
@ -9,6 +10,7 @@ namespace NzbDrone.Core.Qualities
public string Title { get; set; } public string Title { get; set; }
public string GroupName { get; set; }
public int Weight { get; set; } public int Weight { get; set; }
public double? MinSize { get; set; } public double? MinSize { get; set; }

View File

@ -16,17 +16,35 @@ namespace NzbDrone.Core.Qualities
_profile = profile; _profile = profile;
} }
public int Compare(int left, int right, bool respectGroupOrder = false)
{
var leftIndex = _profile.GetIndex(left);
var rightIndex = _profile.GetIndex(right);
return leftIndex.CompareTo(rightIndex, respectGroupOrder);
}
public int Compare(Quality left, Quality right) public int Compare(Quality left, Quality right)
{ {
int leftIndex = _profile.Items.FindIndex(v => v.Quality == left); return Compare(left, right, false);
int rightIndex = _profile.Items.FindIndex(v => v.Quality == right); }
return leftIndex.CompareTo(rightIndex); public int Compare(Quality left, Quality right, bool respectGroupOrder)
{
var leftIndex = _profile.GetIndex(left);
var rightIndex = _profile.GetIndex(right);
return leftIndex.CompareTo(rightIndex, respectGroupOrder);
} }
public int Compare(QualityModel left, QualityModel right) public int Compare(QualityModel left, QualityModel right)
{ {
int result = Compare(left.Quality, right.Quality); return Compare(left, right, false);
}
public int Compare(QualityModel left, QualityModel right, bool respectGroupOrder)
{
int result = Compare(left.Quality, right.Quality, respectGroupOrder);
if (result == 0) if (result == 0)
{ {

View File

@ -19,14 +19,12 @@ namespace NzbDrone.Core.Tv
private readonly IEpisodeRepository _episodeRepository; private readonly IEpisodeRepository _episodeRepository;
private readonly IProfileService _profileService; private readonly IProfileService _profileService;
private readonly ILanguageProfileService _languageProfileService; private readonly ILanguageProfileService _languageProfileService;
private readonly Logger _logger;
public EpisodeCutoffService(IEpisodeRepository episodeRepository, IProfileService profileService, ILanguageProfileService languageProfileService, Logger logger) public EpisodeCutoffService(IEpisodeRepository episodeRepository, IProfileService profileService, ILanguageProfileService languageProfileService, Logger logger)
{ {
_episodeRepository = episodeRepository; _episodeRepository = episodeRepository;
_profileService = profileService; _profileService = profileService;
_languageProfileService = languageProfileService; _languageProfileService = languageProfileService;
_logger = logger;
} }
public PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec) public PagingSpec<Episode> EpisodesWhereCutoffUnmet(PagingSpec<Episode> pagingSpec)
@ -39,11 +37,12 @@ namespace NzbDrone.Core.Tv
//Get all items less than the cutoff //Get all items less than the cutoff
foreach (var profile in profiles) foreach (var profile in profiles)
{ {
var cutoffIndex = profile.Items.FindIndex(v => v.Quality == profile.Cutoff); var cutoffIndex = profile.GetIndex(profile.Cutoff);
var belowCutoff = profile.Items.Take(cutoffIndex).ToList(); var belowCutoff = profile.Items.Take(cutoffIndex.Index).ToList();
if (belowCutoff.Any()) if (belowCutoff.Any())
{ {
qualitiesBelowCutoff.Add(new QualitiesBelowCutoff(profile.Id, belowCutoff.Select(i => i.Quality.Id))); qualitiesBelowCutoff.Add(new QualitiesBelowCutoff(profile.Id, belowCutoff.SelectMany(i => i.GetQualities().Select(q => q.Id))));
} }
} }

View File

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;

View File

@ -0,0 +1,39 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using FluentValidation.Validators;
namespace Sonarr.Api.V3.Profiles.Quality
{
public static class QualityCutoffValidator
{
public static IRuleBuilderOptions<T, int> ValidCutoff<T>(this IRuleBuilder<T, int> ruleBuilder)
{
return ruleBuilder.SetValidator(new ValidCutoffValidator<T>());
}
}
public class ValidCutoffValidator<T> : PropertyValidator
{
public ValidCutoffValidator()
: base("Cutoff must be an allowed quality or group")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var cutoff = (int)context.PropertyValue;
dynamic instance = context.ParentContext.InstanceToValidate;
var items = instance.Items as IList<QualityProfileQualityItemResource>;
var cutoffItem = items.SingleOrDefault(i => (i.Quality == null && i.Id == cutoff) || i.Quality?.Id == cutoff);
if (cutoffItem == null) return false;
if (!cutoffItem.Allowed) return false;
return true;
}
}
}

View File

@ -0,0 +1,197 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using FluentValidation.Validators;
using NzbDrone.Common.Extensions;
namespace Sonarr.Api.V3.Profiles.Quality
{
public static class QualityItemsValidator
{
public static IRuleBuilderOptions<T, IList<QualityProfileQualityItemResource>> ValidItems<T>(this IRuleBuilder<T, IList<QualityProfileQualityItemResource>> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
ruleBuilder.SetValidator(new AllowedValidator<T>());
ruleBuilder.SetValidator(new QualityNameValidator<T>());
ruleBuilder.SetValidator(new EmptyItemGroupNameValidator<T>());
ruleBuilder.SetValidator(new ItemGroupIdValidator<T>());
ruleBuilder.SetValidator(new UniqueIdValidator<T>());
ruleBuilder.SetValidator(new UniqueQualityIdValidator<T>());
return ruleBuilder.SetValidator(new ItemGroupNameValidator<T>());
}
}
public class AllowedValidator<T> : PropertyValidator
{
public AllowedValidator()
: base("Must contain at least one allowed quality")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var list = context.PropertyValue as IList<QualityProfileQualityItemResource>;
if (list == null)
{
return false;
}
if (!list.Any(c => c.Allowed))
{
return false;
}
return true;
}
}
public class EmptyItemGroupNameValidator<T> : PropertyValidator
{
public EmptyItemGroupNameValidator()
: base("Groups must not be empty")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
if (items.Any(i => i.Name.IsNotNullOrWhiteSpace() && i.Items.Empty()))
{
return false;
}
return true;
}
}
public class QualityNameValidator<T> : PropertyValidator
{
public QualityNameValidator()
: base("Individual qualities should not be named")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
if (items.Any(i => i.Name.IsNotNullOrWhiteSpace() && i.Quality != null))
{
return false;
}
return true;
}
}
public class ItemGroupNameValidator<T> : PropertyValidator
{
public ItemGroupNameValidator()
: base("Groups must have a name")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
if (items.Any(i => i.Quality == null && i.Name.IsNullOrWhiteSpace()))
{
return false;
}
return true;
}
}
public class ItemGroupIdValidator<T> : PropertyValidator
{
public ItemGroupIdValidator()
: base("Groups must have an ID")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
if (items.Any(i => i.Quality == null && i.Id == 0))
{
return false;
}
return true;
}
}
public class UniqueIdValidator<T> : PropertyValidator
{
public UniqueIdValidator()
: base("Groups must have a unique ID")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
if (items.Where(i => i.Id > 0).Select(i => i.Id).GroupBy(i => i).Any(g => g.Count() > 1))
{
return false;
}
return true;
}
}
public class UniqueQualityIdValidator<T> : PropertyValidator
{
public UniqueQualityIdValidator()
: base("Qualities can only be used once")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var items = context.PropertyValue as IList<QualityProfileQualityItemResource>;
var qualityIds = new HashSet<int>();
foreach (var item in items)
{
if (item.Id > 0)
{
foreach (var quality in item.Items)
{
if (qualityIds.Contains(quality.Quality.Id))
{
return false;
}
qualityIds.Add(quality.Quality.Id);
}
}
else
{
if (qualityIds.Contains(item.Quality.Id))
{
return false;
}
qualityIds.Add(item.Quality.Id);
}
}
return true;
}
}
}

View File

@ -13,8 +13,10 @@ namespace Sonarr.Api.V3.Profiles.Quality
{ {
_profileService = profileService; _profileService = profileService;
SharedValidator.RuleFor(c => c.Name).NotEmpty(); SharedValidator.RuleFor(c => c.Name).NotEmpty();
SharedValidator.RuleFor(c => c.Cutoff).NotNull(); // TODO: Need to validate the cutoff is allowed and the ID/quality ID exists
SharedValidator.RuleFor(c => c.Items).MustHaveAllowedQuality(); // TODO: Need to validate the Items to ensure groups have names and at no item has no name, no items and no quality
SharedValidator.RuleFor(c => c.Cutoff).ValidCutoff();
SharedValidator.RuleFor(c => c.Items).ValidItems();
GetResourceAll = GetAll; GetResourceAll = GetAll;
GetResourceById = GetById; GetResourceById = GetById;

View File

@ -8,14 +8,21 @@ namespace Sonarr.Api.V3.Profiles.Quality
public class QualityProfileResource : RestResource public class QualityProfileResource : RestResource
{ {
public string Name { get; set; } public string Name { get; set; }
public NzbDrone.Core.Qualities.Quality Cutoff { get; set; } public int Cutoff { get; set; }
public List<QualityProfileQualityItemResource> Items { get; set; } public List<QualityProfileQualityItemResource> Items { get; set; }
} }
public class QualityProfileQualityItemResource : RestResource public class QualityProfileQualityItemResource : RestResource
{ {
public string Name { get; set; }
public NzbDrone.Core.Qualities.Quality Quality { get; set; } public NzbDrone.Core.Qualities.Quality Quality { get; set; }
public List<QualityProfileQualityItemResource> Items { get; set; }
public bool Allowed { get; set; } public bool Allowed { get; set; }
public QualityProfileQualityItemResource()
{
Items = new List<QualityProfileQualityItemResource>();
}
} }
public static class ProfileResourceMapper public static class ProfileResourceMapper
@ -27,7 +34,6 @@ namespace Sonarr.Api.V3.Profiles.Quality
return new QualityProfileResource return new QualityProfileResource
{ {
Id = model.Id, Id = model.Id,
Name = model.Name, Name = model.Name,
Cutoff = model.Cutoff, Cutoff = model.Cutoff,
Items = model.Items.ConvertAll(ToResource), Items = model.Items.ConvertAll(ToResource),
@ -40,7 +46,10 @@ namespace Sonarr.Api.V3.Profiles.Quality
return new QualityProfileQualityItemResource return new QualityProfileQualityItemResource
{ {
Id = model.Id,
Name = model.Name,
Quality = model.Quality, Quality = model.Quality,
Items = model.Items.ConvertAll(ToResource),
Allowed = model.Allowed Allowed = model.Allowed
}; };
} }
@ -52,9 +61,8 @@ namespace Sonarr.Api.V3.Profiles.Quality
return new Profile return new Profile
{ {
Id = resource.Id, Id = resource.Id,
Name = resource.Name, Name = resource.Name,
Cutoff = (NzbDrone.Core.Qualities.Quality)resource.Cutoff.Id, Cutoff = resource.Cutoff,
Items = resource.Items.ConvertAll(ToModel) Items = resource.Items.ConvertAll(ToModel)
}; };
} }
@ -65,7 +73,10 @@ namespace Sonarr.Api.V3.Profiles.Quality
return new ProfileQualityItem return new ProfileQualityItem
{ {
Quality = (NzbDrone.Core.Qualities.Quality)resource.Quality.Id, Id = resource.Id,
Name = resource.Name,
Quality = resource.Quality != null ? (NzbDrone.Core.Qualities.Quality)resource.Quality.Id : null,
Items = resource.Items.ConvertAll(ToModel),
Allowed = resource.Allowed Allowed = resource.Allowed
}; };
} }

View File

@ -1,32 +1,22 @@
using System.Linq;
using NzbDrone.Core.Profiles.Qualities; using NzbDrone.Core.Profiles.Qualities;
using NzbDrone.Core.Qualities;
using Sonarr.Http; using Sonarr.Http;
namespace Sonarr.Api.V3.Profiles.Quality namespace Sonarr.Api.V3.Profiles.Quality
{ {
public class QualityProfileSchemaModule : SonarrRestModule<QualityProfileResource> public class QualityProfileSchemaModule : SonarrRestModule<QualityProfileResource>
{ {
private readonly IQualityDefinitionService _qualityDefinitionService; private readonly IProfileService _profileService;
public QualityProfileSchemaModule(IQualityDefinitionService qualityDefinitionService) public QualityProfileSchemaModule(IProfileService profileService)
: base("/qualityprofile/schema") : base("/qualityprofile/schema")
{ {
_qualityDefinitionService = qualityDefinitionService; _profileService = profileService;
GetResourceSingle = GetSchema; GetResourceSingle = GetSchema;
} }
private QualityProfileResource GetSchema() private QualityProfileResource GetSchema()
{ {
var items = _qualityDefinitionService.All() var qualityProfile = _profileService.GetDefaultProfile(string.Empty);
.OrderBy(v => v.Weight)
.Select(v => new ProfileQualityItem { Quality = v.Quality, Allowed = false })
.ToList();
var qualityProfile = new Profile();
qualityProfile.Cutoff = NzbDrone.Core.Qualities.Quality.Unknown;
qualityProfile.Items = items;
return qualityProfile.ToResource(); return qualityProfile.ToResource();
} }

View File

@ -1,43 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using FluentValidation;
using FluentValidation.Validators;
namespace Sonarr.Api.V3.Profiles.Quality
{
public static class QualityProfileValidation
{
public static IRuleBuilderOptions<T, IList<QualityProfileQualityItemResource>> MustHaveAllowedQuality<T>(this IRuleBuilder<T, IList<QualityProfileQualityItemResource>> ruleBuilder)
{
ruleBuilder.SetValidator(new NotEmptyValidator(null));
return ruleBuilder.SetValidator(new AllowedValidator<T>());
}
}
public class AllowedValidator<T> : PropertyValidator
{
public AllowedValidator()
: base("Must contain at least one allowed quality")
{
}
protected override bool IsValid(PropertyValidatorContext context)
{
var list = context.PropertyValue as IList<QualityProfileQualityItemResource>;
if (list == null)
{
return false;
}
if (!list.Any(c => c.Allowed))
{
return false;
}
return true;
}
}
}

View File

@ -170,7 +170,6 @@
<Compile Include="Profiles\Quality\QualityProfileModule.cs" /> <Compile Include="Profiles\Quality\QualityProfileModule.cs" />
<Compile Include="Profiles\Quality\QualityProfileResource.cs" /> <Compile Include="Profiles\Quality\QualityProfileResource.cs" />
<Compile Include="Profiles\Quality\QualityProfileSchemaModule.cs" /> <Compile Include="Profiles\Quality\QualityProfileSchemaModule.cs" />
<Compile Include="Profiles\Quality\QualityProfileValidation.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ProviderModuleBase.cs" /> <Compile Include="ProviderModuleBase.cs" />
<Compile Include="ProviderResource.cs" /> <Compile Include="ProviderResource.cs" />