From 7adfe65f65e594e48b7541dc3248dd67ddfebe1a Mon Sep 17 00:00:00 2001 From: Qstick Date: Sun, 12 Jan 2020 22:48:13 -0500 Subject: [PATCH] New: Import from Another Radarr Instance Co-Authored-By: Leonardo Galli --- .../NetImport/NetImportFactory.cs | 2 +- .../NetImport/Radarr/RadarrAPIResource.cs | 19 +++++ .../NetImport/Radarr/RadarrImport.cs | 39 ++++++++++ .../NetImport/Radarr/RadarrParser.cs | 74 ++++++++----------- .../Radarr/RadarrRequestGenerator.cs | 14 ++-- .../NetImport/Radarr/RadarrSettings.cs | 15 ++-- .../RadarrListImport.cs} | 20 ++--- .../NetImport/RadarrList/RadarrListParser.cs | 67 +++++++++++++++++ .../RadarrList/RadarrListRequestGenerator.cs | 36 +++++++++ .../RadarrList/RadarrListSettings.cs | 37 ++++++++++ 10 files changed, 252 insertions(+), 71 deletions(-) create mode 100644 src/NzbDrone.Core/NetImport/Radarr/RadarrAPIResource.cs create mode 100644 src/NzbDrone.Core/NetImport/Radarr/RadarrImport.cs rename src/NzbDrone.Core/NetImport/{Radarr/RadarrLists.cs => RadarrList/RadarrListImport.cs} (80%) create mode 100644 src/NzbDrone.Core/NetImport/RadarrList/RadarrListParser.cs create mode 100644 src/NzbDrone.Core/NetImport/RadarrList/RadarrListRequestGenerator.cs create mode 100644 src/NzbDrone.Core/NetImport/RadarrList/RadarrListSettings.cs diff --git a/src/NzbDrone.Core/NetImport/NetImportFactory.cs b/src/NzbDrone.Core/NetImport/NetImportFactory.cs index 00773945c..122344d79 100644 --- a/src/NzbDrone.Core/NetImport/NetImportFactory.cs +++ b/src/NzbDrone.Core/NetImport/NetImportFactory.cs @@ -51,7 +51,7 @@ namespace NzbDrone.Core.NetImport public List Discoverable() { - var enabledImporters = GetAvailableProviders().Where(n => (n.GetType() == typeof(Radarr.RadarrLists) || n.GetType() == typeof(TMDb.Popular.TMDbPopularImport))); + var enabledImporters = GetAvailableProviders().Where(n => (n.GetType() == typeof(RadarrList.RadarrListImport) || n.GetType() == typeof(TMDb.Popular.TMDbPopularImport))); var indexers = FilterBlockedIndexers(enabledImporters); return indexers.ToList(); } diff --git a/src/NzbDrone.Core/NetImport/Radarr/RadarrAPIResource.cs b/src/NzbDrone.Core/NetImport/Radarr/RadarrAPIResource.cs new file mode 100644 index 000000000..8e88027e4 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/Radarr/RadarrAPIResource.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace NzbDrone.Core.NetImport.Radarr +{ + public class RadarrMovie + { + public string Title { get; set; } + public string SortTitle { get; set; } + public int TmdbId { get; set; } + public string Overview { get; set; } + public List Images { get; set; } + public bool Monitored { get; set; } + public DateTime InCinemas { get; set; } + public DateTime PhysicalRelease { get; set; } + public int Year { get; set; } + public string TitleSlug { get; set; } + } +} diff --git a/src/NzbDrone.Core/NetImport/Radarr/RadarrImport.cs b/src/NzbDrone.Core/NetImport/Radarr/RadarrImport.cs new file mode 100644 index 000000000..43bbb8e54 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/Radarr/RadarrImport.cs @@ -0,0 +1,39 @@ +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; + +namespace NzbDrone.Core.NetImport.Radarr +{ + public class RadarrImport : HttpNetImportBase + { + public override string Name => "Radarr"; + public override bool Enabled => true; + public override bool EnableAuto => false; + + public override NetImportType ListType => NetImportType.Other; + + public RadarrImport(IHttpClient httpClient, + IConfigService configService, + IParsingService parsingService, + Logger logger) + : base(httpClient, configService, parsingService, logger) + { + } + + public override INetImportRequestGenerator GetRequestGenerator() + { + return new RadarrRequestGenerator() + { + Settings = Settings, + Logger = _logger, + HttpClient = _httpClient + }; + } + + public override IParseNetImportResponse GetParser() + { + return new RadarrParser(Settings); + } + } +} diff --git a/src/NzbDrone.Core/NetImport/Radarr/RadarrParser.cs b/src/NzbDrone.Core/NetImport/Radarr/RadarrParser.cs index fc0205b2c..3508f557d 100644 --- a/src/NzbDrone.Core/NetImport/Radarr/RadarrParser.cs +++ b/src/NzbDrone.Core/NetImport/Radarr/RadarrParser.cs @@ -1,69 +1,53 @@ -using System.Collections.Generic; -using Newtonsoft.Json; -using NzbDrone.Common.Extensions; -using NzbDrone.Common.Http; -using NzbDrone.Core.MetadataSource; -using NzbDrone.Core.MetadataSource.RadarrAPI; -using NzbDrone.Core.MetadataSource.SkyHook.Resource; +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.Movies; namespace NzbDrone.Core.NetImport.Radarr { public class RadarrParser : IParseNetImportResponse { private readonly RadarrSettings _settings; - private readonly ISearchForNewMovie _skyhookProxy; - private NetImportResponse _importResponse; - - public RadarrParser(RadarrSettings settings, ISearchForNewMovie skyhookProxy) + public RadarrParser(RadarrSettings settings) { - _skyhookProxy = skyhookProxy; _settings = settings; } - public IList ParseResponse(NetImportResponse importResponse) + public IList ParseResponse(NetImportResponse netMovieImporterResponse) { - _importResponse = importResponse; - - var movies = new List(); - - if (!PreProcess(_importResponse)) - { - return movies; - } + var remoteMovies = Json.Deserialize>(netMovieImporterResponse.Content); - var jsonResponse = JsonConvert.DeserializeObject>(_importResponse.Content); + var movies = new List(); - // no movies were return - if (jsonResponse == null) + foreach (var remoteMovie in remoteMovies) { - return movies; + movies.Add(new Movie + { + TmdbId = remoteMovie.TmdbId, + Title = remoteMovie.Title, + SortTitle = remoteMovie.SortTitle, + TitleSlug = remoteMovie.TitleSlug, + Overview = remoteMovie.Overview, + Images = remoteMovie.Images.Select(x => MapImage(x, _settings.BaseUrl)).ToList(), + Monitored = remoteMovie.Monitored, + PhysicalRelease = remoteMovie.PhysicalRelease, + InCinemas = remoteMovie.InCinemas, + Year = remoteMovie.Year + }); } - return jsonResponse.SelectList(_skyhookProxy.MapMovie); + return movies; } - protected virtual bool PreProcess(NetImportResponse netImportResponse) + private static MediaCover.MediaCover MapImage(MediaCover.MediaCover arg, string baseUrl) { - try - { - var error = JsonConvert.DeserializeObject(netImportResponse.HttpResponse.Content); - - if (error != null && error.Errors != null && error.Errors.Count != 0) - { - throw new RadarrAPIException(error); - } - } - catch (JsonSerializationException) - { - //No error! - } - - if (netImportResponse.HttpResponse.StatusCode != System.Net.HttpStatusCode.OK) + var newImage = new MediaCover.MediaCover { - throw new HttpException(netImportResponse.HttpRequest, netImportResponse.HttpResponse); - } + Url = string.Format("{0}{1}", baseUrl, arg.Url), + CoverType = arg.CoverType + }; - return true; + return newImage; } } } diff --git a/src/NzbDrone.Core/NetImport/Radarr/RadarrRequestGenerator.cs b/src/NzbDrone.Core/NetImport/Radarr/RadarrRequestGenerator.cs index 60dbab9ad..09db9cada 100644 --- a/src/NzbDrone.Core/NetImport/Radarr/RadarrRequestGenerator.cs +++ b/src/NzbDrone.Core/NetImport/Radarr/RadarrRequestGenerator.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using NLog; -using NzbDrone.Common.Extensions; using NzbDrone.Common.Http; namespace NzbDrone.Core.NetImport.Radarr @@ -12,24 +10,24 @@ namespace NzbDrone.Core.NetImport.Radarr public IHttpClient HttpClient { get; set; } public Logger Logger { get; set; } - public int MaxPages { get; set; } - public RadarrRequestGenerator() { - MaxPages = 3; } public virtual NetImportPageableRequestChain GetMovies() { var pageableRequests = new NetImportPageableRequestChain(); - var baseUrl = $"{Settings.APIURL.TrimEnd("/")}"; + var baseUrl = Settings.BaseUrl.TrimEnd('/'); + + var request = new NetImportRequest($"{baseUrl}/api/v3/movie", HttpAccept.Json); - var request = new NetImportRequest($"{baseUrl}{Settings.Path}", HttpAccept.Json); + request.HttpRequest.Headers["X-Api-Key"] = Settings.ApiKey; request.HttpRequest.SuppressHttpError = true; pageableRequests.Add(new List { request }); + return pageableRequests; } } diff --git a/src/NzbDrone.Core/NetImport/Radarr/RadarrSettings.cs b/src/NzbDrone.Core/NetImport/Radarr/RadarrSettings.cs index cee94ad98..fc24e6076 100644 --- a/src/NzbDrone.Core/NetImport/Radarr/RadarrSettings.cs +++ b/src/NzbDrone.Core/NetImport/Radarr/RadarrSettings.cs @@ -9,7 +9,8 @@ namespace NzbDrone.Core.NetImport.Radarr { public RadarrSettingsValidator() { - RuleFor(c => c.APIURL).ValidRootUrl(); + RuleFor(c => c.BaseUrl).ValidRootUrl(); + RuleFor(c => c.ApiKey).NotEmpty(); } } @@ -19,15 +20,15 @@ namespace NzbDrone.Core.NetImport.Radarr public RadarrSettings() { - APIURL = "https://api.radarr.video/v2"; - Path = ""; + BaseUrl = ""; + ApiKey = ""; } - [FieldDefinition(0, Label = "Radarr API URL", HelpText = "Link to to Radarr API URL. Use https://staging.api.radarr.video if you are on nightly.")] - public string APIURL { get; set; } + [FieldDefinition(0, Label = "Full URL", HelpText = "URL, including port, of the Radarr V3 instance to import from")] + public string BaseUrl { get; set; } - [FieldDefinition(1, Label = "Path to list", HelpText = "Path to the list proxied by the Radarr API. Check the wiki for available lists.")] - public string Path { get; set; } + [FieldDefinition(1, Label = "API Key", HelpText = "Apikey of the Radarr V3 instance to import from")] + public string ApiKey { get; set; } public NzbDroneValidationResult Validate() { diff --git a/src/NzbDrone.Core/NetImport/Radarr/RadarrLists.cs b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListImport.cs similarity index 80% rename from src/NzbDrone.Core/NetImport/Radarr/RadarrLists.cs rename to src/NzbDrone.Core/NetImport/RadarrList/RadarrListImport.cs index 6d316dad3..e0d583e9b 100644 --- a/src/NzbDrone.Core/NetImport/Radarr/RadarrLists.cs +++ b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListImport.cs @@ -6,19 +6,19 @@ using NzbDrone.Core.MetadataSource; using NzbDrone.Core.Parser; using NzbDrone.Core.ThingiProvider; -namespace NzbDrone.Core.NetImport.Radarr +namespace NzbDrone.Core.NetImport.RadarrList { - public class RadarrLists : HttpNetImportBase + public class RadarrListImport : HttpNetImportBase { + private readonly ISearchForNewMovie _skyhookProxy; + public override string Name => "Radarr Lists"; public override NetImportType ListType => NetImportType.Other; public override bool Enabled => true; public override bool EnableAuto => false; - private readonly ISearchForNewMovie _skyhookProxy; - - public RadarrLists(IHttpClient httpClient, + public RadarrListImport(IHttpClient httpClient, IConfigService configService, IParsingService parsingService, ISearchForNewMovie skyhookProxy, @@ -44,7 +44,7 @@ namespace NzbDrone.Core.NetImport.Radarr EnableAuto = true, ProfileId = 1, Implementation = GetType().Name, - Settings = new RadarrSettings { Path = "/imdb/top250" }, + Settings = new RadarrListSettings { Path = "/imdb/top250" }, }; yield return new NetImportDefinition { @@ -53,7 +53,7 @@ namespace NzbDrone.Core.NetImport.Radarr EnableAuto = true, ProfileId = 1, Implementation = GetType().Name, - Settings = new RadarrSettings { Path = "/imdb/popular" }, + Settings = new RadarrListSettings { Path = "/imdb/popular" }, }; yield return new NetImportDefinition { @@ -62,14 +62,14 @@ namespace NzbDrone.Core.NetImport.Radarr EnableAuto = true, ProfileId = 1, Implementation = GetType().Name, - Settings = new RadarrSettings { Path = "/imdb/list?listId=LISTID" }, + Settings = new RadarrListSettings { Path = "/imdb/list?listId=LISTID" }, }; } } public override INetImportRequestGenerator GetRequestGenerator() { - return new RadarrRequestGenerator() + return new RadarrListRequestGenerator() { Settings = Settings, Logger = _logger, @@ -79,7 +79,7 @@ namespace NzbDrone.Core.NetImport.Radarr public override IParseNetImportResponse GetParser() { - return new RadarrParser(Settings, _skyhookProxy); + return new RadarrListParser(_skyhookProxy); } } } diff --git a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListParser.cs b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListParser.cs new file mode 100644 index 000000000..197c516dd --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListParser.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using Newtonsoft.Json; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.MetadataSource; +using NzbDrone.Core.MetadataSource.RadarrAPI; +using NzbDrone.Core.MetadataSource.SkyHook.Resource; +using NzbDrone.Core.Movies; + +namespace NzbDrone.Core.NetImport.RadarrList +{ + public class RadarrListParser : IParseNetImportResponse + { + private readonly ISearchForNewMovie _skyhookProxy; + + public RadarrListParser(ISearchForNewMovie skyhookProxy) + { + _skyhookProxy = skyhookProxy; + } + + public IList ParseResponse(NetImportResponse netMovieImporterResponse) + { + var importResponse = netMovieImporterResponse; + + var movies = new List(); + + if (!PreProcess(importResponse)) + { + return movies; + } + + var jsonResponse = JsonConvert.DeserializeObject>(importResponse.Content); + + // no movies were return + if (jsonResponse == null) + { + return movies; + } + + return jsonResponse.SelectList(_skyhookProxy.MapMovie); + } + + protected virtual bool PreProcess(NetImportResponse netImportResponse) + { + try + { + var error = JsonConvert.DeserializeObject(netImportResponse.HttpResponse.Content); + + if (error != null && error.Errors != null && error.Errors.Count != 0) + { + throw new RadarrAPIException(error); + } + } + catch (JsonSerializationException) + { + //No error! + } + + if (netImportResponse.HttpResponse.StatusCode != System.Net.HttpStatusCode.OK) + { + throw new HttpException(netImportResponse.HttpRequest, netImportResponse.HttpResponse); + } + + return true; + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListRequestGenerator.cs b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListRequestGenerator.cs new file mode 100644 index 000000000..eca3002f8 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListRequestGenerator.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.NetImport.RadarrList +{ + public class RadarrListRequestGenerator : INetImportRequestGenerator + { + public RadarrListSettings Settings { get; set; } + public IHttpClient HttpClient { get; set; } + public Logger Logger { get; set; } + + public int MaxPages { get; set; } + + public RadarrListRequestGenerator() + { + MaxPages = 3; + } + + public virtual NetImportPageableRequestChain GetMovies() + { + var pageableRequests = new NetImportPageableRequestChain(); + + var baseUrl = $"{Settings.APIURL.TrimEnd("/")}"; + + var request = new NetImportRequest($"{baseUrl}{Settings.Path}", HttpAccept.Json); + + request.HttpRequest.SuppressHttpError = true; + + pageableRequests.Add(new List { request }); + return pageableRequests; + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListSettings.cs b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListSettings.cs new file mode 100644 index 000000000..b86f007a4 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListSettings.cs @@ -0,0 +1,37 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.NetImport.RadarrList +{ + public class RadarrSettingsValidator : AbstractValidator + { + public RadarrSettingsValidator() + { + RuleFor(c => c.APIURL).ValidRootUrl(); + } + } + + public class RadarrListSettings : IProviderConfig + { + private static readonly RadarrSettingsValidator Validator = new RadarrSettingsValidator(); + + public RadarrListSettings() + { + APIURL = "https://api.radarr.video/v2"; + Path = ""; + } + + [FieldDefinition(0, Label = "Radarr API URL", HelpText = "Link to to Radarr API URL. Use https://staging.api.radarr.video if you are on nightly.")] + public string APIURL { get; set; } + + [FieldDefinition(1, Label = "Path to list", HelpText = "Path to the list proxied by the Radarr API. Check the wiki for available lists.")] + public string Path { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +}