summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/music-player.js
diff options
context:
space:
mode:
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.js32
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.