Change editor rule
This commit is contained in:
@@ -18,7 +18,7 @@ generated_code = true
|
|||||||
[*.cs]
|
[*.cs]
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
csharp_style_namespace_declarations = file_scoped:warning
|
csharp_style_namespace_declarations = block_scoped:warning
|
||||||
csharp_using_directive_placement = outside_namespace:warning
|
csharp_using_directive_placement = outside_namespace:warning
|
||||||
dotnet_sort_system_directives_first = true
|
dotnet_sort_system_directives_first = true
|
||||||
dotnet_separate_import_directive_groups = false
|
dotnet_separate_import_directive_groups = false
|
||||||
|
|||||||
+1
-1
@@ -19,7 +19,7 @@ dotnet format Musify.slnx --verify-no-changes
|
|||||||
|
|
||||||
## Style
|
## Style
|
||||||
|
|
||||||
- File-scoped namespaces, usings outside the namespace and sorted (`.editorconfig` enforces it).
|
- Block-scoped namespaces (`namespace X { }`; file-scoped `namespace X;` is not allowed), usings outside the namespace and sorted (`.editorconfig` enforces it).
|
||||||
- Private fields camelCase without underscore, constants and `static readonly` fields PascalCase, explicit accessibility.
|
- Private fields camelCase without underscore, constants and `static readonly` fields PascalCase, explicit accessibility.
|
||||||
- `ArgumentNullException.ThrowIfNull(x)` without `nameof`.
|
- `ArgumentNullException.ThrowIfNull(x)` without `nameof`.
|
||||||
- Use `PlayList` (not `Playlist`) in code; configuration POCOs end in `Configuration`; injected services are named after their type (`storageService`).
|
- Use `PlayList` (not `Playlist`) in code; configuration POCOs end in `Configuration`; injected services are named after their type (`storageService`).
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
/// <summary>Appends your own tracks to an album in the given order, skipping the ones it already has.</summary>
|
/// <summary>Appends your own tracks to an album in the given order, skipping the ones it already has.</summary>
|
||||||
public record AddTracksToAlbumCommand(long UserId, Guid AlbumId, IReadOnlyList<Guid> TrackIds)
|
public record AddTracksToAlbumCommand(long UserId, Guid AlbumId, IReadOnlyList<Guid> TrackIds)
|
||||||
: ICommand<ErrorOr<Success>>;
|
: ICommand<ErrorOr<Success>>;
|
||||||
|
|
||||||
public class AddTracksToAlbumCommandHandler(
|
public class AddTracksToAlbumCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
ILogger<AddTracksToAlbumCommandHandler> logger)
|
ILogger<AddTracksToAlbumCommandHandler> logger)
|
||||||
: ICommandHandler<AddTracksToAlbumCommand, ErrorOr<Success>>
|
: ICommandHandler<AddTracksToAlbumCommand, ErrorOr<Success>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<Success>> Handle(AddTracksToAlbumCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<Success>> Handle(AddTracksToAlbumCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var album = await database.Albums.FindOwnedAsync(request.AlbumId, request.UserId, cancellationToken);
|
var album = await database.Albums.FindOwnedAsync(request.AlbumId, request.UserId, cancellationToken);
|
||||||
@@ -74,4 +74,5 @@ public class AddTracksToAlbumCommandHandler(
|
|||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record CreateAlbumCommand(
|
public record CreateAlbumCommand(
|
||||||
long UserId,
|
long UserId,
|
||||||
string Title,
|
string Title,
|
||||||
string? Description,
|
string? Description,
|
||||||
@@ -22,14 +22,14 @@ public record CreateAlbumCommand(
|
|||||||
Guid PictureIntentId)
|
Guid PictureIntentId)
|
||||||
: ICommand<ErrorOr<AlbumApplicationResponse>>;
|
: ICommand<ErrorOr<AlbumApplicationResponse>>;
|
||||||
|
|
||||||
public class CreateAlbumCommandHandler(
|
public class CreateAlbumCommandHandler(
|
||||||
IEventBus eventBus,
|
IEventBus eventBus,
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
UploadIntentValidator uploadIntentValidator,
|
UploadIntentValidator uploadIntentValidator,
|
||||||
ILogger<CreateAlbumCommandHandler> logger,
|
ILogger<CreateAlbumCommandHandler> logger,
|
||||||
AlbumConfiguration albumConfiguration)
|
AlbumConfiguration albumConfiguration)
|
||||||
: ICommandHandler<CreateAlbumCommand, ErrorOr<AlbumApplicationResponse>>
|
: ICommandHandler<CreateAlbumCommand, ErrorOr<AlbumApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<AlbumApplicationResponse>> Handle(CreateAlbumCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<AlbumApplicationResponse>> Handle(CreateAlbumCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var userExists = await database.Users
|
var userExists = await database.Users
|
||||||
@@ -69,4 +69,5 @@ public class CreateAlbumCommandHandler(
|
|||||||
|
|
||||||
return AlbumApplicationResponse.FromEntity(album, trackCount: 0);
|
return AlbumApplicationResponse.FromEntity(album, trackCount: 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ using Musify.Application.Events;
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record DeleteAlbumCommand(long UserId, Guid AlbumId)
|
public record DeleteAlbumCommand(long UserId, Guid AlbumId)
|
||||||
: ICommand<ErrorOr<Success>>;
|
: ICommand<ErrorOr<Success>>;
|
||||||
|
|
||||||
public class DeleteAlbumCommandHandler(
|
public class DeleteAlbumCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
IEventBus eventBus,
|
IEventBus eventBus,
|
||||||
ILogger<DeleteAlbumCommandHandler> logger)
|
ILogger<DeleteAlbumCommandHandler> logger)
|
||||||
: ICommandHandler<DeleteAlbumCommand, ErrorOr<Success>>
|
: ICommandHandler<DeleteAlbumCommand, ErrorOr<Success>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<Success>> Handle(DeleteAlbumCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<Success>> Handle(DeleteAlbumCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var found = await database.Albums.FindOwnedAsync(request.AlbumId, request.UserId, cancellationToken);
|
var found = await database.Albums.FindOwnedAsync(request.AlbumId, request.UserId, cancellationToken);
|
||||||
@@ -33,4 +33,5 @@ public class DeleteAlbumCommandHandler(
|
|||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ using Musify.Application.Albums.Responses;
|
|||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record GetAlbumByIdQuery(Guid Id)
|
public record GetAlbumByIdQuery(Guid Id)
|
||||||
: IQuery<ErrorOr<AlbumApplicationResponse>>;
|
: IQuery<ErrorOr<AlbumApplicationResponse>>;
|
||||||
|
|
||||||
public class GetAlbumByIdQueryHandler(IDatabase database)
|
public class GetAlbumByIdQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetAlbumByIdQuery, ErrorOr<AlbumApplicationResponse>>
|
: IQueryHandler<GetAlbumByIdQuery, ErrorOr<AlbumApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<AlbumApplicationResponse>> Handle(GetAlbumByIdQuery request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<AlbumApplicationResponse>> Handle(GetAlbumByIdQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var album = await database.Albums
|
var album = await database.Albums
|
||||||
@@ -26,4 +26,5 @@ public class GetAlbumByIdQueryHandler(IDatabase database)
|
|||||||
? AppErrors.NotFound("Album", request.Id)
|
? AppErrors.NotFound("Album", request.Id)
|
||||||
: album;
|
: album;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ using Musify.Application.Contracts;
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
using Musify.Application.Tracks.Responses;
|
using Musify.Application.Tracks.Responses;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record GetAlbumTracksQuery(
|
public record GetAlbumTracksQuery(
|
||||||
Guid AlbumId,
|
Guid AlbumId,
|
||||||
int PageNumber,
|
int PageNumber,
|
||||||
int PageSize)
|
int PageSize)
|
||||||
: IQuery<ErrorOr<PaginatedResponse<TrackApplicationResponse>>>;
|
: IQuery<ErrorOr<PaginatedResponse<TrackApplicationResponse>>>;
|
||||||
|
|
||||||
public class GetAlbumTracksQueryHandler(IDatabase database)
|
public class GetAlbumTracksQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetAlbumTracksQuery, ErrorOr<PaginatedResponse<TrackApplicationResponse>>>
|
: IQueryHandler<GetAlbumTracksQuery, ErrorOr<PaginatedResponse<TrackApplicationResponse>>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<PaginatedResponse<TrackApplicationResponse>>> Handle(GetAlbumTracksQuery request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<PaginatedResponse<TrackApplicationResponse>>> Handle(GetAlbumTracksQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var albumExists = await database.Albums
|
var albumExists = await database.Albums
|
||||||
@@ -35,4 +35,5 @@ public class GetAlbumTracksQueryHandler(IDatabase database)
|
|||||||
.SelectResponse()
|
.SelectResponse()
|
||||||
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ using Musify.Application.Albums.Responses;
|
|||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record GetAlbumsQuery(string? Title, int PageNumber, int PageSize)
|
public record GetAlbumsQuery(string? Title, int PageNumber, int PageSize)
|
||||||
: IQuery<PaginatedResponse<AlbumApplicationResponse>>;
|
: IQuery<PaginatedResponse<AlbumApplicationResponse>>;
|
||||||
|
|
||||||
public class GetAlbumsQueryHandler(IDatabase database)
|
public class GetAlbumsQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetAlbumsQuery, PaginatedResponse<AlbumApplicationResponse>>
|
: IQueryHandler<GetAlbumsQuery, PaginatedResponse<AlbumApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<PaginatedResponse<AlbumApplicationResponse>> Handle(GetAlbumsQuery request, CancellationToken cancellationToken)
|
public async ValueTask<PaginatedResponse<AlbumApplicationResponse>> Handle(GetAlbumsQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var albumsQuery = database.Albums
|
var albumsQuery = database.Albums
|
||||||
@@ -30,4 +30,5 @@ public class GetAlbumsQueryHandler(IDatabase database)
|
|||||||
.SelectResponse()
|
.SelectResponse()
|
||||||
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ using Musify.Application.Albums.Responses;
|
|||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record GetAlbumsByUserIdQuery(long UserId, int PageNumber, int PageSize)
|
public record GetAlbumsByUserIdQuery(long UserId, int PageNumber, int PageSize)
|
||||||
: IQuery<ErrorOr<PaginatedResponse<AlbumApplicationResponse>>>;
|
: IQuery<ErrorOr<PaginatedResponse<AlbumApplicationResponse>>>;
|
||||||
|
|
||||||
public class GetAlbumsByUserIdQueryHandler(IDatabase database)
|
public class GetAlbumsByUserIdQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetAlbumsByUserIdQuery, ErrorOr<PaginatedResponse<AlbumApplicationResponse>>>
|
: IQueryHandler<GetAlbumsByUserIdQuery, ErrorOr<PaginatedResponse<AlbumApplicationResponse>>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<PaginatedResponse<AlbumApplicationResponse>>> Handle(GetAlbumsByUserIdQuery request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<PaginatedResponse<AlbumApplicationResponse>>> Handle(GetAlbumsByUserIdQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var userExists = await database.Users
|
var userExists = await database.Users
|
||||||
@@ -30,4 +30,5 @@ public class GetAlbumsByUserIdQueryHandler(IDatabase database)
|
|||||||
.SelectResponse()
|
.SelectResponse()
|
||||||
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Musify.Application.Albums.Responses;
|
using Musify.Application.Albums.Responses;
|
||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record GetRecentlyListenedAlbumsQuery(long UserId, int Limit)
|
public record GetRecentlyListenedAlbumsQuery(long UserId, int Limit)
|
||||||
: IQuery<IReadOnlyList<AlbumApplicationResponse>>;
|
: IQuery<IReadOnlyList<AlbumApplicationResponse>>;
|
||||||
|
|
||||||
public class GetRecentlyListenedAlbumsQueryHandler(IDatabase database)
|
public class GetRecentlyListenedAlbumsQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetRecentlyListenedAlbumsQuery, IReadOnlyList<AlbumApplicationResponse>>
|
: IQueryHandler<GetRecentlyListenedAlbumsQuery, IReadOnlyList<AlbumApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<IReadOnlyList<AlbumApplicationResponse>> Handle(GetRecentlyListenedAlbumsQuery request, CancellationToken cancellationToken)
|
public async ValueTask<IReadOnlyList<AlbumApplicationResponse>> Handle(GetRecentlyListenedAlbumsQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var listens = database.ListeningHistories
|
var listens = database.ListeningHistories
|
||||||
@@ -48,4 +48,5 @@ public class GetRecentlyListenedAlbumsQueryHandler(IDatabase database)
|
|||||||
.Select(entry => albumsById[entry.AlbumId])
|
.Select(entry => albumsById[entry.AlbumId])
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record RemoveTrackFromAlbumCommand(long UserId, Guid AlbumId, Guid TrackId)
|
public record RemoveTrackFromAlbumCommand(long UserId, Guid AlbumId, Guid TrackId)
|
||||||
: ICommand<ErrorOr<Success>>;
|
: ICommand<ErrorOr<Success>>;
|
||||||
|
|
||||||
public class RemoveTrackFromAlbumCommandHandler(
|
public class RemoveTrackFromAlbumCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
ILogger<RemoveTrackFromAlbumCommandHandler> logger)
|
ILogger<RemoveTrackFromAlbumCommandHandler> logger)
|
||||||
: ICommandHandler<RemoveTrackFromAlbumCommand, ErrorOr<Success>>
|
: ICommandHandler<RemoveTrackFromAlbumCommand, ErrorOr<Success>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<Success>> Handle(RemoveTrackFromAlbumCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<Success>> Handle(RemoveTrackFromAlbumCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var album = await database.Albums.FindOwnedAsync(request.AlbumId, request.UserId, cancellationToken);
|
var album = await database.Albums.FindOwnedAsync(request.AlbumId, request.UserId, cancellationToken);
|
||||||
@@ -42,4 +42,5 @@ public class RemoveTrackFromAlbumCommandHandler(
|
|||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,21 @@ using Musify.Application.Pictures.Responses;
|
|||||||
using Musify.Application.Services;
|
using Musify.Application.Services;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record RequestAlbumPictureUploadCommand(
|
public record RequestAlbumPictureUploadCommand(
|
||||||
long UserId,
|
long UserId,
|
||||||
string FileType,
|
string FileType,
|
||||||
string ContentType,
|
string ContentType,
|
||||||
long? ExpectedSizeBytes = null)
|
long? ExpectedSizeBytes = null)
|
||||||
: ICommand<ErrorOr<PictureUploadResponse>>;
|
: ICommand<ErrorOr<PictureUploadResponse>>;
|
||||||
|
|
||||||
public class RequestAlbumPictureUploadCommandHandler(UploadIntentIssuer issuer, AlbumConfiguration albumConfiguration)
|
public class RequestAlbumPictureUploadCommandHandler(UploadIntentIssuer issuer, AlbumConfiguration albumConfiguration)
|
||||||
: ICommandHandler<RequestAlbumPictureUploadCommand, ErrorOr<PictureUploadResponse>>
|
: ICommandHandler<RequestAlbumPictureUploadCommand, ErrorOr<PictureUploadResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<PictureUploadResponse>> Handle(RequestAlbumPictureUploadCommand request, CancellationToken cancellationToken) =>
|
public async ValueTask<ErrorOr<PictureUploadResponse>> Handle(RequestAlbumPictureUploadCommand request, CancellationToken cancellationToken) =>
|
||||||
await issuer.IssuePictureAsync(
|
await issuer.IssuePictureAsync(
|
||||||
request.UserId, UploadIntentPurpose.AlbumPicture, albumConfiguration,
|
request.UserId, UploadIntentPurpose.AlbumPicture, albumConfiguration,
|
||||||
request.FileType, request.ContentType, request.ExpectedSizeBytes, cancellationToken);
|
request.FileType, request.ContentType, request.ExpectedSizeBytes, cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
|
|
||||||
namespace Musify.Application.Albums.Responses;
|
namespace Musify.Application.Albums.Responses
|
||||||
|
{
|
||||||
public record AlbumApplicationResponse(
|
public record AlbumApplicationResponse(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
string Title,
|
string Title,
|
||||||
string? Description,
|
string? Description,
|
||||||
@@ -15,7 +15,7 @@ public record AlbumApplicationResponse(
|
|||||||
DateTime CreatedAt,
|
DateTime CreatedAt,
|
||||||
DateTime UpdatedAt,
|
DateTime UpdatedAt,
|
||||||
IReadOnlyList<Guid> CoverTrackIds)
|
IReadOnlyList<Guid> CoverTrackIds)
|
||||||
{
|
{
|
||||||
public const int CoverTrackCount = AlbumProjections.CoverTrackCount;
|
public const int CoverTrackCount = AlbumProjections.CoverTrackCount;
|
||||||
|
|
||||||
public static AlbumApplicationResponse FromEntity(
|
public static AlbumApplicationResponse FromEntity(
|
||||||
@@ -37,4 +37,5 @@ public record AlbumApplicationResponse(
|
|||||||
album.UpdatedAt,
|
album.UpdatedAt,
|
||||||
coverTrackIds ?? []);
|
coverTrackIds ?? []);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Albums.Responses;
|
namespace Musify.Application.Albums.Responses
|
||||||
|
|
||||||
public static class AlbumProjections
|
|
||||||
{
|
{
|
||||||
|
public static class AlbumProjections
|
||||||
|
{
|
||||||
public const int CoverTrackCount = 4;
|
public const int CoverTrackCount = 4;
|
||||||
|
|
||||||
public static IQueryable<Album> Active(this IQueryable<Album> query) =>
|
public static IQueryable<Album> Active(this IQueryable<Album> query) =>
|
||||||
@@ -28,4 +28,5 @@ public static class AlbumProjections
|
|||||||
.Take(CoverTrackCount)
|
.Take(CoverTrackCount)
|
||||||
.Select(albumTrack => albumTrack.TrackId)
|
.Select(albumTrack => albumTrack.TrackId)
|
||||||
.ToList()));
|
.ToList()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ using Musify.Application.Services;
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Albums;
|
namespace Musify.Application.Albums
|
||||||
|
{
|
||||||
public record UpdateAlbumCommand(
|
public record UpdateAlbumCommand(
|
||||||
long UserId,
|
long UserId,
|
||||||
Guid AlbumId,
|
Guid AlbumId,
|
||||||
string Title,
|
string Title,
|
||||||
@@ -22,14 +22,14 @@ public record UpdateAlbumCommand(
|
|||||||
Guid? NewPictureIntentId)
|
Guid? NewPictureIntentId)
|
||||||
: ICommand<ErrorOr<AlbumApplicationResponse>>;
|
: ICommand<ErrorOr<AlbumApplicationResponse>>;
|
||||||
|
|
||||||
public class UpdateAlbumCommandHandler(
|
public class UpdateAlbumCommandHandler(
|
||||||
IEventBus eventBus,
|
IEventBus eventBus,
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
UploadIntentValidator uploadIntentValidator,
|
UploadIntentValidator uploadIntentValidator,
|
||||||
ILogger<UpdateAlbumCommandHandler> logger,
|
ILogger<UpdateAlbumCommandHandler> logger,
|
||||||
AlbumConfiguration albumConfiguration)
|
AlbumConfiguration albumConfiguration)
|
||||||
: ICommandHandler<UpdateAlbumCommand, ErrorOr<AlbumApplicationResponse>>
|
: ICommandHandler<UpdateAlbumCommand, ErrorOr<AlbumApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<AlbumApplicationResponse>> Handle(UpdateAlbumCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<AlbumApplicationResponse>> Handle(UpdateAlbumCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var found = await database.Albums.FindOwnedAsync(request.AlbumId, request.UserId, cancellationToken);
|
var found = await database.Albums.FindOwnedAsync(request.AlbumId, request.UserId, cancellationToken);
|
||||||
@@ -70,4 +70,5 @@ public class UpdateAlbumCommandHandler(
|
|||||||
.SelectResponse()
|
.SelectResponse()
|
||||||
.SingleAsync(cancellationToken);
|
.SingleAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ using Microsoft.Extensions.DependencyInjection;
|
|||||||
using Musify.Application.Configuration;
|
using Musify.Application.Configuration;
|
||||||
using Musify.Application.Services;
|
using Musify.Application.Services;
|
||||||
|
|
||||||
namespace Musify.Application;
|
namespace Musify.Application
|
||||||
|
|
||||||
public static class ApplicationDependencyInjection
|
|
||||||
{
|
{
|
||||||
|
public static class ApplicationDependencyInjection
|
||||||
|
{
|
||||||
public static IServiceCollection AddApplication(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddApplication(this IServiceCollection services, IConfiguration configuration)
|
||||||
{
|
{
|
||||||
services.AddMediator();
|
services.AddMediator();
|
||||||
@@ -25,4 +25,5 @@ public static class ApplicationDependencyInjection
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public class AlbumConfiguration : IConfigurationOptions, IPictureOwnerConfiguration
|
|
||||||
{
|
{
|
||||||
|
public class AlbumConfiguration : IConfigurationOptions, IPictureOwnerConfiguration
|
||||||
|
{
|
||||||
public static string SectionName => "Album";
|
public static string SectionName => "Album";
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
@@ -11,4 +11,5 @@ public class AlbumConfiguration : IConfigurationOptions, IPictureOwnerConfigurat
|
|||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public PictureSizes PicturesSizes { get; set; } = new();
|
public PictureSizes PicturesSizes { get; set; } = new();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public class ApplicationStorageConfiguration : IConfigurationOptions
|
|
||||||
{
|
{
|
||||||
|
public class ApplicationStorageConfiguration : IConfigurationOptions
|
||||||
|
{
|
||||||
public static string SectionName => "ApplicationStorage";
|
public static string SectionName => "ApplicationStorage";
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
[StringLength(128, MinimumLength = 1)]
|
[StringLength(128, MinimumLength = 1)]
|
||||||
public required string Bucket { get; set; }
|
public required string Bucket { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public static class ConfigurationDependencyInjection
|
|
||||||
{
|
{
|
||||||
|
public static class ConfigurationDependencyInjection
|
||||||
|
{
|
||||||
public static IServiceCollection AddValidatedOptions<T>(this IServiceCollection services, IConfiguration configuration)
|
public static IServiceCollection AddValidatedOptions<T>(this IServiceCollection services, IConfiguration configuration)
|
||||||
where T : class, IConfigurationOptions =>
|
where T : class, IConfigurationOptions =>
|
||||||
services.AddValidatedOptions<T>(configuration, T.SectionName);
|
services.AddValidatedOptions<T>(configuration, T.SectionName);
|
||||||
@@ -26,4 +26,5 @@ public static class ConfigurationDependencyInjection
|
|||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,17 @@
|
|||||||
using Mediator;
|
using Mediator;
|
||||||
using Musify.Application.Configuration.Responses;
|
using Musify.Application.Configuration.Responses;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
|
|
||||||
public record GetPlaybackPublicConfigQuery : IQuery<PlaybackPublicConfigResponse>;
|
|
||||||
|
|
||||||
public class GetPlaybackPublicConfigQueryHandler(PlaybackConfiguration playbackConfiguration)
|
|
||||||
: IQueryHandler<GetPlaybackPublicConfigQuery, PlaybackPublicConfigResponse>
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public record GetPlaybackPublicConfigQuery : IQuery<PlaybackPublicConfigResponse>;
|
||||||
|
|
||||||
|
public class GetPlaybackPublicConfigQueryHandler(PlaybackConfiguration playbackConfiguration)
|
||||||
|
: IQueryHandler<GetPlaybackPublicConfigQuery, PlaybackPublicConfigResponse>
|
||||||
|
{
|
||||||
public ValueTask<PlaybackPublicConfigResponse> Handle(GetPlaybackPublicConfigQuery query, CancellationToken cancellationToken) =>
|
public ValueTask<PlaybackPublicConfigResponse> Handle(GetPlaybackPublicConfigQuery query, CancellationToken cancellationToken) =>
|
||||||
ValueTask.FromResult(new PlaybackPublicConfigResponse(
|
ValueTask.FromResult(new PlaybackPublicConfigResponse(
|
||||||
playbackConfiguration.AllowAnonymousListening,
|
playbackConfiguration.AllowAnonymousListening,
|
||||||
playbackConfiguration.AnonymousFragmentSeconds));
|
playbackConfiguration.AnonymousFragmentSeconds));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public interface IConfigurationOptions
|
|
||||||
{
|
{
|
||||||
|
public interface IConfigurationOptions
|
||||||
|
{
|
||||||
public static abstract string SectionName { get; }
|
public static abstract string SectionName { get; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public class MixConfiguration : IConfigurationOptions
|
|
||||||
{
|
{
|
||||||
|
public class MixConfiguration : IConfigurationOptions
|
||||||
|
{
|
||||||
public static string SectionName => "Mix";
|
public static string SectionName => "Mix";
|
||||||
|
|
||||||
[Range(4, 100)]
|
[Range(4, 100)]
|
||||||
public int ItemsPerMix { get; set; } = 12;
|
public int ItemsPerMix { get; set; } = 12;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,17 +2,17 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public enum PictureSize
|
|
||||||
{
|
{
|
||||||
|
public enum PictureSize
|
||||||
|
{
|
||||||
Small,
|
Small,
|
||||||
Medium,
|
Medium,
|
||||||
Large
|
Large
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PictureRoutes
|
public class PictureRoutes
|
||||||
{
|
{
|
||||||
[Required]
|
[Required]
|
||||||
public string UploadsFolder { get; set; } = "uploads";
|
public string UploadsFolder { get; set; } = "uploads";
|
||||||
|
|
||||||
@@ -46,10 +46,10 @@ public class PictureRoutes
|
|||||||
|
|
||||||
public string BuildTempPath(string tempRootPrefix, long userId, string objectName) =>
|
public string BuildTempPath(string tempRootPrefix, long userId, string objectName) =>
|
||||||
StorageKey.Combine(tempRootPrefix, userId.ToString(CultureInfo.InvariantCulture), ParentFolder, objectName);
|
StorageKey.Combine(tempRootPrefix, userId.ToString(CultureInfo.InvariantCulture), ParentFolder, objectName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PictureSizes
|
public class PictureSizes
|
||||||
{
|
{
|
||||||
[Range(1, 1024)]
|
[Range(1, 1024)]
|
||||||
public int SmallWidth { get; set; } = 128;
|
public int SmallWidth { get; set; } = 128;
|
||||||
|
|
||||||
@@ -72,20 +72,21 @@ public class PictureSizes
|
|||||||
new ImageSize(routes.FolderPath(PictureSize.Small), SmallWidth, SmallHeight),
|
new ImageSize(routes.FolderPath(PictureSize.Small), SmallWidth, SmallHeight),
|
||||||
new ImageSize(routes.FolderPath(PictureSize.Medium), MediumWidth, MediumHeight),
|
new ImageSize(routes.FolderPath(PictureSize.Medium), MediumWidth, MediumHeight),
|
||||||
new ImageSize(routes.FolderPath(PictureSize.Large), LargeWidth, LargeHeight));
|
new ImageSize(routes.FolderPath(PictureSize.Large), LargeWidth, LargeHeight));
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IPictureOwnerConfiguration
|
public interface IPictureOwnerConfiguration
|
||||||
{
|
{
|
||||||
public PictureRoutes Routes { get; }
|
public PictureRoutes Routes { get; }
|
||||||
|
|
||||||
public PictureSizes PicturesSizes { get; }
|
public PictureSizes PicturesSizes { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class PictureSizeParser
|
public static class PictureSizeParser
|
||||||
{
|
{
|
||||||
/// <summary>Parses the <c>size</c> query value; anything unknown means <see cref="PictureSize.Medium"/>.</summary>
|
/// <summary>Parses the <c>size</c> query value; anything unknown means <see cref="PictureSize.Medium"/>.</summary>
|
||||||
public static PictureSize Parse(string? value) =>
|
public static PictureSize Parse(string? value) =>
|
||||||
Enum.TryParse<PictureSize>(value, ignoreCase: true, out var size) && Enum.IsDefined(size)
|
Enum.TryParse<PictureSize>(value, ignoreCase: true, out var size) && Enum.IsDefined(size)
|
||||||
? size
|
? size
|
||||||
: PictureSize.Medium;
|
: PictureSize.Medium;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public class PlayListConfiguration : IConfigurationOptions, IPictureOwnerConfiguration
|
|
||||||
{
|
{
|
||||||
|
public class PlayListConfiguration : IConfigurationOptions, IPictureOwnerConfiguration
|
||||||
|
{
|
||||||
public static string SectionName => "PlayList";
|
public static string SectionName => "PlayList";
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
@@ -11,4 +11,5 @@ public class PlayListConfiguration : IConfigurationOptions, IPictureOwnerConfigu
|
|||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public PictureSizes PicturesSizes { get; set; } = new();
|
public PictureSizes PicturesSizes { get; set; } = new();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
|
|
||||||
public sealed class PlaybackConfiguration : IConfigurationOptions
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
public sealed class PlaybackConfiguration : IConfigurationOptions
|
||||||
|
{
|
||||||
public static string SectionName => "Playback";
|
public static string SectionName => "Playback";
|
||||||
|
|
||||||
public bool AllowAnonymousListening { get; set; }
|
public bool AllowAnonymousListening { get; set; }
|
||||||
@@ -14,4 +14,5 @@ public sealed class PlaybackConfiguration : IConfigurationOptions
|
|||||||
|
|
||||||
[Range(1, int.MaxValue)]
|
[Range(1, int.MaxValue)]
|
||||||
public int EstimatedAudioBytesPerSecond { get; set; } = 16_000;
|
public int EstimatedAudioBytesPerSecond { get; set; } = 16_000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -1,4 +1,5 @@
|
|||||||
namespace Musify.Application.Configuration.Responses;
|
namespace Musify.Application.Configuration.Responses
|
||||||
|
{
|
||||||
|
|
||||||
|
public record PlaybackPublicConfigResponse(bool AllowAnonymousListening, int AnonymousFragmentSeconds);
|
||||||
public record PlaybackPublicConfigResponse(bool AllowAnonymousListening, int AnonymousFragmentSeconds);
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public class StreamGatewayConfiguration : IConfigurationOptions
|
|
||||||
{
|
{
|
||||||
|
public class StreamGatewayConfiguration : IConfigurationOptions
|
||||||
|
{
|
||||||
public static string SectionName => "StreamGateway";
|
public static string SectionName => "StreamGateway";
|
||||||
|
|
||||||
[Url]
|
[Url]
|
||||||
@@ -12,4 +12,5 @@ public class StreamGatewayConfiguration : IConfigurationOptions
|
|||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string AudioFileName { get; set; } = "audio.m4a";
|
public string AudioFileName { get; set; } = "audio.m4a";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ using System.ComponentModel.DataAnnotations;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public class TrackConfiguration : IConfigurationOptions, IPictureOwnerConfiguration
|
|
||||||
{
|
{
|
||||||
|
public class TrackConfiguration : IConfigurationOptions, IPictureOwnerConfiguration
|
||||||
|
{
|
||||||
public static string SectionName => "Track";
|
public static string SectionName => "Track";
|
||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
@@ -15,10 +15,10 @@ public class TrackConfiguration : IConfigurationOptions, IPictureOwnerConfigurat
|
|||||||
public PictureSizes PicturesSizes { get; set; } = new();
|
public PictureSizes PicturesSizes { get; set; } = new();
|
||||||
|
|
||||||
PictureRoutes IPictureOwnerConfiguration.Routes => Routes;
|
PictureRoutes IPictureOwnerConfiguration.Routes => Routes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TrackRoutes : PictureRoutes
|
public class TrackRoutes : PictureRoutes
|
||||||
{
|
{
|
||||||
public TrackRoutes()
|
public TrackRoutes()
|
||||||
{
|
{
|
||||||
ParentFolder = "Tracks";
|
ParentFolder = "Tracks";
|
||||||
@@ -53,4 +53,5 @@ public class TrackRoutes : PictureRoutes
|
|||||||
|
|
||||||
public string BuildProcessedAudioPath(string folderName) =>
|
public string BuildProcessedAudioPath(string folderName) =>
|
||||||
StorageKey.Combine(ParentFolder, ProcessedAudiosFolder, folderName);
|
StorageKey.Combine(ParentFolder, ProcessedAudiosFolder, folderName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
namespace Musify.Application.Configuration;
|
namespace Musify.Application.Configuration
|
||||||
|
|
||||||
public class UploadIntentConfiguration : IConfigurationOptions
|
|
||||||
{
|
{
|
||||||
|
public class UploadIntentConfiguration : IConfigurationOptions
|
||||||
|
{
|
||||||
public static string SectionName => "UploadIntent";
|
public static string SectionName => "UploadIntent";
|
||||||
|
|
||||||
[Range(10, 3600)]
|
[Range(10, 3600)]
|
||||||
@@ -38,4 +38,5 @@ public class UploadIntentConfiguration : IConfigurationOptions
|
|||||||
|
|
||||||
[Range(60, 86400)]
|
[Range(60, 86400)]
|
||||||
public int TempCleanupJobIntervalSeconds { get; set; } = 3600;
|
public int TempCleanupJobIntervalSeconds { get; set; } = 3600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
{
|
||||||
public record AudioTranscodeResult(int StatusCode, TimeSpan Duration);
|
public record AudioTranscodeResult(int StatusCode, TimeSpan Duration);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
|
||||||
public interface IAudioTranscoderService
|
|
||||||
{
|
{
|
||||||
|
public interface IAudioTranscoderService
|
||||||
|
{
|
||||||
public Task<AudioTranscodeResult> TranscodeToAudioFileAsync(Stream audioStream, string destinationPath, CancellationToken cancellationToken);
|
public Task<AudioTranscodeResult> TranscodeToAudioFileAsync(Stream audioStream, string destinationPath, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ using ErrorOr;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
|
|
||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
|
||||||
public interface IDatabase
|
|
||||||
{
|
{
|
||||||
|
public interface IDatabase
|
||||||
|
{
|
||||||
public DbSet<User> Users { get; }
|
public DbSet<User> Users { get; }
|
||||||
|
|
||||||
public DbSet<PlayList> PlayLists { get; }
|
public DbSet<PlayList> PlayLists { get; }
|
||||||
@@ -39,4 +39,5 @@ public interface IDatabase
|
|||||||
public Task<int> SaveChangesAsync(CancellationToken cancellationToken);
|
public Task<int> SaveChangesAsync(CancellationToken cancellationToken);
|
||||||
|
|
||||||
public Task<ErrorOr<Success>> TrySaveChangesAsync(Error onUniqueViolation, CancellationToken cancellationToken);
|
public Task<ErrorOr<Success>> TrySaveChangesAsync(Error onUniqueViolation, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
|
||||||
public interface IDatabaseTransaction : IAsyncDisposable
|
|
||||||
{
|
{
|
||||||
|
public interface IDatabaseTransaction : IAsyncDisposable
|
||||||
|
{
|
||||||
public Task CommitAsync(CancellationToken cancellationToken = default);
|
public Task CommitAsync(CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
|
||||||
public interface IEventBus
|
|
||||||
{
|
{
|
||||||
|
public interface IEventBus
|
||||||
|
{
|
||||||
public Task PublishAsync<T>(T message, CancellationToken cancellationToken) where T : class;
|
public Task PublishAsync<T>(T message, CancellationToken cancellationToken) where T : class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
|
||||||
public interface IPictureService
|
|
||||||
{
|
{
|
||||||
|
public interface IPictureService
|
||||||
|
{
|
||||||
public Task<Stream> ResizePictureAsWebpAsync(Stream pictureStream, int width, int height, CancellationToken cancellationToken);
|
public Task<Stream> ResizePictureAsWebpAsync(Stream pictureStream, int width, int height, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
|
||||||
public interface IStorageService
|
|
||||||
{
|
{
|
||||||
|
public interface IStorageService
|
||||||
|
{
|
||||||
/// <summary>Opens an object for reading, or returns null when it does not exist.</summary>
|
/// <summary>Opens an object for reading, or returns null when it does not exist.</summary>
|
||||||
public Task<Stream?> GetFileAsync(string bucket, string key, CancellationToken cancellationToken);
|
public Task<Stream?> GetFileAsync(string bucket, string key, CancellationToken cancellationToken);
|
||||||
|
|
||||||
@@ -21,4 +21,5 @@ public interface IStorageService
|
|||||||
CancellationToken cancellationToken);
|
CancellationToken cancellationToken);
|
||||||
|
|
||||||
public IAsyncEnumerable<StorageObject> ListObjectsAsync(string bucket, string prefix, CancellationToken cancellationToken);
|
public IAsyncEnumerable<StorageObject> ListObjectsAsync(string bucket, string prefix, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
|
||||||
public interface IStreamTicketService
|
|
||||||
{
|
{
|
||||||
|
public interface IStreamTicketService
|
||||||
|
{
|
||||||
public StreamTicket IssueTicket(long? userId, string keyPrefix, long? maxBytes = null);
|
public StreamTicket IssueTicket(long? userId, string keyPrefix, long? maxBytes = null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public record StreamTicket(string Token, int ExpiresInSeconds);
|
public record StreamTicket(string Token, int ExpiresInSeconds);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
{
|
||||||
public record ObjectMetaData(string ContentType, long ContentLength);
|
public record ObjectMetaData(string ContentType, long ContentLength);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Contracts;
|
namespace Musify.Application.Contracts
|
||||||
|
{
|
||||||
public record StorageObject(string Key, DateTime LastModifiedUtc);
|
public record StorageObject(string Key, DateTime LastModifiedUtc);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record AlbumPictureProcessed(Guid AlbumId);
|
public record AlbumPictureProcessed(Guid AlbumId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record AlbumPictureProcessingFailed(Guid AlbumId);
|
public record AlbumPictureProcessingFailed(Guid AlbumId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record AlbumProcessingFailed(
|
public record AlbumProcessingFailed(
|
||||||
Guid AlbumId,
|
Guid AlbumId,
|
||||||
string? Bucket,
|
string? Bucket,
|
||||||
string? PictureKey);
|
string? PictureKey);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record CreateAlbumResourcesEvent(
|
public record CreateAlbumResourcesEvent(
|
||||||
Guid AlbumId,
|
Guid AlbumId,
|
||||||
Guid PictureIntentId,
|
Guid PictureIntentId,
|
||||||
string Bucket,
|
string Bucket,
|
||||||
string PictureSourceKey,
|
string PictureSourceKey,
|
||||||
string PictureDestinationKey,
|
string PictureDestinationKey,
|
||||||
ImageSizes Sizes);
|
ImageSizes Sizes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record CreatePlayListResourcesEvent(
|
public record CreatePlayListResourcesEvent(
|
||||||
Guid PlayListId,
|
Guid PlayListId,
|
||||||
Guid PictureIntentId,
|
Guid PictureIntentId,
|
||||||
string Bucket,
|
string Bucket,
|
||||||
string PictureSourceKey,
|
string PictureSourceKey,
|
||||||
string PictureDestinationKey,
|
string PictureDestinationKey,
|
||||||
ImageSizes Sizes);
|
ImageSizes Sizes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record CreateTrackResourcesEvent(
|
public record CreateTrackResourcesEvent(
|
||||||
Guid TrackId,
|
Guid TrackId,
|
||||||
Guid PictureIntentId,
|
Guid PictureIntentId,
|
||||||
Guid AudioIntentId,
|
Guid AudioIntentId,
|
||||||
@@ -13,3 +13,4 @@ public record CreateTrackResourcesEvent(
|
|||||||
string AudioDestinationKey,
|
string AudioDestinationKey,
|
||||||
string AudioProcessedFolderKey,
|
string AudioProcessedFolderKey,
|
||||||
ImageSizes Sizes);
|
ImageSizes Sizes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record DeleteAlbumEvent(
|
public record DeleteAlbumEvent(
|
||||||
Guid AlbumId,
|
Guid AlbumId,
|
||||||
long UserId);
|
long UserId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record DeletePlayListEvent(
|
public record DeletePlayListEvent(
|
||||||
Guid PlayListId,
|
Guid PlayListId,
|
||||||
long UserId);
|
long UserId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record DeleteTrackEvent(
|
public record DeleteTrackEvent(
|
||||||
Guid TrackId,
|
Guid TrackId,
|
||||||
long UserId);
|
long UserId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record PlayListPictureProcessed(Guid PlayListId);
|
public record PlayListPictureProcessed(Guid PlayListId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record PlayListPictureProcessingFailed(Guid PlayListId);
|
public record PlayListPictureProcessingFailed(Guid PlayListId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record PlayListProcessingFailed(
|
public record PlayListProcessingFailed(
|
||||||
Guid PlayListId,
|
Guid PlayListId,
|
||||||
string? Bucket,
|
string? Bucket,
|
||||||
string? PictureKey);
|
string? PictureKey);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record TrackAudioProcessed(Guid TrackId);
|
public record TrackAudioProcessed(Guid TrackId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record TrackAudioProcessingFailed(Guid TrackId);
|
public record TrackAudioProcessingFailed(Guid TrackId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record TrackPictureProcessed(Guid TrackId);
|
public record TrackPictureProcessed(Guid TrackId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record TrackPictureProcessingFailed(Guid TrackId);
|
public record TrackPictureProcessingFailed(Guid TrackId);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record TrackProcessingFailed(
|
public record TrackProcessingFailed(
|
||||||
Guid TrackId,
|
Guid TrackId,
|
||||||
string? Bucket,
|
string? Bucket,
|
||||||
string? PictureKey,
|
string? PictureKey,
|
||||||
string? AudioKey);
|
string? AudioKey);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record UpdateAlbumPictureEvent(
|
public record UpdateAlbumPictureEvent(
|
||||||
Guid AlbumId,
|
Guid AlbumId,
|
||||||
string Bucket,
|
string Bucket,
|
||||||
string SourceKey,
|
string SourceKey,
|
||||||
ImageSizes Sizes);
|
ImageSizes Sizes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record UpdateAlbumPictureSourceEvent(
|
public record UpdateAlbumPictureSourceEvent(
|
||||||
Guid AlbumId,
|
Guid AlbumId,
|
||||||
Guid PictureIntentId,
|
Guid PictureIntentId,
|
||||||
string Bucket,
|
string Bucket,
|
||||||
string PictureSourceKey,
|
string PictureSourceKey,
|
||||||
string PictureDestinationKey,
|
string PictureDestinationKey,
|
||||||
ImageSizes Sizes);
|
ImageSizes Sizes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record UpdatePlayListPictureEvent(
|
public record UpdatePlayListPictureEvent(
|
||||||
Guid PlayListId,
|
Guid PlayListId,
|
||||||
string Bucket,
|
string Bucket,
|
||||||
string SourceKey,
|
string SourceKey,
|
||||||
ImageSizes Sizes);
|
ImageSizes Sizes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record UpdatePlayListPictureSourceEvent(
|
public record UpdatePlayListPictureSourceEvent(
|
||||||
Guid PlayListId,
|
Guid PlayListId,
|
||||||
Guid PictureIntentId,
|
Guid PictureIntentId,
|
||||||
string Bucket,
|
string Bucket,
|
||||||
string PictureSourceKey,
|
string PictureSourceKey,
|
||||||
string PictureDestinationKey,
|
string PictureDestinationKey,
|
||||||
ImageSizes Sizes);
|
ImageSizes Sizes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record UpdateTrackAudioEvent(
|
public record UpdateTrackAudioEvent(
|
||||||
Guid TrackId,
|
Guid TrackId,
|
||||||
string SourceBucket,
|
string SourceBucket,
|
||||||
string SourceKey,
|
string SourceKey,
|
||||||
string DestinationBucket,
|
string DestinationBucket,
|
||||||
string DestinationFolderKey);
|
string DestinationFolderKey);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Events;
|
namespace Musify.Application.Events
|
||||||
|
{
|
||||||
public record UpdateTrackPictureEvent(
|
public record UpdateTrackPictureEvent(
|
||||||
Guid TrackId,
|
Guid TrackId,
|
||||||
string Bucket,
|
string Bucket,
|
||||||
string SourceKey,
|
string SourceKey,
|
||||||
ImageSizes Sizes);
|
ImageSizes Sizes);
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ using Mediator;
|
|||||||
using Musify.Application.Genres.Responses;
|
using Musify.Application.Genres.Responses;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Genres;
|
namespace Musify.Application.Genres
|
||||||
|
|
||||||
public record GetAvailableGenresQuery : IQuery<IReadOnlyList<AvailableGenreResponse>>;
|
|
||||||
|
|
||||||
public class GetAvailableGenresQueryHandler
|
|
||||||
: IQueryHandler<GetAvailableGenresQuery, IReadOnlyList<AvailableGenreResponse>>
|
|
||||||
{
|
{
|
||||||
|
public record GetAvailableGenresQuery : IQuery<IReadOnlyList<AvailableGenreResponse>>;
|
||||||
|
|
||||||
|
public class GetAvailableGenresQueryHandler
|
||||||
|
: IQueryHandler<GetAvailableGenresQuery, IReadOnlyList<AvailableGenreResponse>>
|
||||||
|
{
|
||||||
public ValueTask<IReadOnlyList<AvailableGenreResponse>> Handle(GetAvailableGenresQuery request, CancellationToken cancellationToken)
|
public ValueTask<IReadOnlyList<AvailableGenreResponse>> Handle(GetAvailableGenresQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var all = Enum.GetValues<Genre>();
|
var all = Enum.GetValues<Genre>();
|
||||||
@@ -21,4 +21,5 @@ public class GetAvailableGenresQueryHandler
|
|||||||
|
|
||||||
return ValueTask.FromResult(result);
|
return ValueTask.FromResult(result);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,13 +4,13 @@ using Musify.Application.Contracts;
|
|||||||
using Musify.Application.Genres.Responses;
|
using Musify.Application.Genres.Responses;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Genres;
|
namespace Musify.Application.Genres
|
||||||
|
|
||||||
public record GetGenresQuery : IQuery<IReadOnlyList<GenreResponse>>;
|
|
||||||
|
|
||||||
public class GetGenresQueryHandler(IDatabase database)
|
|
||||||
: IQueryHandler<GetGenresQuery, IReadOnlyList<GenreResponse>>
|
|
||||||
{
|
{
|
||||||
|
public record GetGenresQuery : IQuery<IReadOnlyList<GenreResponse>>;
|
||||||
|
|
||||||
|
public class GetGenresQueryHandler(IDatabase database)
|
||||||
|
: IQueryHandler<GetGenresQuery, IReadOnlyList<GenreResponse>>
|
||||||
|
{
|
||||||
public async ValueTask<IReadOnlyList<GenreResponse>> Handle(GetGenresQuery request, CancellationToken cancellationToken)
|
public async ValueTask<IReadOnlyList<GenreResponse>> Handle(GetGenresQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var rows = await database.TrackTags
|
var rows = await database.TrackTags
|
||||||
@@ -26,4 +26,5 @@ public class GetGenresQueryHandler(IDatabase database)
|
|||||||
.Select(row => new GenreResponse(row.Genre, row.TrackCount))
|
.Select(row => new GenreResponse(row.Genre, row.TrackCount))
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Genres.Responses;
|
namespace Musify.Application.Genres.Responses
|
||||||
|
{
|
||||||
public record AvailableGenreResponse(Genre Genre, IReadOnlyList<Genre> IncompatibleWith);
|
public record AvailableGenreResponse(Genre Genre, IReadOnlyList<Genre> IncompatibleWith);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Genres.Responses;
|
namespace Musify.Application.Genres.Responses
|
||||||
|
{
|
||||||
public record GenreResponse(Genre Genre, int TrackCount);
|
public record GenreResponse(Genre Genre, int TrackCount);
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Application.Tracks.Responses;
|
using Musify.Application.Tracks.Responses;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Likes;
|
namespace Musify.Application.Likes
|
||||||
|
{
|
||||||
public record GetLikedTracksQuery(
|
public record GetLikedTracksQuery(
|
||||||
long UserId,
|
long UserId,
|
||||||
int PageNumber,
|
int PageNumber,
|
||||||
int PageSize)
|
int PageSize)
|
||||||
: IQuery<PaginatedResponse<TrackApplicationResponse>>;
|
: IQuery<PaginatedResponse<TrackApplicationResponse>>;
|
||||||
|
|
||||||
public class GetLikedTracksQueryHandler(IDatabase database)
|
public class GetLikedTracksQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetLikedTracksQuery, PaginatedResponse<TrackApplicationResponse>>
|
: IQueryHandler<GetLikedTracksQuery, PaginatedResponse<TrackApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<PaginatedResponse<TrackApplicationResponse>> Handle(GetLikedTracksQuery request, CancellationToken cancellationToken)
|
public async ValueTask<PaginatedResponse<TrackApplicationResponse>> Handle(GetLikedTracksQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return await database.TrackLikes
|
return await database.TrackLikes
|
||||||
@@ -27,4 +27,5 @@ public class GetLikedTracksQueryHandler(IDatabase database)
|
|||||||
.SelectResponse()
|
.SelectResponse()
|
||||||
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,16 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Likes;
|
namespace Musify.Application.Likes
|
||||||
|
{
|
||||||
public record ToggleTrackLikeCommand(long UserId, Guid TrackId)
|
public record ToggleTrackLikeCommand(long UserId, Guid TrackId)
|
||||||
: ICommand<ErrorOr<bool>>;
|
: ICommand<ErrorOr<bool>>;
|
||||||
|
|
||||||
public class ToggleTrackLikeCommandHandler(
|
public class ToggleTrackLikeCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
ILogger<ToggleTrackLikeCommandHandler> logger)
|
ILogger<ToggleTrackLikeCommandHandler> logger)
|
||||||
: ICommandHandler<ToggleTrackLikeCommand, ErrorOr<bool>>
|
: ICommandHandler<ToggleTrackLikeCommand, ErrorOr<bool>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<bool>> Handle(ToggleTrackLikeCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<bool>> Handle(ToggleTrackLikeCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var trackExists = await database.Tracks
|
var trackExists = await database.Tracks
|
||||||
@@ -55,4 +55,5 @@ public class ToggleTrackLikeCommandHandler(
|
|||||||
|
|
||||||
return nowLiked;
|
return nowLiked;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ using Musify.Application.Contracts;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Mixes;
|
namespace Musify.Application.Mixes
|
||||||
|
{
|
||||||
public record GenerateMixesForUserCommand(long UserId)
|
public record GenerateMixesForUserCommand(long UserId)
|
||||||
: ICommand;
|
: ICommand;
|
||||||
|
|
||||||
public class GenerateMixesForUserCommandHandler(
|
public class GenerateMixesForUserCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
MixConfiguration mixConfiguration,
|
MixConfiguration mixConfiguration,
|
||||||
ILogger<GenerateMixesForUserCommandHandler> logger)
|
ILogger<GenerateMixesForUserCommandHandler> logger)
|
||||||
: ICommandHandler<GenerateMixesForUserCommand>
|
: ICommandHandler<GenerateMixesForUserCommand>
|
||||||
{
|
{
|
||||||
private sealed record MixDraft(MixKind Kind, IReadOnlyList<Guid> TrackIds);
|
private sealed record MixDraft(MixKind Kind, IReadOnlyList<Guid> TrackIds);
|
||||||
|
|
||||||
public async ValueTask<Unit> Handle(GenerateMixesForUserCommand request, CancellationToken cancellationToken)
|
public async ValueTask<Unit> Handle(GenerateMixesForUserCommand request, CancellationToken cancellationToken)
|
||||||
@@ -127,4 +127,5 @@ public class GenerateMixesForUserCommandHandler(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ using Musify.Application.Contracts;
|
|||||||
using Musify.Application.Mixes.Responses;
|
using Musify.Application.Mixes.Responses;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.Mixes;
|
namespace Musify.Application.Mixes
|
||||||
|
{
|
||||||
public record GetMixByIdQuery(long UserId, Guid MixId)
|
public record GetMixByIdQuery(long UserId, Guid MixId)
|
||||||
: IQuery<ErrorOr<MixApplicationResponse>>;
|
: IQuery<ErrorOr<MixApplicationResponse>>;
|
||||||
|
|
||||||
public class GetMixByIdQueryHandler(IDatabase database)
|
public class GetMixByIdQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetMixByIdQuery, ErrorOr<MixApplicationResponse>>
|
: IQueryHandler<GetMixByIdQuery, ErrorOr<MixApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<MixApplicationResponse>> Handle(GetMixByIdQuery request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<MixApplicationResponse>> Handle(GetMixByIdQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var mix = await database.Mixes
|
var mix = await database.Mixes
|
||||||
@@ -28,4 +28,5 @@ public class GetMixByIdQueryHandler(IDatabase database)
|
|||||||
|
|
||||||
return new MixApplicationResponse(mix.Id, mix.Kind, items.Count, items);
|
return new MixApplicationResponse(mix.Id, mix.Kind, items.Count, items);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
using Musify.Application.Mixes.Responses;
|
using Musify.Application.Mixes.Responses;
|
||||||
|
|
||||||
namespace Musify.Application.Mixes;
|
namespace Musify.Application.Mixes
|
||||||
|
{
|
||||||
public record GetMixesByUserIdQuery(long UserId)
|
public record GetMixesByUserIdQuery(long UserId)
|
||||||
: IQuery<IReadOnlyList<MixApplicationResponse>>;
|
: IQuery<IReadOnlyList<MixApplicationResponse>>;
|
||||||
|
|
||||||
public class GetMixesByUserIdQueryHandler(IDatabase database)
|
public class GetMixesByUserIdQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetMixesByUserIdQuery, IReadOnlyList<MixApplicationResponse>>
|
: IQueryHandler<GetMixesByUserIdQuery, IReadOnlyList<MixApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<IReadOnlyList<MixApplicationResponse>> Handle(GetMixesByUserIdQuery request, CancellationToken cancellationToken)
|
public async ValueTask<IReadOnlyList<MixApplicationResponse>> Handle(GetMixesByUserIdQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var mixes = await database.Mixes
|
var mixes = await database.Mixes
|
||||||
@@ -47,4 +47,5 @@ public class GetMixesByUserIdQueryHandler(IDatabase database)
|
|||||||
|
|
||||||
return responses;
|
return responses;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ using Musify.Application.Mixes.Responses;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Mixes;
|
namespace Musify.Application.Mixes
|
||||||
|
|
||||||
|
|
||||||
internal static class MixItemMapper
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
internal static class MixItemMapper
|
||||||
|
{
|
||||||
public static async Task<List<MixItemApplicationResponse>> ToResponsesAsync(
|
public static async Task<List<MixItemApplicationResponse>> ToResponsesAsync(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
IReadOnlyList<MixItem> orderedItems,
|
IReadOnlyList<MixItem> orderedItems,
|
||||||
@@ -37,4 +37,5 @@ internal static class MixItemMapper
|
|||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Mixes.Responses;
|
namespace Musify.Application.Mixes.Responses
|
||||||
|
{
|
||||||
public record MixApplicationResponse(
|
public record MixApplicationResponse(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
MixKind Kind,
|
MixKind Kind,
|
||||||
int ItemCount,
|
int ItemCount,
|
||||||
IReadOnlyList<MixItemApplicationResponse> Items)
|
IReadOnlyList<MixItemApplicationResponse> Items)
|
||||||
{
|
{
|
||||||
public const int CoverItemCount = 4;
|
public const int CoverItemCount = 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
namespace Musify.Application.Mixes.Responses;
|
namespace Musify.Application.Mixes.Responses
|
||||||
|
{
|
||||||
public record MixItemApplicationResponse(
|
public record MixItemApplicationResponse(
|
||||||
Guid TrackId,
|
Guid TrackId,
|
||||||
string Title,
|
string Title,
|
||||||
string? Artist,
|
string? Artist,
|
||||||
double DurationSeconds,
|
double DurationSeconds,
|
||||||
int ListensCount);
|
int ListensCount);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,28 +7,28 @@ using Musify.Application.Contracts;
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Pictures;
|
namespace Musify.Application.Pictures
|
||||||
|
|
||||||
public enum CoverOwner
|
|
||||||
{
|
{
|
||||||
|
public enum CoverOwner
|
||||||
|
{
|
||||||
Track,
|
Track,
|
||||||
Album,
|
Album,
|
||||||
PlayList
|
PlayList
|
||||||
}
|
}
|
||||||
|
|
||||||
public record GetCoverQuery(CoverOwner Owner, Guid Id, PictureSize Size = PictureSize.Medium)
|
public record GetCoverQuery(CoverOwner Owner, Guid Id, PictureSize Size = PictureSize.Medium)
|
||||||
: IQuery<ErrorOr<CoverLocation>>;
|
: IQuery<ErrorOr<CoverLocation>>;
|
||||||
|
|
||||||
public record CoverLocation(string Bucket, string Key, string ContentType);
|
public record CoverLocation(string Bucket, string Key, string ContentType);
|
||||||
|
|
||||||
public class GetCoverQueryHandler(
|
public class GetCoverQueryHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
ApplicationStorageConfiguration storageConfiguration,
|
ApplicationStorageConfiguration storageConfiguration,
|
||||||
TrackConfiguration trackConfiguration,
|
TrackConfiguration trackConfiguration,
|
||||||
AlbumConfiguration albumConfiguration,
|
AlbumConfiguration albumConfiguration,
|
||||||
PlayListConfiguration playListConfiguration)
|
PlayListConfiguration playListConfiguration)
|
||||||
: IQueryHandler<GetCoverQuery, ErrorOr<CoverLocation>>
|
: IQueryHandler<GetCoverQuery, ErrorOr<CoverLocation>>
|
||||||
{
|
{
|
||||||
private sealed record CoverSource(long OwnerUserId, string? Original, string? Small, string? Medium, string? Large);
|
private sealed record CoverSource(long OwnerUserId, string? Original, string? Small, string? Medium, string? Large);
|
||||||
|
|
||||||
public async ValueTask<ErrorOr<CoverLocation>> Handle(GetCoverQuery request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<CoverLocation>> Handle(GetCoverQuery request, CancellationToken cancellationToken)
|
||||||
@@ -72,4 +72,5 @@ public class GetCoverQueryHandler(
|
|||||||
|
|
||||||
return new CoverLocation(storageConfiguration.Bucket, key, MimeUtility.GetMimeMapping(name));
|
return new CoverLocation(storageConfiguration.Bucket, key, MimeUtility.GetMimeMapping(name));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Pictures;
|
namespace Musify.Application.Pictures
|
||||||
|
|
||||||
/// <summary>What an album or playlist needs to switch to a freshly uploaded picture.</summary>
|
|
||||||
public sealed record PreparedPicture(UploadIntent Intent, EntityPictures Pictures, string FinalKey, ImageSizes Sizes);
|
|
||||||
|
|
||||||
public static class PictureSourceChange
|
|
||||||
{
|
{
|
||||||
|
/// <summary>What an album or playlist needs to switch to a freshly uploaded picture.</summary>
|
||||||
|
public sealed record PreparedPicture(UploadIntent Intent, EntityPictures Pictures, string FinalKey, ImageSizes Sizes);
|
||||||
|
|
||||||
|
public static class PictureSourceChange
|
||||||
|
{
|
||||||
public static async Task<ErrorOr<PreparedPicture>> PrepareAsync(
|
public static async Task<ErrorOr<PreparedPicture>> PrepareAsync(
|
||||||
UploadIntentValidator uploadIntentValidator,
|
UploadIntentValidator uploadIntentValidator,
|
||||||
Guid intentId,
|
Guid intentId,
|
||||||
@@ -32,4 +32,5 @@ public static class PictureSourceChange
|
|||||||
owner.Routes.BuildOriginalPicturePath(userId, intent.ObjectName),
|
owner.Routes.BuildOriginalPicturePath(userId, intent.ObjectName),
|
||||||
owner.PicturesSizes.ToImageSizes(owner.Routes));
|
owner.PicturesSizes.ToImageSizes(owner.Routes));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace Musify.Application.Pictures.Responses;
|
namespace Musify.Application.Pictures.Responses
|
||||||
|
{
|
||||||
public record PictureUploadResponse(
|
public record PictureUploadResponse(
|
||||||
Guid IntentId,
|
Guid IntentId,
|
||||||
string Bucket,
|
string Bucket,
|
||||||
string Key,
|
string Key,
|
||||||
@@ -8,3 +8,4 @@ public record PictureUploadResponse(
|
|||||||
string ContentType,
|
string ContentType,
|
||||||
int ExpiresInSeconds,
|
int ExpiresInSeconds,
|
||||||
string UploadUrl);
|
string UploadUrl);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,17 +7,17 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
/// <summary>Appends tracks to a playlist in the given order, skipping the ones it already has.</summary>
|
/// <summary>Appends tracks to a playlist in the given order, skipping the ones it already has.</summary>
|
||||||
public record AddTracksToPlayListCommand(long UserId, Guid PlayListId, IReadOnlyList<Guid> TrackIds)
|
public record AddTracksToPlayListCommand(long UserId, Guid PlayListId, IReadOnlyList<Guid> TrackIds)
|
||||||
: ICommand<ErrorOr<Success>>;
|
: ICommand<ErrorOr<Success>>;
|
||||||
|
|
||||||
public class AddTracksToPlayListCommandHandler(
|
public class AddTracksToPlayListCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
ILogger<AddTracksToPlayListCommandHandler> logger)
|
ILogger<AddTracksToPlayListCommandHandler> logger)
|
||||||
: ICommandHandler<AddTracksToPlayListCommand, ErrorOr<Success>>
|
: ICommandHandler<AddTracksToPlayListCommand, ErrorOr<Success>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<Success>> Handle(AddTracksToPlayListCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<Success>> Handle(AddTracksToPlayListCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var playList = await database.PlayLists.FindOwnedAsync(request.PlayListId, request.UserId, cancellationToken);
|
var playList = await database.PlayLists.FindOwnedAsync(request.PlayListId, request.UserId, cancellationToken);
|
||||||
@@ -70,4 +70,5 @@ public class AddTracksToPlayListCommandHandler(
|
|||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,9 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
public record CreatePlayListCommand(
|
public record CreatePlayListCommand(
|
||||||
long UserId,
|
long UserId,
|
||||||
string Name,
|
string Name,
|
||||||
string? Description,
|
string? Description,
|
||||||
@@ -22,14 +22,14 @@ public record CreatePlayListCommand(
|
|||||||
PlayListVisibility Visibility = PlayListVisibility.Private)
|
PlayListVisibility Visibility = PlayListVisibility.Private)
|
||||||
: ICommand<ErrorOr<PlayListApplicationResponse>>;
|
: ICommand<ErrorOr<PlayListApplicationResponse>>;
|
||||||
|
|
||||||
public class CreatePlayListCommandHandler(
|
public class CreatePlayListCommandHandler(
|
||||||
IEventBus eventBus,
|
IEventBus eventBus,
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
UploadIntentValidator uploadIntentValidator,
|
UploadIntentValidator uploadIntentValidator,
|
||||||
ILogger<CreatePlayListCommandHandler> logger,
|
ILogger<CreatePlayListCommandHandler> logger,
|
||||||
PlayListConfiguration playListConfiguration)
|
PlayListConfiguration playListConfiguration)
|
||||||
: ICommandHandler<CreatePlayListCommand, ErrorOr<PlayListApplicationResponse>>
|
: ICommandHandler<CreatePlayListCommand, ErrorOr<PlayListApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<PlayListApplicationResponse>> Handle(CreatePlayListCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<PlayListApplicationResponse>> Handle(CreatePlayListCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var userExists = await database.Users
|
var userExists = await database.Users
|
||||||
@@ -75,4 +75,5 @@ public class CreatePlayListCommandHandler(
|
|||||||
|
|
||||||
return PlayListApplicationResponse.FromEntity(playList);
|
return PlayListApplicationResponse.FromEntity(playList);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ using Musify.Application.Events;
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
public record DeletePlayListCommand(long UserId, Guid PlayListId)
|
public record DeletePlayListCommand(long UserId, Guid PlayListId)
|
||||||
: ICommand<ErrorOr<Success>>;
|
: ICommand<ErrorOr<Success>>;
|
||||||
|
|
||||||
public class DeletePlayListCommandHandler(
|
public class DeletePlayListCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
IEventBus eventBus,
|
IEventBus eventBus,
|
||||||
ILogger<DeletePlayListCommandHandler> logger)
|
ILogger<DeletePlayListCommandHandler> logger)
|
||||||
: ICommandHandler<DeletePlayListCommand, ErrorOr<Success>>
|
: ICommandHandler<DeletePlayListCommand, ErrorOr<Success>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<Success>> Handle(DeletePlayListCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<Success>> Handle(DeletePlayListCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var found = await database.PlayLists.FindOwnedAsync(request.PlayListId, request.UserId, cancellationToken);
|
var found = await database.PlayLists.FindOwnedAsync(request.PlayListId, request.UserId, cancellationToken);
|
||||||
@@ -33,4 +33,5 @@ public class DeletePlayListCommandHandler(
|
|||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ using Musify.Application.Contracts;
|
|||||||
using Musify.Application.PlayLists.Responses;
|
using Musify.Application.PlayLists.Responses;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
public record GetPlayListByIdQuery(Guid Id, long? RequestingUserId = null)
|
public record GetPlayListByIdQuery(Guid Id, long? RequestingUserId = null)
|
||||||
: IQuery<ErrorOr<PlayListApplicationResponse>>;
|
: IQuery<ErrorOr<PlayListApplicationResponse>>;
|
||||||
|
|
||||||
public class GetPlayListByIdQueryHandler(IDatabase database)
|
public class GetPlayListByIdQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetPlayListByIdQuery, ErrorOr<PlayListApplicationResponse>>
|
: IQueryHandler<GetPlayListByIdQuery, ErrorOr<PlayListApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<PlayListApplicationResponse>> Handle(GetPlayListByIdQuery request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<PlayListApplicationResponse>> Handle(GetPlayListByIdQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var playList = await database.PlayLists
|
var playList = await database.PlayLists
|
||||||
@@ -27,4 +27,5 @@ public class GetPlayListByIdQueryHandler(IDatabase database)
|
|||||||
? AppErrors.NotFound("PlayList", request.Id)
|
? AppErrors.NotFound("PlayList", request.Id)
|
||||||
: playList;
|
: playList;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,18 +7,18 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Application.Tracks.Responses;
|
using Musify.Application.Tracks.Responses;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
public record GetPlayListTracksQuery(
|
public record GetPlayListTracksQuery(
|
||||||
Guid PlayListId,
|
Guid PlayListId,
|
||||||
int PageNumber,
|
int PageNumber,
|
||||||
int PageSize,
|
int PageSize,
|
||||||
long? ViewerId = null)
|
long? ViewerId = null)
|
||||||
: IQuery<ErrorOr<PaginatedResponse<TrackApplicationResponse>>>;
|
: IQuery<ErrorOr<PaginatedResponse<TrackApplicationResponse>>>;
|
||||||
|
|
||||||
public class GetPlayListTracksQueryHandler(IDatabase database)
|
public class GetPlayListTracksQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetPlayListTracksQuery, ErrorOr<PaginatedResponse<TrackApplicationResponse>>>
|
: IQueryHandler<GetPlayListTracksQuery, ErrorOr<PaginatedResponse<TrackApplicationResponse>>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<PaginatedResponse<TrackApplicationResponse>>> Handle(GetPlayListTracksQuery request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<PaginatedResponse<TrackApplicationResponse>>> Handle(GetPlayListTracksQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var playListVisible = await database.PlayLists
|
var playListVisible = await database.PlayLists
|
||||||
@@ -38,4 +38,5 @@ public class GetPlayListTracksQueryHandler(IDatabase database)
|
|||||||
.SelectResponse()
|
.SelectResponse()
|
||||||
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ using Musify.Application.Contracts;
|
|||||||
using Musify.Application.PlayLists.Responses;
|
using Musify.Application.PlayLists.Responses;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
public record GetPlayListsByUserIdQuery(
|
public record GetPlayListsByUserIdQuery(
|
||||||
long UserId,
|
long UserId,
|
||||||
string? Name,
|
string? Name,
|
||||||
int PageNumber,
|
int PageNumber,
|
||||||
@@ -14,9 +14,9 @@ public record GetPlayListsByUserIdQuery(
|
|||||||
long? ViewerId = null)
|
long? ViewerId = null)
|
||||||
: IQuery<PaginatedResponse<PlayListApplicationResponse>>;
|
: IQuery<PaginatedResponse<PlayListApplicationResponse>>;
|
||||||
|
|
||||||
public class GetPlayListsByUserIdQueryHandler(IDatabase database)
|
public class GetPlayListsByUserIdQueryHandler(IDatabase database)
|
||||||
: IQueryHandler<GetPlayListsByUserIdQuery, PaginatedResponse<PlayListApplicationResponse>>
|
: IQueryHandler<GetPlayListsByUserIdQuery, PaginatedResponse<PlayListApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<PaginatedResponse<PlayListApplicationResponse>> Handle(GetPlayListsByUserIdQuery request, CancellationToken cancellationToken)
|
public async ValueTask<PaginatedResponse<PlayListApplicationResponse>> Handle(GetPlayListsByUserIdQuery request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var playListsQuery = database.PlayLists
|
var playListsQuery = database.PlayLists
|
||||||
@@ -37,4 +37,5 @@ public class GetPlayListsByUserIdQueryHandler(IDatabase database)
|
|||||||
.SelectResponse()
|
.SelectResponse()
|
||||||
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
.ToPaginatedAsync(new PageRequest(request.PageNumber, request.PageSize), cancellationToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ using Microsoft.Extensions.Logging;
|
|||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
public record RemoveTrackFromPlayListCommand(long UserId, Guid PlayListId, Guid TrackId)
|
public record RemoveTrackFromPlayListCommand(long UserId, Guid PlayListId, Guid TrackId)
|
||||||
: ICommand<ErrorOr<Success>>;
|
: ICommand<ErrorOr<Success>>;
|
||||||
|
|
||||||
public class RemoveTrackFromPlayListCommandHandler(
|
public class RemoveTrackFromPlayListCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
ILogger<RemoveTrackFromPlayListCommandHandler> logger)
|
ILogger<RemoveTrackFromPlayListCommandHandler> logger)
|
||||||
: ICommandHandler<RemoveTrackFromPlayListCommand, ErrorOr<Success>>
|
: ICommandHandler<RemoveTrackFromPlayListCommand, ErrorOr<Success>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<Success>> Handle(RemoveTrackFromPlayListCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<Success>> Handle(RemoveTrackFromPlayListCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var playList = await database.PlayLists.FindOwnedAsync(request.PlayListId, request.UserId, cancellationToken);
|
var playList = await database.PlayLists.FindOwnedAsync(request.PlayListId, request.UserId, cancellationToken);
|
||||||
@@ -42,4 +42,5 @@ public class RemoveTrackFromPlayListCommandHandler(
|
|||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,21 @@ using Musify.Application.Pictures.Responses;
|
|||||||
using Musify.Application.Services;
|
using Musify.Application.Services;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
public record RequestPlayListPictureUploadCommand(
|
public record RequestPlayListPictureUploadCommand(
|
||||||
long UserId,
|
long UserId,
|
||||||
string FileType,
|
string FileType,
|
||||||
string ContentType,
|
string ContentType,
|
||||||
long? ExpectedSizeBytes = null)
|
long? ExpectedSizeBytes = null)
|
||||||
: ICommand<ErrorOr<PictureUploadResponse>>;
|
: ICommand<ErrorOr<PictureUploadResponse>>;
|
||||||
|
|
||||||
public class RequestPlayListPictureUploadCommandHandler(UploadIntentIssuer issuer, PlayListConfiguration playListConfiguration)
|
public class RequestPlayListPictureUploadCommandHandler(UploadIntentIssuer issuer, PlayListConfiguration playListConfiguration)
|
||||||
: ICommandHandler<RequestPlayListPictureUploadCommand, ErrorOr<PictureUploadResponse>>
|
: ICommandHandler<RequestPlayListPictureUploadCommand, ErrorOr<PictureUploadResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<PictureUploadResponse>> Handle(RequestPlayListPictureUploadCommand request, CancellationToken cancellationToken) =>
|
public async ValueTask<ErrorOr<PictureUploadResponse>> Handle(RequestPlayListPictureUploadCommand request, CancellationToken cancellationToken) =>
|
||||||
await issuer.IssuePictureAsync(
|
await issuer.IssuePictureAsync(
|
||||||
request.UserId, UploadIntentPurpose.PlayListPicture, playListConfiguration,
|
request.UserId, UploadIntentPurpose.PlayListPicture, playListConfiguration,
|
||||||
request.FileType, request.ContentType, request.ExpectedSizeBytes, cancellationToken);
|
request.FileType, request.ContentType, request.ExpectedSizeBytes, cancellationToken);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ using Musify.Domain.Entities;
|
|||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists.Responses;
|
namespace Musify.Application.PlayLists.Responses
|
||||||
|
{
|
||||||
public record PlayListApplicationResponse(
|
public record PlayListApplicationResponse(
|
||||||
Guid Id,
|
Guid Id,
|
||||||
string Name,
|
string Name,
|
||||||
string? Description,
|
string? Description,
|
||||||
@@ -18,7 +18,7 @@ public record PlayListApplicationResponse(
|
|||||||
long OwnerUserId,
|
long OwnerUserId,
|
||||||
int TrackCount,
|
int TrackCount,
|
||||||
double DurationSeconds)
|
double DurationSeconds)
|
||||||
{
|
{
|
||||||
public const int CoverTrackCount = PlayListProjections.CoverTrackCount;
|
public const int CoverTrackCount = PlayListProjections.CoverTrackCount;
|
||||||
|
|
||||||
public static PlayListApplicationResponse FromEntity(
|
public static PlayListApplicationResponse FromEntity(
|
||||||
@@ -42,4 +42,5 @@ public record PlayListApplicationResponse(
|
|||||||
trackCount,
|
trackCount,
|
||||||
durationSeconds);
|
durationSeconds);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists.Responses;
|
namespace Musify.Application.PlayLists.Responses
|
||||||
|
|
||||||
public static class PlayListProjections
|
|
||||||
{
|
{
|
||||||
|
public static class PlayListProjections
|
||||||
|
{
|
||||||
public const int CoverTrackCount = 4;
|
public const int CoverTrackCount = 4;
|
||||||
|
|
||||||
public static IQueryable<PlayList> Active(this IQueryable<PlayList> query) =>
|
public static IQueryable<PlayList> Active(this IQueryable<PlayList> query) =>
|
||||||
@@ -34,4 +34,5 @@ public static class PlayListProjections
|
|||||||
playList.PlayListTracks
|
playList.PlayListTracks
|
||||||
.Where(playListTrack => playListTrack.Track.LifeCycleStatus == LifeCycleStatus.Active)
|
.Where(playListTrack => playListTrack.Track.LifeCycleStatus == LifeCycleStatus.Active)
|
||||||
.Sum(playListTrack => playListTrack.Track.DurationSeconds)));
|
.Sum(playListTrack => playListTrack.Track.DurationSeconds)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ using Musify.Application.Services;
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.PlayLists;
|
namespace Musify.Application.PlayLists
|
||||||
|
{
|
||||||
public record UpdatePlayListCommand(
|
public record UpdatePlayListCommand(
|
||||||
long UserId,
|
long UserId,
|
||||||
Guid PlayListId,
|
Guid PlayListId,
|
||||||
string? NewName,
|
string? NewName,
|
||||||
@@ -22,14 +22,14 @@ public record UpdatePlayListCommand(
|
|||||||
PlayListVisibility? NewVisibility = null)
|
PlayListVisibility? NewVisibility = null)
|
||||||
: ICommand<ErrorOr<PlayListApplicationResponse>>;
|
: ICommand<ErrorOr<PlayListApplicationResponse>>;
|
||||||
|
|
||||||
public class UpdatePlayListCommandHandler(
|
public class UpdatePlayListCommandHandler(
|
||||||
IEventBus eventBus,
|
IEventBus eventBus,
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
UploadIntentValidator uploadIntentValidator,
|
UploadIntentValidator uploadIntentValidator,
|
||||||
ILogger<UpdatePlayListCommandHandler> logger,
|
ILogger<UpdatePlayListCommandHandler> logger,
|
||||||
PlayListConfiguration playListConfiguration)
|
PlayListConfiguration playListConfiguration)
|
||||||
: ICommandHandler<UpdatePlayListCommand, ErrorOr<PlayListApplicationResponse>>
|
: ICommandHandler<UpdatePlayListCommand, ErrorOr<PlayListApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<PlayListApplicationResponse>> Handle(UpdatePlayListCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<PlayListApplicationResponse>> Handle(UpdatePlayListCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var found = await database.PlayLists.FindOwnedAsync(request.PlayListId, request.UserId, cancellationToken);
|
var found = await database.PlayLists.FindOwnedAsync(request.PlayListId, request.UserId, cancellationToken);
|
||||||
@@ -77,4 +77,5 @@ public class UpdatePlayListCommandHandler(
|
|||||||
.SelectResponse()
|
.SelectResponse()
|
||||||
.SingleAsync(cancellationToken);
|
.SingleAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,15 +2,15 @@ using Musify.Application.Configuration;
|
|||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
using Musify.Application.Tracks.Responses;
|
using Musify.Application.Tracks.Responses;
|
||||||
|
|
||||||
namespace Musify.Application.Services;
|
namespace Musify.Application.Services
|
||||||
|
{
|
||||||
/// <summary>Builds the stream URL and signed ticket for a processed track. It never touches the database.</summary>
|
/// <summary>Builds the stream URL and signed ticket for a processed track. It never touches the database.</summary>
|
||||||
public sealed class TrackStreamIssuer(
|
public sealed class TrackStreamIssuer(
|
||||||
IStreamTicketService ticketService,
|
IStreamTicketService ticketService,
|
||||||
TrackConfiguration trackConfiguration,
|
TrackConfiguration trackConfiguration,
|
||||||
StreamGatewayConfiguration streamGatewayConfiguration,
|
StreamGatewayConfiguration streamGatewayConfiguration,
|
||||||
PlaybackConfiguration playbackConfiguration)
|
PlaybackConfiguration playbackConfiguration)
|
||||||
{
|
{
|
||||||
public TrackStreamResponse Issue(string audioFolderName, long? userId, Guid? listenId)
|
public TrackStreamResponse Issue(string audioFolderName, long? userId, Guid? listenId)
|
||||||
{
|
{
|
||||||
var folderPath = trackConfiguration.Routes.BuildProcessedAudioPath(audioFolderName);
|
var folderPath = trackConfiguration.Routes.BuildProcessedAudioPath(audioFolderName);
|
||||||
@@ -32,4 +32,5 @@ public sealed class TrackStreamIssuer(
|
|||||||
|
|
||||||
return (long)playbackConfiguration.AnonymousFragmentSeconds * playbackConfiguration.EstimatedAudioBytesPerSecond;
|
return (long)playbackConfiguration.AnonymousFragmentSeconds * playbackConfiguration.EstimatedAudioBytesPerSecond;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,31 +8,31 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Services;
|
namespace Musify.Application.Services
|
||||||
|
{
|
||||||
public sealed record UploadRequest(
|
public sealed record UploadRequest(
|
||||||
UploadIntentPurpose Purpose,
|
UploadIntentPurpose Purpose,
|
||||||
PictureRoutes Routes,
|
PictureRoutes Routes,
|
||||||
string FileType,
|
string FileType,
|
||||||
string ContentType,
|
string ContentType,
|
||||||
long? ExpectedSizeBytes);
|
long? ExpectedSizeBytes);
|
||||||
|
|
||||||
public sealed record IssuedUpload(
|
public sealed record IssuedUpload(
|
||||||
Guid IntentId,
|
Guid IntentId,
|
||||||
string Key,
|
string Key,
|
||||||
string ObjectName,
|
string ObjectName,
|
||||||
string ContentType,
|
string ContentType,
|
||||||
string UploadUrl);
|
string UploadUrl);
|
||||||
|
|
||||||
/// <summary>Issues pre-signed upload URLs and the upload intents that track them, checking the user quota first.</summary>
|
/// <summary>Issues pre-signed upload URLs and the upload intents that track them, checking the user quota first.</summary>
|
||||||
public sealed class UploadIntentIssuer(
|
public sealed class UploadIntentIssuer(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
IStorageService storageService,
|
IStorageService storageService,
|
||||||
UploadIntentValidator uploadIntentValidator,
|
UploadIntentValidator uploadIntentValidator,
|
||||||
ApplicationStorageConfiguration storageConfiguration,
|
ApplicationStorageConfiguration storageConfiguration,
|
||||||
UploadIntentConfiguration uploadIntentConfiguration,
|
UploadIntentConfiguration uploadIntentConfiguration,
|
||||||
ILogger<UploadIntentIssuer> logger)
|
ILogger<UploadIntentIssuer> logger)
|
||||||
{
|
{
|
||||||
public async Task<ErrorOr<IReadOnlyList<IssuedUpload>>> IssueAsync(
|
public async Task<ErrorOr<IReadOnlyList<IssuedUpload>>> IssueAsync(
|
||||||
long userId,
|
long userId,
|
||||||
IReadOnlyList<UploadRequest> requests,
|
IReadOnlyList<UploadRequest> requests,
|
||||||
@@ -130,4 +130,5 @@ public sealed class UploadIntentIssuer(
|
|||||||
purpose == UploadIntentPurpose.TrackAudio
|
purpose == UploadIntentPurpose.TrackAudio
|
||||||
? uploadIntentConfiguration.DefaultExpectedAudioSizeBytes
|
? uploadIntentConfiguration.DefaultExpectedAudioSizeBytes
|
||||||
: uploadIntentConfiguration.DefaultExpectedPictureSizeBytes;
|
: uploadIntentConfiguration.DefaultExpectedPictureSizeBytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ using Musify.Application.Shared;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Services;
|
namespace Musify.Application.Services
|
||||||
|
{
|
||||||
public sealed class UploadIntentValidator(
|
public sealed class UploadIntentValidator(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
IStorageService storageService,
|
IStorageService storageService,
|
||||||
UploadIntentConfiguration config)
|
UploadIntentConfiguration config)
|
||||||
{
|
{
|
||||||
public async Task<ErrorOr<Success>> CheckQuotaAsync(
|
public async Task<ErrorOr<Success>> CheckQuotaAsync(
|
||||||
long userId,
|
long userId,
|
||||||
long requiredBytes,
|
long requiredBytes,
|
||||||
@@ -74,4 +74,5 @@ public sealed class UploadIntentValidator(
|
|||||||
|
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
using ErrorOr;
|
using ErrorOr;
|
||||||
|
|
||||||
namespace Musify.Application.Shared;
|
namespace Musify.Application.Shared
|
||||||
|
|
||||||
public static class AppErrors
|
|
||||||
{
|
{
|
||||||
|
public static class AppErrors
|
||||||
|
{
|
||||||
public static Error NotFound(string entity, object id) =>
|
public static Error NotFound(string entity, object id) =>
|
||||||
Error.NotFound($"{entity}.NotFound", $"{entity} {id} was not found.");
|
Error.NotFound($"{entity}.NotFound", $"{entity} {id} was not found.");
|
||||||
|
|
||||||
@@ -12,4 +12,5 @@ public static class AppErrors
|
|||||||
|
|
||||||
public static Error Conflict(string code, string description) =>
|
public static Error Conflict(string code, string description) =>
|
||||||
Error.Conflict(code, description);
|
Error.Conflict(code, description);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
namespace Musify.Application.Shared;
|
namespace Musify.Application.Shared
|
||||||
|
{
|
||||||
public record ImageSize(
|
public record ImageSize(
|
||||||
string SavePath,
|
string SavePath,
|
||||||
int Width,
|
int Width,
|
||||||
int Height);
|
int Height);
|
||||||
|
|
||||||
public record ImageSizes(
|
public record ImageSizes(
|
||||||
ImageSize Small,
|
ImageSize Small,
|
||||||
ImageSize Medium,
|
ImageSize Medium,
|
||||||
ImageSize Large);
|
ImageSize Large);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Musify.Domain.Abstractions;
|
using Musify.Domain.Abstractions;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Shared;
|
namespace Musify.Application.Shared
|
||||||
|
|
||||||
public static class OwnershipExtensions
|
|
||||||
{
|
{
|
||||||
|
public static class OwnershipExtensions
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads an active, tracked entity owned by <paramref name="userId"/>: NotFound when it is missing
|
/// Loads an active, tracked entity owned by <paramref name="userId"/>: NotFound when it is missing
|
||||||
/// or not active, Forbidden when someone else owns it.
|
/// or not active, Forbidden when someone else owns it.
|
||||||
@@ -25,4 +25,5 @@ public static class OwnershipExtensions
|
|||||||
? entity
|
? entity
|
||||||
: AppErrors.Forbidden(typeof(T).Name, id);
|
: AppErrors.Forbidden(typeof(T).Name, id);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
|
||||||
namespace Musify.Application.Shared;
|
namespace Musify.Application.Shared
|
||||||
|
|
||||||
public record PageRequest(int PageNumber = 1, int PageSize = 20)
|
|
||||||
{
|
{
|
||||||
|
public record PageRequest(int PageNumber = 1, int PageSize = 20)
|
||||||
|
{
|
||||||
public const int MaxPageSize = 100;
|
public const int MaxPageSize = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
public record PaginatedResponse<T>(
|
public record PaginatedResponse<T>(
|
||||||
IReadOnlyList<T> Items,
|
IReadOnlyList<T> Items,
|
||||||
int PageNumber,
|
int PageNumber,
|
||||||
int PageSize,
|
int PageSize,
|
||||||
@@ -16,8 +16,8 @@ public record PaginatedResponse<T>(
|
|||||||
bool HasNextPage,
|
bool HasNextPage,
|
||||||
bool HasPreviousPage);
|
bool HasPreviousPage);
|
||||||
|
|
||||||
public static class PaginationExtensions
|
public static class PaginationExtensions
|
||||||
{
|
{
|
||||||
/// <summary>Counts and pages the same (already filtered, ordered and projected) query.</summary>
|
/// <summary>Counts and pages the same (already filtered, ordered and projected) query.</summary>
|
||||||
public static async Task<PaginatedResponse<T>> ToPaginatedAsync<T>(
|
public static async Task<PaginatedResponse<T>> ToPaginatedAsync<T>(
|
||||||
this IQueryable<T> query, PageRequest page, CancellationToken cancellationToken)
|
this IQueryable<T> query, PageRequest page, CancellationToken cancellationToken)
|
||||||
@@ -42,4 +42,5 @@ public static class PaginationExtensions
|
|||||||
HasNextPage: pageNumber < pageCount,
|
HasNextPage: pageNumber < pageCount,
|
||||||
HasPreviousPage: pageNumber > 1 && pageCount > 0);
|
HasPreviousPage: pageNumber > 1 && pageCount > 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
namespace Musify.Application.Shared;
|
namespace Musify.Application.Shared
|
||||||
|
|
||||||
public static class StorageKey
|
|
||||||
{
|
{
|
||||||
|
public static class StorageKey
|
||||||
|
{
|
||||||
public static string Combine(params string[] segments)
|
public static string Combine(params string[] segments)
|
||||||
{
|
{
|
||||||
return string.Join('/', segments
|
return string.Join('/', segments
|
||||||
.Where(static s => !string.IsNullOrWhiteSpace(s))
|
.Where(static s => !string.IsNullOrWhiteSpace(s))
|
||||||
.Select(static s => s.Trim().Trim('/', '\\')));
|
.Select(static s => s.Trim().Trim('/', '\\')));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace Musify.Application.Shared;
|
namespace Musify.Application.Shared
|
||||||
|
|
||||||
public static class TextNormalizer
|
|
||||||
{
|
{
|
||||||
|
public static class TextNormalizer
|
||||||
|
{
|
||||||
public static string Normalize(string value) => value.Trim().ToUpperInvariant();
|
public static string Normalize(string value) => value.Trim().ToUpperInvariant();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ using Musify.Application.Tracks.Responses;
|
|||||||
using Musify.Domain.Entities;
|
using Musify.Domain.Entities;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Tracks;
|
namespace Musify.Application.Tracks
|
||||||
|
{
|
||||||
public record CreateTrackCommand(
|
public record CreateTrackCommand(
|
||||||
long UserId,
|
long UserId,
|
||||||
string Title,
|
string Title,
|
||||||
Guid PictureIntentId,
|
Guid PictureIntentId,
|
||||||
@@ -22,14 +22,14 @@ public record CreateTrackCommand(
|
|||||||
bool IsExplicit = false)
|
bool IsExplicit = false)
|
||||||
: ICommand<ErrorOr<TrackApplicationResponse>>;
|
: ICommand<ErrorOr<TrackApplicationResponse>>;
|
||||||
|
|
||||||
public class CreateTrackCommandHandler(
|
public class CreateTrackCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
IEventBus eventBus,
|
IEventBus eventBus,
|
||||||
UploadIntentValidator uploadIntentValidator,
|
UploadIntentValidator uploadIntentValidator,
|
||||||
ILogger<CreateTrackCommandHandler> logger,
|
ILogger<CreateTrackCommandHandler> logger,
|
||||||
TrackConfiguration trackConfiguration)
|
TrackConfiguration trackConfiguration)
|
||||||
: ICommandHandler<CreateTrackCommand, ErrorOr<TrackApplicationResponse>>
|
: ICommandHandler<CreateTrackCommand, ErrorOr<TrackApplicationResponse>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<TrackApplicationResponse>> Handle(CreateTrackCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<TrackApplicationResponse>> Handle(CreateTrackCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var user = await database.Users
|
var user = await database.Users
|
||||||
@@ -110,4 +110,5 @@ public class CreateTrackCommandHandler(
|
|||||||
|
|
||||||
return TrackApplicationResponse.FromEntity(trackEntity, listensCount: 0);
|
return TrackApplicationResponse.FromEntity(trackEntity, listensCount: 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ using Microsoft.EntityFrameworkCore;
|
|||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Musify.Application.Contracts;
|
using Musify.Application.Contracts;
|
||||||
|
|
||||||
namespace Musify.Application.Tracks;
|
namespace Musify.Application.Tracks
|
||||||
|
{
|
||||||
|
public record DeleteStaleUncountedListensCommand : ICommand<int>;
|
||||||
|
|
||||||
public record DeleteStaleUncountedListensCommand : ICommand<int>;
|
public class DeleteStaleUncountedListensCommandHandler(
|
||||||
|
|
||||||
public class DeleteStaleUncountedListensCommandHandler(
|
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
ILogger<DeleteStaleUncountedListensCommandHandler> logger)
|
ILogger<DeleteStaleUncountedListensCommandHandler> logger)
|
||||||
: ICommandHandler<DeleteStaleUncountedListensCommand, int>
|
: ICommandHandler<DeleteStaleUncountedListensCommand, int>
|
||||||
{
|
{
|
||||||
public async ValueTask<int> Handle(DeleteStaleUncountedListensCommand request, CancellationToken cancellationToken)
|
public async ValueTask<int> Handle(DeleteStaleUncountedListensCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var cutoff = DateTime.UtcNow.AddDays(-ListeningRules.UncountedRetentionDays);
|
var cutoff = DateTime.UtcNow.AddDays(-ListeningRules.UncountedRetentionDays);
|
||||||
@@ -29,4 +29,5 @@ public class DeleteStaleUncountedListensCommandHandler(
|
|||||||
logger.LogInformation("Deleted {Count} uncounted listens older than {Days} days", stale.Count, ListeningRules.UncountedRetentionDays);
|
logger.LogInformation("Deleted {Count} uncounted listens older than {Days} days", stale.Count, ListeningRules.UncountedRetentionDays);
|
||||||
return stale.Count;
|
return stale.Count;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,19 +6,19 @@ using Musify.Application.Events;
|
|||||||
using Musify.Application.Shared;
|
using Musify.Application.Shared;
|
||||||
using Musify.Domain.ValueObjects;
|
using Musify.Domain.ValueObjects;
|
||||||
|
|
||||||
namespace Musify.Application.Tracks;
|
namespace Musify.Application.Tracks
|
||||||
|
{
|
||||||
public record DeleteTrackCommand(
|
public record DeleteTrackCommand(
|
||||||
long UserId,
|
long UserId,
|
||||||
Guid TrackId)
|
Guid TrackId)
|
||||||
: ICommand<ErrorOr<Success>>;
|
: ICommand<ErrorOr<Success>>;
|
||||||
|
|
||||||
public class DeleteTrackCommandHandler(
|
public class DeleteTrackCommandHandler(
|
||||||
IDatabase database,
|
IDatabase database,
|
||||||
IEventBus eventBus,
|
IEventBus eventBus,
|
||||||
ILogger<DeleteTrackCommandHandler> logger)
|
ILogger<DeleteTrackCommandHandler> logger)
|
||||||
: ICommandHandler<DeleteTrackCommand, ErrorOr<Success>>
|
: ICommandHandler<DeleteTrackCommand, ErrorOr<Success>>
|
||||||
{
|
{
|
||||||
public async ValueTask<ErrorOr<Success>> Handle(DeleteTrackCommand request, CancellationToken cancellationToken)
|
public async ValueTask<ErrorOr<Success>> Handle(DeleteTrackCommand request, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var found = await database.Tracks.FindOwnedAsync(request.TrackId, request.UserId, cancellationToken);
|
var found = await database.Tracks.FindOwnedAsync(request.TrackId, request.UserId, cancellationToken);
|
||||||
@@ -38,4 +38,5 @@ public class DeleteTrackCommandHandler(
|
|||||||
|
|
||||||
return Result.Success;
|
return Result.Success;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user