using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using PlaylistShared.Api.Entities; using PlaylistShared.Api.Services; 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; public AudioController( UserManager userManager, YandexMusicService yandexService, SharedPlaylistService sharedService, JwtService jwtService) { _userManager = userManager; _yandexService = yandexService; _sharedService = sharedService; _jwtService = jwtService; } /// /// Потоковое воспроизведение трека из Яндекс.Музыки. /// /// ID трека (например, "21696942"). /// gwt пользователя /// ID расшаренного плейлиста [HttpGet("track/{trackId}")] [AllowAnonymous] public async Task StreamTrack(string trackId, [FromQuery] string? access_token = null, [FromQuery] string? shared_id = null) { var user = await GetUserFromToken(access_token); if (user == 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 = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Get, streamUrl); // Пробрасываем Range-заголовок клиента к Яндекс.Музыке if (Request.Headers.ContainsKey("Range")) { request.Headers.Add("Range", Request.Headers["Range"].ToString()); } var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); // Если Яндекс.Музыка поддерживает range, пробрасываем статус 206 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(); } private async Task GetUserFromToken(string? token) { if (string.IsNullOrEmpty(token)) return null; var principal = _jwtService.ValidateToken(token); if (principal == null) return null; var userId = principal.FindFirst(ClaimTypes.NameIdentifier)?.Value; if (string.IsNullOrEmpty(userId)) return null; return await _userManager.FindByIdAsync(userId); } 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()); } }