Merge branch 'dev' into p16-w1-foundation
# Conflicts: # DeepDrftPublic.Client/Controls/SharePopover.razor.cs
This commit is contained in:
@@ -38,6 +38,15 @@ public interface IQueueService
|
||||
/// <summary>The current track, or null when the queue is empty.</summary>
|
||||
TrackDto? Current { get; }
|
||||
|
||||
/// <summary>
|
||||
/// True when the queue has been loaded via <see cref="Arm"/> but no track has streamed yet —
|
||||
/// the embed's pre-gesture state. Set by <see cref="Arm"/>; cleared the moment playback actually
|
||||
/// starts (<see cref="Start"/>/<see cref="PlayRelease"/>/<see cref="Next"/>/<see cref="Previous"/>)
|
||||
/// or on <see cref="Clear"/>. The player bar reads this to route the first play gesture through
|
||||
/// <see cref="Start"/> (which begins the armed release) rather than streaming the staged track alone.
|
||||
/// </summary>
|
||||
bool IsArmed { get; }
|
||||
|
||||
/// <summary>True when there is a track after <see cref="CurrentIndex"/> to advance to.</summary>
|
||||
bool HasNext { get; }
|
||||
|
||||
@@ -59,6 +68,25 @@ public interface IQueueService
|
||||
/// </summary>
|
||||
Task PlayRelease(IEnumerable<TrackDto> tracks, int startIndex = 0);
|
||||
|
||||
/// <summary>
|
||||
/// Loads <paramref name="tracks"/> as the queue and sets the current position to index 0 WITHOUT
|
||||
/// streaming anything — the queue is "armed". This is the embed's prerender-safe entry point: it
|
||||
/// performs no JS interop, so it runs identically during prerender and after WASM boot. The first
|
||||
/// play gesture (see <see cref="IsArmed"/>) then starts playback via <see cref="Start"/>, which
|
||||
/// keeps the loaded release queued so it advances through its tracks. No-op when
|
||||
/// <paramref name="tracks"/> is empty (the queue stays empty and disarmed).
|
||||
/// </summary>
|
||||
void Arm(IEnumerable<TrackDto> tracks);
|
||||
|
||||
/// <summary>
|
||||
/// Begins playback of an armed queue (see <see cref="Arm"/>): streams the current track and clears
|
||||
/// <see cref="IsArmed"/>, leaving the loaded list and position intact so auto-advance carries on
|
||||
/// through the release. This is the first-gesture entry point the embed bar calls. No-op (and stays
|
||||
/// disarmed) when the queue is not armed or is empty — so it never double-streams or disturbs a
|
||||
/// queue already playing.
|
||||
/// </summary>
|
||||
Task Start();
|
||||
|
||||
/// <summary>Appends a track to the end of the queue without changing what is currently playing.</summary>
|
||||
void Enqueue(TrackDto track);
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public sealed class QueueService : IQueueService, IDisposable
|
||||
|
||||
public int CurrentIndex { get; private set; } = -1;
|
||||
|
||||
public bool IsArmed { get; private set; }
|
||||
|
||||
public TrackDto? Current =>
|
||||
CurrentIndex >= 0 && CurrentIndex < _items.Count ? _items[CurrentIndex] : null;
|
||||
|
||||
@@ -62,11 +64,34 @@ public sealed class QueueService : IQueueService, IDisposable
|
||||
_items.Clear();
|
||||
_items.AddRange(list);
|
||||
CurrentIndex = start;
|
||||
// Playback is now starting for real, so the queue is no longer merely armed.
|
||||
IsArmed = false;
|
||||
QueueChanged?.Invoke();
|
||||
|
||||
await PlayCurrent();
|
||||
}
|
||||
|
||||
public void Arm(IEnumerable<TrackDto> tracks)
|
||||
{
|
||||
var list = tracks as IReadOnlyList<TrackDto> ?? tracks.ToList();
|
||||
if (list.Count == 0) return;
|
||||
|
||||
_items.Clear();
|
||||
_items.AddRange(list);
|
||||
CurrentIndex = 0;
|
||||
IsArmed = true;
|
||||
// No PlayCurrent: arming is interop-free state only. The first play gesture drives Start().
|
||||
QueueChanged?.Invoke();
|
||||
}
|
||||
|
||||
public async Task Start()
|
||||
{
|
||||
if (!IsArmed) return;
|
||||
IsArmed = false;
|
||||
QueueChanged?.Invoke();
|
||||
await PlayCurrent();
|
||||
}
|
||||
|
||||
public void Enqueue(TrackDto track)
|
||||
{
|
||||
_items.Add(track);
|
||||
@@ -85,6 +110,7 @@ public sealed class QueueService : IQueueService, IDisposable
|
||||
{
|
||||
if (!HasNext) return;
|
||||
CurrentIndex++;
|
||||
IsArmed = false;
|
||||
QueueChanged?.Invoke();
|
||||
await PlayCurrent();
|
||||
}
|
||||
@@ -93,6 +119,7 @@ public sealed class QueueService : IQueueService, IDisposable
|
||||
{
|
||||
if (!HasPrevious) return;
|
||||
CurrentIndex--;
|
||||
IsArmed = false;
|
||||
QueueChanged?.Invoke();
|
||||
await PlayCurrent();
|
||||
}
|
||||
@@ -102,6 +129,7 @@ public sealed class QueueService : IQueueService, IDisposable
|
||||
if (_items.Count == 0 && CurrentIndex == -1) return;
|
||||
_items.Clear();
|
||||
CurrentIndex = -1;
|
||||
IsArmed = false;
|
||||
QueueChanged?.Invoke();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user