From 65af6c92fcf279b198f72cfa48e25f9cfd3fae21 Mon Sep 17 00:00:00 2001 From: sion123 <450702724@qq.com> Date: Sun, 17 May 2026 16:50:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(video-from-script):=20=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E7=94=9F=E6=88=90=E9=98=B6=E6=AE=B5=E5=A2=9E=E5=8A=A0=E9=87=8D?= =?UTF-8?q?=E5=A4=8D=E4=BB=BB=E5=8A=A1=E6=A3=80=E6=B5=8B=E4=B8=8E=E5=8E=9F?= =?UTF-8?q?=E5=AD=90=E7=8A=B6=E6=80=81=E6=A0=87=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在视频生成流水线中添加两项关键改进: 1. 在 `cmdNext` 中提前将条目状态标记为 `processing` 并持久化,防止同一行被多个并行进程重复取出处理 2. 在 `phaseVideos` 中增加磁盘兜底检测:对无 `video` 字段的条目,根据 `id` 扫描本地视频目录,若发现已有视频文件则恢复引用并跳过生成 3. 优化状态过滤逻辑:`done` 状态且已有视频文件的条目明确跳过并输出原因,减少冗余日志 --- .../scripts/batch-pipeline.js | 5 +++++ .../scripts/lib/phase-videos.js | 20 ++++++++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.claude/skills/video-from-script/scripts/batch-pipeline.js b/.claude/skills/video-from-script/scripts/batch-pipeline.js index a41658f..0ae607d 100644 --- a/.claude/skills/video-from-script/scripts/batch-pipeline.js +++ b/.claude/skills/video-from-script/scripts/batch-pipeline.js @@ -304,6 +304,11 @@ function cmdNext(args) { return } + // 原子标记为 processing,防止同一行被重复取出 + item.status = 'processing' + batch.stats = calcStats(batch.items) + writeJson(manifestPath, batch) + const result = { done: false, row: item.row, diff --git a/.claude/skills/video-from-script/scripts/lib/phase-videos.js b/.claude/skills/video-from-script/scripts/lib/phase-videos.js index 8222520..e0f5f6c 100644 --- a/.claude/skills/video-from-script/scripts/lib/phase-videos.js +++ b/.claude/skills/video-from-script/scripts/lib/phase-videos.js @@ -43,6 +43,8 @@ async function phaseVideos(manifest, manifestPath, options) { const videoCandidates = manifest.items.filter(it => { if (it.confirmed === false) return false if (!it.url || !it.videoPrompt) return false + // 已有视频(本地文件或远程 URL)且状态为 done → 跳过,避免重复生成 + if (it.status === 'done' && (it.video || it.videoUrl)) return false return ['done', 'pending', 'failed'].includes(it.status) }) @@ -54,7 +56,9 @@ async function phaseVideos(manifest, manifestPath, options) { if (it.confirmed === false) reasons.push("confirmed=false") if (!it.url) reasons.push("缺少 url(图片未上传)") if (!it.videoPrompt) reasons.push("缺少 videoPrompt") - if (it.confirmed !== false && it.url && it.videoPrompt && !["done","pending","failed"].includes(it.status)) { + if (it.status === 'done' && (it.video || it.videoUrl)) { + reasons.push("视频已生成,已跳过") + } else if (!["done","pending","failed"].includes(it.status)) { reasons.push("status=" + (it.status || "undefined") + "(不在 done/pending/failed 中)") } console.log(" - item", it.id || manifest.items.indexOf(it), ":", reasons.length > 0 ? reasons.join(", ") : "已满足全部条件(不应在此)") @@ -66,6 +70,20 @@ async function phaseVideos(manifest, manifestPath, options) { const items = [] for (const it of videoCandidates) { + // 磁盘兜底:本地视频文件已存在则恢复引用并跳过 + if (!it.video && it.id) { + const fs = require('fs') + const existingVideos = fs.readdirSync(videosDir).filter(f => + f.includes('_item' + it.id + '_') || f.includes('_item' + it.id + '.') + ) + if (existingVideos.length > 0) { + it.video = 'videos/' + existingVideos[existingVideos.length - 1] + it.status = 'done' + delete it.videoTaskId + log('videos', ` item ${it.id} 发现已有视频文件 ${it.video},跳过生成`) + continue + } + } if (it.video || it.videoUrl) { if (it.status === 'done') continue delete it.video