using System.Net.Http.Headers; using System.Text.Json; using System.Web; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Options; using SWAD.API.Consts.Enums; using SWAD.API.Controllers.DTOs; using SWAD.API.Models.Config.ApiServices; using SWAD.API.Models.JsonStructures.MusicAPI.Spotify; using SWAD.API.Services.MusicAPI.Auth; namespace SWAD.API.Services.MusicAPI.Api; public class SpotifyService : ApiService { private readonly SpotifyAuthService _authService; private readonly Uri _searchUri; private SpotifyAuthResponse? _token; public SpotifyService(IOptions config, IEnumerable authServices) { ServiceType = MusicService.Spotify; var configServices = config.Value.ServicesData; Config = configServices.First(x => x.Name == ServiceType.ToString()); _authService = (authServices.First(x => x.ServiceType == ServiceType) as SpotifyAuthService) ?? throw new ApplicationException("Auth service not found"); _searchUri = new Uri(new Uri(Config.Endpoints.Api), Config.ApiPaths.Search); } //Auto revoke on Expire private SpotifyAuthResponse? Token { get => _token?.ExpireAt < DateTime.UtcNow ? null : _token; set => _token = value; } /// /// Get link to spotify by search query /// /// DTO with search query /// link from spotify /// If token problems /// If response is bad public override async Task GetLinkByQuery(TrackDto query) { if (Token == null) { var newToken = await _authService.GetToken() as SpotifyAuthResponse; Token = newToken ?? throw new AuthenticationFailureException(string.Format(Messages.AuthFailMessage, ServiceType)); } using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.Token); var url = new UriBuilder(_searchUri) { Port = -1 }; var urlQuery = HttpUtility.ParseQueryString(url.Query); urlQuery["q"] = GetQuery(query); urlQuery["type"] = "track"; urlQuery["limit"] = "1"; urlQuery["offset"] = "0"; url.Query = urlQuery.ToString(); var response = await client.GetAsync(url.Uri); if (!response.IsSuccessStatusCode) throw new HttpRequestException("Request unsuccessful"); var json = await JsonSerializer.DeserializeAsync( await response.Content.ReadAsStreamAsync()); // ReSharper disable once NullableWarningSuppressionIsUsed return json!.Tracks.Items[0].ExternalUrls.Spotify; } public override async Task 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 SpotifyAuthResponse; Token = newToken ?? throw new AuthenticationFailureException(string.Format(Messages.AuthFailMessage, ServiceType)); } using var client = new HttpClient(); //Prepare request var clientHeaders = client.DefaultRequestHeaders; clientHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token.Token); var builder = new UriBuilder(Config.Endpoints.Api + Path.Combine(Config.ApiPaths.GetTrack, id)) { Port = -1, Query = $"market={countryCode}" }; var httpRequest = new HttpRequestMessage(HttpMethod.Get, builder.Uri); //Get response var response = await client.SendAsync(httpRequest); if (!response.IsSuccessStatusCode) throw new HttpRequestException("Request unsuccessful"); var json = await JsonSerializer.DeserializeAsync(await response.Content.ReadAsStreamAsync()); // ReSharper disable once NullableWarningSuppressionIsUsed var artists = string.Join(", ", json!.Artists.ToList().ConvertAll(x => x.Name)); return new TrackDto(json.Name, artists, json.Albums.Name, ServiceType); } }