diff --git a/.env.example b/.env.example index 5953953..6352368 100644 --- a/.env.example +++ b/.env.example @@ -116,7 +116,7 @@ SPOTIFY_IMPORT_SYNC_START_MINUTE=15 # Example: If plugin runs at 4:15 PM and window is 2 hours, checks from 4:00 PM to 6:00 PM SPOTIFY_IMPORT_SYNC_WINDOW_HOURS=2 -# Playlists to inject (comma-separated, optional) -# Available: Release Radar, Discover Weekly -# Leave empty to disable all, or specify which ones to enable -SPOTIFY_IMPORT_PLAYLISTS=Release Radar,Discover Weekly +# Playlist IDs to inject (comma-separated) +# Get IDs from Jellyfin playlist URLs: https://jellyfin.example.com/web/#/details?id=PLAYLIST_ID +# Example: SPOTIFY_IMPORT_PLAYLIST_IDS=4383a46d8bcac3be2ef9385053ea18df,ba50e26c867ec9d57ab2f7bf24cfd6b0 +SPOTIFY_IMPORT_PLAYLIST_IDS= diff --git a/allstarr/Controllers/JellyfinController.cs b/allstarr/Controllers/JellyfinController.cs index 92186c9..fb2b24e 100644 --- a/allstarr/Controllers/JellyfinController.cs +++ b/allstarr/Controllers/JellyfinController.cs @@ -1281,36 +1281,23 @@ public class JellyfinController : ControllerBase return _responseBuilder.CreateItemsResponse(tracks); } - // Only check for Spotify playlists if the feature is enabled - _logger.LogInformation("Spotify Import Enabled: {Enabled}, Playlist starts with ext-: {IsExternal}", - _spotifySettings.Enabled, playlistId.StartsWith("ext-")); + // Check if this is a Spotify playlist (by ID) + _logger.LogInformation("Spotify Import Enabled: {Enabled}, Configured IDs: {Count}", + _spotifySettings.Enabled, _spotifySettings.PlaylistIds.Count); - if (_spotifySettings.Enabled && !playlistId.StartsWith("ext-")) + if (_spotifySettings.Enabled && + _spotifySettings.PlaylistIds.Any(id => id.Equals(playlistId, StringComparison.OrdinalIgnoreCase))) { - // Get playlist info from Jellyfin to check the name + // Get playlist info from Jellyfin to get the name for matching missing tracks _logger.LogInformation("Fetching playlist info from Jellyfin for ID: {PlaylistId}", playlistId); var playlistInfo = await _proxyService.GetJsonAsync($"Items/{playlistId}", null, Request.Headers); if (playlistInfo != null && playlistInfo.RootElement.TryGetProperty("Name", out var nameElement)) { var playlistName = nameElement.GetString() ?? ""; - _logger.LogInformation("Jellyfin playlist name: '{PlaylistName}'", playlistName); - - // Check if this matches any configured Spotify playlists - var matchingConfig = _spotifySettings.Playlists - .FirstOrDefault(p => p.Enabled && p.Name.Equals(playlistName, StringComparison.OrdinalIgnoreCase)); - - if (matchingConfig != null) - { - _logger.LogInformation("✓ MATCHED! Intercepting Spotify playlist: {PlaylistName}", playlistName); - return await GetSpotifyPlaylistTracksAsync(matchingConfig.SpotifyName); - } - else - { - _logger.LogInformation("✗ No match found for playlist '{PlaylistName}'", playlistName); - _logger.LogInformation("Configured playlists: {Playlists}", - string.Join(", ", _spotifySettings.Playlists.Select(p => $"'{p.Name}' (enabled={p.Enabled})"))); - } + _logger.LogInformation("✓ MATCHED! Intercepting Spotify playlist: {PlaylistName} (ID: {PlaylistId})", + playlistName, playlistId); + return await GetSpotifyPlaylistTracksAsync(playlistName); } else { @@ -1705,48 +1692,34 @@ public class JellyfinController : ControllerBase // DEBUG: Log EVERY request to see what's happening _logger.LogWarning("ProxyRequest called with path: {Path}", path); - // DEBUG: Log Spotify settings for playlist requests - if (path.Contains("playlist", StringComparison.OrdinalIgnoreCase)) - { - _logger.LogWarning("=== PLAYLIST REQUEST DEBUG ==="); - _logger.LogWarning("Path: {Path}", path); - _logger.LogWarning("Spotify Enabled: {Enabled}", _spotifySettings.Enabled); - _logger.LogWarning("Starts with 'playlists/': {StartsWith}", path.StartsWith("playlists/", StringComparison.OrdinalIgnoreCase)); - _logger.LogWarning("Contains '/items': {Contains}", path.Contains("/items", StringComparison.OrdinalIgnoreCase)); - _logger.LogWarning("Playlists count: {Count}", _spotifySettings.Playlists.Count); - } - - // Intercept Spotify playlist requests FIRST + // Intercept Spotify playlist requests by ID if (_spotifySettings.Enabled && path.StartsWith("playlists/", StringComparison.OrdinalIgnoreCase) && path.Contains("/items", StringComparison.OrdinalIgnoreCase)) { - _logger.LogInformation("========================================"); - _logger.LogInformation("=== SPOTIFY PLAYLIST REQUEST INTERCEPTED ==="); - _logger.LogInformation("Path: {Path}", path); - _logger.LogInformation("Spotify Import Enabled: {Enabled}", _spotifySettings.Enabled); - _logger.LogInformation("Configured Playlists: {Count}", _spotifySettings.Playlists.Count); - foreach (var p in _spotifySettings.Playlists) - { - _logger.LogInformation(" - {Name} (SpotifyName: {SpotifyName}, Enabled: {Enabled})", - p.Name, p.SpotifyName, p.Enabled); - } - _logger.LogInformation("========================================"); - // Extract playlist ID from path: playlists/{id}/items var parts = path.Split('/', StringSplitOptions.RemoveEmptyEntries); if (parts.Length >= 2 && parts[0].Equals("playlists", StringComparison.OrdinalIgnoreCase)) { var playlistId = parts[1]; - _logger.LogInformation("Extracted playlist ID: {PlaylistId}", playlistId); - return await GetPlaylistTracks(playlistId); + + _logger.LogWarning("=== PLAYLIST REQUEST ==="); + _logger.LogWarning("Playlist ID: {PlaylistId}", playlistId); + _logger.LogWarning("Spotify Enabled: {Enabled}", _spotifySettings.Enabled); + _logger.LogWarning("Configured IDs: {Ids}", string.Join(", ", _spotifySettings.PlaylistIds)); + _logger.LogWarning("Is configured: {IsConfigured}", _spotifySettings.PlaylistIds.Contains(playlistId, StringComparer.OrdinalIgnoreCase)); + + // Check if this playlist ID is configured for Spotify injection + if (_spotifySettings.PlaylistIds.Any(id => id.Equals(playlistId, StringComparison.OrdinalIgnoreCase))) + { + _logger.LogInformation("========================================"); + _logger.LogInformation("=== INTERCEPTING SPOTIFY PLAYLIST ==="); + _logger.LogInformation("Playlist ID: {PlaylistId}", playlistId); + _logger.LogInformation("========================================"); + return await GetPlaylistTracks(playlistId); + } } } - - // Intercept Spotify playlist requests (alternative path format) - if (_spotifySettings.Enabled && - path.StartsWith("playlists/", StringComparison.OrdinalIgnoreCase) && - path.EndsWith("/items", StringComparison.OrdinalIgnoreCase)) { // Extract playlist ID from path: playlists/{id}/items var parts = path.Split('/'); @@ -2009,36 +1982,27 @@ public class JellyfinController : ControllerBase { try { - var matchingPlaylist = _spotifySettings.Playlists - .FirstOrDefault(p => p.SpotifyName.Equals(spotifyPlaylistName, StringComparison.OrdinalIgnoreCase)); - - if (matchingPlaylist == null) - { - _logger.LogWarning("Spotify playlist not found in config: {PlaylistName}", spotifyPlaylistName); - return _responseBuilder.CreateItemsResponse(new List()); - } - - var cacheKey = $"spotify:matched:{matchingPlaylist.SpotifyName}"; + var cacheKey = $"spotify:matched:{spotifyPlaylistName}"; var cachedTracks = await _cache.GetAsync>(cacheKey); if (cachedTracks != null) { _logger.LogDebug("Returning {Count} cached matched tracks for {Playlist}", - cachedTracks.Count, matchingPlaylist.Name); + cachedTracks.Count, spotifyPlaylistName); return _responseBuilder.CreateItemsResponse(cachedTracks); } - var missingTracksKey = $"spotify:missing:{matchingPlaylist.SpotifyName}"; + var missingTracksKey = $"spotify:missing:{spotifyPlaylistName}"; var missingTracks = await _cache.GetAsync>(missingTracksKey); if (missingTracks == null || missingTracks.Count == 0) { - _logger.LogInformation("No missing tracks found for {Playlist}", matchingPlaylist.Name); + _logger.LogInformation("No missing tracks found for {Playlist}", spotifyPlaylistName); return _responseBuilder.CreateItemsResponse(new List()); } _logger.LogInformation("Matching {Count} tracks for {Playlist}", - missingTracks.Count, matchingPlaylist.Name); + missingTracks.Count, spotifyPlaylistName); var matchTasks = missingTracks.Select(async track => { @@ -2064,7 +2028,7 @@ public class JellyfinController : ControllerBase await _cache.SetAsync(cacheKey, matchedTracks, TimeSpan.FromHours(1)); _logger.LogInformation("Matched {Matched}/{Total} tracks for {Playlist}", - matchedTracks.Count, missingTracks.Count, matchingPlaylist.Name); + matchedTracks.Count, missingTracks.Count, spotifyPlaylistName); return _responseBuilder.CreateItemsResponse(matchedTracks); } diff --git a/allstarr/Models/Settings/SpotifyImportSettings.cs b/allstarr/Models/Settings/SpotifyImportSettings.cs index b082c30..22e4104 100644 --- a/allstarr/Models/Settings/SpotifyImportSettings.cs +++ b/allstarr/Models/Settings/SpotifyImportSettings.cs @@ -31,29 +31,9 @@ public class SpotifyImportSettings public int SyncWindowHours { get; set; } = 2; /// - /// List of playlists to inject + /// Comma-separated list of Jellyfin playlist IDs to inject + /// Example: "4383a46d8bcac3be2ef9385053ea18df,ba50e26c867ec9d57ab2f7bf24cfd6b0" + /// Get IDs from Jellyfin playlist URLs /// - public List Playlists { get; set; } = new(); -} - -/// -/// Configuration for a single Spotify playlist -/// -public class SpotifyPlaylistConfig -{ - /// - /// Display name in Jellyfin (e.g., "Release Radar") - /// - public string Name { get; set; } = string.Empty; - - /// - /// Playlist name in Spotify Import plugin missing tracks file - /// Must match exactly (e.g., "Release Radar") - /// - public string SpotifyName { get; set; } = string.Empty; - - /// - /// Enable this playlist - /// - public bool Enabled { get; set; } = true; + public List PlaylistIds { get; set; } = new(); } diff --git a/allstarr/Program.cs b/allstarr/Program.cs index f7564c2..60eacf4 100644 --- a/allstarr/Program.cs +++ b/allstarr/Program.cs @@ -113,29 +113,23 @@ builder.Services.Configure(options => { builder.Configuration.GetSection("SpotifyImport").Bind(options); - // Parse SPOTIFY_IMPORT_PLAYLISTS env var (comma-separated) into Playlists array - var playlistsEnv = builder.Configuration.GetValue("SpotifyImport:Playlists"); - if (!string.IsNullOrWhiteSpace(playlistsEnv) && options.Playlists.Count == 0) + // Parse SPOTIFY_IMPORT_PLAYLIST_IDS env var (comma-separated) into PlaylistIds array + var playlistIdsEnv = builder.Configuration.GetValue("SpotifyImport:PlaylistIds"); + if (!string.IsNullOrWhiteSpace(playlistIdsEnv) && options.PlaylistIds.Count == 0) { - options.Playlists = playlistsEnv + options.PlaylistIds = playlistIdsEnv .Split(',', StringSplitOptions.RemoveEmptyEntries) - .Select(name => name.Trim()) - .Where(name => !string.IsNullOrEmpty(name)) - .Select(name => new SpotifyPlaylistConfig - { - Name = name, - SpotifyName = name, - Enabled = true - }) + .Select(id => id.Trim()) + .Where(id => !string.IsNullOrEmpty(id)) .ToList(); } // Log configuration at startup Console.WriteLine($"Spotify Import: Enabled={options.Enabled}, SyncHour={options.SyncStartHour}:{options.SyncStartMinute:D2}, WindowHours={options.SyncWindowHours}"); - Console.WriteLine($"Spotify Import Playlists: {options.Playlists.Count} configured"); - foreach (var p in options.Playlists) + Console.WriteLine($"Spotify Import Playlist IDs: {options.PlaylistIds.Count} configured"); + foreach (var id in options.PlaylistIds) { - Console.WriteLine($" - {p.Name} (SpotifyName: {p.SpotifyName}, Enabled: {p.Enabled})"); + Console.WriteLine($" - {id}"); } });