Smooth Seeking (no scrub audio)
This commit is contained in:
@@ -32,16 +32,20 @@ else
|
|||||||
Indeterminate="@(LoadProgress == 0)"/>
|
Indeterminate="@(LoadProgress == 0)"/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<TimestampLabel CurrentTime="CurrentTime" Duration="Duration"/>
|
<TimestampLabel CurrentTime="DisplayTime" Duration="Duration"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="seekbar-flex mx-3">
|
<div class="seekbar-flex mx-3"
|
||||||
|
@onpointerdown="OnSeekStart"
|
||||||
|
@onpointerup="@(() => OnSeekEnd(_seekPosition))"
|
||||||
|
@onpointerleave="@(() => { if (_isSeeking) OnSeekEnd(_seekPosition); })">
|
||||||
<MudSlider T="double"
|
<MudSlider T="double"
|
||||||
Min="0"
|
Min="0"
|
||||||
Max="@(Duration ?? 0D)"
|
Max="@(Duration ?? 0D)"
|
||||||
Step="0.1"
|
Step="0.1"
|
||||||
Value="@CurrentTime"
|
Value="@DisplayTime"
|
||||||
ValueChanged="@OnSeek"
|
ValueChanged="@OnSeekChange"
|
||||||
|
Immediate="true"
|
||||||
Disabled="@(!CanSeek)"/>
|
Disabled="@(!CanSeek)"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -67,19 +71,24 @@ else
|
|||||||
Indeterminate="@(LoadProgress == 0)"/>
|
Indeterminate="@(LoadProgress == 0)"/>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<TimestampLabel CurrentTime="CurrentTime" Duration="Duration"/>
|
<TimestampLabel CurrentTime="DisplayTime" Duration="Duration"/>
|
||||||
<VolumeControls Volume="@Volume" VolumeChanged="@OnVolumeChange"/>
|
<VolumeControls Volume="@Volume" VolumeChanged="@OnVolumeChange"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MudSlider T="double"
|
<div @onpointerdown="OnSeekStart"
|
||||||
Min="0"
|
@onpointerup="@(() => OnSeekEnd(_seekPosition))"
|
||||||
Max="@(Duration ?? 0D)"
|
@onpointerleave="@(() => { if (_isSeeking) OnSeekEnd(_seekPosition); })">
|
||||||
Step="0.1"
|
<MudSlider T="double"
|
||||||
Value="@CurrentTime"
|
Min="0"
|
||||||
ValueChanged="@OnSeek"
|
Max="@(Duration ?? 0D)"
|
||||||
Disabled="@(!CanSeek)"/>
|
Step="0.1"
|
||||||
|
Value="@DisplayTime"
|
||||||
|
ValueChanged="@OnSeekChange"
|
||||||
|
Immediate="true"
|
||||||
|
Disabled="@(!CanSeek)"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@* Control Buttons - positioned absolutely like original *@
|
@* Control Buttons - positioned absolutely like original *@
|
||||||
<div class="player-controls d-flex align-center justify-center gap-1">
|
<div class="player-controls d-flex align-center justify-center gap-1">
|
||||||
<MudIconButton Icon="@Icons.Material.Filled.Minimize"
|
<MudIconButton Icon="@Icons.Material.Filled.Minimize"
|
||||||
|
|||||||
@@ -7,21 +7,27 @@ namespace DeepDrftWeb.Client.Controls.AudioPlayerBar;
|
|||||||
public partial class AudioPlayerBar : ComponentBase
|
public partial class AudioPlayerBar : ComponentBase
|
||||||
{
|
{
|
||||||
[CascadingParameter] public required IStreamingPlayerService PlayerService { get; set; }
|
[CascadingParameter] public required IStreamingPlayerService PlayerService { get; set; }
|
||||||
|
|
||||||
private bool _isMinimized = true;
|
private bool _isMinimized = true;
|
||||||
|
private bool _isSeeking = false;
|
||||||
|
private double _seekPosition = 0;
|
||||||
|
|
||||||
private bool IsLoaded => PlayerService.IsLoaded;
|
private bool IsLoaded => PlayerService.IsLoaded;
|
||||||
private bool IsLoading => PlayerService.IsLoading;
|
private bool IsLoading => PlayerService.IsLoading;
|
||||||
private bool IsStreaming => PlayerService.CanStartStreaming;
|
private bool IsStreaming => PlayerService.CanStartStreaming;
|
||||||
private bool IsStreamingMode => PlayerService.IsStreamingMode;
|
private bool IsStreamingMode => PlayerService.IsStreamingMode;
|
||||||
private bool IsPlaying => PlayerService.IsPlaying;
|
private bool IsPlaying => PlayerService.IsPlaying;
|
||||||
private bool IsPaused => PlayerService.IsPaused;
|
private bool IsPaused => PlayerService.IsPaused;
|
||||||
private double CurrentTime => PlayerService.CurrentTime;
|
|
||||||
private double? Duration => PlayerService.Duration;
|
private double? Duration => PlayerService.Duration;
|
||||||
private double Volume => PlayerService.Volume;
|
private double Volume => PlayerService.Volume;
|
||||||
private double LoadProgress => PlayerService.LoadProgress;
|
private double LoadProgress => PlayerService.LoadProgress;
|
||||||
private string? ErrorMessage => PlayerService.ErrorMessage;
|
private string? ErrorMessage => PlayerService.ErrorMessage;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Display time - shows seek position while dragging, otherwise current playback time.
|
||||||
|
/// </summary>
|
||||||
|
private double DisplayTime => _isSeeking ? _seekPosition : PlayerService.CurrentTime;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Seek is enabled once track is loaded AND duration is known (from WAV header).
|
/// Seek is enabled once track is loaded AND duration is known (from WAV header).
|
||||||
/// This allows seeking even during streaming, including seeking beyond buffered content.
|
/// This allows seeking even during streaming, including seeking beyond buffered content.
|
||||||
@@ -74,8 +80,21 @@ public partial class AudioPlayerBar : ComponentBase
|
|||||||
await PlayerService.Stop();
|
await PlayerService.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task OnSeek(double position)
|
private void OnSeekStart()
|
||||||
{
|
{
|
||||||
|
_isSeeking = true;
|
||||||
|
_seekPosition = PlayerService.CurrentTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSeekChange(double position)
|
||||||
|
{
|
||||||
|
_seekPosition = position;
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task OnSeekEnd(double position)
|
||||||
|
{
|
||||||
|
_isSeeking = false;
|
||||||
await PlayerService.Seek(position);
|
await PlayerService.Seek(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user