4e06f4abe2
- 后端: 空镜素材迁移到 config/materials.json,duration从文件名_{N}s_自动解析
- 后端: 新增 POST /api/v1/materials/match 接口,后端做关键词匹配
- 前端: VideoGeneration 空镜匹配改为调用后端接口
- 前端: 人物出镜素材改为本地文件选择器直接选取,不走素材库
- 前端: 视频生成流程简化,移除Vidu对口型和七牛云上传
- Rust: 视频合成支持从随机起始时间截取人物素材片段
- Rust: 修复ffprobe参数错误(添加-show_entries format=duration)
13 KiB
13 KiB
空镜素材智能匹配方案
文档版本:v1.0 创建时间:2026-04-22 状态:待实现
一、需求背景
1.1 业务场景
在视频创作流程中,脚本生成阶段会产生分镜脚本,每个分镜包含:
- 画面描述:AI 生成的口播场景描述(如 "厨房台面特写")
- 配音文字:该分镜的口播文案
当前痛点:画面描述只是文字,没有对应的空镜素材,每个分镜只能配音,无法展示实际画面。
1.2 解决思路
建立空镜素材库,AI 生成脚本时同时输出标签,根据标签从素材库匹配对应视频素材,实现:
- 口播 + 空镜画面 的完整分镜效果
- 自动替换/拼接空镜素材,减少人工选材工作量
二、功能需求
2.1 标签体系
| 层级 | 示例 | 说明 |
|---|---|---|
| 父标签 | 室内 |
大场景分类 |
| 子标签 | 客厅、厨房、卫生间、卧室、书房 |
具体房间 |
| 扩展标签 | 台面特写、整体空间、角落细节 |
拍摄角度/风格 |
标签格式:父标签/子标签 或 父标签/子标签/扩展标签
示例:
室内/客厅
室内/厨房/开放式
室内/卫生间/干湿分离
室外/阳台
2.2 素材管理
| 需求 | 描述 |
|---|---|
| 素材上传 | 运营人员上传空镜视频到七牛云 |
| 标签标注 | 上传时指定素材的标签(支持多标签) |
| 素材索引 | 本地维护标签 → 素材文件 的映射关系 |
| 素材检索 | 根据标签快速查找匹配素材 |
2.3 脚本生成增强
| 需求 | 描述 |
|---|---|
| 标签输出 | AI 生成脚本时,在画面描述后输出对应标签 |
| 标签格式 | 统一使用 【标签】室内/客厅 格式 |
| 兼容旧格式 | 已有脚本无需修改,缺失标签的分镜跳过素材匹配 |
示例输出:
{
"segments": [
{
"id": "seg_001",
"scene": "开场:展示整洁的厨房整体空间",
"voiceover": "大家好,今天我们来聊聊厨房装修的注意事项...",
"duration": 15,
"tag": "室内/厨房/整体空间"
},
{
"id": "seg_002",
"scene": "特写:台面材质细节",
"voiceover": "首先,台面的材质选择非常重要...",
"duration": 12,
"tag": "室内/厨房/台面特写"
}
]
}
2.4 素材匹配流程
| 步骤 | 操作 | 说明 |
|---|---|---|
| 1 | 解析标签 | 从分镜数据中提取 tag 字段 |
| 2 | 查询索引 | 在本地索引中查找对应标签的素材列表 |
| 3 | 随机选择 | 从匹配素材中随机选择一个 |
| 4 | 获取 URL | 拼接七牛云访问 URL |
| 5 | 下载素材 | 下载到本地 shots/ 目录 |
| 6 | 记录关联 | 将 videoPath 写入分镜数据 |
2.5 素材路径设计
七牛云存储结构(使用英文路径):
materials/
├── indoor/
│ ├── living_room/
│ │ ├── vid_001.mp4
│ │ ├── vid_002.mp4
│ │ └── ...
│ ├── kitchen/
│ │ ├── open/
│ │ │ ├── vid_010.mp4
│ │ │ └── ...
│ │ └── closed/
│ └── bathroom/
├── outdoor/
│ ├── balcony/
│ └── garden/
└── ...
本地索引结构(materials_index.json):
{
"version": "1.0",
"lastUpdated": "2026-04-22T10:00:00Z",
"tags": {
"室内/客厅": {
"displayName": "室内/客厅",
"qiniuDir": "materials/indoor/living_room",
"videos": [
{
"id": "vid_001",
"filename": "vid_001.mp4",
"qiniuKey": "materials/indoor/living_room/vid_001.mp4",
"duration": 8.5,
"size": 1024000,
"uploadedAt": "2026-04-20T08:30:00Z"
}
]
},
"室内/厨房/开放式": {
"displayName": "室内/厨房/开放式",
"qiniuDir": "materials/indoor/kitchen/open",
"videos": [...]
}
}
}
三、技术方案
3.1 架构概览
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ 脚本生成 │────▶│ 素材匹配服务 │────▶│ 七牛云存储 │
│ (AI 输出标签) │ │ (本地索引查询) │ │ (英文路径) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌──────────────────┐
│ 本地素材索引 │
│ (materials_index) │
└──────────────────┘
3.2 核心模块
| 模块 | 位置 | 职责 |
|---|---|---|
MaterialIndex |
python-api/app/services/material_index.py |
素材索引管理(加载/查询/更新) |
MaterialMatcher |
python-api/app/services/material_matcher.py |
素材匹配逻辑 |
QiniuMaterialService |
python-api/app/services/qiniu_material.py |
七牛云素材相关操作 |
material_router |
python-api/app/api/v1/material.py |
素材管理 API |
3.3 关键设计决策
| 决策 | 方案 | 理由 |
|---|---|---|
| 标签中文,路径英文 | 中文标签映射到英文目录 | URL 可读性、FFmpeg 兼容性 |
| 本地索引 + 七牛云 | 双层架构 | 减少 API 调用、快速检索 |
| 随机选择素材 | random.choice() |
避免每次生成视频都用相同素材 |
| 索引热更新 | 定时刷新 + 手动刷新 | 支持运营动态增删素材 |
四、数据模型
4.1 分镜数据扩展
// 前端 types.ts
interface Segment {
id: string;
type: "segment" | "empty_shot";
scene: string; // 画面描述
voiceover: string; // 配音文案
duration: number; // 预估时长(秒)
tag?: string; // 【新增】素材标签,如 "室内/客厅"
videoPath?: string; // 【新增】匹配到的素材本地路径
videoUrl?: string; // 【新增】七牛云原 URL
status: "pending" | "matched" | "downloaded" | "failed";
}
4.2 素材索引 Schema
// materials_index.json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["version", "lastUpdated", "tags"],
"properties": {
"version": {
"type": "string",
"description": "索引文件版本"
},
"lastUpdated": {
"type": "string",
"format": "date-time",
"description": "最后更新时间"
},
"tags": {
"type": "object",
"description": "标签到素材的映射",
"additionalProperties": {
"type": "object",
"required": ["displayName", "qiniuDir", "videos"],
"properties": {
"displayName": {"type": "string"},
"qiniuDir": {"type": "string"},
"videos": {
"type": "array",
"items": {
"type": "object",
"required": ["id", "filename", "qiniuKey"],
"properties": {
"id": {"type": "string"},
"filename": {"type": "string"},
"qiniuKey": {"type": "string"},
"duration": {"type": "number"},
"size": {"type": "integer"},
"uploadedAt": {"type": "string"}
}
}
}
}
}
}
}
}
五、API 设计
5.1 素材管理 API
| 方法 | 路径 | 描述 |
|---|---|---|
| GET | /api/v1/materials |
获取素材索引 |
| GET | /api/v1/materials/tags |
获取所有标签列表 |
| GET | /api/v1/materials/match?tag=室内/客厅 |
根据标签匹配素材 |
| POST | /api/v1/materials/sync |
同步/刷新素材索引(管理员) |
| POST | /api/v1/materials/upload |
上传新素材并标注标签 |
5.2 匹配流程 API
| 方法 | 路径 | 描述 |
|---|---|---|
| POST | /api/v1/materials/batch-match |
批量匹配分镜素材 |
| GET | /api/v1/materials/download/{id} |
下载素材到本地 |
批量匹配请求:
POST /api/v1/materials/batch-match
{
"segments": [
{"id": "seg_001", "tag": "室内/客厅"},
{"id": "seg_002", "tag": "室内/厨房/台面特写"}
]
}
批量匹配响应:
{
"matched": [
{"segmentId": "seg_001", "videoId": "vid_001", "url": "https://..."},
{"segmentId": "seg_002", "videoId": "vid_015", "url": "https://..."}
],
"unmatched": ["seg_003"],
"summary": {
"total": 3,
"matched": 2,
"unmatched": 1
}
}
六、实施步骤
Phase 1:基础设施(1天)
- 创建
python-api/app/services/material_index.py - 创建
python-api/app/services/material_matcher.py - 创建
python-api/app/services/qiniu_material.py - 初始化示例
materials_index.json
Phase 2:API 层(1天)
- 创建
python-api/app/api/v1/material.py路由 - 注册路由到
router.py - 编写 API 文档和单元测试
Phase 3:脚本生成集成(1天)
- 更新
python-api/app/ai/prompts/system/script.txt添加标签输出要求 - 更新
python-api/app/services/script_service.py支持素材匹配 - 测试端到端流程
Phase 4:前端集成(1天)
- 更新
tauri-app/src/api/types.ts添加 tag 字段 - 更新
SegmentAdapter支持 tag 映射 - 前端显示素材匹配状态
Phase 5:运营工具(待定)
- 开发素材上传 + 标签标注 Web 页面
- 开发素材索引管理后台
七、文件结构
meijiaka-zj/
├── python-api/
│ └── app/
│ ├── services/
│ │ ├── material_index.py # 【新建】素材索引管理
│ │ ├── material_matcher.py # 【新建】素材匹配器
│ │ └── qiniu_material.py # 【新建】七牛云素材操作
│ ├── api/v1/
│ │ ├── material.py # 【新建】素材管理 API
│ │ └── router.py # 【修改】注册 material 路由
│ └── schemas/
│ └── material.py # 【新建】Pydantic 模型
│
├── tauri-app/
│ └── src/
│ ├── api/
│ │ ├── modules/
│ │ │ └── material.ts # 【新建】素材 API 模块
│ │ └── types.ts # 【修改】添加 Segment.tag
│ └── pages/
│ └── VideoCreation/
│ └── ScriptCreation.tsx # 【修改】显示标签
│
├── docs/
│ └── material-matching-plan.md # 【新建】本文档
│
└── materials/
└── materials_index.json # 【新建】素材索引文件
八、配置项
8.1 环境变量
# .env 添加
QINIU_MATERIALS_ENABLED=true # 是否启用素材匹配
QINIU_MATERIALS_DOMAIN=https://materials.xxx.com # 素材专用域名
MATERIALS_INDEX_PATH=./materials_index.json # 本地索引文件路径
8.2 标签-目录映射表
| 中文标签 | 七牛云目录 | 英文路径 |
|---|---|---|
| 室内/客厅 | materials/indoor/living_room | materials/indoor/living_room |
| 室内/厨房 | materials/indoor/kitchen | materials/indoor/kitchen |
| 室内/厨房/开放式 | materials/indoor/kitchen/open | materials/indoor/kitchen/open |
| 室内/卫生间 | materials/indoor/bathroom | materials/indoor/bathroom |
| 室内/卧室 | materials/indoor/bedroom | materials/indoor/bedroom |
| 室外/阳台 | materials/outdoor/balcony | materials/outdoor/balcony |
九、注意事项
9.1 编码问题
- 七牛云 Key:使用 UTF-8 编码,中文 key 技术上支持但建议避免
- 本地路径:下载后确保路径处理兼容中文文件名
- FFmpeg:操作本地文件时优先使用绝对路径和引号包裹
9.2 容错处理
| 场景 | 处理方式 |
|---|---|
| 标签无匹配素材 | 记录 unmatched,不影响后续分镜 |
| 七牛云下载失败 | 标记状态为 failed,允许重试 |
| 索引文件缺失 | 自动重建或返回空列表 |
9.3 性能考虑
- 素材索引加载到内存,避免每次请求都读文件
- 使用 LRU 缓存最近访问的素材 URL
- 下载操作异步执行,不阻塞主流程
十、附录
10.1 七牛云 Bucket 配置建议
Bucket 名称:media-materials
存储区域:华东 zone
访问域名:materials.xxx.com(CDN 加速)
10.2 素材上传规范
| 字段 | 要求 |
|---|---|
| 格式 | MP4、H.264 编码 |
| 时长 | 5-30 秒 |
| 分辨率 | 1920x1080 或 1280x720 |
| 文件名 | vid_{序号}.mp4 |
| 标签 | 上传后填写,支持多标签 |