convert album providers

pull/702/head
Luke Pulverenti 10 years ago
parent 9685b81db5
commit 67fde8c16d

@ -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>

@ -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" />

@ -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; }
}
}

@ -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);
}
}

@ -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
{

@ -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.

@ -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);
}
}

@ -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);
}
}

@ -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; }
}
}

@ -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;

@ -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;

@ -14,7 +14,7 @@ using System.Threading.Tasks;
namespace MediaBrowser.Providers.BoxSets
{
class MovieDbBoxSetImageProvider : IRemoteImageProvider
class MovieDbBoxSetImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IHttpClient _httpClient;

@ -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;

@ -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;

@ -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;

@ -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;

@ -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)

@ -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)

@ -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

@ -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;
}
}
}

@ -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" />

@ -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)

@ -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;

@ -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);

@ -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;

@ -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; }
}
}
}

@ -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;
}
}
}

@ -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;
}
}
}

@ -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");
}
}
}

@ -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;
}

@ -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;
}
}
}

@ -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";

@ -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;

@ -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
});
}
}
}

@ -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
}

@ -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)

@ -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
}

@ -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);
}
}
}
}

@ -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
});
}
}
}

@ -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
});
}
}
}

@ -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; }
}
}
}

@ -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;

@ -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;

@ -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;

@ -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;

@ -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;

@ -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);
}

@ -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;

@ -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;

@ -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");

@ -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;

@ -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;

Loading…
Cancel
Save