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

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

Sven Van den brande 11 лет назад
Родитель
Сommit
09d7bc00c2

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

@@ -288,9 +288,6 @@ namespace MediaBrowser.Api.DefaultTheme
 
             var view = new TvView();
 
-            SetFavoriteGenres(view, series, user);
-            SetFavoriteStudios(view, series, user);
-
             var fields = new List<ItemFields>();
 
             var seriesWithBestBackdrops = FilterItemsForBackdropDisplay(seriesWithBackdrops).ToList();
@@ -401,146 +398,6 @@ namespace MediaBrowser.Api.DefaultTheme
             return ToOptimizedResult(view);
         }
 
-        private void SetFavoriteGenres(TvView 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.SeriesCount;
-
-                    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(TvView 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.SeriesCount;
-
-                    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();
-        }
-
-        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);
@@ -557,9 +414,6 @@ namespace MediaBrowser.Api.DefaultTheme
             var movies = items.OfType<Movie>()
                 .ToList();
 
-            SetFavoriteGenres(view, movies, user);
-            SetFavoriteStudios(view, movies, user);
-            
             var trailers = items.OfType<Trailer>()
                .ToList();
             

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

@@ -34,9 +34,6 @@ 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
@@ -47,8 +44,6 @@ namespace MediaBrowser.Api.DefaultTheme
         public List<ItemStub> RomanceItems { get; set; }
         public List<ItemStub> ComedyItems { get; set; }
 
-        public List<ItemByNameInfo> FavoriteGenres { get; set; }
-        public List<ItemByNameInfo> FavoriteStudios { get; set; }
         public List<string> SeriesIdsInProgress { get; set; }
 
         public List<BaseItemDto> LatestEpisodes { get; set; }

+ 82 - 3
MediaBrowser.Api/PackageReviewService.cs

@@ -1,9 +1,12 @@
 using System.Collections.Generic;
 using System.Globalization;
+using System.Net;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Common.Constants;
 using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Serialization;
 using ServiceStack.ServiceHost;
 
 namespace MediaBrowser.Api
@@ -51,27 +54,103 @@ namespace MediaBrowser.Api
         public string Review { get; set; }
     }
 
+    /// <summary>
+    /// Class InstallPackage
+    /// </summary>
+    [Route("/PackageReviews/{Id}", "GET")]
+    [Api(("Retrieve reviews for a package"))]
+    public class ReviewRequest : IReturn<List<PackageReviewInfo>>
+    {
+        /// <summary>
+        /// Gets or sets the Id.
+        /// </summary>
+        /// <value>The Id.</value>
+        [ApiMember(Name = "Id", Description = "Package Id", IsRequired = true, DataType = "int", ParameterType = "path", Verb = "GET")]
+        public int Id { get; set; }
+
+        /// <summary>
+        /// Gets or sets the max rating.
+        /// </summary>
+        /// <value>The max rating.</value>
+        [ApiMember(Name = "MaxRating", Description = "Retrieve only reviews less than or equal to this", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int MaxRating { get; set; }
+
+        /// <summary>
+        /// Gets or sets the min rating.
+        /// </summary>
+        /// <value>The max rating.</value>
+        [ApiMember(Name = "MinRating", Description = "Retrieve only reviews greator than or equal to this", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int MinRating { get; set; }
+
+        /// <summary>
+        /// Only retrieve reviews with at least a short review.
+        /// </summary>
+        /// <value>True if should only get reviews with a title.</value>
+        [ApiMember(Name = "ForceTitle", Description = "Whether or not to restrict results to those with a title", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
+        public bool ForceTitle { get; set; }
+
+        /// <summary>
+        /// Gets or sets the limit for the query.
+        /// </summary>
+        /// <value>The max rating.</value>
+        [ApiMember(Name = "Limit", Description = "Limit the result to this many reviews (ordered by latest)", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int Limit { get; set; }
+
+    }
 
     public class PackageReviewService : BaseApiService
     {
         private readonly IHttpClient _httpClient;
         private readonly INetworkManager _netManager;
+        private readonly IJsonSerializer _serializer;
 
-        public PackageReviewService(IHttpClient client, INetworkManager net)
+        public PackageReviewService(IHttpClient client, INetworkManager net, IJsonSerializer serializer)
         {
             _httpClient = client;
             _netManager = net;
+            _serializer = serializer;
+        }
+
+        public object Get(ReviewRequest request)
+        {
+            var parms = "?id=" + request.Id;
+            
+            if (request.MaxRating > 0)
+            {
+                parms += "&max=" + request.MaxRating;
+            }
+            if (request.MinRating > 0)
+            {
+                parms += "&min=" + request.MinRating;
+            }
+            if (request.MinRating > 0)
+            {
+                parms += "&limit=" + request.Limit;
+            }
+            if (request.ForceTitle)
+            {
+                parms += "&title=true";
+            }
+
+            var result = _httpClient.Get(Constants.MbAdminUrl + "/service/packageReview/retrieve"+parms, CancellationToken.None).Result;
+
+            var reviews = _serializer.DeserializeFromStream<List<PackageReviewInfo>>(result);
+
+            return ToOptimizedResult(reviews);
         }
 
         public void Post(CreateReviewRequest request)
         {
+            var reviewText = WebUtility.HtmlEncode(request.Review ?? string.Empty);
+            var title = WebUtility.HtmlEncode(request.Title ?? string.Empty);
+
             var review = new Dictionary<string, string>
                              { { "id", request.Id.ToString(CultureInfo.InvariantCulture) },
                                { "mac", _netManager.GetMacAddress() },
                                { "rating", request.Rating.ToString(CultureInfo.InvariantCulture) },
                                { "recommend", request.Recommend.ToString() },
-                               { "title", request.Title },
-                               { "review", request.Review },
+                               { "title", title },
+                               { "review", reviewText },
                              };
 
             Task.WaitAll(_httpClient.Post(Constants.MbAdminUrl + "/service/packageReview/update", review, CancellationToken.None));

+ 39 - 0
MediaBrowser.Common.Implementations/Archiving/ZipClient.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Model.IO;
+using SharpCompress.Archive.Rar;
 using SharpCompress.Archive.SevenZip;
 using SharpCompress.Archive.Tar;
 using SharpCompress.Common;
@@ -123,5 +124,43 @@ namespace MediaBrowser.Common.Implementations.Archiving
                 }
             }
         }
+
+        /// <summary>
+        /// Extracts all from rar.
+        /// </summary>
+        /// <param name="sourceFile">The source file.</param>
+        /// <param name="targetPath">The target path.</param>
+        /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
+        public void ExtractAllFromRar(string sourceFile, string targetPath, bool overwriteExistingFiles)
+        {
+            using (var fileStream = File.OpenRead(sourceFile))
+            {
+                ExtractAllFromRar(fileStream, targetPath, overwriteExistingFiles);
+            }
+        }
+
+        /// <summary>
+        /// Extracts all from rar.
+        /// </summary>
+        /// <param name="source">The source.</param>
+        /// <param name="targetPath">The target path.</param>
+        /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
+        public void ExtractAllFromRar(Stream source, string targetPath, bool overwriteExistingFiles)
+        {
+            using (var archive = RarArchive.Open(source))
+            {
+                using (var reader = archive.ExtractAllEntries())
+                {
+                    var options = ExtractOptions.ExtractFullPath;
+
+                    if (overwriteExistingFiles)
+                    {
+                        options = options | ExtractOptions.Overwrite;
+                    }
+
+                    reader.WriteAllToDirectory(targetPath, options);
+                }
+            }
+        }
     }
 }

+ 1 - 1
MediaBrowser.Controller/Drawing/IImageProcessor.cs

@@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Drawing
         /// <param name="imageEnhancers">The image enhancers.</param>
         /// <returns>Guid.</returns>
         Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified,
-                              IEnumerable<IImageEnhancer> imageEnhancers);
+                              List<IImageEnhancer> imageEnhancers);
 
         /// <summary>
         /// Processes the image.

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

@@ -383,6 +383,12 @@ namespace MediaBrowser.Model.Dto
         /// <value>The album image tag.</value>
         public Guid? AlbumPrimaryImageTag { get; set; }
 
+        /// <summary>
+        /// Gets or sets the series primary image tag.
+        /// </summary>
+        /// <value>The series primary image tag.</value>
+        public Guid? SeriesPrimaryImageTag { get; set; }
+        
         /// <summary>
         /// Gets or sets the album artist.
         /// </summary>

+ 5 - 0
MediaBrowser.Model/Entities/PackageReviewInfo.cs

@@ -33,5 +33,10 @@ namespace MediaBrowser.Model.Entities
         /// </summary>
         public string review { get; set; }
 
+        /// <summary>
+        /// Time of review
+        /// </summary>
+        public DateTime timestamp { get; set; }
+
     }
 }

+ 16 - 0
MediaBrowser.Model/IO/IZipClient.cs

@@ -54,5 +54,21 @@ namespace MediaBrowser.Model.IO
         /// <param name="targetPath">The target path.</param>
         /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
         void ExtractAllFromTar(Stream source, string targetPath, bool overwriteExistingFiles);
+
+        /// <summary>
+        /// Extracts all from rar.
+        /// </summary>
+        /// <param name="sourceFile">The source file.</param>
+        /// <param name="targetPath">The target path.</param>
+        /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
+        void ExtractAllFromRar(string sourceFile, string targetPath, bool overwriteExistingFiles);
+
+        /// <summary>
+        /// Extracts all from rar.
+        /// </summary>
+        /// <param name="source">The source.</param>
+        /// <param name="targetPath">The target path.</param>
+        /// <param name="overwriteExistingFiles">if set to <c>true</c> [overwrite existing files].</param>
+        void ExtractAllFromRar(Stream source, string targetPath, bool overwriteExistingFiles);
     }
 }

+ 0 - 6
MediaBrowser.Model/Updates/PackageInfo.cs

@@ -117,12 +117,6 @@ namespace MediaBrowser.Model.Updates
         /// <value>The name.</value>
         public string guid { get; set; }
 
-        /// <summary>
-        /// Gets or sets the total number of machines who have checked registration for this package (if premium).
-        /// </summary>
-        /// <value>The total hits.</value>
-        public int totalHits { get; set; }
-
         /// <summary>
         /// Gets or sets the total number of ratings for this package.
         /// </summary>

+ 14 - 5
MediaBrowser.Providers/Music/LastfmHelper.cs

@@ -25,8 +25,13 @@ namespace MediaBrowser.Providers.Music
                 }
             }
 
-            artist.PremiereDate = yearFormed > 0 ? new DateTime(yearFormed, 1, 1, 0, 0, 0, DateTimeKind.Utc) : (DateTime?)null;
-            artist.ProductionYear = yearFormed;
+            if (yearFormed > 0)
+            {
+                artist.PremiereDate = new DateTime(yearFormed, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+
+                artist.ProductionYear = yearFormed;
+            }
+            
             if (data.tags != null && !artist.LockedFields.Contains(MetadataFields.Tags))
             {
                 AddTags(artist, data.tags);
@@ -102,10 +107,14 @@ namespace MediaBrowser.Providers.Music
 
             DateTime release;
 
-            if (DateTime.TryParse(data.releasedate, out release) && release.Year != 1901)
+            if (DateTime.TryParse(data.releasedate, out release))
             {
-                item.PremiereDate = release;
-                item.ProductionYear = release.Year;
+                // Lastfm sends back null as sometimes 1901, other times 0
+                if (release.Year > 1901)
+                {
+                    item.PremiereDate = release;
+                    item.ProductionYear = release.Year;
+                }
             }
 
             if (data.toptags != null && !item.LockedFields.Contains(MetadataFields.Tags))

+ 10 - 4
MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs

@@ -589,7 +589,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
 
             var supportedEnhancers = GetSupportedEnhancers(item, imageType);
 
-            return GetImageCacheTag(item, imageType, imagePath, dateModified, supportedEnhancers);
+            return GetImageCacheTag(item, imageType, imagePath, dateModified, supportedEnhancers.ToList());
         }
 
         /// <summary>
@@ -602,7 +602,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
         /// <param name="imageEnhancers">The image enhancers.</param>
         /// <returns>Guid.</returns>
         /// <exception cref="System.ArgumentNullException">item</exception>
-        public Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified, IEnumerable<IImageEnhancer> imageEnhancers)
+        public Guid GetImageCacheTag(BaseItem item, ImageType imageType, string originalImagePath, DateTime dateModified, List<IImageEnhancer> imageEnhancers)
         {
             if (item == null)
             {
@@ -619,6 +619,12 @@ namespace MediaBrowser.Server.Implementations.Drawing
                 throw new ArgumentNullException("originalImagePath");
             }
 
+            // Optimization
+            if (imageEnhancers.Count == 0)
+            {
+                return (originalImagePath + dateModified.Ticks).GetMD5();
+            }
+
             // Cache name is created with supported enhancers combined with the last config change so we pick up new config changes
             var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
             cacheKeys.Add(originalImagePath + dateModified.Ticks);
@@ -879,7 +885,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
             {
                 try
                 {
-                    return i.Supports(item as BaseItem, imageType);
+                    return i.Supports(item, imageType);
                 }
                 catch (Exception ex)
                 {
@@ -888,7 +894,7 @@ namespace MediaBrowser.Server.Implementations.Drawing
                     return false;
                 }
 
-            }).ToList();
+            });
         }
 
         public void Dispose()

+ 16 - 1
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -165,7 +165,8 @@ namespace MediaBrowser.Server.Implementations.Dto
             {
                 var folder = (Folder)item;
 
-                dto.ChildCount = folder.GetChildren(user, true).Count();
+                dto.ChildCount = folder.GetChildren(user, true)
+                    .Count();
 
                 if (!(folder is UserRootFolder))
                 {
@@ -1051,6 +1052,13 @@ namespace MediaBrowser.Server.Implementations.Dto
                 {
                     dto.SeriesThumbImageTag = GetImageCacheTag(series, ImageType.Thumb, series.GetImage(ImageType.Thumb));
                 }
+
+                var imagePath = series.PrimaryImagePath;
+
+                if (!string.IsNullOrEmpty(imagePath))
+                {
+                    dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary, imagePath);
+                }
             }
 
             // Add SeasonInfo
@@ -1064,6 +1072,13 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.SeriesName = series.Name;
                 dto.AirTime = series.AirTime;
                 dto.SeriesStudio = series.Studios.FirstOrDefault();
+
+                var imagePath = series.PrimaryImagePath;
+
+                if (!string.IsNullOrEmpty(imagePath))
+                {
+                    dto.SeriesPrimaryImageTag = GetImageCacheTag(series, ImageType.Primary, imagePath);
+                }
             }
 
             var game = item as Game;

+ 2 - 1
MediaBrowser.Server.Implementations/IO/DirectoryWatchers.cs

@@ -67,7 +67,8 @@ namespace MediaBrowser.Server.Implementations.IO
         public async void RemoveTempIgnore(string path)
         {
             // This is an arbitraty amount of time, but delay it because file system writes often trigger events after RemoveTempIgnore has been called. 
-            await Task.Delay(2000).ConfigureAwait(false);
+            // Seeing long delays in some situations, especially over the network.
+            await Task.Delay(40000).ConfigureAwait(false);
 
             string val;
             _tempIgnoredPaths.TryRemove(path, out val);

+ 2 - 1
MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs

@@ -25,7 +25,8 @@ namespace MediaBrowser.Server.Implementations.Library
             "ps3_vprm",
             "adv_obj",
             "extrafanart",
-            "extrathumbs"
+            "extrathumbs",
+            ".actors"
 
         }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
 

+ 0 - 0
MediaBrowser.Server.Implementations/Localization/Ratings/da.txt → MediaBrowser.Server.Implementations/Localization/Ratings/dk.txt


+ 1 - 1
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -250,7 +250,7 @@
     <EmbeddedResource Include="Localization\Ratings\gb.txt" />
     <EmbeddedResource Include="Localization\Ratings\nl.txt" />
     <EmbeddedResource Include="Localization\Ratings\br.txt" />
-    <EmbeddedResource Include="Localization\Ratings\da.txt" />
+    <EmbeddedResource Include="Localization\Ratings\dk.txt" />
     <EmbeddedResource Include="Localization\Ratings\de.txt" />
     <EmbeddedResource Include="Localization\Ratings\mx.txt" />
     <EmbeddedResource Include="Localization\Ratings\co.txt" />

+ 10 - 1
MediaBrowser.Server.Implementations/Providers/ImageSaver.cs

@@ -13,6 +13,7 @@ using System.IO;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Logging;
 
 namespace MediaBrowser.Server.Implementations.Providers
 {
@@ -37,17 +38,19 @@ namespace MediaBrowser.Server.Implementations.Providers
         /// </summary>
         private readonly IDirectoryWatchers _directoryWatchers;
         private readonly IFileSystem _fileSystem;
+        private readonly ILogger _logger;
 
         /// <summary>
         /// Initializes a new instance of the <see cref="ImageSaver"/> class.
         /// </summary>
         /// <param name="config">The config.</param>
         /// <param name="directoryWatchers">The directory watchers.</param>
-        public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem)
+        public ImageSaver(IServerConfigurationManager config, IDirectoryWatchers directoryWatchers, IFileSystem fileSystem, ILogger logger)
         {
             _config = config;
             _directoryWatchers = directoryWatchers;
             _fileSystem = fileSystem;
+            _logger = logger;
             _remoteImageCache = new FileSystemRepository(config.ApplicationPaths.DownloadedImagesDataPath);
         }
 
@@ -170,7 +173,12 @@ namespace MediaBrowser.Server.Implementations.Providers
         /// <returns>Task.</returns>
         private async Task SaveImageToLocation(Stream source, string path, CancellationToken cancellationToken)
         {
+            _logger.Debug("Saving image to {0}", path);
+
+            var parentFolder = Path.GetDirectoryName(path);
+
             _directoryWatchers.TemporarilyIgnore(path);
+            _directoryWatchers.TemporarilyIgnore(parentFolder);
 
             try
             {
@@ -196,6 +204,7 @@ namespace MediaBrowser.Server.Implementations.Providers
             finally
             {
                 _directoryWatchers.RemoveTempIgnore(path);
+                _directoryWatchers.RemoveTempIgnore(parentFolder);
             }
         }
 

+ 1 - 1
MediaBrowser.Server.Implementations/Providers/ProviderManager.cs

@@ -349,7 +349,7 @@ namespace MediaBrowser.Server.Implementations.Providers
         /// <returns>Task.</returns>
         public Task SaveImage(BaseItem item, Stream source, string mimeType, ImageType type, int? imageIndex, string sourceUrl, CancellationToken cancellationToken)
         {
-            return new ImageSaver(ConfigurationManager, _directoryWatchers, _fileSystem).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken);
+            return new ImageSaver(ConfigurationManager, _directoryWatchers, _fileSystem, _logger).SaveImage(item, source, mimeType, type, imageIndex, sourceUrl, cancellationToken);
         }
 
         /// <summary>

+ 73 - 94
MediaBrowser.WebDashboard/ApiClient.js

@@ -53,20 +53,15 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if ($.browser.chrome) {
                 name = "Chrome";
-            }
-            else if ($.browser.safari) {
+            } else if ($.browser.safari) {
                 name = "Safari";
-            }
-            else if ($.browser.webkit) {
+            } else if ($.browser.webkit) {
                 name = "WebKit";
-            }
-            else if ($.browser.msie) {
+            } else if ($.browser.msie) {
                 name = "Internet Explorer";
-            }
-            else if ($.browser.opera) {
+            } else if ($.browser.opera) {
                 name = "Opera";
-            }
-            else if ($.browser.firefox || $.browser.mozilla) {
+            } else if ($.browser.firefox || $.browser.mozilla) {
                 name = "Firefox";
             }
 
@@ -74,18 +69,15 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 if ($.browser.version) {
                     name += " " + $.browser.version;
                 }
-            }
-            else {
+            } else {
                 name = "Web Browser";
             }
 
             if ($.browser.ipad) {
                 name += " Ipad";
-            }
-            else if ($.browser.iphone) {
+            } else if ($.browser.iphone) {
                 name += " Iphone";
-            }
-            else if ($.browser.android) {
+            } else if ($.browser.android) {
                 name += " Android";
             }
             return name;
@@ -313,28 +305,22 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             if (options.artist) {
                 urlPrefix = "Artists/" + self.encodeName(options.artist);
                 delete options.artist;
-            }
-            else if (options.person) {
+            } else if (options.person) {
                 urlPrefix = "Persons/" + self.encodeName(options.person);
                 delete options.person;
-            }
-            else if (options.genre) {
+            } else if (options.genre) {
                 urlPrefix = "Genres/" + self.encodeName(options.genre);
                 delete options.genre;
-            }
-            else if (options.musicGenre) {
+            } else if (options.musicGenre) {
                 urlPrefix = "MusicGenres/" + self.encodeName(options.musicGenre);
                 delete options.musicGenre;
-            }
-            else if (options.gameGenre) {
+            } else if (options.gameGenre) {
                 urlPrefix = "GameGenres/" + self.encodeName(options.gameGenre);
                 delete options.gameGenre;
-            }
-            else if (options.studio) {
+            } else if (options.studio) {
                 urlPrefix = "Studios/" + self.encodeName(options.studio);
                 delete options.studio;
-            }
-            else {
+            } else {
                 urlPrefix = "Items/" + options.itemId;
                 delete options.itemId;
             }
@@ -624,10 +610,8 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var url = self.getUrl("Items/" + itemId + "/Refresh", {
-
                 forced: force || false,
                 recursive: recursive || false
-
             });
 
             return self.ajax({
@@ -643,9 +627,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var url = self.getUrl("Artists/" + self.encodeName(name) + "/Refresh", {
-
                 forced: force || false
-
             });
 
             return self.ajax({
@@ -661,9 +643,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var url = self.getUrl("Genres/" + self.encodeName(name) + "/Refresh", {
-
                 forced: force || false
-
             });
 
             return self.ajax({
@@ -679,9 +659,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var url = self.getUrl("MusicGenres/" + self.encodeName(name) + "/Refresh", {
-
                 forced: force || false
-
             });
 
             return self.ajax({
@@ -697,9 +675,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var url = self.getUrl("GameGenres/" + self.encodeName(name) + "/Refresh", {
-
                 forced: force || false
-
             });
 
             return self.ajax({
@@ -715,9 +691,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var url = self.getUrl("Persons/" + self.encodeName(name) + "/Refresh", {
-
                 forced: force || false
-
             });
 
             return self.ajax({
@@ -733,9 +707,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var url = self.getUrl("Studios/" + self.encodeName(name) + "/Refresh", {
-
                 forced: force || false
-
             });
 
             return self.ajax({
@@ -1122,7 +1094,6 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             url += "/" + virtualFolderName + "/Paths";
 
             url = self.getUrl(url, {
-
                 refreshLibrary: refreshLibrary ? true : false,
                 path: mediaPath
             });
@@ -1152,7 +1123,6 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             url += "/" + virtualFolderName + "/Paths";
 
             url = self.getUrl(url, {
-
                 refreshLibrary: refreshLibrary ? true : false,
                 path: mediaPath
             });
@@ -1222,23 +1192,17 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if (itemType == "Artist") {
                 url = self.getUrl("Artists/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Genre") {
+            } else if (itemType == "Genre") {
                 url = self.getUrl("Genres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "GameGenre") {
+            } else if (itemType == "GameGenre") {
                 url = self.getUrl("GameGenres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "MusicGenre") {
+            } else if (itemType == "MusicGenre") {
                 url = self.getUrl("MusicGenres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Person") {
+            } else if (itemType == "Person") {
                 url = self.getUrl("Persons/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Studio") {
+            } else if (itemType == "Studio") {
                 url = self.getUrl("Studios/" + self.encodeName(itemName) + "/Images");
-            }
-            else {
+            } else {
                 url = self.getUrl("Items/" + itemId + "/Images");
             }
 
@@ -1284,23 +1248,17 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if (itemType == "Artist") {
                 url = self.getUrl("Artists/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
-            }
-            else if (itemType == "Genre") {
+            } else if (itemType == "Genre") {
                 url = self.getUrl("Genres/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
-            }
-            else if (itemType == "GameGenre") {
+            } else if (itemType == "GameGenre") {
                 url = self.getUrl("GameGenres/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
-            }
-            else if (itemType == "MusicGenre") {
+            } else if (itemType == "MusicGenre") {
                 url = self.getUrl("MusicGenres/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
-            }
-            else if (itemType == "Person") {
+            } else if (itemType == "Person") {
                 url = self.getUrl("Persons/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
-            }
-            else if (itemType == "Studio") {
+            } else if (itemType == "Studio") {
                 url = self.getUrl("Studios/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
-            }
-            else {
+            } else {
                 url = self.getUrl("Items/" + itemId + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
             }
 
@@ -1320,23 +1278,17 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if (itemType == "Artist") {
                 url = self.getUrl("Artists/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Genre") {
+            } else if (itemType == "Genre") {
                 url = self.getUrl("Genres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "GameGenre") {
+            } else if (itemType == "GameGenre") {
                 url = self.getUrl("GameGenres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "MusicGenre") {
+            } else if (itemType == "MusicGenre") {
                 url = self.getUrl("MusicGenres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Person") {
+            } else if (itemType == "Person") {
                 url = self.getUrl("Persons/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Studio") {
+            } else if (itemType == "Studio") {
                 url = self.getUrl("Studios/" + self.encodeName(itemName) + "/Images");
-            }
-            else {
+            } else {
                 url = self.getUrl("Items/" + itemId + "/Images");
             }
 
@@ -1459,23 +1411,17 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if (itemType == "Artist") {
                 url = self.getUrl("Artists/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Genre") {
+            } else if (itemType == "Genre") {
                 url = self.getUrl("Genres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "GameGenre") {
+            } else if (itemType == "GameGenre") {
                 url = self.getUrl("GameGenres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "MusicGenre") {
+            } else if (itemType == "MusicGenre") {
                 url = self.getUrl("MusicGenres/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Person") {
+            } else if (itemType == "Person") {
                 url = self.getUrl("Persons/" + self.encodeName(itemName) + "/Images");
-            }
-            else if (itemType == "Studio") {
+            } else if (itemType == "Studio") {
                 url = self.getUrl("Studios/" + self.encodeName(itemName) + "/Images");
-            }
-            else {
+            } else {
                 url = self.getUrl("Items/" + itemId + "/Images");
             }
 
@@ -2823,6 +2769,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
         };
 
         self.getDateParamValue = function (date) {
+
             function formatDigit(i) {
                 return i < 10 ? "0" + i : i;
             }
@@ -3385,7 +3332,6 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var url = self.getUrl("Users/" + userId + "/PlayingItems/" + itemId, {
-
                 CanSeek: canSeek,
                 QueueableMediaTypes: queueableMediaTypes
             });
@@ -3466,6 +3412,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             }
 
             var params = {
+
             };
 
             if (positionTicks) {
@@ -3579,7 +3526,39 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 url: url,
             });
         };
-    }
+
+        self.getPackageReviews = function (packageId, minRating, maxRating, limit, forceTitle) {
+
+            if (!packageId) {
+                throw new Error("null packageId");
+            }
+
+            var options = {};
+
+            if (minRating) {
+                options.MinRating = minRating;
+            }
+            if (maxRating) {
+                options.MaxRating = maxRating;
+            }
+            if (limit) {
+                options.Limit = limit;
+            }
+            if (forceTitle) {
+                options.ForceTitle = true;
+            }
+
+            var url = self.getUrl("PackageReviews/" + packageId, options);
+
+            return self.ajax({
+                type: "GET",
+                url: url,
+                dataType: "json"
+            });
+        };
+
+
+    };
 
 }(jQuery, navigator, window.JSON, window.WebSocket, setTimeout, window);
 

+ 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.191" targetFramework="net45" />
+  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.192" targetFramework="net45" />
   <package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />
   <package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
 </packages>

+ 3 - 0
MediaBrowser.sln

@@ -237,4 +237,7 @@ Global
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
 	EndGlobalSection
+	GlobalSection(Performance) = preSolution
+		HasPerformanceSessions = true
+	EndGlobalSection
 EndGlobal