New: (Indexers) Per indexer api and download limits

pull/336/head
Qstick 3 years ago
parent 0fe2cf5c2d
commit 4116c10caa

@ -53,6 +53,16 @@ namespace NzbDrone.Common.Extensions
return dateTime >= afterDateTime && dateTime <= beforeDateTime;
}
public static DateTime EndOfDay(this DateTime date)
{
return new DateTime(date.Year, date.Month, date.Day, 23, 59, 59, 999);
}
public static DateTime StartOfDay(this DateTime date)
{
return new DateTime(date.Year, date.Month, date.Day, 0, 0, 0, 0);
}
public static DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
}
}

@ -12,5 +12,6 @@ namespace NzbDrone.Core.Test.IndexerTests
}
public string BaseUrl { get; set; }
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
}
}

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Dapper;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
@ -16,6 +17,7 @@ namespace NzbDrone.Core.History
History MostRecentForIndexer(int indexerId);
List<History> Since(DateTime date, HistoryEventType? eventType);
void Cleanup(int days);
int CountSince(int indexerId, DateTime date, List<HistoryEventType> eventTypes);
}
public class HistoryRepository : BasicRepository<History>, IHistoryRepository
@ -87,5 +89,21 @@ namespace NzbDrone.Core.History
return Query(builder).OrderBy(h => h.Date).ToList();
}
public int CountSince(int indexerId, DateTime date, List<HistoryEventType> eventTypes)
{
var builder = new SqlBuilder()
.SelectCount()
.Where<History>(x => x.IndexerId == indexerId)
.Where<History>(x => x.Date >= date)
.Where<History>(x => eventTypes.Contains(x.EventType));
var sql = builder.AddPageCountTemplate(typeof(History));
using (var conn = _database.OpenConnection())
{
return conn.ExecuteScalar<int>(sql.RawSql, sql.Parameters);
}
}
}
}

@ -24,6 +24,7 @@ namespace NzbDrone.Core.History
List<History> GetByIndexerId(int indexerId, HistoryEventType? eventType);
void UpdateMany(List<History> toUpdate);
List<History> Since(DateTime date, HistoryEventType? eventType);
int CountSince(int indexerId, DateTime date, List<HistoryEventType> eventTypes);
}
public class HistoryService : IHistoryService,
@ -205,5 +206,10 @@ namespace NzbDrone.Core.History
{
_historyRepository.Purge(vacuum: true);
}
public int CountSince(int indexerId, DateTime date, List<HistoryEventType> eventTypes)
{
return _historyRepository.CountSince(indexerId, date, eventTypes);
}
}
}

@ -22,7 +22,7 @@ namespace NzbDrone.Core.IndexerSearch
@"(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F\uFEFF\uFFFE\uFFFF]",
RegexOptions.Compiled);
public List<ReleaseInfo> Releases;
public List<ReleaseInfo> Releases { get; set; }
private static string RemoveInvalidXMLChars(string text)
{

@ -4,7 +4,6 @@ using System.Linq;
using System.Threading.Tasks;
using NLog;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Download;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Indexers.Events;
using NzbDrone.Core.IndexerSearch.Definitions;
@ -20,19 +19,19 @@ namespace NzbDrone.Core.IndexerSearch
public class NzbSearchService : ISearchForNzb
{
private readonly IIndexerLimitService _indexerLimitService;
private readonly IEventAggregator _eventAggregator;
private readonly IIndexerFactory _indexerFactory;
private readonly IDownloadMappingService _downloadMappingService;
private readonly Logger _logger;
public NzbSearchService(IEventAggregator eventAggregator,
IIndexerFactory indexerFactory,
IDownloadMappingService downloadMappingService,
IIndexerLimitService indexerLimitService,
Logger logger)
{
_eventAggregator = eventAggregator;
_indexerFactory = indexerFactory;
_downloadMappingService = downloadMappingService;
_indexerLimitService = indexerLimitService;
_logger = logger;
}
@ -163,6 +162,11 @@ namespace NzbDrone.Core.IndexerSearch
private async Task<IList<ReleaseInfo>> DispatchIndexer(Func<IIndexer, Task<IndexerPageableQueryResult>> searchAction, IIndexer indexer, SearchCriteriaBase criteriaBase)
{
if (_indexerLimitService.AtQueryLimit((IndexerDefinition)indexer.Definition))
{
return new List<ReleaseInfo>();
}
try
{
var indexerReports = await searchAction(indexer);

@ -499,6 +499,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Username", HelpText = "Site Username", Privacy = PrivacyLevel.UserName)]
public string Username { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -356,6 +356,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", Type = FieldType.Password, HelpText = "Site Password", Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -324,6 +324,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -37,6 +37,9 @@ namespace NzbDrone.Core.Indexers.Definitions.Avistaz
[FieldDefinition(4, Label = "PID", HelpText = "PID from My Account or My Profile page")]
public string Pid { get; set; }
[FieldDefinition(5)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -414,6 +414,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(5, Label = "Append Season", Type = FieldType.Checkbox, HelpText = "Append Season for Sonarr Compatibility")]
public bool AppendSeason { get; set; }
[FieldDefinition(6)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -256,6 +256,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "RSS Key", HelpText = "RSS Key from Site", Privacy = PrivacyLevel.ApiKey)]
public string RssKey { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -26,6 +26,9 @@ namespace NzbDrone.Core.Indexers.BroadcastheNet
[FieldDefinition(2, Label = "API Key", Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; }
[FieldDefinition(3)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -24,9 +24,12 @@ namespace NzbDrone.Core.Indexers.Cardigann
[FieldDefinition(0, Hidden = HiddenType.Hidden)]
public string DefinitionFile { get; set; }
[FieldDefinition(1, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")]
[FieldDefinition(2, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")]
public string BaseUrl { get; set; }
[FieldDefinition(1)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public Dictionary<string, object> ExtraFieldData { get; set; }
// Field 8 is used by TorznabSettings MinimumSeeders

@ -247,6 +247,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(2, Label = "API Key", HelpText = "API Key from Site", Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; }
[FieldDefinition(3)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -333,6 +333,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Passphrase", HelpText = "Pass from login cookie")]
public string Passphrase { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -31,6 +31,9 @@ namespace NzbDrone.Core.Indexers.FileList
[FieldDefinition(3, Label = "Passkey", Privacy = PrivacyLevel.ApiKey)]
public string Passkey { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -36,6 +36,9 @@ namespace NzbDrone.Core.Indexers.Gazelle
[FieldDefinition(4, Type = FieldType.Checkbox, Label = "Use Freeleech Token", HelpText = "Use Freeleech Token")]
public bool UseFreeleechToken { get; set; }
[FieldDefinition(5)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -419,6 +419,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Search Group Names", Type = FieldType.Checkbox, HelpText = "Search Group Names Only")]
public bool SearchGroupNames { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -38,6 +38,9 @@ namespace NzbDrone.Core.Indexers.HDBits
[FieldDefinition(5, Label = "Mediums", Type = FieldType.TagSelect, SelectOptions = typeof(HdBitsMedium), Advanced = true, HelpText = "Options: BluRay, Encode, Capture, Remux, WebDL. If unspecified, all options are used.")]
public IEnumerable<int> Mediums { get; set; }
[FieldDefinition(6)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -341,6 +341,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "Site Password")]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -381,6 +381,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", Type = FieldType.Password, HelpText = "Site Password", Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -36,6 +36,9 @@ namespace NzbDrone.Core.Indexers.Headphones
[FieldDefinition(3, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public virtual NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -370,6 +370,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "FreeLeech Only", Type = FieldType.Checkbox, Advanced = true, HelpText = "Search Freeleech torrents only")]
public bool FreeLeechOnly { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -356,6 +356,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", Type = FieldType.Password, HelpText = "Site Password", Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -238,6 +238,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(2, Label = "Apikey", HelpText = "Site ApiKey", Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; }
[FieldDefinition(3)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -381,6 +381,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Type = FieldType.Checkbox, Label = "Exclude VIP", HelpText = "Exclude VIP Torrents from search results")]
public bool ExcludeVip { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -297,6 +297,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(4, Label = "Two Factor Auth", HelpText = "Two-Factor Auth")]
public string TwoFactorAuth { get; set; }
[FieldDefinition(5)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -76,6 +76,9 @@ namespace NzbDrone.Core.Indexers.Newznab
[FieldDefinition(6, Label = "VIP Expiration", HelpText = "Enter date (yyyy-mm-dd) for VIP Expiration or blank, Prowlarr will notify 1 week from expiration of VIP")]
public string VipExpiration { get; set; }
[FieldDefinition(7)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public List<IndexerCategory> Categories { get; set; }
// Field 8 is used by TorznabSettings MinimumSeeders

@ -30,6 +30,9 @@ namespace NzbDrone.Core.Indexers.PassThePopcorn
[FieldDefinition(3, Label = "APIKey", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string APIKey { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -409,6 +409,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(4, Label = "Password", Privacy = PrivacyLevel.Password, Type = FieldType.Password, HelpText = "Site Password")]
public string Password { get; set; }
[FieldDefinition(5)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -29,6 +29,9 @@ namespace NzbDrone.Core.Indexers.Rarbg
[FieldDefinition(3, Type = FieldType.Captcha, Label = "CAPTCHA Token", HelpText = "CAPTCHA Clearance token used to handle CloudFlare Anti-DDOS measures on shared-ip VPNs.")]
public string CaptchaToken { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -349,6 +349,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", Type = FieldType.Password, HelpText = "Site Password", Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -191,6 +191,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(1, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")]
public string BaseUrl { get; set; }
[FieldDefinition(2)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -424,6 +424,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(4, Label = "Api Key", Hidden = HiddenType.Hidden)]
public string ApiKey { get; set; }
[FieldDefinition(5)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -248,6 +248,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(1, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")]
public string BaseUrl { get; set; }
[FieldDefinition(2)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -341,6 +341,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(2, Label = "Cookie", HelpText = "Site Cookie")]
public string Cookie { get; set; }
[FieldDefinition(3)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -318,6 +318,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password, HelpText = "Site Password")]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -266,6 +266,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(1, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")]
public string BaseUrl { get; set; }
[FieldDefinition(2)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult();

@ -287,6 +287,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(2, Label = "Cookie", HelpText = "Site Cookie")]
public string Cookie { get; set; }
[FieldDefinition(3)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -354,6 +354,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(4, Label = "FreeLeech Only", Type = FieldType.Checkbox, Advanced = true, HelpText = "Search Freeleech torrents only")]
public bool FreeLeechOnly { get; set; }
[FieldDefinition(5)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -28,6 +28,9 @@ namespace NzbDrone.Core.Indexers.TorrentPotato
[FieldDefinition(3, Label = "Passkey", HelpText = "The password you use at your Indexer.", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Passkey { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -360,6 +360,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", Type = FieldType.Password, HelpText = "Site Password", Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -26,6 +26,9 @@ namespace NzbDrone.Core.Indexers.Definitions.UNIT3D
[FieldDefinition(2, Label = "Api Key", HelpText = "Api key generated in My Security", Privacy = PrivacyLevel.ApiKey)]
public string ApiKey { get; set; }
[FieldDefinition(3)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -296,6 +296,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(1, Label = "Base Url", Type = FieldType.Select, SelectOptionsProviderAction = "getUrls", HelpText = "Select which baseurl Prowlarr will use for requests to the site")]
public string BaseUrl { get; set; }
[FieldDefinition(2)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -424,6 +424,9 @@ namespace NzbDrone.Core.Indexers.Definitions
[FieldDefinition(3, Label = "Password", HelpText = "Site Password", Type = FieldType.Password, Privacy = PrivacyLevel.Password)]
public string Password { get; set; }
[FieldDefinition(4)]
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
public NzbDroneValidationResult Validate()
{
return new NzbDroneValidationResult(Validator.Validate(this));

@ -15,7 +15,6 @@ using NzbDrone.Core.Indexers.Exceptions;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers
{
@ -52,11 +51,11 @@ namespace NzbDrone.Core.Indexers
public override Task<IndexerPageableQueryResult> Fetch(MovieSearchCriteria searchCriteria)
{
//TODO: Re-Enable when All Indexer Caps are fixed and tests don't fail
//if (!SupportsSearch)
//{
// return Task.FromResult(new Task<IndexerPageableQueryResult>());
//}
if (!SupportsSearch)
{
return Task.FromResult(new IndexerPageableQueryResult());
}
return FetchReleases(g => SetCookieFunctions(g).GetSearchRequests(searchCriteria));
}
@ -158,13 +157,6 @@ namespace NzbDrone.Core.Indexers
var pageableRequestChain = pageableRequestChainSelector(generator);
var fullyUpdated = false;
ReleaseInfo lastReleaseInfo = null;
if (isRecent)
{
lastReleaseInfo = _indexerStatusService.GetLastRssSyncReleaseInfo(Definition.Id);
}
for (int i = 0; i < pageableRequestChain.Tiers; i++)
{
var pageableRequests = pageableRequestChain.GetTier(i);
@ -187,33 +179,6 @@ namespace NzbDrone.Core.Indexers
pagedReleases.AddRange(page.Releases);
if (isRecent && page.Releases.Any())
{
if (lastReleaseInfo == null)
{
fullyUpdated = true;
break;
}
var oldestReleaseDate = page.Releases.Select(v => v.PublishDate).Min();
if (oldestReleaseDate < lastReleaseInfo.PublishDate || page.Releases.Any(v => v.DownloadUrl == lastReleaseInfo.DownloadUrl))
{
fullyUpdated = true;
break;
}
if (pagedReleases.Count >= MaxNumResultsPerQuery &&
oldestReleaseDate < DateTime.UtcNow - TimeSpan.FromHours(24))
{
fullyUpdated = false;
break;
}
}
else if (pagedReleases.Count >= MaxNumResultsPerQuery)
{
break;
}
if (!IsFullPage(page.Releases, pageSize))
{
break;
@ -229,21 +194,6 @@ namespace NzbDrone.Core.Indexers
}
}
if (isRecent && !releases.Empty())
{
var ordered = releases.OrderByDescending(v => v.PublishDate).ToList();
if (!fullyUpdated && lastReleaseInfo != null)
{
var gapStart = lastReleaseInfo.PublishDate;
var gapEnd = ordered.Last().PublishDate;
_logger.Warn("Indexer {0} rss sync didn't cover the period between {1} and {2} UTC. Search may be required.", Definition.Name, gapStart, gapEnd);
}
lastReleaseInfo = ordered.First();
_indexerStatusService.UpdateRssSyncStatus(Definition.Id, lastReleaseInfo);
}
_indexerStatusService.RecordSuccess(Definition.Id);
}
catch (WebException webException)

@ -5,5 +5,6 @@ namespace NzbDrone.Core.Indexers
public interface IIndexerSettings : IProviderConfig
{
string BaseUrl { get; set; }
IndexerBaseSettings BaseSettings { get; set; }
}
}

@ -0,0 +1,23 @@
using FluentValidation;
using NzbDrone.Core.Annotations;
namespace NzbDrone.Core.Indexers
{
public class IndexerCommonSettingsValidator : AbstractValidator<IndexerBaseSettings>
{
public IndexerCommonSettingsValidator()
{
}
}
public class IndexerBaseSettings
{
private static readonly IndexerCommonSettingsValidator Validator = new IndexerCommonSettingsValidator();
[FieldDefinition(1, Type = FieldType.Number, Label = "Query Limit", HelpText = "The number of queries per day Prowlarr will allow to the site", Advanced = true)]
public int? QueryLimit { get; set; }
[FieldDefinition(2, Type = FieldType.Number, Label = "Grab Limit", HelpText = "The number of grabs per day Prowlarr will allow to the site", Advanced = true)]
public int? GrabLimit { get; set; }
}
}

@ -1,7 +0,0 @@
namespace NzbDrone.Core.Indexers
{
public static class IndexerDefaults
{
public const int MINIMUM_SEEDERS = 1;
}
}

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using NLog;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.History;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Indexers
{
public interface IIndexerLimitService
{
bool AtDownloadLimit(IndexerDefinition indexer);
bool AtQueryLimit(IndexerDefinition indexer);
}
public class IndexerLimitService : IIndexerLimitService
{
private readonly IEventAggregator _eventAggregator;
private readonly IHistoryService _historyService;
private readonly Logger _logger;
public IndexerLimitService(IEventAggregator eventAggregator,
IHistoryService historyService,
Logger logger)
{
_eventAggregator = eventAggregator;
_historyService = historyService;
_logger = logger;
}
public bool AtDownloadLimit(IndexerDefinition indexer)
{
if (indexer.Id > 0 && ((IIndexerSettings)indexer.Settings).BaseSettings.GrabLimit.HasValue)
{
var queryCount = _historyService.CountSince(indexer.Id, DateTime.Now.StartOfDay(), new List<HistoryEventType> { HistoryEventType.ReleaseGrabbed });
if (queryCount > ((IIndexerSettings)indexer.Settings).BaseSettings.GrabLimit)
{
_logger.Info("Indexer {0} has exceeded maximum grab limit for today", indexer.Name);
return true;
}
}
return false;
}
public bool AtQueryLimit(IndexerDefinition indexer)
{
if (indexer.Id > 0 && ((IIndexerSettings)indexer.Settings).BaseSettings.QueryLimit.HasValue)
{
var queryCount = _historyService.CountSince(indexer.Id, DateTime.Now.StartOfDay(), new List<HistoryEventType> { HistoryEventType.IndexerQuery, HistoryEventType.IndexerRss });
if (queryCount > ((IIndexerSettings)indexer.Settings).BaseSettings.QueryLimit)
{
_logger.Info("Indexer {0} has exceeded maximum query limit for today", indexer.Name);
return true;
}
}
return false;
}
}
}

@ -3,11 +3,13 @@ using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Http;
using NzbDrone.Core.Download;
using NzbDrone.Core.History;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.Parser;
@ -24,16 +26,19 @@ namespace NzbDrone.Api.V1.Indexers
{
private IIndexerFactory _indexerFactory { get; set; }
private ISearchForNzb _nzbSearchService { get; set; }
private IIndexerLimitService _indexerLimitService { get; set; }
private IDownloadMappingService _downloadMappingService { get; set; }
private IDownloadService _downloadService { get; set; }
public NewznabController(IndexerFactory indexerFactory,
ISearchForNzb nzbSearchService,
IIndexerLimitService indexerLimitService,
IDownloadMappingService downloadMappingService,
IDownloadService downloadService)
{
_indexerFactory = indexerFactory;
_nzbSearchService = nzbSearchService;
_indexerLimitService = indexerLimitService;
_downloadMappingService = downloadMappingService;
_downloadService = downloadService;
}
@ -49,7 +54,7 @@ namespace NzbDrone.Api.V1.Indexers
if (requestType.IsNullOrWhiteSpace())
{
throw new BadRequestException("Missing Function Parameter");
return Content(CreateErrorXML(200, "Missing parameter (t)"), "application/rss+xml");
}
request.imdbid = request.imdbid?.TrimStart('t') ?? null;
@ -58,7 +63,7 @@ namespace NzbDrone.Api.V1.Indexers
{
if (!int.TryParse(request.imdbid, out var imdb) || imdb == 0)
{
throw new BadRequestException("Invalid Value for ImdbId");
return Content(CreateErrorXML(201, "Incorrect parameter (imdbid)"), "application/rss+xml");
}
}
@ -95,40 +100,46 @@ namespace NzbDrone.Api.V1.Indexers
}
}
var indexer = _indexerFactory.Get(id);
var indexerDef = _indexerFactory.Get(id);
if (indexer == null)
if (indexerDef == null)
{
throw new NotFoundException("Indexer Not Found");
}
var indexerInstance = _indexerFactory.GetInstance(indexer);
var indexer = _indexerFactory.GetInstance(indexerDef);
//TODO Optimize this so it's not called here and in NzbSearchService (for manual search)
if (_indexerLimitService.AtQueryLimit(indexerDef))
{
return Content(CreateErrorXML(500, $"Request limit reached ({((IIndexerSettings)indexer.Definition.Settings).BaseSettings.QueryLimit})"), "application/rss+xml");
}
switch (requestType)
{
case "caps":
var caps = indexerInstance.GetCapabilities();
var caps = indexer.GetCapabilities();
return Content(caps.ToXml(), "application/rss+xml");
case "search":
case "tvsearch":
case "music":
case "book":
case "movie":
var results = await _nzbSearchService.Search(request, new List<int> { indexer.Id }, false);
var results = await _nzbSearchService.Search(request, new List<int> { indexerDef.Id }, false);
foreach (var result in results.Releases)
{
result.DownloadUrl = result.DownloadUrl != null ? _downloadMappingService.ConvertToProxyLink(new Uri(result.DownloadUrl), request.server, indexer.Id, result.Title).ToString() : null;
result.DownloadUrl = result.DownloadUrl != null ? _downloadMappingService.ConvertToProxyLink(new Uri(result.DownloadUrl), request.server, indexerDef.Id, result.Title).ToString() : null;
if (result.DownloadProtocol == DownloadProtocol.Torrent)
{
((TorrentInfo)result).MagnetUrl = ((TorrentInfo)result).MagnetUrl != null ? _downloadMappingService.ConvertToProxyLink(new Uri(((TorrentInfo)result).MagnetUrl), request.server, indexer.Id, result.Title).ToString() : null;
((TorrentInfo)result).MagnetUrl = ((TorrentInfo)result).MagnetUrl != null ? _downloadMappingService.ConvertToProxyLink(new Uri(((TorrentInfo)result).MagnetUrl), request.server, indexerDef.Id, result.Title).ToString() : null;
}
}
return Content(results.ToXml(indexerInstance.Protocol), "application/rss+xml");
return Content(results.ToXml(indexer.Protocol), "application/rss+xml");
default:
throw new BadRequestException("Function Not Available");
return Content(CreateErrorXML(202, $"No such function ({requestType})"), "application/rss+xml");
}
}
@ -139,6 +150,11 @@ namespace NzbDrone.Api.V1.Indexers
var indexerDef = _indexerFactory.Get(id);
var indexer = _indexerFactory.GetInstance(indexerDef);
if (_indexerLimitService.AtDownloadLimit(indexerDef))
{
throw new BadRequestException("Grab limit reached");
}
if (link.IsNullOrWhiteSpace() || file.IsNullOrWhiteSpace())
{
throw new BadRequestException("Invalid Prowlarr link");
@ -146,7 +162,7 @@ namespace NzbDrone.Api.V1.Indexers
file = WebUtility.UrlDecode(file);
if (indexer == null)
if (indexerDef == null)
{
throw new NotFoundException("Indexer Not Found");
}
@ -186,5 +202,16 @@ namespace NzbDrone.Api.V1.Indexers
return File(downloadBytes, contentType, filename);
}
public static string CreateErrorXML(int code, string description)
{
var xdoc = new XDocument(
new XDeclaration("1.0", "UTF-8", null),
new XElement("error",
new XAttribute("code", code.ToString()),
new XAttribute("description", description)));
return xdoc.Declaration + Environment.NewLine + xdoc;
}
}
}

Loading…
Cancel
Save