
Seedance Video Gen
- 261 installs
- 316 repo stars
- Updated August 4, 2026
- redfox-data/redfox-community
Use seedance-video-gen for development tasks
About
seedance-video-gen: A skill for development. This provides functionality for development workflows.
- seedance-video-gen
Seedance Video Gen by the numbers
- 261 all-time installs (skills.sh)
- +14 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #1,463 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/redfox-data/redfox-community --skill seedance-video-genAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 261 |
|---|---|
| repo stars | ★ 316 |
| Last updated | August 4, 2026 |
| Repository | redfox-data/redfox-community ↗ |
What it does
Use seedance-video-gen for development tasks
Files
Seedance2.0
简介
Seedance2.0是一款基于火山方舟 Seedance 2.0 模型的 AI 视频生成工具。通过 redfox.hk 平台封装了复杂的 ARK API 鉴权流程,一行命令即可生成视频。
Skill 特色
>
- 支持命令行参数化控制分辨率、比例、时长
- 文生视频 + 虚拟人像引用双模式
为什么用这个 Skill?
- 对开发者:无需去火山方舟申请白名单、配置 AK/SK、对接 ARK 鉴权,一行命令搞定
- 对创作者:不用买会员、不用绑卡订阅,按次消费,用完即走
- 对所有人:极低门槛,注册即用,无需自行搭建 API 链路
适用对象
社交媒体创作者、产品经理、内容运营人员、AI 爱好者,以及任何需要快速将文案转化为视频的用户。
---
功能特性
核心功能
- 文生视频:输入一句中文或英文描述,Seedance 2.0 自动生成带有同步音频的 MP4 视频
- 参数控制:支持分辨率(480p/720p/1080p)、画面比例(16:9/9:16 等 7 种)、视频时长(4-15 秒)
- 虚拟人像:支持通过
asset://格式引用预置虚拟人像,无需上传真实人脸素材 - 任务管理:支持提交任务后获取 taskId,可随时查询任务进度和结果
- 自动轮询:脚本自动轮询任务状态(排队中/生成中/已完成),无需手动等待
技术亮点
- 底层模型:
doubao-seedance-2-0-260128 - 同步音频生成:视频画面与声音同步输出
- 尾帧提取:支持返回视频最后一帧,用于连续视频生成
- 随机种子:支持设置 seed 值,结果可复现
- HTTPS 安全传输:全链路 SSL 验证
参数速查
| 参数 | 说明 | 默认值 |
|---|---|---|
prompt | 视频提示词(必填,中英文均可) | — |
--resolution | 分辨率:480p / 720p / 1080p | 720p |
--ratio | 画面比例:16:9 / 4:3 / 1:1 / 3:4 / 9:16 / 21:9 / adaptive | 16:9 |
--duration | 视频时长(秒):4-15 或 -1(智能) | 5 |
--seed | 随机种子:-1 到 2147483647 | -1 |
--no-audio | 不生成声音 | 默认生成 |
--watermark | 添加水印 | 默认不添加 |
--return-last-frame | 返回尾帧图片 URL | 默认不返回 |
--image-url | 参考图 URL(asset:// 格式) | — |
-o, --output-dir | 输出目录 | ~/Downloads/QoderVideos |
--prefix | 文件名前缀 | video |
--no-download | 仅提交不等待 | — |
--task-id | 查询已有任务 | — |
---
使用场景
场景一:社交媒体内容创作
角色:短视频博主 / 小红书运营
需求:快速生成竖屏(9:16)视频素材,用于抖音/小红书/视频号发布
使用方式:
python3 "$SKILL_PATH/scripts/videogen.py" "美妆产品展示,柔和的灯光,产品缓缓旋转" --ratio 9:16预期收益:降低素材制作成本,从数小时缩短到几分钟
---
场景二:产品功能演示
角色:产品经理 / 创业者
需求:快速生成产品概念视频,用于提案、路演或展示
使用方式:
python3 "$SKILL_PATH/scripts/videogen.py" "一款智能手表在桌面上展示,表盘切换不同功能界面" --resolution 1080p --duration 8预期收益:无需专业视频制作能力,文案即视频
---
场景三:品牌内容运营
角色:内容运营 / 市场人员
需求:批量生成品牌配景视频,搭配文案制作品牌宣传短片
使用方式:
python3 "$SKILL_PATH/scripts/videogen.py" "品牌主题色背景,文字渐变浮现,专业大气" --ratio 16:9 --no-audio预期收益:提升内容产出效率,统一品牌视觉风格
---
场景四:创意灵感快速验证
角色:设计师 / 创意人员
需求:将脑中的画面用自然语言快速变成可视化视频,验证创意方向
使用方式:
python3 "$SKILL_PATH/scripts/videogen.py" "赛博朋克风格的城市夜景,霓虹灯闪烁,雨滴落在镜头上的效果" --duration 6预期收益:降低创意门槛,想法到视频只需一行命令
---
场景五:教育培训可视化
角色:教师 / 培训师
需求:将抽象知识点转化为可视化短片,提升教学效果
使用方式:
python3 "$SKILL_PATH/scripts/videogen.py" "太阳系行星围绕太阳旋转的动画,标注每颗行星的名称" --duration 10预期收益:无需动画制作技能,知识可视化零门槛
---
首次使用
配置 API Key 后即可使用:
# 设置环境变量
export REDFOX_API_KEY=ak_你的密钥
# 运行
python3 "$SKILL_PATH/scripts/videogen.py" "一只橘猫在窗台上打哈欠,阳光温暖地照在它的毛上"前往 redfox.hk 注册获取 API Key。
---
后续使用
可前往 redfox.hk 注册账号获取自己的 API Key,三种配置方式任选其一:
| 配置方式 | 说明 | 命令 |
|---|---|---|
| 环境变量(推荐) | 设置一次,全局生效 | export REDFOX_API_KEY=ak_你的密钥 |
| 命令行参数 | 临时使用,单次生效 | python3 "$SKILL_PATH/scripts/videogen.py" "prompt" --api-key ak_你的密钥 |
| 配置文件 | 持久化存储,跨会话保留 | mkdir -p ~/.qoder/apis && echo '{"api_key":"ak_你的密钥"}' > ~/.qoder/apis/redfox.json |
---
一键安装
依赖安装
pip3 install requests环境变量
| 变量名 | 必填 | 说明 |
|---|---|---|
REDFOX_API_KEY | — | redfox.hk 平台的 API 访问密钥 |
---
使用指南
基础使用
1. 输入提示词生成视频
python3 "$SKILL_PATH/scripts/videogen.py" "一只橘猫在窗台上打哈欠,阳光温暖地照在它的毛上"脚本会自动提交任务、轮询等待(约 3-15 分钟),完成后下载到 ~/Downloads/QoderVideos/。
2. 查看结果
生成成功后会显示视频信息和文件路径:
[✓] Video ready: 5s, 720p, 16:9
[✓] Token usage: 108900
[→] Downloading video: video.mp4
[✓] Done!
/Users/you/Downloads/QoderVideos/video.mp4 (2.5 MB)高级使用
竖屏短视频(适配抖音/小红书)
python3 "$SKILL_PATH/scripts/videogen.py" "时尚都市夜景,霓虹灯闪烁" --ratio 9:16高清长视频
python3 "$SKILL_PATH/scripts/videogen.py" "海浪拍打岩石的慢动作" --resolution 1080p --duration 10使用预置虚拟人像
python3 "$SKILL_PATH/scripts/videogen.py" "一位年轻女性在图书馆安静阅读" --image-url "asset://female_student_01"更多预置虚拟人像 ID 请参考火山方舟「素材&虚拟人像库」文档。
任务管理
# 仅提交任务,返回 taskId 后立即退出
python3 "$SKILL_PATH/scripts/videogen.py" "复杂场景" --no-download
# 稍后用 taskId 查询结果
python3 "$SKILL_PATH/scripts/videogen.py" "ignored" --task-id vg_abc123def456多段连续视频生成
# 1. 生成第一段,获取尾帧
python3 "$SKILL_PATH/scripts/videogen.py" "开场画面" --return-last-frame
# 2. 将返回的 lastFrameUrl 作为下一段的首帧
python3 "$SKILL_PATH/scripts/videogen.py" "转场到第二个场景" --image-url "上一步的lastFrameUrl"命令速查
| 命令 | 功能 |
|---|---|
python3 videogen.py "提示词" | 基础文生视频 |
--ratio 9:16 | 竖屏比例 |
--resolution 1080p | 高清分辨率 |
--duration 10 | 指定时长 |
--no-audio | 静音视频 |
--watermark | 添加水印 |
--seed 42 | 固定种子可复现 |
--image-url "asset://..." | 引用虚拟人像 |
--no-download | 仅提交任务 |
--task-id <id> | 查询已有任务 |
--api-key <key> | 指定 API Key |
-o ~/Desktop | 指定输出目录 |
---
项目架构
目录结构
seedance-video-gen/
├── SKILL.md # Skill 定义与文档
└── scripts/ # 工具脚本
└── videogen.py # 视频生成主程序技术栈
| 组件 | 技术 |
|---|---|
| 运行环境 | Python 3.6+ |
| HTTP 库 | requests |
| API 平台 | redfox.hk |
| 底层模型 | 火山方舟 Seedance 2.0 (doubao-seedance-2-0-260128) |
| 输出格式 | MP4 |
核心模块
| 模块 | 职责 |
|---|---|
get_api_key() | 三级优先级获取 API Key:CLI > 环境变量 > 配置文件 |
submit_video_task() | 构建 content 数组,提交视频生成任务 |
poll_video_result() | 轮询任务状态(queued/running/succeeded/failed/expired),最多 20 分钟 |
download_video() | 流式下载 MP4 视频,带进度条显示 |
main() | CLI 入口:参数解析、API Key 校验、双分支(提交/查询)流程 |
数据流转
用户输入提示词 → submit_video_task() → redfox.hk API → 火山方舟 Seedance 2.0
↓
用户获得 MP4 ← download_video() ← poll_video_result() ← 任务完成回调---
常见问答
安装相关问题
Q1:需要 API Key 吗?
A:需要。前往 redfox.hk 注册获取自己的 API Key,通过环境变量 REDFOX_API_KEY 或 --api-key 参数配置。
Q2:配置文件放在哪里?
A:放在 ~/.qoder/apis/redfox.json,内容格式为:{"api_key": "ak_你的密钥"}。
Q3:如何验证 API Key 是否配置成功?
A:运行 python3 videogen.py "测试" --no-download,如果能返回 taskId 则配置成功。
---
使用相关问题
Q4:生成一个视频需要多久?
A:通常 3-15 分钟,复杂场景可能更长。脚本会自动轮询等待,最多等待 20 分钟。
Q5:支持上传自己的参考图吗?
A:Seedance 2.0 不支持直接上传含真人人脸的参考图/视频。如需指定人物外观,可在提示词中描述(如"一位戴眼镜的亚洲女性"),或使用预置虚拟人像库的 asset:// URL。更多信息请参考火山方舟「素材&虚拟人像库」文档。
Q6:支持哪些提示词语言?
A:中英文均可,API 内部会自动处理。
Q7:输出文件保存在哪里?
A:默认保存到 ~/Downloads/QoderVideos/video.mp4,可通过 -o 参数指定目录。
---
故障排除
Q8:任务超时了怎么办?
A:脚本最多等待 20 分钟。超时后会打印 taskId,你可以稍后通过 --task-id 参数重新查询并下载结果。
Q9:提示"API request failed"?
A:检查网络连接是否正常,确认 redfox.hk 服务可访问。如果持续失败,可能是 API Key 已过期或余额不足。
Q10:视频下载失败?
A:确认输出目录有写入权限,磁盘空间充足。如果 OSS 链接过期,可以用 --task-id 重新查询获取新的下载链接。
---
获取帮助
如有其他问题,可前往 redfox.hk 查看平台文档或联系客服。
Seedance2.0 / seedance-video-gen
---
Overview
An AI video generator based on the Volcano Engine Seedance 2.0 model. Generate MP4 videos with a single command.
Core Value
- Text-to-Video: Enter Chinese or English descriptions to auto-generate MP4 videos with synchronized audio
- Full Control: Adjustable resolution (480p/720p/1080p), aspect ratio (7 options), and duration (4-15 seconds)
- Virtual Avatars: Reference preset virtual avatars without uploading real face assets
Target Users
- 🎬 Short Video Creators — Quickly generate vertical video assets for Douyin/Xiaohongshu/WeChat Channels
- 📦 Product Managers — Generate product concept videos for proposals and demos
- 🎨 Creative Professionals — Turn ideas into visual videos with natural language
---
Features
Core Features
- Text-to-Video: Enter a prompt and Seedance 2.0 generates an MP4 with synchronized audio
- Parameter Control: Customize resolution, aspect ratio, and video duration
- Virtual Avatars: Reference preset virtual avatars without uploading real face assets
- Auto-Polling: The script automatically waits for task completion, no manual refresh needed
- Task Management: Submit-only mode with taskId for later query and download
---
API Key Acquisition & Security
- This skill requires the environment variable:
REDFOX_API_KEY. REDFOX_API_KEYis provided by RedFoxHub (https://redfox.hk).- Please visit RedFoxHub to register and obtain your
REDFOX_API_KEY. - Configure the environment variable
REDFOX_API_KEYon your device before using this skill. - Before providing your key, verify its source, available scope, validity period, and whether it supports reset/revocation.
- Do not hardcode or expose the key in plaintext within code, prompts, logs, or output files.
---
Usage Guide
Simply describe the video scene you want in natural language.
Quick Reference
| Intent | Example | Result |
|---|---|---|
| Text-to-Video | "Generate a video of an orange cat yawning on a windowsill" | Submits task, auto-generates MP4 video |
| Vertical Short Video | "Generate a vertical city nightscape video" | 9:16 aspect ratio for Douyin/Xiaohongshu |
| HD Long Video | "Generate a 1080p landscape video, 10 seconds" | High-quality long-duration output |
| Query Progress | "Check the result of video task XXX" | Query and download completed video by taskId |
---
Use Cases
| Scenario | Role | Example Prompt | Benefit |
|---|---|---|---|
| Social media creation | Short video creator | "Generate a vertical cosmetics product demo video" | Quick video asset production, lower costs |
| Product demo | Product manager | "Generate a smartwatch feature demo video" | No professional skills needed, text-to-video |
| Creative validation | Designer | "Generate a cyberpunk city nightscape" | From idea to video in one command |
| Brand marketing | Content marketer | "Generate a brand-themed background video" | Consistent visual style, higher output efficiency |
Seedance2.0 / seedance-video-gen
---
简介
基于火山方舟 Seedance 2.0 模型的 AI 视频生成器,一行命令即可生成 MP4 视频。
核心价值
- 文生视频:输入中文或英文描述,自动生成带同步音频的 MP4 视频
- 参数可控:支持分辨率(480p/720p/1080p)、画面比例(7 种)、时长(4-15 秒)
- 虚拟人像:支持引用预置虚拟人像,无需上传真实人脸素材
适用对象
- 🎬 短视频创作者 — 快速生成竖屏视频素材,用于抖音/小红书/视频号
- 📦 产品经理 — 生成产品概念视频,用于提案或路演展示
- 🎨 创意人员 — 将脑中的画面用自然语言快速变成可视化视频
---
功能特性
核心功能
- 文生视频:输入提示词,Seedance 2.0 自动生成带同步音频的 MP4
- 参数控制:分辨率、画面比例、视频时长均可自定义
- 虚拟人像:支持引用预置虚拟人像,无需上传真实人脸素材
- 自动轮询:脚本自动等待任务完成,无需手动刷新
- 任务管理:支持仅提交任务获取 taskId,稍后查询下载
---
密钥获取与安全说明
- 本技能需要使用环境变量:
REDFOX_API_KEY。 REDFOX_API_KEY由 红狐 hub (https://redfox.hk)提供。- 请前往 红狐 hub 注册账号,获取
REDFOX_API_KEY。 - 配置设备环境变量
REDFOX_API_KEY后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
- 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。
---
使用指南
直接用自然语言描述你想要的视频画面即可。
常用说法速查
| 意图 | 示例话术 | 效果 |
|---|---|---|
| 文生视频 | 「生成一只橘猫在窗台上打哈欠的视频」 | 提交任务,自动生成 MP4 视频 |
| 竖屏短视频 | 「生成竖屏的城市夜景视频」 | 9:16 比例,适配抖音/小红书 |
| 高清长视频 | 「生成 1080p 的风景视频,10 秒」 | 高画质长时长输出 |
| 查询进度 | 「查询任务 XXX 的视频生成结果」 | 按 taskId 查询并下载已完成视频 |
---
使用场景
| 场景 | 角色 | 示例问法 | 收益 |
|---|---|---|---|
| 社交媒体创作 | 短视频博主 | 「生成竖屏美妆产品展示视频」 | 快速产出视频素材,降低制作成本 |
| 产品演示 | 产品经理 | 「生成智能手表功能演示视频」 | 无需专业制作能力,文案即视频 |
| 创意验证 | 设计师 | 「生成赛博朋克风格的城市夜景」 | 想法到视频只需一行命令 |
| 品牌运营 | 内容运营 | 「生成品牌主题背景视频」 | 统一视觉风格,提升产出效率 |
#!/usr/bin/env python3
"""
Qoder Video Generator - 基于 Seedance 2.0 的视频生成工具
使用 redfox.hk API 提交视频生成任务并下载结果
Usage:
python3 videogen.py "提示词" [options]
python3 videogen.py "ignored" --task-id vg_xxx
"""
import argparse
import json
import os
import sys
import time
from pathlib import Path
import requests
SUBMIT_URL = "https://redfox.hk/story/api/parseWork/videoGen/submit"
RESULT_URL = "https://redfox.hk/story/api/parseWork/videoGen/result"
CONFIG_DIR = Path.home() / ".qoder" / "apis"
CONFIG_FILE = CONFIG_DIR / "redfox.json"
ENV_KEY = "REDFOX_API_KEY"
SOURCE = "seedance2.0-GitHub"
DEFAULT_MODEL = "doubao-seedance-2-0-260128"
POLL_INTERVAL = 10 # seconds
MAX_POLL_ATTEMPTS = 120 # max ~20 minutes
GREEN = "\033[92m"
YELLOW = "\033[93m"
RED = "\033[91m"
CYAN = "\033[96m"
BOLD = "\033[1m"
RESET = "\033[0m"
def info(msg):
print(f"{GREEN}[✓]{RESET} {msg}")
def warn(msg):
print(f"{YELLOW}[!]{RESET} {msg}")
def error(msg):
print(f"{RED}[✗]{RESET} {msg}")
def step(msg):
print(f"{CYAN}[→]{RESET} {msg}")
def get_api_key(cli_key=None):
"""Get API key: CLI arg > env var > config file."""
if cli_key:
return cli_key
env_key = os.environ.get(ENV_KEY)
if env_key:
return env_key
if CONFIG_FILE.exists():
try:
data = json.loads(CONFIG_FILE.read_text())
key = data.get("api_key")
if key:
return key
except (json.JSONDecodeError, OSError):
pass
return None
def confirm_retry():
"""询问用户是否需要重试。"""
while True:
answer = input(f"{YELLOW}[?]{RESET} 是否重试?(y/n): ").strip().lower()
if answer in ('y', 'yes'):
return True
if answer in ('n', 'no'):
return False
def submit_video_task(session, prompt, params, image_url=None):
"""Submit video generation task, return taskId."""
# Build content array
content = [{"type": "text", "text": prompt}]
if image_url:
content.append({
"type": "image_url",
"imageUrl": image_url,
"imageRole": "first_frame",
})
payload = {
"content": content,
"model": DEFAULT_MODEL,
"source": SOURCE,
"resolution": params["resolution"],
"ratio": params["ratio"],
"duration": params["duration"],
"seed": params["seed"],
"watermark": params["watermark"],
"generateAudio": params["generateAudio"],
"returnLastFrame": params["returnLastFrame"],
"executionExpiresAfter": 172800,
}
try:
resp = session.post(SUBMIT_URL, json=payload, timeout=30)
result = resp.json()
except requests.exceptions.RequestException as e:
error(f"API request failed: {e}")
return None
except json.JSONDecodeError:
error(f"API returned invalid JSON: {resp.text[:200]}")
return None
code = result.get("code")
msg = result.get("msg", "")
if not str(code).startswith("2"):
error(f"Submit failed (code {code}): {msg}")
return None
data = result.get("data")
if not data or not data.get("taskId"):
error("API did not return taskId")
return None
return data["taskId"]
def poll_video_result(session, task_id):
"""Poll for video task result until succeeded/failed/expired/timeout."""
consecutive_errors = 0
for attempt in range(1, MAX_POLL_ATTEMPTS + 1):
try:
resp = session.post(RESULT_URL, json={"taskId": task_id}, timeout=15)
result = resp.json()
except requests.exceptions.RequestException as e:
consecutive_errors += 1
warn(f"Poll request failed (attempt {attempt}): {e}")
if consecutive_errors >= 5:
error("Too many consecutive network errors, giving up")
return None
time.sleep(POLL_INTERVAL)
continue
except json.JSONDecodeError:
consecutive_errors += 1
warn(f"Invalid JSON response (attempt {attempt})")
if consecutive_errors >= 5:
error("Too many consecutive decode errors, giving up")
return None
time.sleep(POLL_INTERVAL)
continue
consecutive_errors = 0 # Reset on success
code = result.get("code")
if not str(code).startswith("2"):
error(f"Query failed (code {code}): {result.get('msg', '')}")
return None
data = result.get("data", {})
status = data.get("status")
if status == "succeeded":
return data
elif status == "failed":
reason = data.get("failReason", "unknown")
error(f"Generation failed: {reason}")
return None
elif status == "expired":
error("Task expired: the task did not complete within the allowed time")
return None
else:
# queued or running
elapsed = attempt * POLL_INTERVAL
status_cn = {"queued": "排队中", "running": "生成中"}.get(status, status)
print(f"\r {CYAN}⏳ Generating... ({elapsed}s) [{status_cn}]{RESET}", end="", flush=True)
time.sleep(POLL_INTERVAL)
print()
error(f"Timeout: task did not complete within {MAX_POLL_ATTEMPTS * POLL_INTERVAL}s")
print(f" You can query later with: --task-id {task_id}")
return None
def download_video(session, video_url, output_dir, prefix="video", task_id=""):
"""Download generated video to output directory."""
# Determine extension from URL
ext = ".mp4"
url_path = video_url.split("?")[0]
for fmt in [".mp4", ".mov", ".avi", ".webm"]:
if url_path.lower().endswith(fmt):
ext = fmt
break
# Handle filename conflict: append short taskId if file exists
filename = f"{prefix}{ext}"
filepath = os.path.join(output_dir, filename)
if os.path.exists(filepath) and task_id:
short_id = task_id.replace("vg_", "")[:8]
filename = f"{prefix}_{short_id}{ext}"
filepath = os.path.join(output_dir, filename)
step(f"Downloading video: {filename}")
try:
resp = session.get(video_url, stream=True, timeout=300)
resp.raise_for_status()
total_size = int(resp.headers.get("content-length", 0))
dl = 0
with open(filepath, "wb") as f:
for chunk in resp.iter_content(chunk_size=8192):
if chunk:
f.write(chunk)
dl += len(chunk)
if total_size > 0:
pct = int(dl * 100 / total_size)
bar = "█" * (pct // 5) + "░" * (20 - pct // 5)
print(f"\r {bar} {pct}%", end="", flush=True)
print()
return filepath
except requests.exceptions.RequestException as e:
error(f"Download failed: {e}")
return None
def main():
parser = argparse.ArgumentParser(
description="AI 视频生成器 - 基于 Seedance 2.0 模型,输入关键词生成视频",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
# 基本生成
python3 videogen.py "一只橘猫在窗台上打哈欠,阳光温暖地照在它的毛上"
# 竖屏短视频
python3 videogen.py "时尚都市夜景,霓虹灯闪烁" --ratio 9:16
# 高清 10 秒
python3 videogen.py "海浪拍打岩石的慢动作" --resolution 1080p --duration 10
# 使用预置虚拟人像
python3 videogen.py "一位年轻女性在图书馆阅读" --image-url "asset://female_student_01"
# 仅提交任务
python3 videogen.py "complex scene" --no-download
# 查询已有任务
python3 videogen.py "ignored" --task-id vg_abc123def456
""",
)
parser.add_argument("prompt", help="视频生成提示词(中英文均可)")
parser.add_argument("--resolution", default="720p", choices=["480p", "720p", "1080p"],
help="视频分辨率 (默认 720p)")
parser.add_argument("--ratio", default="16:9",
choices=["16:9", "4:3", "1:1", "3:4", "9:16", "21:9", "adaptive"],
help="画面比例 (默认 16:9)")
parser.add_argument("--duration", type=int, default=5,
help="视频时长秒数: 4-15 或 -1(智能) (默认 5)")
parser.add_argument("--seed", type=int, default=-1,
help="随机种子: -1(随机) 到 2147483647 (默认 -1)")
parser.add_argument("--no-audio", action="store_true",
help="不生成声音 (默认生成)")
parser.add_argument("--watermark", action="store_true",
help="添加水印 (默认不添加)")
parser.add_argument("--return-last-frame", action="store_true",
help="返回视频尾帧图片 URL")
parser.add_argument("--image-url",
help="参考图 URL (asset:// 格式引用预置虚拟人像)")
parser.add_argument("--api-key", help="API Key (前往 https://redfox.hk/settings/api-keys?source=github 注册获取)")
parser.add_argument("-o", "--output-dir", help="输出目录 (默认 ~/Downloads/QoderVideos)")
parser.add_argument("--prefix", default="video", help="下载文件名前缀 (默认 video)")
parser.add_argument("--no-download", action="store_true",
help="仅提交任务并返回 taskId,不等待结果")
parser.add_argument("--task-id", help="直接查询已有任务的结果 (跳过提交)")
args = parser.parse_args()
# ── Banner ──
banner = f"""{CYAN}{BOLD}
╔══════════════════════════════════════════╗
║ Qoder Video Generator (API) ║
║ AI 视频生成工具 · Seedance 2.0 ║
╚══════════════════════════════════════════╝{RESET}
"""
print(banner)
# ── API Key ──
api_key = get_api_key(cli_key=args.api_key)
if not api_key:
error("未找到 API Key,请设置环境变量 REDFOX_API_KEY 或使用 --api-key 参数")
print(f" 获取 Key: https://redfox.hk/settings/api-keys?source=github")
sys.exit(1)
# ── Session (all requests use HTTPS with SSL verification) ──
session = requests.Session()
session.verify = True
session.headers.update({
"Content-Type": "application/json",
"X-API-KEY": api_key,
})
# ── Mode: Query existing task ──
if args.task_id:
step(f"Querying task: {args.task_id}")
result = poll_video_result(session, args.task_id)
if not result:
sys.exit(1)
print()
video_url = result.get("videoUrl")
last_frame_url = result.get("lastFrameUrl")
duration = result.get("duration", "?")
resolution = result.get("resolution", "?")
ratio = result.get("ratio", "?")
usage = result.get("usage", {})
tokens = usage.get("totalTokens", "?") if usage else "?"
info(f"Video ready: {duration}s, {resolution}, {ratio}")
if last_frame_url:
info(f"Last frame: {last_frame_url}")
if usage:
info(f"Token usage: {tokens}")
if video_url:
output_dir = os.path.expanduser(args.output_dir) if args.output_dir else str(Path.home() / "Downloads" / "QoderVideos")
os.makedirs(output_dir, exist_ok=True)
filepath = download_video(session, video_url, output_dir, args.prefix, args.task_id)
if filepath:
size_mb = os.path.getsize(filepath) / (1024 * 1024)
print(f"\n{GREEN}{BOLD}✓ Done!{RESET}")
print(f" {filepath} ({size_mb:.1f} MB)")
else:
print(f"\n{RED}{BOLD}✗ Download failed{RESET}")
sys.exit(1)
else:
warn("Task succeeded but no videoUrl returned")
sys.exit(0)
# ── Mode: Submit new task ──
prompt = args.prompt.strip()
if not prompt:
error("提示词不能为空")
sys.exit(1)
# Validate image-url format
if args.image_url and not args.image_url.startswith("asset://"):
error(f"image-url 仅支持 asset:// 格式引用预置虚拟人像,当前值: {args.image_url}")
error("请参考火山方舟「素材&虚拟人像库」文档获取可用 ID")
sys.exit(1)
# Validate duration
if args.duration != -1 and (args.duration < 4 or args.duration > 15):
error(f"duration 取值范围: 4-15 或 -1(智能),当前值: {args.duration}")
sys.exit(1)
step(f"Prompt: {prompt[:100]}{'...' if len(prompt) > 100 else ''}")
step(f"Settings: {args.resolution}, {args.ratio}, {args.duration}s, seed={args.seed}, audio={'off' if args.no_audio else 'on'}")
if args.image_url:
step(f"Reference image: {args.image_url}")
# Build parameters
params = {
"resolution": args.resolution,
"ratio": args.ratio,
"duration": args.duration,
"seed": args.seed,
"watermark": args.watermark,
"generateAudio": not args.no_audio,
"returnLastFrame": args.return_last_frame,
}
# Submit
while True:
step("Submitting video generation task...")
task_id = submit_video_task(session, prompt, params, args.image_url)
if task_id:
break
if not confirm_retry():
sys.exit(1)
info(f"Task submitted: {task_id}")
if args.no_download:
print(f"\n{GREEN}{BOLD}✓ Task submitted successfully{RESET}")
print(f" taskId: {task_id}")
print(f" 查询命令: python3 videogen.py \"ignored\" --task-id {task_id}")
sys.exit(0)
# Poll for result
step("Waiting for video generation (may take several minutes)...")
result = poll_video_result(session, task_id)
if not result:
sys.exit(1)
print()
video_url = result.get("videoUrl")
last_frame_url = result.get("lastFrameUrl")
duration = result.get("duration", "?")
resolution = result.get("resolution", "?")
ratio = result.get("ratio", "?")
usage = result.get("usage", {})
tokens = usage.get("totalTokens", "?") if usage else "?"
info(f"Video ready: {duration}s, {resolution}, {ratio}")
if last_frame_url:
info(f"Last frame: {last_frame_url}")
if usage:
info(f"Token usage: {tokens}")
if video_url:
output_dir = os.path.expanduser(args.output_dir) if args.output_dir else str(Path.home() / "Downloads" / "QoderVideos")
os.makedirs(output_dir, exist_ok=True)
filepath = download_video(session, video_url, output_dir, args.prefix, task_id)
if filepath:
size_mb = os.path.getsize(filepath) / (1024 * 1024)
print(f"\n{GREEN}{BOLD}✓ Done!{RESET}")
print(f" {filepath} ({size_mb:.1f} MB)")
sys.exit(0)
else:
print(f"\n{RED}{BOLD}✗ Download failed{RESET}")
sys.exit(1)
else:
error("Task succeeded but no videoUrl returned")
sys.exit(1)
if __name__ == "__main__":
main()