1 Commits

Author SHA1 Message Date
713ecd4ec8 v1.2.6: fix search result ordering to prioritize local tracks
Some checks are pending
Docker Build & Push / build-and-test (push) Waiting to run
Docker Build & Push / docker (push) Blocked by required conditions
2026-02-10 13:36:06 -05:00

View File

@@ -279,54 +279,50 @@ public class JellyfinController : ControllerBase
// Parse Jellyfin results into domain models // Parse Jellyfin results into domain models
var (localSongs, localAlbums, localArtists) = _modelMapper.ParseItemsResponse(jellyfinResult); var (localSongs, localAlbums, localArtists) = _modelMapper.ParseItemsResponse(jellyfinResult);
// Respect source ordering (SquidWTF/Tidal has better search ranking than our fuzzy matching) // Sort all results by match score (local tracks get +10 boost)
// Just interleave local and external results based on which source has better overall match // This ensures best matches appear first regardless of source
var allSongs = localSongs.Concat(externalResult.Songs)
.Select(s => new { Song = s, Score = FuzzyMatcher.CalculateSimilarity(cleanQuery, s.Title) + (s.IsLocal ? 10.0 : 0.0) })
.OrderByDescending(x => x.Score)
.Select(x => x.Song)
.ToList();
// Calculate average match score for each source to determine which should come first var allAlbums = localAlbums.Concat(externalResult.Albums)
// Give local tracks a +10 boost to prioritize them .Select(a => new { Album = a, Score = FuzzyMatcher.CalculateSimilarity(cleanQuery, a.Title) + (a.IsLocal ? 10.0 : 0.0) })
var localSongsAvgScore = localSongs.Any() .OrderByDescending(x => x.Score)
? localSongs.Average(s => FuzzyMatcher.CalculateSimilarity(cleanQuery, s.Title) + 10.0) .Select(x => x.Album)
: 0.0; .ToList();
var externalSongsAvgScore = externalResult.Songs.Any()
? externalResult.Songs.Average(s => FuzzyMatcher.CalculateSimilarity(cleanQuery, s.Title))
: 0.0;
var localAlbumsAvgScore = localAlbums.Any() var allArtists = localArtists.Concat(externalResult.Artists)
? localAlbums.Average(a => FuzzyMatcher.CalculateSimilarity(cleanQuery, a.Title) + 10.0) .Select(a => new { Artist = a, Score = FuzzyMatcher.CalculateSimilarity(cleanQuery, a.Name) + (a.IsLocal ? 10.0 : 0.0) })
: 0.0; .OrderByDescending(x => x.Score)
var externalAlbumsAvgScore = externalResult.Albums.Any() .Select(x => x.Artist)
? externalResult.Albums.Average(a => FuzzyMatcher.CalculateSimilarity(cleanQuery, a.Title)) .ToList();
: 0.0;
var localArtistsAvgScore = localArtists.Any() // Log top results for debugging
? localArtists.Average(a => FuzzyMatcher.CalculateSimilarity(cleanQuery, a.Name) + 10.0)
: 0.0;
var externalArtistsAvgScore = externalResult.Artists.Any()
? externalResult.Artists.Average(a => FuzzyMatcher.CalculateSimilarity(cleanQuery, a.Name))
: 0.0;
// Interleave results: put better-matching source first, preserve original ordering within each source
var allSongs = localSongsAvgScore >= externalSongsAvgScore
? localSongs.Concat(externalResult.Songs).ToList()
: externalResult.Songs.Concat(localSongs).ToList();
var allAlbums = localAlbumsAvgScore >= externalAlbumsAvgScore
? localAlbums.Concat(externalResult.Albums).ToList()
: externalResult.Albums.Concat(localAlbums).ToList();
var allArtists = localArtistsAvgScore >= externalArtistsAvgScore
? localArtists.Concat(externalResult.Artists).ToList()
: externalResult.Artists.Concat(localArtists).ToList();
// Log results for debugging
if (_logger.IsEnabled(LogLevel.Debug)) if (_logger.IsEnabled(LogLevel.Debug))
{ {
_logger.LogDebug("🎵 Songs: Local avg score={LocalScore:F2}, External avg score={ExtScore:F2}, Local first={LocalFirst}", if (allSongs.Any())
localSongsAvgScore, externalSongsAvgScore, localSongsAvgScore >= externalSongsAvgScore); {
_logger.LogDebug("💿 Albums: Local avg score={LocalScore:F2}, External avg score={ExtScore:F2}, Local first={LocalFirst}", var topSong = allSongs.First();
localAlbumsAvgScore, externalAlbumsAvgScore, localAlbumsAvgScore >= externalAlbumsAvgScore); var topScore = FuzzyMatcher.CalculateSimilarity(cleanQuery, topSong.Title) + (topSong.IsLocal ? 10.0 : 0.0);
_logger.LogDebug("🎤 Artists: Local avg score={LocalScore:F2}, External avg score={ExtScore:F2}, Local first={LocalFirst}", _logger.LogDebug("🎵 Top song: '{Title}' (local={IsLocal}, score={Score:F2})",
localArtistsAvgScore, externalArtistsAvgScore, localArtistsAvgScore >= externalArtistsAvgScore); topSong.Title, topSong.IsLocal, topScore);
}
if (allAlbums.Any())
{
var topAlbum = allAlbums.First();
var topScore = FuzzyMatcher.CalculateSimilarity(cleanQuery, topAlbum.Title) + (topAlbum.IsLocal ? 10.0 : 0.0);
_logger.LogDebug("💿 Top album: '{Title}' (local={IsLocal}, score={Score:F2})",
topAlbum.Title, topAlbum.IsLocal, topScore);
}
if (allArtists.Any())
{
var topArtist = allArtists.First();
var topScore = FuzzyMatcher.CalculateSimilarity(cleanQuery, topArtist.Name) + (topArtist.IsLocal ? 10.0 : 0.0);
_logger.LogDebug("🎤 Top artist: '{Name}' (local={IsLocal}, score={Score:F2})",
topArtist.Name, topArtist.IsLocal, topScore);
}
} }
// Convert to Jellyfin format // Convert to Jellyfin format
@@ -344,7 +340,7 @@ public class JellyfinController : ControllerBase
mergedAlbums.AddRange(playlistItems); mergedAlbums.AddRange(playlistItems);
} }
_logger.LogInformation("Merged results (preserving source order): Songs={Songs}, Albums={Albums}, Artists={Artists}", _logger.LogInformation("Merged and sorted results by score: Songs={Songs}, Albums={Albums}, Artists={Artists}",
mergedSongs.Count, mergedAlbums.Count, mergedArtists.Count); mergedSongs.Count, mergedAlbums.Count, mergedArtists.Count);
// Pre-fetch lyrics for top 3 songs in background (don't await) // Pre-fetch lyrics for top 3 songs in background (don't await)