aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/search-page.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/search-page.js61
1 files changed, 41 insertions, 20 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
index 6e4e5b9..4ed3df1 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/search-page.js
@@ -147,10 +147,12 @@ async function fetchGroupIndex(groupId, token, bundleKey, username, userId) {
clearTimeout(timer);
const indexMsg = await transport.fetchIndex();
+ // Plural, with the old scalars as the fallback for a node still speaking
+ // MNP 1.0 — the same reading group-page.js does on its own handshake.
const roots = {
- videoRoot: ack.video_root || '',
- audioRoot: ack.audio_root || '',
- photoRoots: ack.photo_roots || [],
+ video: ack.video_directories || (ack.video_root ? [ack.video_root] : []),
+ music: ack.music_directories || (ack.audio_root ? [ack.audio_root] : []),
+ photo: ack.photo_directories || ack.photo_roots || [],
};
// Which of the reader's groups sit on their own node — the tie-breaker
// when the same file is announced by several of them
@@ -201,10 +203,29 @@ async function fetchAllIndexes(groups, token, username, userId, onProgress, onBa
// -- SearchPage ---------------------------------------------------------------
-function underRoot(entry, root) {
- if (!root) return false;
+function underRoot(entry, directories) {
+ const dirs = directories || [];
+ if (!dirs.length) return false;
const p = entry.path || '';
- return p === root || p.startsWith(root + '/');
+ return dirs.some((d) => p === d || p.startsWith(d + '/'));
+}
+
+/**
+ * An app's directories out of a cached group, in either shape.
+ *
+ * The cache lives in IndexedDB and outlives a deploy, so a reader opening
+ * Search after this ships still has entries written by the previous version —
+ * `{videoRoot: 'X'}` where this now writes `{video: ['X']}`. Reading only the
+ * new shape would empty their Videos results with no explanation and no way
+ * to tell it from "nothing matched".
+ */
+function cachedDirs(roots, appKey, legacyKey) {
+ if (!roots) return [];
+ const fresh = roots[appKey];
+ if (Array.isArray(fresh)) return fresh;
+ const legacy = roots[legacyKey];
+ if (Array.isArray(legacy)) return legacy;
+ return legacy ? [legacy] : [];
}
// -- Merging the same file announced by several groups ------------------------
@@ -226,7 +247,7 @@ function underRoot(entry, root) {
// `mergeUnitEntries` folds lists that share a key, so the two copies become
// one unit without this having to group them first.
function videoUnits(entries) {
- const { movies, shows } = groupVideoEntries(entries, SEARCH_VIDEO_ROOT);
+ const { movies, shows } = groupVideoEntries(entries, [SEARCH_VIDEO_ROOT]);
return [
...movies.map((e) => ({ key: `movie:${e.id}`, entries: [e] })),
...shows.map((s) => ({ key: `show:${s.title}`, entries: s.episodes })),
@@ -238,7 +259,7 @@ function videoUnits(entries) {
// object, so the key only has to name it stably — hence `foldKey` over the
// display strings, which are whichever spelling arrived first.
function musicUnits(entries) {
- const { tracks, albums } = groupMusicEntries(entries, SEARCH_AUDIO_ROOT);
+ const { tracks, albums } = groupMusicEntries(entries, [SEARCH_AUDIO_ROOT]);
return [
...albums.map((a) => ({
key: `album:${foldKey(a.artist)}/${foldKey(a.album)}`,
@@ -497,12 +518,12 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
const videoEntries = useMemo(() => {
const result = [];
for (const [groupId, data] of indexedGroups) {
- const root = data.roots.videoRoot;
- if (!root) continue;
+ const dirs = cachedDirs(data.roots, 'video', 'videoRoot');
+ if (!dirs.length) continue;
const conn = groupConns.current.get(groupId);
for (const e of data.entries) {
if (e.type !== 'video') continue;
- if (!underRoot(e, root)) continue;
+ if (!underRoot(e, dirs)) continue;
if (q && !matchesQuery(e)) continue;
result.push({
...e,
@@ -523,12 +544,12 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
const musicEntries = useMemo(() => {
const result = [];
for (const [groupId, data] of indexedGroups) {
- const root = data.roots.audioRoot;
- if (!root) continue;
+ const dirs = cachedDirs(data.roots, 'music', 'audioRoot');
+ if (!dirs.length) continue;
const conn = groupConns.current.get(groupId);
for (const e of data.entries) {
if (e.type !== 'audio') continue;
- if (!underRoot(e, root)) continue;
+ if (!underRoot(e, dirs)) continue;
if (q && !matchesQuery(e)) continue;
result.push({
...e,
@@ -549,13 +570,13 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
const photoEntries = useMemo(() => {
const result = [];
for (const [groupId, data] of indexedGroups) {
- const roots = data.roots.photoRoots;
- if (!roots || !roots.length) continue;
+ const dirs = cachedDirs(data.roots, 'photo', 'photoRoots');
+ if (!dirs.length) continue;
const conn = groupConns.current.get(groupId);
for (const e of data.entries) {
if (e.type !== 'image') continue;
const p = e.path || '';
- if (!roots.some((r) => p === r || p.startsWith(r + '/'))) continue;
+ if (!dirs.some((d) => p === d || p.startsWith(d + '/'))) continue;
if (q && !matchesQuery(e)) continue;
result.push({
...e,
@@ -725,7 +746,7 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
status="connected"
entries=${videoEntries}
onPreview=${onPreview}
- videoRoot=${SEARCH_VIDEO_ROOT}
+ videoDirectories=${[SEARCH_VIDEO_ROOT]}
tmdbConfig=${{ enabled: true }}
isNodeAdmin=${false}
onNeedConn=${connectGroup}
@@ -739,7 +760,7 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
gekRef=${defaultGRef}
status="connected"
entries=${musicEntries}
- audioRoot=${SEARCH_AUDIO_ROOT}
+ musicDirectories=${[SEARCH_AUDIO_ROOT]}
musicbrainzConfig=${{ enabled: true }}
onPlayQueue=${handleMusicPlay}
hideFilter=${true} />
@@ -752,7 +773,7 @@ function SearchPage({ token, username, userId, groups, onPlayQueue, userPrefs })
gekRef=${defaultGRef}
status="connected"
entries=${photoEntries}
- photoRoots=${SEARCH_PHOTO_ROOTS}
+ photoDirectories=${SEARCH_PHOTO_ROOTS}
setError=${noop}
hideFilter=${true}
readOnly=${true} />