From 8a0e70656771d8b3bfcba0d72bc006606cbd4c6c Mon Sep 17 00:00:00 2001 From: Jedrzej Kosinski Date: Fri, 17 Jul 2026 19:06:02 -0700 Subject: [PATCH] Migrate all nodes to the V3 API --- __init__.py | 8 +- animatediff/nodes.py | 735 +++++++++++------------- animatediff/nodes_ad_settings.py | 410 +++++++------- animatediff/nodes_animatelcmi2v.py | 164 +++--- animatediff/nodes_cameractrl.py | 443 ++++++++------- animatediff/nodes_conditioning.py | 737 +++++++----------------- animatediff/nodes_context.py | 477 ++++------------ animatediff/nodes_context_extras.py | 456 ++++----------- animatediff/nodes_deprecated.py | 545 ++++-------------- animatediff/nodes_extras.py | 141 +++-- animatediff/nodes_gen1.py | 52 +- animatediff/nodes_gen2.py | 202 +++---- animatediff/nodes_lora.py | 35 +- animatediff/nodes_multival.py | 165 +++--- animatediff/nodes_per_block.py | 318 ++++------- animatediff/nodes_pia.py | 210 +++---- animatediff/nodes_sample.py | 837 +++++++++++++++------------- animatediff/nodes_scheduling.py | 227 +++----- animatediff/nodes_sigma_schedule.py | 223 ++++---- 19 files changed, 2515 insertions(+), 3870 deletions(-) diff --git a/__init__.py b/__init__.py index 46ab672..7a82545 100644 --- a/__init__.py +++ b/__init__.py @@ -3,7 +3,7 @@ from .animatediff.logger import logger from .animatediff.utils_model import get_available_motion_models, Folders from .animatediff.model_injection import prepare_dinklink_register_definitions from .animatediff.motion_module_ad import prepare_dinklink_motion_module_ad -from .animatediff.nodes import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS +from .animatediff.nodes import AnimateDiffExtension from .animatediff.dinklink import init_dinklink if len(get_available_motion_models()) == 0: @@ -11,8 +11,12 @@ if len(get_available_motion_models()) == 0: WEB_DIRECTORY = "./web" -__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"] +__all__ = ["WEB_DIRECTORY"] init_dinklink() prepare_dinklink_register_definitions() prepare_dinklink_motion_module_ad() + + +async def comfy_entrypoint() -> AnimateDiffExtension: + return AnimateDiffExtension() diff --git a/animatediff/nodes.py b/animatediff/nodes.py index 4a8e5d4..be150b3 100644 --- a/animatediff/nodes.py +++ b/animatediff/nodes.py @@ -1,415 +1,328 @@ -import comfy.sample as comfy_sample +from comfy_api.latest import ComfyExtension, io -from .nodes_gen1 import (AnimateDiffLoaderGen1,) -from .nodes_gen2 import (UseEvolvedSamplingNode, ApplyAnimateDiffModelNode, ApplyAnimateDiffModelBasicNode, ADKeyframeNode, - LoadAnimateDiffModelNode) -from .nodes_animatelcmi2v import (ApplyAnimateLCMI2VModel, LoadAnimateLCMI2VModelNode, LoadAnimateDiffAndInjectI2VNode, UpscaleAndVaeEncode) -from .nodes_cameractrl import (LoadAnimateDiffModelWithCameraCtrl, ApplyAnimateDiffWithCameraCtrl, CameraCtrlADKeyframeNode, - LoadCameraPosesFromFile, LoadCameraPosesFromPath, - CameraCtrlPoseBasic, CameraCtrlPoseCombo, CameraCtrlPoseAdvanced, CameraCtrlManualAppendPose, - CameraCtrlReplaceCameraParameters, CameraCtrlSetOriginalAspectRatio) -from .nodes_motionctrl import (LoadMotionCtrlCMCM, LoadMotionCtrlOMCM, ApplyAnimateDiffMotionCtrlModel, LoadMotionCtrlCameraPosesFromFile) -from .nodes_pia import (ApplyAnimateDiffPIAModel, LoadAnimateDiffAndInjectPIANode, InputPIA_MultivalNode, InputPIA_PaperPresetsNode, PIA_ADKeyframeNode) -from .nodes_fancyvideo import (ApplyAnimateDiffFancyVideo,) -from .nodes_hellomeme import (TestHMRefNetInjection,) -from .nodes_multival import MultivalDynamicNode, MultivalScaledMaskNode, MultivalDynamicFloatInputNode, MultivalDynamicFloatsNode, MultivalConvertToMaskNode -from .nodes_conditioning import (CreateLoraHookKeyframeInterpolationDEPR, - MaskableLoraLoaderDEPR, MaskableLoraLoaderModelOnlyDEPR, MaskableSDModelLoaderDEPR, MaskableSDModelLoaderModelOnlyDEPR, - SetModelLoraHookDEPR, SetClipLoraHookDEPR, - CombineLoraHooksDEPR, CombineLoraHookFourOptionalDEPR, CombineLoraHookEightOptionalDEPR, - PairedConditioningSetMaskHookedDEPR, ConditioningSetMaskHookedDEPR, - PairedConditioningSetMaskAndCombineHookedDEPR, ConditioningSetMaskAndCombineHookedDEPR, - PairedConditioningSetUnmaskedAndCombineHookedDEPR, ConditioningSetUnmaskedAndCombineHookedDEPR, - PairedConditioningCombineDEPR, ConditioningCombineDEPR, - ConditioningTimestepsNodeDEPR, SetLoraHookKeyframesDEPR, - CreateLoraHookKeyframeDEPR, CreateLoraHookKeyframeFromStrengthListDEPR) -from .nodes_sample import (FreeInitOptionsNode, NoiseLayerAddWeightedNode, NoiseLayerNormalizedSumNode, SampleSettingsNode, NoiseLayerAddNode, NoiseLayerReplaceNode, IterationOptionsNode, - CustomCFGNode, CustomCFGSimpleNode, CustomCFGKeyframeNode, CustomCFGKeyframeSimpleNode, CustomCFGKeyframeInterpolationNode, CustomCFGKeyframeFromListNode, - CFGExtrasPAGNode, CFGExtrasPAGSimpleNode, CFGExtrasRescaleCFGNode, CFGExtrasRescaleCFGSimpleNode, - NoisedImageInjectionNode, NoisedImageInjectOptionsNode, NoiseCalibrationNode, AncestralOptionsNode) -from .nodes_sigma_schedule import (SigmaScheduleNode, RawSigmaScheduleNode, WeightedAverageSigmaScheduleNode, InterpolatedWeightedAverageSigmaScheduleNode, SplitAndCombineSigmaScheduleNode, SigmaScheduleToSigmasNode) -from .nodes_context import (LegacyLoopedUniformContextOptionsNode, LoopedUniformContextOptionsNode, LoopedUniformViewOptionsNode, StandardUniformContextOptionsNode, StandardStaticContextOptionsNode, BatchedContextOptionsNode, - StandardStaticViewOptionsNode, StandardUniformViewOptionsNode, ViewAsContextOptionsNode, - VisualizeContextOptionsK, VisualizeContextOptionsKAdv, VisualizeContextOptionsSCustom) -from .nodes_context_extras import (SetContextExtrasOnContextOptions, ContextExtras_NaiveReuse, ContextExtras_ContextRef, - ContextRef_ModeFirst, ContextRef_ModeSliding, ContextRef_ModeIndexes, - ContextRef_TuneAttn, ContextRef_TuneAttnAdain, - ContextRef_KeyframeMultivalNode, ContextRef_KeyframeInterpolationNode, ContextRef_KeyframeFromListNode, - NaiveReuse_KeyframeMultivalNode, NaiveReuse_KeyframeInterpolationNode, NaiveReuse_KeyframeFromListNode) -from .nodes_ad_settings import (AnimateDiffSettingsNode, ManualAdjustPENode, SweetspotStretchPENode, FullStretchPENode, - WeightAdjustAllAddNode, WeightAdjustAllMultNode, WeightAdjustIndivAddNode, WeightAdjustIndivMultNode, - WeightAdjustIndivAttnAddNode, WeightAdjustIndivAttnMultNode) -from .nodes_scheduling import (ConditionExtractionNode, PromptSchedulingNode, PromptSchedulingLatentsNode, ValueSchedulingNode, ValueSchedulingLatentsNode, - AddValuesReplaceNode, FloatToFloatsNode) -from .nodes_per_block import (ADBlockComboNode, ADBlockIndivNode, PerBlockHighLevelNode, - PerBlock_SD15_LowLevelNode, PerBlock_SD15_MidLevelNode, PerBlock_SD15_FromFloatsNode, - PerBlock_SDXL_LowLevelNode, PerBlock_SDXL_MidLevelNode, PerBlock_SDXL_FromFloatsNode) -from .nodes_extras import AnimateDiffUnload, EmptyLatentImageLarge, CheckpointLoaderSimpleWithNoiseSelect, PerturbedAttentionGuidanceMultival, RescaleCFGMultival -from .nodes_deprecated import (AnimateDiffLoaderDEPR, AnimateDiffLoaderAdvancedDEPR, LegacyAnimateDiffLoaderWithContextDEPR, AnimateDiffCombineDEPR, - AnimateDiffModelSettingsDEPR, AnimateDiffModelSettingsSimpleDEPR, AnimateDiffModelSettingsAdvancedDEPR, AnimateDiffModelSettingsAdvancedAttnStrengthsDEPR) +from .nodes_ad_settings import ( + AnimateDiffSettingsNode, + FullStretchPENode, + ManualAdjustPENode, + SweetspotStretchPENode, + WeightAdjustAllAddNode, + WeightAdjustAllMultNode, + WeightAdjustIndivAddNode, + WeightAdjustIndivAttnAddNode, + WeightAdjustIndivAttnMultNode, + WeightAdjustIndivMultNode, +) +from .nodes_animatelcmi2v import ( + ApplyAnimateLCMI2VModel, + LoadAnimateDiffAndInjectI2VNode, + LoadAnimateLCMI2VModelNode, + UpscaleAndVaeEncode, +) +from .nodes_cameractrl import ( + ApplyAnimateDiffWithCameraCtrl, + CameraCtrlADKeyframeNode, + CameraCtrlManualAppendPose, + CameraCtrlPoseAdvanced, + CameraCtrlPoseBasic, + CameraCtrlPoseCombo, + CameraCtrlReplaceCameraParameters, + CameraCtrlSetOriginalAspectRatio, + LoadAnimateDiffModelWithCameraCtrl, + LoadCameraPosesFromFile, + LoadCameraPosesFromPath, +) +from .nodes_conditioning import ( + CombineLoraHookEightOptionalDEPR, + CombineLoraHookFourOptionalDEPR, + CombineLoraHooksDEPR, + ConditioningCombineDEPR, + ConditioningSetMaskAndCombineHookedDEPR, + ConditioningSetMaskHookedDEPR, + ConditioningSetUnmaskedAndCombineHookedDEPR, + ConditioningTimestepsNodeDEPR, + CreateLoraHookKeyframeDEPR, + CreateLoraHookKeyframeFromStrengthListDEPR, + CreateLoraHookKeyframeInterpolationDEPR, + MaskableLoraLoaderDEPR, + MaskableLoraLoaderModelOnlyDEPR, + MaskableSDModelLoaderDEPR, + MaskableSDModelLoaderModelOnlyDEPR, + PairedConditioningCombineDEPR, + PairedConditioningSetMaskAndCombineHookedDEPR, + PairedConditioningSetMaskHookedDEPR, + PairedConditioningSetUnmaskedAndCombineHookedDEPR, + SetClipLoraHookDEPR, + SetLoraHookKeyframesDEPR, + SetModelLoraHookDEPR, +) +from .nodes_context import ( + BatchedContextOptionsNode, + LegacyLoopedUniformContextOptionsNode, + LoopedUniformContextOptionsNode, + LoopedUniformViewOptionsNode, + StandardStaticContextOptionsNode, + StandardStaticViewOptionsNode, + StandardUniformContextOptionsNode, + StandardUniformViewOptionsNode, + ViewAsContextOptionsNode, + VisualizeContextOptionsK, + VisualizeContextOptionsKAdv, + VisualizeContextOptionsSCustom, +) +from .nodes_context_extras import ( + ContextExtras_ContextRef, + ContextExtras_NaiveReuse, + ContextRef_KeyframeFromListNode, + ContextRef_KeyframeInterpolationNode, + ContextRef_KeyframeMultivalNode, + ContextRef_ModeFirst, + ContextRef_ModeIndexes, + ContextRef_ModeSliding, + ContextRef_TuneAttn, + ContextRef_TuneAttnAdain, + NaiveReuse_KeyframeFromListNode, + NaiveReuse_KeyframeInterpolationNode, + NaiveReuse_KeyframeMultivalNode, + SetContextExtrasOnContextOptions, +) +from .nodes_deprecated import ( + AnimateDiffCombineDEPR, + AnimateDiffLoaderAdvancedDEPR, + AnimateDiffLoaderDEPR, + AnimateDiffModelSettingsAdvancedAttnStrengthsDEPR, + AnimateDiffModelSettingsAdvancedDEPR, + AnimateDiffModelSettingsDEPR, + AnimateDiffModelSettingsSimpleDEPR, + LegacyAnimateDiffLoaderWithContextDEPR, +) +from .nodes_extras import ( + AnimateDiffUnload, + CheckpointLoaderSimpleWithNoiseSelect, + EmptyLatentImageLarge, + PerturbedAttentionGuidanceMultival, + RescaleCFGMultival, +) +from .nodes_gen1 import AnimateDiffLoaderGen1 +from .nodes_gen2 import ( + ADKeyframeNode, + ApplyAnimateDiffModelBasicNode, + ApplyAnimateDiffModelNode, + LoadAnimateDiffModelNode, + UseEvolvedSamplingNode, +) from .nodes_lora import AnimateDiffLoraLoader - -from .logger import logger +from .nodes_multival import ( + MultivalConvertToMaskNode, + MultivalDynamicFloatInputNode, + MultivalDynamicFloatsNode, + MultivalDynamicNode, + MultivalScaledMaskNode, +) +from .nodes_per_block import ( + ADBlockComboNode, + ADBlockIndivNode, + PerBlockHighLevelNode, + PerBlock_SD15_FromFloatsNode, + PerBlock_SD15_LowLevelNode, + PerBlock_SD15_MidLevelNode, + PerBlock_SDXL_FromFloatsNode, + PerBlock_SDXL_LowLevelNode, + PerBlock_SDXL_MidLevelNode, +) +from .nodes_pia import ( + ApplyAnimateDiffPIAModel, + InputPIA_MultivalNode, + InputPIA_PaperPresetsNode, + LoadAnimateDiffAndInjectPIANode, + PIA_ADKeyframeNode, +) +from .nodes_sample import ( + AncestralOptionsNode, + CFGExtrasPAGNode, + CFGExtrasPAGSimpleNode, + CFGExtrasRescaleCFGNode, + CFGExtrasRescaleCFGSimpleNode, + CustomCFGKeyframeFromListNode, + CustomCFGKeyframeInterpolationNode, + CustomCFGKeyframeNode, + CustomCFGKeyframeSimpleNode, + CustomCFGNode, + CustomCFGSimpleNode, + FreeInitOptionsNode, + IterationOptionsNode, + NoisedImageInjectionNode, + NoisedImageInjectOptionsNode, + NoiseLayerAddNode, + NoiseLayerAddWeightedNode, + NoiseLayerNormalizedSumNode, + NoiseLayerReplaceNode, + SampleSettingsNode, +) +from .nodes_scheduling import ( + AddValuesReplaceNode, + ConditionExtractionNode, + FloatToFloatsNode, + PromptSchedulingLatentsNode, + PromptSchedulingNode, + ValueSchedulingLatentsNode, + ValueSchedulingNode, +) +from .nodes_sigma_schedule import ( + InterpolatedWeightedAverageSigmaScheduleNode, + RawSigmaScheduleNode, + SigmaScheduleNode, + SigmaScheduleToSigmasNode, + SplitAndCombineSigmaScheduleNode, + WeightedAverageSigmaScheduleNode, +) -NODE_CLASS_MAPPINGS = { - # Unencapsulated - "ADE_AnimateDiffLoRALoader": AnimateDiffLoraLoader, - "ADE_AnimateDiffSamplingSettings": SampleSettingsNode, - "ADE_AnimateDiffKeyframe": ADKeyframeNode, - # Multival Nodes - "ADE_MultivalDynamic": MultivalDynamicNode, - "ADE_MultivalDynamicFloatInput": MultivalDynamicFloatInputNode, - "ADE_MultivalDynamicFloats": MultivalDynamicFloatsNode, - "ADE_MultivalScaledMask": MultivalScaledMaskNode, - "ADE_MultivalConvertToMask": MultivalConvertToMaskNode, - ############################################################################### - #------------------------------------------------------------------------------ - # Context Opts - "ADE_StandardStaticContextOptions": StandardStaticContextOptionsNode, - "ADE_StandardUniformContextOptions": StandardUniformContextOptionsNode, - "ADE_LoopedUniformContextOptions": LoopedUniformContextOptionsNode, - "ADE_ViewsOnlyContextOptions": ViewAsContextOptionsNode, - "ADE_BatchedContextOptions": BatchedContextOptionsNode, - "ADE_AnimateDiffUniformContextOptions": LegacyLoopedUniformContextOptionsNode, # Legacy/Deprecated - "ADE_VisualizeContextOptionsK": VisualizeContextOptionsK, - "ADE_VisualizeContextOptionsKAdv": VisualizeContextOptionsKAdv, - "ADE_VisualizeContextOptionsSCustom": VisualizeContextOptionsSCustom, - # View Opts - "ADE_StandardStaticViewOptions": StandardStaticViewOptionsNode, - "ADE_StandardUniformViewOptions": StandardUniformViewOptionsNode, - "ADE_LoopedUniformViewOptions": LoopedUniformViewOptionsNode, - # Context Extras - "ADE_ContextExtras_Set": SetContextExtrasOnContextOptions, - "ADE_ContextExtras_ContextRef": ContextExtras_ContextRef, - "ADE_ContextExtras_ContextRef_ModeFirst": ContextRef_ModeFirst, - "ADE_ContextExtras_ContextRef_ModeSliding": ContextRef_ModeSliding, - "ADE_ContextExtras_ContextRef_ModeIndexes": ContextRef_ModeIndexes, - "ADE_ContextExtras_ContextRef_TuneAttn": ContextRef_TuneAttn, - "ADE_ContextExtras_ContextRef_TuneAttnAdain": ContextRef_TuneAttnAdain, - "ADE_ContextExtras_ContextRef_Keyframe": ContextRef_KeyframeMultivalNode, - "ADE_ContextExtras_ContextRef_KeyframeInterpolation": ContextRef_KeyframeInterpolationNode, - "ADE_ContextExtras_ContextRef_KeyframeFromList": ContextRef_KeyframeFromListNode, - "ADE_ContextExtras_NaiveReuse": ContextExtras_NaiveReuse, - "ADE_ContextExtras_NaiveReuse_Keyframe": NaiveReuse_KeyframeMultivalNode, - "ADE_ContextExtras_NaiveReuse_KeyframeInterpolation": NaiveReuse_KeyframeInterpolationNode, - "ADE_ContextExtras_NaiveReuse_KeyframeFromList": NaiveReuse_KeyframeFromListNode, - #------------------------------------------------------------------------------ - ############################################################################### - # Iteration Opts - "ADE_IterationOptsDefault": IterationOptionsNode, - "ADE_IterationOptsFreeInit": FreeInitOptionsNode, - # Conditioning - # Conditioning (DEPRECATED) - "ADE_RegisterLoraHook": MaskableLoraLoaderDEPR, - "ADE_RegisterLoraHookModelOnly": MaskableLoraLoaderModelOnlyDEPR, - "ADE_RegisterModelAsLoraHook": MaskableSDModelLoaderDEPR, - "ADE_RegisterModelAsLoraHookModelOnly": MaskableSDModelLoaderModelOnlyDEPR, - "ADE_CombineLoraHooks": CombineLoraHooksDEPR, - "ADE_CombineLoraHooksFour": CombineLoraHookFourOptionalDEPR, - "ADE_CombineLoraHooksEight": CombineLoraHookEightOptionalDEPR, - "ADE_SetLoraHookKeyframe": SetLoraHookKeyframesDEPR, - "ADE_AttachLoraHookToCLIP": SetClipLoraHookDEPR, - "ADE_LoraHookKeyframe": CreateLoraHookKeyframeDEPR, - "ADE_LoraHookKeyframeInterpolation": CreateLoraHookKeyframeInterpolationDEPR, - "ADE_LoraHookKeyframeFromStrengthList": CreateLoraHookKeyframeFromStrengthListDEPR, - "ADE_AttachLoraHookToConditioning": SetModelLoraHookDEPR, - "ADE_PairedConditioningSetMask": PairedConditioningSetMaskHookedDEPR, - "ADE_ConditioningSetMask": ConditioningSetMaskHookedDEPR, - "ADE_PairedConditioningSetMaskAndCombine": PairedConditioningSetMaskAndCombineHookedDEPR, - "ADE_ConditioningSetMaskAndCombine": ConditioningSetMaskAndCombineHookedDEPR, - "ADE_PairedConditioningSetUnmaskedAndCombine": PairedConditioningSetUnmaskedAndCombineHookedDEPR, - "ADE_ConditioningSetUnmaskedAndCombine": ConditioningSetUnmaskedAndCombineHookedDEPR, - "ADE_PairedConditioningCombine": PairedConditioningCombineDEPR, - "ADE_ConditioningCombine": ConditioningCombineDEPR, - "ADE_TimestepsConditioning": ConditioningTimestepsNodeDEPR, - # Noise Layer Nodes - "ADE_NoiseLayerAdd": NoiseLayerAddNode, - "ADE_NoiseLayerAddWeighted": NoiseLayerAddWeightedNode, - "ADE_NoiseLayerNormalizedSum": NoiseLayerNormalizedSumNode, - "ADE_NoiseLayerReplace": NoiseLayerReplaceNode, - # AnimateDiff Settings - "ADE_AnimateDiffSettings": AnimateDiffSettingsNode, - "ADE_AdjustPESweetspotStretch": SweetspotStretchPENode, - "ADE_AdjustPEFullStretch": FullStretchPENode, - "ADE_AdjustPEManual": ManualAdjustPENode, - "ADE_AdjustWeightAllAdd": WeightAdjustAllAddNode, - "ADE_AdjustWeightAllMult": WeightAdjustAllMultNode, - "ADE_AdjustWeightIndivAdd": WeightAdjustIndivAddNode, - "ADE_AdjustWeightIndivMult": WeightAdjustIndivMultNode, - "ADE_AdjustWeightIndivAttnAdd": WeightAdjustIndivAttnAddNode, - "ADE_AdjustWeightIndivAttnMult": WeightAdjustIndivAttnMultNode, - # Sample Settings - "ADE_CustomCFGSimple": CustomCFGSimpleNode, - "ADE_CustomCFG": CustomCFGNode, - "ADE_CustomCFGKeyframeSimple": CustomCFGKeyframeSimpleNode, - "ADE_CustomCFGKeyframe": CustomCFGKeyframeNode, - "ADE_CustomCFGKeyframeInterpolation": CustomCFGKeyframeInterpolationNode, - "ADE_CustomCFGKeyframeFromList": CustomCFGKeyframeFromListNode, - "ADE_CFGExtrasPAGSimple": CFGExtrasPAGSimpleNode, - "ADE_CFGExtrasPAG": CFGExtrasPAGNode, - "ADE_CFGExtrasRescaleCFGSimple": CFGExtrasRescaleCFGSimpleNode, - "ADE_CFGExtrasRescaleCFG": CFGExtrasRescaleCFGNode, - "ADE_SigmaSchedule": SigmaScheduleNode, - "ADE_RawSigmaSchedule": RawSigmaScheduleNode, - "ADE_SigmaScheduleWeightedAverage": WeightedAverageSigmaScheduleNode, - "ADE_SigmaScheduleWeightedAverageInterp": InterpolatedWeightedAverageSigmaScheduleNode, - "ADE_SigmaScheduleSplitAndCombine": SplitAndCombineSigmaScheduleNode, - "ADE_SigmaScheduleToSigmas": SigmaScheduleToSigmasNode, - "ADE_NoisedImageInjection": NoisedImageInjectionNode, - "ADE_NoisedImageInjectOptions": NoisedImageInjectOptionsNode, - "ADE_AncestralOptions": AncestralOptionsNode, - #"ADE_NoiseCalibration": NoiseCalibrationNode, - # Scheduling - PromptSchedulingNode.NodeID: PromptSchedulingNode, - PromptSchedulingLatentsNode.NodeID: PromptSchedulingLatentsNode, - ValueSchedulingNode.NodeID: ValueSchedulingNode, - ValueSchedulingLatentsNode.NodeID: ValueSchedulingLatentsNode, - ConditionExtractionNode.NodeID: ConditionExtractionNode, - AddValuesReplaceNode.NodeID: AddValuesReplaceNode, - FloatToFloatsNode.NodeID: FloatToFloatsNode, - # Per-Block - ADBlockComboNode.NodeID: ADBlockComboNode, - ADBlockIndivNode.NodeID: ADBlockIndivNode, - PerBlockHighLevelNode.NodeID: PerBlockHighLevelNode, - PerBlock_SD15_MidLevelNode.NodeID: PerBlock_SD15_MidLevelNode, - PerBlock_SD15_LowLevelNode.NodeID: PerBlock_SD15_LowLevelNode, - PerBlock_SD15_FromFloatsNode.NodeID: PerBlock_SD15_FromFloatsNode, - PerBlock_SDXL_MidLevelNode.NodeID: PerBlock_SDXL_MidLevelNode, - PerBlock_SDXL_LowLevelNode.NodeID: PerBlock_SDXL_LowLevelNode, - PerBlock_SDXL_FromFloatsNode.NodeID: PerBlock_SDXL_FromFloatsNode, - # Extras Nodes - "ADE_AnimateDiffUnload": AnimateDiffUnload, - "ADE_EmptyLatentImageLarge": EmptyLatentImageLarge, - "CheckpointLoaderSimpleWithNoiseSelect": CheckpointLoaderSimpleWithNoiseSelect, - "ADE_PerturbedAttentionGuidanceMultival": PerturbedAttentionGuidanceMultival, - "ADE_RescaleCFGMultival": RescaleCFGMultival, - # Gen1 Nodes - "ADE_AnimateDiffLoaderGen1": AnimateDiffLoaderGen1, - # Gen2 Nodes - "ADE_UseEvolvedSampling": UseEvolvedSamplingNode, - "ADE_ApplyAnimateDiffModelSimple": ApplyAnimateDiffModelBasicNode, - "ADE_ApplyAnimateDiffModel": ApplyAnimateDiffModelNode, - "ADE_LoadAnimateDiffModel": LoadAnimateDiffModelNode, - # AnimateLCM-I2V Nodes - "ADE_ApplyAnimateLCMI2VModel": ApplyAnimateLCMI2VModel, - "ADE_LoadAnimateLCMI2VModel": LoadAnimateLCMI2VModelNode, - "ADE_UpscaleAndVAEEncode": UpscaleAndVaeEncode, - "ADE_InjectI2VIntoAnimateDiffModel": LoadAnimateDiffAndInjectI2VNode, - # MotionCtrl Nodes - #LoadMotionCtrlCMCM.NodeID: LoadMotionCtrlCMCM, - #LoadMotionCtrlOMCM.NodeID: LoadMotionCtrlOMCM, - #ApplyAnimateDiffMotionCtrlModel.NodeID: ApplyAnimateDiffMotionCtrlModel, - #LoadMotionCtrlCameraPosesFromFile.NodeID: LoadMotionCtrlCameraPosesFromFile, - # CameraCtrl Nodes - "ADE_ApplyAnimateDiffModelWithCameraCtrl": ApplyAnimateDiffWithCameraCtrl, - "ADE_LoadAnimateDiffModelWithCameraCtrl": LoadAnimateDiffModelWithCameraCtrl, - "ADE_CameraCtrlAnimateDiffKeyframe": CameraCtrlADKeyframeNode, - "ADE_LoadCameraPoses": LoadCameraPosesFromFile, - "ADE_LoadCameraPosesFromPath": LoadCameraPosesFromPath, - "ADE_CameraPoseBasic": CameraCtrlPoseBasic, - "ADE_CameraPoseCombo": CameraCtrlPoseCombo, - "ADE_CameraPoseAdvanced": CameraCtrlPoseAdvanced, - "ADE_CameraManualPoseAppend": CameraCtrlManualAppendPose, - "ADE_ReplaceCameraParameters": CameraCtrlReplaceCameraParameters, - "ADE_ReplaceOriginalPoseAspectRatio": CameraCtrlSetOriginalAspectRatio, - # PIA Nodes - "ADE_ApplyAnimateDiffModelWithPIA": ApplyAnimateDiffPIAModel, - "ADE_InputPIA_Multival": InputPIA_MultivalNode, - "ADE_InputPIA_PaperPresets": InputPIA_PaperPresetsNode, - "ADE_PIA_AnimateDiffKeyframe": PIA_ADKeyframeNode, - "ADE_InjectPIAIntoAnimateDiffModel": LoadAnimateDiffAndInjectPIANode, - # FancyVideo - #ApplyAnimateDiffFancyVideo.NodeID: ApplyAnimateDiffFancyVideo, - # HelloMeme - #TestHMRefNetInjection.NodeID: TestHMRefNetInjection, - # Deprecated Nodes - "ADE_AnimateDiffLoaderWithContext": LegacyAnimateDiffLoaderWithContextDEPR, - "AnimateDiffLoaderV1": AnimateDiffLoaderDEPR, - "ADE_AnimateDiffLoaderV1Advanced": AnimateDiffLoaderAdvancedDEPR, - "ADE_AnimateDiffCombine": AnimateDiffCombineDEPR, - "ADE_AnimateDiffModelSettings_Release": AnimateDiffModelSettingsDEPR, - "ADE_AnimateDiffModelSettingsSimple": AnimateDiffModelSettingsSimpleDEPR, - "ADE_AnimateDiffModelSettings": AnimateDiffModelSettingsAdvancedDEPR, - "ADE_AnimateDiffModelSettingsAdvancedAttnStrengths": AnimateDiffModelSettingsAdvancedAttnStrengthsDEPR, -} -NODE_DISPLAY_NAME_MAPPINGS = { - # Unencapsulated - "ADE_AnimateDiffLoRALoader": "Load AnimateDiff LoRA πŸŽ­πŸ…πŸ…“", - "ADE_AnimateDiffSamplingSettings": "Sample Settings πŸŽ­πŸ…πŸ…“", - "ADE_AnimateDiffKeyframe": "AnimateDiff Keyframe πŸŽ­πŸ…πŸ…“", - # Multival Nodes - "ADE_MultivalDynamic": "Multival πŸŽ­πŸ…πŸ…“", - "ADE_MultivalDynamicFloatInput": "Multival [Float List] πŸŽ­πŸ…πŸ…“", - "ADE_MultivalDynamicFloats": "Multival [Floats] πŸŽ­πŸ…πŸ…“", - "ADE_MultivalScaledMask": "Multival Scaled Mask πŸŽ­πŸ…πŸ…“", - "ADE_MultivalConvertToMask": "Multival to Mask πŸŽ­πŸ…πŸ…“", - ############################################################################### - #------------------------------------------------------------------------------ - # Context Opts - "ADE_StandardStaticContextOptions": "Context Optionsβ—†Standard Static πŸŽ­πŸ…πŸ…“", - "ADE_StandardUniformContextOptions": "Context Optionsβ—†Standard Uniform πŸŽ­πŸ…πŸ…“", - "ADE_LoopedUniformContextOptions": "Context Optionsβ—†Looped Uniform πŸŽ­πŸ…πŸ…“", - "ADE_ViewsOnlyContextOptions": "Context Optionsβ—†Views Only [VRAMβ‡ˆ] πŸŽ­πŸ…πŸ…“", - "ADE_BatchedContextOptions": "Context Optionsβ—†Batched [Non-AD] πŸŽ­πŸ…πŸ…“", - "ADE_AnimateDiffUniformContextOptions": "Context Optionsβ—†Looped Uniform πŸŽ­πŸ…πŸ…“", # Legacy/Deprecated - "ADE_VisualizeContextOptionsK": "Visualize Context Options (K.) πŸŽ­πŸ…πŸ…“", - "ADE_VisualizeContextOptionsKAdv": "Visualize Context Options (K.Adv.) πŸŽ­πŸ…πŸ…“", - "ADE_VisualizeContextOptionsSCustom": "Visualize Context Options (S.Cus.) πŸŽ­πŸ…πŸ…“", - # View Opts - "ADE_StandardStaticViewOptions": "View Optionsβ—†Standard Static πŸŽ­πŸ…πŸ…“", - "ADE_StandardUniformViewOptions": "View Optionsβ—†Standard Uniform πŸŽ­πŸ…πŸ…“", - "ADE_LoopedUniformViewOptions": "View Optionsβ—†Looped Uniform πŸŽ­πŸ…πŸ…“", - # Context Extras - "ADE_ContextExtras_Set": "Set Context Extras πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef": "Context Extrasβ—†ContextRef πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef_ModeFirst": "ContextRef Modeβ—†First πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef_ModeSliding": "ContextRef Modeβ—†Sliding πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef_ModeIndexes": "ContextRef Modeβ—†Indexes πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef_TuneAttn": "ContextRef Tuneβ—†Attn πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef_TuneAttnAdain": "ContextRef Tuneβ—†Attn+Adain πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef_Keyframe": "ContextRef Keyframe πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef_KeyframeInterpolation": "ContextRef Keyframes Interp. πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_ContextRef_KeyframeFromList": "ContextRef Keyframes From List πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_NaiveReuse": "Context Extrasβ—†NaiveReuse πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_NaiveReuse_Keyframe": "NaiveReuse Keyframe πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_NaiveReuse_KeyframeInterpolation": "NaiveReuse Keyframes Interp. πŸŽ­πŸ…πŸ…“", - "ADE_ContextExtras_NaiveReuse_KeyframeFromList": "NaiveReuse Keyframes From List πŸŽ­πŸ…πŸ…“", - #------------------------------------------------------------------------------ - ############################################################################### - # Iteration Opts - "ADE_IterationOptsDefault": "Default Iteration Options πŸŽ­πŸ…πŸ…“", - "ADE_IterationOptsFreeInit": "FreeInit Iteration Options πŸŽ­πŸ…πŸ…“", - # Conditioning - # Conditioning (DEPRECATED) - "ADE_RegisterLoraHook": "Register LoRA Hook πŸŽ­πŸ…πŸ…“", - "ADE_RegisterLoraHookModelOnly": "Register LoRA Hook (Model Only) πŸŽ­πŸ…πŸ…“", - "ADE_RegisterModelAsLoraHook": "Register Model as LoRA Hook πŸŽ­πŸ…πŸ…“", - "ADE_RegisterModelAsLoraHookModelOnly": "Register Model as LoRA Hook (MO) πŸŽ­πŸ…πŸ…“", - "ADE_CombineLoraHooks": "Combine LoRA Hooks [2] πŸŽ­πŸ…πŸ…“", - "ADE_CombineLoraHooksFour": "Combine LoRA Hooks [4] πŸŽ­πŸ…πŸ…“", - "ADE_CombineLoraHooksEight": "Combine LoRA Hooks [8] πŸŽ­πŸ…πŸ…“", - "ADE_SetLoraHookKeyframe": "Set LoRA Hook Keyframes πŸŽ­πŸ…πŸ…“", - "ADE_AttachLoraHookToCLIP": "Set CLIP LoRA Hook πŸŽ­πŸ…πŸ…“", - "ADE_LoraHookKeyframe": "LoRA Hook Keyframe πŸŽ­πŸ…πŸ…“", - "ADE_LoraHookKeyframeInterpolation": "LoRA Hook Keyframes Interp. πŸŽ­πŸ…πŸ…“", - "ADE_LoraHookKeyframeFromStrengthList": "LoRA Hook Keyframes From List πŸŽ­πŸ…πŸ…“", - "ADE_AttachLoraHookToConditioning": "Set Model LoRA Hook πŸŽ­πŸ…πŸ…“", - "ADE_PairedConditioningSetMask": "Set Props on Conds πŸŽ­πŸ…πŸ…“", - "ADE_ConditioningSetMask": "Set Props on Cond πŸŽ­πŸ…πŸ…“", - "ADE_PairedConditioningSetMaskAndCombine": "Set Props and Combine Conds πŸŽ­πŸ…πŸ…“", - "ADE_ConditioningSetMaskAndCombine": "Set Props and Combine Cond πŸŽ­πŸ…πŸ…“", - "ADE_PairedConditioningSetUnmaskedAndCombine": "Set Unmasked Conds πŸŽ­πŸ…πŸ…“", - "ADE_ConditioningSetUnmaskedAndCombine": "Set Unmasked Cond πŸŽ­πŸ…πŸ…“", - "ADE_PairedConditioningCombine": "Manual Combine Conds πŸŽ­πŸ…πŸ…“", - "ADE_ConditioningCombine": "Manual Combine Cond πŸŽ­πŸ…πŸ…“", - "ADE_TimestepsConditioning": "Timesteps Conditioning πŸŽ­πŸ…πŸ…“", - # Noise Layer Nodes - "ADE_NoiseLayerAdd": "Noise Layer [Add] πŸŽ­πŸ…πŸ…“", - "ADE_NoiseLayerAddWeighted": "Noise Layer [Add Weighted] πŸŽ­πŸ…πŸ…“", - "ADE_NoiseLayerNormalizedSum": "Noise Layer [Normalized Sum] πŸŽ­πŸ…πŸ…“", - "ADE_NoiseLayerReplace": "Noise Layer [Replace] πŸŽ­πŸ…πŸ…“", - # AnimateDiff Settings - "ADE_AnimateDiffSettings": "AnimateDiff Settings πŸŽ­πŸ…πŸ…“", - "ADE_AdjustPESweetspotStretch": "Adjust PE [Sweetspot] πŸŽ­πŸ…πŸ…“", - "ADE_AdjustPEFullStretch": "Adjust PE [Full Stretch] πŸŽ­πŸ…πŸ…“", - "ADE_AdjustPEManual": "Adjust PE [Manual] πŸŽ­πŸ…πŸ…“", - "ADE_AdjustWeightAllAdd": "Adjust Weight [Allβ—†Add] πŸŽ­πŸ…πŸ…“", - "ADE_AdjustWeightAllMult": "Adjust Weight [Allβ—†Mult] πŸŽ­πŸ…πŸ…“", - "ADE_AdjustWeightIndivAdd": "Adjust Weight [Indivβ—†Add] πŸŽ­πŸ…πŸ…“", - "ADE_AdjustWeightIndivMult": "Adjust Weight [Indivβ—†Mult] πŸŽ­πŸ…πŸ…“", - "ADE_AdjustWeightIndivAttnAdd": "Adjust Weight [Indiv-Attnβ—†Add] πŸŽ­πŸ…πŸ…“", - "ADE_AdjustWeightIndivAttnMult": "Adjust Weight [Indiv-Attnβ—†Mult] πŸŽ­πŸ…πŸ…“", - # Sample Settings - "ADE_CustomCFGSimple": "Custom CFG πŸŽ­πŸ…πŸ…“", - "ADE_CustomCFG": "Custom CFG [Multival] πŸŽ­πŸ…πŸ…“", - "ADE_CustomCFGKeyframeSimple": "Custom CFG Keyframe πŸŽ­πŸ…πŸ…“", - "ADE_CustomCFGKeyframe": "Custom CFG Keyframe [Multival] πŸŽ­πŸ…πŸ…“", - "ADE_CustomCFGKeyframeInterpolation": "Custom CFG Keyframes Interp. πŸŽ­πŸ…πŸ…“", - "ADE_CustomCFGKeyframeFromList": "Custom CFG Keyframes From List πŸŽ­πŸ…πŸ…“", - "ADE_CFGExtrasPAGSimple": "CFG Extrasβ—†PAG πŸŽ­πŸ…πŸ…“", - "ADE_CFGExtrasPAG": "CFG Extrasβ—†PAG [Multival] πŸŽ­πŸ…πŸ…“", - "ADE_CFGExtrasRescaleCFGSimple": "CFG Extrasβ—†RescaleCFG πŸŽ­πŸ…πŸ…“", - "ADE_CFGExtrasRescaleCFG": "CFG Extrasβ—†RescaleCFG [Multival] πŸŽ­πŸ…πŸ…“", - "ADE_SigmaSchedule": "Create Sigma Schedule πŸŽ­πŸ…πŸ…“", - "ADE_RawSigmaSchedule": "Create Raw Sigma Schedule πŸŽ­πŸ…πŸ…“", - "ADE_SigmaScheduleWeightedAverage": "Sigma Schedule Weighted Mean πŸŽ­πŸ…πŸ…“", - "ADE_SigmaScheduleWeightedAverageInterp": "Sigma Schedule Interp. Mean πŸŽ­πŸ…πŸ…“", - "ADE_SigmaScheduleSplitAndCombine": "Sigma Schedule Split Combine πŸŽ­πŸ…πŸ…“", - "ADE_SigmaScheduleToSigmas": "Sigma Schedule To Sigmas πŸŽ­πŸ…πŸ…“", - "ADE_NoisedImageInjection": "Image Injection πŸŽ­πŸ…πŸ…“", - "ADE_NoisedImageInjectOptions": "Image Injection Options πŸŽ­πŸ…πŸ…“", - "ADE_NoiseCalibration": "Noise Calibration πŸŽ­πŸ…πŸ…“", - "ADE_AncestralOptions": "Ancestral Options πŸŽ­πŸ…πŸ…“", - # Scheduling - PromptSchedulingNode.NodeID: PromptSchedulingNode.NodeName, - PromptSchedulingLatentsNode.NodeID: PromptSchedulingLatentsNode.NodeName, - ValueSchedulingNode.NodeID: ValueSchedulingNode.NodeName, - ValueSchedulingLatentsNode.NodeID: ValueSchedulingLatentsNode.NodeName, - ConditionExtractionNode.NodeID: ConditionExtractionNode.NodeName, - AddValuesReplaceNode.NodeID: AddValuesReplaceNode.NodeName, - FloatToFloatsNode.NodeID:FloatToFloatsNode.NodeName, - # Per-Block - ADBlockComboNode.NodeID: ADBlockComboNode.NodeName, - ADBlockIndivNode.NodeID: ADBlockIndivNode.NodeName, - PerBlockHighLevelNode.NodeID: PerBlockHighLevelNode.NodeName, - PerBlock_SD15_MidLevelNode.NodeID: PerBlock_SD15_MidLevelNode.NodeName, - PerBlock_SD15_LowLevelNode.NodeID: PerBlock_SD15_LowLevelNode.NodeName, - PerBlock_SD15_FromFloatsNode.NodeID: PerBlock_SD15_FromFloatsNode.NodeName, - PerBlock_SDXL_MidLevelNode.NodeID: PerBlock_SDXL_MidLevelNode.NodeName, - PerBlock_SDXL_LowLevelNode.NodeID: PerBlock_SDXL_LowLevelNode.NodeName, - PerBlock_SDXL_FromFloatsNode.NodeID: PerBlock_SDXL_FromFloatsNode.NodeName, - # Extras Nodes - "ADE_AnimateDiffUnload": "AnimateDiff Unload πŸŽ­πŸ…πŸ…“", - "ADE_EmptyLatentImageLarge": "Empty Latent Image (Big Batch) πŸŽ­πŸ…πŸ…“", - "CheckpointLoaderSimpleWithNoiseSelect": "Load Checkpoint w/ Noise Select πŸŽ­πŸ…πŸ…“", - "ADE_PerturbedAttentionGuidanceMultival": "PerturbedAttnGuide [Multival] πŸŽ­πŸ…πŸ…“", - "ADE_RescaleCFGMultival": "RescaleCFG [Multival] πŸŽ­πŸ…πŸ…“", - # Gen1 Nodes - "ADE_AnimateDiffLoaderGen1": "AnimateDiff Loader πŸŽ­πŸ…πŸ…“β‘ ", - # Gen2 Nodes - "ADE_UseEvolvedSampling": "Use Evolved Sampling πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_ApplyAnimateDiffModelSimple": "Apply AnimateDiff Model πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_ApplyAnimateDiffModel": "Apply AnimateDiff Model (Adv.) πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_LoadAnimateDiffModel": "Load AnimateDiff Model πŸŽ­πŸ…πŸ…“β‘‘", - # AnimateLCM-I2V Nodes - "ADE_ApplyAnimateLCMI2VModel": "Apply AnimateLCM-I2V Model πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_LoadAnimateLCMI2VModel": "Load AnimateLCM-I2V Model πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_UpscaleAndVAEEncode": "Scale Ref Image and VAE Encode πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_InjectI2VIntoAnimateDiffModel": "πŸ§ͺInject I2V into AnimateDiff Model πŸŽ­πŸ…πŸ…“β‘‘", - # MotionCtrl Nodes - LoadMotionCtrlCMCM.NodeID: LoadMotionCtrlCMCM.NodeName, - LoadMotionCtrlOMCM.NodeID: LoadMotionCtrlOMCM.NodeName, - ApplyAnimateDiffMotionCtrlModel.NodeID: ApplyAnimateDiffMotionCtrlModel.NodeName, - # CameraCtrl Nodes - "ADE_ApplyAnimateDiffModelWithCameraCtrl": "Apply AnimateDiff+CameraCtrl Model πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_LoadAnimateDiffModelWithCameraCtrl": "Load AnimateDiff+CameraCtrl Model πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_CameraCtrlAnimateDiffKeyframe": "AnimateDiff+CameraCtrl Keyframe πŸŽ­πŸ…πŸ…“", - "ADE_LoadCameraPoses": "Load CameraCtrl Poses (File) πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_LoadCameraPosesFromPath": "Load CameraCtrl Poses (Path) πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_CameraPoseBasic": "Create CameraCtrl Poses πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_CameraPoseCombo": "Create CameraCtrl Poses (Combo) πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_CameraPoseAdvanced": "Create CameraCtrl Poses (Adv.) πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_CameraManualPoseAppend": "Manual Append CameraCtrl Poses πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_ReplaceCameraParameters": "Replace Camera Parameters πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_ReplaceOriginalPoseAspectRatio": "Replace Orig. Pose Aspect Ratio πŸŽ­πŸ…πŸ…“β‘‘", - # PIA Nodes - "ADE_ApplyAnimateDiffModelWithPIA": "Apply AnimateDiff-PIA Model πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_InputPIA_Multival": "PIA Input [Multival] πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_InputPIA_PaperPresets": "PIA Input [Paper Presets] πŸŽ­πŸ…πŸ…“β‘‘", - "ADE_PIA_AnimateDiffKeyframe": "AnimateDiff-PIA Keyframe πŸŽ­πŸ…πŸ…“", - "ADE_InjectPIAIntoAnimateDiffModel": "πŸ§ͺInject PIA into AnimateDiff Model πŸŽ­πŸ…πŸ…“β‘‘", - # FancyVideo - ApplyAnimateDiffFancyVideo.NodeID: ApplyAnimateDiffFancyVideo.NodeName, - # HelloMeme - TestHMRefNetInjection.NodeID: TestHMRefNetInjection.NodeName, - # Deprecated Nodes - "ADE_AnimateDiffLoaderWithContext": "AnimateDiff Loader [Legacy] πŸŽ­πŸ…πŸ…“β‘ ", - "AnimateDiffLoaderV1": "🚫AnimateDiff Loader [DEPRECATED] πŸŽ­πŸ…πŸ…“", - "ADE_AnimateDiffLoaderV1Advanced": "🚫AnimateDiff Loader (Advanced) [DEPRECATED] πŸŽ­πŸ…πŸ…“", - "ADE_AnimateDiffCombine": "🚫AnimateDiff Combine [DEPRECATED, Use Video Combine (VHS) Instead!] πŸŽ­πŸ…πŸ…“", - "ADE_AnimateDiffModelSettings_Release": "🚫[DEPR] Motion Model Settings πŸŽ­πŸ…πŸ…“β‘ ", - "ADE_AnimateDiffModelSettingsSimple": "🚫[DEPR] Motion Model Settings (Simple) πŸŽ­πŸ…πŸ…“β‘ ", - "ADE_AnimateDiffModelSettings": "🚫[DEPR] Motion Model Settings (Advanced) πŸŽ­πŸ…πŸ…“β‘ ", - "ADE_AnimateDiffModelSettingsAdvancedAttnStrengths": "🚫[DEPR] Motion Model Settings (Adv. Attn) πŸŽ­πŸ…πŸ…“β‘ ", -} +class AnimateDiffExtension(ComfyExtension): + async def get_node_list(self) -> list[type[io.ComfyNode]]: + return [ + AnimateDiffLoraLoader, + SampleSettingsNode, + ADKeyframeNode, + MultivalDynamicNode, + MultivalDynamicFloatInputNode, + MultivalDynamicFloatsNode, + MultivalScaledMaskNode, + MultivalConvertToMaskNode, + StandardStaticContextOptionsNode, + StandardUniformContextOptionsNode, + LoopedUniformContextOptionsNode, + ViewAsContextOptionsNode, + BatchedContextOptionsNode, + LegacyLoopedUniformContextOptionsNode, + VisualizeContextOptionsK, + VisualizeContextOptionsKAdv, + VisualizeContextOptionsSCustom, + StandardStaticViewOptionsNode, + StandardUniformViewOptionsNode, + LoopedUniformViewOptionsNode, + SetContextExtrasOnContextOptions, + ContextExtras_ContextRef, + ContextRef_ModeFirst, + ContextRef_ModeSliding, + ContextRef_ModeIndexes, + ContextRef_TuneAttn, + ContextRef_TuneAttnAdain, + ContextRef_KeyframeMultivalNode, + ContextRef_KeyframeInterpolationNode, + ContextRef_KeyframeFromListNode, + ContextExtras_NaiveReuse, + NaiveReuse_KeyframeMultivalNode, + NaiveReuse_KeyframeInterpolationNode, + NaiveReuse_KeyframeFromListNode, + IterationOptionsNode, + FreeInitOptionsNode, + MaskableLoraLoaderDEPR, + MaskableLoraLoaderModelOnlyDEPR, + MaskableSDModelLoaderDEPR, + MaskableSDModelLoaderModelOnlyDEPR, + CombineLoraHooksDEPR, + CombineLoraHookFourOptionalDEPR, + CombineLoraHookEightOptionalDEPR, + SetLoraHookKeyframesDEPR, + SetClipLoraHookDEPR, + CreateLoraHookKeyframeDEPR, + CreateLoraHookKeyframeInterpolationDEPR, + CreateLoraHookKeyframeFromStrengthListDEPR, + SetModelLoraHookDEPR, + PairedConditioningSetMaskHookedDEPR, + ConditioningSetMaskHookedDEPR, + PairedConditioningSetMaskAndCombineHookedDEPR, + ConditioningSetMaskAndCombineHookedDEPR, + PairedConditioningSetUnmaskedAndCombineHookedDEPR, + ConditioningSetUnmaskedAndCombineHookedDEPR, + PairedConditioningCombineDEPR, + ConditioningCombineDEPR, + ConditioningTimestepsNodeDEPR, + NoiseLayerAddNode, + NoiseLayerAddWeightedNode, + NoiseLayerNormalizedSumNode, + NoiseLayerReplaceNode, + AnimateDiffSettingsNode, + SweetspotStretchPENode, + FullStretchPENode, + ManualAdjustPENode, + WeightAdjustAllAddNode, + WeightAdjustAllMultNode, + WeightAdjustIndivAddNode, + WeightAdjustIndivMultNode, + WeightAdjustIndivAttnAddNode, + WeightAdjustIndivAttnMultNode, + CustomCFGSimpleNode, + CustomCFGNode, + CustomCFGKeyframeSimpleNode, + CustomCFGKeyframeNode, + CustomCFGKeyframeInterpolationNode, + CustomCFGKeyframeFromListNode, + CFGExtrasPAGSimpleNode, + CFGExtrasPAGNode, + CFGExtrasRescaleCFGSimpleNode, + CFGExtrasRescaleCFGNode, + SigmaScheduleNode, + RawSigmaScheduleNode, + WeightedAverageSigmaScheduleNode, + InterpolatedWeightedAverageSigmaScheduleNode, + SplitAndCombineSigmaScheduleNode, + SigmaScheduleToSigmasNode, + NoisedImageInjectionNode, + NoisedImageInjectOptionsNode, + AncestralOptionsNode, + PromptSchedulingNode, + PromptSchedulingLatentsNode, + ValueSchedulingNode, + ValueSchedulingLatentsNode, + ConditionExtractionNode, + AddValuesReplaceNode, + FloatToFloatsNode, + ADBlockComboNode, + ADBlockIndivNode, + PerBlockHighLevelNode, + PerBlock_SD15_MidLevelNode, + PerBlock_SD15_LowLevelNode, + PerBlock_SD15_FromFloatsNode, + PerBlock_SDXL_MidLevelNode, + PerBlock_SDXL_LowLevelNode, + PerBlock_SDXL_FromFloatsNode, + AnimateDiffUnload, + EmptyLatentImageLarge, + CheckpointLoaderSimpleWithNoiseSelect, + PerturbedAttentionGuidanceMultival, + RescaleCFGMultival, + AnimateDiffLoaderGen1, + UseEvolvedSamplingNode, + ApplyAnimateDiffModelBasicNode, + ApplyAnimateDiffModelNode, + LoadAnimateDiffModelNode, + ApplyAnimateLCMI2VModel, + LoadAnimateLCMI2VModelNode, + UpscaleAndVaeEncode, + LoadAnimateDiffAndInjectI2VNode, + ApplyAnimateDiffWithCameraCtrl, + LoadAnimateDiffModelWithCameraCtrl, + CameraCtrlADKeyframeNode, + LoadCameraPosesFromFile, + LoadCameraPosesFromPath, + CameraCtrlPoseBasic, + CameraCtrlPoseCombo, + CameraCtrlPoseAdvanced, + CameraCtrlManualAppendPose, + CameraCtrlReplaceCameraParameters, + CameraCtrlSetOriginalAspectRatio, + ApplyAnimateDiffPIAModel, + InputPIA_MultivalNode, + InputPIA_PaperPresetsNode, + PIA_ADKeyframeNode, + LoadAnimateDiffAndInjectPIANode, + LegacyAnimateDiffLoaderWithContextDEPR, + AnimateDiffLoaderDEPR, + AnimateDiffLoaderAdvancedDEPR, + AnimateDiffCombineDEPR, + AnimateDiffModelSettingsDEPR, + AnimateDiffModelSettingsSimpleDEPR, + AnimateDiffModelSettingsAdvancedDEPR, + AnimateDiffModelSettingsAdvancedAttnStrengthsDEPR, + ] diff --git a/animatediff/nodes_ad_settings.py b/animatediff/nodes_ad_settings.py index 42e6ef9..b30726d 100644 --- a/animatediff/nodes_ad_settings.py +++ b/animatediff/nodes_ad_settings.py @@ -1,49 +1,55 @@ +from comfy_api.latest import io from .ad_settings import AdjustPE, AdjustWeight, AdjustGroup, AnimateDiffSettings from .utils_model import BIGMAX -class AnimateDiffSettingsNode: +class AnimateDiffSettingsNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "optional": { - "pe_adjust": ("PE_ADJUST",), - "weight_adjust": ("WEIGHT_ADJUST",), - }, - } - - RETURN_TYPES = ("AD_SETTINGS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings" - FUNCTION = "get_ad_settings" - - def get_ad_settings(self, pe_adjust: AdjustGroup=None, weight_adjust: AdjustGroup=None): - return (AnimateDiffSettings(adjust_pe=pe_adjust, adjust_weight=weight_adjust),) + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AnimateDiffSettings', + display_name='AnimateDiff Settings πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings', + inputs=[ + io.Custom("PE_ADJUST").Input('pe_adjust', optional=True), + io.Custom("WEIGHT_ADJUST").Input('weight_adjust', optional=True), + ], + outputs=[ + io.Custom("AD_SETTINGS").Output('AD_SETTINGS'), + ], + ) -class ManualAdjustPENode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cap_initial_pe_length": ("INT", {"default": 0, "min": 0, "step": 1}), - "interpolate_pe_to_length": ("INT", {"default": 0, "min": 0, "step": 1}), - "initial_pe_idx_offset": ("INT", {"default": 0, "min": 0, "step": 1}), - "final_pe_idx_offset": ("INT", {"default": 0, "min": 0, "step": 1}), - "print_adjustment": ("BOOLEAN", {"default": False}), - - }, - "optional": { - "prev_pe_adjust": ("PE_ADJUST",), - }, - } - - RETURN_TYPES = ("PE_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/pe adjust" - FUNCTION = "get_pe_adjust" + def execute(cls, pe_adjust: AdjustGroup=None, weight_adjust: AdjustGroup=None) -> io.NodeOutput: + return io.NodeOutput(AnimateDiffSettings(adjust_pe=pe_adjust, adjust_weight=weight_adjust)) - def get_pe_adjust(self, cap_initial_pe_length: int, interpolate_pe_to_length: int, + +class ManualAdjustPENode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustPEManual', + display_name='Adjust PE [Manual] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/pe adjust', + inputs=[ + io.Int.Input('cap_initial_pe_length', default=0, min=0, step=1), + io.Int.Input('interpolate_pe_to_length', default=0, min=0, step=1), + io.Int.Input('initial_pe_idx_offset', default=0, min=0, step=1), + io.Int.Input('final_pe_idx_offset', default=0, min=0, step=1), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("PE_ADJUST").Input('prev_pe_adjust', optional=True), + ], + outputs=[ + io.Custom("PE_ADJUST").Output('PE_ADJUST'), + ], + ) + + + @classmethod + def execute(cls, cap_initial_pe_length: int, interpolate_pe_to_length: int, initial_pe_idx_offset: int, final_pe_idx_offset: int, print_adjustment: bool, - prev_pe_adjust: AdjustGroup=None): + prev_pe_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_pe_adjust is None: prev_pe_adjust = AdjustGroup() prev_pe_adjust = prev_pe_adjust.clone() @@ -51,82 +57,88 @@ class ManualAdjustPENode: initial_pe_idx_offset=initial_pe_idx_offset, final_pe_idx_offset=final_pe_idx_offset, print_adjustment=print_adjustment) prev_pe_adjust.add(adjust) - return (prev_pe_adjust,) + return io.NodeOutput(prev_pe_adjust) -class SweetspotStretchPENode: +class SweetspotStretchPENode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "sweetspot": ("INT", {"default": 16, "min": 0, "max": BIGMAX},), - "new_sweetspot": ("INT", {"default": 16, "min": 0, "max": BIGMAX},), - "print_adjustment": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_pe_adjust": ("PE_ADJUST",), - }, - } - - RETURN_TYPES = ("PE_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/pe adjust" - FUNCTION = "get_pe_adjust" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustPESweetspotStretch', + display_name='Adjust PE [Sweetspot] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/pe adjust', + inputs=[ + io.Int.Input('sweetspot', default=16, max=9007199254740991, min=0), + io.Int.Input('new_sweetspot', default=16, max=9007199254740991, min=0), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("PE_ADJUST").Input('prev_pe_adjust', optional=True), + ], + outputs=[ + io.Custom("PE_ADJUST").Output('PE_ADJUST'), + ], + ) - def get_pe_adjust(self, sweetspot: int, new_sweetspot: int, print_adjustment: bool, prev_pe_adjust: AdjustGroup=None): + + @classmethod + def execute(cls, sweetspot: int, new_sweetspot: int, print_adjustment: bool, prev_pe_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_pe_adjust is None: prev_pe_adjust = AdjustGroup() prev_pe_adjust = prev_pe_adjust.clone() adjust = AdjustPE(cap_initial_pe_length=sweetspot, interpolate_pe_to_length=new_sweetspot, print_adjustment=print_adjustment) prev_pe_adjust.add(adjust) - return (prev_pe_adjust,) + return io.NodeOutput(prev_pe_adjust) -class FullStretchPENode: +class FullStretchPENode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "pe_stretch": ("INT", {"default": 0, "min": 0, "max": BIGMAX},), - "print_adjustment": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_pe_adjust": ("PE_ADJUST",), - }, - } - - RETURN_TYPES = ("PE_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/pe adjust" - FUNCTION = "get_pe_adjust" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustPEFullStretch', + display_name='Adjust PE [Full Stretch] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/pe adjust', + inputs=[ + io.Int.Input('pe_stretch', default=0, max=9007199254740991, min=0), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("PE_ADJUST").Input('prev_pe_adjust', optional=True), + ], + outputs=[ + io.Custom("PE_ADJUST").Output('PE_ADJUST'), + ], + ) - def get_pe_adjust(self, pe_stretch: int, print_adjustment: bool, prev_pe_adjust: AdjustGroup=None): + + @classmethod + def execute(cls, pe_stretch: int, print_adjustment: bool, prev_pe_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_pe_adjust is None: prev_pe_adjust = AdjustGroup() prev_pe_adjust = prev_pe_adjust.clone() adjust = AdjustPE(motion_pe_stretch=pe_stretch, print_adjustment=print_adjustment) prev_pe_adjust.add(adjust) - return (prev_pe_adjust,) + return io.NodeOutput(prev_pe_adjust) -class WeightAdjustAllAddNode: +class WeightAdjustAllAddNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "all_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "print_adjustment": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_weight_adjust": ("WEIGHT_ADJUST",), - }, - } - - RETURN_TYPES = ("WEIGHT_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust" - FUNCTION = "get_weight_adjust" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustWeightAllAdd', + display_name='Adjust Weight [Allβ—†Add] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust', + inputs=[ + io.Float.Input('all_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("WEIGHT_ADJUST").Input('prev_weight_adjust', optional=True), + ], + outputs=[ + io.Custom("WEIGHT_ADJUST").Output('WEIGHT_ADJUST'), + ], + ) - def get_weight_adjust(self, all_ADD: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None): + + @classmethod + def execute(cls, all_ADD: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_weight_adjust is None: prev_weight_adjust = AdjustGroup() prev_weight_adjust = prev_weight_adjust.clone() @@ -135,27 +147,29 @@ class WeightAdjustAllAddNode: print_adjustment=print_adjustment ) prev_weight_adjust.add(adjust) - return (prev_weight_adjust,) + return io.NodeOutput(prev_weight_adjust) -class WeightAdjustAllMultNode: +class WeightAdjustAllMultNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "all_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "print_adjustment": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_weight_adjust": ("WEIGHT_ADJUST",), - }, - } - - RETURN_TYPES = ("WEIGHT_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust" - FUNCTION = "get_weight_adjust" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustWeightAllMult', + display_name='Adjust Weight [Allβ—†Mult] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust', + inputs=[ + io.Float.Input('all_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("WEIGHT_ADJUST").Input('prev_weight_adjust', optional=True), + ], + outputs=[ + io.Custom("WEIGHT_ADJUST").Output('WEIGHT_ADJUST'), + ], + ) - def get_weight_adjust(self, all_MULT: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None): + + @classmethod + def execute(cls, all_MULT: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_weight_adjust is None: prev_weight_adjust = AdjustGroup() prev_weight_adjust = prev_weight_adjust.clone() @@ -164,29 +178,31 @@ class WeightAdjustAllMultNode: print_adjustment=print_adjustment ) prev_weight_adjust.add(adjust) - return (prev_weight_adjust,) + return io.NodeOutput(prev_weight_adjust) -class WeightAdjustIndivAddNode: +class WeightAdjustIndivAddNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "pe_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "attn_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "other_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "print_adjustment": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_weight_adjust": ("WEIGHT_ADJUST",), - }, - } - - RETURN_TYPES = ("WEIGHT_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust" - FUNCTION = "get_weight_adjust" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustWeightIndivAdd', + display_name='Adjust Weight [Indivβ—†Add] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust', + inputs=[ + io.Float.Input('pe_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('attn_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('other_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("WEIGHT_ADJUST").Input('prev_weight_adjust', optional=True), + ], + outputs=[ + io.Custom("WEIGHT_ADJUST").Output('WEIGHT_ADJUST'), + ], + ) - def get_weight_adjust(self, pe_ADD: float, attn_ADD: float, other_ADD: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None): + + @classmethod + def execute(cls, pe_ADD: float, attn_ADD: float, other_ADD: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_weight_adjust is None: prev_weight_adjust = AdjustGroup() prev_weight_adjust = prev_weight_adjust.clone() @@ -197,29 +213,31 @@ class WeightAdjustIndivAddNode: print_adjustment=print_adjustment ) prev_weight_adjust.add(adjust) - return (prev_weight_adjust,) + return io.NodeOutput(prev_weight_adjust) -class WeightAdjustIndivMultNode: +class WeightAdjustIndivMultNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "pe_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "attn_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "other_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "print_adjustment": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_weight_adjust": ("WEIGHT_ADJUST",), - }, - } - - RETURN_TYPES = ("WEIGHT_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust" - FUNCTION = "get_weight_adjust" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustWeightIndivMult', + display_name='Adjust Weight [Indivβ—†Mult] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust', + inputs=[ + io.Float.Input('pe_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('attn_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('other_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("WEIGHT_ADJUST").Input('prev_weight_adjust', optional=True), + ], + outputs=[ + io.Custom("WEIGHT_ADJUST").Output('WEIGHT_ADJUST'), + ], + ) - def get_weight_adjust(self, pe_MULT: float, attn_MULT: float, other_MULT: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None): + + @classmethod + def execute(cls, pe_MULT: float, attn_MULT: float, other_MULT: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_weight_adjust is None: prev_weight_adjust = AdjustGroup() prev_weight_adjust = prev_weight_adjust.clone() @@ -230,37 +248,39 @@ class WeightAdjustIndivMultNode: print_adjustment=print_adjustment ) prev_weight_adjust.add(adjust) - return (prev_weight_adjust,) + return io.NodeOutput(prev_weight_adjust) -class WeightAdjustIndivAttnAddNode: +class WeightAdjustIndivAttnAddNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "pe_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "attn_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "attn_q_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "attn_k_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "attn_v_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "attn_out_weight_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "attn_out_bias_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "other_ADD": ("FLOAT", {"default": 0.0, "min": -2.0, "max": 2.0, "step": 0.000001}), - "print_adjustment": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_weight_adjust": ("WEIGHT_ADJUST",), - }, - } - - RETURN_TYPES = ("WEIGHT_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust" - FUNCTION = "get_weight_adjust" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustWeightIndivAttnAdd', + display_name='Adjust Weight [Indiv-Attnβ—†Add] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust', + inputs=[ + io.Float.Input('pe_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('attn_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('attn_q_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('attn_k_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('attn_v_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('attn_out_weight_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('attn_out_bias_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Float.Input('other_ADD', default=0.0, max=2.0, min=-2.0, step=1e-06), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("WEIGHT_ADJUST").Input('prev_weight_adjust', optional=True), + ], + outputs=[ + io.Custom("WEIGHT_ADJUST").Output('WEIGHT_ADJUST'), + ], + ) - def get_weight_adjust(self, pe_ADD: float, attn_ADD: float, + + @classmethod + def execute(cls, pe_ADD: float, attn_ADD: float, attn_q_ADD: float, attn_k_ADD: float, attn_v_ADD: float, attn_out_weight_ADD: float, attn_out_bias_ADD: float, - other_ADD: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None): + other_ADD: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_weight_adjust is None: prev_weight_adjust = AdjustGroup() prev_weight_adjust = prev_weight_adjust.clone() @@ -276,37 +296,39 @@ class WeightAdjustIndivAttnAddNode: print_adjustment=print_adjustment ) prev_weight_adjust.add(adjust) - return (prev_weight_adjust,) + return io.NodeOutput(prev_weight_adjust) -class WeightAdjustIndivAttnMultNode: +class WeightAdjustIndivAttnMultNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "pe_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "attn_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "attn_q_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "attn_k_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "attn_v_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "attn_out_weight_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "attn_out_bias_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "other_MULT": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 2.0, "step": 0.000001}), - "print_adjustment": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_weight_adjust": ("WEIGHT_ADJUST",), - }, - } - - RETURN_TYPES = ("WEIGHT_ADJUST",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust" - FUNCTION = "get_weight_adjust" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AdjustWeightIndivAttnMult', + display_name='Adjust Weight [Indiv-Attnβ—†Mult] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/ad settings/weight adjust', + inputs=[ + io.Float.Input('pe_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('attn_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('attn_q_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('attn_k_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('attn_v_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('attn_out_weight_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('attn_out_bias_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Float.Input('other_MULT', default=1.0, max=2.0, min=0.0, step=1e-06), + io.Boolean.Input('print_adjustment', default=False), + io.Custom("WEIGHT_ADJUST").Input('prev_weight_adjust', optional=True), + ], + outputs=[ + io.Custom("WEIGHT_ADJUST").Output('WEIGHT_ADJUST'), + ], + ) - def get_weight_adjust(self, pe_MULT: float, attn_MULT: float, + + @classmethod + def execute(cls, pe_MULT: float, attn_MULT: float, attn_q_MULT: float, attn_k_MULT: float, attn_v_MULT: float, attn_out_weight_MULT: float, attn_out_bias_MULT: float, - other_MULT: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None): + other_MULT: float, print_adjustment: bool, prev_weight_adjust: AdjustGroup=None) -> io.NodeOutput: if prev_weight_adjust is None: prev_weight_adjust = AdjustGroup() prev_weight_adjust = prev_weight_adjust.clone() @@ -322,4 +344,4 @@ class WeightAdjustIndivAttnMultNode: print_adjustment=print_adjustment ) prev_weight_adjust.add(adjust) - return (prev_weight_adjust,) + return io.NodeOutput(prev_weight_adjust) diff --git a/animatediff/nodes_animatelcmi2v.py b/animatediff/nodes_animatelcmi2v.py index 92cea7d..4e9db53 100644 --- a/animatediff/nodes_animatelcmi2v.py +++ b/animatediff/nodes_animatelcmi2v.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from typing import Union import torch @@ -16,40 +17,42 @@ from .motion_module_ad import AnimateDiffFormat from .nodes_gen2 import ApplyAnimateDiffModelNode -class ApplyAnimateLCMI2VModel: +class ApplyAnimateLCMI2VModel(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "motion_model": ("MOTION_MODEL_ADE",), - "ref_latent": ("LATENT",), - "ref_drift": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 10.0, "step": 0.001}), - "apply_ref_when_disabled": ("BOOLEAN", {"default": False}), - "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": { - "motion_lora": ("MOTION_LORA",), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "ad_keyframes": ("AD_KEYFRAMES",), - "prev_m_models": ("M_MODELS",), - "per_block": ("PER_BLOCK",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ApplyAnimateLCMI2VModel', + display_name='Apply AnimateLCM-I2V Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/AnimateLCM-I2V', + inputs=[ + io.Custom("MOTION_MODEL_ADE").Input('motion_model'), + io.Latent.Input('ref_latent'), + io.Float.Input('ref_drift', default=0.0, max=10.0, min=0.0, step=0.001), + io.Boolean.Input('apply_ref_when_disabled', default=False), + 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("MOTION_LORA").Input('motion_lora', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("AD_KEYFRAMES").Input('ad_keyframes', optional=True), + io.Custom("M_MODELS").Input('prev_m_models', optional=True), + io.Custom("PER_BLOCK").Input('per_block', optional=True), + ], + outputs=[ + io.Custom("M_MODELS").Output('M_MODELS'), + ], + ) - RETURN_TYPES = ("M_MODELS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/AnimateLCM-I2V" - FUNCTION = "apply_motion_model" - def apply_motion_model(self, motion_model: MotionModelPatcher, ref_latent: dict, ref_drift: float=0.0, apply_ref_when_disabled=False, start_percent: float=0.0, end_percent: float=1.0, + @classmethod + def execute(cls, motion_model: MotionModelPatcher, ref_latent: dict, ref_drift: float=0.0, apply_ref_when_disabled=False, start_percent: float=0.0, end_percent: float=1.0, motion_lora: MotionLoraList=None, ad_keyframes: ADKeyframeGroup=None, scale_multival=None, effect_multival=None, per_block=None, prev_m_models: MotionModelGroup=None,): - new_m_models = ApplyAnimateDiffModelNode.apply_motion_model(self, motion_model, start_percent=start_percent, end_percent=end_percent, + new_m_models = ApplyAnimateDiffModelNode.execute( motion_model, start_percent=start_percent, end_percent=end_percent, motion_lora=motion_lora, ad_keyframes=ad_keyframes, scale_multival=scale_multival, effect_multival=effect_multival, per_block=per_block, - prev_m_models=prev_m_models) + prev_m_models=prev_m_models).args # most recent added model will always be first in list; curr_model = new_m_models[0].models[0] # confirm that model contains img_encoder @@ -59,27 +62,29 @@ class ApplyAnimateLCMI2VModel: attachment.orig_img_latents = ref_latent["samples"] attachment.orig_ref_drift = ref_drift attachment.orig_apply_ref_when_disabled = apply_ref_when_disabled - return new_m_models + return io.NodeOutput(*new_m_models) -class LoadAnimateLCMI2VModelNode: +class LoadAnimateLCMI2VModelNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model_name": (get_available_motion_models(),), - }, - "optional": { - "ad_settings": ("AD_SETTINGS",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_LoadAnimateLCMI2VModel', + display_name='Load AnimateLCM-I2V Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/AnimateLCM-I2V', + inputs=[ + io.Combo.Input('model_name', options=get_available_motion_models()), + io.Custom("AD_SETTINGS").Input('ad_settings', optional=True), + ], + outputs=[ + io.Custom("MOTION_MODEL_ADE").Output('MOTION_MODEL'), + io.Custom("MOTION_MODEL_ADE").Output('encoder_only'), + ], + ) - RETURN_TYPES = ("MOTION_MODEL_ADE", "MOTION_MODEL_ADE") - RETURN_NAMES = ("MOTION_MODEL", "encoder_only") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/AnimateLCM-I2V" - FUNCTION = "load_motion_model" - def load_motion_model(self, model_name: str, ad_settings: AnimateDiffSettings=None): + @classmethod + def execute(cls, model_name: str, ad_settings: AnimateDiffSettings=None): # load motion module and motion settings, if included motion_model = load_motion_module_gen2(model_name=model_name, motion_model_settings=ad_settings) # make sure model is an AnimateLCM-I2V model @@ -89,61 +94,66 @@ class LoadAnimateLCMI2VModelNode: raise Exception(f"Motion model '{motion_model.model.mm_info.mm_name}' is not an AnimateLCM-I2V model; selected model IS AnimateLCM, but does NOT contain an img_encoder.") # create encoder-only motion model encoder_only_motion_model = create_fresh_encoder_only_model(motion_model=motion_model) - return (motion_model, encoder_only_motion_model) + return io.NodeOutput(motion_model, encoder_only_motion_model) -class LoadAnimateDiffAndInjectI2VNode: - EXPERIMENTAL = True +class LoadAnimateDiffAndInjectI2VNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model_name": (get_available_motion_models(),), - "motion_model": ("MOTION_MODEL_ADE",), - }, - "optional": { - "ad_settings": ("AD_SETTINGS",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_InjectI2VIntoAnimateDiffModel', + display_name='πŸ§ͺInject I2V into AnimateDiff Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/AnimateLCM-I2V/πŸ§ͺexperimental', + inputs=[ + io.Combo.Input('model_name', options=get_available_motion_models()), + io.Custom("MOTION_MODEL_ADE").Input('motion_model'), + io.Custom("AD_SETTINGS").Input('ad_settings', optional=True), + ], + outputs=[ + io.Custom("MOTION_MODEL_ADE").Output('MOTION_MODEL'), + ], + is_experimental=True, + ) - RETURN_TYPES = ("MOTION_MODEL_ADE",) - RETURN_NAMES = ("MOTION_MODEL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/AnimateLCM-I2V/πŸ§ͺexperimental" - FUNCTION = "load_motion_model" - def load_motion_model(self, model_name: str, motion_model: MotionModelPatcher, ad_settings: AnimateDiffSettings=None): + @classmethod + def execute(cls, model_name: str, motion_model: MotionModelPatcher, ad_settings: AnimateDiffSettings=None): # make sure model w/ encoder actually has encoder if motion_model.model.img_encoder is None: raise Exception("Passed-in motion model was expected to have an img_encoder, but did not.") # load motion module and motion settings, if included loaded_motion_model = load_motion_module_gen2(model_name=model_name, motion_model_settings=ad_settings) inject_img_encoder_into_model(motion_model=loaded_motion_model, w_encoder=motion_model) - return (loaded_motion_model,) + return io.NodeOutput(loaded_motion_model,) -class UpscaleAndVaeEncode: +class UpscaleAndVaeEncode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "image": ("IMAGE",), - "vae": ("VAE",), - "latent_size": ("LATENT",), - "scale_method": (ScaleMethods._LIST_IMAGE,), - "crop": (CropMethods._LIST, {"default": CropMethods.CENTER},), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_UpscaleAndVAEEncode', + display_name='Scale Ref Image and VAE Encode πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/AnimateLCM-I2V', + inputs=[ + io.Image.Input('image'), + io.Vae.Input('vae'), + io.Latent.Input('latent_size'), + io.Combo.Input('scale_method', options=['nearest-exact', 'bilinear', 'area', 'bicubic', 'lanczos']), + io.Combo.Input('crop', options=['disabled', 'center'], default='center'), + ], + outputs=[ + io.Latent.Output('LATENT'), + ], + ) - RETURN_TYPES = ("LATENT",) - FUNCTION = "preprocess_images" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/AnimateLCM-I2V" - def preprocess_images(self, image: torch.Tensor, vae: VAE, latent_size: torch.Tensor, scale_method: str, crop: str): + @classmethod + def execute(cls, image: torch.Tensor, vae: VAE, latent_size: torch.Tensor, scale_method: str, crop: str): b, c, h, w = latent_size["samples"].size() image = image.movedim(-1,1) image = comfy.utils.common_upscale(samples=image, width=w*8, height=h*8, upscale_method=scale_method, crop=crop) image = image.movedim(1,-1) # now that images are the expected size, VAEEncode them - return ({"samples": vae_encode_raw_batched(vae, image)},) + return io.NodeOutput({"samples": vae_encode_raw_batched(vae, image)},) diff --git a/animatediff/nodes_cameractrl.py b/animatediff/nodes_cameractrl.py index 3506f2d..d6fa2ac 100644 --- a/animatediff/nodes_cameractrl.py +++ b/animatediff/nodes_cameractrl.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from typing import Union import os import torch @@ -218,38 +219,40 @@ def poses_to_ndarray(poses: list[list[float]]) -> np.ndarray: return RT -class ApplyAnimateDiffWithCameraCtrl: +class ApplyAnimateDiffWithCameraCtrl(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "motion_model": ("MOTION_MODEL_ADE",), - "cameractrl_poses": ("CAMERACTRL_POSES",), - "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": { - "motion_lora": ("MOTION_LORA",), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "cameractrl_multival": ("MULTIVAL",), - "ad_keyframes": ("AD_KEYFRAMES",), - "prev_m_models": ("M_MODELS",), - "per_block": ("PER_BLOCK",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ApplyAnimateDiffModelWithCameraCtrl', + display_name='Apply AnimateDiff+CameraCtrl Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl', + inputs=[ + io.Custom("MOTION_MODEL_ADE").Input('motion_model'), + io.Custom("CAMERACTRL_POSES").Input('cameractrl_poses'), + 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("MOTION_LORA").Input('motion_lora', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("MULTIVAL").Input('cameractrl_multival', optional=True), + io.Custom("AD_KEYFRAMES").Input('ad_keyframes', optional=True), + io.Custom("M_MODELS").Input('prev_m_models', optional=True), + io.Custom("PER_BLOCK").Input('per_block', optional=True), + ], + outputs=[ + io.Custom("M_MODELS").Output('M_MODELS'), + ], + ) - RETURN_TYPES = ("M_MODELS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl" - FUNCTION = "apply_motion_model" - def apply_motion_model(self, motion_model: MotionModelPatcher, cameractrl_poses: list[list[float]], start_percent: float=0.0, end_percent: float=1.0, + @classmethod + def execute(cls, motion_model: MotionModelPatcher, cameractrl_poses: list[list[float]], start_percent: float=0.0, end_percent: float=1.0, motion_lora: MotionLoraList=None, ad_keyframes: ADKeyframeGroup=None, scale_multival=None, effect_multival=None, cameractrl_multival=None, per_block=None, prev_m_models: MotionModelGroup=None,): - new_m_models = ApplyAnimateDiffModelNode.apply_motion_model(self, motion_model, start_percent=start_percent, end_percent=end_percent, + new_m_models = ApplyAnimateDiffModelNode.execute( motion_model, start_percent=start_percent, end_percent=end_percent, motion_lora=motion_lora, ad_keyframes=ad_keyframes, per_block=per_block, - scale_multival=scale_multival, effect_multival=effect_multival, prev_m_models=prev_m_models) + scale_multival=scale_multival, effect_multival=effect_multival, prev_m_models=prev_m_models).args # most recent added model will always be first in list; curr_model = new_m_models[0].models[0] # confirm that model contains camera_encoder @@ -259,84 +262,88 @@ class ApplyAnimateDiffWithCameraCtrl: attachment = get_mm_attachment(curr_model) attachment.orig_camera_entries = camera_entries attachment.cameractrl_multival = cameractrl_multival - return new_m_models + return io.NodeOutput(*new_m_models) -class LoadAnimateDiffModelWithCameraCtrl: +class LoadAnimateDiffModelWithCameraCtrl(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model_name": (get_available_motion_models(),), - "camera_ctrl": (get_available_motion_models(),), - }, - "optional": { - "ad_settings": ("AD_SETTINGS",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_LoadAnimateDiffModelWithCameraCtrl', + display_name='Load AnimateDiff+CameraCtrl Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl', + inputs=[ + io.Combo.Input('model_name', options=get_available_motion_models()), + io.Combo.Input('camera_ctrl', options=get_available_motion_models()), + io.Custom("AD_SETTINGS").Input('ad_settings', optional=True), + ], + outputs=[ + io.Custom("MOTION_MODEL_ADE").Output('MOTION_MODEL'), + ], + ) - RETURN_TYPES = ("MOTION_MODEL_ADE",) - RETURN_NAMES = ("MOTION_MODEL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl" - FUNCTION = "load_camera_ctrl" - def load_camera_ctrl(self, model_name: str, camera_ctrl: str, ad_settings: AnimateDiffSettings=None): + @classmethod + def execute(cls, model_name: str, camera_ctrl: str, ad_settings: AnimateDiffSettings=None): loaded_motion_model = load_motion_module_gen2(model_name=model_name, motion_model_settings=ad_settings) inject_camera_encoder_into_model(motion_model=loaded_motion_model, camera_ctrl_name=camera_ctrl) - return (loaded_motion_model,) + return io.NodeOutput(loaded_motion_model,) -class CameraCtrlADKeyframeNode: +class CameraCtrlADKeyframeNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ), - }, - "optional": { - "prev_ad_keyframes": ("AD_KEYFRAMES", ), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "cameractrl_multival": ("MULTIVAL",), - "inherit_missing": ("BOOLEAN", {"default": True}, ), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CameraCtrlAnimateDiffKeyframe', + display_name='AnimateDiff+CameraCtrl Keyframe πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl', + inputs=[ + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Custom("AD_KEYFRAMES").Input('prev_ad_keyframes', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("MULTIVAL").Input('cameractrl_multival', optional=True), + io.Boolean.Input('inherit_missing', optional=True, default=True), + io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), + ], + outputs=[ + io.Custom("AD_KEYFRAMES").Output('AD_KEYFRAMES'), + ], + ) - RETURN_TYPES = ("AD_KEYFRAMES", ) - FUNCTION = "load_keyframe" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl" - def load_keyframe(self, + @classmethod + def execute(cls, start_percent: float, prev_ad_keyframes=None, scale_multival: Union[float, torch.Tensor]=None, effect_multival: Union[float, torch.Tensor]=None, cameractrl_multival: Union[float, torch.Tensor]=None, inherit_missing: bool=True, guarantee_steps: int=1): - return ADKeyframeNode.load_keyframe(self, + return io.NodeOutput(*ADKeyframeNode.execute( start_percent=start_percent, prev_ad_keyframes=prev_ad_keyframes, scale_multival=scale_multival, effect_multival=effect_multival, cameractrl_multival=cameractrl_multival, inherit_missing=inherit_missing, guarantee_steps=guarantee_steps - ) + ).args) -class LoadCameraPosesFromFile: +class LoadCameraPosesFromFile(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - input_dir = folder_paths.get_input_directory() - files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))] - files = [f for f in files if f.endswith(".txt")] - return { - "required": { - "pose_filename": (sorted(files),), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_LoadCameraPoses', + display_name='Load CameraCtrl Poses (File) πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses', + inputs=[ + io.Combo.Input('pose_filename', options=sorted(f for f in os.listdir(folder_paths.get_input_directory()) if os.path.isfile(os.path.join(folder_paths.get_input_directory(), f)) and f.endswith(".txt"))), + ], + outputs=[ + io.Custom("CAMERACTRL_POSES").Output('CAMERACTRL_POSES'), + ], + ) - RETURN_TYPES = ("CAMERACTRL_POSES",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses" - FUNCTION = "load_camera_poses" - def load_camera_poses(self, pose_filename: str): + @classmethod + def execute(cls, pose_filename: str): file_path = folder_paths.get_annotated_filepath(pose_filename) with open(file_path, 'r') as f: poses = f.readlines() @@ -345,36 +352,40 @@ class LoadCameraPosesFromFile: poses = [pose.strip().split(' ') for pose in poses[1:]] poses = [[float(x) for x in pose] for pose in poses] poses = set_original_pose_dims(poses, pose_width=CAM.DEFAULT_POSE_WIDTH, pose_height=CAM.DEFAULT_POSE_HEIGHT) - return (poses,) + return io.NodeOutput(poses,) -class LoadCameraPosesFromPath: +class LoadCameraPosesFromPath(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "optional": { - "file_path": ("STRING", {"default": "X://path/to/pose_file.txt"}), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_LoadCameraPosesFromPath', + display_name='Load CameraCtrl Poses (Path) πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses', + inputs=[ + io.String.Input('file_path', optional=True, default='X://path/to/pose_file.txt'), + ], + outputs=[ + io.Custom("CAMERACTRL_POSES").Output('CAMERACTRL_POSES'), + ], + ) @classmethod - def IS_CHANGED(s, file_path, **kwargs): + def fingerprint_inputs(cls, file_path, **kwargs): if Path(file_path).is_file(): return calculate_file_hash(strip_path(file_path)) return False @classmethod - def VALIDATE_INPUTS(s, file_path, **kwargs): + def validate_inputs(cls, file_path, **kwargs): # This function never gets ran for some reason, I don't care enough to figure out why right now. if not Path(strip_path(file_path)).is_file(): return f"Pose file not found: {file_path}" return True - RETURN_TYPES = ("CAMERACTRL_POSES",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses" - FUNCTION = "load_camera_poses" - def load_camera_poses(self, file_path: str): + @classmethod + def execute(cls, file_path: str): file_path = strip_path(file_path) if not Path(file_path).is_file(): raise Exception(f"Pose file not found: {file_path}") @@ -385,60 +396,64 @@ class LoadCameraPosesFromPath: poses = [pose.strip().split(' ') for pose in poses[1:]] poses = [[float(x) for x in pose] for pose in poses] poses = set_original_pose_dims(poses, pose_width=CAM.DEFAULT_POSE_WIDTH, pose_height=CAM.DEFAULT_POSE_HEIGHT) - return (poses,) + return io.NodeOutput(poses,) -class CameraCtrlPoseBasic: +class CameraCtrlPoseBasic(io.ComfyNode): @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "motion_type": (CAM._LIST,), - "speed": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}), - "frame_length": ("INT", {"default": 16}), - }, - "optional": { - "prev_poses": ("CAMERACTRL_POSES",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CameraPoseBasic', + display_name='Create CameraCtrl Poses πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses', + inputs=[ + io.Combo.Input('motion_type', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Float.Input('speed', default=1.0, max=100.0, min=-100.0, step=0.01), + io.Int.Input('frame_length', default=16), + io.Custom("CAMERACTRL_POSES").Input('prev_poses', optional=True), + ], + outputs=[ + io.Custom("CAMERACTRL_POSES").Output('CAMERACTRL_POSES'), + ], + ) - RETURN_TYPES = ("CAMERACTRL_POSES",) - FUNCTION = "camera_pose_basic" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses" - def camera_pose_basic(self, motion_type: str, speed: float, frame_length: int, prev_poses: list[list[float]]=None): + @classmethod + def execute(cls, motion_type: str, speed: float, frame_length: int, prev_poses: list[list[float]]=None): motion = CAM.get(motion_type) RT = get_camera_motion(motion.rotate, motion.translate, speed, frame_length) new_motion = ndarray_to_poses(RT=RT) if prev_poses is not None: new_motion = combine_poses(prev_poses, new_motion) - return (new_motion,) + return io.NodeOutput(new_motion,) -class CameraCtrlPoseCombo: +class CameraCtrlPoseCombo(io.ComfyNode): @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "motion_type1": (CAM._LIST,), - "motion_type2": (CAM._LIST,), - "motion_type3": (CAM._LIST,), - "motion_type4": (CAM._LIST,), - "motion_type5": (CAM._LIST,), - "motion_type6": (CAM._LIST,), - "speed": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}), - "frame_length": ("INT", {"default": 16}), - }, - "optional": { - "prev_poses": ("CAMERACTRL_POSES",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CameraPoseCombo', + display_name='Create CameraCtrl Poses (Combo) πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses', + inputs=[ + io.Combo.Input('motion_type1', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Combo.Input('motion_type2', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Combo.Input('motion_type3', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Combo.Input('motion_type4', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Combo.Input('motion_type5', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Combo.Input('motion_type6', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Float.Input('speed', default=1.0, max=100.0, min=-100.0, step=0.01), + io.Int.Input('frame_length', default=16), + io.Custom("CAMERACTRL_POSES").Input('prev_poses', optional=True), + ], + outputs=[ + io.Custom("CAMERACTRL_POSES").Output('CAMERACTRL_POSES'), + ], + ) - RETURN_TYPES = ("CAMERACTRL_POSES",) - FUNCTION = "camera_pose_combo" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses" - def camera_pose_combo(self, + @classmethod + def execute(cls, motion_type1: str, motion_type2: str, motion_type3: str, motion_type4: str, motion_type5: str, motion_type6: str, speed: float, frame_length: int, @@ -452,88 +467,98 @@ class CameraCtrlPoseCombo: new_motion = ndarray_to_poses(RT=RT) if prev_poses is not None: new_motion = combine_poses(prev_poses, new_motion) - return (new_motion,) + return io.NodeOutput(new_motion,) -class CameraCtrlPoseAdvanced: +class CameraCtrlPoseAdvanced(io.ComfyNode): @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "motion_type1": (CAM._LIST,), - "strength1": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "motion_type2": (CAM._LIST,), - "strength2": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "motion_type3": (CAM._LIST,), - "strength3": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "motion_type4": (CAM._LIST,), - "strength4": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "motion_type5": (CAM._LIST,), - "strength5": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "motion_type6": (CAM._LIST,), - "strength6": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "speed": ("FLOAT", {"default": 1.0, "min": -100.0, "max": 100.0, "step": 0.01}), - "frame_length": ("INT", {"default": 16}), - }, - "optional": { - "prev_poses": ("CAMERACTRL_POSES",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CameraPoseAdvanced', + display_name='Create CameraCtrl Poses (Adv.) πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses', + inputs=[ + io.Combo.Input('motion_type1', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Float.Input('strength1', default=1.0, max=10.0, min=0.0, step=0.01), + io.Combo.Input('motion_type2', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Float.Input('strength2', default=1.0, max=10.0, min=0.0, step=0.01), + io.Combo.Input('motion_type3', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Float.Input('strength3', default=1.0, max=10.0, min=0.0, step=0.01), + io.Combo.Input('motion_type4', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Float.Input('strength4', default=1.0, max=10.0, min=0.0, step=0.01), + io.Combo.Input('motion_type5', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Float.Input('strength5', default=1.0, max=10.0, min=0.0, step=0.01), + io.Combo.Input('motion_type6', options=['Static', 'Pan Up', 'Pan Down', 'Pan Left', 'Pan Right', 'Zoom In', 'Zoom Out', 'Roll Clockwise', 'Roll Anticlockwise', 'Tilt Down', 'Tilt Up', 'Tilt Left', 'Tilt Right']), + io.Float.Input('strength6', default=1.0, max=10.0, min=0.0, step=0.01), + io.Float.Input('speed', default=1.0, max=100.0, min=-100.0, step=0.01), + io.Int.Input('frame_length', default=16), + io.Custom("CAMERACTRL_POSES").Input('prev_poses', optional=True), + ], + outputs=[ + io.Custom("CAMERACTRL_POSES").Output('CAMERACTRL_POSES'), + ], + ) - RETURN_TYPES = ("CAMERACTRL_POSES",) - FUNCTION = "camera_pose_combo" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses" - def camera_pose_combo(self, + @classmethod + def execute(cls, motion_type1: str, motion_type2: str, motion_type3: str, motion_type4: str, motion_type5: str, motion_type6: str, speed: float, frame_length: int, prev_poses: list[list[float]]=None, strength1=1.0, strength2=1.0, strength3=1.0, strength4=1.0, strength5=1.0, strength6=1.0): - return CameraCtrlPoseCombo.camera_pose_combo(self, + return io.NodeOutput(*CameraCtrlPoseCombo.execute( motion_type1=motion_type1, motion_type2=motion_type2, motion_type3=motion_type3, motion_type4=motion_type4, motion_type5=motion_type5, motion_type6=motion_type6, speed=speed, frame_length=frame_length, prev_poses=prev_poses, strength1=strength1, strength2=strength2, strength3=strength3, - strength4=strength4, strength5=strength5, strength6=strength6) + strength4=strength4, strength5=strength5, strength6=strength6).args) -class CameraCtrlManualAppendPose: +class CameraCtrlManualAppendPose(io.ComfyNode): @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "poses_first": ("CAMERACTRL_POSES",), - "poses_last": ("CAMERACTRL_POSES",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CameraManualPoseAppend', + display_name='Manual Append CameraCtrl Poses πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses', + inputs=[ + io.Custom("CAMERACTRL_POSES").Input('poses_first'), + io.Custom("CAMERACTRL_POSES").Input('poses_last'), + ], + outputs=[ + io.Custom("CAMERACTRL_POSES").Output('CAMERACTRL_POSES'), + ], + ) - RETURN_TYPES = ("CAMERACTRL_POSES",) - FUNCTION = "camera_manual_append" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses" - def camera_manual_append(self, poses_first: list[list[float]], poses_last: list[list[float]]): - return (combine_poses(poses0=poses_first, poses1=poses_last),) - - -class CameraCtrlReplaceCameraParameters: @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "poses":("CAMERACTRL_POSES",), - "fx": ("FLOAT", {"default": CAM.DEFAULT_FX, "min": 0, "max": 1, "step": 0.000000001}), - "fy": ("FLOAT", {"default": CAM.DEFAULT_FY, "min": 0, "max": 1, "step": 0.000000001}), - "cx": ("FLOAT", {"default": CAM.DEFAULT_CX, "min": 0, "max": 1, "step": 0.01}), - "cy": ("FLOAT", {"default": CAM.DEFAULT_CY, "min": 0, "max": 1, "step": 0.01}), - }, - } - - RETURN_TYPES = ("CAMERACTRL_POSES",) - FUNCTION = "set_camera_parameters" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses" + def execute(cls, poses_first: list[list[float]], poses_last: list[list[float]]): + return io.NodeOutput(combine_poses(poses0=poses_first, poses1=poses_last),) - def set_camera_parameters(self, poses: list[list[float]], fx: float, fy: float, cx: float, cy: float): + +class CameraCtrlReplaceCameraParameters(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ReplaceCameraParameters', + display_name='Replace Camera Parameters πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses', + inputs=[ + io.Custom("CAMERACTRL_POSES").Input('poses'), + io.Float.Input('fx', default=0.474812461, max=1, min=0, step=1e-09), + io.Float.Input('fy', default=0.844111024, max=1, min=0, step=1e-09), + io.Float.Input('cx', default=0.5, max=1, min=0, step=0.01), + io.Float.Input('cy', default=0.5, max=1, min=0, step=0.01), + ], + outputs=[ + io.Custom("CAMERACTRL_POSES").Output('CAMERACTRL_POSES'), + ], + ) + + + @classmethod + def execute(cls, poses: list[list[float]], fx: float, fy: float, cx: float, cy: float): new_poses = copy.deepcopy(poses) for pose in new_poses: # fx,fy,cx,fy are in indexes 1-4 of the 19-long pose list @@ -541,23 +566,27 @@ class CameraCtrlReplaceCameraParameters: pose[2] = fy pose[3] = cx pose[4] = cy - return (new_poses,) + return io.NodeOutput(new_poses,) -class CameraCtrlSetOriginalAspectRatio: +class CameraCtrlSetOriginalAspectRatio(io.ComfyNode): @classmethod - def INPUT_TYPES(cls): - return { - "required": { - "poses":("CAMERACTRL_POSES",), - "orig_pose_width": ("INT", {"default": 1280, "min": 1, "max": BIGMAX}), - "orig_pose_height": ("INT", {"default": 720, "min": 1, "max": BIGMAX}), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ReplaceOriginalPoseAspectRatio', + display_name='Replace Orig. Pose Aspect Ratio πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses', + inputs=[ + io.Custom("CAMERACTRL_POSES").Input('poses'), + io.Int.Input('orig_pose_width', default=1280, max=9007199254740991, min=1), + io.Int.Input('orig_pose_height', default=720, max=9007199254740991, min=1), + ], + outputs=[ + io.Custom("CAMERACTRL_POSES").Output('CAMERACTRL_POSES'), + ], + ) - RETURN_TYPES = ("CAMERACTRL_POSES",) - FUNCTION = "set_aspect_ratio" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/CameraCtrl/poses" - def set_aspect_ratio(self, poses: list[list[float]], orig_pose_width: int, orig_pose_height: int): - return (set_original_pose_dims(poses, pose_width=orig_pose_width, pose_height=orig_pose_height),) + @classmethod + def execute(cls, poses: list[list[float]], orig_pose_width: int, orig_pose_height: int): + return io.NodeOutput(set_original_pose_dims(poses, pose_width=orig_pose_width, pose_height=orig_pose_height),) diff --git a/animatediff/nodes_conditioning.py b/animatediff/nodes_conditioning.py index 7f5df62..d12b400 100644 --- a/animatediff/nodes_conditioning.py +++ b/animatediff/nodes_conditioning.py @@ -1,9 +1,9 @@ +from comfy_api.latest import io import uuid import folder_paths from typing import Union from torch import Tensor from collections.abc import Iterable - from comfy.model_patcher import ModelPatcher from comfy.sd import CLIP import comfy.sd @@ -11,60 +11,28 @@ from comfy.hooks import HookGroup, HookKeyframeGroup, HookKeyframe import comfy_extras.nodes_hooks import comfy.hooks import comfy.utils - from .utils_model import BIGMAX, InterpolationMethod from .logger import logger - -################################################################### -# EVERYTHING BELOW HERE IS DEPRECATED; -# Can be replaced with vanilla ComfyUI nodes -#------------------------------------------------------------------ -#------------------------------------------------------------------ -#------------------------------------------------------------------ -#------------------------------------------------------------------ -#------------------------------------------------------------------ class COND_CONST: - COND_AREA_DEFAULT = "default" - COND_AREA_MASK_BOUNDS = "mask bounds" + COND_AREA_DEFAULT = 'default' + COND_AREA_MASK_BOUNDS = 'mask bounds' _LIST_COND_AREA = [COND_AREA_DEFAULT, COND_AREA_MASK_BOUNDS] +class CreateLoraHookKeyframeInterpolationDEPR(io.ComfyNode): -class CreateLoraHookKeyframeInterpolationDEPR: @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": (InterpolationMethod._LIST, ), - "intervals": ("INT", {"default": 5, "min": 2, "max": 100, "step": 1}), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_hook_kf": ("HOOK_KEYFRAMES",), - }, - } - - DEPRECATED = True - RETURN_TYPES = ("HOOK_KEYFRAMES",) - RETURN_NAMES = ("HOOK_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/schedule lora hooks" - FUNCTION = "create_hook_keyframes" + def define_schema(cls): + return io.Schema(node_id='ADE_LoraHookKeyframeInterpolation', display_name='LoRA Hook Keyframes Interp. πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/schedule lora hooks', 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=5, max=100, min=2, step=1), io.Boolean.Input('print_keyframes', default=False), io.Custom('HOOK_KEYFRAMES').Input('prev_hook_kf', optional=True)], outputs=[io.Custom('HOOK_KEYFRAMES').Output('HOOK_KF')], is_deprecated=True) - def create_hook_keyframes(self, - start_percent: float, end_percent: float, - strength_start: float, strength_end: float, interpolation: str, intervals: int, - prev_hook_kf: HookKeyframeGroup=None, print_keyframes=False): + @classmethod + def execute(cls, start_percent: float, end_percent: float, strength_start: float, strength_end: float, interpolation: str, intervals: int, prev_hook_kf: HookKeyframeGroup=None, print_keyframes=False): if prev_hook_kf: prev_hook_kf = prev_hook_kf.clone() else: prev_hook_kf = HookKeyframeGroup() percents = InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=intervals, method=InterpolationMethod.LINEAR) strengths = InterpolationMethod.get_weights(num_from=strength_start, num_to=strength_end, length=intervals, method=interpolation) - is_first = True for percent, strength in zip(percents, strengths): guarantee_steps = 0 @@ -73,338 +41,143 @@ class CreateLoraHookKeyframeInterpolationDEPR: is_first = False prev_hook_kf.add(HookKeyframe(strength=strength, start_percent=percent, guarantee_steps=guarantee_steps)) if print_keyframes: - logger.info(f"HookKeyframe - start_percent:{percent} = {strength}") - return (prev_hook_kf,) + logger.info(f'HookKeyframe - start_percent:{percent} = {strength}') + return io.NodeOutput(prev_hook_kf) +class PairedConditioningSetMaskHookedDEPR(io.ComfyNode): -############################################### -### Mask, Combine, and Hook Conditioning -############################################### -class PairedConditioningSetMaskHookedDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "positive_ADD": ("CONDITIONING", ), - "negative_ADD": ("CONDITIONING", ), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "set_cond_area": (COND_CONST._LIST_COND_AREA,), - }, - "optional": { - "opt_mask": ("MASK", ), - "opt_lora_hook": ("HOOKS",), - "opt_timesteps": ("TIMESTEPS_RANGE",), - }, - } + def define_schema(cls): + return io.Schema(node_id='ADE_PairedConditioningSetMask', display_name='Set Props on Conds πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning', inputs=[io.Conditioning.Input('positive_ADD'), io.Conditioning.Input('negative_ADD'), io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01), io.Combo.Input('set_cond_area', options=['default', 'mask bounds']), io.Mask.Input('opt_mask', optional=True), io.Custom('HOOKS').Input('opt_lora_hook', optional=True), io.Custom('TIMESTEPS_RANGE').Input('opt_timesteps', optional=True)], outputs=[io.Conditioning.Output('positive'), io.Conditioning.Output('negative')], is_deprecated=True) - RETURN_TYPES = ("CONDITIONING", "CONDITIONING") - RETURN_NAMES = ("positive", "negative") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning" - FUNCTION = "append_and_hook" - DEPRECATED = True - - def append_and_hook(self, positive_ADD, negative_ADD, - strength: float, set_cond_area: str, - opt_mask: Tensor=None, opt_lora_hook: HookGroup=None, opt_timesteps: tuple=None): - final_positive, final_negative = comfy.hooks.set_conds_props(conds=[positive_ADD, negative_ADD], - strength=strength, set_cond_area=set_cond_area, - mask=opt_mask, hooks=opt_lora_hook, timesteps_range=opt_timesteps) - return (final_positive, final_negative) - - -class ConditioningSetMaskHookedDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cond_ADD": ("CONDITIONING",), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "set_cond_area": (COND_CONST._LIST_COND_AREA,), - }, - "optional": { - "opt_mask": ("MASK", ), - "opt_lora_hook": ("HOOKS",), - "opt_timesteps": ("TIMESTEPS_RANGE",), - }, - } + def execute(cls, positive_ADD, negative_ADD, strength: float, set_cond_area: str, opt_mask: Tensor=None, opt_lora_hook: HookGroup=None, opt_timesteps: tuple=None): + final_positive, final_negative = comfy.hooks.set_conds_props(conds=[positive_ADD, negative_ADD], strength=strength, set_cond_area=set_cond_area, mask=opt_mask, hooks=opt_lora_hook, timesteps_range=opt_timesteps) + return io.NodeOutput(final_positive, final_negative) - RETURN_TYPES = ("CONDITIONING",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops" - FUNCTION = "append_and_hook" - DEPRECATED = True +class ConditioningSetMaskHookedDEPR(io.ComfyNode): - def append_and_hook(self, cond_ADD, - strength: float, set_cond_area: str, - opt_mask: Tensor=None, opt_lora_hook: HookGroup=None, opt_timesteps: tuple=None): - (final_conditioning,) = comfy.hooks.set_conds_props(conds=[cond_ADD], - strength=strength, set_cond_area=set_cond_area, - mask=opt_mask, hooks=opt_lora_hook, timesteps_range=opt_timesteps) - return (final_conditioning,) - - -class PairedConditioningSetMaskAndCombineHookedDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "positive": ("CONDITIONING",), - "negative": ("CONDITIONING",), - "positive_ADD": ("CONDITIONING",), - "negative_ADD": ("CONDITIONING",), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "set_cond_area": (COND_CONST._LIST_COND_AREA,), - }, - "optional": { - "opt_mask": ("MASK", ), - "opt_lora_hook": ("HOOKS",), - "opt_timesteps": ("TIMESTEPS_RANGE",), - }, - } - - RETURN_TYPES = ("CONDITIONING", "CONDITIONING") - RETURN_NAMES = ("positive", "negative") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning" - FUNCTION = "append_and_combine" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_ConditioningSetMask', display_name='Set Props on Cond πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops', inputs=[io.Conditioning.Input('cond_ADD'), io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01), io.Combo.Input('set_cond_area', options=['default', 'mask bounds']), io.Mask.Input('opt_mask', optional=True), io.Custom('HOOKS').Input('opt_lora_hook', optional=True), io.Custom('TIMESTEPS_RANGE').Input('opt_timesteps', optional=True)], outputs=[io.Conditioning.Output('CONDITIONING')], is_deprecated=True) - def append_and_combine(self, positive, negative, positive_ADD, negative_ADD, - strength: float, set_cond_area: str, - opt_mask: Tensor=None, opt_lora_hook: HookGroup=None, opt_timesteps: tuple=None): - final_positive, final_negative = comfy.hooks.set_conds_props_and_combine(conds=[positive, negative], new_conds=[positive_ADD, negative_ADD], - strength=strength, set_cond_area=set_cond_area, - mask=opt_mask, hooks=opt_lora_hook, timesteps_range=opt_timesteps) - return (final_positive, final_negative,) - - -class ConditioningSetMaskAndCombineHookedDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cond": ("CONDITIONING",), - "cond_ADD": ("CONDITIONING",), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.01}), - "set_cond_area": (COND_CONST._LIST_COND_AREA,), - }, - "optional": { - "opt_mask": ("MASK", ), - "opt_lora_hook": ("HOOKS",), - "opt_timesteps": ("TIMESTEPS_RANGE",), - }, - } - - RETURN_TYPES = ("CONDITIONING",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops" - FUNCTION = "append_and_combine" - DEPRECATED = True + def execute(cls, cond_ADD, strength: float, set_cond_area: str, opt_mask: Tensor=None, opt_lora_hook: HookGroup=None, opt_timesteps: tuple=None): + final_conditioning, = comfy.hooks.set_conds_props(conds=[cond_ADD], strength=strength, set_cond_area=set_cond_area, mask=opt_mask, hooks=opt_lora_hook, timesteps_range=opt_timesteps) + return io.NodeOutput(final_conditioning) - def append_and_combine(self, cond, cond_ADD, - strength: float, set_cond_area: str, - opt_mask: Tensor=None, opt_lora_hook: HookGroup=None, opt_timesteps: tuple=None): - (final_conditioning,) = comfy.hooks.set_conds_props_and_combine(conds=[cond], new_conds=[cond_ADD], - strength=strength, set_cond_area=set_cond_area, - mask=opt_mask, hooks=opt_lora_hook, timesteps_range=opt_timesteps) - return (final_conditioning,) +class PairedConditioningSetMaskAndCombineHookedDEPR(io.ComfyNode): - -class PairedConditioningSetUnmaskedAndCombineHookedDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "positive": ("CONDITIONING",), - "negative": ("CONDITIONING",), - "positive_DEFAULT": ("CONDITIONING",), - "negative_DEFAULT": ("CONDITIONING",), - }, - "optional": { - "opt_lora_hook": ("HOOKS",), - }, - } - - RETURN_TYPES = ("CONDITIONING", "CONDITIONING") - RETURN_NAMES = ("positive", "negative") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning" - FUNCTION = "append_and_combine" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_PairedConditioningSetMaskAndCombine', display_name='Set Props and Combine Conds πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning', inputs=[io.Conditioning.Input('positive'), io.Conditioning.Input('negative'), io.Conditioning.Input('positive_ADD'), io.Conditioning.Input('negative_ADD'), io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01), io.Combo.Input('set_cond_area', options=['default', 'mask bounds']), io.Mask.Input('opt_mask', optional=True), io.Custom('HOOKS').Input('opt_lora_hook', optional=True), io.Custom('TIMESTEPS_RANGE').Input('opt_timesteps', optional=True)], outputs=[io.Conditioning.Output('positive'), io.Conditioning.Output('negative')], is_deprecated=True) - def append_and_combine(self, positive, negative, positive_DEFAULT, negative_DEFAULT, - opt_lora_hook: HookGroup=None): - final_positive, final_negative = comfy.hooks.set_default_conds_and_combine(conds=[positive, negative], new_conds=[positive_DEFAULT, negative_DEFAULT], - hooks=opt_lora_hook) - return (final_positive, final_negative,) - - -class ConditioningSetUnmaskedAndCombineHookedDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cond": ("CONDITIONING",), - "cond_DEFAULT": ("CONDITIONING",), - }, - "optional": { - "opt_lora_hook": ("HOOKS",), - }, - } - - RETURN_TYPES = ("CONDITIONING",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops" - FUNCTION = "append_and_combine" - DEPRECATED = True + def execute(cls, positive, negative, positive_ADD, negative_ADD, strength: float, set_cond_area: str, opt_mask: Tensor=None, opt_lora_hook: HookGroup=None, opt_timesteps: tuple=None): + final_positive, final_negative = comfy.hooks.set_conds_props_and_combine(conds=[positive, negative], new_conds=[positive_ADD, negative_ADD], strength=strength, set_cond_area=set_cond_area, mask=opt_mask, hooks=opt_lora_hook, timesteps_range=opt_timesteps) + return io.NodeOutput(final_positive, final_negative) - def append_and_combine(self, cond, cond_DEFAULT, - opt_lora_hook: HookGroup=None): - (final_conditioning,) = comfy.hooks.set_default_conds_and_combine(conds=[cond], new_conds=[cond_DEFAULT], - hooks=opt_lora_hook) - return (final_conditioning,) - +class ConditioningSetMaskAndCombineHookedDEPR(io.ComfyNode): -class PairedConditioningCombineDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "positive_A": ("CONDITIONING",), - "negative_A": ("CONDITIONING",), - "positive_B": ("CONDITIONING",), - "negative_B": ("CONDITIONING",), - }, - } + def define_schema(cls): + return io.Schema(node_id='ADE_ConditioningSetMaskAndCombine', display_name='Set Props and Combine Cond πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops', inputs=[io.Conditioning.Input('cond'), io.Conditioning.Input('cond_ADD'), io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.01), io.Combo.Input('set_cond_area', options=['default', 'mask bounds']), io.Mask.Input('opt_mask', optional=True), io.Custom('HOOKS').Input('opt_lora_hook', optional=True), io.Custom('TIMESTEPS_RANGE').Input('opt_timesteps', optional=True)], outputs=[io.Conditioning.Output('CONDITIONING')], is_deprecated=True) - RETURN_TYPES = ("CONDITIONING", "CONDITIONING") - RETURN_NAMES = ("positive", "negative") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning" - FUNCTION = "combine" - DEPRECATED = True - - def combine(self, positive_A, negative_A, positive_B, negative_B): - final_positive, final_negative = comfy.hooks.set_conds_props_and_combine(conds=[positive_A, negative_A], new_conds=[positive_B, negative_B],) - return (final_positive, final_negative,) - - -class ConditioningCombineDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cond_A": ("CONDITIONING",), - "cond_B": ("CONDITIONING",), - }, - } - - RETURN_TYPES = ("CONDITIONING",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops" - FUNCTION = "combine" - DEPRECATED = True + def execute(cls, cond, cond_ADD, strength: float, set_cond_area: str, opt_mask: Tensor=None, opt_lora_hook: HookGroup=None, opt_timesteps: tuple=None): + final_conditioning, = comfy.hooks.set_conds_props_and_combine(conds=[cond], new_conds=[cond_ADD], strength=strength, set_cond_area=set_cond_area, mask=opt_mask, hooks=opt_lora_hook, timesteps_range=opt_timesteps) + return io.NodeOutput(final_conditioning) - def combine(self, cond_A, cond_B): - (final_conditioning,) = comfy.hooks.set_conds_props_and_combine(conds=[cond_A], new_conds=[cond_B],) - return (final_conditioning,) -############################################### -############################################### -############################################### +class PairedConditioningSetUnmaskedAndCombineHookedDEPR(io.ComfyNode): - - -############################################### -### Scheduling -############################################### -class ConditioningTimestepsNodeDEPR: @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}) - }, - } - - RETURN_TYPES = ("TIMESTEPS_RANGE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning" - FUNCTION = "create_schedule" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_PairedConditioningSetUnmaskedAndCombine', display_name='Set Unmasked Conds πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning', inputs=[io.Conditioning.Input('positive'), io.Conditioning.Input('negative'), io.Conditioning.Input('positive_DEFAULT'), io.Conditioning.Input('negative_DEFAULT'), io.Custom('HOOKS').Input('opt_lora_hook', optional=True)], outputs=[io.Conditioning.Output('positive'), io.Conditioning.Output('negative')], is_deprecated=True) - def create_schedule(self, start_percent: float, end_percent: float): - return ((start_percent, end_percent),) - - -class SetLoraHookKeyframesDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "lora_hook": ("HOOKS",), - "hook_kf": ("HOOK_KEYFRAMES",), - }, - } - - RETURN_TYPES = ("HOOKS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning" - FUNCTION = "set_hook_keyframes" - DEPRECATED = True + def execute(cls, positive, negative, positive_DEFAULT, negative_DEFAULT, opt_lora_hook: HookGroup=None): + final_positive, final_negative = comfy.hooks.set_default_conds_and_combine(conds=[positive, negative], new_conds=[positive_DEFAULT, negative_DEFAULT], hooks=opt_lora_hook) + return io.NodeOutput(final_positive, final_negative) - def set_hook_keyframes(self, lora_hook: HookGroup, hook_kf: HookKeyframeGroup): +class ConditioningSetUnmaskedAndCombineHookedDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_ConditioningSetUnmaskedAndCombine', display_name='Set Unmasked Cond πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops', inputs=[io.Conditioning.Input('cond'), io.Conditioning.Input('cond_DEFAULT'), io.Custom('HOOKS').Input('opt_lora_hook', optional=True)], outputs=[io.Conditioning.Output('CONDITIONING')], is_deprecated=True) + + @classmethod + def execute(cls, cond, cond_DEFAULT, opt_lora_hook: HookGroup=None): + final_conditioning, = comfy.hooks.set_default_conds_and_combine(conds=[cond], new_conds=[cond_DEFAULT], hooks=opt_lora_hook) + return io.NodeOutput(final_conditioning) + +class PairedConditioningCombineDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_PairedConditioningCombine', display_name='Manual Combine Conds πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning', inputs=[io.Conditioning.Input('positive_A'), io.Conditioning.Input('negative_A'), io.Conditioning.Input('positive_B'), io.Conditioning.Input('negative_B')], outputs=[io.Conditioning.Output('positive'), io.Conditioning.Output('negative')], is_deprecated=True) + + @classmethod + def execute(cls, positive_A, negative_A, positive_B, negative_B): + final_positive, final_negative = comfy.hooks.set_conds_props_and_combine(conds=[positive_A, negative_A], new_conds=[positive_B, negative_B]) + return io.NodeOutput(final_positive, final_negative) + +class ConditioningCombineDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_ConditioningCombine', display_name='Manual Combine Cond πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops', inputs=[io.Conditioning.Input('cond_A'), io.Conditioning.Input('cond_B')], outputs=[io.Conditioning.Output('CONDITIONING')], is_deprecated=True) + + @classmethod + def execute(cls, cond_A, cond_B): + final_conditioning, = comfy.hooks.set_conds_props_and_combine(conds=[cond_A], new_conds=[cond_B]) + return io.NodeOutput(final_conditioning) + +class ConditioningTimestepsNodeDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_TimestepsConditioning', display_name='Timesteps Conditioning πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning', 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)], outputs=[io.Custom('TIMESTEPS_RANGE').Output('TIMESTEPS_RANGE')], is_deprecated=True) + + @classmethod + def execute(cls, start_percent: float, end_percent: float): + return io.NodeOutput((start_percent, end_percent)) + +class SetLoraHookKeyframesDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_SetLoraHookKeyframe', display_name='Set LoRA Hook Keyframes πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning', inputs=[io.Custom('HOOKS').Input('lora_hook'), io.Custom('HOOK_KEYFRAMES').Input('hook_kf')], outputs=[io.Custom('HOOKS').Output('HOOKS')], is_deprecated=True) + + @classmethod + def execute(cls, lora_hook: HookGroup, hook_kf: HookKeyframeGroup): new_lora_hook = lora_hook.clone() new_lora_hook.set_keyframes_on_hooks(hook_kf=hook_kf) - return (new_lora_hook,) + return io.NodeOutput(new_lora_hook) +class CreateLoraHookKeyframeDEPR(io.ComfyNode): -class CreateLoraHookKeyframeDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - }, - "optional": { - "prev_hook_kf": ("HOOK_KEYFRAMES",), - }, - } - - RETURN_TYPES = ("HOOK_KEYFRAMES",) - RETURN_NAMES = ("HOOK_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/schedule lora hooks" - FUNCTION = "create_hook_keyframe" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_LoraHookKeyframe', display_name='LoRA Hook Keyframe πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/schedule lora hooks', inputs=[io.Float.Input('strength_model', default=1.0, max=20.0, min=-20.0, step=0.01), io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', default=1, max=9007199254740991, min=0), io.Custom('HOOK_KEYFRAMES').Input('prev_hook_kf', optional=True)], outputs=[io.Custom('HOOK_KEYFRAMES').Output('HOOK_KF')], is_deprecated=True) - def create_hook_keyframe(self, strength_model: float, start_percent: float, guarantee_steps: float, - prev_hook_kf: HookKeyframeGroup=None): + @classmethod + def execute(cls, strength_model: float, start_percent: float, guarantee_steps: float, prev_hook_kf: HookKeyframeGroup=None): if prev_hook_kf: prev_hook_kf = prev_hook_kf.clone() else: prev_hook_kf = HookKeyframeGroup() keyframe = HookKeyframe(strength=strength_model, start_percent=start_percent, guarantee_steps=guarantee_steps) prev_hook_kf.add(keyframe) - return (prev_hook_kf,) - + return io.NodeOutput(prev_hook_kf) + +class CreateLoraHookKeyframeFromStrengthListDEPR(io.ComfyNode): -class CreateLoraHookKeyframeFromStrengthListDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "strengths_float": ("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}), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_hook_kf": ("HOOK_KEYFRAMES",), - } - } - - RETURN_TYPES = ("HOOK_KEYFRAMES",) - RETURN_NAMES = ("HOOK_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/schedule lora hooks" - FUNCTION = "create_hook_keyframes" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_LoraHookKeyframeFromStrengthList', display_name='LoRA Hook Keyframes From List πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/schedule lora hooks', inputs=[io.Float.Input('strengths_float', 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.Boolean.Input('print_keyframes', default=False), io.Custom('HOOK_KEYFRAMES').Input('prev_hook_kf', optional=True)], outputs=[io.Custom('HOOK_KEYFRAMES').Output('HOOK_KF')], is_deprecated=True) - def create_hook_keyframes(self, strengths_float: Union[float, list[float]], - start_percent: float, end_percent: float, - prev_hook_kf: HookKeyframeGroup=None, print_keyframes=False): + @classmethod + def execute(cls, strengths_float: Union[float, list[float]], start_percent: float, end_percent: float, prev_hook_kf: HookKeyframeGroup=None, print_keyframes=False): if prev_hook_kf: prev_hook_kf = prev_hook_kf.clone() else: @@ -414,9 +187,8 @@ class CreateLoraHookKeyframeFromStrengthListDEPR: elif isinstance(strengths_float, Iterable): pass else: - raise Exception(f"strengths_float must be either an interable input or a float, but was {type(strengths_float).__repr__}.") + raise Exception(f'strengths_float must be either an interable input or a float, but was {type(strengths_float).__repr__}.') percents = InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=len(strengths_float), method=InterpolationMethod.LINEAR) - is_first = True for percent, strength in zip(percents, strengths_float): guarantee_steps = 0 @@ -425,256 +197,121 @@ class CreateLoraHookKeyframeFromStrengthListDEPR: is_first = False prev_hook_kf.add(HookKeyframe(strength=strength, start_percent=percent, guarantee_steps=guarantee_steps)) if print_keyframes: - logger.info(f"HookKeyframe - start_percent:{percent} = {strength}") - return (prev_hook_kf,) -############################################### -############################################### -############################################### + logger.info(f'HookKeyframe - start_percent:{percent} = {strength}') + return io.NodeOutput(prev_hook_kf) - -############################################### -### Register LoRA Hooks -############################################### -# based on ComfyUI's nodes.py LoraLoader -class MaskableLoraLoaderDEPR: - def __init__(self): - self.loaded_lora = None +class MaskableLoraLoaderDEPR(io.ComfyNode): + loaded_lora = None @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "clip": ("CLIP",), - "lora_name": (folder_paths.get_filename_list("loras"), ), - "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), - "strength_clip": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), - }, - } - - RETURN_TYPES = ("MODEL", "CLIP", "HOOKS") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/register lora hooks" - FUNCTION = "load_lora" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_RegisterLoraHook', display_name='Register LoRA Hook πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/register lora hooks', inputs=[io.Model.Input('model'), io.Clip.Input('clip'), io.Combo.Input('lora_name', options=folder_paths.get_filename_list('loras')), io.Float.Input('strength_model', default=1.0, max=20.0, min=-20.0, step=0.01), io.Float.Input('strength_clip', default=1.0, max=20.0, min=-20.0, step=0.01)], outputs=[io.Model.Output('MODEL'), io.Clip.Output('CLIP'), io.Custom('HOOKS').Output('HOOKS')], is_deprecated=True) - def load_lora(self, model: Union[ModelPatcher], clip: CLIP, lora_name: str, strength_model: float, strength_clip: float): + + @classmethod + def execute(cls, model: Union[ModelPatcher], clip: CLIP, lora_name: str, strength_model: float, strength_clip: float): if strength_model == 0 and strength_clip == 0: - return (model, clip, None) - - lora_path = folder_paths.get_full_path("loras", lora_name) + return io.NodeOutput(model, clip, None) + lora_path = folder_paths.get_full_path('loras', lora_name) lora = None - if self.loaded_lora is not None: - if self.loaded_lora[0] == lora_path: - lora = self.loaded_lora[1] + if cls.loaded_lora is not None: + if cls.loaded_lora[0] == lora_path: + lora = cls.loaded_lora[1] else: - temp = self.loaded_lora - self.loaded_lora = None + temp = cls.loaded_lora + cls.loaded_lora = None del temp - if lora is None: lora = comfy.utils.load_torch_file(lora_path, safe_load=True) - self.loaded_lora = (lora_path, lora) - - model_lora, clip_lora, hooks = comfy.hooks.load_hook_lora_for_models(model=model, clip=clip, lora=lora, - strength_model=strength_model, strength_clip=strength_clip) - return (model_lora, clip_lora, hooks) + cls.loaded_lora = (lora_path, lora) + model_lora, clip_lora, hooks = comfy.hooks.load_hook_lora_for_models(model=model, clip=clip, lora=lora, strength_model=strength_model, strength_clip=strength_clip) + return io.NodeOutput(model_lora, clip_lora, hooks) +class MaskableLoraLoaderModelOnlyDEPR(io.ComfyNode): -class MaskableLoraLoaderModelOnlyDEPR(MaskableLoraLoaderDEPR): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "lora_name": (folder_paths.get_filename_list("loras"), ), - "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), - }, - } + def define_schema(cls): + return io.Schema(node_id='ADE_RegisterLoraHookModelOnly', display_name='Register LoRA Hook (Model Only) πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/register lora hooks', inputs=[io.Model.Input('model'), io.Combo.Input('lora_name', options=folder_paths.get_filename_list('loras')), io.Float.Input('strength_model', default=1.0, max=20.0, min=-20.0, step=0.01)], outputs=[io.Model.Output('MODEL'), io.Custom('HOOKS').Output('HOOKS')], is_deprecated=True) - RETURN_TYPES = ("MODEL", "HOOKS") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/register lora hooks" - FUNCTION = "load_lora_model_only" - DEPRECATED = True - - def load_lora_model_only(self, model: ModelPatcher, lora_name: str, strength_model: float): - model_lora, _, hooks = self.load_lora(model=model, clip=None, lora_name=lora_name, - strength_model=strength_model, strength_clip=0) - return (model_lora, hooks) - - -class MaskableSDModelLoaderDEPR(comfy_extras.nodes_hooks.CreateHookModelAsLora): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "clip": ("CLIP",), - "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ), - "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), - "strength_clip": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), - }, - } - - RETURN_TYPES = ("MODEL", "CLIP", "HOOKS") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/register lora hooks" - FUNCTION = "load_model_as_lora" - DEPRECATED = True + def execute(cls, model: ModelPatcher, lora_name: str, strength_model: float): + model_lora, _, hooks = MaskableLoraLoaderDEPR.execute(model=model, clip=None, lora_name=lora_name, strength_model=strength_model, strength_clip=0).args + return io.NodeOutput(model_lora, hooks) - def load_model_as_lora(self, model: ModelPatcher, clip: CLIP, ckpt_name: str, strength_model: float, strength_clip: float): - returned = self.create_hook(ckpt_name=ckpt_name, strength_model=strength_model, strength_clip=strength_clip) - return (model.clone(), clip.clone(), returned[0]) +class MaskableSDModelLoaderDEPR(io.ComfyNode, comfy_extras.nodes_hooks.CreateHookModelAsLora): + loaded_weights = None - -class MaskableSDModelLoaderModelOnlyDEPR(MaskableSDModelLoaderDEPR): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ), - "strength_model": ("FLOAT", {"default": 1.0, "min": -20.0, "max": 20.0, "step": 0.01}), - }, - } - - RETURN_TYPES = ("MODEL", "HOOKS") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/register lora hooks" - FUNCTION = "load_model_as_lora_model_only" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_RegisterModelAsLoraHook', display_name='Register Model as LoRA Hook πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/register lora hooks', inputs=[io.Model.Input('model'), io.Clip.Input('clip'), io.Combo.Input('ckpt_name', options=folder_paths.get_filename_list('checkpoints')), io.Float.Input('strength_model', default=1.0, max=20.0, min=-20.0, step=0.01), io.Float.Input('strength_clip', default=1.0, max=20.0, min=-20.0, step=0.01)], outputs=[io.Model.Output('MODEL'), io.Clip.Output('CLIP'), io.Custom('HOOKS').Output('HOOKS')], is_deprecated=True, is_experimental=True) - def load_model_as_lora_model_only(self, model: ModelPatcher, ckpt_name: str, strength_model: float): - model_lora, _, hooks = self.load_model_as_lora(model=model, clip=None, ckpt_name=ckpt_name, - strength_model=strength_model, strength_clip=0) - return (model_lora, hooks) -############################################### -############################################### -############################################### - - - -############################################### -### Set LoRA Hooks -############################################### -class SetModelLoraHookDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "conditioning": ("CONDITIONING",), - "lora_hook": ("HOOKS",), - }, - } - - RETURN_TYPES = ("CONDITIONING",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops" - FUNCTION = "attach_lora_hook" - DEPRECATED = True + def execute(cls, model: ModelPatcher, clip: CLIP, ckpt_name: str, strength_model: float, strength_clip: float): + returned = comfy_extras.nodes_hooks.CreateHookModelAsLora.create_hook( + cls, ckpt_name=ckpt_name, strength_model=strength_model, strength_clip=strength_clip + ) + return io.NodeOutput(model.clone(), clip.clone(), returned[0]) - def attach_lora_hook(self, conditioning, lora_hook: HookGroup): - return (comfy.hooks.set_hooks_for_conditioning(conditioning, lora_hook),) - +class MaskableSDModelLoaderModelOnlyDEPR(io.ComfyNode): -class SetClipLoraHookDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "clip": ("CLIP",), - "lora_hook": ("HOOKS",), - }, - } - - RETURN_TYPES = ("CLIP",) - RETURN_NAMES = ("hook_CLIP",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning" - FUNCTION = "apply_lora_hook" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_RegisterModelAsLoraHookModelOnly', display_name='Register Model as LoRA Hook (MO) πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/register lora hooks', inputs=[io.Model.Input('model'), io.Combo.Input('ckpt_name', options=folder_paths.get_filename_list('checkpoints')), io.Float.Input('strength_model', default=1.0, max=20.0, min=-20.0, step=0.01)], outputs=[io.Model.Output('MODEL'), io.Custom('HOOKS').Output('HOOKS')], is_deprecated=True, is_experimental=True) - def apply_lora_hook(self, clip: CLIP, lora_hook: HookGroup): - return comfy_extras.nodes_hooks.SetClipHooks.apply_hooks(self, clip, False, lora_hook) - - -class CombineLoraHooksDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "lora_hook_A": ("HOOKS",), - "lora_hook_B": ("HOOKS",), - }, - } - - RETURN_TYPES = ("HOOKS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/combine lora hooks" - FUNCTION = "combine_lora_hooks" - DEPRECATED = True + def execute(cls, model: ModelPatcher, ckpt_name: str, strength_model: float): + model_lora, _, hooks = MaskableSDModelLoaderDEPR.execute(model=model, clip=None, ckpt_name=ckpt_name, strength_model=strength_model, strength_clip=0).args + return io.NodeOutput(model_lora, hooks) - def combine_lora_hooks(self, lora_hook_A: HookGroup=None, lora_hook_B: HookGroup=None): +class SetModelLoraHookDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_AttachLoraHookToConditioning', display_name='Set Model LoRA Hook πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/single cond ops', inputs=[io.Conditioning.Input('conditioning'), io.Custom('HOOKS').Input('lora_hook')], outputs=[io.Conditioning.Output('CONDITIONING')], is_deprecated=True) + + @classmethod + def execute(cls, conditioning, lora_hook: HookGroup): + return io.NodeOutput(comfy.hooks.set_hooks_for_conditioning(conditioning, lora_hook)) + +class SetClipLoraHookDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_AttachLoraHookToCLIP', display_name='Set CLIP LoRA Hook πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning', inputs=[io.Clip.Input('clip'), io.Custom('HOOKS').Input('lora_hook')], outputs=[io.Clip.Output('hook_CLIP')], is_deprecated=True) + + @classmethod + def execute(cls, clip: CLIP, lora_hook: HookGroup): + return io.NodeOutput(*comfy_extras.nodes_hooks.SetClipHooks.apply_hooks(cls, clip, False, lora_hook)) + +class CombineLoraHooksDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_CombineLoraHooks', display_name='Combine LoRA Hooks [2] πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/combine lora hooks', inputs=[io.Custom('HOOKS').Input('lora_hook_A', optional=True), io.Custom('HOOKS').Input('lora_hook_B', optional=True)], outputs=[io.Custom('HOOKS').Output('HOOKS')], is_deprecated=True) + + @classmethod + def execute(cls, lora_hook_A: HookGroup=None, lora_hook_B: HookGroup=None): candidates = [lora_hook_A, lora_hook_B] - return (HookGroup.combine_all_hooks(candidates),) + return io.NodeOutput(HookGroup.combine_all_hooks(candidates)) +class CombineLoraHookFourOptionalDEPR(io.ComfyNode): -class CombineLoraHookFourOptionalDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "lora_hook_A": ("HOOKS",), - "lora_hook_B": ("HOOKS",), - "lora_hook_C": ("HOOKS",), - "lora_hook_D": ("HOOKS",), - }, - } + def define_schema(cls): + return io.Schema(node_id='ADE_CombineLoraHooksFour', display_name='Combine LoRA Hooks [4] πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/combine lora hooks', inputs=[io.Custom('HOOKS').Input('lora_hook_A', optional=True), io.Custom('HOOKS').Input('lora_hook_B', optional=True), io.Custom('HOOKS').Input('lora_hook_C', optional=True), io.Custom('HOOKS').Input('lora_hook_D', optional=True)], outputs=[io.Custom('HOOKS').Output('HOOKS')], is_deprecated=True) - RETURN_TYPES = ("HOOKS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/combine lora hooks" - FUNCTION = "combine_lora_hooks" - DEPRECATED = True - - def combine_lora_hooks(self, - lora_hook_A: HookGroup=None, lora_hook_B: HookGroup=None, - lora_hook_C: HookGroup=None, lora_hook_D: HookGroup=None,): + @classmethod + def execute(cls, lora_hook_A: HookGroup=None, lora_hook_B: HookGroup=None, lora_hook_C: HookGroup=None, lora_hook_D: HookGroup=None): candidates = [lora_hook_A, lora_hook_B, lora_hook_C, lora_hook_D] - return (HookGroup.combine_all_hooks(candidates),) + return io.NodeOutput(HookGroup.combine_all_hooks(candidates)) +class CombineLoraHookEightOptionalDEPR(io.ComfyNode): -class CombineLoraHookEightOptionalDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "lora_hook_A": ("HOOKS",), - "lora_hook_B": ("HOOKS",), - "lora_hook_C": ("HOOKS",), - "lora_hook_D": ("HOOKS",), - "lora_hook_E": ("HOOKS",), - "lora_hook_F": ("HOOKS",), - "lora_hook_G": ("HOOKS",), - "lora_hook_H": ("HOOKS",), - }, - } + def define_schema(cls): + return io.Schema(node_id='ADE_CombineLoraHooksEight', display_name='Combine LoRA Hooks [8] πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/combine lora hooks', inputs=[io.Custom('HOOKS').Input('lora_hook_A', optional=True), io.Custom('HOOKS').Input('lora_hook_B', optional=True), io.Custom('HOOKS').Input('lora_hook_C', optional=True), io.Custom('HOOKS').Input('lora_hook_D', optional=True), io.Custom('HOOKS').Input('lora_hook_E', optional=True), io.Custom('HOOKS').Input('lora_hook_F', optional=True), io.Custom('HOOKS').Input('lora_hook_G', optional=True), io.Custom('HOOKS').Input('lora_hook_H', optional=True)], outputs=[io.Custom('HOOKS').Output('HOOKS')], is_deprecated=True) - RETURN_TYPES = ("HOOKS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/conditioning/combine lora hooks" - FUNCTION = "combine_lora_hooks" - DEPRECATED = True - - def combine_lora_hooks(self, - lora_hook_A: HookGroup=None, lora_hook_B: HookGroup=None, - lora_hook_C: HookGroup=None, lora_hook_D: HookGroup=None, - lora_hook_E: HookGroup=None, lora_hook_F: HookGroup=None, - lora_hook_G: HookGroup=None, lora_hook_H: HookGroup=None): - candidates = [lora_hook_A, lora_hook_B, lora_hook_C, lora_hook_D, - lora_hook_E, lora_hook_F, lora_hook_G, lora_hook_H] - return (HookGroup.combine_all_hooks(candidates),) - -# NOTE: if at some point I add more Javascript stuff to this repo, there should be a combine node -# that dynamically increases the hooks available to plug in on the node -############################################### -############################################### -############################################### + @classmethod + def execute(cls, lora_hook_A: HookGroup=None, lora_hook_B: HookGroup=None, lora_hook_C: HookGroup=None, lora_hook_D: HookGroup=None, lora_hook_E: HookGroup=None, lora_hook_F: HookGroup=None, lora_hook_G: HookGroup=None, lora_hook_H: HookGroup=None): + candidates = [lora_hook_A, lora_hook_B, lora_hook_C, lora_hook_D, lora_hook_E, lora_hook_F, lora_hook_G, lora_hook_H] + return io.NodeOutput(HookGroup.combine_all_hooks(candidates)) diff --git a/animatediff/nodes_context.py b/animatediff/nodes_context.py index fd50dcd..3ac1722 100644 --- a/animatediff/nodes_context.py +++ b/animatediff/nodes_context.py @@ -1,442 +1,161 @@ +from comfy_api.latest import io from torch import Tensor from typing import Union - import comfy.samplers from comfy.model_patcher import ModelPatcher - -from .context import (ContextFuseMethod, ContextOptions, ContextOptionsGroup, ContextSchedules, - generate_context_visualization) +from .context import ContextFuseMethod, ContextOptions, ContextOptionsGroup, ContextSchedules, generate_context_visualization from .utils_model import BIGMAX, MAX_RESOLUTION +LENGTH_MAX = 128 +STRIDE_MAX = 32 +OVERLAP_MAX = 128 +class LoopedUniformContextOptionsNode(io.ComfyNode): -LENGTH_MAX = 128 # keep an eye on these max values; -STRIDE_MAX = 32 # would need to be updated -OVERLAP_MAX = 128 # if new motion modules come out - - -class LoopedUniformContextOptionsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "context_length": ("INT", {"default": 16, "min": 1, "max": LENGTH_MAX}), - "context_stride": ("INT", {"default": 1, "min": 1, "max": STRIDE_MAX}), - "context_overlap": ("INT", {"default": 4, "min": 0, "max": OVERLAP_MAX}), - "closed_loop": ("BOOLEAN", {"default": False},), - #"sync_context_to_pe": ("BOOLEAN", {"default": False},), - }, - "optional": { - "fuse_method": (ContextFuseMethod.LIST,), - "use_on_equal_length": ("BOOLEAN", {"default": False},), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "prev_context": ("CONTEXT_OPTIONS",), - "view_opts": ("VIEW_OPTS",), - } - } - - RETURN_TYPES = ("CONTEXT_OPTIONS",) - RETURN_NAMES = ("CONTEXT_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts" - FUNCTION = "create_options" + def define_schema(cls): + return io.Schema(node_id='ADE_LoopedUniformContextOptions', display_name='Context Optionsβ—†Looped Uniform πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts', inputs=[io.Int.Input('context_length', default=16, max=128, min=1), io.Int.Input('context_stride', default=1, max=32, min=1), io.Int.Input('context_overlap', default=4, max=128, min=0), io.Boolean.Input('closed_loop', default=False), io.Combo.Input('fuse_method', options=['pyramid', 'flat', 'overlap-linear', 'πŸ”¬delayed reverse sawtooth', 'πŸ”¬pyramid-sigma', 'πŸ”¬pyramid-sigma inverse', 'πŸ”¬gauss-sigma', 'πŸ”¬gauss-sigma inverse', 'πŸ”¬random'], optional=True), io.Boolean.Input('use_on_equal_length', optional=True, default=False), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), io.Custom('CONTEXT_OPTIONS').Input('prev_context', optional=True), io.Custom('VIEW_OPTS').Input('view_opts', optional=True)], outputs=[io.Custom('CONTEXT_OPTIONS').Output('CONTEXT_OPTS')]) - def create_options(self, context_length: int, context_stride: int, context_overlap: int, closed_loop: bool, - fuse_method: str=ContextFuseMethod.FLAT, use_on_equal_length=False, start_percent: float=0.0, guarantee_steps: int=1, - view_opts: ContextOptions=None, prev_context: ContextOptionsGroup=None): + @classmethod + def execute(cls, context_length: int, context_stride: int, context_overlap: int, closed_loop: bool, fuse_method: str=ContextFuseMethod.FLAT, use_on_equal_length=False, start_percent: float=0.0, guarantee_steps: int=1, view_opts: ContextOptions=None, prev_context: ContextOptionsGroup=None): if prev_context is None: prev_context = ContextOptionsGroup() prev_context = prev_context.clone() - - context_options = ContextOptions( - context_length=context_length, - context_stride=context_stride, - context_overlap=context_overlap, - context_schedule=ContextSchedules.UNIFORM_LOOPED, - closed_loop=closed_loop, - fuse_method=fuse_method, - use_on_equal_length=use_on_equal_length, - start_percent=start_percent, - guarantee_steps=guarantee_steps, - view_options=view_opts, - ) - #context_options.set_sync_context_to_pe(sync_context_to_pe) + context_options = ContextOptions(context_length=context_length, context_stride=context_stride, context_overlap=context_overlap, context_schedule=ContextSchedules.UNIFORM_LOOPED, closed_loop=closed_loop, fuse_method=fuse_method, use_on_equal_length=use_on_equal_length, start_percent=start_percent, guarantee_steps=guarantee_steps, view_options=view_opts) prev_context.add(context_options) - return (prev_context,) + return io.NodeOutput(prev_context) +class LegacyLoopedUniformContextOptionsNode(io.ComfyNode): -# This Legacy version exists to maintain compatiblity with old workflows -class LegacyLoopedUniformContextOptionsNode: - DEPRECATED = True @classmethod - def INPUT_TYPES(s): - return { - "required": { - "context_length": ("INT", {"default": 16, "min": 1, "max": LENGTH_MAX}), - "context_stride": ("INT", {"default": 1, "min": 1, "max": STRIDE_MAX}), - "context_overlap": ("INT", {"default": 4, "min": 0, "max": OVERLAP_MAX}), - "context_schedule": (ContextSchedules.LEGACY_UNIFORM_SCHEDULE_LIST,), - "closed_loop": ("BOOLEAN", {"default": False},), - #"sync_context_to_pe": ("BOOLEAN", {"default": False},), - }, - "optional": { - "fuse_method": (ContextFuseMethod.LIST, {"default": ContextFuseMethod.FLAT}), - "use_on_equal_length": ("BOOLEAN", {"default": False},), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "prev_context": ("CONTEXT_OPTIONS",), - "view_opts": ("VIEW_OPTS",), - } - } - - RETURN_TYPES = ("CONTEXT_OPTIONS",) - RETURN_NAMES = ("CONTEXT_OPTS",) - CATEGORY = "" # No Category, so will not appear in menu - FUNCTION = "create_options" + def define_schema(cls): + return io.Schema(node_id='ADE_AnimateDiffUniformContextOptions', display_name='Context Optionsβ—†Looped Uniform πŸŽ­πŸ…πŸ…“', category='', inputs=[io.Int.Input('context_length', default=16, max=128, min=1), io.Int.Input('context_stride', default=1, max=32, min=1), io.Int.Input('context_overlap', default=4, max=128, min=0), io.Combo.Input('context_schedule', options=['uniform']), io.Boolean.Input('closed_loop', default=False), io.Combo.Input('fuse_method', options=['pyramid', 'flat', 'overlap-linear', 'πŸ”¬delayed reverse sawtooth', 'πŸ”¬pyramid-sigma', 'πŸ”¬pyramid-sigma inverse', 'πŸ”¬gauss-sigma', 'πŸ”¬gauss-sigma inverse', 'πŸ”¬random'], optional=True, default='flat'), io.Boolean.Input('use_on_equal_length', optional=True, default=False), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), io.Custom('CONTEXT_OPTIONS').Input('prev_context', optional=True), io.Custom('VIEW_OPTS').Input('view_opts', optional=True)], outputs=[io.Custom('CONTEXT_OPTIONS').Output('CONTEXT_OPTS')], is_deprecated=True) - def create_options(self, fuse_method: str=ContextFuseMethod.FLAT, context_schedule: str=None, **kwargs): - return LoopedUniformContextOptionsNode.create_options(self, fuse_method=fuse_method, **kwargs) - - -class StandardUniformContextOptionsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "context_length": ("INT", {"default": 16, "min": 1, "max": LENGTH_MAX}), - "context_stride": ("INT", {"default": 1, "min": 1, "max": STRIDE_MAX}), - "context_overlap": ("INT", {"default": 4, "min": 0, "max": OVERLAP_MAX}), - }, - "optional": { - "fuse_method": (ContextFuseMethod.LIST,), - "use_on_equal_length": ("BOOLEAN", {"default": False},), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "prev_context": ("CONTEXT_OPTIONS",), - "view_opts": ("VIEW_OPTS",), - } - } - - RETURN_TYPES = ("CONTEXT_OPTIONS",) - RETURN_NAMES = ("CONTEXT_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts" - FUNCTION = "create_options" + def execute(cls, fuse_method: str=ContextFuseMethod.FLAT, context_schedule: str=None, **kwargs): + return LoopedUniformContextOptionsNode.execute(fuse_method=fuse_method, **kwargs) - def create_options(self, context_length: int, context_stride: int, context_overlap: int, - fuse_method: str=ContextFuseMethod.PYRAMID, use_on_equal_length=False, start_percent: float=0.0, guarantee_steps: int=1, - view_opts: ContextOptions=None, prev_context: ContextOptionsGroup=None): +class StandardUniformContextOptionsNode(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_StandardUniformContextOptions', display_name='Context Optionsβ—†Standard Uniform πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts', inputs=[io.Int.Input('context_length', default=16, max=128, min=1), io.Int.Input('context_stride', default=1, max=32, min=1), io.Int.Input('context_overlap', default=4, max=128, min=0), io.Combo.Input('fuse_method', options=['pyramid', 'flat', 'overlap-linear', 'πŸ”¬delayed reverse sawtooth', 'πŸ”¬pyramid-sigma', 'πŸ”¬pyramid-sigma inverse', 'πŸ”¬gauss-sigma', 'πŸ”¬gauss-sigma inverse', 'πŸ”¬random'], optional=True), io.Boolean.Input('use_on_equal_length', optional=True, default=False), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), io.Custom('CONTEXT_OPTIONS').Input('prev_context', optional=True), io.Custom('VIEW_OPTS').Input('view_opts', optional=True)], outputs=[io.Custom('CONTEXT_OPTIONS').Output('CONTEXT_OPTS')]) + + @classmethod + def execute(cls, context_length: int, context_stride: int, context_overlap: int, fuse_method: str=ContextFuseMethod.PYRAMID, use_on_equal_length=False, start_percent: float=0.0, guarantee_steps: int=1, view_opts: ContextOptions=None, prev_context: ContextOptionsGroup=None): if prev_context is None: prev_context = ContextOptionsGroup() prev_context = prev_context.clone() - - context_options = ContextOptions( - context_length=context_length, - context_stride=context_stride, - context_overlap=context_overlap, - context_schedule=ContextSchedules.UNIFORM_STANDARD, - closed_loop=False, - fuse_method=fuse_method, - use_on_equal_length=use_on_equal_length, - start_percent=start_percent, - guarantee_steps=guarantee_steps, - view_options=view_opts, - ) + context_options = ContextOptions(context_length=context_length, context_stride=context_stride, context_overlap=context_overlap, context_schedule=ContextSchedules.UNIFORM_STANDARD, closed_loop=False, fuse_method=fuse_method, use_on_equal_length=use_on_equal_length, start_percent=start_percent, guarantee_steps=guarantee_steps, view_options=view_opts) prev_context.add(context_options) - return (prev_context,) + return io.NodeOutput(prev_context) +class StandardStaticContextOptionsNode(io.ComfyNode): -class StandardStaticContextOptionsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "context_length": ("INT", {"default": 16, "min": 1, "max": LENGTH_MAX}), - "context_overlap": ("INT", {"default": 4, "min": 0, "max": OVERLAP_MAX}), - }, - "optional": { - "fuse_method": (ContextFuseMethod.LIST_STATIC,), - "use_on_equal_length": ("BOOLEAN", {"default": False},), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "prev_context": ("CONTEXT_OPTIONS",), - "view_opts": ("VIEW_OPTS",), - } - } - - RETURN_TYPES = ("CONTEXT_OPTIONS",) - RETURN_NAMES = ("CONTEXT_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts" - FUNCTION = "create_options" + def define_schema(cls): + return io.Schema(node_id='ADE_StandardStaticContextOptions', display_name='Context Optionsβ—†Standard Static πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts', inputs=[io.Int.Input('context_length', default=16, max=128, min=1), io.Int.Input('context_overlap', default=4, max=128, min=0), io.Combo.Input('fuse_method', options=['pyramid', 'relative', 'flat', 'overlap-linear', 'πŸ”¬delayed reverse sawtooth', 'πŸ”¬pyramid-sigma', 'πŸ”¬pyramid-sigma inverse', 'πŸ”¬gauss-sigma', 'πŸ”¬gauss-sigma inverse', 'πŸ”¬random'], optional=True), io.Boolean.Input('use_on_equal_length', optional=True, default=False), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), io.Custom('CONTEXT_OPTIONS').Input('prev_context', optional=True), io.Custom('VIEW_OPTS').Input('view_opts', optional=True)], outputs=[io.Custom('CONTEXT_OPTIONS').Output('CONTEXT_OPTS')]) - def create_options(self, context_length: int, context_overlap: int, - fuse_method: str=ContextFuseMethod.PYRAMID, use_on_equal_length=False, start_percent: float=0.0, guarantee_steps: int=1, - view_opts: ContextOptions=None, prev_context: ContextOptionsGroup=None): + @classmethod + def execute(cls, context_length: int, context_overlap: int, fuse_method: str=ContextFuseMethod.PYRAMID, use_on_equal_length=False, start_percent: float=0.0, guarantee_steps: int=1, view_opts: ContextOptions=None, prev_context: ContextOptionsGroup=None): if prev_context is None: prev_context = ContextOptionsGroup() prev_context = prev_context.clone() - - context_options = ContextOptions( - context_length=context_length, - context_stride=None, - context_overlap=context_overlap, - context_schedule=ContextSchedules.STATIC_STANDARD, - fuse_method=fuse_method, - use_on_equal_length=use_on_equal_length, - start_percent=start_percent, - guarantee_steps=guarantee_steps, - view_options=view_opts, - ) + context_options = ContextOptions(context_length=context_length, context_stride=None, context_overlap=context_overlap, context_schedule=ContextSchedules.STATIC_STANDARD, fuse_method=fuse_method, use_on_equal_length=use_on_equal_length, start_percent=start_percent, guarantee_steps=guarantee_steps, view_options=view_opts) prev_context.add(context_options) - return (prev_context,) + return io.NodeOutput(prev_context) +class BatchedContextOptionsNode(io.ComfyNode): -class BatchedContextOptionsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "context_length": ("INT", {"default": 16, "min": 1, "max": LENGTH_MAX}), - }, - "optional": { - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "prev_context": ("CONTEXT_OPTIONS",), - } - } - - RETURN_TYPES = ("CONTEXT_OPTIONS",) - RETURN_NAMES = ("CONTEXT_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts" - FUNCTION = "create_options" + def define_schema(cls): + return io.Schema(node_id='ADE_BatchedContextOptions', display_name='Context Optionsβ—†Batched [Non-AD] πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts', inputs=[io.Int.Input('context_length', default=16, max=128, min=1), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), io.Custom('CONTEXT_OPTIONS').Input('prev_context', optional=True)], outputs=[io.Custom('CONTEXT_OPTIONS').Output('CONTEXT_OPTS')]) - def create_options(self, context_length: int, start_percent: float=0.0, guarantee_steps: int=1, - prev_context: ContextOptionsGroup=None): + @classmethod + def execute(cls, context_length: int, start_percent: float=0.0, guarantee_steps: int=1, prev_context: ContextOptionsGroup=None): if prev_context is None: prev_context = ContextOptionsGroup() prev_context = prev_context.clone() - - context_options = ContextOptions( - context_length=context_length, - context_overlap=0, - context_schedule=ContextSchedules.BATCHED, - start_percent=start_percent, - guarantee_steps=guarantee_steps, - ) + context_options = ContextOptions(context_length=context_length, context_overlap=0, context_schedule=ContextSchedules.BATCHED, start_percent=start_percent, guarantee_steps=guarantee_steps) prev_context.add(context_options) - return (prev_context,) + return io.NodeOutput(prev_context) +class ViewAsContextOptionsNode(io.ComfyNode): -class ViewAsContextOptionsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "view_opts_req": ("VIEW_OPTS",), - }, - "optional": { - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "prev_context": ("CONTEXT_OPTIONS",), - }, - } - - RETURN_TYPES = ("CONTEXT_OPTIONS",) - RETURN_NAMES = ("CONTEXT_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts" - FUNCTION = "create_options" + def define_schema(cls): + return io.Schema(node_id='ADE_ViewsOnlyContextOptions', display_name='Context Optionsβ—†Views Only [VRAMβ‡ˆ] πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts', inputs=[io.Custom('VIEW_OPTS').Input('view_opts_req'), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), io.Custom('CONTEXT_OPTIONS').Input('prev_context', optional=True)], outputs=[io.Custom('CONTEXT_OPTIONS').Output('CONTEXT_OPTS')]) - def create_options(self, view_opts_req: ContextOptions, start_percent: float=0.0, guarantee_steps: int=1, - prev_context: ContextOptionsGroup=None): + @classmethod + def execute(cls, view_opts_req: ContextOptions, start_percent: float=0.0, guarantee_steps: int=1, prev_context: ContextOptionsGroup=None): if prev_context is None: prev_context = ContextOptionsGroup() prev_context = prev_context.clone() - context_options = ContextOptions( - context_schedule=ContextSchedules.VIEW_AS_CONTEXT, - start_percent=start_percent, - guarantee_steps=guarantee_steps, - view_options=view_opts_req, - use_on_equal_length=True - ) + context_options = ContextOptions(context_schedule=ContextSchedules.VIEW_AS_CONTEXT, start_percent=start_percent, guarantee_steps=guarantee_steps, view_options=view_opts_req, use_on_equal_length=True) prev_context.add(context_options) - return (prev_context,) + return io.NodeOutput(prev_context) +class StandardStaticViewOptionsNode(io.ComfyNode): -######################### -# View Options -class StandardStaticViewOptionsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "view_length": ("INT", {"default": 16, "min": 1, "max": LENGTH_MAX}), - "view_overlap": ("INT", {"default": 4, "min": 0, "max": OVERLAP_MAX}), - }, - "optional": { - "fuse_method": (ContextFuseMethod.LIST,), - } - } - - RETURN_TYPES = ("VIEW_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/view opts" - FUNCTION = "create_options" + def define_schema(cls): + return io.Schema(node_id='ADE_StandardStaticViewOptions', display_name='View Optionsβ—†Standard Static πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/view opts', inputs=[io.Int.Input('view_length', default=16, max=128, min=1), io.Int.Input('view_overlap', default=4, max=128, min=0), io.Combo.Input('fuse_method', options=['pyramid', 'flat', 'overlap-linear', 'πŸ”¬delayed reverse sawtooth', 'πŸ”¬pyramid-sigma', 'πŸ”¬pyramid-sigma inverse', 'πŸ”¬gauss-sigma', 'πŸ”¬gauss-sigma inverse', 'πŸ”¬random'], optional=True)], outputs=[io.Custom('VIEW_OPTS').Output('VIEW_OPTS')]) - def create_options(self, view_length: int, view_overlap: int, - fuse_method: str=ContextFuseMethod.FLAT,): - view_options = ContextOptions( - context_length=view_length, - context_stride=None, - context_overlap=view_overlap, - context_schedule=ContextSchedules.STATIC_STANDARD, - fuse_method=fuse_method, - ) - return (view_options,) - - -class StandardUniformViewOptionsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "view_length": ("INT", {"default": 16, "min": 1, "max": LENGTH_MAX}), - "view_stride": ("INT", {"default": 1, "min": 1, "max": STRIDE_MAX}), - "view_overlap": ("INT", {"default": 4, "min": 0, "max": OVERLAP_MAX}), - }, - "optional": { - "fuse_method": (ContextFuseMethod.LIST,), - } - } - - RETURN_TYPES = ("VIEW_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/view opts" - FUNCTION = "create_options" + def execute(cls, view_length: int, view_overlap: int, fuse_method: str=ContextFuseMethod.FLAT): + view_options = ContextOptions(context_length=view_length, context_stride=None, context_overlap=view_overlap, context_schedule=ContextSchedules.STATIC_STANDARD, fuse_method=fuse_method) + return io.NodeOutput(view_options) - def create_options(self, view_length: int, view_overlap: int, view_stride: int, - fuse_method: str=ContextFuseMethod.PYRAMID,): - view_options = ContextOptions( - context_length=view_length, - context_stride=view_stride, - context_overlap=view_overlap, - context_schedule=ContextSchedules.UNIFORM_STANDARD, - fuse_method=fuse_method, - ) - return (view_options,) +class StandardUniformViewOptionsNode(io.ComfyNode): - -class LoopedUniformViewOptionsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "view_length": ("INT", {"default": 16, "min": 1, "max": LENGTH_MAX}), - "view_stride": ("INT", {"default": 1, "min": 1, "max": STRIDE_MAX}), - "view_overlap": ("INT", {"default": 4, "min": 0, "max": OVERLAP_MAX}), - "closed_loop": ("BOOLEAN", {"default": False},), - }, - "optional": { - "fuse_method": (ContextFuseMethod.LIST,), - "use_on_equal_length": ("BOOLEAN", {"default": False},), - } - } - - RETURN_TYPES = ("VIEW_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/view opts" - FUNCTION = "create_options" + def define_schema(cls): + return io.Schema(node_id='ADE_StandardUniformViewOptions', display_name='View Optionsβ—†Standard Uniform πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/view opts', inputs=[io.Int.Input('view_length', default=16, max=128, min=1), io.Int.Input('view_stride', default=1, max=32, min=1), io.Int.Input('view_overlap', default=4, max=128, min=0), io.Combo.Input('fuse_method', options=['pyramid', 'flat', 'overlap-linear', 'πŸ”¬delayed reverse sawtooth', 'πŸ”¬pyramid-sigma', 'πŸ”¬pyramid-sigma inverse', 'πŸ”¬gauss-sigma', 'πŸ”¬gauss-sigma inverse', 'πŸ”¬random'], optional=True)], outputs=[io.Custom('VIEW_OPTS').Output('VIEW_OPTS')]) - def create_options(self, view_length: int, view_overlap: int, view_stride: int, closed_loop: bool, - fuse_method: str=ContextFuseMethod.PYRAMID, use_on_equal_length=False): - view_options = ContextOptions( - context_length=view_length, - context_stride=view_stride, - context_overlap=view_overlap, - context_schedule=ContextSchedules.UNIFORM_LOOPED, - closed_loop=closed_loop, - fuse_method=fuse_method, - use_on_equal_length=use_on_equal_length, - ) - return (view_options,) - - -class VisualizeContextOptionsKAdv: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ), - "scheduler": (comfy.samplers.KSampler.SCHEDULERS, ), - }, - "optional": { - "context_opts": ("CONTEXT_OPTIONS",), - "visual_width": ("INT", {"min": 32, "max": MAX_RESOLUTION, "default": 1440}), - "latents_length": ("INT", {"min": 1, "max": BIGMAX, "default": 32}), - "steps": ("INT", {"min": 0, "max": BIGMAX, "default": 20}), - "start_step": ("INT", {"min": 0, "max": BIGMAX, "default": 0}), - "end_step": ("INT", {"min": 1, "max": BIGMAX, "default": 20}), - } - } - - RETURN_TYPES = ("IMAGE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/visualize" - FUNCTION = "visualize" + def execute(cls, view_length: int, view_overlap: int, view_stride: int, fuse_method: str=ContextFuseMethod.PYRAMID): + view_options = ContextOptions(context_length=view_length, context_stride=view_stride, context_overlap=view_overlap, context_schedule=ContextSchedules.UNIFORM_STANDARD, fuse_method=fuse_method) + return io.NodeOutput(view_options) - def visualize(self, model: ModelPatcher, sampler_name: str, scheduler: str, context_opts: ContextOptionsGroup=None, - visual_width=1440, latents_length=32, steps=20, start_step=0, end_step=20): - images = generate_context_visualization(model=model, context_opts=context_opts, width=visual_width, video_length=latents_length, - sampler_name=sampler_name, scheduler=scheduler, - steps=steps, start_step=start_step, end_step=end_step) - return (images,) +class LoopedUniformViewOptionsNode(io.ComfyNode): - -class VisualizeContextOptionsK: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "sampler_name": (comfy.samplers.KSampler.SAMPLERS, ), - "scheduler": (comfy.samplers.KSampler.SCHEDULERS, ), - }, - "optional": { - "context_opts": ("CONTEXT_OPTIONS",), - "visual_width": ("INT", {"min": 32, "max": MAX_RESOLUTION, "default": 1440}), - "latents_length": ("INT", {"min": 1, "max": BIGMAX, "default": 32}), - "steps": ("INT", {"min": 0, "max": BIGMAX, "default": 20}), - "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - } - } - - RETURN_TYPES = ("IMAGE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/visualize" - FUNCTION = "visualize" + def define_schema(cls): + return io.Schema(node_id='ADE_LoopedUniformViewOptions', display_name='View Optionsβ—†Looped Uniform πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/view opts', inputs=[io.Int.Input('view_length', default=16, max=128, min=1), io.Int.Input('view_stride', default=1, max=32, min=1), io.Int.Input('view_overlap', default=4, max=128, min=0), io.Boolean.Input('closed_loop', default=False), io.Combo.Input('fuse_method', options=['pyramid', 'flat', 'overlap-linear', 'πŸ”¬delayed reverse sawtooth', 'πŸ”¬pyramid-sigma', 'πŸ”¬pyramid-sigma inverse', 'πŸ”¬gauss-sigma', 'πŸ”¬gauss-sigma inverse', 'πŸ”¬random'], optional=True), io.Boolean.Input('use_on_equal_length', optional=True, default=False)], outputs=[io.Custom('VIEW_OPTS').Output('VIEW_OPTS')]) - def visualize(self, model: ModelPatcher, sampler_name: str, scheduler: str, context_opts: ContextOptionsGroup=None, - visual_width=1440, latents_length=32, steps=20, denoise=1.0): - images = generate_context_visualization(model=model, context_opts=context_opts, width=visual_width, video_length=latents_length, - sampler_name=sampler_name, scheduler=scheduler, - steps=steps, denoise=denoise) - return (images,) - - -class VisualizeContextOptionsSCustom: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "sigmas": ("SIGMAS", ), - }, - "optional": { - "context_opts": ("CONTEXT_OPTIONS",), - "visual_width": ("INT", {"min": 32, "max": MAX_RESOLUTION, "default": 1440}), - "latents_length": ("INT", {"min": 1, "max": BIGMAX, "default": 32}), - } - } - - RETURN_TYPES = ("IMAGE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/visualize" - FUNCTION = "visualize" + def execute(cls, view_length: int, view_overlap: int, view_stride: int, closed_loop: bool, fuse_method: str=ContextFuseMethod.PYRAMID, use_on_equal_length=False): + view_options = ContextOptions(context_length=view_length, context_stride=view_stride, context_overlap=view_overlap, context_schedule=ContextSchedules.UNIFORM_LOOPED, closed_loop=closed_loop, fuse_method=fuse_method, use_on_equal_length=use_on_equal_length) + return io.NodeOutput(view_options) - def visualize(self, model: ModelPatcher, sigmas, context_opts: ContextOptionsGroup=None, - visual_width=1440, latents_length=32): - images = generate_context_visualization(model=model, context_opts=context_opts, width=visual_width, video_length=latents_length, - sigmas=sigmas) - return (images,) +class VisualizeContextOptionsKAdv(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_VisualizeContextOptionsKAdv', display_name='Visualize Context Options (K.Adv.) πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/visualize', inputs=[io.Model.Input('model'), io.Combo.Input('sampler_name', options=comfy.samplers.KSampler.SAMPLERS), io.Combo.Input('scheduler', options=comfy.samplers.KSampler.SCHEDULERS), io.Custom('CONTEXT_OPTIONS').Input('context_opts', optional=True), io.Int.Input('visual_width', optional=True, default=1440, max=16384, min=32), io.Int.Input('latents_length', optional=True, default=32, max=9007199254740991, min=1), io.Int.Input('steps', optional=True, default=20, max=9007199254740991, min=0), io.Int.Input('start_step', optional=True, default=0, max=9007199254740991, min=0), io.Int.Input('end_step', optional=True, default=20, max=9007199254740991, min=1)], outputs=[io.Image.Output('IMAGE')]) + + @classmethod + def execute(cls, model: ModelPatcher, sampler_name: str, scheduler: str, context_opts: ContextOptionsGroup=None, visual_width=1440, latents_length=32, steps=20, start_step=0, end_step=20): + images = generate_context_visualization(model=model, context_opts=context_opts, width=visual_width, video_length=latents_length, sampler_name=sampler_name, scheduler=scheduler, steps=steps, start_step=start_step, end_step=end_step) + return io.NodeOutput(images) + +class VisualizeContextOptionsK(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_VisualizeContextOptionsK', display_name='Visualize Context Options (K.) πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/visualize', inputs=[io.Model.Input('model'), io.Combo.Input('sampler_name', options=comfy.samplers.KSampler.SAMPLERS), io.Combo.Input('scheduler', options=comfy.samplers.KSampler.SCHEDULERS), io.Custom('CONTEXT_OPTIONS').Input('context_opts', optional=True), io.Int.Input('visual_width', optional=True, default=1440, max=16384, min=32), io.Int.Input('latents_length', optional=True, default=32, max=9007199254740991, min=1), io.Int.Input('steps', optional=True, default=20, max=9007199254740991, min=0), io.Float.Input('denoise', optional=True, default=1.0, max=1.0, min=0.0, step=0.01)], outputs=[io.Image.Output('IMAGE')]) + + @classmethod + def execute(cls, model: ModelPatcher, sampler_name: str, scheduler: str, context_opts: ContextOptionsGroup=None, visual_width=1440, latents_length=32, steps=20, denoise=1.0): + images = generate_context_visualization(model=model, context_opts=context_opts, width=visual_width, video_length=latents_length, sampler_name=sampler_name, scheduler=scheduler, steps=steps, denoise=denoise) + return io.NodeOutput(images) + +class VisualizeContextOptionsSCustom(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_VisualizeContextOptionsSCustom', display_name='Visualize Context Options (S.Cus.) πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/visualize', inputs=[io.Model.Input('model'), io.Sigmas.Input('sigmas'), io.Custom('CONTEXT_OPTIONS').Input('context_opts', optional=True), io.Int.Input('visual_width', optional=True, default=1440, max=16384, min=32), io.Int.Input('latents_length', optional=True, default=32, max=9007199254740991, min=1)], outputs=[io.Image.Output('IMAGE')]) + + @classmethod + def execute(cls, model: ModelPatcher, sigmas, context_opts: ContextOptionsGroup=None, visual_width=1440, latents_length=32): + images = generate_context_visualization(model=model, context_opts=context_opts, width=visual_width, video_length=latents_length, sigmas=sigmas) + return io.NodeOutput(images) diff --git a/animatediff/nodes_context_extras.py b/animatediff/nodes_context_extras.py index 7aa484e..6b9ad71 100644 --- a/animatediff/nodes_context_extras.py +++ b/animatediff/nodes_context_extras.py @@ -1,182 +1,89 @@ +from comfy_api.latest import io from torch import Tensor from typing import Union from collections.abc import Iterable - -from .context import (ContextOptionsGroup) -from .context_extras import (ContextExtrasGroup, - ContextRef, ContextRefTune, ContextRefMode, ContextRefKeyframeGroup, ContextRefKeyframe, - NaiveReuse, NaiveReuseKeyframe, NaiveReuseKeyframeGroup) +from .context import ContextOptionsGroup +from .context_extras import ContextExtrasGroup, ContextRef, ContextRefTune, ContextRefMode, ContextRefKeyframeGroup, ContextRefKeyframe, NaiveReuse, NaiveReuseKeyframe, NaiveReuseKeyframeGroup from .utils_model import BIGMAX, InterpolationMethod from .utils_scheduling import convert_str_to_indexes from .logger import logger +class SetContextExtrasOnContextOptions(io.ComfyNode): -class SetContextExtrasOnContextOptions: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "context_opts": ("CONTEXT_OPTIONS",), - }, - "optional": { - "context_extras": ("CONTEXT_EXTRAS",), - }, - } - - RETURN_TYPES = ("CONTEXT_OPTIONS",) - RETURN_NAMES = ("CONTEXT_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras" - FUNCTION = "set_context_extras" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_Set', display_name='Set Context Extras πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras', inputs=[io.Custom('CONTEXT_OPTIONS').Input('context_opts'), io.Custom('CONTEXT_EXTRAS').Input('context_extras', optional=True)], outputs=[io.Custom('CONTEXT_OPTIONS').Output('CONTEXT_OPTS')]) - def set_context_extras(self, context_opts: ContextOptionsGroup, context_extras: ContextExtrasGroup=None): + @classmethod + def execute(cls, context_opts: ContextOptionsGroup, context_extras: ContextExtrasGroup=None): context_opts = context_opts.clone() if context_extras is not None: context_opts.extras = context_extras.clone() - return (context_opts,) + return io.NodeOutput(context_opts) +class ContextExtras_NaiveReuse(io.ComfyNode): -######################################### -# NaiveReuse -class ContextExtras_NaiveReuse: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "prev_extras": ("CONTEXT_EXTRAS",), - "strength_multival": ("MULTIVAL",), - "naivereuse_kf": ("NAIVEREUSE_KEYFRAME",), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "end_percent": ("FLOAT", {"default": 0.15, "min": 0.0, "max": 1.0, "step": 0.001}), - "weighted_mean": ("FLOAT", {"default": 0.95, "min": 0.0, "max": 1.0, "step": 0.001}), - }, - } - - RETURN_TYPES = ("CONTEXT_EXTRAS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras" - FUNCTION = "create_context_extra" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_NaiveReuse', display_name='Context Extrasβ—†NaiveReuse πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras', inputs=[io.Custom('CONTEXT_EXTRAS').Input('prev_extras', optional=True), io.Custom('MULTIVAL').Input('strength_multival', optional=True), io.Custom('NAIVEREUSE_KEYFRAME').Input('naivereuse_kf', optional=True), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Float.Input('end_percent', optional=True, default=0.15, max=1.0, min=0.0, step=0.001), io.Float.Input('weighted_mean', optional=True, default=0.95, max=1.0, min=0.0, step=0.001)], outputs=[io.Custom('CONTEXT_EXTRAS').Output('CONTEXT_EXTRAS')]) - def create_context_extra(self, start_percent=0.0, end_percent=0.1, weighted_mean=0.95, strength_multival: Union[float, Tensor]=None, - naivereuse_kf: NaiveReuseKeyframeGroup=None, prev_extras: ContextExtrasGroup=None): + @classmethod + def execute(cls, start_percent=0.0, end_percent=0.1, weighted_mean=0.95, strength_multival: Union[float, Tensor]=None, naivereuse_kf: NaiveReuseKeyframeGroup=None, prev_extras: ContextExtrasGroup=None): if prev_extras is None: prev_extras = prev_extras = ContextExtrasGroup() prev_extras = prev_extras.clone() - # create extra - naive_reuse = NaiveReuse(start_percent=start_percent, end_percent=end_percent, weighted_mean=weighted_mean, multival_opt=strength_multival, - naivereuse_kf=naivereuse_kf) + naive_reuse = NaiveReuse(start_percent=start_percent, end_percent=end_percent, weighted_mean=weighted_mean, multival_opt=strength_multival, naivereuse_kf=naivereuse_kf) prev_extras.add(naive_reuse) - return (prev_extras,) + return io.NodeOutput(prev_extras) +class NaiveReuse_KeyframeMultivalNode(io.ComfyNode): -class NaiveReuse_KeyframeMultivalNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "prev_kf": ("NAIVEREUSE_KEYFRAME",), - "mult_multival": ("MULTIVAL",), - "mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "inherit_missing": ("BOOLEAN", {"default": True}, ), - }, - } - - RETURN_TYPES = ("NAIVEREUSE_KEYFRAME",) - RETURN_NAMES = ("NAIVEREUSE_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/naivereuse" - FUNCTION = "create_keyframe" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_NaiveReuse_Keyframe', display_name='NaiveReuse Keyframe πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/naivereuse', inputs=[io.Custom('NAIVEREUSE_KEYFRAME').Input('prev_kf', optional=True), io.Custom('MULTIVAL').Input('mult_multival', optional=True), io.Float.Input('mult', optional=True, default=1.0, max=1.0, min=0.0, step=0.001), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), io.Boolean.Input('inherit_missing', optional=True, default=True)], outputs=[io.Custom('NAIVEREUSE_KEYFRAME').Output('NAIVEREUSE_KF')]) - def create_keyframe(self, prev_kf=None, mult=1.0, mult_multival=None, - start_percent=0.0, guarantee_steps=1, inherit_missing=True): + @classmethod + def execute(cls, prev_kf=None, mult=1.0, mult_multival=None, start_percent=0.0, guarantee_steps=1, inherit_missing=True): if prev_kf is None: prev_kf = NaiveReuseKeyframeGroup() prev_kf = prev_kf.clone() - kf = NaiveReuseKeyframe(mult=mult, mult_multival=mult_multival, - start_percent=start_percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing) + kf = NaiveReuseKeyframe(mult=mult, mult_multival=mult_multival, start_percent=start_percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing) prev_kf.add(kf) - return (prev_kf,) + return io.NodeOutput(prev_kf) +class NaiveReuse_KeyframeInterpolationNode(io.ComfyNode): -class NaiveReuse_KeyframeInterpolationNode: @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}), - "mult_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "mult_end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "interpolation": (InterpolationMethod._LIST, ), - "intervals": ("INT", {"default": 50, "min": 2, "max": 100, "step": 1}), - "inherit_missing": ("BOOLEAN", {"default": True}), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_kf": ("NAIVEREUSE_KEYFRAME",), - "mult_multival": ("MULTIVAL",), - }, - } - - RETURN_TYPES = ("NAIVEREUSE_KEYFRAME",) - RETURN_NAMES = ("NAIVEREUSE_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/naivereuse" - FUNCTION = "create_keyframe" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_NaiveReuse_KeyframeInterpolation', display_name='NaiveReuse Keyframes Interp. πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/naivereuse', 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('mult_start', default=1.0, max=1.0, min=0.0, step=0.001), io.Float.Input('mult_end', default=1.0, max=1.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.Boolean.Input('inherit_missing', default=True), io.Boolean.Input('print_keyframes', default=False), io.Custom('NAIVEREUSE_KEYFRAME').Input('prev_kf', optional=True), io.Custom('MULTIVAL').Input('mult_multival', optional=True)], outputs=[io.Custom('NAIVEREUSE_KEYFRAME').Output('NAIVEREUSE_KF')]) - def create_keyframe(self, - start_percent: float, end_percent: float, - mult_start: float, mult_end: float, interpolation: str, intervals: int, - inherit_missing=True, prev_kf: NaiveReuseKeyframeGroup=None, - mult_multival=None, print_keyframes=False): + @classmethod + def execute(cls, start_percent: float, end_percent: float, mult_start: float, mult_end: float, interpolation: str, intervals: int, inherit_missing=True, prev_kf: NaiveReuseKeyframeGroup=None, mult_multival=None, print_keyframes=False): if prev_kf is None: prev_kf = NaiveReuseKeyframeGroup() prev_kf = prev_kf.clone() prev_kf = prev_kf.clone() percents = InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=intervals, method=InterpolationMethod.LINEAR) mults = InterpolationMethod.get_weights(num_from=mult_start, num_to=mult_end, length=intervals, method=interpolation) - is_first = True for percent, mult in zip(percents, mults): guarantee_steps = 0 if is_first: guarantee_steps = 1 is_first = False - prev_kf.add(NaiveReuseKeyframe(mult=mult, mult_multival=mult_multival, - start_percent=percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing)) + prev_kf.add(NaiveReuseKeyframe(mult=mult, mult_multival=mult_multival, start_percent=percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing)) if print_keyframes: - logger.info(f"NaiveReuseKeyframe - start_percent:{percent} = {mult}") - return (prev_kf,) + logger.info(f'NaiveReuseKeyframe - start_percent:{percent} = {mult}') + return io.NodeOutput(prev_kf) +class NaiveReuse_KeyframeFromListNode(io.ComfyNode): -class NaiveReuse_KeyframeFromListNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "mults_float": ("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}), - "inherit_missing": ("BOOLEAN", {"default": True}), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_kf": ("NAIVEREUSE_KEYFRAME",), - "mult_multival": ("MULTIVAL",), - }, - } - - RETURN_TYPES = ("NAIVEREUSE_KEYFRAME",) - RETURN_NAMES = ("NAIVEREUSE_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/naivereuse" - FUNCTION = "create_keyframe" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_NaiveReuse_KeyframeFromList', display_name='NaiveReuse Keyframes From List πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/naivereuse', inputs=[io.Float.Input('mults_float', 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.Boolean.Input('inherit_missing', default=True), io.Boolean.Input('print_keyframes', default=False), io.Custom('NAIVEREUSE_KEYFRAME').Input('prev_kf', optional=True), io.Custom('MULTIVAL').Input('mult_multival', optional=True)], outputs=[io.Custom('NAIVEREUSE_KEYFRAME').Output('NAIVEREUSE_KF')]) - def create_keyframe(self, mults_float: Union[float, list[float]], - start_percent: float, end_percent: float, - inherit_missing=True, prev_kf: NaiveReuseKeyframeGroup=None, - mult_multival=None, print_keyframes=False): + @classmethod + def execute(cls, mults_float: Union[float, list[float]], start_percent: float, end_percent: float, inherit_missing=True, prev_kf: NaiveReuseKeyframeGroup=None, mult_multival=None, print_keyframes=False): if prev_kf is None: prev_kf = NaiveReuseKeyframeGroup() prev_kf = prev_kf.clone() @@ -185,180 +92,85 @@ class NaiveReuse_KeyframeFromListNode: elif isinstance(mults_float, Iterable): pass else: - raise Exception(f"strengths_float must be either an interable input or a float, but was {type(mults_float).__repr__}.") + raise Exception(f'strengths_float must be either an interable input or a float, but was {type(mults_float).__repr__}.') percents = InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=len(mults_float), method=InterpolationMethod.LINEAR) - is_first = True for percent, mult in zip(percents, mults_float): guarantee_steps = 0 if is_first: guarantee_steps = 1 is_first = False - prev_kf.add(NaiveReuseKeyframe(mult=mult, mult_multival=mult_multival, - start_percent=percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing)) + prev_kf.add(NaiveReuseKeyframe(mult=mult, mult_multival=mult_multival, start_percent=percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing)) if print_keyframes: - logger.info(f"NaiveReuseKeyframe - start_percent:{percent} = {mult}") - return (prev_kf,) -#---------------------------------------- -######################################### + logger.info(f'NaiveReuseKeyframe - start_percent:{percent} = {mult}') + return io.NodeOutput(prev_kf) +class ContextExtras_ContextRef(io.ComfyNode): -######################################### -# ContextRef -class ContextExtras_ContextRef: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "prev_extras": ("CONTEXT_EXTRAS",), - "strength_multival": ("MULTIVAL",), - "contextref_mode": ("CONTEXTREF_MODE",), - "contextref_tune": ("CONTEXTREF_TUNE",), - "contextref_kf": ("CONTEXTREF_KEYFRAME",), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "end_percent": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.001}), - }, - } - - RETURN_TYPES = ("CONTEXT_EXTRAS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras" - FUNCTION = "create_context_extra" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef', display_name='Context Extrasβ—†ContextRef πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras', inputs=[io.Custom('CONTEXT_EXTRAS').Input('prev_extras', optional=True), io.Custom('MULTIVAL').Input('strength_multival', optional=True), io.Custom('CONTEXTREF_MODE').Input('contextref_mode', optional=True), io.Custom('CONTEXTREF_TUNE').Input('contextref_tune', optional=True), io.Custom('CONTEXTREF_KEYFRAME').Input('contextref_kf', optional=True), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Float.Input('end_percent', optional=True, default=0.25, max=1.0, min=0.0, step=0.001)], outputs=[io.Custom('CONTEXT_EXTRAS').Output('CONTEXT_EXTRAS')]) - def create_context_extra(self, start_percent=0.0, end_percent=0.1, strength_multival: Union[float, Tensor]=None, - contextref_mode: ContextRefMode=None, contextref_tune: ContextRefTune=None, - contextref_kf: ContextRefKeyframeGroup=None, prev_extras: ContextExtrasGroup=None): + @classmethod + def execute(cls, start_percent=0.0, end_percent=0.1, strength_multival: Union[float, Tensor]=None, contextref_mode: ContextRefMode=None, contextref_tune: ContextRefTune=None, contextref_kf: ContextRefKeyframeGroup=None, prev_extras: ContextExtrasGroup=None): if prev_extras is None: prev_extras = prev_extras = ContextExtrasGroup() prev_extras = prev_extras.clone() - # create extra - # TODO: make customizable, and allow mask input if contextref_tune is None: contextref_tune = ContextRefTune(attn_style_fidelity=1.0, attn_ref_weight=1.0, attn_strength=1.0) if contextref_mode is None: contextref_mode = ContextRefMode.init_first() - context_ref = ContextRef(start_percent=start_percent, end_percent=end_percent, - strength_multival=strength_multival, tune=contextref_tune, mode=contextref_mode, - keyframe=contextref_kf) + context_ref = ContextRef(start_percent=start_percent, end_percent=end_percent, strength_multival=strength_multival, tune=contextref_tune, mode=contextref_mode, keyframe=contextref_kf) prev_extras.add(context_ref) - return (prev_extras,) + return io.NodeOutput(prev_extras) +class ContextRef_KeyframeMultivalNode(io.ComfyNode): -class ContextRef_KeyframeMultivalNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "prev_kf": ("CONTEXTREF_KEYFRAME",), - "mult_multival": ("MULTIVAL",), - "mode_replace": ("CONTEXTREF_MODE",), - "tune_replace": ("CONTEXTREF_TUNE",), - "mult": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - "inherit_missing": ("BOOLEAN", {"default": True}, ), - }, - } - - RETURN_TYPES = ("CONTEXTREF_KEYFRAME",) - RETURN_NAMES = ("CONTEXTREF_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref" - FUNCTION = "create_keyframe" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef_Keyframe', display_name='ContextRef Keyframe πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref', inputs=[io.Custom('CONTEXTREF_KEYFRAME').Input('prev_kf', optional=True), io.Custom('MULTIVAL').Input('mult_multival', optional=True), io.Custom('CONTEXTREF_MODE').Input('mode_replace', optional=True), io.Custom('CONTEXTREF_TUNE').Input('tune_replace', optional=True), io.Float.Input('mult', optional=True, default=1.0, max=1.0, min=0.0, step=0.001), io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), io.Boolean.Input('inherit_missing', optional=True, default=True)], outputs=[io.Custom('CONTEXTREF_KEYFRAME').Output('CONTEXTREF_KF')]) - def create_keyframe(self, prev_kf: ContextRefKeyframeGroup=None, - mult=1.0, mult_multival=None, mode_replace=None, tune_replace=None, - start_percent=1.0, guarantee_steps=1, inherit_missing=True): + @classmethod + def execute(cls, prev_kf: ContextRefKeyframeGroup=None, mult=1.0, mult_multival=None, mode_replace=None, tune_replace=None, start_percent=1.0, guarantee_steps=1, inherit_missing=True): if prev_kf is None: prev_kf = ContextRefKeyframeGroup() prev_kf = prev_kf.clone() - kf = ContextRefKeyframe(mult=mult, mult_multival=mult_multival, tune_replace=tune_replace, mode_replace=mode_replace, - start_percent=start_percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing) + kf = ContextRefKeyframe(mult=mult, mult_multival=mult_multival, tune_replace=tune_replace, mode_replace=mode_replace, start_percent=start_percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing) prev_kf.add(kf) - return (prev_kf,) + return io.NodeOutput(prev_kf) +class ContextRef_KeyframeInterpolationNode(io.ComfyNode): -class ContextRef_KeyframeInterpolationNode: @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}), - "mult_start": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "mult_end": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "interpolation": (InterpolationMethod._LIST, ), - "intervals": ("INT", {"default": 50, "min": 2, "max": 100, "step": 1}), - "inherit_missing": ("BOOLEAN", {"default": True}), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_kf": ("CONTEXTREF_KEYFRAME",), - "mult_multival": ("MULTIVAL",), - "mode_replace": ("CONTEXTREF_MODE",), - "tune_replace": ("CONTEXTREF_TUNE",), - }, - } - - RETURN_TYPES = ("CONTEXTREF_KEYFRAME",) - RETURN_NAMES = ("CONTEXTREF_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref" - FUNCTION = "create_keyframe" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef_KeyframeInterpolation', display_name='ContextRef Keyframes Interp. πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref', 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('mult_start', default=1.0, max=1.0, min=0.0, step=0.001), io.Float.Input('mult_end', default=1.0, max=1.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.Boolean.Input('inherit_missing', default=True), io.Boolean.Input('print_keyframes', default=False), io.Custom('CONTEXTREF_KEYFRAME').Input('prev_kf', optional=True), io.Custom('MULTIVAL').Input('mult_multival', optional=True), io.Custom('CONTEXTREF_MODE').Input('mode_replace', optional=True), io.Custom('CONTEXTREF_TUNE').Input('tune_replace', optional=True)], outputs=[io.Custom('CONTEXTREF_KEYFRAME').Output('CONTEXTREF_KF')]) - def create_keyframe(self, - start_percent: float, end_percent: float, - mult_start: float, mult_end: float, interpolation: str, intervals: int, - inherit_missing=True, prev_kf: ContextRefKeyframeGroup=None, - mult_multival=None, mode_replace=None, tune_replace=None, print_keyframes=False): + @classmethod + def execute(cls, start_percent: float, end_percent: float, mult_start: float, mult_end: float, interpolation: str, intervals: int, inherit_missing=True, prev_kf: ContextRefKeyframeGroup=None, mult_multival=None, mode_replace=None, tune_replace=None, print_keyframes=False): if prev_kf is None: prev_kf = ContextRefKeyframeGroup() prev_kf = prev_kf.clone() percents = InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=intervals, method=InterpolationMethod.LINEAR) mults = InterpolationMethod.get_weights(num_from=mult_start, num_to=mult_end, length=intervals, method=interpolation) - is_first = True for percent, mult in zip(percents, mults): guarantee_steps = 0 if is_first: guarantee_steps = 1 is_first = False - prev_kf.add(ContextRefKeyframe(mult=mult, mult_multival=mult_multival, tune_replace=tune_replace, mode_replace=mode_replace, - start_percent=percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing)) + prev_kf.add(ContextRefKeyframe(mult=mult, mult_multival=mult_multival, tune_replace=tune_replace, mode_replace=mode_replace, start_percent=percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing)) if print_keyframes: - logger.info(f"ContextRefKeyframe - start_percent:{percent} = {mult}") - return (prev_kf,) + logger.info(f'ContextRefKeyframe - start_percent:{percent} = {mult}') + return io.NodeOutput(prev_kf) +class ContextRef_KeyframeFromListNode(io.ComfyNode): -class ContextRef_KeyframeFromListNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "mults_float": ("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}), - "inherit_missing": ("BOOLEAN", {"default": True}), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_kf": ("CONTEXTREF_KEYFRAME",), - "mult_multival": ("MULTIVAL",), - "mode_replace": ("CONTEXTREF_MODE",), - "tune_replace": ("CONTEXTREF_TUNE",), - }, - } - - RETURN_TYPES = ("CONTEXTREF_KEYFRAME",) - RETURN_NAMES = ("CONTEXTREF_KF",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref" - FUNCTION = "create_keyframe" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef_KeyframeFromList', display_name='ContextRef Keyframes From List πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref', inputs=[io.Float.Input('mults_float', 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.Boolean.Input('inherit_missing', default=True), io.Boolean.Input('print_keyframes', default=False), io.Custom('CONTEXTREF_KEYFRAME').Input('prev_kf', optional=True), io.Custom('MULTIVAL').Input('mult_multival', optional=True), io.Custom('CONTEXTREF_MODE').Input('mode_replace', optional=True), io.Custom('CONTEXTREF_TUNE').Input('tune_replace', optional=True)], outputs=[io.Custom('CONTEXTREF_KEYFRAME').Output('CONTEXTREF_KF')]) - def create_keyframe(self, mults_float: Union[float, list[float]], - start_percent: float, end_percent: float, - inherit_missing=True, prev_kf: ContextRefKeyframeGroup=None, - mult_multival=None, mode_replace=None, tune_replace=None, print_keyframes=False): + @classmethod + def execute(cls, mults_float: Union[float, list[float]], start_percent: float, end_percent: float, inherit_missing=True, prev_kf: ContextRefKeyframeGroup=None, mult_multival=None, mode_replace=None, tune_replace=None, print_keyframes=False): if prev_kf is None: prev_kf = ContextRefKeyframeGroup() prev_kf = prev_kf.clone() @@ -367,131 +179,73 @@ class ContextRef_KeyframeFromListNode: elif isinstance(mults_float, Iterable): pass else: - raise Exception(f"strengths_float must be either an interable input or a float, but was {type(mults_float).__repr__}.") + raise Exception(f'strengths_float must be either an interable input or a float, but was {type(mults_float).__repr__}.') percents = InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=len(mults_float), method=InterpolationMethod.LINEAR) - is_first = True for percent, mult in zip(percents, mults_float): guarantee_steps = 0 if is_first: guarantee_steps = 1 is_first = False - prev_kf.add(ContextRefKeyframe(mult=mult, mult_multival=mult_multival, tune_replace=tune_replace, mode_replace=mode_replace, - start_percent=percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing)) + prev_kf.add(ContextRefKeyframe(mult=mult, mult_multival=mult_multival, tune_replace=tune_replace, mode_replace=mode_replace, start_percent=percent, guarantee_steps=guarantee_steps, inherit_missing=inherit_missing)) if print_keyframes: - logger.info(f"ContextRefKeyframe - start_percent:{percent} = {mult}") - return (prev_kf,) + logger.info(f'ContextRefKeyframe - start_percent:{percent} = {mult}') + return io.NodeOutput(prev_kf) +class ContextRef_ModeFirst(io.ComfyNode): -class ContextRef_ModeFirst: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - } - - RETURN_TYPES = ("CONTEXTREF_MODE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref" - FUNCTION = "create_contextref_mode" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef_ModeFirst', display_name='ContextRef Modeβ—†First πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref', inputs=[], outputs=[io.Custom('CONTEXTREF_MODE').Output('CONTEXTREF_MODE')]) - def create_contextref_mode(self): + @classmethod + def execute(cls): mode = ContextRefMode.init_first() - return (mode,) + return io.NodeOutput(mode) +class ContextRef_ModeSliding(io.ComfyNode): -class ContextRef_ModeSliding: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "sliding_width": ("INT", {"default": 2, "min": 2, "max": BIGMAX, "step": 1}), - }, - } - - RETURN_TYPES = ("CONTEXTREF_MODE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref" - FUNCTION = "create_contextref_mode" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef_ModeSliding', display_name='ContextRef Modeβ—†Sliding πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref', inputs=[io.Int.Input('sliding_width', optional=True, default=2, max=9007199254740991, min=2, step=1)], outputs=[io.Custom('CONTEXTREF_MODE').Output('CONTEXTREF_MODE')]) - def create_contextref_mode(self, sliding_width): + @classmethod + def execute(cls, sliding_width): mode = ContextRefMode.init_sliding(sliding_width=sliding_width) - return (mode,) + return io.NodeOutput(mode) +class ContextRef_ModeIndexes(io.ComfyNode): -class ContextRef_ModeIndexes: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "switch_on_idxs": ("STRING", {"default": ""}), - "always_include_0": ("BOOLEAN", {"default": True},), - }, - } - - RETURN_TYPES = ("CONTEXTREF_MODE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref" - FUNCTION = "create_contextref_mode" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef_ModeIndexes', display_name='ContextRef Modeβ—†Indexes πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref', inputs=[io.String.Input('switch_on_idxs', optional=True, default=''), io.Boolean.Input('always_include_0', optional=True, default=True)], outputs=[io.Custom('CONTEXTREF_MODE').Output('CONTEXTREF_MODE')]) - def create_contextref_mode(self, switch_on_idxs: str, always_include_0: bool): + @classmethod + def execute(cls, switch_on_idxs: str, always_include_0: bool): idxs = set(convert_str_to_indexes(indexes_str=switch_on_idxs, length=0, allow_range=False)) if always_include_0 and 0 not in idxs: idxs.add(0) mode = ContextRefMode.init_indexes(indexes=idxs) - return (mode,) + return io.NodeOutput(mode) +class ContextRef_TuneAttnAdain(io.ComfyNode): -class ContextRef_TuneAttnAdain: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "attn_style_fidelity": ("FLOAT", {"default": 1.0, "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": 1.0, "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}), - }, - } - - RETURN_TYPES = ("CONTEXTREF_TUNE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref" - FUNCTION = "create_contextref_tune" + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef_TuneAttnAdain', display_name='ContextRef Tuneβ—†Attn+Adain πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref', inputs=[io.Float.Input('attn_style_fidelity', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), io.Float.Input('attn_ref_weight', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), io.Float.Input('attn_strength', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), io.Float.Input('adain_style_fidelity', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), io.Float.Input('adain_ref_weight', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), io.Float.Input('adain_strength', optional=True, default=1.0, max=1.0, min=0.0, step=0.01)], outputs=[io.Custom('CONTEXTREF_TUNE').Output('CONTEXTREF_TUNE')]) - def create_contextref_tune(self, attn_style_fidelity=1.0, attn_ref_weight=1.0, attn_strength=1.0, - adain_style_fidelity=1.0, adain_ref_weight=1.0, adain_strength=1.0): - params = ContextRefTune(attn_style_fidelity=attn_style_fidelity, adain_style_fidelity=adain_style_fidelity, - attn_ref_weight=attn_ref_weight, adain_ref_weight=adain_ref_weight, - attn_strength=attn_strength, adain_strength=adain_strength) - return (params,) - - -class ContextRef_TuneAttn: @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "attn_style_fidelity": ("FLOAT", {"default": 1.0, "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}), - }, - } - - RETURN_TYPES = ("CONTEXTREF_TUNE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref" - FUNCTION = "create_contextref_tune" + def execute(cls, attn_style_fidelity=1.0, attn_ref_weight=1.0, attn_strength=1.0, adain_style_fidelity=1.0, adain_ref_weight=1.0, adain_strength=1.0): + params = ContextRefTune(attn_style_fidelity=attn_style_fidelity, adain_style_fidelity=adain_style_fidelity, attn_ref_weight=attn_ref_weight, adain_ref_weight=adain_ref_weight, attn_strength=attn_strength, adain_strength=adain_strength) + return io.NodeOutput(params) - def create_contextref_tune(self, attn_style_fidelity=1.0, attn_ref_weight=1.0, attn_strength=1.0): - return ContextRef_TuneAttnAdain.create_contextref_tune(self, - attn_style_fidelity=attn_style_fidelity, attn_ref_weight=attn_ref_weight, attn_strength=attn_strength, - adain_ref_weight=0.0, adain_style_fidelity=0.0, adain_strength=0.0) -#---------------------------------------- -######################################### +class ContextRef_TuneAttn(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_ContextExtras_ContextRef_TuneAttn', display_name='ContextRef Tuneβ—†Attn πŸŽ­πŸ…πŸ…“', category='Animate Diff πŸŽ­πŸ…πŸ…“/context opts/context extras/contextref', inputs=[io.Float.Input('attn_style_fidelity', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), io.Float.Input('attn_ref_weight', optional=True, default=1.0, max=1.0, min=0.0, step=0.01), io.Float.Input('attn_strength', optional=True, default=1.0, max=1.0, min=0.0, step=0.01)], outputs=[io.Custom('CONTEXTREF_TUNE').Output('CONTEXTREF_TUNE')]) + + @classmethod + def execute(cls, attn_style_fidelity=1.0, attn_ref_weight=1.0, attn_strength=1.0): + output = ContextRef_TuneAttnAdain.execute(attn_style_fidelity=attn_style_fidelity, attn_ref_weight=attn_ref_weight, attn_strength=attn_strength, adain_ref_weight=0.0, adain_style_fidelity=0.0, adain_strength=0.0) + return io.NodeOutput(*output.args) diff --git a/animatediff/nodes_deprecated.py b/animatediff/nodes_deprecated.py index 0d16957..8f7b4af 100644 --- a/animatediff/nodes_deprecated.py +++ b/animatediff/nodes_deprecated.py @@ -1,557 +1,222 @@ +from comfy_api.latest import io import json import os import shutil import subprocess from typing import Dict, List - import numpy as np import torch from PIL import Image from PIL.PngImagePlugin import PngInfo - import folder_paths from comfy.model_patcher import ModelPatcher - from .ad_settings import AnimateDiffSettings, AdjustGroup, AdjustPE, AdjustWeight from .context import ContextOptionsGroup, ContextOptions, ContextSchedules from .logger import logger from .utils_model import Folders, BetaSchedules, get_available_motion_models from .utils_motion import ADKeyframeGroup from .motion_lora import MotionLoraList -from .model_injection import (ModelPatcherHelper, InjectionParams, MotionModelGroup, get_mm_attachment, load_motion_module_gen1) +from .model_injection import ModelPatcherHelper, InjectionParams, MotionModelGroup, get_mm_attachment, load_motion_module_gen1 from .sampling import outer_sample_wrapper, sliding_calc_cond_batch from .sample_settings import SampleSettings +class AnimateDiffLoaderDEPR(io.ComfyNode): -class AnimateDiffLoaderDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "latents": ("LATENT",), - "model_name": (get_available_motion_models(),), - "unlimited_area_hack": ("BOOLEAN", {"default": False},), - "beta_schedule": (BetaSchedules.get_alias_list_with_first_element(BetaSchedules.SQRT_LINEAR),), - }, - } + def define_schema(cls): + return io.Schema(node_id='AnimateDiffLoaderV1', display_name='🚫AnimateDiff Loader [DEPRECATED] πŸŽ­πŸ…πŸ…“', category='', inputs=[io.Model.Input('model'), io.Custom('LATENT').Input('latents'), io.Combo.Input('model_name', options=get_available_motion_models()), io.Boolean.Input('unlimited_area_hack', default=False), io.Combo.Input('beta_schedule', options=['sqrt_linear (AnimateDiff)', 'use existing', 'autoselect', 'linear (AnimateDiff-SDXL)', 'linear (HotshotXL/default)', 'avg(sqrt_linear,linear)', 'lcm avg(sqrt_linear,linear)', 'lcm', 'lcm[100_ots]', 'lcm >> sqrt_linear', 'sqrt', 'cosine', 'squaredcos_cap_v2'])], outputs=[io.Model.Output('MODEL'), io.Custom('LATENT').Output('LATENT')], is_deprecated=True) - RETURN_TYPES = ("MODEL", "LATENT") - CATEGORY = "" - FUNCTION = "load_mm_and_inject_params" - DEPRECATED = True - - def load_mm_and_inject_params( - self, - model: ModelPatcher, - latents: Dict[str, torch.Tensor], - model_name: str, unlimited_area_hack: bool, beta_schedule: str, - ): - # load motion module + @classmethod + def execute(cls, model: ModelPatcher, latents: Dict[str, torch.Tensor], model_name: str, unlimited_area_hack: bool, beta_schedule: str): motion_model = load_motion_module_gen1(model_name, model) - # get total frames - init_frames_len = len(latents["samples"]) # deprecated - no longer used for anything lol - # set injection params - params = InjectionParams( - unlimited_area_hack=unlimited_area_hack, - apply_v2_properly=False, - ) - # inject for use in sampling code + init_frames_len = len(latents['samples']) + params = InjectionParams(unlimited_area_hack=unlimited_area_hack, apply_v2_properly=False) model = model.clone() helper = ModelPatcherHelper(model) - helper.set_all_properties( - outer_sampler_wrapper=outer_sample_wrapper, - calc_cond_batch_wrapper=sliding_calc_cond_batch, - params=params, - motion_models=MotionModelGroup(motion_model), - ) - - # save model sampling from BetaSchedule as object patch - # if autoselect, get suggested beta_schedule from motion model - if beta_schedule == BetaSchedules.AUTOSELECT and not model.motion_models.is_empty(): + helper.set_all_properties(outer_sampler_wrapper=outer_sample_wrapper, calc_cond_batch_wrapper=sliding_calc_cond_batch, params=params, motion_models=MotionModelGroup(motion_model)) + if beta_schedule == BetaSchedules.AUTOSELECT and (not model.motion_models.is_empty()): beta_schedule = model.motion_models[0].model.get_best_beta_schedule(log=True) new_model_sampling = BetaSchedules.to_model_sampling(beta_schedule, model) if new_model_sampling is not None: - model.add_object_patch("model_sampling", new_model_sampling) - + model.add_object_patch('model_sampling', new_model_sampling) del motion_model - return (model, latents) + return io.NodeOutput(model, latents) +class AnimateDiffLoaderAdvancedDEPR(io.ComfyNode): -class AnimateDiffLoaderAdvancedDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "latents": ("LATENT",), - "model_name": (get_available_motion_models(),), - "unlimited_area_hack": ("BOOLEAN", {"default": False},), - "context_length": ("INT", {"default": 16, "min": 0, "max": 1000}), - "context_stride": ("INT", {"default": 1, "min": 1, "max": 1000}), - "context_overlap": ("INT", {"default": 4, "min": 0, "max": 1000}), - "context_schedule": (ContextSchedules.LEGACY_UNIFORM_SCHEDULE_LIST,), - "closed_loop": ("BOOLEAN", {"default": False},), - "beta_schedule": (BetaSchedules.get_alias_list_with_first_element(BetaSchedules.SQRT_LINEAR),), - }, - } + def define_schema(cls): + return io.Schema(node_id='ADE_AnimateDiffLoaderV1Advanced', display_name='🚫AnimateDiff Loader (Advanced) [DEPRECATED] πŸŽ­πŸ…πŸ…“', category='', inputs=[io.Model.Input('model'), io.Custom('LATENT').Input('latents'), io.Combo.Input('model_name', options=get_available_motion_models()), io.Boolean.Input('unlimited_area_hack', default=False), io.Int.Input('context_length', default=16, max=1000, min=0), io.Int.Input('context_stride', default=1, max=1000, min=1), io.Int.Input('context_overlap', default=4, max=1000, min=0), io.Combo.Input('context_schedule', options=['uniform']), io.Boolean.Input('closed_loop', default=False), io.Combo.Input('beta_schedule', options=['sqrt_linear (AnimateDiff)', 'use existing', 'autoselect', 'linear (AnimateDiff-SDXL)', 'linear (HotshotXL/default)', 'avg(sqrt_linear,linear)', 'lcm avg(sqrt_linear,linear)', 'lcm', 'lcm[100_ots]', 'lcm >> sqrt_linear', 'sqrt', 'cosine', 'squaredcos_cap_v2'])], outputs=[io.Model.Output('MODEL'), io.Custom('LATENT').Output('LATENT')], is_deprecated=True) - RETURN_TYPES = ("MODEL", "LATENT") - CATEGORY = "" - FUNCTION = "load_mm_and_inject_params" - DEPRECATED = True - - def load_mm_and_inject_params(self, - model: ModelPatcher, - latents: Dict[str, torch.Tensor], - model_name: str, unlimited_area_hack: bool, - context_length: int, context_stride: int, context_overlap: int, context_schedule: str, closed_loop: bool, - beta_schedule: str, - ): - # load motion module + @classmethod + def execute(cls, model: ModelPatcher, latents: Dict[str, torch.Tensor], model_name: str, unlimited_area_hack: bool, context_length: int, context_stride: int, context_overlap: int, context_schedule: str, closed_loop: bool, beta_schedule: str): motion_model = load_motion_module_gen1(model_name, model) - # get total frames - init_frames_len = len(latents["samples"]) # deprecated - no longer used for anything lol - # set injection params - params = InjectionParams( - unlimited_area_hack=unlimited_area_hack, - apply_v2_properly=False, - ) + init_frames_len = len(latents['samples']) + params = InjectionParams(unlimited_area_hack=unlimited_area_hack, apply_v2_properly=False) context_group = ContextOptionsGroup() - context_group.add( - ContextOptions( - context_length=context_length, - context_stride=context_stride, - context_overlap=context_overlap, - context_schedule=context_schedule, - closed_loop=closed_loop, - ) - ) - # set context settings + context_group.add(ContextOptions(context_length=context_length, context_stride=context_stride, context_overlap=context_overlap, context_schedule=context_schedule, closed_loop=closed_loop)) params.set_context(context_options=context_group) - # inject for use in sampling code model = model.clone() helper = ModelPatcherHelper(model) - helper.set_all_properties( - outer_sampler_wrapper=outer_sample_wrapper, - calc_cond_batch_wrapper=sliding_calc_cond_batch, - params=params, - motion_models=MotionModelGroup(motion_model), - ) - - # save model sampling from BetaSchedule as object patch - # if autoselect, get suggested beta_schedule from motion model - if beta_schedule == BetaSchedules.AUTOSELECT and not model.motion_models.is_empty(): + helper.set_all_properties(outer_sampler_wrapper=outer_sample_wrapper, calc_cond_batch_wrapper=sliding_calc_cond_batch, params=params, motion_models=MotionModelGroup(motion_model)) + if beta_schedule == BetaSchedules.AUTOSELECT and (not model.motion_models.is_empty()): beta_schedule = model.motion_models[0].model.get_best_beta_schedule(log=True) new_model_sampling = BetaSchedules.to_model_sampling(beta_schedule, model) if new_model_sampling is not None: - model.add_object_patch("model_sampling", new_model_sampling) - + model.add_object_patch('model_sampling', new_model_sampling) del motion_model - return (model, latents) + return io.NodeOutput(model, latents) +class LegacyAnimateDiffLoaderWithContextDEPR(io.ComfyNode): -class LegacyAnimateDiffLoaderWithContextDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "model_name": (get_available_motion_models(),), - "beta_schedule": (BetaSchedules.ALIAS_LIST, {"default": BetaSchedules.AUTOSELECT}), - #"apply_mm_groupnorm_hack": ("BOOLEAN", {"default": True}), - }, - "optional": { - "context_options": ("CONTEXT_OPTIONS",), - "motion_lora": ("MOTION_LORA",), - "ad_settings": ("AD_SETTINGS",), - "sample_settings": ("SAMPLE_SETTINGS",), - "motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - "apply_v2_models_properly": ("BOOLEAN", {"default": True}), - "ad_keyframes": ("AD_KEYFRAMES",), - } - } - - DEPRECATED = True - RETURN_TYPES = ("MODEL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘  Gen1 nodes β‘ " - FUNCTION = "load_mm_and_inject_params" + def define_schema(cls): + return io.Schema(node_id='ADE_AnimateDiffLoaderWithContext', display_name='AnimateDiff Loader [Legacy] πŸŽ­πŸ…πŸ…“β‘ ', category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘  Gen1 nodes β‘ ', inputs=[io.Model.Input('model'), io.Combo.Input('model_name', options=get_available_motion_models()), io.Combo.Input('beta_schedule', options=['autoselect', 'use existing', 'sqrt_linear (AnimateDiff)', 'linear (AnimateDiff-SDXL)', 'linear (HotshotXL/default)', 'avg(sqrt_linear,linear)', 'lcm avg(sqrt_linear,linear)', 'lcm', 'lcm[100_ots]', 'lcm >> sqrt_linear', 'sqrt', 'cosine', 'squaredcos_cap_v2'], default='autoselect'), io.Custom('CONTEXT_OPTIONS').Input('context_options', optional=True), io.Custom('MOTION_LORA').Input('motion_lora', optional=True), io.Custom('AD_SETTINGS').Input('ad_settings', optional=True), io.Custom('SAMPLE_SETTINGS').Input('sample_settings', optional=True), io.Float.Input('motion_scale', optional=True, default=1.0, min=0.0, step=0.001), io.Boolean.Input('apply_v2_models_properly', optional=True, default=True), io.Custom('AD_KEYFRAMES').Input('ad_keyframes', optional=True)], outputs=[io.Model.Output('MODEL')], is_deprecated=True) - def load_mm_and_inject_params(self, - model: ModelPatcher, - model_name: str, beta_schedule: str,# apply_mm_groupnorm_hack: bool, - context_options: ContextOptionsGroup=None, motion_lora: MotionLoraList=None, ad_settings: AnimateDiffSettings=None, motion_model_settings: AnimateDiffSettings=None, - sample_settings: SampleSettings=None, motion_scale: float=1.0, apply_v2_models_properly: bool=False, ad_keyframes: ADKeyframeGroup=None, - ): + @classmethod + def execute(cls, model: ModelPatcher, model_name: str, beta_schedule: str, context_options: ContextOptionsGroup=None, motion_lora: MotionLoraList=None, ad_settings: AnimateDiffSettings=None, motion_model_settings: AnimateDiffSettings=None, sample_settings: SampleSettings=None, motion_scale: float=1.0, apply_v2_models_properly: bool=False, ad_keyframes: ADKeyframeGroup=None): if ad_settings is not None: motion_model_settings = ad_settings - # load motion module motion_model = load_motion_module_gen1(model_name, model, motion_lora=motion_lora, motion_model_settings=motion_model_settings) - # set injection params - params = InjectionParams( - unlimited_area_hack=False, - apply_v2_properly=apply_v2_models_properly, - ) + params = InjectionParams(unlimited_area_hack=False, apply_v2_properly=apply_v2_models_properly) if context_options: params.set_context(context_options) - # set motion_scale and motion_model_settings if not motion_model_settings: motion_model_settings = AnimateDiffSettings() motion_model_settings.attn_scale = motion_scale params.set_motion_model_settings(motion_model_settings) - attachment = get_mm_attachment(motion_model) if params.motion_model_settings.mask_attn_scale is not None: attachment.scale_multival = params.motion_model_settings.mask_attn_scale * params.motion_model_settings.attn_scale else: attachment.scale_multival = params.motion_model_settings.attn_scale - attachment.keyframes = ad_keyframes.clone() if ad_keyframes else ADKeyframeGroup() - - # need to use a ModelPatcher that supports injection of motion modules into unet model = model.clone() helper = ModelPatcherHelper(model) - helper.set_all_properties( - outer_sampler_wrapper=outer_sample_wrapper, - calc_cond_batch_wrapper=sliding_calc_cond_batch, - params=params, - sample_settings=sample_settings, - motion_models=MotionModelGroup(motion_model), - ) - + helper.set_all_properties(outer_sampler_wrapper=outer_sample_wrapper, calc_cond_batch_wrapper=sliding_calc_cond_batch, params=params, sample_settings=sample_settings, motion_models=MotionModelGroup(motion_model)) sample_settings = helper.get_sample_settings() if sample_settings.custom_cfg is not None: - logger.info("[Sample Settings] custom_cfg is set; will override any KSampler cfg values or patches.") - + logger.info('[Sample Settings] custom_cfg is set; will override any KSampler cfg values or patches.') if sample_settings.sigma_schedule is not None: - logger.info("[Sample Settings] sigma_schedule is set; will override beta_schedule.") - model.add_object_patch("model_sampling", sample_settings.sigma_schedule.clone().model_sampling) + logger.info('[Sample Settings] sigma_schedule is set; will override beta_schedule.') + model.add_object_patch('model_sampling', sample_settings.sigma_schedule.clone().model_sampling) else: - # save model sampling from BetaSchedule as object patch - # if autoselect, get suggested beta_schedule from motion model if beta_schedule == BetaSchedules.AUTOSELECT and helper.get_motion_models(): beta_schedule = helper.get_motion_models()[0].model.get_best_beta_schedule(log=True) new_model_sampling = BetaSchedules.to_model_sampling(beta_schedule, model) if new_model_sampling is not None: - model.add_object_patch("model_sampling", new_model_sampling) - + model.add_object_patch('model_sampling', new_model_sampling) del motion_model - return (model,) + return io.NodeOutput(model) +class AnimateDiffCombineDEPR(io.ComfyNode): -class AnimateDiffCombineDEPR: - ffmpeg_warning_already_shown = False @classmethod - def INPUT_TYPES(s): - ffmpeg_path = shutil.which("ffmpeg") - #Hide ffmpeg formats if ffmpeg isn't available + def get_formats(cls): + ffmpeg_path = shutil.which('ffmpeg') if ffmpeg_path is not None: - ffmpeg_formats = ["video/"+x[:-5] for x in folder_paths.get_filename_list(Folders.VIDEO_FORMATS)] - else: - ffmpeg_formats = [] - if not s.ffmpeg_warning_already_shown: - # Deprecated node are now hidden, so no need to show warning unless node is used. - # logger.warning("This warning can be ignored, you should not be using the deprecated AnimateDiff Combine node anyway. If you are, use Video Combine from ComfyUI-VideoHelperSuite instead. ffmpeg could not be found. Outputs that require it have been disabled") - s.ffmpeg_warning_already_shown = True - return { - "required": { - "images": ("IMAGE",), - "frame_rate": ( - "INT", - {"default": 8, "min": 1, "max": 24, "step": 1}, - ), - "loop_count": ("INT", {"default": 0, "min": 0, "max": 100, "step": 1}), - "filename_prefix": ("STRING", {"default": "AnimateDiff"}), - "format": (["image/gif", "image/webp"] + ffmpeg_formats,), - "pingpong": ("BOOLEAN", {"default": False}), - "save_image": ("BOOLEAN", {"default": True}), - }, - "hidden": { - "prompt": "PROMPT", - "extra_pnginfo": "EXTRA_PNGINFO", - }, - } + return ['image/gif', 'image/webp'] + ['video/' + x[:-5] for x in folder_paths.get_filename_list(Folders.VIDEO_FORMATS)] + cls.ffmpeg_warning_already_shown = True + return ['image/gif', 'image/webp'] - RETURN_TYPES = ("GIF",) - OUTPUT_NODE = True - CATEGORY = "" - FUNCTION = "generate_gif" - DEPRECATED = True + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_AnimateDiffCombine', display_name='🚫AnimateDiff Combine [DEPRECATED, Use Video Combine (VHS) Instead!] πŸŽ­πŸ…πŸ…“', category='', inputs=[io.Image.Input('images'), io.Int.Input('frame_rate', default=8, max=24, min=1, step=1), io.Int.Input('loop_count', default=0, max=100, min=0, step=1), io.String.Input('filename_prefix', default='AnimateDiff'), io.Combo.Input('format', options=cls.get_formats()), io.Boolean.Input('pingpong', default=False), io.Boolean.Input('save_image', default=True)], outputs=[io.Custom('GIF').Output('GIF')], is_deprecated=True, is_output_node=True, hidden=[io.Hidden.prompt, io.Hidden.extra_pnginfo]) + ffmpeg_warning_already_shown = False - def generate_gif( - self, - images, - frame_rate: int, - loop_count: int, - filename_prefix="AnimateDiff", - format="image/gif", - pingpong=False, - save_image=True, - prompt=None, - extra_pnginfo=None, - ): - logger.warning("Do not use AnimateDiff Combine node, it is deprecated. Use Video Combine node from ComfyUI-VideoHelperSuite instead. Video nodes from VideoHelperSuite are actively maintained, more feature-rich, and also automatically attempts to get ffmpeg.") - # convert images to numpy + @classmethod + def execute(cls, images, frame_rate: int, loop_count: int, filename_prefix='AnimateDiff', format='image/gif', pingpong=False, save_image=True, prompt=None, extra_pnginfo=None): + prompt = cls.hidden.prompt + extra_pnginfo = cls.hidden.extra_pnginfo + logger.warning('Do not use AnimateDiff Combine node, it is deprecated. Use Video Combine node from ComfyUI-VideoHelperSuite instead. Video nodes from VideoHelperSuite are actively maintained, more feature-rich, and also automatically attempts to get ffmpeg.') frames: List[Image.Image] = [] for image in images: img = 255.0 * image.cpu().numpy() img = Image.fromarray(np.clip(img, 0, 255).astype(np.uint8)) frames.append(img) - - # get output information - output_dir = ( - folder_paths.get_output_directory() - if save_image - else folder_paths.get_temp_directory() - ) - ( - full_output_folder, - filename, - counter, - subfolder, - _, - ) = folder_paths.get_save_image_path(filename_prefix, output_dir) - + output_dir = folder_paths.get_output_directory() if save_image else folder_paths.get_temp_directory() + full_output_folder, filename, counter, subfolder, _ = folder_paths.get_save_image_path(filename_prefix, output_dir) metadata = PngInfo() if prompt is not None: - metadata.add_text("prompt", json.dumps(prompt)) + metadata.add_text('prompt', json.dumps(prompt)) if extra_pnginfo is not None: for x in extra_pnginfo: metadata.add_text(x, json.dumps(extra_pnginfo[x])) - - # save first frame as png to keep metadata - file = f"{filename}_{counter:05}_.png" + file = f'{filename}_{counter:05}_.png' file_path = os.path.join(full_output_folder, file) - frames[0].save( - file_path, - pnginfo=metadata, - compress_level=4, - ) + frames[0].save(file_path, pnginfo=metadata, compress_level=4) if pingpong: frames = frames + frames[-2:0:-1] - - format_type, format_ext = format.split("/") - file = f"{filename}_{counter:05}_.{format_ext}" + format_type, format_ext = format.split('/') + file = f'{filename}_{counter:05}_.{format_ext}' file_path = os.path.join(full_output_folder, file) - if format_type == "image": - # Use pillow directly to save an animated image - frames[0].save( - file_path, - format=format_ext.upper(), - save_all=True, - append_images=frames[1:], - duration=round(1000 / frame_rate), - loop=loop_count, - compress_level=4, - ) + if format_type == 'image': + frames[0].save(file_path, format=format_ext.upper(), save_all=True, append_images=frames[1:], duration=round(1000 / frame_rate), loop=loop_count, compress_level=4) else: - # Use ffmpeg to save a video - ffmpeg_path = shutil.which("ffmpeg") + ffmpeg_path = shutil.which('ffmpeg') if ffmpeg_path is None: - #Should never be reachable - raise ProcessLookupError("Could not find ffmpeg") - - video_format_path = folder_paths.get_full_path("video_formats", format_ext + ".json") + raise ProcessLookupError('Could not find ffmpeg') + video_format_path = folder_paths.get_full_path('video_formats', format_ext + '.json') with open(video_format_path, 'r') as stream: video_format = json.load(stream) file = f"{filename}_{counter:05}_.{video_format['extension']}" file_path = os.path.join(full_output_folder, file) - dimensions = f"{frames[0].width}x{frames[0].height}" - args = [ffmpeg_path, "-v", "error", "-f", "rawvideo", "-pix_fmt", "rgb24", - "-s", dimensions, "-r", str(frame_rate), "-i", "-"] \ - + video_format['main_pass'] + [file_path] - - env=os.environ.copy() - if "environment" in video_format: - env.update(video_format["environment"]) + dimensions = f'{frames[0].width}x{frames[0].height}' + args = [ffmpeg_path, '-v', 'error', '-f', 'rawvideo', '-pix_fmt', 'rgb24', '-s', dimensions, '-r', str(frame_rate), '-i', '-'] + video_format['main_pass'] + [file_path] + env = os.environ.copy() + if 'environment' in video_format: + env.update(video_format['environment']) with subprocess.Popen(args, stdin=subprocess.PIPE, env=env) as proc: for frame in frames: proc.stdin.write(frame.tobytes()) + previews = [{'filename': file, 'subfolder': subfolder, 'type': 'output' if save_image else 'temp', 'format': format}] + return io.NodeOutput.from_dict({'ui': {'images': previews, 'animated': (True,)}}) - previews = [ - { - "filename": file, - "subfolder": subfolder, - "type": "output" if save_image else "temp", - "format": format, - } - ] - return {"ui": {"images": previews, "animated": (True,)}} +class AnimateDiffModelSettingsDEPR(io.ComfyNode): - - -class AnimateDiffModelSettingsDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "min_motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - "max_motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - }, - "optional": { - "mask_motion_scale": ("MASK",), - } - } - - RETURN_TYPES = ("AD_SETTINGS",) - CATEGORY = "" #"Animate Diff πŸŽ­πŸ…πŸ…“/β‘  Gen1 nodes β‘ /motion settings" - FUNCTION = "get_motion_model_settings" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_AnimateDiffModelSettings_Release', display_name='🚫[DEPR] Motion Model Settings πŸŽ­πŸ…πŸ…“β‘ ', category='', inputs=[io.Float.Input('min_motion_scale', default=1.0, min=0.0, step=0.001), io.Float.Input('max_motion_scale', default=1.0, min=0.0, step=0.001), io.Mask.Input('mask_motion_scale', optional=True)], outputs=[io.Custom('AD_SETTINGS').Output('AD_SETTINGS')], is_deprecated=True) - def get_motion_model_settings(self, mask_motion_scale: torch.Tensor=None, min_motion_scale: float=1.0, max_motion_scale: float=1.0): - motion_model_settings = AnimateDiffSettings( - mask_attn_scale=mask_motion_scale, - mask_attn_scale_min=min_motion_scale, - mask_attn_scale_max=max_motion_scale, - ) - - return (motion_model_settings,) - - -class AnimateDiffModelSettingsSimpleDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "motion_pe_stretch": ("INT", {"default": 0, "min": 0, "step": 1}), - }, - "optional": { - "mask_motion_scale": ("MASK",), - "min_motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - "max_motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - } - } - - RETURN_TYPES = ("AD_SETTINGS",) - CATEGORY = "" #"Animate Diff πŸŽ­πŸ…πŸ…“/β‘  Gen1 nodes β‘ /motion settings/experimental" - FUNCTION = "get_motion_model_settings" - DEPRECATED = True + def execute(cls, mask_motion_scale: torch.Tensor=None, min_motion_scale: float=1.0, max_motion_scale: float=1.0): + motion_model_settings = AnimateDiffSettings(mask_attn_scale=mask_motion_scale, mask_attn_scale_min=min_motion_scale, mask_attn_scale_max=max_motion_scale) + return io.NodeOutput(motion_model_settings) - def get_motion_model_settings(self, motion_pe_stretch: int, - mask_motion_scale: torch.Tensor=None, min_motion_scale: float=1.0, max_motion_scale: float=1.0): +class AnimateDiffModelSettingsSimpleDEPR(io.ComfyNode): + + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_AnimateDiffModelSettingsSimple', display_name='🚫[DEPR] Motion Model Settings (Simple) πŸŽ­πŸ…πŸ…“β‘ ', category='', inputs=[io.Int.Input('motion_pe_stretch', default=0, min=0, step=1), io.Mask.Input('mask_motion_scale', optional=True), io.Float.Input('min_motion_scale', optional=True, default=1.0, min=0.0, step=0.001), io.Float.Input('max_motion_scale', optional=True, default=1.0, min=0.0, step=0.001)], outputs=[io.Custom('AD_SETTINGS').Output('AD_SETTINGS')], is_deprecated=True) + + @classmethod + def execute(cls, motion_pe_stretch: int, mask_motion_scale: torch.Tensor=None, min_motion_scale: float=1.0, max_motion_scale: float=1.0): adjust_pe = AdjustGroup(AdjustPE(motion_pe_stretch=motion_pe_stretch)) - motion_model_settings = AnimateDiffSettings( - adjust_pe=adjust_pe, - mask_attn_scale=mask_motion_scale, - mask_attn_scale_min=min_motion_scale, - mask_attn_scale_max=max_motion_scale, - ) + motion_model_settings = AnimateDiffSettings(adjust_pe=adjust_pe, mask_attn_scale=mask_motion_scale, mask_attn_scale_min=min_motion_scale, mask_attn_scale_max=max_motion_scale) + return io.NodeOutput(motion_model_settings) - return (motion_model_settings,) +class AnimateDiffModelSettingsAdvancedDEPR(io.ComfyNode): - -class AnimateDiffModelSettingsAdvancedDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "pe_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "attn_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "other_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "motion_pe_stretch": ("INT", {"default": 0, "min": 0, "step": 1}), - "cap_initial_pe_length": ("INT", {"default": 0, "min": 0, "step": 1}), - "interpolate_pe_to_length": ("INT", {"default": 0, "min": 0, "step": 1}), - "initial_pe_idx_offset": ("INT", {"default": 0, "min": 0, "step": 1}), - "final_pe_idx_offset": ("INT", {"default": 0, "min": 0, "step": 1}), - }, - "optional": { - "mask_motion_scale": ("MASK",), - "min_motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - "max_motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - } - } - - RETURN_TYPES = ("AD_SETTINGS",) - CATEGORY = "" #"Animate Diff πŸŽ­πŸ…πŸ…“/β‘  Gen1 nodes β‘ /motion settings/experimental" - FUNCTION = "get_motion_model_settings" - DEPRECATED = True + def define_schema(cls): + return io.Schema(node_id='ADE_AnimateDiffModelSettings', display_name='🚫[DEPR] Motion Model Settings (Advanced) πŸŽ­πŸ…πŸ…“β‘ ', category='', inputs=[io.Float.Input('pe_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('attn_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('other_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Int.Input('motion_pe_stretch', default=0, min=0, step=1), io.Int.Input('cap_initial_pe_length', default=0, min=0, step=1), io.Int.Input('interpolate_pe_to_length', default=0, min=0, step=1), io.Int.Input('initial_pe_idx_offset', default=0, min=0, step=1), io.Int.Input('final_pe_idx_offset', default=0, min=0, step=1), io.Mask.Input('mask_motion_scale', optional=True), io.Float.Input('min_motion_scale', optional=True, default=1.0, min=0.0, step=0.001), io.Float.Input('max_motion_scale', optional=True, default=1.0, min=0.0, step=0.001)], outputs=[io.Custom('AD_SETTINGS').Output('AD_SETTINGS')], is_deprecated=True) - def get_motion_model_settings(self, pe_strength: float, attn_strength: float, other_strength: float, - motion_pe_stretch: int, - cap_initial_pe_length: int, interpolate_pe_to_length: int, - initial_pe_idx_offset: int, final_pe_idx_offset: int, - mask_motion_scale: torch.Tensor=None, min_motion_scale: float=1.0, max_motion_scale: float=1.0): - adjust_pe = AdjustGroup(AdjustPE(motion_pe_stretch=motion_pe_stretch, - cap_initial_pe_length=cap_initial_pe_length, interpolate_pe_to_length=interpolate_pe_to_length, - initial_pe_idx_offset=initial_pe_idx_offset, final_pe_idx_offset=final_pe_idx_offset)) - adjust_weight = AdjustGroup(AdjustWeight( - pe_MULT=pe_strength, - attn_MULT=attn_strength, - other_MULT=other_strength, - )) - motion_model_settings = AnimateDiffSettings( - adjust_pe=adjust_pe, - adjust_weight=adjust_weight, - mask_attn_scale=mask_motion_scale, - mask_attn_scale_min=min_motion_scale, - mask_attn_scale_max=max_motion_scale, - ) - - return (motion_model_settings,) - - -class AnimateDiffModelSettingsAdvancedAttnStrengthsDEPR: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "pe_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "attn_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "attn_q_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "attn_k_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "attn_v_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "attn_out_weight_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "attn_out_bias_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "other_strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.0001}), - "motion_pe_stretch": ("INT", {"default": 0, "min": 0, "step": 1}), - "cap_initial_pe_length": ("INT", {"default": 0, "min": 0, "step": 1}), - "interpolate_pe_to_length": ("INT", {"default": 0, "min": 0, "step": 1}), - "initial_pe_idx_offset": ("INT", {"default": 0, "min": 0, "step": 1}), - "final_pe_idx_offset": ("INT", {"default": 0, "min": 0, "step": 1}), - }, - "optional": { - "mask_motion_scale": ("MASK",), - "min_motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - "max_motion_scale": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - } - } - - RETURN_TYPES = ("AD_SETTINGS",) - CATEGORY = "" #"Animate Diff πŸŽ­πŸ…πŸ…“/β‘  Gen1 nodes β‘ /motion settings/experimental" - FUNCTION = "get_motion_model_settings" - DEPRECATED = True + def execute(cls, pe_strength: float, attn_strength: float, other_strength: float, motion_pe_stretch: int, cap_initial_pe_length: int, interpolate_pe_to_length: int, initial_pe_idx_offset: int, final_pe_idx_offset: int, mask_motion_scale: torch.Tensor=None, min_motion_scale: float=1.0, max_motion_scale: float=1.0): + adjust_pe = AdjustGroup(AdjustPE(motion_pe_stretch=motion_pe_stretch, cap_initial_pe_length=cap_initial_pe_length, interpolate_pe_to_length=interpolate_pe_to_length, initial_pe_idx_offset=initial_pe_idx_offset, final_pe_idx_offset=final_pe_idx_offset)) + adjust_weight = AdjustGroup(AdjustWeight(pe_MULT=pe_strength, attn_MULT=attn_strength, other_MULT=other_strength)) + motion_model_settings = AnimateDiffSettings(adjust_pe=adjust_pe, adjust_weight=adjust_weight, mask_attn_scale=mask_motion_scale, mask_attn_scale_min=min_motion_scale, mask_attn_scale_max=max_motion_scale) + return io.NodeOutput(motion_model_settings) - def get_motion_model_settings(self, pe_strength: float, attn_strength: float, - attn_q_strength: float, - attn_k_strength: float, - attn_v_strength: float, - attn_out_weight_strength: float, - attn_out_bias_strength: float, - other_strength: float, - motion_pe_stretch: int, - cap_initial_pe_length: int, interpolate_pe_to_length: int, - initial_pe_idx_offset: int, final_pe_idx_offset: int, - mask_motion_scale: torch.Tensor=None, min_motion_scale: float=1.0, max_motion_scale: float=1.0): - adjust_pe = AdjustGroup(AdjustPE(motion_pe_stretch=motion_pe_stretch, - cap_initial_pe_length=cap_initial_pe_length, interpolate_pe_to_length=interpolate_pe_to_length, - initial_pe_idx_offset=initial_pe_idx_offset, final_pe_idx_offset=final_pe_idx_offset)) - adjust_weight = AdjustGroup(AdjustWeight( - pe_MULT=pe_strength, - attn_MULT=attn_strength, - attn_q_MULT=attn_q_strength, - attn_k_MULT=attn_k_strength, - attn_v_MULT=attn_v_strength, - attn_out_weight_MULT=attn_out_weight_strength, - attn_out_bias_MULT=attn_out_bias_strength, - other_MULT=other_strength, - )) - motion_model_settings = AnimateDiffSettings( - adjust_pe=adjust_pe, - adjust_weight=adjust_weight, - mask_attn_scale=mask_motion_scale, - mask_attn_scale_min=min_motion_scale, - mask_attn_scale_max=max_motion_scale, - ) +class AnimateDiffModelSettingsAdvancedAttnStrengthsDEPR(io.ComfyNode): - return (motion_model_settings,) + @classmethod + def define_schema(cls): + return io.Schema(node_id='ADE_AnimateDiffModelSettingsAdvancedAttnStrengths', display_name='🚫[DEPR] Motion Model Settings (Adv. Attn) πŸŽ­πŸ…πŸ…“β‘ ', category='', inputs=[io.Float.Input('pe_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('attn_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('attn_q_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('attn_k_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('attn_v_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('attn_out_weight_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('attn_out_bias_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Float.Input('other_strength', default=1.0, max=10.0, min=0.0, step=0.0001), io.Int.Input('motion_pe_stretch', default=0, min=0, step=1), io.Int.Input('cap_initial_pe_length', default=0, min=0, step=1), io.Int.Input('interpolate_pe_to_length', default=0, min=0, step=1), io.Int.Input('initial_pe_idx_offset', default=0, min=0, step=1), io.Int.Input('final_pe_idx_offset', default=0, min=0, step=1), io.Mask.Input('mask_motion_scale', optional=True), io.Float.Input('min_motion_scale', optional=True, default=1.0, min=0.0, step=0.001), io.Float.Input('max_motion_scale', optional=True, default=1.0, min=0.0, step=0.001)], outputs=[io.Custom('AD_SETTINGS').Output('AD_SETTINGS')], is_deprecated=True) + @classmethod + def execute(cls, pe_strength: float, attn_strength: float, attn_q_strength: float, attn_k_strength: float, attn_v_strength: float, attn_out_weight_strength: float, attn_out_bias_strength: float, other_strength: float, motion_pe_stretch: int, cap_initial_pe_length: int, interpolate_pe_to_length: int, initial_pe_idx_offset: int, final_pe_idx_offset: int, mask_motion_scale: torch.Tensor=None, min_motion_scale: float=1.0, max_motion_scale: float=1.0): + adjust_pe = AdjustGroup(AdjustPE(motion_pe_stretch=motion_pe_stretch, cap_initial_pe_length=cap_initial_pe_length, interpolate_pe_to_length=interpolate_pe_to_length, initial_pe_idx_offset=initial_pe_idx_offset, final_pe_idx_offset=final_pe_idx_offset)) + adjust_weight = AdjustGroup(AdjustWeight(pe_MULT=pe_strength, attn_MULT=attn_strength, attn_q_MULT=attn_q_strength, attn_k_MULT=attn_k_strength, attn_v_MULT=attn_v_strength, attn_out_weight_MULT=attn_out_weight_strength, attn_out_bias_MULT=attn_out_bias_strength, other_MULT=other_strength)) + motion_model_settings = AnimateDiffSettings(adjust_pe=adjust_pe, adjust_weight=adjust_weight, mask_attn_scale=mask_motion_scale, mask_attn_scale_min=min_motion_scale, mask_attn_scale_max=max_motion_scale) + return io.NodeOutput(motion_model_settings) diff --git a/animatediff/nodes_extras.py b/animatediff/nodes_extras.py index 9be4b8e..d9d8acc 100644 --- a/animatediff/nodes_extras.py +++ b/animatediff/nodes_extras.py @@ -3,6 +3,8 @@ from typing import Union import torch from torch import Tensor +from comfy_api.latest import io + import folder_paths import nodes as comfy_nodes from comfy.model_patcher import ModelPatcher @@ -17,44 +19,36 @@ from .model_injection import get_vanilla_model_patcher from .cfg_extras import perturbed_attention_guidance_patch, rescale_cfg_patch -class AnimateDiffUnload: - def __init__(self) -> None: - pass - +class AnimateDiffUnload(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return {"required": {"model": ("MODEL",)}} - - RETURN_TYPES = ("MODEL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/extras" - FUNCTION = "unload_motion_modules" - - def unload_motion_modules(self, model: ModelPatcher): + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AnimateDiffUnload', + display_name='AnimateDiff Unload πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/extras', + inputs=[io.Model.Input('model')], + outputs=[io.Model.Output('MODEL')] + ) + @classmethod + def execute(cls, model: ModelPatcher): # return model clone with ejected params #model = eject_params_from_model(model) model = get_vanilla_model_patcher(model) - return (model.clone(),) + return io.NodeOutput(model.clone()) -class CheckpointLoaderSimpleWithNoiseSelect: +class CheckpointLoaderSimpleWithNoiseSelect(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "ckpt_name": (folder_paths.get_filename_list("checkpoints"), ), - "beta_schedule": (BetaSchedules.ALIAS_LIST, {"default": BetaSchedules.USE_EXISTING}, ) - }, - "optional": { - "use_custom_scale_factor": ("BOOLEAN", {"default": False}), - "scale_factor": ("FLOAT", {"default": 0.18215, "min": 0.0, "max": 1.0, "step": 0.00001}) - } - } - RETURN_TYPES = ("MODEL", "CLIP", "VAE") - FUNCTION = "load_checkpoint" - - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/extras" - - def load_checkpoint(self, ckpt_name, beta_schedule, output_vae=True, output_clip=True, use_custom_scale_factor=False, scale_factor=0.18215): + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='CheckpointLoaderSimpleWithNoiseSelect', + display_name='Load Checkpoint w/ Noise Select πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/extras', + inputs=[io.Combo.Input('ckpt_name', options=folder_paths.get_filename_list("checkpoints")), io.Combo.Input('beta_schedule', options=['autoselect', 'use existing', 'sqrt_linear (AnimateDiff)', 'linear (AnimateDiff-SDXL)', 'linear (HotshotXL/default)', 'avg(sqrt_linear,linear)', 'lcm avg(sqrt_linear,linear)', 'lcm', 'lcm[100_ots]', 'lcm >> sqrt_linear', 'sqrt', 'cosine', 'squaredcos_cap_v2'] , default='use existing'), io.Boolean.Input('use_custom_scale_factor', default=False, optional=True), io.Float.Input('scale_factor', default=0.18215, max=1.0, min=0.0, step=1e-05, optional=True)], + outputs=[io.Model.Output('MODEL'), io.Clip.Output('CLIP'), io.Vae.Output('VAE')] + ) + @classmethod + def execute(cls, ckpt_name, beta_schedule, output_vae=True, output_clip=True, use_custom_scale_factor=False, scale_factor=0.18215): ckpt_path = folder_paths.get_full_path("checkpoints", ckpt_name) out = load_checkpoint_guess_config(ckpt_path, output_vae=True, output_clip=True, embedding_directory=folder_paths.get_folder_paths("embeddings")) # register chosen beta schedule on model - convert to beta_schedule name recognized by ComfyUI @@ -63,66 +57,55 @@ class CheckpointLoaderSimpleWithNoiseSelect: out[0].model.model_sampling = new_model_sampling if use_custom_scale_factor: out[0].model.latent_format.scale_factor = scale_factor - return out + return io.NodeOutput(*out) -class EmptyLatentImageLarge: - def __init__(self, device="cpu"): - self.device = device - +class EmptyLatentImageLarge(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return {"required": { "width": ("INT", {"default": 512, "min": 64, "max": comfy_nodes.MAX_RESOLUTION, "step": 8}), - "height": ("INT", {"default": 512, "min": 64, "max": comfy_nodes.MAX_RESOLUTION, "step": 8}), - "batch_size": ("INT", {"default": 1, "min": 1, "max": 262144})}} - RETURN_TYPES = ("LATENT",) - FUNCTION = "generate" - - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/extras" - - def generate(self, width, height, batch_size=1): + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_EmptyLatentImageLarge', + display_name='Empty Latent Image (Big Batch) πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/extras', + inputs=[io.Int.Input('width', default=512, max=16384, min=64, step=8), io.Int.Input('height', default=512, max=16384, min=64, step=8), io.Int.Input('batch_size', default=1, max=262144, min=1)], + outputs=[io.Latent.Output('LATENT')] + ) + @classmethod + def execute(cls, width, height, batch_size=1): latent = torch.zeros([batch_size, 4, height // 8, width // 8]) - return ({"samples":latent}, ) + return io.NodeOutput({"samples":latent}) -class PerturbedAttentionGuidanceMultival: +class PerturbedAttentionGuidanceMultival(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "scale_multival": ("MULTIVAL",), - } - } - - RETURN_TYPES = ("MODEL",) - FUNCTION = "patch" - - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/extras" - - def patch(self, model: ModelPatcher, scale_multival: Union[float, Tensor]): + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PerturbedAttentionGuidanceMultival', + display_name='PerturbedAttnGuide [Multival] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/extras', + inputs=[io.Model.Input('model'), io.Custom('MULTIVAL').Input('scale_multival')], + outputs=[io.Model.Output('MODEL')] + ) + @classmethod + def execute(cls, model: ModelPatcher, scale_multival: Union[float, Tensor]): m = model.clone() m.set_model_sampler_post_cfg_function(perturbed_attention_guidance_patch(scale_multival)) - return (m,) + return io.NodeOutput(m) -class RescaleCFGMultival: +class RescaleCFGMultival(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "mult_multival": ("MULTIVAL",), - } - } - - RETURN_TYPES = ("MODEL",) - FUNCTION = "patch" - - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/extras" - - def patch(self, model: ModelPatcher, mult_multival: Union[float, Tensor]): + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_RescaleCFGMultival', + display_name='RescaleCFG [Multival] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/extras', + inputs=[io.Model.Input('model'), io.Custom('MULTIVAL').Input('mult_multival')], + outputs=[io.Model.Output('MODEL')] + ) + @classmethod + def execute(cls, model: ModelPatcher, mult_multival: Union[float, Tensor]): m = model.clone() m.set_model_sampler_cfg_function(rescale_cfg_patch(mult_multival)) - return (m, ) + return io.NodeOutput(m) diff --git a/animatediff/nodes_gen1.py b/animatediff/nodes_gen1.py index 1f694bb..c8ba9b3 100644 --- a/animatediff/nodes_gen1.py +++ b/animatediff/nodes_gen1.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from comfy.model_patcher import ModelPatcher from .ad_settings import AnimateDiffSettings @@ -13,33 +14,34 @@ from .sample_settings import SampleSettings from .sampling import outer_sample_wrapper, sliding_calc_cond_batch -class AnimateDiffLoaderGen1: +class AnimateDiffLoaderGen1(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "model_name": (get_available_motion_models(),), - "beta_schedule": (BetaSchedules.ALIAS_LIST, {"default": BetaSchedules.AUTOSELECT}), - #"apply_mm_groupnorm_hack": ("BOOLEAN", {"default": True}), - }, - "optional": { - "context_options": ("CONTEXT_OPTIONS",), - "motion_lora": ("MOTION_LORA",), - "ad_settings": ("AD_SETTINGS",), - "ad_keyframes": ("AD_KEYFRAMES",), - "sample_settings": ("SAMPLE_SETTINGS",), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "per_block": ("PER_BLOCK",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AnimateDiffLoaderGen1', + display_name='AnimateDiff Loader πŸŽ­πŸ…πŸ…“β‘ ', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘  Gen1 nodes β‘ ', + inputs=[ + io.Model.Input('model'), + io.Combo.Input('model_name', options=get_available_motion_models()), + io.Combo.Input('beta_schedule', options=['autoselect', 'use existing', 'sqrt_linear (AnimateDiff)', 'linear (AnimateDiff-SDXL)', 'linear (HotshotXL/default)', 'avg(sqrt_linear,linear)', 'lcm avg(sqrt_linear,linear)', 'lcm', 'lcm[100_ots]', 'lcm >> sqrt_linear', 'sqrt', 'cosine', 'squaredcos_cap_v2'], default='autoselect'), + io.Custom("CONTEXT_OPTIONS").Input('context_options', optional=True), + io.Custom("MOTION_LORA").Input('motion_lora', optional=True), + io.Custom("AD_SETTINGS").Input('ad_settings', optional=True), + io.Custom("AD_KEYFRAMES").Input('ad_keyframes', optional=True), + io.Custom("SAMPLE_SETTINGS").Input('sample_settings', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("PER_BLOCK").Input('per_block', optional=True), + ], + outputs=[ + io.Model.Output('MODEL'), + ], + ) - RETURN_TYPES = ("MODEL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘  Gen1 nodes β‘ " - FUNCTION = "load_mm_and_inject_params" - def load_mm_and_inject_params(self, + @classmethod + def execute(cls, model: ModelPatcher, model_name: str, beta_schedule: str,# apply_mm_groupnorm_hack: bool, context_options: ContextOptionsGroup=None, motion_lora: MotionLoraList=None, ad_settings: AnimateDiffSettings=None, @@ -107,4 +109,4 @@ class AnimateDiffLoaderGen1: model.add_object_patch("model_sampling", new_model_sampling) del motion_model - return (model,) + return io.NodeOutput(model,) diff --git a/animatediff/nodes_gen2.py b/animatediff/nodes_gen2.py index c11b535..2136d24 100644 --- a/animatediff/nodes_gen2.py +++ b/animatediff/nodes_gen2.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from typing import Union import torch @@ -17,26 +18,28 @@ from .sample_settings import SampleSettings from .sampling import outer_sample_wrapper, sliding_calc_cond_batch -class UseEvolvedSamplingNode: +class UseEvolvedSamplingNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model": ("MODEL",), - "beta_schedule": (BetaSchedules.ALIAS_LIST, {"default": BetaSchedules.AUTOSELECT}), - }, - "optional": { - "m_models": ("M_MODELS",), - "context_options": ("CONTEXT_OPTIONS",), - "sample_settings": ("SAMPLE_SETTINGS",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_UseEvolvedSampling', + display_name='Use Evolved Sampling πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘', + inputs=[ + io.Model.Input('model'), + io.Combo.Input('beta_schedule', options=['autoselect', 'use existing', 'sqrt_linear (AnimateDiff)', 'linear (AnimateDiff-SDXL)', 'linear (HotshotXL/default)', 'avg(sqrt_linear,linear)', 'lcm avg(sqrt_linear,linear)', 'lcm', 'lcm[100_ots]', 'lcm >> sqrt_linear', 'sqrt', 'cosine', 'squaredcos_cap_v2'], default='autoselect'), + io.Custom("M_MODELS").Input('m_models', optional=True), + io.Custom("CONTEXT_OPTIONS").Input('context_options', optional=True), + io.Custom("SAMPLE_SETTINGS").Input('sample_settings', optional=True), + ], + outputs=[ + io.Model.Output('MODEL'), + ], + ) - RETURN_TYPES = ("MODEL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘" - FUNCTION = "use_evolved_sampling" - def use_evolved_sampling(self, model: ModelPatcher, beta_schedule: str, m_models: MotionModelGroup=None, context_options: ContextOptionsGroup=None, + @classmethod + def execute(cls, model: ModelPatcher, beta_schedule: str, m_models: MotionModelGroup=None, context_options: ContextOptionsGroup=None, sample_settings: SampleSettings=None): model = model.clone() helper = ModelPatcherHelper(model) @@ -81,33 +84,35 @@ class UseEvolvedSamplingNode: model.add_object_patch("model_sampling", new_model_sampling) del m_models - return (model,) + return io.NodeOutput(model,) -class ApplyAnimateDiffModelNode: +class ApplyAnimateDiffModelNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "motion_model": ("MOTION_MODEL_ADE",), - "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": { - "motion_lora": ("MOTION_LORA",), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "ad_keyframes": ("AD_KEYFRAMES",), - "prev_m_models": ("M_MODELS",), - "per_block": ("PER_BLOCK",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ApplyAnimateDiffModel', + display_name='Apply AnimateDiff Model (Adv.) πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘', + inputs=[ + io.Custom("MOTION_MODEL_ADE").Input('motion_model'), + 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("MOTION_LORA").Input('motion_lora', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("AD_KEYFRAMES").Input('ad_keyframes', optional=True), + io.Custom("M_MODELS").Input('prev_m_models', optional=True), + io.Custom("PER_BLOCK").Input('per_block', optional=True), + ], + outputs=[ + io.Custom("M_MODELS").Output('M_MODELS'), + ], + ) - RETURN_TYPES = ("M_MODELS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘" - FUNCTION = "apply_motion_model" - def apply_motion_model(self, motion_model: MotionModelPatcher, start_percent: float=0.0, end_percent: float=1.0, + @classmethod + def execute(cls, motion_model: MotionModelPatcher, start_percent: float=0.0, end_percent: float=1.0, motion_lora: MotionLoraList=None, ad_keyframes: ADKeyframeGroup=None, scale_multival=None, effect_multival=None, per_block: AllPerBlocks=None, prev_m_models: MotionModelGroup=None,): @@ -136,85 +141,90 @@ class ApplyAnimateDiffModelNode: attachment.timestep_percent_range = (start_percent, end_percent) # add to beginning, so that after injection, it will be the earliest of prev_m_models to be run prev_m_models.add_to_start(mm=motion_model) - return (prev_m_models,) + return io.NodeOutput(prev_m_models,) -class ApplyAnimateDiffModelBasicNode: +class ApplyAnimateDiffModelBasicNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "motion_model": ("MOTION_MODEL_ADE",), - }, - "optional": { - "motion_lora": ("MOTION_LORA",), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "ad_keyframes": ("AD_KEYFRAMES",), - "per_block": ("PER_BLOCK",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ApplyAnimateDiffModelSimple', + display_name='Apply AnimateDiff Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘', + inputs=[ + io.Custom("MOTION_MODEL_ADE").Input('motion_model'), + io.Custom("MOTION_LORA").Input('motion_lora', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("AD_KEYFRAMES").Input('ad_keyframes', optional=True), + io.Custom("PER_BLOCK").Input('per_block', optional=True), + ], + outputs=[ + io.Custom("M_MODELS").Output('M_MODELS'), + ], + ) - RETURN_TYPES = ("M_MODELS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘" - FUNCTION = "apply_motion_model" - def apply_motion_model(self, + @classmethod + def execute(cls, motion_model: MotionModelPatcher, motion_lora: MotionLoraList=None, scale_multival=None, effect_multival=None, ad_keyframes=None, per_block: AllPerBlocks=None): # just a subset of normal ApplyAnimateDiffModelNode inputs - return ApplyAnimateDiffModelNode.apply_motion_model(self, motion_model, motion_lora=motion_lora, + return io.NodeOutput(*ApplyAnimateDiffModelNode.execute( motion_model, motion_lora=motion_lora, scale_multival=scale_multival, effect_multival=effect_multival, - ad_keyframes=ad_keyframes, per_block=per_block) + ad_keyframes=ad_keyframes, per_block=per_block).args) -class LoadAnimateDiffModelNode: +class LoadAnimateDiffModelNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model_name": (get_available_motion_models(),), - }, - "optional": { - "ad_settings": ("AD_SETTINGS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_LoadAnimateDiffModel', + display_name='Load AnimateDiff Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘', + inputs=[ + io.Combo.Input('model_name', options=get_available_motion_models()), + io.Custom("AD_SETTINGS").Input('ad_settings', optional=True), + ], + outputs=[ + io.Custom("MOTION_MODEL_ADE").Output('MOTION_MODEL'), + ], + ) - RETURN_TYPES = ("MOTION_MODEL_ADE",) - RETURN_NAMES = ("MOTION_MODEL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘" - FUNCTION = "load_motion_model" - def load_motion_model(self, model_name: str, ad_settings: AnimateDiffSettings=None): + @classmethod + def execute(cls, model_name: str, ad_settings: AnimateDiffSettings=None): # load motion module and motion settings, if included motion_model = load_motion_module_gen2(model_name=model_name, motion_model_settings=ad_settings) - return (motion_model,) + return io.NodeOutput(motion_model,) -class ADKeyframeNode: +class ADKeyframeNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ), - }, - "optional": { - "prev_ad_keyframes": ("AD_KEYFRAMES", ), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "per_block_replace": ("PER_BLOCK",), - "inherit_missing": ("BOOLEAN", {"default": True}, ), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AnimateDiffKeyframe', + display_name='AnimateDiff Keyframe πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“', + inputs=[ + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Custom("AD_KEYFRAMES").Input('prev_ad_keyframes', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("PER_BLOCK").Input('per_block_replace', optional=True), + io.Boolean.Input('inherit_missing', optional=True, default=True), + io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), + ], + outputs=[ + io.Custom("AD_KEYFRAMES").Output('AD_KEYFRAMES'), + ], + ) - RETURN_TYPES = ("AD_KEYFRAMES", ) - FUNCTION = "load_keyframe" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“" - def load_keyframe(self, + @classmethod + def execute(cls, start_percent: float, prev_ad_keyframes=None, scale_multival: Union[float, torch.Tensor]=None, effect_multival: Union[float, torch.Tensor]=None, per_block_replace: AllPerBlocks=None, @@ -229,4 +239,4 @@ class ADKeyframeNode: cameractrl_multival=cameractrl_multival, pia_input=pia_input, inherit_missing=inherit_missing, guarantee_steps=guarantee_steps) prev_ad_keyframes.add(keyframe) - return (prev_ad_keyframes,) + return io.NodeOutput(prev_ad_keyframes,) diff --git a/animatediff/nodes_lora.py b/animatediff/nodes_lora.py index e0789be..bc9e11c 100644 --- a/animatediff/nodes_lora.py +++ b/animatediff/nodes_lora.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from pathlib import Path import folder_paths @@ -9,24 +10,26 @@ from .utils_model import get_available_motion_loras, get_motion_lora_path from .motion_lora import MotionLoraInfo, MotionLoraList -class AnimateDiffLoraLoader: +class AnimateDiffLoraLoader(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "name": (get_available_motion_loras(),), - "strength": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001}), - }, - "optional": { - "prev_motion_lora": ("MOTION_LORA",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AnimateDiffLoRALoader', + display_name='Load AnimateDiff LoRA πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“', + inputs=[ + io.Combo.Input('name', options=get_available_motion_loras()), + io.Float.Input('strength', default=1.0, max=10.0, min=0.0, step=0.001), + io.Custom("MOTION_LORA").Input('prev_motion_lora', optional=True), + ], + outputs=[ + io.Custom("MOTION_LORA").Output('MOTION_LORA'), + ], + ) - RETURN_TYPES = ("MOTION_LORA",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“" - FUNCTION = "load_motion_lora" - def load_motion_lora(self, name: str, strength: float, prev_motion_lora: MotionLoraList=None, lora_name: str=None): + @classmethod + def execute(cls, name: str, strength: float, prev_motion_lora: MotionLoraList=None, lora_name: str=None): if prev_motion_lora is None: prev_motion_lora = MotionLoraList() else: @@ -41,4 +44,4 @@ class AnimateDiffLoraLoader: lora_info = MotionLoraInfo(name=name, strength=strength) prev_motion_lora.add_lora(lora_info) - return (prev_motion_lora,) + return io.NodeOutput(prev_motion_lora,) diff --git a/animatediff/nodes_multival.py b/animatediff/nodes_multival.py index ae3cf1a..1f28f43 100644 --- a/animatediff/nodes_multival.py +++ b/animatediff/nodes_multival.py @@ -4,6 +4,8 @@ from typing import Union import torch from torch import Tensor +from comfy_api.latest import io + from .utils_motion import create_multival_combo, linear_conversion, normalize_min_max, extend_to_batch_size, extend_list_to_batch_size @@ -13,45 +15,33 @@ class ScaleType: LIST = [ABSOLUTE, RELATIVE] -class MultivalDynamicNode: +class MultivalDynamicNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "float_val": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001},), - }, - "optional": { - "mask_optional": ("MASK",), - }, - } - - RETURN_TYPES = ("MULTIVAL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/multival" - FUNCTION = "create_multival" - - def create_multival(self, float_val: Union[float, list[float]]=1.0, mask_optional: Tensor=None): - return (create_multival_combo(float_val=float_val, mask_optional=mask_optional),) - - -class MultivalScaledMaskNode: + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_MultivalDynamic', + display_name='Multival πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/multival', + inputs=[io.Float.Input('float_val', default=1.0, min=0.0, step=0.001), io.Mask.Input('mask_optional', optional=True)], + outputs=[io.Custom('MULTIVAL').Output('MULTIVAL')] + ) @classmethod - def INPUT_TYPES(s): - return { - "required": { - "min_float_val": ("FLOAT", {"default": 0.0, "min": 0.0, "step": 0.001}), - "max_float_val": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - "mask": ("MASK",), - }, - "optional": { - "scaling": (ScaleType.LIST,), - }, - } + def execute(cls, float_val: Union[float, list[float]]=1.0, mask_optional: Tensor=None): + return io.NodeOutput(create_multival_combo(float_val=float_val, mask_optional=mask_optional)) - RETURN_TYPES = ("MULTIVAL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/multival" - FUNCTION = "create_multival" - def create_multival(self, min_float_val: float, max_float_val: float, mask: Tensor, scaling: str=ScaleType.ABSOLUTE): +class MultivalScaledMaskNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_MultivalScaledMask', + display_name='Multival Scaled Mask πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/multival', + inputs=[io.Float.Input('min_float_val', default=0.0, min=0.0, step=0.001), io.Float.Input('max_float_val', default=1.0, min=0.0, step=0.001), io.Mask.Input('mask'), io.Combo.Input('scaling', options=['absolute', 'relative'] , optional=True)], + outputs=[io.Custom('MULTIVAL').Output('MULTIVAL')] + ) + @classmethod + def execute(cls, min_float_val: float, max_float_val: float, mask: Tensor, scaling: str=ScaleType.ABSOLUTE): lengths = [mask.shape[0]] iterable_inputs = [False, False] val_inputs = [min_float_val, max_float_val] @@ -77,84 +67,55 @@ class MultivalScaledMaskNode: mask = normalize_min_max(mask.clone(), new_min=min_float_val, new_max=max_float_val) else: raise ValueError(f"scaling '{scaling}' not recognized.") - return MultivalDynamicNode.create_multival(self, mask_optional=mask) + return io.NodeOutput(*MultivalDynamicNode.execute(mask_optional=mask).args) -class MultivalDynamicFloatInputNode: +class MultivalDynamicFloatInputNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "float_val": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001, "forceInput": True},), - }, - "optional": { - "mask_optional": ("MASK",), - }, - } - - RETURN_TYPES = ("MULTIVAL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/multival" - FUNCTION = "create_multival" - - def create_multival(self, float_val: Union[float, list[float]]=None, mask_optional: Tensor=None): - return MultivalDynamicNode.create_multival(self, float_val=float_val, mask_optional=mask_optional) - - -class MultivalDynamicFloatsNode: + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_MultivalDynamicFloatInput', + display_name='Multival [Float List] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/multival', + inputs=[io.Float.Input('float_val', default=1.0, force_input=True, max=10.0, min=0.0, step=0.001), io.Mask.Input('mask_optional', optional=True)], + outputs=[io.Custom('MULTIVAL').Output('MULTIVAL')] + ) @classmethod - def INPUT_TYPES(s): - return { - "required": { - "floats": ("FLOATS", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001},), - }, - "optional": { - "mask_optional": ("MASK",), - }, - } - - RETURN_TYPES = ("MULTIVAL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/multival" - FUNCTION = "create_multival" - - def create_multival(self, floats: Union[float, list[float]]=None, mask_optional: Tensor=None): - return MultivalDynamicNode.create_multival(self, float_val=floats, mask_optional=mask_optional) + def execute(cls, float_val: Union[float, list[float]]=None, mask_optional: Tensor=None): + return io.NodeOutput(*MultivalDynamicNode.execute(float_val=float_val, mask_optional=mask_optional).args) -class MultivalFloatNode: +class MultivalDynamicFloatsNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "float_val": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 10.0, "step": 0.001},), - }, - } - - RETURN_TYPES = ("MULTIVAL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/multival" - FUNCTION = "create_multival" - - def create_multival(self, float_val: Union[float, list[float]]=None): - return MultivalDynamicNode.create_multival(self, float_val=float_val) - - -class MultivalConvertToMaskNode: + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_MultivalDynamicFloats', + display_name='Multival [Floats] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/multival', + inputs=[io.Custom('FLOATS').Input('floats', extra_dict={'default': 1.0, 'min': 0.0, 'max': 10.0, 'step': 0.001}), io.Mask.Input('mask_optional', optional=True)], + outputs=[io.Custom('MULTIVAL').Output('MULTIVAL')] + ) @classmethod - def INPUT_TYPES(s): - return { - "required": { - "multival": ("MULTIVAL",), - }, - } - - RETURN_TYPES = ("MASK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/multival" - FUNCTION = "convert_multival_to_mask" + def execute(cls, floats: Union[float, list[float]]=None, mask_optional: Tensor=None): + return io.NodeOutput(*MultivalDynamicNode.execute(float_val=floats, mask_optional=mask_optional).args) - def convert_multival_to_mask(self, multival: Union[float, Tensor]): + +class MultivalConvertToMaskNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_MultivalConvertToMask', + display_name='Multival to Mask πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/multival', + inputs=[io.Custom('MULTIVAL').Input('multival')], + outputs=[io.Mask.Output('MASK')] + ) + @classmethod + def execute(cls, multival: Union[float, Tensor]): # if already tensor, assume is a valid mask if type(multival) == Tensor: - return (multival,) + return io.NodeOutput(multival) # otherwise, make a single 1x1 mask with the proper value shape = (1,1,1) converted_multival = torch.ones(shape) * multival - return (converted_multival,) + return io.NodeOutput(converted_multival) diff --git a/animatediff/nodes_per_block.py b/animatediff/nodes_per_block.py index bf78dab..b7e5383 100644 --- a/animatediff/nodes_per_block.py +++ b/animatediff/nodes_per_block.py @@ -1,6 +1,8 @@ from typing import Union from torch import Tensor +from comfy_api.latest import io + from .motion_module_ad import BlockType from .utils_model import ModelTypeSD from .utils_motion import AllPerBlocks, PerBlock, PerBlockId, extend_list_to_batch_size @@ -26,80 +28,63 @@ class ADBlockHolder: return not has_anything -class ADBlockComboNode: +class ADBlockComboNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ADBlockCombo', + display_name='AD Block πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('MULTIVAL').Input('effect', optional=True), io.Custom('MULTIVAL').Input('scale', optional=True)], + outputs=[io.Custom('AD_BLOCK').Output('AD_BLOCK')] + ) NodeID = 'ADE_ADBlockCombo' NodeName = 'AD Block πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "effect": ("MULTIVAL",), - "scale": ("MULTIVAL",), - }, - } - - RETURN_TYPES = ("AD_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "block_control" - - def block_control(self, effect: Union[float, Tensor, None]=None, scale: Union[float, Tensor, None]=None): + def execute(cls, effect: Union[float, Tensor, None]=None, scale: Union[float, Tensor, None]=None): scales = [scale, scale] block = ADBlockHolder(effect=effect, scales=scales) if block.is_empty(): block = None - return (block,) + return io.NodeOutput(block) -class ADBlockIndivNode: +class ADBlockIndivNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ADBlockIndiv', + display_name='AD Block+ πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('MULTIVAL').Input('effect', optional=True), io.Custom('MULTIVAL').Input('scale_0', optional=True), io.Custom('MULTIVAL').Input('scale_1', optional=True)], + outputs=[io.Custom('AD_BLOCK').Output('AD_BLOCK')] + ) NodeID = 'ADE_ADBlockIndiv' NodeName = 'AD Block+ πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "effect": ("MULTIVAL",), - "scale_0": ("MULTIVAL",), - "scale_1": ("MULTIVAL",), - } - } - - RETURN_TYPES = ("AD_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "block_control" - - def block_control(self, effect: Union[float, Tensor, None]=None, + def execute(cls, effect: Union[float, Tensor, None]=None, scale_0: Union[float, Tensor, None]=None, scale_1: Union[float, Tensor, None]=None): scales = [scale_0, scale_1] block = ADBlockHolder(effect=effect, scales=scales) if block.is_empty(): block = None - return (block,) + return io.NodeOutput(block) -class PerBlockHighLevelNode: +class PerBlockHighLevelNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PerBlockHighLevel', + display_name='AD Per Block πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('AD_BLOCK').Input('down', optional=True), io.Custom('AD_BLOCK').Input('mid', optional=True), io.Custom('AD_BLOCK').Input('up', optional=True)], + outputs=[io.Custom('PER_BLOCK').Output('PER_BLOCK')] + ) NodeID = 'ADE_PerBlockHighLevel' NodeName = 'AD Per Block πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "down": ("AD_BLOCK",), - "mid": ("AD_BLOCK",), - "up": ("AD_BLOCK",), - } - } - - RETURN_TYPES = ("PER_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "create_per_block" - - def create_per_block(self, + def execute(cls, down: Union[ADBlockHolder, None]=None, mid: Union[ADBlockHolder, None]=None, up: Union[ADBlockHolder, None]=None): @@ -114,35 +99,23 @@ class PerBlockHighLevelNode: blocks.append(PerBlock(id=id, effect=block.effect, scales=block.scales)) if len(blocks) == 0: blocks = None - return (AllPerBlocks(blocks),) + return io.NodeOutput(AllPerBlocks(blocks)) -class PerBlock_SD15_MidLevelNode: +class PerBlock_SD15_MidLevelNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PerBlock_SD15_MidLevel', + display_name='AD Per Block+ (SD1.5) πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('AD_BLOCK').Input('down_0', optional=True), io.Custom('AD_BLOCK').Input('down_1', optional=True), io.Custom('AD_BLOCK').Input('down_2', optional=True), io.Custom('AD_BLOCK').Input('down_3', optional=True), io.Custom('AD_BLOCK').Input('mid', optional=True), io.Custom('AD_BLOCK').Input('up_0', optional=True), io.Custom('AD_BLOCK').Input('up_1', optional=True), io.Custom('AD_BLOCK').Input('up_2', optional=True), io.Custom('AD_BLOCK').Input('up_3', optional=True)], + outputs=[io.Custom('PER_BLOCK').Output('PER_BLOCK')] + ) NodeID = 'ADE_PerBlock_SD15_MidLevel' NodeName = 'AD Per Block+ (SD1.5) πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "down_0": ("AD_BLOCK",), - "down_1": ("AD_BLOCK",), - "down_2": ("AD_BLOCK",), - "down_3": ("AD_BLOCK",), - "mid": ("AD_BLOCK",), - "up_0": ("AD_BLOCK",), - "up_1": ("AD_BLOCK",), - "up_2": ("AD_BLOCK",), - "up_3": ("AD_BLOCK",), - } - } - - RETURN_TYPES = ("PER_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "create_per_block" - - def create_per_block(self, + def execute(cls, down_0: Union[ADBlockHolder, None]=None, down_1: Union[ADBlockHolder, None]=None, down_2: Union[ADBlockHolder, None]=None, @@ -169,47 +142,23 @@ class PerBlock_SD15_MidLevelNode: blocks.append(PerBlock(id=id, effect=block.effect, scales=block.scales)) if len(blocks) == 0: blocks = None - return (AllPerBlocks(blocks, ModelTypeSD.SD1_5),) + return io.NodeOutput(AllPerBlocks(blocks, ModelTypeSD.SD1_5)) -class PerBlock_SD15_LowLevelNode: +class PerBlock_SD15_LowLevelNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PerBlock_SD15_LowLevel', + display_name='AD Per Block++ (SD1.5) πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('AD_BLOCK').Input('down_0__0', optional=True), io.Custom('AD_BLOCK').Input('down_0__1', optional=True), io.Custom('AD_BLOCK').Input('down_1__0', optional=True), io.Custom('AD_BLOCK').Input('down_1__1', optional=True), io.Custom('AD_BLOCK').Input('down_2__0', optional=True), io.Custom('AD_BLOCK').Input('down_2__1', optional=True), io.Custom('AD_BLOCK').Input('down_3__0', optional=True), io.Custom('AD_BLOCK').Input('down_3__1', optional=True), io.Custom('AD_BLOCK').Input('mid', optional=True), io.Custom('AD_BLOCK').Input('up_0__0', optional=True), io.Custom('AD_BLOCK').Input('up_0__1', optional=True), io.Custom('AD_BLOCK').Input('up_0__2', optional=True), io.Custom('AD_BLOCK').Input('up_1__0', optional=True), io.Custom('AD_BLOCK').Input('up_1__1', optional=True), io.Custom('AD_BLOCK').Input('up_1__2', optional=True), io.Custom('AD_BLOCK').Input('up_2__0', optional=True), io.Custom('AD_BLOCK').Input('up_2__1', optional=True), io.Custom('AD_BLOCK').Input('up_2__2', optional=True), io.Custom('AD_BLOCK').Input('up_3__0', optional=True), io.Custom('AD_BLOCK').Input('up_3__1', optional=True), io.Custom('AD_BLOCK').Input('up_3__2', optional=True)], + outputs=[io.Custom('PER_BLOCK').Output('PER_BLOCK')] + ) NodeID = 'ADE_PerBlock_SD15_LowLevel' NodeName = 'AD Per Block++ (SD1.5) πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "down_0__0": ("AD_BLOCK",), - "down_0__1": ("AD_BLOCK",), - "down_1__0": ("AD_BLOCK",), - "down_1__1": ("AD_BLOCK",), - "down_2__0": ("AD_BLOCK",), - "down_2__1": ("AD_BLOCK",), - "down_3__0": ("AD_BLOCK",), - "down_3__1": ("AD_BLOCK",), - "mid": ("AD_BLOCK",), - "up_0__0": ("AD_BLOCK",), - "up_0__1": ("AD_BLOCK",), - "up_0__2": ("AD_BLOCK",), - "up_1__0": ("AD_BLOCK",), - "up_1__1": ("AD_BLOCK",), - "up_1__2": ("AD_BLOCK",), - "up_2__0": ("AD_BLOCK",), - "up_2__1": ("AD_BLOCK",), - "up_2__2": ("AD_BLOCK",), - "up_3__0": ("AD_BLOCK",), - "up_3__1": ("AD_BLOCK",), - "up_3__2": ("AD_BLOCK",), - } - } - - RETURN_TYPES = ("PER_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "create_per_block" - - def create_per_block(self, + def execute(cls, down_0__0: Union[ADBlockHolder, None]=None, down_0__1: Union[ADBlockHolder, None]=None, down_1__0: Union[ADBlockHolder, None]=None, @@ -260,33 +209,28 @@ class PerBlock_SD15_LowLevelNode: blocks.append(PerBlock(id=id, effect=block.effect, scales=block.scales)) if len(blocks) == 0: blocks = None - return (AllPerBlocks(blocks, ModelTypeSD.SD1_5),) + return io.NodeOutput(AllPerBlocks(blocks, ModelTypeSD.SD1_5)) -class PerBlock_SD15_FromFloatsNode: +class PerBlock_SD15_FromFloatsNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PerBlock_SD15_FromFloats', + display_name='AD Per Block Floats (SD1.5) πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('FLOATS').Input('effect_21_floats', optional=True), io.Custom('FLOATS').Input('scale_21_floats', optional=True)], + outputs=[io.Custom('PER_BLOCK').Output('PER_BLOCK')], + description='Use Floats from Value Schedules to select SD1.5 effect/scale values for blocks.' + ) NodeID = 'ADE_PerBlock_SD15_FromFloats' NodeName = 'AD Per Block Floats (SD1.5) πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "effect_21_floats": ("FLOATS",), - "scale_21_floats": ("FLOATS",), - } - } - - RETURN_TYPES = ("PER_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "create_per_block" - DESCRIPTION = 'Use Floats from Value Schedules to select SD1.5 effect/scale values for blocks.' - - def create_per_block(self, + def execute(cls, effect_21_floats: Union[list[float], None]=None, scale_21_floats: Union[list[float], None]=None): if effect_21_floats is None and scale_21_floats is None: - return (AllPerBlocks(None, ModelTypeSD.SD1_5),) + return io.NodeOutput(AllPerBlocks(None, ModelTypeSD.SD1_5)) # SD1.5 has 21 blocks block_total = 21 holders = [ADBlockHolder() for _ in range(block_total)] @@ -298,33 +242,23 @@ class PerBlock_SD15_FromFloatsNode: scale_21_floats = extend_list_to_batch_size(scale_21_floats, block_total) for scale, holder in zip(scale_21_floats, holders): holder.scales = [scale, scale] - return PerBlock_SD15_LowLevelNode.create_per_block(self, *holders) + return PerBlock_SD15_LowLevelNode.execute(*holders) -class PerBlock_SDXL_MidLevelNode: +class PerBlock_SDXL_MidLevelNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PerBlock_SDXL_MidLevel', + display_name='AD Per Block+ (SDXL) πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('AD_BLOCK').Input('down_0', optional=True), io.Custom('AD_BLOCK').Input('down_1', optional=True), io.Custom('AD_BLOCK').Input('down_2', optional=True), io.Custom('AD_BLOCK').Input('mid', optional=True), io.Custom('AD_BLOCK').Input('up_0', optional=True), io.Custom('AD_BLOCK').Input('up_1', optional=True), io.Custom('AD_BLOCK').Input('up_2', optional=True)], + outputs=[io.Custom('PER_BLOCK').Output('PER_BLOCK')] + ) NodeID = 'ADE_PerBlock_SDXL_MidLevel' NodeName = 'AD Per Block+ (SDXL) πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "down_0": ("AD_BLOCK",), - "down_1": ("AD_BLOCK",), - "down_2": ("AD_BLOCK",), - "mid": ("AD_BLOCK",), - "up_0": ("AD_BLOCK",), - "up_1": ("AD_BLOCK",), - "up_2": ("AD_BLOCK",), - } - } - - RETURN_TYPES = ("PER_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "create_per_block" - - def create_per_block(self, + def execute(cls, down_0: Union[ADBlockHolder, None]=None, down_1: Union[ADBlockHolder, None]=None, down_2: Union[ADBlockHolder, None]=None, @@ -347,42 +281,23 @@ class PerBlock_SDXL_MidLevelNode: blocks.append(PerBlock(id=id, effect=block.effect, scales=block.scales)) if len(blocks) == 0: blocks = None - return (AllPerBlocks(blocks, ModelTypeSD.SDXL),) + return io.NodeOutput(AllPerBlocks(blocks, ModelTypeSD.SDXL)) -class PerBlock_SDXL_LowLevelNode: +class PerBlock_SDXL_LowLevelNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PerBlock_SDXL_LowLevel', + display_name='AD Per Block++ (SDXL) πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('AD_BLOCK').Input('down_0__0', optional=True), io.Custom('AD_BLOCK').Input('down_0__1', optional=True), io.Custom('AD_BLOCK').Input('down_1__0', optional=True), io.Custom('AD_BLOCK').Input('down_1__1', optional=True), io.Custom('AD_BLOCK').Input('down_2__0', optional=True), io.Custom('AD_BLOCK').Input('down_2__1', optional=True), io.Custom('AD_BLOCK').Input('mid', optional=True), io.Custom('AD_BLOCK').Input('up_0__0', optional=True), io.Custom('AD_BLOCK').Input('up_0__1', optional=True), io.Custom('AD_BLOCK').Input('up_0__2', optional=True), io.Custom('AD_BLOCK').Input('up_1__0', optional=True), io.Custom('AD_BLOCK').Input('up_1__1', optional=True), io.Custom('AD_BLOCK').Input('up_1__2', optional=True), io.Custom('AD_BLOCK').Input('up_2__0', optional=True), io.Custom('AD_BLOCK').Input('up_2__1', optional=True), io.Custom('AD_BLOCK').Input('up_2__2', optional=True)], + outputs=[io.Custom('PER_BLOCK').Output('PER_BLOCK')] + ) NodeID = 'ADE_PerBlock_SDXL_LowLevel' NodeName = 'AD Per Block++ (SDXL) πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "down_0__0": ("AD_BLOCK",), - "down_0__1": ("AD_BLOCK",), - "down_1__0": ("AD_BLOCK",), - "down_1__1": ("AD_BLOCK",), - "down_2__0": ("AD_BLOCK",), - "down_2__1": ("AD_BLOCK",), - "mid": ("AD_BLOCK",), - "up_0__0": ("AD_BLOCK",), - "up_0__1": ("AD_BLOCK",), - "up_0__2": ("AD_BLOCK",), - "up_1__0": ("AD_BLOCK",), - "up_1__1": ("AD_BLOCK",), - "up_1__2": ("AD_BLOCK",), - "up_2__0": ("AD_BLOCK",), - "up_2__1": ("AD_BLOCK",), - "up_2__2": ("AD_BLOCK",), - } - } - - RETURN_TYPES = ("PER_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "create_per_block" - - def create_per_block(self, + def execute(cls, down_0__0: Union[ADBlockHolder, None]=None, down_0__1: Union[ADBlockHolder, None]=None, down_1__0: Union[ADBlockHolder, None]=None, @@ -423,33 +338,28 @@ class PerBlock_SDXL_LowLevelNode: blocks.append(PerBlock(id=id, effect=block.effect, scales=block.scales)) if len(blocks) == 0: blocks = None - return (AllPerBlocks(blocks, ModelTypeSD.SDXL),) + return io.NodeOutput(AllPerBlocks(blocks, ModelTypeSD.SDXL)) -class PerBlock_SDXL_FromFloatsNode: +class PerBlock_SDXL_FromFloatsNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PerBlock_SDXL_FromFloats', + display_name='AD Per Block Floats (SDXL) πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/per block', + inputs=[io.Custom('FLOATS').Input('effect_16_floats', optional=True), io.Custom('FLOATS').Input('scale_16_floats', optional=True)], + outputs=[io.Custom('PER_BLOCK').Output('PER_BLOCK')], + description='Use Floats from Value Schedules to select SDXL effect/scale values for blocks.' + ) NodeID = 'ADE_PerBlock_SDXL_FromFloats' NodeName = 'AD Per Block Floats (SDXL) πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "effect_16_floats": ("FLOATS",), - "scale_16_floats": ("FLOATS",), - } - } - - RETURN_TYPES = ("PER_BLOCK",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/per block" - FUNCTION = "create_per_block" - DESCRIPTION = 'Use Floats from Value Schedules to select SDXL effect/scale values for blocks.' - - def create_per_block(self, + def execute(cls, effect_16_floats: Union[list[float], None]=None, scale_16_floats: Union[list[float], None]=None): if effect_16_floats is None and scale_16_floats is None: - return (AllPerBlocks(None, ModelTypeSD.SDXL),) + return io.NodeOutput(AllPerBlocks(None, ModelTypeSD.SDXL)) # SDXL has 16 blocks block_total = 16 holders = [ADBlockHolder() for _ in range(block_total)] @@ -461,4 +371,4 @@ class PerBlock_SDXL_FromFloatsNode: scale_16_floats = extend_list_to_batch_size(scale_16_floats, block_total) for scale, holder in zip(scale_16_floats, holders): holder.scales = [scale, scale] - return PerBlock_SDXL_LowLevelNode.create_per_block(self, *holders) + return PerBlock_SDXL_LowLevelNode.execute(*holders) diff --git a/animatediff/nodes_pia.py b/animatediff/nodes_pia.py index a47943a..9e18ce4 100644 --- a/animatediff/nodes_pia.py +++ b/animatediff/nodes_pia.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from typing import Union import torch from torch import Tensor @@ -107,41 +108,43 @@ class InputPIA_PaperPresets(InputPIA): return mask -class ApplyAnimateDiffPIAModel: +class ApplyAnimateDiffPIAModel(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "motion_model": ("MOTION_MODEL_ADE",), - "image": ("IMAGE",), - "vae": ("VAE",), - "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": { - "pia_input": ("PIA_INPUT",), - "motion_lora": ("MOTION_LORA",), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "ad_keyframes": ("AD_KEYFRAMES",), - "prev_m_models": ("M_MODELS",), - "per_block": ("PER_BLOCK",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ApplyAnimateDiffModelWithPIA', + display_name='Apply AnimateDiff-PIA Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA', + inputs=[ + io.Custom("MOTION_MODEL_ADE").Input('motion_model'), + io.Image.Input('image'), + io.Vae.Input('vae'), + 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("PIA_INPUT").Input('pia_input', optional=True), + io.Custom("MOTION_LORA").Input('motion_lora', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("AD_KEYFRAMES").Input('ad_keyframes', optional=True), + io.Custom("M_MODELS").Input('prev_m_models', optional=True), + io.Custom("PER_BLOCK").Input('per_block', optional=True), + ], + outputs=[ + io.Custom("M_MODELS").Output('M_MODELS'), + ], + ) - RETURN_TYPES = ("M_MODELS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA" - FUNCTION = "apply_motion_model" - def apply_motion_model(self, motion_model: MotionModelPatcher, image: Tensor, vae: VAE, + @classmethod + def execute(cls, motion_model: MotionModelPatcher, image: Tensor, vae: VAE, start_percent: float=0.0, end_percent: float=1.0, pia_input: InputPIA=None, motion_lora: MotionLoraList=None, ad_keyframes: ADKeyframeGroup=None, scale_multival=None, effect_multival=None, ref_multival=None, per_block=None, prev_m_models: MotionModelGroup=None,): - new_m_models = ApplyAnimateDiffModelNode.apply_motion_model(self, motion_model, start_percent=start_percent, end_percent=end_percent, + new_m_models = ApplyAnimateDiffModelNode.execute( motion_model, start_percent=start_percent, end_percent=end_percent, motion_lora=motion_lora, ad_keyframes=ad_keyframes, scale_multival=scale_multival, effect_multival=effect_multival, per_block=per_block, - prev_m_models=prev_m_models) + prev_m_models=prev_m_models).args # most recent added model will always be first in list; curr_model = new_m_models[0].models[0] # confirm that model is PIA @@ -154,115 +157,120 @@ class ApplyAnimateDiffPIAModel: pia_input = InputPIA_Multival(1.0) attachment.pia_input = pia_input #curr_model.pia_multival = ref_multival - return new_m_models + return io.NodeOutput(*new_m_models) -class LoadAnimateDiffAndInjectPIANode: - EXPERIMENTAL = True +class LoadAnimateDiffAndInjectPIANode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "model_name": (get_available_motion_models(),), - "motion_model": ("MOTION_MODEL_ADE",), - }, - "optional": { - "ad_settings": ("AD_SETTINGS",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_InjectPIAIntoAnimateDiffModel', + display_name='πŸ§ͺInject PIA into AnimateDiff Model πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA/πŸ§ͺexperimental', + inputs=[ + io.Combo.Input('model_name', options=get_available_motion_models()), + io.Custom("MOTION_MODEL_ADE").Input('motion_model'), + io.Custom("AD_SETTINGS").Input('ad_settings', optional=True), + ], + outputs=[ + io.Custom("MOTION_MODEL_ADE").Output('MOTION_MODEL'), + ], + is_experimental=True, + ) - RETURN_TYPES = ("MOTION_MODEL_ADE",) - RETURN_NAMES = ("MOTION_MODEL",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA/πŸ§ͺexperimental" - FUNCTION = "load_motion_model" - def load_motion_model(self, model_name: str, motion_model: MotionModelPatcher, ad_settings: AnimateDiffSettings=None): + @classmethod + def execute(cls, model_name: str, motion_model: MotionModelPatcher, ad_settings: AnimateDiffSettings=None): # make sure model actually has PIA conv_in if motion_model.model.conv_in is None: raise Exception("Passed-in motion model was expected to be PIA (contain conv_in), but did not.") # load motion module and motion settings, if included loaded_motion_model = load_motion_module_gen2(model_name=model_name, motion_model_settings=ad_settings) inject_pia_conv_in_into_model(motion_model=loaded_motion_model, w_pia=motion_model) - return (loaded_motion_model,) + return io.NodeOutput(loaded_motion_model,) -class PIA_ADKeyframeNode: +class PIA_ADKeyframeNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}, ), - }, - "optional": { - "prev_ad_keyframes": ("AD_KEYFRAMES", ), - "scale_multival": ("MULTIVAL",), - "effect_multival": ("MULTIVAL",), - "pia_input": ("PIA_INPUT",), - "inherit_missing": ("BOOLEAN", {"default": True}, ), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PIA_AnimateDiffKeyframe', + display_name='AnimateDiff-PIA Keyframe πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA', + inputs=[ + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Custom("AD_KEYFRAMES").Input('prev_ad_keyframes', optional=True), + io.Custom("MULTIVAL").Input('scale_multival', optional=True), + io.Custom("MULTIVAL").Input('effect_multival', optional=True), + io.Custom("PIA_INPUT").Input('pia_input', optional=True), + io.Boolean.Input('inherit_missing', optional=True, default=True), + io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=0), + ], + outputs=[ + io.Custom("AD_KEYFRAMES").Output('AD_KEYFRAMES'), + ], + ) - RETURN_TYPES = ("AD_KEYFRAMES", ) - FUNCTION = "load_keyframe" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA" - def load_keyframe(self, + @classmethod + def execute(cls, start_percent: float, prev_ad_keyframes=None, scale_multival: Union[float, torch.Tensor]=None, effect_multival: Union[float, torch.Tensor]=None, pia_input: InputPIA=None, inherit_missing: bool=True, guarantee_steps: int=1): - return ADKeyframeNode.load_keyframe(self, + return io.NodeOutput(*ADKeyframeNode.execute( start_percent=start_percent, prev_ad_keyframes=prev_ad_keyframes, scale_multival=scale_multival, effect_multival=effect_multival, pia_input=pia_input, inherit_missing=inherit_missing, guarantee_steps=guarantee_steps - ) + ).args) -class InputPIA_MultivalNode: +class InputPIA_MultivalNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "multival": ("MULTIVAL",), - }, - # "optional": { - # "effect_multival": ("MULTIVAL",), - # } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_InputPIA_Multival', + display_name='PIA Input [Multival] πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA', + inputs=[ + io.Custom("MULTIVAL").Input('multival'), + ], + outputs=[ + io.Custom("PIA_INPUT").Output('PIA_INPUT'), + ], + ) - RETURN_TYPES = ("PIA_INPUT",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA" - FUNCTION = "create_pia_input" - def create_pia_input(self, multival: Union[float, Tensor], effect_multival: Union[float, Tensor]=None): - return (InputPIA_Multival(multival, effect_multival),) - - -class InputPIA_PaperPresetsNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "preset": (PIA_RANGES._LIST_ALL,), - "batch_index": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX, "step": 1}), - }, - "optional": { - "mult_multival": ("MULTIVAL",), - "print_values": ("BOOLEAN", {"default": False},), - #"effect_multival": ("MULTIVAL",), - }, - } + def execute(cls, multival: Union[float, Tensor], effect_multival: Union[float, Tensor]=None): + return io.NodeOutput(InputPIA_Multival(multival, effect_multival),) - RETURN_TYPES = ("PIA_INPUT",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA" - FUNCTION = "create_pia_input" - def create_pia_input(self, preset: str, batch_index: int, mult_multival: Union[float, Tensor]=None, print_values: bool=False, effect_multival: Union[float, Tensor]=None): +class InputPIA_PaperPresetsNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_InputPIA_PaperPresets', + display_name='PIA Input [Paper Presets] πŸŽ­πŸ…πŸ…“β‘‘', + category='Animate Diff πŸŽ­πŸ…πŸ…“/β‘‘ Gen2 nodes β‘‘/PIA', + inputs=[ + io.Combo.Input('preset', options=['Animation (Small Motion)', 'Animation (Medium Motion)', 'Animation (Large Motion)', 'Loop (Small Motion)', 'Loop (Medium Motion)', 'Loop (Large Motion)', 'Style Transfer (Small Motion)', 'Style Transfer (Medium Motion)', 'Style Transfer (Large Motion)']), + io.Int.Input('batch_index', default=0, max=9007199254740991, min=-9007199254740991, step=1), + io.Custom("MULTIVAL").Input('mult_multival', optional=True), + io.Boolean.Input('print_values', optional=True, default=False), + ], + outputs=[ + io.Custom("PIA_INPUT").Output('PIA_INPUT'), + ], + ) + + + @classmethod + def execute(cls, preset: str, batch_index: int, mult_multival: Union[float, Tensor]=None, print_values: bool=False, effect_multival: Union[float, Tensor]=None): # verify preset exists - function will throw error if does not values = PIA_RANGES.get_preset(preset) if print_values: logger.info(f"PIA Preset '{preset}': {values}") - return (InputPIA_PaperPresets(preset=preset, index=batch_index, mult_multival=mult_multival, effect_multival=effect_multival),) + return io.NodeOutput(InputPIA_PaperPresets(preset=preset, index=batch_index, mult_multival=mult_multival, effect_multival=effect_multival),) diff --git a/animatediff/nodes_sample.py b/animatediff/nodes_sample.py index da0f6ce..5e3b4b7 100644 --- a/animatediff/nodes_sample.py +++ b/animatediff/nodes_sample.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io from typing import Union from torch import Tensor from collections.abc import Iterable @@ -15,94 +16,95 @@ from .cfg_extras import perturbed_attention_guidance_patch, rescale_cfg_patch, s from .logger import logger -class SampleSettingsNode: +class SampleSettingsNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}), - "noise_type": (NoiseLayerType.LIST,), - "seed_gen": (SeedNoiseGeneration.LIST,), - "seed_offset": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX}), - }, - "optional": { - "noise_layers": ("NOISE_LAYERS",), - "iteration_opts": ("ITERATION_OPTS",), - "seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}), - "adapt_denoise_steps": ("BOOLEAN", {"default": False},), - "custom_cfg": ("CUSTOM_CFG",), - "sigma_schedule": ("SIGMA_SCHEDULE",), - "image_inject": ("IMAGE_INJECT",), - "ancestral_opts": ("ANCESTRAL_OPTS",), - #"noise_calib": ("NOISE_CALIBRATION",), # TODO: bring back once NoiseCalibration is working - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AnimateDiffSamplingSettings', + display_name='Sample Settings πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“', + inputs=[ + io.Int.Input('batch_offset', default=0, max=9007199254740991, min=0), + io.Combo.Input('noise_type', options=NoiseLayerType.LIST), + io.Combo.Input('seed_gen', options=SeedNoiseGeneration.LIST), + io.Int.Input('seed_offset', default=0, max=9007199254740991, min=-9007199254740991), + io.Custom("NOISE_LAYERS").Input('noise_layers', optional=True), + io.Custom("ITERATION_OPTS").Input('iteration_opts', optional=True), + io.Int.Input('seed_override', optional=True, default=0, force_input=True, max=18446744073709551615, min=0), + io.Boolean.Input('adapt_denoise_steps', optional=True, default=False), + io.Custom("CUSTOM_CFG").Input('custom_cfg', optional=True), + io.Custom("SIGMA_SCHEDULE").Input('sigma_schedule', optional=True), + io.Custom("IMAGE_INJECT").Input('image_inject', optional=True), + io.Custom("ANCESTRAL_OPTS").Input('ancestral_opts', optional=True), + ], + outputs=[ + io.Custom("SAMPLE_SETTINGS").Output('settings'), + ], + ) - RETURN_TYPES = ("SAMPLE_SETTINGS",) - RETURN_NAMES = ("settings",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“" - FUNCTION = "create_settings" - def create_settings(self, batch_offset: int, noise_type: str, seed_gen: str, seed_offset: int, noise_layers: NoiseLayerGroup=None, + @classmethod + def execute(cls, batch_offset: int, noise_type: str, seed_gen: str, seed_offset: int, noise_layers: NoiseLayerGroup=None, iteration_opts: IterationOptions=None, seed_override: int=None, adapt_denoise_steps=False, custom_cfg: CustomCFGKeyframeGroup=None, sigma_schedule: SigmaSchedule=None, image_inject: NoisedImageToInjectGroup=None, - noise_calib: NoiseCalibration=None, ancestral_opts=None): + noise_calib: NoiseCalibration=None, ancestral_opts=None) -> io.NodeOutput: sampling_settings = SampleSettings(batch_offset=batch_offset, noise_type=noise_type, seed_gen=seed_gen, seed_offset=seed_offset, noise_layers=noise_layers, iteration_opts=iteration_opts, seed_override=seed_override, adapt_denoise_steps=adapt_denoise_steps, custom_cfg=custom_cfg, sigma_schedule=sigma_schedule, image_injection=image_inject, noise_calibration=noise_calib, ancestral_opts=ancestral_opts) - return (sampling_settings,) + return io.NodeOutput(sampling_settings,) -class AncestralOptionsNode: +class AncestralOptionsNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - #"batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}), - "noise_type": (NoiseLayerType.LIST_ANCESTRAL,), - #"determinism": (NoiseDeterminism._LIST,), - "seed_offset": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX}), - #"seed_gen_override": (SeedNoiseGeneration.LIST_WITH_OVERRIDE,), - }, - "optional": { - "seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_AncestralOptions', + display_name='Ancestral Options πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings', + inputs=[ + io.Combo.Input('noise_type', options=NoiseLayerType.LIST_ANCESTRAL), + io.Int.Input('seed_offset', default=0, max=9007199254740991, min=-9007199254740991), + io.Int.Input('seed_override', optional=True, default=0, force_input=True, max=18446744073709551615, min=0), + ], + outputs=[ + io.Custom("ANCESTRAL_OPTS").Output('ANCESTRAL_OPTS'), + ], + ) - RETURN_TYPES = ("ANCESTRAL_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings" - FUNCTION = "create_ancestral_opts" - def create_ancestral_opts(self, noise_type: str, seed_offset: int, determinism: str=NoiseDeterminism.DEFAULT, seed_override: int=None): + @classmethod + def execute(cls, noise_type: str, seed_offset: int, determinism: str=NoiseDeterminism.DEFAULT, seed_override: int=None) -> io.NodeOutput: if isinstance(seed_override, Iterable): raise Exception("Passing in a list of seeds for Ancestral Options is not supported at this time.") - return (AncestralOptions(noise_type=noise_type, determinism=determinism, seed_offset=seed_offset, seed_override=seed_override),) + return io.NodeOutput(AncestralOptions(noise_type=noise_type, determinism=determinism, seed_offset=seed_offset, seed_override=seed_override),) -class NoiseLayerReplaceNode: +class NoiseLayerReplaceNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}), - "noise_type": (NoiseLayerType.LIST,), - "seed_gen_override": (SeedNoiseGeneration.LIST_WITH_OVERRIDE,), - "seed_offset": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX}), - }, - "optional": { - "prev_noise_layers": ("NOISE_LAYERS",), - "mask_optional": ("MASK",), - "seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_NoiseLayerReplace', + display_name='Noise Layer [Replace] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/noise layers', + inputs=[ + io.Int.Input('batch_offset', default=0, max=9007199254740991, min=0), + io.Combo.Input('noise_type', options=NoiseLayerType.LIST), + io.Combo.Input('seed_gen_override', options=SeedNoiseGeneration.LIST_WITH_OVERRIDE), + io.Int.Input('seed_offset', default=0, max=9007199254740991, min=-9007199254740991), + io.Custom("NOISE_LAYERS").Input('prev_noise_layers', optional=True), + io.Mask.Input('mask_optional', optional=True), + io.Int.Input('seed_override', optional=True, default=0, force_input=True, max=18446744073709551615, min=0), + ], + outputs=[ + io.Custom("NOISE_LAYERS").Output('NOISE_LAYERS'), + ], + ) - RETURN_TYPES = ("NOISE_LAYERS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/noise layers" - FUNCTION = "create_layers" - def create_layers(self, batch_offset: int, noise_type: str, seed_gen_override: str, seed_offset: int, - prev_noise_layers: NoiseLayerGroup=None, mask_optional: Tensor=None, seed_override: int=None,): + @classmethod + def execute(cls, batch_offset: int, noise_type: str, seed_gen_override: str, seed_offset: int, + prev_noise_layers: NoiseLayerGroup=None, mask_optional: Tensor=None, seed_override: int=None,) -> io.NodeOutput: # prepare prev_noise_layers if prev_noise_layers is None: prev_noise_layers = NoiseLayerGroup() @@ -111,34 +113,36 @@ class NoiseLayerReplaceNode: layer = NoiseLayerReplace(noise_type=noise_type, batch_offset=batch_offset, seed_gen_override=seed_gen_override, seed_offset=seed_offset, seed_override=seed_override, mask=mask_optional) prev_noise_layers.add_to_start(layer) - return (prev_noise_layers,) + return io.NodeOutput(prev_noise_layers,) -class NoiseLayerAddNode: +class NoiseLayerAddNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}), - "noise_type": (NoiseLayerType.LIST,), - "seed_gen_override": (SeedNoiseGeneration.LIST_WITH_OVERRIDE,), - "seed_offset": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX}), - "noise_weight": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 10.0, "step": 0.001}), - }, - "optional": { - "prev_noise_layers": ("NOISE_LAYERS",), - "mask_optional": ("MASK",), - "seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_NoiseLayerAdd', + display_name='Noise Layer [Add] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/noise layers', + inputs=[ + io.Int.Input('batch_offset', default=0, max=9007199254740991, min=0), + io.Combo.Input('noise_type', options=NoiseLayerType.LIST), + io.Combo.Input('seed_gen_override', options=SeedNoiseGeneration.LIST_WITH_OVERRIDE), + io.Int.Input('seed_offset', default=0, max=9007199254740991, min=-9007199254740991), + io.Float.Input('noise_weight', default=0.5, max=10.0, min=0.0, step=0.001), + io.Custom("NOISE_LAYERS").Input('prev_noise_layers', optional=True), + io.Mask.Input('mask_optional', optional=True), + io.Int.Input('seed_override', optional=True, default=0, force_input=True, max=18446744073709551615, min=0), + ], + outputs=[ + io.Custom("NOISE_LAYERS").Output('NOISE_LAYERS'), + ], + ) - RETURN_TYPES = ("NOISE_LAYERS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/noise layers" - FUNCTION = "create_layers" - def create_layers(self, batch_offset: int, noise_type: str, seed_gen_override: str, seed_offset: int, + @classmethod + def execute(cls, batch_offset: int, noise_type: str, seed_gen_override: str, seed_offset: int, noise_weight: float, - prev_noise_layers: NoiseLayerGroup=None, mask_optional: Tensor=None, seed_override: int=None,): + prev_noise_layers: NoiseLayerGroup=None, mask_optional: Tensor=None, seed_override: int=None,) -> io.NodeOutput: # prepare prev_noise_layers if prev_noise_layers is None: prev_noise_layers = NoiseLayerGroup() @@ -148,35 +152,37 @@ class NoiseLayerAddNode: seed_override=seed_override, mask=mask_optional, noise_weight=noise_weight) prev_noise_layers.add_to_start(layer) - return (prev_noise_layers,) + return io.NodeOutput(prev_noise_layers,) -class NoiseLayerAddWeightedNode: +class NoiseLayerAddWeightedNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}), - "noise_type": (NoiseLayerType.LIST,), - "seed_gen_override": (SeedNoiseGeneration.LIST_WITH_OVERRIDE,), - "seed_offset": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX}), - "noise_weight": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 10.0, "step": 0.001}), - "balance_multiplier": ("FLOAT", {"default": 1.0, "min": 0.0, "step": 0.001}), - }, - "optional": { - "prev_noise_layers": ("NOISE_LAYERS",), - "mask_optional": ("MASK",), - "seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_NoiseLayerAddWeighted', + display_name='Noise Layer [Add Weighted] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/noise layers', + inputs=[ + io.Int.Input('batch_offset', default=0, max=9007199254740991, min=0), + io.Combo.Input('noise_type', options=NoiseLayerType.LIST), + io.Combo.Input('seed_gen_override', options=SeedNoiseGeneration.LIST_WITH_OVERRIDE), + io.Int.Input('seed_offset', default=0, max=9007199254740991, min=-9007199254740991), + io.Float.Input('noise_weight', default=0.5, max=10.0, min=0.0, step=0.001), + io.Float.Input('balance_multiplier', default=1.0, min=0.0, step=0.001), + io.Custom("NOISE_LAYERS").Input('prev_noise_layers', optional=True), + io.Mask.Input('mask_optional', optional=True), + io.Int.Input('seed_override', optional=True, default=0, force_input=True, max=18446744073709551615, min=0), + ], + outputs=[ + io.Custom("NOISE_LAYERS").Output('NOISE_LAYERS'), + ], + ) - RETURN_TYPES = ("NOISE_LAYERS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/noise layers" - FUNCTION = "create_layers" - def create_layers(self, batch_offset: int, noise_type: str, seed_gen_override: str, seed_offset: int, + @classmethod + def execute(cls, batch_offset: int, noise_type: str, seed_gen_override: str, seed_offset: int, noise_weight: float, balance_multiplier: float, - prev_noise_layers: NoiseLayerGroup=None, mask_optional: Tensor=None, seed_override: int=None,): + prev_noise_layers: NoiseLayerGroup=None, mask_optional: Tensor=None, seed_override: int=None,) -> io.NodeOutput: # prepare prev_noise_layers if prev_noise_layers is None: prev_noise_layers = NoiseLayerGroup() @@ -186,34 +192,36 @@ class NoiseLayerAddWeightedNode: seed_override=seed_override, mask=mask_optional, noise_weight=noise_weight, balance_multiplier=balance_multiplier) prev_noise_layers.add_to_start(layer) - return (prev_noise_layers,) + return io.NodeOutput(prev_noise_layers,) -class NoiseLayerNormalizedSumNode: +class NoiseLayerNormalizedSumNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}), - "noise_type": (NoiseLayerType.LIST,), - "seed_gen_override": (SeedNoiseGeneration.LIST_WITH_OVERRIDE,), - "seed_offset": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX}), - "noise_weight": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}), - }, - "optional": { - "prev_noise_layers": ("NOISE_LAYERS",), - "mask_optional": ("MASK",), - "seed_override": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "forceInput": True}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_NoiseLayerNormalizedSum', + display_name='Noise Layer [Normalized Sum] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/noise layers', + inputs=[ + io.Int.Input('batch_offset', default=0, max=9007199254740991, min=0), + io.Combo.Input('noise_type', options=NoiseLayerType.LIST), + io.Combo.Input('seed_gen_override', options=SeedNoiseGeneration.LIST_WITH_OVERRIDE), + io.Int.Input('seed_offset', default=0, max=9007199254740991, min=-9007199254740991), + io.Float.Input('noise_weight', default=0.5, max=1.0, min=0.0, step=0.001), + io.Custom("NOISE_LAYERS").Input('prev_noise_layers', optional=True), + io.Mask.Input('mask_optional', optional=True), + io.Int.Input('seed_override', optional=True, default=0, force_input=True, max=18446744073709551615, min=0), + ], + outputs=[ + io.Custom("NOISE_LAYERS").Output('NOISE_LAYERS'), + ], + ) - RETURN_TYPES = ("NOISE_LAYERS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/noise layers" - FUNCTION = "create_layers" - def create_layers(self, batch_offset: int, noise_type: str, seed_gen_override: str, seed_offset: int, + @classmethod + def execute(cls, batch_offset: int, noise_type: str, seed_gen_override: str, seed_offset: int, noise_weight: float, - prev_noise_layers: NoiseLayerGroup=None, mask_optional: Tensor=None, seed_override: int=None,): + prev_noise_layers: NoiseLayerGroup=None, mask_optional: Tensor=None, seed_override: int=None,) -> io.NodeOutput: # prepare prev_noise_layers if prev_noise_layers is None: prev_noise_layers = NoiseLayerGroup() @@ -223,63 +231,67 @@ class NoiseLayerNormalizedSumNode: seed_override=seed_override, mask=mask_optional, noise_weight=noise_weight) prev_noise_layers.add_to_start(layer) - return (prev_noise_layers,) + return io.NodeOutput(prev_noise_layers,) -class IterationOptionsNode: +class IterationOptionsNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "iterations": ("INT", {"default": 1, "min": 1}), - }, - "optional": { - "iter_batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}), - "iter_seed_offset": ("INT", {"default": 0, "min": BIGMIN, "max": BIGMAX}), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_IterationOptsDefault', + display_name='Default Iteration Options πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/iteration opts', + inputs=[ + io.Int.Input('iterations', default=1, min=1), + io.Int.Input('iter_batch_offset', optional=True, default=0, max=9007199254740991, min=0), + io.Int.Input('iter_seed_offset', optional=True, default=0, max=9007199254740991, min=-9007199254740991), + ], + outputs=[ + io.Custom("ITERATION_OPTS").Output('ITERATION_OPTS'), + ], + ) - RETURN_TYPES = ("ITERATION_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/iteration opts" - FUNCTION = "create_iter_opts" - def create_iter_opts(self, iterations: int, iter_batch_offset: int=0, iter_seed_offset: int=0): + @classmethod + def execute(cls, iterations: int, iter_batch_offset: int=0, iter_seed_offset: int=0) -> io.NodeOutput: iter_opts = IterationOptions(iterations=iterations, iter_batch_offset=iter_batch_offset, iter_seed_offset=iter_seed_offset) - return (iter_opts,) + return io.NodeOutput(iter_opts,) -class FreeInitOptionsNode: +class FreeInitOptionsNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "iterations": ("INT", {"default": 2, "min": 1}), - "filter": (FreeInitFilter.LIST,), - "d_s": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.001}), - "d_t": ("FLOAT", {"default": 0.25, "min": 0.0, "max": 1.0, "step": 0.001}), - "n_butterworth": ("INT", {"default": 4, "min": 1, "max": 100},), - "sigma_step": ("INT", {"default": 999, "min": 1, "max": 999}), - "apply_to_1st_iter": ("BOOLEAN", {"default": False}), - "init_type": (FreeInitOptions.LIST,) - }, - "optional": { - "iter_batch_offset": ("INT", {"default": 0, "min": 0, "max": BIGMAX}), - "iter_seed_offset": ("INT", {"default": 1, "min": BIGMIN, "max": BIGMAX}), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_IterationOptsFreeInit', + display_name='FreeInit Iteration Options πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/iteration opts', + inputs=[ + io.Int.Input('iterations', default=2, min=1), + io.Combo.Input('filter', options=FreeInitFilter.LIST), + io.Float.Input('d_s', default=0.25, max=1.0, min=0.0, step=0.001), + io.Float.Input('d_t', default=0.25, max=1.0, min=0.0, step=0.001), + io.Int.Input('n_butterworth', default=4, max=100, min=1), + io.Int.Input('sigma_step', default=999, max=999, min=1), + io.Boolean.Input('apply_to_1st_iter', default=False), + io.Combo.Input('init_type', options=FreeInitOptions.LIST), + io.Int.Input('iter_batch_offset', optional=True, default=0, max=9007199254740991, min=0), + io.Int.Input('iter_seed_offset', optional=True, default=1, max=9007199254740991, min=-9007199254740991), + ], + outputs=[ + io.Custom("ITERATION_OPTS").Output('ITERATION_OPTS'), + ], + ) - RETURN_TYPES = ("ITERATION_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/iteration opts" - FUNCTION = "create_iter_opts" - def create_iter_opts(self, iterations: int, filter: str, d_s: float, d_t: float, n_butterworth: int, + @classmethod + def execute(cls, iterations: int, filter: str, d_s: float, d_t: float, n_butterworth: int, sigma_step: int, apply_to_1st_iter: bool, init_type: str, - iter_batch_offset: int=0, iter_seed_offset: int=1): + iter_batch_offset: int=0, iter_seed_offset: int=1) -> io.NodeOutput: # init_type does nothing for now, not until I add more methods of applying low+high freq noise iter_opts = FreeInitOptions(iterations=iterations, step=sigma_step, apply_to_1st_iter=apply_to_1st_iter, filter=filter, d_s=d_s, d_t=d_t, n=n_butterworth, init_type=init_type, iter_batch_offset=iter_batch_offset, iter_seed_offset=iter_seed_offset) - return (iter_opts,) + return io.NodeOutput(iter_opts,) class NoiseCalibrationNode: @@ -291,7 +303,7 @@ class NoiseCalibrationNode: "thresh_freq": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}), }, } - + RETURN_TYPES = ("NOISE_CALIBRATION",) RETURN_NAMES = ("NOISE_CALIB",) CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings" @@ -302,131 +314,141 @@ class NoiseCalibrationNode: return (noise_calib,) -class CustomCFGNode: +class CustomCFGNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cfg_multival": ("MULTIVAL",), - }, - "optional": { - "cfg_extras": ("CFG_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CustomCFG', + display_name='Custom CFG [Multival] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg', + inputs=[ + io.Custom("MULTIVAL").Input('cfg_multival'), + io.Custom("CFG_EXTRAS").Input('cfg_extras', optional=True), + ], + outputs=[ + io.Custom("CUSTOM_CFG").Output('CUSTOM_CFG'), + ], + ) - RETURN_TYPES = ("CUSTOM_CFG",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg" - FUNCTION = "create_custom_cfg" - def create_custom_cfg(self, cfg_multival: Union[float, Tensor], cfg_extras: CFGExtrasGroup=None): + @classmethod + def execute(cls, cfg_multival: Union[float, Tensor], cfg_extras: CFGExtrasGroup=None) -> io.NodeOutput: keyframe = CustomCFGKeyframe(cfg_multival=cfg_multival, cfg_extras=cfg_extras) cfg_custom = CustomCFGKeyframeGroup() cfg_custom.add(keyframe) - return (cfg_custom,) + return io.NodeOutput(cfg_custom,) -class CustomCFGSimpleNode: +class CustomCFGSimpleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step": 0.1}), - }, - "optional": { - "cfg_extras": ("CFG_EXTRAS",), - }, - } - - RETURN_TYPES = ("CUSTOM_CFG",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg" - FUNCTION = "create_custom_cfg" - - def create_custom_cfg(self, cfg: float, cfg_extras: CFGExtrasGroup=None): - return CustomCFGNode.create_custom_cfg(self, cfg_multival=cfg, cfg_extras=cfg_extras) + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CustomCFGSimple', + display_name='Custom CFG πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg', + inputs=[ + io.Float.Input('cfg', default=8.0, max=100.0, min=0.0, step=0.1), + io.Custom("CFG_EXTRAS").Input('cfg_extras', optional=True), + ], + outputs=[ + io.Custom("CUSTOM_CFG").Output('CUSTOM_CFG'), + ], + ) -class CustomCFGKeyframeNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cfg_multival": ("MULTIVAL",), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - }, - "optional": { - "prev_custom_cfg": ("CUSTOM_CFG",), - "cfg_extras": ("CFG_EXTRAS",), - }, - } + def execute(cls, cfg: float, cfg_extras: CFGExtrasGroup=None) -> io.NodeOutput: + return CustomCFGNode.execute( cfg_multival=cfg, cfg_extras=cfg_extras) - RETURN_TYPES = ("CUSTOM_CFG",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg" - FUNCTION = "create_custom_cfg" - def create_custom_cfg(self, cfg_multival: Union[float, Tensor], start_percent: float=0.0, guarantee_steps: int=1, - prev_custom_cfg: CustomCFGKeyframeGroup=None, cfg_extras: CFGExtrasGroup=None): +class CustomCFGKeyframeNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CustomCFGKeyframe', + display_name='Custom CFG Keyframe [Multival] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg', + inputs=[ + io.Custom("MULTIVAL").Input('cfg_multival'), + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Int.Input('guarantee_steps', default=1, max=9007199254740991, min=0), + io.Custom("CUSTOM_CFG").Input('prev_custom_cfg', optional=True), + io.Custom("CFG_EXTRAS").Input('cfg_extras', optional=True), + ], + outputs=[ + io.Custom("CUSTOM_CFG").Output('CUSTOM_CFG'), + ], + ) + + + @classmethod + def execute(cls, cfg_multival: Union[float, Tensor], start_percent: float=0.0, guarantee_steps: int=1, + prev_custom_cfg: CustomCFGKeyframeGroup=None, cfg_extras: CFGExtrasGroup=None) -> io.NodeOutput: if not prev_custom_cfg: prev_custom_cfg = CustomCFGKeyframeGroup() prev_custom_cfg = prev_custom_cfg.clone() keyframe = CustomCFGKeyframe(cfg_multival=cfg_multival, start_percent=start_percent, guarantee_steps=guarantee_steps, cfg_extras=cfg_extras) prev_custom_cfg.add(keyframe) - return (prev_custom_cfg,) + return io.NodeOutput(prev_custom_cfg,) -class CustomCFGKeyframeSimpleNode: +class CustomCFGKeyframeSimpleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cfg": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step": 0.1}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 0, "max": BIGMAX}), - }, - "optional": { - "prev_custom_cfg": ("CUSTOM_CFG",), - "cfg_extras": ("CFG_EXTRAS",), - }, - } - - RETURN_TYPES = ("CUSTOM_CFG",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg" - FUNCTION = "create_custom_cfg" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CustomCFGKeyframeSimple', + display_name='Custom CFG Keyframe πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg', + inputs=[ + io.Float.Input('cfg', default=8.0, max=100.0, min=0.0, step=0.1), + io.Float.Input('start_percent', default=0.0, max=1.0, min=0.0, step=0.001), + io.Int.Input('guarantee_steps', default=1, max=9007199254740991, min=0), + io.Custom("CUSTOM_CFG").Input('prev_custom_cfg', optional=True), + io.Custom("CFG_EXTRAS").Input('cfg_extras', optional=True), + ], + outputs=[ + io.Custom("CUSTOM_CFG").Output('CUSTOM_CFG'), + ], + ) - def create_custom_cfg(self, cfg: float, start_percent: float=0.0, guarantee_steps: int=1, - prev_custom_cfg: CustomCFGKeyframeGroup=None, cfg_extras: CFGExtrasGroup=None): - return CustomCFGKeyframeNode.create_custom_cfg(self, cfg_multival=cfg, start_percent=start_percent, + + @classmethod + def execute(cls, cfg: float, start_percent: float=0.0, guarantee_steps: int=1, + prev_custom_cfg: CustomCFGKeyframeGroup=None, cfg_extras: CFGExtrasGroup=None) -> io.NodeOutput: + return CustomCFGKeyframeNode.execute( cfg_multival=cfg, start_percent=start_percent, guarantee_steps=guarantee_steps, prev_custom_cfg=prev_custom_cfg, cfg_extras=cfg_extras) -class CustomCFGKeyframeInterpolationNode: +class CustomCFGKeyframeInterpolationNode(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}), - "cfg_start": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step": 0.1}), - "cfg_end": ("FLOAT", {"default": 8.0, "min": 0.0, "max": 100.0, "step": 0.1}), - "interpolation": (InterpolationMethod._LIST, ), - "intervals": ("INT", {"default": 50, "min": 2, "max": 100, "step": 1}), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_custom_cfg": ("CUSTOM_CFG",), - "cfg_extras": ("CFG_EXTRAS",), - }, - } - - RETURN_TYPES = ("CUSTOM_CFG",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg" - FUNCTION = "create_custom_cfg" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CustomCFGKeyframeInterpolation', + display_name='Custom CFG Keyframes Interp. πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg', + 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('cfg_start', default=8.0, max=100.0, min=0.0, step=0.1), + io.Float.Input('cfg_end', default=8.0, max=100.0, min=0.0, step=0.1), + io.Combo.Input('interpolation', options=InterpolationMethod._LIST), + io.Int.Input('intervals', default=50, max=100, min=2, step=1), + io.Boolean.Input('print_keyframes', default=False), + io.Custom("CUSTOM_CFG").Input('prev_custom_cfg', optional=True), + io.Custom("CFG_EXTRAS").Input('cfg_extras', optional=True), + ], + outputs=[ + io.Custom("CUSTOM_CFG").Output('CUSTOM_CFG'), + ], + ) - def create_custom_cfg(self, + + @classmethod + def execute(cls, start_percent: float, end_percent: float, cfg_start: float, cfg_end: float, interpolation: str, intervals: int, prev_custom_cfg: CustomCFGKeyframeGroup=None, cfg_extras: CFGExtrasGroup=None, - print_keyframes=False): + print_keyframes=False) -> io.NodeOutput: if not prev_custom_cfg: prev_custom_cfg = CustomCFGKeyframeGroup() prev_custom_cfg = prev_custom_cfg.clone() @@ -442,33 +464,35 @@ class CustomCFGKeyframeInterpolationNode: prev_custom_cfg.add(CustomCFGKeyframe(cfg_multival=float(cfg), start_percent=percent, guarantee_steps=guarantee_steps, cfg_extras=cfg_extras)) if print_keyframes: logger.info(f"CustomCFGKeyframe - start_percent:{percent} = {cfg}") - return (prev_custom_cfg,) - + return io.NodeOutput(prev_custom_cfg,) -class CustomCFGKeyframeFromListNode: + +class CustomCFGKeyframeFromListNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "cfgs_float": ("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}), - "print_keyframes": ("BOOLEAN", {"default": False}), - }, - "optional": { - "prev_custom_cfg": ("CUSTOM_CFG",), - "cfg_extras": ("CFG_EXTRAS",), - } - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CustomCFGKeyframeFromList', + display_name='Custom CFG Keyframes From List πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg', + inputs=[ + io.Float.Input('cfgs_float', 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.Boolean.Input('print_keyframes', default=False), + io.Custom("CUSTOM_CFG").Input('prev_custom_cfg', optional=True), + io.Custom("CFG_EXTRAS").Input('cfg_extras', optional=True), + ], + outputs=[ + io.Custom("CUSTOM_CFG").Output('CUSTOM_CFG'), + ], + ) - RETURN_TYPES = ("CUSTOM_CFG",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/custom cfg" - FUNCTION = "create_custom_cfg" - def create_custom_cfg(self, cfgs_float: Union[float, list[float]], + @classmethod + def execute(cls, cfgs_float: Union[float, list[float]], start_percent: float, end_percent: float, prev_custom_cfg: CustomCFGKeyframeGroup=None, cfg_extras: CFGExtrasGroup=None, - print_keyframes=False): + print_keyframes=False) -> io.NodeOutput: if not prev_custom_cfg: prev_custom_cfg = CustomCFGKeyframeGroup() prev_custom_cfg = prev_custom_cfg.clone() @@ -479,7 +503,7 @@ class CustomCFGKeyframeFromListNode: else: raise Exception(f"strengths_float must be either an interable input or a float, but was {type(cfgs_float).__repr__}.") percents = InterpolationMethod.get_weights(num_from=start_percent, num_to=end_percent, length=len(cfgs_float), method=InterpolationMethod.LINEAR) - + is_first = True for percent, cfg in zip(percents, cfgs_float): guarantee_steps = 0 @@ -489,27 +513,29 @@ class CustomCFGKeyframeFromListNode: prev_custom_cfg.add(CustomCFGKeyframe(cfg_multival=float(cfg), start_percent=percent, guarantee_steps=guarantee_steps, cfg_extras=cfg_extras)) if print_keyframes: logger.info(f"CustomCFGKeyframe - start_percent:{percent} = {cfg}") - return (prev_custom_cfg,) + return io.NodeOutput(prev_custom_cfg,) -class CFGExtrasPAGNode: +class CFGExtrasPAGNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "scale_multival": ("MULTIVAL",), - }, - "optional": { - "prev_extras": ("CFG_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CFGExtrasPAG', + display_name='CFG Extrasβ—†PAG [Multival] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/cfg extras', + inputs=[ + io.Custom("MULTIVAL").Input('scale_multival'), + io.Custom("CFG_EXTRAS").Input('prev_extras', optional=True), + ], + outputs=[ + io.Custom("CFG_EXTRAS").Output('CFG_EXTRAS'), + ], + ) - RETURN_TYPES = ("CFG_EXTRAS",) - FUNCTION = "add_cfg_extras" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/cfg extras" - def add_cfg_extras(self, scale_multival: Union[float, Tensor], prev_extras: CFGExtrasGroup=None): + @classmethod + def execute(cls, scale_multival: Union[float, Tensor], prev_extras: CFGExtrasGroup=None) -> io.NodeOutput: if prev_extras is None: prev_extras = CFGExtrasGroup() prev_extras = prev_extras.clone() @@ -517,51 +543,55 @@ class CFGExtrasPAGNode: patch = perturbed_attention_guidance_patch(scale_multival) def call_extras(model_options: dict[str]): return set_model_options_post_cfg_function(model_options.copy(), patch) - + extra = CFGExtras(call_extras) prev_extras.add(extra) - return (prev_extras,) + return io.NodeOutput(prev_extras,) -class CFGExtrasPAGSimpleNode: +class CFGExtrasPAGSimpleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "scale": ("FLOAT", {"default": 3.0, "min": 0.0, "max": 100.0, "step": 0.1, "round": 0.01}), - }, - "optional": { - "prev_extras": ("CFG_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CFGExtrasPAGSimple', + display_name='CFG Extrasβ—†PAG πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/cfg extras', + inputs=[ + io.Float.Input('scale', default=3.0, max=100.0, min=0.0, round=0.01, step=0.1), + io.Custom("CFG_EXTRAS").Input('prev_extras', optional=True), + ], + outputs=[ + io.Custom("CFG_EXTRAS").Output('CFG_EXTRAS'), + ], + ) - RETURN_TYPES = ("CFG_EXTRAS",) - FUNCTION = "add_cfg_extras" - - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/cfg extras" - - def add_cfg_extras(self, scale: float, prev_extras: CFGExtrasGroup=None): - return CFGExtrasPAGNode.add_cfg_extras(self, scale_multival=scale, prev_extras=prev_extras) -class CFGExtrasRescaleCFGNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "mult_multival": ("MULTIVAL",), - }, - "optional": { - "prev_extras": ("CFG_EXTRAS",), - } - } + def execute(cls, scale: float, prev_extras: CFGExtrasGroup=None) -> io.NodeOutput: + return CFGExtrasPAGNode.execute(scale_multival=scale, prev_extras=prev_extras) - RETURN_TYPES = ("CFG_EXTRAS",) - FUNCTION = "add_cfg_extras" - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/cfg extras" +class CFGExtrasRescaleCFGNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CFGExtrasRescaleCFG', + display_name='CFG Extrasβ—†RescaleCFG [Multival] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/cfg extras', + inputs=[ + io.Custom("MULTIVAL").Input('mult_multival'), + io.Custom("CFG_EXTRAS").Input('prev_extras', optional=True), + ], + outputs=[ + io.Custom("CFG_EXTRAS").Output('CFG_EXTRAS'), + ], + ) - def add_cfg_extras(self, mult_multival: Union[float, Tensor], prev_extras: CFGExtrasGroup=None): + + + @classmethod + def execute(cls, mult_multival: Union[float, Tensor], prev_extras: CFGExtrasGroup=None) -> io.NodeOutput: if prev_extras is None: prev_extras = CFGExtrasGroup() prev_extras = prev_extras.clone() @@ -569,60 +599,64 @@ class CFGExtrasRescaleCFGNode: patch = rescale_cfg_patch(mult_multival) def call_extras(model_options: dict[str]): return set_model_options_sampler_cfg_function(model_options.copy(), patch) - + extra = CFGExtras(call_extras) prev_extras.add(extra) - return (prev_extras,) + return io.NodeOutput(prev_extras,) -class CFGExtrasRescaleCFGSimpleNode: +class CFGExtrasRescaleCFGSimpleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "multiplier": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 1.0, "step": 0.01}), - }, - "optional": { - "prev_extras": ("CFG_EXTRAS",), - }, - } + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_CFGExtrasRescaleCFGSimple', + display_name='CFG Extrasβ—†RescaleCFG πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/cfg extras', + inputs=[ + io.Float.Input('multiplier', default=0.7, max=1.0, min=0.0, step=0.01), + io.Custom("CFG_EXTRAS").Input('prev_extras', optional=True), + ], + outputs=[ + io.Custom("CFG_EXTRAS").Output('CFG_EXTRAS'), + ], + ) - RETURN_TYPES = ("CFG_EXTRAS",) - FUNCTION = "add_cfg_extras" - - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/cfg extras" - - def add_cfg_extras(self, multiplier: float, prev_extras: CFGExtrasGroup=None): - return CFGExtrasRescaleCFGNode.add_cfg_extras(self, mult_multival=multiplier, prev_extras=prev_extras) -class NoisedImageInjectionNode: @classmethod - def INPUT_TYPES(s): - return { - "required": { - "image": ("IMAGE", ), - "vae": ("VAE", ), - }, - "optional": { - "mask_opt": ("MASK", ), - "invert_mask": ("BOOLEAN", {"default": False}), - "resize_image": ("BOOLEAN", {"default": True}), - "start_percent": ("FLOAT", {"default": 0.0, "min": 0.0, "max": 1.0, "step": 0.001}), - "guarantee_steps": ("INT", {"default": 1, "min": 1, "max": BIGMAX}), - "img_inject_opts": ("IMAGE_INJECT_OPTIONS", ), - "strength_multival": ("MULTIVAL", ), - "prev_image_inject": ("IMAGE_INJECT", ), - }, - } - - RETURN_TYPES = ("IMAGE_INJECT",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/image inject" - FUNCTION = "create_image_inject" + def execute(cls, multiplier: float, prev_extras: CFGExtrasGroup=None) -> io.NodeOutput: + return CFGExtrasRescaleCFGNode.execute(mult_multival=multiplier, prev_extras=prev_extras) - def create_image_inject(self, image: Tensor, vae: VAE, invert_mask: bool, resize_image: bool, start_percent: float, + +class NoisedImageInjectionNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_NoisedImageInjection', + display_name='Image Injection πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/image inject', + inputs=[ + io.Image.Input('image'), + io.Vae.Input('vae'), + io.Mask.Input('mask_opt', optional=True), + io.Boolean.Input('invert_mask', optional=True, default=False), + io.Boolean.Input('resize_image', optional=True, default=True), + io.Float.Input('start_percent', optional=True, default=0.0, max=1.0, min=0.0, step=0.001), + io.Int.Input('guarantee_steps', optional=True, default=1, max=9007199254740991, min=1), + io.Custom("IMAGE_INJECT_OPTIONS").Input('img_inject_opts', optional=True), + io.Custom("MULTIVAL").Input('strength_multival', optional=True), + io.Custom("IMAGE_INJECT").Input('prev_image_inject', optional=True), + ], + outputs=[ + io.Custom("IMAGE_INJECT").Output('IMAGE_INJECT'), + ], + ) + + + @classmethod + def execute(cls, image: Tensor, vae: VAE, invert_mask: bool, resize_image: bool, start_percent: float, mask_opt: Tensor=None, strength_multival: Union[float, Tensor]=None, prev_image_inject: NoisedImageToInjectGroup=None, guarantee_steps=1, - img_inject_opts=None): + img_inject_opts=None) -> io.NodeOutput: if not prev_image_inject: prev_image_inject = NoisedImageToInjectGroup() prev_image_inject = prev_image_inject.clone() @@ -630,25 +664,26 @@ class NoisedImageInjectionNode: start_percent=start_percent, guarantee_steps=guarantee_steps, img_inject_opts=img_inject_opts) prev_image_inject.add(to_inject) - return (prev_image_inject,) + return io.NodeOutput(prev_image_inject,) -class NoisedImageInjectOptionsNode: +class NoisedImageInjectOptionsNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - }, - "optional": { - "composite_x": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), - "composite_y": ("INT", {"default": 0, "min": 0, "max": MAX_RESOLUTION, "step": 1}), - }, - } - - RETURN_TYPES = ("IMAGE_INJECT_OPTIONS",) - RETURN_NAMES = ("IMG_INJECT_OPTS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/image inject" - FUNCTION = "create_image_inject_opts" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_NoisedImageInjectOptions', + display_name='Image Injection Options πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/image inject', + inputs=[ + io.Int.Input('composite_x', optional=True, default=0, max=16384, min=0, step=1), + io.Int.Input('composite_y', optional=True, default=0, max=16384, min=0, step=1), + ], + outputs=[ + io.Custom("IMAGE_INJECT_OPTIONS").Output('IMG_INJECT_OPTS'), + ], + ) - def create_image_inject_opts(self, x=0, y=0): - return (NoisedImageInjectOptions(x=x, y=y),) + + @classmethod + def execute(cls, composite_x=0, composite_y=0) -> io.NodeOutput: + return io.NodeOutput(NoisedImageInjectOptions(x=composite_x, y=composite_y),) diff --git a/animatediff/nodes_scheduling.py b/animatediff/nodes_scheduling.py index 6f770e1..78339ca 100644 --- a/animatediff/nodes_scheduling.py +++ b/animatediff/nodes_scheduling.py @@ -1,157 +1,119 @@ from typing import Union +from comfy_api.latest import io + from .scheduling import (evaluate_prompt_schedule, evaluate_value_schedule, extract_cond_from_schedule, TensorInterp, PromptOptions, verify_key_value) from .utils_model import BIGMAX from .logger import logger -class PromptSchedulingLatentsNode: +class PromptSchedulingLatentsNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PromptSchedulingLatents', + display_name='Prompt Scheduling [Latents] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/scheduling', + inputs=[io.String.Input('prompts', default='', multiline=True), io.Clip.Input('clip'), io.Latent.Input('latent'), io.String.Input('prepend_text', default='', force_input=True, multiline=True, optional=True), io.String.Input('append_text', default='', force_input=True, multiline=True, optional=True), io.Custom('VALUES_REPLACE').Input('values_replace', optional=True), io.Boolean.Input('print_schedule', default=False, optional=True), io.Combo.Input('tensor_interp', options=['lerp', 'slerp'] , optional=True)], + outputs=[io.Conditioning.Output('CONDITIONING'), io.Latent.Output('LATENT')], + description='Encode a schedule of prompts with automatic interpolation, its length matching passed-in latent count.' + ) NodeID = 'ADE_PromptSchedulingLatents' NodeName = 'Prompt Scheduling [Latents] πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - "prompts": ("STRING", {"multiline": True, "default": ''}), - "clip": ("CLIP",), - "latent": ("LATENT",), - }, - "optional": { - "prepend_text": ("STRING", {"multiline": True, "default": '', "forceInput": True}), - "append_text": ("STRING", {"multiline": True, "default": '', "forceInput": True}), - "values_replace": ("VALUES_REPLACE",), - "print_schedule": ("BOOLEAN", {"default": False}), - "tensor_interp": (TensorInterp._LIST,) - }, - } - - RETURN_TYPES = ("CONDITIONING", "LATENT",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/scheduling" - FUNCTION = "create_schedule" - DESCRIPTION = 'Encode a schedule of prompts with automatic interpolation, its length matching passed-in latent count.' - - def create_schedule(self, prompts: str, clip, latent: dict, print_schedule=False, tensor_interp=TensorInterp.LERP, + def execute(cls, prompts: str, clip, latent: dict, print_schedule=False, tensor_interp=TensorInterp.LERP, prepend_text='', append_text='', values_replace=None): options = PromptOptions(interp=tensor_interp, prepend_text=prepend_text, append_text=append_text, values_replace=values_replace, print_schedule=print_schedule) conditioning = evaluate_prompt_schedule(prompts, latent["samples"].size(0), clip, options) - return (conditioning, latent) + return io.NodeOutput(conditioning, latent) -class PromptSchedulingNode: +class PromptSchedulingNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_PromptScheduling', + display_name='Prompt Scheduling πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/scheduling', + inputs=[io.String.Input('prompts', default='', multiline=True), io.Clip.Input('clip'), io.String.Input('prepend_text', default='', force_input=True, multiline=True, optional=True), io.String.Input('append_text', default='', force_input=True, multiline=True, optional=True), io.Custom('VALUES_REPLACE').Input('values_replace', optional=True), io.Boolean.Input('print_schedule', default=False, optional=True), io.Int.Input('max_length', default=0, max=9007199254740991, min=0, step=1, optional=True), io.Combo.Input('tensor_interp', options=['lerp', 'slerp'] , optional=True)], + outputs=[io.Conditioning.Output('CONDITIONING')], + description='Encode a schedule of prompts with automatic interpolation.' + ) NodeID = 'ADE_PromptScheduling' NodeName = 'Prompt Scheduling πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - "prompts": ("STRING", {"multiline": True, "default": ''}), - "clip": ("CLIP",), - }, - "optional": { - "prepend_text": ("STRING", {"multiline": True, "default": '', "forceInput": True}), - "append_text": ("STRING", {"multiline": True, "default": '', "forceInput": True}), - "values_replace": ("VALUES_REPLACE",), - "print_schedule": ("BOOLEAN", {"default": False}), - "max_length": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}), - "tensor_interp": (TensorInterp._LIST,) - }, - } - - RETURN_TYPES = ("CONDITIONING",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/scheduling" - FUNCTION = "create_schedule" - DESCRIPTION = 'Encode a schedule of prompts with automatic interpolation.' - - def create_schedule(self, prompts: str, clip, print_schedule=False, max_length: int=0, tensor_interp=TensorInterp.LERP, + def execute(cls, prompts: str, clip, print_schedule=False, max_length: int=0, tensor_interp=TensorInterp.LERP, prepend_text='', append_text='', values_replace=None): options = PromptOptions(interp=tensor_interp, prepend_text=prepend_text, append_text=append_text, values_replace=values_replace, print_schedule=print_schedule) conditioning = evaluate_prompt_schedule(prompts, max_length, clip, options) - return (conditioning,) + return io.NodeOutput(conditioning) -class ValueSchedulingLatentsNode: +class ValueSchedulingLatentsNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ValueSchedulingLatents', + display_name='Value Scheduling [Latents] πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/scheduling', + inputs=[io.String.Input('values', default='', multiline=True), io.Latent.Input('latent'), io.Boolean.Input('print_schedule', default=False, optional=True)], + outputs=[io.Float.Output('FLOAT'), io.Custom('FLOATS').Output('FLOATS'), io.Int.Output('INT'), io.Custom('INTS').Output('INTS')], + description='Create a list of values with automatic interpolation, its length matching passed-in latent count.' + ) NodeID = 'ADE_ValueSchedulingLatents' NodeName = 'Value Scheduling [Latents] πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - "values": ("STRING", {"multiline": True, "default": ""}), - "latent": ("LATENT",), - }, - "optional": { - "print_schedule": ("BOOLEAN", {"default": False}), - }, - } - - RETURN_TYPES = ("FLOAT", "FLOATS", "INT", "INTS") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/scheduling" - FUNCTION = "create_schedule" - DESCRIPTION = 'Create a list of values with automatic interpolation, its length matching passed-in latent count.' - - def create_schedule(self, values: str, latent: dict, print_schedule=False): + def execute(cls, values: str, latent: dict, print_schedule=False): float_vals = evaluate_value_schedule(values, latent["samples"].size(0)) int_vals = [round(x) for x in float_vals] if print_schedule: logger.info(f"ValueScheduling ({len(float_vals)} values):") for i, val in enumerate(float_vals): logger.info(f"{i} = {val}") - return (float_vals, float_vals, int_vals, int_vals) + return io.NodeOutput(float_vals, float_vals, int_vals, int_vals) -class ValueSchedulingNode: +class ValueSchedulingNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ValueScheduling', + display_name='Value Scheduling πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/scheduling', + inputs=[io.String.Input('values', default='', multiline=True), io.Boolean.Input('print_schedule', default=False, optional=True), io.Int.Input('max_length', default=0, max=9007199254740991, min=0, step=1, optional=True)], + outputs=[io.Float.Output('FLOAT'), io.Custom('FLOATS').Output('FLOATS'), io.Int.Output('INT'), io.Custom('INTS').Output('INTS')], + description='Create a list of values with automatic interpolation.' + ) NodeID = 'ADE_ValueScheduling' NodeName = 'Value Scheduling πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - "values": ("STRING", {"multiline": True, "default": ""}), - }, - "optional": { - "print_schedule": ("BOOLEAN", {"default": False}), - "max_length": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}), - }, - } - - RETURN_TYPES = ("FLOAT", "FLOATS", "INT", "INTS") - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/scheduling" - FUNCTION = "create_schedule" - DESCRIPTION = 'Create a list of values with automatic interpolation.' - - def create_schedule(self, values: str, max_length: int, print_schedule=False): + def execute(cls, values: str, max_length: int, print_schedule=False): float_vals = evaluate_value_schedule(values, max_length) int_vals = [round(x) for x in float_vals] if print_schedule: logger.info(f"ValueScheduling ({len(float_vals)} values):") for i, val in enumerate(float_vals): logger.info(f"{i} = {val}") - return (float_vals, float_vals, int_vals, int_vals) + return io.NodeOutput(float_vals, float_vals, int_vals, int_vals) -class AddValuesReplaceNode: +class AddValuesReplaceNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ValuesReplace', + display_name='Add Values Replace πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/scheduling', + inputs=[io.String.Input('value_key', default=''), io.Custom('FLOATS').Input('floats'), io.Custom('VALUES_REPLACE').Input('prev_replace', optional=True)], + outputs=[io.Custom('VALUES_REPLACE').Output('VALUES_REPLACE')], + description='Add a values schedule bound to a key to be used in Prompt Scheduling node.' + ) NodeID = 'ADE_ValuesReplace' NodeName = 'Add Values Replace πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - "value_key": ("STRING", {"default": ""}), - "floats": ("FLOATS",) - }, - "optional": { - "prev_replace": ("VALUES_REPLACE",), - }, - } - - RETURN_TYPES = ("VALUES_REPLACE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/scheduling" - FUNCTION = "add_values_replace" - DESCRIPTION = 'Add a values schedule bound to a key to be used in Prompt Scheduling node.' - - def add_values_replace(self, value_key: str, floats: Union[list[float]], prev_replace: dict=None): + def execute(cls, value_key: str, floats: Union[list[float]], prev_replace: dict=None): # key can only have a-z, A-Z, 0-9, and _ characters verify_key_value(key=value_key) # add/replace value floats @@ -161,49 +123,44 @@ class AddValuesReplaceNode: if value_key in prev_replace: logger.warn(f"Value key '{value_key}' is already present - corresponding floats value will be overriden.") prev_replace[value_key] = floats - return (prev_replace,) + return io.NodeOutput(prev_replace) -class FloatToFloatsNode: +class FloatToFloatsNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_FloatToFloats', + display_name='Float to Floats πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/scheduling', + inputs=[io.Float.Input('FLOAT', default=39, force_input=True)], + outputs=[io.Custom('FLOATS').Output('FLOATS')] + ) NodeID = 'ADE_FloatToFloats' NodeName = 'Float to Floats πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - "FLOAT": ("FLOAT", {"default": 39, "forceInput": True}), - }, - } - - RETURN_TYPES = ("FLOATS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/scheduling" - FUNCTION = "convert_to_floats" - - def convert_to_floats(self, FLOAT: Union[float, list[float]]): + def execute(cls, FLOAT: Union[float, list[float]]): floats = None if isinstance(FLOAT, float): floats = [float(FLOAT)] else: floats = list(FLOAT) - return (floats,) + return io.NodeOutput(floats) -class ConditionExtractionNode: +class ConditionExtractionNode(io.ComfyNode): + @classmethod + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_ConditionExtraction', + display_name='Condition Step Extraction πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/scheduling', + inputs=[io.Conditioning.Input('conditioning'), io.Int.Input('index', default=0, min=0, step=1)], + outputs=[io.Conditioning.Output('CONDITIONING')], + description='Extract a single conditioning step from a schedule of prompts.' + ) NodeID = 'ADE_ConditionExtraction' NodeName = 'Condition Step Extraction πŸŽ­πŸ…πŸ…“' @classmethod - def INPUT_TYPES(s): - return { - "required": { - "conditioning": ("CONDITIONING",), - "index": ("INT", {"default": 0, "min": 0, "step": 1}) - }, - } - - RETURN_TYPES = ("CONDITIONING",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/scheduling" - FUNCTION = "extract_conditioning" - DESCRIPTION = 'Extract a single conditioning step from a schedule of prompts.' - - def extract_conditioning(self, conditioning, index: int=0): + def execute(cls, conditioning, index: int=0): conditioning_step = extract_cond_from_schedule(conditioning, index) - return (conditioning_step,) + return io.NodeOutput(conditioning_step) diff --git a/animatediff/nodes_sigma_schedule.py b/animatediff/nodes_sigma_schedule.py index b401361..69657fb 100644 --- a/animatediff/nodes_sigma_schedule.py +++ b/animatediff/nodes_sigma_schedule.py @@ -1,3 +1,4 @@ +from comfy_api.latest import io import torch import comfy.samplers @@ -13,47 +14,54 @@ def validate_sigma_schedule_compatibility(schedule_A: SigmaSchedule, schedule_B: f"{name_b} has {schedule_B.total_sigmas()} sigmas (lcm={schedule_B.is_lcm()}).") -class SigmaScheduleNode: +class SigmaScheduleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "beta_schedule": (BetaSchedules.ALIAS_ACTIVE_LIST,), - } - } - - RETURN_TYPES = ("SIGMA_SCHEDULE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule" - FUNCTION = "get_sigma_schedule" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_SigmaSchedule', + display_name='Create Sigma Schedule πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule', + inputs=[ + io.Combo.Input('beta_schedule', options=BetaSchedules.ALIAS_ACTIVE_LIST), + ], + outputs=[ + io.Custom("SIGMA_SCHEDULE").Output('SIGMA_SCHEDULE'), + ], + ) - def get_sigma_schedule(self, beta_schedule: str): + + @classmethod + def execute(cls, beta_schedule: str) -> io.NodeOutput: model_type = ModelSamplingType.from_alias(ModelSamplingType.EPS) new_model_sampling = BetaSchedules._to_model_sampling(alias=beta_schedule, model_type=model_type) - return (SigmaSchedule(model_sampling=new_model_sampling, model_type=model_type),) + return io.NodeOutput(SigmaSchedule(model_sampling=new_model_sampling, model_type=model_type)) -class RawSigmaScheduleNode: +class RawSigmaScheduleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "raw_beta_schedule": (BetaSchedules.RAW_BETA_SCHEDULE_LIST,), - "linear_start": ("FLOAT", {"default": 0.00085, "min": 0.0, "max": 1.0, "step": 0.000001}), - "linear_end": ("FLOAT", {"default": 0.012, "min": 0.0, "max": 1.0, "step": 0.000001}), - #"cosine_s": ("FLOAT", {"default": 8e-3, "min": 0.0, "max": 1.0, "step": 0.000001}), - "sampling": (ModelSamplingType._FULL_LIST,), - "lcm_original_timesteps": ("INT", {"default": 50, "min": 1, "max": 1000}), - "zsnr": ("BOOLEAN", {"default": False}), - }, - } - - RETURN_TYPES = ("SIGMA_SCHEDULE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule" - FUNCTION = "get_sigma_schedule" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_RawSigmaSchedule', + display_name='Create Raw Sigma Schedule πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule', + inputs=[ + io.Combo.Input('raw_beta_schedule', options=BetaSchedules.RAW_BETA_SCHEDULE_LIST), + io.Float.Input('linear_start', default=0.00085, max=1.0, min=0.0, step=1e-06), + io.Float.Input('linear_end', default=0.012, max=1.0, min=0.0, step=1e-06), + io.Combo.Input('sampling', options=ModelSamplingType._FULL_LIST), + io.Int.Input('lcm_original_timesteps', default=50, max=1000, min=1), + io.Boolean.Input('zsnr', default=False), + ], + outputs=[ + io.Custom("SIGMA_SCHEDULE").Output('SIGMA_SCHEDULE'), + ], + ) - def get_sigma_schedule(self, raw_beta_schedule: str, linear_start: float, linear_end: float,# cosine_s: float, - sampling: str, lcm_original_timesteps: int, zsnr: bool, lcm_zsnr: bool=None): + + @classmethod + def execute(cls, raw_beta_schedule: str, linear_start: float, linear_end: float,# cosine_s: float, + sampling: str, lcm_original_timesteps: int, zsnr: bool, lcm_zsnr: bool=None) -> io.NodeOutput: if lcm_zsnr is not None: zsnr = lcm_zsnr # from pathlib import Path @@ -64,55 +72,63 @@ class RawSigmaScheduleNode: new_config = ModelSamplingConfig(beta_schedule=raw_beta_schedule, linear_start=linear_start, linear_end=linear_end)#, given_betas=given_betas) if sampling != ModelSamplingType.LCM: lcm_original_timesteps=None - model_type = ModelSamplingType.from_alias(sampling) + model_type = ModelSamplingType.from_alias(sampling) new_model_sampling = BetaSchedules._to_model_sampling(alias=BetaSchedules.AUTOSELECT, model_type=model_type, config_override=new_config, original_timesteps=lcm_original_timesteps) if zsnr: SigmaSchedule.apply_zsnr(new_model_sampling=new_model_sampling) - return (SigmaSchedule(model_sampling=new_model_sampling, model_type=model_type),) + return io.NodeOutput(SigmaSchedule(model_sampling=new_model_sampling, model_type=model_type)) -class WeightedAverageSigmaScheduleNode: +class WeightedAverageSigmaScheduleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "schedule_A": ("SIGMA_SCHEDULE",), - "schedule_B": ("SIGMA_SCHEDULE",), - "weight_A": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}), - }, - } - - RETURN_TYPES = ("SIGMA_SCHEDULE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule" - FUNCTION = "get_sigma_schedule" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_SigmaScheduleWeightedAverage', + display_name='Sigma Schedule Weighted Mean πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule', + inputs=[ + io.Custom("SIGMA_SCHEDULE").Input('schedule_A'), + io.Custom("SIGMA_SCHEDULE").Input('schedule_B'), + io.Float.Input('weight_A', default=0.5, max=1.0, min=0.0, step=0.001), + ], + outputs=[ + io.Custom("SIGMA_SCHEDULE").Output('SIGMA_SCHEDULE'), + ], + ) - def get_sigma_schedule(self, schedule_A: SigmaSchedule, schedule_B: SigmaSchedule, weight_A: float): + + @classmethod + def execute(cls, schedule_A: SigmaSchedule, schedule_B: SigmaSchedule, weight_A: float) -> io.NodeOutput: validate_sigma_schedule_compatibility(schedule_A, schedule_B) new_sigmas = schedule_A.model_sampling.sigmas * weight_A + schedule_B.model_sampling.sigmas * (1-weight_A) combo_schedule = schedule_A.clone() combo_schedule.model_sampling.set_sigmas(new_sigmas) - return (combo_schedule,) + return io.NodeOutput(combo_schedule) -class InterpolatedWeightedAverageSigmaScheduleNode: +class InterpolatedWeightedAverageSigmaScheduleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "schedule_A": ("SIGMA_SCHEDULE",), - "schedule_B": ("SIGMA_SCHEDULE",), - "weight_A_Start": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}), - "weight_A_End": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}), - "interpolation": (InterpolationMethod._LIST,), - }, - } - - RETURN_TYPES = ("SIGMA_SCHEDULE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule" - FUNCTION = "get_sigma_schedule" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_SigmaScheduleWeightedAverageInterp', + display_name='Sigma Schedule Interp. Mean πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule', + inputs=[ + io.Custom("SIGMA_SCHEDULE").Input('schedule_A'), + io.Custom("SIGMA_SCHEDULE").Input('schedule_B'), + io.Float.Input('weight_A_Start', default=0.5, max=1.0, min=0.0, step=0.001), + io.Float.Input('weight_A_End', default=0.5, max=1.0, min=0.0, step=0.001), + io.Combo.Input('interpolation', options=InterpolationMethod._LIST), + ], + outputs=[ + io.Custom("SIGMA_SCHEDULE").Output('SIGMA_SCHEDULE'), + ], + ) - def get_sigma_schedule(self, schedule_A: SigmaSchedule, schedule_B: SigmaSchedule, - weight_A_Start: float, weight_A_End: float, interpolation: str): + + @classmethod + def execute(cls, schedule_A: SigmaSchedule, schedule_B: SigmaSchedule, + weight_A_Start: float, weight_A_End: float, interpolation: str) -> io.NodeOutput: validate_sigma_schedule_compatibility(schedule_A, schedule_B) # get reverse weights, since sigmas are currently reversed weights = InterpolationMethod.get_weights(num_from=weight_A_Start, num_to=weight_A_End, @@ -121,58 +137,65 @@ class InterpolatedWeightedAverageSigmaScheduleNode: new_sigmas = schedule_A.model_sampling.sigmas * weights + schedule_B.model_sampling.sigmas * (1.0-weights) combo_schedule = schedule_A.clone() combo_schedule.model_sampling.set_sigmas(new_sigmas) - return (combo_schedule,) + return io.NodeOutput(combo_schedule) -class SplitAndCombineSigmaScheduleNode: +class SplitAndCombineSigmaScheduleNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "schedule_Start": ("SIGMA_SCHEDULE",), - "schedule_End": ("SIGMA_SCHEDULE",), - "idx_split_percent": ("FLOAT", {"default": 0.5, "min": 0.0, "max": 1.0, "step": 0.001}) - }, - } - - RETURN_TYPES = ("SIGMA_SCHEDULE",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule" - FUNCTION = "get_sigma_schedule" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_SigmaScheduleSplitAndCombine', + display_name='Sigma Schedule Split Combine πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule', + inputs=[ + io.Custom("SIGMA_SCHEDULE").Input('schedule_Start'), + io.Custom("SIGMA_SCHEDULE").Input('schedule_End'), + io.Float.Input('idx_split_percent', default=0.5, max=1.0, min=0.0, step=0.001), + ], + outputs=[ + io.Custom("SIGMA_SCHEDULE").Output('SIGMA_SCHEDULE'), + ], + ) - def get_sigma_schedule(self, schedule_Start: SigmaSchedule, schedule_End: SigmaSchedule, idx_split_percent: float): + + @classmethod + def execute(cls, schedule_Start: SigmaSchedule, schedule_End: SigmaSchedule, idx_split_percent: float) -> io.NodeOutput: validate_sigma_schedule_compatibility(schedule_Start, schedule_End) # first, calculate index to act as split; get diff from 1.0 since sigmas are flipped at this stage idx = int((1.0-idx_split_percent) * schedule_Start.total_sigmas()) new_sigmas = torch.cat([schedule_End.model_sampling.sigmas[:idx], schedule_Start.model_sampling.sigmas[idx:]], dim=0) new_schedule = schedule_Start.clone() new_schedule.model_sampling.set_sigmas(new_sigmas) - return (new_schedule,) + return io.NodeOutput(new_schedule) -class SigmaScheduleToSigmasNode: +class SigmaScheduleToSigmasNode(io.ComfyNode): @classmethod - def INPUT_TYPES(s): - return { - "required": { - "sigma_schedule": ("SIGMA_SCHEDULE",), - "scheduler": (comfy.samplers.SCHEDULER_NAMES, ), - "steps": ("INT", {"default": 20, "min": 1, "max": 10000}), - "denoise": ("FLOAT", {"default": 1.0, "min": 0.0, "max": 1.0, "step": 0.01}), - }, - } - - RETURN_TYPES = ("SIGMAS",) - CATEGORY = "Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule" - FUNCTION = "get_sigmas" + def define_schema(cls) -> io.Schema: + return io.Schema( + node_id='ADE_SigmaScheduleToSigmas', + display_name='Sigma Schedule To Sigmas πŸŽ­πŸ…πŸ…“', + category='Animate Diff πŸŽ­πŸ…πŸ…“/sample settings/sigma schedule', + inputs=[ + io.Custom("SIGMA_SCHEDULE").Input('sigma_schedule'), + io.Combo.Input('scheduler', options=comfy.samplers.SCHEDULER_NAMES), + io.Int.Input('steps', default=20, max=10000, min=1), + io.Float.Input('denoise', default=1.0, max=1.0, min=0.0, step=0.01), + ], + outputs=[ + io.Sigmas.Output('SIGMAS'), + ], + ) - def get_sigmas(self, sigma_schedule: SigmaSchedule, scheduler: str, steps: int, denoise: float): + + @classmethod + def execute(cls, sigma_schedule: SigmaSchedule, scheduler: str, steps: int, denoise: float) -> io.NodeOutput: total_steps = steps if denoise < 1.0: if denoise <= 0.0: - return (torch.FloatTensor([]),) + return io.NodeOutput(torch.FloatTensor([])) total_steps = int(steps/denoise) sigmas = comfy.samplers.calculate_sigmas(sigma_schedule, scheduler, total_steps).cpu() sigmas = sigmas[-(steps + 1):] - return (sigmas, ) - \ No newline at end of file + return io.NodeOutput(sigmas)