using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using PlaylistShared.Api.Entities; using PlaylistShared.Api.Extensions; using PlaylistShared.Api.Services; using PlaylistShared.Shared; using PlaylistShared.Shared.Yandex; using System.Security.Claims; namespace PlaylistShared.Api.Controllers; [ApiController] [Route("api/[controller]")] public class AudioController : ControllerBase { private readonly UserManager _userManager; private readonly YandexMusicService _yandexService; private readonly SharedPlaylistService _sharedService; private readonly JwtService _jwtService; private readonly IHttpClientFactory _httpClientFactory; public AudioController( UserManager userManager, YandexMusicService yandexService, SharedPlaylistService sharedService, JwtService jwtService, IHttpClientFactory httpClientFactory) { _userManager = userManager; _yandexService = yandexService; _sharedService = sharedService; _jwtService = jwtService; _httpClientFactory = httpClientFactory; } [HttpGet("play-token")] [Authorize] public IActionResult GetPlayToken() { var userId = User.GetUserId(); var token = _jwtService.CreatePlayToken(userId); return Ok(ApiResponse.Ok(token)); } [HttpGet("track/{trackId}")] [AllowAnonymous] public async Task StreamTrack(string trackId, [FromQuery] string? play_token = null, [FromQuery] string? shared_id = null) { var user = await GetUserFromPlayToken(play_token); if (user == null || user.YandexAccessToken is null) user = await GetUserFromSharedPlaylistId(shared_id); if (user == null) return Unauthorized(); var streamUrl = await _yandexService.GetTrackFileUrlAsync(user, trackId); if (string.IsNullOrEmpty(streamUrl)) return NotFound(); var httpClient = _httpClientFactory.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Get, streamUrl); if (Request.Headers.ContainsKey("Range")) request.Headers.Add("Range", Request.Headers["Range"].ToString()); var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); Response.StatusCode = (int)response.StatusCode; Response.ContentType = response.Content.Headers.ContentType?.ToString() ?? "audio/mpeg"; if (response.Content.Headers.Contains("Content-Range")) Response.Headers.Append("Content-Range", response.Content.Headers.ContentRange?.ToString()); if (response.Headers.Contains("Accept-Ranges")) Response.Headers.Append("Accept-Ranges", response.Headers.AcceptRanges?.ToString()); if (response.Content.Headers.Contains("Content-Length")) Response.Headers.Append("Content-Length", response.Content.Headers.ContentLength?.ToString()); await response.Content.CopyToAsync(Response.Body); return new EmptyResult(); } [HttpGet("track-info/{trackId}")] [AllowAnonymous] public async Task>> GetTrackInfo(string trackId, [FromQuery] string? play_token = null, [FromQuery] string? shared_id = null) { var user = await GetUserFromPlayToken(play_token); if (user == null || user.YandexAccessToken is null) user = await GetUserFromSharedPlaylistId(shared_id); if (user == null) return Unauthorized(); var track = await _yandexService.GetYTrackAsync(user, trackId); if (track == null) return NotFound(); return Ok(ApiResponse.Ok(new YandexTrack { Title = track.Title, CoverUri = track.CoverUri, Artists = track.Artists.Select(a => new YandexArtist { Id = a.Id, Name = a.Name, CoverUrl = a.Cover.GetUrl(), Description = a.Description?.Text ?? string.Empty, }).ToList(), DurationMs = track.DurationMs, })); } private async Task GetUserFromPlayToken(string? token) { if (string.IsNullOrEmpty(token)) return null; var userId = _jwtService.ValidatePlayToken(token); if (!userId.HasValue) return null; return await _userManager.FindByIdAsync(userId.Value.ToString()); } private async Task GetUserFromSharedPlaylistId(string? sharedId) { if (string.IsNullOrEmpty(sharedId)) return null; var playlist = await _sharedService.GetEntityByTokenAsync(sharedId); if (playlist == null) return null; if (!await _sharedService.CanPlayEveryoneAsync(playlist)) return null; return await _userManager.FindByIdAsync(playlist.CreatorUserId.ToString()); } }