feat(voices): 配音素材卡片真实播放 + 预置/克隆音色 TTS 试听 #1544

Merged
auto-approve-bot merged 1 commits from feat/material-card-audio-preview into develop 2026-08-29 21:56:01 +08:00
Owner

背景

配音库三个 tab 的卡片播放全部改为真实出声,共享一套真实播放 hook:

  1. 配音素材 tab:卡片原本只有缩略图/文件名/删除按钮,无播放控件 → 新增播放区,直接播放用户上传音频(asset.file_url
  2. 预置音色 / 我的克隆 tab:原 useAudioPlayer 用 setInterval 模拟进度、无 Audio 对象,点了完全不出声 → 改为调 POST /tts/preview 合成本示例文案后真实播放

改动文件

文件 改动
pages/voices/hooks/useAudioPlayer.ts 重写:废弃 setInterval 假播放,改为真实 new Audio() 播放。素材走 file_url 直链;音色走 previewTts 合成(固定示例文案「你好呀,欢迎使用小虾智剪…」),合成结果按 voiceId 内存 Map 缓存,二次试听不重复合成。互斥播放、timeupdate 进度、loadedmetadata 真实时长、ended 复位、暂停/恢复/seek
pages/voices/components/MaterialVoiceTab.tsx 卡片加波形条 + 播放区(xx-voice-wave/xx-voice-controls,视觉对齐预置音色),播放状态受控由页面 hook 下发;file_url 缺失按钮置灰
pages/voices/components/VoiceCard.tsx 预置音色卡片:新增 isLoading(LoadingOutlined 合成中 spin);非播放时点进度条触发播放、播放中 seek;时长为 0 时时间显示「试听」
pages/voices/components/clone-voice-card/CardFooter.tsx 克隆卡片:合成中 loading;进度条可点击 seek;进度按真实时长计算
ClonedVoiceTab.tsx / PresetVoiceTab.tsx / CloneVoiceCard.tsx props 接线:toggle 无参化、loadingId、playDuration、onSeek
pages/voices/VoiceLibrary.tsx 三个 tab 共用同一个 hook 实例 → 全局互斥;切 tab/离开页面停止(沿用 handleTabChange stopPlayback)
pages/voices/voices.css vmat-card 内波形/控制区间距、播放按钮 disabled 置灰样式
components/MaterialVoiceCard.tsx 删除:废弃旧卡片(仅 smoke 测试引用,页面已用 MaterialVoiceTab)
test/pages/voices/hooks/useAudioPlayer.test.ts 重写:mock Audio + previewTts,11 条用例覆盖素材直链播放/TTS 缓存/失败复位/互斥/seek/ended/stop

关键行为

  • 互斥:三 tab 同一 hook 实例,点别的卡片/切 tab/离开页面都停止
  • TTS 试听:voice_id 传预置 voiceId 或克隆 profile UUID(后端自动解析);克隆未完成后端 400,错误文案(含「音色克隆尚未完成,请稍后再试」)由 apiClient 拦截器统一 toast
  • 不播 sample_url:克隆试听走 TTS 合成,不播用户原声样本
  • TTS 弹窗(AI 配音)逻辑未动;voice-materials 独立管理页的 hook 未动

验收

  • tsc / ESLint(--max-warnings=0) 通过
  • 单元测试 12 条全过
  • staging 实测:素材卡片真实出声、预置/克隆音色 1~3s 合成后出声、二次播放秒响、互斥/切 tab 停止
## 背景 配音库三个 tab 的卡片播放全部改为**真实出声**,共享一套真实播放 hook: 1. **配音素材 tab**:卡片原本只有缩略图/文件名/删除按钮,无播放控件 → 新增播放区,直接播放用户上传音频(`asset.file_url`) 2. **预置音色 / 我的克隆 tab**:原 `useAudioPlayer` 用 setInterval 模拟进度、无 Audio 对象,点了完全不出声 → 改为调 `POST /tts/preview` 合成本示例文案后真实播放 ## 改动文件 | 文件 | 改动 | |---|---| | `pages/voices/hooks/useAudioPlayer.ts` | **重写**:废弃 setInterval 假播放,改为真实 `new Audio()` 播放。素材走 file_url 直链;音色走 previewTts 合成(固定示例文案「你好呀,欢迎使用小虾智剪…」),合成结果按 voiceId 内存 Map 缓存,二次试听不重复合成。互斥播放、timeupdate 进度、loadedmetadata 真实时长、ended 复位、暂停/恢复/seek | | `pages/voices/components/MaterialVoiceTab.tsx` | 卡片加波形条 + 播放区(`xx-voice-wave`/`xx-voice-controls`,视觉对齐预置音色),播放状态受控由页面 hook 下发;file_url 缺失按钮置灰 | | `pages/voices/components/VoiceCard.tsx` | 预置音色卡片:新增 isLoading(LoadingOutlined 合成中 spin);非播放时点进度条触发播放、播放中 seek;时长为 0 时时间显示「试听」 | | `pages/voices/components/clone-voice-card/CardFooter.tsx` | 克隆卡片:合成中 loading;进度条可点击 seek;进度按真实时长计算 | | `ClonedVoiceTab.tsx` / `PresetVoiceTab.tsx` / `CloneVoiceCard.tsx` | props 接线:toggle 无参化、loadingId、playDuration、onSeek | | `pages/voices/VoiceLibrary.tsx` | 三个 tab 共用同一个 hook 实例 → 全局互斥;切 tab/离开页面停止(沿用 handleTabChange stopPlayback) | | `pages/voices/voices.css` | vmat-card 内波形/控制区间距、播放按钮 disabled 置灰样式 | | `components/MaterialVoiceCard.tsx` | **删除**:废弃旧卡片(仅 smoke 测试引用,页面已用 MaterialVoiceTab) | | `test/pages/voices/hooks/useAudioPlayer.test.ts` | 重写:mock Audio + previewTts,11 条用例覆盖素材直链播放/TTS 缓存/失败复位/互斥/seek/ended/stop | ## 关键行为 - **互斥**:三 tab 同一 hook 实例,点别的卡片/切 tab/离开页面都停止 - **TTS 试听**:voice_id 传预置 voiceId 或克隆 profile UUID(后端自动解析);克隆未完成后端 400,错误文案(含「音色克隆尚未完成,请稍后再试」)由 apiClient 拦截器统一 toast - **不播 sample_url**:克隆试听走 TTS 合成,不播用户原声样本 - **TTS 弹窗(AI 配音)逻辑未动**;voice-materials 独立管理页的 hook 未动 ## 验收 - [x] tsc / ESLint(--max-warnings=0) 通过 - [x] 单元测试 12 条全过 - [ ] staging 实测:素材卡片真实出声、预置/克隆音色 1~3s 合成后出声、二次播放秒响、互斥/切 tab 停止
xiaoxia added 1 commit 2026-08-29 21:35:08 +08:00
feat(voices): 配音素材卡片真实播放 + 预置/克隆音色 TTS 试听,三 tab 共用真实播放 hook
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2m42s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m45s
CI/CD Pipeline / Unit Tests (pull_request) Has been skipped
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 3m49s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Successful in 1m25s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Successful in 1m29s
CI/CD Pipeline / PR Build Worker Image (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 2m5s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m58s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 8m14s
AI Code Review / AI Code Review (pull_request) Successful in 8m50s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 9m15s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 16m47s
CI/CD Pipeline / Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 28s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 3m27s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 6m3s
76aa23d61f
- 素材 tab 卡片新增播放区(播放按钮+波形+进度条+时间,视觉对齐预置音色卡片),
  复用 voice-materials 的 new Audio 真实播放,file_url 为空时按钮置灰
- 废弃 pages/voices/hooks/useAudioPlayer.ts 的 setInterval 假播放,重写为
  统一真实播放 hook:素材直链播放 + 预置/克隆音色 POST /tts/preview 合成试听
  (示例文案固定,结果按 voiceId 内存缓存),timeupdate 驱动进度、
  loadedmetadata 取真实时长、ended 复位、暂停/恢复/seek
- 合成中按钮 LoadingOutlined 禁点;互斥播放(切卡片/切 tab/离开页面停止)
- 合成失败 toast 由 apiClient 拦截器统一处理(含「克隆尚未完成」文案)
- 删除废弃 MaterialVoiceCard.tsx(仅 smoke 测试引用),同步更新测试

🚀 预览环境已部署

项目 详情
PR号 #1544
预览链接 https://pr-1544.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1544 | | 预览链接 | [https://pr-1544.preview.xiaoxiajianji.com](https://pr-1544.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot approved these changes 2026-08-29 21:41:08 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-08-29 21:41:08 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot merged commit 0a061af582 into develop 2026-08-29 21:56:01 +08:00
auto-approve-bot deleted branch feat/material-card-audio-preview 2026-08-29 21:56:01 +08:00

🗑️ 预览环境已清理

PR #1544 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1544 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.