Files
ucloud-comfyui-plugin/py/vidu_t2v.py
T
jason.mei bd7d00b1ce feat(modelverse): add Gemini Pro Image and Vidu video generation support
- Add Gemini 3 Pro Image Preview (Nano Banana Pro) ComfyUI node with high-resolution output (1K, 2K, 4K)
- Add GeminiProImageRequest builder supporting aspect ratio control, Google Search grounding, and up to 14 reference images
- Add Vidu video generation nodes: t2v, i2v, ref2v, and startend2v for comprehensive video synthesis
- Add Vidu extend node for video continuation and frame extension
- Update .gitignore to exclude .ace-tool/ and docs/ directories
- Enable professional asset production with advanced image and video generation capabilities
2025-12-29 16:03:39 +08:00

123 lines
4.2 KiB
Python

"""
Vidu Text2Video - 文生视频模型
Model: viduq2
"""
import time
import requests
import io
from .modelverse_api.client import ModelverseClient
from comfy.comfy_types.node_typing import IO
try:
from comfy_extras.nodes_video import VideoFromFile
except ImportError:
try:
from comfy.model_management import VideoFromFile
except ImportError:
class VideoFromFile:
def __init__(self, video_io):
self.video_io = video_io
self.video_data = video_io.getvalue() if hasattr(video_io, 'getvalue') else video_io
ASPECT_RATIOS = ["16:9", "9:16", "3:4", "4:3", "1:1"]
RESOLUTIONS = ["540p", "720p", "1080p"]
class ViduText2VideoNode:
"""
Vidu Text2Video - 文生视频
Model: viduq2
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"client": ("MODELVERSE_API_CLIENT",),
"prompt": (IO.STRING, {"multiline": True, "default": "A beautiful sunset over the ocean", "tooltip": "文本提示词,最长2000字符"}),
"duration": (IO.INT, {"default": 5, "min": 1, "max": 10, "step": 1, "tooltip": "视频时长(秒)"}),
"aspect_ratio": (ASPECT_RATIOS, {"default": "16:9", "tooltip": "长宽比"}),
"resolution": (RESOLUTIONS, {"default": "720p", "tooltip": "分辨率"}),
},
"optional": {
"seed": (IO.INT, {"default": 0, "min": 0, "max": 2147483647, "tooltip": "随机种子,0表示随机"}),
"guidance_scale": (IO.FLOAT, {"default": 7.5, "min": 1.0, "max": 20.0, "step": 0.5, "tooltip": "引导系数"}),
"bgm": (IO.BOOLEAN, {"default": False, "tooltip": "是否添加背景音乐"}),
}
}
RETURN_TYPES = (IO.VIDEO,)
RETURN_NAMES = ("video",)
FUNCTION = "generate"
CATEGORY = "UCLOUD_MODELVERSE/Vidu"
def generate(self, client, prompt, duration, aspect_ratio, resolution, seed=0, guidance_scale=7.5, bgm=False):
api_key = client.get("api_key")
if not api_key:
raise ValueError("API key is not set")
mv_client = ModelverseClient(api_key)
task_input = {"prompt": prompt}
parameters = {
"vidu_type": "text2video",
"duration": duration,
"aspect_ratio": aspect_ratio,
"resolution": resolution,
"seed": seed,
"guidance_scale": guidance_scale,
"bgm": bgm,
}
# Submit task
submit_res = mv_client.submit_task("viduq2", task_input, parameters)
task_id = submit_res.get("output", {}).get("task_id")
if not task_id:
raise Exception(f"Failed to submit task: {submit_res}")
print(f"Vidu T2V task submitted: {task_id}")
# Poll for result
video_url = self._poll_task(mv_client, task_id)
# Download and return video
return self._download_video(video_url)
def _poll_task(self, mv_client, task_id, max_retries=180):
for i in range(max_retries):
status_res = mv_client.get_task_status(task_id)
task_status = status_res.get("output", {}).get("task_status")
if task_status == "Success":
urls = status_res.get("output", {}).get("urls", [])
if urls:
return urls[0]
raise Exception("Task succeeded but no video URL returned")
elif task_status == "Failure":
error = status_res.get("output", {}).get("error_message", "Unknown error")
raise Exception(f"Task failed: {error}")
elif task_status in ["Pending", "Running"]:
print(f"Task {task_id}: {task_status} ({i+1}/{max_retries})")
time.sleep(5)
else:
raise Exception(f"Unknown status: {task_status}")
raise Exception("Task timed out")
def _download_video(self, video_url):
response = requests.get(video_url, timeout=120)
response.raise_for_status()
video_io = io.BytesIO(response.content)
video_io.seek(0)
return (VideoFromFile(video_io),)
NODE_CLASS_MAPPINGS = {
"Vidu_Text2Video": ViduText2VideoNode,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"Vidu_Text2Video": "Vidu Text2Video",
}