fix: Krea-Bild-URLs auf /api/images/file/ korrigieren, Thumbnail-Endpoint hinzufügen
- ui.js: [Krea: datei] URLs von /api/skills/krea-api/output/ auf /api/images/file/ geändert
- KreaEndpoint: GET /api/images/file/{filename}/thumb mit gecachten WebP-Thumbnails (320px)
- Thumbnail-Cache in data/cache/images/, analog zum SkillEndpoint
This commit is contained in:
1 parent
20f96f69d0
commit
1f5f49228b
2 files changed
+81
-2
No files matched your search
@@ -1,3 +1,5 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -5,6 +7,9 @@ using Gott.Services;
|
|||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
|
using SixLabors.ImageSharp;
|
||||||
|
using SixLabors.ImageSharp.Processing;
|
||||||
|
using SixLabors.ImageSharp.Formats.Webp;
|
||||||
|
|
||||||
namespace Gott.Endpoints
|
namespace Gott.Endpoints
|
||||||
{
|
{
|
||||||
@@ -15,6 +20,7 @@ namespace Gott.Endpoints
|
|||||||
app.MapPost("/api/images/generate", HandleGenerate);
|
app.MapPost("/api/images/generate", HandleGenerate);
|
||||||
app.MapGet("/api/images", HandleList);
|
app.MapGet("/api/images", HandleList);
|
||||||
app.MapGet("/api/images/file/{filename}", HandleFile);
|
app.MapGet("/api/images/file/{filename}", HandleFile);
|
||||||
|
app.MapGet("/api/images/file/{filename}/thumb", HandleThumb);
|
||||||
app.MapGet("/api/images/status", HandleStatus);
|
app.MapGet("/api/images/status", HandleStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,5 +114,78 @@ namespace Gott.Endpoints
|
|||||||
models = KreaService.IMAGE_MODELS.Keys,
|
models = KreaService.IMAGE_MODELS.Keys,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static readonly HashSet<string> ThumbExtensions = new(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
".png", ".jpg", ".jpeg", ".gif", ".webp", ".bmp"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static async Task HandleThumb(HttpRequest req, HttpResponse res, KreaService krea, string filename)
|
||||||
|
{
|
||||||
|
if (filename.Contains("..") || filename.Contains("/") || filename.Contains("\\"))
|
||||||
|
{
|
||||||
|
res.StatusCode = 400;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ext = Path.GetExtension(filename);
|
||||||
|
if (!ThumbExtensions.Contains(ext))
|
||||||
|
{
|
||||||
|
res.StatusCode = 400;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var originalPath = krea.GetImagePath(filename);
|
||||||
|
if (originalPath == null)
|
||||||
|
{
|
||||||
|
res.StatusCode = 404;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache-Verzeichnis: data/cache/images/
|
||||||
|
var imagesDir = Path.GetDirectoryName(originalPath);
|
||||||
|
var dataDir = Path.GetDirectoryName(imagesDir);
|
||||||
|
var cacheDir = Path.Combine(dataDir, "cache", "images");
|
||||||
|
Directory.CreateDirectory(cacheDir);
|
||||||
|
|
||||||
|
var thumbFilename = Path.GetFileNameWithoutExtension(filename) + ".webp";
|
||||||
|
var thumbPath = Path.Combine(cacheDir, thumbFilename);
|
||||||
|
|
||||||
|
if (!File.Exists(thumbPath) || File.GetLastWriteTimeUtc(originalPath) > File.GetLastWriteTimeUtc(thumbPath))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var image = await Image.LoadAsync(originalPath);
|
||||||
|
var maxSize = 320;
|
||||||
|
image.Mutate(x => x.Resize(new ResizeOptions
|
||||||
|
{
|
||||||
|
Size = new Size(maxSize, maxSize),
|
||||||
|
Mode = ResizeMode.Max
|
||||||
|
}));
|
||||||
|
var encoder = new WebpEncoder { Quality = 75 };
|
||||||
|
await image.SaveAsync(thumbPath, encoder);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Fallback: Original ausliefern
|
||||||
|
var contentType = ext switch
|
||||||
|
{
|
||||||
|
".png" => "image/png",
|
||||||
|
".jpg" => "image/jpeg",
|
||||||
|
".jpeg" => "image/jpeg",
|
||||||
|
".gif" => "image/gif",
|
||||||
|
_ => "image/webp",
|
||||||
|
};
|
||||||
|
res.ContentType = contentType;
|
||||||
|
res.Headers["Cache-Control"] = "private, max-age=3600";
|
||||||
|
await res.SendFileAsync(originalPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res.ContentType = "image/webp";
|
||||||
|
res.Headers["Cache-Control"] = "private, max-age=86400";
|
||||||
|
await res.SendFileAsync(thumbPath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -170,8 +170,8 @@ function renderMarkdown(text) {
|
|||||||
html = html.replace(/\[Krea:\s*([^\]]+)\]/g, function(match, filename) {
|
html = html.replace(/\[Krea:\s*([^\]]+)\]/g, function(match, filename) {
|
||||||
var fn = filename.trim();
|
var fn = filename.trim();
|
||||||
var safeFn = esc(fn);
|
var safeFn = esc(fn);
|
||||||
var thumbUrl = '/api/skills/krea-api/output/' + encodeURIComponent(fn) + '/thumb';
|
var thumbUrl = '/api/images/file/' + encodeURIComponent(fn) + '/thumb';
|
||||||
var fullUrl = '/api/skills/krea-api/output/' + encodeURIComponent(fn);
|
var fullUrl = '/api/images/file/' + encodeURIComponent(fn);
|
||||||
return '<span class="krea-ref" data-filename="' + safeFn + '" data-fullurl="' + fullUrl + '" title="Klicken für Vollbild">'
|
return '<span class="krea-ref" data-filename="' + safeFn + '" data-fullurl="' + fullUrl + '" title="Klicken für Vollbild">'
|
||||||
+ '<img class="krea-thumb" src="' + thumbUrl + '" alt="' + safeFn + '" loading="lazy">'
|
+ '<img class="krea-thumb" src="' + thumbUrl + '" alt="' + safeFn + '" loading="lazy">'
|
||||||
+ '</span>';
|
+ '</span>';
|
||||||
|
|||||||
Reference in new issue
Block a user