aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Jellyfin.Server/Migrations/MigrationRunner.cs3
-rw-r--r--Jellyfin.Server/Migrations/PreStartupRoutines/MigrateMusicBrainzTimeout.cs89
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs1
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html26
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs90
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs55
6 files changed, 186 insertions, 78 deletions
diff --git a/Jellyfin.Server/Migrations/MigrationRunner.cs b/Jellyfin.Server/Migrations/MigrationRunner.cs
index 23fb9e370..8a6ab7932 100644
--- a/Jellyfin.Server/Migrations/MigrationRunner.cs
+++ b/Jellyfin.Server/Migrations/MigrationRunner.cs
@@ -21,7 +21,8 @@ namespace Jellyfin.Server.Migrations
/// </summary>
private static readonly Type[] _preStartupMigrationTypes =
{
- typeof(PreStartupRoutines.CreateNetworkConfiguration)
+ typeof(PreStartupRoutines.CreateNetworkConfiguration),
+ typeof(PreStartupRoutines.MigrateMusicBrainzTimeout)
};
/// <summary>
diff --git a/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateMusicBrainzTimeout.cs b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateMusicBrainzTimeout.cs
new file mode 100644
index 000000000..14b51bd4c
--- /dev/null
+++ b/Jellyfin.Server/Migrations/PreStartupRoutines/MigrateMusicBrainzTimeout.cs
@@ -0,0 +1,89 @@
+using System;
+using System.IO;
+using System.Xml;
+using System.Xml.Serialization;
+using Emby.Server.Implementations;
+using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Migrations.PreStartupRoutines;
+
+/// <inheritdoc />
+public class MigrateMusicBrainzTimeout : IMigrationRoutine
+{
+ private readonly ServerApplicationPaths _applicationPaths;
+ private readonly ILogger<MigrateMusicBrainzTimeout> _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MigrateMusicBrainzTimeout"/> class.
+ /// </summary>
+ /// <param name="applicationPaths">An instance of <see cref="ServerApplicationPaths"/>.</param>
+ /// <param name="loggerFactory">An instance of the <see cref="ILoggerFactory"/> interface.</param>
+ public MigrateMusicBrainzTimeout(ServerApplicationPaths applicationPaths, ILoggerFactory loggerFactory)
+ {
+ _applicationPaths = applicationPaths;
+ _logger = loggerFactory.CreateLogger<MigrateMusicBrainzTimeout>();
+ }
+
+ /// <inheritdoc />
+ public Guid Id => Guid.Parse("A6DCACF4-C057-4Ef9-80D3-61CEF9DDB4F0");
+
+ /// <inheritdoc />
+ public string Name => nameof(MigrateMusicBrainzTimeout);
+
+ /// <inheritdoc />
+ public bool PerformOnNewInstall => false;
+
+ /// <inheritdoc />
+ public void Perform()
+ {
+ string path = Path.Combine(_applicationPaths.PluginConfigurationsPath, "Jellyfin.Plugin.MusicBrainz.xml");
+ if (!File.Exists(path))
+ {
+ _logger.LogDebug("No MusicBrainz plugin configuration file found, skipping");
+ return;
+ }
+
+ var serverConfigSerializer = new XmlSerializer(typeof(OldMusicBrainzConfiguration), new XmlRootAttribute("PluginConfiguration"));
+ using var xmlReader = XmlReader.Create(path);
+ var oldPluginConfiguration = serverConfigSerializer.Deserialize(xmlReader) as OldMusicBrainzConfiguration;
+
+ if (oldPluginConfiguration is not null)
+ {
+ var newPluginConfiguration = new PluginConfiguration();
+ newPluginConfiguration.Server = oldPluginConfiguration.Server;
+ newPluginConfiguration.ReplaceArtistName = oldPluginConfiguration.ReplaceArtistName;
+ var newRateLimit = oldPluginConfiguration.RateLimit / 1000.0;
+ newPluginConfiguration.RateLimit = newRateLimit < 1.0 ? 1.0 : newRateLimit;
+
+ var pluginConfigurationSerializer = new XmlSerializer(typeof(PluginConfiguration), new XmlRootAttribute("PluginConfiguration"));
+ var xmlWriterSettings = new XmlWriterSettings { Indent = true };
+ using var xmlWriter = XmlWriter.Create(path, xmlWriterSettings);
+ pluginConfigurationSerializer.Serialize(xmlWriter, newPluginConfiguration);
+ }
+ }
+
+#pragma warning disable
+ public sealed class OldMusicBrainzConfiguration
+ {
+ private string _server = string.Empty;
+
+ private long _rateLimit = 0L;
+
+ public string Server
+ {
+ get => _server;
+ set => _server = value.TrimEnd('/');
+ }
+
+ public long RateLimit
+ {
+ get => _rateLimit;
+ set => _rateLimit = value;
+ }
+
+ public bool ReplaceArtistName { get; set; }
+ }
+#pragma warning restore
+
+}
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
index a97b56743..a2f3c63f0 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/PluginConfiguration.cs
@@ -1,5 +1,4 @@
using MediaBrowser.Model.Plugins;
-using MetaBrainz.MusicBrainz;
namespace MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
index 6f1296bb7..62d86cd8f 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/Configuration/config.html
@@ -1,20 +1,16 @@
-<!DOCTYPE html>
-<html>
-<head>
- <title>MusicBrainz</title>
-</head>
-<body>
- <div data-role="page" class="page type-interior pluginConfigurationPage musicBrainzConfigPage" data-require="emby-input,emby-button,emby-checkbox">
- <div data-role="content">
- <div class="content-primary">
- <form class="musicBrainzConfigForm">
+<div id="musicBrainzConfigurationPage" data-role="page"
+ class="page type-interior pluginConfigurationPage musicBrainzConfigurationPage" data-require="emby-input,emby-button,emby-checkbox">
+ <div data-role="content">
+ <div class="content-primary">
+ <h1>MusicBrainz</h1>
+ <form class="musicBrainzConfigurationForm">
<div class="inputContainer">
<input is="emby-input" type="text" id="server" required label="Server" />
<div class="fieldDescription">This can be a mirror of the official server or even a custom server.</div>
</div>
<div class="inputContainer">
- <input is="emby-input" type="number" id="rateLimit" pattern="[0-9]*" required min="0" max="10000" label="Rate Limit" />
- <div class="fieldDescription">Span of time between requests in milliseconds. The official server is limited to one request every two seconds.</div>
+ <input is="emby-input" type="number" id="rateLimit" required pattern="[0-9]*" min="0" max="10" step=".01" label="Rate Limit" />
+ <div class="fieldDescription">Span of time between requests in seconds. The official server is limited to one request every seconds.</div>
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="replaceArtistName" />
@@ -32,7 +28,7 @@
uniquePluginId: "8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a"
};
- document.querySelector('.musicBrainzConfigPage')
+ document.querySelector('.musicBrainzConfigurationPage')
.addEventListener('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
@@ -49,14 +45,14 @@
bubbles: true,
cancelable: false
}));
-
+
document.querySelector('#replaceArtistName').checked = config.ReplaceArtistName;
Dashboard.hideLoadingMsg();
});
});
- document.querySelector('.musicBrainzConfigForm')
+ document.querySelector('.musicBrainzConfigurationForm')
.addEventListener('submit', function (e) {
Dashboard.showLoadingMsg();
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index 34f45f0d5..3afa90bae 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -8,8 +8,10 @@ using Jellyfin.Extensions;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Music;
+using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
using MetaBrainz.MusicBrainz;
using MetaBrainz.MusicBrainz.Interfaces.Entities;
using MetaBrainz.MusicBrainz.Interfaces.Searches;
@@ -23,8 +25,7 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz;
public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder, IDisposable
{
private readonly ILogger<MusicBrainzAlbumProvider> _logger;
- private readonly Query _musicBrainzQuery;
- private readonly string _musicBrainzDefaultUri = "https://musicbrainz.org";
+ private Query _musicBrainzQuery;
/// <summary>
/// Initializes a new instance of the <see cref="MusicBrainzAlbumProvider"/> class.
@@ -33,29 +34,9 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
public MusicBrainzAlbumProvider(ILogger<MusicBrainzAlbumProvider> logger)
{
_logger = logger;
-
- MusicBrainz.Plugin.Instance!.ConfigurationChanged += (_, _) =>
- {
- if (Uri.TryCreate(MusicBrainz.Plugin.Instance.Configuration.Server, UriKind.Absolute, out var server))
- {
- Query.DefaultServer = server.Host;
- Query.DefaultPort = server.Port;
- Query.DefaultUrlScheme = server.Scheme;
- }
- else
- {
- // Fallback to official server
- _logger.LogWarning("Invalid MusicBrainz server specified, falling back to official server");
- var defaultServer = new Uri(_musicBrainzDefaultUri);
- Query.DefaultServer = defaultServer.Host;
- Query.DefaultPort = defaultServer.Port;
- Query.DefaultUrlScheme = defaultServer.Scheme;
- }
-
- Query.DelayBetweenRequests = MusicBrainz.Plugin.Instance.Configuration.RateLimit;
- };
-
_musicBrainzQuery = new Query();
+ ReloadConfig(null, MusicBrainz.Plugin.Instance!.Configuration);
+ MusicBrainz.Plugin.Instance!.ConfigurationChanged += ReloadConfig;
}
/// <inheritdoc />
@@ -64,6 +45,29 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
/// <inheritdoc />
public int Order => 0;
+ private void ReloadConfig(object? sender, BasePluginConfiguration e)
+ {
+ var configuration = (PluginConfiguration)e;
+ if (Uri.TryCreate(configuration.Server, UriKind.Absolute, out var server))
+ {
+ Query.DefaultServer = server.DnsSafeHost;
+ Query.DefaultPort = server.Port;
+ Query.DefaultUrlScheme = server.Scheme;
+ }
+ else
+ {
+ // Fallback to official server
+ _logger.LogWarning("Invalid MusicBrainz server specified, falling back to official server");
+ var defaultServer = new Uri(configuration.Server);
+ Query.DefaultServer = defaultServer.Host;
+ Query.DefaultPort = defaultServer.Port;
+ Query.DefaultUrlScheme = defaultServer.Scheme;
+ }
+
+ Query.DelayBetweenRequests = configuration.RateLimit;
+ _musicBrainzQuery = new Query();
+ }
+
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
{
@@ -72,13 +76,13 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
if (!string.IsNullOrEmpty(releaseId))
{
- var releaseResult = await _musicBrainzQuery.LookupReleaseAsync(new Guid(releaseId), Include.ReleaseGroups, cancellationToken).ConfigureAwait(false);
+ var releaseResult = await _musicBrainzQuery.LookupReleaseAsync(new Guid(releaseId), Include.Artists | Include.ReleaseGroups, cancellationToken).ConfigureAwait(false);
return GetReleaseResult(releaseResult).SingleItemAsEnumerable();
}
if (!string.IsNullOrEmpty(releaseGroupId))
{
- var releaseGroupResult = await _musicBrainzQuery.LookupReleaseGroupAsync(new Guid(releaseGroupId), Include.None, null, cancellationToken).ConfigureAwait(false);
+ var releaseGroupResult = await _musicBrainzQuery.LookupReleaseGroupAsync(new Guid(releaseGroupId), Include.Releases, null, cancellationToken).ConfigureAwait(false);
return GetReleaseGroupResult(releaseGroupResult.Releases);
}
@@ -133,7 +137,9 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
foreach (var result in releaseSearchResults)
{
- yield return GetReleaseResult(result);
+ // Fetch full release info, otherwise artists are missing
+ var fullResult = _musicBrainzQuery.LookupRelease(result.Id, Include.Artists | Include.ReleaseGroups);
+ yield return GetReleaseResult(fullResult);
}
}
@@ -143,21 +149,33 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
{
Name = releaseSearchResult.Title,
ProductionYear = releaseSearchResult.Date?.Year,
- PremiereDate = releaseSearchResult.Date?.NearestDate
+ PremiereDate = releaseSearchResult.Date?.NearestDate,
+ SearchProviderName = Name
};
- if (releaseSearchResult.ArtistCredit?.Count > 0)
+ // Add artists and use first as album artist
+ var artists = releaseSearchResult.ArtistCredit;
+ if (artists is not null && artists.Count > 0)
{
- searchResult.AlbumArtist = new RemoteSearchResult
- {
- SearchProviderName = Name,
- Name = releaseSearchResult.ArtistCredit[0].Name
- };
+ var artistResults = new List<RemoteSearchResult>();
- if (releaseSearchResult.ArtistCredit[0].Artist?.Id is not null)
+ foreach (var artist in artists)
{
- searchResult.AlbumArtist.SetProviderId(MetadataProvider.MusicBrainzArtist, releaseSearchResult.ArtistCredit[0].Artist!.Id.ToString());
+ var artistResult = new RemoteSearchResult
+ {
+ Name = artist.Name
+ };
+
+ if (artist.Artist?.Id is not null)
+ {
+ artistResult.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Artist!.Id.ToString());
+ }
+
+ artistResults.Add(artistResult);
}
+
+ searchResult.AlbumArtist = artistResults[0];
+ searchResult.Artists = artistResults.ToArray();
}
searchResult.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseSearchResult.Id.ToString());
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs
index 718b5a1c4..be1d87675 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs
@@ -8,8 +8,10 @@ using Jellyfin.Extensions;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Music;
+using MediaBrowser.Providers.Plugins.MusicBrainz.Configuration;
using MetaBrainz.MusicBrainz;
using MetaBrainz.MusicBrainz.Interfaces.Entities;
using MetaBrainz.MusicBrainz.Interfaces.Searches;
@@ -23,8 +25,7 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz;
public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IDisposable
{
private readonly ILogger<MusicBrainzArtistProvider> _logger;
- private readonly Query _musicBrainzQuery;
- private readonly string _musicBrainzDefaultUri = "https://musicbrainz.org";
+ private Query _musicBrainzQuery;
/// <summary>
/// Initializes a new instance of the <see cref="MusicBrainzArtistProvider"/> class.
@@ -33,34 +34,37 @@ public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, Ar
public MusicBrainzArtistProvider(ILogger<MusicBrainzArtistProvider> logger)
{
_logger = logger;
-
- MusicBrainz.Plugin.Instance!.ConfigurationChanged += (_, _) =>
- {
- if (Uri.TryCreate(MusicBrainz.Plugin.Instance.Configuration.Server, UriKind.Absolute, out var server))
- {
- Query.DefaultServer = server.Host;
- Query.DefaultPort = server.Port;
- Query.DefaultUrlScheme = server.Scheme;
- }
- else
- {
- // Fallback to official server
- _logger.LogWarning("Invalid MusicBrainz server specified, falling back to official server");
- var defaultServer = new Uri(_musicBrainzDefaultUri);
- Query.DefaultServer = defaultServer.Host;
- Query.DefaultPort = defaultServer.Port;
- Query.DefaultUrlScheme = defaultServer.Scheme;
- }
-
- Query.DelayBetweenRequests = MusicBrainz.Plugin.Instance.Configuration.RateLimit;
- };
-
_musicBrainzQuery = new Query();
+ ReloadConfig(null, MusicBrainz.Plugin.Instance!.Configuration);
+ MusicBrainz.Plugin.Instance!.ConfigurationChanged += ReloadConfig;
}
/// <inheritdoc />
public string Name => "MusicBrainz";
+ private void ReloadConfig(object? sender, BasePluginConfiguration e)
+ {
+ var configuration = (PluginConfiguration)e;
+ if (Uri.TryCreate(configuration.Server, UriKind.Absolute, out var server))
+ {
+ Query.DefaultServer = server.DnsSafeHost;
+ Query.DefaultPort = server.Port;
+ Query.DefaultUrlScheme = server.Scheme;
+ }
+ else
+ {
+ // Fallback to official server
+ _logger.LogWarning("Invalid MusicBrainz server specified, falling back to official server");
+ var defaultServer = new Uri(configuration.Server);
+ Query.DefaultServer = defaultServer.Host;
+ Query.DefaultPort = defaultServer.Port;
+ Query.DefaultUrlScheme = defaultServer.Scheme;
+ }
+
+ Query.DelayBetweenRequests = configuration.RateLimit;
+ _musicBrainzQuery = new Query();
+ }
+
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
{
@@ -112,7 +116,8 @@ public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, Ar
{
Name = artist.Name,
ProductionYear = artist.LifeSpan?.Begin?.Year,
- PremiereDate = artist.LifeSpan?.Begin?.NearestDate
+ PremiereDate = artist.LifeSpan?.Begin?.NearestDate,
+ SearchProviderName = Name,
};
searchResult.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Id.ToString());