mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 07:15:47 +00:00
exp(orbit): iterative refinements
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,12 +70,32 @@ export interface OrbitState {
|
||||
positionAt: number;
|
||||
/** Upcoming queue (not including `currentTrack`). */
|
||||
queue: OrbitQueueItem[];
|
||||
/**
|
||||
* Snapshot of the host's actual upcoming play queue (everything after
|
||||
* `queueIndex`), capped at `ORBIT_PLAY_QUEUE_LIMIT` to fit the state-blob
|
||||
* byte budget. Used by the guest view so guests see what's next in the
|
||||
* host's player rather than just the suggestions backlog. `addedBy`
|
||||
* carries the original suggester when known, otherwise the host.
|
||||
*/
|
||||
playQueue?: { trackId: string; addedBy: string }[];
|
||||
/**
|
||||
* Total length of the host's upcoming play queue, even when `playQueue`
|
||||
* was truncated. Lets the guest UI render a "+ N more" hint.
|
||||
*/
|
||||
playQueueTotal?: number;
|
||||
/** Epoch ms of the last queue shuffle. */
|
||||
lastShuffle: number;
|
||||
/** Currently-present participants (excluding the host). */
|
||||
participants: OrbitParticipant[];
|
||||
/** Usernames blocked from re-joining this session. */
|
||||
kicked: string[];
|
||||
/**
|
||||
* Soft-removed users — short-lived markers (TTL `ORBIT_REMOVED_TTL_MS`)
|
||||
* so the affected guest's next poll surfaces a "you were removed" modal.
|
||||
* Unlike `kicked`, the user is NOT blocked from re-joining via the
|
||||
* invite link. Aged out by the host's sweep tick.
|
||||
*/
|
||||
removed?: { user: string; at: number }[];
|
||||
/** Set when the host has ended the session; guests should exit on next poll. */
|
||||
ended?: boolean;
|
||||
/** Host-settable session rules; absent on older clients — treat missing as all-defaults. */
|
||||
@@ -110,6 +130,14 @@ export const ORBIT_STATE_MAX_BYTES = 4096;
|
||||
/** Default value of `OrbitState.maxUsers` when the host hasn't picked one. */
|
||||
export const ORBIT_DEFAULT_MAX_USERS = 10;
|
||||
|
||||
/**
|
||||
* Hard cap on `playQueue` length. ~30 tracks × ~50 bytes each ≈ 1.5 KB,
|
||||
* leaving room for the rest of the state blob under `ORBIT_STATE_MAX_BYTES`.
|
||||
* Excess upcoming tracks are surfaced via the `playQueueTotal` count so the
|
||||
* guest UI can show a "+ N more" hint instead of pretending there's nothing.
|
||||
*/
|
||||
export const ORBIT_PLAY_QUEUE_LIMIT = 30;
|
||||
|
||||
/**
|
||||
* Build a fresh state blob for a brand-new session. Used by the host on start.
|
||||
*/
|
||||
@@ -135,6 +163,9 @@ export function makeInitialOrbitState(args: {
|
||||
lastShuffle: now,
|
||||
participants: [],
|
||||
kicked: [],
|
||||
removed: [],
|
||||
playQueue: [],
|
||||
playQueueTotal: 0,
|
||||
settings: { ...ORBIT_DEFAULT_SETTINGS },
|
||||
};
|
||||
}
|
||||
@@ -156,6 +187,11 @@ export function parseOrbitState(raw: unknown): OrbitState | null {
|
||||
// currentTrack can be null or an object — no deeper validation here; the
|
||||
// producer is our own code and an item with missing fields would only hurt
|
||||
// the attribution UI, not correctness.
|
||||
// `removed` is optional (older hosts won't write it); coerce to [] if absent or malformed.
|
||||
if (!Array.isArray(s.removed)) s.removed = [];
|
||||
// `playQueue` / `playQueueTotal` are optional (older hosts won't write them).
|
||||
if (!Array.isArray(s.playQueue)) s.playQueue = [];
|
||||
if (typeof s.playQueueTotal !== 'number') s.playQueueTotal = (s.playQueue?.length ?? 0);
|
||||
return s as OrbitState;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user