From e00b7a735c9778779867728ded5674e5132da951 Mon Sep 17 00:00:00 2001 From: FrigaT Date: Wed, 15 Apr 2026 14:07:33 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=BE=D1=82=D0=B0=D0=BD=D0=B0=20=D1=81=D1=82=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=86=D0=B0=20=D0=BF=D0=BE=D0=B8=D1=81=D0=BA=D0=B0=20?= =?UTF-8?q?=D0=B8=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D1=8F=20=D1=82=D1=80=D0=B5=D0=BA=D0=BE=D0=B2=20=D0=B2=20=D0=BF?= =?UTF-8?q?=D0=BB=D0=B5=D0=B9=D0=BB=D0=B8=D1=81=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/FavoritesController.cs | 2 +- .../Controllers/PlaylistsController.cs | 2 +- .../Controllers/SharedPlaylistController.cs | 23 +- .../Controllers/YandexSearchController.cs | 17 +- .../Services/FavoritesService.cs | 2 +- .../Services/SharedPlaylistService.cs | 2 +- .../Services/YandexMusicService.cs | 63 +++- .../Profile/YandexTokenInstructions.razor | 8 +- .../SharedPlaylist/AddTrackBySearch.razor | 93 +++--- .../SharedPlaylist/AddTrackSection.razor | 281 ++++++++++++++---- .../SharedPlaylist/PermissionsDialog.razor | 2 +- .../SharedPlaylist/PlaylistHeader.razor | 14 +- .../SharedPlaylist/TracksTable.razor | 4 +- PlaylistShared.Pwa/Pages/Favorites.razor | 2 +- PlaylistShared.Pwa/Pages/Login.razor | 2 +- PlaylistShared.Pwa/Pages/Profile.razor | 4 +- .../Pages/SharedPlaylistView.razor | 9 +- .../Services/Api/SharedPlaylistClient.cs | 101 +++++++ .../DTO/RemoveTracksRequest.cs | 9 - PlaylistShared.Shared/Enums/EditPermission.cs | 2 +- .../Enums/TrackSearchType.cs | 13 + .../AddTrackByLinkRequest.cs | 2 +- .../SharedPlaylistDto.cs | 2 +- .../UpdatePermissionsDto.cs | 2 +- .../UpdateTrackListRequest.cs} | 4 +- .../YandexPlaylistData.cs | 2 +- 26 files changed, 497 insertions(+), 170 deletions(-) create mode 100644 PlaylistShared.Pwa/Services/Api/SharedPlaylistClient.cs delete mode 100644 PlaylistShared.Shared/DTO/RemoveTracksRequest.cs create mode 100644 PlaylistShared.Shared/Enums/TrackSearchType.cs rename PlaylistShared.Shared/{Shared => SharedPlaylist}/AddTrackByLinkRequest.cs (59%) rename PlaylistShared.Shared/{Shared => SharedPlaylist}/SharedPlaylistDto.cs (98%) rename PlaylistShared.Shared/{Shared => SharedPlaylist}/UpdatePermissionsDto.cs (95%) rename PlaylistShared.Shared/{Shared/AddTracksRequest.cs => SharedPlaylist/UpdateTrackListRequest.cs} (61%) rename PlaylistShared.Shared/{Shared => SharedPlaylist}/YandexPlaylistData.cs (82%) diff --git a/PlaylistShared.Api/Controllers/FavoritesController.cs b/PlaylistShared.Api/Controllers/FavoritesController.cs index f10de23..3c0d3a2 100644 --- a/PlaylistShared.Api/Controllers/FavoritesController.cs +++ b/PlaylistShared.Api/Controllers/FavoritesController.cs @@ -5,7 +5,7 @@ using PlaylistShared.Api.Entities; using PlaylistShared.Api.Extensions; using PlaylistShared.Api.Services; using PlaylistShared.Shared; -using PlaylistShared.Shared.Shared; +using PlaylistShared.Shared.SharedPlaylist; namespace PlaylistShared.Api.Controllers; diff --git a/PlaylistShared.Api/Controllers/PlaylistsController.cs b/PlaylistShared.Api/Controllers/PlaylistsController.cs index aeea4de..91d8734 100644 --- a/PlaylistShared.Api/Controllers/PlaylistsController.cs +++ b/PlaylistShared.Api/Controllers/PlaylistsController.cs @@ -7,7 +7,7 @@ using PlaylistShared.Api.Services; using PlaylistShared.Shared; using PlaylistShared.Shared.Enums; using PlaylistShared.Shared.Playlist; -using PlaylistShared.Shared.Shared; +using PlaylistShared.Shared.SharedPlaylist; using YandexMusic; namespace PlaylistShared.Api.Controllers; diff --git a/PlaylistShared.Api/Controllers/SharedPlaylistController.cs b/PlaylistShared.Api/Controllers/SharedPlaylistController.cs index c37f758..a48c311 100644 --- a/PlaylistShared.Api/Controllers/SharedPlaylistController.cs +++ b/PlaylistShared.Api/Controllers/SharedPlaylistController.cs @@ -6,7 +6,7 @@ using PlaylistShared.Api.Extensions; using PlaylistShared.Api.Services; using PlaylistShared.Shared; using PlaylistShared.Shared.DTO; -using PlaylistShared.Shared.Shared; +using PlaylistShared.Shared.SharedPlaylist; using YandexMusic.API.Models.Playlist; [ApiController] @@ -101,7 +101,7 @@ public class SharedPlaylistController : ControllerBase // POST /api/sharedplaylist/{token}/add-tracks [HttpPost("{token}/add-tracks")] - public async Task>> AddTracks(string token, [FromBody] AddTracksRequest request) + public async Task>> AddTracks(string token, [FromBody] UpdateTrackListRequest request) { var currentUserId = User.GetUserIdOrNull(); var playlist = await _sharedService.GetEntityByTokenAsync(token); @@ -131,7 +131,7 @@ public class SharedPlaylistController : ControllerBase // POST /api/sharedplaylist/{token}/remove-tracks [HttpPost("{token}/remove-tracks")] - public async Task>> RemoveTracks(string token, [FromBody] RemoveTracksRequest request) + public async Task>> RemoveTracks(string token, [FromBody] UpdateTrackListRequest request) { var currentUserId = User.GetUserIdOrNull(); var playlist = await _sharedService.GetEntityByTokenAsync(token); @@ -164,23 +164,6 @@ public class SharedPlaylistController : ControllerBase return Ok(ApiResponse.Ok(new { message = "Треки удалены" })); } - // POST /api/sharedplaylist/{token}/add-track-by-link - [HttpPost("{token}/add-track-by-link")] - public async Task>> AddTrackByLink(string token, [FromBody] AddTrackByLinkRequest request) - { - var trackId = ExtractTrackIdFromLink(request.Link); - if (string.IsNullOrEmpty(trackId)) - return BadRequest(ApiResponse.Fail(new ErrorResponse { StatusCode = 400, Message = "Неверный формат ссылки" })); - - return await AddTracks(token, new AddTracksRequest { TrackIds = new List { trackId } }); - } - - private string? ExtractTrackIdFromLink(string link) - { - var match = System.Text.RegularExpressions.Regex.Match(link, @"/track/(\d+)"); - return match.Success ? match.Groups[1].Value : null; - } - private YandexPlaylistData MapToYandexPlaylistData(YPlaylist playlist) { return new YandexPlaylistData diff --git a/PlaylistShared.Api/Controllers/YandexSearchController.cs b/PlaylistShared.Api/Controllers/YandexSearchController.cs index 94922ac..32ce438 100644 --- a/PlaylistShared.Api/Controllers/YandexSearchController.cs +++ b/PlaylistShared.Api/Controllers/YandexSearchController.cs @@ -6,6 +6,7 @@ using PlaylistShared.Api.Extensions; using PlaylistShared.Api.Services; using PlaylistShared.Shared; using PlaylistShared.Shared.DTO; +using PlaylistShared.Shared.Enums; namespace PlaylistShared.Api.Controllers; @@ -26,9 +27,11 @@ public class YandexSearchController : ControllerBase } [HttpGet("tracks")] - public async Task>>> SearchTracks( + public async Task>>> SearchQuery( [FromQuery] string query, [FromQuery] int limit = 20, + [FromQuery] TrackSearchType? searchType = TrackSearchType.All, + [FromQuery] bool byId = false, [FromQuery] string? shared_id = null) { if (string.IsNullOrWhiteSpace(query)) @@ -68,7 +71,17 @@ public class YandexSearchController : ControllerBase Message = "Токен Яндекс.Музыки не установлен или недействителен." })); - var results = await _yandexService.SearchTracksAsync(user, query, limit); + List? results = null; + + if (byId) + { + results = await _yandexService.SearchTracksByIdAsync(user, query, searchType.Value, limit); + } + else + { + results = await _yandexService.SearchTracksAsync(user, query, searchType, limit); + } + return Ok(ApiResponse>.Ok(results)); } } \ No newline at end of file diff --git a/PlaylistShared.Api/Services/FavoritesService.cs b/PlaylistShared.Api/Services/FavoritesService.cs index 7ff1531..7b0bd2f 100644 --- a/PlaylistShared.Api/Services/FavoritesService.cs +++ b/PlaylistShared.Api/Services/FavoritesService.cs @@ -1,7 +1,7 @@ using Microsoft.EntityFrameworkCore; using PlaylistShared.Api.Data; using PlaylistShared.Api.Entities; -using PlaylistShared.Shared.Shared; +using PlaylistShared.Shared.SharedPlaylist; namespace PlaylistShared.Api.Services; diff --git a/PlaylistShared.Api/Services/SharedPlaylistService.cs b/PlaylistShared.Api/Services/SharedPlaylistService.cs index 49effcd..dba6c5c 100644 --- a/PlaylistShared.Api/Services/SharedPlaylistService.cs +++ b/PlaylistShared.Api/Services/SharedPlaylistService.cs @@ -4,7 +4,7 @@ using PlaylistShared.Api.Entities; using PlaylistShared.Shared.Auth; using PlaylistShared.Shared.Enums; using PlaylistShared.Shared.Playlist; -using PlaylistShared.Shared.Shared; +using PlaylistShared.Shared.SharedPlaylist; namespace PlaylistShared.Api.Services; diff --git a/PlaylistShared.Api/Services/YandexMusicService.cs b/PlaylistShared.Api/Services/YandexMusicService.cs index cb1f783..42ef1eb 100644 --- a/PlaylistShared.Api/Services/YandexMusicService.cs +++ b/PlaylistShared.Api/Services/YandexMusicService.cs @@ -1,6 +1,7 @@ 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; @@ -107,12 +108,26 @@ public class YandexMusicService } } - public async Task> SearchTracksAsync(ApplicationUser user, string query, int limit = 20) + 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 searchResult = await client.SearchAsync(query, YandexMusic.API.Models.Common.YSearchType.Track, page: 0, pageSize: limit); + 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 @@ -124,4 +139,48 @@ public class YandexMusicService 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(); + } } \ No newline at end of file diff --git a/PlaylistShared.Pwa/Components/Profile/YandexTokenInstructions.razor b/PlaylistShared.Pwa/Components/Profile/YandexTokenInstructions.razor index 4539dc3..bf35fc0 100644 --- a/PlaylistShared.Pwa/Components/Profile/YandexTokenInstructions.razor +++ b/PlaylistShared.Pwa/Components/Profile/YandexTokenInstructions.razor @@ -7,28 +7,28 @@ - + 1 Перейдите по ссылке - + 1 Авторизуйтесь в Яндексе (если ещё не вошли) - + 1 Нажмите «Разрешить» - + 1 Скопируйте access_token из адресной строки после перенаправления diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackBySearch.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackBySearch.razor index a47980f..4ac68a5 100644 --- a/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackBySearch.razor +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackBySearch.razor @@ -3,65 +3,56 @@ @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() -
-
- -
+
+ @foreach (var track in _searchResults) + { +
+
+
- } -
+
+ @track.Title + @string.Join(", ", track.Artists) +
+
+ @track.DurationMs.FormatDuration() +
+
+ +
+
+ } +
} else if (!_isFirstSearch) { - Ничего не найдено. Попробуйте изменить запрос. + Ничего не найдено. Попробуйте изменить запрос. } - + @code { [Parameter] public EventCallback OnAddTrack { get; set; } @@ -75,15 +66,11 @@ private async Task SearchTracks() { - - if (string.IsNullOrWhiteSpace(_searchQuery)) - return; - _isFirstSearch = false; _isSearching = true; try { - var url = $"/api/yandexsearch/tracks?query={Uri.EscapeDataString(_searchQuery)}&limit=20"; + var url = $"/api/yandexsearch/query?query={Uri.EscapeDataString(_searchQuery)}&limit=20"; if (!string.IsNullOrEmpty(ShareToken)) url += $"&shared_id={Uri.EscapeDataString(ShareToken)}"; diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackSection.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackSection.razor index 88d99b7..93c692e 100644 --- a/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackSection.razor +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/AddTrackSection.razor @@ -1,73 +1,210 @@ -@using PlaylistShared.Shared.Shared +@using PlaylistShared.Pwa.Components.Common +@using PlaylistShared.Shared.DTO +@using PlaylistShared.Shared.Enums +@using PlaylistShared.Shared.SharedPlaylist @inject HttpClient Http @inject ISnackbar Snackbar - - - - - - - - - @if (_addingTrack) - { - - } - else - { - Добавить - } - - - - - - - - + + + + + + + + + + + @if (_isSearching) + { + + } + else if (_searchResults.Any()) + { +
+ @foreach (var track in _searchResults) + { +
+
+ +
+
+ @track.Title + @string.Join(", ", track.Artists) +
+
+ @track.DurationMs.FormatDuration() +
+
+ +
+
+ } +
+ } + else if (!_isFirstSearch) + { + Ничего не найдено. Попробуйте изменить запрос. + } +
@code { - private int _activeTabIndex = 0; - private string _trackLink = ""; - private bool _addingTrack = false; - [Parameter] public string ShareToken { get; set; } = string.Empty; [Parameter] public EventCallback OnTrackAdded { get; set; } + [Parameter] public EventCallback OnTrackRemoved { get; set; } - private async Task AddTrackByLink() + 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(_trackLink)) + if (string.IsNullOrWhiteSpace(_searchQuery)) { - Snackbar.Add("Введите ссылку на трек", Severity.Warning); return; } - var trackId = ExtractTrackIdFromLink(_trackLink); - if (string.IsNullOrEmpty(trackId)) + var query = _searchQuery.Trim(); + var type = _searchType; + bool byId = false; + + if (Uri.TryCreate(_searchQuery, UriKind.Absolute, out var uri) && uri.Host == "music.yandex.ru") { - Snackbar.Add("Неверный формат ссылки", Severity.Warning); - return; + try + { + (type, query) = ParseYandexMusicUrl(uri); + byId = true; + } + catch (Exception ex) + { + Snackbar.Add($"Ошибка распознавания URL: {ex.Message}", Severity.Error); + return; + } } - await AddTrackById(trackId); - if (!_addingTrack) // если не было ошибки - _trackLink = ""; + _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) { - _addingTrack = true; try { - var request = new AddTracksRequest { TrackIds = new List { trackId } }; + var request = new UpdateTrackListRequest { TrackIds = new List { trackId } }; var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{ShareToken}/add-tracks", request); if (response.IsSuccessStatusCode) { @@ -86,14 +223,58 @@ } finally { - _addingTrack = false; StateHasChanged(); } } - private string? ExtractTrackIdFromLink(string link) + private async Task RemoveTrackById(string trackId) { - var match = System.Text.RegularExpressions.Regex.Match(link, @"/track/(\d+)"); - return match.Success ? match.Groups[1].Value : null; + 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"); } } \ No newline at end of file diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/PermissionsDialog.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/PermissionsDialog.razor index 8e33cd1..a583bb2 100644 --- a/PlaylistShared.Pwa/Components/SharedPlaylist/PermissionsDialog.razor +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/PermissionsDialog.razor @@ -1,5 +1,5 @@ @using PlaylistShared.Shared.Enums -@using PlaylistShared.Shared.Shared +@using PlaylistShared.Shared.SharedPlaylist @inject HttpClient Http @inject ISnackbar Snackbar diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/PlaylistHeader.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/PlaylistHeader.razor index 8a79e2b..4539f0f 100644 --- a/PlaylistShared.Pwa/Components/SharedPlaylist/PlaylistHeader.razor +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/PlaylistHeader.razor @@ -1,20 +1,20 @@ @using PlaylistShared.Pwa.Components.Common -@using PlaylistShared.Shared.Shared @using PlaylistShared.Shared.Enums @using System.Security.Claims +@using PlaylistShared.Shared.SharedPlaylist @inject HttpClient Http @inject NavigationManager Navigation @inject AuthenticationStateProvider AuthProvider @inject ISnackbar Snackbar @inject IDialogService DialogService -
+ @if (!string.IsNullOrEmpty(Playlist?.CoverUrl)) { } -
-
+ + } -
+ Владелец: @Playlist?.Creator?.UserName -
-
+ + @code { [Parameter] public SharedPlaylistDto? Playlist { get; set; } diff --git a/PlaylistShared.Pwa/Components/SharedPlaylist/TracksTable.razor b/PlaylistShared.Pwa/Components/SharedPlaylist/TracksTable.razor index d068f5f..ced4279 100644 --- a/PlaylistShared.Pwa/Components/SharedPlaylist/TracksTable.razor +++ b/PlaylistShared.Pwa/Components/SharedPlaylist/TracksTable.razor @@ -1,6 +1,6 @@ @using PlaylistShared.Pwa.Components.Common @using PlaylistShared.Shared.DTO -@using PlaylistShared.Shared.Shared +@using PlaylistShared.Shared.SharedPlaylist @inject HttpClient Http @inject ISnackbar Snackbar @inject IDialogService DialogService @@ -119,7 +119,7 @@ try { - var request = new RemoveTracksRequest { TrackIds = new List { track.TrackId } }; + var request = new UpdateTrackListRequest { TrackIds = new List { track.TrackId } }; var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{ShareToken}/remove-tracks", request); if (response.IsSuccessStatusCode) { diff --git a/PlaylistShared.Pwa/Pages/Favorites.razor b/PlaylistShared.Pwa/Pages/Favorites.razor index 8457793..37910c5 100644 --- a/PlaylistShared.Pwa/Pages/Favorites.razor +++ b/PlaylistShared.Pwa/Pages/Favorites.razor @@ -1,5 +1,5 @@ @page "/favorites" -@using PlaylistShared.Shared.Shared +@using PlaylistShared.Shared.SharedPlaylist @attribute [Authorize] @inject HttpClient Http @inject ISnackbar Snackbar diff --git a/PlaylistShared.Pwa/Pages/Login.razor b/PlaylistShared.Pwa/Pages/Login.razor index 0f6aa52..53408c4 100644 --- a/PlaylistShared.Pwa/Pages/Login.razor +++ b/PlaylistShared.Pwa/Pages/Login.razor @@ -25,7 +25,7 @@ - + Войти (локально) diff --git a/PlaylistShared.Pwa/Pages/Profile.razor b/PlaylistShared.Pwa/Pages/Profile.razor index 80aaf24..36dfc86 100644 --- a/PlaylistShared.Pwa/Pages/Profile.razor +++ b/PlaylistShared.Pwa/Pages/Profile.razor @@ -14,7 +14,7 @@ -
+ Здесь вы можете указать токен доступа к Яндекс.Музыке. @@ -22,7 +22,7 @@ Color="Color.Info" OnClick="() => _instructionDrawerOpen = true" Title="Как получить токен?" /> -
+ diff --git a/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor b/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor index 799a19b..47d8269 100644 --- a/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor +++ b/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor @@ -3,7 +3,7 @@ @using PlaylistShared.Shared.DTO @using PlaylistShared.Shared.Enums @using PlaylistShared.Pwa.Services -@using PlaylistShared.Shared.Shared +@using PlaylistShared.Shared.SharedPlaylist @inject HttpClient Http @inject ISnackbar Snackbar @inject NavigationManager Navigation @@ -34,14 +34,15 @@ { } -
+ Треки -
+ + /// GET /api/sharedplaylist/{token} + /// + public async Task> GetAsync(string token) + { + var response = await _http.GetFromJsonAsync>($"/api/sharedplaylist/{token}"); + return response ?? ApiResponse.Fail(new ErrorResponse { Message = "Invalid response" }); + } + + /// + /// GET /api/sharedplaylist/{token}/tracks + /// + public async Task> GetTracksAsync(string token) + { + var response = await _http.GetFromJsonAsync>($"/api/sharedplaylist/{token}/tracks"); + return response ?? ApiResponse.Fail(new ErrorResponse { Message = "Invalid response" }); + } + + /// + /// PUT /api/sharedplaylist/{token}/permissions + /// + public async Task> UpdatePermissionsAsync(string token, UpdatePermissionsDto permissions) + { + var response = await _http.PutAsJsonAsync($"/api/sharedplaylist/{token}/permissions", permissions); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync>(); + return result ?? ApiResponse.Fail(new ErrorResponse { Message = "Invalid response" }); + } + else + { + var error = await response.Content.ReadFromJsonAsync>(); + return error ?? ApiResponse.Fail(new ErrorResponse + { + StatusCode = (int)response.StatusCode, + Message = response.ReasonPhrase ?? "Unknown error" + }); + } + } + + /// + /// POST /api/sharedplaylist/{token}/add-tracks + /// + public async Task> AddTracksAsync(string token, List trackIds) + { + var request = new UpdateTrackListRequest { TrackIds = trackIds }; + var response = await _http.PostAsJsonAsync($"/api/sharedplaylist/{token}/add-tracks", request); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync>(); + return result ?? ApiResponse.Fail(new ErrorResponse { Message = "Invalid response" }); + } + else + { + var error = await response.Content.ReadFromJsonAsync>(); + return error ?? ApiResponse.Fail(new ErrorResponse + { + StatusCode = (int)response.StatusCode, + Message = response.ReasonPhrase ?? "Unknown error" + }); + } + } + + /// + /// POST /api/sharedplaylist/{token}/remove-tracks + /// + public async Task> RemoveTracksAsync(string token, List trackIds) + { + var request = new UpdateTrackListRequest { TrackIds = trackIds }; + var response = await _http.PostAsJsonAsync($"/api/sharedplaylist/{token}/remove-tracks", request); + if (response.IsSuccessStatusCode) + { + var result = await response.Content.ReadFromJsonAsync>(); + return result ?? ApiResponse.Fail(new ErrorResponse { Message = "Invalid response" }); + } + else + { + var error = await response.Content.ReadFromJsonAsync>(); + return error ?? ApiResponse.Fail(new ErrorResponse + { + StatusCode = (int)response.StatusCode, + Message = response.ReasonPhrase ?? "Unknown error" + }); + } + } +} \ No newline at end of file diff --git a/PlaylistShared.Shared/DTO/RemoveTracksRequest.cs b/PlaylistShared.Shared/DTO/RemoveTracksRequest.cs deleted file mode 100644 index a0d1b10..0000000 --- a/PlaylistShared.Shared/DTO/RemoveTracksRequest.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Text.Json.Serialization; - -namespace PlaylistShared.Shared.DTO; - -public class RemoveTracksRequest -{ - [JsonPropertyName("trackIds")] - public List TrackIds { get; set; } = new(); -} diff --git a/PlaylistShared.Shared/Enums/EditPermission.cs b/PlaylistShared.Shared/Enums/EditPermission.cs index 92bd9bf..a11ae80 100644 --- a/PlaylistShared.Shared/Enums/EditPermission.cs +++ b/PlaylistShared.Shared/Enums/EditPermission.cs @@ -14,4 +14,4 @@ public enum EditPermission /// Только тот пользователь, который добавил трек (актуально для удаления). AddedByUserOnly, -} \ No newline at end of file +} diff --git a/PlaylistShared.Shared/Enums/TrackSearchType.cs b/PlaylistShared.Shared/Enums/TrackSearchType.cs new file mode 100644 index 0000000..00aa045 --- /dev/null +++ b/PlaylistShared.Shared/Enums/TrackSearchType.cs @@ -0,0 +1,13 @@ +namespace PlaylistShared.Shared.Enums; + +/// +/// Типы поиска треков в Яндекс.Музыке, которые можно указать при поисковом запросе. +/// +public enum TrackSearchType +{ + All, + Artist, + Album, + Playlist, + Track, +} \ No newline at end of file diff --git a/PlaylistShared.Shared/Shared/AddTrackByLinkRequest.cs b/PlaylistShared.Shared/SharedPlaylist/AddTrackByLinkRequest.cs similarity index 59% rename from PlaylistShared.Shared/Shared/AddTrackByLinkRequest.cs rename to PlaylistShared.Shared/SharedPlaylist/AddTrackByLinkRequest.cs index a468262..dd5adc1 100644 --- a/PlaylistShared.Shared/Shared/AddTrackByLinkRequest.cs +++ b/PlaylistShared.Shared/SharedPlaylist/AddTrackByLinkRequest.cs @@ -1,4 +1,4 @@ -namespace PlaylistShared.Shared.Shared; +namespace PlaylistShared.Shared.SharedPlaylist; public class AddTrackByLinkRequest { diff --git a/PlaylistShared.Shared/Shared/SharedPlaylistDto.cs b/PlaylistShared.Shared/SharedPlaylist/SharedPlaylistDto.cs similarity index 98% rename from PlaylistShared.Shared/Shared/SharedPlaylistDto.cs rename to PlaylistShared.Shared/SharedPlaylist/SharedPlaylistDto.cs index 19a6514..ca34eb7 100644 --- a/PlaylistShared.Shared/Shared/SharedPlaylistDto.cs +++ b/PlaylistShared.Shared/SharedPlaylist/SharedPlaylistDto.cs @@ -2,7 +2,7 @@ using PlaylistShared.Shared.Enums; using System.Text.Json.Serialization; -namespace PlaylistShared.Shared.Shared; +namespace PlaylistShared.Shared.SharedPlaylist; /// DTO шеринг-плейлиста (без навигационных свойств). public class SharedPlaylistDto diff --git a/PlaylistShared.Shared/Shared/UpdatePermissionsDto.cs b/PlaylistShared.Shared/SharedPlaylist/UpdatePermissionsDto.cs similarity index 95% rename from PlaylistShared.Shared/Shared/UpdatePermissionsDto.cs rename to PlaylistShared.Shared/SharedPlaylist/UpdatePermissionsDto.cs index 281ee82..02a6801 100644 --- a/PlaylistShared.Shared/Shared/UpdatePermissionsDto.cs +++ b/PlaylistShared.Shared/SharedPlaylist/UpdatePermissionsDto.cs @@ -1,7 +1,7 @@ using PlaylistShared.Shared.Enums; using System.Text.Json.Serialization; -namespace PlaylistShared.Shared.Shared; +namespace PlaylistShared.Shared.SharedPlaylist; /// Запрос на обновление прав доступа шеринг-плейлиста. public class UpdatePermissionsDto diff --git a/PlaylistShared.Shared/Shared/AddTracksRequest.cs b/PlaylistShared.Shared/SharedPlaylist/UpdateTrackListRequest.cs similarity index 61% rename from PlaylistShared.Shared/Shared/AddTracksRequest.cs rename to PlaylistShared.Shared/SharedPlaylist/UpdateTrackListRequest.cs index 49402ff..a1e5453 100644 --- a/PlaylistShared.Shared/Shared/AddTracksRequest.cs +++ b/PlaylistShared.Shared/SharedPlaylist/UpdateTrackListRequest.cs @@ -1,8 +1,8 @@ using System.Text.Json.Serialization; -namespace PlaylistShared.Shared.Shared; +namespace PlaylistShared.Shared.SharedPlaylist; -public class AddTracksRequest +public class UpdateTrackListRequest { [JsonPropertyName("trackIds")] public List TrackIds { get; set; } = new(); diff --git a/PlaylistShared.Shared/Shared/YandexPlaylistData.cs b/PlaylistShared.Shared/SharedPlaylist/YandexPlaylistData.cs similarity index 82% rename from PlaylistShared.Shared/Shared/YandexPlaylistData.cs rename to PlaylistShared.Shared/SharedPlaylist/YandexPlaylistData.cs index eeca066..6095391 100644 --- a/PlaylistShared.Shared/Shared/YandexPlaylistData.cs +++ b/PlaylistShared.Shared/SharedPlaylist/YandexPlaylistData.cs @@ -1,6 +1,6 @@ using PlaylistShared.Shared.DTO; -namespace PlaylistShared.Shared.Shared; +namespace PlaylistShared.Shared.SharedPlaylist; public class YandexPlaylistData {