Добавьте файлы проекта.
This commit is contained in:
315
YandexMusic.API/Common/Ynison/YnisonPlayer.cs
Normal file
315
YandexMusic.API/Common/Ynison/YnisonPlayer.cs
Normal file
@@ -0,0 +1,315 @@
|
||||
using System.Net.WebSockets;
|
||||
using YandexMusic.API.Models.Track;
|
||||
using YandexMusic.API.Models.Ynison;
|
||||
using YandexMusic.API.Models.Ynison.Messages;
|
||||
|
||||
namespace YandexMusic.API.Common.Ynison
|
||||
{
|
||||
public class YnisonPlayer : IDisposable
|
||||
{
|
||||
#region Поля
|
||||
|
||||
private readonly JsonSerializerSettings jsonSettings = new()
|
||||
{
|
||||
Converters = new List<JsonConverter> {
|
||||
new StringEnumConverter(new UpperSnakeCaseNamingStrategy())
|
||||
},
|
||||
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
ContractResolver = new DefaultContractResolver
|
||||
{
|
||||
// Важно! Унисон отдаёт данные в SnakeCase
|
||||
NamingStrategy = new SnakeCaseNamingStrategy()
|
||||
}
|
||||
};
|
||||
|
||||
private AuthStorage storage;
|
||||
private YnisonWebSocket redirector;
|
||||
private YnisonWebSocket state;
|
||||
|
||||
#endregion Поля
|
||||
|
||||
#region Свойства
|
||||
|
||||
/// <summary>
|
||||
/// API
|
||||
/// </summary>
|
||||
public YandexMusicApi API { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Состояние
|
||||
/// </summary>
|
||||
public YYnisonState State { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Текущий проигрываемый трек
|
||||
/// </summary>
|
||||
public YTrack Current => GetCurrent();
|
||||
|
||||
#endregion Свойства
|
||||
|
||||
#region События
|
||||
|
||||
public class ReceiveEventArgs
|
||||
{
|
||||
public YYnisonState State { get; internal set; }
|
||||
}
|
||||
|
||||
public delegate void OnReceiveEventHandler(YnisonPlayer player, 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(YnisonPlayer player, CloseEventArgs args);
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных
|
||||
/// </summary>
|
||||
public event OnCloseEventHandler OnClose;
|
||||
|
||||
#endregion События
|
||||
|
||||
#region Вспомогательные функции
|
||||
|
||||
private string SerializeJson(object data)
|
||||
{
|
||||
return JsonConvert.SerializeObject(data, jsonSettings);
|
||||
}
|
||||
|
||||
private T Deserialize<T>(YYnisonMessageType messageType, string data)
|
||||
{
|
||||
return storage.Debug != null
|
||||
? storage.Debug.Deserialize<T>($"Ynison{messageType}", data, jsonSettings)
|
||||
: JsonConvert.DeserializeObject<T>(data, jsonSettings);
|
||||
}
|
||||
|
||||
private T DeserializeMessage<T>(YYnisonMessageType messageType, string data)
|
||||
{
|
||||
JObject o = JObject.Parse(data);
|
||||
// Сообщение с ошибкой
|
||||
if (o.ContainsKey("error"))
|
||||
{
|
||||
YYnisonErrorMessage exception = Deserialize<YYnisonErrorMessage>(YYnisonMessageType.Error, data);
|
||||
throw exception ?? new Exception("Ошибка десериализации ответа с ошибкой.");
|
||||
}
|
||||
|
||||
return Deserialize<T>(messageType, data);
|
||||
}
|
||||
|
||||
private string DefaultState()
|
||||
{
|
||||
YYnisonVersion version = new()
|
||||
{
|
||||
DeviceId = storage.DeviceId,
|
||||
Version = "0"
|
||||
};
|
||||
|
||||
YYnisonUpdateFullStateMessage fullState = new()
|
||||
{
|
||||
UpdateFullState = new()
|
||||
{
|
||||
Device = new()
|
||||
{
|
||||
Capabilities = new()
|
||||
{
|
||||
CanBePlayer = true
|
||||
},
|
||||
Info = new()
|
||||
{
|
||||
DeviceId = storage.DeviceId,
|
||||
AppName = "Yandex Music API",
|
||||
AppVersion = "0.0.1",
|
||||
Type = "WEB",
|
||||
Title = "YandexMusicAPI"
|
||||
},
|
||||
IsShadow = true
|
||||
},
|
||||
PlayerState = new()
|
||||
{
|
||||
PlayerQueue = new()
|
||||
{
|
||||
Version = version
|
||||
},
|
||||
Status = new()
|
||||
{
|
||||
Version = version
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return SerializeJson(fullState);
|
||||
}
|
||||
|
||||
private YTrack GetCurrent()
|
||||
{
|
||||
if (State == null)
|
||||
return null;
|
||||
|
||||
int index = State.PlayerState.PlayerQueue.CurrentPlayableIndex;
|
||||
if (index < 0 || index > State.PlayerState.PlayerQueue.PlayableList.Count)
|
||||
return null;
|
||||
|
||||
YYnisonPlayableItem item = State.PlayerState.PlayerQueue.PlayableList[index];
|
||||
|
||||
return API.Track.Get(storage, item.PlayableId)
|
||||
.Result
|
||||
.FirstOrDefault();
|
||||
}
|
||||
|
||||
private void UpdateState()
|
||||
{
|
||||
YYnisonUpdatePlayerStateMessage update = new()
|
||||
{
|
||||
UpdatePlayerState = State.PlayerState
|
||||
};
|
||||
|
||||
update.UpdatePlayerState.Status.Version = new()
|
||||
{
|
||||
DeviceId = storage.DeviceId
|
||||
};
|
||||
|
||||
update.UpdatePlayerState.PlayerQueue.Version = new()
|
||||
{
|
||||
DeviceId = storage.DeviceId
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
state.Send(SerializeJson(update));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Вспомогательные функции
|
||||
|
||||
|
||||
|
||||
#region Подключение
|
||||
|
||||
public void Connect()
|
||||
{
|
||||
redirector.Connect(storage, "wss://ynison.music.yandex.ru/redirector.YnisonRedirectService/GetRedirectToYnison");
|
||||
redirector.OnReceive += (socket, data) =>
|
||||
{
|
||||
YYnisonRedirect redirectInfo = Deserialize<YYnisonRedirect>(YYnisonMessageType.Redirect, data.Data);
|
||||
|
||||
if (state.IsConnected)
|
||||
return;
|
||||
|
||||
state.Connect(storage, $"wss://{redirectInfo.Host}/ynison_state.YnisonStateService/PutYnisonState", redirectInfo.RedirectTicket);
|
||||
state.OnReceive += (s, d) =>
|
||||
{
|
||||
YYnisonState message = DeserializeMessage<YYnisonState>(YYnisonMessageType.State, d.Data);
|
||||
|
||||
State = message;
|
||||
|
||||
OnReceive?.Invoke(this, new ReceiveEventArgs
|
||||
{
|
||||
State = State
|
||||
});
|
||||
};
|
||||
|
||||
state.OnClose += (s, args) =>
|
||||
{
|
||||
OnClose?.Invoke(this, new CloseEventArgs
|
||||
{
|
||||
Status = args.Status,
|
||||
Description = args.Description
|
||||
});
|
||||
};
|
||||
|
||||
state.BeginReceive();
|
||||
// Отправка изначального состояния
|
||||
state.Send(DefaultState());
|
||||
};
|
||||
|
||||
redirector.BeginReceive();
|
||||
}
|
||||
|
||||
public void Disconnect()
|
||||
{
|
||||
state?.StopReceive();
|
||||
redirector?.StopReceive();
|
||||
}
|
||||
|
||||
#endregion Подключение
|
||||
|
||||
#region Плеер
|
||||
|
||||
/*
|
||||
public void Play()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Next()
|
||||
{
|
||||
List<YYnisonPlayableItem> list = State.PlayerState.PlayerQueue.PlayableList;
|
||||
|
||||
if (State.PlayerState.PlayerQueue.EntityType == YYnisonEntityType.Radio)
|
||||
{
|
||||
YYnisonPlayableItem next = State.PlayerState.PlayerQueue.Queue.WaveQueue.RecommendedPlayableList
|
||||
.FirstOrDefault();
|
||||
|
||||
list.RemoveAt(0);
|
||||
list.Add(next);
|
||||
|
||||
UpdateState();
|
||||
}
|
||||
|
||||
if (State.PlayerState.PlayerQueue.CurrentPlayableIndex < list.Count - 1)
|
||||
{
|
||||
State.PlayerState.PlayerQueue.CurrentPlayableIndex++;
|
||||
UpdateState();
|
||||
}
|
||||
}
|
||||
|
||||
public void Previous()
|
||||
{
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
#endregion Плеер
|
||||
|
||||
internal YnisonPlayer(YandexMusicApi api, AuthStorage authStorage)
|
||||
{
|
||||
API = api;
|
||||
storage = authStorage;
|
||||
|
||||
redirector = new();
|
||||
state = new();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
redirector?.StopReceive();
|
||||
redirector?.Dispose();
|
||||
}
|
||||
|
||||
#endregion IDisposable
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user