parent
38ba810ae8
commit
0c45eb68fa
@ -0,0 +1,14 @@
|
||||
using FluentMigrator;
|
||||
using NzbDrone.Core.Datastore.Migration.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Datastore.Migration
|
||||
{
|
||||
[Migration(24)]
|
||||
public class newznab_yml : NzbDroneMigrationBase
|
||||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Update.Table("Indexers").Set(new { Implementation = "GenericNewznab", ConfigContract = "GenericNewznabSettings" }).Where(new { Implementation = "Newznab" });
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using FluentValidation.Results;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
public class GenericNewznab : UsenetIndexerBase<GenericNewznabSettings>
|
||||
{
|
||||
private readonly INewznabCapabilitiesProvider _capabilitiesProvider;
|
||||
|
||||
public override string Name => "Generic Newznab";
|
||||
public override string[] IndexerUrls => GetBaseUrlFromSettings();
|
||||
public override string Description => "Newznab is an API search specification for Usenet";
|
||||
public override bool FollowRedirect => true;
|
||||
public override bool SupportsRedirect => true;
|
||||
|
||||
public override DownloadProtocol Protocol => DownloadProtocol.Usenet;
|
||||
public override IndexerPrivacy Privacy => IndexerPrivacy.Private;
|
||||
|
||||
public override IndexerCapabilities Capabilities { get => GetCapabilitiesFromSettings(); protected set => base.Capabilities = value; }
|
||||
|
||||
public override int PageSize => _capabilitiesProvider.GetCapabilities(Settings, Definition).LimitsDefault.Value;
|
||||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new GenericNewznabRequestGenerator(_capabilitiesProvider)
|
||||
{
|
||||
PageSize = PageSize,
|
||||
Settings = Settings
|
||||
};
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new GenericNewznabRssParser(Settings.Categories);
|
||||
}
|
||||
|
||||
public string[] GetBaseUrlFromSettings()
|
||||
{
|
||||
var baseUrl = "";
|
||||
|
||||
if (Definition == null || Settings == null || Settings.Categories == null)
|
||||
{
|
||||
return new string[] { baseUrl };
|
||||
}
|
||||
|
||||
return new string[] { Settings.BaseUrl };
|
||||
}
|
||||
|
||||
protected override GenericNewznabSettings GetDefaultBaseUrl(GenericNewznabSettings settings)
|
||||
{
|
||||
return settings;
|
||||
}
|
||||
|
||||
public IndexerCapabilities GetCapabilitiesFromSettings()
|
||||
{
|
||||
var caps = new IndexerCapabilities();
|
||||
|
||||
if (Definition == null || Settings == null || Settings.Categories == null)
|
||||
{
|
||||
return caps;
|
||||
}
|
||||
|
||||
foreach (var category in Settings.Categories)
|
||||
{
|
||||
caps.Categories.AddCategoryMapping(category.Name, category);
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
public override IndexerCapabilities GetCapabilities()
|
||||
{
|
||||
// Newznab uses different Caps per site, so we need to cache them to db on first indexer add to prevent issues with loading UI and pulling caps every time.
|
||||
return _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
}
|
||||
|
||||
public override IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return GetDefinition("Generic Newznab", GetSettings(""));
|
||||
}
|
||||
}
|
||||
|
||||
public GenericNewznab(INewznabCapabilitiesProvider capabilitiesProvider, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger)
|
||||
{
|
||||
_capabilitiesProvider = capabilitiesProvider;
|
||||
}
|
||||
|
||||
private IndexerDefinition GetDefinition(string name, GenericNewznabSettings settings)
|
||||
{
|
||||
return new IndexerDefinition
|
||||
{
|
||||
Enable = true,
|
||||
Name = name,
|
||||
Implementation = GetType().Name,
|
||||
Settings = settings,
|
||||
Protocol = DownloadProtocol.Usenet,
|
||||
Privacy = IndexerPrivacy.Private,
|
||||
SupportsRss = SupportsRss,
|
||||
SupportsSearch = SupportsSearch,
|
||||
SupportsRedirect = SupportsRedirect,
|
||||
Capabilities = Capabilities
|
||||
};
|
||||
}
|
||||
|
||||
private GenericNewznabSettings GetSettings(string url, string apiPath = null)
|
||||
{
|
||||
var settings = new GenericNewznabSettings { BaseUrl = url };
|
||||
|
||||
if (apiPath.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
settings.ApiPath = apiPath;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected override async Task Test(List<ValidationFailure> failures)
|
||||
{
|
||||
await base.Test(failures);
|
||||
if (failures.HasErrors())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
failures.AddIfNotNull(TestCapabilities());
|
||||
}
|
||||
|
||||
protected static List<int> CategoryIds(IndexerCapabilitiesCategories categories)
|
||||
{
|
||||
var l = categories.GetTorznabCategoryTree().Select(c => c.Id).ToList();
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
protected virtual ValidationFailure TestCapabilities()
|
||||
{
|
||||
try
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
|
||||
if (capabilities.SearchParams != null && capabilities.SearchParams.Contains(SearchParam.Q))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (capabilities.MovieSearchParams != null &&
|
||||
new[] { MovieSearchParam.Q, MovieSearchParam.ImdbId }.Any(v => capabilities.MovieSearchParams.Contains(v)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (capabilities.TvSearchParams != null &&
|
||||
new[] { TvSearchParam.Q, TvSearchParam.TvdbId, TvSearchParam.TmdbId, TvSearchParam.RId }.Any(v => capabilities.TvSearchParams.Contains(v)) &&
|
||||
new[] { TvSearchParam.Season, TvSearchParam.Ep }.All(v => capabilities.TvSearchParams.Contains(v)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (capabilities.MusicSearchParams != null &&
|
||||
new[] { MusicSearchParam.Q, MusicSearchParam.Artist, MusicSearchParam.Album }.Any(v => capabilities.MusicSearchParams.Contains(v)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (capabilities.BookSearchParams != null &&
|
||||
new[] { BookSearchParam.Q, BookSearchParam.Author, BookSearchParam.Title }.Any(v => capabilities.BookSearchParams.Contains(v)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ValidationFailure(string.Empty, "This indexer does not support searching for tv, music, or movies :(. Tell your indexer staff to enable this or force add the indexer by disabling search, adding the indexer and then enabling it again.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Warn(ex, "Unable to connect to indexer: " + ex.Message);
|
||||
|
||||
return new ValidationFailure(string.Empty, "Unable to connect to indexer, check the log for more details");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,292 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using DryIoc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
public class GenericNewznabRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
private readonly INewznabCapabilitiesProvider _capabilitiesProvider;
|
||||
public int MaxPages { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public GenericNewznabSettings Settings { get; set; }
|
||||
public ProviderDefinition Definition { get; set; }
|
||||
|
||||
public GenericNewznabRequestGenerator(INewznabCapabilitiesProvider capabilitiesProvider)
|
||||
{
|
||||
_capabilitiesProvider = capabilitiesProvider;
|
||||
|
||||
MaxPages = 30;
|
||||
PageSize = 100;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = new NameValueCollection();
|
||||
|
||||
if (searchCriteria.TmdbId.HasValue && capabilities.MovieSearchTmdbAvailable)
|
||||
{
|
||||
parameters.Add("tmdbid", searchCriteria.TmdbId.Value.ToString());
|
||||
}
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace() && capabilities.MovieSearchImdbAvailable)
|
||||
{
|
||||
parameters.Add("imdbid", searchCriteria.ImdbId);
|
||||
}
|
||||
|
||||
if (searchCriteria.TraktId.HasValue && capabilities.MovieSearchTraktAvailable)
|
||||
{
|
||||
parameters.Add("traktid", searchCriteria.TraktId.ToString());
|
||||
}
|
||||
|
||||
//Workaround issue with Sphinx search returning garbage results on some indexers. If we don't use id parameters, fallback to t=search
|
||||
if (parameters.Count == 0)
|
||||
{
|
||||
searchCriteria.SearchType = "search";
|
||||
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.SearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.MovieSearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria,
|
||||
parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = new NameValueCollection();
|
||||
|
||||
if (searchCriteria.Artist.IsNotNullOrWhiteSpace() && capabilities.MusicSearchArtistAvailable)
|
||||
{
|
||||
parameters.Add("artist", searchCriteria.Artist);
|
||||
}
|
||||
|
||||
if (searchCriteria.Album.IsNotNullOrWhiteSpace() && capabilities.MusicSearchAlbumAvailable)
|
||||
{
|
||||
parameters.Add("album", searchCriteria.Album);
|
||||
}
|
||||
|
||||
//Workaround issue with Sphinx search returning garbage results on some indexers. If we don't use id parameters, fallback to t=search
|
||||
if (parameters.Count == 0)
|
||||
{
|
||||
searchCriteria.SearchType = "search";
|
||||
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.SearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.MusicSearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria,
|
||||
parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = new NameValueCollection();
|
||||
|
||||
if (searchCriteria.TvdbId.HasValue && capabilities.TvSearchTvdbAvailable)
|
||||
{
|
||||
parameters.Add("tvdbid", searchCriteria.TvdbId.Value.ToString());
|
||||
}
|
||||
|
||||
if (searchCriteria.TmdbId.HasValue && capabilities.TvSearchTvdbAvailable)
|
||||
{
|
||||
parameters.Add("tmdbid", searchCriteria.TvdbId.Value.ToString());
|
||||
}
|
||||
|
||||
if (searchCriteria.ImdbId.IsNotNullOrWhiteSpace() && capabilities.TvSearchImdbAvailable)
|
||||
{
|
||||
parameters.Add("imdbid", searchCriteria.ImdbId);
|
||||
}
|
||||
|
||||
if (searchCriteria.TvMazeId.HasValue && capabilities.TvSearchTvMazeAvailable)
|
||||
{
|
||||
parameters.Add("tvmazeid", searchCriteria.TvMazeId.ToString());
|
||||
}
|
||||
|
||||
if (searchCriteria.RId.HasValue && capabilities.TvSearchTvRageAvailable)
|
||||
{
|
||||
parameters.Add("rid", searchCriteria.RId.ToString());
|
||||
}
|
||||
|
||||
if (searchCriteria.Season.HasValue && capabilities.TvSearchSeasonAvailable)
|
||||
{
|
||||
parameters.Add("season", NewznabifySeasonNumber(searchCriteria.Season.Value));
|
||||
}
|
||||
|
||||
if (searchCriteria.Episode.IsNotNullOrWhiteSpace() && capabilities.TvSearchEpAvailable)
|
||||
{
|
||||
parameters.Add("ep", searchCriteria.Episode);
|
||||
}
|
||||
|
||||
//Workaround issue with Sphinx search returning garbage results on some indexers. If we don't use id parameters, fallback to t=search
|
||||
if (parameters.Count == 0)
|
||||
{
|
||||
searchCriteria.SearchType = "search";
|
||||
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.SearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.TvSearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria,
|
||||
parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = new NameValueCollection();
|
||||
|
||||
if (searchCriteria.Author.IsNotNullOrWhiteSpace() && capabilities.BookSearchAuthorAvailable)
|
||||
{
|
||||
parameters.Add("author", searchCriteria.Author);
|
||||
}
|
||||
|
||||
if (searchCriteria.Title.IsNotNullOrWhiteSpace() && capabilities.BookSearchTitleAvailable)
|
||||
{
|
||||
parameters.Add("title", searchCriteria.Title);
|
||||
}
|
||||
|
||||
//Workaround issue with Sphinx search returning garbage results on some indexers. If we don't use id parameters, fallback to t=search
|
||||
if (parameters.Count == 0)
|
||||
{
|
||||
searchCriteria.SearchType = "search";
|
||||
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.SearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.BookSearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria,
|
||||
parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
var parameters = new NameValueCollection();
|
||||
|
||||
if (searchCriteria.SearchTerm.IsNotNullOrWhiteSpace() && capabilities.SearchAvailable)
|
||||
{
|
||||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(SearchCriteriaBase searchCriteria, NameValueCollection parameters)
|
||||
{
|
||||
var baseUrl = string.Format("{0}{1}?t={2}&extended=1", Settings.BaseUrl.TrimEnd('/'), Settings.ApiPath.TrimEnd('/'), searchCriteria.SearchType);
|
||||
var categories = searchCriteria.Categories;
|
||||
|
||||
if (categories != null && categories.Any())
|
||||
{
|
||||
var categoriesQuery = string.Join(",", categories.Distinct());
|
||||
baseUrl += string.Format("&cat={0}", categoriesQuery);
|
||||
}
|
||||
|
||||
if (Settings.AdditionalParameters.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
baseUrl += Settings.AdditionalParameters;
|
||||
}
|
||||
|
||||
if (Settings.ApiKey.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
baseUrl += "&apikey=" + Settings.ApiKey;
|
||||
}
|
||||
|
||||
if (searchCriteria.Limit.HasValue)
|
||||
{
|
||||
parameters.Add("limit", searchCriteria.Limit.ToString());
|
||||
}
|
||||
|
||||
if (searchCriteria.Offset.HasValue)
|
||||
{
|
||||
parameters.Add("offset", searchCriteria.Offset.ToString());
|
||||
}
|
||||
|
||||
var request = new IndexerRequest(string.Format("{0}&{1}", baseUrl, parameters.GetQueryString()), HttpAccept.Rss);
|
||||
request.HttpRequest.AllowAutoRedirect = true;
|
||||
|
||||
yield return request;
|
||||
}
|
||||
|
||||
private static string NewsnabifyTitle(string title)
|
||||
{
|
||||
return title.Replace("+", "%20");
|
||||
}
|
||||
|
||||
// Temporary workaround for NNTMux considering season=0 -> null. '00' should work on existing newznab indexers.
|
||||
private static string NewznabifySeasonNumber(int seasonNumber)
|
||||
{
|
||||
return seasonNumber == 0 ? "00" : seasonNumber.ToString();
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
public class GenericNewznabSettingsValidator : AbstractValidator<GenericNewznabSettings>
|
||||
{
|
||||
private static readonly string[] ApiKeyWhiteList =
|
||||
{
|
||||
"nzbs.org",
|
||||
"nzb.su",
|
||||
"dognzb.cr",
|
||||
"nzbplanet.net",
|
||||
"nzbid.org",
|
||||
"nzbndx.com",
|
||||
"nzbindex.in"
|
||||
};
|
||||
|
||||
private static bool ShouldHaveApiKey(GenericNewznabSettings settings)
|
||||
{
|
||||
if (settings.BaseUrl == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ApiKeyWhiteList.Any(c => settings.BaseUrl.ToLowerInvariant().Contains(c));
|
||||
}
|
||||
|
||||
private static readonly Regex AdditionalParametersRegex = new Regex(@"(&.+?\=.+?)+", RegexOptions.Compiled);
|
||||
|
||||
public GenericNewznabSettingsValidator()
|
||||
{
|
||||
RuleFor(c => c.BaseUrl).ValidRootUrl();
|
||||
RuleFor(c => c.ApiPath).ValidUrlBase("/api");
|
||||
RuleFor(c => c.ApiKey).NotEmpty().When(ShouldHaveApiKey);
|
||||
RuleFor(c => c.AdditionalParameters).Matches(AdditionalParametersRegex)
|
||||
.When(c => !c.AdditionalParameters.IsNullOrWhiteSpace());
|
||||
|
||||
RuleFor(c => c.VipExpiration).Must(c => c.IsValidDate())
|
||||
.When(c => c.VipExpiration.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Correctly formatted date is required");
|
||||
|
||||
RuleFor(c => c.VipExpiration).Must(c => c.IsFutureDate())
|
||||
.When(c => c.VipExpiration.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Must be a future date");
|
||||
}
|
||||
}
|
||||
|
||||
public class GenericNewznabSettings : IIndexerSettings
|
||||
{
|
||||
private static readonly GenericNewznabSettingsValidator Validator = new GenericNewznabSettingsValidator();
|
||||
|
||||
public GenericNewznabSettings()
|
||||
{
|
||||
ApiPath = "/api";
|
||||
VipExpiration = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "URL")]
|
||||
public string BaseUrl { get; set; }
|
||||
|
||||
[FieldDefinition(1, Label = "API Path", HelpText = "Path to the api, usually /api", Advanced = true)]
|
||||
public string ApiPath { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "API Key", HelpText = "Site API Key", Privacy = PrivacyLevel.ApiKey)]
|
||||
public string ApiKey { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Additional Parameters", HelpText = "Additional Newznab parameters", Advanced = true)]
|
||||
public string AdditionalParameters { get; set; }
|
||||
|
||||
[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
|
||||
// If you need to add another field here, update TorznabSettings as well and this comment
|
||||
public virtual NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Settings
|
||||
{
|
||||
public interface IYmlIndexerSettings : IIndexerSettings
|
||||
{
|
||||
public string DefinitionFile { get; set; }
|
||||
}
|
||||
}
|
Loading…
Reference in new issue