mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-10 07:58:39 -05:00
Fix: Match local Jellyfin tracks by ISRC instead of Spotify ID
- Local Jellyfin tracks don't have Spotify IDs (only plugin-added tracks do) - Now matches by ISRC first (most reliable), then falls back to Spotify ID - Builds dictionaries for fast lookup: existingBySpotifyId and existingByIsrc - Prioritizes local tracks over external matches - Logs: 'X tracks (Y with Spotify IDs, Z with ISRCs)' - This fixes all tracks showing [S] - now uses local files when available
This commit is contained in:
@@ -2862,29 +2862,36 @@ public class JellyfinController : ControllerBase
|
|||||||
Request.Headers);
|
Request.Headers);
|
||||||
|
|
||||||
var existingTracks = new List<Song>();
|
var existingTracks = new List<Song>();
|
||||||
var existingSpotifyIds = new HashSet<string>();
|
var existingBySpotifyId = new Dictionary<string, Song>(); // SpotifyId -> Song
|
||||||
var existingPositions = new Dictionary<string, int>(); // SpotifyId -> position from Jellyfin
|
var existingByIsrc = new Dictionary<string, Song>(); // ISRC -> Song
|
||||||
|
|
||||||
if (existingTracksResponse != null &&
|
if (existingTracksResponse != null &&
|
||||||
existingTracksResponse.RootElement.TryGetProperty("Items", out var items))
|
existingTracksResponse.RootElement.TryGetProperty("Items", out var items))
|
||||||
{
|
{
|
||||||
var position = 0;
|
|
||||||
foreach (var item in items.EnumerateArray())
|
foreach (var item in items.EnumerateArray())
|
||||||
{
|
{
|
||||||
var song = _modelMapper.ParseSong(item);
|
var song = _modelMapper.ParseSong(item);
|
||||||
existingTracks.Add(song);
|
existingTracks.Add(song);
|
||||||
|
|
||||||
// Track Spotify IDs and their positions
|
// Index by Spotify ID if available (from Jellyfin Spotify Import plugin)
|
||||||
if (item.TryGetProperty("ProviderIds", out var providerIds) &&
|
if (item.TryGetProperty("ProviderIds", out var providerIds) &&
|
||||||
providerIds.TryGetProperty("Spotify", out var spotifyId))
|
providerIds.TryGetProperty("Spotify", out var spotifyIdElement))
|
||||||
{
|
{
|
||||||
var id = spotifyId.GetString() ?? "";
|
var spotifyId = spotifyIdElement.GetString();
|
||||||
existingSpotifyIds.Add(id);
|
if (!string.IsNullOrEmpty(spotifyId))
|
||||||
existingPositions[id] = position;
|
{
|
||||||
|
existingBySpotifyId[spotifyId] = song;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index by ISRC for matching (most reliable)
|
||||||
|
if (!string.IsNullOrEmpty(song.Isrc))
|
||||||
|
{
|
||||||
|
existingByIsrc[song.Isrc] = song;
|
||||||
}
|
}
|
||||||
position++;
|
|
||||||
}
|
}
|
||||||
_logger.LogInformation("Found {Count} existing local tracks in Jellyfin playlist", existingTracks.Count);
|
_logger.LogInformation("Found {Count} existing tracks in Jellyfin playlist ({SpotifyIds} with Spotify IDs, {Isrcs} with ISRCs)",
|
||||||
|
existingTracks.Count, existingBySpotifyId.Count, existingByIsrc.Count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the full playlist from Spotify to know the correct order
|
// Get the full playlist from Spotify to know the correct order
|
||||||
@@ -2897,28 +2904,39 @@ public class JellyfinController : ControllerBase
|
|||||||
|
|
||||||
// Build the final track list in correct Spotify order
|
// Build the final track list in correct Spotify order
|
||||||
var finalTracks = new List<Song>();
|
var finalTracks = new List<Song>();
|
||||||
var localUsed = new HashSet<int>(); // Track which local tracks we've placed
|
var localUsedCount = 0;
|
||||||
|
var externalUsedCount = 0;
|
||||||
|
|
||||||
foreach (var spotifyTrack in spotifyTracks.OrderBy(t => t.Position))
|
foreach (var spotifyTrack in spotifyTracks.OrderBy(t => t.Position))
|
||||||
{
|
{
|
||||||
// Check if this track exists locally
|
Song? localTrack = null;
|
||||||
if (existingSpotifyIds.Contains(spotifyTrack.SpotifyId))
|
|
||||||
|
// Try to find local track by Spotify ID first (fastest)
|
||||||
|
if (existingBySpotifyId.TryGetValue(spotifyTrack.SpotifyId, out var trackBySpotifyId))
|
||||||
{
|
{
|
||||||
// Use the local version
|
localTrack = trackBySpotifyId;
|
||||||
if (existingPositions.TryGetValue(spotifyTrack.SpotifyId, out var localIndex) &&
|
}
|
||||||
localIndex < existingTracks.Count)
|
// Try to find by ISRC (most reliable for matching)
|
||||||
{
|
else if (!string.IsNullOrEmpty(spotifyTrack.Isrc) &&
|
||||||
finalTracks.Add(existingTracks[localIndex]);
|
existingByIsrc.TryGetValue(spotifyTrack.Isrc, out var trackByIsrc))
|
||||||
localUsed.Add(localIndex);
|
{
|
||||||
continue;
|
localTrack = trackByIsrc;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we have a matched external track
|
// If we found a local track, use it
|
||||||
|
if (localTrack != null)
|
||||||
|
{
|
||||||
|
finalTracks.Add(localTrack);
|
||||||
|
localUsedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No local track - check if we have a matched external track
|
||||||
var matched = orderedTracks.FirstOrDefault(t => t.SpotifyId == spotifyTrack.SpotifyId);
|
var matched = orderedTracks.FirstOrDefault(t => t.SpotifyId == spotifyTrack.SpotifyId);
|
||||||
if (matched != null)
|
if (matched != null)
|
||||||
{
|
{
|
||||||
finalTracks.Add(matched.MatchedSong);
|
finalTracks.Add(matched.MatchedSong);
|
||||||
|
externalUsedCount++;
|
||||||
}
|
}
|
||||||
// If no match, the track is simply omitted (not available from any source)
|
// If no match, the track is simply omitted (not available from any source)
|
||||||
}
|
}
|
||||||
@@ -2931,8 +2949,8 @@ public class JellyfinController : ControllerBase
|
|||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"Final ordered playlist: {Total} tracks ({Local} local + {External} external) for {Playlist}",
|
"Final ordered playlist: {Total} tracks ({Local} local + {External} external) for {Playlist}",
|
||||||
finalTracks.Count,
|
finalTracks.Count,
|
||||||
localUsed.Count,
|
localUsedCount,
|
||||||
finalTracks.Count - localUsed.Count,
|
externalUsedCount,
|
||||||
spotifyPlaylistName);
|
spotifyPlaylistName);
|
||||||
|
|
||||||
return _responseBuilder.CreateItemsResponse(finalTracks);
|
return _responseBuilder.CreateItemsResponse(finalTracks);
|
||||||
|
|||||||
Reference in New Issue
Block a user