const axios = require('axios'); const fs = require('fs'); const path = require('path'); const { promisify } = require('util'); const stream = require('stream'); const pipeline = promisify(stream.pipeline); const crypto = require('crypto'); const jobsPath = path.join(__dirname, '..', 'data', 'hdr_jobs.json'); const ensureDataDir = () => { const dataDir = path.join(__dirname, '..', 'data'); if (!fs.existsSync(dataDir)) { fs.mkdirSync(dataDir, { recursive: true }); } if (!fs.existsSync(jobsPath)) { fs.writeFileSync(jobsPath, JSON.stringify({})); } }; const saveJobs = (jobs) => { ensureDataDir(); fs.writeFileSync(jobsPath, JSON.stringify(jobs, null, 2)); }; const loadJobs = () => { ensureDataDir(); try { return JSON.parse(fs.readFileSync(jobsPath, 'utf8')); } catch { return {}; } }; const genid = () => { const hex = crypto.randomBytes(16).toString('hex'); return `${hex.substring(0,8)}-${hex.substring(8,12)}-${hex.substring(12,16)}-${hex.substring(16,20)}-${hex.substring(20,32)}`; }; const genpass = () => { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let pass = ''; for (let i = 0; i < 12; i++) { pass += chars.charAt(Math.floor(Math.random() * chars.length)); } return pass; }; const solvets = async (retries = 5) => { const apiKey = process.env.KEYGUA || 'HerzaTXT'; const url = 'https://supawork.ai/id/nano-banana'; const siteKey = '0x4AAAAAACBjrLhJyEE6mq1c'; for (let attempt = 1; attempt <= retries; attempt++) { try { console.log(`[Turnstile] Attempt ${attempt}/${retries}`); const res = await axios.get('https://anabot.my.id/api/tools/bypass', { params: { url: url, siteKey: siteKey, type: 'turnstile-min', apikey: apiKey }, headers: { 'accept': '*/*', 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36' }, timeout: 90000 }); if (!res.data?.success || !res.data?.data?.result?.token) { throw new Error('Token not found in response'); } console.log('[Turnstile] Token obtained successfully'); return res.data.data.result.token; } catch (error) { console.error(`[Turnstile] Error on attempt ${attempt}:`, error.message); if (attempt === retries) { throw new Error(`Turnstile solver failed after ${retries} attempts: ${error.message}`); } const waitTime = Math.min(2000 * Math.pow(2, attempt - 1), 15000); await new Promise(r => setTimeout(r, waitTime)); } } }; const getct = async (tt, iid) => { const headers = { 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Mobile Safari/537.36', 'Accept': 'application/json', 'Referer': 'https://supawork.ai/nano-banana', 'Origin': 'https://supawork.ai' }; if (tt) headers['X-Auth-Challenge'] = tt; if (iid) headers['X-Identity-Id'] = iid; const res = await axios.get('https://supawork.ai/supawork/headshot/api/sys/challenge/token', { headers }); if (res.data?.code !== 100000) throw new Error(`Challenge token failed: ${res.data?.message}`); return res.data.data.challenge_token; }; const createmail = async () => { const res = await axios.post('https://api.internal.temp-mail.io/api/v3/email/new', { min_name_length: 10, max_name_length: 10 }, { headers: { 'Content-Type': 'application/json', 'Application-Name': 'web', 'Application-Version': '4.0.0', 'X-CORS-Header': 'iaWg3pchvFx48fY' } }); return res.data; }; const getcode = async (email) => { for (let i = 0; i < 15; i++) { await new Promise(r => setTimeout(r, 3000)); const res = await axios.get(`https://api.internal.temp-mail.io/api/v3/email/${email}/messages`, { headers: { 'Content-Type': 'application/json', 'Application-Name': 'web', 'Application-Version': '4.0.0', 'X-CORS-Header': 'iaWg3pchvFx48fY' } }); if (res.data.length > 0) { const match = res.data[0].body_text.match(/\d{4}/); if (match) { return match[0]; } } } throw new Error('Code not found'); }; const regacc = async (email, pass, ct, iid) => { const payload = { email, password: pass, register_code: '', credential: '' }; const res = await axios.post('https://supawork.ai/supawork/api/user/register', payload, { headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36', 'Origin': 'https://supawork.ai', 'Referer': 'https://supawork.ai/nano-banana', 'X-Auth-Challenge': ct, 'X-Identity-Id': iid } }); if (res.data?.code !== 100000) throw new Error(`Register failed: ${res.data?.message}`); return res.data.data.credential; }; const verifyacc = async (email, pass, code, cred, ct, iid) => { const payload = { email, password: pass, register_code: code, credential: cred, route_path: '/nano-banana', country: 'ID' }; const res = await axios.post('https://supawork.ai/supawork/api/user/register/code/verify', payload, { headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36', 'Origin': 'https://supawork.ai', 'Referer': 'https://supawork.ai/nano-banana', 'X-Auth-Challenge': ct, 'X-Identity-Id': iid } }); if (res.data?.code !== 100000) throw new Error(`Verify failed: ${res.data?.message}`); return res.data; }; const loginacc = async (email, pass, ct, iid) => { const payload = { email, password: pass }; const res = await axios.post('https://supawork.ai/supawork/api/user/login/password', payload, { headers: { 'Content-Type': 'application/json', 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36', 'Origin': 'https://supawork.ai', 'Referer': 'https://supawork.ai/nano-banana', 'X-Auth-Challenge': ct, 'X-Identity-Id': iid } }); if (res.data?.code !== 100000) throw new Error(`Login failed: ${res.data?.message}`); return res.data.data; }; const dlfile = async (url, fpath) => { const res = await axios({ url, method: 'GET', responseType: 'stream' }); await pipeline(res.data, fs.createWriteStream(fpath)); }; const getuptoken = async (token, cnt, iid) => { const res = await axios.get(`https://supawork.ai/supawork/headshot/api/sys/oss/token?f_suffix=png&get_num=${cnt}&unsafe=1`, { headers: { 'Authorization': token, 'X-Identity-Id': iid, 'X-Auth-Challenge': '', 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36' } }); return res.data.data; }; const uploadimg = async (url, ipath) => { const buf = fs.readFileSync(ipath); await axios.put(url, buf, { headers: { 'content-type': 'application/x-www-form-urlencoded' } }); }; const creategen = async (token, iid, urls, prompt, ct) => { const payload = { identity_id: iid, aigc_app_code: 'image_to_image_generator', model_code: 'google_nano_banana', custom_prompt: prompt, aspect_ratio: 'match_input_image', image_urls: urls, currency_type: 'gold' }; const res = await axios.post('https://supawork.ai/supawork/headshot/api/media/image/generator', payload, { headers: { 'Authorization': token, 'Content-Type': 'application/json', 'X-Auth-Challenge': ct, 'X-Identity-Id': iid, 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36', 'Origin': 'https://supawork.ai', 'Referer': 'https://supawork.ai/nano-banana' } }); if (res.data?.code !== 100000) { throw new Error(`Generator failed: ${res.data?.message || 'Unknown error'}`); } return res.data.data; }; const createHDRJob = async (img) => { console.log('[Start] Creating HDR job'); const jobId = genid(); const jobs = loadJobs(); jobs[jobId] = { id: jobId, status: 'initializing', stage: 'setup', created_at: new Date().toISOString() }; saveJobs(jobs); const tdir = path.join(__dirname, '..', 'temp'); if (!fs.existsSync(tdir)) fs.mkdirSync(tdir, { recursive: true }); const prompt = "Enhance the portrait while strictly preserving the subject's identity with accurate facial geometry. Do not change their expression or face shape. Only allow subtle feature cleanup without altering who they are. Keep the exact same background from the reference image. No replacements, no changes, no new objects, no layout shifts. The environment must look identical. The image must be recreated as if it was shot on a Sony A1, using an 85mm f1.4 lens, at f1.6, ISO 100, 1/200 shutter speed, cinematic shallow depth of field, perfect facial focus, and an editorial-neutral color profile. This Sony A1 + 85mm f1.4 setup is mandatory. The final image must clearly look like premium full-frame Sony A1 quality. Lighting must match the exact direction, angle, and mood of the reference photo. Upgrade the lighting into a cinematic, subject-focused style: soft directional light, warm highlights, cool shadows, deeper contrast, expanded dynamic range, micro-contrast boost, smooth gradations, and zero harsh shadows. Maintain neutral premium color tone, cinematic contrast curve, natural saturation, real skin texture (not plastic), and subtle film grain. No fake glow, no runway lighting, no oversmoothing. Render in 4K resolution, 10-bit color, cinematic editorial style, premium clarity, portrait crop, and keep the original environmental vibe untouched. Re-render the subject with improved realism, depth, texture, and lighting while keeping identity and background fully preserved. NEGATIVE INSTRUCTIONS: No new background. No background change. No overly dramatic lighting. No face morphing. No fake glow. No flat lighting. No over-smooth skin."; (async () => { try { const jobs = loadJobs(); jobs[jobId].stage = 'solving_captcha'; saveJobs(jobs); const tt = await solvets(); jobs[jobId].stage = 'generating_identity'; saveJobs(jobs); const iid = genid(); jobs[jobId].stage = 'getting_challenge_token'; saveJobs(jobs); const ct = await getct(tt, iid); jobs[jobId].stage = 'creating_email'; saveJobs(jobs); const edata = await createmail(); const email = edata.email; const pass = genpass(); jobs[jobId].stage = 'registering_account'; saveJobs(jobs); const cred = await regacc(email, pass, ct, iid); jobs[jobId].stage = 'waiting_verification_code'; saveJobs(jobs); const code = await getcode(email); jobs[jobId].stage = 'verifying_account'; saveJobs(jobs); await verifyacc(email, pass, code, cred, ct, iid); jobs[jobId].stage = 'logging_in'; saveJobs(jobs); const ldata = await loginacc(email, pass, ct, iid); jobs[jobId].stage = 'uploading_image'; saveJobs(jobs); const tpath = path.join(tdir, `img_${Date.now()}.png`); await dlfile(img, tpath); const tokens = await getuptoken(ldata.token, 1, iid); await uploadimg(tokens[0].put, tpath); fs.unlinkSync(tpath); jobs[jobId].stage = 'creating_generation_task'; saveJobs(jobs); await creategen(ldata.token, iid, [tokens[0].get], prompt, ct); jobs[jobId].token = ldata.token; jobs[jobId].identityId = iid; jobs[jobId].email = email; jobs[jobId].password = pass; jobs[jobId].status = 'processing'; jobs[jobId].stage = 'waiting_generation'; saveJobs(jobs); console.log(`[Job ${jobId}] Setup completed, processing started`); } catch (error) { console.error(`[Job ${jobId}] Setup error:`, error.message); const jobs = loadJobs(); jobs[jobId].status = 'failed'; jobs[jobId].error = error.message; jobs[jobId].failed_at = new Date().toISOString(); saveJobs(jobs); } })(); return jobId; }; const handler = async (req, res) => { try { const { image, key } = req.query; if (!image) return res.status(400).json({ author: "Herza", success: false, error: 'image parameter required' }); if (!key) return res.status(400).json({ author: "Herza", success: false, error: 'key parameter required' }); const jobId = await createHDRJob(image); return res.json({ author: "Herza", success: true, message: "Job created successfully. Use /api/AI/cekhdr?id={job_id} to check status", data: { job_id: jobId, status: "initializing", check_url: `/api/AI/cekhdr?id=${jobId}` } }); } catch (error) { console.error('[Error]', error.message); res.status(500).json({ author: "Herza", success: false, error: error.message, timestamp: new Date().toISOString() }); } }; module.exports = { name: 'Image HDR', description: 'Create HDR enhancement job with Sony A1 quality', type: 'GET', routes: ['api/AI/imghdr'], tags: ['AI', 'tools', 'image'], parameters: ['image', 'key'], limit: 3, enabled: true, main: ['AI', 'tools'], handler };