Skip to content

Commit ca638da

Browse files
committed
* 'master' of https://github.com/zhiaixu2009/ComfyUI: Revert "This doesn't seem to be needed on chroma. (comfyanonymous#8209)" (comfyanonymous#8210) This doesn't seem to be needed on chroma. (comfyanonymous#8209) Update README ROCm text to match link (comfyanonymous#8199) Update AMD instructions in README. (comfyanonymous#8198) Remove default delimiter. (comfyanonymous#8183) Update nodes_string.py (comfyanonymous#8173) Node to add pixel space noise to an image. (comfyanonymous#8182) Validate video inputs (comfyanonymous#8133) Add missing category for T5TokenizerOption (comfyanonymous#8177) Remove useless log. (comfyanonymous#8166) Make ImagePadForOutpaint return a 3 channel mask. (comfyanonymous#8157) Remove Desktop versioning claim from README (comfyanonymous#8155)
2 parents 76910f6 + a029970 commit ca638da

File tree

9 files changed

+155
-35
lines changed

9 files changed

+155
-35
lines changed

README.md

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ ComfyUI follows a weekly release cycle every Friday, with three interconnected r
110110

111111
2. **[ComfyUI Desktop](https://github.com/Comfy-Org/desktop)**
112112
- Builds a new release using the latest stable core version
113-
- Version numbers match the core release (e.g., Desktop v1.7.0 uses Core v1.7.0)
114113

115114
3. **[ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend)**
116115
- Weekly frontend updates are merged into the core repository
@@ -198,11 +197,11 @@ Put your VAE in: models/vae
198197
### AMD GPUs (Linux only)
199198
AMD users can install rocm and pytorch with pip if you don't have it already installed, this is the command to install the stable version:
200199

201-
```pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.2.4```
200+
```pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/rocm6.3```
202201

203-
This is the command to install the nightly with ROCm 6.3 which might have some performance improvements:
202+
This is the command to install the nightly with ROCm 6.4 which might have some performance improvements:
204203

205-
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/rocm6.3```
204+
```pip install --pre torch torchvision torchaudio --index-url https://download.pytorch.org/whl/nightly/rocm6.4```
206205

207206
### Intel GPUs (Windows and Linux)
208207

comfy/utils.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ def load_torch_file(ckpt, safe_load=False, device=None, return_metadata=False):
7878
pl_sd = torch.load(ckpt, map_location=device, weights_only=True, **torch_args)
7979
else:
8080
pl_sd = torch.load(ckpt, map_location=device, pickle_module=comfy.checkpoint_pickle)
81-
if "global_step" in pl_sd:
82-
logging.debug(f"Global Step: {pl_sd['global_step']}")
8381
if "state_dict" in pl_sd:
8482
sd = pl_sd["state_dict"]
8583
else:

comfy_api_nodes/nodes_kling.py

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,12 @@
6565
download_url_to_image_tensor,
6666
)
6767
from comfy_api_nodes.mapper_utils import model_field_to_node_input
68+
from comfy_api_nodes.util.validation_utils import (
69+
validate_image_dimensions,
70+
validate_image_aspect_ratio,
71+
validate_video_dimensions,
72+
validate_video_duration,
73+
)
6874
from comfy_api.input.basic_types import AudioInput
6975
from comfy_api.input.video_types import VideoInput
7076
from comfy_api.input_impl import VideoFromFile
@@ -80,18 +86,16 @@
8086
PATH_VIRTUAL_TRY_ON = f"/proxy/kling/{KLING_API_VERSION}/images/kolors-virtual-try-on"
8187
PATH_IMAGE_GENERATIONS = f"/proxy/kling/{KLING_API_VERSION}/images/generations"
8288

83-
8489
MAX_PROMPT_LENGTH_T2V = 2500
8590
MAX_PROMPT_LENGTH_I2V = 500
8691
MAX_PROMPT_LENGTH_IMAGE_GEN = 500
8792
MAX_NEGATIVE_PROMPT_LENGTH_IMAGE_GEN = 200
8893
MAX_PROMPT_LENGTH_LIP_SYNC = 120
8994

90-
# TODO: adjust based on tests
91-
AVERAGE_DURATION_T2V = 319 # 319,
92-
AVERAGE_DURATION_I2V = 164 # 164,
93-
AVERAGE_DURATION_LIP_SYNC = 120
94-
AVERAGE_DURATION_VIRTUAL_TRY_ON = 19 # 19,
95+
AVERAGE_DURATION_T2V = 319
96+
AVERAGE_DURATION_I2V = 164
97+
AVERAGE_DURATION_LIP_SYNC = 455
98+
AVERAGE_DURATION_VIRTUAL_TRY_ON = 19
9599
AVERAGE_DURATION_IMAGE_GEN = 32
96100
AVERAGE_DURATION_VIDEO_EFFECTS = 320
97101
AVERAGE_DURATION_VIDEO_EXTEND = 320
@@ -211,23 +215,8 @@ def validate_input_image(image: torch.Tensor) -> None:
211215
212216
See: https://app.klingai.com/global/dev/document-api/apiReference/model/imageToVideo
213217
"""
214-
if len(image.shape) == 4:
215-
height, width = image.shape[1], image.shape[2]
216-
elif len(image.shape) == 3:
217-
height, width = image.shape[0], image.shape[1]
218-
else:
219-
raise ValueError("Invalid image tensor shape.")
220-
221-
# Ensure minimum resolution is met
222-
if height < 300:
223-
raise ValueError("Image height must be at least 300px")
224-
if width < 300:
225-
raise ValueError("Image width must be at least 300px")
226-
227-
# Ensure aspect ratio is within acceptable range
228-
aspect_ratio = width / height
229-
if aspect_ratio < 1 / 2.5 or aspect_ratio > 2.5:
230-
raise ValueError("Image aspect ratio must be between 1:2.5 and 2.5:1")
218+
validate_image_dimensions(image, min_width=300, min_height=300)
219+
validate_image_aspect_ratio(image, min_aspect_ratio=1 / 2.5, max_aspect_ratio=2.5)
231220

232221

233222
def get_camera_control_input_config(
@@ -1243,6 +1232,17 @@ class KlingLipSyncBase(KlingNodeBase):
12431232
RETURN_TYPES = ("VIDEO", "STRING", "STRING")
12441233
RETURN_NAMES = ("VIDEO", "video_id", "duration")
12451234

1235+
def validate_lip_sync_video(self, video: VideoInput):
1236+
"""
1237+
Validates the input video adheres to the expectations of the Kling Lip Sync API:
1238+
- Video length does not exceed 10s and is not shorter than 2s
1239+
- Length and width dimensions should both be between 720px and 1920px
1240+
1241+
See: https://app.klingai.com/global/dev/document-api/apiReference/model/videoTolip
1242+
"""
1243+
validate_video_dimensions(video, 720, 1920)
1244+
validate_video_duration(video, 2, 10)
1245+
12461246
def validate_text(self, text: str):
12471247
if not text:
12481248
raise ValueError("Text is required")
@@ -1282,6 +1282,7 @@ def api_call(
12821282
) -> tuple[VideoFromFile, str, str]:
12831283
if text:
12841284
self.validate_text(text)
1285+
self.validate_lip_sync_video(video)
12851286

12861287
# Upload video to Comfy API and get download URL
12871288
video_url = upload_video_to_comfyapi(video, auth_kwargs=kwargs)
@@ -1352,7 +1353,7 @@ def INPUT_TYPES(s):
13521353
},
13531354
}
13541355

1355-
DESCRIPTION = "Kling Lip Sync Audio to Video Node. Syncs mouth movements in a video file to the audio content of an audio file."
1356+
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."
13561357

13571358
def api_call(
13581359
self,
@@ -1464,7 +1465,7 @@ def INPUT_TYPES(s):
14641465
},
14651466
}
14661467

1467-
DESCRIPTION = "Kling Lip Sync Text to Video Node. Syncs mouth movements in a video file to a text prompt."
1468+
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."
14681469

14691470
def api_call(
14701471
self,

comfy_api_nodes/util/__init__.py

Whitespace-only changes.
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import logging
2+
from typing import Optional
3+
4+
import torch
5+
from comfy_api.input.video_types import VideoInput
6+
7+
8+
def get_image_dimensions(image: torch.Tensor) -> tuple[int, int]:
9+
if len(image.shape) == 4:
10+
return image.shape[1], image.shape[2]
11+
elif len(image.shape) == 3:
12+
return image.shape[0], image.shape[1]
13+
else:
14+
raise ValueError("Invalid image tensor shape.")
15+
16+
17+
def validate_image_dimensions(
18+
image: torch.Tensor,
19+
min_width: Optional[int] = None,
20+
max_width: Optional[int] = None,
21+
min_height: Optional[int] = None,
22+
max_height: Optional[int] = None,
23+
):
24+
height, width = get_image_dimensions(image)
25+
26+
if min_width is not None and width < min_width:
27+
raise ValueError(f"Image width must be at least {min_width}px, got {width}px")
28+
if max_width is not None and width > max_width:
29+
raise ValueError(f"Image width must be at most {max_width}px, got {width}px")
30+
if min_height is not None and height < min_height:
31+
raise ValueError(
32+
f"Image height must be at least {min_height}px, got {height}px"
33+
)
34+
if max_height is not None and height > max_height:
35+
raise ValueError(f"Image height must be at most {max_height}px, got {height}px")
36+
37+
38+
def validate_image_aspect_ratio(
39+
image: torch.Tensor,
40+
min_aspect_ratio: Optional[float] = None,
41+
max_aspect_ratio: Optional[float] = None,
42+
):
43+
width, height = get_image_dimensions(image)
44+
aspect_ratio = width / height
45+
46+
if min_aspect_ratio is not None and aspect_ratio < min_aspect_ratio:
47+
raise ValueError(
48+
f"Image aspect ratio must be at least {min_aspect_ratio}, got {aspect_ratio}"
49+
)
50+
if max_aspect_ratio is not None and aspect_ratio > max_aspect_ratio:
51+
raise ValueError(
52+
f"Image aspect ratio must be at most {max_aspect_ratio}, got {aspect_ratio}"
53+
)
54+
55+
56+
def validate_video_dimensions(
57+
video: VideoInput,
58+
min_width: Optional[int] = None,
59+
max_width: Optional[int] = None,
60+
min_height: Optional[int] = None,
61+
max_height: Optional[int] = None,
62+
):
63+
try:
64+
width, height = video.get_dimensions()
65+
except Exception as e:
66+
logging.error("Error getting dimensions of video: %s", e)
67+
return
68+
69+
if min_width is not None and width < min_width:
70+
raise ValueError(f"Video width must be at least {min_width}px, got {width}px")
71+
if max_width is not None and width > max_width:
72+
raise ValueError(f"Video width must be at most {max_width}px, got {width}px")
73+
if min_height is not None and height < min_height:
74+
raise ValueError(
75+
f"Video height must be at least {min_height}px, got {height}px"
76+
)
77+
if max_height is not None and height > max_height:
78+
raise ValueError(f"Video height must be at most {max_height}px, got {height}px")
79+
80+
81+
def validate_video_duration(
82+
video: VideoInput,
83+
min_duration: Optional[float] = None,
84+
max_duration: Optional[float] = None,
85+
):
86+
try:
87+
duration = video.get_duration()
88+
except Exception as e:
89+
logging.error("Error getting duration of video: %s", e)
90+
return
91+
92+
epsilon = 0.0001
93+
if min_duration is not None and min_duration - epsilon > duration:
94+
raise ValueError(
95+
f"Video duration must be at least {min_duration}s, got {duration}s"
96+
)
97+
if max_duration is not None and duration > max_duration + epsilon:
98+
raise ValueError(
99+
f"Video duration must be at most {max_duration}s, got {duration}s"
100+
)

comfy_extras/nodes_cond.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def INPUT_TYPES(s):
3131
}
3232
}
3333

34+
CATEGORY = "_for_testing/conditioning"
3435
RETURN_TYPES = ("CLIP",)
3536
FUNCTION = "set_options"
3637

comfy_extras/nodes_images.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import re
1414
from io import BytesIO
1515
from inspect import cleandoc
16+
import torch
1617

1718
from comfy.comfy_types import FileLocator
1819

@@ -74,6 +75,24 @@ def frombatch(self, image, batch_index, length):
7475
s = s_in[batch_index:batch_index + length].clone()
7576
return (s,)
7677

78+
79+
class ImageAddNoise:
80+
@classmethod
81+
def INPUT_TYPES(s):
82+
return {"required": { "image": ("IMAGE",),
83+
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True, "tooltip": "The random seed used for creating the noise."}),
84+
"strength": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}),
85+
}}
86+
RETURN_TYPES = ("IMAGE",)
87+
FUNCTION = "repeat"
88+
89+
CATEGORY = "image"
90+
91+
def repeat(self, image, seed, strength):
92+
generator = torch.manual_seed(seed)
93+
s = torch.clip((image + strength * torch.randn(image.size(), generator=generator, device="cpu").to(image)), min=0.0, max=1.0)
94+
return (s,)
95+
7796
class SaveAnimatedWEBP:
7897
def __init__(self):
7998
self.output_dir = folder_paths.get_output_directory()
@@ -295,6 +314,7 @@ def replacement(match):
295314
"ImageCrop": ImageCrop,
296315
"RepeatImageBatch": RepeatImageBatch,
297316
"ImageFromBatch": ImageFromBatch,
317+
"ImageAddNoise": ImageAddNoise,
298318
"SaveAnimatedWEBP": SaveAnimatedWEBP,
299319
"SaveAnimatedPNG": SaveAnimatedPNG,
300320
"SaveSVGNode": SaveSVGNode,

comfy_extras/nodes_string.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ def INPUT_TYPES(s):
88
return {
99
"required": {
1010
"string_a": (IO.STRING, {"multiline": True}),
11-
"string_b": (IO.STRING, {"multiline": True})
11+
"string_b": (IO.STRING, {"multiline": True}),
12+
"delimiter": (IO.STRING, {"multiline": False, "default": ""})
1213
}
1314
}
1415

1516
RETURN_TYPES = (IO.STRING,)
1617
FUNCTION = "execute"
1718
CATEGORY = "utils/string"
1819

19-
def execute(self, string_a, string_b, **kwargs):
20-
return string_a + string_b,
20+
def execute(self, string_a, string_b, delimiter, **kwargs):
21+
return delimiter.join((string_a, string_b)),
2122

2223
class StringSubstring():
2324
@classmethod

nodes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1940,7 +1940,7 @@ def expand_image(self, image, left, top, right, bottom, feathering):
19401940

19411941
mask[top:top + d2, left:left + d3] = t
19421942

1943-
return (new_image, mask)
1943+
return (new_image, mask.unsqueeze(0))
19441944

19451945

19461946
NODE_CLASS_MAPPINGS = {

0 commit comments

Comments
 (0)