aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub
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-hub/src/meshbay_hub
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-hub/src/meshbay_hub')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/de.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/en.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/es.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/it.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js2
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/video-player.js37
11 files changed, 29 insertions, 28 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
index dd53bcf..9d9319d 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/de.js
@@ -254,6 +254,8 @@ export default {
'video.subtitles': 'Untertitel',
'video.subtitles_off': 'Aus',
'video.subtitle_track_n': 'Spur {n}',
+ 'video.subtitles_forced': 'erzwungen (nur Schrifttafeln)',
+ 'video.subtitles_sdh': 'für Hörgeschädigte',
'video.err_subtitle': 'Diese Untertitelspur konnte nicht geladen werden',
'video.close': 'Schließen (Esc)',
'preview.pdf_fallback': 'Dieser Browser zeigt das PDF nicht direkt an. Laden Sie es '
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
index e9f3d8d..a928a52 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/en.js
@@ -253,6 +253,8 @@ export default {
'video.subtitles': 'Subtitles',
'video.subtitles_off': 'Off',
'video.subtitle_track_n': 'Track {n}',
+ 'video.subtitles_forced': 'forced (signs only)',
+ 'video.subtitles_sdh': 'for the deaf and hard of hearing',
'video.err_subtitle': 'Could not load this subtitle track',
'video.close': 'Close (Esc)',
'preview.pdf_fallback': 'This browser will not display the PDF inline. Download it instead — it was decrypted here either way.',
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
index 68fbe10..2e6ea26 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/es.js
@@ -252,6 +252,8 @@ export default {
'video.subtitles': 'Subtítulos',
'video.subtitles_off': 'Ninguno',
'video.subtitle_track_n': 'Pista {n}',
+ 'video.subtitles_forced': 'forzados (solo carteles)',
+ 'video.subtitles_sdh': 'para sordos',
'video.err_subtitle': 'No se pudo cargar esta pista de subtítulos',
'video.close': 'Cerrar (Esc)',
'preview.pdf_fallback': 'Este navegador no mostrará el PDF integrado. Descárguelo '
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
index 679a458..5e6f258 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/fr.js
@@ -253,6 +253,8 @@ export default {
'video.subtitles': 'Sous-titres',
'video.subtitles_off': 'Aucun',
'video.subtitle_track_n': 'Piste {n}',
+ 'video.subtitles_forced': 'forcés (panneaux seulement)',
+ 'video.subtitles_sdh': 'sourds et malentendants',
'video.err_subtitle': 'Impossible de charger cette piste de sous-titres',
'video.close': 'Fermer (Échap)',
'preview.pdf_fallback': 'Ce navigateur n’affichera pas le PDF directement. '
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
index 9a34c93..ba594d2 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/it.js
@@ -253,6 +253,8 @@ export default {
'video.subtitles': 'Sottotitoli',
'video.subtitles_off': 'Nessuno',
'video.subtitle_track_n': 'Traccia {n}',
+ 'video.subtitles_forced': 'forzati (solo cartelli)',
+ 'video.subtitles_sdh': 'per non udenti',
'video.err_subtitle': 'Impossibile caricare questa traccia di sottotitoli',
'video.close': 'Chiudi (Esc)',
'preview.pdf_fallback': 'Questo browser non mostrerà il PDF nella pagina. Lo scarichi '
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
index 1d58e2c..9bbafb2 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/ja.js
@@ -250,6 +250,8 @@ export default {
'video.subtitles': '字幕',
'video.subtitles_off': 'なし',
'video.subtitle_track_n': 'トラック {n}',
+ 'video.subtitles_forced': '強制(表示物のみ)',
+ 'video.subtitles_sdh': '聴覚障害者向け',
'video.err_subtitle': 'この字幕トラックを読み込めませんでした',
'video.close': '閉じる(Esc)',
'preview.pdf_fallback': 'このブラウザーはページ内に PDF を表示しません。'
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
index 5bef61f..8f28d0c 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/nl.js
@@ -254,6 +254,8 @@ export default {
'video.subtitles': 'Ondertiteling',
'video.subtitles_off': 'Geen',
'video.subtitle_track_n': 'Spoor {n}',
+ 'video.subtitles_forced': 'geforceerd (alleen tekstborden)',
+ 'video.subtitles_sdh': 'voor doven en slechthorenden',
'video.err_subtitle': 'Dit ondertitelspoor kon niet worden geladen',
'video.close': 'Sluiten (Esc)',
'preview.pdf_fallback': 'Deze browser toont de PDF niet in de pagina zelf. Download '
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
index 8989880..fadf622 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pl.js
@@ -259,6 +259,8 @@ export default {
'video.subtitles': 'Napisy',
'video.subtitles_off': 'Brak',
'video.subtitle_track_n': 'Ścieżka {n}',
+ 'video.subtitles_forced': 'wymuszone (tylko napisy w tle)',
+ 'video.subtitles_sdh': 'dla niesłyszących',
'video.err_subtitle': 'Nie udało się wczytać tej ścieżki napisów',
'video.close': 'Zamknij (Esc)',
'preview.pdf_fallback': 'Ta przeglądarka nie wyświetli pliku PDF na stronie. Proszę '
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
index f1b8643..39da2b2 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/pt-BR.js
@@ -254,6 +254,8 @@ export default {
'video.subtitles': 'Legendas',
'video.subtitles_off': 'Nenhuma',
'video.subtitle_track_n': 'Faixa {n}',
+ 'video.subtitles_forced': 'forçadas (apenas letreiros)',
+ 'video.subtitles_sdh': 'para surdos',
'video.err_subtitle': 'Não foi possível carregar esta faixa de legendas',
'video.close': 'Fechar (Esc)',
'preview.pdf_fallback': 'Este navegador não exibirá o PDF na própria página. Baixe '
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
index 91da464..8639592 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/locales/zh-CN.js
@@ -248,6 +248,8 @@ export default {
'video.subtitles': '字幕',
'video.subtitles_off': '关闭',
'video.subtitle_track_n': '字幕 {n}',
+ 'video.subtitles_forced': '强制(仅字幕牌)',
+ 'video.subtitles_sdh': '听障人士专用',
'video.err_subtitle': '无法加载此字幕轨道',
'video.close': '关闭(Esc)',
'preview.pdf_fallback': '此浏览器不会在页面内显示该 PDF。请改为下载——无论如何它都已在本地解密。',
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 3bb9124..1522539 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/video-player.js
@@ -113,7 +113,15 @@ function audioTrackLabel(track) {
function subtitleTrackLabel(track) {
const name = _languageName(track.lang)
|| t('video.subtitle_track_n', { n: track.i + 1 });
- return track.title ? `${name} — ${track.title}` : name;
+ // The disposition wins over the container's title, and is translated, so a
+ // forced track reads as one in the viewer's own language rather than as the
+ // English word a muxer happened to type — or as nothing at all, which is
+ // what a forced track with no title tag looked like.
+ const kind = track.forced ? t('video.subtitles_forced')
+ : track.sdh ? t('video.subtitles_sdh')
+ : null;
+ const detail = kind || track.title;
+ return detail ? `${name} — ${detail}` : name;
}
function _mseSupported(codec) {
@@ -1159,33 +1167,6 @@ function VideoPlayer({ entry, transportRef, gekRef, onClose, onDownload }) {
'mode =', v.textTracks[0] && v.textTracks[0].mode,
'cues =', v.textTracks[0] && v.textTracks[0].cues
? v.textTracks[0].cues.length : 'none');
- if (!subtitleUrl) return;
- // The state above is a single instant, and every part of it can be right
- // there and wrong a second later: a re-render can replace the <track>,
- // which resets the mode nothing would set again. What decides whether a
- // subtitle is on screen is `activeCues`, and that is the one number no
- // log has ever carried. Sampled for ten seconds, with the cue that ought
- // to be showing at this instant, so "none active" and "active but not
- // painted" stop being the same observation.
- let n = 0;
- const probe = setInterval(() => {
- const tt = v.textTracks[0];
- if (!tt) { console.log('[MeshBay] subtitle probe: the track is gone'); return; }
- const cues = tt.cues ? Array.from(tt.cues) : [];
- const now = v.currentTime;
- const should = cues.find((c) => c.startTime <= now && c.endTime >= now);
- console.log('[MeshBay] subtitle probe', ++n,
- 't =', now.toFixed(1), 'mode =', tt.mode,
- 'cues =', cues.length,
- 'active =', tt.activeCues ? tt.activeCues.length : 'null',
- 'due here =', should ? `[${should.startTime.toFixed(1)}-`
- + `${should.endTime.toFixed(1)}] ${should.text.slice(0, 40)}` : 'none',
- 'first/last =', cues.length
- ? `${cues[0].startTime.toFixed(1)}..${cues[cues.length - 1].endTime.toFixed(1)}`
- : '-');
- if (n >= 5) clearInterval(probe);
- }, 2000);
- return () => clearInterval(probe);
}, [subtitleUrl]);
useEffect(() => {