diff --git a/YandexMusic.API/API/YAuthAPI.cs b/YandexMusic.API/API/YAuthAPI.cs new file mode 100644 index 0000000..48da2f1 --- /dev/null +++ b/YandexMusic.API/API/YAuthAPI.cs @@ -0,0 +1,111 @@ +using System.Security.Authentication; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Requests.Account; + +namespace YandexMusic.API; + +/// API для работы с пользователем и авторизации. +public class YAuthAPI : YCommonAPI +{ + public YAuthAPI(YandexMusicApi api) : base(api) { } + + /// Авторизация по готовому музыкальному токену (OAuth). + public async Task AuthorizeAsync(string musicToken) + { + if (string.IsNullOrEmpty(musicToken)) + throw new ArgumentException("Токен не может быть пустым", nameof(musicToken)); + + Api.Storage.Token = musicToken; + var authInfo = await new YGetAuthInfoBuilder(Api).ExecuteAsync(null!); + if (authInfo?.Account?.Uid == null) + throw new Exception("Пользователь не авторизован"); + + Api.Storage.SetAuthorized(authInfo.Account, musicToken); + } + + /// Авторизация по паспортному токену (полученному, например, после QR-входа). + public async Task AuthorizeByPassportTokenAsync(string passportToken) + { + if (string.IsNullOrEmpty(passportToken)) + throw new ArgumentException("Паспортный токен не может быть пустым", nameof(passportToken)); + + var musicToken = await Api.Passport.GetMusicTokenByPassportTokenAsync(passportToken); + if (musicToken?.AccessToken == null) + throw new Exception("Не удалось обменять паспортный токен на музыкальный"); + + await AuthorizeAsync(musicToken.AccessToken); + } + + /// Получение информации о текущем аккаунте. + public Task GetUserAuthAsync() + => new YGetAuthInfoBuilder(Api).ExecuteAsync(null!); + + /// Создание сессии авторизации (получение доступных методов входа). + public async Task CreateAuthSessionAsync(string userName) + { + if (!await Api.Passport.GetCsrfTokenAsync()) // вместо InitSessionAsync + 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; + } + + /// Получение капчи. + public Task GetCaptchaAsync() + { + if (Api.Storage.AuthToken == null) + throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием"); + return new YGetAuthCaptchaBuilder(Api).ExecuteAsync(null!); + } + + /// Авторизация по капче. + public Task AuthorizeByCaptchaAsync(string captchaValue) + { + if (Api.Storage.AuthToken == null) + throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием"); + return new YGetAuthLoginCaptchaBuilder(Api).ExecuteAsync(captchaValue); + } + + /// Отправить письмо для авторизации. + public Task GetAuthLetterAsync() + => new YGetAuthLetterBuilder(Api).ExecuteAsync(null!); + + /// Подтверждение входа по письму и получение музыкального токена. + public async Task AuthorizeByLetterAsync() + { + var status = await new YGetAuthLoginLetterBuilder(Api).ExecuteAsync(null!); + if (status?.Status != YAuthStatus.Ok || !status.MagicLinkConfirmed) + throw new Exception("Письмо не подтверждено"); + + var musicToken = await Api.Passport.GetMusicTokenByCookiesAsync(); + if (musicToken?.AccessToken == null) + throw new Exception("Не удалось получить музыкальный токен после подтверждения письма"); + + await AuthorizeAsync(musicToken.AccessToken); + return true; + } + + /// Авторизация по паролю приложения Яндекс. + public async Task AuthorizeByAppPasswordAsync(string password) + { + if (Api.Storage.AuthToken == null) + throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием"); + + var result = await new YGetAuthAppPasswordBuilder(Api).ExecuteAsync(password); + if (result?.Status != YAuthStatus.Ok) + throw new AuthenticationException("Ошибка авторизации по паролю"); + + var musicToken = await Api.Passport.GetMusicTokenByCookiesAsync(); + if (musicToken?.AccessToken == null) + throw new Exception("Не удалось получить музыкальный токен после ввода пароля"); + + await AuthorizeAsync(musicToken.AccessToken); + return result; + } + + /// Получение информации о пользователе через логин. + public Task GetLoginInfoAsync() + => new YGetLoginInfoBuilder(Api).ExecuteAsync(null!); +} \ No newline at end of file diff --git a/YandexMusic.API/API/YPassportAPI.cs b/YandexMusic.API/API/YPassportAPI.cs new file mode 100644 index 0000000..6d111e3 --- /dev/null +++ b/YandexMusic.API/API/YPassportAPI.cs @@ -0,0 +1,230 @@ +using System.Security.Authentication; +using System.Text.RegularExpressions; +using YandexMusic.API.Models.Account; +using YandexMusic.API.Models.Passport; +using YandexMusic.API.Requests.Account; +using YandexMusic.API.Requests.Passport; + +namespace YandexMusic.API; + +/// API для работы с яндекс паспортом +public class YPassportAPI : YCommonAPI +{ + public YPassportAPI(YandexMusicApi api) : base(api) { } + + public async Task GetMusicTokenByCookiesAsync() + { + if (string.IsNullOrEmpty(Api.Storage.AuthToken.TrackId)) + { + if (!await GetCsrfTokenAsync()) + throw new Exception("Не удалось инициализировать сессию"); + await CreateTrackAsync(); // ваш приватный метод создания track_id + } + return await new YGetAuthCookiesBuilder(Api).ExecuteAsync(null!); + } + + public async Task GetMusicTokenByPassportTokenAsync(string passportToken) + => await GetAccessTokenAsync(passportToken); + + public async Task GetAuthQRLinkAsync() + { + if (!await GetCsrfTokenAsync()) + throw new Exception("Не удалось инициализировать сессию"); + + await CreateTrackAsync(); + + return $"https://passport.yandex.ru/auth/magic/code/?track_id={Api.Storage.AuthToken.TrackId}"; + } + + public async Task CheckQRStatusAsync() + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + + var status = await new YGetQrStatus(Api).ExecuteAsync(null!); + + if (!string.IsNullOrWhiteSpace(status?.TrackId)) + { + Api.Storage.AuthToken.SessionTrackId = status.TrackId; + } + + return status; + } + + public async Task AuthorizeByQRAsync() + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + + if (string.IsNullOrWhiteSpace(Api.Storage.AuthToken.SessionTrackId)) + throw new Exception("Токен сессии не инициализирован"); + + var status = await new YGetAuthLoginQRBuilder(Api).ExecuteAsync(null!); + if (status != null && status.DefaultUid != 0 && await LoginByCookiesAsync()) + return status; + throw new AuthenticationException("Ошибка авторизации по QR"); + } + + /// Многоступенчатая авторизация: начало (передача логина). + public async Task MultistepStartAsync(string login) + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована. Вызовите GetAuthQRLinkAsync или CreateTrackAsync"); + return await new YMultistepStartBuilder(Api).ExecuteAsync(login); + } + + /// Многоступенчатая авторизация: ввод пароля. + public async Task MultistepPasswordAsync(string password) + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YMultiStepPasswordBuilder(Api).ExecuteAsync(password); + } + + /// Авторизация с помощью RFC OTP. + public async Task RfcOtpPasswordAsync(string rfcOtp) + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YRfcOtpBuilder(Api).ExecuteAsync(rfcOtp); + } + + /// Создание сессии пользователя. + public async Task CreateUserSessionAsync() + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YGetSessionBuilder(Api).ExecuteAsync(null!); + } + + /// Проверка состояния сессии. + public async Task GetSessionStateAsync() + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YCheckSessionBuilder(Api).ExecuteAsync(null!); + } + + /// Проверка номера телефона (валидация). + public async Task ValidatePhoneNumberAsync(string phone) + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YValidatePhoneNumberBuilder(Api).ExecuteAsync(phone); + } + + /// Проверка доступности номера. + public async Task CheckPhoneAvailabilityAsync(string phone) + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YCheckPhoneAvailabilityBuilder(Api).ExecuteAsync(phone); + } + + /// Запрос на отправку push-уведомления. + public async Task SuggestSendPushAsync(string phone) + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YSendPushBuilder(Api).ExecuteAsync(phone); + } + + /// Проверка push-кода. + public async Task CheckPushCodeAsync(string code) + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + await new YCheckPushCodeBuilder(Api).ExecuteAsync(code); + } + + /// Проверка на "сквоттера" (захват номера). + public async Task ValidateSquatterAsync(string phone) + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YValidateSquatterBuilder(Api).ExecuteAsync(phone); + } + + /// Получение списка аккаунтов по номеру телефона. + public async Task SuggestByPhoneAsync() + { + if (Api.Storage.AuthToken == null) + throw new Exception("Сессия не инициализирована"); + return await new YSuggestByPhoneBuilder(Api).ExecuteAsync(null!); + } + + /// Вход по паролю (упрощённый, если не нужна многоступенчатость). + public async Task LoginByPasswordAsync(string password) + { + // Сначала запускаем мультистеп (если track_id уже есть) + if (string.IsNullOrEmpty(Api.Storage.AuthToken.TrackId)) + throw new Exception("TrackId не найден. Вызовите MultistepStartAsync сначала."); + return await MultistepPasswordAsync(password); + } + + + + + private async Task CreateTrackAsync() + { + if (!await GetCsrfTokenAsync()) + throw new Exception("Невозможно инициализировать сессию входа."); + + var track = await new YCreateTrackBuilder(Api).ExecuteAsync(null!); + + if (string.IsNullOrWhiteSpace(track?.TrackId) || string.IsNullOrWhiteSpace(track?.CsrfToken)) + throw new Exception("Не удалось создать трек паспорта."); + + Api.Storage.AuthToken.TrackId = track.TrackId; + Api.Storage.AuthToken.CsfrToken = track.CsrfToken; + } + + internal async Task GetCsrfTokenAsync() + { + using var response = await new YGetAuthMethodsBuilder(Api).ExecuteRawAsync(null!); + if (response == null || !response.IsSuccessStatusCode) + throw new HttpRequestException("Не удалось получить CSRF-токен"); + + 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})'"); + + if (!csrfMatch.Success || !processMatch.Success) + return false; + + Api.Storage.HeaderToken = new YAuthToken + { + CsfrToken = csrfMatch.Groups[1].Value, + ProcessUuid = processMatch.Groups[1].Value + }; + + return true; + } + + internal async Task GetAccessTokenAsync(string passportToken) + { + if (string.IsNullOrEmpty(passportToken)) + throw new Exception("Сессия не инициализована"); + + var token = await new YGetMusicTokenBuilder(Api).ExecuteAsync(passportToken); + if (token?.AccessToken != null) + Api.Storage.Token = token.AccessToken; + + return token; + } + + + internal async Task 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; + + await GetAccessTokenAsync(accessToken.AccessToken); + + return true; + } +} \ No newline at end of file diff --git a/YandexMusic.API/API/YUserAPI.cs b/YandexMusic.API/API/YUserAPI.cs deleted file mode 100644 index dd78fa3..0000000 --- a/YandexMusic.API/API/YUserAPI.cs +++ /dev/null @@ -1,185 +0,0 @@ -using System.Security.Authentication; -using System.Text.RegularExpressions; -using YandexMusic.API.Models.Account; -using YandexMusic.API.Requests.Account; - -namespace YandexMusic.API; - -/// API для работы с пользователем и авторизации. -public class YUserAPI : YCommonAPI -{ - public YUserAPI(YandexMusicApi api) : base(api) { } - - private async Task GetCsrfTokenAsync() - { - using var response = await new YGetAuthMethodsBuilder(Api).ExecuteRawAsync(null!); - if (response == null || !response.IsSuccessStatusCode) - throw new HttpRequestException("Не удалось получить CSRF-токен"); - - 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})'"); - - if (!csrfMatch.Success || !processMatch.Success) - return false; - - Api.Storage.HeaderToken = new YAuthToken - { - CsfrToken = csrfMatch.Groups[1].Value, - ProcessUuid = processMatch.Groups[1].Value - }; - - await new YPostAuthStats(Api).ExecuteAsync(null!); - return true; - } - - private async Task 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; - - await AuthorizeByPassportAsync(accessToken.AccessToken); - - return true; - } - - public async Task AuthorizeAsync(string token) - { - if (string.IsNullOrEmpty(token)) - throw new Exception("Токен не может быть пустым"); - - Api.Storage.Token = token; - var authInfo = await new YGetAuthInfoBuilder(Api).ExecuteAsync(null!); - if (authInfo?.Account?.Uid == null) - throw new Exception("Пользователь не авторизован"); - - Api.Storage.SetAuthorized(authInfo.Account, token); - } - - public async Task AuthorizeByPassportAsync(string token) - { - if (string.IsNullOrEmpty(token)) - throw new Exception("Токен не может быть пустым"); - - Api.Storage.Token = token; - await GetAccessTokenAsync(); - await AuthorizeAsync(Api.Storage.Token); - } - - public Task GetUserAuthAsync() - => new YGetAuthInfoBuilder(Api).ExecuteAsync(null!); - - public async Task CreateAuthSessionAsync(string userName) - { - 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; - } - - public async Task GetAuthQRLinkAsync() - { - if (!await GetCsrfTokenAsync()) - throw new Exception("Не удалось инициализировать сессию"); - - var qr = await new YGetAuthQRBuilder(Api).ExecuteAsync(null!); - if (qr?.Status != YAuthStatus.Ok || string.IsNullOrEmpty(qr.TrackId)) - return null; - - Api.Storage.AuthToken = new YAuthToken - { - TrackId = qr.TrackId, - CsfrToken = qr.CsrfToken - }; - return $"https://passport.yandex.ru/auth/magic/code/?track_id={qr.TrackId}"; - } - - public async Task CheckQRStatusAsync() - { - if (Api.Storage.AuthToken == null) - throw new Exception("Сессия не инициализирована"); - - var status = await new YPostQrStatus(Api).ExecuteAsync(null!); - - if (!string.IsNullOrWhiteSpace(status?.TrackId)) - { - Api.Storage.AuthToken.SessionTrackId = status.TrackId; - } - - return status; - } - - public async Task AuthorizeByQRAsync() - { - if (Api.Storage.AuthToken == null) - throw new Exception("Сессия не инициализирована"); - - if (string.IsNullOrWhiteSpace(Api.Storage.AuthToken.SessionTrackId)) - throw new Exception("Токен сессии не инициализирован"); - - var status = await new YGetAuthLoginQRBuilder(Api).ExecuteAsync(null!); - if (status != null && status.DefaultUid != 0 && await LoginByCookiesAsync()) - return status; - throw new AuthenticationException("Ошибка авторизации по QR"); - } - - public Task GetCaptchaAsync() - { - if (Api.Storage.AuthToken == null) - throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием"); - return new YGetAuthCaptchaBuilder(Api).ExecuteAsync(null!); - } - - public Task AuthorizeByCaptchaAsync(string captchaValue) - { - if (Api.Storage.AuthToken == null) - throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием"); - return new YGetAuthLoginCaptchaBuilder(Api).ExecuteAsync(captchaValue); - } - - public Task GetAuthLetterAsync() - => new YGetAuthLetterBuilder(Api).ExecuteAsync(null!); - - public async Task AuthorizeByLetterAsync() - { - var status = await new YGetAuthLoginLetterBuilder(Api).ExecuteAsync(null!); - if (status?.Status != YAuthStatus.Ok || !status.MagicLinkConfirmed) - throw new Exception("Письмо не подтверждено"); - return await LoginByCookiesAsync(); - } - - public async Task AuthorizeByAppPasswordAsync(string password) - { - if (Api.Storage.AuthToken == null) - throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием"); - - var result = await new YGetAuthAppPasswordBuilder(Api).ExecuteAsync(password); - if (result?.Status == YAuthStatus.Ok && await LoginByCookiesAsync()) - return result; - throw new AuthenticationException("Ошибка авторизации по паролю"); - } - - public async Task GetAccessTokenAsync() - { - if (Api.Storage.AuthToken == null) - throw new Exception("Сессия не инициализована"); - - var token = await new YGetMusicTokenBuilder(Api).ExecuteAsync(null!); - if (token?.AccessToken != null) - Api.Storage.Token = token.AccessToken; - return token; - } - - public Task GetLoginInfoAsync() - => new YGetLoginInfoBuilder(Api).ExecuteAsync(null!); -} \ No newline at end of file diff --git a/YandexMusic.API/Common/AuthStorage.cs b/YandexMusic.API/Common/AuthStorage.cs index e7e5e12..27d3632 100644 --- a/YandexMusic.API/Common/AuthStorage.cs +++ b/YandexMusic.API/Common/AuthStorage.cs @@ -45,12 +45,17 @@ public class AuthStorage /// /// Внутренние данные авторизации (CSRF, track_id и т.д.). /// - public YAuthToken HeaderToken { get; set; } = new(); + public YAuthToken? HeaderToken { get; set; } = new(); /// /// Внутренние данные авторизации (CSRF, track_id и т.д.). /// - public YAuthToken AuthToken { get; set; } = new(); + public YAuthToken? AuthToken { get; set; } = new(); + + /// + /// Страна, используемая для авторизации (по умолчанию "ru"). Может влиять на язык интерфейса и доступные методы авторизации. + /// + public object Country { get; set; } = "ru"; /// /// Устанавливает флаг авторизации и сохраняет информацию об аккаунте. diff --git a/YandexMusic.API/Extensions/API/YAlbumExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YAlbumExtensionsAsync.cs index ddcf19d..9370ffb 100644 --- a/YandexMusic.API/Extensions/API/YAlbumExtensionsAsync.cs +++ b/YandexMusic.API/Extensions/API/YAlbumExtensionsAsync.cs @@ -1,6 +1,6 @@ using YandexMusic.API.Models.Album; -namespace YandexMusic.API.Extensions.API; +namespace YandexMusic.API; /// /// Методы-расширения для альбома. diff --git a/YandexMusic.API/Extensions/API/YArtistExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YArtistExtensionsAsync.cs index 537e32f..9c74542 100644 --- a/YandexMusic.API/Extensions/API/YArtistExtensionsAsync.cs +++ b/YandexMusic.API/Extensions/API/YArtistExtensionsAsync.cs @@ -1,7 +1,7 @@ using YandexMusic.API.Models.Artist; using YandexMusic.API.Models.Track; -namespace YandexMusic.API.Extensions.API; +namespace YandexMusic.API; /// /// Методы-расширения для исполнителя. diff --git a/YandexMusic.API/Extensions/API/YPlaylistExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YPlaylistExtensionsAsync.cs index 7821e1d..43c5bff 100644 --- a/YandexMusic.API/Extensions/API/YPlaylistExtensionsAsync.cs +++ b/YandexMusic.API/Extensions/API/YPlaylistExtensionsAsync.cs @@ -1,7 +1,7 @@ using YandexMusic.API.Models.Playlist; using YandexMusic.API.Models.Track; -namespace YandexMusic.API.Extensions.API; +namespace YandexMusic.API; /// /// Методы-расширения для плейлиста. diff --git a/YandexMusic.API/Extensions/API/YStationResultExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YStationResultExtensionsAsync.cs index 0c40ae3..f6b9747 100644 --- a/YandexMusic.API/Extensions/API/YStationResultExtensionsAsync.cs +++ b/YandexMusic.API/Extensions/API/YStationResultExtensionsAsync.cs @@ -1,7 +1,7 @@ using YandexMusic.API.Models.Radio; using YandexMusic.API.Models.Track; -namespace YandexMusic.API.Extensions.API; +namespace YandexMusic.API; /// /// Методы-расширения для радиостанции. diff --git a/YandexMusic.API/Extensions/API/YTrackExtensionsAsync.cs b/YandexMusic.API/Extensions/API/YTrackExtensionsAsync.cs index 5c2dfd8..b98c66d 100644 --- a/YandexMusic.API/Extensions/API/YTrackExtensionsAsync.cs +++ b/YandexMusic.API/Extensions/API/YTrackExtensionsAsync.cs @@ -1,6 +1,6 @@ using YandexMusic.API.Models.Track; -namespace YandexMusic.API.Extensions.API; +namespace YandexMusic.API; /// /// Методы-расширения для трека. diff --git a/YandexMusic.API/Models/Account/YAuthQr.cs b/YandexMusic.API/Models/Passport/YAuthQr.cs similarity index 62% rename from YandexMusic.API/Models/Account/YAuthQr.cs rename to YandexMusic.API/Models/Passport/YAuthQr.cs index a728bb7..6de45f5 100644 --- a/YandexMusic.API/Models/Account/YAuthQr.cs +++ b/YandexMusic.API/Models/Passport/YAuthQr.cs @@ -1,8 +1,9 @@ using System.Text.Json.Serialization; +using YandexMusic.API.Models.Account; -namespace YandexMusic.API.Models.Account; +namespace YandexMusic.API.Models.Passport; -public class YAuthQR : YAuthBase +public class YPassportTrack : YAuthBase { [JsonPropertyName("track_id")] public string TrackId { get; set; } diff --git a/YandexMusic.API/Models/Account/YAuthQRSession.cs b/YandexMusic.API/Models/Passport/YAuthQrSession.cs similarity index 78% rename from YandexMusic.API/Models/Account/YAuthQRSession.cs rename to YandexMusic.API/Models/Passport/YAuthQrSession.cs index f2f6bdf..101fae2 100644 --- a/YandexMusic.API/Models/Account/YAuthQRSession.cs +++ b/YandexMusic.API/Models/Passport/YAuthQrSession.cs @@ -1,8 +1,9 @@ using System.Text.Json.Serialization; +using YandexMusic.API.Models.Account; -namespace YandexMusic.API.Models.Account; +namespace YandexMusic.API.Models.Passport; -public class YAuthQRSession +public class YAuthQrSession { [JsonPropertyName("default_uid")] public int DefaultUid { get; set; } diff --git a/YandexMusic.API/Models/Account/YAuthQrStatus.cs b/YandexMusic.API/Models/Passport/YAuthQrStatus.cs similarity index 85% rename from YandexMusic.API/Models/Account/YAuthQrStatus.cs rename to YandexMusic.API/Models/Passport/YAuthQrStatus.cs index b551706..59c9b38 100644 --- a/YandexMusic.API/Models/Account/YAuthQrStatus.cs +++ b/YandexMusic.API/Models/Passport/YAuthQrStatus.cs @@ -1,6 +1,6 @@ using System.Text.Json.Serialization; -namespace YandexMusic.API.Models.Account; +namespace YandexMusic.API.Models.Passport; public class YAuthQRStatus { diff --git a/YandexMusic.API/Models/Passport/YCheckAvailabilityResult.cs b/YandexMusic.API/Models/Passport/YCheckAvailabilityResult.cs new file mode 100644 index 0000000..cb918f8 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YCheckAvailabilityResult.cs @@ -0,0 +1,39 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YCheckAvailabilityResult +{ + [JsonPropertyName("antifraudScore")] + public string AntifraudScore { get; set; } = string.Empty; + + [JsonPropertyName("hasAvailableAccounts")] + public bool HasAvailableAccounts { get; set; } + + [JsonPropertyName("flnFlowRequired")] + public bool FlnFlowRequired { get; set; } + + [JsonPropertyName("can_use_push")] + public bool CanUsePush { get; set; } + + [JsonPropertyName("can_use_webauthn")] + public bool CanUseWebauthn { get; set; } + + [JsonPropertyName("has_master")] + public bool HasMaster { get; set; } + + [JsonPropertyName("is_session_mastered")] + public bool IsSessionMastered { get; set; } + + [JsonPropertyName("does_master_have_free_slots")] + public bool DoesMasterHaveFreeSlots { get; set; } + + [JsonPropertyName("allowed_registration_flows")] + public List AllowedRegistrationFlows { get; set; } = new(); + + [JsonPropertyName("SuggestBy")] + public string SuggestBy { get; set; } = string.Empty; + + [JsonPropertyName("master_info")] + public YMasterInfo? MasterInfo { get; set; } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YMasterInfo.cs b/YandexMusic.API/Models/Passport/YMasterInfo.cs new file mode 100644 index 0000000..432ef37 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YMasterInfo.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YMasterInfo +{ + [JsonPropertyName("firstname")] + public string FirstName { get; set; } = string.Empty; + + [JsonPropertyName("lastname")] + public string LastName { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YMultistepStart.cs b/YandexMusic.API/Models/Passport/YMultistepStart.cs new file mode 100644 index 0000000..cdb232b --- /dev/null +++ b/YandexMusic.API/Models/Passport/YMultistepStart.cs @@ -0,0 +1,42 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YMultistepStart +{ + [JsonPropertyName("track_id")] + public string TrackId { get; set; } = string.Empty; + + [JsonPropertyName("can_authorize")] + public bool CanAuthorize { get; set; } + + [JsonPropertyName("can_register")] + public bool CanRegister { get; set; } + + [JsonPropertyName("is_rfc_2fa_enabled")] + public bool IsRfc2faEnabled { get; set; } + + [JsonPropertyName("allowed_account_types")] + public List AllowedAccountTypes { get; set; } = new(); + + [JsonPropertyName("location_id")] + public string LocationId { get; set; } = string.Empty; + + [JsonPropertyName("primary_alias_type")] + public int PrimaryAliasType { get; set; } + + [JsonPropertyName("auth_methods")] + public List AuthMethods { get; set; } = new(); + + [JsonPropertyName("preferred_auth_method")] + public string PreferredAuthMethod { get; set; } = string.Empty; + + [JsonPropertyName("csrf_token")] + public string CsrfToken { get; set; } = string.Empty; + + [JsonPropertyName("error")] + public string? Error { get; set; } + + [JsonPropertyName("errors")] + public List? Errors { get; set; } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YPassportAccount.cs b/YandexMusic.API/Models/Passport/YPassportAccount.cs new file mode 100644 index 0000000..d66cb5c --- /dev/null +++ b/YandexMusic.API/Models/Passport/YPassportAccount.cs @@ -0,0 +1,48 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YPassportAccount +{ + [JsonPropertyName("avatar_url")] + public string AvatarUrl { get; set; } = string.Empty; + + [JsonPropertyName("display_login")] + public string DisplayLogin { get; set; } = string.Empty; + + [JsonPropertyName("display_name")] + public YPassportName? DisplayName { get; set; } + + [JsonPropertyName("has_master")] + public bool HasMaster { get; set; } + + [JsonPropertyName("has_plus")] + public bool HasPlus { get; set; } + + [JsonPropertyName("has_secure_phone")] + public bool HasSecurePhone { get; set; } + + [JsonPropertyName("is_2fa_enabled")] + public bool Is2faEnabled { get; set; } + + [JsonPropertyName("is_rfc_2fa_enabled")] + public bool IsRfc2faEnabled { get; set; } + + [JsonPropertyName("is_sms_2fa_enabled")] + public bool IsSms2faEnabled { get; set; } + + [JsonPropertyName("is_workspace_user")] + public bool IsWorkspaceUser { get; set; } + + [JsonPropertyName("is_yandexoid")] + public bool IsYandexoid { get; set; } + + public bool Login { get; set; } + + public YPassportPerson? Person { get; set; } + + [JsonPropertyName("secure_phone_id")] + public int SecurePhoneId { get; set; } + + public int Uid { get; set; } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YPassportName.cs b/YandexMusic.API/Models/Passport/YPassportName.cs new file mode 100644 index 0000000..09912e9 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YPassportName.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YPassportName +{ + [JsonPropertyName("default_avatar")] + public string DefaultAvatar { get; set; } = string.Empty; + + public string Name { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YPassportPerson.cs b/YandexMusic.API/Models/Passport/YPassportPerson.cs new file mode 100644 index 0000000..4bba541 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YPassportPerson.cs @@ -0,0 +1,11 @@ +namespace YandexMusic.API.Models.Passport; + +public class YPassportPerson +{ + public string Birthday { get; set; } = string.Empty; + public string Country { get; set; } = string.Empty; + public string FirstName { get; set; } = string.Empty; + public int Gender { get; set; } + public string Language { get; set; } = string.Empty; + public string LastName { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YPassportSession.cs b/YandexMusic.API/Models/Passport/YPassportSession.cs new file mode 100644 index 0000000..bc86931 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YPassportSession.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YPassportSession +{ + [JsonPropertyName("track_id")] + public string TrackId { get; set; } = string.Empty; + + [JsonPropertyName("default_uid")] + public string DefaultUid { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YPassportSessionStatus.cs b/YandexMusic.API/Models/Passport/YPassportSessionStatus.cs new file mode 100644 index 0000000..b4746a9 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YPassportSessionStatus.cs @@ -0,0 +1,12 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YPassportSessionStatus +{ + [JsonPropertyName("session_is_correct")] + public bool SessionIsCorrect { get; set; } + + [JsonPropertyName("session_has_users")] + public bool SessionHasUsers { get; set; } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YPassportUser.cs b/YandexMusic.API/Models/Passport/YPassportUser.cs new file mode 100644 index 0000000..b437a8a --- /dev/null +++ b/YandexMusic.API/Models/Passport/YPassportUser.cs @@ -0,0 +1,21 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YPassportUser +{ + [JsonPropertyName("track_id")] + public string TrackId { get; set; } = string.Empty; + + [JsonPropertyName("state")] + public string State { get; set; } = string.Empty; + + [JsonPropertyName("account")] + public YPassportAccount? Account { get; set; } + + [JsonPropertyName("error")] + public string? Error { get; set; } + + [JsonPropertyName("errors")] + public List? Errors { get; set; } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YPushApp.cs b/YandexMusic.API/Models/Passport/YPushApp.cs new file mode 100644 index 0000000..cb44526 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YPushApp.cs @@ -0,0 +1,7 @@ +namespace YandexMusic.API.Models.Passport; + +public class YPushApp +{ + public string App { get; set; } = string.Empty; + public string Platform { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YSendPushResult.cs b/YandexMusic.API/Models/Passport/YSendPushResult.cs new file mode 100644 index 0000000..efa677a --- /dev/null +++ b/YandexMusic.API/Models/Passport/YSendPushResult.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YSendPushResult +{ + [JsonPropertyName("pushes_devices_list")] + public List PushesDevicesList { get; set; } = new(); + + [JsonPropertyName("deny_resend_until")] + public int DenyResendUntil { get; set; } + + [JsonPropertyName("is_push_silent")] + public bool IsPushSilent { get; set; } + + [JsonPropertyName("apps_for_bright_push")] + public List AppsForBrightPush { get; set; } = new(); +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YSuggestAccount.cs b/YandexMusic.API/Models/Passport/YSuggestAccount.cs new file mode 100644 index 0000000..8be947a --- /dev/null +++ b/YandexMusic.API/Models/Passport/YSuggestAccount.cs @@ -0,0 +1,54 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YSuggestAccount +{ + [JsonPropertyName("allowed_auth_flows")] + public List AllowedAuthFlows { get; set; } = new(); + + [JsonPropertyName("avatar_url")] + public string AvatarUrl { get; set; } = string.Empty; + + [JsonPropertyName("default_avatar")] + public string DefaultAvatar { get; set; } = string.Empty; + + [JsonPropertyName("display_name")] + public YPassportName? DisplayName { get; set; } + + [JsonPropertyName("has_bank_card")] + public bool HasBankCard { get; set; } + + [JsonPropertyName("has_family")] + public bool HasFamily { get; set; } + + [JsonPropertyName("has_master")] + public bool HasMaster { get; set; } + + [JsonPropertyName("has_plus")] + public bool HasPlus { get; set; } + + [JsonPropertyName("is_communal")] + public bool IsCommunal { get; set; } + + [JsonPropertyName("location_id")] + public string LocationId { get; set; } = string.Empty; + + [JsonPropertyName("login")] + public string Login { get; set; } = string.Empty; + + [JsonPropertyName("primary_alias_type")] + public int PrimaryAliasType { get; set; } + + [JsonPropertyName("priority")] + public int Priority { get; set; } + + [JsonPropertyName("uid")] + public long Uid { get; set; } + + [JsonPropertyName("shields")] + public List Shields { get; set; } = new(); + + [JsonPropertyName("require_additional_sms_to_login")] + public bool RequireAdditionalSmsToLogin { get; set; } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YSuggestByPhoneResult.cs b/YandexMusic.API/Models/Passport/YSuggestByPhoneResult.cs new file mode 100644 index 0000000..20e8f99 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YSuggestByPhoneResult.cs @@ -0,0 +1,15 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YSuggestByPhoneResult +{ + [JsonPropertyName("accounts")] + public List Accounts { get; set; } = new(); + + [JsonPropertyName("allowed_registration_flows")] + public List AllowedRegistrationFlows { get; set; } = new(); + + [JsonPropertyName("uid_from_bb")] + public string UidFromBb { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YValidatePhoneNumberResult.cs b/YandexMusic.API/Models/Passport/YValidatePhoneNumberResult.cs new file mode 100644 index 0000000..ec2a8a1 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YValidatePhoneNumberResult.cs @@ -0,0 +1,37 @@ +using System.Text.Json.Serialization; +using YandexMusic.API.Models.Account; + +namespace YandexMusic.API.Models.Passport; + +public class YValidatePhoneNumberResult +{ + [JsonPropertyName("phone_number")] + public YPhoneNumber? PhoneNumber { get; set; } + + [JsonPropertyName("valid_for_flash_call")] + public bool ValidForFlashCall { get; set; } + + [JsonPropertyName("location_id")] + public string LocationId { get; set; } = string.Empty; + + [JsonPropertyName("valid_for_viber")] + public bool ValidForViber { get; set; } + + [JsonPropertyName("valid_for_whatsapp")] + public bool ValidForWhatsapp { get; set; } + + [JsonPropertyName("valid_for_telegram")] + public bool ValidForTelegram { get; set; } + + [JsonPropertyName("valid_for_sms")] + public bool ValidForSms { get; set; } + + [JsonPropertyName("track_id")] + public string TrackId { get; set; } = string.Empty; + + [JsonPropertyName("error")] + public string? Error { get; set; } + + [JsonPropertyName("errors")] + public List? Errors { get; set; } +} \ No newline at end of file diff --git a/YandexMusic.API/Models/Passport/YValidateSquatter.cs b/YandexMusic.API/Models/Passport/YValidateSquatter.cs new file mode 100644 index 0000000..c567817 --- /dev/null +++ b/YandexMusic.API/Models/Passport/YValidateSquatter.cs @@ -0,0 +1,18 @@ +using System.Text.Json.Serialization; + +namespace YandexMusic.API.Models.Passport; + +public class YValidateSquatter +{ + [JsonPropertyName("require_flow_with_fio")] + public bool RequireFlowWithFio { get; set; } + + [JsonPropertyName("require_flow_with_auth_hint")] + public bool RequireFlowWithAuthHint { get; set; } + + [JsonPropertyName("auth_hint_question_id")] + public string AuthHintQuestionId { get; set; } = string.Empty; + + [JsonPropertyName("suggestBy")] + public string SuggestBy { get; set; } = string.Empty; +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthAppPasswordBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthAppPasswordBuilder.cs index f148f3f..c2e293f 100644 --- a/YandexMusic.API/Requests/Account/YGetAuthAppPasswordBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetAuthAppPasswordBuilder.cs @@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YGetAuthAppPasswordBuilder : YAuthRequestBuilder +internal class YGetAuthAppPasswordBuilder : YPassportRequestBuilder { public YGetAuthAppPasswordBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Post; diff --git a/YandexMusic.API/Requests/Account/YGetAuthCaptchaBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthCaptchaBuilder.cs index 069475d..5a91f92 100644 --- a/YandexMusic.API/Requests/Account/YGetAuthCaptchaBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetAuthCaptchaBuilder.cs @@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YGetAuthCaptchaBuilder : YAuthRequestBuilder +internal class YGetAuthCaptchaBuilder : YPassportRequestBuilder { public YGetAuthCaptchaBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Post; diff --git a/YandexMusic.API/Requests/Account/YGetAuthCookiesBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthCookiesBuilder.cs index f9c31dc..a4ad011 100644 --- a/YandexMusic.API/Requests/Account/YGetAuthCookiesBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetAuthCookiesBuilder.cs @@ -5,7 +5,7 @@ using YandexMusic.API.Requests.Common; namespace YandexMusic.API.Requests.Account; -internal class YGetAuthCookiesBuilder : YAuthRequestBuilder +internal class YGetAuthCookiesBuilder : YPassportRequestBuilder { public YGetAuthCookiesBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Post; diff --git a/YandexMusic.API/Requests/Account/YGetAuthLetterBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLetterBuilder.cs index 2ae9748..ec1e475 100644 --- a/YandexMusic.API/Requests/Account/YGetAuthLetterBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetAuthLetterBuilder.cs @@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YGetAuthLetterBuilder : YAuthRequestBuilder +internal class YGetAuthLetterBuilder : YPassportRequestBuilder { public YGetAuthLetterBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Post; diff --git a/YandexMusic.API/Requests/Account/YGetAuthLoginCaptchaBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLoginCaptchaBuilder.cs index 823c2f8..cf13b82 100644 --- a/YandexMusic.API/Requests/Account/YGetAuthLoginCaptchaBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetAuthLoginCaptchaBuilder.cs @@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YGetAuthLoginCaptchaBuilder : YAuthRequestBuilder +internal class YGetAuthLoginCaptchaBuilder : YPassportRequestBuilder { public YGetAuthLoginCaptchaBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Post; diff --git a/YandexMusic.API/Requests/Account/YGetAuthLoginLetterBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLoginLetterBuilder.cs index d40962e..1316337 100644 --- a/YandexMusic.API/Requests/Account/YGetAuthLoginLetterBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetAuthLoginLetterBuilder.cs @@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YGetAuthLoginLetterBuilder : YAuthRequestBuilder +internal class YGetAuthLoginLetterBuilder : YPassportRequestBuilder { public YGetAuthLoginLetterBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Post; diff --git a/YandexMusic.API/Requests/Account/YGetAuthLoginUserBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthLoginUserBuilder.cs index 86950f5..2c2953e 100644 --- a/YandexMusic.API/Requests/Account/YGetAuthLoginUserBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetAuthLoginUserBuilder.cs @@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YGetAuthLoginUserBuilder : YAuthRequestBuilder +internal class YGetAuthLoginUserBuilder : YPassportRequestBuilder { public YGetAuthLoginUserBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Post; diff --git a/YandexMusic.API/Requests/Account/YGetAuthQRBuilder.cs b/YandexMusic.API/Requests/Account/YGetAuthQRBuilder.cs deleted file mode 100644 index fef50ae..0000000 --- a/YandexMusic.API/Requests/Account/YGetAuthQRBuilder.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Net; -using System.Net.Http.Headers; -using YandexMusic.API.Models.Account; - -namespace YandexMusic.API.Requests.Account; - -internal class YGetAuthQRBuilder : YAuthRequestBuilder -{ - public YGetAuthQRBuilder(YandexMusicApi api) : base(api) { } - protected override string Method => WebRequestMethods.Http.Post; - protected override string PathTemplate => "pwl-yandex/api/passport/auth/password/submit"; - protected override HttpContent? GetContent(object _) - => new FormUrlEncodedContent(new Dictionary { { "retpath", "" } }); - protected override void SetCustomHeaders(HttpRequestHeaders headers) - { - headers.Add("X-Csrf-Token", Api.Storage.HeaderToken.CsfrToken); - headers.Add("Process-Uuid", Api.Storage.HeaderToken.ProcessUuid); - } -} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetLoginInfoBuilder.cs b/YandexMusic.API/Requests/Account/YGetLoginInfoBuilder.cs index a68966a..b693d4e 100644 --- a/YandexMusic.API/Requests/Account/YGetLoginInfoBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetLoginInfoBuilder.cs @@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YGetLoginInfoBuilder : YAuthRequestBuilder +internal class YGetLoginInfoBuilder : YPassportRequestBuilder { public YGetLoginInfoBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Get; diff --git a/YandexMusic.API/Requests/Account/YGetShortAccountInifoBuilder.cs b/YandexMusic.API/Requests/Account/YGetShortAccountInifoBuilder.cs index 0fd8b58..de86401 100644 --- a/YandexMusic.API/Requests/Account/YGetShortAccountInifoBuilder.cs +++ b/YandexMusic.API/Requests/Account/YGetShortAccountInifoBuilder.cs @@ -5,7 +5,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YGetShortAccountInfoBuilder : YAuthRequestBuilder +internal class YGetShortAccountInfoBuilder : YPassportRequestBuilder { public YGetShortAccountInfoBuilder(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Get; diff --git a/YandexMusic.API/Requests/Account/YPostAuthStats.cs b/YandexMusic.API/Requests/Account/YPostAuthStats.cs index 3fe84e8..b7d45d7 100644 --- a/YandexMusic.API/Requests/Account/YPostAuthStats.cs +++ b/YandexMusic.API/Requests/Account/YPostAuthStats.cs @@ -4,7 +4,7 @@ using YandexMusic.API.Models.Account; namespace YandexMusic.API.Requests.Account; -internal class YPostAuthStats : YAuthRequestBuilder +internal class YPostAuthStats : YPassportRequestBuilder { public YPostAuthStats(YandexMusicApi api) : base(api) { } protected override string Method => WebRequestMethods.Http.Post; diff --git a/YandexMusic.API/Requests/Account/YPostQrStatus.cs b/YandexMusic.API/Requests/Account/YPostQrStatus.cs deleted file mode 100644 index 8271559..0000000 --- a/YandexMusic.API/Requests/Account/YPostQrStatus.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Net; -using System.Net.Http.Headers; -using YandexMusic.API.Models.Account; - -namespace YandexMusic.API.Requests.Account; - -internal class YPostQrStatus : YAuthRequestBuilder -{ - public YPostQrStatus(YandexMusicApi api) : base(api) { } - protected override string Method => WebRequestMethods.Http.Post; - protected override string PathTemplate => "pwl-yandex/api/passport/auth/magic/code/status"; - protected override HttpContent? GetContent(object _) - => new FormUrlEncodedContent(new Dictionary - { - ["csrf_token"] = Api.Storage.AuthToken.CsfrToken, - ["track_id"] = Api.Storage.AuthToken.TrackId, - }); - protected override void SetCustomHeaders(HttpRequestHeaders headers) - { - headers.Add("X-Csrf-Token", Api.Storage.HeaderToken.CsfrToken); - headers.Add("Process-Uuid", Api.Storage.HeaderToken.ProcessUuid); - } -} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Artist/YGetArtistTrackBuilder.cs b/YandexMusic.API/Requests/Artist/YGetArtistTrackBuilder.cs index c8a0d73..17f0da8 100644 --- a/YandexMusic.API/Requests/Artist/YGetArtistTrackBuilder.cs +++ b/YandexMusic.API/Requests/Artist/YGetArtistTrackBuilder.cs @@ -12,5 +12,8 @@ internal class YGetArtistTrackBuilder : YMusicRequestBuilder GetSubstitutions((string id, int page, int pageSize) tuple) => new() { { "artistId", tuple.id } }; protected override NameValueCollection GetQueryParams((string id, int page, int pageSize) tuple) - => new() { { "page", tuple.page.ToString() }, { "pageSize", tuple.pageSize.ToString() } }; + => new() { + { "page", tuple.page.ToString() }, + { "pageSize", tuple.pageSize.ToString() }, + }; } \ No newline at end of file diff --git a/YandexMusic.API/Requests/Common/YAuthRequestBuilder.cs b/YandexMusic.API/Requests/Common/YPassportRequestBuilder.cs similarity index 54% rename from YandexMusic.API/Requests/Common/YAuthRequestBuilder.cs rename to YandexMusic.API/Requests/Common/YPassportRequestBuilder.cs index ee606b4..5586dfe 100644 --- a/YandexMusic.API/Requests/Common/YAuthRequestBuilder.cs +++ b/YandexMusic.API/Requests/Common/YPassportRequestBuilder.cs @@ -4,15 +4,20 @@ using YandexMusic.API.Requests.Common; namespace YandexMusic.API.Requests; /// Базовый класс для запросов к Passport (passport.yandex.ru). -internal abstract class YAuthRequestBuilder : YJsonRequestBuilder +internal abstract class YPassportRequestBuilder : YJsonRequestBuilder { protected override string BaseUrl => YConstants.Endpoints.PassportUrl; - protected YAuthRequestBuilder(YandexMusicApi api) : base(api) { } + protected YPassportRequestBuilder(YandexMusicApi api) : base(api) { } protected override void SetCustomHeaders(HttpRequestHeaders headers) { base.SetCustomHeaders(headers); headers.Add("X-Requested-With", "XMLHttpRequest"); + if (Api.Storage.HeaderToken != null) + { + headers.Add("X-Csrf-Token", Api.Storage.HeaderToken.CsfrToken); + headers.Add("Process-Uuid", Api.Storage.HeaderToken.ProcessUuid); + } } } \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YCheckPhoneAvailabilityBuilder.cs b/YandexMusic.API/Requests/Passport/YCheckPhoneAvailabilityBuilder.cs new file mode 100644 index 0000000..8910f2e --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YCheckPhoneAvailabilityBuilder.cs @@ -0,0 +1,27 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YCheckPhoneAvailabilityBuilder : YPassportRequestBuilder +{ + public YCheckPhoneAvailabilityBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/suggest/check_availability"; + + protected override HttpContent? GetContent(string phone) + { + var data = new + { + track_id = Api.Storage.AuthToken.TrackId, + phone_number = phone, + can_use_anmon = true, + check_for_push = true, + push_suggest_log_all_subscriptions = false + }; + + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YCheckPushCodeBuilder.cs b/YandexMusic.API/Requests/Passport/YCheckPushCodeBuilder.cs new file mode 100644 index 0000000..48ab75e --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YCheckPushCodeBuilder.cs @@ -0,0 +1,24 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Account; + +namespace YandexMusic.API.Requests.Passport; + +internal class YCheckPushCodeBuilder : YPassportRequestBuilder +{ + public YCheckPushCodeBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/auth/check-push-code"; + + protected override HttpContent? GetContent(string code) + { + var data = new + { + track_id = Api.Storage.AuthToken.TrackId, + code + }; + + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YCheckSessionBuilder.cs b/YandexMusic.API/Requests/Passport/YCheckSessionBuilder.cs new file mode 100644 index 0000000..9f92b25 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YCheckSessionBuilder.cs @@ -0,0 +1,19 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YCheckSessionBuilder : YPassportRequestBuilder +{ + public YCheckSessionBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/sessions/check_session"; + + protected override HttpContent? GetContent(object _) + { + var data = new { track_id = Api.Storage.AuthToken.TrackId }; + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YCreateTrackBuilder.cs b/YandexMusic.API/Requests/Passport/YCreateTrackBuilder.cs new file mode 100644 index 0000000..7658a68 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YCreateTrackBuilder.cs @@ -0,0 +1,13 @@ +using System.Net; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YCreateTrackBuilder : YPassportRequestBuilder +{ + public YCreateTrackBuilder(YandexMusicApi api) : base(api) { } + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/auth/password/submit"; + protected override HttpContent? GetContent(object _) + => new FormUrlEncodedContent(new Dictionary { { "retpath", "" } }); +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Account/YGetAuthLoginQRBuilder.cs b/YandexMusic.API/Requests/Passport/YGetAuthLoginQRBuilder.cs similarity index 53% rename from YandexMusic.API/Requests/Account/YGetAuthLoginQRBuilder.cs rename to YandexMusic.API/Requests/Passport/YGetAuthLoginQRBuilder.cs index 9436ce3..85a034b 100644 --- a/YandexMusic.API/Requests/Account/YGetAuthLoginQRBuilder.cs +++ b/YandexMusic.API/Requests/Passport/YGetAuthLoginQRBuilder.cs @@ -1,10 +1,9 @@ using System.Net; -using System.Net.Http.Headers; -using YandexMusic.API.Models.Account; +using YandexMusic.API.Models.Passport; -namespace YandexMusic.API.Requests.Account; +namespace YandexMusic.API.Requests.Passport; -internal class YGetAuthLoginQRBuilder : YAuthRequestBuilder +internal class YGetAuthLoginQRBuilder : YPassportRequestBuilder { public YGetAuthLoginQRBuilder(YandexMusicApi yandex) : base(yandex) { @@ -20,9 +19,4 @@ internal class YGetAuthLoginQRBuilder : YAuthRequestBuilder +internal class YGetMusicTokenBuilder : YPassportRequestBuilder { public YGetMusicTokenBuilder(YandexMusicApi api) : base(api) { } protected override string BaseUrl => YConstants.Endpoints.MobilePassportUrl; protected override string Method => WebRequestMethods.Http.Post; protected override string PathTemplate => "/1/token"; - protected override HttpContent? GetContent(object _) + protected override HttpContent? GetContent(string passportToken) => new FormUrlEncodedContent(new Dictionary { { "client_id", YConstants.ClientId }, { "client_secret", YConstants.ClientSecret }, { "grant_type", "x-token" }, - { "access_token", Api.Storage.AccessToken.AccessToken } + { "access_token", passportToken } }); protected override void SetCustomHeaders(HttpRequestHeaders headers) { diff --git a/YandexMusic.API/Requests/Passport/YGetQrStatus.cs b/YandexMusic.API/Requests/Passport/YGetQrStatus.cs new file mode 100644 index 0000000..d07ef64 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YGetQrStatus.cs @@ -0,0 +1,17 @@ +using System.Net; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YGetQrStatus : YPassportRequestBuilder +{ + public YGetQrStatus(YandexMusicApi api) : base(api) { } + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/auth/magic/code/status"; + protected override HttpContent? GetContent(object _) + => new FormUrlEncodedContent(new Dictionary + { + ["csrf_token"] = Api.Storage.AuthToken.CsfrToken, + ["track_id"] = Api.Storage.AuthToken.TrackId, + }); +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YGetSessionBuilder.cs b/YandexMusic.API/Requests/Passport/YGetSessionBuilder.cs new file mode 100644 index 0000000..665f7fd --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YGetSessionBuilder.cs @@ -0,0 +1,20 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YGetSessionBuilder : YPassportRequestBuilder +{ + public YGetSessionBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/sessions/get_session"; + + protected override HttpContent? GetContent(object _) + { + var data = new { track_id = Api.Storage.AuthToken.TrackId }; + return JsonContent.Create(data); + } + +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YMultiStepPasswordBuilder.cs b/YandexMusic.API/Requests/Passport/YMultiStepPasswordBuilder.cs new file mode 100644 index 0000000..32f64f3 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YMultiStepPasswordBuilder.cs @@ -0,0 +1,23 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YMultiStepPasswordBuilder : YPassportRequestBuilder +{ + public YMultiStepPasswordBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/auth/multistep_password"; + + protected override HttpContent? GetContent(string password) + { + var data = new + { + track_id = Api.Storage.AuthToken.TrackId, + password + }; + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YMultistepStartBuilder.cs b/YandexMusic.API/Requests/Passport/YMultistepStartBuilder.cs new file mode 100644 index 0000000..f7ba7d5 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YMultistepStartBuilder.cs @@ -0,0 +1,35 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YMultistepStartBuilder : YPassportRequestBuilder +{ + public YMultistepStartBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/auth/multistep_start"; + + protected override HttpContent? GetContent(string login) + { + var data = new + { + login, + track_id = Api.Storage.AuthToken.TrackId, + display_language = "ru", + retpath = string.Empty, + can_send_push_code = true, + check_for_xtokens_for_pictures = false, + force_check_for_protocols = true, + app_id = "ru.yandex.music", + am_version_name = "7.50.2(750024597)", + app_platform = "android", + app_version_name = "2026.02.3 #135rur", + device_id = Api.Storage.DeviceId, + device_connection_type = "9" + }; + + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YRfcOtpBuilder.cs b/YandexMusic.API/Requests/Passport/YRfcOtpBuilder.cs new file mode 100644 index 0000000..96411d3 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YRfcOtpBuilder.cs @@ -0,0 +1,23 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YRfcOtpBuilder : YPassportRequestBuilder +{ + public YRfcOtpBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/auth/rfc-otp"; + + protected override HttpContent? GetContent(string otp) + { + var data = new + { + track_id = Api.Storage.AuthToken.TrackId, + otp + }; + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YSendPushBuilder.cs b/YandexMusic.API/Requests/Passport/YSendPushBuilder.cs new file mode 100644 index 0000000..6c0f841 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YSendPushBuilder.cs @@ -0,0 +1,26 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YSendPushBuilder : YPassportRequestBuilder +{ + public YSendPushBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/auth/suggest-send-push"; + + protected override HttpContent? GetContent(string phone) + { + var data = new + { + track_id = Api.Storage.AuthToken.TrackId, + phone_number = phone, + can_use_anmon = true, + force_show_code_in_notification = "1", + country = Api.Storage.Country + }; + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YSuggestByPhoneBuilder.cs b/YandexMusic.API/Requests/Passport/YSuggestByPhoneBuilder.cs new file mode 100644 index 0000000..be35338 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YSuggestByPhoneBuilder.cs @@ -0,0 +1,23 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YSuggestByPhoneBuilder : YPassportRequestBuilder +{ + public YSuggestByPhoneBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/suggest/by_phone"; + + protected override HttpContent? GetContent(object _) + { + var data = new + { + track_id = Api.Storage.AuthToken.TrackId, + can_use_anmon = true + }; + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YValidatePhoneNumberBuilder.cs b/YandexMusic.API/Requests/Passport/YValidatePhoneNumberBuilder.cs new file mode 100644 index 0000000..b494774 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YValidatePhoneNumberBuilder.cs @@ -0,0 +1,24 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YValidatePhoneNumberBuilder : YPassportRequestBuilder +{ + public YValidatePhoneNumberBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/validate/phone_number"; + + protected override HttpContent? GetContent(string phone) + { + var data = new + { + track_id = Api.Storage.AuthToken.TrackId, + phone_number = phone, + country = Api.Storage.Country + }; + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Passport/YValidateSquatterBuilder.cs b/YandexMusic.API/Requests/Passport/YValidateSquatterBuilder.cs new file mode 100644 index 0000000..0a90a05 --- /dev/null +++ b/YandexMusic.API/Requests/Passport/YValidateSquatterBuilder.cs @@ -0,0 +1,25 @@ +using System.Net; +using System.Net.Http.Json; +using YandexMusic.API.Models.Passport; + +namespace YandexMusic.API.Requests.Passport; + +internal class YValidateSquatterBuilder : YPassportRequestBuilder +{ + public YValidateSquatterBuilder(YandexMusicApi api) : base(api) { } + + protected override string Method => WebRequestMethods.Http.Post; + protected override string PathTemplate => "pwl-yandex/api/passport/validate/squatter"; + + protected override HttpContent? GetContent(string phone) + { + var data = new + { + track_id = Api.Storage.AuthToken.TrackId, + phone_number = phone, + scenario = "auth", + can_use_anmon = true + }; + return JsonContent.Create(data); + } +} \ No newline at end of file diff --git a/YandexMusic.API/Requests/Track/YStorageDownloadFileBuilder.cs b/YandexMusic.API/Requests/Track/YStorageDownloadFileBuilder.cs index cc31cdd..589c4f2 100644 --- a/YandexMusic.API/Requests/Track/YStorageDownloadFileBuilder.cs +++ b/YandexMusic.API/Requests/Track/YStorageDownloadFileBuilder.cs @@ -9,11 +9,11 @@ namespace YandexMusic.API.Requests.Track; internal class YStorageDownloadFileBuilder : YJsonRequestBuilder { public YStorageDownloadFileBuilder(YandexMusicApi api) : base(api) { } - protected override string BaseUrl => ""; // не используется, т.к. URL берётся из параметра + protected override string BaseUrl => "{src}"; // не используется, т.к. URL берётся из параметра protected override string Method => WebRequestMethods.Http.Get; - protected override string PathTemplate => "{src}"; + protected override string PathTemplate => ""; protected override Dictionary GetSubstitutions(string src) => new() { { "src", src.Split('?')[0] } }; @@ -26,7 +26,7 @@ internal class YStorageDownloadFileBuilder : YJsonRequestBuilder= 2) query.Add(kv[0], kv[1]); } } return query; diff --git a/YandexMusic.API/Requests/Ugc/YUgcUploadBuilder.cs b/YandexMusic.API/Requests/Ugc/YUgcUploadBuilder.cs index 6fd7c91..c4a493a 100644 --- a/YandexMusic.API/Requests/Ugc/YUgcUploadBuilder.cs +++ b/YandexMusic.API/Requests/Ugc/YUgcUploadBuilder.cs @@ -8,11 +8,11 @@ namespace YandexMusic.API.Requests.Ugc; internal class YUgcUploadBuilder : YJsonRequestBuilder?, (string postTargetLink, byte[] fileBytes)> { public YUgcUploadBuilder(YandexMusicApi api) : base(api) { } - protected override string BaseUrl => ""; + protected override string BaseUrl => "{postTargetLink}"; protected override string Method => WebRequestMethods.Http.Post; - protected override string PathTemplate => "{postTargetLink}"; + protected override string PathTemplate => ""; protected override Dictionary GetSubstitutions((string postTargetLink, byte[] fileBytes) tuple) => new() { { "postTargetLink", tuple.postTargetLink } }; diff --git a/YandexMusic.API/YandexMusicApi.cs b/YandexMusic.API/YandexMusicApi.cs index b05b7ad..83fafa1 100644 --- a/YandexMusic.API/YandexMusicApi.cs +++ b/YandexMusic.API/YandexMusicApi.cs @@ -6,9 +6,9 @@ namespace YandexMusic.API; public class YandexMusicApi { /// HttpClient, используемый для всех запросов. - public HttpClient HttpClient { get; } + internal HttpClient HttpClient { get; } /// Хранилище данных авторизации. - public AuthStorage Storage { get; } + internal AuthStorage Storage { get; } /// API для работы с альбомами. public YAlbumAPI Album { get; internal set; } = null!; @@ -33,11 +33,13 @@ public class YandexMusicApi /// API для работы с очередями. public YQueueAPI Queue { get; internal set; } = null!; /// API для работы с пользователем и авторизацией. - public YUserAPI User { get; internal set; } = null!; + public YAuthAPI Auth { get; internal set; } = null!; /// API для загрузки пользовательского контента. public YUgcAPI UserGeneratedContent { get; internal set; } = null!; /// API для работы с протоколом Ynison (WebSocket). public YYnisonAPI Ynison { get; internal set; } = null!; + /// API для работы с яндекс пасспорт. + public YPassportAPI Passport { get; internal set; } = null!; /// /// Инициализирует новый экземпляр API. @@ -49,19 +51,20 @@ public class YandexMusicApi HttpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient)); Storage = storage ?? throw new ArgumentNullException(nameof(storage)); - Album = new YAlbumAPI(this); - Artist = new YArtistAPI(this); - Label = new YLabelAPI(this); - Landing = new YLandingAPI(this); - Library = new YLibraryAPI(this); - Playlist = new YPlaylistAPI(this); - Pins = new YPinsAPI(this); - Radio = new YRadioAPI(this); - Search = new YSearchAPI(this); - Track = new YTrackAPI(this); - Queue = new YQueueAPI(this); - User = new YUserAPI(this); - UserGeneratedContent = new YUgcAPI(this); - Ynison = new YYnisonAPI(this); + Album = new(this); + Artist = new(this); + Label = new(this); + Landing = new(this); + Library = new(this); + Playlist = new(this); + Pins = new(this); + Radio = new(this); + Search = new(this); + Track = new(this); + Queue = new(this); + Auth = new(this); + UserGeneratedContent = new(this); + Ynison = new(this); + Passport = new(this); } } \ No newline at end of file diff --git a/YandexMusic.slnx b/YandexMusic.slnx index 9f6300b..9c0c3da 100644 --- a/YandexMusic.slnx +++ b/YandexMusic.slnx @@ -1,5 +1,5 @@ - + diff --git a/YandexMusic/YandexMusicClient.cs b/YandexMusic/YandexMusicClient.cs index 9783742..b9d233f 100644 --- a/YandexMusic/YandexMusicClient.cs +++ b/YandexMusic/YandexMusicClient.cs @@ -3,18 +3,6 @@ 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; @@ -41,6 +29,9 @@ public class YandexMusicClient : IDisposable /// HttpClient, используемый клиентом (можно получить для настройки кук). public HttpClient HttpClient => _httpClient; + /// API Яндекс Музыки. + public YandexMusicApi Api => _api; + /// Создаёт новый экземпляр клиента с собственным HttpClient. public YandexMusicClient( CookieContainer? cookieContainer = null, @@ -78,348 +69,9 @@ public class YandexMusicClient : IDisposable _api = new YandexMusicApi(_httpClient, _storage); } - #region Авторизация - - /// Авторизация по готовому OAuth-токену. - public async Task Authorize(string token) - { - await _api.User.AuthorizeAsync(token); - return _storage.IsAuthorized; - } - - /// Создание сеанса и получение доступных методов авторизации. - public Task CreateAuthSession(string userName) - => _api.User.CreateAuthSessionAsync(userName); - - /// Получение ссылки на QR-код для авторизации. - public Task GetAuthQRLink() - => _api.User.GetAuthQRLinkAsync(); - - /// Проверка состояния сканирования QR-кода. - public Task CheckQRStatusAsync() - => _api.User.CheckQRStatusAsync(); - - /// Авторизация по QR-коду (после сканирования). - public Task AuthorizeByQR() - => _api.User.AuthorizeByQRAsync(); - - /// Получение капчи. - public Task GetCaptcha() - => _api.User.GetCaptchaAsync(); - - /// Авторизация с вводом капчи. - public Task AuthorizeByCaptcha(string captcha) - => _api.User.AuthorizeByCaptchaAsync(captcha); - - /// Запрос письма для авторизации. - public Task GetAuthLetter() - => _api.User.GetAuthLetterAsync(); - - /// Подтверждение авторизации по письму. - public Task AuthorizeByLetter() - => _api.User.AuthorizeByLetterAsync(); - - /// Авторизация по паролю приложения. - public Task AuthorizeByAppPassword(string password) - => _api.User.AuthorizeByAppPasswordAsync(password); - - /// Получение AccessToken после успешной авторизации. - public Task GetAccessToken() - => _api.User.GetAccessTokenAsync(); - - /// Получение информации о пользователе через логин Яндекса. - public Task GetLoginInfo() - => _api.User.GetLoginInfoAsync(); - - #endregion - - #region Треки - - /// Получает трек по идентификатору. - public async Task GetTrackAsync(string id) - => (await _api.Track.GetAsync(id)); - - /// Получает список треков по идентификаторам. - public async Task> GetTracksAsync(IEnumerable ids) - => (await _api.Track.GetAsync(ids)) ?? new List(); - - #endregion - - #region Альбомы - - /// Получает альбом по идентификатору. - public Task GetAlbumAsync(string id) - => _api.Album.GetAsync(id); - - /// Получает список альбомов по идентификаторам. - public async Task> GetAlbumsAsync(IEnumerable ids) - => (await _api.Album.GetAsync(ids)) ?? new List(); - - #endregion - - #region Главная страница - - /// Получает персональные блоки лендинга. - public Task GetLandingAsync(params YLandingBlockType[] blocks) - => _api.Landing.GetAsync(blocks); - - /// Получает ленту событий. - public Task GetFeedAsync() - => _api.Landing.GetFeedAsync(); - - /// Получает детский лендинг. - public Task GetChildrenLandingAsync() - => _api.Landing.GetChildrenLandingAsync(); - - #endregion - - #region Исполнители - - /// Получает информацию об исполнителе. - public Task GetArtistAsync(string id) - => _api.Artist.GetAsync(id); - - /// Получает список исполнителей. - public async Task> GetArtistsAsync(IEnumerable ids) - => (await _api.Artist.GetAsync(ids)) ?? new List(); - - #endregion - - #region Плейлисты - - /// Получает плейлист по пользователю и идентификатору. - public Task GetPlaylistAsync(string user, string id) - => _api.Playlist.GetAsync(user, id); - - /// Получает плейлист по UUID. - public Task GetPlaylistAsync(string uuid) - => _api.Playlist.GetAsync(uuid); - - /// Получает список плейлистов по списку пар (пользователь, идентификатор). - public async Task> GetPlaylistsAsync(IEnumerable<(string user, string id)> ids) - => (await _api.Playlist.GetAsync(ids)) ?? new List(); - - /// Получает персональные плейлисты (с главной страницы). - public Task> GetPersonalPlaylistsAsync() - => _api.Playlist.GetPersonalPlaylistsAsync(); - - /// Получает избранные плейлисты. - public async Task> GetFavoritesAsync() - => (await _api.Playlist.FavoritesAsync()) ?? new List(); - - /// Получает плейлист «Дежавю». - public Task GetDejaVuAsync() - => _api.Playlist.DejaVuAsync(); - - /// Получает плейлист «Тайник». - public Task GetMissedAsync() - => _api.Playlist.MissedAsync(); - - /// Получает плейлист дня. - public Task GetOfTheDayAsync() - => _api.Playlist.OfTheDayAsync(); - - /// Получает плейлист «Кинопоиск». - public Task GetKinopoiskAsync() - => _api.Playlist.KinopoiskAsync(); - - /// Получает плейлист «Премьера». - public Task GetPremiereAsync() - => _api.Playlist.PremiereAsync(); - - /// Создаёт новый плейлист с заданным именем. - public Task CreatePlaylistAsync(string name) - => _api.Playlist.CreateAsync(name); - - #endregion - - #region Поиск - - /// Выполняет поиск. - public Task SearchAsync(string searchText, YSearchType searchType, int page = 0, int pageSize = 20) - => _api.Search.SearchAsync(searchText, searchType, page, pageSize); - - /// Получает подсказки для поискового запроса. - public Task GetSearchSuggestionsAsync(string searchText) - => _api.Search.GetSearchSuggestionsAsync(searchText); - - #endregion - - #region Библиотека - - /// Получает лайкнутые треки. - public async Task> GetLikedTracksAsync() - { - var likes = await _api.Library.GetLikedTracksAsync(); - var ids = likes?.Library?.Tracks.Select(t => t.Id).ToArray() ?? Array.Empty(); - if (ids.Length == 0) return new List(); - var tracks = await _api.Track.GetAsync(ids); - return tracks ?? new List(); - } - - /// Получает дизлайкнутые треки. - public async Task> GetDislikedTracksAsync() - { - var dislikes = await _api.Library.GetDislikedTracksAsync(); - var ids = dislikes?.Library?.Tracks.Select(t => t.Id).ToArray() ?? Array.Empty(); - if (ids.Length == 0) return new List(); - var tracks = await _api.Track.GetAsync(ids); - return tracks ?? new List(); - } - - /// Получает лайкнутые альбомы. - public async Task> GetLikedAlbumsAsync() - { - var albums = await _api.Library.GetLikedAlbumsAsync(); - var ids = albums?.Select(a => a.Id).ToArray() ?? Array.Empty(); - if (ids.Length == 0) return new List(); - var result = await _api.Album.GetAsync(ids); - return result ?? new List(); - } - - /// Получает лайкнутых исполнителей. - public async Task> GetLikedArtistsAsync() - { - var artists = await _api.Library.GetLikedArtistsAsync(); - var ids = artists?.Select(a => a.Id).ToArray() ?? Array.Empty(); - if (ids.Length == 0) return new List(); - var result = await _api.Artist.GetAsync(ids); - return result ?? new List(); - } - - /// Получает дизлайкнутых исполнителей. - public async Task> GetDislikedArtistsAsync() - { - var artists = await _api.Library.GetDislikedArtistsAsync(); - var ids = artists?.Select(a => a.Id).ToArray() ?? Array.Empty(); - if (ids.Length == 0) return new List(); - var result = await _api.Artist.GetAsync(ids); - return result ?? new List(); - } - - /// Получает лайкнутые плейлисты. - public async Task> GetLikedPlaylistsAsync() - { - var playlists = await _api.Library.GetLikedPlaylistsAsync(); - var ids = playlists? - .Select(p => (p.Playlist.Uid, p.Playlist.Kind)) - .ToArray() ?? Array.Empty<(string, string)>(); - if (ids.Length == 0) return new List(); - var result = await _api.Playlist.GetAsync(ids); - return result ?? new List(); - } - - /// Получает список недавно прослушанных треков. - public async Task> GetRecentlyListenedAsync( - IEnumerable contextTypes, - int trackCount = 50, - int contextCount = 10) - { - var response = await _api.Library.GetRecentlyListenedAsync(contextTypes, trackCount, contextCount); - return response?.Contexts ?? new List(); - } - - #endregion - - #region Радио - - /// Получает список рекомендованных радиостанций. - public async Task> GetRadioDashboardAsync() - { - var dashboard = await _api.Radio.GetStationsDashboardAsync(); - return dashboard?.Stations ?? new List(); - } - - /// Получает список всех радиостанций. - public async Task> GetRadioStationsAsync() - => (await _api.Radio.GetStationsAsync()) ?? new List(); - - /// Получает информацию о радиостанции по идентификатору. - public async Task GetRadioStationAsync(YStationId id) - => (await _api.Radio.GetStationAsync(id))?.FirstOrDefault(); - - #endregion - - #region Очереди - - /// Получает все очереди с разных устройств. - public Task GetQueuesAsync(string? device = null) - => _api.Queue.ListAsync(device); - - /// Получает очередь по идентификатору. - public Task GetQueueAsync(string queueId) - => _api.Queue.GetAsync(queueId); - - /// Создаёт новую очередь. - public Task CreateQueueAsync(YQueue queue, string? device = null) - => _api.Queue.CreateAsync(queue, device); - - /// Обновляет позицию в очереди. - public Task UpdateQueuePositionAsync(string queueId, int currentIndex, bool isInteractive, string? device = null) - => _api.Queue.UpdatePositionAsync(queueId, currentIndex, isInteractive, device); - - #endregion - - #region Загрузка треков (UGC) - - /// Загружает трек из файла в плейлист. - public Task UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, string filePath) - => _api.UserGeneratedContent.UploadTrackToPlaylistAsync(playlist, fileName, filePath); - - /// Загружает трек из потока в плейлист. - public Task UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, Stream stream) - => _api.UserGeneratedContent.UploadTrackToPlaylistAsync(playlist, fileName, stream); - - /// Загружает трек из массива байтов в плейлист. - public Task UploadTrackToPlaylistAsync(YPlaylist playlist, string fileName, byte[] file) - => _api.UserGeneratedContent.UploadTrackToPlaylistAsync(playlist, fileName, file); - - #endregion - - #region Лейблы - - /// Получает альбомы лейбла с пагинацией. - public async Task> GetAlbumsByLabelAsync(YLabel label, int page = 0) - => (await _api.Label.GetAlbumsByLabelAsync(label, page))?.Albums ?? new List(); - - /// Получает артистов лейбла с пагинацией. - public async Task> GetArtistsByLabelAsync(YLabel label, int page = 0) - => (await _api.Label.GetArtistsByLabelAsync(label, page))?.Artists ?? new List(); - - #endregion - - #region Ynison (WebSocket плеер) - - /// Подключается к Ynison и запускает синхронизацию состояния плеера. - public async Task ConnectYnisonAsync() - { - if (_player == null) - _player = _api.Ynison.GetPlayer(); - await _player.ConnectAsync(); - } - - /// Отключается от Ynison. - public async Task DisconnectYnisonAsync() - { - if (_player != null) - await _player.DisconnectAsync(); - } - - #endregion - - #region IDisposable - - private bool _disposed; - - /// Освобождает ресурсы. public void Dispose() { - if (_disposed) return; + _httpClient?.Dispose(); _player?.Dispose(); - _httpClient.Dispose(); - _disposed = true; - GC.SuppressFinalize(this); } - - #endregion } \ No newline at end of file