Fix: Add on-demand external track search when cache is empty

- Search external providers in real-time if no cached match exists
- Use fuzzy matching (60% threshold) for external tracks
- Ensures external tracks are always available even without pre-matching
- Local tracks still prioritized first (70% threshold)
This commit is contained in:
2026-02-03 18:39:33 -05:00
parent f240423822
commit dccdb7b744

View File

@@ -2973,12 +2973,13 @@ public class JellyfinController : ControllerBase
}
// ONLY if no local track exists, check for external match
var matched = orderedTracks.FirstOrDefault(t => t.SpotifyId == spotifyTrack.SpotifyId);
// First check pre-matched cache
var matched = orderedTracks?.FirstOrDefault(t => t.SpotifyId == spotifyTrack.SpotifyId);
if (matched != null)
{
finalTracks.Add(matched.MatchedSong);
externalUsedCount++;
_logger.LogInformation("📥 #{Pos} '{Title}' by {Artist} → EXTERNAL: {Provider}/{Id}",
_logger.LogInformation("📥 #{Pos} '{Title}' by {Artist} → EXTERNAL (cached): {Provider}/{Id}",
spotifyTrack.Position,
spotifyTrack.Title,
spotifyTrack.PrimaryArtist,
@@ -2987,9 +2988,65 @@ public class JellyfinController : ControllerBase
}
else
{
skippedCount++;
_logger.LogWarning("❌ #{Pos} '{Title}' by {Artist} → NO MATCH (not in Jellyfin, not in external cache)",
spotifyTrack.Position, spotifyTrack.Title, spotifyTrack.PrimaryArtist);
// No cached match - search external providers on-demand
try
{
var query = $"{spotifyTrack.Title} {spotifyTrack.PrimaryArtist}";
var searchResults = await _metadataService.SearchSongsAsync(query, limit: 5);
if (searchResults.Count > 0)
{
// Fuzzy match to find best result
var bestExternalMatch = searchResults
.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.6) + (x.ArtistScore * 0.4)
})
.OrderByDescending(x => x.TotalScore)
.FirstOrDefault();
if (bestExternalMatch != null && bestExternalMatch.TotalScore >= 60)
{
finalTracks.Add(bestExternalMatch.Song);
externalUsedCount++;
_logger.LogInformation("📥 #{Pos} '{Title}' by {Artist} → EXTERNAL (on-demand): {Provider}/{Id} (score: {Score:F1}%)",
spotifyTrack.Position,
spotifyTrack.Title,
spotifyTrack.PrimaryArtist,
bestExternalMatch.Song.ExternalProvider,
bestExternalMatch.Song.ExternalId,
bestExternalMatch.TotalScore);
}
else
{
skippedCount++;
_logger.LogWarning("❌ #{Pos} '{Title}' by {Artist} → NO MATCH (best external score: {Score:F1}%, need 60%)",
spotifyTrack.Position, spotifyTrack.Title, spotifyTrack.PrimaryArtist,
bestExternalMatch?.TotalScore ?? 0);
}
}
else
{
skippedCount++;
_logger.LogWarning("❌ #{Pos} '{Title}' by {Artist} → NO MATCH (no external results)",
spotifyTrack.Position, spotifyTrack.Title, spotifyTrack.PrimaryArtist);
}
}
catch (Exception ex)
{
skippedCount++;
_logger.LogError(ex, "❌ #{Pos} '{Title}' by {Artist} → ERROR searching external providers",
spotifyTrack.Position, spotifyTrack.Title, spotifyTrack.PrimaryArtist);
}
}
}