import { NextRequest, NextResponse } from "next/server"; import { exec } from "child_process"; import { promisify } from "util"; import fs from "fs"; import path from "path"; import os from "os"; import { GoogleGenerativeAI } from "@google/generative-ai"; const execAsync = promisify(exec); export async function POST(req: NextRequest) { const tempFiles: string[] = []; try { const formData = await req.formData(); const prompt = formData.get("prompt") as string || "a photographic 360 panorama"; const images = formData.getAll("images") as File[]; // 1. Save uploaded images to temp directory const tempDir = path.join(os.tmpdir(), `panno-${Date.now()}`); if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir); for (let i = 0; i < images.length; i++) { const buffer = Buffer.from(await images[i].arrayBuffer()); const fileName = path.join(tempDir, `img_${i}.png`); fs.writeFileSync(fileName, buffer); tempFiles.push(fileName); } // 2. Load Config & Keys const stabilityKey = process.env.Home_STABILITY_API_KEY || process.env.STABILITY_API_KEY; const geminiKey = process.env.GEMINI_API_KEY || process.env.GOOGLE_API_KEY; if (!stabilityKey) { throw new Error("Missing STABILITY_API_KEY in Environment Variables."); } let result: { image: string | null, method: string } = { image: null, method: "failed" }; // --- CORE LOGIC: Direct Python Local Execution --- // Since we are in a unified Docker container on HF, we always run locally. try { const pythonScript = path.join(process.cwd(), "scripts", "processor.py"); const imageArgs = tempFiles.map(img => `"${img}"`).join(" "); console.log("Unified Container: Executing specialized Python engine..."); // Detect OS and use appropriate python command const pythonCmd = process.platform === "win32" ? "python" : "python3"; const { stdout } = await execAsync(`${pythonCmd} "${pythonScript}" "${stabilityKey}" "${prompt.replace(/"/g, '\\"')}" ${imageArgs}`, { maxBuffer: 1024 * 1024 * 20, timeout: 120000 // 2 minute limit for heavy processing }); const parsed = JSON.parse(stdout); if (parsed.success) { result = { image: parsed.image, method: "unified_hf_engine" }; } else { throw new Error(parsed.error || "Stitching engine failed"); } } catch (err: any) { console.warn("Local Engine Error, attempting Vision Fallback:", err.message); // --- FALLBACK: Pure AI Cloud (If Python fails) --- let visionPrompt = prompt; if (geminiKey && tempFiles.length > 0) { try { const genAI = new GoogleGenerativeAI(geminiKey); const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" }); const visionResult = await model.generateContent([ "Describe room type and style in 10 words.", { inlineData: { data: fs.readFileSync(tempFiles[0]).toString("base64"), mimeType: "image/png" } } ]); visionPrompt = `${prompt}. Style: ${visionResult.response.text()}`; } catch (e) { } } const aiFormData = new FormData(); aiFormData.append("prompt", `${visionPrompt}, 360 panorama, rectilinear, high quality, seamless`); aiFormData.append("output_format", "webp"); aiFormData.append("aspect_ratio", "21:9"); const response = await fetch("https://api.stability.ai/v2beta/stable-image/generate/ultra", { method: "POST", headers: { "Authorization": `Bearer ${stabilityKey}`, "Accept": "application/json" }, body: aiFormData }); const data = await response.json(); if (response.ok && data.image) { result = { image: `data:image/webp;base64,${data.image}`, method: "unified_pure_ai_fallback" }; } else { throw new Error(data.message || "All methods failed"); } } return NextResponse.json({ url: result.image, success: true, method: result.method }); } catch (error: any) { console.error("Unified Pipeline Error:", error.message); return NextResponse.json({ success: false, message: error.message || "Process failed" }, { status: 500 }); } finally { // Cleanup try { tempFiles.forEach(f => { if (fs.existsSync(f)) fs.unlinkSync(f); }); const dir = path.dirname(tempFiles[0]); if (dir && fs.existsSync(dir)) fs.rmdirSync(dir); } catch (e) { } } }