aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-common/src/meshbay_common/__init__.py
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 /packages/meshbay-common/src/meshbay_common/__init__.py
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 'packages/meshbay-common/src/meshbay_common/__init__.py')
-rw-r--r--packages/meshbay-common/src/meshbay_common/__init__.py20
1 files changed, 19 insertions, 1 deletions
diff --git a/packages/meshbay-common/src/meshbay_common/__init__.py b/packages/meshbay-common/src/meshbay_common/__init__.py
index c4c4bf4..c5b0279 100644
--- a/packages/meshbay-common/src/meshbay_common/__init__.py
+++ b/packages/meshbay-common/src/meshbay_common/__init__.py
@@ -201,5 +201,23 @@ __version__ = "0.14.0"
# compatibility switch 3.0 refused, because there is no second branch on the
# node: a 3.2 node always enumerates, always honours what it is asked for, and
# always says which track it used. `MNP_MIN_SUPPORTED` does not move.
-MNP_VERSION = "3.2"
+#
+# **3.3 (2026-09-17): the viewer picks a subtitle track.**
+#
+# `stream_init` carries `subtitle_tracks` (ordinal, language, title, codec);
+# `subtitle_req` asks for one by ordinal and `subtitle_resp` names a cache hash
+# to pull through the ordinary `file_req`/`file_chunk` path, exactly as
+# `audio_transcode_resp` does. Subtitles travel beside the stream because MSE
+# decodes no in-band text track, and whole-file because the extraction is then
+# absolute in time: a seek re-extracts nothing and the `<track>` is untouched.
+#
+# **Additive, MINOR, and for once nothing can be served wrongly.** An
+# `audio_track` a 3.2 node ignores is the wrong language played in silence;
+# `subtitle_req` has no such failure — a node too old to know the message
+# answers `unknown message type` and the client shows no subtitles, which is
+# what it showed before. The selector is still drawn from `subtitle_tracks` in
+# the node's own `stream_init` and from no version number, so the request is
+# never sent to a peer that could not answer it. `MNP_MIN_SUPPORTED` does not
+# move.
+MNP_VERSION = "3.3"
MHP_VERSION = "0.1"