2 Commits

Author SHA1 Message Date
8964d1c069 testing using libvips 2025-08-17 14:57:21 -04:00
8808126905 testing using ffmpeg to generate thumbnails for avif 2025-08-17 03:16:19 -04:00
5 changed files with 101 additions and 2 deletions

View File

@@ -40,6 +40,11 @@ public class AobaService(IMongoDatabase db)
return new PagedResult<Media>(items, page, pageSize, total);
}
public async Task<List<Media>> FindMediaWithExtAsync(string ext, CancellationToken cancellationToken = default)
{
var filter = Builders<Media>.Filter.Eq(m => m.Ext, ext);
return await _media.Find(filter).ToListAsync();
}
public Task AddMediaAsync(Media media, CancellationToken cancellationToken = default)
{
@@ -53,6 +58,13 @@ public class AobaService(IMongoDatabase db)
await _media.UpdateOneAsync(m => m.MediaId == mediaId, upate, cancellationToken: cancellationToken);
}
public async Task RemoveThumbnailAsync(ObjectId mediaId, ThumbnailSize size, CancellationToken cancellationToken = default)
{
var upate = Builders<Media>.Update.Unset(m => m.Thumbnails[size]);
await _media.UpdateOneAsync(m => m.MediaId == mediaId, upate, cancellationToken: cancellationToken);
}
public async Task<ObjectId> GetThumbnailIdAsync(ObjectId mediaId, ThumbnailSize size, CancellationToken cancellationToken = default)
{
var thumb = await _media.Find(m => m.MediaId == mediaId).Project(m => m.Thumbnails[size]).FirstOrDefaultAsync(cancellationToken);

View File

@@ -16,6 +16,7 @@ using SixLabors.ImageSharp.Processing;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.Http.Headers;
using System.Text;
@@ -27,6 +28,28 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
{
private readonly GridFSBucket _gridfs = new GridFSBucket(db);
public async Task<Error?> DeleteThumbnailAsync(ObjectId mediaId, ThumbnailSize size)
{
var thumbId = await aobaService.GetThumbnailIdAsync(mediaId, size);
if (thumbId == default)
return null;
try
{
await _gridfs.DeleteAsync(thumbId);
await aobaService.RemoveThumbnailAsync(mediaId, size);
}
catch (GridFSFileNotFoundException)
{
//Ignore if the file was not found (somehow already deleted)
await aobaService.RemoveThumbnailAsync(mediaId, size);
}
catch (Exception e)
{
return new ExceptionError(e);
}
return null;
}
/// <summary>
///
/// </summary>
@@ -36,6 +59,7 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
/// <returns></returns>
public async Task<Maybe<Stream>> GetOrCreateThumbnailAsync(ObjectId mediaId, ThumbnailSize size, CancellationToken cancellationToken = default)
{
var existingThumb = await GetThumbnailAsync(mediaId, size, cancellationToken);
if (existingThumb != null)
return existingThumb;
@@ -132,7 +156,7 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
return result;
}
public Maybe<Stream> GenerateVideoThumbnail(Stream data, ThumbnailSize size, CancellationToken cancellationToken = default)
public static Maybe<Stream> GenerateVideoThumbnail(Stream data, ThumbnailSize size, CancellationToken cancellationToken = default)
{
var w = (int)size;
var fn = ObjectId.GenerateNewId().ToString();
@@ -166,6 +190,47 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
}
}
public static Maybe<Stream> GenerateAvifThumbnail(Stream data, ThumbnailSize size, CancellationToken cancellationToken)
{
var w = (int)size;
var fn = ObjectId.GenerateNewId().ToString();
var inFilePath = $"/tmp/{fn}.avif";
var outFilePath = $"/tmp/{fn}.webp";
using var source = new FileStream(inFilePath, FileMode.CreateNew);
data.CopyTo(source);
source.Flush();
source.Dispose();
data.Dispose();
try
{
var process = Process.Start(new ProcessStartInfo
{
FileName = "vips",
Arguments = $"smartcrop \"{inFilePath}\" \"{outFilePath}\"[Q=75] {w} {w}",
WorkingDirectory = "/tmp"
});
if (process == null)
return new Error("Failed to run vips command");
process.WaitForExit();
if (process.ExitCode != 0)
return new Error("Failed to convert");
var output = new MemoryStream();
using var oFile = File.OpenRead(outFilePath);
oFile.CopyTo(output);
output.Position = 0;
return output;
}
catch(Exception ex)
{
return ex;
}
finally
{
File.Delete(inFilePath);
File.Delete(outFilePath);
}
}
public async Task<Maybe<Stream>> GenerateDocumentThumbnailAsync(Stream data, ThumbnailSize size, CancellationToken cancellationToken = default)
{
return new NotImplementedException();

View File

@@ -32,7 +32,7 @@ RUN dx bundle --platform web
# Server Build
# This stage is used when running from VS in fast mode (Default for Debug configuration)
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS base
RUN apt-get update && apt-get install -y ffmpeg
RUN apt-get update && apt-get install -y ffmpeg #libvips libvips-tools
USER $APP_UID
WORKDIR /app
EXPOSE 8080

View File

@@ -121,6 +121,9 @@ builder.Services.AddAuthentication(options =>
builder.Services.AddAoba();
#if DEBUG
builder.Services.AddHostedService<DebugService>();
#endif
builder.Services.Configure<FormOptions>(opt =>
{
opt.ValueLengthLimit = int.MaxValue;

View File

@@ -0,0 +1,19 @@

using AobaCore.Services;
namespace AobaServer.Services;
public class DebugService(AobaService aobaService, ThumbnailService thumbnailService) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var mediaItems = await aobaService.FindMediaWithExtAsync(".avif", stoppingToken);
foreach (var item in mediaItems)
{
foreach (var size in item.Thumbnails.Keys)
{
await thumbnailService.DeleteThumbnailAsync(item.MediaId, size);
}
}
}
}