chore(api-nodes): add upload_image_to_comfyapi to make code look better

This commit is contained in:
bigcat88 2026-01-20 09:46:14 +02:00
parent 8ccc0c94fa
commit 36747f27e1
13 changed files with 75 additions and 61 deletions

View File

@ -30,6 +30,7 @@ from comfy_api_nodes.util import (
image_tensor_pair_to_batch,
poll_op,
sync_op,
upload_image_to_comfyapi,
upload_images_to_comfyapi,
validate_image_aspect_ratio,
validate_image_dimensions,
@ -249,7 +250,7 @@ class ByteDanceImageEditNode(IO.ComfyNode):
if get_number_of_images(image) != 1:
raise ValueError("Exactly one input image is required.")
validate_image_aspect_ratio(image, (1, 3), (3, 1))
source_url = (await upload_images_to_comfyapi(cls, image, max_images=1, mime_type="image/png"))[0]
source_url = await upload_image_to_comfyapi(cls, image, mime_type="image/png")
payload = Image2ImageTaskCreationRequest(
model=model,
prompt=prompt,
@ -702,7 +703,7 @@ class ByteDanceImageToVideoNode(IO.ComfyNode):
validate_image_dimensions(image, min_width=300, min_height=300, max_width=6000, max_height=6000)
validate_image_aspect_ratio(image, (2, 5), (5, 2), strict=False) # 0.4 to 2.5
image_url = (await upload_images_to_comfyapi(cls, image, max_images=1))[0]
image_url = await upload_image_to_comfyapi(cls, image)
prompt = (
f"{prompt} "
f"--resolution {resolution} "

View File

@ -71,6 +71,7 @@ from comfy_api_nodes.util import (
sync_op,
tensor_to_base64_string,
upload_audio_to_comfyapi,
upload_image_to_comfyapi,
upload_images_to_comfyapi,
upload_video_to_comfyapi,
validate_image_aspect_ratio,
@ -958,7 +959,7 @@ class OmniProFirstLastFrameNode(IO.ComfyNode):
validate_image_aspect_ratio(first_frame, (1, 2.5), (2.5, 1))
image_list: list[OmniParamImage] = [
OmniParamImage(
image_url=(await upload_images_to_comfyapi(cls, first_frame, wait_label="Uploading first frame"))[0],
image_url=await upload_image_to_comfyapi(cls, first_frame, wait_label="Uploading first frame"),
type="first_frame",
)
]
@ -967,7 +968,7 @@ class OmniProFirstLastFrameNode(IO.ComfyNode):
validate_image_aspect_ratio(end_frame, (1, 2.5), (2.5, 1))
image_list.append(
OmniParamImage(
image_url=(await upload_images_to_comfyapi(cls, end_frame, wait_label="Uploading end frame"))[0],
image_url=await upload_image_to_comfyapi(cls, end_frame, wait_label="Uploading end frame"),
type="end_frame",
)
)
@ -2365,7 +2366,7 @@ class ImageToVideoWithAudio(IO.ComfyNode):
response_model=TaskStatusResponse,
data=ImageToVideoWithAudioRequest(
model_name=model_name,
image=(await upload_images_to_comfyapi(cls, start_frame))[0],
image=await upload_image_to_comfyapi(cls, start_frame),
prompt=prompt,
mode=mode,
duration=str(duration),
@ -2459,7 +2460,7 @@ class MotionControl(IO.ComfyNode):
response_model=TaskStatusResponse,
data=MotionControlRequest(
prompt=prompt,
image_url=(await upload_images_to_comfyapi(cls, reference_image))[0],
image_url=await upload_image_to_comfyapi(cls, reference_image),
video_url=await upload_video_to_comfyapi(cls, reference_video),
keep_original_sound="yes" if keep_original_sound else "no",
character_orientation=character_orientation,

View File

@ -8,7 +8,7 @@ from comfy_api_nodes.util import (
ApiEndpoint,
get_number_of_images,
sync_op_raw,
upload_images_to_comfyapi,
upload_image_to_comfyapi,
validate_string,
)
@ -187,7 +187,7 @@ class ImageToVideoNode(IO.ComfyNode):
cls,
ApiEndpoint("/proxy/ltx/v1/image-to-video", "POST"),
data=ExecuteTaskRequest(
image_uri=(await upload_images_to_comfyapi(cls, image, max_images=1, mime_type="image/png"))[0],
image_uri=await upload_image_to_comfyapi(cls, image, mime_type="image/png"),
prompt=prompt,
model=MODELS_MAP[model],
duration=duration,

View File

@ -30,6 +30,7 @@ from comfy_api_nodes.util import (
download_url_to_video_output,
poll_op,
sync_op,
upload_image_to_comfyapi,
upload_images_to_comfyapi,
validate_string,
)
@ -257,8 +258,8 @@ class LumaImageGenerationNode(IO.ComfyNode):
luma_urls = []
ref_count = 0
for ref in luma_ref.refs:
download_urls = await upload_images_to_comfyapi(cls, ref.image, max_images=1)
luma_urls.append(download_urls[0])
download_url = await upload_image_to_comfyapi(cls, ref.image)
luma_urls.append(download_url)
ref_count += 1
if ref_count >= max_refs:
break
@ -340,8 +341,7 @@ class LumaImageModifyNode(IO.ComfyNode):
image_weight: float,
seed,
) -> IO.NodeOutput:
download_urls = await upload_images_to_comfyapi(cls, image, max_images=1)
image_url = download_urls[0]
image_url = await upload_image_to_comfyapi(cls, image)
response_api = await sync_op(
cls,
ApiEndpoint(path="/proxy/luma/generations/image", method="POST"),
@ -589,11 +589,9 @@ class LumaImageToVideoGenerationNode(IO.ComfyNode):
frame0 = None
frame1 = None
if first_image is not None:
download_urls = await upload_images_to_comfyapi(cls, first_image, max_images=1)
frame0 = LumaImageReference(type="image", url=download_urls[0])
frame0 = LumaImageReference(type="image", url=await upload_image_to_comfyapi(cls, first_image))
if last_image is not None:
download_urls = await upload_images_to_comfyapi(cls, last_image, max_images=1)
frame1 = LumaImageReference(type="image", url=download_urls[0])
frame1 = LumaImageReference(type="image", url=await upload_image_to_comfyapi(cls, last_image))
return LumaKeyframes(frame0=frame0, frame1=frame1)

View File

@ -23,6 +23,7 @@ from comfy_api_nodes.util import (
download_url_to_bytesio,
poll_op,
sync_op,
upload_image_to_comfyapi,
upload_images_to_comfyapi,
validate_string,
)
@ -197,7 +198,7 @@ class MeshyRefineNode(IO.ComfyNode):
if texture_prompt:
validate_string(texture_prompt, field_name="texture_prompt", max_length=600)
if texture_image is not None:
texture_image_url = (await upload_images_to_comfyapi(cls, texture_image, wait_label="Uploading texture"))[0]
texture_image_url = await upload_image_to_comfyapi(cls, texture_image, wait_label="Uploading texture")
response = await sync_op(
cls,
endpoint=ApiEndpoint(path="/proxy/meshy/openapi/v2/text-to-3d", method="POST"),
@ -344,17 +345,15 @@ class MeshyImageToModelNode(IO.ComfyNode):
validate_string(should_texture["texture_prompt"], field_name="texture_prompt", max_length=600)
texture_prompt = should_texture["texture_prompt"]
if should_texture["texture_image"] is not None:
texture_image_url = (
await upload_images_to_comfyapi(
cls, should_texture["texture_image"], wait_label="Uploading texture"
)
)[0]
texture_image_url = await upload_image_to_comfyapi(
cls, should_texture["texture_image"], wait_label="Uploading texture"
)
response = await sync_op(
cls,
ApiEndpoint(path="/proxy/meshy/openapi/v1/image-to-3d", method="POST"),
response_model=MeshyTaskResponse,
data=MeshyImageToModelRequest(
image_url=(await upload_images_to_comfyapi(cls, image, wait_label="Uploading base image"))[0],
image_url=await upload_image_to_comfyapi(cls, image, wait_label="Uploading base image"),
ai_model=model,
topology=should_remesh.get("topology", None),
target_polycount=should_remesh.get("target_polycount", None),
@ -505,11 +504,9 @@ class MeshyMultiImageToModelNode(IO.ComfyNode):
validate_string(should_texture["texture_prompt"], field_name="texture_prompt", max_length=600)
texture_prompt = should_texture["texture_prompt"]
if should_texture["texture_image"] is not None:
texture_image_url = (
await upload_images_to_comfyapi(
cls, should_texture["texture_image"], wait_label="Uploading texture"
)
)[0]
texture_image_url = await upload_image_to_comfyapi(
cls, should_texture["texture_image"], wait_label="Uploading texture"
)
response = await sync_op(
cls,
ApiEndpoint(path="/proxy/meshy/openapi/v1/multi-image-to-3d", method="POST"),
@ -595,7 +592,7 @@ class MeshyRigModelNode(IO.ComfyNode):
) -> IO.NodeOutput:
texture_image_url = None
if texture_image is not None:
texture_image_url = (await upload_images_to_comfyapi(cls, texture_image, wait_label="Uploading texture"))[0]
texture_image_url = await upload_image_to_comfyapi(cls, texture_image, wait_label="Uploading texture")
response = await sync_op(
cls,
endpoint=ApiEndpoint(path="/proxy/meshy/openapi/v1/rigging", method="POST"),
@ -746,7 +743,7 @@ class MeshyTextureNode(IO.ComfyNode):
raise ValueError("Either text_style_prompt or image_style is required")
image_style_url = None
if image_style is not None:
image_style_url = (await upload_images_to_comfyapi(cls, image_style, wait_label="Uploading style"))[0]
image_style_url = await upload_image_to_comfyapi(cls, image_style, wait_label="Uploading style")
response = await sync_op(
cls,
endpoint=ApiEndpoint(path="/proxy/meshy/openapi/v1/retexture", method="POST"),

View File

@ -17,7 +17,7 @@ from comfy_api_nodes.util import (
download_url_to_video_output,
poll_op,
sync_op,
upload_images_to_comfyapi,
upload_image_to_comfyapi,
validate_string,
)
@ -39,12 +39,12 @@ async def _generate_mm_video(
validate_string(prompt_text, field_name="prompt_text")
image_url = None
if image is not None:
image_url = (await upload_images_to_comfyapi(cls, image, max_images=1))[0]
image_url = await upload_image_to_comfyapi(cls, image)
# TODO: figure out how to deal with subject properly, API returns invalid params when using S2V-01 model
subject_reference = None
if subject is not None:
subject_url = (await upload_images_to_comfyapi(cls, subject, max_images=1))[0]
subject_url = await upload_image_to_comfyapi(cls, subject)
subject_reference = [SubjectReferenceItem(image=subject_url)]
response = await sync_op(
@ -384,7 +384,7 @@ class MinimaxHailuoVideoNode(IO.ComfyNode):
# upload image, if passed in
image_url = None
if first_frame_image is not None:
image_url = (await upload_images_to_comfyapi(cls, first_frame_image, max_images=1))[0]
image_url = await upload_image_to_comfyapi(cls, first_frame_image)
response = await sync_op(
cls,

View File

@ -16,7 +16,7 @@ from comfy_api_nodes.util import (
poll_op,
sync_op,
trim_video,
upload_images_to_comfyapi,
upload_image_to_comfyapi,
upload_video_to_comfyapi,
validate_container_format_is_mp4,
validate_image_dimensions,
@ -267,7 +267,7 @@ class MoonvalleyImg2VideoNode(IO.ComfyNode):
# Get MIME type from tensor - assuming PNG format for image tensors
mime_type = "image/png"
image_url = (await upload_images_to_comfyapi(cls, image, max_images=1, mime_type=mime_type))[0]
image_url = await upload_image_to_comfyapi(cls, image, mime_type=mime_type)
task_creation_response = await sync_op(
cls,
endpoint=ApiEndpoint(path=API_IMG2VIDEO_ENDPOINT, method="POST"),

View File

@ -36,6 +36,7 @@ from comfy_api_nodes.util import (
validate_string,
validate_image_dimensions,
validate_image_aspect_ratio,
upload_image_to_comfyapi,
upload_images_to_comfyapi,
download_url_to_video_output,
download_url_to_image_tensor,
@ -203,10 +204,9 @@ class RunwayImageToVideoNodeGen3a(IO.ComfyNode):
validate_image_dimensions(start_frame, max_width=7999, max_height=7999)
validate_image_aspect_ratio(start_frame, (1, 2), (2, 1))
download_urls = await upload_images_to_comfyapi(
download_url = await upload_image_to_comfyapi(
cls,
start_frame,
max_images=1,
mime_type="image/png",
)
@ -220,7 +220,7 @@ class RunwayImageToVideoNodeGen3a(IO.ComfyNode):
duration=Duration(duration),
ratio=AspectRatio(ratio),
promptImage=RunwayPromptImageObject(
root=[RunwayPromptImageDetailedObject(uri=str(download_urls[0]), position="first")]
root=[RunwayPromptImageDetailedObject(uri=str(download_url), position="first")]
),
),
)
@ -297,10 +297,9 @@ class RunwayImageToVideoNodeGen4(IO.ComfyNode):
validate_image_dimensions(start_frame, max_width=7999, max_height=7999)
validate_image_aspect_ratio(start_frame, (1, 2), (2, 1))
download_urls = await upload_images_to_comfyapi(
download_url = await upload_image_to_comfyapi(
cls,
start_frame,
max_images=1,
mime_type="image/png",
)
@ -314,7 +313,7 @@ class RunwayImageToVideoNodeGen4(IO.ComfyNode):
duration=Duration(duration),
ratio=AspectRatio(ratio),
promptImage=RunwayPromptImageObject(
root=[RunwayPromptImageDetailedObject(uri=str(download_urls[0]), position="first")]
root=[RunwayPromptImageDetailedObject(uri=str(download_url), position="first")]
),
),
estimated_duration=AVERAGE_DURATION_FLF_SECONDS,
@ -488,13 +487,12 @@ class RunwayTextToImageNode(IO.ComfyNode):
if reference_image is not None:
validate_image_dimensions(reference_image, max_width=7999, max_height=7999)
validate_image_aspect_ratio(reference_image, (1, 2), (2, 1))
download_urls = await upload_images_to_comfyapi(
download_url = await upload_image_to_comfyapi(
cls,
reference_image,
max_images=1,
mime_type="image/png",
)
reference_images = [ReferenceImage(uri=str(download_urls[0]))]
reference_images = [ReferenceImage(uri=str(download_url))]
initial_response = await sync_op(
cls,

View File

@ -31,7 +31,7 @@ from comfy_api_nodes.util import (
get_number_of_images,
poll_op,
sync_op,
upload_images_to_comfyapi,
upload_image_to_comfyapi,
validate_container_format_is_mp4,
)
@ -169,9 +169,7 @@ class TopazImageEnhance(IO.ComfyNode):
) -> IO.NodeOutput:
if get_number_of_images(image) != 1:
raise ValueError("Only one input image is supported.")
download_url = await upload_images_to_comfyapi(
cls, image, max_images=1, mime_type="image/png", total_pixels=4096 * 4096
)
download_url = await upload_image_to_comfyapi(cls, image, mime_type="image/png", total_pixels=4096 * 4096)
initial_response = await sync_op(
cls,
ApiEndpoint(path="/proxy/topaz/image/v1/enhance-gen/async", method="POST"),
@ -189,7 +187,7 @@ class TopazImageEnhance(IO.ComfyNode):
creativity=creativity,
face_preservation=str(face_preservation).lower(),
color_preservation=str(color_preservation).lower(),
source_url=download_url[0],
source_url=download_url,
output_format="png",
),
content_type="multipart/form-data",

View File

@ -29,7 +29,7 @@ from comfy_api_nodes.util import (
download_url_as_bytesio,
poll_op,
sync_op,
upload_images_to_comfyapi,
upload_image_to_comfyapi,
)
from folder_paths import get_output_directory
@ -298,7 +298,7 @@ class TripoImageToModelNode(IO.ComfyNode):
raise RuntimeError("Image is required")
tripo_file = TripoFileReference(
root=TripoUrlReference(
url=(await upload_images_to_comfyapi(cls, image, max_images=1))[0],
url=await upload_image_to_comfyapi(cls, image),
type="jpeg",
)
)
@ -438,9 +438,7 @@ class TripoMultiviewToModelNode(IO.ComfyNode):
if image_ is not None:
images.append(
TripoFileReference(
root=TripoUrlReference(
url=(await upload_images_to_comfyapi(cls, image_, max_images=1))[0], type="jpeg"
)
root=TripoUrlReference(url=await upload_image_to_comfyapi(cls, image_), type="jpeg")
)
)
else:
@ -637,7 +635,7 @@ class TripoRetargetNode(IO.ComfyNode):
"preset:hexapod:walk",
"preset:octopod:walk",
"preset:serpentine:march",
"preset:aquatic:march"
"preset:aquatic:march",
],
),
],
@ -841,7 +839,7 @@ class TripoConversionNode(IO.ComfyNode):
# Parse part_names from comma-separated string to list
part_names_list = None
if part_names and part_names.strip():
part_names_list = [name.strip() for name in part_names.split(',') if name.strip()]
part_names_list = [name.strip() for name in part_names.split(",") if name.strip()]
response = await sync_op(
cls,

View File

@ -14,6 +14,7 @@ from comfy_api_nodes.util import (
get_number_of_images,
poll_op,
sync_op,
upload_image_to_comfyapi,
upload_images_to_comfyapi,
validate_image_aspect_ratio,
validate_image_dimensions,
@ -462,8 +463,7 @@ class ViduStartEndToVideoNode(IO.ComfyNode):
movement_amplitude=movement_amplitude,
)
payload.images = [
(await upload_images_to_comfyapi(cls, frame, max_images=1, mime_type="image/png"))[0]
for frame in (first_frame, end_frame)
await upload_image_to_comfyapi(cls, frame, mime_type="image/png") for frame in (first_frame, end_frame)
]
results = await execute_task(cls, VIDU_START_END_VIDEO, payload)
return IO.NodeOutput(await download_url_to_video_output(results[0].url))
@ -932,8 +932,7 @@ class Vidu2StartEndToVideoNode(IO.ComfyNode):
resolution=resolution,
movement_amplitude=movement_amplitude,
images=[
(await upload_images_to_comfyapi(cls, frame, max_images=1, mime_type="image/png"))[0]
for frame in (first_frame, end_frame)
await upload_image_to_comfyapi(cls, frame, mime_type="image/png") for frame in (first_frame, end_frame)
],
)
results = await execute_task(cls, VIDU_START_END_VIDEO, payload)

View File

@ -33,6 +33,7 @@ from .download_helpers import (
from .upload_helpers import (
upload_audio_to_comfyapi,
upload_file_to_comfyapi,
upload_image_to_comfyapi,
upload_images_to_comfyapi,
upload_video_to_comfyapi,
)
@ -61,6 +62,7 @@ __all__ = [
# Upload helpers
"upload_audio_to_comfyapi",
"upload_file_to_comfyapi",
"upload_image_to_comfyapi",
"upload_images_to_comfyapi",
"upload_video_to_comfyapi",
# Download helpers

View File

@ -88,6 +88,28 @@ async def upload_images_to_comfyapi(
return download_urls
async def upload_image_to_comfyapi(
cls: type[IO.ComfyNode],
image: torch.Tensor,
*,
mime_type: str | None = None,
wait_label: str | None = "Uploading",
total_pixels: int = 2048 * 2048,
) -> str:
"""Uploads a single image to ComfyUI API and returns its download URL."""
return (
await upload_images_to_comfyapi(
cls,
image,
max_images=1,
mime_type=mime_type,
wait_label=wait_label,
show_batch_index=False,
total_pixels=total_pixels,
)
)[0]
async def upload_audio_to_comfyapi(
cls: type[IO.ComfyNode],
audio: Input.Audio,