[backend/drive] Add ImageProcessorConcurrency configuration option, default it to 8 (was: unrestricted)
This commit is contained in:
parent
aaf3be209d
commit
1fba1ab119
4 changed files with 32 additions and 19 deletions
|
@ -207,6 +207,8 @@ public sealed class Config
|
||||||
public int MaxResolutionMpx { get; init; } = 30;
|
public int MaxResolutionMpx { get; init; } = 30;
|
||||||
public bool LocalOnly { get; init; } = false;
|
public bool LocalOnly { get; init; } = false;
|
||||||
|
|
||||||
|
[Range(0, 128)] public int ImageProcessorConcurrency { get; init; } = 8;
|
||||||
|
|
||||||
public string MaxFileSize
|
public string MaxFileSize
|
||||||
{
|
{
|
||||||
get => MaxFileSizeBytes.ToString();
|
get => MaxFileSizeBytes.ToString();
|
||||||
|
|
|
@ -343,7 +343,7 @@ public class DriveService(
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<ImageVerTriple?> ProcessAndStoreFileVersion(
|
private async Task<ImageVerTriple?> ProcessAndStoreFileVersion(
|
||||||
ImageVersion version, Func<Stream>? encode, string fileName
|
ImageVersion version, Func<Task<Stream>>? encode, string fileName
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (encode == null) return null;
|
if (encode == null) return null;
|
||||||
|
@ -354,7 +354,7 @@ public class DriveService(
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var sw = Stopwatch.StartNew();
|
var sw = Stopwatch.StartNew();
|
||||||
stream = encode();
|
stream = await encode();
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
logger.LogDebug("Encoding {version} image took {ms} ms",
|
logger.LogDebug("Encoding {version} image took {ms} ms",
|
||||||
version.Key.ToString().ToLowerInvariant(), sw.ElapsedMilliseconds);
|
version.Key.ToString().ToLowerInvariant(), sw.ElapsedMilliseconds);
|
||||||
|
@ -426,11 +426,7 @@ public class DriveService(
|
||||||
private static string GenerateAccessKey(string prefix = "", string extension = "webp")
|
private static string GenerateAccessKey(string prefix = "", string extension = "webp")
|
||||||
{
|
{
|
||||||
var guid = Guid.NewGuid().ToStringLower();
|
var guid = Guid.NewGuid().ToStringLower();
|
||||||
// @formatter:off
|
return extension.Length > 0 ? $"{prefix}-{guid}.{extension}" : $"{prefix}-{guid}";
|
||||||
return prefix.Length > 0
|
|
||||||
? extension.Length > 0 ? $"{prefix}-{guid}.{extension}" : $"{prefix}-{guid}"
|
|
||||||
: extension.Length > 0 ? $"{guid}.{extension}" : guid;
|
|
||||||
// @formatter:on
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string CleanMimeType(string? mimeType)
|
private static string CleanMimeType(string? mimeType)
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System.Collections.Immutable;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using Iceshrimp.Backend.Core.Configuration;
|
using Iceshrimp.Backend.Core.Configuration;
|
||||||
using Iceshrimp.Backend.Core.Database.Tables;
|
using Iceshrimp.Backend.Core.Database.Tables;
|
||||||
|
using Iceshrimp.Backend.Core.Helpers;
|
||||||
using Iceshrimp.Backend.Core.Services.ImageProcessing;
|
using Iceshrimp.Backend.Core.Services.ImageProcessing;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using static Iceshrimp.Backend.Core.Services.ImageProcessing.ImageVersion;
|
using static Iceshrimp.Backend.Core.Services.ImageProcessing.ImageVersion;
|
||||||
|
@ -15,6 +16,8 @@ public class ImageProcessor
|
||||||
|
|
||||||
private readonly List<IImageProcessor> _imageProcessors;
|
private readonly List<IImageProcessor> _imageProcessors;
|
||||||
private readonly ILogger<ImageProcessor> _logger;
|
private readonly ILogger<ImageProcessor> _logger;
|
||||||
|
private readonly SemaphorePlus _semaphore;
|
||||||
|
private readonly int _concurrency;
|
||||||
|
|
||||||
public ImageProcessor(
|
public ImageProcessor(
|
||||||
ILogger<ImageProcessor> logger, IOptionsMonitor<Config.StorageSection> config,
|
ILogger<ImageProcessor> logger, IOptionsMonitor<Config.StorageSection> config,
|
||||||
|
@ -24,6 +27,8 @@ public class ImageProcessor
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_config = config;
|
_config = config;
|
||||||
_imageProcessors = imageProcessors.OrderBy(p => p.Priority).ToList();
|
_imageProcessors = imageProcessors.OrderBy(p => p.Priority).ToList();
|
||||||
|
_concurrency = config.CurrentValue.MediaProcessing.ImageProcessorConcurrency;
|
||||||
|
_semaphore = new SemaphorePlus(Math.Max(_concurrency, 1));
|
||||||
|
|
||||||
// @formatter:off
|
// @formatter:off
|
||||||
if (_imageProcessors.Count == 0)
|
if (_imageProcessors.Count == 0)
|
||||||
|
@ -63,17 +68,31 @@ public class ImageProcessor
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
var results = formats
|
var results = formats
|
||||||
.ToDictionary<ImageVersion, ImageVersion, Func<Stream>?>(p => p, ProcessImageFormat)
|
.ToDictionary<ImageVersion, ImageVersion, Func<Task<Stream>>?>(p => p, ProcessImageFormat)
|
||||||
.AsReadOnly();
|
.AsReadOnly();
|
||||||
|
|
||||||
return new ProcessedImage(ident) { RequestedFormats = results, Blurhash = blurhash };
|
return new ProcessedImage(ident) { RequestedFormats = results, Blurhash = blurhash };
|
||||||
|
|
||||||
Func<Stream>? ProcessImageFormat(ImageVersion p)
|
Func<Task<Stream>>? ProcessImageFormat(ImageVersion p)
|
||||||
{
|
{
|
||||||
if (p.Format is ImageFormat.Keep) return () => new MemoryStream(buf);
|
if (p.Format is ImageFormat.Keep) return () => Task.FromResult<Stream>(new MemoryStream(buf));
|
||||||
var proc = _imageProcessors.FirstOrDefault(i => i.CanEncode(p.Format));
|
var proc = _imageProcessors.FirstOrDefault(i => i.CanEncode(p.Format));
|
||||||
if (proc == null) return null;
|
if (proc == null) return null;
|
||||||
return () => proc.Encode(buf, ident, p.Format);
|
return async () =>
|
||||||
|
{
|
||||||
|
if (_concurrency is 0)
|
||||||
|
return proc.Encode(buf, ident, p.Format);
|
||||||
|
|
||||||
|
await _semaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return proc.Encode(buf, ident, p.Format);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_semaphore.Release();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +136,7 @@ public class ImageProcessor
|
||||||
{
|
{
|
||||||
public string? Blurhash;
|
public string? Blurhash;
|
||||||
|
|
||||||
public required IReadOnlyDictionary<ImageVersion, Func<Stream>?> RequestedFormats;
|
public required IReadOnlyDictionary<ImageVersion, Func<Task<Stream>>?> RequestedFormats;
|
||||||
|
|
||||||
public ProcessedImage(IImageInfo info)
|
public ProcessedImage(IImageInfo info)
|
||||||
{
|
{
|
||||||
|
@ -129,9 +148,9 @@ public class ImageProcessor
|
||||||
public ProcessedImage(IImageInfo info, Stream original, DriveFileCreationRequest request) : this(info)
|
public ProcessedImage(IImageInfo info, Stream original, DriveFileCreationRequest request) : this(info)
|
||||||
{
|
{
|
||||||
var format = new ImageFormat.Keep(Path.GetExtension(request.Filename), request.MimeType);
|
var format = new ImageFormat.Keep(Path.GetExtension(request.Filename), request.MimeType);
|
||||||
RequestedFormats = new Dictionary<ImageVersion, Func<Stream>?>
|
RequestedFormats = new Dictionary<ImageVersion, Func<Task<Stream>>?>
|
||||||
{
|
{
|
||||||
{ new ImageVersion(KeyEnum.Original, format), () => original }
|
{ new ImageVersion(KeyEnum.Original, format), () => Task.FromResult(original) }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,10 +161,6 @@ MaxFileSize = 10M
|
||||||
;; Caution: metadata (e.g. location data) for locally originating images will *not* be stripped for files larger than this
|
;; Caution: metadata (e.g. location data) for locally originating images will *not* be stripped for files larger than this
|
||||||
MaxResolutionMpx = 30
|
MaxResolutionMpx = 30
|
||||||
|
|
||||||
;; Should you prefer to reject locally originating images that exceed MaxResolutionMpx, set this option to true.
|
|
||||||
;; Note that this does not apply to remote images, or to local images in a format not supported by the configured image processor.
|
|
||||||
FailIfImageExceedsMaxRes = false
|
|
||||||
|
|
||||||
;; Maxmimum concurrent image encode tasks to run. (0 = no limit)
|
;; Maxmimum concurrent image encode tasks to run. (0 = no limit)
|
||||||
ImageProcessorConcurrency = 8
|
ImageProcessorConcurrency = 8
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue