fix(qconnect): anchor play next to local current track
cx
This commit is contained in:
3 files changed
+61
-2
No files matched your search
@@ -38,6 +38,27 @@ describe('resolveQconnectPlayNextInsertAfter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('prefers the authoritative current track when renderer current is stale but still present in queue', () => {
|
||||
const queueSnapshot = buildQueueSnapshot([123452387, 123452388, 123452389], [123452387, 1, 2]);
|
||||
const rendererSnapshot: QconnectRendererSnapshot = {
|
||||
current_track: { track_id: 123452387, queue_item_id: 123452387 },
|
||||
next_track: { track_id: 123452388, queue_item_id: 1 }
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveQconnectPlayNextInsertAfter(queueSnapshot, rendererSnapshot, {
|
||||
authoritativeCurrentTrackId: 123452388
|
||||
})
|
||||
).toEqual({
|
||||
insertAfter: 1,
|
||||
strategy: 'authoritative_track_id_match',
|
||||
queueIndex: 1,
|
||||
nextQueueIndex: 1,
|
||||
matchedTrackId: 123452388,
|
||||
matchedQueueItemId: 1
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to queue track matching when renderer queue_item_id is stale', () => {
|
||||
const queueSnapshot = buildQueueSnapshot([201, 202, 203], [7001, 7002, 7003]);
|
||||
const rendererSnapshot: QconnectRendererSnapshot = {
|
||||
|
||||
@@ -64,6 +64,10 @@ export type QconnectPlayNextAnchorResolution = {
|
||||
matchedQueueItemId: number | null;
|
||||
};
|
||||
|
||||
type QconnectPlayNextResolutionOptions = {
|
||||
authoritativeCurrentTrackId?: number | null;
|
||||
};
|
||||
|
||||
function isPositiveNumber(value: unknown): value is number {
|
||||
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
||||
}
|
||||
@@ -102,11 +106,13 @@ function findTrackIndex(
|
||||
|
||||
export function resolveQconnectPlayNextInsertAfter(
|
||||
queueSnapshot: QconnectQueueSnapshot | null | undefined,
|
||||
rendererSnapshot: QconnectRendererSnapshot | null | undefined
|
||||
rendererSnapshot: QconnectRendererSnapshot | null | undefined,
|
||||
options: QconnectPlayNextResolutionOptions = {}
|
||||
): QconnectPlayNextAnchorResolution {
|
||||
const queueItems = queueSnapshot?.queue_items ?? [];
|
||||
const currentTrack = rendererSnapshot?.current_track ?? null;
|
||||
const nextTrack = rendererSnapshot?.next_track ?? null;
|
||||
const authoritativeCurrentTrackId = options.authoritativeCurrentTrackId;
|
||||
|
||||
if (queueItems.length === 0) {
|
||||
return {
|
||||
@@ -121,6 +127,34 @@ export function resolveQconnectPlayNextInsertAfter(
|
||||
|
||||
const currentQueueIndex = findQueueIndexByQueueItemId(queueItems, currentTrack?.queue_item_id);
|
||||
const nextQueueIndex = findQueueIndexByQueueItemId(queueItems, nextTrack?.queue_item_id);
|
||||
const authoritativeTrackIndexBeforeNext = nextQueueIndex !== null
|
||||
? findTrackIndexBefore(queueItems, authoritativeCurrentTrackId, nextQueueIndex)
|
||||
: null;
|
||||
const authoritativeCurrentTrackIndex = findTrackIndex(queueItems, authoritativeCurrentTrackId);
|
||||
const authoritativeTrackDisagreesWithRenderer = isPositiveNumber(authoritativeCurrentTrackId)
|
||||
&& authoritativeCurrentTrackId !== currentTrack?.track_id;
|
||||
|
||||
if (authoritativeTrackDisagreesWithRenderer && authoritativeTrackIndexBeforeNext !== null) {
|
||||
return {
|
||||
insertAfter: queueItems[authoritativeTrackIndexBeforeNext].queue_item_id,
|
||||
strategy: 'authoritative_track_id_before_renderer_next',
|
||||
queueIndex: authoritativeTrackIndexBeforeNext,
|
||||
nextQueueIndex,
|
||||
matchedTrackId: queueItems[authoritativeTrackIndexBeforeNext].track_id,
|
||||
matchedQueueItemId: queueItems[authoritativeTrackIndexBeforeNext].queue_item_id
|
||||
};
|
||||
}
|
||||
|
||||
if (authoritativeTrackDisagreesWithRenderer && authoritativeCurrentTrackIndex !== null) {
|
||||
return {
|
||||
insertAfter: queueItems[authoritativeCurrentTrackIndex].queue_item_id,
|
||||
strategy: 'authoritative_track_id_match',
|
||||
queueIndex: authoritativeCurrentTrackIndex,
|
||||
nextQueueIndex,
|
||||
matchedTrackId: queueItems[authoritativeCurrentTrackIndex].track_id,
|
||||
matchedQueueItemId: queueItems[authoritativeCurrentTrackIndex].queue_item_id
|
||||
};
|
||||
}
|
||||
|
||||
if (currentQueueIndex !== null) {
|
||||
return {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
addToQueue,
|
||||
type BackendQueueTrack
|
||||
} from '$lib/stores/queueStore';
|
||||
import { getPlayerState } from '$lib/stores/playerStore';
|
||||
import { addTrackToFavorites } from '$lib/services/playbackService';
|
||||
import { openPlaylistModal } from '$lib/stores/uiStore';
|
||||
import { showToast as storeShowToast, type ToastType } from '$lib/stores/toastStore';
|
||||
@@ -123,7 +124,10 @@ async function resolveQconnectPlayNextInsertAfterFromSnapshots(): Promise<Qconne
|
||||
|
||||
const queueSnapshot = queueSnapshotResult.status === 'fulfilled' ? queueSnapshotResult.value : null;
|
||||
const rendererSnapshot = rendererSnapshotResult.status === 'fulfilled' ? rendererSnapshotResult.value : null;
|
||||
const resolution = resolveQconnectPlayNextInsertAfter(queueSnapshot, rendererSnapshot);
|
||||
const localCurrentTrackId = getPlayerState().currentTrack?.id ?? null;
|
||||
const resolution = resolveQconnectPlayNextInsertAfter(queueSnapshot, rendererSnapshot, {
|
||||
authoritativeCurrentTrackId: localCurrentTrackId
|
||||
});
|
||||
|
||||
const snapshotError = [
|
||||
queueSnapshotResult.status === 'rejected'
|
||||
|
||||
Reference in new issue
Block a user