seedance_video_test.py
#!/usr/bin/env python3
"""
Seedance 2.0 视频生成接口测试脚本
支持所有官方参数:content、resolution、ratio、duration、generate_audio、
watermk、return_last_frame、seed、callback_url、safety_identifier
"""
import requests
import json
import time
import sys
import urllib3
# 禁用 SSL 警告(本地自签名证书)
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
# ==================== 基础配置 ====================
BASE_URL = "https://mjlm.coral-gpu.com:3000/v1"
API_KEY = "sk-your-api-key" # 测试用
MODEL = "doubao-seedance-2-0-260128"
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}
def create_task(content, resolution="720p", ratio="adaptive", duration=5,
generate_audio=False, watermark=False, return_last_frame=False,
seed=None, callback_url=None, safety_identifier=None):
"""
创建视频生成任务
Args:
content: 内容数组,每项包含 type 和对应字段
resolution: 分辨率 480p/720p/1080p
ratio: 宽高比 16:9/9:16/1:1/4:3/3:4/21:9/adaptive
duration: 视频时长(秒),4-15整数,-1为智能时长
generate_audio: 是否生成音频
watermark: 是否加水印
return_last_frame: 是否返回尾帧图片
seed: 随机种子,用于固定生成结果
callback_url: 任务状态回调地址
safety_identifier: 终端用户唯一标识
Returns:
dict: 包含任务ID的响应
"""
url = f"{BASE_URL}/contents/generations/tasks"
payload = {
"model": MODEL,
"content": content,
"resolution": resolution,
"ratio": ratio,
"duration": duration,
"generate_audio": generate_audio,
"watermark": watermark,
"return_last_frame": return_last_frame
}
# 可选参数
if seed is not None:
payload["seed"] = seed
if callback_url:
payload["callback_url"] = callback_url
if safety_identifier:
payload["safety_identifier"] = safety_identifier
print(f"[创建任务] 发送请求...")
print(f" 参数: resolution={resolution}, ratio={ratio}, duration={duration}")
print(f" generate_audio={generate_audio}, watermark={watermark}, return_last_frame={return_last_frame}")
if seed is not None:
print(f" seed={seed}")
response = requests.post(url, headers=HEADERS, json=payload, timeout=60, verify=False)
if response.status_code == 200:
result = response.json()
print(f"[创建任务] 成功! 任务ID: {result.get('id')}")
return result
else:
print(f"[创建任务] 失败! 状态码: {response.status_code}")
print(f" 响应: {response.text}")
return None
def query_task(task_id):
"""
查询视频生成任务状态
Args:
task_id: 任务ID
Returns:
dict: 任务状态信息
"""
url = f"{BASE_URL}/contents/generations/tasks/{task_id}"
response = requests.get(url, headers=HEADERS, timeout=30, verify=False)
if response.status_code == 200:
return response.json()
else:
print(f"[查询任务] 失败! 状态码: {response.status_code}")
print(f" 响应: {response.text}")
return None
def full_flow(content, resolution="720p", ratio="adaptive", duration=5,
generate_audio=False, watermark=False, return_last_frame=False,
seed=None, poll_interval=5, max_wait=300):
"""
完整流程:创建任务 + 轮询直到完成
Args:
content: 内容数组
resolution: 分辨率
ratio: 宽高比
duration: 时长
generate_audio: 是否生成音频
watermark: 是否加水印
return_last_frame: 是否返回尾帧
seed: 随机种子
poll_interval: 轮询间隔(秒)
max_wait: 最大等待时间(秒)
Returns:
dict: 最终任务状态
"""
# 创建任务
result = create_task(content, resolution, ratio, duration,
generate_audio, watermark, return_last_frame, seed)
if not result:
return None
task_id = result.get("id")
print(f"
[轮询] 开始轮询任务状态,间隔 {poll_interval} 秒...")
start_time = time.time()
while True:
elapsed = time.time() - start_time
if elapsed > max_wait:
print(f"[轮询] 超时! 已等待 {max_wait} 秒")
return None
time.sleep(poll_interval)
status_result = query_task(task_id)
if not status_result:
continue
status = status_result.get("status", "unknown")
print(f"[轮询] {elapsed:.0f}秒 - 状态: {status}")
if status == "succeeded":
print(f"
[完成] 视频生成成功!")
print(f" 任务ID: {task_id}")
# 输出视频URL
video_url = None
if "output" in status_result:
video_url = status_result["output"].get("video_url")
elif "data" in status_result and "output" in status_result["data"]:
video_url = status_result["data"]["output"].get("video_url")
if video_url:
print(f" 视频URL: {video_url}")
# 输出尾帧图片
if return_last_frame:
last_frame = None
if "output" in status_result:
last_frame = status_result["output"].get("last_frame_url")
elif "data" in status_result and "output" in status_result["data"]:
last_frame = status_result["data"]["output"].get("last_frame_url")
if last_frame:
print(f" 尾帧URL: {last_frame}")
return status_result
elif status in ["failed", "expired"]:
print(f"
[失败] 任务失败!")
print(f" 状态: {status}")
error = status_result.get("error", {})
if error:
print(f" 错误: {error}")
return status_result
# ==================== Demo 示例 ====================
def demo_text_to_video():
"""Demo 1: 文生视频 - 基础文本提示词"""
print("
" + "="*60)
print("Demo 1: 文生视频")
print("="*60)
content = [
{"type": "text", "text": "一只可爱的橘猫在阳光明媚的草地上奔跑玩耍,毛发随风飘动"}
]
return full_flow(content, resolution="720p", duration=5)
def demo_image_to_video():
"""Demo 2: 图生视频 - 参考图片生成视频"""
print("
" + "="*60)
print("Demo 2: 图生视频")
print("="*60)
content = [
{"type": "text", "text": "让画面中的人物微笑并转身"},
{
"type": "image_url",
"image_url": {"url": "https://example.com/reference.jpg"},
"role": "reference_image"
}
]
return full_flow(content, resolution="720p", duration=5)
def demo_high_resolution():
"""Demo 3: 高清 1080p 视频"""
print("
" + "="*60)
print("Demo 3: 高清 1080p")
print("="*60)
content = [
{"type": "text", "text": "夕阳下的城市天际线,金色阳光洒在摩天大楼上"}
]
return full_flow(content, resolution="1080p", duration=8)
def demo_vertical_video():
"""Demo 4: 竖屏 9:16 视频"""
print("
" + "="*60)
print("Demo 4: 竖屏 9:16")
print("="*60)
content = [
{"type": "text", "text": "一个女孩在花园里跳舞,裙摆随风飘扬"}
]
return full_flow(content, resolution="720p", ratio="9:16", duration=6)
def demo_with_audio():
"""Demo 5: 生成带音频的视频"""
print("
" + "="*60)
print("Demo 5: 带音频视频")
print("="*60)
content = [
{"type": "text", "text": "海边日落,海浪轻轻拍打沙滩,海鸥飞翔"}
]
return full_flow(content, resolution="720p", duration=8, generate_audio=True)
def demo_with_watermark():
"""Demo 6: 带水印视频"""
print("
" + "="*60)
print("Demo 6: 带水印视频")
print("="*60)
content = [
{"type": "text", "text": "雪山脚下的小木屋,炊烟袅袅升起"}
]
return full_flow(content, resolution="720p", duration=5, watermark=True)
def demo_return_last_frame():
"""Demo 7: 返回尾帧图片"""
print("
" + "="*60)
print("Demo 7: 返回尾帧图片")
print("="*60)
content = [
{"type": "text", "text": "一朵花从花苞到盛开的过程"}
]
return full_flow(content, resolution="720p", duration=5, return_last_frame=True)
def demo_fixed_seed():
"""Demo 8: 固定随机种子 - 可复现结果"""
print("
" + "="*60)
print("Demo 8: 固定随机种子")
print("="*60)
content = [
{"type": "text", "text": "科幻风格的太空站,地球在背景中"}
]
return full_flow(content, resolution="720p", duration=5, seed=42)
def demo_smart_duration():
"""Demo 9: 智能时长 - 让模型自动选择"""
print("
" + "="*60)
print("Demo 9: 智能时长")
print("="*60)
content = [
{"type": "text", "text": "制作一个简短的产品展示动画"}
]
return full_flow(content, resolution="720p", duration=-1)
def demo_custom_duration():
"""Demo 10: 自定义时长 10秒"""
print("
" + "="*60)
print("Demo 10: 自定义时长 10秒")
print("="*60)
content = [
{"type": "text", "text": "一段延时摄影:云层在山峰上空流动"}
]
return full_flow(content, resolution="720p", duration=10)
def demo_with_audio_reference():
"""Demo 11: 带音频参考"""
print("
" + "="*60)
print("Demo 11: 带音频参考")
print("="*60)
content = [
{"type": "text", "text": "根据音频生成匹配的舞蹈视频"},
{
"type": "audio_url",
"audio_url": {"url": "https://example.com/music.mp3"},
"role": "reference_audio"
}
]
return full_flow(content, resolution="720p", duration=8)
def demo_square_format():
"""Demo 12: 正方形 1:1 格式"""
print("
" + "="*60)
print("Demo 12: 正方形 1:1")
print("="*60)
content = [
{"type": "text", "text": "一杯咖啡在桌上,蒸汽缓缓升起"}
]
return full_flow(content, resolution="720p", ratio="1:1", duration=5)
# ==================== 主程序 ====================
def main():
"""主函数 - 运行指定的 demo 或全部"""
demos = {
"text": demo_text_to_video,
"image": demo_image_to_video,
"hd": demo_high_resolution,
"vertical": demo_vertical_video,
"audio": demo_with_audio,
"watermark": demo_with_watermark,
"lastframe": demo_return_last_frame,
"seed": demo_fixed_seed,
"smart": demo_smart_duration,
"custom": demo_custom_duration,
"audioref": demo_with_audio_reference,
"square": demo_square_format,
}
if len(sys.argv) > 1:
demo_name = sys.argv[1].lower()
if demo_name in demos:
demos[demo_name]()
elif demo_name == "all":
for name, func in demos.items():
try:
func()
except Exception as e:
print(f"
[错误] Demo {name} 失败: {e}")
else:
print(f"未知的 demo: {demo_name}")
print(f"可用的 demo: {', '.join(demos.keys())}")
print("使用 'all' 运行所有 demo")
else:
print("Seedance 2.0 视频生成接口测试脚本")
print("="*60)
print("
使用方法:")
print(" python seedance_video_test.py <demo_name>")
print(" python seedance_video_test.py all")
print(f"
可用的 demo:")
for name, func in demos.items():
print(f" {name:12} - {func.__doc__.strip()}")
print(f"
示例:")
print(" python seedance_video_test.py text")
print(" python seedance_video_test.py all")
if __name__ == "__main__":
main()