From db7ce046134c2895ef65ee985b0bca549ba69c3b Mon Sep 17 00:00:00 2001 From: 3dtours Date: Thu, 8 Oct 2026 20:50:18 +0700 Subject: [PATCH] fix(library): sync React photos state after scan and support case-insensitive existing photo lookup --- docker/frontend/src/Library.tsx | 13 +++++++------ docker/frontend/src/engine/library.ts | 22 ++++++++++++++++++---- docker/recipescam-images.tar.gz.sha256 | 2 +- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/docker/frontend/src/Library.tsx b/docker/frontend/src/Library.tsx index 0c42424..6145fbd 100644 --- a/docker/frontend/src/Library.tsx +++ b/docker/frontend/src/Library.tsx @@ -796,15 +796,12 @@ function Catalogue() { // roll, and the rows outside the folder being re-read are rows the // reader is looking at. if (!from) setDirs(result.dirs); - // No read back here. The reading handed its frames over as it stored - // them, and the screen watching it reads the catalogue back once the - // reading is done — the same read, half a beat later, and not a second - // one of the whole catalogue for a strip that is already drawn. + await reload(); } catch { setNote(t('lib.failed')); } }, - [t] + [reload, t] ); // The folder this screen is on is read back to the disk on the way in, and again @@ -1003,6 +1000,7 @@ function Catalogue() { // numbers drawn over the column would count a roll down to a branch of itself. // The column falls back on the catalogue until the reading is through. const read = live ? live.progress.counts : null; + const liveDirs = live ? live.progress.dirs : null; // Memoize photo count map derived from photos and dirs array separately so that // live scan progress ticks (every 200ms) do not re-scan tens of thousands of photos. const baseTreeInfo = useMemo(() => { @@ -1032,8 +1030,11 @@ function Catalogue() { if (normDir) add(normF, normDir, true); } for (const dir of dirs) add(dir.folder, dir.rel, false); + if (liveDirs) { + for (const dir of liveDirs) add(dir.folder, dir.rel, false); + } return { hits, foldersUnder }; - }, [photos, dirs]); + }, [photos, dirs, liveDirs]); const nodes = useMemo(() => { const { hits, foldersUnder } = baseTreeInfo; diff --git a/docker/frontend/src/engine/library.ts b/docker/frontend/src/engine/library.ts index 8c36cc2..2fc9ea0 100644 --- a/docker/frontend/src/engine/library.ts +++ b/docker/frontend/src/engine/library.ts @@ -724,7 +724,12 @@ export async function scanFolder( jump?: () => string | null, from = '' ): Promise { - const known = new Map((await listPhotos(folder.name)).map((p) => [p.id, p])); + const rawKnown = await listPhotos(folder.name); + const known = new Map(); + for (const p of rawKnown) { + known.set(p.id, p); + known.set(p.id.toLowerCase(), p); + } // A reading kept to one folder keeps no position of its own. The position file // belongs to the reading that walks the whole roll, and a fraction of the tree // written into it would hand the next visit a roll with the rest of itself @@ -808,9 +813,17 @@ export async function scanFolder( counts[key.toLowerCase()] = counts[key]; } }; - for (const id of known.keys()) count(id.startsWith(`${normRoot}/`) ? id.slice(normRoot.length + 1) : id); + const relOfId = (id: string) => { + const normId = normPath(id); + const prefix = `${normRoot}/`.toLowerCase(); + if (normId.toLowerCase().startsWith(prefix)) { + return normId.slice(normRoot.length + 1); + } + return normId; + }; + for (const id of known.keys()) count(relOfId(id)); const knownPaths = new Set( - [...known.keys()].map((id) => (id.startsWith(`${normRoot}/`) ? id.slice(normRoot.length + 1) : id)) + [...known.keys()].map((id) => relOfId(id)) ); const countedDiscovered = new Set(); const countDiscovered = (rel: string) => { @@ -825,8 +838,9 @@ export async function scanFolder( // been read once before is a reading of a hundred thousand frames even while // the walk is still up in its first folders, and one kept to a subfolder is // measured against the frames under that one alone. + const heldPrefix = `${folder.name}/${from}`.toLowerCase(); const held = bounded - ? [...known.keys()].filter((id) => id.startsWith(`${folder.name}/${from}`)).length + ? [...known.keys()].filter((id) => id.toLowerCase().startsWith(heldPrefix)).length : known.size; // What the screen is told, and how often. The column is redrawn out of the // count and the count moves with every frame, so telling the screen a frame at diff --git a/docker/recipescam-images.tar.gz.sha256 b/docker/recipescam-images.tar.gz.sha256 index 6d19873..95d5e7f 100644 --- a/docker/recipescam-images.tar.gz.sha256 +++ b/docker/recipescam-images.tar.gz.sha256 @@ -1 +1 @@ -3c54c3f27787b7c1a660483aaed2a310eef61b466e8251767b4ef2d80b3d6505 recipescam-images.tar.gz +c0e3966c68c7391acb27769e6eac806e7123249e0459533e193c1d4be98efe43 recipescam-images.tar.gz