Skill: krisyejh/happyhorse-video-gen-edit (version 1.0.0)

--- name: happyhorse-video-gen-edit description: Video Generation and Editing with HappyHorse models. Supports text2video, image2video (first-frame based), reference2video (multi-image character fusion), and video editing capabilities. homepage: https://bailian.console.aliyun.com/cn-beijing?tab=model#/model-market metadata: {"clawdbot":{"emoji":"🐴","requires":{"bins":["python3"],"env":["DASHSCOPE_API_KEY"]},"primaryEnv":"DASHSCOPE_API_KEY"},"author":"KrisYe"} --- # HappyHorse Video Models HappyHorse Video Models, created by Alibaba Group, are video generation and editing models. This skill integrates with HappyHorse Model APIs on ModelStudio (Bailian-Alibaba Model Service Platform). ## Model Overview | Model | Capabilities | Resolution | Duration | | --- | --- | --- | --- | | happyhorse-1.0-t2v | Text to Video | 720P, 1080P | 3-15s | | happyhorse-1.0-i2v | First-frame Image to Video | 720P, 1080P | 3-15s | | happyhorse-1.0-r2v | Reference images to video, Multi-character fusion | 720P, 1080P | 3-15s | | happyhorse-1.0-video-edit | Video editing, Style transfer, Reference-based edit | 720P, 1080P | 3-15s (output) | ## Text to Video Generation (happyhorse-1.0-t2v) Generate videos from text prompts. ### text2video task-submit ```bash python3 {baseDir}/scripts/happyhorse-magic.py text2video-gen --prompt "一座由硬纸板和瓶盖搭建的微型城市，在夜晚焕发出生机" --resolution "720P" --ratio "16:9" --duration 5 python3 {baseDir}/scripts/happyhorse-magic.py text2video-gen --prompt "一只可爱的小猫将军站在悬崖上，远处是壮丽的雪山" --resolution "1080P" --ratio "16:9" --duration 10 python3 {baseDir}/scripts/happyhorse-magic.py text2video-gen --prompt "海浪拍打岩石的慢镜头" --resolution "720P" --ratio "9:16" --duration 8 --no-watermark ``` ### Options - `--prompt`: Text prompt for video generation (required, max 5000 non-Chinese chars or 2500 Chinese chars) - `--resolution`: Video resolution - `720P` or `1080P` (default: 1080P) - `--ratio`: Aspect ratio - `16:9`, `9:16`, `1:1`, `4:3`, `3:4` (default: 16:9) - `--duration`: Video duration in seconds, 3-15 (default: 5) - `--no-watermark`: Disable "Happy Horse" watermark (default: enabled) - `--seed`: Random seed for reproducibility [0, 2147483647] ### text2video tasks-get (round-robin) ```bash python3 {baseDir}/scripts/happyhorse-magic.py text2video-get --task-id "<TASK_ID_FROM_VIDEO_GEN>" ``` ## Image to Video Generation (happyhorse-1.0-i2v) Generate video from a first-frame image. Output aspect ratio automatically follows the input image. ### First-frame to Video ```bash python3 {baseDir}/scripts/happyhorse-magic.py image2video-gen --prompt "一只猫在草地上奔跑" --first-frame "https://example.com/cat.png" --resolution "720P" --duration 5 python3 {baseDir}/scripts/happyhorse-magic.py image2video-gen --first-frame "https://example.com/landscape.jpg" --resolution "1080P" --duration 10 python3 {baseDir}/scripts/happyhorse-magic.py image2video-gen --prompt "花朵缓缓绽放" --first-frame "https://example.com/flower.png" --duration 8 --no-watermark ``` ### Options - `--prompt`: Text prompt for video generation (optional but recommended) - `--first-frame`: First frame image URL (required). Format: JPEG/JPG/PNG/WEBP, min 300px per side, max 10MB. - `--resolution`: Video resolution - `720P` or `1080P` (default: 1080P). Output ratio follows input image. - `--duration`: Video duration in seconds, 3-15 (default: 5) - `--no-watermark`: Disable "Happy Horse" watermark (default: enabled) - `--seed`: Random seed for reproducibility [0, 2147483647] Note: There is no `--ratio` parameter. The output video aspect ratio automatically follows the input first-frame image. ### image2video tasks-get (round-robin) ```bash python3 {baseDir}/scripts/happyhorse-magic.py image2video-get --task-id "<TASK_ID_FROM_VIDEO_GEN>" ``` ## Reference to Video Generation (happyhorse-1.0-r2v) Generate video from reference images as character/object sources. Use "character1", "character2", etc. in the prompt to reference images by their array order. ### Multi-character Reference ```bash python3 {baseDir}/scripts/happyhorse-magic.py reference2video-gen --prompt "身着红色旗袍的女性character1，镜头以侧面中景勾勒旗袍修身剪裁" --reference-images "https://example.com/girl.jpg" --resolution "720P" --ratio "16:9" --duration 5 python3 {baseDir}/scripts/happyhorse-magic.py reference2video-gen --prompt "身着红色旗袍的女性character1，轻抬玉手展开折扇character2时流苏耳坠character3随头部转动轻盈摆动" --reference-images "https://example.com/girl.jpg" "https://example.com/fan.jpg" "https://example.com/earrings.jpg" --resolution "720P" --ratio "16:9" --duration 5 python3 {baseDir}/scripts/happyhorse-magic.py reference2video-gen --prompt "character1在海边散步，阳光洒在沙滩上" --reference-images "https://example.com/person.png" --resolution "1080P" --ratio "16:9" --duration 10 ``` ### Options - `--prompt`: Text prompt with character references (required, max 5000 non-Chinese chars). Use "character1/character2/..." to reference images in array order. - `--reference-images`: Reference image URLs (required, 1-9 images). The 1st image = character1, 2nd = character2, etc. - `--resolution`: Video resolution - `720P` or `1080P` (default: 1080P) - `--ratio`: Aspect ratio - `16:9`, `9:16`, `1:1`, `4:3`, `3:4` (default: 16:9) - `--duration`: Video duration in seconds, 3-15 (default: 5) - `--no-watermark`: Disable "Happy Horse" watermark (default: enabled) - `--seed`: Random seed for reproducibility [0, 2147483647] Note: Reference images should have short side >= 400px. Avoid low-resolution, blurry, or heavily compressed images. ### reference2video tasks-get (round-robin) ```bash python3 {baseDir}/scripts/happyhorse-magic.py reference2video-get --task-id "<TASK_ID_FROM_VIDEO_GEN>" ``` ## Video Editing (happyhorse-1.0-video-edit) Edit existing videos with text instructions and optional reference images. Supports style transfer, local replacement, and other editing tasks. ### Instruction-based Editing ```bash python3 {baseDir}/scripts/happyhorse-magic.py video-edit --prompt "将场景变成夜晚，添加霓虹灯效果" --video "https://example.com/video.mp4" --resolution "720P" python3 {baseDir}/scripts/happyhorse-magic.py video-edit --prompt "为人物换上酷闪的衣服" --video "https://example.com/original.mp4" ``` ### Reference Image-based Editing ```bash python3 {baseDir}/scripts/happyhorse-magic.py video-edit --prompt "让视频中的角色穿上图片中的条纹毛衣" --video "https://example.com/video.mp4" --reference-images "https://example.com/clothes.webp" python3 {baseDir}/scripts/happyhorse-magic.py video-edit --prompt "将背景替换为参考图中的场景" --video "https://example.com/video.mp4" --reference-images "https://example.com/bg.jpg" --resolution "720P" ``` ### Options - `--prompt`: Editing instruction (required, max 5000 non-Chinese chars) - `--video`: Input video URL to edit (required, mp4/mov, 3-60s input, output max 15s) - `--reference-images`: Reference image URLs for editing (optional, 0-5 images) - `--resolution`: Output resolution - `720P` or `1080P` (default: 1080P) - `--audio-setting`: Audio handling - `auto` (default) or `origin` (keep original audio) - `--no-watermark`: Disable "Happy Horse" watermark (default: enabled) - `--seed`: Random seed for reproducibility [0, 2147483647] Note: Output aspect ratio and duration follow the input video. No `--ratio` or `--duration` parameters. If input video > 15s, system auto-truncates to first 15s. ### video-edit tasks-get (round-robin) ```bash python3 {baseDir}/scripts/happyhorse-magic.py video-edit-get --task-id "<TASK_ID_FROM_VIDEO_EDIT>" ``` ## Key Differences from Wan 2.7 | Feature | HappyHorse | Wan 2.7 | | --- | --- | --- | | Watermark default | Enabled ("Happy Horse") | Disabled ("AI Generated") | | prompt_extend | Not supported | Supported | | negative_prompt | Not supported | Supported | | seed parameter | Supported (all models) | Supported (all models) | | I2V modes | First-frame only | First-frame, First+Last-frame, Video continuation | | R2V reference type | Images only (character1/2) | Videos + Images (视频1/图片1) | | R2V reference count | 1-9 images | Max 3 videos + 5 images | | Video Edit input | 3-60s, output max 15s | 2-10s | | Video Edit prompt | Required | Optional | ## Important Notes - **Task ID Validity**: Task IDs are valid for 24 hours - **Video URL Validity**: Generated video URLs are valid for 24 hours, download immediately - **Content Review**: Both input and output are subject to content safety review - **Billing**: Based on resolution (1080P > 720P) × duration (seconds) - **Watermark**: Default enabled with "Happy Horse" text; use `--no-watermark` to disable

path: references/happyhorse-i2v-api-doc.md
content: # HappyHorse Image to Video API Documentation (happyhorse-1.0-i2v)

## Overview

Generate video from a first-frame image with optional text prompt guidance. The output video aspect ratio automatically follows the input image.

## API Endpoint

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

## Request Headers

| Header | Required | Description |
| --- | --- | --- |
| Content-Type | Yes | Must be `application/json` |
| Authorization | Yes | `Bearer {API_KEY}` |
| X-DashScope-Async | Yes | Must be `enable` for async processing |

## Request Body

```json
{
  "model": "happyhorse-1.0-i2v",
  "input": {
    "prompt": "一只猫在草地上奔跑",
    "media": [
      {
        "type": "first_frame",
        "url": "https://example.com/first.png"
      }
    ]
  },
  "parameters": {
    "resolution": "720P",
    "duration": 5,
    "watermark": true,
    "seed": 12345
  }
}
```

### Input Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| prompt | string | No | Text prompt for video generation. Max 5000 non-Chinese chars or 2500 Chinese chars. |
| media | array | Yes | Media list. Must contain exactly 1 `first_frame` element. |

### Media Element

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| type | string | Yes | Must be `first_frame` |
| url | string | Yes | Image URL (HTTP/HTTPS). Format: JPEG/JPG/PNG/WEBP. Min resolution: 300px per side. Aspect ratio: 1:2.5~2.5:1. Max 10MB. |

### Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| resolution | string | No | `720P` or `1080P` (default: 1080P). Output aspect ratio follows input image automatically. |
| duration | integer | No | Video duration in seconds, 3-15 (default: 5) |
| watermark | boolean | No | Add "Happy Horse" watermark (default: true) |
| seed | integer | No | Random seed [0, 2147483647] for reproducibility |

**Note:** There is no `ratio` parameter. The output video aspect ratio automatically follows the input first-frame image.

## Response

### Task Creation Response

```json
{
  "output": {
    "task_status": "PENDING",
    "task_id": "0385dc79-5ff8-4d82-bcb6-xxxxxx"
  },
  "request_id": "4909100c-7b5a-9f92-bfe5-xxxxxx"
}
```

### Task Query Endpoint

```
GET https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}
```

### Success Response

```json
{
  "request_id": "8ae698ba-df2d-966c-abcf-xxxxxx",
  "output": {
    "task_id": "e56d806f-76f9-4037-aefa-xxxxxx",
    "task_status": "SUCCEEDED",
    "submit_time": "2026-04-20 19:33:50.425",
    "scheduled_time": "2026-04-20 19:33:50.463",
    "end_time": "2026-04-20 19:35:34.216",
    "orig_prompt": "一只猫在草地上奔跑",
    "video_url": "https://dashscope-result.oss-cn-beijing.aliyuncs.com/xxx.mp4?Expires=xxx"
  },
  "usage": {
    "duration": 5,
    "input_video_duration": 0,
    "output_video_duration": 5,
    "video_count": 1,
    "SR": 720
  }
}
```

## Task Status Values

| Status | Description |
| --- | --- |
| PENDING | Task queued |
| RUNNING | Task processing |
| SUCCEEDED | Task completed successfully |
| FAILED | Task execution failed |
| CANCELED | Task canceled |
| UNKNOWN | Task not found or expired |

## Important Notes

- Task ID validity: 24 hours
- Video URL validity: 24 hours (download immediately)
- Output aspect ratio follows input image (no ratio parameter)
- Video frame rate: 24fps, format: MP4 (H.264)
- Polling interval: Recommended 15 seconds


path: references/happyhorse-videoedit-api-doc.md
content: # HappyHorse Video Edit API Documentation (happyhorse-1.0-video-edit)

## Overview

Edit existing videos with text instructions and optional reference images. Supports style transfer, local replacement, and other editing tasks.

## API Endpoint

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

## Request Headers

| Header | Required | Description |
| --- | --- | --- |
| Content-Type | Yes | Must be `application/json` |
| Authorization | Yes | `Bearer {API_KEY}` |
| X-DashScope-Async | Yes | Must be `enable` for async processing |

## Request Body

```json
{
  "model": "happyhorse-1.0-video-edit",
  "input": {
    "prompt": "让视频中的马头人身角色穿上图片中的条纹毛衣",
    "media": [
      {
        "type": "video",
        "url": "https://example.com/original.mp4"
      },
      {
        "type": "reference_image",
        "url": "https://example.com/clothes.webp"
      }
    ]
  },
  "parameters": {
    "resolution": "720P",
    "watermark": true,
    "audio_setting": "auto",
    "seed": 12345
  }
}
```

### Input Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| prompt | string | Yes | Editing instruction. Describes the edit intent (style transfer, local replacement, etc.). Max 5000 non-Chinese chars or 2500 Chinese chars. |
| media | array | Yes | Media list. Must contain exactly 1 `video` element. Optionally 0-5 `reference_image` elements. |

### Media Elements

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| type | string | Yes | `video` (required, exactly 1) or `reference_image` (optional, 0-5) |
| url | string | Yes | Media URL (HTTP/HTTPS) |

**Video constraints:**
- Format: MP4, MOV (H.264 recommended)
- Duration: 3-60 seconds input. Output: 3-15 seconds. If input > 15s, auto-truncated to first 15s.
- Resolution: Long side <= 2160px, short side >= 320px
- Aspect ratio: 1:2.5 ~ 2.5:1
- File size: <= 100MB
- Frame rate: > 8fps

**Reference image constraints:**
- Format: JPEG/JPG/PNG/WEBP
- Resolution: Width and height >= 300px
- Aspect ratio: 1:2.5 ~ 2.5:1
- File size: <= 10MB

### Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| resolution | string | No | `720P` or `1080P` (default: 1080P) |
| watermark | boolean | No | Add "Happy Horse" watermark (default: true) |
| audio_setting | string | No | `auto` (default, model controls) or `origin` (keep original audio) |
| seed | integer | No | Random seed [0, 2147483647] for reproducibility |

**Note:** There is no `ratio` or `duration` parameter. Output aspect ratio and duration follow the input video.

## Response

### Task Creation Response

```json
{
  "output": {
    "task_status": "PENDING",
    "task_id": "0385dc79-5ff8-4d82-bcb6-xxxxxx"
  },
  "request_id": "4909100c-7b5a-9f92-bfe5-xxxxxx"
}
```

### Task Query Endpoint

```
GET https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}
```

### Success Response

```json
{
  "request_id": "c11018a8-3f83-9591-a636-xxxxxx",
  "output": {
    "task_id": "051c7b40-b2c5-4341-aee4-xxxxxx",
    "task_status": "SUCCEEDED",
    "submit_time": "2026-04-26 14:13:14.373",
    "scheduled_time": "2026-04-26 14:13:14.419",
    "end_time": "2026-04-26 14:14:13.679",
    "orig_prompt": "让视频中的马头人身角色穿上图片中的条纹毛衣",
    "video_url": "https://dashscope-result.oss-cn-beijing.aliyuncs.com/xxxx.mp4"
  },
  "usage": {
    "duration": 13.24,
    "input_video_duration": 6.62,
    "output_video_duration": 6.62,
    "video_count": 1,
    "SR": 720
  }
}
```

## Task Status Values

| Status | Description |
| --- | --- |
| PENDING | Task queued |
| RUNNING | Task processing |
| SUCCEEDED | Task completed successfully |
| FAILED | Task execution failed |
| CANCELED | Task canceled |
| UNKNOWN | Task not found or expired |

## Important Notes

- Task ID validity: 24 hours
- Video URL validity: 24 hours (download immediately)
- Input video: 3-60s, output auto-truncated to max 15s
- Output aspect ratio follows input video (no ratio parameter)
- Output duration follows input video (no duration parameter)
- Polling interval: Recommended 15 seconds


path: references/happyhorse-t2v-api-doc.md
content: # HappyHorse Text to Video API Documentation (happyhorse-1.0-t2v)

## Overview

Generate videos from text prompts. Produces physically realistic and smoothly moving video content.

## API Endpoint

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

## Request Headers

| Header | Required | Description |
| --- | --- | --- |
| Content-Type | Yes | Must be `application/json` |
| Authorization | Yes | `Bearer {API_KEY}` |
| X-DashScope-Async | Yes | Must be `enable` for async processing |

## Request Body

```json
{
  "model": "happyhorse-1.0-t2v",
  "input": {
    "prompt": "一座由硬纸板和瓶盖搭建的微型城市，在夜晚焕发出生机。一列硬纸板火车缓缓驶过，小灯点缀其间，照亮前路。"
  },
  "parameters": {
    "resolution": "720P",
    "ratio": "16:9",
    "duration": 5,
    "watermark": true,
    "seed": 12345
  }
}
```

### Input Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| prompt | string | Yes | Text prompt for video generation. Max 5000 non-Chinese chars or 2500 Chinese chars. |

### Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| resolution | string | No | `720P` or `1080P` (default: 1080P) |
| ratio | string | No | Aspect ratio: `16:9` (default), `9:16`, `1:1`, `4:3`, `3:4` |
| duration | integer | No | Video duration in seconds, 3-15 (default: 5) |
| watermark | boolean | No | Add "Happy Horse" watermark (default: true) |
| seed | integer | No | Random seed [0, 2147483647] for reproducibility |

## Response

### Task Creation Response

```json
{
  "output": {
    "task_status": "PENDING",
    "task_id": "0385dc79-5ff8-4d82-bcb6-xxxxxx"
  },
  "request_id": "4909100c-7b5a-9f92-bfe5-xxxxxx"
}
```

### Task Query Endpoint

```
GET https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}
```

### Success Response

```json
{
  "request_id": "99243b47-ec5f-9413-9993-xxxxxx",
  "output": {
    "task_id": "4673458e-28be-4a05-bf2a-xxxxxx",
    "task_status": "SUCCEEDED",
    "submit_time": "2026-04-20 17:55:17.075",
    "scheduled_time": "2026-04-20 17:55:17.129",
    "end_time": "2026-04-20 17:56:36.658",
    "orig_prompt": "一座由硬纸板和瓶盖搭建的微型城市...",
    "video_url": "https://dashscope-result.oss-cn-beijing.aliyuncs.com/xxx.mp4?Expires=xxx"
  },
  "usage": {
    "duration": 5,
    "input_video_duration": 0,
    "output_video_duration": 5,
    "video_count": 1,
    "SR": 720,
    "ratio": "16:9"
  }
}
```

## Task Status Values

| Status | Description |
| --- | --- |
| PENDING | Task queued |
| RUNNING | Task processing |
| SUCCEEDED | Task completed successfully |
| FAILED | Task execution failed |
| CANCELED | Task canceled |
| UNKNOWN | Task not found or expired |

## Important Notes

- Task ID validity: 24 hours
- Video URL validity: 24 hours (download immediately)
- Billing: resolution (1080P > 720P) × duration (seconds)
- Polling interval: Recommended 15 seconds


path: references/happyhorse-r2v-api-doc.md
content: # HappyHorse Reference to Video API Documentation (happyhorse-1.0-r2v)

## Overview

Generate video from reference images. Supports multiple reference images as character/object sources, fusing them into a coherent video guided by text prompts. Use "character1", "character2", etc. in the prompt to reference images in order.

## API Endpoint

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

## Request Headers

| Header | Required | Description |
| --- | --- | --- |
| Content-Type | Yes | Must be `application/json` |
| Authorization | Yes | `Bearer {API_KEY}` |
| X-DashScope-Async | Yes | Must be `enable` for async processing |

## Request Body

```json
{
  "model": "happyhorse-1.0-r2v",
  "input": {
    "prompt": "身着红色旗袍的女性character1，镜头先以侧面中景勾勒旗袍修身剪裁与S型曲线，随即切换至低角度仰拍，捕捉她轻抬玉手展开折扇character2时流苏耳坠character3随头部转动轻盈摆动的细节。",
    "media": [
      {
        "type": "reference_image",
        "url": "https://example.com/girl.jpg"
      },
      {
        "type": "reference_image",
        "url": "https://example.com/fan.jpg"
      },
      {
        "type": "reference_image",
        "url": "https://example.com/earrings.jpg"
      }
    ]
  },
  "parameters": {
    "resolution": "720P",
    "ratio": "16:9",
    "duration": 5,
    "watermark": true,
    "seed": 12345
  }
}
```

### Input Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| prompt | string | Yes | Text prompt with character references. Use "character1", "character2", etc. to reference images in order. Max 5000 non-Chinese chars or 2500 Chinese chars. |
| media | array | Yes | Reference image list. 1-9 images of type `reference_image`. |

### Media Element

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| type | string | Yes | Must be `reference_image` |
| url | string | Yes | Image URL (HTTP/HTTPS). Format: JPEG/JPG/PNG/WEBP. Short side >= 400px, recommend 720P+. Max 10MB. |

**Note:** The 1st image in media array corresponds to `character1`, the 2nd to `character2`, and so on.

### Parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| resolution | string | No | `720P` or `1080P` (default: 1080P) |
| ratio | string | No | Aspect ratio: `16:9` (default), `9:16`, `1:1`, `4:3`, `3:4` |
| duration | integer | No | Video duration in seconds, 3-15 (default: 5) |
| watermark | boolean | No | Add "Happy Horse" watermark (default: true) |
| seed | integer | No | Random seed [0, 2147483647] for reproducibility |

## Response

### Task Creation Response

```json
{
  "output": {
    "task_status": "PENDING",
    "task_id": "0385dc79-5ff8-4d82-bcb6-xxxxxx"
  },
  "request_id": "4909100c-7b5a-9f92-bfe5-xxxxxx"
}
```

### Task Query Endpoint

```
GET https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}
```

### Success Response

```json
{
  "request_id": "35137489-2862-96cb-b6f2-xxxxxx",
  "output": {
    "task_id": "1469cfc3-3004-4d9e-ab10-xxxxxx",
    "task_status": "SUCCEEDED",
    "submit_time": "2026-04-25 15:03:25.848",
    "scheduled_time": "2026-04-25 15:03:25.884",
    "end_time": "2026-04-25 15:04:05.882",
    "orig_prompt": "身着红色旗袍的女性character1...",
    "video_url": "https://dashscope-result.oss-cn-beijing.aliyuncs.com/xxxx.mp4"
  },
  "usage": {
    "duration": 5,
    "input_video_duration": 0,
    "output_video_duration": 5,
    "video_count": 1,
    "SR": 720,
    "ratio": "16:9"
  }
}
```

## Task Status Values

| Status | Description |
| --- | --- |
| PENDING | Task queued |
| RUNNING | Task processing |
| SUCCEEDED | Task completed successfully |
| FAILED | Task execution failed |
| CANCELED | Task canceled |
| UNKNOWN | Task not found or expired |

## Important Notes

- Task ID validity: 24 hours
- Video URL validity: 24 hours (download immediately)
- Reference images: 1-9 images, use "character1/character2/..." in prompt to reference them
- Avoid low-resolution, blurry, or heavily compressed images for best results
- Polling interval: Recommended 15 seconds


path: scripts/happyhorse-magic.py
content: #!/usr/bin/env python3
"""
HappyHorse Video Magic - Video Generation & Editing Tool

Supports:
- happyhorse-1.0-t2v: Text to Video
- happyhorse-1.0-i2v: Image to Video (first-frame based)
- happyhorse-1.0-r2v: Reference to Video (multi-image character fusion)
- happyhorse-1.0-video-edit: Video Editing (instruction + reference image)
"""

import sys
import json
import requests
import os
import argparse
import base64
import mimetypes

BASE_URL = "https://dashscope.aliyuncs.com"


def get_api_key():
    [REDACTED_SECRET]"DASHSCOPE_API_KEY")
    if not [REDACTED_SECRET]"Error: DASHSCOPE_API_KEY environment variable is not set.", file=sys.stderr)
        sys.exit(1)
    return api_key


def encode_local_file(path):
    """If path is a local file, return a data URI (data:{mime};base64,...).
    Otherwise return the original string (assumed to be a URL)."""
    if path.startswith(("http://", "https://", "oss://", "data:")):
        return path
    abs_path = os.path.expanduser(path)
    if not os.path.isfile(abs_path):
        print(f"Error: file not found: {abs_path}", file=sys.stderr)
        sys.exit(1)
    mime, _ = mimetypes.guess_type(abs_path)
    if mime is None:
        mime = "application/octet-stream"
    with open(abs_path, "rb") as f:
        b64 = base64.b64encode(f.read()).decode("utf-8")
    print(f"  [base64] Encoded local file: {abs_path} ({mime}, {len(b64)} chars)")
    return f"data:{mime};base64,{b64}"


def get_headers(api_key, async_mode=False):
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {api_key}",
    }
    if async_mode:
        headers["X-DashScope-Async"] = "enable"
    return headers


# ──────────────────────────────────────────────
#  text2video-gen  (happyhorse-1.0-t2v, HTTP async)
# ──────────────────────────────────────────────
def text2video_gen(args):
    [REDACTED_SECRET])
    url = f"{BASE_URL}/api/v1/services/aigc/video-generation/video-synthesis"

    input_data = {"prompt": args.prompt}

    parameters = {
        "resolution": args.resolution,
        "ratio": args.ratio,
        "duration": args.duration,
        "watermark": args.watermark,
    }
    if args.seed is not None:
        parameters["seed"] = args.seed

    payload = {
        "model": "happyhorse-1.0-t2v",
        "input": input_data,
        "parameters": parameters,
    }

    headers = get_headers(api_key, async_mode=True)

    print(f"[text2video-gen] Submitting task ...")
    print(f"  Model     : happyhorse-1.0-t2v")
    print(f"  Prompt    : {args.prompt[:80]}..." if len(args.prompt) > 80 else f"  Prompt    : {args.prompt}")
    print(f"  Resolution: {args.resolution}")
    print(f"  Ratio     : {args.ratio}")
    print(f"  Duration  : {args.duration}s")
    print(f"  Watermark : {args.watermark}")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
        print(f"Error: {result.get('code')} - {result.get('message')}", file=sys.stderr)
        sys.exit(1)

    output = result.get("output", {})
    task_id = output.get("task_id", "")
    task_status = output.get("task_status", "")

    print(f"\nTask submitted successfully!")
    print(f"  Task ID : {task_id}")
    print(f"  Status  : {task_status}")
    print(f"\nTo check the result, run:")
    print(f"  python3 happyhorse-magic.py text2video-get --task-id {task_id}")

    print("\n--- Full Response ---")
    print(json.dumps(result, ensure_ascii=False, indent=2))


def text2video_get(args):
    [REDACTED_SECRET])
    url = f"{BASE_URL}/api/v1/tasks/{args.task_id}"
    headers = {"Authorization": f"Bearer {api_key}"}

    print(f"[text2video-get] Checking task: {args.task_id}")

    resp = requests.get(url, headers=headers, timeout=60)
    result = resp.json()

    if "code" in result:
        print(f"Error: {result.get('code')} - {result.get('message')}", file=sys.stderr)
        sys.exit(1)

    output = result.get("output", {})
    task_status = output.get("task_status", "")

    print(f"  Task ID : {output.get('task_id', '')}")
    print(f"  Status  : {task_status}")

    if task_status == "SUCCEEDED":
        video_url = output.get("video_url", "")
        print(f"  Video   : {video_url}")
    elif task_status == "FAILED":
        print(f"  Error   : {output.get('code', '')} - {output.get('message', '')}")
    else:
        print(f"\nTask is still {task_status}. Please check again later:")
        print(f"  python3 happyhorse-magic.py text2video-get --task-id {args.task_id}")

    print("\n--- Full Response ---")
    print(json.dumps(result, ensure_ascii=False, indent=2))


# ──────────────────────────────────────────────
#  image2video-gen  (happyhorse-1.0-i2v, HTTP async)
# ──────────────────────────────────────────────
def image2video_gen(args):
    [REDACTED_SECRET])
    url = f"{BASE_URL}/api/v1/services/aigc/video-generation/video-synthesis"

    # Build media array - only first_frame supported
    media = [{"type": "first_frame", "url": encode_local_file(args.first_frame)}]

    input_data = {"media": media}
    if args.prompt:
        input_data["prompt"] = args.prompt

    parameters = {
        "resolution": args.resolution,
        "duration": args.duration,
        "watermark": args.watermark,
    }
    if args.seed is not None:
        parameters["seed"] = args.seed

    payload = {
        "model": "happyhorse-1.0-i2v",
        "input": input_data,
        "parameters": parameters,
    }

    headers = get_headers(api_key, async_mode=True)

    print(f"[image2video-gen] Submitting task ...")
    print(f"  Model      : happyhorse-1.0-i2v")
    if args.prompt:
        print(f"  Prompt     : {args.prompt[:60]}..." if len(args.prompt) > 60 else f"  Prompt     : {args.prompt}")
    print(f"  First Frame: {args.first_frame}")
    print(f"  Resolution : {args.resolution}")
    print(f"  Duration   : {args.duration}s")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
        print(f"Error: {result.get('code')} - {result.get('message')}", file=sys.stderr)
        sys.exit(1)

    output = result.get("output", {})
    task_id = output.get("task_id", "")
    task_status = output.get("task_status", "")

    print(f"\nTask submitted successfully!")
    print(f"  Task ID : {task_id}")
    print(f"  Status  : {task_status}")
    print(f"\nTo check the result, run:")
    print(f"  python3 happyhorse-magic.py image2video-get --task-id {task_id}")

    print("\n--- Full Response ---")
    print(json.dumps(result, ensure_ascii=False, indent=2))


def image2video_get(args):
    [REDACTED_SECRET])
    url = f"{BASE_URL}/api/v1/tasks/{args.task_id}"
    headers = {"Authorization": f"Bearer {api_key}"}

    print(f"[image2video-get] Checking task: {args.task_id}")

    resp = requests.get(url, headers=headers, timeout=60)
    result = resp.json()

    if "code" in result:
        print(f"Error: {result.get('code')} - {result.get('message')}", file=sys.stderr)
        sys.exit(1)

    output = result.get("output", {})
    task_status = output.get("task_status", "")

    print(f"  Task ID : {output.get('task_id', '')}")
    print(f"  Status  : {task_status}")

    if task_status == "SUCCEEDED":
        video_url = output.get("video_url", "")
        print(f"  Video   : {video_url}")
    elif task_status == "FAILED":
        print(f"  Error   : {output.get('code', '')} - {output.get('message', '')}")
    else:
        print(f"\nTask is still {task_status}. Please check again later:")
        print(f"  python3 happyhorse-magic.py image2video-get --task-id {args.task_id}")

    print("\n--- Full Response ---")
    print(json.dumps(result, ensure_ascii=False, indent=2))


# ──────────────────────────────────────────────
#  reference2video-gen  (happyhorse-1.0-r2v, HTTP async)
# ──────────────────────────────────────────────
def reference2video_gen(args):
    [REDACTED_SECRET])
    url = f"{BASE_URL}/api/v1/services/aigc/video-generation/video-synthesis"

    # Build media array from reference images (1-9 images)
    media = []
    if args.reference_images:
        for ref_image in args.reference_images:
            media.append({"type": "reference_image", "url": encode_local_file(ref_image)})

    if not media:
        print("Error: At least one --reference-images is required.", file=sys.stderr)
        sys.exit(1)

    input_data = {
        "prompt": args.prompt,
        "media": media,
    }

    parameters = {
        "resolution": args.resolution,
        "ratio": args.ratio,
        "duration": args.duration,
        "watermark": args.watermark,
    }
    if args.seed is not None:
        parameters["seed"] = args.seed

    payload = {
        "model": "happyhorse-1.0-r2v",
        "input": input_data,
        "parameters": parameters,
    }

    headers = get_headers(api_key, async_mode=True)

    print(f"[reference2video-gen] Submitting task ...")
    print(f"  Model      : happyhorse-1.0-r2v")
    print(f"  Prompt     : {args.prompt[:60]}..." if len(args.prompt) > 60 else f"  Prompt     : {args.prompt}")
    print(f"  Ref Images : {args.reference_images}")
    print(f"  Resolution : {args.resolution}")
    print(f"  Ratio      : {args.ratio}")
    print(f"  Duration   : {args.duration}s")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
        print(f"Error: {result.get('code')} - {result.get('message')}", file=sys.stderr)
        sys.exit(1)

    output = result.get("output", {})
    task_id = output.get("task_id", "")
    task_status = output.get("task_status", "")

    print(f"\nTask submitted successfully!")
    print(f"  Task ID : {task_id}")
    print(f"  Status  : {task_status}")
    print(f"\nTo check the result, run:")
    print(f"  python3 happyhorse-magic.py reference2video-get --task-id {task_id}")

    print("\n--- Full Response ---")
    print(json.dumps(result, ensure_ascii=False, indent=2))


def reference2video_get(args):
    [REDACTED_SECRET])
    url = f"{BASE_URL}/api/v1/tasks/{args.task_id}"
    headers = {"Authorization": f"Bearer {api_key}"}

    print(f"[reference2video-get] Checking task: {args.task_id}")

    resp = requests.get(url, headers=headers, timeout=60)
    result = resp.json()

    if "code" in result:
        print(f"Error: {result.get('code')} - {result.get('message')}", file=sys.stderr)
        sys.exit(1)

    output = result.get("output", {})
    task_status = output.get("task_status", "")

    print(f"  Task ID : {output.get('task_id', '')}")
    print(f"  Status  : {task_status}")

    if task_status == "SUCCEEDED":
        video_url = output.get("video_url", "")
        print(f"  Video   : {video_url}")
    elif task_status == "FAILED":
        print(f"  Error   : {output.get('code', '')} - {output.get('message', '')}")
    else:
        print(f"\nTask is still {task_status}. Please check again later:")
        print(f"  python3 happyhorse-magic.py reference2video-get --task-id {args.task_id}")

    print("\n--- Full Response ---")
    print(json.dumps(result, ensure_ascii=False, indent=2))


# ──────────────────────────────────────────────
#  video-edit  (happyhorse-1.0-video-edit, HTTP async)
# ──────────────────────────────────────────────
def video_edit(args):
    [REDACTED_SECRET])
    url = f"{BASE_URL}/api/v1/services/aigc/video-generation/video-synthesis"

    # Build media array
    media = [{"type": "video", "url": encode_local_file(args.video)}]

    if args.reference_images:
        for img in args.reference_images:
            media.append({"type": "reference_image", "url": encode_local_file(img)})

    input_data = {
        "prompt": args.prompt,
        "media": media,
    }

    parameters = {
        "resolution": args.resolution,
        "watermark": args.watermark,
        "audio_setting": args.audio_setting,
    }
    if args.seed is not None:
        parameters["seed"] = args.seed

    payload = {
        "model": "happyhorse-1.0-video-edit",
        "input": input_data,
        "parameters": parameters,
    }

    headers = get_headers(api_key, async_mode=True)

    print(f"[video-edit] Submitting task ...")
    print(f"  Model      : happyhorse-1.0-video-edit")
    print(f"  Prompt     : {args.prompt[:60]}..." if len(args.prompt) > 60 else f"  Prompt     : {args.prompt}")
    print(f"  Video      : {args.video}")
    if args.reference_images:
        print(f"  References : {args.reference_images}")
    print(f"  Resolution : {args.resolution}")
    print(f"  Audio      : {args.audio_setting}")

    resp = requests.post(url, headers=headers, json=payload, timeout=60)
    result = resp.json()

    if "code" in result:
        print(f"Error: {result.get('code')} - {result.get('message')}", file=sys.stderr)
        sys.exit(1)

    output = result.get("output", {})
    task_id = output.get("task_id", "")
    task_status = output.get("task_status", "")

    print(f"\nTask submitted successfully!")
    print(f"  Task ID : {task_id}")
    print(f"  Status  : {task_status}")
    print(f"\nTo check the result, run:")
    print(f"  python3 happyhorse-magic.py video-edit-get --task-id {task_id}")

    print("\n--- Full Response ---")
    print(json.dumps(result, ensure_ascii=False, indent=2))


def video_edit_get(args):
    [REDACTED_SECRET])
    url = f"{BASE_URL}/api/v1/tasks/{args.task_id}"
    headers = {"Authorization": f"Bearer {api_key}"}

    print(f"[video-edit-get] Checking task: {args.task_id}")

    resp = requests.get(url, headers=headers, timeout=60)
    result = resp.json()

    if "code" in result:
        print(f"Error: {result.get('code')} - {result.get('message')}", file=sys.stderr)
        sys.exit(1)

    output = result.get("output", {})
    task_status = output.get("task_status", "")

    print(f"  Task ID : {output.get('task_id', '')}")
    print(f"  Status  : {task_status}")

    if task_status == "SUCCEEDED":
        video_url = output.get("video_url", "")
        print(f"  Video   : {video_url}")
    elif task_status == "FAILED":
        print(f"  Error   : {output.get('code', '')} - {output.get('message', '')}")
    else:
        print(f"\nTask is still {task_status}. Please check again later:")
        print(f"  python3 happyhorse-magic.py video-edit-get --task-id {args.task_id}")

    print("\n--- Full Response ---")
    print(json.dumps(result, ensure_ascii=False, indent=2))


# ──────────────────────────────────────────────
#  CLI entry point
# ──────────────────────────────────────────────
def main():
    parser = argparse.ArgumentParser(
        description="HappyHorse Video Magic - Video Generation & Editing Tool"
    )
    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # --- text2video-gen ---
    p = subparsers.add_parser("text2video-gen", help="Submit text-to-video task (happyhorse-1.0-t2v)")
    p.add_argument("--prompt", required=True, help="Text prompt for video generation (max 5000 non-Chinese chars or 2500 Chinese chars)")
    p.add_argument("--resolution", default="1080P", choices=["720P", "1080P"], help="Video resolution (default: 1080P)")
    p.add_argument("--ratio", default="16:9", choices=["16:9", "9:16", "1:1", "4:3", "3:4"], help="Aspect ratio (default: 16:9)")
    p.add_argument("--duration", type=int, default=5, help="Video duration in seconds, 3-15 (default: 5)")
    p.add_argument("--no-watermark", action="store_false", dest="watermark", help="Disable 'Happy Horse' watermark (default: enabled)")
    p.add_argument("--seed", type=int, help="Random seed [0, 2147483647] for reproducibility")

    # --- text2video-get ---
    p = subparsers.add_parser("text2video-get", help="Get text-to-video task result")
    p.add_argument("--task-id", required=True, help="Task ID from text2video-gen")

    # --- image2video-gen ---
    p = subparsers.add_parser("image2video-gen", help="Submit image-to-video task (happyhorse-1.0-i2v)")
    p.add_argument("--prompt", help="Text prompt for video generation (optional)")
    p.add_argument("--first-frame", required=True, help="First frame image URL (required)")
    p.add_argument("--resolution", default="1080P", choices=["720P", "1080P"], help="Video resolution (default: 1080P). Output ratio follows input image.")
    p.add_argument("--duration", type=int, default=5, help="Video duration in seconds, 3-15 (default: 5)")
    p.add_argument("--no-watermark", action="store_false", dest="watermark", help="Disable 'Happy Horse' watermark (default: enabled)")
    p.add_argument("--seed", type=int, help="Random seed [0, 2147483647] for reproducibility")

    # --- image2video-get ---
    p = subparsers.add_parser("image2video-get", help="Get image-to-video task result")
    p.add_argument("--task-id", required=True, help="Task ID from image2video-gen")

    # --- reference2video-gen ---
    p = subparsers.add_parser("reference2video-gen", help="Submit reference-to-video task (happyhorse-1.0-r2v)")
    p.add_argument("--prompt", required=True, help="Text prompt with character references like 'character1', 'character2' (max 5000 non-Chinese chars)")
    p.add_argument("--reference-images", nargs="+", required=True, help="Reference image URLs (1-9 images, use 'character1/character2/...' in prompt)")
    p.add_argument("--resolution", default="1080P", choices=["720P", "1080P"], help="Video resolution (default: 1080P)")
    p.add_argument("--ratio", default="16:9", choices=["16:9", "9:16", "1:1", "4:3", "3:4"], help="Aspect ratio (default: 16:9)")
    p.add_argument("--duration", type=int, default=5, help="Video duration in seconds, 3-15 (default: 5)")
    p.add_argument("--no-watermark", action="store_false", dest="watermark", help="Disable 'Happy Horse' watermark (default: enabled)")
    p.add_argument("--seed", type=int, help="Random seed [0, 2147483647] for reproducibility")

    # --- reference2video-get ---
    p = subparsers.add_parser("reference2video-get", help="Get reference-to-video task result")
    p.add_argument("--task-id", required=True, help="Task ID from reference2video-gen")

    # --- video-edit ---
    p = subparsers.add_parser("video-edit", help="Submit video editing task (happyhorse-1.0-video-edit)")
    p.add_argument("--prompt", required=True, help="Editing instruction (required, max 5000 non-Chinese chars)")
    p.add_argument("--video", required=True, help="Input video URL to edit (mp4/mov, 3-60s, output max 15s)")
    p.add_argument("--reference-images", nargs="+", help="Reference image URLs for editing (0-5 images)")
    p.add_argument("--resolution", default="1080P", choices=["720P", "1080P"], help="Output resolution (default: 1080P)")
    p.add_argument("--audio-setting", default="auto", choices=["auto", "origin"], help="Audio handling: auto (default) or origin (keep original)")
    p.add_argument("--no-watermark", action="store_false", dest="watermark", help="Disable 'Happy Horse' watermark (default: enabled)")
    p.add_argument("--seed", type=int, help="Random seed [0, 2147483647] for reproducibility")

    # --- video-edit-get ---
    p = subparsers.add_parser("video-edit-get", help="Get video editing task result")
    p.add_argument("--task-id", required=True, help="Task ID from video-edit")

    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(1)

    # Default watermark to True for all commands
    if not hasattr(args, "watermark"):
        args.watermark = True

    command_map = {
        "text2video-gen": text2video_gen,
        "text2video-get": text2video_get,
        "image2video-gen": image2video_gen,
        "image2video-get": image2video_get,
        "reference2video-gen": reference2video_gen,
        "reference2video-get": reference2video_get,
        "video-edit": video_edit,
        "video-edit-get": video_edit_get,
    }

    handler = command_map.get(args.command)
    if handler:
        handler(args)
    else:
        print(f"Unknown command: {args.command}", file=sys.stderr)
        parser.print_help()
        sys.exit(1)


if __name__ == "__main__":
    main()


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