Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 526353d679 | |||
| eb1eba0162 | |||
|
|
34261d02a9 | ||
|
|
5f761d4fe8 | ||
|
|
b6f78da9c8 | ||
|
|
0bbaac5689 | ||
|
|
a7caf829d3 | ||
|
|
add7f08215 |
111
YandexMusic.API/API/YAuthAPI.cs
Normal file
111
YandexMusic.API/API/YAuthAPI.cs
Normal file
@@ -0,0 +1,111 @@
|
||||
using System.Security.Authentication;
|
||||
using YandexMusic.API.Models.Account;
|
||||
using YandexMusic.API.Requests.Account;
|
||||
|
||||
namespace YandexMusic.API;
|
||||
|
||||
/// <summary>API для работы с пользователем и авторизации.</summary>
|
||||
public class YAuthAPI : YCommonAPI
|
||||
{
|
||||
public YAuthAPI(YandexMusicApi api) : base(api) { }
|
||||
|
||||
/// <summary>Авторизация по готовому музыкальному токену (OAuth).</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>Авторизация по паспортному токену (полученному, например, после QR-входа).</summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>Получение информации о текущем аккаунте.</summary>
|
||||
public Task<YAccountResult?> GetUserAuthAsync()
|
||||
=> new YGetAuthInfoBuilder(Api).ExecuteAsync(null!);
|
||||
|
||||
/// <summary>Создание сессии авторизации (получение доступных методов входа).</summary>
|
||||
public async Task<YAuthTypes?> 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;
|
||||
}
|
||||
|
||||
/// <summary>Получение капчи.</summary>
|
||||
public Task<YAuthCaptcha?> GetCaptchaAsync()
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
|
||||
return new YGetAuthCaptchaBuilder(Api).ExecuteAsync(null!);
|
||||
}
|
||||
|
||||
/// <summary>Авторизация по капче.</summary>
|
||||
public Task<YAuthBase?> AuthorizeByCaptchaAsync(string captchaValue)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
|
||||
return new YGetAuthLoginCaptchaBuilder(Api).ExecuteAsync(captchaValue);
|
||||
}
|
||||
|
||||
/// <summary>Отправить письмо для авторизации.</summary>
|
||||
public Task<YAuthLetter?> GetAuthLetterAsync()
|
||||
=> new YGetAuthLetterBuilder(Api).ExecuteAsync(null!);
|
||||
|
||||
/// <summary>Подтверждение входа по письму и получение музыкального токена.</summary>
|
||||
public async Task<bool> 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;
|
||||
}
|
||||
|
||||
/// <summary>Авторизация по паролю приложения Яндекс.</summary>
|
||||
public async Task<YAuthBase?> 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;
|
||||
}
|
||||
|
||||
/// <summary>Получение информации о пользователе через логин.</summary>
|
||||
public Task<YLoginInfo?> GetLoginInfoAsync()
|
||||
=> new YGetLoginInfoBuilder(Api).ExecuteAsync(null!);
|
||||
}
|
||||
230
YandexMusic.API/API/YPassportAPI.cs
Normal file
230
YandexMusic.API/API/YPassportAPI.cs
Normal file
@@ -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;
|
||||
|
||||
/// <summary>API для работы с яндекс паспортом</summary>
|
||||
public class YPassportAPI : YCommonAPI
|
||||
{
|
||||
public YPassportAPI(YandexMusicApi api) : base(api) { }
|
||||
|
||||
public async Task<YAccessToken?> 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<YAccessToken?> GetMusicTokenByPassportTokenAsync(string passportToken)
|
||||
=> await GetAccessTokenAsync(passportToken);
|
||||
|
||||
public async Task<string?> 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<YAuthQRStatus?> 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<YAuthQrSession?> 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");
|
||||
}
|
||||
|
||||
/// <summary>Многоступенчатая авторизация: начало (передача логина).</summary>
|
||||
public async Task<YMultistepStart?> MultistepStartAsync(string login)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована. Вызовите GetAuthQRLinkAsync или CreateTrackAsync");
|
||||
return await new YMultistepStartBuilder(Api).ExecuteAsync(login);
|
||||
}
|
||||
|
||||
/// <summary>Многоступенчатая авторизация: ввод пароля.</summary>
|
||||
public async Task<YPassportUser?> MultistepPasswordAsync(string password)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YMultiStepPasswordBuilder(Api).ExecuteAsync(password);
|
||||
}
|
||||
|
||||
/// <summary>Авторизация с помощью RFC OTP.</summary>
|
||||
public async Task<YPassportUser?> RfcOtpPasswordAsync(string rfcOtp)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YRfcOtpBuilder(Api).ExecuteAsync(rfcOtp);
|
||||
}
|
||||
|
||||
/// <summary>Создание сессии пользователя.</summary>
|
||||
public async Task<YPassportSession?> CreateUserSessionAsync()
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YGetSessionBuilder(Api).ExecuteAsync(null!);
|
||||
}
|
||||
|
||||
/// <summary>Проверка состояния сессии.</summary>
|
||||
public async Task<YPassportSessionStatus?> GetSessionStateAsync()
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YCheckSessionBuilder(Api).ExecuteAsync(null!);
|
||||
}
|
||||
|
||||
/// <summary>Проверка номера телефона (валидация).</summary>
|
||||
public async Task<YValidatePhoneNumberResult?> ValidatePhoneNumberAsync(string phone)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YValidatePhoneNumberBuilder(Api).ExecuteAsync(phone);
|
||||
}
|
||||
|
||||
/// <summary>Проверка доступности номера.</summary>
|
||||
public async Task<YCheckAvailabilityResult?> CheckPhoneAvailabilityAsync(string phone)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YCheckPhoneAvailabilityBuilder(Api).ExecuteAsync(phone);
|
||||
}
|
||||
|
||||
/// <summary>Запрос на отправку push-уведомления.</summary>
|
||||
public async Task<YSendPushResult?> SuggestSendPushAsync(string phone)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YSendPushBuilder(Api).ExecuteAsync(phone);
|
||||
}
|
||||
|
||||
/// <summary>Проверка push-кода.</summary>
|
||||
public async Task CheckPushCodeAsync(string code)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
await new YCheckPushCodeBuilder(Api).ExecuteAsync(code);
|
||||
}
|
||||
|
||||
/// <summary>Проверка на "сквоттера" (захват номера).</summary>
|
||||
public async Task<YValidateSquatter?> ValidateSquatterAsync(string phone)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YValidateSquatterBuilder(Api).ExecuteAsync(phone);
|
||||
}
|
||||
|
||||
/// <summary>Получение списка аккаунтов по номеру телефона.</summary>
|
||||
public async Task<YSuggestByPhoneResult?> SuggestByPhoneAsync()
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
return await new YSuggestByPhoneBuilder(Api).ExecuteAsync(null!);
|
||||
}
|
||||
|
||||
/// <summary>Вход по паролю (упрощённый, если не нужна многоступенчатость).</summary>
|
||||
public async Task<YPassportUser?> 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<bool> 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<YAccessToken?> 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<bool> LoginByCookiesAsync()
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new AuthenticationException("Сессия входа не инициализирована");
|
||||
|
||||
var accessToken = await new YGetAuthCookiesBuilder(Api).ExecuteAsync(null!);
|
||||
if (accessToken == null || string.IsNullOrEmpty(accessToken.AccessToken))
|
||||
return false;
|
||||
|
||||
await GetAccessTokenAsync(accessToken.AccessToken);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,8 @@ public class YTrackAPI : YCommonAPI
|
||||
return $"https://{host}/get-{codec}/{sign}/{ts}{path}";
|
||||
}
|
||||
|
||||
public Task<YTrack?> GetAsync(string trackId)
|
||||
=> GetAsync(trackId);
|
||||
public async Task<YTrack?> GetAsync(string trackId)
|
||||
=> (await GetAsync([trackId]))?.FirstOrDefault();
|
||||
|
||||
public Task<List<YTrack>?> GetAsync(IEnumerable<string> trackIds)
|
||||
=> new YGetTracksBuilder(Api).ExecuteAsync(trackIds);
|
||||
|
||||
@@ -1,159 +0,0 @@
|
||||
using System.Security.Authentication;
|
||||
using System.Text.RegularExpressions;
|
||||
using YandexMusic.API.Models.Account;
|
||||
using YandexMusic.API.Requests.Account;
|
||||
|
||||
namespace YandexMusic.API;
|
||||
|
||||
/// <summary>API для работы с пользователем и авторизации.</summary>
|
||||
public class YUserAPI : YCommonAPI
|
||||
{
|
||||
public YUserAPI(YandexMusicApi api) : base(api) { }
|
||||
|
||||
private async Task<bool> 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.AuthToken = new YAuthToken
|
||||
{
|
||||
CsfrToken = csrfMatch.Groups[1].Value,
|
||||
ProcessUuid = processMatch.Groups[1].Value
|
||||
};
|
||||
|
||||
await new YPostAuthStats(Api).ExecuteAsync(null!);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task<bool> LoginByCookiesAsync()
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new AuthenticationException("Сессия входа не инициализирована");
|
||||
|
||||
var accessToken = await new YGetAuthCookiesBuilder(Api).ExecuteAsync(null!);
|
||||
if (accessToken == null || string.IsNullOrEmpty(accessToken.AccessToken))
|
||||
return false;
|
||||
|
||||
Api.Storage.AccessToken = accessToken;
|
||||
Api.Storage.Token = accessToken.AccessToken;
|
||||
|
||||
var shortInfo = await new YGetShortAccountInfoBuilder(Api).ExecuteAsync(null!);
|
||||
if (shortInfo?.Status != YAuthStatus.Ok || string.IsNullOrWhiteSpace(shortInfo.Uid))
|
||||
throw new Exception("Не удалось подтвердить авторизацию");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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 Task<YAccountResult?> GetUserAuthAsync()
|
||||
=> new YGetAuthInfoBuilder(Api).ExecuteAsync(null!);
|
||||
|
||||
public async Task<YAuthTypes?> 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<string?> 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<YAuthQRStatus?> AuthorizeByQRAsync()
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new Exception("Сессия не инициализирована");
|
||||
|
||||
var status = await new YGetAuthLoginQRBuilder(Api).ExecuteAsync(null!);
|
||||
if (status?.Status == YAuthStatus.Ok && await LoginByCookiesAsync())
|
||||
return status;
|
||||
throw new AuthenticationException("Ошибка авторизации по QR");
|
||||
}
|
||||
|
||||
public Task<YAuthCaptcha?> GetCaptchaAsync()
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
|
||||
return new YGetAuthCaptchaBuilder(Api).ExecuteAsync(null!);
|
||||
}
|
||||
|
||||
public Task<YAuthBase?> AuthorizeByCaptchaAsync(string captchaValue)
|
||||
{
|
||||
if (Api.Storage.AuthToken == null)
|
||||
throw new AuthenticationException("Выполните CreateAuthSessionAsync перед использованием");
|
||||
return new YGetAuthLoginCaptchaBuilder(Api).ExecuteAsync(captchaValue);
|
||||
}
|
||||
|
||||
public Task<YAuthLetter?> GetAuthLetterAsync()
|
||||
=> new YGetAuthLetterBuilder(Api).ExecuteAsync(null!);
|
||||
|
||||
public async Task<bool> 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<YAuthBase?> 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<YAccessToken?> 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<YLoginInfo?> GetLoginInfoAsync()
|
||||
=> new YGetLoginInfoBuilder(Api).ExecuteAsync(null!);
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Common;
|
||||
@@ -7,6 +8,15 @@ namespace YandexMusic.API.Common;
|
||||
/// </summary>
|
||||
public class AuthStorage
|
||||
{
|
||||
private CookieContainer _cookieContainer;
|
||||
|
||||
public AuthStorage(CookieContainer cookieContainer)
|
||||
{
|
||||
_cookieContainer = cookieContainer;
|
||||
}
|
||||
|
||||
public CookieContainer CookieContainer => _cookieContainer;
|
||||
|
||||
/// <summary>
|
||||
/// Флаг, указывающий, авторизован ли пользователь.
|
||||
/// </summary>
|
||||
@@ -35,7 +45,17 @@ public class AuthStorage
|
||||
/// <summary>
|
||||
/// Внутренние данные авторизации (CSRF, track_id и т.д.).
|
||||
/// </summary>
|
||||
internal YAuthToken AuthToken { get; set; } = new();
|
||||
public YAuthToken? HeaderToken { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Внутренние данные авторизации (CSRF, track_id и т.д.).
|
||||
/// </summary>
|
||||
public YAuthToken? AuthToken { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Страна, используемая для авторизации (по умолчанию "ru"). Может влиять на язык интерфейса и доступные методы авторизации.
|
||||
/// </summary>
|
||||
public object Country { get; set; } = "ru";
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливает флаг авторизации и сохраняет информацию об аккаунте.
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Converters;
|
||||
|
||||
public class IntToStringConverter : JsonConverter<string>
|
||||
internal class IntToStringConverter : JsonConverter<string>
|
||||
{
|
||||
public override string Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using System.Text.Json.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Converters;
|
||||
|
||||
public class StringToIntConverter : JsonConverter<int>
|
||||
internal class StringToIntConverter : JsonConverter<int>
|
||||
{
|
||||
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
using YandexMusic.API.Models.Album;
|
||||
|
||||
namespace YandexMusic.API.Extensions.API;
|
||||
namespace YandexMusic.API;
|
||||
|
||||
/// <summary>
|
||||
/// Методы-расширения для альбома.
|
||||
@@ -15,7 +15,7 @@ public static class YAlbumExtensions
|
||||
if (album.Volumes != null)
|
||||
return album;
|
||||
|
||||
var result = await album.Context.API.Album.GetAsync(album.Id);
|
||||
var result = await album.Context.Api.Album.GetAsync(album.Id);
|
||||
return result ?? album;
|
||||
}
|
||||
|
||||
@@ -23,11 +23,11 @@ public static class YAlbumExtensions
|
||||
/// Добавляет альбом в список лайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<string?> AddLikeAsync(this YAlbum album)
|
||||
=> await album.Context.API.Library.AddAlbumLikeAsync(album);
|
||||
=> await album.Context.Api.Library.AddAlbumLikeAsync(album);
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет альбом из списка лайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<string?> RemoveLikeAsync(this YAlbum album)
|
||||
=> await album.Context.API.Library.RemoveAlbumLikeAsync(album);
|
||||
=> await album.Context.Api.Library.RemoveAlbumLikeAsync(album);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using YandexMusic.API.Models.Artist;
|
||||
using YandexMusic.API.Models.Track;
|
||||
|
||||
namespace YandexMusic.API.Extensions.API;
|
||||
namespace YandexMusic.API;
|
||||
|
||||
/// <summary>
|
||||
/// Методы-расширения для исполнителя.
|
||||
@@ -12,29 +12,29 @@ public static class YArtistExtensions
|
||||
/// Получает расширенную информацию об исполнителе.
|
||||
/// </summary>
|
||||
public static async Task<YArtistBriefInfo?> BriefInfoAsync(this YArtist artist)
|
||||
=> await artist.Context.API.Artist.GetAsync(artist.Id);
|
||||
=> await artist.Context.Api.Artist.GetAsync(artist.Id);
|
||||
|
||||
/// <summary>
|
||||
/// Получает страницу треков исполнителя.
|
||||
/// </summary>
|
||||
public static async Task<YTracksPage?> GetTracksAsync(this YArtist artist, int page = 0, int pageSize = 20)
|
||||
=> await artist.Context.API.Artist.GetTracksAsync(artist.Id, page, pageSize);
|
||||
=> await artist.Context.Api.Artist.GetTracksAsync(artist.Id, page, pageSize);
|
||||
|
||||
/// <summary>
|
||||
/// Получает все треки исполнителя.
|
||||
/// </summary>
|
||||
public static async Task<List<YTrack>?> GetAllTracksAsync(this YArtist artist)
|
||||
=> (await artist.Context.API.Artist.GetAllTracksAsync(artist.Id))?.Tracks;
|
||||
=> (await artist.Context.Api.Artist.GetAllTracksAsync(artist.Id))?.Tracks;
|
||||
|
||||
/// <summary>
|
||||
/// Добавляет исполнителя в список лайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<string?> AddLikeAsync(this YArtist artist)
|
||||
=> await artist.Context.API.Library.AddArtistLikeAsync(artist);
|
||||
=> await artist.Context.Api.Library.AddArtistLikeAsync(artist);
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет исполнителя из списка лайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<string?> RemoveLikeAsync(this YArtist artist)
|
||||
=> await artist.Context.API.Library.RemoveArtistLikeAsync(artist);
|
||||
=> await artist.Context.Api.Library.RemoveArtistLikeAsync(artist);
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using YandexMusic.API.Models.Playlist;
|
||||
using YandexMusic.API.Models.Track;
|
||||
|
||||
namespace YandexMusic.API.Extensions.API;
|
||||
namespace YandexMusic.API;
|
||||
|
||||
/// <summary>
|
||||
/// Методы-расширения для плейлиста.
|
||||
@@ -18,44 +18,44 @@ public static class YPlaylistExtensions
|
||||
{
|
||||
if (playlist.Tracks != null)
|
||||
return playlist;
|
||||
return await playlist.Context.API.Playlist.GetAsync(playlist);
|
||||
return await playlist.Context.Api.Playlist.GetAsync(playlist);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавляет плейлист в список лайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<string?> AddLikeAsync(this YPlaylist playlist)
|
||||
=> await playlist.Context.API.Library.AddPlaylistLikeAsync(playlist);
|
||||
=> await playlist.Context.Api.Library.AddPlaylistLikeAsync(playlist);
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет плейлист из списка лайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<string?> RemoveLikeAsync(this YPlaylist playlist)
|
||||
=> await playlist.Context.API.Library.RemovePlaylistLikeAsync(playlist);
|
||||
=> await playlist.Context.Api.Library.RemovePlaylistLikeAsync(playlist);
|
||||
|
||||
/// <summary>
|
||||
/// Переименовывает плейлист (только для владельца).
|
||||
/// </summary>
|
||||
public static async Task<YPlaylist?> RenameAsync(this YPlaylist playlist, string newName)
|
||||
=> IsOwner(playlist) ? await playlist.Context.API.Playlist.RenameAsync(playlist, newName) : playlist;
|
||||
=> IsOwner(playlist) ? await playlist.Context.Api.Playlist.RenameAsync(playlist, newName) : playlist;
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет плейлист (только для владельца).
|
||||
/// </summary>
|
||||
public static async Task<bool> DeleteAsync(this YPlaylist playlist)
|
||||
=> IsOwner(playlist) && await playlist.Context.API.Playlist.DeleteAsync(playlist);
|
||||
=> IsOwner(playlist) && await playlist.Context.Api.Playlist.DeleteAsync(playlist);
|
||||
|
||||
/// <summary>
|
||||
/// Вставляет треки в начало плейлиста (только для владельца).
|
||||
/// </summary>
|
||||
public static async Task<YPlaylist?> InsertTracksAsync(this YPlaylist playlist, params YTrack[] tracks)
|
||||
=> IsOwner(playlist) ? await playlist.Context.API.Playlist.InsertTracksAsync(playlist, tracks) : playlist;
|
||||
=> IsOwner(playlist) ? await playlist.Context.Api.Playlist.InsertTracksAsync(playlist, tracks) : playlist;
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет треки из плейлиста (только для владельца).
|
||||
/// </summary>
|
||||
public static async Task<YPlaylist?> RemoveTracksAsync(this YPlaylist playlist, params YTrack[] tracks)
|
||||
=> IsOwner(playlist) ? await playlist.Context.API.Playlist.DeleteTracksAsync(playlist, tracks) : playlist;
|
||||
=> IsOwner(playlist) ? await playlist.Context.Api.Playlist.DeleteTracksAsync(playlist, tracks) : playlist;
|
||||
|
||||
/// <summary>
|
||||
/// Загружает трек в плейлист (только для владельца).
|
||||
@@ -63,7 +63,7 @@ public static class YPlaylistExtensions
|
||||
public static async Task<bool> UploadTrackAsync(this YPlaylist playlist, string filePath, string fileName)
|
||||
{
|
||||
if (!IsOwner(playlist)) return false;
|
||||
var result = await playlist.Context.API.UserGeneratedContent.UploadTrackToPlaylistAsync(playlist, fileName, filePath);
|
||||
var result = await playlist.Context.Api.UserGeneratedContent.UploadTrackToPlaylistAsync(playlist, fileName, filePath);
|
||||
return result == "CREATED";
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
using YandexMusic.API.Models.Radio;
|
||||
using YandexMusic.API.Models.Track;
|
||||
|
||||
namespace YandexMusic.API.Extensions.API;
|
||||
namespace YandexMusic.API;
|
||||
|
||||
/// <summary>
|
||||
/// Методы-расширения для радиостанции.
|
||||
@@ -12,17 +12,17 @@ public static class YStationResultExtensions
|
||||
/// Получает список треков для радиостанции.
|
||||
/// </summary>
|
||||
public static async Task<List<YSequenceItem>?> GetTracksAsync(this YStation station, string prevTrackId = "")
|
||||
=> (await station.Context.API.Radio.GetStationTracksAsync(station, prevTrackId))?.Sequence;
|
||||
=> (await station.Context.Api.Radio.GetStationTracksAsync(station, prevTrackId))?.Sequence;
|
||||
|
||||
/// <summary>
|
||||
/// Устанавливает настройки станции.
|
||||
/// </summary>
|
||||
public static async Task<string?> SetSettings2Async(this YStation station, YStationSettings2 settings)
|
||||
=> await station.Context.API.Radio.SetStationSettings2Async(station, settings);
|
||||
=> await station.Context.Api.Radio.SetStationSettings2Async(station, settings);
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет обратную связь о прослушивании.
|
||||
/// </summary>
|
||||
public static Task<string?> SendFeedbackAsync(this YStation station, YStationFeedbackType type, YTrack? track = null, string batchId = "", double totalPlayedSeconds = 0)
|
||||
=> station.Context.API.Radio.SendStationFeedbackAsync(station, type, track, batchId, totalPlayedSeconds);
|
||||
=> station.Context.Api.Radio.SendStationFeedbackAsync(station, type, track, batchId, totalPlayedSeconds);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
using YandexMusic.API.Models.Track;
|
||||
|
||||
namespace YandexMusic.API.Extensions.API;
|
||||
namespace YandexMusic.API;
|
||||
|
||||
/// <summary>
|
||||
/// Методы-расширения для трека.
|
||||
@@ -11,53 +11,53 @@ public static class YTrackExtensions
|
||||
/// Получает прямую ссылку на скачивание трека.
|
||||
/// </summary>
|
||||
public static Task<string?> GetLinkAsync(this YTrack track)
|
||||
=> track.Context.API.Track.GetFileLinkAsync(track);
|
||||
=> track.Context.Api.Track.GetFileLinkAsync(track);
|
||||
|
||||
/// <summary>
|
||||
/// Сохраняет трек в файл.
|
||||
/// </summary>
|
||||
public static Task SaveAsync(this YTrack track, string filePath)
|
||||
=> track.Context.API.Track.ExtractToFileAsync(track, filePath);
|
||||
=> track.Context.Api.Track.ExtractToFileAsync(track, filePath);
|
||||
|
||||
/// <summary>
|
||||
/// Добавляет трек в список лайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<int?> AddLikeAsync(this YTrack track)
|
||||
=> await track.Context.API.Library.AddTrackLikeAsync(track);
|
||||
=> await track.Context.Api.Library.AddTrackLikeAsync(track);
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет трек из списка лайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<int?> RemoveLikeAsync(this YTrack track)
|
||||
=> await track.Context.API.Library.RemoveTrackLikeAsync(track);
|
||||
=> await track.Context.Api.Library.RemoveTrackLikeAsync(track);
|
||||
|
||||
/// <summary>
|
||||
/// Добавляет трек в список дизлайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<int?> AddDislikeAsync(this YTrack track)
|
||||
=> await track.Context.API.Library.AddTrackDislikeAsync(track);
|
||||
=> await track.Context.Api.Library.AddTrackDislikeAsync(track);
|
||||
|
||||
/// <summary>
|
||||
/// Удаляет трек из списка дизлайкнутых.
|
||||
/// </summary>
|
||||
public static async Task<int?> RemoveDislikeAsync(this YTrack track)
|
||||
=> await track.Context.API.Library.RemoveTrackDislikeAsync(track);
|
||||
=> await track.Context.Api.Library.RemoveTrackDislikeAsync(track);
|
||||
|
||||
/// <summary>
|
||||
/// Отправляет информацию о воспроизведении трека.
|
||||
/// </summary>
|
||||
public static Task<string?> SendPlayTrackInfoAsync(this YTrack track, string from, bool fromCache = false, string playId = "", string playlistId = "", double totalPlayedSeconds = 0, double endPositionSeconds = 0)
|
||||
=> track.Context.API.Track.SendPlayTrackInfoAsync(track, from, fromCache, playId, playlistId, totalPlayedSeconds, endPositionSeconds);
|
||||
=> track.Context.Api.Track.SendPlayTrackInfoAsync(track, from, fromCache, playId, playlistId, totalPlayedSeconds, endPositionSeconds);
|
||||
|
||||
/// <summary>
|
||||
/// Получает дополнительную информацию о треке.
|
||||
/// </summary>
|
||||
public static async Task<YTrackSupplement?> SupplementAsync(this YTrack track)
|
||||
=> await track.Context.API.Track.GetSupplementAsync(track);
|
||||
=> await track.Context.Api.Track.GetSupplementAsync(track);
|
||||
|
||||
/// <summary>
|
||||
/// Получает похожие треки.
|
||||
/// </summary>
|
||||
public static async Task<YTrackSimilar?> SimilarAsync(this YTrack track)
|
||||
=> await track.Context.API.Track.GetSimilarAsync(track);
|
||||
=> await track.Context.Api.Track.GetSimilarAsync(track);
|
||||
}
|
||||
@@ -2,4 +2,4 @@
|
||||
|
||||
public class YAuthEmpty
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
9
YandexMusic.API/Models/Account/YAuthQrState.cs
Normal file
9
YandexMusic.API/Models/Account/YAuthQrState.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Models.Account;
|
||||
|
||||
public enum YAuthQrState
|
||||
{
|
||||
[EnumMember(Value = "otp_auth_finished")]
|
||||
OtpAuthFinished,
|
||||
}
|
||||
@@ -1,16 +1,13 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Models.Account;
|
||||
namespace YandexMusic.API.Models.Account;
|
||||
|
||||
public class YAuthToken
|
||||
{
|
||||
[JsonPropertyName("csfr_token")]
|
||||
public string CsfrToken { get; set; }
|
||||
|
||||
[JsonPropertyName("track_id")]
|
||||
public string TrackId { get; set; }
|
||||
|
||||
[JsonPropertyName("process_uuid")]
|
||||
public string SessionTrackId { get; set; }
|
||||
|
||||
public string ProcessUuid { get; set; }
|
||||
|
||||
public Dictionary<string, string> Cookie { get; set; } = new();
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace YandexMusic.API.Models.Common;
|
||||
public class YExecutionContext
|
||||
{
|
||||
/// <summary>Экземпляр основного API.</summary>
|
||||
public YandexMusicApi API { get; internal set; } = null!;
|
||||
public YandexMusicApi Api { get; internal set; } = null!;
|
||||
|
||||
/// <summary>Хранилище данных авторизации.</summary>
|
||||
public AuthStorage Storage { get; internal set; } = null!;
|
||||
|
||||
@@ -38,7 +38,7 @@ public class YExecutionContextConverter : JsonConverter<object>
|
||||
var obj = JsonSerializer.Deserialize(ref reader, typeToConvert, innerOptions);
|
||||
if (obj is YBaseModel baseModel)
|
||||
{
|
||||
baseModel.Context = new YExecutionContext { API = _api, Storage = _storage };
|
||||
baseModel.Context = new YExecutionContext { Api = _api, Storage = _storage };
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Models.Common;
|
||||
|
||||
[XmlRoot("download-info")]
|
||||
public class YStorageDownloadFile
|
||||
{
|
||||
[XmlElement("host")]
|
||||
public string Host { get; set; }
|
||||
[XmlElement("path")]
|
||||
public string Path { get; set; }
|
||||
[XmlElement("s")]
|
||||
public string S { get; set; }
|
||||
[XmlElement("ts")]
|
||||
public string Ts { get; set; }
|
||||
}
|
||||
@@ -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; }
|
||||
@@ -1,12 +1,14 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Models.Account;
|
||||
namespace YandexMusic.API.Models.Passport;
|
||||
|
||||
public class YAuthQRStatus : YAuthBase
|
||||
public class YAuthQrSession
|
||||
{
|
||||
[JsonPropertyName("default_uid")]
|
||||
public int DefaultUid { get; set; }
|
||||
|
||||
[JsonPropertyName("retpath")]
|
||||
public string RetPath { get; set; }
|
||||
|
||||
[JsonPropertyName("track_id")]
|
||||
12
YandexMusic.API/Models/Passport/YAuthQrStatus.cs
Normal file
12
YandexMusic.API/Models/Passport/YAuthQrStatus.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Models.Passport;
|
||||
|
||||
public class YAuthQRStatus
|
||||
{
|
||||
[JsonPropertyName("state")]
|
||||
public string? State { get; set; } = null;
|
||||
|
||||
[JsonPropertyName("trackId")]
|
||||
public string TrackId { get; set; } = string.Empty;
|
||||
}
|
||||
39
YandexMusic.API/Models/Passport/YCheckAvailabilityResult.cs
Normal file
39
YandexMusic.API/Models/Passport/YCheckAvailabilityResult.cs
Normal file
@@ -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<object> AllowedRegistrationFlows { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("SuggestBy")]
|
||||
public string SuggestBy { get; set; } = string.Empty;
|
||||
|
||||
[JsonPropertyName("master_info")]
|
||||
public YMasterInfo? MasterInfo { get; set; }
|
||||
}
|
||||
12
YandexMusic.API/Models/Passport/YMasterInfo.cs
Normal file
12
YandexMusic.API/Models/Passport/YMasterInfo.cs
Normal file
@@ -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;
|
||||
}
|
||||
42
YandexMusic.API/Models/Passport/YMultistepStart.cs
Normal file
42
YandexMusic.API/Models/Passport/YMultistepStart.cs
Normal file
@@ -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<string> 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<string> 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<string>? Errors { get; set; }
|
||||
}
|
||||
48
YandexMusic.API/Models/Passport/YPassportAccount.cs
Normal file
48
YandexMusic.API/Models/Passport/YPassportAccount.cs
Normal file
@@ -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; }
|
||||
}
|
||||
11
YandexMusic.API/Models/Passport/YPassportName.cs
Normal file
11
YandexMusic.API/Models/Passport/YPassportName.cs
Normal file
@@ -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;
|
||||
}
|
||||
11
YandexMusic.API/Models/Passport/YPassportPerson.cs
Normal file
11
YandexMusic.API/Models/Passport/YPassportPerson.cs
Normal file
@@ -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;
|
||||
}
|
||||
12
YandexMusic.API/Models/Passport/YPassportSession.cs
Normal file
12
YandexMusic.API/Models/Passport/YPassportSession.cs
Normal file
@@ -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;
|
||||
}
|
||||
12
YandexMusic.API/Models/Passport/YPassportSessionStatus.cs
Normal file
12
YandexMusic.API/Models/Passport/YPassportSessionStatus.cs
Normal file
@@ -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; }
|
||||
}
|
||||
21
YandexMusic.API/Models/Passport/YPassportUser.cs
Normal file
21
YandexMusic.API/Models/Passport/YPassportUser.cs
Normal file
@@ -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<string>? Errors { get; set; }
|
||||
}
|
||||
7
YandexMusic.API/Models/Passport/YPushApp.cs
Normal file
7
YandexMusic.API/Models/Passport/YPushApp.cs
Normal file
@@ -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;
|
||||
}
|
||||
18
YandexMusic.API/Models/Passport/YSendPushResult.cs
Normal file
18
YandexMusic.API/Models/Passport/YSendPushResult.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Models.Passport;
|
||||
|
||||
public class YSendPushResult
|
||||
{
|
||||
[JsonPropertyName("pushes_devices_list")]
|
||||
public List<object> 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<YPushApp> AppsForBrightPush { get; set; } = new();
|
||||
}
|
||||
54
YandexMusic.API/Models/Passport/YSuggestAccount.cs
Normal file
54
YandexMusic.API/Models/Passport/YSuggestAccount.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Models.Passport;
|
||||
|
||||
public class YSuggestAccount
|
||||
{
|
||||
[JsonPropertyName("allowed_auth_flows")]
|
||||
public List<string> 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<string> Shields { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("require_additional_sms_to_login")]
|
||||
public bool RequireAdditionalSmsToLogin { get; set; }
|
||||
}
|
||||
15
YandexMusic.API/Models/Passport/YSuggestByPhoneResult.cs
Normal file
15
YandexMusic.API/Models/Passport/YSuggestByPhoneResult.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Models.Passport;
|
||||
|
||||
public class YSuggestByPhoneResult
|
||||
{
|
||||
[JsonPropertyName("accounts")]
|
||||
public List<YSuggestAccount> Accounts { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("allowed_registration_flows")]
|
||||
public List<string> AllowedRegistrationFlows { get; set; } = new();
|
||||
|
||||
[JsonPropertyName("uid_from_bb")]
|
||||
public string UidFromBb { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -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<string>? Errors { get; set; }
|
||||
}
|
||||
18
YandexMusic.API/Models/Passport/YValidateSquatter.cs
Normal file
18
YandexMusic.API/Models/Passport/YValidateSquatter.cs
Normal file
@@ -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;
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthAppPasswordBuilder : YAuthRequestBuilder<YAuthBase?, string>
|
||||
internal class YGetAuthAppPasswordBuilder : YPassportRequestBuilder<YAuthBase?, string>
|
||||
{
|
||||
public YGetAuthAppPasswordBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthCaptchaBuilder : YAuthRequestBuilder<YAuthCaptcha?, object>
|
||||
internal class YGetAuthCaptchaBuilder : YPassportRequestBuilder<YAuthCaptcha?, object>
|
||||
{
|
||||
public YGetAuthCaptchaBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -1,14 +1,55 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using YandexMusic.API.Models.Account;
|
||||
using YandexMusic.API.Requests.Common;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthCookiesBuilder : YAuthRequestBuilder<YAccessToken?, object>
|
||||
internal class YGetAuthCookiesBuilder : YPassportRequestBuilder<YAccessToken?, object>
|
||||
{
|
||||
public YGetAuthCookiesBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
protected override string BaseUrl => YConstants.Endpoints.MobilePassportUrl;
|
||||
protected override string PathTemplate => "1/bundle/oauth/token_by_sessionid";
|
||||
protected override HttpContent? GetContent(object _)
|
||||
=> new FormUrlEncodedContent(new Dictionary<string, string> { { "client_id", YConstants.XClientId }, { "client_secret", YConstants.XClientSecret } });
|
||||
protected override void SetCustomHeaders(HttpRequestHeaders headers)
|
||||
{
|
||||
base.SetCustomHeaders(headers);
|
||||
headers.Add("ya-client-host", "passport.yandex.ru");
|
||||
|
||||
var cookieString = GetCookieString();
|
||||
if (!string.IsNullOrEmpty(cookieString))
|
||||
headers.Add("Ya-Client-Cookie", cookieString);
|
||||
}
|
||||
private string GetCookieString()
|
||||
{
|
||||
var container = Storage.CookieContainer;
|
||||
if (container == null) return string.Empty;
|
||||
|
||||
var uris = new[]
|
||||
{
|
||||
new Uri("https://yandex.ru"),
|
||||
new Uri("https://passport.yandex.ru"),
|
||||
new Uri("https://mobileproxy.passport.yandex.net")
|
||||
};
|
||||
|
||||
var cookies = new List<string>();
|
||||
foreach (var uri in uris)
|
||||
{
|
||||
var cookieCollection = container.GetCookies(uri);
|
||||
foreach (Cookie cookie in cookieCollection)
|
||||
{
|
||||
cookies.Add($"{cookie.Name}={cookie.Value}");
|
||||
}
|
||||
}
|
||||
|
||||
var distinct = cookies
|
||||
.Select(c => c.Split('=')[0])
|
||||
.Distinct()
|
||||
.Select(name => cookies.First(c => c.StartsWith(name + "=")))
|
||||
.ToList();
|
||||
|
||||
return string.Join("; ", distinct);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthInfoBuilder : YMusicRequestBuilder<YAccountResult?, object>
|
||||
internal class YGetAuthInfoBuilder : YMusicRequestBuilder<YAccountResult?, object>
|
||||
{
|
||||
public YGetAuthInfoBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthLetterBuilder : YAuthRequestBuilder<YAuthLetter?, object>
|
||||
internal class YGetAuthLetterBuilder : YPassportRequestBuilder<YAuthLetter?, object>
|
||||
{
|
||||
public YGetAuthLetterBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthLoginCaptchaBuilder : YAuthRequestBuilder<YAuthBase?, string>
|
||||
internal class YGetAuthLoginCaptchaBuilder : YPassportRequestBuilder<YAuthBase?, string>
|
||||
{
|
||||
public YGetAuthLoginCaptchaBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthLoginLetterBuilder : YAuthRequestBuilder<YAuthLetterStatus?, object>
|
||||
internal class YGetAuthLoginLetterBuilder : YPassportRequestBuilder<YAuthLetterStatus?, object>
|
||||
{
|
||||
public YGetAuthLoginLetterBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
internal class YGetAuthLoginQRBuilder : YAuthRequestBuilder<YAuthQRStatus, string>
|
||||
{
|
||||
public YGetAuthLoginQRBuilder(YandexMusicApi yandex) : base(yandex)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
protected override string PathTemplate => "auth/new/magic/status/";
|
||||
|
||||
protected override HttpContent GetContent(string tuple)
|
||||
{
|
||||
return new FormUrlEncodedContent(new Dictionary<string, string> {
|
||||
{ "csrf_token", Api.Storage.AuthToken.CsfrToken },
|
||||
{ "track_id", Api.Storage.AuthToken.TrackId }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthLoginUserBuilder : YAuthRequestBuilder<YAuthTypes?, (string token, string login)>
|
||||
internal class YGetAuthLoginUserBuilder : YPassportRequestBuilder<YAuthTypes?, (string token, string login)>
|
||||
{
|
||||
public YGetAuthLoginUserBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Requests.Common;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthMethodsBuilder : YRequestBuilder<object>
|
||||
internal class YGetAuthMethodsBuilder : YRequestBuilder<object>
|
||||
{
|
||||
public YGetAuthMethodsBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetAuthQRBuilder : YAuthRequestBuilder<YAuthQR?, object>
|
||||
{
|
||||
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<string, string> { { "retpath", "" } });
|
||||
protected override void SetCustomHeaders(HttpRequestHeaders headers)
|
||||
{
|
||||
headers.Add("X-Csrf-Token", Api.Storage.AuthToken.CsfrToken);
|
||||
headers.Add("Process-Uuid", Api.Storage.AuthToken.ProcessUuid);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Account;
|
||||
using YandexMusic.API.Requests.Common;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetLoginInfoBuilder : YAuthRequestBuilder<YLoginInfo?, object>
|
||||
internal class YGetLoginInfoBuilder : YPassportRequestBuilder<YLoginInfo?, object>
|
||||
{
|
||||
public YGetLoginInfoBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -5,7 +5,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YGetShortAccountInfoBuilder : YAuthRequestBuilder<YShortAccountInfo?, object>
|
||||
internal class YGetShortAccountInfoBuilder : YPassportRequestBuilder<YShortAccountInfo?, object>
|
||||
{
|
||||
public YGetShortAccountInfoBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -4,7 +4,7 @@ using YandexMusic.API.Models.Account;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
|
||||
public class YPostAuthStats : YAuthRequestBuilder<YAuthEmpty?, object>
|
||||
internal class YPostAuthStats : YPassportRequestBuilder<YAuthEmpty?, object>
|
||||
{
|
||||
public YPostAuthStats(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
@@ -13,7 +13,7 @@ public class YPostAuthStats : YAuthRequestBuilder<YAuthEmpty?, object>
|
||||
=> new FormUrlEncodedContent(new Dictionary<string, string> { { "messageType", "CLIENT_READY" } });
|
||||
protected override void SetCustomHeaders(HttpRequestHeaders headers)
|
||||
{
|
||||
headers.Add("X-Csrf-Token", Api.Storage.AuthToken.CsfrToken);
|
||||
headers.Add("Process-Uuid", Api.Storage.AuthToken.ProcessUuid);
|
||||
headers.Add("X-Csrf-Token", Api.Storage.HeaderToken.CsfrToken);
|
||||
headers.Add("Process-Uuid", Api.Storage.HeaderToken.ProcessUuid);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Album;
|
||||
|
||||
namespace YandexMusic.API.Requests.Album;
|
||||
|
||||
public class YGetAlbumBuilder : YMusicRequestBuilder<YAlbum?, string>
|
||||
internal class YGetAlbumBuilder : YMusicRequestBuilder<YAlbum?, string>
|
||||
{
|
||||
public YGetAlbumBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Album;
|
||||
|
||||
namespace YandexMusic.API.Requests.Album;
|
||||
|
||||
public class YGetAlbumsBuilder : YMusicRequestBuilder<List<YAlbum>?, IEnumerable<string>>
|
||||
internal class YGetAlbumsBuilder : YMusicRequestBuilder<List<YAlbum>?, IEnumerable<string>>
|
||||
{
|
||||
public YGetAlbumsBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Artist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Artist;
|
||||
|
||||
public class YGetArtistBuilder : YMusicRequestBuilder<YArtistBriefInfo?, string>
|
||||
internal class YGetArtistBuilder : YMusicRequestBuilder<YArtistBriefInfo?, string>
|
||||
{
|
||||
public YGetArtistBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -4,7 +4,7 @@ using YandexMusic.API.Models.Artist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Artist;
|
||||
|
||||
public class YGetArtistTrackBuilder : YMusicRequestBuilder<YTracksPage?, (string id, int page, int pageSize)>
|
||||
internal class YGetArtistTrackBuilder : YMusicRequestBuilder<YTracksPage?, (string id, int page, int pageSize)>
|
||||
{
|
||||
public YGetArtistTrackBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
@@ -12,5 +12,8 @@ public class YGetArtistTrackBuilder : YMusicRequestBuilder<YTracksPage?, (string
|
||||
protected override Dictionary<string, string> 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() },
|
||||
};
|
||||
}
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Artist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Artist;
|
||||
|
||||
public class YGetArtistsBuilder : YMusicRequestBuilder<List<YArtist>?, IEnumerable<string>>
|
||||
internal class YGetArtistsBuilder : YMusicRequestBuilder<List<YArtist>?, IEnumerable<string>>
|
||||
{
|
||||
public YGetArtistsBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -12,5 +12,6 @@ internal class YConstants
|
||||
{
|
||||
public const string MusicUrl = "https://api.music.yandex.net";
|
||||
public const string PassportUrl = "https://passport.yandex.ru/";
|
||||
public const string MobilePassportUrl = "https://mobileproxy.passport.yandex.net";
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,19 @@ namespace YandexMusic.API.Requests.Common;
|
||||
/// <summary>
|
||||
/// Строитель запросов с десериализацией JSON-ответа в TResponse.
|
||||
/// </summary>
|
||||
public abstract class YJsonRequestBuilder<TResponse, TParams> : YRequestBuilder<TParams>
|
||||
internal abstract class YJsonRequestBuilder<TResponse, TParams> : YRequestBuilder<TParams>
|
||||
{
|
||||
protected YJsonRequestBuilder(YandexMusicApi api) : base(api) { }
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
|
||||
protected YJsonRequestBuilder(YandexMusicApi api) : base(api)
|
||||
{
|
||||
_jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
|
||||
};
|
||||
}
|
||||
|
||||
protected virtual async Task<TResponse?> DeserializeAsync(HttpResponseMessage response)
|
||||
{
|
||||
@@ -43,6 +53,8 @@ public abstract class YJsonRequestBuilder<TResponse, TParams> : YRequestBuilder<
|
||||
}
|
||||
}
|
||||
|
||||
protected string SerializeJson(object data) => JsonSerializer.Serialize(data, _jsonOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет запрос и возвращает десериализованный объект типа TResponse.
|
||||
/// </summary>
|
||||
@@ -51,4 +63,4 @@ public abstract class YJsonRequestBuilder<TResponse, TParams> : YRequestBuilder<
|
||||
using var response = await ExecuteRawAsync(parameters);
|
||||
return await DeserializeAsync(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ using YandexMusic.API.Requests.Common;
|
||||
namespace YandexMusic.API.Requests;
|
||||
|
||||
/// <summary>Базовый класс для запросов к API Яндекс Музыки (api.music.yandex.net).</summary>
|
||||
public abstract class YMusicRequestBuilder<TResponse, TParams> : YJsonRequestBuilder<TResponse, TParams>
|
||||
internal abstract class YMusicRequestBuilder<TResponse, TParams> : YJsonRequestBuilder<TResponse, TParams>
|
||||
{
|
||||
protected override string BaseUrl => YConstants.Endpoints.MusicUrl;
|
||||
|
||||
|
||||
@@ -4,15 +4,20 @@ using YandexMusic.API.Requests.Common;
|
||||
namespace YandexMusic.API.Requests;
|
||||
|
||||
/// <summary>Базовый класс для запросов к Passport (passport.yandex.ru).</summary>
|
||||
public abstract class YAuthRequestBuilder<TResponse, TParams> : YJsonRequestBuilder<TResponse, TParams>
|
||||
internal abstract class YPassportRequestBuilder<TResponse, TParams> : YJsonRequestBuilder<TResponse, TParams>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using YandexMusic.API.Common;
|
||||
@@ -12,7 +10,7 @@ namespace YandexMusic.API.Requests.Common;
|
||||
|
||||
/// <summary>Базовый строитель HTTP-запросов.</summary>
|
||||
/// <typeparam name="TParams">Тип параметров запроса.</typeparam>
|
||||
public abstract class YRequestBuilder<TParams>
|
||||
internal abstract class YRequestBuilder<TParams>
|
||||
{
|
||||
/// <summary>HTTP-метод (GET, POST и т.д.).</summary>
|
||||
protected abstract string Method { get; }
|
||||
@@ -23,7 +21,8 @@ public abstract class YRequestBuilder<TParams>
|
||||
/// <summary>Шаблон пути (может содержать плейсхолдеры вида {id}).</summary>
|
||||
protected abstract string PathTemplate { get; }
|
||||
|
||||
private readonly JsonSerializerOptions _jsonOptions;
|
||||
/// <summary>Определяет, нужно ли добавлять заголовок Authorization для этого запроса.</summary>
|
||||
protected virtual bool ShouldAddAuthorization => true;
|
||||
|
||||
/// <summary>Основной экземпляр API.</summary>
|
||||
protected YandexMusicApi Api { get; }
|
||||
@@ -34,12 +33,6 @@ public abstract class YRequestBuilder<TParams>
|
||||
protected YRequestBuilder(YandexMusicApi api)
|
||||
{
|
||||
Api = api;
|
||||
_jsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
|
||||
};
|
||||
}
|
||||
|
||||
private string FullUrl => $"{BaseUrl.TrimEnd('/')}/{PathTemplate.TrimStart('/')}";
|
||||
@@ -66,7 +59,7 @@ public abstract class YRequestBuilder<TParams>
|
||||
};
|
||||
msg.Headers.TryAddWithoutValidation(GetHeaderName(HttpRequestHeader.AcceptCharset), Encoding.UTF8.WebName);
|
||||
msg.Headers.TryAddWithoutValidation(GetHeaderName(HttpRequestHeader.AcceptEncoding), "gzip");
|
||||
if (!string.IsNullOrEmpty(Storage.Token))
|
||||
if (ShouldAddAuthorization && !string.IsNullOrEmpty(Storage.Token))
|
||||
msg.Headers.TryAddWithoutValidation(GetHeaderName(HttpRequestHeader.Authorization), $"OAuth {Storage.Token}");
|
||||
SetCustomHeaders(msg.Headers);
|
||||
return msg;
|
||||
@@ -120,8 +113,6 @@ public abstract class YRequestBuilder<TParams>
|
||||
protected virtual HttpContent? GetContent(TParams parameters) => null;
|
||||
protected virtual void SetCustomHeaders(HttpRequestHeaders headers) { }
|
||||
|
||||
protected string SerializeJson(object data) => JsonSerializer.Serialize(data, _jsonOptions);
|
||||
|
||||
/// <summary>Выполняет запрос и возвращает десериализованный ответ.</summary>
|
||||
public async Task<HttpResponseMessage?> ExecuteRawAsync(TParams parameters)
|
||||
{
|
||||
|
||||
47
YandexMusic.API/Requests/Common/YXmlRequestBuilder.cs
Normal file
47
YandexMusic.API/Requests/Common/YXmlRequestBuilder.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Xml;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace YandexMusic.API.Requests.Common;
|
||||
|
||||
/// <summary>
|
||||
/// Строитель запросов с десериализацией XML-ответа в TResponse.
|
||||
/// </summary>
|
||||
internal abstract class YXmlRequestBuilder<TResponse, TParams> : YRequestBuilder<TParams>
|
||||
{
|
||||
protected YXmlRequestBuilder(YandexMusicApi api) : base(api) { }
|
||||
|
||||
/// <summary>
|
||||
/// Десериализует XML-ответ в объект типа TResponse.
|
||||
/// </summary>
|
||||
protected virtual async Task<TResponse?> DeserializeAsync(HttpResponseMessage response)
|
||||
{
|
||||
var xml = await response.Content.ReadAsStringAsync();
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
// Для XML-ошибок можно создать отдельную модель, но для простоты выбрасываем исключение
|
||||
throw new Exception($"Ошибка HTTP {response.StatusCode}: {xml}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
using var stringReader = new StringReader(xml);
|
||||
using var xmlReader = XmlReader.Create(stringReader, new XmlReaderSettings { Async = true });
|
||||
var serializer = new XmlSerializer(typeof(TResponse));
|
||||
return (TResponse?)serializer.Deserialize(xmlReader);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Ошибка десериализации XML: {ex.Message}\nXML: {xml}", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет запрос и возвращает десериализованный объект типа TResponse.
|
||||
/// </summary>
|
||||
public async Task<TResponse?> ExecuteAsync(TParams parameters)
|
||||
{
|
||||
using var response = await ExecuteRawAsync(parameters);
|
||||
return await DeserializeAsync(response);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Feed;
|
||||
using YandexMusic.API.Requests.Common;
|
||||
|
||||
namespace YandexMusic.API.Requests.Feed;
|
||||
|
||||
public class YGetFeedBuilder : YMusicRequestBuilder<YFeed?, object>
|
||||
internal class YGetFeedBuilder : YMusicRequestBuilder<YFeed?, object>
|
||||
{
|
||||
public YGetFeedBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -5,7 +5,7 @@ using YandexMusic.API.Models.Label;
|
||||
|
||||
namespace YandexMusic.API.Requests.Label;
|
||||
|
||||
public class YGetLabelAlbumsBuilder : YMusicRequestBuilder<YLabelAlbums?, (YLabel label, int pageNumber)>
|
||||
internal class YGetLabelAlbumsBuilder : YMusicRequestBuilder<YLabelAlbums?, (YLabel label, int pageNumber)>
|
||||
{
|
||||
public YGetLabelAlbumsBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -5,7 +5,7 @@ using YandexMusic.API.Models.Label;
|
||||
|
||||
namespace YandexMusic.API.Requests.Label;
|
||||
|
||||
public class YGetLabelArtistsBuilder : YMusicRequestBuilder<YLabelArtists?, (YLabel label, int pageNumber)>
|
||||
internal class YGetLabelArtistsBuilder : YMusicRequestBuilder<YLabelArtists?, (YLabel label, int pageNumber)>
|
||||
{
|
||||
public YGetLabelArtistsBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Landing;
|
||||
using YandexMusic.API.Requests.Common;
|
||||
|
||||
namespace YandexMusic.API.Requests.Landing;
|
||||
|
||||
public class YGetChildrenLandingBuilder : YMusicRequestBuilder<YChildrenLanding?, object>
|
||||
internal class YGetChildrenLandingBuilder : YMusicRequestBuilder<YChildrenLanding?, object>
|
||||
{
|
||||
public YGetChildrenLandingBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -4,7 +4,7 @@ using YandexMusic.API.Models.Landing;
|
||||
|
||||
namespace YandexMusic.API.Requests.Landing;
|
||||
|
||||
public class YGetLandingBuilder : YMusicRequestBuilder<YLanding?, YLandingBlockType[]>
|
||||
internal class YGetLandingBuilder : YMusicRequestBuilder<YLanding?, YLandingBlockType[]>
|
||||
{
|
||||
public YGetLandingBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -5,7 +5,7 @@ using YandexMusic.API.Models.Library;
|
||||
|
||||
namespace YandexMusic.API.Requests.Library;
|
||||
|
||||
public class YGetLibraryRecentlyListenedBuilder : YMusicRequestBuilder<YRecentlyListenedContext?, (IEnumerable<YPlayContextType> contextTypes, int trackCount, int contextCount)>
|
||||
internal class YGetLibraryRecentlyListenedBuilder : YMusicRequestBuilder<YRecentlyListenedContext?, (IEnumerable<YPlayContextType> contextTypes, int trackCount, int contextCount)>
|
||||
{
|
||||
public YGetLibraryRecentlyListenedBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Library;
|
||||
|
||||
namespace YandexMusic.API.Requests.Library;
|
||||
|
||||
public class YGetLibrarySectionBuilder<T> : YMusicRequestBuilder<T?, (YLibrarySection section, YLibrarySectionType type)>
|
||||
internal class YGetLibrarySectionBuilder<T> : YMusicRequestBuilder<T?, (YLibrarySection section, YLibrarySectionType type)>
|
||||
{
|
||||
public YGetLibrarySectionBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Library;
|
||||
|
||||
namespace YandexMusic.API.Requests.Library;
|
||||
|
||||
public class YLibraryAddBuilder<T> : YMusicRequestBuilder<T?, (string id, YLibrarySection section, YLibrarySectionType type)>
|
||||
internal class YLibraryAddBuilder<T> : YMusicRequestBuilder<T?, (string id, YLibrarySection section, YLibrarySectionType type)>
|
||||
{
|
||||
public YLibraryAddBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Library;
|
||||
|
||||
namespace YandexMusic.API.Requests.Library;
|
||||
|
||||
public class YLibraryRemoveBuilder<T> : YMusicRequestBuilder<T?, (string id, YLibrarySection section, YLibrarySectionType type)>
|
||||
internal class YLibraryRemoveBuilder<T> : YMusicRequestBuilder<T?, (string id, YLibrarySection section, YLibrarySectionType type)>
|
||||
{
|
||||
public YLibraryRemoveBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -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<YCheckAvailabilityResult?, string>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
24
YandexMusic.API/Requests/Passport/YCheckPushCodeBuilder.cs
Normal file
24
YandexMusic.API/Requests/Passport/YCheckPushCodeBuilder.cs
Normal file
@@ -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<YAuthEmpty?, string>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
19
YandexMusic.API/Requests/Passport/YCheckSessionBuilder.cs
Normal file
19
YandexMusic.API/Requests/Passport/YCheckSessionBuilder.cs
Normal file
@@ -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<YPassportSessionStatus?, object>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
13
YandexMusic.API/Requests/Passport/YCreateTrackBuilder.cs
Normal file
13
YandexMusic.API/Requests/Passport/YCreateTrackBuilder.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Passport;
|
||||
|
||||
namespace YandexMusic.API.Requests.Passport;
|
||||
|
||||
internal class YCreateTrackBuilder : YPassportRequestBuilder<YPassportTrack?, object>
|
||||
{
|
||||
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<string, string> { { "retpath", "" } });
|
||||
}
|
||||
22
YandexMusic.API/Requests/Passport/YGetAuthLoginQRBuilder.cs
Normal file
22
YandexMusic.API/Requests/Passport/YGetAuthLoginQRBuilder.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Passport;
|
||||
|
||||
namespace YandexMusic.API.Requests.Passport;
|
||||
|
||||
internal class YGetAuthLoginQRBuilder : YPassportRequestBuilder<YAuthQrSession, string>
|
||||
{
|
||||
public YGetAuthLoginQRBuilder(YandexMusicApi yandex) : base(yandex)
|
||||
{
|
||||
}
|
||||
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
protected override string PathTemplate => "pwl-yandex/api/passport/sessions/get_session";
|
||||
|
||||
protected override HttpContent GetContent(string tuple)
|
||||
{
|
||||
return new FormUrlEncodedContent(new Dictionary<string, string> {
|
||||
{ "track_id", Api.Storage.AuthToken.SessionTrackId }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,20 +3,21 @@ using System.Net.Http.Headers;
|
||||
using YandexMusic.API.Models.Account;
|
||||
using YandexMusic.API.Requests.Common;
|
||||
|
||||
namespace YandexMusic.API.Requests.Account;
|
||||
namespace YandexMusic.API.Requests.Passport;
|
||||
|
||||
public class YGetMusicTokenBuilder : YAuthRequestBuilder<YAccessToken?, object>
|
||||
internal class YGetMusicTokenBuilder : YPassportRequestBuilder<YAccessToken?, string>
|
||||
{
|
||||
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<string, string>
|
||||
{
|
||||
{ "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)
|
||||
{
|
||||
17
YandexMusic.API/Requests/Passport/YGetQrStatus.cs
Normal file
17
YandexMusic.API/Requests/Passport/YGetQrStatus.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Passport;
|
||||
|
||||
namespace YandexMusic.API.Requests.Passport;
|
||||
|
||||
internal class YGetQrStatus : YPassportRequestBuilder<YAuthQRStatus?, object>
|
||||
{
|
||||
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<string, string>
|
||||
{
|
||||
["csrf_token"] = Api.Storage.AuthToken.CsfrToken,
|
||||
["track_id"] = Api.Storage.AuthToken.TrackId,
|
||||
});
|
||||
}
|
||||
20
YandexMusic.API/Requests/Passport/YGetSessionBuilder.cs
Normal file
20
YandexMusic.API/Requests/Passport/YGetSessionBuilder.cs
Normal file
@@ -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<YPassportSession?, object>
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<YPassportUser?, string>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
35
YandexMusic.API/Requests/Passport/YMultistepStartBuilder.cs
Normal file
35
YandexMusic.API/Requests/Passport/YMultistepStartBuilder.cs
Normal file
@@ -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<YMultistepStart?, string>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
23
YandexMusic.API/Requests/Passport/YRfcOtpBuilder.cs
Normal file
23
YandexMusic.API/Requests/Passport/YRfcOtpBuilder.cs
Normal file
@@ -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<YPassportUser?, string>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
26
YandexMusic.API/Requests/Passport/YSendPushBuilder.cs
Normal file
26
YandexMusic.API/Requests/Passport/YSendPushBuilder.cs
Normal file
@@ -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<YSendPushResult?, string>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
23
YandexMusic.API/Requests/Passport/YSuggestByPhoneBuilder.cs
Normal file
23
YandexMusic.API/Requests/Passport/YSuggestByPhoneBuilder.cs
Normal file
@@ -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<YSuggestByPhoneResult?, object>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<YValidatePhoneNumberResult?, string>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<YValidateSquatter?, string>
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Net;
|
||||
using YandexMusic.API.Models.Pins;
|
||||
using YandexMusic.API.Requests.Common;
|
||||
|
||||
namespace YandexMusic.API.Requests.Pins;
|
||||
|
||||
public class YGetPinsBuilder : YMusicRequestBuilder<YPins?, object>
|
||||
internal class YGetPinsBuilder : YMusicRequestBuilder<YPins?, object>
|
||||
{
|
||||
public YGetPinsBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Playlist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Playlist;
|
||||
|
||||
public class YGetPlaylistBuilder : YMusicRequestBuilder<YPlaylist?, (string user, string kind)>
|
||||
internal class YGetPlaylistBuilder : YMusicRequestBuilder<YPlaylist?, (string user, string kind)>
|
||||
{
|
||||
public YGetPlaylistBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Playlist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Playlist;
|
||||
|
||||
public class YGetPlaylistByUuidBuilder : YMusicRequestBuilder<YPlaylist?, string>
|
||||
internal class YGetPlaylistByUuidBuilder : YMusicRequestBuilder<YPlaylist?, string>
|
||||
{
|
||||
public YGetPlaylistByUuidBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Playlist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Playlist;
|
||||
|
||||
public class YGetPlaylistFavoritesBuilder : YMusicRequestBuilder<List<YPlaylist>?, object>
|
||||
internal class YGetPlaylistFavoritesBuilder : YMusicRequestBuilder<List<YPlaylist>?, object>
|
||||
{
|
||||
public YGetPlaylistFavoritesBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Playlist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Playlist;
|
||||
|
||||
public class YGetPlaylistsBuilder : YMusicRequestBuilder<List<YPlaylist>?, IEnumerable<(string User, string Kind)>>
|
||||
internal class YGetPlaylistsBuilder : YMusicRequestBuilder<List<YPlaylist>?, IEnumerable<(string User, string Kind)>>
|
||||
{
|
||||
public YGetPlaylistsBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Playlist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Playlist;
|
||||
|
||||
public class YPlaylistChangeBuilder : YMusicRequestBuilder<YPlaylist?, (YPlaylist playlist, IEnumerable<YPlaylistChange> changes)>
|
||||
internal class YPlaylistChangeBuilder : YMusicRequestBuilder<YPlaylist?, (YPlaylist playlist, IEnumerable<YPlaylistChange> changes)>
|
||||
{
|
||||
public YPlaylistChangeBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Playlist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Playlist;
|
||||
|
||||
public class YPlaylistCreateBuilder : YMusicRequestBuilder<YPlaylist?, string>
|
||||
internal class YPlaylistCreateBuilder : YMusicRequestBuilder<YPlaylist?, string>
|
||||
{
|
||||
public YPlaylistCreateBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace YandexMusic.API.Requests.Playlist;
|
||||
|
||||
public class YPlaylistRemoveBuilder : YMusicRequestBuilder<HttpResponseMessage, string>
|
||||
internal class YPlaylistRemoveBuilder : YMusicRequestBuilder<HttpResponseMessage, string>
|
||||
{
|
||||
public YPlaylistRemoveBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Playlist;
|
||||
|
||||
namespace YandexMusic.API.Requests.Playlist;
|
||||
|
||||
public class YPlaylistRenameBuilder : YMusicRequestBuilder<YPlaylist?, (string kind, string name)>
|
||||
internal class YPlaylistRenameBuilder : YMusicRequestBuilder<YPlaylist?, (string kind, string name)>
|
||||
{
|
||||
public YPlaylistRenameBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Post;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Queue;
|
||||
|
||||
namespace YandexMusic.API.Requests.Queue;
|
||||
|
||||
public class YGetQueueBuilder : YMusicRequestBuilder<YQueue?, string>
|
||||
internal class YGetQueueBuilder : YMusicRequestBuilder<YQueue?, string>
|
||||
{
|
||||
public YGetQueueBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -8,7 +8,7 @@ using YandexMusic.API.Models.Queue;
|
||||
|
||||
namespace YandexMusic.API.Requests.Queue;
|
||||
|
||||
public class YQueueCreateBuilder : YMusicRequestBuilder<YNewQueue?, YQueue>
|
||||
internal class YQueueCreateBuilder : YMusicRequestBuilder<YNewQueue?, YQueue>
|
||||
{
|
||||
private string? _device;
|
||||
public YQueueCreateBuilder(YandexMusicApi api, string? device = null) : base(api) => _device = device;
|
||||
|
||||
@@ -5,7 +5,7 @@ using YandexMusic.API.Models.Queue;
|
||||
|
||||
namespace YandexMusic.API.Requests.Queue;
|
||||
|
||||
public class YQueueUpdatePositionBuilder : YMusicRequestBuilder<YUpdatedQueue?, (string queueId, int currentIndex, bool isInteractive)>
|
||||
internal class YQueueUpdatePositionBuilder : YMusicRequestBuilder<YUpdatedQueue?, (string queueId, int currentIndex, bool isInteractive)>
|
||||
{
|
||||
private string? _device;
|
||||
public YQueueUpdatePositionBuilder(YandexMusicApi api, string? device = null) : base(api) => _device = device;
|
||||
|
||||
@@ -4,7 +4,7 @@ using YandexMusic.API.Models.Queue;
|
||||
|
||||
namespace YandexMusic.API.Requests.Queue;
|
||||
|
||||
public class YQueuesListBuilder : YMusicRequestBuilder<YQueueItemsContainer?, string?>
|
||||
internal class YQueuesListBuilder : YMusicRequestBuilder<YQueueItemsContainer?, string?>
|
||||
{
|
||||
private string? _device;
|
||||
public YQueuesListBuilder(YandexMusicApi api, string? device = null) : base(api) => _device = device;
|
||||
|
||||
@@ -3,7 +3,7 @@ using YandexMusic.API.Models.Radio;
|
||||
|
||||
namespace YandexMusic.API.Requests.Radio;
|
||||
|
||||
public class YGetStationBuilder : YMusicRequestBuilder<List<YStation>?, (string type, string tag)>
|
||||
internal class YGetStationBuilder : YMusicRequestBuilder<List<YStation>?, (string type, string tag)>
|
||||
{
|
||||
public YGetStationBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
@@ -4,7 +4,7 @@ using YandexMusic.API.Models.Radio;
|
||||
|
||||
namespace YandexMusic.API.Requests.Radio;
|
||||
|
||||
public class YGetStationTracksBuilder : YMusicRequestBuilder<YStationSequence?, (YStationDescription station, string prevTrackId)>
|
||||
internal class YGetStationTracksBuilder : YMusicRequestBuilder<YStationSequence?, (YStationDescription station, string prevTrackId)>
|
||||
{
|
||||
public YGetStationTracksBuilder(YandexMusicApi api) : base(api) { }
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user