diff --git a/PlaylistShared.Api/Controllers/YandexSearchController.cs b/PlaylistShared.Api/Controllers/YandexSearchController.cs index bd6a162..a7b3a26 100644 --- a/PlaylistShared.Api/Controllers/YandexSearchController.cs +++ b/PlaylistShared.Api/Controllers/YandexSearchController.cs @@ -82,6 +82,6 @@ public class YandexSearchController : ControllerBase results = await _yandexService.SearchAsync(user, query, searchType, limit); } - return Ok(ApiResponse>.Ok(results)); + return Ok(ApiResponse.Ok(results)); } } \ No newline at end of file diff --git a/PlaylistShared.Api/Services/YandexMusicService.cs b/PlaylistShared.Api/Services/YandexMusicService.cs index 7b901c8..4198a6d 100644 --- a/PlaylistShared.Api/Services/YandexMusicService.cs +++ b/PlaylistShared.Api/Services/YandexMusicService.cs @@ -158,7 +158,7 @@ public class YandexMusicService Id = a.Id, Name = a.Name, CoverUrl = a.Cover.GetUrl(), - Description = a.Description.Text, + Description = a.Description?.Text ?? string.Empty, }).ToList(), Albums = searchResult.Albums?.Results.Select(a => new YandexAlbum @@ -170,7 +170,7 @@ public class YandexMusicService Id = t.Id, Name = t.Name, CoverUrl = t.Cover.GetUrl(), - Description = t.Description.Text, + Description = t.Description?.Text ?? string.Empty, }).ToList(), CoverUrl = string.IsNullOrEmpty(a.CoverUri) ? a.Cover.GetUrl() : a.CoverUri, Description = a.Description, diff --git a/PlaylistShared.Pwa/Components/Common/TrackCoverWithPlay.razor b/PlaylistShared.Pwa/Components/Common/TrackCoverWithPlay.razor index a199b5b..5700217 100644 --- a/PlaylistShared.Pwa/Components/Common/TrackCoverWithPlay.razor +++ b/PlaylistShared.Pwa/Components/Common/TrackCoverWithPlay.razor @@ -1,5 +1,6 @@ @using Microsoft.AspNetCore.Components.Web @using PlaylistShared.Shared.DTO +@using PlaylistShared.Shared.Yandex @inject IAudioPlayerService AudioPlayerService diff --git a/PlaylistShared.Pwa/Components/Global/TrackProgress.razor b/PlaylistShared.Pwa/Components/Global/TrackProgress.razor index 9a315e1..bf67915 100644 --- a/PlaylistShared.Pwa/Components/Global/TrackProgress.razor +++ b/PlaylistShared.Pwa/Components/Global/TrackProgress.razor @@ -2,7 +2,7 @@
+ style="--track-height: @(Height)px; height: @(Height)px; --track-opacity: @(Opacity.ToString(System.Globalization.CultureInfo.InvariantCulture));">
@if (Buffer) @@ -23,7 +23,8 @@ min="@Min.ToString(System.Globalization.CultureInfo.InvariantCulture)" max="@Max.ToString(System.Globalization.CultureInfo.InvariantCulture)" step="@Step.ToString(System.Globalization.CultureInfo.InvariantCulture)" - value="@Value.ToString(System.Globalization.CultureInfo.InvariantCulture)" + value="@Value.ToString(System.Globalization.CultureInfo.InvariantCulture)" + height="@Height" @oninput="OnInput" class="progress-input" />
diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackSection.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackSection.razor index 58bc434..f0bad6b 100644 --- a/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackSection.razor +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackSection.razor @@ -1,4 +1,5 @@ @using PlaylistShared.Pwa.Components.Common +@using PlaylistShared.Pwa.Components.SharedPlaylist.Cards @using PlaylistShared.Shared.DTO @using PlaylistShared.Shared.Enums @using PlaylistShared.Shared.SharedPlaylist @@ -9,7 +10,7 @@ - - - + + + + - - - - - - - - - - + + @if (_isSearching) + { + + } + else if (_searchResult != null) + { + + @* Секция исполнителей *@ + @if (ShouldShowSection(TrackSearchType.Artist)) + { + + + @foreach (var artist in _searchResult.Artists!) + { + + + + } + + + } + + @* Секция альбомов *@ + @if (ShouldShowSection(TrackSearchType.Album)) + { + + + @foreach (var album in _searchResult.Albums!) + { + + + + } + + + } + + @* Секция плейлистов *@ + @if (ShouldShowSection(TrackSearchType.Playlist)) + { + + + @foreach (var playlist in _searchResult.Playlists!) + { + + + + } + + + } + + @* Секция треков *@ + @if (ShouldShowSection(TrackSearchType.Track)) + { + + + + + + + + + + + + + } + + } + @code { @@ -63,20 +125,44 @@ private bool _isSearching = false; private bool _isFirstSearch = true; private TrackSearchType _searchType = TrackSearchType.All; - private List _searchResults = new(); + private YandexSearchResult? _searchResult = null; - private async Task SearchTracks() - { - if (string.IsNullOrWhiteSpace(_searchQuery)) + private bool ShouldShowSection(TrackSearchType sectionType) + { + return sectionType switch { + TrackSearchType.Track => _searchResult?.Tracks?.Any() == true, + TrackSearchType.Album => _searchResult?.Albums?.Any() == true, + TrackSearchType.Playlist => _searchResult?.Playlists?.Any() == true, + TrackSearchType.Artist => _searchResult?.Artists?.Any() == true, + _ => false + }; + } + + private async Task OnSearchQueryChanged() + { + await SearchTracks(byId: false); + } + + private async Task OnSearchTypeChanged() + { + await SearchTracks(byId: false); + } + + private async Task SearchTracks(bool byId = false, string? forcedQuery = null) + { + var query = forcedQuery ?? _searchQuery; + if (string.IsNullOrWhiteSpace(query)) + { + _searchResult = null; + _isFirstSearch = true; 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") + // Распознавание ссылки Яндекс.Музыки + if (!byId && Uri.TryCreate(query, UriKind.Absolute, out var uri) && uri.Host == "music.yandex.ru") { try { @@ -92,56 +178,59 @@ _isFirstSearch = false; _isSearching = true; + _searchResult = null; + StateHasChanged(); + try { - var url = $"/api/yandexsearch/tracks?query={Uri.EscapeDataString(query)}&searchType={Uri.EscapeDataString(type.ToString())}&limit=20"; + var url = $"/api/yandexsearch/search?query={Uri.EscapeDataString(query)}&searchType={Uri.EscapeDataString(type.ToString())}&limit=20"; + if (byId) + url += "&byId=true"; if (!string.IsNullOrEmpty(ShareToken)) url += $"&shared_id={Uri.EscapeDataString(ShareToken)}"; - if (byId) - url += $"&byId={byId}"; - var response = await Http.GetFromJsonAsync>>(url); + var response = await Http.GetFromJsonAsync>(url); if (response?.Success == true) - _searchResults = response.Data ?? new(); + { + _searchResult = response.Data ?? new YandexSearchResult(); + } else + { Snackbar.Add(response?.Error?.Message ?? "Ошибка поиска", Severity.Error); + _searchResult = null; + } } catch (Exception ex) { Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error); + _searchResult = null; } finally { _isSearching = false; StateHasChanged(); } - } - private async Task SearchTracksByQuery(string query) + private async Task SearchTracksByEntity(string entityId, TrackSearchType entityType) { - if (string.IsNullOrWhiteSpace(query)) - return; - + // Переключаем тип на треки и ищем по ID + _searchType = TrackSearchType.Track; + await SearchTracks(byId: true, forcedQuery: entityId); } private async Task ToggleTrack(YandexTrack track) { if (ExistingTrackIds.Contains(track.TrackId)) - { await RemoveTrack(track); - } else - { await AddTrack(track); - } } private async Task RemoveTrack(YandexTrack track) { if (!ExistingTrackIds.Remove(track.TrackId)) return; - try { await RemoveTrackById(track.TrackId); @@ -165,7 +254,7 @@ var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{ShareToken}/remove-tracks", request); if (response.IsSuccessStatusCode) { - await OnTrackAdded.InvokeAsync(); // уведомляем родителя, что список треков изменился + await OnTrackAdded.InvokeAsync(); } else { @@ -202,7 +291,7 @@ var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{ShareToken}/add-tracks", request); if (response.IsSuccessStatusCode) { - await OnTrackAdded.InvokeAsync(); // уведомляем родителя, что список треков изменился + await OnTrackAdded.InvokeAsync(); } else { diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/AlbumCard.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/AlbumCard.razor new file mode 100644 index 0000000..773219c --- /dev/null +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/AlbumCard.razor @@ -0,0 +1,24 @@ +@using PlaylistShared.Shared.Yandex + + + @if (!string.IsNullOrEmpty(Item.CoverUrl)) + { + + } + else + { + + + + } + @Item.Title + + @string.Join(", ", Item.Artists.Select(a => a.Name)) + + + +@code { + [Parameter] public YandexAlbum Item { get; set; } = null!; + [Parameter] public EventCallback OnClick { get; set; } + [Parameter] public int Size { get; set; } = 50; +} \ No newline at end of file diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/ArtistCard.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/ArtistCard.razor new file mode 100644 index 0000000..3076905 --- /dev/null +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/ArtistCard.razor @@ -0,0 +1,21 @@ +@using PlaylistShared.Shared.Yandex + + + @if (!string.IsNullOrEmpty(Item.CoverUrl)) + { + + } + else + { + + + + } + @Item.Name + + +@code { + [Parameter] public YandexArtist Item { get; set; } = null!; + [Parameter] public EventCallback OnClick { get; set; } + [Parameter] public int Size { get; set; } = 50; +} \ No newline at end of file diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/PlaylistCard.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/PlaylistCard.razor new file mode 100644 index 0000000..24c08b5 --- /dev/null +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/Cards/PlaylistCard.razor @@ -0,0 +1,21 @@ +@using PlaylistShared.Shared.Yandex + + + @if (!string.IsNullOrEmpty(Item.CoverUrl)) + { + + } + else + { + + + + } + @Item.Title + + +@code { + [Parameter] public YandexPlaylist Item { get; set; } = null!; + [Parameter] public EventCallback OnClick { get; set; } + [Parameter] public int Size { get; set; } = 50; +} \ No newline at end of file diff --git a/PlaylistShared.Pwa/Pages/MyPlaylists.razor b/PlaylistShared.Pwa/Pages/MyPlaylists.razor index 3d9cb5a..6c73326 100644 --- a/PlaylistShared.Pwa/Pages/MyPlaylists.razor +++ b/PlaylistShared.Pwa/Pages/MyPlaylists.razor @@ -3,7 +3,8 @@ @attribute [Authorize] @using PlaylistShared.Shared.DTO -@using PlaylistShared.Shared.Playlist +@using PlaylistShared.Shared.SharedPlaylist +@using PlaylistShared.Shared.Yandex @inject HttpClient Http @inject ISnackbar Snackbar @inject NavigationManager Navigation @@ -70,11 +71,11 @@ @code { - private List _playlists; + private List _playlists; private bool _loading = true; private bool _showOnlyShared = false; - private List FilteredPlaylists => _showOnlyShared ? _playlists?.Where(p => p.IsShared).ToList() : _playlists; + private List FilteredPlaylists => _showOnlyShared ? _playlists?.Where(p => p.IsShared).ToList() : _playlists; protected override async Task OnInitializedAsync() { @@ -86,7 +87,7 @@ _loading = true; try { - var response = await Http.GetFromJsonAsync>>("/api/playlists"); + var response = await Http.GetFromJsonAsync>>("/api/playlists"); if (response?.Success == true) _playlists = response.Data; else @@ -103,7 +104,7 @@ } } - private async Task SharePlaylist(YandexPlaylistInfo playlist) + private async Task SharePlaylist(YandexPlaylistShare playlist) { var request = new SharePlaylistRequest { Kind = playlist.Kind, OwnerUid = playlist.OwnerUid }; var response = await Http.PostAsJsonAsync("/api/playlists/share", request); @@ -118,7 +119,7 @@ } } - private void GoToShared(YandexPlaylistInfo playlist) + private void GoToShared(YandexPlaylistShare playlist) { if (!string.IsNullOrEmpty(playlist.ShareToken)) Navigation.NavigateTo($"/shared/{playlist.ShareToken}"); diff --git a/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor b/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor index 9e152f5..82a8a8d 100644 --- a/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor +++ b/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor @@ -7,6 +7,7 @@ @using PlaylistShared.Shared.Enums @using PlaylistShared.Pwa.Services @using PlaylistShared.Shared.SharedPlaylist +@using PlaylistShared.Shared.Yandex @inject HttpClient Http @inject ISnackbar Snackbar @inject NavigationManager Navigation