From 52b34696062121709ba082554c893bec0f3160b7 Mon Sep 17 00:00:00 2001 From: Chenlei Hu Date: Wed, 5 Mar 2025 15:33:23 -0500 Subject: [PATCH 1/3] [NodeDef] Explicitly add control_after_generate to seed/noise_seed (#7059) * [NodeDef] Explicitly add control_after_generate to seed/noise_seed * Update comfy/comfy_types/node_typing.py Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com> --------- Co-authored-by: filtered <176114999+webfiltered@users.noreply.github.com> --- comfy/comfy_types/node_typing.py | 2 ++ comfy_extras/nodes_custom_sampler.py | 16 +++++++++++----- nodes.py | 4 ++-- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/comfy/comfy_types/node_typing.py b/comfy/comfy_types/node_typing.py index 0696dbe5e..6146b70f8 100644 --- a/comfy/comfy_types/node_typing.py +++ b/comfy/comfy_types/node_typing.py @@ -134,6 +134,8 @@ class InputTypeOptions(TypedDict): """ remote: RemoteInputOptions """Specifies the configuration for a remote input.""" + control_after_generate: bool + """Specifies whether a control widget should be added to the input, adding options to automatically change the value after each prompt is queued. Currently only used for INT and COMBO types.""" class HiddenInputTypeDict(TypedDict): diff --git a/comfy_extras/nodes_custom_sampler.py b/comfy_extras/nodes_custom_sampler.py index 576fc3b2c..c9689b745 100644 --- a/comfy_extras/nodes_custom_sampler.py +++ b/comfy_extras/nodes_custom_sampler.py @@ -454,7 +454,7 @@ class SamplerCustom: return {"required": {"model": ("MODEL",), "add_noise": ("BOOLEAN", {"default": True}), - "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}), "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}), "positive": ("CONDITIONING", ), "negative": ("CONDITIONING", ), @@ -605,10 +605,16 @@ class DisableNoise: class RandomNoise(DisableNoise): @classmethod def INPUT_TYPES(s): - return {"required":{ - "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), - } - } + return { + "required": { + "noise_seed": ("INT", { + "default": 0, + "min": 0, + "max": 0xffffffffffffffff, + "control_after_generate": True, + }), + } + } def get_noise(self, noise_seed): return (Noise_RandomNoise(noise_seed),) diff --git a/nodes.py b/nodes.py index f7f6cb156..dec6cdc86 100644 --- a/nodes.py +++ b/nodes.py @@ -1519,7 +1519,7 @@ class KSampler: return { "required": { "model": ("MODEL", {"tooltip": "The model used for denoising the input latent."}), - "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "tooltip": "The random seed used for creating the noise."}), + "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True, "tooltip": "The random seed used for creating the noise."}), "steps": ("INT", {"default": 20, "min": 1, "max": 10000, "tooltip": "The number of steps used in the denoising process."}), "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01, "tooltip": "The Classifier-Free Guidance scale balances creativity and adherence to the prompt. Higher values result in images more closely matching the prompt however too high values will negatively impact quality."}), "sampler_name": (comfy.samplers.KSampler.SAMPLERS, {"tooltip": "The algorithm used when sampling, this can affect the quality, speed, and style of the generated output."}), @@ -1547,7 +1547,7 @@ class KSamplerAdvanced: return {"required": {"model": ("MODEL",), "add_noise": (["enable", "disable"], ), - "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), + "noise_seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "control_after_generate": True}), "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step":0.1, "round": 0.01}), "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ), From c1909f350fb2eef4d4fd87b54f87f042a6bceba5 Mon Sep 17 00:00:00 2001 From: Silver <65376327+silveroxides@users.noreply.github.com> Date: Wed, 5 Mar 2025 21:34:22 +0100 Subject: [PATCH 2/3] Better argument handling of front-end-root (#7043) * Better argument handling of front-end-root Improves handling of front-end-root launch argument. Several instances where users have set it and ComfyUI launches as normal and completely disregards the launch arg which doesn't make sense. Better to indicate to user that something is incorrect. * Removed unused import There was no real reason to use "Optional" typing in ther front-end-root argument. --- comfy/cli_args.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/comfy/cli_args.py b/comfy/cli_args.py index c99c9e65e..a864205be 100644 --- a/comfy/cli_args.py +++ b/comfy/cli_args.py @@ -1,7 +1,6 @@ import argparse import enum import os -from typing import Optional import comfy.options @@ -166,13 +165,14 @@ parser.add_argument( """, ) -def is_valid_directory(path: Optional[str]) -> Optional[str]: - """Validate if the given path is a directory.""" - if path is None: - return None - +def is_valid_directory(path: str) -> str: + """Validate if the given path is a directory, and check permissions.""" + if not os.path.exists(path): + raise argparse.ArgumentTypeError(f"The path '{path}' does not exist.") if not os.path.isdir(path): - raise argparse.ArgumentTypeError(f"{path} is not a valid directory.") + raise argparse.ArgumentTypeError(f"'{path}' is not a directory.") + if not os.access(path, os.R_OK): + raise argparse.ArgumentTypeError(f"You do not have read permissions for '{path}'.") return path parser.add_argument( From 5d84607bf3a761d796fb0cf3b6fdba8480ead5f7 Mon Sep 17 00:00:00 2001 From: Chenlei Hu Date: Wed, 5 Mar 2025 15:35:26 -0500 Subject: [PATCH 3/3] Add type hint for FileLocator (#6968) * Add type hint for FileLocator * nit --- comfy/comfy_types/__init__.py | 3 ++- comfy/comfy_types/node_typing.py | 11 +++++++++++ comfy_extras/nodes_audio.py | 5 ++++- comfy_extras/nodes_images.py | 6 +++++- comfy_extras/nodes_video.py | 5 ++++- nodes.py | 4 ++-- 6 files changed, 28 insertions(+), 6 deletions(-) diff --git a/comfy/comfy_types/__init__.py b/comfy/comfy_types/__init__.py index 19ec33f98..7640fbe3f 100644 --- a/comfy/comfy_types/__init__.py +++ b/comfy/comfy_types/__init__.py @@ -1,6 +1,6 @@ import torch from typing import Callable, Protocol, TypedDict, Optional, List -from .node_typing import IO, InputTypeDict, ComfyNodeABC, CheckLazyMixin +from .node_typing import IO, InputTypeDict, ComfyNodeABC, CheckLazyMixin, FileLocator class UnetApplyFunction(Protocol): @@ -42,4 +42,5 @@ __all__ = [ InputTypeDict.__name__, ComfyNodeABC.__name__, CheckLazyMixin.__name__, + FileLocator.__name__, ] diff --git a/comfy/comfy_types/node_typing.py b/comfy/comfy_types/node_typing.py index 6146b70f8..fe130567d 100644 --- a/comfy/comfy_types/node_typing.py +++ b/comfy/comfy_types/node_typing.py @@ -295,3 +295,14 @@ class CheckLazyMixin: need = [name for name in kwargs if kwargs[name] is None] return need + + +class FileLocator(TypedDict): + """Provides type hinting for the file location""" + + filename: str + """The filename of the file.""" + subfolder: str + """The subfolder of the file.""" + type: Literal["input", "output", "temp"] + """The root folder of the file.""" diff --git a/comfy_extras/nodes_audio.py b/comfy_extras/nodes_audio.py index 3cb918e09..136ad6159 100644 --- a/comfy_extras/nodes_audio.py +++ b/comfy_extras/nodes_audio.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import torchaudio import torch import comfy.model_management @@ -10,6 +12,7 @@ import random import hashlib import node_helpers from comfy.cli_args import args +from comfy.comfy_types import FileLocator class EmptyLatentAudio: def __init__(self): @@ -164,7 +167,7 @@ class SaveAudio: def save_audio(self, audio, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None): filename_prefix += self.prefix_append full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir) - results = list() + results: list[FileLocator] = [] metadata = {} if not args.disable_metadata: diff --git a/comfy_extras/nodes_images.py b/comfy_extras/nodes_images.py index af37666b2..e11a4583a 100644 --- a/comfy_extras/nodes_images.py +++ b/comfy_extras/nodes_images.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import nodes import folder_paths from comfy.cli_args import args @@ -9,6 +11,8 @@ import numpy as np import json import os +from comfy.comfy_types import FileLocator + MAX_RESOLUTION = nodes.MAX_RESOLUTION class ImageCrop: @@ -99,7 +103,7 @@ class SaveAnimatedWEBP: method = self.methods.get(method) filename_prefix += self.prefix_append full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0]) - results = list() + results: list[FileLocator] = [] pil_images = [] for image in images: i = 255. * image.cpu().numpy() diff --git a/comfy_extras/nodes_video.py b/comfy_extras/nodes_video.py index 53920ba18..97ca513d8 100644 --- a/comfy_extras/nodes_video.py +++ b/comfy_extras/nodes_video.py @@ -1,9 +1,12 @@ +from __future__ import annotations + import os import av import torch import folder_paths import json from fractions import Fraction +from comfy.comfy_types import FileLocator class SaveWEBM: @@ -62,7 +65,7 @@ class SaveWEBM: container.mux(stream.encode()) container.close() - results = [{ + results: list[FileLocator] = [{ "filename": file, "subfolder": subfolder, "type": self.type diff --git a/nodes.py b/nodes.py index dec6cdc86..bbf49915c 100644 --- a/nodes.py +++ b/nodes.py @@ -25,7 +25,7 @@ import comfy.sample import comfy.sd import comfy.utils import comfy.controlnet -from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict +from comfy.comfy_types import IO, ComfyNodeABC, InputTypeDict, FileLocator import comfy.clip_vision @@ -479,7 +479,7 @@ class SaveLatent: file = f"{filename}_{counter:05}_.latent" - results = list() + results: list[FileLocator] = [] results.append({ "filename": file, "subfolder": subfolder,