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', () => {
|
it('falls back to queue track matching when renderer queue_item_id is stale', () => {
|
||||||
const queueSnapshot = buildQueueSnapshot([201, 202, 203], [7001, 7002, 7003]);
|
const queueSnapshot = buildQueueSnapshot([201, 202, 203], [7001, 7002, 7003]);
|
||||||
const rendererSnapshot: QconnectRendererSnapshot = {
|
const rendererSnapshot: QconnectRendererSnapshot = {
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ export type QconnectPlayNextAnchorResolution = {
|
|||||||
matchedQueueItemId: number | null;
|
matchedQueueItemId: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type QconnectPlayNextResolutionOptions = {
|
||||||
|
authoritativeCurrentTrackId?: number | null;
|
||||||
|
};
|
||||||
|
|
||||||
function isPositiveNumber(value: unknown): value is number {
|
function isPositiveNumber(value: unknown): value is number {
|
||||||
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
||||||
}
|
}
|
||||||
@@ -102,11 +106,13 @@ function findTrackIndex(
|
|||||||
|
|
||||||
export function resolveQconnectPlayNextInsertAfter(
|
export function resolveQconnectPlayNextInsertAfter(
|
||||||
queueSnapshot: QconnectQueueSnapshot | null | undefined,
|
queueSnapshot: QconnectQueueSnapshot | null | undefined,
|
||||||
rendererSnapshot: QconnectRendererSnapshot | null | undefined
|
rendererSnapshot: QconnectRendererSnapshot | null | undefined,
|
||||||
|
options: QconnectPlayNextResolutionOptions = {}
|
||||||
): QconnectPlayNextAnchorResolution {
|
): QconnectPlayNextAnchorResolution {
|
||||||
const queueItems = queueSnapshot?.queue_items ?? [];
|
const queueItems = queueSnapshot?.queue_items ?? [];
|
||||||
const currentTrack = rendererSnapshot?.current_track ?? null;
|
const currentTrack = rendererSnapshot?.current_track ?? null;
|
||||||
const nextTrack = rendererSnapshot?.next_track ?? null;
|
const nextTrack = rendererSnapshot?.next_track ?? null;
|
||||||
|
const authoritativeCurrentTrackId = options.authoritativeCurrentTrackId;
|
||||||
|
|
||||||
if (queueItems.length === 0) {
|
if (queueItems.length === 0) {
|
||||||
return {
|
return {
|
||||||
@@ -121,6 +127,34 @@ export function resolveQconnectPlayNextInsertAfter(
|
|||||||
|
|
||||||
const currentQueueIndex = findQueueIndexByQueueItemId(queueItems, currentTrack?.queue_item_id);
|
const currentQueueIndex = findQueueIndexByQueueItemId(queueItems, currentTrack?.queue_item_id);
|
||||||
const nextQueueIndex = findQueueIndexByQueueItemId(queueItems, nextTrack?.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) {
|
if (currentQueueIndex !== null) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
addToQueue,
|
addToQueue,
|
||||||
type BackendQueueTrack
|
type BackendQueueTrack
|
||||||
} from '$lib/stores/queueStore';
|
} from '$lib/stores/queueStore';
|
||||||
|
import { getPlayerState } from '$lib/stores/playerStore';
|
||||||
import { addTrackToFavorites } from '$lib/services/playbackService';
|
import { addTrackToFavorites } from '$lib/services/playbackService';
|
||||||
import { openPlaylistModal } from '$lib/stores/uiStore';
|
import { openPlaylistModal } from '$lib/stores/uiStore';
|
||||||
import { showToast as storeShowToast, type ToastType } from '$lib/stores/toastStore';
|
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 queueSnapshot = queueSnapshotResult.status === 'fulfilled' ? queueSnapshotResult.value : null;
|
||||||
const rendererSnapshot = rendererSnapshotResult.status === 'fulfilled' ? rendererSnapshotResult.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 = [
|
const snapshotError = [
|
||||||
queueSnapshotResult.status === 'rejected'
|
queueSnapshotResult.status === 'rejected'
|
||||||
|
|||||||
Reference in new issue
Block a user