mirror of
https://github.com/comfyanonymous/ComfyUI.git
synced 2025-12-16 09:42:29 +08:00
convert nodes_pixverse.py to V3 schema (#10177)
This commit is contained in:
parent
d7aa414141
commit
8c26d7bbe6
@ -1,5 +1,7 @@
|
|||||||
from inspect import cleandoc
|
from inspect import cleandoc
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
from typing_extensions import override
|
||||||
|
from io import BytesIO
|
||||||
from comfy_api_nodes.apis.pixverse_api import (
|
from comfy_api_nodes.apis.pixverse_api import (
|
||||||
PixverseTextVideoRequest,
|
PixverseTextVideoRequest,
|
||||||
PixverseImageVideoRequest,
|
PixverseImageVideoRequest,
|
||||||
@ -26,12 +28,11 @@ from comfy_api_nodes.apinode_utils import (
|
|||||||
tensor_to_bytesio,
|
tensor_to_bytesio,
|
||||||
validate_string,
|
validate_string,
|
||||||
)
|
)
|
||||||
from comfy.comfy_types.node_typing import IO, ComfyNodeABC
|
|
||||||
from comfy_api.input_impl import VideoFromFile
|
from comfy_api.input_impl import VideoFromFile
|
||||||
|
from comfy_api.latest import ComfyExtension, io as comfy_io
|
||||||
|
|
||||||
import torch
|
import torch
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from io import BytesIO
|
|
||||||
|
|
||||||
|
|
||||||
AVERAGE_DURATION_T2V = 32
|
AVERAGE_DURATION_T2V = 32
|
||||||
@ -72,100 +73,101 @@ async def upload_image_to_pixverse(image: torch.Tensor, auth_kwargs=None):
|
|||||||
return response_upload.Resp.img_id
|
return response_upload.Resp.img_id
|
||||||
|
|
||||||
|
|
||||||
class PixverseTemplateNode:
|
class PixverseTemplateNode(comfy_io.ComfyNode):
|
||||||
"""
|
"""
|
||||||
Select template for PixVerse Video generation.
|
Select template for PixVerse Video generation.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RETURN_TYPES = (PixverseIO.TEMPLATE,)
|
@classmethod
|
||||||
RETURN_NAMES = ("pixverse_template",)
|
def define_schema(cls) -> comfy_io.Schema:
|
||||||
FUNCTION = "create_template"
|
return comfy_io.Schema(
|
||||||
CATEGORY = "api node/video/PixVerse"
|
node_id="PixverseTemplateNode",
|
||||||
|
display_name="PixVerse Template",
|
||||||
|
category="api node/video/PixVerse",
|
||||||
|
inputs=[
|
||||||
|
comfy_io.Combo.Input("template", options=[list(pixverse_templates.keys())]),
|
||||||
|
],
|
||||||
|
outputs=[comfy_io.Custom(PixverseIO.TEMPLATE).Output(display_name="pixverse_template")],
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def execute(cls, template: str) -> comfy_io.NodeOutput:
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"template": (list(pixverse_templates.keys()),),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def create_template(self, template: str):
|
|
||||||
template_id = pixverse_templates.get(template, None)
|
template_id = pixverse_templates.get(template, None)
|
||||||
if template_id is None:
|
if template_id is None:
|
||||||
raise Exception(f"Template '{template}' is not recognized.")
|
raise Exception(f"Template '{template}' is not recognized.")
|
||||||
# just return the integer
|
# just return the integer
|
||||||
return (template_id,)
|
return comfy_io.NodeOutput(template_id)
|
||||||
|
|
||||||
|
|
||||||
class PixverseTextToVideoNode(ComfyNodeABC):
|
class PixverseTextToVideoNode(comfy_io.ComfyNode):
|
||||||
"""
|
"""
|
||||||
Generates videos based on prompt and output_size.
|
Generates videos based on prompt and output_size.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RETURN_TYPES = (IO.VIDEO,)
|
@classmethod
|
||||||
DESCRIPTION = cleandoc(__doc__ or "") # Handle potential None value
|
def define_schema(cls) -> comfy_io.Schema:
|
||||||
FUNCTION = "api_call"
|
return comfy_io.Schema(
|
||||||
API_NODE = True
|
node_id="PixverseTextToVideoNode",
|
||||||
CATEGORY = "api node/video/PixVerse"
|
display_name="PixVerse Text to Video",
|
||||||
|
category="api node/video/PixVerse",
|
||||||
|
description=cleandoc(cls.__doc__ or ""),
|
||||||
|
inputs=[
|
||||||
|
comfy_io.String.Input(
|
||||||
|
"prompt",
|
||||||
|
multiline=True,
|
||||||
|
default="",
|
||||||
|
tooltip="Prompt for the video generation",
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"aspect_ratio",
|
||||||
|
options=[ratio.value for ratio in PixverseAspectRatio],
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"quality",
|
||||||
|
options=[resolution.value for resolution in PixverseQuality],
|
||||||
|
default=PixverseQuality.res_540p,
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"duration_seconds",
|
||||||
|
options=[dur.value for dur in PixverseDuration],
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"motion_mode",
|
||||||
|
options=[mode.value for mode in PixverseMotionMode],
|
||||||
|
),
|
||||||
|
comfy_io.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="Seed for video generation.",
|
||||||
|
),
|
||||||
|
comfy_io.String.Input(
|
||||||
|
"negative_prompt",
|
||||||
|
default="",
|
||||||
|
force_input=True,
|
||||||
|
tooltip="An optional text description of undesired elements on an image.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
|
comfy_io.Custom(PixverseIO.TEMPLATE).Input(
|
||||||
|
"pixverse_template",
|
||||||
|
tooltip="An optional template to influence style of generation, created by the PixVerse Template node.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[comfy_io.Video.Output()],
|
||||||
|
hidden=[
|
||||||
|
comfy_io.Hidden.auth_token_comfy_org,
|
||||||
|
comfy_io.Hidden.api_key_comfy_org,
|
||||||
|
comfy_io.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
async def execute(
|
||||||
return {
|
cls,
|
||||||
"required": {
|
|
||||||
"prompt": (
|
|
||||||
IO.STRING,
|
|
||||||
{
|
|
||||||
"multiline": True,
|
|
||||||
"default": "",
|
|
||||||
"tooltip": "Prompt for the video generation",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"aspect_ratio": ([ratio.value for ratio in PixverseAspectRatio],),
|
|
||||||
"quality": (
|
|
||||||
[resolution.value for resolution in PixverseQuality],
|
|
||||||
{
|
|
||||||
"default": PixverseQuality.res_540p,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"duration_seconds": ([dur.value for dur in PixverseDuration],),
|
|
||||||
"motion_mode": ([mode.value for mode in PixverseMotionMode],),
|
|
||||||
"seed": (
|
|
||||||
IO.INT,
|
|
||||||
{
|
|
||||||
"default": 0,
|
|
||||||
"min": 0,
|
|
||||||
"max": 2147483647,
|
|
||||||
"control_after_generate": True,
|
|
||||||
"tooltip": "Seed for video generation.",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
"negative_prompt": (
|
|
||||||
IO.STRING,
|
|
||||||
{
|
|
||||||
"default": "",
|
|
||||||
"forceInput": True,
|
|
||||||
"tooltip": "An optional text description of undesired elements on an image.",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"pixverse_template": (
|
|
||||||
PixverseIO.TEMPLATE,
|
|
||||||
{
|
|
||||||
"tooltip": "An optional template to influence style of generation, created by the PixVerse Template node."
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"hidden": {
|
|
||||||
"auth_token": "AUTH_TOKEN_COMFY_ORG",
|
|
||||||
"comfy_api_key": "API_KEY_COMFY_ORG",
|
|
||||||
"unique_id": "UNIQUE_ID",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
async def api_call(
|
|
||||||
self,
|
|
||||||
prompt: str,
|
prompt: str,
|
||||||
aspect_ratio: str,
|
aspect_ratio: str,
|
||||||
quality: str,
|
quality: str,
|
||||||
@ -174,9 +176,7 @@ class PixverseTextToVideoNode(ComfyNodeABC):
|
|||||||
seed,
|
seed,
|
||||||
negative_prompt: str = None,
|
negative_prompt: str = None,
|
||||||
pixverse_template: int = None,
|
pixverse_template: int = None,
|
||||||
unique_id: Optional[str] = None,
|
) -> comfy_io.NodeOutput:
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
validate_string(prompt, strip_whitespace=False)
|
validate_string(prompt, strip_whitespace=False)
|
||||||
# 1080p is limited to 5 seconds duration
|
# 1080p is limited to 5 seconds duration
|
||||||
# only normal motion_mode supported for 1080p or for non-5 second duration
|
# only normal motion_mode supported for 1080p or for non-5 second duration
|
||||||
@ -186,6 +186,10 @@ class PixverseTextToVideoNode(ComfyNodeABC):
|
|||||||
elif duration_seconds != PixverseDuration.dur_5:
|
elif duration_seconds != PixverseDuration.dur_5:
|
||||||
motion_mode = PixverseMotionMode.normal
|
motion_mode = PixverseMotionMode.normal
|
||||||
|
|
||||||
|
auth = {
|
||||||
|
"auth_token": cls.hidden.auth_token_comfy_org,
|
||||||
|
"comfy_api_key": cls.hidden.api_key_comfy_org,
|
||||||
|
}
|
||||||
operation = SynchronousOperation(
|
operation = SynchronousOperation(
|
||||||
endpoint=ApiEndpoint(
|
endpoint=ApiEndpoint(
|
||||||
path="/proxy/pixverse/video/text/generate",
|
path="/proxy/pixverse/video/text/generate",
|
||||||
@ -203,7 +207,7 @@ class PixverseTextToVideoNode(ComfyNodeABC):
|
|||||||
template_id=pixverse_template,
|
template_id=pixverse_template,
|
||||||
seed=seed,
|
seed=seed,
|
||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=auth,
|
||||||
)
|
)
|
||||||
response_api = await operation.execute()
|
response_api = await operation.execute()
|
||||||
|
|
||||||
@ -224,8 +228,8 @@ class PixverseTextToVideoNode(ComfyNodeABC):
|
|||||||
PixverseStatus.deleted,
|
PixverseStatus.deleted,
|
||||||
],
|
],
|
||||||
status_extractor=lambda x: x.Resp.status,
|
status_extractor=lambda x: x.Resp.status,
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=auth,
|
||||||
node_id=unique_id,
|
node_id=cls.hidden.unique_id,
|
||||||
result_url_extractor=get_video_url_from_response,
|
result_url_extractor=get_video_url_from_response,
|
||||||
estimated_duration=AVERAGE_DURATION_T2V,
|
estimated_duration=AVERAGE_DURATION_T2V,
|
||||||
)
|
)
|
||||||
@ -233,77 +237,75 @@ class PixverseTextToVideoNode(ComfyNodeABC):
|
|||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(response_poll.Resp.url) as vid_response:
|
async with session.get(response_poll.Resp.url) as vid_response:
|
||||||
return (VideoFromFile(BytesIO(await vid_response.content.read())),)
|
return comfy_io.NodeOutput(VideoFromFile(BytesIO(await vid_response.content.read())))
|
||||||
|
|
||||||
|
|
||||||
class PixverseImageToVideoNode(ComfyNodeABC):
|
class PixverseImageToVideoNode(comfy_io.ComfyNode):
|
||||||
"""
|
"""
|
||||||
Generates videos based on prompt and output_size.
|
Generates videos based on prompt and output_size.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RETURN_TYPES = (IO.VIDEO,)
|
@classmethod
|
||||||
DESCRIPTION = cleandoc(__doc__ or "") # Handle potential None value
|
def define_schema(cls) -> comfy_io.Schema:
|
||||||
FUNCTION = "api_call"
|
return comfy_io.Schema(
|
||||||
API_NODE = True
|
node_id="PixverseImageToVideoNode",
|
||||||
CATEGORY = "api node/video/PixVerse"
|
display_name="PixVerse Image to Video",
|
||||||
|
category="api node/video/PixVerse",
|
||||||
|
description=cleandoc(cls.__doc__ or ""),
|
||||||
|
inputs=[
|
||||||
|
comfy_io.Image.Input("image"),
|
||||||
|
comfy_io.String.Input(
|
||||||
|
"prompt",
|
||||||
|
multiline=True,
|
||||||
|
default="",
|
||||||
|
tooltip="Prompt for the video generation",
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"quality",
|
||||||
|
options=[resolution.value for resolution in PixverseQuality],
|
||||||
|
default=PixverseQuality.res_540p,
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"duration_seconds",
|
||||||
|
options=[dur.value for dur in PixverseDuration],
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"motion_mode",
|
||||||
|
options=[mode.value for mode in PixverseMotionMode],
|
||||||
|
),
|
||||||
|
comfy_io.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="Seed for video generation.",
|
||||||
|
),
|
||||||
|
comfy_io.String.Input(
|
||||||
|
"negative_prompt",
|
||||||
|
default="",
|
||||||
|
force_input=True,
|
||||||
|
tooltip="An optional text description of undesired elements on an image.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
|
comfy_io.Custom(PixverseIO.TEMPLATE).Input(
|
||||||
|
"pixverse_template",
|
||||||
|
tooltip="An optional template to influence style of generation, created by the PixVerse Template node.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[comfy_io.Video.Output()],
|
||||||
|
hidden=[
|
||||||
|
comfy_io.Hidden.auth_token_comfy_org,
|
||||||
|
comfy_io.Hidden.api_key_comfy_org,
|
||||||
|
comfy_io.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
async def execute(
|
||||||
return {
|
cls,
|
||||||
"required": {
|
|
||||||
"image": (IO.IMAGE,),
|
|
||||||
"prompt": (
|
|
||||||
IO.STRING,
|
|
||||||
{
|
|
||||||
"multiline": True,
|
|
||||||
"default": "",
|
|
||||||
"tooltip": "Prompt for the video generation",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"quality": (
|
|
||||||
[resolution.value for resolution in PixverseQuality],
|
|
||||||
{
|
|
||||||
"default": PixverseQuality.res_540p,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"duration_seconds": ([dur.value for dur in PixverseDuration],),
|
|
||||||
"motion_mode": ([mode.value for mode in PixverseMotionMode],),
|
|
||||||
"seed": (
|
|
||||||
IO.INT,
|
|
||||||
{
|
|
||||||
"default": 0,
|
|
||||||
"min": 0,
|
|
||||||
"max": 2147483647,
|
|
||||||
"control_after_generate": True,
|
|
||||||
"tooltip": "Seed for video generation.",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
"negative_prompt": (
|
|
||||||
IO.STRING,
|
|
||||||
{
|
|
||||||
"default": "",
|
|
||||||
"forceInput": True,
|
|
||||||
"tooltip": "An optional text description of undesired elements on an image.",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"pixverse_template": (
|
|
||||||
PixverseIO.TEMPLATE,
|
|
||||||
{
|
|
||||||
"tooltip": "An optional template to influence style of generation, created by the PixVerse Template node."
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"hidden": {
|
|
||||||
"auth_token": "AUTH_TOKEN_COMFY_ORG",
|
|
||||||
"comfy_api_key": "API_KEY_COMFY_ORG",
|
|
||||||
"unique_id": "UNIQUE_ID",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
async def api_call(
|
|
||||||
self,
|
|
||||||
image: torch.Tensor,
|
image: torch.Tensor,
|
||||||
prompt: str,
|
prompt: str,
|
||||||
quality: str,
|
quality: str,
|
||||||
@ -312,11 +314,13 @@ class PixverseImageToVideoNode(ComfyNodeABC):
|
|||||||
seed,
|
seed,
|
||||||
negative_prompt: str = None,
|
negative_prompt: str = None,
|
||||||
pixverse_template: int = None,
|
pixverse_template: int = None,
|
||||||
unique_id: Optional[str] = None,
|
) -> comfy_io.NodeOutput:
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
validate_string(prompt, strip_whitespace=False)
|
validate_string(prompt, strip_whitespace=False)
|
||||||
img_id = await upload_image_to_pixverse(image, auth_kwargs=kwargs)
|
auth = {
|
||||||
|
"auth_token": cls.hidden.auth_token_comfy_org,
|
||||||
|
"comfy_api_key": cls.hidden.api_key_comfy_org,
|
||||||
|
}
|
||||||
|
img_id = await upload_image_to_pixverse(image, auth_kwargs=auth)
|
||||||
|
|
||||||
# 1080p is limited to 5 seconds duration
|
# 1080p is limited to 5 seconds duration
|
||||||
# only normal motion_mode supported for 1080p or for non-5 second duration
|
# only normal motion_mode supported for 1080p or for non-5 second duration
|
||||||
@ -343,7 +347,7 @@ class PixverseImageToVideoNode(ComfyNodeABC):
|
|||||||
template_id=pixverse_template,
|
template_id=pixverse_template,
|
||||||
seed=seed,
|
seed=seed,
|
||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=auth,
|
||||||
)
|
)
|
||||||
response_api = await operation.execute()
|
response_api = await operation.execute()
|
||||||
|
|
||||||
@ -364,8 +368,8 @@ class PixverseImageToVideoNode(ComfyNodeABC):
|
|||||||
PixverseStatus.deleted,
|
PixverseStatus.deleted,
|
||||||
],
|
],
|
||||||
status_extractor=lambda x: x.Resp.status,
|
status_extractor=lambda x: x.Resp.status,
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=auth,
|
||||||
node_id=unique_id,
|
node_id=cls.hidden.unique_id,
|
||||||
result_url_extractor=get_video_url_from_response,
|
result_url_extractor=get_video_url_from_response,
|
||||||
estimated_duration=AVERAGE_DURATION_I2V,
|
estimated_duration=AVERAGE_DURATION_I2V,
|
||||||
)
|
)
|
||||||
@ -373,72 +377,71 @@ class PixverseImageToVideoNode(ComfyNodeABC):
|
|||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(response_poll.Resp.url) as vid_response:
|
async with session.get(response_poll.Resp.url) as vid_response:
|
||||||
return (VideoFromFile(BytesIO(await vid_response.content.read())),)
|
return comfy_io.NodeOutput(VideoFromFile(BytesIO(await vid_response.content.read())))
|
||||||
|
|
||||||
|
|
||||||
class PixverseTransitionVideoNode(ComfyNodeABC):
|
class PixverseTransitionVideoNode(comfy_io.ComfyNode):
|
||||||
"""
|
"""
|
||||||
Generates videos based on prompt and output_size.
|
Generates videos based on prompt and output_size.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RETURN_TYPES = (IO.VIDEO,)
|
@classmethod
|
||||||
DESCRIPTION = cleandoc(__doc__ or "") # Handle potential None value
|
def define_schema(cls) -> comfy_io.Schema:
|
||||||
FUNCTION = "api_call"
|
return comfy_io.Schema(
|
||||||
API_NODE = True
|
node_id="PixverseTransitionVideoNode",
|
||||||
CATEGORY = "api node/video/PixVerse"
|
display_name="PixVerse Transition Video",
|
||||||
|
category="api node/video/PixVerse",
|
||||||
|
description=cleandoc(cls.__doc__ or ""),
|
||||||
|
inputs=[
|
||||||
|
comfy_io.Image.Input("first_frame"),
|
||||||
|
comfy_io.Image.Input("last_frame"),
|
||||||
|
comfy_io.String.Input(
|
||||||
|
"prompt",
|
||||||
|
multiline=True,
|
||||||
|
default="",
|
||||||
|
tooltip="Prompt for the video generation",
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"quality",
|
||||||
|
options=[resolution.value for resolution in PixverseQuality],
|
||||||
|
default=PixverseQuality.res_540p,
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"duration_seconds",
|
||||||
|
options=[dur.value for dur in PixverseDuration],
|
||||||
|
),
|
||||||
|
comfy_io.Combo.Input(
|
||||||
|
"motion_mode",
|
||||||
|
options=[mode.value for mode in PixverseMotionMode],
|
||||||
|
),
|
||||||
|
comfy_io.Int.Input(
|
||||||
|
"seed",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=2147483647,
|
||||||
|
control_after_generate=True,
|
||||||
|
tooltip="Seed for video generation.",
|
||||||
|
),
|
||||||
|
comfy_io.String.Input(
|
||||||
|
"negative_prompt",
|
||||||
|
default="",
|
||||||
|
force_input=True,
|
||||||
|
tooltip="An optional text description of undesired elements on an image.",
|
||||||
|
optional=True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[comfy_io.Video.Output()],
|
||||||
|
hidden=[
|
||||||
|
comfy_io.Hidden.auth_token_comfy_org,
|
||||||
|
comfy_io.Hidden.api_key_comfy_org,
|
||||||
|
comfy_io.Hidden.unique_id,
|
||||||
|
],
|
||||||
|
is_api_node=True,
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
async def execute(
|
||||||
return {
|
cls,
|
||||||
"required": {
|
|
||||||
"first_frame": (IO.IMAGE,),
|
|
||||||
"last_frame": (IO.IMAGE,),
|
|
||||||
"prompt": (
|
|
||||||
IO.STRING,
|
|
||||||
{
|
|
||||||
"multiline": True,
|
|
||||||
"default": "",
|
|
||||||
"tooltip": "Prompt for the video generation",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"quality": (
|
|
||||||
[resolution.value for resolution in PixverseQuality],
|
|
||||||
{
|
|
||||||
"default": PixverseQuality.res_540p,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
"duration_seconds": ([dur.value for dur in PixverseDuration],),
|
|
||||||
"motion_mode": ([mode.value for mode in PixverseMotionMode],),
|
|
||||||
"seed": (
|
|
||||||
IO.INT,
|
|
||||||
{
|
|
||||||
"default": 0,
|
|
||||||
"min": 0,
|
|
||||||
"max": 2147483647,
|
|
||||||
"control_after_generate": True,
|
|
||||||
"tooltip": "Seed for video generation.",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
"negative_prompt": (
|
|
||||||
IO.STRING,
|
|
||||||
{
|
|
||||||
"default": "",
|
|
||||||
"forceInput": True,
|
|
||||||
"tooltip": "An optional text description of undesired elements on an image.",
|
|
||||||
},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"hidden": {
|
|
||||||
"auth_token": "AUTH_TOKEN_COMFY_ORG",
|
|
||||||
"comfy_api_key": "API_KEY_COMFY_ORG",
|
|
||||||
"unique_id": "UNIQUE_ID",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
async def api_call(
|
|
||||||
self,
|
|
||||||
first_frame: torch.Tensor,
|
first_frame: torch.Tensor,
|
||||||
last_frame: torch.Tensor,
|
last_frame: torch.Tensor,
|
||||||
prompt: str,
|
prompt: str,
|
||||||
@ -447,12 +450,14 @@ class PixverseTransitionVideoNode(ComfyNodeABC):
|
|||||||
motion_mode: str,
|
motion_mode: str,
|
||||||
seed,
|
seed,
|
||||||
negative_prompt: str = None,
|
negative_prompt: str = None,
|
||||||
unique_id: Optional[str] = None,
|
) -> comfy_io.NodeOutput:
|
||||||
**kwargs,
|
|
||||||
):
|
|
||||||
validate_string(prompt, strip_whitespace=False)
|
validate_string(prompt, strip_whitespace=False)
|
||||||
first_frame_id = await upload_image_to_pixverse(first_frame, auth_kwargs=kwargs)
|
auth = {
|
||||||
last_frame_id = await upload_image_to_pixverse(last_frame, auth_kwargs=kwargs)
|
"auth_token": cls.hidden.auth_token_comfy_org,
|
||||||
|
"comfy_api_key": cls.hidden.api_key_comfy_org,
|
||||||
|
}
|
||||||
|
first_frame_id = await upload_image_to_pixverse(first_frame, auth_kwargs=auth)
|
||||||
|
last_frame_id = await upload_image_to_pixverse(last_frame, auth_kwargs=auth)
|
||||||
|
|
||||||
# 1080p is limited to 5 seconds duration
|
# 1080p is limited to 5 seconds duration
|
||||||
# only normal motion_mode supported for 1080p or for non-5 second duration
|
# only normal motion_mode supported for 1080p or for non-5 second duration
|
||||||
@ -479,7 +484,7 @@ class PixverseTransitionVideoNode(ComfyNodeABC):
|
|||||||
negative_prompt=negative_prompt if negative_prompt else None,
|
negative_prompt=negative_prompt if negative_prompt else None,
|
||||||
seed=seed,
|
seed=seed,
|
||||||
),
|
),
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=auth,
|
||||||
)
|
)
|
||||||
response_api = await operation.execute()
|
response_api = await operation.execute()
|
||||||
|
|
||||||
@ -500,8 +505,8 @@ class PixverseTransitionVideoNode(ComfyNodeABC):
|
|||||||
PixverseStatus.deleted,
|
PixverseStatus.deleted,
|
||||||
],
|
],
|
||||||
status_extractor=lambda x: x.Resp.status,
|
status_extractor=lambda x: x.Resp.status,
|
||||||
auth_kwargs=kwargs,
|
auth_kwargs=auth,
|
||||||
node_id=unique_id,
|
node_id=cls.hidden.unique_id,
|
||||||
result_url_extractor=get_video_url_from_response,
|
result_url_extractor=get_video_url_from_response,
|
||||||
estimated_duration=AVERAGE_DURATION_T2V,
|
estimated_duration=AVERAGE_DURATION_T2V,
|
||||||
)
|
)
|
||||||
@ -509,19 +514,19 @@ class PixverseTransitionVideoNode(ComfyNodeABC):
|
|||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
async with session.get(response_poll.Resp.url) as vid_response:
|
async with session.get(response_poll.Resp.url) as vid_response:
|
||||||
return (VideoFromFile(BytesIO(await vid_response.content.read())),)
|
return comfy_io.NodeOutput(VideoFromFile(BytesIO(await vid_response.content.read())))
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
class PixVerseExtension(ComfyExtension):
|
||||||
"PixverseTextToVideoNode": PixverseTextToVideoNode,
|
@override
|
||||||
"PixverseImageToVideoNode": PixverseImageToVideoNode,
|
async def get_node_list(self) -> list[type[comfy_io.ComfyNode]]:
|
||||||
"PixverseTransitionVideoNode": PixverseTransitionVideoNode,
|
return [
|
||||||
"PixverseTemplateNode": PixverseTemplateNode,
|
PixverseTextToVideoNode,
|
||||||
}
|
PixverseImageToVideoNode,
|
||||||
|
PixverseTransitionVideoNode,
|
||||||
|
PixverseTemplateNode,
|
||||||
|
]
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
||||||
"PixverseTextToVideoNode": "PixVerse Text to Video",
|
async def comfy_entrypoint() -> PixVerseExtension:
|
||||||
"PixverseImageToVideoNode": "PixVerse Image to Video",
|
return PixVerseExtension()
|
||||||
"PixverseTransitionVideoNode": "PixVerse Transition Video",
|
|
||||||
"PixverseTemplateNode": "PixVerse Template",
|
|
||||||
}
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user