aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/transport.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/transport.js37
1 files changed, 37 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/transport.js b/packages/meshbay-hub/src/meshbay_hub/static/transport.js
index 262b00e..63c71ff 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/transport.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/transport.js
@@ -1607,6 +1607,25 @@ class MeshBayTransport {
}
/**
+ * One embedded subtitle track, extracted node-side to WebVTT. Returns
+ * `{ hash, size, mime, track }` — the *cache* hash to pull through the
+ * ordinary file_req/chunk path, the same indirection as an audio transcode
+ * or a TMDB poster, and cached node-side under the file's own id so a film
+ * is extracted once rather than once per viewing.
+ *
+ * `track` is the ordinal the node published in `stream_init.subtitle_tracks`
+ * and is passed back untouched: it counts every subtitle stream in the
+ * container, including the bitmap ones that are never listed, so it is not
+ * a position in the list this client received.
+ */
+ async requestSubtitle(fileId, track) {
+ const msg = await this._sendAndWait(
+ { type: 'subtitle_req', v: '0.9', file_id: fileId, track }, 90000);
+ if (msg.type === 'error') throw new Error(msg.detail);
+ return msg;
+ }
+
+ /**
* Whether MusicBrainz lookups run for this group at all — per-group from
* the start (docs/musicbay.md §3.2/§6). Signed like setTmdbEnabled.
*/
@@ -2968,6 +2987,12 @@ class MeshBayTransport {
// Same reordering hazard as media_meta_req: the player prefetches
// the next track while the current one may still be transcoding.
: obj.type === 'audio_transcode_req' ? `audio_transcode:${obj.file_id}`
+ // The track ordinal is part of the key, not just the file id: a
+ // viewer who opens the menu and picks a second language before the
+ // first extraction has answered has two of these in flight for the
+ // same film, and the one that arrives first is not necessarily the
+ // one that was asked for first.
+ : obj.type === 'subtitle_req' ? `subtitle:${obj.file_id}:${obj.track}`
// Two-step admin-op flow (_authorizeAdminOp) — see ADMIN_OP_TYPES'
// own comment for the race this closes. The initial request and
// the admin_response that follows it are keyed the same way
@@ -3399,6 +3424,18 @@ class MeshBayTransport {
return;
}
+ // Keyed on file *and* track — see the `subtitle_req` key above. The node
+ // echoes `track` back for exactly this: without it a reply could only be
+ // matched to the film, and the two tracks of one film are precisely the
+ // pair that can be in flight together.
+ if (msg.type === 'subtitle_resp') {
+ const key = `subtitle:${msg.file_id}:${msg.track}`;
+ for (const [, handler] of this._pending) {
+ if (handler._key === key) { handler.resolve(msg); return; }
+ }
+ return;
+ }
+
// Same reasoning as media_meta_resp: keyed, not arrival-order, and
// "nobody's waiting any more" must not fall through either.
if (msg.type === 'season_meta_resp') {