mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Fix GetPlaylists to use pre-built cache with manual mappings for accurate counts
This commit is contained in:
@@ -247,99 +247,137 @@ public class AdminController : ControllerBase
|
|||||||
|
|
||||||
if (jellyfinDoc.RootElement.TryGetProperty("Items", out var items))
|
if (jellyfinDoc.RootElement.TryGetProperty("Items", out var items))
|
||||||
{
|
{
|
||||||
// Build list of local tracks from Jellyfin (match by name only)
|
|
||||||
var localTracks = new List<(string Title, string Artist)>();
|
|
||||||
foreach (var item in items.EnumerateArray())
|
|
||||||
{
|
|
||||||
var title = item.TryGetProperty("Name", out var nameEl) ? nameEl.GetString() ?? "" : "";
|
|
||||||
var artist = "";
|
|
||||||
|
|
||||||
if (item.TryGetProperty("Artists", out var artistsEl) && artistsEl.GetArrayLength() > 0)
|
|
||||||
{
|
|
||||||
artist = artistsEl[0].GetString() ?? "";
|
|
||||||
}
|
|
||||||
else if (item.TryGetProperty("AlbumArtist", out var albumArtistEl))
|
|
||||||
{
|
|
||||||
artist = albumArtistEl.GetString() ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(title))
|
|
||||||
{
|
|
||||||
localTracks.Add((title, artist));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get Spotify tracks to match against
|
// Get Spotify tracks to match against
|
||||||
var spotifyTracks = await _playlistFetcher.GetPlaylistTracksAsync(config.Name);
|
var spotifyTracks = await _playlistFetcher.GetPlaylistTracksAsync(config.Name);
|
||||||
|
|
||||||
// Get matched external tracks cache once
|
// Try to use the pre-built playlist cache first (includes manual mappings!)
|
||||||
var matchedTracksKey = $"spotify:matched:ordered:{config.Name}";
|
var playlistItemsCacheKey = $"spotify:playlist:items:{config.Name}";
|
||||||
var matchedTracks = await _cache.GetAsync<List<MatchedTrack>>(matchedTracksKey);
|
var cachedPlaylistItems = await _cache.GetAsync<List<Dictionary<string, object?>>>(playlistItemsCacheKey);
|
||||||
var matchedSpotifyIds = new HashSet<string>(
|
|
||||||
matchedTracks?.Select(m => m.SpotifyId) ?? Enumerable.Empty<string>()
|
|
||||||
);
|
|
||||||
|
|
||||||
var localCount = 0;
|
if (cachedPlaylistItems != null && cachedPlaylistItems.Count > 0)
|
||||||
var externalMatchedCount = 0;
|
|
||||||
var externalMissingCount = 0;
|
|
||||||
|
|
||||||
// Match each Spotify track to determine if it's local, external, or missing
|
|
||||||
foreach (var track in spotifyTracks)
|
|
||||||
{
|
{
|
||||||
var isLocal = false;
|
// Use the pre-built cache which respects manual mappings
|
||||||
|
var localCount = 0;
|
||||||
|
var externalCount = 0;
|
||||||
|
|
||||||
if (localTracks.Count > 0)
|
foreach (var item in cachedPlaylistItems)
|
||||||
{
|
{
|
||||||
var bestMatch = localTracks
|
// Check if it's a local track (has Path) or external (no Path)
|
||||||
.Select(local => new
|
if (item.TryGetValue("Path", out var pathObj) && pathObj != null)
|
||||||
{
|
|
||||||
Local = local,
|
|
||||||
TitleScore = FuzzyMatcher.CalculateSimilarity(track.Title, local.Title),
|
|
||||||
ArtistScore = FuzzyMatcher.CalculateSimilarity(track.PrimaryArtist, local.Artist)
|
|
||||||
})
|
|
||||||
.Select(x => new
|
|
||||||
{
|
|
||||||
x.Local,
|
|
||||||
x.TitleScore,
|
|
||||||
x.ArtistScore,
|
|
||||||
TotalScore = (x.TitleScore * 0.7) + (x.ArtistScore * 0.3)
|
|
||||||
})
|
|
||||||
.OrderByDescending(x => x.TotalScore)
|
|
||||||
.FirstOrDefault();
|
|
||||||
|
|
||||||
// Use 70% threshold (same as playback matching)
|
|
||||||
if (bestMatch != null && bestMatch.TotalScore >= 70)
|
|
||||||
{
|
{
|
||||||
isLocal = true;
|
localCount++;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isLocal)
|
|
||||||
{
|
|
||||||
localCount++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Check if external track is matched
|
|
||||||
if (matchedSpotifyIds.Contains(track.SpotifyId))
|
|
||||||
{
|
|
||||||
externalMatchedCount++;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
externalMissingCount++;
|
externalCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var externalMissingCount = spotifyTracks.Count - cachedPlaylistItems.Count;
|
||||||
|
if (externalMissingCount < 0) externalMissingCount = 0;
|
||||||
|
|
||||||
|
playlistInfo["localTracks"] = localCount;
|
||||||
|
playlistInfo["externalMatched"] = externalCount;
|
||||||
|
playlistInfo["externalMissing"] = externalMissingCount;
|
||||||
|
playlistInfo["externalTotal"] = externalCount + externalMissingCount;
|
||||||
|
playlistInfo["totalInJellyfin"] = cachedPlaylistItems.Count;
|
||||||
|
|
||||||
|
_logger.LogDebug("Playlist {Name} (from cache): {Total} Spotify tracks, {Local} local, {ExtMatched} external matched, {ExtMissing} external missing",
|
||||||
|
config.Name, spotifyTracks.Count, localCount, externalCount, externalMissingCount);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Fallback: Build list of local tracks from Jellyfin (match by name only)
|
||||||
|
var localTracks = new List<(string Title, string Artist)>();
|
||||||
|
foreach (var item in items.EnumerateArray())
|
||||||
|
{
|
||||||
|
var title = item.TryGetProperty("Name", out var nameEl) ? nameEl.GetString() ?? "" : "";
|
||||||
|
var artist = "";
|
||||||
|
|
||||||
playlistInfo["localTracks"] = localCount;
|
if (item.TryGetProperty("Artists", out var artistsEl) && artistsEl.GetArrayLength() > 0)
|
||||||
playlistInfo["externalMatched"] = externalMatchedCount;
|
{
|
||||||
playlistInfo["externalMissing"] = externalMissingCount;
|
artist = artistsEl[0].GetString() ?? "";
|
||||||
playlistInfo["externalTotal"] = externalMatchedCount + externalMissingCount;
|
}
|
||||||
playlistInfo["totalInJellyfin"] = localCount + externalMatchedCount;
|
else if (item.TryGetProperty("AlbumArtist", out var albumArtistEl))
|
||||||
|
{
|
||||||
|
artist = albumArtistEl.GetString() ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
_logger.LogDebug("Playlist {Name}: {Total} Spotify tracks, {Local} local, {ExtMatched} external matched, {ExtMissing} external missing",
|
if (!string.IsNullOrEmpty(title))
|
||||||
config.Name, spotifyTrackCount, localCount, externalMatchedCount, externalMissingCount);
|
{
|
||||||
|
localTracks.Add((title, artist));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get matched external tracks cache once
|
||||||
|
var matchedTracksKey = $"spotify:matched:ordered:{config.Name}";
|
||||||
|
var matchedTracks = await _cache.GetAsync<List<MatchedTrack>>(matchedTracksKey);
|
||||||
|
var matchedSpotifyIds = new HashSet<string>(
|
||||||
|
matchedTracks?.Select(m => m.SpotifyId) ?? Enumerable.Empty<string>()
|
||||||
|
);
|
||||||
|
|
||||||
|
var localCount = 0;
|
||||||
|
var externalMatchedCount = 0;
|
||||||
|
var externalMissingCount = 0;
|
||||||
|
|
||||||
|
// Match each Spotify track to determine if it's local, external, or missing
|
||||||
|
foreach (var track in spotifyTracks)
|
||||||
|
{
|
||||||
|
var isLocal = false;
|
||||||
|
|
||||||
|
if (localTracks.Count > 0)
|
||||||
|
{
|
||||||
|
var bestMatch = localTracks
|
||||||
|
.Select(local => new
|
||||||
|
{
|
||||||
|
Local = local,
|
||||||
|
TitleScore = FuzzyMatcher.CalculateSimilarity(track.Title, local.Title),
|
||||||
|
ArtistScore = FuzzyMatcher.CalculateSimilarity(track.PrimaryArtist, local.Artist)
|
||||||
|
})
|
||||||
|
.Select(x => new
|
||||||
|
{
|
||||||
|
x.Local,
|
||||||
|
x.TitleScore,
|
||||||
|
x.ArtistScore,
|
||||||
|
TotalScore = (x.TitleScore * 0.7) + (x.ArtistScore * 0.3)
|
||||||
|
})
|
||||||
|
.OrderByDescending(x => x.TotalScore)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
// Use 70% threshold (same as playback matching)
|
||||||
|
if (bestMatch != null && bestMatch.TotalScore >= 70)
|
||||||
|
{
|
||||||
|
isLocal = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLocal)
|
||||||
|
{
|
||||||
|
localCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Check if external track is matched
|
||||||
|
if (matchedSpotifyIds.Contains(track.SpotifyId))
|
||||||
|
{
|
||||||
|
externalMatchedCount++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
externalMissingCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
playlistInfo["localTracks"] = localCount;
|
||||||
|
playlistInfo["externalMatched"] = externalMatchedCount;
|
||||||
|
playlistInfo["externalMissing"] = externalMissingCount;
|
||||||
|
playlistInfo["externalTotal"] = externalMatchedCount + externalMissingCount;
|
||||||
|
playlistInfo["totalInJellyfin"] = localCount + externalMatchedCount;
|
||||||
|
|
||||||
|
_logger.LogDebug("Playlist {Name} (fallback): {Total} Spotify tracks, {Local} local, {ExtMatched} external matched, {ExtMissing} external missing",
|
||||||
|
config.Name, spotifyTracks.Count, localCount, externalMatchedCount, externalMissingCount);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user