From 19a95edf58eb7f412008c75cb8c020512d1cb846 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 2 May 2013 18:32:15 -0400 Subject: [PATCH] fixes #222 - Music providers not downloading art. --- .../HttpClientManager/HttpClientManager.cs | 1 - .../Entities/Audio/MusicArtist.cs | 1 - MediaBrowser.Controller/Entities/BaseItem.cs | 14 +-- .../Providers/BaseProviderInfo.cs | 5 +- .../Providers/FanartBaseProvider.cs | 9 +- .../Providers/ImagesByNameProvider.cs | 7 +- .../Providers/Movies/MovieDbProvider.cs | 2 +- .../Movies/RottenTomatoesMovieProvider.cs | 17 ++- .../Providers/Movies/TmdbPersonProvider.cs | 2 +- .../Providers/Music/FanArtAlbumProvider.cs | 119 +++++++++++++++++- .../Providers/Music/FanArtArtistProvider.cs | 16 +++ .../Providers/Music/LastfmAlbumProvider.cs | 53 +++++++- .../Providers/ProviderManager.cs | 7 +- 13 files changed, 221 insertions(+), 32 deletions(-) diff --git a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs index c707209ea..dd02bdc09 100644 --- a/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs +++ b/MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs @@ -97,7 +97,6 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager }; client = new HttpClient(handler); - client.DefaultRequestHeaders.Add("Accept", "application/json,image/*"); client.Timeout = TimeSpan.FromSeconds(30); _httpClients.TryAdd(host, client); } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 1f1d5e083..b2fc04873 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -6,6 +6,5 @@ namespace MediaBrowser.Controller.Entities.Audio /// public class MusicArtist : Folder { - } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 89aedadee..10fd3e79e 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -35,6 +35,8 @@ namespace MediaBrowser.Controller.Entities ScreenshotImagePaths = new List(); BackdropImagePaths = new List(); ProductionLocations = new List(); + Images = new Dictionary(); + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); } /// @@ -977,10 +979,10 @@ namespace MediaBrowser.Controller.Entities /// public virtual void ClearMetaValues() { - Images = null; + Images.Clear(); ForcedSortName = null; PremiereDate = null; - BackdropImagePaths = null; + BackdropImagePaths.Clear(); OfficialRating = null; CustomRating = null; Overview = null; @@ -992,7 +994,7 @@ namespace MediaBrowser.Controller.Entities RunTimeTicks = null; AspectRatio = null; ProductionYear = null; - ProviderIds = null; + ProviderIds.Clear(); DisplayMediaType = GetType().Name; ResolveArgs = null; } @@ -1465,12 +1467,6 @@ namespace MediaBrowser.Controller.Entities } else { - // Ensure it exists - if (Images == null) - { - Images = new Dictionary(); - } - Images[typeKey] = path; } } diff --git a/MediaBrowser.Controller/Providers/BaseProviderInfo.cs b/MediaBrowser.Controller/Providers/BaseProviderInfo.cs index 6262392fd..72fddfd93 100644 --- a/MediaBrowser.Controller/Providers/BaseProviderInfo.cs +++ b/MediaBrowser.Controller/Providers/BaseProviderInfo.cs @@ -28,10 +28,9 @@ namespace MediaBrowser.Controller.Providers /// The provider version. public string ProviderVersion { get; set; } /// - /// Gets or sets the custom data. + /// Contains a has of data that can be used to determine if the provider should refresh again /// - /// The custom data. - public string CustomData { get; set; } + public Guid Data { get; set; } } /// diff --git a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs index de749d7cc..617cd3256 100644 --- a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs +++ b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs @@ -1,9 +1,8 @@ -using System.Threading; -using MediaBrowser.Controller.Configuration; -using System.Collections.Generic; -using MediaBrowser.Controller.Entities; -using System; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Threading; namespace MediaBrowser.Controller.Providers { diff --git a/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs b/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs index 1df52f061..15e0ecfa8 100644 --- a/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs +++ b/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.IO; using MediaBrowser.Model.Logging; @@ -52,7 +53,7 @@ namespace MediaBrowser.Controller.Providers protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) { // Force a refresh if the IBN path changed - if (!string.Equals(providerInfo.CustomData, ConfigurationManager.ApplicationPaths.ItemsByNamePath, StringComparison.OrdinalIgnoreCase)) + if (providerInfo.Data != ConfigurationManager.ApplicationPaths.ItemsByNamePath.GetMD5()) { return true; } @@ -120,7 +121,7 @@ namespace MediaBrowser.Controller.Providers if (item.ProviderData.TryGetValue(Id, out data)) { - data.CustomData = ConfigurationManager.ApplicationPaths.ItemsByNamePath; + data.Data = ConfigurationManager.ApplicationPaths.ItemsByNamePath.GetMD5(); } return result; diff --git a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs index d750c335a..c5041e54b 100644 --- a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs @@ -253,7 +253,7 @@ namespace MediaBrowser.Controller.Providers.Movies new Regex(@"(?.*)") // last resort matches the whole string as the name }; - public const string LOCAL_META_FILE_NAME = "mbmovie.js"; + public const string LOCAL_META_FILE_NAME = "mbmovie.json"; public const string ALT_META_FILE_NAME = "movie.xml"; protected string ItemType = "movie"; diff --git a/MediaBrowser.Controller/Providers/Movies/RottenTomatoesMovieProvider.cs b/MediaBrowser.Controller/Providers/Movies/RottenTomatoesMovieProvider.cs index edb6db14d..ee2869e10 100644 --- a/MediaBrowser.Controller/Providers/Movies/RottenTomatoesMovieProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/RottenTomatoesMovieProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -114,7 +115,7 @@ namespace MediaBrowser.Controller.Providers.Movies } // Refresh if imdb id has changed - if (!string.Equals(item.GetProviderId(MetadataProviders.Imdb), providerInfo.CustomData)) + if (providerInfo.Data != GetComparisonData(item.GetProviderId(MetadataProviders.Imdb))) { return true; } @@ -144,7 +145,7 @@ namespace MediaBrowser.Controller.Providers.Movies if (item.ProviderData.TryGetValue(Id, out data)) { - data.CustomData = item.GetProviderId(MetadataProviders.Imdb); + data.Data = GetComparisonData(item.GetProviderId(MetadataProviders.Imdb)); } SetLastRefreshed(item, DateTime.UtcNow); @@ -152,6 +153,16 @@ namespace MediaBrowser.Controller.Providers.Movies return Task.FromResult(true); } + /// + /// Gets the comparison data. + /// + /// The imdb id. + /// Guid. + private Guid GetComparisonData(string imdbId) + { + return string.IsNullOrEmpty(imdbId) ? Guid.Empty : imdbId.GetMD5(); + } + /// /// Gets the priority. /// diff --git a/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs b/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs index affd4757a..ac0bf9911 100644 --- a/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Providers.Movies /// /// The meta file name /// - protected const string MetaFileName = "mbperson.js"; + protected const string MetaFileName = "mbperson.json"; protected readonly IProviderManager ProviderManager; diff --git a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs index 33ea8b9a9..15188bc69 100644 --- a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs @@ -7,18 +7,41 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using System; using System.IO; +using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; namespace MediaBrowser.Controller.Providers.Music { + /// + /// Class FanArtAlbumProvider + /// public class FanArtAlbumProvider : FanartBaseProvider { + /// + /// The _provider manager + /// 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; } + /// + /// Initializes a new instance of the class. + /// + /// The HTTP client. + /// The log manager. + /// The configuration manager. + /// The provider manager. public FanArtAlbumProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) : base(logManager, configurationManager) { @@ -26,11 +49,20 @@ namespace MediaBrowser.Controller.Providers.Music HttpClient = httpClient; } + /// + /// Supportses the specified item. + /// + /// The item. + /// true if XXXX, false otherwise public override bool Supports(BaseItem item) { return item is MusicAlbum; } + /// + /// Gets a value indicating whether [refresh on version change]. + /// + /// true if [refresh on version change]; otherwise, false. protected override bool RefreshOnVersionChange { get @@ -39,11 +71,15 @@ namespace MediaBrowser.Controller.Providers.Music } } + /// + /// Gets the provider version. + /// + /// The provider version. protected override string ProviderVersion { get { - return "20130501.5"; + return "11"; } } @@ -69,11 +105,27 @@ namespace MediaBrowser.Controller.Providers.Music return base.NeedsRefreshInternal(item, providerInfo); } + /// + /// 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 url = string.Format("http://api.fanart.tv/webservice/album/{0}/{1}/xml/all/1/1", APIKey, item.GetProviderId(MetadataProviders.Musicbrainz)); + var releaseGroupId = await GetReleaseGroupId(item.GetProviderId(MetadataProviders.Musicbrainz), cancellationToken).ConfigureAwait(false); + + if (string.IsNullOrEmpty(releaseGroupId)) + { + SetLastRefreshed(item, DateTime.UtcNow); + + return true; + } + + var url = string.Format("http://api.fanart.tv/webservice/album/{0}/{1}/xml/all/1/1", APIKey, releaseGroupId); var doc = new XmlDocument(); @@ -143,5 +195,68 @@ namespace MediaBrowser.Controller.Providers.Music return true; } + + /// + /// The _last music brainz request + /// + private DateTime _lastMusicBrainzRequest = DateTime.MinValue; + + /// + /// Gets the release group id. + /// + /// The release entry id. + /// The cancellation token. + /// Task{System.String}. + private async Task GetReleaseGroupId(string releaseEntryId, CancellationToken cancellationToken) + { + await _musicBrainzResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + var diff = 1000 - (DateTime.Now - _lastMusicBrainzRequest).TotalMilliseconds; + + // MusicBrainz is extremely adamant about limiting to one request per second + + if (diff > 0) + { + await Task.Delay(Convert.ToInt32(diff), cancellationToken).ConfigureAwait(false); + } + + _lastMusicBrainzRequest = DateTime.Now; + + return await GetReleaseGroupIdInternal(releaseEntryId, cancellationToken).ConfigureAwait(false); + } + finally + { + _musicBrainzResourcePool.Release(); + } + } + + /// + /// Gets the release group id internal. + /// + /// The release entry id. + /// The cancellation token. + /// Task{System.String}. + private async Task GetReleaseGroupIdInternal(string releaseEntryId, CancellationToken cancellationToken) + { + var url = string.Format("http://www.musicbrainz.org/ws/2/release-group/?query=reid:{0}", releaseEntryId); + + var doc = new XmlDocument(); + + using (var xml = await HttpClient.Get(url, cancellationToken).ConfigureAwait(false)) + { + using (var oReader = new StreamReader(xml, Encoding.UTF8)) + { + doc.Load(oReader); + } + } + + 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.Controller/Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs index 86c7a22f4..320acb701 100644 --- a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs @@ -58,6 +58,22 @@ namespace MediaBrowser.Controller.Providers.Music get { return ConfigurationManager.Configuration.SaveLocalMeta; } } + protected override bool RefreshOnVersionChange + { + get + { + return true; + } + } + + protected override string ProviderVersion + { + get + { + return "1"; + } + } + /// /// Needses the refresh internal. /// diff --git a/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs index a3fad6e8a..0cdd9b19b 100644 --- a/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs @@ -1,7 +1,9 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using MoreLinq; @@ -32,6 +34,24 @@ namespace MediaBrowser.Controller.Providers.Music return BlankId; } + /// + /// Needses the refresh internal. + /// + /// The item. + /// The provider info. + /// true if XXXX, false otherwise + protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) + { + // 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.Data) + { + return true; + } + + return base.NeedsRefreshInternal(item, providerInfo); + } + protected override async Task FetchLastfmData(BaseItem item, string id, CancellationToken cancellationToken) { var result = await GetAlbumResult(item, cancellationToken).ConfigureAwait(false); @@ -51,6 +71,13 @@ namespace MediaBrowser.Controller.Providers.Music } } + + BaseProviderInfo data; + + if (item.ProviderData.TryGetValue(Id, out data)) + { + data.Data = GetComparisonData(item as MusicAlbum); + } } private async Task GetAlbumResult(BaseItem item, CancellationToken cancellationToken) @@ -102,5 +129,29 @@ namespace MediaBrowser.Controller.Providers.Music return true; } } + + /// + /// Gets the data. + /// + /// The album. + /// Guid. + private Guid GetComparisonData(MusicAlbum album) + { + var songs = album.RecursiveChildren.OfType