Полностью переписанное api
All checks were successful
Release / pack-and-publish (release) Successful in 36s

This commit is contained in:
FrigaT
2026-04-19 17:00:05 +03:00
parent 5541d0ad27
commit 36e28ce3fe
111 changed files with 1552 additions and 3358 deletions

View File

@@ -1,6 +1,4 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Album;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Requests.Album;
namespace YandexMusic.API;
@@ -8,21 +6,13 @@ namespace YandexMusic.API;
/// <summary>API для работы с альбомами.</summary>
public class YAlbumAPI : YCommonAPI
{
/// <summary>Инициализирует новый экземпляр API альбомов.</summary>
/// <param name="yandex">Экземпляр основного API.</param>
public YAlbumAPI(YandexMusicApi yandex) : base(yandex) { }
public YAlbumAPI(YandexMusicApi api) : base(api) { }
/// <summary>Получает альбом по идентификатору.</summary>
/// <param name="storage">Хранилище данных авторизации.</param>
/// <param name="albumId">Идентификатор альбома.</param>
/// <returns>Ответ API с моделью альбома.</returns>
public Task<YResponse<YAlbum>> GetAsync(AuthStorage storage, string albumId)
=> new YGetAlbumBuilder(api, storage).Build(albumId).GetResponseAsync();
public Task<YAlbum?> GetAsync(string albumId)
=> new YGetAlbumBuilder(Api).ExecuteAsync(albumId);
/// <summary>Получает несколько альбомов по списку идентификаторов.</summary>
/// <param name="storage">Хранилище данных авторизации.</param>
/// <param name="albumIds">Список идентификаторов альбомов.</param>
/// <returns>Ответ API со списком альбомов.</returns>
public Task<YResponse<List<YAlbum>>> GetAsync(AuthStorage storage, IEnumerable<string> albumIds)
=> new YGetAlbumsBuilder(api, storage).Build(albumIds).GetResponseAsync();
public Task<List<YAlbum>?> GetAsync(IEnumerable<string> albumIds)
=> new YGetAlbumsBuilder(Api).ExecuteAsync(albumIds);
}

View File

@@ -1,71 +1,27 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Artist;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Requests.Artist;
namespace YandexMusic.API;
/// <summary>
/// API для взаимодействия с исполнителями
/// </summary>
/// <summary>API для работы с исполнителями.</summary>
public class YArtistAPI : YCommonAPI
{
public YArtistAPI(YandexMusicApi yandex) : base(yandex)
public YArtistAPI(YandexMusicApi api) : base(api) { }
public Task<YArtistBriefInfo?> GetAsync(string artistId)
=> new YGetArtistBuilder(Api).ExecuteAsync(artistId);
public Task<List<YArtist>?> GetAsync(IEnumerable<string> artistIds)
=> new YGetArtistsBuilder(Api).ExecuteAsync(artistIds);
public Task<YTracksPage?> GetTracksAsync(string artistId, int page = 0, int pageSize = 20)
=> new YGetArtistTrackBuilder(Api).ExecuteAsync((artistId, page, pageSize));
public async Task<YTracksPage?> GetAllTracksAsync(string artistId)
{
var info = await GetAsync(artistId);
if (info?.Artist?.Counts?.Tracks == null)
return null;
return await GetTracksAsync(artistId, pageSize: info.Artist.Counts.Tracks);
}
/// <summary>
/// Получение исполнителя
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="artistId">Идентификатор</param>
public Task<YResponse<YArtistBriefInfo>> GetAsync(AuthStorage storage, string artistId)
{
return new YGetArtistBuilder(api, storage)
.Build(artistId)
.GetResponseAsync();
}
/// <summary>
/// Получение исполнителей
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="artistIds">Идентификаторы</param>
public Task<YResponse<List<YArtist>>> GetAsync(AuthStorage storage, IEnumerable<string> artistIds)
{
return new YGetArtistsBuilder(api, storage)
.Build(artistIds)
.GetResponseAsync();
}
/// <summary>
/// Получение треков исполнителя с пагинацией
/// <remarks>
/// Треки поставляются по <paramref name="pageSize"/> штук на страницу,
/// для получения всех треков необходимо использовать метод <see cref="GetAllTracksAsync"/>
/// </remarks>
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="artistId">Идентификатор исполнителя</param>
/// <param name="page">Страница ответов</param>
/// <param name="pageSize">Количество треков на странице ответов</param>
public Task<YResponse<YTracksPage>> GetTracksAsync(AuthStorage storage, string artistId, int page = 0, int pageSize = 20)
{
return new YGetArtistTrackBuilder(api, storage)
.Build((artistId, page, pageSize))
.GetResponseAsync();
}
/// <summary>
/// Получение всех треков исполнителя
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="artistId">Идентификатор исполнителя</param>
public async Task<YResponse<YTracksPage>> GetAllTracksAsync(AuthStorage storage, string artistId)
{
YResponse<YArtistBriefInfo> response = await GetAsync(storage, artistId);
return await GetTracksAsync(storage, artistId, pageSize: response.Result.Artist.Counts.Tracks);
}
}

View File

@@ -1,12 +1,10 @@
namespace YandexMusic.API;
/// <summary>Родительский класс для всех веток API.</summary>
/// <summary>Базовый класс для всех веток API.</summary>
public abstract class YCommonAPI
{
/// <summary>Основной экземпляр API.</summary>
protected readonly YandexMusicApi api;
protected YandexMusicApi Api { get; }
/// <summary>Инициализирует новый экземпляр.</summary>
/// <param name="yandex">Экземпляр основного API.</param>
protected YCommonAPI(YandexMusicApi yandex) => api = yandex;
protected YCommonAPI(YandexMusicApi api) => Api = api;
}

View File

@@ -1,39 +1,19 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Label;
using YandexMusic.API.Requests.Label;
namespace YandexMusic.API;
public partial class YLabelAPI : YCommonAPI
/// <summary>API для работы с лейблами.</summary>
public class YLabelAPI : YCommonAPI
{
public YLabelAPI(YandexMusicApi yandex) : base(yandex)
{
}
public YLabelAPI(YandexMusicApi api) : base(api) { }
/// <summary>
/// Постраничное получение альбомов лейбла
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="label">Лейбл</param>
/// <param name="page">Страница</param>
public Task<YResponse<YLabelAlbums>> GetAlbumsByLabelAsync(AuthStorage storage, YLabel label, int page)
{
return new YGetLabelAlbumsBuilder(api, storage)
.Build((label, page))
.GetResponseAsync();
}
/// <summary>Получает альбомы лейбла с пагинацией.</summary>
public Task<YLabelAlbums?> GetAlbumsByLabelAsync(YLabel label, int page = 0)
=> new YGetLabelAlbumsBuilder(Api).ExecuteAsync((label, page));
/// <summary>
/// Постраничное получение артистов лейбла
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="label">Лейбл</param>
/// <param name="page">Страница</param>
public Task<YResponse<YLabelArtists>> GetArtistsByLabelAsync(AuthStorage storage, YLabel label, int page)
{
return new YGetLabelArtistsBuilder(api, storage)
.Build((label, page))
.GetResponseAsync();
}
/// <summary>Получает артистов лейбла с пагинацией.</summary>
public Task<YLabelArtists?> GetArtistsByLabelAsync(YLabel label, int page = 0)
=> new YGetLabelArtistsBuilder(Api).ExecuteAsync((label, page));
}

View File

@@ -1,43 +1,21 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Feed;
using YandexMusic.API.Models.Feed;
using YandexMusic.API.Models.Landing;
using YandexMusic.API.Requests.Feed;
using YandexMusic.API.Requests.Landing;
namespace YandexMusic.API;
/// <summary>API для взаимодействия с главной страницей (лендингом).</summary>
/// <summary>API для работы с главной страницей (лендингом).</summary>
public class YLandingAPI : YCommonAPI
{
/// <summary>Инициализирует новый экземпляр API лендинга.</summary>
/// <param name="yandex">Экземпляр основного API.</param>
public YLandingAPI(YandexMusicApi yandex) : base(yandex) { }
public YLandingAPI(YandexMusicApi api) : base(api) { }
/// <summary>Получает персональные блоки лендинга.</summary>
/// <param name="storage">Хранилище авторизации.</param>
/// <param name="blocks">Типы запрашиваемых блоков.</param>
/// <returns>Ответ API с лендингом.</returns>
/// <exception cref="ArgumentNullException">Если массив blocks равен null.</exception>
public Task<YResponse<YLanding>> GetAsync(AuthStorage storage, params YLandingBlockType[] blocks)
{
if (blocks == null)
throw new ArgumentNullException(nameof(blocks), "Массив блоков не может быть null");
public Task<YLanding?> GetAsync(params YLandingBlockType[] blocks)
=> new YGetLandingBuilder(Api).ExecuteAsync(blocks);
return new YGetLandingBuilder(api, storage)
.Build(blocks)
.GetResponseAsync();
}
public Task<YFeed?> GetFeedAsync()
=> new YGetFeedBuilder(Api).ExecuteAsync(null!);
/// <summary>Получает ленту событий (фид).</summary>
/// <param name="storage">Хранилище авторизации.</param>
/// <returns>Ответ API с лентой.</returns>
public Task<YResponse<YFeed>> GetFeedAsync(AuthStorage storage)
=> new YGetFeedBuilder(api, storage).Build(null!).GetResponseAsync();
/// <summary>Получает лендинг детского раздела.</summary>
/// <param name="storage">Хранилище авторизации.</param>
/// <returns>Ответ API с детским лендингом.</returns>
public Task<YResponse<YChildrenLanding>> GetChildrenLandingAsync(AuthStorage storage)
=> new YGetChildrenLandingBuilder(api, storage).Build(null!).GetResponseAsync();
public Task<YChildrenLanding?> GetChildrenLandingAsync()
=> new YGetChildrenLandingBuilder(Api).ExecuteAsync(null!);
}

View File

@@ -1,4 +1,3 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Album;
using YandexMusic.API.Models.Artist;
using YandexMusic.API.Models.Common;
@@ -10,244 +9,82 @@ using YandexMusic.API.Requests.Library;
namespace YandexMusic.API;
/// <summary>
/// API для взаимодействия с библиотекой
/// </summary>
public partial class YLibraryAPI : YCommonAPI
/// <summary>API для работы с библиотекой (лайки, дизлайки, недавно прослушанное).</summary>
public class YLibraryAPI : YCommonAPI
{
/// <summary>
/// Получение секции библиотеки
/// </summary>
/// <typeparam name="T">Тип объекта библиотеки</typeparam>
/// <param name="storage">Хранилище</param>
/// <param name="section">Секция</param>
/// <param name="type">Тип</param>
/// <returns>Список объектов из секции</returns>
private Task<YResponse<T>> GetLibrarySection<T>(AuthStorage storage, YLibrarySection section, YLibrarySectionType type = YLibrarySectionType.Likes)
{
return new YGetLibrarySectionBuilder<T>(api, storage)
.Build((section, type))
.GetResponseAsync();
}
public YLibraryAPI(YandexMusicApi yandex) : base(yandex)
{
}
public YLibraryAPI(YandexMusicApi api) : base(api) { }
#region Лайки
/// <summary>
/// Получение лайкнутых треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<YLibraryTracks>> GetLikedTracksAsync(AuthStorage storage)
{
return GetLibrarySection<YLibraryTracks>(storage, YLibrarySection.Tracks);
}
public Task<YLibraryTracks?> GetLikedTracksAsync()
=> new YGetLibrarySectionBuilder<YLibraryTracks>(Api).ExecuteAsync((YLibrarySection.Tracks, YLibrarySectionType.Likes));
/// <summary>
/// Получение лайкнутых альбомов
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<List<YLibraryAlbum>>> GetLikedAlbumsAsync(AuthStorage storage)
{
return GetLibrarySection<List<YLibraryAlbum>>(storage, YLibrarySection.Albums);
}
public Task<List<YLibraryAlbum>?> GetLikedAlbumsAsync()
=> new YGetLibrarySectionBuilder<List<YLibraryAlbum>>(Api).ExecuteAsync((YLibrarySection.Albums, YLibrarySectionType.Likes));
/// <summary>
/// Получение лайкнутых исполнителей
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<List<YArtist>>> GetLikedArtistsAsync(AuthStorage storage)
{
return GetLibrarySection<List<YArtist>>(storage, YLibrarySection.Artists);
}
public Task<List<YArtist>?> GetLikedArtistsAsync()
=> new YGetLibrarySectionBuilder<List<YArtist>>(Api).ExecuteAsync((YLibrarySection.Artists, YLibrarySectionType.Likes));
/// <summary>
/// Получение лайкнутых плейлистов
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<List<YLibraryPlaylists>>> GetLikedPlaylistsAsync(AuthStorage storage)
{
return GetLibrarySection<List<YLibraryPlaylists>>(storage, YLibrarySection.Playlists);
}
public Task<List<YLibraryPlaylists>?> GetLikedPlaylistsAsync()
=> new YGetLibrarySectionBuilder<List<YLibraryPlaylists>>(Api).ExecuteAsync((YLibrarySection.Playlists, YLibrarySectionType.Likes));
#endregion Лайки
#endregion
#region Дизлайки
/// <summary>
/// Получение дизлайкнутых треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<YLibraryTracks>> GetDislikedTracksAsync(AuthStorage storage)
{
return GetLibrarySection<YLibraryTracks>(storage, YLibrarySection.Tracks, YLibrarySectionType.Dislikes);
}
public Task<YLibraryTracks?> GetDislikedTracksAsync()
=> new YGetLibrarySectionBuilder<YLibraryTracks>(Api).ExecuteAsync((YLibrarySection.Tracks, YLibrarySectionType.Dislikes));
/// <summary>
/// Получение дизлайкнутых исполнителей
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<List<YArtist>>> GetDislikedArtistsAsync(AuthStorage storage)
{
return GetLibrarySection<List<YArtist>>(storage, YLibrarySection.Artists, YLibrarySectionType.Dislikes);
}
public Task<List<YArtist>?> GetDislikedArtistsAsync()
=> new YGetLibrarySectionBuilder<List<YArtist>>(Api).ExecuteAsync((YLibrarySection.Artists, YLibrarySectionType.Dislikes));
#endregion Дизлайки
#endregion
#region Добавление в списки лайков/дизлайков
#region Добавление/удаление
/// <summary>
/// Добавить трек в список лайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <returns></returns>
public Task<YResponse<YPlaylist>> AddTrackLikeAsync(AuthStorage storage, YTrack track)
{
return new YLibraryAddBuilder<YPlaylist>(api, storage)
.Build((track.GetKey().ToString(), YLibrarySection.Tracks, YLibrarySectionType.Likes))
.GetResponseAsync();
}
public Task<int?> AddTrackLikeAsync(YTrack track)
=> new YLibraryAddBuilder<YRevision>(Api).ExecuteAsync((track.Id, YLibrarySection.Tracks, YLibrarySectionType.Likes))
.ContinueWith(t => t.Result?.Revision);
/// <summary>
/// Удалить трек из списка лайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <returns></returns>
public Task<YResponse<YRevision>> RemoveTrackLikeAsync(AuthStorage storage, YTrack track)
{
return new YLibraryRemoveBuilder<YRevision>(api, storage)
.Build((track.GetKey().ToString(), YLibrarySection.Tracks, YLibrarySectionType.Likes))
.GetResponseAsync();
}
public Task<int?> RemoveTrackLikeAsync(YTrack track)
=> new YLibraryRemoveBuilder<YRevision>(Api).ExecuteAsync((track.Id, YLibrarySection.Tracks, YLibrarySectionType.Likes))
.ContinueWith(t => t.Result?.Revision);
/// <summary>
/// Добавить трек в список дизлайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <returns></returns>
public Task<YResponse<YRevision>> AddTrackDislikeAsync(AuthStorage storage, YTrack track)
{
return new YLibraryAddBuilder<YRevision>(api, storage)
.Build((track.GetKey().ToString(), YLibrarySection.Tracks, YLibrarySectionType.Dislikes))
.GetResponseAsync();
}
public Task<int?> AddTrackDislikeAsync(YTrack track)
=> new YLibraryAddBuilder<YRevision>(Api).ExecuteAsync((track.Id, YLibrarySection.Tracks, YLibrarySectionType.Dislikes))
.ContinueWith(t => t.Result?.Revision);
/// <summary>
/// Удалить трек из списка дизлайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <returns></returns>
public Task<YResponse<YRevision>> RemoveTrackDislikeAsync(AuthStorage storage, YTrack track)
{
return new YLibraryRemoveBuilder<YRevision>(api, storage)
.Build((track.GetKey().ToString(), YLibrarySection.Tracks, YLibrarySectionType.Dislikes))
.GetResponseAsync();
}
public Task<int?> RemoveTrackDislikeAsync(YTrack track)
=> new YLibraryRemoveBuilder<YRevision>(Api).ExecuteAsync((track.Id, YLibrarySection.Tracks, YLibrarySectionType.Dislikes))
.ContinueWith(t => t.Result?.Revision);
/// <summary>
/// Добавить альбом в список лайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="album">Альбом</param>
/// <returns></returns>
public Task<YResponse<string>> AddAlbumLikeAsync(AuthStorage storage, YAlbum album)
{
return new YLibraryAddBuilder<string>(api, storage)
.Build((album.Id, YLibrarySection.Albums, YLibrarySectionType.Likes))
.GetResponseAsync();
}
public Task<string?> AddAlbumLikeAsync(YAlbum album)
=> new YLibraryAddBuilder<string>(Api).ExecuteAsync((album.Id, YLibrarySection.Albums, YLibrarySectionType.Likes));
/// <summary>
/// Удалить альбом из списка лайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="album">Альбом</param>
/// <returns></returns>
public Task<YResponse<string>> RemoveAlbumLikeAsync(AuthStorage storage, YAlbum album)
{
return new YLibraryRemoveBuilder<string>(api, storage)
.Build((album.Id, YLibrarySection.Albums, YLibrarySectionType.Likes))
.GetResponseAsync();
}
public Task<string?> RemoveAlbumLikeAsync(YAlbum album)
=> new YLibraryRemoveBuilder<string>(Api).ExecuteAsync((album.Id, YLibrarySection.Albums, YLibrarySectionType.Likes));
/// <summary>
/// Добавить исполнителя в список лайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="artist">Исполнитель</param>
/// <returns></returns>
public Task<YResponse<string>> AddArtistLikeAsync(AuthStorage storage, YArtist artist)
{
return new YLibraryAddBuilder<string>(api, storage)
.Build((artist.Id, YLibrarySection.Artists, YLibrarySectionType.Likes))
.GetResponseAsync();
}
public Task<string?> AddArtistLikeAsync(YArtist artist)
=> new YLibraryAddBuilder<string>(Api).ExecuteAsync((artist.Id, YLibrarySection.Artists, YLibrarySectionType.Likes));
/// <summary>
/// Удалить исполнителя из списка лайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="artist">Исполнитель</param>
/// <returns></returns>
public Task<YResponse<string>> RemoveArtistLikeAsync(AuthStorage storage, YArtist artist)
{
return new YLibraryRemoveBuilder<string>(api, storage)
.Build((artist.Id, YLibrarySection.Artists, YLibrarySectionType.Likes))
.GetResponseAsync();
}
public Task<string?> RemoveArtistLikeAsync(YArtist artist)
=> new YLibraryRemoveBuilder<string>(Api).ExecuteAsync((artist.Id, YLibrarySection.Artists, YLibrarySectionType.Likes));
/// <summary>
/// Добавить плейлист в список лайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="playlist">Плейлист</param>
/// <returns></returns>
public Task<YResponse<string>> AddPlaylistLikeAsync(AuthStorage storage, YPlaylist playlist)
{
return new YLibraryAddBuilder<string>(api, storage)
.Build((playlist.GetKey().ToString(), YLibrarySection.Playlists, YLibrarySectionType.Likes))
.GetResponseAsync();
}
public Task<string?> AddPlaylistLikeAsync(YPlaylist playlist)
=> new YLibraryAddBuilder<string>(Api).ExecuteAsync((playlist.GetKey().ToString(), YLibrarySection.Playlists, YLibrarySectionType.Likes));
/// <summary>
/// Удалить плейлист из списка лайкнутых
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="playlist">Плейлист</param>
/// <returns></returns>
public Task<YResponse<string>> RemovePlaylistLikeAsync(AuthStorage storage, YPlaylist playlist)
{
return new YLibraryRemoveBuilder<string>(api, storage)
.Build((playlist.GetKey().ToString(), YLibrarySection.Playlists, YLibrarySectionType.Likes))
.GetResponseAsync();
}
public Task<string?> RemovePlaylistLikeAsync(YPlaylist playlist)
=> new YLibraryRemoveBuilder<string>(Api).ExecuteAsync((playlist.GetKey().ToString(), YLibrarySection.Playlists, YLibrarySectionType.Likes));
#endregion Добавление/удаление в списки лайков/дизлайков
#endregion
#region Получение списка "Вы недавно слушали"
public Task<YResponse<YRecentlyListenedContext>> GetRecentlyListenedAsync(AuthStorage storage, IEnumerable<YPlayContextType> contextTypes, int trackCount, int contextCount)
{
return new YGetLibraryRecentlyListenedBuilder(api, storage)
.Build((contextTypes, trackCount, contextCount))
.GetResponseAsync();
}
#endregion Получение списка "Вы недавно слушали"
#region Недавно прослушанное
public Task<YRecentlyListenedContext?> GetRecentlyListenedAsync(
IEnumerable<YPlayContextType> contextTypes,
int trackCount = 50,
int contextCount = 10)
=> new YGetLibraryRecentlyListenedBuilder(Api).ExecuteAsync((contextTypes, trackCount, contextCount));
#endregion
}

View File

@@ -1,20 +1,13 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Pins;
using YandexMusic.API.Requests.Pins;
namespace YandexMusic.API;
/// <summary>API для взаимодействия с закреплёнными объектами (пинами).</summary>
/// <summary>API для работы с закреплёнными объектами (пинами).</summary>
public class YPinsAPI : YCommonAPI
{
/// <summary>Инициализирует новый экземпляр API пинов.</summary>
/// <param name="yandex">Экземпляр основного API.</param>
public YPinsAPI(YandexMusicApi yandex) : base(yandex) { }
public YPinsAPI(YandexMusicApi api) : base(api) { }
/// <summary>Получает список закреплённых объектов.</summary>
/// <param name="storage">Хранилище авторизации.</param>
/// <returns>Ответ API со списком пинов.</returns>
public Task<YResponse<YPins>> GetAsync(AuthStorage storage)
=> new YGetPinsBuilder(api, storage).Build(null!).GetResponseAsync();
public Task<YPins?> GetAsync()
=> new YGetPinsBuilder(Api).ExecuteAsync(null!);
}

View File

@@ -1,5 +1,3 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Landing;
using YandexMusic.API.Models.Landing.Entity.Entities;
using YandexMusic.API.Models.Playlist;
@@ -8,143 +6,114 @@ using YandexMusic.API.Requests.Playlist;
namespace YandexMusic.API;
/// <summary>API для взаимодействия с плейлистами.</summary>
/// <summary>API для работы с плейлистами.</summary>
public class YPlaylistAPI : YCommonAPI
{
/// <summary>Инициализирует новый экземпляр API плейлистов.</summary>
/// <param name="yandex">Экземпляр основного API.</param>
public YPlaylistAPI(YandexMusicApi yandex) : base(yandex) { }
public YPlaylistAPI(YandexMusicApi api) : base(api) { }
/// <summary>Получает список персональных плейлистов с главной страницы.</summary>
/// <param name="storage">Хранилище авторизации.</param>
/// <returns>Список ответов с плейлистами.</returns>
public async Task<List<YResponse<YPlaylist>>> GetPersonalPlaylistsAsync(AuthStorage storage)
public async Task<List<YPlaylist>> GetPersonalPlaylistsAsync()
{
var landing = await api.Landing.GetAsync(storage, YLandingBlockType.PersonalPlaylists);
var block = landing.Result?.Blocks?.FirstOrDefault(b => b.Type == YLandingBlockType.PersonalPlaylists);
var landing = await Api.Landing.GetAsync(YLandingBlockType.PersonalPlaylists);
var block = landing?.Blocks?.FirstOrDefault(b => b.Type == YLandingBlockType.PersonalPlaylists);
if (block?.Entities == null)
return new List<YResponse<YPlaylist>>();
return new List<YPlaylist>();
var tasks = block.Entities
.OfType<YLandingEntityPersonalPlaylist>()
.Select(e => api.Playlist.GetAsync(storage, e.Data?.Data));
.Select(e => GetAsync(e.Data?.Data?.Owner?.Uid ?? Api.Storage.User.Uid, e.Data?.Data?.Kind ?? ""))
.Where(t => t != null)
.ToList();
return new List<YResponse<YPlaylist>>(await Task.WhenAll(tasks));
var results = await Task.WhenAll(tasks);
return results.Where(p => p != null).ToList()!;
}
/// <summary>Получает избранные плейлисты.</summary>
public Task<YResponse<List<YPlaylist>>> FavoritesAsync(AuthStorage storage)
=> new YGetPlaylistFavoritesBuilder(api, storage).Build(null!).GetResponseAsync();
public Task<YPlaylist?> GetAsync(string user, string kind)
=> new YGetPlaylistBuilder(Api).ExecuteAsync((user, kind));
/// <summary>Получает плейлист дня.</summary>
public Task<YResponse<YPlaylist>> OfTheDayAsync(AuthStorage storage)
=> GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.PlaylistOfTheDay);
public Task<YPlaylist?> GetAsync(string uuid)
=> new YGetPlaylistByUuidBuilder(Api).ExecuteAsync(uuid);
/// <summary>Получает плейлист «Дежавю».</summary>
public Task<YResponse<YPlaylist>> DejaVuAsync(AuthStorage storage)
=> GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.NeverHeard);
public Task<YPlaylist?> GetAsync(YPlaylist playlist)
=> GetAsync(playlist.Owner.Uid, playlist.Kind);
/// <summary>Получает плейлист «Премьера».</summary>
public Task<YResponse<YPlaylist>> PremiereAsync(AuthStorage storage)
=> GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.RecentTracks);
public Task<List<YPlaylist>?> GetAsync(IEnumerable<(string user, string kind)> ids)
=> new YGetPlaylistsBuilder(Api).ExecuteAsync(ids);
/// <summary>Получает плейлист «Тайник».</summary>
public Task<YResponse<YPlaylist>> MissedAsync(AuthStorage storage)
=> GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.MissedLikes);
public Task<List<YPlaylist>?> FavoritesAsync()
=> new YGetPlaylistFavoritesBuilder(Api).ExecuteAsync(null!);
/// <summary>Получает плейлист «Кинопоиск».</summary>
public Task<YResponse<YPlaylist>> KinopoiskAsync(AuthStorage storage)
=> GetPersonalPlaylistAsync(storage, YGeneratedPlaylistType.Kinopoisk);
public async Task<YPlaylist?> OfTheDayAsync()
=> (await GetPersonalPlaylistsAsync()).FirstOrDefault(p => p?.GeneratedPlaylistType == YGeneratedPlaylistType.PlaylistOfTheDay.ToString());
private async Task<YResponse<YPlaylist>> GetPersonalPlaylistAsync(AuthStorage storage, YGeneratedPlaylistType type)
{
var list = await GetPersonalPlaylistsAsync(storage);
return list.FirstOrDefault(e => string.Equals(e.Result?.GeneratedPlaylistType, type.ToString(), StringComparison.CurrentCultureIgnoreCase))
?? throw new Exception($"Плейлист типа {type} не найден.");
}
public async Task<YPlaylist?> DejaVuAsync()
=> (await GetPersonalPlaylistsAsync()).FirstOrDefault(p => p?.GeneratedPlaylistType == YGeneratedPlaylistType.NeverHeard.ToString());
/// <summary>Получает плейлист по идентификатору пользователя и типа.</summary>
public Task<YResponse<YPlaylist>> GetAsync(AuthStorage storage, string user, string kind)
=> new YGetPlaylistBuilder(api, storage).Build((user, kind)).GetResponseAsync();
public async Task<YPlaylist?> PremiereAsync()
=> (await GetPersonalPlaylistsAsync()).FirstOrDefault(p => p?.GeneratedPlaylistType == YGeneratedPlaylistType.RecentTracks.ToString());
/// <summary>Получает плейлист по UUID.</summary>
public Task<YResponse<YPlaylist>> GetAsync(AuthStorage storage, string uuid)
=> new YGetPlaylistByUuidBuilder(api, storage).Build(uuid).GetResponseAsync();
public async Task<YPlaylist?> MissedAsync()
=> (await GetPersonalPlaylistsAsync()).FirstOrDefault(p => p?.GeneratedPlaylistType == YGeneratedPlaylistType.MissedLikes.ToString());
/// <summary>Получает несколько плейлистов по списку пар (пользователь, тип).</summary>
public Task<YResponse<List<YPlaylist>>> GetAsync(AuthStorage storage, IEnumerable<(string user, string kind)> ids)
=> new YGetPlaylistsBuilder(api, storage).Build(ids).GetResponseAsync();
public async Task<YPlaylist?> KinopoiskAsync()
=> (await GetPersonalPlaylistsAsync()).FirstOrDefault(p => p?.GeneratedPlaylistType == YGeneratedPlaylistType.Kinopoisk.ToString());
/// <summary>Получает плейлист по объекту плейлиста (обновляет его треки).</summary>
public Task<YResponse<YPlaylist>> GetAsync(AuthStorage storage, YPlaylist playlist)
=> new YGetPlaylistBuilder(api, storage).Build((playlist.Owner.Uid, playlist.Kind)).GetResponseAsync();
public Task<YPlaylist?> CreateAsync(string name)
=> new YPlaylistCreateBuilder(Api).ExecuteAsync(name);
/// <summary>Создаёт новый плейлист с заданным именем.</summary>
public Task<YResponse<YPlaylist>> CreateAsync(AuthStorage storage, string name)
=> new YPlaylistCreateBuilder(api, storage).Build(name).GetResponseAsync();
public Task<YPlaylist?> RenameAsync(string kind, string name)
=> new YPlaylistRenameBuilder(Api).ExecuteAsync((kind, name));
/// <summary>Переименовывает плейлист.</summary>
public Task<YResponse<YPlaylist>> RenameAsync(AuthStorage storage, string kind, string name)
=> new YPlaylistRenameBuilder(api, storage).Build((kind, name)).GetResponseAsync();
public Task<YPlaylist?> RenameAsync(YPlaylist playlist, string name)
=> RenameAsync(playlist.Kind, name);
/// <summary>Переименовывает плейлист.</summary>
public Task<YResponse<YPlaylist>> RenameAsync(AuthStorage storage, YPlaylist playlist, string name)
=> RenameAsync(storage, playlist.Kind, name);
/// <summary>Удаляет плейлист.</summary>
public async Task<bool> DeleteAsync(AuthStorage storage, string kind)
public async Task<bool> DeleteAsync(string kind)
{
try
{
await new YPlaylistRemoveBuilder(api, storage).Build(kind).GetResponseAsync();
await new YPlaylistRemoveBuilder(Api).ExecuteAsync(kind);
return true;
}
catch (Exception ex)
catch
{
// Логирование ошибки можно добавить через ILogger
return false;
}
}
/// <summary>Удаляет плейлист.</summary>
public Task<bool> DeleteAsync(AuthStorage storage, YPlaylist playlist)
=> DeleteAsync(storage, playlist.Kind);
public Task<bool> DeleteAsync(YPlaylist playlist)
=> DeleteAsync(playlist.Kind);
/// <summary>Добавляет треки в начало плейлиста.</summary>
public async Task<YResponse<YPlaylist>> InsertTracksAsync(AuthStorage storage, YPlaylist playlist, IEnumerable<YTrack> tracks)
public async Task<YPlaylist?> InsertTracksAsync(YPlaylist playlist, IEnumerable<YTrack> tracks)
{
var change = await ChangePlaylistAsync(storage, playlist, new List<YPlaylistChange>
var change = await new YPlaylistChangeBuilder(Api).ExecuteAsync((playlist, new[]
{
new()
new YPlaylistChange
{
Operation = YPlaylistChangeType.Insert,
At = 0,
Tracks = tracks.Select(t => t.GetKey())
}
});
return await GetAsync(storage, change.Result);
}));
return change != null ? await GetAsync(change) : null;
}
/// <summary>Удаляет треки из плейлиста.</summary>
public Task<YResponse<YPlaylist>> DeleteTracksAsync(AuthStorage storage, YPlaylist playlist, IEnumerable<YTrack> tracks)
public async Task<YPlaylist?> DeleteTracksAsync(YPlaylist playlist, IEnumerable<YTrack> tracks)
{
var distinctTracks = tracks.Distinct().ToList();
var changes = distinctTracks
var indices = distinctTracks
.Select(t => playlist.Tracks?.FindIndex(ct => ct.Track?.GetKey() == t.GetKey()) ?? -1)
.Where(i => i != -1)
.Select(i => new YPlaylistChange
{
Operation = YPlaylistChangeType.Delete,
From = i,
To = i + 1,
Tracks = new List<YTrackAlbumPair> { playlist.Tracks![i].Track!.GetKey() }
})
.ToList();
return ChangePlaylistAsync(storage, playlist, changes);
}
var changes = indices.Select(i => new YPlaylistChange
{
Operation = YPlaylistChangeType.Delete,
From = i,
To = i + 1,
Tracks = new[] { playlist.Tracks![i].Track!.GetKey() }
});
private Task<YResponse<YPlaylist>> ChangePlaylistAsync(AuthStorage storage, YPlaylist playlist, IEnumerable<YPlaylistChange> changes)
=> new YPlaylistChangeBuilder(api, storage).Build((playlist, changes)).GetResponseAsync();
var change = await new YPlaylistChangeBuilder(Api).ExecuteAsync((playlist, changes));
return change != null ? await GetAsync(change) : null;
}
}

View File

@@ -1,72 +1,22 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Queue;
using YandexMusic.API.Requests.Queue;
namespace YandexMusic.API;
/// <summary>
/// API для взаимодействия с очередями
/// </summary>
public partial class YQueueAPI : YCommonAPI
/// <summary>API для работы с очередями воспроизведения.</summary>
public class YQueueAPI : YCommonAPI
{
public YQueueAPI(YandexMusicApi yandex) : base(yandex)
{
}
public YQueueAPI(YandexMusicApi api) : base(api) { }
/// <summary>
/// Получение всех очередей треков с разных устройств для синхронизации между ними
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="device">Устройство</param>
/// <returns></returns>
public Task<YResponse<YQueueItemsContainer>> ListAsync(AuthStorage storage, string? device = null)
{
return new YQueuesListBuilder(api, storage)
.Build(device)
.GetResponseAsync();
}
public Task<YQueueItemsContainer?> ListAsync(string? device = null)
=> new YQueuesListBuilder(Api, device).ExecuteAsync(null!);
/// <summary>
/// Получение очереди
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="queueId">Идентификатор очереди</param>
/// <returns></returns>
public Task<YResponse<YQueue>> GetAsync(AuthStorage storage, string queueId)
{
return new YGetQueueBuilder(api, storage)
.Build(queueId)
.GetResponseAsync();
}
public Task<YQueue?> GetAsync(string queueId)
=> new YGetQueueBuilder(Api).ExecuteAsync(queueId);
/// <summary>
/// Создание новой очереди треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="queue">Очередь треков</param>
/// <param name="device">Устройство</param>
/// <returns></returns>
public Task<YResponse<YNewQueue>> CreateAsync(AuthStorage storage, YQueue queue, string? device = null)
{
return new YQueueCreateBuilder(api, storage, device)
.Build(queue)
.GetResponseAsync();
}
public Task<YNewQueue?> CreateAsync(YQueue queue, string? device = null)
=> new YQueueCreateBuilder(Api, device).ExecuteAsync(queue);
/// <summary>
/// Установка текущего индекса проигрываемого трека в очереди треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="queueId">Идентификатор очереди</param>
/// <param name="currentIndex">Текущий индекс</param>
/// <param name="isInteractive">Флаг интерактивности</param>
/// <param name="device">Устройство</param>
/// <returns></returns>
public Task<YResponse<YUpdatedQueue>> UpdatePositionAsync(AuthStorage storage, string queueId, int currentIndex, bool isInteractive, string device = null)
{
return new YQueueUpdatePositionBuilder(api, storage, device)
.Build((queueId, currentIndex, isInteractive))
.GetResponseAsync();
}
}
public Task<YUpdatedQueue?> UpdatePositionAsync(string queueId, int currentIndex, bool isInteractive, string? device = null)
=> new YQueueUpdatePositionBuilder(Api, device).ExecuteAsync((queueId, currentIndex, isInteractive));
}

View File

@@ -1,113 +1,37 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Radio;
using YandexMusic.API.Models.Track;
using YandexMusic.API.Requests.Radio;
namespace YandexMusic.API;
/// <summary>
/// API для взаимодействия с радио
/// </summary>
public partial class YRadioAPI : YCommonAPI
/// <summary>API для работы с радио.</summary>
public class YRadioAPI : YCommonAPI
{
public YRadioAPI(YandexMusicApi yandex) : base(yandex)
{
}
public YRadioAPI(YandexMusicApi api) : base(api) { }
/// <summary>
/// Получение списка рекомендованных радиостанций
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<YStationsDashboard>> GetStationsDashboardAsync(AuthStorage storage)
{
return new YGetStationsDashboardBuilder(api, storage)
.Build(null)
.GetResponseAsync();
}
public Task<YStationsDashboard?> GetStationsDashboardAsync()
=> new YGetStationsDashboardBuilder(Api).ExecuteAsync(null!);
/// <summary>
/// Получение списка радиостанций
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<List<YStation>>> GetStationsAsync(AuthStorage storage)
{
return new YGetStationsBuilder(api, storage)
.Build(null)
.GetResponseAsync();
}
public Task<List<YStation>?> GetStationsAsync()
=> new YGetStationsBuilder(Api).ExecuteAsync(null!);
/// <summary>
/// Получение информации о радиостанции
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="type">Тип</param>
/// <param name="tag">Тэг</param>
/// <returns></returns>
public Task<YResponse<List<YStation>>> GetStationAsync(AuthStorage storage, string type, string tag)
{
return new YGetStationBuilder(api, storage)
.Build((type, tag))
.GetResponseAsync();
}
public Task<List<YStation>?> GetStationAsync(string type, string tag)
=> new YGetStationBuilder(Api).ExecuteAsync((type, tag));
/// <summary>
/// Получение информации о радиостанции
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="id">Идентификатор станции</param>
/// <returns></returns>
public Task<YResponse<List<YStation>>> GetStationAsync(AuthStorage storage, YStationId id)
{
return GetStationAsync(storage, id.Type, id.Tag);
}
public Task<List<YStation>?> GetStationAsync(YStationId id)
=> GetStationAsync(id.Type, id.Tag);
/// <summary>
/// Получение последовательности треков радиостанции
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="station">Радиостанция</param>
/// <param name="prevTrackId">Идентификатор предыдущего трека</param>
/// <returns></returns>
public Task<YResponse<YStationSequence>> GetStationTracksAsync(AuthStorage storage, YStation station, string prevTrackId = "")
{
return new YGetStationTracksBuilder(api, storage)
.Build((station.Station, prevTrackId))
.GetResponseAsync();
}
/// <summary>
/// Установка настроек подбора треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="station">Радиостанция</param>
/// <param name="settings">Настройки</param>
/// <returns></returns>
public Task<YResponse<string>> SetStationSettings2Async(AuthStorage storage, YStation station, YStationSettings2 settings)
{
return new YSetSettings2Builder(api, storage)
.Build((station.Station, settings))
.GetResponseAsync();
}
/// <summary>
/// Отправка обратной связи на действия при прослушивании радио
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="station">Радиостанция</param>
/// <param name="type">Тип обратной связи</param>
/// <param name="track">Трек</param>
/// <param name="batchId">Уникальный идентификатор партии треков. Возвращается при получении треков</param>
/// <param name="totalPlayedSeconds">Сколько было проиграно секунд трека перед действием</param>
/// <returns></returns>
public Task<string> SendStationFeedBackAsync(AuthStorage storage, YStation station, YStationFeedbackType type, YTrack track = null, string batchId = "", double totalPlayedSeconds = 0)
{
return new YSetStationFeedbackBuilder(api, storage)
.Build((type, station, track, batchId, totalPlayedSeconds))
.GetResponseAsync();
}
public Task<YStationSequence?> GetStationTracksAsync(YStation station, string prevTrackId = "")
=> new YGetStationTracksBuilder(Api).ExecuteAsync((station.Station, prevTrackId));
public Task<string?> SetStationSettings2Async(YStation station, YStationSettings2 settings)
=> new YSetSettings2Builder(Api).ExecuteAsync((station.Station, settings));
public Task<string?> SendStationFeedbackAsync(
YStation station,
YStationFeedbackType type,
YTrack? track = null,
string batchId = "",
double totalPlayedSeconds = 0)
=> new YSetStationFeedbackBuilder(Api).ExecuteAsync((type, station, track, batchId, totalPlayedSeconds));
}

View File

@@ -1,139 +1,38 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Search;
using YandexMusic.API.Requests.Search;
namespace YandexMusic.API;
/// <summary>
/// API для поиска
/// </summary>
public partial class YSearchAPI : YCommonAPI
/// <summary>API для поиска.</summary>
public class YSearchAPI : YCommonAPI
{
public YSearchAPI(YandexMusicApi api) : base(api) { }
public YSearchAPI(YandexMusicApi yandex) : base(yandex)
{
}
public Task<YSearch?> TrackAsync(string trackName, int page = 0, int pageSize = 20)
=> SearchAsync(trackName, YSearchType.Track, page, pageSize);
/// <summary>
/// Поиск по трекам
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackName">Имя трека</param>
/// <param name="pageNumber">Номер страницы</param>
/// <param name="pageSize">Размер страницы</param>
/// <returns></returns>
public Task<YResponse<YSearch>> TrackAsync(AuthStorage storage, string trackName, int pageNumber = 0, int pageSize = 20)
{
return SearchAsync(storage, trackName, YSearchType.Track, pageNumber, pageSize);
}
public Task<YSearch?> AlbumsAsync(string albumName, int page = 0, int pageSize = 20)
=> SearchAsync(albumName, YSearchType.Album, page, pageSize);
/// <summary>
/// Поиск по альбомам
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="albumName">Имя альбома</param>
/// <param name="pageNumber">Номер страницы</param>
/// <param name="pageSize">Размер страницы</param>
/// <returns></returns>
public Task<YResponse<YSearch>> AlbumsAsync(AuthStorage storage, string albumName, int pageNumber = 0, int pageSize = 20)
{
return SearchAsync(storage, albumName, YSearchType.Album, pageNumber, pageSize);
}
public Task<YSearch?> ArtistAsync(string artistName, int page = 0, int pageSize = 20)
=> SearchAsync(artistName, YSearchType.Artist, page, pageSize);
/// <summary>
/// Поиск по артисту
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="artistName">Имя артиста</param>
/// <param name="pageNumber">Номер страницы</param>
/// <param name="pageSize">Размер страницы</param>
/// <returns></returns>
public Task<YResponse<YSearch>> ArtistAsync(AuthStorage storage, string artistName, int pageNumber = 0, int pageSize = 20)
{
return SearchAsync(storage, artistName, YSearchType.Artist, pageNumber, pageSize);
}
public Task<YSearch?> PlaylistAsync(string playlistName, int page = 0, int pageSize = 20)
=> SearchAsync(playlistName, YSearchType.Playlist, page, pageSize);
/// <summary>
/// Поиск по плейлистам
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="playlistName">Имя плейлиста</param>
/// <param name="pageNumber">Номер страницы</param>
/// <param name="pageSize">Размер страницы</param>
/// <returns></returns>
public Task<YResponse<YSearch>> PlaylistAsync(AuthStorage storage, string playlistName, int pageNumber = 0, int pageSize = 20)
{
return SearchAsync(storage, playlistName, YSearchType.Playlist, pageNumber, pageSize);
}
public Task<YSearch?> PodcastEpisodeAsync(string podcastName, int page = 0, int pageSize = 20)
=> SearchAsync(podcastName, YSearchType.PodcastEpisode, page, pageSize);
/// <summary>
/// Поиск по плейлистам
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="podcastName">Имя подкаста</param>
/// <param name="pageNumber">Номер страницы</param>
/// <param name="pageSize">Размер страницы</param>
/// <returns></returns>
public Task<YResponse<YSearch>> PodcastEpisodeAsync(AuthStorage storage, string podcastName, int pageNumber = 0, int pageSize = 20)
{
return SearchAsync(storage, podcastName, YSearchType.PodcastEpisode, pageNumber, pageSize);
}
public Task<YSearch?> VideosAsync(string videoName, int page = 0, int pageSize = 20)
=> SearchAsync(videoName, YSearchType.Video, page, pageSize);
/// <summary>
/// Поиск по видео
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="videoName">Имя видео</param>
/// <param name="pageNumber">Номер страницы</param>
/// <param name="pageSize">Размер страницы</param>
/// <returns></returns>
public Task<YResponse<YSearch>> VideosAsync(AuthStorage storage, string videoName, int pageNumber = 0, int pageSize = 20)
{
return SearchAsync(storage, videoName, YSearchType.Video, pageNumber, pageSize);
}
/// <summary>
/// Поиск по пользователям
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="userName">Имя пользователя</param>
/// <param name="pageNumber">Номер страницы</param>
/// <param name="pageSize">Размер страницы</param>
/// <returns></returns>
public Task<YResponse<YSearch>> UsersAsync(AuthStorage storage, string userName, int pageNumber = 0, int pageSize = 20)
{
return SearchAsync(storage, userName, YSearchType.User, pageNumber, pageSize);
}
/// <summary>
/// Поиск
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="searchText">Поисковый запрос</param>
/// <param name="searchType">Тип поиска</param>
/// <param name="page">Страница</param>
/// <param name="pageSize">Размер страницы</param>
/// <returns></returns>
public Task<YResponse<YSearch>> SearchAsync(AuthStorage storage, string searchText, YSearchType searchType, int page = 0, int pageSize = 20)
{
return new YSearchBuilder(api, storage)
.Build((searchText, searchType, page, pageSize))
.GetResponseAsync();
}
/// <summary>
/// Подсказка
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="searchText">Поисковый запрос</param>
/// <returns></returns>
public Task<YResponse<YSearchSuggest>> SuggestAsync(AuthStorage storage, string searchText)
{
return new YSearchSuggestBuilder(api, storage)
.Build(searchText)
.GetResponseAsync();
}
public Task<YSearch?> UsersAsync(string userName, int page = 0, int pageSize = 20)
=> SearchAsync(userName, YSearchType.User, page, pageSize);
public Task<YSearch?> SearchAsync(string searchText, YSearchType searchType, int page = 0, int pageSize = 20)
=> new YSearchBuilder(Api).ExecuteAsync((searchText, searchType, page, pageSize));
public Task<YSearchSuggest?> GetSearchSuggestionsAsync(string searchText)
=> new YSearchSuggestBuilder(Api).ExecuteAsync(searchText);
}

View File

@@ -1,307 +1,110 @@
using System.Security.Cryptography;
using System.Text;
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Track;
using YandexMusic.API.Requests.Track;
namespace YandexMusic.API;
/// <summary>
/// API для взаимодействия с треками
/// </summary>
public partial class YTrackAPI : YCommonAPI
/// <summary>API для работы с треками (получение, загрузка, метаданные).</summary>
public class YTrackAPI : YCommonAPI
{
#region Вспомогательные функции
public YTrackAPI(YandexMusicApi api) : base(api) { }
private string BuildLinkForDownload(YTrackDownloadInfo mainDownloadResponse, YStorageDownloadFile storageDownload)
private static string BuildDownloadLink(YTrackDownloadInfo info, YStorageDownloadFile storageDownload)
{
string path = storageDownload.Path;
string host = storageDownload.Host;
string ts = storageDownload.Ts;
string s = storageDownload.S;
string codec = mainDownloadResponse.Codec;
var path = storageDownload.Path;
var host = storageDownload.Host;
var ts = storageDownload.Ts;
var s = storageDownload.S;
var codec = info.Codec;
string secret = $"XGRlBW9FXlekgbPrRHuSiA{path.Substring(1, path.Length - 1)}{s}";
MD5 md5 = MD5.Create();
byte[] md5Hash = md5.ComputeHash(Encoding.UTF8.GetBytes(secret));
HMACSHA1 hmacsha1 = new();
byte[] hmasha1Hash = hmacsha1.ComputeHash(md5Hash);
string sign = BitConverter.ToString(hmasha1Hash).Replace("-", "").ToLower();
string link = $"https://{host}/get-{codec}/{sign}/{ts}{path}";
return link;
var secret = $"XGRlBW9FXlekgbPrRHuSiA{path[1..]}{s}";
var md5Hash = MD5.HashData(Encoding.UTF8.GetBytes(secret));
var hmacsha1 = new HMACSHA1(md5Hash);
var sign = BitConverter.ToString(hmacsha1.ComputeHash(md5Hash)).Replace("-", "").ToLower();
return $"https://{host}/get-{codec}/{sign}/{ts}{path}";
}
#endregion Вспомогательные функции
public Task<YTrack?> GetAsync(string trackId)
=> GetAsync(trackId);
public Task<List<YTrack>?> GetAsync(IEnumerable<string> trackIds)
=> new YGetTracksBuilder(Api).ExecuteAsync(trackIds);
public Task<List<YTrackDownloadInfo>?> GetMetadataForDownloadAsync(string trackKey, bool direct = false)
=> new YTrackDownloadInfoBuilder(Api).ExecuteAsync((trackKey, direct));
public Task<List<YTrackDownloadInfo>?> GetMetadataForDownloadAsync(YTrack track, bool direct = false)
=> GetMetadataForDownloadAsync(track.GetKey().ToString(), direct);
public YTrackAPI(YandexMusicApi yandex) : base(yandex)
public Task<YStorageDownloadFile?> GetDownloadFileInfoAsync(YTrackDownloadInfo metadataInfo)
=> new YStorageDownloadFileBuilder(Api).ExecuteAsync(metadataInfo.DownloadInfoUrl);
public async Task<string?> GetFileLinkAsync(string trackKey)
{
var meta = await GetMetadataForDownloadAsync(trackKey);
var info = meta?.OrderByDescending(i => i.BitrateInKbps).FirstOrDefault(m => m.Codec == "mp3");
if (info == null) return null;
var storageDownload = await GetDownloadFileInfoAsync(info);
if (storageDownload == null) return null;
return BuildDownloadLink(info, storageDownload);
}
/// <summary>
/// Получение треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackId">Идентификатор трека</param>
/// <returns></returns>
public Task<YResponse<List<YTrack>>> GetAsync(AuthStorage storage, string trackId)
public Task<string?> GetFileLinkAsync(YTrack track)
=> GetFileLinkAsync(track.GetKey().ToString());
public async Task ExtractToFileAsync(string trackKey, string filePath)
{
return new YGetTracksBuilder(api, storage)
.Build(new[] { trackId })
.GetResponseAsync();
var url = await GetFileLinkAsync(trackKey);
if (string.IsNullOrEmpty(url)) throw new Exception("Не удалось получить ссылку на трек");
using var response = await Api.HttpClient.GetAsync(url);
await using var fs = File.Create(filePath);
await response.Content.CopyToAsync(fs);
}
/// <summary>
/// Получение треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackIds">Идентификаторы треков</param>
/// <returns></returns>
public Task<YResponse<List<YTrack>>> GetAsync(AuthStorage storage, IEnumerable<string> trackIds)
public Task ExtractToFileAsync(YTrack track, string filePath)
=> ExtractToFileAsync(track.GetKey().ToString(), filePath);
public async Task<byte[]> ExtractDataAsync(string trackKey)
{
return new YGetTracksBuilder(api, storage)
.Build(trackIds)
.GetResponseAsync();
var url = await GetFileLinkAsync(trackKey);
if (string.IsNullOrEmpty(url)) throw new Exception("Не удалось получить ссылку на трек");
return await Api.HttpClient.GetByteArrayAsync(url);
}
/// <summary>
/// Получение метаданных для загрузки
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackKey">Ключ трека в формате {идентифактор трека:идентификатор альбома}</param>
/// <param name="direct">Должен ли ответ содержать прямую ссылку на загрузку</param>
/// <returns></returns>
public Task<YResponse<List<YTrackDownloadInfo>>> GetMetadataForDownloadAsync(AuthStorage storage, string trackKey, bool direct = false)
public Task<byte[]> ExtractDataAsync(YTrack track)
=> ExtractDataAsync(track.GetKey().ToString());
public async Task<Stream> ExtractStreamAsync(string trackKey, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead)
{
return new YTrackDownloadInfoBuilder(api, storage)
.Build((trackKey, direct))
.GetResponseAsync();
var url = await GetFileLinkAsync(trackKey);
if (string.IsNullOrEmpty(url)) throw new Exception("Не удалось получить ссылку на трек");
var response = await Api.HttpClient.GetAsync(url, completionOption);
return await response.Content.ReadAsStreamAsync();
}
/// <summary>
/// Получение метаданных для загрузки
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <param name="direct">Должен ли ответ содержать прямую ссылку на загрузку</param>
/// <returns></returns>
public Task<YResponse<List<YTrackDownloadInfo>>> GetMetadataForDownloadAsync(AuthStorage storage, YTrack track, bool direct = false)
{
return GetMetadataForDownloadAsync(storage, track.GetKey().ToString(), direct);
}
public Task<Stream> ExtractStreamAsync(YTrack track, HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead)
=> ExtractStreamAsync(track.GetKey().ToString(), completionOption);
/// <summary>
/// Получение информации для формирования ссылки для загрузки
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="metadataInfo">Метаданные для загрузки</param>
/// <returns></returns>
public Task<YStorageDownloadFile> GetDownloadFileInfoAsync(AuthStorage storage, YTrackDownloadInfo metadataInfo)
{
return new YStorageDownloadFileBuilder(api, storage)
.Build(metadataInfo.DownloadInfoUrl)
.GetResponseAsync();
}
public Task<string?> SendPlayTrackInfoAsync(
YTrack track,
string from,
bool fromCache = false,
string playId = "",
string playlistId = "",
double totalPlayedSeconds = 0,
double endPositionSeconds = 0)
=> new YSendTrackInfoBuilder(Api).ExecuteAsync((track, from, fromCache, playId, playlistId, totalPlayedSeconds, endPositionSeconds));
/// <summary>
/// Получение ссылки для загрузки
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackKey">Ключ трека в формате {идентификатор трека:идентификатор альбома}</param>
/// <returns></returns>
public async Task<string> GetFileLinkAsync(AuthStorage storage, string trackKey)
{
YResponse<List<YTrackDownloadInfo>> meta = await GetMetadataForDownloadAsync(storage, trackKey);
YTrackDownloadInfo info = meta.Result
.OrderByDescending(i => i.BitrateInKbps)
.First(m => m.Codec == "mp3");
YStorageDownloadFile storageDownload = await GetDownloadFileInfoAsync(storage, info);
return BuildLinkForDownload(info, storageDownload);
}
public Task<YTrackSupplement?> GetSupplementAsync(string trackId)
=> new YGetTrackSupplementBuilder(Api).ExecuteAsync(trackId);
/// <summary>
/// Получение ссылки для загрузки
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <returns></returns>
public Task<string> GetFileLinkAsync(AuthStorage storage, YTrack track)
{
return GetFileLinkAsync(storage, track.GetKey().ToString());
}
/// <summary>
/// Отправка текущего состояния прослушиваемого трека
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <param name="from">Наименования клиента, с которого происходит прослушивание</param>
/// <param name="fromCache">Проигрывается ли трек с кеша</param>
/// <param name="playId">Уникальный идентификатор проигрывания</param>
/// <param name="playlistId">Уникальный идентификатор плейлиста, если таковой прослушивается</param>
/// <param name="totalPlayedSeconds">Сколько было всего воспроизведено трека в секундах</param>
/// <param name="endPositionSeconds">Окончательное значение воспроизведенных секунд</param>
/// </summary>
/// <returns></returns>
public Task<string> SendPlayTrackInfoAsync(AuthStorage storage, YTrack track, string from, bool fromCache = false, string playId = "", string playlistId = "", double totalPlayedSeconds = 0, double endPositionSeconds = 0)
{
return new YSendTrackInfoBuilder(api, storage)
.Build((track, from, fromCache, playId, playlistId, totalPlayedSeconds, endPositionSeconds))
.GetResponseAsync();
}
#region GetSupplement
/// <summary>
/// Получение дополнительной информации для трека
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackId">Идентификатор трека</param>
/// <returns></returns>
public Task<YResponse<YTrackSupplement>> GetSupplementAsync(AuthStorage storage, string trackId)
{
return new YGetTrackSupplementBuilder(api, storage)
.Build(trackId)
.GetResponseAsync();
}
/// <summary>
/// Получение дополнительной информации для трека
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <returns></returns>
public Task<YResponse<YTrackSupplement>> GetSupplementAsync(AuthStorage storage, YTrack track)
{
return new YGetTrackSupplementBuilder(api, storage)
.Build(track.GetKey().ToString())
.GetResponseAsync();
}
#endregion GetSupplement
#region GetSimilar
/// <summary>
/// Получение похожих треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackId">Идентификатор трека</param>
/// <returns></returns>
public Task<YResponse<YTrackSimilar>> GetSimilarAsync(AuthStorage storage, string trackId)
{
return new YGetTrackSimilarBuilder(api, storage)
.Build(trackId)
.GetResponseAsync();
}
/// <summary>
/// Получение похожих треков
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <returns></returns>
public Task<YResponse<YTrackSimilar>> GetSimilarAsync(AuthStorage storage, YTrack track)
{
return new YGetTrackSimilarBuilder(api, storage)
.Build(track.GetKey().ToString())
.GetResponseAsync();
}
#endregion GetSimilar
#region Получение данных трека
#region В файл
/// <summary>
/// Выгрузка в файл
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackKey">Ключ трека в формате {идентификатор трека:идентификатор альбома}</param>
/// <param name="filePath">Путь для файла</param>
public async Task ExtractToFileAsync(AuthStorage storage, string trackKey, string filePath)
{
string url = await GetFileLinkAsync(storage, trackKey);
await new DataDownloader(storage).ToFile(url, filePath);
}
/// <summary>
/// Выгрузка в файл
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <param name="filePath">Путь для файла</param>
public Task ExtractToFileAsync(AuthStorage storage, YTrack track, string filePath)
{
return ExtractToFileAsync(storage, track.GetKey().ToString(), filePath);
}
#endregion В файл
#region В массив байт
/// <summary>
/// Получение двоичного массива данных
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackKey">Ключ трека в формате {идентификатор трека:идентификатор альбома}</param>
/// <returns></returns>
public async Task<byte[]> ExtractDataAsync(AuthStorage storage, string trackKey)
{
string url = await GetFileLinkAsync(storage, trackKey);
return await new DataDownloader(storage).AsBytes(url);
}
/// <summary>
/// Получение двоичного массива данных
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <returns></returns>
public Task<byte[]> ExtractDataAsync(AuthStorage storage, YTrack track)
{
return ExtractDataAsync(storage, track.GetKey().ToString());
}
#endregion В массив байт
#region В поток
/// <summary>
/// Получение потока данных
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="trackKey">Ключ трека в формате {идентификатор трека:идентификатор альбома}</param>
/// <param name="httpCompletionOption">Параметры передачи управления при http запросе</param>
/// <returns></returns>
public async Task<Stream> ExtractStreamAsync(AuthStorage storage, string trackKey,
HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead)
{
string url = await GetFileLinkAsync(storage, trackKey);
return await new DataDownloader(storage).AsStream(url, httpCompletionOption);
}
/// <summary>
/// Получение потока данных
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="track">Трек</param>
/// <param name="httpCompletionOption">Параметры передачи управления при http запросе</param>
/// <returns></returns>
public Task<Stream> ExtractStreamAsync(AuthStorage storage, YTrack track,
HttpCompletionOption httpCompletionOption = HttpCompletionOption.ResponseContentRead)
{
return ExtractStreamAsync(storage, track.GetKey().ToString(), httpCompletionOption);
}
#endregion В поток
#endregion Получение данных трека
public Task<YTrackSupplement?> GetSupplementAsync(YTrack track)
=> GetSupplementAsync(track.GetKey().ToString());
public Task<YTrackSimilar?> GetSimilarAsync(string trackId)
=> new YGetTrackSimilarBuilder(Api).ExecuteAsync(trackId);
public Task<YTrackSimilar?> GetSimilarAsync(YTrack track)
=> GetSimilarAsync(track.GetKey().ToString());
}

View File

@@ -1,71 +1,36 @@
using YandexMusic.API.Common;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Models.Playlist;
using YandexMusic.API.Models.Playlist;
using YandexMusic.API.Models.Ugc;
using YandexMusic.API.Requests.Ugc;
namespace YandexMusic.API;
public partial class YUgcAPI : YCommonAPI
/// <summary>API для загрузки пользовательского контента (UGC).</summary>
public class YUgcAPI : YCommonAPI
{
public YUgcAPI(YandexMusicApi yandex) : base(yandex)
{
}
public YUgcAPI(YandexMusicApi api) : base(api) { }
/// <summary>
/// Получение ссылки на загрузчик трека
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="playlist">Плейлист, куда будет загружен трек</param>
/// <param name="fileName">Название файла для загрузки</param>
public Task<YUgcUpload> GetUgcUploadLinkAsync(AuthStorage storage, YPlaylist playlist, string fileName)
{
return new YUgcGetUploadLinkBuilder(api, storage)
.Build((playlist, fileName))
.GetResponseAsync();
}
public Task<YUgcUpload?> GetUgcUploadLinkAsync(YPlaylist playlist, string fileName)
=> new YUgcGetUploadLinkBuilder(Api).ExecuteAsync((playlist, fileName));
/// <summary>
/// Загрузка трека из файла
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="uploadLink">Ссылка на балансировщик для загрузки, можно получить из GetUgcUploadLinkAsync</param>
/// <param name="filePath">Загружаемый файл</param>
public Task<YResponse<string>> UploadUgcTrackAsync(AuthStorage storage, string uploadLink, string filePath)
public async Task<string?> UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, string filePath)
{
if (!File.Exists(filePath))
throw new FileNotFoundException("Файл для загрузки не существует.", filePath);
return UploadUgcTrackAsync(storage, uploadLink, File.Open(filePath, FileMode.Open));
throw new FileNotFoundException("Файл не найден", filePath);
return await UploadTrackToPlaylistAsync(playlist, fileName, await File.ReadAllBytesAsync(filePath));
}
/// <summary>
/// Загрузка трека из потока
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="uploadLink">Ссылка на балансировщик для загрузки, можно получить из GetUgcUploadLinkAsync</param>
/// <param name="stream">Поток с данными для загрузки</param>
public Task<YResponse<string>> UploadUgcTrackAsync(AuthStorage storage, string uploadLink, Stream stream)
public async Task<string?> UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, Stream stream)
{
if (stream == null)
throw new NullReferenceException("Пустая ссылка на поток загрузки.");
using MemoryStream ms = new();
stream.CopyTo(ms);
return UploadUgcTrackAsync(storage, uploadLink, ms.ToArray());
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
return await UploadTrackToPlaylistAsync(playlist, fileName, ms.ToArray());
}
/// <summary>
/// Загрузка трека из массива
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="uploadLink">Ссылка на балансировщик для загрузки, можно получить из GetUgcUploadLinkAsync</param>
/// <param name="file">Загружаемый трек в виде массив байтов</param>
public Task<YResponse<string>> UploadUgcTrackAsync(AuthStorage storage, string uploadLink, byte[] file)
public async Task<string?> UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, byte[] file)
{
return new YUgcUploadBuilder(api, storage)
.Build((uploadLink, file))
.GetResponseAsync();
var uploadLink = await GetUgcUploadLinkAsync(playlist, fileName);
if (uploadLink?.PostTarget == null) return null;
var result = await new YUgcUploadBuilder(Api).ExecuteAsync((uploadLink.PostTarget, file));
return result?.Result;
}
}

View File

@@ -1,300 +1,159 @@
using System.Security.Authentication;
using System.Text.RegularExpressions;
using YandexMusic.API.Common;
using YandexMusic.API.Models.Account;
using YandexMusic.API.Models.Common;
using YandexMusic.API.Requests.Account;
namespace YandexMusic.API;
/// <summary>
/// API для пользователя
/// </summary>
public partial class YUserAPI : YCommonAPI
/// <summary>API для работы с пользователем и авторизации.</summary>
public class YUserAPI : YCommonAPI
{
#region Вспомогательные функции
public YUserAPI(YandexMusicApi api) : base(api) { }
private async Task<bool> GetCsrfTokenAsync(AuthStorage storage)
private async Task<bool> GetCsrfTokenAsync()
{
using HttpResponseMessage authMethodsResponse = await new YGetAuthMethodsBuilder(api, storage)
.Build(null)
.GetResponseAsync();
using var response = await new YGetAuthMethodsBuilder(Api).ExecuteRawAsync(null!);
if (response == null || !response.IsSuccessStatusCode)
throw new HttpRequestException("Не удалось получить CSRF-токен");
if (!authMethodsResponse.IsSuccessStatusCode)
throw new HttpRequestException("Невозможно получить CFRF-токен.");
var content = await response.Content.ReadAsStringAsync();
var csrfMatch = Regex.Match(content, @"window\.__CSRF__\s*=\s*""([^""]+)""");
var processMatch = Regex.Match(content, @"'process_uuid'\s*:\s*'([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})'");
string responseString = await authMethodsResponse.Content
.ReadAsStringAsync();
Match match = Regex.Match(responseString, "\"csrf_token\" value=\"([^\"]+)\"");
if (!match.Success || match.Groups.Count < 2)
if (!csrfMatch.Success || !processMatch.Success)
return false;
storage.AuthToken = new YAuthToken
Api.Storage.AuthToken = new YAuthToken
{
CsfrToken = match.Groups[1].Value
CsfrToken = csrfMatch.Groups[1].Value,
ProcessUuid = processMatch.Groups[1].Value
};
await new YPostAuthStats(Api).ExecuteAsync(null!);
return true;
}
private async Task<bool> LoginByCookiesAsync()
{
if (Api.Storage.AuthToken == null)
throw new AuthenticationException("Сессия входа не инициализирована");
var accessToken = await new YGetAuthCookiesBuilder(Api).ExecuteAsync(null!);
if (accessToken == null || string.IsNullOrEmpty(accessToken.AccessToken))
return false;
Api.Storage.AccessToken = accessToken;
Api.Storage.Token = accessToken.AccessToken;
var shortInfo = await new YGetShortAccountInfoBuilder(Api).ExecuteAsync(null!);
if (shortInfo?.Status != YAuthStatus.Ok || string.IsNullOrWhiteSpace(shortInfo.Uid))
throw new Exception("Не удалось подтвердить авторизацию");
return true;
}
private async Task<bool> LoginByCookiesAsync(AuthStorage storage)
{
if (storage.AuthToken == null)
throw new AuthenticationException("Невозможно инициализировать сессию входа.");
YAccessToken accessToken = await new YGetAuthCookiesBuilder(api, storage)
.Build(null)
.GetResponseAsync();
storage.IsAuthorized = !string.IsNullOrEmpty(accessToken.AccessToken);
storage.AccessToken = accessToken;
storage.Token = accessToken.AccessToken;
YShortAccountInfo validateTokenResponse = await new YGetShortAccountInifoBuilder(api, storage)
.Build(null)
.GetResponseAsync();
if (validateTokenResponse.Status != YAuthStatus.Ok)
throw new Exception("Вход в аккаунт не выполнен.");
storage.IsAuthorized = !string.IsNullOrWhiteSpace(validateTokenResponse.Uid);
return storage.IsAuthorized;
}
#endregion Вспомогательные функции
public YUserAPI(YandexMusicApi yandex) : base(yandex)
{
}
/// <summary>
/// Авторизация
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="token">Токен авторизации</param>
/// <returns></returns>
public async Task AuthorizeAsync(AuthStorage storage, string token)
public async Task AuthorizeAsync(string token)
{
if (string.IsNullOrEmpty(token))
throw new Exception("Задан пустой токен авторизации.");
throw new Exception("Токен не может быть пустым");
storage.Token = token;
Api.Storage.Token = token;
var authInfo = await new YGetAuthInfoBuilder(Api).ExecuteAsync(null!);
if (authInfo?.Account?.Uid == null)
throw new Exception("Пользователь не авторизован");
// Пытаемся получить информацию о пользователе
YResponse<YAccountResult> authInfo = await GetUserAuthAsync(storage);
// Если не авторизован, то авторизуем
if (string.IsNullOrEmpty(authInfo.Result.Account.Uid))
throw new Exception("Пользователь незалогинен.");
// Флаг авторизации
storage.IsAuthorized = true;
storage.User = authInfo.Result.Account;
Api.Storage.SetAuthorized(authInfo.Account, token);
}
/// <summary>
/// Получение информации об авторизации
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YResponse<YAccountResult>> GetUserAuthAsync(AuthStorage storage)
public Task<YAccountResult?> GetUserAuthAsync()
=> new YGetAuthInfoBuilder(Api).ExecuteAsync(null!);
public async Task<YAuthTypes?> CreateAuthSessionAsync(string userName)
{
return new YGetAuthInfoBuilder(api, storage)
.Build(null)
.GetResponseAsync();
if (!await GetCsrfTokenAsync())
throw new Exception("Не удалось инициализировать сессию");
var result = await new YGetAuthLoginUserBuilder(Api).ExecuteAsync((Api.Storage.AuthToken.CsfrToken, userName));
if (result?.TrackId != null)
Api.Storage.AuthToken.TrackId = result.TrackId;
return result;
}
/// <summary>
/// Создание сеанса и получение доступных методов авторизации
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="userName">Имя пользователя</param>
/// <returns></returns>
public async Task<YAuthTypes> CreateAuthSessionAsync(AuthStorage storage, string userName)
public async Task<string?> GetAuthQRLinkAsync()
{
if (!await GetCsrfTokenAsync(storage))
throw new Exception("Невозможно инициализировать сессию входа.");
if (!await GetCsrfTokenAsync())
throw new Exception("Не удалось инициализировать сессию");
YAuthTypes types = await new YGetAuthLoginUserBuilder(api, storage)
.Build((storage.AuthToken.CsfrToken, userName))
.GetResponseAsync();
var qr = await new YGetAuthQRBuilder(Api).ExecuteAsync(null!);
if (qr?.Status != YAuthStatus.Ok || string.IsNullOrEmpty(qr.TrackId))
return null;
storage.AuthToken.TrackId = types.TrackId;
return types;
}
/// <summary>
/// Получение ссылки на QR-код
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public async Task<string> GetAuthQRLinkAsync(AuthStorage storage)
{
if (!await GetCsrfTokenAsync(storage))
throw new Exception("Невозможно инициализировать сессию входа.");
YAuthQR result = await new YGetAuthQRBuilder(api, storage)
.Build(null)
.GetResponseAsync();
if (result.Status != YAuthStatus.Ok)
return string.Empty;
storage.AuthToken = new YAuthToken
Api.Storage.AuthToken = new YAuthToken
{
TrackId = result.TrackId,
CsfrToken = result.CsrfToken
TrackId = qr.TrackId,
CsfrToken = qr.CsrfToken
};
return $"https://passport.yandex.ru/auth/magic/code/?track_id={result.TrackId}";
return $"https://passport.yandex.ru/auth/magic/code/?track_id={qr.TrackId}";
}
/// <summary>
/// Авторизация по QR-коду
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public async Task<YAuthQRStatus> AuthorizeByQRAsync(AuthStorage storage)
public async Task<YAuthQRStatus?> AuthorizeByQRAsync()
{
if (storage.AuthToken == null)
throw new Exception("Не выполнен запрос на авторизацию по QR.");
if (Api.Storage.AuthToken == null)
throw new Exception("Сессия не инициализирована");
try
{
YAuthQRStatus qrStatus = await new YGetAuthLoginQRBuilder(api, storage)
.Build(null)
.GetResponseAsync();
if (qrStatus.Status != YAuthStatus.Ok)
return qrStatus;
bool ok = await LoginByCookiesAsync(storage);
if (!ok)
throw new AuthenticationException("Ошибка авторизации по QR.");
return qrStatus;
}
catch (Exception ex)
{
throw new AuthenticationException("Ошибка авторизации по QR.", ex);
}
var status = await new YGetAuthLoginQRBuilder(Api).ExecuteAsync(null!);
if (status?.Status == YAuthStatus.Ok && await LoginByCookiesAsync())
return status;
throw new AuthenticationException("Ошибка авторизации по QR");
}
/// <summary>
/// Получение <see cref="YAuthCaptcha"/>
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YAuthCaptcha> GetCaptchaAsync(AuthStorage storage)
public Task<YAuthCaptcha?> GetCaptchaAsync()
{
if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken))
throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием.");
return new YGetAuthCaptchaBuilder(api, storage)
.Build(null)
.GetResponseAsync();
if (Api.Storage.AuthToken == null)
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
return new YGetAuthCaptchaBuilder(Api).ExecuteAsync(null!);
}
/// <summary>
/// Авторизация по captcha
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="captchaValue">Значение captcha</param>
/// <returns></returns>
public Task<YAuthBase> AuthorizeByCaptchaAsync(AuthStorage storage, string captchaValue)
public Task<YAuthBase?> AuthorizeByCaptchaAsync(string captchaValue)
{
if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken))
throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием.");
return new YGetAuthLoginCaptchaBuilder(api, storage)
.Build(captchaValue)
.GetResponseAsync();
if (Api.Storage.AuthToken == null)
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
return new YGetAuthLoginCaptchaBuilder(Api).ExecuteAsync(captchaValue);
}
/// <summary>
/// Получение письма авторизации на почту пользователя
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public Task<YAuthLetter> GetAuthLetterAsync(AuthStorage storage)
public Task<YAuthLetter?> GetAuthLetterAsync()
=> new YGetAuthLetterBuilder(Api).ExecuteAsync(null!);
public async Task<bool> AuthorizeByLetterAsync()
{
return new YGetAuthLetterBuilder(api, storage)
.Build(null)
.GetResponseAsync();
var status = await new YGetAuthLoginLetterBuilder(Api).ExecuteAsync(null!);
if (status?.Status != YAuthStatus.Ok || !status.MagicLinkConfirmed)
throw new Exception("Письмо не подтверждено");
return await LoginByCookiesAsync();
}
/// <summary>
/// Авторизация после подтверждения входа через письмо
/// </summary>
/// <param name="storage">Хранилище</param>
/// <returns></returns>
public async Task<bool> AuthorizeByLetterAsync(AuthStorage storage)
public async Task<YAuthBase?> AuthorizeByAppPasswordAsync(string password)
{
YAuthLetterStatus status = await new YGetAuthLoginLetterBuilder(api, storage)
.Build(null)
.GetResponseAsync();
if (Api.Storage.AuthToken == null)
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
if (status.Status == YAuthStatus.Ok && !status.MagicLinkConfirmed)
throw new Exception("Не подтвержден вход посредством e-mail.");
return await LoginByCookiesAsync(storage);
var result = await new YGetAuthAppPasswordBuilder(Api).ExecuteAsync(password);
if (result?.Status == YAuthStatus.Ok && await LoginByCookiesAsync())
return result;
throw new AuthenticationException("Ошибка авторизации по паролю");
}
/// <summary>
/// Авторизация с помощью пароля из приложения Яндекс
/// </summary>
/// <param name="storage">Хранилище</param>
/// <param name="password">Пароль</param>
/// <returns></returns>
public async Task<YAuthBase> AuthorizeByAppPasswordAsync(AuthStorage storage, string password)
public async Task<YAccessToken?> GetAccessTokenAsync()
{
if (storage.AuthToken == null || string.IsNullOrWhiteSpace(storage.AuthToken.CsfrToken))
throw new AuthenticationException($"Не найдена сессия входа. Выполните {nameof(CreateAuthSessionAsync)} перед использованием.");
if (Api.Storage.AuthToken == null)
throw new Exception("Сессия не инициализована");
YAuthBase response = await new YGetAuthAppPasswordBuilder(api, storage)
.Build(password)
.GetResponseAsync();
if (response.Status == YAuthStatus.Ok)
{
bool ok = await LoginByCookiesAsync(storage);
if (!ok)
throw new AuthenticationException("Ошибка авторизации.");
}
return response;
var token = await new YGetMusicTokenBuilder(Api).ExecuteAsync(null!);
if (token?.AccessToken != null)
Api.Storage.Token = token.AccessToken;
return token;
}
/// <summary>
/// Получение <see cref="YAccessToken"/> после авторизации с помощью QR, e-mail, пароля из приложения
/// </summary>
public async Task<YAccessToken> GetAccessTokenAsync(AuthStorage storage)
{
if (storage.AuthToken == null)
throw new Exception("Не найдена сессия входа.");
YAccessToken accessToken = await new YGetMusicTokenBuilder(api, storage)
.Build(null)
.GetResponseAsync();
storage.Token = accessToken.AccessToken;
return accessToken;
}
/// <summary>
/// Получение информации о пользователе через логин Яндекса
/// </summary>
public Task<YLoginInfo> GetLoginInfoAsync(AuthStorage storage)
{
return new YGetLoginInfoBuilder(api, storage)
.Build(null)
.GetResponseAsync();
}
}
public Task<YLoginInfo?> GetLoginInfoAsync()
=> new YGetLoginInfoBuilder(Api).ExecuteAsync(null!);
}

View File

@@ -1,21 +1,16 @@
using YandexMusic.API.Common;
using YandexMusic.API.Common.Ynison;
namespace YandexMusic.API;
/// <summary>
/// API Ynison
/// </summary>
public partial class YYnisonAPI : YCommonAPI
/// <summary>API для работы с Ynison (WebSocket-плеер).</summary>
public class YYnisonAPI : YCommonAPI
{
public YYnisonAPI(YandexMusicApi yandex) : base(yandex)
{
}
public YYnisonAPI(YandexMusicApi api) : base(api) { }
public YnisonPlayer GetPlayer(AuthStorage storage)
public YnisonPlayer GetPlayer()
{
if (string.IsNullOrEmpty(storage.Token))
throw new Exception("Токен пользователя не задан.");
return new(api, storage);
if (string.IsNullOrEmpty(Api.Storage.Token))
throw new Exception("Токен пользователя не задан");
return new YnisonPlayer(Api, Api.Storage);
}
}
}