Files
daniel-c-harvey 912256d99a Add whole-release embeds to FramePlayer and un-gate the release embed share affordance
The queue gains an armed-but-idle state (Arm/Start) so a release embed stages track 0 prerender-safe, then queues the full release on first play and auto-advances.
2026-06-19 12:05:35 -04:00

76 lines
3.1 KiB
Plaintext

@using DeepDrftModels.DTOs
@using DeepDrftPublic.Client.Controls.AudioPlayerBar
@using DeepDrftPublic.Client.Layout
@using DeepDrftPublic.Client.Services
@using DeepDrftPublic.Client.ViewModels
@page "/FramePlayer"
@layout EmbedLayout
<AudioPlayerBar Fixed />
@code {
[CascadingParameter] public IStreamingPlayerService? PlayerService { get; set; }
[CascadingParameter] public IQueueService? Queue { get; set; }
// Two mutually-exclusive embed targets. ReleaseEntryKey wins if both are somehow supplied — a
// release embed is the richer surface, and the single-track path would otherwise mask it.
[SupplyParameterFromQuery] public string? ReleaseEntryKey { get; set; }
[SupplyParameterFromQuery] public string? TrackEntryKey { get; set; }
[Inject] public required ITrackDataService TrackDataService { get; set; }
[Inject] public required FramePlayerViewModel ViewModel { get; set; }
private string? _stagedKey;
protected override async Task OnParametersSetAsync()
{
if (PlayerService is null) return;
if (!string.IsNullOrWhiteSpace(ReleaseEntryKey))
{
await StageRelease(ReleaseEntryKey);
}
else if (!string.IsNullOrWhiteSpace(TrackEntryKey))
{
await StageSingleTrack(TrackEntryKey);
}
}
// Release embed: resolve the release's ordered tracks, stage the first so the bar shows the release
// ready, and arm the queue with the whole list. No JS interop here (StageTrack and Arm are both
// interop-free), so this runs identically during prerender and after WASM boot. The first play
// click (handled in AudioPlayerBar) routes through Queue.PlayRelease, queueing the full release.
private async Task StageRelease(string releaseEntryKey)
{
// OnParametersSetAsync can fire repeatedly; only act when the key actually changes.
if (releaseEntryKey == _stagedKey) return;
_stagedKey = releaseEntryKey;
await ViewModel.Load(releaseEntryKey);
if (ViewModel.Tracks.Count == 0) return; // No tracks: leave the bar idle.
await PlayerService!.StageTrack(ViewModel.Tracks[0]);
Queue?.Arm(ViewModel.Tracks);
}
// Single-track embed: unchanged behaviour — stage exactly the requested track. The first play
// click streams it directly (the queue stays empty/disarmed).
private async Task StageSingleTrack(string trackEntryKey)
{
if (trackEntryKey == _stagedKey) return;
_stagedKey = trackEntryKey;
var result = await TrackDataService.GetTrack(trackEntryKey);
if (result.Success && result.Value is not null)
{
// Stage only — no audio context, no streaming. The browser blocks audio until a user
// gesture, so the embed shows the track ready and the first play click calls
// SelectTrackStreaming. This keeps this pass free of JS interop, so it works whether it
// runs during prerender or after WASM is interactive.
await PlayerService!.StageTrack(result.Value);
}
// On failure, leave the bar idle; a stream-level error surfaces via PlayerService.ErrorMessage.
}
}