const fs = require('fs'); const path = require('path'); const config = require('../config'); const PATH = 'images/edits'; const TIMEOUT_MS = 180000; /** * @param {string} imageUrl data:image/...;base64,... 或本地绝对/相对路径 */ function parseImageInput (imageUrl) { const s = String(imageUrl || '').trim(); const m = s.match(/^data:([^;]+);base64,([\s\S]+)$/i); if (m) { return { buffer: Buffer.from(m[2], 'base64'), filename: 'image.png', mime: (m[1] || 'image/png').split(';')[0].trim() || 'image/png', }; } if (fs.existsSync(s)) { const buf = fs.readFileSync(s); const base = path.basename(s) || 'image.png'; return { buffer: buf, filename: base, mime: 'image/png' }; } throw new Error('img2img: image 需为 data URL 或存在的本地图片路径'); } /** * OpenAI 官方 /v1/images/edits 要求 multipart/form-data(非 JSON) * 字段:prompt, image(文件), n, size;可选 model */ function buildFormData (prompt, imageUrl, modelOverride) { const form = new FormData(); form.append('prompt', String(prompt || '')); form.append('n', '1'); form.append('size', '1024x1024'); const { buffer, filename, mime } = parseImageInput(imageUrl); const blob = new Blob([buffer], { type: mime }); form.append('image', blob, filename.endsWith('.png') || filename.endsWith('.jpg') || filename.endsWith('.webp') ? filename : `${filename}.png`); if (modelOverride && String(modelOverride).trim()) { form.append('model', String(modelOverride).trim()); } return form; } /** @deprecated 仅结构占位;真实请求请用 buildFormData + multipart */ function getBody (prompt, imageUrl) { return { prompt, image: typeof imageUrl === 'string' && imageUrl.length > 80 ? `${imageUrl.slice(0, 40)}…` : imageUrl, n: 1, size: '1024x1024', }; } function getDoubaoBody (prompt, imageUrl, modelOverride) { const model = (modelOverride && String(modelOverride).trim()) || config.DOUBAO_MODEL; return { model, prompt, image: imageUrl, n: 1, size: '1024x1024', }; } module.exports = { path: PATH, getBody, getDoubaoBody, buildFormData, timeoutMs: TIMEOUT_MS, };