diff --git a/VoidCat/Services/Files/CompressContent.cs b/VoidCat/Services/Files/CompressContent.cs new file mode 100644 index 0000000..cf79e34 --- /dev/null +++ b/VoidCat/Services/Files/CompressContent.cs @@ -0,0 +1,65 @@ +using FFMpegCore; + +namespace VoidCat.Services.Files; + +/// +/// Service which utilizes ffmpeg to strip metadata from media +/// and compress media to reduce storage costs +/// +public class CompressContent +{ + private readonly ILogger _logger; + + public CompressContent(ILogger logger) + { + _logger = logger; + } + + public async Task TryCompressMedia(string input, string output, CancellationToken cts) + { + try + { + string? outMime = null; + switch (Path.GetExtension(input)) + { + case ".jpg": + case ".jpeg": + case ".gif": + case ".png": + case ".bmp": + case ".tiff": + { + output = Path.ChangeExtension(output, ".webp"); + outMime = "image/webp"; + break; + } + } + + var ffmpeg = FFMpegArguments + .FromFileInput(input) + .OutputToFile(output, true, o => + { + o.WithoutMetadata(); + }) + .CancellableThrough(cts); + + _logger.LogInformation("Running: {command}", ffmpeg.Arguments); + var result = await ffmpeg.ProcessAsynchronously(); + return new(result, output) + { + MimeType = outMime + }; + } + catch (Exception ex) + { + _logger.LogError(ex, "Could not strip metadata"); + } + + return new(false, output); + } + + public record CompressResult(bool Success, string OutPath) + { + public string? MimeType { get; init; } + } +} diff --git a/VoidCat/Services/Files/FileStorageStartup.cs b/VoidCat/Services/Files/FileStorageStartup.cs index 889f446..d0dd16e 100644 --- a/VoidCat/Services/Files/FileStorageStartup.cs +++ b/VoidCat/Services/Files/FileStorageStartup.cs @@ -10,7 +10,7 @@ public static class FileStorageStartup { services.AddTransient(); services.AddTransient(); - services.AddTransient(); + services.AddTransient(); if (settings.CloudStorage != default) { diff --git a/VoidCat/Services/Files/LocalDiskFileStorage.cs b/VoidCat/Services/Files/LocalDiskFileStorage.cs index 6554d97..2748727 100644 --- a/VoidCat/Services/Files/LocalDiskFileStorage.cs +++ b/VoidCat/Services/Files/LocalDiskFileStorage.cs @@ -10,9 +10,9 @@ public class LocalDiskFileStore : StreamFileStore, IFileStore { private const string FilesDir = "files-v1"; private readonly VoidSettings _settings; - private readonly StripMetadata _stripMetadata; + private readonly CompressContent _stripMetadata; - public LocalDiskFileStore(VoidSettings settings, IAggregateStatsCollector stats, StripMetadata stripMetadata) + public LocalDiskFileStore(VoidSettings settings, IAggregateStatsCollector stats, CompressContent stripMetadata) : base(stats) { _settings = settings; @@ -58,9 +58,10 @@ public class LocalDiskFileStore : StreamFileStore, IFileStore File.Move(finalPath, srcPath); var dstPath = $"{finalPath}_dst{ext}"; - if (await _stripMetadata.TryStripMediaMetadata(srcPath, dstPath, cts)) + var res = await _stripMetadata.TryCompressMedia(srcPath, dstPath, cts); + if (res.Success) { - File.Move(dstPath, finalPath); + File.Move(res.OutPath, finalPath); File.Delete(srcPath); // recompute metadata @@ -71,7 +72,8 @@ public class LocalDiskFileStore : StreamFileStore, IFileStore Metadata = vf.Metadata! with { Size = (ulong)fInfo.Length, - Digest = hash.ToHex() + Digest = hash.ToHex(), + MimeType = res.MimeType ?? vf.Metadata.MimeType } }; } diff --git a/VoidCat/Services/Files/StripMetadata.cs b/VoidCat/Services/Files/StripMetadata.cs deleted file mode 100644 index a21aff4..0000000 --- a/VoidCat/Services/Files/StripMetadata.cs +++ /dev/null @@ -1,50 +0,0 @@ -using FFMpegCore; -using FFMpegCore.Enums; -using FFMpegCore.Pipes; -using Newtonsoft.Json; - -namespace VoidCat.Services.Files; - -/// -/// Service which utilizes ffmpeg to strip metadata from media -/// -public class StripMetadata -{ - private readonly ILogger _logger; - - public StripMetadata(ILogger logger) - { - _logger = logger; - } - - public async Task TryStripMediaMetadata(string input, string output, CancellationToken cts) - { - try - { - var ffprobe = await FFProbe.AnalyseAsync(input, cancellationToken: cts); - if (ffprobe == default) - { - throw new InvalidOperationException("Could not determine media type with ffprobe"); - } - - _logger.LogInformation("Stripping content from {type}", ffprobe.Format.FormatName); - - var ffmpeg = FFMpegArguments - .FromFileInput(input) - .OutputToFile(output, true, o => - { - o.WithoutMetadata(); - }) - .CancellableThrough(cts); - - _logger.LogInformation("Running: {command}", ffmpeg.Arguments); - return await ffmpeg.ProcessAsynchronously(); - } - catch (Exception ex) - { - _logger.LogError(ex, "Could not strip metadata"); - } - - return false; - } -}