[P0] 批量预览/前端五连修:配音列表显时长、预览时长失真45s、独立配音预览无声、结果页竖屏比例、封面按钮交互 #1750

Closed
opened 2026-09-06 21:26:50 +08:00 by xiaoxia · 4 comments
Owner

背景

2026-09-06 21:00 staging 实测批量 3 视频,用户截图反馈 5 个前端问题。后端成片为 1080×1920 竖屏、时长 10.6/21.0/10.6s,前端显示与实际严重不符。

问题 1:配音选择列表不显示配音时长

  • 用户在"视频 N 的配音"选择器里无法区分每个配音多长(截图:仅显示名字+文件大小)
  • Step5VoiceSelect.tsx 已有 getDuration() 工具函数但卡片底部只渲染了文件大小,未渲染时长
  • 要求:每个配音卡片在文件大小旁显示时长(如 "14.7秒 / 345 KB");AI 音色显示"按文本合成";时长为 0 的历史数据做兜底显示

问题 2:批量预览 3 个视频都显示 45 秒(与成片完全不符)

  • 截图 3 张卡片均显示 00:00/00:45;实际 plan 片段总时长 15.9s、成片 10.6/21.0s
  • 根因:FrontendPreviewPlayer.buildPlaybackSegments 的 fallback 本地模拟分支用 tplSeg.duration_max 兜底(模板段最大时长导致 3 段≈45s),或 serverClips 未生效(assetMap 按 assets 列表查 id,若后端变体 plan 引用的素材不在前端 previewAssets 内则 segments 为空→整卡回退本地模拟)
  • 要求:①排查 #1748 上线后 serverClips 实际命中率(variant-plans 接口返回 clips 的 asset_id 是否都在前端素材列表内;asset 缺失时前端应补拉素材详情而不是静默回退);②预览总时长必须等于 serverClips 片段时长之和(与成片同源);③降级本地模拟分支的段时长改用素材实际 duration 而非模板 duration_max;④严禁再出现预览时长与成片时长不一致

问题 3:批量预览卡片听不到配音,放的是素材原声(独立配音模式)

  • GeneratePage.tsx:138-170 配音预览 useEffect 只追踪共用 selectedVoice(voiceMaterials.find(m=>m.id===selectedVoice)),独立配音模式下 selectedVoice 为空、3 个视频各自的 voiceLibraryIds[0/1/2] 完全没接入
  • 且独立模式每个视频配音不同,CanvasPreviewGrid 只传了一条 voiceAudioUrl 给所有卡片,架构上不支持
  • 要求:①独立配音模式下,每个预览卡片挂载各自变体对应的配音 URL(voiceLibraryIds[i]),共用模式维持一条;②有配音时素材视频必须静音、配音正确播放(现在用户听到的是素材原声,说明 voiceAudioUrl 为空导致 video 未静音);③配音时长与预览片段时长不一致时,预览播放行为与成片对齐(成片侧 #1749 修复后视频按配音长度)

问题 4:生成中/结果页播放器是横屏 16:9,竖屏成片左右大黑边

  • 后端成片 DB 确认 output_width=1080/output_height=1920(竖屏正确),但结果页/生成中播放组件按 16:9 横框渲染,竖屏视频挤在中间
  • 要求:视频播放器容器按视频实际宽高比自适应(9:16 竖屏竖放),批量生成网格、单视频结果、生成中预览全部覆盖;N=1 旧流程零回归

问题 5:封面"一键全部自动生成"要点 3 次 + 按钮文字换行难看

  • 截图:批量 3 视频,点"一键全部自动生成"只出 1 个封面,要点 3 次才全部生成;按钮文案在窄卡片里挤成两行
  • 要求:①一键按钮一次触发所有未生成封面的变体(批量循环/并行调用,失败的单个提示不阻塞其他);②按钮文案一行显示不换行(加 white-space:nowrap、宽度自适应或图标按钮+tooltip)

通用要求(用户原话)

  • 不要修一个弄坏另外一个:N=1 单视频全链路零回归(配音选择、预览、结果播放、封面)
  • 多次修改累积的冗余代码一并删除(如失效的 variantSeed 模拟分支确认 serverClips 稳定后应删除而非长期并存、注释掉的死代码等),保持代码干净,不留技术债
  • CI 全绿部署 staging

参考

  • 截图见项目群(配音列表/45秒预览/横屏结果页/封面按钮)
  • 相关代码:apps/web/src/pages/generate/components/Step5VoiceSelect.tsx(卡片渲染)、FrontendPreviewPlayer.tsx(buildPlaybackSegments/配音 audio 同步)、CanvasPreviewGrid.tsx(variantClips/voiceAudioUrl 单条)、GeneratePage.tsx:134-170(previewVoiceAudioUrl 只接共用配音)、Step6CoverSettings.tsx:147(一键全部按钮)、结果页/生成中播放组件(BatchGenerationGrid/ProgressIndicator 等)
  • 配套后端工单:#1749(变体配音错位/成片截断/素材避让)
## 背景 2026-09-06 21:00 staging 实测批量 3 视频,用户截图反馈 5 个前端问题。后端成片为 1080×1920 竖屏、时长 10.6/21.0/10.6s,前端显示与实际严重不符。 ## 问题 1:配音选择列表不显示配音时长 - 用户在"视频 N 的配音"选择器里无法区分每个配音多长(截图:仅显示名字+文件大小) - `Step5VoiceSelect.tsx` 已有 getDuration() 工具函数但卡片底部只渲染了文件大小,未渲染时长 - 要求:每个配音卡片在文件大小旁显示时长(如 "14.7秒 / 345 KB");AI 音色显示"按文本合成";时长为 0 的历史数据做兜底显示 ## 问题 2:批量预览 3 个视频都显示 45 秒(与成片完全不符) - 截图 3 张卡片均显示 00:00/00:45;实际 plan 片段总时长 15.9s、成片 10.6/21.0s - 根因:FrontendPreviewPlayer.buildPlaybackSegments 的 fallback 本地模拟分支用 `tplSeg.duration_max` 兜底(模板段最大时长导致 3 段≈45s),或 serverClips 未生效(assetMap 按 assets 列表查 id,若后端变体 plan 引用的素材不在前端 previewAssets 内则 segments 为空→整卡回退本地模拟) - 要求:①排查 #1748 上线后 serverClips 实际命中率(variant-plans 接口返回 clips 的 asset_id 是否都在前端素材列表内;asset 缺失时前端应补拉素材详情而不是静默回退);②预览总时长必须等于 serverClips 片段时长之和(与成片同源);③降级本地模拟分支的段时长改用素材实际 duration 而非模板 duration_max;④严禁再出现预览时长与成片时长不一致 ## 问题 3:批量预览卡片听不到配音,放的是素材原声(独立配音模式) - `GeneratePage.tsx:138-170` 配音预览 useEffect 只追踪共用 selectedVoice(voiceMaterials.find(m=>m.id===selectedVoice)),独立配音模式下 selectedVoice 为空、3 个视频各自的 voiceLibraryIds[0/1/2] 完全没接入 - 且独立模式每个视频配音不同,CanvasPreviewGrid 只传了一条 voiceAudioUrl 给所有卡片,架构上不支持 - 要求:①独立配音模式下,每个预览卡片挂载各自变体对应的配音 URL(voiceLibraryIds[i]),共用模式维持一条;②有配音时素材视频必须静音、配音正确播放(现在用户听到的是素材原声,说明 voiceAudioUrl 为空导致 video 未静音);③配音时长与预览片段时长不一致时,预览播放行为与成片对齐(成片侧 #1749 修复后视频按配音长度) ## 问题 4:生成中/结果页播放器是横屏 16:9,竖屏成片左右大黑边 - 后端成片 DB 确认 output_width=1080/output_height=1920(竖屏正确),但结果页/生成中播放组件按 16:9 横框渲染,竖屏视频挤在中间 - 要求:视频播放器容器按视频实际宽高比自适应(9:16 竖屏竖放),批量生成网格、单视频结果、生成中预览全部覆盖;N=1 旧流程零回归 ## 问题 5:封面"一键全部自动生成"要点 3 次 + 按钮文字换行难看 - 截图:批量 3 视频,点"✨一键全部自动生成"只出 1 个封面,要点 3 次才全部生成;按钮文案在窄卡片里挤成两行 - 要求:①一键按钮一次触发所有未生成封面的变体(批量循环/并行调用,失败的单个提示不阻塞其他);②按钮文案一行显示不换行(加 white-space:nowrap、宽度自适应或图标按钮+tooltip) ## 通用要求(用户原话) - 不要修一个弄坏另外一个:N=1 单视频全链路零回归(配音选择、预览、结果播放、封面) - 多次修改累积的冗余代码一并删除(如失效的 variantSeed 模拟分支确认 serverClips 稳定后应删除而非长期并存、注释掉的死代码等),保持代码干净,不留技术债 - CI 全绿部署 staging ## 参考 - 截图见项目群(配音列表/45秒预览/横屏结果页/封面按钮) - 相关代码:apps/web/src/pages/generate/components/Step5VoiceSelect.tsx(卡片渲染)、FrontendPreviewPlayer.tsx(buildPlaybackSegments/配音 audio 同步)、CanvasPreviewGrid.tsx(variantClips/voiceAudioUrl 单条)、GeneratePage.tsx:134-170(previewVoiceAudioUrl 只接共用配音)、Step6CoverSettings.tsx:147(一键全部按钮)、结果页/生成中播放组件(BatchGenerationGrid/ProgressIndicator 等) - 配套后端工单:#1749(变体配音错位/成片截断/素材避让)
Author
Owner

补充(与后端 #1749 配套的时长规则)

用户明确:成片时长以配音时长为准,模板不再有时长概念。后端选片阶段改为按配音时长规划片段后,前端预览联动要求:

  1. 预览总时长 = 后端 serverClips 真实片段时长之和(后端修复后该值≈所选配音时长),前端严禁再用模板 segment 的 duration_min/duration_max 计算时长;FrontendPreviewPlayer fallback 本地模拟分支里 tplSeg.duration_max 相关逻辑一并删除(真实片段稳定后 fallback 分支整体清理,见问题2)
  2. 配音卡片时长显示(问题1)的数据直接来自 assets duration 字段,与后端选片目标一致,用户在第3步选配音时即可看到时长
  3. 独立配音模式 N 个配音时长不同 → N 个预览卡片总时长理应不同,这是正确行为,不要再显示成统一时长
## 补充(与后端 #1749 配套的时长规则) 用户明确:成片时长以配音时长为准,模板不再有时长概念。后端选片阶段改为按配音时长规划片段后,前端预览联动要求: 1. 预览总时长 = 后端 serverClips 真实片段时长之和(后端修复后该值≈所选配音时长),前端严禁再用模板 segment 的 duration_min/duration_max 计算时长;FrontendPreviewPlayer fallback 本地模拟分支里 `tplSeg.duration_max` 相关逻辑一并删除(真实片段稳定后 fallback 分支整体清理,见问题2) 2. 配音卡片时长显示(问题1)的数据直接来自 assets duration 字段,与后端选片目标一致,用户在第3步选配音时即可看到时长 3. 独立配音模式 N 个配音时长不同 → N 个预览卡片总时长理应不同,这是正确行为,不要再显示成统一时长
Author
Owner

补充(staging 实测铁证,2026-09-06 21:35)

45 秒预览精确根因(已实测,非推测)

  1. staging 实测后端 POST /api/v1/generation/variant-plans 返回 404——接口后端从未实现(#1749 已要求后端补实现,必须与正式生成共用同一套选片服务)
  2. 前端 useBatchVariantPlans catch 404 后静默降级 → FrontendPreviewPlayer buildPlaybackSegments 走 fallback 本地模拟分支:按素材个数逐素材建段、每段时长 = clip(模板 duration_max=5, duration_min=5, 素材时长) = 5s。本批用户选了 9 个素材 → 9 段 × 5s = 45s,三张卡完全一样——与截图 0:45 精确吻合
  3. 而正式成片只用模板 3 段 × 5.3s = 15.9s,预览和成片是两套数据

用户明确要求:预览视频与后端成片 100% 一致(预览即成片)。前端要求:

  1. 后端接口上线后,批量预览一律使用服务端返回的真实 clips(plan_id+片段顺序/素材/start_time/duration 与正式成片同源),删除 fallback 本地模拟整条分支(含 variantSeed 素材轮换/随机偏移模拟逻辑)
  2. 接口失败时严禁用假数据冒充预览:variant-plans 请求失败(404/400/网络错误)时,批量预览区显示明确错误提示(如「预览加载失败,请重试」),不允许静默展示本地模拟画面让用户误以为真——本次问题正是静默降级导致用户看到 45s 假预览
  3. 删除模板时长在前端的全部遗留:calculateTotalVideoDuration.ts 中 duration_max/duration_min 估算逻辑、useMyTemplates/templateLibrary 中 (duration_min+duration_max)/2 估算、SegmentList 时长范围展示等(模板不再有时长概念;8月19日的配音时长校验改为基于所选配音时长与真实片段总时长对比,具体以后端 clips 为准)
  4. 独立配音模式:N 个配音时长不同 → N 个预览时长理应不同,每个预览卡片挂载各自变体的配音(问题3),配音时长在配音卡片上直接显示(问题1)
  5. 验证标准:N=3 批量预览,三张卡时长各不相同且分别≈所选配音时长、画面片段与最终成片逐一对应
## 补充(staging 实测铁证,2026-09-06 21:35) ### 45 秒预览精确根因(已实测,非推测) 1. staging 实测后端 `POST /api/v1/generation/variant-plans` 返回 **404**——接口后端从未实现(#1749 已要求后端补实现,必须与正式生成共用同一套选片服务) 2. 前端 useBatchVariantPlans catch 404 后静默降级 → FrontendPreviewPlayer buildPlaybackSegments 走 fallback 本地模拟分支:**按素材个数逐素材建段、每段时长 = clip(模板 duration_max=5, duration_min=5, 素材时长) = 5s**。本批用户选了 **9 个素材 → 9 段 × 5s = 45s**,三张卡完全一样——与截图 0:45 精确吻合 3. 而正式成片只用模板 3 段 × 5.3s = 15.9s,预览和成片是两套数据 用户明确要求:**预览视频与后端成片 100% 一致(预览即成片)**。前端要求: 1. 后端接口上线后,批量预览一律使用服务端返回的真实 clips(plan_id+片段顺序/素材/start_time/duration 与正式成片同源),**删除 fallback 本地模拟整条分支**(含 variantSeed 素材轮换/随机偏移模拟逻辑) 2. **接口失败时严禁用假数据冒充预览**:variant-plans 请求失败(404/400/网络错误)时,批量预览区显示明确错误提示(如「预览加载失败,请重试」),不允许静默展示本地模拟画面让用户误以为真——本次问题正是静默降级导致用户看到 45s 假预览 3. 删除模板时长在前端的全部遗留:calculateTotalVideoDuration.ts 中 duration_max/duration_min 估算逻辑、useMyTemplates/templateLibrary 中 (duration_min+duration_max)/2 估算、SegmentList 时长范围展示等(模板不再有时长概念;8月19日的配音时长校验改为基于所选配音时长与真实片段总时长对比,具体以后端 clips 为准) 4. 独立配音模式:N 个配音时长不同 → N 个预览时长理应不同,每个预览卡片挂载各自变体的配音(问题3),配音时长在配音卡片上直接显示(问题1) 5. 验证标准:N=3 批量预览,三张卡时长各不相同且分别≈所选配音时长、画面片段与最终成片逐一对应
Author
Owner

时长方案定稿同步(方案A,与后端 #1749 对齐)

  1. 片段数由模板定死,配音总时长按片段等比分配到每段(后端选片时完成),预览 clips.duration 即分配后的真实段长;前端预览总时长一律 = 服务端 clips 时长之和(减去转场重叠如有),不再做任何前端侧时长估算
  2. 9月4日方案里「配音列表按 90%~110% 过滤」不再做:模板已无时长概念,没有可比对的预估总时长;改为配音卡片显示真实时长即可,素材/配音匹配性由后端 variant-plans 接口校验(素材覆盖不了配音时后端返回明确错误,前端原样展示错误提示)
  3. 配音卡片时长显示(问题1)数据取 assets.duration;独立配音 N 个配音时长不同 → N 个预览总时长不同,属正确行为
  4. 前端 calculateTotalVideoDuration.ts 等基于模板 duration_min/max 的估算逻辑全部删除,调用方改为使用服务端 clips 总时长
## ✅ 时长方案定稿同步(方案A,与后端 #1749 对齐) 1. 片段数由模板定死,配音总时长按片段等比分配到每段(后端选片时完成),预览 clips.duration 即分配后的真实段长;前端预览总时长一律 = 服务端 clips 时长之和(减去转场重叠如有),不再做任何前端侧时长估算 2. **9月4日方案里「配音列表按 90%~110% 过滤」不再做**:模板已无时长概念,没有可比对的预估总时长;改为配音卡片显示真实时长即可,素材/配音匹配性由后端 variant-plans 接口校验(素材覆盖不了配音时后端返回明确错误,前端原样展示错误提示) 3. 配音卡片时长显示(问题1)数据取 assets.duration;独立配音 N 个配音时长不同 → N 个预览总时长不同,属正确行为 4. 前端 calculateTotalVideoDuration.ts 等基于模板 duration_min/max 的估算逻辑全部删除,调用方改为使用服务端 clips 总时长
Author
Owner

⚠️ 规则更正(产品方 22:24 确认)

此前评论中「素材覆盖不了配音时后端返回错误、前端展示」的表述作废:不存在素材不足场景,后端任何时候都会产出与配音等长的成片(短素材循环/冻结兜底)。前端无需处理此类错误提示,只需处理接口参数错误/网络失败的通用重试提示。其余规则不变。

## ⚠️ 规则更正(产品方 22:24 确认) 此前评论中「素材覆盖不了配音时后端返回错误、前端展示」的表述**作废**:不存在素材不足场景,后端任何时候都会产出与配音等长的成片(短素材循环/冻结兜底)。前端无需处理此类错误提示,只需处理接口参数错误/网络失败的通用重试提示。其余规则不变。
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: xiaoxia/xiaoxia-saas#1750