diff --git a/comfy_api/v3/io.py b/comfy_api/v3/io.py index f85b59253..6351432ca 100644 --- a/comfy_api/v3/io.py +++ b/comfy_api/v3/io.py @@ -202,6 +202,7 @@ class WidgetInputV3(InputV3): return super().as_dict_V1() | prune_dict({ "default": self.default, "socketless": self.socketless, + "widgetType": self.widgetType, "forceInput": self.force_input, }) @@ -210,7 +211,7 @@ class WidgetInputV3(InputV3): class OutputV3(IO_V3): - def __init__(self, id: str, display_name: str=None, tooltip: str=None, + def __init__(self, id: str=None, display_name: str=None, tooltip: str=None, is_output_list=False): self.id = id self.display_name = display_name @@ -296,7 +297,7 @@ class Boolean: def __init__(self, id: str, display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None, default: bool=None, label_on: str=None, label_off: str=None, socketless: bool=None, force_input: bool=None): - super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, self.io_type, force_input) + super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, None, force_input) self.label_on = label_on self.label_off = label_off self.default: bool @@ -319,7 +320,7 @@ class Int: def __init__(self, id: str, display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None, default: int=None, min: int=None, max: int=None, step: int=None, control_after_generate: bool=None, display_mode: NumberDisplay=None, socketless: bool=None, force_input: bool=None): - super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, self.io_type, force_input) + super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, None, force_input) self.min = min self.max = max self.step = step @@ -348,7 +349,7 @@ class Float(ComfyTypeIO): def __init__(self, id: str, display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None, default: float=None, min: float=None, max: float=None, step: float=None, round: float=None, display_mode: NumberDisplay=None, socketless: bool=None, force_input: bool=None): - super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, self.io_type, force_input) + super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, None, force_input) self.min = min self.max = max self.step = step @@ -374,7 +375,7 @@ class String(ComfyTypeIO): def __init__(self, id: str, display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None, multiline=False, placeholder: str=None, default: str=None, socketless: bool=None, force_input: bool=None): - super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, self.io_type, force_input) + super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, None, force_input) self.multiline = multiline self.placeholder = placeholder self.default: str @@ -396,7 +397,7 @@ class Combo(ComfyType): image_upload: bool=None, image_folder: FolderType=None, content_types: list[Literal["image", "video", "audio", "model"]]=None, remote: RemoteOptions=None, socketless: bool=None): - super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, self.io_type) + super().__init__(id, display_name, optional, tooltip, lazy, default, socketless) self.multiselect = False self.options = options self.control_after_generate = control_after_generate @@ -456,7 +457,7 @@ class Webcam(ComfyTypeIO): self, id: str, display_name: str=None, optional=False, tooltip: str=None, lazy: bool=None, default: str=None, socketless: bool=None ): - super().__init__(id, display_name, optional, tooltip, lazy, default, socketless, self.io_type) + super().__init__(id, display_name, optional, tooltip, lazy, default, socketless) @comfytype(io_type="MASK") @@ -739,12 +740,15 @@ class MultiType: # if id is an Input, then use that Input with overridden values self.input_override = None if isinstance(id, InputV3): - self.input_override = id + self.input_override = copy.copy(id) optional = id.optional if id.optional is True else optional tooltip = id.tooltip if id.tooltip is not None else tooltip display_name = id.display_name if id.display_name is not None else display_name lazy = id.lazy if id.lazy is not None else lazy id = id.id + # if is a widget input, make sure widgetType is set appropriately + if isinstance(self.input_override, WidgetInputV3): + self.input_override.widgetType = self.input_override.get_io_type_V1() super().__init__(id, display_name, optional, tooltip, lazy, extra_dict) self._io_types = types @@ -786,6 +790,10 @@ class DynamicOutput(OutputV3, ABC): ''' Abstract class for dynamic output registration. ''' + def __init__(self, id: str, display_name: str=None, tooltip: str=None, + is_output_list=False): + super().__init__(id, display_name, tooltip, is_output_list) + @abstractmethod def get_dynamic(self) -> list[OutputV3]: ... @@ -987,7 +995,7 @@ class SchemaV3: raise ValueError("\n".join(issues)) def finalize(self): - """Add hidden based on selected schema options.""" + """Add hidden based on selected schema options, and give outputs without ids default ids.""" # if is an api_node, will need key-related hidden if self.is_api_node: if self.hidden is None: @@ -1004,6 +1012,11 @@ class SchemaV3: self.hidden.append(Hidden.prompt) if Hidden.extra_pnginfo not in self.hidden: self.hidden.append(Hidden.extra_pnginfo) + # give outputs without ids default ids + if self.outputs is not None: + for i, output in enumerate(self.outputs): + if output.id is None: + output.id = f"_{i}_{output.io_type}_" class Serializer: diff --git a/comfy_api/v3/ui.py b/comfy_api/v3/ui.py index ed2f5d555..ac2a9b15b 100644 --- a/comfy_api/v3/ui.py +++ b/comfy_api/v3/ui.py @@ -27,7 +27,7 @@ class SavedResult: } class PreviewImage(_UIOutput): - def __init__(self, image: Image.Type, animated: bool=False, node: ComfyNodeV3=None, **kwargs): + def __init__(self, image: Image.Type, animated: bool=False, cls: ComfyNodeV3=None, **kwargs): output_dir = folder_paths.get_temp_directory() type = "temp" prefix_append = "_temp_" + ''.join(random.choice("abcdefghijklmnopqrstupvxyz") for x in range(5)) @@ -41,13 +41,13 @@ class PreviewImage(_UIOutput): i = 255. * image.cpu().numpy() img = PILImage.fromarray(np.clip(i, 0, 255).astype(np.uint8)) metadata = None - if not args.disable_metadata and node is not None: + if not args.disable_metadata and cls is not None: metadata = PngInfo() - if node.hidden.prompt is not None: - metadata.add_text("prompt", json.dumps(node.hidden.prompt)) - if node.hidden.extra_pnginfo is not None: - for x in node.hidden.extra_pnginfo: - metadata.add_text(x, json.dumps(node.hidden.extra_pnginfo[x])) + if cls.hidden.prompt is not None: + metadata.add_text("prompt", json.dumps(cls.hidden.prompt)) + if cls.hidden.extra_pnginfo is not None: + for x in cls.hidden.extra_pnginfo: + metadata.add_text(x, json.dumps(cls.hidden.extra_pnginfo[x])) filename_with_batch_num = filename.replace("%batch_num%", str(batch_number)) file = f"{filename_with_batch_num}_{counter:05}_.png" @@ -66,9 +66,9 @@ class PreviewImage(_UIOutput): } class PreviewMask(PreviewImage): - def __init__(self, mask: PreviewMask.Type, animated: bool=False, node: ComfyNodeV3=None, **kwargs): + def __init__(self, mask: PreviewMask.Type, animated: bool=False, cls: ComfyNodeV3=None, **kwargs): preview = mask.reshape((-1, 1, mask.shape[-2], mask.shape[-1])).movedim(1, -1).expand(-1, -1, -1, 3) - super().__init__(preview, animated, node, **kwargs) + super().__init__(preview, animated, cls, **kwargs) # class UILatent(_UIOutput): # def __init__(self, values: list[SavedResult | dict], **kwargs): diff --git a/comfy_extras/nodes_v3_test.py b/comfy_extras/nodes_v3_test.py index 55f179f2a..426a5e3f8 100644 --- a/comfy_extras/nodes_v3_test.py +++ b/comfy_extras/nodes_v3_test.py @@ -62,8 +62,8 @@ class V3TestNode(io.ComfyNodeV3): # ]] ], outputs=[ - io.Int.Output("int_output"), - io.Image.Output("img_output", display_name="img🖼️", tooltip="This is an image"), + io.Int.Output(), + io.Image.Output(display_name="img🖼️", tooltip="This is an image"), ], hidden=[ io.Hidden.prompt, @@ -105,7 +105,7 @@ class V3TestNode(io.ComfyNodeV3): if hasattr(cls, "doohickey"): raise Exception("The 'cls' variable leaked state on class properties between runs!") cls.doohickey = "LOLJK" - return io.NodeOutput(some_int, image, ui=ui.PreviewImage(image)) + return io.NodeOutput(some_int, image, ui=ui.PreviewImage(image, cls=cls)) class V3LoraLoader(io.ComfyNodeV3): @@ -142,8 +142,8 @@ class V3LoraLoader(io.ComfyNodeV3): ), ], outputs=[ - io.Model.Output("model_out"), - io.Clip.Output("clip_out"), + io.Model.Output(), + io.Clip.Output(), ], ) @@ -169,7 +169,7 @@ class NInputsTest(io.ComfyNodeV3): io.AutogrowDynamic.Input("nmock2", template_input=io.Int.Input("int"), optional=True, min=1, max=4), ], outputs=[ - io.Image.Output("image_out"), + io.Image.Output(), ], ) diff --git a/comfy_extras/v3/nodes_images.py b/comfy_extras/v3/nodes_images.py index 94891c1c4..105904581 100644 --- a/comfy_extras/v3/nodes_images.py +++ b/comfy_extras/v3/nodes_images.py @@ -24,7 +24,6 @@ class SaveImage_V3(io.ComfyNodeV3): inputs=[ io.Image.Input( "images", - display_name="images", tooltip="The images to save.", ), io.String.Input( @@ -79,7 +78,6 @@ class PreviewImage_V3(io.ComfyNodeV3): inputs=[ io.Image.Input( "images", - display_name="images", tooltip="The images to preview.", ), ], @@ -89,7 +87,7 @@ class PreviewImage_V3(io.ComfyNodeV3): @classmethod def execute(cls, images): - return io.NodeOutput(ui=ui.PreviewImage(images)) + return io.NodeOutput(ui=ui.PreviewImage(images, cls=cls)) class LoadImage_V3(io.ComfyNodeV3): @@ -102,7 +100,6 @@ class LoadImage_V3(io.ComfyNodeV3): inputs=[ io.Combo.Input( "image", - display_name="image", image_upload=True, image_folder=io.FolderType.input, content_types=["image"], @@ -110,12 +107,8 @@ class LoadImage_V3(io.ComfyNodeV3): ), ], outputs=[ - io.Image.Output( - "IMAGE", - ), - io.Mask.Output( - "MASK", - ), + io.Image.Output(), + io.Mask.Output(), ], ) @@ -199,7 +192,6 @@ class LoadImageOutput_V3(io.ComfyNodeV3): inputs=[ io.Combo.Input( "image", - display_name="image", image_upload=True, image_folder=io.FolderType.output, content_types=["image"], @@ -211,12 +203,8 @@ class LoadImageOutput_V3(io.ComfyNodeV3): ), ], outputs=[ - io.Image.Output( - "IMAGE", - ), - io.Mask.Output( - "MASK", - ), + io.Image.Output(), + io.Mask.Output(), ], ) diff --git a/comfy_extras/v3/nodes_mask.py b/comfy_extras/v3/nodes_mask.py index a3ff64e72..90cbb3b59 100644 --- a/comfy_extras/v3/nodes_mask.py +++ b/comfy_extras/v3/nodes_mask.py @@ -12,13 +12,10 @@ class MaskPreview_V3(io.ComfyNodeV3): def DEFINE_SCHEMA(cls): return io.SchemaV3( node_id="MaskPreview_V3", - display_name="Convert Mask to Image _V3", + display_name="Preview Mask _V3", category="mask", inputs=[ - io.Mask.Input( - "masks", - display_name="masks", - ), + io.Mask.Input("masks"), ], hidden=[io.Hidden.prompt, io.Hidden.extra_pnginfo], is_output_node=True, diff --git a/comfy_extras/v3/nodes_webcam.py b/comfy_extras/v3/nodes_webcam.py index 2624cca1b..3a4cf8da0 100644 --- a/comfy_extras/v3/nodes_webcam.py +++ b/comfy_extras/v3/nodes_webcam.py @@ -21,13 +21,9 @@ class WebcamCapture_V3(io.ComfyNodeV3): display_name="Webcam Capture _V3", category="image", inputs=[ - io.Webcam.Input( - "image", - display_name="image", - ), + io.Webcam.Input("image"), io.Int.Input( "width", - display_name="width", default=0, min=0, max=MAX_RESOLUTION, @@ -35,21 +31,15 @@ class WebcamCapture_V3(io.ComfyNodeV3): ), io.Int.Input( "height", - display_name="height", default=0, min=0, max=MAX_RESOLUTION, step=1, ), - io.Boolean.Input( - "capture_on_queue", - default=True, - ), + io.Boolean.Input("capture_on_queue", default=True), ], outputs=[ - io.Image.Output( - "IMAGE", - ), + io.Image.Output(), ], )