diff --git a/src/NzbDrone.Api/Indexers/ReleaseModule.cs b/src/NzbDrone.Api/Indexers/ReleaseModule.cs index b88e4e87a..b12604cb8 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseModule.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseModule.cs @@ -131,13 +131,16 @@ namespace NzbDrone.Api.Indexers if (downloadDecision.RemoteEpisode.Series != null) { - release.QualityWeight = downloadDecision.RemoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == release.Quality.Quality) * 2; + release.QualityWeight = downloadDecision.RemoteEpisode + .Series + .Profile + .Value + .Items + .FindIndex(v => v.Quality == release.Quality.Quality) * 100; } - if (!release.Quality.Proper) - { - release.QualityWeight++; - } + release.QualityWeight += release.Quality.Revision.Real * 10; + release.QualityWeight += release.Quality.Revision.Version; result.Add(release); } diff --git a/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs b/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs index a457d4d52..bddf0ba79 100644 --- a/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs +++ b/src/NzbDrone.Core.Test/Datastore/DatabaseRelationshipFixture.cs @@ -1,4 +1,5 @@ -using System.Linq; +using System; +using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; @@ -12,65 +13,6 @@ namespace NzbDrone.Core.Test.Datastore [TestFixture] public class DatabaseRelationshipFixture : DbTest { - /* [Test] - [Explicit] - public void benchmark() - { - var series = Builder.CreateNew() - .With(c => c.Id = 0) - .Build(); - - Marr.Data.MapRepository.Instance.EnableTraceLogging = false; - - Db.Insert(series); - - var covers = Builder.CreateListOfSize(5) - .All() - .With(c => c.SeriesId = series.Id) - .With(c => c.Id = 0) - .Build() - .ToList(); - - Db.InsertMany(covers); - - var loadedSeries = Db.Single(); - - var sw = Stopwatch.StartNew(); - for (int i = 0; i < 10000; i++) - { - loadedSeries = Db.Single(); - var list = loadedSeries.Covers.Value; - } - - sw.Stop(); - - Console.WriteLine(sw.Elapsed); - - loadedSeries.Covers.Value.Should().HaveSameCount(covers); - } - - [Test] - public void one_to_many() - { - var series = Builder.CreateNew() - .With(c => c.Id = 0) - .Build(); - - Db.Insert(series); - - var covers = Builder.CreateListOfSize(5) - .All() - .With(c => c.SeriesId = series.Id) - .With(c => c.Id = 0) - .Build() - .ToList(); - - Db.InsertMany(covers); - - var loadedSeries = Db.Single(); - loadedSeries.Covers.Value.Should().HaveSameCount(covers); - }*/ - [Test] public void one_to_one() { @@ -114,7 +56,7 @@ namespace NzbDrone.Core.Test.Datastore [Test] public void embedded_document_as_json() { - var quality = new QualityModel { Quality = Quality.Bluray720p, Proper = true }; + var quality = new QualityModel { Quality = Quality.Bluray720p, Revision = new Revision(version: 2 )}; var history = Builder.CreateNew() .With(c => c.Id = 0) @@ -130,14 +72,12 @@ namespace NzbDrone.Core.Test.Datastore [Test] public void embedded_list_of_document_with_json() { - var quality = new QualityModel { Quality = Quality.Bluray720p, Proper = true }; - var history = Builder.CreateListOfSize(2) .All().With(c => c.Id = 0) .Build().ToList(); - history[0].Quality = new QualityModel(Quality.HDTV1080p, true); - history[1].Quality = new QualityModel(Quality.Bluray720p, true); + history[0].Quality = new QualityModel(Quality.HDTV1080p, new Revision(version: 2)); + history[1].Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 2)); Db.InsertMany(history); diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/AcceptableSizeSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/AcceptableSizeSpecificationFixture.cs index 5515fcedf..07cba6829 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/AcceptableSizeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/AcceptableSizeSpecificationFixture.cs @@ -31,7 +31,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Series = series, Release = new ReleaseInfo(), - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) }, Episodes = new List { new Episode(), new Episode(), new Episode(), new Episode(), new Episode(), new Episode() } }; @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Series = series, Release = new ReleaseInfo(), - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) }, Episodes = new List { new Episode(), new Episode() } }; @@ -47,7 +47,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { Series = series, Release = new ReleaseInfo(), - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.SDTV, new Revision(version: 2)) }, Episodes = new List { new Episode() { Id = 2 } } }; @@ -182,7 +182,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { var parseResult = new RemoteEpisode { - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.RAWHD, false) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.RAWHD) }, }; Subject.IsSatisfiedBy(parseResult, null).Should().BeTrue(); @@ -193,7 +193,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { var parseResult = new RemoteEpisode { - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.Unknown, false) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.Unknown) }, }; Subject.IsSatisfiedBy(parseResult, null).Should().BeFalse(); diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs index d7ae1643c..e038ba82c 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs @@ -2,7 +2,6 @@ using NUnit.Framework; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Test.Framework; @@ -15,35 +14,37 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_return_true_if_current_episode_is_less_than_cutoff() { Subject.CutoffNotMet(new Profile { Cutoff = Quality.Bluray1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }, - new QualityModel(Quality.DVD, true)).Should().BeTrue(); + new QualityModel(Quality.DVD, new Revision(version: 2))).Should().BeTrue(); } [Test] public void should_return_false_if_current_episode_is_equal_to_cutoff() { Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() }, - new QualityModel(Quality.HDTV720p, true)).Should().BeFalse(); + new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeFalse(); } [Test] public void should_return_false_if_current_episode_is_greater_than_cutoff() { Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() }, - new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); + new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse(); } [Test] public void should_return_true_when_new_episode_is_proper_but_existing_is_not() { Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() }, - new QualityModel(Quality.HDTV720p, false), new QualityModel(Quality.HDTV720p, true)).Should().BeTrue(); + new QualityModel(Quality.HDTV720p, new Revision(version: 1)), + new QualityModel(Quality.HDTV720p, new Revision(version: 2))).Should().BeTrue(); } [Test] public void should_return_false_if_cutoff_is_met_and_quality_is_higher() { Subject.CutoffNotMet(new Profile { Cutoff = Quality.HDTV720p, Items = Qualities.QualityFixture.GetDefaultQualities() }, - new QualityModel(Quality.HDTV720p, true), new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); + new QualityModel(Quality.HDTV720p, new Revision(version: 2)), + new QualityModel(Quality.Bluray1080p, new Revision(version: 2))).Should().BeFalse(); } } -} \ No newline at end of file +} diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs index f26cdf7ea..06f96b88a 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/HistorySpecificationFixture.cs @@ -49,19 +49,19 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _parseResultMulti = new RemoteEpisode { Series = _fakeSeries, - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, Episodes = doubleEpisodeList }; _parseResultSingle = new RemoteEpisode { Series = _fakeSeries, - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, Episodes = singleEpisodeList }; - _upgradableQuality = new QualityModel(Quality.SDTV, false); - _notupgradableQuality = new QualityModel(Quality.HDTV1080p, true); + _upgradableQuality = new QualityModel(Quality.SDTV, new Revision(version: 1)); + _notupgradableQuality = new QualityModel(Quality.HDTV1080p, new Revision(version: 2)); Mocker.GetMock().Setup(c => c.GetBestQualityInHistory(It.IsAny(), 1)).Returns(_notupgradableQuality); Mocker.GetMock().Setup(c => c.GetBestQualityInHistory(It.IsAny(), 2)).Returns(_notupgradableQuality); @@ -134,8 +134,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_not_be_upgradable_if_episode_is_of_same_quality_as_existing() { _fakeSeries.Profile = new Profile { Cutoff = Quality.WEBDL1080p, Items = Qualities.QualityFixture.GetDefaultQualities() }; - _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false); - _upgradableQuality = new QualityModel(Quality.WEBDL1080p, false); + _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); + _upgradableQuality = new QualityModel(Quality.WEBDL1080p, new Revision(version: 1)); Mocker.GetMock().Setup(c => c.GetBestQualityInHistory(It.IsAny(), 1)).Returns(_upgradableQuality); diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs index 1f8119b68..6972f9950 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs @@ -49,15 +49,15 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [Test] public void should_put_propers_before_non_propers() { - var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, false)); - var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, true)); + var remoteEpisode1 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, new Revision(version: 1))); + var remoteEpisode2 = GivenRemoteEpisode(new List { GivenEpisode(1) }, new QualityModel(Quality.HDTV720p, new Revision(version: 2))); var decisions = new List(); decisions.Add(new DownloadDecision(remoteEpisode1)); decisions.Add(new DownloadDecision(remoteEpisode2)); var qualifiedReports = Subject.PrioritizeDecisions(decisions); - qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Proper.Should().BeTrue(); + qualifiedReports.First().RemoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version.Should().Be(2); } [Test] diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs index 02f017d7a..d0b2b78fc 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityAllowedByProfileSpecificationFixture.cs @@ -42,7 +42,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests remoteEpisode = new RemoteEpisode { Series = fakeSeries, - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, }; } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs index b8596840f..994a47674 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QualityUpgradeSpecificationFixture.cs @@ -1,10 +1,9 @@ -using System.Linq; +using System; using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Configuration; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Test.Framework; @@ -16,14 +15,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests { public static object[] IsUpgradeTestCases = { - new object[] { Quality.SDTV, false, Quality.SDTV, true, Quality.SDTV, true }, - new object[] { Quality.WEBDL720p, false, Quality.WEBDL720p, true, Quality.WEBDL720p, true }, - new object[] { Quality.SDTV, false, Quality.SDTV, false, Quality.SDTV, false }, - new object[] { Quality.WEBDL720p, false, Quality.HDTV720p, true, Quality.Bluray720p, false }, - new object[] { Quality.WEBDL720p, false, Quality.HDTV720p, true, Quality.WEBDL720p, false }, - new object[] { Quality.WEBDL720p, false, Quality.WEBDL720p, false, Quality.WEBDL720p, false }, - new object[] { Quality.SDTV, false, Quality.SDTV, true, Quality.SDTV, true }, - new object[] { Quality.WEBDL1080p, false, Quality.WEBDL1080p, false, Quality.WEBDL1080p, false } + new object[] { Quality.SDTV, 1, Quality.SDTV, 2, Quality.SDTV, true }, + new object[] { Quality.WEBDL720p, 1, Quality.WEBDL720p, 2, Quality.WEBDL720p, true }, + new object[] { Quality.SDTV, 1, Quality.SDTV, 1, Quality.SDTV, false }, + new object[] { Quality.WEBDL720p, 1, Quality.HDTV720p, 2, Quality.Bluray720p, false }, + new object[] { Quality.WEBDL720p, 1, Quality.HDTV720p, 2, Quality.WEBDL720p, false }, + new object[] { Quality.WEBDL720p, 1, Quality.WEBDL720p, 1, Quality.WEBDL720p, false }, + new object[] { Quality.SDTV, 1, Quality.SDTV, 2, Quality.SDTV, true }, + new object[] { Quality.WEBDL1080p, 1, Quality.WEBDL1080p, 1, Quality.WEBDL1080p, false } }; [SetUp] @@ -40,13 +39,13 @@ namespace NzbDrone.Core.Test.DecisionEngineTests } [Test, TestCaseSource("IsUpgradeTestCases")] - public void IsUpgradeTest(Quality current, bool currentProper, Quality newQuality, bool newProper, Quality cutoff, bool expected) + public void IsUpgradeTest(Quality current, Int32 currentVersion, Quality newQuality, Int32 newVersion, Quality cutoff, Boolean expected) { GivenAutoDownloadPropers(true); var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }; - Subject.IsUpgradable(profile, new QualityModel(current, currentProper), new QualityModel(newQuality, newProper)) + Subject.IsUpgradable(profile, new QualityModel(current, new Revision(version: currentVersion)), new QualityModel(newQuality, new Revision(version: newVersion))) .Should().Be(expected); } @@ -57,7 +56,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests var profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }; - Subject.IsUpgradable(profile, new QualityModel(Quality.DVD, true), new QualityModel(Quality.DVD, false)) + Subject.IsUpgradable(profile, new QualityModel(Quality.DVD, new Revision(version: 2)), new QualityModel(Quality.DVD, new Revision(version: 1))) .Should().BeFalse(); } } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs index 29ce17057..fb784d696 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/DelaySpecificationFixture.cs @@ -6,9 +6,9 @@ using FluentAssertions; using Marr.Data; using Moq; using NUnit.Framework; +using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine.Specifications.RssSync; using NzbDrone.Core.Download.Pending; -using NzbDrone.Core.History; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.Parser.Model; @@ -46,23 +46,32 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync _profile.Items.Add(new ProfileQualityItem { Allowed = true, Quality = Quality.Bluray720p }); _profile.Cutoff = Quality.WEBDL720p; - _profile.GrabDelayMode = GrabDelayMode.Always; _remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo(); _remoteEpisode.Release = new ReleaseInfo(); _remoteEpisode.Episodes = Builder.CreateListOfSize(1).Build().ToList(); + _remoteEpisode.Episodes.First().EpisodeFileId = 0; } private void GivenExistingFile(QualityModel quality) { - _remoteEpisode.Episodes[0].EpisodeFile = new LazyLoaded(new EpisodeFile + _remoteEpisode.Episodes.First().EpisodeFileId = 1; + + _remoteEpisode.Episodes.First().EpisodeFile = new LazyLoaded(new EpisodeFile { Quality = quality }); } + private void GivenUpgradeForExistingFile() + { + Mocker.GetMock() + .Setup(s => s.IsUpgradable(It.IsAny(), It.IsAny(), It.IsAny())) + .Returns(true); + } + [Test] public void should_be_true_when_search() { @@ -108,12 +117,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync } [Test] - public void should_be_true_when_release_is_proper_for_existing_episode() + public void should_be_true_when_release_is_a_proper_for_existing_episode() { - _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, true); + _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2)); _remoteEpisode.Release.PublishDate = DateTime.UtcNow; GivenExistingFile(new QualityModel(Quality.HDTV720p)); + GivenUpgradeForExistingFile(); + + Mocker.GetMock() + .Setup(s => s.IsRevisionUpgrade(It.IsAny(), It.IsAny())) + .Returns(true); _profile.GrabDelay = 12; @@ -121,9 +135,21 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync } [Test] - public void should_be_false_when_release_is_proper_and_no_existing_episode() + public void should_be_true_when_release_is_a_real_for_existing_episode() { - + _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(real: 1)); + _remoteEpisode.Release.PublishDate = DateTime.UtcNow; + + GivenExistingFile(new QualityModel(Quality.HDTV720p)); + GivenUpgradeForExistingFile(); + + Mocker.GetMock() + .Setup(s => s.IsRevisionUpgrade(It.IsAny(), It.IsAny())) + .Returns(true); + + _profile.GrabDelay = 12; + + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue(); } [Test] @@ -165,7 +191,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync [Test] public void should_be_false_when_release_is_proper_for_existing_episode_of_different_quality() { - _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, true); + _remoteEpisode.ParsedEpisodeInfo.Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 2)); _remoteEpisode.Release.PublishDate = DateTime.UtcNow; GivenExistingFile(new QualityModel(Quality.SDTV)); @@ -200,8 +226,6 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync _profile.GrabDelay = 12; - var pendingRemoteEpisode = _remoteEpisode.JsonClone(); - Mocker.GetMock() .Setup(s => s.GetPendingRemoteEpisodes(It.IsAny())) .Returns(new List { _remoteEpisode.JsonClone() }); diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs index f377b80d5..6a555d96d 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs @@ -31,8 +31,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync { Mocker.Resolve(); - _firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, false), DateAdded = DateTime.Now }; - _secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, false), DateAdded = DateTime.Now }; + _firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)), DateAdded = DateTime.Now }; + _secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)), DateAdded = DateTime.Now }; var singleEpisodeList = new List { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; var doubleEpisodeList = new List { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; @@ -44,14 +44,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync _parseResultMulti = new RemoteEpisode { Series = fakeSeries, - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, Episodes = doubleEpisodeList }; _parseResultSingle = new RemoteEpisode { Series = fakeSeries, - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, Episodes = singleEpisodeList }; } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs index e21e10d12..8591b9cc4 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs @@ -32,8 +32,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Mocker.Resolve(); _upgradeDisk = Mocker.Resolve(); - _firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, true), DateAdded = DateTime.Now }; - _secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, true), DateAdded = DateTime.Now }; + _firstFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now }; + _secondFile = new EpisodeFile { Quality = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)), DateAdded = DateTime.Now }; var singleEpisodeList = new List { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; var doubleEpisodeList = new List { new Episode { EpisodeFile = _firstFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = _secondFile, EpisodeFileId = 1 }, new Episode { EpisodeFile = null } }; @@ -45,14 +45,14 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _parseResultMulti = new RemoteEpisode { Series = fakeSeries, - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, Episodes = doubleEpisodeList }; _parseResultSingle = new RemoteEpisode { Series = fakeSeries, - ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, new Revision(version: 2)) }, Episodes = singleEpisodeList }; } @@ -121,7 +121,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public void should_not_be_upgradable_if_qualities_are_the_same() { _firstFile.Quality = new QualityModel(Quality.WEBDL1080p); - _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false); + _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p); _upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); } } diff --git a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs index a217610af..bfbab5472 100644 --- a/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/MediaFiles/EpisodeImport/Specifications/UpgradeSpecificationFixture.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications _localEpisode = new LocalEpisode { Path = @"C:\Test\30 Rock\30.rock.s01e01.avi", - Quality = new QualityModel(Quality.HDTV720p, false), + Quality = new QualityModel(Quality.HDTV720p, new Revision(version: 1)), Series = _series }; } @@ -70,7 +70,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications .With(e => e.EpisodeFile = new LazyLoaded( new EpisodeFile { - Quality = new QualityModel(Quality.SDTV, false) + Quality = new QualityModel(Quality.SDTV, new Revision(version: 1)) })) .Build() .ToList(); @@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications .With(e => e.EpisodeFile = new LazyLoaded( new EpisodeFile { - Quality = new QualityModel(Quality.SDTV, false) + Quality = new QualityModel(Quality.SDTV, new Revision(version: 1)) })) .Build() .ToList(); @@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications .With(e => e.EpisodeFile = new LazyLoaded( new EpisodeFile { - Quality = new QualityModel(Quality.Bluray720p, false) + Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1)) })) .Build() .ToList(); @@ -121,7 +121,7 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications .With(e => e.EpisodeFile = new LazyLoaded( new EpisodeFile { - Quality = new QualityModel(Quality.Bluray720p, false) + Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1)) })) .Build() .ToList(); @@ -138,14 +138,14 @@ namespace NzbDrone.Core.Test.MediaFiles.EpisodeImport.Specifications .With(e => e.EpisodeFile = new LazyLoaded( new EpisodeFile { - Quality = new QualityModel(Quality.SDTV, false) + Quality = new QualityModel(Quality.SDTV, new Revision(version: 1)) })) .TheNext(1) .With(e => e.EpisodeFileId = 2) .With(e => e.EpisodeFile = new LazyLoaded( new EpisodeFile { - Quality = new QualityModel(Quality.Bluray720p, false) + Quality = new QualityModel(Quality.Bluray720p, new Revision(version: 1)) })) .Build() .ToList(); diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 8d820a3eb..ed90a2e7a 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -197,6 +197,7 @@ + @@ -229,6 +230,7 @@ + diff --git a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs index 708fc57c7..5aa943204 100644 --- a/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs +++ b/src/NzbDrone.Core.Test/OrganizerTests/FileNameBuilderFixture.cs @@ -61,7 +61,7 @@ namespace NzbDrone.Core.Test.OrganizerTests private void GivenProper() { - _episodeFile.Quality.Proper = true; + _episodeFile.Quality.Revision.Version =2; } [Test] @@ -208,7 +208,7 @@ namespace NzbDrone.Core.Test.OrganizerTests public void should_replace_quality_title_with_proper() { _namingConfig.StandardEpisodeFormat = "{Quality Title}"; - _episodeFile.Quality.Proper = true; + GivenProper(); Subject.BuildFileName(new List { _episode1 }, _series, _episodeFile) .Should().Be("HDTV-720p Proper"); diff --git a/src/NzbDrone.Core.Test/ParserTests/ExtendedQualityParserRegex.cs b/src/NzbDrone.Core.Test/ParserTests/ExtendedQualityParserRegex.cs new file mode 100644 index 000000000..4bba3a93f --- /dev/null +++ b/src/NzbDrone.Core.Test/ParserTests/ExtendedQualityParserRegex.cs @@ -0,0 +1,49 @@ +using System; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.ParserTests +{ + [TestFixture] + public class ExtendedQualityParserRegex : CoreTest + { + [TestCase("Chuck.S04E05.HDTV.XviD-LOL", 0)] + [TestCase("Gold.Rush.S04E05.Garnets.or.Gold.REAL.REAL.PROPER.HDTV.x264-W4F", 2)] + [TestCase("Chuck.S03E17.REAL.PROPER.720p.HDTV.x264-ORENJI-RP", 1)] + [TestCase("Covert.Affairs.S05E09.REAL.PROPER.HDTV.x264-KILLERS", 1)] + [TestCase("Mythbusters.S14E01.REAL.PROPER.720p.HDTV.x264-KILLERS", 1)] + [TestCase("Orange.Is.the.New.Black.s02e06.real.proper.720p.webrip.x264-2hd", 1)] + [TestCase("Top.Gear.S21E07.Super.Duper.Real.Proper.HDTV.x264-FTP", 1)] + [TestCase("Top.Gear.S21E07.PROPER.HDTV.x264-RiVER-RP", 0)] + [TestCase("House.S07E11.PROPER.REAL.RERIP.1080p.BluRay.x264-TENEIGHTY", 1)] + [TestCase("[MGS] - Kuragehime - Episode 02v2 - [D8B6C90D]", 0)] + [TestCase("[Hatsuyuki] Tokyo Ghoul - 07 [v2][848x480][23D8F455].avi", 0)] + [TestCase("[DeadFish] Barakamon - 01v3 [720p][AAC]", 0)] + [TestCase("[DeadFish] Momo Kyun Sword - 01v4 [720p][AAC]", 0)] + public void should_parse_reality_from_title(String title, Int32 reality) + { + //TODO: re-enable this when we have a reliable way to determine real + //QualityParser.ParseQuality(title).Revision.Real.Should().Be(reality); + } + + [TestCase("Chuck.S04E05.HDTV.XviD-LOL", 1)] + [TestCase("Gold.Rush.S04E05.Garnets.or.Gold.REAL.REAL.PROPER.HDTV.x264-W4F", 2)] + [TestCase("Chuck.S03E17.REAL.PROPER.720p.HDTV.x264-ORENJI-RP", 2)] + [TestCase("Covert.Affairs.S05E09.REAL.PROPER.HDTV.x264-KILLERS", 2)] + [TestCase("Mythbusters.S14E01.REAL.PROPER.720p.HDTV.x264-KILLERS", 2)] + [TestCase("Orange.Is.the.New.Black.s02e06.real.proper.720p.webrip.x264-2hd", 2)] + [TestCase("Top.Gear.S21E07.Super.Duper.Real.Proper.HDTV.x264-FTP", 2)] + [TestCase("Top.Gear.S21E07.PROPER.HDTV.x264-RiVER-RP", 2)] + [TestCase("House.S07E11.PROPER.REAL.RERIP.1080p.BluRay.x264-TENEIGHTY", 2)] + [TestCase("[MGS] - Kuragehime - Episode 02v2 - [D8B6C90D]", 2)] + [TestCase("[Hatsuyuki] Tokyo Ghoul - 07 [v2][848x480][23D8F455].avi", 2)] + [TestCase("[DeadFish] Barakamon - 01v3 [720p][AAC]", 3)] + [TestCase("[DeadFish] Momo Kyun Sword - 01v4 [720p][AAC]", 4)] + public void should_parse_version_from_title(String title, Int32 version) + { + QualityParser.ParseQuality(title).Revision.Version.Should().Be(version); + } + } +} diff --git a/src/NzbDrone.Core.Test/ParserTests/HashedReleaseFixture.cs b/src/NzbDrone.Core.Test/ParserTests/HashedReleaseFixture.cs index 5b7f1e447..720ad8ad1 100644 --- a/src/NzbDrone.Core.Test/ParserTests/HashedReleaseFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/HashedReleaseFixture.cs @@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.ParserTests { var result = Parser.Parser.ParsePath(path); result.SeriesTitle.Should().Be(title); - result.Quality.ToString().Should().Be(quality); + result.Quality.ToString().Should().Be(quality + " v1"); result.ReleaseGroup.Should().Be(releaseGroup); } } diff --git a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs index f2946c59e..87e627047 100644 --- a/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/QualityParserFixture.cs @@ -1,6 +1,7 @@ using System; using FluentAssertions; using NUnit.Framework; +using NzbDrone.Core.Parser; using NzbDrone.Core.Qualities; using NzbDrone.Core.Test.Framework; @@ -206,7 +207,7 @@ namespace NzbDrone.Core.Test.ParserTests public void parsing_our_own_quality_enum_name(Quality quality) { var fileName = String.Format("My series S01E01 [{0}]", quality.Name); - var result = Parser.QualityParser.ParseQuality(fileName); + var result = QualityParser.ParseQuality(fileName); result.Quality.Should().Be(quality); } @@ -221,12 +222,13 @@ namespace NzbDrone.Core.Test.ParserTests } } - private void ParseAndVerifyQuality(string title, Quality quality, bool proper) { - var result = Parser.QualityParser.ParseQuality(title); + var result = QualityParser.ParseQuality(title); result.Quality.Should().Be(quality); - result.Proper.Should().Be(proper); + + var version = proper ? 2 : 1; + result.Revision.Version.Should().Be(version); } } } diff --git a/src/NzbDrone.Core.Test/Qualities/QualityModelComparerFixture.cs b/src/NzbDrone.Core.Test/Qualities/QualityModelComparerFixture.cs index 78262f0a5..47ecbde16 100644 --- a/src/NzbDrone.Core.Test/Qualities/QualityModelComparerFixture.cs +++ b/src/NzbDrone.Core.Test/Qualities/QualityModelComparerFixture.cs @@ -1,10 +1,7 @@ -using System.Linq; -using System.Collections.Generic; -using FluentAssertions; +using FluentAssertions; using NUnit.Framework; using NzbDrone.Core.Profiles; using NzbDrone.Core.Qualities; -using NzbDrone.Core.Tv; using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.Qualities @@ -25,38 +22,25 @@ namespace NzbDrone.Core.Test.Qualities } [Test] - public void Icomparer_greater_test() + public void should_be_greater_when_first_quality_is_greater_than_second() { GivenDefaultProfile(); - var first = new QualityModel(Quality.DVD, true); - var second = new QualityModel(Quality.Bluray1080p, true); + var first = new QualityModel(Quality.Bluray1080p); + var second = new QualityModel(Quality.DVD); - var compare = Subject.Compare(second, first); - - compare.Should().BeGreaterThan(0); - } - - [Test] - public void Icomparer_greater_proper() - { - GivenDefaultProfile(); - - var first = new QualityModel(Quality.Bluray1080p, false); - var second = new QualityModel(Quality.Bluray1080p, true); - - var compare = Subject.Compare(second, first); + var compare = Subject.Compare(first, second); compare.Should().BeGreaterThan(0); } [Test] - public void Icomparer_lesser() + public void should_be_lesser_when_second_quality_is_greater_than_first() { GivenDefaultProfile(); - var first = new QualityModel(Quality.DVD, true); - var second = new QualityModel(Quality.Bluray1080p, true); + var first = new QualityModel(Quality.DVD); + var second = new QualityModel(Quality.Bluray1080p); var compare = Subject.Compare(first, second); @@ -64,25 +48,25 @@ namespace NzbDrone.Core.Test.Qualities } [Test] - public void Icomparer_lesser_proper() + public void should_be_greater_when_first_quality_is_a_proper_for_the_same_quality() { GivenDefaultProfile(); - var first = new QualityModel(Quality.DVD, false); - var second = new QualityModel(Quality.DVD, true); + var first = new QualityModel(Quality.Bluray1080p, new Revision(version: 2)); + var second = new QualityModel(Quality.Bluray1080p, new Revision(version: 1)); var compare = Subject.Compare(first, second); - compare.Should().BeLessThan(0); + compare.Should().BeGreaterThan(0); } [Test] - public void Icomparer_greater_custom_order() + public void should_be_greater_when_using_a_custom_profile() { GivenCustomProfile(); - var first = new QualityModel(Quality.DVD, true); - var second = new QualityModel(Quality.Bluray720p, true); + var first = new QualityModel(Quality.DVD); + var second = new QualityModel(Quality.Bluray720p); var compare = Subject.Compare(first, second); diff --git a/src/NzbDrone.Core.Test/Qualities/RevisionComparableFixture.cs b/src/NzbDrone.Core.Test/Qualities/RevisionComparableFixture.cs new file mode 100644 index 000000000..54bbcd033 --- /dev/null +++ b/src/NzbDrone.Core.Test/Qualities/RevisionComparableFixture.cs @@ -0,0 +1,149 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Profiles; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Qualities +{ + [TestFixture] + public class RevisionComparableFixture : CoreTest + { + [Test] + public void should_be_greater_when_first_quality_is_a_real() + { + var first = new Revision(real: 1); + var second = new Revision(); + + first.Should().BeGreaterThan(second); + } + + [Test] + public void should_be_greater_when_first_quality_is_a_proper() + { + var first = new Revision(version: 2); + var second = new Revision(); + + first.Should().BeGreaterThan(second); + } + + [Test] + public void should_be_greater_when_first_is_a_proper_for_a_real() + { + var first = new Revision(real: 1, version: 2); + var second = new Revision(real: 1); + + first.Should().BeGreaterThan(second); + } + + [Test] + public void should_be_lesser_when_second_quality_is_a_real() + { + var first = new Revision(); + var second = new Revision(real: 1); + + first.Should().BeLessThan(second); + } + + [Test] + public void should_be_lesser_when_second_quality_is_a_proper() + { + var first = new Revision(); + var second = new Revision(version: 2); + + first.Should().BeLessThan(second); + } + + [Test] + public void should_be_lesser_when_second_is_a_proper_for_a_real() + { + var first = new Revision(real: 1); + var second = new Revision(real: 1, version: 2); + + first.Should().BeLessThan(second); + } + + [Test] + public void should_be_equal_when_both_real_and_version_match() + { + var first = new Revision(); + var second = new Revision(); + + first.CompareTo(second).Should().Be(0); + } + + [Test] + public void should_be_equal_when_both_real_and_version_match_for_real() + { + var first = new Revision(real: 1); + var second = new Revision(real: 1); + + first.CompareTo(second).Should().Be(0); + } + + [Test] + public void should_be_equal_when_both_real_and_version_match_for_real_proper() + { + var first = new Revision(version: 2, real: 1); + var second = new Revision(version: 2, real: 1); + + first.CompareTo(second).Should().Be(0); + } + + [Test] + public void equal_operator_tests() + { + var first = new Revision(); + var second = new Revision(); + + (first > second).Should().BeFalse(); + (first < second).Should().BeFalse(); + (first != second).Should().BeFalse(); + (first >= second).Should().BeTrue(); + (first <= second).Should().BeTrue(); + (first == second).Should().BeTrue(); + } + + [Test] + public void greater_than_operator_tests() + { + var first = new Revision(version: 2); + var second = new Revision(); + + (first > second).Should().BeTrue(); + (first < second).Should().BeFalse(); + (first != second).Should().BeTrue(); + (first >= second).Should().BeTrue(); + (first <= second).Should().BeFalse(); + (first == second).Should().BeFalse(); + } + + [Test] + public void less_than_operator_tests() + { + var first = new Revision(); + var second = new Revision(version: 2); + + (first > second).Should().BeFalse(); + (first < second).Should().BeTrue(); + (first != second).Should().BeTrue(); + (first >= second).Should().BeFalse(); + (first <= second).Should().BeTrue(); + (first == second).Should().BeFalse(); + } + + [Test] + public void operating_on_nulls() + { + (null > new Revision()).Should().BeFalse(); + (null >= new Revision()).Should().BeFalse(); + (null < new Revision()).Should().BeTrue(); + (null <= new Revision()).Should().BeTrue(); + + (new Revision() > null).Should().BeTrue(); + (new Revision() >= null).Should().BeTrue(); + (new Revision() < null).Should().BeFalse(); + (new Revision() <= null).Should().BeFalse(); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs b/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs index f423758f1..18fd52eb2 100644 --- a/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/QualityIntConverter.cs @@ -2,8 +2,6 @@ using Marr.Data.Converters; using Marr.Data.Mapping; using NzbDrone.Core.Qualities; -using System.Collections.Generic; -using NzbDrone.Common.Serializer; using Newtonsoft.Json; namespace NzbDrone.Core.Datastore.Converters diff --git a/src/NzbDrone.Core/Datastore/Migration/036_update_with_quality_converters.cs b/src/NzbDrone.Core/Datastore/Migration/036_update_with_quality_converters.cs index 78ffb967d..842da1428 100644 --- a/src/NzbDrone.Core/Datastore/Migration/036_update_with_quality_converters.cs +++ b/src/NzbDrone.Core/Datastore/Migration/036_update_with_quality_converters.cs @@ -1,4 +1,5 @@ -using FluentMigrator; +using System; +using FluentMigrator; using NzbDrone.Core.Datastore.Migration.Framework; using System.Data; using System.Linq; @@ -82,9 +83,9 @@ namespace NzbDrone.Core.Datastore.Migration { var qualityJson = qualityModelReader.GetString(0); - QualityModel quality; + QualityModel036 quality; - if (!Json.TryDeserialize(qualityJson, out quality)) + if (!Json.TryDeserialize(qualityJson, out quality)) { continue; } @@ -104,5 +105,11 @@ namespace NzbDrone.Core.Datastore.Migration } } } + + private class QualityModel036 + { + public Int32 Quality { get; set; } + public Boolean Proper { get; set; } + } } } diff --git a/src/NzbDrone.Core/Datastore/Migration/062_convert_quality_models.cs b/src/NzbDrone.Core/Datastore/Migration/062_convert_quality_models.cs new file mode 100644 index 000000000..b592df63d --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/062_convert_quality_models.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Data; +using FluentMigrator; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.Datastore.Migration.Framework; +using NzbDrone.Core.Qualities; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(62)] + public class convert_quality_models : NzbDroneMigrationBase + { + protected override void MainDbUpgrade() + { + Execute.WithConnection(ConvertQualityModels); + } + + private void ConvertQualityModels(IDbConnection conn, IDbTransaction tran) + { + ConvertQualityModelsOnTable(conn, tran, "EpisodeFiles"); + ConvertQualityModelsOnTable(conn, tran, "Blacklist"); + ConvertQualityModelsOnTable(conn, tran, "History"); + } + + private void ConvertQualityModelsOnTable(IDbConnection conn, IDbTransaction tran, String tableName) + { + var qualitiesToUpdate = new Dictionary(); + + using (IDbCommand qualityModelCmd = conn.CreateCommand()) + { + qualityModelCmd.Transaction = tran; + qualityModelCmd.CommandText = @"SELECT Distinct Quality FROM " + tableName; + + using (IDataReader qualityModelReader = qualityModelCmd.ExecuteReader()) + { + while (qualityModelReader.Read()) + { + var qualityJson = qualityModelReader.GetString(0); + + LegacyQualityModel062 quality; + + if (!Json.TryDeserialize(qualityJson, out quality)) + { + continue; + } + + var newQualityModel = new QualityModel062 { Quality = quality.Quality, Revision = new Revision() }; + if (quality.Proper) + newQualityModel.Revision.Version = 2; + var newQualityJson = newQualityModel.ToJson(); + + qualitiesToUpdate.Add(qualityJson, newQualityJson); + } + } + } + + foreach (var quality in qualitiesToUpdate) + { + using (IDbCommand updateCmd = conn.CreateCommand()) + { + updateCmd.Transaction = tran; + updateCmd.CommandText = "UPDATE " + tableName + " SET Quality = ? WHERE Quality = ?"; + updateCmd.AddParameter(quality.Value); + updateCmd.AddParameter(quality.Key); + + updateCmd.ExecuteNonQuery(); + } + } + } + + private class LegacyQualityModel062 + { + public Int32 Quality { get; set; } + public Boolean Proper { get; set; } + } + + private class QualityModel062 + { + public Int32 Quality { get; set; } + public Revision Revision { get; set; } + } + } +} diff --git a/src/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs b/src/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs index 0c83be0e3..1e443c23e 100644 --- a/src/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs @@ -8,7 +8,7 @@ namespace NzbDrone.Core.DecisionEngine { bool IsUpgradable(Profile profile, QualityModel currentQuality, QualityModel newQuality = null); bool CutoffNotMet(Profile profile, QualityModel currentQuality, QualityModel newQuality = null); - bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality); + bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality); } public class QualityUpgradableSpecification : IQualityUpgradableSpecification @@ -31,7 +31,7 @@ namespace NzbDrone.Core.DecisionEngine return false; } - if (IsProperUpgrade(currentQuality, newQuality)) + if (IsRevisionUpgrade(currentQuality, newQuality)) { return true; } @@ -46,7 +46,7 @@ namespace NzbDrone.Core.DecisionEngine if (compare >= 0) { - if (newQuality != null && IsProperUpgrade(currentQuality, newQuality)) + if (newQuality != null && IsRevisionUpgrade(currentQuality, newQuality)) { return true; } @@ -58,13 +58,13 @@ namespace NzbDrone.Core.DecisionEngine return true; } - public bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality) + public bool IsRevisionUpgrade(QualityModel currentQuality, QualityModel newQuality) { - int compare = newQuality.Proper.CompareTo(currentQuality.Proper); + int compare = newQuality.Revision.CompareTo(currentQuality.Revision); if (currentQuality.Quality == newQuality.Quality && compare > 0) { - _logger.Debug("New quality is a proper for existing quality"); + _logger.Debug("New quality is a better revision for existing quality"); return true; } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs index 3d117fc54..a7760340c 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/DelaySpecification.cs @@ -11,11 +11,13 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync public class DelaySpecification : IDecisionEngineSpecification { private readonly IPendingReleaseService _pendingReleaseService; + private readonly IQualityUpgradableSpecification _qualityUpgradableSpecification; private readonly Logger _logger; - public DelaySpecification(IPendingReleaseService pendingReleaseService, Logger logger) + public DelaySpecification(IPendingReleaseService pendingReleaseService, IQualityUpgradableSpecification qualityUpgradableSpecification, Logger logger) { _pendingReleaseService = pendingReleaseService; + _qualityUpgradableSpecification = qualityUpgradableSpecification; _logger = logger; } @@ -50,19 +52,18 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync var comparer = new QualityModelComparer(profile); - if (subject.ParsedEpisodeInfo.Quality.Proper) + 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)) + var upgradable = _qualityUpgradableSpecification.IsUpgradable(profile, file.Quality, subject.ParsedEpisodeInfo.Quality); + + if (upgradable) { - if (comparer.Compare(subject.ParsedEpisodeInfo.Quality, file.Quality) > 0) - { - var properCompare = subject.ParsedEpisodeInfo.Quality.Proper.CompareTo(file.Quality.Proper); + var revisionUpgrade = _qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality); - if (subject.ParsedEpisodeInfo.Quality.Quality == file.Quality.Quality && properCompare > 0) - { - _logger.Debug("New quality is a proper for existing quality, skipping delay"); - return true; - } + if (revisionUpgrade) + { + _logger.Debug("New quality is a better revision for existing quality, skipping delay"); + return true; } } } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs index a5ce14a84..ab7dda8ac 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs @@ -39,7 +39,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync foreach (var file in subject.Episodes.Where(c => c.EpisodeFileId != 0).Select(c => c.EpisodeFile.Value)) { - if (_qualityUpgradableSpecification.IsProperUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality)) + if (_qualityUpgradableSpecification.IsRevisionUpgrade(file.Quality, subject.ParsedEpisodeInfo.Quality)) { if (file.DateAdded < DateTime.Today.AddDays(-7)) { diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index deda0de3c..459a5b9f8 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -226,6 +226,7 @@ + @@ -562,6 +563,7 @@ + diff --git a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs index 5c4081523..62fd5314f 100644 --- a/src/NzbDrone.Core/Organizer/FileNameBuilder.cs +++ b/src/NzbDrone.Core/Organizer/FileNameBuilder.cs @@ -102,7 +102,6 @@ namespace NzbDrone.Core.Organizer } var pattern = namingConfig.StandardEpisodeFormat; - var tokenHandlers = new Dictionary>(FileNameBuilderTokenEqualityComparer.Instance); episodes = episodes.OrderBy(e => e.SeasonNumber).ThenBy(e => e.EpisodeNumber).ToList(); @@ -118,15 +117,11 @@ namespace NzbDrone.Core.Organizer } pattern = AddSeasonEpisodeNumberingTokens(pattern, tokenHandlers, episodes, namingConfig); - pattern = AddAbsoluteNumberingTokens(pattern, tokenHandlers, series, episodes, namingConfig); AddSeriesTokens(tokenHandlers, series); - AddEpisodeTokens(tokenHandlers, episodes); - - AddEpisodeFileTokens(tokenHandlers, episodeFile); - + AddEpisodeFileTokens(tokenHandlers, series, episodeFile); AddMediaInfoTokens(tokenHandlers, episodeFile); var filename = ReplaceTokens(pattern, tokenHandlers).Trim(); @@ -401,11 +396,11 @@ namespace NzbDrone.Core.Organizer tokenHandlers["{Episode Title}"] = m => GetEpisodeTitle(episodes); } - private void AddEpisodeFileTokens(Dictionary> tokenHandlers, EpisodeFile episodeFile) + private void AddEpisodeFileTokens(Dictionary> tokenHandlers, Series series, EpisodeFile episodeFile) { tokenHandlers["{Original Title}"] = m => episodeFile.SceneName; tokenHandlers["{Release Group}"] = m => episodeFile.ReleaseGroup ?? "DRONE"; - tokenHandlers["{Quality Title}"] = m => GetQualityTitle(episodeFile.Quality); + tokenHandlers["{Quality Title}"] = m => GetQualityTitle(series, episodeFile.Quality); } private void AddMediaInfoTokens(Dictionary> tokenHandlers, EpisodeFile episodeFile) @@ -651,12 +646,24 @@ namespace NzbDrone.Core.Organizer return String.Join(" + ", titles); } - private String GetQualityTitle(QualityModel quality) + private String GetQualityTitle(Series series, QualityModel quality) { - if (quality.Proper) - return _qualityDefinitionService.Get(quality.Quality).Title + " Proper"; - else - return _qualityDefinitionService.Get(quality.Quality).Title; + var qualitySuffix = String.Empty; + + if (quality.Revision.Version > 1) + { + if (series.SeriesType == SeriesTypes.Anime) + { + qualitySuffix = " v" + quality.Revision.Version; + } + + else + { + qualitySuffix = " Proper"; + } + } + + return _qualityDefinitionService.Get(quality.Quality).Title + qualitySuffix; } } diff --git a/src/NzbDrone.Core/Parser/QualityParser.cs b/src/NzbDrone.Core/Parser/QualityParser.cs index 7222e625e..4b63cb0ab 100644 --- a/src/NzbDrone.Core/Parser/QualityParser.cs +++ b/src/NzbDrone.Core/Parser/QualityParser.cs @@ -31,6 +31,12 @@ namespace NzbDrone.Core.Parser private static readonly Regex ProperRegex = new Regex(@"\b(?proper|repack)\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex VersionRegex = new Regex(@"\dv(?\d)\b|\[v(?\d)\]", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + + private static readonly Regex RealRegex = new Regex(@"\b(?)real\b", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex ResolutionRegex = new Regex(@"\b(?:(?<_480p>480p|640x480|848x480)|(?<_576p>576p)|(?<_720p>720p|1280x720)|(?<_1080p>1080p|1920x1080))\b", RegexOptions.Compiled | RegexOptions.IgnoreCase); @@ -49,9 +55,8 @@ namespace NzbDrone.Core.Parser name = name.Trim(); var normalizedName = name.Replace('_', ' ').Trim().ToLower(); - var result = new QualityModel { Quality = Quality.Unknown }; + var result = ParseQualityModifiers(normalizedName); - result.Proper = ProperRegex.IsMatch(normalizedName); if (RawHDRegex.IsMatch(normalizedName)) { @@ -282,7 +287,7 @@ namespace NzbDrone.Core.Parser return result; } - private static Resolution ParseResolution(string name) + private static Resolution ParseResolution(String name) { var match = ResolutionRegex.Match(name); @@ -295,7 +300,7 @@ namespace NzbDrone.Core.Parser return Resolution.Unknown; } - private static Quality OtherSourceMatch(string name) + private static Quality OtherSourceMatch(String name) { var match = OtherSourceRegex.Match(name); @@ -305,6 +310,34 @@ namespace NzbDrone.Core.Parser return Quality.Unknown; } + + private static QualityModel ParseQualityModifiers(String normalizedName) + { + var result = new QualityModel { Quality = Quality.Unknown }; + + if (ProperRegex.IsMatch(normalizedName)) + { + result.Revision.Version = 2; + } + + var versionRegexResult = VersionRegex.Match(normalizedName); + + if (versionRegexResult.Success) + { + result.Revision.Version = Convert.ToInt32(versionRegexResult.Groups["version"].Value); + } + + //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(normalizedName); +// +// if (realRegexResult.Count > 0) +// { +// result.Revision.Real = realRegexResult.Count; +// } + + return result; + } } public enum Resolution diff --git a/src/NzbDrone.Core/Qualities/QualityModel.cs b/src/NzbDrone.Core/Qualities/QualityModel.cs index ef4dc9bd0..ad1b16b43 100644 --- a/src/NzbDrone.Core/Qualities/QualityModel.cs +++ b/src/NzbDrone.Core/Qualities/QualityModel.cs @@ -1,37 +1,28 @@ using System; using NzbDrone.Core.Datastore; -using NzbDrone.Core.Qualities; namespace NzbDrone.Core.Qualities { public class QualityModel : IEmbeddedDocument, IEquatable { public Quality Quality { get; set; } - - public Boolean Proper { get; set; } + public Revision Revision { get; set; } public QualityModel() - : this(Quality.Unknown) + : this(Quality.Unknown, new Revision()) { } - public QualityModel(Quality quality, Boolean proper = false) + public QualityModel(Quality quality, Revision revision = null) { Quality = quality; - Proper = proper; + Revision = revision ?? new Revision(); } - public override string ToString() { - string result = Quality.ToString(); - if (Proper) - { - result += " Proper"; - } - - return result; + return String.Format("{0} {1}", Quality, Revision); } public override int GetHashCode() @@ -39,7 +30,7 @@ namespace NzbDrone.Core.Qualities unchecked // Overflow is fine, just wrap { int hash = 17; - hash = hash * 23 + Proper.GetHashCode(); + hash = hash * 23 + Revision.GetHashCode(); hash = hash * 23 + Quality.GetHashCode(); return hash; } @@ -49,7 +40,8 @@ namespace NzbDrone.Core.Qualities { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; - return other.Quality.Equals(Quality) && other.Proper.Equals(Proper); + + return other.Quality.Equals(Quality) && other.Revision.Equals(Revision); } public override bool Equals(object obj) diff --git a/src/NzbDrone.Core/Qualities/QualityModelComparer.cs b/src/NzbDrone.Core/Qualities/QualityModelComparer.cs index ed392e229..64f1939b8 100644 --- a/src/NzbDrone.Core/Qualities/QualityModelComparer.cs +++ b/src/NzbDrone.Core/Qualities/QualityModelComparer.cs @@ -29,7 +29,9 @@ namespace NzbDrone.Core.Qualities int result = Compare(left.Quality, right.Quality); if (result == 0) - result = left.Proper.CompareTo(right.Proper); + { + result = left.Revision.CompareTo(right.Revision); + } return result; } diff --git a/src/NzbDrone.Core/Qualities/Revision.cs b/src/NzbDrone.Core/Qualities/Revision.cs new file mode 100644 index 000000000..d4fd8a4c2 --- /dev/null +++ b/src/NzbDrone.Core/Qualities/Revision.cs @@ -0,0 +1,102 @@ +using System; +using System.Text; + +namespace NzbDrone.Core.Qualities +{ + public class Revision : IEquatable, IComparable + { + public Revision(Int32 version = 1, Int32 real = 0) + { + Version = version; + Real = real; + } + + public Int32 Version { get; set; } + public Int32 Real { get; set; } + + public bool Equals(Revision other) + { + if (ReferenceEquals(null, other)) return false; + + return other.Version.Equals(Version) && other.Real.Equals(Real); + } + + public int CompareTo(Revision other) + { + if (Real > other.Real) return 1; + if (Real < other.Real) return -1; + if (Version > other.Version) return 1; + if (Version < other.Version) return -1; + + return 0; + } + + public override string ToString() + { + var sb = new StringBuilder(); + sb.AppendFormat("v{0}", Version); + + if (Real > 0) + { + sb.AppendFormat(" Real:{0}", Real); + } + + return sb.ToString(); + } + + public override int GetHashCode() + { + return Version ^ Real << 8; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + + return Equals(obj as Revision); + } + + public static bool operator ==(Revision left, Revision right) + { + return Equals(left, right); + } + + public static bool operator !=(Revision left, Revision right) + { + return !Equals(left, right); + } + + public static bool operator >(Revision left, Revision right) + { + if (left == null) return false; + if (right == null) return true; + + return left.CompareTo(right) > 0; + } + + public static bool operator <(Revision left, Revision right) + { + if (left == null) return true; + if (right == null) return false; + + return left.CompareTo(right) < 0; + } + + public static bool operator >=(Revision left, Revision right) + { + if (left == null) return false; + if (right == null) return true; + + return left.CompareTo(right) >= 0; + } + + public static bool operator <=(Revision left, Revision right) + { + if (left == null) return true; + if (right == null) return false; + + return left.CompareTo(right) <= 0; + } + } +} diff --git a/src/UI/Cells/Edit/QualityCellEditor.js b/src/UI/Cells/Edit/QualityCellEditor.js index 475f0108d..a77b3d263 100644 --- a/src/UI/Cells/Edit/QualityCellEditor.js +++ b/src/UI/Cells/Edit/QualityCellEditor.js @@ -55,8 +55,11 @@ define( }); var newQuality = { - proper : false, - quality: profileItem.quality + quality : profileItem.quality, + revision : { + version : 1, + real : 0 + } }; model.set(column.get('name'), newQuality); diff --git a/src/UI/Cells/EpisodeStatusCell.js b/src/UI/Cells/EpisodeStatusCell.js index a7ed8a694..baec32371 100644 --- a/src/UI/Cells/EpisodeStatusCell.js +++ b/src/UI/Cells/EpisodeStatusCell.js @@ -41,10 +41,15 @@ define( this.listenTo(this.episodeFile, 'change', this._refresh); var quality = this.episodeFile.get('quality'); + var revision = quality.revision; var size = FormatHelpers.bytes(this.episodeFile.get('size')); var title = 'Episode downloaded'; - if (quality.proper) { + if (revision.real && revision.real > 0) { + title += '[REAL]'; + } + + if (revision.version && revision.version > 1) { title += ' [PROPER]'; } @@ -59,7 +64,6 @@ define( this.$el.html('{1}'.format(title, quality.quality.name)); } - return; } diff --git a/src/UI/Cells/QualityCellTemplate.hbs b/src/UI/Cells/QualityCellTemplate.hbs index 28b8c83a2..ede7dfe52 100644 --- a/src/UI/Cells/QualityCellTemplate.hbs +++ b/src/UI/Cells/QualityCellTemplate.hbs @@ -1,5 +1,5 @@ -{{#if proper}} +{{#if_gt proper compare="1"}} {{quality.name}} {{else}} {{quality.name}} -{{/if}} \ No newline at end of file +{{/if_gt}} \ No newline at end of file diff --git a/src/UI/History/Table/HistoryQualityCell.js b/src/UI/History/Table/HistoryQualityCell.js index f580f529a..ef53855ad 100644 --- a/src/UI/History/Table/HistoryQualityCell.js +++ b/src/UI/History/Table/HistoryQualityCell.js @@ -11,11 +11,18 @@ define( var title = ''; var quality = this.model.get('quality'); + var revision = quality.revision; - if (quality.proper) { - title = 'PROPER'; + if (revision.real && revision.real > 0) { + title += ' REAL'; } + if (revision.version && revision.version > 1) { + title += ' PROPER'; + } + + title = title.trim(); + if (this.model.get('qualityCutoffNotMet')) { this.$el.html('{1}'.format(title, quality.quality.name)); }