api-nodes: fixed dynamic pricing format; import comfy_io directly (#10336)

This commit is contained in:
Alexander Piskun
2025-10-14 09:55:56 +03:00
committed by GitHub
parent 84867067ea
commit 7a883849ea
19 changed files with 1331 additions and 1322 deletions

View File

@@ -76,7 +76,7 @@ from comfy_api_nodes.util.validation_utils import (
from comfy_api.input_impl import VideoFromFile
from comfy_api.input.basic_types import AudioInput
from comfy_api.input.video_types import VideoInput
from comfy_api.latest import ComfyExtension, io as comfy_io
from comfy_api.latest import ComfyExtension, IO
KLING_API_VERSION = "v1"
PATH_TEXT_TO_VIDEO = f"/proxy/kling/{KLING_API_VERSION}/videos/text2video"
@@ -387,7 +387,7 @@ async def execute_text2video(
duration: str,
aspect_ratio: str,
camera_control: Optional[KlingCameraControl] = None,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
validate_prompts(prompt, negative_prompt, MAX_PROMPT_LENGTH_T2V)
initial_operation = SynchronousOperation(
endpoint=ApiEndpoint(
@@ -428,7 +428,7 @@ async def execute_text2video(
validate_video_result_response(final_response)
video = get_video_from_response(final_response)
return comfy_io.NodeOutput(await download_url_to_video_output(str(video.url)), str(video.id), str(video.duration))
return IO.NodeOutput(await download_url_to_video_output(str(video.url)), str(video.id), str(video.duration))
async def execute_image2video(
@@ -444,7 +444,7 @@ async def execute_image2video(
duration: str,
camera_control: Optional[KlingCameraControl] = None,
end_frame: Optional[torch.Tensor] = None,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
validate_prompts(prompt, negative_prompt, MAX_PROMPT_LENGTH_I2V)
validate_input_image(start_frame)
@@ -499,7 +499,7 @@ async def execute_image2video(
validate_video_result_response(final_response)
video = get_video_from_response(final_response)
return comfy_io.NodeOutput(await download_url_to_video_output(str(video.url)), str(video.id), str(video.duration))
return IO.NodeOutput(await download_url_to_video_output(str(video.url)), str(video.id), str(video.duration))
async def execute_video_effect(
@@ -576,7 +576,7 @@ async def execute_lipsync(
text: Optional[str] = None,
voice_speed: Optional[float] = None,
voice_id: Optional[str] = None,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
if text:
validate_string(text, field_name="Text", max_length=MAX_PROMPT_LENGTH_LIP_SYNC)
validate_video_dimensions(video, 720, 1920)
@@ -634,77 +634,77 @@ async def execute_lipsync(
validate_video_result_response(final_response)
video = get_video_from_response(final_response)
return comfy_io.NodeOutput(await download_url_to_video_output(str(video.url)), str(video.id), str(video.duration))
return IO.NodeOutput(await download_url_to_video_output(str(video.url)), str(video.id), str(video.duration))
class KlingCameraControls(comfy_io.ComfyNode):
class KlingCameraControls(IO.ComfyNode):
"""Kling Camera Controls Node"""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingCameraControls",
display_name="Kling Camera Controls",
category="api node/video/Kling",
description="Allows specifying configuration options for Kling Camera Controls and motion control effects.",
inputs=[
comfy_io.Combo.Input("camera_control_type", options=KlingCameraControlType),
comfy_io.Float.Input(
IO.Combo.Input("camera_control_type", options=KlingCameraControlType),
IO.Float.Input(
"horizontal_movement",
default=0.0,
min=-10.0,
max=10.0,
step=0.25,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Controls camera's movement along horizontal axis (x-axis). Negative indicates left, positive indicates right",
),
comfy_io.Float.Input(
IO.Float.Input(
"vertical_movement",
default=0.0,
min=-10.0,
max=10.0,
step=0.25,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Controls camera's movement along vertical axis (y-axis). Negative indicates downward, positive indicates upward.",
),
comfy_io.Float.Input(
IO.Float.Input(
"pan",
default=0.5,
min=-10.0,
max=10.0,
step=0.25,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Controls camera's rotation in vertical plane (x-axis). Negative indicates downward rotation, positive indicates upward rotation.",
),
comfy_io.Float.Input(
IO.Float.Input(
"tilt",
default=0.0,
min=-10.0,
max=10.0,
step=0.25,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Controls camera's rotation in horizontal plane (y-axis). Negative indicates left rotation, positive indicates right rotation.",
),
comfy_io.Float.Input(
IO.Float.Input(
"roll",
default=0.0,
min=-10.0,
max=10.0,
step=0.25,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Controls camera's rolling amount (z-axis). Negative indicates counterclockwise, positive indicates clockwise.",
),
comfy_io.Float.Input(
IO.Float.Input(
"zoom",
default=0.0,
min=-10.0,
max=10.0,
step=0.25,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Controls change in camera's focal length. Negative indicates narrower field of view, positive indicates wider field of view.",
),
],
outputs=[comfy_io.Custom("CAMERA_CONTROL").Output(display_name="camera_control")],
outputs=[IO.Custom("CAMERA_CONTROL").Output(display_name="camera_control")],
)
@classmethod
@@ -740,8 +740,8 @@ class KlingCameraControls(comfy_io.ComfyNode):
tilt: float,
roll: float,
zoom: float,
) -> comfy_io.NodeOutput:
return comfy_io.NodeOutput(
) -> IO.NodeOutput:
return IO.NodeOutput(
KlingCameraControl(
type=KlingCameraControlType(camera_control_type),
config=KlingCameraConfig(
@@ -756,27 +756,27 @@ class KlingCameraControls(comfy_io.ComfyNode):
)
class KlingTextToVideoNode(comfy_io.ComfyNode):
class KlingTextToVideoNode(IO.ComfyNode):
"""Kling Text to Video Node"""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
def define_schema(cls) -> IO.Schema:
modes = list(MODE_TEXT2VIDEO.keys())
return comfy_io.Schema(
return IO.Schema(
node_id="KlingTextToVideoNode",
display_name="Kling Text to Video",
category="api node/video/Kling",
description="Kling Text to Video Node",
inputs=[
comfy_io.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
comfy_io.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
comfy_io.Float.Input("cfg_scale", default=1.0, min=0.0, max=1.0),
comfy_io.Combo.Input(
IO.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
IO.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
IO.Float.Input("cfg_scale", default=1.0, min=0.0, max=1.0),
IO.Combo.Input(
"aspect_ratio",
options=KlingVideoGenAspectRatio,
default="16:9",
),
comfy_io.Combo.Input(
IO.Combo.Input(
"mode",
options=modes,
default=modes[4],
@@ -784,14 +784,14 @@ class KlingTextToVideoNode(comfy_io.ComfyNode):
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -804,7 +804,7 @@ class KlingTextToVideoNode(comfy_io.ComfyNode):
cfg_scale: float,
mode: str,
aspect_ratio: str,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
model_mode, duration, model_name = MODE_TEXT2VIDEO[mode]
return await execute_text2video(
auth_kwargs={
@@ -822,42 +822,42 @@ class KlingTextToVideoNode(comfy_io.ComfyNode):
)
class KlingCameraControlT2VNode(comfy_io.ComfyNode):
class KlingCameraControlT2VNode(IO.ComfyNode):
"""
Kling Text to Video Camera Control Node. This node is a text to video node, but it supports controlling the camera.
Duration, mode, and model_name request fields are hard-coded because camera control is only supported in pro mode with the kling-v1-5 model at 5s duration as of 2025-05-02.
"""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingCameraControlT2VNode",
display_name="Kling Text to Video (Camera Control)",
category="api node/video/Kling",
description="Transform text into cinematic videos with professional camera movements that simulate real-world cinematography. Control virtual camera actions including zoom, rotation, pan, tilt, and first-person view, while maintaining focus on your original text.",
inputs=[
comfy_io.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
comfy_io.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
comfy_io.Float.Input("cfg_scale", default=0.75, min=0.0, max=1.0),
comfy_io.Combo.Input(
IO.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
IO.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
IO.Float.Input("cfg_scale", default=0.75, min=0.0, max=1.0),
IO.Combo.Input(
"aspect_ratio",
options=KlingVideoGenAspectRatio,
default="16:9",
),
comfy_io.Custom("CAMERA_CONTROL").Input(
IO.Custom("CAMERA_CONTROL").Input(
"camera_control",
tooltip="Can be created using the Kling Camera Controls node. Controls the camera movement and motion during the video generation.",
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -870,7 +870,7 @@ class KlingCameraControlT2VNode(comfy_io.ComfyNode):
cfg_scale: float,
aspect_ratio: str,
camera_control: Optional[KlingCameraControl] = None,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
return await execute_text2video(
auth_kwargs={
"auth_token": cls.hidden.auth_token_comfy_org,
@@ -888,43 +888,43 @@ class KlingCameraControlT2VNode(comfy_io.ComfyNode):
)
class KlingImage2VideoNode(comfy_io.ComfyNode):
class KlingImage2VideoNode(IO.ComfyNode):
"""Kling Image to Video Node"""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingImage2VideoNode",
display_name="Kling Image to Video",
category="api node/video/Kling",
description="Kling Image to Video Node",
inputs=[
comfy_io.Image.Input("start_frame", tooltip="The reference image used to generate the video."),
comfy_io.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
comfy_io.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
comfy_io.Combo.Input(
IO.Image.Input("start_frame", tooltip="The reference image used to generate the video."),
IO.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
IO.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
IO.Combo.Input(
"model_name",
options=KlingVideoGenModelName,
default="kling-v2-master",
),
comfy_io.Float.Input("cfg_scale", default=0.8, min=0.0, max=1.0),
comfy_io.Combo.Input("mode", options=KlingVideoGenMode, default=KlingVideoGenMode.std),
comfy_io.Combo.Input(
IO.Float.Input("cfg_scale", default=0.8, min=0.0, max=1.0),
IO.Combo.Input("mode", options=KlingVideoGenMode, default=KlingVideoGenMode.std),
IO.Combo.Input(
"aspect_ratio",
options=KlingVideoGenAspectRatio,
default=KlingVideoGenAspectRatio.field_16_9,
),
comfy_io.Combo.Input("duration", options=KlingVideoGenDuration, default=KlingVideoGenDuration.field_5),
IO.Combo.Input("duration", options=KlingVideoGenDuration, default=KlingVideoGenDuration.field_5),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -942,7 +942,7 @@ class KlingImage2VideoNode(comfy_io.ComfyNode):
duration: str,
camera_control: Optional[KlingCameraControl] = None,
end_frame: Optional[torch.Tensor] = None,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
return await execute_image2video(
auth_kwargs={
"auth_token": cls.hidden.auth_token_comfy_org,
@@ -962,46 +962,46 @@ class KlingImage2VideoNode(comfy_io.ComfyNode):
)
class KlingCameraControlI2VNode(comfy_io.ComfyNode):
class KlingCameraControlI2VNode(IO.ComfyNode):
"""
Kling Image to Video Camera Control Node. This node is a image to video node, but it supports controlling the camera.
Duration, mode, and model_name request fields are hard-coded because camera control is only supported in pro mode with the kling-v1-5 model at 5s duration as of 2025-05-02.
"""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingCameraControlI2VNode",
display_name="Kling Image to Video (Camera Control)",
category="api node/video/Kling",
description="Transform still images into cinematic videos with professional camera movements that simulate real-world cinematography. Control virtual camera actions including zoom, rotation, pan, tilt, and first-person view, while maintaining focus on your original image.",
inputs=[
comfy_io.Image.Input(
IO.Image.Input(
"start_frame",
tooltip="Reference Image - URL or Base64 encoded string, cannot exceed 10MB, resolution not less than 300*300px, aspect ratio between 1:2.5 ~ 2.5:1. Base64 should not include data:image prefix.",
),
comfy_io.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
comfy_io.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
comfy_io.Float.Input("cfg_scale", default=0.75, min=0.0, max=1.0),
comfy_io.Combo.Input(
IO.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
IO.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
IO.Float.Input("cfg_scale", default=0.75, min=0.0, max=1.0),
IO.Combo.Input(
"aspect_ratio",
options=KlingVideoGenAspectRatio,
default=KlingVideoGenAspectRatio.field_16_9,
),
comfy_io.Custom("CAMERA_CONTROL").Input(
IO.Custom("CAMERA_CONTROL").Input(
"camera_control",
tooltip="Can be created using the Kling Camera Controls node. Controls the camera movement and motion during the video generation.",
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1015,7 +1015,7 @@ class KlingCameraControlI2VNode(comfy_io.ComfyNode):
cfg_scale: float,
aspect_ratio: str,
camera_control: KlingCameraControl,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
return await execute_image2video(
auth_kwargs={
"auth_token": cls.hidden.auth_token_comfy_org,
@@ -1034,37 +1034,37 @@ class KlingCameraControlI2VNode(comfy_io.ComfyNode):
)
class KlingStartEndFrameNode(comfy_io.ComfyNode):
class KlingStartEndFrameNode(IO.ComfyNode):
"""
Kling First Last Frame Node. This node allows creation of a video from a first and last frame. It calls the normal image to video endpoint, but only allows the subset of input options that support the `image_tail` request field.
"""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
def define_schema(cls) -> IO.Schema:
modes = list(MODE_START_END_FRAME.keys())
return comfy_io.Schema(
return IO.Schema(
node_id="KlingStartEndFrameNode",
display_name="Kling Start-End Frame to Video",
category="api node/video/Kling",
description="Generate a video sequence that transitions between your provided start and end images. The node creates all frames in between, producing a smooth transformation from the first frame to the last.",
inputs=[
comfy_io.Image.Input(
IO.Image.Input(
"start_frame",
tooltip="Reference Image - URL or Base64 encoded string, cannot exceed 10MB, resolution not less than 300*300px, aspect ratio between 1:2.5 ~ 2.5:1. Base64 should not include data:image prefix.",
),
comfy_io.Image.Input(
IO.Image.Input(
"end_frame",
tooltip="Reference Image - End frame control. URL or Base64 encoded string, cannot exceed 10MB, resolution not less than 300*300px. Base64 should not include data:image prefix.",
),
comfy_io.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
comfy_io.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
comfy_io.Float.Input("cfg_scale", default=0.5, min=0.0, max=1.0),
comfy_io.Combo.Input(
IO.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
IO.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
IO.Float.Input("cfg_scale", default=0.5, min=0.0, max=1.0),
IO.Combo.Input(
"aspect_ratio",
options=[i.value for i in KlingVideoGenAspectRatio],
default="16:9",
),
comfy_io.Combo.Input(
IO.Combo.Input(
"mode",
options=modes,
default=modes[2],
@@ -1072,14 +1072,14 @@ class KlingStartEndFrameNode(comfy_io.ComfyNode):
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1094,7 +1094,7 @@ class KlingStartEndFrameNode(comfy_io.ComfyNode):
cfg_scale: float,
aspect_ratio: str,
mode: str,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
mode, duration, model_name = MODE_START_END_FRAME[mode]
return await execute_image2video(
auth_kwargs={
@@ -1114,41 +1114,41 @@ class KlingStartEndFrameNode(comfy_io.ComfyNode):
)
class KlingVideoExtendNode(comfy_io.ComfyNode):
class KlingVideoExtendNode(IO.ComfyNode):
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingVideoExtendNode",
display_name="Kling Video Extend",
category="api node/video/Kling",
description="Kling Video Extend Node. Extend videos made by other Kling nodes. The video_id is created by using other Kling Nodes.",
inputs=[
comfy_io.String.Input(
IO.String.Input(
"prompt",
multiline=True,
tooltip="Positive text prompt for guiding the video extension",
),
comfy_io.String.Input(
IO.String.Input(
"negative_prompt",
multiline=True,
tooltip="Negative text prompt for elements to avoid in the extended video",
),
comfy_io.Float.Input("cfg_scale", default=0.5, min=0.0, max=1.0),
comfy_io.String.Input(
IO.Float.Input("cfg_scale", default=0.5, min=0.0, max=1.0),
IO.String.Input(
"video_id",
force_input=True,
tooltip="The ID of the video to be extended. Supports videos generated by text-to-video, image-to-video, and previous video extension operations. Cannot exceed 3 minutes total duration after extension.",
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1160,7 +1160,7 @@ class KlingVideoExtendNode(comfy_io.ComfyNode):
negative_prompt: str,
cfg_scale: float,
video_id: str,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
validate_prompts(prompt, negative_prompt, MAX_PROMPT_LENGTH_T2V)
auth = {
"auth_token": cls.hidden.auth_token_comfy_org,
@@ -1201,49 +1201,49 @@ class KlingVideoExtendNode(comfy_io.ComfyNode):
validate_video_result_response(final_response)
video = get_video_from_response(final_response)
return comfy_io.NodeOutput(await download_url_to_video_output(str(video.url)), str(video.id), str(video.duration))
return IO.NodeOutput(await download_url_to_video_output(str(video.url)), str(video.id), str(video.duration))
class KlingDualCharacterVideoEffectNode(comfy_io.ComfyNode):
class KlingDualCharacterVideoEffectNode(IO.ComfyNode):
"""Kling Dual Character Video Effect Node"""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingDualCharacterVideoEffectNode",
display_name="Kling Dual Character Video Effects",
category="api node/video/Kling",
description="Achieve different special effects when generating a video based on the effect_scene. First image will be positioned on left side, second on right side of the composite.",
inputs=[
comfy_io.Image.Input("image_left", tooltip="Left side image"),
comfy_io.Image.Input("image_right", tooltip="Right side image"),
comfy_io.Combo.Input(
IO.Image.Input("image_left", tooltip="Left side image"),
IO.Image.Input("image_right", tooltip="Right side image"),
IO.Combo.Input(
"effect_scene",
options=[i.value for i in KlingDualCharacterEffectsScene],
),
comfy_io.Combo.Input(
IO.Combo.Input(
"model_name",
options=[i.value for i in KlingCharacterEffectModelName],
default="kling-v1",
),
comfy_io.Combo.Input(
IO.Combo.Input(
"mode",
options=[i.value for i in KlingVideoGenMode],
default="std",
),
comfy_io.Combo.Input(
IO.Combo.Input(
"duration",
options=[i.value for i in KlingVideoGenDuration],
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1257,7 +1257,7 @@ class KlingDualCharacterVideoEffectNode(comfy_io.ComfyNode):
model_name: KlingCharacterEffectModelName,
mode: KlingVideoGenMode,
duration: KlingVideoGenDuration,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
video, _, duration = await execute_video_effect(
auth_kwargs={
"auth_token": cls.hidden.auth_token_comfy_org,
@@ -1272,43 +1272,43 @@ class KlingDualCharacterVideoEffectNode(comfy_io.ComfyNode):
image_1=image_left,
image_2=image_right,
)
return comfy_io.NodeOutput(video, duration)
return IO.NodeOutput(video, duration)
class KlingSingleImageVideoEffectNode(comfy_io.ComfyNode):
class KlingSingleImageVideoEffectNode(IO.ComfyNode):
"""Kling Single Image Video Effect Node"""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingSingleImageVideoEffectNode",
display_name="Kling Video Effects",
category="api node/video/Kling",
description="Achieve different special effects when generating a video based on the effect_scene.",
inputs=[
comfy_io.Image.Input("image", tooltip=" Reference Image. URL or Base64 encoded string (without data:image prefix). File size cannot exceed 10MB, resolution not less than 300*300px, aspect ratio between 1:2.5 ~ 2.5:1"),
comfy_io.Combo.Input(
IO.Image.Input("image", tooltip=" Reference Image. URL or Base64 encoded string (without data:image prefix). File size cannot exceed 10MB, resolution not less than 300*300px, aspect ratio between 1:2.5 ~ 2.5:1"),
IO.Combo.Input(
"effect_scene",
options=[i.value for i in KlingSingleImageEffectsScene],
),
comfy_io.Combo.Input(
IO.Combo.Input(
"model_name",
options=[i.value for i in KlingSingleImageEffectModelName],
),
comfy_io.Combo.Input(
IO.Combo.Input(
"duration",
options=[i.value for i in KlingVideoGenDuration],
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1320,8 +1320,8 @@ class KlingSingleImageVideoEffectNode(comfy_io.ComfyNode):
effect_scene: KlingSingleImageEffectsScene,
model_name: KlingSingleImageEffectModelName,
duration: KlingVideoGenDuration,
) -> comfy_io.NodeOutput:
return comfy_io.NodeOutput(
) -> IO.NodeOutput:
return IO.NodeOutput(
*(
await execute_video_effect(
auth_kwargs={
@@ -1339,34 +1339,34 @@ class KlingSingleImageVideoEffectNode(comfy_io.ComfyNode):
)
class KlingLipSyncAudioToVideoNode(comfy_io.ComfyNode):
class KlingLipSyncAudioToVideoNode(IO.ComfyNode):
"""Kling Lip Sync Audio to Video Node. Syncs mouth movements in a video file to the audio content of an audio file."""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingLipSyncAudioToVideoNode",
display_name="Kling Lip Sync Video with Audio",
category="api node/video/Kling",
description="Kling Lip Sync Audio to Video Node. Syncs mouth movements in a video file to the audio content of an audio file. When using, ensure that the audio contains clearly distinguishable vocals and that the video contains a distinct face. The audio file should not be larger than 5MB. The video file should not be larger than 100MB, should have height/width between 720px and 1920px, and should be between 2s and 10s in length.",
inputs=[
comfy_io.Video.Input("video"),
comfy_io.Audio.Input("audio"),
comfy_io.Combo.Input(
IO.Video.Input("video"),
IO.Audio.Input("audio"),
IO.Combo.Input(
"voice_language",
options=[i.value for i in KlingLipSyncVoiceLanguage],
default="en",
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1377,7 +1377,7 @@ class KlingLipSyncAudioToVideoNode(comfy_io.ComfyNode):
video: VideoInput,
audio: AudioInput,
voice_language: str,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
return await execute_lipsync(
auth_kwargs={
"auth_token": cls.hidden.auth_token_comfy_org,
@@ -1391,46 +1391,46 @@ class KlingLipSyncAudioToVideoNode(comfy_io.ComfyNode):
)
class KlingLipSyncTextToVideoNode(comfy_io.ComfyNode):
class KlingLipSyncTextToVideoNode(IO.ComfyNode):
"""Kling Lip Sync Text to Video Node. Syncs mouth movements in a video file to a text prompt."""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingLipSyncTextToVideoNode",
display_name="Kling Lip Sync Video with Text",
category="api node/video/Kling",
description="Kling Lip Sync Text to Video Node. Syncs mouth movements in a video file to a text prompt. The video file should not be larger than 100MB, should have height/width between 720px and 1920px, and should be between 2s and 10s in length.",
inputs=[
comfy_io.Video.Input("video"),
comfy_io.String.Input(
IO.Video.Input("video"),
IO.String.Input(
"text",
multiline=True,
tooltip="Text Content for Lip-Sync Video Generation. Required when mode is text2video. Maximum length is 120 characters.",
),
comfy_io.Combo.Input(
IO.Combo.Input(
"voice",
options=list(VOICES_CONFIG.keys()),
default="Melody",
),
comfy_io.Float.Input(
IO.Float.Input(
"voice_speed",
default=1,
min=0.8,
max=2.0,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Speech Rate. Valid range: 0.8~2.0, accurate to one decimal place.",
),
],
outputs=[
comfy_io.Video.Output(),
comfy_io.String.Output(display_name="video_id"),
comfy_io.String.Output(display_name="duration"),
IO.Video.Output(),
IO.String.Output(display_name="video_id"),
IO.String.Output(display_name="duration"),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1442,7 +1442,7 @@ class KlingLipSyncTextToVideoNode(comfy_io.ComfyNode):
text: str,
voice: str,
voice_speed: float,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
voice_id, voice_language = VOICES_CONFIG[voice]
return await execute_lipsync(
auth_kwargs={
@@ -1459,32 +1459,32 @@ class KlingLipSyncTextToVideoNode(comfy_io.ComfyNode):
)
class KlingVirtualTryOnNode(comfy_io.ComfyNode):
class KlingVirtualTryOnNode(IO.ComfyNode):
"""Kling Virtual Try On Node."""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingVirtualTryOnNode",
display_name="Kling Virtual Try On",
category="api node/image/Kling",
description="Kling Virtual Try On Node. Input a human image and a cloth image to try on the cloth on the human. You can merge multiple clothing item pictures into one image with a white background.",
inputs=[
comfy_io.Image.Input("human_image"),
comfy_io.Image.Input("cloth_image"),
comfy_io.Combo.Input(
IO.Image.Input("human_image"),
IO.Image.Input("cloth_image"),
IO.Combo.Input(
"model_name",
options=[i.value for i in KlingVirtualTryOnModelName],
default="kolors-virtual-try-on-v1",
),
],
outputs=[
comfy_io.Image.Output(),
IO.Image.Output(),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1495,7 +1495,7 @@ class KlingVirtualTryOnNode(comfy_io.ComfyNode):
human_image: torch.Tensor,
cloth_image: torch.Tensor,
model_name: KlingVirtualTryOnModelName,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
auth = {
"auth_token": cls.hidden.auth_token_comfy_org,
"comfy_api_key": cls.hidden.api_key_comfy_org,
@@ -1534,70 +1534,70 @@ class KlingVirtualTryOnNode(comfy_io.ComfyNode):
validate_image_result_response(final_response)
images = get_images_from_response(final_response)
return comfy_io.NodeOutput(await image_result_to_node_output(images))
return IO.NodeOutput(await image_result_to_node_output(images))
class KlingImageGenerationNode(comfy_io.ComfyNode):
class KlingImageGenerationNode(IO.ComfyNode):
"""Kling Image Generation Node. Generate an image from a text prompt with an optional reference image."""
@classmethod
def define_schema(cls) -> comfy_io.Schema:
return comfy_io.Schema(
def define_schema(cls) -> IO.Schema:
return IO.Schema(
node_id="KlingImageGenerationNode",
display_name="Kling Image Generation",
category="api node/image/Kling",
description="Kling Image Generation Node. Generate an image from a text prompt with an optional reference image.",
inputs=[
comfy_io.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
comfy_io.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
comfy_io.Combo.Input(
IO.String.Input("prompt", multiline=True, tooltip="Positive text prompt"),
IO.String.Input("negative_prompt", multiline=True, tooltip="Negative text prompt"),
IO.Combo.Input(
"image_type",
options=[i.value for i in KlingImageGenImageReferenceType],
),
comfy_io.Float.Input(
IO.Float.Input(
"image_fidelity",
default=0.5,
min=0.0,
max=1.0,
step=0.01,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Reference intensity for user-uploaded images",
),
comfy_io.Float.Input(
IO.Float.Input(
"human_fidelity",
default=0.45,
min=0.0,
max=1.0,
step=0.01,
display_mode=comfy_io.NumberDisplay.slider,
display_mode=IO.NumberDisplay.slider,
tooltip="Subject reference similarity",
),
comfy_io.Combo.Input(
IO.Combo.Input(
"model_name",
options=[i.value for i in KlingImageGenModelName],
default="kling-v1",
),
comfy_io.Combo.Input(
IO.Combo.Input(
"aspect_ratio",
options=[i.value for i in KlingImageGenAspectRatio],
default="16:9",
),
comfy_io.Int.Input(
IO.Int.Input(
"n",
default=1,
min=1,
max=9,
tooltip="Number of generated images",
),
comfy_io.Image.Input("image", optional=True),
IO.Image.Input("image", optional=True),
],
outputs=[
comfy_io.Image.Output(),
IO.Image.Output(),
],
hidden=[
comfy_io.Hidden.auth_token_comfy_org,
comfy_io.Hidden.api_key_comfy_org,
comfy_io.Hidden.unique_id,
IO.Hidden.auth_token_comfy_org,
IO.Hidden.api_key_comfy_org,
IO.Hidden.unique_id,
],
is_api_node=True,
)
@@ -1614,7 +1614,7 @@ class KlingImageGenerationNode(comfy_io.ComfyNode):
n: int,
aspect_ratio: KlingImageGenAspectRatio,
image: Optional[torch.Tensor] = None,
) -> comfy_io.NodeOutput:
) -> IO.NodeOutput:
validate_string(prompt, field_name="prompt", min_length=1, max_length=MAX_PROMPT_LENGTH_IMAGE_GEN)
validate_string(negative_prompt, field_name="negative_prompt", max_length=MAX_PROMPT_LENGTH_IMAGE_GEN)
@@ -1669,12 +1669,12 @@ class KlingImageGenerationNode(comfy_io.ComfyNode):
validate_image_result_response(final_response)
images = get_images_from_response(final_response)
return comfy_io.NodeOutput(await image_result_to_node_output(images))
return IO.NodeOutput(await image_result_to_node_output(images))
class KlingExtension(ComfyExtension):
@override
async def get_node_list(self) -> list[type[comfy_io.ComfyNode]]:
async def get_node_list(self) -> list[type[IO.ComfyNode]]:
return [
KlingCameraControls,
KlingTextToVideoNode,