using Microsoft.AspNetCore.DataProtection; using PlaylistShared.Api.Entities; using System.Net; using YandexMusic; using YandexMusic.API.Common; namespace PlaylistShared.Api.Services; /// /// Сервис для работы с API Яндекс Музыки в ASP.NET Core. /// public class YandexApiService : IDisposable { private readonly IDataProtector _dataProtector; private readonly HttpClient _httpClient; private readonly YandexMusicClient _client; private readonly CookieContainer _cookieContainer; /// /// Экземпляр клиента Яндекс Музыки. /// public YandexMusicClient Client => _client; /// /// Контейнер кук, используемый клиентом. /// public CookieContainer CookieContainer => _cookieContainer; /// /// Создаёт сервис с автоматическим созданием HttpClient (рекомендуется). /// public YandexApiService(IDataProtectionProvider provider, IWebProxy? proxy = null, TimeSpan? timeout = null) { _dataProtector = provider.CreateProtector("YandexTokens"); _cookieContainer = new(); _httpClient = YandexMusicHttpClientFactory.CreateDefault( cookieContainer: _cookieContainer, proxy: proxy, timeout: timeout ); _client = new YandexMusicClient(_httpClient); } public async Task AuthAsync(ApplicationUser user) { if (string.IsNullOrEmpty(user.YandexAccessToken)) return null; var decryptedToken = DecryptToken(user.YandexAccessToken); if (decryptedToken == null) return null; return await _client.Authorize(decryptedToken); } /// /// Засшифровывает и возвращает токен для хранения в базе данных. /// /// /// public string EncryptToken(string token) => _dataProtector.Protect(token); /// /// Расшифровывает ключ из базы данных. Если токен повреждён или недействителен, возвращает null. /// /// /// public string DecryptToken(string encryptedToken) { try { return _dataProtector.Unprotect(encryptedToken); } catch { return null; } } /// /// Устанавливает куки из строки для указанного домена. /// public void SetCookies(string cookieString, string domain) { var uri = new Uri(domain.StartsWith("http") ? domain : $"https://{domain}"); _cookieContainer.SetCookies(uri, cookieString); } /// /// Получает все куки для указанного домена в виде строки. /// public string GetCookies(string domain) { var uri = new Uri(domain.StartsWith("http") ? domain : $"https://{domain}"); var cookies = _cookieContainer.GetCookies(uri); return string.Join("; ", cookies.Cast().Select(c => $"{c.Name}={c.Value}")); } /// /// Получает значение конкретной куки. /// public string? GetCookie(string domain, string cookieName) { var uri = new Uri(domain.StartsWith("http") ? domain : $"https://{domain}"); var cookie = _cookieContainer.GetCookies(uri)[cookieName]; return cookie?.Value; } private void UpdateHttpClientCookieContainer(CookieContainer container) { var handler = GetInnerHandler(_httpClient); if (handler is HttpClientHandler httpHandler) httpHandler.CookieContainer = container; } private static HttpMessageHandler GetInnerHandler(HttpClient client) { var field = client.GetType().GetField("_handler", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (field?.GetValue(client) is HttpMessageHandler handler) return handler; return new HttpClientHandler(); } /// /// Авторизуется с помощью OAuth-токена. /// public async Task AuthorizeAsync(string token) { return await _client.Authorize(token); } public void Dispose() { _client.Dispose(); _httpClient.Dispose(); } }