Add Veo3 video generation node with audio support

- Create new Veo3VideoGenerationNode that extends VeoVideoGenerationNode
- Add generateAudio parameter for Veo3 models (veo-3.0-generate-001, veo-3.0-fast-generate-001)
- Fix Veo3 duration to 8 seconds only
- Update original node to clearly be Veo 2 only
- Regenerate API types to include new Veo endpoints
- Mark generated API types file in .gitattributes
This commit is contained in:
bymyself 2025-07-30 01:08:30 -07:00
parent ccd02afd16
commit 9ecb45550a
3 changed files with 2655 additions and 94 deletions

1
.gitattributes vendored
View File

@ -1,2 +1,3 @@
/web/assets/** linguist-generated /web/assets/** linguist-generated
/web/** linguist-vendored /web/** linguist-vendored
comfy_api_nodes/apis/__init__.py linguist-generated

File diff suppressed because it is too large Load Diff

View File

@ -8,10 +8,10 @@ from typing import Optional
from comfy.comfy_types.node_typing import IO, ComfyNodeABC from comfy.comfy_types.node_typing import IO, ComfyNodeABC
from comfy_api.input_impl.video_types import VideoFromFile from comfy_api.input_impl.video_types import VideoFromFile
from comfy_api_nodes.apis import ( from comfy_api_nodes.apis import (
Veo2GenVidRequest, VeoGenVidRequest,
Veo2GenVidResponse, VeoGenVidResponse,
Veo2GenVidPollRequest, VeoGenVidPollRequest,
Veo2GenVidPollResponse VeoGenVidPollResponse
) )
from comfy_api_nodes.apis.client import ( from comfy_api_nodes.apis.client import (
ApiEndpoint, ApiEndpoint,
@ -35,7 +35,7 @@ def convert_image_to_base64(image: torch.Tensor):
return tensor_to_base64_string(scaled_image) return tensor_to_base64_string(scaled_image)
def get_video_url_from_response(poll_response: Veo2GenVidPollResponse) -> Optional[str]: def get_video_url_from_response(poll_response: VeoGenVidPollResponse) -> Optional[str]:
if ( if (
poll_response.response poll_response.response
and hasattr(poll_response.response, "videos") and hasattr(poll_response.response, "videos")
@ -52,11 +52,10 @@ def get_video_url_from_response(poll_response: Veo2GenVidPollResponse) -> Option
class VeoVideoGenerationNode(ComfyNodeABC): class VeoVideoGenerationNode(ComfyNodeABC):
""" """
Generates videos from text prompts using Google's Veo API. Generates videos from text prompts using Google's Veo 2 API.
Supported models: Supported models:
- veo-2.0-generate-001 - veo-2.0-generate-001
- veo-3.0-generate-preview
This node can create videos from text descriptions and optional image inputs, This node can create videos from text descriptions and optional image inputs,
with control over parameters like aspect ratio, duration, and more. with control over parameters like aspect ratio, duration, and more.
@ -137,9 +136,9 @@ class VeoVideoGenerationNode(ComfyNodeABC):
"model": ( "model": (
IO.COMBO, IO.COMBO,
{ {
"options": ["veo-2.0-generate-001", "veo-3.0-generate-preview"], "options": ["veo-2.0-generate-001"],
"default": "veo-2.0-generate-001", "default": "veo-2.0-generate-001",
"tooltip": "Model to use for video generation. Defaults to veo 2.0", "tooltip": "Veo 2 model to use for video generation",
}, },
), ),
}, },
@ -153,7 +152,7 @@ class VeoVideoGenerationNode(ComfyNodeABC):
RETURN_TYPES = (IO.VIDEO,) RETURN_TYPES = (IO.VIDEO,)
FUNCTION = "generate_video" FUNCTION = "generate_video"
CATEGORY = "api node/video/Veo" CATEGORY = "api node/video/Veo"
DESCRIPTION = "Generates videos from text prompts using Google's Veo API" DESCRIPTION = "Generates videos from text prompts using Google's Veo 2 API"
API_NODE = True API_NODE = True
def generate_video( def generate_video(
@ -167,6 +166,7 @@ class VeoVideoGenerationNode(ComfyNodeABC):
seed=0, seed=0,
image=None, image=None,
model="veo-2.0-generate-001", model="veo-2.0-generate-001",
generate_audio=False,
unique_id: Optional[str] = None, unique_id: Optional[str] = None,
**kwargs, **kwargs,
): ):
@ -201,16 +201,19 @@ class VeoVideoGenerationNode(ComfyNodeABC):
parameters["negativePrompt"] = negative_prompt parameters["negativePrompt"] = negative_prompt
if seed > 0: if seed > 0:
parameters["seed"] = seed parameters["seed"] = seed
# Only add generateAudio for Veo 3 models
if "veo-3.0" in model:
parameters["generateAudio"] = generate_audio
# Initial request to start video generation # Initial request to start video generation
initial_operation = SynchronousOperation( initial_operation = SynchronousOperation(
endpoint=ApiEndpoint( endpoint=ApiEndpoint(
path=f"/proxy/veo/{model}/generate", path=f"/proxy/veo/{model}/generate",
method=HttpMethod.POST, method=HttpMethod.POST,
request_model=Veo2GenVidRequest, request_model=VeoGenVidRequest,
response_model=Veo2GenVidResponse response_model=VeoGenVidResponse
), ),
request=Veo2GenVidRequest( request=VeoGenVidRequest(
instances=instances, instances=instances,
parameters=parameters parameters=parameters
), ),
@ -238,14 +241,14 @@ class VeoVideoGenerationNode(ComfyNodeABC):
poll_endpoint=ApiEndpoint( poll_endpoint=ApiEndpoint(
path=f"/proxy/veo/{model}/poll", path=f"/proxy/veo/{model}/poll",
method=HttpMethod.POST, method=HttpMethod.POST,
request_model=Veo2GenVidPollRequest, request_model=VeoGenVidPollRequest,
response_model=Veo2GenVidPollResponse response_model=VeoGenVidPollResponse
), ),
completed_statuses=["completed"], completed_statuses=["completed"],
failed_statuses=[], # No failed statuses, we'll handle errors after polling failed_statuses=[], # No failed statuses, we'll handle errors after polling
status_extractor=status_extractor, status_extractor=status_extractor,
progress_extractor=progress_extractor, progress_extractor=progress_extractor,
request=Veo2GenVidPollRequest( request=VeoGenVidPollRequest(
operationName=operation_name operationName=operation_name
), ),
auth_kwargs=kwargs, auth_kwargs=kwargs,
@ -311,11 +314,64 @@ class VeoVideoGenerationNode(ComfyNodeABC):
return (VideoFromFile(video_io),) return (VideoFromFile(video_io),)
# Register the node class Veo3VideoGenerationNode(VeoVideoGenerationNode):
"""
Generates videos from text prompts using Google's Veo 3 API.
Supported models:
- veo-3.0-generate-001
- veo-3.0-fast-generate-001
This node extends the base Veo node with Veo 3 specific features including
audio generation and fixed 8-second duration.
"""
@classmethod
def INPUT_TYPES(s):
parent_input = super().INPUT_TYPES()
# Update model options for Veo 3
parent_input["optional"]["model"] = (
IO.COMBO,
{
"options": ["veo-3.0-generate-001", "veo-3.0-fast-generate-001"],
"default": "veo-3.0-generate-001",
"tooltip": "Veo 3 model to use for video generation",
},
)
# Add generateAudio parameter
parent_input["optional"]["generate_audio"] = (
IO.BOOLEAN,
{
"default": False,
"tooltip": "Generate audio for the video. Supported by all Veo 3 models.",
}
)
# Update duration constraints for Veo 3 (only 8 seconds supported)
parent_input["optional"]["duration_seconds"] = (
IO.INT,
{
"default": 8,
"min": 8,
"max": 8,
"step": 1,
"display": "number",
"tooltip": "Duration of the output video in seconds (Veo 3 only supports 8 seconds)",
},
)
return parent_input
# Register the nodes
NODE_CLASS_MAPPINGS = { NODE_CLASS_MAPPINGS = {
"VeoVideoGenerationNode": VeoVideoGenerationNode, "VeoVideoGenerationNode": VeoVideoGenerationNode,
"Veo3VideoGenerationNode": Veo3VideoGenerationNode,
} }
NODE_DISPLAY_NAME_MAPPINGS = { NODE_DISPLAY_NAME_MAPPINGS = {
"VeoVideoGenerationNode": "Google Veo Video Generation", "VeoVideoGenerationNode": "Google Veo 2 Video Generation",
"Veo3VideoGenerationNode": "Google Veo 3 Video Generation",
} }