> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gravitex.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 万相 2.7 视频编辑

> 阿里万相 wan2.7-videoedit 指令式视频编辑

## 简介

通过 Gravitex 网关调用阿里云百炼 `wan2.7-videoedit` 模型，对已有视频做指令式编辑（替换人物、修改服装、调整场景风格等）。接口为异步：先提交任务，再轮询结果。

| 接口       | 方法     | 路径                                |
| -------- | ------ | --------------------------------- |
| 创建视频编辑任务 | `POST` | `/v1/video/generations`           |
| 查询任务状态   | `GET`  | `/v1/video/generations/{task_id}` |

其中 `{task_id}` 为创建任务返回的 `id`。

<Note>
  与 [万相 2.7 视频生成](/cn/api-reference/endpoint/wan2.7)（T2V / I2V / R2V）共用统一视频入口，但模型与 `media` 参数语义不同，请勿混用。
</Note>

## 认证

<ParamField header="Authorization" type="string" required>
  Bearer Token，如 `Bearer sk-xxxxxxxx`
</ParamField>

<ParamField header="Content-Type" type="string" required>
  固定为 `application/json`
</ParamField>

<ParamField header="X-Trace-ID" type="string">
  请求跟踪 ID，建议每次请求生成唯一值
</ParamField>

## 请求参数

<ParamField body="model" type="string" required>
  固定为 `wan2.7-videoedit`
</ParamField>

<ParamField body="prompt" type="string" required>
  视频编辑指令
</ParamField>

<ParamField body="duration" type="integer">
  输出视频时长（秒），示例为 `4`
</ParamField>

<ParamField body="metadata.input.prompt" type="string" required>
  传给阿里云的原始提示词，建议与顶层 `prompt` 保持一致
</ParamField>

<ParamField body="metadata.input.media" type="array" required>
  输入媒体数组，至少包含一个视频
</ParamField>

<ParamField body="metadata.input.media[].type" type="string" required>
  原视频使用 `video`；参考图使用 `reference_image`（最多 4 张）
</ParamField>

<ParamField body="metadata.input.media[].url" type="string" required>
  公网可访问的 HTTP/HTTPS URL 或符合上游要求的临时 URL
</ParamField>

<ParamField body="metadata.parameters.resolution" type="string">
  `720P` 或 `1080P`
</ParamField>

<ParamField body="metadata.parameters.prompt_extend" type="boolean">
  是否启用提示词智能改写
</ParamField>

<ParamField body="metadata.parameters.watermark" type="boolean">
  是否添加水印
</ParamField>

## 请求示例

**最小请求（仅输入视频）：**

```bash theme={null}
curl --location 'https://api.gravitex.ai/v1/video/generations' \
  --header 'Authorization: Bearer sk-xxxxxxxx' \
  --header 'Content-Type: application/json' \
  --header 'X-Trace-ID: wan27-videoedit-test-001' \
  --data '{
    "model": "wan2.7-videoedit",
    "prompt": "背景不变，把视频中的小羊换成黑色的",
    "duration": 4,
    "metadata": {
      "input": {
        "prompt": "背景不变，把视频中的小羊换成黑色的",
        "media": [
          {
            "type": "video",
            "url": "https://your-public-oss.example.com/input.mp4"
          }
        ]
      },
      "parameters": {
        "resolution": "720P",
        "prompt_extend": true,
        "watermark": false
      }
    }
  }'
```

**添加参考图：**

可在同一个 `media` 数组中追加参考图；提示词里可用「图 1」指代。最多支持 **4** 张参考图。

```json theme={null}
{
  "model": "wan2.7-videoedit",
  "prompt": "保持视频中的人物动作不变，把人物衣服替换成参考图中的黑色西装",
  "duration": 4,
  "metadata": {
    "input": {
      "prompt": "保持视频中的人物动作不变，把人物衣服替换成参考图中的黑色西装",
      "media": [
        {
          "type": "video",
          "url": "https://your-public-oss.example.com/input.mp4"
        },
        {
          "type": "reference_image",
          "url": "https://your-public-oss.example.com/reference.png"
        }
      ]
    },
    "parameters": {
      "resolution": "720P",
      "prompt_extend": true,
      "watermark": false
    }
  }
}
```

## 创建任务响应

创建成功后返回标准化视频任务对象（无 `code/message/data` 外层）：

```json theme={null}
{
  "id": "76dc2556-f4****************29247",
  "object": "video",
  "model": "wan2.7-videoedit",
  "status": "queued",
  "progress": 0,
  "created_at": 1784714517,
  "metadata": {
    "output": {
      "task_id": "76dc2556-f4****************29247",
      "task_status": "PENDING"
    },
    "request_id": "86c168af-****************bf96d"
  }
}
```

请保存返回的 `id`，用于后续查询。

## 查询任务

```bash theme={null}
curl --location \
  'https://api.gravitex.ai/v1/video/generations/76dc2556-f4****************29247' \
  --header 'Authorization: Bearer sk-xxxxxxxx' \
  --header 'X-Trace-ID: wan27-videoedit-query-001'
```

查询响应带有 `code/message/data` 外层：

```json theme={null}
{
  "code": "success",
  "message": "",
  "data": {
    "id": "76dc2556-f4****************29247",
    "object": "video",
    "model": "wan2.7-videoedit",
    "status": "completed",
    "progress": 100,
    "created_at": 1784714517,
    "completed_at": 1784714687,
    "seconds": "8",
    "url": "https://signed-result-url.example.com/result.mp4",
    "video_url": "https://signed-result-url.example.com/result.mp4",
    "metadata": {
      "output": {
        "task_status": "SUCCEEDED",
        "input_video_duration": 4,
        "output_video_duration": 4,
        "video_url": "https://signed-result-url.example.com/result.mp4"
      }
    }
  }
}
```

## 状态处理

客户端应以 `data.status` 作为平台标准状态：

| `data.status` | 说明   | 客户端处理                            |
| ------------- | ---- | -------------------------------- |
| `queued`      | 排队中  | 继续轮询                             |
| `in_progress` | 生成中  | 继续轮询                             |
| `completed`   | 生成成功 | 读取 `data.video_url` 或 `data.url` |
| `failed`      | 生成失败 | 读取 `data.error.message`          |

上游状态位于 `data.metadata.output.task_status`（如 `PENDING`、`SUCCEEDED`），仅用于排查，不建议作为主状态判断依据。

建议轮询间隔 **5～15 秒**，不要高频轮询。

## 输出视频 URL

成功后优先读取 `data.video_url`，也可读取 `data.url`（当前适配器中通常相同）。

输出 URL 为带签名的临时地址，不应永久保存为业务资源地址；需长期保存时，请在有效期内下载或转存到自有 OSS。

## 时长和计费

实测中请求 `duration: 4` 时，上游 `usage` 可能如下：

| 字段                      | 含义                   |
| ----------------------- | -------------------- |
| `input_video_duration`  | 输入视频时长               |
| `output_video_duration` | 输出视频时长               |
| `usage.duration`        | 输入 + 输出的总计费时长        |
| `data.seconds`          | 网关响应对应上游总时长（示例为 `8`） |

因此 `data.seconds = 8` **不代表**输出生成了 8 秒，而是输入与输出合计计费时长。

阿里云官方计费：按「输入视频时长 + 输出视频时长」计费；输入图像不计费。详见 [官方计费说明](https://www.alibabacloud.com/help/en/model-studio/wan-video-editing-guide)。

## 常见错误排查

| 问题              | 说明                                                                                                                      |
| --------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `media` 缺少视频    | 至少包含 `{ "type": "video", "url": "..." }`                                                                                |
| 视频 URL 无法访问     | 勿用需登录页面、权限不足或已过期的 OSS URL；须公网可访问                                                                                        |
| JSON 不合法        | 布尔值写 `true`/`false`，不要写成 `**true**` 等                                                                                   |
| 只传了 `video_url` | 新接入请用 `input.media`；网关虽兼容旧 `video_url`，但不推荐                                                                             |
| 与 Wan 2.1 参数混用  | `wan2.7-videoedit` 用 `input.media`；旧版 `wan2.1-vace-plus` 用 `input.function = video_repainting` + `input.video_url`，不可混用 |

## Python 最小示例

```python theme={null}
import time
import uuid
import requests

BASE_URL = "https://api.gravitex.ai"
API_KEY = "sk-xxxxxxxx"

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
    "X-Trace-ID": uuid.uuid4().hex,
}

payload = {
    "model": "wan2.7-videoedit",
    "prompt": "背景不变，把视频中的小羊换成黑色的",
    "duration": 4,
    "metadata": {
        "input": {
            "prompt": "背景不变，把视频中的小羊换成黑色的",
            "media": [
                {
                    "type": "video",
                    "url": "https://your-public-oss.example.com/input.mp4",
                }
            ],
        },
        "parameters": {
            "resolution": "720P",
            "prompt_extend": True,
            "watermark": False,
        },
    },
}

response = requests.post(
    f"{BASE_URL}/v1/video/generations",
    headers=headers,
    json=payload,
    timeout=30,
)
response.raise_for_status()
task = response.json()
task_id = task["id"]

while True:
    result = requests.get(
        f"{BASE_URL}/v1/video/generations/{task_id}",
        headers={
            "Authorization": f"Bearer {API_KEY}",
            "X-Trace-ID": uuid.uuid4().hex,
        },
        timeout=30,
    )
    result.raise_for_status()
    data = result.json()["data"]
    status = data["status"]
    print(status, data.get("progress", 0))

    if status == "completed":
        print("video_url:", data.get("video_url") or data.get("url"))
        break
    if status == "failed":
        raise RuntimeError(data.get("error", {}).get("message", "video generation failed"))

    time.sleep(10)
```
