diff --git a/docs/source-analysis.html b/docs/source-analysis.html index baadc11..90d9fa8 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -569,13 +569,17 @@

业务管线

-

当前产品方向已收窄为“信息流广告快速复刻”:主界面左侧是素材输入列,右侧是信息流复刻工作表。用户粘贴 TK 链接或上传视频后点击“开始分析”,系统自动下载源视频;下载完成后并行启动音频文案路和视频视觉路。音频文案路提取原音频文案/字幕,分析讲话人、语速节奏、背景音乐/环境声/音效,并为后续新口播和分镜文案提供时间轴;视频视觉路同步抽取参考帧,参考帧只用于人工选择主体并生成相似主体白底视图。产品图上传后独立形成产品资产包:自动识别视角、左右/上下/内外侧、结构点、比例和风险,并补缺角度。最终分镜规划按逐句时间轴把文案、相似主体资产和产品资产汇合;当前暂停直接调视频模型,先逐条生成并审核首帧/尾帧,确认后再决定哪些分镜进入视频候选。

+

当前产品方向已收窄为“信息流广告快速复刻”:主界面左侧是素材输入列,右侧是信息流复刻工作表。顶部固定显示 01-09 流程顺序和每一步的判定依据,编号不再是装饰文本,而是按素材任务、源视频、音频文案、抽帧、主体资产、产品资产、分镜文案、首尾帧和视频候选这些状态解锁。用户粘贴 TK 链接或上传视频后点击“开始分析”,系统自动下载源视频;下载完成后并行启动音频文案路和视频视觉路。音频文案路提取原音频文案/字幕,分析讲话人、语速节奏、背景音乐/环境声/音效,并为后续新口播和分镜文案提供时间轴;视频视觉路同步抽取参考帧,参考帧只用于人工选择主体并生成相似主体白底视图。产品图上传后独立形成产品资产包:自动识别视角、左右/上下/内外侧、结构点、比例和风险,并补缺角度。最终分镜规划按逐句时间轴把文案、相似主体资产和产品资产汇合;当前暂停直接调视频模型,先逐条生成并审核首帧/尾帧,确认后再决定哪些分镜进入视频候选。

-
1

导入素材

粘贴 TK / 信息流视频链接或上传本地视频;“开始”只把任务放入第一步队列。

-
2

下载源视频

后端用 yt-dlp 或本地上传文件落 source.mp4,记录时长、尺寸和视频只读地址。

-
3

并行素材分析

下载完成后前端同时触发 triggerTranscribeanalyzeJob:音频路生成字幕/节奏/背景音,视觉路自动抽 12 张参考帧。

-
4

资产包准备

用户可删除/补选参考帧并生成相似主体视图;参考帧到这里为止,后续首尾帧和视频不再把原关键帧当画面真源。产品图上传后自动识别视角、结构和风险,并补缺角度,形成产品资产包。

-
5

首尾帧闸门

按逐句时间轴生成竖向分镜行;每行先规划镜头类型、人物描述、是否需要人物/产品、首帧、尾帧和产品出现方式,再按需求从相似主体 6/10 视图里选择合适人物视角,并和产品素材一起生成首尾帧,先看图确认,不直接批量提交视频。

+
01

素材输入

有当前素材任务即通过;输入框只负责创建或切换任务。

+
02

源视频下载

job.video_url 存在即通过;created/downloading 视为运行中。

+
03

音频文案

audio_script.source_texttranscript 逐句时间轴有内容即通过。

+
04

抽帧参考

job.frames.length > 0 即通过;参考帧只做主体重构证据。

+
05

相似主体

关键帧里存在 subject_assets 即通过;生成类似创新主体,不复刻原人。

+
06

产品素材池

product_refs 有记录即通过;不限量上传,后续按分镜最多挑 6 张。

+
07

分镜文案

逐句时间轴生成后进入分镜;新口播可单段或整片改写。

+
08

画面首尾帧

先定场景+人+产品+动作,再生成 asset 类型首帧/尾帧;旧 keyframe 不算通过。

+
09

视频候选

当前不直接调视频模型;首尾帧审核后才开放单条或批量提交。

@@ -589,7 +593,7 @@ web/next.config.mjsNext.js 构建配置:静态导出、图片不走优化、禁用开发环境左下角 Next Dev Indicator,并移除 Next 16 已不支持的 eslint 顶层配置,避免本地 dev 出现配置 Issue 提示。 web/app/globals.css全局主题变量、登录页视觉样式、ReactFlow 样式引用,以及本地开发态 nextjs-portal 遮挡隐藏规则。 web/app/page.tsx产品工作台主状态:jobs、activeJobId、生成任务状态;主渲染为全屏素材输入列 + 信息流广告复刻工作表;“开始分析”会把 job 放入并行素材分析队列,下载完成后触发 triggerTranscribe 解析音频,并触发 analyzeJob 自动抽 12 张参考帧,形成“音频文案路 + 视频视觉路”同步推进;底部吸附音频条不再从主界面渲染。 - web/components/ad-recreation-board.tsx信息流广告复刻工作表:左侧素材输入只负责链接/上传和任务切换,不再重复放横版原视频预览;右侧顶部用“音频文案路、视频视觉路、主体资产、产品资产”四个状态条显示后台并行进度。源视频工作区展示视频下载状态和默认折叠的文案依据。音频解析结果改成默认折叠的辅助信息,展开后同一行看讲话人/节奏/背景音;主工作区左侧是按 9:16 显示的竖版原视频播放器,播放器内覆盖“当前点抽帧”,按当前播放秒数手动补参考帧;右侧上方是音频波形 / 切点参考,下方是逐句时间轴;下一行铺开“关键帧 / 相似主体”。音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点,顶部同时显示当前播放秒数、总时长和鼠标指针停点秒数。视频播放时通过 requestAnimationFrame 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。关键帧区的主入口是“自动抽帧 12 张”,一键按动作峰值目标重新抽取 12 张源视频参考帧,优先抓手势、表情变化、节奏点和镜头变化,缩略图按竖版完整比例显示不裁切并用更多列紧凑铺开,鼠标停留会通过固定浮层放大展示完整帧。“生成 10 张高清图”放在相似主体白底视图区,不和抽参考按钮平齐;如果用户没有勾选帧,默认把全部关键帧作为主体参考,勾选后只传已选帧;生成区可在“透明骨架 / 普通真人”之间切换,可选择桌面导入的 5 套内置形象作为创意方向,并可填写统一主体方向,例如年轻女性、更运动、更高级。关键帧和相似主体白底视图都用更小的竖版缩略图密排;白底视图只展示每个 view 的最新一张,缩略图上提供“重新生成这一张”和“删除这一张”,单张重生会用 replace_views=true 替换同一视角。前端调用 generateSubjectAssets 时按主体类型传 subject_style=transparent_humansource_actor,按需传 character_id,并使用 reconstruction_mode=similar;后端会把关键帧和内置形象视为同一个主体的创意证据,并锁定同一性别表现、年龄段、体型、材质、风格和视觉身份,同时生成全身多视角 + 肩颈正/左右近景 + 后颈肩背特写,避免整套图出现男女性别、老少年龄或样式混杂。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px,但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后按“套在脖子上的 U 形肩颈按摩仪”进行同一产品批量识别,左/右按佩戴者身体左右、上/下按佩戴方向,额外标注内外侧、开口方向、局部结构点、背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停通过固定浮层显示大图预览,能盖过滚动容器和分镜框架;缺视角补图失败时保留重试入口。脚本区在分镜行上方提供“作者想法”和“整片改写”,每行新口播文案可直接编辑并可单段 AI 改写,分镜时间和原内容列压缩为窄摘要列,新口播列进一步收窄,把横向空间留给画面规划和首尾帧。每条音频分镜纵向排列,行内从左到右串起原内容、新口播文案、画面规划/产品融入和历史候选视频槽;画面规划区先选择镜头类型(人物/情绪、人物+产品、产品特写、场景过渡),再用人物/产品开关、首帧规划、尾帧规划和产品出现方式决定这一条到底需不需要产品图或相似主体参考。当前主流程暂停直接调用视频模型,不再提供“生成本条 · Seedance”或“一键提交全部”视频入口;行内新增“首尾帧闸门”,分别显示/生成首帧和尾帧,旧 keyframe 类型首尾帧会被忽略,只认真正的 asset 首尾帧。生成首尾帧时调用 generateSceneAsset,先按人物描述、镜头类型、首尾状态和产品佩戴需求,从相似主体 6/10 视图里自动挑选最多 5 张最相关主体视角,再传入 subject_images 和该行自动挑选的产品图 product_images;关键帧只作为前置主体重构证据和行数据承载位置,不再作为后续视频首尾帧参考。视频候选槽只展示历史候选和待生成占位,按钮改为“保存本条规划 / 保存全部规划”。只有该行勾选“产品”时,首尾帧生成才会从产品素材池按分镜角色、视角优先级、用途标签、置信度和风险自动挑选最多 6 张相关产品图;未勾选产品时不会把产品图提交给首尾帧/后续生视频模型。只有该行勾选“人物”时,才会传按需筛选后的相似主体参考图;否则 prompt 会明确禁止强行添加主角式透明骨架人,后端也不会再给产品特写强加透明骨架人约束。ModelTrace 会在音频解析、产品识别/补图、相似主体高清视图包、脚本改写等入口旁直接展示模型名;所有生图入口都显示并使用 gpt-image-2,没有其他图片模型 fallback;点击后用固定浮层展示模型链路、输入输出和回退逻辑。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。 + web/components/ad-recreation-board.tsx信息流广告复刻工作表:顶部由 buildWorkflowSteps 统一生成 01-09 流程顺序、状态和判定依据,WorkflowOrderBar 展示完整顺序,WorkflowStepBadge / PipelineLane / 分镜列标题共用同一套编号。左侧素材输入只负责链接/上传和任务切换,不再重复放横版原视频预览;右侧顶部用“音频文案、抽帧参考、相似主体、产品素材池”四个状态条显示后台并行进度。源视频工作区展示视频下载状态和默认折叠的文案依据。音频解析结果改成默认折叠的辅助信息,展开后同一行看讲话人/节奏/背景音;主工作区左侧是按 9:16 显示的竖版原视频播放器,播放器内覆盖“当前点抽帧”,按当前播放秒数手动补参考帧;右侧上方是音频波形 / 切点参考,下方是逐句时间轴;下一行铺开“关键帧 / 相似主体”。音频波形用参考图式的连续灰色包络显示响度、停顿和密集爆点,顶部同时显示当前播放秒数、总时长和鼠标指针停点秒数。视频播放时通过 requestAnimationFrame 平滑驱动波形播放线,同时同步高亮并滚动当前句;点击音频波形或字幕行会跳转原视频时间。关键帧区的主入口是“自动抽帧 12 张”,一键按动作峰值目标重新抽取 12 张源视频参考帧,优先抓手势、表情变化、节奏点和镜头变化,缩略图按竖版完整比例显示不裁切并用更多列紧凑铺开,鼠标停留会通过固定浮层放大展示完整帧。“生成 10 张高清图”放在相似主体白底视图区,不和抽参考按钮平齐;如果用户没有勾选帧,默认把全部关键帧作为主体参考,勾选后只传已选帧;生成区可在“透明骨架 / 普通真人”之间切换,可选择桌面导入的 5 套内置形象作为创意方向,并可填写统一主体方向,例如年轻女性、更运动、更高级。关键帧和相似主体白底视图都用更小的竖版缩略图密排;白底视图只展示每个 view 的最新一张,缩略图上提供“重新生成这一张”和“删除这一张”,单张重生会用 replace_views=true 替换同一视角。前端调用 generateSubjectAssets 时按主体类型传 subject_style=transparent_humansource_actor,按需传 character_id,并使用 reconstruction_mode=similar;后端会把关键帧和内置形象视为同一个主体的创意证据,并锁定同一性别表现、年龄段、体型、材质、风格和视觉身份,同时生成全身多视角 + 肩颈正/左右近景 + 后颈肩背特写,避免整套图出现男女性别、老少年龄或样式混杂。音频结果下方是信息流复刻分镜工作台:顶部产品参考区是“同一产品素材池”,不限量上传产品图,不做不同产品身份判断;上传原图推荐长边 1200-2000px、短边至少 600px,但后端会统一生成最长边 1600px、JPEG 92 的 AI 工作副本,并回显尺寸、自动转换和风险标注;上传后按“套在脖子上的 U 形肩颈按摩仪”进行同一产品批量识别,左/右按佩戴者身体左右、上/下按佩戴方向,额外标注内外侧、开口方向、局部结构点、背景类型、用途标签、生成风险和备注,用户只检查备注,鼠标悬停通过固定浮层显示大图预览,能盖过滚动容器和分镜框架;缺视角补图失败时保留重试入口。脚本区在分镜行上方提供“作者想法”和“整片改写”,每行新口播文案可直接编辑并可单段 AI 改写,分镜时间和原内容列压缩为窄摘要列,新口播列进一步收窄,把横向空间留给画面规划和首尾帧。每条音频分镜纵向排列,行内从左到右串起原内容、新口播文案、画面规划/产品融入和历史候选视频槽;画面规划区先选择镜头类型(人物/情绪、人物+产品、产品特写、场景过渡),再用人物/产品开关、首帧规划、尾帧规划和产品出现方式决定这一条到底需不需要产品图或相似主体参考。当前主流程暂停直接调用视频模型,不再提供“生成本条 · Seedance”或“一键提交全部”视频入口;行内新增“首尾帧闸门”,分别显示/生成首帧和尾帧,旧 keyframe 类型首尾帧会被忽略,只认真正的 asset 首尾帧。生成首尾帧时调用 generateSceneAsset,先按人物描述、镜头类型、首尾状态和产品佩戴需求,从相似主体 6/10 视图里自动挑选最多 5 张最相关主体视角,再传入 subject_images 和该行自动挑选的产品图 product_images;关键帧只作为前置主体重构证据和行数据承载位置,不再作为后续视频首尾帧参考。视频候选槽只展示历史候选和待生成占位,按钮改为“保存本条规划 / 保存全部规划”。只有该行勾选“产品”时,首尾帧生成才会从产品素材池按分镜角色、视角优先级、用途标签、置信度和风险自动挑选最多 6 张相关产品图;未勾选产品时不会把产品图提交给首尾帧/后续生视频模型。只有该行勾选“人物”时,才会传按需筛选后的相似主体参考图;否则 prompt 会明确禁止强行添加主角式透明骨架人,后端也不会再给产品特写强加透明骨架人约束。ModelTrace 会在音频解析、产品识别/补图、相似主体高清视图包、脚本改写等入口旁直接展示模型名;所有生图入口都显示并使用 gpt-image-2,没有其他图片模型 fallback;点击后用固定浮层展示模型链路、输入输出和回退逻辑。旧分镜卡、抽帧控制和视频生成组件仍保留在文件里,但当前主路径不渲染。 web/components/media-asset-tile.tsx项目内媒体素材缩略图基底组件:图片、视频、抽帧、产品图、相似主体图、首尾帧和视频候选默认从这里获得统一交互。组件负责缩略图显示、顶层固定浮层 hover 放大、删除按钮、重新生成等操作按钮、忙碌遮罩和图片/视频共用预览,避免每个新板块重复手写不同的媒体交互。 web/app/login/page.tsx生产登录页:访问账号/访问密钥表单、保持登录、错误/成功状态;当前只在原版 Digital Oasis 动态背景上叠加一个组合登录框,桌面端左侧是动态角色,右侧是图标化登录表单;面板左上角展示官网 SKG 字标和中文“营销内容工作台”系统标识。 web/app/login/layout.tsx登录路由专属 layout:覆盖全站默认网页标题和描述为空,避免 /login 继承工作台 metadata 后在页面源码里继续出现登录界面文字以外的文案。 @@ -628,8 +632,9 @@ web/app/page.tsx -> 信息流广告复刻工作表:web/components/ad-recreation-board.tsx -> 开始分析:创建/激活 job → 下载完成后并行触发视频视觉路 analyzeJob 与音频文案路 triggerTranscribe - -> 左侧素材输入列 + 右侧四路状态条 + 默认折叠的文案依据 + 源视频工作区(音频解析结果默认折叠,竖版 9:16 原视频播放器内可当前点抽帧,右侧上方连续响度波形显示当前/总时长/指针停点,右侧下方逐句时间轴联动滚动,参考帧池在下方多列铺开且主入口为“自动抽帧 12 张”,相似主体高清视图包生成按钮放在视图区;不勾选帧则默认用全部帧,勾选后只用已选帧,可叠加 5 套内置形象) - -> 信息流复刻分镜工作台:同一产品素材池不限量上传 → 自动识别视角 / 背景 / 用途 / 风险 → 人工检查备注 → 逐句时间轴 → 原内容 / 新口播文案 / 画面规划与产品融入(镜头类型、人物描述、人物/产品开关、首帧、尾帧、产品出现方式)→ 首尾帧闸门:按需求选择主体视角 + 产品素材生成首帧/尾帧 → 保存规划 → 历史候选视频槽(当前不直接批量提交视频) + -> WorkflowOrderBar:01 素材输入 → 02 源视频下载 → 03 音频文案 → 04 抽帧参考 → 05 相似主体 → 06 产品素材池 → 07 分镜文案 → 08 画面首尾帧 → 09 视频候选;每步从 buildWorkflowSteps 取判定依据和状态 + -> 左侧素材输入列 + 右侧 03-06 状态条 + 默认折叠的文案依据 + 源视频工作区(音频解析结果默认折叠,竖版 9:16 原视频播放器内可当前点抽帧,右侧上方连续响度波形显示当前/总时长/指针停点,右侧下方逐句时间轴联动滚动,参考帧池在下方多列铺开且主入口为“自动抽帧 12 张”,相似主体高清视图包生成按钮放在视图区;不勾选帧则默认用全部帧,勾选后只用已选帧,可叠加 5 套内置形象) + -> 信息流复刻分镜工作台:06 同一产品素材池不限量上传 → 自动识别视角 / 背景 / 用途 / 风险 → 人工检查备注 → 07 逐句时间轴 / 原内容 / 新口播文案 → 08 画面规划与产品融入(镜头类型、人物描述、人物/产品开关、首帧、尾帧、产品出现方式)→ 首尾帧闸门:按需求选择主体视角 + 产品素材生成首帧/尾帧 → 保存规划 → 09 历史候选视频槽(当前不直接批量提交视频) -> 底部音频条:不再渲染,音频结果集中到右侧工作表 -> 旧节点/深度素材面板:web/components/nodes/index.tsx、web/components/lightbox.tsx、web/components/storyboard-workbench.tsx(底层保留,当前不作为主入口) -> API 契约:web/lib/api.ts @@ -1016,6 +1021,18 @@ ProductRefStateItem {

变更记录

这个记录不是 git log 的替代品。它记录“产品理解发生了什么变化、影响了哪些源码、你以后描述需求时该怎么说”。后续每次改功能都要补一条。

+
+
+

2026-05-18 · 前端流程编号改为状态判定驱动

+ UI + Workflow +
+
+

问题:页面里的 01、02 只是硬编码视觉标签,用户无法判断编号代表什么流程,也看不出每一步是按什么条件通过、运行、阻塞或暂停。

+

改动:AdRecreationBoard 新增 buildWorkflowSteps,统一定义 01 素材输入、02 源视频下载、03 音频文案、04 抽帧参考、05 相似主体、06 产品素材池、07 分镜文案、08 画面首尾帧、09 视频候选。顶部新增 WorkflowOrderBar 展示顺序、状态和判定依据;素材输入、源视频区、四路状态条、产品素材池、分镜文案、画面规划和视频候选列共用 WorkflowStepBadge / PipelineLane 的同一编号。

+

影响:以后说“改 03 音频文案”或“08 首尾帧怎么判定”时,可以直接定位到这套步骤配置;编号不再散落在 JSX 里。

+
+

2026-05-18 · 首尾帧按人物描述选择主体视角

diff --git a/web/components/ad-recreation-board.tsx b/web/components/ad-recreation-board.tsx index c347fd0..e29ce8f 100644 --- a/web/components/ad-recreation-board.tsx +++ b/web/components/ad-recreation-board.tsx @@ -112,6 +112,16 @@ type SubjectPlanningRef = ImageRef & { view: string; roleHint: string } type SubjectStyleMode = "transparent_human" | "source_actor" type StoryboardVisualMode = NonNullable type RowPlanPatch = Partial> +type WorkflowStepId = "input" | "source" | "audio" | "visual" | "subject" | "product" | "script" | "scene" | "video" +type WorkflowStepStatus = "blocked" | "pending" | "running" | "ready" | "paused" +type WorkflowStep = { + id: WorkflowStepId + no: string + title: string + detail: string + judge: string + status: WorkflowStepStatus +} const VISUAL_MODE_OPTIONS: Array<{ value: StoryboardVisualMode; label: string; description: string }> = [ { value: "person_only", label: "人物/情绪", description: "只拍人物、状态、痛点或口播,不强制露产品。" }, @@ -297,6 +307,131 @@ function countSubjectAssetViews(job: Job | null) { 0) } +function countEndpointFramePairs(job: Job | null) { + if (!job) return 0 + return job.frames.filter((frame) => endpointAssetRef(frame, "first_frame") && endpointAssetRef(frame, "last_frame")).length +} + +function stepStatus({ ready, running, blocked, paused }: { ready?: boolean; running?: boolean; blocked?: boolean; paused?: boolean }): WorkflowStepStatus { + if (running) return "running" + if (ready) return "ready" + if (paused) return "paused" + if (blocked) return "blocked" + return "pending" +} + +function buildWorkflowSteps({ + job, + submitting, + audioReady, + audioRunning, + transcriptCount, + visualReady, + visualRunning, + subjectAssetCount, + productAssetCount, + endpointFramePairCount, + generatedVideoCount, +}: { + job: Job | null + submitting: boolean + audioReady: boolean + audioRunning: boolean + transcriptCount: number + visualReady: boolean + visualRunning: boolean + subjectAssetCount: number + productAssetCount: number + endpointFramePairCount: number + generatedVideoCount: number +}): WorkflowStep[] { + const hasSourceVideo = !!job?.video_url + const downloading = !!job && ["created", "downloading"].includes(job.status) + const storyboardReady = transcriptCount > 0 + const endpointTargetCount = Math.max(transcriptCount, 0) + return [ + { + id: "input", + no: "01", + title: "素材输入", + detail: job ? `当前 ${shortId(job.id)}` : "待链接/上传", + judge: "有当前素材任务即通过;输入框只负责创建或切换任务。", + status: stepStatus({ ready: !!job, running: submitting }), + }, + { + id: "source", + no: "02", + title: "源视频下载", + detail: hasSourceVideo ? "源视频已就绪" : downloading ? "下载中" : "待下载", + judge: "job.video_url 存在即通过;created/downloading 视为运行中。", + status: stepStatus({ ready: hasSourceVideo, running: downloading, blocked: !job }), + }, + { + id: "audio", + no: "03", + title: "音频文案", + detail: audioReady ? `${transcriptCount} 段字幕` : "待转写/分析", + judge: "audio_script.source_text 有内容,或 transcript 逐句时间轴有内容即通过。", + status: stepStatus({ ready: audioReady, running: audioRunning, blocked: !hasSourceVideo }), + }, + { + id: "visual", + no: "04", + title: "抽帧参考", + detail: visualReady ? `${job?.frames.length ?? 0} 张参考帧` : "待抽帧", + judge: "job.frames.length 大于 0 即通过;这些帧只做主体重构证据。", + status: stepStatus({ ready: visualReady, running: visualRunning, blocked: !hasSourceVideo }), + }, + { + id: "subject", + no: "05", + title: "相似主体", + detail: subjectAssetCount ? `${subjectAssetCount} 张白底视图` : "待生成主体", + judge: "关键帧里存在 subject_assets 即通过;生成的是类似创新主体,不复刻原人。", + status: stepStatus({ ready: subjectAssetCount > 0, blocked: !visualReady }), + }, + { + id: "product", + no: "06", + title: "产品素材池", + detail: productAssetCount ? `${productAssetCount} 张产品图` : "待上传/识别", + judge: "product_refs 有记录即通过;不限量,但每条视频后续最多挑 6 张相关图。", + status: stepStatus({ ready: productAssetCount > 0, blocked: !job }), + }, + { + id: "script", + no: "07", + title: "分镜文案", + detail: storyboardReady ? `${transcriptCount} 条分镜` : "待音频时间轴", + judge: "逐句时间轴生成后进入分镜;新口播可按单段或整片改写。", + status: stepStatus({ ready: storyboardReady, running: audioRunning, blocked: !audioReady }), + }, + { + id: "scene", + no: "08", + title: "画面首尾帧", + detail: endpointTargetCount ? `${endpointFramePairCount}/${endpointTargetCount} 组首尾帧` : "待分镜", + judge: "每条分镜先确定场景+人+产品+动作,再生成 asset 类型首帧/尾帧;keyframe 不算通过。", + status: stepStatus({ ready: endpointTargetCount > 0 && endpointFramePairCount >= endpointTargetCount, blocked: !storyboardReady }), + }, + { + id: "video", + no: "09", + title: "视频候选", + detail: generatedVideoCount ? `${generatedVideoCount} 条历史` : "生成入口暂停", + judge: "当前不直接调视频模型;首尾帧审核后才开放单条或批量提交。", + status: generatedVideoCount > 0 ? "ready" : "paused", + }, + ] +} + +function workflowStepMap(steps: WorkflowStep[]) { + return steps.reduce((acc, step) => { + acc[step.id] = step + return acc + }, {} as Record) +} + function guessSubjectKind(name: string): SubjectKind { return /人|人物|模特|骨架|身体|脸|手|person|people|human|body|face|hand|character/i.test(name) ? "living" @@ -1142,6 +1277,21 @@ export function AdRecreationBoard({ const visualReady = (job?.frames.length ?? 0) > 0 const subjectAssetCount = countSubjectAssetViews(job) const productAssetCount = job?.product_refs?.length ?? 0 + const endpointFramePairCount = countEndpointFramePairs(job) + const workflowSteps = buildWorkflowSteps({ + job, + submitting: data.submitting, + audioReady, + audioRunning, + transcriptCount, + visualReady, + visualRunning, + subjectAssetCount, + productAssetCount, + endpointFramePairCount, + generatedVideoCount: generatedVideos.length, + }) + const workflow = workflowStepMap(workflowSteps) const statusMessage = job?.message?.startsWith("视频生成已提交") ? "历史候选视频已保留;当前已暂停直接提交视频,先逐条生成并审核首尾帧。" : job?.message @@ -1338,9 +1488,12 @@ export function AdRecreationBoard({
+ +
- 02 +

源视频解析与参考帧

@@ -1396,10 +1549,10 @@ export function AdRecreationBoard({
- - - 0} /> - 0} /> + + + +
@@ -1419,6 +1572,10 @@ export function AdRecreationBoard({ onJobUpdate={data.onJobUpdate} onDeleteVideo={data.onDeleteVideo} runtimeModels={runtimeModels} + productStep={workflow.product} + scriptStep={workflow.script} + sceneStep={workflow.scene} + videoStep={workflow.video} />
@@ -1430,6 +1587,7 @@ export function AdRecreationBoard({ function MaterialColumn({ data, + step, jobs, job, activeJobId, @@ -1440,6 +1598,7 @@ function MaterialColumn({ onStartProduction, }: { data: NodeData + step: WorkflowStep jobs: Job[] job: Job | null activeJobId: string | null @@ -1454,7 +1613,7 @@ function MaterialColumn({
- 01 +

素材输入

一个素材就是一次文件任务

@@ -2192,12 +2351,20 @@ function AudioStoryboardPlanPanel({ onJobUpdate, onDeleteVideo, runtimeModels, + productStep, + scriptStep, + sceneStep, + videoStep, }: { job: Job | null selectedFrames: Set onJobUpdate?: (job: Job) => void onDeleteVideo?: (videoId: string) => void runtimeModels?: RuntimeModels + productStep: WorkflowStep + scriptStep: WorkflowStep + sceneStep: WorkflowStep + videoStep: WorkflowStep }) { const [storyboardSaveBusyRow, setStoryboardSaveBusyRow] = useState(null) const [batchStoryboardSaveBusy, setBatchStoryboardSaveBusy] = useState(false) @@ -2630,7 +2797,10 @@ function AudioStoryboardPlanPanel({
- } title="信息流复刻分镜工作台" /> +
+ } title="信息流复刻分镜工作台" /> + +

每条分镜纵向排列;行内完成原内容、新文案、画面/产品和视频候选。关键帧选择与相似主体重构在源视频工作区下方统一处理。

@@ -2645,6 +2815,7 @@ function AudioStoryboardPlanPanel({
} title="同一产品素材池 / 视角标注" /> + {productItems.length ? `${productItems.length} 张素材` : "素材池不限量"} {(productAnalyzing || productAngleBusy) && ( @@ -2778,7 +2949,7 @@ function AudioStoryboardPlanPanel({

{row.source}

- +