< Summary - Jellyfin

Information
Class: MediaBrowser.Providers.Plugins.MusicBrainz.Plugin
Assembly: MediaBrowser.Providers
File(s): /srv/git/jellyfin/MediaBrowser.Providers/Plugins/MusicBrainz/Plugin.cs
Line coverage
69%
Covered lines: 36
Uncovered lines: 16
Coverable lines: 52
Total lines: 162
Line coverage: 69.2%
Branch coverage
66%
Covered branches: 4
Total branches: 6
Branch coverage: 66.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 2/13/2026 - 12:11:21 AM Line coverage: 100% (11/11) Total lines: 644/19/2026 - 12:14:27 AM Line coverage: 100% (17/17) Total lines: 645/7/2026 - 12:15:44 AM Line coverage: 68.6% (35/51) Branch coverage: 66.6% (4/6) Total lines: 1585/22/2026 - 12:15:17 AM Line coverage: 69.2% (36/52) Branch coverage: 66.6% (4/6) Total lines: 162 5/7/2026 - 12:15:44 AM Line coverage: 68.6% (35/51) Branch coverage: 66.6% (4/6) Total lines: 1585/22/2026 - 12:15:17 AM Line coverage: 69.2% (36/52) Branch coverage: 66.6% (4/6) Total lines: 162

Coverage delta

Coverage delta 32 -32

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Id()100%11100%
get_Name()100%11100%
get_Description()100%11100%
get_ConfigurationFileName()100%11100%
get_ImageResourceName()100%11100%
get_MusicBrainzQuery()100%210%
GetPages()100%11100%
Dispose()100%11100%
Dispose(...)75%4487.5%
OnConfigurationChanged(...)100%210%
ApplyServerConfig(...)50%3240%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Diagnostics.CodeAnalysis;
 4using System.Net.Http.Headers;
 5using System.Threading;
 6using MediaBrowser.Common;
 7using MediaBrowser.Common.Configuration;
 8using MediaBrowser.Common.Plugins;
 9using MediaBrowser.Controller.Plugins;
 10using MediaBrowser.Model.Plugins;
 11using MediaBrowser.Model.Serialization;
 12using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
 13using MetaBrainz.MusicBrainz;
 14using Microsoft.Extensions.Logging;
 15
 16namespace MediaBrowser.Providers.Plugins.MusicBrainz;
 17
 18/// <summary>
 19/// Plugin instance.
 20/// </summary>
 21public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages, IHasEmbeddedImage, IDisposable
 22{
 23    private readonly ILogger<Plugin> _logger;
 3524    private readonly Lock _queryLock = new();
 25    private Query _musicBrainzQuery;
 26    private bool _disposed;
 27
 28    /// <summary>
 29    /// Initializes a new instance of the <see cref="Plugin"/> class.
 30    /// </summary>
 31    /// <param name="applicationPaths">Instance of the <see cref="IApplicationPaths"/> interface.</param>
 32    /// <param name="xmlSerializer">Instance of the <see cref="IXmlSerializer"/> interface.</param>
 33    /// <param name="applicationHost">Instance of the <see cref="IApplicationHost"/> interface.</param>
 34    /// <param name="logger">Instance of the <see cref="ILogger{Plugin}"/> interface.</param>
 35    public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer, IApplicationHost applicationHost, IL
 3536        : base(applicationPaths, xmlSerializer)
 37    {
 3538        Instance = this;
 3539        _logger = logger;
 40
 41        // TODO: Change this to "JellyfinMusicBrainzPlugin" once we take it out of the server repo.
 3542        Query.DefaultUserAgent.Add(new ProductInfoHeaderValue(applicationHost.Name.Replace(' ', '-'), applicationHost.Ap
 3543        Query.DefaultUserAgent.Add(new ProductInfoHeaderValue($"({applicationHost.ApplicationUserAgentAddress})"));
 44
 3545        ApplyServerConfig(Configuration);
 3546        Query.DelayBetweenRequests = Configuration.RateLimit;
 3547        _musicBrainzQuery = new Query();
 48
 3549        ConfigurationChanged += OnConfigurationChanged;
 3550    }
 51
 52    /// <summary>
 53    /// Gets the current plugin instance.
 54    /// </summary>
 55    public static Plugin? Instance { get; private set; }
 56
 57    /// <inheritdoc />
 2458    public override Guid Id => new Guid("8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a");
 59
 60    /// <inheritdoc />
 2961    public override string Name => "MusicBrainz";
 62
 63    /// <inheritdoc />
 164    public override string Description => "Get artist and album metadata from any MusicBrainz server.";
 65
 66    /// <inheritdoc />
 67    // TODO remove when plugin removed from server.
 7868    public override string ConfigurationFileName => "Jellyfin.Plugin.MusicBrainz.xml";
 69
 70    /// <inheritdoc />
 2171    public string ImageResourceName => GetType().Namespace + ".jellyfin-plugin-musicbrainz.svg";
 72
 73    /// <summary>
 74    /// Gets the current MusicBrainz query client.
 75    /// </summary>
 76    /// <remarks>
 77    /// Always read this property anew before each request — the underlying instance is
 78    /// replaced when the server URL changes. Old instances are intentionally left alive
 79    /// so in-flight requests can finish; their unmanaged resources leak until GC.
 80    /// </remarks>
 81    public Query MusicBrainzQuery
 82    {
 83        get
 084        {
 85            lock (_queryLock)
 86            {
 087                return _musicBrainzQuery;
 88            }
 089        }
 90    }
 91
 92    /// <inheritdoc />
 93    public IEnumerable<PluginPageInfo> GetPages()
 94    {
 595        yield return new PluginPageInfo
 596        {
 597            Name = Name,
 598            EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html"
 599        };
 5100    }
 101
 102    /// <inheritdoc />
 103    public void Dispose()
 104    {
 21105        Dispose(true);
 21106        GC.SuppressFinalize(this);
 21107    }
 108
 109    /// <summary>
 110    /// Releases unmanaged and managed resources.
 111    /// </summary>
 112    /// <param name="disposing">Whether to dispose managed resources.</param>
 113    protected virtual void Dispose(bool disposing)
 114    {
 21115        if (_disposed)
 116        {
 0117            return;
 118        }
 119
 21120        if (disposing)
 121        {
 21122            ConfigurationChanged -= OnConfigurationChanged;
 123            lock (_queryLock)
 124            {
 21125                _musicBrainzQuery.Dispose();
 21126            }
 127        }
 128
 21129        _disposed = true;
 21130    }
 131
 132    [SuppressMessage("IDisposableAnalyzers.Correctness", "IDISP003:Dispose previous before re-assigning", Justification 
 133    private void OnConfigurationChanged(object? sender, BasePluginConfiguration e)
 134    {
 0135        var configuration = (PluginConfiguration)e;
 0136        ApplyServerConfig(configuration);
 0137        Query.DelayBetweenRequests = configuration.RateLimit;
 138
 139        lock (_queryLock)
 140        {
 0141            _musicBrainzQuery = new Query();
 0142        }
 0143    }
 144
 145    private void ApplyServerConfig(PluginConfiguration configuration)
 146    {
 35147        if (Uri.TryCreate(configuration.Server, UriKind.Absolute, out var server))
 148        {
 35149            Query.DefaultServer = server.DnsSafeHost;
 35150            Query.DefaultPort = server.Port;
 35151            Query.DefaultUrlScheme = server.Scheme;
 152        }
 153        else
 154        {
 0155            _logger.LogWarning("Invalid MusicBrainz server specified, falling back to official server");
 0156            var defaultServer = new Uri(PluginConfiguration.DefaultServer);
 0157            Query.DefaultServer = defaultServer.Host;
 0158            Query.DefaultPort = defaultServer.Port;
 0159            Query.DefaultUrlScheme = defaultServer.Scheme;
 160        }
 0161    }
 162}