aboutsummaryrefslogtreecommitdiffstats
path: root/docs/MESHBAY_DESIGN.md
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-17 13:39:23 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-17 13:39:23 +0200
commitad4ca3229002997934ccb5b2eaeb553c13b8888f (patch)
treea680f9e5d3ba43a84236b84f73e0b71eaf916db4 /docs/MESHBAY_DESIGN.md
parent3e6d514663a5df1be3b2f0286c5f67f669d9c1d6 (diff)
downloadmeshbay-ad4ca3229002997934ccb5b2eaeb553c13b8888f.tar.gz
feat: embedded subtitles in the video player (MNP 3.3)
MSE decodes no in-band text track, so a subtitle cannot ride inside the fragmented MP4 the player is fed. The node extracts one track whole, converts it to WebVTT and caches it under its own hash; the client pulls that blob through the ordinary file_req/chunk path and hangs a <track> on the video element — the same indirection as a TMDB poster or an audio transcode, which is what makes a film's subtitles extracted once in the life of the file rather than once per viewing. Whole-file also makes the cues absolute, so a seek and an audio-language change both leave the track untouched. **The ordinal counts every subtitle stream, including the ones never listed.** Only text codecs are offered: a bitmap track (PGS, VOBSUB — about a fifth of a real library) has no path to WebVTT without OCR, and one extracted anyway yields a header with no cues, which is a menu entry that shows nothing and reports no error. Numbering the survivors of that filter would give a PGS/SRT/SRT file the ordinals 0 and 1 for its text tracks and `-map 0:s:0` would then extract the PGS — the same trap `AudioTrack.ordinal` exists for, one level deeper. A fixture whose first subtitle stream cannot be decoded pins it, and the handler checks membership of the probed list, never a range. Additive and MINOR: the selector is drawn from `subtitle_tracks` in the node's own `stream_init` and from no version number, so `subtitle_req` is never sent to a peer that would not answer it. The floor stays at 3.0. Also here: a failed extraction never touches playback, a superseded reply cannot install its blob over a newer choice, and `_languageName` is shared with the audio labels — lifted by both label harnesses, since a lift that names one function stops covering the rule the moment logic moves out of it. Tests: 9 node (tracks told apart by the words in the extracted cues, not by tags), 10 client. Full suite green: 1545 node/common, 1252 hub. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGY17EPph5LsLzePPXhUVc
Diffstat (limited to 'docs/MESHBAY_DESIGN.md')
-rw-r--r--docs/MESHBAY_DESIGN.md14
1 files changed, 10 insertions, 4 deletions
diff --git a/docs/MESHBAY_DESIGN.md b/docs/MESHBAY_DESIGN.md
index 92f56ef..817afaa 100644
--- a/docs/MESHBAY_DESIGN.md
+++ b/docs/MESHBAY_DESIGN.md
@@ -1132,13 +1132,16 @@ it**.
> the branches come back.**
**The floor is not the current version, and MINOR additions are why.** It is
-`MNP_MIN_SUPPORTED` in `handshake.py`, it equals the last MAJOR, and 3.1 and 3.2
-have both been added above it without moving it. So a peer can be reachable and
+`MNP_MIN_SUPPORTED` in `handshake.py`, it equals the last MAJOR, and 3.1, 3.2 and
+3.3 have all been added above it without moving it. So a peer can be reachable and
still not do something the current version can, and the client has to cope with
that — **by reading the peer's own answer, never by comparing version numbers**.
3.2's audio tracks are the worked example: the node lists them in `stream_init`,
the client draws its selector from that list, and a node that sends no list gets no
-selector. That is not the branch the box above refuses. The branch it refuses is a
+selector. 3.3's subtitles repeat it exactly, and add the case where the list is
+narrower than the file: only the tracks the node can convert to WebVTT are named,
+so what the menu offers is what will actually appear, and a track's ordinal is
+therefore not its position in the list. That is not the branch the box above refuses. The branch it refuses is a
flag the client sets from a version it parsed; this is the node stating what it
has, in the same message the feature already needed, and it takes exactly one path
per peer because the peer said which.
@@ -2940,7 +2943,8 @@ The hub, the node daemon, both transports, the unified handshake, admission and
pairing, device linking with member-visible evidence, per-node identity, named roots
with RO/RW and eject/plug, the indexer with partial hashing, uploads, the sealed
index and sealed upload path, encrypted chat with epochs, video streaming with
-seeking, transfer leases with queueing, pause and resume, the group-application
+seeking, audio-language and subtitle selection, transfer leases with queueing,
+pause and resume, the group-application
framework with Chat, Files, Videos,
Music and Photos, cross-group search with source merging, the operator CLI and
loopback control API, the desktop client through its identity and download stages,
@@ -2956,6 +2960,8 @@ account recovery, and the Windows port through packaging.
| **Stage D11** | Windows clean-machine install verification and code signing |
| **Stage D12** | Release key, signed repositories, updates through the OS |
| — | DLNA/UPnP casting (§11.4) |
+| — | **Subtitles on a Chromecast.** The client shows them; the relay re-serves the same fMP4 bytes the browser gets, and a `<track>` in the renderer's DOM never leaves the machine. The route is an out-of-band `/subs.vtt` on the relay plus `tracks` on `player.load()`, and its one unknown — whether the receiver's CORS requirement can be met by the relay — cannot be settled by reading code |
+| — | **Bitmap subtitles** (PGS, VOBSUB — about a fifth of the embedded streams). No WebVTT without OCR; they are not listed rather than listed and blank. Burn-in covers them and costs `-c:v copy`, which is what the eight-slot sizing assumes never happens |
| — | Delegation (§3.4) |
| — | Tier 3 roster attestation (§3.3) |
| — | Playlists (§9.10) |