diff --git a/.editorconfig b/.editorconfig
index 5a7b38e..b84e563 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -13,7 +13,7 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
-max_line_length = 9999
+max_line_length = off
# YAML indentation
[*.{yml,yaml}]
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktEpisode.cs b/Trakt/Api/DataContracts/BaseModel/TraktEpisode.cs
index e6206cc..d6f6382 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktEpisode.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktEpisode.cs
@@ -1,13 +1,30 @@
-namespace Trakt.Api.DataContracts.BaseModel
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktEpisode
{
- public class TraktEpisode
- {
- public int? season { get; set; }
+ ///
+ /// Gets or sets the season number.
+ ///
+ [JsonPropertyName("season")]
+ public int? Season { get; set; }
- public int? number { get; set; }
+ ///
+ /// Gets or sets the episode number.
+ ///
+ [JsonPropertyName("number")]
+ public int? Number { get; set; }
- public string title { get; set; }
+ ///
+ /// Gets or sets the episode title.
+ ///
+ [JsonPropertyName("title")]
+ public string Title { get; set; }
- public TraktEpisodeId ids { get; set; }
- }
-}
\ No newline at end of file
+ ///
+ /// Gets or sets the episode ids.
+ ///
+ [JsonPropertyName("ids")]
+ public TraktEpisodeId Ids { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktEpisodeId.cs b/Trakt/Api/DataContracts/BaseModel/TraktEpisodeId.cs
index 64a129f..5e18f74 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktEpisodeId.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktEpisodeId.cs
@@ -1,6 +1,5 @@
-namespace Trakt.Api.DataContracts.BaseModel
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktEpisodeId : TraktTVId
{
- public class TraktEpisodeId : TraktTVId
- {
- }
-}
\ No newline at end of file
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktIMDBandTMDBId.cs b/Trakt/Api/DataContracts/BaseModel/TraktIMDBandTMDBId.cs
index 0651c94..8e1a66a 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktIMDBandTMDBId.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktIMDBandTMDBId.cs
@@ -1,9 +1,12 @@
-namespace Trakt.Api.DataContracts.BaseModel
-{
- public class TraktIMDBandTMDBId : TraktId
- {
- public string imdb { get; set; }
+using System.Text.Json.Serialization;
- public int? tmdb { get; set; }
- }
-}
\ No newline at end of file
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktIMDBandTMDBId : TraktId
+{
+ [JsonPropertyName("imdb")]
+ public string Imdb { get; set; }
+
+ [JsonPropertyName("tmdb")]
+ public int? Tmdb { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktId.cs b/Trakt/Api/DataContracts/BaseModel/TraktId.cs
index a1f77bc..26104d9 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktId.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktId.cs
@@ -1,10 +1,18 @@
-
-namespace Trakt.Api.DataContracts.BaseModel
-{
- public class TraktId
- {
- public int? trakt { get; set; }
+using System.Text.Json.Serialization;
- public string slug { get; set; }
- }
-}
\ No newline at end of file
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktId
+{
+ ///
+ /// Gets or sets the Trakt item id.
+ ///
+ [JsonPropertyName("trakt")]
+ public int? Trakt { get; set; }
+
+ ///
+ /// Gets or sets the item slug.
+ ///
+ [JsonPropertyName("slug")]
+ public string Slug { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktMovie.cs b/Trakt/Api/DataContracts/BaseModel/TraktMovie.cs
index 67e946d..3c958eb 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktMovie.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktMovie.cs
@@ -1,12 +1,15 @@
-
-namespace Trakt.Api.DataContracts.BaseModel
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktMovie
{
- public class TraktMovie
- {
- public string title { get; set; }
+ [JsonPropertyName("title")]
+ public string Title { get; set; }
- public int? year { get; set; }
+ [JsonPropertyName("year")]
+ public int? Year { get; set; }
- public TraktMovieId ids { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("ids")]
+ public TraktMovieId Ids { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktMovieId.cs b/Trakt/Api/DataContracts/BaseModel/TraktMovieId.cs
index 75d2616..ba277a6 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktMovieId.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktMovieId.cs
@@ -1,6 +1,5 @@
-namespace Trakt.Api.DataContracts.BaseModel
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktMovieId : TraktIMDBandTMDBId
{
- public class TraktMovieId : TraktIMDBandTMDBId
- {
- }
-}
\ No newline at end of file
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktPerson.cs b/Trakt/Api/DataContracts/BaseModel/TraktPerson.cs
index f6a7bf1..c2cd971 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktPerson.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktPerson.cs
@@ -1,9 +1,12 @@
-namespace Trakt.Api.DataContracts.BaseModel
-{
- public class TraktPerson
- {
- public string name { get; set; }
+using System.Text.Json.Serialization;
- public TraktPersonId ids { get; set; }
- }
-}
\ No newline at end of file
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktPerson
+{
+ [JsonPropertyName("name")]
+ public string Name { get; set; }
+
+ [JsonPropertyName("ids")]
+ public TraktPersonId Ids { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktPersonId.cs b/Trakt/Api/DataContracts/BaseModel/TraktPersonId.cs
index 1b1b1fb..d47314d 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktPersonId.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktPersonId.cs
@@ -1,7 +1,9 @@
-namespace Trakt.Api.DataContracts.BaseModel
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktPersonId : TraktIMDBandTMDBId
{
- public class TraktPersonId : TraktIMDBandTMDBId
- {
- public int? tvrage { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("tvrage")]
+ public int? Tvrage { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktRated.cs b/Trakt/Api/DataContracts/BaseModel/TraktRated.cs
index ec57a51..1fa93af 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktRated.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktRated.cs
@@ -1,9 +1,12 @@
-namespace Trakt.Api.DataContracts.BaseModel
-{
- public abstract class TraktRated
- {
- public int? rating { get; set; }
+using System.Text.Json.Serialization;
- public string rated_at { get; set; }
- }
-}
\ No newline at end of file
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public abstract class TraktRated
+{
+ [JsonPropertyName("rating")]
+ public int? Rating { get; set; }
+
+ [JsonPropertyName("rated_at")]
+ public string RatedAt { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktSeason.cs b/Trakt/Api/DataContracts/BaseModel/TraktSeason.cs
index b9022dd..07f4efd 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktSeason.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktSeason.cs
@@ -1,9 +1,12 @@
-namespace Trakt.Api.DataContracts.BaseModel
-{
- public class TraktSeason
- {
- public int? number { get; set; }
+using System.Text.Json.Serialization;
- public TraktSeasonId ids { get; set; }
- }
-}
\ No newline at end of file
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktSeason
+{
+ [JsonPropertyName("number")]
+ public int? Number { get; set; }
+
+ [JsonPropertyName("ids")]
+ public TraktSeasonId Ids { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktSeasonId.cs b/Trakt/Api/DataContracts/BaseModel/TraktSeasonId.cs
index c34442c..10e5c3a 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktSeasonId.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktSeasonId.cs
@@ -1,11 +1,15 @@
-namespace Trakt.Api.DataContracts.BaseModel
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktSeasonId : TraktId
{
- public class TraktSeasonId : TraktId
- {
- public int? tmdb { get; set; }
+ [JsonPropertyName("tmdb")]
+ public int? Tmdb { get; set; }
- public int? tvdb { get; set; }
+ [JsonPropertyName("tvdb")]
+ public int? Tvdb { get; set; }
- public int? tvrage { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("tvrage")]
+ public int? Tvrage { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktShow.cs b/Trakt/Api/DataContracts/BaseModel/TraktShow.cs
index 215b4f8..3e5664d 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktShow.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktShow.cs
@@ -1,11 +1,15 @@
-namespace Trakt.Api.DataContracts.BaseModel
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktShow
{
- public class TraktShow
- {
- public string title { get; set; }
+ [JsonPropertyName("title")]
+ public string Title { get; set; }
- public int? year { get; set; }
+ [JsonPropertyName("year")]
+ public int? Year { get; set; }
- public TraktShowId ids { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("ids")]
+ public TraktShowId Ids { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktShowId.cs b/Trakt/Api/DataContracts/BaseModel/TraktShowId.cs
index 885915d..cd66573 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktShowId.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktShowId.cs
@@ -1,7 +1,5 @@
-
-namespace Trakt.Api.DataContracts.BaseModel
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktShowId : TraktTVId
{
- public class TraktShowId : TraktTVId
- {
- }
-}
\ No newline at end of file
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktTVId.cs b/Trakt/Api/DataContracts/BaseModel/TraktTVId.cs
index 91c9378..c706c3a 100644
--- a/Trakt/Api/DataContracts/BaseModel/TraktTVId.cs
+++ b/Trakt/Api/DataContracts/BaseModel/TraktTVId.cs
@@ -1,10 +1,12 @@
+using System.Text.Json.Serialization;
-namespace Trakt.Api.DataContracts.BaseModel
+namespace Trakt.Api.DataContracts.BaseModel;
+
+public class TraktTVId : TraktIMDBandTMDBId
{
- public class TraktTVId : TraktIMDBandTMDBId
- {
- public int? tvdb { get; set; }
+ [JsonPropertyName("tvdb")]
+ public int? Tvdb { get; set; }
- public int? tvrage { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("tvrage")]
+ public int? Tvrage { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/BaseModel/TraktUserSummary.cs b/Trakt/Api/DataContracts/BaseModel/TraktUserSummary.cs
deleted file mode 100644
index 14a0b35..0000000
--- a/Trakt/Api/DataContracts/BaseModel/TraktUserSummary.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-
-namespace Trakt.Api.DataContracts.BaseModel
-{
- public class TraktUserSummary
- {
- public string username { get; set; }
-
- public string name { get; set; }
-
- public bool vip { get; set; }
-
- public bool @private { get; set; }
- }
-}
\ No newline at end of file
diff --git a/Trakt/Api/DataContracts/Comments/TraktComment.cs b/Trakt/Api/DataContracts/Comments/TraktComment.cs
deleted file mode 100644
index f4d2101..0000000
--- a/Trakt/Api/DataContracts/Comments/TraktComment.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using Trakt.Api.DataContracts.BaseModel;
-
-namespace Trakt.Api.DataContracts.Comments
-{
- public class TraktComment
- {
- public int id { get; set; }
-
- public int? parent_id { get; set; }
-
- public string created_at { get; set; }
-
- public string comment { get; set; }
-
- public bool spoiler { get; set; }
-
- public bool review { get; set; }
-
- public int replies { get; set; }
-
- public int likes { get; set; }
-
- public int? user_rating { get; set; }
-
- public TraktUserSummary user { get; set; }
- }
-}
\ No newline at end of file
diff --git a/Trakt/Api/DataContracts/Scrobble/SocialMedia.cs b/Trakt/Api/DataContracts/Scrobble/SocialMedia.cs
new file mode 100644
index 0000000..61c65cd
--- /dev/null
+++ b/Trakt/Api/DataContracts/Scrobble/SocialMedia.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.Scrobble;
+
+public class SocialMedia
+{
+ [JsonPropertyName("facebook")]
+ public bool Facebook { get; set; }
+
+ [JsonPropertyName("twitter")]
+ public bool Twitter { get; set; }
+
+ [JsonPropertyName("tumblr")]
+ public bool Tumblr { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Scrobble/TraktScrobbleEpisode.cs b/Trakt/Api/DataContracts/Scrobble/TraktScrobbleEpisode.cs
index 67d9ceb..799c50f 100644
--- a/Trakt/Api/DataContracts/Scrobble/TraktScrobbleEpisode.cs
+++ b/Trakt/Api/DataContracts/Scrobble/TraktScrobbleEpisode.cs
@@ -1,17 +1,22 @@
-using Trakt.Api.DataContracts.BaseModel;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Scrobble
+namespace Trakt.Api.DataContracts.Scrobble;
+
+public class TraktScrobbleEpisode
{
- public class TraktScrobbleEpisode
- {
- public TraktShow show { get; set; }
+ [JsonPropertyName("show")]
+ public TraktShow Show { get; set; }
- public TraktEpisode episode { get; set; }
+ [JsonPropertyName("episode")]
+ public TraktEpisode Episode { get; set; }
- public float progress { get; set; }
+ [JsonPropertyName("progress")]
+ public float Progress { get; set; }
- public string app_version { get; set; }
+ [JsonPropertyName("app_version")]
+ public string AppVersion { get; set; }
- public string app_date { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("app_date")]
+ public string AppDate { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Scrobble/TraktScrobbleMovie.cs b/Trakt/Api/DataContracts/Scrobble/TraktScrobbleMovie.cs
index e374faa..80a81a7 100644
--- a/Trakt/Api/DataContracts/Scrobble/TraktScrobbleMovie.cs
+++ b/Trakt/Api/DataContracts/Scrobble/TraktScrobbleMovie.cs
@@ -1,15 +1,19 @@
-using Trakt.Api.DataContracts.BaseModel;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Scrobble
+namespace Trakt.Api.DataContracts.Scrobble;
+
+public class TraktScrobbleMovie
{
- public class TraktScrobbleMovie
- {
- public TraktMovie movie { get; set; }
+ [JsonPropertyName("movie")]
+ public TraktMovie Movie { get; set; }
- public float progress { get; set; }
+ [JsonPropertyName("progress")]
+ public float Progress { get; set; }
- public string app_version { get; set; }
+ [JsonPropertyName("app_version")]
+ public string AppVersion { get; set; }
- public string app_date { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("app_date")]
+ public string AppDate { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Scrobble/TraktScrobbleResponse.cs b/Trakt/Api/DataContracts/Scrobble/TraktScrobbleResponse.cs
index 8a2e02b..48f1022 100644
--- a/Trakt/Api/DataContracts/Scrobble/TraktScrobbleResponse.cs
+++ b/Trakt/Api/DataContracts/Scrobble/TraktScrobbleResponse.cs
@@ -1,28 +1,25 @@
-using Trakt.Api.DataContracts.BaseModel;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Scrobble
+namespace Trakt.Api.DataContracts.Scrobble;
+
+public class TraktScrobbleResponse
{
- public class TraktScrobbleResponse
- {
- public string action { get; set; }
+ [JsonPropertyName("action")]
+ public string Action { get; set; }
- public float progress { get; set; }
+ [JsonPropertyName("progress")]
+ public float Progress { get; set; }
- public SocialMedia sharing { get; set; }
+ [JsonPropertyName("sharing")]
+ public SocialMedia Sharing { get; set; }
- public class SocialMedia
- {
- public bool facebook { get; set; }
+ [JsonPropertyName("movie")]
+ public TraktMovie Movie { get; set; }
- public bool twitter { get; set; }
+ [JsonPropertyName("episode")]
+ public TraktEpisode Episode { get; set; }
- public bool tumblr { get; set; }
- }
-
- public TraktMovie movie { get; set; }
-
- public TraktEpisode episode { get; set; }
-
- public TraktShow show { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("show")]
+ public TraktShow Show { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Collection/TraktEpisodeCollected.cs b/Trakt/Api/DataContracts/Sync/Collection/TraktEpisodeCollected.cs
index 6301b13..38a7138 100644
--- a/Trakt/Api/DataContracts/Sync/Collection/TraktEpisodeCollected.cs
+++ b/Trakt/Api/DataContracts/Sync/Collection/TraktEpisodeCollected.cs
@@ -1,19 +1,24 @@
-using Trakt.Api.DataContracts.BaseModel;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Collection
+namespace Trakt.Api.DataContracts.Sync.Collection;
+
+public class TraktEpisodeCollected : TraktEpisode
{
- public class TraktEpisodeCollected : TraktEpisode
- {
- public string collected_at { get; set; }
+ [JsonPropertyName("collected_at")]
+ public string CollectedAt { get; set; }
- public string media_type { get; set; }
+ [JsonPropertyName("media_type")]
+ public string MediaType { get; set; }
- public string resolution { get; set; }
+ [JsonPropertyName("resolution")]
+ public string Resolution { get; set; }
- public string audio { get; set; }
+ [JsonPropertyName("audio")]
+ public string Audio { get; set; }
- public string audio_channels { get; set; }
+ [JsonPropertyName("audio_channels")]
+ public string AudioChannels { get; set; }
- //public bool 3d { get; set; }
- }
-}
\ No newline at end of file
+ // public bool 3d { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Collection/TraktMovieCollected.cs b/Trakt/Api/DataContracts/Sync/Collection/TraktMovieCollected.cs
index b4ce621..93d8366 100644
--- a/Trakt/Api/DataContracts/Sync/Collection/TraktMovieCollected.cs
+++ b/Trakt/Api/DataContracts/Sync/Collection/TraktMovieCollected.cs
@@ -1,19 +1,24 @@
-using Trakt.Api.DataContracts.BaseModel;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Collection
+namespace Trakt.Api.DataContracts.Sync.Collection;
+
+public class TraktMovieCollected : TraktMovie
{
- public class TraktMovieCollected : TraktMovie
- {
- public string collected_at { get; set; }
+ [JsonPropertyName("collected_at")]
+ public string CollectedAt { get; set; }
- public string media_type { get; set; }
+ [JsonPropertyName("media_type")]
+ public string MediaType { get; set; }
- public string resolution { get; set; }
+ [JsonPropertyName("resolution")]
+ public string Resolution { get; set; }
- public string audio { get; set; }
+ [JsonPropertyName("audio")]
+ public string Audio { get; set; }
- public string audio_channels { get; set; }
+ [JsonPropertyName("audio_channels")]
+ public string AudioChannels { get; set; }
- //public bool 3d { get; set; }
- }
-}
\ No newline at end of file
+ // public bool 3d { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Collection/TraktSeasonCollected.cs b/Trakt/Api/DataContracts/Sync/Collection/TraktSeasonCollected.cs
new file mode 100644
index 0000000..f377b0c
--- /dev/null
+++ b/Trakt/Api/DataContracts/Sync/Collection/TraktSeasonCollected.cs
@@ -0,0 +1,16 @@
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.Sync.Collection;
+
+public class TraktSeasonCollected
+{
+ [JsonPropertyName("number")]
+ public int Number { get; set; }
+
+ [JsonPropertyName("episodes")]
+ public List Episodes { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Collection/TraktShowCollected.cs b/Trakt/Api/DataContracts/Sync/Collection/TraktShowCollected.cs
index 7d75e7f..0d0476d 100644
--- a/Trakt/Api/DataContracts/Sync/Collection/TraktShowCollected.cs
+++ b/Trakt/Api/DataContracts/Sync/Collection/TraktShowCollected.cs
@@ -1,17 +1,14 @@
-using System.Collections.Generic;
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Collection
+namespace Trakt.Api.DataContracts.Sync.Collection;
+
+public class TraktShowCollected : TraktShow
{
- public class TraktShowCollected : TraktShow
- {
- public List seasons { get; set; }
-
- public class TraktSeasonCollected
- {
- public int number { get; set; }
-
- public List episodes { get; set; }
- }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("seasons")]
+ public List Seasons { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Items.cs b/Trakt/Api/DataContracts/Sync/Items.cs
new file mode 100644
index 0000000..e58a5f8
--- /dev/null
+++ b/Trakt/Api/DataContracts/Sync/Items.cs
@@ -0,0 +1,21 @@
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.Sync;
+
+public class Items
+{
+ [JsonPropertyName("movies")]
+ public int Movies { get; set; }
+
+ [JsonPropertyName("shows")]
+ public int Shows { get; set; }
+
+ [JsonPropertyName("seasons")]
+ public int Seasons { get; set; }
+
+ [JsonPropertyName("episodes")]
+ public int Episodes { get; set; }
+
+ [JsonPropertyName("people")]
+ public int People { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/NotFoundObjects.cs b/Trakt/Api/DataContracts/Sync/NotFoundObjects.cs
new file mode 100644
index 0000000..da53377
--- /dev/null
+++ b/Trakt/Api/DataContracts/Sync/NotFoundObjects.cs
@@ -0,0 +1,26 @@
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
+
+namespace Trakt.Api.DataContracts.Sync;
+
+public class NotFoundObjects
+{
+ [JsonPropertyName("movies")]
+ public List Movies { get; set; }
+
+ [JsonPropertyName("shows")]
+ public List Shows { get; set; }
+
+ [JsonPropertyName("episodes")]
+ public List Episodes { get; set; }
+
+ [JsonPropertyName("seasons")]
+ public List Seasons { get; set; }
+
+ [JsonPropertyName("people")]
+ public List People { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Ratings/TraktEpisodeRated.cs b/Trakt/Api/DataContracts/Sync/Ratings/TraktEpisodeRated.cs
index b3ec287..c01faaa 100644
--- a/Trakt/Api/DataContracts/Sync/Ratings/TraktEpisodeRated.cs
+++ b/Trakt/Api/DataContracts/Sync/Ratings/TraktEpisodeRated.cs
@@ -1,11 +1,13 @@
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Ratings
-{
- public class TraktEpisodeRated : TraktRated
- {
- public int? number { get; set; }
+namespace Trakt.Api.DataContracts.Sync.Ratings;
- public TraktEpisodeId ids { get; set; }
- }
-}
\ No newline at end of file
+public class TraktEpisodeRated : TraktRated
+{
+ [JsonPropertyName("number")]
+ public int? Number { get; set; }
+
+ [JsonPropertyName("ids")]
+ public TraktEpisodeId Ids { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Ratings/TraktMovieRated.cs b/Trakt/Api/DataContracts/Sync/Ratings/TraktMovieRated.cs
index 5b94371..94fc380 100644
--- a/Trakt/Api/DataContracts/Sync/Ratings/TraktMovieRated.cs
+++ b/Trakt/Api/DataContracts/Sync/Ratings/TraktMovieRated.cs
@@ -1,13 +1,16 @@
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Ratings
+namespace Trakt.Api.DataContracts.Sync.Ratings;
+
+public class TraktMovieRated : TraktRated
{
- public class TraktMovieRated : TraktRated
- {
- public string title { get; set; }
+ [JsonPropertyName("title")]
+ public string Title { get; set; }
- public int? year { get; set; }
+ [JsonPropertyName("year")]
+ public int? Year { get; set; }
- public TraktMovieId ids { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("ids")]
+ public TraktMovieId Ids { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Ratings/TraktSeasonRated.cs b/Trakt/Api/DataContracts/Sync/Ratings/TraktSeasonRated.cs
new file mode 100644
index 0000000..4c42cad
--- /dev/null
+++ b/Trakt/Api/DataContracts/Sync/Ratings/TraktSeasonRated.cs
@@ -0,0 +1,17 @@
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
+
+namespace Trakt.Api.DataContracts.Sync.Ratings;
+
+public class TraktSeasonRated : TraktRated
+{
+ [JsonPropertyName("number")]
+ public int? Number { get; set; }
+
+ [JsonPropertyName("episodes")]
+ public List Episodes { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Ratings/TraktShowRated.cs b/Trakt/Api/DataContracts/Sync/Ratings/TraktShowRated.cs
index 61e1e5b..0e1c0d6 100644
--- a/Trakt/Api/DataContracts/Sync/Ratings/TraktShowRated.cs
+++ b/Trakt/Api/DataContracts/Sync/Ratings/TraktShowRated.cs
@@ -1,23 +1,23 @@
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
using System.Collections.Generic;
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Ratings
+namespace Trakt.Api.DataContracts.Sync.Ratings;
+
+public class TraktShowRated : TraktRated
{
- public class TraktShowRated : TraktRated
- {
- public string title { get; set; }
+ [JsonPropertyName("title")]
+ public string Title { get; set; }
- public int? year { get; set; }
+ [JsonPropertyName("year")]
+ public int? Year { get; set; }
- public TraktShowId ids { get; set; }
+ [JsonPropertyName("ids")]
+ public TraktShowId Ids { get; set; }
- public List seasons { get; set; }
-
- public class TraktSeasonRated : TraktRated
- {
- public int? number { get; set; }
-
- public List episodes { get; set; }
- }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("seasons")]
+ public List Seasons { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/TraktSync.cs b/Trakt/Api/DataContracts/Sync/TraktSync.cs
index da8eda5..3b34d25 100644
--- a/Trakt/Api/DataContracts/Sync/TraktSync.cs
+++ b/Trakt/Api/DataContracts/Sync/TraktSync.cs
@@ -1,28 +1,19 @@
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
using System.Collections.Generic;
-using Trakt.Api.DataContracts.Sync.Collection;
-using Trakt.Api.DataContracts.Sync.Ratings;
-using Trakt.Api.DataContracts.Sync.Watched;
+using System.Text.Json.Serialization;
-namespace Trakt.Api.DataContracts.Sync
+namespace Trakt.Api.DataContracts.Sync;
+
+public class TraktSync
{
- public class TraktSync
- {
- public List movies { get; set; }
+ [JsonPropertyName("movies")]
+ public List Movies { get; set; }
- public List shows { get; set; }
+ [JsonPropertyName("shows")]
+ public List Shows { get; set; }
- public List episodes { get; set; }
- }
-
- public class TraktSyncRated : TraktSync
- {
- }
-
- public class TraktSyncWatched : TraktSync
- {
- }
-
- public class TraktSyncCollected : TraktSync
- {
- }
-}
\ No newline at end of file
+ [JsonPropertyName("episodes")]
+ public List Episodes { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/TraktSyncCollected.cs b/Trakt/Api/DataContracts/Sync/TraktSyncCollected.cs
new file mode 100644
index 0000000..650026f
--- /dev/null
+++ b/Trakt/Api/DataContracts/Sync/TraktSyncCollected.cs
@@ -0,0 +1,7 @@
+using Trakt.Api.DataContracts.Sync.Collection;
+
+namespace Trakt.Api.DataContracts.Sync;
+
+public class TraktSyncCollected : TraktSync
+{
+}
diff --git a/Trakt/Api/DataContracts/Sync/TraktSyncRated.cs b/Trakt/Api/DataContracts/Sync/TraktSyncRated.cs
new file mode 100644
index 0000000..5b70589
--- /dev/null
+++ b/Trakt/Api/DataContracts/Sync/TraktSyncRated.cs
@@ -0,0 +1,7 @@
+using Trakt.Api.DataContracts.Sync.Ratings;
+
+namespace Trakt.Api.DataContracts.Sync;
+
+public class TraktSyncRated : TraktSync
+{
+}
diff --git a/Trakt/Api/DataContracts/Sync/TraktSyncResponse.cs b/Trakt/Api/DataContracts/Sync/TraktSyncResponse.cs
index 8c2bc6c..c541d40 100644
--- a/Trakt/Api/DataContracts/Sync/TraktSyncResponse.cs
+++ b/Trakt/Api/DataContracts/Sync/TraktSyncResponse.cs
@@ -1,42 +1,18 @@
-using System.Collections.Generic;
-using Trakt.Api.DataContracts.BaseModel;
+using System.Text.Json.Serialization;
-namespace Trakt.Api.DataContracts.Sync
+namespace Trakt.Api.DataContracts.Sync;
+
+public class TraktSyncResponse
{
- public class TraktSyncResponse
- {
- public Items added { get; set; }
+ [JsonPropertyName("added")]
+ public Items Added { get; set; }
- public Items deleted { get; set; }
+ [JsonPropertyName("deleted")]
+ public Items Deleted { get; set; }
- public Items existing { get; set; }
+ [JsonPropertyName("existing")]
+ public Items Existing { get; set; }
- public class Items
- {
- public int movies { get; set; }
-
- public int shows { get; set; }
-
- public int seasons { get; set; }
-
- public int episodes { get; set; }
-
- public int people { get; set; }
- }
-
- public NotFoundObjects not_found { get; set; }
-
- public class NotFoundObjects
- {
- public List movies { get; set; }
-
- public List shows { get; set; }
-
- public List episodes { get; set; }
-
- public List seasons { get; set; }
-
- public List people { get; set; }
- }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("not_found")]
+ public NotFoundObjects NotFound { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/TraktSyncWatched.cs b/Trakt/Api/DataContracts/Sync/TraktSyncWatched.cs
new file mode 100644
index 0000000..e433350
--- /dev/null
+++ b/Trakt/Api/DataContracts/Sync/TraktSyncWatched.cs
@@ -0,0 +1,7 @@
+using Trakt.Api.DataContracts.Sync.Watched;
+
+namespace Trakt.Api.DataContracts.Sync;
+
+public class TraktSyncWatched : TraktSync
+{
+}
diff --git a/Trakt/Api/DataContracts/Sync/Watched/TraktEpisodeWatched.cs b/Trakt/Api/DataContracts/Sync/Watched/TraktEpisodeWatched.cs
index df1c13f..998f828 100644
--- a/Trakt/Api/DataContracts/Sync/Watched/TraktEpisodeWatched.cs
+++ b/Trakt/Api/DataContracts/Sync/Watched/TraktEpisodeWatched.cs
@@ -1,9 +1,10 @@
-using Trakt.Api.DataContracts.BaseModel;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Watched
+namespace Trakt.Api.DataContracts.Sync.Watched;
+
+public class TraktEpisodeWatched : TraktEpisode
{
- public class TraktEpisodeWatched : TraktEpisode
- {
- public string watched_at { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("watched_at")]
+ public string WatchedAt { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Watched/TraktMovieWatched.cs b/Trakt/Api/DataContracts/Sync/Watched/TraktMovieWatched.cs
index 888f113..23c4d76 100644
--- a/Trakt/Api/DataContracts/Sync/Watched/TraktMovieWatched.cs
+++ b/Trakt/Api/DataContracts/Sync/Watched/TraktMovieWatched.cs
@@ -1,9 +1,10 @@
-using Trakt.Api.DataContracts.BaseModel;
+using System.Text.Json.Serialization;
+using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Watched
+namespace Trakt.Api.DataContracts.Sync.Watched;
+
+public class TraktMovieWatched : TraktMovie
{
- public class TraktMovieWatched : TraktMovie
- {
- public string watched_at { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("watched_at")]
+ public string WatchedAt { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Sync/Watched/TraktSeasonWatched.cs b/Trakt/Api/DataContracts/Sync/Watched/TraktSeasonWatched.cs
index be1ee04..b0b1dc3 100644
--- a/Trakt/Api/DataContracts/Sync/Watched/TraktSeasonWatched.cs
+++ b/Trakt/Api/DataContracts/Sync/Watched/TraktSeasonWatched.cs
@@ -1,12 +1,17 @@
-using System.Collections.Generic;
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Watched
-{
- public class TraktSeasonWatched : TraktSeason
- {
- public string watched_at { get; set; }
+namespace Trakt.Api.DataContracts.Sync.Watched;
- public List episodes { get; set; }
- }
+public class TraktSeasonWatched : TraktSeason
+{
+ [JsonPropertyName("watched_at")]
+ public string WatchedAt { get; set; }
+
+ [JsonPropertyName("episodes")]
+ public List Episodes { get; set; }
}
diff --git a/Trakt/Api/DataContracts/Sync/Watched/TraktShowWatched.cs b/Trakt/Api/DataContracts/Sync/Watched/TraktShowWatched.cs
index 232fd5e..432e9ca 100644
--- a/Trakt/Api/DataContracts/Sync/Watched/TraktShowWatched.cs
+++ b/Trakt/Api/DataContracts/Sync/Watched/TraktShowWatched.cs
@@ -1,12 +1,17 @@
-using System.Collections.Generic;
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Sync.Watched
-{
- public class TraktShowWatched : TraktShow
- {
- public string watched_at { get; set; }
+namespace Trakt.Api.DataContracts.Sync.Watched;
- public List seasons { get; set; }
- }
-}
\ No newline at end of file
+public class TraktShowWatched : TraktShow
+{
+ [JsonPropertyName("watched_at")]
+ public string WatchedAt { get; set; }
+
+ [JsonPropertyName("seasons")]
+ public List Seasons { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/TraktDeviceCode.cs b/Trakt/Api/DataContracts/TraktDeviceCode.cs
index a6dfc09..5eeafb6 100644
--- a/Trakt/Api/DataContracts/TraktDeviceCode.cs
+++ b/Trakt/Api/DataContracts/TraktDeviceCode.cs
@@ -1,11 +1,21 @@
-namespace Trakt.Api.DataContracts
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts;
+
+public class TraktDeviceCode
{
- public class TraktDeviceCode
- {
- public string device_code { get; set; }
- public string user_code { get; set; }
- public string verification_url { get; set; }
- public int expires_in { get; set; }
- public int interval { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("device_code")]
+ public string DeviceCode { get; set; }
+
+ [JsonPropertyName("user_code")]
+ public string UserCode { get; set; }
+
+ [JsonPropertyName("verification_url")]
+ public string VerificationUrl { get; set; }
+
+ [JsonPropertyName("expires_in")]
+ public int ExpiresIn { get; set; }
+
+ [JsonPropertyName("interval")]
+ public int Interval { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/TraktUserAccessToken.cs b/Trakt/Api/DataContracts/TraktUserAccessToken.cs
index 07a2120..47bad36 100644
--- a/Trakt/Api/DataContracts/TraktUserAccessToken.cs
+++ b/Trakt/Api/DataContracts/TraktUserAccessToken.cs
@@ -1,16 +1,29 @@
-namespace Trakt.Api.DataContracts
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts;
+
+public class TraktUserAccessToken
{
- public class TraktUserAccessToken
- {
- 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 int created_at { get; set; }
-
- // Expiration can be a bit of a problem with Trakt. It's usually 90 days, but it's unclear when the
- // refresh_token expires, so leave a little buffer for expiration...
- public int expirationWithBuffer => expires_in * 3 / 4;
- }
-}
\ No newline at end of file
+ [JsonPropertyName("access_token")]
+ public string AccessToken { get; set; }
+
+ [JsonPropertyName("token_type")]
+ public string TokenType { get; set; }
+
+ [JsonPropertyName("expires_in")]
+ public int ExpiresIn { get; set; }
+
+ [JsonPropertyName("refresh_token")]
+ public string RefreshToken { get; set; }
+
+ [JsonPropertyName("scope")]
+ public string Scope { get; set; }
+
+ [JsonPropertyName("created_at")]
+ public int CreatedAt { get; set; }
+
+ // Expiration can be a bit of a problem with Trakt. It's usually 90 days, but it's unclear when the
+ // refresh_token expires, so leave a little buffer for expiration...
+ [JsonPropertyName("expirationWithBuffer")]
+ public int ExpirationWithBuffer => ExpiresIn * 3 / 4;
+}
diff --git a/Trakt/Api/DataContracts/TraktUserRefreshTokenRequest.cs b/Trakt/Api/DataContracts/TraktUserRefreshTokenRequest.cs
index cf7e8a2..fdcf600 100644
--- a/Trakt/Api/DataContracts/TraktUserRefreshTokenRequest.cs
+++ b/Trakt/Api/DataContracts/TraktUserRefreshTokenRequest.cs
@@ -1,11 +1,21 @@
-namespace Trakt.Api.DataContracts
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts;
+
+public class TraktUserRefreshTokenRequest
{
- public class TraktUserRefreshTokenRequest
- {
- public string refresh_token { get; set; }
- public string client_id { get; set; }
- public string client_secret { get; set; }
- public string redirect_uri { get; set; }
- public string grant_type { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("refresh_token")]
+ public string RefreshToken { get; set; }
+
+ [JsonPropertyName("client_id")]
+ public string ClientId { get; set; }
+
+ [JsonPropertyName("client_secret")]
+ public string ClientSecret { get; set; }
+
+ [JsonPropertyName("redirect_uri")]
+ public string RedirectUri { get; set; }
+
+ [JsonPropertyName("grant_type")]
+ public string GrantType { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Collection/TraktEpisodeCollected.cs b/Trakt/Api/DataContracts/Users/Collection/TraktEpisodeCollected.cs
new file mode 100644
index 0000000..e35d9d4
--- /dev/null
+++ b/Trakt/Api/DataContracts/Users/Collection/TraktEpisodeCollected.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.Users.Collection;
+
+public class TraktEpisodeCollected
+{
+ [JsonPropertyName("number")]
+ public int Number { get; set; }
+
+ [JsonPropertyName("collected_at")]
+ public string CollectedAt { get; set; }
+
+ [JsonPropertyName("metadata")]
+ public TraktMetadata Metadata { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Collection/TraktMetadata.cs b/Trakt/Api/DataContracts/Users/Collection/TraktMetadata.cs
index 3860739..db65d4b 100644
--- a/Trakt/Api/DataContracts/Users/Collection/TraktMetadata.cs
+++ b/Trakt/Api/DataContracts/Users/Collection/TraktMetadata.cs
@@ -1,17 +1,20 @@
-
+using System.Text.Json.Serialization;
-namespace Trakt.Api.DataContracts.Users.Collection
+namespace Trakt.Api.DataContracts.Users.Collection;
+
+public class TraktMetadata
{
- public class TraktMetadata
- {
- public string media_type { get; set; }
+ [JsonPropertyName("media_type")]
+ public string MediaType { get; set; }
- public string resolution { get; set; }
+ [JsonPropertyName("resolution")]
+ public string Resolution { get; set; }
- public string audio { get; set; }
+ [JsonPropertyName("audio")]
+ public string Audio { get; set; }
- public string audio_channels { get; set; }
+ [JsonPropertyName("audio_channels")]
+ public string AudioChannels { get; set; }
- //public bool 3d { get; set; }
- }
-}
\ No newline at end of file
+ // public bool 3d { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Collection/TraktMovieCollected.cs b/Trakt/Api/DataContracts/Users/Collection/TraktMovieCollected.cs
index b06cc2c..50e19c2 100644
--- a/Trakt/Api/DataContracts/Users/Collection/TraktMovieCollected.cs
+++ b/Trakt/Api/DataContracts/Users/Collection/TraktMovieCollected.cs
@@ -1,14 +1,16 @@
-
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Users.Collection
+namespace Trakt.Api.DataContracts.Users.Collection;
+
+public class TraktMovieCollected
{
- public class TraktMovieCollected
- {
- public string collected_at { get; set; }
+ [JsonPropertyName("collected_at")]
+ public string CollectedAt { get; set; }
- public TraktMetadata metadata { get; set; }
+ [JsonPropertyName("metadata")]
+ public TraktMetadata Metadata { get; set; }
- public TraktMovie movie { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("movie")]
+ public TraktMovie Movie { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Collection/TraktSeasonCollected.cs b/Trakt/Api/DataContracts/Users/Collection/TraktSeasonCollected.cs
new file mode 100644
index 0000000..b47a064
--- /dev/null
+++ b/Trakt/Api/DataContracts/Users/Collection/TraktSeasonCollected.cs
@@ -0,0 +1,16 @@
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.Users.Collection;
+
+public class TraktSeasonCollected
+{
+ [JsonPropertyName("number")]
+ public int Number { get; set; }
+
+ [JsonPropertyName("episodes")]
+ public List Episodes { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Collection/TraktShowCollected.cs b/Trakt/Api/DataContracts/Users/Collection/TraktShowCollected.cs
index 192e354..bb93f30 100644
--- a/Trakt/Api/DataContracts/Users/Collection/TraktShowCollected.cs
+++ b/Trakt/Api/DataContracts/Users/Collection/TraktShowCollected.cs
@@ -1,31 +1,20 @@
-using System.Collections.Generic;
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Users.Collection
+namespace Trakt.Api.DataContracts.Users.Collection;
+
+public class TraktShowCollected
{
- public class TraktShowCollected
- {
- public string last_collected_at { get; set; }
+ [JsonPropertyName("last_collected_at")]
+ public string LastCollectedAt { get; set; }
- public TraktShow show { get; set; }
+ [JsonPropertyName("show")]
+ public TraktShow Show { get; set; }
- public List seasons { get; set; }
-
- public class TraktSeasonCollected
- {
- public int number { get; set; }
-
- public List episodes { get; set; }
-
- public class TraktEpisodeCollected
- {
- public int number { get; set; }
-
- public string collected_at { get; set; }
-
- public TraktMetadata metadata { get; set; }
- }
- }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("seasons")]
+ public List Seasons { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Ratings/TraktEpisodeRated.cs b/Trakt/Api/DataContracts/Users/Ratings/TraktEpisodeRated.cs
index 9a4e5c5..9fe8014 100644
--- a/Trakt/Api/DataContracts/Users/Ratings/TraktEpisodeRated.cs
+++ b/Trakt/Api/DataContracts/Users/Ratings/TraktEpisodeRated.cs
@@ -1,9 +1,10 @@
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Users.Ratings
+namespace Trakt.Api.DataContracts.Users.Ratings;
+
+public class TraktEpisodeRated : TraktRated
{
- public class TraktEpisodeRated : TraktRated
- {
- public TraktEpisode episode { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("episode")]
+ public TraktEpisode Episode { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Ratings/TraktMovieRated.cs b/Trakt/Api/DataContracts/Users/Ratings/TraktMovieRated.cs
index 6dd37db..20433cc 100644
--- a/Trakt/Api/DataContracts/Users/Ratings/TraktMovieRated.cs
+++ b/Trakt/Api/DataContracts/Users/Ratings/TraktMovieRated.cs
@@ -1,9 +1,10 @@
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Users.Ratings
+namespace Trakt.Api.DataContracts.Users.Ratings;
+
+public class TraktMovieRated : TraktRated
{
- public class TraktMovieRated : TraktRated
- {
- public TraktMovie movie { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("movie")]
+ public TraktMovie Movie { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Ratings/TraktSeasonRated.cs b/Trakt/Api/DataContracts/Users/Ratings/TraktSeasonRated.cs
index c15e37a..3af4f42 100644
--- a/Trakt/Api/DataContracts/Users/Ratings/TraktSeasonRated.cs
+++ b/Trakt/Api/DataContracts/Users/Ratings/TraktSeasonRated.cs
@@ -1,9 +1,10 @@
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Users.Ratings
+namespace Trakt.Api.DataContracts.Users.Ratings;
+
+public class TraktSeasonRated : TraktRated
{
- public class TraktSeasonRated : TraktRated
- {
- public TraktSeason season { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("season")]
+ public TraktSeason Season { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Ratings/TraktShowRated.cs b/Trakt/Api/DataContracts/Users/Ratings/TraktShowRated.cs
index 2c79767..3f6975e 100644
--- a/Trakt/Api/DataContracts/Users/Ratings/TraktShowRated.cs
+++ b/Trakt/Api/DataContracts/Users/Ratings/TraktShowRated.cs
@@ -1,9 +1,10 @@
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Users.Ratings
+namespace Trakt.Api.DataContracts.Users.Ratings;
+
+public class TraktShowRated : TraktRated
{
- public class TraktShowRated : TraktRated
- {
- public TraktShow show { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("show")]
+ public TraktShow Show { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Watched/Episode.cs b/Trakt/Api/DataContracts/Users/Watched/Episode.cs
new file mode 100644
index 0000000..43f56ef
--- /dev/null
+++ b/Trakt/Api/DataContracts/Users/Watched/Episode.cs
@@ -0,0 +1,15 @@
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.Users.Watched;
+
+public class Episode
+{
+ [JsonPropertyName("last_watched_at")]
+ public string LastWatchedAt { get; set; }
+
+ [JsonPropertyName("number")]
+ public int Number { get; set; }
+
+ [JsonPropertyName("plays")]
+ public int Plays { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Watched/Season.cs b/Trakt/Api/DataContracts/Users/Watched/Season.cs
new file mode 100644
index 0000000..ede3e22
--- /dev/null
+++ b/Trakt/Api/DataContracts/Users/Watched/Season.cs
@@ -0,0 +1,16 @@
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
+
+namespace Trakt.Api.DataContracts.Users.Watched;
+
+public class Season
+{
+ [JsonPropertyName("number")]
+ public int Number { get; set; }
+
+ [JsonPropertyName("episodes")]
+ public List Episodes { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Watched/TraktMovieWatched.cs b/Trakt/Api/DataContracts/Users/Watched/TraktMovieWatched.cs
index 90b2b64..cb6f05e 100644
--- a/Trakt/Api/DataContracts/Users/Watched/TraktMovieWatched.cs
+++ b/Trakt/Api/DataContracts/Users/Watched/TraktMovieWatched.cs
@@ -1,14 +1,16 @@
-
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Users.Watched
+namespace Trakt.Api.DataContracts.Users.Watched;
+
+public class TraktMovieWatched
{
- public class TraktMovieWatched
- {
- public int plays { get; set; }
+ [JsonPropertyName("plays")]
+ public int Plays { get; set; }
- public string last_watched_at { get; set; }
+ [JsonPropertyName("last_watched_at")]
+ public string LastWatchedAt { get; set; }
- public TraktMovie movie { get; set; }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("movie")]
+ public TraktMovie Movie { get; set; }
+}
diff --git a/Trakt/Api/DataContracts/Users/Watched/TraktShowWatched.cs b/Trakt/Api/DataContracts/Users/Watched/TraktShowWatched.cs
index 2b6e20c..88fd5eb 100644
--- a/Trakt/Api/DataContracts/Users/Watched/TraktShowWatched.cs
+++ b/Trakt/Api/DataContracts/Users/Watched/TraktShowWatched.cs
@@ -1,35 +1,26 @@
-using System.Collections.Generic;
+#pragma warning disable CA2227
+#pragma warning disable CA1002
+using System.Collections.Generic;
+using System.Text.Json.Serialization;
using Trakt.Api.DataContracts.BaseModel;
-namespace Trakt.Api.DataContracts.Users.Watched
+namespace Trakt.Api.DataContracts.Users.Watched;
+
+public class TraktShowWatched
{
- public class TraktShowWatched
- {
- public int plays { get; set; }
+ [JsonPropertyName("plays")]
+ public int Plays { get; set; }
- public string last_watched_at { get; set; }
+ [JsonPropertyName("reset_at")]
+ public string ResetAt { get; set; }
- public string reset_at { get; set; }
+ [JsonPropertyName("last_watched_at")]
+ public string LastWatchedAt { get; set; }
- public TraktShow show { get; set; }
+ [JsonPropertyName("show")]
+ public TraktShow Show { get; set; }
- public List seasons { get; set; }
-
- public class Season
- {
- public int number { get; set; }
-
- public List episodes { get; set; }
-
- public class Episode
- {
- public string last_watched_at { get; set; }
-
- public int number { get; set; }
-
- public int plays { get; set; }
- }
- }
- }
-}
\ No newline at end of file
+ [JsonPropertyName("seasons")]
+ public List Seasons { get; set; }
+}
diff --git a/Trakt/Api/TraktApi.cs b/Trakt/Api/TraktApi.cs
index d80ce38..35b0300 100644
--- a/Trakt/Api/TraktApi.cs
+++ b/Trakt/Api/TraktApi.cs
@@ -1,13 +1,17 @@
-using System;
+#pragma warning disable CA1002
+
+using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
-using Microsoft.Net.Http.Headers;
+using System.Net.Mime;
+using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
+using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
@@ -17,10 +21,12 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
+using Microsoft.Net.Http.Headers;
using Trakt.Api.DataContracts;
using Trakt.Api.DataContracts.BaseModel;
using Trakt.Api.DataContracts.Scrobble;
using Trakt.Api.DataContracts.Sync;
+using Trakt.Api.DataContracts.Sync.Collection;
using Trakt.Api.DataContracts.Sync.Ratings;
using Trakt.Api.DataContracts.Sync.Watched;
using Trakt.Helpers;
@@ -28,146 +34,358 @@ using Trakt.Model;
using TraktEpisodeCollected = Trakt.Api.DataContracts.Sync.Collection.TraktEpisodeCollected;
using TraktMovieCollected = Trakt.Api.DataContracts.Sync.Collection.TraktMovieCollected;
using TraktShowCollected = Trakt.Api.DataContracts.Sync.Collection.TraktShowCollected;
-using System.Text.Json;
-using System.Net.Mime;
-using Jellyfin.Extensions.Json;
-namespace Trakt.Api
+namespace Trakt.Api;
+
+///
+///
+///
+public class TraktApi
{
- ///
- ///
- ///
- public class TraktApi
+ private static readonly SemaphoreSlim _traktResourcePool = new SemaphoreSlim(1, 1);
+
+ private readonly ILogger _logger;
+ private readonly IHttpClientFactory _httpClientFactory;
+ private readonly IServerApplicationHost _appHost;
+ private readonly IUserDataManager _userDataManager;
+ private readonly IFileSystem _fileSystem;
+ private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
+
+ public TraktApi(
+ ILogger logger,
+ IHttpClientFactory httpClientFactory,
+ IServerApplicationHost appHost,
+ IUserDataManager userDataManager,
+ IFileSystem fileSystem)
{
- private static readonly SemaphoreSlim _traktResourcePool = new SemaphoreSlim(1, 1);
+ _httpClientFactory = httpClientFactory;
+ _appHost = appHost;
+ _userDataManager = userDataManager;
+ _fileSystem = fileSystem;
+ _logger = logger;
+ }
- private readonly ILogger _logger;
- private readonly IHttpClientFactory _httpClientFactory;
- private readonly IServerApplicationHost _appHost;
- private readonly IUserDataManager _userDataManager;
- private readonly IFileSystem _fileSystem;
- private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
-
- public TraktApi(
- ILogger logger,
- IHttpClientFactory httpClientFactory,
- IServerApplicationHost appHost,
- IUserDataManager userDataManager,
- IFileSystem fileSystem)
+ ///
+ /// Checks whether it's possible/allowed to sync a for a .
+ ///
+ ///
+ /// Item to check.
+ ///
+ ///
+ /// The trakt user to check for.
+ ///
+ ///
+ /// indicates if it's possible/allowed to sync this item.
+ ///
+ public bool CanSync(BaseItem item, TraktUser traktUser)
+ {
+ if (item.Path == null || item.LocationType == LocationType.Virtual)
{
- _httpClientFactory = httpClientFactory;
- _appHost = appHost;
- _userDataManager = userDataManager;
- _fileSystem = fileSystem;
- _logger = logger;
- }
-
- ///
- /// Checks whether it's possible/allowed to sync a for a .
- ///
- ///
- /// Item to check.
- ///
- ///
- /// The trakt user to check for.
- ///
- ///
- /// indicates if it's possible/allowed to sync this item.
- ///
- public bool CanSync(BaseItem item, TraktUser traktUser)
- {
- if (item.Path == null || item.LocationType == LocationType.Virtual)
- {
- return false;
- }
-
- if (traktUser.LocationsExcluded != null && traktUser.LocationsExcluded.Any(s => _fileSystem.ContainsSubPath(s, item.Path)))
- {
- return false;
- }
-
- if (item is Movie movie)
- {
- return !string.IsNullOrEmpty(movie.GetProviderId(MetadataProvider.Imdb)) ||
- !string.IsNullOrEmpty(movie.GetProviderId(MetadataProvider.Tmdb));
- }
-
- if (item is Episode episode
- && episode.Series != null
- && !episode.IsMissingEpisode
- && (episode.IndexNumber.HasValue
- || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProvider.Imdb))
- || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProvider.Tmdb))
- || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProvider.Tvdb))
- || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProvider.TvRage))
- ))
- {
- var series = episode.Series;
-
- return !string.IsNullOrEmpty(series.GetProviderId(MetadataProvider.Imdb))
- || !string.IsNullOrEmpty(series.GetProviderId(MetadataProvider.Tmdb))
- || !string.IsNullOrEmpty(series.GetProviderId(MetadataProvider.TvRage))
- || !string.IsNullOrEmpty(series.GetProviderId(MetadataProvider.Tvdb));
- }
-
return false;
}
- ///
- /// Report to trakt.tv that a movie is being watched, or has been watched.
- ///
- /// The movie being watched/scrobbled
- /// MediaStatus enum dictating whether item is being watched or scrobbled
- /// The user that watching the current movie
- ///
- /// A standard TraktResponse Data Contract
- public async Task SendMovieStatusUpdateAsync(Movie movie, MediaStatus mediaStatus, TraktUser traktUser, float progressPercent)
+ if (traktUser.LocationsExcluded != null && traktUser.LocationsExcluded.Any(s => _fileSystem.ContainsSubPath(s, item.Path)))
{
- var movieData = new TraktScrobbleMovie
+ return false;
+ }
+
+ if (item is Movie movie)
+ {
+ return !string.IsNullOrEmpty(movie.GetProviderId(MetadataProvider.Imdb)) ||
+ !string.IsNullOrEmpty(movie.GetProviderId(MetadataProvider.Tmdb));
+ }
+
+ if (item is Episode episode
+ && episode.Series != null
+ && !episode.IsMissingEpisode
+ && (episode.IndexNumber.HasValue
+ || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProvider.Imdb))
+ || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProvider.Tmdb))
+ || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProvider.Tvdb))
+ || !string.IsNullOrEmpty(episode.GetProviderId(MetadataProvider.TvRage))
+ ))
+ {
+ var series = episode.Series;
+
+ return !string.IsNullOrEmpty(series.GetProviderId(MetadataProvider.Imdb))
+ || !string.IsNullOrEmpty(series.GetProviderId(MetadataProvider.Tmdb))
+ || !string.IsNullOrEmpty(series.GetProviderId(MetadataProvider.TvRage))
+ || !string.IsNullOrEmpty(series.GetProviderId(MetadataProvider.Tvdb));
+ }
+
+ return false;
+ }
+
+ ///
+ /// Report to trakt.tv that a movie is being watched, or has been watched.
+ ///
+ /// The movie being watched/scrobbled
+ /// MediaStatus enum dictating whether item is being watched or scrobbled
+ /// The user that watching the current movie
+ ///
+ /// A standard TraktResponse Data Contract
+ public async Task SendMovieStatusUpdateAsync(Movie movie, MediaStatus mediaStatus, TraktUser traktUser, float progressPercent)
+ {
+ var movieData = new TraktScrobbleMovie
+ {
+ AppDate = DateTimeOffset.Now.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
+ AppVersion = _appHost.ApplicationVersionString,
+ Progress = progressPercent,
+ Movie = new TraktMovie
{
- app_date = DateTimeOffset.Now.Date.ToString("yyyy-MM-dd"),
- app_version = _appHost.ApplicationVersionString,
- progress = progressPercent,
- movie = new TraktMovie
+ Title = movie.Name,
+ Year = movie.ProductionYear,
+ Ids = GetTraktIMDBTMDBIds(movie)
+ }
+ };
+
+ string url;
+ switch (mediaStatus)
+ {
+ case MediaStatus.Watching:
+ url = TraktUris.ScrobbleStart;
+ break;
+ case MediaStatus.Paused:
+ url = TraktUris.ScrobblePause;
+ break;
+ default:
+ url = TraktUris.ScrobbleStop;
+ break;
+ }
+
+ using (var response = await PostToTrakt(url, movieData, traktUser, CancellationToken.None).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Reports to trakt.tv that an episode is being watched. Or that Episode(s) have been watched.
+ ///
+ /// The episode being watched
+ /// Enum indicating whether an episode is being watched or scrobbled
+ /// The user that's watching the episode
+ ///
+ /// A List of standard TraktResponse Data Contracts
+ public async Task> SendEpisodeStatusUpdateAsync(Episode episode, MediaStatus status, TraktUser traktUser, float progressPercent)
+ {
+ var episodeDatas = new List();
+
+ var indexNumber = 0;
+ var finalNumber = 0;
+ if (episode.IndexNumber.HasValue)
+ {
+ indexNumber = episode.IndexNumber.Value;
+ finalNumber = (episode.IndexNumberEnd ?? episode.IndexNumber).Value;
+ }
+
+ var number = indexNumber;
+ var firstPass = true;
+ do
+ {
+ var scrobbleEpisode = new TraktScrobbleEpisode
+ {
+ AppDate = DateTimeOffset.Now.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
+ AppVersion = _appHost.ApplicationVersionString,
+ Progress = progressPercent,
+ Episode = new TraktEpisode
{
- title = movie.Name,
- year = movie.ProductionYear,
- ids = GetTraktIMDBTMDBIds(movie)
+ Season = episode.GetSeasonNumber()
+ },
+ Show = new TraktShow
+ {
+ Title = episode.Series.Name,
+ Year = episode.Series.ProductionYear,
+ Ids = GetTraktTvIds(episode.Series)
}
};
-
- string url;
- switch (mediaStatus)
+ if (episode.IndexNumber.HasValue)
{
- case MediaStatus.Watching:
- url = TraktUris.ScrobbleStart;
- break;
- case MediaStatus.Paused:
- url = TraktUris.ScrobblePause;
- break;
- default:
- url = TraktUris.ScrobbleStop;
- break;
+ scrobbleEpisode.Episode.Number = number;
}
- using (var response = await PostToTrakt(url, movieData, CancellationToken.None, traktUser).ConfigureAwait(false))
+ // provider IDs in multi-episode file will be for the first episode only
+ if (firstPass)
{
- return await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
+ // output provider IDs for first episode
+ scrobbleEpisode.Episode.Ids = GetTraktTvIds(episode);
+ firstPass = false;
+ }
+
+ episodeDatas.Add(scrobbleEpisode);
+
+ number++;
+ }
+ while (episode.IndexNumber.HasValue && number <= finalNumber);
+
+ string url;
+ switch (status)
+ {
+ case MediaStatus.Watching:
+ url = TraktUris.ScrobbleStart;
+ break;
+ case MediaStatus.Paused:
+ url = TraktUris.ScrobblePause;
+ break;
+ default:
+ url = TraktUris.ScrobbleStop;
+ break;
+ }
+
+ var responses = new List();
+ foreach (var traktScrobbleEpisode in episodeDatas)
+ {
+ using (var response = await PostToTrakt(url, traktScrobbleEpisode, traktUser, CancellationToken.None).ConfigureAwait(false))
+ {
+ responses.Add(await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false));
}
}
+ return responses;
+ }
- ///
- /// Reports to trakt.tv that an episode is being watched. Or that Episode(s) have been watched.
- ///
- /// The episode being watched
- /// Enum indicating whether an episode is being watched or scrobbled
- /// The user that's watching the episode
- ///
- /// A List of standard TraktResponse Data Contracts
- public async Task> SendEpisodeStatusUpdateAsync(Episode episode, MediaStatus status, TraktUser traktUser, float progressPercent)
+ ///
+ /// Add or remove a list of movies to/from the users trakt.tv library
+ ///
+ /// The movies to add
+ /// The user who's library is being updated
+ ///
+ /// The cancellation token.
+ /// Task{TraktResponseDataContract}.
+ public async Task> SendLibraryUpdateAsync(
+ IList movies,
+ TraktUser traktUser,
+ EventType eventType,
+ CancellationToken cancellationToken)
+ {
+ if (movies == null)
{
- var episodeDatas = new List();
+ throw new ArgumentNullException(nameof(movies));
+ }
+
+ if (traktUser == null)
+ {
+ throw new ArgumentNullException(nameof(traktUser));
+ }
+
+ if (eventType == EventType.Update)
+ {
+ return null;
+ }
+
+ var moviesPayload = movies.Select(m =>
+ {
+ var audioStream = m.GetMediaStreams().FirstOrDefault(x => x.Type == MediaStreamType.Audio);
+ var traktMovieCollected = new TraktMovieCollected
+ {
+ CollectedAt = m.DateCreated.ToISO8601(),
+ Title = m.Name,
+ Year = m.ProductionYear,
+ Ids = GetTraktIMDBTMDBIds(m)
+ };
+ if (traktUser.ExportMediaInfo)
+ {
+ traktMovieCollected.AudioChannels = audioStream.GetAudioChannels();
+ traktMovieCollected.Audio = audioStream.GetCodecRepresetation();
+ traktMovieCollected.Resolution = m.GetDefaultVideoStream().GetResolution();
+ }
+
+ return traktMovieCollected;
+ }).ToList();
+ var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove;
+
+ var responses = new List();
+ var chunks = moviesPayload.ToChunks(100);
+ foreach (var chunk in chunks)
+ {
+ var data = new TraktSyncCollected
+ {
+ Movies = chunk.ToList()
+ };
+ using (var response = await PostToTrakt(url, data, traktUser, cancellationToken).ConfigureAwait(false))
+ {
+ responses.Add(await JsonSerializer.DeserializeAsync(response, _jsonOptions, cancellationToken).ConfigureAwait(false));
+ }
+ }
+
+ return responses;
+ }
+
+ ///
+ /// Add or remove a list of Episodes to/from the users trakt.tv library
+ ///
+ /// The episodes to add
+ /// The user who's library is being updated
+ ///
+ /// The cancellation token.
+ /// Task{TraktResponseDataContract}.
+ public async Task> SendLibraryUpdateAsync(
+ IReadOnlyList episodes,
+ TraktUser traktUser,
+ EventType eventType,
+ CancellationToken cancellationToken)
+ {
+ if (episodes == null)
+ {
+ throw new ArgumentNullException(nameof(episodes));
+ }
+
+ if (traktUser == null)
+ {
+ throw new ArgumentNullException(nameof(traktUser));
+ }
+
+ if (eventType == EventType.Update)
+ {
+ return null;
+ }
+
+ var responses = new List();
+ var chunks = episodes.ToChunks(100);
+ foreach (var chunk in chunks)
+ {
+ responses.Add(await SendLibraryUpdateInternalAsync(chunk.ToList(), traktUser, eventType, cancellationToken).ConfigureAwait(false));
+ }
+
+ return responses;
+ }
+
+ private async Task SendLibraryUpdateInternalAsync(
+ IEnumerable episodes,
+ TraktUser traktUser,
+ EventType eventType,
+ CancellationToken cancellationToken)
+ {
+ var episodesPayload = new List();
+ var showPayload = new List();
+ foreach (Episode episode in episodes)
+ {
+ var audioStream = episode.GetMediaStreams().FirstOrDefault(x => x.Type == MediaStreamType.Audio);
+
+ var syncShow = FindShow(showPayload, episode.Series);
+ if (syncShow == null)
+ {
+ syncShow = new TraktShowCollected
+ {
+ Ids = GetTraktTvIds(episode.Series),
+ Seasons = new List()
+ };
+
+ showPayload.Add(syncShow);
+ }
+
+ var syncSeason =
+ syncShow.Seasons.FirstOrDefault(ss => ss.Number == episode.GetSeasonNumber());
+ if (syncSeason == null)
+ {
+ syncSeason = new TraktSeasonCollected
+ {
+ Number = episode.GetSeasonNumber(),
+ Episodes = new List()
+ };
+
+ syncShow.Seasons.Add(syncSeason);
+ }
var indexNumber = 0;
var finalNumber = 0;
@@ -181,925 +399,716 @@ namespace Trakt.Api
var firstPass = true;
do
{
- var scrobbleEpisode = new TraktScrobbleEpisode
+ var traktEpisodeCollected = new TraktEpisodeCollected
{
- app_date = DateTimeOffset.Now.Date.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
- app_version = _appHost.ApplicationVersionString,
- progress = progressPercent,
- episode = new TraktEpisode
- {
- season = episode.GetSeasonNumber()
- },
- show = new TraktShow
- {
- title = episode.Series.Name,
- year = episode.Series.ProductionYear,
- ids = GetTraktTVIds(episode.Series)
- }
+ CollectedAt = episode.DateCreated.ToISO8601()
};
- if (episode.IndexNumber.HasValue)
+
+ if (episode.IndexNumber.HasValue)
{
- scrobbleEpisode.episode.number = number;
+ traktEpisodeCollected.Number = number;
}
- //provider IDs in multi-episode file will be for the first episode only
+
if (firstPass)
{
- //output provider IDs for first episode
- scrobbleEpisode.episode.ids = GetTraktTVIds(episode);
+ // Omit this from the rest because then we end up attaching the provider IDs of the first episode to the subsequent ones
+ traktEpisodeCollected.Ids = GetTraktTvIds(episode);
firstPass = false;
}
- episodeDatas.Add(scrobbleEpisode);
- number++;
- } while (episode.IndexNumber.HasValue && number <= finalNumber);
-
-
- string url;
- switch (status)
- {
- case MediaStatus.Watching:
- url = TraktUris.ScrobbleStart;
- break;
- case MediaStatus.Paused:
- url = TraktUris.ScrobblePause;
- break;
- default:
- url = TraktUris.ScrobbleStop;
- break;
- }
- var responses = new List();
- foreach (var traktScrobbleEpisode in episodeDatas)
- {
- using (var response = await PostToTrakt(url, traktScrobbleEpisode, CancellationToken.None, traktUser).ConfigureAwait(false))
- {
- responses.Add(await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false));
- }
- }
- return responses;
- }
-
- ///
- /// Add or remove a list of movies to/from the users trakt.tv library
- ///
- /// The movies to add
- /// The user who's library is being updated
- /// The cancellation token.
- ///
- /// Task{TraktResponseDataContract}.
- public async Task> SendLibraryUpdateAsync(
- IList movies,
- TraktUser traktUser,
- CancellationToken cancellationToken,
- EventType eventType)
- {
- if (movies == null)
- {
- throw new ArgumentNullException(nameof(movies));
- }
-
- if (traktUser == null)
- {
- throw new ArgumentNullException(nameof(traktUser));
- }
-
- if (eventType == EventType.Update)
- {
- return null;
- }
-
- var moviesPayload = movies.Select(m =>
- {
- var audioStream = m.GetMediaStreams().FirstOrDefault(x => x.Type == MediaStreamType.Audio);
- var traktMovieCollected = new TraktMovieCollected
- {
- collected_at = m.DateCreated.ToISO8601(),
- title = m.Name,
- year = m.ProductionYear,
- ids = GetTraktIMDBTMDBIds(m)
- };
if (traktUser.ExportMediaInfo)
{
- traktMovieCollected.audio_channels = audioStream.GetAudioChannels();
- traktMovieCollected.audio = audioStream.GetCodecRepresetation();
- traktMovieCollected.resolution = m.GetDefaultVideoStream().GetResolution();
+ // traktEpisodeCollected.Is3D = episode.Is3D;
+ traktEpisodeCollected.AudioChannels = audioStream.GetAudioChannels();
+ traktEpisodeCollected.Audio = audioStream.GetCodecRepresetation();
+ traktEpisodeCollected.Resolution = episode.GetDefaultVideoStream().GetResolution();
}
- return traktMovieCollected;
- }).ToList();
- var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove;
+ syncSeason.Episodes.Add(traktEpisodeCollected);
- var responses = new List();
- var chunks = moviesPayload.ToChunks(100);
- foreach (var chunk in chunks)
- {
- var data = new TraktSyncCollected
- {
- movies = chunk.ToList()
- };
- using (var response = await PostToTrakt(url, data, cancellationToken, traktUser).ConfigureAwait(false))
- {
- responses.Add(await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false));
- }
+ number++;
}
-
- return responses;
+ while (episode.IndexNumber.HasValue && number <= finalNumber);
}
- ///
- /// Add or remove a list of Episodes to/from the users trakt.tv library
- ///
- /// The episodes to add
- /// The user who's library is being updated
- /// The cancellation token.
- ///
- /// Task{TraktResponseDataContract}.
- public async Task> SendLibraryUpdateAsync(
- IReadOnlyList episodes,
- TraktUser traktUser,
- CancellationToken cancellationToken,
- EventType eventType)
+ var data = new TraktSyncCollected
{
- if (episodes == null)
- {
- throw new ArgumentNullException(nameof(episodes));
- }
+ Episodes = episodesPayload.ToList(),
+ Shows = showPayload.ToList()
+ };
- if (traktUser == null)
- {
- throw new ArgumentNullException(nameof(traktUser));
- }
+ var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove;
+ using (var response = await PostToTrakt(url, data, traktUser, cancellationToken).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ }
+ }
- if (eventType == EventType.Update)
- {
- return null;
- }
-
- var responses = new List();
- var chunks = episodes.ToChunks(100);
- foreach (var chunk in chunks)
- {
- responses.Add(await SendLibraryUpdateInternalAsync(chunk.ToList(), traktUser, cancellationToken, eventType).ConfigureAwait(false));
- }
- return responses;
+ ///
+ /// Add or remove a Show(Series) to/from the users trakt.tv library
+ ///
+ /// The show to remove
+ /// The user who's library is being updated
+ ///
+ /// The cancellation token.
+ /// Task{TraktResponseDataContract}.
+ public async Task SendLibraryUpdateAsync(
+ Series show,
+ TraktUser traktUser,
+ EventType eventType,
+ CancellationToken cancellationToken)
+ {
+ if (show == null)
+ {
+ throw new ArgumentNullException(nameof(show));
}
- private async Task SendLibraryUpdateInternalAsync(
- IEnumerable episodes,
- TraktUser traktUser,
- CancellationToken cancellationToken,
- EventType eventType)
+ if (traktUser == null)
{
- var episodesPayload = new List();
- var showPayload = new List();
- foreach (Episode episode in episodes)
- {
- var audioStream = episode.GetMediaStreams().FirstOrDefault(x => x.Type == MediaStreamType.Audio);
-
- var syncShow = findShow(showPayload, episode.Series);
- if (syncShow == null)
- {
- syncShow = new TraktShowCollected
- {
- ids = GetTraktTVIds(episode.Series),
- seasons = new List()
- };
-
- showPayload.Add(syncShow);
- }
- var syncSeason =
- syncShow.seasons.FirstOrDefault(ss => ss.number == episode.GetSeasonNumber());
- if (syncSeason == null)
- {
- syncSeason = new TraktShowCollected.TraktSeasonCollected
- {
- number = episode.GetSeasonNumber(),
- episodes = new List()
- };
-
- syncShow.seasons.Add(syncSeason);
- }
-
- var indexNumber = 0;
- var finalNumber = 0;
- if (episode.IndexNumber.HasValue)
- {
- indexNumber = episode.IndexNumber.Value;
- finalNumber = (episode.IndexNumberEnd ?? episode.IndexNumber).Value;
- }
-
- var number = indexNumber;
- var firstPass = true;
- do
- {
- var traktEpisodeCollected = new TraktEpisodeCollected
- {
- collected_at = episode.DateCreated.ToISO8601()
- };
- if (episode.IndexNumber.HasValue)
- {
- traktEpisodeCollected.number = number;
- }
- if (firstPass)
- {
- // Omit this from the rest because then we end up attaching the provider IDs of the first episode to the subsequent ones
- traktEpisodeCollected.ids = GetTraktTVIds(episode);
- firstPass = false;
- }
- if (traktUser.ExportMediaInfo)
- {
- //traktEpisodeCollected.Is3D = episode.Is3D;
- traktEpisodeCollected.audio_channels = audioStream.GetAudioChannels();
- traktEpisodeCollected.audio = audioStream.GetCodecRepresetation();
- traktEpisodeCollected.resolution = episode.GetDefaultVideoStream().GetResolution();
- }
-
- syncSeason.episodes.Add(traktEpisodeCollected);
-
- number++;
- } while (episode.IndexNumber.HasValue && number <= finalNumber);
-
- }
-
- var data = new TraktSyncCollected
- {
- episodes = episodesPayload.ToList(),
- shows = showPayload.ToList()
- };
-
- var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove;
- using (var response = await PostToTrakt(url, data, cancellationToken, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
- }
+ throw new ArgumentNullException(nameof(traktUser));
}
- ///
- /// Add or remove a Show(Series) to/from the users trakt.tv library
- ///
- /// The show to remove
- /// The user who's library is being updated
- /// The cancellation token.
- ///
- /// Task{TraktResponseDataContract}.
- public async Task SendLibraryUpdateAsync(
- Series show,
- TraktUser traktUser,
- CancellationToken cancellationToken,
- EventType eventType)
+ if (eventType == EventType.Update)
{
- if (show == null)
- {
- throw new ArgumentNullException(nameof(show));
- }
+ return null;
+ }
- if (traktUser == null)
+ var showPayload = new List
+ {
+ new TraktShowCollected
{
- throw new ArgumentNullException(nameof(traktUser));
+ Title = show.Name,
+ Year = show.ProductionYear,
+ Ids = GetTraktTvIds(show)
}
+ };
- if (eventType == EventType.Update)
- {
- return null;
- }
+ var data = new TraktSyncCollected
+ {
+ Shows = showPayload.ToList()
+ };
- var showPayload = new List
+ var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove;
+ using (var response = await PostToTrakt(url, data, traktUser, cancellationToken).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Rate an item
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task SendItemRating(BaseItem item, int rating, TraktUser traktUser)
+ {
+ object data = new { };
+ if (item is Movie)
+ {
+ data = new
{
- new TraktShowCollected
+ movies = new[]
{
- title = show.Name,
- year = show.ProductionYear,
- ids = GetTraktTVIds(show)
+ new TraktMovieRated
+ {
+ Title = item.Name,
+ Year = item.ProductionYear,
+ Ids = GetTraktIMDBTMDBIds((Movie)item),
+ Rating = rating
+ }
}
};
-
- var data = new TraktSyncCollected
- {
- shows = showPayload.ToList()
- };
-
- var url = eventType == EventType.Add ? TraktUris.SyncCollectionAdd : TraktUris.SyncCollectionRemove;
- using (var response = await PostToTrakt(url, data, cancellationToken, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
- }
}
-
-
-
- ///
- /// Rate an item
- ///
- ///
- ///
- ///
- ///
- public async Task SendItemRating(BaseItem item, int rating, TraktUser traktUser)
+ else if (item is Episode episode)
{
- object data = new { };
- if (item is Movie)
+ var show = new TraktShowRated
{
- data = new
+ Ids = GetTraktTvIds(episode.Series),
+ Seasons = new List
{
- movies = new[]
+ new TraktSeasonRated
{
- new TraktMovieRated
+ Number = episode.GetSeasonNumber(),
+ Episodes = new List
{
- title = item.Name,
- year = item.ProductionYear,
- ids = GetTraktIMDBTMDBIds((Movie) item),
- rating = rating
- }
- }
- };
-
- }
- else if (item is Episode)
- {
- var episode = item as Episode;
-
- var show = new TraktShowRated
- {
- ids = GetTraktTVIds(episode.Series),
- seasons = new List
- {
- new TraktShowRated.TraktSeasonRated
+ new TraktEpisodeRated
{
- number = episode.GetSeasonNumber(),
- episodes = new List
- {
- new TraktEpisodeRated
- {
- number = episode.IndexNumber,
- rating = rating,
- ids = GetTraktTVIds(episode)
- }
- }
+ Number = episode.IndexNumber,
+ Rating = rating,
+ Ids = GetTraktTvIds(episode)
}
}
- };
- data = new
- {
- shows = new[]
- {
- show
- }
- };
- }
- else // It's a Series
- {
- data = new
- {
- shows = new[]
- {
- new TraktShowRated
- {
- rating = rating,
- title = item.Name,
- year = item.ProductionYear,
- ids = GetTraktTVIds((Series) item)
- }
- }
- };
- }
-
- using (var response = await PostToTrakt(TraktUris.SyncRatingsAdd, data, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public async Task> SendMovieRecommendationsRequest(TraktUser traktUser)
- {
- using (var response = await GetFromTrakt(TraktUris.RecommendationsMovies, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public async Task> SendShowRecommendationsRequest(TraktUser traktUser)
- {
- using (var response = await GetFromTrakt(TraktUris.RecommendationsShows, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public async Task> SendGetAllWatchedMoviesRequest(TraktUser traktUser)
- {
- using (var response = await GetFromTrakt(TraktUris.WatchedMovies, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public async Task> SendGetWatchedShowsRequest(TraktUser traktUser)
- {
- using (var response = await GetFromTrakt(TraktUris.WatchedShows, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public async Task> SendGetAllCollectedMoviesRequest(TraktUser traktUser)
- {
- using (var response = await GetFromTrakt(TraktUris.CollectedMovies, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
- }
- }
-
- ///
- ///
- ///
- ///
- ///
- public async Task> SendGetCollectedShowsRequest(TraktUser traktUser)
- {
- using (var response = await GetFromTrakt(TraktUris.CollectedShows, traktUser).ConfigureAwait(false))
- {
- return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
- }
- }
-
- ///
- /// Send a list of movies to trakt.tv that have been marked watched or unwatched.
- ///
- /// The list of movies to send
- /// The trakt user profile that is being updated
- /// True if movies are being marked seen, false otherwise
- /// The Cancellation Token
- ///
- // TODO: netstandard2.1: use IAsyncEnumerable
- public async Task> SendMoviePlaystateUpdates(List movies, TraktUser traktUser, bool seen, CancellationToken cancellationToken)
- {
- if (movies == null)
- {
- throw new ArgumentNullException(nameof(movies));
- }
-
- if (traktUser == null)
- {
- throw new ArgumentNullException(nameof(traktUser));
- }
-
- var moviesPayload = movies.Select(m =>
- {
- var lastPlayedDate = seen
- ? _userDataManager.GetUserData(new Guid(traktUser.LinkedMbUserId), m).LastPlayedDate
- : null;
- return new TraktMovieWatched
- {
- title = m.Name,
- ids = GetTraktIMDBTMDBIds(m),
- year = m.ProductionYear,
- watched_at = lastPlayedDate?.ToISO8601()
- };
- }).ToList();
- var chunks = moviesPayload.ToChunks(100).ToList();
- var traktResponses = new List();
-
- foreach (var chunk in chunks)
- {
- var data = new TraktSyncWatched
- {
- movies = chunk.ToList()
- };
- var url = seen ? TraktUris.SyncWatchedHistoryAdd : TraktUris.SyncWatchedHistoryRemove;
-
- using (var response = await PostToTrakt(url, data, cancellationToken, traktUser).ConfigureAwait(false))
- {
- if (response != null)
- {
- traktResponses.Add(await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false));
}
}
- }
-
- return traktResponses;
+ };
+ data = new
+ {
+ shows = new[]
+ {
+ show
+ }
+ };
+ }
+ else // It's a Series
+ {
+ data = new
+ {
+ shows = new[]
+ {
+ new TraktShowRated
+ {
+ Rating = rating,
+ Title = item.Name,
+ Year = item.ProductionYear,
+ Ids = GetTraktTvIds((Series)item)
+ }
+ }
+ };
}
- ///
- /// Send a list of episodes to trakt.tv that have been marked watched or unwatched
- ///
- /// The list of episodes to send
- /// The trakt user profile that is being updated
- /// True if episodes are being marked seen, false otherwise
- /// The Cancellation Token
- ///
- public async Task> SendEpisodePlaystateUpdates(List episodes, TraktUser traktUser, bool seen, CancellationToken cancellationToken)
+ using (var response = await PostToTrakt(TraktUris.SyncRatingsAdd, data, traktUser).ConfigureAwait(false))
{
- if (episodes == null)
- {
- throw new ArgumentNullException(nameof(episodes));
- }
+ return await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
+ }
+ }
- if (traktUser == null)
- {
- throw new ArgumentNullException(nameof(traktUser));
- }
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> SendMovieRecommendationsRequest(TraktUser traktUser)
+ {
+ using (var response = await GetFromTrakt(TraktUris.RecommendationsMovies, traktUser).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
+ }
+ }
- var chunks = episodes.ToChunks(100).ToList();
- var traktResponses = new List();
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> SendShowRecommendationsRequest(TraktUser traktUser)
+ {
+ using (var response = await GetFromTrakt(TraktUris.RecommendationsShows, traktUser).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
+ }
+ }
- foreach (var chunk in chunks)
- {
- var response = await SendEpisodePlaystateUpdatesInternalAsync(chunk, traktUser, seen, cancellationToken).ConfigureAwait(false);
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> SendGetAllWatchedMoviesRequest(TraktUser traktUser)
+ {
+ using (var response = await GetFromTrakt(TraktUris.WatchedMovies, traktUser).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
+ }
+ }
- if (response != null)
- {
- traktResponses.Add(response);
- }
- }
- return traktResponses;
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> SendGetWatchedShowsRequest(TraktUser traktUser)
+ {
+ using (var response = await GetFromTrakt(TraktUris.WatchedShows, traktUser).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> SendGetAllCollectedMoviesRequest(TraktUser traktUser)
+ {
+ using (var response = await GetFromTrakt(TraktUris.CollectedMovies, traktUser).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ public async Task> SendGetCollectedShowsRequest(TraktUser traktUser)
+ {
+ using (var response = await GetFromTrakt(TraktUris.CollectedShows, traktUser).ConfigureAwait(false))
+ {
+ return await JsonSerializer.DeserializeAsync>(response, _jsonOptions).ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Send a list of movies to trakt.tv that have been marked watched or unwatched.
+ ///
+ /// The list of movies to send
+ /// The trakt user profile that is being updated
+ /// True if movies are being marked seen, false otherwise
+ /// The Cancellation Token
+ ///
+ // TODO: netstandard2.1: use IAsyncEnumerable
+ public async Task> SendMoviePlaystateUpdates(List movies, TraktUser traktUser, bool seen, CancellationToken cancellationToken)
+ {
+ if (movies == null)
+ {
+ throw new ArgumentNullException(nameof(movies));
}
- private async Task SendEpisodePlaystateUpdatesInternalAsync(IEnumerable episodeChunk, TraktUser traktUser, bool seen, CancellationToken cancellationToken)
+ if (traktUser == null)
{
- var data = new TraktSyncWatched { episodes = new List(), shows = new List() };
- foreach (var episode in episodeChunk)
+ throw new ArgumentNullException(nameof(traktUser));
+ }
+
+ var moviesPayload = movies.Select(m =>
+ {
+ var lastPlayedDate = seen
+ ? _userDataManager.GetUserData(new Guid(traktUser.LinkedMbUserId), m).LastPlayedDate
+ : null;
+ return new TraktMovieWatched
{
- var lastPlayedDate = seen
- ? _userDataManager.GetUserData(new Guid(traktUser.LinkedMbUserId), episode)
- .LastPlayedDate
- : null;
+ Title = m.Name,
+ Ids = GetTraktIMDBTMDBIds(m),
+ Year = m.ProductionYear,
+ WatchedAt = lastPlayedDate?.ToISO8601()
+ };
+ }).ToList();
+ var chunks = moviesPayload.ToChunks(100).ToList();
+ var traktResponses = new List();
- var syncShow = findShow(data.shows, episode.Series);
- if (syncShow == null)
- {
- syncShow = new TraktShowWatched
- {
- ids = GetTraktTVIds(episode.Series),
- seasons = new List()
- };
- data.shows.Add(syncShow);
- }
-
- var syncSeason = syncShow.seasons.FirstOrDefault(ss => ss.number == episode.GetSeasonNumber());
- if (syncSeason == null)
- {
- syncSeason = new TraktSeasonWatched
- {
- number = episode.GetSeasonNumber(),
- episodes = new List()
- };
- syncShow.seasons.Add(syncSeason);
- }
-
- var indexNumber = 0;
- var finalNumber = 0;
- if (episode.IndexNumber.HasValue)
- {
- indexNumber = episode.IndexNumber.Value;
- finalNumber = (episode.IndexNumberEnd ?? episode.IndexNumber).Value;
- }
-
- var number = indexNumber;
- var firstPass = true;
- do
- {
- var watchedEpisode = new TraktEpisodeWatched
- {
- watched_at = lastPlayedDate.HasValue ? lastPlayedDate.Value.ToISO8601() : null
- };
- if (episode.IndexNumber.HasValue)
- {
- watchedEpisode.number = number;
- }
- //provider IDs in multi-episode file will be for the first episode only
- if (firstPass)
- {
- //output provider IDs for first episode
- watchedEpisode.ids = GetTraktTVIds(episode);
- firstPass = false;
- }
- syncSeason.episodes.Add(watchedEpisode);
-
- number++;
- } while (episode.IndexNumber.HasValue && number <= finalNumber);
-
- }
+ foreach (var chunk in chunks)
+ {
+ var data = new TraktSyncWatched
+ {
+ Movies = chunk.ToList()
+ };
var url = seen ? TraktUris.SyncWatchedHistoryAdd : TraktUris.SyncWatchedHistoryRemove;
- using (var response = await PostToTrakt(url, data, cancellationToken, traktUser).ConfigureAwait(false))
+ using (var response = await PostToTrakt(url, data, traktUser, cancellationToken).ConfigureAwait(false))
{
- return await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
- }
- }
-
- public async Task AuthorizeDevice(TraktUser traktUser)
- {
- var deviceCodeRequest = new
- {
- client_id = TraktUris.ClientId
- };
-
- TraktDeviceCode deviceCode;
- using (var response = await PostToTrakt(TraktUris.DeviceCode, deviceCodeRequest, null).ConfigureAwait(false))
- {
- deviceCode = await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
- }
-
- // Start polling in the background
- Plugin.Instance.PollingTasks[traktUser.LinkedMbUserId] = Task.Run(() => PollForAccessToken(deviceCode, traktUser));
-
- return deviceCode.user_code;
- }
-
- public async Task PollForAccessToken(TraktDeviceCode deviceCode, TraktUser traktUser)
- {
- var deviceAccessTokenRequest = new
- {
- code = deviceCode.device_code,
- client_id = TraktUris.ClientId,
- client_secret = TraktUris.ClientSecret
- };
-
- var pollingInterval = deviceCode.interval;
- var expiresAt = DateTime.UtcNow.AddSeconds(deviceCode.expires_in);
- _logger.LogInformation("Polling for access token every {PollingInterval}s. Expires at {ExpiresAt} UTC.", pollingInterval, expiresAt);
- while (DateTime.UtcNow < expiresAt)
- {
- using (var response = await PostToTrakt(TraktUris.DeviceToken, deviceAccessTokenRequest).ConfigureAwait(false))
+ if (response != null)
{
- switch (response.StatusCode)
- {
- case HttpStatusCode.BadRequest:
- // Pending - waiting for the user to authorize your app
- break;
- case HttpStatusCode.NotFound:
- _logger.LogError("Not Found - invalid device_code");
- break;
- case HttpStatusCode.Conflict:
- _logger.LogWarning("Already Used - user already approved this code");
- return false;
- case HttpStatusCode.Gone:
- _logger.LogError("Expired - the tokens have expired, restart the process");
- break;
- case (HttpStatusCode)418:
- _logger.LogInformation("Denied - user explicitly denied this code");
- return false;
- case (HttpStatusCode)429:
- _logger.LogWarning("Polling too quickly. Slowing down");
- pollingInterval += 1;
- break;
- case HttpStatusCode.OK:
- _logger.LogInformation("Device successfully authorized");
+ traktResponses.Add(await JsonSerializer.DeserializeAsync(response, _jsonOptions, cancellationToken).ConfigureAwait(false));
+ }
+ }
+ }
- var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- var userAccessToken = await JsonSerializer.DeserializeAsync(stream, _jsonOptions).ConfigureAwait(false);
- if (userAccessToken != null)
- {
- traktUser.AccessToken = userAccessToken.access_token;
- traktUser.RefreshToken = userAccessToken.refresh_token;
- traktUser.AccessTokenExpiration = DateTime.Now.AddSeconds(userAccessToken.expirationWithBuffer);
- Plugin.Instance.SaveConfiguration();
- return true;
- }
- break;
- }
+ return traktResponses;
+ }
+
+ ///
+ /// Send a list of episodes to trakt.tv that have been marked watched or unwatched
+ ///
+ /// The list of episodes to send
+ /// The trakt user profile that is being updated
+ /// True if episodes are being marked seen, false otherwise
+ /// The Cancellation Token
+ ///
+ public async Task> SendEpisodePlaystateUpdates(List episodes, TraktUser traktUser, bool seen, CancellationToken cancellationToken)
+ {
+ if (episodes == null)
+ {
+ throw new ArgumentNullException(nameof(episodes));
+ }
+
+ if (traktUser == null)
+ {
+ throw new ArgumentNullException(nameof(traktUser));
+ }
+
+ var chunks = episodes.ToChunks(100).ToList();
+ var traktResponses = new List();
+
+ foreach (var chunk in chunks)
+ {
+ var response = await SendEpisodePlaystateUpdatesInternalAsync(chunk, traktUser, seen, cancellationToken).ConfigureAwait(false);
+
+ if (response != null)
+ {
+ traktResponses.Add(response);
+ }
+ }
+
+ return traktResponses;
+ }
+
+ private async Task SendEpisodePlaystateUpdatesInternalAsync(IEnumerable episodeChunk, TraktUser traktUser, bool seen, CancellationToken cancellationToken)
+ {
+ var data = new TraktSyncWatched { Episodes = new List(), Shows = new List() };
+ foreach (var episode in episodeChunk)
+ {
+ var lastPlayedDate = seen
+ ? _userDataManager.GetUserData(new Guid(traktUser.LinkedMbUserId), episode)
+ .LastPlayedDate
+ : null;
+
+ var syncShow = FindShow(data.Shows, episode.Series);
+ if (syncShow == null)
+ {
+ syncShow = new TraktShowWatched
+ {
+ Ids = GetTraktTvIds(episode.Series),
+ Seasons = new List()
+ };
+ data.Shows.Add(syncShow);
+ }
+
+ var syncSeason = syncShow.Seasons.FirstOrDefault(ss => ss.Number == episode.GetSeasonNumber());
+ if (syncSeason == null)
+ {
+ syncSeason = new TraktSeasonWatched
+ {
+ Number = episode.GetSeasonNumber(),
+ Episodes = new List()
+ };
+ syncShow.Seasons.Add(syncSeason);
+ }
+
+ var indexNumber = 0;
+ var finalNumber = 0;
+ if (episode.IndexNumber.HasValue)
+ {
+ indexNumber = episode.IndexNumber.Value;
+ finalNumber = (episode.IndexNumberEnd ?? episode.IndexNumber).Value;
+ }
+
+ var number = indexNumber;
+ var firstPass = true;
+ do
+ {
+ var watchedEpisode = new TraktEpisodeWatched
+ {
+ WatchedAt = lastPlayedDate.HasValue ? lastPlayedDate.Value.ToISO8601() : null
+ };
+ if (episode.IndexNumber.HasValue)
+ {
+ watchedEpisode.Number = number;
}
- await Task.Delay(pollingInterval * 1000).ConfigureAwait(false);
- }
- return false;
- }
-
- public async Task RefreshUserAccessToken(TraktUser traktUser)
- {
- if (string.IsNullOrWhiteSpace(traktUser.RefreshToken))
- {
- _logger.LogError("Tried to reauthenticate with Trakt, but no refreshToken was available");
- return;
- }
-
- var data = new TraktUserRefreshTokenRequest
- {
- client_id = TraktUris.ClientId,
- client_secret = TraktUris.ClientSecret,
- redirect_uri = "urn:ietf:wg:oauth:2.0:oob",
- refresh_token = traktUser.RefreshToken,
- grant_type = "refresh_token"
- };
-
- TraktUserAccessToken userAccessToken;
- try
- {
- using (var response = await PostToTrakt(TraktUris.AccessToken, data).ConfigureAwait(false))
+ // provider IDs in multi-episode file will be for the first episode only
+ if (firstPass)
{
- await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- userAccessToken = await JsonSerializer.DeserializeAsync(stream, _jsonOptions).ConfigureAwait(false);
+ // output provider IDs for first episode
+ watchedEpisode.Ids = GetTraktTvIds(episode);
+ firstPass = false;
}
- }
- catch (HttpRequestException ex)
- {
- _logger.LogError(ex, "An error occurred during token refresh");
- return;
- }
+ syncSeason.Episodes.Add(watchedEpisode);
- if (userAccessToken != null)
- {
- traktUser.AccessToken = userAccessToken.access_token;
- traktUser.RefreshToken = userAccessToken.refresh_token;
- traktUser.AccessTokenExpiration = DateTime.Now.AddSeconds(userAccessToken.expirationWithBuffer);
- Plugin.Instance.SaveConfiguration();
- _logger.LogInformation("Successfully refreshed the access token for user {UserId}", traktUser.LinkedMbUserId);
+ number++;
}
+ while (episode.IndexNumber.HasValue && number <= finalNumber);
}
- private Task GetFromTrakt(string url, TraktUser traktUser)
+ var url = seen ? TraktUris.SyncWatchedHistoryAdd : TraktUris.SyncWatchedHistoryRemove;
+
+ using (var response = await PostToTrakt(url, data, traktUser, cancellationToken).ConfigureAwait(false))
{
- return GetFromTrakt(url, CancellationToken.None, traktUser);
+ return await JsonSerializer.DeserializeAsync(response, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ }
+ }
+
+ public async Task AuthorizeDevice(TraktUser traktUser)
+ {
+ var deviceCodeRequest = new
+ {
+ client_id = TraktUris.ClientId
+ };
+
+ TraktDeviceCode deviceCode;
+ using (var response = await PostToTrakt(TraktUris.DeviceCode, deviceCodeRequest, null).ConfigureAwait(false))
+ {
+ deviceCode = await JsonSerializer.DeserializeAsync(response, _jsonOptions).ConfigureAwait(false);
}
- private async Task GetFromTrakt(string url, CancellationToken cancellationToken, TraktUser traktUser)
+ // Start polling in the background
+ Plugin.Instance.PollingTasks[traktUser.LinkedMbUserId] = Task.Run(() => PollForAccessToken(deviceCode, traktUser));
+
+ return deviceCode.UserCode;
+ }
+
+ public async Task PollForAccessToken(TraktDeviceCode deviceCode, TraktUser traktUser)
+ {
+ var deviceAccessTokenRequest = new
{
- var httpClient = GetHttpClient();
+ code = deviceCode.DeviceCode,
+ client_id = TraktUris.ClientId,
+ client_secret = TraktUris.ClientSecret
+ };
- if (traktUser != null)
- {
- await SetRequestHeaders(httpClient, traktUser).ConfigureAwait(false);
- }
-
- await _traktResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var response = await RetryHttpRequest(async () => await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
- return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- }
- finally
- {
- _traktResourcePool.Release();
- }
- }
-
- private async Task PostToTrakt(string url, object data)
+ var pollingInterval = deviceCode.Interval;
+ var expiresAt = DateTime.UtcNow.AddSeconds(deviceCode.ExpiresIn);
+ _logger.LogInformation("Polling for access token every {PollingInterval}s. Expires at {ExpiresAt} UTC.", pollingInterval, expiresAt);
+ while (DateTime.UtcNow < expiresAt)
{
- var httpClient = GetHttpClient();
-
- var bytes = JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions);
- var content = new ByteArrayContent(bytes);
- content.Headers.Add(HeaderNames.ContentType, MediaTypeNames.Application.Json);
-
- await _traktResourcePool.WaitAsync().ConfigureAwait(false);
-
- try
+ using (var response = await PostToTrakt(TraktUris.DeviceToken, deviceAccessTokenRequest).ConfigureAwait(false))
{
- return await httpClient.PostAsync(url, content).ConfigureAwait(false);
- }
- finally
- {
- _traktResourcePool.Release();
- }
- }
-
- private Task PostToTrakt(string url, object data, TraktUser traktUser)
- {
- return PostToTrakt(url, data, CancellationToken.None, traktUser);
- }
-
- ///
- /// Posts data to url, authenticating with .
- ///
- /// If null, authentication headers not added.
- private async Task PostToTrakt(
- string url,
- object data,
- CancellationToken cancellationToken,
- TraktUser traktUser)
- {
- if (traktUser != null && traktUser.ExtraLogging)
- {
- _logger.LogDebug("{@JsonData}", data);
- }
-
- var httpClient = GetHttpClient();
-
- if (traktUser != null)
- {
- await SetRequestHeaders(httpClient, traktUser).ConfigureAwait(false);
- }
-
- var bytes = JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions);
- var content = new ByteArrayContent(bytes);
- content.Headers.Add(HeaderNames.ContentType, MediaTypeNames.Application.Json);
-
- await _traktResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
-
- try
- {
- var response = await RetryHttpRequest(async () => await httpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
- return await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
- }
- finally
- {
- _traktResourcePool.Release();
- }
- }
-
- private async Task RetryHttpRequest(Func> function)
- {
- HttpResponseMessage response = null;
- for (int i = 0; i < 3; i++)
- {
- try
+ switch (response.StatusCode)
{
- response = await function().ConfigureAwait(false);
- if (response.StatusCode == (HttpStatusCode) 429)
- {
- var delay = response.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(1);
- await Task.Delay(delay).ConfigureAwait(false);
- }
- else
- {
+ case HttpStatusCode.BadRequest:
+ // Pending - waiting for the user to authorize your app
break;
- }
- } catch {}
- }
-
+ case HttpStatusCode.NotFound:
+ _logger.LogError("Not Found - invalid device_code");
+ break;
+ case HttpStatusCode.Conflict:
+ _logger.LogWarning("Already Used - user already approved this code");
+ return false;
+ case HttpStatusCode.Gone:
+ _logger.LogError("Expired - the tokens have expired, restart the process");
+ break;
+ case (HttpStatusCode)418:
+ _logger.LogInformation("Denied - user explicitly denied this code");
+ return false;
+ case (HttpStatusCode)429:
+ _logger.LogWarning("Polling too quickly. Slowing down");
+ pollingInterval += 1;
+ break;
+ case HttpStatusCode.OK:
+ _logger.LogInformation("Device successfully authorized");
- return response;
- }
+ var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+ var userAccessToken = await JsonSerializer.DeserializeAsync(stream, _jsonOptions).ConfigureAwait(false);
+ if (userAccessToken != null)
+ {
+ traktUser.AccessToken = userAccessToken.AccessToken;
+ traktUser.RefreshToken = userAccessToken.RefreshToken;
+ traktUser.AccessTokenExpiration = DateTime.Now.AddSeconds(userAccessToken.ExpirationWithBuffer);
+ Plugin.Instance.SaveConfiguration();
+ return true;
+ }
- private HttpClient GetHttpClient()
- {
- var client = _httpClientFactory.CreateClient(NamedClient.Default);
- client.DefaultRequestHeaders.Add("trakt-api-version", "2");
- client.DefaultRequestHeaders.Add("trakt-api-key", TraktUris.ClientId);
- return client;
- }
-
- private async Task SetRequestHeaders(HttpClient httpClient, TraktUser traktUser)
- {
- if (DateTimeOffset.Now > traktUser.AccessTokenExpiration)
- {
- traktUser.AccessToken = string.Empty;
- await RefreshUserAccessToken(traktUser).ConfigureAwait(false);
+ break;
+ }
}
- if (!string.IsNullOrEmpty(traktUser.AccessToken))
+ await Task.Delay(pollingInterval * 1000).ConfigureAwait(false);
+ }
+
+ return false;
+ }
+
+ public async Task RefreshUserAccessToken(TraktUser traktUser)
+ {
+ if (string.IsNullOrWhiteSpace(traktUser.RefreshToken))
+ {
+ _logger.LogError("Tried to reauthenticate with Trakt, but no refreshToken was available");
+ return;
+ }
+
+ var data = new TraktUserRefreshTokenRequest
+ {
+ ClientId = TraktUris.ClientId,
+ ClientSecret = TraktUris.ClientSecret,
+ RedirectUri = "urn:ietf:wg:oauth:2.0:oob",
+ RefreshToken = traktUser.RefreshToken,
+ GrantType = "refresh_token"
+ };
+
+ TraktUserAccessToken userAccessToken;
+ try
+ {
+ using (var response = await PostToTrakt(TraktUris.AccessToken, data).ConfigureAwait(false))
+ {
+#pragma warning disable CA2007
+ await using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
+#pragma warning restore CA2007
+ userAccessToken = await JsonSerializer.DeserializeAsync(stream, _jsonOptions).ConfigureAwait(false);
+ }
+ }
+ catch (HttpRequestException ex)
+ {
+ _logger.LogError(ex, "An error occurred during token refresh");
+ return;
+ }
+
+ if (userAccessToken != null)
+ {
+ traktUser.AccessToken = userAccessToken.AccessToken;
+ traktUser.RefreshToken = userAccessToken.RefreshToken;
+ traktUser.AccessTokenExpiration = DateTime.Now.AddSeconds(userAccessToken.ExpirationWithBuffer);
+ Plugin.Instance.SaveConfiguration();
+ _logger.LogInformation("Successfully refreshed the access token for user {UserId}", traktUser.LinkedMbUserId);
+ }
+ }
+
+ private Task GetFromTrakt(string url, TraktUser traktUser)
+ {
+ return GetFromTrakt(url, traktUser, CancellationToken.None);
+ }
+
+ private async Task GetFromTrakt(string url, TraktUser traktUser, CancellationToken cancellationToken)
+ {
+ var httpClient = GetHttpClient();
+
+ if (traktUser != null)
+ {
+ await SetRequestHeaders(httpClient, traktUser).ConfigureAwait(false);
+ }
+
+ await _traktResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ var response = await RetryHttpRequest(async () => await httpClient.GetAsync(url, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
+ return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ }
+ finally
+ {
+ _traktResourcePool.Release();
+ }
+ }
+
+ private async Task PostToTrakt(string url, object data)
+ {
+ var httpClient = GetHttpClient();
+
+ var bytes = JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions);
+ var content = new ByteArrayContent(bytes);
+ content.Headers.Add(HeaderNames.ContentType, MediaTypeNames.Application.Json);
+
+ await _traktResourcePool.WaitAsync().ConfigureAwait(false);
+
+ try
+ {
+ return await httpClient.PostAsync(url, content).ConfigureAwait(false);
+ }
+ finally
+ {
+ _traktResourcePool.Release();
+ }
+ }
+
+ private Task PostToTrakt(string url, object data, TraktUser traktUser)
+ {
+ return PostToTrakt(url, data, traktUser, CancellationToken.None);
+ }
+
+ ///
+ /// Posts data to url, authenticating with .
+ ///
+ /// If null, authentication headers not added.
+ private async Task PostToTrakt(
+ string url,
+ object data,
+ TraktUser traktUser,
+ CancellationToken cancellationToken)
+ {
+ if (traktUser != null && traktUser.ExtraLogging)
+ {
+ _logger.LogDebug("{@JsonData}", data);
+ }
+
+ var httpClient = GetHttpClient();
+
+ if (traktUser != null)
+ {
+ await SetRequestHeaders(httpClient, traktUser).ConfigureAwait(false);
+ }
+
+ var bytes = JsonSerializer.SerializeToUtf8Bytes(data, _jsonOptions);
+ var content = new ByteArrayContent(bytes);
+ content.Headers.Add(HeaderNames.ContentType, MediaTypeNames.Application.Json);
+
+ await _traktResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+ try
+ {
+ var response = await RetryHttpRequest(async () => await httpClient.PostAsync(url, content, cancellationToken).ConfigureAwait(false)).ConfigureAwait(false);
+ return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ }
+ finally
+ {
+ _traktResourcePool.Release();
+ }
+ }
+
+ private async Task RetryHttpRequest(Func> function)
+ {
+ HttpResponseMessage response = null;
+ for (int i = 0; i < 3; i++)
+ {
+ try
+ {
+ response = await function().ConfigureAwait(false);
+ if (response.StatusCode == (HttpStatusCode)429)
+ {
+ var delay = response.Headers.RetryAfter.Delta ?? TimeSpan.FromSeconds(1);
+ await Task.Delay(delay).ConfigureAwait(false);
+ }
+ else
+ {
+ break;
+ }
+ }
+ catch (Exception)
{
- httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + traktUser.AccessToken);
}
}
- private static TReturn GetTraktIMDBTMDBIds(TInput mediaObject)
- where TInput : IHasProviderIds where TReturn : TraktIMDBandTMDBId, new()
+ return response;
+ }
+
+ private HttpClient GetHttpClient()
+ {
+ var client = _httpClientFactory.CreateClient(NamedClient.Default);
+ client.DefaultRequestHeaders.Add("trakt-api-version", "2");
+ client.DefaultRequestHeaders.Add("trakt-api-key", TraktUris.ClientId);
+ return client;
+ }
+
+ private async Task SetRequestHeaders(HttpClient httpClient, TraktUser traktUser)
+ {
+ if (DateTimeOffset.Now > traktUser.AccessTokenExpiration)
{
- return new TReturn
- {
- imdb = mediaObject.GetProviderId(MetadataProvider.Imdb),
- tmdb = mediaObject.GetProviderId(MetadataProvider.Tmdb).ConvertToInt()
- };
+ traktUser.AccessToken = string.Empty;
+ await RefreshUserAccessToken(traktUser).ConfigureAwait(false);
}
- private static TReturn GetTraktTVIds(TInput mediaObject)
- where TInput : IHasProviderIds where TReturn : TraktTVId, new()
+ if (!string.IsNullOrEmpty(traktUser.AccessToken))
{
- TReturn retval = GetTraktIMDBTMDBIds(mediaObject);
- retval.tvdb = mediaObject.GetProviderId(MetadataProvider.Tvdb).ConvertToInt();
- retval.tvrage = mediaObject.GetProviderId(MetadataProvider.TvRage).ConvertToInt();
- return retval;
+ httpClient.DefaultRequestHeaders.Add(HeaderNames.Authorization, "Bearer " + traktUser.AccessToken);
}
+ }
- private static TTraktShow findShow(List shows, Series series)
- where TTraktShow : TraktShow
+ private static TReturn GetTraktIMDBTMDBIds(TInput mediaObject)
+ where TInput : IHasProviderIds
+ where TReturn : TraktIMDBandTMDBId, new()
+ {
+ return new TReturn
{
- return shows.FirstOrDefault(
- sre => sre.ids != null &&
- (
- sre.ids.imdb == series.GetProviderId(MetadataProvider.Imdb) &&
- sre.ids.tmdb == series.GetProviderId(MetadataProvider.Tmdb).ConvertToInt() &&
- sre.ids.tvdb == series.GetProviderId(MetadataProvider.Tvdb).ConvertToInt() &&
- sre.ids.tvrage == series.GetProviderId(MetadataProvider.TvRage).ConvertToInt()
- ));
- }
+ Imdb = mediaObject.GetProviderId(MetadataProvider.Imdb),
+ Tmdb = mediaObject.GetProviderId(MetadataProvider.Tmdb).ConvertToInt()
+ };
+ }
+ private static TReturn GetTraktTvIds(TInput mediaObject)
+ where TInput : IHasProviderIds
+ where TReturn : TraktTVId, new()
+ {
+ TReturn retval = GetTraktIMDBTMDBIds(mediaObject);
+ retval.Tvdb = mediaObject.GetProviderId(MetadataProvider.Tvdb).ConvertToInt();
+ retval.Tvrage = mediaObject.GetProviderId(MetadataProvider.TvRage).ConvertToInt();
+ return retval;
+ }
+
+ private static TTraktShow FindShow(List shows, Series series)
+ where TTraktShow : TraktShow
+ {
+ return shows.FirstOrDefault(
+ sre => sre.Ids != null && sre.Ids.Imdb == series.GetProviderId(MetadataProvider.Imdb) && sre.Ids.Tmdb == series.GetProviderId(MetadataProvider.Tmdb).ConvertToInt() && sre.Ids.Tvdb == series.GetProviderId(MetadataProvider.Tvdb).ConvertToInt() && sre.Ids.Tvrage == series.GetProviderId(MetadataProvider.TvRage).ConvertToInt());
}
}
diff --git a/Trakt/Api/TraktController.cs b/Trakt/Api/TraktController.cs
index fdf6d6f..180391d 100644
--- a/Trakt/Api/TraktController.cs
+++ b/Trakt/Api/TraktController.cs
@@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
-using System.Net.Mime;
using System.Net.Http;
+using System.Net.Mime;
using System.Threading.Tasks;
-using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.IO;
@@ -14,145 +13,145 @@ using Trakt.Api.DataContracts.BaseModel;
using Trakt.Api.DataContracts.Sync;
using Trakt.Helpers;
-namespace Trakt.Api
+namespace Trakt.Api;
+
+///
+/// The Trakt.tv controller.
+///
+[ApiController]
+[Route("[controller]")]
+[Produces(MediaTypeNames.Application.Json)]
+public class TraktController : ControllerBase
{
+ private readonly TraktApi _traktApi;
+ private readonly ILibraryManager _libraryManager;
+ private readonly ILogger _logger;
+
///
- /// The Trakt.tv controller.
+ /// Initializes a new instance of the class.
///
- [ApiController]
- [Route("[controller]")]
- [Produces(MediaTypeNames.Application.Json)]
- public class TraktController : ControllerBase
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ /// Instance of the interface.
+ public TraktController(
+ IUserDataManager userDataManager,
+ ILoggerFactory loggerFactory,
+ IHttpClientFactory httpClientFactory,
+ IServerApplicationHost appHost,
+ IFileSystem fileSystem,
+ ILibraryManager libraryManager)
{
- private readonly TraktApi _traktApi;
- private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ _logger = loggerFactory.CreateLogger();
+ _traktApi = new TraktApi(loggerFactory.CreateLogger(), httpClientFactory, appHost, userDataManager, fileSystem);
+ _libraryManager = libraryManager;
+ }
- ///
- /// Initializes a new instance of the class.
- ///
- /// Instance of the interface.
- /// Instance of the interface.
- /// Instance of the interface.
- /// Instance of the interface.
- /// Instance of the interface.
- /// Instance of the interface.
- public TraktController(
- IUserDataManager userDataManager,
- ILoggerFactory loggerFactory,
- IHttpClientFactory httpClientFactory,
- IServerApplicationHost appHost,
- IFileSystem fileSystem,
- ILibraryManager libraryManager)
+ ///
+ /// Authorize this server with trakt.
+ ///
+ /// The user id of the user connecting to trakt.
+ /// Authorization code requested successfully.
+ /// The trakt authorization code.
+ [HttpPost("Users/{userId}/Authorize")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public async Task> TraktDeviceAuthorization([FromRoute] string userId)
+ {
+ _logger.LogInformation("TraktDeviceAuthorization request received");
+
+ // Create a user if we don't have one yet - TODO there should be an endpoint for this that creates a default user
+ var traktUser = UserHelper.GetTraktUser(userId);
+ if (traktUser == null)
{
- _logger = loggerFactory.CreateLogger();
- _traktApi = new TraktApi(loggerFactory.CreateLogger(), httpClientFactory, appHost, userDataManager, fileSystem);
- _libraryManager = libraryManager;
+ Plugin.Instance.PluginConfiguration.AddUser(userId);
+ traktUser = UserHelper.GetTraktUser(userId);
+ Plugin.Instance.SaveConfiguration();
}
- ///
- /// Authorize this server with trakt.
- ///
- /// The user id of the user connecting to trakt.
- /// Authorization code requested successfully.
- /// The trakt authorization code.
- [HttpPost("Users/{userId}/Authorize")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- public async Task> TraktDeviceAuthorization([FromRoute] string userId)
+ string userCode = await _traktApi.AuthorizeDevice(traktUser).ConfigureAwait(false);
+
+ return new
{
- _logger.LogInformation("TraktDeviceAuthorization request received");
+ userCode
+ };
+ }
- // Create a user if we don't have one yet - TODO there should be an endpoint for this that creates a default user
- var traktUser = UserHelper.GetTraktUser(userId);
- if (traktUser == null)
- {
- Plugin.Instance.PluginConfiguration.AddUser(userId);
- traktUser = UserHelper.GetTraktUser(userId);
- Plugin.Instance.SaveConfiguration();
- }
- string userCode = await _traktApi.AuthorizeDevice(traktUser).ConfigureAwait(false);
+ ///
+ /// Poll the trakt device authorization status
+ ///
+ /// The user id.
+ /// Polling successful.
+ /// A value indicating whether the authorization code was connected to a trakt account.
+ [HttpGet("Users/{userId}/PollAuthorizationStatus")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public ActionResult