@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Music
private readonly IApplicationHost _appHost ;
private readonly ILogger _logger ;
public static string MusicBrainzBaseUrl = "http ://musicbrainz.fercasas.com:5000 ";
public static string MusicBrainzBaseUrl = "http s://www.musicbrainz.org ";
public MusicBrainzAlbumProvider ( IHttpClient httpClient , IApplicationHost appHost , ILogger logger )
{
@ -44,7 +44,7 @@ namespace MediaBrowser.Providers.Music
if ( ! string . IsNullOrEmpty ( releaseId ) )
{
url = string . Format ( MusicBrainzBaseUrl + "/ws/2/release/?query=reid:{0}" , releaseId ) ;
url = string . Format ( "/ws/2/release/?query=reid:{0}" , releaseId ) ;
}
else
{
@ -52,7 +52,7 @@ namespace MediaBrowser.Providers.Music
if ( ! string . IsNullOrWhiteSpace ( artistMusicBrainzId ) )
{
url = string . Format ( MusicBrainzBaseUrl + "/ws/2/release/?query=\"{0}\" AND arid:{1}" ,
url = string . Format ( "/ws/2/release/?query=\"{0}\" AND arid:{1}" ,
WebUtility . UrlEncode ( searchInfo . Name ) ,
artistMusicBrainzId ) ;
}
@ -60,7 +60,7 @@ namespace MediaBrowser.Providers.Music
{
isNameSearch = true ;
url = string . Format ( MusicBrainzBaseUrl + "/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"" ,
url = string . Format ( "/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"" ,
WebUtility . UrlEncode ( searchInfo . Name ) ,
WebUtility . UrlEncode ( searchInfo . GetAlbumArtist ( ) ) ) ;
}
@ -199,57 +199,86 @@ namespace MediaBrowser.Providers.Music
private async Task < ReleaseResult > GetReleaseResult ( string albumName , string artistId , CancellationToken cancellationToken )
{
var url = string . Format ( MusicBrainzBaseUrl + "/ws/2/release/?query=\"{0}\" AND arid:{1}" ,
var url = string . Format ( "/ws/2/release/?query=\"{0}\" AND arid:{1}" ,
WebUtility . UrlEncode ( albumName ) ,
artistId ) ;
var doc = await GetMusicBrainzResponse ( url , true , cancellationToken ) . ConfigureAwait ( false ) ;
return Get ReleaseResult( doc ) ;
return ReleaseResult. Parse ( doc ) ;
}
private async Task < ReleaseResult > GetReleaseResultByArtistName ( string albumName , string artistName , CancellationToken cancellationToken )
{
var url = string . Format ( MusicBrainzBaseUrl + "/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"" ,
var url = string . Format ( "/ws/2/release/?query=\"{0}\" AND artist:\"{1}\"" ,
WebUtility . UrlEncode ( albumName ) ,
WebUtility . UrlEncode ( artistName ) ) ;
var doc = await GetMusicBrainzResponse ( url , true , cancellationToken ) . ConfigureAwait ( false ) ;
return Get ReleaseResult( doc ) ;
return ReleaseResult. Parse ( doc ) ;
}
private ReleaseResult GetReleaseResult ( XmlDocument doc )
private class ReleaseResult
{
var ns = new XmlNamespaceManager ( doc . NameTable ) ;
ns . AddNamespace ( "mb" , MusicBrainzBaseUrl + "/ns/mmd-2.0#" ) ;
public string ReleaseId ;
public string ReleaseGroupId ;
var result = new ReleaseResult
public static ReleaseResult Parse ( XmlDocument doc )
{
var docElem = doc . DocumentElement ;
} ;
if ( docElem = = null )
{
return new ReleaseResult ( ) ;
}
var releaseIdNode = doc . SelectSingleNode ( "//mb:release-list/mb:release/@id" , ns ) ;
var releaseList = docElem . FirstChild ;
if ( releaseList = = null )
{
return new ReleaseResult ( ) ;
}
if ( releaseIdNode ! = null )
{
result . ReleaseId = releaseIdNode . Value ;
}
var nodes = releaseList . ChildNodes ;
string releaseId = null ;
string releaseGroupId = null ;
var releaseGroupIdNode = doc . SelectSingleNode ( "//mb:release-list/mb:release/mb:release-group/@id" , ns ) ;
if ( nodes ! = null )
{
foreach ( var node in nodes . Cast < XmlNode > ( ) )
{
if ( string . Equals ( node . Name , "release" , StringComparison . OrdinalIgnoreCase ) )
{
releaseId = node . Attributes [ "id" ] . Value ;
releaseGroupId = GetReleaseGroupIdFromReleaseNode ( node ) ;
break ;
}
}
}
if ( releaseGroupIdNode ! = null )
{
result . ReleaseGroupId = releaseGroupIdNode . Value ;
return new ReleaseResult
{
ReleaseId = releaseId ,
ReleaseGroupId = releaseGroupId
} ;
}
return result ;
}
private static string GetReleaseGroupIdFromReleaseNode ( XmlNode node )
{
var subNodes = node . ChildNodes ;
if ( subNodes ! = null )
{
foreach ( var subNode in subNodes . Cast < XmlNode > ( ) )
{
if ( string . Equals ( subNode . Name , "release-group" , StringComparison . OrdinalIgnoreCase ) )
{
return subNode . Attributes [ "id" ] . Value ;
}
}
}
private class ReleaseResult
{
public string ReleaseId ;
public string ReleaseGroupId ;
return null ;
}
}
/// <summary>
@ -260,15 +289,37 @@ namespace MediaBrowser.Providers.Music
/// <returns>Task{System.String}.</returns>
private async Task < string > GetReleaseGroupId ( string releaseEntryId , CancellationToken cancellationToken )
{
var url = string . Format ( MusicBrainzBaseUrl + "/ws/2/release-group/?query=reid:{0}" , releaseEntryId ) ;
var url = string . Format ( "/ws/2/release-group/?query=reid:{0}" , releaseEntryId ) ;
var doc = await GetMusicBrainzResponse ( url , false , cancellationToken ) . ConfigureAwait ( false ) ;
var ns = new XmlNamespaceManager ( doc . NameTable ) ;
ns . AddNamespace ( "mb" , MusicBrainzBaseUrl + "/ns/mmd-2.0#" ) ;
var node = doc . SelectSingleNode ( "//mb:release-group-list/mb:release-group/@id" , ns ) ;
var docElem = doc . DocumentElement ;
if ( docElem = = null )
{
return null ;
}
return node ! = null ? node . Value : null ;
var releaseList = docElem . FirstChild ;
if ( releaseList = = null )
{
return null ;
}
var nodes = releaseList . ChildNodes ;
string releaseGroupId = null ;
if ( nodes ! = null )
{
foreach ( var node in nodes . Cast < XmlNode > ( ) )
{
if ( string . Equals ( node . Name , "release-group" , StringComparison . OrdinalIgnoreCase ) )
{
return node . Attributes [ "id" ] . Value ;
}
}
}
return null ;
}
/// <summary>
@ -276,6 +327,49 @@ namespace MediaBrowser.Providers.Music
/// </summary>
private readonly SemaphoreSlim _musicBrainzResourcePool = new SemaphoreSlim ( 1 , 1 ) ;
private long _lastMbzUrlQueryTicks = 0 ;
private List < MbzUrl > _mbzUrls = null ;
private MbzUrl _chosenUrl ;
private async Task < MbzUrl > GetMbzUrl ( )
{
if ( _mbzUrls = = null | | ( DateTime . UtcNow . Ticks - _lastMbzUrlQueryTicks ) > TimeSpan . FromHours ( 12 ) . Ticks )
{
await RefreshMzbUrls ( ) . ConfigureAwait ( false ) ;
var urls = _mbzUrls . ToList ( ) ;
_chosenUrl = urls [ new Random ( ) . Next ( 0 , urls . Count - 1 ) ] ;
}
return _chosenUrl ;
}
private async Task RefreshMzbUrls ( )
{
try
{
_mbzUrls = new List < MbzUrl >
{
new MbzUrl
{
url = MusicBrainzBaseUrl ,
throttleMs = 1000
}
} ;
}
catch
{
_mbzUrls = new List < MbzUrl >
{
new MbzUrl
{
url = MusicBrainzBaseUrl ,
throttleMs = 1000
}
} ;
}
}
/// <summary>
/// Gets the music brainz response.
/// </summary>
@ -285,9 +379,15 @@ namespace MediaBrowser.Providers.Music
/// <returns>Task{XmlDocument}.</returns>
internal async Task < XmlDocument > GetMusicBrainzResponse ( string url , bool isSearch , CancellationToken cancellationToken )
{
// MusicBrainz is extremely adamant about limiting to one request per second
var urlInfo = await GetMbzUrl ( ) . ConfigureAwait ( false ) ;
await Task . Delay ( 1000 , cancellationToken ) . ConfigureAwait ( false ) ;
if ( urlInfo . throttleMs > 0 )
{
// MusicBrainz is extremely adamant about limiting to one request per second
await Task . Delay ( urlInfo . throttleMs , cancellationToken ) . ConfigureAwait ( false ) ;
}
url = urlInfo . url . TrimEnd ( '/' ) + url ;
var doc = new XmlDocument ( ) ;
@ -319,5 +419,11 @@ namespace MediaBrowser.Providers.Music
{
throw new NotImplementedException ( ) ;
}
internal class MbzUrl
{
public string url { get ; set ; }
public int throttleMs { get ; set ; }
}
}
}