< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Plugins.MusicBrainz.MusicBrainzArtistProvider
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs
Line coverage
2%
Covered lines: 2
Uncovered lines: 73
Coverable lines: 75
Total lines: 190
Line coverage: 2.6%
Branch coverage
0%
Covered branches: 0
Total branches: 58
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/11/2026 - 12:15:59 AM Line coverage: 0% (0/48) Branch coverage: 0% (0/28) Total lines: 1317/29/2026 - 12:15:53 AM Line coverage: 0% (0/72) Branch coverage: 0% (0/56) Total lines: 17510/29/2026 - 12:17:11 AM Line coverage: 2.6% (2/75) Branch coverage: 0% (0/58) Total lines: 190 5/11/2026 - 12:15:59 AM Line coverage: 0% (0/48) Branch coverage: 0% (0/28) Total lines: 1317/29/2026 - 12:15:53 AM Line coverage: 0% (0/72) Branch coverage: 0% (0/56) Total lines: 17510/29/2026 - 12:17:11 AM Line coverage: 2.6% (2/75) Branch coverage: 0% (0/58) Total lines: 190

Coverage delta

Coverage delta 3 -3

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Name()100%210%
get_Order()100%210%
GetSearchResults()0%156120%
GetResultsFromResponse()0%2040%
GetResultFromResponse(...)0%7280%
GetMetadata()0%1190340%
GetImageResponse(...)100%210%

File(s)

/srv/git/jellyfin/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Net.Http;
 5using System.Threading;
 6using System.Threading.Tasks;
 7using Jellyfin.Extensions;
 8using MediaBrowser.Controller.Entities.Audio;
 9using MediaBrowser.Controller.Providers;
 10using MediaBrowser.Model.Entities;
 11using MediaBrowser.Model.Providers;
 12using MediaBrowser.Providers.Music;
 13using MetaBrainz.MusicBrainz;
 14using MetaBrainz.MusicBrainz.Interfaces.Entities;
 15using MetaBrainz.MusicBrainz.Interfaces.Searches;
 16using Microsoft.Extensions.Logging;
 17
 18namespace MediaBrowser.Providers.Plugins.MusicBrainz;
 19
 20/// <summary>
 21/// MusicBrainz artist provider.
 22/// </summary>
 23public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IHasOrder
 24{
 25    private readonly ILogger<MusicBrainzArtistProvider> _logger;
 26
 27    /// <summary>
 28    /// Initializes a new instance of the <see cref="MusicBrainzArtistProvider"/> class.
 29    /// </summary>
 30    /// <param name="logger">The logger.</param>
 31    public MusicBrainzArtistProvider(ILogger<MusicBrainzArtistProvider> logger)
 32    {
 2733        _logger = logger;
 2734    }
 35
 36    /// <inheritdoc />
 037    public string Name => "MusicBrainz";
 38
 39    /// <inheritdoc />
 40    /// Runs first to populate the MusicBrainz artist ID used by downstream providers.
 041    public int Order => 0;
 42
 43    /// <inheritdoc />
 44    public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancell
 45    {
 046        var query = MusicBrainz.Plugin.Instance!.MusicBrainzQuery;
 047        var artistId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchInfo.GetMusicBrainzArtistId(), "artist", _log
 48
 049        if (artistId is not null)
 50        {
 051            var artistResult = await query.LookupArtistOrNullAsync(artistId.Value, Include.Aliases, _logger, cancellatio
 052            if (artistResult is not null)
 53            {
 054                return GetResultFromResponse(artistResult).SingleItemAsEnumerable();
 55            }
 56        }
 57
 058        if (string.IsNullOrWhiteSpace(searchInfo.Name))
 59        {
 060            return [];
 61        }
 62
 063        var artistSearchResults = await query.FindArtistsWithRetryAsync($"\"{searchInfo.Name}\"", _logger, cancellationT
 064            .ConfigureAwait(false);
 065        if (artistSearchResults.Results.Count > 0)
 66        {
 067            return GetResultsFromResponse(artistSearchResults.Results);
 68        }
 69
 070        if (searchInfo.Name.HasDiacritics())
 71        {
 72            // Try again using the search with an accented characters query
 073            var artistAccentsSearchResults = await query.FindArtistsWithRetryAsync($"artistaccent:\"{searchInfo.Name}\""
 074                .ConfigureAwait(false);
 075            if (artistAccentsSearchResults.Results.Count > 0)
 76            {
 077                return GetResultsFromResponse(artistAccentsSearchResults.Results);
 78            }
 79        }
 80
 081        return [];
 082    }
 83
 84    private IEnumerable<RemoteSearchResult> GetResultsFromResponse(IEnumerable<ISearchResult<IArtist>>? releaseSearchRes
 85    {
 086        if (releaseSearchResults is null)
 87        {
 088            yield break;
 89        }
 90
 091        foreach (var result in releaseSearchResults)
 92        {
 093            yield return GetResultFromResponse(result.Item);
 94        }
 095    }
 96
 97    private RemoteSearchResult GetResultFromResponse(IArtist artist)
 98    {
 099        var searchResult = new RemoteSearchResult
 0100        {
 0101            Name = artist.Name,
 0102            ProductionYear = artist.LifeSpan?.Begin?.Year,
 0103            PremiereDate = artist.LifeSpan?.Begin?.NearestDate,
 0104            SearchProviderName = Name,
 0105        };
 106
 0107        searchResult.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Id.ToString());
 108
 0109        return searchResult;
 110    }
 111
 112    /// <inheritdoc />
 113    public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
 114    {
 0115        var result = new MetadataResult<MusicArtist> { Item = new MusicArtist() };
 116
 0117        var musicBrainzId = MusicBrainzQueryExtensions.ParseMusicBrainzId(info.GetMusicBrainzArtistId(), "artist", _logg
 118
 119        // If we don't have an id yet, resolve one by name so we can look the artist up.
 0120        if (musicBrainzId is null)
 121        {
 0122            var searchResults = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
 0123            musicBrainzId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchResults.FirstOrDefault()?.GetProviderId(
 124        }
 125
 0126        if (musicBrainzId is null)
 127        {
 0128            return result;
 129        }
 130
 0131        var query = Plugin.Instance!.MusicBrainzQuery;
 0132        var artist = await query.LookupArtistOrNullAsync(musicBrainzId.Value, Include.Genres | Include.Tags, _logger, ca
 133
 0134        if (artist is null)
 135        {
 0136            return result;
 137        }
 138
 0139        result.HasMetadata = true;
 0140        result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Id.ToString());
 141
 0142        if (Plugin.Instance!.Configuration.ReplaceArtistName && !string.IsNullOrWhiteSpace(artist.Name))
 143        {
 0144            result.Item.Name = artist.Name;
 145        }
 146
 0147        if (artist.LifeSpan?.Begin is not null)
 148        {
 0149            result.Item.PremiereDate = artist.LifeSpan.Begin.NearestDate;
 0150            result.Item.ProductionYear = artist.LifeSpan.Begin.Year;
 151        }
 152
 0153        if (artist.LifeSpan?.End is not null)
 154        {
 0155            result.Item.EndDate = artist.LifeSpan.End.NearestDate;
 156        }
 157
 0158        var location = string.IsNullOrWhiteSpace(artist.Area?.Name) ? artist.Country : artist.Area!.Name;
 0159        if (!string.IsNullOrWhiteSpace(location))
 160        {
 0161            result.Item.ProductionLocations = [location];
 162        }
 163
 0164        if (artist.Genres is not null && artist.Genres.Count > 0)
 165        {
 0166            result.Item.Genres = artist.Genres
 0167                .OrderByDescending(genre => genre.VoteCount)
 0168                .Select(genre => genre.Name)
 0169                .Where(name => !string.IsNullOrWhiteSpace(name))
 0170                .ToArray();
 171        }
 172
 0173        if (artist.Tags is not null && artist.Tags.Count > 0)
 174        {
 0175            result.Item.Tags = artist.Tags
 0176                .OrderByDescending(tag => tag.VoteCount)
 0177                .Select(tag => tag.Name)
 0178                .Where(name => !string.IsNullOrWhiteSpace(name))
 0179                .ToArray();
 180        }
 181
 0182        return result;
 0183    }
 184
 185    /// <inheritdoc />
 186    public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
 187    {
 0188        throw new NotImplementedException();
 189    }
 190}