fix(library): optimize image scanning, fast raw preview extraction and lazy thumbnail loading

This commit is contained in:
2026-10-05 06:34:37 +07:00
parent 44468ac178
commit 77cf872b16
13 changed files with 625 additions and 160 deletions
+7 -15
View File
@@ -23,6 +23,7 @@
import type { LibraryEdit, LibraryFolder } from './library';
import {
ensurePermission,
getPhotoThumb,
listEdits,
listFolders,
listPhotos,
@@ -125,6 +126,7 @@ async function folderHandle(): Promise<FileSystemDirectoryHandle | null> {
// without a click behind it is a prompt the browser may refuse outright, and the
// buttons on the screen are where the click is.
async function granted(handle: FileSystemHandle, write: boolean): Promise<boolean> {
if (!handle) return false;
const askable = handle as FileSystemHandle & {
queryPermission?: (o: { mode: string }) => Promise<PermissionState>;
};
@@ -314,32 +316,22 @@ export async function backupNow(onProgress?: (p: BackupProgress) => void): Promi
let done = 0;
const rows: { id: string; size: number }[] = [];
for (const photo of photos) {
// Progress counts frames and not tiles: a folder of frames read without one
// is still a reading, and a bar that does not move is a bar that reads as a
// screen that has stopped.
if (++done % 64 === 0) onProgress?.({ done, total: photos.length });
if (!photo.thumb) continue;
if (had.get(photo.id) === photo.thumb.size) continue;
const blob = photo.thumb ?? (await getPhotoThumb(photo.id));
if (!blob) continue;
if (had.get(photo.id) === blob.size) continue;
const parts = photo.id.split('/');
parts.pop();
const rel = [THUMBS, ...parts, photo.name].join('/');
try {
await put(root, rel, photo.thumb);
await put(root, rel, blob);
} catch (err) {
// A frame the browser will not write is not a library that is never
// written: the run goes on without it, and the name it stopped on goes to
// the console, since the line on the screen is one line over a strip of
// photographs. Whatever survives `spell` is a name longer than a folder
// will hold, and a name spelled back is a name the restore still finds.
if (skipped < 5) console.warn('backup: could not write', rel, err);
skipped++;
continue;
}
rows.push({ id: photo.id, size: photo.thumb.size });
rows.push({ id: photo.id, size: blob.size });
written++;
// The list is kept in step with the writes: a run that is interrupted
// continues from the frame it stopped at instead of writing the folder
// again.
if (rows.length >= 200) await keep(WRITTEN, rows.splice(0, rows.length));
}
await keep(WRITTEN, rows);