mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Combine Spotify playlist config into single JSON array with local track position option
This commit is contained in:
20
.env.example
20
.env.example
@@ -126,13 +126,13 @@ 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
|
# 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
|
SPOTIFY_IMPORT_SYNC_WINDOW_HOURS=2
|
||||||
|
|
||||||
# Playlist IDs to inject (comma-separated)
|
# Playlists configuration (JSON array format - combines name, ID, and local track position)
|
||||||
# Get IDs from Jellyfin playlist URLs: https://jellyfin.example.com/web/#/details?id=PLAYLIST_ID
|
# Format: [["PlaylistName","JellyfinPlaylistId","first|last"],...]
|
||||||
# Example: SPOTIFY_IMPORT_PLAYLIST_IDS=4383a46d8bcac3be2ef9385053ea18df,ba50e26c867ec9d57ab2f7bf24cfd6b0
|
# - PlaylistName: Name as it appears in Jellyfin (e.g., "Discover Weekly", "Release Radar")
|
||||||
SPOTIFY_IMPORT_PLAYLIST_IDS=
|
# - JellyfinPlaylistId: Get from playlist URL: https://jellyfin.example.com/web/#/details?id=PLAYLIST_ID
|
||||||
|
# - first|last: Where to position local tracks relative to external tracks
|
||||||
# Playlist names (comma-separated, must match Spotify Import plugin format)
|
# - "first": Local tracks appear first, external tracks at the end (default)
|
||||||
# IMPORTANT: Use the exact playlist names as they appear in Jellyfin
|
# - "last": External tracks appear first, local tracks at the end
|
||||||
# Must be in same order as SPOTIFY_IMPORT_PLAYLIST_IDS
|
# Example with 2 playlists:
|
||||||
# Example: SPOTIFY_IMPORT_PLAYLIST_NAMES=Discover Weekly,Release Radar
|
# SPOTIFY_IMPORT_PLAYLISTS=[["Discover Weekly","4383a46d8bcac3be2ef9385053ea18df","first"],["Release Radar","ba50e26c867ec9d57ab2f7bf24cfd6b0","last"]]
|
||||||
SPOTIFY_IMPORT_PLAYLIST_NAMES=
|
SPOTIFY_IMPORT_PLAYLISTS=
|
||||||
|
|||||||
@@ -1366,11 +1366,10 @@ public class JellyfinController : ControllerBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a Spotify playlist (by ID)
|
// Check if this is a Spotify playlist (by ID)
|
||||||
_logger.LogInformation("Spotify Import Enabled: {Enabled}, Configured IDs: {Count}",
|
_logger.LogInformation("Spotify Import Enabled: {Enabled}, Configured Playlists: {Count}",
|
||||||
_spotifySettings.Enabled, _spotifySettings.PlaylistIds.Count);
|
_spotifySettings.Enabled, _spotifySettings.Playlists.Count);
|
||||||
|
|
||||||
if (_spotifySettings.Enabled &&
|
if (_spotifySettings.Enabled && _spotifySettings.IsSpotifyPlaylist(playlistId))
|
||||||
_spotifySettings.PlaylistIds.Any(id => id.Equals(playlistId, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
{
|
||||||
// Get playlist info from Jellyfin to get the name for matching missing tracks
|
// Get playlist info from Jellyfin to get the name for matching missing tracks
|
||||||
_logger.LogInformation("Fetching playlist info from Jellyfin for ID: {PlaylistId}", playlistId);
|
_logger.LogInformation("Fetching playlist info from Jellyfin for ID: {PlaylistId}", playlistId);
|
||||||
@@ -2242,11 +2241,11 @@ public class JellyfinController : ControllerBase
|
|||||||
_logger.LogInformation("=== PLAYLIST REQUEST ===");
|
_logger.LogInformation("=== PLAYLIST REQUEST ===");
|
||||||
_logger.LogInformation("Playlist ID: {PlaylistId}", playlistId);
|
_logger.LogInformation("Playlist ID: {PlaylistId}", playlistId);
|
||||||
_logger.LogInformation("Spotify Enabled: {Enabled}", _spotifySettings.Enabled);
|
_logger.LogInformation("Spotify Enabled: {Enabled}", _spotifySettings.Enabled);
|
||||||
_logger.LogInformation("Configured IDs: {Ids}", string.Join(", ", _spotifySettings.PlaylistIds));
|
_logger.LogInformation("Configured Playlists: {Playlists}", string.Join(", ", _spotifySettings.Playlists.Select(p => $"{p.Name}:{p.Id}")));
|
||||||
_logger.LogInformation("Is configured: {IsConfigured}", _spotifySettings.PlaylistIds.Contains(playlistId, StringComparer.OrdinalIgnoreCase));
|
_logger.LogInformation("Is configured: {IsConfigured}", _spotifySettings.IsSpotifyPlaylist(playlistId));
|
||||||
|
|
||||||
// Check if this playlist ID is configured for Spotify injection
|
// Check if this playlist ID is configured for Spotify injection
|
||||||
if (_spotifySettings.PlaylistIds.Any(id => id.Equals(playlistId, StringComparison.OrdinalIgnoreCase)))
|
if (_spotifySettings.IsSpotifyPlaylist(playlistId))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("========================================");
|
_logger.LogInformation("========================================");
|
||||||
_logger.LogInformation("=== INTERCEPTING SPOTIFY PLAYLIST ===");
|
_logger.LogInformation("=== INTERCEPTING SPOTIFY PLAYLIST ===");
|
||||||
@@ -2502,18 +2501,16 @@ public class JellyfinController : ControllerBase
|
|||||||
var playlistId = idProp.GetString();
|
var playlistId = idProp.GetString();
|
||||||
_logger.LogDebug("Checking item with ID: {Id}", playlistId);
|
_logger.LogDebug("Checking item with ID: {Id}", playlistId);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(playlistId) &&
|
if (!string.IsNullOrEmpty(playlistId) && _spotifySettings.IsSpotifyPlaylist(playlistId))
|
||||||
_spotifySettings.PlaylistIds.Any(id => id.Equals(playlistId, StringComparison.OrdinalIgnoreCase)))
|
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Found Spotify playlist: {Id}", playlistId);
|
_logger.LogInformation("Found Spotify playlist: {Id}", playlistId);
|
||||||
|
|
||||||
// This is a Spotify playlist - get the actual track count
|
// This is a Spotify playlist - get the actual track count
|
||||||
var playlistIndex = _spotifySettings.PlaylistIds.FindIndex(id =>
|
var playlistConfig = _spotifySettings.GetPlaylistById(playlistId);
|
||||||
id.Equals(playlistId, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (playlistIndex >= 0 && playlistIndex < _spotifySettings.PlaylistNames.Count)
|
if (playlistConfig != null)
|
||||||
{
|
{
|
||||||
var playlistName = _spotifySettings.PlaylistNames[playlistIndex];
|
var playlistName = playlistConfig.Name;
|
||||||
var missingTracksKey = $"spotify:missing:{playlistName}";
|
var missingTracksKey = $"spotify:missing:{playlistName}";
|
||||||
var missingTracks = await _cache.GetAsync<List<allstarr.Models.Spotify.MissingTrack>>(missingTracksKey);
|
var missingTracks = await _cache.GetAsync<List<allstarr.Models.Spotify.MissingTrack>>(missingTracksKey);
|
||||||
|
|
||||||
@@ -2828,18 +2825,32 @@ public class JellyfinController : ControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build final track list: local tracks first, then matched external tracks
|
// Build final track list based on playlist configuration
|
||||||
// Local tracks are already in Jellyfin's playlist - just return them as-is
|
// Local tracks position is configurable per-playlist
|
||||||
// Append external matches at the end for tracks the plugin couldn't find locally
|
var playlistConfig = _spotifySettings.GetPlaylistById(playlistId);
|
||||||
var finalTracks = new List<Song>(existingTracks);
|
var localTracksPosition = playlistConfig?.LocalTracksPosition ?? LocalTracksPosition.First;
|
||||||
|
|
||||||
|
var finalTracks = new List<Song>();
|
||||||
|
if (localTracksPosition == LocalTracksPosition.First)
|
||||||
|
{
|
||||||
|
// Local tracks first, external tracks at the end
|
||||||
|
finalTracks.AddRange(existingTracks);
|
||||||
finalTracks.AddRange(matchedBySpotifyId.Values);
|
finalTracks.AddRange(matchedBySpotifyId.Values);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// External tracks first, local tracks at the end
|
||||||
|
finalTracks.AddRange(matchedBySpotifyId.Values);
|
||||||
|
finalTracks.AddRange(existingTracks);
|
||||||
|
}
|
||||||
|
|
||||||
await _cache.SetAsync(cacheKey, finalTracks, TimeSpan.FromHours(1));
|
await _cache.SetAsync(cacheKey, finalTracks, TimeSpan.FromHours(1));
|
||||||
|
|
||||||
_logger.LogInformation("Final playlist: {Total} tracks ({Existing} local + {Matched} external)",
|
_logger.LogInformation("Final playlist: {Total} tracks ({Existing} local + {Matched} external, LocalTracksPosition: {Position})",
|
||||||
finalTracks.Count,
|
finalTracks.Count,
|
||||||
existingTracks.Count,
|
existingTracks.Count,
|
||||||
matchedBySpotifyId.Count);
|
matchedBySpotifyId.Count,
|
||||||
|
localTracksPosition);
|
||||||
|
|
||||||
return _responseBuilder.CreateItemsResponse(finalTracks);
|
return _responseBuilder.CreateItemsResponse(finalTracks);
|
||||||
}
|
}
|
||||||
@@ -2982,25 +2993,22 @@ public class JellyfinController : ControllerBase
|
|||||||
|
|
||||||
// Check what was cached
|
// Check what was cached
|
||||||
var results = new Dictionary<string, object>();
|
var results = new Dictionary<string, object>();
|
||||||
for (int i = 0; i < _spotifySettings.PlaylistIds.Count; i++)
|
foreach (var playlist in _spotifySettings.Playlists)
|
||||||
{
|
{
|
||||||
var playlistName = i < _spotifySettings.PlaylistNames.Count
|
var cacheKey = $"spotify:missing:{playlist.Name}";
|
||||||
? _spotifySettings.PlaylistNames[i]
|
|
||||||
: _spotifySettings.PlaylistIds[i];
|
|
||||||
|
|
||||||
var cacheKey = $"spotify:missing:{playlistName}";
|
|
||||||
var tracks = await _cache.GetAsync<List<allstarr.Models.Spotify.MissingTrack>>(cacheKey);
|
var tracks = await _cache.GetAsync<List<allstarr.Models.Spotify.MissingTrack>>(cacheKey);
|
||||||
|
|
||||||
if (tracks != null && tracks.Count > 0)
|
if (tracks != null && tracks.Count > 0)
|
||||||
{
|
{
|
||||||
results[playlistName] = new {
|
results[playlist.Name] = new {
|
||||||
status = "success",
|
status = "success",
|
||||||
tracks = tracks.Count
|
tracks = tracks.Count,
|
||||||
|
localTracksPosition = playlist.LocalTracksPosition.ToString()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
results[playlistName] = new {
|
results[playlist.Name] = new {
|
||||||
status = "not_found",
|
status = "not_found",
|
||||||
message = "No missing tracks found"
|
message = "No missing tracks found"
|
||||||
};
|
};
|
||||||
@@ -3052,9 +3060,7 @@ public class JellyfinController : ControllerBase
|
|||||||
{
|
{
|
||||||
status = "started",
|
status = "started",
|
||||||
message = "Track matching started in background. Check logs for progress.",
|
message = "Track matching started in background. Check logs for progress.",
|
||||||
playlists = _spotifySettings.PlaylistNames.Count > 0
|
playlists = _spotifySettings.Playlists.Select(p => new { p.Name, p.Id, localTracksPosition = p.LocalTracksPosition.ToString() })
|
||||||
? _spotifySettings.PlaylistNames
|
|
||||||
: _spotifySettings.PlaylistIds
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3113,12 +3119,12 @@ public class JellyfinController : ControllerBase
|
|||||||
|
|
||||||
var cleared = new List<string>();
|
var cleared = new List<string>();
|
||||||
|
|
||||||
foreach (var playlistName in _spotifySettings.PlaylistNames)
|
foreach (var playlist in _spotifySettings.Playlists)
|
||||||
{
|
{
|
||||||
var matchedKey = $"spotify:matched:{playlistName}";
|
var matchedKey = $"spotify:matched:{playlist.Name}";
|
||||||
await _cache.DeleteAsync(matchedKey);
|
await _cache.DeleteAsync(matchedKey);
|
||||||
cleared.Add(playlistName);
|
cleared.Add(playlist.Name);
|
||||||
_logger.LogInformation("Cleared cache for {Playlist}", playlistName);
|
_logger.LogInformation("Cleared cache for {Playlist}", playlist.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(new { status = "success", cleared = cleared });
|
return Ok(new { status = "success", cleared = cleared });
|
||||||
|
|||||||
@@ -1,5 +1,44 @@
|
|||||||
namespace allstarr.Models.Settings;
|
namespace allstarr.Models.Settings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where to position local tracks relative to external matched tracks in Spotify playlists.
|
||||||
|
/// </summary>
|
||||||
|
public enum LocalTracksPosition
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Local tracks appear first, external tracks appended at the end (default)
|
||||||
|
/// </summary>
|
||||||
|
First,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// External tracks appear first, local tracks appended at the end
|
||||||
|
/// </summary>
|
||||||
|
Last
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuration for a single Spotify Import playlist.
|
||||||
|
/// </summary>
|
||||||
|
public class SpotifyPlaylistConfig
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Playlist name as it appears in Jellyfin/Spotify Import plugin
|
||||||
|
/// Example: "Discover Weekly", "Release Radar"
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Jellyfin playlist ID (get from playlist URL)
|
||||||
|
/// Example: "4383a46d8bcac3be2ef9385053ea18df"
|
||||||
|
/// </summary>
|
||||||
|
public string Id { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Where to position local tracks: "first" or "last"
|
||||||
|
/// </summary>
|
||||||
|
public LocalTracksPosition LocalTracksPosition { get; set; } = LocalTracksPosition.First;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Configuration for Spotify playlist injection feature.
|
/// Configuration for Spotify playlist injection feature.
|
||||||
/// Requires Jellyfin Spotify Import Plugin: https://github.com/Viperinius/jellyfin-plugin-spotify-import
|
/// Requires Jellyfin Spotify Import Plugin: https://github.com/Viperinius/jellyfin-plugin-spotify-import
|
||||||
@@ -34,17 +73,41 @@ public class SpotifyImportSettings
|
|||||||
public int SyncWindowHours { get; set; } = 2;
|
public int SyncWindowHours { get; set; } = 2;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Comma-separated list of Jellyfin playlist IDs to inject
|
/// Combined playlist configuration as JSON array.
|
||||||
/// Example: "4383a46d8bcac3be2ef9385053ea18df,ba50e26c867ec9d57ab2f7bf24cfd6b0"
|
/// Format: [["Name","Id","first|last"],...]
|
||||||
/// Get IDs from Jellyfin playlist URLs
|
/// Example: [["Discover Weekly","abc123","first"],["Release Radar","def456","last"]]
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
public List<SpotifyPlaylistConfig> Playlists { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Legacy: Comma-separated list of Jellyfin playlist IDs to inject
|
||||||
|
/// Deprecated: Use Playlists instead
|
||||||
|
/// </summary>
|
||||||
|
[Obsolete("Use Playlists instead")]
|
||||||
public List<string> PlaylistIds { get; set; } = new();
|
public List<string> PlaylistIds { get; set; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Comma-separated list of playlist names (must match Spotify Import plugin format)
|
/// Legacy: Comma-separated list of playlist names
|
||||||
/// Example: "Discover_Weekly,Release_Radar"
|
/// Deprecated: Use Playlists instead
|
||||||
/// Must be in same order as PlaylistIds
|
|
||||||
/// Plugin replaces spaces with underscores in filenames
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Obsolete("Use Playlists instead")]
|
||||||
public List<string> PlaylistNames { get; set; } = new();
|
public List<string> PlaylistNames { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the playlist configuration by Jellyfin playlist ID.
|
||||||
|
/// </summary>
|
||||||
|
public SpotifyPlaylistConfig? GetPlaylistById(string playlistId) =>
|
||||||
|
Playlists.FirstOrDefault(p => p.Id.Equals(playlistId, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the playlist configuration by name.
|
||||||
|
/// </summary>
|
||||||
|
public SpotifyPlaylistConfig? GetPlaylistByName(string name) =>
|
||||||
|
Playlists.FirstOrDefault(p => p.Name.Equals(name, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a playlist ID is configured for Spotify import.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSpotifyPlaylist(string playlistId) =>
|
||||||
|
Playlists.Any(p => p.Id.Equals(playlistId, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -117,7 +117,47 @@ builder.Services.Configure<SpotifyImportSettings>(options =>
|
|||||||
{
|
{
|
||||||
builder.Configuration.GetSection("SpotifyImport").Bind(options);
|
builder.Configuration.GetSection("SpotifyImport").Bind(options);
|
||||||
|
|
||||||
// Parse SPOTIFY_IMPORT_PLAYLIST_IDS env var (comma-separated) into PlaylistIds array
|
// Parse SPOTIFY_IMPORT_PLAYLISTS env var (JSON array format)
|
||||||
|
// Format: [["Name","Id","first|last"],["Name2","Id2","first|last"]]
|
||||||
|
var playlistsEnv = builder.Configuration.GetValue<string>("SpotifyImport:Playlists");
|
||||||
|
if (!string.IsNullOrWhiteSpace(playlistsEnv) && options.Playlists.Count == 0)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Parse as JSON array of arrays
|
||||||
|
var playlistArrays = System.Text.Json.JsonSerializer.Deserialize<string[][]>(playlistsEnv);
|
||||||
|
if (playlistArrays != null)
|
||||||
|
{
|
||||||
|
foreach (var arr in playlistArrays)
|
||||||
|
{
|
||||||
|
if (arr.Length >= 2)
|
||||||
|
{
|
||||||
|
var config = new SpotifyPlaylistConfig
|
||||||
|
{
|
||||||
|
Name = arr[0].Trim(),
|
||||||
|
Id = arr[1].Trim(),
|
||||||
|
LocalTracksPosition = arr.Length >= 3 &&
|
||||||
|
arr[2].Trim().Equals("last", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? LocalTracksPosition.Last
|
||||||
|
: LocalTracksPosition.First
|
||||||
|
};
|
||||||
|
options.Playlists.Add(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Text.Json.JsonException ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Warning: Failed to parse SPOTIFY_IMPORT_PLAYLISTS: {ex.Message}");
|
||||||
|
Console.WriteLine("Expected format: [[\"Name\",\"Id\",\"first|last\"],[\"Name2\",\"Id2\",\"first|last\"]]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Legacy support: Parse old SPOTIFY_IMPORT_PLAYLIST_IDS/NAMES env vars
|
||||||
|
// Only used if new Playlists format is not configured
|
||||||
|
if (options.Playlists.Count == 0)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618 // Type or member is obsolete
|
||||||
var playlistIdsEnv = builder.Configuration.GetValue<string>("SpotifyImport:PlaylistIds");
|
var playlistIdsEnv = builder.Configuration.GetValue<string>("SpotifyImport:PlaylistIds");
|
||||||
if (!string.IsNullOrWhiteSpace(playlistIdsEnv) && options.PlaylistIds.Count == 0)
|
if (!string.IsNullOrWhiteSpace(playlistIdsEnv) && options.PlaylistIds.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -128,7 +168,6 @@ builder.Services.Configure<SpotifyImportSettings>(options =>
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse SPOTIFY_IMPORT_PLAYLIST_NAMES env var (comma-separated) into PlaylistNames array
|
|
||||||
var playlistNamesEnv = builder.Configuration.GetValue<string>("SpotifyImport:PlaylistNames");
|
var playlistNamesEnv = builder.Configuration.GetValue<string>("SpotifyImport:PlaylistNames");
|
||||||
if (!string.IsNullOrWhiteSpace(playlistNamesEnv) && options.PlaylistNames.Count == 0)
|
if (!string.IsNullOrWhiteSpace(playlistNamesEnv) && options.PlaylistNames.Count == 0)
|
||||||
{
|
{
|
||||||
@@ -139,13 +178,26 @@ builder.Services.Configure<SpotifyImportSettings>(options =>
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log configuration at startup
|
// Convert legacy format to new Playlists array
|
||||||
Console.WriteLine($"Spotify Import: Enabled={options.Enabled}, SyncHour={options.SyncStartHour}:{options.SyncStartMinute:D2}, WindowHours={options.SyncWindowHours}");
|
|
||||||
Console.WriteLine($"Spotify Import Playlist IDs: {options.PlaylistIds.Count} configured");
|
|
||||||
for (int i = 0; i < options.PlaylistIds.Count; i++)
|
for (int i = 0; i < options.PlaylistIds.Count; i++)
|
||||||
{
|
{
|
||||||
var name = i < options.PlaylistNames.Count ? options.PlaylistNames[i] : options.PlaylistIds[i];
|
var name = i < options.PlaylistNames.Count ? options.PlaylistNames[i] : options.PlaylistIds[i];
|
||||||
Console.WriteLine($" - {name} (ID: {options.PlaylistIds[i]})");
|
options.Playlists.Add(new SpotifyPlaylistConfig
|
||||||
|
{
|
||||||
|
Name = name,
|
||||||
|
Id = options.PlaylistIds[i],
|
||||||
|
LocalTracksPosition = LocalTracksPosition.First // Default for legacy
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 playlist in options.Playlists)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" - {playlist.Name} (ID: {playlist.Id}, LocalTracks: {playlist.LocalTracksPosition})");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ public class SpotifyMissingTracksFetcher : BackgroundService
|
|||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Spotify Import ENABLED");
|
_logger.LogInformation("Spotify Import ENABLED");
|
||||||
_logger.LogInformation("Configured Playlist IDs: {Count}", _spotifySettings.Value.PlaylistIds.Count);
|
_logger.LogInformation("Configured Playlists: {Count}", _spotifySettings.Value.Playlists.Count);
|
||||||
|
|
||||||
// Log the search schedule
|
// Log the search schedule
|
||||||
var settings = _spotifySettings.Value;
|
var settings = _spotifySettings.Value;
|
||||||
@@ -186,15 +186,10 @@ public class SpotifyMissingTracksFetcher : BackgroundService
|
|||||||
{
|
{
|
||||||
_playlistIdToName.Clear();
|
_playlistIdToName.Clear();
|
||||||
|
|
||||||
// Use configured playlist names instead of fetching from API
|
// Use configured playlists
|
||||||
for (int i = 0; i < _spotifySettings.Value.PlaylistIds.Count; i++)
|
foreach (var playlist in _spotifySettings.Value.Playlists)
|
||||||
{
|
{
|
||||||
var playlistId = _spotifySettings.Value.PlaylistIds[i];
|
_playlistIdToName[playlist.Id] = playlist.Name;
|
||||||
var playlistName = i < _spotifySettings.Value.PlaylistNames.Count
|
|
||||||
? _spotifySettings.Value.PlaylistNames[i]
|
|
||||||
: playlistId; // Fallback to ID if name not configured
|
|
||||||
|
|
||||||
_playlistIdToName[playlistId] = playlistName;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -85,8 +85,8 @@ public class SpotifyTrackMatchingService : BackgroundService
|
|||||||
{
|
{
|
||||||
_logger.LogInformation("=== STARTING TRACK MATCHING ===");
|
_logger.LogInformation("=== STARTING TRACK MATCHING ===");
|
||||||
|
|
||||||
var playlistNames = _spotifySettings.Value.PlaylistNames;
|
var playlists = _spotifySettings.Value.Playlists;
|
||||||
if (playlistNames.Count == 0)
|
if (playlists.Count == 0)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("No playlists configured for matching");
|
_logger.LogInformation("No playlists configured for matching");
|
||||||
return;
|
return;
|
||||||
@@ -95,17 +95,17 @@ public class SpotifyTrackMatchingService : BackgroundService
|
|||||||
using var scope = _serviceProvider.CreateScope();
|
using var scope = _serviceProvider.CreateScope();
|
||||||
var metadataService = scope.ServiceProvider.GetRequiredService<IMusicMetadataService>();
|
var metadataService = scope.ServiceProvider.GetRequiredService<IMusicMetadataService>();
|
||||||
|
|
||||||
foreach (var playlistName in playlistNames)
|
foreach (var playlist in playlists)
|
||||||
{
|
{
|
||||||
if (cancellationToken.IsCancellationRequested) break;
|
if (cancellationToken.IsCancellationRequested) break;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await MatchPlaylistTracksAsync(playlistName, metadataService, cancellationToken);
|
await MatchPlaylistTracksAsync(playlist.Name, metadataService, cancellationToken);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error matching tracks for playlist {Playlist}", playlistName);
|
_logger.LogError(ex, "Error matching tracks for playlist {Playlist}", playlist.Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,8 +81,7 @@ services:
|
|||||||
- SpotifyImport__SyncStartHour=${SPOTIFY_IMPORT_SYNC_START_HOUR:-16}
|
- SpotifyImport__SyncStartHour=${SPOTIFY_IMPORT_SYNC_START_HOUR:-16}
|
||||||
- SpotifyImport__SyncStartMinute=${SPOTIFY_IMPORT_SYNC_START_MINUTE:-15}
|
- SpotifyImport__SyncStartMinute=${SPOTIFY_IMPORT_SYNC_START_MINUTE:-15}
|
||||||
- SpotifyImport__SyncWindowHours=${SPOTIFY_IMPORT_SYNC_WINDOW_HOURS:-2}
|
- SpotifyImport__SyncWindowHours=${SPOTIFY_IMPORT_SYNC_WINDOW_HOURS:-2}
|
||||||
- SpotifyImport__PlaylistIds=${SPOTIFY_IMPORT_PLAYLIST_IDS:-}
|
- SpotifyImport__Playlists=${SPOTIFY_IMPORT_PLAYLISTS:-}
|
||||||
- SpotifyImport__PlaylistNames=${SPOTIFY_IMPORT_PLAYLIST_NAMES:-}
|
|
||||||
|
|
||||||
# ===== SHARED =====
|
# ===== SHARED =====
|
||||||
- Library__DownloadPath=/app/downloads
|
- Library__DownloadPath=/app/downloads
|
||||||
|
|||||||
Reference in New Issue
Block a user