Переделано воспроизведение аудио
All checks were successful
Release / pack-and-publish (release) Successful in 36s
All checks were successful
Release / pack-and-publish (release) Successful in 36s
This commit is contained in:
@@ -15,7 +15,7 @@ public static class YAlbumExtensions
|
|||||||
if (album.Volumes != null)
|
if (album.Volumes != null)
|
||||||
return album;
|
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;
|
return result ?? album;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,11 +23,11 @@ public static class YAlbumExtensions
|
|||||||
/// Добавляет альбом в список лайкнутых.
|
/// Добавляет альбом в список лайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<string?> AddLikeAsync(this YAlbum album)
|
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>
|
||||||
/// Удаляет альбом из списка лайкнутых.
|
/// Удаляет альбом из списка лайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<string?> RemoveLikeAsync(this YAlbum album)
|
public static async Task<string?> RemoveLikeAsync(this YAlbum album)
|
||||||
=> await album.Context.API.Library.RemoveAlbumLikeAsync(album);
|
=> await album.Context.Api.Library.RemoveAlbumLikeAsync(album);
|
||||||
}
|
}
|
||||||
@@ -12,29 +12,29 @@ public static class YArtistExtensions
|
|||||||
/// Получает расширенную информацию об исполнителе.
|
/// Получает расширенную информацию об исполнителе.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<YArtistBriefInfo?> BriefInfoAsync(this YArtist artist)
|
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>
|
||||||
/// Получает страницу треков исполнителя.
|
/// Получает страницу треков исполнителя.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<YTracksPage?> GetTracksAsync(this YArtist artist, int page = 0, int pageSize = 20)
|
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>
|
||||||
/// Получает все треки исполнителя.
|
/// Получает все треки исполнителя.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<List<YTrack>?> GetAllTracksAsync(this YArtist artist)
|
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>
|
||||||
/// Добавляет исполнителя в список лайкнутых.
|
/// Добавляет исполнителя в список лайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<string?> AddLikeAsync(this YArtist artist)
|
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>
|
||||||
/// Удаляет исполнителя из списка лайкнутых.
|
/// Удаляет исполнителя из списка лайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<string?> RemoveLikeAsync(this YArtist artist)
|
public static async Task<string?> RemoveLikeAsync(this YArtist artist)
|
||||||
=> await artist.Context.API.Library.RemoveArtistLikeAsync(artist);
|
=> await artist.Context.Api.Library.RemoveArtistLikeAsync(artist);
|
||||||
}
|
}
|
||||||
@@ -18,44 +18,44 @@ public static class YPlaylistExtensions
|
|||||||
{
|
{
|
||||||
if (playlist.Tracks != null)
|
if (playlist.Tracks != null)
|
||||||
return playlist;
|
return playlist;
|
||||||
return await playlist.Context.API.Playlist.GetAsync(playlist);
|
return await playlist.Context.Api.Playlist.GetAsync(playlist);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Добавляет плейлист в список лайкнутых.
|
/// Добавляет плейлист в список лайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<string?> AddLikeAsync(this YPlaylist playlist)
|
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>
|
||||||
/// Удаляет плейлист из списка лайкнутых.
|
/// Удаляет плейлист из списка лайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<string?> RemoveLikeAsync(this YPlaylist playlist)
|
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>
|
||||||
/// Переименовывает плейлист (только для владельца).
|
/// Переименовывает плейлист (только для владельца).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<YPlaylist?> RenameAsync(this YPlaylist playlist, string newName)
|
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>
|
||||||
/// Удаляет плейлист (только для владельца).
|
/// Удаляет плейлист (только для владельца).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<bool> DeleteAsync(this YPlaylist playlist)
|
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>
|
||||||
/// Вставляет треки в начало плейлиста (только для владельца).
|
/// Вставляет треки в начало плейлиста (только для владельца).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<YPlaylist?> InsertTracksAsync(this YPlaylist playlist, params YTrack[] tracks)
|
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>
|
||||||
/// Удаляет треки из плейлиста (только для владельца).
|
/// Удаляет треки из плейлиста (только для владельца).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<YPlaylist?> RemoveTracksAsync(this YPlaylist playlist, params YTrack[] tracks)
|
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>
|
/// <summary>
|
||||||
/// Загружает трек в плейлист (только для владельца).
|
/// Загружает трек в плейлист (только для владельца).
|
||||||
@@ -63,7 +63,7 @@ public static class YPlaylistExtensions
|
|||||||
public static async Task<bool> UploadTrackAsync(this YPlaylist playlist, string filePath, string fileName)
|
public static async Task<bool> UploadTrackAsync(this YPlaylist playlist, string filePath, string fileName)
|
||||||
{
|
{
|
||||||
if (!IsOwner(playlist)) return false;
|
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";
|
return result == "CREATED";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,17 +12,17 @@ public static class YStationResultExtensions
|
|||||||
/// Получает список треков для радиостанции.
|
/// Получает список треков для радиостанции.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<List<YSequenceItem>?> GetTracksAsync(this YStation station, string prevTrackId = "")
|
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>
|
||||||
/// Устанавливает настройки станции.
|
/// Устанавливает настройки станции.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<string?> SetSettings2Async(this YStation station, YStationSettings2 settings)
|
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>
|
||||||
/// Отправляет обратную связь о прослушивании.
|
/// Отправляет обратную связь о прослушивании.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Task<string?> SendFeedbackAsync(this YStation station, YStationFeedbackType type, YTrack? track = null, string batchId = "", double totalPlayedSeconds = 0)
|
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);
|
||||||
}
|
}
|
||||||
@@ -11,53 +11,53 @@ public static class YTrackExtensions
|
|||||||
/// Получает прямую ссылку на скачивание трека.
|
/// Получает прямую ссылку на скачивание трека.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Task<string?> GetLinkAsync(this YTrack track)
|
public static Task<string?> GetLinkAsync(this YTrack track)
|
||||||
=> track.Context.API.Track.GetFileLinkAsync(track);
|
=> track.Context.Api.Track.GetFileLinkAsync(track);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Сохраняет трек в файл.
|
/// Сохраняет трек в файл.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Task SaveAsync(this YTrack track, string filePath)
|
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>
|
||||||
/// Добавляет трек в список лайкнутых.
|
/// Добавляет трек в список лайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<int?> AddLikeAsync(this YTrack track)
|
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>
|
||||||
/// Удаляет трек из списка лайкнутых.
|
/// Удаляет трек из списка лайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<int?> RemoveLikeAsync(this YTrack track)
|
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>
|
||||||
/// Добавляет трек в список дизлайкнутых.
|
/// Добавляет трек в список дизлайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<int?> AddDislikeAsync(this YTrack track)
|
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>
|
||||||
/// Удаляет трек из списка дизлайкнутых.
|
/// Удаляет трек из списка дизлайкнутых.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<int?> RemoveDislikeAsync(this YTrack track)
|
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>
|
||||||
/// Отправляет информацию о воспроизведении трека.
|
/// Отправляет информацию о воспроизведении трека.
|
||||||
/// </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)
|
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>
|
||||||
/// Получает дополнительную информацию о треке.
|
/// Получает дополнительную информацию о треке.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<YTrackSupplement?> SupplementAsync(this YTrack track)
|
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>
|
||||||
/// Получает похожие треки.
|
/// Получает похожие треки.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static async Task<YTrackSimilar?> SimilarAsync(this YTrack track)
|
public static async Task<YTrackSimilar?> SimilarAsync(this YTrack track)
|
||||||
=> await track.Context.API.Track.GetSimilarAsync(track);
|
=> await track.Context.Api.Track.GetSimilarAsync(track);
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace YandexMusic.API.Models.Common;
|
|||||||
public class YExecutionContext
|
public class YExecutionContext
|
||||||
{
|
{
|
||||||
/// <summary>Экземпляр основного API.</summary>
|
/// <summary>Экземпляр основного API.</summary>
|
||||||
public YandexMusicApi API { get; internal set; } = null!;
|
public YandexMusicApi Api { get; internal set; } = null!;
|
||||||
|
|
||||||
/// <summary>Хранилище данных авторизации.</summary>
|
/// <summary>Хранилище данных авторизации.</summary>
|
||||||
public AuthStorage Storage { get; internal set; } = null!;
|
public AuthStorage Storage { get; internal set; } = null!;
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ public class YExecutionContextConverter : JsonConverter<object>
|
|||||||
var obj = JsonSerializer.Deserialize(ref reader, typeToConvert, innerOptions);
|
var obj = JsonSerializer.Deserialize(ref reader, typeToConvert, innerOptions);
|
||||||
if (obj is YBaseModel baseModel)
|
if (obj is YBaseModel baseModel)
|
||||||
{
|
{
|
||||||
baseModel.Context = new YExecutionContext { API = _api, Storage = _storage };
|
baseModel.Context = new YExecutionContext { Api = _api, Storage = _storage };
|
||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
|
using System.Xml.Serialization;
|
||||||
|
|
||||||
namespace YandexMusic.API.Models.Common;
|
namespace YandexMusic.API.Models.Common;
|
||||||
|
|
||||||
|
[XmlRoot("download-info")]
|
||||||
public class YStorageDownloadFile
|
public class YStorageDownloadFile
|
||||||
{
|
{
|
||||||
|
[XmlElement("host")]
|
||||||
public string Host { get; set; }
|
public string Host { get; set; }
|
||||||
|
[XmlElement("path")]
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
|
[XmlElement("s")]
|
||||||
public string S { get; set; }
|
public string S { get; set; }
|
||||||
|
[XmlElement("ts")]
|
||||||
public string Ts { get; set; }
|
public string Ts { get; set; }
|
||||||
}
|
}
|
||||||
@@ -10,7 +10,17 @@ namespace YandexMusic.API.Requests.Common;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
internal 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)
|
protected virtual async Task<TResponse?> DeserializeAsync(HttpResponseMessage response)
|
||||||
{
|
{
|
||||||
@@ -43,6 +53,8 @@ internal abstract class YJsonRequestBuilder<TResponse, TParams> : YRequestBuilde
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected string SerializeJson(object data) => JsonSerializer.Serialize(data, _jsonOptions);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Выполняет запрос и возвращает десериализованный объект типа TResponse.
|
/// Выполняет запрос и возвращает десериализованный объект типа TResponse.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -51,4 +63,4 @@ internal abstract class YJsonRequestBuilder<TResponse, TParams> : YRequestBuilde
|
|||||||
using var response = await ExecuteRawAsync(parameters);
|
using var response = await ExecuteRawAsync(parameters);
|
||||||
return await DeserializeAsync(response);
|
return await DeserializeAsync(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
using YandexMusic.API.Common;
|
using YandexMusic.API.Common;
|
||||||
@@ -23,7 +21,8 @@ internal abstract class YRequestBuilder<TParams>
|
|||||||
/// <summary>Шаблон пути (может содержать плейсхолдеры вида {id}).</summary>
|
/// <summary>Шаблон пути (может содержать плейсхолдеры вида {id}).</summary>
|
||||||
protected abstract string PathTemplate { get; }
|
protected abstract string PathTemplate { get; }
|
||||||
|
|
||||||
private readonly JsonSerializerOptions _jsonOptions;
|
/// <summary>Определяет, нужно ли добавлять заголовок Authorization для этого запроса.</summary>
|
||||||
|
protected virtual bool ShouldAddAuthorization => true;
|
||||||
|
|
||||||
/// <summary>Основной экземпляр API.</summary>
|
/// <summary>Основной экземпляр API.</summary>
|
||||||
protected YandexMusicApi Api { get; }
|
protected YandexMusicApi Api { get; }
|
||||||
@@ -34,12 +33,6 @@ internal abstract class YRequestBuilder<TParams>
|
|||||||
protected YRequestBuilder(YandexMusicApi api)
|
protected YRequestBuilder(YandexMusicApi api)
|
||||||
{
|
{
|
||||||
Api = api;
|
Api = api;
|
||||||
_jsonOptions = new JsonSerializerOptions
|
|
||||||
{
|
|
||||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
|
||||||
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string FullUrl => $"{BaseUrl.TrimEnd('/')}/{PathTemplate.TrimStart('/')}";
|
private string FullUrl => $"{BaseUrl.TrimEnd('/')}/{PathTemplate.TrimStart('/')}";
|
||||||
@@ -66,7 +59,7 @@ internal abstract class YRequestBuilder<TParams>
|
|||||||
};
|
};
|
||||||
msg.Headers.TryAddWithoutValidation(GetHeaderName(HttpRequestHeader.AcceptCharset), Encoding.UTF8.WebName);
|
msg.Headers.TryAddWithoutValidation(GetHeaderName(HttpRequestHeader.AcceptCharset), Encoding.UTF8.WebName);
|
||||||
msg.Headers.TryAddWithoutValidation(GetHeaderName(HttpRequestHeader.AcceptEncoding), "gzip");
|
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}");
|
msg.Headers.TryAddWithoutValidation(GetHeaderName(HttpRequestHeader.Authorization), $"OAuth {Storage.Token}");
|
||||||
SetCustomHeaders(msg.Headers);
|
SetCustomHeaders(msg.Headers);
|
||||||
return msg;
|
return msg;
|
||||||
@@ -120,8 +113,6 @@ internal abstract class YRequestBuilder<TParams>
|
|||||||
protected virtual HttpContent? GetContent(TParams parameters) => null;
|
protected virtual HttpContent? GetContent(TParams parameters) => null;
|
||||||
protected virtual void SetCustomHeaders(HttpRequestHeaders headers) { }
|
protected virtual void SetCustomHeaders(HttpRequestHeaders headers) { }
|
||||||
|
|
||||||
protected string SerializeJson(object data) => JsonSerializer.Serialize(data, _jsonOptions);
|
|
||||||
|
|
||||||
/// <summary>Выполняет запрос и возвращает десериализованный ответ.</summary>
|
/// <summary>Выполняет запрос и возвращает десериализованный ответ.</summary>
|
||||||
public async Task<HttpResponseMessage?> ExecuteRawAsync(TParams parameters)
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,9 +6,11 @@ using YandexMusic.API.Requests.Common;
|
|||||||
namespace YandexMusic.API.Requests.Track;
|
namespace YandexMusic.API.Requests.Track;
|
||||||
|
|
||||||
/// <summary>Особый запрос – не к api.music.yandex.net, а к произвольному URL.</summary>
|
/// <summary>Особый запрос – не к api.music.yandex.net, а к произвольному URL.</summary>
|
||||||
internal class YStorageDownloadFileBuilder : YJsonRequestBuilder<YStorageDownloadFile?, string>
|
internal class YStorageDownloadFileBuilder : YXmlRequestBuilder<YStorageDownloadFile?, string>
|
||||||
{
|
{
|
||||||
public YStorageDownloadFileBuilder(YandexMusicApi api) : base(api) { }
|
public YStorageDownloadFileBuilder(YandexMusicApi api) : base(api) { }
|
||||||
|
protected override bool ShouldAddAuthorization => false;
|
||||||
|
|
||||||
protected override string BaseUrl => "{src}"; // не используется, т.к. URL берётся из параметра
|
protected override string BaseUrl => "{src}"; // не используется, т.к. URL берётся из параметра
|
||||||
|
|
||||||
protected override string Method => WebRequestMethods.Http.Get;
|
protected override string Method => WebRequestMethods.Http.Get;
|
||||||
@@ -16,10 +18,11 @@ internal class YStorageDownloadFileBuilder : YJsonRequestBuilder<YStorageDownloa
|
|||||||
protected override string PathTemplate => "";
|
protected override string PathTemplate => "";
|
||||||
|
|
||||||
protected override Dictionary<string, string> GetSubstitutions(string src)
|
protected override Dictionary<string, string> GetSubstitutions(string src)
|
||||||
=> new() { { "src", src.Split('?')[0] } };
|
=> new() { { "src", src } };
|
||||||
|
|
||||||
protected override NameValueCollection GetQueryParams(string src)
|
protected override NameValueCollection GetQueryParams(string src)
|
||||||
{
|
{
|
||||||
var query = new NameValueCollection { { "format", "json" } };
|
var query = new NameValueCollection();
|
||||||
var parts = src.Split('?');
|
var parts = src.Split('?');
|
||||||
if (parts.Length > 1)
|
if (parts.Length > 1)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user