Просмотр исходного кода

Merge branch 'master' of https://github.com/MediaBrowser/MediaBrowser

Eric Reed 11 лет назад
Родитель
Сommit
bda3a301e7
51 измененных файлов с 765 добавлено и 369 удалено
  1. 73 0
      MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs
  2. 3 0
      MediaBrowser.Api/DefaultTheme/Models.cs
  3. 1 12
      MediaBrowser.Api/Images/ImageService.cs
  4. 1 8
      MediaBrowser.Api/Images/RemoteImageService.cs
  5. 6 2
      MediaBrowser.Api/ItemUpdateService.cs
  6. 11 1
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  7. 32 0
      MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs
  8. 1 0
      MediaBrowser.Controller/Entities/Audio/Artist.cs
  9. 1 0
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  10. 1 0
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  11. 0 12
      MediaBrowser.Controller/Entities/BaseItem.cs
  12. 20 0
      MediaBrowser.Controller/Entities/IHasCriticRating.cs
  13. 13 1
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  14. 13 1
      MediaBrowser.Controller/Entities/Trailer.cs
  15. 1 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  16. 15 4
      MediaBrowser.Controller/Providers/BaseItemXmlParser.cs
  17. 27 6
      MediaBrowser.Controller/Providers/BaseMetadataProvider.cs
  18. 66 3
      MediaBrowser.Model/Drawing/DrawingUtils.cs
  19. 55 0
      MediaBrowser.Providers/CollectionFolderImageProvider.cs
  20. 38 2
      MediaBrowser.Providers/ImageFromMediaLocationProvider.cs
  21. 27 127
      MediaBrowser.Providers/ImagesByNameProvider.cs
  22. 2 0
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  23. 29 14
      MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs
  24. 1 5
      MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
  25. 1 1
      MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs
  26. 10 5
      MediaBrowser.Providers/Movies/MovieDbProvider.cs
  27. 19 15
      MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs
  28. 1 8
      MediaBrowser.Providers/Music/FanArtArtistProvider.cs
  29. 27 41
      MediaBrowser.Providers/Music/LastFmImageProvider.cs
  30. 1 1
      MediaBrowser.Providers/Music/LastfmAlbumProvider.cs
  31. 1 1
      MediaBrowser.Providers/Music/LastfmArtistProvider.cs
  32. 18 7
      MediaBrowser.Providers/Music/LastfmHelper.cs
  33. 1 1
      MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs
  34. 1 1
      MediaBrowser.Providers/Music/ManualFanartArtistProvider.cs
  35. 110 0
      MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs
  36. 10 6
      MediaBrowser.Providers/Savers/XmlSaverHelpers.cs
  37. 1 5
      MediaBrowser.Providers/TV/FanArtTVProvider.cs
  38. 1 5
      MediaBrowser.Providers/TV/TvdbSeasonProvider.cs
  39. 1 5
      MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs
  40. 82 49
      MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs
  41. 8 4
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  42. 3 0
      MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs
  43. 10 6
      MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs
  44. 11 2
      MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
  45. 3 1
      MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs
  46. 1 0
      MediaBrowser.ServerApplication/App.config
  47. 1 1
      MediaBrowser.ServerApplication/ApplicationHost.cs
  48. 1 1
      MediaBrowser.WebDashboard/Api/DashboardService.cs
  49. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  50. 1 1
      Nuget/MediaBrowser.Common.nuspec
  51. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 73 - 0
MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs

@@ -471,6 +471,76 @@ namespace MediaBrowser.Api.DefaultTheme
             }).Where(i => i != null).ToList();
         }
 
+        private void SetFavoriteGenres(MoviesView view, IEnumerable<BaseItem> inputItems, User user)
+        {
+            var all = inputItems.SelectMany(i => i.Genres)
+                .Distinct(StringComparer.OrdinalIgnoreCase);
+
+            view.FavoriteGenres = all.Select(i =>
+            {
+                try
+                {
+                    var itemByName = _libraryManager.GetGenre(i);
+
+                    var counts = itemByName.GetItemByNameCounts(user);
+
+                    var count = counts == null ? 0 : counts.MovieCount;
+
+                    if (count > 0 && _userDataManager.GetUserData(user.Id, itemByName.GetUserDataKey()).IsFavorite)
+                    {
+                        return new ItemByNameInfo
+                        {
+                            Name = itemByName.Name,
+                            ItemCount = count
+                        };
+                    }
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error getting genre {0}", ex, i);
+
+                }
+
+                return null;
+
+            }).Where(i => i != null).ToList();
+        }
+
+        private void SetFavoriteStudios(MoviesView view, IEnumerable<BaseItem> inputItems, User user)
+        {
+            var all = inputItems.SelectMany(i => i.Studios)
+                .Distinct(StringComparer.OrdinalIgnoreCase);
+
+            view.FavoriteStudios = all.Select(i =>
+            {
+                try
+                {
+                    var itemByName = _libraryManager.GetStudio(i);
+
+                    var counts = itemByName.GetItemByNameCounts(user);
+
+                    var count = counts == null ? 0 : counts.MovieCount;
+
+                    if (count > 0 && _userDataManager.GetUserData(user.Id, itemByName.GetUserDataKey()).IsFavorite)
+                    {
+                        return new ItemByNameInfo
+                        {
+                            Name = itemByName.Name,
+                            ItemCount = count
+                        };
+                    }
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error getting studio {0}", ex, i);
+
+                }
+
+                return null;
+
+            }).Where(i => i != null).ToList();
+        }
+
         public object Get(GetMovieView request)
         {
             var user = _userManager.GetUserById(request.UserId);
@@ -487,6 +557,9 @@ namespace MediaBrowser.Api.DefaultTheme
             var movies = items.OfType<Movie>()
                 .ToList();
 
+            SetFavoriteGenres(view, movies, user);
+            SetFavoriteStudios(view, movies, user);
+            
             var trailers = items.OfType<Trailer>()
                .ToList();
             

+ 3 - 0
MediaBrowser.Api/DefaultTheme/Models.cs

@@ -34,6 +34,9 @@ namespace MediaBrowser.Api.DefaultTheme
 
         public List<BaseItemDto> LatestTrailers { get; set; }
         public List<BaseItemDto> LatestMovies { get; set; }
+
+        public List<ItemByNameInfo> FavoriteGenres { get; set; }
+        public List<ItemByNameInfo> FavoriteStudios { get; set; }
     }
 
     public class TvView : BaseView

+ 1 - 12
MediaBrowser.Api/Images/ImageService.cs

@@ -861,21 +861,10 @@ namespace MediaBrowser.Api.Images
                     Position = 0
                 };
 
-                var imageIndex = 0;
-
-                if (imageType == ImageType.Screenshot)
-                {
-                    imageIndex = entity.ScreenshotImagePaths.Count;
-                }
-                else if (imageType == ImageType.Backdrop)
-                {
-                    imageIndex = entity.BackdropImagePaths.Count;
-                }
-
                 // Handle image/png; charset=utf-8
                 mimeType = mimeType.Split(';').FirstOrDefault();
 
-                await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, imageIndex, null, CancellationToken.None).ConfigureAwait(false);
+                await _providerManager.SaveImage(entity, memoryStream, mimeType, imageType, null, null, CancellationToken.None).ConfigureAwait(false);
 
                 await entity.RefreshMetadata(CancellationToken.None, forceRefresh: true, forceSave: true, allowSlowProviders: false).ConfigureAwait(false);
             }

+ 1 - 8
MediaBrowser.Api/Images/RemoteImageService.cs

@@ -282,14 +282,7 @@ namespace MediaBrowser.Api.Images
         /// <returns>Task.</returns>
         private async Task DownloadRemoteImage(BaseItem item, BaseDownloadRemoteImage request)
         {
-            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 _providerManager.SaveImage(item, request.ImageUrl, null, request.Type, null, CancellationToken.None).ConfigureAwait(false);
 
             await item.RefreshMetadata(CancellationToken.None, forceSave: true, allowSlowProviders: false)
                     .ConfigureAwait(false);

+ 6 - 2
MediaBrowser.Api/ItemUpdateService.cs

@@ -219,8 +219,12 @@ namespace MediaBrowser.Api
             item.Budget = request.Budget;
             item.Revenue = request.Revenue;
 
-            item.CriticRating = request.CriticRating;
-            item.CriticRatingSummary = request.CriticRatingSummary;
+            var hasCriticRating = item as IHasCriticRating;
+            if (hasCriticRating != null)
+            {
+                hasCriticRating.CriticRating = request.CriticRating;
+                hasCriticRating.CriticRatingSummary = request.CriticRatingSummary;
+            }
 
             item.DisplayMediaType = request.DisplayMediaType;
             item.CommunityRating = request.CommunityRating;

+ 11 - 1
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -581,7 +581,17 @@ namespace MediaBrowser.Api.UserLibrary
             {
                 var val = request.MinCriticRating.Value;
 
-                items = items.Where(i => i.CriticRating.HasValue && i.CriticRating >= val);
+                items = items.Where(i =>
+                {
+                    var hasCriticRating = i as IHasCriticRating;
+
+                    if (hasCriticRating != null)
+                    {
+                        return hasCriticRating.CriticRating.HasValue && hasCriticRating.CriticRating >= val;
+                    }
+
+                    return false;
+                });
             }
 
             // Artists

+ 32 - 0
MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs

@@ -40,5 +40,37 @@ namespace MediaBrowser.Controller.Drawing
         public int? PercentPlayed { get; set; }
 
         public string BackgroundColor { get; set; }
+
+        public bool HasDefaultOptions()
+        {
+            return HasDefaultOptionsWithoutSize() && 
+                !Width.HasValue && 
+                !Height.HasValue && 
+                !MaxWidth.HasValue && 
+                !MaxHeight.HasValue;
+        }
+
+        public bool HasDefaultOptionsWithoutSize()
+        {
+            return !CropWhiteSpace &&
+                (!Quality.HasValue || Quality.Value == 100) &&
+                IsOutputFormatDefault &&
+                !AddPlayedIndicator &&
+                !PercentPlayed.HasValue &&
+                string.IsNullOrEmpty(BackgroundColor);
+        }
+
+        private bool IsOutputFormatDefault
+        {
+            get
+            {
+                if (OutputFormat == ImageOutputFormat.Original)
+                {
+                    return true;
+                }
+
+                return false;
+            }
+        }
     }
 }

+ 1 - 0
MediaBrowser.Controller/Entities/Audio/Artist.cs

@@ -20,6 +20,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         }
 
         public string LastFmImageUrl { get; set; }
+        public string LastFmImageSize { get; set; }
 
         /// <summary>
         /// Gets the user data key.

+ 1 - 0
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -17,6 +17,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         }
 
         public string LastFmImageUrl { get; set; }
+        public string LastFmImageSize { get; set; }
 
         /// <summary>
         /// Songs will group into us so don't also include us in the index

+ 1 - 0
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -11,6 +11,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         /// </summary>
         /// <value>The last fm image URL.</value>
         public string LastFmImageUrl { get; set; }
+        public string LastFmImageSize { get; set; }
 
         /// <summary>
         /// Gets the user data key.

+ 0 - 12
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -111,18 +111,6 @@ namespace MediaBrowser.Controller.Entities
         /// <value>The revenue.</value>
         public double? Revenue { get; set; }
 
-        /// <summary>
-        /// Gets or sets the critic rating.
-        /// </summary>
-        /// <value>The critic rating.</value>
-        public float? CriticRating { get; set; }
-
-        /// <summary>
-        /// Gets or sets the critic rating summary.
-        /// </summary>
-        /// <value>The critic rating summary.</value>
-        public string CriticRatingSummary { get; set; }
-
         /// <summary>
         /// Gets or sets the trailer URL.
         /// </summary>

+ 20 - 0
MediaBrowser.Controller/Entities/IHasCriticRating.cs

@@ -0,0 +1,20 @@
+namespace MediaBrowser.Controller.Entities
+{
+    /// <summary>
+    /// Interface IHasCriticRating
+    /// </summary>
+    public interface IHasCriticRating
+    {
+        /// <summary>
+        /// Gets or sets the critic rating.
+        /// </summary>
+        /// <value>The critic rating.</value>
+        float? CriticRating { get; set; }
+
+        /// <summary>
+        /// Gets or sets the critic rating summary.
+        /// </summary>
+        /// <value>The critic rating summary.</value>
+        string CriticRatingSummary { get; set; }
+    }
+}

+ 13 - 1
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -11,7 +11,7 @@ namespace MediaBrowser.Controller.Entities.Movies
     /// <summary>
     /// Class Movie
     /// </summary>
-    public class Movie : Video
+    public class Movie : Video, IHasCriticRating
     {
         public List<Guid> SpecialFeatureIds { get; set; }
 
@@ -20,6 +20,18 @@ namespace MediaBrowser.Controller.Entities.Movies
             SpecialFeatureIds = new List<Guid>();
         }
 
+        /// <summary>
+        /// Gets or sets the critic rating.
+        /// </summary>
+        /// <value>The critic rating.</value>
+        public float? CriticRating { get; set; }
+
+        /// <summary>
+        /// Gets or sets the critic rating summary.
+        /// </summary>
+        /// <value>The critic rating summary.</value>
+        public string CriticRatingSummary { get; set; }
+
         /// <summary>
         /// Gets or sets the name of the TMDB collection.
         /// </summary>

+ 13 - 1
MediaBrowser.Controller/Entities/Trailer.cs

@@ -7,7 +7,7 @@ namespace MediaBrowser.Controller.Entities
     /// <summary>
     /// Class Trailer
     /// </summary>
-    public class Trailer : Video
+    public class Trailer : Video, IHasCriticRating
     {
         public Trailer()
         {
@@ -15,6 +15,18 @@ namespace MediaBrowser.Controller.Entities
             Taglines = new List<string>();
         }
 
+        /// <summary>
+        /// Gets or sets the critic rating.
+        /// </summary>
+        /// <value>The critic rating.</value>
+        public float? CriticRating { get; set; }
+
+        /// <summary>
+        /// Gets or sets the critic rating summary.
+        /// </summary>
+        /// <value>The critic rating summary.</value>
+        public string CriticRatingSummary { get; set; }
+
         /// <summary>
         /// Gets a value indicating whether this instance is local trailer.
         /// </summary>

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

@@ -89,6 +89,7 @@
     <Compile Include="Entities\GameGenre.cs" />
     <Compile Include="Entities\GameSystem.cs" />
     <Compile Include="Entities\IByReferenceItem.cs" />
+    <Compile Include="Entities\IHasCriticRating.cs" />
     <Compile Include="Entities\IItemByName.cs" />
     <Compile Include="Entities\ILibraryItem.cs" />
     <Compile Include="Entities\ImageSourceInfo.cs" />

+ 15 - 4
MediaBrowser.Controller/Providers/BaseItemXmlParser.cs

@@ -143,10 +143,16 @@ namespace MediaBrowser.Controller.Providers
                 case "CriticRating":
                     {
                         var text = reader.ReadElementContentAsString();
-                        float value;
-                        if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
+
+                        var hasCriticRating = item as IHasCriticRating;
+
+                        if (hasCriticRating != null && !string.IsNullOrEmpty(text))
                         {
-                            item.CriticRating = value;
+                            float value;
+                            if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
+                            {
+                                hasCriticRating.CriticRating = value;
+                            }
                         }
 
                         break;
@@ -207,7 +213,12 @@ namespace MediaBrowser.Controller.Providers
 
                         if (!string.IsNullOrWhiteSpace(val))
                         {
-                            item.CriticRatingSummary = val;
+                            var hasCriticRating = item as IHasCriticRating;
+
+                            if (hasCriticRating != null)
+                            {
+                                hasCriticRating.CriticRatingSummary = val;
+                            }
                         }
 
                         break;

+ 27 - 6
MediaBrowser.Controller/Providers/BaseMetadataProvider.cs

@@ -24,6 +24,7 @@ namespace MediaBrowser.Controller.Providers
         /// </summary>
         /// <value>The logger.</value>
         protected ILogger Logger { get; set; }
+
         protected ILogManager LogManager { get; set; }
 
         /// <summary>
@@ -41,6 +42,7 @@ namespace MediaBrowser.Controller.Providers
         /// The true task result
         /// </summary>
         protected static readonly Task<bool> TrueTaskResult = Task.FromResult(true);
+
         protected static readonly Task<bool> FalseTaskResult = Task.FromResult(false);
 
         protected static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(5, 5);
@@ -132,7 +134,8 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="providerVersion">The provider version.</param>
         /// <param name="status">The status.</param>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        public virtual void SetLastRefreshed(BaseItem item, DateTime value, string providerVersion, ProviderRefreshStatus status = ProviderRefreshStatus.Success)
+        public virtual void SetLastRefreshed(BaseItem item, DateTime value, string providerVersion,
+            ProviderRefreshStatus status = ProviderRefreshStatus.Success)
         {
             if (item == null)
             {
@@ -172,7 +175,8 @@ namespace MediaBrowser.Controller.Providers
         /// <param name="item">The item.</param>
         /// <param name="value">The value.</param>
         /// <param name="status">The status.</param>
-        public void SetLastRefreshed(BaseItem item, DateTime value, ProviderRefreshStatus status = ProviderRefreshStatus.Success)
+        public void SetLastRefreshed(BaseItem item, DateTime value,
+            ProviderRefreshStatus status = ProviderRefreshStatus.Success)
         {
             SetLastRefreshed(item, value, ProviderVersion, status);
         }
@@ -238,7 +242,8 @@ namespace MediaBrowser.Controller.Providers
                 return true;
             }
 
-            if (RefreshOnFileSystemStampChange && item.LocationType == LocationType.FileSystem && HasFileSystemStampChanged(item, providerInfo))
+            if (RefreshOnFileSystemStampChange && item.LocationType == LocationType.FileSystem &&
+                HasFileSystemStampChanged(item, providerInfo))
             {
                 return true;
             }
@@ -349,21 +354,23 @@ namespace MediaBrowser.Controller.Providers
         }
 
         private Dictionary<string, string> _fileStampExtensionsDictionary;
+
         private Dictionary<string, string> FileStampExtensionsDictionary
         {
             get
             {
                 return _fileStampExtensionsDictionary ??
                        (_fileStampExtensionsDictionary =
-                        FilestampExtensions.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase));
+                           FilestampExtensions.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase));
             }
         }
+
         /// <summary>
         /// Gets the file system stamp.
         /// </summary>
         /// <param name="item">The item.</param>
         /// <returns>Guid.</returns>
-        private Guid GetFileSystemStamp(BaseItem item)
+        protected virtual Guid GetFileSystemStamp(BaseItem item)
         {
             // If there's no path or the item is a file, there's nothing to do
             if (item.LocationType != LocationType.FileSystem)
@@ -404,6 +411,20 @@ namespace MediaBrowser.Controller.Providers
         private static readonly Dictionary<string, string> FoldersToMonitor = new[] { "extrafanart", "extrathumbs" }
             .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
 
+        protected Guid GetFileSystemStamp(IEnumerable<FileSystemInfo> files)
+        {
+            var sb = new StringBuilder();
+
+            var extensions = FileStampExtensionsDictionary;
+            var numExtensions = FilestampExtensions.Length;
+
+            // Record the name of each file 
+            // Need to sort these because accoring to msdn docs, our i/o methods are not guaranteed in any order
+            AddFiles(sb, files, extensions, numExtensions);
+
+            return sb.ToString().GetMD5();
+        }
+
         /// <summary>
         /// Adds the files.
         /// </summary>
@@ -424,7 +445,7 @@ namespace MediaBrowser.Controller.Providers
                         {
                             sb.Append(file.Name);
 
-                            var children = ((DirectoryInfo) file).EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList();
+                            var children = ((DirectoryInfo)file).EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList();
                             AddFiles(sb, children, extensions, numExtensions);
                         }
                     }

+ 66 - 3
MediaBrowser.Model/Drawing/DrawingUtils.cs

@@ -1,4 +1,5 @@
-
+using System.Globalization;
+
 namespace MediaBrowser.Model.Drawing
 {
     /// <summary>
@@ -131,15 +132,77 @@ namespace MediaBrowser.Model.Drawing
     /// </summary>
     public struct ImageSize
     {
+        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+        private double _height;
+        private double _width;
+
         /// <summary>
         /// Gets or sets the height.
         /// </summary>
         /// <value>The height.</value>
-        public double Height { get; set; }
+        public double Height
+        {
+            get
+            {
+                return _height;
+            }
+            set
+            {
+                _height = value;
+            }
+        }
+
         /// <summary>
         /// Gets or sets the width.
         /// </summary>
         /// <value>The width.</value>
-        public double Width { get; set; }
+        public double Width
+        {
+            get { return _width; }
+            set { _width = value; }
+        }
+
+        public bool Equals(ImageSize size)
+        {
+            return Width.Equals(size.Width) && Height.Equals(size.Height);
+        }
+
+        public override string ToString()
+        {
+            return string.Format("{0}-{1}", Width, Height);
+        }
+
+        public ImageSize(string value)
+        {
+            _width = 0;
+
+            _height = 0;
+
+            ParseValue(value);
+        }
+
+        private void ParseValue(string value)
+        {
+            if (!string.IsNullOrEmpty(value))
+            {
+                var parts = value.Split('-');
+
+                if (parts.Length == 2)
+                {
+                    double val;
+
+                    if (double.TryParse(parts[0], NumberStyles.Any, UsCulture, out val))
+                    {
+                        _width = val;
+                    }
+
+                    if (double.TryParse(parts[1], NumberStyles.Any, UsCulture, out val))
+                    {
+                        _height = val;
+                    }
+                }
+            }
+        }
     }
 }

+ 55 - 0
MediaBrowser.Providers/CollectionFolderImageProvider.cs

@@ -0,0 +1,55 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using System;
+using System.IO;
+using System.Linq;
+
+namespace MediaBrowser.Providers
+{
+    public class CollectionFolderImageProvider : ImageFromMediaLocationProvider
+    {
+        public CollectionFolderImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
+            : base(logManager, configurationManager, fileSystem)
+        {
+        }
+
+        public override bool Supports(BaseItem item)
+        {
+            return item is CollectionFolder && item.LocationType == LocationType.FileSystem;
+        }
+
+        public override MetadataProviderPriority Priority
+        {
+            get { return MetadataProviderPriority.Second; }
+        }
+
+        protected override FileSystemInfo GetImage(BaseItem item, ItemResolveArgs args, string filenameWithoutExtension)
+        {
+            return item.ResolveArgs.PhysicalLocations
+                .Select(i => GetImageFromLocation(i, filenameWithoutExtension))
+                .FirstOrDefault(i => i != null);
+        }
+
+        protected override Guid GetFileSystemStamp(BaseItem item)
+        {
+            var files = item.ResolveArgs.PhysicalLocations
+                .Select(i => new DirectoryInfo(i))
+                .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
+                .Where(i =>
+                {
+                    var ext = i.Extension;
+
+                    return !string.IsNullOrEmpty(ext) &&
+                        BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
+                })
+                .ToList();
+
+            return GetFileSystemStamp(files);
+        }
+    }
+}

+ 38 - 2
MediaBrowser.Providers/ImageFromMediaLocationProvider.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
 using MediaBrowser.Controller.Entities.TV;
@@ -21,9 +22,12 @@ namespace MediaBrowser.Providers
     /// </summary>
     public class ImageFromMediaLocationProvider : BaseMetadataProvider
     {
-        public ImageFromMediaLocationProvider(ILogManager logManager, IServerConfigurationManager configurationManager)
+        protected readonly IFileSystem FileSystem;
+        
+        public ImageFromMediaLocationProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
             : base(logManager, configurationManager)
         {
+            FileSystem = fileSystem;
         }
 
         public override ItemUpdateType ItemUpdateType
@@ -543,5 +547,37 @@ namespace MediaBrowser.Providers
                 item.ScreenshotImagePaths = screenshotFiles;
             }
         }
+
+        protected FileSystemInfo GetImageFromLocation(string path, string filenameWithoutExtension)
+        {
+            try
+            {
+                var files = new DirectoryInfo(path)
+                    .EnumerateFiles()
+                    .Where(i =>
+                    {
+                        var fileName = Path.GetFileNameWithoutExtension(i.FullName);
+
+                        if (!string.Equals(fileName, filenameWithoutExtension, StringComparison.OrdinalIgnoreCase))
+                        {
+                            return false;
+                        }
+
+                        var ext = i.Extension;
+
+                        return !string.IsNullOrEmpty(ext) &&
+                            BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
+                    })
+                    .ToList();
+
+                return BaseItem.SupportedImageExtensions
+                    .Select(ext => files.FirstOrDefault(i => string.Equals(ext, i.Extension, StringComparison.OrdinalIgnoreCase)))
+                    .FirstOrDefault(file => file != null);
+            }
+            catch (DirectoryNotFoundException)
+            {
+                return null;
+            }
+        }
     }
 }

+ 27 - 127
MediaBrowser.Providers/ImagesByNameProvider.cs

@@ -1,16 +1,12 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Logging;
 using System;
 using System.IO;
 using System.Linq;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace MediaBrowser.Providers
 {
@@ -19,22 +15,11 @@ namespace MediaBrowser.Providers
     /// </summary>
     public class ImagesByNameProvider : ImageFromMediaLocationProvider
     {
-        private readonly IFileSystem _fileSystem;
-        
         public ImagesByNameProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
-            : base(logManager, configurationManager)
+            : base(logManager, configurationManager, fileSystem)
         {
-            _fileSystem = fileSystem;
         }
 
-        public override ItemUpdateType ItemUpdateType
-        {
-            get
-            {
-                return ItemUpdateType.ImageUpdate;
-            }
-        }
-        
         /// <summary>
         /// Supportses the specified item.
         /// </summary>
@@ -42,8 +27,8 @@ namespace MediaBrowser.Providers
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
         public override bool Supports(BaseItem item)
         {
-            //only run for these generic types since we are expensive in file i/o
-            return item is IndexFolder || item is BasePluginFolder || item is CollectionFolder;
+            // Only run for these generic types since we are expensive in file i/o
+            return item is BasePluginFolder || item is CollectionFolder;
         }
 
         /// <summary>
@@ -58,95 +43,6 @@ namespace MediaBrowser.Providers
             }
         }
 
-        /// <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)
-        {
-            // Force a refresh if the IBN path changed
-            if (providerInfo.FileStamp != ConfigurationManager.ApplicationPaths.ItemsByNamePath.GetMD5())
-            {
-                return true;
-            }
-
-            return base.NeedsRefreshInternal(item, providerInfo);
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether [refresh on file system stamp change].
-        /// </summary>
-        /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value>
-        protected override bool RefreshOnFileSystemStampChange
-        {
-            get
-            {
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Override this to return the date that should be compared to the last refresh date
-        /// to determine if this provider should be re-fetched.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>DateTime.</returns>
-        protected override DateTime CompareDate(BaseItem item)
-        {
-            // If the IBN location exists return the last modified date of any file in it
-            var location = GetLocation(item);
-
-            var directoryInfo = new DirectoryInfo(location);
-
-            if (!directoryInfo.Exists)
-            {
-                return DateTime.MinValue;
-            }
-
-            var files = directoryInfo.EnumerateFiles().ToList();
-
-            if (files.Count == 0)
-            {
-                return DateTime.MinValue;
-            }
-
-            return files.Select(f =>
-            {
-                var lastWriteTime = _fileSystem.GetLastWriteTimeUtc(f);
-                var creationTime = _fileSystem.GetCreationTimeUtc(f);
-
-                return creationTime > lastWriteTime ? creationTime : lastWriteTime;
-
-            }).Max();
-        }
-
-        /// <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, CancellationToken cancellationToken)
-        {
-            var result = await base.FetchAsync(item, force, cancellationToken).ConfigureAwait(false);
-
-            BaseProviderInfo data;
-
-            if (!item.ProviderData.TryGetValue(Id, out data))
-            {
-                data = new BaseProviderInfo();
-                item.ProviderData[Id] = data;
-            }
-
-            data.FileStamp = ConfigurationManager.ApplicationPaths.ItemsByNamePath.GetMD5();
-            SetLastRefreshed(item, DateTime.UtcNow);
-     
-            return result;
-        }
-
         /// <summary>
         /// Gets the location.
         /// </summary>
@@ -154,7 +50,7 @@ namespace MediaBrowser.Providers
         /// <returns>System.String.</returns>
         protected string GetLocation(BaseItem item)
         {
-            var name = _fileSystem.GetValidFilename(item.Name);
+            var name = FileSystem.GetValidFilename(item.Name);
 
             return Path.Combine(ConfigurationManager.ApplicationPaths.GeneralPath, name);
         }
@@ -170,30 +66,34 @@ namespace MediaBrowser.Providers
         {
             var location = GetLocation(item);
 
-            var directoryInfo = new DirectoryInfo(location);
-
-            if (!directoryInfo.Exists)
-            {
-                return null;
-            }
-
-            var files = directoryInfo.EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList();
+            return GetImageFromLocation(location, filenameWithoutExtension);
+        }
 
-            var file = files.FirstOrDefault(i => string.Equals(i.Name, filenameWithoutExtension + ".png", StringComparison.OrdinalIgnoreCase));
+        protected override Guid GetFileSystemStamp(BaseItem item)
+        {
+            var location = GetLocation(item);
 
-            if (file != null)
+            try
             {
-                return file;
+                var files = new DirectoryInfo(location)
+                    .EnumerateFiles("*", SearchOption.TopDirectoryOnly)
+                    .Where(i =>
+                    {
+                        var ext = i.Extension;
+
+                        return !string.IsNullOrEmpty(ext) &&
+                            BaseItem.SupportedImageExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase);
+                    })
+                    .ToList();
+
+                return GetFileSystemStamp(files);
             }
-
-            file = files.FirstOrDefault(i => string.Equals(i.Name, filenameWithoutExtension + ".jpg", StringComparison.OrdinalIgnoreCase));
-
-            if (file != null)
+            catch (DirectoryNotFoundException)
             {
-                return file;
-            }
+                // User doesn't have the folder. No need to log or blow up
 
-            return null;
+                return Guid.Empty;
+            }
         }
     }
 }

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

@@ -47,6 +47,7 @@
     </Reference>
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="CollectionFolderImageProvider.cs" />
     <Compile Include="FanartBaseProvider.cs" />
     <Compile Include="FolderProviderFromXml.cs" />
     <Compile Include="Games\GameXmlParser.cs" />
@@ -92,6 +93,7 @@
     <Compile Include="Music\AlbumDynamicInfoProvider.cs" />
     <Compile Include="Music\ManualFanartAlbumProvider.cs" />
     <Compile Include="Music\ManualFanartArtistProvider.cs" />
+    <Compile Include="Music\ManualLastFmImageProvider.cs" />
     <Compile Include="Music\MusicVideoXmlParser.cs" />
     <Compile Include="Music\SoundtrackPostScanTask.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 29 - 14
MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs

@@ -251,17 +251,33 @@ namespace MediaBrowser.Providers.MediaInfo
 
         private string GetAspectRatio(MediaStreamInfo info)
         {
-            if (info.height > 0 && info.width > 0)
+            var original = info.display_aspect_ratio;
+
+            int height;
+            int width;
+
+            var parts = (original ?? string.Empty).Split(':');
+            if (!(parts.Length == 2 &&
+                int.TryParse(parts[0], NumberStyles.Any, UsCulture, out width) &&
+                int.TryParse(parts[1], NumberStyles.Any, UsCulture, out height) &&
+                width > 0 &&
+                height > 0))
+            {
+                width = info.width;
+                height = info.height;
+            }
+
+            if (width > 0 && height > 0)
             {
-                double ratio = info.width;
-                ratio /= info.height;
+                double ratio = width;
+                ratio /= height;
 
-                if (IsClose(ratio, 1.777777778))
+                if (IsClose(ratio, 1.777777778, .03))
                 {
                     return "16:9";
                 }
 
-                if (IsClose(ratio, 1.3333333333))
+                if (IsClose(ratio, 1.3333333333, .05))
                 {
                     return "4:3";
                 }
@@ -286,31 +302,30 @@ namespace MediaBrowser.Providers.MediaInfo
                     return "5:3";
                 }
 
-                if (IsClose(ratio, 1.85))
+                if (IsClose(ratio, 1.85, .02))
                 {
                     return "1.85:1";
                 }
 
-                if (IsClose(ratio, 2.39))
+                if (IsClose(ratio, 2.35, .025))
                 {
-                    return "2.39:1";
+                    return "2.35:1";
                 }
 
-                if (IsClose(ratio, 2.4))
+                if (IsClose(ratio, 2.4, .025))
                 {
-                    return "2.4:1";
+                    return "2.40:1";
                 }
             }
 
-            return info.display_aspect_ratio;
+            return original;
         }
 
-        private bool IsClose(double d1, double d2)
+        private bool IsClose(double d1, double d2, double variance = .005)
         {
-            return Math.Abs(d1 - d2) <= .005;
+            return Math.Abs(d1 - d2) <= variance;
         }
 
-
         /// <summary>
         /// Gets a frame rate from a string value in ffprobe output
         /// This could be a number or in the format of 2997/125.

+ 1 - 5
MediaBrowser.Providers/Movies/FanArtMovieProvider.cs

@@ -327,15 +327,11 @@ namespace MediaBrowser.Providers.Movies
             if (ConfigurationManager.Configuration.DownloadMovieImages.Backdrops &&
                 item.BackdropImagePaths.Count < backdropLimit)
             {
-                var numBackdrops = item.BackdropImagePaths.Count;
-
                 foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
                 {
-                    await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Backdrop, numBackdrops, cancellationToken)
+                    await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Backdrop, null, cancellationToken)
                                         .ConfigureAwait(false);
 
-                    numBackdrops++;
-
                     if (item.BackdropImagePaths.Count >= backdropLimit) break;
                 }
             }

+ 1 - 1
MediaBrowser.Providers/Movies/MovieDbImagesProvider.cs

@@ -231,7 +231,7 @@ namespace MediaBrowser.Providers.Movies
 
                         }).ConfigureAwait(false);
 
-                        await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(url), ImageType.Backdrop, item.BackdropImagePaths.Count, url, cancellationToken)
+                        await _providerManager.SaveImage(item, img, MimeTypes.GetMimeType(url), ImageType.Backdrop, null, url, cancellationToken)
                           .ConfigureAwait(false);
                     }
 

+ 10 - 5
MediaBrowser.Providers/Movies/MovieDbProvider.cs

@@ -822,13 +822,18 @@ namespace MediaBrowser.Providers.Movies
 
                 // genres
                 // Movies get this from imdb
-                if (movieData.genres != null && !movie.LockedFields.Contains(MetadataFields.Genres) && movie is BoxSet)
+                if (movieData.genres != null && !movie.LockedFields.Contains(MetadataFields.Genres))
                 {
-                    movie.Genres.Clear();
-
-                    foreach (var genre in movieData.genres.Select(g => g.name))
+                    // Only grab them if a boxset or there are no genres.
+                    // For movies and trailers we'll use imdb via omdb
+                    if (movie is BoxSet || movie.Genres.Count == 0)
                     {
-                        movie.AddGenre(genre);
+                        movie.Genres.Clear();
+
+                        foreach (var genre in movieData.genres.Select(g => g.name))
+                        {
+                            movie.AddGenre(genre);
+                        }
                     }
                 }
 

+ 19 - 15
MediaBrowser.Providers/Movies/OpenMovieDatabaseProvider.cs

@@ -139,22 +139,26 @@ namespace MediaBrowser.Providers.Movies
             {
                 var result = JsonSerializer.DeserializeFromStream<RootObject>(stream);
 
-                // Seeing some bogus RT data on omdb for series, so filter it out here
-                // RT doesn't even have tv series
-                int tomatoMeter;
-
-                if (!string.IsNullOrEmpty(result.tomatoMeter)
-                    && int.TryParse(result.tomatoMeter, NumberStyles.Integer, UsCulture, out tomatoMeter)
-                    && tomatoMeter >= 0)
-                {
-                    item.CriticRating = tomatoMeter;
-                }
-
-                if (!string.IsNullOrEmpty(result.tomatoConsensus)
-                    && !string.Equals(result.tomatoConsensus, "n/a", StringComparison.OrdinalIgnoreCase)
-                    && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
+                var hasCriticRating = item as IHasCriticRating;
+                if (hasCriticRating != null)
                 {
-                    item.CriticRatingSummary = result.tomatoConsensus;
+                    // Seeing some bogus RT data on omdb for series, so filter it out here
+                    // RT doesn't even have tv series
+                    int tomatoMeter;
+
+                    if (!string.IsNullOrEmpty(result.tomatoMeter)
+                        && int.TryParse(result.tomatoMeter, NumberStyles.Integer, UsCulture, out tomatoMeter)
+                        && tomatoMeter >= 0)
+                    {
+                        hasCriticRating.CriticRating = tomatoMeter;
+                    }
+
+                    if (!string.IsNullOrEmpty(result.tomatoConsensus)
+                        && !string.Equals(result.tomatoConsensus, "n/a", StringComparison.OrdinalIgnoreCase)
+                        && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
+                    {
+                        hasCriticRating.CriticRatingSummary = result.tomatoConsensus;
+                    }
                 }
 
                 int voteCount;

+ 1 - 8
MediaBrowser.Providers/Music/FanArtArtistProvider.cs

@@ -8,17 +8,14 @@ using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Providers;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
-using System.Net;
 using System.Threading;
 using System.Threading.Tasks;
-using System.Xml;
 
 namespace MediaBrowser.Providers.Music
 {
@@ -323,15 +320,11 @@ namespace MediaBrowser.Providers.Music
             if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Backdrops &&
                 item.BackdropImagePaths.Count < backdropLimit)
             {
-                var numBackdrops = item.BackdropImagePaths.Count;
-
                 foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
                 {
-                    await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Backdrop, numBackdrops, cancellationToken)
+                    await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Backdrop, null, cancellationToken)
                                         .ConfigureAwait(false);
 
-                    numBackdrops++;
-
                     if (item.BackdropImagePaths.Count >= backdropLimit) break;
                 }
             }

+ 27 - 41
MediaBrowser.Providers/Music/LastFmImageProvider.cs

@@ -4,7 +4,10 @@ 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.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -55,11 +58,6 @@ namespace MediaBrowser.Providers.Music
                 return false;
             }
 
-            if (string.IsNullOrWhiteSpace(GetImageUrl(item)))
-            {
-                return false;
-            }
-
             return base.NeedsRefreshInternal(item, providerInfo);
         }
 
@@ -72,56 +70,44 @@ namespace MediaBrowser.Providers.Music
         /// <returns>Task{System.Boolean}.</returns>
         public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
         {
-            var url = GetImageUrl(item);
-
-            if (!string.IsNullOrWhiteSpace(url) && !item.HasImage(ImageType.Primary))
+            if (!item.HasImage(ImageType.Primary))
             {
-                await _providerManager.SaveImage(item, url, LastfmBaseProvider.LastfmResourcePool, ImageType.Primary, null, cancellationToken)
-                                    .ConfigureAwait(false);
+                var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualLastFmImageProvider.ProviderName).ConfigureAwait(false);
+
+                await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
             }
 
             SetLastRefreshed(item, DateTime.UtcNow);
+
             return true;
         }
 
-        /// <summary>
-        /// Gets the priority.
-        /// </summary>
-        /// <value>The priority.</value>
-        public override MetadataProviderPriority Priority
+        private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken)
         {
-            get { return MetadataProviderPriority.Fifth; }
-        }
+            cancellationToken.ThrowIfCancellationRequested();
 
-        /// <summary>
-        /// Gets the image URL.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>System.String.</returns>
-        private string GetImageUrl(BaseItem item)
-        {
-            var musicArtist = item as MusicArtist;
-
-            if (musicArtist != null)
-            {
-                return musicArtist.LastFmImageUrl;
-            }
+            var configSetting = item is MusicAlbum
+                ? ConfigurationManager.Configuration.DownloadMusicAlbumImages
+                : ConfigurationManager.Configuration.DownloadMusicArtistImages;
 
-            var artistByName = item as Artist;
-
-            if (artistByName != null)
+            if (configSetting.Primary && !item.HasImage(ImageType.Primary))
             {
-                return artistByName.LastFmImageUrl;
-            }
+                var image = images.FirstOrDefault(i => i.Type == ImageType.Primary);
 
-            var album = item as MusicAlbum;
-
-            if (album != null)
-            {
-                return album.LastFmImageUrl;
+                if (image != null)
+                {
+                    await _providerManager.SaveImage(item, image.Url, LastfmBaseProvider.LastfmResourcePool, ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
+                }
             }
+        }
 
-            return null;
+        /// <summary>
+        /// Gets the priority.
+        /// </summary>
+        /// <value>The priority.</value>
+        public override MetadataProviderPriority Priority
+        {
+            get { return MetadataProviderPriority.Fifth; }
         }
     }
 }

+ 1 - 1
MediaBrowser.Providers/Music/LastfmAlbumProvider.cs

@@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.Music
         {
             get
             {
-                return "8";
+                return "9";
             }
         }
 

+ 1 - 1
MediaBrowser.Providers/Music/LastfmArtistProvider.cs

@@ -58,7 +58,7 @@ namespace MediaBrowser.Providers.Music
         {
             get
             {
-                return "8";
+                return "9";
             }
         }
 

+ 18 - 7
MediaBrowser.Providers/Music/LastfmHelper.cs

@@ -34,21 +34,27 @@ namespace MediaBrowser.Providers.Music
 
             var musicArtist = artist as MusicArtist;
 
+            string imageSize; 
+
             if (musicArtist != null)
             {
-                musicArtist.LastFmImageUrl = GetImageUrl(data);
+                musicArtist.LastFmImageUrl = GetImageUrl(data, out imageSize);
+                musicArtist.LastFmImageSize = imageSize;
             }
 
             var artistByName = artist as Artist;
 
             if (artistByName != null)
             {
-                artistByName.LastFmImageUrl = GetImageUrl(data);
+                artistByName.LastFmImageUrl = GetImageUrl(data, out imageSize);
+                artistByName.LastFmImageSize = imageSize;
             }
         }
 
-        private static string GetImageUrl(IHasLastFmImages data)
+        private static string GetImageUrl(IHasLastFmImages data, out string size)
         {
+            size = null;
+
             if (data.image == null)
             {
                 return null;
@@ -61,12 +67,13 @@ namespace MediaBrowser.Providers.Music
             var img = validImages
                 .FirstOrDefault(i => string.Equals(i.size, "mega", StringComparison.OrdinalIgnoreCase)) ??
                 data.image.FirstOrDefault(i => string.Equals(i.size, "extralarge", StringComparison.OrdinalIgnoreCase)) ??
-                data.image.FirstOrDefault(i => string.Equals(i.size, "large", StringComparison.OrdinalIgnoreCase)) ?? 
-                data.image.FirstOrDefault(i => string.Equals(i.size, "medium", StringComparison.OrdinalIgnoreCase)) ?? 
+                data.image.FirstOrDefault(i => string.Equals(i.size, "large", StringComparison.OrdinalIgnoreCase)) ??
+                data.image.FirstOrDefault(i => string.Equals(i.size, "medium", StringComparison.OrdinalIgnoreCase)) ??
                 data.image.FirstOrDefault();
 
             if (img != null)
             {
+                size = img.size;
                 return img.url;
             }
 
@@ -81,7 +88,7 @@ namespace MediaBrowser.Providers.Music
             target.Overview = source.Overview;
             target.ProductionLocations = source.ProductionLocations.ToList();
         }
-        
+
         public static void ProcessAlbumData(BaseItem item, LastfmAlbum data)
         {
             if (!string.IsNullOrWhiteSpace(data.mbid)) item.SetProviderId(MetadataProviders.Musicbrainz, data.mbid);
@@ -107,7 +114,11 @@ namespace MediaBrowser.Providers.Music
             }
 
             var album = (MusicAlbum)item;
-            album.LastFmImageUrl = GetImageUrl(data);
+
+            string imageSize;
+
+            album.LastFmImageUrl = GetImageUrl(data, out imageSize);
+            album.LastFmImageSize = imageSize;
         }
 
         private static void AddTags(BaseItem item, LastfmTags tags)

+ 1 - 1
MediaBrowser.Providers/Music/ManualFanartAlbumProvider.cs

@@ -325,7 +325,7 @@ namespace MediaBrowser.Providers.Music
 
         public int Priority
         {
-            get { return 0; }
+            get { return 1; }
         }
     }
 }

+ 1 - 1
MediaBrowser.Providers/Music/ManualFanartArtistProvider.cs

@@ -334,7 +334,7 @@ namespace MediaBrowser.Providers.Music
 
         public int Priority
         {
-            get { return 0; }
+            get { return 1; }
         }
     }
 }

+ 110 - 0
MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs

@@ -0,0 +1,110 @@
+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.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.Music
+{
+    public class ManualLastFmImageProvider : IImageProvider
+    {
+        public string Name
+        {
+            get { return ProviderName; }
+        }
+
+        public static string ProviderName
+        {
+            get { return "last.fm"; }
+        }
+
+        public bool Supports(BaseItem item)
+        {
+            return item is MusicAlbum || item is MusicArtist || item is Artist;
+        }
+
+        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>();
+
+            RemoteImageInfo info = null;
+
+            var artist = item as Artist;
+
+            if (artist != null)
+            {
+                info = GetInfo(artist.LastFmImageUrl, artist.LastFmImageSize);
+            }
+
+            var album = item as MusicAlbum;
+            if (album != null)
+            {
+                info = GetInfo(album.LastFmImageUrl, album.LastFmImageSize);
+            }
+
+            var musicArtist = item as MusicArtist;
+            if (musicArtist != null)
+            {
+                info = GetInfo(musicArtist.LastFmImageUrl, musicArtist.LastFmImageSize);
+            }
+
+            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
+            };
+
+            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 Priority
+        {
+            get { return 0; }
+        }
+    }
+}

+ 10 - 6
MediaBrowser.Providers/Savers/XmlSaverHelpers.cs

@@ -209,14 +209,18 @@ namespace MediaBrowser.Providers.Savers
                 builder.Append("<Type>" + SecurityElement.Escape(item.DisplayMediaType) + "</Type>");
             }
 
-            if (item.CriticRating.HasValue)
+            var hasCriticRating = item as IHasCriticRating;
+            if (hasCriticRating != null)
             {
-                builder.Append("<CriticRating>" + SecurityElement.Escape(item.CriticRating.Value.ToString(UsCulture)) + "</CriticRating>");
-            }
+                if (hasCriticRating.CriticRating.HasValue)
+                {
+                    builder.Append("<CriticRating>" + SecurityElement.Escape(hasCriticRating.CriticRating.Value.ToString(UsCulture)) + "</CriticRating>");
+                }
 
-            if (!string.IsNullOrEmpty(item.CriticRatingSummary))
-            {
-                builder.Append("<CriticRatingSummary><![CDATA[" + item.CriticRatingSummary + "]]></CriticRatingSummary>");
+                if (!string.IsNullOrEmpty(hasCriticRating.CriticRatingSummary))
+                {
+                    builder.Append("<CriticRatingSummary><![CDATA[" + hasCriticRating.CriticRatingSummary + "]]></CriticRatingSummary>");
+                }
             }
 
             if (!string.IsNullOrEmpty(item.Overview))

+ 1 - 5
MediaBrowser.Providers/TV/FanArtTVProvider.cs

@@ -260,15 +260,11 @@ namespace MediaBrowser.Providers.TV
             if (ConfigurationManager.Configuration.DownloadSeriesImages.Backdrops &&
                 item.BackdropImagePaths.Count < backdropLimit)
             {
-                var numBackdrops = item.BackdropImagePaths.Count;
-
                 foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
                 {
-                    await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Backdrop, numBackdrops, cancellationToken)
+                    await _providerManager.SaveImage(item, image.Url, FanArtResourcePool, ImageType.Backdrop, null, cancellationToken)
                                         .ConfigureAwait(false);
 
-                    numBackdrops++;
-
                     if (item.BackdropImagePaths.Count >= backdropLimit) break;
                 }
             }

+ 1 - 5
MediaBrowser.Providers/TV/TvdbSeasonProvider.cs

@@ -181,8 +181,6 @@ namespace MediaBrowser.Providers.TV
 
             if (ConfigurationManager.Configuration.DownloadSeasonImages.Backdrops && item.BackdropImagePaths.Count < backdropLimit)
             {
-                var bdNo = item.BackdropImagePaths.Count;
-
                 foreach (var backdrop in images.Where(i => i.Type == ImageType.Backdrop))
                 {
                     var url = backdrop.Url;
@@ -192,9 +190,7 @@ namespace MediaBrowser.Providers.TV
                         continue;
                     }
 
-                    await _providerManager.SaveImage(item, url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, bdNo, cancellationToken).ConfigureAwait(false);
-
-                    bdNo++;
+                    await _providerManager.SaveImage(item, url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, null, cancellationToken).ConfigureAwait(false);
 
                     if (item.BackdropImagePaths.Count >= backdropLimit) break;
                 }

+ 1 - 5
MediaBrowser.Providers/TV/TvdbSeriesImageProvider.cs

@@ -191,8 +191,6 @@ namespace MediaBrowser.Providers.TV
 
             if (ConfigurationManager.Configuration.DownloadSeriesImages.Backdrops && item.BackdropImagePaths.Count < backdropLimit)
             {
-                var bdNo = item.BackdropImagePaths.Count;
-
                 foreach (var backdrop in images.Where(i => i.Type == ImageType.Backdrop && 
                     (!i.Width.HasValue || 
                     i.Width.Value >= ConfigurationManager.Configuration.MinSeriesBackdropDownloadWidth)))
@@ -204,9 +202,7 @@ namespace MediaBrowser.Providers.TV
                         continue;
                     }
 
-                    await _providerManager.SaveImage(item, url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, bdNo, cancellationToken).ConfigureAwait(false);
-
-                    bdNo++;
+                    await _providerManager.SaveImage(item, url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, null, cancellationToken).ConfigureAwait(false);
 
                     if (item.BackdropImagePaths.Count >= backdropLimit) break;
                 }

+ 82 - 49
MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs

@@ -3,11 +3,11 @@ using MediaBrowser.Common.IO;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.IO;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Serialization;
 using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
@@ -25,7 +25,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
     /// <summary>
     /// Class ImageProcessor
     /// </summary>
-    public class ImageProcessor : IImageProcessor
+    public class ImageProcessor : IImageProcessor, IDisposable
     {
         /// <summary>
         /// The us culture
@@ -35,7 +35,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
         /// <summary>
         /// The _cached imaged sizes
         /// </summary>
-        private readonly ConcurrentDictionary<string, ImageSize> _cachedImagedSizes = new ConcurrentDictionary<string, ImageSize>();
+        private readonly ConcurrentDictionary<Guid, ImageSize> _cachedImagedSizes;
 
         /// <summary>
         /// Gets the list of currently registered image processors
@@ -50,21 +50,41 @@ namespace MediaBrowser.Server.Implementations.Drawing
         private readonly ILogger _logger;
 
         private readonly IFileSystem _fileSystem;
+        private readonly IJsonSerializer _jsonSerializer;
+        private readonly IServerApplicationPaths _appPaths;
 
         private readonly string _imageSizeCachePath;
         private readonly string _croppedWhitespaceImageCachePath;
         private readonly string _enhancedImageCachePath;
         private readonly string _resizedImageCachePath;
 
-        public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem)
+        public ImageProcessor(ILogger logger, IServerApplicationPaths appPaths, IFileSystem fileSystem, IJsonSerializer jsonSerializer)
         {
             _logger = logger;
             _fileSystem = fileSystem;
+            _jsonSerializer = jsonSerializer;
+            _appPaths = appPaths;
 
             _imageSizeCachePath = Path.Combine(appPaths.ImageCachePath, "image-sizes");
             _croppedWhitespaceImageCachePath = Path.Combine(appPaths.ImageCachePath, "cropped-images");
             _enhancedImageCachePath = Path.Combine(appPaths.ImageCachePath, "enhanced-images");
             _resizedImageCachePath = Path.Combine(appPaths.ImageCachePath, "resized-images");
+
+            _saveImageSizeTimer = new Timer(SaveImageSizeCallback, null, Timeout.Infinite, Timeout.Infinite);
+
+            Dictionary<Guid, ImageSize> sizeDictionary;
+
+            try
+            {
+                sizeDictionary = jsonSerializer.DeserializeFromFile<Dictionary<Guid, ImageSize>>(ImageSizeFile);
+            }
+            catch (IOException)
+            {
+                // No biggie
+                sizeDictionary = new Dictionary<Guid, ImageSize>();
+            }
+
+            _cachedImagedSizes = new ConcurrentDictionary<Guid, ImageSize>(sizeDictionary);
         }
 
         public void AddParts(IEnumerable<IImageEnhancer> enhancers)
@@ -85,6 +105,17 @@ namespace MediaBrowser.Server.Implementations.Drawing
             }
 
             var originalImagePath = options.OriginalImagePath;
+
+            if (options.HasDefaultOptions())
+            {
+                // Just spit out the original file if all the options are default
+                using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+                {
+                    await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
+                    return;
+                }
+            }
+
             var dateModified = options.OriginalImageDateModified;
 
             if (options.CropWhiteSpace)
@@ -106,6 +137,16 @@ namespace MediaBrowser.Server.Implementations.Drawing
             // Determine the output size based on incoming parameters
             var newSize = DrawingUtils.Resize(originalImageSize, options.Width, options.Height, options.MaxWidth, options.MaxHeight);
 
+            if (options.HasDefaultOptionsWithoutSize() && newSize.Equals(originalImageSize))
+            {
+                // Just spit out the original file the new size equals the old
+                using (var fileStream = _fileSystem.GetFileStream(originalImagePath, FileMode.Open, FileAccess.Read, FileShare.Read, true))
+                {
+                    await fileStream.CopyToAsync(toStream).ConfigureAwait(false);
+                    return;
+                }
+            }
+
             var quality = options.Quality ?? 90;
 
             var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, options.OutputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.BackgroundColor);
@@ -465,11 +506,13 @@ namespace MediaBrowser.Server.Implementations.Drawing
 
             ImageSize size;
 
-            if (!_cachedImagedSizes.TryGetValue(name, out size))
+            var cacheHash = name.GetMD5();
+
+            if (!_cachedImagedSizes.TryGetValue(cacheHash, out size))
             {
-                size = GetImageSizeInternal(name, path);
+                size = GetImageSizeInternal(path);
 
-                _cachedImagedSizes.AddOrUpdate(name, size, (keyName, oldValue) => size);
+                _cachedImagedSizes.AddOrUpdate(cacheHash, size, (keyName, oldValue) => size);
             }
 
             return size;
@@ -478,62 +521,47 @@ namespace MediaBrowser.Server.Implementations.Drawing
         /// <summary>
         /// Gets the image size internal.
         /// </summary>
-        /// <param name="cacheKey">The cache key.</param>
         /// <param name="path">The path.</param>
         /// <returns>ImageSize.</returns>
-        private ImageSize GetImageSizeInternal(string cacheKey, string path)
+        private ImageSize GetImageSizeInternal(string path)
         {
-            // Now check the file system cache
-            var fullCachePath = GetCachePath(_imageSizeCachePath, cacheKey, ".txt");
+            var size = ImageHeader.GetDimensions(path, _logger, _fileSystem);
 
-            try
-            {
-                var result = File.ReadAllText(fullCachePath).Split('|');
+            StartSaveImageSizeTimer();
 
-                return new ImageSize
-                {
-                    Width = double.Parse(result[0], UsCulture),
-                    Height = double.Parse(result[1], UsCulture)
-                };
-            }
-            catch (IOException)
-            {
-                // Cache file doesn't exist or is currently being written to
-            }
+            return new ImageSize { Width = size.Width, Height = size.Height };
+        }
 
-            var syncLock = GetObjectLock(fullCachePath);
+        private readonly Timer _saveImageSizeTimer;
+        private const int SaveImageSizeTimeout = 5000;
+        private readonly object _saveImageSizeLock = new object();
+        private void StartSaveImageSizeTimer()
+        {
+            _saveImageSizeTimer.Change(SaveImageSizeTimeout, Timeout.Infinite);
+        }
 
-            lock (syncLock)
+        private void SaveImageSizeCallback(object state)
+        {
+            lock (_saveImageSizeLock)
             {
                 try
                 {
-                    var result = File.ReadAllText(fullCachePath).Split('|');
-
-                    return new ImageSize
-                    {
-                        Width = double.Parse(result[0], UsCulture),
-                        Height = double.Parse(result[1], UsCulture)
-                    };
-                }
-                catch (FileNotFoundException)
-                {
-                    // Cache file doesn't exist no biggie
+                    var path = ImageSizeFile;
+                    Directory.CreateDirectory(Path.GetDirectoryName(path));
+                    _jsonSerializer.SerializeToFile(_cachedImagedSizes, path);
                 }
-                catch (DirectoryNotFoundException)
+                catch (Exception ex)
                 {
-                    // Cache file doesn't exist no biggie
+                    _logger.ErrorException("Error saving image size file", ex);
                 }
+            }
+        }
 
-                var size = ImageHeader.GetDimensions(path, _logger, _fileSystem);
-
-                var parentPath = Path.GetDirectoryName(fullCachePath);
-
-                Directory.CreateDirectory(parentPath);
-
-                // Update the file system cache
-                File.WriteAllText(fullCachePath, size.Width.ToString(UsCulture) + @"|" + size.Height.ToString(UsCulture));
-
-                return new ImageSize { Width = size.Width, Height = size.Height };
+        private string ImageSizeFile
+        {
+            get
+            {
+                return Path.Combine(_appPaths.DataPath, "imagesizes.json");
             }
         }
 
@@ -862,5 +890,10 @@ namespace MediaBrowser.Server.Implementations.Drawing
 
             }).ToList();
         }
+
+        public void Dispose()
+        {
+            _saveImageSizeTimer.Dispose();
+        }
     }
 }

+ 8 - 4
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -792,11 +792,15 @@ namespace MediaBrowser.Server.Implementations.Dto
             dto.MediaType = item.MediaType;
             dto.LocationType = item.LocationType;
 
-            dto.CriticRating = item.CriticRating;
-
-            if (fields.Contains(ItemFields.CriticRatingSummary))
+            var hasCriticRating = item as IHasCriticRating;
+            if (hasCriticRating != null)
             {
-                dto.CriticRatingSummary = item.CriticRatingSummary;
+                dto.CriticRating = hasCriticRating.CriticRating;
+
+                if (fields.Contains(ItemFields.CriticRatingSummary))
+                {
+                    dto.CriticRatingSummary = hasCriticRating.CriticRatingSummary;
+                }
             }
 
             var localTrailerCount = item.LocalTrailerIds.Count;

+ 3 - 0
MediaBrowser.Server.Implementations/HttpServer/HttpServer.cs

@@ -181,6 +181,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
         /// <param name="dto">The dto.</param>
         private void FilterResponse(IHttpRequest req, IHttpResponse res, object dto)
         {
+            // Try to prevent compatibility view
+            res.AddHeader("X-UA-Compatible", "IE=Edge");
+            
             var exception = dto as Exception;
 
             if (exception != null)

+ 10 - 6
MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs

@@ -363,6 +363,7 @@ namespace MediaBrowser.Server.Implementations.IO
             {
                 if (string.Equals(i, e.FullPath, StringComparison.OrdinalIgnoreCase))
                 {
+                    Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
                     return true;
                 }
 
@@ -370,6 +371,7 @@ namespace MediaBrowser.Server.Implementations.IO
                 var parent = Path.GetDirectoryName(i);
                 if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
                 {
+                    Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
                     return true;
                 }
 
@@ -379,10 +381,18 @@ namespace MediaBrowser.Server.Implementations.IO
                     parent = Path.GetDirectoryName(i);
                     if (string.Equals(parent, e.FullPath, StringComparison.OrdinalIgnoreCase))
                     {
+                        Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
                         return true;
                     }
                 }
 
+                if (i.StartsWith(e.FullPath, StringComparison.OrdinalIgnoreCase) || 
+                    e.FullPath.StartsWith(i, StringComparison.OrdinalIgnoreCase))
+                {
+                    Logger.Debug("Watcher ignoring change to {0}", e.FullPath);
+                    return true;
+                }
+
                 return false;
 
             }))
@@ -390,12 +400,6 @@ namespace MediaBrowser.Server.Implementations.IO
                 return;
             }
 
-            if (tempIgnorePaths.Contains(e.FullPath, StringComparer.OrdinalIgnoreCase))
-            {
-                Logger.Debug("Watcher requested to ignore change to " + e.FullPath);
-                return;
-            }
-
             Logger.Info("Watcher sees change of type " + e.ChangeType + " to " + e.FullPath);
 
             //Since we're watching created, deleted and renamed we always want the parent of the item to be the affected path

+ 11 - 2
MediaBrowser.Server.Implementations/Providers/ImageSaver.cs

@@ -1,5 +1,4 @@
-using System.Collections.Generic;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
@@ -8,6 +7,7 @@ using MediaBrowser.Controller.IO;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 using System;
+using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
@@ -70,6 +70,15 @@ namespace MediaBrowser.Server.Implementations.Providers
                 throw new ArgumentNullException("mimeType");
             }
 
+            if (type == ImageType.Backdrop && imageIndex == null)
+            {
+                imageIndex = item.BackdropImagePaths.Count;
+            }
+            else if (type == ImageType.Screenshot && imageIndex == null)
+            {
+                imageIndex = item.ScreenshotImagePaths.Count;
+            }
+
             var saveLocally = _config.Configuration.SaveLocalMeta && item.Parent != null && !(item is Audio);
 
             if (item is IItemByName || item is User)

+ 3 - 1
MediaBrowser.Server.Implementations/Sorting/CriticRatingComparer.cs

@@ -22,7 +22,9 @@ namespace MediaBrowser.Server.Implementations.Sorting
 
         private float GetValue(BaseItem x)
         {
-            return x.CriticRating ?? 0;
+            var hasCriticRating = x as IHasCriticRating;
+
+            return hasCriticRating == null ? 0 : hasCriticRating.CriticRating ?? 0;
         }
 
         /// <summary>

+ 1 - 0
MediaBrowser.ServerApplication/App.config

@@ -19,6 +19,7 @@
     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
   </startup>
   <runtime>
+    <gcAllowVeryLargeObjects enabled="true" />
     <gcServer enabled="true" />
     <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
       <dependentAssembly>

+ 1 - 1
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -288,7 +288,7 @@ namespace MediaBrowser.ServerApplication
             LocalizationManager = new LocalizationManager(ServerConfigurationManager, FileSystemManager);
             RegisterSingleInstance(LocalizationManager);
 
-            ImageProcessor = new ImageProcessor(Logger, ServerConfigurationManager.ApplicationPaths, FileSystemManager);
+            ImageProcessor = new ImageProcessor(Logger, ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer);
             RegisterSingleInstance(ImageProcessor);
 
             DtoService = new DtoService(Logger, LibraryManager, UserManager, UserDataManager, ItemRepository, ImageProcessor);

+ 1 - 1
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -375,9 +375,9 @@ namespace MediaBrowser.WebDashboard.Api
         {
             var sb = new StringBuilder();
 
+            sb.Append("<meta http-equiv=\"X-UA-Compatibility\" content=\"IE=Edge\">");
             sb.Append("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\">");
             sb.Append("<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">");
-            sb.Append("<meta http-equiv=\"X-UA-Compatibility\" content=\"IE=Edge\">");
             //sb.Append("<meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">");
 
             // http://developer.apple.com/library/ios/#DOCUMENTATION/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.237</version>
+        <version>3.0.238</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.237" />
+            <dependency id="MediaBrowser.Common" version="3.0.238" />
             <dependency id="NLog" version="2.1.0" />
             <dependency id="ServiceStack.Text" version="3.9.58" />
             <dependency id="SimpleInjector" version="2.3.6" />

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.237</version>
+        <version>3.0.238</version>
         <title>MediaBrowser.Common</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.237</version>
+        <version>3.0.238</version>
         <title>Media Browser.Server.Core</title>
         <authors>Media Browser Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Media Browser Server.</description>
         <copyright>Copyright © Media Browser 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.237" />
+            <dependency id="MediaBrowser.Common" version="3.0.238" />
         </dependencies>
     </metadata>
     <files>