Author SHA1 Message Date
Jedrzej KosinskiandAmp 50ea19fa90 Bump version to 1.6.0
Amp-Thread-ID: https://ampcode.com/threads/T-019fa859-4259-74d5-ac32-0e4101490f8f
Co-authored-by: Amp <amp@ampcode.com>
2026-07-28 04:39:02 -07:00
Jedrzej Kosinski 2a12eaaec0 Merge pull request #578 from Kosinkadink/refactor/v3-node-api
Migrate all nodes to the ComfyUI V3 API
2026-07-17 20:41:58 -07:00
Jedrzej Kosinski 8a0e706567 Migrate all nodes to the V3 API 2026-07-17 19:06:02 -07:00
Jedrzej Kosinski 13ed169c47 Merge pull request #577 from Kosinkadink/cleanup/remove-obsolete-frontend
Remove obsolete frontend extensions
2026-07-17 18:12:52 -07:00
Jedrzej Kosinski d0db79787d Use native node help documentation 2026-07-17 17:49:55 -07:00
Jedrzej Kosinski 2dc014a33e Remove obsolete frontend extensions 2026-07-17 16:11:14 -07:00
Jedrzej Kosinski d8d163cd90 Merge pull request #573 from Kosinkadink/fix/issues-569-572-cast-bias-weight
fix: update cast_bias_weight usage and fix motion model dtype loading
2026-03-29 18:39:20 -07:00
Jedrzej KosinskiandAmp 8c277d92bd fix: update cast_bias_weight usage and fix motion model dtype loading
- Fix #572: Use offloadable=True with cast_bias_weight in groupnorm_mm_forward
  to match ComfyUI's updated 3-return-value API, and call uncast_bias_weight
  after use for proper async-offload support.
- Fix #569: Move .to(dtype) call after load_state_dict across all model loading
  functions. Previously, the dtype cast was done before load_state_dict, which
  overwrote weights with the original float32 values from disk, causing Half vs
  Float mismatches at runtime when the UNet runs in float16. Affected functions:
  load_motion_module_gen1, load_motion_module_gen2, create_fresh_motion_module,
  create_fresh_encoder_only_model, inject_img_encoder_into_model,
  inject_pia_conv_in_into_model, inject_camera_encoder_into_model,
  load_hmreferenceadapter.

Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d3bef-9580-7798-aec3-7a009a0f62a4
2026-03-29 18:37:59 -07:00
Jedrzej Kosinski 90fb133120 Merge pull request #564 from Kosinkadink/develop
version bump
2025-08-05 21:06:05 -07:00
38 changed files with 2754 additions and 4928 deletions
+7 -4
View File
@@ -3,17 +3,20 @@ 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 import documentation
from .animatediff.nodes import AnimateDiffExtension
from .animatediff.dinklink import init_dinklink
if len(get_available_motion_models()) == 0:
logger.error(f"No motion models found. Please download one and place in: {folder_paths.get_folder_paths(Folders.ANIMATEDIFF_MODELS)}")
WEB_DIRECTORY = "./web"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
documentation.format_descriptions(NODE_CLASS_MAPPINGS)
__all__ = ["WEB_DIRECTORY"]
init_dinklink()
prepare_dinklink_register_definitions()
prepare_dinklink_motion_module_ad()
async def comfy_entrypoint() -> AnimateDiffExtension:
return AnimateDiffExtension()
+1 -1
View File
@@ -70,9 +70,9 @@ def load_hmreferenceadapter(model_name: str):
else:
ops = comfy.ops.manual_cast
hmref = HMReferenceAdapter(ops=ops)
hmref.to(comfy.model_management.unet_dtype())
hmref.to(comfy.model_management.unet_offload_device())
load_result = hmref.load_state_dict(state_dict, strict=True)
hmref.to(comfy.model_management.unet_dtype())
hmref_model = create_HMModelPatcher(model=hmref, load_device=comfy.model_management.get_torch_device(),
offload_device=comfy.model_management.unet_offload_device())
return hmref_model
-75
View File
@@ -1,75 +0,0 @@
from typing import Union
from .logger import logger
def image(src):
return f'<img src={src} style="width: 0px; min-width: 100%">'
def video(src):
return f'<video src={src} autoplay muted loop controls controlslist="nodownload noremoteplayback noplaybackrate" style="width: 0px; min-width: 100%" class="VHS_loopedvideo">'
def short_desc(desc):
return f'<div id=VHS_shortdesc style="font-size: .8em">{desc}</div>'
def coll(text: str):
return f"{text}_collapsed"
descriptions = {
}
sizes = ['1.4','1.2','1']
def as_html(entry, depth=0):
if isinstance(entry, dict):
size = 0.8 if depth < 2 else 1
html = ''
for k in entry:
if k == "collapsed":
continue
collapse_single = k.endswith("_collapsed")
if collapse_single:
name = k[:-len("_collapsed")]
else:
name = k
collapse_flag = ' VHS_precollapse' if entry.get("collapsed", False) or collapse_single else ''
html += f'<div vhs_title=\"{name}\" style=\"display: flex; font-size: {size}em\" class=\"VHS_collapse{collapse_flag}\"><div style=\"color: #AAA; height: 1.5em;\">[<span style=\"font-family: monospace\">-</span>]</div><div style=\"width: 100%\">{name}: {as_html(entry[k], depth=depth+1)}</div></div>'
return html
if isinstance(entry, list):
html = ''
for i in entry:
html += f'<div>{as_html(i, depth=depth)}</div>'
return html
return str(entry)
def register_description(node_id: str, desc: Union[list, dict]):
descriptions[node_id] = desc
def format_descriptions(nodes):
for k in descriptions:
if k.endswith("_collapsed"):
k = k[:-len("_collapsed")]
nodes[k].DESCRIPTION = as_html(descriptions[k])
# undocumented_nodes = []
# for k in nodes:
# if not hasattr(nodes[k], "DESCRIPTION"):
# undocumented_nodes.append(k)
# if len(undocumented_nodes) > 0:
# logger.info(f"Undocumented nodes: {undocumented_nodes}")
class DocHelper:
def __init__(self):
self.actual_dict = {}
def add(self, add_dict):
self.actual_dict.update(add_dict)
return self
def get(self):
return self.actual_dict
@staticmethod
def combine(*args):
docs = DocHelper()
for doc in args:
docs.add(doc)
return docs.get()
+7 -7
View File
@@ -841,9 +841,9 @@ def load_motion_module_gen1(model_name: str, model: ModelPatcher, motion_lora: M
mm_state_dict = apply_mm_settings(model_dict=mm_state_dict, mm_settings=motion_model_settings)
# initialize AnimateDiffModelWrapper
ad_wrapper = AnimateDiffModel(mm_state_dict=mm_state_dict, mm_info=mm_info)
ad_wrapper.to(model.model_dtype())
ad_wrapper.to(model.offload_device)
load_result = ad_wrapper.load_state_dict(mm_state_dict, strict=False)
ad_wrapper.to(model.model_dtype())
verify_load_result(load_result=load_result, mm_info=mm_info)
# wrap motion_module into a ModelPatcher, to allow motion lora patches
motion_model = create_MotionModelPatcher(model=ad_wrapper, load_device=model.load_device, offload_device=model.offload_device)
@@ -865,9 +865,9 @@ def load_motion_module_gen2(model_name: str, motion_model_settings: AnimateDiffS
mm_state_dict = apply_mm_settings(model_dict=mm_state_dict, mm_settings=motion_model_settings)
# initialize AnimateDiffModelWrapper
ad_wrapper = AnimateDiffModel(mm_state_dict=mm_state_dict, mm_info=mm_info)
ad_wrapper.to(comfy.model_management.unet_dtype())
ad_wrapper.to(comfy.model_management.unet_offload_device())
load_result = ad_wrapper.load_state_dict(mm_state_dict, strict=False)
ad_wrapper.to(comfy.model_management.unet_dtype())
verify_load_result(load_result=load_result, mm_info=mm_info)
# wrap motion_module into a ModelPatcher, to allow motion lora patches
motion_model = create_MotionModelPatcher(model=ad_wrapper, load_device=comfy.model_management.get_torch_device(),
@@ -907,34 +907,34 @@ def verify_load_result(load_result: IncompatibleKeys, mm_info: AnimateDiffInfo):
def create_fresh_motion_module(motion_model: MotionModelPatcher) -> MotionModelPatcher:
ad_wrapper = AnimateDiffModel(mm_state_dict=motion_model.model.state_dict(), mm_info=motion_model.model.mm_info)
ad_wrapper.to(comfy.model_management.unet_dtype())
ad_wrapper.to(comfy.model_management.unet_offload_device())
ad_wrapper.load_state_dict(motion_model.model.state_dict())
ad_wrapper.to(comfy.model_management.unet_dtype())
return create_MotionModelPatcher(model=ad_wrapper, load_device=comfy.model_management.get_torch_device(),
offload_device=comfy.model_management.unet_offload_device())
def create_fresh_encoder_only_model(motion_model: MotionModelPatcher) -> MotionModelPatcher:
ad_wrapper = EncoderOnlyAnimateDiffModel(mm_state_dict=motion_model.model.state_dict(), mm_info=motion_model.model.mm_info)
ad_wrapper.to(comfy.model_management.unet_dtype())
ad_wrapper.to(comfy.model_management.unet_offload_device())
ad_wrapper.load_state_dict(motion_model.model.state_dict(), strict=False)
ad_wrapper.to(comfy.model_management.unet_dtype())
return create_MotionModelPatcher(model=ad_wrapper, load_device=comfy.model_management.get_torch_device(),
offload_device=comfy.model_management.unet_offload_device())
def inject_img_encoder_into_model(motion_model: MotionModelPatcher, w_encoder: MotionModelPatcher):
motion_model.model.init_img_encoder()
motion_model.model.img_encoder.to(comfy.model_management.unet_dtype())
motion_model.model.img_encoder.to(comfy.model_management.unet_offload_device())
motion_model.model.img_encoder.load_state_dict(w_encoder.model.img_encoder.state_dict())
motion_model.model.img_encoder.to(comfy.model_management.unet_dtype())
def inject_pia_conv_in_into_model(motion_model: MotionModelPatcher, w_pia: MotionModelPatcher):
motion_model.model.init_conv_in(w_pia.model.state_dict())
motion_model.model.conv_in.to(comfy.model_management.unet_dtype())
motion_model.model.conv_in.to(comfy.model_management.unet_offload_device())
motion_model.model.conv_in.load_state_dict(w_pia.model.conv_in.state_dict())
motion_model.model.conv_in.to(comfy.model_management.unet_dtype())
motion_model.model.mm_info.mm_format = AnimateDiffFormat.PIA
@@ -956,9 +956,9 @@ def inject_camera_encoder_into_model(motion_model: MotionModelPatcher, camera_ct
# initialize CameraPoseEncoder on motion model, and load keys
camera_encoder = CameraPoseEncoder(channels=motion_model.model.layer_channels, nums_rb=2, ops=motion_model.model.ops).to(
device=comfy.model_management.unet_offload_device(),
dtype=comfy.model_management.unet_dtype()
)
camera_encoder.load_state_dict(camera_state_dict)
camera_encoder.to(dtype=comfy.model_management.unet_dtype())
camera_encoder.temporal_pe_max_len = get_position_encoding_max_len(camera_state_dict, mm_name=camera_ctrl_name, mm_format=AnimateDiffFormat.ANIMATEDIFF)
motion_model.model.set_camera_encoder(camera_encoder=camera_encoder)
# initialize qkv_merge on specific attention blocks, and load keys
+324 -411
View File
@@ -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,
]
+216 -224
View File
@@ -1,55 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -57,91 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -150,30 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -182,32 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -218,32 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -254,40 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -303,40 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -352,4 +344,4 @@ class WeightAdjustIndivAttnMultNode:
print_adjustment=print_adjustment
)
prev_weight_adjust.add(adjust)
return (prev_weight_adjust,)
return io.NodeOutput(prev_weight_adjust)
+87 -80
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
from typing import Union
import torch
@@ -16,43 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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
@@ -62,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
@@ -92,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:
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",),
"deprecation_warning": ("ADEWARN", {"text": "Experimental. Don't expect to work.", "warn_type": "experimental", "color": "#CFC"}),
}
}
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)},)
+236 -213
View File
@@ -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,87 +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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -348,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}")
@@ -388,63 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,
@@ -458,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
@@ -547,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),)
+187 -637
View File
@@ -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,64 +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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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
@@ -77,385 +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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
}
}
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",),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
}
}
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})
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
}
}
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:
@@ -465,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
@@ -476,292 +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}),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
}
}
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}),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
}
}
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}),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
}
}
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}),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
}
}
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",),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"optional": {
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated - use native ComfyUI nodes instead."}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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))
+98 -382
View File
@@ -1,445 +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:
@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",),
"deprecation_warning": ("ADEWARN", {"text": ""}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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)
+105 -393
View File
@@ -1,197 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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}, ),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -200,192 +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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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}, ),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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()
@@ -394,146 +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": {
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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},),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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)
+105 -448
View File
@@ -1,565 +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),),
},
"optional": {"deprecation_warning": ("ADEWARN", {"text": "Deprecated"})},
}
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),),
},
"optional": {"deprecation_warning": ("ADEWARN", {"text": "Deprecated"})},
}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated; use AnimateDiff Loader instead."}),
}
}
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}),
},
"optional": {"deprecation_warning": ("ADEWARN", {"text": "Deprecated. Use VHS Video Combine"})},
"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": {"gifs": previews}}
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",),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated"}),
}
}
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}),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated"}),
}
}
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}),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated"}),
}
}
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}),
"deprecation_warning": ("ADEWARN", {"text": "Deprecated"}),
}
}
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)
+62 -79
View File
@@ -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)
-3
View File
@@ -36,9 +36,6 @@ class ApplyAnimateDiffFancyVideo:
"prev_m_models": ("M_MODELS",),
"per_block": ("PER_BLOCK",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
RETURN_TYPES = ("M_MODELS",)
+27 -25
View File
@@ -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,)
+106 -108
View File
@@ -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,36 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,):
@@ -139,94 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 50}),
}
}
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,
@@ -241,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,)
+19 -19
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
from pathlib import Path
import folder_paths
@@ -9,27 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 30}),
}
}
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:
@@ -44,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,)
-3
View File
@@ -114,9 +114,6 @@ class ApplyAnimateDiffMotionCtrlModel:
"prev_m_models": ("M_MODELS",),
"per_block": ("PER_BLOCK",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
RETURN_TYPES = ("M_MODELS",)
+63 -120
View File
@@ -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,51 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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]
@@ -83,96 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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},),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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)
+114 -236
View File
@@ -1,7 +1,8 @@
from typing import Union
from torch import Tensor
from .documentation import short_desc, register_description, coll, DocHelper
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
@@ -27,85 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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):
@@ -120,36 +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",),
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,
@@ -176,48 +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",),
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,
@@ -268,44 +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",),
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
RETURN_TYPES = ("PER_BLOCK",)
CATEGORY = "Animate Diff 🎭🅐🅓/per block"
FUNCTION = "create_per_block"
Desc = [
short_desc('Use Floats from Value Schedules to select SD1.5 effect/scale values for blocks.'),
'SD1.5 Motion Modules contain 21 blocks:',
'idx 0 - start of down blocks (down_0__0)',
'idx 7 - end of down blocks (down_3__1)',
'idx 8 - mid block (mid)',
'idx 9 - start of up blocks (up_0__0)',
'idx 20 - end of up blocks (up_3__2)',
]
register_description(NodeID, Desc)
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)]
@@ -317,34 +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",),
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,
@@ -367,43 +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",),
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,
@@ -444,44 +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",),
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
RETURN_TYPES = ("PER_BLOCK",)
CATEGORY = "Animate Diff 🎭🅐🅓/per block"
FUNCTION = "create_per_block"
Desc = [
short_desc('Use Floats from Value Schedules to select SDXL effect/scale values for blocks.'),
'SDXL Motion Modules contain 16 blocks:',
'idx 0 - start of down blocks (down_0__0)',
'idx 5 - end of down blocks (down_2__1)',
'idx 6 - mid block (mid)',
'idx 7 - start of up blocks (up_0__0)',
'idx 15 - end of up blocks (up_2__2)',
]
register_description(NodeID, Desc)
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)]
@@ -493,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)
+109 -110
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
from typing import Union
import torch
from torch import Tensor
@@ -107,44 +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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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
@@ -157,121 +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:
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",),
"deprecation_warning": ("ADEWARN", {"text": "Experimental. Don't expect to work.", "warn_type": "experimental", "color": "#CFC"}),
}
}
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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),)
File diff suppressed because it is too large Load Diff
+92 -240
View File
@@ -1,253 +1,119 @@
from typing import Union
from .documentation import register_description, short_desc, coll, DocHelper
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
desc_values = {coll('values'): 'Write your values here.'}
desc_prompts = {coll('prompts'): 'Write your prompts here.'}
desc_clip = {'clip': 'CLIP to use for encoding prompts.'}
desc_latent = {'latent': 'Used to get the amount of frames (max_length) to use for scheduling.'}
desc_prepend_text = {'prepend_text': 'OPTIONAL, adds text before all prompts.'}
desc_append_text = {'append_text': 'OPTIONAL, adds text after all prompts.'}
desc_values_replace = {'values_replace': 'OPTIONAL, replaces keys from value_replace keys with provided value schedules. Keys in the prompt are written as `some_key`, surrounded by the ` characters.'}
desc_tensor_interp = {'tensor_interp': 'Selects method of interpolating prompt conds - defaults to lerp.'}
desc_print_schedule = {'print_schedule': 'When True, prints output values for each frame.'}
desc_max_length = {'max_length': 'Used to select the intended length of schedule. If set to 0, will use the largest index in the schedule as max_length, but will disable relative indexes (negative and decimal).'}
desc_floats = {'floats': 'List of floats, likely outputted by a Value Scheduling node.'}
desc_FLOAT = {'FLOAT': 'Float (or list of floats) to convert to FLOATS type.'}
desc_value_key = {'value_key': 'Key to use for value schedule in Prompt Scheduling node. Can only contain a-z, A-Z, 0-9, and _ characters. In Prompt Scheduling, keys can be referred to as `some_key`, where the key is surrounded by ` characters.'}
desc_prev_replace = {'prev_replace': 'OPTIONAL, other values_replace can be chained.'}
desc_input_conditioning = {'conditioning': 'Encoded prompts. The output of a Prompt Scheduling node.'}
desc_index = {'index': 'The index to extract. Must be within the range [0,N] where N is the length of scheduled prompts.'}
desc_output_conditioning_single = {'CONDITIONING': 'The single step conditioning from the schedule.'}
desc_output_conditioning = {'CONDITIONING': 'Encoded prompts.'}
desc_output_latent = {'LATENT': 'Unmodified input latents; can be used as pipe, or can be ignored.'}
desc_format_allowed_idxs = {'allowed idxs':
{'single': 'A positive integer (e.g. 0, 2) schedules value for frame. A negative integer (e.g. -1, -5) schedules value for frame from the end (-1 would be the last frame). ' +
'A decimal (e.g. 0.5, 1.0) selects frame based relative location in whole schedule (0.5 would be halfway, 1.0 would be last frame).',
'range': 'Using rules above, single:single chooses uninterpolated prompts from start idx (included) to end idx (excluded). Examples -> 0:12, 0:-5, 2:0.5',
'hold': 'Putting a colon after a single idx stops interpolation until the next provided index. Examples -> 0:, 0.5:, 16: '}
}
desc_format_prompt = [
'Scheduling supports two formats: JSON and pythonic.',
{'JSON': ['"idx": "your prompt here", ...'],
'pythonic': ['idx = "your prompt here", ...']},
'The idx is the index of the frame - first frame is 0, last frame is max_frames-1. An idx may be the following:',
desc_format_allowed_idxs,
'The prompts themselves should be surrounded by double quotes ("your prompt here"). Portions of prompts can use value schedules provided values_replace.',
{'JSON': ['"0": "blue rock on mountain",', '"16": "green rock in lake"'],
'pythonic': ['0 = "blue rock on mountain",', '16 = "green rock in lake"']}
]
desc_format_values = [
'Scheduling supports two formats: JSON and pythonic.',
{'JSON': ['"idx": float/int_value, ...'],
'pythonic': ['idx = float/int_value, ...']},
'The idx is the index of the frame - first frame is 0, last frame is max_frames-1. An idx may be the following:',
desc_format_allowed_idxs,
'The values can be written without any special formatting.',
{'JSON': ['"0": 1.0,', '"16": 1.3'],
'pythonic': ['0 = 1.0,', '16 = 1.3']}
]
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"
Desc = [
short_desc('Encode a schedule of prompts with automatic interpolation, its length matching passed-in latent count.'),
{'Format': desc_format_prompt},
{coll('Inputs'): DocHelper.combine(desc_prompts, desc_clip, desc_latent, desc_values_replace, desc_prepend_text, desc_append_text, desc_tensor_interp, desc_print_schedule)},
{coll('Outputs'): DocHelper.combine(desc_output_conditioning, desc_output_latent)}
]
register_description(NodeID, Desc)
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"
Desc = [
short_desc('Encode a schedule of prompts with automatic interpolation.'),
{'Format': desc_format_prompt},
{coll('Inputs'): DocHelper.combine(desc_prompts, desc_clip, desc_values_replace, desc_prepend_text, desc_append_text, desc_max_length, desc_tensor_interp, desc_print_schedule)},
{coll('Outputs'): DocHelper.combine(desc_output_conditioning)}
]
register_description(NodeID, Desc)
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
RETURN_TYPES = ("FLOAT", "FLOATS", "INT", "INTS")
CATEGORY = "Animate Diff 🎭🅐🅓/scheduling"
FUNCTION = "create_schedule"
Desc = [
short_desc('Create a list of values with automatic interpolation, its length matching passed-in latent count.'),
{'Format': desc_format_values},
{coll('Inputs'): DocHelper.combine(desc_values, desc_latent, desc_print_schedule)},
]
register_description(NodeID, Desc)
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
RETURN_TYPES = ("FLOAT", "FLOATS", "INT", "INTS")
CATEGORY = "Animate Diff 🎭🅐🅓/scheduling"
FUNCTION = "create_schedule"
Desc = [
short_desc('Create a list of values with automatic interpolation.'),
{'Format': desc_format_values},
{coll('Inputs'): DocHelper.combine(desc_values, desc_max_length, desc_print_schedule)},
]
register_description(NodeID, Desc)
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",),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
RETURN_TYPES = ("VALUES_REPLACE",)
CATEGORY = "Animate Diff 🎭🅐🅓/scheduling"
FUNCTION = "add_values_replace"
Desc = [
short_desc('Add a values schedule bound to a key to be used in Prompt Scheduling node.'),
{'Inputs': DocHelper.combine(desc_value_key, desc_floats, desc_prev_replace)},
]
register_description(NodeID, Desc)
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
@@ -257,58 +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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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"
Desc = [
short_desc('Extract a single conditioning step from a schedule of prompts.'),
{coll('Inputs'): DocHelper.combine(desc_input_conditioning, desc_index)},
{coll('Outputs'): DocHelper.combine(desc_output_conditioning)}
]
register_description(NodeID, Desc)
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)
+123 -115
View File
@@ -1,3 +1,4 @@
from comfy_api.latest import io
import torch
import comfy.samplers
@@ -13,50 +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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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
@@ -67,61 +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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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,
@@ -130,64 +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})
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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}),
},
"hidden": {
"autosize": ("ADEAUTOSIZE", {"padding": 0}),
}
}
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, )
return io.NodeOutput(sigmas)
+4 -1
View File
@@ -203,10 +203,13 @@ def groupnorm_mm_factory(params: InjectionParams, manual_cast=False):
input = rearrange(input, "(b f) c h w -> b c f h w", b=batched_conds)
if manual_cast:
weight, bias = comfy.ops.cast_bias_weight(self, input)
weight, bias, offload_stream = comfy.ops.cast_bias_weight(self, input, offloadable=True)
else:
weight, bias = self.weight, self.bias
offload_stream = None
input = group_norm(input, self.num_groups, weight, bias, self.eps)
if offload_stream is not None:
comfy.ops.uncast_bias_weight(self, weight, bias, offload_stream)
input = rearrange(input, "b c f h w -> (b f) c h w", b=batched_conds)
return input
return groupnorm_mm_forward
+2 -1
View File
@@ -1,7 +1,7 @@
[project]
name = "comfyui-animatediff-evolved"
description = "Improved AnimateDiff integration for ComfyUI."
version = "1.5.6"
version = "1.6.0"
license = { file = "LICENSE" }
dependencies = []
@@ -13,3 +13,4 @@ Repository = "https://github.com/Kosinkadink/ComfyUI-AnimateDiff-Evolved"
PublisherId = "kosinkadink"
DisplayName = "ComfyUI-AnimateDiff-Evolved"
Icon = ""
requires-comfyui = ">=0.3.68"
+12
View File
@@ -0,0 +1,12 @@
# Condition Step Extraction
Extract a single conditioning step from a schedule of prompts.
## Inputs
- `conditioning`: Encoded prompts from a Prompt Scheduling node.
- `index`: The step to extract. It must be within the scheduled prompt range.
## Outputs
- `CONDITIONING`: The single conditioning step from the schedule.
+24
View File
@@ -0,0 +1,24 @@
# AD Per Block Floats (SD1.5)
Use Floats from Value Schedules to select SD1.5 effect and scale values for blocks.
## Inputs
- `effect_21_floats`: Optional effect values. The list is extended to the required 21 values when needed.
- `scale_21_floats`: Optional scale values. The list is extended to the required 21 values when needed.
## Block index map
SD1.5 motion modules contain 21 blocks.
| Index | Block |
| ---: | --- |
| 0 | Start of down blocks (`down_0__0`) |
| 7 | End of down blocks (`down_3__1`) |
| 8 | Mid block (`mid`) |
| 9 | Start of up blocks (`up_0__0`) |
| 20 | End of up blocks (`up_3__2`) |
## Outputs
- `PER_BLOCK`: Per-block effect and scale configuration for an SD1.5 motion module.
+24
View File
@@ -0,0 +1,24 @@
# AD Per Block Floats (SDXL)
Use Floats from Value Schedules to select SDXL effect and scale values for blocks.
## Inputs
- `effect_16_floats`: Optional effect values. The list is extended to the required 16 values when needed.
- `scale_16_floats`: Optional scale values. The list is extended to the required 16 values when needed.
## Block index map
SDXL motion modules contain 16 blocks.
| Index | Block |
| ---: | --- |
| 0 | Start of down blocks (`down_0__0`) |
| 5 | End of down blocks (`down_2__1`) |
| 6 | Mid block (`mid`) |
| 7 | Start of up blocks (`up_0__0`) |
| 15 | End of up blocks (`up_2__2`) |
## Outputs
- `PER_BLOCK`: Per-block effect and scale configuration for an SDXL motion module.
+42
View File
@@ -0,0 +1,42 @@
# Prompt Scheduling
Encode a schedule of prompts with automatic interpolation.
## Schedule format
Schedules support JSON and Python-like formats. Frame 0 is the first frame and `max_frames - 1` is the last.
```text
"0": "blue rock on mountain",
"16": "green rock in lake"
```
```text
0 = "blue rock on mountain",
16 = "green rock in lake"
```
Prompts must be enclosed in double quotes. Prompt portions may use keys supplied through `values_replace`.
### Allowed indices
- **Single:** A positive integer such as `0` or `2` selects that frame. A negative integer such as `-1` or `-5` selects from the end (`-1` is the last frame). A decimal such as `0.5` or `1.0` selects a relative position (`0.5` is halfway and `1.0` is the last frame).
- **Range:** `start:end` uses an uninterpolated prompt from the included start index to the excluded end index. Examples: `0:12`, `0:-5`, `2:0.5`.
- **Hold:** A colon after one index stops interpolation until the next supplied index. Examples: `0:`, `0.5:`, `16:`.
## Inputs
| Input | Description |
| --- | --- |
| `prompts` | The prompt schedule. |
| `clip` | CLIP used to encode prompts. |
| `prepend_text` | Optional text added before every prompt. |
| `append_text` | Optional text added after every prompt. |
| `values_replace` | Optional value schedules substituted for keys written as `` `some_key` `` in prompts. |
| `print_schedule` | Print the resulting schedule when enabled. |
| `max_length` | Intended schedule length. At 0, the largest schedule index determines the length, but negative and decimal relative indices are disabled. |
| `tensor_interp` | Prompt-conditioning interpolation method; defaults to linear interpolation. |
## Outputs
- `CONDITIONING`: Encoded prompts.
+43
View File
@@ -0,0 +1,43 @@
# Prompt Scheduling [Latents]
Encode a schedule of prompts with automatic interpolation, its length matching the passed-in latent count.
## Schedule format
Schedules support JSON and Python-like formats. Frame 0 is the first frame and `max_frames - 1` is the last.
```text
"0": "blue rock on mountain",
"16": "green rock in lake"
```
```text
0 = "blue rock on mountain",
16 = "green rock in lake"
```
Prompts must be enclosed in double quotes. Prompt portions may use keys supplied through `values_replace`.
### Allowed indices
- **Single:** A positive integer such as `0` or `2` selects that frame. A negative integer such as `-1` or `-5` selects from the end (`-1` is the last frame). A decimal such as `0.5` or `1.0` selects a relative position (`0.5` is halfway and `1.0` is the last frame).
- **Range:** `start:end` uses an uninterpolated prompt from the included start index to the excluded end index. Examples: `0:12`, `0:-5`, `2:0.5`.
- **Hold:** A colon after one index stops interpolation until the next supplied index. Examples: `0:`, `0.5:`, `16:`.
## Inputs
| Input | Description |
| --- | --- |
| `prompts` | The prompt schedule. |
| `clip` | CLIP used to encode prompts. |
| `latent` | Supplies the frame count used as the schedule length. |
| `prepend_text` | Optional text added before every prompt. |
| `append_text` | Optional text added after every prompt. |
| `values_replace` | Optional value schedules substituted for keys written as `` `some_key` `` in prompts. |
| `tensor_interp` | Prompt-conditioning interpolation method; defaults to linear interpolation. |
| `print_schedule` | Print the resulting schedule when enabled. |
## Outputs
- `CONDITIONING`: Encoded prompts.
- `LATENT`: The unmodified input latents, usable as a pipe or safely ignored.
+33
View File
@@ -0,0 +1,33 @@
# Value Scheduling
Create a list of values with automatic interpolation.
## Schedule format
Schedules support JSON and Python-like formats. Values need no special formatting.
```text
"0": 1.0,
"16": 1.3
```
```text
0 = 1.0,
16 = 1.3
```
Frame 0 is the first frame and `max_frames - 1` is the last.
- **Single:** Positive integers select a frame, negative integers select from the end (`-1` is last), and decimals select a relative position (`0.5` is halfway and `1.0` is last).
- **Range:** `start:end` holds the start value without interpolation through the excluded end. Examples: `0:12`, `0:-5`, `2:0.5`.
- **Hold:** A trailing colon stops interpolation until the next index. Examples: `0:`, `0.5:`, `16:`.
## Inputs
- `values`: The value schedule.
- `max_length`: Intended schedule length. At 0, the largest schedule index determines the length, but negative and decimal relative indices are disabled.
- `print_schedule`: Print each output value when enabled.
## Outputs
The schedule is returned as `FLOAT`, `FLOATS`, rounded `INT`, and rounded `INTS` outputs.
+33
View File
@@ -0,0 +1,33 @@
# Value Scheduling [Latents]
Create a list of values with automatic interpolation, its length matching the passed-in latent count.
## Schedule format
Schedules support JSON and Python-like formats. Values need no special formatting.
```text
"0": 1.0,
"16": 1.3
```
```text
0 = 1.0,
16 = 1.3
```
Frame 0 is the first frame and `max_frames - 1` is the last.
- **Single:** Positive integers select a frame, negative integers select from the end (`-1` is last), and decimals select a relative position (`0.5` is halfway and `1.0` is last).
- **Range:** `start:end` holds the start value without interpolation through the excluded end. Examples: `0:12`, `0:-5`, `2:0.5`.
- **Hold:** A trailing colon stops interpolation until the next index. Examples: `0:`, `0.5:`, `16:`.
## Inputs
- `values`: The value schedule.
- `latent`: Supplies the frame count used as the schedule length.
- `print_schedule`: Print each output value when enabled.
## Outputs
The schedule is returned as `FLOAT`, `FLOATS`, rounded `INT`, and rounded `INTS` outputs.
+13
View File
@@ -0,0 +1,13 @@
# Add Values Replace
Add a value schedule bound to a key for use in a Prompt Scheduling node.
## Inputs
- `value_key`: The key for the value schedule. It may contain only `a-z`, `A-Z`, `0-9`, and `_`. Refer to it in a prompt by surrounding it with backticks, for example `` `some_key` ``.
- `floats`: A list of floats, typically produced by a Value Scheduling node.
- `prev_replace`: Optional existing replacements, allowing multiple Values Replace nodes to be chained.
## Outputs
- `VALUES_REPLACE`: The replacement mapping for a Prompt Scheduling node.
-53
View File
@@ -1,53 +0,0 @@
import { app } from '../../../scripts/app.js'
function addResizeHook(node, padding, useOldMin=false) {
let origOnCreated = node.onNodeCreated
node.onNodeCreated = function() {
let r = origOnCreated?.apply(this, arguments)
let size = this.computeSize();
size[0] += padding || 0;
if (useOldMin) {
//equal to LiteGraph.NODE_WIDTH*1.5*1.5
size[0] = Math.max(size[0], 315)
}
this.setSize(size);
return r
}
}
app.registerExtension({
name: "AnimateDiffEvolved.autosize",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
//since python_module is based off folder path,
//it could be changed by users and should only be used as fallback
if (nodeData?.name?.startsWith("ADE_")
|| nodeData.python_module == 'custom_nodes.ComfyUI-AnimateDiff-Evolved') {
if (nodeData?.input?.hidden?.autosize) {
addResizeHook(nodeType.prototype, nodeData.input.hidden.autosize[1]?.padding)
} else if (!nodeData?.input?.optional?.autosize) {
addResizeHook(nodeType.prototype, 0, true)
}
}
},
async getCustomWidgets() {
return {
ADEAUTOSIZE(node, inputName, inputData) {
let w = {
name : inputName,
type : "ADE.AUTOSIZE",
value : "",
options : {"serialize": false},
computeSize : function(width) {
return [0, -4];
}
}
if (!node.widgets) {
node.widgets = []
}
node.widgets.push(w)
addResizeHook(node, inputData[1].padding);
return w;
}
}
}
});
-51
View File
@@ -1,51 +0,0 @@
import { app } from '../../../scripts/app.js'
const deprecate_nodes = {
name: 'AnimateDiff.deprecate_nodes',
async getCustomWidgets() {
return {
ADEWARN(node, inputName, inputData) {
let w = {
name : inputName,
type : "ADE.WARN",
value : "",
draw : function(ctx, node, widget_width, y, H) {
var show_text = app.canvas.ds.scale > 0.5;
var margin = 15;
var text_color = inputData[1]['color'] || "#FCC"
ctx.textAlign = "center";
if (show_text) {
if(!this.disabled)
ctx.stroke();
ctx.save();
ctx.beginPath();
ctx.rect(margin, y, widget_width - margin * 2, H);
ctx.clip();
ctx.fillStyle = text_color;
let disp_text = inputData[1]['text']
ctx.fillText(disp_text, widget_width/2, y + H * 0.7);
ctx.restore();
}
},
options : {"serialize": false},
computeSize : function(width) {
if (inputData[1]['text']) {
return [width, 20]
}
return [0, -4]
}
}
if (!node.widgets) {
node.widgets = []
}
node.widgets.push(w)
return w
}
}
}
}
app.registerExtension(deprecate_nodes)
-292
View File
@@ -1,292 +0,0 @@
import { app } from '../../../scripts/app.js'
function chainCallback(object, property, callback) {
if (object == undefined) {
//This should not happen.
console.error("Tried to add callback to non-existant object")
return;
}
if (property in object && object[property]) {
const callback_orig = object[property]
object[property] = function () {
const r = callback_orig.apply(this, arguments);
callback.apply(this, arguments);
return r
};
} else {
object[property] = callback;
}
}
var helpDOM;
function initHelpDOM() {
let parentDOM = document.createElement("div");
document.body.appendChild(parentDOM)
parentDOM.appendChild(helpDOM)
helpDOM.className = "litegraph";
let scrollbarStyle = document.createElement('style');
parentDOM.className = "VHS_floatinghelp"
scrollbarStyle.innerHTML = `
.VHS_floatinghelp {
scrollbar-width: 6px;
scrollbar-color: #0003 #0000;
&::-webkit-scrollbar {
background: transparent;
width: 6px;
}
&::-webkit-scrollbar-thumb {
background: #0005;
border-radius: 20px
}
&::-webkit-scrollbar-button {
display: none;
}
}
.VHS_loopedvideo::-webkit-media-controls-mute-button {
display:none;
}
.VHS_loopedvideo::-webkit-media-controls-fullscreen-button {
display:none;
}
`
parentDOM.appendChild(scrollbarStyle)
chainCallback(app.canvas, "onDrawForeground", function (ctx, visible_rect){
let n = helpDOM.node
if (!n || !n?.graph) {
parentDOM.style['left'] = '-5000px'
return
}
//draw : function(ctx, node, widgetWidth, widgetY, height) {
//update widget position, even if off screen
const transform = ctx.getTransform();
const scale = app.canvas.ds.scale;//gets the litegraph zoom
//calculate coordinates with account for browser zoom
const bcr = app.canvas.canvas.getBoundingClientRect()
const x = transform.e*scale/transform.a + bcr.x;
const y = transform.f*scale/transform.a + bcr.y;
//TODO: text reflows at low zoom. investigate alternatives
Object.assign(parentDOM.style, {
left: (x+(n.pos[0] + n.size[0]+15)*scale) + "px",
top: (y+(n.pos[1]-LiteGraph.NODE_TITLE_HEIGHT)*scale) + "px",
width: "400px",
minHeight: "100px",
maxHeight: "600px",
overflowY: 'scroll',
transformOrigin: '0 0',
transform: 'scale(' + scale + ',' + scale +')',
fontSize: '18px',
backgroundColor: LiteGraph.NODE_DEFAULT_BGCOLOR,
boxShadow: '0 0 10px black',
borderRadius: '4px',
padding: '3px',
zIndex: 3,
position: "absolute",
display: 'inline',
});
});
function setCollapse(el, doCollapse) {
if (doCollapse) {
el.children[0].children[0].innerHTML = '+'
Object.assign(el.children[1].style, {
color: '#CCC',
overflowX: 'hidden',
width: '0px',
minWidth: 'calc(100% - 20px)',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
})
for (let child of el.children[1].children) {
if (child.style.display != 'none'){
child.origDisplay = child.style.display
}
child.style.display = 'none'
}
} else {
el.children[0].children[0].innerHTML = '-'
Object.assign(el.children[1].style, {
color: '',
overflowX: '',
width: '100%',
minWidth: '',
textOverflow: '',
whiteSpace: '',
})
for (let child of el.children[1].children) {
child.style.display = child.origDisplay
}
}
}
helpDOM.collapseOnClick = function() {
let doCollapse = this.children[0].innerHTML == '-'
setCollapse(this.parentElement, doCollapse)
}
helpDOM.selectHelp = function(name, value) {
//attempt to navigate to name in help
function collapseUnlessMatch(items,t) {
var match = items.querySelector('[vhs_title="' + t + '"]')
if (!match) {
for (let i of items.children) {
if (i.innerHTML.slice(0,t.length+5).includes(t)) {
match = i
break
}
}
}
if (!match) {
return null
}
//For longer documentation items with fewer collapsable elements,
//scroll to make sure the entirety of the selected item is visible
//This has the unfortunate side effect of trying to scroll the main
//window if the documentation windows is forcibly offscreen,
//but it's easy to simply scroll the main window back and seems to
//have no visual side effects
match.scrollIntoView(false)
window.scrollTo(0,0)
for (let i of items.querySelectorAll('.VHS_collapse')) {
if (i.contains(match)) {
setCollapse(i, false)
} else {
setCollapse(i, true)
}
}
return match
}
let target = collapseUnlessMatch(helpDOM, name)
if (target && value) {
collapseUnlessMatch(target, value)
}
}
helpDOM.addHelp = function(node, nodeType, description) {
if (!description) {
return
}
//Pad computed size for the clickable question mark
let originalComputeSize = node.computeSize
node.computeSize = function() {
let size = originalComputeSize.apply(this, arguments)
if (!this.title) {
return size
}
let title_width = this.title.length * 0.6 * LiteGraph.NODE_TEXT_SIZE
size[0] = Math.max(size[0], title_width + LiteGraph.NODE_TITLE_HEIGHT)
return size
}
node.description = description
chainCallback(node, "onDrawForeground", function (ctx) {
//draw question mark
ctx.save()
ctx.font = 'bold 20px Arial'
ctx.fillText("?", this.size[0]-17, -8)
ctx.restore()
})
chainCallback(node, "onMouseDown", function (e, pos, canvas) {
//On click would be preferred, but this'll be good enough
if (pos[1] < 0 && pos[0] + LiteGraph.NODE_TITLE_HEIGHT > this.size[0]) {
//corner question mark clicked
if (helpDOM.node == this) {
helpDOM.node = undefined
} else {
helpDOM.node = this;
helpDOM.innerHTML = this.description || "no help provided ".repeat(20)
for (let e of helpDOM.querySelectorAll('.VHS_collapse')) {
e.children[0].onclick = helpDOM.collapseOnClick
e.children[0].style.cursor = 'pointer'
}
for (let e of helpDOM.querySelectorAll('.VHS_precollapse')) {
setCollapse(e, true)
}
}
return true
}
})
let timeout = null
chainCallback(node, "onMouseMove", function (e, pos, canvas) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
if (helpDOM.node != this) {
return
}
timeout = setTimeout(() => {
let n = this
if (pos[0] > 0 && pos[0] < n.size[0]
&& pos[1] > 0 && pos[1] < n.size[1]) {
//TODO: provide help specific to element clicked
let inputRows = Math.max(n.inputs.length, n.outputs.length)
if (pos[1] < LiteGraph.NODE_SLOT_HEIGHT * inputRows) {
let row = Math.floor((pos[1] - 7) / LiteGraph.NODE_SLOT_HEIGHT)
if (pos[0] < n.size[0]/2) {
if (row < n.inputs.length) {
helpDOM.selectHelp(n.inputs[row].name)
}
} else {
if (row < n.outputs.length) {
helpDOM.selectHelp(n.outputs[row].name)
}
}
} else {
//probably widget, but widgets have variable height.
let basey = LiteGraph.NODE_SLOT_HEIGHT * inputRows + 6
for (let w of n.widgets) {
if (w.y) {
basey = w.y
}
let wheight = LiteGraph.NODE_WIDGET_HEIGHT+4
if (w.computeSize) {
wheight = w.computeSize(n.size[0])[1]
}
if (pos[1] < basey + wheight) {
helpDOM.selectHelp(w.name, w.value)
break
}
basey += wheight
}
}
}
}, 500)
})
chainCallback(node, "onMouseLeave", function (e, pos, canvas) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
});
}
}
app.registerExtension({
name: "AnimateDiffEvolved.documentation",
async init() {
if (app.VHSHelp) {
helpDOM = app.VHSHelp
} else {
helpDOM = document.createElement("div");
initHelpDOM()
app.VHSHelp = helpDOM
}
},
async beforeRegisterNodeDef(nodeType, nodeData, app) {
// NOTE: May need manual adjusting for the few non-namespaced nodes
if(nodeData?.name?.startsWith("ADE_") && nodeData.description) {
let description = nodeData.description
let el = document.createElement("div")
el.innerHTML = description
if (!el.children.length) {
//Is plaintext. Do minor convenience formatting
let chunks = description.split('\n')
nodeData.description = chunks[0]
description = chunks.join('<br>')
} else {
nodeData.description = el.querySelector('#VHS_shortdesc')?.innerHTML || el.children[1]?.firstChild?.innerHTML
}
chainCallback(nodeType.prototype, "onNodeCreated", function () {
helpDOM.addHelp(this, nodeType, description)
})
}
},
});
-142
View File
@@ -1,142 +0,0 @@
import { app } from '../../../scripts/app.js'
import { api } from '../../../scripts/api.js'
function offsetDOMWidget(
widget,
ctx,
node,
widgetWidth,
widgetY,
height
) {
const margin = 10
const elRect = ctx.canvas.getBoundingClientRect()
const transform = new DOMMatrix()
.scaleSelf(
elRect.width / ctx.canvas.width,
elRect.height / ctx.canvas.height
)
.multiplySelf(ctx.getTransform())
.translateSelf(0, widgetY + margin)
const scale = new DOMMatrix().scaleSelf(transform.a, transform.d)
Object.assign(widget.inputEl.style, {
transformOrigin: '0 0',
transform: scale,
left: `${transform.e}px`,
top: `${transform.d + transform.f}px`,
width: `${widgetWidth}px`,
height: `${(height || widget.parent?.inputHeight || 32) - margin}px`,
position: 'absolute',
background: !node.color ? '' : node.color,
color: !node.color ? '' : 'white',
zIndex: 5, //app.graph._nodes.indexOf(node),
})
}
export const hasWidgets = (node) => {
if (!node.widgets || !node.widgets?.[Symbol.iterator]) {
return false
}
return true
}
export const cleanupNode = (node) => {
if (!hasWidgets(node)) {
return
}
for (const w of node.widgets) {
if (w.canvas) {
w.canvas.remove()
}
if (w.inputEl) {
w.inputEl.remove()
}
// calls the widget remove callback
w.onRemoved?.()
}
}
const CreatePreviewElement = (name, val, format) => {
const [type] = format.split('/')
const w = {
name,
type,
value: val,
draw: function (ctx, node, widgetWidth, widgetY, height) {
const [cw, ch] = this.computeSize(widgetWidth)
offsetDOMWidget(this, ctx, node, widgetWidth, widgetY, ch)
},
computeSize: function (_) {
const ratio = this.inputRatio || 1
const width = Math.max(220, this.parent.size[0])
return [width, (width / ratio + 10)]
},
onRemoved: function () {
if (this.inputEl) {
this.inputEl.remove()
}
},
}
w.inputEl = document.createElement(type === 'video' ? 'video' : 'img')
w.inputEl.src = w.value
if (type === 'video') {
w.inputEl.setAttribute('type', 'video/webm');
w.inputEl.autoplay = true
w.inputEl.loop = true
w.inputEl.controls = false;
}
w.inputEl.onload = function () {
w.inputRatio = w.inputEl.naturalWidth / w.inputEl.naturalHeight
}
document.body.appendChild(w.inputEl)
return w
}
const gif_preview = {
name: 'AnimateDiff.gif_preview',
async beforeRegisterNodeDef(nodeType, nodeData, app) {
switch (nodeData.name) {
case 'ADE_AnimateDiffCombine':{
const onExecuted = nodeType.prototype.onExecuted
nodeType.prototype.onExecuted = function (message) {
const prefix = 'ad_gif_preview_'
const r = onExecuted ? onExecuted.apply(this, message) : undefined
if (this.widgets) {
const pos = this.widgets.findIndex((w) => w.name === `${prefix}_0`)
if (pos !== -1) {
for (let i = pos; i < this.widgets.length; i++) {
this.widgets[i].onRemoved?.()
}
this.widgets.length = pos
}
if (message?.gifs) {
message.gifs.forEach((params, i) => {
const previewUrl = api.apiURL(
'/view?' + new URLSearchParams(params).toString()
)
const w = this.addCustomWidget(
CreatePreviewElement(`${prefix}_${i}`, previewUrl, params.format || 'image/gif')
)
w.parent = this
})
}
const onRemoved = this.onRemoved
this.onRemoved = () => {
cleanupNode(this)
return onRemoved?.()
}
}
this.setSize([this.size[0], this.computeSize([this.size[0], this.size[1]])[1]])
return r
}
break
}
}
}
}
app.registerExtension(gif_preview)