Добавлен playlist shared
This commit is contained in:
14
PlaylistShared/Services/IYandexMusicService.cs
Normal file
14
PlaylistShared/Services/IYandexMusicService.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace PlaylistShared.Services;
|
||||
|
||||
public interface IYandexMusicService
|
||||
{
|
||||
Task<string> CreatePlaylistAsync(string userId, string title);
|
||||
Task AddTrackToPlaylistAsync(string userId, string yandexPlaylistId, string trackId);
|
||||
Task RemoveTrackFromPlaylistAsync(string userId, string yandexPlaylistId, string trackId);
|
||||
Task<List<YandexTrackInfo>> GetPlaylistTracksAsync(string userId, string yandexPlaylistId);
|
||||
Task<YandexPlaylistInfo> GetPlaylistInfoAsync(string userId, string yandexPlaylistId);
|
||||
Task<string?> RefreshUserTokenAsync(string userId);
|
||||
}
|
||||
|
||||
public record YandexTrackInfo(string Id, string Title, string? Artist, string? AlbumTitle, int DurationMs);
|
||||
public record YandexPlaylistInfo(string Kind, string Title, int TrackCount);
|
||||
173
PlaylistShared/Services/YandexMusicService.cs
Normal file
173
PlaylistShared/Services/YandexMusicService.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using PlaylistShared.Data.Contexts;
|
||||
using PlaylistShared.Data.Entities;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace PlaylistShared.Services;
|
||||
|
||||
public class YandexMusicService : IYandexMusicService
|
||||
{
|
||||
private readonly AppDbContext _db;
|
||||
private readonly HttpClient _http;
|
||||
private readonly IConfiguration _config;
|
||||
private readonly ILogger<YandexMusicService> _logger;
|
||||
|
||||
public YandexMusicService(AppDbContext db, IHttpClientFactory factory, IConfiguration config, ILogger<YandexMusicService> logger)
|
||||
{
|
||||
_db = db;
|
||||
_http = factory.CreateClient();
|
||||
_config = config;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private async Task<ApplicationUser> GetUserWithValidTokenAsync(string userId)
|
||||
{
|
||||
var user = await _db.Users.FindAsync(userId);
|
||||
if (user == null) throw new Exception("User not found");
|
||||
|
||||
if (user.AccessTokenExpiresAt <= DateTime.UtcNow.AddMinutes(5))
|
||||
{
|
||||
var newToken = await RefreshYandexTokenAsync(user.RefreshToken!);
|
||||
user.AccessToken = newToken.access_token;
|
||||
user.RefreshToken = newToken.refresh_token;
|
||||
user.AccessTokenExpiresAt = DateTime.UtcNow.AddSeconds(newToken.expires_in);
|
||||
await _db.SaveChangesAsync();
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private async Task<(string access_token, string refresh_token, int expires_in)> RefreshYandexTokenAsync(string refreshToken)
|
||||
{
|
||||
var content = new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("grant_type", "refresh_token"),
|
||||
new KeyValuePair<string, string>("refresh_token", refreshToken),
|
||||
new KeyValuePair<string, string>("client_id", _config["YandexOAuth:ClientId"]),
|
||||
new KeyValuePair<string, string>("client_secret", _config["YandexOAuth:ClientSecret"])
|
||||
});
|
||||
var response = await _http.PostAsync(_config["YandexOAuth:TokenEndpoint"], content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
using var doc = JsonDocument.Parse(json);
|
||||
return (
|
||||
doc.RootElement.GetProperty("access_token").GetString()!,
|
||||
doc.RootElement.GetProperty("refresh_token").GetString()!,
|
||||
doc.RootElement.GetProperty("expires_in").GetInt32()
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<string> CreatePlaylistAsync(string userId, string title)
|
||||
{
|
||||
var user = await GetUserWithValidTokenAsync(userId);
|
||||
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", user.AccessToken);
|
||||
var payload = new { title, visibility = "public" };
|
||||
var json = JsonSerializer.Serialize(payload);
|
||||
var response = await _http.PostAsync("https://api.music.yandex.net/users/self/playlists/create",
|
||||
new StringContent(json, Encoding.UTF8, "application/json"));
|
||||
response.EnsureSuccessStatusCode();
|
||||
var resultJson = await response.Content.ReadAsStringAsync();
|
||||
var result = JsonSerializer.Deserialize<YandexApiResponse<YandexPlaylistData>>(resultJson);
|
||||
return result!.Result.Kind;
|
||||
}
|
||||
|
||||
public async Task AddTrackToPlaylistAsync(string userId, string yandexPlaylistId, string trackId)
|
||||
{
|
||||
var user = await GetUserWithValidTokenAsync(userId);
|
||||
var uid = user.YandexId; // или user.Id? В API Яндекса используется uid из аккаунта, но можно использовать "self"
|
||||
// Получаем альбом трека (опционально, можно передать без albumId)
|
||||
// Для простоты используем трек без albumId
|
||||
var diff = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
op = "insert",
|
||||
at = 0,
|
||||
tracks = new[] { new { id = trackId } }
|
||||
}
|
||||
};
|
||||
var json = JsonSerializer.Serialize(diff);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
var url = $"https://api.music.yandex.net/users/{uid}/playlists/{yandexPlaylistId}/change";
|
||||
var response = await _http.PostAsync(url, content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task RemoveTrackFromPlaylistAsync(string userId, string yandexPlaylistId, string trackId)
|
||||
{
|
||||
var user = await GetUserWithValidTokenAsync(userId);
|
||||
var uid = user.YandexId;
|
||||
// Сначала нужно получить позицию трека в плейлисте – упростим: удаляем по индексу (не надёжно)
|
||||
// Лучше получить плейлист, найти индекс трека, потом удалить
|
||||
var playlist = await GetPlaylistInfoAsync(userId, yandexPlaylistId);
|
||||
var tracks = await GetPlaylistTracksAsync(userId, yandexPlaylistId);
|
||||
var index = tracks.FindIndex(t => t.Id == trackId);
|
||||
if (index == -1) throw new Exception("Track not found in playlist");
|
||||
var diff = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
op = "delete",
|
||||
from = index,
|
||||
to = index + 1
|
||||
}
|
||||
};
|
||||
var json = JsonSerializer.Serialize(diff);
|
||||
var content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
var url = $"https://api.music.yandex.net/users/{uid}/playlists/{yandexPlaylistId}/change";
|
||||
var response = await _http.PostAsync(url, content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task<List<YandexTrackInfo>> GetPlaylistTracksAsync(string userId, string yandexPlaylistId)
|
||||
{
|
||||
var user = await GetUserWithValidTokenAsync(userId);
|
||||
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", user.AccessToken);
|
||||
var uid = user.YandexId;
|
||||
var url = $"https://api.music.yandex.net/users/{uid}/playlists/{yandexPlaylistId}";
|
||||
var response = await _http.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
var data = JsonSerializer.Deserialize<YandexApiResponse<YandexPlaylistFull>>(json);
|
||||
return data!.Result.Tracks?.Select(t => new YandexTrackInfo(
|
||||
t.Id.ToString(),
|
||||
t.Title,
|
||||
t.Artists?.FirstOrDefault()?.Name,
|
||||
t.Albums?.FirstOrDefault()?.Title,
|
||||
t.DurationMs
|
||||
)).ToList() ?? new();
|
||||
}
|
||||
|
||||
public async Task<YandexPlaylistInfo> GetPlaylistInfoAsync(string userId, string yandexPlaylistId)
|
||||
{
|
||||
var user = await GetUserWithValidTokenAsync(userId);
|
||||
_http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("OAuth", user.AccessToken);
|
||||
var uid = user.YandexId;
|
||||
var url = $"https://api.music.yandex.net/users/{uid}/playlists/{yandexPlaylistId}";
|
||||
var response = await _http.GetAsync(url);
|
||||
response.EnsureSuccessStatusCode();
|
||||
var json = await response.Content.ReadAsStringAsync();
|
||||
var data = JsonSerializer.Deserialize<YandexApiResponse<YandexPlaylistFull>>(json);
|
||||
return new YandexPlaylistInfo(data!.Result.Kind, data.Result.Title, data.Result.TrackCount);
|
||||
}
|
||||
|
||||
public async Task<string?> RefreshUserTokenAsync(string userId)
|
||||
{
|
||||
var user = await _db.Users.FindAsync(userId);
|
||||
if (user?.RefreshToken == null) return null;
|
||||
var newToken = await RefreshYandexTokenAsync(user.RefreshToken);
|
||||
user.AccessToken = newToken.access_token;
|
||||
user.RefreshToken = newToken.refresh_token;
|
||||
user.AccessTokenExpiresAt = DateTime.UtcNow.AddSeconds(newToken.expires_in);
|
||||
await _db.SaveChangesAsync();
|
||||
return newToken.access_token;
|
||||
}
|
||||
|
||||
// Вспомогательные record
|
||||
private record YandexApiResponse<T>(T Result);
|
||||
private record YandexPlaylistData(string Kind);
|
||||
private record YandexPlaylistFull(string Kind, string Title, int TrackCount, List<YandexTrack> Tracks);
|
||||
private record YandexTrack(string Id, string Title, int DurationMs, List<YandexArtist> Artists, List<YandexAlbum> Albums);
|
||||
private record YandexArtist(string Name);
|
||||
private record YandexAlbum(string Title);
|
||||
}
|
||||
Reference in New Issue
Block a user