File size: 5,010 Bytes
dce7eca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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) { }
    }
}