// Single-active-player coordination, with optional cross-tab sync. // Each Player instance registers an id and a `pause()` callback. When another // player becomes active, all others get paused. Cross-tab via BroadcastChannel. const CHANNEL = 'djangocfg-audioplayer:active'; type Listener = (activeId: string | null) => void; let activeId: string | null = null; let lastActiveId: string | null = null; const listeners = new Set(); const pausers = new Map void>(); let channel: BroadcastChannel | null = null; function getChannel(): BroadcastChannel | null { if (typeof BroadcastChannel === 'undefined') return null; if (channel) return channel; channel = new BroadcastChannel(CHANNEL); channel.addEventListener('message', (e) => { const next = typeof e.data === 'string' ? e.data : null; if (next === activeId) return; activeId = next; if (next) lastActiveId = next; for (const [id, pause] of pausers) { if (id !== next) pause(); } for (const l of listeners) l(next); }); return channel; } export function registerPlayer(id: string, pause: () => void): () => void { pausers.set(id, pause); return () => { pausers.delete(id); if (activeId === id) activeId = null; }; } export function setActivePlayer(id: string | null): void { if (activeId === id) return; activeId = id; if (id) lastActiveId = id; for (const [pid, pause] of pausers) { if (pid !== id) pause(); } for (const l of listeners) l(id); getChannel()?.postMessage(id); } export function getActivePlayer(): string | null { return activeId; } export function getLastActivePlayer(): string | null { return lastActiveId; } export function subscribeActivePlayer(cb: Listener): () => void { listeners.add(cb); getChannel(); // ensure channel hot return () => listeners.delete(cb); }