jellyfin-server/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs

388 lines
14 KiB
C#
Raw Normal View History

using MediaBrowser.Common.IO;
using MediaBrowser.Common.Net;
2013-03-05 15:45:59 +00:00
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
2013-06-09 16:47:28 +00:00
using MediaBrowser.Controller.Providers;
2014-01-31 19:55:21 +00:00
using MediaBrowser.Model.Dto;
2013-03-05 15:45:59 +00:00
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Providers;
2015-05-23 20:44:15 +00:00
using MediaBrowser.Providers.TV;
using System;
using System.Collections.Generic;
2014-01-31 19:55:21 +00:00
using System.Globalization;
2013-04-22 04:38:03 +00:00
using System.IO;
using System.Linq;
2014-01-31 19:55:21 +00:00
using System.Text;
using System.Threading;
using System.Threading.Tasks;
2014-01-31 19:55:21 +00:00
using System.Xml;
2013-03-05 15:45:59 +00:00
2013-06-09 16:47:28 +00:00
namespace MediaBrowser.Providers.Music
2013-03-05 15:45:59 +00:00
{
2014-01-31 19:55:21 +00:00
public class FanartAlbumProvider : IRemoteImageProvider, IHasChangeMonitor, IHasOrder
2013-03-05 15:45:59 +00:00
{
2014-01-31 19:55:21 +00:00
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
2014-01-31 19:55:21 +00:00
public FanartAlbumProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
2013-03-05 15:45:59 +00:00
{
2014-01-31 19:55:21 +00:00
_config = config;
_httpClient = httpClient;
_fileSystem = fileSystem;
2013-03-05 15:45:59 +00:00
}
2014-01-31 19:55:21 +00:00
public string Name
2013-08-07 16:00:20 +00:00
{
2014-01-31 19:55:21 +00:00
get { return ProviderName; }
2013-08-07 16:00:20 +00:00
}
2014-01-31 19:55:21 +00:00
public static string ProviderName
{
get { return "FanArt"; }
}
public bool Supports(IHasImages item)
2013-03-05 15:45:59 +00:00
{
2013-04-22 04:38:03 +00:00
return item is MusicAlbum;
2013-03-05 15:45:59 +00:00
}
2014-01-31 19:55:21 +00:00
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
{
2014-01-31 19:55:21 +00:00
return new List<ImageType>
{
2014-01-31 19:55:21 +00:00
ImageType.Primary,
ImageType.Disc
};
}
2014-01-31 19:55:21 +00:00
2014-02-13 05:11:54 +00:00
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken)
2014-01-31 19:55:21 +00:00
{
var album = (MusicAlbum)item;
var list = new List<RemoteImageInfo>();
var artistMusicBrainzId = album.MusicArtist.GetProviderId(MetadataProviders.MusicBrainzArtist);
2014-01-31 19:55:21 +00:00
if (!string.IsNullOrEmpty(artistMusicBrainzId))
2013-05-01 16:06:57 +00:00
{
2014-07-13 21:03:57 +00:00
await FanartArtistProvider.Current.EnsureArtistXml(artistMusicBrainzId, cancellationToken).ConfigureAwait(false);
2014-01-31 19:55:21 +00:00
var artistXmlPath = FanartArtistProvider.GetArtistXmlPath(_config.CommonApplicationPaths, artistMusicBrainzId);
var musicBrainzReleaseGroupId = album.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
var musicBrainzId = album.GetProviderId(MetadataProviders.MusicBrainzAlbum);
2014-01-31 19:55:21 +00:00
try
{
AddImages(list, artistXmlPath, musicBrainzId, musicBrainzReleaseGroupId, cancellationToken);
}
catch (FileNotFoundException)
{
2014-12-23 03:58:14 +00:00
}
catch (DirectoryNotFoundException)
{
2014-01-31 19:55:21 +00:00
}
2013-05-01 16:06:57 +00:00
}
2014-01-31 19:55:21 +00:00
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);
2013-05-01 16:06:57 +00:00
}
/// <summary>
2014-01-31 19:55:21 +00:00
/// Adds the images.
/// </summary>
2014-01-31 19:55:21 +00:00
/// <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)
2013-05-01 16:06:57 +00:00
{
2014-01-31 19:55:21 +00:00
using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8))
2013-05-01 16:06:57 +00:00
{
2014-01-31 19:55:21 +00:00
// 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;
}
}
}
}
2013-05-01 16:06:57 +00:00
}
}
/// <summary>
2014-01-31 19:55:21 +00:00
/// Adds the images from music node.
/// </summary>
2014-01-31 19:55:21 +00:00
/// <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)
2013-03-05 15:45:59 +00:00
{
2014-01-31 19:55:21 +00:00
reader.MoveToContent();
2014-01-31 19:55:21 +00:00
while (reader.Read())
2013-03-05 15:45:59 +00:00
{
2014-01-31 19:55:21 +00:00
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;
}
}
}
2013-03-05 15:45:59 +00:00
}
}
2014-01-31 19:55:21 +00:00
/// <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)
{
2014-01-31 19:55:21 +00:00
reader.MoveToContent();
2014-01-31 19:55:21 +00:00
while (reader.Read())
{
2014-01-31 19:55:21 +00:00
if (reader.NodeType == XmlNodeType.Element)
{
2014-01-31 19:55:21 +00:00
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;
}
}
}
2014-01-31 19:55:21 +00:00
}
}
/// <summary>
2014-01-31 19:55:21 +00:00
/// Adds the images.
/// </summary>
2014-01-31 19:55:21 +00:00
/// <param name="list">The list.</param>
/// <param name="reader">The reader.</param>
/// <param name="cancellationToken">The cancellation token.</param>
2014-01-31 19:55:21 +00:00
private void AddImages(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken)
{
2014-01-31 19:55:21 +00:00
reader.MoveToContent();
while (reader.Read())
2013-12-15 00:26:01 +00:00
{
2014-01-31 19:55:21 +00:00
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;
}
}
}
2013-12-15 00:26:01 +00:00
}
}
/// <summary>
2014-01-31 19:55:21 +00:00
/// Adds the image.
/// </summary>
2014-01-31 19:55:21 +00:00
/// <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)
{
2014-01-31 19:55:21 +00:00
var url = reader.GetAttribute("url");
var size = reader.GetAttribute("size");
2014-01-31 19:55:21 +00:00
if (!string.IsNullOrEmpty(size))
{
2014-01-31 19:55:21 +00:00
int sizeNum;
if (int.TryParse(size, NumberStyles.Any, _usCulture, out sizeNum))
{
width = sizeNum;
height = sizeNum;
}
}
2014-01-31 19:55:21 +00:00
var likesString = reader.GetAttribute("likes");
int likes;
2014-01-31 19:55:21 +00:00
var info = new RemoteImageInfo
{
2014-01-31 19:55:21 +00:00
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;
}
2014-01-31 19:55:21 +00:00
list.Add(info);
}
2014-01-31 19:55:21 +00:00
public int Order
{
get
{
// After embedded provider
return 1;
}
2014-01-31 19:55:21 +00:00
}
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{
return _httpClient.GetResponse(new HttpRequestOptions
{
2014-01-31 19:55:21 +00:00
CancellationToken = cancellationToken,
Url = url,
ResourcePool = FanartArtistProvider.Current.FanArtResourcePool
2014-01-31 19:55:21 +00:00
});
}
2014-02-10 18:39:41 +00:00
public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
2014-01-31 19:55:21 +00:00
{
2015-01-18 05:45:10 +00:00
var options = FanartSeriesProvider.Current.GetFanartOptions();
if (!options.EnableAutomaticUpdates)
{
return false;
}
2014-01-31 19:55:21 +00:00
var album = (MusicAlbum)item;
var artist = album.MusicArtist;
2014-01-31 19:55:21 +00:00
if (artist != null)
2014-01-31 19:55:21 +00:00
{
var artistMusicBrainzId = artist.GetProviderId(MetadataProviders.MusicBrainzArtist);
2014-01-31 19:55:21 +00:00
if (!String.IsNullOrEmpty(artistMusicBrainzId))
{
// Process images
var artistXmlPath = FanartArtistProvider.GetArtistXmlPath(_config.CommonApplicationPaths, artistMusicBrainzId);
2014-01-31 19:55:21 +00:00
var fileInfo = new FileInfo(artistXmlPath);
return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
}
}
2014-01-31 19:55:21 +00:00
return false;
}
2013-03-05 15:45:59 +00:00
}
}