<feed xmlns='http://www.w3.org/2005/Atom'>
<title>meshbay.git/packages/meshbay-hub/tests/test_video_subtitles.py, branch 0.15</title>
<subtitle>MeshBay — read-only public mirror</subtitle>
<id>https://git.meshbay.org/meshbay.git/atom?h=0.15</id>
<link rel='self' href='https://git.meshbay.org/meshbay.git/atom?h=0.15'/>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/'/>
<updated>2026-09-18T07:49:27Z</updated>
<entry>
<title>feat(cast): carry subtitles to a Chromecast, on the relay's clock</title>
<updated>2026-09-18T07:49:27Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-18T07:47:01Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=c03512aeab576a06f8d5026e5eb484897ec45f99'/>
<id>urn:sha1:c03512aeab576a06f8d5026e5eb484897ec45f99</id>
<content type='text'>
The relay forwards the node's fragments untouched, and those begin at zero
at the seek point. The player never notices because its SourceBuffer is given
`timestampOffset = start`; a receiver has no equivalent, so the cues are
shifted by `-start` before they leave, recomputed at every restart of the
relay. Sent as they are, a subtitle would be out by the whole seek.

The document is served from the relay's own port at /subs.vtt, behind the same
token as the stream and with CORS: a receiver fetches a side-loaded track with
XHR from its own origin, and without the headers it fails as a network error
with nothing on screen to say so. The URL carries a version because a track is
cached by address — changing the cues behind a fixed URL leaves the previous
language showing.

Cues that end before the stream begins are dropped rather than clamped, so a
line from before the seek cannot appear over the first frames after it.

The relay is plain Node, so the tests start it and fetch from it rather than
reading its source.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01UGY17EPph5LsLzePPXhUVc
</content>
</entry>
<entry>
<title>feat: tell a forced subtitle track from a full one</title>
<updated>2026-09-17T13:06:57Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-17T13:06:57Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=d472725167c1d335996059d24b7b74f3730768fb'/>
<id>urn:sha1:d472725167c1d335996059d24b7b74f3730768fb</id>
<content type='text'>
Reported as "I click a subtitle and nothing appears", on three films. Nothing
was broken. The track selected was the container's forced track, which carries
signage and foreign dialogue only: measured on the film in question, 30 cues
and 77 seconds of text across 2h32 — 0.8% of the running time, against 1559
cues and 41.8% for the full track sitting beside it under the same language
tag. At all three positions tested there was genuinely no cue to show; the
full track would have shown one at two of them.

So the defect is that the menu could not say which was which. The label used
the container's title tag, which said "Forced" on that film and says nothing
at all on most, and no other field was carried. The disposition is the half
that is always there: `probe_video` now reads `forced` and `hearing_impaired`,
`stream_init` carries them, and the label states them in the reader's own
language rather than repeating an English word a muxer happened to type.

The node fixture grows a forced track with no title, because a title would
let the old code pass. The label harness's `t` stub took a parameters object
unconditionally and threw on a key that has none — a fixture narrower than
production, fixed here rather than worked around.

Also removes the activeCues probe that found this. It answered its question:
mode showing, cues 30, active 0, none due at that instant.

Co-Authored-By: Claude Opus 5 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01UGY17EPph5LsLzePPXhUVc
</content>
</entry>
<entry>
<title>feat: embedded subtitles in the video player (MNP 3.3)</title>
<updated>2026-09-17T11:39:23Z</updated>
<author>
<name>Christophe Besson</name>
<email>cbesson@gmail.com</email>
</author>
<published>2026-09-17T11:39:23Z</published>
<link rel='alternate' type='text/html' href='https://git.meshbay.org/meshbay.git/commit/?id=ad4ca3229002997934ccb5b2eaeb553c13b8888f'/>
<id>urn:sha1:ad4ca3229002997934ccb5b2eaeb553c13b8888f</id>
<content type='text'>
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 &lt;track&gt; 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 &lt;noreply@anthropic.com&gt;
Claude-Session: https://claude.ai/code/session_01UGY17EPph5LsLzePPXhUVc
</content>
</entry>
</feed>
