Image analyzing and transcoding

This commit is contained in:
2025-04-15 00:15:52 +08:00
parent 4512f47299
commit b9fc1907fc
3 changed files with 96 additions and 5 deletions

View File

@ -1,22 +1,59 @@
using System.Globalization;
using FFMpegCore;
using System.Security.Cryptography;
using Blurhash.ImageSharp;
using Microsoft.EntityFrameworkCore;
using Minio;
using Minio.DataModel.Args;
using Minio.DataModel.Tags;
using NodaTime;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Metadata.Profiles.Exif;
using ExifTag = SixLabors.ImageSharp.Metadata.Profiles.Exif.ExifTag;
namespace DysonNetwork.Sphere.Storage;
public class FileService(AppDatabase db, IConfiguration configuration)
{
public async Task<CloudFile> AnalyzeFileAsync(
private static readonly List<ExifTag> BlacklistExifTags =
[
ExifTag.GPSLatitudeRef,
ExifTag.GPSLatitude,
ExifTag.GPSLongitudeRef,
ExifTag.GPSLongitude,
ExifTag.GPSAltitudeRef,
ExifTag.GPSAltitude,
ExifTag.GPSSatellites,
ExifTag.GPSStatus,
ExifTag.GPSMeasureMode,
ExifTag.GPSDOP,
ExifTag.GPSSpeedRef,
ExifTag.GPSSpeed,
ExifTag.GPSTrackRef,
ExifTag.GPSTrack,
ExifTag.GPSImgDirectionRef,
ExifTag.GPSImgDirection,
ExifTag.GPSMapDatum,
ExifTag.GPSDestLatitudeRef,
ExifTag.GPSDestLatitude,
ExifTag.GPSDestLongitudeRef,
ExifTag.GPSDestLongitude,
ExifTag.GPSDestBearingRef,
ExifTag.GPSDestBearing,
ExifTag.GPSDestDistanceRef,
ExifTag.GPSDestDistance,
ExifTag.GPSProcessingMethod,
ExifTag.GPSAreaInformation,
ExifTag.GPSDateStamp,
ExifTag.GPSDifferential
];
public async Task<(CloudFile, Stream)> AnalyzeFileAsync(
Account.Account account,
string fileId,
Stream stream,
string fileName,
string? contentType
string? contentType,
string? filePath = null
)
{
var fileSize = stream.Length;
@ -24,7 +61,7 @@ public class FileService(AppDatabase db, IConfiguration configuration)
contentType ??= !fileName.Contains('.') ? "application/octet-stream" : MimeTypes.GetMimeType(fileName);
var existingFile = await db.Files.Where(f => f.Hash == hash).FirstOrDefaultAsync();
if (existingFile is not null) return existingFile;
if (existingFile is not null) return (existingFile, stream);
var file = new CloudFile
{
@ -38,6 +75,53 @@ public class FileService(AppDatabase db, IConfiguration configuration)
switch (contentType.Split('/')[0])
{
case "image":
stream.Position = 0;
using (var imageSharp = await Image.LoadAsync<Rgba32>(stream))
{
var width = imageSharp.Width;
var height = imageSharp.Height;
var blurhash = Blurhasher.Encode(imageSharp, 3, 3);
var format = imageSharp.Metadata.DecodedImageFormat?.Name ?? "unknown";
var exifProfile = imageSharp.Metadata.ExifProfile;
ushort orientation = 1;
List<IExifValue> exif = [];
if (exifProfile is not null)
{
exif = exifProfile.Values
.Where(v => !BlacklistExifTags.Contains((ExifTag)v.Tag))
.ToList<IExifValue>();
if (exifProfile.Values.FirstOrDefault(e => e.Tag == ExifTag.Orientation)
?.GetValue() is ushort o)
orientation = o;
}
if (orientation is 6 or 8)
(width, height) = (height, width);
var aspectRatio = height != 0 ? (double)width / height : 0;
file.FileMeta = new Dictionary<string, object>
{
["blur"] = blurhash,
["format"] = format,
["width"] = width,
["height"] = height,
["orientation"] = orientation,
["ratio"] = aspectRatio,
["exif"] = exif
};
var newStream = new MemoryStream();
await imageSharp.SaveAsWebpAsync(newStream);
file.MimeType = "image/webp";
stream = newStream;
}
break;
case "video":
case "audio":
var mediaInfo = await FFProbe.AnalyseAsync(stream);
@ -56,7 +140,7 @@ public class FileService(AppDatabase db, IConfiguration configuration)
db.Files.Add(file);
await db.SaveChangesAsync();
return file;
return (file, stream);
}
private static async Task<string> HashFileAsync(Stream stream, int chunkSize = 1024 * 1024, long? fileSize = null)