diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 68936bb1a8..dbe7eba5b9 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -108,6 +108,7 @@ + diff --git a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs index 548fbd5a69..8ea7532f38 100644 --- a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs +++ b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Configuration; +using System.Threading; +using MediaBrowser.Controller.Configuration; +using System.Collections.Generic; using MediaBrowser.Controller.Entities; using System; using MediaBrowser.Model.Logging; @@ -10,27 +12,44 @@ namespace MediaBrowser.Controller.Providers /// public abstract class FanartBaseProvider : BaseMetadataProvider { + + protected static readonly SemaphoreSlim FanArtResourcePool = new SemaphoreSlim(5,5); + /// /// The LOG o_ FILE /// protected const string LOGO_FILE = "logo.png"; + /// /// The AR t_ FILE /// protected const string ART_FILE = "clearart.png"; + /// /// The THUM b_ FILE /// protected const string THUMB_FILE = "thumb.jpg"; + /// /// The DIS c_ FILE /// protected const string DISC_FILE = "disc.png"; + /// /// The BANNE r_ FILE /// protected const string BANNER_FILE = "banner.png"; + /// + /// The Backdrop + /// + protected const string BACKDROP_FILE = "backdrop.jpg"; + + /// + /// The Primary image + /// + protected const string PRIMARY_FILE = "folder.jpg"; + /// /// The API key /// @@ -60,10 +79,7 @@ namespace MediaBrowser.Controller.Providers /// true if [requires internet]; otherwise, false. public override bool RequiresInternet { - get - { - return true; - } + get { return true; } } /// @@ -85,6 +101,32 @@ namespace MediaBrowser.Controller.Providers { return false; } + #region Result Objects + + protected class FanArtImageInfo + { + public string id { get; set; } + public string url { get; set; } + public string likes { get; set; } + } + + protected class FanArtMusicInfo + { + public string mbid_id { get; set; } + public List musiclogo { get; set; } + public List artistbackground { get; set; } + public List artistthumb { get; set; } + public List hdmusiclogo { get; set; } + public List musicbanner { get; set; } + } + + protected class FanArtMusicResult + { + public FanArtMusicInfo result { get; set; } + } + + #endregion } + } diff --git a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs new file mode 100644 index 0000000000..d4585d58ae --- /dev/null +++ b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs @@ -0,0 +1,241 @@ +using System.Collections.Specialized; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace MediaBrowser.Controller.Providers.Music +{ + /// + /// Class FanArtArtistProvider + /// + class FanArtArtistProvider : FanartBaseProvider + { + /// + /// Gets the HTTP client. + /// + /// The HTTP client. + protected IHttpClient HttpClient { get; private set; } + + public FanArtArtistProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) + { + if (httpClient == null) + { + throw new ArgumentNullException("httpClient"); + } + HttpClient = httpClient; + } + + /// + /// The fan art base URL + /// + protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/artist/{0}/{1}/xml/all/1/1"; + + /// + /// Supportses the specified item. + /// + /// The item. + /// true if XXXX, false otherwise + public override bool Supports(BaseItem item) + { + return item is MusicArtist; + } + + /// + /// Shoulds the fetch. + /// + /// The item. + /// The provider info. + /// true if XXXX, false otherwise + protected override bool ShouldFetch(BaseItem item, BaseProviderInfo providerInfo) + { + var baseItem = item; + if (item.Path == null || item.DontFetchMeta || string.IsNullOrEmpty(baseItem.GetProviderId(MetadataProviders.Musicbrainz))) return false; //nothing to do + var artExists = item.ResolveArgs.ContainsMetaFileByName(ART_FILE); + var logoExists = item.ResolveArgs.ContainsMetaFileByName(LOGO_FILE); + var discExists = item.ResolveArgs.ContainsMetaFileByName(DISC_FILE); + + return (!artExists && ConfigurationManager.Configuration.DownloadMovieArt) + || (!logoExists && ConfigurationManager.Configuration.DownloadMovieLogo) + || (!discExists && ConfigurationManager.Configuration.DownloadMovieDisc); + } + + /// + /// 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}. + protected override async Task FetchAsyncInternal(BaseItem item, bool force, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + + var artist = item; + if (ShouldFetch(artist, artist.ProviderData.GetValueOrDefault(Id, new BaseProviderInfo { ProviderId = Id }))) + { + var url = string.Format(FanArtBaseUrl, APIKey, artist.GetProviderId(MetadataProviders.Musicbrainz)); + var doc = new XmlDocument(); + + try + { + using (var xml = await HttpClient.Get(url, FanArtResourcePool, cancellationToken).ConfigureAwait(false)) + { + doc.Load(xml); + } + } + catch (HttpException) + { + } + + cancellationToken.ThrowIfCancellationRequested(); + + if (doc.HasChildNodes) + { + string path; + var hd = ConfigurationManager.Configuration.DownloadHDFanArt ? "hd" : ""; + if (ConfigurationManager.Configuration.DownloadMovieLogo && !item.ResolveArgs.ContainsMetaFileByName(LOGO_FILE)) + { + var node = + doc.SelectSingleNode("//fanart/music/musiclogos/" + hd + "musiclogo/@url") ?? + doc.SelectSingleNode("//fanart/music/musiclogos/musiclogo/@url"); + path = node != null ? node.Value : null; + if (!string.IsNullOrEmpty(path)) + { + Logger.Debug("FanArtProvider getting ClearLogo for " + artist.Name); + try + { + artist.SetImage(ImageType.Logo, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + cancellationToken.ThrowIfCancellationRequested(); + + if (!item.ResolveArgs.ContainsMetaFileByName(BACKDROP_FILE)) + { + var nodes = doc.SelectNodes("//fanart/music/artistbackgrounds//@url"); + if (nodes != null) + { + var numBackdrops = 0; + foreach (XmlNode node in nodes) + { + path = node.Value; + if (!string.IsNullOrEmpty(path)) + { + Logger.Debug("FanArtProvider getting Backdrop for " + artist.Name); + try + { + artist.BackdropImagePaths.Add(await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, ("Backdrop"+(numBackdrops > 0 ? numBackdrops.ToString() : "")+".jpg"), FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + numBackdrops++; + if (numBackdrops >= ConfigurationManager.Configuration.MaxBackdrops) break; + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + + } + + } + + cancellationToken.ThrowIfCancellationRequested(); + + if (ConfigurationManager.Configuration.DownloadMovieArt && !item.ResolveArgs.ContainsMetaFileByName(ART_FILE)) + { + var node = + doc.SelectSingleNode("//fanart/music/musicarts/" + hd + "musicart/@url") ?? + doc.SelectSingleNode("//fanart/music/musicarts/musicart/@url"); + path = node != null ? node.Value : null; + if (!string.IsNullOrEmpty(path)) + { + Logger.Debug("FanArtProvider getting ClearArt for " + artist.Name); + try + { + artist.SetImage(ImageType.Art, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + cancellationToken.ThrowIfCancellationRequested(); + + if (ConfigurationManager.Configuration.DownloadMovieBanner && !item.ResolveArgs.ContainsMetaFileByName(BANNER_FILE)) + { + var node = doc.SelectSingleNode("//fanart/music/musicbanners/"+hd+"musicbanner/@url") ?? + doc.SelectSingleNode("//fanart/music/musicbanners/musicbanner/@url"); + path = node != null ? node.Value : null; + if (!string.IsNullOrEmpty(path)) + { + Logger.Debug("FanArtProvider getting Banner for " + artist.Name); + try + { + artist.SetImage(ImageType.Banner, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, BANNER_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + + cancellationToken.ThrowIfCancellationRequested(); + + // Artist thumbs are actually primary images (they are square/portrait) + if (!item.ResolveArgs.ContainsMetaFileByName(PRIMARY_FILE)) + { + var node = doc.SelectSingleNode("//fanart/music/artistthumbs/artistthumb/@url"); + path = node != null ? node.Value : null; + if (!string.IsNullOrEmpty(path)) + { + Logger.Debug("FanArtProvider getting Primary image for " + artist.Name); + try + { + artist.SetImage(ImageType.Primary, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, PRIMARY_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + } + } + SetLastRefreshed(artist, DateTime.UtcNow); + return true; + } + } +} diff --git a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs index ba32ba9e6f..0ac6d770d2 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; +using MediaBrowser.Model.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Logging; @@ -17,7 +18,6 @@ namespace MediaBrowser.Controller.Providers.Music { public class LastfmArtistProvider : LastfmBaseArtistProvider { - internal readonly SemaphoreSlim LastfmResourcePool = new SemaphoreSlim(5, 5); public LastfmArtistProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager) : base(jsonSerializer, httpClient, logManager, configurationManager) diff --git a/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs index a77cc48462..2d4bb1f59a 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs @@ -24,6 +24,8 @@ namespace MediaBrowser.Controller.Providers.Music /// public abstract class LastfmBaseProvider : BaseMetadataProvider { + protected static readonly SemaphoreSlim LastfmResourcePool = new SemaphoreSlim(5, 5); + /// /// Initializes a new instance of the class. /// @@ -197,6 +199,8 @@ namespace MediaBrowser.Controller.Providers.Music cancellationToken.ThrowIfCancellationRequested(); + item.SetProviderId(MetadataProviders.Musicbrainz, id); + await FetchLastfmData(item, id, cancellationToken).ConfigureAwait(false); } else