Artifact and media metadata were previously reconstructed from job result refs and JSON sidecar files. This change regularizes metadata into SQLite, keeps filesystem blobs in place, and preserves backward compatibility via lazy fallback and backfill from existing job refs and JSON metadata. Constraint: Blob storage remains on the local filesystem in this phase Rejected: Migrate blobs into SQLite | larger scope and worse operational profile for current media sizes Rejected: Hard cutover without fallback | unsafe for historical data already on the test server Confidence: medium Scope-risk: moderate Directive: Treat SQLite as the metadata source of truth; JSON sidecars are compatibility fallback only Tested: go test ./...; deployed to test server 101.42.99.35; verified /v1/artifacts, /v1/artifacts/:id, signed media URL 200, unsigned content 401 Not-tested: Full historical backfill sweep over all existing artifact rows under production-sized data volume
137 lines
3.9 KiB
Markdown
137 lines
3.9 KiB
Markdown
# popiartServer Stable Media URL V1
|
||
|
||
这份文档只描述 `popiartServer` 这一层为了支持稳定媒体 URL 所做的职责扩展,不覆盖 `popiartcli` 的命令面,也不要求修改 `PopiNewAPI` 的现有通道实现。
|
||
|
||
## 背景
|
||
|
||
原有本地开发版 `popiartServer` 更偏向:
|
||
|
||
- `artifact` read-through
|
||
- `result_refs_json` 存 `data_url` 或上游临时 `url`
|
||
- `GET /artifacts/:id/content` 由 server 当场去解码或代理下载
|
||
|
||
这种实现能跑通基本流程,但不适合多模态 skill 的复用:
|
||
|
||
- `img2img` 经常还要重新拉流或转 base64
|
||
- 上游签名 URL 可能过期
|
||
- 前一个 job 的输出不能稳定地作为下一个 job 的 URL 输入
|
||
|
||
## V1 目标
|
||
|
||
`popiartServer` 在不依赖外部对象存储的前提下,先提供一套本地可运行的稳定媒体 URL 能力:
|
||
|
||
- 本地上传文件可立即获得稳定 URL
|
||
- 新生成的 job 结果会被 re-host 到本地 media store
|
||
- 新 artifact 会携带 `media_id` 和 `url`
|
||
- `image2video` 的 `vidu*` 路由可以直接复用 artifact URL
|
||
|
||
## 新接口
|
||
|
||
### `POST /v1/media/upload`
|
||
|
||
上传一个本地文件,直接创建 media 记录并返回:
|
||
|
||
- `id`
|
||
- `project_id`
|
||
- `filename`
|
||
- `content_type`
|
||
- `size_bytes`
|
||
- `created_at`
|
||
- `url`
|
||
- `visibility`
|
||
- `sha256`
|
||
|
||
### `GET /v1/media/:id`
|
||
|
||
读取 media 元数据。这个接口需要登录态,并要求 media 属于当前用户。
|
||
|
||
### `GET /v1/media/:id/content`
|
||
|
||
读取稳定内容 URL。
|
||
|
||
当前语义改为:
|
||
|
||
- 元数据接口仍然要求登录态,并要求 media 属于当前用户
|
||
- 内容 URL 使用短期签名 query 参数作为 capability URL
|
||
- 外部模型可以直接 fetch 已签名的 URL,不需要 bearer session
|
||
- 裸的 `/content` 路径默认不可直接匿名读取
|
||
|
||
## Artifact 行为变化
|
||
|
||
`POST /v1/artifacts/upload` 现在不再只把内容塞成 `data_url`:
|
||
|
||
1. server 先把上传文件写入本地 media store
|
||
2. 再创建本地 upload job
|
||
3. 最终 artifact 返回:
|
||
- `id`
|
||
- `media_id`
|
||
- `url`
|
||
- `visibility`
|
||
- `sha256`
|
||
- `storage_status`
|
||
|
||
## Job 结果行为变化
|
||
|
||
对于新的 `text2image`、`img2img`、`image2video` 结果:
|
||
|
||
1. 先从 `PopiNewAPI` 或上游临时结果读出真实内容
|
||
2. re-host 到 `POPIART_DATA_DIR/media/`
|
||
3. 把 `local_path + media_id + stable url` 写回 `result_refs_json`
|
||
|
||
这样新的 artifact 不再依赖上游临时 URL。
|
||
|
||
## 本地 media store
|
||
|
||
V1 的本地开发版不引入 S3/R2/OSS,而是用本地文件系统:
|
||
|
||
- blob 文件:`POPIART_DATA_DIR/media/blobs/`
|
||
- metadata JSON:`POPIART_DATA_DIR/media/meta/`
|
||
|
||
如果 `POPIART_DATA_DIR` 没显式配置,但 `POPIART_SQLITE_PATH` 已配置,则 `DataDir` 会自动落到 SQLite 所在目录,避免测试把 media 文件写进源码目录。
|
||
|
||
## 当前已打通的 URL 复用
|
||
|
||
### Artifact / media
|
||
|
||
- `media upload` -> stable URL
|
||
- `artifact upload` -> stable URL
|
||
- 新 `artifact` 的 `GET /artifacts/:id` -> `url`
|
||
|
||
### Runtime output
|
||
|
||
- `text2image` 输出会 re-host
|
||
- `img2img` 输出会 re-host
|
||
- `image2video` 输出会 re-host
|
||
|
||
### URL-first dispatch
|
||
|
||
当前已优先 URL 化的路径是:
|
||
|
||
- `video.image2video`
|
||
- 当模型 ID 以 `vidu` 开头,且 reference 已有 stable URL 时,server 会优先用 JSON `images` 传给 `/v1/videos`
|
||
|
||
这是为了先覆盖当前测试环境最常用的 `viduq2-pro-fast` 路由。
|
||
|
||
## 兼容策略
|
||
|
||
旧数据仍然兼容:
|
||
|
||
- 如果 `result_ref.kind == data_url`,继续解码读取
|
||
- 如果 `result_ref.kind == url`,继续代理下载
|
||
- 如果 `result_ref.kind == local_path`,优先从本地 media store 读取
|
||
|
||
也就是说:
|
||
|
||
- 历史 artifact 不会立即失效
|
||
- 新 artifact 才开始享受稳定 URL
|
||
|
||
## 后续阶段
|
||
|
||
后续如果要走生产化路线,可以保持接口不变,只把签名 URL 和存储实现替换成对象存储:
|
||
|
||
- 本地 `media/blobs/` -> S3 / R2 / OSS / COS
|
||
- metadata JSON -> 独立 media table 或对象存储元数据
|
||
- `GET /v1/media/:id/content` -> CDN / 受控稳定 URL
|
||
|
||
但这一步不是 V1 本地开发验证的前置条件。
|