diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-24 15:57:41 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-24 15:57:41 +0200 |
| commit | c5585beab3d6adefaa2ef9444946dd3816960a7c (patch) | |
| tree | a321e540c2db0458716d526e4a045fca123937b2 /packages/meshbay-hub/src/meshbay_hub/static/group-settings.js | |
| parent | 317f09328ed8bf20148b707470c9b0fe82e59575 (diff) | |
| download | meshbay-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.js | 71 |
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>`} |