mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
fix: show both local and external artists with same name
- Artists with same name now appear separately (local + external [S]) - Fixed deduplication to keep one local AND one external per name - Added logging to SquidWTF artist search to show actual URLs - External artists get [S] suffix to distinguish from local - Allows users to browse external artist albums not in local library - TIDAL image URLs already correctly implemented with UUID splitting
This commit is contained in:
@@ -294,14 +294,58 @@ public class JellyfinController : ControllerBase
|
|||||||
.Select(x => x.Item)
|
.Select(x => x.Item)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
// Dedupe artists by name, keeping highest scored version
|
// Dedupe artists by name, but KEEP both local and external versions
|
||||||
var artistScores = scoredLocalArtists.Concat(scoredExternalArtists)
|
// Group by name, then for each name keep ONE local and ONE external (if both exist)
|
||||||
.GroupBy(x => x.Item.Name, StringComparer.OrdinalIgnoreCase)
|
var artistsByName = scoredLocalArtists.Concat(scoredExternalArtists)
|
||||||
.Select(g => g.OrderByDescending(x => x.Score).First())
|
.GroupBy(x => x.Item.Name, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
var artistScores = new List<Artist>();
|
||||||
|
foreach (var group in artistsByName)
|
||||||
|
{
|
||||||
|
// Get best local artist (if any)
|
||||||
|
var bestLocal = group
|
||||||
|
.Where(x => x.Item.IsLocal)
|
||||||
.OrderByDescending(x => x.Score)
|
.OrderByDescending(x => x.Score)
|
||||||
.Select(x => x.Item)
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
// Get best external artist (if any)
|
||||||
|
var bestExternal = group
|
||||||
|
.Where(x => !x.Item.IsLocal)
|
||||||
|
.OrderByDescending(x => x.Score)
|
||||||
|
.FirstOrDefault();
|
||||||
|
|
||||||
|
// Add both (if they exist)
|
||||||
|
if (bestLocal.Item != null)
|
||||||
|
{
|
||||||
|
artistScores.Add(bestLocal.Item);
|
||||||
|
}
|
||||||
|
if (bestExternal.Item != null)
|
||||||
|
{
|
||||||
|
artistScores.Add(bestExternal.Item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by score
|
||||||
|
artistScores = artistScores
|
||||||
|
.OrderByDescending(a => {
|
||||||
|
// Find the score for this artist
|
||||||
|
var scored = scoredLocalArtists.Concat(scoredExternalArtists)
|
||||||
|
.FirstOrDefault(x => x.Item.Id == a.Id);
|
||||||
|
return scored.Score;
|
||||||
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
|
// Log deduplication details for debugging
|
||||||
|
if (_logger.IsEnabled(LogLevel.Debug))
|
||||||
|
{
|
||||||
|
var localArtistNames = scoredLocalArtists.Select(x => $"{x.Item.Name} (local, score: {x.Score:F2})").ToList();
|
||||||
|
var externalArtistNames = scoredExternalArtists.Select(x => $"{x.Item.Name} ({x.Item.ExternalProvider}, score: {x.Score:F2})").ToList();
|
||||||
|
_logger.LogDebug("🎤 Artist deduplication: Local={LocalArtists}, External={ExternalArtists}, Final={FinalCount}",
|
||||||
|
string.Join(", ", localArtistNames),
|
||||||
|
string.Join(", ", externalArtistNames),
|
||||||
|
artistScores.Count);
|
||||||
|
}
|
||||||
|
|
||||||
// Convert to Jellyfin format
|
// Convert to Jellyfin format
|
||||||
var mergedSongs = allSongs.Select(s => _responseBuilder.ConvertSongToJellyfinItem(s)).ToList();
|
var mergedSongs = allSongs.Select(s => _responseBuilder.ConvertSongToJellyfinItem(s)).ToList();
|
||||||
var mergedAlbums = allAlbums.Select(a => _responseBuilder.ConvertAlbumToJellyfinItem(a)).ToList();
|
var mergedAlbums = allAlbums.Select(a => _responseBuilder.ConvertAlbumToJellyfinItem(a)).ToList();
|
||||||
@@ -680,27 +724,30 @@ public class JellyfinController : ControllerBase
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge and deduplicate by name
|
// Merge and deduplicate by name, but KEEP both local and external versions
|
||||||
|
// This allows users to see both their local "Taylor Swift" and external "Taylor Swift [S]"
|
||||||
var artistNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var artistNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
var mergedArtists = new List<Artist>();
|
var mergedArtists = new List<Artist>();
|
||||||
|
|
||||||
|
// Add all local artists first
|
||||||
foreach (var artist in localArtists)
|
foreach (var artist in localArtists)
|
||||||
{
|
{
|
||||||
if (artistNames.Add(artist.Name))
|
if (artistNames.Add(artist.Name + ":local"))
|
||||||
{
|
{
|
||||||
mergedArtists.Add(artist);
|
mergedArtists.Add(artist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add all external artists (even if name matches local)
|
||||||
foreach (var artist in externalArtists)
|
foreach (var artist in externalArtists)
|
||||||
{
|
{
|
||||||
if (artistNames.Add(artist.Name))
|
if (artistNames.Add(artist.Name + ":external"))
|
||||||
{
|
{
|
||||||
mergedArtists.Add(artist);
|
mergedArtists.Add(artist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Returning {Count} merged artists", mergedArtists.Count);
|
_logger.LogInformation("Returning {Count} merged artists (local + external)", mergedArtists.Count);
|
||||||
|
|
||||||
// Convert to Jellyfin format
|
// Convert to Jellyfin format
|
||||||
var artistItems = mergedArtists.Select(a => _responseBuilder.ConvertArtistToJellyfinItem(a)).ToList();
|
var artistItems = mergedArtists.Select(a => _responseBuilder.ConvertArtistToJellyfinItem(a)).ToList();
|
||||||
|
|||||||
@@ -176,10 +176,13 @@ public class SquidWTFMetadataService : IMusicMetadataService
|
|||||||
return await TryWithFallbackAsync(async (baseUrl) =>
|
return await TryWithFallbackAsync(async (baseUrl) =>
|
||||||
{
|
{
|
||||||
var url = $"{baseUrl}/search/?a={Uri.EscapeDataString(query)}";
|
var url = $"{baseUrl}/search/?a={Uri.EscapeDataString(query)}";
|
||||||
|
_logger.LogInformation("🔍 SQUIDWTF: Searching artists with URL: {Url}", url);
|
||||||
|
|
||||||
var response = await _httpClient.GetAsync(url);
|
var response = await _httpClient.GetAsync(url);
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
|
_logger.LogWarning("⚠️ SQUIDWTF: Artist search failed with status {StatusCode}", response.StatusCode);
|
||||||
return new List<Artist>();
|
return new List<Artist>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,11 +199,14 @@ public class SquidWTFMetadataService : IMusicMetadataService
|
|||||||
{
|
{
|
||||||
if (count >= limit) break;
|
if (count >= limit) break;
|
||||||
|
|
||||||
artists.Add(ParseTidalArtist(artist));
|
var parsedArtist = ParseTidalArtist(artist);
|
||||||
|
artists.Add(parsedArtist);
|
||||||
|
_logger.LogDebug("🎤 SQUIDWTF: Found artist: {Name} (ID: {Id})", parsedArtist.Name, parsedArtist.ExternalId);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("✓ SQUIDWTF: Artist search returned {Count} results", artists.Count);
|
||||||
return artists;
|
return artists;
|
||||||
}, new List<Artist>());
|
}, new List<Artist>());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user