视频模型
万相 2.7 视频生成
通过统一视频任务接口调用万相 2.7 文生视频、图生视频、参考生视频和视频编辑模型。
- 提交路由:
POST /v1/videos - 任务查询:
GET /v1/videos/{task_id} - 内容下载:
GET /v1/videos/{task_id}/content
生成视频为异步任务。提交后先拿到 task_id,再轮询查询接口;任务成功后请尽快通过内容下载接口保存视频。万相 2.7 上游结果链接通常保留 24 小时,过期后需要重新发起任务。
支持的模型
| 模型 | 场景 | 主要输入 |
|---|---|---|
wan2.7-t2v | 文生视频 | prompt |
wan2.7-i2v | 图生视频、首尾帧、视频续写 | prompt、media 或 image/images |
wan2.7-r2v | 参考生视频、多主体参考、参考音色 | prompt、media |
wan2.7-videoedit | 视频编辑 | prompt、media 中的 video |
公共请求字段
| 字段 | 类型 | 说明 |
|---|---|---|
model | string | 必填。wan2.7-r2v 用于参考生视频。 |
prompt | string | 文本提示词。wan2.7-r2v 必填,最长 5000 字符。中文提示词中用“图1”“视频1”指代素材;英文提示词中用 “Image 1”“Video 1”。 |
media | array | 结构化素材列表。wan2.7-r2v 必填,至少包含一个 reference_image 或 reference_video。 |
negative_prompt | string | 反向提示词,最长 500 字符。 |
resolution | string | 分辨率档位。720P 或 1080P,默认 1080P;费用会随分辨率变化。 |
ratio | string | 宽高比。支持 16:9、9:16、1:1、4:3、3:4,默认 16:9。wan2.7-r2v 传入 first_frame 时会忽略该字段,并按首帧近似比例输出。 |
duration | number | 视频时长,单位秒,默认 5。wan2.7-r2v 含参考视频时范围为 2 到 10;不含参考视频时范围为 2 到 15。 |
seconds | string | 与 duration 等价的字符串形式;推荐新接入优先使用 duration。 |
metadata | object | 扩展参数。wan2.7-r2v 常用:prompt_extend、watermark、seed。 |
wan2.7-r2v 的 media
media 每一项至少包含 type 和 url。数组顺序决定提示词中的引用编号:第一个 reference_image 是“图1”,第一个 reference_video 是“视频1”。图片和视频分别计数。
| type | 是否可用 | 说明 |
|---|---|---|
reference_image | 必填组合之一 | 参考图像,可作为人物、动物、物体或场景参考。支持公网 URL、OSS 临时 URL、Base64 Data URL。 |
reference_video | 必填组合之一 | 参考视频,建议包含单一主体角色,可同时提供动作和音色参考。支持公网 URL、OSS 临时 URL。 |
first_frame | 可选 | 首帧图像,最多 1 张。传入后 ratio 会被忽略,输出比例以首帧为准。 |
reference_image 与 reference_video 合计最多 5 个;如果参考素材是主体角色,官方建议每个素材只包含单一角色。reference_image 和 reference_video 可携带 reference_voice,用于指定该主体音色。
素材限制
| 素材 | 格式 | 限制 |
|---|---|---|
reference_image / first_frame | JPEG、JPG、PNG、BMP、WEBP | 宽高范围 [240, 8000],宽高比 1:8 到 8:1,文件不超过 20MB;PNG 不支持透明通道。 |
reference_video | MP4、MOV | 时长 1 到 30 秒,宽高范围 [240, 4096],宽高比 1:8 到 8:1,文件不超过 100MB。 |
reference_voice | WAV、MP3 | 时长 1 到 10 秒,文件不超过 15MB;只参考音色,不决定说话内容。 |
参考生视频
多主体参考示例,包含参考图、参考视频和参考音色:
curl --request POST \
--url https://api.magickapi.com/v1/videos \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"model": "wan2.7-r2v",
"prompt": "视频1抱着图3,在图4的椅子上弹奏一支舒缓的乡村民谣,并说道:“今天的阳光真好。”图1手中拿着图2,路过视频1,把手中的图2放到视频1旁边的桌子上,并说道:“真好听,能不能再唱一遍”。",
"media": [
{
"type": "reference_image",
"url": "https://example.com/girl.jpg",
"reference_voice": "https://example.com/girl-voice.mp3"
},
{
"type": "reference_video",
"url": "https://example.com/boy-role.mp4",
"reference_voice": "https://example.com/boy-voice.mp3"
},
{
"type": "reference_image",
"url": "https://example.com/prop.png"
},
{
"type": "reference_image",
"url": "https://example.com/chair.png"
}
],
"resolution": "720P",
"ratio": "16:9",
"duration": 10,
"metadata": {
"prompt_extend": false,
"watermark": false,
"seed": 7
}
}'如果只传一张多宫格故事板参考图,也使用 reference_image:
{
"model": "wan2.7-r2v",
"prompt": "参考图片,3D 卡通冒险电影风,保持角色与森林场景一致。分镜:全景展示森林,小男孩拨开藤蔓,小机器人扫描前方,最后两人发现宝箱。",
"media": [
{
"type": "reference_image",
"url": "https://example.com/storyboard.png"
}
],
"resolution": "720P",
"duration": 10,
"metadata": {
"prompt_extend": false,
"watermark": false
}
}需要首帧约束时,可以额外传 first_frame:
{
"model": "wan2.7-r2v",
"prompt": "视频1中的角色从图1的房间门口走入画面,与图2中的角色交谈。",
"media": [
{
"type": "first_frame",
"url": "https://example.com/first-frame.png"
},
{
"type": "reference_video",
"url": "https://example.com/role.mp4"
},
{
"type": "reference_image",
"url": "https://example.com/second-role.png"
}
],
"resolution": "720P",
"duration": 8
}其他 Wan 2.7 场景
文生视频:
curl --request POST \
--url https://api.magickapi.com/v1/videos \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"model": "wan2.7-t2v",
"prompt": "一只纸飞机在阳光下缓慢飞过书桌,镜头稳定,画面简洁。",
"resolution": "720P",
"ratio": "16:9",
"duration": 5
}'图生视频:
{
"model": "wan2.7-i2v",
"prompt": "让画面中的小猫抬头看向镜头,镜头轻微推进。",
"image": "https://example.com/first-frame.png",
"resolution": "720P",
"duration": 5
}视频编辑:
{
"model": "wan2.7-videoedit",
"prompt": "保留主体动作,把背景替换为夜晚的赛博朋克街道。",
"media": [
{
"type": "video",
"url": "https://example.com/source.mp4"
},
{
"type": "reference_image",
"url": "https://example.com/style-reference.png"
}
],
"resolution": "720P",
"duration": 5,
"metadata": {
"audio_setting": "origin",
"watermark": false
}
}响应、查询与下载
提交成功后返回任务对象:
{
"id": "task_01KXYZ1234567890ABCDE",
"task_id": "task_01KXYZ1234567890ABCDE",
"object": "video",
"model": "wan2.7-r2v",
"status": "queued",
"progress": 0,
"created_at": 1712345678
}查询任务状态:
curl https://api.magickapi.com/v1/videos/task_01KXYZ1234567890ABCDE \
-H 'Authorization: Bearer YOUR_API_KEY'状态可能为 queued、in_progress、completed 或 failed。成功后响应的 metadata.url 是上游临时视频地址,建议直接使用内容下载接口保存文件:
curl -L https://api.magickapi.com/v1/videos/task_01KXYZ1234567890ABCDE/content \
-H 'Authorization: Bearer YOUR_API_KEY' \
--output wan2-7-r2v-output.mp4轮询示例
import { writeFile } from "node:fs/promises";
const baseUrl = "https://api.magickapi.com";
const apiKey = "YOUR_API_KEY";
const submitResp = await fetch(`${baseUrl}/v1/videos`, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "wan2.7-r2v",
prompt: "Image 1 sits by the cafe window playing guitar. Video 1 walks into the frame and talks with Image 1.",
media: [
{
type: "reference_image",
url: "https://example.com/character.png",
reference_voice: "https://example.com/voice.mp3",
},
{
type: "reference_video",
url: "https://example.com/action.mp4",
},
],
resolution: "720P",
ratio: "16:9",
duration: 10,
metadata: {
prompt_extend: false,
watermark: false,
},
}),
});
if (!submitResp.ok) {
throw new Error(await submitResp.text());
}
const task = await submitResp.json();
const taskId = task.id || task.task_id;
while (true) {
const queryResp = await fetch(`${baseUrl}/v1/videos/${taskId}`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const status = await queryResp.json();
if (status.status === "completed") {
const downloadResp = await fetch(`${baseUrl}/v1/videos/${taskId}/content`, {
headers: { Authorization: `Bearer ${apiKey}` },
});
const buffer = Buffer.from(await downloadResp.arrayBuffer());
await writeFile("wan2-7-r2v-output.mp4", buffer);
break;
}
if (status.status === "failed") {
throw new Error(status.error?.message || "video generation failed");
}
await new Promise((resolve) => setTimeout(resolve, 15000));
}最后更新于