mirror of
https://github.com/Radarr/Radarr.git
synced 2026-04-24 22:35:49 -04:00
New: Trakt List Organization, User Collection List Support
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using NLog;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
@@ -9,7 +9,7 @@ namespace NzbDrone.Core.NetImport.CouchPotato
|
||||
{
|
||||
public override string Name => "CouchPotato";
|
||||
|
||||
public override NetImportType ListType => NetImportType.Other;
|
||||
public override NetImportType ListType => NetImportType.Program;
|
||||
public override bool Enabled => true;
|
||||
public override bool EnableAuto => false;
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
namespace NzbDrone.Core.NetImport
|
||||
namespace NzbDrone.Core.NetImport
|
||||
{
|
||||
public enum NetImportType
|
||||
{
|
||||
Program,
|
||||
TMDB,
|
||||
Trakt,
|
||||
Other
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using NLog;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
@@ -11,7 +11,7 @@ namespace NzbDrone.Core.NetImport.Radarr
|
||||
public override bool Enabled => true;
|
||||
public override bool EnableAuto => false;
|
||||
|
||||
public override NetImportType ListType => NetImportType.Other;
|
||||
public override NetImportType ListType => NetImportType.Program;
|
||||
|
||||
public RadarrImport(IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace NzbDrone.Core.NetImport.TMDb.Popular
|
||||
public TMDbPopularSettingsValidator()
|
||||
: base()
|
||||
{
|
||||
RuleFor(c => c.TMDbListType).NotEmpty();
|
||||
RuleFor(c => c.TMDbListType).NotNull();
|
||||
|
||||
RuleFor(c => c.FilterCriteria).SetValidator(_ => new TMDbFilterSettingsValidator());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.List
|
||||
{
|
||||
public class TraktListImport : TraktImportBase<TraktListSettings>
|
||||
{
|
||||
public TraktListImport(INetImportRepository netImportRepository,
|
||||
IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
Logger logger)
|
||||
: base(netImportRepository, httpClient, configService, parsingService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Trakt List";
|
||||
public override bool Enabled => true;
|
||||
public override bool EnableAuto => false;
|
||||
|
||||
public override INetImportRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new TraktListRequestGenerator()
|
||||
{
|
||||
Settings = Settings
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.List
|
||||
{
|
||||
public class TraktListRequestGenerator : INetImportRequestGenerator
|
||||
{
|
||||
public TraktListSettings Settings { get; set; }
|
||||
|
||||
public TraktListRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual NetImportPageableRequestChain GetMovies()
|
||||
{
|
||||
var pageableRequests = new NetImportPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetMoviesRequest());
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<NetImportRequest> GetMoviesRequest()
|
||||
{
|
||||
var link = Settings.Link.Trim();
|
||||
|
||||
var listName = Parser.Parser.ToUrlSlug(Settings.Listname.Trim());
|
||||
link += $"/users/{Settings.Username.Trim()}/lists/{listName}/items/movies?limit={Settings.Limit}";
|
||||
|
||||
var request = new NetImportRequest($"{link}", HttpAccept.Json);
|
||||
|
||||
request.HttpRequest.Headers.Add("trakt-api-version", "2");
|
||||
request.HttpRequest.Headers.Add("trakt-api-key", Settings.ClientId); //aeon
|
||||
|
||||
if (Settings.AccessToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.HttpRequest.Headers.Add("Authorization", "Bearer " + Settings.AccessToken);
|
||||
}
|
||||
|
||||
yield return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.List
|
||||
{
|
||||
public class TraktListSettingsValidator : TraktSettingsBaseValidator<TraktListSettings>
|
||||
{
|
||||
public TraktListSettingsValidator()
|
||||
: base()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public class TraktListSettings : TraktSettingsBase<TraktListSettings>
|
||||
{
|
||||
protected override AbstractValidator<TraktListSettings> Validator => new TraktListSettingsValidator();
|
||||
|
||||
public TraktListSettings()
|
||||
{
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "Username", HelpText = "Username for the List to import from")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "List Name", HelpText = "List name for import, list must be public or you must have access to the list")]
|
||||
public string Listname { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.Popular
|
||||
{
|
||||
public class TraktPopularImport : TraktImportBase<TraktPopularSettings>
|
||||
{
|
||||
public TraktPopularImport(INetImportRepository netImportRepository,
|
||||
IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
Logger logger)
|
||||
: base(netImportRepository, httpClient, configService, parsingService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Trakt Popular List";
|
||||
public override bool Enabled => true;
|
||||
public override bool EnableAuto => false;
|
||||
|
||||
public override IParseNetImportResponse GetParser()
|
||||
{
|
||||
return new TraktPopularParser(Settings);
|
||||
}
|
||||
|
||||
public override INetImportRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new TraktPopularRequestGenerator()
|
||||
{
|
||||
Settings = Settings
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.Popular
|
||||
{
|
||||
public enum TraktPopularListType
|
||||
{
|
||||
[EnumMember(Value = "Trending Movies")]
|
||||
Trending = 0,
|
||||
[EnumMember(Value = "Popular Movies")]
|
||||
Popular = 1,
|
||||
[EnumMember(Value = "Top Anticipated Movies")]
|
||||
Anticipated = 2,
|
||||
[EnumMember(Value = "Top Box Office Movies")]
|
||||
BoxOffice = 3,
|
||||
|
||||
[EnumMember(Value = "Top Watched Movies By Week")]
|
||||
TopWatchedByWeek = 4,
|
||||
[EnumMember(Value = "Top Watched Movies By Month")]
|
||||
TopWatchedByMonth = 5,
|
||||
[EnumMember(Value = "Top Watched Movies By Year")]
|
||||
TopWatchedByYear = 6,
|
||||
[EnumMember(Value = "Top Watched Movies Of All Time")]
|
||||
TopWatchedByAllTime = 7
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Movies;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.Popular
|
||||
{
|
||||
public class TraktPopularParser : TraktParser
|
||||
{
|
||||
private readonly TraktPopularSettings _settings;
|
||||
private NetImportResponse _importResponse;
|
||||
|
||||
public TraktPopularParser(TraktPopularSettings settings)
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public override IList<Movie> ParseResponse(NetImportResponse importResponse)
|
||||
{
|
||||
_importResponse = importResponse;
|
||||
|
||||
var movies = new List<Movie>();
|
||||
|
||||
if (!PreProcess(_importResponse))
|
||||
{
|
||||
return movies;
|
||||
}
|
||||
|
||||
var jsonResponse = new List<TraktMovieResource>();
|
||||
|
||||
if (_settings.TraktListType == (int)TraktPopularListType.Popular)
|
||||
{
|
||||
jsonResponse = JsonConvert.DeserializeObject<List<TraktMovieResource>>(_importResponse.Content);
|
||||
}
|
||||
else
|
||||
{
|
||||
jsonResponse = JsonConvert.DeserializeObject<List<TraktResponse>>(_importResponse.Content).SelectList(c => c.Movie);
|
||||
}
|
||||
|
||||
// no movies were return
|
||||
if (jsonResponse == null)
|
||||
{
|
||||
return movies;
|
||||
}
|
||||
|
||||
foreach (var movie in jsonResponse)
|
||||
{
|
||||
movies.AddIfNotNull(new Movies.Movie()
|
||||
{
|
||||
Title = movie.Title,
|
||||
ImdbId = movie.Ids.Imdb,
|
||||
TmdbId = movie.Ids.Tmdb,
|
||||
Year = movie.Year ?? 0
|
||||
});
|
||||
}
|
||||
|
||||
return movies;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.Popular
|
||||
{
|
||||
public class TraktPopularRequestGenerator : INetImportRequestGenerator
|
||||
{
|
||||
public TraktPopularSettings Settings { get; set; }
|
||||
|
||||
public TraktPopularRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual NetImportPageableRequestChain GetMovies()
|
||||
{
|
||||
var pageableRequests = new NetImportPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetMoviesRequest());
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<NetImportRequest> GetMoviesRequest()
|
||||
{
|
||||
var link = Settings.Link.Trim();
|
||||
|
||||
var filtersAndLimit = $"?years={Settings.Years}&genres={Settings.Genres.ToLower()}&ratings={Settings.Rating}&certifications={Settings.Certification.ToLower()}&limit={Settings.Limit}{Settings.TraktAdditionalParameters}";
|
||||
|
||||
switch (Settings.TraktListType)
|
||||
{
|
||||
case (int)TraktPopularListType.Trending:
|
||||
link += "/movies/trending" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktPopularListType.Popular:
|
||||
link += "/movies/popular" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktPopularListType.Anticipated:
|
||||
link += "/movies/anticipated" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktPopularListType.BoxOffice:
|
||||
link += "/movies/boxoffice" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktPopularListType.TopWatchedByWeek:
|
||||
link += "/movies/watched/weekly" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktPopularListType.TopWatchedByMonth:
|
||||
link += "/movies/watched/monthly" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktPopularListType.TopWatchedByYear:
|
||||
link += "/movies/watched/yearly" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktPopularListType.TopWatchedByAllTime:
|
||||
link += "/movies/watched/all" + filtersAndLimit;
|
||||
break;
|
||||
}
|
||||
|
||||
var request = new NetImportRequest($"{link}", HttpAccept.Json);
|
||||
|
||||
request.HttpRequest.Headers.Add("trakt-api-version", "2");
|
||||
request.HttpRequest.Headers.Add("trakt-api-key", Settings.ClientId); //aeon
|
||||
|
||||
if (Settings.AccessToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.HttpRequest.Headers.Add("Authorization", "Bearer " + Settings.AccessToken);
|
||||
}
|
||||
|
||||
yield return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.Popular
|
||||
{
|
||||
public class TraktPopularSettingsValidator : TraktSettingsBaseValidator<TraktPopularSettings>
|
||||
{
|
||||
public TraktPopularSettingsValidator()
|
||||
: base()
|
||||
{
|
||||
RuleFor(c => c.TraktListType).NotNull();
|
||||
}
|
||||
}
|
||||
|
||||
public class TraktPopularSettings : TraktSettingsBase<TraktPopularSettings>
|
||||
{
|
||||
protected override AbstractValidator<TraktPopularSettings> Validator => new TraktPopularSettingsValidator();
|
||||
|
||||
public TraktPopularSettings()
|
||||
{
|
||||
TraktListType = (int)TraktPopularListType.Popular;
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktPopularListType), HelpText = "Type of list your seeking to import from")]
|
||||
public int TraktListType { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,34 +1,53 @@
|
||||
namespace NzbDrone.Core.NetImport.Trakt
|
||||
namespace NzbDrone.Core.NetImport.Trakt
|
||||
{
|
||||
public class Ids
|
||||
public class TraktMovieIdsResource
|
||||
{
|
||||
public int trakt { get; set; }
|
||||
public string slug { get; set; }
|
||||
public string imdb { get; set; }
|
||||
public int tmdb { get; set; }
|
||||
public int Trakt { get; set; }
|
||||
public string Slug { get; set; }
|
||||
public string Imdb { get; set; }
|
||||
public int Tmdb { get; set; }
|
||||
}
|
||||
|
||||
public class Movie
|
||||
public class TraktMovieResource
|
||||
{
|
||||
public string title { get; set; }
|
||||
public int? year { get; set; }
|
||||
public Ids ids { get; set; }
|
||||
public string Title { get; set; }
|
||||
public int? Year { get; set; }
|
||||
public TraktMovieIdsResource Ids { get; set; }
|
||||
}
|
||||
|
||||
public class TraktResponse
|
||||
{
|
||||
public int? rank { get; set; }
|
||||
public string listed_at { get; set; }
|
||||
public string type { get; set; }
|
||||
public int? Rank { get; set; }
|
||||
public string Listed_at { get; set; }
|
||||
public string Type { get; set; }
|
||||
|
||||
public int? watchers { get; set; }
|
||||
public int? Watchers { get; set; }
|
||||
|
||||
public long? revenue { get; set; }
|
||||
public long? Revenue { get; set; }
|
||||
|
||||
public long? watcher_count { get; set; }
|
||||
public long? play_count { get; set; }
|
||||
public long? collected_count { get; set; }
|
||||
public long? Watcher_count { get; set; }
|
||||
public long? Play_count { get; set; }
|
||||
public long? Collected_count { get; set; }
|
||||
|
||||
public Movie movie { get; set; }
|
||||
public TraktMovieResource Movie { get; set; }
|
||||
}
|
||||
|
||||
public class RefreshRequestResponse
|
||||
{
|
||||
public string Access_token { get; set; }
|
||||
public string Token_type { get; set; }
|
||||
public int Expires_in { get; set; }
|
||||
public string Refresh_token { get; set; }
|
||||
public string Scope { get; set; }
|
||||
}
|
||||
|
||||
public class UserSettingsResponse
|
||||
{
|
||||
public TraktUserResource User { get; set; }
|
||||
}
|
||||
|
||||
public class TraktUserResource
|
||||
{
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
+74
-44
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NLog;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
@@ -8,17 +9,14 @@ using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt
|
||||
{
|
||||
public class TraktImport : HttpNetImportBase<TraktSettings>
|
||||
public abstract class TraktImportBase<TSettings> : HttpNetImportBase<TSettings>
|
||||
where TSettings : TraktSettingsBase<TSettings>, new()
|
||||
{
|
||||
public override string Name => "Trakt List";
|
||||
|
||||
public override NetImportType ListType => NetImportType.Other;
|
||||
public override bool Enabled => true;
|
||||
public override bool EnableAuto => false;
|
||||
public override NetImportType ListType => NetImportType.Trakt;
|
||||
|
||||
private INetImportRepository _netImportRepository;
|
||||
|
||||
public TraktImport(INetImportRepository netImportRepository,
|
||||
protected TraktImportBase(INetImportRepository netImportRepository,
|
||||
IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
@@ -28,40 +26,7 @@ namespace NzbDrone.Core.NetImport.Trakt
|
||||
_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<RefreshRequestResponse>(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()
|
||||
public override NetImportFetchResult Fetch()
|
||||
{
|
||||
Settings.Validate().Filter("AccessToken", "RefreshToken").ThrowOnError();
|
||||
_logger.Trace($"Access token expires at {Settings.Expires}");
|
||||
@@ -71,12 +36,13 @@ namespace NzbDrone.Core.NetImport.Trakt
|
||||
RefreshToken();
|
||||
}
|
||||
|
||||
return new TraktRequestGenerator() { Settings = Settings, _configService = _configService, HttpClient = _httpClient, };
|
||||
var generator = GetRequestGenerator();
|
||||
return FetchMovies(generator.GetMovies());
|
||||
}
|
||||
|
||||
public override IParseNetImportResponse GetParser()
|
||||
{
|
||||
return new TraktParser(Settings);
|
||||
return new TraktParser();
|
||||
}
|
||||
|
||||
public override object RequestAction(string action, IDictionary<string, string> query)
|
||||
@@ -99,10 +65,74 @@ namespace NzbDrone.Core.NetImport.Trakt
|
||||
accessToken = query["access"],
|
||||
expires = DateTime.UtcNow.AddSeconds(4838400),
|
||||
refreshToken = query["refresh"],
|
||||
authUser = GetUserName(query["access"])
|
||||
};
|
||||
}
|
||||
|
||||
return new { };
|
||||
}
|
||||
|
||||
private string GetUserName(string accessToken)
|
||||
{
|
||||
var request = new HttpRequestBuilder(string.Format("{0}/users/settings", Settings.Link))
|
||||
.Build();
|
||||
|
||||
request.Headers.Add("trakt-api-version", "2");
|
||||
request.Headers.Add("trakt-api-key", Settings.ClientId); //aeon
|
||||
|
||||
if (accessToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.Headers.Add("Authorization", "Bearer " + accessToken);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var response = _httpClient.Get<UserSettingsResponse>(request);
|
||||
|
||||
if (response != null && response.Resource != null)
|
||||
{
|
||||
return response.Resource.User.Username;
|
||||
}
|
||||
}
|
||||
catch (HttpException)
|
||||
{
|
||||
_logger.Warn($"Error refreshing trakt access token");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
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<RefreshRequestResponse>(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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt
|
||||
{
|
||||
public enum TraktListType
|
||||
{
|
||||
[EnumMember(Value = "User Watch List")]
|
||||
UserWatchList = 0,
|
||||
[EnumMember(Value = "User Watched List")]
|
||||
UserWatchedList = 1,
|
||||
[EnumMember(Value = "User Custom List")]
|
||||
UserCustomList = 2,
|
||||
|
||||
[EnumMember(Value = "Trending Movies")]
|
||||
Trending = 3,
|
||||
[EnumMember(Value = "Popular Movies")]
|
||||
Popular = 4,
|
||||
[EnumMember(Value = "Top Anticipated Movies")]
|
||||
Anticipated = 5,
|
||||
[EnumMember(Value = "Top Box Office Movies")]
|
||||
BoxOffice = 6,
|
||||
|
||||
[EnumMember(Value = "Top Watched Movies By Week")]
|
||||
TopWatchedByWeek = 7,
|
||||
[EnumMember(Value = "Top Watched Movies By Month")]
|
||||
TopWatchedByMonth = 8,
|
||||
[EnumMember(Value = "Top Watched Movies By Year")]
|
||||
TopWatchedByYear = 9,
|
||||
[EnumMember(Value = "Top Watched Movies Of All Time")]
|
||||
TopWatchedByAllTime = 10
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Newtonsoft.Json;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -8,15 +8,13 @@ namespace NzbDrone.Core.NetImport.Trakt
|
||||
{
|
||||
public class TraktParser : IParseNetImportResponse
|
||||
{
|
||||
private readonly TraktSettings _settings;
|
||||
private NetImportResponse _importResponse;
|
||||
|
||||
public TraktParser(TraktSettings settings)
|
||||
public TraktParser()
|
||||
{
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
public IList<Movies.Movie> ParseResponse(NetImportResponse importResponse)
|
||||
public virtual IList<Movies.Movie> ParseResponse(NetImportResponse importResponse)
|
||||
{
|
||||
_importResponse = importResponse;
|
||||
|
||||
@@ -27,41 +25,23 @@ namespace NzbDrone.Core.NetImport.Trakt
|
||||
return movies;
|
||||
}
|
||||
|
||||
if (_settings.TraktListType == (int)TraktListType.Popular)
|
||||
{
|
||||
var jsonResponse = JsonConvert.DeserializeObject<List<Movie>>(_importResponse.Content);
|
||||
var jsonResponse = JsonConvert.DeserializeObject<List<TraktResponse>>(_importResponse.Content);
|
||||
|
||||
foreach (var movie in jsonResponse)
|
||||
{
|
||||
movies.AddIfNotNull(new Movies.Movie()
|
||||
{
|
||||
Title = movie.title,
|
||||
ImdbId = movie.ids.imdb,
|
||||
TmdbId = movie.ids.tmdb,
|
||||
Year = movie.year ?? 0
|
||||
});
|
||||
}
|
||||
// no movies were return
|
||||
if (jsonResponse == null)
|
||||
{
|
||||
return movies;
|
||||
}
|
||||
else
|
||||
|
||||
foreach (var movie in jsonResponse)
|
||||
{
|
||||
var jsonResponse = JsonConvert.DeserializeObject<List<TraktResponse>>(_importResponse.Content);
|
||||
|
||||
// no movies were return
|
||||
if (jsonResponse == null)
|
||||
movies.AddIfNotNull(new Movies.Movie()
|
||||
{
|
||||
return movies;
|
||||
}
|
||||
|
||||
foreach (var movie in jsonResponse)
|
||||
{
|
||||
movies.AddIfNotNull(new Movies.Movie()
|
||||
{
|
||||
Title = movie.movie.title,
|
||||
ImdbId = movie.movie.ids.imdb,
|
||||
TmdbId = movie.movie.ids.tmdb,
|
||||
Year = movie.movie.year ?? 0
|
||||
});
|
||||
}
|
||||
Title = movie.Movie.Title,
|
||||
ImdbId = movie.Movie.Ids.Imdb,
|
||||
TmdbId = movie.Movie.Ids.Tmdb,
|
||||
Year = movie.Movie.Year ?? 0
|
||||
});
|
||||
}
|
||||
|
||||
return movies;
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt
|
||||
{
|
||||
public class RefreshRequestResponse
|
||||
{
|
||||
public string access_token { get; set; }
|
||||
public string token_type { get; set; }
|
||||
public int expires_in { get; set; }
|
||||
public string refresh_token { get; set; }
|
||||
public string scope { get; set; }
|
||||
}
|
||||
|
||||
public class TraktRequestGenerator : INetImportRequestGenerator
|
||||
{
|
||||
public IConfigService _configService;
|
||||
public IHttpClient HttpClient { get; set; }
|
||||
public TraktSettings Settings { get; set; }
|
||||
|
||||
public TraktRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual NetImportPageableRequestChain GetMovies()
|
||||
{
|
||||
var pageableRequests = new NetImportPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetMovies(null));
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<NetImportRequest> GetMovies(string searchParameters)
|
||||
{
|
||||
var link = Settings.Link.Trim();
|
||||
|
||||
var filtersAndLimit = $"?years={Settings.Years}&genres={Settings.Genres.ToLower()}&ratings={Settings.Rating}&certifications={Settings.Certification.ToLower()}&limit={Settings.Limit}{Settings.TraktAdditionalParameters}";
|
||||
|
||||
switch (Settings.TraktListType)
|
||||
{
|
||||
case (int)TraktListType.UserCustomList:
|
||||
var listName = Parser.Parser.ToUrlSlug(Settings.Listname.Trim());
|
||||
link = link + $"/users/{Settings.Username.Trim()}/lists/{listName}/items/movies?limit={Settings.Limit}";
|
||||
break;
|
||||
case (int)TraktListType.UserWatchList:
|
||||
link = link + $"/users/{Settings.Username.Trim()}/watchlist/movies?limit={Settings.Limit}";
|
||||
break;
|
||||
case (int)TraktListType.UserWatchedList:
|
||||
link = link + $"/users/{Settings.Username.Trim()}/watched/movies?limit={Settings.Limit}";
|
||||
break;
|
||||
case (int)TraktListType.Trending:
|
||||
link = link + "/movies/trending" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktListType.Popular:
|
||||
link = link + "/movies/popular" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktListType.Anticipated:
|
||||
link = link + "/movies/anticipated" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktListType.BoxOffice:
|
||||
link = link + "/movies/boxoffice" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktListType.TopWatchedByWeek:
|
||||
link = link + "/movies/watched/weekly" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktListType.TopWatchedByMonth:
|
||||
link = link + "/movies/watched/monthly" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktListType.TopWatchedByYear:
|
||||
link = link + "/movies/watched/yearly" + filtersAndLimit;
|
||||
break;
|
||||
case (int)TraktListType.TopWatchedByAllTime:
|
||||
link = link + "/movies/watched/all" + filtersAndLimit;
|
||||
break;
|
||||
}
|
||||
|
||||
var request = new NetImportRequest($"{link}", HttpAccept.Json);
|
||||
|
||||
request.HttpRequest.Headers.Add("trakt-api-version", "2");
|
||||
request.HttpRequest.Headers.Add("trakt-api-key", Settings.ClientId); //aeon
|
||||
|
||||
if (Settings.AccessToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.HttpRequest.Headers.Add("Authorization", "Bearer " + Settings.AccessToken);
|
||||
}
|
||||
|
||||
yield return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
-37
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
@@ -8,27 +8,16 @@ using NzbDrone.Core.Validation;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt
|
||||
{
|
||||
public class TraktSettingsValidator : AbstractValidator<TraktSettings>
|
||||
public class TraktSettingsBaseValidator<TSettings> : AbstractValidator<TSettings>
|
||||
where TSettings : TraktSettingsBase<TSettings>
|
||||
{
|
||||
public TraktSettingsValidator()
|
||||
public TraktSettingsBaseValidator()
|
||||
{
|
||||
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)
|
||||
.Matches(@"^[A-Za-z0-9\-_]+$", RegexOptions.IgnoreCase)
|
||||
.When(c => c.TraktListType == (int)TraktListType.UserCustomList)
|
||||
.WithMessage("List name is required when using Custom Trakt Lists");
|
||||
|
||||
// Username required for UserWatchedList/UserWatchList
|
||||
RuleFor(c => c.Username)
|
||||
.Matches(@"^[A-Za-z0-9\-_]+$", RegexOptions.IgnoreCase)
|
||||
.When(c => c.TraktListType == (int)TraktListType.UserWatchedList || c.TraktListType == (int)TraktListType.UserWatchList)
|
||||
.WithMessage("Username is required when using User Trakt Lists");
|
||||
|
||||
// Loose validation @TODO
|
||||
RuleFor(c => c.Rating)
|
||||
.Matches(@"^\d+\-\d+$", RegexOptions.IgnoreCase)
|
||||
@@ -56,17 +45,15 @@ namespace NzbDrone.Core.NetImport.Trakt
|
||||
}
|
||||
}
|
||||
|
||||
public class TraktSettings : IProviderConfig
|
||||
public class TraktSettingsBase<TSettings> : IProviderConfig
|
||||
where TSettings : TraktSettingsBase<TSettings>
|
||||
{
|
||||
private static readonly TraktSettingsValidator Validator = new TraktSettingsValidator();
|
||||
protected virtual AbstractValidator<TSettings> Validator => new TraktSettingsBaseValidator<TSettings>();
|
||||
|
||||
public TraktSettings()
|
||||
public TraktSettingsBase()
|
||||
{
|
||||
Link = "https://api.trakt.tv";
|
||||
SignIn = "startOAuth";
|
||||
TraktListType = (int)Trakt.TraktListType.Popular;
|
||||
Username = "";
|
||||
Listname = "";
|
||||
Rating = "0-100";
|
||||
Certification = "NR,G,PG,PG-13,R,NC-17";
|
||||
Genres = "";
|
||||
@@ -88,34 +75,28 @@ namespace NzbDrone.Core.NetImport.Trakt
|
||||
[FieldDefinition(0, Label = "Expires", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public DateTime Expires { get; set; }
|
||||
|
||||
[FieldDefinition(0, Label = "Auth User", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AuthUser { 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; }
|
||||
|
||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktListType), HelpText = "Trakt list type")]
|
||||
public int TraktListType { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "Username", HelpText = "Required for User List (Ignores Filtering Options)")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "List Name", HelpText = "Required for Custom List (Ignores Filtering Options)")]
|
||||
public string Listname { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "Rating", HelpText = "Filter movies by rating range (0-100)")]
|
||||
[FieldDefinition(1, Label = "Rating", HelpText = "Filter movies by rating range (0-100)")]
|
||||
public string Rating { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "Certification", HelpText = "Filter movies by a certification (NR,G,PG,PG-13,R,NC-17), (Comma Separated)")]
|
||||
[FieldDefinition(2, Label = "Certification", HelpText = "Filter movies by a certification (NR,G,PG,PG-13,R,NC-17), (Comma Separated)")]
|
||||
public string Certification { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "Genres", HelpText = "Filter movies by Trakt Genre Slug (Comma Separated)")]
|
||||
[FieldDefinition(3, Label = "Genres", HelpText = "Filter movies by Trakt Genre Slug (Comma Separated)")]
|
||||
public string Genres { get; set; }
|
||||
|
||||
[FieldDefinition(7, Label = "Years", HelpText = "Filter movies by year or year range")]
|
||||
[FieldDefinition(4, Label = "Years", HelpText = "Filter movies by year or year range")]
|
||||
public string Years { get; set; }
|
||||
|
||||
[FieldDefinition(8, Label = "Limit", HelpText = "Limit the number of movies to get")]
|
||||
[FieldDefinition(5, Label = "Limit", HelpText = "Limit the number of movies to get")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
[FieldDefinition(9, Label = "Additional Parameters", HelpText = "Additional Trakt API parameters", Advanced = true)]
|
||||
[FieldDefinition(6, Label = "Additional Parameters", HelpText = "Additional Trakt API parameters", Advanced = true)]
|
||||
public string TraktAdditionalParameters { get; set; }
|
||||
|
||||
[FieldDefinition(99, Label = "Authenticate with Trakt", Type = FieldType.OAuth)]
|
||||
@@ -123,7 +104,7 @@ namespace NzbDrone.Core.NetImport.Trakt
|
||||
|
||||
public NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
return new NzbDroneValidationResult(Validator.Validate((TSettings)this));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using NLog;
|
||||
using NzbDrone.Common.Http;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Parser;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.User
|
||||
{
|
||||
public class TraktUserImport : TraktImportBase<TraktUserSettings>
|
||||
{
|
||||
public TraktUserImport(INetImportRepository netImportRepository,
|
||||
IHttpClient httpClient,
|
||||
IConfigService configService,
|
||||
IParsingService parsingService,
|
||||
Logger logger)
|
||||
: base(netImportRepository, httpClient, configService, parsingService, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override string Name => "Trakt User";
|
||||
public override bool Enabled => true;
|
||||
public override bool EnableAuto => false;
|
||||
|
||||
public override INetImportRequestGenerator GetRequestGenerator()
|
||||
{
|
||||
return new TraktUserRequestGenerator()
|
||||
{
|
||||
Settings = Settings
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.User
|
||||
{
|
||||
public enum TraktUserListType
|
||||
{
|
||||
[EnumMember(Value = "User Watch List")]
|
||||
UserWatchList = 0,
|
||||
[EnumMember(Value = "User Watched List")]
|
||||
UserWatchedList = 1,
|
||||
[EnumMember(Value = "User Collection List")]
|
||||
UserCollectionList = 2
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Collections.Generic;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Common.Http;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.User
|
||||
{
|
||||
public class TraktUserRequestGenerator : INetImportRequestGenerator
|
||||
{
|
||||
public TraktUserSettings Settings { get; set; }
|
||||
|
||||
public TraktUserRequestGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
public virtual NetImportPageableRequestChain GetMovies()
|
||||
{
|
||||
var pageableRequests = new NetImportPageableRequestChain();
|
||||
|
||||
pageableRequests.Add(GetMoviesRequest());
|
||||
|
||||
return pageableRequests;
|
||||
}
|
||||
|
||||
private IEnumerable<NetImportRequest> GetMoviesRequest()
|
||||
{
|
||||
var link = Settings.Link.Trim();
|
||||
|
||||
switch (Settings.TraktListType)
|
||||
{
|
||||
case (int)TraktUserListType.UserWatchList:
|
||||
link += $"/users/{Settings.AuthUser.Trim()}/watchlist/movies?limit={Settings.Limit}";
|
||||
break;
|
||||
case (int)TraktUserListType.UserWatchedList:
|
||||
link += $"/users/{Settings.AuthUser.Trim()}/watched/movies?limit={Settings.Limit}";
|
||||
break;
|
||||
case (int)TraktUserListType.UserCollectionList:
|
||||
link += $"/users/{Settings.AuthUser.Trim()}/collection/movies?limit={Settings.Limit}";
|
||||
break;
|
||||
}
|
||||
|
||||
var request = new NetImportRequest($"{link}", HttpAccept.Json);
|
||||
|
||||
request.HttpRequest.Headers.Add("trakt-api-version", "2");
|
||||
request.HttpRequest.Headers.Add("trakt-api-key", Settings.ClientId);
|
||||
|
||||
if (Settings.AccessToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.HttpRequest.Headers.Add("Authorization", "Bearer " + Settings.AccessToken);
|
||||
}
|
||||
|
||||
yield return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using FluentValidation;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
||||
namespace NzbDrone.Core.NetImport.Trakt.User
|
||||
{
|
||||
public class TraktUserSettingsValidator : TraktSettingsBaseValidator<TraktUserSettings>
|
||||
{
|
||||
public TraktUserSettingsValidator()
|
||||
: base()
|
||||
{
|
||||
RuleFor(c => c.TraktListType).NotNull();
|
||||
RuleFor(c => c.AuthUser).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public class TraktUserSettings : TraktSettingsBase<TraktUserSettings>
|
||||
{
|
||||
protected override AbstractValidator<TraktUserSettings> Validator => new TraktUserSettingsValidator();
|
||||
|
||||
public TraktUserSettings()
|
||||
{
|
||||
TraktListType = (int)TraktUserListType.UserWatchList;
|
||||
}
|
||||
|
||||
[FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktUserListType), HelpText = "Type of list your seeking to import from")]
|
||||
public int TraktListType { get; set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user