Browse Source

fixes #551 - Add manual image selection for movies

Luke Pulverenti 11 years ago
parent
commit
926a610075

+ 55 - 5
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<RemoteImageResult>
     {
@@ -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; }
 
         /// <summary>
         /// Skips over a given number of items within the results. Use for paging.
@@ -35,6 +36,30 @@ namespace MediaBrowser.Api
         /// <value>The limit.</value>
         [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
+    {
+        /// <summary>
+        /// Gets or sets the id.
+        /// </summary>
+        /// <value>The id.</value>
+        [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);
+        }
     }
 }

+ 17 - 4
MediaBrowser.Controller/Providers/IImageProvider.cs

@@ -22,17 +22,30 @@ namespace MediaBrowser.Controller.Providers
         /// Supportses the specified item.
         /// </summary>
         /// <param name="item">The item.</param>
-        /// <param name="imageType">Type of the image.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        bool Supports(BaseItem item, ImageType imageType);
+        bool Supports(BaseItem item);
 
         /// <summary>
-        /// Gets the available images.
+        /// Gets the images.
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="imageType">Type of the image.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
-        Task<IEnumerable<RemoteImageInfo>> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken);
+        Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the images.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
+        Task<IEnumerable<RemoteImageInfo>> GetAllImages(BaseItem item, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the priority.
+        /// </summary>
+        /// <value>The priority.</value>
+        int Priority { get; }
     }
 }

+ 10 - 2
MediaBrowser.Controller/Providers/IProviderManager.cs

@@ -60,9 +60,17 @@ namespace MediaBrowser.Controller.Providers
         /// Gets the available remote images.
         /// </summary>
         /// <param name="item">The item.</param>
-        /// <param name="type">The type.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
+        /// <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, ImageType type, CancellationToken cancellationToken);
+        Task<IEnumerable<RemoteImageInfo>> GetAvailableRemoteImages(BaseItem item, CancellationToken cancellationToken, string providerName = null, ImageType? type = null);
+
+        /// <summary>
+        /// Gets the image providers.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns>IEnumerable{IImageProvider}.</returns>
+        IEnumerable<IImageProvider> GetImageProviders(BaseItem item);
     }
 }

+ 6 - 0
MediaBrowser.Model/Dto/IItemDto.cs

@@ -18,4 +18,10 @@ namespace MediaBrowser.Model.Dto
         /// <value>The original primary image aspect ratio.</value>
         double? OriginalPrimaryImageAspectRatio { get; set; }
     }
+
+    public enum RatingType
+    {
+        Score,
+        Likes
+    }
 }

+ 8 - 1
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
         /// </summary>
         /// <value>The type.</value>
         public ImageType Type { get; set; }
+
+        /// <summary>
+        /// Gets or sets the type of the rating.
+        /// </summary>
+        /// <value>The type of the rating.</value>
+        public RatingType RatingType { get; set; }
     }
+
 }

+ 6 - 0
MediaBrowser.Model/Providers/RemoteImageResult.cs

@@ -18,5 +18,11 @@ namespace MediaBrowser.Model.Providers
         /// </summary>
         /// <value>The total record count.</value>
         public int TotalRecordCount { get; set; }
+
+        /// <summary>
+        /// Gets or sets the providers.
+        /// </summary>
+        /// <value>The providers.</value>
+        public List<string> Providers { get; set; }
     }
 }

+ 1 - 0
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -61,6 +61,7 @@
     <Compile Include="MediaInfo\VideoImageProvider.cs" />
     <Compile Include="Movies\BoxSetProviderFromXml.cs" />
     <Compile Include="Movies\ManualMovieDbImageProvider.cs" />
+    <Compile Include="Movies\ManualFanartMovieImageProvider.cs" />
     <Compile Include="Movies\MovieUpdatesPrescanTask.cs" />
     <Compile Include="Movies\MovieXmlParser.cs" />
     <Compile Include="Movies\FanArtMovieProvider.cs" />

+ 9 - 44
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
         /// </summary>
         private readonly IProviderManager _providerManager;
 
-        /// <summary>
-        /// The us culture
-        /// </summary>
-        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");
+        }
+
         /// <summary>
         /// Downloads the movie XML.
         /// </summary>

+ 291 - 0
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<IEnumerable<RemoteImageInfo>> GetImages(BaseItem 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(BaseItem item, CancellationToken cancellationToken)
+        {
+            var list = new List<RemoteImageInfo>();
+
+            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<IEnumerable<RemoteImageInfo>>(list);
+        }
+
+        private void AddImages(List<RemoteImageInfo> 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<RemoteImageInfo> 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<RemoteImageInfo> 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; }
+        }
+    }
+}

+ 16 - 12
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<IEnumerable<RemoteImageInfo>> GetAvailableImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> 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);
         }
 
         /// <summary>
@@ -164,5 +163,10 @@ namespace MediaBrowser.Providers.Movies
 
             return null;
         }
+
+        public int Priority
+        {
+            get { return 2; }
+        }
     }
 }

+ 5 - 8
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;
         }
 
         /// <summary>
@@ -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;
 

+ 40 - 13
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();
         }
 
         /// <summary>
@@ -356,52 +356,79 @@ namespace MediaBrowser.Server.Implementations.Providers
         /// Gets the available remote images.
         /// </summary>
         /// <param name="item">The item.</param>
-        /// <param name="type">The type.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
+        /// <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, ImageType type, CancellationToken cancellationToken)
+        public async Task<IEnumerable<RemoteImageInfo>> 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<RemoteImageInfo>();
                 }
-            }));
+
+            }, cancellationToken));
 
             var results = await Task.WhenAll(tasks).ConfigureAwait(false);
 
             return results.SelectMany(i => i);
         }
 
+        private IEnumerable<RemoteImageInfo> FilterImages(IEnumerable<RemoteImageInfo> 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;
+        }
+
         /// <summary>
         /// Gets the supported image providers.
         /// </summary>
         /// <param name="item">The item.</param>
-        /// <param name="type">The type.</param>
         /// <returns>IEnumerable{IImageProvider}.</returns>
-        private IEnumerable<IImageProvider> GetSupportedImageProviders(BaseItem item, ImageType type)
+        public IEnumerable<IImageProvider> 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;
                 }
-
             });
         }
     }

+ 54 - 7
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
          */

+ 1 - 1
MediaBrowser.WebDashboard/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.184" targetFramework="net45" />
+  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.187" targetFramework="net45" />
   <package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />
   <package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
 </packages>