Добавьте файлы проекта.
This commit is contained in:
179
YandexMusic.API/Common/Ynison/YnisonWebSocket.cs
Normal file
179
YandexMusic.API/Common/Ynison/YnisonWebSocket.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
|
||||
namespace YandexMusic.API.Common.Ynison
|
||||
{
|
||||
public class YnisonWebSocket : IDisposable
|
||||
{
|
||||
#region Поля
|
||||
|
||||
private readonly JsonSerializerSettings jsonSettings = new()
|
||||
{
|
||||
Converters = new List<JsonConverter> {
|
||||
new StringEnumConverter {
|
||||
NamingStrategy = new CamelCaseNamingStrategy()
|
||||
}
|
||||
},
|
||||
NullValueHandling = NullValueHandling.Ignore
|
||||
};
|
||||
|
||||
private readonly ClientWebSocket socketClient = new();
|
||||
|
||||
private CancellationTokenSource cancellationTokenSource = new();
|
||||
private CancellationToken cancellation;
|
||||
|
||||
private readonly StringBuilder data = new();
|
||||
private readonly int size = 4096;
|
||||
|
||||
#endregion Поля
|
||||
|
||||
#region Свойства
|
||||
|
||||
public bool IsConnected => socketClient.State == WebSocketState.Open;
|
||||
|
||||
#endregion Свойства
|
||||
|
||||
#region События
|
||||
|
||||
public class ReceiveEventArgs
|
||||
{
|
||||
public string Data { get; internal set; }
|
||||
}
|
||||
|
||||
public delegate void OnReceiveEventHandler(YnisonWebSocket socket, ReceiveEventArgs args);
|
||||
/// <summary>
|
||||
/// Получение данных
|
||||
/// </summary>
|
||||
public event OnReceiveEventHandler OnReceive;
|
||||
|
||||
public class CloseEventArgs
|
||||
{
|
||||
public WebSocketCloseStatus? Status { get; set; }
|
||||
public string Description { get; set; }
|
||||
}
|
||||
|
||||
public delegate void OnCloseEventHandler(YnisonWebSocket socket, CloseEventArgs args);
|
||||
/// <summary>
|
||||
/// Закрытие соединения
|
||||
/// </summary>
|
||||
public event OnCloseEventHandler OnClose;
|
||||
|
||||
#endregion События
|
||||
|
||||
#region Вспомогательные функции
|
||||
|
||||
private string SerializeJson(object obj)
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj, jsonSettings);
|
||||
}
|
||||
|
||||
private string GetProtocolData(string deviceId, string redirectTicket)
|
||||
{
|
||||
Dictionary<string, object> deviceInfo = new() {
|
||||
{ "app_name", "Chrome" },
|
||||
{ "type", 1 }
|
||||
};
|
||||
|
||||
Dictionary<string, string> protocol = new() {
|
||||
{ "Ynison-Device-Id", deviceId },
|
||||
{ "Ynison-Device-Info", SerializeJson(deviceInfo) }
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(redirectTicket))
|
||||
protocol.Add("Ynison-Redirect-Ticket", redirectTicket);
|
||||
|
||||
return SerializeJson(protocol);
|
||||
}
|
||||
|
||||
private async Task<string> ReadSocketContent()
|
||||
{
|
||||
byte[] buffer = new byte[size];
|
||||
WebSocketReceiveResult result;
|
||||
|
||||
do
|
||||
{
|
||||
result = await socketClient.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
data.Append(Encoding.UTF8.GetString(buffer, 0, result.Count));
|
||||
} while (!result.EndOfMessage);
|
||||
|
||||
return data.ToString();
|
||||
}
|
||||
|
||||
#endregion Вспомогательные функции
|
||||
|
||||
|
||||
|
||||
public bool Connect(AuthStorage storage, string url, string redirectTicket = null)
|
||||
{
|
||||
socketClient.Options.AddSubProtocol("Bearer");
|
||||
|
||||
socketClient.Options.SetRequestHeader("Sec-WebSocket-Protocol", $"Bearer, v2, {GetProtocolData(storage.DeviceId, redirectTicket)}");
|
||||
socketClient.Options.SetRequestHeader("Origin", "https://music.yandex.ru");
|
||||
socketClient.Options.SetRequestHeader("Authorization", $"OAuth {storage.Token}");
|
||||
|
||||
socketClient.Options.Proxy = storage.Context.WebProxy;
|
||||
|
||||
socketClient.ConnectAsync(new Uri(url), CancellationToken.None)
|
||||
.GetAwaiter()
|
||||
.GetResult();
|
||||
|
||||
cancellation = cancellationTokenSource.Token;
|
||||
|
||||
return socketClient.State == WebSocketState.Open;
|
||||
}
|
||||
|
||||
public async Task BeginReceive()
|
||||
{
|
||||
if (socketClient.State != WebSocketState.Open)
|
||||
return;
|
||||
|
||||
do
|
||||
{
|
||||
string content = await ReadSocketContent();
|
||||
OnReceive?.Invoke(this, new ReceiveEventArgs
|
||||
{
|
||||
Data = content
|
||||
});
|
||||
|
||||
data.Clear();
|
||||
} while (!cancellation.IsCancellationRequested && socketClient.State == WebSocketState.Open);
|
||||
|
||||
OnClose?.Invoke(this, new CloseEventArgs
|
||||
{
|
||||
Status = socketClient.CloseStatus,
|
||||
Description = socketClient.CloseStatusDescription
|
||||
});
|
||||
|
||||
await socketClient.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None);
|
||||
}
|
||||
|
||||
|
||||
public ValueTask Send(string json)
|
||||
{
|
||||
ReadOnlyMemory<byte> message = new(Encoding.UTF8.GetBytes(json));
|
||||
return socketClient.SendAsync(message, WebSocketMessageType.Text, false, CancellationToken.None);
|
||||
}
|
||||
|
||||
public Task StopReceive()
|
||||
{
|
||||
if (socketClient.State != WebSocketState.Open)
|
||||
return Task.CompletedTask;
|
||||
|
||||
cancellationTokenSource.Cancel(false);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
socketClient?.Dispose();
|
||||
cancellationTokenSource?.Dispose();
|
||||
}
|
||||
|
||||
#endregion IDisposable
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user