diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index eaadb73e36..5fa3a62966 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -90,6 +90,7 @@ + diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs index d6c7f1dfd7..b3ab4c3672 100644 --- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs @@ -7,12 +7,13 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Providers; using System; +using System.Collections.Generic; using System.IO; -using System.Text; +using System.Linq; using System.Threading; using System.Threading.Tasks; -using System.Xml; namespace MediaBrowser.Providers.Music { @@ -26,18 +27,12 @@ namespace MediaBrowser.Providers.Music /// private readonly IProviderManager _providerManager; - /// - /// The _music brainz resource pool - /// - private readonly SemaphoreSlim _musicBrainzResourcePool = new SemaphoreSlim(1, 1); - /// /// Gets the HTTP client. /// /// The HTTP client. protected IHttpClient HttpClient { get; private set; } - internal static FanArtAlbumProvider Current { get; private set; } private readonly IFileSystem _fileSystem; /// @@ -53,8 +48,6 @@ namespace MediaBrowser.Providers.Music _providerManager = providerManager; _fileSystem = fileSystem; HttpClient = httpClient; - - Current = this; } /// @@ -116,13 +109,13 @@ namespace MediaBrowser.Providers.Music /// true if XXXX, false otherwise protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) { - if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz))) + if (!ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && + !ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary) { return false; } - if (!ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && - !ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary) + if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Disc)) { return false; } @@ -159,163 +152,47 @@ namespace MediaBrowser.Providers.Music /// Task{System.Boolean}. public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); - - var artistMusicBrainzId = item.Parent.GetProviderId(MetadataProviders.Musicbrainz); - - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - if (!string.IsNullOrEmpty(artistMusicBrainzId)) - { - var artistXmlPath = FanArtArtistProvider.GetArtistDataPath(ConfigurationManager.CommonApplicationPaths, artistMusicBrainzId); - artistXmlPath = Path.Combine(artistXmlPath, "fanart.xml"); - - var artistXmlFileInfo = new FileInfo(artistXmlPath); - - if (artistXmlFileInfo.Exists) - { - var album = (MusicAlbum)item; - - var releaseEntryId = item.GetProviderId(MetadataProviders.Musicbrainz); - - var musicBrainzReleaseGroupId = album.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); - // Fanart uses the release group id so we'll have to get that now using the release entry id - if (string.IsNullOrEmpty(musicBrainzReleaseGroupId)) - { - musicBrainzReleaseGroupId = await GetReleaseGroupId(releaseEntryId, cancellationToken).ConfigureAwait(false); - - album.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, musicBrainzReleaseGroupId); - } - - var doc = new XmlDocument(); - - doc.Load(artistXmlPath); - - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && !item.HasImage(ImageType.Disc)) - { - // Try try with the release entry Id, if that doesn't produce anything try the release group id - var node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + releaseEntryId + "\"]/cdart/@url"); - - if (node == null && !string.IsNullOrEmpty(musicBrainzReleaseGroupId)) - { - node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + musicBrainzReleaseGroupId + "\"]/cdart/@url"); - } - - var path = node != null ? node.Value : null; - - if (!string.IsNullOrEmpty(path)) - { - await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Disc, null, cancellationToken) - .ConfigureAwait(false); - } - } - - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary && !item.HasImage(ImageType.Primary)) - { - // Try try with the release entry Id, if that doesn't produce anything try the release group id - var node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + releaseEntryId + "\"]/albumcover/@url"); - - if (node == null && !string.IsNullOrEmpty(musicBrainzReleaseGroupId)) - { - node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + musicBrainzReleaseGroupId + "\"]/albumcover/@url"); - } - - var path = node != null ? node.Value : null; - - if (!string.IsNullOrEmpty(path)) - { - await _providerManager.SaveImage(item, path, FanArtResourcePool, ImageType.Primary, null, cancellationToken) - .ConfigureAwait(false); - } - } - } - - } + var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualFanartAlbumProvider.ProviderName).ConfigureAwait(false); + await FetchFromXml(item, images.ToList(), cancellationToken).ConfigureAwait(false); + SetLastRefreshed(item, DateTime.UtcNow); return true; } /// - /// The _last music brainz request + /// Fetches from XML. /// - private DateTime _lastRequestDate = DateTime.MinValue; - - /// - /// Gets the music brainz response. - /// - /// The URL. + /// The item. + /// The images. /// The cancellation token. - /// Task{XmlDocument}. - internal async Task GetMusicBrainzResponse(string url, CancellationToken cancellationToken) + /// Task. + private async Task FetchFromXml(BaseItem item, List images, CancellationToken cancellationToken) { - await _musicBrainzResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); - try + if (ConfigurationManager.Configuration.DownloadSeriesImages.Primary && !item.HasImage(ImageType.Primary)) { - var diff = 1500 - (DateTime.Now - _lastRequestDate).TotalMilliseconds; + var image = images.FirstOrDefault(i => i.Type == ImageType.Primary); - // MusicBrainz is extremely adamant about limiting to one request per second - - if (diff > 0) + if (image != null) { - await Task.Delay(Convert.ToInt32(diff), cancellationToken).ConfigureAwait(false); + await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); } - - _lastRequestDate = DateTime.Now; - - var doc = new XmlDocument(); - - using (var xml = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - UserAgent = Environment.MachineName - - }).ConfigureAwait(false)) - { - using (var oReader = new StreamReader(xml, Encoding.UTF8)) - { - doc.Load(oReader); - } - } - - return doc; } - finally + + cancellationToken.ThrowIfCancellationRequested(); + + if (ConfigurationManager.Configuration.DownloadSeriesImages.Disc && !item.HasImage(ImageType.Disc)) { - _lastRequestDate = DateTime.Now; + var image = images.FirstOrDefault(i => i.Type == ImageType.Disc); - _musicBrainzResourcePool.Release(); + if (image != null) + { + await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Disc, null, cancellationToken).ConfigureAwait(false); + } } } - - /// - /// Gets the release group id internal. - /// - /// The release entry id. - /// The cancellation token. - /// Task{System.String}. - private async Task GetReleaseGroupId(string releaseEntryId, CancellationToken cancellationToken) - { - var url = string.Format("http://www.musicbrainz.org/ws/2/release-group/?query=reid:{0}", releaseEntryId); - - var doc = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); - - var ns = new XmlNamespaceManager(doc.NameTable); - ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); - var node = doc.SelectSingleNode("//mb:release-group-list/mb:release-group/@id", ns); - - return node != null ? node.Value : null; - } } } diff --git a/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs b/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs index 1365d87641..7192b5949b 100644 --- a/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs @@ -11,24 +11,26 @@ using MoreLinq; using System; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Xml; namespace MediaBrowser.Providers.Music { public class LastfmAlbumProvider : LastfmBaseProvider { - private static readonly Task BlankId = Task.FromResult(""); + internal static LastfmAlbumProvider Current; + + /// + /// The _music brainz resource pool + /// + private readonly SemaphoreSlim _musicBrainzResourcePool = new SemaphoreSlim(1, 1); public LastfmAlbumProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager) : base(jsonSerializer, httpClient, logManager, configurationManager) { - } - - protected override Task FindId(BaseItem item, CancellationToken cancellationToken) - { - // We don't fetch by id - return BlankId; + Current = this; } /// @@ -61,14 +63,16 @@ namespace MediaBrowser.Providers.Music /// true if XXXX, false otherwise protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) { - if (HasAltMeta(item)) + var hasId = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz)) && + !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup)); + + if (hasId && HasAltMeta(item)) { return false; } // If song metadata has changed and we don't have an mbid, refresh - if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz)) && - GetComparisonData(item as MusicAlbum) != providerInfo.FileStamp) + if (!hasId && GetComparisonData(item as MusicAlbum) != providerInfo.FileStamp) { return true; } @@ -76,8 +80,17 @@ namespace MediaBrowser.Providers.Music return base.NeedsRefreshInternal(item, providerInfo); } - protected override async Task FetchLastfmData(BaseItem item, string id, bool force, CancellationToken cancellationToken) + /// + /// Fetches metadata and returns true or false indicating if any work that requires persistence was done + /// + /// The item. + /// if set to true [force]. + /// The cancellation token + /// Task{System.Boolean}. + public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) { + cancellationToken.ThrowIfCancellationRequested(); + var album = (MusicAlbum)item; var result = await GetAlbumResult(album, cancellationToken).ConfigureAwait(false); @@ -87,6 +100,20 @@ namespace MediaBrowser.Providers.Music LastfmHelper.ProcessAlbumData(item, result.album); } + var releaseEntryId = item.GetProviderId(MetadataProviders.Musicbrainz); + + if (!string.IsNullOrEmpty(releaseEntryId)) + { + var musicBrainzReleaseGroupId = album.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + if (string.IsNullOrEmpty(musicBrainzReleaseGroupId)) + { + musicBrainzReleaseGroupId = await GetReleaseGroupId(releaseEntryId, cancellationToken).ConfigureAwait(false); + + album.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, musicBrainzReleaseGroupId); + } + } + BaseProviderInfo data; if (!item.ProviderData.TryGetValue(Id, out data)) { @@ -95,8 +122,12 @@ namespace MediaBrowser.Providers.Music } data.FileStamp = GetComparisonData(album); + + SetLastRefreshed(item, DateTime.UtcNow); + return true; } + private async Task GetAlbumResult(MusicAlbum item, CancellationToken cancellationToken) { // Try album release Id @@ -181,11 +212,6 @@ namespace MediaBrowser.Providers.Music return JsonSerializer.DeserializeFromStream(json); } } - - protected override Task FetchData(BaseItem item, bool force, CancellationToken cancellationToken) - { - return FetchLastfmData(item, string.Empty, force, cancellationToken); - } public override bool Supports(BaseItem item) { @@ -215,5 +241,77 @@ namespace MediaBrowser.Providers.Music return string.Join(string.Empty, albumArtists.OrderBy(i => i).ToArray()).GetMD5(); } + + /// + /// Gets the release group id internal. + /// + /// The release entry id. + /// The cancellation token. + /// Task{System.String}. + private async Task GetReleaseGroupId(string releaseEntryId, CancellationToken cancellationToken) + { + var url = string.Format("http://www.musicbrainz.org/ws/2/release-group/?query=reid:{0}", releaseEntryId); + + var doc = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + + var ns = new XmlNamespaceManager(doc.NameTable); + ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); + var node = doc.SelectSingleNode("//mb:release-group-list/mb:release-group/@id", ns); + + return node != null ? node.Value : null; + } + /// + /// The _last music brainz request + /// + private DateTime _lastRequestDate = DateTime.MinValue; + + /// + /// Gets the music brainz response. + /// + /// The URL. + /// The cancellation token. + /// Task{XmlDocument}. + internal async Task GetMusicBrainzResponse(string url, CancellationToken cancellationToken) + { + await _musicBrainzResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + var diff = 1500 - (DateTime.Now - _lastRequestDate).TotalMilliseconds; + + // MusicBrainz is extremely adamant about limiting to one request per second + + if (diff > 0) + { + await Task.Delay(Convert.ToInt32(diff), cancellationToken).ConfigureAwait(false); + } + + _lastRequestDate = DateTime.Now; + + var doc = new XmlDocument(); + + using (var xml = await HttpClient.Get(new HttpRequestOptions + { + Url = url, + CancellationToken = cancellationToken, + UserAgent = Environment.MachineName + + }).ConfigureAwait(false)) + { + using (var oReader = new StreamReader(xml, Encoding.UTF8)) + { + doc.Load(oReader); + } + } + + return doc; + } + finally + { + _lastRequestDate = DateTime.Now; + + _musicBrainzResourcePool.Release(); + } + } } } diff --git a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs index 52493fd240..7d83c3f6f2 100644 --- a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs @@ -82,7 +82,7 @@ namespace MediaBrowser.Providers.Music /// The item. /// The cancellation token. /// Task{System.String}. - protected override async Task FindId(BaseItem item, CancellationToken cancellationToken) + private async Task FindId(BaseItem item, CancellationToken cancellationToken) { if (item is Artist) { @@ -112,6 +112,32 @@ namespace MediaBrowser.Providers.Music } } + /// + /// Fetches metadata and returns true or false indicating if any work that requires persistence was done + /// + /// The item. + /// if set to true [force]. + /// The cancellation token + /// Task{System.Boolean}. + public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var id = item.GetProviderId(MetadataProviders.Musicbrainz) ?? await FindId(item, cancellationToken).ConfigureAwait(false); + + if (!string.IsNullOrWhiteSpace(id)) + { + cancellationToken.ThrowIfCancellationRequested(); + + item.SetProviderId(MetadataProviders.Musicbrainz, id); + + await FetchLastfmData(item, id, force, cancellationToken).ConfigureAwait(false); + } + + SetLastRefreshed(item, DateTime.UtcNow); + return true; + } + /// /// Finds the id from music artist entity. /// @@ -138,7 +164,7 @@ namespace MediaBrowser.Providers.Music var url = string.Format("http://www.musicbrainz.org/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch)); - var doc = await FanArtAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + var doc = await LastfmAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); var ns = new XmlNamespaceManager(doc.NameTable); ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); @@ -154,7 +180,7 @@ namespace MediaBrowser.Providers.Music // Try again using the search with accent characters url url = string.Format("http://www.musicbrainz.org/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); - doc = await FanArtAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); + doc = await LastfmAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); ns = new XmlNamespaceManager(doc.NameTable); ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); @@ -200,7 +226,7 @@ namespace MediaBrowser.Providers.Music /// The music brainz id. /// The cancellation token. /// Task. - protected override async Task FetchLastfmData(BaseItem item, string musicBrainzId, bool force, CancellationToken cancellationToken) + protected virtual async Task FetchLastfmData(BaseItem item, string musicBrainzId, bool force, CancellationToken cancellationToken) { // Get artist info with provided id var url = RootUrl + string.Format("method=artist.getInfo&mbid={0}&api_key={1}&format=json", UrlEncode(musicBrainzId), ApiKey); diff --git a/MediaBrowser.Providers/Music/LastfmBaseProvider.cs b/MediaBrowser.Providers/Music/LastfmBaseProvider.cs index 0efe65213e..26796f7e44 100644 --- a/MediaBrowser.Providers/Music/LastfmBaseProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmBaseProvider.cs @@ -1,15 +1,12 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using System; using System.Collections.Generic; using System.Net; using System.Threading; -using System.Threading.Tasks; namespace MediaBrowser.Providers.Music { @@ -94,36 +91,6 @@ namespace MediaBrowser.Providers.Music protected const string RootUrl = @"http://ws.audioscrobbler.com/2.0/?"; protected static string ApiKey = "7b76553c3eb1d341d642755aecc40a33"; - /// - /// Fetches the items data. - /// - /// The item. - /// - /// Task. - protected virtual async Task FetchData(BaseItem item, bool force, CancellationToken cancellationToken) - { - var id = item.GetProviderId(MetadataProviders.Musicbrainz) ?? await FindId(item, cancellationToken).ConfigureAwait(false); - if (!string.IsNullOrWhiteSpace(id)) - { - Logger.Debug("LastfmProvider - getting info for {0}", item.Name); - - cancellationToken.ThrowIfCancellationRequested(); - - item.SetProviderId(MetadataProviders.Musicbrainz, id); - - await FetchLastfmData(item, id, force, cancellationToken).ConfigureAwait(false); - } - else - { - Logger.Info("LastfmProvider could not find " + item.Name + ". Check name on Last.fm."); - } - - } - - protected abstract Task FindId(BaseItem item, CancellationToken cancellationToken); - - protected abstract Task FetchLastfmData(BaseItem item, string id, bool force, CancellationToken cancellationToken); - /// /// Encodes an URL. /// @@ -133,22 +100,6 @@ namespace MediaBrowser.Providers.Music { return WebUtility.UrlEncode(name); } - - /// - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// - /// The item. - /// if set to true [force]. - /// The cancellation token - /// Task{System.Boolean}. - public override async Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - await FetchData(item, force, cancellationToken).ConfigureAwait(false); - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } } #region Result Objects diff --git a/MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs b/MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs new file mode 100644 index 0000000000..a7f4428ffe --- /dev/null +++ b/MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs @@ -0,0 +1,317 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace MediaBrowser.Providers.Music +{ + public class ManualFanartAlbumProvider : IImageProvider + { + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly IServerConfigurationManager _config; + + public ManualFanartAlbumProvider(IServerConfigurationManager config) + { + _config = config; + } + + public string Name + { + get { return ProviderName; } + } + + public static string ProviderName + { + get { return "FanArt"; } + } + + public bool Supports(BaseItem item) + { + return item is MusicAlbum; + } + + public async Task> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken) + { + var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); + + return images.Where(i => i.Type == imageType); + } + + public Task> GetAllImages(BaseItem item, CancellationToken cancellationToken) + { + var list = new List(); + + var artistMusicBrainzId = item.Parent.GetProviderId(MetadataProviders.Musicbrainz); + + if (!string.IsNullOrEmpty(artistMusicBrainzId)) + { + var artistXmlPath = FanArtArtistProvider.GetArtistDataPath(_config.CommonApplicationPaths, artistMusicBrainzId); + artistXmlPath = Path.Combine(artistXmlPath, "fanart.xml"); + + var musicBrainzReleaseGroupId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + var musicBrainzId = item.GetProviderId(MetadataProviders.Musicbrainz); + + try + { + AddImages(list, artistXmlPath, musicBrainzId, musicBrainzReleaseGroupId, cancellationToken); + } + catch (FileNotFoundException) + { + + } + } + + var language = _config.Configuration.PreferredMetadataLanguage; + + var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); + + // Sort first by width to prioritize HD versions + list = list.OrderByDescending(i => i.Width ?? 0) + .ThenByDescending(i => + { + if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + if (!isLanguageEn) + { + if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } + } + if (string.IsNullOrEmpty(i.Language)) + { + return isLanguageEn ? 3 : 2; + } + return 0; + }) + .ThenByDescending(i => i.CommunityRating ?? 0) + .ThenByDescending(i => i.VoteCount ?? 0) + .ToList(); + + return Task.FromResult>(list); + } + + /// + /// Adds the images. + /// + /// The list. + /// The XML path. + /// The release identifier. + /// The release group identifier. + /// The cancellation token. + private void AddImages(List list, string xmlPath, string releaseId, string releaseGroupId, CancellationToken cancellationToken) + { + using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings + { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + })) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "music": + { + using (var subReader = reader.ReadSubtree()) + { + AddImagesFromMusicNode(list, releaseId, releaseGroupId, subReader, cancellationToken); + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + } + } + + /// + /// Adds the images from music node. + /// + /// The list. + /// The release identifier. + /// The release group identifier. + /// The reader. + /// The cancellation token. + private void AddImagesFromMusicNode(List list, string releaseId, string releaseGroupId, XmlReader reader, CancellationToken cancellationToken) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "albums": + { + using (var subReader = reader.ReadSubtree()) + { + AddImagesFromAlbumsNode(list, releaseId, releaseGroupId, subReader, cancellationToken); + } + break; + } + default: + { + using (reader.ReadSubtree()) + { + } + break; + } + } + } + } + } + + /// + /// Adds the images from albums node. + /// + /// The list. + /// The release identifier. + /// The release group identifier. + /// The reader. + /// The cancellation token. + private void AddImagesFromAlbumsNode(List list, string releaseId, string releaseGroupId, XmlReader reader, CancellationToken cancellationToken) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "album": + { + var id = reader.GetAttribute("id"); + + using (var subReader = reader.ReadSubtree()) + { + if (string.Equals(id, releaseId, StringComparison.OrdinalIgnoreCase) || + string.Equals(id, releaseGroupId, StringComparison.OrdinalIgnoreCase)) + { + AddImages(list, subReader, cancellationToken); + } + } + break; + } + default: + { + using (reader.ReadSubtree()) + { + } + break; + } + } + } + } + } + + private void AddImages(List list, XmlReader reader, CancellationToken cancellationToken) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "cdart": + { + AddImage(list, reader, ImageType.Disc, 1000, 1000); + break; + } + case "albumcover": + { + AddImage(list, reader, ImageType.Primary, 1000, 1000); + break; + } + default: + { + using (reader.ReadSubtree()) + { + } + break; + } + } + } + } + } + + private void AddImage(List list, XmlReader reader, ImageType type, int width, int height) + { + var url = reader.GetAttribute("url"); + + var size = reader.GetAttribute("size"); + + if (!string.IsNullOrEmpty(size)) + { + int sizeNum; + if (int.TryParse(size, NumberStyles.Any, _usCulture, out sizeNum)) + { + width = sizeNum; + height = sizeNum; + } + } + + var likesString = reader.GetAttribute("likes"); + int likes; + + var info = new RemoteImageInfo + { + RatingType = RatingType.Likes, + Type = type, + Width = width, + Height = height, + ProviderName = Name, + Url = url, + Language = reader.GetAttribute("lang") + }; + + if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes)) + { + info.CommunityRating = likes; + } + + list.Add(info); + } + + public int Priority + { + get { return 0; } + } + } +}