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.
 

4.3 KiB

修复 ElevenLabs 音效生成 OSS 上传问题

日期: 2026-02-10
类型: Bug 修复
影响范围: AI 音效生成任务

问题描述

ElevenLabs 音效生成后未上传到 OSS,导致:

  • 生成的音频文件无法访问
  • 任务完成但 output_data 中缺少 file_url
  • 用户无法下载生成的音效

根本原因

  1. ElevenLabs Provider 返回格式

    • 返回 {'audio_data': bytes, 'audio_url': None, ...}
    • 音频数据以二进制形式存储在 audio_data 字段
  2. 任务检查逻辑错误generate_sound_task):

    # ❌ 错误:只检查 'url' 字段
    if result.get('url'):
        file_metadata = await _download_and_upload_file(...)
    
    • ElevenLabs 不返回 url 字段,条件永远为 False
    • 跳过 OSS 上传逻辑
  3. 不一致性

    • generate_voice_task 已正确处理 audio_data 字段
    • generate_sound_task 缺少相同逻辑

解决方案

修改 generate_sound_task

参考 generate_voice_task 的实现,添加 audio_data 检查逻辑:

# ✅ 修复后:优先检查 audio_data(二进制数据)
if result.get('audio_data'):
    # 直接上传二进制数据到 MinIO
    file_metadata = await _upload_file_from_bytes(
        file_data=result['audio_data'],
        filename=f"sound_{job_id}.mp3",
        content_type='audio/mpeg',
        category='ai-generated/sounds',
        user_id=user_id
    )
    # 更新 result,移除 audio_data,添加文件元数据
    result.pop('audio_data', None)
    result.update(file_metadata)
elif result.get('url'):
    # 兼容旧的 URL 方式(其他 Provider)
    file_metadata = await _download_and_upload_file(...)
    result.update(file_metadata)

处理流程

  1. 优先检查 audio_data(ElevenLabs 返回格式)

    • 使用 _upload_file_from_bytes() 直接上传二进制数据
    • 移除 audio_data 字段(避免存储大量二进制数据)
    • 添加文件元数据(file_url, file_size, checksum 等)
  2. 降级检查 url(兼容其他 Provider)

    • 使用 _download_and_upload_file() 下载后上传
    • 保持向后兼容性

影响范围

修改文件

  • server/app/tasks/ai_tasks.py - generate_sound_task 函数

受益功能

  • ElevenLabs 音效生成(/api/v1/ai/generate-sound
  • 所有使用 ElevenLabs 的音效生成任务

兼容性

  • 向后兼容:保留 url 检查逻辑
  • 不影响其他 Provider(OpenAI、Mock 等)
  • generate_voice_task 逻辑一致

测试验证

测试步骤

  1. 调用音效生成 API

    docker exec jointo-server-app python -c "
    import asyncio
    from app.services.ai_providers.elevenlabs_provider import ElevenLabsProvider
    
    async def test():
        provider = ElevenLabsProvider('elevenlabs-sound-v2')
        result = await provider.generate_sound_effect(
            text='Dog barking in the distance',
            duration_seconds=5.0
        )
        print(f'✅ 音效生成成功: {len(result[\"audio_data\"])} bytes')
    
    asyncio.run(test())
    "
    
  2. 检查任务输出

    • 查询 ai_jobs 表的 output_data 字段
    • 验证包含 file_url, file_size, checksum 等字段
  3. 验证文件可访问

    • 访问 file_url 确认音频文件可下载
    • 检查 MinIO 存储路径 ai-generated/sounds/source/2026/02/10/

预期结果

{
  "file_url": "/api/v1/files/ai-generated/sounds/source/2026/02/10/abc123.mp3",
  "file_size": 81920,
  "checksum": "abc123...",
  "mime_type": "audio/mpeg",
  "storage_provider": "minio",
  "storage_path": "ai-generated/sounds/source/2026/02/10/abc123.mp3",
  "duration": 5.0,
  "metadata": {
    "text": "Dog barking in the distance",
    "duration_seconds": 5.0,
    "audio_size": 81920
  }
}

相关文档

  • RFC 142: ElevenLabs 集成方案
  • Changelog: 2026-02-10 ElevenLabs 集成实现
  • 测试结果: server/tests/manual/elevenlabs_test_results.md

后续优化

  1. 统一上传逻辑

    • 所有 AI 生成任务统一使用 audio_data 优先策略
    • 减少 URL 下载的网络开销
  2. 添加单元测试

    • 测试 generate_sound_taskaudio_data 处理逻辑
    • 测试 url 降级逻辑
  3. 监控告警

    • 监控 OSS 上传失败率
    • 告警音频文件缺失情况