add http routes for custom nodes

This commit is contained in:
Terry Ji 2025-06-19 07:57:11 +08:00
parent 5b12b55e32
commit 8b1226dac2
2 changed files with 163 additions and 2 deletions

View File

@ -2078,6 +2078,7 @@ EXTENSION_WEB_DIRS = {}
# Dictionary of successfully loaded module names and associated directories. # Dictionary of successfully loaded module names and associated directories.
LOADED_MODULE_DIRS = {} LOADED_MODULE_DIRS = {}
LOADED_CUSTOM_NODES = {}
def get_module_name(module_path: str) -> str: def get_module_name(module_path: str) -> str:
""" """
@ -2103,6 +2104,8 @@ def get_module_name(module_path: str) -> str:
def load_custom_node(module_path: str, ignore=set(), module_parent="custom_nodes") -> bool: def load_custom_node(module_path: str, ignore=set(), module_parent="custom_nodes") -> bool:
module_name = get_module_name(module_path) module_name = get_module_name(module_path)
project_name = module_name
if os.path.isfile(module_path): if os.path.isfile(module_path):
sp = os.path.splitext(module_path) sp = os.path.splitext(module_path)
module_name = sp[0] module_name = sp[0]
@ -2110,6 +2113,15 @@ def load_custom_node(module_path: str, ignore=set(), module_parent="custom_nodes
elif os.path.isdir(module_path): elif os.path.isdir(module_path):
sys_module_name = module_path.replace(".", "_x_") sys_module_name = module_path.replace(".", "_x_")
is_custom_node = module_parent == "custom_nodes"
if is_custom_node:
LOADED_CUSTOM_NODES[project_name] = {
"project_name": project_name,
"status": "loading",
"error": None
}
try: try:
logging.debug("Trying to load custom node {}".format(module_path)) logging.debug("Trying to load custom node {}".format(module_path))
if os.path.isfile(module_path): if os.path.isfile(module_path):
@ -2130,14 +2142,20 @@ def load_custom_node(module_path: str, ignore=set(), module_parent="custom_nodes
project_config = config_parser.extract_node_configuration(module_path) project_config = config_parser.extract_node_configuration(module_path)
project_name_from_config = project_config.project.name
if is_custom_node and project_name_from_config != project_name:
LOADED_CUSTOM_NODES[project_name_from_config] = LOADED_CUSTOM_NODES.pop(project_name)
LOADED_CUSTOM_NODES[project_name_from_config]["project_name"] = project_name_from_config
project_name = project_name_from_config
web_dir_name = project_config.tool_comfy.web web_dir_name = project_config.tool_comfy.web
if web_dir_name: if web_dir_name:
web_dir_path = os.path.join(module_path, web_dir_name) web_dir_path = os.path.join(module_path, web_dir_name)
if os.path.isdir(web_dir_path): if os.path.isdir(web_dir_path):
project_name = project_config.project.name
EXTENSION_WEB_DIRS[project_name] = web_dir_path EXTENSION_WEB_DIRS[project_name] = web_dir_path
logging.info("Automatically register web folder {} for {}".format(web_dir_name, project_name)) logging.info("Automatically register web folder {} for {}".format(web_dir_name, project_name))
@ -2150,17 +2168,39 @@ def load_custom_node(module_path: str, ignore=set(), module_parent="custom_nodes
EXTENSION_WEB_DIRS[module_name] = web_dir EXTENSION_WEB_DIRS[module_name] = web_dir
if hasattr(module, "NODE_CLASS_MAPPINGS") and getattr(module, "NODE_CLASS_MAPPINGS") is not None: if hasattr(module, "NODE_CLASS_MAPPINGS") and getattr(module, "NODE_CLASS_MAPPINGS") is not None:
node_count = 0
for name, node_cls in module.NODE_CLASS_MAPPINGS.items(): for name, node_cls in module.NODE_CLASS_MAPPINGS.items():
if name not in ignore: if name not in ignore:
NODE_CLASS_MAPPINGS[name] = node_cls NODE_CLASS_MAPPINGS[name] = node_cls
node_cls.RELATIVE_PYTHON_MODULE = "{}.{}".format(module_parent, get_module_name(module_path)) node_cls.RELATIVE_PYTHON_MODULE = "{}.{}".format(module_parent, get_module_name(module_path))
node_count += 1
if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS") and getattr(module, "NODE_DISPLAY_NAME_MAPPINGS") is not None: if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS") and getattr(module, "NODE_DISPLAY_NAME_MAPPINGS") is not None:
NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS) NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS)
if is_custom_node:
LOADED_CUSTOM_NODES[project_name].update({
"status": "loaded",
"node_count": node_count,
"nodes": list(module.NODE_CLASS_MAPPINGS.keys())
})
logging.info(f"Successfully loaded custom node: {project_name} with {node_count} nodes")
return True return True
else: else:
if is_custom_node:
LOADED_CUSTOM_NODES[project_name].update({
"status": "skipped",
"error": "No NODE_CLASS_MAPPINGS found"
})
logging.warning(f"Skip {module_path} module for custom nodes due to the lack of NODE_CLASS_MAPPINGS.") logging.warning(f"Skip {module_path} module for custom nodes due to the lack of NODE_CLASS_MAPPINGS.")
return False return False
except Exception as e: except Exception as e:
if is_custom_node:
LOADED_CUSTOM_NODES[project_name].update({
"status": "failed",
"error": str(e)
})
logging.warning(traceback.format_exc()) logging.warning(traceback.format_exc())
logging.warning(f"Cannot import {module_path} module for custom nodes: {e}") logging.warning(f"Cannot import {module_path} module for custom nodes: {e}")
return False return False

121
server.py
View File

@ -711,6 +711,127 @@ class PromptServer():
return web.Response(status=200) return web.Response(status=200)
@routes.get("/custom_nodes")
async def get_custom_nodes(request):
"""
{
"custom_nodes": [
{
"project_name": "node_1",
"status": "loaded|failed|skipped|loading",
"node_count": 5,
"nodes": ["NodeClass1", "NodeClass2"],
"error": "error message if failed"
}
],
"total_count": 10,
"loaded_count": 8,
"failed_count": 1,
"skipped_count": 1
}
"""
try:
custom_nodes_list = []
loaded_count = 0
failed_count = 0
skipped_count = 0
for project_name, node_info in nodes.LOADED_CUSTOM_NODES.items():
node_data = {
"project_name": node_info.get("project_name", project_name),
"status": node_info.get("status", "unknown")
}
if node_info.get("status") == "loaded":
node_data.update({
"node_count": node_info.get("node_count", 0),
"nodes": node_info.get("nodes", [])
})
loaded_count += 1
elif node_info.get("status") == "failed":
node_data["error"] = node_info.get("error", "Unknown error")
failed_count += 1
elif node_info.get("status") == "skipped":
node_data["error"] = node_info.get("error", "No NODE_CLASS_MAPPINGS found")
skipped_count += 1
custom_nodes_list.append(node_data)
response_data = {
"custom_nodes": custom_nodes_list,
"total_count": len(custom_nodes_list),
"loaded_count": loaded_count,
"failed_count": failed_count,
"skipped_count": skipped_count
}
return web.json_response(response_data)
except Exception as e:
logging.error(f"Error in get_custom_nodes: {e}")
logging.error(traceback.format_exc())
return web.json_response({
"error": f"Failed to get custom nodes: {str(e)}",
"custom_nodes": [],
"total_count": 0,
"loaded_count": 0,
"failed_count": 0,
"skipped_count": 0
}, status=500)
@routes.get("/custom_node/{project_name}")
async def get_custom_node_by_name(request):
try:
project_name = request.match_info["project_name"]
if not project_name:
return web.json_response({
"error": "Project name is required",
"message": "Please provide a valid project name in the URL path"
}, status=400)
import urllib.parse
project_name = urllib.parse.unquote(project_name)
if project_name not in nodes.LOADED_CUSTOM_NODES:
return web.json_response({
"error": f"Custom node '{project_name}' not found",
"message": f"The custom node '{project_name}' is not loaded or does not exist"
}, status=404)
node_info = nodes.LOADED_CUSTOM_NODES[project_name]
response_data = {
"project_name": node_info.get("project_name", project_name),
"status": node_info.get("status", "unknown")
}
if node_info.get("status") == "loaded":
response_data.update({
"node_count": node_info.get("node_count", 0),
"nodes": node_info.get("nodes", [])
})
elif node_info.get("status") in ["failed", "skipped"]:
response_data["error"] = node_info.get("error", "Unknown error")
return web.json_response(response_data)
except KeyError as e:
logging.error(f"Missing parameter in get_custom_node_by_name: {e}")
return web.json_response({
"error": "Invalid request",
"message": "Required parameter is missing from the request"
}, status=400)
except Exception as e:
logging.error(f"Error in get_custom_node_by_name: {e}")
logging.error(traceback.format_exc())
return web.json_response({
"error": f"Failed to get custom node information: {str(e)}",
"message": "An internal error occurred while retrieving the custom node information"
}, status=500)
async def setup(self): async def setup(self):
timeout = aiohttp.ClientTimeout(total=None) # no timeout timeout = aiohttp.ClientTimeout(total=None) # no timeout
self.client_session = aiohttp.ClientSession(timeout=timeout) self.client_session = aiohttp.ClientSession(timeout=timeout)