1
0
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:
Mark McDowall 2012-04-19 23:42:13 -07:00
parent b9e3d1a921
commit c7f8f57f77
13 changed files with 314 additions and 81 deletions

View File

@ -6,9 +6,11 @@
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Jobs; using NzbDrone.Core.Jobs;
using NzbDrone.Core.Model;
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers; using NzbDrone.Core.Providers;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Search;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common.AutoMoq; using NzbDrone.Test.Common.AutoMoq;
@ -39,10 +41,16 @@ public void SeasonSearch_full_season_success()
[Test] [Test]
public void SeasonSearch_partial_season_success() 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) var episodes = Builder<Episode>.CreateListOfSize(5)
.All() .All()
.With(e => e.SeriesId = 1)
.With(e => e.SeasonNumber = 1) .With(e => e.SeasonNumber = 1)
.With(e => e.SeriesId = 5)
.Build(); .Build();
var notification = new ProgressNotification("Season Search"); var notification = new ProgressNotification("Season Search");
@ -55,7 +63,7 @@ public void SeasonSearch_partial_season_success()
Mocker.GetMock<SearchProvider>() Mocker.GetMock<SearchProvider>()
.Setup(c => c.PartialSeasonSearch(notification, 1, 1)) .Setup(c => c.PartialSeasonSearch(notification, 1, 1))
.Returns(episodes.Select(e => e.EpisodeNumber).ToList()); .Returns(resultItems.ToList());
//Act //Act
Mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1); Mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
@ -88,7 +96,7 @@ public void SeasonSearch_partial_season_failure()
Mocker.GetMock<SearchProvider>() Mocker.GetMock<SearchProvider>()
.Setup(c => c.PartialSeasonSearch(notification, 1, 1)) .Setup(c => c.PartialSeasonSearch(notification, 1, 1))
.Returns(new List<int>{1}); .Returns(new List<SearchResultItem>{ new SearchResultItem{ Success = true }});
//Act //Act
Mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1); 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>() Mocker.GetMock<SearchProvider>()
.Setup(c => c.PartialSeasonSearch(notification, 1, 1)) .Setup(c => c.PartialSeasonSearch(notification, 1, 1))
.Returns(new List<int>()); .Returns(new List<SearchResultItem> { new SearchResultItem { Success = false, SearchError = ReportRejectionType.Size} });
//Act //Act

View File

@ -88,42 +88,42 @@ private void WithOverRetention()
[Test] [Test]
public void should_be_allowed_if_all_conditions_are_met() public void should_be_allowed_if_all_conditions_are_met()
{ {
spec.IsSatisfiedBy(parseResult).Should().BeTrue(); spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.None);
} }
[Test] [Test]
public void should_not_be_allowed_if_profile_is_not_allowed() public void should_not_be_allowed_if_profile_is_not_allowed()
{ {
WithProfileNotAllowed(); WithProfileNotAllowed();
spec.IsSatisfiedBy(parseResult).Should().BeFalse(); spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.QualityNotWanted);
} }
[Test] [Test]
public void should_not_be_allowed_if_size_is_not_allowed() public void should_not_be_allowed_if_size_is_not_allowed()
{ {
WithNotAcceptableSize(); WithNotAcceptableSize();
spec.IsSatisfiedBy(parseResult).Should().BeFalse(); spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.Size);
} }
[Test] [Test]
public void should_not_be_allowed_if_disk_is_not_upgrade() public void should_not_be_allowed_if_disk_is_not_upgrade()
{ {
WithNoDiskUpgrade(); WithNoDiskUpgrade();
spec.IsSatisfiedBy(parseResult).Should().BeFalse(); spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.ExistingQualityIsEqualOrBetter);
} }
[Test] [Test]
public void should_not_be_allowed_if_episode_is_already_in_queue() public void should_not_be_allowed_if_episode_is_already_in_queue()
{ {
WithEpisodeAlreadyInQueue(); WithEpisodeAlreadyInQueue();
spec.IsSatisfiedBy(parseResult).Should().BeFalse(); spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.AlreadyInQueue);
} }
[Test] [Test]
public void should_not_be_allowed_if_report_is_over_retention() public void should_not_be_allowed_if_report_is_over_retention()
{ {
WithOverRetention(); WithOverRetention();
spec.IsSatisfiedBy(parseResult).Should().BeFalse(); spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.Retention);
} }
[Test] [Test]
@ -134,7 +134,7 @@ public void should_not_be_allowed_if_none_of_conditions_are_met()
WithProfileNotAllowed(); WithProfileNotAllowed();
WithOverRetention(); WithOverRetention();
spec.IsSatisfiedBy(parseResult).Should().BeFalse(); spec.IsSatisfiedBy(parseResult).Should().Be(ReportRejectionType.QualityNotWanted);
} }
} }
} }

View File

@ -77,14 +77,14 @@ private void WithQualityNeeded()
{ {
Mocker.GetMock<AllowedDownloadSpecification>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())) .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true); .Returns(ReportRejectionType.None);
} }
private void WithQualityNotNeeded() private void WithQualityNotNeeded()
{ {
Mocker.GetMock<AllowedDownloadSpecification>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())) .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false); .Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter);
} }
[Test] [Test]
@ -103,13 +103,13 @@ public void processSearchResults_higher_quality_should_be_called_first()
Mocker.GetMock<AllowedDownloadSpecification>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p))) .Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p)))
.Returns(true); .Returns(ReportRejectionType.None);
//Act //Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert //Assert
result.Should().BeTrue(); result.Should().Contain(n => n.Success);
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Once()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert //Assert
result.Should().BeFalse(); result.Should().NotContain(n => n.Success);
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Exactly(5)); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert //Assert
result.Should().BeFalse(); result.Should().NotContain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert //Assert
result.Should().BeFalse(); result.Should().NotContain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert //Assert
result.Should().BeTrue(); result.Should().Contain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Once()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert //Assert
result.Should().BeTrue(); result.Should().Contain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Exactly(2)); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert //Assert
result.Should().BeFalse(); result.Should().NotContain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, DateTime.Today);
//Assert //Assert
result.Should().BeFalse(); result.Should().NotContain(n => n.Success);
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); Times.Never());

View File

@ -73,14 +73,14 @@ private void WithQualityNeeded()
{ {
Mocker.GetMock<AllowedDownloadSpecification>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())) .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(true); .Returns(ReportRejectionType.None);
} }
private void WithQualityNotNeeded() private void WithQualityNotNeeded()
{ {
Mocker.GetMock<AllowedDownloadSpecification>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())) .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
.Returns(false); .Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter);
} }
[Test] [Test]
@ -102,14 +102,14 @@ public void processSearchResults_higher_quality_should_be_called_first()
Mocker.GetMock<AllowedDownloadSpecification>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p))) .Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.QualityType == QualityTypes.Bluray1080p)))
.Returns(true); .Returns(ReportRejectionType.None);
//Act //Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
//Assert //Assert
result.Should().HaveCount(1); result.Should().HaveCount(parseResults.Count);
result.First().Should().Be(1); result.Should().Contain(s => s.Success);
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()), Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Once()); Times.Once());
@ -135,13 +135,14 @@ public void processSearchResults_newer_report_should_be_called_first()
WithSuccessfulDownload(); WithSuccessfulDownload();
Mocker.GetMock<AllowedDownloadSpecification>() Mocker.GetMock<AllowedDownloadSpecification>()
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(true); .Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(ReportRejectionType.None);
//Act //Act
var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, 1, 1); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(MockNotification, parseResults, _matchingSeries, 1, 1);
//Assert //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()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
//Assert //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>()), Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
Times.Exactly(5)); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
//Assert //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>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
//Assert //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>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
//Assert //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>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1, 1);
//Assert //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>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Never()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1);
//Assert //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>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Once()); 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); var result = Mocker.Resolve<SearchProvider>().ProcessSearchResults(new ProgressNotification("Test"), parseResults, _matchingSeries, 1);
//Assert //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>()), Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
Times.Exactly(2)); Times.Exactly(2));

View File

@ -70,7 +70,7 @@ public void Start(ProgressNotification notification, int targetId, int secondary
try try
{ {
if (_isMonitoredEpisodeSpecification.IsSatisfiedBy(episodeParseResult) && if (_isMonitoredEpisodeSpecification.IsSatisfiedBy(episodeParseResult) &&
_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult) && _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult) == ReportRejectionType.None &&
_upgradeHistorySpecification.IsSatisfiedBy(episodeParseResult)) _upgradeHistorySpecification.IsSatisfiedBy(episodeParseResult))
{ {
_downloadProvider.DownloadReport(episodeParseResult); _downloadProvider.DownloadReport(episodeParseResult);

View File

@ -60,15 +60,15 @@ public virtual void Start(ProgressNotification notification, int targetId, int s
//Perform a Partial Season Search //Perform a Partial Season Search
var addedSeries = _searchProvider.PartialSeasonSearch(notification, targetId, secondaryTargetId); var addedSeries = _searchProvider.PartialSeasonSearch(notification, targetId, secondaryTargetId);
addedSeries.Distinct().ToList().Sort(); //addedSeries.Distinct().ToList().Sort();
var episodeNumbers = episodes.Where(w => w.AirDate <= DateTime.Today.AddDays(1)).Select(s => s.EpisodeNumber).ToList(); //var episodeNumbers = episodes.Where(w => w.AirDate <= DateTime.Today.AddDays(1)).Select(s => s.EpisodeNumber).ToList();
episodeNumbers.Sort(); //episodeNumbers.Sort();
if (addedSeries.SequenceEqual(episodeNumbers)) //if (addedSeries.SequenceEqual(episodeNumbers))
return; // return;
//Get the list of episodes that weren't downloaded ////Get the list of episodes that weren't downloaded
var missingEpisodes = episodeNumbers.Except(addedSeries).ToList(); //var missingEpisodes = episodeNumbers.Except(addedSeries).ToList();
//TODO: do one by one check only when max number of feeds have been returned by the indexer //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) //Only process episodes that is in missing episodes (To ensure we double check if the episode is available)

View 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,
}
}

View File

@ -277,6 +277,7 @@
<Compile Include="Providers\Indexer\NzbIndex.cs" /> <Compile Include="Providers\Indexer\NzbIndex.cs" />
<Compile Include="Providers\Indexer\FileSharingTalk.cs" /> <Compile Include="Providers\Indexer\FileSharingTalk.cs" />
<Compile Include="Providers\Indexer\Wombles.cs" /> <Compile Include="Providers\Indexer\Wombles.cs" />
<Compile Include="Providers\SearchResultProvider.cs" />
<Compile Include="Providers\SeasonProvider.cs" /> <Compile Include="Providers\SeasonProvider.cs" />
<Compile Include="Jobs\RecentBacklogSearchJob.cs" /> <Compile Include="Jobs\RecentBacklogSearchJob.cs" />
<Compile Include="Jobs\TrimLogsJob.cs" /> <Compile Include="Jobs\TrimLogsJob.cs" />
@ -304,6 +305,9 @@
<Compile Include="Providers\AnalyticsProvider.cs"> <Compile Include="Providers\AnalyticsProvider.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </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="Repository\Season.cs" />
<Compile Include="Providers\AutoConfigureProvider.cs"> <Compile Include="Providers\AutoConfigureProvider.cs">
<SubType>Code</SubType> <SubType>Code</SubType>

View File

@ -2,6 +2,7 @@
using NLog; using NLog;
using Ninject; using Ninject;
using NzbDrone.Core.Model; using NzbDrone.Core.Model;
using NzbDrone.Core.Repository.Search;
namespace NzbDrone.Core.Providers.DecisionEngine 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 (!_qualityAllowedByProfileSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.QualityNotWanted;
if (!_upgradeDiskSpecification.IsSatisfiedBy(subject)) return false; if (!_upgradeDiskSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.ExistingQualityIsEqualOrBetter;
if (!_retentionSpecification.IsSatisfiedBy(subject)) return false; if (!_retentionSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.Retention;
if (!_acceptableSizeSpecification.IsSatisfiedBy(subject)) return false; if (!_acceptableSizeSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.Size;
if (_alreadyInQueueSpecification.IsSatisfiedBy(subject)) return false; if (_alreadyInQueueSpecification.IsSatisfiedBy(subject)) return ReportRejectionType.AlreadyInQueue;
logger.Debug("Episode {0} is needed", subject); logger.Debug("Episode {0} is needed", subject);
return true; return ReportRejectionType.None;
} }
} }
} }

View File

@ -8,6 +8,7 @@
using NzbDrone.Core.Model.Notification; using NzbDrone.Core.Model.Notification;
using NzbDrone.Core.Providers.DecisionEngine; using NzbDrone.Core.Providers.DecisionEngine;
using NzbDrone.Core.Repository; using NzbDrone.Core.Repository;
using NzbDrone.Core.Repository.Search;
namespace NzbDrone.Core.Providers namespace NzbDrone.Core.Providers
{ {
@ -21,13 +22,15 @@ public class SearchProvider
private readonly SceneMappingProvider _sceneMappingProvider; private readonly SceneMappingProvider _sceneMappingProvider;
private readonly UpgradePossibleSpecification _upgradePossibleSpecification; private readonly UpgradePossibleSpecification _upgradePossibleSpecification;
private readonly AllowedDownloadSpecification _allowedDownloadSpecification; private readonly AllowedDownloadSpecification _allowedDownloadSpecification;
private readonly SearchResultProvider _searchResultProvider;
private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
[Inject] [Inject]
public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider downloadProvider, SeriesProvider seriesProvider, public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider downloadProvider, SeriesProvider seriesProvider,
IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider, IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider,
UpgradePossibleSpecification upgradePossibleSpecification, AllowedDownloadSpecification allowedDownloadSpecification) UpgradePossibleSpecification upgradePossibleSpecification, AllowedDownloadSpecification allowedDownloadSpecification,
SearchResultProvider searchResultProvider)
{ {
_episodeProvider = episodeProvider; _episodeProvider = episodeProvider;
_downloadProvider = downloadProvider; _downloadProvider = downloadProvider;
@ -36,6 +39,7 @@ public SearchProvider(EpisodeProvider episodeProvider, DownloadProvider download
_sceneMappingProvider = sceneMappingProvider; _sceneMappingProvider = sceneMappingProvider;
_upgradePossibleSpecification = upgradePossibleSpecification; _upgradePossibleSpecification = upgradePossibleSpecification;
_allowedDownloadSpecification = allowedDownloadSpecification; _allowedDownloadSpecification = allowedDownloadSpecification;
_searchResultProvider = searchResultProvider;
} }
public SearchProvider() public SearchProvider()
@ -44,13 +48,20 @@ public SearchProvider()
public virtual bool SeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber) 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); var series = _seriesProvider.GetSeries(seriesId);
if (series == null) if (series == null)
{ {
Logger.Error("Unable to find an series {0} in database", seriesId); Logger.Error("Unable to find an series {0} in database", seriesId);
return false; return false;
} }
//Return false if the series is a daily series (we only support individual episode searching //Return false if the series is a daily series (we only support individual episode searching
if (series.IsDaily) if (series.IsDaily)
@ -80,46 +91,45 @@ public virtual bool SeasonSearch(ProgressNotification notification, int seriesId
e => e.EpisodeNumbers = episodeNumbers.ToList() e => e.EpisodeNumbers = episodeNumbers.ToList()
); );
var downloadedEpisodes = ProcessSearchResults(notification, reports, series, seasonNumber); searchResult.SearchResultItems = ProcessSearchResults(notification, reports, series, seasonNumber);
downloadedEpisodes.Sort(); return (searchResult.SearchResultItems.Select(s => s.Success).Count() == episodeNumbers.Count);
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));
} }
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); var series = _seriesProvider.GetSeries(seriesId);
if (series == null) if (series == null)
{ {
Logger.Error("Unable to find an series {0} in database", seriesId); 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 //Return empty list if the series is a daily series (we only support individual episode searching
if (series.IsDaily) if (series.IsDaily)
return new List<int>(); return new List<SearchResultItem>();
notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber); notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series.Title, seasonNumber);
var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber); var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber);
var reports = PerformSearch(notification, series, seasonNumber, episodes); var reports = PerformSearch(notification, series, seasonNumber, episodes);
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
if (reports.Count == 0) if (reports.Count == 0)
return new List<int>(); return new List<SearchResultItem>();
notification.CurrentMessage = "Processing search results"; 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) public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId)
@ -136,7 +146,7 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode
if (!_upgradePossibleSpecification.IsSatisfiedBy(episode)) if (!_upgradePossibleSpecification.IsSatisfiedBy(episode))
{ {
Logger.Info("Search for {0} was aborted, file in disk meets or exceeds Profile's Cutoff", 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; return false;
} }
@ -145,19 +155,41 @@ public virtual bool EpisodeSearch(ProgressNotification notification, int episode
if (episode.Series.IsDaily && !episode.AirDate.HasValue) if (episode.Series.IsDaily && !episode.AirDate.HasValue)
{ {
Logger.Warn("AirDate is not Valid for: {0}", episode); Logger.Warn("AirDate is not Valid for: {0}", episode);
notification.CurrentMessage = String.Format("Search for {0} Failed, AirDate is invalid", episode);
return false; 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 }); var reports = PerformSearch(notification, episode.Series, episode.SeasonNumber, new List<Episode> { episode });
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count); Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
notification.CurrentMessage = "Processing search results"; notification.CurrentMessage = "Processing search results";
if (!episode.Series.IsDaily && ProcessSearchResults(notification, reports, episode.Series, episode.SeasonNumber, episode.EpisodeNumber).Count == 1) if (episode.Series.IsDaily)
return true; {
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; return true;
}
Logger.Warn("Unable to find {0} in any of indexers.", episode); 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); notification.CurrentMessage = String.Format("Sorry, couldn't find you {0} in any of indexers.", episode);
} }
return false; return false;
} }
@ -227,9 +258,10 @@ public List<EpisodeParseResult> PerformSearch(ProgressNotification notification,
return reports; 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 successes = new List<int>();
var items = new List<SearchResultItem>();
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality).ThenBy(c => c.Age)) 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); Logger.Trace("Analysing report " + episodeParseResult);
var item = new SearchResultItem
{
ReportTitle = episodeParseResult.OriginalString,
NzbUrl = episodeParseResult.NzbUrl
};
items.Add(item);
//Get the matching series //Get the matching series
episodeParseResult.Series = _seriesProvider.FindSeries(episodeParseResult.CleanTitle); 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) if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId)
{ {
Logger.Trace("Unexpected series for search: {0}. Skipping.", episodeParseResult.CleanTitle); Logger.Trace("Unexpected series for search: {0}. Skipping.", episodeParseResult.CleanTitle);
item.SearchError = ReportRejectionType.WrongSeries;
continue; continue;
} }
@ -251,6 +292,7 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
if (episodeParseResult.SeasonNumber != seasonNumber) if (episodeParseResult.SeasonNumber != seasonNumber)
{ {
Logger.Trace("Season number does not match searched season number, skipping."); Logger.Trace("Season number does not match searched season number, skipping.");
item.SearchError = ReportRejectionType.WrongSeason;
continue; continue;
} }
@ -258,6 +300,7 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
if (episodeNumber.HasValue && !episodeParseResult.EpisodeNumbers.Contains(episodeNumber.Value)) if (episodeNumber.HasValue && !episodeParseResult.EpisodeNumbers.Contains(episodeNumber.Value))
{ {
Logger.Trace("Searched episode number is not contained in post, skipping."); Logger.Trace("Searched episode number is not contained in post, skipping.");
item.SearchError = ReportRejectionType.WrongEpisode;
continue; continue;
} }
@ -265,10 +308,12 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
if (successes.Intersect(episodeParseResult.EpisodeNumbers).Any()) if (successes.Intersect(episodeParseResult.EpisodeNumbers).Any())
{ {
Logger.Trace("Episode has already been downloaded in this search, skipping."); Logger.Trace("Episode has already been downloaded in this search, skipping.");
item.SearchError = ReportRejectionType.Skipped;
continue; continue;
} }
if (_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult)) var rejectionType = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult);
if (rejectionType == ReportRejectionType.None)
{ {
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
try try
@ -279,12 +324,18 @@ public List<int> ProcessSearchResults(ProgressNotification notification, IEnumer
//Add the list of episode numbers from this release //Add the list of episode numbers from this release
successes.AddRange(episodeParseResult.EpisodeNumbers); successes.AddRange(episodeParseResult.EpisodeNumbers);
item.Success = true;
}
else
{
item.SearchError = ReportRejectionType.DownloadClientFailure;
} }
} }
catch (Exception e) catch (Exception e)
{ {
Logger.ErrorException("Unable to add report to download queue." + episodeParseResult, 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); 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)) foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality))
{ {
try 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); Logger.Trace("Analysing report " + episodeParseResult);
//Get the matching series //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 series is null or doesn't match the series we're looking for return
if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId) if (episodeParseResult.Series == null || episodeParseResult.Series.SeriesId != series.SeriesId)
{
item.SearchError = ReportRejectionType.WrongSeries;
continue; continue;
}
//If parse result doesn't have an air date or it doesn't match passed in airdate, skip the report. //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) if (!episodeParseResult.AirDate.HasValue || episodeParseResult.AirDate.Value.Date != airDate.Date)
{
item.SearchError = ReportRejectionType.WrongEpisode;
continue; continue;
}
if (_allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult)) var allowedDownload = _allowedDownloadSpecification.IsSatisfiedBy(episodeParseResult);
if (allowedDownload == ReportRejectionType.None)
{ {
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult); Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
try try
@ -327,7 +402,12 @@ public bool ProcessSearchResults(ProgressNotification notification, IEnumerable<
String.Format("{0} - {1} {2} Added to download queue", String.Format("{0} - {1} {2} Added to download queue",
episodeParseResult.Series.Title, episodeParseResult.AirDate.Value.ToShortDateString(), episodeParseResult.Quality); episodeParseResult.Series.Title, episodeParseResult.AirDate.Value.ToShortDateString(), episodeParseResult.Quality);
return true; item.Success = true;
skip = true;
}
else
{
item.SearchError = ReportRejectionType.DownloadClientFailure;
} }
} }
catch (Exception e) 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); notification.CurrentMessage = String.Format("Unable to add report to download queue. {0}", episodeParseResult);
} }
} }
else
{
item.SearchError = allowedDownload;
}
} }
catch (Exception e) catch (Exception e)
{ {
Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, 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) private List<int> GetEpisodeNumberPrefixes(IEnumerable<int> episodeNumbers)

View 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;
}
}
}

View 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; }
}
}

View 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; }
}
}