mirror of
https://github.com/Sonarr/Sonarr
synced 2026-05-08 13:01:10 +02:00
Merge bdd09957f5 into bf5d48c76a
This commit is contained in:
commit
f7c1b361b8
10 changed files with 394 additions and 65 deletions
|
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using FluentAssertions;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.ImportLists.Trakt;
|
||||
using NzbDrone.Core.ImportLists.Trakt.List;
|
||||
using NzbDrone.Core.ImportLists.Trakt.Popular;
|
||||
using NzbDrone.Core.ImportLists.Trakt.User;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
|
||||
namespace NzbDrone.Core.Test.ImportListTests.Trakt
|
||||
{
|
||||
[TestFixture]
|
||||
public class TraktValidationFixture : CoreTest
|
||||
{
|
||||
[TestCase("0-100")]
|
||||
[TestCase("50-50")]
|
||||
[TestCase("100-100")]
|
||||
public void should_accept_supported_rating_ranges(string rating)
|
||||
{
|
||||
CreateValidListSettings(rating: rating).Validate().IsValid.Should().BeTrue();
|
||||
CreateValidPopularSettings(rating: rating).Validate().IsValid.Should().BeTrue();
|
||||
CreateValidUserSettings(rating: rating).Validate().IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("10")]
|
||||
[TestCase("10-5")]
|
||||
[TestCase("00-10")]
|
||||
[TestCase("10-101")]
|
||||
[TestCase("1000-1000")]
|
||||
[TestCase("10 - 20")]
|
||||
public void should_reject_invalid_rating_ranges(string rating)
|
||||
{
|
||||
CreateValidListSettings(rating: rating).Validate().IsValid.Should().BeFalse();
|
||||
CreateValidPopularSettings(rating: rating).Validate().IsValid.Should().BeFalse();
|
||||
CreateValidUserSettings(rating: rating).Validate().IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("1990")]
|
||||
[TestCase("1990-2000")]
|
||||
public void should_accept_supported_year_filters(string years)
|
||||
{
|
||||
CreateValidListSettings(years: years).Validate().IsValid.Should().BeTrue();
|
||||
CreateValidPopularSettings(years: years).Validate().IsValid.Should().BeTrue();
|
||||
CreateValidUserSettings(years: years).Validate().IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[TestCase("234923498237423-234723477")]
|
||||
[TestCase("199")]
|
||||
[TestCase("1990-1980")]
|
||||
[TestCase("1990 - 2000")]
|
||||
public void should_reject_invalid_year_filters(string years)
|
||||
{
|
||||
CreateValidListSettings(years: years).Validate().IsValid.Should().BeFalse();
|
||||
CreateValidPopularSettings(years: years).Validate().IsValid.Should().BeFalse();
|
||||
CreateValidUserSettings(years: years).Validate().IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[TestCase("genres=comedy")]
|
||||
[TestCase("ratings=80-100")]
|
||||
[TestCase("years=1990-2000")]
|
||||
[TestCase("limit=10")]
|
||||
public void should_reject_reserved_additional_parameters(string additionalParameters)
|
||||
{
|
||||
CreateValidListSettings(additionalParameters: additionalParameters).Validate().IsValid.Should().BeFalse();
|
||||
CreateValidPopularSettings(additionalParameters: additionalParameters).Validate().IsValid.Should().BeFalse();
|
||||
CreateValidUserSettings(additionalParameters: additionalParameters).Validate().IsValid.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_allow_non_reserved_additional_parameters()
|
||||
{
|
||||
CreateValidListSettings(additionalParameters: "languages=en").Validate().IsValid.Should().BeTrue();
|
||||
CreateValidPopularSettings(additionalParameters: "languages=en").Validate().IsValid.Should().BeTrue();
|
||||
CreateValidUserSettings(additionalParameters: "languages=en").Validate().IsValid.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_ignore_reserved_additional_parameters_when_building_filter_parameters()
|
||||
{
|
||||
var parameters = TraktQueryHelper.BuildFilterParameters(
|
||||
"80-100",
|
||||
"Drama",
|
||||
"1990-2000",
|
||||
25,
|
||||
"genres=comedy&ratings=10-20&years=2000-2010&limit=10&languages=en");
|
||||
|
||||
parameters.Should().ContainKey("genres").WhoseValue.Should().Be("drama");
|
||||
parameters.Should().ContainKey("ratings").WhoseValue.Should().Be("80-100");
|
||||
parameters.Should().ContainKey("years").WhoseValue.Should().Be("1990-2000");
|
||||
parameters.Should().ContainKey("limit").WhoseValue.Should().Be("25");
|
||||
parameters.Should().ContainKey("languages").WhoseValue.Should().Be("en");
|
||||
parameters.Should().HaveCount(5);
|
||||
}
|
||||
|
||||
private static TraktListSettings CreateValidListSettings(string rating = "80-100", string years = "1990-2000", string additionalParameters = "languages=en")
|
||||
{
|
||||
return new TraktListSettings
|
||||
{
|
||||
AccessToken = "access-token",
|
||||
RefreshToken = "refresh-token",
|
||||
Expires = DateTime.UtcNow.AddDays(1),
|
||||
Username = "sonarr",
|
||||
Listname = "watchlist",
|
||||
Rating = rating,
|
||||
Years = years,
|
||||
TraktAdditionalParameters = additionalParameters
|
||||
};
|
||||
}
|
||||
|
||||
private static TraktPopularSettings CreateValidPopularSettings(string rating = "80-100", string years = "1990-2000", string additionalParameters = "languages=en")
|
||||
{
|
||||
return new TraktPopularSettings
|
||||
{
|
||||
AccessToken = "access-token",
|
||||
RefreshToken = "refresh-token",
|
||||
Expires = DateTime.UtcNow.AddDays(1),
|
||||
Rating = rating,
|
||||
Years = years,
|
||||
TraktAdditionalParameters = additionalParameters
|
||||
};
|
||||
}
|
||||
|
||||
private static TraktUserSettings CreateValidUserSettings(string rating = "80-100", string years = "1990-2000", string additionalParameters = "languages=en")
|
||||
{
|
||||
return new TraktUserSettings
|
||||
{
|
||||
AccessToken = "access-token",
|
||||
RefreshToken = "refresh-token",
|
||||
Expires = DateTime.UtcNow.AddDays(1),
|
||||
AuthUser = "sonarr-user",
|
||||
Rating = rating,
|
||||
Years = years,
|
||||
TraktAdditionalParameters = additionalParameters
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -20,21 +20,31 @@ public virtual ImportListPageableRequestChain GetListItems()
|
|||
|
||||
private IEnumerable<ImportListRequest> GetSeriesRequest()
|
||||
{
|
||||
var link = Settings.BaseUrl.Trim();
|
||||
var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl.Trim());
|
||||
|
||||
link += $"/users/{Settings.Username.Trim()}/lists/{Settings.Listname.ToUrlSlug()}/items/show,season,episode?limit={Settings.Limit}";
|
||||
requestBuilder
|
||||
.Resource("/users/{userName}/lists/{listName}/items/show,season,episode")
|
||||
.SetSegment("userName", Settings.Username.Trim())
|
||||
.SetSegment("listName", Settings.Listname.ToUrlSlug())
|
||||
.Accept(HttpAccept.Json);
|
||||
|
||||
var request = new ImportListRequest(link, HttpAccept.Json);
|
||||
var filterParams = TraktQueryHelper.BuildFilterParameters(Settings.Rating, Settings.Genres, Settings.Years, Settings.Limit, Settings.TraktAdditionalParameters);
|
||||
|
||||
request.HttpRequest.Headers.Add("trakt-api-version", "2");
|
||||
request.HttpRequest.Headers.Add("trakt-api-key", ClientId);
|
||||
foreach (var param in filterParams)
|
||||
{
|
||||
requestBuilder.AddQueryParam(param.Key, param.Value);
|
||||
}
|
||||
|
||||
requestBuilder
|
||||
.SetHeader("trakt-api-version", "2")
|
||||
.SetHeader("trakt-api-key", ClientId);
|
||||
|
||||
if (Settings.AccessToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.HttpRequest.Headers.Add("Authorization", "Bearer " + Settings.AccessToken);
|
||||
requestBuilder.SetHeader("Authorization", $"Bearer {Settings.AccessToken}");
|
||||
}
|
||||
|
||||
yield return request;
|
||||
yield return new ImportListRequest(requestBuilder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
|
|
@ -10,6 +11,11 @@ public TraktListSettingsValidator()
|
|||
{
|
||||
RuleFor(c => c.Username).NotEmpty();
|
||||
RuleFor(c => c.Listname).NotEmpty();
|
||||
|
||||
RuleFor(c => c.Years)
|
||||
.Must(BeValidYearRange)
|
||||
.When(c => c.Years.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Not a valid year or range of years");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -23,6 +29,9 @@ public class TraktListSettings : TraktSettingsBase<TraktListSettings>
|
|||
[FieldDefinition(2, Label = "ImportListsTraktSettingsListName", HelpText = "ImportListsTraktSettingsListNameHelpText")]
|
||||
public string Listname { get; set; }
|
||||
|
||||
[FieldDefinition(3, Label = "ImportListsTraktSettingsYears", HelpText = "ImportListsTraktSettingsYearsSeriesHelpText")]
|
||||
public string Years { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
|
|
|||
|
|
@ -21,63 +21,72 @@ public virtual ImportListPageableRequestChain GetListItems()
|
|||
|
||||
private IEnumerable<ImportListRequest> GetSeriesRequest()
|
||||
{
|
||||
var link = Settings.BaseUrl.Trim();
|
||||
var requestBuilder = new HttpRequestBuilder(Settings.BaseUrl.Trim());
|
||||
|
||||
var resource = "/shows";
|
||||
|
||||
switch (Settings.TraktListType)
|
||||
{
|
||||
case (int)TraktPopularListType.Trending:
|
||||
link += "/shows/trending";
|
||||
resource += "/trending";
|
||||
break;
|
||||
case (int)TraktPopularListType.Popular:
|
||||
link += "/shows/popular";
|
||||
resource += "/popular";
|
||||
break;
|
||||
case (int)TraktPopularListType.Anticipated:
|
||||
link += "/shows/anticipated";
|
||||
resource += "/anticipated";
|
||||
break;
|
||||
case (int)TraktPopularListType.TopWatchedByWeek:
|
||||
link += "/shows/watched/weekly";
|
||||
resource += "/watched/weekly";
|
||||
break;
|
||||
case (int)TraktPopularListType.TopWatchedByMonth:
|
||||
link += "/shows/watched/monthly";
|
||||
resource += "/watched/monthly";
|
||||
break;
|
||||
#pragma warning disable CS0612
|
||||
case (int)TraktPopularListType.TopWatchedByYear:
|
||||
#pragma warning restore CS0612
|
||||
link += "/shows/watched/yearly";
|
||||
resource += "/watched/yearly";
|
||||
break;
|
||||
case (int)TraktPopularListType.TopWatchedByAllTime:
|
||||
link += "/shows/watched/all";
|
||||
resource += "/watched/all";
|
||||
break;
|
||||
case (int)TraktPopularListType.RecommendedByWeek:
|
||||
link += "/shows/recommended/weekly";
|
||||
resource += "/recommended/weekly";
|
||||
break;
|
||||
case (int)TraktPopularListType.RecommendedByMonth:
|
||||
link += "/shows/recommended/monthly";
|
||||
resource += "/recommended/monthly";
|
||||
break;
|
||||
#pragma warning disable CS0612
|
||||
case (int)TraktPopularListType.RecommendedByYear:
|
||||
#pragma warning restore CS0612
|
||||
link += "/shows/recommended/yearly";
|
||||
resource += "/recommended/yearly";
|
||||
break;
|
||||
case (int)TraktPopularListType.RecommendedByAllTime:
|
||||
link += "/shows/recommended/all";
|
||||
resource += "/recommended/all";
|
||||
break;
|
||||
}
|
||||
|
||||
var filtersAndLimit = $"?years={Settings.Years}&genres={Settings.Genres?.ToLower()}&ratings={Settings.Rating}&limit={Settings.Limit}{Settings.TraktAdditionalParameters}";
|
||||
link += filtersAndLimit;
|
||||
requestBuilder
|
||||
.Resource(resource)
|
||||
.Accept(HttpAccept.Json);
|
||||
|
||||
var request = new ImportListRequest(link, HttpAccept.Json);
|
||||
var filterParams = TraktQueryHelper.BuildFilterParameters(Settings.Rating, Settings.Genres, Settings.Years, Settings.Limit, Settings.TraktAdditionalParameters);
|
||||
|
||||
request.HttpRequest.Headers.Add("trakt-api-version", "2");
|
||||
request.HttpRequest.Headers.Add("trakt-api-key", ClientId);
|
||||
foreach (var param in filterParams)
|
||||
{
|
||||
requestBuilder.AddQueryParam(param.Key, param.Value);
|
||||
}
|
||||
|
||||
requestBuilder
|
||||
.SetHeader("trakt-api-version", "2")
|
||||
.SetHeader("trakt-api-key", ClientId);
|
||||
|
||||
if (Settings.AccessToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
request.HttpRequest.Headers.Add("Authorization", "Bearer " + Settings.AccessToken);
|
||||
requestBuilder.SetHeader("Authorization", $"Bearer {Settings.AccessToken}");
|
||||
}
|
||||
|
||||
yield return request;
|
||||
yield return new ImportListRequest(requestBuilder.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Text.RegularExpressions;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
|
@ -19,15 +18,8 @@ public TraktPopularSettingsValidator()
|
|||
.WithMessage("Yearly lists are no longer supported");
|
||||
#pragma warning restore CS0612
|
||||
|
||||
// Loose validation @TODO
|
||||
RuleFor(c => c.Rating)
|
||||
.Matches(@"^\d+\-\d+$", RegexOptions.IgnoreCase)
|
||||
.When(c => c.Rating.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Not a valid rating");
|
||||
|
||||
// Loose validation @TODO
|
||||
RuleFor(c => c.Years)
|
||||
.Matches(@"^\d+(\-\d+)?$", RegexOptions.IgnoreCase)
|
||||
.Must(BeValidYearRange)
|
||||
.When(c => c.Years.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Not a valid year or range of years");
|
||||
}
|
||||
|
|
@ -45,18 +37,9 @@ public TraktPopularSettings()
|
|||
[FieldDefinition(1, Label = "ImportListsTraktSettingsListType", Type = FieldType.Select, SelectOptions = typeof(TraktPopularListType), HelpText = "ImportListsTraktSettingsListTypeHelpText")]
|
||||
public int TraktListType { get; set; }
|
||||
|
||||
[FieldDefinition(2, Label = "ImportListsTraktSettingsRating", HelpText = "ImportListsTraktSettingsRatingSeriesHelpText")]
|
||||
public string Rating { get; set; }
|
||||
|
||||
[FieldDefinition(4, Label = "ImportListsTraktSettingsGenres", HelpText = "ImportListsTraktSettingsGenresSeriesHelpText")]
|
||||
public string Genres { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "ImportListsTraktSettingsYears", HelpText = "ImportListsTraktSettingsYearsSeriesHelpText")]
|
||||
[FieldDefinition(2, Label = "ImportListsTraktSettingsYears", HelpText = "ImportListsTraktSettingsYearsSeriesHelpTextPopular")]
|
||||
public string Years { get; set; }
|
||||
|
||||
[FieldDefinition(6, Label = "ImportListsTraktSettingsAdditionalParameters", HelpText = "ImportListsTraktSettingsAdditionalParametersHelpText", Advanced = true)]
|
||||
public string TraktAdditionalParameters { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
|
|
|||
91
src/NzbDrone.Core/ImportLists/Trakt/TraktQueryHelper.cs
Normal file
91
src/NzbDrone.Core/ImportLists/Trakt/TraktQueryHelper.cs
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NzbDrone.Common.Extensions;
|
||||
|
||||
namespace NzbDrone.Core.ImportLists.Trakt
|
||||
{
|
||||
public static class TraktQueryHelper
|
||||
{
|
||||
private static readonly HashSet<string> ReservedFilterParameters = new(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"genres",
|
||||
"ratings",
|
||||
"years",
|
||||
"limit"
|
||||
};
|
||||
|
||||
public static Dictionary<string, string> BuildFilterParameters(string rating, string genres, string years, int limit, string additionalParameters)
|
||||
{
|
||||
var parameters = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (additionalParameters.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var trimmed = additionalParameters.TrimStart('?').TrimStart('&');
|
||||
|
||||
foreach (var param in trimmed.Split('&', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var parts = param.Split('=', 2);
|
||||
|
||||
if (parts.Length == 2 && parts[0].IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var key = parts[0].Trim();
|
||||
|
||||
if (ReservedFilterParameters.Contains(key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
parameters[key] = parts[1].Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (genres.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters["genres"] = genres.ToLower();
|
||||
}
|
||||
|
||||
if (rating.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters["ratings"] = rating;
|
||||
}
|
||||
|
||||
if (years.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
parameters["years"] = years;
|
||||
}
|
||||
|
||||
parameters["limit"] = limit.ToString();
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public static bool ContainsReservedFilterParameters(string additionalParameters)
|
||||
{
|
||||
if (additionalParameters.IsNullOrWhiteSpace())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var trimmed = additionalParameters.TrimStart('?').TrimStart('&');
|
||||
|
||||
foreach (var param in trimmed.Split('&', StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
var parts = param.Split('=', 2);
|
||||
|
||||
if (parts.Length == 2 && parts[0].IsNotNullOrWhiteSpace() && ReservedFilterParameters.Contains(parts[0].Trim()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string ToQueryString(this Dictionary<string, string> parameters)
|
||||
{
|
||||
return string.Join("&", parameters.Where(p => p.Value.IsNotNullOrWhiteSpace()).Select(p => $"{p.Key}={p.Value}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
|
|
@ -31,6 +32,73 @@ public TraktSettingsBaseValidator()
|
|||
RuleFor(c => c.Limit)
|
||||
.GreaterThan(0)
|
||||
.WithMessage("Must be integer greater than 0");
|
||||
|
||||
RuleFor(c => c.Rating)
|
||||
.Must(BeValidRatingRange)
|
||||
.When(c => c.Rating.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Not a valid rating");
|
||||
|
||||
RuleFor(c => c.TraktAdditionalParameters)
|
||||
.Must(additionalParameters => !TraktQueryHelper.ContainsReservedFilterParameters(additionalParameters))
|
||||
.When(c => c.TraktAdditionalParameters.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Additional parameters cannot include genres, ratings, years, or limit");
|
||||
}
|
||||
|
||||
protected static bool BeValidYearRange(string years)
|
||||
{
|
||||
var parts = years.Split('-', StringSplitOptions.None);
|
||||
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
return TryParseYear(parts[0], out _);
|
||||
}
|
||||
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryParseYear(parts[0], out var startYear) &&
|
||||
TryParseYear(parts[1], out var endYear) &&
|
||||
startYear <= endYear;
|
||||
}
|
||||
|
||||
private static bool BeValidRatingRange(string rating)
|
||||
{
|
||||
var parts = rating.Split('-', StringSplitOptions.None);
|
||||
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryParseRating(parts[0], out var minimumRating) &&
|
||||
TryParseRating(parts[1], out var maximumRating) &&
|
||||
minimumRating <= maximumRating;
|
||||
}
|
||||
|
||||
private static bool TryParseYear(string token, out int year)
|
||||
{
|
||||
year = default;
|
||||
|
||||
return token.Length == 4 &&
|
||||
int.TryParse(token, NumberStyles.None, CultureInfo.InvariantCulture, out year) &&
|
||||
year >= 1000;
|
||||
}
|
||||
|
||||
private static bool TryParseRating(string token, out int rating)
|
||||
{
|
||||
if (!int.TryParse(token, NumberStyles.None, CultureInfo.InvariantCulture, out rating))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rating is < 0 or > 100)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return token == rating.ToString(CultureInfo.InvariantCulture);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -59,6 +127,15 @@ public TraktSettingsBase()
|
|||
[FieldDefinition(0, Label = "ImportListsSettingsAuthUser", Type = FieldType.Textbox, Hidden = HiddenType.Hidden)]
|
||||
public string AuthUser { get; set; }
|
||||
|
||||
[FieldDefinition(95, Label = "ImportListsTraktSettingsRating", HelpText = "ImportListsTraktSettingsRatingSeriesHelpText")]
|
||||
public string Rating { get; set; }
|
||||
|
||||
[FieldDefinition(96, Label = "ImportListsTraktSettingsGenres", HelpText = "ImportListsTraktSettingsGenresSeriesHelpText")]
|
||||
public string Genres { get; set; }
|
||||
|
||||
[FieldDefinition(97, Label = "ImportListsTraktSettingsAdditionalParameters", HelpText = "ImportListsTraktSettingsAdditionalParametersHelpText", Advanced = true)]
|
||||
public string TraktAdditionalParameters { get; set; }
|
||||
|
||||
[FieldDefinition(98, Label = "ImportListsTraktSettingsLimit", HelpText = "ImportListsTraktSettingsLimitSeriesHelpText")]
|
||||
public int Limit { get; set; }
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ public virtual ImportListPageableRequestChain GetListItems()
|
|||
|
||||
private IEnumerable<ImportListRequest> GetSeriesRequest()
|
||||
{
|
||||
var requestBuilder = new HttpRequestBuilder(_settings.BaseUrl.Trim());
|
||||
var link = _settings.BaseUrl.Trim();
|
||||
|
||||
var userName = _settings.Username.IsNotNullOrWhiteSpace() ? _settings.Username.Trim() : _settings.AuthUser.Trim();
|
||||
|
||||
switch (_settings.TraktListType)
|
||||
{
|
||||
|
|
@ -39,36 +41,37 @@ private IEnumerable<ImportListRequest> GetSeriesRequest()
|
|||
_ => "rank"
|
||||
};
|
||||
|
||||
requestBuilder
|
||||
.Resource("/users/{userName}/watchlist/shows/{sorting}")
|
||||
.SetSegment("sorting", watchSorting);
|
||||
link += $"/users/{userName}/watchlist/shows/{watchSorting}";
|
||||
break;
|
||||
case (int)TraktUserListType.UserWatchedList:
|
||||
requestBuilder
|
||||
.Resource("/users/{userName}/watched/shows")
|
||||
.AddQueryParam("extended", "full");
|
||||
link += $"/users/{userName}/watched/shows";
|
||||
break;
|
||||
case (int)TraktUserListType.UserCollectionList:
|
||||
requestBuilder.Resource("/users/{userName}/collection/shows");
|
||||
link += $"/users/{userName}/collection/shows";
|
||||
break;
|
||||
}
|
||||
|
||||
var userName = _settings.Username.IsNotNullOrWhiteSpace() ? _settings.Username.Trim() : _settings.AuthUser.Trim();
|
||||
var filterParams = TraktQueryHelper.BuildFilterParameters(_settings.Rating, _settings.Genres, _settings.Years, _settings.Limit, _settings.TraktAdditionalParameters);
|
||||
|
||||
requestBuilder
|
||||
.SetSegment("userName", userName)
|
||||
.Accept(HttpAccept.Json)
|
||||
.WithRateLimit(4)
|
||||
.SetHeader("trakt-api-version", "2")
|
||||
.SetHeader("trakt-api-key", _clientId)
|
||||
.AddQueryParam("limit", _settings.Limit.ToString());
|
||||
// Add extended parameter for watched list
|
||||
if (_settings.TraktListType == (int)TraktUserListType.UserWatchedList)
|
||||
{
|
||||
filterParams["extended"] = "full";
|
||||
}
|
||||
|
||||
link += "?" + filterParams.ToQueryString();
|
||||
|
||||
var request = new ImportListRequest(link, HttpAccept.Json);
|
||||
|
||||
request.HttpRequest.Headers.Add("trakt-api-version", "2");
|
||||
request.HttpRequest.Headers.Add("trakt-api-key", _clientId);
|
||||
|
||||
if (_settings.AccessToken.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
requestBuilder.SetHeader("Authorization", $"Bearer {_settings.AccessToken}");
|
||||
request.HttpRequest.Headers.Add("Authorization", $"Bearer {_settings.AccessToken}");
|
||||
}
|
||||
|
||||
yield return new ImportListRequest(requestBuilder.Build());
|
||||
yield return request;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using FluentValidation;
|
||||
using NzbDrone.Common.Extensions;
|
||||
using NzbDrone.Core.Annotations;
|
||||
using NzbDrone.Core.Validation;
|
||||
|
||||
|
|
@ -11,6 +12,11 @@ public TraktUserSettingsValidator()
|
|||
RuleFor(c => c.TraktListType).NotNull();
|
||||
RuleFor(c => c.TraktWatchedListType).NotNull();
|
||||
RuleFor(c => c.AuthUser).NotEmpty();
|
||||
|
||||
RuleFor(c => c.Years)
|
||||
.Must(BeValidYearRange)
|
||||
.When(c => c.Years.IsNotNullOrWhiteSpace())
|
||||
.WithMessage("Not a valid year or range of years");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -37,6 +43,9 @@ public TraktUserSettings()
|
|||
[FieldDefinition(4, Label = "Username", HelpText = "ImportListsTraktSettingsUserListUsernameHelpText")]
|
||||
public string Username { get; set; }
|
||||
|
||||
[FieldDefinition(5, Label = "ImportListsTraktSettingsYears", HelpText = "ImportListsTraktSettingsYearsSeriesHelpText")]
|
||||
public string Years { get; set; }
|
||||
|
||||
public override NzbDroneValidationResult Validate()
|
||||
{
|
||||
return new NzbDroneValidationResult(Validator.Validate(this));
|
||||
|
|
|
|||
|
|
@ -957,7 +957,7 @@
|
|||
"ImportListsTraktSettingsAdditionalParametersHelpText": "Additional Trakt API parameters",
|
||||
"ImportListsTraktSettingsAuthenticateWithTrakt": "Authenticate with Trakt",
|
||||
"ImportListsTraktSettingsGenres": "Genres",
|
||||
"ImportListsTraktSettingsGenresSeriesHelpText": "Filter series by Trakt Genre Slug (Comma Separated) Only for Popular Lists",
|
||||
"ImportListsTraktSettingsGenresSeriesHelpText": "Filter series by Trakt Genre slug, comma separated (action,comedy)",
|
||||
"ImportListsTraktSettingsLimit": "Limit",
|
||||
"ImportListsTraktSettingsLimitSeriesHelpText": "Limit the number of series to get",
|
||||
"ImportListsTraktSettingsListName": "List Name",
|
||||
|
|
@ -992,7 +992,8 @@
|
|||
"ImportListsTraktSettingsWatchedListTypeCompleted": "100% Watched",
|
||||
"ImportListsTraktSettingsWatchedListTypeInProgress": "In Progress",
|
||||
"ImportListsTraktSettingsYears": "Years",
|
||||
"ImportListsTraktSettingsYearsSeriesHelpText": "Filter series by year or year range",
|
||||
"ImportListsTraktSettingsYearsSeriesHelpText": "Filter series by minimum year or year range (1990-2000)",
|
||||
"ImportListsTraktSettingsYearsSeriesHelpTextPopular": "Filter series by a specific year or year range (1990-2000)",
|
||||
"ImportListsValidationInvalidApiKey": "API Key is invalid",
|
||||
"ImportListsValidationTestFailed": "Test was aborted due to an error: {exceptionMessage}",
|
||||
"ImportListsValidationUnableToConnectException": "Unable to connect to import list: {exceptionMessage}. Check the log surrounding this error for details.",
|
||||
|
|
|
|||
Loading…
Reference in a new issue