From 0b0da86f1f9d6f0b1a27b5e1e1658c42de9f356a Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Mon, 24 Aug 2026 14:33:20 +0200 Subject: feat(node,hub): season-specific overviews, manual TMDB match correction, and wizard polish MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two operator-facing fixes for a real 3-season show whose automatic TMDB match was wrong at the show level: per-season overview/air_date tabs in the detail modal (falling back to the show-level text when a season's own is empty), and a "Fix match…" search-and-correct affordance that re-resolves every file sharing the corrected show's display_title. New signed op OP_TMDB_OVERRIDE and two read-only pairs (season_meta_req/resp, tmdb_search_req/resp), MNP_VERSION 0.5 -> 0.6. Also: the create-group wizard gets a spinning indexing indicator and an app-selection step, group settings default the TMDB language to the operator's own locale (never as a global default), and a file renamed mid-session now re-triggers title parsing instead of being silently skipped by the enrichment dedup guard. Fixes two bugs found during this work: the search overlay's z-index lost to the base video-overlay class and rendered invisibly, and season_meta's own empty overview didn't fall back to the show-level one. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01LAmyXtc6dAADsH23ydXQpY --- packages/meshbay-hub/src/meshbay_hub/static/app.js | 66 ++++++++++++++++++---- 1 file changed, 56 insertions(+), 10 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/app.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js index 58ffe8e..f9bcd43 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js @@ -839,6 +839,15 @@ function CreateGroupWizard({ token, username, onCreated }) { const [joinPolicy, setJoinPolicy] = useState('invite'); const [roots, setRoots] = useState([]); const [uploadIdx, setUploadIdx] = useState(0); + // Every registered app, on by default — narrowing this down here means + // members never briefly see one the operator meant to leave off, the way + // toggling it afterward from Settings would. + const [enabledApps, setEnabledApps] = useState(() => APPS.map(a => a.key)); + const toggleWizardApp = useCallback((key) => { + setEnabledApps(prev => prev.includes(key) + ? prev.filter(k => k !== key) + : [...prev, key]); + }, []); // Step 2 progress const [setupSteps, setSetupSteps] = useState([]); @@ -915,8 +924,13 @@ function CreateGroupWizard({ token, username, onCreated }) { const steps = [ { label: t('wizard.step_create_hub'), status: 'pending' }, { label: t('wizard.step_attach'), status: 'pending' }, - { label: t('wizard.step_index'), status: 'pending' }, ]; + // Only a step at all when it does something — the common case (every + // app left on, the default) has nothing to set and no reason to show a + // step for it. + if (enabledApps.length < APPS.length) + steps.push({ label: t('wizard.step_apps'), status: 'pending' }); + steps.push({ label: t('wizard.step_index'), status: 'pending' }); if (roots.length > 1) steps.push({ label: t('wizard.step_add_roots'), status: 'pending' }); steps.push({ label: t('wizard.step_gek'), status: 'pending' }); @@ -980,11 +994,24 @@ function CreateGroupWizard({ token, username, onCreated }) { update('done'); advance(); - // 3. Wait for the node's own initial scan of this group to finish — + // 3. Narrow the enabled apps down, if the operator unchecked any — + // before the scan below, so a member who joins while it is still + // running never briefly sees an app meant to be off. Same + // not-hosted-yet race as steps 4+ below: the group is attached, but + // may not have reached groups_ctx yet. + if (enabledApps.length < APPS.length) { + update('running'); + await withRetry(() => platform.node.call( + 'PUT', `/api/groups/${gid}/apps`, { apps: enabledApps })); + update('done'); + advance(); + } + + // 4. Wait for the node's own initial scan of this group to finish — // the group is not usable for anything below (extra roots, GEK) until // this finishes, so nobody lands on a page that looks broken, or hits // a "not configured" error from racing ahead of it. Can take tens of - // minutes on a slow disk (see the StarWars benchmark) — the node + // minutes on a slow disk with a large library — the node // keeps scanning on its own either way (test_hot_reload_survives_ // client_close.py); this step is only about not lying about it. update('running'); @@ -992,7 +1019,7 @@ function CreateGroupWizard({ token, username, onCreated }) { update('done'); advance(); - // 4. Add extra roots (if >1) + // 5. Add extra roots (if >1) if (roots.length > 1) { update('running'); for (let i = 0; i < roots.length; i++) { @@ -1007,13 +1034,13 @@ function CreateGroupWizard({ token, username, onCreated }) { advance(); } - // 5. GEK init + // 6. GEK init update('running'); await withRetry(() => platform.node.call('POST', `/api/groups/${gid}/gek`)); update('done'); advance(); - // 6. Generate pairing code + // 7. Generate pairing code update('running'); const pairResult = await platform.node.call('POST', '/api/operator/pair'); if (pairResult && pairResult.code) { @@ -1031,7 +1058,7 @@ function CreateGroupWizard({ token, username, onCreated }) { update('error'); setSetupError(platform.bridgeMessage(err)); } - }, [name, description, joinPolicy, roots, uploadIdx, token, onCreated]); + }, [name, description, joinPolicy, roots, uploadIdx, enabledApps, token, onCreated]); // Step 0: Node detection if (step === 0) { @@ -1065,7 +1092,7 @@ function CreateGroupWizard({ token, username, onCreated }) { // Step 1: Group details + directories if (step === 1) { - const canProceed = name.trim() && roots.length > 0; + const canProceed = name.trim() && roots.length > 0 && enabledApps.length > 0; return html`

${t('wizard.title')}

${error && html`
${error}
`} @@ -1111,6 +1138,25 @@ function CreateGroupWizard({ token, username, onCreated }) {
+
+

${t('members.apps_title')}

+

+ ${t('members.apps_hint')}

+
    + ${APPS.map(a => html` +
  • + +
  • + `)} +
+ ${enabledApps.length === 0 && html` +

${t('members.apps_need_one')}

`} +
+

${t('wizard.directories')}

@@ -1161,8 +1207,8 @@ function CreateGroupWizard({ token, username, onCreated }) { ${setupSteps.map((s, i) => html`

- ${s.status === 'done' ? '✓' : - s.status === 'running' ? '●' : + ${s.status === 'running' ? html`` : + s.status === 'done' ? '✓' : s.status === 'error' ? '✗' : '○'} ${s.label} -- cgit v1.2.3