{
+ private const int MaxThumbnailWidth = 500;
+ private const int MaxThumbnailHeight = 500;
+
public override void Configure()
{
Post("/api/albums/{AlbumId}/photos");
@@ -62,6 +76,7 @@ public class AddPhotoToAlbumHandler(
{
var userId = User.GetUserId();
+ // Fetch the album we want to add photos to
var album = await context
.Albums
.SingleOrDefaultAsync(
@@ -85,37 +100,95 @@ public class AddPhotoToAlbumHandler(
return;
}
- // Get the next order number
- var nextOrder = await context
- .AlbumPhotos
- .Where(p => p.AlbumId == request.AlbumId)
- .MaxAsync(p => (int?)p.Order, ct) ?? 0;
+ try
+ {
+ var (originalUrl, thumbnailUrl) = await ProcessAndUploadImage(request, ct);
- // Upload the photo to blob storage
- var photoUrl = await blobStorage.UploadFileAsync(
+ // Get the next order number
+ var nextOrder = await context
+ .AlbumPhotos
+ .Where(p => p.AlbumId == request.AlbumId)
+ .MaxAsync(p => (int?)p.Order, ct) ?? 0;
+
+ // Create the album photo
+ var photo = new AlbumPhoto
+ {
+ Id = request.PhotoId,
+ CreatedBy = userId,
+ AlbumId = request.AlbumId,
+ OriginalUrl = originalUrl,
+ ThumbnailUrl = thumbnailUrl,
+ Caption = request.Caption,
+ Order = nextOrder + 1
+ };
+
+ context.AlbumPhotos.Add(photo);
+ await context.SaveChangesAsync(ct);
+
+ await SendOkAsync(
+ new AddPhotoToAlbumResponse(photo.Id, originalUrl, thumbnailUrl),
+ ct);
+ }
+ catch (UnknownImageFormatException)
+ {
+ await SendStringAsync("Invalid image format", 400, cancellation: ct);
+ }
+ catch (Exception ex)
+ {
+ await SendStringAsync("Error processing image", 500, cancellation: ct);
+ }
+ }
+
+ private async Task<(string originalUrl, string thumbnailUrl)> ProcessAndUploadImage(
+ AddPhotoToAlbumRequest request,
+ CancellationToken ct)
+ {
+ var originalFileName = Path.GetFileName(request.File.FileName);
+ var nameWithoutExt = Path.GetFileNameWithoutExtension(originalFileName);
+ var extension = Path.GetExtension(originalFileName);
+
+ var filenameOriginal = $"{nameWithoutExt}{extension}";
+ var filenameThumbnail = $"{nameWithoutExt}.thumbnail{extension}";
+
+ var blobOriginal = $"{SubDirectoryNames.Albums}/{request.AlbumId}/{filenameOriginal}";
+ var blobThumbnail = $"{SubDirectoryNames.Albums}/{request.AlbumId}/{filenameThumbnail}";
+
+ // Process the original image
+ await using var originalStream = request.File.OpenReadStream();
+ using var image = await Image.LoadAsync(originalStream, ct);
+
+ // Calculate target size while preserving the original aspect ratio
+ var originalWidth = image.Width;
+ var originalHeight = image.Height;
+
+ double ratioX = (double)MaxThumbnailWidth / originalWidth;
+ double ratioY = (double)MaxThumbnailHeight / originalHeight;
+ double ratio = Math.Min(ratioX, ratioY);
+
+ int newWidth = (int)(originalWidth * ratio);
+ int newHeight = (int)(originalHeight * ratio);
+
+ // Create thumbnail
+ using var thumbnailStream = new MemoryStream();
+ image.Mutate(x => x.Resize(newWidth, newHeight));
+ await image.SaveAsync(thumbnailStream, image.Metadata.DecodedImageFormat!, ct);
+ thumbnailStream.Position = 0;
+
+ // Upload both versions
+ var originalUrl = await blobStorage.UploadFileAsync(
ContainerNames.Creators,
- $"{SubDirectoryNames.Albums}/{request.AlbumId}/{request.PhotoId}",
+ blobOriginal,
request.File.OpenReadStream(),
request.File.ContentType,
ct);
- // Create the album photo
- var photo = new AlbumPhoto
- {
- Id = request.PhotoId,
- CreatedBy = userId,
- AlbumId = request.AlbumId,
- PhotoUrl = photoUrl,
- Caption = request.Caption,
- Order = nextOrder + 1
- };
-
- context.AlbumPhotos.Add(photo);
-
- await context.SaveChangesAsync(ct);
-
- await SendOkAsync(
- new AddPhotoToAlbumResponse(photo.Id, photoUrl),
+ var thumbnailUrl = await blobStorage.UploadFileAsync(
+ ContainerNames.Creators,
+ blobThumbnail,
+ thumbnailStream,
+ request.File.ContentType,
ct);
+
+ return (originalUrl, thumbnailUrl);
}
-}
+}
diff --git a/backend/src/Web/Features/Contents/Handlers/GetAlbum.cs b/backend/src/Web/Features/Contents/Handlers/GetAlbum.cs
index 6aee04f..591a05a 100644
--- a/backend/src/Web/Features/Contents/Handlers/GetAlbum.cs
+++ b/backend/src/Web/Features/Contents/Handlers/GetAlbum.cs
@@ -13,7 +13,8 @@ public record GetAlbumRequest(
[PublicAPI]
public record AlbumPhotoDto(
Guid Id,
- string PhotoUrl,
+ string OriginalUrl,
+ string ThumbnailUrl,
string? Caption,
int Order,
DateTimeOffset CreatedAt);
@@ -68,7 +69,8 @@ public class GetAlbumHandler(
var photos = album.Photos
.Select(p => new AlbumPhotoDto(
p.Id,
- p.PhotoUrl,
+ p.OriginalUrl,
+ p.ThumbnailUrl,
p.Caption,
p.Order,
p.CreatedAt))
diff --git a/backend/src/Web/Web.csproj b/backend/src/Web/Web.csproj
index 3d906ce..13c4ef0 100644
--- a/backend/src/Web/Web.csproj
+++ b/backend/src/Web/Web.csproj
@@ -26,6 +26,7 @@
all
+
diff --git a/frontend/src/views/creators/AboutCreator.vue b/frontend/src/views/creators/AboutCreator.vue
index 884e7aa..7853558 100644
--- a/frontend/src/views/creators/AboutCreator.vue
+++ b/frontend/src/views/creators/AboutCreator.vue
@@ -10,9 +10,9 @@
@@ -20,10 +20,10 @@
@@ -31,9 +31,9 @@
@@ -56,15 +56,15 @@
@@ -88,11 +88,11 @@
@@ -101,18 +101,23 @@
+ }]"
+ :images="thumbnailUrls"
+ @photo-click="handlePhotoClick"/>
+
+
-
+