Open Quasimondo opened 1 month ago
I haven't done it... but my instinct would be to use the nodeCreated method to inspect each new node, find the ones you want, and then call addWidget.
Could maybe say more if you have more details :)
Here is one of my tests where I was trying to override a method in the animatediff library and whilst this causes no errors, it also does not get called when the animatediff module is running:
import custom_nodes
from importlib import import_module
ad_loader = import_module('custom_nodes.ComfyUI-AnimateDiff-Evolved.animatediff')
print("PATCH", vars(ad_loader.nodes_gen1.AnimateDiffLoaderGen1))
def override_get_context_weights(num_frames: int, fuse_method: str):
print("ADContextManipulator",num_frames,fuse_method)
return [1.0]*num_frames
animatediff.context.get_context_weights = override_get_context_weights
But yeah it sounds like I should try to rather patch it in the created node as you suggest.
I created a dependency loader to address this exact issue. I figured since ComfyUI already loads all custom nodes as modules, why try to import them twice? This code, when run after all ComfyUI nodes are loaded, should give you the modeules you need based on the dependencies you specified.
You can define dependencies like so:
node_dependencies = { # Must include path as period '.' delimited
"ComfyUI_IPAdapter_plus.IPAdapterPlus": ["IPAdapterAdvanced", "IPAdapterUnifiedLoader"],
"ComfyUI-KJNodes.nodes.mask_nodes": ["CreateFadeMaskAdvanced"],
}
Then call the following code to handle errors of imports not found:
dependencies = load_dependencies(node_dependencies, location="handle_timeline")
if dependencies is None:
print("Dependencies returned none, please see console for additional information related to the custom node")
return None
dependency_loader.py
:class Dummy:
def __init__(self, module_name):
self.module_name = module_name
def get_dependency(module_name, items):
module_items = []
for item in items:
failed = False
try:
module_items.append(getattr(sys.modules[module_name], item))
except KeyError:
logging.error(f"TimeUI.dependency_loader KeyError: Couldn't find module {module_name} in sys")
failed = True
except AttributeError:
logging.error(f"TimeUI.dependency_loader AttributeError: {item} not in {module_name}")
failed = True
if failed: module_items.append(Dummy(f"{module_name}.{item}"))
return module_items
def load_dependencies(dependency_dict: dict={}, location: str=None):
import_success = True
dependencies = []
for key, items in dependency_dict.items():
dependencies.extend(get_dependency(key, items))
for item in dependencies:
if type(item) == Dummy:
import_success = False
file_path = "sample_file_path/for_verbose_error_messages/"
logging.error(f"{file_path}{f'at {location}' if location is not None else ''}: could not find required dependency \"{item.module_name}\", please see list of required nodes to install requirements and try again")
return tuple(dependencies) if import_success else None
This method provides a way of adding to the custom nodes as modules on the python end if that's what you're going for.
But I don't think this answers the original question. If you want to add a widget to an existing node, you need to catch in using the following method:
app.registerExtension({
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeType.comfyClass === "the_comfy_class_of_the_node_you_want_to_hijack") {
// Your hijack code here; Recommend adding a customWidget()
// I would recommend hijacking the `nodeType.prototype.onNodeCreated() {...}` method like so:
const origOnNodeCreated = nodeType.prototype.onNodeCreated;
nodeType.prototype.onNodeCreated = function () {
origOnNodeCreated?.apply(this, arguments); // Call to make sure the original `onNodeCreated` is called
// use `this` only under the `onNodeCreated` context as the instanced node you want to add a custom widget to
// Example:
const widget = {...};
this.addCustomWidget(widget);
// You can also reference any of the widgets added to the node already using this.widgets[index].
// I'm not sure if overriding a widget like `this.widgets[0] = newWidget;` would work or not, but you could always try!
}
}
}
})
beforeRegisterNodeDef
is called for every single node (custom or not) so you can apply all of your overrides here. In fact, all you'd have to do is write an __init__.py
file pointing to a js
folder like so WEB_DIRECTORY = "./js"
and __all__ = ["WEB_DIRECTORY"]
then put your javascript file with the app.registerExtension({...})
override in it and you could override any node you want without having to create a node of your own!
Thanks a lot for looking into this and sharing those recipes!
I wonder if you already have some experience with this. I was trying to add an extra widget (at runtime) to a node from a different custom nodes package (not from the core library), but even though I was able to find the class through import libs it seems like I was missing some crucial part.