diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/NotInQueueSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/NotInQueueSpecificationFixture.cs new file mode 100644 index 000000000..1ca3e9c41 --- /dev/null +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/NotInQueueSpecificationFixture.cs @@ -0,0 +1,242 @@ +using System.Collections.Generic; +using FizzWare.NBuilder; +using FluentAssertions; +using Moq; +using NUnit.Framework; +using NzbDrone.Core.DecisionEngine.Specifications; +using NzbDrone.Core.Download; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Qualities; +using NzbDrone.Core.Tv; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.DecisionEngineTests +{ + [TestFixture] + public class NotInQueueSpecificationFixture : CoreTest + { + private Series _series; + private Episode _episode; + private RemoteEpisode _remoteEpisode; + private Mock _downloadClient; + + private Series _otherSeries; + private Episode _otherEpisode; + + [SetUp] + public void Setup() + { + _series = Builder.CreateNew().Build(); + + _episode = Builder.CreateNew() + .With(e => e.SeriesId = _series.Id) + .Build(); + + _otherSeries = Builder.CreateNew() + .With(s => s.Id = 2) + .Build(); + + _otherEpisode = Builder.CreateNew() + .With(e => e.SeriesId = _otherSeries.Id) + .With(e => e.Id = 2) + .With(e => e.SeasonNumber = 2) + .With(e => e.EpisodeNumber = 2) + .Build(); + + _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.DVD)}) + .Build(); + + _downloadClient = Mocker.GetMock(); + + Mocker.GetMock() + .Setup(s => s.GetDownloadClient()) + .Returns(_downloadClient.Object); + + _downloadClient.SetupGet(s => s.IsConfigured) + .Returns(true); + } + + private void GivenEmptyQueue() + { + _downloadClient.Setup(s => s.GetQueue()) + .Returns(new List()); + } + + private void GivenQueue(IEnumerable remoteEpisodes) + { + var queue = new List(); + + foreach (var remoteEpisode in remoteEpisodes) + { + queue.Add(new QueueItem + { + RemoteEpisode = remoteEpisode + }); + } + + _downloadClient.Setup(s => s.GetQueue()) + .Returns(queue); + } + + [Test] + public void should_return_true_when_queue_is_empty() + { + GivenEmptyQueue(); + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue(); + } + + [Test] + public void should_return_true_when_series_doesnt_match() + { + var remoteEpisode = Builder.CreateNew() + .With(r => r.Series = _otherSeries) + .With(r => r.Episodes = new List { _episode }) + .Build(); + + GivenQueue(new List { remoteEpisode }); + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue(); + } + + [Test] + public void should_return_true_when_quality_in_queue_is_lower() + { + 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.SDTV) + }) + .Build(); + + GivenQueue(new List { remoteEpisode }); + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue(); + } + + [Test] + public void should_return_true_when_episode_doesnt_match() + { + var remoteEpisode = Builder.CreateNew() + .With(r => r.Series = _series) + .With(r => r.Episodes = new List { _otherEpisode }) + .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo + { + Quality = new QualityModel(Quality.DVD) + }) + .Build(); + + GivenQueue(new List { remoteEpisode }); + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeTrue(); + } + + [Test] + public void should_return_false_when_qualities_are_the_same() + { + 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.DVD) + }) + .Build(); + + GivenQueue(new List { remoteEpisode }); + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse(); + } + + [Test] + public void should_return_false_when_quality_in_queue_is_better() + { + 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).Should().BeFalse(); + } + + [Test] + public void should_return_false_if_matching_multi_episode_is_in_queue() + { + var remoteEpisode = Builder.CreateNew() + .With(r => r.Series = _series) + .With(r => r.Episodes = new List { _episode, _otherEpisode }) + .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo + { + Quality = new QualityModel(Quality.HDTV720p) + }) + .Build(); + + GivenQueue(new List { remoteEpisode }); + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse(); + } + + [Test] + public void should_return_false_if_multi_episode_has_one_episode_in_queue() + { + 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(); + + _remoteEpisode.Episodes.Add(_otherEpisode); + + GivenQueue(new List { remoteEpisode }); + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse(); + } + + [Test] + public void should_return_false_if_multi_part_episode_is_already_in_queue() + { + var remoteEpisode = Builder.CreateNew() + .With(r => r.Series = _series) + .With(r => r.Episodes = new List { _episode, _otherEpisode }) + .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo + { + Quality = new QualityModel(Quality.HDTV720p) + }) + .Build(); + + _remoteEpisode.Episodes.Add(_otherEpisode); + + GivenQueue(new List { remoteEpisode }); + Subject.IsSatisfiedBy(_remoteEpisode, null).Should().BeFalse(); + } + + [Test] + public void should_return_false_if_multi_part_episode_has_two_episodes_in_queue() + { + var remoteEpisodes = Builder.CreateListOfSize(2) + .All() + .With(r => r.Series = _series) + .With(r => r.ParsedEpisodeInfo = new ParsedEpisodeInfo + { + Quality = + new QualityModel( + Quality.HDTV720p) + }) + .TheFirst(1) + .With(r => r.Episodes = new List {_episode}) + .TheNext(1) + .With(r => r.Episodes = new List {_otherEpisode}) + .Build(); + + _remoteEpisode.Episodes.Add(_otherEpisode); + GivenQueue(remoteEpisodes); + Subject.IsSatisfiedBy(_remoteEpisode, null ).Should().BeFalse(); + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/QueueFixture.cs b/src/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/QueueFixture.cs index d4ee7aef4..6c83e0bcb 100644 --- a/src/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/QueueFixture.cs +++ b/src/NzbDrone.Core.Test/Download/DownloadClientTests/NzbgetProviderTests/QueueFixture.cs @@ -5,11 +5,14 @@ using NUnit.Framework; using NzbDrone.Common; using NzbDrone.Core.Configuration; using NzbDrone.Core.Download.Clients.Nzbget; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Test.Framework; +using NzbDrone.Core.Tv; namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetProviderTests { - public class QueueFixture : CoreTest + public class QueueFixture : CoreTest { [SetUp] public void Setup() @@ -49,10 +52,9 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetProviderTests { WithEmptyQueue(); - Mocker.Resolve() - .GetQueue() - .Should() - .BeEmpty(); + Subject.GetQueue() + .Should() + .BeEmpty(); } [Test] @@ -60,10 +62,13 @@ namespace NzbDrone.Core.Test.Download.DownloadClientTests.NzbgetProviderTests { WithFullQueue(); - Mocker.Resolve() - .GetQueue() - .Should() - .HaveCount(1); + Mocker.GetMock() + .Setup(s => s.Map(It.IsAny(), 0, null)) + .Returns(new RemoteEpisode {Series = new Series()}); + + Subject.GetQueue() + .Should() + .HaveCount(1); } } } diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index cbe2b0f67..4bdb919b9 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -111,6 +111,7 @@ + diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs index 03a2b46f6..6a8adc40f 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/NotInQueueSpecification.cs @@ -4,6 +4,7 @@ using System.Linq; using NLog; using NzbDrone.Core.Download; using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Tv; @@ -38,31 +39,17 @@ namespace NzbDrone.Core.DecisionEngine.Specifications return true; } - var queue = downloadClient.GetQueue().Select(queueItem => Parser.Parser.ParseTitle(queueItem.Title)).Where(episodeInfo => episodeInfo != null); + var queue = downloadClient.GetQueue().Select(q => q.RemoteEpisode); return !IsInQueue(subject, queue); } - private bool IsInQueue(RemoteEpisode newEpisode, IEnumerable queue) + private bool IsInQueue(RemoteEpisode newEpisode, IEnumerable queue) { - var matchingTitle = queue.Where(q => String.Equals(q.SeriesTitle, newEpisode.Series.CleanTitle, StringComparison.InvariantCultureIgnoreCase)); + var matchingSeries = queue.Where(q => q.Series.Id == newEpisode.Series.Id); + var matchingSeriesAndQuality = matchingSeries.Where(q => q.ParsedEpisodeInfo.Quality >= newEpisode.ParsedEpisodeInfo.Quality); - var matchingTitleWithQuality = matchingTitle.Where(q => q.Quality >= newEpisode.ParsedEpisodeInfo.Quality); - - if (newEpisode.Series.SeriesType == SeriesTypes.Daily) - { - return matchingTitleWithQuality.Any(q => q.AirDate.Value.Date == newEpisode.ParsedEpisodeInfo.AirDate.Value.Date); - } - - var matchingSeason = matchingTitleWithQuality.Where(q => q.SeasonNumber == newEpisode.ParsedEpisodeInfo.SeasonNumber); - - if (newEpisode.ParsedEpisodeInfo.FullSeason) - { - return matchingSeason.Any(); - } - - return matchingSeason.Any(q => q.EpisodeNumbers != null && q.EpisodeNumbers.Any(e => newEpisode.ParsedEpisodeInfo.EpisodeNumbers.Contains(e))); + return matchingSeriesAndQuality.Any(q => q.Episodes.Select(e => e.Id).Intersect(newEpisode.Episodes.Select(e => e.Id)).Any()); } - } } diff --git a/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs index e7a53fde9..f3d692d32 100644 --- a/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs +++ b/src/NzbDrone.Core/Download/Clients/Nzbget/NzbgetClient.cs @@ -4,6 +4,7 @@ using NLog; using NzbDrone.Common; using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Download.Clients.Nzbget @@ -12,12 +13,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget { private readonly IConfigService _configService; private readonly IHttpProvider _httpProvider; + private readonly IParsingService _parsingService; private readonly Logger _logger; - public NzbgetClient(IConfigService configService, IHttpProvider httpProvider, Logger logger) + public NzbgetClient(IConfigService configService, IHttpProvider httpProvider, IParsingService parsingService, Logger logger) { _configService = configService; _httpProvider = httpProvider; + _parsingService = parsingService; _logger = logger; } @@ -75,6 +78,14 @@ namespace NzbDrone.Core.Download.Clients.Nzbget queueItem.Size = nzbGetQueueItem.FileSizeMb; queueItem.Sizeleft = nzbGetQueueItem.RemainingSizeMb; + var parsedEpisodeInfo = Parser.Parser.ParseTitle(queueItem.Title); + if (parsedEpisodeInfo == null) continue; + + var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0); + if (remoteEpisode.Series == null) continue; + + queueItem.RemoteEpisode = remoteEpisode; + yield return queueItem; } } diff --git a/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs b/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs index 3e36e941d..4e1fabcfc 100644 --- a/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs +++ b/src/NzbDrone.Core/Download/Clients/Sabnzbd/SabnzbdClient.cs @@ -7,6 +7,7 @@ using NzbDrone.Common; using NzbDrone.Common.Cache; using NzbDrone.Common.Serializer; using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; using NzbDrone.Core.Parser.Model; using RestSharp; @@ -53,13 +54,19 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd { private readonly IConfigService _configService; private readonly IHttpProvider _httpProvider; + private readonly IParsingService _parsingService; private readonly ICached> _queueCache; private readonly Logger _logger; - public SabnzbdClient(IConfigService configService, IHttpProvider httpProvider, ICacheManger cacheManger, Logger logger) + public SabnzbdClient(IConfigService configService, + IHttpProvider httpProvider, + ICacheManger cacheManger, + IParsingService parsingService, + Logger logger) { _configService = configService; _httpProvider = httpProvider; + _parsingService = parsingService; _queueCache = cacheManger.GetCache>(GetType(), "queue"); _logger = logger; } @@ -121,6 +128,14 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd queueItem.Timeleft = sabQueueItem.Timeleft; queueItem.Status = sabQueueItem.Status; + var parsedEpisodeInfo = Parser.Parser.ParseTitle(queueItem.Title); + if (parsedEpisodeInfo == null) continue; + + var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0); + if (remoteEpisode.Series == null) continue; + + queueItem.RemoteEpisode = remoteEpisode; + queueItems.Add(queueItem); } diff --git a/src/NzbDrone.Core/Download/IDownloadClient.cs b/src/NzbDrone.Core/Download/IDownloadClient.cs index aca724d1b..ce32b62b2 100644 --- a/src/NzbDrone.Core/Download/IDownloadClient.cs +++ b/src/NzbDrone.Core/Download/IDownloadClient.cs @@ -9,5 +9,4 @@ namespace NzbDrone.Core.Download bool IsConfigured { get; } IEnumerable GetQueue(); } - } diff --git a/src/NzbDrone.Core/Download/QueueItem.cs b/src/NzbDrone.Core/Download/QueueItem.cs index c63d98c1a..9112680b9 100644 --- a/src/NzbDrone.Core/Download/QueueItem.cs +++ b/src/NzbDrone.Core/Download/QueueItem.cs @@ -1,4 +1,5 @@ using System; +using NzbDrone.Core.Parser.Model; namespace NzbDrone.Core.Download { @@ -10,5 +11,6 @@ namespace NzbDrone.Core.Download public decimal Sizeleft { get; set; } public TimeSpan Timeleft { get; set; } public String Status { get; set; } + public RemoteEpisode RemoteEpisode { get; set; } } } diff --git a/src/NzbDrone.Core/Queue/QueueService.cs b/src/NzbDrone.Core/Queue/QueueService.cs index e3e78c7ac..1bc417fef 100644 --- a/src/NzbDrone.Core/Queue/QueueService.cs +++ b/src/NzbDrone.Core/Queue/QueueService.cs @@ -15,13 +15,11 @@ namespace NzbDrone.Core.Queue public class QueueService : IQueueService { private readonly IProvideDownloadClient _downloadClientProvider; - private readonly IParsingService _parsingService; private readonly Logger _logger; - public QueueService(IProvideDownloadClient downloadClientProvider, IParsingService parsingService, Logger logger) + public QueueService(IProvideDownloadClient downloadClientProvider, Logger logger) { _downloadClientProvider = downloadClientProvider; - _parsingService = parsingService; _logger = logger; } @@ -39,31 +37,19 @@ namespace NzbDrone.Core.Queue foreach (var queueItem in queueItems) { - var parsedEpisodeInfo = Parser.Parser.ParseTitle(queueItem.Title); - - if (parsedEpisodeInfo != null && !string.IsNullOrWhiteSpace(parsedEpisodeInfo.SeriesTitle)) + foreach (var episode in queueItem.RemoteEpisode.Episodes) { - var remoteEpisode = _parsingService.Map(parsedEpisodeInfo, 0); - - if (remoteEpisode.Series == null) - { - continue; - } - - foreach (var episode in remoteEpisode.Episodes) - { - var queue = new Queue(); - queue.Id = queueItem.Id.GetHashCode(); - queue.Series = remoteEpisode.Series; - queue.Episode = episode; - queue.Quality = remoteEpisode.ParsedEpisodeInfo.Quality; - queue.Title = queueItem.Title; - queue.Size = queueItem.Size; - queue.Sizeleft = queueItem.Sizeleft; - queue.Timeleft = queueItem.Timeleft; - queue.Status = queueItem.Status; - queued.Add(queue); - } + var queue = new Queue(); + queue.Id = queueItem.Id.GetHashCode(); + queue.Series = queueItem.RemoteEpisode.Series; + queue.Episode = episode; + queue.Quality = queueItem.RemoteEpisode.ParsedEpisodeInfo.Quality; + queue.Title = queueItem.Title; + queue.Size = queueItem.Size; + queue.Sizeleft = queueItem.Sizeleft; + queue.Timeleft = queueItem.Timeleft; + queue.Status = queueItem.Status; + queued.Add(queue); } }