From 4134729543139f4934c4ec4e4c6a2cda0606bcdf Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 17 Sep 2026 14:36:25 +0200 Subject: fix(hub): trace the subtitle path end to end in the player MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A subtitle button that spins for ever and never shows a track could not be told apart from a node that answered, a blob that never arrived, or a track attached with no cues in it: the whole path was silent. Every step of it happens on someone else's machine, over a link, against a file that may be gigabytes, so the only question worth asking when it does not finish is which step did not — and nothing recorded that. It now logs the request, the node's answer with hash, size and elapsed time, the fetched blob and its chunk count, the attachment, and the live TextTrack's mode and cue count. A failure says how long it took before failing. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01UGY17EPph5LsLzePPXhUVc --- .../src/meshbay_hub/static/video-player.js | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'packages') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/video-player.js b/packages/meshbay-hub/src/meshbay_hub/static/video-player.js index a150512..596a848 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/video-player.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/video-player.js @@ -1100,21 +1100,39 @@ function VideoPlayer({ entry, transportRef, gekRef, onClose, onDownload }) { if (!transport) return; setSubtitleTrack(track.i); setSubtitleBusy(true); + // Traced end to end on purpose. Every step here happens on someone else's + // machine, over a link, against a file that may be gigabytes: when this + // does not finish, the only question worth asking is *which* step did not, + // and no other record of that exists. + const t0 = performance.now(); + console.log('[MeshBay] subtitle: asking for track', track.i, 'of', entry.id.slice(0, 12)); try { const info = await transport.requestSubtitle(entry.id, track.i); + console.log('[MeshBay] subtitle: node answered in', + Math.round(performance.now() - t0), 'ms —', + 'hash=', String(info.hash).slice(0, 12), 'size=', info.size, + 'mime=', info.mime); const chunks = await pipelinedDownload( transport, gekRef.current, info.hash, Math.ceil(info.size / CHUNK_SIZE)); + console.log('[MeshBay] subtitle: blob fetched in', + Math.round(performance.now() - t0), 'ms —', + chunks.length, 'chunk(s)'); const url = URL.createObjectURL( new Blob(chunks, { type: info.mime || 'text/vtt' })); // Someone changed their mind while this was in flight. Dropping the blob // rather than installing it is the whole point of the generation: the // reply that arrives last is not the choice that was made last. - if (subtitleGenRef.current !== gen) { URL.revokeObjectURL(url); return; } + if (subtitleGenRef.current !== gen) { + console.log('[MeshBay] subtitle: superseded, blob dropped'); + URL.revokeObjectURL(url); return; + } subtitleUrlRef.current = url; setSubtitleUrl(url); + console.log('[MeshBay] subtitle: track attached'); } catch (err) { if (subtitleGenRef.current !== gen) return; - console.warn('[MeshBay] subtitle track', track.i, 'failed:', err); + console.warn('[MeshBay] subtitle track', track.i, 'failed after', + Math.round(performance.now() - t0), 'ms:', err); setSubtitleTrack(null); setSubtitleError(true); } finally { @@ -1137,6 +1155,10 @@ function VideoPlayer({ entry, transportRef, gekRef, onClose, onDownload }) { for (let i = 0; i < v.textTracks.length; i++) { v.textTracks[i].mode = subtitleUrl ? 'showing' : 'disabled'; } + console.log('[MeshBay] subtitle: textTracks =', v.textTracks.length, + 'mode =', v.textTracks[0] && v.textTracks[0].mode, + 'cues =', v.textTracks[0] && v.textTracks[0].cues + ? v.textTracks[0].cues.length : 'none'); }, [subtitleUrl]); useEffect(() => { -- cgit v1.2.3