ソースを参照

Merge pull request #631 from Bond-009/imagesize

 Cleanup ImageProcessor and SkiaEncoder
Andrew Rabert 6 年 前
コミット
edcfd8b565

+ 92 - 145
Emby.Drawing/ImageProcessor.cs

@@ -1,4 +1,3 @@
-using SkiaSharp;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Globalization;
 using System.Globalization;
@@ -18,9 +17,8 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
-using MediaBrowser.Model.Serialization;
-using MediaBrowser.Model.Threading;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
+using SkiaSharp;
 
 
 namespace Emby.Drawing
 namespace Emby.Drawing
 {
 {
@@ -47,7 +45,6 @@ namespace Emby.Drawing
         private readonly ILogger _logger;
         private readonly ILogger _logger;
 
 
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
-        private readonly IJsonSerializer _jsonSerializer;
         private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationPaths _appPaths;
         private IImageEncoder _imageEncoder;
         private IImageEncoder _imageEncoder;
         private readonly Func<ILibraryManager> _libraryManager;
         private readonly Func<ILibraryManager> _libraryManager;
@@ -57,19 +54,19 @@ namespace Emby.Drawing
             ILoggerFactory loggerFactory,
             ILoggerFactory loggerFactory,
             IServerApplicationPaths appPaths,
             IServerApplicationPaths appPaths,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
-            IJsonSerializer jsonSerializer,
             IImageEncoder imageEncoder,
             IImageEncoder imageEncoder,
-            Func<ILibraryManager> libraryManager, ITimerFactory timerFactory, Func<IMediaEncoder> mediaEncoder)
+            Func<ILibraryManager> libraryManager,
+            Func<IMediaEncoder> mediaEncoder)
         {
         {
             _logger = loggerFactory.CreateLogger(nameof(ImageProcessor));
             _logger = loggerFactory.CreateLogger(nameof(ImageProcessor));
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
-            _jsonSerializer = jsonSerializer;
             _imageEncoder = imageEncoder;
             _imageEncoder = imageEncoder;
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
             _mediaEncoder = mediaEncoder;
             _mediaEncoder = mediaEncoder;
             _appPaths = appPaths;
             _appPaths = appPaths;
 
 
-            ImageEnhancers = new IImageEnhancer[] { };
+            ImageEnhancers = Array.Empty<IImageEnhancer>();
+            
             ImageHelper.ImageProcessor = this;
             ImageHelper.ImageProcessor = this;
         }
         }
 
 
@@ -146,21 +143,19 @@ namespace Emby.Drawing
             return _imageEncoder.SupportedOutputFormats;
             return _imageEncoder.SupportedOutputFormats;
         }
         }
 
 
-        private readonly string[] TransparentImageTypes = new string[] { ".png", ".webp", ".gif" };
+        private static readonly string[] TransparentImageTypes = new string[] { ".png", ".webp", ".gif" };
         public bool SupportsTransparency(string path)
         public bool SupportsTransparency(string path)
-        {
-            return TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty);
-        }
+            => TransparentImageTypes.Contains(Path.GetExtension(path).ToLower());
 
 
-        public async Task<Tuple<string, string, DateTime>> ProcessImage(ImageProcessingOptions options)
+        public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
         {
         {
             if (options == null)
             if (options == null)
             {
             {
                 throw new ArgumentNullException(nameof(options));
                 throw new ArgumentNullException(nameof(options));
             }
             }
 
 
-            var originalImage = options.Image;
-            var item = options.Item;
+            ItemImageInfo originalImage = options.Image;
+            BaseItem item = options.Item;
 
 
             if (!originalImage.IsLocalFile)
             if (!originalImage.IsLocalFile)
             {
             {
@@ -171,19 +166,23 @@ namespace Emby.Drawing
                 originalImage = await _libraryManager().ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false);
                 originalImage = await _libraryManager().ConvertImageToLocal(item, originalImage, options.ImageIndex).ConfigureAwait(false);
             }
             }
 
 
-            var originalImagePath = originalImage.Path;
-            var dateModified = originalImage.DateModified;
-            var originalImageSize = originalImage.Width > 0 && originalImage.Height > 0 ? new ImageSize(originalImage.Width, originalImage.Height) : (ImageSize?)null;
+            string originalImagePath = originalImage.Path;
+            DateTime dateModified = originalImage.DateModified;
+            ImageSize? originalImageSize = null;
+            if (originalImage.Width > 0 && originalImage.Height > 0)
+            {
+                originalImageSize = new ImageSize(originalImage.Width, originalImage.Height);
+            }
 
 
             if (!_imageEncoder.SupportsImageEncoding)
             if (!_imageEncoder.SupportsImageEncoding)
             {
             {
-                return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
+                return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
             }
 
 
             var supportedImageInfo = await GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false);
             var supportedImageInfo = await GetSupportedImage(originalImagePath, dateModified).ConfigureAwait(false);
-            originalImagePath = supportedImageInfo.Item1;
-            dateModified = supportedImageInfo.Item2;
-            var requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath) ?? string.Empty);
+            originalImagePath = supportedImageInfo.path;
+            dateModified = supportedImageInfo.dateModified;
+            bool requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath));
 
 
             if (options.Enhancers.Length > 0)
             if (options.Enhancers.Length > 0)
             {
             {
@@ -197,20 +196,18 @@ namespace Emby.Drawing
                     DateModified = dateModified,
                     DateModified = dateModified,
                     Type = originalImage.Type,
                     Type = originalImage.Type,
                     Path = originalImagePath
                     Path = originalImagePath
-
                 }, requiresTransparency, item, options.ImageIndex, options.Enhancers, CancellationToken.None).ConfigureAwait(false);
                 }, requiresTransparency, item, options.ImageIndex, options.Enhancers, CancellationToken.None).ConfigureAwait(false);
 
 
-                originalImagePath = tuple.Item1;
-                dateModified = tuple.Item2;
-                requiresTransparency = tuple.Item3;
+                originalImagePath = tuple.path;
+                dateModified = tuple.dateModified;
+                requiresTransparency = tuple.transparent;
                 // TODO: Get this info
                 // TODO: Get this info
                 originalImageSize = null;
                 originalImageSize = null;
             }
             }
 
 
-            var photo = item as Photo;
-            var autoOrient = false;
+            bool autoOrient = false;
             ImageOrientation? orientation = null;
             ImageOrientation? orientation = null;
-            if (photo != null)
+            if (item is Photo photo)
             {
             {
                 if (photo.Orientation.HasValue)
                 if (photo.Orientation.HasValue)
                 {
                 {
@@ -231,26 +228,18 @@ namespace Emby.Drawing
             if (options.HasDefaultOptions(originalImagePath, originalImageSize) && (!autoOrient || !options.RequiresAutoOrientation))
             if (options.HasDefaultOptions(originalImagePath, originalImageSize) && (!autoOrient || !options.RequiresAutoOrientation))
             {
             {
                 // Just spit out the original file if all the options are default
                 // Just spit out the original file if all the options are default
-                return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
+                return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
             }
 
 
-            //ImageSize? originalImageSize = GetSavedImageSize(originalImagePath, dateModified);
-            //if (originalImageSize.HasValue && options.HasDefaultOptions(originalImagePath, originalImageSize.Value) && !autoOrient)
-            //{
-            //    // Just spit out the original file if all the options are default
-            //    _logger.LogInformation("Returning original image {0}", originalImagePath);
-            //    return new ValueTuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
-            //}
-
-            var newSize = ImageHelper.GetNewImageSize(options, null);
-            var quality = options.Quality;
+            ImageSize newSize = ImageHelper.GetNewImageSize(options, null);
+            int quality = options.Quality;
 
 
-            var outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
-            var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer);
+            ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
+            string cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer);
 
 
             CheckDisposed();
             CheckDisposed();
 
 
-            var lockInfo = GetLock(cacheFilePath);
+            LockInfo lockInfo = GetLock(cacheFilePath);
 
 
             await lockInfo.Lock.WaitAsync().ConfigureAwait(false);
             await lockInfo.Lock.WaitAsync().ConfigureAwait(false);
 
 
@@ -263,17 +252,15 @@ namespace Emby.Drawing
                         options.CropWhiteSpace = false;
                         options.CropWhiteSpace = false;
                     }
                     }
 
 
-                    var resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat);
+                    string resultPath = _imageEncoder.EncodeImage(originalImagePath, dateModified, cacheFilePath, autoOrient, orientation, quality, options, outputFormat);
 
 
                     if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase))
                     if (string.Equals(resultPath, originalImagePath, StringComparison.OrdinalIgnoreCase))
                     {
                     {
-                        return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
+                        return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
                     }
                     }
-
-                    return new Tuple<string, string, DateTime>(cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
                 }
                 }
 
 
-                return new Tuple<string, string, DateTime>(cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
+                return (cacheFilePath, GetMimeType(outputFormat, cacheFilePath), _fileSystem.GetLastWriteTimeUtc(cacheFilePath));
             }
             }
             catch (ArgumentOutOfRangeException ex)
             catch (ArgumentOutOfRangeException ex)
             {
             {
@@ -282,7 +269,7 @@ namespace Emby.Drawing
                 _logger.LogError(ex, "Error encoding image");
                 _logger.LogError(ex, "Error encoding image");
 #endif
 #endif
                 // Just spit out the original file if all the options are default
                 // Just spit out the original file if all the options are default
-                return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
+                return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {
@@ -290,7 +277,7 @@ namespace Emby.Drawing
                 _logger.LogError(ex, "Error encoding image");
                 _logger.LogError(ex, "Error encoding image");
 
 
                 // Just spit out the original file if all the options are default
                 // Just spit out the original file if all the options are default
-                return new Tuple<string, string, DateTime>(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
+                return (originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified);
             }
             }
             finally
             finally
             {
             {
@@ -326,42 +313,17 @@ namespace Emby.Drawing
             return ImageFormat.Jpg;
             return ImageFormat.Jpg;
         }
         }
 
 
-        private void CopyFile(string src, string destination)
-        {
-            try
-            {
-                _fileSystem.CopyFile(src, destination, true);
-            }
-            catch
-            {
-
-            }
-        }
-
         private string GetMimeType(ImageFormat format, string path)
         private string GetMimeType(ImageFormat format, string path)
         {
         {
-            if (format == ImageFormat.Bmp)
-            {
-                return MimeTypes.GetMimeType("i.bmp");
-            }
-            if (format == ImageFormat.Gif)
-            {
-                return MimeTypes.GetMimeType("i.gif");
-            }
-            if (format == ImageFormat.Jpg)
+            switch(format)
             {
             {
-                return MimeTypes.GetMimeType("i.jpg");
+                case ImageFormat.Bmp:  return MimeTypes.GetMimeType("i.bmp");
+                case ImageFormat.Gif:  return MimeTypes.GetMimeType("i.gif");
+                case ImageFormat.Jpg:  return MimeTypes.GetMimeType("i.jpg");
+                case ImageFormat.Png:  return MimeTypes.GetMimeType("i.png");
+                case ImageFormat.Webp: return MimeTypes.GetMimeType("i.webp");
+                default:               return MimeTypes.GetMimeType(path);
             }
             }
-            if (format == ImageFormat.Png)
-            {
-                return MimeTypes.GetMimeType("i.png");
-            }
-            if (format == ImageFormat.Webp)
-            {
-                return MimeTypes.GetMimeType("i.webp");
-            }
-
-            return MimeTypes.GetMimeType(path);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -374,17 +336,12 @@ namespace Emby.Drawing
         /// </summary>
         /// </summary>
         private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer)
         private string GetCacheFilePath(string originalPath, ImageSize outputSize, int quality, DateTime dateModified, ImageFormat format, bool addPlayedIndicator, double percentPlayed, int? unwatchedCount, int? blur, string backgroundColor, string foregroundLayer)
         {
         {
-            var filename = originalPath;
-
-            filename += "width=" + outputSize.Width;
-
-            filename += "height=" + outputSize.Height;
-
-            filename += "quality=" + quality;
-
-            filename += "datemodified=" + dateModified.Ticks;
-
-            filename += "f=" + format;
+            var filename = originalPath
+                + "width=" + outputSize.Width
+                + "height=" + outputSize.Height
+                + "quality=" + quality
+                + "datemodified=" + dateModified.Ticks
+                + "f=" + format;
 
 
             if (addPlayedIndicator)
             if (addPlayedIndicator)
             {
             {
@@ -422,26 +379,20 @@ namespace Emby.Drawing
         }
         }
 
 
         public ImageSize GetImageSize(BaseItem item, ItemImageInfo info)
         public ImageSize GetImageSize(BaseItem item, ItemImageInfo info)
-        {
-            return GetImageSize(item, info, true);
-        }
+            => GetImageSize(item, info, true);
 
 
         public ImageSize GetImageSize(BaseItem item, ItemImageInfo info, bool updateItem)
         public ImageSize GetImageSize(BaseItem item, ItemImageInfo info, bool updateItem)
         {
         {
-            var width = info.Width;
-            var height = info.Height;
+            int width = info.Width;
+            int height = info.Height;
 
 
             if (height > 0 && width > 0)
             if (height > 0 && width > 0)
             {
             {
-                return new ImageSize
-                {
-                    Width = width,
-                    Height = height
-                };
+                return new ImageSize(width, height);
             }
             }
 
 
-            var path = info.Path;
-            _logger.LogInformation("Getting image size for item {0} {1}", item.GetType().Name, path);
+            string path = info.Path;
+            _logger.LogInformation("Getting image size for item {ItemType} {Path}", item.GetType().Name, path);
 
 
             var size = GetImageSize(path);
             var size = GetImageSize(path);
 
 
@@ -467,15 +418,11 @@ namespace Emby.Drawing
             }
             }
 
 
             using (var s = new SKFileStream(path))
             using (var s = new SKFileStream(path))
-                using (var codec = SKCodec.Create(s))
-                {
-                    var info = codec.Info;
-                    return new ImageSize
-                    {
-                        Height = info.Height,
-                        Width = info.Width
-                    };
-                }
+            using (var codec = SKCodec.Create(s))
+            {
+                var info = codec.Info;
+                return new ImageSize(info.Width, info.Height);
+            }
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -519,9 +466,9 @@ namespace Emby.Drawing
         /// <exception cref="ArgumentNullException">item</exception>
         /// <exception cref="ArgumentNullException">item</exception>
         public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers)
         public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers)
         {
         {
-            var originalImagePath = image.Path;
-            var dateModified = image.DateModified;
-            var imageType = image.Type;
+            string originalImagePath = image.Path;
+            DateTime dateModified = image.DateModified;
+            ImageType imageType = image.Type;
 
 
             // Optimization
             // Optimization
             if (imageEnhancers.Length == 0)
             if (imageEnhancers.Length == 0)
@@ -533,28 +480,28 @@ namespace Emby.Drawing
             var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
             var cacheKeys = imageEnhancers.Select(i => i.GetConfigurationCacheKey(item, imageType)).ToList();
             cacheKeys.Add(originalImagePath + dateModified.Ticks);
             cacheKeys.Add(originalImagePath + dateModified.Ticks);
 
 
-            return string.Join("|", cacheKeys.ToArray()).GetMD5().ToString("N");
+            return string.Join("|", cacheKeys).GetMD5().ToString("N");
         }
         }
 
 
-        private async Task<ValueTuple<string, DateTime>> GetSupportedImage(string originalImagePath, DateTime dateModified)
+        private async Task<(string path, DateTime dateModified)> GetSupportedImage(string originalImagePath, DateTime dateModified)
         {
         {
-            var inputFormat = (Path.GetExtension(originalImagePath) ?? string.Empty)
+            var inputFormat = Path.GetExtension(originalImagePath)
                 .TrimStart('.')
                 .TrimStart('.')
                 .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
                 .Replace("jpeg", "jpg", StringComparison.OrdinalIgnoreCase);
 
 
             // These are just jpg files renamed as tbn
             // These are just jpg files renamed as tbn
             if (string.Equals(inputFormat, "tbn", StringComparison.OrdinalIgnoreCase))
             if (string.Equals(inputFormat, "tbn", StringComparison.OrdinalIgnoreCase))
             {
             {
-                return new ValueTuple<string, DateTime>(originalImagePath, dateModified);
+                return (originalImagePath, dateModified);
             }
             }
 
 
             if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat, StringComparer.OrdinalIgnoreCase))
             if (!_imageEncoder.SupportedInputFormats.Contains(inputFormat, StringComparer.OrdinalIgnoreCase))
             {
             {
                 try
                 try
                 {
                 {
-                    var filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N");
+                    string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N");
 
 
-                    var cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
+                    string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
                     var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
                     var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
 
 
                     var file = _fileSystem.GetFileInfo(outputPath);
                     var file = _fileSystem.GetFileInfo(outputPath);
@@ -572,11 +519,11 @@ namespace Emby.Drawing
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
-                    _logger.LogError(ex, "Image conversion failed for {originalImagePath}", originalImagePath);
+                    _logger.LogError(ex, "Image conversion failed for {Path}", originalImagePath);
                 }
                 }
             }
             }
 
 
-            return new ValueTuple<string, DateTime>(originalImagePath, dateModified);
+            return (originalImagePath, dateModified);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -590,16 +537,17 @@ namespace Emby.Drawing
         {
         {
             var enhancers = GetSupportedEnhancers(item, imageType);
             var enhancers = GetSupportedEnhancers(item, imageType);
 
 
-            var imageInfo = item.GetImageInfo(imageType, imageIndex);
+            ItemImageInfo imageInfo = item.GetImageInfo(imageType, imageIndex);
 
 
-            var inputImageSupportsTransparency = SupportsTransparency(imageInfo.Path);
+            bool inputImageSupportsTransparency = SupportsTransparency(imageInfo.Path);
 
 
             var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers, CancellationToken.None);
             var result = await GetEnhancedImage(imageInfo, inputImageSupportsTransparency, item, imageIndex, enhancers, CancellationToken.None);
 
 
-            return result.Item1;
+            return result.path;
         }
         }
 
 
-        private async Task<ValueTuple<string, DateTime, bool>> GetEnhancedImage(ItemImageInfo image,
+        private async Task<(string path, DateTime dateModified, bool transparent)> GetEnhancedImage(
+            ItemImageInfo image,
             bool inputImageSupportsTransparency,
             bool inputImageSupportsTransparency,
             BaseItem item,
             BaseItem item,
             int imageIndex,
             int imageIndex,
@@ -617,14 +565,14 @@ namespace Emby.Drawing
                 // Enhance if we have enhancers
                 // Enhance if we have enhancers
                 var enhancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid, cancellationToken).ConfigureAwait(false);
                 var enhancedImageInfo = await GetEnhancedImageInternal(originalImagePath, item, imageType, imageIndex, enhancers, cacheGuid, cancellationToken).ConfigureAwait(false);
 
 
-                var enhancedImagePath = enhancedImageInfo.Item1;
+                string enhancedImagePath = enhancedImageInfo.path;
 
 
                 // If the path changed update dateModified
                 // If the path changed update dateModified
                 if (!string.Equals(enhancedImagePath, originalImagePath, StringComparison.OrdinalIgnoreCase))
                 if (!string.Equals(enhancedImagePath, originalImagePath, StringComparison.OrdinalIgnoreCase))
                 {
                 {
-                    var treatmentRequiresTransparency = enhancedImageInfo.Item2;
+                    var treatmentRequiresTransparency = enhancedImageInfo.transparent;
 
 
-                    return new ValueTuple<string, DateTime, bool>(enhancedImagePath, _fileSystem.GetLastWriteTimeUtc(enhancedImagePath), treatmentRequiresTransparency);
+                    return (enhancedImagePath, _fileSystem.GetLastWriteTimeUtc(enhancedImagePath), treatmentRequiresTransparency);
                 }
                 }
             }
             }
             catch (Exception ex)
             catch (Exception ex)
@@ -632,7 +580,7 @@ namespace Emby.Drawing
                 _logger.LogError(ex, "Error enhancing image");
                 _logger.LogError(ex, "Error enhancing image");
             }
             }
 
 
-            return new ValueTuple<string, DateTime, bool>(originalImagePath, dateModified, inputImageSupportsTransparency);
+            return (originalImagePath, dateModified, inputImageSupportsTransparency);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -650,7 +598,8 @@ namespace Emby.Drawing
         /// or
         /// or
         /// item
         /// item
         /// </exception>
         /// </exception>
-        private async Task<ValueTuple<string, bool>> GetEnhancedImageInternal(string originalImagePath,
+        private async Task<(string path, bool transparent)> GetEnhancedImageInternal(
+            string originalImagePath,
             BaseItem item,
             BaseItem item,
             ImageType imageType,
             ImageType imageType,
             int imageIndex,
             int imageIndex,
@@ -678,13 +627,13 @@ namespace Emby.Drawing
             }
             }
 
 
             // All enhanced images are saved as png to allow transparency
             // All enhanced images are saved as png to allow transparency
-            var cacheExtension = _imageEncoder.SupportedOutputFormats.Contains(ImageFormat.Webp) ?
+            string cacheExtension = _imageEncoder.SupportedOutputFormats.Contains(ImageFormat.Webp) ?
                 ".webp" :
                 ".webp" :
                 (treatmentRequiresTransparency ? ".png" : ".jpg");
                 (treatmentRequiresTransparency ? ".png" : ".jpg");
 
 
-            var enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + cacheExtension);
+            string enhancedImagePath = GetCachePath(EnhancedImageCachePath, cacheGuid + cacheExtension);
 
 
-            var lockInfo = GetLock(enhancedImagePath);
+            LockInfo lockInfo = GetLock(enhancedImagePath);
 
 
             await lockInfo.Lock.WaitAsync(cancellationToken).ConfigureAwait(false);
             await lockInfo.Lock.WaitAsync(cancellationToken).ConfigureAwait(false);
 
 
@@ -693,14 +642,14 @@ namespace Emby.Drawing
                 // Check again in case of contention
                 // Check again in case of contention
                 if (_fileSystem.FileExists(enhancedImagePath))
                 if (_fileSystem.FileExists(enhancedImagePath))
                 {
                 {
-                    return new ValueTuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
+                    return (enhancedImagePath, treatmentRequiresTransparency);
                 }
                 }
 
 
                 _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath));
                 _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(enhancedImagePath));
 
 
                 await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
                 await ExecuteImageEnhancers(supportedEnhancers, originalImagePath, enhancedImagePath, item, imageType, imageIndex).ConfigureAwait(false);
 
 
-                return new ValueTuple<string, bool>(enhancedImagePath, treatmentRequiresTransparency);
+                return (enhancedImagePath, treatmentRequiresTransparency);
             }
             }
             finally
             finally
             {
             {
@@ -718,7 +667,7 @@ namespace Emby.Drawing
         /// <param name="imageType">Type of the image.</param>
         /// <param name="imageType">Type of the image.</param>
         /// <param name="imageIndex">Index of the image.</param>
         /// <param name="imageIndex">Index of the image.</param>
         /// <returns>Task{EnhancedImage}.</returns>
         /// <returns>Task{EnhancedImage}.</returns>
-        private async Task ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, string inputPath, string outputPath, BaseItem item, ImageType imageType, int imageIndex)
+        private static async Task ExecuteImageEnhancers(IEnumerable<IImageEnhancer> imageEnhancers, string inputPath, string outputPath, BaseItem item, ImageType imageType, int imageIndex)
         {
         {
             // Run the enhancers sequentially in order of priority
             // Run the enhancers sequentially in order of priority
             foreach (var enhancer in imageEnhancers)
             foreach (var enhancer in imageEnhancers)
@@ -789,18 +738,16 @@ namespace Emby.Drawing
 
 
             var prefix = filename.Substring(0, 1);
             var prefix = filename.Substring(0, 1);
 
 
-            path = Path.Combine(path, prefix);
-
-            return Path.Combine(path, filename);
+            return Path.Combine(path, prefix, filename);
         }
         }
 
 
         public void CreateImageCollage(ImageCollageOptions options)
         public void CreateImageCollage(ImageCollageOptions options)
         {
         {
-            _logger.LogInformation("Creating image collage and saving to {0}", options.OutputPath);
+            _logger.LogInformation("Creating image collage and saving to {Path}", options.OutputPath);
 
 
             _imageEncoder.CreateImageCollage(options);
             _imageEncoder.CreateImageCollage(options);
 
 
-            _logger.LogInformation("Completed creation of image collage and saved to {0}", options.OutputPath);
+            _logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath);
         }
         }
 
 
         public IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType)
         public IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType)

+ 2 - 2
Emby.Drawing/PercentPlayedDrawer.cs

@@ -4,11 +4,11 @@ using SkiaSharp;
 
 
 namespace Emby.Drawing
 namespace Emby.Drawing
 {
 {
-    public class PercentPlayedDrawer
+    public static class PercentPlayedDrawer
     {
     {
         private const int IndicatorHeight = 8;
         private const int IndicatorHeight = 8;
 
 
-        public void Process(SKCanvas canvas, ImageSize imageSize, double percent)
+        public static void Process(SKCanvas canvas, ImageSize imageSize, double percent)
         {
         {
             using (var paint = new SKPaint())
             using (var paint = new SKPaint())
             {
             {

+ 2 - 16
Emby.Drawing/PlayedIndicatorDrawer.cs

@@ -1,27 +1,13 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.IO;
 using SkiaSharp;
 using SkiaSharp;
 
 
 namespace Emby.Drawing
 namespace Emby.Drawing
 {
 {
-    public class PlayedIndicatorDrawer
+    public static class PlayedIndicatorDrawer
     {
     {
         private const int OffsetFromTopRightCorner = 38;
         private const int OffsetFromTopRightCorner = 38;
 
 
-        private readonly IApplicationPaths _appPaths;
-        private readonly IHttpClient _iHttpClient;
-        private readonly IFileSystem _fileSystem;
-
-        public PlayedIndicatorDrawer(IApplicationPaths appPaths, IHttpClient iHttpClient, IFileSystem fileSystem)
-        {
-            _appPaths = appPaths;
-            _iHttpClient = iHttpClient;
-            _fileSystem = fileSystem;
-        }
-
-        public void DrawPlayedIndicator(SKCanvas canvas, ImageSize imageSize)
+        public static void DrawPlayedIndicator(SKCanvas canvas, ImageSize imageSize)
         {
         {
             var x = imageSize.Width - OffsetFromTopRightCorner;
             var x = imageSize.Width - OffsetFromTopRightCorner;
 
 

+ 3 - 7
Emby.Drawing/SkiaEncoder.cs

@@ -4,7 +4,6 @@ using System.IO;
 using System.Linq;
 using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Drawing;
@@ -19,20 +18,17 @@ namespace Emby.Drawing
     {
     {
         private readonly ILogger _logger;
         private readonly ILogger _logger;
         private static IApplicationPaths _appPaths;
         private static IApplicationPaths _appPaths;
-        private readonly Func<IHttpClient> _httpClientFactory;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
         private static ILocalizationManager _localizationManager;
         private static ILocalizationManager _localizationManager;
 
 
         public SkiaEncoder(
         public SkiaEncoder(
             ILoggerFactory loggerFactory,
             ILoggerFactory loggerFactory,
             IApplicationPaths appPaths,
             IApplicationPaths appPaths,
-            Func<IHttpClient> httpClientFactory,
             IFileSystem fileSystem,
             IFileSystem fileSystem,
             ILocalizationManager localizationManager)
             ILocalizationManager localizationManager)
         {
         {
             _logger = loggerFactory.CreateLogger("ImageEncoder");
             _logger = loggerFactory.CreateLogger("ImageEncoder");
             _appPaths = appPaths;
             _appPaths = appPaths;
-            _httpClientFactory = httpClientFactory;
             _fileSystem = fileSystem;
             _fileSystem = fileSystem;
             _localizationManager = localizationManager;
             _localizationManager = localizationManager;
 
 
@@ -641,16 +637,16 @@ namespace Emby.Drawing
 
 
                 if (options.AddPlayedIndicator)
                 if (options.AddPlayedIndicator)
                 {
                 {
-                    new PlayedIndicatorDrawer(_appPaths, _httpClientFactory(), _fileSystem).DrawPlayedIndicator(canvas, currentImageSize);
+                    PlayedIndicatorDrawer.DrawPlayedIndicator(canvas, currentImageSize);
                 }
                 }
                 else if (options.UnplayedCount.HasValue)
                 else if (options.UnplayedCount.HasValue)
                 {
                 {
-                    new UnplayedCountIndicator(_appPaths, _httpClientFactory(), _fileSystem).DrawUnplayedCountIndicator(canvas, currentImageSize, options.UnplayedCount.Value);
+                    UnplayedCountIndicator.DrawUnplayedCountIndicator(canvas, currentImageSize, options.UnplayedCount.Value);
                 }
                 }
 
 
                 if (options.PercentPlayed > 0)
                 if (options.PercentPlayed > 0)
                 {
                 {
-                    new PercentPlayedDrawer().Process(canvas, currentImageSize, options.PercentPlayed);
+                    PercentPlayedDrawer.Process(canvas, currentImageSize, options.PercentPlayed);
                 }
                 }
             }
             }
             catch (Exception ex)
             catch (Exception ex)

+ 2 - 16
Emby.Drawing/UnplayedCountIndicator.cs

@@ -1,28 +1,14 @@
 using System.Globalization;
 using System.Globalization;
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Drawing;
 using MediaBrowser.Model.Drawing;
-using MediaBrowser.Model.IO;
 using SkiaSharp;
 using SkiaSharp;
 
 
 namespace Emby.Drawing
 namespace Emby.Drawing
 {
 {
-    public class UnplayedCountIndicator
+    public static class UnplayedCountIndicator
     {
     {
         private const int OffsetFromTopRightCorner = 38;
         private const int OffsetFromTopRightCorner = 38;
 
 
-        private readonly IApplicationPaths _appPaths;
-        private readonly IHttpClient _iHttpClient;
-        private readonly IFileSystem _fileSystem;
-
-        public UnplayedCountIndicator(IApplicationPaths appPaths, IHttpClient iHttpClient, IFileSystem fileSystem)
-        {
-            _appPaths = appPaths;
-            _iHttpClient = iHttpClient;
-            _fileSystem = fileSystem;
-        }
-
-        public void DrawUnplayedCountIndicator(SKCanvas canvas, ImageSize imageSize, int count)
+        public static void DrawUnplayedCountIndicator(SKCanvas canvas, ImageSize imageSize, int count)
         {
         {
             var x = imageSize.Width - OffsetFromTopRightCorner;
             var x = imageSize.Width - OffsetFromTopRightCorner;
             var text = count.ToString(CultureInfo.InvariantCulture);
             var text = count.ToString(CultureInfo.InvariantCulture);

+ 1 - 1
Emby.Server.Implementations/ApplicationHost.cs

@@ -1042,7 +1042,7 @@ namespace Emby.Server.Implementations
 
 
         private IImageProcessor GetImageProcessor()
         private IImageProcessor GetImageProcessor()
         {
         {
-            return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, JsonSerializer, ImageEncoder, () => LibraryManager, TimerFactory, () => MediaEncoder);
+            return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
         }
         }
 
 
         protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
         protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()

+ 3 - 6
Jellyfin.Server/Program.cs

@@ -14,11 +14,9 @@ using Emby.Server.Implementations.EnvironmentInfo;
 using Emby.Server.Implementations.IO;
 using Emby.Server.Implementations.IO;
 using Emby.Server.Implementations.Networking;
 using Emby.Server.Implementations.Networking;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.Globalization;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.IO;
-using MediaBrowser.Model.System;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Configuration;
 using Microsoft.Extensions.Logging;
 using Microsoft.Extensions.Logging;
 using Serilog;
 using Serilog;
@@ -103,7 +101,7 @@ namespace Jellyfin.Server
             {
             {
                 appHost.Init();
                 appHost.Init();
 
 
-                appHost.ImageProcessor.ImageEncoder = getImageEncoder(fileSystem, () => appHost.HttpClient, appPaths, appHost.LocalizationManager);
+                appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager);
 
 
                 _logger.LogInformation("Running startup tasks");
                 _logger.LogInformation("Running startup tasks");
 
 
@@ -256,15 +254,14 @@ namespace Jellyfin.Server
             }
             }
         }
         }
 
 
-        public static IImageEncoder getImageEncoder(
+        public static IImageEncoder GetImageEncoder(
             IFileSystem fileSystem,
             IFileSystem fileSystem,
-            Func<IHttpClient> httpClient,
             IApplicationPaths appPaths,
             IApplicationPaths appPaths,
             ILocalizationManager localizationManager)
             ILocalizationManager localizationManager)
         {
         {
             try
             try
             {
             {
-                return new SkiaEncoder(_loggerFactory, appPaths, httpClient, fileSystem, localizationManager);
+                return new SkiaEncoder(_loggerFactory, appPaths, fileSystem, localizationManager);
             }
             }
             catch (Exception ex)
             catch (Exception ex)
             {
             {

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

@@ -82,7 +82,7 @@ namespace MediaBrowser.Controller.Drawing
         /// </summary>
         /// </summary>
         /// <param name="options">The options.</param>
         /// <param name="options">The options.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
-        Task<Tuple<string, string, DateTime>> ProcessImage(ImageProcessingOptions options);
+        Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options);
 
 
         /// <summary>
         /// <summary>
         /// Gets the enhanced image.
         /// Gets the enhanced image.