Проведен аудит. Добавлено переключение треков
This commit is contained in:
@@ -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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user