diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/music-player.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/music-player.js | 32 |
1 files changed, 12 insertions, 20 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/music-player.js b/packages/meshbay-hub/src/meshbay_hub/static/music-player.js index ffe092d..2d3c002 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/music-player.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/music-player.js @@ -8,13 +8,14 @@ import { CHUNK_SIZE, pipelinedDownload } from './file-utils.js'; /** * The Music app's persistent player bar (docs/musicbay.md §2.3, §7.2). * - * Owned and rendered by group-page.js, *not* by music-app.js: it is the one - * piece of this feature that lives outside the tab-switched area, so - * playback survives navigating to Chat or Files, exactly the way the - * video/preview modals are shell-owned rather than owned by whichever app - * opened them. music-app.js never touches audio state directly — it only - * calls `onPlayQueue(tracks, startIndex)`, threaded down from group-page.js, - * to hand this component a new queue. + * Owned and rendered by app.js — the router's parent — so playback survives + * navigating between groups, search, and other pages. Both group-page.js + * and search-page.js trigger playback by calling `onPlayQueue(tracks, + * startIndex)`, which flows up to app.js. + * + * Each track carries a `groupId`; the player calls `getConnection(groupId)` + * per track to obtain the right transport — a pool-based lazy connection + * that transparently handles single-group and cross-group queues. * * No streaming, no MSE, no node-side transcode pool for the common case: a * track is a few megabytes, so it is downloaded and decrypted once through @@ -177,7 +178,7 @@ function QueuePanel({ tracks, order, pos, onSelect, onClose }) { `; } -function MusicPlayerBar({ transportRef, gekRef, queue, onClose, userPrefs }) { +function MusicPlayerBar({ getConnection, queue, onClose, userPrefs }) { const audioRef = useRef(null); const blobCacheRef = useRef(new Map()); // file id -> { url, order: insertion index } const blobInsertRef = useRef(0); @@ -284,13 +285,8 @@ function MusicPlayerBar({ transportRef, gekRef, queue, onClose, userPrefs }) { const fetchTrackBlob = useCallback(async (entry) => { const cached = blobCacheRef.current.get(entry.id); if (cached) return cached.url; - const transport = transportRef.current; + const { transport, gek } = await getConnection(entry.groupId); if (!transport) throw new Error(t('music.err_transport')); - // A track ending (or "next") right after a screen-lock reconnect started - // is exactly when this used to throw: `connected` was still false because - // the reconnect it only had to wait a few seconds for hadn't landed yet. - // waitForReconnect is a no-op when nothing is in flight, so this costs - // nothing on the ordinary path. if (!transport.connected) await transport.waitForReconnect(); if (!transport.connected) throw new Error(t('music.err_transport')); @@ -310,17 +306,13 @@ function MusicPlayerBar({ transportRef, gekRef, queue, onClose, userPrefs }) { } const totalChunks = Math.ceil(downloadSize / CHUNK_SIZE); - const chunks = await pipelinedDownload(transport, gekRef.current, downloadId, totalChunks); + const chunks = await pipelinedDownload(transport, gek, downloadId, totalChunks); const blob = new Blob(chunks, { type: mime }); const url = URL.createObjectURL(blob); - // Keyed by the track's own id, not `downloadId` — the transcode cache - // hash is an implementation detail of getting there, and a second play - // of the same track must still hit this cache rather than re-requesting - // a transcode the node already ran once. blobCacheRef.current.set(entry.id, { url, order: blobInsertRef.current++ }); evictOldBlobs(); return url; - }, [transportRef, gekRef, evictOldBlobs]); + }, [getConnection, evictOldBlobs]); // Silently warms the cache for the next tracks so pressing "next" doesn't // visibly wait (musicbay.md §2.2) — best-effort, never surfaces an error. |