mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Add fuzzy name matching as fallback for local tracks + better error logging
- Add fuzzy matching by title+artist as fallback (like Jellyfin Spotify Import plugin) - Add clear error messages when JELLYFIN_USER_ID is not configured - Add emoji logging for easier debugging (🔍 📌 ✅ ❌) - Check HTTP status code when fetching playlist items - This should fix the issue where all tracks show [S] even when they exist locally
This commit is contained in:
@@ -2860,20 +2860,26 @@ public class JellyfinController : ControllerBase
|
|||||||
var userId = _settings.UserId;
|
var userId = _settings.UserId;
|
||||||
if (string.IsNullOrEmpty(userId))
|
if (string.IsNullOrEmpty(userId))
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No UserId configured - attempting to fetch existing playlist tracks may fail");
|
_logger.LogError("❌ JELLYFIN_USER_ID is NOT configured! Cannot fetch playlist tracks. Set it in .env or admin UI.");
|
||||||
|
return null; // Fall back to legacy mode
|
||||||
}
|
}
|
||||||
|
|
||||||
var playlistItemsUrl = $"Playlists/{playlistId}/Items";
|
var playlistItemsUrl = $"Playlists/{playlistId}/Items?UserId={userId}";
|
||||||
if (!string.IsNullOrEmpty(userId))
|
|
||||||
{
|
|
||||||
playlistItemsUrl += $"?UserId={userId}";
|
|
||||||
}
|
|
||||||
|
|
||||||
var (existingTracksResponse, _) = await _proxyService.GetJsonAsync(
|
_logger.LogInformation("🔍 Fetching existing tracks from Jellyfin playlist {PlaylistId} with UserId {UserId}",
|
||||||
|
playlistId, userId);
|
||||||
|
|
||||||
|
var (existingTracksResponse, statusCode) = await _proxyService.GetJsonAsync(
|
||||||
playlistItemsUrl,
|
playlistItemsUrl,
|
||||||
null,
|
null,
|
||||||
Request.Headers);
|
Request.Headers);
|
||||||
|
|
||||||
|
if (statusCode != 200)
|
||||||
|
{
|
||||||
|
_logger.LogError("❌ Failed to fetch Jellyfin playlist items: HTTP {StatusCode}. Check JELLYFIN_USER_ID is correct.", statusCode);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var existingTracks = new List<Song>();
|
var existingTracks = new List<Song>();
|
||||||
var existingBySpotifyId = new Dictionary<string, Song>(); // SpotifyId -> Song
|
var existingBySpotifyId = new Dictionary<string, Song>(); // SpotifyId -> Song
|
||||||
var existingByIsrc = new Dictionary<string, Song>(); // ISRC -> Song
|
var existingByIsrc = new Dictionary<string, Song>(); // ISRC -> Song
|
||||||
@@ -2894,7 +2900,7 @@ public class JellyfinController : ControllerBase
|
|||||||
if (!string.IsNullOrEmpty(spotifyId))
|
if (!string.IsNullOrEmpty(spotifyId))
|
||||||
{
|
{
|
||||||
existingBySpotifyId[spotifyId] = song;
|
existingBySpotifyId[spotifyId] = song;
|
||||||
_logger.LogDebug("Indexed local track by Spotify ID: {SpotifyId} -> {Title}", spotifyId, song.Title);
|
_logger.LogDebug(" 📌 Indexed local track by Spotify ID: {SpotifyId} -> {Title}", spotifyId, song.Title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2902,15 +2908,16 @@ public class JellyfinController : ControllerBase
|
|||||||
if (!string.IsNullOrEmpty(song.Isrc))
|
if (!string.IsNullOrEmpty(song.Isrc))
|
||||||
{
|
{
|
||||||
existingByIsrc[song.Isrc] = song;
|
existingByIsrc[song.Isrc] = song;
|
||||||
_logger.LogDebug("Indexed local track by ISRC: {Isrc} -> {Title}", song.Isrc, song.Title);
|
_logger.LogDebug(" 📌 Indexed local track by ISRC: {Isrc} -> {Title}", song.Isrc, song.Title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_logger.LogInformation("Found {Count} existing tracks in Jellyfin playlist ({SpotifyIds} with Spotify IDs, {Isrcs} with ISRCs)",
|
_logger.LogInformation("✅ Found {Count} existing tracks in Jellyfin playlist ({SpotifyIds} with Spotify IDs, {Isrcs} with ISRCs)",
|
||||||
existingTracks.Count, existingBySpotifyId.Count, existingByIsrc.Count);
|
existingTracks.Count, existingBySpotifyId.Count, existingByIsrc.Count);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No existing tracks found in Jellyfin playlist {PlaylistId} - may need UserId parameter", playlistId);
|
_logger.LogError("❌ No existing tracks found in Jellyfin playlist {PlaylistId} - Jellyfin Spotify Import plugin may not have run yet", playlistId);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the full playlist from Spotify to know the correct order
|
// Get the full playlist from Spotify to know the correct order
|
||||||
@@ -2930,7 +2937,7 @@ public class JellyfinController : ControllerBase
|
|||||||
{
|
{
|
||||||
Song? localTrack = null;
|
Song? localTrack = null;
|
||||||
|
|
||||||
// Try to find local track by Spotify ID first (fastest)
|
// Try to find local track by Spotify ID first (fastest and most reliable)
|
||||||
if (existingBySpotifyId.TryGetValue(spotifyTrack.SpotifyId, out var trackBySpotifyId))
|
if (existingBySpotifyId.TryGetValue(spotifyTrack.SpotifyId, out var trackBySpotifyId))
|
||||||
{
|
{
|
||||||
localTrack = trackBySpotifyId;
|
localTrack = trackBySpotifyId;
|
||||||
@@ -2945,6 +2952,34 @@ public class JellyfinController : ControllerBase
|
|||||||
_logger.LogDebug("#{Pos} {Title} - Found LOCAL by ISRC: {Isrc}",
|
_logger.LogDebug("#{Pos} {Title} - Found LOCAL by ISRC: {Isrc}",
|
||||||
spotifyTrack.Position, spotifyTrack.Title, spotifyTrack.Isrc);
|
spotifyTrack.Position, spotifyTrack.Title, spotifyTrack.Isrc);
|
||||||
}
|
}
|
||||||
|
// Fallback: Match by title + artist name (like Jellyfin Spotify Import plugin does)
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var bestMatch = existingTracks
|
||||||
|
.Select(song => new
|
||||||
|
{
|
||||||
|
Song = song,
|
||||||
|
TitleScore = FuzzyMatcher.CalculateSimilarity(spotifyTrack.Title, song.Title),
|
||||||
|
ArtistScore = FuzzyMatcher.CalculateSimilarity(spotifyTrack.PrimaryArtist, song.Artist)
|
||||||
|
})
|
||||||
|
.Select(x => new
|
||||||
|
{
|
||||||
|
x.Song,
|
||||||
|
x.TitleScore,
|
||||||
|
x.ArtistScore,
|
||||||
|
TotalScore = (x.TitleScore * 0.7) + (x.ArtistScore * 0.3) // Weight title more
|
||||||
|
})
|
||||||
|
.OrderByDescending(x => x.TotalScore)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
// Only use if match is good enough (>75% combined score)
|
||||||
|
if (bestMatch != null && bestMatch.TotalScore >= 75)
|
||||||
|
{
|
||||||
|
localTrack = bestMatch.Song;
|
||||||
|
_logger.LogDebug("#{Pos} {Title} - Found LOCAL by fuzzy match: {MatchTitle} (score: {Score:F1})",
|
||||||
|
spotifyTrack.Position, spotifyTrack.Title, bestMatch.Song.Title, bestMatch.TotalScore);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we found a local track, use it
|
// If we found a local track, use it
|
||||||
if (localTrack != null)
|
if (localTrack != null)
|
||||||
|
|||||||
Reference in New Issue
Block a user