/** * Generate text-to-image * Author : Nasirxml * Base : https://freegen.app * Note: untuk img-to-img nya ga dibikin karna hasil nya jelek🗿 */ const WebSocket = require('ws'); const crypto = require('crypto'); const https = require('https'); const http = require('http'); const SIGNER_URL = 'https://prompt-signer.freegen.app'; const GENERATOR_URL = 'https://image-generator.freegen.app'; const WEBSOCKET_URL = 'wss://websocket-bridge.freegen.app/ws'; const STATS_API_URL = 'https://stats.freegen.app'; // Aspect ratios available const RATIOS = { '1:1': { width: 1024, height: 1024 }, '4:3': { width: 1024, height: 768 }, '3:4': { width: 768, height: 1024 }, '16:9': { width: 1024, height: 576 }, '9:16': { width: 576, height: 1024 } }; class FreeGenAPI { constructor() { this.prompt = ''; this.ratio = '4:3'; } async signer(prompt) { return new Promise((resolve, reject) => { const data = JSON.stringify({ prompt }); const req = https.request(`${SIGNER_URL}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(data) } }, (res) => { let body = ''; res.on('data', chunk => body += chunk); res.on('end', () => { try { const json = JSON.parse(body); resolve(json); } catch (e) { reject(new Error('Failed to parse signer response')); } }); }); req.on('error', reject); req.write(data); req.end(); }); } async generator(prompt, ts, sig, ratioId) { return new Promise((resolve, reject) => { const body = JSON.stringify({ prompt, ts, sig, ratio_id: ratioId }); const req = https.request(`${GENERATOR_URL}`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } }, (res) => { let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => { try { const json = JSON.parse(data); resolve(json); } catch (e) { reject(new Error('Failed to parse generator response')); } }); }); req.on('error', reject); req.write(body); req.end(); }); } createAuth(jobId, timestamp) { const message = jobId + timestamp.toString(); const hash = crypto.createHash('sha256').update(message).digest(); const auth = hash.toString('base64').substring(0, 20) + ':' + timestamp; return auth; } async waitForResult(jobId, auth) { return new Promise((resolve, reject) => { const ws = new WebSocket(WEBSOCKET_URL); let timeout = setTimeout(() => { ws.close(); reject(new Error('Timeout waiting for result')); }, 120000); ws.on('open', () => { console.log('[WS] Connected to WebSocket'); // Subscribe to job ws.send(JSON.stringify({ type: 'subscribe', job_id: jobId, auth: auth })); console.log('[WS] Subscribed to job:', jobId); }); ws.on('message', (data) => { try { const msg = JSON.parse(data.toString()); if (msg.type === 'status') { console.log('[WS] Status:', msg.message); } else if (msg.type === 'result') { console.log('[WS] Image generated!'); clearTimeout(timeout); ws.close(); resolve(msg.image_data); } else if (msg.type === 'error') { clearTimeout(timeout); ws.close(); reject(new Error(msg.message || 'Unknown error')); } } catch (e) { console.error('[WS] Failed to parse message:', e); } }); ws.on('error', (err) => { clearTimeout(timeout); reject(err); }); ws.on('close', () => { console.log('[WS] Connection closed'); }); }); } async recordCompletion(jobId, totalTimeMs) { return new Promise((resolve) => { const body = JSON.stringify({ job_id: jobId, total_time_ms: totalTimeMs, timestamp: new Date().toISOString() }); const req = https.request(`${STATS_API_URL}/record-completion`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) } }, (res) => { resolve(); }); req.on('error', () => resolve()); // Ignore errors req.write(body); req.end(); }); } async generate(prompt, ratio = '4:3') { console.log('\n=== FreeGen.app API ==='); console.log('Prompt:', prompt); console.log('Ratio:', ratio); console.log(''); const startTime = Date.now(); try { // Step 1: Get signature from signer console.log('[1/4] Getting signature from signer...'); const { ts, sig } = await this.signer(prompt); console.log('[OK] Signature received:', sig.substring(0, 20) + '...'); // Step 2: Request image generation console.log('\n[2/4] Requesting image generation...'); const jobData = await this.generator(prompt, ts, sig, ratio); console.log('[OK] Job created:', jobData.job_id); if (jobData.status) { console.log('[OK] Status:', jobData.status); } // Step 3: Wait for result via WebSocket console.log('\n[3/4] Connecting to WebSocket for real-time updates...'); const auth = this.createAuth(jobData.job_id, ts); const imageUrl = await this.waitForResult(jobData.job_id, auth); // Step 4: Record completion timing const totalTime = Date.now() - startTime; console.log('\n[4/4] Recording completion timing...'); await this.recordCompletion(jobData.job_id, totalTime); console.log('\n' + '='.repeat(50)); console.log('IMAGE GENERATED!'); console.log('='.repeat(50)); console.log('URL:', imageUrl); console.log('Time:', (totalTime / 1000).toFixed(2) + 's'); console.log('='.repeat(50) + '\n'); return { success: true, imageUrl, jobId: jobData.job_id, time: totalTime }; } catch (error) { console.error('\n[ERROR]', error.message); return { success: false, error: error.message }; } } } // Main execution async function main() { const args = process.argv.slice(2); if (args.length === 0 || args[0] === '--help' || args[0] === '-h') { console.log(` FreeGen.app API - AI Image Generator Usage: node freegen.js "your prompt" [options] Options: --ratio Aspect ratio (1:1, 4:3, 3:4, 16:9, 9:16) --help, -h Show this help message Examples: node freegen.js "a cute cat" node freegen.js "sunset over ocean" --ratio 16:9 node freegen.js "cyberpunk city" --ratio 3:4 `); process.exit(0); } let prompt = ''; let ratio = '4:3'; // Parse arguments for (let i = 0; i < args.length; i++) { if (args[i] === '--ratio' && args[i + 1]) { ratio = args[i + 1]; i++; } else if (!args[i].startsWith('--')) { prompt += (prompt ? ' ' : '') + args[i]; } } if (!prompt) { console.error('Error: Please provide a prompt'); process.exit(1); } if (!RATIOS[ratio]) { console.error('Error: Invalid ratio. Available: 1:1, 4:3, 3:4, 16:9, 9:16'); process.exit(1); } const api = new FreeGenAPI(); const result = await api.generate(prompt, ratio); if (!result.success) { process.exit(1); } } main().catch(console.error); module.exports = FreeGenAPI;