From 5d7804478bd915979e1938855c899a8dcc109669 Mon Sep 17 00:00:00 2001 From: Qstick Date: Sat, 14 Dec 2019 21:09:41 -0500 Subject: [PATCH] New: Trakt Auth per List --- .../Config/NetImportConfigResource.cs | 14 +--- .../Configuration/ConfigService.cs | 39 --------- .../Configuration/IConfigService.cs | 6 -- .../NetImport/NetImportRepository.cs | 7 +- .../NetImport/Trakt/TraktImport.cs | 80 ++++++++++++++++++- .../NetImport/Trakt/TraktRequestGenerator.cs | 59 ++------------ .../NetImport/Trakt/TraktSettings.cs | 22 +++++ .../Config/NetImportConfigResource.cs | 8 +- 8 files changed, 117 insertions(+), 118 deletions(-) diff --git a/src/NzbDrone.Api/Config/NetImportConfigResource.cs b/src/NzbDrone.Api/Config/NetImportConfigResource.cs index c70b25ed2..6a5dc48da 100644 --- a/src/NzbDrone.Api/Config/NetImportConfigResource.cs +++ b/src/NzbDrone.Api/Config/NetImportConfigResource.cs @@ -6,11 +6,8 @@ namespace NzbDrone.Api.Config public class NetImportConfigResource : RestResource { public int NetImportSyncInterval { get; set; } - public string ListSyncLevel { get; set; } - public string ImportExclusions { get; set; } - public string TraktAuthToken { get; set; } - public string TraktRefreshToken { get; set; } - public int TraktTokenExpiry { get; set; } + public string ListSyncLevel { get; set; } + public string ImportExclusions { get; set; } } public static class NetImportConfigResourceMapper @@ -20,11 +17,8 @@ namespace NzbDrone.Api.Config return new NetImportConfigResource { NetImportSyncInterval = model.NetImportSyncInterval, - ListSyncLevel = model.ListSyncLevel, - ImportExclusions = model.ImportExclusions, - TraktAuthToken = model.TraktAuthToken, - TraktRefreshToken = model.TraktRefreshToken, - TraktTokenExpiry = model.TraktTokenExpiry, + ListSyncLevel = model.ListSyncLevel, + ImportExclusions = model.ImportExclusions, }; } } diff --git a/src/NzbDrone.Core/Configuration/ConfigService.cs b/src/NzbDrone.Core/Configuration/ConfigService.cs index 547a52113..5d4b9d2cc 100644 --- a/src/NzbDrone.Core/Configuration/ConfigService.cs +++ b/src/NzbDrone.Core/Configuration/ConfigService.cs @@ -119,45 +119,6 @@ namespace NzbDrone.Core.Configuration set { SetValue("NetImportSyncInterval", value); } } - public string TraktAuthToken - { - get { return GetValue("TraktAuthToken", string.Empty); } - - set { SetValue("TraktAuthToken", value); } - } - - public string TraktRefreshToken - { - get { return GetValue("TraktRefreshToken", string.Empty); } - - set { SetValue("TraktRefreshToken", value); } - } - - public int TraktTokenExpiry - { - get { return GetValueInt("TraktTokenExpiry", 0); } - - set { SetValue("TraktTokenExpiry", value); } - } - - public string NewTraktAuthToken - { - get { return GetValue("NewTraktAuthToken", string.Empty); } - set { SetValue("NewTraktAuthToken", value); } - } - - public string NewTraktRefreshToken - { - get { return GetValue("NewTraktRefreshToken", string.Empty); } - set { SetValue("NewTraktRefreshToken", value); } - } - - public int NewTraktTokenExpiry - { - get { return GetValueInt("NewTraktTokenExpiry", 0); } - set { SetValue("NewTraktTokenExpiry", value); } - } - public string ListSyncLevel { get { return GetValue("ListSyncLevel", "disabled"); } diff --git a/src/NzbDrone.Core/Configuration/IConfigService.cs b/src/NzbDrone.Core/Configuration/IConfigService.cs index c744e3458..48b498cb4 100644 --- a/src/NzbDrone.Core/Configuration/IConfigService.cs +++ b/src/NzbDrone.Core/Configuration/IConfigService.cs @@ -66,12 +66,6 @@ namespace NzbDrone.Core.Configuration int NetImportSyncInterval { get; set; } string ListSyncLevel { get; set; } string ImportExclusions { get; set; } - string TraktAuthToken { get; set; } - string TraktRefreshToken { get; set; } - int TraktTokenExpiry { get; set; } - string NewTraktAuthToken { get; set; } - string NewTraktRefreshToken {get; set; } - int NewTraktTokenExpiry { get; set; } //UI int FirstDayOfWeek { get; set; } diff --git a/src/NzbDrone.Core/NetImport/NetImportRepository.cs b/src/NzbDrone.Core/NetImport/NetImportRepository.cs index 8efa8a4a8..f3edd4377 100644 --- a/src/NzbDrone.Core/NetImport/NetImportRepository.cs +++ b/src/NzbDrone.Core/NetImport/NetImportRepository.cs @@ -7,7 +7,7 @@ namespace NzbDrone.Core.NetImport { public interface INetImportRepository : IProviderRepository { - + void UpdateSettings(NetImportDefinition model); } public class NetImportRepository : ProviderRepository, INetImportRepository @@ -16,5 +16,10 @@ namespace NzbDrone.Core.NetImport : base(database, eventAggregator) { } + + public void UpdateSettings(NetImportDefinition model) + { + SetFields(model, m => m.Settings); + } } } \ No newline at end of file diff --git a/src/NzbDrone.Core/NetImport/Trakt/TraktImport.cs b/src/NzbDrone.Core/NetImport/Trakt/TraktImport.cs index 754d8bea9..91ab39fc1 100644 --- a/src/NzbDrone.Core/NetImport/Trakt/TraktImport.cs +++ b/src/NzbDrone.Core/NetImport/Trakt/TraktImport.cs @@ -2,6 +2,9 @@ using NzbDrone.Common.Http; using NzbDrone.Core.Configuration; using NzbDrone.Core.Parser; +using NzbDrone.Core.Validation; +using System; +using System.Collections.Generic; namespace NzbDrone.Core.NetImport.Trakt { @@ -11,13 +14,62 @@ namespace NzbDrone.Core.NetImport.Trakt public override bool Enabled => true; public override bool EnableAuto => false; - public TraktImport(IHttpClient httpClient, IConfigService configService, IParsingService parsingService, Logger logger) + private INetImportRepository _netImportRepository; + + public TraktImport(INetImportRepository netImportRepository, + IHttpClient httpClient, + IConfigService configService, + IParsingService parsingService, + Logger logger) : base(httpClient, configService, parsingService, logger) { + _netImportRepository = netImportRepository; + } + + private void RefreshToken() + { + _logger.Trace("Refreshing Token"); + + Settings.Validate().Filter("RefreshToken").ThrowOnError(); + + var request = new HttpRequestBuilder(Settings.RenewUri) + .AddQueryParam("refresh", Settings.RefreshToken) + .Build(); + + try + { + var response = _httpClient.Get(request); + + if (response != null && response.Resource != null) + { + var token = response.Resource; + Settings.AccessToken = token.access_token; + Settings.Expires = DateTime.UtcNow.AddSeconds(token.expires_in); + Settings.RefreshToken = token.refresh_token != null ? token.refresh_token : Settings.RefreshToken; + + if (Definition.Id > 0) + { + _netImportRepository.UpdateSettings((NetImportDefinition)Definition); + } + } + } + catch (HttpException) + { + _logger.Warn($"Error refreshing trakt access token"); + } + } public override INetImportRequestGenerator GetRequestGenerator() { + Settings.Validate().Filter("AccessToken", "RefreshToken").ThrowOnError(); + _logger.Trace($"Access token expires at {Settings.Expires}"); + + if (Settings.Expires < DateTime.UtcNow.AddMinutes(5)) + { + RefreshToken(); + } + return new TraktRequestGenerator() { Settings = Settings, _configService=_configService, HttpClient = _httpClient, }; } @@ -25,5 +77,31 @@ namespace NzbDrone.Core.NetImport.Trakt { return new TraktParser(Settings); } + + public override object RequestAction(string action, IDictionary query) + { + if (action == "startOAuth") + { + var request = new HttpRequestBuilder(Settings.OAuthUrl) + .AddQueryParam("target", query["callbackUrl"]) + .Build(); + + return new + { + OauthUrl = request.Url.ToString() + }; + } + else if (action == "getOAuthToken") + { + return new + { + accessToken = query["access"], + expires = DateTime.UtcNow.AddSeconds(4838400), + refreshToken = query["refresh"], + }; + } + + return new { }; + } } } diff --git a/src/NzbDrone.Core/NetImport/Trakt/TraktRequestGenerator.cs b/src/NzbDrone.Core/NetImport/Trakt/TraktRequestGenerator.cs index e327822cb..df1015b23 100644 --- a/src/NzbDrone.Core/NetImport/Trakt/TraktRequestGenerator.cs +++ b/src/NzbDrone.Core/NetImport/Trakt/TraktRequestGenerator.cs @@ -24,11 +24,8 @@ namespace NzbDrone.Core.NetImport.Trakt public IHttpClient HttpClient { get; set; } public TraktSettings Settings { get; set; } - public string RadarrTraktUrl { get; set; } - public TraktRequestGenerator() { - RadarrTraktUrl = "http://radarr.aeonlucid.com/v1/trakt/refresh?refresh="; } public virtual NetImportPageableRequestChain GetMovies() { @@ -39,52 +36,6 @@ namespace NzbDrone.Core.NetImport.Trakt return pageableRequests; } - private void Authenticate() - { - if (_configService.TraktRefreshToken != string.Empty) - { - //tokens were overwritten with something other than nothing - if (_configService.NewTraktTokenExpiry > _configService.TraktTokenExpiry) - { - //but our refreshedTokens are more current - _configService.TraktAuthToken = _configService.NewTraktAuthToken; - _configService.TraktRefreshToken = _configService.NewTraktRefreshToken; - _configService.TraktTokenExpiry = _configService.NewTraktTokenExpiry; - } - - var unixTime = (int)DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1)).TotalSeconds; - - if (unixTime > _configService.TraktTokenExpiry) - { - var requestBuilder = new HttpRequestBuilder($"{RadarrTraktUrl + _configService.TraktRefreshToken}") - { - LogResponseContent = true - }; - - requestBuilder.Method = HttpMethod.GET; - - var authLoginRequest = requestBuilder - .SetHeader("Content-Type", "application/json") - .Accept(HttpAccept.Json) - .Build(); - - var response = HttpClient.Execute(authLoginRequest); - var result = Json.Deserialize(response.Content); - - _configService.TraktAuthToken = result.access_token; - _configService.TraktRefreshToken = result.refresh_token; - - //lets have it expire in 8 weeks (4838400 seconds) - _configService.TraktTokenExpiry = unixTime + 4838400; - - //store the refreshed tokens in case they get overwritten by an old set of tokens - _configService.NewTraktAuthToken = _configService.TraktAuthToken; - _configService.NewTraktRefreshToken = _configService.TraktRefreshToken; - _configService.NewTraktTokenExpiry = _configService.TraktTokenExpiry; - } - } - } - private IEnumerable GetMovies(string searchParameters) { var link = Settings.Link.Trim(); @@ -129,14 +80,14 @@ namespace NzbDrone.Core.NetImport.Trakt break; } - Authenticate(); - var request = new NetImportRequest($"{link}", HttpAccept.Json); + request.HttpRequest.Headers.Add("trakt-api-version", "2"); - request.HttpRequest.Headers.Add("trakt-api-key", "964f67b126ade0112c4ae1f0aea3a8fb03190f71117bd83af6a0560a99bc52e6"); //aeon - if (_configService.TraktAuthToken.IsNotNullOrWhiteSpace()) + request.HttpRequest.Headers.Add("trakt-api-key", Settings.ClientId); //aeon + + if (Settings.AccessToken.IsNotNullOrWhiteSpace()) { - request.HttpRequest.Headers.Add("Authorization", "Bearer " + _configService.TraktAuthToken); + request.HttpRequest.Headers.Add("Authorization", "Bearer " + Settings.AccessToken); } yield return request; diff --git a/src/NzbDrone.Core/NetImport/Trakt/TraktSettings.cs b/src/NzbDrone.Core/NetImport/Trakt/TraktSettings.cs index bb6ac84a0..b4fa9074d 100644 --- a/src/NzbDrone.Core/NetImport/Trakt/TraktSettings.cs +++ b/src/NzbDrone.Core/NetImport/Trakt/TraktSettings.cs @@ -3,6 +3,7 @@ using NzbDrone.Common.Extensions; using NzbDrone.Core.Annotations; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; +using System; using System.Text.RegularExpressions; namespace NzbDrone.Core.NetImport.Trakt @@ -13,6 +14,9 @@ namespace NzbDrone.Core.NetImport.Trakt public TraktSettingsValidator() { RuleFor(c => c.Link).ValidRootUrl(); + RuleFor(c => c.AccessToken).NotEmpty(); + RuleFor(c => c.RefreshToken).NotEmpty(); + RuleFor(c => c.Expires).NotEmpty(); // List name required for UserCustomList RuleFor(c => c.Listname) @@ -59,6 +63,7 @@ namespace NzbDrone.Core.NetImport.Trakt public TraktSettings() { Link = "https://api.trakt.tv"; + SignIn = "startOAuth"; ListType = (int)TraktListType.Popular; Username = ""; Listname = ""; @@ -69,6 +74,20 @@ namespace NzbDrone.Core.NetImport.Trakt Limit = 100; } + public string OAuthUrl => "http://radarr.aeonlucid.com/v1/trakt/redirect"; + public string RenewUri => "http://radarr.aeonlucid.com/v1/trakt/refresh"; + public string ClientId => "964f67b126ade0112c4ae1f0aea3a8fb03190f71117bd83af6a0560a99bc52e6"; + public virtual string Scope => ""; + + [FieldDefinition(0, Label = "Access Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] + public string AccessToken { get; set; } + + [FieldDefinition(0, Label = "Refresh Token", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] + public string RefreshToken { get; set; } + + [FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)] + public DateTime Expires { get; set; } + [FieldDefinition(0, Label = "Trakt API URL", HelpText = "Link to to Trakt API URL, do not change unless you know what you are doing.")] public string Link { get; set; } @@ -99,6 +118,9 @@ namespace NzbDrone.Core.NetImport.Trakt [FieldDefinition(9, Label = "Additional Parameters", HelpText = "Additional Trakt API parameters", Advanced = true)] public string TraktAdditionalParameters { get; set; } + [FieldDefinition(99, Label = "Authenticate with Trakt", Type = FieldType.OAuth)] + public string SignIn { get; set; } + public NzbDroneValidationResult Validate() { diff --git a/src/Radarr.Api.V3/Config/NetImportConfigResource.cs b/src/Radarr.Api.V3/Config/NetImportConfigResource.cs index 3b78755ab..43e6b29e4 100644 --- a/src/Radarr.Api.V3/Config/NetImportConfigResource.cs +++ b/src/Radarr.Api.V3/Config/NetImportConfigResource.cs @@ -8,9 +8,6 @@ namespace Radarr.Api.V3.Config public int NetImportSyncInterval { get; set; } public string ListSyncLevel { get; set; } public string ImportExclusions { get; set; } - public string TraktAuthToken { get; set; } - public string TraktRefreshToken { get; set; } - public int TraktTokenExpiry { get; set; } } public static class NetImportConfigResourceMapper @@ -21,10 +18,7 @@ namespace Radarr.Api.V3.Config { NetImportSyncInterval = model.NetImportSyncInterval, ListSyncLevel = model.ListSyncLevel, - ImportExclusions = model.ImportExclusions, - TraktAuthToken = model.TraktAuthToken, - TraktRefreshToken = model.TraktRefreshToken, - TraktTokenExpiry = model.TraktTokenExpiry, + ImportExclusions = model.ImportExclusions }; } }