diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/transport.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/transport.js | 37 |
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') { |