aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-08-24 15:57:41 +0200
committerChristophe Besson <cbesson@gmail.com>2026-08-24 15:57:41 +0200
commitc5585beab3d6adefaa2ef9444946dd3816960a7c (patch)
treea321e540c2db0458716d526e4a045fca123937b2 /packages/meshbay-hub/src/meshbay_hub/static/group-settings.js
parent317f09328ed8bf20148b707470c9b0fe82e59575 (diff)
downloadmeshbay-c5585beab3d6adefaa2ef9444946dd3816960a7c.tar.gz
fix(node,hub): HEVC transcode fallback, live-add progress, per-group TMDB toggle
Three bugs found live testing the Videos app against a real HEVC/EAC3 show, plus a design change requested afterward: - Streaming always did "-c:v copy", which faithfully reports a source's real hev1 codec string but is unplayable in a browser with no HEVC decoder (most Chrome/Linux builds). The node now transcodes to H264 whenever the probed codec is browser-incompatible (media_probe.py's new BROWSER_INCOMPATIBLE_VIDEO_CODECS), with a `transcode_incompatible_video` node.toml opt-out for operators who know their viewers already decode it. - Dropping a whole season into an already-watched folder gave no scanning indicator and no progress bar: IndexProgress was only ever updated by the two bulk scan paths, never by the real-time per-file watchdog path (_schedule_update/_debounce/_update_entry). That path now accounts a "burst" the same way, without double-counting a file rewritten mid-debounce. - A stray literal "0" rendered in the video detail modal when there was no TMDB match (`meta.confidence` is 0, and `0 && x` renders "0" in JSX/htm, not nothing) — `confident` is now a real boolean. - Whether TMDB is used at all moves from a node-wide setting to per-group (OP_TMDB_ENABLED/tmdb_enabled/tmdb_enabled_ack, scoped like OP_VIDEO_ROOT): an operator running a real media-library group alongside test/demo groups on one node wants outbound TMDB traffic for the one that needs it, not all of them. The custom API token and query language stay node-wide, one shared credential/cache (tmdb_config/OP_TMDB_CONFIG, unchanged reasoning). MNP_VERSION 0.6 -> 0.7, additive. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LAmyXtc6dAADsH23ydXQpY
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/group-settings.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/group-settings.js71
1 files changed, 51 insertions, 20 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js
index 22532fa..88ab68e 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js
@@ -33,7 +33,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef,
memberUpload, onMemberUpload,
enabledApps, onEnabledApps,
scanSettings, onScanSettings,
- tmdbConfig, onTmdbConfig,
+ tmdbConfig, onTmdbConfig, onTmdbEnabled,
entries, nodeDirs, videoRoot, onVideoRoot,
onPaired, onLeft }) {
const [members, setMembers] = useState([]);
@@ -259,6 +259,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef,
const [tmdbBusy, setTmdbBusy] = useState(false);
const [tmdbMsg, setTmdbMsg] = useState('');
const [tmdbTokenDraft, setTmdbTokenDraft] = useState('');
+ const [tmdbEnabledBusy, setTmdbEnabledBusy] = useState(false);
const tmdbEnabled = tmdbConfig ? tmdbConfig.enabled : true;
// Pre-filled from the operator's own current UI language the first time
// this renders with nothing configured yet — a sensible default, not a
@@ -271,15 +272,44 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef,
}, [tmdbConfig && tmdbConfig.language]);
/**
- * TMDB on/off, an optional custom API token, and the language TMDB is
- * queried in — node-wide, not per-group (docs/mediacenter.md §5.5). Same
- * shape as saveScanSettings: signed, and the toggle does not claim
- * success until the node confirms it. The token field is cleared after a
- * save either way: it is never echoed back by the node (tmdb_config_ack
- * carries only whether one is set, never the value), so there is
- * nothing to keep showing.
+ * Whether TMDB is used at all — per-group (2026-08-24, used to be bundled
+ * into the same signed op as the token/language below): a real
+ * media-library group and a test/demo group on the same node need not
+ * share this decision. Saves immediately on toggle, same as an ordinary
+ * checkbox-style setting elsewhere — there is nothing else on the form to
+ * batch it with any more.
*/
- const saveTmdbConfig = useCallback(async (nextEnabled) => {
+ const saveTmdbEnabled = useCallback(async (nextEnabled) => {
+ const transport = transportRef && transportRef.current;
+ setTmdbMsg('');
+ setTmdbEnabledBusy(true);
+ try {
+ if (!transport || !transport.connected) {
+ throw new Error('Not connected to the node');
+ }
+ const sk = transport.sessionKeys && transport.sessionKeys.skEdB64;
+ const signFn = (sk && window.MeshBayKeys)
+ ? (transcript) => window.MeshBayKeys.signBytes(sk, transcript)
+ : null;
+ await transport.setTmdbEnabled(nextEnabled, signFn);
+ if (onTmdbEnabled) onTmdbEnabled(nextEnabled);
+ } catch (err) {
+ setTmdbMsg(err.message);
+ } finally {
+ setTmdbEnabledBusy(false);
+ }
+ }, [transportRef, onTmdbEnabled]);
+
+ /**
+ * An optional custom API token, and the language TMDB is queried in —
+ * node-wide, not per-group (docs/mediacenter.md §5.5): one shared
+ * credential and cache. Same shape as saveScanSettings: signed, and the
+ * button does not claim success until the node confirms it. The token
+ * field is cleared after a save either way: it is never echoed back by
+ * the node (tmdb_config_ack carries only whether one is set, never the
+ * value), so there is nothing to keep showing.
+ */
+ const saveTmdbConfig = useCallback(async () => {
const transport = transportRef && transportRef.current;
setTmdbMsg('');
setTmdbBusy(true);
@@ -292,11 +322,10 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef,
? (transcript) => window.MeshBayKeys.signBytes(sk, transcript)
: null;
const token = tmdbTokenDraft.trim();
- await transport.setTmdbConfig(nextEnabled, token || undefined, tmdbLanguage, signFn);
+ await transport.setTmdbConfig(token || undefined, tmdbLanguage, signFn);
setTmdbTokenDraft('');
if (onTmdbConfig) {
onTmdbConfig({
- enabled: nextEnabled,
tokenCustomized: token
? true
: (tmdbConfig ? tmdbConfig.tokenCustomized : false),
@@ -326,8 +355,8 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef,
if (!isNodeAdmin || !connected || !tmdbConfig || tmdbConfig.language) return;
if (autoLanguageSetRef.current) return;
autoLanguageSetRef.current = true;
- saveTmdbConfig(tmdbEnabled);
- }, [isNodeAdmin, connected, tmdbConfig, tmdbEnabled, saveTmdbConfig]);
+ saveTmdbConfig();
+ }, [isNodeAdmin, connected, tmdbConfig, saveTmdbConfig]);
// Every folder anywhere in the group's shared index, deepest included —
// `entries[].path` is each file's containing directory (files-app.js's own
@@ -607,18 +636,20 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef,
</div>
`}
- ${/* TMDB on/off + custom token, node-wide (docs/mediacenter.md §5.5) —
- new outbound third-party traffic the node did not have before
- the Videos app, so it is a signed operator setting like the
- rest, not a display preference. */
+ ${/* The on/off switch is per-group (2026-08-24); the custom token and
+ query language stay node-wide, one shared credential/cache
+ (docs/mediacenter.md §5.5). Both are new outbound third-party
+ traffic the node did not have before the Videos app, so both are
+ signed operator settings, not display preferences — but two
+ independent ones now, saved separately. */
isNodeAdmin && connected && html`
<div class="settings-section">
<h3 class="settings-heading">${t('settings_node.tmdb_title')}</h3>
<p class="settings-hint">${t('settings_node.tmdb_hint')}</p>
<div class="settings-row">
<label class="settings-label">
- <input type="checkbox" checked=${tmdbEnabled} disabled=${tmdbBusy}
- onChange=${(e) => saveTmdbConfig(e.target.checked)} />
+ <input type="checkbox" checked=${tmdbEnabled} disabled=${tmdbEnabledBusy}
+ onChange=${(e) => saveTmdbEnabled(e.target.checked)} />
${' '}${tmdbEnabled ? t('settings_node.tmdb_enabled') : t('settings_node.tmdb_disabled')}
</label>
</div>
@@ -650,7 +681,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef,
<p class="settings-hint">${t('settings_node.tmdb_language_hint')}</p>
</div>
<button class="btn btn-small btn-secondary" style="margin-top:8px"
- disabled=${tmdbBusy} onClick=${() => saveTmdbConfig(tmdbEnabled)}>
+ disabled=${tmdbBusy} onClick=${() => saveTmdbConfig()}>
${tmdbBusy ? t('settings_node.scan_saving') : t('settings_node.tmdb_save')}
</button>
${tmdbMsg && html`<p class="settings-hint">${tmdbMsg}</p>`}