From 46a9d4635b202f04add26dad78512b51008e1ded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Mart=C3=ADn=20Gonz=C3=A1lez?= Date: Fri, 29 Sep 2023 19:08:06 +0200 Subject: [PATCH] New: Add Watched filter type to Trakt User Import List --- .../ImportLists/Trakt/TraktAPI.cs | 27 +++++-- .../ImportLists/Trakt/User/TraktUserImport.cs | 5 ++ .../ImportLists/Trakt/User/TraktUserParser.cs | 71 +++++++++++++++++++ .../Trakt/User/TraktUserRequestGenerator.cs | 2 +- .../Trakt/User/TraktUserSettings.cs | 7 +- .../Trakt/User/TraktUserWatchedListType.cs | 14 ++++ 6 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserParser.cs create mode 100644 src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserWatchedListType.cs diff --git a/src/NzbDrone.Core/ImportLists/Trakt/TraktAPI.cs b/src/NzbDrone.Core/ImportLists/Trakt/TraktAPI.cs index dcaf98f20..43318c1bc 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/TraktAPI.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/TraktAPI.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using System.Collections.Generic; +using System.Text.Json.Serialization; namespace NzbDrone.Core.ImportLists.Trakt { @@ -16,6 +17,8 @@ namespace NzbDrone.Core.ImportLists.Trakt public string Title { get; set; } public int? Year { get; set; } public TraktSeriesIdsResource Ids { get; set; } + [JsonPropertyName("aired_episodes")] + public int AiredEpisodes { get; set; } } public class TraktResponse @@ -23,13 +26,29 @@ namespace NzbDrone.Core.ImportLists.Trakt public TraktSeriesResource Show { get; set; } } + public class TraktWatchedEpisodeResource + { + public int? Plays { get; set; } + } + + public class TraktWatchedSeasonResource + { + public int? Number { get; set; } + public List Episodes { get; set; } + } + + public class TraktWatchedResponse : TraktResponse + { + public List Seasons { get; set; } + } + public class RefreshRequestResponse { - [JsonProperty("access_token")] + [JsonPropertyName("access_token")] public string AccessToken { get; set; } - [JsonProperty("expires_in")] + [JsonPropertyName("expires_in")] public int ExpiresIn { get; set; } - [JsonProperty("refresh_token")] + [JsonPropertyName("refresh_token")] public string RefreshToken { get; set; } } diff --git a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserImport.cs b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserImport.cs index 03fde5d08..76bcf71d7 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserImport.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserImport.cs @@ -19,6 +19,11 @@ namespace NzbDrone.Core.ImportLists.Trakt.User public override string Name => "Trakt User"; + public override IParseImportListResponse GetParser() + { + return new TraktUserParser(Settings); + } + public override IImportListRequestGenerator GetRequestGenerator() { return new TraktUserRequestGenerator() diff --git a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserParser.cs b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserParser.cs new file mode 100644 index 000000000..cdbcbfd18 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserParser.cs @@ -0,0 +1,71 @@ +using System.Collections.Generic; +using System.Linq; +using NzbDrone.Common.Extensions; +using NzbDrone.Common.Serializer; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.ImportLists.Trakt.User +{ + public class TraktUserParser : TraktParser + { + private readonly TraktUserSettings _settings; + private ImportListResponse _importResponse; + + public TraktUserParser(TraktUserSettings settings) + { + _settings = settings; + } + + public override IList ParseResponse(ImportListResponse importResponse) + { + _importResponse = importResponse; + + var listItems = new List(); + + if (!PreProcess(_importResponse)) + { + return listItems; + } + + var jsonResponse = new List(); + + if (_settings.TraktListType == (int)TraktUserListType.UserWatchedList) + { + var jsonWatchedResponse = STJson.Deserialize>(_importResponse.Content); + switch (_settings.TraktWatchedListType) + { + case (int)TraktUserWatchedListType.InProgress: + jsonResponse = jsonWatchedResponse.Where(c => c.Seasons.Where(s => s.Number > 0).Sum(s => s.Episodes.Count) < c.Show.AiredEpisodes).SelectList(c => c.Show); + break; + case (int)TraktUserWatchedListType.CompletelyWatched: + jsonResponse = jsonWatchedResponse.Where(c => c.Seasons.Where(s => s.Number > 0).Sum(s => s.Episodes.Count) == c.Show.AiredEpisodes).SelectList(c => c.Show); + break; + default: + jsonResponse = jsonWatchedResponse.SelectList(c => c.Show); + break; + } + } + else + { + jsonResponse = STJson.Deserialize>(_importResponse.Content).SelectList(c => c.Show); + } + + // no series were return + if (jsonResponse == null) + { + return listItems; + } + + foreach (var series in jsonResponse) + { + listItems.AddIfNotNull(new ImportListItemInfo() + { + Title = series.Title, + TvdbId = series.Ids.Tvdb.GetValueOrDefault(), + }); + } + + return listItems; + } + } +} diff --git a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs index 3bfd0d629..8f7c0396d 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserRequestGenerator.cs @@ -30,7 +30,7 @@ namespace NzbDrone.Core.ImportLists.Trakt.User link += $"/users/{userName}/watchlist/shows?limit={Settings.Limit}"; break; case (int)TraktUserListType.UserWatchedList: - link += $"/users/{userName}/watched/shows?limit={Settings.Limit}"; + link += $"/users/{userName}/watched/shows?extended=full&limit={Settings.Limit}"; break; case (int)TraktUserListType.UserCollectionList: link += $"/users/{userName}/collection/shows?limit={Settings.Limit}"; diff --git a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserSettings.cs b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserSettings.cs index 9784c858b..057f3edca 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserSettings.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserSettings.cs @@ -9,6 +9,7 @@ namespace NzbDrone.Core.ImportLists.Trakt.User : base() { RuleFor(c => c.TraktListType).NotNull(); + RuleFor(c => c.TraktWatchedListType).NotNull(); RuleFor(c => c.AuthUser).NotEmpty(); } } @@ -20,12 +21,16 @@ namespace NzbDrone.Core.ImportLists.Trakt.User public TraktUserSettings() { TraktListType = (int)TraktUserListType.UserWatchList; + TraktWatchedListType = (int)TraktUserWatchedListType.All; } [FieldDefinition(1, Label = "List Type", Type = FieldType.Select, SelectOptions = typeof(TraktUserListType), HelpText = "Type of list you're seeking to import from")] public int TraktListType { get; set; } - [FieldDefinition(2, Label = "Username", HelpText = "Username for the List to import from (empty to use Auth User)")] + [FieldDefinition(2, Label = "Watched List Filter", Type = FieldType.Select, SelectOptions = typeof(TraktUserWatchedListType), HelpText = "If List Type is Watched. Series do you want to import from")] + public int TraktWatchedListType { get; set; } + + [FieldDefinition(3, Label = "Username", HelpText = "Username for the List to import from (empty to use Auth User)")] public string Username { get; set; } } } diff --git a/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserWatchedListType.cs b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserWatchedListType.cs new file mode 100644 index 000000000..5b6526e6e --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserWatchedListType.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; + +namespace NzbDrone.Core.ImportLists.Trakt.User +{ + public enum TraktUserWatchedListType + { + [EnumMember(Value = "All")] + All = 0, + [EnumMember(Value = "In Progress")] + InProgress = 1, + [EnumMember(Value = "100% Watched")] + CompletelyWatched = 2 + } +}