Streaming Bug Fixes

This commit is contained in:
daniel-c-harvey
2025-12-06 06:41:32 -05:00
parent 605fc94fbb
commit 2baf0575bc
17 changed files with 1510 additions and 1054 deletions
@@ -60,40 +60,24 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
private async Task LoadTrackStreaming(TrackEntity track)
{
// Cancel and replace any previous streaming operation atomically
var oldCancellation = _streamingCancellation;
// Always reset to clean state before loading new track
await ResetToIdle();
// Create new cancellation token for this streaming operation
_streamingCancellation = new CancellationTokenSource();
// Cancel the old operation after we've replaced it
oldCancellation?.Cancel();
oldCancellation?.Dispose();
try
{
// No need to check IsLoading - we cancel previous operations
if (IsPlaying || IsPaused)
{
await Unload();
}
// Reset state to indicate streaming has started
// Set state to indicate loading has started
ErrorMessage = null;
LoadProgress = 0;
IsLoaded = false;
IsLoading = true;
IsStreamingMode = true;
CanStartStreaming = false;
HeaderParsed = false;
BufferedChunks = 0;
_streamingPlaybackStarted = false;
Duration = null;
CurrentTime = 0;
// Reset adaptive buffer sizing
_currentBufferSize = DefaultBufferSize;
_consecutiveSlowReads = 0;
await NotifyStateChanged();
var mediaResult = await _trackMediaClient.GetTrackMedia(track.EntryKey);
@@ -190,6 +174,13 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
CanStartStreaming = chunkResult.CanStartStreaming;
HeaderParsed = chunkResult.HeaderParsed;
BufferedChunks = chunkResult.BufferCount;
// Set duration from WAV header when available (only set once)
if (chunkResult.Duration.HasValue && Duration == null)
{
Duration = chunkResult.Duration.Value;
_logger.LogInformation("Duration set from WAV header: {Duration:F2} seconds", Duration);
}
// Start playback as soon as we can
if (!_streamingPlaybackStarted && CanStartStreaming)
@@ -245,20 +236,63 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
}
}
/// <summary>
/// In streaming mode, Stop fully resets to Idle state since audio data is consumed.
/// This is equivalent to Unload for streaming playback.
/// </summary>
public override async Task Stop()
{
// In streaming mode, Stop = Unload (data is consumed, can't replay)
await ResetToIdle();
}
/// <summary>
/// Fully resets the player to Idle state, ready for a new track.
/// </summary>
public override async Task Unload()
{
// Cancel any ongoing streaming operation
await ResetToIdle();
}
/// <summary>
/// Single method to reset all state - called by both Stop and Unload.
/// </summary>
private async Task ResetToIdle()
{
// 1. Cancel any ongoing streaming operation
_streamingCancellation?.Cancel();
_streamingCancellation?.Dispose();
_streamingCancellation = null;
// 2. Tell JS to stop and unload
try
{
await _audioInterop.StopAsync(PlayerId);
await _audioInterop.UnloadAsync(PlayerId);
}
catch
{
// Ignore JS errors during cleanup
}
// 3. Reset ALL state to Idle
IsPlaying = false;
IsPaused = false;
IsLoaded = false;
IsLoading = false;
CurrentTime = 0;
Duration = null;
LoadProgress = 0;
ErrorMessage = null;
// 4. Reset streaming-specific state
IsStreamingMode = false;
CanStartStreaming = false;
HeaderParsed = false;
BufferedChunks = 0;
_streamingPlaybackStarted = false;
await base.Unload();
await NotifyStateChanged();
}
private async Task ThrottledNotifyStateChanged()