128 lines
5.1 KiB
C#
128 lines
5.1 KiB
C#
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<ApplicationUser> _userManager;
|
|
private readonly YandexMusicService _yandexService;
|
|
private readonly SharedPlaylistService _sharedService;
|
|
private readonly JwtService _jwtService;
|
|
|
|
public AudioController(
|
|
UserManager<ApplicationUser> userManager,
|
|
YandexMusicService yandexService,
|
|
SharedPlaylistService sharedService,
|
|
JwtService jwtService)
|
|
{
|
|
_userManager = userManager;
|
|
_yandexService = yandexService;
|
|
_sharedService = sharedService;
|
|
_jwtService = jwtService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Потоковое воспроизведение трека из Яндекс.Музыки.
|
|
/// </summary>
|
|
/// <param name="trackId">ID трека (например, "21696942").</param>
|
|
/// <param name="access_token">gwt пользователя</param>
|
|
/// <param name="shared_id">ID расшаренного плейлиста</param>
|
|
[HttpGet("track/{trackId}")]
|
|
[AllowAnonymous]
|
|
public async Task<IActionResult> StreamTrack(string trackId, [FromQuery] string? access_token = null, [FromQuery] string? shared_id = null)
|
|
{
|
|
var user = await GetUserFromToken(access_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 = 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();
|
|
}
|
|
|
|
[HttpGet("track-info/{trackId}")]
|
|
[AllowAnonymous]
|
|
public async Task<ActionResult<ApiResponse<YandexTrack>>> GetTrackInfo(string trackId, [FromQuery] string? access_token = null, [FromQuery] string? shared_id = null)
|
|
{
|
|
var user = await GetUserFromToken(access_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<YandexTrack>.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<ApplicationUser?> 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<ApplicationUser?> 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());
|
|
|
|
}
|
|
} |