diff --git a/src/NzbDrone.Core/ImportLists/Trakt/List/TraktListRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/Trakt/List/TraktListRequestGenerator.cs index 660bf9e36..a9f6f7c7d 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/List/TraktListRequestGenerator.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/List/TraktListRequestGenerator.cs @@ -22,27 +22,10 @@ private IEnumerable GetSeriesRequest() { var link = Settings.BaseUrl.Trim(); - link += $"/users/{Settings.Username.Trim()}/lists/{Settings.Listname.ToUrlSlug()}/items/show,season,episode?limit={Settings.Limit}"; + link += $"/users/{Settings.Username.Trim()}/lists/{Settings.Listname.ToUrlSlug()}/items/show,season,episode"; - if (Settings.Rating.IsNotNullOrWhiteSpace()) - { - link += $"&ratings={Settings.Rating}"; - } - - if (Settings.Genres.IsNotNullOrWhiteSpace()) - { - link += $"&genres={Settings.Genres.ToLower()}"; - } - - if (Settings.Years.IsNotNullOrWhiteSpace()) - { - link += $"&years={Settings.Years}"; - } - - if (Settings.TraktAdditionalParameters.IsNotNullOrWhiteSpace()) - { - link += $"&{Settings.TraktAdditionalParameters.TrimStart('?').TrimStart('&')}"; - } + var filterParams = TraktQueryHelper.BuildFilterParameters(Settings.Rating, Settings.Genres, Settings.Years, Settings.Limit, Settings.TraktAdditionalParameters); + link += "?" + filterParams.ToQueryString(); var request = new ImportListRequest(link, HttpAccept.Json); diff --git a/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularRequestGenerator.cs index c9272b3b0..3ff86cf57 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularRequestGenerator.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularRequestGenerator.cs @@ -64,8 +64,8 @@ private IEnumerable GetSeriesRequest() break; } - var filtersAndLimit = $"?years={Settings.Years}&genres={Settings.Genres?.ToLower()}&ratings={Settings.Rating}&limit={Settings.Limit}{Settings.TraktAdditionalParameters}"; - link += filtersAndLimit; + var filterParams = TraktQueryHelper.BuildFilterParameters(Settings.Rating, Settings.Genres, Settings.Years, Settings.Limit, Settings.TraktAdditionalParameters); + link += "?" + filterParams.ToQueryString(); var request = new ImportListRequest(link, HttpAccept.Json); diff --git a/src/NzbDrone.Core/ImportLists/Trakt/TraktQueryHelper.cs b/src/NzbDrone.Core/ImportLists/Trakt/TraktQueryHelper.cs new file mode 100644 index 000000000..46cb4e053 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Trakt/TraktQueryHelper.cs @@ -0,0 +1,87 @@ +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 CommaSeparatedParams = new(StringComparer.OrdinalIgnoreCase) + { + "genres", + "certifications", + "networks", + "languages", + "countries", + "status" + }; + + public static Dictionary BuildFilterParameters(string rating, string genres, string years, int limit, string additionalParameters) + { + var parameters = new Dictionary(StringComparer.OrdinalIgnoreCase); + + // Parse additional parameters first (lower priority) + 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()) + { + parameters[parts[0].Trim()] = parts[1].Trim(); + } + } + } + + // Apply explicit settings (higher priority) + // For comma-separated params like genres, combine values from both sources + if (genres.IsNotNullOrWhiteSpace()) + { + if (parameters.TryGetValue("genres", out var existingGenres) && existingGenres.IsNotNullOrWhiteSpace()) + { + var allGenres = new HashSet(StringComparer.OrdinalIgnoreCase); + + foreach (var g in genres.ToLower().Split(',', StringSplitOptions.RemoveEmptyEntries)) + { + allGenres.Add(g.Trim()); + } + + foreach (var g in existingGenres.ToLower().Split(',', StringSplitOptions.RemoveEmptyEntries)) + { + allGenres.Add(g.Trim()); + } + + parameters["genres"] = string.Join(",", allGenres); + } + else + { + parameters["genres"] = genres.ToLower(); + } + } + + // For ratings and years, explicit settings override additional parameters + if (rating.IsNotNullOrWhiteSpace()) + { + parameters["ratings"] = rating; + } + + if (years.IsNotNullOrWhiteSpace()) + { + parameters["years"] = years; + } + + parameters["limit"] = limit.ToString(); + + return parameters; + } + + public static string ToQueryString(this Dictionary parameters) + { + return string.Join("&", parameters.Where(p => p.Value.IsNotNullOrWhiteSpace()).Select(p => $"{p.Key}={p.Value}")); + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs index 71d1c6ddf..a4a5e2088 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs @@ -63,19 +63,14 @@ private IEnumerable GetSeriesRequest() .SetHeader("trakt-api-key", _clientId) .AddQueryParam("limit", _settings.Limit.ToString()); - if (_settings.Rating.IsNotNullOrWhiteSpace()) - { - requestBuilder.AddQueryParam("ratings", _settings.Rating); - } + var filterParams = TraktQueryHelper.BuildFilterParameters(_settings.Rating, _settings.Genres, _settings.Years, _settings.Limit, _settings.TraktAdditionalParameters); - if (_settings.Genres.IsNotNullOrWhiteSpace()) + foreach (var param in filterParams) { - requestBuilder.AddQueryParam("genres", _settings.Genres.ToLower()); - } - - if (_settings.Years.IsNotNullOrWhiteSpace()) - { - requestBuilder.AddQueryParam("years", _settings.Years); + if (param.Key != "limit") + { + requestBuilder.AddQueryParam(param.Key, param.Value); + } } if (_settings.AccessToken.IsNotNullOrWhiteSpace()) @@ -83,21 +78,6 @@ private IEnumerable GetSeriesRequest() requestBuilder.SetHeader("Authorization", $"Bearer {_settings.AccessToken}"); } - if (_settings.TraktAdditionalParameters.IsNotNullOrWhiteSpace()) - { - var additionalParams = _settings.TraktAdditionalParameters.TrimStart('?').TrimStart('&'); - - foreach (var param in additionalParams.Split('&')) - { - var parts = param.Split('=', 2); - - if (parts.Length == 2) - { - requestBuilder.AddQueryParam(parts[0], parts[1]); - } - } - } - yield return new ImportListRequest(requestBuilder.Build()); } }