Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion Brainarr.Plugin/Services/Enrichment/ArtistMbidResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ public async Task<List<Recommendation>> EnrichArtistsAsync(List<Recommendation>
return new List<Recommendation>();

var result = new List<Recommendation>(recommendations.Count);
var resolvableCount = 0;
var resolvedCount = 0;

foreach (var rec in recommendations)
{
Expand All @@ -53,24 +55,34 @@ public async Task<List<Recommendation>> EnrichArtistsAsync(List<Recommendation>
continue;
}

resolvableCount++;

try
{
var enriched = await ResolveArtistAsync(rec, ct);
if (enriched != null)
{
resolvedCount++;
result.Add(enriched);
}
else
{
// Best-effort: preserve the original recommendation when MBID resolution fails.
// Downstream safety gates (RequireMbids) can still enforce MBID presence when configured.
result.Add(rec);
}
}
catch (Exception ex)
{
_logger.Debug(ex, $"Artist MBID resolution error for '{rec.Artist}'");
result.Add(rec);
}

// Be nice to MusicBrainz
await Task.Delay(200, ct);
}

_logger.Info($"Artist MBID resolution complete: {result.Count}/{recommendations.Count} resolvable artists");
_logger.Info($"Artist MBID resolution complete: resolved {resolvedCount}/{resolvableCount} artists (returned {result.Count})");
return result;
}

Expand Down
13 changes: 12 additions & 1 deletion Brainarr.Plugin/Services/Enrichment/MusicBrainzResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ public async Task<List<Recommendation>> EnrichWithMbidsAsync(List<Recommendation
return new List<Recommendation>();

var result = new List<Recommendation>(recommendations.Count);
var resolvableCount = 0;
var resolvedCount = 0;
// Deduplicate within this batch to avoid repeated queries
foreach (var rec in recommendations)
{
Expand All @@ -58,6 +60,8 @@ public async Task<List<Recommendation>> EnrichWithMbidsAsync(List<Recommendation
continue;
}

resolvableCount++;

// Check LRU cache first
var key = MakeKey(rec.Artist, rec.Album);
Recommendation cached = null;
Expand All @@ -72,6 +76,7 @@ public async Task<List<Recommendation>> EnrichWithMbidsAsync(List<Recommendation
var enriched = cached ?? await ResolveMbidAsync(rec, ct);
if (enriched != null)
{
resolvedCount++;
// Update LRU
lock (_lruLock)
{
Expand All @@ -85,6 +90,12 @@ public async Task<List<Recommendation>> EnrichWithMbidsAsync(List<Recommendation
}
result.Add(enriched);
}
else
{
// Best-effort: preserve the original recommendation when MBID resolution fails.
// Downstream safety gates (RequireMbids) can still enforce MBID presence when configured.
result.Add(rec);
}
}
catch (Exception ex)
{
Expand All @@ -94,7 +105,7 @@ public async Task<List<Recommendation>> EnrichWithMbidsAsync(List<Recommendation
// Throttling handled centrally via RateLimiter("musicbrainz")
}

_logger.Info($"MBID resolution complete: {result.Count}/{recommendations.Count} resolvable recommendations");
_logger.Info($"MBID resolution complete: resolved {resolvedCount}/{resolvableCount} recommendations (returned {result.Count})");
return result;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Brainarr.Tests.Helpers;
using FluentAssertions;
using NzbDrone.Core.ImportLists.Brainarr.Models;
using NzbDrone.Core.ImportLists.Brainarr.Services.Enrichment;
using Xunit;

namespace Brainarr.Tests.Services.Enrichment
{
public class ArtistMbidResolverBestEffortTests
{
private sealed class StubHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var response = new HttpResponseMessage(HttpStatusCode.ServiceUnavailable)
{
Content = new StringContent("{}")
};
return Task.FromResult(response);
}
}

[Fact]
public async Task EnrichArtistsAsync_PreservesOriginalRecommendation_WhenLookupFails()
{
// Arrange
var httpClient = new HttpClient(new StubHandler());
var resolver = new ArtistMbidResolver(TestLogger.CreateNullLogger(), httpClient);
var rec = new Recommendation { Artist = "Radiohead", Confidence = 0.9 };

// Act
var result = await resolver.EnrichArtistsAsync(new List<Recommendation> { rec }, CancellationToken.None);

// Assert
result.Should().HaveCount(1);
result[0].Artist.Should().Be("Radiohead");
result[0].ArtistMusicBrainzId.Should().BeNull();
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,20 @@ private class StubHandler : HttpMessageHandler
{
private int _calls;
private readonly string _content;
private readonly HttpStatusCode _statusCode;

public StubHandler(string content)
public StubHandler(string content, HttpStatusCode statusCode = HttpStatusCode.OK)
{
_content = content;
_statusCode = statusCode;
}

public int Calls => _calls;

protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
Interlocked.Increment(ref _calls);
var response = new HttpResponseMessage(HttpStatusCode.OK)
var response = new HttpResponseMessage(_statusCode)
{
Content = new StringContent(_content)
};
Expand Down Expand Up @@ -59,5 +61,27 @@ public async Task EnrichWithMbidsAsync_UsesInProcessCache_ForDuplicateItems()
result.Should().HaveCount(2);
handler.Calls.Should().Be(1); // Second item served from in-process cache
}

[Fact]
public async Task EnrichWithMbidsAsync_PreservesOriginalRecommendation_WhenLookupFails()
{
// Arrange
var handler = new StubHandler("{}", HttpStatusCode.ServiceUnavailable);
var httpClient = new HttpClient(handler);
var logger = TestLogger.CreateNullLogger();
var resolver = new MusicBrainzResolver(logger, httpClient);

var rec = new Recommendation { Artist = "Radiohead", Album = "OK Computer", Confidence = 0.9 };

// Act
var result = await resolver.EnrichWithMbidsAsync(new List<Recommendation> { rec }, CancellationToken.None);

// Assert
result.Should().HaveCount(1);
result[0].Artist.Should().Be("Radiohead");
result[0].Album.Should().Be("OK Computer");
result[0].ArtistMusicBrainzId.Should().BeNull();
result[0].AlbumMusicBrainzId.Should().BeNull();
}
}
}
Loading