diff --git a/allstarr/Controllers/AdminController.cs b/allstarr/Controllers/AdminController.cs index a9e2aca..33adaed 100644 --- a/allstarr/Controllers/AdminController.cs +++ b/allstarr/Controllers/AdminController.cs @@ -2044,11 +2044,16 @@ public class AdminController : ControllerBase trackStats = await GetPlaylistTrackStats(id!); } + // Use actual track stats for configured playlists, otherwise use Jellyfin's count + var actualTrackCount = isConfigured + ? trackStats.LocalTracks + trackStats.ExternalTracks + : childCount; + playlists.Add(new { id, name, - trackCount = childCount, + trackCount = actualTrackCount, linkedSpotifyId, isConfigured, localTracks = trackStats.LocalTracks, diff --git a/allstarr/Services/Jellyfin/JellyfinSessionManager.cs b/allstarr/Services/Jellyfin/JellyfinSessionManager.cs index 97e4882..494cc1c 100644 --- a/allstarr/Services/Jellyfin/JellyfinSessionManager.cs +++ b/allstarr/Services/Jellyfin/JellyfinSessionManager.cs @@ -85,6 +85,10 @@ public class JellyfinSessionManager : IDisposable _logger.LogDebug("Session created for {DeviceId}", deviceId); // Track this session + var clientIp = headers["X-Forwarded-For"].FirstOrDefault()?.Split(',')[0].Trim() + ?? headers["X-Real-IP"].FirstOrDefault() + ?? "Unknown"; + _sessions[deviceId] = new SessionInfo { DeviceId = deviceId, @@ -92,7 +96,8 @@ public class JellyfinSessionManager : IDisposable Device = device, Version = version, LastActivity = DateTime.UtcNow, - Headers = CloneHeaders(headers) + Headers = CloneHeaders(headers), + ClientIp = clientIp }; // Start a WebSocket connection to Jellyfin on behalf of this client @@ -222,6 +227,7 @@ public class JellyfinSessionManager : IDisposable Client = s.Client, Device = s.Device, Version = s.Version, + ClientIp = s.ClientIp, LastActivity = s.LastActivity, InactiveMinutes = Math.Round((now - s.LastActivity).TotalMinutes, 1), HasWebSocket = s.WebSocket != null, @@ -565,6 +571,7 @@ public class JellyfinSessionManager : IDisposable public ClientWebSocket? WebSocket { get; set; } public string? LastPlayingItemId { get; set; } public long? LastPlayingPositionTicks { get; set; } + public string? ClientIp { get; set; } } public void Dispose() diff --git a/allstarr/Services/Spotify/SpotifyApiClient.cs b/allstarr/Services/Spotify/SpotifyApiClient.cs index c66e6b8..cc348e5 100644 --- a/allstarr/Services/Spotify/SpotifyApiClient.cs +++ b/allstarr/Services/Spotify/SpotifyApiClient.cs @@ -838,6 +838,17 @@ public class SpotifyApiClient : IDisposable continue; } + // Check __typename to filter out folders and only include playlists + if (playlistItem.TryGetProperty("__typename", out var typename)) + { + var typeStr = typename.GetString(); + // Skip folders - only process Playlist types + if (typeStr != null && typeStr.Contains("Folder", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + } + // Get playlist URI/ID string? uri = null; if (playlistItem.TryGetProperty("uri", out var uriProp)) @@ -851,6 +862,12 @@ public class SpotifyApiClient : IDisposable if (string.IsNullOrEmpty(uri)) continue; + // Skip if not a playlist URI (e.g., folders have different URI format) + if (!uri.StartsWith("spotify:playlist:", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + var spotifyId = uri.Replace("spotify:playlist:", "", StringComparison.OrdinalIgnoreCase); var itemName = playlist.TryGetProperty("name", out var n) ? n.GetString() ?? "" : ""; @@ -862,12 +879,32 @@ public class SpotifyApiClient : IDisposable continue; } - // Get track count if available + // Get track count if available - try multiple possible paths var trackCount = 0; - if (playlist.TryGetProperty("content", out var content) && - content.TryGetProperty("totalCount", out var totalTrackCount)) + if (playlist.TryGetProperty("content", out var content)) { - trackCount = totalTrackCount.GetInt32(); + if (content.TryGetProperty("totalCount", out var totalTrackCount)) + { + trackCount = totalTrackCount.GetInt32(); + } + } + // Fallback: try attributes.itemCount + else if (playlist.TryGetProperty("attributes", out var attributes) && + attributes.TryGetProperty("itemCount", out var itemCountProp)) + { + trackCount = itemCountProp.GetInt32(); + } + // Fallback: try totalCount directly + else if (playlist.TryGetProperty("totalCount", out var directTotalCount)) + { + trackCount = directTotalCount.GetInt32(); + } + + // Log if we couldn't find track count for debugging + if (trackCount == 0) + { + _logger.LogDebug("Could not find track count for playlist {Name} (ID: {Id}). Response structure: {Json}", + itemName, spotifyId, playlist.GetRawText()); } // Get owner name