Experimental changes

This commit is contained in:
2026-03-29 17:15:39 +02:00
parent c6f919ee89
commit 697eed4e58
35 changed files with 279 additions and 445 deletions
@@ -1,11 +0,0 @@
using Ardalis.Result;
namespace Musify.Application.Contracts.Infrastructure
{
public interface IAudioTranscoder
{
Task<Result> TranscodeToDashAsync(Stream audioStream, string destinationPath, CancellationToken cancellationToken);
Task<Result<bool>> IsValidAudioFileAsync(string filePath, CancellationToken cancellationToken);
}
}
@@ -0,0 +1,9 @@
namespace Musify.Application.Contracts.Infrastructure
{
public interface IAudioTranscoderService
{
Task<int> TranscodeToDashAsync(Stream audioStream, string destinationPath, CancellationToken cancellationToken);
Task<bool> IsValidAudioFileAsync(string filePath, CancellationToken cancellationToken);
}
}
@@ -1,11 +1,10 @@
using Ardalis.Result;
using Musify.Application.Users.Responses;
using Musify.Application.Users.Responses;
namespace Musify.Application.Contracts.Infrastructure
{
public interface IKeycloakUserService
{
Task<Result<KeycloakUserResponse>> GetUserByIdAsync(string keycloakId, CancellationToken cancellationToken);
Task<KeycloakUserResponse> GetUserByIdAsync(string keycloakId, CancellationToken cancellationToken);
Task<IReadOnlyCollection<KeycloakUserResponse>> GetUsersAsync(
CancellationToken cancellationToken,
@@ -1,9 +0,0 @@
using Ardalis.Result;
namespace Musify.Application.Contracts.Infrastructure
{
public interface IPictureHandler
{
Task<Result<Stream>> ResizePictureAsWebpAsync(Stream pictureStream, int width, int height, CancellationToken cancellationToken);
}
}
@@ -0,0 +1,7 @@
namespace Musify.Application.Contracts.Infrastructure
{
public interface IPictureService
{
Task<Stream> ResizePictureAsWebpAsync(Stream pictureStream, int width, int height, CancellationToken cancellationToken);
}
}
@@ -1,22 +0,0 @@
using Ardalis.Result;
namespace Musify.Application.Contracts.Infrastructure
{
public interface IStorageHandler
{
Task<Result<Stream>> GetFileAsync(string bucket, string key, CancellationToken cancellationToken);
Task<string> GetUrlAsync(string bucket, string key, TimeSpan ExpirationTime, CancellationToken cancellationToken);
Task<Result<string>> UploadFileAsync(string filePath, string bucket, string key, CancellationToken cancellationToken);
Task<Result<string>> UploadFileAsync(Stream sourceStream, string contentType, string bucket, string key, CancellationToken cancellationToken);
Task<Result> TransferFilesAsync(string sourceDirectory, string bucket, string route, CancellationToken cancellationToken);
Task<Result> RemoveFileAsync(string bucket, string key, CancellationToken cancellationToken);
Task<Result> CopyFileAsync(string sourceBucket, string sourceKey, string destinationBucket, string destinationKey,
CancellationToken cancellationToken);
}
}
@@ -0,0 +1,20 @@
namespace Musify.Application.Contracts.Infrastructure
{
public interface IStorageService
{
Task<Stream> GetFileAsync(string bucket, string key, CancellationToken cancellationToken);
Task<string> GetUrlAsync(string bucket, string key, TimeSpan ExpirationTime, CancellationToken cancellationToken);
Task UploadFileAsync(string filePath, string bucket, string key, CancellationToken cancellationToken);
Task UploadFileAsync(Stream sourceStream, string contentType, string bucket, string key, CancellationToken cancellationToken);
Task TransferFilesAsync(string sourceDirectory, string bucket, string route, CancellationToken cancellationToken);
Task RemoveFileAsync(string bucket, string key, CancellationToken cancellationToken);
Task CopyFileAsync(string sourceBucket, string sourceKey, string destinationBucket, string destinationKey,
CancellationToken cancellationToken);
}
}
@@ -13,7 +13,7 @@ using Musify.Domain.Entities;
namespace Musify.Application.PlayLists.Handlers
{
public class CreatePlayListCommandHandler(IEventBus eventBus, IDatabase database, IStorageHandler storageHandler,
public class CreatePlayListCommandHandler(IEventBus eventBus, IDatabase database, IStorageService storageHandler,
ILogger<CreatePlayListCommandHandler> logger, ApplicationStorageConfiguration storageConfiguration, PlayListConfiguration playListConfiguration)
: IRequestHandler<CreatePlayListCommand, Task<Result<PlayListResponse>> >
{
@@ -80,16 +80,16 @@ namespace Musify.Application.PlayLists.Handlers
var pictureName = Guid.NewGuid() + picture.FileType;
var pictureKey = Path.Combine(playListConfiguration.Routes.OriginalPicturesPath, pictureName);
var uploadResult = await storageHandler.UploadFileAsync(picture.FileStream, picture.ContentType, storageConfiguration.BucketName, pictureKey, cancellationToken);
if (!uploadResult.IsSuccess)
try
{
logger.LogError("Failed to upload picture for playlist {PlayListId} to storage: {ErrorMessage}",
playList.Id, string.Join(", ", uploadResult.Errors));
return uploadResult.ToErrorResult();
await storageHandler.UploadFileAsync(picture.FileStream, picture.ContentType, storageConfiguration.BucketName, pictureKey, cancellationToken);
}
catch
{
return Result.Error($"An error occurred while uploading picture for playlist {playList.Id} to storage.");
}
playList.OriginalPictureName = pictureName;
return Result.Success();
}
@@ -12,7 +12,7 @@ using Musify.Domain.Entities;
namespace Musify.Application.PlayLists.Handlers
{
public class UpdatePlayListCommandHandler(IEventBus eventBus, IDatabase database, IStorageHandler storageHandler,
public class UpdatePlayListCommandHandler(IEventBus eventBus, IDatabase database, IStorageService storageHandler,
ILogger<UpdatePlayListCommandHandler> logger, ApplicationStorageConfiguration storageConfiguration,
PlayListConfiguration playListConfiguration)
: IRequestHandler<UpdatePlayListCommand, Task<Result<PlayListResponse>> >
@@ -80,19 +80,20 @@ namespace Musify.Application.PlayLists.Handlers
var pictureName = Guid.NewGuid() + newPicture.FileType;
var pictureKey = Path.Combine(playListConfiguration.Routes.OriginalPicturesPath, pictureName);
var uploadResult = await storageHandler.UploadFileAsync(
newPicture.FileStream,
newPicture.ContentType,
storageConfiguration.BucketName,
pictureKey,
cancellationToken);
if (!uploadResult.IsSuccess)
try
{
logger.LogError("Failed to upload picture for playlist {PlayListId} to storage: {ErrorMessage}",
playList.Id, string.Join("; ", uploadResult.Errors));
return Result.Error($"Failed to upload playlist {playList.Id} picture");
await storageHandler.UploadFileAsync(
newPicture.FileStream,
newPicture.ContentType,
storageConfiguration.BucketName,
pictureKey,
cancellationToken);
}
catch
{
return Result.Error("Failed to upload picture to storage");
}
playList.OriginalPictureName = pictureName;
return Result.Success(pictureKey);
}
@@ -10,7 +10,7 @@ using Musify.Domain.Entities;
namespace Musify.Application.Tracks.Handler
{
public class CreateTrackCommandHandler(IDatabase database, IEventBus eventBus, IStorageHandler storageHandler,
public class CreateTrackCommandHandler(IDatabase database, IEventBus eventBus, IStorageService storageHandler,
ILogger<CreateTrackCommandHandler> logger, ApplicationStorageConfiguration storageConfiguration, TrackConfiguration trackConfiguration)
: IRequestHandler<CreateTrackCommand, Task<Result<TrackResponse> >>
{
@@ -49,33 +49,34 @@ namespace Musify.Application.Tracks.Handler
string audioKey,
CancellationToken cancellationToken)
{
var pictureUpload = await storageHandler.UploadFileAsync(
request.Picture.FileStream,
request.Picture.ContentType,
storageConfiguration.BucketName,
pictureKey,
cancellationToken);
if (!pictureUpload.IsSuccess)
try
{
await storageHandler.UploadFileAsync(
request.Picture.FileStream,
request.Picture.ContentType,
storageConfiguration.BucketName,
pictureKey,
cancellationToken);
}
catch
{
var errors = string.Join(", ", pictureUpload.Errors);
return Result.Error($"Failed to upload picture for {request.Title}");
}
var audioUpload = await storageHandler.UploadFileAsync(
request.Audio.FileStream,
request.Audio.ContentType,
storageConfiguration.BucketName,
audioKey,
cancellationToken);
if (!audioUpload.IsSuccess)
try
{
await storageHandler.UploadFileAsync(
request.Audio.FileStream,
request.Audio.ContentType,
storageConfiguration.BucketName,
audioKey,
cancellationToken);
}
catch
{
await RollbackFilesAsync(pictureKey, audioKey, cancellationToken);
var errors = string.Join(", ", audioUpload.Errors);
return Result.Error($"Failed to upload audio for {request.Title}");
}
return Result.Success();
}
@@ -8,7 +8,7 @@ using Musify.Domain.Entities;
namespace Musify.Application.Tracks.Handler
{
public class DeleteTrackCommandHandler(IDatabase database, IStorageHandler storageHandler, ILogger<DeleteTrackCommandHandler> logger,
public class DeleteTrackCommandHandler(IDatabase database, IStorageService storageHandler, ILogger<DeleteTrackCommandHandler> logger,
TrackConfiguration trackConfiguration, ApplicationStorageConfiguration storageConfiguration)
: IRequestHandler<DeleteTrackCommand, Task<Result>>
{
@@ -20,10 +20,13 @@ namespace Musify.Application.Users.Handlers
return Result<UserResponse>.Conflict($"User {request.Id} already exists");
}
var keycloakUser = await keycloakUserClient.GetUserByIdAsync(request.Id.ToString(), cancellationToken);
if (!keycloakUser.IsSuccess)
KeycloakUserResponse keycloakUser;
try
{
keycloakUser = await keycloakUserClient.GetUserByIdAsync(request.Id.ToString(), cancellationToken);
}
catch
{
logger.LogWarning("Keycloak user {UserId} not found", request.Id);
return Result.NotFound("Keycloak user not found");
}
@@ -34,9 +37,9 @@ namespace Musify.Application.Users.Handlers
logger.LogInformation("Created user {UserId}", newUser.Id);
return Result.Created(new UserResponse(
newUser.Id.ToString(),
keycloakUser.Value.Name,
keycloakUser.Value.FirstName,
keycloakUser.Value.SecondName,
keycloakUser.Name,
keycloakUser.FirstName,
keycloakUser.SecondName,
newUser.CreatedAt,
newUser.UpdatedAt));
}
@@ -18,15 +18,21 @@ namespace Musify.Application.Users.Handlers
if (user is null)
return Result.NotFound("User not found");
var keycloakUser = await keycloakUserClient.GetUserByIdAsync(user.Id.ToString(), cancellationToken);
if (!keycloakUser.IsSuccess)
return Result.NotFound("Keycloak user not found");
KeycloakUserResponse keycloakUser;
try
{
keycloakUser = await keycloakUserClient.GetUserByIdAsync(user.Id.ToString(), cancellationToken);
}
catch
{
return Result.Error("Keycloak user not found");
}
return Result.Success(new UserResponse(
user.Id.ToString(),
keycloakUser.Value.Name,
keycloakUser.Value.FirstName,
keycloakUser.Value.SecondName,
keycloakUser.Name,
keycloakUser.FirstName,
keycloakUser.SecondName,
user.CreatedAt,
user.UpdatedAt));
}
@@ -18,15 +18,21 @@ namespace Musify.Application.Users.Handlers
if (user is null)
return Result.NotFound("User not found");
var keycloakUser = await keycloakUserClient.GetUserByIdAsync(user.Id.ToString(), cancellationToken);
if (!keycloakUser.IsSuccess)
KeycloakUserResponse keycloakUser;
try
{
keycloakUser = await keycloakUserClient.GetUserByIdAsync(user.Id.ToString(), cancellationToken);
}
catch
{
return Result.NotFound("Keycloak user not found");
}
return Result.Success(new UserResponse(
user.Id.ToString(),
keycloakUser.Value.Name,
keycloakUser.Value.FirstName,
keycloakUser.Value.SecondName,
keycloakUser.Name,
keycloakUser.FirstName,
keycloakUser.SecondName,
user.CreatedAt,
user.UpdatedAt));
}