2 Commits
v1.2.7 ... beta

Author SHA1 Message Date
0a9e528418 v1.3.0: Bump version to 1.3.0
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-11 00:01:06 -05:00
f74728fc73 fix: use MBID lookup for MusicBrainz genre enrichment
Search API doesn't return genres even with inc=genres parameter.
Now doing search to get MBID, then lookup by MBID to get genres.
2026-02-10 23:52:14 -05:00
5 changed files with 67 additions and 12 deletions

View File

@@ -139,7 +139,7 @@ public class WebSocketProxyMiddleware
} }
// Set user agent // Set user agent
serverWebSocket.Options.SetRequestHeader("User-Agent", "Allstarr/1.0"); serverWebSocket.Options.SetRequestHeader("User-Agent", "Allstarr/1.3.0");
await serverWebSocket.ConnectAsync(new Uri(jellyfinWsUrl), context.RequestAborted); await serverWebSocket.ConnectAsync(new Uri(jellyfinWsUrl), context.RequestAborted);
_logger.LogDebug("✓ WEBSOCKET: Connected to Jellyfin WebSocket"); _logger.LogDebug("✓ WEBSOCKET: Connected to Jellyfin WebSocket");

View File

@@ -18,7 +18,7 @@ public class LrclibService
ILogger<LrclibService> logger) ILogger<LrclibService> logger)
{ {
_httpClient = httpClientFactory.CreateClient(); _httpClient = httpClientFactory.CreateClient();
_httpClient.DefaultRequestHeaders.Add("User-Agent", "Allstarr/1.0.0 (https://github.com/SoPat712/allstarr)"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "Allstarr/1.3.0 (https://github.com/SoPat712/allstarr)");
_cache = cache; _cache = cache;
_logger = logger; _logger = logger;
} }

View File

@@ -22,7 +22,7 @@ public class LyricsPlusService
ILogger<LyricsPlusService> logger) ILogger<LyricsPlusService> logger)
{ {
_httpClient = httpClientFactory.CreateClient(); _httpClient = httpClientFactory.CreateClient();
_httpClient.DefaultRequestHeaders.Add("User-Agent", "Allstarr/1.0.0 (https://github.com/SoPat712/allstarr)"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "Allstarr/1.3.0 (https://github.com/SoPat712/allstarr)");
_cache = cache; _cache = cache;
_logger = logger; _logger = logger;
} }

View File

@@ -25,7 +25,7 @@ public class MusicBrainzService
ILogger<MusicBrainzService> logger) ILogger<MusicBrainzService> logger)
{ {
_httpClient = httpClientFactory.CreateClient(); _httpClient = httpClientFactory.CreateClient();
_httpClient.DefaultRequestHeaders.Add("User-Agent", "Allstarr/1.0.0 (https://github.com/SoPat712/allstarr)"); _httpClient.DefaultRequestHeaders.Add("User-Agent", "Allstarr/1.3.0 (https://github.com/SoPat712/allstarr)");
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
_settings = settings.Value; _settings = settings.Value;
@@ -92,6 +92,7 @@ public class MusicBrainzService
/// <summary> /// <summary>
/// Searches for recordings by title and artist. /// Searches for recordings by title and artist.
/// Note: Search API doesn't return genres, only MBIDs. Use LookupByMbidAsync to get genres.
/// </summary> /// </summary>
public async Task<List<MusicBrainzRecording>> SearchRecordingsAsync(string title, string artist, int limit = 5) public async Task<List<MusicBrainzRecording>> SearchRecordingsAsync(string title, string artist, int limit = 5)
{ {
@@ -107,7 +108,8 @@ public class MusicBrainzService
// Build Lucene query // Build Lucene query
var query = $"recording:\"{title}\" AND artist:\"{artist}\""; var query = $"recording:\"{title}\" AND artist:\"{artist}\"";
var encodedQuery = Uri.EscapeDataString(query); var encodedQuery = Uri.EscapeDataString(query);
var url = $"{_settings.BaseUrl}/recording?query={encodedQuery}&fmt=json&limit={limit}&inc=genres+tags"; // Note: Search API doesn't support inc=genres, only returns basic info + MBIDs
var url = $"{_settings.BaseUrl}/recording?query={encodedQuery}&fmt=json&limit={limit}";
_logger.LogDebug("MusicBrainz search: {Url}", url); _logger.LogDebug("MusicBrainz search: {Url}", url);
@@ -140,9 +142,56 @@ public class MusicBrainzService
} }
} }
/// <summary>
/// Looks up a recording by MBID to get full details including genres.
/// </summary>
public async Task<MusicBrainzRecording?> LookupByMbidAsync(string mbid)
{
if (!_settings.Enabled)
{
return null;
}
await RateLimitAsync();
try
{
var url = $"{_settings.BaseUrl}/recording/{mbid}?fmt=json&inc=artists+releases+release-groups+genres+tags";
_logger.LogDebug("MusicBrainz MBID lookup: {Url}", url);
var response = await _httpClient.GetAsync(url);
if (!response.IsSuccessStatusCode)
{
_logger.LogWarning("MusicBrainz MBID lookup failed: {StatusCode}", response.StatusCode);
return null;
}
var json = await response.Content.ReadAsStringAsync();
var recording = JsonSerializer.Deserialize<MusicBrainzRecording>(json, JsonOptions);
if (recording == null)
{
_logger.LogDebug("No MusicBrainz recording found for MBID: {Mbid}", mbid);
return null;
}
var genres = recording.Genres?.Select(g => g.Name).Where(n => !string.IsNullOrEmpty(n)).ToList() ?? new List<string?>();
_logger.LogInformation("✓ Found MusicBrainz recording for MBID {Mbid}: {Title} by {Artist} (Genres: {Genres})",
mbid, recording.Title, recording.ArtistCredit?[0]?.Name ?? "Unknown", string.Join(", ", genres));
return recording;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error looking up MBID {Mbid} in MusicBrainz", mbid);
return null;
}
}
/// <summary> /// <summary>
/// Enriches a song with genre information from MusicBrainz. /// Enriches a song with genre information from MusicBrainz.
/// First tries ISRC lookup, then falls back to title/artist search. /// First tries ISRC lookup, then falls back to title/artist search + MBID lookup.
/// </summary> /// </summary>
public async Task<List<string>> GetGenresForSongAsync(string title, string artist, string? isrc = null) public async Task<List<string>> GetGenresForSongAsync(string title, string artist, string? isrc = null)
{ {
@@ -153,17 +202,23 @@ public class MusicBrainzService
MusicBrainzRecording? recording = null; MusicBrainzRecording? recording = null;
// Try ISRC lookup first (most accurate) // Try ISRC lookup first (most accurate and includes genres)
if (!string.IsNullOrEmpty(isrc)) if (!string.IsNullOrEmpty(isrc))
{ {
recording = await LookupByIsrcAsync(isrc); recording = await LookupByIsrcAsync(isrc);
} }
// Fall back to search if ISRC lookup failed or no ISRC provided // Fall back to search + MBID lookup if ISRC lookup failed or no ISRC provided
if (recording == null) if (recording == null)
{ {
var recordings = await SearchRecordingsAsync(title, artist, limit: 1); var recordings = await SearchRecordingsAsync(title, artist, limit: 1);
recording = recordings.FirstOrDefault(); var searchResult = recordings.FirstOrDefault();
// If we found a recording from search, do a full lookup by MBID to get genres
if (searchResult != null && !string.IsNullOrEmpty(searchResult.Id))
{
recording = await LookupByMbidAsync(searchResult.Id);
}
} }
if (recording == null) if (recording == null)

View File

@@ -5,9 +5,9 @@
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>allstarr</RootNamespace> <RootNamespace>allstarr</RootNamespace>
<Version>1.2.2</Version> <Version>1.3.0</Version>
<AssemblyVersion>1.2.2.0</AssemblyVersion> <AssemblyVersion>1.3.0.0</AssemblyVersion>
<FileVersion>1.2.2.0</FileVersion> <FileVersion>1.3.0.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>