From 67b20fdf0f970041480b1ea7d08d80b72a394c51 Mon Sep 17 00:00:00 2001 From: Eric Reed Date: Mon, 4 Mar 2013 09:34:00 -0500 Subject: [PATCH 1/3] Initial FanArtArtistProvider --- .../MediaBrowser.Controller.csproj | 1 + .../Providers/FanartBaseProvider.cs | 55 +++- .../Providers/Music/FanArtArtistProvider.cs | 240 ++++++++++++++++++ .../Providers/Music/LastfmArtistProvider.cs | 1 + .../Providers/Music/LastfmBaseProvider.cs | 2 + 5 files changed, 291 insertions(+), 8 deletions(-) create mode 100644 MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index b0044745c3..604aa318fe 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -110,6 +110,7 @@ + diff --git a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs index 7a38fbb7a8..f775d0d3fc 100644 --- a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs +++ b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Entities; +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; using System; using MediaBrowser.Model.Logging; @@ -13,29 +14,44 @@ namespace MediaBrowser.Controller.Providers /// 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 /// protected const string APIKey = "5c6b04c68e904cfed1e6cbc9a9e683d4"; - protected FanartBaseProvider(ILogManager logManager) : base(logManager) + protected FanartBaseProvider(ILogManager logManager) + : base(logManager) { } @@ -49,8 +65,8 @@ namespace MediaBrowser.Controller.Providers { if (item.DontFetchMeta) return false; - return DateTime.UtcNow > (providerInfo.LastRefreshed.AddDays(Kernel.Instance.Configuration.MetadataRefreshDays)) - && ShouldFetch(item, providerInfo); + return DateTime.UtcNow > (providerInfo.LastRefreshed.AddDays(Kernel.Instance.Configuration.MetadataRefreshDays)) + && ShouldFetch(item, providerInfo); } /// @@ -59,10 +75,7 @@ namespace MediaBrowser.Controller.Providers /// true if [requires internet]; otherwise, false. public override bool RequiresInternet { - get - { - return true; - } + get { return true; } } /// @@ -84,6 +97,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..fa7b67e582 --- /dev/null +++ b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs @@ -0,0 +1,240 @@ +using System.Collections.Specialized; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; +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) + : base(logManager) + { + 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 && Kernel.Instance.Configuration.DownloadMovieArt) + || (!logoExists && Kernel.Instance.Configuration.DownloadMovieLogo) + || (!discExists && Kernel.Instance.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, Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)) + { + doc.Load(xml); + } + } + catch (HttpException) + { + } + + cancellationToken.ThrowIfCancellationRequested(); + + if (doc.HasChildNodes) + { + string path; + var hd = Kernel.Instance.Configuration.DownloadHDFanArt ? "hd" : ""; + if (Kernel.Instance.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, Kernel.Instance.ResourcePools.FanArt, 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"), Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)); + numBackdrops++; + if (numBackdrops >= Kernel.Instance.Configuration.MaxBackdrops) break; + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + + } + + } + + cancellationToken.ThrowIfCancellationRequested(); + + if (Kernel.Instance.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, Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)); + } + catch (HttpException) + { + } + catch (IOException) + { + + } + } + } + cancellationToken.ThrowIfCancellationRequested(); + + if (Kernel.Instance.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, Kernel.Instance.ResourcePools.FanArt, 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, Kernel.Instance.ResourcePools.FanArt, 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 5203b6f06b..5589c5c036 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs @@ -6,6 +6,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Net; +using MediaBrowser.Model.Entities; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Logging; diff --git a/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs index 54792932e3..ff46bde14f 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs @@ -195,6 +195,8 @@ namespace MediaBrowser.Controller.Providers.Music cancellationToken.ThrowIfCancellationRequested(); + item.SetProviderId(MetadataProviders.Musicbrainz, id); + await FetchLastfmData(item, id, cancellationToken).ConfigureAwait(false); } else From 669eeed8cf3c62fd91d91569cfe4f384feb4788b Mon Sep 17 00:00:00 2001 From: Eric Reed Date: Mon, 4 Mar 2013 10:13:03 -0500 Subject: [PATCH 2/3] Fix up providers from internal changes --- .../Providers/FanartBaseProvider.cs | 6 +++- .../Providers/Music/FanArtArtistProvider.cs | 33 ++++++++++--------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs index fe19b119ae..8ea7532f38 100644 --- a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs +++ b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using System.Threading; +using MediaBrowser.Controller.Configuration; using System.Collections.Generic; using MediaBrowser.Controller.Entities; using System; @@ -11,6 +12,9 @@ namespace MediaBrowser.Controller.Providers /// public abstract class FanartBaseProvider : BaseMetadataProvider { + + protected static readonly SemaphoreSlim FanArtResourcePool = new SemaphoreSlim(5,5); + /// /// The LOG o_ FILE /// diff --git a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs index fa7b67e582..d4585d58ae 100644 --- a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs @@ -1,6 +1,7 @@ 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; @@ -26,8 +27,8 @@ namespace MediaBrowser.Controller.Providers.Music /// The HTTP client. protected IHttpClient HttpClient { get; private set; } - public FanArtArtistProvider(IHttpClient httpClient, ILogManager logManager) - : base(logManager) + public FanArtArtistProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager) + : base(logManager, configurationManager) { if (httpClient == null) { @@ -65,9 +66,9 @@ namespace MediaBrowser.Controller.Providers.Music var logoExists = item.ResolveArgs.ContainsMetaFileByName(LOGO_FILE); var discExists = item.ResolveArgs.ContainsMetaFileByName(DISC_FILE); - return (!artExists && Kernel.Instance.Configuration.DownloadMovieArt) - || (!logoExists && Kernel.Instance.Configuration.DownloadMovieLogo) - || (!discExists && Kernel.Instance.Configuration.DownloadMovieDisc); + return (!artExists && ConfigurationManager.Configuration.DownloadMovieArt) + || (!logoExists && ConfigurationManager.Configuration.DownloadMovieLogo) + || (!discExists && ConfigurationManager.Configuration.DownloadMovieDisc); } /// @@ -89,7 +90,7 @@ namespace MediaBrowser.Controller.Providers.Music try { - using (var xml = await HttpClient.Get(url, Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)) + using (var xml = await HttpClient.Get(url, FanArtResourcePool, cancellationToken).ConfigureAwait(false)) { doc.Load(xml); } @@ -103,8 +104,8 @@ namespace MediaBrowser.Controller.Providers.Music if (doc.HasChildNodes) { string path; - var hd = Kernel.Instance.Configuration.DownloadHDFanArt ? "hd" : ""; - if (Kernel.Instance.Configuration.DownloadMovieLogo && !item.ResolveArgs.ContainsMetaFileByName(LOGO_FILE)) + 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") ?? @@ -115,7 +116,7 @@ namespace MediaBrowser.Controller.Providers.Music Logger.Debug("FanArtProvider getting ClearLogo for " + artist.Name); try { - artist.SetImage(ImageType.Logo, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, LOGO_FILE, Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)); + artist.SetImage(ImageType.Logo, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, LOGO_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -142,9 +143,9 @@ namespace MediaBrowser.Controller.Providers.Music 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"), Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)); + artist.BackdropImagePaths.Add(await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, ("Backdrop"+(numBackdrops > 0 ? numBackdrops.ToString() : "")+".jpg"), FanArtResourcePool, cancellationToken).ConfigureAwait(false)); numBackdrops++; - if (numBackdrops >= Kernel.Instance.Configuration.MaxBackdrops) break; + if (numBackdrops >= ConfigurationManager.Configuration.MaxBackdrops) break; } catch (HttpException) { @@ -162,7 +163,7 @@ namespace MediaBrowser.Controller.Providers.Music cancellationToken.ThrowIfCancellationRequested(); - if (Kernel.Instance.Configuration.DownloadMovieArt && !item.ResolveArgs.ContainsMetaFileByName(ART_FILE)) + if (ConfigurationManager.Configuration.DownloadMovieArt && !item.ResolveArgs.ContainsMetaFileByName(ART_FILE)) { var node = doc.SelectSingleNode("//fanart/music/musicarts/" + hd + "musicart/@url") ?? @@ -173,7 +174,7 @@ namespace MediaBrowser.Controller.Providers.Music Logger.Debug("FanArtProvider getting ClearArt for " + artist.Name); try { - artist.SetImage(ImageType.Art, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, ART_FILE, Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)); + artist.SetImage(ImageType.Art, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, ART_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -186,7 +187,7 @@ namespace MediaBrowser.Controller.Providers.Music } cancellationToken.ThrowIfCancellationRequested(); - if (Kernel.Instance.Configuration.DownloadMovieBanner && !item.ResolveArgs.ContainsMetaFileByName(BANNER_FILE)) + 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"); @@ -196,7 +197,7 @@ namespace MediaBrowser.Controller.Providers.Music Logger.Debug("FanArtProvider getting Banner for " + artist.Name); try { - artist.SetImage(ImageType.Banner, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, BANNER_FILE, Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)); + artist.SetImage(ImageType.Banner, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, BANNER_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { @@ -220,7 +221,7 @@ namespace MediaBrowser.Controller.Providers.Music Logger.Debug("FanArtProvider getting Primary image for " + artist.Name); try { - artist.SetImage(ImageType.Primary, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, PRIMARY_FILE, Kernel.Instance.ResourcePools.FanArt, cancellationToken).ConfigureAwait(false)); + artist.SetImage(ImageType.Primary, await Kernel.Instance.ProviderManager.DownloadAndSaveImage(artist, path, PRIMARY_FILE, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } catch (HttpException) { From c1eb5a3b60e460b68eba1b9f1e7c084145e4dcbb Mon Sep 17 00:00:00 2001 From: Eric Reed Date: Mon, 4 Mar 2013 10:33:54 -0500 Subject: [PATCH 3/3] Move lastfm resource pool to base --- MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs | 1 - MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs index 83d3e7dbad..0ac6d770d2 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs @@ -18,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 10ce46aa61..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. ///