Skill: 547895019/qwen-video (version 1.1.0)

--- name: qwen-video description: Generate videos using Alibaba Cloud DashScope Wan (通义万相) text-to-video (t2v) API (e.g., wan2.6-t2v). Use when the user asks to create a short video from a text prompt via 百炼/通义万相/wan 文生视频, and wants the agent to submit an async task, poll status, and download the mp4 locally (e.g., to Windows Desktop from WSL2). --- # Qwen / Wan Video (DashScope) — 文生视频 This skill provides simple CLI scripts to: 1) submit an async Wan t2v job 2) poll task status until SUCCEEDED/FAILED 3) download the resulting mp4 ## Requirements - Set API key: ```bash export DASHSCOPE_API_KEY="sk-..." ``` ## Quick start (one command) Generate a video and download to Windows Desktop (WSL2): ```bash bash {baseDir}/scripts/generate.sh \ --prompt "4秒赛博朋克雨夜城市镜头，霓虹反射，电影感镜头运动，高清" \ --duration 4 \ --size 1280*720 \ --out "/mnt/c/Users/<USERNAME>/Desktop/wan_video.mp4" ``` ## Submit only (returns task_id) ```bash bash {baseDir}/scripts/submit.sh --prompt "..." --duration 4 --size 1280*720 ``` ## Poll status only ```bash bash {baseDir}/scripts/poll.sh --task-id "<task_id>" ``` ## 高级功能 ### 多镜头叙事 (Multi-shot) 仅 wan2.6 系列模型支持此功能。通过设置 `prompt_extend: true` 和 `shot_type: "multi"` 启用。 ```bash curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis' \ -H 'X-DashScope-Async: enable' \ -H "Authorization: Bearer $DASHSCOPE_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "model": "wan2.6-t2v", "input": { "prompt": "史诗级战斗场景..." }, "parameters": { "size": "1280*720", "prompt_extend": true, "duration": 10, "shot_type": "multi" } }' ``` ### 自动配音 (Auto Audio) 仅 wan2.6 和 wan2.5 系列模型支持。若不提供 `input.audio_url`，模型将根据视频内容自动生成匹配的背景音乐或音效。 ```bash curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis' \ -H 'X-DashScope-Async: enable' \ -H "Authorization: Bearer $DASHSCOPE_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "model": "wan2.5-t2v-preview", "input": { "prompt": "史诗级战斗场景..." }, "parameters": { "size": "832*480", "prompt_extend": true, "duration": 10 } }' ``` ### 传入音频文件 (Custom Audio) 仅 wan2.6 和 wan2.5 系列模型支持。通过 `input.audio_url` 参数传入自定义音频的 URL。 ```bash curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis' \ -H 'X-DashScope-Async: enable' \ -H "Authorization: Bearer $DASHSCOPE_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "model": "wan2.5-t2v-preview", "input": { "prompt": "史诗级战斗场景...", "audio_url": "https://example.com/audio.mp3" }, "parameters": { "size": "832*480", "prompt_extend": true, "duration": 10 } }' ``` ### 生成无声视频 (Silent Video) 仅 wan2.2 和 wanx2.1 系列模型支持生成无声视频。默认生成无声视频，无需设置。 > wan2.6 及 wan2.5 系列模型默认生成有声视频。 ```bash curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis' \ -H 'X-DashScope-Async: enable' \ -H "Authorization: Bearer $DASHSCOPE_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "model": "wan2.2-t2v-plus", "input": { "prompt": "低对比度，复古70年代风格地铁站..." }, "parameters": { "size": "832*480", "prompt_extend": true } }' ``` ### 使用反向提示词 (Negative Prompt) 通过 `negative_prompt` 排除不需要的元素。 ```bash curl --location 'https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis' \ -H 'X-DashScope-Async: enable' \ -H "Authorization: Bearer $DASHSCOPE_API_KEY" \ -H 'Content-Type: application/json' \ -d '{ "model": "wan2.2-t2v-plus", "input": { "prompt": "一只小猫在月光下奔跑", "negative_prompt": "花朵" }, "parameters": { "size": "832*480" } }' ``` ## API Endpoint (current) - Submit: `POST https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis` - Status: `GET https://dashscope.aliyuncs.com/api/v1/tasks/<task_id>` Scripts print: - `TASK_ID: ...` - `VIDEO_URL: ...` (when succeeded) - `MEDIA: <local_path>` (when downloaded) ## 模型功能对照表 | 模型 | 多镜头叙事 | 自动配音 | 自定义音频 | 无声视频 | 反向提示词 | |------|-----------|---------|-----------|---------|-----------| | wan2.6-t2v | ✅ | ✅ | ✅ | - | ✅ | | wan2.5-t2v-preview | - | ✅ | ✅ | - | ✅ | | wan2.2-t2v-plus | - | - | - | ✅ | ✅ | | wanx2.1 | - | - | - | ✅ | - |

path: references/api.md
content: # DashScope Wan 文生视频 (t2v) API notes

## Submit (Async)

Endpoint:

`POST https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis`

Headers:
- `Authorization: Bearer $DASHSCOPE_API_KEY`
- `Content-Type: application/json`
- `X-DashScope-Async: enable`

Body (example):
```json
{
  "model": "wan2.6-t2v",
  "input": {
    "prompt": "4秒赛博朋克雨夜城市镜头...",
    "audio_url": "https://...mp3" 
  },
  "parameters": {
    "size": "1280*720",
    "prompt_extend": true,
    "duration": 4,
    "shot_type": "single"
  }
}
```

Response (example):
```json
{
  "request_id": "...",
  "output": {
    "task_id": "...",
    "task_status": "PENDING"
  }
}
```

## Poll / Get Result

Endpoint:

`GET https://dashscope.aliyuncs.com/api/v1/tasks/<task_id>`

Response on success (example):
- `output.task_status = SUCCEEDED`
- `output.video_url` contains the signed mp4 URL.

## Common issues

- TLS errors (`unexpected eof while reading`): often proxy/TLS interception. Try temporarily unsetting proxy env vars or using `-k` for debugging.
- `InvalidApiKey`: key lacks permissions for video service, or wrong key is being used.


path: scripts/generate.sh
content: #!/usr/bin/env bash
set -euo pipefail

PROMPT=""
SIZE="1280*720"
DURATION=4
MODEL="wan2.6-t2v"
SHOT_TYPE="single"
OUT="./wan_video.mp4"
MAX_WAIT_SEC=1200
INTERVAL_SEC=5
PROMPT_EXTEND=true
AUDIO_URL=""

usage(){
  cat <<'EOF'
Usage:
  generate.sh --prompt "TEXT" [--size 1280*720] [--duration 4]
              [--out /path/to/out.mp4] [--max-wait-sec 1200] [--interval-sec 5]
              [--model wan2.6-t2v] [--shot-type single|multi] [--no-prompt-extend]
              [--audio-url URL]

Env:
  DASHSCOPE_API_KEY (required)

Output:
  TASK_ID: ...
  VIDEO_URL: ...
  MEDIA: /abs/path/to/out.mp4
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    -h|--help) usage; exit 0;;
    --prompt) PROMPT=${2:-}; shift 2;;
    --size) SIZE=${2:-}; shift 2;;
    --duration) DURATION=${2:-4}; shift 2;;
    --out) OUT=${2:-}; shift 2;;
    --max-wait-sec) MAX_WAIT_SEC=${2:-1200}; shift 2;;
    --interval-sec) INTERVAL_SEC=${2:-5}; shift 2;;
    --model) MODEL=${2:-}; shift 2;;
    --shot-type) SHOT_TYPE=${2:-single}; shift 2;;
    --no-prompt-extend) PROMPT_EXTEND=false; shift 1;;
    --audio-url) AUDIO_URL=${2:-}; shift 2;;
    *) echo "Unknown arg: $1" >&2; usage; exit 2;;
  esac
done

if [[ -z "$PROMPT" ]]; then
  echo "Error: --prompt is required" >&2
  exit 2
fi

BASE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)

# Submit
SUBMIT_OUT=$(bash "$BASE_DIR/scripts/submit.sh" \
  --prompt "$PROMPT" \
  --size "$SIZE" \
  --duration "$DURATION" \
  --model "$MODEL" \
  --shot-type "$SHOT_TYPE" \
  $( [[ "$PROMPT_EXTEND" == "false" ]] && echo "--no-prompt-extend" ) \
  $( [[ -n "$AUDIO_URL" ]] && printf -- "--audio-url %q" "$AUDIO_URL" )
)

echo "$SUBMIT_OUT"
TASK_ID=$(echo "$SUBMIT_OUT" | sed -n 's/^TASK_ID: //p' | tail -n 1)

# Poll
VIDEO_URL=$(bash "$BASE_DIR/scripts/poll.sh" --task-id "$TASK_ID" --max-wait-sec "$MAX_WAIT_SEC" --interval-sec "$INTERVAL_SEC" | sed -n 's/^VIDEO_URL: //p' | tail -n 1)

echo "VIDEO_URL: $VIDEO_URL"

# Download
mkdir -p "$(dirname "$OUT")"
curl -L -k -o "$OUT" "$VIDEO_URL"

FULL=$(python3 -c 'import os,sys; print(os.path.abspath(sys.argv[1]))' "$OUT")
echo "MEDIA: $FULL"


path: scripts/poll.sh
content: #!/usr/bin/env bash
set -euo pipefail

TASK_ID=""
MAX_WAIT_SEC=1200
INTERVAL_SEC=5

usage(){
  cat <<'EOF'
Usage:
  poll.sh --task-id <id> [--max-wait-sec 1200] [--interval-sec 5]

Env:
  DASHSCOPE_API_KEY (required)

Output:
  Prints status lines. On success prints:
    VIDEO_URL: <url>
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    -h|--help) usage; exit 0;;
    --task-id) TASK_ID=${2:-}; shift 2;;
    --max-wait-sec) MAX_WAIT_SEC=${2:-1200}; shift 2;;
    --interval-sec) INTERVAL_SEC=${2:-5}; shift 2;;
    *) echo "Unknown arg: $1" >&2; usage; exit 2;;
  esac
done

if [[ -z "$TASK_ID" ]]; then
  echo "Error: --task-id is required" >&2
  exit 2
fi

if [[ -z "${DASHSCOPE_API_KEY:-}" ]]; then
  echo "Error: DASHSCOPE_API_KEY is not set" >&2
  exit 2
fi

START=$(date +%s)
while true; do
  RESP=$(curl -sS -k --location "https://dashscope.aliyuncs.com/api/v1/tasks/$TASK_ID" \
    -H "Authorization: Bearer $DASHSCOPE_API_KEY")

  STATUS=$(echo "$RESP" | sed -n 's/.*"task_status"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)
  NOW=$(date +%s)
  ELAPSED=$((NOW-START))

  echo "[${ELAPSED}s] status=${STATUS:-UNKNOWN}"

  if [[ "$STATUS" == "SUCCEEDED" ]]; then
    VIDEO_URL=$(echo "$RESP" | sed -n 's/.*"video_url"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)
    if [[ -n "$VIDEO_URL" ]]; then
      echo "VIDEO_URL: $VIDEO_URL"
      exit 0
    else
      echo "Error: SUCCEEDED but no video_url found" >&2
      echo "$RESP" >&2
      exit 1
    fi
  fi

  if [[ "$STATUS" == "FAILED" ]]; then
    echo "FAILED" >&2
    echo "$RESP" >&2
    exit 1
  fi

  if (( ELAPSED >= MAX_WAIT_SEC )); then
    echo "Timeout after ${MAX_WAIT_SEC}s" >&2
    echo "$RESP" >&2
    exit 1
  fi

  sleep "$INTERVAL_SEC"
done


path: scripts/submit.sh
content: #!/usr/bin/env bash
set -euo pipefail

API_URL="https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis"
MODEL="wan2.6-t2v"
PROMPT=""
SIZE="1280*720"
DURATION=4
SHOT_TYPE="single"
PROMPT_EXTEND=true
AUDIO_URL=""

usage(){
  cat <<'EOF'
Usage:
  submit.sh --prompt "TEXT" [--size 1280*720] [--duration 4] [--model wan2.6-t2v]
           [--shot-type single|multi] [--no-prompt-extend] [--audio-url URL]

Env:
  DASHSCOPE_API_KEY  (required)

Output:
  Prints JSON response and a parsed line: TASK_ID: <id>
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    -h|--help) usage; exit 0;;
    --prompt) PROMPT=${2:-}; shift 2;;
    --size) SIZE=${2:-}; shift 2;;
    --duration) DURATION=${2:-4}; shift 2;;
    --model) MODEL=${2:-}; shift 2;;
    --shot-type) SHOT_TYPE=${2:-single}; shift 2;;
    --no-prompt-extend) PROMPT_EXTEND=false; shift 1;;
    --audio-url) AUDIO_URL=${2:-}; shift 2;;
    *) echo "Unknown arg: $1" >&2; usage; exit 2;;
  esac
done

if [[ -z "$PROMPT" ]]; then
  echo "Error: --prompt is required" >&2
  usage >&2
  exit 2
fi

if [[ -z "${DASHSCOPE_API_KEY:-}" ]]; then
  echo "Error: DASHSCOPE_API_KEY is not set" >&2
  exit 2
fi

# Build JSON (avoid jq dependency)
AUDIO_FIELD=""
if [[ -n "$AUDIO_URL" ]]; then
  AUDIO_FIELD=",\"audio_url\":\"$AUDIO_URL\""
fi

DATA="{\"model\":\"$MODEL\",\"input\":{\"prompt\":\"$PROMPT\"$AUDIO_FIELD},\"parameters\":{\"size\":\"$SIZE\",\"prompt_extend\":$PROMPT_EXTEND,\"duration\":$DURATION,\"shot_type\":\"$SHOT_TYPE\"}}"

RESP=$(curl -sS -k --location "$API_URL" \
  -H 'X-DashScope-Async: enable' \
  -H "Authorization: Bearer $DASHSCOPE_API_KEY" \
  -H 'Content-Type: application/json' \
  -d "$DATA")

echo "$RESP"
TASK_ID=$(echo "$RESP" | sed -n 's/.*"task_id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1)
if [[ -n "$TASK_ID" ]]; then
  echo "TASK_ID: $TASK_ID"
else
  echo "Failed to parse task_id" >&2
  exit 1
fi


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (HIGH)