Merge pull request #631 from Bond-009/imagesize

Cleanup ImageProcessor and SkiaEncoder
This commit is contained in:
Andrew Rabert 2019-01-20 20:49:51 -05:00 committed by GitHub
commit edcfd8b565
8 changed files with 106 additions and 194 deletions

View File

@ -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)
{ => TransparentImageTypes.Contains(Path.GetExtension(path).ToLower());
return TransparentImageTypes.Contains(Path.GetExtension(path) ?? string.Empty);
}
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; ItemImageInfo originalImage = options.Image;
var item = options.Item; 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; string originalImagePath = originalImage.Path;
var dateModified = originalImage.DateModified; DateTime dateModified = originalImage.DateModified;
var originalImageSize = originalImage.Width > 0 && originalImage.Height > 0 ? new ImageSize(originalImage.Width, originalImage.Height) : (ImageSize?)null; 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; originalImagePath = supportedImageInfo.path;
dateModified = supportedImageInfo.Item2; dateModified = supportedImageInfo.dateModified;
var requiresTransparency = TransparentImageTypes.Contains(Path.GetExtension(originalImagePath) ?? string.Empty); 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; originalImagePath = tuple.path;
dateModified = tuple.Item2; dateModified = tuple.dateModified;
requiresTransparency = tuple.Item3; requiresTransparency = tuple.transparent;
// TODO: Get this info // TODO: Get this info
originalImageSize = null; originalImageSize = null;
} }
var photo = item as Photo; bool autoOrient = false;
var 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); ImageSize newSize = ImageHelper.GetNewImageSize(options, null);
//if (originalImageSize.HasValue && options.HasDefaultOptions(originalImagePath, originalImageSize.Value) && !autoOrient) int quality = options.Quality;
//{
// // 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); ImageFormat outputFormat = GetOutputFormat(options.SupportedOutputFormats, requiresTransparency);
var quality = options.Quality; string cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer);
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);
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) switch(format)
{ {
return MimeTypes.GetMimeType("i.bmp"); 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.Gif)
{
return MimeTypes.GetMimeType("i.gif");
}
if (format == ImageFormat.Jpg)
{
return MimeTypes.GetMimeType("i.jpg");
}
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; var filename = originalPath
+ "width=" + outputSize.Width
filename += "width=" + outputSize.Width; + "height=" + outputSize.Height
+ "quality=" + quality
filename += "height=" + outputSize.Height; + "datemodified=" + dateModified.Ticks
+ "f=" + format;
filename += "quality=" + quality;
filename += "datemodified=" + dateModified.Ticks;
filename += "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)
{ => GetImageSize(item, info, true);
return 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; int width = info.Width;
var height = info.Height; int height = info.Height;
if (height > 0 && width > 0) if (height > 0 && width > 0)
{ {
return new ImageSize return new ImageSize(width, height);
{
Width = width,
Height = height
};
} }
var path = info.Path; string path = info.Path;
_logger.LogInformation("Getting image size for item {0} {1}", item.GetType().Name, 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)) using (var codec = SKCodec.Create(s))
{ {
var info = codec.Info; var info = codec.Info;
return new ImageSize return new ImageSize(info.Width, info.Height);
{ }
Height = info.Height,
Width = info.Width
};
}
} }
/// <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; string originalImagePath = image.Path;
var dateModified = image.DateModified; DateTime dateModified = image.DateModified;
var imageType = image.Type; 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, prefix, filename);
return Path.Combine(path, 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)

View File

@ -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())
{ {

View File

@ -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; public static void DrawPlayedIndicator(SKCanvas canvas, ImageSize imageSize)
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)
{ {
var x = imageSize.Width - OffsetFromTopRightCorner; var x = imageSize.Width - OffsetFromTopRightCorner;

View File

@ -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)

View File

@ -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; public static void DrawUnplayedCountIndicator(SKCanvas canvas, ImageSize imageSize, int count)
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)
{ {
var x = imageSize.Width - OffsetFromTopRightCorner; var x = imageSize.Width - OffsetFromTopRightCorner;
var text = count.ToString(CultureInfo.InvariantCulture); var text = count.ToString(CultureInfo.InvariantCulture);

View File

@ -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()

View File

@ -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)
{ {

View File

@ -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.