You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
10 KiB
10 KiB
批量对话配音生成接口 - 实施总结
✅ 实施完成
日期: 2026-02-13
方案: 方案 2 - 新增专用接口
状态: 代码实现完成,待测试验证
📦 改动文件清单
| 文件 | 改动内容 | 状态 |
|---|---|---|
server/app/models/ai_job.py |
新增 AIJobType.DIALOGUE_VOICEOVER = 10 |
✅ |
server/app/schemas/ai.py |
新增 GenerateDialogueVoiceoversRequest |
✅ |
server/app/api/v1/ai.py |
新增 /generate-dialogue-voiceovers 路由 + 导入 |
✅ |
server/app/services/ai_service.py |
新增 generate_dialogue_voiceovers() 方法 |
✅ |
server/app/tasks/ai_tasks.py |
新增 generate_dialogue_voiceovers_task |
✅ |
docs/server/rfcs/145-dialogue-voiceovers-batch-generation.md |
RFC 文档 | ✅ |
docs/server/changelogs/2026-02-13-dialogue-voiceovers-batch-generation.md |
Changelog | ✅ |
🎯 核心功能
1. 新增 API 接口
端点: POST /api/v1/ai/generate-dialogue-voiceovers
参数:
{
dialogue_ids: string[]; // 对话 ID 列表(1-50 个)
voice_id: string; // 音色 ID
voice_name?: string; // 音色名称(可选)
speed: number; // 语速(0.25-4.0,默认 1.0)
volume: number; // 音量(0.0-2.0,默认 1.0)
pitch: number; // 音调(0.5-2.0,默认 1.0)
is_active: boolean; // 是否设为激活配音(默认 false)
model?: string; // 模型名称(可选)
}
响应:
{
"code": 200,
"message": "批量配音生成任务创建成功",
"data": {
"jobId": "550e8400-e29b-41d4-a716-446655440000",
"taskId": "celery-task-id",
"status": "pending",
"estimatedCredits": 150,
"dialogueCount": 10
}
}
2. 业务流程
用户请求
↓
AIService.generate_dialogue_voiceovers()
├─ 验证用户存在
├─ 验证对话数量(1-50)
├─ 获取所有对话并验证存在性
├─ 验证所有对话属于同一分镜
├─ 验证分镜权限(至少 viewer)
├─ 检查配额
├─ 获取模型配置(默认 AUDIO 类型)
├─ 计算所需积分(基于总字符数)
├─ 预扣积分
├─ 创建 AI Job(job_type=10)
└─ 启动 Celery 任务
↓
generate_dialogue_voiceovers_task (Celery 异步)
├─ 逐个处理对话:
│ ├─ 获取对话内容
│ ├─ 调用 TTS Provider 生成配音
│ ├─ 上传音频到 MinIO
│ └─ 写入 storyboard_voiceovers 表
├─ 收集成功/失败列表
└─ 更新任务状态为 COMPLETED
↓
返回结果
├─ successful_count: 成功数量
├─ failed_count: 失败数量
├─ successful_voiceovers: 成功的配音列表
└─ failed_dialogues: 失败的对话列表
3. 关键特性
✅ 批量处理
- 一次请求最多 50 个对话
- 推荐 10-20 个对话(性能平衡)
✅ 部分失败容错
- 单个对话失败不影响其他对话
- 已成功的配音保留
- 失败的对话在结果中标记
✅ 完整参数支持
voice_id+voice_name:音色配置speed,volume,pitch:TTS 参数is_active:自动激活配音
✅ 自动写入业务表
- 直接写入
storyboard_voiceovers表 - 无需手动上传或关联
✅ 权限验证
- 验证用户存在性
- 验证对话存在性
- 验证分镜权限(至少 viewer)
- 所有对话必须属于同一分镜
📊 与现有接口对比
| 特性 | /generate-voice(通用 TTS) |
/generate-dialogue-voiceovers(批量对话) |
|---|---|---|
| 用途 | 任意文本转语音 | 为分镜对话生成配音 |
| 输入 | 自由文本(text) |
对话 ID 列表(dialogue_ids) |
| 数据源 | 用户输入 | storyboard_dialogues 表 |
| 写入表 | ai_generation_results |
storyboard_voiceovers |
| 批量支持 | ❌ 单次单个 | ✅ 单次最多 50 个 |
| 分镜关联 | 可选 | 强制验证 |
| 权限验证 | 用户存在性 | 分镜权限 |
| 失败策略 | 全部失败 | 部分失败继续 |
| 参数 | voice_type, speed, language |
voice_id, speed, volume, pitch, is_active |
保留原有接口:
- ✅
/generate-voice可用于通用 TTS(非分镜场景) - ✅ 测试音色、预览效果
- ✅ 其他业务模块使用
🧪 测试清单
必须测试
-
基本功能
- 成功创建批量配音任务
- 配音写入
storyboard_voiceovers表 - 任务状态正确更新(PENDING → PROCESSING → COMPLETED)
-
参数验证
- 空对话列表 → ValidationError
- 超过 50 个对话 → ValidationError
- 无效的对话 ID → ValidationError
- 对话属于不同分镜 → ValidationError
-
权限验证
- 用户不存在 → ValidationError
- 对话不存在 → ValidationError
- 分镜不存在 → ValidationError
- 无分镜权限 → PermissionError
-
积分系统
- 积分不足 → InsufficientCreditsError
- 积分正确预扣(基于总字符数)
- 任务成功后积分确认
- 部分失败不退款
-
部分失败容错
- 单个对话失败,其他继续
- 成功的配音保留
- 失败原因在
outputData.failed_dialogues中记录
-
激活配音
is_active=true自动停用其他配音is_active=false不影响现有激活状态
手动测试脚本
# 1. 准备测试数据(创建分镜和对话)
export TOKEN="your-test-token"
export STORYBOARD_ID="test-storyboard-id"
# 创建测试对话
curl -X POST "http://localhost:6160/api/v1/storyboard-resources/dialogues" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"storyboard_id": "'$STORYBOARD_ID'",
"content": "这是测试对话1",
"character_name": "角色A",
"sequence_order": 1
}'
# 获取对话 ID
export DIALOGUE_ID_1="returned-dialogue-id-1"
export DIALOGUE_ID_2="returned-dialogue-id-2"
# 2. 批量生成配音
curl -X POST "http://localhost:6160/api/v1/ai/generate-dialogue-voiceovers" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dialogue_ids": ["'$DIALOGUE_ID_1'", "'$DIALOGUE_ID_2'"],
"voice_id": "EXAVITQu4vr4xnSDxMaL",
"voice_name": "Bella",
"speed": 1.0,
"volume": 1.0,
"pitch": 1.0,
"is_active": true
}'
# 获取 job_id
export JOB_ID="returned-job-id"
# 3. 查询任务状态(轮询直到完成)
watch -n 2 "curl -s -X GET 'http://localhost:6160/api/v1/ai/jobs/$JOB_ID' \
-H 'Authorization: Bearer $TOKEN' | jq '.data.status, .data.progress'"
# 4. 查看任务结果
curl -X GET "http://localhost:6160/api/v1/ai/jobs/$JOB_ID" \
-H "Authorization: Bearer $TOKEN" | jq '.data.outputData'
# 5. 验证配音已写入
curl -X GET "http://localhost:6160/api/v1/storyboard-resources/dialogues/$DIALOGUE_ID_1/voiceovers" \
-H "Authorization: Bearer $TOKEN" | jq '.data'
⚠️ 注意事项
1. 积分消耗
- 预扣全部积分:基于所有对话的总字符数一次性预扣
- 部分失败不退款:即使部分对话失败,积分不退还
- 建议:先测试少量对话(2-3个),确认效果后再批量生成
2. 性能考虑
- 串行处理:当前实现为串行处理(逐个对话)
- 预计时长:每个对话约 5-10 秒(TTS + 上传)
- 推荐批量:10-20 个对话(避免超时)
3. 失败处理
- 部分失败:已成功的配音保留在数据库
- 重试策略:失败的对话可单独重新提交
- 查看原因:查询任务状态,检查
outputData.failed_dialogues
4. 激活配音
is_active=true:每个对话的新配音会自动停用该对话的其他配音is_active=false:生成配音但不激活,需手动激活
🚀 部署步骤
1. 确认依赖
# 确认 Celery 正常运行
docker exec jointo-server-celery-ai celery -A app.core.celery_app inspect active
# 确认 Redis 正常
docker exec jointo-server-redis redis-cli ping
2. 重启服务
# 重启后端服务(应用新代码)
docker-compose restart server-app
# 重启 Celery Worker
docker-compose restart server-celery-ai
3. 验证部署
# 检查 API 文档(确认新端点存在)
curl http://localhost:6160/docs
# 查看日志
docker logs -f jointo-server-app | grep "generate-dialogue-voiceovers"
📚 文档资源
-
RFC 145:
/docs/server/rfcs/145-dialogue-voiceovers-batch-generation.md- 完整的技术设计文档
- 业务流程详解
- 使用示例和测试计划
-
Changelog:
/docs/server/changelogs/2026-02-13-dialogue-voiceovers-batch-generation.md- 变更概述
- API 使用示例
- 注意事项
✅ 验收标准
- API 接口实现完成
- 支持批量生成(1-50 个对话)
- 配音自动写入
storyboard_voiceovers表 - 支持部分失败容错
- 完整的权限验证
- 积分预扣和消耗记录
- Celery 异步任务实现
- 文档完整(RFC + Changelog)
- 代码语法检查通过
- 单元测试覆盖(待补充)
- 集成测试覆盖(待补充)
- 手动测试验证(待执行)
🎉 总结
成功实现了批量对话配音生成接口,核心功能包括:
- ✅ 新增专用接口:
/api/v1/ai/generate-dialogue-voiceovers - ✅ 批量处理:一次请求最多 50 个对话
- ✅ 自动写入:配音直接写入
storyboard_voiceovers表 - ✅ 部分失败容错:已成功的配音保留
- ✅ 完整参数支持:voice_id, speed, volume, pitch, is_active
- ✅ 权限验证:分镜权限、对话归属验证
- ✅ 文档完整:RFC + Changelog
下一步:
- 执行手动测试验证功能
- 补充单元测试和集成测试
- 前端集成调用
- 性能优化(考虑并行生成)