You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Prowlarr/src/NzbDrone.Core/Indexers/IndexerFactory.cs

329 lines
12 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FluentValidation.Results;
using NLog;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers.Definitions.Cardigann;
using NzbDrone.Core.Indexers.Newznab;
using NzbDrone.Core.IndexerVersions;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.ThingiProvider;
namespace NzbDrone.Core.Indexers
{
public interface IIndexerFactory : IProviderFactory<IIndexer, IndexerDefinition>
{
List<IIndexer> Enabled(bool filterBlockedIndexers = true);
List<IIndexer> AllProviders(bool filterBlockedIndexers = true);
}
public class IndexerFactory : ProviderFactory<IIndexer, IndexerDefinition>, IIndexerFactory
{
private readonly IIndexerDefinitionUpdateService _definitionService;
private readonly INewznabCapabilitiesProvider _newznabCapabilitiesProvider;
private readonly IIndexerStatusService _indexerStatusService;
private readonly Logger _logger;
public IndexerFactory(IIndexerDefinitionUpdateService definitionService,
INewznabCapabilitiesProvider newznabCapabilitiesProvider,
IIndexerStatusService indexerStatusService,
IIndexerRepository providerRepository,
IEnumerable<IIndexer> providers,
IServiceProvider container,
IEventAggregator eventAggregator,
Logger logger)
: base(providerRepository, providers, container, eventAggregator, logger)
{
_definitionService = definitionService;
_indexerStatusService = indexerStatusService;
_newznabCapabilitiesProvider = newznabCapabilitiesProvider;
_logger = logger;
}
public override List<IndexerDefinition> All()
{
var definitions = base.All();
var filteredDefinitions = new List<IndexerDefinition>();
foreach (var definition in definitions)
{
if (definition.Implementation == nameof(Cardigann))
{
try
{
MapCardigannDefinition(definition);
}
catch
{
// Skip indexer if we fail in Cardigann mapping
_logger.Debug("Indexer '{0}' has no definition", definition.Name);
}
}
filteredDefinitions.Add(definition);
}
return filteredDefinitions;
}
public override IndexerDefinition Get(int id)
{
var definition = base.Get(id);
if (definition.Implementation == nameof(Cardigann))
{
try
{
MapCardigannDefinition(definition);
}
catch
{
throw new ModelNotFoundException(typeof(IndexerDefinition), id);
}
}
return definition;
}
protected override List<IndexerDefinition> Active()
{
return base.Active().Where(c => c.Enable).ToList();
}
private void MapCardigannDefinition(IndexerDefinition definition)
{
var settings = (CardigannSettings)definition.Settings;
var defFile = _definitionService.GetCachedDefinition(settings.DefinitionFile);
definition.ExtraFields = defFile.Settings;
if (defFile.Login?.Captcha != null && !definition.ExtraFields.Any(x => x.Type == "cardigannCaptcha"))
{
definition.ExtraFields.Add(new SettingsField
{
Name = "cardigannCaptcha",
Type = "cardigannCaptcha",
Label = "CAPTCHA"
});
}
definition.IndexerUrls = defFile.Links.ToArray();
definition.LegacyUrls = defFile.Legacylinks.ToArray();
definition.Description = defFile.Description;
definition.Language = defFile.Language;
definition.Encoding = Encoding.GetEncoding(defFile.Encoding);
definition.Privacy = defFile.Type switch
{
"private" => IndexerPrivacy.Private,
"public" => IndexerPrivacy.Public,
_ => IndexerPrivacy.SemiPrivate
};
definition.Capabilities = new IndexerCapabilities();
definition.Capabilities.ParseCardigannSearchModes(defFile.Caps.Modes);
definition.Capabilities.SupportsRawSearch = defFile.Caps.Allowrawsearch;
MapCardigannCategories(definition, defFile);
if (defFile.Caps?.LimitsDefault is > 0)
{
definition.Capabilities.LimitsDefault = defFile.Caps.LimitsDefault;
}
if (defFile.Caps?.LimitsMax is > 0)
{
definition.Capabilities.LimitsMax = defFile.Caps.LimitsMax;
}
}
private void MapCardigannCategories(IndexerDefinition def, CardigannDefinition defFile)
{
if (defFile.Caps.Categories != null && defFile.Caps.Categories.Any())
{
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 && defFile.Caps.Categorymappings.Any())
{
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);
}
}
}
public override IEnumerable<IndexerDefinition> GetDefaultDefinitions()
{
foreach (var provider in _providers)
{
if (provider.IsObsolete())
{
continue;
}
var definitions = provider.DefaultDefinitions
.Where(v => v.Name != null && v.Name != nameof(Cardigann) && v.Name != nameof(Newznab.Newznab) && v.Name != nameof(Torznab.Torznab))
.Cast<IndexerDefinition>();
foreach (var definition in definitions)
{
SetProviderCharacteristics(provider, definition);
yield return definition;
}
}
}
public override IEnumerable<IndexerDefinition> GetPresetDefinitions(IndexerDefinition providerDefinition)
{
return Array.Empty<IndexerDefinition>();
}
public override void SetProviderCharacteristics(IIndexer provider, IndexerDefinition definition)
{
base.SetProviderCharacteristics(provider, definition);
definition.Protocol = provider.Protocol;
definition.SupportsRss = provider.SupportsRss;
definition.SupportsSearch = provider.SupportsSearch;
definition.SupportsRedirect = provider.SupportsRedirect;
definition.SupportsPagination = provider.SupportsPagination;
//We want to use the definition Caps and Privacy for Cardigann instead of the provider.
if (definition.Implementation != nameof(Cardigann))
{
definition.IndexerUrls = provider.IndexerUrls;
definition.LegacyUrls = provider.LegacyUrls;
definition.Privacy = provider.Privacy;
definition.Description ??= provider.Description;
definition.Encoding = provider.Encoding;
definition.Language ??= provider.Language;
definition.Capabilities = provider.Capabilities;
}
}
public List<IIndexer> Enabled(bool filterBlockedIndexers = true)
{
var enabledIndexers = GetAvailableProviders().Where(n => ((IndexerDefinition)n.Definition).Enable);
if (filterBlockedIndexers)
{
return FilterBlockedIndexers(enabledIndexers).ToList();
}
return enabledIndexers.ToList();
}
public List<IIndexer> AllProviders(bool filterBlockedIndexers = true)
{
var enabledIndexers = All().Select(GetInstance);
if (filterBlockedIndexers)
{
return FilterBlockedIndexers(enabledIndexers).ToList();
}
return enabledIndexers.ToList();
}
private IEnumerable<IIndexer> FilterBlockedIndexers(IEnumerable<IIndexer> indexers)
{
var blockedIndexers = _indexerStatusService.GetBlockedProviders().ToDictionary(v => v.ProviderId, v => v);
foreach (var indexer in indexers)
{
if (blockedIndexers.TryGetValue(indexer.Definition.Id, out var blockedIndexerStatus))
{
_logger.Debug("Temporarily ignoring indexer {0} till {1} due to recent failures.", indexer.Definition.Name, blockedIndexerStatus.DisabledTill.Value.ToLocalTime());
continue;
}
yield return indexer;
}
}
public override ValidationResult Test(IndexerDefinition definition)
{
var result = base.Test(definition);
if (definition.Id == 0)
{
return result;
}
if (result == null || result.IsValid)
{
_indexerStatusService.RecordSuccess(definition.Id);
}
else
{
_indexerStatusService.RecordFailure(definition.Id);
}
return result;
}
public override IndexerDefinition Create(IndexerDefinition definition)
{
definition.Added = DateTime.UtcNow;
var provider = _providers.First(v => v.GetType().Name == definition.Implementation);
SetProviderCharacteristics(provider, definition);
if (definition.Enable && definition.Implementation is nameof(Newznab.Newznab) or nameof(Torznab.Torznab))
{
var settings = (NewznabSettings)definition.Settings;
settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList();
}
if (definition.Implementation == nameof(Cardigann))
{
MapCardigannDefinition(definition);
}
return base.Create(definition);
}
public override void Update(IndexerDefinition definition)
{
var provider = _providers.First(v => v.GetType().Name == definition.Implementation);
SetProviderCharacteristics(provider, definition);
if (definition.Enable && definition.Implementation is nameof(Newznab.Newznab) or nameof(Torznab.Torznab))
{
var settings = (NewznabSettings)definition.Settings;
settings.Categories = _newznabCapabilitiesProvider.GetCapabilities(settings, definition)?.Categories.GetTorznabCategoryList();
}
if (definition.Implementation == nameof(Cardigann))
{
MapCardigannDefinition(definition);
}
base.Update(definition);
}
}
}