using System; using System.Collections.Generic; using System.IO; using System.Linq; using NLog; using NzbDrone.Common.Cache; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; using NzbDrone.Core.Indexers.Cardigann; using NzbDrone.Core.Messaging.Commands; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; namespace NzbDrone.Core.IndexerVersions { public interface IIndexerDefinitionUpdateService { List All(); CardigannDefinition GetDefinition(string fileKey); } public class IndexerDefinitionUpdateService : IIndexerDefinitionUpdateService, IExecute { private const int DEFINITION_VERSION = 1; private readonly IHttpClient _httpClient; private readonly IAppFolderInfo _appFolderInfo; private readonly ICached _cache; private readonly Logger _logger; private readonly IDeserializer _deserializer = new DeserializerBuilder() .IgnoreUnmatchedProperties() .WithNamingConvention(CamelCaseNamingConvention.Instance) .Build(); public IndexerDefinitionUpdateService(IHttpClient httpClient, IAppFolderInfo appFolderInfo, ICacheManager cacheManager, Logger logger) { _appFolderInfo = appFolderInfo; _cache = cacheManager.GetCache(typeof(CardigannDefinition), "definitions"); _httpClient = httpClient; _logger = logger; } public List All() { var request = new HttpRequest($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}"); var response = _httpClient.Get>(request); var remoteDefs = response.Resource.ToDictionary(x => x.File); var startupFolder = _appFolderInfo.StartUpFolder; var prefix = Path.Combine(startupFolder, "Definitions"); var directoryInfos = new List { new DirectoryInfo(prefix) }; var existingDirectories = directoryInfos.Where(d => d.Exists); var files = existingDirectories.SelectMany(d => d.GetFiles("*.yml")); var indexerList = new List(); foreach (var file in files) { indexerList.AddIfNotNull(remoteDefs[Path.GetFileNameWithoutExtension(file.Name)]); } return indexerList; } public CardigannDefinition GetDefinition(string file) { if (string.IsNullOrEmpty(file)) { throw new ArgumentNullException(nameof(file)); } var definition = _cache.Get(file, () => LoadIndexerDef(file)); return definition; } private CardigannDefinition GetHttpDefinition(string id) { var req = new HttpRequest($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}/{id}"); var response = _httpClient.Get(req); return _deserializer.Deserialize(response.Content); } private CardigannDefinition LoadIndexerDef(string fileKey) { if (string.IsNullOrEmpty(fileKey)) { throw new ArgumentNullException(nameof(fileKey)); } var definitionFolder = Path.Combine(_appFolderInfo.StartUpFolder, "Definitions"); var directoryInfo = new DirectoryInfo(definitionFolder); if (directoryInfo.Exists) { var files = directoryInfo.GetFiles($"{fileKey}.yml"); if (files.Any()) { var file = files.First(); _logger.Trace("Loading Cardigann definition " + file.FullName); try { var definitionString = File.ReadAllText(file.FullName); var definition = _deserializer.Deserialize(definitionString); return definition; } catch (Exception e) { _logger.Error($"Error while parsing Cardigann definition {file.FullName}\n{e}"); } } } else { //Do some things here to get it from server if we don't have it local } throw new ArgumentOutOfRangeException(nameof(fileKey)); } public void Execute(IndexerDefinitionUpdateCommand message) { UpdateLocalDefinitions(); } private void UpdateLocalDefinitions() { var request = new HttpRequest($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}"); var response = _httpClient.Get>(request); foreach (var def in response.Resource) { try { var startupFolder = _appFolderInfo.StartUpFolder; var saveFile = Path.Combine(startupFolder, "Definitions", $"{def.File}.yml"); _httpClient.DownloadFile($"https://indexers.prowlarr.com/master/{DEFINITION_VERSION}/{def.File}", saveFile); _cache.Remove(def.File); _logger.Info("Updated definition: {0}", def.File); } catch (Exception ex) { _logger.Error("Definition download failed: {0}, {1}", def.File, ex.Message); } } } } }