138 lines
4.7 KiB
C#
138 lines
4.7 KiB
C#
using Microsoft.AspNetCore.DataProtection;
|
||
using PlaylistShared.Api.Entities;
|
||
using System.Net;
|
||
using YandexMusic;
|
||
using YandexMusic.API.Common;
|
||
|
||
namespace PlaylistShared.Api.Services;
|
||
|
||
/// <summary>
|
||
/// Сервис для работы с API Яндекс Музыки в ASP.NET Core.
|
||
/// </summary>
|
||
public class YandexApiService : IDisposable
|
||
{
|
||
private readonly IDataProtector _dataProtector;
|
||
private readonly HttpClient _httpClient;
|
||
private readonly YandexMusicClient _client;
|
||
private readonly CookieContainer _cookieContainer;
|
||
|
||
/// <summary>
|
||
/// Экземпляр клиента Яндекс Музыки.
|
||
/// </summary>
|
||
public YandexMusicClient Client => _client;
|
||
|
||
/// <summary>
|
||
/// Контейнер кук, используемый клиентом.
|
||
/// </summary>
|
||
public CookieContainer CookieContainer => _cookieContainer;
|
||
|
||
/// <summary>
|
||
/// Создаёт сервис с автоматическим созданием HttpClient (рекомендуется).
|
||
/// </summary>
|
||
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<bool?> 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);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Засшифровывает и возвращает токен для хранения в базе данных.
|
||
/// </summary>
|
||
/// <param name="token"></param>
|
||
/// <returns></returns>
|
||
public string EncryptToken(string token) => _dataProtector.Protect(token);
|
||
|
||
/// <summary>
|
||
/// Расшифровывает ключ из базы данных. Если токен повреждён или недействителен, возвращает null.
|
||
/// </summary>
|
||
/// <param name="encryptedToken"></param>
|
||
/// <returns></returns>
|
||
public string DecryptToken(string encryptedToken)
|
||
{
|
||
try
|
||
{
|
||
return _dataProtector.Unprotect(encryptedToken);
|
||
}
|
||
catch
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Устанавливает куки из строки для указанного домена.
|
||
/// </summary>
|
||
public void SetCookies(string cookieString, string domain)
|
||
{
|
||
var uri = new Uri(domain.StartsWith("http") ? domain : $"https://{domain}");
|
||
_cookieContainer.SetCookies(uri, cookieString);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получает все куки для указанного домена в виде строки.
|
||
/// </summary>
|
||
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<Cookie>().Select(c => $"{c.Name}={c.Value}"));
|
||
}
|
||
|
||
/// <summary>
|
||
/// Получает значение конкретной куки.
|
||
/// </summary>
|
||
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();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Авторизуется с помощью OAuth-токена.
|
||
/// </summary>
|
||
public async Task<bool> AuthorizeAsync(string token)
|
||
{
|
||
return await _client.Authorize(token);
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_client.Dispose();
|
||
_httpClient.Dispose();
|
||
}
|
||
} |