summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-17 15:06:57 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-17 15:06:57 +0200
commitd472725167c1d335996059d24b7b74f3730768fb (patch)
tree1ffca323c9dfe682c8fff2a20da1cfae0106f830 /packages/meshbay-node/src
parent30980ba0994d3e99d41c5252555320d6cbb1f5a9 (diff)
downloadmeshbay-d472725167c1d335996059d24b7b74f3730768fb.tar.gz
feat: tell a forced subtitle track from a full one
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UGY17EPph5LsLzePPXhUVc
Diffstat (limited to 'packages/meshbay-node/src')
-rw-r--r--packages/meshbay-node/src/meshbay_node/media_probe.py14
-rw-r--r--packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py6
2 files changed, 20 insertions, 0 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/media_probe.py b/packages/meshbay-node/src/meshbay_node/media_probe.py
index 06b1e28..81174c8 100644
--- a/packages/meshbay-node/src/meshbay_node/media_probe.py
+++ b/packages/meshbay-node/src/meshbay_node/media_probe.py
@@ -69,6 +69,16 @@ class SubtitleTrack:
language: str | None
title: str | None
codec_name: str | None
+ # **A forced track is not a shorter version of the full one.** It carries
+ # only signage and the lines spoken in another language — measured on a
+ # real film: 30 cues and 77 seconds of text across 2h32, against 1559 cues
+ # and 41% of the running time for the full track beside it. Picking it and
+ # seeing nothing for ten minutes is the ordinary outcome, not a fault, and
+ # nothing in the menu let a viewer tell those two apart. The disposition is
+ # what says which it is; the title tag that would also say it ("Forced",
+ # "SDH") is absent as often as it is present.
+ forced: bool = False
+ hearing_impaired: bool = False
@dataclass
@@ -143,6 +153,7 @@ async def probe_video(path: str) -> VideoProbe:
"-show_entries",
"stream=codec_name,profile,level,codec_type,width,height,channels",
"-show_entries", "stream_tags=language,title",
+ "-show_entries", "stream_disposition=forced,hearing_impaired",
"-show_entries", "format=duration",
"-of", "json", path,
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
@@ -192,11 +203,14 @@ async def probe_video(path: str) -> VideoProbe:
if codec_name not in TEXT_SUBTITLE_CODECS:
continue
tags = s.get("tags") or {}
+ disp = s.get("disposition") or {}
subtitle_tracks.append(SubtitleTrack(
ordinal=ordinal,
language=(tags.get("language") or "").strip() or None,
title=(tags.get("title") or "").strip() or None,
codec_name=codec_name,
+ forced=bool(disp.get("forced")),
+ hearing_impaired=bool(disp.get("hearing_impaired")),
))
has_audio = bool(audio_tracks)
diff --git a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
index 1e85f29..2863679 100644
--- a/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
+++ b/packages/meshbay-node/src/meshbay_node/transport/webrtc_server.py
@@ -6267,6 +6267,12 @@ class WebRTCPeerSession:
"lang": tr.language,
"title": tr.title,
"codec": tr.codec_name,
+ # What tells a full translation from signage-only. Without
+ # it the two are the same menu entry, and picking the
+ # forced one shows nothing for minutes at a time — which
+ # reads as a broken feature and was reported as one.
+ "forced": tr.forced,
+ "sdh": tr.hearing_impaired,
}
for tr in probe.subtitle_tracks
],