diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/NotInQueueSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs similarity index 90% rename from src/NzbDrone.Core.Test/DecisionEngineTests/NotInQueueSpecificationFixture.cs rename to src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs index 9991b9c53..4ca13f8a4 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/NotInQueueSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/QueueSpecificationFixture.cs @@ -3,6 +3,7 @@ using System.Linq; using FizzWare.NBuilder; using FluentAssertions; using NUnit.Framework; +using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine.Specifications; using NzbDrone.Core.Download.TrackedDownloads; using NzbDrone.Core.Parser.Model; @@ -15,7 +16,7 @@ using NzbDrone.Core.Test.Framework; namespace NzbDrone.Core.Test.DecisionEngineTests { [TestFixture] - public class NotInQueueSpecificationFixture : CoreTest + public class QueueSpecificationFixture : CoreTest { private Series _series; private Episode _episode; @@ -27,6 +28,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [SetUp] public void Setup() { + Mocker.Resolve(); + _series = Builder.CreateNew() .With(e => e.Profile = new Profile { Items = Qualities.QualityFixture.GetDefaultQualities() }) .Build(); @@ -91,10 +94,11 @@ namespace NzbDrone.Core.Test.DecisionEngineTests Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeTrue(); } - [Test] public void should_return_true_when_quality_in_queue_is_lower() { + _series.Profile.Value.Cutoff = Quality.Bluray1080p; + var remoteEpisode = Builder.CreateNew() .With(r => r.Series = _series) .With(r => r.Episodes = new List { _episode }) @@ -143,6 +147,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests [Test] public void should_return_false_when_quality_in_queue_is_better() { + _series.Profile.Value.Cutoff = Quality.Bluray1080p; + var remoteEpisode = Builder.CreateNew() .With(r => r.Series = _series) .With(r => r.Episodes = new List { _episode }) @@ -230,5 +236,24 @@ namespace NzbDrone.Core.Test.DecisionEngineTests GivenQueue(remoteEpisodes); Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); } + + [Test] + public void should_return_false_if_quality_in_queue_meets_cutoff() + { + _series.Profile.Value.Cutoff = _remoteEpisode.ParsedEpisodeInfo.Quality.Quality; + + var remoteEpisode = Builder.CreateNew() + .With(r => r.Series = _series) + .With(r => r.Episodes = new List { _episode }) + .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo + { + Quality = new QualityModel(Quality.HDTV720p) + }) + .Build(); + + GivenQueue(new List { remoteEpisode }); + + Subject.IsSatisfiedBy(_remoteEpisode, null).Accepted.Should().BeFalse(); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 7bfc92ab3..61c0ff644 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -136,7 +136,7 @@ - + diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs deleted file mode 100644 index 3fa9915ca..000000000 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using NLog; -using NzbDrone.Core.IndexerSearch.Definitions; -using NzbDrone.Core.Parser.Model; -using NzbDrone.Core.Qualities; -using NzbDrone.Core.Queue; - -namespace NzbDrone.Core.DecisionEngine.Specifications -{ - public class NotInQueueSpecification : IDecisionEngineSpecification - { - private readonly IQueueService _queueService; - private readonly Logger _logger; - - public NotInQueueSpecification(IQueueService queueService, Logger logger) - { - _queueService = queueService; - _logger = logger; - } - - public RejectionType Type { get { return RejectionType.Permanent; } } - - public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) - { - var queue = _queueService.GetQueue() - .Select(q => q.RemoteEpisode).ToList(); - - if (IsInQueue(subject, queue)) - { - _logger.Debug("Already in queue, rejecting."); - return Decision.Reject("Already in download queue"); - } - - return Decision.Accept(); - } - - private bool IsInQueue(RemoteEpisode newEpisode, IEnumerable episodesInQueue) - { - var matchingSeries = episodesInQueue.Where(q => q.Series.Id == newEpisode.Series.Id); - var matchingSeriesAndQuality = matchingSeries.Where(q => new QualityModelComparer(q.Series.Profile).Compare(q.ParsedEpisodeInfo.Quality, newEpisode.ParsedEpisodeInfo.Quality) >= 0); - - return matchingSeriesAndQuality.Any(q => q.Episodes.Select(e => e.Id).Intersect(newEpisode.Episodes.Select(e => e.Id)).Any()); - } - } -} diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs new file mode 100644 index 000000000..8a3221896 --- /dev/null +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/QueueSpecification.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Linq; +using NLog; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Queue; + +namespace NzbDrone.Core.DecisionEngine.Specifications +{ + public class QueueSpecification : IDecisionEngineSpecification + { + private readonly IQueueService _queueService; + private readonly QualityUpgradableSpecification _qualityUpgradableSpecification; + private readonly Logger _logger; + + public QueueSpecification(IQueueService queueService, + QualityUpgradableSpecification qualityUpgradableSpecification, + Logger logger) + { + _queueService = queueService; + _qualityUpgradableSpecification = qualityUpgradableSpecification; + _logger = logger; + } + + public RejectionType Type { get { return RejectionType.Permanent; } } + + public Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) + { + var queue = _queueService.GetQueue() + .Select(q => q.RemoteEpisode).ToList(); + + var matchingSeries = queue.Where(q => q.Series.Id == subject.Series.Id); + var matchingEpisode = matchingSeries.Where(q => q.Episodes.Select(e => e.Id).Intersect(subject.Episodes.Select(e => e.Id)).Any()); + + foreach (var remoteEpisode in matchingEpisode) + { + _logger.Debug("Checking if existing release in queue meets cutoff. Queued quality is: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); + + if (!_qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, remoteEpisode.ParsedEpisodeInfo.Quality, subject.ParsedEpisodeInfo.Quality)) + { + return Decision.Reject("Quality for release in queue alerady meets cutoff: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); + } + + _logger.Debug("Checking if release is higher quality than queued release. Queued quality is: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); + + if (!_qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, remoteEpisode.ParsedEpisodeInfo.Quality, subject.ParsedEpisodeInfo.Quality)) + { + return Decision.Reject("Quality for release in queue is of equal or higher preference: {0}", remoteEpisode.ParsedEpisodeInfo.Quality); + } + } + + return Decision.Accept(); + } + } +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index b9a7686f5..c8b4b24d5 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -280,7 +280,7 @@ - +