diff options
52 files changed, 1145 insertions, 1535 deletions
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 203e6dc43..b3bf0d2b6 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -21,9 +21,6 @@ namespace MediaBrowser.Controller.Entities.Audio Tags = new List<string>(); } - public string LastFmImageUrl { get; set; } - public string LastFmImageSize { get; set; } - /// <summary> /// Gets or sets the tags. /// </summary> diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index ee8bb2761..b05f14a13 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -146,11 +146,13 @@ <Compile Include="Providers\IDynamicInfoProvider.cs" /> <Compile Include="Providers\IHasMetadata.cs" /> <Compile Include="Providers\IImageProvider.cs" /> + <Compile Include="Providers\ILocalMetadataProvider.cs" /> <Compile Include="Providers\IProviderRepository.cs" /> <Compile Include="Providers\IRemoteImageProvider.cs" /> <Compile Include="Providers\ILocalImageProvider.cs" /> <Compile Include="Providers\IMetadataProvider.cs" /> <Compile Include="Providers\IMetadataService.cs" /> + <Compile Include="Providers\IRemoteMetadataProvider.cs" /> <Compile Include="Providers\ItemId.cs" /> <Compile Include="Providers\MetadataRefreshOptions.cs" /> <Compile Include="Providers\NameParser.cs" /> diff --git a/MediaBrowser.Controller/Providers/IImageProvider.cs b/MediaBrowser.Controller/Providers/IImageProvider.cs index 61f5579f4..1e5bdfeaf 100644 --- a/MediaBrowser.Controller/Providers/IImageProvider.cs +++ b/MediaBrowser.Controller/Providers/IImageProvider.cs @@ -19,11 +19,5 @@ namespace MediaBrowser.Controller.Providers /// <param name="item">The item.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> bool Supports(IHasImages item); - - /// <summary> - /// Gets the order. - /// </summary> - /// <value>The order.</value> - int Order { get; } } } diff --git a/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs new file mode 100644 index 000000000..62b208b59 --- /dev/null +++ b/MediaBrowser.Controller/Providers/ILocalMetadataProvider.cs @@ -0,0 +1,27 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Providers +{ + public interface ILocalMetadataProvider : IMetadataProvider + { + /// <summary> + /// Determines whether [has local metadata] [the specified item]. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if [has local metadata] [the specified item]; otherwise, <c>false</c>.</returns> + bool HasLocalMetadata(IHasMetadata item); + } + + public interface ILocalMetadataProvider<TItemType> : IMetadataProvider<TItemType>, ILocalMetadataProvider + where TItemType : IHasMetadata + { + /// <summary> + /// Gets the metadata. + /// </summary> + /// <param name="path">The path.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{MetadataResult{`0}}.</returns> + Task<MetadataResult<TItemType>> GetMetadata(string path, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Providers/IMetadataProvider.cs b/MediaBrowser.Controller/Providers/IMetadataProvider.cs index 843ba263b..123e806ed 100644 --- a/MediaBrowser.Controller/Providers/IMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/IMetadataProvider.cs @@ -1,6 +1,4 @@ using System; -using System.Threading; -using System.Threading.Tasks; namespace MediaBrowser.Controller.Providers { @@ -20,32 +18,6 @@ namespace MediaBrowser.Controller.Providers where TItemType : IHasMetadata { } - - public interface ILocalMetadataProvider : IMetadataProvider - { - /// <summary> - /// Determines whether [has local metadata] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if [has local metadata] [the specified item]; otherwise, <c>false</c>.</returns> - bool HasLocalMetadata(IHasMetadata item); - } - - public interface IRemoteMetadataProvider : IMetadataProvider - { - } - - public interface IRemoteMetadataProvider<TItemType> : IMetadataProvider<TItemType>, IRemoteMetadataProvider - where TItemType : IHasMetadata - { - Task<MetadataResult<TItemType>> GetMetadata(ItemId id, CancellationToken cancellationToken); - } - - public interface ILocalMetadataProvider<TItemType> : IMetadataProvider<TItemType>, ILocalMetadataProvider - where TItemType : IHasMetadata - { - Task<MetadataResult<TItemType>> GetMetadata(string path, CancellationToken cancellationToken); - } public interface IHasChangeMonitor { @@ -58,6 +30,11 @@ namespace MediaBrowser.Controller.Providers bool HasChanged(IHasMetadata item, DateTime date); } + public interface IHasOrder + { + int Order { get; } + } + public class MetadataResult<T> where T : IHasMetadata { diff --git a/MediaBrowser.Controller/Providers/IMetadataService.cs b/MediaBrowser.Controller/Providers/IMetadataService.cs index c6cc2b716..e9ce320ab 100644 --- a/MediaBrowser.Controller/Providers/IMetadataService.cs +++ b/MediaBrowser.Controller/Providers/IMetadataService.cs @@ -10,8 +10,7 @@ namespace MediaBrowser.Controller.Providers /// Adds the parts. /// </summary> /// <param name="providers">The providers.</param> - /// <param name="imageProviders">The image providers.</param> - void AddParts(IEnumerable<IMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders); + void AddParts(IEnumerable<IMetadataProvider> providers); /// <summary> /// Determines whether this instance can refresh the specified item. diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index dc57552c4..0159f778f 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -74,13 +74,13 @@ namespace MediaBrowser.Controller.Providers /// <param name="providerName">Name of the provider.</param> /// <param name="type">The type.</param> /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns> - Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null); + Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null); /// <summary> /// Gets the image providers. /// </summary> /// <param name="item">The item.</param> /// <returns>IEnumerable{ImageProviderInfo}.</returns> - IEnumerable<ImageProviderInfo> GetImageProviderInfo(BaseItem item); + IEnumerable<ImageProviderInfo> GetImageProviderInfo(IHasImages item); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs new file mode 100644 index 000000000..6007a5af6 --- /dev/null +++ b/MediaBrowser.Controller/Providers/IRemoteMetadataProvider.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Providers +{ + public interface IRemoteMetadataProvider : IMetadataProvider + { + } + + public interface IRemoteMetadataProvider<TItemType> : IMetadataProvider<TItemType>, IRemoteMetadataProvider + where TItemType : IHasMetadata + { + /// <summary> + /// Gets the metadata. + /// </summary> + /// <param name="id">The identifier.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{MetadataResult{`0}}.</returns> + Task<MetadataResult<TItemType>> GetMetadata(ItemId id, CancellationToken cancellationToken); + } +} diff --git a/MediaBrowser.Controller/Providers/ItemId.cs b/MediaBrowser.Controller/Providers/ItemId.cs index b3fe5bee5..3abb64bfb 100644 --- a/MediaBrowser.Controller/Providers/ItemId.cs +++ b/MediaBrowser.Controller/Providers/ItemId.cs @@ -37,4 +37,18 @@ namespace MediaBrowser.Controller.Providers ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); } } + + public class AlbumId : ItemId + { + /// <summary> + /// Gets or sets the album artist. + /// </summary> + /// <value>The album artist.</value> + public string AlbumArtist { get; set; } + /// <summary> + /// Gets or sets the artist music brainz identifier. + /// </summary> + /// <value>The artist music brainz identifier.</value> + public string ArtistMusicBrainzId { get; set; } + } } diff --git a/MediaBrowser.Providers/All/LocalImageProvider.cs b/MediaBrowser.Providers/All/LocalImageProvider.cs index 88a68bc15..089cfe549 100644 --- a/MediaBrowser.Providers/All/LocalImageProvider.cs +++ b/MediaBrowser.Providers/All/LocalImageProvider.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; @@ -38,13 +39,14 @@ namespace MediaBrowser.Providers.All if (locationType == LocationType.FileSystem) { // Episode has it's own provider - if (item is Episode) + if (item is Episode || item is Audio) { return false; } return true; } + if (locationType == LocationType.Virtual) { var season = item as Season; diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs index b9b4e40d7..53ab3614d 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs +++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs @@ -15,7 +15,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.BoxSets { - public class BoxSetMetadataService : ConcreteMetadataService<BoxSet> + public class BoxSetMetadataService : ConcreteMetadataService<BoxSet, ItemId> { private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _iLocalizationManager; diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs index 2e75a66cb..b7672a6a7 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.BoxSets { - class MovieDbBoxSetImageProvider : IRemoteImageProvider + class MovieDbBoxSetImageProvider : IRemoteImageProvider, IHasOrder { private readonly IHttpClient _httpClient; diff --git a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs index 94209c309..1eefb0798 100644 --- a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs +++ b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.GameGenres { - public class GameGenreMetadataService : ConcreteMetadataService<GameGenre> + public class GameGenreMetadataService : ConcreteMetadataService<GameGenre, ItemId> { private readonly ILibraryManager _libraryManager; diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs index 21addc390..fa4a4c955 100644 --- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs +++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Genres { - public class GenreMetadataService : ConcreteMetadataService<Genre> + public class GenreMetadataService : ConcreteMetadataService<Genre, ItemId> { private readonly ILibraryManager _libraryManager; diff --git a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs index 067894337..5c476c89d 100644 --- a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs +++ b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.LiveTv { - public class ChannelMetadataService : ConcreteMetadataService<LiveTvChannel> + public class ChannelMetadataService : ConcreteMetadataService<LiveTvChannel, ItemId> { private readonly ILibraryManager _libraryManager; diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs index 6f08b199a..0a22daf2f 100644 --- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs +++ b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.LiveTv { - public class ProgramMetadataService : ConcreteMetadataService<LiveTvProgram> + public class ProgramMetadataService : ConcreteMetadataService<LiveTvProgram, ItemId> { private readonly ILibraryManager _libraryManager; diff --git a/MediaBrowser.Providers/Manager/ConcreteMetadataService.cs b/MediaBrowser.Providers/Manager/ConcreteMetadataService.cs index 3a4bc06ca..f3644581e 100644 --- a/MediaBrowser.Providers/Manager/ConcreteMetadataService.cs +++ b/MediaBrowser.Providers/Manager/ConcreteMetadataService.cs @@ -4,8 +4,9 @@ using MediaBrowser.Model.Logging; namespace MediaBrowser.Providers.Manager { - public abstract class ConcreteMetadataService<TItemType> : MetadataService<TItemType> - where TItemType : IHasMetadata, new() + public abstract class ConcreteMetadataService<TItemType, TIdType> : MetadataService<TItemType, TIdType> + where TItemType : IHasMetadata, new() + where TIdType : ItemId, new() { protected ConcreteMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo) : base(serverConfigurationManager, logger, providerManager, providerRepo) diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index e8bae1d2f..7f0dc8b89 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Common.Extensions; +using System.IO; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -21,6 +23,7 @@ namespace MediaBrowser.Providers.Manager private readonly ILogger _logger; private readonly IProviderManager _providerManager; private readonly IServerConfigurationManager _config; + private readonly IFileSystem _fileSystem; public ItemImageProvider(ILogger logger, IProviderManager providerManager, IServerConfigurationManager config) { @@ -97,9 +100,21 @@ namespace MediaBrowser.Providers.Manager if (response.HasImage) { - var mimeType = "image/" + response.Format.ToString().ToLower(); + if (!string.IsNullOrEmpty(response.Path)) + { + var mimeType = "image/" + Path.GetExtension(response.Path).TrimStart('.').ToLower(); - await _providerManager.SaveImage((BaseItem)item, response.Stream, mimeType, imageType, null, Guid.NewGuid().ToString(), cancellationToken).ConfigureAwait(false); + var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, + FileShare.Read, true); + + await _providerManager.SaveImage((BaseItem)item, stream, mimeType, imageType, null, Guid.NewGuid().ToString(), cancellationToken).ConfigureAwait(false); + } + else + { + var mimeType = "image/" + response.Format.ToString().ToLower(); + + await _providerManager.SaveImage((BaseItem)item, response.Stream, mimeType, imageType, null, Guid.NewGuid().ToString(), cancellationToken).ConfigureAwait(false); + } result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate; } @@ -227,26 +242,14 @@ namespace MediaBrowser.Providers.Manager /// <returns>IEnumerable{IImageProvider}.</returns> private IEnumerable<IImageProvider> GetImageProviders(IHasImages item, IEnumerable<IImageProvider> imageProviders) { - var providers = imageProviders.Where(i => - { - try - { - return i.Supports(item); - } - catch (Exception ex) - { - _logger.ErrorException("Error in ImageProvider.Supports", ex, i.Name); - - return false; - } - }); + var providers = imageProviders; if (!_config.Configuration.EnableInternetProviders) { providers = providers.Where(i => !(i is IRemoteImageProvider)); } - return providers.OrderBy(i => i.Order); + return providers; } private bool MergeImages(IHasImages item, List<LocalImageInfo> images) diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index d8d9ee0ca..19f155a07 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -13,8 +12,9 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Manager { - public abstract class MetadataService<TItemType> : IMetadataService + public abstract class MetadataService<TItemType, TIdType> : IMetadataService where TItemType : IHasMetadata + where TIdType : ItemId, new() { protected readonly IServerConfigurationManager ServerConfigurationManager; protected readonly ILogger Logger; @@ -23,8 +23,6 @@ namespace MediaBrowser.Providers.Manager private IMetadataProvider<TItemType>[] _providers = { }; - private IImageProvider[] _imageProviders = { }; - protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo) { ServerConfigurationManager = serverConfigurationManager; @@ -37,13 +35,10 @@ namespace MediaBrowser.Providers.Manager /// Adds the parts. /// </summary> /// <param name="providers">The providers.</param> - /// <param name="imageProviders">The image providers.</param> - public void AddParts(IEnumerable<IMetadataProvider> providers, IEnumerable<IImageProvider> imageProviders) + public void AddParts(IEnumerable<IMetadataProvider> providers) { _providers = providers.OfType<IMetadataProvider<TItemType>>() .ToArray(); - - _imageProviders = imageProviders.OrderBy(i => i.Order).ToArray(); } /// <summary> @@ -79,11 +74,13 @@ namespace MediaBrowser.Providers.Manager var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager); var localImagesFailed = false; + var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item).ToList(); + // Start by validating images try { // Always validate images and check for new locally stored ones. - if (itemImageProvider.ValidateImages(item, GetLocalImageProviders(item))) + if (itemImageProvider.ValidateImages(item, allImageProviders.OfType<ILocalImageProvider>())) { updateType = updateType | ItemUpdateType.ImageUpdate; } @@ -114,7 +111,7 @@ namespace MediaBrowser.Providers.Manager // Next run remote image providers, but only if local image providers didn't throw an exception if (!localImagesFailed && options.ImageRefreshMode != ImageRefreshMode.ValidationOnly) { - var providers = GetNonLocalImageProviders(item, lastResult.DateLastImagesRefresh.HasValue, options).ToList(); + var providers = GetNonLocalImageProviders(item, allImageProviders, lastResult.DateLastImagesRefresh.HasValue, options).ToList(); if (providers.Count > 0) { @@ -135,7 +132,7 @@ namespace MediaBrowser.Providers.Manager { if (string.IsNullOrEmpty(item.Name)) { - throw new InvalidOperationException("Item has no name"); + throw new InvalidOperationException(item.GetType().Name + " has no name: " + item.Path); } // Save to database @@ -167,7 +164,7 @@ namespace MediaBrowser.Providers.Manager protected virtual IEnumerable<IMetadataProvider> GetProviders(IHasMetadata item, bool hasRefreshedMetadata, MetadataRefreshOptions options) { // Get providers to refresh - var providers = _providers.Where(i => CanRefresh(i, item)).ToList(); + var providers = ((ProviderManager) ProviderManager).GetMetadataProviders<TItemType>(item).ToList(); // Run all if either of these flags are true var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || !hasRefreshedMetadata; @@ -193,22 +190,10 @@ namespace MediaBrowser.Providers.Manager return providers; } - protected virtual IEnumerable<IImageProvider> GetNonLocalImageProviders(IHasMetadata item, bool hasRefreshedImages, ImageRefreshOptions options) + protected virtual IEnumerable<IImageProvider> GetNonLocalImageProviders(IHasMetadata item, IEnumerable<IImageProvider> allImageProviders, bool hasRefreshedImages, ImageRefreshOptions options) { // Get providers to refresh - var providers = _imageProviders.Where(i => - { - try - { - return !(i is ILocalImageProvider) && i.Supports(item); - } - catch (Exception ex) - { - Logger.ErrorException("Error in ImageProvider.Supports", ex, i.Name); - - return false; - } - }).ToList(); + var providers = allImageProviders.Where(i => !(i is ILocalImageProvider)).ToList(); // Run all if either of these flags are true var runAllProviders = options.ImageRefreshMode == ImageRefreshMode.FullRefresh || !hasRefreshedImages; @@ -226,33 +211,12 @@ namespace MediaBrowser.Providers.Manager return providers; } - - /// <summary> - /// Determines whether this instance can refresh the specified provider. - /// </summary> - /// <param name="provider">The provider.</param> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if this instance can refresh the specified provider; otherwise, <c>false</c>.</returns> - protected bool CanRefresh(IMetadataProvider provider, IHasMetadata item) - { - if (!ServerConfigurationManager.Configuration.EnableInternetProviders && provider is IRemoteMetadataProvider) - { - return false; - } - - if (item.LocationType != LocationType.FileSystem && provider is ILocalMetadataProvider) - { - return false; - } - - return true; - } protected abstract Task SaveItem(TItemType item, ItemUpdateType reason, CancellationToken cancellationToken); - protected virtual ItemId GetId(IHasMetadata item) + protected virtual TIdType GetId(TItemType item) { - return new ItemId + return new TIdType { MetadataCountryCode = item.GetPreferredMetadataCountryCode(), MetadataLanguage = item.GetPreferredMetadataLanguage(), @@ -371,23 +335,6 @@ namespace MediaBrowser.Providers.Manager return 0; } } - - private IEnumerable<ILocalImageProvider> GetLocalImageProviders(IHasImages item) - { - return _imageProviders.OfType<ILocalImageProvider>().Where(i => - { - try - { - return i.Supports(item); - } - catch (Exception ex) - { - Logger.ErrorException("Error in ImageProvider.Supports", ex, i.Name); - - return false; - } - }); - } } public class RefreshResult diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 3696bd02f..77a9298ee 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -50,7 +50,6 @@ namespace MediaBrowser.Providers.Manager /// <value>The metadata providers enumerable.</value> private BaseMetadataProvider[] MetadataProviders { get; set; } - private IRemoteImageProvider[] RemoteImageProviders { get; set; } private IImageProvider[] ImageProviders { get; set; } private readonly IFileSystem _fileSystem; @@ -58,6 +57,7 @@ namespace MediaBrowser.Providers.Manager private readonly IProviderRepository _providerRepo; private IMetadataService[] _metadataServices = { }; + private IMetadataProvider[] _metadataProviders = { }; /// <summary> /// Initializes a new instance of the <see cref="ProviderManager" /> class. @@ -89,16 +89,10 @@ namespace MediaBrowser.Providers.Manager { MetadataProviders = providers.OrderBy(e => e.Priority).ToArray(); - ImageProviders = imageProviders.OrderBy(i => i.Order).ToArray(); - RemoteImageProviders = ImageProviders.OfType<IRemoteImageProvider>().ToArray(); + ImageProviders = imageProviders.ToArray(); _metadataServices = metadataServices.OrderBy(i => i.Order).ToArray(); - - var providerList = metadataProviders.ToList(); - foreach (var service in _metadataServices) - { - service.AddParts(providerList, ImageProviders); - } + _metadataProviders = metadataProviders.ToArray(); } public Task RefreshMetadata(IHasMetadata item, MetadataRefreshOptions options, CancellationToken cancellationToken) @@ -391,7 +385,7 @@ namespace MediaBrowser.Providers.Manager /// <param name="providerName">Name of the provider.</param> /// <param name="type">The type.</param> /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns> - public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null) + public async Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(IHasImages item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null) { var providers = GetRemoteImageProviders(item); @@ -418,7 +412,7 @@ namespace MediaBrowser.Providers.Manager /// <param name="preferredLanguage">The preferred language.</param> /// <param name="type">The type.</param> /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns> - private async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken, IRemoteImageProvider i, string preferredLanguage, ImageType? type = null) + private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken, IRemoteImageProvider i, string preferredLanguage, ImageType? type = null) { try { @@ -452,9 +446,23 @@ namespace MediaBrowser.Providers.Manager return images; } - private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(BaseItem item) + /// <summary> + /// Gets the supported image providers. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>IEnumerable{IImageProvider}.</returns> + public IEnumerable<ImageProviderInfo> GetImageProviderInfo(IHasImages item) + { + return GetRemoteImageProviders(item).Select(i => new ImageProviderInfo + { + Name = i.Name, + Order = GetOrder(item, i) + }); + } + + public IEnumerable<IImageProvider> GetImageProviders(IHasImages item) { - return RemoteImageProviders.Where(i => + return ImageProviders.Where(i => { try { @@ -466,22 +474,77 @@ namespace MediaBrowser.Providers.Manager return false; } - }); + }).OrderBy(i => GetOrder(item, i)); + } + + public IEnumerable<IMetadataProvider<T>> GetMetadataProviders<T>(IHasMetadata item) + where T : IHasMetadata + { + return _metadataProviders.OfType<IMetadataProvider<T>>() + .Where(i => CanRefresh(i, item)) + .OrderBy(i => GetOrder(item, i)); + } + + private IEnumerable<IRemoteImageProvider> GetRemoteImageProviders(IHasImages item) + { + return GetImageProviders(item).OfType<IRemoteImageProvider>(); } /// <summary> - /// Gets the supported image providers. + /// Determines whether this instance can refresh the specified provider. /// </summary> + /// <param name="provider">The provider.</param> /// <param name="item">The item.</param> - /// <returns>IEnumerable{IImageProvider}.</returns> - public IEnumerable<ImageProviderInfo> GetImageProviderInfo(BaseItem item) + /// <returns><c>true</c> if this instance can refresh the specified provider; otherwise, <c>false</c>.</returns> + protected bool CanRefresh(IMetadataProvider provider, IHasMetadata item) { - return GetRemoteImageProviders(item).Select(i => new ImageProviderInfo + if (!ConfigurationManager.Configuration.EnableInternetProviders && provider is IRemoteMetadataProvider) { - Name = i.Name, - Order = i.Order + return false; + } - }); + if (item.LocationType != LocationType.FileSystem && provider is ILocalMetadataProvider) + { + return false; + } + + return true; + } + + /// <summary> + /// Gets the order. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="provider">The provider.</param> + /// <returns>System.Int32.</returns> + private int GetOrder(IHasImages item, IImageProvider provider) + { + var hasOrder = provider as IHasOrder; + + if (hasOrder == null) + { + return 0; + } + + return hasOrder.Order; + } + + /// <summary> + /// Gets the order. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="provider">The provider.</param> + /// <returns>System.Int32.</returns> + private int GetOrder(IHasMetadata item, IMetadataProvider provider) + { + var hasOrder = provider as IHasOrder; + + if (hasOrder == null) + { + return 0; + } + + return hasOrder.Order; } } } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 83c628e23..512b11051 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -100,29 +100,26 @@ <Compile Include="Movies\ManualMovieDbImageProvider.cs" /> <Compile Include="Movies\ManualFanartMovieImageProvider.cs" /> <Compile Include="MusicGenres\MusicGenreMetadataService.cs" /> + <Compile Include="Music\AlbumMetadataService.cs" /> <Compile Include="Music\ArtistMetadataService.cs" /> - <Compile Include="Music\LastFmArtistProvider.cs" /> + <Compile Include="Music\LastfmArtistProvider.cs" /> <Compile Include="People\MovieDbPersonImageProvider.cs" /> <Compile Include="Movies\MovieUpdatesPrescanTask.cs" /> <Compile Include="Movies\MovieXmlParser.cs" /> - <Compile Include="Movies\FanArtMovieProvider.cs" /> - <Compile Include="Movies\FanArtMovieUpdatesPrescanTask.cs" /> + <Compile Include="Movies\FanartMovieProvider.cs" /> + <Compile Include="Movies\FanartMovieUpdatesPrescanTask.cs" /> <Compile Include="Movies\MovieDbImagesProvider.cs" /> <Compile Include="Movies\MovieDbProvider.cs" /> <Compile Include="Movies\MovieProviderFromXml.cs" /> <Compile Include="Movies\OpenMovieDatabaseProvider.cs" /> - <Compile Include="Music\AlbumInfoFromSongProvider.cs" /> - <Compile Include="Music\AlbumProviderFromXml.cs" /> + <Compile Include="Music\AlbumXmlProvider.cs" /> <Compile Include="Music\ArtistXmlProvider.cs" /> - <Compile Include="Music\FanArtAlbumProvider.cs" /> - <Compile Include="Music\FanArtUpdatesPrescanTask.cs" /> + <Compile Include="Music\FanartUpdatesPrescanTask.cs" /> <Compile Include="Music\LastfmAlbumProvider.cs" /> - <Compile Include="Music\LastFmImageProvider.cs" /> - <Compile Include="Music\LastfmBaseProvider.cs" /> <Compile Include="Music\LastfmHelper.cs" /> - <Compile Include="Music\ManualFanartAlbumProvider.cs" /> + <Compile Include="Music\FanartAlbumProvider.cs" /> <Compile Include="Music\FanartArtistProvider.cs" /> - <Compile Include="Music\ManualLastFmImageProvider.cs" /> + <Compile Include="Music\LastfmImageProvider.cs" /> <Compile Include="Music\MusicBrainzAlbumProvider.cs" /> <Compile Include="Music\MusicVideoXmlParser.cs" /> <Compile Include="Music\SoundtrackPostScanTask.cs" /> diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs index 2cf403b09..f2cd2718a 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs +++ b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.Movies /// <summary> /// Class FanArtMovieProvider /// </summary> - class FanArtMovieProvider : BaseMetadataProvider + class FanartMovieProvider : BaseMetadataProvider { /// <summary> /// Gets the HTTP client. @@ -37,18 +37,18 @@ namespace MediaBrowser.Providers.Movies /// </summary> private readonly IProviderManager _providerManager; - internal static FanArtMovieProvider Current { get; private set; } + internal static FanartMovieProvider Current { get; private set; } private readonly IFileSystem _fileSystem; /// <summary> - /// Initializes a new instance of the <see cref="FanArtMovieProvider" /> class. + /// Initializes a new instance of the <see cref="FanartMovieProvider" /> class. /// </summary> /// <param name="httpClient">The HTTP client.</param> /// <param name="logManager">The log manager.</param> /// <param name="configurationManager">The configuration manager.</param> /// <param name="providerManager">The provider manager.</param> /// <exception cref="System.ArgumentNullException">httpClient</exception> - public FanArtMovieProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) + public FanartMovieProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) : base(logManager, configurationManager) { if (httpClient == null) diff --git a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs index 88f478d1c..47948455b 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/Movies/FanArtMovieUpdatesPrescanTask.cs @@ -16,7 +16,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Movies { - class FanArtMovieUpdatesPrescanTask : ILibraryPostScanTask + class FanartMovieUpdatesPrescanTask : ILibraryPostScanTask { private const string UpdatesUrl = "http://api.fanart.tv/webservice/newmovies/{0}/{1}/"; @@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.Movies private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public FanArtMovieUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IFileSystem fileSystem) + public FanartMovieUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _config = config; @@ -60,7 +60,7 @@ namespace MediaBrowser.Providers.Movies return; } - var path = FanArtMovieProvider.GetMoviesDataPath(_config.CommonApplicationPaths); + var path = FanartMovieProvider.GetMoviesDataPath(_config.CommonApplicationPaths); Directory.CreateDirectory(path); @@ -118,7 +118,7 @@ namespace MediaBrowser.Providers.Movies return new List<string>(); } - var updates = _jsonSerializer.DeserializeFromString<List<FanArtUpdatesPrescanTask.FanArtUpdate>>(json); + var updates = _jsonSerializer.DeserializeFromString<List<FanartUpdatesPrescanTask.FanArtUpdate>>(json); var existingDictionary = existingIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); @@ -136,7 +136,7 @@ namespace MediaBrowser.Providers.Movies { _logger.Info("Updating movie " + id); - await FanArtMovieProvider.Current.DownloadMovieXml(id, cancellationToken).ConfigureAwait(false); + await FanartMovieProvider.Current.DownloadMovieXml(id, cancellationToken).ConfigureAwait(false); numComplete++; double percent = numComplete; diff --git a/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs index c94339a7e..70aaec526 100644 --- a/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs +++ b/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs @@ -20,7 +20,7 @@ using MediaBrowser.Providers.Music; namespace MediaBrowser.Providers.Movies { - public class ManualFanartMovieImageProvider : IRemoteImageProvider, IHasChangeMonitor + public class ManualFanartMovieImageProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder { private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IServerConfigurationManager _config; @@ -86,9 +86,9 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(movieId)) { - await FanArtMovieProvider.Current.EnsureMovieXml(movieId, cancellationToken).ConfigureAwait(false); + await FanartMovieProvider.Current.EnsureMovieXml(movieId, cancellationToken).ConfigureAwait(false); - var xmlPath = FanArtMovieProvider.Current.GetFanartXmlPath(movieId); + var xmlPath = FanartMovieProvider.Current.GetFanartXmlPath(movieId); try { @@ -344,7 +344,7 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(id)) { // Process images - var xmlPath = FanArtMovieProvider.Current.GetFanartXmlPath(id); + var xmlPath = FanartMovieProvider.Current.GetFanartXmlPath(id); var fileInfo = new FileInfo(xmlPath); diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs index 8c6e2d92e..bc4652e2c 100644 --- a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs @@ -15,7 +15,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Movies { - class ManualMovieDbImageProvider : IRemoteImageProvider + class ManualMovieDbImageProvider : IRemoteImageProvider, IHasOrder { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; diff --git a/MediaBrowser.Providers/Music/AlbumInfoFromSongProvider.cs b/MediaBrowser.Providers/Music/AlbumInfoFromSongProvider.cs deleted file mode 100644 index 47799b8f3..000000000 --- a/MediaBrowser.Providers/Music/AlbumInfoFromSongProvider.cs +++ /dev/null @@ -1,154 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Music -{ - public class AlbumInfoFromSongProvider : BaseMetadataProvider - { - public AlbumInfoFromSongProvider(ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - } - - public override bool Supports(BaseItem item) - { - return item is MusicAlbum; - } - - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - protected override string ProviderVersion - { - get - { - return "2"; - } - } - - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - // If song metadata has changed - if (GetComparisonData((MusicAlbum)item) != providerInfo.FileStamp) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - /// <summary> - /// Gets the data. - /// </summary> - /// <param name="album">The album.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(MusicAlbum album) - { - var songs = album.RecursiveChildren.OfType<Audio>().ToList(); - - return GetComparisonData(songs); - } - - private Guid GetComparisonData(List<Audio> songs) - { - var albumArtistNames = songs.Select(i => i.AlbumArtist) - .Where(i => !string.IsNullOrEmpty(i)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - var studios = songs.SelectMany(i => i.Studios) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - var genres = songs.SelectMany(i => i.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - albumArtistNames.AddRange(studios); - albumArtistNames.AddRange(genres); - - return string.Join(string.Empty, albumArtistNames.OrderBy(i => i).ToArray()).GetMD5(); - } - - public override Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) - { - var album = (MusicAlbum)item; - - var songs = album.RecursiveChildren.OfType<Audio>().ToList(); - - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - var name = songs.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i)); - - if (!string.IsNullOrEmpty(name)) - { - album.Name = name; - } - } - - if (!item.LockedFields.Contains(MetadataFields.Studios)) - { - album.Studios = songs.SelectMany(i => i.Studios) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - } - - if (!item.LockedFields.Contains(MetadataFields.Genres)) - { - album.Genres = songs.SelectMany(i => i.Genres) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - } - - album.AlbumArtist = songs - .Select(i => i.AlbumArtist) - .FirstOrDefault(i => !string.IsNullOrEmpty(i)); - - album.Artists = songs.SelectMany(i => i.Artists) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - var date = songs.Select(i => i.PremiereDate) - .FirstOrDefault(i => i.HasValue); - - if (date.HasValue) - { - album.PremiereDate = date.Value; - album.ProductionYear = date.Value.Year; - } - else - { - var year = songs.Select(i => i.ProductionYear ?? 1800).FirstOrDefault(i => i != 1800); - - if (year != 1800) - { - album.ProductionYear = year; - } - } - - providerInfo.FileStamp = GetComparisonData(songs); - - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - return TrueTaskResult; - } - - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Second; } - } - } -} diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs new file mode 100644 index 000000000..511fa0547 --- /dev/null +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -0,0 +1,188 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Providers.Manager; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.Music +{ + public class AlbumMetadataService : ConcreteMetadataService<MusicAlbum, AlbumId> + { + private readonly ILibraryManager _libraryManager; + + public AlbumMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, ILibraryManager libraryManager) + : base(serverConfigurationManager, logger, providerManager, providerRepo) + { + _libraryManager = libraryManager; + } + + /// <summary> + /// Merges the specified source. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="target">The target.</param> + /// <param name="lockedFields">The locked fields.</param> + /// <param name="replaceData">if set to <c>true</c> [replace data].</param> + /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param> + protected override void MergeData(MusicAlbum source, MusicAlbum target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + } + + protected override Task SaveItem(MusicAlbum item, ItemUpdateType reason, CancellationToken cancellationToken) + { + return _libraryManager.UpdateItem(item, reason, cancellationToken); + } + + protected override ItemUpdateType AfterMetadataRefresh(MusicAlbum item) + { + var updateType = base.AfterMetadataRefresh(item); + + var songs = item.RecursiveChildren.OfType<Audio>().ToList(); + + if (!item.LockedFields.Contains(MetadataFields.Genres)) + { + var currentList = item.Genres.ToList(); + + item.Genres = songs.SelectMany(i => i.Genres) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) + { + updateType = updateType | ItemUpdateType.MetadataDownload; + } + } + + if (!item.LockedFields.Contains(MetadataFields.Studios)) + { + var currentList = item.Studios.ToList(); + + item.Studios = songs.SelectMany(i => i.Studios) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + if (currentList.Count != item.Studios.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Studios.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) + { + updateType = updateType | ItemUpdateType.MetadataDownload; + } + } + + if (!item.LockedFields.Contains(MetadataFields.Name)) + { + var name = songs.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i)); + + if (!string.IsNullOrEmpty(name)) + { + if (!string.Equals(item.Name, name, StringComparison.Ordinal)) + { + item.Name = name; + updateType = updateType | ItemUpdateType.MetadataDownload; + } + } + } + + updateType = updateType | SetAlbumArtistFromSongs(item, songs); + updateType = updateType | SetArtistsFromSongs(item, songs); + updateType = updateType | SetDateFromSongs(item, songs); + + return updateType; + } + + protected override AlbumId GetId(MusicAlbum item) + { + var id = base.GetId(item); + + id.AlbumArtist = item.AlbumArtist; + + var artist = item.Parents.OfType<MusicArtist>().FirstOrDefault(); + + if (artist != null) + { + id.ArtistMusicBrainzId = artist.GetProviderId(MetadataProviders.Musicbrainz); + id.AlbumArtist = id.AlbumArtist ?? artist.Name; + } + + return id; + } + + private ItemUpdateType SetAlbumArtistFromSongs(MusicAlbum item, IEnumerable<Audio> songs) + { + var updateType = ItemUpdateType.Unspecified; + + var albumArtist = songs + .Select(i => i.AlbumArtist) + .FirstOrDefault(i => !string.IsNullOrEmpty(i)); + + if (!string.IsNullOrEmpty(albumArtist)) + { + if (!string.Equals(item.AlbumArtist, albumArtist, StringComparison.Ordinal)) + { + item.AlbumArtist = albumArtist; + updateType = updateType | ItemUpdateType.MetadataDownload; + } + } + + return updateType; + } + + private ItemUpdateType SetArtistsFromSongs(MusicAlbum item, IEnumerable<Audio> songs) + { + var updateType = ItemUpdateType.Unspecified; + + var currentList = item.Artists.ToList(); + + item.Artists = songs.SelectMany(i => i.Artists) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); + + if (currentList.Count != item.Artists.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Artists.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) + { + updateType = updateType | ItemUpdateType.MetadataDownload; + } + + return updateType; + } + + private ItemUpdateType SetDateFromSongs(MusicAlbum item, List<Audio> songs) + { + var updateType = ItemUpdateType.Unspecified; + + var date = songs.Select(i => i.PremiereDate) + .FirstOrDefault(i => i.HasValue); + + var originalPremiereDate = item.PremiereDate; + var originalProductionYear = item.ProductionYear; + + if (date.HasValue) + { + item.PremiereDate = date.Value; + item.ProductionYear = date.Value.Year; + } + else + { + var year = songs.Select(i => i.ProductionYear ?? 1800).FirstOrDefault(i => i != 1800); + + if (year != 1800) + { + item.ProductionYear = year; + } + } + + if ((originalPremiereDate ?? DateTime.MinValue) != (item.PremiereDate ?? DateTime.MinValue) || + (originalProductionYear ?? -1) != (item.ProductionYear ?? -1)) + { + updateType = updateType | ItemUpdateType.MetadataDownload; + } + + return updateType; + } + } +} diff --git a/MediaBrowser.Providers/Music/AlbumProviderFromXml.cs b/MediaBrowser.Providers/Music/AlbumProviderFromXml.cs deleted file mode 100644 index 32d45d1ae..000000000 --- a/MediaBrowser.Providers/Music/AlbumProviderFromXml.cs +++ /dev/null @@ -1,92 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Music -{ - class AlbumProviderFromXml : BaseMetadataProvider - { - private readonly IFileSystem _fileSystem; - - public AlbumProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) - : base(logManager, configurationManager) - { - _fileSystem = fileSystem; - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is MusicAlbum && item.LocationType == LocationType.FileSystem; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - - private const string XmlFileName = "album.xml"; - protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo) - { - var xml = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName)); - - if (xml == null) - { - return false; - } - - return _fileSystem.GetLastWriteTimeUtc(xml) > item.DateLastSaved; - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName)); - - if (metadataFile != null) - { - var path = metadataFile.FullName; - - await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - new BaseItemXmlParser<MusicAlbum>(Logger).Fetch((MusicAlbum)item, path, cancellationToken); - } - finally - { - XmlParsingResourcePool.Release(); - } - - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - return true; - } - - return false; - } - } -} diff --git a/MediaBrowser.Providers/Music/AlbumXmlProvider.cs b/MediaBrowser.Providers/Music/AlbumXmlProvider.cs new file mode 100644 index 000000000..66095fb7f --- /dev/null +++ b/MediaBrowser.Providers/Music/AlbumXmlProvider.cs @@ -0,0 +1,59 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.Music +{ + class AlbumXmlProvider : BaseXmlProvider, ILocalMetadataProvider<MusicAlbum> + { + private readonly ILogger _logger; + + public AlbumXmlProvider(IFileSystem fileSystem, ILogger logger) + : base(fileSystem) + { + _logger = logger; + } + + public async Task<MetadataResult<MusicAlbum>> GetMetadata(string path, CancellationToken cancellationToken) + { + path = GetXmlPath(path); + + var result = new MetadataResult<MusicAlbum>(); + + await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + var item = new MusicAlbum(); + + new BaseItemXmlParser<MusicAlbum>(_logger).Fetch(item, path, cancellationToken); + result.HasMetadata = true; + result.Item = item; + } + catch (FileNotFoundException) + { + result.HasMetadata = false; + } + finally + { + XmlParsingResourcePool.Release(); + } + + return result; + } + + public string Name + { + get { return "Media Browser Xml"; } + } + + protected override string GetXmlPath(string path) + { + return Path.Combine(path, "album.xml"); + } + } +} diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs index 31202feb2..1b598e596 100644 --- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs +++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs @@ -13,7 +13,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Music { - public class ArtistMetadataService : ConcreteMetadataService<MusicArtist> + public class ArtistMetadataService : ConcreteMetadataService<MusicArtist, ItemId> { private readonly ILibraryManager _libraryManager; @@ -49,13 +49,13 @@ namespace MediaBrowser.Providers.Music { var songs = item.RecursiveChildren.OfType<Audio>().ToList(); - var currentGenres = item.Genres.ToList(); + var currentList = item.Genres.ToList(); item.Genres = songs.SelectMany(i => i.Genres) .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); - if (currentGenres.Count != item.Genres.Count || !currentGenres.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) + if (currentList.Count != item.Genres.Count || !currentList.OrderBy(i => i).SequenceEqual(item.Genres.OrderBy(i => i), StringComparer.OrdinalIgnoreCase)) { updateType = updateType | ItemUpdateType.MetadataDownload; } diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs index e2dde6c4f..cd87d213b 100644 --- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs @@ -3,210 +3,373 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; 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 MediaBrowser.Model.Net; -using System.Net; +using System.Xml; namespace MediaBrowser.Providers.Music { - /// <summary> - /// Class FanArtAlbumProvider - /// </summary> - public class FanArtAlbumProvider : BaseMetadataProvider + public class FanartAlbumProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly IServerConfigurationManager _config; + private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; - /// <summary> - /// Initializes a new instance of the <see cref="FanArtAlbumProvider"/> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - public FanArtAlbumProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) - : base(logManager, configurationManager) + public FanartAlbumProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) { - _providerManager = providerManager; + _config = config; + _httpClient = httpClient; _fileSystem = fileSystem; - HttpClient = httpClient; } - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority + public string Name { - get { return MetadataProviderPriority.Fifth; } + get { return ProviderName; } } - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) + public static string ProviderName + { + get { return "FanArt"; } + } + + public bool Supports(IHasImages item) { return item is MusicAlbum; } - public override ItemUpdateType ItemUpdateType + public IEnumerable<ImageType> GetSupportedImages(IHasImages item) { - get + return new List<ImageType> { - return ItemUpdateType.ImageUpdate; - } + ImageType.Primary, + ImageType.Disc + }; } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange + + public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) { - get + var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); + + return images.Where(i => i.Type == imageType); + } + + public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken) + { + var album = (MusicAlbum)item; + + var list = new List<RemoteImageInfo>(); + + var artistMusicBrainzId = album.Parent.GetProviderId(MetadataProviders.Musicbrainz); + + if (!string.IsNullOrEmpty(artistMusicBrainzId)) { - return true; + await FanartArtistProvider.Current.EnsureMovieXml(artistMusicBrainzId, cancellationToken).ConfigureAwait(false); + + var artistXmlPath = FanartArtistProvider.GetArtistXmlPath(_config.CommonApplicationPaths, artistMusicBrainzId); + + var musicBrainzReleaseGroupId = album.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + var musicBrainzId = album.GetProviderId(MetadataProviders.Musicbrainz); + + try + { + AddImages(list, artistXmlPath, musicBrainzId, musicBrainzReleaseGroupId, cancellationToken); + } + catch (FileNotFoundException) + { + + } } + + var language = item.GetPreferredMetadataLanguage(); + + var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); + + // Sort first by width to prioritize HD versions + return 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); } /// <summary> - /// Gets the provider version. + /// Adds the images. /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion + /// <param name="list">The list.</param> + /// <param name="xmlPath">The XML path.</param> + /// <param name="releaseId">The release identifier.</param> + /// <param name="releaseGroupId">The release group identifier.</param> + /// <param name="cancellationToken">The cancellation token.</param> + private void AddImages(List<RemoteImageInfo> list, string xmlPath, string releaseId, string releaseGroupId, CancellationToken cancellationToken) { - get + using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) { - return "18"; + // 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; + } + } + } + } } } /// <summary> - /// Needses the refresh internal. + /// Adds the images from music node. /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) + /// <param name="list">The list.</param> + /// <param name="releaseId">The release identifier.</param> + /// <param name="releaseGroupId">The release group identifier.</param> + /// <param name="reader">The reader.</param> + /// <param name="cancellationToken">The cancellation token.</param> + private void AddImagesFromMusicNode(List<RemoteImageInfo> list, string releaseId, string releaseGroupId, XmlReader reader, CancellationToken cancellationToken) { - if (!ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && - !ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary) - { - return false; - } + reader.MoveToContent(); - if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Disc)) + while (reader.Read()) { - return false; + 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; + } + } + } } - - return base.NeedsRefreshInternal(item, providerInfo); } - protected override DateTime CompareDate(BaseItem item) + /// <summary> + /// Adds the images from albums node. + /// </summary> + /// <param name="list">The list.</param> + /// <param name="releaseId">The release identifier.</param> + /// <param name="releaseGroupId">The release group identifier.</param> + /// <param name="reader">The reader.</param> + /// <param name="cancellationToken">The cancellation token.</param> + private void AddImagesFromAlbumsNode(List<RemoteImageInfo> list, string releaseId, string releaseGroupId, XmlReader reader, CancellationToken cancellationToken) { - var artistMusicBrainzId = item.Parent.GetProviderId(MetadataProviders.Musicbrainz); + reader.MoveToContent(); - if (!string.IsNullOrEmpty(artistMusicBrainzId)) + while (reader.Read()) { - var artistXmlPath = FanartArtistProvider.GetArtistXmlPath(ConfigurationManager.CommonApplicationPaths, artistMusicBrainzId); - - var file = new FileInfo(artistXmlPath); - - if (file.Exists) + if (reader.NodeType == XmlNodeType.Element) { - return _fileSystem.GetLastWriteTimeUtc(file); + 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; + } + } } - } - - return base.CompareDate(item); + } } /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done + /// Adds the images. /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> + /// <param name="list">The list.</param> + /// <param name="reader">The reader.</param> /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) + private void AddImages(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken) { - if (!item.LockedFields.Contains(MetadataFields.Images)) + reader.MoveToContent(); + + while (reader.Read()) { - var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualFanartAlbumProvider.ProviderName).ConfigureAwait(false); - await FetchFromXml(item, images.ToList(), cancellationToken).ConfigureAwait(false); + 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; + } + } + } } - - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - - return true; } /// <summary> - /// Fetches from XML. + /// Adds the image. /// </summary> - /// <param name="item">The item.</param> - /// <param name="images">The images.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchFromXml(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken) + /// <param name="list">The list.</param> + /// <param name="reader">The reader.</param> + /// <param name="type">The type.</param> + /// <param name="width">The width.</param> + /// <param name="height">The height.</param> + private void AddImage(List<RemoteImageInfo> list, XmlReader reader, ImageType type, int width, int height) { - cancellationToken.ThrowIfCancellationRequested(); + var url = reader.GetAttribute("url"); + + var size = reader.GetAttribute("size"); - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary && !item.HasImage(ImageType.Primary)) + if (!string.IsNullOrEmpty(size)) { - await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false); + int sizeNum; + if (int.TryParse(size, NumberStyles.Any, _usCulture, out sizeNum)) + { + width = sizeNum; + height = sizeNum; + } } - cancellationToken.ThrowIfCancellationRequested(); + var likesString = reader.GetAttribute("likes"); + int likes; - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && !item.HasImage(ImageType.Disc)) + var info = new RemoteImageInfo { - await SaveImage(item, images, ImageType.Disc, cancellationToken).ConfigureAwait(false); + 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); } - private async Task SaveImage(BaseItem item, List<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken) + public int Order { - foreach (var image in images.Where(i => i.Type == type)) + get { return 0; } + } + + public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) + { + return _httpClient.GetResponse(new HttpRequestOptions { - try - { - await _providerManager.SaveImage(item, image.Url, FanartArtistProvider.FanArtResourcePool, type, null, cancellationToken).ConfigureAwait(false); - break; - } - catch (HttpException ex) - { - // Sometimes fanart has bad url's in their xml - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) - { - continue; - } - break; - } + CancellationToken = cancellationToken, + Url = url, + ResourcePool = FanartArtistProvider.FanArtResourcePool + }); + } + + public bool HasChanged(IHasMetadata item, DateTime date) + { + var album = (MusicAlbum)item; + + var artistMusicBrainzId = album.Parent.GetProviderId(MetadataProviders.Musicbrainz); + + if (!String.IsNullOrEmpty(artistMusicBrainzId)) + { + // Process images + var artistXmlPath = FanartArtistProvider.GetArtistXmlPath(_config.CommonApplicationPaths, artistMusicBrainzId); + + var fileInfo = new FileInfo(artistXmlPath); + + return fileInfo.Exists && _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; } + + return false; } } } diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index 35d27036d..2b4a814c5 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs @@ -20,7 +20,7 @@ using System.Xml; namespace MediaBrowser.Providers.Music { - public class FanartArtistProvider : IRemoteImageProvider, IHasChangeMonitor + public class FanartArtistProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder { internal static readonly SemaphoreSlim FanArtResourcePool = new SemaphoreSlim(3, 3); internal const string ApiKey = "5c6b04c68e904cfed1e6cbc9a9e683d4"; diff --git a/MediaBrowser.Providers/Music/FanArtUpdatesPrescanTask.cs b/MediaBrowser.Providers/Music/FanArtUpdatesPrescanTask.cs index c3a879f8c..d002c02f2 100644 --- a/MediaBrowser.Providers/Music/FanArtUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/Music/FanArtUpdatesPrescanTask.cs @@ -15,7 +15,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Music { - class FanArtUpdatesPrescanTask : ILibraryPostScanTask + class FanartUpdatesPrescanTask : ILibraryPostScanTask { private const string UpdatesUrl = "http://api.fanart.tv/webservice/newmusic/{0}/{1}/"; @@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Music private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public FanArtUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IFileSystem fileSystem) + public FanartUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _config = config; diff --git a/MediaBrowser.Providers/Music/LastFmImageProvider.cs b/MediaBrowser.Providers/Music/LastFmImageProvider.cs index 71278ce5c..659ae5bef 100644 --- a/MediaBrowser.Providers/Music/LastFmImageProvider.cs +++ b/MediaBrowser.Providers/Music/LastFmImageProvider.cs @@ -1,113 +1,165 @@ -using MediaBrowser.Controller.Configuration; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; 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.Linq; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Providers.Music { - /// <summary> - /// Class LastFmArtistImageProvider - /// </summary> - public class LastFmImageProvider : BaseMetadataProvider + public class LastfmImageProvider : IRemoteImageProvider, IHasOrder { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// Initializes a new instance of the <see cref="LastFmImageProvider"/> class. - /// </summary> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - public LastFmImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) : - base(logManager, configurationManager) + private readonly IHttpClient _httpClient; + private readonly IServerConfigurationManager _config; + + public LastfmImageProvider(IHttpClient httpClient, IServerConfigurationManager config) + { + _httpClient = httpClient; + _config = config; + } + + public string Name + { + get { return ProviderName; } + } + + public static string ProviderName { - _providerManager = providerManager; + get { return "last.fm"; } } - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) + public bool Supports(IHasImages item) { - return item is MusicAlbum; + return item is MusicAlbum || item is MusicArtist; } - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) + public IEnumerable<ImageType> GetSupportedImages(IHasImages item) { - if (item.HasImage(ImageType.Primary)) + return new List<ImageType> { - return false; - } + ImageType.Primary + }; + } + + public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) + { + var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); - return base.NeedsRefreshInternal(item, providerInfo); + return images.Where(i => i.Type == imageType); } - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) + public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken) { - if (!item.HasImage(ImageType.Primary)) + var list = new List<RemoteImageInfo>(); + + RemoteImageInfo info = null; + + var musicBrainzId = item.GetProviderId(MetadataProviders.Musicbrainz); + + if (!string.IsNullOrEmpty(musicBrainzId)) { - var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualLastFmImageProvider.ProviderName).ConfigureAwait(false); + var cachePath = Path.Combine(_config.ApplicationPaths.CachePath, "lastfm", musicBrainzId, "image.txt"); + + try + { + var parts = File.ReadAllText(cachePath).Split('|'); - await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false); + info = GetInfo(parts.FirstOrDefault(), parts.LastOrDefault()); + } + catch (DirectoryNotFoundException) + { + } + catch (FileNotFoundException) + { + } } - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); + if (info == null) + { + var musicBrainzReleaseGroupId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + if (!string.IsNullOrEmpty(musicBrainzReleaseGroupId)) + { + var cachePath = Path.Combine(_config.ApplicationPaths.CachePath, "lastfm", musicBrainzReleaseGroupId, "image.txt"); + + try + { + var parts = File.ReadAllText(cachePath).Split('|'); - return true; + info = GetInfo(parts.FirstOrDefault(), parts.LastOrDefault()); + } + catch (DirectoryNotFoundException) + { + } + catch (FileNotFoundException) + { + } + } + } + + if (info != null) + { + list.Add(info); + } + + // The only info we have is size + return Task.FromResult<IEnumerable<RemoteImageInfo>>(list.OrderByDescending(i => i.Width ?? 0)); } - private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken) + private RemoteImageInfo GetInfo(string url, string size) { - cancellationToken.ThrowIfCancellationRequested(); + if (string.IsNullOrEmpty(url)) + { + return null; + } - var configSetting = item is MusicAlbum - ? ConfigurationManager.Configuration.DownloadMusicAlbumImages - : ConfigurationManager.Configuration.DownloadMusicArtistImages; + var info = new RemoteImageInfo + { + ProviderName = Name, + Url = url, + Type = ImageType.Primary + }; - if (configSetting.Primary && !item.HasImage(ImageType.Primary) && !item.LockedFields.Contains(MetadataFields.Images)) + if (string.Equals(size, "mega", StringComparison.OrdinalIgnoreCase)) + { + + } + else if (string.Equals(size, "extralarge", StringComparison.OrdinalIgnoreCase)) + { + + } + else if (string.Equals(size, "large", StringComparison.OrdinalIgnoreCase)) + { + + } + else if (string.Equals(size, "medium", StringComparison.OrdinalIgnoreCase)) { - var image = images.FirstOrDefault(i => i.Type == ImageType.Primary); - if (image != null) - { - await _providerManager.SaveImage(item, image.Url, LastFmArtistProvider.LastfmResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false); - } } + + return info; + } + + public int Order + { + get { return 1; } } - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority + public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) { - get { return MetadataProviderPriority.Fifth; } + return _httpClient.GetResponse(new HttpRequestOptions + { + CancellationToken = cancellationToken, + Url = url, + ResourcePool = LastfmArtistProvider.LastfmResourcePool + }); } } } diff --git a/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs b/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs index 5cfcf054d..f9d7d3018 100644 --- a/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmAlbumProvider.cs @@ -1,106 +1,51 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; -using MoreLinq; using System; +using System.Collections.Generic; using System.IO; -using System.Linq; +using System.Net; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Providers.Music { - public class LastfmAlbumProvider : LastfmBaseProvider + public class LastfmAlbumProvider : IRemoteMetadataProvider<MusicAlbum>, IHasOrder { - internal static LastfmAlbumProvider Current; + private readonly IJsonSerializer _json; + private readonly IHttpClient _httpClient; - public LastfmAlbumProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager) - : base(jsonSerializer, httpClient, logManager, configurationManager) - { - Current = this; - } + private readonly IServerConfigurationManager _config; + private readonly ILogger _logger; - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority + public LastfmAlbumProvider(IHttpClient httpClient, IJsonSerializer json, IServerConfigurationManager config, ILogger logger) { - get { return MetadataProviderPriority.Fourth; } + _httpClient = httpClient; + _json = json; + _config = config; + _logger = logger; } - protected override string ProviderVersion + public async Task<MetadataResult<MusicAlbum>> GetMetadata(ItemId id, CancellationToken cancellationToken) { - get - { - return "9"; - } - } + var result = new MetadataResult<MusicAlbum>(); - private bool HasAltMeta(BaseItem item) - { - return item.LocationType == LocationType.FileSystem && item.ResolveArgs.ContainsMetaFileByName("album.xml"); - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - var hasId = !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz)) && - !string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup)); + var lastFmData = await GetAlbumResult((AlbumId)id, cancellationToken).ConfigureAwait(false); - if (hasId && HasAltMeta(item)) + if (lastFmData != null && lastFmData.album != null) { - return false; + result.HasMetadata = true; + ProcessAlbumData(result.Item, lastFmData.album); } - // If song metadata has changed and we don't have an mbid, refresh - if (!hasId && GetComparisonData(item as MusicAlbum) != providerInfo.FileStamp) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); + return result; } - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var album = (MusicAlbum)item; - - var result = await GetAlbumResult(album, cancellationToken).ConfigureAwait(false); - - if (result != null && result.album != null) - { - LastfmHelper.ProcessAlbumData(item, result.album); - } - - providerInfo.FileStamp = GetComparisonData(album); - - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - return true; - } - - - private async Task<LastfmGetAlbumResult> GetAlbumResult(MusicAlbum item, CancellationToken cancellationToken) + private async Task<LastfmGetAlbumResult> GetAlbumResult(AlbumId item, CancellationToken cancellationToken) { // Try album release Id if (!string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz))) @@ -123,33 +68,37 @@ namespace MediaBrowser.Providers.Music return result; } } - - // Get each song, distinct by the combination of AlbumArtist and Album - var songs = item.RecursiveChildren.OfType<Audio>().DistinctBy(i => (i.AlbumArtist ?? string.Empty) + (i.Album ?? string.Empty), StringComparer.OrdinalIgnoreCase).ToList(); - foreach (var song in songs.Where(song => !string.IsNullOrEmpty(song.Album) && !string.IsNullOrEmpty(song.AlbumArtist))) - { - var result = await GetAlbumResult(song.AlbumArtist, song.Album, cancellationToken).ConfigureAwait(false); + //// Get each song, distinct by the combination of AlbumArtist and Album + //var songs = item.RecursiveChildren.OfType<Audio>().DistinctBy(i => (i.AlbumArtist ?? string.Empty) + (i.Album ?? string.Empty), StringComparer.OrdinalIgnoreCase).ToList(); - if (result != null && result.album != null) - { - return result; - } + //foreach (var song in songs.Where(song => !string.IsNullOrEmpty(song.Album) && !string.IsNullOrEmpty(song.AlbumArtist))) + //{ + // var result = await GetAlbumResult(song.AlbumArtist, song.Album, cancellationToken).ConfigureAwait(false); + + // if (result != null && result.album != null) + // { + // return result; + // } + //} + + if (string.IsNullOrEmpty(item.AlbumArtist)) + { + return null; } - // Try the folder name - return await GetAlbumResult(item.Parent.Name, item.Name, cancellationToken); + return await GetAlbumResult(item.AlbumArtist, item.Name, cancellationToken); } private async Task<LastfmGetAlbumResult> GetAlbumResult(string artist, string album, CancellationToken cancellationToken) { // Get albu info using artist and album name - var url = LastFmArtistProvider.RootUrl + string.Format("method=album.getInfo&artist={0}&album={1}&api_key={2}&format=json", UrlEncode(artist), UrlEncode(album), LastFmArtistProvider.ApiKey); + var url = LastfmArtistProvider.RootUrl + string.Format("method=album.getInfo&artist={0}&album={1}&api_key={2}&format=json", UrlEncode(artist), UrlEncode(album), LastfmArtistProvider.ApiKey); - using (var json = await HttpClient.Get(new HttpRequestOptions + using (var json = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = LastFmArtistProvider.LastfmResourcePool, + ResourcePool = LastfmArtistProvider.LastfmResourcePool, CancellationToken = cancellationToken, EnableHttpCompression = false @@ -162,7 +111,7 @@ namespace MediaBrowser.Providers.Music // Fix their bad json jsonText = jsonText.Replace("\"#text\"", "\"url\""); - return JsonSerializer.DeserializeFromString<LastfmGetAlbumResult>(jsonText); + return _json.DeserializeFromString<LastfmGetAlbumResult>(jsonText); } } } @@ -170,48 +119,180 @@ namespace MediaBrowser.Providers.Music private async Task<LastfmGetAlbumResult> GetAlbumResult(string musicbraizId, CancellationToken cancellationToken) { // Get albu info using artist and album name - var url = LastFmArtistProvider.RootUrl + string.Format("method=album.getInfo&mbid={0}&api_key={1}&format=json", musicbraizId, LastFmArtistProvider.ApiKey); + var url = LastfmArtistProvider.RootUrl + string.Format("method=album.getInfo&mbid={0}&api_key={1}&format=json", musicbraizId, LastfmArtistProvider.ApiKey); - using (var json = await HttpClient.Get(new HttpRequestOptions + using (var json = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = LastFmArtistProvider.LastfmResourcePool, + ResourcePool = LastfmArtistProvider.LastfmResourcePool, CancellationToken = cancellationToken, EnableHttpCompression = false }).ConfigureAwait(false)) { - return JsonSerializer.DeserializeFromStream<LastfmGetAlbumResult>(json); + return _json.DeserializeFromStream<LastfmGetAlbumResult>(json); } } - public override bool Supports(BaseItem item) + private void ProcessAlbumData(MusicAlbum item, LastfmAlbum data) { - return item is MusicAlbum; + var overview = data.wiki != null ? data.wiki.content : null; + + if (!item.LockedFields.Contains(MetadataFields.Overview)) + { + item.Overview = overview; + } + + // Only grab the date here if the album doesn't already have one, since id3 tags are preferred + DateTime release; + + if (DateTime.TryParse(data.releasedate, out release)) + { + // Lastfm sends back null as sometimes 1901, other times 0 + if (release.Year > 1901) + { + if (!item.PremiereDate.HasValue) + { + item.PremiereDate = release; + } + + if (!item.ProductionYear.HasValue) + { + item.ProductionYear = release.Year; + } + } + } + + string imageSize; + var url = LastfmHelper.GetImageUrl(data, out imageSize); + + var musicBrainzId = item.GetProviderId(MetadataProviders.Musicbrainz) ?? + item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + + LastfmHelper.SaveImageInfo(_config.ApplicationPaths, _logger, musicBrainzId, url, imageSize); } /// <summary> - /// Gets the data. + /// Encodes an URL. /// </summary> - /// <param name="album">The album.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(MusicAlbum album) + /// <param name="name">The name.</param> + /// <returns>System.String.</returns> + private string UrlEncode(string name) + { + return WebUtility.UrlEncode(name); + } + + public string Name { - var songs = album.RecursiveChildren.OfType<Audio>().ToList(); + get { return "last.fm"; } + } - var albumArtists = songs.Select(i => i.AlbumArtist) - .Where(i => !string.IsNullOrEmpty(i)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); + public int Order + { + get { return 1; } + } + } - var albumNames = songs.Select(i => i.AlbumArtist) - .Where(i => !string.IsNullOrEmpty(i)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); + #region Result Objects - albumArtists.AddRange(albumNames); + public class LastfmStats + { + public string listeners { get; set; } + public string playcount { get; set; } + } - return string.Join(string.Empty, albumArtists.OrderBy(i => i).ToArray()).GetMD5(); - } + public class LastfmTag + { + public string name { get; set; } + public string url { get; set; } } + + + public class LastfmTags + { + public List<LastfmTag> tag { get; set; } + } + + public class LastfmFormationInfo + { + public string yearfrom { get; set; } + public string yearto { get; set; } + } + + public class LastFmBio + { + public string published { get; set; } + public string summary { get; set; } + public string content { get; set; } + public string placeformed { get; set; } + public string yearformed { get; set; } + public List<LastfmFormationInfo> formationlist { get; set; } + } + + public class LastFmImage + { + public string url { get; set; } + public string size { get; set; } + } + + public class LastfmArtist : IHasLastFmImages + { + public string name { get; set; } + public string mbid { get; set; } + public string url { get; set; } + public string streamable { get; set; } + public string ontour { get; set; } + public LastfmStats stats { get; set; } + public List<LastfmArtist> similar { get; set; } + public LastfmTags tags { get; set; } + public LastFmBio bio { get; set; } + public List<LastFmImage> image { get; set; } + } + + + public class LastfmAlbum : IHasLastFmImages + { + public string name { get; set; } + public string artist { get; set; } + public string id { get; set; } + public string mbid { get; set; } + public string releasedate { get; set; } + public int listeners { get; set; } + public int playcount { get; set; } + public LastfmTags toptags { get; set; } + public LastFmBio wiki { get; set; } + public List<LastFmImage> image { get; set; } + } + + public interface IHasLastFmImages + { + List<LastFmImage> image { get; set; } + } + + public class LastfmGetAlbumResult + { + public LastfmAlbum album { get; set; } + } + + public class LastfmGetArtistResult + { + public LastfmArtist artist { get; set; } + } + + public class Artistmatches + { + public List<LastfmArtist> artist { get; set; } + } + + public class LastfmArtistSearchResult + { + public Artistmatches artistmatches { get; set; } + } + + public class LastfmArtistSearchResults + { + public LastfmArtistSearchResult results { get; set; } + } + + #endregion } diff --git a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs index 488340cf6..2924180ff 100644 --- a/MediaBrowser.Providers/Music/LastfmArtistProvider.cs +++ b/MediaBrowser.Providers/Music/LastfmArtistProvider.cs @@ -19,7 +19,7 @@ using System.Xml; namespace MediaBrowser.Providers.Music { - public class LastFmArtistProvider : IRemoteMetadataProvider<MusicArtist> + public class LastfmArtistProvider : IRemoteMetadataProvider<MusicArtist> { private readonly IJsonSerializer _json; private readonly IHttpClient _httpClient; @@ -30,12 +30,14 @@ namespace MediaBrowser.Providers.Music internal static string ApiKey = "7b76553c3eb1d341d642755aecc40a33"; private readonly IServerConfigurationManager _config; - private ILogger _logger; + private readonly ILogger _logger; - public LastFmArtistProvider(IHttpClient httpClient, IJsonSerializer json) + public LastfmArtistProvider(IHttpClient httpClient, IJsonSerializer json, IServerConfigurationManager config, ILogger logger) { _httpClient = httpClient; _json = json; + _config = config; + _logger = logger; } public async Task<MetadataResult<MusicArtist>> GetMetadata(ItemId id, CancellationToken cancellationToken) @@ -119,25 +121,7 @@ namespace MediaBrowser.Providers.Music string imageSize; var url = LastfmHelper.GetImageUrl(data, out imageSize); - var cachePath = Path.Combine(_config.ApplicationPaths.CachePath, "lastfm", musicBrainzId, "image.txt"); - - try - { - if (string.IsNullOrEmpty(url)) - { - File.Delete(cachePath); - } - else - { - Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); - File.WriteAllText(cachePath, url + "|" + imageSize); - } - } - catch (IOException ex) - { - // Don't fail if this is unable to write - _logger.ErrorException("Error saving to {0}", ex, cachePath); - } + LastfmHelper.SaveImageInfo(_config.ApplicationPaths, _logger, musicBrainzId, url, imageSize); } private async Task<string> FindId(ItemId item, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/Music/LastfmBaseProvider.cs b/MediaBrowser.Providers/Music/LastfmBaseProvider.cs deleted file mode 100644 index 7ff7bf407..000000000 --- a/MediaBrowser.Providers/Music/LastfmBaseProvider.cs +++ /dev/null @@ -1,194 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; - -namespace MediaBrowser.Providers.Music -{ - /// <summary> - /// Class MovieDbProvider - /// </summary> - public abstract class LastfmBaseProvider : BaseMetadataProvider - { - /// <summary> - /// Initializes a new instance of the <see cref="LastfmBaseProvider" /> class. - /// </summary> - /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <exception cref="System.ArgumentNullException">jsonSerializer</exception> - protected LastfmBaseProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - if (jsonSerializer == null) - { - throw new ArgumentNullException("jsonSerializer"); - } - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - JsonSerializer = jsonSerializer; - HttpClient = httpClient; - } - - protected override string ProviderVersion - { - get - { - return "8"; - } - } - - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the json serializer. - /// </summary> - /// <value>The json serializer.</value> - protected IJsonSerializer JsonSerializer { get; private set; } - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - /// <summary> - /// Encodes an URL. - /// </summary> - /// <param name="name">The name.</param> - /// <returns>System.String.</returns> - protected static string UrlEncode(string name) - { - return WebUtility.UrlEncode(name); - } - } - - #region Result Objects - - public class LastfmStats - { - public string listeners { get; set; } - public string playcount { get; set; } - } - - public class LastfmTag - { - public string name { get; set; } - public string url { get; set; } - } - - - public class LastfmTags - { - public List<LastfmTag> tag { get; set; } - } - - public class LastfmFormationInfo - { - public string yearfrom { get; set; } - public string yearto { get; set; } - } - - public class LastFmBio - { - public string published { get; set; } - public string summary { get; set; } - public string content { get; set; } - public string placeformed { get; set; } - public string yearformed { get; set; } - public List<LastfmFormationInfo> formationlist { get; set; } - } - - public class LastFmImage - { - public string url { get; set; } - public string size { get; set; } - } - - public class LastfmArtist : IHasLastFmImages - { - public string name { get; set; } - public string mbid { get; set; } - public string url { get; set; } - public string streamable { get; set; } - public string ontour { get; set; } - public LastfmStats stats { get; set; } - public List<LastfmArtist> similar { get; set; } - public LastfmTags tags { get; set; } - public LastFmBio bio { get; set; } - public List<LastFmImage> image { get; set; } - } - - - public class LastfmAlbum : IHasLastFmImages - { - public string name { get; set; } - public string artist { get; set; } - public string id { get; set; } - public string mbid { get; set; } - public string releasedate { get; set; } - public int listeners { get; set; } - public int playcount { get; set; } - public LastfmTags toptags { get; set; } - public LastFmBio wiki { get; set; } - public List<LastFmImage> image { get; set; } - } - - public interface IHasLastFmImages - { - List<LastFmImage> image { get; set; } - } - - public class LastfmGetAlbumResult - { - public LastfmAlbum album { get; set; } - } - - public class LastfmGetArtistResult - { - public LastfmArtist artist { get; set; } - } - - public class Artistmatches - { - public List<LastfmArtist> artist { get; set; } - } - - public class LastfmArtistSearchResult - { - public Artistmatches artistmatches { get; set; } - } - - public class LastfmArtistSearchResults - { - public LastfmArtistSearchResult results { get; set; } - } - - #endregion -} diff --git a/MediaBrowser.Providers/Music/LastfmHelper.cs b/MediaBrowser.Providers/Music/LastfmHelper.cs index 0895d245c..0c67e7ea7 100644 --- a/MediaBrowser.Providers/Music/LastfmHelper.cs +++ b/MediaBrowser.Providers/Music/LastfmHelper.cs @@ -1,7 +1,7 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Entities; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Logging; using System; +using System.IO; using System.Linq; namespace MediaBrowser.Providers.Music @@ -37,41 +37,27 @@ namespace MediaBrowser.Providers.Music return null; } - public static void ProcessAlbumData(BaseItem item, LastfmAlbum data) + public static void SaveImageInfo(IApplicationPaths appPaths, ILogger logger, string musicBrainzId, string url, string size) { - var overview = data.wiki != null ? data.wiki.content : null; + var cachePath = Path.Combine(appPaths.CachePath, "lastfm", musicBrainzId, "image.txt"); - if (!item.LockedFields.Contains(MetadataFields.Overview)) + try { - item.Overview = overview; - } - - // Only grab the date here if the album doesn't already have one, since id3 tags are preferred - DateTime release; - - if (DateTime.TryParse(data.releasedate, out release)) - { - // Lastfm sends back null as sometimes 1901, other times 0 - if (release.Year > 1901) + if (string.IsNullOrEmpty(url)) { - if (!item.PremiereDate.HasValue) - { - item.PremiereDate = release; - } - - if (!item.ProductionYear.HasValue) - { - item.ProductionYear = release.Year; - } + File.Delete(cachePath); + } + else + { + Directory.CreateDirectory(Path.GetDirectoryName(cachePath)); + File.WriteAllText(cachePath, url + "|" + size); } } - - var album = (MusicAlbum)item; - - string imageSize; - - album.LastFmImageUrl = GetImageUrl(data, out imageSize); - album.LastFmImageSize = imageSize; + catch (IOException ex) + { + // Don't fail if this is unable to write + logger.ErrorException("Error saving to {0}", ex, cachePath); + } } } } diff --git a/MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs b/MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs deleted file mode 100644 index f1dda948f..000000000 --- a/MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs +++ /dev/null @@ -1,354 +0,0 @@ -using MediaBrowser.Common.Net; -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 : IRemoteImageProvider - { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; - - public ManualFanartAlbumProvider(IServerConfigurationManager config, IHttpClient httpClient) - { - _config = config; - _httpClient = httpClient; - } - - public string Name - { - get { return ProviderName; } - } - - public static string ProviderName - { - get { return "FanArt"; } - } - - public bool Supports(IHasImages item) - { - return item is MusicAlbum; - } - - public IEnumerable<ImageType> GetSupportedImages(IHasImages item) - { - return new List<ImageType> - { - ImageType.Primary, - ImageType.Disc - }; - } - - public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) - { - var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); - - return images.Where(i => i.Type == imageType); - } - - public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken) - { - var album = (MusicAlbum)item; - - var list = new List<RemoteImageInfo>(); - - var artistMusicBrainzId = album.Parent.GetProviderId(MetadataProviders.Musicbrainz); - - if (!string.IsNullOrEmpty(artistMusicBrainzId)) - { - var artistXmlPath = FanartArtistProvider.GetArtistXmlPath(_config.CommonApplicationPaths, artistMusicBrainzId); - - var musicBrainzReleaseGroupId = album.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); - - var musicBrainzId = album.GetProviderId(MetadataProviders.Musicbrainz); - - try - { - AddImages(list, artistXmlPath, musicBrainzId, musicBrainzReleaseGroupId, cancellationToken); - } - catch (FileNotFoundException) - { - - } - } - - var language = item.GetPreferredMetadataLanguage(); - - 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<IEnumerable<RemoteImageInfo>>(list); - } - - /// <summary> - /// Adds the images. - /// </summary> - /// <param name="list">The list.</param> - /// <param name="xmlPath">The XML path.</param> - /// <param name="releaseId">The release identifier.</param> - /// <param name="releaseGroupId">The release group identifier.</param> - /// <param name="cancellationToken">The cancellation token.</param> - private void AddImages(List<RemoteImageInfo> 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; - } - } - } - } - } - } - - /// <summary> - /// Adds the images from music node. - /// </summary> - /// <param name="list">The list.</param> - /// <param name="releaseId">The release identifier.</param> - /// <param name="releaseGroupId">The release group identifier.</param> - /// <param name="reader">The reader.</param> - /// <param name="cancellationToken">The cancellation token.</param> - private void AddImagesFromMusicNode(List<RemoteImageInfo> 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; - } - } - } - } - } - - /// <summary> - /// Adds the images from albums node. - /// </summary> - /// <param name="list">The list.</param> - /// <param name="releaseId">The release identifier.</param> - /// <param name="releaseGroupId">The release group identifier.</param> - /// <param name="reader">The reader.</param> - /// <param name="cancellationToken">The cancellation token.</param> - private void AddImagesFromAlbumsNode(List<RemoteImageInfo> 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; - } - } - } - } - } - - /// <summary> - /// Adds the images. - /// </summary> - /// <param name="list">The list.</param> - /// <param name="reader">The reader.</param> - /// <param name="cancellationToken">The cancellation token.</param> - private void AddImages(List<RemoteImageInfo> 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; - } - } - } - } - } - - /// <summary> - /// Adds the image. - /// </summary> - /// <param name="list">The list.</param> - /// <param name="reader">The reader.</param> - /// <param name="type">The type.</param> - /// <param name="width">The width.</param> - /// <param name="height">The height.</param> - private void AddImage(List<RemoteImageInfo> 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 Order - { - get { return 0; } - } - - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - ResourcePool = FanartArtistProvider.FanArtResourcePool - }); - } - } -} diff --git a/MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs b/MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs deleted file mode 100644 index b1e9fa9b3..000000000 --- a/MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs +++ /dev/null @@ -1,150 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -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.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Music -{ - public class ManualLastFmImageProvider : IRemoteImageProvider - { - private readonly IHttpClient _httpClient; - private readonly IServerConfigurationManager _config; - - public ManualLastFmImageProvider(IHttpClient httpClient, IServerConfigurationManager config) - { - _httpClient = httpClient; - _config = config; - } - - public string Name - { - get { return ProviderName; } - } - - public static string ProviderName - { - get { return "last.fm"; } - } - - public bool Supports(IHasImages item) - { - return item is MusicAlbum || item is MusicArtist; - } - - public IEnumerable<ImageType> GetSupportedImages(IHasImages item) - { - return new List<ImageType> - { - ImageType.Primary - }; - } - - public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) - { - var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); - - return images.Where(i => i.Type == imageType); - } - - public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken) - { - var list = new List<RemoteImageInfo>(); - - RemoteImageInfo info = null; - - var musicBrainzId = item.GetProviderId(MetadataProviders.Musicbrainz); - - var album = item as MusicAlbum; - if (album != null) - { - info = GetInfo(album.LastFmImageUrl, album.LastFmImageSize); - } - - var musicArtist = item as MusicArtist; - if (musicArtist != null && !string.IsNullOrEmpty(musicBrainzId)) - { - var cachePath = Path.Combine(_config.ApplicationPaths.CachePath, "lastfm", musicBrainzId, "image.txt"); - - try - { - var parts = File.ReadAllText(cachePath).Split('|'); - - info = GetInfo(parts.FirstOrDefault(), parts.LastOrDefault()); - } - catch (DirectoryNotFoundException ex) - { - } - catch (FileNotFoundException ex) - { - } - - } - - if (info != null) - { - list.Add(info); - } - - // The only info we have is size - return Task.FromResult<IEnumerable<RemoteImageInfo>>(list.OrderByDescending(i => i.Width ?? 0)); - } - - private RemoteImageInfo GetInfo(string url, string size) - { - if (string.IsNullOrEmpty(url)) - { - return null; - } - - var info = new RemoteImageInfo - { - ProviderName = Name, - Url = url, - Type = ImageType.Primary - }; - - if (string.Equals(size, "mega", StringComparison.OrdinalIgnoreCase)) - { - - } - else if (string.Equals(size, "extralarge", StringComparison.OrdinalIgnoreCase)) - { - - } - else if (string.Equals(size, "large", StringComparison.OrdinalIgnoreCase)) - { - - } - else if (string.Equals(size, "medium", StringComparison.OrdinalIgnoreCase)) - { - - } - - return info; - } - - public int Order - { - get { return 1; } - } - - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - ResourcePool = LastFmArtistProvider.LastfmResourcePool - }); - } - } -} diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index 29edac6b0..3f7c0ce96 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs @@ -1,11 +1,8 @@ using MediaBrowser.Common; using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; using System; using System.IO; using System.Net; @@ -16,46 +13,46 @@ using System.Xml; namespace MediaBrowser.Providers.Music { - public class MusicBrainzAlbumProvider : BaseMetadataProvider + public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum>, IHasOrder { internal static MusicBrainzAlbumProvider Current; private readonly IHttpClient _httpClient; private readonly IApplicationHost _appHost; - public MusicBrainzAlbumProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IHttpClient httpClient, IApplicationHost appHost) - : base(logManager, configurationManager) + public MusicBrainzAlbumProvider(IHttpClient httpClient, IApplicationHost appHost) { _httpClient = httpClient; _appHost = appHost; - Current = this; } - public override bool Supports(BaseItem item) + public async Task<MetadataResult<MusicAlbum>> GetMetadata(ItemId id, CancellationToken cancellationToken) { - return item is MusicAlbum; - } + var albumId = (AlbumId)id; + var releaseId = albumId.GetProviderId(MetadataProviders.Musicbrainz); + var releaseGroupId = albumId.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); - public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) - { - var releaseId = item.GetProviderId(MetadataProviders.Musicbrainz); - var releaseGroupId = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); + var result = new MetadataResult<MusicAlbum>(); if (string.IsNullOrEmpty(releaseId)) { - var result = await GetReleaseResult((MusicAlbum)item, cancellationToken).ConfigureAwait(false); + var releaseResult = await GetReleaseResult(albumId.ArtistMusicBrainzId, albumId.AlbumArtist, albumId.Name, cancellationToken).ConfigureAwait(false); - if (!string.IsNullOrEmpty(result.ReleaseId)) + result.Item = new MusicAlbum(); + + if (!string.IsNullOrEmpty(releaseResult.ReleaseId)) { - releaseId = result.ReleaseId; - item.SetProviderId(MetadataProviders.Musicbrainz, releaseId); + releaseId = releaseResult.ReleaseId; + result.HasMetadata = true; + result.Item.SetProviderId(MetadataProviders.Musicbrainz, releaseId); } - if (!string.IsNullOrEmpty(result.ReleaseGroupId)) + if (!string.IsNullOrEmpty(releaseResult.ReleaseGroupId)) { - releaseGroupId = result.ReleaseGroupId; - item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, releaseGroupId); + releaseGroupId = releaseResult.ReleaseGroupId; + result.HasMetadata = true; + result.Item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, releaseGroupId); } } @@ -63,25 +60,26 @@ namespace MediaBrowser.Providers.Music if (!string.IsNullOrEmpty(releaseId) && string.IsNullOrEmpty(releaseGroupId)) { releaseGroupId = await GetReleaseGroupId(releaseId, cancellationToken).ConfigureAwait(false); - - item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, releaseGroupId); + result.HasMetadata = true; + result.Item.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, releaseGroupId); } - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - return true; + return result; } - private Task<ReleaseResult> GetReleaseResult(MusicAlbum album, CancellationToken cancellationToken) + public string Name { - var artist = album.Parent; - var artistId = artist.GetProviderId(MetadataProviders.Musicbrainz); + get { return "MusicBrainz"; } + } - if (!string.IsNullOrEmpty(artistId)) + private Task<ReleaseResult> GetReleaseResult(string artistMusicBrainId, string artistName, string albumName, CancellationToken cancellationToken) + { + if (!string.IsNullOrEmpty(artistMusicBrainId)) { - return GetReleaseResult(album.Name, artistId, cancellationToken); + return GetReleaseResult(albumName, artistMusicBrainId, cancellationToken); } - return GetReleaseResultByArtistName(album.Name, artist.Name, cancellationToken); + return GetReleaseResultByArtistName(albumName, artistName, cancellationToken); } private async Task<ReleaseResult> GetReleaseResult(string albumName, string artistId, CancellationToken cancellationToken) @@ -218,10 +216,9 @@ namespace MediaBrowser.Providers.Music } } - - public override MetadataProviderPriority Priority + public int Order { - get { return MetadataProviderPriority.Third; } + get { return 0; } } } } diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs index aba60e7ba..b252970cb 100644 --- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs +++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.MusicGenres { - public class MusicGenreMetadataService : ConcreteMetadataService<MusicGenre> + public class MusicGenreMetadataService : ConcreteMetadataService<MusicGenre, ItemId> { private readonly ILibraryManager _libraryManager; diff --git a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs index 2b4e43f3c..3f8278bda 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs @@ -14,7 +14,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.People { - public class MovieDbPersonImageProvider : IRemoteImageProvider + public class MovieDbPersonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; private readonly IJsonSerializer _jsonSerializer; diff --git a/MediaBrowser.Providers/People/PersonMetadataService.cs b/MediaBrowser.Providers/People/PersonMetadataService.cs index fe17f67b1..f39ae68bd 100644 --- a/MediaBrowser.Providers/People/PersonMetadataService.cs +++ b/MediaBrowser.Providers/People/PersonMetadataService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.People { - public class PersonMetadataService : ConcreteMetadataService<Person> + public class PersonMetadataService : ConcreteMetadataService<Person, ItemId> { private readonly ILibraryManager _libraryManager; diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index aa4b9e0d8..769f07788 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -18,7 +18,7 @@ using System.Xml; namespace MediaBrowser.Providers.People { - public class TvdbPersonImageProvider : IRemoteImageProvider + public class TvdbPersonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; private readonly ILibraryManager _library; diff --git a/MediaBrowser.Providers/Studios/StudioMetadataService.cs b/MediaBrowser.Providers/Studios/StudioMetadataService.cs index f27a5c3b0..78ef1b51c 100644 --- a/MediaBrowser.Providers/Studios/StudioMetadataService.cs +++ b/MediaBrowser.Providers/Studios/StudioMetadataService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Studios { - public class StudioMetadataService : ConcreteMetadataService<Studio> + public class StudioMetadataService : ConcreteMetadataService<Studio, ItemId> { private readonly ILibraryManager _libraryManager; diff --git a/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs b/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs index 0c5b3fabc..6b005c9dc 100644 --- a/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs +++ b/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs @@ -127,7 +127,7 @@ namespace MediaBrowser.Providers.TV var existingDictionary = existingSeriesIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); - var updates = _jsonSerializer.DeserializeFromString<List<FanArtUpdatesPrescanTask.FanArtUpdate>>(json); + var updates = _jsonSerializer.DeserializeFromString<List<FanartUpdatesPrescanTask.FanArtUpdate>>(json); return updates.Select(i => i.id).Where(existingDictionary.ContainsKey); } diff --git a/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs b/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs index 6fdd7d094..c7b2f595b 100644 --- a/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs @@ -19,7 +19,7 @@ using MediaBrowser.Providers.Music; namespace MediaBrowser.Providers.TV { - public class ManualFanartSeasonImageProvider : IRemoteImageProvider + public class ManualFanartSeasonImageProvider : IRemoteImageProvider, IHasOrder { private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/TV/ManualFanartSeriesProvider.cs b/MediaBrowser.Providers/TV/ManualFanartSeriesProvider.cs index f7e19a6c1..9e492d8ea 100644 --- a/MediaBrowser.Providers/TV/ManualFanartSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/ManualFanartSeriesProvider.cs @@ -19,7 +19,7 @@ using MediaBrowser.Providers.Music; namespace MediaBrowser.Providers.TV { - public class ManualFanartSeriesImageProvider : IRemoteImageProvider + public class ManualFanartSeriesImageProvider : IRemoteImageProvider, IHasOrder { private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IServerConfigurationManager _config; diff --git a/MediaBrowser.Providers/TV/ManualTvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/ManualTvdbSeasonImageProvider.cs index f672942db..458482bf6 100644 --- a/MediaBrowser.Providers/TV/ManualTvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/ManualTvdbSeasonImageProvider.cs @@ -19,7 +19,7 @@ using System.Xml; namespace MediaBrowser.Providers.TV { - public class ManualTvdbSeasonImageProvider : IRemoteImageProvider + public class ManualTvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); diff --git a/MediaBrowser.Providers/TV/ManualTvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/ManualTvdbSeriesImageProvider.cs index a1c7114fc..0cc2d8899 100644 --- a/MediaBrowser.Providers/TV/ManualTvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/ManualTvdbSeriesImageProvider.cs @@ -19,7 +19,7 @@ using System.Xml; namespace MediaBrowser.Providers.TV { - public class ManualTvdbSeriesImageProvider : IRemoteImageProvider + public class ManualTvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder { private readonly IServerConfigurationManager _config; private readonly IHttpClient _httpClient; diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs index f2b2da087..3a7abd79b 100644 --- a/MediaBrowser.Providers/Users/UserMetadataService.cs +++ b/MediaBrowser.Providers/Users/UserMetadataService.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Users { - public class UserMetadataService : ConcreteMetadataService<User> + public class UserMetadataService : ConcreteMetadataService<User, ItemId> { private readonly IUserManager _userManager; |
