From 7201380504e9cde0dfc6e640de33778b2c78d59e Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 30 Mar 2026 13:36:04 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20paginate=20Immich=20photo=20search=20?= =?UTF-8?q?=E2=80=94=20no=20longer=20limited=20to=20200=20=E2=80=94=20clos?= =?UTF-8?q?es=20#137?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Immich metadata search was hardcoded to size: 200. Now paginates through all results (1000 per page, up to 20k photos max). --- server/src/routes/immich.ts | 40 ++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/server/src/routes/immich.ts b/server/src/routes/immich.ts index 4efc5ce..83b480d 100644 --- a/server/src/routes/immich.ts +++ b/server/src/routes/immich.ts @@ -77,20 +77,32 @@ router.post('/search', authenticate, async (req: Request, res: Response) => { if (!user?.immich_url || !user?.immich_api_key) return res.status(400).json({ error: 'Immich not configured' }); try { - const resp = await fetch(`${user.immich_url}/api/search/metadata`, { - method: 'POST', - headers: { 'x-api-key': user.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: 200, - }), - signal: AbortSignal.timeout(15000), - }); - if (!resp.ok) return res.status(resp.status).json({ error: 'Search failed' }); - const data = await resp.json() as { assets?: { items?: any[] } }; - const assets = (data.assets?.items || []).map((a: any) => ({ + // Paginate through all results (Immich limits per-page to 1000) + const allAssets: any[] = []; + let page = 1; + const pageSize = 1000; + while (true) { + const resp = await fetch(`${user.immich_url}/api/search/metadata`, { + method: 'POST', + headers: { 'x-api-key': user.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), + }); + if (!resp.ok) return res.status(resp.status).json({ error: 'Search failed' }); + const data = await resp.json() as { assets?: { items?: any[] } }; + const items = data.assets?.items || []; + allAssets.push(...items); + if (items.length < pageSize) break; // Last page + page++; + if (page > 20) break; // Safety limit (20k photos max) + } + const assets = allAssets.map((a: any) => ({ id: a.id, takenAt: a.fileCreatedAt || a.createdAt, city: a.exifInfo?.city || null,