From 678419814ad4f1816eb603b46d9c5536669c86c4 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Sun, 22 Mar 2026 04:02:30 +0100 Subject: [PATCH] Staged --- .../Configuration/TrackConfiguration.cs | 3 + .../Infrastructure/IAudioTranscoder.cs | 2 +- Application/Events/ProcessTrackEvent.cs | 4 - .../Events/TranscodeAudioFromTrackEvent.cs | 8 + .../Handler/CreateTrackCommandHandler.cs | 35 +- Infrastructure/Audio/AudioTranscoder.cs | 15 +- .../DownloadFileFromBucketArguments.cs | 7 + ...ts.cs => RemoveFileFromBucketArguments.cs} | 2 +- .../Arguments/TranscodeDashAudioArguments.cs | 4 + .../TransferFilesToBucketArguments.cs | 7 + .../Messaging/Activities/CopyFilesActivity.cs | 39 -- .../DownloadFileFromBucketActivity.cs | 41 ++ ...ity.cs => RemoveFileFromBucketActivity.cs} | 10 +- .../Activities/TranscodeDashAudioActivity.cs | 49 +++ .../Activities/TransferFilesToBucket.cs | 31 ++ .../Messaging/Consumers/RemoveFileConsumer.cs | 4 +- .../TranscodeAudioFromTrackConsumer.cs | 54 +++ .../UpdatePlayListPictureConsumer.cs | 4 +- .../Consumers/UpdateTrackPictureConsumer.cs | 4 +- .../Messaging/MessagingDependencyInjection.cs | 57 ++- .../20260322023145_FirstMigration.Designer.cs | 382 ++++++++++++++++++ .../20260322023145_FirstMigration.cs | 247 +++++++++++ .../Migrations/DatabaseModelSnapshot.cs | 379 +++++++++++++++++ 23 files changed, 1318 insertions(+), 70 deletions(-) delete mode 100644 Application/Events/ProcessTrackEvent.cs create mode 100644 Application/Events/TranscodeAudioFromTrackEvent.cs create mode 100644 Infrastructure/Messaging/Activities/Arguments/DownloadFileFromBucketArguments.cs rename Infrastructure/Messaging/Activities/Arguments/{RemoveFileArguments.cs => RemoveFileFromBucketArguments.cs} (71%) create mode 100644 Infrastructure/Messaging/Activities/Arguments/TranscodeDashAudioArguments.cs create mode 100644 Infrastructure/Messaging/Activities/Arguments/TransferFilesToBucketArguments.cs delete mode 100644 Infrastructure/Messaging/Activities/CopyFilesActivity.cs create mode 100644 Infrastructure/Messaging/Activities/DownloadFileFromBucketActivity.cs rename Infrastructure/Messaging/Activities/{RemoveFilesActivity.cs => RemoveFileFromBucketActivity.cs} (89%) create mode 100644 Infrastructure/Messaging/Activities/TranscodeDashAudioActivity.cs create mode 100644 Infrastructure/Messaging/Activities/TransferFilesToBucket.cs create mode 100644 Infrastructure/Messaging/Consumers/TranscodeAudioFromTrackConsumer.cs create mode 100644 Infrastructure/Persistence/Migrations/20260322023145_FirstMigration.Designer.cs create mode 100644 Infrastructure/Persistence/Migrations/20260322023145_FirstMigration.cs create mode 100644 Infrastructure/Persistence/Migrations/DatabaseModelSnapshot.cs diff --git a/Application/Configuration/TrackConfiguration.cs b/Application/Configuration/TrackConfiguration.cs index 298a66a..d352048 100644 --- a/Application/Configuration/TrackConfiguration.cs +++ b/Application/Configuration/TrackConfiguration.cs @@ -38,6 +38,9 @@ namespace Musify.Application.Configuration [Required] public string PresetLargePicture { get; set; } = "PresetLargePicture.webp"; + + [Required] + public string Audios { get; set; } = "Audios"; } public class TrackPicturesSizes diff --git a/Application/Contracts/Infrastructure/IAudioTranscoder.cs b/Application/Contracts/Infrastructure/IAudioTranscoder.cs index 1ec7092..81f8941 100644 --- a/Application/Contracts/Infrastructure/IAudioTranscoder.cs +++ b/Application/Contracts/Infrastructure/IAudioTranscoder.cs @@ -4,6 +4,6 @@ namespace Musify.Application.Contracts.Infrastructure { public interface IAudioTranscoder { - Task TranscodeToDash(Stream audioStream, string folderName, CancellationToken cancellationToken); + Task TranscodeToDashAsync(Stream audioStream, string destinationPath, TimeSpan timeOut, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/Application/Events/ProcessTrackEvent.cs b/Application/Events/ProcessTrackEvent.cs deleted file mode 100644 index 30899f2..0000000 --- a/Application/Events/ProcessTrackEvent.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Musify.Application.Events -{ - public record ProcessTrackEvent(); -} \ No newline at end of file diff --git a/Application/Events/TranscodeAudioFromTrackEvent.cs b/Application/Events/TranscodeAudioFromTrackEvent.cs new file mode 100644 index 0000000..69cd150 --- /dev/null +++ b/Application/Events/TranscodeAudioFromTrackEvent.cs @@ -0,0 +1,8 @@ +namespace Musify.Application.Events +{ + public record TranscodeAudioFromTrackEvent( + string SourceBucketName, + string SourceKeyName, + string DestinationBucketName, + string DestinationKeyName); +} \ No newline at end of file diff --git a/Application/Tracks/Handler/CreateTrackCommandHandler.cs b/Application/Tracks/Handler/CreateTrackCommandHandler.cs index dd0035e..eeed857 100644 --- a/Application/Tracks/Handler/CreateTrackCommandHandler.cs +++ b/Application/Tracks/Handler/CreateTrackCommandHandler.cs @@ -52,19 +52,37 @@ namespace Musify.Application.Tracks.Handler return Result.Error($"Failed to publish resize picture event for Track with id {track.Id}."); } + var audioKeyName = Path.Combine(trackConfiguration.Routes.Audios, $"{Guid.NewGuid() }.{request.Audio.FileType}"); + var audioUploadResult = await UploadFile(request.Audio, audioKeyName, cancellationToken); + if (!audioUploadResult.IsSuccess) + { + logger.LogError("Failed to upload audio for track {TrackTitle}", request.Title); + return Result.Error($"Failed to upload audio for track {request.Title}"); + } + + try + { + await PublishTranscodeEvent(track.Id, audioUploadResult, cancellationToken); + } + catch (Exception exception) + { + logger.LogError(exception, "Failed to publish transcode audio event for Track with id={TrackId}.", track.Id); + return Result.Error($"An error occurred while processing the audio for Track with id {track.Id}."); + } + await database.Tracks.AddAsync(track, cancellationToken); await database.SaveChangesAsync(cancellationToken); return Result.Success(); } - async Task> UploadFile(IFileData fileData, string originalPictureKeyName, CancellationToken cancellationToken) + async Task> UploadFile(IFileData fileData, string keyName, CancellationToken cancellationToken) { var uploadFile = await storageHandler.UploadFileAsync( fileData.FileStream, fileData.ContentType, storageConfiguration.BucketName, - originalPictureKeyName, + keyName, cancellationToken); return uploadFile; @@ -96,5 +114,18 @@ namespace Musify.Application.Tracks.Handler trackConfiguration.PicturesSizes.LargePictureWidth, trackConfiguration.PicturesSizes.LargePictureHeight), cancellationToken); } + + async Task PublishTranscodeEvent(Guid trackId, string audioKeyName, CancellationToken cancellationToken) + { + var destinationAudioKeyName = Path.Combine( + trackConfiguration.Routes.Audios, + trackId.ToString()); + + await eventBus.PublishAsync(new TranscodeAudioFromTrackEvent( + storageConfiguration.BucketName, + audioKeyName, + storageConfiguration.BucketName, + destinationAudioKeyName), cancellationToken); + } } } diff --git a/Infrastructure/Audio/AudioTranscoder.cs b/Infrastructure/Audio/AudioTranscoder.cs index 979166f..9ae77ac 100644 --- a/Infrastructure/Audio/AudioTranscoder.cs +++ b/Infrastructure/Audio/AudioTranscoder.cs @@ -1,31 +1,28 @@ using Ardalis.Result; using Microsoft.Extensions.Logging; using Musify.Application.Contracts.Infrastructure; -using Musify.Infrastructure.Configuration; using System.Diagnostics; namespace Musify.Infrastructure.Audio { - public class AudioTranscoder(ILogger logger, AudioTranscoderConfiguration transcoderConfiguration) + public class AudioTranscoder(ILogger logger) : IAudioTranscoder { const string FfmpegProcessName = "ffmpeg"; - public async Task TranscodeToDash(Stream audioStream, string folderName, CancellationToken cancellationToken) + public async Task TranscodeToDashAsync(Stream audioStream, string destinationPath, TimeSpan timeOut, CancellationToken cancellationToken) { - var workingDirectory = Path.Combine(transcoderConfiguration.Routes.WorkingDirectory, folderName); - try { - Directory.CreateDirectory(workingDirectory); + Directory.CreateDirectory(destinationPath); using var process = new Process { StartInfo = new ProcessStartInfo { FileName = FfmpegProcessName, - WorkingDirectory = workingDirectory, - Arguments = BuildDashArguments(workingDirectory), + WorkingDirectory = destinationPath, + Arguments = BuildDashArguments(destinationPath), RedirectStandardInput = true, RedirectStandardOutput = true, RedirectStandardError = true, @@ -44,7 +41,7 @@ namespace Musify.Infrastructure.Audio var outputTask = process.StandardOutput.ReadToEndAsync(cancellationToken); using var linkedCancellationTokens = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); - linkedCancellationTokens.CancelAfter(transcoderConfiguration.TranscodingTimeout); + linkedCancellationTokens.CancelAfter(timeOut); await process.WaitForExitAsync(linkedCancellationTokens.Token); var standardError = await errorTask; diff --git a/Infrastructure/Messaging/Activities/Arguments/DownloadFileFromBucketArguments.cs b/Infrastructure/Messaging/Activities/Arguments/DownloadFileFromBucketArguments.cs new file mode 100644 index 0000000..3b4ed66 --- /dev/null +++ b/Infrastructure/Messaging/Activities/Arguments/DownloadFileFromBucketArguments.cs @@ -0,0 +1,7 @@ +namespace Musify.Infrastructure.Messaging.Activities.Arguments +{ + public record DownloadFileFromBucketArguments( + string BucketName, + string KeyName, + string DestinationPath); +} \ No newline at end of file diff --git a/Infrastructure/Messaging/Activities/Arguments/RemoveFileArguments.cs b/Infrastructure/Messaging/Activities/Arguments/RemoveFileFromBucketArguments.cs similarity index 71% rename from Infrastructure/Messaging/Activities/Arguments/RemoveFileArguments.cs rename to Infrastructure/Messaging/Activities/Arguments/RemoveFileFromBucketArguments.cs index a0e8851..23e7058 100644 --- a/Infrastructure/Messaging/Activities/Arguments/RemoveFileArguments.cs +++ b/Infrastructure/Messaging/Activities/Arguments/RemoveFileFromBucketArguments.cs @@ -1,6 +1,6 @@ namespace Musify.Infrastructure.Messaging.Activities.Arguments { - public record RemoveFileArguments( + public record RemoveFileFromBucketArguments( string BucketName, string KeyName); } \ No newline at end of file diff --git a/Infrastructure/Messaging/Activities/Arguments/TranscodeDashAudioArguments.cs b/Infrastructure/Messaging/Activities/Arguments/TranscodeDashAudioArguments.cs new file mode 100644 index 0000000..ddbd8c4 --- /dev/null +++ b/Infrastructure/Messaging/Activities/Arguments/TranscodeDashAudioArguments.cs @@ -0,0 +1,4 @@ +namespace Musify.Infrastructure.Messaging.Activities.Arguments +{ + public record TranscodeDashAudioArguments(string SourceFilePath, string DestinationFolderName); +} \ No newline at end of file diff --git a/Infrastructure/Messaging/Activities/Arguments/TransferFilesToBucketArguments.cs b/Infrastructure/Messaging/Activities/Arguments/TransferFilesToBucketArguments.cs new file mode 100644 index 0000000..8f39ecd --- /dev/null +++ b/Infrastructure/Messaging/Activities/Arguments/TransferFilesToBucketArguments.cs @@ -0,0 +1,7 @@ +namespace Musify.Infrastructure.Messaging.Activities.Arguments +{ + public record TransferFilesToBucketArguments( + string FolderPath, + string DestinationBucketName, + string DestinationKeyName); +} \ No newline at end of file diff --git a/Infrastructure/Messaging/Activities/CopyFilesActivity.cs b/Infrastructure/Messaging/Activities/CopyFilesActivity.cs deleted file mode 100644 index 64499c2..0000000 --- a/Infrastructure/Messaging/Activities/CopyFilesActivity.cs +++ /dev/null @@ -1,39 +0,0 @@ -using MassTransit; -using Microsoft.Extensions.Logging; -using Musify.Application.Contracts.Infrastructure; -using Musify.Infrastructure.Messaging.Activities.Arguments; - -namespace Musify.Infrastructure.Messaging.Activities -{ - public class CopyFilesActivity(IStorageHandler storageHandler, ILogger logger) - : IExecuteActivity - { - public async Task Execute(ExecuteContext executeContext) - { - foreach (var file in executeContext.Arguments) - { - var copyFileResult = await storageHandler.CopyFileAsync( - file.SourceBucketName, - file.SourceKeyName, - file.DestinationBucketName, - file.SourceKeyName, - executeContext.CancellationToken); - - if (!copyFileResult.IsSuccess) - { - string errorMessage = string.Join("; ", copyFileResult.Errors); - logger.LogError( - "Failed to copy file from {SourceBucket}/{SourceKey} to {DestinationBucket}/{DestinationKey}. Error: {ErrorMessage}", - file.SourceBucketName, - file.SourceKeyName, - file.DestinationBucketName, - file.DestinationKeyName, - errorMessage); - throw new Exception(errorMessage); - } - } - - return executeContext.Completed(); - } - } -} \ No newline at end of file diff --git a/Infrastructure/Messaging/Activities/DownloadFileFromBucketActivity.cs b/Infrastructure/Messaging/Activities/DownloadFileFromBucketActivity.cs new file mode 100644 index 0000000..37bf996 --- /dev/null +++ b/Infrastructure/Messaging/Activities/DownloadFileFromBucketActivity.cs @@ -0,0 +1,41 @@ +using MassTransit; +using Microsoft.Extensions.Logging; +using Musify.Application.Contracts.Infrastructure; +using Musify.Infrastructure.Messaging.Activities.Arguments; +using Musify.Infrastructure.Messaging.Activities.Logs; + +namespace Musify.Infrastructure.Messaging.Activities +{ + public class DownloadFileFromBucketActivity(IStorageHandler storageHandler, ILogger logger) + : IExecuteActivity + { + public const string ExecuteEndpointName = "Download-File-From-Bucket"; + + public async Task Execute(ExecuteContext executeContext) + { + var getFile = await storageHandler.GetFileAsync( + executeContext.Arguments.BucketName, + executeContext.Arguments.KeyName, + executeContext.CancellationToken); + + if (!getFile.IsSuccess) + { + var error = string.Join("; ", getFile.Errors); + logger.LogError("Failed to download file from bucket. Bucket: {BucketName}, Key: {KeyName}", + executeContext.Arguments.BucketName, + executeContext.Arguments.KeyName); + throw new Exception(error); + } + + using var fileStream = getFile.Value; + if (fileStream.CanSeek) + fileStream.Position = 0; + + var fileName = Path.GetFileName(executeContext.Arguments.KeyName); + using var destinationStream = File.Create($"D:\\tempsFilesDev\\{fileName}"); + await fileStream.CopyToAsync(destinationStream, executeContext.CancellationToken); + + return executeContext.Completed(); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Messaging/Activities/RemoveFilesActivity.cs b/Infrastructure/Messaging/Activities/RemoveFileFromBucketActivity.cs similarity index 89% rename from Infrastructure/Messaging/Activities/RemoveFilesActivity.cs rename to Infrastructure/Messaging/Activities/RemoveFileFromBucketActivity.cs index fd0b6b8..185260e 100644 --- a/Infrastructure/Messaging/Activities/RemoveFilesActivity.cs +++ b/Infrastructure/Messaging/Activities/RemoveFileFromBucketActivity.cs @@ -6,15 +6,15 @@ using Musify.Infrastructure.Messaging.Activities.Arguments; namespace Musify.Infrastructure.Messaging.Activities { - public class RemoveFilesActivity( + public class RemoveFileFromBucketActivity( IStorageHandler storageHandler, - ILogger logger, + ILogger logger, IProcessTrackingStore processTrackingStore) - : IExecuteActivity + : IExecuteActivity { public const string ExecuteEndpointName = "Remove-File"; - public async Task Execute(ExecuteContext executeContext) + public async Task Execute(ExecuteContext executeContext) { var processId = await processTrackingStore.GetOrCreateProcessAsync( "RoutingSlip", @@ -25,7 +25,7 @@ namespace Musify.Infrastructure.Messaging.Activities var stepId = await processTrackingStore.StartStepAsync( processId, - nameof(RemoveFilesActivity), + nameof(RemoveFileFromBucketActivity), ProcessStepComponentType.Activity, 0, executeContext.CancellationToken); diff --git a/Infrastructure/Messaging/Activities/TranscodeDashAudioActivity.cs b/Infrastructure/Messaging/Activities/TranscodeDashAudioActivity.cs new file mode 100644 index 0000000..659afbb --- /dev/null +++ b/Infrastructure/Messaging/Activities/TranscodeDashAudioActivity.cs @@ -0,0 +1,49 @@ +using MassTransit; +using Microsoft.Extensions.Logging; +using Musify.Application.Contracts.Infrastructure; +using Musify.Infrastructure.Configuration; +using Musify.Infrastructure.Messaging.Activities.Arguments; +using Musify.Infrastructure.Messaging.Activities.Logs; + +namespace Musify.Infrastructure.Messaging.Activities +{ + public class TranscodeDashAudioActivity(IAudioTranscoder audioTranscoder, ILogger logger, + AudioTranscoderConfiguration audioTranscoderConfiguration) + : IExecuteActivity + { + public const string ExecuteEndpointName = "Transcode-Dash-Audio"; + + public async Task Execute(ExecuteContext executeContext) + { + var workingDirectory = Path.Combine( + audioTranscoderConfiguration.Routes.WorkingDirectory, + executeContext.Arguments.DestinationFolderName); + + try + { + await using var fileStream = File.OpenRead(executeContext.Arguments.SourceFilePath); + + var result = await audioTranscoder.TranscodeToDashAsync( + fileStream, + executeContext.Arguments.DestinationFolderName, + audioTranscoderConfiguration.TranscodingTimeout, + executeContext.CancellationToken); + + if (!result.IsSuccess) + { + var errors = string.Join("; ", result.Errors); + logger.LogError("transcoding failed for file {SourceFilePath}. Error: {ErrorMessage}", + executeContext.Arguments.SourceFilePath, errors); + throw new Exception(errors); + } + + return executeContext.Completed(); + } + catch (Exception exception) + { + logger.LogError(exception, "Error during transcoding activity."); + throw; + } + } + } +} \ No newline at end of file diff --git a/Infrastructure/Messaging/Activities/TransferFilesToBucket.cs b/Infrastructure/Messaging/Activities/TransferFilesToBucket.cs new file mode 100644 index 0000000..76c97a8 --- /dev/null +++ b/Infrastructure/Messaging/Activities/TransferFilesToBucket.cs @@ -0,0 +1,31 @@ +using MassTransit; +using Microsoft.Extensions.Logging; +using Musify.Application.Contracts.Infrastructure; +using Musify.Infrastructure.Messaging.Activities.Arguments; + +namespace Musify.Infrastructure.Messaging.Activities +{ + public class TransferFilesToBucket(IStorageHandler storageHandler, ILogger logger) + : IExecuteActivity + { + public const string ExecuteEndpointName = "Transfer-Files-To-Bucket"; + + public async Task Execute(ExecuteContext executeContext) + { + var transferFilesResult = await storageHandler.TransferFilesAsync( + executeContext.Arguments.FolderPath, + executeContext.Arguments.DestinationBucketName, + executeContext.Arguments.DestinationKeyName, + executeContext.CancellationToken); + + if (!transferFilesResult.IsSuccess) + { + var errors = string.Join("; ", transferFilesResult.Errors); + logger.LogError($"An error occurred while transferring files to bucket: {errors}"); + throw new Exception(errors); + } + + return executeContext.Completed(); + } + } +} \ No newline at end of file diff --git a/Infrastructure/Messaging/Consumers/RemoveFileConsumer.cs b/Infrastructure/Messaging/Consumers/RemoveFileConsumer.cs index a057f45..79a2650 100644 --- a/Infrastructure/Messaging/Consumers/RemoveFileConsumer.cs +++ b/Infrastructure/Messaging/Consumers/RemoveFileConsumer.cs @@ -16,8 +16,8 @@ namespace Musify.Infrastructure.Messaging.Consumers routingSlipBuilder.AddActivity( "RemoveFile", - BuildExecuteUri(RemoveFilesActivity.ExecuteEndpointName), - new RemoveFileArguments( + BuildExecuteUri(RemoveFileFromBucketActivity.ExecuteEndpointName), + new RemoveFileFromBucketArguments( consumeContext.Message.BucketName, consumeContext.Message.KeyName)); diff --git a/Infrastructure/Messaging/Consumers/TranscodeAudioFromTrackConsumer.cs b/Infrastructure/Messaging/Consumers/TranscodeAudioFromTrackConsumer.cs new file mode 100644 index 0000000..a50204b --- /dev/null +++ b/Infrastructure/Messaging/Consumers/TranscodeAudioFromTrackConsumer.cs @@ -0,0 +1,54 @@ +using MassTransit; +using Musify.Application.Events; +using Musify.Infrastructure.Configuration; +using Musify.Infrastructure.Messaging.Activities; +using Musify.Infrastructure.Messaging.Activities.Arguments; + +namespace Musify.Infrastructure.Messaging.Consumers +{ + public class TranscodeAudioFromTrackConsumer(IBus bus, AudioTranscoderConfiguration audioTranscoderConfiguration) + : IConsumer + { + public const string QueueName = "Transcode-Audio-From-Track"; + + public async Task Consume(ConsumeContext consumeContext) + { + var routingSlipBuilder = new RoutingSlipBuilder(NewId.NextGuid()); + + var folderName = Guid.NewGuid().ToString(); + var workingDirectory = Path.Combine("D:\\tempsFilesDev\\"); + + routingSlipBuilder.AddActivity( + "DownloadFile", + BuildExecuteUri(DownloadFileFromBucketActivity.ExecuteEndpointName), + new DownloadFileFromBucketArguments( + consumeContext.Message.SourceBucketName, + consumeContext.Message.SourceKeyName, + workingDirectory) + ); + var fileName = Path.GetFileName(consumeContext.Message.SourceKeyName); + + routingSlipBuilder.AddActivity( + "TranscodeAudio", + BuildExecuteUri(TranscodeDashAudioActivity.ExecuteEndpointName), + new TranscodeDashAudioArguments( + $"D:\\tempsFilesDev\\{fileName}", + "D:\\tempsFilesDev\\") + ); + + routingSlipBuilder.AddActivity( + "UploadFile", + BuildExecuteUri(TransferFilesToBucket.ExecuteEndpointName), + new TransferFilesToBucketArguments( + workingDirectory, + consumeContext.Message.DestinationBucketName, + consumeContext.Message.DestinationKeyName) + ); + + var routingSlip = routingSlipBuilder.Build(); + await bus.Execute(routingSlip); + } + + static Uri BuildExecuteUri(string endpointName) => new($"queue:{endpointName}_execute"); + } +} \ No newline at end of file diff --git a/Infrastructure/Messaging/Consumers/UpdatePlayListPictureConsumer.cs b/Infrastructure/Messaging/Consumers/UpdatePlayListPictureConsumer.cs index e542bc0..b9271a1 100644 --- a/Infrastructure/Messaging/Consumers/UpdatePlayListPictureConsumer.cs +++ b/Infrastructure/Messaging/Consumers/UpdatePlayListPictureConsumer.cs @@ -43,8 +43,8 @@ namespace Musify.Infrastructure.Messaging.Consumers routingSlipBuilder.AddActivity( "DeleteOriginal", - BuildExecuteUri(RemoveFilesActivity.ExecuteEndpointName), - new RemoveFileArguments( + BuildExecuteUri(RemoveFileFromBucketActivity.ExecuteEndpointName), + new RemoveFileFromBucketArguments( consumeContext.Message.BucketName, consumeContext.Message.OriginalPictureKeyName) ); diff --git a/Infrastructure/Messaging/Consumers/UpdateTrackPictureConsumer.cs b/Infrastructure/Messaging/Consumers/UpdateTrackPictureConsumer.cs index 00e0911..74a4fdd 100644 --- a/Infrastructure/Messaging/Consumers/UpdateTrackPictureConsumer.cs +++ b/Infrastructure/Messaging/Consumers/UpdateTrackPictureConsumer.cs @@ -43,8 +43,8 @@ namespace Musify.Infrastructure.Messaging.Consumers routingSlipBuilder.AddActivity( "DeleteOriginal", - BuildExecuteUri(RemoveFilesActivity.ExecuteEndpointName), - new RemoveFileArguments( + BuildExecuteUri(RemoveFileFromBucketActivity.ExecuteEndpointName), + new RemoveFileFromBucketArguments( consumeContext.Message.BucketName, consumeContext.Message.OriginalPictureKeyName) ); diff --git a/Infrastructure/Messaging/MessagingDependencyInjection.cs b/Infrastructure/Messaging/MessagingDependencyInjection.cs index 0613f72..d43bc7e 100644 --- a/Infrastructure/Messaging/MessagingDependencyInjection.cs +++ b/Infrastructure/Messaging/MessagingDependencyInjection.cs @@ -3,6 +3,7 @@ using Microsoft.Extensions.DependencyInjection; using Musify.Infrastructure.Configuration; using Musify.Infrastructure.Messaging.Activities; using Musify.Infrastructure.Messaging.Activities.Arguments; +using Musify.Infrastructure.Messaging.Activities.Logs; using Musify.Infrastructure.Messaging.Consumers; using Musify.Infrastructure.Messaging.Filters; @@ -29,9 +30,15 @@ namespace Musify.Infrastructure.Messaging { options.AddConsumer(); options.AddConsumer(); + options.AddConsumer(); + options.AddConsumer(); + options.AddExecuteActivity(); - options.AddExecuteActivity(); + options.AddExecuteActivity(); options.AddExecuteActivity(); + options.AddExecuteActivity(); + options.AddExecuteActivity(); + options.AddExecuteActivity(); options.UsingRabbitMq((busRegistrationContext, busFactoryConfigurator) => { @@ -51,20 +58,45 @@ namespace Musify.Infrastructure.Messaging endpointConfigurator.ConfigureConsumer(busRegistrationContext); } ); + busFactoryConfigurator.ReceiveEndpoint(TranscodeAudioFromTrackConsumer.QueueName, endpointConfigurator => + { + endpointConfigurator.ConfigureConsumer(busRegistrationContext); + } ); + + busFactoryConfigurator.ReceiveEndpoint(UpdateTrackPictureConsumer.QueueName, endpointConfigurator => + { + endpointConfigurator.ConfigureConsumer(busRegistrationContext); + } ); + ConfigureExecuteActivityEndpoint( busFactoryConfigurator, busRegistrationContext, ResizePictureActivity.ExecuteEndpointName); - ConfigureExecuteActivityEndpoint( + ConfigureExecuteActivityEndpoint( busFactoryConfigurator, busRegistrationContext, - RemoveFilesActivity.ExecuteEndpointName); + RemoveFileFromBucketActivity.ExecuteEndpointName); ConfigureExecuteActivityEndpoint( busFactoryConfigurator, busRegistrationContext, UpdatePlayListPictureActivity.ExecuteEndpointName); + + ConfigureExecuteActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + UpdateTrackPictureActivity.ExecuteEndpointName); + + ConfigureExecuteActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + DownloadFileFromBucketActivity.ExecuteEndpointName); + + ConfigureExecuteActivityEndpoint( + busFactoryConfigurator, + busRegistrationContext, + TranscodeDashAudioActivity.ExecuteEndpointName); } ); } ); @@ -94,5 +126,24 @@ namespace Musify.Infrastructure.Messaging endpointConfigurator.ExecuteActivityHost(busRegistrationContext); } ); } + + static void ConfigureActivityEndpoints( + IRabbitMqBusFactoryConfigurator busFactoryConfigurator, + IBusRegistrationContext busRegistrationContext, + string endpointName) + where TActivity : class, IActivity + where TArguments : class + where TLog : class + { + busFactoryConfigurator.ReceiveEndpoint($"{endpointName}_execute", endpointConfigurator => + { + endpointConfigurator.ExecuteActivityHost(busRegistrationContext); + } ); + + busFactoryConfigurator.ReceiveEndpoint($"{endpointName}_compensate", endpointConfigurator => + { + endpointConfigurator.CompensateActivityHost(busRegistrationContext); + } ); + } } } \ No newline at end of file diff --git a/Infrastructure/Persistence/Migrations/20260322023145_FirstMigration.Designer.cs b/Infrastructure/Persistence/Migrations/20260322023145_FirstMigration.Designer.cs new file mode 100644 index 0000000..da7a421 --- /dev/null +++ b/Infrastructure/Persistence/Migrations/20260322023145_FirstMigration.Designer.cs @@ -0,0 +1,382 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Musify.Infrastructure.Persistence; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Musify.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(Database))] + [Migration("20260322023145_FirstMigration")] + partial class FirstMigration + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Musify.Domain.Entities.PlayList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(254) + .HasColumnType("character varying(254)"); + + b.Property("LargePictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("MediumPictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("SmallPictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("PlayLists"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.PlayListHasTrack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayListId") + .HasColumnType("uuid"); + + b.Property("Position") + .HasColumnType("integer"); + + b.Property("TrackId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayListId"); + + b.HasIndex("TrackId"); + + b.ToTable("PlayListHasTrack"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.ProcessExecution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CorrelationId") + .HasColumnType("uuid"); + + b.Property("ErrorMessage") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("FinishedDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("ProcessName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("StartedDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId", "ProcessName"); + + b.ToTable("ProcessExecution"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.ProcessStepExecution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Attempt") + .HasColumnType("integer"); + + b.Property("ComponentType") + .HasColumnType("integer"); + + b.Property("ErrorMessage") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("FinishedDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ProcessExecutionId") + .HasColumnType("uuid"); + + b.Property("StartedDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("StepName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ProcessExecutionId", "StepName"); + + b.ToTable("ProcessStepExecution"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.Track", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LargePictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("MediumPictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NormalizedTitle") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("SmallPictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Tracks"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.Upload", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BucketName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ContentType") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("KeyName") + .IsRequired() + .HasColumnType("text"); + + b.Property("State") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Upload"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.UserHasTrack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("TrackId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TrackId"); + + b.HasIndex("UserId"); + + b.ToTable("UserHasTrack"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.PlayList", b => + { + b.HasOne("Musify.Domain.Entities.User", "User") + .WithMany("PlayLists") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.PlayListHasTrack", b => + { + b.HasOne("Musify.Domain.Entities.PlayList", "PlayList") + .WithMany("PlayListTracks") + .HasForeignKey("PlayListId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Musify.Domain.Entities.Track", "Track") + .WithMany("PlayListTracks") + .HasForeignKey("TrackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayList"); + + b.Navigation("Track"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.ProcessStepExecution", b => + { + b.HasOne("Musify.Domain.Entities.ProcessExecution", "ProcessExecution") + .WithMany("Steps") + .HasForeignKey("ProcessExecutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ProcessExecution"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.UserHasTrack", b => + { + b.HasOne("Musify.Domain.Entities.Track", "Track") + .WithMany("UserTracks") + .HasForeignKey("TrackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Musify.Domain.Entities.User", "User") + .WithMany("UserTracks") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Track"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.PlayList", b => + { + b.Navigation("PlayListTracks"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.ProcessExecution", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.Track", b => + { + b.Navigation("PlayListTracks"); + + b.Navigation("UserTracks"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.User", b => + { + b.Navigation("PlayLists"); + + b.Navigation("UserTracks"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Infrastructure/Persistence/Migrations/20260322023145_FirstMigration.cs b/Infrastructure/Persistence/Migrations/20260322023145_FirstMigration.cs new file mode 100644 index 0000000..e32ecc2 --- /dev/null +++ b/Infrastructure/Persistence/Migrations/20260322023145_FirstMigration.cs @@ -0,0 +1,247 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Musify.Infrastructure.Persistence.Migrations +{ + /// + public partial class FirstMigration : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "ProcessExecution", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CorrelationId = table.Column(type: "uuid", nullable: true), + ConversationId = table.Column(type: "uuid", nullable: true), + MessageId = table.Column(type: "uuid", nullable: true), + ProcessName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + Status = table.Column(type: "integer", nullable: false), + StartedDateTime = table.Column(type: "timestamp with time zone", nullable: false), + FinishedDateTime = table.Column(type: "timestamp with time zone", nullable: true), + ErrorMessage = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProcessExecution", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Tracks", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + Title = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + NormalizedTitle = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + SmallPictureKeyName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + MediumPictureKeyName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + LargePictureKeyName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + CreatedDate = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedDate = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Tracks", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "Upload", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + KeyName = table.Column(type: "text", nullable: false), + BucketName = table.Column(type: "text", nullable: false), + ContentType = table.Column(type: "text", nullable: false), + CreatedTime = table.Column(type: "timestamp with time zone", nullable: false), + State = table.Column(type: "integer", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Upload", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "User", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + CreatedAt = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_User", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "ProcessStepExecution", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + ProcessExecutionId = table.Column(type: "uuid", nullable: false), + StepName = table.Column(type: "character varying(256)", maxLength: 256, nullable: false), + ComponentType = table.Column(type: "integer", nullable: false), + Status = table.Column(type: "integer", nullable: false), + Attempt = table.Column(type: "integer", nullable: false), + StartedDateTime = table.Column(type: "timestamp with time zone", nullable: false), + FinishedDateTime = table.Column(type: "timestamp with time zone", nullable: true), + ErrorMessage = table.Column(type: "character varying(2048)", maxLength: 2048, nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_ProcessStepExecution", x => x.Id); + table.ForeignKey( + name: "FK_ProcessStepExecution_ProcessExecution_ProcessExecutionId", + column: x => x.ProcessExecutionId, + principalTable: "ProcessExecution", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PlayLists", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + Name = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + NormalizedName = table.Column(type: "character varying(50)", maxLength: 50, nullable: false), + Description = table.Column(type: "character varying(254)", maxLength: 254, nullable: false), + SmallPictureKeyName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + MediumPictureKeyName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + LargePictureKeyName = table.Column(type: "character varying(64)", maxLength: 64, nullable: false), + CreatedDate = table.Column(type: "timestamp with time zone", nullable: false), + UpdatedDate = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PlayLists", x => x.Id); + table.ForeignKey( + name: "FK_PlayLists_User_UserId", + column: x => x.UserId, + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "UserHasTrack", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + UserId = table.Column(type: "uuid", nullable: false), + TrackId = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_UserHasTrack", x => x.Id); + table.ForeignKey( + name: "FK_UserHasTrack_Tracks_TrackId", + column: x => x.TrackId, + principalTable: "Tracks", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_UserHasTrack_User_UserId", + column: x => x.UserId, + principalTable: "User", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "PlayListHasTrack", + columns: table => new + { + Id = table.Column(type: "uuid", nullable: false), + PlayListId = table.Column(type: "uuid", nullable: false), + TrackId = table.Column(type: "uuid", nullable: false), + Position = table.Column(type: "integer", nullable: false), + AddedAt = table.Column(type: "timestamp with time zone", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_PlayListHasTrack", x => x.Id); + table.ForeignKey( + name: "FK_PlayListHasTrack_PlayLists_PlayListId", + column: x => x.PlayListId, + principalTable: "PlayLists", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_PlayListHasTrack_Tracks_TrackId", + column: x => x.TrackId, + principalTable: "Tracks", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateIndex( + name: "IX_PlayListHasTrack_PlayListId", + table: "PlayListHasTrack", + column: "PlayListId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayListHasTrack_TrackId", + table: "PlayListHasTrack", + column: "TrackId"); + + migrationBuilder.CreateIndex( + name: "IX_PlayLists_UserId", + table: "PlayLists", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_ProcessExecution_CorrelationId_ProcessName", + table: "ProcessExecution", + columns: new[] { "CorrelationId", "ProcessName" }); + + migrationBuilder.CreateIndex( + name: "IX_ProcessStepExecution_ProcessExecutionId_StepName", + table: "ProcessStepExecution", + columns: new[] { "ProcessExecutionId", "StepName" }); + + migrationBuilder.CreateIndex( + name: "IX_UserHasTrack_TrackId", + table: "UserHasTrack", + column: "TrackId"); + + migrationBuilder.CreateIndex( + name: "IX_UserHasTrack_UserId", + table: "UserHasTrack", + column: "UserId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "PlayListHasTrack"); + + migrationBuilder.DropTable( + name: "ProcessStepExecution"); + + migrationBuilder.DropTable( + name: "Upload"); + + migrationBuilder.DropTable( + name: "UserHasTrack"); + + migrationBuilder.DropTable( + name: "PlayLists"); + + migrationBuilder.DropTable( + name: "ProcessExecution"); + + migrationBuilder.DropTable( + name: "Tracks"); + + migrationBuilder.DropTable( + name: "User"); + } + } +} diff --git a/Infrastructure/Persistence/Migrations/DatabaseModelSnapshot.cs b/Infrastructure/Persistence/Migrations/DatabaseModelSnapshot.cs new file mode 100644 index 0000000..f859ef0 --- /dev/null +++ b/Infrastructure/Persistence/Migrations/DatabaseModelSnapshot.cs @@ -0,0 +1,379 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Musify.Infrastructure.Persistence; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Musify.Infrastructure.Persistence.Migrations +{ + [DbContext(typeof(Database))] + partial class DatabaseModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.5") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Musify.Domain.Entities.PlayList", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("Description") + .IsRequired() + .HasMaxLength(254) + .HasColumnType("character varying(254)"); + + b.Property("LargePictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("MediumPictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Name") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("NormalizedName") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("SmallPictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("UpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("PlayLists"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.PlayListHasTrack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("AddedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("PlayListId") + .HasColumnType("uuid"); + + b.Property("Position") + .HasColumnType("integer"); + + b.Property("TrackId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("PlayListId"); + + b.HasIndex("TrackId"); + + b.ToTable("PlayListHasTrack"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.ProcessExecution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("ConversationId") + .HasColumnType("uuid"); + + b.Property("CorrelationId") + .HasColumnType("uuid"); + + b.Property("ErrorMessage") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("FinishedDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("MessageId") + .HasColumnType("uuid"); + + b.Property("ProcessName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.Property("StartedDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.HasIndex("CorrelationId", "ProcessName"); + + b.ToTable("ProcessExecution"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.ProcessStepExecution", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("Attempt") + .HasColumnType("integer"); + + b.Property("ComponentType") + .HasColumnType("integer"); + + b.Property("ErrorMessage") + .HasMaxLength(2048) + .HasColumnType("character varying(2048)"); + + b.Property("FinishedDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("ProcessExecutionId") + .HasColumnType("uuid"); + + b.Property("StartedDateTime") + .HasColumnType("timestamp with time zone"); + + b.Property("Status") + .HasColumnType("integer"); + + b.Property("StepName") + .IsRequired() + .HasMaxLength(256) + .HasColumnType("character varying(256)"); + + b.HasKey("Id"); + + b.HasIndex("ProcessExecutionId", "StepName"); + + b.ToTable("ProcessStepExecution"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.Track", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedDate") + .HasColumnType("timestamp with time zone"); + + b.Property("LargePictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("MediumPictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("NormalizedTitle") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("SmallPictureKeyName") + .IsRequired() + .HasMaxLength(64) + .HasColumnType("character varying(64)"); + + b.Property("Title") + .IsRequired() + .HasMaxLength(50) + .HasColumnType("character varying(50)"); + + b.Property("UpdatedDate") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("Tracks"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.Upload", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("BucketName") + .IsRequired() + .HasColumnType("text"); + + b.Property("ContentType") + .IsRequired() + .HasColumnType("text"); + + b.Property("CreatedTime") + .HasColumnType("timestamp with time zone"); + + b.Property("KeyName") + .IsRequired() + .HasColumnType("text"); + + b.Property("State") + .HasColumnType("integer"); + + b.HasKey("Id"); + + b.ToTable("Upload"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("CreatedAt") + .HasColumnType("timestamp with time zone"); + + b.Property("UpdatedAt") + .HasColumnType("timestamp with time zone"); + + b.HasKey("Id"); + + b.ToTable("User"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.UserHasTrack", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("uuid"); + + b.Property("TrackId") + .HasColumnType("uuid"); + + b.Property("UserId") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.HasIndex("TrackId"); + + b.HasIndex("UserId"); + + b.ToTable("UserHasTrack"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.PlayList", b => + { + b.HasOne("Musify.Domain.Entities.User", "User") + .WithMany("PlayLists") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.PlayListHasTrack", b => + { + b.HasOne("Musify.Domain.Entities.PlayList", "PlayList") + .WithMany("PlayListTracks") + .HasForeignKey("PlayListId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Musify.Domain.Entities.Track", "Track") + .WithMany("PlayListTracks") + .HasForeignKey("TrackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("PlayList"); + + b.Navigation("Track"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.ProcessStepExecution", b => + { + b.HasOne("Musify.Domain.Entities.ProcessExecution", "ProcessExecution") + .WithMany("Steps") + .HasForeignKey("ProcessExecutionId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("ProcessExecution"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.UserHasTrack", b => + { + b.HasOne("Musify.Domain.Entities.Track", "Track") + .WithMany("UserTracks") + .HasForeignKey("TrackId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.HasOne("Musify.Domain.Entities.User", "User") + .WithMany("UserTracks") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + + b.Navigation("Track"); + + b.Navigation("User"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.PlayList", b => + { + b.Navigation("PlayListTracks"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.ProcessExecution", b => + { + b.Navigation("Steps"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.Track", b => + { + b.Navigation("PlayListTracks"); + + b.Navigation("UserTracks"); + }); + + modelBuilder.Entity("Musify.Domain.Entities.User", b => + { + b.Navigation("PlayLists"); + + b.Navigation("UserTracks"); + }); +#pragma warning restore 612, 618 + } + } +}