diff --git a/.memory/worklog.json b/.memory/worklog.json index aae3869..0ff2f7c 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,19 +1,5 @@ { "entries": [ - { - "files_changed": 1, - "hash": "169951b", - "message": "auto-save 2026-05-13 06:09 (~1)", - "ts": "2026-05-13T06:09:56+08:00", - "type": "commit" - }, - { - "files_changed": 1, - "hash": "d0b73fd", - "message": "auto-save 2026-05-13 06:15 (~1)", - "ts": "2026-05-13T06:15:50+08:00", - "type": "commit" - }, { "files_changed": 1, "hash": "1dd2c67", @@ -3287,6 +3273,19 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 12:37 (~6)", "files_changed": 1 + }, + { + "ts": "2026-05-14T12:43:03+08:00", + "type": "commit", + "message": "auto-save 2026-05-14 12:42 (~9)", + "hash": "2d1a89f", + "files_changed": 9 + }, + { + "ts": "2026-05-14T04:46:11Z", + "type": "session-heartbeat", + "message": "Codex 会话活跃 · 最近命令:codex · 4 项未提交变更 · 最近提交:auto-save 2026-05-14 12:42 (~9)", + "files_changed": 4 } ] } diff --git a/docs/source-analysis.html b/docs/source-analysis.html index 92d9465..c4b464e 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -556,8 +556,8 @@
3

清洗水印

对关键帧做全图或区域清洗,清洗版先进入待审核状态;确认后可单张替换,也可一键替换全部待应用清洗版。

4

主体识别

识别场景和主体候选,只是候选,不应锁死。

5

素材准备

清洗关键帧,把多张关键帧作为同一主体的参考,先重绘六张标准站立主体资产图,再按关键帧生成多个去主体、相似或换风格场景图。

-
6

分镜改造

把参考主体、场景、动作和 SKG 产品放入分镜结构;产品融合使用纵向 6 行镜头工作表,每行绑定产品图、白底人物图、产品区域、场景图、描述词、秒数和单条生成入口。

-
7

生成视频

普通分镜可调用 Seedance / Kling / Veo 3;产品融合固定用 GPT Image 2 生成位置引导图,再用 Seedance 按秒数生成视频,结果回写到画面工作台节点。

+
6

分镜改造

把参考主体、场景、动作和 SKG 产品放入分镜结构;产品融合使用纵向 6 行镜头工作表,只补人物首帧、尾帧、描述词和秒数,产品图固定内置。

+
7

生成视频

普通分镜可调用 Seedance / Kling / Veo 3;产品融合自动传入固定 4 张 SKG 产品图和每行首尾帧,用 Seedance 按秒数生成视频,结果回写到对应行。

8

声音文案

音频轨独立处理:提取原音频并按实际秒数生成 SKG 英文产品介绍 voice-over,ASR/翻译只作为改前对照和节奏参考;配置 MiniMax 后从男声、女声、成熟声池随机生成自然英文配音 mp3。底部音频条播放原音频时,指针会按时间走过字幕节点。

9

合成成品

片段、字幕、配音、转场合成最终 mp4。当前未实现。

@@ -629,7 +629,7 @@ api/main.py
你看到的区域关键帧素材审核面板
-
主要源码FrameLightbox;按“原图/清洗、主体资产、首尾帧、产品融合、审核”五个页签组织;左侧只放主图/框选画布,但主体资产页左侧改为全部已清洗/已选参考帧网格,首尾帧页左侧显示全部关键帧并可勾选人物/机位参考。主体识别页会显示透明骨架人目标和 Vision 验收分数。清洗页右侧支持一键清洗未处理帧、单张替换清洗版和一键替换全部待应用清洗版;批量替换顺序调用 applyCleanedFrame,不新增后端接口。产品融合页左侧是纵向 6 行镜头工作表:每行只显示首帧、尾帧、已预填动作描述、秒数、生成按钮和对应视频结果;四张桌面 SKG 产品图作为固定产品参考,生成时通过 copyProductLibraryAsset 自动写入镜头,不再暴露产品角度槽、产品融合辅助栏或产品图库选择器。产品融合槽位的“粘贴”优先使用应用内 clipboard,也支持选中槽位后 Cmd+V 粘贴系统图片。主体资产页只确认一个统一主体,后端按参考重绘六张纯背景、占满画面的标准站立透明骨架人资产图;首尾帧页通过地点、风格、参考要素和可编辑 prompt 做文字生图,生成结果写入 scene_assets 但以 asset_role=first_frame/last_frame 标记,并自动传入当前产品融合镜头。相关接口包括 cleanupFrameapplyCleanedFrameaddElementgenerateSubjectAssetsgenerateSceneAssetcopyProductLibraryAsset
+
主要源码FrameLightbox;按“原图/清洗、主体资产、首尾帧、产品融合、审核”五个页签组织;左侧只放主图/框选画布,但主体资产页左侧改为全部已清洗/已选参考帧网格,首尾帧页左侧显示全部关键帧并可勾选人物/机位参考。主体识别页会显示透明骨架人目标和 Vision 验收分数。清洗页右侧支持一键清洗未处理帧、单张替换清洗版和一键替换全部待应用清洗版;批量替换顺序调用 applyCleanedFrame,不新增后端接口。产品融合页左侧是纵向 6 行镜头工作表:每行只显示首帧、尾帧、已预填动作描述、秒数、生成按钮和对应视频结果;描述词内置 36 条镜头语言模板,按“建立出场、产品入画、佩戴贴合、使用感受、生活延展、收尾记忆”排列,点击“换一组”只刷新 6 行描述词。四张桌面 SKG 产品图作为固定产品参考,生成时通过 copyProductLibraryAsset 自动写入镜头,不再暴露产品角度槽、产品融合辅助栏或产品图库选择器。产品融合槽位的“粘贴”优先使用应用内 clipboard,也支持选中槽位后 Cmd+V 粘贴系统图片。主体资产页只确认一个统一主体,后端按参考重绘六张纯背景、占满画面的标准站立透明骨架人资产图;首尾帧页通过地点、风格、参考要素和可编辑 prompt 做文字生图,生成结果写入 scene_assets 但以 asset_role=first_frame/last_frame 标记,并自动传入当前产品融合镜头。相关接口包括 cleanupFrameapplyCleanedFrameaddElementgenerateSubjectAssetsgenerateSceneAssetcopyProductLibraryAsset
适合怎么描述“这一组关键帧如何共同生成一个统一主体包;某张关键帧的水印、去主体场景图、产品融合镜头组和质量风险应该如何审核”。
@@ -806,7 +806,7 @@ SubjectAsset { 产品图库GET /product-library/skglistProductLibrary读取内置 SKG 白底图库 manifest,返回产品标题、品类、尺寸、白底评分和预览图 URL。 产品图入库到 jobPOST /jobs/{id}/assets/product-librarycopyProductLibraryAsset把一个内置产品图库条目复制为当前 job 的普通 asset,返回 ImageRef(kind="asset"),用于画面工作台产品融合和分镜产品参考组。 产品融合引导图POST /jobs/{id}/product-fusion/guidecreateProductFusionGuide旧流程兼容接口:读取产品图和白底人物图,按 product_region 合成位置引导图。当前首尾帧流程不再主动调用它。 - 产品融合描述词POST /jobs/{id}/product-fusion/descriptionsgenerateProductFusionDescriptions兼容接口:可生成 20 条产品融合动作描述库。当前前端默认直接用本地 20 条精准模板预填 6 行镜头,不再显示单独的 AI 草拟入口。 + 产品融合描述词POST /jobs/{id}/product-fusion/descriptionsgenerateProductFusionDescriptions兼容接口:可生成产品融合动作描述库。当前前端默认直接用本地 36 条镜头语言模板预填 6 行镜头,并通过“换一组”按钮按 6 条一组轮换。 分镜保存PUT /frames/{idx}/storyboardupdateStoryboard保存 4 图槽、时长和改造说明。 生图POST /frames/{idx}/generategenerateImage基于关键帧或已选生成图做 image-to-image,目前可用。 @@ -917,6 +917,18 @@ SubjectAsset {

变更记录

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

+
+
+

2026-05-14 · 音频提取直接生成英文产品口播

+ Audio + MiniMax +
+
+

问题:“提取音频”不能只做原音频转文字再改写,用户需要点击后直接得到介绍 SKG 产品的英文文案和配音,长度尽量贴近原音频,并且声音不能生硬。

+

改动:pipeline_transcribe 提取 audio.wav 后读取原音频时长,用该时长估算英文口播词数;_rewrite_audio_script_sync 改为生成自然、有趣、可直接 TTS 的 SKG 英文产品介绍。ASR/翻译保留为对照和节奏参考,ASR 不可用时仍继续生成产品口播。MiniMax voice_id 改为从 MINIMAX_TTS_VOICE_POOL 随机选择男声、女声或成熟声。

+

影响:api/main.pyapi/.env.exampleapi/README.mdRULES.mdweb/components/nodes/index.tsxweb/components/audio-strip.tsxweb/components/dashboard.tsxdocs/source-analysis.html

+
+

2026-05-14 · 产品融合描述词扩成 20 条精准模板

@@ -1042,13 +1054,13 @@ SubjectAsset {
-

2026-05-14 · 音频处理接入 SKG 英文口播改写与 MiniMax 配音

+

2026-05-14 · 音频处理接入 SKG 英文产品口播与 MiniMax 配音

Audio MiniMax
-

问题:音频处理节点之前只说明“音轨 → ASR → 翻译 → 改写”,没有真实改写产物,也没有配音输出;用户无法直接拿到符合 SKG 产品语境的英文口播。

-

改动:Job 新增 audio_scriptpipeline_transcribe 在 ASR 和翻译后生成 SKG 英文改写文案,并在配置 MINIMAX_API_KEY 时调用 MiniMax T2A 输出 /jobs/{id}/audio-script.mp3。前端 AudioNode 和侧栏 Rewrite 区显示模型链路、英文改写文案和配音播放器。

+

问题:音频处理节点之前只说明“音轨 → ASR → 翻译 → 改写”,没有按原音频时长生成的产品介绍产物,也没有配音输出;用户无法直接拿到符合 SKG 产品语境的英文口播。

+

改动:Job 新增 audio_scriptpipeline_transcribe 提取 audio.wav 后按原音频秒数生成 SKG 英文产品介绍文案,并在配置 MINIMAX_API_KEY 时调用 MiniMax T2A 输出 /jobs/{id}/audio-script.mp3。MiniMax voice_id 从英文男声、女声、成熟声池随机选择;前端 AudioNode 和侧栏 Rewrite 区显示模型链路、英文产品文案和配音播放器。

边界:MiniMax 官方 Speech API 当前接入的是 TTS 配音,不替代 ASR;原始音频文案提取仍走现有 OpenAI-compatible audio transcription 入口。

影响:api/main.pyapi/.env.exampleapi/README.mdweb/lib/api.tsweb/components/nodes/index.tsxweb/components/dashboard.tsxweb/app/page.tsxdocs/source-analysis.html

diff --git a/web/app/page.tsx b/web/app/page.tsx index f67e4d7..261230a 100644 --- a/web/app/page.tsx +++ b/web/app/page.tsx @@ -539,9 +539,12 @@ export default function Home() { `产品角度图 2:${labelOf(productRefs[1], "SKG 产品侧面/斜侧视角")}。`, `产品角度图 3:${labelOf(productRefs[2], "SKG 产品背面/细节视角")}。`, `产品角度图 4:${labelOf(productRefs[3], "SKG 产品补充/底部或佩戴视角")}。`, + "产品使用部位:这是颈部/肩颈按摩仪,只能自然佩戴或贴合在脖子、后颈、颈肩交界处;不要放到手臂、腰、腿、胸口、眼部或背景里。", + "比例尺寸:产品应符合真实颈部按摩仪大小,U 形结构环绕后颈但不能巨大化、缩小成饰品、嵌入身体、悬浮或穿透透明人体。", + "镜头语言:严格按动作描述里的出场方式、景别、运镜、产品进入方式、佩戴贴合动作和收尾方式执行。", `动作描述:${shot.action_text.trim()}`, TRANSPARENT_HUMAN_VIDEO_PROMPT, - "融合要求:产品必须自然出现在透明骨架人动作中,尺寸可信,透视一致,贴合身体/手部/使用区域,不能悬浮、漂移、融化、扭曲或变成其他物体。", + "融合要求:产品必须自然出现在透明骨架人动作中,尺寸可信,透视一致,只贴合手部拿取和后颈/颈肩使用区域,不能悬浮、漂移、融化、扭曲或变成其他物体。", "首尾连续性:镜头从首帧自然运动到尾帧,中间不要跳切,不换角色,不换产品,不突然改变场景。", "产品一致性:严格保持 SKG 产品外观、颜色、材质、U 形结构、按摩触点、按键和比例;四张产品角度图是产品身份真源。", "场景要求:背景、空间、光线和阴影要自然统一,不要出现水印、平台 UI、字幕或竞品包装。", diff --git a/web/components/lightbox.tsx b/web/components/lightbox.tsx index 97e7547..78f9066 100644 --- a/web/components/lightbox.tsx +++ b/web/components/lightbox.tsx @@ -120,7 +120,7 @@ type FusionUploadTarget = { } type FusionFrameRole = "first_image" | "last_image" const FUSION_PROMPT_MARKER_PREFIX = "产品融合镜头ID:" -const PRODUCT_FUSION_DESCRIPTION_PRESETS = [ +const LEGACY_PRODUCT_FUSION_DESCRIPTION_PRESETS = [ "清晨卧室柔光里,透明骨架人把白色 SKG 颈部按摩仪轻戴到后颈,微微闭眼露出放松微笑。", "现代客厅沙发旁,透明骨架人双手扶住 SKG 机身两侧,肩线慢慢放低,表情从紧绷变舒适。", "居家办公桌前,透明骨架人轻按 SKG 侧边控制键,颈部骨架区域清晰可见,神情安静享受。", @@ -143,6 +143,60 @@ const PRODUCT_FUSION_DESCRIPTION_PRESETS = [ "收尾特写镜头里,透明骨架人佩戴 SKG 后缓慢抬头微笑,白色骨架清楚,整体干净高级。", ] +const PRODUCT_FUSION_LENS_STAGES = [ + "01 建立出场", + "02 产品入画", + "03 佩戴贴合", + "04 使用感受", + "05 生活延展", + "06 收尾记忆", +] + +const PRODUCT_FUSION_DESCRIPTION_PRESETS = [ + "镜头01|建立出场|半身中景,透明骨架人先自然出现在清晨卧室柔光里,SKG 白色产品放在桌面或手边;镜头慢慢推近,让人物透明外壳和白色骨架先成立,结尾手准备伸向产品。", + "镜头02|产品入画|从桌面产品近景开始,透明骨架人的手把 SKG 产品拿起带入画面;镜头从产品轻移到人物肩颈,产品尺寸真实,不能漂浮,结尾靠近后颈。", + "镜头03|佩戴贴合|肩颈侧面近景,透明骨架人双手扶住 SKG 两端贴合后颈并微调角度;镜头轻微环绕展示 U 形结构、触点和颈椎位置,结尾产品稳定贴合。", + "镜头04|使用感受|半身近景,产品已佩戴,透明骨架人闭眼呼吸放慢、肩线下沉、嘴角微笑;镜头慢推,不换场景,突出舒适享受但不要医疗治疗暗示。", + "镜头05|生活延展|现代客厅、办公桌或窗边休息场景,透明骨架人保持佩戴 SKG 做轻松阅读或休息动作;镜头横移或轻绕,产品位置稳定,人物透明身体和骨架清晰。", + "镜头06|收尾记忆|产品和人物肩颈半身特写,透明骨架人缓慢抬头微笑定格,SKG 产品轮廓清楚可辨;镜头停在干净高级的广告收尾,不出现文字和 logo 字幕。", + "备用01|镜中出场|浴室或卧室镜前,透明骨架人先在镜面中出现,手边放着 SKG 产品;镜头从镜中人物拉到真实人物,最后拿起产品准备佩戴。", + "备用02|手部带入|产品先在白色桌面上清楚出现,透明骨架人的手进入画面拿起 SKG;镜头跟随手部移动到肩颈区域,强调产品被真实拿起而不是凭空出现。", + "备用03|侧面贴合|45 度侧面近景,透明骨架人把 SKG 产品从颈侧滑入正确位置,轻轻按压贴合;镜头短距离环绕,确保产品透视、比例和身体接触真实。", + "备用04|按键反馈|产品佩戴后,透明骨架人用指尖轻按侧边按键,肩颈骨架区域被柔和光线照亮;镜头从按键细节回到人物放松表情。", + "备用05|办公舒缓|居家办公桌前,透明骨架人佩戴 SKG 靠回椅背,手从键盘移开,肩部慢慢放松;镜头从电脑桌面横移到人物半身。", + "备用06|沙发休息|现代客厅沙发上,透明骨架人戴着 SKG 闭眼休息,呼吸节奏变慢;镜头轻微推近产品和肩颈,最后停在舒适表情。", + "备用07|窗边阅读|窗边阅读角中,透明骨架人一边翻书一边稳定佩戴 SKG;镜头从书页过渡到产品,再到透明骨架人的平和微笑。", + "备用08|影棚展示|高端白色影棚里,透明骨架人佩戴 SKG 缓慢转身展示正面和侧面贴合效果;镜头平稳环绕,产品外观不能变形。", + "备用09|床边放松|暖色卧室床边,透明骨架人坐下后把 SKG 戴稳,肩线下沉,脸部从疲惫转为舒适;镜头慢慢推近收住。", + "备用10|阳台伸展|午后阳台休息区,透明骨架人戴着 SKG 缓慢侧头伸展,颈椎白骨清楚可见;镜头横移跟随动作,产品不漂移。", + "备用11|产品特写转人|开场为 SKG 产品白底特写,随后自然切到透明骨架人佩戴后的半身画面;镜头语言干净商业,强调产品身份一致。", + "备用12|拿起到佩戴一镜到底|透明骨架人从桌面拿起 SKG,抬手、对准后颈、轻轻戴上,一镜到底完成动作;产品始终保持真实尺寸和方向。", + "备用13|舒适反应特写|肩颈近景转脸部特写,透明骨架人闭眼微笑,骨架和透明外壳保持同一角色;镜头稳定,不夸张表演。", + "备用14|最终定格|透明骨架人佩戴 SKG 面向镜头轻轻微笑,产品清晰贴合后颈,背景干净高级;最后 1 秒稳定定格作为广告收尾。", + "备用15|门口入场|透明骨架人从现代公寓门口走入画面,肩颈略显紧绷,SKG 产品放在玄关台面;镜头中景跟随,结尾视线落到产品。", + "备用16|从包中取出|透明骨架人坐到办公椅上,从随身包里拿出白色 SKG 产品;镜头从包内产品切到人物手部,强调产品自然进入生活场景。", + "备用17|正面佩戴|正面半身镜头,透明骨架人双手把 SKG 从胸前抬到后颈,动作慢而准确;产品贴合后颈时停顿,比例和接触关系真实。", + "备用18|颈肩舒展|产品已佩戴,透明骨架人缓慢转动脖颈、肩部下沉,脸部露出轻松表情;镜头轻推近肩颈,不出现夸张疗效表达。", + "备用19|移动生活镜头|透明骨架人佩戴 SKG 在客厅和窗边之间轻松移动,透明身体和白色骨架始终清楚;镜头平稳横移,产品位置不变。", + "备用20|产品轮廓收束|收尾以肩颈侧面特写呈现 SKG 轮廓,透明骨架人微笑停住,背景柔和虚化;画面干净高级,无文字和水印。", + "备用21|影棚人物建立|高端白色影棚中,透明骨架人站立转向镜头,白色骨架清晰可见,SKG 产品置于旁边展台;镜头缓慢推近建立商业感。", + "备用22|产品旋转展示|白底产品图感的 SKG 产品在人物手中被轻轻转动展示正侧面,随后靠近透明骨架人的后颈;镜头跟随产品,不让产品变形。", + "备用23|侧后方落位|从人物侧后方观察 SKG 落到后颈位置,透明皮肤包裹白色颈椎骨架,双手轻调两端;镜头短距离环绕确认贴合。", + "备用24|安静闭眼|透明骨架人佩戴后闭眼停留,呼吸放慢、肩部放松,产品与颈部阴影自然;镜头不切换,只做细微推进。", + "备用25|场景转为日常|镜头从肩颈近景拉开到完整生活场景,人物继续佩戴 SKG 阅读、休息或看窗外;产品清楚但不抢走人物主体。", + "备用26|广告式收束|透明骨架人面对镜头轻轻微笑,手放下不再遮挡产品,SKG 稳定贴合后颈;镜头保持半身构图作为干净收尾。", + "备用27|疲惫到放松|开场透明骨架人坐在办公桌前揉肩,桌面有 SKG 产品;镜头从疲惫状态慢慢推近,结尾人物准备拿起产品。", + "备用28|桌面到肩颈|产品从桌面被拿起,镜头沿手部轨迹移动到肩颈,透明骨架人的颈椎和肋骨清晰;产品必须跟随手部运动。", + "备用29|双手校准|透明骨架人用双手对称扶住 SKG 两端,微调到后颈正确位置;镜头正侧之间轻微移动,强调贴合和尺寸可信。", + "备用30|舒适微表情|佩戴稳定后,透明骨架人眼神变柔和、嘴角轻扬,肩颈线条放松;镜头从产品细节回到脸部,保持广告级质感。", +] + +const legacyFusionDescriptionSet = new Set(LEGACY_PRODUCT_FUSION_DESCRIPTION_PRESETS) +const shouldUseDefaultFusionDescription = (value?: string | null) => { + const text = value?.trim() + return !text || legacyFusionDescriptionSet.has(text) +} + const createFusionShots = (): ProductFusionShot[] => Array.from({ length: FUSION_SHOT_COUNT }, (_, i) => ({ id: `shot-${i + 1}`, @@ -169,7 +223,7 @@ const normalizeFusionShots = (shots?: ProductFusionShot[] | null): ProductFusion ...item, ...shot, product_images: shot.product_images?.slice(0, PRODUCT_ANGLE_COUNT) ?? [], - action_text: shot.action_text?.trim() || item.action_text, + action_text: shouldUseDefaultFusionDescription(shot.action_text) ? item.action_text : shot.action_text, id: shot.id || item.id, } }) @@ -200,6 +254,7 @@ export function FrameLightbox({ jobId, frames, generatedVideos = [], activeIndex const [fusionUploadTarget, setFusionUploadTarget] = useState(null) const [fusionGenerating, setFusionGenerating] = useState(null) const [fusionSaving, setFusionSaving] = useState(false) + const [fusionPresetPage, setFusionPresetPage] = useState(0) const [editingElement, setEditingElement] = useState<{ frameIndex: number id: string @@ -415,6 +470,19 @@ export function FrameLightbox({ jobId, frames, generatedVideos = [], activeIndex } } + const rotateFusionDescriptions = () => { + const page = fusionPresetPage + 1 + const start = (page * FUSION_SHOT_COUNT) % PRODUCT_FUSION_DESCRIPTION_PRESETS.length + const next = fusionShots.map((shot, i) => ({ + ...shot, + action_text: PRODUCT_FUSION_DESCRIPTION_PRESETS[(start + i) % PRODUCT_FUSION_DESCRIPTION_PRESETS.length] || shot.action_text, + })) + setFusionPresetPage(page) + setFusionShots(next) + void persistFusionShots(next) + toast.success(`已换第 ${Math.floor(start / FUSION_SHOT_COUNT) + 1} 组镜头语言`) + } + const runFusionVideo = async (index: number) => { const shot = fusionShots[index] if (!shot?.first_image || !shot.last_image || !shot.action_text?.trim()) { @@ -989,6 +1057,16 @@ export function FrameLightbox({ jobId, frames, generatedVideos = [], activeIndex {fusionReadyCount}/6 可生成 +