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 { List Enabled(bool filterBlockedIndexers = true); List AllProviders(bool filterBlockedIndexers = true); } public class IndexerFactory : ProviderFactory, 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 providers, IServiceProvider container, IEventAggregator eventAggregator, Logger logger) : base(providerRepository, providers, container, eventAggregator, logger) { _definitionService = definitionService; _indexerStatusService = indexerStatusService; _newznabCapabilitiesProvider = newznabCapabilitiesProvider; _logger = logger; } public override List All() { var definitions = base.All(); var filteredDefinitions = new List(); 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 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 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(); foreach (var definition in definitions) { SetProviderCharacteristics(provider, definition); yield return definition; } } } public override IEnumerable GetPresetDefinitions(IndexerDefinition providerDefinition) { return Array.Empty(); } 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 Enabled(bool filterBlockedIndexers = true) { var enabledIndexers = GetAvailableProviders().Where(n => ((IndexerDefinition)n.Definition).Enable); if (filterBlockedIndexers) { return FilterBlockedIndexers(enabledIndexers).ToList(); } return enabledIndexers.ToList(); } public List AllProviders(bool filterBlockedIndexers = true) { var enabledIndexers = All().Select(GetInstance); if (filterBlockedIndexers) { return FilterBlockedIndexers(enabledIndexers).ToList(); } return enabledIndexers.ToList(); } private IEnumerable FilterBlockedIndexers(IEnumerable 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); } } }