mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-21 06:11:45 +00:00
fix(photos): paginated search with infinite scroll (#613)
Replace bulk-loading all Immich photos (up to 20k) with paginated search: 50 photos per page, automatic infinite scroll via IntersectionObserver. Prevents server blocking on large libraries. - Backend: searchPhotos accepts page/size params, returns hasMore - Frontend: loads 50 at a time, appends on scroll - AbortController cancels in-flight requests on tab switch
This commit is contained in:
@@ -60,10 +60,10 @@ router.get('/browse', authenticate, async (req: Request, res: Response) => {
|
||||
|
||||
router.post('/search', authenticate, async (req: Request, res: Response) => {
|
||||
const authReq = req as AuthRequest;
|
||||
const { from, to } = req.body;
|
||||
const result = await searchPhotos(authReq.user.id, from, to);
|
||||
const { from, to, page, size } = req.body;
|
||||
const result = await searchPhotos(authReq.user.id, from, to, Number(page) || 1, Math.min(Number(size) || 50, 200));
|
||||
if (result.error) return res.status(result.status!).json({ error: result.error });
|
||||
res.json({ assets: result.assets });
|
||||
res.json({ assets: result.assets, hasMore: result.hasMore });
|
||||
});
|
||||
|
||||
// ── Asset Details ──────────────────────────────────────────────────────────
|
||||
|
||||
@@ -149,44 +149,36 @@ export async function browseTimeline(
|
||||
export async function searchPhotos(
|
||||
userId: number,
|
||||
from?: string,
|
||||
to?: string
|
||||
): Promise<{ assets?: any[]; error?: string; status?: number }> {
|
||||
to?: string,
|
||||
page: number = 1,
|
||||
size: number = 50,
|
||||
): Promise<{ assets?: any[]; hasMore?: boolean; error?: string; status?: number }> {
|
||||
const creds = getImmichCredentials(userId);
|
||||
if (!creds) return { error: 'Immich not configured', status: 400 };
|
||||
|
||||
try {
|
||||
const allAssets: any[] = [];
|
||||
let page = 1;
|
||||
const pageSize = 1000;
|
||||
const maxPages = 5; // Cap at 5000 photos to avoid timeouts on large libraries
|
||||
while (true) {
|
||||
const resp = await safeFetch(`${creds.immich_url}/api/search/metadata`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-api-key': creds.immich_api_key, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
takenAfter: from ? `${from}T00:00:00.000Z` : undefined,
|
||||
takenBefore: to ? `${to}T23:59:59.999Z` : undefined,
|
||||
type: 'IMAGE',
|
||||
size: pageSize,
|
||||
page,
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000) as any,
|
||||
});
|
||||
if (!resp.ok) return { error: 'Search failed', status: resp.status };
|
||||
const data = await resp.json() as { assets?: { items?: any[] } };
|
||||
const items = data.assets?.items || [];
|
||||
allAssets.push(...items);
|
||||
if (items.length < pageSize) break;
|
||||
page++;
|
||||
if (page > maxPages) break;
|
||||
}
|
||||
const assets = allAssets.map((a: any) => ({
|
||||
const resp = await safeFetch(`${creds.immich_url}/api/search/metadata`, {
|
||||
method: 'POST',
|
||||
headers: { 'x-api-key': creds.immich_api_key, 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
takenAfter: from ? `${from}T00:00:00.000Z` : undefined,
|
||||
takenBefore: to ? `${to}T23:59:59.999Z` : undefined,
|
||||
type: 'IMAGE',
|
||||
size,
|
||||
page,
|
||||
}),
|
||||
signal: AbortSignal.timeout(15000) as any,
|
||||
});
|
||||
if (!resp.ok) return { error: 'Search failed', status: resp.status };
|
||||
const data = await resp.json() as { assets?: { items?: any[] } };
|
||||
const items = data.assets?.items || [];
|
||||
const assets = items.map((a: any) => ({
|
||||
id: a.id,
|
||||
takenAt: a.fileCreatedAt || a.createdAt,
|
||||
city: a.exifInfo?.city || null,
|
||||
country: a.exifInfo?.country || null,
|
||||
}));
|
||||
return { assets, hasMore: page > maxPages };
|
||||
return { assets, hasMore: items.length >= size };
|
||||
} catch {
|
||||
return { error: 'Could not reach Immich', status: 502 };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user