Добавление треков

This commit is contained in:
FrigaT
2026-04-13 16:02:10 +03:00
parent 6fb2f90cd8
commit e440529ea1
17 changed files with 510 additions and 114 deletions

View File

@@ -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
<MudContainer MaxWidth="MaxWidth.Large" Class="mt-8">
@if (_loading)
@@ -21,14 +21,25 @@
else
{
<MudCard>
<!-- Заголовок с обложкой -->
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.h5">@_playlist.Title</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">Владелец: @_playlist.Creator?.UserName</MudText>
<div style="display: flex; gap: 16px; align-items: center;">
@if (!string.IsNullOrEmpty(_playlist.CoverUrl))
{
<MudImage Src="@FormatCoverUrl(_playlist.CoverUrl)" Height="80" Width="80" Class="rounded" />
}
<div>
<MudText Typo="Typo.h5">@_playlist.Title</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">Владелец: @_playlist.Creator?.UserName</MudText>
</div>
</div>
</CardHeaderContent>
</MudCardHeader>
<MudCardContent>
@if (_isCreator)
<!-- Настройки доступа (только для создателя, который авторизован) -->
@if (_isCreator && _isAuthenticated)
{
<MudPaper Class="pa-4 mb-4" Elevation="0" Style="background-color: rgba(0,0,0,0.05); border-radius: 8px;">
<MudText Typo="Typo.h6" GutterBottom>Настройки доступа</MudText>
@@ -68,8 +79,85 @@
</MudPaper>
}
<!-- Здесь будет отображение треков и управление -->
<MudText>Функционал управления треками в разработке</MudText>
<!-- Блок добавления трека (только для авторизованных с правом добавления) -->
@if (_canAdd)
{
<MudPaper Class="pa-4 mb-4" Elevation="0" Style="background-color: rgba(0,0,0,0.05); border-radius: 8px;">
<MudText Typo="Typo.h6" GutterBottom>Добавить трек</MudText>
<MudGrid>
<MudItem xs="12" sm="10">
<MudTextField @bind-Value="_trackLink" Label="Ссылка на трек Яндекс.Музыки"
Variant="Variant.Outlined" FullWidth="true"
Placeholder="https://music.yandex.ru/album/2488464/track/21696942" />
</MudItem>
<MudItem xs="12" sm="2">
<MudButton Variant="Variant.Filled" Color="Color.Primary" OnClick="AddTrack"
Disabled="_addingTrack" FullWidth="true" Style="height: 100%;">
@if (_addingTrack)
{
<MudProgressCircular Size="Size.Small" Indeterminate />
}
else
{
<span>Добавить</span>
}
</MudButton>
</MudItem>
</MudGrid>
<MudText Typo="Typo.body2" Class="mt-2" Color="Color.Secondary">
Поддерживаются ссылки вида: https://music.yandex.ru/album/12345/track/67890
</MudText>
</MudPaper>
}
<!-- Список треков -->
<div style="display: flex; justify-content: space-between; align-items: center;">
<MudText Typo="Typo.h6" GutterBottom>Треки</MudText>
<MudIconButton Icon="@Icons.Material.Filled.Refresh" OnClick="LoadTracks" Disabled="_tracksLoading" Size="Size.Small" />
</div>
@if (_tracksLoading)
{
<MudProgressCircular Indeterminate />
}
else if (_tracks == null || !_tracks.Any())
{
<MudAlert Severity="Severity.Info">В плейлисте пока нет треков</MudAlert>
}
else
{
<MudTable Items="@_tracks" Hover="true" Breakpoint="Breakpoint.Sm">
<HeaderContent>
<MudTh>#</MudTh>
<MudTh>Обложка</MudTh>
<MudTh>Название</MudTh>
<MudTh>Исполнитель</MudTh>
<MudTh>Длительность</MudTh>
@if (_canRemove)
{
<MudTh></MudTh>
}
</HeaderContent>
<RowTemplate>
<MudTd>@context.Index</MudTd>
<MudTd>
@if (!string.IsNullOrEmpty(context.CoverUri))
{
<MudImage Src="@FormatCoverUrl(context.CoverUri, "50x50")" Height="50" Width="50" Class="rounded" />
}
</MudTd>
<MudTd>@context.Title</MudTd>
<MudTd>@string.Join(", ", context.Artists)</MudTd>
<MudTd>@FormatDuration(context.DurationMs)</MudTd>
@if (_isAuthenticated && _canRemove)
{
<MudTd>
<MudIconButton Icon="@Icons.Material.Filled.Delete" Color="Color.Error" OnClick="() => RemoveTrack(context)" />
</MudTd>
}
</RowTemplate>
</MudTable>
}
</MudCardContent>
</MudCard>
}
@@ -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<YandexTrackDisplay> _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<ApiResponse<YandexPlaylistData>>(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<ApiResponse<object>>();
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<string> { 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<ApiResponse<object>>();
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; }
}
}