feat(player): playNext for M7 #372 track-actions menu
Inserts at _queue[_index + 1] so the next-up slot is overwritten with the chosen track. Empty-queue seeding mirrors enqueueTrack's behavior. Clears _radioSeedId since user-driven enqueue invalidates the M4c auto-refresh trigger.
This commit is contained in:
@@ -211,6 +211,22 @@ export function enqueueTrack(t: TrackRef): void {
|
|||||||
if (_state === 'idle') _index = 0;
|
if (_state === 'idle') _index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* M7 #372: insert a track immediately after the currently-playing one
|
||||||
|
* so it plays next. If the queue is empty, behaves like enqueueTrack
|
||||||
|
* and starts the queue with this track at index 0.
|
||||||
|
*/
|
||||||
|
export function playNext(t: TrackRef): void {
|
||||||
|
_radioSeedId = null; // M4c: any user-driven enqueue clears the radio refresh state
|
||||||
|
if (_queue.length === 0) {
|
||||||
|
_queue = [t];
|
||||||
|
_index = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const next = _index + 1;
|
||||||
|
_queue = [..._queue.slice(0, next), t, ..._queue.slice(next)];
|
||||||
|
}
|
||||||
|
|
||||||
export function enqueueTracks(ts: TrackRef[]): void {
|
export function enqueueTracks(ts: TrackRef[]): void {
|
||||||
_radioSeedId = null; // M4c
|
_radioSeedId = null; // M4c
|
||||||
if (ts.length === 0) return;
|
if (ts.length === 0) return;
|
||||||
|
|||||||
@@ -280,7 +280,7 @@ describe('player store — shuffle + repeat + audio reports', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
import { enqueueTrack, enqueueTracks, playRadio } from './store.svelte';
|
import { enqueueTrack, enqueueTracks, playNext, playRadio } from './store.svelte';
|
||||||
import * as client from '$lib/api/client';
|
import * as client from '$lib/api/client';
|
||||||
|
|
||||||
describe('player store — enqueue + playRadio', () => {
|
describe('player store — enqueue + playRadio', () => {
|
||||||
@@ -334,6 +334,22 @@ describe('player store — enqueue + playRadio', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('playNext', () => {
|
||||||
|
test('splices at _index + 1, leaving _index unchanged', () => {
|
||||||
|
playQueue([track('a'), track('b')], 0);
|
||||||
|
playNext(track('c'));
|
||||||
|
expect(player.queue.map((t) => t.id)).toEqual(['a', 'c', 'b']);
|
||||||
|
expect(player.index).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('on empty queue, seeds with the track at index 0', () => {
|
||||||
|
playQueue([]);
|
||||||
|
playNext(track('x'));
|
||||||
|
expect(player.queue.map((t) => t.id)).toEqual(['x']);
|
||||||
|
expect(player.index).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('M4c radio auto-refresh at 80%', () => {
|
describe('M4c radio auto-refresh at 80%', () => {
|
||||||
test('refresh fires at 80% queue consumption', async () => {
|
test('refresh fires at 80% queue consumption', async () => {
|
||||||
const tracks = [track('seed'), track('t1'), track('t2'), track('t3'), track('t4')];
|
const tracks = [track('seed'), track('t1'), track('t2'), track('t3'), track('t4')];
|
||||||
|
|||||||
Reference in New Issue
Block a user