diff --git a/cm-cli.py b/cm-cli.py
index 4c388f9f..26cd67fc 100644
--- a/cm-cli.py
+++ b/cm-cli.py
@@ -55,6 +55,7 @@ def check_comfyui_hash():
check_comfyui_hash() # This is a preparation step for manager_core
+core.check_invalid_nodes()
def read_downgrade_blacklist():
diff --git a/glob/manager_core.py b/glob/manager_core.py
index 1d7302b2..8f7f30b2 100644
--- a/glob/manager_core.py
+++ b/glob/manager_core.py
@@ -56,6 +56,55 @@ def download_url(url, dest_folder, filename):
custom_nodes_path = os.path.abspath(os.path.join(comfyui_manager_path, '..'))
+invalid_nodes = {}
+
+
+def check_invalid_nodes():
+ global invalid_nodes
+
+ try:
+ import folder_paths
+ node_paths = folder_paths.get_folder_paths("custom_nodes")
+ except:
+ try:
+ sys.path.append(comfy_path)
+ import folder_paths
+ except:
+ raise Exception(f"Invalid COMFYUI_PATH: {comfy_path}")
+
+ def check(root):
+ global invalid_nodes
+
+ subdirs = [d for d in os.listdir(root) if os.path.isdir(os.path.join(root, d))]
+ for subdir in subdirs:
+ if subdir in ['.disabled', '__pycache__']:
+ continue
+
+ if '@' in subdir:
+ spec = subdir.split('@')
+ if spec[1] in ['unknown', 'nightly']:
+ continue
+
+ if not os.path.exists(os.path.join(root, subdir, '.tracking')):
+ invalid_nodes[spec[0]] = os.path.join(root, subdir)
+
+ node_paths = folder_paths.get_folder_paths("custom_nodes")
+ for x in node_paths:
+ check(x)
+
+ disabled_dir = os.path.join(x, '.disabled')
+ if os.path.exists(disabled_dir):
+ check(disabled_dir)
+
+ if len(invalid_nodes):
+ print(f"\n-------------------- ComfyUI-Manager invalid nodes notice ----------------")
+ print(f"\nNodes requiring reinstallation have been detected:\n(Directly delete the corresponding path and reinstall.)\n")
+
+ for x in invalid_nodes.values():
+ print(x)
+
+ print("\n---------------------------------------------------------------------------\n")
+
comfy_path = os.environ.get('COMFYUI_PATH')
if comfy_path is None:
@@ -2368,6 +2417,9 @@ async def get_unified_total_nodes(channel, mode):
updatable = False
cnr = unified_manager.cnr_map[cnr_id]
+ if cnr_id in invalid_nodes:
+ v['invalid-installation'] = True
+
if cnr_id in unified_manager.active_nodes:
# installed
v['state'] = 'enabled'
@@ -2608,4 +2660,3 @@ async def check_need_to_migrate():
print("The following custom nodes were installed using the old management method and require migration:")
print(", ".join(legacy_custom_nodes))
print("---------------------------------------------------------------------------\n")
-
diff --git a/glob/manager_server.py b/glob/manager_server.py
index d3f5287c..87bf687e 100644
--- a/glob/manager_server.py
+++ b/glob/manager_server.py
@@ -202,8 +202,7 @@ def print_comfyui_version():
print_comfyui_version()
-
-
+core.check_invalid_nodes()
def setup_environment():
@@ -787,6 +786,12 @@ async def get_disabled_versions(request):
return web.Response(status=400)
+@routes.post("/customnode/reinstall")
+async def reinstall_custom_node(request):
+ await uninstall_custom_node(request)
+ await install_custom_node(request)
+
+
@routes.post("/customnode/install")
async def install_custom_node(request):
if not is_allowed_security_level('middle'):
diff --git a/js/custom-nodes-manager.js b/js/custom-nodes-manager.js
index 4f64f721..1378e362 100644
--- a/js/custom-nodes-manager.js
+++ b/js/custom-nodes-manager.js
@@ -253,6 +253,11 @@ const pageCss = `
color: white;
}
+.cn-manager .cn-btn-reinstall {
+ background-color: #993333;
+ color: white;
+}
+
.cn-manager .cn-btn-switch {
background-color: #448833;
color: white;
@@ -587,18 +592,26 @@ export class CustomNodesManager {
mode: "fix"
},
+ "reinstall": {
+ label: "Reinstall",
+ mode: "reinstall"
+ },
+
"install": {
label: "Install",
mode: "install"
},
+
"try-install": {
label: "Try install",
mode: "install"
},
+
"uninstall": {
label: "Uninstall",
mode: "uninstall"
},
+
"switch": {
label: "Switch",
mode: "switch"
@@ -611,7 +624,8 @@ export class CustomNodesManager {
"import-fail": ["try-fix", "switch", "disable", "uninstall"],
"enabled": ["try-update", "switch", "disable", "uninstall"],
"not-installed": ["install"],
- 'unknown': ["try-install"]
+ 'unknown': ["try-install"],
+ "invalid-installation": ["reinstall"],
}
if (!manager_instance.update_check_checkbox.checked) {
@@ -887,8 +901,16 @@ export class CustomNodesManager {
maxWidth: 500,
classMap: 'cn-node-name',
formatter: (title, rowItem, columnItem) => {
- return `${rowItem.action === 'import-fail' ? '(IMPORT FAILED)' : ''}
- ${title}`;
+ var prefix = '';
+ if(rowItem.action === 'invalid-installation') {
+ prefix = '(INVALID)';
+ }
+
+ else if(rowItem.action === 'import-fail') {
+ prefix = '(IMPORT FAILED)';
+ }
+
+ return `${prefix}${title}`;
}
}, {
id: 'version',
@@ -1189,6 +1211,13 @@ export class CustomNodesManager {
}
}
+ if(mode === "reinstall") {
+ title = title || `${list.length} custom nodes`;
+ if (!confirm(`Are you sure reinstall ${title}?`)) {
+ return;
+ }
+ }
+
target.classList.add("cn-btn-loading");
this.showError("");
@@ -1228,6 +1257,10 @@ export class CustomNodesManager {
api_mode = 'install';
}
+ if(install_mode == 'reinstall') {
+ api_mode = 'reinstall';
+ }
+
const res = await api.fetchApi(`/customnode/${api_mode}`, {
method: 'POST',
body: JSON.stringify(data)
@@ -1538,6 +1571,10 @@ export class CustomNodesManager {
nodeItem.action = nodeItem.state;
}
+ if(nodeItem['invalid-installation']) {
+ nodeItem.action = 'invalid-installation';
+ }
+
const filterTypes = new Set();
this.filterList.forEach(filterItem => {
const { value, hashMap } = filterItem;
@@ -1586,6 +1623,10 @@ export class CustomNodesManager {
if(nodeItem['import-fail']) {
filterTypes.add("import-fail");
}
+
+ if(nodeItem['invalid-installation']) {
+ filterTypes.add("invalid-installation");
+ }
}
});