diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index f86cdeab9f..8a3860d74e 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -28,6 +29,7 @@ namespace MediaBrowser.Providers.Music private readonly IApplicationHost _appHost; private readonly ILogger _logger; private readonly IJsonSerializer _json; + private Stopwatch _stopWatchMusicBrainz = new Stopwatch(); public readonly string MusicBrainzBaseUrl; @@ -45,6 +47,9 @@ namespace MediaBrowser.Providers.Music MusicBrainzBaseUrl = configuration["MusicBrainz:BaseUrl"]; + // Use a stopwatch to ensure we don't exceed the MusicBrainz rate limit + _stopWatchMusicBrainz.Start(); + Current = this; } @@ -54,8 +59,6 @@ namespace MediaBrowser.Providers.Music var releaseGroupId = searchInfo.GetReleaseGroupId(); string url; - var isNameSearch = false; - bool forceMusicBrainzProper = false; if (!string.IsNullOrEmpty(releaseId)) { @@ -64,7 +67,6 @@ namespace MediaBrowser.Providers.Music else if (!string.IsNullOrEmpty(releaseGroupId)) { url = string.Format("/ws/2/release?release-group={0}", releaseGroupId); - forceMusicBrainzProper = true; } else { @@ -78,8 +80,6 @@ namespace MediaBrowser.Providers.Music } else { - isNameSearch = true; - // I'm sure there is a better way but for now it resolves search for 12" Mixes var queryName = searchInfo.Name.Replace("\"", string.Empty); @@ -91,7 +91,7 @@ namespace MediaBrowser.Providers.Music if (!string.IsNullOrWhiteSpace(url)) { - using (var response = await GetMusicBrainzResponse(url, isNameSearch, forceMusicBrainzProper, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) { using (var stream = response.Content) { @@ -247,7 +247,7 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), artistId); - using (var response = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) using (var stream = response.Content) using (var oReader = new StreamReader(stream, Encoding.UTF8)) { @@ -272,7 +272,7 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), WebUtility.UrlEncode(artistName)); - using (var response = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) using (var stream = response.Content) using (var oReader = new StreamReader(stream, Encoding.UTF8)) { @@ -589,7 +589,7 @@ namespace MediaBrowser.Providers.Music { var url = string.Format("/ws/2/release?release-group={0}", releaseGroupId); - using (var response = await GetMusicBrainzResponse(url, true, true, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) using (var stream = response.Content) using (var oReader = new StreamReader(stream, Encoding.UTF8)) { @@ -625,7 +625,7 @@ namespace MediaBrowser.Providers.Music { var url = string.Format("/ws/2/release-group/?query=reid:{0}", releaseEntryId); - using (var response = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false)) + using (var response = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) using (var stream = response.Content) using (var oReader = new StreamReader(stream, Encoding.UTF8)) { @@ -710,34 +710,32 @@ namespace MediaBrowser.Providers.Music return null; } - internal Task GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken) - { - return GetMusicBrainzResponse(url, isSearch, false, cancellationToken); - } - /// - /// Gets the music brainz response. + /// Makes request to MusicBrainz server and awaits a response. /// - internal async Task GetMusicBrainzResponse(string url, bool isSearch, bool forceMusicBrainzProper, CancellationToken cancellationToken) + internal async Task GetMusicBrainzResponse(string url, CancellationToken cancellationToken) { - var urlInfo = new MbzUrl(MusicBrainzBaseUrl, 1000); - var throttleMs = urlInfo.throttleMs; + // The Jellyfin user-agent is unrestricted but source IP must not exceed + // one request per second, therefore we rate limit to avoid throttling + // https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting + const long QueryIntervalMs = 1000u; - if (throttleMs > 0) + // Only delay if necessary + if (_stopWatchMusicBrainz.ElapsedMilliseconds < QueryIntervalMs) { - // MusicBrainz is extremely adamant about limiting to one request per second - _logger.LogDebug("Throttling MusicBrainz by {0}ms", throttleMs.ToString(CultureInfo.InvariantCulture)); - await Task.Delay(throttleMs, cancellationToken).ConfigureAwait(false); + var delayMs = QueryIntervalMs - _stopWatchMusicBrainz.ElapsedMilliseconds; + await Task.Delay((int)delayMs, cancellationToken).ConfigureAwait(false); } - url = urlInfo.url.TrimEnd('/') + url; + _logger.LogDebug("MusicBrainz time since previous request: {0}ms", _stopWatchMusicBrainz.ElapsedMilliseconds); + _stopWatchMusicBrainz.Restart(); var options = new HttpRequestOptions { - Url = url, + Url = MusicBrainzBaseUrl.TrimEnd('/') + url, CancellationToken = cancellationToken, UserAgent = _appHost.ApplicationUserAgent, - BufferContent = throttleMs > 0 + BufferContent = false }; return await _httpClient.SendAsync(options, "GET").ConfigureAwait(false); @@ -749,17 +747,5 @@ namespace MediaBrowser.Providers.Music { throw new NotImplementedException(); } - - internal class MbzUrl - { - internal MbzUrl(string url, int throttleMs) - { - this.url = url; - this.throttleMs = throttleMs; - } - - public string url { get; set; } - public int throttleMs { get; set; } - } } } diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs index 55aab7778a..59280df897 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs @@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.Music { var url = string.Format("/ws/2/artist/?query=arid:{0}", musicBrainzId); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false)) + using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) { using (var stream = response.Content) { @@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.Music var url = string.Format("/ws/2/artist/?query=\"{0}\"&dismax=true", UrlEncode(nameToSearch)); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) { using (var stream = response.Content) { @@ -64,7 +64,7 @@ namespace MediaBrowser.Providers.Music // Try again using the search with accent characters url url = string.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); - using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + using (var response = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false)) { using (var stream = response.Content) {