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"); + } } });