@using Microsoft.JSInterop @inject IAudioPlayerService AudioPlayerService @inject IJSRuntime JS @inject TokenStorage TokenStorage @inject PlayerStorage PlayerStorage @inject AuthenticationStateProvider AuthProvider @inject ISnackbar Snackbar @inject HttpClient Http @if (!string.IsNullOrEmpty(AudioPlayerService.CurrentTrack?.CoverUri)) { } @AudioPlayerService.CurrentTrack?.Title @if (AudioPlayerService.CurrentTrack != null) @string.Join(", ", AudioPlayerService.CurrentTrack.Artists) @AudioPlayerService.CurrentTimeString / @AudioPlayerService.TotalTimeString @code { private const double _volumeDefault = 50; // Генерируем уникальный ID для аудиоэлемента, чтобы избежать конфликтов при множественных экземплярах private string _audioId = $"audio_{Guid.NewGuid():N}"; private IJSObjectReference? _audioModule; private IJSObjectReference? _audioElement; // Громкость private bool _volumeIsOpen; private double _volumeBeforeMute; private double _bufferSecond; private bool _isPlayHovered; protected override async Task OnInitializedAsync() { AudioPlayerService.OnLoadAndPlayRequested += OnServiceLoadAndPlay; AudioPlayerService.OnPlayRequested += OnServicePlay; AudioPlayerService.OnPauseRequested += OnServicePause; AudioPlayerService.OnSeekRequested += OnServiceSeek; AudioPlayerService.OnVolumeChangeRequested += OnServiceVolumeChange; AudioPlayerService.OnStateChanged += OnServiceStateChanged; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { await EnsureAudioModuleAsync(); } } private async Task EnsureAudioModuleAsync() { if (_audioModule == null) _audioModule = await JS.InvokeAsync("import", "/js/AudioPlayer.js"); if (_audioElement == null) _audioElement = await _audioModule.InvokeAsync("init", _audioId, DotNetObjectReference.Create(this)); } #region Обработка JS [JSInvokable] public async Task OnAudioEnded() { AudioPlayerService.NotifyTrackEnded(); } [JSInvokable] public async Task OnTimeUpdate(double currentTime, double duration) { if (!double.IsNaN(duration) && !double.IsNaN(currentTime) && duration > 0) { AudioPlayerService.UpdateProgress(currentTime, duration); } } [JSInvokable] public async Task OnDownloadProgress(double second) { _bufferSecond = second; } #endregion #region Обработка сервиса private async Task OnServiceLoadAndPlay(string trackId, string? accessToken, string? sharedPlaylistId) { if (string.IsNullOrWhiteSpace(accessToken)) { var tokens = await TokenStorage.GetTokensAsync(); accessToken = tokens.token; } if (string.IsNullOrWhiteSpace(accessToken) && string.IsNullOrWhiteSpace(sharedPlaylistId)) { Snackbar.Add("Токен авторизации не найден. Пожалуйста, войдите заново.", Severity.Error); return; } var streamUrl = new Uri(Http.BaseAddress!, $"/api/audio/track/{trackId}").ToString(); await EnsureAudioModuleAsync(); await _audioElement!.InvokeVoidAsync("loadAndPlay", streamUrl, accessToken, sharedPlaylistId); } private async Task OnServicePlay() { if (_audioElement == null) return; await _audioElement.InvokeVoidAsync("play"); } private async Task OnServicePause() { if (_audioElement == null) return; await _audioElement.InvokeVoidAsync("pause"); } private async Task OnServiceSeek(double time) { if (_audioElement == null) return; try { await _audioElement.InvokeVoidAsync("setCurrentTime", time); } catch (Exception ex) { Console.WriteLine($"Seek error: {ex.Message}"); } } private async Task OnServiceVolumeChange(double volume) { if (_audioElement == null) return; if (volume == AudioPlayerService.CurrentVolume) return; try { await _audioElement.InvokeVoidAsync("setVolume", volume / 100); StateHasChanged(); } catch (Exception ex) { Console.WriteLine($"Volume change error: {ex.Message}"); } } private void OnServiceStateChanged() { InvokeAsync(StateHasChanged); } #endregion private async Task CheckAuthAsync() { var authState = await AuthProvider.GetAuthenticationStateAsync(); if (!authState.User.Identity?.IsAuthenticated == true) { Snackbar.Add("Воспроизведение доступно только авторизованным пользователям", Severity.Warning); return false; } return true; } private async Task OnVolumeHandleWheel(WheelEventArgs e) { // Изменяем громкость на 5 единиц за один тик колесика double step = 5; double newVolume = e.DeltaY < 0 ? Math.Min(AudioPlayerService.CurrentVolume + step, 100) : Math.Max(AudioPlayerService.CurrentVolume - step, 0); await ChangeVolume(newVolume); } private async Task SeekTo(double value) { await AudioPlayerService.SeekToAsync(value); } private async Task ChangeVolume(double value) { await AudioPlayerService.SetVolumeAsync(value); } private async Task ToggleMute() { if (AudioPlayerService.CurrentVolume > 0) { _volumeBeforeMute = AudioPlayerService.CurrentVolume; await AudioPlayerService.SetVolumeAsync(0); } else { await AudioPlayerService.SetVolumeAsync(_volumeBeforeMute); _volumeBeforeMute = 0; } } private async Task OnPlayClick() { if (AudioPlayerService.IsPlaying) await AudioPlayerService.PauseAsync(); else await AudioPlayerService.PlayAsync(); } public async ValueTask DisposeAsync() { try { if (_audioElement != null) await _audioElement.DisposeAsync(); if (_audioModule != null) await _audioModule.DisposeAsync(); } catch { } } }