# 图像 / 视频 / 音乐 API

> /v1/images/generations 与 /v1/images/edits 按张计费 · /v1/videos/generations 异步按秒计费 · /v1/music/generations 异步按次计费 · /v1/transcripts/extract 短视频提文案按次计费

来源：https://zhonkemodel.dflop.top/docs/reference/media-apis

除四个聊天协议端点外,本平台还提供三类媒体端点。鉴权方式与聊天端点完全一致 —— 同一把 `sk-gpushare-*` Key,四种方式任选(`x-api-key` / `x-goog-api-key` header / `?key=` query / `Authorization: Bearer`),详见 [鉴权](./authentication.md)。所有计费都扣账户余额(全部 Key 共享),余额不足返回 **402** `quota_exceeded`。

| 端点 | 用途 | 计费 |
|---|---|---|
| `POST /v1/images/generations` | 文生图 / 图生图(同步,或 `"async": true` 转任务) | **按张** |
| `POST /v1/images/edits` | 图生图,收 JSON 或 multipart 文件上传(同步,或 `"async": true`) | **按张**(与上一行同价) |
| `POST /v1/videos/generations` | 文生视频 / 图生视频(异步任务) | **按秒** |
| `GET /v1/videos/generations/{id}` | 查询视频任务状态 | 免费 |
| `GET /v1/videos/generations` | 列出当前账号的视频任务(**默认全部状态**,`?limit=` 默认 30 最大 100,`?status=` 可筛选) | 免费 |
| `POST /v1/music/generations` | AI 音乐生成(Suno,异步任务) | **按次**(一次 2 首) |
| `GET /v1/music/generations/{id}` | 查询音乐任务状态 | 免费 |
| `GET /v1/music/generations` | 列出当前账号的音乐任务(同上) | 免费 |
| `POST /v1/audio/speech` | 语音合成(同步,或 `"async": true` 转异步任务) | **按字符** |
| `GET /v1/audio/speech/{id}` | 查询语音合成任务状态 | 免费 |
| `GET /v1/audio/speech` | 列出当前账号的语音合成任务(同上) | 免费 |
| `POST /v1/transcripts/extract` | 短视频链接 → 口播文案(同步) | **按次** |

> 这些端点的响应都带 `x-gateway-trace` header —— **包括 4xx/5xx 错误响应**。报障或对账时可连同时间戳、model 与完整错误体一起提供;在 [logs.dflop.top](https://logs.dflop.top) 粘贴它即可反查那一笔。

---

## 重试不会重复扣费：`Idempotency-Key`

所有**会扣费**的 POST 端点（图片 / 视频 / 音乐 / 语音合成 / 音色克隆 / 数字人形象 / 文案提取）都支持 `Idempotency-Key` 请求头。带上它，同一个请求重发多少次都只会真正执行**一次**：

```bash
IDEM=$(uuidgen)   # 一个提交意图一把键，这次提交的每一次重试都复用它

curl https://zhonkezhonkeapi.dflop.top/v1/videos/generations \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $IDEM" \
  -d '{"model":"doubao-seedance-2-0-260128","prompt":"海边日落","duration":5}'
```

- **重发同一把键 + 同样的请求体** → 原样返回**第一次的响应**（含同一个任务 `id`），响应头多一个 `Idempotency-Replayed: true`，**不会再开一次任务、不会再扣一次钱**。
- 这也是**找回任务 id 的最简单办法**：忘了保存返回的 `id`？把当初那条 curl 原样再跑一遍（键和请求体都不变），返回的就是原来那条任务。
- 保留期 **7 天**；超过后同一把键会被当作新请求。
- 键要求：1–200 个可打印 ASCII 字符（推荐直接用 UUID），一个提交意图一把，**不要**跨不同请求复用。
- **作用域是账号**，不是单把 API Key：同一账号下换一把 `sk-gpushare-*` 重发同一个 `Idempotency-Key`，一样命中重放。（钱包本来就是全账号共享的，跨 Key 不去重才会漏。）
- **并发**：同一把键的两个请求同时到达时，只有一个会真正执行，另一个立刻拿到 **409** `idempotency_in_flight`——不会两个都跑。

```python
import uuid, requests

idem = str(uuid.uuid4())           # 一个提交意图一把键
body = {
    "model": "doubao-seedance-2-0-260128",
    "content": [{"type": "text", "text": "海边日落，无人机航拍"}],
    "duration": 5,
}

def submit():
    r = requests.post(
        "https://zhonkezhonkeapi.dflop.top/v1/videos/generations",
        headers={
            "Authorization": f"Bearer {PLATFORM_API_KEY}",
            "Idempotency-Key": idem,          # ← 每次重试都用同一把
        },
        json=body,
        timeout=60,
    )
    r.raise_for_status()
    # 重放时这个头是 "true"，说明拿到的是第一次的结果，没有二次计费
    replayed = r.headers.get("Idempotency-Replayed") == "true"
    return r.json()["id"], replayed

task_id, _ = submit()
task_id_again, replayed = submit()   # 丢了 id？原样再调一次
assert task_id == task_id_again and replayed
```

```typescript
const idem = crypto.randomUUID();          // 一个提交意图一把键

async function submit() {
  const res = await fetch("https://zhonkezhonkeapi.dflop.top/v1/videos/generations", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${PLATFORM_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": idem,             // ← 每次重试都用同一把
    },
    body: JSON.stringify({
      model: "doubao-seedance-2-0-260128",
      content: [{ type: "text", text: "海边日落，无人机航拍" }],
      duration: 5,
    }),
  });
  if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
  return {
    id: (await res.json()).id,
    replayed: res.headers.get("Idempotency-Replayed") === "true",
  };
}
```

| 情况 | 返回 |
|---|---|
| 不带这个头 | 行为与从前完全一致（不做任何去重） |
| 键格式非法 | **400** `invalid_idempotency_key` |
| 同键、请求体不同 | **409** `idempotency_key_reuse` —— 换一把新键 |
| 同键、上一次还在处理中 | **409** `idempotency_in_flight` —— 稍等重试，**不要**换键（换键会真的再提交一次）。通常几秒即可；如果上一次请求是客户端中途断开的，这把键最多会被占用 10 分钟才自动释放 |
| 同键、原响应体过大未留存 | **409** `idempotency_response_not_cached` —— 只会出现在显式 `response_format:"b64_json"` 的出图上（图片字节太大不予留存）。改用默认的 `response_format:"url"` 即可正常重放；已发生的那次只能换新键重发（会重新计费） |

> 只有 **2xx** 会被记住。上游报错、参数错误等失败**不会**占用这把键，可以直接用同一把键重试。

---

## 找回任务 id

异步任务（视频 / 音乐 / 语音合成）提交后返回一个任务 `id`。万一没保存，有三条路，按从易到难排：

1. **原样重发那条 curl**（带同一个 `Idempotency-Key`）→ 直接拿回原任务 id。见上一节。
2. **列出任务**：`GET /v1/videos/generations`、`GET /v1/music/generations`、`GET /v1/audio/speech`。**默认返回全部状态**（含排队中、生成中、已失败），按提交时间倒序，`?limit=` 默认 30 最大 100。
   ```bash
   curl "https://zhonkezhonkeapi.dflop.top/v1/videos/generations?limit=10" \
     -H "Authorization: Bearer $PLATFORM_API_KEY"
   ```
   `?status=` 可筛选，逗号多选：视频用 `queued,running,succeeded,failed,expired,cancelled,all`；音乐用 `processing,succeeded,failed,expired,cancelled,all`；语音合成用 `pending,succeeded,failed,all`。传 `?status=succeeded` 即为 2026-07-31 之前的旧默认行为。
3. **调用日志站** [logs.dflop.top](https://logs.dflop.top)：搜索框同时按 **任务 ID** 与 **请求 ID** 反查（粘进去就行，不用分辨手里那串是哪一种）。异步任务（出图 `"async": true` / 视频 / 音乐 / 语音 / 数字人形象 / 音色）的记录会带任务 ID；聊天、同步出图、文案提取这类没有任务行的调用只有请求 ID。
   - 请求 ID 就是响应头 `x-gateway-trace` 的值（所有响应都带，包括 4xx/5xx）。
   - ⚠️ 异步任务要**跑到终态结算后**才会成为账目行；还在生成中的任务会出现在明细页顶部的「进行中」区（带任务 id 与预扣积分），也可用上面第 2 条的列表端点查。日志默认只查最近 30 天。口径详见下方[对账与调用日志](#对账与调用日志)。

---

## 对账与调用日志

[logs.dflop.top](https://logs.dflop.top)（用 zhonkemodel.dflop.top 账号登录）是网关逐笔计量的账本，也是与你自己系统对账的依据。口径如下：

- **一行 = 一次对外调用的终态。** 网关在多条上游线路间自动切换产生的失败尝试**不单独成行、不计费、不计入请求数**，它们折叠在对应终态行的「内部尝试（未计费）」时间线里（行首标 `换道 ×N`）。
- **状态四种**：`success` 成功 / `error` 失败 / `interrupted` 中断（客户端断开或网关超时）/ `rejected` **已拒绝**。已拒绝 = 请求在提交那一刻被网关拒绝（参数错误、模型不可用、余额不足、内容拦截），**费用恒为 0**，但会入账，便于核对「发了但没成」的请求；`GET /api/v1/usage` 的 `total_requests` 含已拒绝、不含内部尝试。
- **进行中**：尚未到终态的异步任务（出图 / 视频 / 音乐 / 语音）显示在明细页顶部的「进行中」区，带任务 id、提交时间与**预扣**积分；结算后按实际产出多退少补并转为正常行。
- **提交时间 / 完成时间**：异步任务两者都记；同步调用记完成时间，提交时间按耗时反推。
- **结果链接**：出图 / 视频 / 音乐 / 语音的成功行带 `result_urls` 与 `result_expires_at`（本平台托管的图片 **24 小时**后清理；视频 / 音乐 / 语音为长期链接或带自己的过期时刻）。过期后日志只保留链接文本，不再能打开。
- **按 id 反查**：搜索框同时匹配任务 id 与请求 id（= 响应头 `x-gateway-trace`）。
- **CSV 导出**列：`id, created_at, key_name, key_prefix, model, status, error_code, error_message, input_tokens, output_tokens, cached_tokens, unit_count, unit_type, cost_usd, latency_ms, request_id, task_id, submitted_at, attempts_count, result_urls, result_expires_at`。`result_urls` 多个链接以 `|` 分隔；`cost_usd` 列名是历史遗留，值是**积分**。单次导出上限 10000 行，超出时响应头 `x-truncated: true`，请缩小时间范围分段导出。

---

## POST /v1/images/generations

OpenAI Images API 兼容形状,默认同步返回;长耗时可用 `"async": true` 转成提交 + 轮询(见下方「异步模式」)。

### 可用模型

| Model ID | 显示名 | 价格 (每张) | 备注 |
|---|---|---|---|
| `doubao-seedream-4-0-250828` | Seedream 4.0 | 11.73 | size ≥ 960×960 |
| `doubao-seedream-4-5-251128` | Seedream 4.5 | 14.96 | **size 须 ≥ 1920×1920**,否则上游返 400 |
| `doubao-seedream-5-0-260128` | Seedream 5.0 | 12.94 | **size 须 ≥ 1920×1920**,否则上游返 400 |
| `doubao-seedream-5-0-pro-260628` | Seedream 5.0 Pro | 输出 ≤236万像素 17.79,**超过 35.59** | size ≥ 960×960;**不传 `size` 时上游默认 2048×2048,按 35.59 档计费**——想走低档请显式传 ≤236万像素的尺寸(如 `1536x1536`);带 `image[]` 参考图每张输入另计 1.21(计入同一条账单行) |
| `grok-imagine-image` | Grok Imagine (Image) | 28.31 | 标准档 |
| `grok-imagine-image-quality` | Grok Imagine (Quality) | 28.31 | 高质量档 |
| `gpt-image-2` | GPT-Image 2 | 23.86 | 文生图 + 图生图(参考图不额外计费)。**上游不遵守 `size`**,比例请写进 `prompt`;实测耗时 30–215 秒。旧 id `gpt-image-2-low/medium/high`、`tvod-gpt-image2-*` 作为 alias 解析到本卡 |
| `tvod-midjourney-v8.1` | Midjourney v8.1 | 40.44 | **一次请求固定返回 4 张**(2×2 网格),`n` 不控制张数 ⇒ **单次实收 161.76**。`size` 只取宽高比,不承诺像素值。输出档位写在 `prompt` 末尾:`--sd` / `--hd` |
| `tvod-midjourney-v7` | Midjourney v7 | 32.35 | 同上,固定 4 张 ⇒ **单次实收 129.41** |

> `size` 原样透传给上游,gateway 不改写 —— Seedream 4.5/5.0 传小于 1920×1920 会直接拿到上游的 400 错误。Seedream 5.0 Pro 按**请求的输出像素面积**分档计费(阈值 236 万像素 ≈ 1536×1536)。**`gpt-image-2` 是例外:上游根本不读 `size`**,想控制画幅就在 `prompt` 里写「画面比例:16:9(横构图)」,提示词会压过 `size`。

> **Midjourney 两张卡与本端点的其余卡有两处不同,请按上表备注集成**:
> 1. **`n` 不控制出图数量** —— 上游 Midjourney 出的是 2×2 网格,一次请求**恒定返回 4 张**(2026-09-01 实测:`n=1` 与 `n=2` 都回 4 张)。请把 `n` 固定传 `1`。计费按**实际返回张数**结算,所以一次 v8.1 请求是 `40.44 × 4 = 161.76` 积分,余额需覆盖这个数(网关的预扣闸门也按 4 张校验,余额不够会在提交时就返回 402,而不是事后透支)。
> 2. **`size` 只表达宽高比,不表达像素** —— 网关把 `"宽x高"` 归一成最接近的画幅比(`1:1` / `16:9` / `9:16` / `4:3` / `3:4`)交给上游,实际像素由模型和输出档位决定。实测:`2048x2048` → 4 张 `1024x1024`;`2048x1152` → 4 张 `1456x816`;同样 `2048x1152` 加 `--hd` → 4 张 `2944x1648`。**不要把 `size` 当固定分辨率承诺**;比例落不到上面五档之一时按模型默认画幅出图。
>
> 输出档位写在 `prompt` 末尾(`--sd` 低档 / `--hd` 高档),与其余 Midjourney 参数一样随提示词透传。参考图、`--iw`、`--sref` 等高级参数**尚未在本渠道完成回归验证**,暂不作为已支持能力承诺。因为单次耗时较长,建议一律用下面的 **异步模式**(`"async": true` + 轮询)。

### 请求

```json
{
  "model": "doubao-seedream-4-5-251128",
  "prompt": "一只在竹林里喝茶的熊猫,水彩风格",
  "size": "2048x2048",
  "n": 1
}
```

| 字段 | 必填 | 说明 |
|---|---|---|
| `model` | ✓ | 上表 Model ID |
| `prompt` | ✓ | 描述文本 |
| `size` | | `"宽x高"`,透传上游(注意各 SKU 最小尺寸) |
| `n` | | 张数,默认 1,**上限 10**(超出返 400 `invalid_request`)。提交时按 `单价 × n` 预扣余额,**结算按实际返回张数**。⚠️ **固定网格的 SKU 忽略 `n`**(Midjourney 恒 4 张):这类卡按 `单价 × max(n, 4)` 预扣,请传 `n: 1`,详见上方模型表备注 |
| `image` | | 参考图(图生图):字符串或数组,每项是**公网 https URL** 或 `data:image/...;base64,...`。不传 = 文生图。`gpt-image-2` / Seedream / `nano-banana-2` 收 data URI,nano-banana 与 nano-banana-pro **仅收公网 URL** |
| `image_urls` | | `image` 的等价写法(数组);两者会被合并,wire 顺序保留(多图融合时顺序有语义) |

### 图生图(参考图)

同一个端点,带 `image` 就是图生图。完整示例、SDK 用法、比例控制与常见坑见 [图改图 (Image-to-Image)](../guides/image-editing.md)。

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/images/generations \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  --max-time 300 \
  -d '{
    "model": "gpt-image-2",
    "prompt": "把沙发改成蓝色,其余保持不变。画面比例:3:2(横构图)",
    "image": ["https://your-host.com/original.png"]
  }'
```

⚠️ 参考图给 URL 时,**去拉这张图的是上游服务器**,不是本网关 —— 国内对象存储 / 内网地址 / 需鉴权或挡爬虫的图床,上游会返 400 `Unable to download content from the provided URL`。这种情况改传 data URI,或走下面的 `/v1/images/edits` 直接上传文件字节。

### 响应

```json
{
  "model": "doubao-seedream-4-5-251128",
  "created": 1765432100,
  "expires_at": 1765518500,
  "data": [{ "url": "https://...", "size": "2048x2048" }],
  "usage": { "generated_images": 1, "output_tokens": 4096, "total_tokens": 4096 }
}
```

响应是上游原样透传(OpenAI Images 形状),`usage` 各字段以上游实际返回为准。`expires_at`(本网关扩展)是 `data[].url` 的过期时刻(Unix 秒),**只在图片由本平台托管(`r2.dflop.top`)时出现**;上游预签名链接不带此字段。

### curl

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/images/generations \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "doubao-seedream-4-5-251128",
    "prompt": "一只在竹林里喝茶的熊猫,水彩风格",
    "size": "2048x2048"
  }'
```

### 限制

- **返回的图片 URL 一律是临时链接** —— 多数 SKU 是上游预签名链接(约 24 小时过期),`gpt-image-2` 实测返回本平台托管的 `https://r2.dflop.top/gateway/images/ephemeral/<uuid>.png`(**24 小时后自动清理**,响应体顶层 `expires_at` 是精确过期时刻;经中转出的图同样转存到这个域)。两种都不是永久图床,拿到后请尽快下载转存到自己的存储
- 默认是同步接口。gateway 对上游的**单跳**超时是 **240 秒**(`IMAGES_UPSTREAM_TIMEOUT_SECS`),整条渠道阶梯的总上限是 **280 秒**(`IMAGES_LADDER_DEADLINE_SECS`)。多数 SKU 生成耗时 5–20 秒,但 `gpt-image-2` 实测 30–215 秒 —— **客户端超时请设 ≥ 300 秒**,否则会在网关仍在正常等待时被自己的超时打断(费用照产生,结果拿不到)
- 错误为 OpenAI 形状 `{"error": {"code", "message", "param", "type"}}`,上游 4xx/5xx 原状态码 + 原响应体透传(不计费)

### 异步模式(长耗时请用它)

同步调用受 CDN 约 125 秒的非流式响应上限约束。网关侧已做保活(超 90 秒会先把响应头发出去、边等边滴空白,所以**同步调用不会再被 CDN 掐断**),代价是:**超过 90 秒之后才失败的那一轮拿不到真实的 HTTP 状态码**,而是 `200` + `{"error": ...}` 响应体。

要拿到干净的失败语义、或者不想让一条 HTTP 连接开几分钟,请求体里加 `"async": true` 即可改成提交 + 轮询:

```bash
# 1. 提交 —— 立即返回,不等图
curl https://zhonkezhonkeapi.dflop.top/v1/images/generations \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-image-2", "prompt": "一只在竹林里喝茶的熊猫", "async": true}'
# → {"id": "9f1e...", "status": "queued", "model": "gpt-image-2", "created_at": 1786000000}

# 2. 轮询
curl https://zhonkezhonkeapi.dflop.top/v1/images/generations/9f1e... \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

轮询的契约只有一条:

| 响应 | 含义 |
|---|---|
| **`202`** + `{"id","status","model","created_at"}` | 还在跑,继续轮(建议 2–3 秒一次) |
| **其它任何状态码** | 这就是答案 —— 与同步调用**逐字节相同**的那个响应(成功是 `200` + `data[]`,失败是上游原状态码 + 原响应体) |

- 参数错误、模型不可用、余额不足、内容拦截等**仍然在提交那一刻同步返回真实 4xx**,不会先给你一个任务 id 再异步失败
- 任务最长跑 **280 秒**(渠道阶梯总预算),轮询预算请留 **≥ 5 分钟**;**客户端超时不等于任务失败** —— 任务仍在后台跑并照常结算,请用下面的列表接口按 id 找回结果,不要重新提交
- 提交时按最坏情况(`每张单价 × n` + 参考图费用)预留余额,终态按实际产出结算,零产出全额退回
- `GET /v1/images/generations` 列出你自己的任务(默认返回**全部状态**,新的在前)—— 任务 id 丢了从这里找回,不必重新提交;参数与字段见下方「列出任务」
- `/v1/images/edits` 同样支持:JSON 体加 `"async": true`,multipart 加一个 `-F "async=true"` 字段
- 被中断的任务(部署热替换等)一律**全额退款**并标记 `failed`,绝不重复扣费

#### 列出任务

`GET /v1/images/generations` 可筛可翻页(2026-10-16 起),对账时按提交时间段拉全量即可:

| 参数 | 含义 |
|---|---|
| `status` | `queued` / `running` / `succeeded` / `failed` / `in_flight`(排队中 + 生成中);缺省全部 |
| `from` / `to` | 按**提交时间**过滤,收 unix 秒、RFC 3339 或 `YYYY-MM-DD`(`to` 不含当天) |
| `limit` | 1–100,缺省 30 |
| `cursor` | 上一页响应的 `next_cursor`;为 `null` 即到末尾 |

每一项:`id` / `status` / `model` / `endpoint`(`generations` 或 `edits`)/ `created_at`(提交)/ `started_at` / `completed_at`(unix 秒)/ `unit_count`(实际出图张数)/ `cost`(实结**积分**,失败为 `0`)/ `error_code` / `request_id`(= `x-gateway-trace`)。`succeeded` 的项另带 `result: {"urls": [...], "expires_at": <unix 秒|null>}` —— `urls` 与轮询响应里的 `data[].url` 逐字相同,`expires_at` 只在托管链接上出现(24 小时)。

```bash
curl "https://zhonkezhonkeapi.dflop.top/v1/images/generations?status=succeeded&from=2026-08-30&to=2026-08-31&limit=100" \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

> Nano Banana 家族(`nano-banana` 15.77/张、`nano-banana-pro` 54.19/张、`nano-banana-2` 16.18/张)也走本端点按张计费;`nano-banana-2` 的响应以 `b64_json` 返回(自建首跳),其余通常返回 URL。旧 id(`gemini-2.5-flash-image` / `gemini-3-pro-image-preview` / `gemini-3.1-flash-image(-preview)` / `tvod-nano-*`)作为 alias 长期兼容。

---

## POST /v1/images/edits

图生图的另一个入口,存在的意义是让你**直接用 OpenAI SDK 已有的调用形状**(`client.images.edit(image=...)` 发的是 multipart),或者直接上传本地文件字节而不必自己做 base64。

模型、价格、渠道阶梯、`Idempotency-Key`、响应与错误形状**与 `/v1/images/generations` 完全一致** —— multipart 会被归一化成同一个 JSON 信封再往下走。唯一的行为差别:**这里不带参考图直接返回 400**(在这个端点上缺参考图是调用方的 bug,不会静默降级成文生图)。

### 两种请求体

**JSON**(与 generations 同形状):

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/images/edits \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  -H "Content-Type: application/json" \
  --max-time 300 \
  -d '{
    "model": "gpt-image-2",
    "prompt": "把沙发改成蓝色,其余保持不变",
    "image": ["data:image/png;base64,iVBORw0KGgo..."]
  }'
```

**multipart/form-data**(文件字段名 `image`,多张用 `image[]`;其余文本字段原样透传):

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/images/edits \
  -H "Authorization: Bearer $PLATFORM_API_KEY" \
  --max-time 300 \
  -F "model=gpt-image-2" \
  -F "prompt=把沙发改成蓝色,其余保持不变" \
  -F "image[]=@original.png" \
  -F "image[]=@style-reference.png"
```

### OpenAI SDK

```python
from openai import OpenAI

client = OpenAI(
    api_key=PLATFORM_API_KEY,
    base_url="https://zhonkezhonkeapi.dflop.top/v1",
    timeout=300.0,                       # gpt-image-2 实测 30–215 秒
)

result = client.images.edit(
    model="gpt-image-2",
    image=open("original.png", "rb"),
    prompt="把沙发改成蓝色,其余保持不变。画面比例:3:2(横构图)",
)
print(result.data[0].url)
```

### 限制

- 缺 `image` → **400**(错误信息会提示改用 `/v1/images/generations` 做文生图)
- 请求体上限 **95 MB**;base64 会让体积涨约 33%,真实手机照片(~3 MB/张)实际约 20 张到顶
- 其余限制(URL 24h 过期、超时预算、错误形状)同 `/v1/images/generations`
- 更完整的用法与选型见 [图改图 (Image-to-Image)](../guides/image-editing.md)

---

## POST /v1/videos/generations

**异步任务**:提交后立即返回任务 id,轮询查询直到 `succeeded`。

### 可用模型

| Model ID | 显示名 | 价格 (每秒) | 备注 |
|---|---|---|---|
| `doubao-seedance-1-0-pro-fast-251015` | Seedance 1.0 Pro Fast | 32.35 | |
| `doubao-seedance-1-0-pro-250528` | Seedance 1.0 Pro | 60.66 | |
| `doubao-seedance-1-5-pro-251215` | Seedance 1.5 Pro | 72.79 | |
| `doubao-seedance-2-0-fast-260128` | Seedance 2.0 Fast | 48.53 | |
| `doubao-seedance-2-0-260128` | Seedance 2.0 | 88.97 | |
| `doubao-seedance-2.0` | Seedance 2.0 | 分辨率分级 480p 33.16 / 720p 59.45 / 1080p 147.61 / 2k 291.17 / 4k 355.87 | 支持真人照片出镜;参考图自动审核入库。**带参考视频的轮按 token 计费**(用量含参考视频时长;此时必须显式传 `resolution`,否则 400 —— 见下方计费口径) |
| `doubao-seedance-2.0-fast` | Seedance 2.0 Fast | 分辨率分级 480p 23.86 / 720p 47.72 / 1080p 117.28 / 2k 141.54 / 4k 169.85 | **带参考视频的轮按 token 计费**(用量含参考视频时长;此时必须显式传 `resolution`,否则 400 —— 见下方计费口径) |
| `doubao-seedance-2.0-mini` | Seedance 2.0 Mini | 分辨率分级 480p 14.96 / 720p 29.93 | 轻量档;仅 480p/720p,4-15 秒。**带参考视频的轮按 token 计费**(用量含参考视频时长;此时必须显式传 `resolution`,否则 400 —— 见下方计费口径) |
| `doubao-seedance-2.5` | Seedance 2.5 | 分辨率分级 480p 42.06 / 720p 90.59 / 1080p 226 | 图片(≤30 张)/视频/音频多模态参考;可生成有声视频;4-30 秒。**带参考视频的轮按 token 计费**(2499.19/1M,不含视频输入时 4165.32/1M;**1080p 档 2790.12/1M,不含视频输入时 4650.21/1M** —— 见下方计费口径) |
| `doubao-seedance-2.0-lite` | Seedance 2.0 Lite | 分辨率分级 720p 29.93 / 1080p 64.7 | 高性价比档;**必须显式传 `resolution`**(仅 720p/1080p)。**带参考视频的轮按 token 计费**(用量含参考视频时长;此时必须显式传 `resolution`,否则 400 —— 见下方计费口径) |
| `doubao-seedance-2.0-fast-lite` | Seedance 2.0 Fast Lite | 分辨率分级 720p 24.67 / 1080p 52.57 | 同上,低延迟。**带参考视频的轮按 token 计费**(用量含参考视频时长;此时必须显式传 `resolution`,否则 400 —— 见下方计费口径) |
| `doubao-seedance-2.0-mini-lite` | Seedance 2.0 Mini Lite | 分辨率分级 720p 16.18 / 1080p 34.78 | 同上,轻量场景最低成本。**带参考视频的轮按 token 计费**(用量含参考视频时长;此时必须显式传 `resolution`,否则 400 —— 见下方计费口径) |
| `doubao-seedance-2.5-lite` | Seedance 2.5 Lite | 分辨率分级 720p 44.48 / 1080p 95.84 | 同上;能力同 `doubao-seedance-2.5`(4-30 秒、多模态参考、有声视频)。它的 1080p 档比正价卡便宜得多(95.84 vs 226) —— 两者交付分辨率相同、成本结构不同。带参考视频的轮按 token 计费(2758.01/1M,不含视频输入时 4424.14/1M) |
| `grok-imagine-video` | Grok Imagine Video | 283.08 | 文生/图生视频 |
| `grok-imagine-video-1.5-preview` | Grok Imagine Video 1.5 | 586.38 | **仅图生视频**(无参考图上游返 400) |
| `dh-avatar` | 数字人视频 | flat 按视频秒数(定价以站内目录为准) | 需先有一个可复用的数字人形象 `avatar`(照片/视频创建);形象 + 驱动音频(或文字+音色)→ 开口说话视频 |
| `clip-realman` | 智能剪辑 · 真人口播 | 4.04/秒(按成片时长) | 真人口播源视频 + 模板 → 自动加标题/字幕/身份栏/背景音乐的成片 |
| `clip-mixcut` | 智能剪辑 · 素材混剪 | 4.04/秒(按成片时长) | 口播音频 + 图片/视频素材 + 模板 → 自动配字幕/包装的成片 |
| `clip-news` | 智能剪辑 · 新闻快讯 | 2.43/秒(按成片时长) | 标题 + 图片/视频素材 + 模板 → 新闻体短视频,时长 5–300 秒可控 |

### 提交

```json
{
  "model": "doubao-seedance-1-0-pro-fast-251015",
  "content": [
    { "type": "text", "text": "海边日落,无人机航拍视角 --ratio 16:9" }
  ],
  "duration": 5
}
```

| 字段 | 必填 | 说明 |
|---|---|---|
| `model` | ✓ | 上表 Model ID(grok SKU 也用同一形状,gateway 自动转译) |
| `content` | ✓ | 数组:`{type:"text", text}` 必有;**图生视频**追加 `{type:"image_url", image_url:{url:"https://..."}}` |
| `duration` | | 秒数。**缺省按 12 秒(Seedance 上限)预扣余额**,结算按实际生成时长 —— 建议显式传 |
| `ratio` / `resolution` / `watermark` | | 透传上游(Seedance 文档口径)。⚠️ **Lite(`-lite`)四张卡的 `resolution` 是必填**:整张卡按交付分辨率计价,缺省会返 **400**;词表仅 `720p` / `1080p`,传其它档同样 400 |
| `video_mode` | | 仅 grok SKU + 参考视频时生效:`"extend"` = 续写,缺省/其他值 = 改写(见下) |

> **grok 视频转译细节**:grok SKU 走 xAI 上游,gateway 自动把上面的 Seedance 形状转译成 xAI 形状。`content` 里追加 `{type:"video_url", video_url:{url:"https://..."}}` 参考视频时,任务路由到 video-to-video 端点 —— 默认**改写**(按 prompt 重绘整段,沿用源视频比例/分辨率,不接受自定义 `duration`);`video_mode: "extend"` 切到**续写**(从末帧续 `duration` 秒,2–10 秒)。图生视频时 gateway 刻意不下发 `ratio`(跟随源图原生比例,避免拉伸变形)。

### 真人出镜(真实人脸)

`doubao-seedance-2.0` / `doubao-seedance-2.0-fast` / `doubao-seedance-2.0-mini` 三个 SKU 支持**上传真人照片生成真人出镜视频**。真人合规链路完全在 gateway 内部完成,调用方无需任何特殊步骤 —— 用普通图生视频形状提交即可,gateway 自动把参考图送审并入库(换成合规素材句柄)后再生成。若首选渠道拒绝真人图,gateway 自动切换到支持真人素材化的渠道,对调用方透明。

```json
{
  "model": "doubao-seedance-2.0",
  "resolution": "720p",
  "duration": 5,
  "content": [
    { "type": "text", "text": "照片中的人对着镜头微笑挥手,背景不变" },
    { "type": "image_url", "image_url": { "url": "https://your-cdn.com/face.jpg" } }
  ],
  "portrait_auth": true
}
```

| 字段 | 说明 |
|---|---|
| `image_url.url` | **必须是公网可直接拉取的 http(s) URL**(上游从公网抓取)。**不支持 base64 / `data:` 内联图**,会被 400 拒绝。图片需境内可达(本平台对象存储 `r2.dflop.top` 链接可用;部分境外源上游拉不到)。 |
| `portrait_auth` | 可选布尔。声明"已取得画面中真人的肖像授权",供平台审计留痕。**不影响是否出片**(真人路由由 SKU 决定),但涉及真人内容时建议显式传 `true` 表明合规责任。 |
| `resolution` | 真人档分辨率词表:`doubao-seedance-2.0` 支持 `480p`/`720p`/`1080p`/`2k`/`4k`;`-fast` 支持 `480p`/`720p`/`1080p`;`-mini` 仅 `480p`/`720p`。缺省由上游取默认档并按 flat 单价计费。 |

> **多模态参考(仅 Seedance 2.0 系)**:除单张首帧图外,`content[]` 还可携带带 `role` 的参考媒体项 —— `{type:"image_url", role:"reference_image", image_url:{url}}` / `{type:"video_url", role:"reference_video", video_url:{url}}` / `{type:"audio_url", role:"reference_audio", audio_url:{url}}`(参考图上限 10 张)。所有外部图同样走上述公网 URL + 自动送审规则。

响应:

```json
{ "id": "9f2c...", "status": "queued", "model": "doubao-seedance-1-0-pro-fast-251015", "created_at": 1765432100 }
```

提交本身是同步 HTTP(gateway 对上游超时 60 秒),生成在后台异步进行,不占请求时长。

### 数字人扩展字段(dh-avatar)

数字人 `dh-avatar` 复用同一视频提交端点,在请求体**顶层**追加以下字段(`duration` **必填** —— 取驱动音频/文案预估秒数,缺失返回 400)。⚠️ 数字人是**两段式**:必须先有一个可复用的数字人形象 `avatar` —— 在站内「克隆形象」上传照片/视频创建(平台公共形象因上游不返预览图已从站内下线);sk-key 直调传已有的形象 id 即可。

> **视频时长 = 驱动音频/文案时长**(上游自测真实时长,无固定上限)。`duration` 仅用于**计费预留**,结算按上游实际秒数。

| 字段 | 必填 | 说明 |
|---|---|---|
| `avatar` | ✓ | 数字人形象 id(站内「克隆形象」创建后可复用) |
| `audio_url` | 二选一 | 驱动音频(公网 URL,mp3/wav)—— 用音频驱动形象说话 |
| `voice` + `text` | 二选一 | 文字驱动:`voice`=音色 id(公共音色或克隆音色)、`text`=文案(≤10000 字),上游合成后驱动形象,一步出片 |
| `title` | | 作品名(≤20 字) |

注意事项:

- 输入媒体 URL 必须公网可直接访问(本平台 `r2.dflop.top` 上传产物可直接使用);
- 成片按中国 AIGC 内容标识要求自动叠加"AI 生成"标识。

### 智能剪辑扩展字段(clip-realman / clip-mixcut / clip-news)

智能剪辑三个 SKU 复用同一视频提交端点,在请求体**顶层**追加以下字段。三者共用 `style_id`(模板 id)、`title`、`language`、`materials[]`、`bgm`、`cover_url`,各自另有必填项。**成片长度由源媒体决定**(realman=源视频、mixcut=口播音频、news=`duration`),`duration` 对 realman/mixcut 仅供计费参考、不下发上游。

```json
{
  "model": "clip-realman",
  "style_id": "tpl_xxx",
  "title": "今日要闻",
  "source_video_url": "https://your-cdn.com/talk.mp4",
  "materials": [
    { "type": "image", "file_url": "https://your-cdn.com/a.jpg" },
    { "type": "video", "file_url": "https://your-cdn.com/b.mp4", "sound_switch": false }
  ],
  "bgm": { "mode": "auto" }
}
```

| 字段 | 适用 | 说明 |
|---|---|---|
| `style_id` | 全部 ✓ | 模板 id(取自平台智能剪辑模板库) |
| `source_video_url` | realman ✓ | 真人口播源视频(公网 URL) |
| `audio_url` | mixcut ✓ | 口播音频(公网 URL) |
| `materials` | mixcut/news ✓、realman 可选 | 数组 `{type:"image"\|"video", file_url, sound_switch?}`,最多 10 条 |
| `title` | news ✓、其余可选 | 作品/新闻标题 |
| `duration` | news | 目标成片秒数,`5–300`(超界自动 clamp);realman/mixcut 仅计费参考 |
| `material_composition` | news | `random`(随机)/ `order`(按序),缺省随机 |
| `preprocess` | realman | `roughCut` / `sliceMerge` 素材预处理方式 |
| `bgm` | 全部 | `{mode:"auto"\|"none"\|"custom", url?, volume?}`,缺省跟随模板 |
| `cover_url` | 全部 | 自定义首帧封面(公网图 URL) |
| `introduce_card` | 全部 | 身份栏 `{name, description}` |
| `language` | 全部 | 字幕语言 |

> **计费**:按**成片实际时长**(轮询返回的真实秒数)计费。外部 sk-key 提交时按 clip 成片上限(300 秒)预留余额,任务成功后结算退到实际时长;若显式传更长的 `duration`(如长源视频),按其预留。余额不足返回 402。失败/过期全额退回。
>
> **模板 id**:`style_id` 取自平台智能剪辑模板库;当前模板发现仅在站内数字人工作台内可见,sk-key 直调需使用已知的模板 id。

#### 素材与媒体要求（上游硬限）

所有 URL 必须公网可直接拉取。以下限制与上游一致,不满足会被上游拒(站内数字人工作台在上传时已就格式/分辨率/时长/大小先行校验)。

| 媒体 | 格式 | 大小 | 分辨率 | 时长 |
|---|---|---|---|---|
| 真人口播源视频 `source_video_url` | mp4 / mov(编码 h264 / HEVC,帧率 10–60fps 推荐 25) | < 500MB | 单边 < 2000px | < 5 分钟 |
| 素材图片 `materials[].file_url` (image) | jpg / png / webp 静态图 | — | 单边 < 2000px | 计 2s/张 |
| 素材视频 `materials[].file_url` (video) | mp4 / mov | < 500MB | 单边 < 2000px | 单个 ≤ 60s |
| 口播音频 `audio_url`(素材混剪) | mp3 / wav / m4a | ≤ 120MB | — | ≤ 5 分钟,需可语音转文本 |
| 背景音乐 `bgm.url` | mp3 / wav / m4a | ≤ 120MB | — | ≤ 5 分钟 |
| 首帧封面 `cover_url` | jpg / jpeg / png | ≤ 10MB | 单边 < 2000px | — |

- **素材总时长 ≤ 5 分钟**:图片各按 2s、视频按实际时长累加,超出上游拒。
- 真人口播源视频**画面内音频需能语音转文本**(用于自动字幕);无清晰人声会失败。
- `clip-news` 的成片时长由 `duration`(5–300s)控制;`clip-realman`/`clip-mixcut` 成片时长分别由源视频 / 口播音频决定。

### 轮询

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/videos/generations/$TASK_ID \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

`status` 取值:`queued` → `running` → `succeeded` / `failed` / `expired` / `cancelled`。

在飞行任务(`queued` / `running`)的响应可能带 `progress`(0-100 整数,上游生成进度)——仅在上游报告进度时出现,当前只有 Seedance 2.0 真人出镜档提供;字段缺失表示该模型无进度数据,不代表 0%。

成功时:

```json
{
  "id": "9f2c...",
  "status": "succeeded",
  "model": "doubao-seedance-1-0-pro-fast-251015",
  "created_at": 1765432100,
  "updated_at": 1765432460,
  "ratio": "16:9",
  "resolution": "1080p",
  "duration": 5,
  "generate_audio": true,
  "seed": 33608,
  "framespersecond": 24,
  "content": {
    "video_url": "https://...",
    "last_frame_url": "https://..."
  },
  "usage": { "completion_tokens": 411300, "total_tokens": 411300 },
  "video_url": "https://...",
  "expires_at": 1765435700,
  "duration_sec": 5.0
}
```

失败时带 `error: {code, message}`。生成一般需要 1–5 分钟,建议 5–10 秒一次轮询。

任务 id 仅本账号可见 —— 查询不存在或他人的任务一律返回 404(`code: "not_found"`),不做区分。

#### 响应字段

| 字段 | 出现时机 | 说明 |
|---|---|---|
| `id` | 恒有 | 任务 id |
| `status` | 恒有 | `queued` / `running` / `succeeded` / `failed` / `expired` / `cancelled` |
| `model` | 恒有 | 提交时的 Model ID(经规范化) |
| `created_at` | 恒有 | 任务创建时刻,Unix 秒 |
| `updated_at` | 恒有 | 状态最后变动时刻,Unix 秒。终态 = 任务落定时刻;仍在飞时回落 `created_at`(队列中/生成中的中间状态我们不单独打时间戳)。恒 ≥ `created_at`,恒不超过当前时间 |
| `ratio` / `resolution` / `frames` / `seed` / `generate_audio` / `output_format` / `service_tier` / `safety_identifier` / `execution_expires_after` / `draft` | 提交时传了才有 | 你在创建请求里指定的值,原样回显。**没传的键不出现** —— 我们不替上游编默认值(上游按自己的默认跑,我们无从观测)。请求侧的"你来定"哨兵同样不回显:`seed: -1`(随机)、`ratio: "adaptive"`(自动适配)——它们在响应里会被读成一个具体答案,而我们不知道上游实际取了什么 |
| `duration` | 仅 `succeeded` | 成片时长,**整数秒**。这是**交付时长**(与 `duration_sec` 同源、四舍五入),不是你下单时传的 `duration`。两者对 Seedance 恒相等;数字人文字驱动等按稿件估长的 SKU 上会不同,以本字段为准 |
| `content` | 仅 `succeeded` | 输出内容块。`video_url` = 成品地址;`last_frame_url` = 尾帧图(见下) |
| `content.last_frame_url` | 创建时传了 `return_last_frame: true`,且成功 | 成片**最后一帧**的 PNG,宽高与视频一致、无水印。用于**接龙生成长视频**:把它作为下一个任务的首帧图。已转存到本平台对象存储,是永久链接(上游原始链接 24 小时过期) |
| `framespersecond` | 上游报了才有 | 成片帧率。上游未提供时该键**不出现**(不代表 24) |
| `usage` | 仅 `succeeded`,且上游报了 token 用量时 | `{completion_tokens, total_tokens}`。视频模型不统计输入 token,故两者相等。**这是上游的用量口径,不等于本平台的计费口径** —— 见下方说明 |
| `video_url` | 仅 `succeeded` | 同 `content.video_url`,平铺一份 |
| `expires_at` | 仅 `succeeded` | `video_url` 过期时刻,Unix 秒 |
| `duration_sec` | 仅 `succeeded` | 同 `duration`,但保留小数精度。本网关扩展字段,火山没有 |
| `output_files` | 仅 `succeeded` 且为智能字幕类 SKU | 每语种一项的下载链接数组 |
| `progress` | 仅 `queued`/`running` 且上游报进度时 | 0–100 整数。**字段缺失表示该模型无进度数据,不代表 0%** |
| `error` | 仅 `failed`/`expired`/`cancelled` | `{code, message}` |

#### `usage` 与计费的关系

`usage.completion_tokens` 是**上游原样透传**的用量数字:上游报了我们就下发,不区分该 SKU 在本平台按什么计价。所以**看到 token 数不代表这张卡按 token 收费**。

本平台每张卡的计价口径以[模型与价格](./models.md)为准,只有两种:

- **按秒**(绝大多数视频 SKU):账单 = 单价 × 成片秒数,对账用 `duration_sec`。此时响应里的 `usage` 仅供参考,不参与计价。
- **按 token**(火山 Seedance 全家 —— 2.0 系与 2.5 系):账单 = 单价 × `usage.completion_tokens`,对账用它。

两种口径下的实际扣费,均以控制台用量页面里该次请求的记录为准。

##### Seedance 什么时候按秒、什么时候按 token

上游按 `(输入参考视频时长 + 输出视频时长) × 宽 × 高 × 帧率 / 1024` 计 token,
所以**成本会随参考视频变长而涨,而成片秒数一点没变** —— 按秒的算术表达不了这种轮。
判据只有一条:`content[]` 里**有没有 `video_url` 项**。

| 这一轮 | 计价 | 预扣 |
|---|---|---|
| 不带参考视频(文生 / 图生 / 首尾帧 / 参考图 / 参考音频) | **按秒** × 该分辨率档单价,与从前逐字节相同 | 单价 × 你传的 `duration` |
| 带参考视频(`{type:"video_url", …}`) | **按 token**,单价见[模型与价格](./models.md)的逐档表 | 单价 × (`duration` + **30 秒**) |

- **参考图与文字 prompt 不计入 token**(上游的 `prompt_tokens` 恒 0),多传参考图不额外收费。
- **参考音频不触发 token 计价** —— 官方公式里没有音频时长,带参考音频的轮仍按秒。
- ⚠️ **预扣要按 `duration + 30` 备余额,不是按 `duration`**。服务端量不了你那段参考视频有多长
  (也不能只信请求里的自报值),所以按上游允许的最大输入总时长 30 秒做最坏情况冻结;
  终态按上游回传的真实 `usage.completion_tokens` 结算,**多冻结的部分原样退回**。
  余额不足会在提交时直接返回 402 且不扣费。
- ⚠️ **带参考视频时 `resolution` 变成必填**,缺省返回 400。token 单价按交付档分级,
  没有档位就没法定价 —— 不带参考视频的轮不受此限,缺省仍按 flat 单价走(见下方 `resolution` 说明)。

#### 与火山方舟字段的对齐

Seedance 系 SKU 的请求/响应字段对齐火山方舟[创建](https://docs.volcengine.com/docs/82379/1520757) / [查询视频生成任务](https://docs.volcengine.com/docs/82379/1521309),照火山文档或官方 SDK 写死字段路径的代码可以直接指过来。五点差异请注意:

- **成品地址两处都有**:`content.video_url`(火山形状)与顶层 `video_url`(本网关早期形状)始终同值同时下发,读哪个都行。
- **任务 id 是本平台自己的 UUID**(如 `9f2c8a1e-…`),不是火山的 `cgt-` 前缀格式。把 id 当不透明字符串存,不要校验前缀或长度。
- **参数类字段是"你传的值",不是"上游实际取的值"**。火山把 `ratio` / `resolution` / `seed` 等定义为成品的实际属性;本网关回显的是你创建请求里指定的那个值。两者在指定了具体值时一致,所以我们只在你**确实指定过**时才发这些键,并且跳过 `adaptive` / `-1` 这类"由上游定"的哨兵 —— 宁可键缺席,也不报一个我们没观测过的数。
- **本网关的扩展字段**:`expires_at` / `duration_sec` / `output_files` / `progress` 火山没有,多出来的键不影响按火山口径解析。
- **暂未提供的火山字段**:`tools`(实际使用的工具)、`usage.tool_usage`(工具调用次数)、`priority`(执行优先级,本平台无优先级调度)。请求侧照传不误(原样转发上游),但响应里读不到。
- **本平台不适用**:`draft` / `draft_task_id`(样片模式)。火山限定 `Seedance 1.5 Pro`,本平台未上架该模型,这两个字段恒不返回。

### 列出任务

`GET /v1/videos/generations`(不带 id)—— 本账号的视频任务,按提交时间**倒序**。免费。
没保存任务 id 时用它找回,也可以直接当"生成记录"用。

| 查询参数 | 默认 | 说明 |
|---|---|---|
| `limit` | 30 | 1–100,超出按 100 截断 |
| `status` | *(全部)* | `queued` / `running` / `succeeded` / `failed` / `expired` / `cancelled` / `all`,**逗号可多选**(如 `?status=queued,running`)。取值非法返回 400 `invalid_request` |

> **2026-07-31 起默认返回全部状态**。此前默认只返回 `succeeded`,导致"任务还在跑时列表是空的"。要回到旧行为传 `?status=succeeded`。

```json
{
  "data": [
    {
      "id": "9a31d5c2-5c13-4caf-ad8b-1ee70ff5887f",
      "status": "running",
      "model": "doubao-seedance-2.0-fast-lite",
      "created_at": 1785495460,
      "progress": 42
    },
    {
      "id": "ed2ab10b-ceb1-4d0c-8c25-84ade94c95ac",
      "status": "succeeded",
      "model": "doubao-seedance-1-0-pro-fast-251015",
      "created_at": 1785490000,
      "content": { "video_url": "https://..." },
      "video_url": "https://...",
      "expires_at": 1786094800
    }
  ]
}
```

| 字段 | 出现时机 | 说明 |
|---|---|---|
| `id` | 恒有 | 任务 id,与轮询端点 `GET /v1/videos/generations/{id}` 的入参一致 |
| `status` | 恒有 | 同轮询端点的状态词表 |
| `model` | 恒有 | 提交时的 Model ID(经规范化) |
| `created_at` | 恒有 | 提交时刻,Unix 秒 |
| `progress` | 仅 `queued`/`running` 且上游报进度时 | 0–100 整数。**字段缺失表示该模型无进度数据,不代表 0%** |
| `content` | 仅 `succeeded` | `{video_url}`,与轮询端点**同形状**。⚠️ 但**不保证同 URL**:本端点优先给永久公共域,轮询端点给的是预签名链接。两者指向同一个成品,别拿字符串去比对或去重 |
| `video_url` | 仅 `succeeded` | 同 `content.video_url`。预签名链接 7 天有效;过期后重新调用本端点会自动重签 |
| `expires_at` | 仅 `succeeded` | `video_url` 过期时刻,Unix 秒 |
| `output_files` | 仅 `succeeded` 且为智能字幕类 SKU | 每语种一项的下载链接数组 |
| `error` | 仅 `failed`/`expired`/`cancelled` | `{code, message}` |

> 本端点是本网关的扩展(火山没有列表端点)。每项另带 `mode`(`t2v`/`i2v`)/ `prompt` / `ratio` / `resolution` / `duration` 等卡片字段,供"生成记录"直接渲染 —— 注意这几个是从**创建请求**还原的(`duration` 是你下单的秒数),与轮询端点里那个交付时长口径不同。

```python
import requests
r = requests.get(
    "https://zhonkezhonkeapi.dflop.top/v1/videos/generations",
    headers={"Authorization": f"Bearer {PLATFORM_API_KEY}"},
    params={"limit": 20},                       # 想只看在飞的:{"status": "queued,running"}
    timeout=30,
)
for t in r.json()["data"]:
    print(t["id"], t["status"], t.get("video_url", ""))
```

### 计费口径

- 提交时按 `单价 × duration` 从账户余额**预留**(缺 `duration` 按 12 秒预留);余额不足返回 402
- 任务终态结算:成功按**实际时长**计费(上游未报实际时长时按请求秒数兜底),失败/过期**全额退回**;提交阶段任何失败(上游报错、任务落库失败)也即时退回预留
- `GET /v1/videos/generations`(不带 id)列出本账号的任务,**默认返回全部状态**(`?limit=` 默认 30 最大 100;`?status=succeeded` 可只看成功的),可用于"生成记录"与找回丢失的任务 id —— 详见 [找回任务 id](#找回任务-id)

### 限制

- 成功的视频会自动转存到本平台对象存储,`video_url` 是 **7 天有效**的预签名链接(`expires_at` 为过期时间);过期后重新调用列表端点会自动重签新链接
- 上游内容审核可能在生成完成后拦截(`OutputVideoSensitiveContentDetected` 类错误码),该情况按失败处理不计费

---

## POST /v1/music/generations

Suno AI 音乐生成,异步任务形状与视频端点一致:提交返回任务 id,轮询到终态。**一次生成产出 2 首完整歌曲**(含歌词与封面图)。

### 可用模型

| Model ID | 显示名 | 价格 (每次生成) |
|---|---|---|
| `suno-v3.5` | Suno V3.5 | 19.41 |
| `suno-v4` | Suno V4 | 19.41 |
| `suno-v4.5` | Suno V4.5 | 19.41 |
| `suno-v5` | Suno V5 | 19.41 |
| `suno-v5.5` | Suno V5.5 (最新) | 19.41 |

### 请求

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/music/generations \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "suno-v5.5",
    "prompt": "一首关于夏天海边散步的轻快中文流行歌"
  }'
```

| 字段 | 类型 | 说明 |
|---|---|---|
| `model` | string | 必填,上表任一 id |
| `prompt` | string | 灵感模式描述词(≤200 字):AI 自行作词/起名/演唱 |
| `lyrics` | string | 自定义歌词(≤3000 字);传了即进入自定义模式,`prompt` 不再使用 |
| `title` | string | 歌名(自定义模式) |
| `tags` | string | 曲风,如 `"synthwave, female vocal"` |
| `negative_tags` | string | 排除曲风 |
| `instrumental` | bool | 纯音乐(忽略歌词) |

`prompt` 与 `lyrics` 至少传一个(纯音乐 `instrumental: true` 时可都省)。响应:`{"id": "<task_id>", "status": "queued", "model": "...", "created_at": ...}`。

### 查询任务

```bash
curl https://zhonkezhonkeapi.dflop.top/v1/music/generations/$TASK_ID \
  -H "Authorization: Bearer $GPUSHARE_API_KEY"
```

`status` 取值 `processing | succeeded | failed | expired`。成功时 `tracks` 数组给出每首歌:

```json
{
  "id": "…",
  "status": "succeeded",
  "tracks": [
    {
      "clip_id": "…",
      "title": "海风慢慢吹",
      "duration_sec": 192.0,
      "audio_url": "https://…mp3",
      "image_url": "https://…jpeg",
      "lyrics": "[Verse]…"
    }
  ]
}
```

生成一般需要 2–4 分钟,建议 10–20 秒一次轮询。任务 id 仅本账号可见,他人/不存在的任务一律 404。

### 列出任务

`GET /v1/music/generations`(不带 id)—— 本账号的音乐任务,按提交时间**倒序**。免费。
没保存任务 id 时用它找回。

| 查询参数 | 默认 | 说明 |
|---|---|---|
| `limit` | 30 | 1–100 |
| `status` | *(全部)* | `processing` / `succeeded` / `failed` / `expired` / `cancelled` / `all`,逗号可多选。⚠️ 音乐族**没有** `queued`/`running` —— 提交后到终态之间统一是 `processing`(与轮询端点同一套词表)。取值非法返回 400 |

响应是 `{"data": [ … ]}`,每项与上面轮询端点的单任务响应**逐字段一致**(`id` / `model` / `status` / `upstream_status` / `tracks[]` / `error_code` / `error_message` / `created_at` / `completed_at`),拿到列表项可以直接当轮询结果用,不必写两套解析。

```bash
curl "https://zhonkezhonkeapi.dflop.top/v1/music/generations?limit=10&status=processing" \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

### 计费口径

- 提交时按**固定单价**从账户余额预留(一次生成 = 2 首,单价已含);余额不足返回 402
- 任务终态结算:**至少 1 首成功即按全价计费**;两首全部失败或超 30 分钟未完成(过期)**全额退回**;提交阶段任何失败也即时退回预留
- 音频与封面会自动转存到本平台对象存储,`audio_url` / `image_url` 为 7 天预签名链接;转存失败时降级返回上游原始链接

---

## POST /v1/audio/speech

语音合成:文本(≤5000 字符)→ MP3,支持克隆音色与语速调节(语速仅对克隆音色生效)。按输入字符计费(`voice-tts-pro`,**125.36/千字符**)。

> ⚠️ **长文案请用异步模式。** 合成走上游异步队列(短文本几秒返回,长文案可能跑几分钟),而同步调用受 CDN 约 100 秒的非流式响应上限约束 —— 超时被掐断时音频照样渲染完、费用照样产生,但你什么也拿不到。请求体里加 `"async": true` 即可改成提交 + 轮询。

### 请求

```json
{
  "model": "voice-tts-pro",
  "input": "你好,欢迎使用语音合成。",
  "voice": "<可选,平台预设音色 id 或 /v1/audio/voices 返回的克隆音色 id;缺省为默认音色>",
  "speed": 1.0,
  "async": false
}
```

### 响应(同步,`async` 缺省 / `false`)

```json
{
  "model": "voice-tts-pro",
  "audio_url": "https://r2.dflop.top/audio-speech/…/xxx.mp3",
  "characters": 12,
  "cost_usd": "0.0037"
}
```

`audio_url` 是本平台对象存储的**永久公网链接**,可直接作为数字人(`dh-avatar`)的 `audio_url` 输入。

### 响应(`"async": true`)

立即返回任务 id,不阻塞:

```json
{ "id": "3a8e…", "model": "voice-tts-pro", "status": "pending", "characters": 1200, "created_at": "…" }
```

不带 id 的 **`GET /v1/audio/speech`** 列出本账号的合成任务(**默认全部状态**,`?limit=` 默认 30 最大 100,`?status=pending,succeeded,failed,all` 可筛选)—— 没保存任务 id 时用它找回。

再轮询 **`GET /v1/audio/speech/{id}`**(免费):

```json
{
  "id": "3a8e…", "model": "voice-tts-pro", "status": "succeeded",
  "characters": 1200, "duration_sec": "86.40",
  "audio_url": "https://r2.dflop.top/audio-speech/…/xxx.mp3", "created_at": "…"
}
```

`status` 三态:`pending` / `succeeded` / `failed`。**失败自动全额退款**;成功时才计费,音频同样落到永久链接。

### 列出任务

`GET /v1/audio/speech`(不带 id)—— 本账号的合成任务,按创建时间**倒序**。免费。
`"async": true` 提交后没保存任务 id 时用它找回。

| 查询参数 | 默认 | 说明 |
|---|---|---|
| `limit` | 30 | 1–100 |
| `status` | *(全部)* | `pending` / `succeeded` / `failed` / `all`,逗号可多选。取值非法返回 400 |

响应是 `{"data": [ … ]}`,每项与 `GET /v1/audio/speech/{id}` 的单任务响应**逐字段一致**(`id` / `model` / `status` / `characters` / `created_at`,成功时另有 `audio_url` / `duration_sec`,失败时有 `error.message`)。

```bash
curl "https://zhonkezhonkeapi.dflop.top/v1/audio/speech?limit=10" \
  -H "Authorization: Bearer $PLATFORM_API_KEY"
```

## /v1/audio/voices — 声音克隆与音色管理

| 方法 | 路径 | 说明 |
|---|---|---|
| POST | `/v1/audio/voices` | 克隆音色:`{name, audio_url, async?}`(参考音频公网 URL,5 秒–3 分钟清晰人声)。按次计费(`voice-clone-pro`,**40.44/次**) |
| GET | `/v1/audio/voices` | 列出本账号克隆音色 + 平台预设音色:`{voices:[…], presets:[{id, name}]}` |
| GET | `/v1/audio/voices/{id}` | 查询单个音色状态(`pending` / `ready` / `failed`) |
| DELETE | `/v1/audio/voices/{id}` | 删除音色(本地记录) |

克隆同样支持 **`"async": true`**:立即返回 `{id, status:"pending"}`,再轮询 `GET /v1/audio/voices/{id}` 到 `ready`。缺省是阻塞至就绪(数十秒–数分钟)—— 同上,受 CDN 约 100 秒上限约束,**新接入一律建议用异步**。失败自动全额退款。

克隆得到的音色 id 传入 `/v1/audio/speech` 的 `voice` 字段即可用该音色合成,也可以直接作为数字人 `dh-avatar` 文字驱动的 `voice`(见 [数字人 API](./digital-human-apis.md))。克隆真实人声前请确认已获得声音所有者授权。

平台还提供一批**公共音色**(`GET /v1/audio/voices` 响应里的 `presets`),无需克隆即可直接把公共音色的 `id` 传入 `voice` 使用。

---

## POST /v1/transcripts/extract

短视频链接 → 口播文案:粘贴一条短视频分享链接 / 分享口令,提取原视频的口播文案正文 + 元信息(标题 / 封面 / 平台 / 时长)。上游**自动识别平台**(抖音 / 快手 / 小红书 / B站 / 视频号 等主流平台),无需指定来源。

> 服务端**同步阻塞**至提取完成(内部:创建任务 → 轮询上游,通常 5–40 秒、最长约 55 秒返回)—— 客户端请把读取超时设足(建议 **≥ 90 秒**)。上游并发上限较低,高并发调用会排队变慢。

### 请求

```json
{
  "url": "https://v.douyin.com/xxxxxx/   —— 或直接粘贴分享口令原文"
}
```

| 字段 | 说明 |
|---|---|
| `url` | **必填**。短视频分享链接或分享口令原文(≤ 2000 字符)。`input` 为等价别名。 |

### 响应

```json
{
  "model": "video-transcript",
  "content": "提取出的口播文案正文……",
  "title": "原视频标题",
  "cover": "https://…封面图 URL",
  "platform": "douyin",
  "duration_sec": 42,
  "origin_link": "https://…上游回显的原始链接"
}
```

`platform` 为上游识别到的平台标识(如 `douyin` / `kuaishou`)。`content` 是核心口播文案;`title` / `cover` / `duration_sec` 为附带元信息,视频无对应字段时可能为空。

### curl

```bash
curl -X POST https://zhonkezhonkeapi.dflop.top/v1/transcripts/extract \
  -H "Authorization: Bearer $GPUSHARE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://v.douyin.com/xxxxxx/"}'
```

### 计费口径

- 按次固定计费(`video-transcript`,**20.22/次**),扣账户余额(全部 Key 共享)。
- **仅成功扣费**:链接无法解析 / 视频不支持 / 提取超时 / 提取到的文案被内容安全拦截,**均不计费**;只有干净成功返回文案才扣一次。
- 每次调用在用量与调用日志中以 `unit_type=transcript` 记录(响应带 `x-gateway-trace`,报障时连同时间戳一并提供)。

### 限制与错误

- `cover` 封面 URL 可能有**时效**(约 24 小时),需长期留存请自行下载转存。
- 输入 ≤ 2000 字符;上游并发上限较低,高并发会排队。
- 错误为归一化形状(与其它端点一致):链接无效 / 视频不支持 → **400**,提取超时 → **504**,服务额度暂不足 → **503**,上游连接失败 → **502**,余额不足 → **402**。
- 如需把某把 Key 限定为**只能调用本能力**,在该 Key 的 `allowed_models` 里加入 `video-transcript` 即可(不设 `allowed_models` = 可调用账户全部可用模型)。
