Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8964d1c069 | |||
| 8808126905 | |||
| d36aaac836 | |||
| bb2c6c4683 | |||
| f8d457a096 |
46
.github/workflows/.woodpecker.yaml
vendored
46
.github/workflows/.woodpecker.yaml
vendored
@@ -1,46 +0,0 @@
|
||||
pipeline:
|
||||
build-and-push:
|
||||
image: plugins/docker
|
||||
settings:
|
||||
dockerfile: AobaServer/Dockerfile
|
||||
context: .
|
||||
repo: git.kaisei.app/amatsugu/aoba
|
||||
tags: latest
|
||||
username:
|
||||
from_secret: docker_user
|
||||
password:
|
||||
from_secret: docker_pass
|
||||
|
||||
deploy:
|
||||
image: appleboy/ssh
|
||||
settings:
|
||||
host: your-app-host.internal
|
||||
username: deploy
|
||||
key:
|
||||
from_secret: ssh_key
|
||||
port: 22
|
||||
script:
|
||||
- docker pull git.kaisei.app/amatsugu/aoba:latest
|
||||
|
||||
# Run temporary container on docker network, no port binding
|
||||
- docker run -d --rm \
|
||||
--name aoba-temp \
|
||||
--network aoba-net \
|
||||
git.kaisei.app/amatsugu/aoba:latest
|
||||
|
||||
# Wait for it to become healthy
|
||||
- sleep 3
|
||||
- curl -f http://aoba-temp:8080 --connect-timeout 2 || (echo "Health check failed" && docker stop aoba-temp && exit 1)
|
||||
|
||||
# Stop old container (bound to host port)
|
||||
- docker stop aoba || true
|
||||
|
||||
# Start new container on network and bind to host port 8080
|
||||
- docker run -d --rm \
|
||||
--name aoba \
|
||||
--network aoba-net \
|
||||
-p 9432:8080 \
|
||||
git.kaisei.app/amatsugu/aoba:latest
|
||||
|
||||
# Stop temp container
|
||||
- docker stop aoba-temp || true
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
19
AobaServer/Services/DebugService.cs
Normal file
19
AobaServer/Services/DebugService.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user