From a131c88d5fe04af75bab7b0feadcf7c81aec9f81 Mon Sep 17 00:00:00 2001 From: Daniel Martin Gonzalez Date: Tue, 17 Oct 2023 08:58:05 +0200 Subject: [PATCH] New: Add Watched filter type to Trakt User Import List --- .../Trakt/Popular/TraktPopularParser.cs | 12 ++-- .../ImportLists/Trakt/TraktAPI.cs | 27 +++++-- .../ImportLists/Trakt/TraktParser.cs | 12 ++-- .../ImportLists/Trakt/User/TraktUserImport.cs | 5 ++ .../ImportLists/Trakt/User/TraktUserParser.cs | 72 +++++++++++++++++++ .../Trakt/User/TraktUserRequestGenerator.cs | 2 +- .../Trakt/User/TraktUserSettings.cs | 7 +- .../Trakt/User/TraktUserWatchedListType.cs | 14 ++++ 8 files changed, 133 insertions(+), 18 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/Popular/TraktPopularParser.cs b/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularParser.cs index 006e1f7ef..ce1e9aac9 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularParser.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/Popular/TraktPopularParser.cs @@ -26,24 +26,24 @@ namespace NzbDrone.Core.ImportLists.Trakt.Popular return listItems; } - var jsonResponse = new List(); + var traktSeries = new List(); if (_settings.TraktListType == (int)TraktPopularListType.Popular) { - jsonResponse = STJson.Deserialize>(_importResponse.Content); + traktSeries = STJson.Deserialize>(_importResponse.Content); } else { - jsonResponse = STJson.Deserialize>(_importResponse.Content).SelectList(c => c.Show); + traktSeries = STJson.Deserialize>(_importResponse.Content).SelectList(c => c.Show); } - // no movies were return - if (jsonResponse == null) + // no series were returned + if (traktSeries == null) { return listItems; } - foreach (var series in jsonResponse) + foreach (var series in traktSeries) { listItems.AddIfNotNull(new ImportListItemInfo() { 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/TraktParser.cs b/src/NzbDrone.Core/ImportLists/Trakt/TraktParser.cs index 313378363..ffde5a462 100644 --- a/src/NzbDrone.Core/ImportLists/Trakt/TraktParser.cs +++ b/src/NzbDrone.Core/ImportLists/Trakt/TraktParser.cs @@ -22,20 +22,20 @@ namespace NzbDrone.Core.ImportLists.Trakt return series; } - var jsonResponse = STJson.Deserialize>(_importResponse.Content); + var traktResponses = STJson.Deserialize>(_importResponse.Content); - // no series were return - if (jsonResponse == null) + // no series were returned + if (traktResponses == null) { return series; } - foreach (var show in jsonResponse) + foreach (var traktResponse in traktResponses) { series.AddIfNotNull(new ImportListItemInfo() { - Title = show.Show.Title, - TvdbId = show.Show.Ids.Tvdb.GetValueOrDefault() + Title = traktResponse.Show.Title, + TvdbId = traktResponse.Show.Ids.Tvdb.GetValueOrDefault() }); } 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..3f3f27283 --- /dev/null +++ b/src/NzbDrone.Core/ImportLists/Trakt/User/TraktUserParser.cs @@ -0,0 +1,72 @@ +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 traktSeries = new List(); + + if (_settings.TraktListType == (int)TraktUserListType.UserWatchedList) + { + var jsonWatchedResponse = STJson.Deserialize>(_importResponse.Content); + + switch (_settings.TraktWatchedListType) + { + case (int)TraktUserWatchedListType.InProgress: + traktSeries = 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: + traktSeries = jsonWatchedResponse.Where(c => c.Seasons.Where(s => s.Number > 0).Sum(s => s.Episodes.Count) == c.Show.AiredEpisodes).SelectList(c => c.Show); + break; + default: + traktSeries = jsonWatchedResponse.SelectList(c => c.Show); + break; + } + } + else + { + traktSeries = STJson.Deserialize>(_importResponse.Content).SelectList(c => c.Show); + } + + // no series were returned + if (traktSeries == null) + { + return listItems; + } + + foreach (var series in traktSeries) + { + 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 + } +}