fix(rsplat): resize images client-side before 3D generation, pass through fal.ai errors
Mobile photos (12MP+) were causing generation failures due to large base64 payloads. Now resizes to max 1024px before sending. Server now returns actual fal.ai error messages instead of generic "3D generation failed". Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
e5b5c551b1
commit
c5f757d050
|
|
@ -521,15 +521,13 @@ export class FolkSplatViewer extends HTMLElement {
|
||||||
submitBtn.disabled = true;
|
submitBtn.disabled = true;
|
||||||
actions.style.display = "none";
|
actions.style.display = "none";
|
||||||
progress.style.display = "block";
|
progress.style.display = "block";
|
||||||
status.textContent = "";
|
status.textContent = "Preparing image...";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const reader = new FileReader();
|
// Resize image to max 1024px to reduce payload and improve API success
|
||||||
const dataUrl = await new Promise<string>((resolve) => {
|
const dataUrl = await this.resizeImage(selectedFile!, 1024);
|
||||||
reader.onload = () => resolve(reader.result as string);
|
|
||||||
reader.readAsDataURL(selectedFile!);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
status.textContent = "Generating 3D model...";
|
||||||
const res = await fetch("/api/3d-gen", {
|
const res = await fetch("/api/3d-gen", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
|
|
@ -572,6 +570,30 @@ export class FolkSplatViewer extends HTMLElement {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Image helpers ──
|
||||||
|
|
||||||
|
private resizeImage(file: File, maxSize: number): Promise<string> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
let { width, height } = img;
|
||||||
|
if (width > maxSize || height > maxSize) {
|
||||||
|
const scale = maxSize / Math.max(width, height);
|
||||||
|
width = Math.round(width * scale);
|
||||||
|
height = Math.round(height * scale);
|
||||||
|
}
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
const ctx = canvas.getContext("2d")!;
|
||||||
|
ctx.drawImage(img, 0, 0, width, height);
|
||||||
|
resolve(canvas.toDataURL("image/jpeg", 0.9));
|
||||||
|
};
|
||||||
|
img.onerror = () => reject(new Error("Failed to load image"));
|
||||||
|
img.src = URL.createObjectURL(file);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// ── Viewer ──
|
// ── Viewer ──
|
||||||
|
|
||||||
private renderViewer() {
|
private renderViewer() {
|
||||||
|
|
|
||||||
|
|
@ -998,9 +998,18 @@ app.post("/api/3d-gen", async (c) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
const err = await res.text();
|
const errText = await res.text();
|
||||||
console.error("[3d-gen] fal.ai error:", err);
|
console.error("[3d-gen] fal.ai error:", res.status, errText);
|
||||||
return c.json({ error: "3D generation failed" }, 502);
|
let detail = "3D generation failed";
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(errText);
|
||||||
|
if (parsed.detail) {
|
||||||
|
detail = typeof parsed.detail === "string" ? parsed.detail
|
||||||
|
: Array.isArray(parsed.detail) ? parsed.detail[0]?.msg || detail
|
||||||
|
: detail;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
return c.json({ error: detail }, 502);
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue