@using PlaylistShared.Pwa.Components.Common @using PlaylistShared.Shared.DTO @using PlaylistShared.Shared.Enums @using PlaylistShared.Shared.SharedPlaylist @inject HttpClient Http @inject ISnackbar Snackbar @if (_isSearching) { } else if (_searchResults.Any()) {
@foreach (var track in _searchResults) {
@track.Title @string.Join(", ", track.Artists)
@track.DurationMs.FormatDuration()
}
} else if (!_isFirstSearch) { Ничего не найдено. Попробуйте изменить запрос. }
@code { [Parameter] public string ShareToken { get; set; } = string.Empty; [Parameter] public EventCallback OnTrackAdded { get; set; } [Parameter] public EventCallback OnTrackRemoved { get; set; } private string _searchQuery = ""; private bool _isSearching = false; private bool _isFirstSearch = true; private TrackSearchType _searchType = TrackSearchType.All; private List _searchResults = new(); private HashSet _addingTrackIds = new(); private async Task SearchTracks() { if (string.IsNullOrWhiteSpace(_searchQuery)) { return; } var query = _searchQuery.Trim(); var type = _searchType; bool byId = false; if (Uri.TryCreate(_searchQuery, UriKind.Absolute, out var uri) && uri.Host == "music.yandex.ru") { try { (type, query) = ParseYandexMusicUrl(uri); byId = true; } catch (Exception ex) { Snackbar.Add($"Ошибка распознавания URL: {ex.Message}", Severity.Error); return; } } _isFirstSearch = false; _isSearching = true; try { var url = $"/api/yandexsearch/tracks?query={Uri.EscapeDataString(query)}&type={Uri.EscapeDataString(type.ToString())}&limit=20"; if (!string.IsNullOrEmpty(ShareToken)) url += $"&shared_id={Uri.EscapeDataString(ShareToken)}"; if (byId) url += $"&byId={byId}"; var response = await Http.GetFromJsonAsync>>(url); if (response?.Success == true) _searchResults = response.Data ?? new(); else Snackbar.Add(response?.Error?.Message ?? "Ошибка поиска", Severity.Error); } catch (Exception ex) { Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error); } finally { _isSearching = false; StateHasChanged(); } } private async Task SearchTracksByQuery(string query) { if (string.IsNullOrWhiteSpace(query)) return; } private async Task ToggleTrack(YandexTrack track) { if (_addingTrackIds.Contains(track.TrackId)) { await RemoveTrack(track); } else { await AddTrack(track); } } private async Task RemoveTrack(YandexTrack track) { if (!_addingTrackIds.Remove(track.TrackId)) return; try { await RemoveTrackById(track.TrackId); await OnTrackRemoved.InvokeAsync(); Snackbar.Add($"Трек \"{track.Title}\" добавлен", Severity.Success); } catch (Exception ex) { Snackbar.Add($"Ошибка добавления: {ex.Message}", Severity.Error); _addingTrackIds.Add(track.TrackId); } finally { StateHasChanged(); } } private async Task AddTrack(YandexTrack track) { if (_addingTrackIds.Contains(track.TrackId)) return; _addingTrackIds.Add(track.TrackId); try { await AddTrackById(track.TrackId); await OnTrackAdded.InvokeAsync(); Snackbar.Add($"Трек \"{track.Title}\" добавлен", Severity.Success); } catch (Exception ex) { Snackbar.Add($"Ошибка добавления: {ex.Message}", Severity.Error); _addingTrackIds.Remove(track.TrackId); } finally { StateHasChanged(); } } private async Task AddTrackById(string trackId) { try { var request = new UpdateTrackListRequest { TrackIds = new List { trackId } }; var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{ShareToken}/add-tracks", request); if (response.IsSuccessStatusCode) { Snackbar.Add("Трек успешно добавлен", Severity.Success); await OnTrackAdded.InvokeAsync(); // уведомляем родителя, что список треков изменился } else { var error = await response.Content.ReadFromJsonAsync>(); Snackbar.Add(error?.Error?.Message ?? "Ошибка добавления трека", Severity.Error); } } catch (Exception ex) { Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error); } finally { StateHasChanged(); } } private async Task RemoveTrackById(string trackId) { try { var request = new UpdateTrackListRequest { TrackIds = new List { trackId } }; var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{ShareToken}/remove-tracks", request); if (response.IsSuccessStatusCode) { Snackbar.Add("Трек успешно удален", Severity.Success); await OnTrackAdded.InvokeAsync(); // уведомляем родителя, что список треков изменился } else { var error = await response.Content.ReadFromJsonAsync>(); Snackbar.Add(error?.Error?.Message ?? "Ошибка удаления трека", Severity.Error); } } catch (Exception ex) { Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error); } finally { StateHasChanged(); } } private static (TrackSearchType Type, string Id) ParseYandexMusicUrl(Uri uri) { var segments = uri.AbsolutePath.Split('/', StringSplitOptions.RemoveEmptyEntries); var dataMap = segments .Select((val, idx) => new { val, idx }) .GroupBy(x => x.idx / 2) .ToDictionary( g => g.First().val, g => g.ElementAtOrDefault(1)?.val ); if (dataMap.TryGetValue("track", out var trackId) && !string.IsNullOrEmpty(trackId)) return (TrackSearchType.Track, trackId); if (dataMap.TryGetValue("album", out var albumId) && !string.IsNullOrEmpty(albumId)) return (TrackSearchType.Album, albumId); if (dataMap.TryGetValue("playlist", out var playlistId) && !string.IsNullOrEmpty(playlistId)) return (TrackSearchType.Playlist, playlistId); if (dataMap.TryGetValue("artist", out var artistId) && !string.IsNullOrEmpty(artistId)) return (TrackSearchType.Artist, artistId); throw new ArgumentException("Unsupported URL pattern"); } }