Carrier workflow + 4K upscale scripts

This commit is contained in:
tony
2026-01-07 10:12:38 +08:00
parent f5cb1042ae
commit 578a360894
6 changed files with 1185 additions and 0 deletions

161
upscale-to-4k-ai.js Normal file
View File

@@ -0,0 +1,161 @@
/**
* 4K AI 升级脚本 (1:1 还原版)
* 逻辑:
* 1. 读取 v2 版本已生成的图片(客户已对齐的版本)
* 2. 将其作为 img_url (参考图) 传给 AI
* 3. 使用原 prompt并开启 imageSize: '4K'
* 4. 这样 AI 会在 v2 的基础上进行高清化重绘,保持构图 1:1 还原
*/
require('dotenv').config();
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
const imageProcessor = require('./lib/image-processor');
// ============================================================
// 配置
// ============================================================
const API_KEY = 'G9rXx3Ag2Xfa7Gs8zou6t6HqeZ';
const API_BASE = 'https://api.wuyinkeji.com/api';
const r2Client = new S3Client({
region: 'auto',
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
},
forcePathStyle: true,
});
// ============================================================
// 工具函数
// ============================================================
async function uploadToR2(buffer, filename) {
const fileName = `upscale-4k-${Date.now()}-${filename}`;
await r2Client.send(new PutObjectCommand({
Bucket: process.env.R2_BUCKET_NAME || 'ai-flow',
Key: fileName,
Body: buffer,
ContentType: 'image/jpeg',
}));
return process.env.R2_PUBLIC_DOMAIN
? `${process.env.R2_PUBLIC_DOMAIN}/${fileName}`
: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com/${process.env.R2_BUCKET_NAME}/${fileName}`;
}
async function submitUpscaleTask(prompt, refImageUrl, aspectRatio) {
const payload = {
key: API_KEY,
prompt: prompt, // 使用原 prompt 保持语义一致
img_url: refImageUrl, // 使用已生成的图作为底图
aspectRatio: aspectRatio,
imageSize: '4K' // 升级到 4K
};
const response = await axios.post(`${API_BASE}/img/nanoBanana-pro`, payload, { timeout: 60000 });
return response.data.data?.id || response.data.id;
}
async function pollImageResult(taskId) {
let attempts = 0;
const maxAttempts = 120; // 4K 稍微给多点时间
while (attempts < maxAttempts) {
try {
const response = await axios.get(`${API_BASE}/img/drawDetail`, {
params: { key: API_KEY, id: taskId },
timeout: 10000
});
const data = response.data.data;
if (data && data.status === 2 && data.image_url) return data.image_url;
if (data && data.status === 3) throw new Error('Upscale failed: ' + (data.fail_reason || 'Unknown'));
process.stdout.write('.');
await new Promise(r => setTimeout(r, 2000));
attempts++;
} catch (error) {
if (error.message.includes('Upscale failed')) throw error;
process.stdout.write('x');
await new Promise(r => setTimeout(r, 3000));
attempts++;
}
}
throw new Error('Timeout');
}
async function downloadImage(url) {
const response = await axios.get(url, { responseType: 'arraybuffer', timeout: 30000 });
return Buffer.from(response.data);
}
// ============================================================
// 主流程
// ============================================================
async function main() {
const v2Dir = path.join(__dirname, 'output_carrier_v2_2025-12-27');
const outputDir = path.join(__dirname, `output_carrier_v3_4K_Upscale_${new Date().toISOString().slice(0, 19).replace(/:/g, '-')}`);
if (!fs.existsSync(v2Dir)) {
console.error('❌ 找不到 v2 目录:', v2Dir);
return;
}
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
console.log('🚀 开始 4K AI 升级 (基于 v2 已对齐版本)...');
console.log('📂 输入目录:', v2Dir);
console.log('📂 输出目录:', outputDir);
const files = fs.readdirSync(v2Dir).filter(f => f.endsWith('.jpg'));
for (const file of files) {
const id = file.replace('.jpg', '');
const promptFile = path.join(v2Dir, `${id}_prompt.txt`);
if (!fs.existsSync(promptFile)) {
console.log(`⏭️ 跳过 ${file} (找不到对应的 prompt 文件)`);
continue;
}
console.log(`\n🎨 升级 [${id}]...`);
try {
const prompt = fs.readFileSync(promptFile, 'utf-8');
const imgBuffer = fs.readFileSync(path.join(v2Dir, file));
const aspectRatio = id.startsWith('APlus') ? '3:2' : '1:1';
// 1. 上传 v2 图片到 R2
const imgUrl = await uploadToR2(imgBuffer, file);
// 2. 提交 4K 任务
const taskId = await submitUpscaleTask(prompt, imgUrl, aspectRatio);
console.log(` Task ID: ${taskId}`);
// 3. 轮询结果
const resultUrl = await pollImageResult(taskId);
console.log('\n ✓ 4K 生成完成');
// 4. 下载并保存
const resultBuffer = await downloadImage(resultUrl);
await imageProcessor.saveImage(resultBuffer, path.join(outputDir, file), 'jpeg', 95);
// 拷贝 prompt 文件
fs.copyFileSync(promptFile, path.join(outputDir, `${id}_prompt.txt`));
console.log(`${file} 已升级到 4K`);
} catch (e) {
console.error(`\n${id} 失败:`, e.message);
}
}
console.log('\n✨ 全部升级任务完成!');
}
main().catch(console.error);