/***
@ Base: https://imgeditor.co/
@ Author: Shannz
@ Note: image edit with nanobanana ai
***/
import axios from 'axios';
import { readFileSync } from 'fs';
import { basename } from 'path';
const BASE_URL = 'https://imgeditor.co';
const HEADERS = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Mobile Safari/537.36',
'Accept-Encoding': 'gzip, deflate, br, zstd',
'sec-ch-ua-platform': '"Android"',
'sec-ch-ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
'dnt': '1',
'sec-ch-ua-mobile': '?1',
'origin': BASE_URL,
'sec-fetch-site': 'same-origin',
'sec-fetch-mode': 'cors',
'sec-fetch-dest': 'empty',
'referer': `${BASE_URL}/generator`,
'accept-language': 'id,en-US;q=0.9,en;q=0.8,ja;q=0.7',
'priority': 'u=1, i'
};
export const imgeditor = {
getPresign: async (fileName, contentType = 'image/jpeg', fileSize) => {
try {
const response = await axios.post(
`${BASE_URL}/api/get-upload-url`,
{
fileName,
contentType,
fileSize
},
{
headers: {
...HEADERS,
'Content-Type': 'application/json'
}
}
);
return response.data;
} catch (error) {
throw new Error(`Failed to get presigned URL: ${error.message}`);
}
},
upload: async (uploadUrl, fileData, contentType = 'image/jpeg') => {
try {
const data = typeof fileData === 'string' ? readFileSync(fileData) : fileData;
const response = await axios.put(uploadUrl, data, {
headers: {
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Mobile Safari/537.36',
'Accept-Encoding': 'gzip, deflate, br, zstd',
'Content-Type': contentType,
'sec-ch-ua-platform': '"Android"',
'sec-ch-ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
'DNT': '1',
'sec-ch-ua-mobile': '?1',
'Origin': BASE_URL,
'Sec-Fetch-Site': 'cross-site',
'Sec-Fetch-Mode': 'cors',
'Sec-Fetch-Dest': 'empty',
'Referer': `${BASE_URL}/`,
'Accept-Language': 'id,en-US;q=0.9,en;q=0.8,ja;q=0.7'
}
});
return {
success: response.status === 200,
etag: response.headers.etag
};
} catch (error) {
throw new Error(`Failed to upload file: ${error.message}`);
}
},
submit: async (options) => {
const {
prompt,
imageUrl,
styleId = 'realistic',
mode = 'image',
imageSize = 'auto',
quality = 'standard',
numImages = 1,
outputFormat = 'png',
model = 'nano-banana'
} = options;
try {
const response = await axios.post(
`${BASE_URL}/api/generate-image`,
{
prompt,
styleId,
mode,
imageUrl,
imageUrls: [imageUrl],
imageSize,
quality,
numImages,
outputFormat,
model
},
{
headers: {
...HEADERS,
'Content-Type': 'application/json'
}
}
);
return response.data;
} catch (error) {
throw new Error(`Failed to submit generation: ${error.message}`);
}
},
status: async (taskId) => {
try {
const response = await axios.get(
`${BASE_URL}/api/generate-image/status`,
{
params: { taskId },
headers: HEADERS
}
);
return response.data;
} catch (error) {
throw new Error(`Failed to check status: ${error.message}`);
}
},
create: async (filePath, prompt, options = {}, pollInterval = 5000) => {
try {
const fileData = readFileSync(filePath);
const fileName = basename(filePath);
const contentType = fileName.endsWith('.png') ? 'image/png' : 'image/jpeg';
const fileSize = fileData.length;
const presignData = await imgeditor.getPresign(fileName, contentType, fileSize);
await imgeditor.upload(presignData.uploadUrl, fileData, contentType);
const submitData = await imgeditor.submit({
prompt,
imageUrl: presignData.publicUrl,
...options
});
let statusData;
let attempts = 0;
const maxAttempts = 60;
while (attempts < maxAttempts) {
statusData = await imgeditor.status(submitData.taskId);
if (statusData.status === 'completed') {
return {
imageUrl: statusData.imageUrl,
taskId: submitData.taskId,
completedAt: statusData.completedAt
};
} else if (statusData.status === 'failed') {
throw new Error(`Generation failed: ${statusData.error || 'Unknown error'}`);
}
await new Promise(resolve => setTimeout(resolve, pollInterval));
attempts++;
}
throw new Error('Generation timeout after 5 minutes');
} catch (error) {
throw new Error(`Create failed: ${error.message}`);
}
}
};