diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index 72ae79891..821a70173 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Http.Proxy; using NzbDrone.Core.Configuration.Events; using NzbDrone.Core.ImportLists; +using NzbDrone.Core.IndexerSearch; using NzbDrone.Core.Languages; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.EpisodeImport; @@ -129,6 +130,13 @@ public int MinimumAge set { SetValue("MinimumAge", value); } } + public AnimeSeasonSearchFallback AnimeSeasonSearchFallback + { + get { return GetValueEnum("AnimeSeasonSearchFallback", AnimeSeasonSearchFallback.FullSeasonNotAired); } + + set { SetValue("AnimeSeasonSearchFallback", value); } + } + public ProperDownloadTypes DownloadPropersAndRepacks { get { return GetValueEnum("DownloadPropersAndRepacks", ProperDownloadTypes.PreferAndUpgrade); } diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index 5ebb51b94..6b352950c 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using NzbDrone.Common.Http.Proxy; using NzbDrone.Core.ImportLists; +using NzbDrone.Core.IndexerSearch; using NzbDrone.Core.MediaFiles; using NzbDrone.Core.MediaFiles.EpisodeImport; using NzbDrone.Core.Qualities; @@ -52,6 +53,9 @@ public interface IConfigService string ChmodFolder { get; set; } string ChownGroup { get; set; } + // Anime Season Search + AnimeSeasonSearchFallback AnimeSeasonSearchFallback { get; set; } + // Indexers int Retention { get; set; } int RssSyncInterval { get; set; } diff --git a/src/NzbDrone.Core/IndexerSearch/AnimeSeasonSearchFallback.cs b/src/NzbDrone.Core/IndexerSearch/AnimeSeasonSearchFallback.cs new file mode 100644 index 000000000..80ffd7934 --- /dev/null +++ b/src/NzbDrone.Core/IndexerSearch/AnimeSeasonSearchFallback.cs @@ -0,0 +1,10 @@ +namespace NzbDrone.Core.IndexerSearch +{ + public enum AnimeSeasonSearchFallback + { + Never = 0, + Always = 1, + FullSeasonAired = 2, + FullSeasonNotAired = 3 + } +} diff --git a/src/NzbDrone.Core/IndexerSearch/ReleaseSearchService.cs b/src/NzbDrone.Core/IndexerSearch/ReleaseSearchService.cs index 0d6762674..2ffc7c236 100644 --- a/src/NzbDrone.Core/IndexerSearch/ReleaseSearchService.cs +++ b/src/NzbDrone.Core/IndexerSearch/ReleaseSearchService.cs @@ -7,6 +7,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation.Extensions; using NzbDrone.Core.DataAugmentation.Scene; +using NzbDrone.Core.Configuration; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Exceptions; using NzbDrone.Core.Indexers; @@ -32,6 +33,7 @@ public class ReleaseSearchService : ISearchForReleases private readonly ISeriesService _seriesService; private readonly IEpisodeService _episodeService; private readonly IMakeDownloadDecision _makeDownloadDecision; + private readonly IConfigService _configService; private readonly Logger _logger; public ReleaseSearchService(IIndexerFactory indexerFactory, @@ -39,6 +41,7 @@ public ReleaseSearchService(IIndexerFactory indexerFactory, ISeriesService seriesService, IEpisodeService episodeService, IMakeDownloadDecision makeDownloadDecision, + IConfigService configService, Logger logger) { _indexerFactory = indexerFactory; @@ -46,6 +49,7 @@ public ReleaseSearchService(IIndexerFactory indexerFactory, _seriesService = seriesService; _episodeService = episodeService; _makeDownloadDecision = makeDownloadDecision; + _configService = configService; _logger = logger; } @@ -430,11 +434,32 @@ private async Task> SearchAnimeSeason(Series series, List } else { - _logger.Debug("No approved season results for {0}, falling back to per-episode search for {1} episodes", series.Title, episodesToSearch.Count); + var fallbackSetting = _configService.AnimeSeasonSearchFallback; + var allEpisodesAired = episodesToSearch.All(e => e.AirDateUtc.HasValue && e.AirDateUtc.Value.Before(DateTime.UtcNow)); - foreach (var episode in episodesToSearch) + var shouldFallback = fallbackSetting switch { - downloadDecisions.AddRange(await SearchAnime(series, episode, monitoredOnly, userInvokedSearch, interactiveSearch, true)); + AnimeSeasonSearchFallback.Never => false, + AnimeSeasonSearchFallback.Always => true, + AnimeSeasonSearchFallback.FullSeasonAired => allEpisodesAired, + AnimeSeasonSearchFallback.FullSeasonNotAired => !allEpisodesAired, + _ => !allEpisodesAired + }; + + if (shouldFallback) + { + _logger.Debug("No approved season results for {0}, falling back to per-episode search for {1} episodes (fallback: {2}, allAired: {3})", + series.Title, episodesToSearch.Count, fallbackSetting, allEpisodesAired); + + foreach (var episode in episodesToSearch) + { + downloadDecisions.AddRange(await SearchAnime(series, episode, monitoredOnly, userInvokedSearch, interactiveSearch, true)); + } + } + else + { + _logger.Debug("No approved season results for {0}, per-episode fallback skipped (fallback: {1}, allAired: {2})", + series.Title, fallbackSetting, allEpisodesAired); } } diff --git a/src/Sonarr.Api.V3/Config/IndexerConfigResource.cs b/src/Sonarr.Api.V3/Config/IndexerConfigResource.cs index 6082d18b1..2ac3ae900 100644 --- a/src/Sonarr.Api.V3/Config/IndexerConfigResource.cs +++ b/src/Sonarr.Api.V3/Config/IndexerConfigResource.cs @@ -1,4 +1,5 @@ using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch; using Sonarr.Http.REST; namespace Sonarr.Api.V3.Config @@ -9,6 +10,7 @@ public class IndexerConfigResource : RestResource public int Retention { get; set; } public int MaximumSize { get; set; } public int RssSyncInterval { get; set; } + public AnimeSeasonSearchFallback AnimeSeasonSearchFallback { get; set; } } public static class IndexerConfigResourceMapper @@ -20,7 +22,8 @@ public static IndexerConfigResource ToResource(IConfigService model) MinimumAge = model.MinimumAge, Retention = model.Retention, MaximumSize = model.MaximumSize, - RssSyncInterval = model.RssSyncInterval + RssSyncInterval = model.RssSyncInterval, + AnimeSeasonSearchFallback = model.AnimeSeasonSearchFallback }; } } diff --git a/src/Sonarr.Api.V5/Settings/IndexerSettingsResource.cs b/src/Sonarr.Api.V5/Settings/IndexerSettingsResource.cs index 3c1ffaac5..282a5657a 100644 --- a/src/Sonarr.Api.V5/Settings/IndexerSettingsResource.cs +++ b/src/Sonarr.Api.V5/Settings/IndexerSettingsResource.cs @@ -1,4 +1,5 @@ using NzbDrone.Core.Configuration; +using NzbDrone.Core.IndexerSearch; using Sonarr.Http.REST; namespace Sonarr.Api.V5.Settings @@ -9,6 +10,7 @@ public class IndexerSettingsResource : RestResource public int Retention { get; set; } public int MaximumSize { get; set; } public int RssSyncInterval { get; set; } + public AnimeSeasonSearchFallback AnimeSeasonSearchFallback { get; set; } } public static class IndexerConfigResourceMapper @@ -20,7 +22,8 @@ public static IndexerSettingsResource ToResource(IConfigService model) MinimumAge = model.MinimumAge, Retention = model.Retention, MaximumSize = model.MaximumSize, - RssSyncInterval = model.RssSyncInterval + RssSyncInterval = model.RssSyncInterval, + AnimeSeasonSearchFallback = model.AnimeSeasonSearchFallback }; } }