2.6 KiB
2.6 KiB
实现方案 — 视频帧显示链路修复
后端
1. backend/schemas.py — ProjectOut 增加 frame_count
class ProjectOut(ProjectBase):
id: int
created_at: datetime
updated_at: datetime
frame_count: int = 0
2. backend/routers/projects.py — 三处修改
list_projects: 为每个项目计算frame_count = len(p.frames)(利用 ORM relationship)list_frames: 返回前将每个frame.image_url替换为get_presigned_url(frame.image_url, expires=3600)get_project: 同样添加frame_count
3. backend/main.py — lifespan 默认视频种子
启动时异步后台任务:
- 若数据库无项目且
Data_MyVideo_1.mp4存在 - 创建 Project → 上传 MinIO → 调用 FFmpeg 解析帧 → 上传帧到 MinIO → 注册 Frame 记录 → 更新 status="ready"
- 使用
asyncio.to_thread()避免阻塞事件循环
4. backend/routers/media.py — upload 自动创建项目
当 project_id 为空时:
- 以文件名创建 Project
- 将上传文件关联到该 project
- 返回中增加
project_id
前端
5. src/lib/api.ts — 新增两个 API
export async function getProjectFrames(projectId: string): Promise<Array<{ id: number; project_id: number; frame_index: number; image_url: string; width: number | null; height: number | null }>>
export async function parseMedia(projectId: string): Promise<{ project_id: number; frames_extracted: number; status: string; message: string }>
6. src/components/VideoWorkspace.tsx — 帧加载中枢
- 读取
currentProject和frames useEffect监听currentProject.id:- 调用
getProjectFrames - 映射后端字段到前端
Frame[]结构写入 store - 若帧数为 0 且项目有 video_path,自动调用
parseMedia触发解析,解析完成后重新获取
- 调用
- 将当前帧 URL 通过 prop 传给
CanvasArea - 将帧列表和索引控制传给
FrameTimeline - 标题显示
currentProject?.name
7. src/components/CanvasArea.tsx — 真实帧渲染
- 新增
frameUrlprop const [image] = useImage(frameUrl || '')runInference使用frameUrl替代硬编码 URL
8. src/components/FrameTimeline.tsx — 真实帧导航
- 从 store 读取
frames和currentFrameIndex totalFrames = frames.length- 每个帧方块显示为
<img>缩略图 - 点击调用
setCurrentFrame(index) - 进度条范围 1..frames.length
9. src/components/ProjectLibrary.tsx — 完整上传链路
上传流程改为:
createProject({ name: file.name })uploadMedia(file, projectId)parseMedia(projectId)getProjects()刷新列表