using System.Collections.Specialized;
using System.Net;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Web;
using YandexMusic.API.Common;
using YandexMusic.API.Extensions;
using YandexMusic.API.Requests.Common.Attributes;
namespace YandexMusic.API.Requests.Common;
/// Базовый строитель HTTP-запросов к API Яндекс.Музыки.
/// Тип ответа.
/// Тип параметров запроса.
public abstract class YRequestBuilder
{
private readonly YRequestAttribute _requestInfo;
private Dictionary _substitutions = null!;
private readonly JsonSerializerOptions _jsonOptions;
protected readonly YandexMusicApi api;
protected readonly AuthStorage storage;
protected string device;
protected YRequestBuilder(YandexMusicApi yandex, AuthStorage auth)
{
_requestInfo = GetType().GetCustomAttribute()
?? throw new NotImplementedException($"Отсутствует атрибут {nameof(YRequestAttribute)}");
api = yandex;
storage = auth;
device = $"os=CSharp; os_version=; manufacturer=FrigaT; model=Yandex Music API; clid=; device_id={storage.DeviceId}; uuid=random";
_jsonOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }
};
}
private Uri BuildUri(TParams tuple)
{
var queryParams = GetQueryParams(tuple);
var modifiedParams = HttpUtility.ParseQueryString(string.Empty);
foreach (string? key in queryParams)
if (key != null)
modifiedParams[key] = ReplaceSubs(queryParams[key]!);
var endpoint = ReplaceSubs(_requestInfo.Url);
var builder = new UriBuilder(endpoint) { Query = modifiedParams.ToString() ?? string.Empty };
return builder.Uri;
}
private HttpRequestMessage CreateMessage(TParams tuple)
{
var msg = new HttpRequestMessage
{
RequestUri = BuildUri(tuple),
Method = new HttpMethod(_requestInfo.Method),
Content = GetContent(tuple)
};
msg.Headers.TryAddWithoutValidation(HttpRequestHeader.AcceptCharset.GetName(), Encoding.UTF8.WebName);
msg.Headers.TryAddWithoutValidation(HttpRequestHeader.AcceptEncoding.GetName(), "gzip");
if (!string.IsNullOrEmpty(storage.Token))
msg.Headers.TryAddWithoutValidation(HttpRequestHeader.Authorization.GetName(), $"OAuth {storage.Token}");
SetCustomHeaders(msg.Headers);
return msg;
}
protected string ReplaceSubs(string str)
{
var subs = str.GetMatches(@"\{.+?\}");
foreach (var s in subs)
{
var key = s.ReplaceRegex(@"[\{\}]", string.Empty);
if (!_substitutions.TryGetValue(key, out var value))
throw new Exception($"Не найдена подстановка {s}");
str = str.Replace(s, value);
}
return str;
}
protected virtual Dictionary GetSubstitutions(TParams tuple) => [];
protected virtual NameValueCollection GetQueryParams(TParams tuple) => [];
protected virtual HttpContent? GetContent(TParams tuple) => null;
protected virtual void SetCustomHeaders(HttpRequestHeaders headers) { }
protected string SerializeJson(object data) => JsonSerializer.Serialize(data, _jsonOptions);
internal YRequest Build(TParams tuple)
{
_substitutions = GetSubstitutions(tuple);
var msg = CreateMessage(tuple);
return new YRequest(msg, api, storage);
}
}