@ -46,6 +46,7 @@ namespace MediaBrowser.Providers.Music
private readonly string _musicBrainzBaseUrl ;
private readonly string _musicBrainzBaseUrl ;
private SemaphoreSlim _apiRequestLock = new SemaphoreSlim ( 1 , 1 ) ;
private Stopwatch _stopWatchMusicBrainz = new Stopwatch ( ) ;
private Stopwatch _stopWatchMusicBrainz = new Stopwatch ( ) ;
public MusicBrainzAlbumProvider (
public MusicBrainzAlbumProvider (
@ -742,48 +743,58 @@ namespace MediaBrowser.Providers.Music
/// </summary>
/// </summary>
internal async Task < HttpResponseMessage > GetMusicBrainzResponse ( string url , CancellationToken cancellationToken )
internal async Task < HttpResponseMessage > GetMusicBrainzResponse ( string url , CancellationToken cancellationToken )
{
{
using var options = new HttpRequestMessage ( HttpMethod . Get , _musicBrainzBaseUrl . TrimEnd ( '/' ) + url ) ;
await _apiRequestLock . WaitAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
// MusicBrainz request a contact email address is supplied, as comment, in user agent field:
try
// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent
options . Headers . UserAgent . ParseAdd ( string . Format (
CultureInfo . InvariantCulture ,
"{0} ( {1} )" ,
_appHost . ApplicationUserAgent ,
_appHost . ApplicationUserAgentAddress ) ) ;
HttpResponseMessage response ;
var attempts = 0 u ;
do
{
{
attempts + + ;
HttpResponseMessage response ;
var attempts = 0 u ;
var requestUrl = _musicBrainzBaseUrl . TrimEnd ( '/' ) + url ;
if ( _stopWatchMusicBrainz . ElapsedMilliseconds < _musicBrainzQueryIntervalMs )
do
{
{
// MusicBrainz is extremely adamant about limiting to one request per second
attempts + + ;
var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz . ElapsedMilliseconds ;
await Task . Delay ( ( int ) delayMs , cancellationToken ) . ConfigureAwait ( false ) ;
}
// Write time since last request to debug log as evidence we're meeting rate limit
if ( _stopWatchMusicBrainz . ElapsedMilliseconds < _musicBrainzQueryIntervalMs )
// requirement, before resetting stopwatch back to zero.
{
_logger . LogDebug ( "GetMusicBrainzResponse: Time since previous request: {0} ms" , _stopWatchMusicBrainz . ElapsedMilliseconds ) ;
// MusicBrainz is extremely adamant about limiting to one request per second.
_stopWatchMusicBrainz . Restart ( ) ;
var delayMs = _musicBrainzQueryIntervalMs - _stopWatchMusicBrainz . ElapsedMilliseconds ;
await Task . Delay ( ( int ) delayMs , cancellationToken ) . ConfigureAwait ( false ) ;
}
response = await _httpClientFactory . CreateClient ( NamedClient . Default ) . SendAsync ( options ) . ConfigureAwait ( false ) ;
// Write time since last request to debug log as evidence we're meeting rate limit
// requirement, before resetting stopwatch back to zero.
_logger . LogDebug ( "GetMusicBrainzResponse: Time since previous request: {0} ms" , _stopWatchMusicBrainz . ElapsedMilliseconds ) ;
_stopWatchMusicBrainz . Restart ( ) ;
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling)
using var request = new HttpRequestMessage ( HttpMethod . Get , requestUrl ) ;
}
while ( attempts < MusicBrainzQueryAttempts & & response . StatusCode = = HttpStatusCode . ServiceUnavailable ) ;
// MusicBrainz request a contact email address is supplied, as comment, in user agent field:
// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting#User-Agent .
request . Headers . UserAgent . ParseAdd ( string . Format (
CultureInfo . InvariantCulture ,
"{0} ( {1} )" ,
_appHost . ApplicationUserAgent ,
_appHost . ApplicationUserAgentAddress ) ) ;
// Log error if unable to query MB database due to throttling
response = await _httpClientFactory . CreateClient ( NamedClient . Default ) . SendAsync ( request ) . ConfigureAwait ( false ) ;
if ( attempts = = MusicBrainzQueryAttempts & & response . StatusCode = = HttpStatusCode . ServiceUnavailable )
// We retry a finite number of times, and only whilst MB is indicating 503 (throttling).
}
while ( attempts < MusicBrainzQueryAttempts & & response . StatusCode = = HttpStatusCode . ServiceUnavailable ) ;
// Log error if unable to query MB database due to throttling.
if ( attempts = = MusicBrainzQueryAttempts & & response . StatusCode = = HttpStatusCode . ServiceUnavailable )
{
_logger . LogError ( "GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}" , attempts , requestUrl ) ;
}
return response ;
}
finally
{
{
_logger . LogError ( "GetMusicBrainzResponse: 503 Service Unavailable (throttled) response received {0} times whilst requesting {1}" , attempts , options . RequestUri ) ;
_ apiRequestLock. Release ( ) ;
}
}
return response ;
}
}
/// <inheritdoc />
/// <inheritdoc />