diff --git a/MediaBrowser.Api/RemoteImageService.cs b/MediaBrowser.Api/RemoteImageService.cs index b50bf4ffe3..c5f1005c75 100644 --- a/MediaBrowser.Api/RemoteImageService.cs +++ b/MediaBrowser.Api/RemoteImageService.cs @@ -5,10 +5,11 @@ using MediaBrowser.Model.Providers; using ServiceStack.ServiceHost; using System.Linq; using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Api { - [Route("/Items/{Id}/RemoteImages/{Type}", "GET")] + [Route("/Items/{Id}/RemoteImages", "GET")] [Api(Description = "Gets available remote images for an item")] public class GetRemoteImages : IReturn { @@ -19,8 +20,8 @@ namespace MediaBrowser.Api [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] public string Id { get; set; } - [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] - public ImageType Type { get; set; } + [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public ImageType? Type { get; set; } /// /// Skips over a given number of items within the results. Use for paging. @@ -35,6 +36,30 @@ namespace MediaBrowser.Api /// The limit. [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } + + [ApiMember(Name = "ProviderName", Description = "Optional. The image provider to use", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ProviderName { get; set; } + } + + [Route("/Items/{Id}/RemoteImages/Download", "POST")] + [Api(Description = "Downloads a remote image for an item")] + public class DownloadRemoteImage : IReturnVoid + { + /// + /// Gets or sets the id. + /// + /// The id. + [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")] + public string Id { get; set; } + + [ApiMember(Name = "Type", Description = "The image type", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] + public ImageType Type { get; set; } + + [ApiMember(Name = "ProviderName", Description = "The image provider", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ProviderName { get; set; } + + [ApiMember(Name = "ImageUrl", Description = "The image url", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public string ImageUrl { get; set; } } public class RemoteImageService : BaseApiService @@ -53,13 +78,14 @@ namespace MediaBrowser.Api { var item = _dtoService.GetItemByDtoId(request.Id); - var images = _providerManager.GetAvailableRemoteImages(item, request.Type, CancellationToken.None).Result; + var images = _providerManager.GetAvailableRemoteImages(item, CancellationToken.None, request.ProviderName, request.Type).Result; var imagesList = images.ToList(); var result = new RemoteImageResult { - TotalRecordCount = imagesList.Count + TotalRecordCount = imagesList.Count, + Providers = _providerManager.GetImageProviders(item).Select(i => i.Name).ToList() }; if (request.StartIndex.HasValue) @@ -78,5 +104,29 @@ namespace MediaBrowser.Api return ToOptimizedResult(result); } + + public void Post(DownloadRemoteImage request) + { + var task = DownloadRemoteImage(request); + + Task.WaitAll(task); + } + + private async Task DownloadRemoteImage(DownloadRemoteImage request) + { + var item = _dtoService.GetItemByDtoId(request.Id); + + int? index = null; + + if (request.Type == ImageType.Backdrop) + { + index = item.BackdropImagePaths.Count; + } + + await _providerManager.SaveImage(item, request.ImageUrl, null, request.Type, index, CancellationToken.None).ConfigureAwait(false); + + await item.RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false) + .ConfigureAwait(false); + } } } diff --git a/MediaBrowser.Controller/Providers/IImageProvider.cs b/MediaBrowser.Controller/Providers/IImageProvider.cs index 0764794388..d70532b592 100644 --- a/MediaBrowser.Controller/Providers/IImageProvider.cs +++ b/MediaBrowser.Controller/Providers/IImageProvider.cs @@ -22,17 +22,30 @@ namespace MediaBrowser.Controller.Providers /// Supportses the specified item. /// /// The item. - /// Type of the image. /// true if XXXX, false otherwise - bool Supports(BaseItem item, ImageType imageType); + bool Supports(BaseItem item); /// - /// Gets the available images. + /// Gets the images. /// /// The item. /// Type of the image. /// The cancellation token. /// Task{IEnumerable{RemoteImageInfo}}. - Task> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken); + Task> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken); + + /// + /// Gets the images. + /// + /// The item. + /// The cancellation token. + /// Task{IEnumerable{RemoteImageInfo}}. + Task> GetAllImages(BaseItem item, CancellationToken cancellationToken); + + /// + /// Gets the priority. + /// + /// The priority. + int Priority { get; } } } diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 2eb2be6db1..728030ecc9 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -60,9 +60,17 @@ namespace MediaBrowser.Controller.Providers /// Gets the available remote images. /// /// The item. - /// The type. /// The cancellation token. + /// Name of the provider. + /// The type. /// Task{IEnumerable{RemoteImageInfo}}. - Task> GetAvailableRemoteImages(BaseItem item, ImageType type, CancellationToken cancellationToken); + Task> GetAvailableRemoteImages(BaseItem item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null); + + /// + /// Gets the image providers. + /// + /// The item. + /// IEnumerable{IImageProvider}. + IEnumerable GetImageProviders(BaseItem item); } } \ No newline at end of file diff --git a/MediaBrowser.Model/Dto/IItemDto.cs b/MediaBrowser.Model/Dto/IItemDto.cs index af46d29b9f..3ec6419184 100644 --- a/MediaBrowser.Model/Dto/IItemDto.cs +++ b/MediaBrowser.Model/Dto/IItemDto.cs @@ -18,4 +18,10 @@ namespace MediaBrowser.Model.Dto /// The original primary image aspect ratio. double? OriginalPrimaryImageAspectRatio { get; set; } } + + public enum RatingType + { + Score, + Likes + } } diff --git a/MediaBrowser.Model/Providers/RemoteImageInfo.cs b/MediaBrowser.Model/Providers/RemoteImageInfo.cs index 1a281f07d2..9a16d89d35 100644 --- a/MediaBrowser.Model/Providers/RemoteImageInfo.cs +++ b/MediaBrowser.Model/Providers/RemoteImageInfo.cs @@ -1,4 +1,4 @@ - +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; namespace MediaBrowser.Model.Providers @@ -55,5 +55,12 @@ namespace MediaBrowser.Model.Providers /// /// The type. public ImageType Type { get; set; } + + /// + /// Gets or sets the type of the rating. + /// + /// The type of the rating. + public RatingType RatingType { get; set; } } + } diff --git a/MediaBrowser.Model/Providers/RemoteImageResult.cs b/MediaBrowser.Model/Providers/RemoteImageResult.cs index 9c3fd94070..1c60db6ae1 100644 --- a/MediaBrowser.Model/Providers/RemoteImageResult.cs +++ b/MediaBrowser.Model/Providers/RemoteImageResult.cs @@ -18,5 +18,11 @@ namespace MediaBrowser.Model.Providers /// /// The total record count. public int TotalRecordCount { get; set; } + + /// + /// Gets or sets the providers. + /// + /// The providers. + public List Providers { get; set; } } } diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 7a8975937d..e944787c6d 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -61,6 +61,7 @@ + diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs index 3458622d34..355d3df0d1 100644 --- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs +++ b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -35,11 +34,6 @@ namespace MediaBrowser.Providers.Movies /// private readonly IProviderManager _providerManager; - /// - /// The us culture - /// - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - internal static FanArtMovieProvider Current { get; private set; } private readonly IFileSystem _fileSystem; @@ -139,28 +133,6 @@ namespace MediaBrowser.Providers.Movies return false; } - if (!ConfigurationManager.Configuration.DownloadMovieImages.Art && - !ConfigurationManager.Configuration.DownloadMovieImages.Logo && - !ConfigurationManager.Configuration.DownloadMovieImages.Disc && - !ConfigurationManager.Configuration.DownloadMovieImages.Backdrops && - !ConfigurationManager.Configuration.DownloadMovieImages.Banner && - !ConfigurationManager.Configuration.DownloadMovieImages.Thumb && - !ConfigurationManager.Configuration.DownloadMovieImages.Primary) - { - return false; - } - - if (item.HasImage(ImageType.Primary) && - item.HasImage(ImageType.Art) && - item.HasImage(ImageType.Logo) && - item.HasImage(ImageType.Disc) && - item.HasImage(ImageType.Banner) && - item.HasImage(ImageType.Thumb) && - item.BackdropImagePaths.Count >= ConfigurationManager.Configuration.MaxBackdrops) - { - return false; - } - return base.NeedsRefreshInternal(item, providerInfo); } @@ -171,24 +143,11 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(id)) { // Process images - var path = GetMovieDataPath(ConfigurationManager.ApplicationPaths, id); + var xmlPath = GetFanartXmlPath(id); - try - { - var files = new DirectoryInfo(path) - .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => _fileSystem.GetLastWriteTimeUtc(i)) - .ToList(); + var fileInfo = new FileInfo(xmlPath); - if (files.Count > 0) - { - return files.Max() > providerInfo.LastRefreshed; - } - } - catch (DirectoryNotFoundException) - { - return true; - } + return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed; } return base.NeedsRefreshBasedOnCompareDate(item, providerInfo); @@ -253,6 +212,12 @@ namespace MediaBrowser.Providers.Movies return true; } + public string GetFanartXmlPath(string tmdbId) + { + var movieDataPath = GetMovieDataPath(ConfigurationManager.ApplicationPaths, tmdbId); + return Path.Combine(movieDataPath, "fanart.xml"); + } + /// /// Downloads the movie XML. /// diff --git a/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs new file mode 100644 index 0000000000..85dd13936a --- /dev/null +++ b/MediaBrowser.Providers/Movies/ManualFanartMovieImageProvider.cs @@ -0,0 +1,291 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +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.Movies +{ + public class ManualFanartMovieImageProvider : IImageProvider + { + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly IServerConfigurationManager _config; + + public ManualFanartMovieImageProvider(IServerConfigurationManager config) + { + _config = config; + } + + public string Name + { + get { return "FanArt"; } + } + + public bool Supports(BaseItem item) + { + return FanArtMovieProvider.Current.Supports(item); + } + + public async Task> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken) + { + var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); + + return images.Where(i => i.Type == imageType); + } + + public Task> GetAllImages(BaseItem item, CancellationToken cancellationToken) + { + var list = new List(); + + var movieId = item.GetProviderId(MetadataProviders.Tmdb); + + if (!string.IsNullOrEmpty(movieId)) + { + var xmlPath = FanArtMovieProvider.Current.GetFanartXmlPath(movieId); + + try + { + AddImages(list, xmlPath, cancellationToken); + } + catch (FileNotFoundException) + { + // No biggie. Don't blow up + } + } + + var language = _config.Configuration.PreferredMetadataLanguage; + + var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); + + 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) + .ToList(); + + return Task.FromResult>(list); + } + + private void AddImages(List list, string xmlPath, 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 "movie": + { + using (var subReader = reader.ReadSubtree()) + { + AddImages(list, subReader, cancellationToken); + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + } + } + + private void AddImages(List list, XmlReader reader, CancellationToken cancellationToken) + { + reader.MoveToContent(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "hdmoviecleararts": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Art, 1000, 562); + } + break; + } + case "hdmovielogos": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Logo, 800, 310); + } + break; + } + case "moviediscs": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Disc, 1000, 1000); + } + break; + } + case "movieposters": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Primary, 1000, 1426); + } + break; + } + case "movielogos": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Logo, 400, 155); + } + break; + } + case "moviearts": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Art, 500, 281); + } + break; + } + case "moviethumbs": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 1000, 562); + } + break; + } + case "moviebanners": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Banner, 1000, 185); + } + break; + } + case "moviebackgrounds": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Backdrop, 1920, 1080); + } + break; + } + default: + reader.Skip(); + break; + } + } + } + } + + private void PopulateImageCategory(List list, XmlReader reader, CancellationToken cancellationToken, ImageType type, int width, int height) + { + reader.MoveToContent(); + + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "hdmovielogo": + case "moviedisc": + case "hdmovieclearart": + case "movieposter": + case "movielogo": + case "movieart": + case "moviethumb": + case "moviebanner": + case "moviebackground": + { + var url = reader.GetAttribute("url"); + + if (!string.IsNullOrEmpty(url)) + { + 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); + } + break; + } + default: + reader.Skip(); + break; + } + } + } + } + + public int Priority + { + get { return 1; } + } + } +} diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs index f84845af7d..c02c60f754 100644 --- a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs @@ -1,6 +1,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Serialization; @@ -26,20 +27,15 @@ namespace MediaBrowser.Providers.Movies public string Name { - get { return "TheMovieDB"; } + get { return "TheMovieDb"; } } - public bool Supports(BaseItem item, ImageType imageType) + public bool Supports(BaseItem item) { - if (MovieDbImagesProvider.SupportsItem(item)) - { - return imageType == ImageType.Primary || imageType == ImageType.Backdrop; - } - - return false; + return MovieDbImagesProvider.SupportsItem(item); } - public async Task> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken) + public async Task> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken) { var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); @@ -70,7 +66,8 @@ namespace MediaBrowser.Providers.Movies Height = i.height, Language = i.iso_639_1, ProviderName = Name, - Type = ImageType.Primary + Type = ImageType.Primary, + RatingType = RatingType.Score })); list.AddRange(GetBackdrops(results, item).Select(i => new RemoteImageInfo @@ -81,7 +78,8 @@ namespace MediaBrowser.Providers.Movies Width = i.width, Height = i.height, ProviderName = Name, - Type = ImageType.Backdrop + Type = ImageType.Backdrop, + RatingType = RatingType.Score })); return list; @@ -124,6 +122,7 @@ namespace MediaBrowser.Providers.Movies return 0; }) .ThenByDescending(i => i.vote_average) + .ThenByDescending(i => i.vote_count) .ToList(); } @@ -139,7 +138,7 @@ namespace MediaBrowser.Providers.Movies images.backdrops.Where(i => i.width >= _config.Configuration.MinMovieBackdropWidth) .ToList(); - return eligibleBackdrops.OrderByDescending(i => i.vote_average); + return eligibleBackdrops.OrderByDescending(i => i.vote_average).ThenByDescending(i => i.vote_count); } /// @@ -164,5 +163,10 @@ namespace MediaBrowser.Providers.Movies return null; } + + public int Priority + { + get { return 2; } + } } } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index d7b7faeea7..50adda6bbe 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -216,16 +216,13 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(path)) { var fileInfo = new FileInfo(path); + var defaultFileInfo = new FileInfo(Path.Combine(Path.GetDirectoryName(path), "default.json")); - if (fileInfo.Exists) - { - return _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed; - } - - return true; + return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed || + !defaultFileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(defaultFileInfo) > providerInfo.LastRefreshed; } - return base.NeedsRefreshBasedOnCompareDate(item, providerInfo); + return true; } /// @@ -510,7 +507,7 @@ namespace MediaBrowser.Providers.Movies var dataFilePath = GetDataFilePath(item, language); - if (string.IsNullOrEmpty(dataFilePath) || !File.Exists(dataFilePath)) + if (string.IsNullOrEmpty(dataFilePath) || !File.Exists(dataFilePath) || !File.Exists(Path.Combine(Path.GetDirectoryName(dataFilePath), "default.json"))) { var isBoxSet = item is BoxSet; diff --git a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs index af89122db1..0252373f00 100644 --- a/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs +++ b/MediaBrowser.Server.Implementations/Providers/ProviderManager.cs @@ -7,13 +7,13 @@ using MediaBrowser.Controller.Library; 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; -using MediaBrowser.Model.Providers; namespace MediaBrowser.Server.Implementations.Providers { @@ -77,7 +77,7 @@ namespace MediaBrowser.Server.Implementations.Providers { MetadataProviders = providers.OrderBy(e => e.Priority).ToArray(); - ImageProviders = imageProviders.ToArray(); + ImageProviders = imageProviders.OrderByDescending(i => i.Priority).ToArray(); } /// @@ -356,52 +356,79 @@ namespace MediaBrowser.Server.Implementations.Providers /// Gets the available remote images. /// /// The item. - /// The type. /// The cancellation token. + /// Name of the provider. + /// The type. /// Task{IEnumerable{RemoteImageInfo}}. - public async Task> GetAvailableRemoteImages(BaseItem item, ImageType type, CancellationToken cancellationToken) + public async Task> GetAvailableRemoteImages(BaseItem item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null) { - var providers = GetSupportedImageProviders(item, type); + var providers = GetImageProviders(item); + + if (!string.IsNullOrEmpty(providerName)) + { + providers = providers.Where(i => string.Equals(i.Name, providerName, StringComparison.OrdinalIgnoreCase)); + } + + var preferredLanguage = ConfigurationManager.Configuration.PreferredMetadataLanguage; var tasks = providers.Select(i => Task.Run(async () => { try { - var result = await i.GetAvailableImages(item, type, cancellationToken).ConfigureAwait(false); - return result.ToList(); + if (type.HasValue) + { + var result = await i.GetImages(item, type.Value, cancellationToken).ConfigureAwait(false); + + return FilterImages(result, preferredLanguage); + } + else + { + var result = await i.GetAllImages(item, cancellationToken).ConfigureAwait(false); + return FilterImages(result, preferredLanguage); + } } catch (Exception ex) { - _logger.ErrorException("{0} failed in GetAvailableImages for type {1}", ex, i.GetType().Name, item.GetType().Name); + _logger.ErrorException("{0} failed in GetImages for type {1}", ex, i.GetType().Name, item.GetType().Name); return new List(); } - })); + + }, cancellationToken)); var results = await Task.WhenAll(tasks).ConfigureAwait(false); return results.SelectMany(i => i); } + private IEnumerable FilterImages(IEnumerable images, string preferredLanguage) + { + if (string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase)) + { + images = images.Where(i => string.IsNullOrEmpty(i.Language) || + string.Equals(i.Language, "en", StringComparison.OrdinalIgnoreCase)); + } + + return images; + } + /// /// Gets the supported image providers. /// /// The item. - /// The type. /// IEnumerable{IImageProvider}. - private IEnumerable GetSupportedImageProviders(BaseItem item, ImageType type) + public IEnumerable GetImageProviders(BaseItem item) { return ImageProviders.Where(i => { try { - return i.Supports(item, type); + return i.Supports(item); } catch (Exception ex) { _logger.ErrorException("{0} failed in Supports for type {1}", ex, i.GetType().Name, item.GetType().Name); return false; } - }); } } diff --git a/MediaBrowser.WebDashboard/ApiClient.js b/MediaBrowser.WebDashboard/ApiClient.js index 9a75c12be0..25cbc98779 100644 --- a/MediaBrowser.WebDashboard/ApiClient.js +++ b/MediaBrowser.WebDashboard/ApiClient.js @@ -305,6 +305,42 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi url: url }); }; + + function getRemoteImagePrefix(options) { + + var urlPrefix; + + if (options.artist) { + urlPrefix = "Artists/" + encodeName(options.artist); + delete options.artist; + } + else if (options.person) { + urlPrefix = "Persons/" + encodeName(options.person); + delete options.person; + } + else if (options.genre) { + urlPrefix = "Genres/" + encodeName(options.genre); + delete options.genre; + } + else if (options.musicGenre) { + urlPrefix = "MusicGenres/" + encodeName(options.musicGenre); + delete options.musicGenre; + } + else if (options.gameGenre) { + urlPrefix = "GameGenres/" + encodeName(options.gameGenre); + delete options.gameGenre; + } + else if (options.studio) { + urlPrefix = "Studios/" + encodeName(options.studio); + delete options.studio; + } + else { + urlPrefix = "Items/" + options.itemId; + delete options.itemId; + } + + return urlPrefix; + } self.getAvailableRemoteImages = function (options) { @@ -312,14 +348,9 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi throw new Error("null options"); } - var urlPrefix = "Items/" + options.itemId; - - var imageType = options.imageType; - - delete options.itemId; - delete options.imageType; + var urlPrefix = getRemoteImagePrefix(options); - var url = self.getUrl(urlPrefix + "/RemoteImages/" + imageType, options); + var url = self.getUrl(urlPrefix + "/RemoteImages", options); return self.ajax({ type: "GET", @@ -328,6 +359,22 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi }); }; + self.downloadRemoteImage = function (options) { + + if (!options) { + throw new Error("null options"); + } + + var urlPrefix = getRemoteImagePrefix(options); + + var url = self.getUrl(urlPrefix + "/RemoteImages/Download", options); + + return self.ajax({ + type: "POST", + url: url + }); + }; + /** * Gets the current server status */ diff --git a/MediaBrowser.WebDashboard/packages.config b/MediaBrowser.WebDashboard/packages.config index ba7498605a..fb2cf4df5b 100644 --- a/MediaBrowser.WebDashboard/packages.config +++ b/MediaBrowser.WebDashboard/packages.config @@ -1,6 +1,6 @@  - + \ No newline at end of file