-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathremove-bg.mjs
More file actions
137 lines (113 loc) · 4.11 KB
/
Copy pathremove-bg.mjs
File metadata and controls
137 lines (113 loc) · 4.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env node
import { writeFileSync, readFileSync, existsSync } from 'node:fs';
import { parseArgs } from 'node:util';
import { basename } from 'node:path';
const BASE = process.env.AI_API_BASE_URL;
const KEY = process.env.AI_API_KEY;
if (!BASE || !KEY) {
console.error('Missing AI_API_BASE_URL / AI_API_KEY. Set them in .env (see .env.example).');
process.exit(1);
}
const { values } = parseArgs({
options: {
in: { type: 'string' },
out: { type: 'string' },
debug: { type: 'boolean', default: false },
},
});
if (!values.in || !values.out) {
console.error('Usage: node scripts/remove-bg.mjs --in input.png --out output.png');
process.exit(1);
}
const FILEID_CACHE = '.file-id-cache.json';
async function getFileId(localPath) {
const cache = existsSync(FILEID_CACHE)
? JSON.parse(readFileSync(FILEID_CACHE, 'utf8'))
: {};
if (cache[localPath]) {
console.log(`Cached file_id: ${cache[localPath]}`);
return cache[localPath];
}
console.log(`Uploading ${basename(localPath)}...`);
const blob = new Blob([readFileSync(localPath)], { type: 'image/png' });
const form = new FormData();
form.append('file', blob, basename(localPath));
form.append('purpose', 'vision');
const res = await fetch(`${BASE}/files`, {
method: 'POST',
headers: { Authorization: `Bearer ${KEY}` },
body: form,
});
const json = await res.json();
if (!json.id) throw new Error(`Upload failed: ${JSON.stringify(json)}`);
cache[localPath] = json.id;
writeFileSync(FILEID_CACHE, JSON.stringify(cache, null, 2));
return json.id;
}
async function removeBg() {
const fileId = await getFileId(values.in);
console.log(`Removing background via ideogram-remove-background...`);
const res = await fetch(`${BASE}/images/edit`, {
method: 'POST',
headers: {
Authorization: `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'ideogram-remove-background',
image: fileId,
prompt: 'remove background',
async: true,
}),
});
const json = await res.json();
if (values.debug) {
console.log(`Status: ${res.status}`);
console.log(JSON.stringify(json, null, 2).slice(0, 2000));
}
if (!res.ok) throw new Error(`API error: ${JSON.stringify(json)}`);
// Handle async polling
const jobId = json.id;
if (jobId && json.status !== 'completed') {
console.log(`Job ${jobId} — polling...`);
const maxWait = 120;
const start = Date.now();
while ((Date.now() - start) / 1000 < maxWait) {
await new Promise(r => setTimeout(r, 3000));
const pollRes = await fetch(`${BASE}/images/edits/${jobId}`, {
headers: { Authorization: `Bearer ${KEY}` },
});
const pollJson = await pollRes.json();
process.stdout.write(` [${pollJson.status}]`);
if (pollJson.status === 'failed') throw new Error(`Job failed`);
if (pollJson.status === 'completed') {
console.log(' ✓');
return extractImage(pollJson);
}
}
throw new Error('Timed out');
}
return extractImage(json);
}
function extractImage(json) {
const item = json.data?.[0];
if (item?.b64_json) return Buffer.from(item.b64_json, 'base64');
if (item?.url) return fetchBuf(item.url);
if (json.b64_json) return Buffer.from(json.b64_json, 'base64');
if (json.url) return fetchBuf(json.url);
const out = json.output?.[0];
if (out?.b64_json) return Buffer.from(out.b64_json, 'base64');
if (out?.url) return fetchBuf(out.url);
throw new Error(`No image in response: ${JSON.stringify(json).slice(0, 500)}`);
}
async function fetchBuf(url) {
return Buffer.from(await (await fetch(url)).arrayBuffer());
}
try {
const buffer = await removeBg();
writeFileSync(values.out, buffer);
console.log(`\nSaved: ${values.out}`);
} catch (err) {
console.error(`\nFailed: ${err.message}`);
process.exit(1);
}