Jackett/src/Jackett/Services/IndexerManagerService.cs

236 lines
9.5 KiB
C#
Raw Normal View History

using Jackett.Indexers;
2015-07-19 00:27:41 +00:00
using Jackett.Models;
using Jackett.Utils;
2015-07-28 19:22:23 +00:00
using Jackett.Utils.Clients;
2015-07-19 00:59:30 +00:00
using Newtonsoft.Json.Linq;
2015-07-19 00:27:41 +00:00
using NLog;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Jackett.Indexers.Meta;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
Feature/netcore preparation (#2035) * Move to use package reference for restoring nuget packages. * Return a task result for this async method. * Update to a supported version of the .NET Framework. This also has the side effect of allowing us to automatically generate our binding redirects on build. * Set the solution to target VS2017 * Update test solution csproj file to support being built through MSBuild 15 * Move to use package reference for restoring nuget packages. * Return a task result for this async method. * Update to a supported version of the .NET Framework. This also has the side effect of allowing us to automatically generate our binding redirects on build. * Set the solution to target VS2017 * Update test solution csproj file to support being built through MSBuild 15 * DateTimeRoutines does not have Nuget packages that support .NET Standard (and therefore .NET Core). We will have to include them for now until we can get rid of this dependency. * Move the interfaces into their own files. This will be useful when we share them between the .NET Core and .NET Framework WebAPI * Stage services that need to point to the new interface namespace. * Update CurlSharp to fix memory leak issue and support better runtime compatibility with OSX and Linux * Start spliting some interfaces into their own files - this will help by allowing us to split them out in the future into a seperate project so the actual implementations can stay within their respective architectures when required
2017-10-29 10:19:09 +00:00
using Jackett.Services.Interfaces;
2015-07-19 00:27:41 +00:00
namespace Jackett.Services
{
public class IndexerManagerService : IIndexerManagerService
{
private ICacheService cacheService;
private IIndexerConfigurationService configService;
private IProtectionService protectionService;
private IWebClient webClient;
private IProcessService processService;
private IConfigurationService globalConfigService;
private Logger logger;
private Dictionary<string, IIndexer> indexers = new Dictionary<string, IIndexer>();
private AggregateIndexer aggregateIndexer;
2015-07-19 00:27:41 +00:00
public IndexerManagerService(IIndexerConfigurationService config, IProtectionService protectionService, IWebClient webClient, Logger l, ICacheService cache, IProcessService processService, IConfigurationService globalConfigService)
2015-07-19 00:27:41 +00:00
{
configService = config;
this.protectionService = protectionService;
this.webClient = webClient;
this.processService = processService;
this.globalConfigService = globalConfigService;
2015-07-19 00:27:41 +00:00
logger = l;
cacheService = cache;
2015-07-19 00:27:41 +00:00
}
public void InitIndexers(IEnumerable<string> path)
2015-07-19 00:27:41 +00:00
{
InitIndexers();
InitCardigannIndexers(path);
InitAggregateIndexer();
}
2015-07-28 19:22:23 +00:00
private void InitIndexers()
{
logger.Info("Using HTTP Client: " + webClient.GetType().Name);
var allTypes = GetType().Assembly.GetTypes();
var allIndexerTypes = allTypes.Where(p => typeof(IIndexer).IsAssignableFrom(p));
var allInstantiatableIndexerTypes = allIndexerTypes.Where(p => !p.IsInterface && !p.IsAbstract);
var allNonMetaInstantiatableIndexerTypes = allInstantiatableIndexerTypes.Where(p => !typeof(BaseMetaIndexer).IsAssignableFrom(p));
var indexerTypes = allNonMetaInstantiatableIndexerTypes.Where(p => p.Name != "CardigannIndexer");
var ixs = indexerTypes.Select(type =>
2015-07-19 13:22:50 +00:00
{
var constructorArgumentTypes = new Type[] { typeof(IIndexerConfigurationService), typeof(IWebClient), typeof(Logger), typeof(IProtectionService) };
var constructor = type.GetConstructor(constructorArgumentTypes);
if (constructor != null)
{
// create own webClient instance for each indexer (seperate cookies stores, etc.)
var indexerWebClientInstance = (IWebClient)Activator.CreateInstance(webClient.GetType(), processService, logger, globalConfigService);
var arguments = new object[] { configService, indexerWebClientInstance, logger, protectionService };
var indexer = (IIndexer)constructor.Invoke(arguments);
return indexer;
}
else
{
logger.Error("Cannot instantiate " + type.Name);
}
return null;
});
foreach (var idx in ixs)
{
if (idx == null)
continue;
2015-07-19 19:28:08 +00:00
indexers.Add(idx.ID, idx);
configService.Load(idx);
}
}
private void InitCardigannIndexers(IEnumerable<string> path)
{
logger.Info("Loading Cardigann definitions from: " + string.Join(", ", path));
var deserializer = new DeserializerBuilder()
.WithNamingConvention(new CamelCaseNamingConvention())
.IgnoreUnmatchedProperties()
.Build();
try
{
var directoryInfos = path.Select(p => new DirectoryInfo(p));
var existingDirectories = directoryInfos.Where(d => d.Exists);
var files = existingDirectories.SelectMany(d => d.GetFiles("*.yml"));
var definitions = files.Select(file =>
{
logger.Info("Loading Cardigann definition " + file.FullName);
try {
string DefinitionString = File.ReadAllText(file.FullName);
var definition = deserializer.Deserialize<IndexerDefinition>(DefinitionString);
return definition;
}
catch (Exception ex)
{
logger.Error(ex, "Error while parsing Cardigann definition " + file.FullName + ": " + ex.Message);
return null;
}
}).Where(definition => definition != null);
List<IIndexer> cardigannIndexers = definitions.Select(definition =>
{
try
{
// create own webClient instance for each indexer (seperate cookies stores, etc.)
var indexerWebClientInstance = (IWebClient)Activator.CreateInstance(webClient.GetType(), processService, logger, globalConfigService);
IIndexer indexer = new CardigannIndexer(configService, indexerWebClientInstance, logger, protectionService, definition);
configService.Load(indexer);
return indexer;
}
catch (Exception ex)
{
logger.Error(ex, "Error while creating Cardigann instance from Definition: " + ex.Message);
return null;
}
}).Where(cardigannIndexer => cardigannIndexer != null).ToList(); // Explicit conversion to list to avoid repeated resource loading
foreach (var indexer in cardigannIndexers)
{
if (indexers.ContainsKey(indexer.ID))
{
logger.Debug(string.Format("Ignoring definition ID={0}: Indexer already exists", indexer.ID));
continue;
}
indexers.Add(indexer.ID, indexer);
}
}
catch (Exception ex)
{
logger.Error(ex, "Error while loading Cardigann definitions: " + ex.Message);
2015-07-19 13:22:50 +00:00
}
2015-07-19 00:27:41 +00:00
}
public void InitAggregateIndexer()
{
var omdbApiKey = Engine.Server.Config.OmdbApiKey;
IFallbackStrategyProvider fallbackStrategyProvider = null;
IResultFilterProvider resultFilterProvider = null;
2017-07-21 15:56:29 +00:00
if (!omdbApiKey.IsNullOrEmptyOrWhitespace())
{
var imdbResolver = new OmdbResolver(webClient, omdbApiKey.ToNonNull());
fallbackStrategyProvider = new ImdbFallbackStrategyProvider(imdbResolver);
resultFilterProvider = new ImdbTitleResultFilterProvider(imdbResolver);
}
else
{
fallbackStrategyProvider = new NoFallbackStrategyProvider();
resultFilterProvider = new NoResultFilterProvider();
}
logger.Info("Adding aggregate indexer");
aggregateIndexer = new AggregateIndexer(fallbackStrategyProvider, resultFilterProvider, configService, webClient, logger, protectionService);
aggregateIndexer.Indexers = indexers.Values;
}
2015-07-19 13:22:50 +00:00
public IIndexer GetIndexer(string name)
2015-07-19 00:27:41 +00:00
{
if (indexers.ContainsKey(name))
2015-07-19 17:18:54 +00:00
{
return indexers[name];
2015-07-19 17:18:54 +00:00
}
else if (name == "all")
{
return aggregateIndexer;
}
2015-07-19 17:18:54 +00:00
else
2015-07-19 13:22:50 +00:00
{
logger.Error("Request for unknown indexer: " + name);
throw new Exception("Unknown indexer: " + name);
}
}
public IWebIndexer GetWebIndexer(string name)
{
if (indexers.ContainsKey(name))
{
return indexers[name] as IWebIndexer;
}
else if (name == "all")
{
return aggregateIndexer as IWebIndexer;
}
logger.Error("Request for unknown indexer: " + name);
throw new Exception("Unknown indexer: " + name);
}
2015-07-19 13:22:50 +00:00
public IEnumerable<IIndexer> GetAllIndexers()
{
return indexers.Values.OrderBy(_ => _.DisplayName);
2015-07-19 00:27:41 +00:00
}
2015-07-19 18:25:47 +00:00
public async Task TestIndexer(string name)
2015-07-19 00:27:41 +00:00
{
var indexer = GetIndexer(name);
var browseQuery = new TorznabQuery();
2017-08-11 16:13:22 +00:00
browseQuery.QueryType = "search";
browseQuery.SearchTerm = "";
2016-12-06 17:24:40 +00:00
browseQuery.IsTest = true;
var result = await indexer.ResultsForQuery(browseQuery);
logger.Info(string.Format("Found {0} releases from {1}", result.Releases.Count(), indexer.DisplayName));
if (result.Releases.Count() == 0)
2015-07-19 00:27:41 +00:00
throw new Exception("Found no results while trying to browse this tracker");
cacheService.CacheRssResults(indexer, result.Releases);
2015-07-19 00:27:41 +00:00
}
public void DeleteIndexer(string name)
{
var indexer = GetIndexer(name);
configService.Delete(indexer);
indexer.Unconfigure();
}
2015-07-19 00:27:41 +00:00
}
}