diff --git a/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs index ebb433bb7..7ff36a03b 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/CutoffSpecificationFixture.cs @@ -11,24 +11,38 @@ namespace NzbDrone.Core.Test.DecisionEngineTests public class CutoffSpecificationFixture : CoreTest { [Test] - public void IsUpgradePossible_should_return_true_if_current_episode_is_less_than_cutoff() + public void should_return_true_if_current_episode_is_less_than_cutoff() { Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.Bluray1080p }, new QualityModel(Quality.DVD, true)).Should().BeTrue(); } [Test] - public void IsUpgradePossible_should_return_false_if_current_episode_is_equal_to_cutoff() + public void should_return_false_if_current_episode_is_equal_to_cutoff() { Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, new QualityModel(Quality.HDTV720p, true)).Should().BeFalse(); } [Test] - public void IsUpgradePossible_should_return_false_if_current_episode_is_greater_than_cutoff() + public void should_return_false_if_current_episode_is_greater_than_cutoff() { Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); } + + [Test] + public void should_return_true_when_new_episode_is_proper_but_existing_is_not() + { + Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, + new QualityModel(Quality.HDTV720p, false), new QualityModel(Quality.HDTV720p, true)).Should().BeTrue(); + } + + [Test] + public void should_return_false_if_cutoff_is_met_and_quality_is_higher() + { + Subject.CutoffNotMet(new QualityProfile { Cutoff = Quality.HDTV720p }, + new QualityModel(Quality.HDTV720p, true), new QualityModel(Quality.Bluray1080p, true)).Should().BeFalse(); + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs new file mode 100644 index 000000000..08dd9d9ee --- /dev/null +++ b/NzbDrone.Core.Test/DecisionEngineTests/RssSync/ProperSpecificationFixture.cs @@ -0,0 +1,138 @@ +using System; +using System.Collections.Generic; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.DecisionEngine.Specifications; +using NzbDrone.Core.DecisionEngine.Specifications.RssSync; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.MediaFiles; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Tv; +using NzbDrone.Core.DecisionEngine; + +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.DecisionEngineTests.RssSync +{ + [TestFixture] + + public class ProperSpecificationFixture : CoreTest + { + private RemoteEpisode _parseResultMulti; + private RemoteEpisode _parseResultSingle; + private EpisodeFile _firstFile; + private EpisodeFile _secondFile; + + [SetUp] + public void Setup() + { + 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 }; + + 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 } }; + + var fakeSeries = Builder.CreateNew() + .With(c => c.QualityProfile = new QualityProfile { Cutoff = Quality.Bluray1080p }) + .Build(); + + _parseResultMulti = new RemoteEpisode + { + Series = fakeSeries, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + Episodes = doubleEpisodeList + }; + + _parseResultSingle = new RemoteEpisode + { + Series = fakeSeries, + ParsedEpisodeInfo = new ParsedEpisodeInfo { Quality = new QualityModel(Quality.DVD, true) }, + Episodes = singleEpisodeList + }; + } + + private void WithFirstFileUpgradable() + { + _firstFile.Quality = new QualityModel(Quality.SDTV); + } + + private void GivenAutoDownloadPropers() + { + Mocker.GetMock() + .Setup(s => s.AutoDownloadPropers) + .Returns(true); + } + + [Test] + public void should_return_false_when_episodeFile_was_added_more_than_7_days_ago() + { + _firstFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_first_episodeFile_was_added_more_than_7_days_ago() + { + _firstFile.Quality.Quality = Quality.DVD; + _secondFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_second_episodeFile_was_added_more_than_7_days_ago() + { + _firstFile.Quality.Quality = Quality.DVD; + _secondFile.Quality.Quality = Quality.DVD; + + _secondFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse(); + } + + [Test] + public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_proper_is_for_better_quality() + { + WithFirstFileUpgradable(); + + _firstFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue(); + } + + [Test] + public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_is_for_search() + { + WithFirstFileUpgradable(); + + _firstFile.DateAdded = DateTime.Today.AddDays(-30); + Subject.IsSatisfiedBy(_parseResultSingle, new SingleEpisodeSearchCriteria()).Should().BeTrue(); + } + + [Test] + public void should_return_false_when_proper_but_auto_download_propers_is_false() + { + _firstFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today; + Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); + } + + [Test] + public void should_return_true_when_episodeFile_was_added_today() + { + GivenAutoDownloadPropers(); + + _firstFile.Quality.Quality = Quality.DVD; + + _firstFile.DateAdded = DateTime.Today; + Subject.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs b/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs index db083a6b9..cff16ba99 100644 --- a/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs +++ b/NzbDrone.Core.Test/DecisionEngineTests/UpgradeDiskSpecificationFixture.cs @@ -124,52 +124,5 @@ namespace NzbDrone.Core.Test.DecisionEngineTests _parseResultSingle.ParsedEpisodeInfo.Quality = new QualityModel(Quality.WEBDL1080p, false); _upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); } - - [Test] - public void should_return_false_when_episodeFile_was_added_more_than_7_days_ago() - { - _firstFile.Quality.Quality = Quality.DVD; - - _firstFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeFalse(); - } - - [Test] - public void should_return_false_when_first_episodeFile_was_added_more_than_7_days_ago() - { - _firstFile.Quality.Quality = Quality.DVD; - _secondFile.Quality.Quality = Quality.DVD; - - _firstFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse(); - } - - [Test] - public void should_return_false_when_second_episodeFile_was_added_more_than_7_days_ago() - { - _firstFile.Quality.Quality = Quality.DVD; - _secondFile.Quality.Quality = Quality.DVD; - - _secondFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultMulti, null).Should().BeFalse(); - } - - [Test] - public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_proper_is_for_better_quality() - { - WithFirstFileUpgradable(); - - _firstFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultSingle, null).Should().BeTrue(); - } - - [Test] - public void should_return_true_when_episodeFile_was_added_more_than_7_days_ago_but_is_for_search() - { - WithFirstFileUpgradable(); - - _firstFile.DateAdded = DateTime.Today.AddDays(-30); - _upgradeDisk.IsSatisfiedBy(_parseResultSingle, new SingleEpisodeSearchCriteria()).Should().BeTrue(); - } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 1170aa661..d6929c4f5 100644 --- a/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -112,6 +112,7 @@ + diff --git a/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs b/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs index 2f35a1db6..5694d1402 100644 --- a/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/QualityUpgradableSpecification.cs @@ -10,16 +10,15 @@ namespace NzbDrone.Core.DecisionEngine { bool IsUpgradable(QualityModel currentQuality, QualityModel newQuality = null); bool CutoffNotMet(QualityProfile profile, QualityModel currentQuality, QualityModel newQuality = null); + bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality); } public class QualityUpgradableSpecification : IQualityUpgradableSpecification { - private readonly IConfigService _configService; private readonly Logger _logger; - public QualityUpgradableSpecification(IConfigService configService, Logger logger) + public QualityUpgradableSpecification(Logger logger) { - _configService = configService; _logger = logger; } @@ -33,9 +32,8 @@ namespace NzbDrone.Core.DecisionEngine return false; } - if (currentQuality.Quality == newQuality.Quality && newQuality.Proper && _configService.AutoDownloadPropers) + if (IsProperUpgrade(currentQuality, newQuality)) { - _logger.Trace("Upgrading existing item to proper."); return true; } } @@ -47,11 +45,27 @@ namespace NzbDrone.Core.DecisionEngine { if (currentQuality.Quality >= profile.Cutoff) { + if (newQuality != null && IsProperUpgrade(currentQuality, newQuality)) + { + return true; + } + _logger.Trace("Existing item meets cut-off. skipping."); return false; } return true; } + + public bool IsProperUpgrade(QualityModel currentQuality, QualityModel newQuality) + { + if (currentQuality.Quality == newQuality.Quality && newQuality > currentQuality) + { + _logger.Trace("New quality is a proper for existing quality"); + return true; + } + + return false; + } } } diff --git a/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs new file mode 100644 index 000000000..f0a133409 --- /dev/null +++ b/NzbDrone.Core/DecisionEngine/Specifications/RssSync/ProperSpecification.cs @@ -0,0 +1,59 @@ +using System; +using System.Linq; +using NLog; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync +{ + public class ProperSpecification : IDecisionEngineSpecification + { + private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; + private readonly IConfigService _configService; + private readonly Logger _logger; + + public ProperSpecification(QualityUpgradableSpecification qualityUpgradableSpecification, IConfigService configService, Logger logger) + { + _qualityUpgradableSpecification = qualityUpgradableSpecification; + _configService = configService; + _logger = logger; + } + + public string RejectionReason + { + get + { + return "Proper for old episode"; + } + } + + public virtual bool IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + { + if (searchCriteria != null) + { + return true; + } + + 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 (file.DateAdded < DateTime.Today.AddDays(-7)) + { + _logger.Trace("Proper for old file, skipping: {0}", subject); + return false; + } + + if (!_configService.AutoDownloadPropers) + { + _logger.Trace("Auto downloading of propers is disabled"); + return false; + } + } + } + + return true; + } + } +} diff --git a/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs b/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs index 85aaf220b..1e4bfb361 100644 --- a/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs +++ b/NzbDrone.Core/DecisionEngine/Specifications/UpgradeDiskSpecification.cs @@ -35,15 +35,6 @@ namespace NzbDrone.Core.DecisionEngine.Specifications { return false; } - - if (searchCriteria == null && - subject.ParsedEpisodeInfo.Quality.Quality == file.Quality.Quality && - subject.ParsedEpisodeInfo.Quality.Proper && - file.DateAdded < DateTime.Today.AddDays(-7)) - { - _logger.Trace("Proper for old file, skipping: {0}", subject); - return false; - } } return true; diff --git a/NzbDrone.Core/NzbDrone.Core.csproj b/NzbDrone.Core/NzbDrone.Core.csproj index dd2fb5d5d..37024228a 100644 --- a/NzbDrone.Core/NzbDrone.Core.csproj +++ b/NzbDrone.Core/NzbDrone.Core.csproj @@ -187,6 +187,7 @@ +