2015-07-19 00:27:41 +00:00
|
|
|
|
using Autofac;
|
2015-07-19 13:22:50 +00:00
|
|
|
|
using Jackett.Indexers;
|
2015-07-19 00:27:41 +00:00
|
|
|
|
using Jackett.Models;
|
2015-07-19 17:37:08 +00:00
|
|
|
|
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.Text;
|
|
|
|
|
using System.Threading.Tasks;
|
2017-05-14 16:55:36 +00:00
|
|
|
|
using Jackett.Indexers.Meta;
|
2015-07-19 00:27:41 +00:00
|
|
|
|
|
|
|
|
|
namespace Jackett.Services
|
|
|
|
|
{
|
|
|
|
|
public interface IIndexerManagerService
|
|
|
|
|
{
|
2015-07-19 18:25:47 +00:00
|
|
|
|
Task TestIndexer(string name);
|
2015-07-19 00:27:41 +00:00
|
|
|
|
void DeleteIndexer(string name);
|
2015-07-19 13:22:50 +00:00
|
|
|
|
IIndexer GetIndexer(string name);
|
|
|
|
|
IEnumerable<IIndexer> GetAllIndexers();
|
|
|
|
|
void SaveConfig(IIndexer indexer, JToken obj);
|
|
|
|
|
void InitIndexers();
|
2016-10-27 07:30:03 +00:00
|
|
|
|
void InitCardigannIndexers(string path);
|
2017-04-30 08:06:29 +00:00
|
|
|
|
void InitAggregateIndexer();
|
2016-12-05 12:03:30 +00:00
|
|
|
|
void SortIndexers();
|
2015-07-19 00:27:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class IndexerManagerService : IIndexerManagerService
|
|
|
|
|
{
|
2017-01-05 17:08:10 +00:00
|
|
|
|
private static readonly object configWriteLock = new object();
|
|
|
|
|
|
2015-07-19 00:27:41 +00:00
|
|
|
|
private IContainer container;
|
|
|
|
|
private IConfigurationService configService;
|
|
|
|
|
private Logger logger;
|
2015-07-19 17:18:54 +00:00
|
|
|
|
private Dictionary<string, IIndexer> indexers = new Dictionary<string, IIndexer>();
|
2015-07-28 23:56:08 +00:00
|
|
|
|
private ICacheService cacheService;
|
2017-05-07 07:10:57 +00:00
|
|
|
|
private AggregateIndexer aggregateIndexer;
|
2015-07-19 00:27:41 +00:00
|
|
|
|
|
2015-07-28 23:56:08 +00:00
|
|
|
|
public IndexerManagerService(IContainer c, IConfigurationService config, Logger l, ICacheService cache)
|
2015-07-19 00:27:41 +00:00
|
|
|
|
{
|
|
|
|
|
container = c;
|
|
|
|
|
configService = config;
|
|
|
|
|
logger = l;
|
2015-07-28 23:56:08 +00:00
|
|
|
|
cacheService = cache;
|
2015-07-19 00:27:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-15 08:45:10 +00:00
|
|
|
|
protected void LoadIndexerConfig(IIndexer idx)
|
|
|
|
|
{
|
|
|
|
|
var configFilePath = GetIndexerConfigFilePath(idx);
|
|
|
|
|
if (File.Exists(configFilePath))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var fileStr = File.ReadAllText(configFilePath);
|
|
|
|
|
var jsonString = JToken.Parse(fileStr);
|
|
|
|
|
idx.LoadFromSavedConfiguration(jsonString);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
logger.Error(ex, "Failed loading configuration for {0}, trying backup", idx.DisplayName);
|
|
|
|
|
var configFilePathBak = configFilePath + ".bak";
|
|
|
|
|
if (File.Exists(configFilePathBak))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var fileStrBak = File.ReadAllText(configFilePathBak);
|
|
|
|
|
var jsonStringBak = JToken.Parse(fileStrBak);
|
|
|
|
|
idx.LoadFromSavedConfiguration(jsonStringBak);
|
|
|
|
|
logger.Info("Successfully loaded backup config for {0}", idx.DisplayName);
|
|
|
|
|
idx.SaveConfig();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception exbak)
|
|
|
|
|
{
|
|
|
|
|
logger.Error(exbak, "Failed loading backup configuration for {0}, you must reconfigure this indexer", idx.DisplayName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Error(ex, "Failed loading backup configuration for {0} (no backup available), you must reconfigure this indexer", idx.DisplayName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-27 07:30:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-07-19 13:22:50 +00:00
|
|
|
|
public void InitIndexers()
|
2015-07-19 00:27:41 +00:00
|
|
|
|
{
|
2015-07-28 19:22:23 +00:00
|
|
|
|
logger.Info("Using HTTP Client: " + container.Resolve<IWebClient>().GetType().Name);
|
|
|
|
|
|
2017-04-30 08:06:29 +00:00
|
|
|
|
foreach (var idx in container.Resolve<IEnumerable<IIndexer>>().Where(p => p.ID != "cardigannindexer" && p.ID != "aggregateindexer").OrderBy(_ => _.DisplayName))
|
2015-07-19 13:22:50 +00:00
|
|
|
|
{
|
2015-07-19 19:28:08 +00:00
|
|
|
|
indexers.Add(idx.ID, idx);
|
2016-10-27 07:30:03 +00:00
|
|
|
|
LoadIndexerConfig(idx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void InitCardigannIndexers(string path)
|
|
|
|
|
{
|
|
|
|
|
logger.Info("Loading Cardigann definitions from: " + path);
|
|
|
|
|
|
2017-04-15 08:45:10 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
if (!Directory.Exists(path))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DirectoryInfo d = new DirectoryInfo(path);
|
|
|
|
|
|
|
|
|
|
foreach (var file in d.GetFiles("*.yml"))
|
|
|
|
|
{
|
|
|
|
|
logger.Info("Loading Cardigann definition " + file.FullName);
|
|
|
|
|
string DefinitionString = File.ReadAllText(file.FullName);
|
|
|
|
|
CardigannIndexer idx = new CardigannIndexer(this, container.Resolve<IWebClient>(), logger, container.Resolve<IProtectionService>(), DefinitionString);
|
|
|
|
|
if (indexers.ContainsKey(idx.ID))
|
|
|
|
|
{
|
|
|
|
|
logger.Debug(string.Format("Ignoring definition ID={0}, file={1}: Indexer already exists", idx.ID, file.FullName));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
indexers.Add(idx.ID, idx);
|
|
|
|
|
LoadIndexerConfig(idx);
|
|
|
|
|
}
|
2016-10-29 16:01:43 +00:00
|
|
|
|
}
|
2017-04-15 08:45:10 +00:00
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2017-04-30 08:06:29 +00:00
|
|
|
|
public void InitAggregateIndexer()
|
|
|
|
|
{
|
2017-07-01 16:23:57 +00:00
|
|
|
|
var omdbApiKey = container.Resolve<IServerService>().Config.OmdbApiKey;
|
|
|
|
|
IFallbackStrategyProvider fallbackStrategyProvider = null;
|
|
|
|
|
IResultFilterProvider resultFilterProvider = null;
|
|
|
|
|
if (omdbApiKey != null)
|
|
|
|
|
{
|
|
|
|
|
var imdbResolver = new OmdbResolver(container.Resolve<IWebClient>(), omdbApiKey.ToNonNull());
|
|
|
|
|
fallbackStrategyProvider = new ImdbFallbackStrategyProvider(imdbResolver);
|
|
|
|
|
resultFilterProvider = new ImdbTitleResultFilterProvider(imdbResolver);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fallbackStrategyProvider = new NoFallbackStrategyProvider();
|
|
|
|
|
resultFilterProvider = new NoResultFilterProvider();
|
|
|
|
|
}
|
2017-06-28 05:31:38 +00:00
|
|
|
|
|
2017-04-30 08:06:29 +00:00
|
|
|
|
logger.Info("Adding aggregate indexer");
|
2017-07-01 16:23:57 +00:00
|
|
|
|
AggregateIndexer aggregateIndexer = new AggregateIndexer(fallbackStrategyProvider, resultFilterProvider, this, container.Resolve<IWebClient>(), logger, container.Resolve<IProtectionService>());
|
2017-04-30 08:06:29 +00:00
|
|
|
|
this.aggregateIndexer = aggregateIndexer;
|
2017-05-07 07:10:57 +00:00
|
|
|
|
UpdateAggregateIndexer();
|
2017-04-30 08:06:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-07-19 13:22:50 +00:00
|
|
|
|
public IIndexer GetIndexer(string name)
|
2015-07-19 00:27:41 +00:00
|
|
|
|
{
|
2015-07-22 22:06:46 +00:00
|
|
|
|
if (indexers.ContainsKey(name))
|
2015-07-19 17:18:54 +00:00
|
|
|
|
{
|
2015-07-22 22:06:46 +00:00
|
|
|
|
return indexers[name];
|
2015-07-19 17:18:54 +00:00
|
|
|
|
}
|
2017-04-30 08:06:29 +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 IEnumerable<IIndexer> GetAllIndexers()
|
|
|
|
|
{
|
2015-07-19 17:18:54 +00:00
|
|
|
|
return indexers.Values;
|
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();
|
2016-12-06 17:24:40 +00:00
|
|
|
|
browseQuery.IsTest = true;
|
2017-07-03 05:15:47 +00:00
|
|
|
|
var results = await indexer.ResultsForQuery(browseQuery);
|
2015-08-13 21:51:49 +00:00
|
|
|
|
results = indexer.CleanLinks(results);
|
2015-07-28 23:10:04 +00:00
|
|
|
|
logger.Info(string.Format("Found {0} releases from {1}", results.Count(), indexer.DisplayName));
|
|
|
|
|
if (results.Count() == 0)
|
2015-07-19 00:27:41 +00:00
|
|
|
|
throw new Exception("Found no results while trying to browse this tracker");
|
2015-08-05 22:00:09 +00:00
|
|
|
|
cacheService.CacheRssResults(indexer, results);
|
2015-07-19 00:27:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void DeleteIndexer(string name)
|
|
|
|
|
{
|
|
|
|
|
var indexer = GetIndexer(name);
|
2015-07-19 17:18:54 +00:00
|
|
|
|
var configPath = GetIndexerConfigFilePath(indexer);
|
2015-07-19 00:27:41 +00:00
|
|
|
|
File.Delete(configPath);
|
2017-04-15 08:45:10 +00:00
|
|
|
|
if (indexer.GetType() == typeof(CardigannIndexer))
|
|
|
|
|
{
|
|
|
|
|
indexers[name] = new CardigannIndexer(this, container.Resolve<IWebClient>(), logger, container.Resolve<IProtectionService>(), ((CardigannIndexer)indexer).DefinitionString);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
indexers[name] = container.ResolveNamed<IIndexer>(indexer.ID);
|
2016-12-10 17:20:04 +00:00
|
|
|
|
}
|
2017-05-07 07:10:57 +00:00
|
|
|
|
UpdateAggregateIndexer();
|
2015-07-19 00:27:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-07-19 13:22:50 +00:00
|
|
|
|
private string GetIndexerConfigFilePath(IIndexer indexer)
|
2015-07-19 00:27:41 +00:00
|
|
|
|
{
|
2015-07-19 19:28:08 +00:00
|
|
|
|
return Path.Combine(configService.GetIndexerConfigDir(), indexer.ID + ".json");
|
2015-07-19 00:27:41 +00:00
|
|
|
|
}
|
2015-07-19 00:59:30 +00:00
|
|
|
|
|
2015-07-19 13:22:50 +00:00
|
|
|
|
public void SaveConfig(IIndexer indexer, JToken obj)
|
2015-07-19 00:59:30 +00:00
|
|
|
|
{
|
2017-05-07 07:10:57 +00:00
|
|
|
|
UpdateAggregateIndexer();
|
2017-04-15 08:45:10 +00:00
|
|
|
|
lock (configWriteLock)
|
2017-01-05 17:08:10 +00:00
|
|
|
|
{
|
|
|
|
|
var uID = Guid.NewGuid().ToString("N");
|
|
|
|
|
var configFilePath = GetIndexerConfigFilePath(indexer);
|
|
|
|
|
var configFilePathBak = configFilePath + ".bak";
|
|
|
|
|
var configFilePathTmp = configFilePath + "." + uID + ".tmp";
|
|
|
|
|
var content = obj.ToString();
|
2016-11-10 08:06:35 +00:00
|
|
|
|
|
2017-01-05 17:08:10 +00:00
|
|
|
|
logger.Debug(string.Format("Saving new config file: {0}", configFilePathTmp));
|
2016-11-10 08:06:35 +00:00
|
|
|
|
|
2017-04-15 08:45:10 +00:00
|
|
|
|
if (string.IsNullOrWhiteSpace(content))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception(string.Format("New config content for {0} is empty, please report this bug.", indexer.ID));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (content.Contains("\x00"))
|
|
|
|
|
{
|
|
|
|
|
throw new Exception(string.Format("New config content for {0} contains 0x00, please report this bug. Content: {1}", indexer.ID, content));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make sure the config directory exists
|
2017-01-05 17:08:10 +00:00
|
|
|
|
if (!Directory.Exists(configService.GetIndexerConfigDir()))
|
|
|
|
|
Directory.CreateDirectory(configService.GetIndexerConfigDir());
|
|
|
|
|
|
|
|
|
|
// create new temporary config file
|
|
|
|
|
File.WriteAllText(configFilePathTmp, content);
|
|
|
|
|
var fileInfo = new FileInfo(configFilePathTmp);
|
2017-04-15 08:45:10 +00:00
|
|
|
|
if (fileInfo.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
throw new Exception(string.Format("New config file {0} is empty, please report this bug.", configFilePathTmp));
|
2017-01-05 17:08:10 +00:00
|
|
|
|
}
|
2016-11-10 08:06:35 +00:00
|
|
|
|
|
2017-01-05 17:08:10 +00:00
|
|
|
|
// create backup file
|
|
|
|
|
File.Delete(configFilePathBak);
|
2017-04-15 08:45:10 +00:00
|
|
|
|
if (File.Exists(configFilePath))
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
File.Move(configFilePath, configFilePathBak);
|
|
|
|
|
}
|
2017-01-05 17:08:10 +00:00
|
|
|
|
catch (IOException ex)
|
2017-04-15 08:45:10 +00:00
|
|
|
|
{
|
|
|
|
|
logger.Error(string.Format("Error while moving {0} to {1}: {2}", configFilePath, configFilePathBak, ex.ToString()));
|
|
|
|
|
}
|
2017-01-05 17:08:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// replace the actual config file
|
|
|
|
|
File.Delete(configFilePath);
|
2017-04-15 08:45:10 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
File.Move(configFilePathTmp, configFilePath);
|
|
|
|
|
}
|
2016-11-10 08:06:35 +00:00
|
|
|
|
catch (IOException ex)
|
2017-04-15 08:45:10 +00:00
|
|
|
|
{
|
|
|
|
|
logger.Error(string.Format("Error while moving {0} to {1}: {2}", configFilePathTmp, configFilePath, ex.ToString()));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SortIndexers()
|
|
|
|
|
{
|
|
|
|
|
// Apparently Dictionary are ordered but can't be sorted again
|
|
|
|
|
// This will recreate the indexers Dictionary to workaround this limitation
|
|
|
|
|
Dictionary<string, IIndexer> newIndexers = new Dictionary<string, IIndexer>();
|
|
|
|
|
foreach (var indexer in indexers.OrderBy(_ => _.Value.DisplayName))
|
|
|
|
|
newIndexers.Add(indexer.Key, indexer.Value);
|
|
|
|
|
indexers = newIndexers;
|
|
|
|
|
}
|
2017-05-07 07:10:57 +00:00
|
|
|
|
|
|
|
|
|
private void UpdateAggregateIndexer()
|
|
|
|
|
{
|
2017-05-14 16:55:36 +00:00
|
|
|
|
aggregateIndexer.Indexers = indexers.Where(p => p.Value.IsConfigured).Select(p => p.Value);
|
2017-05-07 07:10:57 +00:00
|
|
|
|
}
|
2015-07-19 00:27:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|