Merge pull request #2436 from dkanada/music

Add config options for MusicBrainz
This commit is contained in:
Vasily 2020-03-02 12:54:17 +03:00 committed by GitHub
commit 13d30a0a79
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 302 additions and 128 deletions

View File

@ -8,7 +8,6 @@ namespace Emby.Server.Implementations
public static Dictionary<string, string> Configuration => new Dictionary<string, string>
{
{ "HttpListenerHost:DefaultRedirectPath", "web/index.html" },
{ "MusicBrainz:BaseUrl", "https://www.musicbrainz.org" },
{ FfmpegProbeSizeKey, "1G" },
{ FfmpegAnalyzeDurationKey, "200M" }
};

View File

@ -944,7 +944,6 @@ namespace Emby.Server.Implementations.Library
IncludeItemTypes = new[] { typeof(T).Name },
Name = name,
DtoOptions = options
}).Cast<MusicArtist>()
.OrderBy(i => i.IsAccessedByName ? 1 : 0)
.Cast<T>()
@ -1080,7 +1079,7 @@ namespace Emby.Server.Implementations.Library
var innerProgress = new ActionableProgress<double>();
innerProgress.RegisterAction(pct => progress.Report(pct * pct * 0.96));
innerProgress.RegisterAction(pct => progress.Report(pct * 0.96));
// Validate the entire media library
await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true).ConfigureAwait(false);

View File

@ -198,6 +198,7 @@ namespace MediaBrowser.Controller.Entities.Audio
return true;
}
}
return base.RequiresRefresh();
}

View File

@ -322,10 +322,10 @@ namespace MediaBrowser.Controller.Entities
ProviderManager.OnRefreshProgress(this, 5);
}
//build a dictionary of the current children we have now by Id so we can compare quickly and easily
// Build a dictionary of the current children we have now by Id so we can compare quickly and easily
var currentChildren = GetActualChildrenDictionary();
//create a list for our validated children
// Create a list for our validated children
var newItems = new List<BaseItem>();
cancellationToken.ThrowIfCancellationRequested();
@ -391,7 +391,7 @@ namespace MediaBrowser.Controller.Entities
var folder = this;
innerProgress.RegisterAction(p =>
{
double newPct = .80 * p + 10;
double newPct = 0.80 * p + 10;
progress.Report(newPct);
ProviderManager.OnRefreshProgress(folder, newPct);
});
@ -421,7 +421,7 @@ namespace MediaBrowser.Controller.Entities
var folder = this;
innerProgress.RegisterAction(p =>
{
double newPct = .10 * p + 90;
double newPct = 0.10 * p + 90;
progress.Report(newPct);
if (recursive)
{

View File

@ -238,7 +238,7 @@ namespace MediaBrowser.Model.Configuration
CodecsUsed = Array.Empty<string>();
PathSubstitutions = Array.Empty<PathSubstitution>();
IgnoreVirtualInterfaces = false;
EnableSimpleArtistDetection = true;
EnableSimpleArtistDetection = false;
DisplaySpecialsWithinSeasons = true;
EnableExternalContentInSuggestions = true;

View File

@ -24,4 +24,9 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<None Remove="Plugins\MusicBrainz\Configuration\config.html" />
<EmbeddedResource Include="Plugins\MusicBrainz\Configuration\config.html" />
</ItemGroup>
</Project>

View File

@ -1,105 +1,9 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
namespace MediaBrowser.Providers.Music
{
public class MusicBrainzReleaseGroupExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Release Group";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://musicbrainz.org/release-group/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item)
=> item is Audio || item is MusicAlbum;
}
public class MusicBrainzAlbumArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Album Artist";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://musicbrainz.org/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item)
=> item is Audio;
}
public class MusicBrainzAlbumExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Album";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://musicbrainz.org/release/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item)
=> item is Audio || item is MusicAlbum;
}
public class MusicBrainzArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://musicbrainz.org/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is MusicArtist;
}
public class MusicBrainzOtherArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Artist";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://musicbrainz.org/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item)
=> item is Audio || item is MusicAlbum;
}
public class MusicBrainzTrackId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Track";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzTrack.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://musicbrainz.org/track/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio;
}
public class ImvdbId : IExternalId
{
/// <inheritdoc />

View File

@ -15,7 +15,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using Microsoft.Extensions.Configuration;
using MediaBrowser.Providers.Plugins.MusicBrainz;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Music
@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Music
/// Be prudent, use a value slightly above the minimun required.
/// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting
/// </summary>
private const long MusicBrainzQueryIntervalMs = 1050u;
private readonly long _musicBrainzQueryIntervalMs;
/// <summary>
/// For each single MB lookup/search, this is the maximum number of
@ -50,14 +50,14 @@ namespace MediaBrowser.Providers.Music
public MusicBrainzAlbumProvider(
IHttpClient httpClient,
IApplicationHost appHost,
ILogger logger,
IConfiguration configuration)
ILogger logger)
{
_httpClient = httpClient;
_appHost = appHost;
_logger = logger;
_musicBrainzBaseUrl = configuration["MusicBrainz:BaseUrl"];
_musicBrainzBaseUrl = Plugin.Instance.Configuration.Server;
_musicBrainzQueryIntervalMs = Plugin.Instance.Configuration.RateLimit;
// Use a stopwatch to ensure we don't exceed the MusicBrainz rate limit
_stopWatchMusicBrainz.Start();
@ -74,6 +74,12 @@ namespace MediaBrowser.Providers.Music
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
{
// TODO maybe remove when artist metadata can be disabled
if (!Plugin.Instance.Configuration.Enable)
{
return Enumerable.Empty<RemoteSearchResult>();
}
var releaseId = searchInfo.GetReleaseId();
var releaseGroupId = searchInfo.GetReleaseGroupId();
@ -107,8 +113,8 @@ namespace MediaBrowser.Providers.Music
url = string.Format(
CultureInfo.InvariantCulture,
"/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"",
WebUtility.UrlEncode(queryName),
WebUtility.UrlEncode(searchInfo.GetAlbumArtist()));
WebUtility.UrlEncode(queryName),
WebUtility.UrlEncode(searchInfo.GetAlbumArtist()));
}
}
@ -170,7 +176,6 @@ namespace MediaBrowser.Providers.Music
}
return result;
});
}
}
@ -187,6 +192,12 @@ namespace MediaBrowser.Providers.Music
Item = new MusicAlbum()
};
// TODO maybe remove when artist metadata can be disabled
if (!Plugin.Instance.Configuration.Enable)
{
return result;
}
// If we have a release group Id but not a release Id...
if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId))
{
@ -456,18 +467,6 @@ namespace MediaBrowser.Providers.Music
}
case "artist-credit":
{
// TODO
/*
* <artist-credit>
<name-credit>
<artist id="e225cda5-882d-4b80-b8a3-b36d7175b1ea">
<name>SARCASTIC+ZOOKEEPER</name>
<sort-name>SARCASTIC+ZOOKEEPER</sort-name>
</artist>
</name-credit>
</artist-credit>
*/
using (var subReader = reader.ReadSubtree())
{
var artist = ParseArtistCredit(subReader);
@ -764,10 +763,10 @@ namespace MediaBrowser.Providers.Music
{
attempts++;
if (_stopWatchMusicBrainz.ElapsedMilliseconds < MusicBrainzQueryIntervalMs)
if (_stopWatchMusicBrainz.ElapsedMilliseconds < _musicBrainzQueryIntervalMs)
{
// MusicBrainz is extremely adamant about limiting to one request per second
var delayMs = MusicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds;
var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds;
await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false);
}
@ -778,7 +777,7 @@ namespace MediaBrowser.Providers.Music
response = await _httpClient.SendAsync(options, "GET").ConfigureAwait(false);
// We retry a finite number of times, and only whilst MB is indcating 503 (throttling)
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling)
}
while (attempts < MusicBrainzQueryAttempts && response.StatusCode == HttpStatusCode.ServiceUnavailable);

View File

@ -14,6 +14,7 @@ using MediaBrowser.Controller.Extensions;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
using MediaBrowser.Providers.Plugins.MusicBrainz;
namespace MediaBrowser.Providers.Music
{
@ -22,6 +23,12 @@ namespace MediaBrowser.Providers.Music
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
{
// TODO maybe remove when artist metadata can be disabled
if (!Plugin.Instance.Configuration.Enable)
{
return Enumerable.Empty<RemoteSearchResult>();
}
var musicBrainzId = searchInfo.GetMusicBrainzArtistId();
if (!string.IsNullOrWhiteSpace(musicBrainzId))
@ -226,6 +233,12 @@ namespace MediaBrowser.Providers.Music
Item = new MusicArtist()
};
// TODO maybe remove when artist metadata can be disabled
if (!Plugin.Instance.Configuration.Enable)
{
return result;
}
var musicBrainzId = id.GetMusicBrainzArtistId();
if (string.IsNullOrWhiteSpace(musicBrainzId))
@ -237,8 +250,12 @@ namespace MediaBrowser.Providers.Music
if (singleResult != null)
{
musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist);
//result.Item.Name = singleResult.Name;
result.Item.Overview = singleResult.Overview;
if (Plugin.Instance.Configuration.ReplaceArtistName)
{
result.Item.Name = singleResult.Name;
}
}
}

View File

@ -0,0 +1,44 @@
using MediaBrowser.Model.Plugins;
namespace MediaBrowser.Providers.Plugins.MusicBrainz
{
public class PluginConfiguration : BasePluginConfiguration
{
private string _server = Plugin.DefaultServer;
private long _rateLimit = Plugin.DefaultRateLimit;
public string Server
{
get
{
return _server;
}
set
{
_server = value.TrimEnd('/');
}
}
public long RateLimit
{
get
{
return _rateLimit;
}
set
{
if (value < Plugin.DefaultRateLimit && _server == Plugin.DefaultServer)
{
RateLimit = Plugin.DefaultRateLimit;
}
}
}
public bool Enable { get; set; }
public bool ReplaceArtistName { get; set; }
}
}

View File

@ -0,0 +1,69 @@
<!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 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>
</div>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="enable" />
<span>Enable this provider for metadata searches on artists and albums.</span>
</label>
<label class="checkboxContainer">
<input is="emby-checkbox" type="checkbox" id="replaceArtistName" />
<span>When an artist is found during a metadata search, replace the artist name with the value on the server.</span>
</label>
<br />
<div>
<button is="emby-button" type="submit" class="raised button-submit block"><span>Save</span></button>
</div>
</form>
</div>
</div>
<script type="text/javascript">
var MusicBrainzPluginConfig = {
uniquePluginId: "8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a"
};
$('.musicBrainzConfigPage').on('pageshow', function () {
Dashboard.showLoadingMsg();
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
$('#server').val(config.Server).change();
$('#rateLimit').val(config.RateLimit).change();
$('#enable').checked(config.Enable);
$('#replaceArtistName').checked(config.ReplaceArtistName);
Dashboard.hideLoadingMsg();
});
});
$('.musicBrainzConfigForm').on('submit', function (e) {
Dashboard.showLoadingMsg();
var form = this;
ApiClient.getPluginConfiguration(MusicBrainzPluginConfig.uniquePluginId).then(function (config) {
config.Server = $('#server', form).val();
config.RateLimit = $('#rateLimit', form).val();
config.Enable = $('#enable', form).checked();
config.ReplaceArtistName = $('#replaceArtistName', form).checked();
ApiClient.updatePluginConfiguration(MusicBrainzPluginConfig.uniquePluginId, config).then(Dashboard.processPluginConfigurationUpdateResult);
});
return false;
});
</script>
</div>
</body>
</html>

View File

@ -0,0 +1,98 @@
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Providers.Plugins.MusicBrainz;
namespace MediaBrowser.Providers.Music
{
public class MusicBrainzReleaseGroupExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Release Group";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzReleaseGroup.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release-group/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
}
public class MusicBrainzAlbumArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Album Artist";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzAlbumArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio;
}
public class MusicBrainzAlbumExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Album";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/release/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
}
public class MusicBrainzArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is MusicArtist;
}
public class MusicBrainzOtherArtistExternalId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Artist";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio || item is MusicAlbum;
}
public class MusicBrainzTrackId : IExternalId
{
/// <inheritdoc />
public string Name => "MusicBrainz Track";
/// <inheritdoc />
public string Key => MetadataProviders.MusicBrainzTrack.ToString();
/// <inheritdoc />
public string UrlFormatString => Plugin.Instance.Configuration.Server + "/track/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) => item is Audio;
}
}

View File

@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Model.Plugins;
using MediaBrowser.Model.Serialization;
namespace MediaBrowser.Providers.Plugins.MusicBrainz
{
public class Plugin : BasePlugin<PluginConfiguration>, IHasWebPages
{
public static Plugin Instance { get; private set; }
public override Guid Id => new Guid("8c95c4d2-e50c-4fb0-a4f3-6c06ff0f9a1a");
public override string Name => "MusicBrainz";
public override string Description => "Get artist and album metadata from any MusicBrainz server.";
public const string DefaultServer = "https://musicbrainz.org";
public const long DefaultRateLimit = 2000u;
public Plugin(IApplicationPaths applicationPaths, IXmlSerializer xmlSerializer)
: base(applicationPaths, xmlSerializer)
{
Instance = this;
}
public IEnumerable<PluginPageInfo> GetPages()
{
yield return new PluginPageInfo
{
Name = Name,
EmbeddedResourcePath = GetType().Namespace + ".Configuration.config.html"
};
}
}
}