fix(rsplat): resize images with sharp before fal.ai submission
Phone photos (3-4MB, 4000px+) were failing with "Invalid image" on fal.ai Hunyuan3D. Now resizes to max 1024px JPEG with sharp before submitting, and uses PUBLIC_ORIGIN HTTPS URL instead of data URI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
05b8e2676a
commit
d999c98b98
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { resolve } from "node:path";
|
import { resolve } from "node:path";
|
||||||
|
import sharp from "sharp";
|
||||||
import { Hono } from "hono";
|
import { Hono } from "hono";
|
||||||
import { cors } from "hono/cors";
|
import { cors } from "hono/cors";
|
||||||
import type { ServerWebSocket } from "bun";
|
import type { ServerWebSocket } from "bun";
|
||||||
|
|
@ -738,22 +739,27 @@ async function process3DGenJob(job: Gen3DJob) {
|
||||||
const MODEL = "fal-ai/hunyuan3d-v21";
|
const MODEL = "fal-ai/hunyuan3d-v21";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Read staged image from disk and convert to data URI for reliable fal.ai delivery
|
// 1. Resize staged image with sharp (max 1024px) for reliable fal.ai processing
|
||||||
let imageInput = job.imageUrl;
|
let imageInput = job.imageUrl;
|
||||||
const stagedMatch = job.imageUrl.match(/\/(?:api\/files|data\/files)\/generated\/([^?#]+)/);
|
const stagedMatch = job.imageUrl.match(/\/(?:api\/files|data\/files)\/generated\/([^?#]+)/);
|
||||||
if (stagedMatch) {
|
if (stagedMatch) {
|
||||||
try {
|
try {
|
||||||
const dir = resolve(process.env.FILES_DIR || "./data/files", "generated");
|
const dir = resolve(process.env.FILES_DIR || "./data/files", "generated");
|
||||||
const file = Bun.file(resolve(dir, stagedMatch[1]));
|
const srcPath = resolve(dir, stagedMatch[1]);
|
||||||
|
const file = Bun.file(srcPath);
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
const buf = Buffer.from(await file.arrayBuffer());
|
const resizedBuf = await sharp(srcPath)
|
||||||
const ext = stagedMatch[1].split(".").pop()?.toLowerCase() || "jpg";
|
.resize(1024, 1024, { fit: "inside", withoutEnlargement: true })
|
||||||
const mime = ext === "png" ? "image/png" : ext === "webp" ? "image/webp" : "image/jpeg";
|
.jpeg({ quality: 90 })
|
||||||
imageInput = `data:${mime};base64,${buf.toString("base64")}`;
|
.toBuffer();
|
||||||
console.log(`[3d-gen] Using data URI (${Math.round(buf.length / 1024)}KB) instead of URL`);
|
const resizedName = `stage-resized-${Date.now()}-${Math.random().toString(36).slice(2, 6)}.jpg`;
|
||||||
|
await Bun.write(resolve(dir, resizedName), resizedBuf);
|
||||||
|
// Use public HTTPS URL for the resized image
|
||||||
|
imageInput = `${PUBLIC_ORIGIN}/api/files/generated/${resizedName}`;
|
||||||
|
console.log(`[3d-gen] Resized image: ${Math.round(resizedBuf.length / 1024)}KB → ${imageInput}`);
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
console.warn(`[3d-gen] Data URI fallback failed, using URL:`, e.message);
|
console.warn(`[3d-gen] Resize failed, using original URL:`, e.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue