Переделано воспроизведение аудио
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:
@@ -10,7 +10,17 @@ namespace YandexMusic.API.Requests.Common;
|
||||
/// </summary>
|
||||
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 @@ internal abstract class YJsonRequestBuilder<TResponse, TParams> : YRequestBuilde
|
||||
}
|
||||
}
|
||||
|
||||
protected string SerializeJson(object data) => JsonSerializer.Serialize(data, _jsonOptions);
|
||||
|
||||
/// <summary>
|
||||
/// Выполняет запрос и возвращает десериализованный объект типа TResponse.
|
||||
/// </summary>
|
||||
@@ -51,4 +63,4 @@ internal abstract class YJsonRequestBuilder<TResponse, TParams> : YRequestBuilde
|
||||
using var response = await ExecuteRawAsync(parameters);
|
||||
return await DeserializeAsync(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -23,7 +21,8 @@ internal 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 @@ internal 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 @@ internal 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 @@ internal 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);
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,11 @@ using YandexMusic.API.Requests.Common;
|
||||
namespace YandexMusic.API.Requests.Track;
|
||||
|
||||
/// <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) { }
|
||||
protected override bool ShouldAddAuthorization => false;
|
||||
|
||||
protected override string BaseUrl => "{src}"; // не используется, т.к. URL берётся из параметра
|
||||
|
||||
protected override string Method => WebRequestMethods.Http.Get;
|
||||
@@ -16,10 +18,11 @@ internal class YStorageDownloadFileBuilder : YJsonRequestBuilder<YStorageDownloa
|
||||
protected override string PathTemplate => "";
|
||||
|
||||
protected override Dictionary<string, string> GetSubstitutions(string src)
|
||||
=> new() { { "src", src.Split('?')[0] } };
|
||||
=> new() { { "src", src } };
|
||||
|
||||
protected override NameValueCollection GetQueryParams(string src)
|
||||
{
|
||||
var query = new NameValueCollection { { "format", "json" } };
|
||||
var query = new NameValueCollection();
|
||||
var parts = src.Split('?');
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user