Generalize YouTube-backed endpoints behind a source parameter
Removes the last YouTube-specific routes (/youtube/*) and merges them
into the general track/album/playlist endpoints, keyed by a `source`
string validated against TrackSource. This mirrors the earlier track
search merge for albums and playlist-track additions:
- GetAlbums now merges local albums with live YouTube Music search
results, paginated via a youtubeContinuationToken, deduped against
already-materialized ExternalAlbum rows.
- New GET /albums/external/{source}/{externalId} and
GET /tracks/external/{source}/{externalId}/stream replace the
YouTube-only equivalents.
- POST /playlists/{id}/tracks replaces the separate
.../youtube-tracks endpoint, accepting an optional Source/ExternalId
pair alongside TrackId.
- AlbumSearchItemResponse/AlbumsSearchResponse follow the same
discriminated-union + trailing-HasNextPage-field pattern already
used for track search, so HasNextPage stays a required field in the
generated OpenAPI schema.
- Frontend routes renamed accordingly (/albums/youtube/[albumId] ->
/albums/external/[source]/[externalId], etc.) with a single
/api/thumbnail proxy replacing /api/youtube/thumbnail.
Musify.Api.Endpoints.YouTubeEndpoints and the standalone
SearchYouTubeAlbums query are removed as a result.
This commit is contained in:
@@ -3,6 +3,9 @@ using Musify.Application.Tracks;
|
||||
using Musify.Application.Tracks.Responses;
|
||||
using Musify.Application.Shared;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.YouTube;
|
||||
using Musify.Application.YouTube.Responses;
|
||||
using Musify.Domain.ValueObjects;
|
||||
using Musify.Api.Authentication;
|
||||
using Musify.Api.DataTransferObjects.Tracks;
|
||||
using Musify.Api.Extensions;
|
||||
@@ -41,6 +44,14 @@ public static class TrackEndpoints
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
group.MapGet("/external/{source}/{externalId}/stream", GetExternalTrackStream)
|
||||
.WithName("GetExternalTrackStream")
|
||||
.WithSummary("Resolve Playback For A Track From An External Source (Currently Only YouTube Music): Server Stream If Downloaded, Direct Stream From The Source Otherwise. Works Anonymously When The Playback Configuration Allows It.")
|
||||
.Produces<YouTubeStreamResponse>()
|
||||
.Produces(StatusCodes.Status401Unauthorized)
|
||||
.Produces(StatusCodes.Status404NotFound)
|
||||
.Produces(StatusCodes.Status409Conflict);
|
||||
|
||||
group.MapGet("/users/{userId}", GetTracksByUserId)
|
||||
.WithName("GetTracksByUserId")
|
||||
.WithSummary("Get Paginated Tracks For A User.")
|
||||
@@ -129,6 +140,20 @@ public static class TrackEndpoints
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetExternalTrackStream(
|
||||
IMediator mediator,
|
||||
CurrentUser currentUser,
|
||||
string source,
|
||||
string externalId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!Enum.TryParse<TrackSource>(source, ignoreCase: true, out var trackSource) || trackSource != TrackSource.YouTube)
|
||||
return Results.NotFound($"Unknown or unsupported source '{source}'.");
|
||||
|
||||
var result = await mediator.Send(new ResolveYouTubeTrackStreamCommand(externalId, currentUser.Id), cancellationToken);
|
||||
return result.ToHttpResult();
|
||||
}
|
||||
|
||||
private static async Task<IResult> GetTracksByUserId(
|
||||
IMediator mediator,
|
||||
long userId,
|
||||
|
||||
Reference in New Issue
Block a user