diff --git a/src/NzbDrone.Core/NetImport/Trakt/TraktAPI.cs b/src/NzbDrone.Core/NetImport/Trakt/TraktAPI.cs new file mode 100644 index 000000000..175ff0555 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/Trakt/TraktAPI.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Windows.Forms; +using System.Xml.Serialization; + +namespace NzbDrone.Core.NetImport.Trakt +{ + public class Ids + { + public int trakt { get; set; } + public string slug { get; set; } + public string imdb { get; set; } + public int tmdb { get; set; } + } + + public class Movie + { + public string title { get; set; } + public int year { get; set; } + public Ids ids { get; set; } + } + + public class TraktResponse + { + public int rank { get; set; } + public string listed_at { get; set; } + public string type { get; set; } + public Movie movie { get; set; } + } +} diff --git a/src/NzbDrone.Core/NetImport/Trakt/TraktImport.cs b/src/NzbDrone.Core/NetImport/Trakt/TraktImport.cs new file mode 100644 index 000000000..1e0e083ae --- /dev/null +++ b/src/NzbDrone.Core/NetImport/Trakt/TraktImport.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Xml.Serialization; +using FluentValidation.Results; +using NLog; +using NzbDrone.Common.Http; +using NzbDrone.Core.Configuration; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.Indexers.PassThePopcorn; +using NzbDrone.Core.Parser; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Tv; + +namespace NzbDrone.Core.NetImport.Trakt +{ + public class TraktImport : HttpNetImportBase + { + public override string Name => "Trakt User List"; + public override bool Enabled => true; + public override bool EnableAuto => false; + + public TraktImport(IHttpClient httpClient, IConfigService configService, IParsingService parsingService, Logger logger) + : base(httpClient, configService, parsingService, logger) + { } + + public override INetImportRequestGenerator GetRequestGenerator() + { + return new TraktRequestGenerator() { Settings = Settings }; + } + + public override IParseNetImportResponse GetParser() + { + return new TraktParser(Settings); + } + } +} diff --git a/src/NzbDrone.Core/NetImport/Trakt/TraktParser.cs b/src/NzbDrone.Core/NetImport/Trakt/TraktParser.cs new file mode 100644 index 000000000..611dfc5b8 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/Trakt/TraktParser.cs @@ -0,0 +1,84 @@ +using Newtonsoft.Json; +using NzbDrone.Core.NetImport.Exceptions; +using NzbDrone.Core.Tv; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using System.Xml; +using System.Xml.Linq; +using NLog; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Http; +using NzbDrone.Core.Indexers; +using NzbDrone.Core.Indexers.Exceptions; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.NetImport.Trakt +{ + public class TraktParser : IParseNetImportResponse + { + private readonly TraktSettings _settings; + private NetImportResponse _importResponse; + private readonly Logger _logger; + + public TraktParser(TraktSettings settings) + { + _settings = settings; + } + + public IList ParseResponse(NetImportResponse importResponse) + { + _importResponse = importResponse; + + var movies = new List(); + + if (!PreProcess(_importResponse)) + { + return movies; + } + + var jsonResponse = JsonConvert.DeserializeObject>(_importResponse.Content); + + // no movies were return + if (jsonResponse == null) + { + return movies; + } + + foreach (var movie in jsonResponse) + { + movies.AddIfNotNull(new Tv.Movie() + { + Title = movie.movie.title, + ImdbId = movie.movie.ids.imdb, + TmdbId = movie.movie.ids.tmdb, + Year = movie.movie.year + }); + } + + return movies; + } + + protected virtual bool PreProcess(NetImportResponse indexerResponse) + { + if (indexerResponse.HttpResponse.StatusCode != HttpStatusCode.OK) + { + throw new NetImportException(indexerResponse, "Indexer API call resulted in an unexpected StatusCode [{0}]", indexerResponse.HttpResponse.StatusCode); + } + + if (indexerResponse.HttpResponse.Headers.ContentType != null && indexerResponse.HttpResponse.Headers.ContentType.Contains("text/json") && + indexerResponse.HttpRequest.Headers.Accept != null && !indexerResponse.HttpRequest.Headers.Accept.Contains("text/json")) + { + throw new NetImportException(indexerResponse, "Indexer responded with html content. Site is likely blocked or unavailable."); + } + + return true; + } + + } +} diff --git a/src/NzbDrone.Core/NetImport/Trakt/TraktRequestGenerator.cs b/src/NzbDrone.Core/NetImport/Trakt/TraktRequestGenerator.cs new file mode 100644 index 000000000..152da8986 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/Trakt/TraktRequestGenerator.cs @@ -0,0 +1,35 @@ +using NzbDrone.Common.Http; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace NzbDrone.Core.NetImport.Trakt +{ + public class TraktRequestGenerator : INetImportRequestGenerator + { + public TraktSettings Settings { get; set; } + + public virtual NetImportPageableRequestChain GetMovies() + { + var pageableRequests = new NetImportPageableRequestChain(); + + pageableRequests.Add(GetMovies(null)); + + return pageableRequests; + } + + private IEnumerable GetMovies(string searchParameters) + { + // https://api.trakt.tv/users/timdturner/lists/custom1/items/movies + // trakt-api-version = 2 + // trakt-api-key = 657bb899dcb81ec8ee838ff09f6e013ff7c740bf0ccfa54dd41e791b9a70b2f0 + + var request = new NetImportRequest($"{Settings.Link.Trim()}{Settings.Username.Trim()}/lists/{Settings.Listname.Trim()}/items/movies", HttpAccept.Json); + request.HttpRequest.Headers.Add("trakt-api-version", "2"); + request.HttpRequest.Headers.Add("trakt-api-key", "657bb899dcb81ec8ee838ff09f6e013ff7c740bf0ccfa54dd41e791b9a70b2f0"); + + yield return request; + } + } +} diff --git a/src/NzbDrone.Core/NetImport/Trakt/TraktSettings.cs b/src/NzbDrone.Core/NetImport/Trakt/TraktSettings.cs new file mode 100644 index 000000000..2279cf373 --- /dev/null +++ b/src/NzbDrone.Core/NetImport/Trakt/TraktSettings.cs @@ -0,0 +1,30 @@ +using FluentValidation; +using NzbDrone.Core.Annotations; +using NzbDrone.Core.Profiles; +using NzbDrone.Core.ThingiProvider; +using NzbDrone.Core.Validation; + +namespace NzbDrone.Core.NetImport.Trakt +{ + + public class TraktSettings : NetImportBaseSettings + { + public TraktSettings() + { + Link = "https://api.trakt.tv/users/"; + Username = ""; + Listname = ""; + } + + [FieldDefinition(0, Label = "Trakt API URL", HelpText = "Link to to Trakt API URL, do not change unless you know what you are doing.")] + public new string Link { get; set; } + + [FieldDefinition(1, Label = "Trakt Username", HelpText = "Trakt Username the list belongs to.")] + public string Username { get; set; } + + [FieldDefinition(2, Label = "Trakt List Name", HelpText = "Trakt List Name")] + public string Listname { get; set; } + + } + +} diff --git a/src/NzbDrone.Core/NzbDrone.Core.csproj b/src/NzbDrone.Core/NzbDrone.Core.csproj index 4f2c6c742..1b6ef7f6f 100644 --- a/src/NzbDrone.Core/NzbDrone.Core.csproj +++ b/src/NzbDrone.Core/NzbDrone.Core.csproj @@ -123,6 +123,11 @@ + + + + + @@ -132,7 +137,7 @@ - +