using Microsoft.AspNetCore.DataProtection; using PlaylistShared.Api.Entities; using PlaylistShared.Api.Extensions; using PlaylistShared.Shared.Enums; using PlaylistShared.Shared.Yandex; 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 SearchAsync( ApplicationUser user, string query, TrackSearchType? searchType = TrackSearchType.All, int limit = 20 ) { var client = await CreateClientAsync(user); if (client == null) return new YandexSearchResult(); 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 YandexSearchResult(); return new YandexSearchResult { Tracks = searchResult.Tracks.Results.Select(t => new YandexTrack { TrackId = t.Id, Title = t.Title, Artists = t.Artists.Select(t => new YandexArtist() { Id = t.Id, Name = t.Name, CoverUrl = t.Cover.GetUrl(), Description = t.Description?.Text ?? string.Empty, }).ToList(), CoverUri = t.CoverUri, DurationMs = t.DurationMs, }).ToList(), Playlists = searchResult.Playlists?.Results.Select(p => new YandexPlaylist { Uuid = p.PlaylistUuid, Kind = p.Kind, OwnerUid = p.Owner?.Uid ?? string.Empty, Title = p.Title, Description = p.Description, CoverUrl = string.IsNullOrEmpty(p.CoverUri) ? p.Cover.GetUrl() : p.CoverUri, TrackCount = p.TrackCount, }).ToList(), Artists = searchResult.Artists?.Results.Select(a => new YandexArtist { Id = a.Id, Name = a.Name, CoverUrl = a.Cover.GetUrl(), Description = a.Description?.Text ?? string.Empty, }).ToList(), Albums = searchResult.Albums?.Results.Select(a => new YandexAlbum { Id = a.Id, Title = a.Title, Artists = a.Artists.Select(t => new YandexArtist() { Id = t.Id, Name = t.Name, CoverUrl = t.Cover.GetUrl(), Description = t.Description?.Text ?? string.Empty, }).ToList(), CoverUrl = string.IsNullOrEmpty(a.CoverUri) ? a.Cover.GetUrl() : a.CoverUri, Description = a.Description, }).ToList(), }; } public async Task SearchTracksByIdAsync( ApplicationUser user, string id, TrackSearchType searchType, int limit = 20 ) { YandexSearchResult result = new(); var client = await CreateClientAsync(user); if (client == null) return result; if (searchType == TrackSearchType.All) { throw new Exception("Для поиска по ID необходимо указать конкретный тип (трек, альбом, исполнитель или плейлист)."); } else if (searchType == TrackSearchType.Track) { var track = await client.GetTrackAsync(id); if (track != null) { result.Tracks = new List() { new() { TrackId = track.Id, Title = track.Title, Artists = track.Artists.Select(t => new YandexArtist() { Id = t.Id, Name = t.Name, CoverUrl = t.Cover.GetUrl(), Description = t.Description?.Text ?? string.Empty, }).ToList(), CoverUri = track.CoverUri ?? string.Empty, DurationMs = track.DurationMs, } }; } } else if (searchType == TrackSearchType.Album) { var album = await client.GetAlbumAsync(id); result.Tracks = album?.Volumes.SelectMany(v => v).Select(t => new YandexTrack { TrackId = t.Id, Title = t.Title, Artists = t.Artists.Select(t => new YandexArtist() { Id = t.Id, Name = t.Name, CoverUrl = t.Cover.GetUrl(), Description = t.Description?.Text ?? string.Empty, }).ToList(), CoverUri = t.CoverUri ?? string.Empty, DurationMs = t.DurationMs, }).ToList(); } else if (searchType == TrackSearchType.Artist) { var artist = await client.GetArtistAsync(id); if (artist != null) { result.Albums = artist.Albums.Select(a => new YandexAlbum() { Id = a.Id, Title = a.Title, Artists = a.Artists.Select(t => new YandexArtist() { Id = t.Id, Name = t.Name, CoverUrl = t.Cover.GetUrl(), Description = t.Description?.Text ?? string.Empty, }).ToList(), CoverUrl = string.IsNullOrEmpty(a.CoverUri) ? a.Cover.GetUrl() : a.CoverUri, Description = a.Description, }).ToList(); result.Playlists = artist.Playlists.Select(p => new YandexPlaylist { Uuid = p.PlaylistUuid, Kind = p.Kind, OwnerUid = p.Owner?.Uid ?? string.Empty, Title = p.Title, Description = p.Description, CoverUrl = p.Cover.GetUrl(), TrackCount = p.TrackCount, }).ToList(); result.Tracks = artist.PopularTracks.Select(t => new YandexTrack { TrackId = t.Id, Title = t.Title, Artists = t.Artists.Select(a => new YandexArtist() { Id = a.Id, Name = a.Name, CoverUrl = a.Cover.GetUrl(), Description = a.Description?.Text ?? string.Empty, }).ToList(), CoverUri = t.CoverUri ?? string.Empty, DurationMs = t.DurationMs, }).ToList(); } } else if (searchType == TrackSearchType.Playlist) { var playlist = await client.GetPlaylistAsync(id); result.Tracks = playlist?.Tracks.Select(p => new YandexTrack { TrackId = p.Track.Id, CoverUri = p.Track.CoverUri, Artists = p.Track.Artists.Select(a => new YandexArtist { Id = a.Id, Name = a.Name, CoverUrl = a.Cover.GetUrl(), Description = a.Description?.Text ?? string.Empty, }).ToList(), Title = p.Track.Title, DurationMs = p.Track.DurationMs, }).ToList(); } return result; } }