using Microsoft.AspNetCore.DataProtection; using PlaylistShared.Api.Entities; using PlaylistShared.Shared.DTO; using PlaylistShared.Shared.Enums; using YandexMusic; using YandexMusic.API.Extensions.API; using YandexMusic.API.Models.Playlist; using YandexMusic.API.Models.Track; namespace PlaylistShared.Api.Services; public class YandexMusicService { private readonly IDataProtector _dataProtector; public YandexMusicService(IDataProtectionProvider provider) { _dataProtector = provider.CreateProtector("YandexTokens"); } private async Task CreateClientAsync(ApplicationUser user) { if (string.IsNullOrEmpty(user.YandexAccessToken)) return null; string decryptedToken; try { decryptedToken = _dataProtector.Unprotect(user.YandexAccessToken); } catch { return null; } var client = new YandexMusicClient(); var success = await client.Authorize(decryptedToken); return success ? client : null; } public async Task GetPlaylistAsync(ApplicationUser user, string ownerUid, string kind) { var client = await CreateClientAsync(user); if (client == null) return null; return await client.GetPlaylistAsync(ownerUid, kind); } public async Task CreatePlaylistAsync(ApplicationUser user, string title) { var client = await CreateClientAsync(user); if (client == null) return null; return await client.CreatePlaylistAsync(title); } public async Task AddTracksAsync(ApplicationUser user, string ownerUid, string kind, IEnumerable trackIds) { var client = await CreateClientAsync(user); if (client == null) return null; var playlist = await client.GetPlaylistAsync(ownerUid, kind); if (playlist == null) return null; var tracks = await client.GetTracksAsync(trackIds); if (tracks == null || !tracks.Any()) return null; var insertedTracks = tracks.Where(t => !playlist.Tracks.Any(p => p.Track.Id == t.Id)).ToArray(); return await playlist.InsertTracksAsync(insertedTracks); } public async Task RemoveTracksAsync(ApplicationUser user, string ownerUid, string kind, IEnumerable trackIds) { var client = await CreateClientAsync(user); if (client == null) return null; var tracks = await client.GetTracksAsync(trackIds); if (tracks == null || !tracks.Any()) return null; var playlist = await client.GetPlaylistAsync(ownerUid, kind); if (playlist == null) return null; return await playlist.RemoveTracksAsync(tracks.ToArray()); } public async Task GetTrackFileUrlAsync(ApplicationUser user, string trackId) { var track = await GetYTrackAsync(user, trackId); if (track == null) return null; return await track.GetLinkAsync(); } public async Task GetYTrackAsync(ApplicationUser user, string trackId) { using var client = await CreateClientAsync(user); if (client == null) return null; var track = await client.GetTrackAsync(trackId); return track; } public string EncryptToken(string token) => _dataProtector.Protect(token); public string DecryptToken(string encryptedToken) { try { return _dataProtector.Unprotect(encryptedToken); } catch { return null; } } public async Task> SearchTracksAsync( ApplicationUser user, string query, TrackSearchType? searchType = TrackSearchType.All, int limit = 20 ) { var client = await CreateClientAsync(user); if (client == null) return new List(); var ySerchType = searchType switch { TrackSearchType.Artist => YandexMusic.API.Models.Common.YSearchType.Artist, TrackSearchType.Album => YandexMusic.API.Models.Common.YSearchType.Album, TrackSearchType.Playlist => YandexMusic.API.Models.Common.YSearchType.Playlist, TrackSearchType.Track => YandexMusic.API.Models.Common.YSearchType.Track, _ => YandexMusic.API.Models.Common.YSearchType.All }; var searchResult = await client.SearchAsync(query, ySerchType, page: 0, pageSize: limit); if (searchResult?.Tracks?.Results == null) return new List(); return searchResult.Tracks.Results.Select(t => new YandexTrack { TrackId = t.Id, Title = t.Title, Artists = t.Artists?.Select(a => a.Name).ToList() ?? new List(), CoverUri = t.CoverUri ?? string.Empty, DurationMs = t.DurationMs, }).ToList(); } public async Task> SearchTracksByIdAsync( ApplicationUser user, string id, TrackSearchType searchType, int limit = 20 ) { var client = await CreateClientAsync(user); if (client == null) return new List(); var ySerchType = searchType switch { TrackSearchType.Artist => YandexMusic.API.Models.Common.YSearchType.Artist, TrackSearchType.Album => YandexMusic.API.Models.Common.YSearchType.Album, TrackSearchType.Playlist => YandexMusic.API.Models.Common.YSearchType.Playlist, TrackSearchType.Track => YandexMusic.API.Models.Common.YSearchType.Track, _ => YandexMusic.API.Models.Common.YSearchType.All }; IEnumerable searchResult = searchType switch { TrackSearchType.Playlist => (await client.GetPlaylistAsync(id)).Tracks.Select(t => t.Track), TrackSearchType.Track => (await client.GetTracksAsync([id])), TrackSearchType.Album => (await client.GetAlbumAsync(id)).Volumes.SelectMany(t => t), TrackSearchType.Artist => (await client.GetArtistAsync(id)).Albums.SelectMany(t => t.Volumes.SelectMany(v => v)), _ => new List() }; if (searchType != TrackSearchType.Track) { searchResult = searchResult.Distinct(); if (limit > 0) searchResult = searchResult.Take(limit); } return searchResult.Select(t => new YandexTrack { TrackId = t.Id, Title = t.Title, Artists = t.Artists?.Select(a => a.Name).ToList() ?? new List(), CoverUri = t.CoverUri ?? string.Empty, DurationMs = t.DurationMs, }).ToList(); } }