diff --git a/.memory/worklog.json b/.memory/worklog.json index 30de37b..79ed182 100644 --- a/.memory/worklog.json +++ b/.memory/worklog.json @@ -1,12 +1,5 @@ { "entries": [ - { - "files_changed": 1, - "hash": "8b1e0bd", - "message": "auto-save 2026-05-14 17:31 (~1)", - "ts": "2026-05-14T17:32:12+08:00", - "type": "commit" - }, { "files_changed": 1, "message": "Codex 会话活跃 · 最近命令:codex · 1 项未提交变更 · 最近提交:auto-save 2026-05-14 17:31 (~1)", @@ -3268,6 +3261,13 @@ "type": "session-heartbeat", "message": "Codex 会话活跃 · 最近命令:codex · 分支 main · 4 项未提交变更 · 最近提交:refactor: merge storyboard workflow into segment board", "files_changed": 4 + }, + { + "ts": "2026-05-17T12:28:26+08:00", + "type": "commit", + "message": "auto-save 2026-05-17 12:28 (~4)", + "hash": "08f1837", + "files_changed": 4 } ] } diff --git a/.project.json b/.project.json index e27086d..2911927 100644 --- a/.project.json +++ b/.project.json @@ -27,7 +27,7 @@ "type": "web_login" } ], - "description": "SKG 信息流广告快速复刻分镜生产板:左侧素材输入列按文件任务管理素材;右侧单一分镜生产板块按分镜纵向排列,每个分镜从上到下对应音频分镜文案、关键元素/抽帧生成、视频生成候选,完整视频合成暂为待接入入口。", + "description": "SKG 信息流广告快速复刻分镜生产板:粘贴/上传素材后点击开始生产,自动下载、抽帧、解析音频、扫描关键元素并生成分镜初稿;用户在每张分镜卡中选择关键元素生成提取图和 6 视图,审核文案后可单条或全量生成视频候选,完整视频合成暂为待接入入口。", "kind": "app", "name": "SKG Marketing Studio / SKG 营销内容工作台", "ownership": "company", diff --git a/RULES.md b/RULES.md index f912074..596b700 100644 --- a/RULES.md +++ b/RULES.md @@ -11,7 +11,7 @@ - 详见 `CLAUDE.md` 立项决策段 + `.memory/plan.md` 七步管线拆解 - 风格:`04-Dark-Gallery-Ambient`(路径:`~/Projects/research/20260305-网页风格库/04-Dark-Gallery-Ambient.md`) - 第一冲刺:步骤 1-4(下载 / 拆轨 / 关键帧 / ASR+翻译) -- 当前产品方向(2026-05-17 确认):优先做信息流广告快速复刻产出,不再把主界面做成可视化流程节点;主界面为“左侧素材输入列 + 右侧单一分镜生产板块”。分镜生产板块内,每个分镜从上到下依次包含音频分镜文案、该分镜关键元素 / 抽帧生成、该分镜视频生成;可继续追加草稿分镜,绑定关键帧后保存并生成候选视频。完整视频合成入口保留为待接入能力。 +- 当前产品方向(2026-05-17 确认):优先做信息流广告快速复刻产出,不再把主界面做成可视化流程节点;主界面为“左侧素材输入列 + 右侧单一分镜生产板块”。用户粘贴链接或上传素材后点击“开始”,系统自动下载、抽帧、解析音频、扫描关键元素并生成分镜初稿;分镜生产板块内,每个分镜从上到下依次包含音频分镜文案、该分镜关键元素 / 抽帧生成、该分镜视频生成。用户在关键元素候选里选择后生成元素提取图和 6 视图,审核分镜规划后可单条生成或“生成全部视频”。完整视频合成入口保留为待接入能力。 ## 部署事实 - 平台:VPS `76.13.31.179`(Ubuntu 24.04 / Docker Compose / Coolify Traefik) diff --git a/api/main.py b/api/main.py index 80ad454..49e4546 100644 --- a/api/main.py +++ b/api/main.py @@ -435,6 +435,8 @@ class AudioScript(BaseModel): source_text: str = "" source_zh: str = "" rewritten_text: str = "" + speaker_profile: str = "" + rhythm_profile: str = "" product_brief: str = "" rewrite_model: str = "" voice_provider: str = "" @@ -1763,6 +1765,26 @@ def _fallback_audio_script(segments: list[TranscriptSegment], target_seconds: fl ) +def _audio_delivery_profile(segments: list[TranscriptSegment], target_seconds: float, voice_id: str) -> tuple[str, str]: + duration = max(float(target_seconds or 0), _segment_duration(segments), 0.0) + words = sum(len([w for w in s.en.replace("\n", " ").split(" ") if w.strip()]) for s in segments) + sentence_count = len([s for s in segments if (s.en or s.zh).strip()]) + wpm = int(round(words / max(duration, 1.0) * 60)) if words else 0 + avg_sentence = duration / sentence_count if sentence_count else 0.0 + speaker = ( + f"按原素材的短视频单人旁白处理;当前近似音色为 {voice_id},用于保持商业口播的亲近感和节奏。" + if voice_id + else "按原素材的短视频单人旁白处理;等待选择 TTS 音色。" + ) + rhythm = ( + f"源音频约 {duration:.1f}s,{sentence_count} 个语义段,语速约 {wpm} wpm,平均每段 {avg_sentence:.1f}s;" + "新配音按相同时长、短句停顿和信息密度改写。" + if duration > 0 and sentence_count + else "源音频节奏信息不足;新配音按 8-12 秒信息流广告口播节奏生成。" + ) + return speaker, rhythm + + def _rewrite_audio_script_sync(segments: list[TranscriptSegment], target_seconds: float = 12.0) -> tuple[str, str]: fallback = _fallback_audio_script(segments, target_seconds) if not LLM_API_KEY: @@ -1889,6 +1911,7 @@ def _build_audio_script_sync(job_id: str, segments: list[TranscriptSegment], tar duration = max(float(target_seconds or 0), _segment_duration(segments), 4.0) rewritten, rewrite_error = _rewrite_audio_script_sync(segments, duration) selected_voice_id = _choose_minimax_voice_id() + speaker_profile, rhythm_profile = _audio_delivery_profile(segments, duration, selected_voice_id) voice_url = "" voice_error = "" try: @@ -1902,6 +1925,8 @@ def _build_audio_script_sync(job_id: str, segments: list[TranscriptSegment], tar source_text=source_text, source_zh=source_zh, rewritten_text=rewritten, + speaker_profile=speaker_profile, + rhythm_profile=rhythm_profile, product_brief=AUDIO_PRODUCT_BRIEF, rewrite_model=AUDIO_REWRITE_MODEL, voice_provider="minimax", @@ -2472,6 +2497,8 @@ async def trigger_transcribe(job_id: str, bg: BackgroundTasks) -> Job: manage_job_status = job.status != "splitting" audio_payload = AudioScript( status="rewriting", + speaker_profile="正在分析原音频讲话人和口播节奏…", + rhythm_profile="正在按原音频时长、语速和停顿生成 SKG 产品配音脚本…", product_brief=AUDIO_PRODUCT_BRIEF, rewrite_model=AUDIO_REWRITE_MODEL, voice_provider="minimax", diff --git a/docs/source-analysis.html b/docs/source-analysis.html index d9b7328..7ce639a 100644 --- a/docs/source-analysis.html +++ b/docs/source-analysis.html @@ -569,12 +569,12 @@

业务管线

-

当前产品方向收敛为“信息流广告快速复刻分镜生产板”:主界面左侧是素材输入列,右侧是单一分镜生产板块。每个分镜卡片从上到下对应音频分镜文案、该分镜关键元素 / 抽帧生成、该分镜视频生成,用户可以继续追加草稿分镜并在绑定关键帧后保存和生成候选视频。它不再保留单独的右侧空白画布,也不再把音频、元素和合成拆成多列。

+

当前产品方向收敛为“信息流广告快速复刻分镜生产板”:主界面左侧是素材输入列,右侧是单一分镜生产板块。用户粘贴链接或上传素材后点击“开始”,系统自动下载、抽帧、解析音频、扫描关键元素并生成分镜初稿;每个分镜卡片从上到下对应音频分镜文案、该分镜关键元素 / 抽帧生成、该分镜视频生成。用户在关键元素候选里选择后生成元素提取图和 6 视图,审核分镜规划后可单条生成或“生成全部视频”。它不再保留单独的右侧空白画布,也不再把音频、元素和合成拆成多列。

-
1

素材输入列

TK / 信息流视频链接或本地上传;每个素材就是一个文件任务,可在列内切换。

-
2

分镜文案

解析音频后结合产品内容填写新剧情、产品融入、动作 / 镜头;分镜卡片从上到下无限追加。

-
3

关键元素

在同一张分镜卡内绑定或使用关键帧,直接查看识别元素、提取元素图和生成图。

-
4

视频生成

在同一张分镜卡底部选择模型并生成本分镜候选片段;完整 mp4 合成接口当前仍是占位。

+
1

开始生产

TK / 信息流视频链接或本地上传;点击“开始”后创建任务,下载完成后自动进入抽帧和音频处理。

+
2

自动规划

抽帧后逐帧 Vision 扫描关键元素,同时音频按原时长、语速和停顿生成 SKG 英文产品口播与配音。

+
3

人工选择元素

每张分镜卡展示候选元素;用户选择后生成独立提取图和 6 视图,作为后续产品融合/视频生成参考。

+
4

单条 / 全量生成

审核分镜文案后,可在单张分镜内生成视频,也可点击“生成全部视频”;生成时默认带入四张 SKG 产品角度图。

@@ -587,8 +587,8 @@ 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 隔离的 selectedFrames/音频条/生成任务状态;主渲染为全屏素材输入列 + 分镜生产板块。 - web/components/ad-recreation-board.tsx信息流广告分镜生产板:左侧素材输入;右侧按分镜纵向排列,每张分镜卡内部依次承载音频分镜文案、关键元素 / 抽帧生成、视频生成候选;追加草稿分镜可绑定关键帧后保存。 + web/app/page.tsx产品工作台主状态:jobs、activeJobId、按 job 隔离的 selectedFrames/音频条/生成任务状态;主渲染为全屏素材输入列 + 分镜生产板块;新增“开始生产”编排状态,负责下载完成后自动触发抽帧、音频处理、逐帧 Vision 扫描和分镜初稿保存;视频生成时默认复制四张 SKG 产品角度图作为参考。 + web/components/ad-recreation-board.tsx信息流广告分镜生产板:左侧素材输入;右侧按分镜纵向排列,每张分镜卡内部依次承载音频分镜文案、关键元素 / 抽帧生成、视频生成候选;关键元素候选可点击生成提取图 + 6 视图;支持单条生成和“生成全部视频”。 web/app/login/page.tsx生产登录页:访问账号/访问密钥表单、保持登录、错误/成功状态;当前只在原版 Digital Oasis 动态背景上叠加一个组合登录框,桌面端左侧是动态角色,右侧是图标化登录表单;面板左上角展示官网 SKG 字标和中文“营销内容工作台”系统标识。 web/app/login/layout.tsx登录路由专属 layout:覆盖全站默认网页标题和描述为空,避免 /login 继承工作台 metadata 后在页面源码里继续出现登录界面文字以外的文案。 web/components/login/oasis-canvas.tsx登录页全屏动态视觉层:用 iframe 直接承载下载包 web/public/oasis-source/index.html 的原 WebGPU / Three.js 草场源码;父级登录页只覆盖自己的文案和表单,并在捕获阶段把全局鼠标坐标同时用原生事件和 postMessage 转发给 iframe,避免登录面板或输入框遮挡时草地失去鼠标响应。 @@ -624,8 +624,9 @@
前端主链路:
 web/app/page.tsx
   -> 分镜生产板:web/components/ad-recreation-board.tsx
+  -> 开始生产:创建/激活 job → 自动抽帧 → 自动音频处理 → 自动 Vision 扫描 → 自动写入分镜初稿
   -> 左侧素材输入列 + 右侧分镜卡片列表
-  -> 每张分镜卡:音频分镜文案 → 关键元素 / 抽帧生成 → 视频生成候选
+  -> 每张分镜卡:音频分镜文案 → 候选元素选择 / 提取图 / 6 视图 → 单条或全部视频生成
   -> 底部音频条:web/components/audio-strip.tsx(原音频播放 / 指针 / 英文 / 中文 / 波形 / 英文改写稿)
   -> 旧节点/深度素材面板:web/components/nodes/index.tsx、web/components/lightbox.tsx、web/components/storyboard-workbench.tsx(底层保留,当前不作为主入口)
   -> API 契约:web/lib/api.ts
@@ -643,11 +644,11 @@ api/main.py
           
你看到的区域信息流广告分镜生产板
主要源码AdRecreationBoard in web/components/ad-recreation-board.tsx;状态、轮询和接口回写仍在 web/app/page.tsx
-
适合怎么描述“素材输入列、分镜生产板块、分镜卡片的文案/元素/视频生成层要如何调整”。
+
适合怎么描述“开始生产后哪些步骤自动跑、素材输入列、分镜生产板块、分镜卡片的文案/元素/视频生成层要如何调整”。
你看到的区域单个分镜卡片
-
主要源码StoryboardSegmentCardDraftSegmentCard in web/components/ad-recreation-board.tsx;复用 updateStoryboardaddElementcutoutElementgenerateStoryboardVideo 等接口。
+
主要源码StoryboardSegmentCardDraftSegmentCard in web/components/ad-recreation-board.tsx;复用 updateStoryboardaddElementcutoutElementgenerateSubjectAssetsgenerateStoryboardVideo 等接口。
适合怎么描述“每个分镜内部音频文案、关键元素和视频生成候选从上到下应该怎么对应”。
@@ -730,6 +731,8 @@ api/main.py source_text, source_zh, rewritten_text, + speaker_profile, + rhythm_profile, product_brief, rewrite_model, voice_provider: minimax, @@ -815,7 +818,7 @@ SubjectAsset { 上传视频POST /jobs/uploaduploadJob保存 source.mp4,然后同样进入下载完成状态。 删除输入视频DELETE /jobs/{id}deleteJob从任务队列、URL 和磁盘 jobs/<id> 目录移除整个 job,包括源视频、关键帧、元素提取图和生成视频。 解析视频POST /jobs/{id}/analyze?frames=&target=&mode=&quality=analyzeJob拆轨 + 目标化抽关键帧。默认 frames=12target 支持透明骨架人、综合、清晰主体、转场变化、表情瞬间、动作峰值;当前 UI 默认 transparent_human。透明骨架人目标现在只走本地清晰度、中心主体、对比度、画面变化和 pHash 去重,不在抽帧阶段逐帧调用 Vision;mode=append 追加新关键帧;quality=auto 为展示友好档,最高只自动选择精细,不会自动上极准;极准保留为手动选择。抽帧开始时同步拆出 audio.wav 并启动音频处理线程。多个抽帧请求进入后端队列顺序处理。 - 音频文案轨POST /jobs/{id}/transcribetriggerTranscribe若尚未拆轨,先从 source.mp4 提取 audio.wav 并回填 source_audio_url;随后用原音频实际秒数估算英文词数,按 AUDIO_PRODUCT_BRIEF 生成有趣、自然的 SKG 英文产品介绍 audio_script.rewritten_text。ASR/翻译结果保留为改前对照和节奏参考;如果 ASR 不可用,也会用原音频时长继续生成产品口播。配置 MINIMAX_API_KEY 后调用 MiniMax T2A,并从 MINIMAX_TTS_VOICE_POOL 随机选择男声、女声或成熟声生成 audio_script.voice_url。 + 音频文案轨POST /jobs/{id}/transcribetriggerTranscribe若尚未拆轨,先从 source.mp4 提取 audio.wav 并回填 source_audio_url;随后用原音频实际秒数估算英文词数,按 AUDIO_PRODUCT_BRIEF 生成有趣、自然的 SKG 英文产品介绍 audio_script.rewritten_text,并写入 speaker_profilerhythm_profile 作为讲话人 / 节奏参考。ASR/翻译结果保留为改前对照;如果 ASR 不可用,也会用原音频时长继续生成产品口播。配置 MINIMAX_API_KEY 后调用 MiniMax T2A,并从 MINIMAX_TTS_VOICE_POOL 随机选择男声、女声或成熟声生成 audio_script.voice_url。 原始音频文件GET /jobs/{id}/audio.wavsourceAudioUrl返回拆轨得到的 wav;底部 AudioStrip 拉取该文件,用 Web Audio API 解码并计算波形峰值。原音频播放器驱动时间轴,播放时全局指针和当前字幕节点内指针同步移动。 改写配音文件GET /jobs/{id}/audio-script.mp3apiAssetUrl(job.audio_script.voice_url)返回 MiniMax T2A 生成的英文 mp3。没有配置 MiniMax 或生成失败时该文件不存在,但英文改写文案仍会保存在 audio_script.rewritten_text。 手动加帧POST /jobs/{id}/frames?t=addManualFrame按视频时间戳抽一帧,index 递增但 frames 按 timestamp 排序。 @@ -848,7 +851,7 @@ SubjectAsset { 分镜生产板 - 承载当前主路径:素材输入列按文件任务管理素材;分镜生产板块按分镜纵向排列;每张分镜卡从上到下编辑音频分镜文案、确认该分镜关键元素、生成本分镜候选视频;底部仅汇总完整视频合成入口。 + 承载当前主路径:素材输入列按文件任务管理素材;点击“开始”后自动触发下载后抽帧、音频处理、Vision 扫描和分镜初稿;分镜生产板块按分镜纵向排列;每张分镜卡从上到下编辑音频分镜文案、选择关键元素并生成提取图/6 视图、生成本分镜候选视频;顶部可“生成全部视频”,底部仅汇总完整视频合成入口。 不要再拆回多个画布节点;不要恢复右侧空白画布占位。 web/components/ad-recreation-board.tsxweb/app/page.tsx @@ -866,7 +869,7 @@ SubjectAsset { 候选片段 - 生成视频结果直接显示在对应分镜卡片的视频生成层,可作为后续完整合成输入。 + 生成视频结果直接显示在对应分镜卡片的视频生成层;单条生成和“生成全部视频”都会默认带入四张 SKG 产品角度图,已生成的关键元素 6 视图会作为主体参考图。 不要把 Compose 提前变成最终剪辑台;最终合成仍是占位。 /storyboard/videogenerated_videosAdRecreationBoard @@ -885,9 +888,10 @@ SubjectAsset {
  • 手动按时间戳加关键帧。
  • 关键帧清洗水印,全图或区域清洗。
  • Vision 识别关键帧,输出 scene、objects、style、suggested_prompt,并作为主体候选来源。
  • -
  • 主体候选确认、改名、删除和主体资产包生成。
  • +
  • “开始生产”会在下载完成后自动抽帧、触发音频处理、逐帧 Vision 扫描并保存分镜初稿。
  • +
  • 主体候选确认、改名、删除和主体资产包生成;当前分镜卡可点击候选元素直接生成提取图 + 6 视图。
  • 分镜工作台 4 图槽和改造说明自动保存。
  • -
  • 音频文案轨:点击提取音频后按原音频时长自动生成 SKG 英文产品介绍口播;配置 MiniMax 后从男声、女声、成熟声池随机生成自然英文配音 mp3。底部音频条可播放原音频并用指针逐段对齐字幕节点。
  • +
  • 音频文案轨:点击开始或提取音频后按原音频时长、语速和停顿自动生成 SKG 英文产品介绍口播;配置 MiniMax 后从男声、女声、成熟声池随机生成自然英文配音 mp3。底部音频条可播放原音频并用指针逐段对齐字幕节点。
  • nano-banana-pro image-to-image 生图。
  • @@ -912,7 +916,7 @@ SubjectAsset {

    改分镜生产板

    -

    “我在素材输入列或右侧分镜生产板块,这里应该怎么展示、编辑、保存和触发下一步。”

    +

    “我在素材输入列或右侧分镜生产板块,开始生产后哪些步骤自动跑,哪些步骤留给人工选择和审核。”

    改分镜卡片层级

    @@ -937,6 +941,18 @@ SubjectAsset {

    变更记录

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

    +
    +
    +

    2026-05-17 · 开始生产自动编排

    + UI + Workflow +
    +
    +

    问题:用户希望粘贴视频链接后点击一次“开始”,系统就自动完成素材准备:抽帧、音频分析、关键元素扫描和分镜初稿;人工只负责判断规划是否合理、选择关键元素、再单条或全量生成视频。

    +

    改动:web/app/page.tsx 新增开始生产编排状态:创建/激活 job 后,下载完成自动触发 analyzeJobtriggerTranscribe,关键帧出来后逐帧调用 describeFrame 并用 updateStoryboard 保存分镜初稿。视频生成时若分镜未显式选择产品图,会自动复制四张 desktop-skg-product-angle-01..04 作为 SKG 产品真源,并把已生成的关键元素 6 视图作为主体参考。AdRecreationBoard 把导入按钮改为“开始”,分镜卡里的候选元素可点击生成提取图 + 6 视图,顶部新增“生成全部视频”。AudioScript 新增 speaker_profilerhythm_profile,用于展示讲话人 / 节奏参考。

    +

    影响:web/app/page.tsxweb/components/ad-recreation-board.tsxweb/components/nodes/index.tsxweb/lib/api.tsapi/main.pyRULES.md.project.jsondocs/source-analysis.html。后续需求应区分“开始生产自动编排”和“人工审核/选择/生成”的边界。

    +
    +

    2026-05-17 · 合并为单一分镜生产板块

    diff --git a/web/components/ad-recreation-board.tsx b/web/components/ad-recreation-board.tsx index b8cf8f3..083f739 100644 --- a/web/components/ad-recreation-board.tsx +++ b/web/components/ad-recreation-board.tsx @@ -405,6 +405,12 @@ export function AdRecreationBoard({
    {audioPreview(job)}
    + {(job?.audio_script?.speaker_profile || job?.audio_script?.rhythm_profile) && ( +
    + {job.audio_script.speaker_profile &&
    讲话人:{job.audio_script.speaker_profile}
    } + {job.audio_script.rhythm_profile &&
    节奏:{job.audio_script.rhythm_profile}
    } +
    + )}