mirror of
https://github.com/Sonarr/Sonarr.git
synced 2025-03-03 15:12:13 +02:00
Full Season searching added (Single NZB). SearchProvider added to perform both Season and Episode searching, triggered via jobs.
Tests added for season searching.
This commit is contained in:
parent
07a4c94032
commit
485f618e02
@ -7,6 +7,7 @@ using AutoMoq;
|
|||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using NzbDrone.Core.Model;
|
using NzbDrone.Core.Model;
|
||||||
|
using NzbDrone.Core.Model.Search;
|
||||||
using NzbDrone.Core.Providers;
|
using NzbDrone.Core.Providers;
|
||||||
using NzbDrone.Core.Providers.Core;
|
using NzbDrone.Core.Providers.Core;
|
||||||
using NzbDrone.Core.Providers.Indexer;
|
using NzbDrone.Core.Providers.Indexer;
|
||||||
@ -73,7 +74,7 @@ namespace NzbDrone.Core.Test
|
|||||||
get { return new[] { "www.google.com" }; }
|
get { return new[] { "www.google.com" }; }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IList<string> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber)
|
protected override IList<string> GetSearchUrls(SearchModel searchModel)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@ -112,7 +113,7 @@ namespace NzbDrone.Core.Test
|
|||||||
get { return new[] { "http://rss.nzbmatrix.com/rss.php?cat=TV" }; }
|
get { return new[] { "http://rss.nzbmatrix.com/rss.php?cat=TV" }; }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IList<string> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber)
|
protected override IList<string> GetSearchUrls(SearchModel searchModel)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@ -135,14 +136,12 @@ namespace NzbDrone.Core.Test
|
|||||||
get { return "Custom parser"; }
|
get { return "Custom parser"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
protected override string[] Urls
|
protected override string[] Urls
|
||||||
{
|
{
|
||||||
get { return new[] { "http://www.google.com" }; }
|
get { return new[] { "http://www.google.com" }; }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IList<string> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber)
|
protected override IList<string> GetSearchUrls(SearchModel searchModel)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -88,8 +88,9 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="SearchJobTest.cs" />
|
||||||
<Compile Include="SeriesSearchJobTest.cs" />
|
<Compile Include="SeriesSearchJobTest.cs" />
|
||||||
<Compile Include="SeasonSearchJobTest.cs" />
|
<Compile Include="SearchProviderTest_Season.cs" />
|
||||||
<Compile Include="EventClientProviderTest.cs" />
|
<Compile Include="EventClientProviderTest.cs" />
|
||||||
<Compile Include="CentralDispatchTest.cs" />
|
<Compile Include="CentralDispatchTest.cs" />
|
||||||
<Compile Include="XbmcProviderTest.cs" />
|
<Compile Include="XbmcProviderTest.cs" />
|
||||||
@ -104,7 +105,7 @@
|
|||||||
<Compile Include="Framework\TestBase.cs" />
|
<Compile Include="Framework\TestBase.cs" />
|
||||||
<Compile Include="InventoryProvider_IsMonitoredTest.cs" />
|
<Compile Include="InventoryProvider_IsMonitoredTest.cs" />
|
||||||
<Compile Include="DownloadProviderTest.cs" />
|
<Compile Include="DownloadProviderTest.cs" />
|
||||||
<Compile Include="EpisodeSearchJobTest.cs" />
|
<Compile Include="SearchProviderTest_Episode.cs" />
|
||||||
<Compile Include="EpisodeStatusTest.cs" />
|
<Compile Include="EpisodeStatusTest.cs" />
|
||||||
<Compile Include="ImportNewSeriesJobTest.cs" />
|
<Compile Include="ImportNewSeriesJobTest.cs" />
|
||||||
<Compile Include="DiskScanJobTest.cs" />
|
<Compile Include="DiskScanJobTest.cs" />
|
||||||
|
37
NzbDrone.Core.Test/SearchJobTest.cs
Normal file
37
NzbDrone.Core.Test/SearchJobTest.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using AutoMoq;
|
||||||
|
using Moq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Model.Notification;
|
||||||
|
using NzbDrone.Core.Providers;
|
||||||
|
using NzbDrone.Core.Providers.Jobs;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
public class SearchJobTest
|
||||||
|
{
|
||||||
|
[TestCase(0)]
|
||||||
|
[TestCase(-1)]
|
||||||
|
[TestCase(-100)]
|
||||||
|
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||||
|
public void start_target_id_less_than_0_throws_exception(int target)
|
||||||
|
{
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), target, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestCase(0)]
|
||||||
|
[TestCase(-1)]
|
||||||
|
[TestCase(-100)]
|
||||||
|
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
||||||
|
public void start_secondary_target_id_less_than_0_throws_exception(int target)
|
||||||
|
{
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
mocker.Resolve<SeasonSearchJob>().Start(new ProgressNotification("Test"), 0, target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,7 +19,7 @@ namespace NzbDrone.Core.Test
|
|||||||
{
|
{
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
// ReSharper disable InconsistentNaming
|
// ReSharper disable InconsistentNaming
|
||||||
public class EpisodeSearchJobTest : TestBase
|
public class SearchProviderTest_Episode : TestBase
|
||||||
{
|
{
|
||||||
[Test]
|
[Test]
|
||||||
public void processResults_ParseResult_should_return_after_match()
|
public void processResults_ParseResult_should_return_after_match()
|
||||||
@ -40,7 +40,7 @@ namespace NzbDrone.Core.Test
|
|||||||
|
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
|
mocker.Resolve<SearchProvider>().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
mocker.VerifyAllMocks();
|
mocker.VerifyAllMocks();
|
||||||
@ -76,7 +76,7 @@ namespace NzbDrone.Core.Test
|
|||||||
.Returns(true);
|
.Returns(true);
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
|
mocker.Resolve<SearchProvider>().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
mocker.VerifyAllMocks();
|
mocker.VerifyAllMocks();
|
||||||
@ -109,7 +109,7 @@ namespace NzbDrone.Core.Test
|
|||||||
|
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
|
mocker.Resolve<SearchProvider>().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
mocker.VerifyAllMocks();
|
mocker.VerifyAllMocks();
|
||||||
@ -134,7 +134,7 @@ namespace NzbDrone.Core.Test
|
|||||||
.Setup(c => c.IsQualityNeeded(It.IsAny<EpisodeParseResult>())).Returns(false);
|
.Setup(c => c.IsQualityNeeded(It.IsAny<EpisodeParseResult>())).Returns(false);
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
|
mocker.Resolve<SearchProvider>().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
mocker.VerifyAllMocks();
|
mocker.VerifyAllMocks();
|
||||||
@ -158,7 +158,7 @@ namespace NzbDrone.Core.Test
|
|||||||
.Setup(c => c.IsQualityNeeded(It.IsAny<EpisodeParseResult>())).Throws(new Exception());
|
.Setup(c => c.IsQualityNeeded(It.IsAny<EpisodeParseResult>())).Throws(new Exception());
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
|
mocker.Resolve<SearchProvider>().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
mocker.VerifyAllMocks();
|
mocker.VerifyAllMocks();
|
||||||
@ -185,7 +185,7 @@ namespace NzbDrone.Core.Test
|
|||||||
.Setup(c => c.DownloadReport(It.IsAny<EpisodeParseResult>())).Throws(new Exception());
|
.Setup(c => c.DownloadReport(It.IsAny<EpisodeParseResult>())).Throws(new Exception());
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().ProcessResults(new ProgressNotification("Test"), episode, parseResults);
|
mocker.Resolve<SearchProvider>().ProcessEpisodeSearchResults(new ProgressNotification("Test"), episode, parseResults);
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
mocker.VerifyAllMocks();
|
mocker.VerifyAllMocks();
|
||||||
@ -200,25 +200,7 @@ namespace NzbDrone.Core.Test
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
[TestCase(0)]
|
|
||||||
[TestCase(-1)]
|
|
||||||
[TestCase(-100)]
|
|
||||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
|
||||||
public void start_target_id_less_than_0_throws_exception(int target)
|
|
||||||
{
|
|
||||||
var mocker = new AutoMoqer(MockBehavior.Strict);
|
|
||||||
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), target, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestCase(0)]
|
|
||||||
[TestCase(-1)]
|
|
||||||
[TestCase(-100)]
|
|
||||||
[ExpectedException(typeof(ArgumentOutOfRangeException))]
|
|
||||||
public void start_secondary_target_id_less_than_0_throws_exception(int target)
|
|
||||||
{
|
|
||||||
var mocker = new AutoMoqer(MockBehavior.Strict);
|
|
||||||
mocker.Resolve<SeasonSearchJob>().Start(new ProgressNotification("Test"), 0, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void start_should_search_all_providers()
|
public void start_should_search_all_providers()
|
||||||
@ -237,12 +219,10 @@ namespace NzbDrone.Core.Test
|
|||||||
.Setup(c => c.GetEpisode(episode.EpisodeId))
|
.Setup(c => c.GetEpisode(episode.EpisodeId))
|
||||||
.Returns(episode);
|
.Returns(episode);
|
||||||
|
|
||||||
|
|
||||||
var indexer1 = new Mock<IndexerBase>();
|
var indexer1 = new Mock<IndexerBase>();
|
||||||
indexer1.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
|
indexer1.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
|
||||||
.Returns(parseResults).Verifiable();
|
.Returns(parseResults).Verifiable();
|
||||||
|
|
||||||
|
|
||||||
var indexer2 = new Mock<IndexerBase>();
|
var indexer2 = new Mock<IndexerBase>();
|
||||||
indexer2.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
|
indexer2.Setup(c => c.FetchEpisode(episode.Series.Title, episode.SeasonNumber, episode.EpisodeNumber))
|
||||||
.Returns(parseResults).Verifiable();
|
.Returns(parseResults).Verifiable();
|
||||||
@ -260,7 +240,7 @@ namespace NzbDrone.Core.Test
|
|||||||
.Setup(s => s.GetSceneName(It.IsAny<int>())).Returns("");
|
.Setup(s => s.GetSceneName(It.IsAny<int>())).Returns("");
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId, 0);
|
mocker.Resolve<SearchProvider>().EpisodeSearch(new ProgressNotification("Test"), episode.EpisodeId);
|
||||||
|
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
@ -311,7 +291,7 @@ namespace NzbDrone.Core.Test
|
|||||||
.Setup(s => s.GetSceneName(71256)).Returns("The Daily Show");
|
.Setup(s => s.GetSceneName(71256)).Returns("The Daily Show");
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId, 0);
|
mocker.Resolve<SearchProvider>().EpisodeSearch(new ProgressNotification("Test"), episode.EpisodeId);
|
||||||
|
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
@ -368,7 +348,7 @@ namespace NzbDrone.Core.Test
|
|||||||
.Setup(s => s.GetSceneName(It.IsAny<int>())).Returns("");
|
.Setup(s => s.GetSceneName(It.IsAny<int>())).Returns("");
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), episode.EpisodeId, 0);
|
mocker.Resolve<SearchProvider>().EpisodeSearch(new ProgressNotification("Test"), episode.EpisodeId);
|
||||||
|
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
@ -394,7 +374,7 @@ namespace NzbDrone.Core.Test
|
|||||||
.Returns<Episode>(null);
|
.Returns<Episode>(null);
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
mocker.Resolve<EpisodeSearchJob>().Start(new ProgressNotification("Test"), 12, 0);
|
mocker.Resolve<SearchProvider>().EpisodeSearch(new ProgressNotification("Test"), 12);
|
||||||
|
|
||||||
|
|
||||||
//Assert
|
//Assert
|
223
NzbDrone.Core.Test/SearchProviderTest_Season.cs
Normal file
223
NzbDrone.Core.Test/SearchProviderTest_Season.cs
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using AutoMoq;
|
||||||
|
using FizzWare.NBuilder;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Moq;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Core.Model;
|
||||||
|
using NzbDrone.Core.Model.Notification;
|
||||||
|
using NzbDrone.Core.Providers;
|
||||||
|
using NzbDrone.Core.Providers.Indexer;
|
||||||
|
using NzbDrone.Core.Providers.Jobs;
|
||||||
|
using NzbDrone.Core.Repository;
|
||||||
|
using NzbDrone.Core.Repository.Quality;
|
||||||
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Test
|
||||||
|
{
|
||||||
|
[TestFixture]
|
||||||
|
// ReSharper disable InconsistentNaming
|
||||||
|
public class SearchProviderTest_Season : TestBase
|
||||||
|
{
|
||||||
|
[Test]
|
||||||
|
public void SeasonSearch_season_success()
|
||||||
|
{
|
||||||
|
var series = Builder<Series>.CreateNew()
|
||||||
|
.With(s => s.SeriesId = 1)
|
||||||
|
.With(s => s.Title = "Title1")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var episodes = Builder<Episode>.CreateListOfSize(5)
|
||||||
|
.WhereAll()
|
||||||
|
.Have(e => e.Series = series)
|
||||||
|
.Have(e => e.SeriesId = 1)
|
||||||
|
.Have(e => e.SeasonNumber = 1)
|
||||||
|
.Have(e => e.Ignored = false)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(4)
|
||||||
|
.WhereTheFirst(1)
|
||||||
|
.Has(p => p.CleanTitle = "title")
|
||||||
|
.Has(p => p.SeasonNumber = 1)
|
||||||
|
.Has(p => p.FullSeason = true)
|
||||||
|
.Has(p => p.EpisodeNumbers = null)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
|
||||||
|
var notification = new ProgressNotification("Season Search");
|
||||||
|
|
||||||
|
var indexer1 = new Mock<IndexerBase>();
|
||||||
|
indexer1.Setup(c => c.FetchSeason(episodes[0].Series.Title, episodes[0].SeasonNumber))
|
||||||
|
.Returns(parseResults).Verifiable();
|
||||||
|
|
||||||
|
var indexer2 = new Mock<IndexerBase>();
|
||||||
|
indexer2.Setup(c => c.FetchSeason(episodes[0].Series.Title, episodes[0].SeasonNumber))
|
||||||
|
.Returns(parseResults).Verifiable();
|
||||||
|
|
||||||
|
var indexers = new List<IndexerBase> { indexer1.Object, indexer2.Object };
|
||||||
|
|
||||||
|
mocker.GetMock<IndexerProvider>()
|
||||||
|
.Setup(c => c.GetEnabledIndexers())
|
||||||
|
.Returns(indexers);
|
||||||
|
|
||||||
|
mocker.GetMock<SeriesProvider>()
|
||||||
|
.Setup(c => c.GetSeries(1)).Returns(series);
|
||||||
|
|
||||||
|
mocker.GetMock<EpisodeProvider>()
|
||||||
|
.Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes);
|
||||||
|
|
||||||
|
mocker.GetMock<SceneMappingProvider>()
|
||||||
|
.Setup(s => s.GetSceneName(1)).Returns(String.Empty);
|
||||||
|
|
||||||
|
mocker.GetMock<InventoryProvider>()
|
||||||
|
.Setup(s => s.IsQualityNeeded(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||||
|
|
||||||
|
mocker.GetMock<DownloadProvider>()
|
||||||
|
.Setup(s => s.DownloadReport(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mocker.Resolve<SearchProvider>().SeasonSearch(notification, 1, 1);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
mocker.VerifyAllMocks();
|
||||||
|
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
||||||
|
Times.Never());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void SeasonSearch_season_failure()
|
||||||
|
{
|
||||||
|
var series = Builder<Series>.CreateNew()
|
||||||
|
.With(s => s.SeriesId = 1)
|
||||||
|
.With(s => s.Title = "Title1")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var episodes = Builder<Episode>.CreateListOfSize(5)
|
||||||
|
.WhereAll()
|
||||||
|
.Have(e => e.Series = series)
|
||||||
|
.Have(e => e.SeriesId = 1)
|
||||||
|
.Have(e => e.SeasonNumber = 1)
|
||||||
|
.Have(e => e.Ignored = false)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(4)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
|
||||||
|
var notification = new ProgressNotification("Season Search");
|
||||||
|
|
||||||
|
var indexer1 = new Mock<IndexerBase>();
|
||||||
|
indexer1.Setup(c => c.FetchSeason(episodes[0].Series.Title, episodes[0].SeasonNumber))
|
||||||
|
.Returns(parseResults).Verifiable();
|
||||||
|
|
||||||
|
var indexer2 = new Mock<IndexerBase>();
|
||||||
|
indexer2.Setup(c => c.FetchSeason(episodes[0].Series.Title, episodes[0].SeasonNumber))
|
||||||
|
.Returns(parseResults).Verifiable();
|
||||||
|
|
||||||
|
var indexers = new List<IndexerBase> { indexer1.Object, indexer2.Object };
|
||||||
|
|
||||||
|
mocker.GetMock<IndexerProvider>()
|
||||||
|
.Setup(c => c.GetEnabledIndexers())
|
||||||
|
.Returns(indexers);
|
||||||
|
|
||||||
|
mocker.GetMock<SeriesProvider>()
|
||||||
|
.Setup(c => c.GetSeries(1)).Returns(series);
|
||||||
|
|
||||||
|
mocker.GetMock<EpisodeProvider>()
|
||||||
|
.Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes);
|
||||||
|
|
||||||
|
mocker.GetMock<SceneMappingProvider>()
|
||||||
|
.Setup(s => s.GetSceneName(1)).Returns(String.Empty);
|
||||||
|
|
||||||
|
//mocker.GetMock<InventoryProvider>()
|
||||||
|
// .Setup(s => s.IsQualityNeeded(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||||
|
|
||||||
|
//mocker.GetMock<DownloadProvider>()
|
||||||
|
// .Setup(s => s.DownloadReport(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mocker.Resolve<SearchProvider>().SeasonSearch(notification, 1, 1);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
ExceptionVerification.ExcpectedWarns(1);
|
||||||
|
mocker.VerifyAllMocks();
|
||||||
|
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
||||||
|
Times.Never());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ProcessSeasonSearchResults_success()
|
||||||
|
{
|
||||||
|
var series = Builder<Series>.CreateNew()
|
||||||
|
.With(s => s.SeriesId = 1)
|
||||||
|
.With(s => s.Title = "Title1")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(4)
|
||||||
|
.WhereTheFirst(1)
|
||||||
|
.Has(p => p.CleanTitle = "title")
|
||||||
|
.Has(p => p.SeasonNumber = 1)
|
||||||
|
.Has(p => p.FullSeason = true)
|
||||||
|
.Has(p => p.EpisodeNumbers = null)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
|
||||||
|
var notification = new ProgressNotification("Season Search");
|
||||||
|
|
||||||
|
mocker.GetMock<InventoryProvider>()
|
||||||
|
.Setup(s => s.IsQualityNeeded(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||||
|
|
||||||
|
mocker.GetMock<DownloadProvider>()
|
||||||
|
.Setup(s => s.DownloadReport(It.IsAny<EpisodeParseResult>())).Returns(true);
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mocker.Resolve<SearchProvider>().ProcessSeasonSearchResults(notification, series, 1, parseResults);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
mocker.VerifyAllMocks();
|
||||||
|
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
||||||
|
Times.Never());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ProcessSeasonSearchResults_failure()
|
||||||
|
{
|
||||||
|
var series = Builder<Series>.CreateNew()
|
||||||
|
.With(s => s.SeriesId = 1)
|
||||||
|
.With(s => s.Title = "Title1")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(4)
|
||||||
|
.WhereTheFirst(1)
|
||||||
|
.Has(p => p.CleanTitle = "title")
|
||||||
|
.Has(p => p.SeasonNumber = 1)
|
||||||
|
.Has(p => p.FullSeason = true)
|
||||||
|
.Has(p => p.EpisodeNumbers = null)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
var mocker = new AutoMoqer(MockBehavior.Strict);
|
||||||
|
|
||||||
|
var notification = new ProgressNotification("Season Search");
|
||||||
|
|
||||||
|
mocker.GetMock<InventoryProvider>()
|
||||||
|
.Setup(s => s.IsQualityNeeded(It.IsAny<EpisodeParseResult>())).Returns(false);
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mocker.Resolve<SearchProvider>().ProcessSeasonSearchResults(notification, series, 1, parseResults);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
ExceptionVerification.ExcpectedWarns(1);
|
||||||
|
mocker.VerifyAllMocks();
|
||||||
|
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
||||||
|
Times.Never());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,105 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using AutoMoq;
|
|
||||||
using FizzWare.NBuilder;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Moq;
|
|
||||||
using NUnit.Framework;
|
|
||||||
using NzbDrone.Core.Model;
|
|
||||||
using NzbDrone.Core.Model.Notification;
|
|
||||||
using NzbDrone.Core.Providers;
|
|
||||||
using NzbDrone.Core.Providers.Indexer;
|
|
||||||
using NzbDrone.Core.Providers.Jobs;
|
|
||||||
using NzbDrone.Core.Repository;
|
|
||||||
using NzbDrone.Core.Repository.Quality;
|
|
||||||
using NzbDrone.Core.Test.Framework;
|
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test
|
|
||||||
{
|
|
||||||
[TestFixture]
|
|
||||||
// ReSharper disable InconsistentNaming
|
|
||||||
public class SeasonSearchJobTest : TestBase
|
|
||||||
{
|
|
||||||
[Test]
|
|
||||||
public void SeasonSearch_success()
|
|
||||||
{
|
|
||||||
var episodes = Builder<Episode>.CreateListOfSize(5)
|
|
||||||
.WhereAll()
|
|
||||||
.Have(e => e.SeriesId = 1)
|
|
||||||
.Have(e => e.SeasonNumber = 1)
|
|
||||||
.Have(e => e.Ignored = false)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var mocker = new AutoMoqer(MockBehavior.Strict);
|
|
||||||
|
|
||||||
var notification = new ProgressNotification("Season Search");
|
|
||||||
|
|
||||||
mocker.GetMock<EpisodeProvider>()
|
|
||||||
.Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes);
|
|
||||||
|
|
||||||
mocker.GetMock<EpisodeSearchJob>()
|
|
||||||
.Setup(c => c.Start(notification, It.IsAny<int>(), 0)).Verifiable();
|
|
||||||
|
|
||||||
//Act
|
|
||||||
mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
mocker.VerifyAllMocks();
|
|
||||||
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
|
||||||
Times.Exactly(episodes.Count));
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void SeasonSearch_no_episodes()
|
|
||||||
{
|
|
||||||
var mocker = new AutoMoqer(MockBehavior.Strict);
|
|
||||||
var notification = new ProgressNotification("Season Search");
|
|
||||||
List<Episode> nullList = null;
|
|
||||||
|
|
||||||
mocker.GetMock<EpisodeProvider>()
|
|
||||||
.Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(nullList);
|
|
||||||
|
|
||||||
//Act
|
|
||||||
mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
mocker.VerifyAllMocks();
|
|
||||||
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
|
||||||
Times.Never());
|
|
||||||
ExceptionVerification.ExcpectedWarns(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Test]
|
|
||||||
public void SeasonSearch_skip_ignored()
|
|
||||||
{
|
|
||||||
var episodes = Builder<Episode>.CreateListOfSize(10)
|
|
||||||
.WhereAll()
|
|
||||||
.Have(e => e.SeriesId = 1)
|
|
||||||
.Have(e => e.SeasonNumber = 1)
|
|
||||||
.WhereTheFirst(5)
|
|
||||||
.Have(e => e.Ignored = false)
|
|
||||||
.AndTheRemaining()
|
|
||||||
.Have(e => e.Ignored = true)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
var mocker = new AutoMoqer(MockBehavior.Strict);
|
|
||||||
|
|
||||||
var notification = new ProgressNotification("Season Search");
|
|
||||||
|
|
||||||
mocker.GetMock<EpisodeProvider>()
|
|
||||||
.Setup(c => c.GetEpisodesBySeason(1, 1)).Returns(episodes);
|
|
||||||
|
|
||||||
mocker.GetMock<EpisodeSearchJob>()
|
|
||||||
.Setup(c => c.Start(notification, It.IsAny<int>(), 0)).Verifiable();
|
|
||||||
|
|
||||||
//Act
|
|
||||||
mocker.Resolve<SeasonSearchJob>().Start(notification, 1, 1);
|
|
||||||
|
|
||||||
//Assert
|
|
||||||
mocker.VerifyAllMocks();
|
|
||||||
mocker.GetMock<EpisodeSearchJob>().Verify(c => c.Start(notification, It.IsAny<int>(), 0),
|
|
||||||
Times.Exactly(5));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
16
NzbDrone.Core/Model/Search/SearchModel.cs
Normal file
16
NzbDrone.Core/Model/Search/SearchModel.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Model.Search
|
||||||
|
{
|
||||||
|
public class SearchModel
|
||||||
|
{
|
||||||
|
public string SeriesTitle { get; set; }
|
||||||
|
public int EpisodeNumber { get; set; }
|
||||||
|
public int SeasonNumber { get; set; }
|
||||||
|
public DateTime AirDate { get; set; }
|
||||||
|
public SearchType SearchType { get; set; }
|
||||||
|
}
|
||||||
|
}
|
14
NzbDrone.Core/Model/Search/SearchType.cs
Normal file
14
NzbDrone.Core/Model/Search/SearchType.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Model.Search
|
||||||
|
{
|
||||||
|
public enum SearchType
|
||||||
|
{
|
||||||
|
EpisodeSearch = 0,
|
||||||
|
DailySearch = 1,
|
||||||
|
SeasonSearch = 2
|
||||||
|
}
|
||||||
|
}
|
@ -193,6 +193,8 @@
|
|||||||
<Compile Include="Model\Quality.cs" />
|
<Compile Include="Model\Quality.cs" />
|
||||||
<Compile Include="Model\SabnzbdCategoryModel.cs" />
|
<Compile Include="Model\SabnzbdCategoryModel.cs" />
|
||||||
<Compile Include="Model\SabnzbdInfoModel.cs" />
|
<Compile Include="Model\SabnzbdInfoModel.cs" />
|
||||||
|
<Compile Include="Model\Search\SearchModel.cs" />
|
||||||
|
<Compile Include="Model\Search\SearchType.cs" />
|
||||||
<Compile Include="Model\Xbmc\ActionType.cs" />
|
<Compile Include="Model\Xbmc\ActionType.cs" />
|
||||||
<Compile Include="Model\Xbmc\ActivePlayersResult.cs" />
|
<Compile Include="Model\Xbmc\ActivePlayersResult.cs" />
|
||||||
<Compile Include="Model\Xbmc\ErrorResult.cs" />
|
<Compile Include="Model\Xbmc\ErrorResult.cs" />
|
||||||
@ -202,6 +204,7 @@
|
|||||||
<Compile Include="Providers\Jobs\SeriesSearchJob.cs" />
|
<Compile Include="Providers\Jobs\SeriesSearchJob.cs" />
|
||||||
<Compile Include="Providers\Jobs\RenameSeasonJob.cs" />
|
<Compile Include="Providers\Jobs\RenameSeasonJob.cs" />
|
||||||
<Compile Include="Providers\Jobs\SeasonSearchJob.cs" />
|
<Compile Include="Providers\Jobs\SeasonSearchJob.cs" />
|
||||||
|
<Compile Include="Providers\SearchProvider.cs" />
|
||||||
<Compile Include="Providers\Xbmc\ResourceManager.cs" />
|
<Compile Include="Providers\Xbmc\ResourceManager.cs" />
|
||||||
<Compile Include="Model\Xbmc\TvShowResult.cs" />
|
<Compile Include="Model\Xbmc\TvShowResult.cs" />
|
||||||
<Compile Include="Model\Xbmc\Params.cs" />
|
<Compile Include="Model\Xbmc\Params.cs" />
|
||||||
|
@ -8,6 +8,7 @@ using System.Web;
|
|||||||
using Ninject;
|
using Ninject;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Core.Model;
|
using NzbDrone.Core.Model;
|
||||||
|
using NzbDrone.Core.Model.Search;
|
||||||
using NzbDrone.Core.Providers.Core;
|
using NzbDrone.Core.Providers.Core;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Providers.Indexer
|
namespace NzbDrone.Core.Providers.Indexer
|
||||||
@ -57,11 +58,9 @@ namespace NzbDrone.Core.Providers.Indexer
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the rss url for specific episode search
|
/// Gets the rss url for specific episode search
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="seriesTitle">The series title.</param>
|
/// <param name="searchModel">SearchModel containing episode information</param>
|
||||||
/// <param name="seasonNumber">The season number.</param>
|
|
||||||
/// <param name="episodeNumber">The episode number.</param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
protected abstract IList<String> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber);
|
protected abstract IList<String> GetSearchUrls(SearchModel searchModel);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This method can be overwritten to provide indexer specific info parsing
|
/// This method can be overwritten to provide indexer specific info parsing
|
||||||
@ -99,6 +98,31 @@ namespace NzbDrone.Core.Providers.Indexer
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual IList<EpisodeParseResult> FetchSeason(string seriesTitle, int seasonNumber)
|
||||||
|
{
|
||||||
|
_logger.Debug("Searching {0} for {1}-Season {2}", Name, seriesTitle, seasonNumber);
|
||||||
|
|
||||||
|
var result = new List<EpisodeParseResult>();
|
||||||
|
|
||||||
|
var searchModel = new SearchModel
|
||||||
|
{
|
||||||
|
SeriesTitle = GetQueryTitle(seriesTitle),
|
||||||
|
SeasonNumber = seasonNumber,
|
||||||
|
SearchType = SearchType.SeasonSearch
|
||||||
|
};
|
||||||
|
|
||||||
|
var searchUrls = GetSearchUrls(searchModel);
|
||||||
|
|
||||||
|
foreach (var url in searchUrls)
|
||||||
|
{
|
||||||
|
result.AddRange(Fetch(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
result = result.Where(e => e.CleanTitle == Parser.NormalizeTitle(seriesTitle)).ToList();
|
||||||
|
|
||||||
|
_logger.Info("Finished searching {0} for {1}-S{2}, Found {3}", Name, seriesTitle, seasonNumber, result.Count);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual IList<EpisodeParseResult> FetchEpisode(string seriesTitle, int seasonNumber, int episodeNumber)
|
public virtual IList<EpisodeParseResult> FetchEpisode(string seriesTitle, int seasonNumber, int episodeNumber)
|
||||||
{
|
{
|
||||||
@ -106,7 +130,15 @@ namespace NzbDrone.Core.Providers.Indexer
|
|||||||
|
|
||||||
var result = new List<EpisodeParseResult>();
|
var result = new List<EpisodeParseResult>();
|
||||||
|
|
||||||
var searchUrls = GetSearchUrls(GetQueryTitle(seriesTitle), seasonNumber, episodeNumber);
|
var searchModel = new SearchModel
|
||||||
|
{
|
||||||
|
SeriesTitle = GetQueryTitle(seriesTitle),
|
||||||
|
SeasonNumber = seasonNumber,
|
||||||
|
EpisodeNumber = episodeNumber,
|
||||||
|
SearchType = SearchType.EpisodeSearch
|
||||||
|
};
|
||||||
|
|
||||||
|
var searchUrls = GetSearchUrls(searchModel);
|
||||||
|
|
||||||
foreach (var url in searchUrls)
|
foreach (var url in searchUrls)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,7 @@ using System.ServiceModel.Syndication;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
using NzbDrone.Core.Model;
|
using NzbDrone.Core.Model;
|
||||||
|
using NzbDrone.Core.Model.Search;
|
||||||
using NzbDrone.Core.Providers.Core;
|
using NzbDrone.Core.Providers.Core;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Providers.Indexer
|
namespace NzbDrone.Core.Providers.Indexer
|
||||||
@ -38,10 +39,25 @@ namespace NzbDrone.Core.Providers.Indexer
|
|||||||
get { return new NetworkCredential(_configProvider.NewzbinUsername, _configProvider.NewzbinPassword); }
|
get { return new NetworkCredential(_configProvider.NewzbinUsername, _configProvider.NewzbinPassword); }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IList<string> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber)
|
protected override IList<string> GetSearchUrls(SearchModel searchModel)
|
||||||
{
|
{
|
||||||
|
if (searchModel.SearchType == SearchType.EpisodeSearch)
|
||||||
|
{
|
||||||
|
return new List<string>
|
||||||
|
{
|
||||||
|
String.Format(
|
||||||
|
@"http://www.newzbin.com/search/query/?q={0}+{1}x{2:00}&fpn=p&searchaction=Go&category=8&{3}",
|
||||||
|
searchModel.SeriesTitle, searchModel.SeasonNumber,
|
||||||
|
searchModel.EpisodeNumber, UrlParams)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new List<string> { String.Format(@"http://www.newzbin.com/search/query/?q={0}+{1}x{2:00}&fpn=p&searchaction=Go&category=8&{3}", GetQueryTitle(seriesTitle), seasonNumber, episodeNumber, UrlParams) };
|
return new List<string>
|
||||||
|
{
|
||||||
|
String.Format(
|
||||||
|
@"http://www.newzbin.com/search/query/?q={0}+Season+{1}&fpn=p&searchaction=Go&category=8&{2}",
|
||||||
|
searchModel.SeriesTitle, searchModel.SeasonNumber, UrlParams)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string Name
|
public override string Name
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ServiceModel.Syndication;
|
using System.ServiceModel.Syndication;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
using NzbDrone.Core.Model.Search;
|
||||||
using NzbDrone.Core.Providers.Core;
|
using NzbDrone.Core.Providers.Core;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Providers.Indexer
|
namespace NzbDrone.Core.Providers.Indexer
|
||||||
@ -38,13 +39,22 @@ namespace NzbDrone.Core.Providers.Indexer
|
|||||||
return item.Links[0].Uri.ToString();
|
return item.Links[0].Uri.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IList<string> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber)
|
protected override IList<string> GetSearchUrls(SearchModel searchModel)
|
||||||
{
|
{
|
||||||
var searchUrls = new List<String>();
|
var searchUrls = new List<String>();
|
||||||
|
|
||||||
foreach (var url in Urls)
|
foreach (var url in Urls)
|
||||||
{
|
{
|
||||||
searchUrls.Add(String.Format("{0}&term={1}+s{2:00}e{3:00}", url, GetQueryTitle(seriesTitle), seasonNumber, episodeNumber));
|
if (searchModel.SearchType == SearchType.EpisodeSearch)
|
||||||
|
{
|
||||||
|
searchUrls.Add(String.Format("{0}&term={1}+s{2:00}e{3:00}", url, searchModel.SeriesTitle,
|
||||||
|
searchModel.SeasonNumber, searchModel.EpisodeNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searchUrls.Add(String.Format("{0}&term={1}+Season", url, searchModel.SeriesTitle));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return searchUrls;
|
return searchUrls;
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ServiceModel.Syndication;
|
using System.ServiceModel.Syndication;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
using NzbDrone.Core.Model.Search;
|
||||||
using NzbDrone.Core.Providers.Core;
|
using NzbDrone.Core.Providers.Core;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Providers.Indexer
|
namespace NzbDrone.Core.Providers.Indexer
|
||||||
@ -36,13 +37,23 @@ namespace NzbDrone.Core.Providers.Indexer
|
|||||||
return item.Id;
|
return item.Id;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IList<string> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber)
|
protected override IList<string> GetSearchUrls(SearchModel searchModel)
|
||||||
{
|
{
|
||||||
var searchUrls = new List<String>();
|
var searchUrls = new List<String>();
|
||||||
|
|
||||||
foreach (var url in Urls)
|
foreach (var url in Urls)
|
||||||
{
|
{
|
||||||
searchUrls.Add(String.Format("{0}&action=search&q={1}+s{2:00}e{3:00}", url, GetQueryTitle(seriesTitle), seasonNumber, episodeNumber));
|
if (searchModel.SearchType == SearchType.EpisodeSearch)
|
||||||
|
{
|
||||||
|
searchUrls.Add(String.Format("{0}&action=search&q={1}+s{2:00}e{3:00}", url,
|
||||||
|
searchModel.SeriesTitle, searchModel.SeasonNumber, searchModel.EpisodeNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
searchUrls.Add(String.Format("{0}&action=search&q={1}+Season", url,
|
||||||
|
searchModel.SeriesTitle));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return searchUrls;
|
return searchUrls;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ServiceModel.Syndication;
|
using System.ServiceModel.Syndication;
|
||||||
using Ninject;
|
using Ninject;
|
||||||
|
using NzbDrone.Core.Model.Search;
|
||||||
using NzbDrone.Core.Providers.Core;
|
using NzbDrone.Core.Providers.Core;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Providers.Indexer
|
namespace NzbDrone.Core.Providers.Indexer
|
||||||
@ -36,7 +37,7 @@ namespace NzbDrone.Core.Providers.Indexer
|
|||||||
return item.Links[0].Uri.ToString();
|
return item.Links[0].Uri.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override IList<string> GetSearchUrls(string seriesTitle, int seasonNumber, int episodeNumber)
|
protected override IList<string> GetSearchUrls(SearchModel searchModel)
|
||||||
{
|
{
|
||||||
return new List<string>();
|
return new List<string>();
|
||||||
}
|
}
|
||||||
|
@ -11,24 +11,12 @@ namespace NzbDrone.Core.Providers.Jobs
|
|||||||
{
|
{
|
||||||
public class EpisodeSearchJob : IJob
|
public class EpisodeSearchJob : IJob
|
||||||
{
|
{
|
||||||
private readonly InventoryProvider _inventoryProvider;
|
private readonly SearchProvider _searchProvider;
|
||||||
private readonly DownloadProvider _downloadProvider;
|
|
||||||
private readonly IndexerProvider _indexerProvider;
|
|
||||||
private readonly EpisodeProvider _episodeProvider;
|
|
||||||
private readonly SceneMappingProvider _sceneNameMappingProvider;
|
|
||||||
|
|
||||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
|
||||||
|
|
||||||
[Inject]
|
[Inject]
|
||||||
public EpisodeSearchJob(InventoryProvider inventoryProvider, DownloadProvider downloadProvider,
|
public EpisodeSearchJob(SearchProvider searchProvider)
|
||||||
IndexerProvider indexerProvider, EpisodeProvider episodeProvider,
|
|
||||||
SceneMappingProvider sceneNameMappingProvider)
|
|
||||||
{
|
{
|
||||||
_inventoryProvider = inventoryProvider;
|
_searchProvider = searchProvider;
|
||||||
_downloadProvider = downloadProvider;
|
|
||||||
_indexerProvider = indexerProvider;
|
|
||||||
_episodeProvider = episodeProvider;
|
|
||||||
_sceneNameMappingProvider = sceneNameMappingProvider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public EpisodeSearchJob()
|
public EpisodeSearchJob()
|
||||||
@ -51,91 +39,7 @@ namespace NzbDrone.Core.Providers.Jobs
|
|||||||
if (targetId <= 0)
|
if (targetId <= 0)
|
||||||
throw new ArgumentOutOfRangeException("targetId");
|
throw new ArgumentOutOfRangeException("targetId");
|
||||||
|
|
||||||
var episode = _episodeProvider.GetEpisode(targetId);
|
_searchProvider.EpisodeSearch(notification, targetId);
|
||||||
|
|
||||||
if (episode == null)
|
|
||||||
{
|
|
||||||
Logger.Error("Unable to find an episode {0} in database", targetId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
notification.CurrentMessage = "Searching for " + episode;
|
|
||||||
|
|
||||||
|
|
||||||
var series = episode.Series;
|
|
||||||
|
|
||||||
var indexers = _indexerProvider.GetEnabledIndexers();
|
|
||||||
var reports = new List<EpisodeParseResult>();
|
|
||||||
|
|
||||||
var title = _sceneNameMappingProvider.GetSceneName(series.SeriesId);
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(title))
|
|
||||||
{
|
|
||||||
title = series.Title;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var indexer in indexers)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//notification.CurrentMessage = String.Format("Searching for {0} in {1}", episode, indexer.Name);
|
|
||||||
|
|
||||||
//TODO:Add support for daily episodes, maybe search using both date and season/episode?
|
|
||||||
var indexerResults = indexer.FetchEpisode(title, episode.SeasonNumber, episode.EpisodeNumber);
|
|
||||||
|
|
||||||
reports.AddRange(indexerResults);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
|
|
||||||
notification.CurrentMessage = "Processing search results";
|
|
||||||
|
|
||||||
|
|
||||||
//TODO:fix this so when search returns more than one episode
|
|
||||||
//TODO:-its populated with more than the original episode.
|
|
||||||
reports.ForEach(c =>
|
|
||||||
{
|
|
||||||
c.Series = series;
|
|
||||||
});
|
|
||||||
|
|
||||||
ProcessResults(notification, episode, reports);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ProcessResults(ProgressNotification notification, Episode episode, IEnumerable<EpisodeParseResult> reports)
|
|
||||||
{
|
|
||||||
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Logger.Trace("Analysing report " + episodeParseResult);
|
|
||||||
if (_inventoryProvider.IsQualityNeeded(episodeParseResult))
|
|
||||||
{
|
|
||||||
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
|
|
||||||
try
|
|
||||||
{
|
|
||||||
_downloadProvider.DownloadReport(episodeParseResult);
|
|
||||||
notification.CurrentMessage = String.Format("{0} {1} Added to download queue", episode, episodeParseResult.Quality);
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.Warn("Unable to find {0} in any of indexers.", episode);
|
|
||||||
notification.CurrentMessage = String.Format("Unable to find {0} in any of indexers.", episode);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,15 +10,18 @@ namespace NzbDrone.Core.Providers.Jobs
|
|||||||
{
|
{
|
||||||
public class SeasonSearchJob : IJob
|
public class SeasonSearchJob : IJob
|
||||||
{
|
{
|
||||||
private readonly EpisodeProvider _episodeProvider;
|
private readonly SearchProvider _searchProvider;
|
||||||
private readonly EpisodeSearchJob _episodeSearchJob;
|
private readonly EpisodeSearchJob _episodeSearchJob;
|
||||||
|
private readonly EpisodeProvider _episodeProvider;
|
||||||
|
|
||||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
public SeasonSearchJob(EpisodeProvider episodeProvider, EpisodeSearchJob episodeSearchJob)
|
public SeasonSearchJob(SearchProvider searchProvider, EpisodeSearchJob episodeSearchJob,
|
||||||
|
EpisodeProvider episodeProvider)
|
||||||
{
|
{
|
||||||
_episodeProvider = episodeProvider;
|
_searchProvider = searchProvider;
|
||||||
_episodeSearchJob = episodeSearchJob;
|
_episodeSearchJob = episodeSearchJob;
|
||||||
|
_episodeProvider = episodeProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Name
|
public string Name
|
||||||
@ -39,6 +42,9 @@ namespace NzbDrone.Core.Providers.Jobs
|
|||||||
if (secondaryTargetId <= 0)
|
if (secondaryTargetId <= 0)
|
||||||
throw new ArgumentOutOfRangeException("secondaryTargetId");
|
throw new ArgumentOutOfRangeException("secondaryTargetId");
|
||||||
|
|
||||||
|
if (_searchProvider.SeasonSearch(notification, targetId, secondaryTargetId))
|
||||||
|
return;
|
||||||
|
|
||||||
Logger.Debug("Getting episodes from database for series: {0} and season: {1}", targetId, secondaryTargetId);
|
Logger.Debug("Getting episodes from database for series: {0} and season: {1}", targetId, secondaryTargetId);
|
||||||
var episodes = _episodeProvider.GetEpisodesBySeason(targetId, secondaryTargetId);
|
var episodes = _episodeProvider.GetEpisodesBySeason(targetId, secondaryTargetId);
|
||||||
|
|
||||||
@ -48,8 +54,6 @@ namespace NzbDrone.Core.Providers.Jobs
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Todo: Search for a full season NZB before individual episodes
|
|
||||||
|
|
||||||
foreach (var episode in episodes.Where(e => !e.Ignored))
|
foreach (var episode in episodes.Where(e => !e.Ignored))
|
||||||
{
|
{
|
||||||
_episodeSearchJob.Start(notification, episode.EpisodeId, 0);
|
_episodeSearchJob.Start(notification, episode.EpisodeId, 0);
|
||||||
|
236
NzbDrone.Core/Providers/SearchProvider.cs
Normal file
236
NzbDrone.Core/Providers/SearchProvider.cs
Normal file
@ -0,0 +1,236 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using NLog;
|
||||||
|
using Ninject;
|
||||||
|
using NzbDrone.Core.Model;
|
||||||
|
using NzbDrone.Core.Model.Notification;
|
||||||
|
using NzbDrone.Core.Providers.Jobs;
|
||||||
|
using NzbDrone.Core.Repository;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Providers
|
||||||
|
{
|
||||||
|
public class SearchProvider
|
||||||
|
{
|
||||||
|
//Season and Episode Searching
|
||||||
|
private readonly EpisodeProvider _episodeProvider;
|
||||||
|
private readonly InventoryProvider _inventoryProvider;
|
||||||
|
private readonly DownloadProvider _downloadProvider;
|
||||||
|
private readonly SeriesProvider _seriesProvider;
|
||||||
|
private readonly IndexerProvider _indexerProvider;
|
||||||
|
private readonly SceneMappingProvider _sceneMappingProvider;
|
||||||
|
|
||||||
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
|
[Inject]
|
||||||
|
public SearchProvider(EpisodeProvider episodeProvider, InventoryProvider inventoryProvider,
|
||||||
|
DownloadProvider downloadProvider, SeriesProvider seriesProvider,
|
||||||
|
IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider)
|
||||||
|
{
|
||||||
|
_episodeProvider = episodeProvider;
|
||||||
|
_inventoryProvider = inventoryProvider;
|
||||||
|
_downloadProvider = downloadProvider;
|
||||||
|
_seriesProvider = seriesProvider;
|
||||||
|
_indexerProvider = indexerProvider;
|
||||||
|
_sceneMappingProvider = sceneMappingProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchProvider()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool SeasonSearch(ProgressNotification notification, int seriesId, int seasonNumber)
|
||||||
|
{
|
||||||
|
var series = _seriesProvider.GetSeries(seriesId);
|
||||||
|
|
||||||
|
if (series == null)
|
||||||
|
{
|
||||||
|
Logger.Error("Unable to find an series {0} in database", seriesId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
notification.CurrentMessage = String.Format("Searching for {0} Season {1}", series, seasonNumber);
|
||||||
|
|
||||||
|
var indexers = _indexerProvider.GetEnabledIndexers();
|
||||||
|
var reports = new List<EpisodeParseResult>();
|
||||||
|
|
||||||
|
var title = _sceneMappingProvider.GetSceneName(series.SeriesId);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(title))
|
||||||
|
{
|
||||||
|
title = series.Title;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var indexer in indexers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var indexerResults = indexer.FetchSeason(title, seasonNumber);
|
||||||
|
|
||||||
|
reports.AddRange(indexerResults);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
|
||||||
|
|
||||||
|
if (reports.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Logger.Debug("Getting episodes from database for series: {0} and season: {1}", seriesId, seasonNumber);
|
||||||
|
var episodes = _episodeProvider.GetEpisodesBySeason(seriesId, seasonNumber);
|
||||||
|
|
||||||
|
if (episodes == null)
|
||||||
|
{
|
||||||
|
Logger.Warn("No episodes in database found for series: {0} and season: {1}.", seriesId, seasonNumber);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var episodeNumbers = new List<int>();
|
||||||
|
episodeNumbers.AddRange(episodes.Select(e => e.EpisodeNumber));
|
||||||
|
|
||||||
|
notification.CurrentMessage = "Processing search results";
|
||||||
|
|
||||||
|
var reportsToProcess = reports.Where(p => p.FullSeason && p.SeasonNumber == seasonNumber).ToList();
|
||||||
|
|
||||||
|
reportsToProcess.ForEach(c =>
|
||||||
|
{
|
||||||
|
c.Series = series;
|
||||||
|
c.EpisodeNumbers = episodeNumbers;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ProcessSeasonSearchResults(notification, series, seasonNumber, reportsToProcess);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ProcessSeasonSearchResults(ProgressNotification notification, Series series, int seasonNumber, IEnumerable<EpisodeParseResult> reports)
|
||||||
|
{
|
||||||
|
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Trace("Analysing report " + episodeParseResult);
|
||||||
|
if (_inventoryProvider.IsQualityNeeded(episodeParseResult))
|
||||||
|
{
|
||||||
|
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_downloadProvider.DownloadReport(episodeParseResult);
|
||||||
|
notification.CurrentMessage = String.Format("{0} Season {1} {2} Added to download queue", series.Title, seasonNumber, episodeParseResult.Quality);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Warn("Unable to find {0} Season {1} in any of indexers.", series.Title, seasonNumber);
|
||||||
|
notification.CurrentMessage = String.Format("Unable to find {0} Season {1} in any of indexers.", series.Title, seasonNumber);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool EpisodeSearch(ProgressNotification notification, int episodeId)
|
||||||
|
{
|
||||||
|
var episode = _episodeProvider.GetEpisode(episodeId);
|
||||||
|
|
||||||
|
if (episode == null)
|
||||||
|
{
|
||||||
|
Logger.Error("Unable to find an episode {0} in database", episodeId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
notification.CurrentMessage = "Searching for " + episode;
|
||||||
|
|
||||||
|
|
||||||
|
var series = episode.Series;
|
||||||
|
|
||||||
|
var indexers = _indexerProvider.GetEnabledIndexers();
|
||||||
|
var reports = new List<EpisodeParseResult>();
|
||||||
|
|
||||||
|
var title = _sceneMappingProvider.GetSceneName(series.SeriesId);
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(title))
|
||||||
|
{
|
||||||
|
title = series.Title;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var indexer in indexers)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
//notification.CurrentMessage = String.Format("Searching for {0} in {1}", episode, indexer.Name);
|
||||||
|
|
||||||
|
//TODO:Add support for daily episodes, maybe search using both date and season/episode?
|
||||||
|
var indexerResults = indexer.FetchEpisode(title, episode.SeasonNumber, episode.EpisodeNumber);
|
||||||
|
|
||||||
|
reports.AddRange(indexerResults);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.ErrorException("An error has occurred while fetching items from " + indexer.Name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Debug("Finished searching all indexers. Total {0}", reports.Count);
|
||||||
|
notification.CurrentMessage = "Processing search results";
|
||||||
|
|
||||||
|
|
||||||
|
//TODO:fix this so when search returns more than one episode
|
||||||
|
//TODO:-its populated with more than the original episode.
|
||||||
|
reports.ForEach(c =>
|
||||||
|
{
|
||||||
|
c.Series = series;
|
||||||
|
});
|
||||||
|
|
||||||
|
return ProcessEpisodeSearchResults(notification, episode, reports);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ProcessEpisodeSearchResults(ProgressNotification notification, Episode episode, IEnumerable<EpisodeParseResult> reports)
|
||||||
|
{
|
||||||
|
foreach (var episodeParseResult in reports.OrderByDescending(c => c.Quality))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.Trace("Analysing report " + episodeParseResult);
|
||||||
|
if (_inventoryProvider.IsQualityNeeded(episodeParseResult))
|
||||||
|
{
|
||||||
|
Logger.Debug("Found '{0}'. Adding to download queue.", episodeParseResult);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_downloadProvider.DownloadReport(episodeParseResult);
|
||||||
|
notification.CurrentMessage = String.Format("{0} {1} Added to download queue", episode, episodeParseResult.Quality);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.ErrorException("An error has occurred while processing parse result items from " + episodeParseResult, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.Warn("Unable to find {0} in any of indexers.", episode);
|
||||||
|
notification.CurrentMessage = String.Format("Unable to find {0} in any of indexers.", episode);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -23,7 +23,7 @@
|
|||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
margin: 2px;
|
margin: 2px;
|
||||||
padding: 2px 5px;
|
padding: 2px 5px;
|
||||||
width: 95px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.seasonToggleLabel
|
.seasonToggleLabel
|
||||||
|
Loading…
x
Reference in New Issue
Block a user