using MudBlazor; using PlaylistShared.Shared; using PlaylistShared.Shared.DTO; 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 string? _currentTrackId; private string? _currentTrackTitle; private string? _currentTrackCoverUrl; private bool _isPlaying; private double _currentVolume = 70; private double _currentProgress; private string _currentTime = "0:00"; private string _totalTime = "0:00"; public string? CurrentTrackId => _currentTrackId; public string? CurrentTrackTitle => _currentTrackTitle; public string? CurrentTrackCoverUrl => _currentTrackCoverUrl; public bool IsPlaying => _isPlaying; public double CurrentVolume { get => _currentVolume; set { _currentVolume = value; OnStateChanged?.Invoke(); } } public double CurrentProgress => _currentProgress; public string CurrentTime => _currentTime; public string TotalTime => _totalTime; public event Action? OnStateChanged; public AudioPlayerService(TokenStorage tokenStorage, ISnackbar snackbar, HttpClient httpClient) { _tokenStorage = tokenStorage; _snackbar = snackbar; _http = httpClient; } // Внешние команды (вызываются из компонентов) public async Task LoadAndPlayAsync(string trackId, string? accessToken = null, string? playlistShareToken = null, string? title = null, string? coverUrl = null) { // Если 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 (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(coverUrl)) { try { var trackInfo = await GetTrackInfo(trackId, accessToken, playlistShareToken); title = trackInfo?.Title; coverUrl = trackInfo?.CoverUri; } catch (Exception ex) { // Логируем ошибку, но продолжаем без обложки/названия Console.WriteLine($"Failed to fetch track info: {ex.Message}"); } } _currentTrackId = trackId; _currentTrackTitle = title ?? "Неизвестный трек"; _currentTrackCoverUrl = coverUrl; _isPlaying = true; OnStateChanged?.Invoke(); OnLoadAndPlayRequested?.Invoke(trackId, accessToken, playlistShareToken); } public async Task PlayAsync() { _isPlaying = true; OnStateChanged?.Invoke(); OnPlayRequested?.Invoke(); } public async Task PauseAsync() { _isPlaying = false; OnStateChanged?.Invoke(); OnPauseRequested?.Invoke(); } public async Task StopAsync() { _isPlaying = false; _currentTrackId = null; _currentProgress = 0; _currentTime = "0:00"; OnStateChanged?.Invoke(); OnStopRequested?.Invoke(); } public async Task SeekToAsync(double percent) { OnSeekRequested?.Invoke(percent); } public async Task SetVolumeAsync(double volume) { _currentVolume = volume; OnStateChanged?.Invoke(); OnVolumeChangeRequested?.Invoke(volume); } // События для связи с реальным AudioPlayer компонентом public event Func? OnLoadAndPlayRequested; public event Func? OnPlayRequested; public event Func? OnPauseRequested; public event Func? OnStopRequested; public event Func? OnSeekRequested; public event Func? OnVolumeChangeRequested; // Внутренние методы для обновления состояния из AudioPlayer public void SetPlayingState(bool isPlaying) { _isPlaying = isPlaying; OnStateChanged?.Invoke(); } public void SetCurrentTrack(string? trackId) { _currentTrackId = trackId; OnStateChanged?.Invoke(); } public void UpdateProgress(double progress, string currentTime, string totalTime) { _currentProgress = progress; _currentTime = currentTime; _totalTime = totalTime; OnStateChanged?.Invoke(); } public void NotifyTrackEnded() { _isPlaying = false; _currentTrackId = null; _currentProgress = 0; _currentTime = "0:00"; OnStateChanged?.Invoke(); } /// /// Вспомогательный метод для получения информации о треке через API /// /// /// /// /// private async Task<(string? Title, string? CoverUri)?> 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>(url); if (response?.Success == true) { return (response.Data.Title, response.Data.CoverUri); } return null; } }