Restore IsStreamingMode on recovery; guard superseded-load else-branch
RecoverFromFailedRefill now sets IsStreamingMode=true so the in-place seek-retry route isn't wedged. The generic-catch unload path is gated on the loadCts identity, so a superseded load no longer clobbers a newer operation's state.
This commit is contained in:
@@ -295,15 +295,22 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
|
||||
{
|
||||
await RecoverFromFailedRefill(CurrentTime, userError);
|
||||
}
|
||||
else
|
||||
else if (ReferenceEquals(_streamingCancellation, loadCts))
|
||||
{
|
||||
// First-segment failure (nothing buffered / playing yet) or superseded load: the
|
||||
// normal unload-to-error path is correct — no buffered tail to halt.
|
||||
// First-segment failure (nothing buffered / playing yet), still the active operation:
|
||||
// the normal unload-to-error path is correct — nothing is in the scheduler to halt.
|
||||
ErrorMessage = userError;
|
||||
LoadProgress = 0;
|
||||
IsLoaded = false;
|
||||
IsStreamingMode = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Superseded load: a newer seek (or track switch) has already claimed _streamingCancellation
|
||||
// and owns all shared state. Writing IsLoaded/IsStreamingMode here would corrupt the live
|
||||
// operation — mirror the OCE catch's identity guard and do nothing to shared state.
|
||||
_logger.LogDebug("Generic throw on superseded load for track {TrackId} — newer operation owns state, skipping unload", track.EntryKey);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -879,13 +886,16 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
|
||||
_logger.LogWarning("Refill-failure recovery interop did not succeed: {Error}", recovered.Error);
|
||||
}
|
||||
|
||||
// Settle C# into the matching recoverable state: not playing, paused at the target, still loaded.
|
||||
// IsLoaded = true is load-bearing — the "paused-but-loaded" contract lets the listener retry
|
||||
// the seek or pick another track; resetting to unloaded would evict the track identity.
|
||||
// Settle C# into the matching recoverable state: not playing, paused at the target, still loaded
|
||||
// and still in streaming mode. IsLoaded = true and IsStreamingMode = true are both load-bearing —
|
||||
// the "paused-but-loaded" contract lets the listener retry the seek (Seek early-returns when
|
||||
// !IsLoaded || !IsStreamingMode), resume via TogglePlayPause, or pick another track. Resetting
|
||||
// either to false would wedge at least one of the three retry routes (AC6 / Phase 21.3).
|
||||
ErrorMessage = userFacingError;
|
||||
IsPlaying = false;
|
||||
IsPaused = true;
|
||||
IsLoaded = true;
|
||||
IsStreamingMode = true;
|
||||
CurrentTime = seekPosition;
|
||||
IsSeekingBeyondBuffer = false;
|
||||
await NotifyStateChanged();
|
||||
|
||||
Reference in New Issue
Block a user