From e103be43a98bcb81b22347b407970bbb3e837a2d Mon Sep 17 00:00:00 2001 From: Alexander Rufenach Date: Thu, 3 Jun 2021 08:40:34 -0400 Subject: [PATCH] Cleaned up scrobble logic and refined API calls --- Jellyfin.Plugin.AnilistSync/API/AnilistApi.cs | 35 +++++++-- .../Services/PlaybackScrobbler.cs | 77 +++++++++++++------ 2 files changed, 81 insertions(+), 31 deletions(-) diff --git a/Jellyfin.Plugin.AnilistSync/API/AnilistApi.cs b/Jellyfin.Plugin.AnilistSync/API/AnilistApi.cs index 6322828..8f2736c 100644 --- a/Jellyfin.Plugin.AnilistSync/API/AnilistApi.cs +++ b/Jellyfin.Plugin.AnilistSync/API/AnilistApi.cs @@ -24,14 +24,14 @@ namespace Jellyfin.Plugin.AnilistSync.API private readonly IHttpClientFactory _httpClientFactory; private readonly JsonSerializerOptions _jsonSerializerOptions; - private const string listIdQuery = @"mutation ($mediaId: Int) {SaveMediaListEntry(mediaId: $mediaId) {id, status, repeat}}&variables={""mediaId"": ""{0}""}"; + private const string listIdQuery = @"mutation ($mediaId: Int) {SaveMediaListEntry(mediaId: $mediaId) {id, status, repeat, progress}}&variables={""mediaId"": ""{0}""}"; private const string listUpdateQuery = @"mutation ($id: Int, $progress: Int, $status: MediaListStatus, $repeat: Int) {SaveMediaListEntry(id: $id, progress: $progress, status: $status, repeat: $repeat) {id, progress, status, repeat}}"; + private const string listStatusUpdateQuery = @"mutation ($id: Int, $status: MediaListStatus) {SaveMediaListEntry(id: $id, status: $status) {id, status}}"; private const string episodeQuery = @"query ($id: Int) {Media (id: $id) {episodes}}"; private const string currentUserQuery = @"query {Viewer {id, name}}"; private const string listUpdateVars1 = @"&variables={""id"":""{0}"", ""progress"":""{1}""}"; - private const string listUpdateVars2 = @"&variables={""id"":""{0}"", ""progress"":""{1}"", ""status"":""{2}""}"; - private const string listUpdateVars3 = @"&variables={""id"":""{0}"", ""progress"":""{1}"", ""status"":""{2}"", ""repeat"":""{3}""}"; + private const string listUpdateVars2 = @"&variables={""id"":""{0}"", ""status"":""{1}""}"; public const string BaseOauthUrl = @"https://anilist.co/api/v2"; public const string BaseGraphQLUrl = @"https://graphql.anilist.co/api/v2?query="; @@ -115,17 +115,36 @@ namespace Jellyfin.Plugin.AnilistSync.API return data; } - public async Task PostListUpdate(string? anilistMediaId, string? userToken, int? progress, MediaListStatus? status, int? timesRewatched) + public async Task PostListStatusUpdate(string? anilistMediaId, string? userToken, MediaListStatus? status) + { + var requestMessage = new HttpRequestMessage(); + requestMessage.RequestUri = new Uri( + BaseGraphQLUrl + + listStatusUpdateQuery + + listUpdateVars2 + .Replace("{0}", anilistMediaId) + .Replace("{1}", status.ToString())); + requestMessage.Method = HttpMethod.Post; + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", userToken); + requestMessage.Content = new StringContent("", Encoding.UTF8, "application/json"); + var responseMessage = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(requestMessage); + var data = await responseMessage.Content.ReadFromJsonAsync(_jsonSerializerOptions); + if (data?.Errors != null) + { + throw new AnilistAPIException(data.Errors); + } + return data; + } + + public async Task PostListProgressUpdate(string? anilistMediaId, string? userToken, int? progress) { var requestMessage = new HttpRequestMessage(); requestMessage.RequestUri = new Uri( BaseGraphQLUrl + listUpdateQuery + - listUpdateVars3 + listUpdateVars1 .Replace("{0}", anilistMediaId) - .Replace("{1}", progress.ToString()) - .Replace("{2}", status.ToString()) - .Replace("{3}", timesRewatched.ToString())); + .Replace("{1}", progress.ToString())); requestMessage.Method = HttpMethod.Post; requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", userToken); requestMessage.Content = new StringContent("", Encoding.UTF8, "application/json"); diff --git a/Jellyfin.Plugin.AnilistSync/Services/PlaybackScrobbler.cs b/Jellyfin.Plugin.AnilistSync/Services/PlaybackScrobbler.cs index 0dc0c55..3f12896 100644 --- a/Jellyfin.Plugin.AnilistSync/Services/PlaybackScrobbler.cs +++ b/Jellyfin.Plugin.AnilistSync/Services/PlaybackScrobbler.cs @@ -173,30 +173,63 @@ namespace Jellyfin.Plugin.AnilistSync.Services var listEntry = _anilistApi.GetListEntry(anilistId, userConfig.UserToken).Result?.Data?.ListEntry; int? currentIndex = eventArgs.Item.IndexNumber; - + int? currentRemoteIndex = listEntry?.Progress; int? timesRewatched = listEntry?.TimesRewatched; MediaListStatus? status = listEntry?.Status; - // Check if STARTING a rewatch - if (status == MediaListStatus.COMPLETED) + switch (status) { - if (userConfig.ScrobbleRewatches) - { - if (currentIndex == 1) // Check rewatching first episode + case MediaListStatus.COMPLETED: // Check if STARTING a rewatch + if (userConfig.ScrobbleRewatches) { - status = MediaListStatus.REPEATING; + if (currentIndex == 1) // Only initialize a rewatch if watching first episode + { + status = MediaListStatus.REPEATING; + await _anilistApi.PostListStatusUpdate(listEntry?.Id.ToString(), userConfig.UserToken, status); + _logger.LogInformation("Rewatch started"); + } + else + { + _logger.LogInformation("Attempting to start rewatch from middle episode, discarding scrobble"); + return; + } } else { - _logger.LogDebug("Attempting to start rewatch from middle episode, discarding scrobble"); + _logger.LogInformation("User has chosen not to scrobble rewatches"); return; - } - } - else - { - _logger.LogDebug("User has chosen not to scrobble rewatches"); - return; - } + } + break; + case MediaListStatus.REPEATING: + if (userConfig.ScrobbleRewatches) + { + if (currentIndex <= currentRemoteIndex) + { + _logger.LogInformation("Episode number <= Anilist episode watch count, discarding scrobble"); + return; + } + } + else + { + _logger.LogInformation("User has chosen not to scrobble rewatches"); + return; + } + break; + case MediaListStatus.PLANNING: + case MediaListStatus.DROPPED: + case MediaListStatus.PAUSED: + status = MediaListStatus.CURRENT; + await _anilistApi.PostListStatusUpdate(listEntry?.Id.ToString(), userConfig.UserToken, status); + break; + case MediaListStatus.CURRENT: + if (currentIndex <= currentRemoteIndex) + { + _logger.LogInformation("Episode number <= Anilist episode watch count, discarding scrobble"); + return; + } + break; + default: + break; } // Get total number of episodes of current item from Anilist @@ -207,17 +240,15 @@ namespace Jellyfin.Plugin.AnilistSync.Services // If watching LAST episode change status to completed if (currentIndex == episodes) { - if (status == MediaListStatus.REPEATING) - { - timesRewatched += 1; - } status = MediaListStatus.COMPLETED; + var statusResponse = await _anilistApi.PostListStatusUpdate(listEntry?.Id.ToString(), userConfig.UserToken, status); + } + else + { + var response = await _anilistApi.PostListProgressUpdate(listEntry?.Id.ToString(), userConfig.UserToken, currentIndex); } _logger.LogInformation("Current watch status: {status}", status); - - // Send post request to API to update list - var response = await _anilistApi.PostListUpdate(listEntry?.Id.ToString(), userConfig.UserToken, currentIndex, status, timesRewatched); - _logger.LogDebug("Scrobbled without errors"); + _logger.LogInformation("Scrobbled without errors"); _lastScrobbled[eventArgs.Session.Id] = eventArgs.MediaInfo.Id; }