|  | @@ -22,42 +22,47 @@ using Microsoft.Extensions.Logging;
 | 
	
		
			
				|  |  |  namespace Emby.Drawing
 | 
	
		
			
				|  |  |  {
 | 
	
		
			
				|  |  |      /// <summary>
 | 
	
		
			
				|  |  | -    /// Class ImageProcessor
 | 
	
		
			
				|  |  | +    /// Class ImageProcessor.
 | 
	
		
			
				|  |  |      /// </summary>
 | 
	
		
			
				|  |  |      public class ImageProcessor : IImageProcessor, IDisposable
 | 
	
		
			
				|  |  |      {
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// The us culture
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
 | 
	
		
			
				|  |  | +        // Increment this when there's a change requiring caches to be invalidated
 | 
	
		
			
				|  |  | +        private const string Version = "3";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the list of currently registered image processors
 | 
	
		
			
				|  |  | -        /// Image processors are specialized metadata providers that run after the normal ones
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <value>The image enhancers.</value>
 | 
	
		
			
				|  |  | -        public IImageEnhancer[] ImageEnhancers { get; private set; }
 | 
	
		
			
				|  |  | +        private static readonly HashSet<string> _transparentImageTypes
 | 
	
		
			
				|  |  | +            = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// The _logger
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
		
			
				|  |  |          private readonly ILogger _logger;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          private readonly IFileSystem _fileSystem;
 | 
	
		
			
				|  |  |          private readonly IServerApplicationPaths _appPaths;
 | 
	
		
			
				|  |  |          private IImageEncoder _imageEncoder;
 | 
	
		
			
				|  |  |          private readonly Func<ILibraryManager> _libraryManager;
 | 
	
		
			
				|  |  |          private readonly Func<IMediaEncoder> _mediaEncoder;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        private readonly Dictionary<string, LockInfo> _locks = new Dictionary<string, LockInfo>();
 | 
	
		
			
				|  |  | +        private bool _disposed = false;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        /// <summary>
 | 
	
		
			
				|  |  | +        ///
 | 
	
		
			
				|  |  | +        /// </summary>
 | 
	
		
			
				|  |  | +        /// <param name="logger"></param>
 | 
	
		
			
				|  |  | +        /// <param name="appPaths"></param>
 | 
	
		
			
				|  |  | +        /// <param name="fileSystem"></param>
 | 
	
		
			
				|  |  | +        /// <param name="imageEncoder"></param>
 | 
	
		
			
				|  |  | +        /// <param name="libraryManager"></param>
 | 
	
		
			
				|  |  | +        /// <param name="mediaEncoder"></param>
 | 
	
		
			
				|  |  |          public ImageProcessor(
 | 
	
		
			
				|  |  | -            ILoggerFactory loggerFactory,
 | 
	
		
			
				|  |  | +            ILogger<ImageProcessor> logger,
 | 
	
		
			
				|  |  |              IServerApplicationPaths appPaths,
 | 
	
		
			
				|  |  |              IFileSystem fileSystem,
 | 
	
		
			
				|  |  |              IImageEncoder imageEncoder,
 | 
	
		
			
				|  |  |              Func<ILibraryManager> libraryManager,
 | 
	
		
			
				|  |  |              Func<IMediaEncoder> mediaEncoder)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            _logger = loggerFactory.CreateLogger(nameof(ImageProcessor));
 | 
	
		
			
				|  |  | +            _logger = logger;
 | 
	
		
			
				|  |  |              _fileSystem = fileSystem;
 | 
	
		
			
				|  |  |              _imageEncoder = imageEncoder;
 | 
	
		
			
				|  |  |              _libraryManager = libraryManager;
 | 
	
	
		
			
				|  | @@ -69,20 +74,11 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              ImageHelper.ImageProcessor = this;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        public IImageEncoder ImageEncoder
 | 
	
		
			
				|  |  | -        {
 | 
	
		
			
				|  |  | -            get => _imageEncoder;
 | 
	
		
			
				|  |  | -            set
 | 
	
		
			
				|  |  | -            {
 | 
	
		
			
				|  |  | -                if (value == null)
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    throw new ArgumentNullException(nameof(value));
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | +        private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -                _imageEncoder = value;
 | 
	
		
			
				|  |  | -            }
 | 
	
		
			
				|  |  | -        }
 | 
	
		
			
				|  |  | +        private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images");
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public IReadOnlyCollection<string> SupportedInputFormats =>
 | 
	
		
			
				|  |  |              new HashSet<string>(StringComparer.OrdinalIgnoreCase)
 | 
	
		
			
				|  |  |              {
 | 
	
	
		
			
				|  | @@ -115,18 +111,20 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |                  "wbmp"
 | 
	
		
			
				|  |  |              };
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  | +        public IReadOnlyCollection<IImageEnhancer> ImageEnhancers { get; set; }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public bool SupportsImageCollageCreation => _imageEncoder.SupportsImageCollageCreation;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private string ResizedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "resized-images");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        private string EnhancedImageCachePath => Path.Combine(_appPaths.ImageCachePath, "enhanced-images");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -        public void AddParts(IEnumerable<IImageEnhancer> enhancers)
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  | +        public IImageEncoder ImageEncoder
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            ImageEnhancers = enhancers.ToArray();
 | 
	
		
			
				|  |  | +            get => _imageEncoder;
 | 
	
		
			
				|  |  | +            set => _imageEncoder = value ?? throw new ArgumentNullException(nameof(value));
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public async Task ProcessImage(ImageProcessingOptions options, Stream toStream)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              var file = await ProcessImage(options).ConfigureAwait(false);
 | 
	
	
		
			
				|  | @@ -137,15 +135,15 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public IReadOnlyCollection<ImageFormat> GetSupportedImageOutputFormats()
 | 
	
		
			
				|  |  |              => _imageEncoder.SupportedOutputFormats;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private static readonly HashSet<string> TransparentImageTypes
 | 
	
		
			
				|  |  | -            = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { ".png", ".webp", ".gif" };
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public bool SupportsTransparency(string path)
 | 
	
		
			
				|  |  | -            => TransparentImageTypes.Contains(Path.GetExtension(path));
 | 
	
		
			
				|  |  | +            => _transparentImageTypes.Contains(Path.GetExtension(path));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public async Task<(string path, string mimeType, DateTime dateModified)> ProcessImage(ImageProcessingOptions options)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (options == null)
 | 
	
	
		
			
				|  | @@ -187,9 +185,9 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              dateModified = supportedImageInfo.dateModified;
 | 
	
		
			
				|  |  | -            bool requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath));
 | 
	
		
			
				|  |  | +            bool requiresTransparency = _transparentImageTypes.Contains(Path.GetExtension(originalImagePath));
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -            if (options.Enhancers.Length > 0)
 | 
	
		
			
				|  |  | +            if (options.Enhancers.Count > 0)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  if (item == null)
 | 
	
		
			
				|  |  |                  {
 | 
	
	
		
			
				|  | @@ -279,7 +277,7 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private ImageFormat GetOutputFormat(ImageFormat[] clientSupportedFormats, bool requiresTransparency)
 | 
	
		
			
				|  |  | +        private ImageFormat GetOutputFormat(IReadOnlyCollection<ImageFormat> clientSupportedFormats, bool requiresTransparency)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              var serverFormats = GetSupportedImageOutputFormats();
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -320,11 +318,6 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Increment this when there's a change requiring caches to be invalidated
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        private const string Version = "3";
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |          /// <summary>
 | 
	
		
			
				|  |  |          /// Gets the cache file path based on a set of parameters
 | 
	
		
			
				|  |  |          /// </summary>
 | 
	
	
		
			
				|  | @@ -372,9 +365,11 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              return GetCachePath(ResizedImageCachePath, filename, "." + format.ToString().ToLowerInvariant());
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info)
 | 
	
		
			
				|  |  |              => GetImageDimensions(item, info, true);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public ImageDimensions GetImageDimensions(BaseItem item, ItemImageInfo info, bool updateItem)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              int width = info.Width;
 | 
	
	
		
			
				|  | @@ -400,26 +395,19 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              return size;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the size of the image.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public ImageDimensions GetImageDimensions(string path)
 | 
	
		
			
				|  |  |              => _imageEncoder.GetImageSize(path);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the image cache tag.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="item">The item.</param>
 | 
	
		
			
				|  |  | -        /// <param name="image">The image.</param>
 | 
	
		
			
				|  |  | -        /// <returns>Guid.</returns>
 | 
	
		
			
				|  |  | -        /// <exception cref="ArgumentNullException">item</exception>
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public string GetImageCacheTag(BaseItem item, ItemImageInfo image)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            var supportedEnhancers = GetSupportedEnhancers(item, image.Type);
 | 
	
		
			
				|  |  | +            var supportedEnhancers = GetSupportedEnhancers(item, image.Type).ToArray();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              return GetImageCacheTag(item, image, supportedEnhancers);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public string GetImageCacheTag(BaseItem item, ChapterInfo chapter)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              try
 | 
	
	
		
			
				|  | @@ -437,22 +425,15 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the image cache tag.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="item">The item.</param>
 | 
	
		
			
				|  |  | -        /// <param name="image">The image.</param>
 | 
	
		
			
				|  |  | -        /// <param name="imageEnhancers">The image enhancers.</param>
 | 
	
		
			
				|  |  | -        /// <returns>Guid.</returns>
 | 
	
		
			
				|  |  | -        /// <exception cref="ArgumentNullException">item</exception>
 | 
	
		
			
				|  |  | -        public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IImageEnhancer[] imageEnhancers)
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  | +        public string GetImageCacheTag(BaseItem item, ItemImageInfo image, IReadOnlyCollection<IImageEnhancer> imageEnhancers)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              string originalImagePath = image.Path;
 | 
	
		
			
				|  |  |              DateTime dateModified = image.DateModified;
 | 
	
		
			
				|  |  |              ImageType imageType = image.Type;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              // Optimization
 | 
	
		
			
				|  |  | -            if (imageEnhancers.Length == 0)
 | 
	
		
			
				|  |  | +            if (imageEnhancers.Count == 0)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  return (originalImagePath + dateModified.Ticks).GetMD5().ToString("N", CultureInfo.InvariantCulture);
 | 
	
		
			
				|  |  |              }
 | 
	
	
		
			
				|  | @@ -480,7 +461,7 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  try
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    string filename = (originalImagePath + dateModified.Ticks.ToString(UsCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
 | 
	
		
			
				|  |  | +                    string filename = (originalImagePath + dateModified.Ticks.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("N", CultureInfo.InvariantCulture);
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |                      string cacheExtension = _mediaEncoder().SupportsEncoder("libwebp") ? ".webp" : ".png";
 | 
	
		
			
				|  |  |                      var outputPath = Path.Combine(_appPaths.ImageCachePath, "converted-images", filename + cacheExtension);
 | 
	
	
		
			
				|  | @@ -507,16 +488,10 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              return (originalImagePath, dateModified);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        /// <summary>
 | 
	
		
			
				|  |  | -        /// Gets the enhanced image.
 | 
	
		
			
				|  |  | -        /// </summary>
 | 
	
		
			
				|  |  | -        /// <param name="item">The item.</param>
 | 
	
		
			
				|  |  | -        /// <param name="imageType">Type of the image.</param>
 | 
	
		
			
				|  |  | -        /// <param name="imageIndex">Index of the image.</param>
 | 
	
		
			
				|  |  | -        /// <returns>Task{System.String}.</returns>
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public async Task<string> GetEnhancedImage(BaseItem item, ImageType imageType, int imageIndex)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            var enhancers = GetSupportedEnhancers(item, imageType);
 | 
	
		
			
				|  |  | +            var enhancers = GetSupportedEnhancers(item, imageType).ToArray();
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |              ItemImageInfo imageInfo = item.GetImageInfo(imageType, imageIndex);
 | 
	
		
			
				|  |  |  
 | 
	
	
		
			
				|  | @@ -532,7 +507,7 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              bool inputImageSupportsTransparency,
 | 
	
		
			
				|  |  |              BaseItem item,
 | 
	
		
			
				|  |  |              int imageIndex,
 | 
	
		
			
				|  |  | -            IImageEnhancer[] enhancers,
 | 
	
		
			
				|  |  | +            IReadOnlyCollection<IImageEnhancer> enhancers,
 | 
	
		
			
				|  |  |              CancellationToken cancellationToken)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              var originalImagePath = image.Path;
 | 
	
	
		
			
				|  | @@ -573,6 +548,7 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |          /// <param name="imageIndex">Index of the image.</param>
 | 
	
		
			
				|  |  |          /// <param name="supportedEnhancers">The supported enhancers.</param>
 | 
	
		
			
				|  |  |          /// <param name="cacheGuid">The cache unique identifier.</param>
 | 
	
		
			
				|  |  | +        /// <param name="cancellationToken">The cancellation token.</param>
 | 
	
		
			
				|  |  |          /// <returns>Task<System.String>.</returns>
 | 
	
		
			
				|  |  |          /// <exception cref="ArgumentNullException">
 | 
	
		
			
				|  |  |          /// originalImagePath
 | 
	
	
		
			
				|  | @@ -584,9 +560,9 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              BaseItem item,
 | 
	
		
			
				|  |  |              ImageType imageType,
 | 
	
		
			
				|  |  |              int imageIndex,
 | 
	
		
			
				|  |  | -            IImageEnhancer[] supportedEnhancers,
 | 
	
		
			
				|  |  | +            IReadOnlyCollection<IImageEnhancer> supportedEnhancers,
 | 
	
		
			
				|  |  |              string cacheGuid,
 | 
	
		
			
				|  |  | -            CancellationToken cancellationToken)
 | 
	
		
			
				|  |  | +            CancellationToken cancellationToken = default)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              if (string.IsNullOrEmpty(originalImagePath))
 | 
	
		
			
				|  |  |              {
 | 
	
	
		
			
				|  | @@ -680,6 +656,7 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  throw new ArgumentNullException(nameof(path));
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |              if (string.IsNullOrEmpty(uniqueName))
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  |                  throw new ArgumentNullException(nameof(uniqueName));
 | 
	
	
		
			
				|  | @@ -722,6 +699,7 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              return Path.Combine(path, prefix, filename);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public void CreateImageCollage(ImageCollageOptions options)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              _logger.LogInformation("Creating image collage and saving to {Path}", options.OutputPath);
 | 
	
	
		
			
				|  | @@ -731,38 +709,25 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              _logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath);
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        public IImageEnhancer[] GetSupportedEnhancers(BaseItem item, ImageType imageType)
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  | +        public IEnumerable<IImageEnhancer> GetSupportedEnhancers(BaseItem item, ImageType imageType)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  | -            List<IImageEnhancer> list = null;
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |              foreach (var i in ImageEnhancers)
 | 
	
		
			
				|  |  |              {
 | 
	
		
			
				|  |  | -                try
 | 
	
		
			
				|  |  | -                {
 | 
	
		
			
				|  |  | -                    if (i.Supports(item, imageType))
 | 
	
		
			
				|  |  | -                    {
 | 
	
		
			
				|  |  | -                        if (list == null)
 | 
	
		
			
				|  |  | -                        {
 | 
	
		
			
				|  |  | -                            list = new List<IImageEnhancer>();
 | 
	
		
			
				|  |  | -                        }
 | 
	
		
			
				|  |  | -                        list.Add(i);
 | 
	
		
			
				|  |  | -                    }
 | 
	
		
			
				|  |  | -                }
 | 
	
		
			
				|  |  | -                catch (Exception ex)
 | 
	
		
			
				|  |  | +                if (i.Supports(item, imageType))
 | 
	
		
			
				|  |  |                  {
 | 
	
		
			
				|  |  | -                    _logger.LogError(ex, "Error in image enhancer: {0}", i.GetType().Name);
 | 
	
		
			
				|  |  | +                    yield return i;
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -            return list == null ? Array.Empty<IImageEnhancer>() : list.ToArray();
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private Dictionary<string, LockInfo> _locks = new Dictionary<string, LockInfo>();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          private class LockInfo
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              public SemaphoreSlim Lock = new SemaphoreSlim(1, 1);
 | 
	
		
			
				|  |  |              public int Count = 1;
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |          private LockInfo GetLock(string key)
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              lock (_locks)
 | 
	
	
		
			
				|  | @@ -795,7 +760,7 @@ namespace Emby.Drawing
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |          }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -        private bool _disposed;
 | 
	
		
			
				|  |  | +        /// <inheritdoc />
 | 
	
		
			
				|  |  |          public void Dispose()
 | 
	
		
			
				|  |  |          {
 | 
	
		
			
				|  |  |              _disposed = true;
 |