feat(backend): YouTube Music search and playback resolution endpoints
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
using ErrorOr;
|
||||
using Mediator;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Musify.Application.Contracts;
|
||||
using Musify.Application.Services;
|
||||
using Musify.Application.YouTube.Responses;
|
||||
using Musify.Domain.ValueObjects;
|
||||
|
||||
namespace Musify.Application.YouTube
|
||||
{
|
||||
public record GetYouTubeTrackStreamQuery(string VideoId, long UserId)
|
||||
: IQuery<ErrorOr<YouTubeStreamResponse>>;
|
||||
|
||||
public class GetYouTubeTrackStreamQueryHandler(
|
||||
IDatabase database,
|
||||
IYouTubeMusicService youTubeMusicService,
|
||||
TrackStreamIssuer streamIssuer)
|
||||
: IQueryHandler<GetYouTubeTrackStreamQuery, ErrorOr<YouTubeStreamResponse>>
|
||||
{
|
||||
public async ValueTask<ErrorOr<YouTubeStreamResponse>> Handle(GetYouTubeTrackStreamQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
var track = await database.Tracks.AsNoTracking()
|
||||
.Where(t => t.Source == TrackSource.YouTube && t.ExternalId == request.VideoId)
|
||||
.Select(t => new { t.Id, t.AudioTranscodeProcessingStatus, t.AudioFolderName } )
|
||||
.SingleOrDefaultAsync(cancellationToken);
|
||||
|
||||
if (track != null
|
||||
&& !string.IsNullOrWhiteSpace(track.AudioFolderName)
|
||||
&& track.AudioTranscodeProcessingStatus == ProcessingStatus.Completed)
|
||||
{
|
||||
var serverStream = await streamIssuer.IssueAsync(track.Id, track.AudioFolderName, request.UserId, cancellationToken);
|
||||
return new YouTubeStreamResponse(
|
||||
YouTubeStreamMode.Server,
|
||||
serverStream.ManifestUrl,
|
||||
serverStream.Ticket,
|
||||
serverStream.ExpiresInSeconds,
|
||||
track.Id);
|
||||
}
|
||||
|
||||
var streamInfo = await youTubeMusicService.GetAudioStreamAsync(request.VideoId, cancellationToken);
|
||||
if (streamInfo.IsError)
|
||||
return streamInfo.Errors;
|
||||
|
||||
return new YouTubeStreamResponse(
|
||||
YouTubeStreamMode.YouTube,
|
||||
streamInfo.Value.Url,
|
||||
string.Empty,
|
||||
streamInfo.Value.ExpiresInSeconds,
|
||||
Guid.Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
namespace Musify.Application.YouTube.Responses
|
||||
{
|
||||
public enum YouTubeStreamMode
|
||||
{
|
||||
Server = 0,
|
||||
YouTube = 1
|
||||
}
|
||||
|
||||
public record YouTubeStreamResponse(
|
||||
YouTubeStreamMode Mode,
|
||||
string StreamUrl,
|
||||
string Ticket,
|
||||
int ExpiresInSeconds,
|
||||
Guid TrackId);
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using ErrorOr;
|
||||
using Mediator;
|
||||
using Musify.Application.Contracts;
|
||||
|
||||
namespace Musify.Application.YouTube
|
||||
{
|
||||
public record SearchYouTubeTracksQuery(string Query, string ContinuationToken)
|
||||
: IQuery<ErrorOr<YouTubeSearchResult>>;
|
||||
|
||||
public class SearchYouTubeTracksQueryHandler(IYouTubeMusicService youTubeMusicService)
|
||||
: IQueryHandler<SearchYouTubeTracksQuery, ErrorOr<YouTubeSearchResult>>
|
||||
{
|
||||
public async ValueTask<ErrorOr<YouTubeSearchResult>> Handle(SearchYouTubeTracksQuery request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (string.IsNullOrEmpty(request.Query))
|
||||
return Error.Validation(description: "The search query must not be empty.");
|
||||
|
||||
return await youTubeMusicService.SearchSongsAsync(request.Query, request.ContinuationToken, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user