mirror of
https://github.com/Readarr/Readarr.git
synced 2026-04-23 22:25:09 -04:00
Searching refactored
#ND-135 fixed
This commit is contained in:
+84
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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.Search;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests.DailyEpisodeSearchTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckReportFixture : TestBase
|
||||
{
|
||||
private Series _series;
|
||||
private Episode _episode;
|
||||
private EpisodeParseResult _episodeParseResult;
|
||||
private SearchHistoryItem _searchHistoryItem;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>
|
||||
.CreateNew()
|
||||
.Build();
|
||||
|
||||
_episode = Builder<Episode>
|
||||
.CreateNew()
|
||||
.With(e => e.SeriesId = _series.SeriesId)
|
||||
.With(e => e.Series = _series)
|
||||
.Build();
|
||||
|
||||
_episodeParseResult = Builder<EpisodeParseResult>
|
||||
.CreateNew()
|
||||
.With(p => p.AirDate = _episode.AirDate)
|
||||
.With(p => p.Episodes = new List<Episode> { _episode })
|
||||
.With(p => p.Series = _series)
|
||||
.Build();
|
||||
|
||||
_searchHistoryItem = new SearchHistoryItem();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_WrongEpisode_is_parseResult_doesnt_have_airdate()
|
||||
{
|
||||
_episodeParseResult.AirDate = null;
|
||||
|
||||
Mocker.Resolve<DailyEpisodeSearch>()
|
||||
.CheckReport(_series, new {Episode = _episode}, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.WrongEpisode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_WrongEpisode_is_parseResult_airdate_doesnt_match_episode()
|
||||
{
|
||||
_episodeParseResult.AirDate = _episode.AirDate.Value.AddDays(-10);
|
||||
|
||||
Mocker.Resolve<DailyEpisodeSearch>()
|
||||
.CheckReport(_series, new { Episode = _episode }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.WrongEpisode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_return_error_when_airDates_match()
|
||||
{
|
||||
Mocker.Resolve<DailyEpisodeSearch>()
|
||||
.CheckReport(_series, new { Episode = _episode }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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.Search;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests.DailyEpisodeSearchTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class PerformSearchFixture : PerformSearchTestBase
|
||||
{
|
||||
[Test]
|
||||
public void should_throw_if_episode_is_null()
|
||||
{
|
||||
Episode nullEpisode = null;
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
Mocker.Resolve<DailyEpisodeSearch>()
|
||||
.PerformSearch(_series, new { Episode = nullEpisode }, notification));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fetch_results_from_indexers()
|
||||
{
|
||||
WithValidIndexers();
|
||||
|
||||
Mocker.Resolve<DailyEpisodeSearch>()
|
||||
.PerformSearch(_series, new {Episode = _episode}, notification)
|
||||
.Should()
|
||||
.HaveCount(20);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_error_when_fetching_from_indexer_fails()
|
||||
{
|
||||
WithInvalidIndexers();
|
||||
|
||||
Mocker.Resolve<DailyEpisodeSearch>()
|
||||
.PerformSearch(_series, new { Episode = _episode }, notification)
|
||||
.Should()
|
||||
.HaveCount(0);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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.Search;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests.EpisodeSearchTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckReportFixture : TestBase
|
||||
{
|
||||
private Series _series;
|
||||
private Episode _episode;
|
||||
private EpisodeParseResult _episodeParseResult;
|
||||
private SearchHistoryItem _searchHistoryItem;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>
|
||||
.CreateNew()
|
||||
.Build();
|
||||
|
||||
_episode = Builder<Episode>
|
||||
.CreateNew()
|
||||
.With(e => e.SeriesId = _series.SeriesId)
|
||||
.With(e => e.Series = _series)
|
||||
.Build();
|
||||
|
||||
_episodeParseResult = Builder<EpisodeParseResult>
|
||||
.CreateNew()
|
||||
.With(p => p.SeasonNumber = 1)
|
||||
.With(p => p.EpisodeNumbers = new List<int>{ _episode.EpisodeNumber })
|
||||
.With(p => p.Episodes = new List<Episode> { _episode })
|
||||
.With(p => p.Series = _series)
|
||||
.Build();
|
||||
|
||||
_searchHistoryItem = new SearchHistoryItem();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_WrongSeason_when_season_doesnt_match()
|
||||
{
|
||||
_episode.SeasonNumber = 10;
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.CheckReport(_series, new {Episode = _episode}, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.WrongSeason);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_WrongEpisode_when_episode_doesnt_match()
|
||||
{
|
||||
_episode.EpisodeNumber = 10;
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.CheckReport(_series, new { Episode = _episode }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.WrongEpisode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_return_error_when_season_and_episode_match()
|
||||
{
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.CheckReport(_series, new { Episode = _episode }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.None);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_WrongSeason_when_season_doesnt_match_for_scene_series()
|
||||
{
|
||||
_series.UseSceneNumbering = true;
|
||||
_episode.SceneSeasonNumber = 10;
|
||||
_episode.SeasonNumber = 10;
|
||||
_episode.EpisodeNumber = 10;
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.CheckReport(_series, new { Episode = _episode }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.WrongSeason);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_WrongEpisode_when_episode_doesnt_match_for_scene_series()
|
||||
{
|
||||
_series.UseSceneNumbering = true;
|
||||
_episode.SceneEpisodeNumber = 10;
|
||||
_episode.SeasonNumber = 10;
|
||||
_episode.EpisodeNumber = 10;
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.CheckReport(_series, new { Episode = _episode }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.WrongEpisode);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_return_error_when_season_and_episode_match_for_scene_series()
|
||||
{
|
||||
_series.UseSceneNumbering = true;
|
||||
_episode.SceneSeasonNumber = _episode.SeasonNumber;
|
||||
_episode.SceneEpisodeNumber = _episode.EpisodeNumber;
|
||||
_episode.SeasonNumber = 10;
|
||||
_episode.EpisodeNumber = 10;
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.CheckReport(_series, new { Episode = _episode }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError
|
||||
.Should()
|
||||
.Be(ReportRejectionType.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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.Search;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests.EpisodeSearchTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class PerformSearchFixture : PerformSearchTestBase
|
||||
{
|
||||
[Test]
|
||||
public void should_throw_if_episode_is_null()
|
||||
{
|
||||
Episode nullEpisode = null;
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.PerformSearch(_series, new { Episode = nullEpisode }, notification));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fetch_results_from_indexers()
|
||||
{
|
||||
WithValidIndexers();
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.PerformSearch(_series, new {Episode = _episode}, notification)
|
||||
.Should()
|
||||
.HaveCount(20);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_error_when_fetching_from_indexer_fails()
|
||||
{
|
||||
WithInvalidIndexers();
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.PerformSearch(_series, new { Episode = _episode }, notification)
|
||||
.Should()
|
||||
.HaveCount(0);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(2);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_scene_numbering_when_available()
|
||||
{
|
||||
_series.UseSceneNumbering = true;
|
||||
_episode.SceneEpisodeNumber = 5;
|
||||
_episode.SceneSeasonNumber = 10;
|
||||
|
||||
WithValidIndexers();
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.PerformSearch(_series, new { Episode = _episode }, notification)
|
||||
.Should()
|
||||
.HaveCount(20);
|
||||
|
||||
_indexer1.Verify(v => v.FetchEpisode(_series.Title, 10, 5), Times.Once());
|
||||
_indexer2.Verify(v => v.FetchEpisode(_series.Title, 10, 5), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_standard_numbering_when_scene_series_set_but_info_is_not_available()
|
||||
{
|
||||
_series.UseSceneNumbering = true;
|
||||
_episode.SceneEpisodeNumber = 0;
|
||||
_episode.SceneSeasonNumber = 0;
|
||||
|
||||
WithValidIndexers();
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.PerformSearch(_series, new { Episode = _episode }, notification)
|
||||
.Should()
|
||||
.HaveCount(20);
|
||||
|
||||
_indexer1.Verify(v => v.FetchEpisode(_series.Title, _episode.SeasonNumber, _episode.EpisodeNumber), Times.Once());
|
||||
_indexer2.Verify(v => v.FetchEpisode(_series.Title, _episode.SeasonNumber, _episode.EpisodeNumber), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_use_standard_numbering_when_not_scene_series()
|
||||
{
|
||||
_series.UseSceneNumbering = false;
|
||||
|
||||
WithValidIndexers();
|
||||
|
||||
Mocker.Resolve<EpisodeSearch>()
|
||||
.PerformSearch(_series, new { Episode = _episode }, notification)
|
||||
.Should()
|
||||
.HaveCount(20);
|
||||
|
||||
_indexer1.Verify(v => v.FetchEpisode(_series.Title, _episode.SeasonNumber, _episode.EpisodeNumber), Times.Once());
|
||||
_indexer2.Verify(v => v.FetchEpisode(_series.Title, _episode.SeasonNumber, _episode.EpisodeNumber), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Providers.Search;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests
|
||||
{
|
||||
public class GetSearchTitleFixture : TestBase
|
||||
{
|
||||
private Series _series;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>
|
||||
.CreateNew()
|
||||
.With(s => s.Title = "Hawaii Five-0")
|
||||
.Build();
|
||||
}
|
||||
|
||||
private void WithSceneMapping()
|
||||
{
|
||||
Mocker.GetMock<SceneMappingProvider>()
|
||||
.Setup(s => s.GetSceneName(_series.SeriesId))
|
||||
.Returns("Hawaii Five 0 2010");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_series_title_when_there_is_no_scene_mapping()
|
||||
{
|
||||
Mocker.Resolve<TestSearch>().GetSearchTitle(_series, 5)
|
||||
.Should().Be(_series.Title);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_scene_mapping_when_one_exists()
|
||||
{
|
||||
WithSceneMapping();
|
||||
|
||||
Mocker.Resolve<TestSearch>().GetSearchTitle(_series, 5)
|
||||
.Should().Be("Hawaii Five 0 2010");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_replace_ampersand_with_and()
|
||||
{
|
||||
_series.Title = "Franklin & Bash";
|
||||
|
||||
Mocker.Resolve<TestSearch>().GetSearchTitle(_series, 5)
|
||||
.Should().Be("Franklin and Bash");
|
||||
}
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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.Search;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests.PartialSeasonSearchTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class CheckReportFixture : TestBase
|
||||
{
|
||||
private Series _series;
|
||||
private List<Episode> _episodes;
|
||||
private EpisodeParseResult _episodeParseResult;
|
||||
private SearchHistoryItem _searchHistoryItem;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>
|
||||
.CreateNew()
|
||||
.Build();
|
||||
|
||||
_episodes = Builder<Episode>
|
||||
.CreateListOfSize(10)
|
||||
.All()
|
||||
.With(e => e.SeriesId = _series.SeriesId)
|
||||
.With(e => e.Series = _series)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_episodeParseResult = Builder<EpisodeParseResult>
|
||||
.CreateNew()
|
||||
.With(p => p.SeasonNumber = 1)
|
||||
.Build();
|
||||
|
||||
_searchHistoryItem = new SearchHistoryItem();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_wrongSeason_when_season_does_not_match()
|
||||
{
|
||||
Mocker.Resolve<PartialSeasonSearch>()
|
||||
.CheckReport(_series, new { SeasonNumber = 2, Episodes = _episodes }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError.Should().Be(ReportRejectionType.WrongSeason);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_return_error_when_season_matches()
|
||||
{
|
||||
Mocker.Resolve<PartialSeasonSearch>()
|
||||
.CheckReport(_series, new { SeasonNumber = 1, Episodes = _episodes }, _episodeParseResult, _searchHistoryItem)
|
||||
.SearchError.Should().Be(ReportRejectionType.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
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.Search;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests.PartialSeasonSearchTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class PerformSearchFixture : PerformSearchTestBase
|
||||
{
|
||||
[Test]
|
||||
public void should_throw_if_season_number_is_less_than_zero()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
Mocker.Resolve<PartialSeasonSearch>()
|
||||
.PerformSearch(_series, new
|
||||
{
|
||||
SeasonNumber = -1,
|
||||
Episodes = new List<Episode>{ new Episode() }
|
||||
}, notification));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_throw_if_episodes_is_empty()
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() =>
|
||||
Mocker.Resolve<PartialSeasonSearch>()
|
||||
.PerformSearch(_series, new { SeasonNumber = 10, Episodes = new List<Episode>() }, notification));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_fetch_results_from_indexers()
|
||||
{
|
||||
WithValidIndexers();
|
||||
|
||||
Mocker.Resolve<PartialSeasonSearch>()
|
||||
.PerformSearch(_series, new { SeasonNumber = 1, Episodes = _episodes }, notification)
|
||||
.Should()
|
||||
.HaveCount(40);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_log_error_when_fetching_from_indexer_fails()
|
||||
{
|
||||
WithInvalidIndexers();
|
||||
|
||||
Mocker.Resolve<PartialSeasonSearch>()
|
||||
.PerformSearch(_series, new { SeasonNumber = 1, Episodes = _episodes }, notification)
|
||||
.Should()
|
||||
.HaveCount(0);
|
||||
|
||||
ExceptionVerification.ExpectedErrors(4);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_hit_each_indexer_once_for_each_prefix()
|
||||
{
|
||||
WithValidIndexers();
|
||||
|
||||
Mocker.Resolve<PartialSeasonSearch>()
|
||||
.PerformSearch(_series, new { SeasonNumber = 1, Episodes = _episodes }, notification)
|
||||
.Should()
|
||||
.HaveCount(40);
|
||||
|
||||
_indexer1.Verify(v => v.FetchPartialSeason(_series.Title, 1, 0), Times.Once());
|
||||
_indexer1.Verify(v => v.FetchPartialSeason(_series.Title, 1, 1), Times.Once());
|
||||
_indexer2.Verify(v => v.FetchPartialSeason(_series.Title, 1, 0), Times.Once());
|
||||
_indexer2.Verify(v => v.FetchPartialSeason(_series.Title, 1, 1), Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using FizzWare.NBuilder;
|
||||
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.Repository;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests
|
||||
{
|
||||
public class PerformSearchTestBase : TestBase
|
||||
{
|
||||
protected Series _series;
|
||||
protected Episode _episode;
|
||||
protected List<Episode> _episodes;
|
||||
protected ProgressNotification notification = new ProgressNotification("Testing");
|
||||
|
||||
protected Mock<IndexerBase> _indexer1;
|
||||
protected Mock<IndexerBase> _indexer2;
|
||||
protected List<IndexerBase> _indexers;
|
||||
protected IList<EpisodeParseResult> _parseResults;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_series = Builder<Series>
|
||||
.CreateNew()
|
||||
.Build();
|
||||
|
||||
_episode = Builder<Episode>
|
||||
.CreateNew()
|
||||
.With(e => e.SeriesId = _series.SeriesId)
|
||||
.With(e => e.Series = _series)
|
||||
.Build();
|
||||
|
||||
_episodes = Builder<Episode>
|
||||
.CreateListOfSize(10)
|
||||
.All()
|
||||
.With(e => e.SeriesId = _series.SeriesId)
|
||||
.With(e => e.Series = _series)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
_parseResults = Builder<EpisodeParseResult>
|
||||
.CreateListOfSize(10)
|
||||
.Build();
|
||||
|
||||
_indexer1 = new Mock<IndexerBase>();
|
||||
_indexer2 = new Mock<IndexerBase>();
|
||||
_indexers = new List<IndexerBase> { _indexer1.Object, _indexer2.Object };
|
||||
|
||||
Mocker.GetMock<IndexerProvider>()
|
||||
.Setup(c => c.GetEnabledIndexers())
|
||||
.Returns(_indexers);
|
||||
}
|
||||
|
||||
protected void WithValidIndexers()
|
||||
{
|
||||
_indexer1.Setup(c => c.FetchEpisode(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(_parseResults);
|
||||
_indexer1.Setup(c => c.FetchDailyEpisode(It.IsAny<string>(), It.IsAny<DateTime>()))
|
||||
.Returns(_parseResults);
|
||||
_indexer1.Setup(c => c.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(_parseResults);
|
||||
|
||||
_indexer2.Setup(c => c.FetchEpisode(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(_parseResults);
|
||||
_indexer2.Setup(c => c.FetchDailyEpisode(It.IsAny<string>(), It.IsAny<DateTime>()))
|
||||
.Returns(_parseResults);
|
||||
_indexer2.Setup(c => c.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Returns(_parseResults);
|
||||
}
|
||||
|
||||
protected void WithInvalidIndexers()
|
||||
{
|
||||
_indexer1.Setup(c => c.FetchEpisode(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Throws(new Exception());
|
||||
_indexer1.Setup(c => c.FetchDailyEpisode(It.IsAny<string>(), It.IsAny<DateTime>()))
|
||||
.Throws(new Exception());
|
||||
_indexer1.Setup(c => c.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Throws(new Exception());
|
||||
|
||||
_indexer2.Setup(c => c.FetchEpisode(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Throws(new Exception());
|
||||
_indexer2.Setup(c => c.FetchDailyEpisode(It.IsAny<string>(), It.IsAny<DateTime>()))
|
||||
.Throws(new Exception());
|
||||
_indexer2.Setup(c => c.FetchPartialSeason(It.IsAny<string>(), It.IsAny<int>(), It.IsAny<int>()))
|
||||
.Throws(new Exception());
|
||||
|
||||
_indexer1.SetupGet(c => c.Name).Returns("Indexer1");
|
||||
_indexer1.SetupGet(c => c.Name).Returns("Indexer2");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
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.DecisionEngine;
|
||||
using NzbDrone.Core.Providers.Search;
|
||||
using NzbDrone.Core.Repository;
|
||||
using NzbDrone.Core.Repository.Quality;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ProcessResultsFixture : TestBase
|
||||
{
|
||||
private Series _matchingSeries;
|
||||
private Series _mismatchedSeries;
|
||||
private Series _nullSeries = null;
|
||||
|
||||
private SearchHistory _searchHistory;
|
||||
private ProgressNotification _notification;
|
||||
|
||||
private IList<Episode> _episodes;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
_matchingSeries = Builder<Series>.CreateNew()
|
||||
.With(s => s.SeriesId = 79488)
|
||||
.With(s => s.Title = "30 Rock")
|
||||
.Build();
|
||||
|
||||
_mismatchedSeries = Builder<Series>.CreateNew()
|
||||
.With(s => s.SeriesId = 12345)
|
||||
.With(s => s.Title = "Not 30 Rock")
|
||||
.Build();
|
||||
|
||||
_searchHistory = new SearchHistory();
|
||||
_notification = new ProgressNotification("Test");
|
||||
|
||||
_episodes = Builder<Episode>
|
||||
.CreateListOfSize(1)
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<EpisodeProvider>()
|
||||
.Setup(s => s.GetEpisodesByParseResult(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(_episodes);
|
||||
}
|
||||
|
||||
private void WithMatchingSeries()
|
||||
{
|
||||
Mocker.GetMock<SeriesProvider>()
|
||||
.Setup(s => s.FindSeries(It.IsAny<string>())).Returns(_matchingSeries);
|
||||
}
|
||||
|
||||
private void WithMisMatchedSeries()
|
||||
{
|
||||
Mocker.GetMock<SeriesProvider>()
|
||||
.Setup(s => s.FindSeries(It.IsAny<string>())).Returns(_mismatchedSeries);
|
||||
}
|
||||
|
||||
private void WithNullSeries()
|
||||
{
|
||||
Mocker.GetMock<SeriesProvider>()
|
||||
.Setup(s => s.FindSeries(It.IsAny<string>())).Returns(_nullSeries);
|
||||
}
|
||||
|
||||
private void WithSuccessfulDownload()
|
||||
{
|
||||
Mocker.GetMock<DownloadProvider>()
|
||||
.Setup(s => s.DownloadReport(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(true);
|
||||
}
|
||||
|
||||
private void WithFailingDownload()
|
||||
{
|
||||
Mocker.GetMock<DownloadProvider>()
|
||||
.Setup(s => s.DownloadReport(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(false);
|
||||
}
|
||||
|
||||
private void WithQualityNeeded()
|
||||
{
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(ReportRejectionType.None);
|
||||
}
|
||||
|
||||
private void WithQualityNotNeeded()
|
||||
{
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()))
|
||||
.Returns(ReportRejectionType.ExistingQualityIsEqualOrBetter);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_higher_quality_results_first()
|
||||
{
|
||||
WithMatchingSeries();
|
||||
WithSuccessfulDownload();
|
||||
|
||||
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 1 })
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.DVD, true))
|
||||
.With(c => c.Age = 10)
|
||||
.Random(1)
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.Bluray1080p, true))
|
||||
.With(c => c.Age = 100)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.Quality == QualityTypes.Bluray1080p)))
|
||||
.Returns(ReportRejectionType.None);
|
||||
|
||||
var result = Mocker.Resolve<TestSearch>().ProcessReports(_matchingSeries, new { }, parseResults, _searchHistory, _notification);
|
||||
|
||||
result.SearchHistoryItems.Should().HaveCount(parseResults.Count);
|
||||
result.SearchHistoryItems.Should().Contain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_newer_reports_first()
|
||||
{
|
||||
WithMatchingSeries();
|
||||
WithSuccessfulDownload();
|
||||
|
||||
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 1 })
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.Bluray1080p, true))
|
||||
.With(c => c.Age = 300)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
parseResults[2].Age = 100;
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.IsAny<EpisodeParseResult>())).Returns(ReportRejectionType.None);
|
||||
|
||||
var result = Mocker.Resolve<TestSearch>().ProcessReports(_matchingSeries, new { }, parseResults, _searchHistory, _notification);
|
||||
|
||||
result.SearchHistoryItems.Should().HaveCount(parseResults.Count);
|
||||
result.SearchHistoryItems.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.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_check_other_reports_when_quality_is_not_wanted()
|
||||
{
|
||||
WithMatchingSeries();
|
||||
WithQualityNotNeeded();
|
||||
|
||||
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 1 })
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.DVD, true))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
var result = Mocker.Resolve<TestSearch>().ProcessReports(_matchingSeries, new { }, parseResults, _searchHistory, _notification);
|
||||
|
||||
result.SearchHistoryItems.Should().HaveCount(parseResults.Count);
|
||||
result.SearchHistoryItems.Should().NotContain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Exactly(5));
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_should_skip_if_series_is_not_watched()
|
||||
{
|
||||
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 1 })
|
||||
.With(e => e.Quality = new QualityModel(QualityTypes.HDTV720p, false))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
WithNullSeries();
|
||||
|
||||
//Act
|
||||
var result = Mocker.Resolve<TestSearch>().ProcessReports(_matchingSeries, new { }, parseResults, _searchHistory, _notification);
|
||||
|
||||
//Assert
|
||||
result.SearchHistoryItems.Should().HaveCount(parseResults.Count);
|
||||
result.SearchHistoryItems.Should().NotContain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_if_series_does_not_match_searched_series()
|
||||
{
|
||||
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 1 })
|
||||
.With(e => e.Quality = new QualityModel(QualityTypes.HDTV720p, false))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
WithMisMatchedSeries();
|
||||
|
||||
//Act
|
||||
var result = Mocker.Resolve<TestSearch>().ProcessReports(_matchingSeries, new { }, parseResults, _searchHistory, _notification);
|
||||
|
||||
//Assert
|
||||
result.SearchHistoryItems.Should().HaveCount(parseResults.Count);
|
||||
result.SearchHistoryItems.Should().NotContain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Never());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_if_episode_was_already_downloaded()
|
||||
{
|
||||
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 5 })
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.DVD, true))
|
||||
.TheLast(1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 1, 2, 3, 4, 5 })
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
WithMatchingSeries();
|
||||
WithQualityNeeded();
|
||||
WithSuccessfulDownload();
|
||||
|
||||
//Act
|
||||
var result = Mocker.Resolve<TestSearch>().ProcessReports(_matchingSeries, new { }, parseResults, _searchHistory, _notification);
|
||||
|
||||
//Assert
|
||||
result.SearchHistoryItems.Should().HaveCount(parseResults.Count);
|
||||
result.SearchHistoryItems.Should().Contain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_try_next_report_if_download_fails()
|
||||
{
|
||||
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(2)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 1 })
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.DVD, true))
|
||||
.TheLast(1)
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.SDTV, true))
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
WithMatchingSeries();
|
||||
WithQualityNeeded();
|
||||
|
||||
Mocker.GetMock<DownloadProvider>()
|
||||
.Setup(s => s.DownloadReport(It.Is<EpisodeParseResult>(d => d.Quality.Quality == QualityTypes.DVD)))
|
||||
.Returns(false);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>()
|
||||
.Setup(s => s.DownloadReport(It.Is<EpisodeParseResult>(d => d.Quality.Quality == QualityTypes.SDTV)))
|
||||
.Returns(true);
|
||||
|
||||
//Act
|
||||
var result = Mocker.Resolve<TestSearch>().ProcessReports(_matchingSeries, new { }, parseResults, _searchHistory, _notification);
|
||||
|
||||
//Assert
|
||||
result.SearchHistoryItems.Should().HaveCount(parseResults.Count);
|
||||
result.SearchHistoryItems.Should().Contain(s => s.Success);
|
||||
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Exactly(2));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_return_valid_successes_when_one_or_more_downloaded()
|
||||
{
|
||||
var parseResults = Builder<EpisodeParseResult>.CreateListOfSize(5)
|
||||
.All()
|
||||
.With(e => e.SeasonNumber = 1)
|
||||
.With(e => e.EpisodeNumbers = new List<int> { 1 })
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.DVD, true))
|
||||
.With(c => c.Age = 10)
|
||||
.Random(1)
|
||||
.With(c => c.Quality = new QualityModel(QualityTypes.Bluray1080p, true))
|
||||
.With(c => c.Age = 100)
|
||||
.Build()
|
||||
.ToList();
|
||||
|
||||
WithMatchingSeries();
|
||||
WithSuccessfulDownload();
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>()
|
||||
.Setup(s => s.IsSatisfiedBy(It.Is<EpisodeParseResult>(d => d.Quality.Quality == QualityTypes.Bluray1080p)))
|
||||
.Returns(ReportRejectionType.None);
|
||||
|
||||
//Act
|
||||
var result = Mocker.Resolve<TestSearch>().ProcessReports(_matchingSeries, new { }, parseResults, _searchHistory, _notification);
|
||||
|
||||
//Assert
|
||||
result.Successes.Should().NotBeNull();
|
||||
result.Successes.Should().NotBeEmpty();
|
||||
|
||||
Mocker.GetMock<AllowedDownloadSpecification>().Verify(c => c.IsSatisfiedBy(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
Mocker.GetMock<DownloadProvider>().Verify(c => c.DownloadReport(It.IsAny<EpisodeParseResult>()),
|
||||
Times.Once());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Model;
|
||||
using NzbDrone.Core.Providers;
|
||||
using NzbDrone.Core.Providers.DecisionEngine;
|
||||
using NzbDrone.Core.Providers.Search;
|
||||
using NzbDrone.Core.Repository.Search;
|
||||
|
||||
namespace NzbDrone.Core.Test.ProviderTests.SearchTests
|
||||
{
|
||||
public class TestSearch : SearchBase
|
||||
{
|
||||
private static readonly Logger logger = LogManager.GetCurrentClassLogger();
|
||||
|
||||
public TestSearch(SeriesProvider seriesProvider, EpisodeProvider episodeProvider, DownloadProvider downloadProvider,
|
||||
IndexerProvider indexerProvider, SceneMappingProvider sceneMappingProvider,
|
||||
AllowedDownloadSpecification allowedDownloadSpecification, SearchHistoryProvider searchHistoryProvider)
|
||||
: base(seriesProvider, episodeProvider, downloadProvider, indexerProvider, sceneMappingProvider,
|
||||
allowedDownloadSpecification, searchHistoryProvider)
|
||||
{
|
||||
}
|
||||
|
||||
public override List<EpisodeParseResult> PerformSearch(Repository.Series series, dynamic options, Model.Notification.ProgressNotification notification)
|
||||
{
|
||||
if (options.Episode == null)
|
||||
throw new ArgumentException("Episode is invalid");
|
||||
|
||||
notification.CurrentMessage = "Looking for " + options.Episode;
|
||||
|
||||
var reports = new List<EpisodeParseResult>();
|
||||
var title = GetSearchTitle(series);
|
||||
|
||||
var seasonNumber = options.Episode.SeasonNumber;
|
||||
var episodeNumber = options.Episode.EpisodeNumber;
|
||||
|
||||
Parallel.ForEach(_indexerProvider.GetEnabledIndexers(), indexer =>
|
||||
{
|
||||
try
|
||||
{
|
||||
reports.AddRange(indexer.FetchEpisode(title, seasonNumber, episodeNumber));
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.ErrorException(String.Format("An error has occurred while searching for {0}-S{1:00}E{2:00} from: {3}",
|
||||
series.Title, options.SeasonNumber, options.EpisodeNumber, indexer.Name), e);
|
||||
}
|
||||
});
|
||||
|
||||
return reports;
|
||||
}
|
||||
|
||||
public override SearchHistoryItem CheckReport(Repository.Series series, dynamic options, EpisodeParseResult episodeParseResult, Repository.Search.SearchHistoryItem item)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
protected override void FinalizeSearch(Repository.Series series, dynamic options, bool reportsFound, Model.Notification.ProgressNotification notification)
|
||||
{
|
||||
logger.Warn("Unable to find {0} in any of indexers.", series.Title);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user