/* * pixpunk-remove-bg * Base: https://pixpunk.ai * Sumber: https://whatsapp.com/channel/0029VbB4Kw8EFeXfeExaXc3Q * Note: remove background image * ESM | axios + form-data * * Usage: * node pixpunk.js [output-path] */ import axios from 'axios'; import FormData from 'form-data'; import fs from 'fs'; import path from 'path'; import { Readable } from 'stream'; const API_URL = 'https://api.pixpunk.ai/api/remove-background'; const HEADERS = { 'Origin': 'https://pixpunk.ai', 'Referer': 'https://pixpunk.ai/', 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:140.0) Gecko/20100101 Firefox/140.0', 'Accept': 'application/json', }; function guessContentType(filename) { const map = { '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png', '.webp': 'image/webp', '.gif': 'image/gif' }; return map[path.extname(filename).toLowerCase()] || 'image/jpeg'; } function resolveOutputPath(inputName, outputPath) { if (outputPath) return outputPath; const base = path.basename(inputName, path.extname(inputName)); return `${base}_no_bg.png`; } async function fetchImageFromUrl(url) { const res = await axios.get(url, { responseType: 'arraybuffer', headers: { 'User-Agent': HEADERS['User-Agent'] } }); const contentType = res.headers['content-type'] || 'image/jpeg'; const ext = contentType.split('/')[1]?.split(';')[0] || 'jpg'; const urlPath = new URL(url).pathname; const basename = path.basename(urlPath) || `image.${ext}`; const filename = basename.includes('.') ? basename : `${basename}.${ext}`; return { buffer: Buffer.from(res.data), contentType, filename }; } async function callApi(form) { const res = await axios.post(API_URL, form, { headers: { ...HEADERS, ...form.getHeaders() }, responseType: 'arraybuffer', maxBodyLength: Infinity, maxContentLength: Infinity, }); if (res.status !== 200) throw new Error(`API error: ${res.status} ${res.statusText}`); return Buffer.from(res.data); } async function removeBackground(input, outputPath = '') { let resultBuffer, outPath; if (/^https?:\/\//i.test(input)) { const { buffer, contentType, filename } = await fetchImageFromUrl(input); const form = new FormData(); form.append('image', Readable.from(buffer), { filename, contentType, knownLength: buffer.length }); resultBuffer = await callApi(form); outPath = resolveOutputPath(filename, outputPath); } else { if (!fs.existsSync(input)) throw new Error(`File tidak ditemukan: ${input}`); const filename = path.basename(input); const form = new FormData(); form.append('image', fs.createReadStream(input), { filename, contentType: guessContentType(filename) }); resultBuffer = await callApi(form); outPath = resolveOutputPath(input, outputPath); } const dir = path.dirname(outPath); if (dir && dir !== '.' && !fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); fs.writeFileSync(outPath, resultBuffer); console.log(JSON.stringify({ status: 'success', input, output: outPath, size_bytes: resultBuffer.length, }, null, 2)); return outPath; } // ── Run ───────────────────────────────────────────────────── const [,, input, output] = process.argv; if (!input) { console.log(JSON.stringify({ status: 'error', message: 'Usage: node index.js [output-path]' }, null, 2)); process.exit(1); } removeBackground(input, output).catch(err => { console.log(JSON.stringify({ status: 'error', message: err.message }, null, 2)); process.exit(1); });