diff --git a/Services/DatabaseService.cs b/Services/DatabaseService.cs index 400f675..197a6a0 100644 --- a/Services/DatabaseService.cs +++ b/Services/DatabaseService.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading.Tasks; using Dapper; using Gott.Models; using Microsoft.Data.Sqlite; +using SixLabors.ImageSharp; +using SixLabors.ImageSharp.Formats.Webp; namespace Gott.Services; @@ -258,6 +261,63 @@ public class DatabaseService CREATE INDEX IF NOT EXISTS idx_shared_messages_token ON shared_messages(share_token); CREATE INDEX IF NOT EXISTS idx_shared_messages_message ON shared_messages(message_id); "); + + // Migration: Bestehende PNG/JPEG-Avatare/Images zu WebP konvertieren + MigrateImagesToWebP(conn); + } + + private void MigrateImagesToWebP(SqliteConnection conn) + { + const int webPQuality = 80; + + // Agent Avatare + var agentImages = conn.Query<(string AgentId, string Avatar)>( + "SELECT agent_id, avatar FROM agents WHERE avatar IS NOT NULL AND avatar != '' AND avatar NOT LIKE 'data:image/webp%'") + .ToList(); + + foreach (var (agentId, avatar) in agentImages) + { + var converted = ConvertDataUrlToWebP(avatar, webPQuality); + if (converted != null) + conn.Execute("UPDATE agents SET avatar = @converted WHERE agent_id = @agentId", new { converted, agentId }); + } + + // Session Images + var sessionImages = conn.Query<(string SessionId, string Image)>( + "SELECT session_id, image FROM sessions WHERE image IS NOT NULL AND image != '' AND image NOT LIKE 'data:image/webp%'") + .ToList(); + + foreach (var (sessionId, image) in sessionImages) + { + var converted = ConvertDataUrlToWebP(image, webPQuality); + if (converted != null) + conn.Execute("UPDATE sessions SET image = @converted WHERE session_id = @sessionId", new { converted, sessionId }); + } + + if (agentImages.Count > 0 || sessionImages.Count > 0) + Console.WriteLine($"[Migration] Converted {agentImages.Count} agent avatars and {sessionImages.Count} session images to WebP"); + } + + private static string ConvertDataUrlToWebP(string dataUrl, int quality) + { + try + { + var commaIndex = dataUrl.IndexOf(','); + if (commaIndex < 0) return null; + + var base64 = dataUrl.Substring(commaIndex + 1); + var bytes = Convert.FromBase64String(base64); + + using var image = Image.Load(bytes); + using var ms = new MemoryStream(); + image.Save(ms, new WebpEncoder { Quality = quality }); + + return "data:image/webp;base64," + Convert.ToBase64String(ms.ToArray()); + } + catch + { + return null; // Beschädigte Bilder überspringen + } } private void TryAddColumn(SqliteConnection conn, string table, string column, string type) diff --git a/Services/ImageService.cs b/Services/ImageService.cs index 6ff7336..5f247e5 100644 --- a/Services/ImageService.cs +++ b/Services/ImageService.cs @@ -1,7 +1,7 @@ using System; using System.IO; using SixLabors.ImageSharp; -using SixLabors.ImageSharp.Formats.Png; +using SixLabors.ImageSharp.Formats.Webp; using SixLabors.ImageSharp.Processing; namespace Gott.Services; @@ -9,10 +9,11 @@ namespace Gott.Services; public class ImageService { private const int MaxSideLength = 512; + private const int WebPQuality = 80; /// /// Nimmt eine Base64-Data-URL (z.B. "data:image/png;base64,..."), skaliert das Bild - /// auf max 512px Seitenlänge und gibt eine neue Data-URL zurück. + /// auf max 512px Seitenlänge, konvertiert zu WebP und gibt eine neue Data-URL zurück. /// Gibt null zurück wenn input null/leer ist. /// public string ResizeDataUrl(string dataUrl) @@ -28,20 +29,21 @@ public class ImageService using var image = Image.Load(bytes); - // Nur skalieren wenn nötig - if (image.Width <= MaxSideLength && image.Height <= MaxSideLength) - return dataUrl; - - image.Mutate(x => x.Resize(new ResizeOptions + // Skalieren wenn nötig + if (image.Width > MaxSideLength || image.Height > MaxSideLength) { - Size = new Size(MaxSideLength, MaxSideLength), - Mode = ResizeMode.Max - })); + image.Mutate(x => x.Resize(new ResizeOptions + { + Size = new Size(MaxSideLength, MaxSideLength), + Mode = ResizeMode.Max + })); + } + // Immer als WebP encodieren using var ms = new MemoryStream(); - image.Save(ms, new PngEncoder()); - var resizedBase64 = Convert.ToBase64String(ms.ToArray()); + image.Save(ms, new WebpEncoder { Quality = WebPQuality }); + var webpBase64 = Convert.ToBase64String(ms.ToArray()); - return "data:image/png;base64," + resizedBase64; + return "data:image/webp;base64," + webpBase64; } }