refactor(player): E.1 — extract queue-identity helpers to utils/ (#563)

Four pure helpers move out of playerStore: normalizeAnalysisTrackId,
sameQueueTrackId, queuesStructuralEqual, shallowCloneQueueTracks.
Adds focused unit tests for the stream:-prefix normalization and the
no-op detection that prevents unnecessary queue rewrites.

Behaviour preserved verbatim. playerStore 3618 → 3598 LOC.
This commit is contained in:
Frank Stellmacher
2026-05-12 11:08:21 +02:00
committed by GitHub
parent f0971d5108
commit dcf3dd98e0
3 changed files with 150 additions and 26 deletions
+6 -26
View File
@@ -24,6 +24,12 @@ import { resolveReplayGainDb } from '../utils/resolveReplayGainDb';
import { shuffleArray } from '../utils/shuffleArray';
import { songToTrack } from '../utils/songToTrack';
import { buildInfiniteQueueCandidates } from '../utils/buildInfiniteQueueCandidates';
import {
normalizeAnalysisTrackId,
queuesStructuralEqual,
sameQueueTrackId,
shallowCloneQueueTracks,
} from '../utils/queueIdentity';
import { getWindowKind } from '../app/windowKind';
// Re-export for backward compatibility with the ~30 call sites that still
@@ -370,10 +376,6 @@ export function consumePendingQueueListScrollTop(): number | undefined {
return v;
}
function shallowCloneQueueTracks(queue: Track[]): Track[] {
return queue.map(t => ({ ...t }));
}
function queueUndoSnapshotFromState(s: PlayerState): QueueUndoSnapshot {
const scrollTop = readQueueListScrollTopForUndo();
return {
@@ -476,28 +478,6 @@ function emitNormalizationDebug(step: string, details?: Record<string, unknown>)
}).catch(() => {});
}
function normalizeAnalysisTrackId(trackId?: string | null): string | null {
if (!trackId) return null;
if (trackId.startsWith('stream:')) return trackId.slice('stream:'.length);
return trackId;
}
/** Compare track ids across `stream:` / bare Subsonic forms. */
function sameQueueTrackId(a: string | undefined | null, b: string | undefined | null): boolean {
if (a == null || b == null) return false;
const na = normalizeAnalysisTrackId(a) ?? a;
const nb = normalizeAnalysisTrackId(b) ?? b;
return na === nb;
}
function queuesStructuralEqual(a: Track[], b: Track[]): boolean {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (!sameQueueTrackId(a[i]?.id, b[i]?.id)) return false;
}
return true;
}
function normalizationAlmostEqual(a: number | null, b: number | null, eps = 0.12): boolean {
if (a == null && b == null) return true;
if (a == null || b == null) return false;