
Aliyun Vidu Video
- 52 installs
- 396 repo stars
- Updated July 18, 2026
- cinience/alicloud-skills
Generates video from DashScope Vidu models supporting text-to-video, image-to-video, keyframe interpolation, and reference-to-video via the async video-synthesis API.
About
This skill creates and polls async video-synthesis tasks against DashScope Vidu models across text, image, keyframe, and reference input modes. A developer uses it to produce AI videos from prompts or reference media with controllable resolution, size, and duration.
- Four modes: text2video, img2video, start-end2video, reference2video
- Async-only API with Q3/Q2 model tiers and detailed size/resolution tables
Aliyun Vidu Video by the numbers
- 52 all-time installs (skills.sh)
- Ranked #884 of 1,335 Generative Media skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/cinience/alicloud-skills --skill aliyun-vidu-videoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 52 |
|---|---|
| repo stars | ★ 396 |
| Last updated | July 18, 2026 |
| Repository | cinience/alicloud-skills ↗ |
What it does
Generates video from DashScope Vidu models supporting text-to-video, image-to-video, keyframe interpolation, and reference-to-video via the async video-synthesis API.
Files
Vidu Video Generation
Validation
mkdir -p output/aliyun-vidu-video
python -m py_compile skills/ai/video/aliyun-vidu-video/scripts/generate_vidu_video.py && echo "py_compile_ok" > output/aliyun-vidu-video/validate.txtPass criteria: command exits 0 and output/aliyun-vidu-video/validate.txt is generated.
Output And Evidence
- Save task IDs, polling responses, and final video URLs to
output/aliyun-vidu-video/. - Keep at least one end-to-end run log for troubleshooting.
Prerequisites
- Set
DASHSCOPE_API_KEYin your environment (Beijing region key required). - Region: China Mainland (Beijing) only. Model, Endpoint URL, and API Key must belong to the same region.
- Enable Vidu models in the Alibaba Cloud Model Studio console before first use.
Critical model names
Text-to-video
vidu/viduq3-pro_text2videovidu/viduq3-turbo_text2videovidu/viduq2_text2video
Image-to-video (first frame)
vidu/viduq3-pro_img2videovidu/viduq3-turbo_img2videovidu/viduq2-pro_img2videovidu/viduq2-turbo_img2video
Keyframe-to-video (first+last frame)
vidu/viduq3-pro_start-end2videovidu/viduq3-turbo_start-end2videovidu/viduq2-pro_start-end2videovidu/viduq2-turbo_start-end2video
Reference-to-video
vidu/viduq2_reference2videovidu/viduq2-pro_reference2video
Capabilities
| Capability | Description | Model suffix | Required input |
|---|---|---|---|
| Text-to-video | Generate video from text prompt only | _text2video | prompt |
| Image-to-video | Generate video from a single image + optional prompt | _img2video | media[image] |
| Keyframe-to-video | Interpolate video between first and last frame images | _start-end2video | media[image x2] + prompt |
| Reference-to-video | Embed reference subject(s) into prompted scene | _reference2video | media[image 1-7] + prompt |
API endpoint (async only)
POST https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesisRequired headers:
Authorization: Bearer $DASHSCOPE_API_KEYContent-Type: application/jsonX-DashScope-Async: enable
Normalized interface
Request
model(string, required) -- one of the model names listed aboveinput.prompt(string) -- up to 5000 characters, describes desired video content- Required for text-to-video, keyframe, and reference modes
- Optional for image-to-video
input.media(array) -- media objects withtypeandurlfields (not used for text-to-video)type:imageorvideourl: public URL (HTTP/HTTPS)parameters.resolution(string, optional) --540P,720P(default), or1080Pparameters.size(string, optional) -- pixel dimensionswidth*height(e.g.,1280*720). Values depend on resolution tier. For text-to-video and reference-to-video, explicit size values are supported.parameters.duration(integer, optional) -- video length in seconds- Q3 models: [1, 16], default 5
- Q2 models: [1, 10], default 5
parameters.audio(boolean, optional) -- generate audio track (Q3 models only, default false)parameters.watermark(boolean, optional) -- add "AI generated" watermark (default false)parameters.seed(integer, optional) -- range [0, 2147483647]
Size values by resolution tier (text-to-video)
| Resolution | Aspect ratio | Size (width*height) |
|---|---|---|
| 540P | 16:9 | 960*528 |
| 540P | 9:16 | 528*960 |
| 540P | 1:1 | 720*720 |
| 540P | 4:3 | 816*608 |
| 540P | 3:4 | 608*816 |
| 720P | 16:9 | 1280*720 |
| 720P | 9:16 | 720*1280 |
| 720P | 1:1 | 960*960 |
| 720P | 4:3 | 1104*816 |
| 720P | 3:4 | 816*1104 |
| 1080P | 16:9 | 1920*1080 |
| 1080P | 9:16 | 1080*1920 |
| 1080P | 1:1 | 1440*1440 |
| 1080P | 4:3 | 1674*1238 |
| 1080P | 3:4 | 1238*1674 |
Size values by resolution tier (reference-to-video)
| Resolution | Aspect ratio | Size (width*height) |
|---|---|---|
| 540P | 16:9 | 960*540 |
| 540P | 9:16 | 540*960 |
| 540P | 1:1 | 540*540 |
| 540P | 4:3 | 720*540 |
| 540P | 3:4 | 540*720 |
| 720P | 16:9 | 1280*720 |
| 720P | 9:16 | 720*1280 |
| 720P | 1:1 | 720*720 |
| 720P | 4:3 | 960*720 |
| 720P | 3:4 | 720*960 |
| 1080P | 16:9 | 1920*1080 |
| 1080P | 9:16 | 1080*1920 |
| 1080P | 1:1 | 1080*1080 |
| 1080P | 4:3 | 1440*1080 |
| 1080P | 3:4 | 1080*1440 |
Media input limits
Images (type=image):
- Formats: JPG, PNG, WEBP
- Aspect ratio: 1:4 to 4:1
- Max size: 50MB
Videos (type=video, reference-to-video only):
- Formats: mp4, avi, mov
- Resolution: min 128x128 pixels
- Aspect ratio: 1:4 to 4:1
- Duration: 1-5s
- Max size: 50MB
Response (task creation)
output.task_id(string) -- use for polling, valid 24 hoursoutput.task_status(string) -- PENDING | RUNNING | SUCCEEDED | FAILED | CANCELED | UNKNOWNrequest_id(string)
Response (task result)
output.video_url(string) -- generated video URL (MP4, H.264), valid 24 hoursoutput.orig_prompt(string) -- original promptusage.duration(integer) -- billable video duration in secondsusage.output_video_duration(integer) -- actual output durationusage.size(string) -- output resolutionusage.fps(integer) -- frame rate (24)usage.audio(boolean) -- whether audio was generatedusage.SR(string) -- resolution tier
Quick start (Python + HTTP)
import os
import json
import time
import requests
API_KEY = os.getenv("DASHSCOPE_API_KEY")
BASE_URL = "https://dashscope.aliyuncs.com/api/v1"
def create_vidu_task(req: dict) -> str:
"""Create a Vidu video generation task and return task_id."""
payload = {
"model": req["model"],
"input": {},
"parameters": {
"resolution": req.get("resolution", "720P"),
"duration": req.get("duration", 5),
},
}
if req.get("prompt"):
payload["input"]["prompt"] = req["prompt"]
if req.get("media"):
payload["input"]["media"] = req["media"]
if req.get("size"):
payload["parameters"]["size"] = req["size"]
if req.get("audio") is not None:
payload["parameters"]["audio"] = req["audio"]
if req.get("watermark") is not None:
payload["parameters"]["watermark"] = req["watermark"]
if req.get("seed") is not None:
payload["parameters"]["seed"] = req["seed"]
resp = requests.post(
f"{BASE_URL}/services/aigc/video-generation/video-synthesis",
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"X-DashScope-Async": "enable",
},
json=payload,
)
resp.raise_for_status()
data = resp.json()
return data["output"]["task_id"]
def poll_task(task_id: str, interval: int = 15) -> dict:
"""Poll until task completes. Returns final response."""
while True:
resp = requests.get(
f"{BASE_URL}/tasks/{task_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
)
resp.raise_for_status()
data = resp.json()
status = data["output"]["task_status"]
if status in ("SUCCEEDED", "FAILED", "CANCELED"):
return data
time.sleep(interval)Mode-specific examples
# Text-to-video
task_id = create_vidu_task({
"model": "vidu/viduq3-turbo_text2video",
"prompt": "A cat running under moonlight",
"resolution": "540P",
"size": "960*528",
"duration": 5,
})
# Image-to-video (first frame)
task_id = create_vidu_task({
"model": "vidu/viduq3-pro_img2video",
"prompt": "Camera slowly pans upward",
"media": [{"type": "image", "url": "https://example.com/image.jpg"}],
"resolution": "720P",
"duration": 5,
})
# Keyframe-to-video (first + last frame)
task_id = create_vidu_task({
"model": "vidu/viduq3-turbo_start-end2video",
"prompt": "A cat jumps from windowsill to sofa",
"media": [
{"type": "image", "url": "https://example.com/first.png"},
{"type": "image", "url": "https://example.com/last.png"},
],
"resolution": "540P",
"duration": 5,
})
# Reference-to-video
task_id = create_vidu_task({
"model": "vidu/viduq2_reference2video",
"prompt": "Man playing guitar in a cafe",
"media": [
{"type": "image", "url": "https://example.com/ref1.jpg"},
{"type": "image", "url": "https://example.com/ref2.jpg"},
],
"resolution": "720P",
"size": "1280*720",
"duration": 5,
})Error handling
| Error | Likely cause | Action |
|---|---|---|
| 401/403 | Missing or invalid DASHSCOPE_API_KEY | Check env var; ensure Beijing region key |
400 InvalidParameter | Unsupported resolution/size combo, bad duration, missing media | Validate parameters against size tables |
| "does not support synchronous calls" | Missing X-DashScope-Async: enable header | Add required header |
| 429 | Rate limit or quota | Retry with backoff |
| Cross-region error | Model and API Key from different regions | Ensure all are Beijing region |
Output location
- Default output:
output/aliyun-vidu-video/videos/ - Override base dir with
OUTPUT_DIR.
Anti-patterns
- Do not use model names not listed in "Critical model names" above.
- Do not call this API synchronously -- async header is required.
- Do not omit
sizewhen using reference-to-video -- it is required for that mode. - Do not pass
audio=truewith Q2 models -- only Q3 models support audio generation. - Video URLs expire after 24 hours; download and persist immediately.
- For image-to-video, supply exactly 1 image. For keyframe, supply exactly 2 images (first, then last).
- For reference-to-video with
viduq2_reference2video, only images are accepted (1-7). Forviduq2-pro_reference2video, images (1-4) plus optional videos (1-2) are accepted. - Keyframe mode: first and last frame pixel count ratio must be between 0.8 and 1.25.
Workflow
1) Confirm user intent: text-to-video, image-to-video, keyframe, or reference-to-video. 2) Select the appropriate model name based on capability and quality tier (Q3 pro/turbo or Q2). 3) Prepare input: prompt and/or media array with correct types and valid public URLs. 4) Set resolution, size, and duration parameters. 5) Create async task and poll for results (15s interval recommended). 6) Download and save generated video before URL expiration (24 hours).
References
- See
references/api_reference.mdfor full HTTP API details. - See
references/sources.mdfor source links.
DashScope API Reference (Vidu Video Generation)
Models
| Model | Capability | Resolution | Duration |
|---|---|---|---|
| vidu/viduq3-pro_text2video | Text-to-video | 540P, 720P, 1080P | 1-16s |
| vidu/viduq3-turbo_text2video | Text-to-video | 540P, 720P, 1080P | 1-16s |
| vidu/viduq2_text2video | Text-to-video | 540P, 720P, 1080P | 1-10s |
| vidu/viduq3-pro_img2video | Image-to-video | 540P, 720P, 1080P | 1-16s |
| vidu/viduq3-turbo_img2video | Image-to-video | 540P, 720P, 1080P | 1-16s |
| vidu/viduq2-pro_img2video | Image-to-video | 720P, 1080P | 1-10s |
| vidu/viduq2-turbo_img2video | Image-to-video | 720P, 1080P | 1-10s |
| vidu/viduq3-pro_start-end2video | Keyframe (first+last) | 540P, 720P, 1080P | 1-16s |
| vidu/viduq3-turbo_start-end2video | Keyframe (first+last) | 540P, 720P, 1080P | 1-16s |
| vidu/viduq2-pro_start-end2video | Keyframe (first+last) | 540P, 720P, 1080P | 1-10s |
| vidu/viduq2-turbo_start-end2video | Keyframe (first+last) | 540P, 720P, 1080P | 1-10s |
| vidu/viduq2_reference2video | Reference-to-video | 540P, 720P, 1080P | 1-10s |
| vidu/viduq2-pro_reference2video | Reference-to-video | 540P, 720P, 1080P | 1-10s |
Endpoint
POST https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesisRegion: Beijing (China Mainland) only.
Required headers
| Header | Value |
|---|---|
| Content-Type | application/json |
| Authorization | Bearer $DASHSCOPE_API_KEY |
| X-DashScope-Async | enable |
Request body -- Text-to-video
{
"model": "vidu/viduq3-turbo_text2video",
"input": {
"prompt": "A cat running under moonlight"
},
"parameters": {
"resolution": "540P",
"size": "960*528",
"duration": 5,
"audio": false,
"watermark": true,
"seed": 12345
}
}Request body -- Image-to-video
{
"model": "vidu/viduq3-pro_img2video",
"input": {
"media": [
{"type": "image", "url": "https://example.com/image.jpg"}
],
"prompt": "Camera slowly pans upward"
},
"parameters": {
"duration": 5,
"resolution": "720P",
"watermark": true
}
}Request body -- Keyframe-to-video
{
"model": "vidu/viduq3-turbo_start-end2video",
"input": {
"media": [
{"type": "image", "url": "https://example.com/first_frame.png"},
{"type": "image", "url": "https://example.com/last_frame.png"}
],
"prompt": "A cat jumps from windowsill to sofa"
},
"parameters": {
"resolution": "540P",
"duration": 5,
"watermark": true
}
}Request body -- Reference-to-video (images only)
{
"model": "vidu/viduq2_reference2video",
"input": {
"media": [
{"type": "image", "url": "https://example.com/ref1.jpg"},
{"type": "image", "url": "https://example.com/ref2.png"},
{"type": "image", "url": "https://example.com/ref3.png"}
],
"prompt": "Man playing guitar in a cafe"
},
"parameters": {
"duration": 5,
"size": "1280*720",
"resolution": "720P",
"watermark": true
}
}Request body -- Reference-to-video (images + video)
Supported by vidu/viduq2-pro_reference2video only.
{
"model": "vidu/viduq2-pro_reference2video",
"input": {
"media": [
{"type": "video", "url": "https://example.com/ref_video.mp4"},
{"type": "image", "url": "https://example.com/ref1.png"},
{"type": "image", "url": "https://example.com/ref2.png"}
],
"prompt": "Man playing guitar in a cafe"
},
"parameters": {
"duration": 5,
"size": "1280*720",
"resolution": "720P",
"watermark": true
}
}input fields
| Field | Type | Required | Description |
|---|---|---|---|
| prompt | string | Varies | Text prompt, up to 5000 chars. Required for t2v, keyframe, ref. Optional for i2v. |
| media | array | Varies | Media objects with type and url. Required for i2v, keyframe, ref. Not used for t2v. |
media element fields
| Field | Type | Required | Values |
|---|---|---|---|
| type | string | Yes | image or video |
| url | string | Yes | Public HTTP/HTTPS URL |
Media constraints by mode
| Mode | Images | Videos | Notes |
|---|---|---|---|
| Text-to-video | N/A | N/A | No media input |
| Image-to-video | Exactly 1 | 0 | Single first-frame image |
| Keyframe | Exactly 2 | 0 | First element = first frame, second = last frame |
| Reference (viduq2) | 1-7 | 0 | Images only |
| Reference (viduq2-pro) | 1-4 | 0-2 | Images required, videos optional |
Image constraints
| Property | Limit |
|---|---|
| Formats | JPG, PNG, WEBP |
| Aspect ratio | 1:4 to 4:1 |
| Max file size | 50MB |
Video constraints (reference mode)
| Property | Limit |
|---|---|
| Formats | mp4, avi, mov |
| Min resolution | 128x128 |
| Aspect ratio | 1:4 to 4:1 |
| Duration | 1-5s |
| Max file size | 50MB |
parameters fields
| Field | Type | Default | Description |
|---|---|---|---|
| resolution | string | 720P | 540P, 720P, or 1080P. Affects pricing. |
| size | string | varies | Pixel dimensions width*height. See size tables in SKILL.md. |
| duration | integer | 5 | Video length in seconds. Q3: [1,16], Q2: [1,10]. Affects pricing. |
| audio | boolean | false | Generate audio track. Q3 models only. |
| watermark | boolean | false | Add "AI generated" watermark |
| seed | integer | auto | Range [0, 2147483647] |
Task creation response
{
"output": {
"task_status": "PENDING",
"task_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"request_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
}Task polling
GET https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}
Header: Authorization: Bearer $DASHSCOPE_API_KEYRecommended polling interval: 15 seconds.
Success response
{
"request_id": "...",
"output": {
"task_id": "...",
"task_status": "SUCCEEDED",
"submit_time": "2026-03-27 13:32:13.962",
"scheduled_time": "2026-03-27 13:32:14.008",
"end_time": "2026-03-27 13:32:43.375",
"orig_prompt": "original prompt text",
"video_url": "https://prod-ss-vidu.s3.cn-northwest-1.amazonaws.com.cn/xxx.mp4?xxx"
},
"usage": {
"duration": 5,
"size": "960*528",
"output_video_duration": 5,
"fps": 24,
"video_count": 1,
"audio": false,
"SR": "540"
}
}Failure response
{
"request_id": "...",
"output": {
"task_id": "...",
"task_status": "FAILED",
"code": "InvalidParameter",
"message": "The size is not match xxxxxx"
}
}Task statuses
| Status | Description |
|---|---|
| PENDING | Queued |
| RUNNING | Processing |
| SUCCEEDED | Complete -- video_url available |
| FAILED | Failed -- check code/message |
| CANCELED | Canceled |
| UNKNOWN | Not found or expired (task_id valid for 24h) |
Error codes
| Code | Description | Resolution |
|---|---|---|
| InvalidApiKey | API Key missing or invalid | Check DASHSCOPE_API_KEY |
| InvalidParameter | Parameter validation failed | Check size/resolution/duration values |
| Throttling | Rate limit exceeded | Retry with backoff |
| InternalError | Server error | Retry after delay |
Sources
- Vidu-文生视频API参考 -- Text-to-video API documentation
- Vidu-图生视频-基于首帧API参考 -- Image-to-video (first frame) API documentation
- Vidu-图生视频-基于首尾帧API参考 -- Keyframe-to-video (first+last frame) API documentation
- Vidu-参考生视频API参考 -- Reference-to-video API documentation
- 视频生成模型概览 -- Model overview and selection guide
- 模型价格 -- Pricing details
"""Vidu video generation via DashScope HTTP API.
Supports: text-to-video, image-to-video, keyframe-to-video, reference-to-video.
Usage:
# Text-to-video
python generate_vidu_video.py --mode t2v --model vidu/viduq3-turbo_text2video --prompt "A cat running"
# Image-to-video (first frame)
python generate_vidu_video.py --mode i2v --model vidu/viduq3-pro_img2video --image https://example.com/img.jpg
# Keyframe-to-video (first + last frame)
python generate_vidu_video.py --mode keyframe --model vidu/viduq3-turbo_start-end2video \
--image https://example.com/first.png --end-image https://example.com/last.png \
--prompt "A cat jumps from windowsill to sofa"
# Reference-to-video
python generate_vidu_video.py --mode ref --model vidu/viduq2_reference2video \
--image https://example.com/ref1.jpg --image https://example.com/ref2.jpg \
--prompt "Man playing guitar in a cafe" --size 1280*720
"""
import argparse
import json
import os
import sys
import time
import requests
API_KEY = os.getenv("DASHSCOPE_API_KEY", "")
BASE_URL = os.getenv(
"DASHSCOPE_BASE_URL",
"https://dashscope.aliyuncs.com/api/v1",
)
OUTPUT_DIR = os.getenv("OUTPUT_DIR", "output/aliyun-vidu-video/videos")
VALID_MODELS = {
"t2v": [
"vidu/viduq3-pro_text2video",
"vidu/viduq3-turbo_text2video",
"vidu/viduq2_text2video",
],
"i2v": [
"vidu/viduq3-pro_img2video",
"vidu/viduq3-turbo_img2video",
"vidu/viduq2-pro_img2video",
"vidu/viduq2-turbo_img2video",
],
"keyframe": [
"vidu/viduq3-pro_start-end2video",
"vidu/viduq3-turbo_start-end2video",
"vidu/viduq2-pro_start-end2video",
"vidu/viduq2-turbo_start-end2video",
],
"ref": [
"vidu/viduq2_reference2video",
"vidu/viduq2-pro_reference2video",
],
}
ALL_MODELS = [m for models in VALID_MODELS.values() for m in models]
def _headers() -> dict:
return {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
"X-DashScope-Async": "enable",
}
def create_task(
model: str,
prompt: str = "",
media: list[dict] | None = None,
resolution: str = "720P",
size: str | None = None,
duration: int = 5,
audio: bool = False,
watermark: bool = False,
seed: int | None = None,
) -> str:
"""Submit an async Vidu task. Returns task_id."""
payload: dict = {
"model": model,
"input": {},
"parameters": {
"resolution": resolution,
"duration": duration,
"watermark": watermark,
},
}
if prompt:
payload["input"]["prompt"] = prompt
if media:
payload["input"]["media"] = media
if size:
payload["parameters"]["size"] = size
if audio:
payload["parameters"]["audio"] = True
if seed is not None:
payload["parameters"]["seed"] = seed
resp = requests.post(
f"{BASE_URL}/services/aigc/video-generation/video-synthesis",
headers=_headers(),
json=payload,
)
resp.raise_for_status()
data = resp.json()
if "output" not in data or "task_id" not in data["output"]:
raise RuntimeError(f"Unexpected response: {json.dumps(data, ensure_ascii=False)}")
return data["output"]["task_id"]
def poll_task(task_id: str, interval: int = 15) -> dict:
"""Poll until terminal status. Returns full response."""
while True:
resp = requests.get(
f"{BASE_URL}/tasks/{task_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
)
resp.raise_for_status()
data = resp.json()
status = data["output"]["task_status"]
print(f" status: {status}")
if status in ("SUCCEEDED", "FAILED", "CANCELED"):
return data
time.sleep(interval)
def _build_media(args: argparse.Namespace) -> list[dict] | None:
"""Build media array from CLI args."""
media = []
if args.image:
for url in args.image:
media.append({"type": "image", "url": url})
if args.end_image:
media.append({"type": "image", "url": args.end_image})
if args.ref_video:
for url in args.ref_video:
media.append({"type": "video", "url": url})
return media if media else None
def _validate_mode_model(mode: str, model: str) -> None:
"""Validate that the model matches the selected mode."""
if mode not in VALID_MODELS:
print(f"Error: unknown mode '{mode}'", file=sys.stderr)
sys.exit(1)
if model not in VALID_MODELS[mode]:
valid = ", ".join(VALID_MODELS[mode])
print(
f"Error: model '{model}' is not valid for mode '{mode}'. "
f"Valid models: {valid}",
file=sys.stderr,
)
sys.exit(1)
def main() -> None:
parser = argparse.ArgumentParser(description="Vidu Video Generation")
parser.add_argument(
"--mode",
required=True,
choices=["t2v", "i2v", "keyframe", "ref"],
help="Generation mode: t2v (text-to-video), i2v (image-to-video), "
"keyframe (first+last frame), ref (reference-to-video)",
)
parser.add_argument(
"--model",
required=True,
choices=ALL_MODELS,
help="Model name",
)
parser.add_argument("--prompt", default="", help="Text prompt (up to 5000 chars)")
parser.add_argument(
"--image",
action="append",
help="Image URL (repeatable for reference mode). "
"For keyframe mode, use this for the first frame.",
)
parser.add_argument(
"--end-image",
help="Last frame image URL (keyframe mode only)",
)
parser.add_argument(
"--ref-video",
action="append",
help="Reference video URL (reference mode with viduq2-pro only, repeatable)",
)
parser.add_argument(
"--resolution",
default="720P",
choices=["540P", "720P", "1080P"],
help="Resolution tier (default: 720P)",
)
parser.add_argument("--size", default=None, help="Pixel size WxH, e.g. 1280*720")
parser.add_argument(
"--duration", type=int, default=5, help="Video duration in seconds"
)
parser.add_argument(
"--audio", action="store_true", help="Generate audio (Q3 models only)"
)
parser.add_argument("--watermark", action="store_true", help="Add watermark")
parser.add_argument("--seed", type=int, default=None, help="Random seed")
parser.add_argument(
"--output", default=OUTPUT_DIR, help="Output directory"
)
args = parser.parse_args()
if not API_KEY:
print("Error: DASHSCOPE_API_KEY not set", file=sys.stderr)
sys.exit(1)
_validate_mode_model(args.mode, args.model)
# Validate mode-specific requirements
if args.mode == "t2v":
if not args.prompt:
print("Error: --prompt is required for text-to-video mode", file=sys.stderr)
sys.exit(1)
elif args.mode == "i2v":
if not args.image or len(args.image) != 1:
print("Error: exactly one --image is required for image-to-video mode", file=sys.stderr)
sys.exit(1)
elif args.mode == "keyframe":
if not args.image or len(args.image) != 1 or not args.end_image:
print(
"Error: --image (first frame) and --end-image (last frame) "
"are both required for keyframe mode",
file=sys.stderr,
)
sys.exit(1)
if not args.prompt:
print("Error: --prompt is required for keyframe mode", file=sys.stderr)
sys.exit(1)
elif args.mode == "ref":
if not args.image:
print("Error: at least one --image is required for reference mode", file=sys.stderr)
sys.exit(1)
if not args.prompt:
print("Error: --prompt is required for reference mode", file=sys.stderr)
sys.exit(1)
media = _build_media(args) if args.mode != "t2v" else None
print(f"Creating Vidu {args.mode} task with model {args.model}...")
task_id = create_task(
model=args.model,
prompt=args.prompt,
media=media,
resolution=args.resolution,
size=args.size,
duration=args.duration,
audio=args.audio,
watermark=args.watermark,
seed=args.seed,
)
print(f"Task created: {task_id}")
print("Polling for result...")
result = poll_task(task_id)
os.makedirs(args.output, exist_ok=True)
out_path = os.path.join(args.output, f"{task_id}.json")
with open(out_path, "w") as f:
json.dump(result, f, indent=2, ensure_ascii=False)
status = result["output"]["task_status"]
if status == "SUCCEEDED":
video_url = result["output"].get("video_url", "")
print(f"Video URL: {video_url}")
else:
print(f"Task {status}: {json.dumps(result, ensure_ascii=False)}", file=sys.stderr)
sys.exit(1)
print(f"Result saved to {out_path}")
if __name__ == "__main__":
main()