# π€ Π ΡΠΊΠΎΠ²ΠΎΠ΄ΡΡΠ²ΠΎ Π΄Π»Ρ ΡΡΠ°ΡΡΠ½ΠΈΠΊΠΎΠ²
Π‘ΠΏΠ°ΡΠΈΠ±ΠΎ Π·Π° ΠΈΠ½ΡΠ΅ΡΠ΅Ρ ΠΊ ΠΏΡΠΎΠ΅ΠΊΡΡ 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;
/// API Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ Π°Π»ΡΠ±ΠΎΠΌΠ°ΠΌΠΈ.
public class YAlbumAPI : YCommonAPI
{
/// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΠ΅Ρ Π½ΠΎΠ²ΡΠΉ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ.
/// ΠΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠ³ΠΎ API.
public YAlbumAPI(YandexMusicApi yandex) : base(yandex) { }
/// ΠΠΎΠ»ΡΡΠ°Π΅Ρ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ ΠΎΠ± Π°Π»ΡΠ±ΠΎΠΌΠ΅ ΠΏΠΎ ΠΈΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡΡ.
/// ΠΠ΄Π΅Π½ΡΠΈΡΠΈΠΊΠ°ΡΠΎΡ Π°Π»ΡΠ±ΠΎΠΌΠ°
/// ΠΠΎΠ΄Π΅Π»Ρ Π°Π»ΡΠ±ΠΎΠΌΠ° ΠΈΠ»ΠΈ null Π΅ΡΠ»ΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½
/// ΠΡΠ»ΠΈ albumId null
public async Task GetAlbumAsync(string albumId)
{
ArgumentNullException.ThrowIfNull(albumId);
// Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ
return await Task.FromResult(null);
}
}
```
### ΠΡΠ°Π²ΠΈΠ»Π° ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½ΠΈΡ
```csharp
// ΠΠ»Π°ΡΡΡ: PascalCase
public class YandexMusicApi { }
// ΠΠ΅ΡΠΎΠ΄Ρ: PascalCase Ρ Async ΡΡΡΡΠΈΠΊΡΠΎΠΌ Π΄Π»Ρ Π°ΡΠΈΠ½Ρ
ΡΠΎΠ½Π½ΡΡ
public async Task 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();
```
### 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
ΠΡΠ΅Π³Π΄Π° Π²ΠΊΠ»ΡΡΠ΅Π½ `enable`. ΠΡΠ°Π²ΠΈΠ»Π°:
```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 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 GetTrackAsync(string id)
{
return await api.Track.GetTrackAsync(id).ConfigureAwait(false);
}
```
### ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° ΠΎΡΠΈΠ±ΠΎΠΊ
```csharp
// β
Π₯ΠΎΡΠΎΡΠΎ
public async Task 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 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;
/// API Π΄Π»Ρ ΡΠ°Π±ΠΎΡΡ Ρ ΡΡΠ΅ΠΊΠ°ΠΌΠΈ.
public class YTrackAPI : YCommonAPI
{
/// ΠΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·ΠΈΡΡΠ΅Ρ Π½ΠΎΠ²ΡΠΉ ΡΠΊΠ·Π΅ΠΌΠΏΠ»ΡΡ.
public YTrackAPI(YandexMusicApi yandex) : base(yandex) { }
/// ΠΠΎΠ»ΡΡΠ°Π΅Ρ ΡΡΠ΅ΠΊ.
public async Task GetTrackAsync(string trackId)
{
// ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ
}
/// ΠΠΎΠ»ΡΡΠ°Π΅Ρ Π½Π΅ΡΠΊΠΎΠ»ΡΠΊΠΎ ΡΡΠ΅ΠΊΠΎΠ².
public async Task> GetTracksAsync(IEnumerable trackIds)
{
// ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ
}
}
```
## π Commit ΡΠΎΠΎΠ±ΡΠ΅Π½ΠΈΡ
### Π€ΠΎΡΠΌΠ°Ρ
```
():