Добавьте файлы проекта.

This commit is contained in:
FrigaT
2026-04-10 12:12:33 +03:00
parent 9615cf42ee
commit 11d0b0d72f
383 changed files with 9661 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
public class YApiRequestAttribute : YBasePathRequestAttribute
{
public YApiRequestAttribute(string method, string url) : base(method, url)
{
basePath = "https://api.music.yandex.net";
}
}
}

View File

@@ -0,0 +1,32 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
/// <summary>
/// Атрибут запроса относительно базового адреса
/// </summary>
public class YBasePathRequestAttribute : YRequestAttribute
{
#region Поля
protected string basePath;
#endregion Поля
#region Свойства
public override string Url => GetFullUrl();
#endregion Свойства
#region Вспомогательные функции
private string GetFullUrl()
{
return $"{basePath.TrimEnd('/')}/{path.TrimStart('/')}";
}
#endregion Вспомогательные функции
public YBasePathRequestAttribute(string method, string url) : base(method, url)
{
}
}
}

View File

@@ -0,0 +1,10 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
public class YLoginRequestAttribute : YBasePathRequestAttribute
{
public YLoginRequestAttribute(string method, string url) : base(method, url)
{
basePath = "https://login.yandex.ru";
}
}
}

View File

@@ -0,0 +1,10 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
public class YMobileProxyRequestAttribute : YBasePathRequestAttribute
{
public YMobileProxyRequestAttribute(string method, string url) : base(method, url)
{
basePath = "https://mobileproxy.passport.yandex.net";
}
}
}

View File

@@ -0,0 +1,10 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
public class YOAuthMobileAttribute : YBasePathRequestAttribute
{
public YOAuthMobileAttribute(string method, string url) : base(method, url)
{
basePath = "https://oauth.mobile.yandex.net";
}
}
}

View File

@@ -0,0 +1,10 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
public class YOAuthRequestAttribute : YBasePathRequestAttribute
{
public YOAuthRequestAttribute(string method, string url) : base(method, url)
{
basePath = "https://oauth.yandex.ru";
}
}
}

View File

@@ -0,0 +1,10 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
public class YPassportRequestAttribute : YBasePathRequestAttribute
{
public YPassportRequestAttribute(string method, string url) : base(method, url)
{
basePath = "https://passport.yandex.ru";
}
}
}

View File

@@ -0,0 +1,27 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
/// <summary>
/// Атрибут запроса без привязки к базовому адресу
/// </summary>
public class YRequestAttribute : Attribute
{
#region Поля
protected string path;
#endregion Поля
#region Свойства
public string Method { get; }
public virtual string Url => path;
#endregion Свойства
public YRequestAttribute(string method, string url)
{
Method = method;
path = url;
}
}
}

View File

@@ -0,0 +1,10 @@
namespace YandexMusic.API.Requests.Common.Attributes
{
public class YWebApiRequestAttribute : YBasePathRequestAttribute
{
public YWebApiRequestAttribute(string method, string url) : base(method, url)
{
basePath = "https://music.yandex.ru";
}
}
}

View File

@@ -0,0 +1,26 @@
using System.Net;
namespace YandexMusic.API.Requests.Common
{
public class HttpContext
{
public CookieContainer Cookies;
public HttpContext()
{
Cookies = new CookieContainer();
}
public IWebProxy WebProxy { get; set; }
public long GetTimeInterval()
{
DateTime dt = TimeZoneInfo.ConvertTimeToUtc(DateTime.Now);
DateTime dt1970 = new(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
TimeSpan tsInterval = dt.Subtract(dt1970);
long iMilliseconds = Convert.ToInt64(tsInterval.TotalMilliseconds);
return iMilliseconds;
}
}
}

View File

@@ -0,0 +1,11 @@
namespace YandexMusic.API.Requests.Common
{
internal class YConstants
{
public static string ClientId = "23cabbbdc6cd418abb4b39c32c41195d";
public static string ClientSecret = "53bc75238f0c4d08a118e51fe9203300";
public const string XClientId = "c0ebe342af7d48fbbbfcf2d2eedb8f9e";
public const string XClientSecret = "ad0a908f0aa341a182a37ecd75bc319e";
}
}

View File

@@ -0,0 +1,48 @@
using YandexMusic.API.Common;
using YandexMusic.API.Common.Providers;
namespace YandexMusic.API.Requests.Common
{
internal class YRequest<T>
{
#region Поля
private HttpRequestMessage msg;
private IRequestProvider provider;
protected YandexMusicApi api;
#endregion Поля
public YRequest(HttpRequestMessage message, YandexMusicApi yandex, AuthStorage auth)
{
msg = message;
api = yandex;
provider = auth.Provider;
}
public async Task<T> GetResponseAsync()
{
if (msg == null)
return default;
HttpResponseMessage response = await provider.GetWebResponseAsync(msg);
if (typeof(T) == typeof(HttpResponseMessage))
return (T)(object)response;
try
{
return await provider.GetDataFromResponseAsync<T>(api, response);
}
finally
{
response.Dispose();
}
}
}
}

View File

@@ -0,0 +1,158 @@
using System.Collections.Specialized;
using System.Net;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Web;
using YandexMusic.API.Common;
using YandexMusic.API.Extensions;
using YandexMusic.API.Requests.Common.Attributes;
namespace YandexMusic.API.Requests.Common
{
public class YRequestBuilder<ResponseType, ParamsTuple>
{
#region Поля
private readonly JsonSerializerSettings jsonSettings = new()
{
Converters = new List<JsonConverter> {
new StringEnumConverter {
NamingStrategy = new CamelCaseNamingStrategy()
}
},
NullValueHandling = NullValueHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
private YRequestAttribute requestInfo;
private Dictionary<string, string> subs;
protected YandexMusicApi api;
protected AuthStorage storage;
protected string device;
#endregion Поля
#region Свойства
protected YandexMusicApi API => api;
protected AuthStorage Storage => storage;
#endregion Свойства
#region Вспомогательные функции
private Uri BuildUri(ParamsTuple tuple)
{
NameValueCollection queryParams = GetQueryParams(tuple);
NameValueCollection modifiedParams = HttpUtility.ParseQueryString(string.Empty);
// Подстановка в параметры
foreach (string key in queryParams.Keys)
{
modifiedParams.Set(key, ReplaceSubs(queryParams.Get(key)));
}
string endpoint = ReplaceSubs(requestInfo.Url);
UriBuilder builder = new(endpoint)
{
Query = modifiedParams.ToString() ?? string.Empty
};
return builder.Uri;
}
private HttpRequestMessage CreateMessage(ParamsTuple tuple)
{
HttpRequestMessage msg = new()
{
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)
{
string[] sub = str.GetMatches(@"\{.+?\}");
foreach (string s in sub)
{
if (!subs.TryGetValue(s.ReplaceRegex(@"[\{\}]", string.Empty), out string value))
throw new Exception($"Не найдена подстановка {s}");
str = str.Replace(s, value);
}
return str;
}
protected virtual Dictionary<string, string> GetSubstitutions(ParamsTuple tuple)
{
return new Dictionary<string, string>();
}
protected virtual NameValueCollection GetQueryParams(ParamsTuple tuple)
{
return new NameValueCollection();
}
protected virtual HttpContent GetContent(ParamsTuple tuple)
{
return null;
}
protected virtual void SetCustomHeaders(HttpRequestHeaders headers)
{
}
protected string SerializeJson(object data)
{
return JsonConvert.SerializeObject(data, jsonSettings);
}
#endregion Вспомогательные функции
public YRequestBuilder(YandexMusicApi yandex, AuthStorage auth)
{
requestInfo = GetType()
.GetCustomAttributes<YRequestAttribute>()
.FirstOrDefault();
if (requestInfo == null)
throw new NotImplementedException($"Отсутствует атрибут {nameof(YRequestAttribute)}");
api = yandex;
storage = auth;
// Устройство по умолчанию
device = $"os=CSharp; os_version=; manufacturer=K1llM@n; model=Yandex Music API; clid=; device_id={storage.DeviceId}; uuid=random";
}
internal YRequest<ResponseType> Build(ParamsTuple tuple)
{
subs = GetSubstitutions(tuple);
HttpRequestMessage msg = CreateMessage(tuple);
return new YRequest<ResponseType>(msg, api, storage);
}
}
}