feat(video-from-script): 批量生产元数据增强 — 选题/转发文案/草稿命名/导出/草稿箱改名

- batch-pipeline.js: 新增 mark 元数据字段(topicA/B, draftName, forwardCopy, hashtags)
- batch-pipeline.js: 新增 export 命令导出 CSV/XLSX 最终表格
- batch-pipeline.js: 新增 rename-drafts 命令批量重命名剪映草稿(Mac 直接 mv 文件夹)
- batch-pipeline.js: 完善 displayTitle 向后兼容旧 topic 字段
- lib/phase-tts: 增强 TTS 生成稳定性
- lib/phase-videos: 视频生成优化
- lib/video-poll-utils: 提取轮询重试共享工具
- CLAUDE.md: 补充批量生产选题/转发文案/草稿命名/导出/草稿箱改名文档
- 执黑先行 account.json: 配置更新

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
lc
2026-05-14 23:16:12 +08:00
parent 2449fbabdf
commit 8787d369d3
9 changed files with 617 additions and 49 deletions

View File

@@ -5,6 +5,11 @@
* 两层重试:轮询级(同一 taskId处理网络瞬断→ 任务级(创建新 task + 优化提示词)
*/
const path = require('path')
const fs = require('fs')
const https = require('https')
const http = require('http')
const TRANSIENT_RE = /timeout|ECONNRESET|ETIMEDOUT|network|socket/i
const POLL_RETRIES = 2 // 同一 task 轮询重试次数
@@ -15,6 +20,26 @@ function isTransientError(err) {
return TRANSIENT_RE.test(err.message || '')
}
async function download(url, outputPath) {
const protocol = url.startsWith('https') ? https : http
return new Promise((resolve, reject) => {
const file = fs.createWriteStream(outputPath)
protocol.get(url, (response) => {
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
file.close()
fs.unlinkSync(outputPath)
return download(response.headers.location, outputPath).then(resolve).catch(reject)
}
response.pipe(file)
file.on('finish', () => { file.close(); resolve(outputPath) })
}).on('error', (err) => {
file.close()
if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath)
reject(err)
})
})
}
/**
* 创建 pollWithRetry 函数
*