Files
PlaylistShared/PlaylistShared.Pwa/Services/AudioPlayerService.cs
2026-04-23 09:11:43 +03:00

214 lines
6.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using MudBlazor;
using PlaylistShared.Shared;
using PlaylistShared.Shared.Yandex;
using System.Net.Http.Json;
namespace PlaylistShared.Pwa.Services;
public class AudioPlayerService : IAudioPlayerService
{
private readonly TokenStorage _tokenStorage;
private readonly ISnackbar _snackbar;
private readonly HttpClient _http;
private readonly PlayerStorage _playerStorage;
private string? _currentTrackId;
private YandexTrack? _currentTrack;
private bool _isPlaying;
private double _currentVolume = 50;
private double _currentProgress;
private double _currentTime = 0;
private double _totalTime = 0;
private string _currentTimeString = "0:00";
private string _totalTimeString = "0:00";
public string? CurrentTrackId => _currentTrackId;
public YandexTrack? CurrentTrack => _currentTrack;
public bool IsPlaying => _isPlaying;
public double CurrentVolume
{
get => _currentVolume;
set
{
_currentVolume = value;
OnStateChanged?.Invoke();
}
}
public double CurrentProgress => _currentProgress;
public double CurrentTime => _currentTime;
public double TotalTime => _totalTime;
public string CurrentTimeString => _currentTimeString;
public string TotalTimeString => _totalTimeString;
public event Action? OnStateChanged;
public event Action? OnStartedTrack;
public event Action? OnEndedTrack;
public AudioPlayerService(TokenStorage tokenStorage, ISnackbar snackbar, HttpClient httpClient, PlayerStorage playerStorage)
{
_tokenStorage = tokenStorage;
_snackbar = snackbar;
_http = httpClient;
_playerStorage = playerStorage;
_ = LoadVolume();
}
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)
{
await PlayAsync();
return;
}
_currentTrackId = trackId;
// Если accessToken не передан, пытаемся получить его из хранилища
if (string.IsNullOrWhiteSpace(accessToken))
{
var tokens = await _tokenStorage.GetTokensAsync();
accessToken = tokens.token;
}
// Проверяем, есть ли чем авторизоваться
if (string.IsNullOrWhiteSpace(accessToken) && string.IsNullOrWhiteSpace(playlistShareToken))
{
_snackbar.Add("Не удалось воспроизвести трек: отсутствует токен авторизации или идентификатор расшаренного плейлиста.", Severity.Error);
return;
}
// Если title и coverUrl не переданы, нужно запросить через API
if (track is null)
{
try
{
track = await GetTrackInfo(trackId, accessToken, playlistShareToken);
}
catch (Exception ex)
{
// Логируем ошибку, но продолжаем без обложки/названия
Console.WriteLine($"Failed to fetch track info: {ex.Message}");
}
}
_currentTrack = track;
_isPlaying = true;
OnStateChanged?.Invoke();
OnLoadAndPlayRequested?.Invoke(trackId, accessToken, playlistShareToken);
OnStartedTrack?.Invoke();
}
public async Task PlayAsync()
{
_isPlaying = true;
OnStateChanged?.Invoke();
OnPlayRequested?.Invoke();
OnStartedTrack?.Invoke();
}
public async Task PauseAsync()
{
_isPlaying = false;
OnStateChanged?.Invoke();
OnPauseRequested?.Invoke();
}
public async Task SeekToAsync(double second)
{
OnSeekRequested?.Invoke(second);
}
public async Task SetVolumeAsync(double volume)
{
_currentVolume = volume;
OnStateChanged?.Invoke();
OnVolumeChangeRequested?.Invoke(volume);
await _playerStorage.SetVolumeAsync(volume);
}
// События для связи с реальным AudioPlayer компонентом
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;
OnStateChanged?.Invoke();
}
public void SetCurrentTrack(string? trackId)
{
_currentTrackId = trackId;
OnStateChanged?.Invoke();
}
public void UpdateProgress(double currentTime, double totalTime)
{
var progress = (currentTime / totalTime) * 100;
_currentProgress = progress;
_currentTime = currentTime;
_currentTimeString = FormatDuration(currentTime);
_totalTime = totalTime;
_totalTimeString = FormatDuration(totalTime);
OnStateChanged?.Invoke();
}
public void NotifyTrackEnded()
{
_isPlaying = false;
_currentTrackId = null;
_currentProgress = 0;
_currentTime = 0;
_currentTimeString = "0:00";
_totalTime = 0;
_currentTimeString = "0:00";
OnStateChanged?.Invoke();
OnEndedTrack?.Invoke();
}
/// <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)
{
var url = $"/api/audio/track-info/{trackId}";
if (!string.IsNullOrEmpty(accessToken))
url += $"?access_token={accessToken}";
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;
}
private string FormatDuration(double seconds)
{
var mins = (int)(seconds / 60);
var secs = (int)(seconds % 60);
return $"{mins}:{secs:D2}";
}
}