From eba2e6b14484c124f3c87ff95cd7ee640833e5d3 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Fri, 28 Aug 2026 15:35:20 +0200 Subject: feat(hub): cross-group search with reuse of existing views MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Search page fetches indexes from all groups, then renders consolidated entries through the existing FilesPanel, VideoApp, and MusicApp components — no reimplemented views. Groups appear as top-level directories in the file browser; video/music entries use a synthetic root with per-entry transport refs for thumbnails and metadata across groups. Music player lifted to app.js with getConnection(groupId) for cross-group playback. Connection pool (max 3, LRU eviction) manages lazy WebRTC connections. All 10 locales updated with search keys. Co-Authored-By: Claude Opus 4.6 --- .../src/meshbay_hub/static/video-app.js | 46 +++++++++++++++++----- 1 file changed, 36 insertions(+), 10 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/video-app.js') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/video-app.js b/packages/meshbay-hub/src/meshbay_hub/static/video-app.js index dd120da..df930fd 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/video-app.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/video-app.js @@ -147,6 +147,13 @@ function MediaThumb({ thumbHash, transportRef, gekRef, alt, cls = 'video-thumb', onReady, emptyIcon = 'video', }) { const [blobUrl, setBlobUrl] = useState(() => _thumbBlobCache.get(thumbHash) || null); + const [retryToken, setRetryToken] = useState(0); + + useEffect(() => { + const listener = () => setRetryToken((n) => n + 1); + _thumbRetryListeners.add(listener); + return () => _thumbRetryListeners.delete(listener); + }, []); // Re-checks the cache by the CURRENT thumbHash on every change rather than // trusting the `blobUrl` state variable — a PosterCard swaps this same @@ -181,7 +188,7 @@ function MediaThumb({ } })(); return () => { cancelled = true; }; - }, [thumbHash]); + }, [thumbHash, retryToken]); if (!blobUrl) return html`
<${Icon} name=${emptyIcon} />
`; return html`${alt`; @@ -201,6 +208,11 @@ function bumpMediaMetaGeneration() { for (const fn of _mediaMetaListeners) fn(); } +const _thumbRetryListeners = new Set(); +function bumpThumbGeneration() { + for (const fn of _thumbRetryListeners) fn(); +} + function useMediaMeta(transportRef, fileId, active) { const [meta, setMeta] = useState(null); const [refetchToken, setRefetchToken] = useState(0); @@ -266,7 +278,9 @@ function useSeasonMeta(transportRef, tmdbId, season, active) { // ── Mode A: poster grid ────────────────────────────────────────────────────── function PosterCard({ title, subtitle, repEntry, transportRef, gekRef, onOpen, groupKey, onMetaResolved }) { - const meta = useMediaMeta(transportRef, repEntry.id, true); + const tRef = repEntry._tRef || transportRef; + const gRef = repEntry._gRef || gekRef; + const meta = useMediaMeta(tRef, repEntry.id, true); const confident = Boolean(meta && meta.confidence && meta.tmdb_id); const metaReady = meta !== null; @@ -318,7 +332,7 @@ function PosterCard({ title, subtitle, repEntry, transportRef, gekRef, onOpen, g
${metaReady && html` <${MediaThumb} thumbHash=${posterHash} alt=${title} - cls="video-poster" transportRef=${transportRef} gekRef=${gekRef} + cls="video-poster" transportRef=${tRef} gekRef=${gRef} onReady=${handleImageReady} /> `}
@@ -330,6 +344,9 @@ function PosterCard({ title, subtitle, repEntry, transportRef, gekRef, onOpen, g ${confident && meta.first_air_date ? yearOf(meta.first_air_date) : ''} ${subtitle ? ` · ${subtitle}` : ''} + ${repEntry.groupName && html` +
${repEntry.groupName}
+ `} `} @@ -534,7 +551,7 @@ function VideoDetailModal({ - `} ${filteredMovies.length === 0 && filteredShows.length === 0 && html`

${needle ? t('group.empty_filter') : t('video.empty')}

@@ -836,4 +862,4 @@ function VideoApp({ // blob" and "mount only once actually scrolled near" mechanisms apply to a // track's cover art unchanged, so Music imports them here rather than // re-implementing (apps.md §4's checklist). -export { VideoApp, MediaThumb, LazyTile }; +export { VideoApp, MediaThumb, LazyTile, groupVideoEntries, bumpMediaMetaGeneration, bumpThumbGeneration }; -- cgit v1.2.3