From 92bd06751d5fa3dea862299fdc8e7e19735c9631 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Wed, 18 Mar 2026 10:26:40 +0100 Subject: [PATCH] Upload playlist image --- Application/Application.csproj | 2 - .../Configuration/PlayListConfiguration.cs | 8 ++- .../Contracts/Application/IPictureService.cs | 11 ++++ .../ResizeCommand.cs | 10 +-- .../ResizePicture/ResizeCommandHandler.cs | 17 ++++++ .../Pictures/Contracts/PictureResize.cs | 6 ++ .../Pictures/Events/ImageResizeEvent.cs | 8 --- .../Pictures/Events/PictureResizeEvent.cs | 8 +++ ...izeCommandHandler.cs => PictureService.cs} | 28 ++++----- .../CreatePlayList/CreatePlayListCommand.cs | 14 +++++ .../CreatePlayListCommandHandler.cs | 61 +++++++++++++++++++ .../PlayLists/Contracts/PlayListResponse.cs | 24 +++++++- .../Consumers/ImageResizedConsumer.cs | 4 +- Musify.Backend/Entities/PlayList.cs | 10 ++- 14 files changed, 174 insertions(+), 37 deletions(-) create mode 100644 Application/Contracts/Application/IPictureService.cs rename Application/Pictures/Commands/{ResizeImage => ResizePicture}/ResizeCommand.cs (54%) create mode 100644 Application/Pictures/Commands/ResizePicture/ResizeCommandHandler.cs create mode 100644 Application/Pictures/Contracts/PictureResize.cs delete mode 100644 Application/Pictures/Events/ImageResizeEvent.cs create mode 100644 Application/Pictures/Events/PictureResizeEvent.cs rename Application/Pictures/{Commands/ResizeImage/ResizeCommandHandler.cs => PictureService.cs} (61%) create mode 100644 Application/PlayLists/Commands/CreatePlayList/CreatePlayListCommand.cs create mode 100644 Application/PlayLists/Commands/CreatePlayList/CreatePlayListCommandHandler.cs diff --git a/Application/Application.csproj b/Application/Application.csproj index 1764b90..d1a1586 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -21,8 +21,6 @@ - - diff --git a/Application/Configuration/PlayListConfiguration.cs b/Application/Configuration/PlayListConfiguration.cs index ea14d99..fea1cf7 100644 --- a/Application/Configuration/PlayListConfiguration.cs +++ b/Application/Configuration/PlayListConfiguration.cs @@ -14,7 +14,7 @@ public int LargePictureWidth { get; private set; } = 512; - public float LargePictureHeight { get; private set; } = 512; + public int LargePictureHeight { get; private set; } = 512; } public class PlayListRoutes @@ -26,5 +26,11 @@ public string MediumPictures { get; set; } = "MediumPictures"; public string LargePictures { get; set; } = "LargePictures"; + + public string DefaultSmallPicture { get; set; } = "DefaultSmallPicture.webp"; + + public string DefaultMediumPicture { get; set; } = "DefaultMediumPicture.webp"; + + public string DefaultLargePicture { get; set; } = "DefaultLargePicture.webp"; } } \ No newline at end of file diff --git a/Application/Contracts/Application/IPictureService.cs b/Application/Contracts/Application/IPictureService.cs new file mode 100644 index 0000000..5adba92 --- /dev/null +++ b/Application/Contracts/Application/IPictureService.cs @@ -0,0 +1,11 @@ +using Ardalis.Result; +using Musify.Application.Pictures.Contracts; + +namespace Musify.Application.Contracts.Application +{ + public interface IPictureService + { + Task> ResizePictureAsync(string keyName, string contentType, Stream pictureStream, PictureResize[] pictureResizes, + CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/Application/Pictures/Commands/ResizeImage/ResizeCommand.cs b/Application/Pictures/Commands/ResizePicture/ResizeCommand.cs similarity index 54% rename from Application/Pictures/Commands/ResizeImage/ResizeCommand.cs rename to Application/Pictures/Commands/ResizePicture/ResizeCommand.cs index c79b5e5..628ad6d 100644 --- a/Application/Pictures/Commands/ResizeImage/ResizeCommand.cs +++ b/Application/Pictures/Commands/ResizePicture/ResizeCommand.cs @@ -1,13 +1,13 @@ using Ardalis.Result; using DispatchR.Abstractions.Send; +using Musify.Application.Pictures.Contracts; -namespace Musify.Application.Pictures.Commands.ResizeImage +namespace Musify.Application.Pictures.Commands.ResizePicture { public record ResizeCommand( - Stream PictureStream, + string KeyName, string ContentType, - int Width, - int Height, - string SaveRoute) + Stream PictureStream, + PictureResize[] PictureResizes) : IRequest>; } \ No newline at end of file diff --git a/Application/Pictures/Commands/ResizePicture/ResizeCommandHandler.cs b/Application/Pictures/Commands/ResizePicture/ResizeCommandHandler.cs new file mode 100644 index 0000000..95b61fd --- /dev/null +++ b/Application/Pictures/Commands/ResizePicture/ResizeCommandHandler.cs @@ -0,0 +1,17 @@ +using Ardalis.Result; +using DispatchR.Abstractions.Send; +using Musify.Application.Contracts.Application; + +namespace Musify.Application.Pictures.Commands.ResizePicture +{ + public class ResizeCommandHandler(IPictureService pictureService) + : IRequestHandler >> + { + public async Task> Handle(ResizeCommand request, CancellationToken cancellationToken) + { + var result = await pictureService.ResizePictureAsync(request.KeyName, request.ContentType, request.PictureStream, request.PictureResizes, cancellationToken); + + return result; + } + } +} \ No newline at end of file diff --git a/Application/Pictures/Contracts/PictureResize.cs b/Application/Pictures/Contracts/PictureResize.cs new file mode 100644 index 0000000..44b79fa --- /dev/null +++ b/Application/Pictures/Contracts/PictureResize.cs @@ -0,0 +1,6 @@ +namespace Musify.Application.Pictures.Contracts +{ + public record PictureResize( + int Width, + int Height); +} \ No newline at end of file diff --git a/Application/Pictures/Events/ImageResizeEvent.cs b/Application/Pictures/Events/ImageResizeEvent.cs deleted file mode 100644 index c08aec6..0000000 --- a/Application/Pictures/Events/ImageResizeEvent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Musify.Application.Pictures.Events -{ - public record ImageResizeEvent( - Guid UploadId, - int Width, - int Height, - string SaveRoute); -} \ No newline at end of file diff --git a/Application/Pictures/Events/PictureResizeEvent.cs b/Application/Pictures/Events/PictureResizeEvent.cs new file mode 100644 index 0000000..8439e14 --- /dev/null +++ b/Application/Pictures/Events/PictureResizeEvent.cs @@ -0,0 +1,8 @@ +using Musify.Application.Pictures.Contracts; + +namespace Musify.Application.Pictures.Events +{ + public record PictureResizeEvent( + Guid UploadId, + PictureResize[] ImageResizes); +} \ No newline at end of file diff --git a/Application/Pictures/Commands/ResizeImage/ResizeCommandHandler.cs b/Application/Pictures/PictureService.cs similarity index 61% rename from Application/Pictures/Commands/ResizeImage/ResizeCommandHandler.cs rename to Application/Pictures/PictureService.cs index 037992b..306131f 100644 --- a/Application/Pictures/Commands/ResizeImage/ResizeCommandHandler.cs +++ b/Application/Pictures/PictureService.cs @@ -1,19 +1,22 @@ using Ardalis.Result; -using DispatchR.Abstractions.Send; using MassTransit; using Microsoft.Extensions.Logging; using Musify.Application.Configuration; +using Musify.Application.Contracts.Application; using Musify.Application.Contracts.Infrastructure; +using Musify.Application.Pictures.Commands.ResizePicture; +using Musify.Application.Pictures.Contracts; using Musify.Application.Pictures.Events; using Musify.Domain.Entities; -namespace Musify.Application.Pictures.Commands.ResizeImage +namespace Musify.Application.Pictures { - public class ResizeCommandHandler(IStorageHandler storageHandler, IDatabase database, IPublishEndpoint publishEndpoint, + public class PictureService(IStorageHandler storageHandler, IDatabase database, IPublishEndpoint publishEndpoint, ILogger logger, StorageConfiguration storageConfiguration) - : IRequestHandler >> + : IPictureService { - public async Task> Handle(ResizeCommand request, CancellationToken cancellationToken) + public async Task> ResizePictureAsync(string keyName, string contentType, Stream pictureStream, PictureResize[] pictureResizes, + CancellationToken cancellationToken) { var pictureId = Guid.NewGuid(); @@ -21,7 +24,7 @@ namespace Musify.Application.Pictures.Commands.ResizeImage { Id = pictureId, KeyName = $"{storageConfiguration.Routes.Uploads}/{pictureId}", - ContentType = request.ContentType, + ContentType = contentType, BucketName = storageConfiguration.BucketName, }; @@ -30,10 +33,10 @@ namespace Musify.Application.Pictures.Commands.ResizeImage await database.SaveChangesAsync(cancellationToken); var uploadResult = await storageHandler.UploadFileAsync( - request.PictureStream, - request.ContentType, + pictureStream, + contentType, storageConfiguration.BucketName, - pictureId.ToString(), + $"{storageConfiguration.Routes.Uploads}/{pictureId}", cancellationToken); if (!uploadResult.IsSuccess) @@ -42,12 +45,7 @@ namespace Musify.Application.Pictures.Commands.ResizeImage return Result.Error("Failed to upload the picture."); } - await publishEndpoint.Publish(new ImageResizeEvent( - pictureId, - request.Width, - request.Height, - request.SaveRoute), - cancellationToken); + await publishEndpoint.Publish(new PictureResizeEvent(pictureId, pictureResizes)); return Result.Success(); } diff --git a/Application/PlayLists/Commands/CreatePlayList/CreatePlayListCommand.cs b/Application/PlayLists/Commands/CreatePlayList/CreatePlayListCommand.cs new file mode 100644 index 0000000..b55d20a --- /dev/null +++ b/Application/PlayLists/Commands/CreatePlayList/CreatePlayListCommand.cs @@ -0,0 +1,14 @@ +using Ardalis.Result; +using DispatchR.Abstractions.Send; +using Musify.Application.PlayLists.Contracts; + +namespace Musify.Application.PlayLists.Commands.CreatePlayList +{ + public record CreatePlayListCommand( + Guid UserId, + string Name, + string Description, + Stream PictureStream, + string PictureContentType) + : IRequest>; +} \ No newline at end of file diff --git a/Application/PlayLists/Commands/CreatePlayList/CreatePlayListCommandHandler.cs b/Application/PlayLists/Commands/CreatePlayList/CreatePlayListCommandHandler.cs new file mode 100644 index 0000000..2ac9fe3 --- /dev/null +++ b/Application/PlayLists/Commands/CreatePlayList/CreatePlayListCommandHandler.cs @@ -0,0 +1,61 @@ +using Ardalis.Result; +using DispatchR.Abstractions.Send; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Musify.Application.Configuration; +using Musify.Application.Contracts.Application; +using Musify.Application.Contracts.Infrastructure; +using Musify.Application.Pictures.Contracts; +using Musify.Application.PlayLists.Contracts; +using Musify.Domain.Entities; + +namespace Musify.Application.PlayLists.Commands.CreatePlayList +{ + public class CreatePlayListCommandHandler(IPictureService pictureService, IDatabase database, ILogger logger, + PlayListConfiguration playListConfiguration) + : IRequestHandler> > + { + public async Task> Handle(CreatePlayListCommand request, CancellationToken cancellationToken) + { + var userExists = await database.Users.AnyAsync(u => u.Id == request.UserId, cancellationToken); + + if (!userExists) + { + logger.LogWarning("User with Id={UserId} does not exist.", request.UserId); + return Result.NotFound($"User with Id {request.UserId} does not exist."); + } + + var playList = new PlayList + { + UserId = request.UserId, + Name = request.Name, + NormalizedName = request.Name.Trim().ToUpperInvariant(), + Description = request.Description, + SmallPictureKeyName = playListConfiguration.Routes.SmallPictures, + MediumPictureKeyName = playListConfiguration.Routes.MediumPictures, + LargePictureKeyName = playListConfiguration.Routes.LargePictures, + }; + + await database.PlayLists.AddAsync(playList, cancellationToken); + + await database.SaveChangesAsync(cancellationToken); + + logger.LogInformation("PlayList with Id={PlayListId} created successfully for UserId={UserId}.", playList.Id, request.UserId); + + await pictureService.ResizePictureAsync( + playList.Id.ToString(), + request.PictureContentType, + request.PictureStream, + [ + new PictureResize(playListConfiguration.SmallPictureWidth, playListConfiguration.SmallPictureHeight), + new PictureResize(playListConfiguration.MediumPictureWidth, playListConfiguration.MediumPictureHeight), + new PictureResize(playListConfiguration.LargePictureWidth, playListConfiguration.LargePictureHeight) + ], + cancellationToken); + + logger.LogInformation("Picture for PlayList with Id={PlayListId} ", playList.Id); + + return Result.Created(PlayListResponse.Map(playList)); + } + } +} \ No newline at end of file diff --git a/Application/PlayLists/Contracts/PlayListResponse.cs b/Application/PlayLists/Contracts/PlayListResponse.cs index 4d4e582..ba96c40 100644 --- a/Application/PlayLists/Contracts/PlayListResponse.cs +++ b/Application/PlayLists/Contracts/PlayListResponse.cs @@ -1,10 +1,28 @@ -namespace Musify.Application.PlayLists.Contracts +using Musify.Domain.Entities; + +namespace Musify.Application.PlayLists.Contracts { public record PlayListResponse( Guid Id, string Name, string Description, - string ImageName, + string SmallImageKeyName, + string MediumImageKeyName, + string LargeImageKeyName, DateTime CreatedAt, - DateTime UpdatedAt); + DateTime UpdatedAt) + { + public static PlayListResponse Map(PlayList playList) + { + return new PlayListResponse( + playList.Id, + playList.Name, + playList.Description, + playList.SmallPictureKeyName, + playList.MediumPictureKeyName, + playList.LargePictureKeyName, + playList.CreatedDate, + playList.UpdatedDate); + } + } } \ No newline at end of file diff --git a/Infrastructure/Messaging/Consumers/ImageResizedConsumer.cs b/Infrastructure/Messaging/Consumers/ImageResizedConsumer.cs index 303f268..a03800b 100644 --- a/Infrastructure/Messaging/Consumers/ImageResizedConsumer.cs +++ b/Infrastructure/Messaging/Consumers/ImageResizedConsumer.cs @@ -9,9 +9,9 @@ using Musify.Domain.Entities; namespace Musify.Infrastructure.Messaging.Consumers { public class ImageResizedConsumer(IStorageHandler storageHandler, IDatabase database, IPictureHandler pictureHandler, ILogger logger) - : IConsumer + : IConsumer { - public async Task Consume(ConsumeContext consumeContext) + public async Task Consume(ConsumeContext consumeContext) { try { diff --git a/Musify.Backend/Entities/PlayList.cs b/Musify.Backend/Entities/PlayList.cs index 4247756..44d751f 100644 --- a/Musify.Backend/Entities/PlayList.cs +++ b/Musify.Backend/Entities/PlayList.cs @@ -27,7 +27,15 @@ namespace Musify.Domain.Entities [Required] [MaxLength(64)] - public required string ImageName { get; set; } + public required string SmallPictureKeyName { get; set; } + + [Required] + [MaxLength(64)] + public required string MediumPictureKeyName { get; set; } + + [Required] + [MaxLength(64)] + public required string LargePictureKeyName { get; set; } [Required] public DateTime CreatedDate { get; set; } = DateTime.UtcNow;