diff --git a/PlaylistShared.Api/Controllers/PlaylistController.cs b/PlaylistShared.Api/Controllers/PlaylistsController.cs similarity index 57% rename from PlaylistShared.Api/Controllers/PlaylistController.cs rename to PlaylistShared.Api/Controllers/PlaylistsController.cs index beeab3a..883f6b1 100644 --- a/PlaylistShared.Api/Controllers/PlaylistController.cs +++ b/PlaylistShared.Api/Controllers/PlaylistsController.cs @@ -14,79 +14,20 @@ namespace PlaylistShared.Api.Controllers; [ApiController] [Route("api/[controller]")] [Authorize] -public class PlaylistController : ControllerBase +public class PlaylistsController : ControllerBase { private readonly UserManager _userManager; private readonly SharedPlaylistService _sharedService; private readonly YandexMusicService _yandexService; - private readonly TrackAdditionLogService _trackLogService; - public PlaylistController( + public PlaylistsController( UserManager userManager, SharedPlaylistService sharedService, - YandexMusicService yandexService, - TrackAdditionLogService trackLogService) + YandexMusicService yandexService) { _userManager = userManager; _sharedService = sharedService; _yandexService = yandexService; - _trackLogService = trackLogService; - } - - [HttpPost("add-tracks")] - public async Task>> AddTracks([FromBody] AddTrackRequest request) - { - var currentUserId = User.GetUserId(); - var playlist = await _sharedService.GetEntityByTokenAsync(request.SharedPlaylistToken); - if (playlist == null) - return NotFound(ApiResponse.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден" })); - - if (!await _sharedService.CanAddTrackAsync(playlist, currentUserId)) - return StatusCode(403, ApiResponse.Fail(new ErrorResponse { StatusCode = 403, Message = "Недостаточно прав для добавления треков" })); - - var creator = await _userManager.FindByIdAsync(playlist.CreatorUserId.ToString()); - if (creator == null) - return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Владелец плейлиста не найден" })); - - var updatedPlaylist = await _yandexService.AddTracksAsync(creator, playlist.YandexPlaylistOwnerUid, playlist.YandexPlaylistKind, request.TrackIds); - if (updatedPlaylist == null) - return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Ошибка при добавлении треков в Яндекс.Музыку" })); - - // Логируем добавления для права AddedByUserOnly - foreach (var trackId in request.TrackIds) - await _trackLogService.LogAdditionAsync(playlist.Id, trackId, currentUserId); - - return Ok(ApiResponse.Ok(new { message = "Треки успешно добавлены" })); - } - - [HttpPost("remove-tracks")] - public async Task>> RemoveTracks([FromBody] AddTrackRequest request) - { - var currentUserId = User.GetUserId(); - var playlist = await _sharedService.GetEntityByTokenAsync(request.SharedPlaylistToken); - if (playlist == null) - return NotFound(ApiResponse.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден" })); - - // Проверяем права на удаление каждого трека - foreach (var trackId in request.TrackIds) - { - if (!await _sharedService.CanRemoveTrackAsync(playlist, currentUserId, trackId)) - return StatusCode(403, ApiResponse.Fail(new ErrorResponse { StatusCode = 403, Message = $"Недостаточно прав для удаления трека {trackId}" })); - } - - var creator = await _userManager.FindByIdAsync(playlist.CreatorUserId.ToString()); - if (creator == null) - return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Владелец плейлиста не найден" })); - - var updatedPlaylist = await _yandexService.RemoveTracksAsync(creator, playlist.YandexPlaylistOwnerUid, playlist.YandexPlaylistKind, request.TrackIds); - if (updatedPlaylist == null) - return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Ошибка при удалении треков из Яндекс.Музыки" })); - - // Удаляем логи добавления для этих треков - foreach (var trackId in request.TrackIds) - await _trackLogService.RemoveLogsForTrackAsync(playlist.Id, trackId); - - return Ok(ApiResponse.Ok(new { message = "Треки успешно удалены" })); } [HttpGet("info/{ownerUid}/{kind}")] @@ -108,7 +49,7 @@ public class PlaylistController : ControllerBase return Ok(ApiResponse.Ok(yandexPlaylist)); } - [HttpGet("my")] + [HttpGet] public async Task>>> GetMyPlaylists() { var userId = User.GetUserId(); diff --git a/PlaylistShared.Api/Controllers/SharedPlaylistController.cs b/PlaylistShared.Api/Controllers/SharedPlaylistController.cs index ba56930..8eab622 100644 --- a/PlaylistShared.Api/Controllers/SharedPlaylistController.cs +++ b/PlaylistShared.Api/Controllers/SharedPlaylistController.cs @@ -1,11 +1,12 @@ using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using PlaylistShared.Api.Entities; using PlaylistShared.Api.Extensions; using PlaylistShared.Api.Services; using PlaylistShared.Shared.DTO; using PlaylistShared.Shared.Models; - -namespace PlaylistShared.Api.Controllers; +using YandexMusic.API.Models.Playlist; [ApiController] [Route("api/[controller]")] @@ -13,25 +14,22 @@ public class SharedPlaylistController : ControllerBase { private readonly SharedPlaylistService _sharedService; private readonly YandexMusicService _yandexService; + private readonly UserManager _userManager; + private readonly TrackAdditionLogService _trackLogService; - public SharedPlaylistController(SharedPlaylistService sharedService, YandexMusicService yandexService) + public SharedPlaylistController( + SharedPlaylistService sharedService, + YandexMusicService yandexService, + UserManager userManager, + TrackAdditionLogService trackLogService) { _sharedService = sharedService; _yandexService = yandexService; + _userManager = userManager; + _trackLogService = trackLogService; } - [HttpPost] - [Authorize] - public async Task>> Create([FromBody] SharePlaylistDto dto) - { - var userId = User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; - if (string.IsNullOrEmpty(userId) || !Guid.TryParse(userId, out var guid)) - return Unauthorized(); - - var result = await _sharedService.CreateAsync(guid, dto); - return Ok(ApiResponse.Ok(result)); - } - + // GET /api/sharedplaylist/{token} [HttpGet("{token}")] public async Task>> GetByToken(string token) { @@ -50,6 +48,31 @@ public class SharedPlaylistController : ControllerBase return Ok(ApiResponse.Ok(playlist)); } + // GET /api/sharedplaylist/{token}/tracks + [HttpGet("{token}/tracks")] + public async Task>> GetTracks(string token) + { + var currentUserId = User.GetUserIdOrNull(); + var playlist = await _sharedService.GetEntityByTokenAsync(token); + if (playlist == null) + return NotFound(ApiResponse.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден" })); + + if (!await _sharedService.CanViewAsync(playlist, currentUserId)) + return Unauthorized(); + + var creator = await _userManager.FindByIdAsync(playlist.CreatorUserId.ToString()); + if (creator == null) + return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Владелец плейлиста не найден" })); + + var yandexPlaylist = await _yandexService.GetPlaylistAsync(creator, playlist.YandexPlaylistOwnerUid, playlist.YandexPlaylistKind); + if (yandexPlaylist == null) + return NotFound(ApiResponse.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден в Яндекс.Музыке" })); + + var dto = MapToYandexPlaylistData(yandexPlaylist); + return Ok(ApiResponse.Ok(dto)); + } + + // PUT /api/sharedplaylist/{token}/permissions [HttpPut("{token}/permissions")] [Authorize] public async Task>> UpdatePermissions(string token, [FromBody] UpdatePermissionsDto dto) @@ -68,4 +91,90 @@ public class SharedPlaylistController : ControllerBase return Ok(ApiResponse.Ok(updated)); } + + // POST /api/sharedplaylist/{token}/add-tracks + [HttpPost("{token}/add-tracks")] + public async Task>> AddTracks(string token, [FromBody] AddTracksRequest request) + { + var currentUserId = User.GetUserIdOrNull(); + var playlist = await _sharedService.GetEntityByTokenAsync(token); + if (playlist == null) + return NotFound(ApiResponse.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден" })); + + if (!await _sharedService.CanAddTrackAsync(playlist, currentUserId)) + return StatusCode(403, ApiResponse.Fail(new ErrorResponse { StatusCode = 403, Message = "Недостаточно прав для добавления треков" })); + + var creator = await _userManager.FindByIdAsync(playlist.CreatorUserId.ToString()); + if (creator == null) + return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Владелец плейлиста не найден" })); + + var updatedPlaylist = await _yandexService.AddTracksAsync(creator, playlist.YandexPlaylistOwnerUid, playlist.YandexPlaylistKind, request.TrackIds); + if (updatedPlaylist == null) + return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Ошибка при добавлении треков" })); + + return Ok(ApiResponse.Ok(new { message = "Треки добавлены" })); + } + + // POST /api/sharedplaylist/{token}/remove-tracks + [HttpPost("{token}/remove-tracks")] + public async Task>> RemoveTracks(string token, [FromBody] RemoveTracksRequest request) + { + var currentUserId = User.GetUserIdOrNull(); + var playlist = await _sharedService.GetEntityByTokenAsync(token); + if (playlist == null) + return NotFound(ApiResponse.Fail(new ErrorResponse { StatusCode = 404, Message = "Плейлист не найден" })); + + foreach (var trackId in request.TrackIds) + { + if (!await _sharedService.CanRemoveTrackAsync(playlist, currentUserId, trackId)) + return StatusCode(403, ApiResponse.Fail(new ErrorResponse { StatusCode = 403, Message = $"Недостаточно прав для удаления трека {trackId}" })); + } + + var creator = await _userManager.FindByIdAsync(playlist.CreatorUserId.ToString()); + if (creator == null) + return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Владелец плейлиста не найден" })); + + var updatedPlaylist = await _yandexService.RemoveTracksAsync(creator, playlist.YandexPlaylistOwnerUid, playlist.YandexPlaylistKind, request.TrackIds); + if (updatedPlaylist == null) + return StatusCode(500, ApiResponse.Fail(new ErrorResponse { StatusCode = 500, Message = "Ошибка при удалении треков" })); + + foreach (var trackId in request.TrackIds) + await _trackLogService.RemoveLogsForTrackAsync(playlist.Id, trackId); + + 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 + { + Title = playlist.Title ?? "", + Description = playlist.Description ?? "", + Tracks = playlist.Tracks?.Select(t => new YandexTrack + { + Id = t.Track?.Id ?? "", + Title = t.Track?.Title ?? "", + Artists = t.Track?.Artists?.Select(a => a.Name).ToList() ?? new List(), + DurationMs = (int)(t.Track?.DurationMs ?? 0), + CoverUri = t.Track?.CoverUri ?? "" + }).ToList() ?? new List() + }; + } } \ No newline at end of file diff --git a/PlaylistShared.Api/PlaylistShared.Api.csproj b/PlaylistShared.Api/PlaylistShared.Api.csproj index 7a5084b..3f4f3c7 100644 --- a/PlaylistShared.Api/PlaylistShared.Api.csproj +++ b/PlaylistShared.Api/PlaylistShared.Api.csproj @@ -26,7 +26,7 @@ - + diff --git a/PlaylistShared.Pwa/Layout/MainLayout.razor b/PlaylistShared.Pwa/Layout/MainLayout.razor index 3cd4ef5..e4da386 100644 --- a/PlaylistShared.Pwa/Layout/MainLayout.razor +++ b/PlaylistShared.Pwa/Layout/MainLayout.razor @@ -8,12 +8,12 @@ - Application + Playlist share - - About + + Git diff --git a/PlaylistShared.Pwa/Layout/NavMenu.razor b/PlaylistShared.Pwa/Layout/NavMenu.razor index 87cc4aa..705072a 100644 --- a/PlaylistShared.Pwa/Layout/NavMenu.razor +++ b/PlaylistShared.Pwa/Layout/NavMenu.razor @@ -4,8 +4,6 @@ Мои плейлисты Профиль - Создать плейлист - Мои ссылки \ No newline at end of file diff --git a/PlaylistShared.Pwa/Pages/Home.razor b/PlaylistShared.Pwa/Pages/Home.razor index 7721bf2..c816713 100644 --- a/PlaylistShared.Pwa/Pages/Home.razor +++ b/PlaylistShared.Pwa/Pages/Home.razor @@ -1,18 +1,91 @@ @page "/" +@using PlaylistShared.Pwa.Services +@inject NavigationManager Navigation -Home + + + + + 🎵 PlaylistShared + Делитесь плейлистами Яндекс.Музыки с друзьями и управляйте треками вместе! + + -Hello, world! -Welcome to your new app, powered by MudBlazor and the .NET 10 Template! + + 🚀 Как начать - - Before authentication will function correctly, you must configure your provider details in Program.cs. - + + + + 1️⃣ Регистрация и вход + + • Нажмите «Регистрация» и создайте аккаунт.
+ • Или войдите через вход, если уже зарегистрированы. +
+
+
+ + + 2️⃣ Получение токена Яндекс.Музыки + + Токен нужен для доступа к вашим плейлистам. Получите его один раз: + +
    +
  1. Перейдите по ссылке
  2. +
  3. Авторизуйтесь в Яндексе (если ещё не вошли)
  4. +
  5. Нажмите «Разрешить»
  6. +
  7. Скопируйте access_token из адресной строки после перенаправления
  8. +
+ + Пример: https://music.yandex.ru/#access_token=ВАШ_ТОКЕН&... + +
+
- - You can find documentation and examples on our website here: - - www.mudblazor.com - - + + + 3️⃣ Добавление токена в профиле + + • Перейдите в Профиль
+ • Вставьте скопированный токен в поле «Токен Яндекс.Музыки»
+ • Нажмите «Сохранить» +
+ ✅ Токен сохраняется в зашифрованном виде. +
+
+ + + + 4️⃣ Расшаривание плейлиста + + • Откройте Мои плейлисты
+ • Нажмите «Поделиться» рядом с нужным плейлистом
+ • Скопируйте полученную ссылку и отправьте друзьям +
+ + Вы можете настроить права на добавление/удаление треков для гостей. + +
+
+
+ + + + 📌 Важно + + + + 🔐 Токен даёт доступ к вашим плейлистам. Никому его не сообщайте. + + + + + 🎧 Для работы с плейлистами нужна активная подписка Яндекс.Плюс?
+ Нет, достаточно обычного аккаунта. +
+
+
+
+
+
\ No newline at end of file diff --git a/PlaylistShared.Pwa/Pages/Login.razor b/PlaylistShared.Pwa/Pages/Login.razor index e3a061f..e8adad3 100644 --- a/PlaylistShared.Pwa/Pages/Login.razor +++ b/PlaylistShared.Pwa/Pages/Login.razor @@ -24,7 +24,7 @@ - + Войти (локально) diff --git a/PlaylistShared.Pwa/Pages/MyPlaylists.razor b/PlaylistShared.Pwa/Pages/MyPlaylists.razor index a00d411..3f0c94f 100644 --- a/PlaylistShared.Pwa/Pages/MyPlaylists.razor +++ b/PlaylistShared.Pwa/Pages/MyPlaylists.razor @@ -83,7 +83,7 @@ _loading = true; try { - var response = await Http.GetFromJsonAsync>>("/api/playlist/my"); + var response = await Http.GetFromJsonAsync>>("/api/playlists"); if (response?.Success == true) _playlists = response.Data; else @@ -103,7 +103,7 @@ private async Task SharePlaylist(YandexPlaylistInfo playlist) { var request = new SharePlaylistRequest { Kind = playlist.Kind, OwnerUid = playlist.OwnerUid }; - var response = await Http.PostAsJsonAsync("/api/playlist/share", request); + var response = await Http.PostAsJsonAsync("/api/playlists/share", request); if (response.IsSuccessStatusCode) { Snackbar.Add("Плейлист расшарен", Severity.Success); diff --git a/PlaylistShared.Pwa/Pages/Register.razor b/PlaylistShared.Pwa/Pages/Register.razor index 6abff26..33dab5c 100644 --- a/PlaylistShared.Pwa/Pages/Register.razor +++ b/PlaylistShared.Pwa/Pages/Register.razor @@ -11,8 +11,8 @@ - - + + Зарегистрироваться diff --git a/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor b/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor index 1c2acd8..3a37a13 100644 --- a/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor +++ b/PlaylistShared.Pwa/Pages/SharedPlaylistView.razor @@ -1,5 +1,4 @@ @page "/shared/{token}" -@attribute [Authorize] @using PlaylistShared.Shared.DTO @using PlaylistShared.Shared.Enums @using PlaylistShared.Pwa.Services @@ -8,6 +7,7 @@ @inject ISnackbar Snackbar @inject NavigationManager Navigation @inject AuthenticationStateProvider AuthProvider +@inject IDialogService DialogService @if (_loading) @@ -21,14 +21,25 @@ else { + - @_playlist.Title - Владелец: @_playlist.Creator?.UserName +
+ @if (!string.IsNullOrEmpty(_playlist.CoverUrl)) + { + + } +
+ @_playlist.Title + Владелец: @_playlist.Creator?.UserName +
+
+ - @if (_isCreator) + + @if (_isCreator && _isAuthenticated) { Настройки доступа @@ -68,8 +79,85 @@ } - - Функционал управления треками в разработке + + @if (_canAdd) + { + + Добавить трек + + + + + + + @if (_addingTrack) + { + + } + else + { + + Добавить + } + + + + + Поддерживаются ссылки вида: https://music.yandex.ru/album/12345/track/67890 + + + } + + +
+ Треки + +
+ @if (_tracksLoading) + { + + } + else if (_tracks == null || !_tracks.Any()) + { + В плейлисте пока нет треков + } + else + { + + + # + Обложка + Название + Исполнитель + Длительность + @if (_canRemove) + { + + } + + + @context.Index + + @if (!string.IsNullOrEmpty(context.CoverUri)) + { + + } + + @context.Title + @string.Join(", ", context.Artists) + @FormatDuration(context.DurationMs) + @if (_isAuthenticated && _canRemove) + { + + + + } + + + }
} @@ -80,14 +168,24 @@ private SharedPlaylistDto? _playlist; private bool _loading = true; + private bool _isAuthenticated; private bool _isCreator; + private bool _canAdd; + private bool _canRemove; private UpdatePermissionsDto _editPermissions = new(); private bool _savingPermissions; private string? _currentUserId; + private List _tracks = new(); + private bool _tracksLoading; + + private string _trackLink = ""; + private bool _addingTrack; + protected override async Task OnInitializedAsync() { var authState = await AuthProvider.GetAuthenticationStateAsync(); + _isAuthenticated = authState.User.Identity?.IsAuthenticated == true; _currentUserId = authState.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value; await LoadPlaylist(); } @@ -101,7 +199,16 @@ { _playlist = response.Data; _isCreator = _playlist.CreatorUserId.ToString() == _currentUserId; - if (_isCreator) + + _canAdd = _isCreator + || _playlist.AddPermission == EditPermission.Everyone + || (_playlist.AddPermission == EditPermission.AuthorizedOnly && _isAuthenticated); + + _canRemove = _isCreator + || _playlist.RemovePermission == EditPermission.Everyone + || (_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated); + + if (_isCreator && _isAuthenticated) { _editPermissions = new UpdatePermissionsDto { @@ -110,6 +217,8 @@ RemovePermission = _playlist.RemovePermission }; } + + await LoadTracks(); } else { @@ -127,8 +236,121 @@ } } + private async Task LoadTracks() + { + if (_playlist == null) return; + _tracksLoading = true; + try + { + var url = $"/api/sharedplaylist/{Token}/tracks"; + var response = await Http.GetFromJsonAsync>(url); + if (response?.Success == true && response.Data != null) + { + _tracks = response.Data.Tracks.Select((t, idx) => new YandexTrackDisplay + { + Id = t.Id, + Title = t.Title, + Artists = t.Artists, + DurationMs = t.DurationMs, + CoverUri = t.CoverUri, + Index = idx + 1 + }).ToList(); + } + else + { + Snackbar.Add(response?.Error?.Message ?? "Не удалось загрузить треки", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"Ошибка загрузки треков: {ex.Message}", Severity.Error); + } + finally + { + _tracksLoading = false; + StateHasChanged(); + } + } + + private async Task AddTrack() + { + if (!_isAuthenticated) + { + Snackbar.Add("Для добавления треков необходимо войти", Severity.Warning); + return; + } + if (string.IsNullOrWhiteSpace(_trackLink)) + { + Snackbar.Add("Введите ссылку на трек", Severity.Warning); + return; + } + + _addingTrack = true; + try + { + var request = new AddTrackByLinkRequest { Link = _trackLink }; + var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{Token}/add-track-by-link", request); + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Трек успешно добавлен", Severity.Success); + _trackLink = ""; + await LoadTracks(); + } + else + { + var error = await response.Content.ReadFromJsonAsync>(); + Snackbar.Add(error?.Error?.Message ?? "Ошибка добавления трека", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error); + } + finally + { + _addingTrack = false; + } + } + + private async Task RemoveTrack(YandexTrackDisplay track) + { + if (!_isAuthenticated) + { + Snackbar.Add("Для удаления треков необходимо войти", Severity.Warning); + return; + } + + var confirmed = await DialogService.ShowMessageBoxAsync( + "Подтверждение удаления", + $"Вы уверены, что хотите удалить трек \"{track.Title}\"?", + yesText: "Удалить", cancelText: "Отмена"); + + if (confirmed != true) return; + + try + { + var request = new RemoveTracksRequest { TrackIds = new List { track.Id } }; + var response = await Http.PostAsJsonAsync($"/api/sharedplaylist/{Token}/remove-tracks", request); + if (response.IsSuccessStatusCode) + { + Snackbar.Add("Трек удалён", Severity.Success); + await LoadTracks(); + } + else + { + var error = await response.Content.ReadFromJsonAsync>(); + Snackbar.Add(error?.Error?.Message ?? "Ошибка удаления", Severity.Error); + } + } + catch (Exception ex) + { + Snackbar.Add($"Ошибка: {ex.Message}", Severity.Error); + } + } + private async Task SavePermissions() { + if (!_isAuthenticated) return; _savingPermissions = true; try { @@ -140,6 +362,10 @@ { _playlist = result.Data; Snackbar.Add("Права доступа обновлены", Severity.Success); + _canAdd = _isCreator || _playlist.AddPermission == EditPermission.Everyone || + (_playlist.AddPermission == EditPermission.AuthorizedOnly && _isAuthenticated); + _canRemove = _isCreator || _playlist.RemovePermission == EditPermission.Everyone || + (_playlist.RemovePermission == EditPermission.AuthorizedOnly && _isAuthenticated); } else { @@ -160,4 +386,23 @@ _savingPermissions = false; } } + + private string FormatDuration(int ms) + { + var seconds = ms / 1000; + var mins = seconds / 60; + var secs = seconds % 60; + return $"{mins}:{secs:D2}"; + } + + private string FormatCoverUrl(string? url, string size = "200x200") + { + if (string.IsNullOrEmpty(url)) return ""; + return "https://" + url.Replace("%%", size); + } + + private class YandexTrackDisplay : YandexTrack + { + public int Index { get; set; } + } } \ No newline at end of file diff --git a/PlaylistShared.Pwa/wwwroot/appsettings.json b/PlaylistShared.Pwa/wwwroot/appsettings.json index 62a9192..4f18580 100644 --- a/PlaylistShared.Pwa/wwwroot/appsettings.json +++ b/PlaylistShared.Pwa/wwwroot/appsettings.json @@ -1,3 +1,3 @@ { - "ApiBaseUrl": "" + "ApiBaseUrl": "https://api.playlistshare.frigat.duckdns.org" } \ No newline at end of file diff --git a/PlaylistShared.Shared/DTO/AddTrackByLinkRequest.cs b/PlaylistShared.Shared/DTO/AddTrackByLinkRequest.cs new file mode 100644 index 0000000..c139501 --- /dev/null +++ b/PlaylistShared.Shared/DTO/AddTrackByLinkRequest.cs @@ -0,0 +1,6 @@ +namespace PlaylistShared.Shared.DTO; + +public class AddTrackByLinkRequest +{ + public string Link { get; set; } +} \ No newline at end of file diff --git a/PlaylistShared.Shared/DTO/AddTrackRequest.cs b/PlaylistShared.Shared/DTO/AddTracksRequest.cs similarity index 55% rename from PlaylistShared.Shared/DTO/AddTrackRequest.cs rename to PlaylistShared.Shared/DTO/AddTracksRequest.cs index 065465c..a0de29a 100644 --- a/PlaylistShared.Shared/DTO/AddTrackRequest.cs +++ b/PlaylistShared.Shared/DTO/AddTracksRequest.cs @@ -2,11 +2,8 @@ namespace PlaylistShared.Shared.DTO; -public class AddTrackRequest +public class AddTracksRequest { - [JsonPropertyName("sharedPlaylistToken")] - public string SharedPlaylistToken { get; set; } = null!; - [JsonPropertyName("trackIds")] public List TrackIds { get; set; } = new(); } \ No newline at end of file diff --git a/PlaylistShared.Shared/DTO/RemoveTracksRequest.cs b/PlaylistShared.Shared/DTO/RemoveTracksRequest.cs new file mode 100644 index 0000000..a0d1b10 --- /dev/null +++ b/PlaylistShared.Shared/DTO/RemoveTracksRequest.cs @@ -0,0 +1,9 @@ +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/DTO/YandexPlaylistData.cs b/PlaylistShared.Shared/DTO/YandexPlaylistData.cs new file mode 100644 index 0000000..f2fc680 --- /dev/null +++ b/PlaylistShared.Shared/DTO/YandexPlaylistData.cs @@ -0,0 +1,8 @@ +namespace PlaylistShared.Shared.DTO; + +public class YandexPlaylistData +{ + public string Title { get; set; } = ""; + public string Description { get; set; } = ""; + public List Tracks { get; set; } = new(); +} diff --git a/PlaylistShared.Shared/DTO/YandexTrack.cs b/PlaylistShared.Shared/DTO/YandexTrack.cs new file mode 100644 index 0000000..eed291a --- /dev/null +++ b/PlaylistShared.Shared/DTO/YandexTrack.cs @@ -0,0 +1,10 @@ +namespace PlaylistShared.Shared.DTO; + +public class YandexTrack +{ + public string Id { get; set; } = ""; + public string Title { get; set; } = ""; + public List Artists { get; set; } = new(); + public int DurationMs { get; set; } + public string CoverUri { get; set; } = ""; +} \ No newline at end of file diff --git a/run-docker-compose.bat b/run-docker-compose.bat index e846b84..dc74f7f 100644 --- a/run-docker-compose.bat +++ b/run-docker-compose.bat @@ -1,2 +1,2 @@ -docker-compose up -d --force-recreate +docker-compose up -d --build --force-recreate pause \ No newline at end of file