Add MusicBrainz genre enrichment and improve track counting

- Fixed external track detection (check for provider prefix in ID)
- Added genre support to MusicBrainz service (inc=genres+tags)
- Created GenreEnrichmentService for async genre lookup with caching
- Show provider name and search query for external tracks in admin UI
- Display search query that will be used for external track streaming
- Aggregate playlist genres from track genres
- All 225 tests passing
This commit is contained in:
2026-02-04 16:43:17 -05:00
parent 7938871556
commit bf02dc5a57
5 changed files with 234 additions and 22 deletions

View File

@@ -252,30 +252,29 @@ public class AdminController : ControllerBase
// Count local vs external tracks
foreach (var item in items.EnumerateArray())
{
// Check if track has a real file path (local) or is external
// External tracks from allstarr have ExternalProvider in ProviderIds
// Local tracks have real filesystem paths
var hasPath = item.TryGetProperty("Path", out var pathProp) &&
pathProp.ValueKind == JsonValueKind.String &&
!string.IsNullOrEmpty(pathProp.GetString());
if (hasPath)
// Check if it's an external track by looking at the ID format
// External tracks have IDs like "deezer:123456" or "qobuz:123456"
var isExternal = false;
if (item.TryGetProperty("Id", out var idProp))
{
var pathStr = pathProp.GetString()!;
// Local tracks have filesystem paths starting with / or containing :\
if (pathStr.StartsWith("/") || pathStr.Contains(":\\"))
{
localCount++;
}
else
{
// External track (downloaded from Deezer/Qobuz/etc)
externalMatchedCount++;
}
var id = idProp.GetString() ?? "";
isExternal = id.Contains(":"); // External IDs contain provider prefix
}
else
if (isExternal)
{
// No path means external
externalMatchedCount++;
}
else if (hasPath)
{
localCount++;
}
}
var totalInJellyfin = localCount + externalMatchedCount;
@@ -422,7 +421,10 @@ public class AdminController : ControllerBase
spotifyId = track.SpotifyId,
durationMs = track.DurationMs,
albumArtUrl = track.AlbumArtUrl,
isLocal = isLocal
isLocal = isLocal,
// For external tracks, show what will be searched
externalProvider = isLocal ? null : _configuration.GetValue<string>("Subsonic:MusicService") ?? _configuration.GetValue<string>("Jellyfin:MusicService") ?? "Deezer",
searchQuery = isLocal ? null : $"{track.Title} {track.PrimaryArtist}"
});
}
@@ -456,7 +458,9 @@ public class AdminController : ControllerBase
spotifyId = t.SpotifyId,
durationMs = t.DurationMs,
albumArtUrl = t.AlbumArtUrl,
isLocal = (bool?)null // Unknown
isLocal = (bool?)null, // Unknown
externalProvider = _configuration.GetValue<string>("Subsonic:MusicService") ?? _configuration.GetValue<string>("Jellyfin:MusicService") ?? "Deezer",
searchQuery = $"{t.Title} {t.PrimaryArtist}"
})
});
}