mirror of
https://github.com/Prowlarr/Prowlarr.git
synced 2025-04-18 19:15:10 -04:00
Newznab to Yml
This commit is contained in:
parent
38ba810ae8
commit
0c45eb68fa
22 changed files with 806 additions and 278 deletions
|
@ -15,14 +15,14 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||
[TestFixture]
|
||||
public class NewznabCapabilitiesProviderFixture : CoreTest<NewznabCapabilitiesProvider>
|
||||
{
|
||||
private NewznabSettings _settings;
|
||||
private GenericNewznabSettings _settings;
|
||||
private IndexerDefinition _definition;
|
||||
private string _caps;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_settings = new NewznabSettings()
|
||||
_settings = new GenericNewznabSettings()
|
||||
{
|
||||
BaseUrl = "http://indxer.local"
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||
|
||||
_caps = new IndexerCapabilities();
|
||||
Mocker.GetMock<INewznabCapabilitiesProvider>()
|
||||
.Setup(v => v.GetCapabilities(It.IsAny<NewznabSettings>(), It.IsAny<IndexerDefinition>()))
|
||||
.Setup(v => v.GetCapabilities(It.IsAny<GenericNewznabSettings>(), It.IsAny<IndexerDefinition>()))
|
||||
.Returns(_caps);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ using NzbDrone.Core.Test.Framework;
|
|||
|
||||
namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
||||
{
|
||||
public class NewznabRequestGeneratorFixture : CoreTest<NewznabRequestGenerator>
|
||||
public class NewznabRequestGeneratorFixture : CoreTest<GenericNewznabRequestGenerator>
|
||||
{
|
||||
private MovieSearchCriteria _movieSearchCriteria;
|
||||
private TvSearchCriteria _tvSearchCriteria;
|
||||
|
@ -19,7 +19,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
Subject.Settings = new NewznabSettings()
|
||||
Subject.Settings = new GenericNewznabSettings()
|
||||
{
|
||||
BaseUrl = "http://127.0.0.1:1234/",
|
||||
ApiKey = "abcd",
|
||||
|
@ -41,7 +41,7 @@ namespace NzbDrone.Core.Test.IndexerTests.NewznabTests
|
|||
_capabilities = new IndexerCapabilities();
|
||||
|
||||
Mocker.GetMock<INewznabCapabilitiesProvider>()
|
||||
.Setup(v => v.GetCapabilities(It.IsAny<NewznabSettings>(), It.IsAny<IndexerDefinition>()))
|
||||
.Setup(v => v.GetCapabilities(It.IsAny<GenericNewznabSettings>(), It.IsAny<IndexerDefinition>()))
|
||||
.Returns(_capabilities);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace NzbDrone.Core.Test.IndexerTests.TorznabTests
|
|||
|
||||
_caps = new IndexerCapabilities();
|
||||
Mocker.GetMock<INewznabCapabilitiesProvider>()
|
||||
.Setup(v => v.GetCapabilities(It.IsAny<NewznabSettings>(), It.IsAny<IndexerDefinition>()))
|
||||
.Setup(v => v.GetCapabilities(It.IsAny<GenericNewznabSettings>(), It.IsAny<IndexerDefinition>()))
|
||||
.Returns(_caps);
|
||||
}
|
||||
|
||||
|
|
14
src/NzbDrone.Core/Datastore/Migration/024_newznab_yml.cs
Normal file
14
src/NzbDrone.Core/Datastore/Migration/024_newznab_yml.cs
Normal file
|
@ -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" });
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,7 +19,8 @@ namespace NzbDrone.Core.IndexerVersions
|
|||
{
|
||||
public interface IIndexerDefinitionUpdateService
|
||||
{
|
||||
List<CardigannMetaDefinition> All();
|
||||
List<IndexerMetaDefinition> All();
|
||||
List<IndexerMetaDefinition> AllForImplementation(string implementation);
|
||||
CardigannDefinition GetCachedDefinition(string fileKey);
|
||||
List<string> GetBlocklist();
|
||||
}
|
||||
|
@ -28,8 +29,8 @@ namespace NzbDrone.Core.IndexerVersions
|
|||
{
|
||||
/* Update Service will fall back if version # does not exist for an indexer per Ta */
|
||||
|
||||
private const string DEFINITION_BRANCH = "master";
|
||||
private const int DEFINITION_VERSION = 7;
|
||||
private const string DEFINITION_BRANCH = "newznab-yml";
|
||||
private const int DEFINITION_VERSION = 8;
|
||||
|
||||
//Used when moving yml to C#
|
||||
private readonly List<string> _defintionBlocklist = new List<string>()
|
||||
|
@ -78,9 +79,9 @@ namespace NzbDrone.Core.IndexerVersions
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<CardigannMetaDefinition> All()
|
||||
public List<IndexerMetaDefinition> All()
|
||||
{
|
||||
var indexerList = new List<CardigannMetaDefinition>();
|
||||
var indexerList = new List<IndexerMetaDefinition>();
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -88,7 +89,7 @@ namespace NzbDrone.Core.IndexerVersions
|
|||
try
|
||||
{
|
||||
var request = new HttpRequest($"https://indexers.prowlarr.com/{DEFINITION_BRANCH}/{DEFINITION_VERSION}");
|
||||
var response = _httpClient.Get<List<CardigannMetaDefinition>>(request);
|
||||
var response = _httpClient.Get<List<IndexerMetaDefinition>>(request);
|
||||
indexerList = response.Resource.Where(i => !_defintionBlocklist.Contains(i.File)).ToList();
|
||||
}
|
||||
catch
|
||||
|
@ -111,6 +112,11 @@ namespace NzbDrone.Core.IndexerVersions
|
|||
return indexerList;
|
||||
}
|
||||
|
||||
public List<IndexerMetaDefinition> AllForImplementation(string implementation)
|
||||
{
|
||||
return All().Where(d => d.Implementation == implementation.ToLower()).ToList();
|
||||
}
|
||||
|
||||
public CardigannDefinition GetCachedDefinition(string fileKey)
|
||||
{
|
||||
if (string.IsNullOrEmpty(fileKey))
|
||||
|
@ -128,7 +134,7 @@ namespace NzbDrone.Core.IndexerVersions
|
|||
return _defintionBlocklist;
|
||||
}
|
||||
|
||||
private List<CardigannMetaDefinition> ReadDefinitionsFromDisk(List<CardigannMetaDefinition> defs, string path, SearchOption options = SearchOption.TopDirectoryOnly)
|
||||
private List<IndexerMetaDefinition> ReadDefinitionsFromDisk(List<IndexerMetaDefinition> defs, string path, SearchOption options = SearchOption.TopDirectoryOnly)
|
||||
{
|
||||
var indexerList = defs;
|
||||
|
||||
|
@ -145,7 +151,7 @@ namespace NzbDrone.Core.IndexerVersions
|
|||
try
|
||||
{
|
||||
var definitionString = File.ReadAllText(file.FullName);
|
||||
var definition = _deserializer.Deserialize<CardigannMetaDefinition>(definitionString);
|
||||
var definition = _deserializer.Deserialize<IndexerMetaDefinition>(definitionString);
|
||||
|
||||
definition.File = Path.GetFileNameWithoutExtension(file.Name);
|
||||
|
||||
|
@ -243,6 +249,11 @@ namespace NzbDrone.Core.IndexerVersions
|
|||
definition.Login.Method = "form";
|
||||
}
|
||||
|
||||
if (definition.Search == null)
|
||||
{
|
||||
definition.Search = new SearchBlock();
|
||||
}
|
||||
|
||||
if (definition.Search.Paths == null)
|
||||
{
|
||||
definition.Search.Paths = new List<SearchPathBlock>();
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Cardigann
|
||||
namespace NzbDrone.Core.IndexerVersions
|
||||
{
|
||||
public class CardigannMetaDefinition
|
||||
public class IndexerMetaDefinition
|
||||
{
|
||||
public CardigannMetaDefinition()
|
||||
public IndexerMetaDefinition()
|
||||
{
|
||||
Legacylinks = new List<string>();
|
||||
}
|
||||
|
@ -13,6 +14,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
|||
public string File { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string Implementation { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string Language { get; set; }
|
||||
public string Encoding { get; set; }
|
|
@ -78,7 +78,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
|||
{
|
||||
get
|
||||
{
|
||||
foreach (var def in _definitionService.All())
|
||||
foreach (var def in _definitionService.AllForImplementation(GetType().Name))
|
||||
{
|
||||
yield return GetDefinition(def);
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
|||
_generatorCache = cacheManager.GetRollingCache<CardigannRequestGenerator>(GetType(), "CardigannGeneratorCache", TimeSpan.FromMinutes(5));
|
||||
}
|
||||
|
||||
private IndexerDefinition GetDefinition(CardigannMetaDefinition definition)
|
||||
private IndexerDefinition GetDefinition(IndexerMetaDefinition definition)
|
||||
{
|
||||
var defaultSettings = new List<SettingsField>
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace NzbDrone.Core.Indexers.Cardigann
|
|||
}
|
||||
}
|
||||
|
||||
public class CardigannSettings : NoAuthTorrentBaseSettings
|
||||
public class CardigannSettings : NoAuthTorrentBaseSettings, IYmlIndexerSettings
|
||||
{
|
||||
private static readonly CardigannSettingsValidator Validator = new CardigannSettingsValidator();
|
||||
|
||||
|
|
195
src/NzbDrone.Core/Indexers/Definitions/Newznab/GenericNewznab.cs
Normal file
195
src/NzbDrone.Core/Indexers/Definitions/Newznab/GenericNewznab.cs
Normal file
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -8,17 +8,17 @@ using NzbDrone.Core.Parser.Model;
|
|||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
public class NewznabRssParser : RssParser
|
||||
public class GenericNewznabRssParser : RssParser
|
||||
{
|
||||
public const string ns = "{http://www.newznab.com/DTD/2010/feeds/attributes/}";
|
||||
|
||||
private readonly NewznabSettings _settings;
|
||||
private readonly List<IndexerCategory> _categories;
|
||||
|
||||
public NewznabRssParser(NewznabSettings settings)
|
||||
public GenericNewznabRssParser(List<IndexerCategory> categories)
|
||||
{
|
||||
PreferredEnclosureMimeTypes = UsenetEnclosureMimeTypes;
|
||||
UseEnclosureUrl = true;
|
||||
_settings = settings;
|
||||
_categories = categories;
|
||||
}
|
||||
|
||||
public static void CheckError(XDocument xdoc, IndexerResponse indexerResponse)
|
||||
|
@ -125,7 +125,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
{
|
||||
if (int.TryParse(cat, out var intCategory))
|
||||
{
|
||||
var indexerCat = _settings.Categories?.FirstOrDefault(c => c.Id == intCategory) ?? null;
|
||||
var indexerCat = _categories?.FirstOrDefault(c => c.Id == intCategory) ?? null;
|
||||
|
||||
if (indexerCat != null)
|
||||
{
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,9 +5,10 @@ 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.Indexers.Cardigann;
|
||||
using NzbDrone.Core.IndexerVersions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
@ -16,10 +17,10 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
{
|
||||
public class Newznab : UsenetIndexerBase<NewznabSettings>
|
||||
{
|
||||
private readonly INewznabCapabilitiesProvider _capabilitiesProvider;
|
||||
private readonly IIndexerDefinitionUpdateService _definitionService;
|
||||
|
||||
public override string Name => "Newznab";
|
||||
public override string[] IndexerUrls => GetBaseUrlFromSettings();
|
||||
public override string[] IndexerUrls => new string[] { "" };
|
||||
public override string Description => "Newznab is an API search specification for Usenet";
|
||||
public override bool FollowRedirect => true;
|
||||
public override bool SupportsRedirect => true;
|
||||
|
@ -27,130 +28,72 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
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 NewznabRequestGenerator(_capabilitiesProvider)
|
||||
var defFile = _definitionService.GetCachedDefinition(Settings.DefinitionFile);
|
||||
|
||||
return new NewznabRequestGenerator()
|
||||
{
|
||||
PageSize = PageSize,
|
||||
Settings = Settings
|
||||
Settings = Settings,
|
||||
Definition = defFile
|
||||
};
|
||||
}
|
||||
|
||||
public override IParseIndexerResponse GetParser()
|
||||
{
|
||||
return new NewznabRssParser(Settings);
|
||||
}
|
||||
var defFile = _definitionService.GetCachedDefinition(Settings.DefinitionFile);
|
||||
var capabilities = new IndexerCapabilities();
|
||||
capabilities.ParseYmlSearchModes(defFile.Caps.Modes);
|
||||
capabilities.SupportsRawSearch = defFile.Caps.Allowrawsearch;
|
||||
capabilities.MapYmlCategories(defFile);
|
||||
|
||||
public string[] GetBaseUrlFromSettings()
|
||||
{
|
||||
var baseUrl = "";
|
||||
|
||||
if (Definition == null || Settings == null || Settings.Categories == null)
|
||||
{
|
||||
return new string[] { baseUrl };
|
||||
}
|
||||
|
||||
return new string[] { Settings.BaseUrl };
|
||||
}
|
||||
|
||||
protected override NewznabSettings GetDefaultBaseUrl(NewznabSettings 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);
|
||||
return new GenericNewznabRssParser(capabilities.Categories.GetTorznabCategoryList());
|
||||
}
|
||||
|
||||
public override IEnumerable<ProviderDefinition> DefaultDefinitions
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return GetDefinition("abNZB", GetSettings("https://abnzb.com"));
|
||||
yield return GetDefinition("altHUB", GetSettings("https://api.althub.co.za"));
|
||||
yield return GetDefinition("AnimeTosho (Usenet)", GetSettings("https://feed.animetosho.org"));
|
||||
yield return GetDefinition("DOGnzb", GetSettings("https://api.dognzb.cr"));
|
||||
yield return GetDefinition("DrunkenSlug", GetSettings("https://drunkenslug.com"));
|
||||
yield return GetDefinition("GingaDADDY", GetSettings("https://www.gingadaddy.com"));
|
||||
yield return GetDefinition("Miatrix", GetSettings("https://www.miatrix.com"));
|
||||
yield return GetDefinition("Newz-Complex", GetSettings("https://newz-complex.org/www"));
|
||||
yield return GetDefinition("Newz69", GetSettings("https://newz69.keagaming.com"));
|
||||
yield return GetDefinition("NinjaCentral", GetSettings("https://ninjacentral.co.za"));
|
||||
yield return GetDefinition("Nzb.su", GetSettings("https://api.nzb.su"));
|
||||
yield return GetDefinition("NZBCat", GetSettings("https://nzb.cat"));
|
||||
yield return GetDefinition("NZBFinder", GetSettings("https://nzbfinder.ws"));
|
||||
yield return GetDefinition("NZBgeek", GetSettings("https://api.nzbgeek.info"));
|
||||
yield return GetDefinition("NzbNoob", GetSettings("https://www.nzbnoob.com"));
|
||||
yield return GetDefinition("NZBNDX", GetSettings("https://www.nzbndx.com"));
|
||||
yield return GetDefinition("NzbPlanet", GetSettings("https://api.nzbplanet.net"));
|
||||
yield return GetDefinition("NZBStars", GetSettings("https://nzbstars.com"));
|
||||
yield return GetDefinition("OZnzb", GetSettings("https://api.oznzb.com"));
|
||||
yield return GetDefinition("SimplyNZBs", GetSettings("https://simplynzbs.com"));
|
||||
yield return GetDefinition("SpotNZB", GetSettings("https://spotnzb.xyz"));
|
||||
yield return GetDefinition("Tabula Rasa", GetSettings("https://www.tabula-rasa.pw", apiPath: @"/api/v1/api"));
|
||||
yield return GetDefinition("Usenet Crawler", GetSettings("https://www.usenet-crawler.com"));
|
||||
yield return GetDefinition("Generic Newznab", GetSettings(""));
|
||||
foreach (var def in _definitionService.AllForImplementation(GetType().Name))
|
||||
{
|
||||
yield return GetDefinition(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Newznab(INewznabCapabilitiesProvider capabilitiesProvider, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
|
||||
public Newznab(IIndexerDefinitionUpdateService definitionService, IIndexerHttpClient httpClient, IEventAggregator eventAggregator, IIndexerStatusService indexerStatusService, IConfigService configService, IValidateNzbs nzbValidationService, Logger logger)
|
||||
: base(httpClient, eventAggregator, indexerStatusService, configService, nzbValidationService, logger)
|
||||
{
|
||||
_capabilitiesProvider = capabilitiesProvider;
|
||||
_definitionService = definitionService;
|
||||
}
|
||||
|
||||
private IndexerDefinition GetDefinition(string name, NewznabSettings settings)
|
||||
private IndexerDefinition GetDefinition(IndexerMetaDefinition definition)
|
||||
{
|
||||
return new IndexerDefinition
|
||||
{
|
||||
Enable = true,
|
||||
Name = name,
|
||||
Name = definition.Name,
|
||||
Language = definition.Language,
|
||||
Description = definition.Description,
|
||||
Implementation = GetType().Name,
|
||||
Settings = settings,
|
||||
IndexerUrls = definition.Links.ToArray(),
|
||||
LegacyUrls = definition.Legacylinks.ToArray(),
|
||||
Settings = new NewznabSettings { DefinitionFile = definition.File },
|
||||
Protocol = DownloadProtocol.Usenet,
|
||||
Privacy = IndexerPrivacy.Private,
|
||||
Privacy = definition.Type switch
|
||||
{
|
||||
"private" => IndexerPrivacy.Private,
|
||||
"public" => IndexerPrivacy.Public,
|
||||
_ => IndexerPrivacy.SemiPrivate
|
||||
},
|
||||
SupportsRss = SupportsRss,
|
||||
SupportsSearch = SupportsSearch,
|
||||
SupportsRedirect = SupportsRedirect,
|
||||
Capabilities = Capabilities
|
||||
Capabilities = new IndexerCapabilities()
|
||||
};
|
||||
}
|
||||
|
||||
private NewznabSettings GetSettings(string url, string apiPath = null)
|
||||
{
|
||||
var settings = new NewznabSettings { BaseUrl = url };
|
||||
|
||||
if (apiPath.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
settings.ApiPath = apiPath;
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
protected override async Task Test(List<ValidationFailure> failures)
|
||||
{
|
||||
await base.Test(failures);
|
||||
|
@ -158,61 +101,21 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
failures.AddIfNotNull(TestCapabilities());
|
||||
}
|
||||
|
||||
protected static List<int> CategoryIds(IndexerCapabilitiesCategories categories)
|
||||
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||
{
|
||||
var l = categories.GetTorznabCategoryTree().Select(c => c.Id).ToList();
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
protected virtual ValidationFailure TestCapabilities()
|
||||
{
|
||||
try
|
||||
if (action == "getUrls")
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
var devices = ((IndexerDefinition)Definition).IndexerUrls;
|
||||
|
||||
if (capabilities.SearchParams != null && capabilities.SearchParams.Contains(SearchParam.Q))
|
||||
return new
|
||||
{
|
||||
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.");
|
||||
options = devices.Select(d => new { Value = d, Name = d })
|
||||
};
|
||||
}
|
||||
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");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
{
|
||||
public interface INewznabCapabilitiesProvider
|
||||
{
|
||||
IndexerCapabilities GetCapabilities(NewznabSettings settings, ProviderDefinition definition);
|
||||
IndexerCapabilities GetCapabilities(GenericNewznabSettings settings, ProviderDefinition definition);
|
||||
}
|
||||
|
||||
public class NewznabCapabilitiesProvider : INewznabCapabilitiesProvider
|
||||
|
@ -30,7 +30,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
public IndexerCapabilities GetCapabilities(NewznabSettings indexerSettings, ProviderDefinition definition)
|
||||
public IndexerCapabilities GetCapabilities(GenericNewznabSettings indexerSettings, ProviderDefinition definition)
|
||||
{
|
||||
var key = indexerSettings.ToJson();
|
||||
var capabilities = _capabilitiesCache.Get(key, () => FetchCapabilities(indexerSettings, definition), TimeSpan.FromDays(7));
|
||||
|
@ -38,7 +38,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
return capabilities;
|
||||
}
|
||||
|
||||
private IndexerCapabilities FetchCapabilities(NewznabSettings indexerSettings, ProviderDefinition definition)
|
||||
private IndexerCapabilities FetchCapabilities(GenericNewznabSettings indexerSettings, ProviderDefinition definition)
|
||||
{
|
||||
var capabilities = new IndexerCapabilities();
|
||||
|
||||
|
@ -96,7 +96,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
throw new XmlException("Invalid XML").WithData(response);
|
||||
}
|
||||
|
||||
NewznabRssParser.CheckError(xDoc, new IndexerResponse(new IndexerRequest(response.Request), response));
|
||||
GenericNewznabRssParser.CheckError(xDoc, new IndexerResponse(new IndexerRequest(response.Request), response));
|
||||
|
||||
var xmlRoot = xDoc.Element("caps");
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ using System.Linq;
|
|||
using DryIoc;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
@ -13,23 +14,20 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
{
|
||||
public class NewznabRequestGenerator : IIndexerRequestGenerator
|
||||
{
|
||||
private readonly INewznabCapabilitiesProvider _capabilitiesProvider;
|
||||
public int MaxPages { get; set; }
|
||||
public int PageSize { get; set; }
|
||||
public NewznabSettings Settings { get; set; }
|
||||
public ProviderDefinition Definition { get; set; }
|
||||
public CardigannDefinition Definition { get; set; }
|
||||
|
||||
public NewznabRequestGenerator(INewznabCapabilitiesProvider capabilitiesProvider)
|
||||
public NewznabRequestGenerator()
|
||||
{
|
||||
_capabilitiesProvider = capabilitiesProvider;
|
||||
|
||||
MaxPages = 30;
|
||||
PageSize = 100;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MovieSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
var capabilities = GetCapabilities();
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = new NameValueCollection();
|
||||
|
@ -67,15 +65,14 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria,
|
||||
parameters));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, capabilities, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(MusicSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
var capabilities = GetCapabilities();
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = new NameValueCollection();
|
||||
|
@ -108,15 +105,14 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria,
|
||||
parameters));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, capabilities, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(TvSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
var capabilities = GetCapabilities();
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = new NameValueCollection();
|
||||
|
@ -174,15 +170,14 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria,
|
||||
parameters));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, capabilities, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BookSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
var capabilities = GetCapabilities();
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
var parameters = new NameValueCollection();
|
||||
|
@ -215,15 +210,15 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
}
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria,
|
||||
parameters));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, capabilities, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
public IndexerPageableRequestChain GetSearchRequests(BasicSearchCriteria searchCriteria)
|
||||
{
|
||||
var capabilities = _capabilitiesProvider.GetCapabilities(Settings, Definition);
|
||||
var capabilities = GetCapabilities();
|
||||
|
||||
var pageableRequests = new IndexerPageableRequestChain();
|
||||
|
||||
var parameters = new NameValueCollection();
|
||||
|
@ -233,15 +228,15 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
parameters.Add("q", NewsnabifyTitle(searchCriteria.SearchTerm));
|
||||
}
|
||||
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, parameters));
|
||||
pageableRequests.Add(GetPagedRequests(searchCriteria, capabilities, parameters));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(SearchCriteriaBase searchCriteria, NameValueCollection parameters)
|
||||
private IEnumerable<IndexerRequest> GetPagedRequests(SearchCriteriaBase searchCriteria, IndexerCapabilities capabilities, NameValueCollection parameters)
|
||||
{
|
||||
var baseUrl = string.Format("{0}{1}?t={2}&extended=1", Settings.BaseUrl.TrimEnd('/'), Settings.ApiPath.TrimEnd('/'), searchCriteria.SearchType);
|
||||
var categories = searchCriteria.Categories;
|
||||
var baseUrl = string.Format("{0}{1}?t={2}&extended=1", ResolveSiteLink().TrimEnd('/'), Settings.ApiPath.TrimEnd('/'), searchCriteria.SearchType);
|
||||
var categories = capabilities.Categories.MapTorznabCapsToTrackers(searchCriteria.Categories);
|
||||
|
||||
if (categories != null && categories.Any())
|
||||
{
|
||||
|
@ -286,6 +281,34 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
return seasonNumber == 0 ? "00" : seasonNumber.ToString();
|
||||
}
|
||||
|
||||
protected string ResolveSiteLink()
|
||||
{
|
||||
var settingsBaseUrl = Settings?.BaseUrl;
|
||||
var defaultLink = Definition.Links.First();
|
||||
|
||||
if (settingsBaseUrl == null)
|
||||
{
|
||||
return defaultLink;
|
||||
}
|
||||
|
||||
if (Definition?.Legacylinks?.Contains(settingsBaseUrl) ?? false)
|
||||
{
|
||||
return defaultLink;
|
||||
}
|
||||
|
||||
return settingsBaseUrl;
|
||||
}
|
||||
|
||||
private IndexerCapabilities GetCapabilities()
|
||||
{
|
||||
var capabilities = new IndexerCapabilities();
|
||||
|
||||
capabilities.ParseYmlSearchModes(Definition.Caps.Modes);
|
||||
capabilities.MapYmlCategories(Definition);
|
||||
|
||||
return capabilities;
|
||||
}
|
||||
|
||||
public Func<IDictionary<string, string>> GetCookies { get; set; }
|
||||
public Action<IDictionary<string, string>, DateTime?> CookiesUpdater { get; set; }
|
||||
}
|
||||
|
|
|
@ -4,40 +4,18 @@ using System.Text.RegularExpressions;
|
|||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.Indexers.Newznab
|
||||
{
|
||||
public class NewznabSettingsValidator : AbstractValidator<NewznabSettings>
|
||||
{
|
||||
private static readonly string[] ApiKeyWhiteList =
|
||||
{
|
||||
"nzbs.org",
|
||||
"nzb.su",
|
||||
"dognzb.cr",
|
||||
"nzbplanet.net",
|
||||
"nzbid.org",
|
||||
"nzbndx.com",
|
||||
"nzbindex.in"
|
||||
};
|
||||
|
||||
private static bool ShouldHaveApiKey(NewznabSettings 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 NewznabSettingsValidator()
|
||||
{
|
||||
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());
|
||||
|
||||
|
@ -51,7 +29,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
}
|
||||
}
|
||||
|
||||
public class NewznabSettings : IIndexerSettings
|
||||
public class NewznabSettings : IYmlIndexerSettings
|
||||
{
|
||||
private static readonly NewznabSettingsValidator Validator = new NewznabSettingsValidator();
|
||||
|
||||
|
@ -61,7 +39,7 @@ namespace NzbDrone.Core.Indexers.Newznab
|
|||
VipExpiration = "";
|
||||
}
|
||||
|
||||
[FieldDefinition(0, Label = "URL")]
|
||||
[FieldDefinition(0, 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, Label = "API Path", HelpText = "Path to the api, usually /api", Advanced = true)]
|
||||
|
@ -76,6 +54,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(0, Hidden = HiddenType.Hidden)]
|
||||
public string DefinitionFile { get; set; }
|
||||
|
||||
[FieldDefinition(7)]
|
||||
public IndexerBaseSettings BaseSettings { get; set; } = new IndexerBaseSettings();
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ namespace NzbDrone.Core.Indexers.Torznab
|
|||
|
||||
public override IIndexerRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new NewznabRequestGenerator(_capabilitiesProvider)
|
||||
return new GenericNewznabRequestGenerator(_capabilitiesProvider)
|
||||
{
|
||||
PageSize = PageSize,
|
||||
Settings = Settings
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace NzbDrone.Core.Indexers.Torznab
|
|||
}
|
||||
}
|
||||
|
||||
public class TorznabSettings : NewznabSettings, ITorrentIndexerSettings
|
||||
public class TorznabSettings : GenericNewznabSettings, ITorrentIndexerSettings
|
||||
{
|
||||
private static readonly TorznabSettingsValidator Validator = new TorznabSettingsValidator();
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using DryIoc.ImTools;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
|
||||
namespace NzbDrone.Core.Indexers
|
||||
{
|
||||
|
@ -127,7 +129,7 @@ namespace NzbDrone.Core.Indexers
|
|||
LimitsMax = 100;
|
||||
}
|
||||
|
||||
public void ParseCardigannSearchModes(Dictionary<string, List<string>> modes)
|
||||
public void ParseYmlSearchModes(Dictionary<string, List<string>> modes)
|
||||
{
|
||||
if (modes == null || !modes.Any())
|
||||
{
|
||||
|
@ -169,6 +171,48 @@ namespace NzbDrone.Core.Indexers
|
|||
}
|
||||
}
|
||||
|
||||
public void MapYmlCategories(CardigannDefinition defFile)
|
||||
{
|
||||
if (defFile.Caps.Categories != null)
|
||||
{
|
||||
foreach (var category in defFile.Caps.Categories)
|
||||
{
|
||||
var cat = NewznabStandardCategory.GetCatByName(category.Value);
|
||||
|
||||
if (cat == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Categories.AddCategoryMapping(category.Key, cat);
|
||||
}
|
||||
}
|
||||
|
||||
if (defFile.Caps.Categorymappings != null)
|
||||
{
|
||||
foreach (var categorymapping in defFile.Caps.Categorymappings)
|
||||
{
|
||||
IndexerCategory torznabCat = null;
|
||||
|
||||
if (categorymapping.cat != null)
|
||||
{
|
||||
torznabCat = NewznabStandardCategory.GetCatByName(categorymapping.cat);
|
||||
if (torznabCat == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Categories.AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc);
|
||||
|
||||
//if (categorymapping.Default)
|
||||
//{
|
||||
// DefaultCategories.Add(categorymapping.id);
|
||||
//}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ParseTvSearchParams(IEnumerable<string> paramsList)
|
||||
{
|
||||
if (paramsList == null)
|
||||
|
|
|
@ -7,6 +7,7 @@ using NLog;
|
|||
using NzbDrone.Core.Datastore;
|
||||
using NzbDrone.Core.Indexers.Cardigann;
|
||||
using NzbDrone.Core.Indexers.Newznab;
|
||||
using NzbDrone.Core.Indexers.Settings;
|
||||
using NzbDrone.Core.IndexerVersions;
|
||||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.ThingiProvider;
|
||||
|
@ -50,11 +51,11 @@ namespace NzbDrone.Core.Indexers
|
|||
|
||||
foreach (var definition in definitions)
|
||||
{
|
||||
if (definition.Implementation == typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Settings.GetType().GetInterface(nameof(IYmlIndexerSettings)) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
MapCardigannDefinition(definition);
|
||||
MapYmlDefinition(definition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -73,11 +74,11 @@ namespace NzbDrone.Core.Indexers
|
|||
{
|
||||
var definition = base.Get(id);
|
||||
|
||||
if (definition.Implementation == typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Settings.GetType().GetInterface(nameof(IYmlIndexerSettings)) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
MapCardigannDefinition(definition);
|
||||
MapYmlDefinition(definition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -93,9 +94,9 @@ namespace NzbDrone.Core.Indexers
|
|||
return base.Active().Where(c => c.Enable).ToList();
|
||||
}
|
||||
|
||||
private void MapCardigannDefinition(IndexerDefinition definition)
|
||||
private void MapYmlDefinition(IndexerDefinition definition)
|
||||
{
|
||||
var settings = (CardigannSettings)definition.Settings;
|
||||
var settings = (IYmlIndexerSettings)definition.Settings;
|
||||
var defFile = _definitionService.GetCachedDefinition(settings.DefinitionFile);
|
||||
definition.ExtraFields = defFile.Settings;
|
||||
|
||||
|
@ -121,51 +122,9 @@ namespace NzbDrone.Core.Indexers
|
|||
_ => IndexerPrivacy.SemiPrivate
|
||||
};
|
||||
definition.Capabilities = new IndexerCapabilities();
|
||||
definition.Capabilities.ParseCardigannSearchModes(defFile.Caps.Modes);
|
||||
definition.Capabilities.ParseYmlSearchModes(defFile.Caps.Modes);
|
||||
definition.Capabilities.SupportsRawSearch = defFile.Caps.Allowrawsearch;
|
||||
MapCardigannCategories(definition, defFile);
|
||||
}
|
||||
|
||||
private void MapCardigannCategories(IndexerDefinition def, CardigannDefinition defFile)
|
||||
{
|
||||
if (defFile.Caps.Categories != null)
|
||||
{
|
||||
foreach (var category in defFile.Caps.Categories)
|
||||
{
|
||||
var cat = NewznabStandardCategory.GetCatByName(category.Value);
|
||||
|
||||
if (cat == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
def.Capabilities.Categories.AddCategoryMapping(category.Key, cat);
|
||||
}
|
||||
}
|
||||
|
||||
if (defFile.Caps.Categorymappings != null)
|
||||
{
|
||||
foreach (var categorymapping in defFile.Caps.Categorymappings)
|
||||
{
|
||||
IndexerCategory torznabCat = null;
|
||||
|
||||
if (categorymapping.cat != null)
|
||||
{
|
||||
torznabCat = NewznabStandardCategory.GetCatByName(categorymapping.cat);
|
||||
if (torznabCat == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
def.Capabilities.Categories.AddCategoryMapping(categorymapping.id, torznabCat, categorymapping.desc);
|
||||
|
||||
//if (categorymapping.Default)
|
||||
//{
|
||||
// DefaultCategories.Add(categorymapping.id);
|
||||
//}
|
||||
}
|
||||
}
|
||||
definition.Capabilities.MapYmlCategories(defFile);
|
||||
}
|
||||
|
||||
public override IEnumerable<IndexerDefinition> GetDefaultDefinitions()
|
||||
|
@ -178,7 +137,7 @@ namespace NzbDrone.Core.Indexers
|
|||
}
|
||||
|
||||
var definitions = provider.DefaultDefinitions
|
||||
.Where(v => v.Name != null && (v.Name != typeof(Cardigann.Cardigann).Name || v.Name != typeof(Newznab.Newznab).Name || v.Name != typeof(Torznab.Torznab).Name));
|
||||
.Where(v => v.Name != null && (v.Name != typeof(Cardigann.Cardigann).Name || v.Name != typeof(Newznab.Newznab).Name || v.Name != typeof(Newznab.GenericNewznab).Name || v.Name != typeof(Torznab.Torznab).Name));
|
||||
|
||||
foreach (IndexerDefinition definition in definitions)
|
||||
{
|
||||
|
@ -203,7 +162,7 @@ namespace NzbDrone.Core.Indexers
|
|||
definition.SupportsRedirect = provider.SupportsRedirect;
|
||||
|
||||
//We want to use the definition Caps and Privacy for Cardigann instead of the provider.
|
||||
if (definition.Implementation != typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Settings.GetType().GetInterface(nameof(IYmlIndexerSettings)) == null)
|
||||
{
|
||||
definition.IndexerUrls = provider.IndexerUrls;
|
||||
definition.LegacyUrls = provider.LegacyUrls;
|
||||
|
@ -288,15 +247,15 @@ namespace NzbDrone.Core.Indexers
|
|||
|
||||
SetProviderCharacteristics(provider, definition);
|
||||
|
||||
if (definition.Implementation == typeof(Newznab.Newznab).Name || definition.Implementation == typeof(Torznab.Torznab).Name)
|
||||
if (definition.Implementation == typeof(Newznab.GenericNewznab).Name || definition.Implementation == typeof(Torznab.Torznab).Name)
|
||||
{
|
||||
var settings = (NewznabSettings)definition.Settings;
|
||||
var settings = (GenericNewznabSettings)definition.Settings;
|
||||
settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList() ?? null;
|
||||
}
|
||||
|
||||
if (definition.Implementation == typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Settings.GetType().GetInterface(nameof(IYmlIndexerSettings)) != null)
|
||||
{
|
||||
MapCardigannDefinition(definition);
|
||||
MapYmlDefinition(definition);
|
||||
}
|
||||
|
||||
return base.Create(definition);
|
||||
|
@ -310,13 +269,13 @@ namespace NzbDrone.Core.Indexers
|
|||
|
||||
if (definition.Enable && (definition.Implementation == typeof(Newznab.Newznab).Name || definition.Implementation == typeof(Torznab.Torznab).Name))
|
||||
{
|
||||
var settings = (NewznabSettings)definition.Settings;
|
||||
var settings = (GenericNewznabSettings)definition.Settings;
|
||||
settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList() ?? null;
|
||||
}
|
||||
|
||||
if (definition.Implementation == typeof(Cardigann.Cardigann).Name)
|
||||
if (definition.Settings.GetType().GetInterface(nameof(IYmlIndexerSettings)) != null)
|
||||
{
|
||||
MapCardigannDefinition(definition);
|
||||
MapYmlDefinition(definition);
|
||||
}
|
||||
|
||||
base.Update(definition);
|
||||
|
|
13
src/NzbDrone.Core/Indexers/Settings/IYmlIndexerSettings.cs
Normal file
13
src/NzbDrone.Core/Indexers/Settings/IYmlIndexerSettings.cs
Normal file
|
@ -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…
Add table
Reference in a new issue