using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; namespace MediaBrowser.Providers.Music { public class MusicBrainzArtistProvider : IRemoteMetadataProvider { public async Task> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) { var musicBrainzId = searchInfo.GetMusicBrainzArtistId(); if (!string.IsNullOrWhiteSpace(musicBrainzId)) { var url = string.Format("https://www.musicbrainz.org/ws/2/artist/?query=arid:{0}", musicBrainzId); var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken) .ConfigureAwait(false); return GetResultsFromResponse(doc); } else { // They seem to throw bad request failures on any term with a slash var nameToSearch = searchInfo.Name.Replace('/', ' '); var url = String.Format("https://www.musicbrainz.org/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch)); var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false); var results = GetResultsFromResponse(doc).ToList(); if (results.Count > 0) { return results; } if (HasDiacritics(searchInfo.Name)) { // Try again using the search with accent characters url url = String.Format("https://www.musicbrainz.org/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false); return GetResultsFromResponse(doc); } } return new List(); } private IEnumerable GetResultsFromResponse(XmlDocument doc) { var ns = new XmlNamespaceManager(doc.NameTable); ns.AddNamespace("mb", "https://musicbrainz.org/ns/mmd-2.0#"); var list = new List(); var nodes = doc.SelectNodes("//mb:artist-list/mb:artist", ns); if (nodes != null) { foreach (var node in nodes.Cast()) { if (node.Attributes != null) { string name = null; string mbzId = node.Attributes["id"].Value; var nameNode = node.SelectSingleNode("//mb:name", ns); if (nameNode != null) { name = nameNode.InnerText; } if (!string.IsNullOrWhiteSpace(mbzId) && !string.IsNullOrWhiteSpace(name)) { var result = new RemoteSearchResult { Name = name }; result.SetProviderId(MetadataProviders.MusicBrainzArtist, mbzId); list.Add(result); } } } } return list; } public async Task> GetMetadata(ArtistInfo id, CancellationToken cancellationToken) { var result = new MetadataResult { Item = new MusicArtist() }; var musicBrainzId = id.GetMusicBrainzArtistId(); if (string.IsNullOrWhiteSpace(musicBrainzId)) { var searchResults = await GetSearchResults(id, cancellationToken).ConfigureAwait(false); var singleResult = searchResults.FirstOrDefault(); if (singleResult != null) { musicBrainzId = singleResult.GetProviderId(MetadataProviders.MusicBrainzArtist); //result.Item.Name = singleResult.Name; } } if (!string.IsNullOrWhiteSpace(musicBrainzId)) { result.HasMetadata = true; result.Item.SetProviderId(MetadataProviders.MusicBrainzArtist, musicBrainzId); } return result; } /// /// Determines whether the specified text has diacritics. /// /// The text. /// true if the specified text has diacritics; otherwise, false. private bool HasDiacritics(string text) { return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal); } /// /// Removes the diacritics. /// /// The text. /// System.String. private string RemoveDiacritics(string text) { return String.Concat( text.Normalize(NormalizationForm.FormD) .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark) ).Normalize(NormalizationForm.FormC); } /// /// Encodes an URL. /// /// The name. /// System.String. private string UrlEncode(string name) { return WebUtility.UrlEncode(name); } public string Name { get { return "MusicBrainz"; } } public Task GetImageResponse(string url, CancellationToken cancellationToken) { throw new NotImplementedException(); } } }