diff --git a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs index 86f125e45..c1b8e42ad 100644 --- a/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs +++ b/src/NzbDrone.Core/Indexers/HttpIndexerBase.cs @@ -43,7 +43,7 @@ public abstract class HttpIndexerBase : IndexerBase { Exception: HttpException { Response.HasHttpServerError: true } } => PredicateResult.True(), _ => PredicateResult.False() }, - Delay = RateLimit, + Delay = GetResolvedRateLimit(), MaxRetryAttempts = 2, BackoffType = DelayBackoffType.Exponential, UseJitter = true, @@ -81,6 +81,31 @@ public abstract class HttpIndexerBase : IndexerBase public virtual int PageSize => 0; public virtual TimeSpan RateLimit => TimeSpan.FromSeconds(2); + private TimeSpan GetResolvedRateLimit(TimeSpan? requestDelay = null) + { + var systemDelay = requestDelay ?? TimeSpan.Zero; + + if (systemDelay < RateLimit) + { + systemDelay = RateLimit; + } + + if (Settings?.BaseSettings?.RequestDelay is null or < 2 or > 60) + { + return systemDelay; + } + + var userDelay = TimeSpan.FromSeconds(Settings.BaseSettings.RequestDelay.Value); + + if (userDelay > systemDelay) + { + _logger.Trace("Using user defined request delay of {0} seconds for indexer {1}", userDelay.TotalSeconds, Definition.Name); + return userDelay; + } + + return systemDelay; + } + public abstract IIndexerRequestGenerator GetRequestGenerator(); public abstract IParseIndexerResponse GetParser(); @@ -236,11 +261,7 @@ public override async Task Download(Uri link) return new IndexerDownloadResponse(Encoding.UTF8.GetBytes(request.Url.FullUri)); } - if (request.RateLimit < RateLimit) - { - request.RateLimit = RateLimit; - } - + request.RateLimit = GetResolvedRateLimit(request.RateLimit); request.AllowAutoRedirect = false; byte[] fileData; @@ -636,10 +657,7 @@ protected virtual async Task FetchIndexerResponse(IndexerReques { _logger.Debug("Downloading Feed " + request.HttpRequest.ToString(false)); - if (request.HttpRequest.RateLimit < RateLimit) - { - request.HttpRequest.RateLimit = RateLimit; - } + request.HttpRequest.RateLimit = GetResolvedRateLimit(request.HttpRequest.RateLimit); if (_configService.LogIndexerResponse) { @@ -716,11 +734,7 @@ protected async Task ExecuteAuth(HttpRequest request) request.RequestTimeout = TimeSpan.FromSeconds(15); } - if (request.RateLimit < RateLimit) - { - request.RateLimit = RateLimit; - } - + request.RateLimit = GetResolvedRateLimit(request.RateLimit); var response = await _httpClient.ExecuteProxiedAsync(request, Definition); _eventAggregator.PublishEvent(new IndexerAuthEvent(Definition.Id, !response.HasHttpError, response.ElapsedTime)); diff --git a/src/NzbDrone.Core/Indexers/IndexerBaseSettings.cs b/src/NzbDrone.Core/Indexers/IndexerBaseSettings.cs index 5bada9b42..8a7a4637b 100644 --- a/src/NzbDrone.Core/Indexers/IndexerBaseSettings.cs +++ b/src/NzbDrone.Core/Indexers/IndexerBaseSettings.cs @@ -16,6 +16,10 @@ public IndexerCommonSettingsValidator() .GreaterThan(0) .When(c => c.GrabLimit.HasValue) .WithMessage("Should be greater than zero"); + + RuleFor(c => c.RequestDelay) + .InclusiveBetween(2, 60) + .When(c => c.RequestDelay.HasValue); } } @@ -29,6 +33,9 @@ public class IndexerBaseSettings [FieldDefinition(3, Type = FieldType.Select, Label = "IndexerSettingsLimitsUnit", SelectOptions = typeof(IndexerLimitsUnit), HelpText = "IndexerSettingsLimitsUnitHelpText", Advanced = true)] public int LimitsUnit { get; set; } = (int)IndexerLimitsUnit.Day; + + [FieldDefinition(4, Type = FieldType.Number, Label = "IndexerSettingsRequestDelay", HelpText = "IndexerSettingsRequestDelayHelpText", Advanced = true)] + public double? RequestDelay { get; set; } } public enum IndexerLimitsUnit diff --git a/src/NzbDrone.Core/Localization/Core/en.json b/src/NzbDrone.Core/Localization/Core/en.json index a789c6046..414eaa917 100644 --- a/src/NzbDrone.Core/Localization/Core/en.json +++ b/src/NzbDrone.Core/Localization/Core/en.json @@ -428,6 +428,8 @@ "IndexerSettingsPreferMagnetUrlHelpText": "When enabled, this indexer will prefer the use of magnet URLs for grabs with fallback to torrent links", "IndexerSettingsQueryLimit": "Query Limit", "IndexerSettingsQueryLimitHelpText": "The number of max queries as specified by the respective unit that {appName} will allow to the site", + "IndexerSettingsRequestDelay": "Minimum Request Delay", + "IndexerSettingsRequestDelayHelpText": "Minimum delay in seconds between requests for this indexer. The rate limit can only be increased by this setting. Setting a lower value than the default delay will be ignored.", "IndexerSettingsRssKey": "RSS Key", "IndexerSettingsSeedRatio": "Seed Ratio", "IndexerSettingsSeedRatioHelpText": "The ratio a torrent should reach before stopping, empty uses the download client's default. Ratio should be at least 1.0 and follow the indexers rules",