diff --git a/src/NzbDrone.Core.Test/Datastore/Migration/178_new_list_serverFixture.cs b/src/NzbDrone.Core.Test/Datastore/Migration/178_new_list_serverFixture.cs new file mode 100644 index 000000000..011bb441e --- /dev/null +++ b/src/NzbDrone.Core.Test/Datastore/Migration/178_new_list_serverFixture.cs @@ -0,0 +1,174 @@ +using System.Security.Policy; +using System.Text.Json; +using System.Text.Json.Serialization; +using FizzWare.NBuilder; +using FluentAssertions; +using NUnit.Framework; +using NzbDrone.Core.Datastore.Migration; +using NzbDrone.Core.NetImport.RadarrList; +using NzbDrone.Core.NetImport.RadarrList2.IMDbList; +using NzbDrone.Core.NetImport.RadarrList2.StevenLu; +using NzbDrone.Core.NetImport.StevenLu; +using NzbDrone.Core.Test.Framework; + +namespace NzbDrone.Core.Test.Datastore.Migration +{ + [TestFixture] + public class new_list_serverFixture : MigrationTest + { + private JsonSerializerOptions _serializerSettings; + + [SetUp] + public void Setup() + { + _serializerSettings = new JsonSerializerOptions + { + AllowTrailingCommas = true, + IgnoreNullValues = false, + PropertyNameCaseInsensitive = true, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + + _serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true)); + } + + [TestCase("https://api.radarr.video/v2")] + [TestCase("https://staging.api.radarr.video")] + public void should_switch_some_radarr_to_imdb(string url) + { + var db = WithMigrationTestDb(c => + { + var rows = Builder.CreateListOfSize(6) + .All() + .With(x => x.Implementation = typeof(RadarrListImport).Name) + .With(x => x.ConfigContract = typeof(RadarrListSettings).Name) + .TheFirst(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.RadarrListSettings177 + { + APIURL = url, + Path = "/imdb/top250" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.RadarrListSettings177 + { + APIURL = url, + Path = "/imdb/popular" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.RadarrListSettings177 + { + APIURL = url, + Path = "/imdb/missing" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.RadarrListSettings177 + { + APIURL = url, + Path = "/imdb/list?listId=ls001" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.RadarrListSettings177 + { + APIURL = url, + Path = "/imdb/list?listId=ls00ad" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.RadarrListSettings177 + { + APIURL = url, + Path = "/imdb/list?listId=ur002" + }, _serializerSettings)) + .BuildListOfNew(); + + var i = 1; + foreach (var row in rows) + { + row.Id = i++; + c.Insert.IntoTable("NetImport").Row(row); + } + }); + + var items = db.Query("SELECT * FROM NetImport"); + + items.Should().HaveCount(6); + + VerifyRow(items[0], typeof(IMDbListImport).Name, typeof(IMDbListSettings).Name, new IMDbListSettings { ListId = "top250" }); + VerifyRow(items[1], typeof(IMDbListImport).Name, typeof(IMDbListSettings).Name, new IMDbListSettings { ListId = "popular" }); + VerifyRow(items[2], typeof(RadarrListImport).Name, typeof(RadarrListSettings).Name, new RadarrListSettings { Url = url + "/imdb/missing" }); + VerifyRow(items[3], typeof(IMDbListImport).Name, typeof(IMDbListSettings).Name, new IMDbListSettings { ListId = "ls001" }); + VerifyRow(items[4], typeof(RadarrListImport).Name, typeof(RadarrListSettings).Name, new RadarrListSettings { Url = url + "/imdb/list?listId=ls00ad" }); + VerifyRow(items[5], typeof(IMDbListImport).Name, typeof(IMDbListSettings).Name, new IMDbListSettings { ListId = "ur002" }); + } + + public void should_switch_some_stevenlu_stevenlu2() + { + var rows = Builder.CreateListOfSize(6) + .All() + .With(x => x.Implementation = typeof(StevenLuImport).Name) + .With(x => x.ConfigContract = typeof(StevenLuSettings).Name) + .TheFirst(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.StevenLuSettings178 + { + Link = "https://s3.amazonaws.com/popular-movies/movies.json" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.StevenLuSettings178 + { + Link = "https://s3.amazonaws.com/popular-movies/movies-metacritic-min50.json" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.StevenLuSettings178 + { + Link = "https://s3.amazonaws.com/popular-movies/movies-imdb-min8.json" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.StevenLuSettings178 + { + Link = "https://s3.amazonaws.com/popular-movies/movies-rottentomatoes-min70.json" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.StevenLuSettings178 + { + Link = "https://s3.amazonaws.com/popular-movies/movies-min70.json" + }, _serializerSettings)) + .TheNext(1) + .With(x => x.Settings = JsonSerializer.Serialize(new new_list_server.StevenLuSettings178 + { + Link = "https://aapjeisbaas.nl/api/v1/popular-movies/imdb?fresh=True&max=20&rating=6&votes=50000" + }, _serializerSettings)) + .BuildListOfNew(); + + var db = WithMigrationTestDb(c => + { + var i = 1; + foreach (var row in rows) + { + row.Id = i++; + c.Insert.IntoTable("NetImport").Row(row); + } + }); + + var items = db.Query("SELECT * FROM NetImport"); + + items.Should().HaveCount(5); + + VerifyRow(items[0], typeof(StevenLu2Import).Name, typeof(StevenLu2Import).Name, new StevenLu2Settings { Source = (int)StevenLuSource.Standard, MinScore = 5 }); + VerifyRow(items[1], typeof(StevenLu2Import).Name, typeof(StevenLu2Import).Name, new StevenLu2Settings { Source = (int)StevenLuSource.Metacritic, MinScore = 5 }); + VerifyRow(items[2], typeof(StevenLu2Import).Name, typeof(StevenLu2Import).Name, new StevenLu2Settings { Source = (int)StevenLuSource.Imdb, MinScore = 8 }); + VerifyRow(items[3], typeof(StevenLu2Import).Name, typeof(StevenLu2Import).Name, new StevenLu2Settings { Source = (int)StevenLuSource.RottenTomatoes, MinScore = 7 }); + + // Bad formats so should not get changed + VerifyRow(items[4], rows[4].Implementation, rows[4].ConfigContract, rows[4].Settings); + VerifyRow(items[5], rows[5].Implementation, rows[5].ConfigContract, rows[5].Settings); + } + + private void VerifyRow(new_list_server.NetImportDefinition178 row, string impl, string config, object settings) + { + row.Implementation.Should().Be(impl); + row.ConfigContract.Should().Be(config); + row.Settings.Should().Be(JsonSerializer.Serialize(settings, _serializerSettings)); + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Migration/178_new_list_server.cs b/src/NzbDrone.Core/Datastore/Migration/178_new_list_server.cs new file mode 100644 index 000000000..f35943b8f --- /dev/null +++ b/src/NzbDrone.Core/Datastore/Migration/178_new_list_server.cs @@ -0,0 +1,221 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; +using Dapper; +using FluentMigrator; +using NzbDrone.Core.Datastore.Migration.Framework; + +namespace NzbDrone.Core.Datastore.Migration +{ + [Migration(178)] + public class new_list_server : NzbDroneMigrationBase + { + private static readonly Regex ImdbIdRegex = new Regex(@"^/?imdb/list\?listId=(?(ls|ur)\d+)$", + RegexOptions.Compiled | RegexOptions.IgnoreCase); + private readonly JsonSerializerOptions _serializerSettings; + + public new_list_server() + { + _serializerSettings = new JsonSerializerOptions + { + AllowTrailingCommas = true, + IgnoreNullValues = false, + PropertyNameCaseInsensitive = true, + DictionaryKeyPolicy = JsonNamingPolicy.CamelCase, + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + WriteIndented = true + }; + + _serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true)); + } + + protected override void MainDbUpgrade() + { + Execute.WithConnection(FixRadarrLists); + Execute.WithConnection(FixStevenLuLists); + } + + private void FixRadarrLists(IDbConnection conn, IDbTransaction tran) + { + var rows = conn.Query($"SELECT * FROM NetImport WHERE ConfigContract = 'RadarrListSettings'"); + + var radarrUrls = new List + { + "https://api.radarr.video/v2", + "https://staging.api.radarr.video" + }; + + foreach (var row in rows) + { + var settings = JsonSerializer.Deserialize(row.Settings, _serializerSettings); + object newSettings; + + if (!radarrUrls.Contains(settings.APIURL)) + { + // Combine root and path in new settings + newSettings = new RadarrListSettings178 + { + Url = settings.APIURL.TrimEnd('/') + '/' + settings.Path.TrimStart('/') + }; + } + else + { + // It should be an imdb list + if (settings.Path == "/imdb/top250") + { + newSettings = new IMDbListSettings178 + { + ListId = "top250" + }; + row.ConfigContract = "IMDbListSettings"; + row.Implementation = "IMDbListImport"; + } + else if (settings.Path == "/imdb/popular") + { + newSettings = new IMDbListSettings178 + { + ListId = "popular" + }; + row.ConfigContract = "IMDbListSettings"; + row.Implementation = "IMDbListImport"; + } + else + { + var match = ImdbIdRegex.Match(settings.Path); + if (match.Success) + { + newSettings = new IMDbListSettings178 + { + ListId = match.Groups["id"].Value + }; + row.ConfigContract = "IMDbListSettings"; + row.Implementation = "IMDbListImport"; + } + else + { + newSettings = new RadarrListSettings178 + { + Url = settings.APIURL.TrimEnd('/') + '/' + settings.Path.TrimStart('/') + }; + } + } + } + + row.Settings = JsonSerializer.Serialize(newSettings, _serializerSettings); + } + + var updateSql = "UPDATE NetImport SET Implementation = @Implementation, " + + "ConfigContract = @ConfigContract, " + + "Settings = @Settings " + + "WHERE Id = @Id"; + + conn.Execute(updateSql, rows, transaction: tran); + } + + private void FixStevenLuLists(IDbConnection conn, IDbTransaction tran) + { + var rows = conn.Query($"SELECT * FROM NetImport WHERE ConfigContract = 'StevenLuSettings'"); + + var updated = new List(); + + var scores = new[] { 5, 6, 7, 8, 50, 60, 70, 80 }; + + foreach (var row in rows) + { + var settings = JsonSerializer.Deserialize(row.Settings, _serializerSettings); + + if (settings.Link.StartsWith("https://s3.amazonaws.com/popular-movies")) + { + var newSettings = new StevenLu2Settings178(); + + // convert to 2 + if (settings.Link == "https://s3.amazonaws.com/popular-movies/movies.json") + { + newSettings.Source = (int)StevenLuSource178.Standard; + newSettings.MinScore = 5; + updated.Add(row); + } + else + { + var split = settings.Link.Split('/').Last().Split('-'); + if (split.Length == 3 && + split[0] == "movies" && + Enum.TryParse(split[1], out StevenLuSource178 source) && + int.TryParse(split[2], out var score) && + scores.Contains(score)) + { + newSettings.Source = (int)source; + newSettings.MinScore = source == StevenLuSource178.Imdb ? score : score / 10; + updated.Add(row); + } + } + + row.ConfigContract = "StevenLu2Settings"; + row.Implementation = "StevenLu2Import"; + row.Settings = JsonSerializer.Serialize(newSettings, _serializerSettings); + } + } + + var updateSql = "UPDATE NetImport SET Implementation = @Implementation, " + + "ConfigContract = @ConfigContract, " + + "Settings = @Settings " + + "WHERE Id = @Id"; + conn.Execute(updateSql, updated, transaction: tran); + } + + public class NetImportDefinition178 : ModelBase + { + public bool Enabled { get; set; } + public string Name { get; set; } + public string Implementation { get; set; } + public string ConfigContract { get; set; } + public string Settings { get; set; } + public bool EnableAuto { get; set; } + public string RootFolderPath { get; set; } + public bool ShouldMonitor { get; set; } + public int ProfileId { get; set; } + public int MinimumAvailability { get; set; } + public string Tags { get; set; } + } + + public class RadarrListSettings177 + { + public string APIURL { get; set; } + public string Path { get; set; } + } + + public class RadarrListSettings178 + { + public string Url { get; set; } + } + + public class IMDbListSettings178 + { + public string ListId { get; set; } + } + + public class StevenLuSettings178 + { + public string Link { get; set; } + } + + public class StevenLu2Settings178 + { + public int Source { get; set; } + + public int MinScore { get; set; } + } + + public enum StevenLuSource178 + { + Standard, + Imdb, + Metacritic, + RottenTomatoes + } + } +} diff --git a/src/NzbDrone.Core/NetImport/NetImportType.cs b/src/NzbDrone.Core/NetImport/NetImportType.cs index 7b75c288b..ea432592f 100644 --- a/src/NzbDrone.Core/NetImport/NetImportType.cs +++ b/src/NzbDrone.Core/NetImport/NetImportType.cs @@ -5,6 +5,7 @@ namespace NzbDrone.Core.NetImport Program, TMDB, Trakt, - Other + Other, + Advanced } } diff --git a/src/NzbDrone.Core/NetImport/RSSImport/RSSImport.cs b/src/NzbDrone.Core/NetImport/RSSImport/RSSImport.cs index d487b5f2f..e373e1671 100644 --- a/src/NzbDrone.Core/NetImport/RSSImport/RSSImport.cs +++ b/src/NzbDrone.Core/NetImport/RSSImport/RSSImport.cs @@ -9,9 +9,9 @@ namespace NzbDrone.Core.NetImport.RSSImport { public class RSSImport : HttpNetImportBase { - public override string Name => "RSSList"; + public override string Name => "RSS List"; - public override NetImportType ListType => NetImportType.Other; + public override NetImportType ListType => NetImportType.Advanced; public override bool Enabled => true; public override bool EnableAuto => false; diff --git a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListImport.cs b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListImport.cs index 0fbda3ec3..0d45f0e1d 100644 --- a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListImport.cs +++ b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListImport.cs @@ -9,9 +9,9 @@ namespace NzbDrone.Core.NetImport.RadarrList { public class RadarrListImport : HttpNetImportBase { - public override string Name => "Radarr Lists"; + public override string Name => "Custom Lists"; - public override NetImportType ListType => NetImportType.Other; + public override NetImportType ListType => NetImportType.Advanced; public override bool Enabled => true; public override bool EnableAuto => false; @@ -32,34 +32,6 @@ namespace NzbDrone.Core.NetImport.RadarrList { yield return def; } - - yield return new NetImportDefinition - { - Name = "IMDb Top 250", - Enabled = Enabled, - EnableAuto = true, - ProfileId = 1, - Implementation = GetType().Name, - Settings = new RadarrListSettings { Path = "/imdb/top250" }, - }; - yield return new NetImportDefinition - { - Name = "IMDb Popular Movies", - Enabled = Enabled, - EnableAuto = true, - ProfileId = 1, - Implementation = GetType().Name, - Settings = new RadarrListSettings { Path = "/imdb/popular" }, - }; - yield return new NetImportDefinition - { - Name = "IMDb List", - Enabled = Enabled, - EnableAuto = true, - ProfileId = 1, - Implementation = GetType().Name, - Settings = new RadarrListSettings { Path = "/imdb/list?listId=LISTID" }, - }; } } diff --git a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListRequestGenerator.cs b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListRequestGenerator.cs index eca3002f8..4ea030688 100644 --- a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListRequestGenerator.cs +++ b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListRequestGenerator.cs @@ -12,20 +12,11 @@ namespace NzbDrone.Core.NetImport.RadarrList 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); + var request = new NetImportRequest(Settings.Url, HttpAccept.Json); request.HttpRequest.SuppressHttpError = true; diff --git a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListSettings.cs b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListSettings.cs index b86f007a4..5c03aec8e 100644 --- a/src/NzbDrone.Core/NetImport/RadarrList/RadarrListSettings.cs +++ b/src/NzbDrone.Core/NetImport/RadarrList/RadarrListSettings.cs @@ -9,7 +9,7 @@ namespace NzbDrone.Core.NetImport.RadarrList { public RadarrSettingsValidator() { - RuleFor(c => c.APIURL).ValidRootUrl(); + RuleFor(c => c.Url).ValidRootUrl(); } } @@ -17,17 +17,8 @@ namespace NzbDrone.Core.NetImport.RadarrList { 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; } + [FieldDefinition(0, Label = "List URL", HelpText = "The URL for the movie list")] + public string Url { get; set; } public NzbDroneValidationResult Validate() { diff --git a/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListImport.cs b/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListImport.cs new file mode 100644 index 000000000..55e60180f --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListImport.cs @@ -0,0 +1,78 @@ +using System.Collections.Generic; +using NLog; +using NzbDrone.Common.Cloud; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; +using NzbDrone.Core.ThingiProvider; + +namespace NzbDrone.Core.NetImport.RadarrList2.IMDbList +{ + public class IMDbListImport : HttpNetImportBase + { + private readonly IHttpRequestBuilderFactory _radarrMetadata; + + public override string Name => "IMDb Lists"; + + public override NetImportType ListType => NetImportType.Other; + public override bool Enabled => true; + public override bool EnableAuto => false; + + public IMDbListImport(IRadarrCloudRequestBuilder requestBuilder, + IHttpClient httpClient, + INetImportStatusService netImportStatusService, + IConfigService configService, + IParsingService parsingService, + Logger logger) + : base(httpClient, netImportStatusService, configService, parsingService, logger) + { + _radarrMetadata = requestBuilder.RadarrMetadata; + } + + public override IEnumerable DefaultDefinitions + { + get + { + foreach (var def in base.DefaultDefinitions) + { + yield return def; + } + + yield return new NetImportDefinition + { + Name = "IMDb Top 250", + Enabled = Enabled, + EnableAuto = true, + ProfileId = 1, + Implementation = GetType().Name, + Settings = new IMDbListSettings { ListId = "top250" }, + }; + yield return new NetImportDefinition + { + Name = "IMDb Popular Movies", + Enabled = Enabled, + EnableAuto = true, + ProfileId = 1, + Implementation = GetType().Name, + Settings = new IMDbListSettings { ListId = "popular" }, + }; + } + } + + public override INetImportRequestGenerator GetRequestGenerator() + { + return new IMDbListRequestGenerator() + { + Settings = Settings, + Logger = _logger, + HttpClient = _httpClient, + RequestBuilder = _radarrMetadata + }; + } + + public override IParseNetImportResponse GetParser() + { + return new RadarrList2Parser(); + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListRequestGenerator.cs b/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListRequestGenerator.cs new file mode 100644 index 000000000..122ae9de8 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListRequestGenerator.cs @@ -0,0 +1,16 @@ +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.NetImport.RadarrList2.IMDbList +{ + public class IMDbListRequestGenerator : RadarrList2RequestGeneratorBase + { + public IMDbListSettings Settings { get; set; } + + protected override HttpRequest GetHttpRequest() + { + return RequestBuilder.Create() + .SetSegment("route", $"list/imdb/{Settings.ListId}") + .Build(); + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListSettings.cs b/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListSettings.cs new file mode 100644 index 000000000..7483d29b2 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList2/IMDb/IMDbListSettings.cs @@ -0,0 +1,34 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.NetImport.RadarrList2.IMDbList +{ + public class IMDbSettingsValidator : AbstractValidator + { + public IMDbSettingsValidator() + { + RuleFor(c => c.ListId) + .Matches(@"^top250$|^popular$|^ls\d+$|^ur\d+$") + .WithMessage("List ID mist be 'top250', 'popular', an IMDb List ID of the form 'ls12345678' or an IMDb user watchlist of the form 'ur12345678'"); + } + } + + public class IMDbListSettings : IProviderConfig + { + private static readonly IMDbSettingsValidator Validator = new IMDbSettingsValidator(); + + public IMDbListSettings() + { + } + + [FieldDefinition(1, Label = "List/User ID", HelpText = "IMDb list ID (e.g ls12345678), IMDb user ID (e.g. ur12345678), 'top250' or 'popular'")] + public string ListId { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList2/RadarrList2Parser.cs b/src/NzbDrone.Core/NetImport/RadarrList2/RadarrList2Parser.cs new file mode 100644 index 000000000..c38bcd4ac --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList2/RadarrList2Parser.cs @@ -0,0 +1,56 @@ +using System.Collections.Generic; +using System.Net; +using Newtonsoft.Json; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.MetadataSource.SkyHook.Resource; +using NzbDrone.Core.Movies; +using NzbDrone.Core.NetImport.Exceptions; + +namespace NzbDrone.Core.NetImport.RadarrList2 +{ + public class RadarrList2Parser : IParseNetImportResponse + { + 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(m => new Movie { TmdbId = m.TmdbId }); + } + + protected virtual bool PreProcess(NetImportResponse listResponse) + { + if (listResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + { + throw new NetImportException(listResponse, + "Radarr API call resulted in an unexpected StatusCode [{0}]", + listResponse.HttpResponse.StatusCode); + } + + if (listResponse.HttpResponse.Headers.ContentType != null && + listResponse.HttpResponse.Headers.ContentType.Contains("text/json") && + listResponse.HttpRequest.Headers.Accept != null && + !listResponse.HttpRequest.Headers.Accept.Contains("text/json")) + { + throw new NetImportException(listResponse, + "Radarr API responded with html content. Site is likely blocked or unavailable."); + } + + return true; + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList2/RadarrList2RequestGenerator.cs b/src/NzbDrone.Core/NetImport/RadarrList2/RadarrList2RequestGenerator.cs new file mode 100644 index 000000000..d2f3b8dd9 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList2/RadarrList2RequestGenerator.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using NLog; +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.NetImport.RadarrList2 +{ + public abstract class RadarrList2RequestGeneratorBase : INetImportRequestGenerator + { + public IHttpRequestBuilderFactory RequestBuilder { get; set; } + public IHttpClient HttpClient { get; set; } + public Logger Logger { get; set; } + + protected abstract HttpRequest GetHttpRequest(); + + public virtual NetImportPageableRequestChain GetMovies() + { + var pageableRequests = new NetImportPageableRequestChain(); + + var httpRequest = GetHttpRequest(); + + var request = new NetImportRequest(httpRequest.Url.ToString(), HttpAccept.Json); + + request.HttpRequest.SuppressHttpError = true; + + pageableRequests.Add(new List { request }); + return pageableRequests; + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2Import.cs b/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2Import.cs new file mode 100644 index 000000000..0e4a0116f --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2Import.cs @@ -0,0 +1,46 @@ +using NLog; +using NzbDrone.Common.Cloud; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Parser; + +namespace NzbDrone.Core.NetImport.RadarrList2.StevenLu +{ + public class StevenLu2Import : HttpNetImportBase + { + private readonly IHttpRequestBuilderFactory _radarrMetadata; + + public override string Name => "StevenLu List"; + + public override NetImportType ListType => NetImportType.Other; + public override bool Enabled => true; + public override bool EnableAuto => false; + + public StevenLu2Import(IRadarrCloudRequestBuilder requestBuilder, + IHttpClient httpClient, + INetImportStatusService netImportStatusService, + IConfigService configService, + IParsingService parsingService, + Logger logger) + : base(httpClient, netImportStatusService, configService, parsingService, logger) + { + _radarrMetadata = requestBuilder.RadarrMetadata; + } + + public override INetImportRequestGenerator GetRequestGenerator() + { + return new StevenLu2RequestGenerator() + { + Settings = Settings, + Logger = _logger, + HttpClient = _httpClient, + RequestBuilder = _radarrMetadata + }; + } + + public override IParseNetImportResponse GetParser() + { + return new RadarrList2Parser(); + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2RequestGenerator.cs b/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2RequestGenerator.cs new file mode 100644 index 000000000..6f3b6bfd5 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2RequestGenerator.cs @@ -0,0 +1,25 @@ +using NzbDrone.Common.Http; + +namespace NzbDrone.Core.NetImport.RadarrList2.StevenLu +{ + public class StevenLu2RequestGenerator : RadarrList2RequestGeneratorBase + { + public StevenLu2Settings Settings { get; set; } + + protected override HttpRequest GetHttpRequest() + { + var builder = RequestBuilder.Create() + .SetSegment("route", $"list/stevenlu"); + + if (Settings.Source != (int)StevenLuSource.Standard) + { + var source = ((StevenLuSource)Settings.Source).ToString().ToLower(); + + var minScore = Settings.Source == (int)StevenLuSource.Imdb ? Settings.MinScore : Settings.MinScore * 10; + builder.Resource($"{source}/{minScore}"); + } + + return builder.Build(); + } + } +} diff --git a/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2Settings.cs b/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2Settings.cs new file mode 100644 index 000000000..6d2eef6a2 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/RadarrList2/StevenLu/StevenLu2Settings.cs @@ -0,0 +1,44 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.NetImport.RadarrList2.StevenLu +{ + public class StevenLu2SettingsValidator : AbstractValidator + { + public StevenLu2SettingsValidator() + { + RuleFor(c => c.MinScore).GreaterThanOrEqualTo(x => 5).LessThanOrEqualTo(x => 8); + } + } + + public enum StevenLuSource + { + Standard, + Imdb, + Metacritic, + RottenTomatoes + } + + public class StevenLu2Settings : IProviderConfig + { + private static readonly StevenLu2SettingsValidator Validator = new StevenLu2SettingsValidator(); + + public StevenLu2Settings() + { + MinScore = 5; + } + + [FieldDefinition(1, Label = "Rating source", Type = FieldType.Select, SelectOptions = typeof(StevenLuSource), HelpText = "StevenLu ratings source")] + public int Source { get; set; } + + [FieldDefinition(1, Label = "Minimum Score", Type = FieldType.Number, HelpText = "Only applies if 'Rating source' is not 'Standard'")] + public int MinScore { get; set; } + + public NzbDroneValidationResult Validate() + { + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } +} diff --git a/src/NzbDrone.Core/NetImport/StevenLu/StevenLuImport.cs b/src/NzbDrone.Core/NetImport/StevenLu/StevenLuImport.cs index 46b13b78c..2c849f458 100644 --- a/src/NzbDrone.Core/NetImport/StevenLu/StevenLuImport.cs +++ b/src/NzbDrone.Core/NetImport/StevenLu/StevenLuImport.cs @@ -7,9 +7,9 @@ namespace NzbDrone.Core.NetImport.StevenLu { public class StevenLuImport : HttpNetImportBase { - public override string Name => "StevenLu"; + public override string Name => "StevenLu Custom"; - public override NetImportType ListType => NetImportType.Other; + public override NetImportType ListType => NetImportType.Advanced; public override bool Enabled => true; public override bool EnableAuto => false;