/** * Centralized Immich API client for rPhotos. * * All calls use the single admin RPHOTOS_API_KEY internally. * This replaces scattered raw fetch calls in mod.ts. */ const IMMICH_BASE = process.env.RPHOTOS_IMMICH_URL || "http://localhost:2284"; const IMMICH_API_KEY = process.env.RPHOTOS_API_KEY || ""; function headers(extra?: Record): Record { return { "x-api-key": IMMICH_API_KEY, ...extra }; } // ── Albums ── export async function immichCreateAlbum(name: string, description?: string) { const res = await fetch(`${IMMICH_BASE}/api/albums`, { method: "POST", headers: headers({ "Content-Type": "application/json" }), body: JSON.stringify({ albumName: name, description: description || "" }), }); if (!res.ok) throw new Error(`Immich createAlbum failed: ${res.status}`); return res.json() as Promise<{ id: string; albumName: string }>; } export async function immichDeleteAlbum(albumId: string) { const res = await fetch(`${IMMICH_BASE}/api/albums/${albumId}`, { method: "DELETE", headers: headers(), }); if (!res.ok) throw new Error(`Immich deleteAlbum failed: ${res.status}`); } export async function immichGetAlbum(albumId: string) { const res = await fetch(`${IMMICH_BASE}/api/albums/${albumId}`, { headers: headers(), }); if (!res.ok) return null; return res.json(); } // ── Search ── export async function immichSearchInAlbum(albumId: string, opts: { size?: number; type?: string } = {}) { const res = await fetch(`${IMMICH_BASE}/api/search/metadata`, { method: "POST", headers: headers({ "Content-Type": "application/json" }), body: JSON.stringify({ size: opts.size || 50, order: "desc", type: opts.type || "IMAGE", ...(albumId ? { albumIds: [albumId] } : {}), }), }); if (!res.ok) return { items: [] }; const data = await res.json(); return { items: data.assets?.items || [] }; } // ── Assets ── export async function immichUploadAsset(formData: FormData) { const res = await fetch(`${IMMICH_BASE}/api/assets`, { method: "POST", headers: { "x-api-key": IMMICH_API_KEY }, body: formData, }); if (!res.ok) throw new Error(`Immich upload failed: ${res.status}`); return res.json() as Promise<{ id: string; status: string }>; } export async function immichAddAssetsToAlbum(albumId: string, assetIds: string[]) { const res = await fetch(`${IMMICH_BASE}/api/albums/${albumId}/assets`, { method: "PUT", headers: headers({ "Content-Type": "application/json" }), body: JSON.stringify({ ids: assetIds }), }); if (!res.ok) throw new Error(`Immich addAssets failed: ${res.status}`); return res.json(); } export async function immichDeleteAssets(assetIds: string[]) { const res = await fetch(`${IMMICH_BASE}/api/assets`, { method: "DELETE", headers: headers({ "Content-Type": "application/json" }), body: JSON.stringify({ ids: assetIds, force: true }), }); if (!res.ok) throw new Error(`Immich deleteAssets failed: ${res.status}`); } export async function immichGetAssetThumbnail(id: string, size: string = "thumbnail") { const res = await fetch(`${IMMICH_BASE}/api/assets/${id}/thumbnail?size=${size}`, { headers: headers(), }); if (!res.ok) return null; return { body: await res.arrayBuffer(), contentType: res.headers.get("Content-Type") || "image/jpeg", }; } export async function immichGetAssetOriginal(id: string) { const res = await fetch(`${IMMICH_BASE}/api/assets/${id}/original`, { headers: headers(), }); if (!res.ok) return null; return { body: await res.arrayBuffer(), contentType: res.headers.get("Content-Type") || "image/jpeg", }; }