
Aliyun Wan Video
- 76 installs
- 396 repo stars
- Updated July 18, 2026
- cinience/alicloud-skills
Generates video via the DashScope SDK using Wan text-to-video and image-to-video models with standardized prompt, duration, fps, size, and seed inputs.
About
This skill standardizes video.generate requests and responses for Wan t2v and i2v models through the DashScope Python SDK. A developer uses it to integrate consistent Wan video generation into a video-agent pipeline.
- Multiple model strings including wan2.6-t2v, wan2.6-i2v, and regional variants
- Normalized async video.generate interface with polling for task completion
Aliyun Wan Video by the numbers
- 76 all-time installs (skills.sh)
- Ranked #827 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-wan-videoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 76 |
|---|---|
| repo stars | ★ 396 |
| Last updated | July 18, 2026 |
| Repository | cinience/alicloud-skills ↗ |
What it does
Generates video via the DashScope SDK using Wan text-to-video and image-to-video models with standardized prompt, duration, fps, size, and seed inputs.
Files
Category: provider
Model Studio Wan Video
Validation
mkdir -p output/aliyun-wan-video
python -m py_compile skills/ai/video/aliyun-wan-video/scripts/generate_video.py && echo "py_compile_ok" > output/aliyun-wan-video/validate.txtPass criteria: command exits 0 and output/aliyun-wan-video/validate.txt is generated.
Output And Evidence
- Save task IDs, polling responses, and final video URLs to
output/aliyun-wan-video/. - Keep one end-to-end run log for troubleshooting.
Provide consistent video generation behavior for the video-agent pipeline by standardizing video.generate inputs/outputs and using DashScope SDK (Python) with the exact model name.
Critical model names
Use one of these exact model strings:
wan2.6-t2vwan2.6-t2v-uswan2.2-t2v-pluswan2.2-t2v-flashwan2.6-i2v-flashwan2.6-i2vwan2.6-i2v-uswanx2.1-t2v-turbo
Prerequisites
- Install SDK (recommended in a venv to avoid PEP 668 limits):
python3 -m venv .venv
. .venv/bin/activate
python -m pip install dashscope- Set
DASHSCOPE_API_KEYin your environment, or adddashscope_api_keyto~/.alibabacloud/credentials(env takes precedence).
Normalized interface (video.generate)
Request
prompt(string, required)negative_prompt(string, optional)duration(number, required) secondsfps(number, required)size(string, required) e.g.1280*720seed(int, optional)reference_image(string | bytes, optional for t2v, required for i2v family models)motion_strength(number, optional)
Response
video_url(string)duration(number)fps(number)seed(int)
Quick start (Python + DashScope SDK)
Video generation is usually asynchronous. Expect a task ID and poll until completion. Note: Wan i2v models require an input image; pure t2v models such as wan2.6-t2v can omit reference_image.
import os
from dashscope import VideoSynthesis
# Prefer env var for auth: export DASHSCOPE_API_KEY=...
# Or use ~/.alibabacloud/credentials with dashscope_api_key under [default].
def generate_video(req: dict) -> dict:
payload = {
"model": req.get("model", "wan2.6-i2v-flash"),
"prompt": req["prompt"],
"negative_prompt": req.get("negative_prompt"),
"duration": req.get("duration", 4),
"fps": req.get("fps", 24),
"size": req.get("size", "1280*720"),
"seed": req.get("seed"),
"motion_strength": req.get("motion_strength"),
"api_key": os.getenv("DASHSCOPE_API_KEY"),
}
if req.get("reference_image"):
# DashScope expects img_url for i2v models; local files are auto-uploaded.
payload["img_url"] = req["reference_image"]
response = VideoSynthesis.call(**payload)
# Some SDK versions require polling for the final result.
# If a task_id is returned, poll until status is SUCCEEDED.
result = response.output.get("results", [None])[0]
return {
"video_url": None if not result else result.get("url"),
"duration": response.output.get("duration"),
"fps": response.output.get("fps"),
"seed": response.output.get("seed"),
}Async handling (polling)
import os
from dashscope import VideoSynthesis
task = VideoSynthesis.async_call(
model=req.get("model", "wan2.6-i2v-flash"),
prompt=req["prompt"],
img_url=req["reference_image"],
duration=req.get("duration", 4),
fps=req.get("fps", 24),
size=req.get("size", "1280*720"),
api_key=os.getenv("DASHSCOPE_API_KEY"),
)
final = VideoSynthesis.wait(task)
video_url = final.output.get("video_url")Operational guidance
- Video generation can take minutes; expose progress and allow cancel/retry.
- Cache by
(prompt, negative_prompt, duration, fps, size, seed, reference_image hash, motion_strength). - Store video assets in object storage and persist only URLs in metadata.
reference_imagecan be a URL or local path; the SDK auto-uploads local files.- If you get
Field required: input.img_url, the reference image is missing or not mapped. wan2.6-t2vandwan2.6-t2v-usadd multi-shot narrative support and optional audio input according to the official docs.
Size notes
- Use
WxHformat (e.g.1280*720). - Prefer common sizes; unsupported sizes can return 400.
Output location
- Default output:
output/aliyun-wan-video/videos/ - Override base dir with
OUTPUT_DIR.
Anti-patterns
- Do not invent model names or aliases; use official Wan i2v model IDs only.
- Do not block the UI without progress updates.
- Do not retry blindly on 4xx; handle validation failures explicitly.
Workflow
1) Confirm user intent, region, identifiers, and whether the operation is read-only or mutating. 2) Run one minimal read-only query first to verify connectivity and permissions. 3) Execute the target operation with explicit parameters and bounded scope. 4) Verify results and save output/evidence files.
References
- See
references/api_reference.mdfor DashScope SDK mapping and async handling notes.
- Source list:
references/sources.md
interface:
display_name: "Alibaba Cloud AI Video Wan Video"
short_description: "AI video generation and orchestration"
default_prompt: "Use $aliyun-wan-video to complete this ai/video task on Alibaba Cloud."
DashScope SDK Reference (Wan Video)
Keep this reference minimal and update it only when the DashScope SDK behavior changes.
Install
python3 -m venv .venv
. .venv/bin/activate
python -m pip install dashscopeEnvironment
export DASHSCOPE_API_KEY=your_keyIf env vars are not set, you can also place dashscope_api_key under [default] in ~/.alibabacloud/credentials.
Suggested mapping
import os
from dashscope import VideoSynthesis
payload = {
"model": "wan2.6-i2v-flash",
"prompt": prompt,
"negative_prompt": negative_prompt,
"duration": duration,
"fps": fps,
"size": size,
"seed": seed,
"motion_strength": motion_strength,
"api_key": os.getenv("DASHSCOPE_API_KEY"),
}
if reference_image:
# DashScope expects img_url for i2v models; local files are auto-uploaded.
payload["img_url"] = reference_image
response = VideoSynthesis.call(**payload)Async handling
If the SDK returns a task ID rather than a direct result URL, poll until completion. Use exponential backoff and a hard timeout; fail gracefully with the task ID for later resumption.
task = VideoSynthesis.async_call(**payload)
final = VideoSynthesis.wait(task)
video_url = final.output.get("video_url")Response parsing
Normalize to:
video_urldurationfpsseed
Prefer the first result URL if multiple are returned.
Notes
wan2.6-i2v-flashrequiresimg_url; missing it yieldsField required: input.img_url.reference_imagecan be a URL or local path; the SDK auto-uploads local files.
- 模型上下架与更新(wan2.6-t2v、wan2.6-i2v-flash、wan2.6-r2v-flash): https://help.aliyun.com/zh/model-studio/newly-released-models
- 万相文生视频: https://help.aliyun.com/zh/model-studio/text-to-video-guide
- 万相图生视频-基于首帧: https://help.aliyun.com/zh/model-studio/first-frame-image-to-video
- 模型列表: https://help.aliyun.com/zh/model-studio/models
#!/usr/bin/env python3
"""Generate a dancing video: first create an image, then animate it.
Usage:
python scripts/generate_dancing_video.py --prompt "亚洲美女在跳舞" --output output/dancing_video.mp4
"""
from __future__ import annotations
import argparse
import configparser
import json
import os
import sys
import time
import urllib.request
from pathlib import Path
from typing import Any
try:
from dashscope.aigc.image_generation import ImageGeneration
from dashscope import VideoSynthesis
except ImportError:
print("Error: dashscope is not installed. Run: pip install dashscope", file=sys.stderr)
sys.exit(1)
IMAGE_MODEL = "qwen-image-max"
VIDEO_MODEL = "wan2.6-i2v-flash"
DEFAULT_IMAGE_SIZE = "1024*1024"
DEFAULT_VIDEO_SIZE = "1280*720"
DEFAULT_FPS = 24
DEFAULT_DURATION = 5
def _find_repo_root(start: Path) -> Path | None:
for parent in [start] + list(start.parents):
if (parent / ".git").exists():
return parent
return None
def _load_dotenv(path: Path) -> None:
if not path.exists():
return
for line in path.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = value
def _load_env() -> None:
_load_dotenv(Path.cwd() / ".env")
repo_root = _find_repo_root(Path(__file__).resolve())
if repo_root:
_load_dotenv(repo_root / ".env")
def _load_dashscope_api_key_from_credentials() -> None:
if os.environ.get("DASHSCOPE_API_KEY"):
return
credentials_path = Path(os.path.expanduser("~/.alibabacloud/credentials"))
if not credentials_path.exists():
return
config = configparser.ConfigParser()
try:
config.read(credentials_path)
except configparser.Error:
return
profile = os.getenv("ALIBABA_CLOUD_PROFILE") or os.getenv("ALICLOUD_PROFILE") or "default"
if not config.has_section(profile):
return
key = config.get(profile, "dashscope_api_key", fallback="").strip()
if not key:
key = config.get(profile, "DASHSCOPE_API_KEY", fallback="").strip()
if key:
os.environ["DASHSCOPE_API_KEY"] = key
def generate_image(prompt: str, size: str = DEFAULT_IMAGE_SIZE) -> dict[str, Any]:
"""Generate an image of an Asian beauty."""
print(f"Step 1: Generating image with prompt: {prompt}")
messages = [{"role": "user", "content": [{"text": prompt}]}]
response = ImageGeneration.call(
model=IMAGE_MODEL,
messages=messages,
size=size,
api_key=os.getenv("DASHSCOPE_API_KEY"),
)
content = response.output["choices"][0]["message"]["content"]
image_url = None
for item in content:
if isinstance(item, dict) and item.get("image"):
image_url = item["image"]
break
if not image_url:
raise RuntimeError("No image URL returned by DashScope")
print(f" Image generated: {image_url}")
return {"image_url": image_url}
def generate_video(image_url: str, prompt: str, duration: int = DEFAULT_DURATION,
fps: int = DEFAULT_FPS, size: str = DEFAULT_VIDEO_SIZE) -> dict[str, Any]:
"""Generate a dancing video from the image."""
print(f"Step 2: Generating video from image...")
print(f" Video prompt: {prompt}")
print(f" Duration: {duration}s, FPS: {fps}, Size: {size}")
payload = {
"model": VIDEO_MODEL,
"prompt": prompt,
"duration": duration,
"fps": fps,
"size": size,
"api_key": os.getenv("DASHSCOPE_API_KEY"),
"img_url": image_url,
}
task = VideoSynthesis.async_call(**payload)
print(" Waiting for video generation (this may take 2-5 minutes)...")
poll_interval = 10
timeout_s = 600
start = time.time()
while True:
final = VideoSynthesis.wait(task)
output = getattr(final, "output", None) or {}
status = output.get("status")
if status in ("SUCCEEDED", "FAILED") or output.get("video_url"):
break
if time.time() - start > timeout_s:
raise TimeoutError(f"Video generation timed out after {timeout_s}s")
print(f" Still processing... ({int(time.time() - start)}s elapsed)")
time.sleep(poll_interval)
output = getattr(final, "output", None) or {}
video_url = output.get("video_url")
if not video_url:
results = output.get("results") or []
if results and isinstance(results, list):
video_url = results[0].get("url")
if not video_url:
raise RuntimeError("No video URL returned by DashScope")
print(f" Video generated: {video_url}")
return {"video_url": video_url, "duration": output.get("duration"), "fps": output.get("fps")}
def download_file(url: str, output_path: Path) -> None:
"""Download a file from URL."""
output_path.parent.mkdir(parents=True, exist_ok=True)
print(f"Downloading to: {output_path}")
with urllib.request.urlopen(url) as response:
output_path.write_bytes(response.read())
def main() -> None:
parser = argparse.ArgumentParser(description="Generate a dancing video (image + animation)")
parser.add_argument("--prompt", required=True, help="Prompt for the dancing video")
parser.add_argument("--image-prompt", help="Optional separate prompt for the base image")
parser.add_argument("--duration", type=int, default=DEFAULT_DURATION, help="Video duration in seconds")
parser.add_argument("--fps", type=int, default=DEFAULT_FPS, help="Video FPS")
parser.add_argument("--output", help="Output video path")
parser.add_argument("--save-image", action="store_true", help="Also save the generated image")
parser.add_argument("--print-response", action="store_true", help="Print response JSON")
args = parser.parse_args()
_load_env()
_load_dashscope_api_key_from_credentials()
if not os.environ.get("DASHSCOPE_API_KEY"):
print("Error: DASHSCOPE_API_KEY is not set.", file=sys.stderr)
print("Configure via environment variable, .env file, or ~/.alibabacloud/credentials", file=sys.stderr)
sys.exit(1)
# Default image prompt if not specified
image_prompt = args.image_prompt or args.prompt
if not args.image_prompt:
# Enhance the prompt for image generation if user didn't specify
image_prompt = f"一位美丽的亚洲女性,专业摄影,高质量,{args.prompt}"
output_dir = Path(os.getenv("OUTPUT_DIR", "output")) / "ai-video-wan-video" / "videos"
output_path = Path(args.output) if args.output else output_dir / "dancing_video.mp4"
image_output_path = output_path.with_name(output_path.stem + "_reference.png")
try:
# Step 1: Generate image
image_result = generate_image(image_prompt)
# Save image if requested
if args.save_image:
download_file(image_result["image_url"], image_output_path)
print(f"Image saved to: {image_output_path}")
# Step 2: Generate video
video_result = generate_video(
image_result["image_url"],
args.prompt,
duration=args.duration,
fps=args.fps,
)
# Download video
download_file(video_result["video_url"], output_path)
print(f"\nVideo saved to: {output_path}")
if args.print_response:
print(json.dumps({
"image": image_result,
"video": video_result,
"output_path": str(output_path),
}, ensure_ascii=False, indent=2))
print("\nDone!")
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Generate a video using DashScope (Wan t2v or i2v) from a normalized request.
Usage:
python scripts/generate_video.py --request '{"prompt":"...","model":"wan2.6-t2v"}'
python scripts/generate_video.py --request '{"prompt":"...","model":"wan2.6-i2v-flash","reference_image":"./ref.png"}'
python scripts/generate_video.py --file request.json --output output/ai-video-wan-video/videos/output.mp4
"""
from __future__ import annotations
import argparse
import configparser
import json
import os
import sys
import time
import urllib.request
from pathlib import Path
from typing import Any
try:
from dashscope import VideoSynthesis
except ImportError:
print("Error: dashscope is not installed. Run: pip install dashscope", file=sys.stderr)
sys.exit(1)
MODEL_NAME = "wan2.6-t2v"
DEFAULT_SIZE = "1280*720"
DEFAULT_FPS = 24
DEFAULT_DURATION = 4
def _find_repo_root(start: Path) -> Path | None:
for parent in [start] + list(start.parents):
if (parent / ".git").exists():
return parent
return None
def _load_dotenv(path: Path) -> None:
if not path.exists():
return
for line in path.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, value = line.split("=", 1)
key = key.strip()
value = value.strip().strip('"').strip("'")
if key and key not in os.environ:
os.environ[key] = value
def _load_env() -> None:
_load_dotenv(Path.cwd() / ".env")
repo_root = _find_repo_root(Path(__file__).resolve())
if repo_root:
_load_dotenv(repo_root / ".env")
def _load_dashscope_api_key_from_credentials() -> None:
if os.environ.get("DASHSCOPE_API_KEY"):
return
credentials_path = Path(os.path.expanduser("~/.alibabacloud/credentials"))
if not credentials_path.exists():
return
config = configparser.ConfigParser()
try:
config.read(credentials_path)
except configparser.Error:
return
profile = os.getenv("ALIBABA_CLOUD_PROFILE") or os.getenv("ALICLOUD_PROFILE") or "default"
if not config.has_section(profile):
return
key = config.get(profile, "dashscope_api_key", fallback="").strip()
if not key:
key = config.get(profile, "DASHSCOPE_API_KEY", fallback="").strip()
if key:
os.environ["DASHSCOPE_API_KEY"] = key
def load_request(args: argparse.Namespace) -> dict[str, Any]:
if args.request:
return json.loads(args.request)
if args.file:
with open(args.file, "r", encoding="utf-8") as f:
return json.load(f)
raise ValueError("Either --request or --file must be provided")
def resolve_reference_image(value: str) -> Any:
if value.startswith("http://") or value.startswith("https://"):
return value
path = Path(value)
if path.exists():
return str(path)
return value
def model_requires_reference_image(model: str) -> bool:
return "-i2v" in model
def call_generate(req: dict[str, Any]) -> dict[str, Any]:
prompt = req.get("prompt")
if not prompt:
raise ValueError("prompt is required")
model = req.get("model", MODEL_NAME)
reference_image = req.get("reference_image")
if model_requires_reference_image(model) and not reference_image:
raise ValueError(f"reference_image is required for {model}")
payload = {
"model": model,
"prompt": prompt,
"negative_prompt": req.get("negative_prompt"),
"duration": req.get("duration", DEFAULT_DURATION),
"fps": req.get("fps", DEFAULT_FPS),
"size": req.get("size", DEFAULT_SIZE),
"seed": req.get("seed"),
"motion_strength": req.get("motion_strength"),
"api_key": os.getenv("DASHSCOPE_API_KEY"),
}
if reference_image:
payload["img_url"] = resolve_reference_image(reference_image)
task = VideoSynthesis.async_call(**payload)
timeout_s = req.get("timeout_s", 600)
poll_interval = req.get("poll_interval_s", 5)
start = time.time()
while True:
final = VideoSynthesis.wait(task)
output = getattr(final, "output", None) or {}
status = output.get("status")
if status in ("SUCCEEDED", "FAILED") or output.get("video_url"):
break
if time.time() - start > timeout_s:
raise TimeoutError(f"Video generation timed out after {timeout_s}s")
time.sleep(poll_interval)
output = getattr(final, "output", None) or {}
video_url = output.get("video_url")
if not video_url:
results = output.get("results") or []
if results and isinstance(results, list):
video_url = results[0].get("url")
if not video_url:
raise RuntimeError("No video URL returned by DashScope")
return {
"video_url": video_url,
"duration": output.get("duration"),
"fps": output.get("fps"),
"seed": output.get("seed"),
}
def download_video(video_url: str, output_path: Path) -> None:
output_path.parent.mkdir(parents=True, exist_ok=True)
with urllib.request.urlopen(video_url) as response:
output_path.write_bytes(response.read())
def main() -> None:
parser = argparse.ArgumentParser(description="Generate video with Wan text-to-video or image-to-video models")
parser.add_argument("--request", help="Inline JSON request string")
parser.add_argument("--file", help="Path to JSON request file")
default_output_dir = Path(os.getenv("OUTPUT_DIR", "output")) / "ai-video-wan-video" / "videos"
parser.add_argument(
"--output",
default=str(default_output_dir / "output.mp4"),
help="Output video path",
)
parser.add_argument("--print-response", action="store_true", help="Print normalized response JSON")
args = parser.parse_args()
_load_env()
_load_dashscope_api_key_from_credentials()
if not os.environ.get("DASHSCOPE_API_KEY"):
print(
"Error: DASHSCOPE_API_KEY is not set. Configure it via env/.env or ~/.alibabacloud/credentials.",
file=sys.stderr,
)
print("Example .env:\n DASHSCOPE_API_KEY=your_key_here", file=sys.stderr)
print(
"Example credentials:\n [default]\n dashscope_api_key=your_key_here",
file=sys.stderr,
)
sys.exit(1)
req = load_request(args)
result = call_generate(req)
download_video(result["video_url"], Path(args.output))
if args.print_response:
print(json.dumps(result, ensure_ascii=True))
if __name__ == "__main__":
main()