import "server-only"; import { randomUUID } from "node:crypto"; import { fetchImageBytes, cacheImageBytes } from "./image-byte-cache"; import { normalizeCanvasDownloadAsset } from "@/lib/canvas/photoshop-safe-download"; import { claimImageDownloadTask, saveImageDownloadAsset, getImageDownloadTasks } from "./image-archive-db"; import { uploadFile } from "./service"; export async function prepareImageArchiveDownload(id: string) { const task = await claimImageDownloadTask(id); if (!task?.targetUrl) return; try { const source = await fetchImageBytes(task.targetUrl); const asset = await normalizeCanvasDownloadAsset({ item: { url: task.targetUrl }, index: 0, bytes: source.buffer, contentType: source.contentType, includeSequencePrefix: false, }); if (!asset.ok) throw new Error(asset.error); // An already compatible PNG shares the original object; other formats get // one prepared PNG, without changing the original dimensions or transparency. const url = source.buffer.equals(asset.bytes) && asset.contentType === "image/png" ? task.targetUrl : (await uploadFile(`canvas-download-${randomUUID()}.${asset.filename.split(".").pop() || "png"}`, Uint8Array.from(asset.bytes).buffer)).url; await saveImageDownloadAsset(id, { status: "ready", url, bytes: asset.bytes.length, contentType: asset.contentType, preparedAt: new Date().toISOString() }); await cacheImageBytes(`source::${url}`, { buffer: asset.bytes, contentType: asset.contentType }); } catch { const attempts = Number((task.metadata as { downloadAsset?: { attempts?: number } } | null)?.downloadAsset?.attempts || 1); await saveImageDownloadAsset(id, { status: attempts >= 12 ? "abandoned" : "failed", attempts, retryAt: new Date(Date.now() + 10 * 60_000).toISOString() }); } } export async function preparePendingImageDownloads() { const tasks = await getImageDownloadTasks(); let cursor = 0; await Promise.all(Array.from({ length: Math.min(2, tasks.length) }, async () => { while (cursor < tasks.length) await prepareImageArchiveDownload(tasks[cursor++].id); })); return tasks.length; }