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.
 

5.7 KiB

分镜视频缩略图功能实现

日期: 2026-02-14
类型: Feature
影响范围: 后端 API、数据库、AI 生成流程

📋 变更概述

storyboard_videos 表添加 thumbnail_url 字段,并在视频生成完成后自动提取第一帧作为缩略图。

🗄️ 数据库变更

1. Model 更新

文件: server/app/models/storyboard_resource.py

class StoryboardVideo(SQLModel, table=True):
    # ... 其他字段
    thumbnail_url: Optional[str] = Field(default=None, max_length=500)  # 新增字段

2. 数据库迁移

文件: server/alembic/versions/20260214_1214_add_thumbnail_url_to_storyboard_videos.py

ALTER TABLE storyboard_videos ADD COLUMN thumbnail_url VARCHAR(500);

执行命令:

docker exec jointo-server-app python scripts/db_migrate.py upgrade

🔧 业务逻辑实现

1. 缩略图生成服务

文件: server/app/services/ai_generation_result_service.py

新增方法: _generate_video_thumbnail

实现逻辑:

  1. 从 MinIO 下载视频文件
  2. 使用 ffmpeg 提取第一帧(0 秒位置)
  3. 缩放到 300x300(保持宽高比)
  4. 上传缩略图到 MinIO(category: thumbnail_video
  5. 返回缩略图 URL

参考实现: project_resource_service._generate_video_thumbnail

2. 视频保存逻辑更新

文件: server/app/services/ai_generation_result_service.py

方法: _save_storyboard_video

变更内容:

# 生成视频缩略图
thumbnail_url = await self._generate_video_thumbnail(
    video_url=output_data['file_url'],
    checksum=output_data['checksum'],
    user_id=user_id
)

# 保存到数据库
video = StoryboardVideo(
    # ... 其他字段
    thumbnail_url=thumbnail_url,  # ✅ 新增
)

3. Schema 更新

文件: server/app/schemas/storyboard_resource.py

: VideoResponse

class VideoResponse(BaseModel):
    # ... 其他字段
    thumbnail_url: Optional[str] = Field(None, serialization_alias="thumbnailUrl", description="缩略图URL")
    
    @field_serializer("thumbnail_url")
    def serialize_thumbnail_url(self, value: Optional[str]) -> Optional[str]:
        """统一构建缩略图访问 URL"""
        if value is None:
            return None
        return build_file_url(value)

📡 API 响应变更

获取分镜视频列表/详情

API: GET /api/v1/storyboards/{storyboard_id}/videos

响应示例:

{
  "success": true,
  "data": {
    "videoId": "01a2b3c4-...",
    "storyboardId": "01b3c4d5-...",
    "url": "http://localhost:9000/jointo/videos/video_xxx.mp4",
    "thumbnailUrl": "http://localhost:9000/jointo/thumbnail_video/thumbnail_xxx.jpg",  //  新增
    "status": 2,
    "isActive": true,
    // ... 其他字段
  }
}

🧪 测试命令

1. 创建 AI 对话并生成视频

# 1. 创建对话会话
curl -X POST 'http://localhost:8000/api/v1/ai/conversations' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
  "projectId": "PROJECT_ID",
  "targetType": 1,
  "targetId": "STORYBOARD_ID",
  "mediaType": 2
}'

# 2. 发送消息生成视频(替换 {conversation_id})
curl -X POST 'http://localhost:8000/api/v1/ai/conversations/{conversation_id}/messages' \
-H 'Authorization: Bearer YOUR_TOKEN' \
-H 'Content-Type: application/json' \
-d '{
  "content": "生成一个日落海滩的视频",
  "generate": {
    "generationType": "video",
    "modelId": "wan-video-1.0",
    "aiParams": {
      "resolution": "1080p",
      "aspectRatio": "16:9",
      "duration": 5
    }
  }
}'

2. 查询视频缩略图

# 查看视频详情(确认 thumbnailUrl 字段)
curl -X GET 'http://localhost:8000/api/v1/storyboards/{storyboard_id}/videos' \
-H 'Authorization: Bearer YOUR_TOKEN'

🔍 技术要点

ffmpeg 命令

ffmpeg -i input.mp4 \
  -ss 00:00:00 \            # 从第 0 秒开始
  -vframes 1 \               # 提取 1 帧
  -vf scale=300:300:force_original_aspect_ratio=decrease \  # 缩放
  -y output.jpg              # 覆盖输出

依赖关系

  • ffmpeg: 容器内必须安装(已在 Dockerfile 中配置)
  • httpx: 下载视频文件
  • FileStorageService: 上传缩略图到 MinIO

错误处理

  • 缩略图生成失败不影响视频保存(thumbnail_urlNULL
  • 日志记录所有错误便于排查
  • 临时文件自动清理

📝 相关文件清单

server/
├── alembic/versions/
│   └── 20260214_1214_add_thumbnail_url_to_storyboard_videos.py  # 迁移脚本
├── app/
│   ├── models/
│   │   └── storyboard_resource.py                              # Model 更新
│   ├── schemas/
│   │   └── storyboard_resource.py                              # Schema 更新
│   └── services/
│       └── ai_generation_result_service.py                      # 业务逻辑实现
└── docs/server/changelogs/
    └── 2026-02-14-storyboard-video-thumbnail.md                 # 本文档

验证清单

  • 数据库字段已添加
  • 迁移脚本已执行成功
  • Model 更新完成
  • Schema 更新完成
  • 缩略图生成逻辑已实现
  • API 响应包含 thumbnailUrl 字段
  • 前端集成测试(待前端实现)
  • 生产环境部署验证(待部署)

🚀 后续优化建议

  1. 异步处理: 将缩略图生成改为 Celery 异步任务,避免阻塞视频保存
  2. 多帧选择: 支持提取视频中间帧或多帧合成
  3. 缓存策略: 对同一视频的缩略图请求添加缓存
  4. CDN 加速: 缩略图 URL 接入 CDN 提升加载速度
  5. 质量配置: 支持自定义缩略图尺寸和质量参数