diff --git a/src/NzbDrone.Core/ImportLists/Custom/CustomAPIResource.cs b/src/NzbDrone.Core/ImportLists/Custom/CustomAPIResource.cs new file mode 100644 index 000000000..3dbe7e184 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Custom/CustomAPIResource.cs @@ -0,0 +1,7 @@ +namespace NzbDrone.Core.ImportLists.Custom +{ + public class CustomSeries + { + public int TvdbId { get; set; } + } +} diff --git a/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs b/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs new file mode 100644 index 000000000..1efc0d038 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Custom/CustomImport.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; + +using FluentValidation.Results; + +using NLog; + +using NzbDrone.Common.Extensions; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.ImportLists.Custom +{ + public class CustomImport : ImportListBase + { + private readonly ICustomProxy _customProxy; + public override string Name => "Custom List"; + + public override ImportListType ListType => ImportListType.Advanced; + + public CustomImport(ICustomProxy customProxy, + IImportListStatusService importListStatusService, + IConfigService configService, + IParsingService parsingService, + Logger logger) + : base(importListStatusService, configService, parsingService, logger) + { + _customProxy = customProxy; + } + + public override IList Fetch() + { + var series = new List(); + + try + { + var remoteSeries = _customProxy.GetSeries(Settings); + + foreach (var item in remoteSeries) + { + series.Add(new ImportListItemInfo + { + TvdbId = item.TvdbId + }); + } + + _importListStatusService.RecordSuccess(Definition.Id); + } + catch + { + _importListStatusService.RecordFailure(Definition.Id); + } + + return CleanupListItems(series); + } + + public override object RequestAction(string action, IDictionary query) + { + return new { }; + } + + protected override void Test(List failures) + { + failures.AddIfNotNull(_customProxy.Test(Settings)); + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/Custom/CustomImportProxy.cs b/src/NzbDrone.Core/ImportLists/Custom/CustomImportProxy.cs new file mode 100644 index 000000000..4eaac9976 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Custom/CustomImportProxy.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Net; + +using FluentValidation.Results; + +using Newtonsoft.Json; + +using NLog; + +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.ImportLists.Custom +{ + public interface ICustomImportProxy + { + List GetSeries(CustomSettings settings); + ValidationFailure Test(CustomSettings settings); + } + + public class CustomImportProxy : ICustomImportProxy + { + private readonly IHttpClient _httpClient; + private readonly Logger _logger; + + public CustomImportProxy(IHttpClient httpClient, Logger logger) + { + _httpClient = httpClient; + _logger = logger; + } + + public List GetSeries(CustomSettings settings) + { + return Execute(settings); + } + + public ValidationFailure Test(CustomSettings settings) + { + try + { + GetSeries(settings); + } + catch (HttpException ex) + { + if (ex.Response.StatusCode == HttpStatusCode.Unauthorized) + { + _logger.Error(ex, "There was an authorization issue. We cannot get the list from the provider."); + return new ValidationFailure("BaseUrl", "It seems we are unauthorized to make this request."); + } + + _logger.Error(ex, "Unable to send test message"); + return new ValidationFailure("BaseUrl", $"We are unable to make the request to that URL. StatusCode: {ex.Response.StatusCode}"); + } + catch (Exception ex) + { + _logger.Error(ex, "Unable to send test message"); + return new ValidationFailure("", "Unable to send test message"); + } + + return null; + } + + private List Execute(CustomSettings settings) + { + if (settings.BaseUrl.IsNullOrWhiteSpace()) + { + return new List(); + } + + var baseUrl = settings.BaseUrl.TrimEnd('/'); + var request = new HttpRequestBuilder(baseUrl).Accept(HttpAccept.Json).Build(); + var response = _httpClient.Get(request); + var results = JsonConvert.DeserializeObject>(response.Content); + + return results; + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/Custom/CustomSettings.cs b/src/NzbDrone.Core/ImportLists/Custom/CustomSettings.cs new file mode 100644 index 000000000..e756225ac --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Custom/CustomSettings.cs @@ -0,0 +1,33 @@ +using FluentValidation; + +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.ImportLists.Custom +{ + public class CustomSettingsValidator : AbstractValidator + { + public CustomSettingsValidator() + { + RuleFor(c => c.BaseUrl).ValidRootUrl(); + } + } + + public class CustomSettings : IImportListSettings + { + private static readonly CustomSettingsValidator Validator = new CustomSettingsValidator(); + + public CustomSettings() + { + BaseUrl = ""; + } + + [FieldDefinition(0, Label = "List URL", HelpText = "The URL for the series list")] + public string BaseUrl { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/ImportListType.cs b/src/NzbDrone.Core/ImportLists/ImportListType.cs index 1ccb1520e..179db800c 100644 --- a/src/NzbDrone.Core/ImportLists/ImportListType.cs +++ b/src/NzbDrone.Core/ImportLists/ImportListType.cs @@ -5,6 +5,7 @@ namespace NzbDrone.Core.ImportLists Program, Plex, Trakt, - Other + Other, + Advanced } }