diff --git a/MediaBrowser.Api/Images/ImageRequest.cs b/MediaBrowser.Api/Images/ImageRequest.cs index 0b6f09c5a2..719b0de5ea 100644 --- a/MediaBrowser.Api/Images/ImageRequest.cs +++ b/MediaBrowser.Api/Images/ImageRequest.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Entities; +using MediaBrowser.Controller.Drawing; +using MediaBrowser.Model.Entities; using ServiceStack.ServiceHost; namespace MediaBrowser.Api.Images @@ -52,9 +53,14 @@ namespace MediaBrowser.Api.Images [ApiMember(Name = "EnableImageEnhancers", Description = "Enable or disable image enhancers such as cover art.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool EnableImageEnhancers { get; set; } + [ApiMember(Name = "Format", Description = "Determines the output foramt of the image - original,gif,jpg,png", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public ImageOutputFormat Format { get; set; } + public ImageRequest() { EnableImageEnhancers = true; + + Format = ImageOutputFormat.Original; } } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index b8c6fc8f0b..9f1c235ad3 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; -using MediaBrowser.Controller; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Api/Images/ImageWriter.cs b/MediaBrowser.Api/Images/ImageWriter.cs index da2c9c043a..03266fc996 100644 --- a/MediaBrowser.Api/Images/ImageWriter.cs +++ b/MediaBrowser.Api/Images/ImageWriter.cs @@ -75,9 +75,24 @@ namespace MediaBrowser.Api.Images cropwhitespace = Request.CropWhitespace.Value; } - return ImageProcessor.ProcessImage(Item, Request.Type, Request.Index ?? 0, OriginalImagePath, cropwhitespace, - OriginalImageDateModified, responseStream, Request.Width, Request.Height, Request.MaxWidth, - Request.MaxHeight, Request.Quality, Enhancers); + var options = new ImageProcessingOptions + { + CropWhiteSpace = cropwhitespace, + Enhancers = Enhancers, + Height = Request.Height, + ImageIndex = Request.Index ?? 0, + ImageType = Request.Type, + Item = Item, + MaxHeight = Request.MaxHeight, + MaxWidth = Request.MaxWidth, + OriginalImageDateModified = OriginalImageDateModified, + OriginalImagePath = OriginalImagePath, + Quality = Request.Quality, + Width = Request.Width, + OutputFormat = Request.Format + }; + + return ImageProcessor.ProcessImage(options, responseStream); } } } diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index 4079e41efa..5b07a7bd7d 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -221,7 +221,7 @@ namespace MediaBrowser.Api.UserLibrary /// System.Object. public object Get(GetItems request) { - var result = GetItems(request).Result; + var result = GetItems(request); return ToOptimizedResult(result); } @@ -231,7 +231,7 @@ namespace MediaBrowser.Api.UserLibrary /// /// The request. /// Task{ItemsResult}. - private async Task GetItems(GetItems request) + private ItemsResult GetItems(GetItems request) { var user = _userManager.GetUserById(request.UserId); diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 55c279b0cc..c7df48c041 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -73,22 +73,9 @@ namespace MediaBrowser.Controller.Drawing /// /// Processes the image. /// - /// The entity. - /// Type of the image. - /// Index of the image. - /// The original image path. - /// if set to true [crop whitespace]. - /// The date modified. + /// The options. /// To stream. - /// The width. - /// The height. - /// Width of the max. - /// Height of the max. - /// The quality. - /// The enhancers. /// Task. - Task ProcessImage(BaseItem entity, ImageType imageType, int imageIndex, string originalImagePath, bool cropWhitespace, - DateTime dateModified, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, - int? quality, List enhancers); + Task ProcessImage(ImageProcessingOptions options, Stream toStream); } } diff --git a/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs new file mode 100644 index 0000000000..7a56015adb --- /dev/null +++ b/MediaBrowser.Controller/Drawing/ImageProcessingOptions.cs @@ -0,0 +1,46 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Drawing +{ + public class ImageProcessingOptions + { + public BaseItem Item { get; set; } + + public ImageType ImageType { get; set; } + + public int ImageIndex { get; set; } + + public string OriginalImagePath { get; set; } + + public DateTime OriginalImageDateModified { get; set; } + + public bool CropWhiteSpace { get; set; } + + public int? Width { get; set; } + + public int? Height { get; set; } + + public int? MaxWidth { get; set; } + + public int? MaxHeight { get; set; } + + public int? Quality { get; set; } + + public List Enhancers { get; set; } + + public ImageOutputFormat OutputFormat { get; set; } + } + + public enum ImageOutputFormat + { + Original, + Bmp, + Gif, + Jpg, + Png + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 960f597ac3..ad8b499543 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -72,6 +72,7 @@ Properties\SharedVersion.cs + diff --git a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs index d16c2a4ded..07a944e403 100644 --- a/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs +++ b/MediaBrowser.Server.Implementations/Drawing/ImageProcessor.cs @@ -1,24 +1,23 @@ -using System.Collections.Generic; -using System.Drawing; -using System.Drawing.Drawing2D; -using System.Drawing.Imaging; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; using MediaBrowser.Controller; -using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Drawing.Imaging; +using System.Globalization; using System.IO; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.Drawing { @@ -74,11 +73,11 @@ namespace MediaBrowser.Server.Implementations.Drawing ImageEnhancers = enhancers.ToArray(); } - public async Task ProcessImage(BaseItem entity, ImageType imageType, int imageIndex, string originalImagePath, bool cropWhitespace, DateTime dateModified, Stream toStream, int? width, int? height, int? maxWidth, int? maxHeight, int? quality, List enhancers) + public async Task ProcessImage(ImageProcessingOptions options, Stream toStream) { - if (entity == null) + if (options == null) { - throw new ArgumentNullException("entity"); + throw new ArgumentNullException("options"); } if (toStream == null) @@ -86,43 +85,31 @@ namespace MediaBrowser.Server.Implementations.Drawing throw new ArgumentNullException("toStream"); } - if (cropWhitespace) + var originalImagePath = options.OriginalImagePath; + var dateModified = options.OriginalImageDateModified; + + if (options.CropWhiteSpace) { originalImagePath = await GetWhitespaceCroppedImage(originalImagePath, dateModified).ConfigureAwait(false); } // No enhancement - don't cache - if (enhancers.Count > 0) + if (options.Enhancers.Count > 0) { - try - { - // Enhance if we have enhancers - var ehnancedImagePath = await GetEnhancedImage(originalImagePath, dateModified, entity, imageType, imageIndex, enhancers).ConfigureAwait(false); + var tuple = await GetEnhancedImage(originalImagePath, dateModified, options.Item, options.ImageType, options.ImageIndex, options.Enhancers).ConfigureAwait(false); - // If the path changed update dateModified - if (!ehnancedImagePath.Equals(originalImagePath, StringComparison.OrdinalIgnoreCase)) - { - dateModified = File.GetLastWriteTimeUtc(ehnancedImagePath); - originalImagePath = ehnancedImagePath; - } - } - catch (Exception ex) - { - _logger.Error("Error enhancing image", ex); - } + originalImagePath = tuple.Item1; + dateModified = tuple.Item2; } var originalImageSize = GetImageSize(originalImagePath, dateModified); // Determine the output size based on incoming parameters - var newSize = DrawingUtils.Resize(originalImageSize, width, height, maxWidth, maxHeight); + var newSize = DrawingUtils.Resize(originalImageSize, options.Width, options.Height, options.MaxWidth, options.MaxHeight); - if (!quality.HasValue) - { - quality = 90; - } + var quality = options.Quality ?? 90; - var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality.Value, dateModified); + var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, options.OutputFormat); try { @@ -188,12 +175,12 @@ namespace MediaBrowser.Server.Implementations.Drawing thumbnailGraph.DrawImage(originalImage, 0, 0, newWidth, newHeight); - var outputFormat = originalImage.RawFormat; + var outputFormat = GetOutputFormat(originalImage, options.OutputFormat); using (var outputMemoryStream = new MemoryStream()) { // Save to the memory stream - thumbnail.Save(outputFormat, outputMemoryStream, quality.Value); + thumbnail.Save(outputFormat, outputMemoryStream, quality); var bytes = outputMemoryStream.ToArray(); @@ -217,6 +204,29 @@ namespace MediaBrowser.Server.Implementations.Drawing } } + /// + /// Gets the output format. + /// + /// The image. + /// The output format. + /// ImageFormat. + private ImageFormat GetOutputFormat(Image image, ImageOutputFormat outputFormat) + { + switch (outputFormat) + { + case ImageOutputFormat.Bmp: + return ImageFormat.Bmp; + case ImageOutputFormat.Gif: + return ImageFormat.Gif; + case ImageOutputFormat.Jpg: + return ImageFormat.Jpeg; + case ImageOutputFormat.Png: + return ImageFormat.Png; + default: + return image.RawFormat; + } + } + /// /// Crops whitespace from an image, caches the result, and returns the cached path /// @@ -317,7 +327,7 @@ namespace MediaBrowser.Server.Implementations.Drawing /// Quality level, from 0-100. Currently only applies to JPG. The default value should suffice. /// The last modified date of the image /// System.String. - private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified) + private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageOutputFormat format) { var filename = originalPath; @@ -329,6 +339,11 @@ namespace MediaBrowser.Server.Implementations.Drawing filename += "datemodified=" + dateModified.Ticks; + if (format != ImageOutputFormat.Original) + { + filename += "format=" + format; + } + return GetCachePath(_resizedImageCachePath, filename, Path.GetExtension(originalPath)); } @@ -452,7 +467,7 @@ namespace MediaBrowser.Server.Implementations.Drawing var dateModified = item.GetImageDateModified(imagePath); - var supportedEnhancers = GetSupportedEnhancers(item, imageType).ToList(); + var supportedEnhancers = GetSupportedEnhancers(item, imageType); return GetImageCacheTag(item, imageType, imagePath, dateModified, supportedEnhancers); } @@ -491,39 +506,29 @@ namespace MediaBrowser.Server.Implementations.Drawing return string.Join("|", cacheKeys.ToArray()).GetMD5(); } - /// - /// Gets the enhanced image. - /// - /// The original image path. - /// The date modified. - /// The item. - /// Type of the image. - /// Index of the image. - /// Task{System.String}. - /// item - public Task GetEnhancedImage(string originalImagePath, DateTime dateModified, BaseItem item, ImageType imageType, int imageIndex) + private async Task> GetEnhancedImage(string originalImagePath, DateTime dateModified, BaseItem item, + ImageType imageType, int imageIndex, + List enhancers) { - if (item == null) + try { - throw new ArgumentNullException("item"); - } + // Enhance if we have enhancers + var ehnancedImagePath = await GetEnhancedImageInternal(originalImagePath, dateModified, item, imageType, imageIndex, enhancers).ConfigureAwait(false); - var supportedImageEnhancers = ImageEnhancers.Where(i => - { - try + // If the path changed update dateModified + if (!ehnancedImagePath.Equals(originalImagePath, StringComparison.OrdinalIgnoreCase)) { - return i.Supports(item, imageType); - } - catch (Exception ex) - { - _logger.ErrorException("Error in image enhancer: {0}", ex, i.GetType().Name); + dateModified = File.GetLastWriteTimeUtc(ehnancedImagePath); - return false; + return new Tuple(ehnancedImagePath, dateModified); } + } + catch (Exception ex) + { + _logger.Error("Error enhancing image", ex); + } - }).ToList(); - - return GetEnhancedImage(originalImagePath, dateModified, item, imageType, imageIndex, supportedImageEnhancers); + return new Tuple(originalImagePath, dateModified); } /// @@ -537,7 +542,7 @@ namespace MediaBrowser.Server.Implementations.Drawing /// The supported enhancers. /// System.String. /// originalImagePath - public async Task GetEnhancedImage(string originalImagePath, DateTime dateModified, BaseItem item, ImageType imageType, int imageIndex, List supportedEnhancers) + private async Task GetEnhancedImageInternal(string originalImagePath, DateTime dateModified, BaseItem item, ImageType imageType, int imageIndex, List supportedEnhancers) { if (string.IsNullOrEmpty(originalImagePath)) {