Полностью переписанное api
All checks were successful
Release / pack-and-publish (release) Successful in 36s
All checks were successful
Release / pack-and-publish (release) Successful in 36s
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Net;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -7,14 +8,15 @@ namespace YandexMusic.API.Common.Ynison;
|
||||
/// <summary>WebSocket-клиент для взаимодействия с протоколом Ynison.</summary>
|
||||
public class YnisonWebSocket : IDisposable
|
||||
{
|
||||
private readonly ClientWebSocket _socketClient = new();
|
||||
private ClientWebSocket? _socketClient;
|
||||
private CancellationTokenSource? _cancellationTokenSource;
|
||||
private CancellationToken _cancellationToken;
|
||||
private readonly StringBuilder _data = new();
|
||||
private const int BufferSize = 4096;
|
||||
private readonly IWebProxy? _proxy;
|
||||
|
||||
/// <summary>Флаг, указывает, открыто ли соединение.</summary>
|
||||
public bool IsConnected => _socketClient.State == WebSocketState.Open;
|
||||
public bool IsConnected => _socketClient?.State == WebSocketState.Open;
|
||||
|
||||
/// <summary>Событие получения сообщения.</summary>
|
||||
public event EventHandler<ReceiveEventArgs>? OnReceive;
|
||||
@@ -25,19 +27,25 @@ public class YnisonWebSocket : IDisposable
|
||||
/// <summary>Аргументы события получения данных.</summary>
|
||||
public class ReceiveEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>Полученные данные (JSON-строка).</summary>
|
||||
public string Data { get; init; } = null!;
|
||||
}
|
||||
|
||||
/// <summary>Аргументы события закрытия соединения.</summary>
|
||||
public class CloseEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>Статус закрытия.</summary>
|
||||
public WebSocketCloseStatus? Status { get; init; }
|
||||
/// <summary>Описание причины закрытия.</summary>
|
||||
public string? Description { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Инициализирует новый экземпляр WebSocket-клиента.
|
||||
/// </summary>
|
||||
/// <param name="proxy">Прокси-сервер (опционально).</param>
|
||||
public YnisonWebSocket(IWebProxy? proxy = null)
|
||||
{
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
private static string GetProtocolData(string deviceId, string? redirectTicket)
|
||||
{
|
||||
var deviceInfo = new Dictionary<string, object>
|
||||
@@ -57,6 +65,9 @@ public class YnisonWebSocket : IDisposable
|
||||
|
||||
private async Task<string> ReadSocketContentAsync()
|
||||
{
|
||||
if (_socketClient == null)
|
||||
throw new InvalidOperationException("WebSocket не инициализирован");
|
||||
|
||||
var buffer = new byte[BufferSize];
|
||||
WebSocketReceiveResult result;
|
||||
do
|
||||
@@ -68,17 +79,19 @@ public class YnisonWebSocket : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>Подключается к WebSocket.</summary>
|
||||
/// <param name="storage">Хранилище авторизации.</param>
|
||||
/// <param name="storage">Хранилище авторизации (для токена и deviceId).</param>
|
||||
/// <param name="url">URL WebSocket.</param>
|
||||
/// <param name="redirectTicket">Тикет перенаправления (опционально).</param>
|
||||
public async Task ConnectAsync(AuthStorage storage, string url, string? redirectTicket = null)
|
||||
{
|
||||
_socketClient = new ClientWebSocket();
|
||||
_socketClient.Options.AddSubProtocol("Bearer");
|
||||
var protocolData = GetProtocolData(storage.DeviceId, redirectTicket);
|
||||
_socketClient.Options.SetRequestHeader("Sec-WebSocket-Protocol", $"Bearer, v2, {protocolData}");
|
||||
_socketClient.Options.SetRequestHeader("Origin", "https://music.yandex.ru");
|
||||
_socketClient.Options.SetRequestHeader("Authorization", $"OAuth {storage.Token}");
|
||||
_socketClient.Options.Proxy = storage.Context.WebProxy;
|
||||
if (_proxy != null)
|
||||
_socketClient.Options.Proxy = _proxy;
|
||||
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_cancellationToken = _cancellationTokenSource.Token;
|
||||
@@ -89,7 +102,7 @@ public class YnisonWebSocket : IDisposable
|
||||
/// <summary>Начинает асинхронный приём сообщений.</summary>
|
||||
public async Task BeginReceiveAsync()
|
||||
{
|
||||
if (_socketClient.State != WebSocketState.Open)
|
||||
if (_socketClient == null || _socketClient.State != WebSocketState.Open)
|
||||
return;
|
||||
|
||||
try
|
||||
@@ -116,9 +129,11 @@ public class YnisonWebSocket : IDisposable
|
||||
}
|
||||
|
||||
/// <summary>Отправляет JSON-сообщение.</summary>
|
||||
/// <param name="json">JSON-строка.</param>
|
||||
public async ValueTask SendAsync(string json)
|
||||
{
|
||||
if (_socketClient == null)
|
||||
throw new InvalidOperationException("WebSocket не инициализирован");
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
await _socketClient.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, _cancellationToken);
|
||||
}
|
||||
@@ -127,16 +142,14 @@ public class YnisonWebSocket : IDisposable
|
||||
public async Task StopReceiveAsync()
|
||||
{
|
||||
if (_cancellationTokenSource != null && !_cancellationTokenSource.IsCancellationRequested)
|
||||
{
|
||||
await _cancellationTokenSource.CancelAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Освобождает ресурсы.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_socketClient.Dispose();
|
||||
_socketClient?.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user