mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-21 23:05:46 +00:00
fix(orbit): host single-track playTrack appends instead of replacing (#529)
* fix(orbit): host single-track playTrack appends instead of replacing Reported by cucadmuh: "When playing from offline, the Orbit queue doesn't get appended to — it gets overwritten." Root cause: the Orbit bulk-guard fires only when `queue.length > 1`. A `playTrack(track, [track])` call (one example: OfflineLibrary's "Play this album" on a single-track album, but any UI that passes an explicit 1-track replacement queue triggers it) slips past the guard and replaces the host's `playerStore.queue`. The host's queue *is* the shared Orbit queue — replacing it wipes every guest suggestion + every upcoming track in one click. Re-route to append + jump when role is host: if the track is already in the queue, jump to that slot; otherwise append it and jump to the new tail. The guest path is intentionally left alone — guests opting out of host-sync via a local Play is the existing "guest running their own show" divergence behaviour. `useOrbitGuest`'s `syncToHost` is the only guest-side caller of `playTrack(track, [track])`, and it never matches `role === 'host'` so it's never intercepted. * docs(changelog): add host single-track Orbit-queue protection bullet
This commit is contained in:
committed by
GitHub
parent
eae649bcad
commit
25507888a9
@@ -2523,6 +2523,34 @@ export const usePlayerStore = create<PlayerState>()(
|
||||
}
|
||||
}
|
||||
|
||||
// Orbit-host single-track protection. The host's `playerStore.queue`
|
||||
// *is* the shared Orbit queue. A `playTrack(track, [track])` call
|
||||
// (e.g. OfflineLibrary's "Play this album" on a single-track album,
|
||||
// or any other surface that explicitly passes a 1-track replacement
|
||||
// queue) would otherwise blow away every guest suggestion + every
|
||||
// upcoming track. Re-route to append + jump so the queue survives.
|
||||
// Guest stays unguarded — a guest clicking Play locally is choosing
|
||||
// to opt out of host-sync, which is the existing "guest is running
|
||||
// their own show" path. `useOrbitGuest`'s `syncToHost` is also a
|
||||
// guest-only call site, so it's never intercepted here.
|
||||
if (!_orbitConfirmed && queue && queue.length === 1) {
|
||||
const orbitRole = useOrbitStore.getState().role;
|
||||
if (orbitRole === 'host') {
|
||||
const current = get().queue;
|
||||
const currentTrackId = current[get().queueIndex]?.id;
|
||||
if (track.id !== currentTrackId) {
|
||||
const existsAt = current.findIndex(t => sameQueueTrackId(t.id, track.id));
|
||||
if (existsAt >= 0) {
|
||||
get().playTrack(track, current, manual, true, existsAt);
|
||||
} else {
|
||||
const newQueue = [...current, track];
|
||||
get().playTrack(track, newQueue, manual, true, newQueue.length - 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ghost-command guard: if a gapless switch happened within 500 ms,
|
||||
// this playTrack call is likely a stale IPC echo — suppress it.
|
||||
if (Date.now() - lastGaplessSwitchTime < 500) {
|
||||
|
||||
Reference in New Issue
Block a user