feat(voice): 提取视频配音接口 + 素材来源标识 #1654
Reference in New Issue
Block a user
Delete Branch "feat/extract-video-voice-and-source-label"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
功能 1:提取视频配音接口
POST /voices/extract-voice
接收视频文件,用 ffmpeg 提取人声并降噪,保存到配音素材库。
接口规格
highpass=f=80 → afftdn=nf=-25 → lowpass=f=8000uploads/voice/extracted/{uuid}.mp3{id, name, audio_url, duration, file_size, status, source}source: "video_extract",original_video: 原文件名错误处理
功能 2:素材来源标识
在 Asset 的
metadata.source字段标识来源:tts_jobtts.pysave_tts_job_to_library()(已有)uploadingest.pyworker(本次新增)video_extractvoices.pyextract_voice_from_video()(本次新增)AssetResponse.metadata已包含此字段,前端可直接通过asset.metadata.source判断来源,区分 AI 生成配音和用户上传配音。文件变更
apps/api/app/api/routes/voices.py— 新增 extract-voice 端点 + 辅助函数apps/worker/worker_app/tasks/ingest.py— 上传素材写入 source=upload 标识🚀 预览环境已部署
【阻塞级判定】
📊 审查概览
🔴 阻塞级问题(必须修复)
[apps/api/app/api/routes/voices.py: 535] 参数
project_id未被使用extract_voice_from_video接收了project_id表单参数,但在函数体内部完全未使用。代码逻辑是调用_find_or_create_voice_library_for_extract,该函数会遍历用户的所有可访问项目并选取第一个匹配项。这导致用户无法指定将提取的配音存放到特定项目,违背了接口设计意图。project_id传递给_find_or_create_voice_library_for_extract函数,并修改该函数逻辑以优先使用指定的project_id,而非遍历查找。[apps/worker/worker_app/tasks/ingest.py: 688] Metadata 覆盖导致数据丢失
asset.metadata = metadata进行直接赋值。metadata变量在当前上下文中仅包含source: "upload"等字段。如果该 Asset 是由voices.py创建的(包含source: "video_extract"和original_video等元数据),此操作会清空原有的业务元数据,导致数据丢失。asset.metadata.update(metadata))或仅更新特定字段,避免全量覆盖。[apps/api/app/api/routes/voices.py: 564] 文件名处理存在路径遍历风险
video_path时直接使用了file.filename:tmp_dir / f"...{file.filename}"。如果攻击者上传的文件名包含../(如../../evil.mp4),pathlib解析后可能会将文件写入临时目录之外(如/tmp/evil.mp4),虽然受限于系统权限,但仍存在污染系统临时目录或覆盖其他文件的风险。此外,特殊字符可能导致ffmpeg调用失败。uuid生成安全的临时文件名,原始文件名仅用于元数据记录或返回值。💡 改进建议(不阻塞合并)
[apps/worker/worker_app/tasks/ingest.py: 633, 669, 688] 重复的 Metadata 赋值
ingest_error分支、新建 Asset 分支和更新 Asset 分支均设置了metadata["source"] = "upload"。建议在函数入口处统一处理metadata的初始化或默认值,减少重复代码。[apps/api/app/api/routes/voices.py: 589] 依赖外部命令未做存在性检查
subprocess.run(["ffmpeg", ...])。如果运行环境未安装 ffmpeg 或不在 PATH 中,会导致运行时错误。虽然这是环境配置问题,但在关键路径上增加预检查或捕获更具体的FileNotFoundError会更健壮。✅ 良好实践
subprocess.run的列表参数形式调用外部命令,有效避免了 Shell 注入风险。voices.py中设置了timeout=300,防止长时间处理阻塞服务。tempfile.mkdtemp和try...finally确保临时文件被清理,资源管理良好。🤖 由 AI 代码审查机器人自动生成 | 2026-09-03 10:33:05 | 模型:
🗑️ 预览环境已清理
PR #1654 已关闭或合并,对应的预览环境已被清理。