feat(backend): auto-download YouTube tracks saved to playlists

This commit is contained in:
2026-07-16 01:06:04 +02:00
parent 11ed8b40ff
commit 73826b9d08
18 changed files with 672 additions and 1 deletions
@@ -0,0 +1,8 @@
namespace Musify.Api.DataTransferObjects.PlayLists;
public record AddYouTubeTrackRequest(
string VideoId,
string Title,
string Artist,
int DurationSeconds,
string ThumbnailUrl);
@@ -93,6 +93,17 @@ public static class PlayListEndpoints
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status409Conflict);
group.MapPost("/{playlistId}/youtube-tracks", AddYouTubeTrackToPlayList)
.WithName("AddYouTubeTrackToPlayList")
.WithSummary("Add A YouTube Track To A PlayList, Downloading It In The Background If Needed.")
.AddEndpointFilter<ValidationFilter<AddYouTubeTrackRequest>>()
.RequireAuthorization()
.Produces<TrackApplicationResponse>(StatusCodes.Status201Created)
.ProducesValidationProblem()
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound)
.Produces(StatusCodes.Status409Conflict);
group.MapDelete("/{playlistId}/tracks/{trackId}", RemoveTrackFromPlayList)
.WithName("RemoveTrackFromPlayList")
.WithSummary("Remove A Track From A PlayList.")
@@ -232,6 +243,27 @@ public static class PlayListEndpoints
return result.ToHttpResult();
}
private static async Task<IResult> AddYouTubeTrackToPlayList(
IMediator mediator,
CurrentUser currentUser,
Guid playlistId,
AddYouTubeTrackRequest request,
CancellationToken cancellationToken)
{
var result = await mediator.Send(
new AddYouTubeTrackToPlayListCommand(
currentUser.RequiredId,
playlistId,
request.VideoId,
request.Title,
request.Artist,
request.DurationSeconds,
request.ThumbnailUrl),
cancellationToken);
return result.ToCreatedResult(track => $"/tracks/{track.Id}");
}
private static async Task<IResult> RemoveTrackFromPlayList(
IMediator mediator,
CurrentUser currentUser,
@@ -0,0 +1,29 @@
using FluentValidation;
using Musify.Api.DataTransferObjects.PlayLists;
namespace Musify.Api.Validators.PlayLists;
public sealed class AddYouTubeTrackRequestValidator : AbstractValidator<AddYouTubeTrackRequest>
{
public AddYouTubeTrackRequestValidator()
{
RuleFor(x => x.VideoId)
.NotEmpty()
.Matches("^[A-Za-z0-9_-]{11}$");
RuleFor(x => x.Title)
.NotEmpty()
.MaximumLength(200);
RuleFor(x => x.Artist)
.MaximumLength(400);
RuleFor(x => x.DurationSeconds)
.GreaterThan(0);
RuleFor(x => x.ThumbnailUrl)
.NotEmpty()
.Must(url => Uri.TryCreate(url, UriKind.Absolute, out var uri) && uri.Scheme == Uri.UriSchemeHttps)
.WithMessage("ThumbnailUrl must be an absolute https URL.");
}
}