mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-14 11:23:42 +02:00
Episode searching now stores the results of the tests.
This commit is contained in:
parent
b9e3d1a921
commit
c7f8f57f77
@ -6,9 +6,11 @@
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Jobs;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Model.Notification;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common.AutoMoq;
|
||||
|
||||
@ -39,10 +41,16 @@ public void SeasonSearch_full_season_success()
|
||||
[Test]
|
||||
public void SeasonSearch_partial_season_success()
|
||||
{
|
||||
var resultItems = Builder<SearchResultItem>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SearchError = ReportRejectionType.None)
|
||||
.With(e => e.Success = true)
|
||||
.Build();
|
||||
|
||||
var episodes = Builder<Episode>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SeriesId = 1)
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.SeriesId = 5)
|
||||
.Build();
|
||||
|
||||
var notification = new ProgressNotification("Season Search");
|
||||
@ -55,7 +63,7 @@ public void SeasonSearch_partial_season_success()
|
||||
|
||||
Mocker.GetMock<SearchProvider>()
|
||||
.Setup(c => c.PartialSeasonSearch(notification, 1, 1))
|
||||
.Returns(episodes.Select(e => e.EpisodeNumber).ToList());
|
||||
.Returns(resultItems.ToList());
|
||||
|
||||
//Act
|
||||
Mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
|
||||
@ -88,7 +96,7 @@ public void SeasonSearch_partial_season_failure()
|
||||
|
||||
Mocker.GetMock<SearchProvider>()
|
||||
.Setup(c => c.PartialSeasonSearch(notification, 1, 1))
|
||||
.Returns(new List<int>{1});
|
||||
.Returns(new List<SearchResultItem>{ new SearchResultItem{ Success = true }});
|
||||
|
||||
//Act
|
||||
Mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
|
||||
@ -122,7 +130,7 @@ public void SeasonSearch_should_not_search_for_episodes_that_havent_aired_yet_or
|
||||
|
||||
Mocker.GetMock<SearchProvider>()
|
||||
.Setup(c => c.PartialSeasonSearch(notification, 1, 1))
|
||||
.Returns(new List<int>());
|
||||
.Returns(new List<SearchResultItem> { new SearchResultItem { Success = false, SearchError = ReportRejectionType.Size} });
|
||||
|
||||
|
||||
//Act
|
||||
|
@ -88,42 +88,42 @@ private void WithOverRetention()
|
||||
[Test]
|
||||
public void should_be_allowed_if_all_conditions_are_met()
|
||||
{
|
||||
spec.IsSatisfiedBy(parseResult).Should().BeTrue();
|
||||
spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.None);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_allowed_if_profile_is_not_allowed()
|
||||
{
|
||||
WithProfileNotAllowed();
|
||||
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
|
||||
spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.QualityNotWanted);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_allowed_if_size_is_not_allowed()
|
||||
{
|
||||
WithNotAcceptableSize();
|
||||
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
|
||||
spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.Size);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_allowed_if_disk_is_not_upgrade()
|
||||
{
|
||||
WithNoDiskUpgrade();
|
||||
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
|
||||
spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.ExistingQualityIsEqualOrBetter);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_allowed_if_episode_is_already_in_queue()
|
||||
{
|
||||
WithEpisodeAlreadyInQueue();
|
||||
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
|
||||
spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.AlreadyInQueue);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_be_allowed_if_report_is_over_retention()
|
||||
{
|
||||
WithOverRetention();
|
||||
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
|
||||
spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.Retention);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -134,7 +134,7 @@ public void should_not_be_allowed_if_none_of_conditions_are_met()
|
||||
WithProfileNotAllowed();
|
||||
WithOverRetention();
|
||||
|
||||
spec.IsSatisfiedBy(parseResult).Should().BeFalse();
|
||||
spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.QualityNotWanted);
|
||||
}
|
||||
}
|
||||
}
|
@ -77,14 +77,14 @@ private void WithQualityNeeded()
|
||||
{
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(true);
|
||||
.Returns(ReportRejectionType.None);
|
||||
}
|
||||
|
||||
private void WithQualityNotNeeded()
|
||||
{
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(false);
|
||||
.Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -103,13 +103,13 @@ public void processSearchResults_higher_quality_should_be_called_first()
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p)))
|
||||
.Returns(true);
|
||||
.Returns(ReportRejectionType.None);
|
||||
|
||||
//Act
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
|
||||
|
||||
//Assert
|
||||
result.Should().BeTrue();
|
||||
result.Should().Contain(n => n.Success);
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
@ -133,7 +133,7 @@ public void processSearchResults_when_quality_is_not_needed_should_check_the_res
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
|
||||
|
||||
//Assert
|
||||
result.Should().BeFalse();
|
||||
result.Should().NotContain(n => n.Success);
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Exactly(5));
|
||||
@ -155,7 +155,7 @@ public void processSearchResults_should_skip_if_series_is_null()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
|
||||
|
||||
//Assert
|
||||
result.Should().BeFalse();
|
||||
result.Should().NotContain(n => n.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
@ -175,7 +175,7 @@ public void processSearchResults_should_skip_if_series_is_mismatched()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
|
||||
|
||||
//Assert
|
||||
result.Should().BeFalse();
|
||||
result.Should().NotContain(n => n.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
@ -198,7 +198,7 @@ public void processSearchResults_should_return_after_successful_download()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
|
||||
|
||||
//Assert
|
||||
result.Should().BeTrue();
|
||||
result.Should().Contain(n => n.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
@ -230,7 +230,7 @@ public void processSearchResults_should_try_next_if_download_fails()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
|
||||
|
||||
//Assert
|
||||
result.Should().BeTrue();
|
||||
result.Should().Contain(n => n.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Exactly(2));
|
||||
@ -250,7 +250,7 @@ public void processSearchResults_should_skip_if_parseResult_does_not_have_airdat
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
|
||||
|
||||
//Assert
|
||||
result.Should().BeFalse();
|
||||
result.Should().NotContain(n => n.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
@ -270,7 +270,7 @@ public void processSearchResults_should_skip_if_parseResult_airdate_does_not_mat
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
|
||||
|
||||
//Assert
|
||||
result.Should().BeFalse();
|
||||
result.Should().NotContain(n => n.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
|
@ -73,14 +73,14 @@ private void WithQualityNeeded()
|
||||
{
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(true);
|
||||
.Returns(ReportRejectionType.None);
|
||||
}
|
||||
|
||||
private void WithQualityNotNeeded()
|
||||
{
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(false);
|
||||
.Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -102,14 +102,14 @@ public void processSearchResults_higher_quality_should_be_called_first()
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p)))
|
||||
.Returns(true);
|
||||
.Returns(ReportRejectionType.None);
|
||||
|
||||
//Act
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(1);
|
||||
result.First().Should().Be(1);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().Contain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
@ -135,13 +135,14 @@ public void processSearchResults_newer_report_should_be_called_first()
|
||||
WithSuccessfulDownload();
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(ReportRejectionType.None);
|
||||
|
||||
//Act
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, 1, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(1);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().Contain(s => s.Success);
|
||||
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.Is<EpisodeParseResult>(d => d.Age != 100)), Times.Never());
|
||||
@ -165,7 +166,8 @@ public void processSearchResults_when_quality_is_not_needed_should_check_the_res
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(0);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().NotContain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Exactly(5));
|
||||
@ -188,7 +190,8 @@ public void processSearchResults_should_skip_if_series_is_null()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(0);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().NotContain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
@ -209,7 +212,8 @@ public void processSearchResults_should_skip_if_series_is_mismatched()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(0);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().NotContain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
@ -230,7 +234,8 @@ public void processSearchResults_should_skip_if_season_doesnt_match()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(0);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().NotContain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
@ -251,7 +256,8 @@ public void processSearchResults_should_skip_if_episodeNumber_doesnt_match()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(0);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().NotContain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
@ -277,7 +283,8 @@ public void processSearchResults_should_skip_if_any_episodeNumber_was_already_ad
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(1);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().Contain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
@ -310,7 +317,8 @@ public void processSearchResults_should_try_next_if_download_fails()
|
||||
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1);
|
||||
|
||||
//Assert
|
||||
result.Should().HaveCount(1);
|
||||
result.Should().HaveCount(parseResults.Count);
|
||||
result.Should().Contain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Exactly(2));
|
||||
|
@ -70,7 +70,7 @@ public void Start(ProgressNotification notification, int targetId, int secondary
|
||||
try
|
||||
{
|
||||
if (_isMonitoredEpisodeSpecification.IsSatisfiedBy(episodeParseResult) &&
|
||||
_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult) &&
|
||||
_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult) == ReportRejectionType.None &&
|
||||
_upgradeHistorySpecification.IsSatisfiedBy(episodeParseResult))
|
||||
{
|
||||
_downloadProvider.DownloadReport(episodeParseResult);
|
||||
|
@ -60,15 +60,15 @@ public virtual void Start(ProgressNotification notification, int targetId, int s
|
||||
//Perform a Partial Season Search
|
||||
var addedSeries = _searchProvider.PartialSeasonSearch(notification, targetId, secondaryTargetId);
|
||||
|
||||
addedSeries.Distinct().ToList().Sort();
|
||||
var episodeNumbers = episodes.Where(w => w.AirDate <= DateTime.Today.AddDays(1)).Select(s => s.EpisodeNumber).ToList();
|
||||
episodeNumbers.Sort();
|
||||
//addedSeries.Distinct().ToList().Sort();
|
||||
//var episodeNumbers = episodes.Where(w => w.AirDate <= DateTime.Today.AddDays(1)).Select(s => s.EpisodeNumber).ToList();
|
||||
//episodeNumbers.Sort();
|
||||
|
||||
if (addedSeries.SequenceEqual(episodeNumbers))
|
||||
return;
|
||||
//if (addedSeries.SequenceEqual(episodeNumbers))
|
||||
// return;
|
||||
|
||||
//Get the list of episodes that weren't downloaded
|
||||
var missingEpisodes = episodeNumbers.Except(addedSeries).ToList();
|
||||
////Get the list of episodes that weren't downloaded
|
||||
//var missingEpisodes = episodeNumbers.Except(addedSeries).ToList();
|
||||
|
||||
//TODO: do one by one check only when max number of feeds have been returned by the indexer
|
||||
//Only process episodes that is in missing episodes (To ensure we double check if the episode is available)
|
||||
|
21
NzbDrone.Core/Model/ReportRejectionType.cs
Normal file
21
NzbDrone.Core/Model/ReportRejectionType.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Linq;
|
||||
|
||||
namespace NzbDrone.Core.Model
|
||||
{
|
||||
public enum ReportRejectionType
|
||||
{
|
||||
None = 0,
|
||||
WrongSeries = 1,
|
||||
QualityNotWanted = 2,
|
||||
WrongSeason = 3,
|
||||
WrongEpisode = 3,
|
||||
Size = 3,
|
||||
Retention = 3,
|
||||
ExistingQualityIsEqualOrBetter = 4,
|
||||
Cutoff = 5,
|
||||
AlreadyInQueue = 6,
|
||||
DownloadClientFailure = 7,
|
||||
Skipped = 8,
|
||||
Failure,
|
||||
}
|
||||
}
|
@ -277,6 +277,7 @@
|
||||
<Compile Include="Providers\Indexer\NzbIndex.cs" />
|
||||
<Compile Include="Providers\Indexer\FileSharingTalk.cs" />
|
||||
<Compile Include="Providers\Indexer\Wombles.cs" />
|
||||
<Compile Include="Providers\SearchResultProvider.cs" />
|
||||
<Compile Include="Providers\SeasonProvider.cs" />
|
||||
<Compile Include="Jobs\RecentBacklogSearchJob.cs" />
|
||||
<Compile Include="Jobs\TrimLogsJob.cs" />
|
||||
@ -304,6 +305,9 @@
|
||||
<Compile Include="Providers\AnalyticsProvider.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="Repository\Search\SearchResultItem.cs" />
|
||||
<Compile Include="Repository\Search\SearchResult.cs" />
|
||||
<Compile Include="Model\ReportRejectionType.cs" />
|
||||
<Compile Include="Repository\Season.cs" />
|
||||
<Compile Include="Providers\AutoConfigureProvider.cs">
|
||||
<SubType>Code</SubType>
|
||||
|
@ -2,6 +2,7 @@
|
||||
using NLog;
|
||||
using Ninject;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
|
||||
namespace NzbDrone.Core.Providers.DecisionEngine
|
||||
{
|
||||
@ -30,16 +31,16 @@ public AllowedDownloadSpecification()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual bool IsSatisfiedBy(EpisodeParseResult subject)
|
||||
public virtual ReportRejectionType IsSatisfiedBy(EpisodeParseResult subject)
|
||||
{
|
||||
if (!_qualityAllowedByProfileSpecification.IsSatisfiedBy(subject)) return false;
|
||||
if (!_upgradeDiskSpecification.IsSatisfiedBy(subject)) return false;
|
||||
if (!_retentionSpecification.IsSatisfiedBy(subject)) return false;
|
||||
if (!_acceptableSizeSpecification.IsSatisfiedBy(subject)) return false;
|
||||
if (_alreadyInQueueSpecification.IsSatisfiedBy(subject)) return false;
|
||||
if (!_qualityAllowedByProfileSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.QualityNotWanted;
|
||||
if (!_upgradeDiskSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.ExistingQualityIsEqualOrBetter;
|
||||
if (!_retentionSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.Retention;
|
||||
if (!_acceptableSizeSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.Size;
|
||||
if (_alreadyInQueueSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.AlreadyInQueue;
|
||||
|
||||
logger.Debug("Episode {0} is needed", subject);
|
||||
return true;
|
||||
return ReportRejectionType.None;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@
|
||||
using NzbDrone.Core.Model.Notification;
|
||||
using NzbDrone.Core.Providers.DecisionEngine;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
|
||||
namespace NzbDrone.Core.Providers
|
||||
{
|
||||
@ -21,13 +22,15 @@ public class SearchProvider
|
||||
private readonly SceneMappingProvider _sceneMappingProvider;
|
||||
private readonly UpgradePossibleSpecification _upgradePossibleSpecification;
|
||||
private readonly AllowedDownloadSpecification _allowedDownloadSpecification;
|
||||
private readonly SearchResultProvider _searchResultProvider;
|
||||
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
[Inject]
|
||||
public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider downloadProvider, SeriesProvider seriesProvider,
|
||||
IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider,
|
||||
UpgradePossibleSpecification upgradePossibleSpecification, AllowedDownloadSpecification allowedDownloadSpecification)
|
||||
UpgradePossibleSpecification upgradePossibleSpecification, AllowedDownloadSpecification allowedDownloadSpecification,
|
||||
SearchResultProvider searchResultProvider)
|
||||
{
|
||||
_episodeProvider = episodeProvider;
|
||||
_downloadProvider = downloadProvider;
|
||||
@ -36,6 +39,7 @@ public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider download
|
||||
_sceneMappingProvider = sceneMappingProvider;
|
||||
_upgradePossibleSpecification = upgradePossibleSpecification;
|
||||
_allowedDownloadSpecification = allowedDownloadSpecification;
|
||||
_searchResultProvider = searchResultProvider;
|
||||
}
|
||||
|
||||
public SearchProvider()
|
||||
@ -44,13 +48,20 @@ public SearchProvider()
|
||||
|
||||
public virtual bool SeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber)
|
||||
{
|
||||
var searchResult = new SearchResult
|
||||
{
|
||||
SearchTime = DateTime.Now,
|
||||
SeriesId = seriesId,
|
||||
SeasonNumber = seasonNumber
|
||||
};
|
||||
|
||||
var series = _seriesProvider.GetSeries(seriesId);
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
Logger.Error("Unable to find an series {0} in database", seriesId);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//Return false if the series is a daily series (we only support individual episode searching
|
||||
if (series.IsDaily)
|
||||
@ -80,46 +91,45 @@ public virtual bool SeasonSearch(ProgressNotification notification, int seriesId
|
||||
e => e.EpisodeNumbers = episodeNumbers.ToList()
|
||||
);
|
||||
|
||||
var downloadedEpisodes = ProcessSearchResults(notification, reports, series, seasonNumber);
|
||||
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber);
|
||||
|
||||
downloadedEpisodes.Sort();
|
||||
episodeNumbers.ToList().Sort();
|
||||
|
||||
//Returns true if the list of downloaded episodes matches the list of episode numbers
|
||||
//(either a full season release was grabbed or all individual episodes)
|
||||
return (downloadedEpisodes.SequenceEqual(episodeNumbers));
|
||||
return (searchResult.SearchResultItems.Select(s => s.Success).Count() == episodeNumbers.Count);
|
||||
}
|
||||
|
||||
public virtual List<int> PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber)
|
||||
public virtual List<SearchResultItem> PartialSeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber)
|
||||
{
|
||||
//This method will search for episodes in a season in groups of 10 episodes S01E0, S01E1, S01E2, etc
|
||||
var searchResult = new SearchResult
|
||||
{
|
||||
SearchTime = DateTime.Now,
|
||||
SeriesId = seriesId,
|
||||
SeasonNumber = seasonNumber
|
||||
};
|
||||
|
||||
var series = _seriesProvider.GetSeries(seriesId);
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
Logger.Error("Unable to find an series {0} in database", seriesId);
|
||||
return new List<int>();
|
||||
return new List<SearchResultItem>();
|
||||
}
|
||||
|
||||
//Return empty list if the series is a daily series (we only support individual episode searching
|
||||
if (series.IsDaily)
|
||||
return new List<int>();
|
||||
return new List<SearchResultItem>();
|
||||
|
||||
notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber);
|
||||
|
||||
var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber);
|
||||
|
||||
var reports = PerformSearch(notification, series, seasonNumber, episodes);
|
||||
|
||||
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
|
||||
|
||||
if (reports.Count == 0)
|
||||
return new List<int>();
|
||||
return new List<SearchResultItem>();
|
||||
|
||||
notification.CurrentMessage = "Processing search results";
|
||||
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber);
|
||||
|
||||
return ProcessSearchResults(notification, reports, series, seasonNumber);
|
||||
_searchResultProvider.Add(searchResult);
|
||||
return searchResult.SearchResultItems;
|
||||
}
|
||||
|
||||
public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId)
|
||||
@ -136,7 +146,7 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode
|
||||
if (!_upgradePossibleSpecification.IsSatisfiedBy(episode))
|
||||
{
|
||||
Logger.Info("Search for {0} was aborted, file in disk meets or exceeds Profile's Cutoff", episode);
|
||||
notification.CurrentMessage = String.Format("Skipping search for {0}, file you have is already at cutoff", episode);
|
||||
notification.CurrentMessage = String.Format("Skipping search for {0}, the file you have is already at cutoff", episode);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -145,19 +155,41 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode
|
||||
if (episode.Series.IsDaily && !episode.AirDate.HasValue)
|
||||
{
|
||||
Logger.Warn("AirDate is not Valid for: {0}", episode);
|
||||
notification.CurrentMessage = String.Format("Search for {0} Failed, AirDate is invalid", episode);
|
||||
return false;
|
||||
}
|
||||
|
||||
var searchResult = new SearchResult
|
||||
{
|
||||
SearchTime = DateTime.Now,
|
||||
SeriesId = episode.Series.SeriesId
|
||||
};
|
||||
|
||||
var reports = PerformSearch(notification, episode.Series, episode.SeasonNumber, new List<Episode> { episode });
|
||||
|
||||
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
|
||||
notification.CurrentMessage = "Processing search results";
|
||||
|
||||
if (!episode.Series.IsDaily && ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber).Count == 1)
|
||||
return true;
|
||||
if (episode.Series.IsDaily)
|
||||
{
|
||||
searchResult.AirDate = episode.AirDate.Value;
|
||||
searchResult.SearchResultItems = ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value);
|
||||
|
||||
if (episode.Series.IsDaily && ProcessSearchResults(notification, reports, episode.Series, episode.AirDate.Value))
|
||||
_searchResultProvider.Add(searchResult);
|
||||
|
||||
if (searchResult.SearchResultItems.Any(r => r.Success))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!episode.Series.IsDaily)
|
||||
{
|
||||
searchResult.SeasonNumber = episode.SeasonNumber;
|
||||
searchResult.EpisodeId = episodeId;
|
||||
ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber);
|
||||
return true;
|
||||
}
|
||||
|
||||
Logger.Warn("Unable to find {0} in any of indexers.", episode);
|
||||
|
||||
@ -170,7 +202,6 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode
|
||||
notification.CurrentMessage = String.Format("Sorry, couldn't find you {0} in any of indexers.", episode);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -227,9 +258,10 @@ public List<EpisodeParseResult> PerformSearch(ProgressNotification notification,
|
||||
return reports;
|
||||
}
|
||||
|
||||
public List<int> ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, Series series, int seasonNumber, int? episodeNumber = null)
|
||||
public List<SearchResultItem> ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, Series series, int seasonNumber, int? episodeNumber = null)
|
||||
{
|
||||
var successes = new List<int>();
|
||||
var items = new List<SearchResultItem>();
|
||||
|
||||
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality).ThenBy(c => c.Age))
|
||||
{
|
||||
@ -237,6 +269,14 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
|
||||
{
|
||||
Logger.Trace("Analysing report " + episodeParseResult);
|
||||
|
||||
var item = new SearchResultItem
|
||||
{
|
||||
ReportTitle = episodeParseResult.OriginalString,
|
||||
NzbUrl = episodeParseResult.NzbUrl
|
||||
};
|
||||
|
||||
items.Add(item);
|
||||
|
||||
//Get the matching series
|
||||
episodeParseResult.Series = _seriesProvider.FindSeries(episodeParseResult.CleanTitle);
|
||||
|
||||
@ -244,6 +284,7 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
|
||||
if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId)
|
||||
{
|
||||
Logger.Trace("Unexpected series for search: {0}. Skipping.", episodeParseResult.CleanTitle);
|
||||
item.SearchError = ReportRejectionType.WrongSeries;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -251,6 +292,7 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
|
||||
if (episodeParseResult.SeasonNumber != seasonNumber)
|
||||
{
|
||||
Logger.Trace("Season number does not match searched season number, skipping.");
|
||||
item.SearchError = ReportRejectionType.WrongSeason;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -258,6 +300,7 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
|
||||
if (episodeNumber.HasValue && !episodeParseResult.EpisodeNumbers.Contains(episodeNumber.Value))
|
||||
{
|
||||
Logger.Trace("Searched episode number is not contained in post, skipping.");
|
||||
item.SearchError = ReportRejectionType.WrongEpisode;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -265,10 +308,12 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
|
||||
if (successes.Intersect(episodeParseResult.EpisodeNumbers).Any())
|
||||
{
|
||||
Logger.Trace("Episode has already been downloaded in this search, skipping.");
|
||||
item.SearchError = ReportRejectionType.Skipped;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult))
|
||||
var rejectionType = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult);
|
||||
if (rejectionType == ReportRejectionType.None)
|
||||
{
|
||||
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
|
||||
try
|
||||
@ -279,12 +324,18 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
|
||||
|
||||
//Add the list of episode numbers from this release
|
||||
successes.AddRange(episodeParseResult.EpisodeNumbers);
|
||||
item.Success = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SearchError = ReportRejectionType.DownloadClientFailure;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException("Unable to add report to download queue." + episodeParseResult, e);
|
||||
notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult);
|
||||
item.SearchError = ReportRejectionType.DownloadClientFailure;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -294,15 +345,32 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
|
||||
}
|
||||
}
|
||||
|
||||
return successes;
|
||||
return items;
|
||||
}
|
||||
|
||||
public bool ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, Series series, DateTime airDate)
|
||||
public List<SearchResultItem> ProcessSearchResults(ProgressNotification notification, IEnumerable<EpisodeParseResult> reports, Series series, DateTime airDate)
|
||||
{
|
||||
var items = new List<SearchResultItem>();
|
||||
var skip = false;
|
||||
|
||||
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality))
|
||||
{
|
||||
try
|
||||
{
|
||||
var item = new SearchResultItem
|
||||
{
|
||||
ReportTitle = episodeParseResult.OriginalString,
|
||||
NzbUrl = episodeParseResult.NzbUrl
|
||||
};
|
||||
|
||||
items.Add(item);
|
||||
|
||||
if (skip)
|
||||
{
|
||||
item.SearchError = ReportRejectionType.Skipped;
|
||||
continue;
|
||||
}
|
||||
|
||||
Logger.Trace("Analysing report " + episodeParseResult);
|
||||
|
||||
//Get the matching series
|
||||
@ -310,13 +378,20 @@ public bool ProcessSearchResults(ProgressNotification notification, IEnumerable<
|
||||
|
||||
//If series is null or doesn't match the series we're looking for return
|
||||
if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId)
|
||||
{
|
||||
item.SearchError = ReportRejectionType.WrongSeries;
|
||||
continue;
|
||||
}
|
||||
|
||||
//If parse result doesn't have an air date or it doesn't match passed in airdate, skip the report.
|
||||
if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value.Date != airDate.Date)
|
||||
{
|
||||
item.SearchError = ReportRejectionType.WrongEpisode;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult))
|
||||
var allowedDownload = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult);
|
||||
if (allowedDownload == ReportRejectionType.None)
|
||||
{
|
||||
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
|
||||
try
|
||||
@ -327,7 +402,12 @@ public bool ProcessSearchResults(ProgressNotification notification, IEnumerable<
|
||||
String.Format("{0} - {1} {2} Added to download queue",
|
||||
episodeParseResult.Series.Title, episodeParseResult.AirDate.Value.ToShortDateString(), episodeParseResult.Quality);
|
||||
|
||||
return true;
|
||||
item.Success = true;
|
||||
skip = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SearchError = ReportRejectionType.DownloadClientFailure;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@ -336,13 +416,18 @@ public bool ProcessSearchResults(ProgressNotification notification, IEnumerable<
|
||||
notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
item.SearchError = allowedDownload;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<int> GetEpisodeNumberPrefixes(IEnumerable<int> episodeNumbers)
|
||||
|
60
NzbDrone.Core/Providers/SearchResultProvider.cs
Normal file
60
NzbDrone.Core/Providers/SearchResultProvider.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using Ninject;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
using PetaPoco;
|
||||
|
||||
namespace NzbDrone.Core.Providers
|
||||
{
|
||||
public class SearchResultProvider
|
||||
{
|
||||
private readonly IDatabase _database;
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
[Inject]
|
||||
public SearchResultProvider(IDatabase database)
|
||||
{
|
||||
_database = database;
|
||||
}
|
||||
|
||||
public SearchResultProvider()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public virtual void Add(SearchResult searchResult)
|
||||
{
|
||||
logger.Trace("Adding new search result");
|
||||
var id = Convert.ToInt32(_database.Insert(searchResult));
|
||||
|
||||
searchResult.SearchResultItems.ForEach(s => s.Id = id);
|
||||
logger.Trace("Adding search result items");
|
||||
_database.InsertMany(searchResult.SearchResultItems);
|
||||
}
|
||||
|
||||
public virtual void Delete(int id)
|
||||
{
|
||||
logger.Trace("Deleting search result items attached to: {0}", id);
|
||||
_database.Execute("DELETE FROM SearchResultItems WHERE SearchResultId = @0", id);
|
||||
|
||||
logger.Trace("Deleting search result: {0}", id);
|
||||
_database.Delete<SearchResult>(id);
|
||||
}
|
||||
|
||||
public virtual List<SearchResult> AllSearchResults()
|
||||
{
|
||||
return _database.Fetch<SearchResult>();
|
||||
}
|
||||
|
||||
public virtual SearchResult GetSearchResult(int id)
|
||||
{
|
||||
var result = _database.Single<SearchResult>(id);
|
||||
result.SearchResultItems = _database.Fetch<SearchResultItem>("WHERE SearchResultId = @0", id);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
24
NzbDrone.Core/Repository/Search/SearchResult.cs
Normal file
24
NzbDrone.Core/Repository/Search/SearchResult.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Repository.Quality;
|
||||
using PetaPoco;
|
||||
|
||||
namespace NzbDrone.Core.Repository.Search
|
||||
{
|
||||
[PrimaryKey("Id", autoIncrement = true)]
|
||||
[TableName("SearchResults")]
|
||||
public class SearchResult
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int SeriesId { get; set; }
|
||||
public int? SeasonNumber { get; set; }
|
||||
public int? EpisodeId { get; set; }
|
||||
public DateTime? AirDate { get; set; }
|
||||
public DateTime SearchTime { get; set; }
|
||||
|
||||
[ResultColumn]
|
||||
public List<SearchResultItem> SearchResultItems { get; set; }
|
||||
}
|
||||
}
|
22
NzbDrone.Core/Repository/Search/SearchResultItem.cs
Normal file
22
NzbDrone.Core/Repository/Search/SearchResultItem.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Repository.Quality;
|
||||
using PetaPoco;
|
||||
|
||||
namespace NzbDrone.Core.Repository.Search
|
||||
{
|
||||
[PrimaryKey("Id", autoIncrement = true)]
|
||||
[TableName("SearchResultItems")]
|
||||
public class SearchResultItem
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int SearchResultId { get; set; }
|
||||
public string ReportTitle { get; set; }
|
||||
public string NzbUrl { get; set; }
|
||||
public string NzbInfoUrl { get; set; }
|
||||
public bool Success { get; set; }
|
||||
public ReportRejectionType SearchError { get; set; }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user