Проведен аудит. Добавлено переключение треков

This commit is contained in:
FrigaT
2026-05-21 20:49:55 +03:00
parent 38af6174fa
commit 9139d8ecfe
23 changed files with 351 additions and 222 deletions

View File

@@ -1,6 +1,7 @@
using MudBlazor;
using MudBlazor;
using PlaylistShared.Shared;
using PlaylistShared.Shared.Yandex;
using System.Net.Http.Headers;
using System.Net.Http.Json;
namespace PlaylistShared.Pwa.Services;
@@ -22,9 +23,16 @@ public class AudioPlayerService : IAudioPlayerService
private string _currentTimeString = "0:00";
private string _totalTimeString = "0:00";
private List<YandexTrack> _queue = new();
private int _queueIndex = -1;
private string? _queueShareToken;
public string? CurrentTrackId => _currentTrackId;
public YandexTrack? CurrentTrack => _currentTrack;
public bool IsPlaying => _isPlaying;
public IReadOnlyList<YandexTrack> CurrentQueue => _queue;
public bool HasNext => _queueIndex >= 0 && _queueIndex < _queue.Count - 1;
public bool HasPrevious => _queueIndex > 0;
public double CurrentVolume
{
get => _currentVolume;
@@ -57,14 +65,10 @@ public class AudioPlayerService : IAudioPlayerService
private async Task LoadVolume()
{
var savedVolume = await _playerStorage.GetVolumeAsync();
if (savedVolume != null)
{
_currentVolume = savedVolume.Value;
}
}
// Внешние команды (вызываются из компонентов)
public async Task LoadAndPlayAsync(string trackId, string? accessToken = null, string? playlistShareToken = null, YandexTrack? track = null)
{
if (_currentTrackId == trackId)
@@ -75,30 +79,33 @@ public class AudioPlayerService : IAudioPlayerService
_currentTrackId = trackId;
// Если accessToken не передан, пытаемся получить его из хранилища
var idx = _queue.FindIndex(t => t.TrackId == trackId);
if (idx >= 0) _queueIndex = idx;
if (string.IsNullOrWhiteSpace(accessToken))
{
var tokens = await _tokenStorage.GetTokensAsync();
accessToken = tokens.token;
}
// Проверяем, есть ли чем авторизоваться
if (string.IsNullOrWhiteSpace(accessToken) && string.IsNullOrWhiteSpace(playlistShareToken))
string? playToken = null;
if (!string.IsNullOrWhiteSpace(accessToken))
playToken = await FetchPlayTokenAsync(accessToken);
if (string.IsNullOrWhiteSpace(playToken) && string.IsNullOrWhiteSpace(playlistShareToken))
{
_snackbar.Add("Не удалось воспроизвести трек: отсутствует токен авторизации или идентификатор расшаренного плейлиста.", Severity.Error);
return;
}
// Если title и coverUrl не переданы, нужно запросить через API
if (track is null)
{
try
{
track = await GetTrackInfo(trackId, accessToken, playlistShareToken);
track = await GetTrackInfo(trackId, playToken, playlistShareToken);
}
catch (Exception ex)
{
// Логируем ошибку, но продолжаем без обложки/названия
Console.WriteLine($"Failed to fetch track info: {ex.Message}");
}
}
@@ -106,7 +113,7 @@ public class AudioPlayerService : IAudioPlayerService
_currentTrack = track;
_isPlaying = true;
OnStateChanged?.Invoke();
OnLoadAndPlayRequested?.Invoke(trackId, accessToken, playlistShareToken);
OnLoadAndPlayRequested?.Invoke(trackId, playToken, playlistShareToken);
OnStartedTrack?.Invoke();
}
@@ -138,14 +145,36 @@ public class AudioPlayerService : IAudioPlayerService
await _playerStorage.SetVolumeAsync(volume);
}
// События для связи с реальным AudioPlayer компонентом
public void SetQueue(IEnumerable<YandexTrack> tracks, int startIndex = 0, string? shareToken = null)
{
_queue = tracks.ToList();
_queueIndex = _queue.Count > 0 ? Math.Clamp(startIndex, 0, _queue.Count - 1) : -1;
_queueShareToken = shareToken;
OnStateChanged?.Invoke();
}
public async Task PlayNextAsync()
{
if (!HasNext) return;
_queueIndex++;
var track = _queue[_queueIndex];
await LoadAndPlayAsync(track.TrackId, playlistShareToken: _queueShareToken, track: track);
}
public async Task PlayPreviousAsync()
{
if (!HasPrevious) return;
_queueIndex--;
var track = _queue[_queueIndex];
await LoadAndPlayAsync(track.TrackId, playlistShareToken: _queueShareToken, track: track);
}
public event Func<string, string?, string?, Task>? OnLoadAndPlayRequested;
public event Func<Task>? OnPlayRequested;
public event Func<Task>? OnPauseRequested;
public event Func<double, Task>? OnSeekRequested;
public event Func<double, Task>? OnVolumeChangeRequested;
// Внутренние методы для обновления состояния из AudioPlayer
public void SetPlayingState(bool isPlaying)
{
_isPlaying = isPlaying;
@@ -172,37 +201,40 @@ public class AudioPlayerService : IAudioPlayerService
public void NotifyTrackEnded()
{
_isPlaying = false;
_currentTrackId = null;
_currentProgress = 0;
_currentTime = 0;
_currentTimeString = "0:00";
_totalTime = 0;
_currentTimeString = "0:00";
_totalTimeString = "0:00";
OnStateChanged?.Invoke();
OnEndedTrack?.Invoke();
if (HasNext)
_ = PlayNextAsync();
else
_currentTrackId = null;
}
/// <summary>
/// Вспомогательный метод для получения информации о треке через API
/// </summary>
/// <param name="trackId"></param>
/// <param name="accessToken"></param>
/// <param name="sharedPlaylistId"></param>
/// <returns></returns>
private async Task<YandexTrack?> GetTrackInfo(string trackId, string? accessToken, string? sharedPlaylistId)
private async Task<string?> FetchPlayTokenAsync(string jwt)
{
using var request = new HttpRequestMessage(HttpMethod.Get, "/api/audio/play-token");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", jwt);
using var response = await _http.SendAsync(request);
if (!response.IsSuccessStatusCode) return null;
var result = await response.Content.ReadFromJsonAsync<ApiResponse<string>>();
return result?.Data;
}
private async Task<YandexTrack?> GetTrackInfo(string trackId, string? playToken, string? sharedPlaylistId)
{
var url = $"/api/audio/track-info/{trackId}";
if (!string.IsNullOrEmpty(accessToken))
url += $"?access_token={accessToken}";
if (!string.IsNullOrEmpty(playToken))
url += $"?play_token={playToken}";
else if (!string.IsNullOrEmpty(sharedPlaylistId))
url += $"?shared_id={sharedPlaylistId}";
var response = await _http.GetFromJsonAsync<ApiResponse<YandexTrack>>(url);
if (response?.Success == true)
{
return response.Data;
}
return null;
return response?.Success == true ? response.Data : null;
}
private string FormatDuration(double seconds)
@@ -211,4 +243,4 @@ public class AudioPlayerService : IAudioPlayerService
var secs = (int)(seconds % 60);
return $"{mins}:{secs:D2}";
}
}
}