< 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
54%
Covered lines: 19
Uncovered lines: 16
Coverable lines: 35
Total lines: 186
Line coverage: 54.2%
Branch coverage
25%
Covered branches: 3
Total branches: 12
Branch coverage: 25%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Name()100%210%
ReloadConfig(...)50%2.23261.53%
GetResultFromResponse(...)0%7280%
GetImageResponse(...)100%210%
Dispose()100%11100%
Dispose(...)100%22100%

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.Plugins;
 12using MediaBrowser.Model.Providers;
 13using MediaBrowser.Providers.Music;
 14using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
 15using MetaBrainz.MusicBrainz;
 16using MetaBrainz.MusicBrainz.Interfaces.Entities;
 17using MetaBrainz.MusicBrainz.Interfaces.Searches;
 18using Microsoft.Extensions.Logging;
 19
 20namespace MediaBrowser.Providers.Plugins.MusicBrainz;
 21
 22/// <summary>
 23/// MusicBrainz artist provider.
 24/// </summary>
 25public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IDisposable
 26{
 27    private readonly ILogger<MusicBrainzArtistProvider> _logger;
 28    private Query _musicBrainzQuery;
 29
 30    /// <summary>
 31    /// Initializes a new instance of the <see cref="MusicBrainzArtistProvider"/> class.
 32    /// </summary>
 33    /// <param name="logger">The logger.</param>
 34    public MusicBrainzArtistProvider(ILogger<MusicBrainzArtistProvider> logger)
 35    {
 2236        _logger = logger;
 2237        _musicBrainzQuery = new Query();
 2238        ReloadConfig(null, MusicBrainz.Plugin.Instance!.Configuration);
 2239        MusicBrainz.Plugin.Instance!.ConfigurationChanged += ReloadConfig;
 2240    }
 41
 42    /// <inheritdoc />
 043    public string Name => "MusicBrainz";
 44
 45    private void ReloadConfig(object? sender, BasePluginConfiguration e)
 46    {
 2247        var configuration = (PluginConfiguration)e;
 2248        if (Uri.TryCreate(configuration.Server, UriKind.Absolute, out var server))
 49        {
 2250            Query.DefaultServer = server.DnsSafeHost;
 2251            Query.DefaultPort = server.Port;
 2252            Query.DefaultUrlScheme = server.Scheme;
 53        }
 54        else
 55        {
 56            // Fallback to official server
 057            _logger.LogWarning("Invalid MusicBrainz server specified, falling back to official server");
 058            var defaultServer = new Uri(PluginConfiguration.DefaultServer);
 059            Query.DefaultServer = defaultServer.Host;
 060            Query.DefaultPort = defaultServer.Port;
 061            Query.DefaultUrlScheme = defaultServer.Scheme;
 62        }
 63
 2264        Query.DelayBetweenRequests = configuration.RateLimit;
 2265        _musicBrainzQuery = new Query();
 2266    }
 67
 68    /// <inheritdoc />
 69    public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancell
 70    {
 71        var artistId = searchInfo.GetMusicBrainzArtistId();
 72
 73        if (!string.IsNullOrWhiteSpace(artistId))
 74        {
 75            var artistResult = await _musicBrainzQuery.LookupArtistAsync(new Guid(artistId), Include.Aliases, null, null
 76            return GetResultFromResponse(artistResult).SingleItemAsEnumerable();
 77        }
 78
 79        var artistSearchResults = await _musicBrainzQuery.FindArtistsAsync($"\"{searchInfo.Name}\"", null, null, false, 
 80            .ConfigureAwait(false);
 81        if (artistSearchResults.Results.Count > 0)
 82        {
 83            return GetResultsFromResponse(artistSearchResults.Results);
 84        }
 85
 86        if (searchInfo.Name.HasDiacritics())
 87        {
 88            // Try again using the search with an accented characters query
 89            var artistAccentsSearchResults = await _musicBrainzQuery.FindArtistsAsync($"artistaccent:\"{searchInfo.Name}
 90                .ConfigureAwait(false);
 91            if (artistAccentsSearchResults.Results.Count > 0)
 92            {
 93                return GetResultsFromResponse(artistAccentsSearchResults.Results);
 94            }
 95        }
 96
 97        return Enumerable.Empty<RemoteSearchResult>();
 98    }
 99
 100    private IEnumerable<RemoteSearchResult> GetResultsFromResponse(IEnumerable<ISearchResult<IArtist>>? releaseSearchRes
 101    {
 102        if (releaseSearchResults is null)
 103        {
 104            yield break;
 105        }
 106
 107        foreach (var result in releaseSearchResults)
 108        {
 109            yield return GetResultFromResponse(result.Item);
 110        }
 111    }
 112
 113    private RemoteSearchResult GetResultFromResponse(IArtist artist)
 114    {
 0115        var searchResult = new RemoteSearchResult
 0116        {
 0117            Name = artist.Name,
 0118            ProductionYear = artist.LifeSpan?.Begin?.Year,
 0119            PremiereDate = artist.LifeSpan?.Begin?.NearestDate,
 0120            SearchProviderName = Name,
 0121        };
 122
 0123        searchResult.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Id.ToString());
 124
 0125        return searchResult;
 126    }
 127
 128    /// <inheritdoc />
 129    public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
 130    {
 131        var result = new MetadataResult<MusicArtist> { Item = new MusicArtist() };
 132
 133        var musicBrainzId = info.GetMusicBrainzArtistId();
 134
 135        if (string.IsNullOrWhiteSpace(musicBrainzId))
 136        {
 137            var searchResults = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
 138
 139            var singleResult = searchResults.FirstOrDefault();
 140
 141            if (singleResult is not null)
 142            {
 143                musicBrainzId = singleResult.GetProviderId(MetadataProvider.MusicBrainzArtist);
 144                result.Item.Overview = singleResult.Overview;
 145
 146                if (Plugin.Instance!.Configuration.ReplaceArtistName)
 147                {
 148                    result.Item.Name = singleResult.Name;
 149                }
 150            }
 151        }
 152
 153        if (!string.IsNullOrWhiteSpace(musicBrainzId))
 154        {
 155            result.HasMetadata = true;
 156            result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzId);
 157        }
 158
 159        return result;
 160    }
 161
 162    /// <inheritdoc />
 163    public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
 164    {
 0165        throw new NotImplementedException();
 166    }
 167
 168    /// <inheritdoc />
 169    public void Dispose()
 170    {
 22171        Dispose(true);
 22172        GC.SuppressFinalize(this);
 22173    }
 174
 175    /// <summary>
 176    /// Dispose all resources.
 177    /// </summary>
 178    /// <param name="disposing">Whether to dispose.</param>
 179    protected virtual void Dispose(bool disposing)
 180    {
 22181        if (disposing)
 182        {
 22183            _musicBrainzQuery.Dispose();
 184        }
 22185    }
 186}