Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions glob/manager_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@
import re
import shutil
import git
import glob
import json
from datetime import datetime
from contextlib import contextmanager

from server import PromptServer
import manager_core as core
Expand Down Expand Up @@ -726,6 +729,86 @@ async def fetch_updates(request):
except:
traceback.print_exc()
return web.Response(status=400)

@routes.get("/customnode/get_node_types_in_workflows")
async def get_node_types_in_workflows(request):
try:
# get our username from the request header
user_id = PromptServer.instance.user_manager.get_request_user_id(request)

# get the base workflow directory (TODO: figure out if non-standard directories are possible, and how to find them)
workflow_files_base_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), user_id, "workflows"))

logging.debug(f"workflows base path: {workflow_files_base_path}")

# workflow directory doesn't actually exist, return 204 (No Content)
if not os.path.isdir(workflow_files_base_path):
logging.debug("workflows base path doesn't exist - nothing to do...")
return web.Response(status=204)

# get all JSON files under the workflow directory
workflow_file_relative_paths: list[str] = glob.glob(pathname="**/*.json", root_dir=workflow_files_base_path, recursive=True)

logging.debug(f"found the following workflows: {workflow_file_relative_paths}")

# set up our list of workflow/node-lists
workflow_node_mappings: list[dict[str, str | list[str]]] = []

# iterate over each found JSON file
for workflow_file_path in workflow_file_relative_paths:

try:
workflow_file_absolute_path = os.path.abspath(os.path.join(workflow_files_base_path, workflow_file_path))
logging.debug(f"starting work on {workflow_file_absolute_path}")
# load the JSON file
workflow_file_data = json.load(open(workflow_file_absolute_path, "r"))

# make sure there's a nodes key (otherwise this might not actually be a workflow file)
if "nodes" not in workflow_file_data:
logging.warning(f"{workflow_file_path} has no 'nodes' key (possibly invalid?) - skipping...")
# skip to next file
continue

# now this looks like a valid file, so let's get to work
new_mapping = {"workflow_file_name": workflow_file_path}
# we can't use an actual set, because you can't use dicts as set members
node_set = []

# iterate over each node in the workflow
for node in workflow_file_data["nodes"]:
if "id" not in node:
logging.warning(f"Found a node with no ID - possibly corrupt/invalid workflow?")
continue
# if there's no type, throw a warning
if "type" not in node:
logging.warning(f"Node type not found in {workflow_file_path} for node ID {node['id']}")
# skip to next node
continue

node_data_to_return = {"type": node["type"]}
if "properties" not in node:
logging.warning(f"Node ${node['id']} has no properties field - can't determine cnr_id")
else:
for property_key in ["cnr_id", "ver"]:
if property_key in node["properties"]:
node_data_to_return[property_key] = node["properties"][property_key]

# add it to the list for this workflow
if not node_data_to_return in node_set:
node_set.append(node_data_to_return)

# annoyingly, Python can't serialize sets to JSON
new_mapping["node_types"] = list(node_set)
workflow_node_mappings.append(new_mapping)

except Exception as e:
logging.warning(f"Couldn't open {workflow_file_path}: {e}")

return web.json_response(workflow_node_mappings, content_type='application/json')

except:
traceback.print_exc()
return web.Response(status=500)


@routes.get("/manager/queue/update_all")
Expand Down
2 changes: 2 additions & 0 deletions js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This directory contains the JavaScript frontend implementation for ComfyUI-Manag
- **model-manager.js**: Handles the model management interface for downloading and organizing AI models.
- **components-manager.js**: Manages reusable workflow components system.
- **snapshot.js**: Implements the snapshot system for backing up and restoring installations.
- **node-usage-analyzer.js**: Implements the UI for analyzing node usage in workflows.

## Sharing Components

Expand Down Expand Up @@ -46,5 +47,6 @@ The frontend follows a modular component-based architecture:
CSS files are included for specific components:
- **custom-nodes-manager.css**: Styling for the node management UI
- **model-manager.css**: Styling for the model management UI
- **node-usage-analyzer.css**: Styling for the node usage analyzer UI

This frontend implementation provides a comprehensive yet user-friendly interface for managing the ComfyUI ecosystem.
12 changes: 12 additions & 0 deletions js/comfyui-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
} from "./common.js";
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
import { CustomNodesManager } from "./custom-nodes-manager.js";
import { NodeUsageAnalyzer } from "./node-usage-analyzer.js";
import { ModelManager } from "./model-manager.js";
import { SnapshotManager } from "./snapshot.js";

Expand Down Expand Up @@ -910,6 +911,17 @@ class ManagerMenuDialog extends ComfyDialog {
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.IN_WORKFLOW);
}
}),
$el("button.cm-button", {
type: "button",
textContent: "Node Usage Analyzer",
onclick:
() => {
if(!NodeUsageAnalyzer.instance) {
NodeUsageAnalyzer.instance = new NodeUsageAnalyzer(app, self);
}
NodeUsageAnalyzer.instance.show(NodeUsageAnalyzer.SortMode.BY_PACKAGE);
}
}),

$el("br", {}, []),
$el("button.cm-button", {
Expand Down
Loading