67 lines
2.6 KiB
Markdown
67 lines
2.6 KiB
Markdown
# 实现方案 — 视频帧显示链路修复
|
||
|
||
## 后端
|
||
|
||
### 1. `backend/schemas.py` — ProjectOut 增加 frame_count
|
||
```python
|
||
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
|
||
```ts
|
||
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` — 真实帧渲染
|
||
- 新增 `frameUrl` prop
|
||
- `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` — 完整上传链路
|
||
上传流程改为:
|
||
1. `createProject({ name: file.name })`
|
||
2. `uploadMedia(file, projectId)`
|
||
3. `parseMedia(projectId)`
|
||
4. `getProjects()` 刷新列表
|