|
|
import { sendSuccess, ErrorResponses } from "../../lib/response-helper.js"; |
|
|
import { AIEnhancer } from "./ai_image_utils.js"; |
|
|
import multer from "multer"; |
|
|
import axios from "axios"; |
|
|
import path from "path"; |
|
|
|
|
|
const ModelMap = { |
|
|
nano_banana: 2, |
|
|
seed_dream: 5, |
|
|
flux: 8, |
|
|
qwen_image: 9 |
|
|
}; |
|
|
|
|
|
const storage = multer.memoryStorage(); |
|
|
const upload = multer({ |
|
|
storage: storage, |
|
|
limits: { |
|
|
fileSize: 5 * 1024 * 1024, |
|
|
}, |
|
|
fileFilter: (req, file, cb) => { |
|
|
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; |
|
|
if (allowedTypes.includes(file.mimetype)) { |
|
|
cb(null, true); |
|
|
} else { |
|
|
cb(new Error('Invalid file type. Only JPEG, PNG, GIF, and WebP are allowed.')); |
|
|
} |
|
|
} |
|
|
}); |
|
|
|
|
|
|
|
|
const handler = { |
|
|
name: "AI Image Editor", |
|
|
description: "Edit your images using AI", |
|
|
method: "POST", |
|
|
version: "1.0.0", |
|
|
category: ["ai"], |
|
|
alias: ["aiImageEditor"], |
|
|
tags: ["ai", "image"], |
|
|
disabled: true, |
|
|
disabledReason: "The API Server is restricting access to their API with IP Tracking, We are making some efforts to find a way at the moment, Please wait until it is fixed", |
|
|
parameters: { |
|
|
body: [ |
|
|
{ |
|
|
name: "image", |
|
|
type: "file", |
|
|
required: true, |
|
|
description: "Image file to edit (or URL)", |
|
|
fileConstraints: { |
|
|
maxSize: 5 * 1024 * 1024, |
|
|
acceptedTypes: ["image/jpeg", "image/png", "image/gif", "image/webp"], |
|
|
acceptedExtensions: [".jpg", ".jpeg", ".png", ".gif", ".webp"] |
|
|
}, |
|
|
acceptUrl: true |
|
|
}, |
|
|
{ |
|
|
name: "model", |
|
|
type: "string", |
|
|
required: true, |
|
|
description: "Select an AI model to edit your image.", |
|
|
example: "nano_banana", |
|
|
enum: ["nano_banana", "seed_dream", "flux", "qwen_image"], |
|
|
default: "nano_banana" |
|
|
}, |
|
|
{ |
|
|
name: "prompt", |
|
|
type: "string", |
|
|
required: true, |
|
|
description: "Tell AI how your image will look edited", |
|
|
example: "Put Cristiano Ronaldo next to that person" |
|
|
}, |
|
|
{ |
|
|
name: "output_size", |
|
|
type: "string", |
|
|
required: true, |
|
|
description: "Output size", |
|
|
example: "2K", |
|
|
enum: ["2K", "4K", "8K"], |
|
|
default: "2K" |
|
|
}, |
|
|
{ |
|
|
name: "disable_safety_checker", |
|
|
type: "boolean", |
|
|
required: false, |
|
|
description: "If you want to disable safety checker for indecent images, set to true (default false)", |
|
|
example: false, |
|
|
default: false |
|
|
} |
|
|
] |
|
|
}, |
|
|
responses: { |
|
|
200: { |
|
|
status: 200, |
|
|
description: "Successfully retrieved data", |
|
|
example: { |
|
|
status: 200, |
|
|
author: "Ditzzy", |
|
|
note: "Thank you for using this API!", |
|
|
results: { |
|
|
image_url: "https://example.com/edited-image.jpg", |
|
|
filename: "edited-image.jpg", |
|
|
size: "2K" |
|
|
} |
|
|
} |
|
|
}, |
|
|
404: { |
|
|
status: 404, |
|
|
description: "Missing required parameter", |
|
|
example: { |
|
|
status: 404, |
|
|
message: "Missing required parameter" |
|
|
} |
|
|
}, |
|
|
400: { |
|
|
status: 400, |
|
|
description: "Invalid file or URL", |
|
|
example: { |
|
|
success: false, |
|
|
message: "Invalid image file or URL" |
|
|
} |
|
|
}, |
|
|
413: { |
|
|
status: 413, |
|
|
description: "File too large", |
|
|
example: { |
|
|
success: false, |
|
|
message: "File size exceeds 5MB limit" |
|
|
} |
|
|
}, |
|
|
500: { |
|
|
status: 500, |
|
|
description: "Server error or unavailable", |
|
|
example: { |
|
|
status: 500, |
|
|
message: "An error occurred, please try again later." |
|
|
} |
|
|
} |
|
|
}, |
|
|
exec: async (req, res) => { |
|
|
upload.single('image')(req, res, async (err) => { |
|
|
if (err) { |
|
|
if (err instanceof multer.MulterError) { |
|
|
if (err.code === 'LIMIT_FILE_SIZE') { |
|
|
return res.status(413).json({ |
|
|
success: false, |
|
|
message: "File size exceeds 5MB limit" |
|
|
}); |
|
|
} |
|
|
} |
|
|
return res.status(400).json({ |
|
|
success: false, |
|
|
message: err.message || "File upload error" |
|
|
}); |
|
|
} |
|
|
|
|
|
try { |
|
|
const { prompt, output_size, disable_safety_checker, model } = req.body; |
|
|
|
|
|
if (!prompt) { |
|
|
return ErrorResponses.missingParameter(res, "prompt"); |
|
|
} |
|
|
if (!output_size) { |
|
|
return ErrorResponses.missingParameter(res, "output_size"); |
|
|
} |
|
|
if (!model) { |
|
|
return ErrorResponses.missingParameter(res, "model"); |
|
|
} |
|
|
|
|
|
if (!ModelMap[model]) { |
|
|
return res.status(400).json({ |
|
|
success: false, |
|
|
message: `Invalid model. Must be one of: ${Object.keys(ModelMap).join(', ')}` |
|
|
}); |
|
|
} |
|
|
|
|
|
const ai = new AIEnhancer(); |
|
|
let imageBuffer; |
|
|
let filename; |
|
|
|
|
|
if (req.file) { |
|
|
imageBuffer = req.file.buffer; |
|
|
filename = req.file.originalname; |
|
|
} |
|
|
else if (req.body.image && typeof req.body.image === 'string') { |
|
|
const imageUrl = req.body.image; |
|
|
|
|
|
try { |
|
|
new URL(imageUrl); |
|
|
} catch { |
|
|
return ErrorResponses.invalidUrl(res, "Invalid image URL"); |
|
|
} |
|
|
|
|
|
const response = await axios.get(imageUrl, { |
|
|
responseType: 'arraybuffer', |
|
|
maxContentLength: 5 * 1024 * 1024, |
|
|
timeout: 10000, |
|
|
headers: { |
|
|
'User-Agent': 'Mozilla/5.0 (compatible; AIImageEditor/1.0)' |
|
|
} |
|
|
}); |
|
|
|
|
|
imageBuffer = Buffer.from(response.data); |
|
|
filename = path.basename(new URL(imageUrl).pathname) || 'image'; |
|
|
} else { |
|
|
return ErrorResponses.missingParameter(res, "image (file or URL)"); |
|
|
} |
|
|
|
|
|
const edit = await ai.ImageAIEditor(imageBuffer, model, { |
|
|
size: output_size, |
|
|
aspect_ratio: "match_input_image", |
|
|
go_fast: true, |
|
|
prompt: prompt, |
|
|
output_quality: 100, |
|
|
disable_safety_checker: disable_safety_checker === 'true' || disable_safety_checker === true, |
|
|
}); |
|
|
|
|
|
return sendSuccess(res, edit.results); |
|
|
} catch (e) { |
|
|
console.error("Exception caught:", e); |
|
|
return ErrorResponses.serverError(res, "An error occurred, try again later."); |
|
|
} |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
export default handler; |