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