API / features /ghibli-remix.js
Puruu Puruu
Ghibli AI
60a7dd1
/*
* Lokasi: features/ghibli-remix.js
* Versi: v1
*/
const axios = require('axios');
const { createDecipheriv } = require('crypto');
async function sendCallback(url, payload) {
try {
await axios.post(url, payload, { headers: { 'Content-Type': 'application/json' } });
} catch (error) {
console.error('Callback failed:', error.response ? error.response.data : error.message);
}
}
const ghibai = {
api: {
base: 'https://generate-api.ghibli-gpt.net',
endpoints: {
generate: '/v1/gpt4o-image/generate',
task: '/v1/gpt4o-image/record-info',
},
},
headers: {
accept: '*/*',
'content-type': 'application/json',
origin: 'https://ghibli-gpt.net',
referer: 'https://ghibli-gpt.net/',
'user-agent': 'NB Android/1.0.0',
authorization: ''
},
state: { token: null },
security: {
keyBase64: 'UBsnTxs80g8p4iW72eYyPaDvGZbpzun8K2cnoSSEz1Y',
ivBase64: 'fG1SBDUyE2IG8kPw',
ciphertextBase64: '2QpqZCkOD/WMHixMqt46AvhdKRYgy5aUMLXi6D0nOPGuDbH4gbNKDV0ZW/+9w9I=',
decrypt: async () => {
if (ghibai.state.token) return ghibai.state.token;
const buf = k => Buffer.from(ghibai.security[k], 'base64');
const [key, iv, ciphertext] = ['keyBase64', 'ivBase64', 'ciphertextBase64'].map(buf);
const decipher = createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(ciphertext.slice(-16));
const decrypted = decipher.update(ciphertext.slice(0, -16), undefined, 'utf8') + decipher.final('utf8');
ghibai.state.token = decrypted;
ghibai.headers.authorization = `Bearer ${decrypted}`;
return decrypted;
}
},
getTask: async (jobDetails) => {
const { taskId, prompt, jobId, callbackUrl, callbackKey } = jobDetails;
await ghibai.security.decrypt();
for (let i = 0; i < 60; i++) {
try {
const { data } = await axios.get(`${ghibai.api.base}${ghibai.api.endpoints.task}?taskId=${taskId}`, { headers: ghibai.headers });
const d = data?.data || {};
const status = d.status || 'unknown';
if (status === 'SUCCESS' && d.response?.resultUrls?.length) {
return await sendCallback(callbackUrl, {
jobId, callbackKey, status: 'success',
result: {
prompt,
imageUrl: d.response.resultUrls[0],
thumbnailUrl: d.response.thumbnailUrls?.[0],
}
});
}
await new Promise(r => setTimeout(r, 3000));
} catch (err) {
if (err.response?.status === 429) {
await new Promise(r => setTimeout(r, 5000));
continue;
}
return await sendCallback(callbackUrl, {
jobId, callbackKey, status: 'failed',
result: { error: 'Failed to poll task status.', details: err.message }
});
}
}
await sendCallback(callbackUrl, {
jobId, callbackKey, status: 'failed',
result: { error: 'Task polling timed out after several attempts.' }
});
},
generate: async (jobDetails) => {
const { imageDataUri, prompt, size } = jobDetails;
await ghibai.security.decrypt();
try {
const { data } = await axios.post(
`${ghibai.api.base}${ghibai.api.endpoints.generate}`,
{ filesUrl: [''], files: [imageDataUri], prompt, size, nVariants: 1 },
{ headers: ghibai.headers }
);
const taskId = data?.data?.taskId;
if (!taskId) {
throw new Error('Failed to retrieve Task ID from the generation API.');
}
await ghibai.getTask({ ...jobDetails, taskId });
} catch (err) {
if (err.response?.status === 429) {
await new Promise(r => setTimeout(r, 5000));
return await ghibai.generate(jobDetails);
}
await sendCallback(jobDetails.callbackUrl, {
jobId: jobDetails.jobId, callbackKey: jobDetails.callbackKey, status: 'failed',
result: { error: 'Failed to initiate image generation.', details: err.message }
});
}
}
};
module.exports = async function(req, res) {
const { imageDataUri, prompt, size, jobId, callbackUrl, callbackKey } = req.body;
if (!imageDataUri || !prompt || !jobId || !callbackUrl || !callbackKey) {
return res.status(400).json({ error: 'Missing required parameters for worker.' });
}
res.status(202).json({ message: 'Job accepted by worker and is being processed in the background.' });
ghibai.generate({ imageDataUri, prompt, size, jobId, callbackUrl, callbackKey });
};