feat: Stream Now instant-play of a random track from the nav button

This commit is contained in:
daniel-c-harvey
2026-06-07 18:33:08 -04:00
parent 2b4cdeaf72
commit 0d4ef369b9
11 changed files with 287 additions and 2 deletions
@@ -63,6 +63,17 @@ public interface IStreamingPlayerService : IPlayerService
// Streaming control methods
Task SelectTrackStreaming(TrackDto track);
/// <summary>
/// Initializes the player (if needed) and resumes the AudioContext. Call this synchronously at
/// the very start of a user-gesture handler — before any <c>await</c> on network I/O — so the
/// gesture is still "active" when the context resumes. Safari refuses to start a suspended
/// AudioContext once the originating gesture has been consumed by an intervening await
/// (e.g. fetching which track to play), so warming here and streaming after is load-bearing.
/// <see cref="SelectTrackStreaming"/> also resumes the context, but only after its own internal
/// awaits — too late for a handler that must first fetch the track to play.
/// </summary>
Task WarmAudioContext();
/// <summary>
/// Stages a track as the current track without touching the audio context or starting the
/// stream. Used by the embed player, where there is no user gesture on initial load: the track
@@ -20,4 +20,11 @@ public interface ITrackDataService
bool sortDescending = false);
Task<ApiResult<TrackDto>> GetTrack(string trackId);
/// <summary>
/// Fetches a random track from the public library for instant play. Success with a value on a
/// hit; success with a null value when the library is empty (a valid state, not a failure);
/// failure on any other transport error.
/// </summary>
Task<ApiResult<TrackDto?>> GetRandomTrack();
}
@@ -46,6 +46,13 @@ public class StreamingAudioPlayerService : AudioPlayerService, IStreamingPlayerS
await SelectTrackStreaming(track);
}
/// <inheritdoc />
public async Task WarmAudioContext()
{
await EnsureInitializedAsync();
await _audioInterop.EnsureAudioContextReady(PlayerId);
}
public async Task SelectTrackStreaming(TrackDto track)
{
await EnsureInitializedAsync();
@@ -28,4 +28,7 @@ public class TrackClientDataService : ITrackDataService
public Task<ApiResult<TrackDto>> GetTrack(string trackId)
=> _trackClient.GetTrack(trackId);
public Task<ApiResult<TrackDto?>> GetRandomTrack()
=> _trackClient.GetRandom();
}