const axios = require('axios'); const fs = require('fs'); const path = require('path'); require('dotenv').config(); // 配置 const config = { apiKey: process.env.API_KEY, apiBaseUrl: process.env.API_BASE_URL || 'https://api.wuyinkeji.com', generateApi: process.env.GENERATE_API || '/api/img/nanoBanana-pro', queryApi: process.env.QUERY_API || '/api/img/drawDetail', sourceImageUrl: process.env.SOURCE_IMAGE_URL, imageSize: process.env.IMAGE_SIZE || '2K', aspectRatio: process.env.ASPECT_RATIO || '1:1', queryInterval: parseInt(process.env.QUERY_INTERVAL) || 5000, maxQueryCount: parseInt(process.env.MAX_QUERY_COUNT) || 60, outputDir: path.join(__dirname, 'img_2') }; // 确保输出目录存在 if (!fs.existsSync(config.outputDir)) { fs.mkdirSync(config.outputDir, { recursive: true }); } // 亚马逊主图prompt模板 const MAIN_IMAGE_PROMPTS = [ { name: 'main_image_white_bg', prompt: 'Create a professional Amazon product main image with pure white background. The product should be centered, well-lit, showing all key features clearly. High quality commercial photography style, clean and minimalist.', aspectRatio: '1:1' }, { name: 'main_image_lifestyle', prompt: 'Create a lifestyle Amazon product main image showing the product in a natural, appealing setting. Professional product photography, bright and inviting atmosphere.', aspectRatio: '1:1' } ]; // 亚马逊产品图prompt模板 const PRODUCT_IMAGE_PROMPTS = [ { name: 'product_detail_1', prompt: 'Create a detailed Amazon product image showing close-up details and features. Professional product photography, white background, high resolution.', aspectRatio: '1:1' }, { name: 'product_detail_2', prompt: 'Create an Amazon product image showing different angles and perspectives. Clean white background, professional lighting, showcasing product quality.', aspectRatio: '1:1' }, { name: 'product_in_use', prompt: 'Create an Amazon product image showing the product in use scenario. Natural setting, professional photography, highlighting product benefits.', aspectRatio: '4:3' }, { name: 'product_features', prompt: 'Create an Amazon product image highlighting key features and specifications. Clean layout, professional design, easy to understand.', aspectRatio: '16:9' } ]; /** * 调用生图API */ async function generateImage(prompt, imageConfig) { try { const url = `${config.apiBaseUrl}${config.generateApi}`; const requestData = { prompt: prompt.prompt, aspectRatio: imageConfig.aspectRatio || prompt.aspectRatio || config.aspectRatio, imageSize: imageConfig.imageSize || config.imageSize }; // 如果提供了源图片URL,添加到请求中 if (config.sourceImageUrl) { requestData.img_url = config.sourceImageUrl; } console.log(`\n正在生成图片: ${prompt.name}`); console.log(`Prompt: ${prompt.prompt.substring(0, 100)}...`); console.log(`请求URL: ${url}`); const response = await axios.post(url, requestData, { headers: { 'Content-Type': 'application/json;charset:utf-8;', 'Authorization': config.apiKey }, timeout: 30000 }); if (response.data && response.data.code === 200) { const taskId = response.data.data?.id || response.data.data?.taskId || response.data.taskId; console.log(`✅ 任务提交成功,任务ID: ${taskId}`); return { success: true, taskId, name: prompt.name }; } else { console.error(`❌ 任务提交失败:`, response.data); return { success: false, error: response.data }; } } catch (error) { console.error(`❌ 生成图片时出错:`, error.message); if (error.response) { console.error(`响应数据:`, error.response.data); } return { success: false, error: error.message }; } } /** * 查询图片生成结果 */ async function queryImageResult(taskId, imageName) { let queryCount = 0; return new Promise((resolve, reject) => { const queryInterval = setInterval(async () => { queryCount++; if (queryCount > config.maxQueryCount) { clearInterval(queryInterval); reject(new Error(`查询超时,已查询${queryCount}次`)); return; } try { // 根据API文档: https://api.wuyinkeji.com/doc/9 // 接口地址: https://api.wuyinkeji.com/api/img/drawDetail // 请求方式: GET // 请求参数: id (必填, int类型) - 图片ID // 返回格式: data.status (0:排队中,1:生成中,2:成功,3:失败) // data.image_url (生成的图片地址) const queryUrl = `${config.apiBaseUrl}${config.queryApi}?id=${taskId}`; console.log(`[${queryCount}/${config.maxQueryCount}] 查询任务状态: ID=${taskId}`); const response = await axios.get(queryUrl, { headers: { 'Content-Type': 'application/json;charset:utf-8;', 'Authorization': config.apiKey }, timeout: 10000 }); const data = response.data; // 根据API文档响应格式: // code: 状态码 // msg: 状态信息 // data.status: 0:排队中,1:生成中,2:成功,3:失败 // data.image_url: 生成的图片地址 if (data.code === 200 && data.data) { const status = data.data.status; const imageUrl = data.data.image_url; // status: 2 表示成功 if (status === 2) { if (imageUrl) { clearInterval(queryInterval); console.log(`✅ 图片生成成功: ${imageName}`); console.log(`图片URL: ${imageUrl}`); resolve({ imageUrl, taskId, imageName }); } else { console.log(`⏳ 图片生成成功但URL未返回,继续等待...`); } } // status: 3 表示失败 else if (status === 3) { clearInterval(queryInterval); reject(new Error(`图片生成失败: ${data.msg || data.data.msg || '未知错误'}`)); } // status: 0 排队中, 1 生成中 else { const statusText = status === 0 ? '排队中' : status === 1 ? '生成中' : `未知状态${status}`; console.log(`⏳ ${statusText}...`); } } else if (data.code !== 200) { // API返回错误 console.log(`⚠️ 查询返回错误: ${data.msg || '未知错误'} (code: ${data.code})`); // 继续重试,不立即失败 } else { console.log(`⏳ 等待中... (响应: ${data.msg || '处理中'})`); } } catch (error) { if (error.response && error.response.status === 404) { // 任务可能还在处理中 console.log(`⏳ 任务处理中,继续等待...`); } else { console.error(`查询时出错:`, error.message); // 不立即失败,继续重试 } } }, config.queryInterval); }); } /** * 下载并保存图片 */ async function downloadAndSaveImage(imageUrl, imageName) { try { console.log(`\n开始下载图片: ${imageUrl}`); const response = await axios({ url: imageUrl, method: 'GET', responseType: 'stream', timeout: 60000 }); // 确定文件扩展名 const contentType = response.headers['content-type']; let extension = '.jpg'; if (contentType) { if (contentType.includes('png')) extension = '.png'; else if (contentType.includes('webp')) extension = '.webp'; } const filename = `${imageName}${extension}`; const filepath = path.join(config.outputDir, filename); const writer = fs.createWriteStream(filepath); response.data.pipe(writer); return new Promise((resolve, reject) => { writer.on('finish', () => { console.log(`✅ 图片已保存: ${filepath}`); resolve(filepath); }); writer.on('error', (error) => { console.error(`❌ 保存图片失败:`, error); reject(error); }); }); } catch (error) { console.error(`❌ 下载图片失败:`, error.message); throw error; } } /** * 处理单个图片生成任务 */ async function processImageGeneration(prompt) { try { // 1. 提交生成任务 const generateResult = await generateImage(prompt, { aspectRatio: prompt.aspectRatio, imageSize: config.imageSize }); if (!generateResult.success) { throw new Error('任务提交失败'); } // 2. 查询生成结果 const queryResult = await queryImageResult(generateResult.taskId, prompt.name); // 3. 下载并保存图片 await downloadAndSaveImage(queryResult.imageUrl, queryResult.imageName); return { success: true, name: prompt.name }; } catch (error) { console.error(`处理图片 ${prompt.name} 时出错:`, error.message); return { success: false, name: prompt.name, error: error.message }; } } /** * 主工作流 */ async function main() { console.log('🚀 开始亚马逊产品图生成工作流...\n'); console.log('配置信息:'); console.log(`- API地址: ${config.apiBaseUrl}`); console.log(`- 源图片: ${config.sourceImageUrl || '使用本地图片'}`); console.log(`- 输出目录: ${config.outputDir}`); console.log(`- 图片尺寸: ${config.imageSize}`); console.log(`- 查询间隔: ${config.queryInterval}ms\n`); if (!config.apiKey) { console.error('❌ 错误: 未设置API_KEY,请在.env文件中配置'); process.exit(1); } if (!config.sourceImageUrl) { console.warn('⚠️ 警告: 未设置SOURCE_IMAGE_URL'); console.warn('提示: 请将P1191464.JPG上传到图床服务,然后在.env中设置SOURCE_IMAGE_URL'); console.warn('或者修改代码以支持直接上传本地图片\n'); } const allPrompts = [...MAIN_IMAGE_PROMPTS, ...PRODUCT_IMAGE_PROMPTS]; const results = []; // 顺序处理每个图片生成任务 for (const prompt of allPrompts) { const result = await processImageGeneration(prompt); results.push(result); // 在任务之间稍作延迟,避免请求过快 if (allPrompts.indexOf(prompt) < allPrompts.length - 1) { console.log('\n等待3秒后处理下一个任务...\n'); await new Promise(resolve => setTimeout(resolve, 3000)); } } // 输出总结 console.log('\n' + '='.repeat(50)); console.log('📊 工作流执行总结:'); console.log('='.repeat(50)); const successCount = results.filter(r => r.success).length; const failCount = results.filter(r => !r.success).length; console.log(`✅ 成功: ${successCount}/${results.length}`); console.log(`❌ 失败: ${failCount}/${results.length}`); if (failCount > 0) { console.log('\n失败的任务:'); results.filter(r => !r.success).forEach(r => { console.log(` - ${r.name}: ${r.error}`); }); } console.log('='.repeat(50)); } // 运行主工作流 if (require.main === module) { main().catch(error => { console.error('❌ 工作流执行失败:', error); process.exit(1); }); } module.exports = { generateImage, queryImageResult, downloadAndSaveImage, processImageGeneration };