From dfd7d678e7e3628a9100ee775a6c26f5ca75bcc5 Mon Sep 17 00:00:00 2001 From: Josh Patra Date: Wed, 4 Feb 2026 23:42:16 -0500 Subject: [PATCH] Add internal API method and fix playlist count authentication - Added GetJsonAsyncInternal method to JellyfinProxyService for server-side requests - Uses server API key instead of client tokens for internal operations - Updated UpdateSpotifyPlaylistCounts to use internal method with proper authentication - This should resolve 401 Unauthorized errors when updating playlist counts Now Spotify playlists should show correct track counts without authentication issues. --- allstarr/Controllers/JellyfinController.cs | 8 ++-- .../Services/Jellyfin/JellyfinProxyService.cs | 39 +++++++++++++++++++ 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/allstarr/Controllers/JellyfinController.cs b/allstarr/Controllers/JellyfinController.cs index 4b540c1..b9668f8 100644 --- a/allstarr/Controllers/JellyfinController.cs +++ b/allstarr/Controllers/JellyfinController.cs @@ -2823,15 +2823,15 @@ public class JellyfinController : ControllerBase // Include UserId parameter to avoid 401 Unauthorized var userId = _settings.UserId; var playlistItemsUrl = $"Playlists/{playlistId}/Items"; + var queryParams = new Dictionary(); if (!string.IsNullOrEmpty(userId)) { - playlistItemsUrl += $"?UserId={userId}"; + queryParams["UserId"] = userId; } - var (localTracksResponse, _) = await _proxyService.GetJsonAsync( + var (localTracksResponse, _) = await _proxyService.GetJsonAsyncInternal( playlistItemsUrl, - null, - Request.Headers); + queryParams); if (localTracksResponse != null && localTracksResponse.RootElement.TryGetProperty("Items", out var localItems)) diff --git a/allstarr/Services/Jellyfin/JellyfinProxyService.cs b/allstarr/Services/Jellyfin/JellyfinProxyService.cs index 187a7a8..80e0846 100644 --- a/allstarr/Services/Jellyfin/JellyfinProxyService.cs +++ b/allstarr/Services/Jellyfin/JellyfinProxyService.cs @@ -967,4 +967,43 @@ public class JellyfinProxyService return url; } + + /// + /// Sends a GET request to the Jellyfin server using the server's API key for internal operations. + /// This should only be used for server-side operations, not for proxying client requests. + /// + public async Task<(JsonDocument? Body, int StatusCode)> GetJsonAsyncInternal(string endpoint, Dictionary? queryParams = null) + { + var url = BuildUrl(endpoint, queryParams); + + using var request = new HttpRequestMessage(HttpMethod.Get, url); + + // Use server's API key for authentication + var authHeader = GetAuthorizationHeader(); + request.Headers.TryAddWithoutValidation("X-Emby-Authorization", authHeader); + + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + + var response = await _httpClient.SendAsync(request); + var statusCode = (int)response.StatusCode; + var content = await response.Content.ReadAsStringAsync(); + + if (!response.IsSuccessStatusCode) + { + _logger.LogWarning("Jellyfin internal request returned {StatusCode} for {Url}: {Content}", + statusCode, url, content); + return (null, statusCode); + } + + try + { + var jsonDocument = JsonDocument.Parse(content); + return (jsonDocument, statusCode); + } + catch (JsonException ex) + { + _logger.LogError(ex, "Failed to parse JSON response from {Url}: {Content}", url, content); + return (null, statusCode); + } + } }