diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/Search/SingleEpisodeSearchMatchSpecificationTests/AnimeSearchFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/Search/SingleEpisodeSearchMatchSpecificationTests/AnimeSearchFixture.cs new file mode 100644 index 000000000..fb3d61f3b --- /dev/null +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/Search/SingleEpisodeSearchMatchSpecificationTests/AnimeSearchFixture.cs @@ -0,0 +1,47 @@ +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.DecisionEngine.Specifications.Search; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.DecisionEngineTests.Search.SingleEpisodeSearchMatchSpecificationTests +{ + [TestFixture] + public class AnimeSearchFixture : TestBase + { + private RemoteEpisode _remoteEpisode = new RemoteEpisode(); + private AnimeEpisodeSearchCriteria _searchCriteria = new AnimeEpisodeSearchCriteria(); + + [SetUp] + public void Setup() + { + _remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo(); + } + + [Test] + public void should_return_false_if_full_season_result_for_single_episode_search() + { + _remoteEpisode.ParsedEpisodeInfo.FullSeason = true; + + Subject.IsSatisfiedBy(_remoteEpisode, _searchCriteria).Accepted.Should().BeFalse(); + } + + [Test] + public void should_return_true_if_not_a_full_season_result() + { + _remoteEpisode.ParsedEpisodeInfo.FullSeason = false; + + Subject.IsSatisfiedBy(_remoteEpisode, _searchCriteria).Accepted.Should().BeTrue(); + } + + [Test] + public void should_return_true_if_full_season_result_for_full_season_search() + { + _remoteEpisode.ParsedEpisodeInfo.FullSeason = true; + _searchCriteria.IsSeasonSearch = true; + + Subject.IsSatisfiedBy(_remoteEpisode, _searchCriteria).Accepted.Should().BeTrue(); + } + } +} diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/Search/SingleEpisodeSearchMatchSpecificationTests/StandardEpisodeSearch.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/Search/SingleEpisodeSearchMatchSpecificationTests/StandardEpisodeSearch.cs new file mode 100644 index 000000000..993c2c936 --- /dev/null +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/Search/SingleEpisodeSearchMatchSpecificationTests/StandardEpisodeSearch.cs @@ -0,0 +1,58 @@ +using System; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.DecisionEngine.Specifications.Search; +using NzbDrone.Core.IndexerSearch.Definitions; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Test.Common; + +namespace NzbDrone.Core.Test.DecisionEngineTests.Search.SingleEpisodeSearchMatchSpecificationTests +{ + [TestFixture] + public class StandardEpisodeSearch : TestBase + { + private RemoteEpisode _remoteEpisode = new RemoteEpisode(); + private SingleEpisodeSearchCriteria _searchCriteria = new SingleEpisodeSearchCriteria(); + + [SetUp] + public void Setup() + { + _remoteEpisode.ParsedEpisodeInfo = new ParsedEpisodeInfo(); + _remoteEpisode.ParsedEpisodeInfo.SeasonNumber = 5; + _remoteEpisode.ParsedEpisodeInfo.EpisodeNumbers = new[] { 1 }; + + _searchCriteria.SeasonNumber = 5; + _searchCriteria.EpisodeNumber = 1; + } + + [Test] + public void should_return_false_if_season_does_not_match() + { + _remoteEpisode.ParsedEpisodeInfo.SeasonNumber = 10; + + Subject.IsSatisfiedBy(_remoteEpisode, _searchCriteria).Accepted.Should().BeFalse(); + } + + [Test] + public void should_return_false_if_full_season_result_for_single_episode_search() + { + _remoteEpisode.ParsedEpisodeInfo.EpisodeNumbers = Array.Empty(); + + Subject.IsSatisfiedBy(_remoteEpisode, _searchCriteria).Accepted.Should().BeFalse(); + } + + [Test] + public void should_return_false_if_episode_number_does_not_match_search_criteria() + { + _remoteEpisode.ParsedEpisodeInfo.EpisodeNumbers = new []{ 2 }; + + Subject.IsSatisfiedBy(_remoteEpisode, _searchCriteria).Accepted.Should().BeFalse(); + } + + [Test] + public void should_return_true_if_full_season_result_for_full_season_search() + { + Subject.IsSatisfiedBy(_remoteEpisode, _searchCriteria).Accepted.Should().BeTrue(); + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs b/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs index 9c6eedbe1..28f9c5e1c 100644 --- a/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs +++ b/src/NzbDrone.Core.Test/IndexerSearchTests/NzbSearchServiceFixture.cs @@ -281,6 +281,24 @@ namespace NzbDrone.Core.Test.IndexerSearchTests criteria.Count.Should().Be(0); } + [Test] + public void season_search_for_anime_should_set_isSeasonSearch_flag() + { + WithEpisodes(); + _xemSeries.SeriesType = SeriesTypes.Anime; + _xemEpisodes.ForEach(e => e.EpisodeFileId = 0); + + var seasonNumber = 1; + var allCriteria = WatchForSearchCriteria(); + + Subject.SeasonSearch(_xemSeries.Id, seasonNumber, true, true, false); + + var criteria = allCriteria.OfType().ToList(); + + criteria.Count.Should().Be(_xemEpisodes.Count(e => e.SeasonNumber == seasonNumber)); + criteria.ForEach(c => c.IsSeasonSearch.Should().BeTrue()); + } + [Test] public void season_search_for_daily_should_search_multiple_years() { diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 6fea040e0..1489a3054 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -162,6 +162,8 @@ + + diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeSearchMatchSpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeSearchMatchSpecification.cs index 2f1c341e4..fc9b0b895 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeSearchMatchSpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/Search/SingleEpisodeSearchMatchSpecification.cs @@ -56,9 +56,9 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.Search return Decision.Accept(); } - private Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, AnimeEpisodeSearchCriteria singleEpisodeSpec) + private Decision IsSatisfiedBy(RemoteEpisode remoteEpisode, AnimeEpisodeSearchCriteria animeEpisodeSpec) { - if (remoteEpisode.ParsedEpisodeInfo.FullSeason) + if (remoteEpisode.ParsedEpisodeInfo.FullSeason && !animeEpisodeSpec.IsSeasonSearch) { _logger.Debug("Full season result during single episode search, skipping."); return Decision.Reject("Full season pack"); diff --git a/src/NzbDrone.Core/IndexerSearch/Definitions/AnimeEpisodeSearchCriteria.cs b/src/NzbDrone.Core/IndexerSearch/Definitions/AnimeEpisodeSearchCriteria.cs index a7cd8b9d2..297d36558 100644 --- a/src/NzbDrone.Core/IndexerSearch/Definitions/AnimeEpisodeSearchCriteria.cs +++ b/src/NzbDrone.Core/IndexerSearch/Definitions/AnimeEpisodeSearchCriteria.cs @@ -3,6 +3,7 @@ public class AnimeEpisodeSearchCriteria : SearchCriteriaBase { public int AbsoluteEpisodeNumber { get; set; } + public bool IsSeasonSearch { get; set; } public override string ToString() { diff --git a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs index 3f51ef006..61bdc322a 100644 --- a/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/NzbSearchService.cs @@ -193,10 +193,12 @@ namespace NzbDrone.Core.IndexerSearch return Dispatch(indexer => indexer.Fetch(searchSpec), searchSpec); } - private List SearchAnime(Series series, Episode episode, bool userInvokedSearch, bool interactiveSearch) + private List SearchAnime(Series series, Episode episode, bool userInvokedSearch, bool interactiveSearch, bool isSeasonSearch = false) { var searchSpec = Get(series, new List { episode }, userInvokedSearch, interactiveSearch); + searchSpec.IsSeasonSearch = isSeasonSearch; + if (episode.SceneAbsoluteEpisodeNumber.HasValue) { searchSpec.AbsoluteEpisodeNumber = episode.SceneAbsoluteEpisodeNumber.Value; @@ -232,7 +234,7 @@ namespace NzbDrone.Core.IndexerSearch // Only search for aired episodes when performing a season anime search foreach (var episode in episodes.Where(e => e.Monitored && e.AirDateUtc.HasValue && e.AirDateUtc.Value.Before(DateTime.UtcNow))) { - downloadDecisions.AddRange(SearchAnime(series, episode, userInvokedSearch, interactiveSearch)); + downloadDecisions.AddRange(SearchAnime(series, episode, userInvokedSearch, interactiveSearch, true)); } return downloadDecisions;