Files
YandexMusic/YandexMusic/YandexMusicClient.cs
FrigaT 266aa2e181
All checks were successful
Release / pack-and-publish (release) Successful in 31s
fix
2026-04-13 02:30:14 +03:00

516 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using YandexMusic.API;
using YandexMusic.API.Common;
using YandexMusic.API.Common.Ynison;
using YandexMusic.API.Models.Account;
using YandexMusic.API.Models.Album;
using YandexMusic.API.Models.Artist;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Feed;
using YandexMusic.API.Models.Landing;
using YandexMusic.API.Models.Landing.Entity.Entities.Context;
using YandexMusic.API.Models.Library;
using YandexMusic.API.Models.Playlist;
using YandexMusic.API.Models.Queue;
using YandexMusic.API.Models.Radio;
using YandexMusic.API.Models.Search;
using YandexMusic.API.Models.Track;
namespace YandexMusic;
/// <summary>Асинхронный клиент для работы с API Яндекс Музыки.</summary>
public class YandexMusicClient : IDisposable
{
private readonly YandexMusicApi _api;
private readonly AuthStorage _storage;
private YnisonPlayer? _player;
/// <summary>Информация об аккаунте (после авторизации).</summary>
public YAccount Account => _storage.User;
/// <summary>Флаг авторизации.</summary>
public bool IsAuthorized => _storage.IsAuthorized;
/// <summary>Плеер Ynison (WebSocket) для синхронизации воспроизведения.</summary>
public YnisonPlayer? Ynison => _player;
/// <summary>Создаёт новый экземпляр клиента Яндекс Музыки.</summary>
public YandexMusicClient()
{
_api = new YandexMusicApi();
_storage = new AuthStorage();
}
#region Авторизация
/// <summary>
/// Авторизация по токену
/// </summary>
/// <param name="token">Токен авторизации</param>
/// <returns></returns>
public async Task<bool> Authorize(string token)
{
await _api.User.AuthorizeAsync(_storage, token);
return _storage.IsAuthorized;
}
/// <summary>
/// Создание сеанса и получение доступных методов авторизации
/// </summary>
/// <param name="userName">Имя пользователя</param>
/// <returns></returns>
public Task<YAuthTypes> CreateAuthSession(string userName)
{
return _api.User.CreateAuthSessionAsync(_storage, userName);
}
/// <summary>
/// Получение ссылки на QR-код
/// </summary>
/// <returns></returns>
public Task<string> GetAuthQRLink()
{
return _api.User.GetAuthQRLinkAsync(_storage);
}
/// <summary>
/// Авторизация по QR-коду
/// </summary>
/// <returns></returns>
public Task<YAuthQRStatus> AuthorizeByQR()
{
return _api.User.AuthorizeByQRAsync(_storage);
}
/// <summary>
/// Получение <see cref="YAuthCaptcha"/>
/// </summary>
/// <returns></returns>
public Task<YAuthCaptcha> GetCaptcha()
{
return _api.User.GetCaptchaAsync(_storage);
}
/// <summary>
/// Авторизация по captcha
/// </summary>
/// <param name="captcha">Значение captcha</param>
/// <returns></returns>
public Task<YAuthBase> AuthorizeByCaptcha(string captcha)
{
return _api.User.AuthorizeByCaptchaAsync(_storage, captcha);
}
/// <summary>
/// Получение письма авторизации на почту пользователя
/// </summary>
/// <returns></returns>
public Task<YAuthLetter> GetAuthLetter()
{
return _api.User.GetAuthLetterAsync(_storage);
}
/// <summary>
/// Авторизация после подтверждения входа через письмо
/// </summary>
/// <returns></returns>
public Task<bool> AuthorizeByLetter()
{
return _api.User.AuthorizeByLetterAsync(_storage);
}
/// <summary>
/// Авторизация с помощью пароля из приложения Яндекс
/// </summary>
/// <param name="password">Пароль</param>
/// <returns></returns>
public Task<YAuthBase> AuthorizeByAppPassword(string password)
{
return _api.User.AuthorizeByAppPasswordAsync(_storage, password);
}
/// <summary>
/// Получение <see cref="YAccessToken"/> после авторизации с помощью QR, e-mail, пароля из приложения
/// </summary>
public Task<YAccessToken> GetAccessToken()
{
return _api.User.GetAccessTokenAsync(_storage);
}
/// <summary>
/// Получение информации о пользователе через логин Яндекса
/// </summary>
public Task<YLoginInfo> GetLoginInfo()
{
return _api.User.GetLoginInfoAsync(_storage);
}
#endregion Авторизация
#region Треки
/// <summary>Получает трек по идентификатору.</summary>
public async Task<YTrack?> GetTrackAsync(string id)
{
var response = await _api.Track.GetAsync(_storage, id);
return response?.Result?.FirstOrDefault();
}
/// <summary>Получает список треков по идентификаторам.</summary>
public async Task<List<YTrack>> GetTracksAsync(IEnumerable<string> ids)
{
var response = await _api.Track.GetAsync(_storage, ids);
return response?.Result ?? new List<YTrack>();
}
#endregion
#region Альбомы
/// <summary>Получает альбом по идентификатору.</summary>
public async Task<YAlbum?> GetAlbumAsync(string id)
{
var response = await _api.Album.GetAsync(_storage, id);
return response?.Result;
}
/// <summary>Получает список альбомов по идентификаторам.</summary>
public async Task<List<YAlbum>> GetAlbumsAsync(IEnumerable<string> ids)
{
var response = await _api.Album.GetAsync(_storage, ids);
return response?.Result ?? new List<YAlbum>();
}
#endregion
#region Главная страница
/// <summary>Получает персональные блоки лендинга.</summary>
public async Task<YLanding?> GetLandingAsync(params YLandingBlockType[] blocks)
{
var response = await _api.Landing.GetAsync(_storage, blocks);
return response?.Result;
}
/// <summary>Получает ленту событий.</summary>
public async Task<YFeed?> GetFeedAsync()
{
var response = await _api.Landing.GetFeedAsync(_storage);
return response?.Result;
}
/// <summary>Получает детский лендинг.</summary>
public async Task<YChildrenLanding?> GetChildrenLandingAsync()
{
var response = await _api.Landing.GetChildrenLandingAsync(_storage);
return response?.Result;
}
#endregion
#region Исполнители
/// <summary>Получает информацию об исполнителе.</summary>
public async Task<YArtistBriefInfo?> GetArtistAsync(string id)
{
var response = await _api.Artist.GetAsync(_storage, id);
return response?.Result;
}
/// <summary>Получает список исполнителей.</summary>
public async Task<List<YArtist>> GetArtistsAsync(IEnumerable<string> ids)
{
var response = await _api.Artist.GetAsync(_storage, ids);
return response?.Result ?? new List<YArtist>();
}
#endregion
#region Плейлисты
/// <summary>Получает плейлист по пользователю и идентификатору.</summary>
public async Task<YPlaylist?> GetPlaylistAsync(string user, string id)
{
var response = await _api.Playlist.GetAsync(_storage, user, id);
return response?.Result;
}
/// <summary>Получает плейлист по UUID.</summary>
public async Task<YPlaylist?> GetPlaylistAsync(string uuid)
{
var response = await _api.Playlist.GetAsync(_storage, uuid);
return response?.Result;
}
/// <summary>Получает список плейлистов по списку пар (пользователь, идентификатор).</summary>
public async Task<List<YPlaylist>> GetPlaylistsAsync(IEnumerable<(string user, string id)> ids)
{
var response = await _api.Playlist.GetAsync(_storage, ids);
return response?.Result ?? new List<YPlaylist>();
}
/// <summary>Получает персональные плейлисты (с главной страницы).</summary>
public async Task<List<YPlaylist>> GetPersonalPlaylistsAsync()
{
var playlists = await _api.Playlist.GetPersonalPlaylistsAsync(_storage);
return playlists.Select(r => r.Result).Where(p => p != null).ToList()!;
}
/// <summary>Получает избранные плейлисты.</summary>
public async Task<List<YPlaylist>> GetFavoritesAsync()
{
var response = await _api.Playlist.FavoritesAsync(_storage);
return response?.Result ?? new List<YPlaylist>();
}
/// <summary>Получает плейлист «Дежавю».</summary>
public async Task<YPlaylist?> GetDejaVuAsync()
{
var response = await _api.Playlist.DejaVuAsync(_storage);
return response?.Result;
}
/// <summary>Получает плейлист «Тайник».</summary>
public async Task<YPlaylist?> GetMissedAsync()
{
var response = await _api.Playlist.MissedAsync(_storage);
return response?.Result;
}
/// <summary>Получает плейлист дня.</summary>
public async Task<YPlaylist?> GetOfTheDayAsync()
{
var response = await _api.Playlist.OfTheDayAsync(_storage);
return response?.Result;
}
/// <summary>Получает плейлист «Кинопоиск».</summary>
public async Task<YPlaylist?> GetKinopoiskAsync()
{
var response = await _api.Playlist.KinopoiskAsync(_storage);
return response?.Result;
}
/// <summary>Получает плейлист «Премьера».</summary>
public async Task<YPlaylist?> GetPremiereAsync()
{
var response = await _api.Playlist.PremiereAsync(_storage);
return response?.Result;
}
/// <summary>Создаёт новый плейлист с заданным именем.</summary>
public async Task<YPlaylist?> CreatePlaylistAsync(string name)
{
var response = await _api.Playlist.CreateAsync(_storage, name);
return response?.Result;
}
#endregion
#region Поиск
/// <summary>Выполняет поиск.</summary>
public async Task<YSearch?> SearchAsync(string searchText, YSearchType searchType, int page = 0, int pageSize = 20)
{
var response = await _api.Search.SearchAsync(_storage, searchText, searchType, page, pageSize);
return response?.Result;
}
/// <summary>Получает подсказки для поискового запроса.</summary>
public async Task<YSearchSuggest?> GetSearchSuggestionsAsync(string searchText)
{
var response = await _api.Search.SuggestAsync(_storage, searchText);
return response?.Result;
}
#endregion
#region Библиотека
/// <summary>Получает лайкнутые треки.</summary>
public async Task<List<YTrack>> GetLikedTracksAsync()
{
var likes = await _api.Library.GetLikedTracksAsync(_storage);
var ids = likes.Result?.Library.Tracks.Select(t => t.Id).ToArray() ?? Array.Empty<string>();
if (ids.Length == 0) return new List<YTrack>();
var response = await _api.Track.GetAsync(_storage, ids);
return response?.Result ?? new List<YTrack>();
}
/// <summary>Получает дизлайкнутые треки.</summary>
public async Task<List<YTrack>> GetDislikedTracksAsync()
{
var dislikes = await _api.Library.GetDislikedTracksAsync(_storage);
var ids = dislikes.Result?.Library.Tracks.Select(t => t.Id).ToArray() ?? Array.Empty<string>();
if (ids.Length == 0) return new List<YTrack>();
var response = await _api.Track.GetAsync(_storage, ids);
return response?.Result ?? new List<YTrack>();
}
/// <summary>Получает лайкнутые альбомы.</summary>
public async Task<List<YAlbum>> GetLikedAlbumsAsync()
{
var albums = await _api.Library.GetLikedAlbumsAsync(_storage);
var ids = albums.Result?.Select(a => a.Id).ToArray() ?? Array.Empty<string>();
if (ids.Length == 0) return new List<YAlbum>();
var response = await _api.Album.GetAsync(_storage, ids);
return response?.Result ?? new List<YAlbum>();
}
/// <summary>Получает лайкнутых исполнителей.</summary>
public async Task<List<YArtist>> GetLikedArtistsAsync()
{
var artists = await _api.Library.GetLikedArtistsAsync(_storage);
var ids = artists.Result?.Select(a => a.Id).ToArray() ?? Array.Empty<string>();
if (ids.Length == 0) return new List<YArtist>();
var response = await _api.Artist.GetAsync(_storage, ids);
return response?.Result ?? new List<YArtist>();
}
/// <summary>Получает дизлайкнутых исполнителей.</summary>
public async Task<List<YArtist>> GetDislikedArtistsAsync()
{
var artists = await _api.Library.GetDislikedArtistsAsync(_storage);
var ids = artists.Result?.Select(a => a.Id).ToArray() ?? Array.Empty<string>();
if (ids.Length == 0) return new List<YArtist>();
var response = await _api.Artist.GetAsync(_storage, ids);
return response?.Result ?? new List<YArtist>();
}
/// <summary>Получает лайкнутые плейлисты.</summary>
public async Task<List<YPlaylist>> GetLikedPlaylistsAsync()
{
var playlists = await _api.Library.GetLikedPlaylistsAsync(_storage);
var ids = playlists.Result?
.Select(p => (p.Playlist.Uid, p.Playlist.Kind))
.ToArray() ?? Array.Empty<(string, string)>();
if (ids.Length == 0) return new List<YPlaylist>();
var response = await _api.Playlist.GetAsync(_storage, ids);
return response?.Result ?? new List<YPlaylist>();
}
/// <summary>Получает список недавно прослушанных треков.</summary>
/// <param name="contextTypes">Типы контекстов (альбом, исполнитель, плейлист).</param>
/// <param name="trackCount">Количество треков на контекст.</param>
/// <param name="contextCount">Количество контекстов.</param>
public async Task<List<YRecentlyListened>> GetRecentlyListenedAsync(
IEnumerable<YPlayContextType> contextTypes,
int trackCount = 50,
int contextCount = 10)
{
var response = await _api.Library.GetRecentlyListenedAsync(_storage, contextTypes, trackCount, contextCount);
return response?.Result?.Contexts ?? new List<YRecentlyListened>();
}
#endregion
#region Радио
/// <summary>Получает список рекомендованных радиостанций.</summary>
public async Task<List<YStation>> GetRadioDashboardAsync()
{
var response = await _api.Radio.GetStationsDashboardAsync(_storage);
return response?.Result?.Stations ?? new List<YStation>();
}
/// <summary>Получает список всех радиостанций.</summary>
public async Task<List<YStation>> GetRadioStationsAsync()
{
var response = await _api.Radio.GetStationsAsync(_storage);
return response?.Result ?? new List<YStation>();
}
/// <summary>Получает информацию о радиостанции по идентификатору.</summary>
public async Task<YStation?> GetRadioStationAsync(YStationId id)
{
var response = await _api.Radio.GetStationAsync(_storage, id);
return response?.Result?.FirstOrDefault();
}
#endregion
#region Очереди
/// <summary>Получает все очереди с разных устройств.</summary>
public async Task<YQueueItemsContainer?> GetQueuesAsync(string? device = null)
{
var response = await _api.Queue.ListAsync(_storage, device);
return response?.Result;
}
/// <summary>Получает очередь по идентификатору.</summary>
public async Task<YQueue?> GetQueueAsync(string queueId)
{
var response = await _api.Queue.GetAsync(_storage, queueId);
return response?.Result;
}
/// <summary>Создаёт новую очередь.</summary>
public async Task<YNewQueue?> CreateQueueAsync(YQueue queue, string? device = null)
{
var response = await _api.Queue.CreateAsync(_storage, queue, device);
return response?.Result;
}
/// <summary>Обновляет позицию в очереди.</summary>
public async Task<YUpdatedQueue?> UpdateQueuePositionAsync(string queueId, int currentIndex, bool isInteractive, string? device = null)
{
var response = await _api.Queue.UpdatePositionAsync(_storage, queueId, currentIndex, isInteractive, device);
return response?.Result;
}
#endregion
#region Загрузка треков (UGC)
/// <summary>Загружает трек из файла в плейлист.</summary>
public async Task<string?> UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, string filePath)
{
var uploadLink = await _api.UserGeneratedContent.GetUgcUploadLinkAsync(_storage, playlist, fileName);
var response = await _api.UserGeneratedContent.UploadUgcTrackAsync(_storage, uploadLink.PostTarget, filePath);
return response?.Result;
}
/// <summary>Загружает трек из потока в плейлист.</summary>
public async Task<string?> UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, Stream stream)
{
var uploadLink = await _api.UserGeneratedContent.GetUgcUploadLinkAsync(_storage, playlist, fileName);
var response = await _api.UserGeneratedContent.UploadUgcTrackAsync(_storage, uploadLink.PostTarget, stream);
return response?.Result;
}
/// <summary>Загружает трек из массива байтов в плейлист.</summary>
public async Task<string?> UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, byte[] file)
{
var uploadLink = await _api.UserGeneratedContent.GetUgcUploadLinkAsync(_storage, playlist, fileName);
var response = await _api.UserGeneratedContent.UploadUgcTrackAsync(_storage, uploadLink.PostTarget, file);
return response?.Result;
}
#endregion
#region Лейблы
/// <summary>Получает альбомы лейбла с пагинацией.</summary>
public async Task<List<YAlbum>> GetAlbumsByLabelAsync(YLabel label, int page = 0)
{
var response = await _api.Label.GetAlbumsByLabelAsync(_storage, label, page);
return response?.Result?.Albums ?? new List<YAlbum>();
}
/// <summary>Получает артистов лейбла с пагинацией.</summary>
public async Task<List<YArtist>> GetArtistsByLabelAsync(YLabel label, int page = 0)
{
var response = await _api.Label.GetArtistsByLabelAsync(_storage, label, page);
return response?.Result?.Artists ?? new List<YArtist>();
}
#endregion
#region Ynison (WebSocket плеер)
/// <summary>Подключается к Ynison и запускает синхронизацию состояния плеера.</summary>
public async Task ConnectYnisonAsync()
{
if (_player == null)
_player = _api.Ynison.GetPlayer(_storage);
await _player.ConnectAsync();
}
/// <summary>Отключается от Ynison.</summary>
public async Task DisconnectYnisonAsync()
{
if (_player != null)
await _player.DisconnectAsync();
}
#endregion
#region IDisposable
private bool _disposed;
/// <summary>Освобождает ресурсы.</summary>
public void Dispose()
{
if (_disposed) return;
_player?.Dispose();
_disposed = true;
GC.SuppressFinalize(this);
}
#endregion
}