Separate responsibilities: start-listening command, API-side JSON converters, bulk track endpoints, mix kinds

This commit is contained in:
2026-09-24 12:14:53 +02:00
parent 75d0c0817e
commit 0fa033faff
64 changed files with 2903 additions and 651 deletions
@@ -18,16 +18,16 @@ public class GenerateMixesForUserCommandHandler(
ILogger<GenerateMixesForUserCommandHandler> logger)
: ICommandHandler<GenerateMixesForUserCommand, ErrorOr<Success>>
{
private sealed record MixDraft(string Title, string? Subtitle, IReadOnlyList<Guid> TrackIds);
private sealed record MixDraft(MixKind Kind, IReadOnlyList<Guid> TrackIds);
public async ValueTask<ErrorOr<Success>> Handle(GenerateMixesForUserCommand request, CancellationToken cancellationToken)
{
var userId = request.UserId;
var libraryTrackIds = await database.UserHasTracks
var libraryTrackIds = await database.Tracks
.AsNoTracking()
.Where(userTrack => userTrack.UserId == userId && userTrack.Track.LifeCycleStatus == LifeCycleStatus.Active)
.Select(userTrack => userTrack.TrackId)
.Where(track => track.OwnerUserId == userId && track.LifeCycleStatus == LifeCycleStatus.Active)
.Select(track => track.Id)
.ToListAsync(cancellationToken);
if (libraryTrackIds.Count == 0)
@@ -51,19 +51,13 @@ public class GenerateMixesForUserCommandHandler(
var discovery = Take(Shuffle(unheardTrackIds));
if (discovery.Count > 0)
{
drafts.Add(new MixDraft(
"Descubrimiento",
"Canciones de tu biblioteca que todavía no has escuchado.",
discovery));
drafts.Add(new MixDraft(MixKind.Discovery, discovery));
}
var daily = Take(Interleave(Shuffle(historyTrackIds), Shuffle(unheardTrackIds)));
if (daily.Count > 0)
{
drafts.Add(new MixDraft(
"Tu mezcla diaria",
"Lo que más escuchas, con alguna sorpresa de tu biblioteca.",
daily));
drafts.Add(new MixDraft(MixKind.Daily, daily));
}
if (drafts.Count == 0)
@@ -108,12 +102,6 @@ public class GenerateMixesForUserCommandHandler(
if (previousMixes.Count > 0)
{
var previousIds = previousMixes.Select(mix => mix.Id).ToList();
var previousItems = await database.MixItems
.Where(item => previousIds.Contains(item.MixId))
.ToListAsync(cancellationToken);
database.MixItems.RemoveRange(previousItems);
database.Mixes.RemoveRange(previousMixes);
}
@@ -123,8 +111,7 @@ public class GenerateMixesForUserCommandHandler(
var mix = new Mix
{
UserId = userId,
Title = draft.Title,
Subtitle = draft.Subtitle,
Kind = draft.Kind,
Position = position
};
@@ -26,6 +26,6 @@ public class GetMixByIdQueryHandler(IDatabase database)
var orderedItems = mix.Items.OrderBy(item => item.Position).ToList();
var items = await MixItemMapper.ToResponsesAsync(database, orderedItems, cancellationToken);
return new MixApplicationResponse(mix.Id, mix.Title, mix.Subtitle, items.Count, items);
return new MixApplicationResponse(mix.Id, mix.Kind, items.Count, items);
}
}
@@ -42,7 +42,7 @@ public class GetMixesByUserIdQueryHandler(IDatabase database)
.Select(item => coverResponses[item.TrackId])
.ToList();
responses.Add(new MixApplicationResponse(mix.Id, mix.Title, mix.Subtitle, orderedItems.Count, covers));
responses.Add(new MixApplicationResponse(mix.Id, mix.Kind, orderedItems.Count, covers));
}
return responses;
@@ -1,9 +1,10 @@
using Musify.Domain.ValueObjects;
namespace Musify.Application.Mixes.Responses;
public record MixApplicationResponse(
Guid Id,
string Title,
string? Subtitle,
MixKind Kind,
int ItemCount,
IReadOnlyList<MixItemApplicationResponse> Items)
{