189 lines
7.7 KiB
C#
189 lines
7.7 KiB
C#
using System.Net;
|
||
using System.Net.Http.Headers;
|
||
using System.Text;
|
||
using System.Text.Encodings.Web;
|
||
using System.Text.Json;
|
||
using System.Web;
|
||
using Microsoft.AspNetCore.Authentication;
|
||
using Microsoft.Extensions.Options;
|
||
using SWAD.API.Consts;
|
||
using SWAD.API.Consts.Enums;
|
||
using SWAD.API.Controllers.DTOs;
|
||
using SWAD.API.Exceptions;
|
||
using SWAD.API.Models.Config.ApiServices;
|
||
using SWAD.API.Models.JsonStructures.MusicAPI.Tidal;
|
||
using SWAD.API.Services.MusicAPI.Auth;
|
||
|
||
namespace SWAD.API.Services.MusicAPI.Api;
|
||
|
||
public class TidalService : ApiService
|
||
{
|
||
private readonly TidalAuthService _authService;
|
||
private TidalAuthResponse? _token;
|
||
|
||
public TidalService(IOptions<ApiServicesConfig> config, IEnumerable<AbstractAuthService> authServices)
|
||
{
|
||
ServiceType = MusicService.Tidal;
|
||
var configServices = config.Value.ServicesData;
|
||
Config = configServices.First(x => x.Name == ServiceType.ToString());
|
||
_authService = authServices.First(x => x.ServiceType == ServiceType) as TidalAuthService
|
||
?? throw new ApplicationException("Auth service not found");
|
||
}
|
||
|
||
//Auto revoke on Expire
|
||
private TidalAuthResponse? Token
|
||
{
|
||
get => _token?.ExpireAt < DateTime.UtcNow ? null : _token;
|
||
set => _token = value;
|
||
}
|
||
|
||
public override async Task<string?> GetLinkByQuery(TrackDto query)
|
||
{
|
||
var searchUri = new UriBuilder(Config.Endpoints.Api);
|
||
if (Token == null)
|
||
{
|
||
var newToken = await _authService.GetToken() as TidalAuthResponse;
|
||
Token = newToken ??
|
||
throw new AuthenticationFailureException(string.Format(Messages.AuthFailMessage, ServiceType));
|
||
}
|
||
|
||
using var client = new HttpClient();
|
||
var clientHeaders = client.DefaultRequestHeaders;
|
||
clientHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.Token);
|
||
clientHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.api+json"));
|
||
|
||
searchUri.Scheme = "https";
|
||
searchUri.Path = Path.Combine(searchUri.Path, Config.ApiPaths.Search, WebUtility.UrlEncode(GetQuery(query)
|
||
//TODO: Это нужно, разработчики TIDAL дауны
|
||
));
|
||
var url = new UriBuilder(searchUri.Uri)
|
||
{
|
||
Port = -1
|
||
};
|
||
var urlQuery = HttpUtility.ParseQueryString(url.Query);
|
||
//TODO: придумать что-то с этим
|
||
urlQuery["countryCode"] = "US";
|
||
urlQuery["include"] = "tracks";
|
||
|
||
url.Query = urlQuery.ToString();
|
||
var httpRequest = new HttpRequestMessage(HttpMethod.Get, url.Uri);
|
||
httpRequest.Content =
|
||
new StringContent(string.Empty, new MediaTypeHeaderValue("application/vnd.api+json"));
|
||
|
||
var response = await client.SendAsync(httpRequest);
|
||
if (!response.IsSuccessStatusCode)
|
||
{
|
||
var requestDebugData = await response.Content.ReadAsStringAsync();
|
||
throw new HttpRequestException(ErrorResources.Unsuccessful);
|
||
}
|
||
|
||
var json =
|
||
await JsonSerializer.DeserializeAsync<TidalSearchResponse>(await response.Content.ReadAsStreamAsync());
|
||
// ReSharper disable once NullableWarningSuppressionIsUsed
|
||
if (json.Data.RelationShips.Track.Data.Count < 1)
|
||
{
|
||
throw new TrackNotFoundException($"Track is not found in {ServiceType}");
|
||
}
|
||
|
||
//return null!; //TODO: Rewrite whole service
|
||
return Config.Endpoints.MusicLink[0] + json.Data.RelationShips.Track.Data[0].Id;
|
||
}
|
||
|
||
public override async Task<TrackDto> GetQueryObject(string link, string countryCode = "US")
|
||
{
|
||
var url = new Uri(link);
|
||
var id = url.Segments[^1];
|
||
|
||
if (Token == null)
|
||
{
|
||
var newToken = await _authService.GetToken() as TidalAuthResponse;
|
||
Token = newToken ??
|
||
throw new AuthenticationFailureException(string.Format(Messages.AuthFailMessage, ServiceType));
|
||
}
|
||
|
||
//Prepare request
|
||
using var client = new HttpClient();
|
||
|
||
//Get response
|
||
var nameResponse = await client.SendAsync(GetRequestMessage(client, id, countryCode));
|
||
if (!nameResponse.IsSuccessStatusCode)
|
||
if (nameResponse.StatusCode == HttpStatusCode.NotFound)
|
||
{
|
||
//TODO: переписать на норм обработку CountryCode
|
||
Thread.Sleep(1000);
|
||
nameResponse = await client.SendAsync(GetRequestMessage(client, id, "GB")); //Оверрайдим на европу
|
||
if (!nameResponse.IsSuccessStatusCode)
|
||
{
|
||
throw new HttpRequestException(ErrorResources.Unsuccessful);
|
||
}
|
||
}
|
||
else
|
||
throw new HttpRequestException(ErrorResources.Unsuccessful);
|
||
|
||
var trackJson =
|
||
await JsonSerializer.DeserializeAsync<TidalTrackResponse>(await nameResponse.Content.ReadAsStreamAsync());
|
||
|
||
StringBuilder artists = new();
|
||
foreach (var data in trackJson?.Data.RelationShips.Artist.Data ?? new())
|
||
{
|
||
var artistsResponse = await client.SendAsync(GetRequestMessage(client, data.Id, countryCode, true));
|
||
if (!artistsResponse.IsSuccessStatusCode)
|
||
if (artistsResponse.StatusCode == HttpStatusCode.NotFound)
|
||
{
|
||
//TODO: переписать на норм обработку CountryCode
|
||
Thread.Sleep(1000);
|
||
artistsResponse =
|
||
await client.SendAsync(GetRequestMessage(client, id, "GB", true)); //Оверрайдим на европу
|
||
if (!artistsResponse.IsSuccessStatusCode)
|
||
{
|
||
throw new HttpRequestException(ErrorResources.Unsuccessful);
|
||
}
|
||
}
|
||
else
|
||
throw new HttpRequestException(ErrorResources.Unsuccessful);
|
||
|
||
var artistJson =
|
||
await JsonSerializer.DeserializeAsync<TidalArtistResponse>(
|
||
await artistsResponse.Content.ReadAsStreamAsync());
|
||
artists.AppendJoin(",", artistJson?.Data.Attributes.Name);
|
||
}
|
||
|
||
// ReSharper disable once NullableWarningSuppressionIsUsed
|
||
//var artists = string.Join(", ", json!.Resource.Artists.ToList().ConvertAll(x => x.Name));
|
||
return new TrackDto(trackJson?.Data.Attributes.Title!, artists.ToString(), null!, ServiceType);
|
||
}
|
||
|
||
private HttpRequestMessage GetRequestMessage(HttpClient client, string id, string countryCode,
|
||
bool isArtist = false)
|
||
{
|
||
var clientHeaders = client.DefaultRequestHeaders;
|
||
clientHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.Token);
|
||
clientHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/vnd.api+json"));
|
||
|
||
UriBuilder builder;
|
||
if (isArtist)
|
||
{
|
||
builder = new UriBuilder(Config.Endpoints.Api + Path.Combine(Config.ApiPaths.GetArtist, id))
|
||
{
|
||
Port = -1
|
||
};
|
||
}
|
||
else
|
||
{
|
||
builder = new UriBuilder(Config.Endpoints.Api + Path.Combine(Config.ApiPaths.GetTrack, id))
|
||
{
|
||
Port = -1,
|
||
Query = "include=artists"
|
||
};
|
||
}
|
||
|
||
var urlQuery = HttpUtility.ParseQueryString(builder.Query);
|
||
//TODO: Придумать что-то с countryCode
|
||
urlQuery["countryCode"] = countryCode;
|
||
builder.Query = urlQuery.ToString();
|
||
|
||
var httpRequest = new HttpRequestMessage(HttpMethod.Get, builder.Uri);
|
||
httpRequest.Content = new StringContent(string.Empty, Encoding.UTF8, "application/vnd.api+json");
|
||
return httpRequest;
|
||
}
|
||
} |