feat(backend): YouTube Music search and playback resolution endpoints

This commit is contained in:
2026-07-16 01:05:42 +02:00
parent 2cecd118ed
commit 11ed8b40ff
20 changed files with 506 additions and 27 deletions
@@ -47,6 +47,9 @@
"OpenTelemetry": {
"OtlpEndpoint": "http://localhost:4317"
},
"YouTube": {
"GeographicalLocation": "ES"
},
"Workers": {
"Routes": {
"TemporaryFilesDirectory": "D:\\tempsFilesDev"
+5
View File
@@ -48,6 +48,11 @@
"OpenTelemetry": {
"OtlpEndpoint": ""
},
"YouTube": {
"GeographicalLocation": "US",
"StreamUrlCacheSeconds": 1800,
"SearchContinuationCacheSeconds": 600
},
"PlayList": {
"Routes": {
"UploadsFolder": "uploads",
@@ -0,0 +1,61 @@
using Mediator;
using Musify.Api.Authentication;
using Musify.Api.Extensions;
using Musify.Application.Contracts;
using Musify.Application.YouTube;
using Musify.Application.YouTube.Responses;
namespace Musify.Api.Endpoints;
public static class YouTubeEndpoints
{
public static IEndpointRouteBuilder MapYouTubeEndpoints(this IEndpointRouteBuilder app)
{
var group = app.MapGroup("/youtube")
.WithTags("YouTube");
group.MapGet("/search", SearchTracks)
.WithName("SearchYouTubeTracks")
.WithSummary("Search Songs On YouTube Music.")
.RequireAuthorization()
.Produces<YouTubeSearchResult>()
.Produces(StatusCodes.Status401Unauthorized)
.ProducesValidationProblem();
group.MapGet("/tracks/{videoId}/stream", GetTrackStream)
.WithName("GetYouTubeTrackStream")
.WithSummary("Resolve Playback For A YouTube Track: Server Stream If Downloaded, Direct YouTube Stream Otherwise.")
.RequireAuthorization()
.Produces<YouTubeStreamResponse>()
.Produces(StatusCodes.Status401Unauthorized)
.Produces(StatusCodes.Status404NotFound);
return app;
}
private static async Task<IResult> GetTrackStream(
IMediator mediator,
CurrentUser currentUser,
string videoId,
CancellationToken cancellationToken)
{
var result = await mediator.Send(
new GetYouTubeTrackStreamQuery(videoId, currentUser.RequiredId),
cancellationToken);
return result.ToHttpResult();
}
private static async Task<IResult> SearchTracks(
IMediator mediator,
CancellationToken cancellationToken,
string query,
string? continuation = null)
{
var result = await mediator.Send(
new SearchYouTubeTracksQuery(query, continuation ?? string.Empty),
cancellationToken);
return result.ToHttpResult();
}
}
@@ -18,6 +18,7 @@ public static class ServiceCollectionExtensions
services.AddDatabase(configuration);
services.AddStorageService(configuration);
services.AddStreamTicketService(configuration);
services.AddYouTubeMusicService(configuration);
services.AddMassTransitClient(configuration);
services.AddAuthenticationConfiguration(configuration);
services.AddOpenApiConfiguration();
+6 -1
View File
@@ -1,3 +1,4 @@
using System.Text.Json.Serialization;
using Musify.Infrastructure.Observability;
using Scalar.AspNetCore;
using Musify.Api.Endpoints;
@@ -5,6 +6,9 @@ using Musify.Api.Extensions;
var builder = WebApplication.CreateBuilder();
builder.Services.ConfigureHttpJsonOptions(options =>
options.SerializerOptions.Converters.Add(new JsonStringEnumConverter()));
const string devCorsPolicy = "dev-cors";
builder.Services
@@ -36,6 +40,7 @@ app.UseAuthorization();
app
.MapPlayListEndpoints()
.MapTrackEndpoints()
.MapUserEndpoints();
.MapUserEndpoints()
.MapYouTubeEndpoints();
app.Run();