diff --git a/__init__.py b/__init__.py index fcd3fa7..64c9eb4 100644 --- a/__init__.py +++ b/__init__.py @@ -1,8 +1,10 @@ -from .adv_control.nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS +from .adv_control.nodes import AdvancedControlNetExtension from .adv_control.dinklink import init_dinklink from .adv_control.sampling import prepare_dinklink_acn_wrapper -__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS'] - init_dinklink() prepare_dinklink_acn_wrapper() + + +async def comfy_entrypoint() -> AdvancedControlNetExtension: + return AdvancedControlNetExtension() diff --git a/adv_control/nodes.py b/adv_control/nodes.py index 18eea35..565fbe6 100644 --- a/adv_control/nodes.py +++ b/adv_control/nodes.py @@ -1,4 +1,4 @@ -import comfy.sample +from comfy_api.latest import ComfyExtension, io from .nodes_main import (ControlNetLoaderAdvanced, DiffControlNetLoaderAdvanced, AnimaLLLiteLoaderAdvanced, AdvancedControlNetApply, AdvancedControlNetApplySingle) @@ -12,132 +12,62 @@ from .nodes_sparsectrl import SparseCtrlMergedLoaderAdvanced, SparseCtrlLoaderAd from .nodes_reference import ReferenceControlNetNode, ReferenceControlFinetune, ReferencePreprocessorNode from .nodes_plusplus import PlusPlusLoaderAdvanced, PlusPlusLoaderSingle, PlusPlusInputNode from .nodes_ctrlora import CtrLoRALoader -from .nodes_loosecontrol import ControlNetLoaderWithLoraAdvanced from .nodes_deprecated import (LoadImagesFromDirectory, ScaledSoftUniversalWeightsDeprecated, SoftControlNetWeightsDeprecated, CustomControlNetWeightsDeprecated, SoftT2IAdapterWeightsDeprecated, CustomT2IAdapterWeightsDeprecated, AdvancedControlNetApplyDEPR, AdvancedControlNetApplySingleDEPR, ControlNetLoaderAdvancedDEPR, DiffControlNetLoaderAdvancedDEPR) -from .logger import logger -# NODE MAPPING -NODE_CLASS_MAPPINGS = { - # Keyframes - "TimestepKeyframe": TimestepKeyframeNode, - "ACN_TimestepKeyframeInterpolation": TimestepKeyframeInterpolationNode, - "ACN_TimestepKeyframeFromStrengthList": TimestepKeyframeFromStrengthListNode, - "LatentKeyframe": LatentKeyframeNode, - "LatentKeyframeTiming": LatentKeyframeInterpolationNode, - "LatentKeyframeBatchedGroup": LatentKeyframeBatchedGroupNode, - "LatentKeyframeGroup": LatentKeyframeGroupNode, - # Conditioning - "ACN_AdvancedControlNetApply_v2": AdvancedControlNetApply, - "ACN_AdvancedControlNetApplySingle_v2": AdvancedControlNetApplySingle, - # Loaders - "ACN_ControlNetLoaderAdvanced": ControlNetLoaderAdvanced, - "ACN_DiffControlNetLoaderAdvanced": DiffControlNetLoaderAdvanced, - "ACN_AnimaLLLiteLoaderAdvanced": AnimaLLLiteLoaderAdvanced, - # Weights - "ACN_ScaledSoftControlNetWeights": ScaledSoftUniversalWeights, - "ScaledSoftMaskedUniversalWeights": ScaledSoftMaskedUniversalWeights, - "ACN_SoftControlNetWeightsSD15": SoftControlNetWeightsSD15, - "ACN_CustomControlNetWeightsSD15": CustomControlNetWeightsSD15, - "ACN_CustomControlNetWeightsFlux": CustomControlNetWeightsFlux, - "ACN_CustomControlNetWeightsAnima": CustomControlNetWeightsAnima, - "ACN_SoftT2IAdapterWeights": SoftT2IAdapterWeights, - "ACN_CustomT2IAdapterWeights": CustomT2IAdapterWeights, - "ACN_DefaultUniversalWeights": DefaultWeights, - "ACN_ExtrasMiddleMult": ExtrasMiddleMultNode, - "ACN_AnimaLLLiteExtras": AnimaLLLiteExtras, - # SparseCtrl - "ACN_SparseCtrlRGBPreprocessor": RgbSparseCtrlPreprocessor, - "ACN_SparseCtrlLoaderAdvanced": SparseCtrlLoaderAdvanced, - "ACN_SparseCtrlMergedLoaderAdvanced": SparseCtrlMergedLoaderAdvanced, - "ACN_SparseCtrlIndexMethodNode": SparseIndexMethodNode, - "ACN_SparseCtrlSpreadMethodNode": SparseSpreadMethodNode, - "ACN_SparseCtrlWeightExtras": SparseWeightExtras, - # ControlNet++ - "ACN_ControlNet++LoaderSingle": PlusPlusLoaderSingle, - "ACN_ControlNet++LoaderAdvanced": PlusPlusLoaderAdvanced, - "ACN_ControlNet++InputNode": PlusPlusInputNode, - # CtrLoRA - "ACN_CtrLoRALoader": CtrLoRALoader, - # Reference - "ACN_ReferencePreprocessor": ReferencePreprocessorNode, - "ACN_ReferenceControlNet": ReferenceControlNetNode, - "ACN_ReferenceControlNetFinetune": ReferenceControlFinetune, - # LOOSEControl - #"ACN_ControlNetLoaderWithLoraAdvanced": ControlNetLoaderWithLoraAdvanced, - # Deprecated - "LoadImagesFromDirectory": LoadImagesFromDirectory, - "ScaledSoftControlNetWeights": ScaledSoftUniversalWeightsDeprecated, - "SoftControlNetWeights": SoftControlNetWeightsDeprecated, - "CustomControlNetWeights": CustomControlNetWeightsDeprecated, - "SoftT2IAdapterWeights": SoftT2IAdapterWeightsDeprecated, - "CustomT2IAdapterWeights": CustomT2IAdapterWeightsDeprecated, - "ACN_AdvancedControlNetApply": AdvancedControlNetApplyDEPR, - "ACN_AdvancedControlNetApplySingle": AdvancedControlNetApplySingleDEPR, - "ControlNetLoaderAdvanced": ControlNetLoaderAdvancedDEPR, - "DiffControlNetLoaderAdvanced": DiffControlNetLoaderAdvancedDEPR, -} -NODE_DISPLAY_NAME_MAPPINGS = { - # Keyframes - "TimestepKeyframe": "Timestep Keyframe ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_TimestepKeyframeInterpolation": "Timestep Keyframe Interp. ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_TimestepKeyframeFromStrengthList": "Timestep Keyframe From List ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "LatentKeyframe": "Latent Keyframe ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "LatentKeyframeTiming": "Latent Keyframe Interp. ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "LatentKeyframeBatchedGroup": "Latent Keyframe From List ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "LatentKeyframeGroup": "Latent Keyframe Group ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # Conditioning - "ACN_AdvancedControlNetApply_v2": "Apply Advanced ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_AdvancedControlNetApplySingle_v2": "Apply Advanced ControlNet(1) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # Loaders - "ACN_ControlNetLoaderAdvanced": "Load Advanced ControlNet Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_DiffControlNetLoaderAdvanced": "Load Advanced ControlNet Model (diff) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_AnimaLLLiteLoaderAdvanced": "Load Anima LLLite Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # Weights - "ACN_ScaledSoftControlNetWeights": "Scaled Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ScaledSoftMaskedUniversalWeights": "Scaled Soft Masked Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_SoftControlNetWeightsSD15": "ControlNet Soft Weights [SD1.5] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_CustomControlNetWeightsSD15": "ControlNet Custom Weights [SD1.5] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_CustomControlNetWeightsFlux": "ControlNet Custom Weights [Flux] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_CustomControlNetWeightsAnima": "ControlNet Custom Weights [Anima] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_SoftT2IAdapterWeights": "T2IAdapter Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_CustomT2IAdapterWeights": "T2IAdapter Custom Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_DefaultUniversalWeights": "Default Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_ExtrasMiddleMult": "Middle Weight Extras ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_AnimaLLLiteExtras": "Anima LLLite Extras ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # SparseCtrl - "ACN_SparseCtrlRGBPreprocessor": "RGB SparseCtrl ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_SparseCtrlLoaderAdvanced": "Load SparseCtrl Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_SparseCtrlMergedLoaderAdvanced": "๐ŸงชLoad Merged SparseCtrl Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_SparseCtrlIndexMethodNode": "SparseCtrl Index Method ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_SparseCtrlSpreadMethodNode": "SparseCtrl Spread Method ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_SparseCtrlWeightExtras": "SparseCtrl Weight Extras ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # ControlNet++ - "ACN_ControlNet++LoaderSingle": "Load ControlNet++ Model (Single) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_ControlNet++LoaderAdvanced": "Load ControlNet++ Model (Multi) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_ControlNet++InputNode": "ControlNet++ Input ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # CtrLoRA - "ACN_CtrLoRALoader": "Load CtrLoRA Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # Reference - "ACN_ReferencePreprocessor": "Reference Preproccessor ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_ReferenceControlNet": "Reference ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_ReferenceControlNetFinetune": "Reference ControlNet (Finetune) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # LOOSEControl - #"ACN_ControlNetLoaderWithLoraAdvanced": "Load Adv. ControlNet Model w/ LoRA ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - # Deprecated - "LoadImagesFromDirectory": "๐ŸšซLoad Images [DEPRECATED] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ScaledSoftControlNetWeights": "Scaled Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "SoftControlNetWeights": "ControlNet Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "CustomControlNetWeights": "ControlNet Custom Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "SoftT2IAdapterWeights": "T2IAdapter Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "CustomT2IAdapterWeights": "T2IAdapter Custom Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_AdvancedControlNetApply": "Apply Advanced ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ACN_AdvancedControlNetApplySingle": "Apply Advanced ControlNet(1) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "ControlNetLoaderAdvanced": "Load Advanced ControlNet Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", - "DiffControlNetLoaderAdvanced": "Load Advanced ControlNet Model (diff) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…", -} + +class AdvancedControlNetExtension(ComfyExtension): + async def get_node_list(self) -> list[type[io.ComfyNode]]: + return [ + TimestepKeyframeNode, + TimestepKeyframeInterpolationNode, + TimestepKeyframeFromStrengthListNode, + LatentKeyframeNode, + LatentKeyframeInterpolationNode, + LatentKeyframeBatchedGroupNode, + LatentKeyframeGroupNode, + AdvancedControlNetApply, + AdvancedControlNetApplySingle, + ControlNetLoaderAdvanced, + DiffControlNetLoaderAdvanced, + AnimaLLLiteLoaderAdvanced, + ScaledSoftUniversalWeights, + ScaledSoftMaskedUniversalWeights, + SoftControlNetWeightsSD15, + CustomControlNetWeightsSD15, + CustomControlNetWeightsFlux, + CustomControlNetWeightsAnima, + SoftT2IAdapterWeights, + CustomT2IAdapterWeights, + DefaultWeights, + ExtrasMiddleMultNode, + AnimaLLLiteExtras, + RgbSparseCtrlPreprocessor, + SparseCtrlLoaderAdvanced, + SparseCtrlMergedLoaderAdvanced, + SparseIndexMethodNode, + SparseSpreadMethodNode, + SparseWeightExtras, + PlusPlusLoaderSingle, + PlusPlusLoaderAdvanced, + PlusPlusInputNode, + CtrLoRALoader, + ReferencePreprocessorNode, + ReferenceControlNetNode, + ReferenceControlFinetune, + LoadImagesFromDirectory, + ScaledSoftUniversalWeightsDeprecated, + SoftControlNetWeightsDeprecated, + CustomControlNetWeightsDeprecated, + SoftT2IAdapterWeightsDeprecated, + CustomT2IAdapterWeightsDeprecated, + AdvancedControlNetApplyDEPR, + AdvancedControlNetApplySingleDEPR, + ControlNetLoaderAdvancedDEPR, + DiffControlNetLoaderAdvancedDEPR + ] diff --git a/adv_control/nodes_ctrlora.py b/adv_control/nodes_ctrlora.py index d0b154f..1d98b2e 100644 --- a/adv_control/nodes_ctrlora.py +++ b/adv_control/nodes_ctrlora.py @@ -1,25 +1,28 @@ +from comfy_api.latest import io import folder_paths from .control_ctrlora import load_ctrlora - -class CtrLoRALoader: +class CtrLoRALoader(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "base": (folder_paths.get_filename_list("controlnet"), ), - "lora": (folder_paths.get_filename_list("controlnet"), ), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_CtrLoRALoader', + display_name='Load CtrLoRA Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/CtrLoRA', + inputs=[ + io.Combo.Input('base', options=folder_paths.get_filename_list("controlnet")), + io.Combo.Input('lora', options=folder_paths.get_filename_list("controlnet")) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET",) - FUNCTION = "load_controlnet_plusplus" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/CtrLoRA" - - def load_controlnet_plusplus(self, base: str, lora: str): + @classmethod + def execute(cls, base: str, lora: str): base_path = folder_paths.get_full_path("controlnet", base) lora_path = folder_paths.get_full_path("controlnet", lora) controlnet = load_ctrlora(base_path, lora_path) - return (controlnet,) + return io.NodeOutput(controlnet,) diff --git a/adv_control/nodes_deprecated.py b/adv_control/nodes_deprecated.py index cabd1f1..5b55bd7 100644 --- a/adv_control/nodes_deprecated.py +++ b/adv_control/nodes_deprecated.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io import os import torch @@ -7,29 +8,31 @@ import numpy as np from PIL import Image, ImageOps from .control import load_controlnet, is_advanced_controlnet from .nodes_main import AdvancedControlNetApply -from .utils import BIGMAX, ControlWeights, TimestepKeyframeGroup, TimestepKeyframe, get_properly_arranged_t2i_weights -from .logger import logger +from .utils import ControlWeights, TimestepKeyframeGroup, TimestepKeyframe, get_properly_arranged_t2i_weights - -class LoadImagesFromDirectory: +class LoadImagesFromDirectory(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "directory": ("STRING", {"default": ""}), - }, - "optional": { - "image_load_cap": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}), - "start_index": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='LoadImagesFromDirectory', + display_name='๐ŸšซLoad Images [DEPRECATED] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.String.Input('directory', default=''), + io.Int.Input('image_load_cap', optional=True, default=0, max=9007199254740991, min=0, step=1), + io.Int.Input('start_index', optional=True, default=0, max=9007199254740991, min=0, step=1) + ], + outputs=[ + io.Image.Output('IMAGE', is_output_list=False), + io.Mask.Output('MASK', is_output_list=False), + io.Int.Output('INT', is_output_list=False) + ], + is_deprecated=True + ) - RETURN_TYPES = ("IMAGE", "MASK", "INT") - FUNCTION = "load_images" - CATEGORY = "" - - def load_images(self, directory: str, image_load_cap: int = 0, start_index: int = 0): + @classmethod + def execute(cls, directory: str, image_load_cap: int = 0, start_index: int = 0): if not os.path.isdir(directory): raise FileNotFoundError(f"Directory '{directory} cannot be found.'") dir_files = os.listdir(directory) @@ -71,285 +74,283 @@ class LoadImagesFromDirectory: if len(images) == 0: raise FileNotFoundError(f"No images could be loaded from directory '{directory}'.") - return (torch.cat(images, dim=0), torch.stack(masks, dim=0), image_count) + return io.NodeOutput(torch.cat(images, dim=0), torch.stack(masks, dim=0), image_count) - -class ScaledSoftUniversalWeightsDeprecated: +class ScaledSoftUniversalWeightsDeprecated(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "base_multiplier": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 1.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ScaledSoftControlNetWeights', + display_name='Scaled Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Float.Input('base_multiplier', default=0.825, max=1.0, min=0.0, step=0.001), + io.Boolean.Input('flip_weights', default=False), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") - FUNCTION = "load_weights" - CATEGORY = "" - - def load_weights(self, base_multiplier, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + @classmethod + def execute(cls, base_multiplier, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): weights = ControlWeights.universal(base_multiplier=base_multiplier, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class SoftControlNetWeightsDeprecated: +class SoftControlNetWeightsDeprecated(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "weight_00": ("FLOAT", {"default": 0.09941396206337118, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_01": ("FLOAT", {"default": 0.12050177219802567, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_02": ("FLOAT", {"default": 0.14606275417942507, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_03": ("FLOAT", {"default": 0.17704576264172736, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_04": ("FLOAT", {"default": 0.214600924414215, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_05": ("FLOAT", {"default": 0.26012233262329093, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_06": ("FLOAT", {"default": 0.3152997971191405, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_07": ("FLOAT", {"default": 0.3821815722656249, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_08": ("FLOAT", {"default": 0.4632503906249999, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_09": ("FLOAT", {"default": 0.561515625, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_10": ("FLOAT", {"default": 0.6806249999999999, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_11": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='SoftControlNetWeights', + display_name='ControlNet Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Float.Input('weight_00', default=0.09941396206337118, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_01', default=0.12050177219802567, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_02', default=0.14606275417942507, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_03', default=0.17704576264172736, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_04', default=0.214600924414215, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_05', default=0.26012233262329093, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_06', default=0.3152997971191405, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_07', default=0.3821815722656249, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_08', default=0.4632503906249999, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_09', default=0.561515625, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_10', default=0.6806249999999999, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_11', default=0.825, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_12', default=1.0, max=10.0, min=0.0, step=0.001), + io.Boolean.Input('flip_weights', default=False), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ], + is_deprecated=True + ) - DEPRECATED = True - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") - FUNCTION = "load_weights" - CATEGORY = "" - - def load_weights(self, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, + @classmethod + def execute(cls, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, weight_07, weight_08, weight_09, weight_10, weight_11, weight_12, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): weights_output = [weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, weight_07, weight_08, weight_09, weight_10, weight_11] weights_middle = [weight_12] weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class CustomControlNetWeightsDeprecated: +class CustomControlNetWeightsDeprecated(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "weight_00": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_01": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_02": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_04": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_05": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_06": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_07": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_08": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_09": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='CustomControlNetWeights', + display_name='ControlNet Custom Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Float.Input('weight_00', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_01', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_02', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_04', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_05', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_06', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_07', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_08', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_09', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_10', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_11', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_12', default=1.0, max=10.0, min=0.0, step=0.001), + io.Boolean.Input('flip_weights', default=False), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ], + is_deprecated=True + ) - DEPRECATED = True - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") - FUNCTION = "load_weights" - CATEGORY = "" - - def load_weights(self, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, + @classmethod + def execute(cls, weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, weight_07, weight_08, weight_09, weight_10, weight_11, weight_12, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): weights_output = [weight_00, weight_01, weight_02, weight_03, weight_04, weight_05, weight_06, weight_07, weight_08, weight_09, weight_10, weight_11] weights_middle = [weight_12] weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class SoftT2IAdapterWeightsDeprecated: +class SoftT2IAdapterWeightsDeprecated(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "weight_00": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_01": ("FLOAT", {"default": 0.62, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_02": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='SoftT2IAdapterWeights', + display_name='T2IAdapter Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Float.Input('weight_00', default=0.25, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_01', default=0.62, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_02', default=0.825, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001), + io.Boolean.Input('flip_weights', default=False), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ], + is_deprecated=True + ) - DEPRECATED = True - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") - FUNCTION = "load_weights" - CATEGORY = "" - - def load_weights(self, weight_00, weight_01, weight_02, weight_03, flip_weights, + @classmethod + def execute(cls, weight_00, weight_01, weight_02, weight_03, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): weights = [weight_00, weight_01, weight_02, weight_03] weights = get_properly_arranged_t2i_weights(weights) weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class CustomT2IAdapterWeightsDeprecated: +class CustomT2IAdapterWeightsDeprecated(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "weight_00": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_01": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_02": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "weight_03": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "flip_weights": ("BOOLEAN", {"default": False}), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='CustomT2IAdapterWeights', + display_name='T2IAdapter Custom Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Float.Input('weight_00', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_01', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_02', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('weight_03', default=1.0, max=10.0, min=0.0, step=0.001), + io.Boolean.Input('flip_weights', default=False), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ], + is_deprecated=True + ) - DEPRECATED = True - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") - FUNCTION = "load_weights" - CATEGORY = "" - - def load_weights(self, weight_00, weight_01, weight_02, weight_03, flip_weights, + @classmethod + def execute(cls, weight_00, weight_01, weight_02, weight_03, flip_weights, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): weights = [weight_00, weight_01, weight_02, weight_03] weights = get_properly_arranged_t2i_weights(weights) weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class AdvancedControlNetApplyDEPR: +class AdvancedControlNetApplyDEPR(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "positive": ("CONDITIONING", ), - "negative": ("CONDITIONING", ), - "control_net": ("CONTROL_NET", ), - "image": ("IMAGE", ), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}) - }, - "optional": { - "mask_optional": ("MASK", ), - "timestep_kf": ("TIMESTEP_KEYFRAME", ), - "latent_kf_override": ("LATENT_KEYFRAME", ), - "weights_override": ("CONTROL_NET_WEIGHTS", ), - "model_optional": ("MODEL",), - "vae_optional": ("VAE",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_AdvancedControlNetApply', + display_name='Apply Advanced ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Conditioning.Input('positive'), + io.Conditioning.Input('negative'), + io.ControlNet.Input('control_net'), + io.Image.Input('image'), + io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01), + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001), + io.Mask.Input('mask_optional', optional=True), + io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True), + io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True), + io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True), + io.Model.Input('model_optional', optional=True), + io.Vae.Input('vae_optional', optional=True) + ], + outputs=[ + io.Conditioning.Output('positive', is_output_list=False), + io.Conditioning.Output('negative', is_output_list=False), + io.Model.Output('model_opt', is_output_list=False) + ], + is_deprecated=True + ) - DEPRECATED = True - RETURN_TYPES = ("CONDITIONING","CONDITIONING","MODEL",) - RETURN_NAMES = ("positive", "negative", "model_opt") - FUNCTION = "apply_controlnet" - - CATEGORY = "" - - def apply_controlnet(self, positive, negative, control_net, image, strength, start_percent, end_percent, + @classmethod + def execute(cls, positive, negative, control_net, image, strength, start_percent, end_percent, mask_optional=None, model_optional=None, vae_optional=None, timestep_kf: TimestepKeyframeGroup=None, latent_kf_override=None, weights_override: ControlWeights=None, control_apply_to_uncond=False): - new_positive, new_negative = AdvancedControlNetApply.apply_controlnet(self, positive=positive, negative=negative, control_net=control_net, image=image, + new_positive, new_negative = AdvancedControlNetApply.execute(positive=positive, negative=negative, control_net=control_net, image=image, strength=strength, start_percent=start_percent, end_percent=end_percent, mask_optional=mask_optional, vae_optional=vae_optional, - timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override,) - return (new_positive, new_negative, model_optional) + timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override,).args + return io.NodeOutput(new_positive, new_negative, model_optional) - -class AdvancedControlNetApplySingleDEPR: +class AdvancedControlNetApplySingleDEPR(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "conditioning": ("CONDITIONING", ), - "control_net": ("CONTROL_NET", ), - "image": ("IMAGE", ), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}) - }, - "optional": { - "mask_optional": ("MASK", ), - "timestep_kf": ("TIMESTEP_KEYFRAME", ), - "latent_kf_override": ("LATENT_KEYFRAME", ), - "weights_override": ("CONTROL_NET_WEIGHTS", ), - "model_optional": ("MODEL",), - "vae_optional": ("VAE",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_AdvancedControlNetApplySingle', + display_name='Apply Advanced ControlNet(1) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Conditioning.Input('conditioning'), + io.ControlNet.Input('control_net'), + io.Image.Input('image'), + io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01), + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001), + io.Mask.Input('mask_optional', optional=True), + io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True), + io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True), + io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True), + io.Model.Input('model_optional', optional=True), + io.Vae.Input('vae_optional', optional=True) + ], + outputs=[ + io.Conditioning.Output('CONDITIONING', is_output_list=False), + io.Model.Output('model_opt', is_output_list=False) + ], + is_deprecated=True + ) - DEPRECATED = True - RETURN_TYPES = ("CONDITIONING","MODEL",) - RETURN_NAMES = ("CONDITIONING", "model_opt") - FUNCTION = "apply_controlnet" - - CATEGORY = "" - - def apply_controlnet(self, conditioning, control_net, image, strength, start_percent, end_percent, + @classmethod + def execute(cls, conditioning, control_net, image, strength, start_percent, end_percent, mask_optional=None, model_optional=None, vae_optional=None, timestep_kf: TimestepKeyframeGroup=None, latent_kf_override=None, weights_override: ControlWeights=None): - values = AdvancedControlNetApply.apply_controlnet(self, positive=conditioning, negative=None, control_net=control_net, image=image, + values = AdvancedControlNetApply.execute(positive=conditioning, negative=None, control_net=control_net, image=image, strength=strength, start_percent=start_percent, end_percent=end_percent, mask_optional=mask_optional, vae_optional=vae_optional, timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override, control_apply_to_uncond=True) - return (values[0], model_optional) + return io.NodeOutput(values.args[0], model_optional) - -class ControlNetLoaderAdvancedDEPR: +class ControlNetLoaderAdvancedDEPR(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "control_net_name": (folder_paths.get_filename_list("controlnet"), ), - }, - "optional": { - "tk_optional": ("TIMESTEP_KEYFRAME", ), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ControlNetLoaderAdvanced', + display_name='Load Advanced ControlNet Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")), + io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ], + is_deprecated=True + ) - DEPRECATED = True - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - - CATEGORY = "" - - def load_controlnet(self, control_net_name, + @classmethod + def execute(cls, control_net_name, tk_optional: TimestepKeyframeGroup=None, timestep_keyframe: TimestepKeyframeGroup=None, ): @@ -357,29 +358,30 @@ class ControlNetLoaderAdvancedDEPR: tk_optional = timestep_keyframe controlnet_path = folder_paths.get_full_path("controlnet", control_net_name) controlnet = load_controlnet(controlnet_path, tk_optional) - return (controlnet,) + return io.NodeOutput(controlnet,) -class DiffControlNetLoaderAdvancedDEPR: +class DiffControlNetLoaderAdvancedDEPR(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "control_net_name": (folder_paths.get_filename_list("controlnet"), ) - }, - "optional": { - "tk_optional": ("TIMESTEP_KEYFRAME", ), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='DiffControlNetLoaderAdvanced', + display_name='Load Advanced ControlNet Model (diff) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='', + inputs=[ + io.Model.Input('model'), + io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")), + io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ], + is_deprecated=True + ) - DEPRECATED = True - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - CATEGORY = "" - - def load_controlnet(self, control_net_name, model, + @classmethod + def execute(cls, control_net_name, model, tk_optional: TimestepKeyframeGroup=None, timestep_keyframe: TimestepKeyframeGroup=None ): @@ -389,4 +391,4 @@ class DiffControlNetLoaderAdvancedDEPR: controlnet = load_controlnet(controlnet_path, tk_optional, model) if is_advanced_controlnet(controlnet): controlnet.verify_all_weights() - return (controlnet,) + return io.NodeOutput(controlnet,) diff --git a/adv_control/nodes_keyframes.py b/adv_control/nodes_keyframes.py index bc4f1b8..c210d8c 100644 --- a/adv_control/nodes_keyframes.py +++ b/adv_control/nodes_keyframes.py @@ -1,40 +1,40 @@ +from comfy_api.latest import io from typing import Union import numpy as np from collections.abc import Iterable -from .utils import ControlWeights, TimestepKeyframe, TimestepKeyframeGroup, LatentKeyframe, LatentKeyframeGroup, BIGMIN, BIGMAX +from .utils import ControlWeights, TimestepKeyframe, TimestepKeyframeGroup, LatentKeyframe, LatentKeyframeGroup from .utils import StrengthInterpolation as SI from .logger import logger - -class TimestepKeyframeNode: +class TimestepKeyframeNode(io.ComfyNode): OUTDATED_DUMMY = -39 @classmethod - def INPUT_TYPES(s): - return { - "required": { - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ), - }, - "optional": { - "prev_timestep_kf": ("TIMESTEP_KEYFRAME", ), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "cn_weights": ("CONTROL_NET_WEIGHTS", ), - "latent_keyframe": ("LATENT_KEYFRAME", ), - "null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "inherit_missing": ("BOOLEAN", {"default": True}, ), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "mask_optional": ("MASK", ), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='TimestepKeyframe', + display_name='Timestep Keyframe ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes', + inputs=[ + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True), + io.Float.Input('strength', optional=True, default=1.0, max=10.0, min=0.0, step=0.001), + io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True), + io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True), + io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001), + io.Boolean.Input('inherit_missing', optional=True, default=True), + io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), + io.Mask.Input('mask_optional', optional=True) + ], + outputs=[ + io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False) + ] + ) - RETURN_NAMES = ("TIMESTEP_KF", ) - RETURN_TYPES = ("TIMESTEP_KEYFRAME", ) - FUNCTION = "load_keyframe" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes" - - def load_keyframe(self, + @classmethod + def execute(cls, start_percent: float, strength: float=1.0, cn_weights: ControlWeights=None, control_net_weights: ControlWeights=None, # old name @@ -46,7 +46,7 @@ class TimestepKeyframeNode: guarantee_usage=True, # old input mask_optional=None,): # if using outdated dummy value, means node on workflow is outdated and should appropriately convert behavior - if guarantee_steps == self.OUTDATED_DUMMY: + if guarantee_steps == cls.OUTDATED_DUMMY: guarantee_steps = int(guarantee_usage) control_net_weights = control_net_weights if control_net_weights else cn_weights prev_timestep_keyframe = prev_timestep_keyframe if prev_timestep_keyframe else prev_timestep_kf @@ -58,39 +58,39 @@ class TimestepKeyframeNode: control_weights=control_net_weights, latent_keyframes=latent_keyframe, inherit_missing=inherit_missing, guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional) prev_timestep_keyframe.add(keyframe) - return (prev_timestep_keyframe,) + return io.NodeOutput(prev_timestep_keyframe,) -class TimestepKeyframeInterpolationNode: +class TimestepKeyframeInterpolationNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001},), - "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "strength_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001},), - "strength_end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001},), - "interpolation": (SI._LIST, ), - "intervals": ("INT", {"default": 50, "min": 2, "max": 100, "step": 1}), - }, - "optional": { - "prev_timestep_kf": ("TIMESTEP_KEYFRAME", ), - "cn_weights": ("CONTROL_NET_WEIGHTS", ), - "latent_keyframe": ("LATENT_KEYFRAME", ), - "null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001},), - "inherit_missing": ("BOOLEAN", {"default": True},), - "mask_optional": ("MASK", ), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_TimestepKeyframeInterpolation', + display_name='Timestep Keyframe Interp. ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes', + inputs=[ + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('strength_start', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('strength_end', default=1.0, max=10.0, min=0.0, step=0.001), + io.Combo.Input('interpolation', options=['linear', 'ease-in', 'ease-out', 'ease-in-out']), + io.Int.Input('intervals', default=50, max=100, min=2, step=1), + io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True), + io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True), + io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True), + io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001), + io.Boolean.Input('inherit_missing', optional=True, default=True), + io.Mask.Input('mask_optional', optional=True), + io.Boolean.Input('print_keyframes', optional=True, default=False) + ], + outputs=[ + io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False) + ] + ) - RETURN_NAMES = ("TIMESTEP_KF", ) - RETURN_TYPES = ("TIMESTEP_KEYFRAME", ) - FUNCTION = "load_keyframe" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes" - - def load_keyframe(self, + @classmethod + def execute(cls, start_percent: float, end_percent: float, strength_start: float, strength_end: float, interpolation: str, intervals: int, cn_weights: ControlWeights=None, @@ -119,36 +119,35 @@ class TimestepKeyframeInterpolationNode: guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional)) if print_keyframes: logger.info(f"TimestepKeyframe - start_percent:{percent} = {strength}") - return (prev_timestep_kf,) + return io.NodeOutput(prev_timestep_kf,) - -class TimestepKeyframeFromStrengthListNode: +class TimestepKeyframeFromStrengthListNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "float_strengths": ("FLOAT", {"default": -1, "min": -1, "step": 0.001, "forceInput": True}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001},), - "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), - }, - "optional": { - "prev_timestep_kf": ("TIMESTEP_KEYFRAME", ), - "cn_weights": ("CONTROL_NET_WEIGHTS", ), - "latent_keyframe": ("LATENT_KEYFRAME", ), - "null_latent_kf_strength": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001},), - "inherit_missing": ("BOOLEAN", {"default": True},), - "mask_optional": ("MASK", ), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_TimestepKeyframeFromStrengthList', + display_name='Timestep Keyframe From List ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes', + inputs=[ + io.Float.Input('float_strengths', default=-1, force_input=True, min=-1, step=0.001), + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001), + io.Custom('TIMESTEP_KEYFRAME').Input('prev_timestep_kf', optional=True), + io.Custom('CONTROL_NET_WEIGHTS').Input('cn_weights', optional=True), + io.Custom('LATENT_KEYFRAME').Input('latent_keyframe', optional=True), + io.Float.Input('null_latent_kf_strength', optional=True, default=0.0, max=10.0, min=0.0, step=0.001), + io.Boolean.Input('inherit_missing', optional=True, default=True), + io.Mask.Input('mask_optional', optional=True), + io.Boolean.Input('print_keyframes', optional=True, default=False) + ], + outputs=[ + io.Custom('TIMESTEP_KEYFRAME').Output('TIMESTEP_KF', is_output_list=False) + ] + ) - RETURN_NAMES = ("TIMESTEP_KF", ) - RETURN_TYPES = ("TIMESTEP_KEYFRAME", ) - FUNCTION = "load_keyframe" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes" - - def load_keyframe(self, + @classmethod + def execute(cls, start_percent: float, end_percent: float, float_strengths: float, cn_weights: ControlWeights=None, @@ -182,29 +181,27 @@ class TimestepKeyframeFromStrengthListNode: guarantee_steps=guarantee_steps, mask_hint_orig=mask_optional)) if print_keyframes: logger.info(f"TimestepKeyframe - start_percent:{percent} = {strength}") - return (prev_timestep_kf,) + return io.NodeOutput(prev_timestep_kf,) - -class LatentKeyframeNode: +class LatentKeyframeNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "batch_index": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - "optional": { - "prev_latent_kf": ("LATENT_KEYFRAME", ), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='LatentKeyframe', + display_name='Latent Keyframe ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes', + inputs=[ + io.Int.Input('batch_index', default=0, max=9007199254740991, min=-9007199254740991, step=1), + io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.001), + io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True) + ], + outputs=[ + io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False) + ] + ) - RETURN_NAMES = ("LATENT_KF", ) - RETURN_TYPES = ("LATENT_KEYFRAME", ) - FUNCTION = "load_keyframe" - - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes" - - def load_keyframe(self, + @classmethod + def execute(cls, batch_index: int, strength: float, prev_latent_kf: LatentKeyframeGroup=None, @@ -217,30 +214,29 @@ class LatentKeyframeNode: prev_latent_keyframe = prev_latent_keyframe.clone() keyframe = LatentKeyframe(batch_index, strength) prev_latent_keyframe.add(keyframe) - return (prev_latent_keyframe,) + return io.NodeOutput(prev_latent_keyframe,) - -class LatentKeyframeGroupNode: +class LatentKeyframeGroupNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "index_strengths": ("STRING", {"multiline": True, "default": ""}), - }, - "optional": { - "prev_latent_kf": ("LATENT_KEYFRAME", ), - "latent_optional": ("LATENT", ), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='LatentKeyframeGroup', + display_name='Latent Keyframe Group ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes', + inputs=[ + io.String.Input('index_strengths', default='', multiline=True), + io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True), + io.Latent.Input('latent_optional', optional=True), + io.Boolean.Input('print_keyframes', optional=True, default=False) + ], + outputs=[ + io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False) + ] + ) - RETURN_NAMES = ("LATENT_KF", ) - RETURN_TYPES = ("LATENT_KEYFRAME", ) - FUNCTION = "load_keyframes" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes" - - def validate_index(self, index: int, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int: + @staticmethod + def validate_index(index: int, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int: # if part of range, do nothing if is_range: return index @@ -258,13 +254,15 @@ class LatentKeyframeGroupNode: index = conv_index return index - def convert_to_index_int(self, raw_index: str, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int: + @classmethod + def convert_to_index_int(cls, raw_index: str, latent_count: int = 0, is_range: bool = False, allow_negative = False) -> int: try: - return self.validate_index(int(raw_index), latent_count=latent_count, is_range=is_range, allow_negative=allow_negative) + return cls.validate_index(int(raw_index), latent_count=latent_count, is_range=is_range, allow_negative=allow_negative) except ValueError as e: raise ValueError(f"index '{raw_index}' must be an integer.", e) - def convert_to_latent_keyframes(self, latent_indeces: str, latent_count: int) -> set[LatentKeyframe]: + @classmethod + def convert_to_latent_keyframes(cls, latent_indeces: str, latent_count: int) -> set[LatentKeyframe]: if not latent_indeces: return set() int_latent_indeces = [i for i in range(0, latent_count)] @@ -289,8 +287,8 @@ class LatentKeyframeGroupNode: if ':' in g: index_range = g.split(":", 1) index_range = [r.strip() for r in index_range] - start_index = self.convert_to_index_int(index_range[0], latent_count=latent_count, is_range=True, allow_negative=allow_negative) - end_index = self.convert_to_index_int(index_range[1], latent_count=latent_count, is_range=True, allow_negative=allow_negative) + start_index = cls.convert_to_index_int(index_range[0], latent_count=latent_count, is_range=True, allow_negative=allow_negative) + end_index = cls.convert_to_index_int(index_range[1], latent_count=latent_count, is_range=True, allow_negative=allow_negative) # if latents were passed in, base indeces on known latent count if len(int_latent_indeces) > 0: for i in int_latent_indeces[start_index:end_index]: @@ -301,14 +299,16 @@ class LatentKeyframeGroupNode: chosen_indeces.add(LatentKeyframe(i, strength)) # parse individual indeces else: - chosen_indeces.add(LatentKeyframe(self.convert_to_index_int(g, latent_count=latent_count, allow_negative=allow_negative), strength)) + chosen_indeces.add(LatentKeyframe(cls.convert_to_index_int(g, latent_count=latent_count, allow_negative=allow_negative), strength)) return chosen_indeces - def load_keyframes(self, + @classmethod + def execute(cls, index_strengths: str, prev_latent_kf: LatentKeyframeGroup=None, prev_latent_keyframe: LatentKeyframeGroup=None, # old name - latent_image_opt=None, + latent_optional=None, + latent_image_opt=None, # old name print_keyframes=False): prev_latent_keyframe = prev_latent_keyframe if prev_latent_keyframe else prev_latent_kf if not prev_latent_keyframe: @@ -317,10 +317,11 @@ class LatentKeyframeGroupNode: prev_latent_keyframe = prev_latent_keyframe.clone() curr_latent_keyframe = LatentKeyframeGroup() + latent_image_opt = latent_image_opt if latent_image_opt is not None else latent_optional latent_count = -1 if latent_image_opt: latent_count = latent_image_opt['samples'].size()[0] - latent_keyframes = self.convert_to_latent_keyframes(index_strengths, latent_count=latent_count) + latent_keyframes = cls.convert_to_latent_keyframes(index_strengths, latent_count=latent_count) for latent_keyframe in latent_keyframes: curr_latent_keyframe.add(latent_keyframe) @@ -333,32 +334,32 @@ class LatentKeyframeGroupNode: for latent_keyframe in prev_latent_keyframe.keyframes: curr_latent_keyframe.add(latent_keyframe) - return (curr_latent_keyframe,) + return io.NodeOutput(curr_latent_keyframe,) -class LatentKeyframeInterpolationNode: +class LatentKeyframeInterpolationNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "batch_index_from": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}), - "batch_index_to_excl": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}), - "strength_from": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "strength_to": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "interpolation": (SI._LIST, ), - }, - "optional": { - "prev_latent_kf": ("LATENT_KEYFRAME", ), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='LatentKeyframeTiming', + display_name='Latent Keyframe Interp. ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes', + inputs=[ + io.Int.Input('batch_index_from', default=0, max=9007199254740991, min=-9007199254740991, step=1), + io.Int.Input('batch_index_to_excl', default=0, max=9007199254740991, min=-9007199254740991, step=1), + io.Float.Input('strength_from', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('strength_to', default=1.0, max=10.0, min=0.0, step=0.001), + io.Combo.Input('interpolation', options=['linear', 'ease-in', 'ease-out', 'ease-in-out']), + io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True), + io.Boolean.Input('print_keyframes', optional=True, default=False) + ], + outputs=[ + io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False) + ] + ) - RETURN_NAMES = ("LATENT_KF", ) - RETURN_TYPES = ("LATENT_KEYFRAME", ) - FUNCTION = "load_keyframe" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes" - - def load_keyframe(self, + @classmethod + def execute(cls, batch_index_from: int, strength_from: float, batch_index_to_excl: int, @@ -407,28 +408,27 @@ class LatentKeyframeInterpolationNode: for latent_keyframe in prev_latent_keyframe.keyframes: curr_latent_keyframe.add(latent_keyframe) - return (curr_latent_keyframe,) + return io.NodeOutput(curr_latent_keyframe,) - -class LatentKeyframeBatchedGroupNode: +class LatentKeyframeBatchedGroupNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "float_strengths": ("FLOAT", {"default": -1, "min": -1, "step": 0.001, "forceInput": True}), - }, - "optional": { - "prev_latent_kf": ("LATENT_KEYFRAME", ), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='LatentKeyframeBatchedGroup', + display_name='Latent Keyframe From List ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes', + inputs=[ + io.Float.Input('float_strengths', default=-1, force_input=True, min=-1, step=0.001), + io.Custom('LATENT_KEYFRAME').Input('prev_latent_kf', optional=True), + io.Boolean.Input('print_keyframes', optional=True, default=False) + ], + outputs=[ + io.Custom('LATENT_KEYFRAME').Output('LATENT_KF', is_output_list=False) + ] + ) - RETURN_NAMES = ("LATENT_KF", ) - RETURN_TYPES = ("LATENT_KEYFRAME", ) - FUNCTION = "load_keyframe" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/keyframes" - - def load_keyframe(self, float_strengths: Union[float, list[float]], + @classmethod + def execute(cls, float_strengths: Union[float, list[float]], prev_latent_kf: LatentKeyframeGroup=None, prev_latent_keyframe: LatentKeyframeGroup=None, # old name print_keyframes=False): @@ -458,4 +458,4 @@ class LatentKeyframeBatchedGroupNode: for latent_keyframe in prev_latent_keyframe.keyframes: curr_latent_keyframe.add(latent_keyframe) - return (curr_latent_keyframe,) + return io.NodeOutput(curr_latent_keyframe,) diff --git a/adv_control/nodes_main.py b/adv_control/nodes_main.py index a0cd536..c705e69 100644 --- a/adv_control/nodes_main.py +++ b/adv_control/nodes_main.py @@ -1,123 +1,120 @@ +from comfy_api.latest import io from torch import Tensor import folder_paths -from comfy.model_patcher import ModelPatcher from .control import load_controlnet, convert_to_advanced, is_advanced_controlnet, is_sd3_advanced_controlnet from .control_lllite import load_anima_lllite -from .utils import ControlWeights, LatentKeyframeGroup, TimestepKeyframeGroup, AbstractPreprocWrapper, BIGMAX +from .utils import ControlWeights, LatentKeyframeGroup, TimestepKeyframeGroup, AbstractPreprocWrapper -from .logger import logger - - -class ControlNetLoaderAdvanced: +class ControlNetLoaderAdvanced(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cnet": (folder_paths.get_filename_list("controlnet"), ), - }, - "optional": { - "_tk_opt": ("TIMESTEP_KEYFRAME", ), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ControlNetLoaderAdvanced', + display_name='Load Advanced ControlNet Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + inputs=[ + io.Combo.Input('cnet', options=folder_paths.get_filename_list("controlnet")), + io.Custom('TIMESTEP_KEYFRAME').Input('_tk_opt', optional=True) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…" - - def load_controlnet(self, cnet, + @classmethod + def execute(cls, cnet, _tk_opt: TimestepKeyframeGroup=None, ): controlnet_path = folder_paths.get_full_path("controlnet", cnet) controlnet = load_controlnet(controlnet_path, _tk_opt) - return (controlnet,) + return io.NodeOutput(controlnet,) -class DiffControlNetLoaderAdvanced: +class DiffControlNetLoaderAdvanced(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "cnet": (folder_paths.get_filename_list("controlnet"), ) - }, - "optional": { - "_tk_opt": ("TIMESTEP_KEYFRAME", ), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_DiffControlNetLoaderAdvanced', + display_name='Load Advanced ControlNet Model (diff) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + inputs=[ + io.Model.Input('model'), + io.Combo.Input('cnet', options=folder_paths.get_filename_list("controlnet")), + io.Custom('TIMESTEP_KEYFRAME').Input('_tk_opt', optional=True) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…" - - def load_controlnet(self, cnet, model, + @classmethod + def execute(cls, cnet, model, _tk_opt: TimestepKeyframeGroup=None, ): controlnet_path = folder_paths.get_full_path("controlnet", cnet) controlnet = load_controlnet(controlnet_path, _tk_opt, model) if is_advanced_controlnet(controlnet): controlnet.verify_all_weights() - return (controlnet,) + return io.NodeOutput(controlnet,) - -class AnimaLLLiteLoaderAdvanced: +class AnimaLLLiteLoaderAdvanced(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model_patch": (folder_paths.get_filename_list("model_patches"), ), - }, - "optional": { - "timestep_kf": ("TIMESTEP_KEYFRAME", ), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_AnimaLLLiteLoaderAdvanced', + display_name='Load Anima LLLite Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/loaders', + inputs=[ + io.Combo.Input('model_patch', options=folder_paths.get_filename_list("model_patches")), + io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/loaders" - - def load_controlnet(self, model_patch, timestep_kf: TimestepKeyframeGroup=None): + @classmethod + def execute(cls, model_patch, timestep_kf: TimestepKeyframeGroup=None): model_patch_path = folder_paths.get_full_path_or_raise("model_patches", model_patch) - return (load_anima_lllite(model_patch_path, timestep_keyframe=timestep_kf),) + return io.NodeOutput(load_anima_lllite(model_patch_path, timestep_keyframe=timestep_kf),) - -class AdvancedControlNetApply: +class AdvancedControlNetApply(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "positive": ("CONDITIONING", ), - "negative": ("CONDITIONING", ), - "control_net": ("CONTROL_NET", ), - "image": ("IMAGE", ), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}) - }, - "optional": { - "mask_optional": ("MASK", ), - "timestep_kf": ("TIMESTEP_KEYFRAME", ), - "latent_kf_override": ("LATENT_KEYFRAME", ), - "weights_override": ("CONTROL_NET_WEIGHTS", ), - "vae_optional": ("VAE",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_AdvancedControlNetApply_v2', + display_name='Apply Advanced ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + inputs=[ + io.Conditioning.Input('positive'), + io.Conditioning.Input('negative'), + io.ControlNet.Input('control_net'), + io.Image.Input('image'), + io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01), + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001), + io.Mask.Input('mask_optional', optional=True), + io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True), + io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True), + io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True), + io.Vae.Input('vae_optional', optional=True) + ], + outputs=[ + io.Conditioning.Output('positive', is_output_list=False), + io.Conditioning.Output('negative', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONDITIONING","CONDITIONING",) - RETURN_NAMES = ("positive", "negative") - FUNCTION = "apply_controlnet" - - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…" - - def apply_controlnet(self, positive, negative, control_net, image, strength, start_percent, end_percent, + @classmethod + def execute(cls, positive, negative, control_net, image, strength, start_percent, end_percent, mask_optional: Tensor=None, vae_optional=None, timestep_kf: TimestepKeyframeGroup=None, latent_kf_override: LatentKeyframeGroup=None, weights_override: ControlWeights=None, control_apply_to_uncond=False): if strength == 0: - return (positive, negative) + return io.NodeOutput(positive, negative) control_hint = image.movedim(-1,1) cnets = {} @@ -183,43 +180,43 @@ class AdvancedControlNetApply: n = [t[0], d] c.append(n) out.append(c) - return (out[0], out[1]) + return io.NodeOutput(out[0], out[1]) -class AdvancedControlNetApplySingle: +class AdvancedControlNetApplySingle(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "conditioning": ("CONDITIONING", ), - "control_net": ("CONTROL_NET", ), - "image": ("IMAGE", ), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "end_percent": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}) - }, - "optional": { - "mask_optional": ("MASK", ), - "timestep_kf": ("TIMESTEP_KEYFRAME", ), - "latent_kf_override": ("LATENT_KEYFRAME", ), - "weights_override": ("CONTROL_NET_WEIGHTS", ), - "vae_optional": ("VAE",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_AdvancedControlNetApplySingle_v2', + display_name='Apply Advanced ControlNet(1) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + inputs=[ + io.Conditioning.Input('conditioning'), + io.ControlNet.Input('control_net'), + io.Image.Input('image'), + io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01), + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('end_percent', default=1.0, max=1.0, min=0.0, step=0.001), + io.Mask.Input('mask_optional', optional=True), + io.Custom('TIMESTEP_KEYFRAME').Input('timestep_kf', optional=True), + io.Custom('LATENT_KEYFRAME').Input('latent_kf_override', optional=True), + io.Custom('CONTROL_NET_WEIGHTS').Input('weights_override', optional=True), + io.Vae.Input('vae_optional', optional=True) + ], + outputs=[ + io.Conditioning.Output('CONDITIONING', is_output_list=False), + io.Model.Output('model_opt', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONDITIONING","MODEL",) - RETURN_NAMES = ("CONDITIONING", "model_opt") - FUNCTION = "apply_controlnet" - - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…" - - def apply_controlnet(self, conditioning, control_net, image, strength, start_percent, end_percent, + @classmethod + def execute(cls, conditioning, control_net, image, strength, start_percent, end_percent, mask_optional: Tensor=None, vae_optional=None, timestep_kf: TimestepKeyframeGroup=None, latent_kf_override: LatentKeyframeGroup=None, weights_override: ControlWeights=None): - values = AdvancedControlNetApply.apply_controlnet(self, positive=conditioning, negative=None, control_net=control_net, image=image, + values = AdvancedControlNetApply.execute(positive=conditioning, negative=None, control_net=control_net, image=image, strength=strength, start_percent=start_percent, end_percent=end_percent, mask_optional=mask_optional, vae_optional=vae_optional, timestep_kf=timestep_kf, latent_kf_override=latent_kf_override, weights_override=weights_override, control_apply_to_uncond=True) - return (values[0],) + return io.NodeOutput(values.args[0], None) diff --git a/adv_control/nodes_plusplus.py b/adv_control/nodes_plusplus.py index f4241d0..ba8ae2f 100644 --- a/adv_control/nodes_plusplus.py +++ b/adv_control/nodes_plusplus.py @@ -1,78 +1,82 @@ +from comfy_api.latest import io from torch import Tensor import math import folder_paths -from .control_plusplus import load_controlnetplusplus, PlusPlusType, PlusPlusInput, PlusPlusInputGroup, PlusPlusImageWrapper -from .utils import BIGMAX +from .control_plusplus import load_controlnetplusplus, PlusPlusInput, PlusPlusInputGroup, PlusPlusImageWrapper - -class PlusPlusLoaderAdvanced: +class PlusPlusLoaderAdvanced(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "plus_input": ("PLUS_INPUT", ), - "name": (folder_paths.get_filename_list("controlnet"), ), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ControlNet++LoaderAdvanced', + display_name='Load ControlNet++ Model (Multi) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/ControlNet++', + inputs=[ + io.Custom('PLUS_INPUT').Input('plus_input'), + io.Combo.Input('name', options=folder_paths.get_filename_list("controlnet")) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False), + io.Image.Output('IMAGE', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET", "IMAGE",) - FUNCTION = "load_controlnet_plusplus" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/ControlNet++" - - def load_controlnet_plusplus(self, plus_input: PlusPlusInputGroup, name: str): + @classmethod + def execute(cls, plus_input: PlusPlusInputGroup, name: str): controlnet_path = folder_paths.get_full_path("controlnet", name) controlnet = load_controlnetplusplus(controlnet_path) controlnet.verify_control_type(name, plus_input) controlnet.allow_condhint_latents = True - return (controlnet, PlusPlusImageWrapper(plus_input),) + return io.NodeOutput(controlnet, PlusPlusImageWrapper(plus_input),) - -class PlusPlusLoaderSingle: +class PlusPlusLoaderSingle(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "name": (folder_paths.get_filename_list("controlnet"), ), - "control_type": (PlusPlusType._LIST_WITH_NONE, {"default": PlusPlusType.NONE}, ), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ControlNet++LoaderSingle', + display_name='Load ControlNet++ Model (Single) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/ControlNet++', + inputs=[ + io.Combo.Input('name', options=folder_paths.get_filename_list("controlnet")), + io.Combo.Input('control_type', options=['openpose', 'depth', 'hed/pidi/scribble/ted', 'canny/lineart/mlsd', 'normal', 'segment', 'tile', 'inpaint/outpaint', 'none'], default='none') + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET",) - FUNCTION = "load_controlnet_plusplus" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/ControlNet++" - - def load_controlnet_plusplus(self, name: str, control_type: str): + @classmethod + def execute(cls, name: str, control_type: str): controlnet_path = folder_paths.get_full_path("controlnet", name) controlnet = load_controlnetplusplus(controlnet_path) controlnet.single_control_type = control_type controlnet.verify_control_type(name) - return (controlnet,) + return io.NodeOutput(controlnet,) - -class PlusPlusInputNode: +class PlusPlusInputNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "image": ("IMAGE",), - "control_type": (PlusPlusType._LIST,), - }, - "optional": { - "prev_plus_input": ("PLUS_INPUT",), - #"strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": BIGMAX, "step": 0.01}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ControlNet++InputNode', + display_name='ControlNet++ Input ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/ControlNet++', + inputs=[ + io.Image.Input('image'), + io.Combo.Input('control_type', options=['openpose', 'depth', 'hed/pidi/scribble/ted', 'canny/lineart/mlsd', 'normal', 'segment', 'tile', 'inpaint/outpaint']), + io.Custom('PLUS_INPUT').Input('prev_plus_input', optional=True) + ], + outputs=[ + io.Custom('PLUS_INPUT').Output('PLUS_INPUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("PLUS_INPUT", ) - FUNCTION = "wrap_images" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/ControlNet++" - - def wrap_images(self, image: Tensor, control_type: str, strength=1.0, prev_plus_input: PlusPlusInputGroup=None): + @classmethod + def execute(cls, image: Tensor, control_type: str, strength=1.0, prev_plus_input: PlusPlusInputGroup=None): if prev_plus_input is None: prev_plus_input = PlusPlusInputGroup() prev_plus_input = prev_plus_input.clone() @@ -82,4 +86,4 @@ class PlusPlusInputNode: pp_input = PlusPlusInput(image, control_type, strength) prev_plus_input.add(pp_input) - return (prev_plus_input,) + return io.NodeOutput(prev_plus_input,) diff --git a/adv_control/nodes_reference.py b/adv_control/nodes_reference.py index fd0e4dc..85713f2 100644 --- a/adv_control/nodes_reference.py +++ b/adv_control/nodes_reference.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from torch import Tensor from nodes import VAEEncode @@ -6,77 +7,81 @@ from comfy.sd import VAE from .control_reference import ReferenceAdvanced, ReferenceOptions, ReferenceType, ReferencePreprocWrapper - # node for ReferenceCN -class ReferenceControlNetNode: +class ReferenceControlNetNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "reference_type": (ReferenceType._LIST,), - "style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), - "ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ReferenceControlNet', + display_name='Reference ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/Reference', + inputs=[ + io.Combo.Input('reference_type', options=['reference_attn', 'reference_adain', 'reference_attn+adain']), + io.Float.Input('style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01), + io.Float.Input('ref_weight', default=1.0, max=1.0, min=0.0, step=0.01) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/Reference" - - def load_controlnet(self, reference_type: str, style_fidelity: float, ref_weight: float): + @classmethod + def execute(cls, reference_type: str, style_fidelity: float, ref_weight: float): ref_opts = ReferenceOptions.create_combo(reference_type=reference_type, style_fidelity=style_fidelity, ref_weight=ref_weight) controlnet = ReferenceAdvanced(ref_opts=ref_opts, timestep_keyframes=None) - return (controlnet,) + return io.NodeOutput(controlnet,) - -class ReferenceControlFinetune: +class ReferenceControlFinetune(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "attn_style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), - "attn_ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - "attn_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - "adain_style_fidelity": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.01}), - "adain_ref_weight": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - "adain_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ReferenceControlNetFinetune', + display_name='Reference ControlNet (Finetune) ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/Reference', + inputs=[ + io.Float.Input('attn_style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01), + io.Float.Input('attn_ref_weight', default=1.0, max=1.0, min=0.0, step=0.01), + io.Float.Input('attn_strength', default=1.0, max=1.0, min=0.0, step=0.01), + io.Float.Input('adain_style_fidelity', default=0.5, max=1.0, min=0.0, step=0.01), + io.Float.Input('adain_ref_weight', default=1.0, max=1.0, min=0.0, step=0.01), + io.Float.Input('adain_strength', default=1.0, max=1.0, min=0.0, step=0.01) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/Reference" - - def load_controlnet(self, + @classmethod + def execute(cls, attn_style_fidelity: float, attn_ref_weight: float, attn_strength: float, adain_style_fidelity: float, adain_ref_weight: float, adain_strength: float): ref_opts = ReferenceOptions(reference_type=ReferenceType.ATTN_ADAIN, attn_style_fidelity=attn_style_fidelity, attn_ref_weight=attn_ref_weight, attn_strength=attn_strength, adain_style_fidelity=adain_style_fidelity, adain_ref_weight=adain_ref_weight, adain_strength=adain_strength) controlnet = ReferenceAdvanced(ref_opts=ref_opts, timestep_keyframes=None) - return (controlnet,) + return io.NodeOutput(controlnet,) - -class ReferencePreprocessorNode: +class ReferencePreprocessorNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "image": ("IMAGE", ), - "vae": ("VAE", ), - "latent_size": ("LATENT", ), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ReferencePreprocessor', + display_name='Reference Preproccessor ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/Reference/preprocess', + inputs=[ + io.Image.Input('image'), + io.Vae.Input('vae'), + io.Latent.Input('latent_size') + ], + outputs=[ + io.Image.Output('proc_IMAGE', is_output_list=False) + ] + ) - RETURN_TYPES = ("IMAGE",) - RETURN_NAMES = ("proc_IMAGE",) - FUNCTION = "preprocess_images" - - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/Reference/preprocess" - - def preprocess_images(self, vae: VAE, image: Tensor, latent_size: Tensor): + @classmethod + def execute(cls, vae: VAE, image: Tensor, latent_size: Tensor): # first, resize image to match latents image = image.movedim(-1,1) image = comfy.utils.common_upscale(image, latent_size["samples"].shape[3] * 8, latent_size["samples"].shape[2] * 8, 'nearest-exact', "center") @@ -87,4 +92,4 @@ class ReferencePreprocessorNode: except Exception: image = VAEEncode.vae_encode_crop_pixels(image) encoded = vae.encode(image[:,:,:,:3]) - return (ReferencePreprocWrapper(condhint=encoded),) + return io.NodeOutput(ReferencePreprocWrapper(condhint=encoded),) diff --git a/adv_control/nodes_sparsectrl.py b/adv_control/nodes_sparsectrl.py index 25812f9..14f8349 100644 --- a/adv_control/nodes_sparsectrl.py +++ b/adv_control/nodes_sparsectrl.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from torch import Tensor import folder_paths @@ -7,68 +8,68 @@ from comfy.sd import VAE from .utils import TimestepKeyframeGroup from .control_sparsectrl import SparseMethod, SparseIndexMethod, SparseSettings, SparseSpreadMethod, PreprocSparseRGBWrapper, SparseConst, SparseContextAware, get_idx_list_from_str -from .control import load_sparsectrl, load_controlnet, ControlNetAdvanced, SparseCtrlAdvanced - +from .control import load_sparsectrl, load_controlnet, ControlNetAdvanced # node for SparseCtrl loading -class SparseCtrlLoaderAdvanced: +class SparseCtrlLoaderAdvanced(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "sparsectrl_name": (folder_paths.get_filename_list("controlnet"), ), - "use_motion": ("BOOLEAN", {"default": True}, ), - "motion_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - "optional": { - "sparse_method": ("SPARSE_METHOD", ), - "tk_optional": ("TIMESTEP_KEYFRAME", ), - "context_aware": (SparseContextAware.LIST, ), - "sparse_hint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "sparse_nonhint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "sparse_mask_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_SparseCtrlLoaderAdvanced', + display_name='Load SparseCtrl Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl', + inputs=[ + io.Combo.Input('sparsectrl_name', options=folder_paths.get_filename_list("controlnet")), + io.Boolean.Input('use_motion', default=True), + io.Float.Input('motion_strength', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('motion_scale', default=1.0, max=10.0, min=0.0, step=0.001), + io.Custom('SPARSE_METHOD').Input('sparse_method', optional=True), + io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True), + io.Combo.Input('context_aware', optional=True, options=['nearest_hint', 'off']), + io.Float.Input('sparse_hint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('sparse_nonhint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('sparse_mask_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl" - - def load_controlnet(self, sparsectrl_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None, + @classmethod + def execute(cls, sparsectrl_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None, context_aware=SparseContextAware.NEAREST_HINT, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0): sparsectrl_path = folder_paths.get_full_path("controlnet", sparsectrl_name) sparse_settings = SparseSettings(sparse_method=sparse_method, use_motion=use_motion, motion_strength=motion_strength, motion_scale=motion_scale, context_aware=context_aware, sparse_mask_mult=sparse_mask_mult, sparse_hint_mult=sparse_hint_mult, sparse_nonhint_mult=sparse_nonhint_mult) sparsectrl = load_sparsectrl(sparsectrl_path, timestep_keyframe=tk_optional, sparse_settings=sparse_settings) - return (sparsectrl,) + return io.NodeOutput(sparsectrl,) - -class SparseCtrlMergedLoaderAdvanced: +class SparseCtrlMergedLoaderAdvanced(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "sparsectrl_name": (folder_paths.get_filename_list("controlnet"), ), - "control_net_name": (folder_paths.get_filename_list("controlnet"), ), - "use_motion": ("BOOLEAN", {"default": True}, ), - "motion_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - "optional": { - "sparse_method": ("SPARSE_METHOD", ), - "tk_optional": ("TIMESTEP_KEYFRAME", ), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_SparseCtrlMergedLoaderAdvanced', + display_name='๐ŸงชLoad Merged SparseCtrl Model ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl/experimental', + inputs=[ + io.Combo.Input('sparsectrl_name', options=folder_paths.get_filename_list("controlnet")), + io.Combo.Input('control_net_name', options=folder_paths.get_filename_list("controlnet")), + io.Boolean.Input('use_motion', default=True), + io.Float.Input('motion_strength', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('motion_scale', default=1.0, max=10.0, min=0.0, step=0.001), + io.Custom('SPARSE_METHOD').Input('sparse_method', optional=True), + io.Custom('TIMESTEP_KEYFRAME').Input('tk_optional', optional=True) + ], + outputs=[ + io.ControlNet.Output('CONTROL_NET', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET", ) - FUNCTION = "load_controlnet" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl/experimental" - - def load_controlnet(self, sparsectrl_name: str, control_net_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None): + @classmethod + def execute(cls, sparsectrl_name: str, control_net_name: str, use_motion: bool, motion_strength: float, motion_scale: float, sparse_method: SparseMethod=SparseSpreadMethod(), tk_optional: TimestepKeyframeGroup=None): sparsectrl_path = folder_paths.get_full_path("controlnet", sparsectrl_name) controlnet_path = folder_paths.get_full_path("controlnet", control_net_name) sparse_settings = SparseSettings(sparse_method=sparse_method, use_motion=use_motion, motion_strength=motion_strength, motion_scale=motion_scale, merged=True) @@ -85,64 +86,68 @@ class SparseCtrlMergedLoaderAdvanced: new_state_dict[key] = value # now, reload sparsectrl with real settings sparsectrl = load_sparsectrl(sparsectrl_path, controlnet_data=new_state_dict, timestep_keyframe=tk_optional, sparse_settings=sparse_settings) - return (sparsectrl,) + return io.NodeOutput(sparsectrl,) - -class SparseIndexMethodNode: +class SparseIndexMethodNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "indexes": ("STRING", {"default": "0"}), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_SparseCtrlIndexMethodNode', + display_name='SparseCtrl Index Method ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl', + inputs=[ + io.String.Input('indexes', default='0') + ], + outputs=[ + io.Custom('SPARSE_METHOD').Output('SPARSE_METHOD', is_output_list=False) + ] + ) - RETURN_TYPES = ("SPARSE_METHOD",) - FUNCTION = "get_method" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl" - - def get_method(self, indexes: str): + @classmethod + def execute(cls, indexes: str): idxs = get_idx_list_from_str(indexes) - return (SparseIndexMethod(idxs),) + return io.NodeOutput(SparseIndexMethod(idxs),) - -class SparseSpreadMethodNode: +class SparseSpreadMethodNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "spread": (SparseSpreadMethod.LIST,), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_SparseCtrlSpreadMethodNode', + display_name='SparseCtrl Spread Method ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl', + inputs=[ + io.Combo.Input('spread', options=['uniform', 'starting', 'ending', 'center']) + ], + outputs=[ + io.Custom('SPARSE_METHOD').Output('SPARSE_METHOD', is_output_list=False) + ] + ) - RETURN_TYPES = ("SPARSE_METHOD",) - FUNCTION = "get_method" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl" - - def get_method(self, spread: str): - return (SparseSpreadMethod(spread=spread),) - - -class RgbSparseCtrlPreprocessor: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "image": ("IMAGE", ), - "vae": ("VAE", ), - "latent_size": ("LATENT", ), - }, - } + def execute(cls, spread: str): + return io.NodeOutput(SparseSpreadMethod(spread=spread),) - RETURN_TYPES = ("IMAGE",) - RETURN_NAMES = ("proc_IMAGE",) - FUNCTION = "preprocess_images" +class RgbSparseCtrlPreprocessor(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_SparseCtrlRGBPreprocessor', + display_name='RGB SparseCtrl ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl/preprocess', + inputs=[ + io.Image.Input('image'), + io.Vae.Input('vae'), + io.Latent.Input('latent_size') + ], + outputs=[ + io.Image.Output('proc_IMAGE', is_output_list=False) + ] + ) - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl/preprocess" - - def preprocess_images(self, vae: VAE, image: Tensor, latent_size: Tensor): + @classmethod + def execute(cls, vae: VAE, image: Tensor, latent_size: Tensor): # first, resize image to match latents image = image.movedim(-1,1) image = comfy.utils.common_upscale(image, latent_size["samples"].shape[3] * 8, latent_size["samples"].shape[2] * 8, 'nearest-exact', "center") @@ -153,30 +158,31 @@ class RgbSparseCtrlPreprocessor: except Exception: image = VAEEncode.vae_encode_crop_pixels(image) encoded = vae.encode(image[:,:,:,:3]) - return (PreprocSparseRGBWrapper(condhint=encoded),) + return io.NodeOutput(PreprocSparseRGBWrapper(condhint=encoded),) - -class SparseWeightExtras: +class SparseWeightExtras(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "optional": { - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - "sparse_hint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "sparse_nonhint_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "sparse_mask_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_SparseCtrlWeightExtras', + display_name='SparseCtrl Weight Extras ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl/extras', + inputs=[ + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True), + io.Float.Input('sparse_hint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('sparse_nonhint_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('sparse_mask_mult', optional=True, default=1.0, max=10.0, min=0.0, step=0.001) + ], + outputs=[ + io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False) + ] + ) - RETURN_TYPES = ("CN_WEIGHTS_EXTRAS", ) - RETURN_NAMES = ("cn_extras", ) - FUNCTION = "create_weight_extras" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/SparseCtrl/extras" - - def create_weight_extras(self, cn_extras: dict[str]={}, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0): + @classmethod + def execute(cls, cn_extras: dict[str]={}, sparse_hint_mult=1.0, sparse_nonhint_mult=1.0, sparse_mask_mult=1.0): cn_extras = cn_extras.copy() cn_extras[SparseConst.HINT_MULT] = sparse_hint_mult cn_extras[SparseConst.NONHINT_MULT] = sparse_nonhint_mult cn_extras[SparseConst.MASK_MULT] = sparse_mask_mult - return (cn_extras, ) + return io.NodeOutput(cn_extras, ) diff --git a/adv_control/nodes_weight.py b/adv_control/nodes_weight.py index 5a94fb7..5e62c92 100644 --- a/adv_control/nodes_weight.py +++ b/adv_control/nodes_weight.py @@ -1,57 +1,56 @@ +from comfy_api.latest import io from torch import Tensor import torch from .utils import TimestepKeyframe, TimestepKeyframeGroup, ControlWeights, Extras, get_properly_arranged_t2i_weights, linear_conversion from .control_lllite import AnimaLLLiteConst -from .logger import logger - WEIGHTS_RETURN_NAMES = ("CN_WEIGHTS", "TK_SHORTCUT") - -class DefaultWeights: +class DefaultWeights(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "optional": { - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_DefaultUniversalWeights', + display_name='Default Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights', + inputs=[ + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights" - - def load_weights(self, cn_extras: dict[str]={}): + @classmethod + def execute(cls, cn_extras: dict[str]={}): weights = ControlWeights.default(extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class ScaledSoftMaskedUniversalWeights: +class ScaledSoftMaskedUniversalWeights(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "mask": ("MASK", ), - "min_base_multiplier": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ), - "max_base_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}, ), - #"lock_min": ("BOOLEAN", {"default": False}, ), - #"lock_max": ("BOOLEAN", {"default": False}, ), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ScaledSoftMaskedUniversalWeights', + display_name='Scaled Soft Masked Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights', + inputs=[ + io.Mask.Input('mask'), + io.Float.Input('min_base_multiplier', default=0.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('max_base_multiplier', default=1.0, max=1.0, min=0.0, step=0.001), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights" - - def load_weights(self, mask: Tensor, min_base_multiplier: float, max_base_multiplier: float, lock_min=False, lock_max=False, + @classmethod + def execute(cls, mask: Tensor, min_base_multiplier: float, max_base_multiplier: float, lock_min=False, lock_max=False, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): # normalize mask mask = mask.clone() @@ -62,107 +61,107 @@ class ScaledSoftMaskedUniversalWeights: else: mask = linear_conversion(mask, x_min, x_max, min_base_multiplier, max_base_multiplier) weights = ControlWeights.universal_mask(weight_mask=mask, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class ScaledSoftUniversalWeights: +class ScaledSoftUniversalWeights(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "base_multiplier": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 1.0, "step": 0.001}, ), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ScaledSoftControlNetWeights', + display_name='Scaled Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights', + inputs=[ + io.Float.Input('base_multiplier', default=0.825, max=1.0, min=0.0, step=0.001), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights" - - def load_weights(self, base_multiplier, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): + @classmethod + def execute(cls, base_multiplier, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): weights = ControlWeights.universal(base_multiplier=base_multiplier, uncond_multiplier=uncond_multiplier, extras=cn_extras) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class SoftControlNetWeightsSD15: +class SoftControlNetWeightsSD15(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "output_0": ("FLOAT", {"default": 0.09941396206337118, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_1": ("FLOAT", {"default": 0.12050177219802567, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_2": ("FLOAT", {"default": 0.14606275417942507, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_3": ("FLOAT", {"default": 0.17704576264172736, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_4": ("FLOAT", {"default": 0.214600924414215, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_5": ("FLOAT", {"default": 0.26012233262329093, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_6": ("FLOAT", {"default": 0.3152997971191405, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_7": ("FLOAT", {"default": 0.3821815722656249, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_8": ("FLOAT", {"default": 0.4632503906249999, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_9": ("FLOAT", {"default": 0.561515625, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_10": ("FLOAT", {"default": 0.6806249999999999, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_11": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "middle_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_SoftControlNetWeightsSD15', + display_name='ControlNet Soft Weights [SD1.5] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/ControlNet', + inputs=[ + io.Float.Input('output_0', default=0.09941396206337118, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_1', default=0.12050177219802567, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_2', default=0.14606275417942507, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_3', default=0.17704576264172736, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_4', default=0.214600924414215, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_5', default=0.26012233262329093, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_6', default=0.3152997971191405, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_7', default=0.3821815722656249, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_8', default=0.4632503906249999, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_9', default=0.561515625, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_10', default=0.6806249999999999, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_11', default=0.825, max=10.0, min=0.0, step=0.001), + io.Float.Input('middle_0', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/ControlNet" - - def load_weights(self, output_0, output_1, output_2, output_3, output_4, output_5, output_6, + @classmethod + def execute(cls, output_0, output_1, output_2, output_3, output_4, output_5, output_6, output_7, output_8, output_9, output_10, output_11, middle_0, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): - return CustomControlNetWeightsSD15.load_weights(self, + return CustomControlNetWeightsSD15.execute( output_0=output_0, output_1=output_1, output_2=output_2, output_3=output_3, output_4=output_4, output_5=output_5, output_6=output_6, output_7=output_7, output_8=output_8, output_9=output_9, output_10=output_10, output_11=output_11, middle_0=middle_0, uncond_multiplier=uncond_multiplier, cn_extras=cn_extras) - -class CustomControlNetWeightsSD15: +class CustomControlNetWeightsSD15(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "output_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_4": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_5": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_6": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_7": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_8": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_9": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "output_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "middle_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_CustomControlNetWeightsSD15', + display_name='ControlNet Custom Weights [SD1.5] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/ControlNet', + inputs=[ + io.Float.Input('output_0', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_1', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_2', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_3', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_4', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_5', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_6', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_7', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_8', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_9', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_10', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('output_11', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('middle_0', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/ControlNet" - - def load_weights(self, output_0, output_1, output_2, output_3, output_4, output_5, output_6, + @classmethod + def execute(cls, output_0, output_1, output_2, output_3, output_4, output_5, output_6, output_7, output_8, output_9, output_10, output_11, middle_0, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): weights_output = [output_0, output_1, output_2, output_3, output_4, output_5, output_6, @@ -170,47 +169,47 @@ class CustomControlNetWeightsSD15: weights_middle = [middle_0] weights = ControlWeights.controlnet(weights_output=weights_output, weights_middle=weights_middle, uncond_multiplier=uncond_multiplier, extras=cn_extras, disable_applied_to=True) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class CustomControlNetWeightsFlux: +class CustomControlNetWeightsFlux(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "input_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_4": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_5": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_6": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_7": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_8": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_9": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_10": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_11": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_12": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_13": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_14": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_15": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_16": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_17": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_18": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_CustomControlNetWeightsFlux', + display_name='ControlNet Custom Weights [Flux] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/ControlNet', + inputs=[ + io.Float.Input('input_0', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_1', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_2', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_4', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_5', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_6', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_7', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_8', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_9', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_10', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_11', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_12', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_13', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_14', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_15', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_16', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_17', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_18', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/ControlNet" - - def load_weights(self, input_0, input_1, input_2, input_3, input_4, input_5, input_6, + @classmethod + def execute(cls, input_0, input_1, input_2, input_3, input_4, input_5, input_6, input_7, input_8, input_9, input_10, input_11, input_12, input_13, input_14, input_15, input_16, input_17, input_18, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): @@ -218,139 +217,162 @@ class CustomControlNetWeightsFlux: input_6, input_7, input_8, input_9, input_10, input_11, input_12, input_13, input_14, input_15, input_16, input_17, input_18] weights = ControlWeights.controlnet(weights_input=weights_input, uncond_multiplier=uncond_multiplier, extras=cn_extras, disable_applied_to=True) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class CustomControlNetWeightsAnima: +class CustomControlNetWeightsAnima(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - required = { - f"block_{index}": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}) - for index in range(28) - } - return { - "required": required, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_CustomControlNetWeightsAnima', + display_name='ControlNet Custom Weights [Anima] ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/ControlNet', + inputs=[ + io.Float.Input('block_0', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_1', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_2', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_3', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_4', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_5', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_6', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_7', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_8', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_9', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_10', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_11', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_12', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_13', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_14', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_15', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_16', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_17', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_18', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_19', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_20', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_21', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_22', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_23', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_24', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_25', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_26', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('block_27', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/ControlNet" - - def load_weights(self, uncond_multiplier: float=1.0, cn_extras: dict[str]={}, **kwargs): + @classmethod + def execute(cls, uncond_multiplier: float=1.0, cn_extras: dict[str]={}, **kwargs): weights = [kwargs[f"block_{index}"] for index in range(28)] control_weights = ControlWeights.controllllite( weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras, ) - return (control_weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=control_weights))) + return io.NodeOutput(control_weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=control_weights))) - -class SoftT2IAdapterWeights: +class SoftT2IAdapterWeights(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "input_0": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_1": ("FLOAT", {"default": 0.62, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_2": ("FLOAT", {"default": 0.825, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_SoftT2IAdapterWeights', + display_name='T2IAdapter Soft Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/T2IAdapter', + inputs=[ + io.Float.Input('input_0', default=0.25, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_1', default=0.62, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_2', default=0.825, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/T2IAdapter" - - def load_weights(self, input_0, input_1, input_2, input_3, + @classmethod + def execute(cls, input_0, input_1, input_2, input_3, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): - return CustomT2IAdapterWeights.load_weights(self, input_0=input_0, input_1=input_1, input_2=input_2, input_3=input_3, + return CustomT2IAdapterWeights.execute(input_0=input_0, input_1=input_1, input_2=input_2, input_3=input_3, uncond_multiplier=uncond_multiplier, cn_extras=cn_extras) - -class CustomT2IAdapterWeights: +class CustomT2IAdapterWeights(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "input_0": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - "input_3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}, ), - }, - "optional": { - "uncond_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}, ), - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_CustomT2IAdapterWeights', + display_name='T2IAdapter Custom Weights ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/T2IAdapter', + inputs=[ + io.Float.Input('input_0', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_1', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_2', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('input_3', default=1.0, max=10.0, min=0.0, step=0.001), + io.Float.Input('uncond_multiplier', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CONTROL_NET_WEIGHTS').Output('CN_WEIGHTS', is_output_list=False), + io.Custom('TIMESTEP_KEYFRAME').Output('TK_SHORTCUT', is_output_list=False) + ] + ) - RETURN_TYPES = ("CONTROL_NET_WEIGHTS", "TIMESTEP_KEYFRAME",) - RETURN_NAMES = WEIGHTS_RETURN_NAMES - FUNCTION = "load_weights" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/T2IAdapter" - - def load_weights(self, input_0, input_1, input_2, input_3, + @classmethod + def execute(cls, input_0, input_1, input_2, input_3, uncond_multiplier: float=1.0, cn_extras: dict[str]={}): weights = [input_0, input_1, input_2, input_3] weights = get_properly_arranged_t2i_weights(weights) weights = ControlWeights.t2iadapter(weights_input=weights, uncond_multiplier=uncond_multiplier, extras=cn_extras, disable_applied_to=True) - return (weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) + return io.NodeOutput(weights, TimestepKeyframeGroup.default(TimestepKeyframe(control_weights=weights))) - -class ExtrasMiddleMultNode: +class ExtrasMiddleMultNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "middle_mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}), - }, - "optional": { - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_ExtrasMiddleMult', + display_name='Middle Weight Extras ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/extras', + inputs=[ + io.Float.Input('middle_mult', default=1.0, max=10.0, min=0.0, step=0.001), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False) + ] + ) - RETURN_TYPES = ("CN_WEIGHTS_EXTRAS",) - RETURN_NAMES = ("cn_extras",) - FUNCTION = "create_extras" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/extras" - - def create_extras(self, middle_mult: float, cn_extras: dict[str]={}): + @classmethod + def execute(cls, middle_mult: float, cn_extras: dict[str]={}): cn_extras = cn_extras.copy() cn_extras[Extras.MIDDLE_MULT] = middle_mult - return (cn_extras,) + return io.NodeOutput(cn_extras,) - -class AnimaLLLiteExtras: +class AnimaLLLiteExtras(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "inpaint_mask": ("MASK",), - }, - "optional": { - "cn_extras": ("CN_WEIGHTS_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ACN_AnimaLLLiteExtras', + display_name='Anima LLLite Extras ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…', + category='Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/extras', + inputs=[ + io.Mask.Input('inpaint_mask'), + io.Custom('CN_WEIGHTS_EXTRAS').Input('cn_extras', optional=True) + ], + outputs=[ + io.Custom('CN_WEIGHTS_EXTRAS').Output('cn_extras', is_output_list=False) + ] + ) - RETURN_TYPES = ("CN_WEIGHTS_EXTRAS",) - RETURN_NAMES = ("cn_extras",) - FUNCTION = "create_extras" - CATEGORY = "Adv-ControlNet ๐Ÿ›‚๐Ÿ…๐Ÿ…’๐Ÿ…/weights/extras" - - def create_extras(self, inpaint_mask: Tensor, cn_extras: dict[str]={}): + @classmethod + def execute(cls, inpaint_mask: Tensor, cn_extras: dict[str]={}): cn_extras = cn_extras.copy() cn_extras[AnimaLLLiteConst.INPAINT_MASK] = inpaint_mask.clone() - return (cn_extras,) + return io.NodeOutput(cn_extras,)