testing using ffmpeg to generate thumbnails for avif

This commit is contained in:
2025-08-17 03:16:19 -04:00
parent d36aaac836
commit 8808126905
4 changed files with 104 additions and 1 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

@@ -27,6 +27,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 +58,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;
@@ -112,6 +135,9 @@ public class ThumbnailService(IMongoDatabase db, AobaService aobaService)
public static async Task<Maybe<Stream>> GenerateImageThumbnailAsync(Stream stream, ThumbnailSize size, string ext, CancellationToken cancellationToken = default)
{
if(ext == ".avif")
return GenerateAvifThumbnail(stream, size, cancellationToken);
var img = LoadImage(stream, ext);
if (img.HasError)
return img.Error;
@@ -132,7 +158,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 +192,49 @@ 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 filePath = $"/tmp/{fn}.in";
using var source = new FileStream(filePath, FileMode.CreateNew);
data.CopyTo(source);
source.Flush();
source.Dispose();
data.Dispose();
try
{
var output = new MemoryStream();
FFMpegArguments.FromFileInput(filePath, false, opt =>
{
//opt.WithCustomArgument("-vf ");
})
.OutputToPipe(new StreamPipeSink(output), opt =>
{
var tonemap = ",format=gbrpf32le,zscale=primaries=bt2020:transfer=smpte2084:matrix=gbr,tonemap=hable,zscale=primaries=bt709:transfer=bt709:matrix=bt709,format=yuv420p";
var args = $"-vf \"crop='min(in_w,in_h)':'min(in_w,in_h)',scale={w}:{w},"
+ $"{tonemap}\""
//+ "zscale=primaries=bt2020:transfer=smpte2084:matrix=bt2020nc,format=gbrpf32le,"
//+ "zscale=primaries=bt709:transfer=bt709:matrix=bt709:range=tv,tonemap=hable,"
//+ "zscale=matrix=bt709:transfer=bt709:primaries=bt709,"
//+ "format=yuv420p\" "
+ "-colorspace bt709";
opt.WithCustomArgument(args)
.ForceFormat("webp");
}).ProcessSynchronously();
output.Position = 0;
return output;
}
catch(Exception ex)
{
return ex;
}
finally
{
File.Delete(filePath);
}
}
public async Task<Maybe<Stream>> GenerateDocumentThumbnailAsync(Stream data, ThumbnailSize size, CancellationToken cancellationToken = default)
{
return new NotImplementedException();