# 视频生成 API 文档

更新时间：2026-10-17。服务地址：`https://video.laoma.667734.xyz`。
本文依据当前服务代码核对；示例中的 KEY、令牌、任务和代理 ID 均为占位符，不是真实凭据。

## 1. 鉴权与接口分类

| 用途 | 请求头 | 获取方式 |
| --- | --- | --- |
| 视频 API | `Authorization: Bearer YOUR_API_KEY` | 客户门户的“我的密钥” |
| 客户门户 | `X-Portal-Key: YOUR_PORTAL_TOKEN` | `/api/portal/login` 返回的 `token` |
| 管理接口 | `X-Admin-Key: YOUR_ADMIN_TOKEN` | `/api/admin/login` 返回的 `token` |

三类凭据不能混用。客户无需提供上游账号 CK 或代理密码。不要把密钥写入公开页面、公开仓库、截图或日志。令牌失效返回 401；重新登录获取令牌。运行环境和入口网关可能有额外请求体限制。

## 2. 快速开始

以下为 Bash/curl 示例。设置 `BASE` 为服务根地址，不带 `/v1/videos`。

```bash
export BASE='https://video.laoma.667734.xyz'
export VIDEO_API_KEY='YOUR_API_KEY'

curl --fail-with-body "$BASE/v1/client" \
  -H "Authorization: Bearer $VIDEO_API_KEY"

curl --fail-with-body "$BASE/v1/videos" \
  -H "Authorization: Bearer $VIDEO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"prompt":"秋风落叶","model":"video-2.0-fast","duration":5,"ratio":"16:9"}'
```

提交成功返回 **202** 和任务 `id`，不表示视频生成成功。保存该 ID，查询原任务，不要反复提交。

```bash
export TASK_ID='REPLACE_WITH_RETURNED_ID'
curl --fail-with-body "$BASE/v1/videos/$TASK_ID" \
  -H "Authorization: Bearer $VIDEO_API_KEY"
```

仅在 `status=completed` 后下载：

```bash
curl --fail-with-body -L "$BASE/v1/videos/$TASK_ID/content" \
  -H "Authorization: Bearer $VIDEO_API_KEY" -o video.mp4
```

下载可能返回 307 重定向到对象存储签名地址；不要使用 `--location-trusted`，不要把本站凭据转发到其他主机。也可以使用任务返回的 `video_url`；以 `/` 开头时需拼接服务根地址。

可运行 Python 示例：[example.py.txt](./example.py.txt)（以文本提供下载，运行时文件名使用 example.py）。安装依赖：`python -m pip install requests`。先设置 `VIDEO_BASE_URL` 和 `VIDEO_API_KEY`，运行 `python example.py`。该脚本会真实提交一条任务，可能产生费用。

## 3. 视频 API

| 方法 | 路径 | 鉴权 | 返回 |
| --- | --- | --- | --- |
| GET | `/v1/models` | 无 | 公共模型列表 |
| GET | `/v1/client` | Bearer KEY | 当前 KEY 的额度策略、使用量、并发策略、可用模型时长 |
| POST | `/v1/videos` | Bearer KEY | 202，异步提交 |
| POST | `/v1/videos/generations` | Bearer KEY | 同一受理逻辑，成功 HTTP 状态为 200 |
| GET | `/v1/videos/{id}` | Bearer KEY | 任务状态 |
| GET | `/v1/videos?limit=20&offset=0` | Bearer KEY | 当前 KEY 的历史任务 |
| GET | `/v1/videos/{id}/content` | Bearer KEY | MP4 或 307 对象存储重定向 |
| GET | `/api/portal/pricing` | 无 | 当前公开模型、时长和价格 |

### 提交参数

同时支持 JSON 和 `multipart/form-data`。推荐显式提供 `model`、`duration`、`ratio`。

| 字段 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `prompt` | string | 是 | 非空提示词；最大长度由服务配置决定，`/v1/client.max_prompt_chars` 可查 |
| `model` | string | 否 | `video-2.0-fast` / `video-2.5`；默认 2.0 fast |
| `duration` | integer | 否 | 秒；视频 API 未提供任何时长字段时默认 10。2.0 fast 标准档位 5/10/15；2.5 标准档位 5/10/15/30；以 KEY 策略和开放配置为准 |
| `seconds` / `duration_seconds` | integer | 否 | 兼容时长字段；优先级 `duration` > `seconds` > `duration_seconds` |
| `ratio` | string | 否 | 建议显式传 `16:9` / `9:16` / `1:1` / `4:3` / `3:4` |
| `reference_images` | string[] | 否 | 公网图片 URL 或 base64 data URL |
| `reference_videos` | string[] | 否 | 公网视频 URL 或 base64 data URL |
| `reference_audios` | string[] | 否 | 公网音频 URL 或 base64 data URL |

图片默认最多30张、单张15 MiB；参考视频默认最多10个、音频默认最多10段，视频/音频单文件32 MiB。图片格式 JPEG/PNG/WebP，视频 MP4/MOV，音频 MP3/WAV。**这些是本服务配置的接收上限，不是上游保证支持的数量或素材组合**；内容、编码和模型仍可能被上游拒绝。生产配置和入口网关可能限制更严，过大的 multipart/base64 请求不建议一次发送。

URL 必须可由服务器访问；内网、回环和保留地址不允许。不要传只能在本机访问的路径。未提供独立的文件预上传接口：文件与生成请求一起上传，或使用已有公网对象 URL。

```bash
curl --fail-with-body "$BASE/v1/videos" \
  -H "Authorization: Bearer $VIDEO_API_KEY" \
  -F 'prompt=秋风落叶' -F 'model=video-2.0-fast' -F 'duration=5' -F 'ratio=16:9' \
  -F 'image[]=@frame.jpg' -F 'video[]=@clip.mp4' -F 'audio[]=@track.mp3'
```

可重复 `image[]`、`video[]`、`audio[]` 字段提交多个文件。不要手写 multipart 的 `Content-Type`，客户端会自动生成 boundary。

### 响应与轮询

以下为字段结构示例，不是真实生成结果：

```json
{
  "id": "video_EXAMPLE",
  "status": "queued",
  "model": "video-2.0-fast",
  "prompt": "秋风落叶",
  "video_url": null,
  "error": null,
  "progress": {
    "state": "queued",
    "position": 1,
    "ahead": 0,
    "running": 0,
    "workers": 0,
    "typical_seconds": 420,
    "eta_seconds": 0,
    "eta_text": "",
    "message": "等待符合条件的空闲账号",
    "blocker": {"code":"accounts_busy","message":"等待符合条件的空闲账号","retryable":true}
  }
}
```

| `status` | 含义 | 客户动作 |
| --- | --- | --- |
| `queued` | 等待派发或生成前准备 | 查询原任务，不重复提交 |
| `processing` | 已开始处理，可能正在提交、等待上游或转存 | 继续查询 |
| `completed` | 本站任务已完成 | 保存 `video_url` 或下载 `/content` |
| `failed` | 本站任务失败 | 停止轮询，查看 `error` |
| `风控` | 兼容历史异常状态 | 按失败处理，联系管理员核查 |

建议每5–10秒查询一次，设定合理总等待时限。`progress.message` 是阶段说明，`blocker` 可能为 null；常见阻塞码包括 `accounts_busy`、`client_concurrency`、`global_concurrency`。ETA 仅为估算，不是承诺，`workers=0` 表示不设全局并发闸门，不代表没有服务器资源限制。HTTP 200、会话编号或心跳均不等于视频成功。

历史查询参数：`limit` 默认20，范围1–50；`offset` 默认0；`status` 可选上述状态。返回 `total`、`limit`、`offset`、`tasks`。只能读取当前 KEY 的任务，其他 KEY 的任务返回404。

### HTTP 错误

错误通常返回 `{"detail":"原因"}`；框架字段校验也可能返回结构化数组，网关错误可能不是 JSON。

| HTTP | 含义及处理 |
| --- | --- |
| 400 | JSON、业务参数或分配计划不合法，修正后再请求 |
| 401 | 凭据缺失、失效或无权使用，检查鉴权类型 |
| 403 | 自助注册关闭、邀请码错误或当前操作禁止 |
| 404 | 任务/资源不存在或不属于当前身份 |
| 409 | 视频尚未就绪，或用户名冲突等资源状态冲突 |
| 413 | 请求体超过入口允许大小，缩小素材 |
| 422 | 参数、模型时长、素材或 KEY 时长策略校验失败 |
| 429 | 本站额度、请求限制或上游退避；若有 `Retry-After`，至少等待指定秒数 |
| 502/503 | 存储或服务容量暂不可用，先核对原任务是否已创建 |

提交接口没有承诺幂等键。POST 超时/断线后不能认定“未受理”，先查任务列表或联系管理员核对；不要立即重复提交，避免重复计费。GET 查询失败可以退避重试。

## 4. 客户注册、登录与密钥

| 方法 | 路径 | 请求体 / 说明 |
| --- | --- | --- |
| GET | `/api/portal/config` | 无鉴权；注册开关、密码长度、KEY 数量上限和模型配置 |
| POST | `/api/portal/register` | `{"username":"yourname","password":"YOUR_PASSWORD","email":"","invite":""}`；是否可注册以 config 为准，返回 `token` 与默认 `key` |
| POST | `/api/portal/login` | `{"username":"yourname","password":"YOUR_PASSWORD"}`，返回门户 `token` |
| POST | `/api/portal/key-login` | `{"key":"YOUR_API_KEY"}`，客户所属 KEY 换门户 token |
| GET | `/api/portal/me` | 携带 X-Portal-Key；账号、名下 KEY 与用量 |
| POST | `/api/portal/keys` | 携带 X-Portal-Key；`{"name":"业务服务"}` 创建 KEY |
| PATCH | `/api/portal/keys/{key}` | 携带 X-Portal-Key；修改 `name` 或 `enabled` |
| POST | `/api/portal/keys/{key}/rotate` | 携带 X-Portal-Key；换新 KEY，旧值立即失效 |
| DELETE | `/api/portal/keys/{key}` | 携带 X-Portal-Key；删除 KEY，受至少保留一把启用 KEY 约束 |
| POST | `/api/portal/logout` | 携带 X-Portal-Key；注销门户会话 |

用户名格式与最小密码长度以 config 和校验返回为准。管理账号不能使用客户登录接口。密钥路径含敏感值，应避免记录完整 URL。

门户也可使用 `POST /api/portal/generate` 提交，以及 `GET /api/portal/tasks`、`GET /api/portal/tasks/{id}` 查询。使用 X-Portal-Key，不是 Bearer KEY；生成字段与上述一致，门户默认时长5秒，返回 `{"ok":true,"quote":{},"task":{}}` 结构，不是视频 API 的直接 TaskResponse。服务端集成优先使用 `/v1/videos`。

## 5. 管理员：静态 IP 按数量分配

管理员接口不是客户 KEY 权限。先登录取得 token：

```bash
curl --fail-with-body "$BASE/api/admin/login" \
  -H 'Content-Type: application/json' \
  -d '{"username":"YOUR_ADMIN_USERNAME","password":"YOUR_ADMIN_PASSWORD"}'
```

使用 `X-Admin-Key: RETURNED_TOKEN` 查询 `GET /api/admin/accounts` 和 `GET /api/admin/proxies`。使用返回的代理 `id`，不是 IP 地址作为 `proxy_id`。只允许选择已启用且 `mode=static` 的代理。

### 预览与应用

每条静态 IP 可保存自己的账号绑定上限。在管理面板的「代理管理 → 绑定数量」中设置，或调用：

```http
PATCH /api/admin/proxies/PROXY_A_ID
X-Admin-Key: RETURNED_TOKEN
Content-Type: application/json

{"account_limit":20}
```

`account_limit` 是0-1000000的整数，0表示不限制；创建静态代理时也可传入。比如代理A设20、代理B设80，分别独立保存，服务重启后保留。保存上限本身不分配或解绑账号；要实际分配，使用下面的批量接口。

代理列表返回 `account_count`（已绑定数）、`account_limit`（上限）、`account_slots`（剩余名额；不限时为null）和 `account_over_limit`（已有绑定是否超限）。单号绑定、批量绑定及底层写入均检查静态代理容量；待应用绑定也预占名额。降低上限不会删除已有绑定，已绑账号可保留，但满额后不能新增。未设置过上限的旧代理默认为0，不改动旧绑定。

同批调整不会预先扣除即将迁出的账号，避免尚未生效的迁出让名额超卖。满额代理之间调换时，请先释放名额或临时调高上限。平均分配仍是均分本批账号，并非按容量加权，超限会明确报错。

`POST /api/admin/accounts/allocate-static-proxies`

| 字段 | 类型 | 默认 | 含义 |
| --- | --- | --- | --- |
| `names` | string[] | [] | 本批账号名，最多500个，不能为空或重复，必须已存在 |
| `mode` | string | `custom` | `custom` 指定数量；`equal` 平均分配 |
| `allocations` | object[] | [] | 顺序稳定的代理列表；`proxy_id` 唯一；`count` 为非负整数 |
| `preview` | boolean | `true` | true 只读预览；false 才写入绑定 |

指定数量示例：

```json
{
  "names": ["acc1", "acc2", "acc3", "acc4", "acc5"],
  "mode": "custom",
  "allocations": [
    {"proxy_id": "PROXY_A_ID", "count": 1},
    {"proxy_id": "PROXY_B_ID", "count": 4}
  ],
  "preview": true
}
```

100个账号按20/80分配时，提交完整100个账号名，两个 `count` 分别填20和80。`count` 是**本批分配数**，`account_limit` 是**该代理保存的总绑定上限**。未在 names 中的账号不会更改或自动解绑；预览和应用时均会计入已有及待应用绑定，超过保存的上限会拒绝。

平均分配：`mode=equal`，`allocations` 只填各个 `proxy_id` 即可。100个号/2条代理为50/50；5个号/2条为3/2，余数按代理列表顺序分配。账号按 names 顺序分段，每个账号恰好分给一条代理，不会重复分配。

响应结构：

```json
{
  "ok": true,
  "preview": true,
  "mode": "custom",
  "total": 5,
  "plan": [
    {"proxy_id":"PROXY_A_ID","proxy_name":"代理A","count":1,"names":["acc1"]},
    {"proxy_id":"PROXY_B_ID","proxy_name":"代理B","count":4,"names":["acc2","acc3","acc4","acc5"]}
  ],
  "updated": [],
  "queued": [],
  "failed": []
}
```

核对预览后，使用同一请求将 `preview` 改为 false 应用。接口重新校验账号和代理。`updated` 是立即生效账号；`queued` 是正在运行、等空闲后应用的账号；`failed` 是逐账号运行期失败列表。参数校验失败不写任何绑定；运行期失败允许部分成功，必须检查 `failed`，不能只看 HTTP 200。

分配不修改账号国家、调度开关、额度、限流状态，也不触发登录、探测或生成。静态代理配置不保证供应商实际公网出口固定、国家正确或账号不被限制。忙碌账号的待应用设置沿用现有内存队列，服务重启前未应用的设置不保证保留。应用前应保存原绑定快照，后续可用现有单号/批量代理接口恢复。

账号列表会返回待应用状态，可刷新检查；不要把“已提交待应用”误认为代理已经切换。
