OhMyDitzzy
commited on
Commit
Β·
4c8cf27
1
Parent(s):
33cfc81
fix: image editor
Browse files
src/server/plugins/ai/ai_image_editor.js
CHANGED
|
@@ -4,6 +4,13 @@ import multer from "multer";
|
|
| 4 |
import axios from "axios";
|
| 5 |
import path from "path";
|
| 6 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
const storage = multer.memoryStorage();
|
| 8 |
const upload = multer({
|
| 9 |
storage: storage,
|
|
@@ -20,13 +27,6 @@ const upload = multer({
|
|
| 20 |
}
|
| 21 |
});
|
| 22 |
|
| 23 |
-
const ModelMap = {
|
| 24 |
-
nano_banana: 2,
|
| 25 |
-
seed_dream: 5,
|
| 26 |
-
flux: 8,
|
| 27 |
-
qwen_image: 9
|
| 28 |
-
};
|
| 29 |
-
|
| 30 |
/** @type {import("../../types/plugin.ts").ApiPluginHandler}*/
|
| 31 |
const handler = {
|
| 32 |
name: "AI Image Editor",
|
|
@@ -36,9 +36,6 @@ const handler = {
|
|
| 36 |
category: ["ai"],
|
| 37 |
alias: ["aiImageEditor"],
|
| 38 |
tags: ["ai", "image"],
|
| 39 |
-
disabled: true,
|
| 40 |
-
disabledReason: "Temporarily disabled",
|
| 41 |
-
|
| 42 |
parameters: {
|
| 43 |
body: [
|
| 44 |
{
|
|
@@ -137,14 +134,8 @@ const handler = {
|
|
| 137 |
}
|
| 138 |
},
|
| 139 |
exec: async (req, res) => {
|
| 140 |
-
console.log("π΅ [1] Request received at AI Image Editor");
|
| 141 |
-
console.log("π΅ [1] Headers:", req.headers);
|
| 142 |
-
|
| 143 |
upload.single('image')(req, res, async (err) => {
|
| 144 |
-
console.log("π΅ [2] Inside multer middleware");
|
| 145 |
-
|
| 146 |
if (err) {
|
| 147 |
-
console.error("β [2] Multer error:", err);
|
| 148 |
if (err instanceof multer.MulterError) {
|
| 149 |
if (err.code === 'LIMIT_FILE_SIZE') {
|
| 150 |
return res.status(413).json({
|
|
@@ -159,65 +150,43 @@ const handler = {
|
|
| 159 |
});
|
| 160 |
}
|
| 161 |
|
| 162 |
-
console.log("π΅ [3] Multer successful");
|
| 163 |
-
console.log("π΅ [3] Body:", req.body);
|
| 164 |
-
console.log("π΅ [3] File:", req.file ? `${req.file.originalname} (${req.file.size} bytes)` : "No file");
|
| 165 |
-
|
| 166 |
try {
|
| 167 |
-
const { prompt, output_size, disable_safety_checker, model } = req.body;
|
| 168 |
|
| 169 |
-
console.log("π΅ [4] Extracted params:", { prompt, output_size, model, disable_safety_checker });
|
| 170 |
-
|
| 171 |
-
// Validate required parameters
|
| 172 |
if (!prompt) {
|
| 173 |
-
console.log("β [4] Missing prompt");
|
| 174 |
return ErrorResponses.missingParameter(res, "prompt");
|
| 175 |
}
|
| 176 |
if (!output_size) {
|
| 177 |
-
console.log("β [4] Missing output_size");
|
| 178 |
return ErrorResponses.missingParameter(res, "output_size");
|
| 179 |
}
|
| 180 |
if (!model) {
|
| 181 |
-
console.log("β [4] Missing model");
|
| 182 |
return ErrorResponses.missingParameter(res, "model");
|
| 183 |
}
|
| 184 |
|
| 185 |
-
// Validate model
|
| 186 |
if (!ModelMap[model]) {
|
| 187 |
-
console.log("β [4] Invalid model:", model);
|
| 188 |
return res.status(400).json({
|
| 189 |
success: false,
|
| 190 |
message: `Invalid model. Must be one of: ${Object.keys(ModelMap).join(', ')}`
|
| 191 |
});
|
| 192 |
}
|
| 193 |
|
| 194 |
-
console.log("π΅ [5] Creating AIEnhancer instance");
|
| 195 |
const ai = new AIEnhancer();
|
| 196 |
-
console.log("β
[5] AIEnhancer created");
|
| 197 |
-
|
| 198 |
let imageBuffer;
|
| 199 |
let filename;
|
| 200 |
-
|
| 201 |
-
// Handle uploaded file
|
| 202 |
if (req.file) {
|
| 203 |
-
console.log("π΅ [6] Using uploaded file");
|
| 204 |
imageBuffer = req.file.buffer;
|
| 205 |
filename = req.file.originalname;
|
| 206 |
}
|
| 207 |
-
// Handle URL
|
| 208 |
else if (req.body.image && typeof req.body.image === 'string') {
|
| 209 |
-
console.log("π΅ [6] Downloading from URL:", req.body.image);
|
| 210 |
const imageUrl = req.body.image;
|
| 211 |
|
| 212 |
-
// Validate URL
|
| 213 |
try {
|
| 214 |
new URL(imageUrl);
|
| 215 |
} catch {
|
| 216 |
-
console.log("β [6] Invalid URL");
|
| 217 |
return ErrorResponses.invalidUrl(res, "Invalid image URL");
|
| 218 |
}
|
| 219 |
-
|
| 220 |
-
// Download image from URL
|
| 221 |
const response = await axios.get(imageUrl, {
|
| 222 |
responseType: 'arraybuffer',
|
| 223 |
maxContentLength: 5 * 1024 * 1024,
|
|
@@ -229,21 +198,11 @@ const handler = {
|
|
| 229 |
|
| 230 |
imageBuffer = Buffer.from(response.data);
|
| 231 |
filename = path.basename(new URL(imageUrl).pathname) || 'image';
|
| 232 |
-
console.log("β
[6] Downloaded from URL:", filename);
|
| 233 |
} else {
|
| 234 |
-
console.log("β [6] No image provided");
|
| 235 |
return ErrorResponses.missingParameter(res, "image (file or URL)");
|
| 236 |
}
|
| 237 |
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
// Get model ID from map
|
| 241 |
-
const modelId = ModelMap[model];
|
| 242 |
-
console.log("π΅ [8] Model ID:", modelId);
|
| 243 |
-
|
| 244 |
-
// Call AI Image Editor
|
| 245 |
-
console.log("π΅ [9] Calling ImageAIEditor...");
|
| 246 |
-
const edit = await ai.ImageAIEditor(imageBuffer, modelId, {
|
| 247 |
size: output_size,
|
| 248 |
aspect_ratio: "match_input_image",
|
| 249 |
go_fast: true,
|
|
@@ -251,13 +210,10 @@ const handler = {
|
|
| 251 |
output_quality: 100,
|
| 252 |
disable_safety_checker: disable_safety_checker === 'true' || disable_safety_checker === true,
|
| 253 |
});
|
| 254 |
-
|
| 255 |
-
console.log("β
[9] ImageAIEditor response:", edit);
|
| 256 |
|
| 257 |
-
return sendSuccess(res, edit.results
|
| 258 |
} catch (e) {
|
| 259 |
-
console.error("
|
| 260 |
-
console.error("β [ERROR] Stack trace:", e.stack);
|
| 261 |
return ErrorResponses.serverError(res, "An error occurred, try again later.");
|
| 262 |
}
|
| 263 |
});
|
|
|
|
| 4 |
import axios from "axios";
|
| 5 |
import path from "path";
|
| 6 |
|
| 7 |
+
const ModelMap = {
|
| 8 |
+
nano_banana: 2,
|
| 9 |
+
seed_dream: 5,
|
| 10 |
+
flux: 8,
|
| 11 |
+
qwen_image: 9
|
| 12 |
+
};
|
| 13 |
+
|
| 14 |
const storage = multer.memoryStorage();
|
| 15 |
const upload = multer({
|
| 16 |
storage: storage,
|
|
|
|
| 27 |
}
|
| 28 |
});
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
/** @type {import("../../types/plugin.ts").ApiPluginHandler}*/
|
| 31 |
const handler = {
|
| 32 |
name: "AI Image Editor",
|
|
|
|
| 36 |
category: ["ai"],
|
| 37 |
alias: ["aiImageEditor"],
|
| 38 |
tags: ["ai", "image"],
|
|
|
|
|
|
|
|
|
|
| 39 |
parameters: {
|
| 40 |
body: [
|
| 41 |
{
|
|
|
|
| 134 |
}
|
| 135 |
},
|
| 136 |
exec: async (req, res) => {
|
|
|
|
|
|
|
|
|
|
| 137 |
upload.single('image')(req, res, async (err) => {
|
|
|
|
|
|
|
| 138 |
if (err) {
|
|
|
|
| 139 |
if (err instanceof multer.MulterError) {
|
| 140 |
if (err.code === 'LIMIT_FILE_SIZE') {
|
| 141 |
return res.status(413).json({
|
|
|
|
| 150 |
});
|
| 151 |
}
|
| 152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
try {
|
| 154 |
+
const { prompt, output_size, disable_safety_checker, model } = req.body;
|
| 155 |
|
|
|
|
|
|
|
|
|
|
| 156 |
if (!prompt) {
|
|
|
|
| 157 |
return ErrorResponses.missingParameter(res, "prompt");
|
| 158 |
}
|
| 159 |
if (!output_size) {
|
|
|
|
| 160 |
return ErrorResponses.missingParameter(res, "output_size");
|
| 161 |
}
|
| 162 |
if (!model) {
|
|
|
|
| 163 |
return ErrorResponses.missingParameter(res, "model");
|
| 164 |
}
|
| 165 |
|
|
|
|
| 166 |
if (!ModelMap[model]) {
|
|
|
|
| 167 |
return res.status(400).json({
|
| 168 |
success: false,
|
| 169 |
message: `Invalid model. Must be one of: ${Object.keys(ModelMap).join(', ')}`
|
| 170 |
});
|
| 171 |
}
|
| 172 |
|
|
|
|
| 173 |
const ai = new AIEnhancer();
|
|
|
|
|
|
|
| 174 |
let imageBuffer;
|
| 175 |
let filename;
|
| 176 |
+
|
|
|
|
| 177 |
if (req.file) {
|
|
|
|
| 178 |
imageBuffer = req.file.buffer;
|
| 179 |
filename = req.file.originalname;
|
| 180 |
}
|
|
|
|
| 181 |
else if (req.body.image && typeof req.body.image === 'string') {
|
|
|
|
| 182 |
const imageUrl = req.body.image;
|
| 183 |
|
|
|
|
| 184 |
try {
|
| 185 |
new URL(imageUrl);
|
| 186 |
} catch {
|
|
|
|
| 187 |
return ErrorResponses.invalidUrl(res, "Invalid image URL");
|
| 188 |
}
|
| 189 |
+
|
|
|
|
| 190 |
const response = await axios.get(imageUrl, {
|
| 191 |
responseType: 'arraybuffer',
|
| 192 |
maxContentLength: 5 * 1024 * 1024,
|
|
|
|
| 198 |
|
| 199 |
imageBuffer = Buffer.from(response.data);
|
| 200 |
filename = path.basename(new URL(imageUrl).pathname) || 'image';
|
|
|
|
| 201 |
} else {
|
|
|
|
| 202 |
return ErrorResponses.missingParameter(res, "image (file or URL)");
|
| 203 |
}
|
| 204 |
|
| 205 |
+
const edit = await ai.ImageAIEditor(imageBuffer, model, {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
size: output_size,
|
| 207 |
aspect_ratio: "match_input_image",
|
| 208 |
go_fast: true,
|
|
|
|
| 210 |
output_quality: 100,
|
| 211 |
disable_safety_checker: disable_safety_checker === 'true' || disable_safety_checker === true,
|
| 212 |
});
|
|
|
|
|
|
|
| 213 |
|
| 214 |
+
return sendSuccess(res, edit.results);
|
| 215 |
} catch (e) {
|
| 216 |
+
console.error("Exception caught:", e);
|
|
|
|
| 217 |
return ErrorResponses.serverError(res, "An error occurred, try again later.");
|
| 218 |
}
|
| 219 |
});
|
src/server/plugins/ai/ai_image_utils.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
const ModelMap = {
|
| 2 |
nano_banana: 2,
|
| 3 |
seed_dream: 5,
|
|
|
|
| 1 |
+
import axios from "axios";
|
| 2 |
+
import { fileTypeFromBuffer } from "file-type";
|
| 3 |
+
import { readFileSync } from "fs";
|
| 4 |
+
import CryptoJS from "crypto-js";
|
| 5 |
+
|
| 6 |
const ModelMap = {
|
| 7 |
nano_banana: 2,
|
| 8 |
seed_dream: 5,
|