readme
This commit is contained in:
607
ARCHITECTURE.md
Normal file
607
ARCHITECTURE.md
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
# 🏗️ Архитектура YandexMusic
|
||||||
|
|
||||||
|
Полное описание архитектуры, компонентов и взаимодействия между слоями решения YandexMusic.
|
||||||
|
|
||||||
|
## 📋 Содержание
|
||||||
|
|
||||||
|
- [Обзор архитектуры](#обзор-архитектуры)
|
||||||
|
- [Структура слоёв](#структура-слоёв)
|
||||||
|
- [Основные компоненты](#основные-компоненты)
|
||||||
|
- [Потоки данных](#потоки-данных)
|
||||||
|
- [Паттерны проектирования](#паттерны-проектирования)
|
||||||
|
- [Модульная структура](#модульная-структура)
|
||||||
|
- [Расширяемость](#расширяемость)
|
||||||
|
|
||||||
|
## 🔍 Обзор архитектуры
|
||||||
|
|
||||||
|
### Многослойная архитектура
|
||||||
|
|
||||||
|
Проект построен на классической трёхслойной архитектуре с элементами DDD:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌────────────────────────────────────────┐
|
||||||
|
│ CLI Layer (YaMusicCli) │ ← Точка входа
|
||||||
|
├────────────────────────────────────────┤
|
||||||
|
│ Client Layer (YandexMusic) │ ← Удобный интерфейс
|
||||||
|
├────────────────────────────────────────┤
|
||||||
|
│ API Layer (YandexMusic.API) │ ← Логика API
|
||||||
|
├────────────────────────────────────────┤
|
||||||
|
│ Data Layer (Models) │ ← Данные и сущности
|
||||||
|
├────────────────────────────────────────┤
|
||||||
|
│ Infrastructure Layer │ ← HTTP, Serialization
|
||||||
|
└────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ключевые принципы
|
||||||
|
|
||||||
|
- **Separation of Concerns** - каждый слой отвечает за одно
|
||||||
|
- **Dependency Injection** - слабая связанность через интерфейсы
|
||||||
|
- **Single Responsibility** - каждый класс решает одну задачу
|
||||||
|
- **Open/Closed Principle** - открыто для расширения, закрыто для модификации
|
||||||
|
- **Asynchronous First** - всё асинхронное по умолчанию
|
||||||
|
|
||||||
|
## 📦 Структура слоёв
|
||||||
|
|
||||||
|
### 1. CLI Layer (YaMusicCli)
|
||||||
|
|
||||||
|
**Назначение:** Командная строка интерфейс для пользователей.
|
||||||
|
|
||||||
|
```
|
||||||
|
YaMusicCli/
|
||||||
|
├── Program.cs # Точка входа, парсинг аргументов
|
||||||
|
└── Commands/ # Команды для CLI
|
||||||
|
├── SearchCommand
|
||||||
|
├── PlaylistCommand
|
||||||
|
└── TrackCommand
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ответственность:**
|
||||||
|
|
||||||
|
- Парсинг команд пользователя
|
||||||
|
- Вывод результатов в консоль
|
||||||
|
- Обработка пользовательского ввода
|
||||||
|
- Форматирование данных для вывода
|
||||||
|
|
||||||
|
**Зависимости:** YandexMusic (Client Layer)
|
||||||
|
|
||||||
|
### 2. Client Layer (YandexMusic)
|
||||||
|
|
||||||
|
**Назначение:** Удобный оборачиватель (wrapper) для работы с API.
|
||||||
|
|
||||||
|
```
|
||||||
|
YandexMusic/
|
||||||
|
└── YandexMusicClient.cs
|
||||||
|
├── Initialization (конструктор)
|
||||||
|
├── Authorization (авторизация)
|
||||||
|
├── Properties (доступ к API и хранилищу)
|
||||||
|
└── Cleanup (dispose)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Основной класс:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class YandexMusicClient : IDisposable
|
||||||
|
{
|
||||||
|
// Инициализация
|
||||||
|
public YandexMusicClient(
|
||||||
|
CookieContainer? cookieContainer = null,
|
||||||
|
IWebProxy? proxy = null,
|
||||||
|
TimeSpan? timeout = null,
|
||||||
|
string? userAgent = null)
|
||||||
|
|
||||||
|
// Авторизация
|
||||||
|
public async Task AuthorizeAsync(string login, string password)
|
||||||
|
public async Task AuthorizeByTokenAsync(string token)
|
||||||
|
|
||||||
|
// Свойства доступа
|
||||||
|
public YandexMusicApi Api { get; } // API Яндекс Музыки
|
||||||
|
public AuthStorage AuthStorage { get; } // Хранилище данных
|
||||||
|
public HttpClient HttpClient { get; } // HttpClient
|
||||||
|
public YAccount Account { get; } // Информация об аккаунте
|
||||||
|
public bool IsAuthorized { get; } // Статус авторизации
|
||||||
|
public YnisonPlayer? Ynison { get; } // WebSocket плеер
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ответственность:**
|
||||||
|
|
||||||
|
- Упрощение работы с API
|
||||||
|
- Управление HttpClient и cookies
|
||||||
|
- Обработка авторизации
|
||||||
|
- Инициализация компонентов
|
||||||
|
|
||||||
|
**Зависимости:** YandexMusic.API (API Layer)
|
||||||
|
|
||||||
|
### 3. API Layer (YandexMusic.API)
|
||||||
|
|
||||||
|
**Назначение:** Низкоуровневой доступ к API Яндекс Музыки.
|
||||||
|
|
||||||
|
```
|
||||||
|
YandexMusic.API/
|
||||||
|
├── YandexMusicApi.cs # Главный класс - фасад
|
||||||
|
├── API/ # API классы по функциям
|
||||||
|
│ ├── YAlbumAPI
|
||||||
|
│ ├── YArtistAPI
|
||||||
|
│ ├── YTrackAPI
|
||||||
|
│ ├── YPlaylistAPI
|
||||||
|
│ ├── YRadioAPI
|
||||||
|
│ ├── YSearchAPI
|
||||||
|
│ └── ... (остальные API)
|
||||||
|
├── Requests/ # Построители запросов
|
||||||
|
│ ├── Common/
|
||||||
|
│ ├── Album/
|
||||||
|
│ ├── Artist/
|
||||||
|
│ └── ... (по категориям)
|
||||||
|
├── Models/ # Модели данных
|
||||||
|
│ ├── Common/
|
||||||
|
│ ├── Album/
|
||||||
|
│ ├── Track/
|
||||||
|
│ └── ... (по типам)
|
||||||
|
└── Common/ # Вспомогательные компоненты
|
||||||
|
├── AuthStorage
|
||||||
|
├── Providers/
|
||||||
|
├── Encryptor
|
||||||
|
└── Ynison/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Главный класс:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class YandexMusicApi
|
||||||
|
{
|
||||||
|
// API Методы
|
||||||
|
public YAlbumAPI Album { get; }
|
||||||
|
public YArtistAPI Artist { get; }
|
||||||
|
public YTrackAPI Track { get; }
|
||||||
|
public YPlaylistAPI Playlist { get; }
|
||||||
|
public YRadioAPI Radio { get; }
|
||||||
|
public YSearchAPI Search { get; }
|
||||||
|
// ... и так далее для всех веток API
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**API Классы:**
|
||||||
|
|
||||||
|
Каждый API класс наследуется от `YCommonAPI`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public abstract class YCommonAPI
|
||||||
|
{
|
||||||
|
protected readonly YandexMusicApi api;
|
||||||
|
protected YCommonAPI(YandexMusicApi yandex) => api = yandex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пример конкретного API
|
||||||
|
public class YTrackAPI : YCommonAPI
|
||||||
|
{
|
||||||
|
public YTrackAPI(YandexMusicApi yandex) : base(yandex) { }
|
||||||
|
|
||||||
|
public async Task<YTrack?> GetTrackAsync(string trackId) { }
|
||||||
|
public async Task<IEnumerable<YTrack>> GetTracksAsync(IEnumerable<string> ids) { }
|
||||||
|
// ... остальные методы
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ответственность:**
|
||||||
|
|
||||||
|
- Построение HTTP запросов
|
||||||
|
- Вызов провайдера запросов
|
||||||
|
- Десериализация ответов
|
||||||
|
- Обработка ошибок API
|
||||||
|
|
||||||
|
### 4. Data Layer (Models)
|
||||||
|
|
||||||
|
**Назначение:** Модели данных, отражающие структуру API Яндекс Музыки.
|
||||||
|
|
||||||
|
```
|
||||||
|
Models/
|
||||||
|
├── Common/ # Общие модели
|
||||||
|
│ ├── YBaseModel # Базовая модель с контекстом
|
||||||
|
│ ├── YResponse<T> # Ответ от API
|
||||||
|
│ ├── YError # Ошибка
|
||||||
|
│ └── ... (остальные)
|
||||||
|
├── Album/ # Модели альбомов
|
||||||
|
│ ├── YAlbum
|
||||||
|
│ └── YAlbumMenuItem
|
||||||
|
├── Track/ # Модели треков
|
||||||
|
│ ├── YTrack
|
||||||
|
│ ├── YTrackSupplement
|
||||||
|
│ └── YTrackSimilar
|
||||||
|
├── Playlist/ # Модели плейлистов
|
||||||
|
│ ├── YPlaylist
|
||||||
|
│ ├── YPlaylistChange
|
||||||
|
│ └── YPlaylistMadeFor
|
||||||
|
└── ... (остальные модели)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Базовая модель:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public abstract class YBaseModel
|
||||||
|
{
|
||||||
|
[JsonIgnore]
|
||||||
|
public YExecutionContext? Context { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пример модели
|
||||||
|
public class YTrack : YBaseModel
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public string Id { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("title")]
|
||||||
|
public string Title { get; set; }
|
||||||
|
|
||||||
|
[JsonPropertyName("duration_ms")]
|
||||||
|
public int DurationMs { get; set; }
|
||||||
|
|
||||||
|
// ... остальные свойства
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ответственность:**
|
||||||
|
|
||||||
|
- Представление данных API
|
||||||
|
- System.Text.Json сериализация/десериализация
|
||||||
|
- Хранение контекста выполнения (опционально)
|
||||||
|
|
||||||
|
### 5. Infrastructure Layer
|
||||||
|
|
||||||
|
**Назначение:** Низкоуровневая инфраструктура для работы с HTTP и сериализацией.
|
||||||
|
|
||||||
|
```
|
||||||
|
Common/
|
||||||
|
├── Providers/ # Провайдеры запросов
|
||||||
|
│ ├── IRequestProvider # Интерфейс
|
||||||
|
│ ├── DefaultRequestProvider
|
||||||
|
│ ├── CommonRequestProvider
|
||||||
|
│ └── MockRequestProvider
|
||||||
|
├── AuthStorage # Управление авторизацией
|
||||||
|
├── Encryptor # Шифрование данных
|
||||||
|
├── DataDownloader # Загрузка файлов
|
||||||
|
└── Ynison/ # WebSocket
|
||||||
|
├── YnisonPlayer
|
||||||
|
└── YnisonWebSocket
|
||||||
|
```
|
||||||
|
|
||||||
|
**IRequestProvider:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public interface IRequestProvider
|
||||||
|
{
|
||||||
|
// Выполняет HTTP запрос
|
||||||
|
Task<HttpResponseMessage> GetWebResponseAsync(
|
||||||
|
HttpRequestMessage message,
|
||||||
|
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead);
|
||||||
|
|
||||||
|
// Преобразует ответ в модель
|
||||||
|
Task<T> GetDataFromResponseAsync<T>(
|
||||||
|
YandexMusicApi api,
|
||||||
|
HttpResponseMessage response);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Потоки данных
|
||||||
|
|
||||||
|
### Типичный поток запроса
|
||||||
|
|
||||||
|
```
|
||||||
|
1. User Action
|
||||||
|
↓
|
||||||
|
2. YandexMusicClient Method
|
||||||
|
↓
|
||||||
|
3. YandexMusicApi.SomeAPI.SomeMethodAsync()
|
||||||
|
↓
|
||||||
|
4. YRequestBuilder<T> builds HttpRequestMessage
|
||||||
|
↓
|
||||||
|
5. IRequestProvider.GetWebResponseAsync()
|
||||||
|
↓
|
||||||
|
6. HttpClient sends request
|
||||||
|
↓
|
||||||
|
7. Server responds with HttpResponseMessage
|
||||||
|
↓
|
||||||
|
8. IRequestProvider.GetDataFromResponseAsync<T>()
|
||||||
|
↓
|
||||||
|
9. System.Text.Json deserializes to Model<T>
|
||||||
|
↓
|
||||||
|
10. Model<T> returned to caller
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример: Получение трека
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 1. Пользователь вызывает метод
|
||||||
|
var track = await client.Api.Track.GetTrackAsync("trackId123");
|
||||||
|
|
||||||
|
// 2. YTrackAPI.GetTrackAsync() создаёт запрос
|
||||||
|
var builder = new YGetTracksBuilder(api);
|
||||||
|
var message = builder.Build("trackId123");
|
||||||
|
|
||||||
|
// 3. Запрос отправляется через провайдер
|
||||||
|
var response = await authStorage.Provider.GetWebResponseAsync(message);
|
||||||
|
|
||||||
|
// 4. Ответ преобразуется в модель
|
||||||
|
var track = await provider.GetDataFromResponseAsync<YTrack>(api, response);
|
||||||
|
|
||||||
|
// 5. Модель возвращается пользователю
|
||||||
|
return track;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Паттерны проектирования
|
||||||
|
|
||||||
|
### 1. Facade Pattern (YandexMusicApi)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Фасад предоставляет простой интерфейс к сложной подсистеме
|
||||||
|
public class YandexMusicApi
|
||||||
|
{
|
||||||
|
public YAlbumAPI Album { get; } // Скрывает сложность
|
||||||
|
public YArtistAPI Artist { get; } // инициализации
|
||||||
|
public YTrackAPI Track { get; } // каждого компонента
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Builder Pattern (Request Builders)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Построитель для конструирования запросов
|
||||||
|
public class YSearchBuilder : YRequestBuilder<YSearch>
|
||||||
|
{
|
||||||
|
private string _query;
|
||||||
|
private int _page = 0;
|
||||||
|
|
||||||
|
public YSearchBuilder Query(string q) { _query = q; return this; }
|
||||||
|
public YSearchBuilder Page(int p) { _page = p; return this; }
|
||||||
|
public override HttpRequestMessage Build() { /* ... */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Использование
|
||||||
|
var search = new YSearchBuilder()
|
||||||
|
.Query("Beatles")
|
||||||
|
.Page(1)
|
||||||
|
.Build();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Strategy Pattern (IRequestProvider)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Интерфейс позволяет выбрать стратегию обработки запросов
|
||||||
|
public interface IRequestProvider
|
||||||
|
{
|
||||||
|
Task<HttpResponseMessage> GetWebResponseAsync(HttpRequestMessage message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Разные реализации для разных сценариев
|
||||||
|
public class DefaultRequestProvider : IRequestProvider { }
|
||||||
|
public class MockRequestProvider : IRequestProvider { }
|
||||||
|
public class CustomRequestProvider : IRequestProvider { }
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Dependency Injection
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Зависимости передаются через конструктор
|
||||||
|
public class YTrackAPI : YCommonAPI
|
||||||
|
{
|
||||||
|
protected readonly YandexMusicApi api; // Injected
|
||||||
|
|
||||||
|
public YTrackAPI(YandexMusicApi yandex) => api = yandex;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Template Method Pattern
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Базовый класс определяет структуру
|
||||||
|
public abstract class YCommonAPI
|
||||||
|
{
|
||||||
|
protected readonly YandexMusicApi api;
|
||||||
|
protected YCommonAPI(YandexMusicApi yandex) => api = yandex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Подклассы реализуют специфичные методы
|
||||||
|
public class YAlbumAPI : YCommonAPI
|
||||||
|
{
|
||||||
|
// Реализация методов для альбомов
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🧩 Модульная структура
|
||||||
|
|
||||||
|
### Логическое разделение на модули
|
||||||
|
|
||||||
|
```
|
||||||
|
Core Module (YandexMusic.API)
|
||||||
|
├── Authentication Module
|
||||||
|
│ ├── AuthStorage
|
||||||
|
│ └── Authorization methods
|
||||||
|
├── Request Module
|
||||||
|
│ ├── IRequestProvider
|
||||||
|
│ ├── Request builders
|
||||||
|
│ └── HttpContext
|
||||||
|
├── Model Module
|
||||||
|
│ └── All data models
|
||||||
|
└── API Module
|
||||||
|
├── YAlbumAPI
|
||||||
|
├── YTrackAPI
|
||||||
|
├── YPlaylistAPI
|
||||||
|
└── ... (each API endpoint)
|
||||||
|
|
||||||
|
Client Module (YandexMusic)
|
||||||
|
├── YandexMusicClient
|
||||||
|
└── Client configuration
|
||||||
|
|
||||||
|
CLI Module (YaMusicCli)
|
||||||
|
├── Program.cs
|
||||||
|
└── CLI commands
|
||||||
|
```
|
||||||
|
|
||||||
|
### Зависимости между модулями
|
||||||
|
|
||||||
|
```
|
||||||
|
CLI Module
|
||||||
|
↓ depends on
|
||||||
|
Client Module
|
||||||
|
↓ depends on
|
||||||
|
Core Module
|
||||||
|
├── Authentication Module
|
||||||
|
├── Request Module
|
||||||
|
├── Model Module
|
||||||
|
└── API Module
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔌 Расширяемость
|
||||||
|
|
||||||
|
### Как добавить новый API метод
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// 1. Создайте builder в Requests/YourCategory/
|
||||||
|
public class YGetYourResourceBuilder : YRequestBuilder<YYourResource>
|
||||||
|
{
|
||||||
|
public override HttpRequestMessage Build()
|
||||||
|
{
|
||||||
|
// Реализуйте построение запроса
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Добавьте метод в соответствующий API класс
|
||||||
|
public class YYourResourceAPI : YCommonAPI
|
||||||
|
{
|
||||||
|
public async Task<YYourResource?> GetYourResourceAsync(string id)
|
||||||
|
{
|
||||||
|
var builder = new YGetYourResourceBuilder(api);
|
||||||
|
var request = builder.Build();
|
||||||
|
return await new YRequest<YYourResource?>(request, api, api.AuthStorage).GetResponseAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Используйте новый метод
|
||||||
|
var resource = await api.YourResource.GetYourResourceAsync("id123");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Как создать custom RequestProvider
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class MyCustomRequestProvider : IRequestProvider
|
||||||
|
{
|
||||||
|
private readonly AuthStorage _storage;
|
||||||
|
|
||||||
|
public MyCustomRequestProvider(AuthStorage storage)
|
||||||
|
{
|
||||||
|
_storage = storage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<HttpResponseMessage> GetWebResponseAsync(
|
||||||
|
HttpRequestMessage message,
|
||||||
|
HttpCompletionOption completionOption = HttpCompletionOption.ResponseContentRead)
|
||||||
|
{
|
||||||
|
// Ваша логика обработки запроса
|
||||||
|
using var handler = new HttpClientHandler();
|
||||||
|
using var client = new HttpClient(handler);
|
||||||
|
return await client.SendAsync(message, completionOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> GetDataFromResponseAsync<T>(
|
||||||
|
YandexMusicApi api,
|
||||||
|
HttpResponseMessage response)
|
||||||
|
{
|
||||||
|
// Ваша логика десериализации
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
return JsonSerializer.Deserialize<T>(content) ?? default!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Использование
|
||||||
|
var provider = new MyCustomRequestProvider(storage);
|
||||||
|
var storage = new AuthStorage(provider);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Как создать extension method
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public static class YPlaylistExtensions
|
||||||
|
{
|
||||||
|
public static async Task<IEnumerable<YTrack>> GetAllTracksAsync(
|
||||||
|
this YPlaylist playlist,
|
||||||
|
YandexMusicApi api)
|
||||||
|
{
|
||||||
|
// Расширение функциональности существующего класса
|
||||||
|
var allTracks = new List<YTrack>();
|
||||||
|
var currentPage = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var page = await api.Playlist.GetPlaylistAsync(
|
||||||
|
playlist.Uid,
|
||||||
|
page: currentPage);
|
||||||
|
|
||||||
|
if (page?.Tracks?.Count == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
allTracks.AddRange(page.Tracks ?? []);
|
||||||
|
currentPage++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return allTracks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Диаграмма взаимодействия компонентов
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────┐
|
||||||
|
│ CLI Layer │
|
||||||
|
│ (YaMusicCli) │
|
||||||
|
└────────┬─────────┘
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
┌──────────────────────────┐
|
||||||
|
│ Client Layer │
|
||||||
|
│ (YandexMusicClient) │
|
||||||
|
└────────┬─────────────────┘
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
┌────────────────────────────────────┐
|
||||||
|
│ API Layer │
|
||||||
|
│ ┌─────────────────────────────┐ │
|
||||||
|
│ │ YandexMusicApi (Facade) │ │
|
||||||
|
│ └──┬──────────────────────────┘ │
|
||||||
|
│ │ │
|
||||||
|
│ ┌──┴─────────┬─────────────────┐│
|
||||||
|
│ ↓ ↓ ↓│
|
||||||
|
│ YTrackAPI YPlaylistAPI YSearchAPI │
|
||||||
|
│ │ │ │ │
|
||||||
|
│ └──────┬─────┴────────┬────────┘ │
|
||||||
|
└─────────┼──────────────┼───────────┘
|
||||||
|
│ │
|
||||||
|
↓ ↓
|
||||||
|
┌──────────────────────────┐
|
||||||
|
│ IRequestProvider │
|
||||||
|
│ (Strategy Pattern) │
|
||||||
|
└──────┬───────────────────┘
|
||||||
|
│
|
||||||
|
┌──┴───┬──────────┐
|
||||||
|
↓ ↓ ↓
|
||||||
|
Default Common Mock
|
||||||
|
Provider Provider Provider
|
||||||
|
│ │ │
|
||||||
|
└──────┼────────┘
|
||||||
|
↓
|
||||||
|
┌──────────────────┐
|
||||||
|
│ HttpClient │
|
||||||
|
└──────┬───────────┘
|
||||||
|
│
|
||||||
|
↓
|
||||||
|
┌──────────────────┐
|
||||||
|
│ Network Layer │
|
||||||
|
└──────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 Безопасность архитектуры
|
||||||
|
|
||||||
|
1. **Инкапсуляция** - приватные поля и контролируемый доступ
|
||||||
|
2. **Валидация** - все входные параметры валидируются
|
||||||
|
3. **Null-safety** - полная поддержка nullable reference types
|
||||||
|
4. **Async-safe** - асинхронные операции без blocking
|
||||||
|
5. **Encryption** - встроенное шифрование для чувствительных данных
|
||||||
410
CONTRIBUTING.md
Normal file
410
CONTRIBUTING.md
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
# 🤝 Руководство для участников
|
||||||
|
|
||||||
|
Спасибо за интерес к проекту YandexMusic! Этот документ содержит рекомендации для тех, кто хочет внести свой вклад в развитие проекта.
|
||||||
|
|
||||||
|
## 📋 Содержание
|
||||||
|
|
||||||
|
- [Кодекс поведения](#кодекс-поведения)
|
||||||
|
- [Как начать](#как-начать)
|
||||||
|
- [Процесс разработки](#процесс-разработки)
|
||||||
|
- [Стандарты кода](#стандарты-кода)
|
||||||
|
- [Commit сообщения](#commit-сообщения)
|
||||||
|
- [Pull Requests](#pull-requests)
|
||||||
|
- [Отчёты об ошибках](#отчёты-об-ошибках)
|
||||||
|
- [Предложения по улучшениям](#предложения-по-улучшениям)
|
||||||
|
|
||||||
|
## 🤐 Кодекс поведения
|
||||||
|
|
||||||
|
- Будьте уважительны к другим участникам
|
||||||
|
- Не допускайте дискриминацию, оскорбления и враждебного поведения
|
||||||
|
- Критикуйте идеи, а не людей
|
||||||
|
- Решайте конфликты конструктивно
|
||||||
|
|
||||||
|
## 🚀 Как начать
|
||||||
|
|
||||||
|
### 1. Подготовка окружения
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Клонируем репозиторий
|
||||||
|
git clone https://git.frigat.duckdns.org/FrigaT/YandexMusic.git
|
||||||
|
cd YandexMusic
|
||||||
|
|
||||||
|
# Создаём ветку для своей работы
|
||||||
|
git checkout -b feature/my-feature
|
||||||
|
|
||||||
|
# Восстанавливаем зависимости
|
||||||
|
dotnet restore
|
||||||
|
|
||||||
|
# Собираем проект
|
||||||
|
dotnet build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Установка инструментов
|
||||||
|
|
||||||
|
- **Visual Studio 2026 Enterprise** (рекомендуется)
|
||||||
|
- **Visual Studio Code** + C# extension (альтернатива)
|
||||||
|
- **.NET 10 SDK**
|
||||||
|
|
||||||
|
### 3. Запуск тестов
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔄 Процесс разработки
|
||||||
|
|
||||||
|
### Workflow
|
||||||
|
|
||||||
|
```
|
||||||
|
1. Выберите issue или создайте новый
|
||||||
|
2. Создайте feature-ветку
|
||||||
|
3. Внесите изменения
|
||||||
|
4. Напишите/обновите тесты
|
||||||
|
5. Убедитесь что код собирается и тесты проходят
|
||||||
|
6. Создайте Pull Request
|
||||||
|
7. Ждите review
|
||||||
|
8. Исправьте замечания если нужно
|
||||||
|
9. Merge в master
|
||||||
|
```
|
||||||
|
|
||||||
|
### Версионирование веток
|
||||||
|
|
||||||
|
```
|
||||||
|
master → Production версия
|
||||||
|
├── feature/* → Новые функции
|
||||||
|
├── bugfix/* → Исправления ошибок
|
||||||
|
├── refactor/* → Рефакторинг кода
|
||||||
|
└── docs/* → Обновления документации
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📐 Стандарты кода
|
||||||
|
|
||||||
|
### Общие правила
|
||||||
|
|
||||||
|
- **Язык:** C# 12
|
||||||
|
- **Платформа:** .NET 10
|
||||||
|
- **Стиль:** Microsoft C# Coding Conventions
|
||||||
|
- **Документация:** Все публичные члены должны иметь XML документацию на русском
|
||||||
|
|
||||||
|
### Пример документированного кода
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
namespace YandexMusic.API.API;
|
||||||
|
|
||||||
|
/// <summary>API для работы с альбомами.</summary>
|
||||||
|
public class YAlbumAPI : YCommonAPI
|
||||||
|
{
|
||||||
|
/// <summary>Инициализирует новый экземпляр.</summary>
|
||||||
|
/// <param name="yandex">Экземпляр основного API.</param>
|
||||||
|
public YAlbumAPI(YandexMusicApi yandex) : base(yandex) { }
|
||||||
|
|
||||||
|
/// <summary>Получает информацию об альбоме по идентификатору.</summary>
|
||||||
|
/// <param name="albumId">Идентификатор альбома</param>
|
||||||
|
/// <returns>Модель альбома или null если не найден</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">Если albumId null</exception>
|
||||||
|
public async Task<YAlbum?> GetAlbumAsync(string albumId)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(albumId);
|
||||||
|
|
||||||
|
// Реализация
|
||||||
|
return await Task.FromResult<YAlbum?>(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Правила именования
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Классы: PascalCase
|
||||||
|
public class YandexMusicApi { }
|
||||||
|
|
||||||
|
// Методы: PascalCase с Async суффиксом для асинхронных
|
||||||
|
public async Task<YTrack?> GetTrackAsync(string trackId) { }
|
||||||
|
|
||||||
|
// Свойства: PascalCase
|
||||||
|
public YandexMusicApi Api { get; }
|
||||||
|
|
||||||
|
// Приватные поля: _camelCase
|
||||||
|
private readonly HttpClient _httpClient;
|
||||||
|
|
||||||
|
// Параметры: camelCase
|
||||||
|
public void DoSomething(string userName, int userId) { }
|
||||||
|
|
||||||
|
// Локальные переменные: camelCase
|
||||||
|
var trackList = new List<YTrack>();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Style
|
||||||
|
|
||||||
|
Используйте `.editorconfig` для автоматического форматирования:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Отступы - 4 пробела
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
# Новая строка для фигурных скобок
|
||||||
|
csharp_new_line_before_open_brace = all
|
||||||
|
|
||||||
|
# Используйте var где возможно
|
||||||
|
csharp_style_var_for_built_in_types = true
|
||||||
|
|
||||||
|
# Null-forgiving operator с осторожностью
|
||||||
|
csharp_style_null_forgiving_operator = false
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nullable Reference Types
|
||||||
|
|
||||||
|
Всегда включен `<Nullable>enable</Nullable>`. Правила:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ Хорошо - явно указано может быть null
|
||||||
|
public string? GetUserName() { }
|
||||||
|
|
||||||
|
// ✅ Хорошо - явно не null
|
||||||
|
public string GetTitle() => "Title";
|
||||||
|
|
||||||
|
// ❌ Плохо - неявная nullable ссылка
|
||||||
|
public object GetSomething() { }
|
||||||
|
|
||||||
|
// ✅ Хорошо - используйте null-coalescing
|
||||||
|
var result = value ?? defaultValue;
|
||||||
|
|
||||||
|
// ✅ Хорошо - используйте null-conditional
|
||||||
|
var count = list?.Count ?? 0;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Асинхронное программирование
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ Хорошо - async/await
|
||||||
|
public async Task<YTrack?> GetTrackAsync(string id)
|
||||||
|
{
|
||||||
|
return await api.Track.GetTrackAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Плохо - Task.Result блокирует поток
|
||||||
|
public YTrack? GetTrack(string id)
|
||||||
|
{
|
||||||
|
return api.Track.GetTrackAsync(id).Result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Хорошо - ConfigureAwait(false) в библиотеках
|
||||||
|
public async Task<YTrack?> GetTrackAsync(string id)
|
||||||
|
{
|
||||||
|
return await api.Track.GetTrackAsync(id).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Обработка ошибок
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// ✅ Хорошо
|
||||||
|
public async Task<YTrack?> GetTrackAsync(string trackId)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(trackId);
|
||||||
|
ArgumentException.ThrowIfNullOrWhiteSpace(trackId);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _provider.GetTrackAsync(trackId);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Ошибка при получении трека");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ❌ Плохо - молча игнорируем ошибки
|
||||||
|
public async Task<YTrack?> GetTrackAsync(string trackId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _provider.GetTrackAsync(trackId);
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Структура файла
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace YandexMusic.API.API;
|
||||||
|
|
||||||
|
/// <summary>API для работы с треками.</summary>
|
||||||
|
public class YTrackAPI : YCommonAPI
|
||||||
|
{
|
||||||
|
/// <summary>Инициализирует новый экземпляр.</summary>
|
||||||
|
public YTrackAPI(YandexMusicApi yandex) : base(yandex) { }
|
||||||
|
|
||||||
|
/// <summary>Получает трек.</summary>
|
||||||
|
public async Task<YTrack?> GetTrackAsync(string trackId)
|
||||||
|
{
|
||||||
|
// реализация
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Получает несколько треков.</summary>
|
||||||
|
public async Task<IEnumerable<YTrack>> GetTracksAsync(IEnumerable<string> trackIds)
|
||||||
|
{
|
||||||
|
// реализация
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📝 Commit сообщения
|
||||||
|
|
||||||
|
### Формат
|
||||||
|
|
||||||
|
```
|
||||||
|
<type>(<scope>): <subject>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type
|
||||||
|
|
||||||
|
- `feat:` - новая функция
|
||||||
|
- `fix:` - исправление ошибки
|
||||||
|
- `docs:` - обновление документации
|
||||||
|
- `style:` - форматирование кода (не влияет на функциональность)
|
||||||
|
- `refactor:` - переписывание кода (не меняет функциональность)
|
||||||
|
- `perf:` - улучшение производительности
|
||||||
|
- `test:` - добавление или обновление тестов
|
||||||
|
- `ci:` - изменения в CI/CD
|
||||||
|
- `chore:` - обновление зависимостей, версии и т.д.
|
||||||
|
|
||||||
|
### Примеры
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "feat(track-api): add support for track recommendations"
|
||||||
|
|
||||||
|
git commit -m "fix(auth): handle refresh token expiration correctly"
|
||||||
|
|
||||||
|
git commit -m "docs(readme): update installation instructions"
|
||||||
|
|
||||||
|
git commit -m "refactor(models): simplify YTrack class structure"
|
||||||
|
|
||||||
|
git commit -m "test(playlist): add tests for playlist operations"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔀 Pull Requests
|
||||||
|
|
||||||
|
### Перед созданием PR
|
||||||
|
|
||||||
|
- [ ] Код собирается без ошибок: `dotnet build`
|
||||||
|
- [ ] Все тесты проходят: `dotnet test`
|
||||||
|
- [ ] Код отформатирован согласно стилю
|
||||||
|
- [ ] Добавлена XML документация для всех публичных членов
|
||||||
|
- [ ] Обновлена документация если нужно
|
||||||
|
|
||||||
|
### Шаблон PR
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Описание
|
||||||
|
Краткое описание внесённых изменений.
|
||||||
|
|
||||||
|
## Тип изменения
|
||||||
|
- [ ] Новая функция (non-breaking change)
|
||||||
|
- [ ] Исправление ошибки (non-breaking change)
|
||||||
|
- [ ] Breaking change (поясните почему)
|
||||||
|
- [ ] Обновление документации
|
||||||
|
|
||||||
|
## Как это было протестировано?
|
||||||
|
Опишите шаги для тестирования.
|
||||||
|
|
||||||
|
## Связанные Issues
|
||||||
|
Closes #issue_number
|
||||||
|
|
||||||
|
## Чеклист
|
||||||
|
- [ ] Код следует стилю проекта
|
||||||
|
- [ ] Добавлена документация
|
||||||
|
- [ ] Нет новых warnings
|
||||||
|
- [ ] Тесты проходят
|
||||||
|
- [ ] Изменения совместимы с существующим кодом
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🐛 Отчёты об ошибках
|
||||||
|
|
||||||
|
### Шаблон Issue
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Описание ошибки
|
||||||
|
Четкое и краткое описание проблемы.
|
||||||
|
|
||||||
|
## Шаги воспроизведения
|
||||||
|
1. Сделайте...
|
||||||
|
2. Затем...
|
||||||
|
3. Ошибка воспроизведится
|
||||||
|
|
||||||
|
## Ожидаемое поведение
|
||||||
|
Что должно было происходить.
|
||||||
|
|
||||||
|
## Фактическое поведение
|
||||||
|
Что происходит на самом деле.
|
||||||
|
|
||||||
|
## Окружение
|
||||||
|
- OS: Windows 11
|
||||||
|
- .NET Version: 10.0
|
||||||
|
- Visual Studio: 2026 Enterprise
|
||||||
|
- YandexMusic Version: 0.0.1
|
||||||
|
|
||||||
|
## Дополнительный контекст
|
||||||
|
Скриптстек, логи, код примера.
|
||||||
|
```
|
||||||
|
|
||||||
|
## 💡 Предложения по улучшениям
|
||||||
|
|
||||||
|
### Шаблон Feature Request
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Описание
|
||||||
|
Четкое описание желаемой функции.
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
Как бы выглядел код с использованием этой функции?
|
||||||
|
|
||||||
|
## Альтернативы
|
||||||
|
Есть ли другие способы решить эту проблему?
|
||||||
|
|
||||||
|
## Дополнительный контекст
|
||||||
|
Почему это нужно добавить?
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎯 Области для внесения вклада
|
||||||
|
|
||||||
|
### High Priority
|
||||||
|
|
||||||
|
- [ ] Документация API методов
|
||||||
|
- [ ] Unit тесты для API классов
|
||||||
|
- [ ] Обработка ошибок
|
||||||
|
- [ ] Оптимизация производительности
|
||||||
|
|
||||||
|
### Medium Priority
|
||||||
|
|
||||||
|
- [ ] Примеры использования
|
||||||
|
- [ ] Интеграционные тесты
|
||||||
|
- [ ] Улучшение логирования
|
||||||
|
- [ ] Расширение функциональности
|
||||||
|
|
||||||
|
### Low Priority
|
||||||
|
|
||||||
|
- [ ] Рефакторинг кода
|
||||||
|
- [ ] Улучшение читаемости
|
||||||
|
- [ ] Документация
|
||||||
|
|
||||||
|
## 📞 Контакты
|
||||||
|
|
||||||
|
Вопросы? Свяжитесь с разработчиком:
|
||||||
|
|
||||||
|
- **Issues:** [https://git.frigat.duckdns.org/FrigaT/YandexMusic/issues]
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Спасибо за то, что делаете YandexMusic лучше! 🚀
|
||||||
446
FAQ.md
Normal file
446
FAQ.md
Normal file
@@ -0,0 +1,446 @@
|
|||||||
|
# ❓ FAQ - Часто задаваемые вопросы
|
||||||
|
|
||||||
|
Ответы на самые частые вопросы по использованию YandexMusic.
|
||||||
|
|
||||||
|
## 📋 Содержание
|
||||||
|
|
||||||
|
- [Общие вопросы](#общие-вопросы)
|
||||||
|
- [Установка и настройка](#установка-и-настройка)
|
||||||
|
- [Использование](#использование)
|
||||||
|
- [Авторизация](#авторизация)
|
||||||
|
- [Ошибки и проблемы](#ошибки-и-проблемы)
|
||||||
|
- [Производительность](#производительность)
|
||||||
|
- [Разработка](#разработка)
|
||||||
|
|
||||||
|
## 📖 Общие вопросы
|
||||||
|
|
||||||
|
### Q: Что такое YandexMusic?
|
||||||
|
|
||||||
|
**A:** YandexMusic — это .NET 10 библиотека для работы с неофициальным API Яндекс Музыки. Она позволяет искать музыку, управлять плейлистами, получать информацию об альбомах и исполнителях, а также работать с WebSocket через протокол Ynison.
|
||||||
|
|
||||||
|
### Q: Это официальная библиотека?
|
||||||
|
|
||||||
|
**A:** Нет, это неофициальная библиотека. Используйте её на свой риск и соблюдайте Terms of Service Яндекс Музыки.
|
||||||
|
|
||||||
|
### Q: На каких платформах работает?
|
||||||
|
|
||||||
|
**A:** На любых платформах, поддерживающих .NET 10:
|
||||||
|
- Windows (10/11)
|
||||||
|
- Linux (Ubuntu, Debian, Red Hat, etc.)
|
||||||
|
- macOS (Intel & Apple Silicon)
|
||||||
|
|
||||||
|
### Q: Нужна ли авторизация?
|
||||||
|
|
||||||
|
**A:** Нет, многие операции работают без авторизации (поиск, получение информации о треках). Но для некоторых операций (работа с лайками, плейлистами) требуется авторизация.
|
||||||
|
|
||||||
|
### Q: Где я могу сообщить об ошибке?
|
||||||
|
|
||||||
|
**A:** Создайте issue на GitHub:
|
||||||
|
- https://git.frigat.duckdns.org/FrigaT/YandexMusic/issues
|
||||||
|
|
||||||
|
## 🚀 Установка и настройка
|
||||||
|
|
||||||
|
### Q: Какие требования?
|
||||||
|
|
||||||
|
**A:**
|
||||||
|
- .NET 10 SDK или выше
|
||||||
|
- C# 12 совместимый компилятор
|
||||||
|
- Интернет соединение
|
||||||
|
|
||||||
|
### Q: Как установить библиотеку?
|
||||||
|
|
||||||
|
**A:** Несколько способов:
|
||||||
|
|
||||||
|
1. **Через NuGet:**
|
||||||
|
```bash
|
||||||
|
dotnet add package YandexMusic
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Из GitHub:**
|
||||||
|
```bash
|
||||||
|
git clone https://git.frigat.duckdns.org/FrigaT/YandexMusic.git
|
||||||
|
cd YandexMusic
|
||||||
|
dotnet build
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Добавить ссылку на проект:**
|
||||||
|
```bash
|
||||||
|
dotnet add reference ../YandexMusic/YandexMusic.csproj
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Как обновить библиотеку?
|
||||||
|
|
||||||
|
**A:** Через NuGet:
|
||||||
|
```bash
|
||||||
|
dotnet package update YandexMusic
|
||||||
|
```
|
||||||
|
|
||||||
|
Или если вы клонировали репозиторий:
|
||||||
|
```bash
|
||||||
|
git pull origin master
|
||||||
|
dotnet build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Какие зависимости нужны?
|
||||||
|
|
||||||
|
**A:** Только встроенные в .NET 10:
|
||||||
|
- System.Net.Http
|
||||||
|
- System.Text.Json
|
||||||
|
- System.Net.WebSockets
|
||||||
|
|
||||||
|
Нет дополнительных NuGet пакетов!
|
||||||
|
|
||||||
|
## 💻 Использование
|
||||||
|
|
||||||
|
### Q: Как начать использовать?
|
||||||
|
|
||||||
|
**A:** Самый простой способ:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using YandexMusic;
|
||||||
|
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
var results = await client.Api.Search.SearchAsync("Beatles");
|
||||||
|
```
|
||||||
|
|
||||||
|
Подробнее в [QUICKSTART.md](QUICKSTART.md).
|
||||||
|
|
||||||
|
### Q: Как получить информацию о треке?
|
||||||
|
|
||||||
|
**A:**
|
||||||
|
```csharp
|
||||||
|
var track = await client.Api.Track.GetTrackAsync("trackId");
|
||||||
|
Console.WriteLine($"{track?.Title} - {track?.Artists?.FirstOrDefault()?.Title}");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Как найти ID трека/альбома/плейлиста?
|
||||||
|
|
||||||
|
**A:**
|
||||||
|
- В URL страницы на music.yandex.ru
|
||||||
|
- Через API при поиске
|
||||||
|
- На официальном сайте в адресной строке
|
||||||
|
|
||||||
|
Примеры:
|
||||||
|
- Трек: `https://music.yandex.ru/album/123/track/456` → ID: `456`
|
||||||
|
- Альбом: `https://music.yandex.ru/album/123` → ID: `123`
|
||||||
|
- Плейлист: `https://music.yandex.ru/playlist/123` → ID: `123`
|
||||||
|
|
||||||
|
### Q: Почему я получаю null?
|
||||||
|
|
||||||
|
**A:** Несколько причин:
|
||||||
|
|
||||||
|
1. Неправильный ID
|
||||||
|
2. Ресурс был удален
|
||||||
|
3. Нет доступа (требуется авторизация)
|
||||||
|
4. Сервер вернул ошибку
|
||||||
|
|
||||||
|
Всегда проверяйте результат:
|
||||||
|
```csharp
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(id);
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Трек не найден или нет доступа");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Как работать с пагинацией?
|
||||||
|
|
||||||
|
**A:** Используйте параметр `page`:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var page1 = await client.Api.Search.SearchAsync("query", page: 0);
|
||||||
|
var page2 = await client.Api.Search.SearchAsync("query", page: 1);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Как получить большое количество данных?
|
||||||
|
|
||||||
|
**A:** Используйте цикл с пагинацией:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var allResults = new List<YTrack>();
|
||||||
|
int page = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var search = await client.Api.Search.SearchAsync("query", page: page);
|
||||||
|
if (search?.Tracks?.Results?.Count == 0) break;
|
||||||
|
|
||||||
|
allResults.AddRange(search.Tracks.Results ?? []);
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 Авторизация
|
||||||
|
|
||||||
|
### Q: Как авторизироваться?
|
||||||
|
|
||||||
|
**A:** Два способа:
|
||||||
|
|
||||||
|
1. **Через логин и пароль:**
|
||||||
|
```csharp
|
||||||
|
await client.AuthorizeAsync("email@gmail.com", "password");
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Через токен:**
|
||||||
|
```csharp
|
||||||
|
await client.AuthorizeByTokenAsync("your-oauth-token");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Где получить OAuth токен?
|
||||||
|
|
||||||
|
**A:** Вы можете:
|
||||||
|
1. Авторизироваться через логин/пароль (библиотека получит токен автоматически)
|
||||||
|
2. Получить токен через официальное приложение Яндекса
|
||||||
|
3. Использовать токен из DevTools браузера
|
||||||
|
|
||||||
|
### Q: Как проверить авторизирован ли я?
|
||||||
|
|
||||||
|
**A:**
|
||||||
|
```csharp
|
||||||
|
if (client.IsAuthorized)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Авторизирован: {client.Account.User?.DisplayName}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("Требуется авторизация");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Сохраняется ли авторизация?
|
||||||
|
|
||||||
|
**A:** По умолчанию нет. Вы должны авторизироваться при каждом запуске.
|
||||||
|
|
||||||
|
Если хотите сохранить токен:
|
||||||
|
```csharp
|
||||||
|
// После авторизации
|
||||||
|
string token = client.AuthStorage.Token;
|
||||||
|
|
||||||
|
// Сохранить в файл, БД и т.д.
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// При следующем запуске
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
await client.AuthorizeByTokenAsync(savedToken);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Безопасно ли хранить пароль?
|
||||||
|
|
||||||
|
**A:** **Нет!** Никогда не сохраняйте пароль в открытом виде. Лучше:
|
||||||
|
1. Используйте OAuth токены
|
||||||
|
2. Сохраняйте токены в безопасном хранилище
|
||||||
|
3. Используйте переменные окружения
|
||||||
|
|
||||||
|
## ⚠️ Ошибки и проблемы
|
||||||
|
|
||||||
|
### Q: Получаю `HttpRequestException`
|
||||||
|
|
||||||
|
**A:** Проблемы с сетью или сервером:
|
||||||
|
```csharp
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(id);
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Сетевая ошибка: {ex.Message}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Проверьте:
|
||||||
|
- Интернет соединение
|
||||||
|
- Доступность сервера Яндекса
|
||||||
|
- Firewall/прокси настройки
|
||||||
|
|
||||||
|
### Q: Получаю `JsonException`
|
||||||
|
|
||||||
|
**A:** Проблема с десериализацией JSON. Обновите библиотеку или сообщите об issue.
|
||||||
|
|
||||||
|
### Q: Получаю 401 Unauthorized
|
||||||
|
|
||||||
|
**A:** Проблема с авторизацией:
|
||||||
|
```csharp
|
||||||
|
// Проверьте токен
|
||||||
|
if (!client.IsAuthorized)
|
||||||
|
{
|
||||||
|
await client.AuthorizeAsync("email", "password");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Или обновите токен
|
||||||
|
await client.AuthorizeByTokenAsync(newToken);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Получаю 429 Too Many Requests
|
||||||
|
|
||||||
|
**A:** Вы отправляете слишком много запросов. Добавьте задержку:
|
||||||
|
```csharp
|
||||||
|
for (int i = 0; i < ids.Count; i++)
|
||||||
|
{
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(ids[i]);
|
||||||
|
if (i < ids.Count - 1) await Task.Delay(100); // 100ms задержка
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Результаты пусты/null
|
||||||
|
|
||||||
|
**A:** Несколько причин:
|
||||||
|
|
||||||
|
1. **Неправильный ID:**
|
||||||
|
```csharp
|
||||||
|
var track = await client.Api.Track.GetTrackAsync("invalid-id");
|
||||||
|
// Будет null если ID неверный
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Нет результатов поиска:**
|
||||||
|
```csharp
|
||||||
|
var results = await client.Api.Search.SearchAsync("абракадабра");
|
||||||
|
// results?.Tracks?.Results будет пусто
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Требуется авторизация:**
|
||||||
|
```csharp
|
||||||
|
var library = await client.Api.Library.GetLibraryAsync();
|
||||||
|
// Вернёт null если не авторизирован
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Почему медленно работает?
|
||||||
|
|
||||||
|
**A:** Несколько причин:
|
||||||
|
1. Медленное интернет соединение
|
||||||
|
2. Сервер Яндекса перегружен
|
||||||
|
3. Получаете большое количество данных
|
||||||
|
4. Блокирующие операции вместо async
|
||||||
|
|
||||||
|
Используйте асинхронные операции:
|
||||||
|
```csharp
|
||||||
|
// ✅ Хорошо
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(id);
|
||||||
|
|
||||||
|
// ❌ Плохо
|
||||||
|
var track = client.Api.Track.GetTrackAsync(id).Result;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 Производительность
|
||||||
|
|
||||||
|
### Q: Как оптимизировать производительность?
|
||||||
|
|
||||||
|
**A:** Несколько советов:
|
||||||
|
|
||||||
|
1. **Используйте пакетные операции:**
|
||||||
|
```csharp
|
||||||
|
// ✅ Хорошо - один запрос
|
||||||
|
var tracks = await client.Api.Track.GetTracksAsync(["id1", "id2", "id3"]);
|
||||||
|
|
||||||
|
// ❌ Плохо - три запроса
|
||||||
|
foreach (var id in ids)
|
||||||
|
{
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(id);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Кешируйте результаты:**
|
||||||
|
```csharp
|
||||||
|
private Dictionary<string, YTrack?> cache = new();
|
||||||
|
|
||||||
|
public async Task<YTrack?> GetCached(string id)
|
||||||
|
{
|
||||||
|
if (cache.TryGetValue(id, out var cached)) return cached;
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(id);
|
||||||
|
cache[id] = track;
|
||||||
|
return track;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Запускайте запросы параллельно:**
|
||||||
|
```csharp
|
||||||
|
var (albums, artists) = await (
|
||||||
|
Task.Run(() => client.Api.Album.GetAlbumsAsync(ids)),
|
||||||
|
Task.Run(() => client.Api.Artist.GetArtistsAsync(ids))
|
||||||
|
).AsAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Добавляйте задержку между запросами:**
|
||||||
|
```csharp
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(id);
|
||||||
|
await Task.Delay(100); // 100ms между запросами
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Есть ли какие-то лимиты?
|
||||||
|
|
||||||
|
**A:** Яндекс Музыка имеет лимиты:
|
||||||
|
- Количество запросов в секунду
|
||||||
|
- Размер результатов поиска
|
||||||
|
- Размер файлов для загрузки
|
||||||
|
|
||||||
|
Уважайте эти лимиты и не создавайте нагрузку на сервер.
|
||||||
|
|
||||||
|
## 🔧 Разработка
|
||||||
|
|
||||||
|
### Q: Как стать разработчиком?
|
||||||
|
|
||||||
|
**A:** Читайте [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
### Q: Как создать pull request?
|
||||||
|
|
||||||
|
**A:**
|
||||||
|
1. Форкните репозиторий
|
||||||
|
2. Создайте ветку: `git checkout -b feature/my-feature`
|
||||||
|
3. Сделайте изменения
|
||||||
|
4. Коммитьте: `git commit -m "feat: описание"`
|
||||||
|
5. Пушьте: `git push origin feature/my-feature`
|
||||||
|
6. Создайте Pull Request
|
||||||
|
|
||||||
|
### Q: Как собрать проект?
|
||||||
|
|
||||||
|
**A:**
|
||||||
|
```bash
|
||||||
|
dotnet build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Как запустить тесты?
|
||||||
|
|
||||||
|
**A:**
|
||||||
|
```bash
|
||||||
|
dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
### Q: Есть ли стиль кода?
|
||||||
|
|
||||||
|
**A:** Да, используется Microsoft C# Coding Conventions. Смотрите [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
### Q: Как добавить новый API метод?
|
||||||
|
|
||||||
|
**A:** Прочитайте [ARCHITECTURE.md](ARCHITECTURE.md) раздел "Как добавить новый API метод".
|
||||||
|
|
||||||
|
### Q: Как создать тест?
|
||||||
|
|
||||||
|
**A:** Создайте файл в папке Tests:
|
||||||
|
```csharp
|
||||||
|
using Xunit;
|
||||||
|
using YandexMusic.API;
|
||||||
|
|
||||||
|
public class YTrackAPITests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task GetTrackAsync_WithValidId_ReturnsTrack()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var api = new YandexMusicApi();
|
||||||
|
string trackId = "valid-id";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await api.Track.GetTrackAsync(trackId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.NotNull(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🆘 Не нашли ответ?
|
||||||
|
|
||||||
|
Создайте вопрос через GitHub Issues:
|
||||||
|
https://git.frigat.duckdns.org/FrigaT/YandexMusic/issues
|
||||||
536
QUICKSTART.md
Normal file
536
QUICKSTART.md
Normal file
@@ -0,0 +1,536 @@
|
|||||||
|
# ⚡ Быстрый старт - YandexMusic
|
||||||
|
|
||||||
|
5-минутное введение в использование YandexMusic для начинающих.
|
||||||
|
|
||||||
|
## 📋 Содержание
|
||||||
|
|
||||||
|
- [Установка](#установка)
|
||||||
|
- [Ваш первый запрос](#ваш-первый-запрос)
|
||||||
|
- [Авторизация](#авторизация)
|
||||||
|
- [Основные операции](#основные-операции)
|
||||||
|
- [WebSocket (Ynison)](#websocket-ynison)
|
||||||
|
- [Обработка ошибок](#обработка-ошибок)
|
||||||
|
- [Дальнейшее обучение](#дальнейшее-обучение)
|
||||||
|
|
||||||
|
## 📦 Установка
|
||||||
|
|
||||||
|
### 1. Требования
|
||||||
|
|
||||||
|
- .NET 10 SDK
|
||||||
|
- C# 12 совместимый компилятор
|
||||||
|
|
||||||
|
### 2. Создание проекта
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Создаём консольное приложение
|
||||||
|
dotnet new console -n MyMusicApp
|
||||||
|
cd MyMusicApp
|
||||||
|
|
||||||
|
# Добавляем ссылку на YandexMusic (если опубликовано в NuGet)
|
||||||
|
dotnet add package YandexMusic
|
||||||
|
|
||||||
|
# ИЛИ добавляем ссылку на проект локально
|
||||||
|
dotnet add reference ../YandexMusic/YandexMusic.csproj
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Восстановление зависимостей
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet restore
|
||||||
|
```
|
||||||
|
|
||||||
|
## ✨ Ваш первый запрос
|
||||||
|
|
||||||
|
### Простой поиск (без авторизации)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using YandexMusic;
|
||||||
|
using YandexMusic.API;
|
||||||
|
|
||||||
|
// Создаём клиент
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
// Ищем музыку
|
||||||
|
var results = await client.Api.Search.SearchAsync("Ленинград");
|
||||||
|
|
||||||
|
// Выводим первые результаты
|
||||||
|
if (results?.Tracks?.Results != null)
|
||||||
|
{
|
||||||
|
foreach (var track in results.Tracks.Results.Take(5))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"🎵 {track.Title} - {string.Join(", ", track.Artists?.Select(a => a.Title) ?? [])}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Получение информации о треке
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var track = await client.Api.Track.GetTrackAsync("trackId");
|
||||||
|
|
||||||
|
if (track != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Трек: {track.Title}");
|
||||||
|
Console.WriteLine($"Исполнитель: {track.Artists?.FirstOrDefault()?.Title}");
|
||||||
|
Console.WriteLine($"Альбом: {track.Album?.Title}");
|
||||||
|
Console.WriteLine($"Длительность: {track.DurationMs / 1000} сек");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔐 Авторизация
|
||||||
|
|
||||||
|
### Способ 1: Через логин и пароль
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Авторизуемся
|
||||||
|
await client.AuthorizeAsync("your-email@gmail.com", "your-password");
|
||||||
|
|
||||||
|
Console.WriteLine($"✅ Авторизирован: {client.Account.User?.DisplayName}");
|
||||||
|
Console.WriteLine($"💰 Подписка: {client.Account.Plus?.HasPlus}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Ошибка: {ex.Message}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Способ 2: Через токен
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
// Если у вас уже есть токен
|
||||||
|
await client.AuthorizeByTokenAsync("your-oauth-token");
|
||||||
|
|
||||||
|
Console.WriteLine($"✅ Авторизирован через токен");
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Основные операции
|
||||||
|
|
||||||
|
### 1. Поиск
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Поиск треков
|
||||||
|
var search = await client.Api.Search.SearchAsync("Beatles");
|
||||||
|
var tracks = search?.Tracks?.Results ?? [];
|
||||||
|
foreach (var t in tracks.Take(10))
|
||||||
|
{
|
||||||
|
Console.WriteLine($" {t.Title}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Поиск альбомов
|
||||||
|
var albumSearch = await client.Api.Search.SearchAsync("Abbey Road", type: "album");
|
||||||
|
|
||||||
|
// Поиск исполнителей
|
||||||
|
var artistSearch = await client.Api.Search.SearchAsync("John Lennon", type: "artist");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Альбомы
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение альбома
|
||||||
|
var album = await client.Api.Album.GetAlbumAsync("albumId");
|
||||||
|
Console.WriteLine($"Альбом: {album?.Title} ({album?.Year})");
|
||||||
|
Console.WriteLine($"Жанр: {album?.Genre}");
|
||||||
|
|
||||||
|
// Список треков в альбоме
|
||||||
|
foreach (var track in album?.Tracks ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($" {track.Position}. {track.Title}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение нескольких альбомов
|
||||||
|
var albums = await client.Api.Album.GetAlbumsAsync(["albumId1", "albumId2"]);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Исполнители
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение исполнителя
|
||||||
|
var artist = await client.Api.Artist.GetArtistAsync("artistId");
|
||||||
|
Console.WriteLine($"Исполнитель: {artist?.Title}");
|
||||||
|
Console.WriteLine($"Жанр: {artist?.Genre}");
|
||||||
|
|
||||||
|
// Фото исполнителя
|
||||||
|
if (artist?.Cover?.Pic != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Фото: {artist.Cover.Pic}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Информация об исполнителе
|
||||||
|
Console.WriteLine($"Альбомов: {artist?.Albums?.Count}");
|
||||||
|
Console.WriteLine($"Треков: {artist?.Tracks?.Count}");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Плейлисты
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение плейлиста
|
||||||
|
var playlist = await client.Api.Playlist.GetPlaylistAsync("playlistId");
|
||||||
|
Console.WriteLine($"Плейлист: {playlist?.Title}");
|
||||||
|
Console.WriteLine($"Описание: {playlist?.Description}");
|
||||||
|
Console.WriteLine($"Треков: {playlist?.Tracks?.Count}");
|
||||||
|
|
||||||
|
// Список треков
|
||||||
|
foreach (var track in playlist?.Tracks ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($" {track.Title}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Получение информации о плейлисте через UUID
|
||||||
|
var playlistByUuid = await client.Api.Playlist.GetPlaylistByUuidAsync("uuid");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Треки
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение одного трека
|
||||||
|
var track = await client.Api.Track.GetTrackAsync("trackId");
|
||||||
|
|
||||||
|
// Получение нескольких треков
|
||||||
|
var tracks = await client.Api.Track.GetTracksAsync(["id1", "id2", "id3"]);
|
||||||
|
|
||||||
|
// Дополнительная информация о треке
|
||||||
|
var supplement = await client.Api.Track.GetTrackSupplementAsync("trackId");
|
||||||
|
|
||||||
|
// Похожие треки
|
||||||
|
var similar = await client.Api.Track.GetTrackSimilarAsync("trackId");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. Радио
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение станций
|
||||||
|
var stations = await client.Api.Radio.GetStationsAsync();
|
||||||
|
|
||||||
|
foreach (var station in stations?.Stations ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($"📻 {station.Title}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Треки станции
|
||||||
|
var stationTracks = await client.Api.Radio.GetStationTracksAsync("stationId");
|
||||||
|
foreach (var track in stationTracks?.Sequence ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($" {track.Track?.Title}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Библиотека (Лайки)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение лайков
|
||||||
|
var library = await client.Api.Library.GetLibraryAsync();
|
||||||
|
|
||||||
|
Console.WriteLine($"Лайки: {library?.Library?.Count}");
|
||||||
|
|
||||||
|
// Добавление трека в лайки
|
||||||
|
await client.Api.Library.LibraryAddAsync("trackId");
|
||||||
|
|
||||||
|
// Удаление из лайков
|
||||||
|
await client.Api.Library.LibraryRemoveAsync("trackId");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8. Главная страница (Landing)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение рекомендаций
|
||||||
|
var landing = await client.Api.Landing.GetLandingAsync("home");
|
||||||
|
|
||||||
|
foreach (var block in landing?.Blocks ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Блок: {block.Title}");
|
||||||
|
|
||||||
|
foreach (var entity in block.Entities ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($" {entity.Title}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🌐 WebSocket (Ynison)
|
||||||
|
|
||||||
|
### Подключение к Ynison
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
// Авторизуемся
|
||||||
|
await client.AuthorizeAsync("email@gmail.com", "password");
|
||||||
|
|
||||||
|
// Получаем плеер
|
||||||
|
var player = client.Ynison;
|
||||||
|
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
// Подключаемся
|
||||||
|
await player.ConnectAsync();
|
||||||
|
Console.WriteLine("✅ Подключены к Ynison");
|
||||||
|
|
||||||
|
// Используем плеер...
|
||||||
|
|
||||||
|
// Отключаемся
|
||||||
|
await player.DisconnectAsync();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚠️ Обработка ошибок
|
||||||
|
|
||||||
|
### Базовая обработка
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var track = await client.Api.Track.GetTrackAsync("trackId");
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Трек не найден");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (HttpRequestException ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Ошибка сети: {ex.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Неожиданная ошибка: {ex.Message}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Проверка авторизации
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
if (!client.IsAuthorized)
|
||||||
|
{
|
||||||
|
Console.WriteLine("❌ Требуется авторизация");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выполняем авторизованные операции
|
||||||
|
var account = await client.Api.User.GetAccountAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
### Валидация входных параметров
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public async Task GetTrackInfo(string trackId)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(trackId))
|
||||||
|
{
|
||||||
|
Console.WriteLine("❌ ID трека не может быть пустым");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(trackId);
|
||||||
|
if (track == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Трек с ID {trackId} не найден");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Console.WriteLine($"✅ {track.Title}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔍 Полные примеры
|
||||||
|
|
||||||
|
### Пример 1: Поиск и загрузка информации
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using YandexMusic;
|
||||||
|
|
||||||
|
// Создание клиента
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
// Поиск
|
||||||
|
Console.Write("Введите исполнителя: ");
|
||||||
|
string query = Console.ReadLine() ?? "Beatles";
|
||||||
|
|
||||||
|
var search = await client.Api.Search.SearchAsync(query);
|
||||||
|
var artists = search?.Artists?.Results ?? [];
|
||||||
|
|
||||||
|
if (artists.Count == 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("Исполнители не найдены");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Выбор первого результата
|
||||||
|
var artist = artists.First();
|
||||||
|
Console.WriteLine($"Найден: {artist.Title}");
|
||||||
|
|
||||||
|
// Получение полной информации
|
||||||
|
var fullArtist = await client.Api.Artist.GetArtistAsync(artist.Id);
|
||||||
|
Console.WriteLine($"Жанр: {fullArtist?.Genre}");
|
||||||
|
Console.WriteLine($"Альбомов: {fullArtist?.Albums?.Count}");
|
||||||
|
|
||||||
|
// Список треков
|
||||||
|
Console.WriteLine("\nПопулярные треки:");
|
||||||
|
foreach (var track in fullArtist?.Tracks?.Take(5) ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($" 🎵 {track.Title}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 2: Работа с плейлистом
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using YandexMusic;
|
||||||
|
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
// ID плейлиста Яндекса
|
||||||
|
string playlistId = "1130499373";
|
||||||
|
|
||||||
|
// Получение плейлиста
|
||||||
|
var playlist = await client.Api.Playlist.GetPlaylistAsync(playlistId);
|
||||||
|
|
||||||
|
Console.WriteLine($"📋 {playlist?.Title}");
|
||||||
|
Console.WriteLine($"Описание: {playlist?.Description}");
|
||||||
|
Console.WriteLine($"Треков: {playlist?.Tracks?.Count}");
|
||||||
|
|
||||||
|
// Вывод треков с номерами
|
||||||
|
Console.WriteLine("\nТреки:");
|
||||||
|
int i = 1;
|
||||||
|
foreach (var track in playlist?.Tracks ?? [])
|
||||||
|
{
|
||||||
|
var artists = string.Join(", ", track.Artists?.Select(a => a.Title) ?? []);
|
||||||
|
Console.WriteLine($"{i}. {track.Title} - {artists}");
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Общая длительность
|
||||||
|
var totalSeconds = (playlist?.Tracks ?? []).Sum(t => t.DurationMs ?? 0) / 1000;
|
||||||
|
var hours = totalSeconds / 3600;
|
||||||
|
var minutes = (totalSeconds % 3600) / 60;
|
||||||
|
Console.WriteLine($"\nОбщая длительность: {hours}:{minutes:D2}");
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 3: Авторизация и библиотека
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using YandexMusic;
|
||||||
|
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
Console.Write("Email: ");
|
||||||
|
string email = Console.ReadLine() ?? "";
|
||||||
|
|
||||||
|
Console.Write("Password: ");
|
||||||
|
string password = Console.ReadLine() ?? "";
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await client.AuthorizeAsync(email, password);
|
||||||
|
Console.WriteLine($"✅ Добро пожаловать, {client.Account.User?.DisplayName}!");
|
||||||
|
|
||||||
|
if (client.IsAuthorized)
|
||||||
|
{
|
||||||
|
// Получение лайков
|
||||||
|
var library = await client.Api.Library.GetLibraryAsync();
|
||||||
|
Console.WriteLine($"\n❤️ Ваши лайки: {library?.Library?.Count} треков");
|
||||||
|
|
||||||
|
// Список лайков
|
||||||
|
if (library?.Library?.Count > 0)
|
||||||
|
{
|
||||||
|
Console.WriteLine("\nПервые 5 лайков:");
|
||||||
|
var trackIds = library.Library.Select(l => l.TrackId).Take(5);
|
||||||
|
var tracks = await client.Api.Track.GetTracksAsync(trackIds);
|
||||||
|
|
||||||
|
foreach (var track in tracks)
|
||||||
|
{
|
||||||
|
Console.WriteLine($" ❤️ {track?.Title}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Ошибка: {ex.Message}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📖 Дальнейшее обучение
|
||||||
|
|
||||||
|
### Документация
|
||||||
|
|
||||||
|
- **[README.md](README.md)** - Полное описание проекта
|
||||||
|
- **[ARCHITECTURE.md](ARCHITECTURE.md)** - Архитектура и паттерны
|
||||||
|
- **[YandexMusic.API/README.md](YandexMusic.API/README.md)** - Низкоуровневый API
|
||||||
|
- **[CONTRIBUTING.md](CONTRIBUTING.md)** - Как внести вклад
|
||||||
|
|
||||||
|
### Полезные ресурсы
|
||||||
|
|
||||||
|
- [.NET 10 Documentation](https://learn.microsoft.com/dotnet/)
|
||||||
|
- [C# 12 Features](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12)
|
||||||
|
- [System.Text.Json Guide](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json)
|
||||||
|
|
||||||
|
### Где найти ID
|
||||||
|
|
||||||
|
**Трека:** `https://music.yandex.ru/album/123456/track/789012`
|
||||||
|
- Album ID: `123456`
|
||||||
|
- Track ID: `789012`
|
||||||
|
|
||||||
|
**Плейлиста:** `https://music.yandex.ru/playlist/1130499373`
|
||||||
|
- Playlist ID: `1130499373`
|
||||||
|
|
||||||
|
**Исполнителя:** `https://music.yandex.ru/artist/123456`
|
||||||
|
- Artist ID: `123456`
|
||||||
|
|
||||||
|
## ⚡ Советы и трюки
|
||||||
|
|
||||||
|
### 1. Работа с async/await
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение нескольких ресурсов параллельно
|
||||||
|
var (albums, artists, tracks) = await (
|
||||||
|
client.Api.Album.GetAlbumsAsync(ids),
|
||||||
|
client.Api.Artist.GetArtistsAsync(ids),
|
||||||
|
client.Api.Track.GetTracksAsync(ids)
|
||||||
|
).AsAsync();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Обработка больших списков
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Пагинация для больших результатов
|
||||||
|
var allPlaylists = new List<YPlaylist>();
|
||||||
|
int page = 0;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
var playlists = await client.Api.Playlist.GetPlaylistsAsync(page: page);
|
||||||
|
if (playlists?.Playlists?.Count == 0) break;
|
||||||
|
|
||||||
|
allPlaylists.AddRange(playlists.Playlists ?? []);
|
||||||
|
page++;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Кеширование результатов
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
private readonly Dictionary<string, YTrack?> _trackCache = new();
|
||||||
|
|
||||||
|
public async Task<YTrack?> GetTrackAsync(string id)
|
||||||
|
{
|
||||||
|
if (_trackCache.TryGetValue(id, out var cached))
|
||||||
|
return cached;
|
||||||
|
|
||||||
|
var track = await client.Api.Track.GetTrackAsync(id);
|
||||||
|
_trackCache[id] = track;
|
||||||
|
return track;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Следующий шаг:** Прочитайте [ARCHITECTURE.md](ARCHITECTURE.md) для понимания внутреннего устройства.
|
||||||
|
|
||||||
|
**Нужна помощь?** Посмотрите [README.md](README.md) или создайте issue.
|
||||||
|
|
||||||
|
Удачи в разработке! 🚀
|
||||||
437
README.md
Normal file
437
README.md
Normal file
@@ -0,0 +1,437 @@
|
|||||||
|
# 🎵 YandexMusic - Complete Solution
|
||||||
|
|
||||||
|
Полнофункциональное решение для работы с неофициальным API Яндекс Музыки на базе .NET 10. Содержит библиотеку API, оборачиватель клиента и CLI приложение.
|
||||||
|
|
||||||
|
## 📋 Содержание
|
||||||
|
|
||||||
|
- [Описание](#описание)
|
||||||
|
- [Структура решения](#структура-решения)
|
||||||
|
- [Требования](#требования)
|
||||||
|
- [Установка](#установка)
|
||||||
|
- [Быстрый старт](#быстрый-старт)
|
||||||
|
- [Проекты](#проекты)
|
||||||
|
- [Примеры использования](#примеры-использования)
|
||||||
|
- [Архитектура](#архитектура)
|
||||||
|
- [Лицензия](#лицензия)
|
||||||
|
|
||||||
|
## 📖 Описание
|
||||||
|
|
||||||
|
**YandexMusic** — это комплексное решение для взаимодействия с API Яндекс Музыки на платформе .NET 10. Решение состоит из двух ключевых компонентов:
|
||||||
|
|
||||||
|
1. **YandexMusic.API** — низкоуровневая библиотека для прямого взаимодействия с API
|
||||||
|
2. **YandexMusic** — удобный оборачиватель (wrapper) с клиентом `YandexMusicClient`
|
||||||
|
|
||||||
|
### 🎯 Основные возможности
|
||||||
|
|
||||||
|
- ✅ **Полная асинхронная архитектура** — Async/await на всех уровнях
|
||||||
|
- ✅ **Типобезопасный код** — Nullable reference types, full type safety
|
||||||
|
- ✅ **Модульная структура** — Легко расширяемые компоненты
|
||||||
|
- ✅ **Современный стек** — .NET 10, C# 12, System.Text.Json
|
||||||
|
- ✅ **Полная документация на русском** — XML docs для всех публичных членов
|
||||||
|
- ✅ **WebSocket поддержка** — Протокол Ynison для real-time синхронизации
|
||||||
|
- ✅ **Гибкая конфигурация** — Поддержка прокси, cookies, custom headers
|
||||||
|
- ✅ **Отладка встроена** — Debug settings для логирования и анализа
|
||||||
|
|
||||||
|
## 🏗️ Структура решения
|
||||||
|
|
||||||
|
```
|
||||||
|
YandexMusic/
|
||||||
|
├── YandexMusic.API/ # Низкоуровневая библиотека API
|
||||||
|
│ ├── API/ # Классы для разных веток API
|
||||||
|
│ ├── Models/ # Модели данных
|
||||||
|
│ ├── Requests/ # Построители запросов
|
||||||
|
│ ├── Common/ # Вспомогательные компоненты
|
||||||
|
│ ├── Extensions/ # Методы расширения
|
||||||
|
│ ├── YandexMusicApi.cs # Главный класс API
|
||||||
|
│ ├── YandexMusic.API.csproj
|
||||||
|
│ └── README.md # Документация по API
|
||||||
|
│
|
||||||
|
├── YandexMusic/ # Оборачиватель и клиент
|
||||||
|
│ ├── YandexMusicClient.cs # Основной класс клиента
|
||||||
|
│ ├── YandexMusic.csproj
|
||||||
|
│ └── YandexMusicClient.cs # Реализация клиента
|
||||||
|
│
|
||||||
|
└── README.md # Этот файл
|
||||||
|
```
|
||||||
|
|
||||||
|
### Зависимости между проектами
|
||||||
|
|
||||||
|
```
|
||||||
|
YandexMusic (Client wrapper)
|
||||||
|
↓
|
||||||
|
YandexMusic.API (Core library)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 Требования
|
||||||
|
|
||||||
|
- **.NET 10** или выше
|
||||||
|
- **C# 12** или выше
|
||||||
|
- **Visual Studio 2026** (рекомендуется) или Visual Studio Code
|
||||||
|
|
||||||
|
### Системные требования
|
||||||
|
|
||||||
|
- Windows 10/11, Linux, macOS (любая ОС с .NET 10)
|
||||||
|
- Минимум 512 MB RAM
|
||||||
|
- Интернет соединение для работы с API
|
||||||
|
|
||||||
|
## 🚀 Установка
|
||||||
|
|
||||||
|
### Клонирование репозитория
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.frigat.duckdns.org/FrigaT/YandexMusic.git
|
||||||
|
cd YandexMusic
|
||||||
|
```
|
||||||
|
|
||||||
|
### Восстановление зависимостей
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet restore
|
||||||
|
```
|
||||||
|
|
||||||
|
### Сборка решения
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Запуск тестов (если есть)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
dotnet test
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚡ Быстрый старт
|
||||||
|
|
||||||
|
### 1. Как клиент (рекомендуется для большинства случаев)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using YandexMusic;
|
||||||
|
|
||||||
|
// Создание клиента
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
// Получение информации о треке
|
||||||
|
var track = await client.Api.Track.GetTrackAsync("trackId123");
|
||||||
|
Console.WriteLine($"Трек: {track?.Title}");
|
||||||
|
|
||||||
|
// Поиск музыки
|
||||||
|
var results = await client.Api.Search.SearchAsync("Ленинград");
|
||||||
|
Console.WriteLine($"Найдено результатов: {results?.Tracks?.Results?.Count}");
|
||||||
|
|
||||||
|
// Работа с плейлистами
|
||||||
|
var playlists = await client.Api.Playlist.GetPlaylistAsync("playlistId");
|
||||||
|
Console.WriteLine($"Плейлист: {playlists?.Title}");
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Низкоуровневой API (расширенная работа)
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using YandexMusic.API;
|
||||||
|
|
||||||
|
// Создание API
|
||||||
|
var api = new YandexMusicApi();
|
||||||
|
|
||||||
|
// Прямая работа с API
|
||||||
|
var track = await api.Track.GetTrackAsync("trackId");
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Проекты
|
||||||
|
|
||||||
|
### YandexMusic.API
|
||||||
|
|
||||||
|
Низкоуровневая библиотека, предоставляющая полный доступ к API Яндекс Музыки.
|
||||||
|
|
||||||
|
**Основные компоненты:**
|
||||||
|
|
||||||
|
- `YandexMusicApi` — главный класс, содержит все API классы
|
||||||
|
- `AuthStorage` — управление авторизацией и cookies
|
||||||
|
- `IRequestProvider` — интерфейс для обработки HTTP запросов
|
||||||
|
- `YCommonAPI` — базовый класс для всех API веток
|
||||||
|
|
||||||
|
**API Классы:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class YandexMusicApi
|
||||||
|
{
|
||||||
|
public YAlbumAPI Album { get; } // Альбомы
|
||||||
|
public YArtistAPI Artist { get; } // Исполнители
|
||||||
|
public YLabelAPI Label { get; } // Лейблы
|
||||||
|
public YLandingAPI Landing { get; } // Рекомендации
|
||||||
|
public YLibraryAPI Library { get; } // Библиотека
|
||||||
|
public YPlaylistAPI Playlist { get; } // Плейлисты
|
||||||
|
public YPinsAPI Pins { get; } // Закреплённые
|
||||||
|
public YRadioAPI Radio { get; } // Радио
|
||||||
|
public YSearchAPI Search { get; } // Поиск
|
||||||
|
public YTrackAPI Track { get; } // Треки
|
||||||
|
public YQueueAPI Queue { get; } // Очередь
|
||||||
|
public YUserAPI User { get; } // Пользователь
|
||||||
|
public YUgcAPI UserGeneratedContent { get; } // UGC
|
||||||
|
public YYnisonAPI Ynison { get; } // WebSocket
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Особенности:**
|
||||||
|
|
||||||
|
- 300+ моделей данных для полного покрытия API
|
||||||
|
- Асинхронные методы для всех операций
|
||||||
|
- Поддержка WebSocket (Ynison)
|
||||||
|
- Встроенная обработка ошибок
|
||||||
|
- System.Text.Json для сериализации
|
||||||
|
|
||||||
|
**Документация:** [YandexMusic.API/README.md](YandexMusic.API/README.md)
|
||||||
|
|
||||||
|
### YandexMusic
|
||||||
|
|
||||||
|
Удобный оборачиватель (wrapper) над низкоуровневой библиотекой с клиентом `YandexMusicClient`.
|
||||||
|
|
||||||
|
**Основной класс:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class YandexMusicClient : IDisposable
|
||||||
|
{
|
||||||
|
// Свойства
|
||||||
|
public AuthStorage AuthStorage { get; }
|
||||||
|
public YAccount Account { get; }
|
||||||
|
public bool IsAuthorized { get; }
|
||||||
|
public YnisonPlayer? Ynison { get; }
|
||||||
|
public HttpClient HttpClient { get; }
|
||||||
|
public YandexMusicApi Api { get; }
|
||||||
|
|
||||||
|
// Методы авторизации
|
||||||
|
public Task AuthorizeAsync(string login, string password);
|
||||||
|
public Task AuthorizeByTokenAsync(string token);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Возможности:**
|
||||||
|
|
||||||
|
- Интеграция с собственным HttpClient
|
||||||
|
- Управление cookies и прокси
|
||||||
|
- Встроенная авторизация
|
||||||
|
- WebSocket плеер Ynison
|
||||||
|
- Удобное API через свойство `Api`
|
||||||
|
|
||||||
|
**Использование:**
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// С пользовательскими настройками
|
||||||
|
var client = new YandexMusicClient(
|
||||||
|
cookieContainer: new CookieContainer(),
|
||||||
|
proxy: new WebProxy("http://proxy:8080"),
|
||||||
|
timeout: TimeSpan.FromSeconds(30),
|
||||||
|
userAgent: "Custom Agent"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Авторизация
|
||||||
|
await client.AuthorizeAsync("login@gmail.com", "password");
|
||||||
|
|
||||||
|
// Использование
|
||||||
|
var playlists = await client.Api.Playlist.GetPlaylistAsync("123");
|
||||||
|
```
|
||||||
|
|
||||||
|
## 💡 Примеры использования
|
||||||
|
|
||||||
|
### Пример 1: Поиск и получение информации
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using YandexMusic;
|
||||||
|
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
// Поиск треков
|
||||||
|
var search = await client.Api.Search.SearchAsync("The Beatles");
|
||||||
|
var track = search?.Tracks?.Results?.FirstOrDefault();
|
||||||
|
|
||||||
|
if (track != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"🎵 {track.Title}");
|
||||||
|
Console.WriteLine($"👤 {string.Join(", ", track.Artists?.Select(a => a.Title) ?? [])}");
|
||||||
|
Console.WriteLine($"⏱️ {track.DurationMs / 1000} сек");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 2: Работа с плейлистами
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение плейлиста
|
||||||
|
var playlist = await client.Api.Playlist.GetPlaylistAsync("playlistId");
|
||||||
|
Console.WriteLine($"Плейлист: {playlist?.Title}");
|
||||||
|
Console.WriteLine($"Треков: {playlist?.Tracks?.Count}");
|
||||||
|
|
||||||
|
// Вывод треков
|
||||||
|
foreach (var track in playlist?.Tracks ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($" - {track.Title}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 3: Работа с альбомами
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Получение альбома
|
||||||
|
var album = await client.Api.Album.GetAlbumAsync("albumId");
|
||||||
|
|
||||||
|
Console.WriteLine($"Альбом: {album?.Title}");
|
||||||
|
Console.WriteLine($"Исполнитель: {album?.Artists?.FirstOrDefault()?.Title}");
|
||||||
|
Console.WriteLine($"Год: {album?.Year}");
|
||||||
|
Console.WriteLine($"Жанр: {album?.Genre}");
|
||||||
|
|
||||||
|
// Вывод треков в альбоме
|
||||||
|
foreach (var track in album?.Tracks ?? [])
|
||||||
|
{
|
||||||
|
Console.WriteLine($" {track.Position}. {track.Title}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 4: Авторизация
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var client = new YandexMusicClient();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Авторизация через логин и пароль
|
||||||
|
await client.AuthorizeAsync("your-email@gmail.com", "your-password");
|
||||||
|
|
||||||
|
Console.WriteLine($"✅ Авторизирован: {client.Account.User?.DisplayName}");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"❌ Ошибка авторизации: {ex.Message}");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример 5: WebSocket Ynison
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// Подключение к Ynison (если авторизирован)
|
||||||
|
var player = client.Ynison;
|
||||||
|
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
await player.ConnectAsync();
|
||||||
|
Console.WriteLine("✅ Подключено к Ynison");
|
||||||
|
|
||||||
|
// Использование плеера...
|
||||||
|
|
||||||
|
await player.DisconnectAsync();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🏛️ Архитектура
|
||||||
|
|
||||||
|
### Слои
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────┐
|
||||||
|
│ YandexMusic (Client Wrapper) │ ← Удобный клиент
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ YandexMusic.API (Core Library) │ ← Низкоуровневой API
|
||||||
|
├─────────────────────────────────────┤
|
||||||
|
│ HttpClient, System.Text.Json │ ← .NET Framework
|
||||||
|
└─────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ключевые компоненты YandexMusic.API
|
||||||
|
|
||||||
|
```
|
||||||
|
YandexMusicApi (Main Entry Point)
|
||||||
|
├── API Classes (YAlbumAPI, YTrackAPI, etc.)
|
||||||
|
│ └── Requests (YGetAlbumBuilder, YSearchBuilder, etc.)
|
||||||
|
│
|
||||||
|
├── Models (YAlbum, YTrack, YPlaylist, etc.)
|
||||||
|
│ └── Common Models (YBaseModel, YResponse, etc.)
|
||||||
|
│
|
||||||
|
├── AuthStorage (Authorization Management)
|
||||||
|
│ └── IRequestProvider (HTTP Request Handling)
|
||||||
|
│ ├── DefaultRequestProvider
|
||||||
|
│ ├── CommonRequestProvider
|
||||||
|
│ └── MockRequestProvider
|
||||||
|
│
|
||||||
|
└── Extensions & Utilities
|
||||||
|
├── HttpRequestHeaderExtensions
|
||||||
|
├── StringExtensions
|
||||||
|
├── Encryptor (для шифрования)
|
||||||
|
└── DataDownloader
|
||||||
|
```
|
||||||
|
|
||||||
|
### Обработка запросов
|
||||||
|
|
||||||
|
```
|
||||||
|
Request Builder (YRequestBuilder<T>)
|
||||||
|
↓
|
||||||
|
HttpRequestMessage
|
||||||
|
↓
|
||||||
|
IRequestProvider.GetWebResponseAsync()
|
||||||
|
↓
|
||||||
|
HttpResponseMessage
|
||||||
|
↓
|
||||||
|
System.Text.Json Deserialization
|
||||||
|
↓
|
||||||
|
Model<T>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 Конфигурация
|
||||||
|
|
||||||
|
### Настройка HttpClient
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
var client = new YandexMusicClient(
|
||||||
|
cookieContainer: new CookieContainer(),
|
||||||
|
proxy: new WebProxy("http://127.0.0.1:8080"),
|
||||||
|
timeout: TimeSpan.FromSeconds(30),
|
||||||
|
userAgent: "MyCustomAgent/1.0"
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📊 Статистика проекта
|
||||||
|
|
||||||
|
| Метрика | Значение |
|
||||||
|
|---------|----------|
|
||||||
|
| Проектов | 3 |
|
||||||
|
| Целевая платформа | .NET 10 |
|
||||||
|
| Язык C# | 12 |
|
||||||
|
| Основных API методов | 50+ |
|
||||||
|
| Моделей данных | 300+ |
|
||||||
|
| Документированных членов | 100% |
|
||||||
|
| Асинхронных методов | 100% |
|
||||||
|
|
||||||
|
## 🔐 Безопасность
|
||||||
|
|
||||||
|
- ✅ Использование HTTPS для всех запросов
|
||||||
|
- ✅ Поддержка прокси для безопасности
|
||||||
|
- ✅ Встроенное шифрование для чувствительных данных
|
||||||
|
- ✅ Управление cookies и сессиями
|
||||||
|
- ✅ Валидация всех входных данных
|
||||||
|
|
||||||
|
**⚠️ Важно:** Это неофициальная библиотека. Используйте её на свой риск и соблюдайте Terms of Service Яндекс Музыки.
|
||||||
|
|
||||||
|
## 🚦 Статус проекта
|
||||||
|
|
||||||
|
- ✅ **Стабильный** — Основная функциональность работает
|
||||||
|
- 🔄 **Активная разработка** — Регулярные обновления
|
||||||
|
- 📝 **Документирован** — Полная документация на русском
|
||||||
|
|
||||||
|
## 📝 Лицензия
|
||||||
|
|
||||||
|
Это неофициальная библиотека для работы с API Яндекс Музыки.
|
||||||
|
|
||||||
|
**Дисклеймер:** Автор не несет ответственности за неправомерное использование данной библиотеки. Используйте её в соответствии с Terms of Service Яндекс Музыки.
|
||||||
|
|
||||||
|
## 👨💻 Автор
|
||||||
|
|
||||||
|
**FrigaT** - Разработчик
|
||||||
|
|
||||||
|
## 🤝 Поддержка
|
||||||
|
|
||||||
|
Для вопросов, багов и предложений:
|
||||||
|
- 🐛 Issues: [https://git.frigat.duckdns.org/FrigaT/YandexMusic/issues]
|
||||||
|
- 💬 Discussions: [https://git.frigat.duckdns.org/FrigaT/YandexMusic/discussions]
|
||||||
|
|
||||||
|
## 📚 Дополнительная информация
|
||||||
|
|
||||||
|
- [YandexMusic.API/README.md](YandexMusic.API/README.md) — Документация по низкоуровневому API
|
||||||
|
- [Официальный сайт Яндекс Музыки](https://music.yandex.ru)
|
||||||
|
- [.NET 10 Documentation](https://learn.microsoft.com/dotnet/)
|
||||||
|
- [C# 12 Features](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-12)
|
||||||
Reference in New Issue
Block a user