diff --git a/zero/Media/ImageSharp/Processors/BlurWebProcessor.cs b/zero/Media/ImageSharp/Processors/BlurWebProcessor.cs new file mode 100644 index 00000000..ddb5e49f --- /dev/null +++ b/zero/Media/ImageSharp/Processors/BlurWebProcessor.cs @@ -0,0 +1,48 @@ +using Microsoft.Extensions.Logging; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Web; +using SixLabors.ImageSharp.Web.Commands; +using SixLabors.ImageSharp.Web.Processors; +using System.Globalization; + +namespace zero.Media.ImageSharp.Processors; + +public class BlurWebProcessor : IImageWebProcessor +{ + // + /// The command constant for the gaussian blur sigma. + /// + public const string Blur = "blur"; + + /// + /// The reusable collection of commands. + /// + private static readonly IEnumerable BlurCommands = new[] { Blur }; + + /// + public IEnumerable Commands { get; } = BlurCommands; + + /// + public FormattedImage Process( + FormattedImage image, + ILogger logger, + CommandCollection commands, + CommandParser parser, + CultureInfo culture) + { + if (commands.Contains(Blur)) + { + float sigma = parser.ParseValue(commands.GetValueOrDefault(Blur), culture); + + if (sigma != 0) + { + image.Image.Mutate(x => x.GaussianBlur(sigma)); + } + } + + return image; + } + + /// + public bool RequiresTrueColorPixelFormat(CommandCollection commands, CommandParser parser, CultureInfo culture) => true; +} diff --git a/zero/Media/Indexes/Media_ByChildren.cs b/zero/Media/Indexes/Media_ByChildren.cs deleted file mode 100644 index 0f32893b..00000000 --- a/zero/Media/Indexes/Media_ByChildren.cs +++ /dev/null @@ -1,38 +0,0 @@ -// using Raven.Client.Documents.Indexes; -// -// namespace zero.Media; -// -// public class Media_ByChildren : ZeroMultiMapIndex -// { -// public class Result : ZeroIdEntity, ISupportsDbConventions -// { -// public string ParentId { get; set; } -// -// public int ChildrenCount { get; set; } -// -// public string[] ChildrenIds { get; set; } -// } -// -// -// protected override void Create() -// { -// AddMap(items => items.Select(item => new Result() -// { -// Id = item.Id, -// ParentId = item.ParentId, -// ChildrenCount = 1, -// ChildrenIds = new string[] { } -// })); -// -// Reduce = results => results.GroupBy(x => new { x.ParentId }).Select(group => new Result() -// { -// Id = null, -// ParentId = group.Key.ParentId, -// ChildrenCount = group.Sum(x => x.ChildrenCount), -// ChildrenIds = group.Select(x => x.Id).ToArray() -// }); -// -// StoreAllFields(FieldStorage.Yes); -// Index(x => x.ParentId, FieldIndexing.Exact); -// } -// } \ No newline at end of file diff --git a/zero/Media/Indexes/Media_ByHierarchy.cs b/zero/Media/Indexes/Media_ByHierarchy.cs deleted file mode 100644 index 46ce620a..00000000 --- a/zero/Media/Indexes/Media_ByHierarchy.cs +++ /dev/null @@ -1,45 +0,0 @@ -// using Raven.Client.Documents.Indexes; -// -// namespace zero.Media; -// -// public class Media_ByHierarchy : ZeroIndex -// { -// public class Result : ZeroIdEntity, ISupportsDbConventions -// { -// public string Name { get; set; } -// -// public List Path { get; set; } = new List(); -// -// public string[] PathIds { get; set; } = Array.Empty(); -// } -// -// -// public class PathResult -// { -// public string Id { get; set; } -// -// public string Name { get; set; } -// } -// -// -// protected override void Create() -// { -// Map = items => items -// .Select(item => new -// { -// Item = item, -// Path = Recurse(item, x => LoadDocument(x.ParentId)).Where(x => x != null && x.Id != null && x.Id != item.Id).Reverse() -// }) -// .Select(item => new Result -// { -// Id = item.Item.Id, -// Name = item.Item.Name, -// Path = item.Path.Select(current => new PathResult() { Id = current.Id, Name = current.Name }).ToList(), -// PathIds = item.Path.Select(current => current.Id).ToArray() -// }); -// -// StoreAllFields(FieldStorage.Yes); -// Index("PathIds", FieldIndexing.Exact); -// //Index(x => x.ChannelId, FieldIndexing.Exact); -// } -// } diff --git a/zero/Media/MediaCreator.cs b/zero/Media/MediaCreator.cs index 16751620..04c00f02 100644 --- a/zero/Media/MediaCreator.cs +++ b/zero/Media/MediaCreator.cs @@ -1,6 +1,9 @@ using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Jpeg; using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; @@ -61,6 +64,7 @@ public class MediaCreator : IMediaCreator if (isImage) { + // load image to create thumbnails using Image image = await Image.LoadAsync(fileInfo.AbsolutePath, cancellationToken); model.Metadata = GetImageMetadata(image.Metadata, image.Size); @@ -68,17 +72,22 @@ public class MediaCreator : IMediaCreator if (Options.Thumbnails != null) { - foreach ((string key, ResizeOptions opts) in Options.Thumbnails) + foreach ((string key, ThumbnailOptions opts) in Options.Thumbnails) { Image imageFrame = image.Frames.Count > 1 ? image.Frames.CloneFrame(0) : image.Clone(); - imageFrame.Mutate(x => x.Resize(opts)); + IImageEncoder encoder = opts.Encoder ?? new WebpEncoder(); + + if (opts.Mutate != null) + { + imageFrame.Mutate(opts.Mutate); + } using MemoryStream stream = new(); - await imageFrame.SaveAsync(stream, new PngEncoder(), cancellationToken); + await imageFrame.SaveAsync(stream, encoder, cancellationToken); stream.Position = 0; - string thumbFilename = normalizedFilename.TrimEnd(extension) + "." + Safenames.File(key) + ".png"; + string thumbFilename = normalizedFilename.TrimEnd(extension) + "." + Safenames.File(key) + opts.Extension.Or(".webp"); string path = directory + '/' + thumbFilename; await FileSystem.CreateFile(path, stream, cancellationToken: cancellationToken); diff --git a/zero/Media/MediaOptions.cs b/zero/Media/MediaOptions.cs index c74e48a7..a8a74905 100644 --- a/zero/Media/MediaOptions.cs +++ b/zero/Media/MediaOptions.cs @@ -1,11 +1,13 @@ -using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Web.Caching; namespace zero.Media; public class MediaOptions { - public string FolderPath { get; set; } + public string FolderPath { get; set; } = string.Empty; public string PublicPathPrefix { get; set; } = string.Empty; @@ -13,7 +15,7 @@ public class MediaOptions public List AllowedImageFileExtensions { get; set; } - public Dictionary Thumbnails { get; set; } + public Dictionary Thumbnails { get; set; } public ImageSharpOptions ImageSharp { get; set; } = new(); } @@ -40,4 +42,14 @@ public class ImagingSharpRemoteCacheOptions public string MediaFolder { get; set; } = "/media/_remote/"; public string KeyMapFile { get; set; } = "/Config/remotekeys.json"; +} + + +public class ThumbnailOptions +{ + public string Extension { get; set; } + + public IImageEncoder Encoder { get; set; } + + public Action Mutate { get; set; } } \ No newline at end of file diff --git a/zero/Media/ZeroMediaModule.cs b/zero/Media/ZeroMediaModule.cs index 9eaf369c..60c258d8 100644 --- a/zero/Media/ZeroMediaModule.cs +++ b/zero/Media/ZeroMediaModule.cs @@ -22,7 +22,13 @@ internal class ZeroMediaModule : ZeroModule .AddProvider() .AddProcessor() .AddProcessor() - .Configure(configuration.GetSection("Zero:Media:ImageSharp:Cache")); + .AddProcessor(); + + services.AddOptions().Configure(opts => + { + opts.CacheRootPath = "~/"; + opts.CacheFolder = "cache"; + }).Bind(configuration.GetSection("Zero:Media:ImageSharp:Cache")); //configuration.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), "Config/imaging.json"), true, true); //configuration.AddJsonFile(Path.Combine(Directory.GetCurrentDirectory(), $"Config/imaging.{builder.Environment.EnvironmentName}.json"), true);