mirror of
https://git.datalinker.icu/ltdrdata/ComfyUI-Manager
synced 2025-12-08 21:54:26 +08:00
feat: provide error messages for import failed custom node.
This commit is contained in:
parent
a935c8bb35
commit
0eb1cbce43
@ -110,3 +110,6 @@ def add_on_revision_detected(k, f):
|
||||
traceback.print_exc()
|
||||
else:
|
||||
variables['cm.on_revision_detected_handler'].append((k, f))
|
||||
|
||||
|
||||
error_dict = {}
|
||||
@ -41,7 +41,7 @@ import manager_downloader
|
||||
from node_package import InstalledNodePackage
|
||||
|
||||
|
||||
version_code = [3, 7, 6]
|
||||
version_code = [3, 8]
|
||||
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||
|
||||
|
||||
@ -122,7 +122,6 @@ def check_invalid_nodes():
|
||||
if subdir in ['.disabled', '__pycache__']:
|
||||
continue
|
||||
|
||||
|
||||
package = unified_manager.installed_node_packages.get(subdir)
|
||||
if not package:
|
||||
continue
|
||||
@ -368,6 +367,16 @@ class UnifiedManager:
|
||||
self.custom_node_map_cache = {} # (channel, mode) -> augmented custom node list json
|
||||
self.processed_install = set()
|
||||
|
||||
def get_module_name(self, x):
|
||||
info = self.active_nodes.get(x)
|
||||
if info is None:
|
||||
for url, fullpath in self.unknown_active_nodes.values():
|
||||
if url == x:
|
||||
return os.path.basename(fullpath)
|
||||
else:
|
||||
return os.path.basename(info[1])
|
||||
|
||||
return None
|
||||
|
||||
def get_cnr_by_repo(self, url):
|
||||
return self.repo_cnr_map.get(git_utils.normalize_url(url))
|
||||
@ -501,8 +510,8 @@ class UnifiedManager:
|
||||
self.installed_node_packages[node_package.id] = node_package
|
||||
|
||||
if node_package.is_disabled and node_package.is_unknown:
|
||||
# NOTE: unknown package does not have an url.
|
||||
self.unknown_inactive_nodes[node_package.id] = ('', node_package.fullpath)
|
||||
url = git_utils.git_url(node_package.fullpath)
|
||||
self.unknown_inactive_nodes[node_package.id] = (url, node_package.fullpath)
|
||||
|
||||
if node_package.is_disabled and node_package.is_nightly:
|
||||
self.nightly_inactive_nodes[node_package.id] = node_package.fullpath
|
||||
@ -511,8 +520,8 @@ class UnifiedManager:
|
||||
self.active_nodes[node_package.id] = node_package.version, node_package.fullpath
|
||||
|
||||
if node_package.is_enabled and node_package.is_unknown:
|
||||
# NOTE: unknown package does not have an url.
|
||||
self.unknown_active_nodes[node_package.id] = ('', node_package.fullpath)
|
||||
url = git_utils.git_url(node_package.fullpath)
|
||||
self.unknown_active_nodes[node_package.id] = (url, node_package.fullpath)
|
||||
|
||||
if node_package.is_from_cnr and node_package.is_disabled:
|
||||
self.add_to_cnr_inactive_nodes(node_package.id, node_package.version, node_package.fullpath)
|
||||
@ -726,7 +735,6 @@ class UnifiedManager:
|
||||
# default_channel = normalize_channel('default')
|
||||
# cache = self.custom_node_map_cache.get((default_channel, mode)) # CNR/nightly should always be based on the default channel.
|
||||
|
||||
|
||||
channel = normalize_channel(channel)
|
||||
cache = self.custom_node_map_cache.get((channel, mode)) # CNR/nightly should always be based on the default channel.
|
||||
|
||||
|
||||
@ -839,6 +839,23 @@ async def get_disabled_versions(request):
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.post("/customnode/import_fail_info")
|
||||
async def import_fail_info(request):
|
||||
json_data = await request.json()
|
||||
|
||||
if 'cnr_id' in json_data:
|
||||
module_name = core.unified_manager.get_module_name(json_data['cnr_id'])
|
||||
else:
|
||||
module_name = core.unified_manager.get_module_name(json_data['url'])
|
||||
|
||||
if module_name is not None:
|
||||
info = cm_global.error_dict.get(module_name)
|
||||
if info is not None:
|
||||
return web.json_response(info)
|
||||
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@routes.post("/customnode/reinstall")
|
||||
async def reinstall_custom_node(request):
|
||||
await uninstall_custom_node(request)
|
||||
@ -1437,3 +1454,4 @@ cm_global.register_extension('ComfyUI-Manager',
|
||||
'nodes': {},
|
||||
'description': 'This extension provides the ability to manage custom nodes in ComfyUI.', })
|
||||
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ async def get_data(uri, silent=False):
|
||||
json_obj = json.loads(json_text)
|
||||
|
||||
if not silent:
|
||||
logging.info(" [DONE]")
|
||||
print(" [DONE]")
|
||||
|
||||
return json_obj
|
||||
|
||||
|
||||
@ -397,3 +397,12 @@ export const icons = {
|
||||
passed: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 426.667 426.667"><path fill="#6AC259" d="M213.333,0C95.518,0,0,95.514,0,213.333s95.518,213.333,213.333,213.333c117.828,0,213.333-95.514,213.333-213.333S331.157,0,213.333,0z M174.199,322.918l-93.935-93.931l31.309-31.309l62.626,62.622l140.894-140.898l31.309,31.309L174.199,322.918z"/></svg>',
|
||||
download: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" width="100%" height="100%" viewBox="0 0 32 32"><path fill="currentColor" d="M26 24v4H6v-4H4v4a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-4zm0-10l-1.41-1.41L17 20.17V2h-2v18.17l-7.59-7.58L6 14l10 10l10-10z"></path></svg>'
|
||||
}
|
||||
|
||||
export function sanitizeHTML(str) {
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
@ -4,7 +4,7 @@ import { api } from "../../scripts/api.js";
|
||||
|
||||
import {
|
||||
manager_instance, rebootAPI, install_via_git_url,
|
||||
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt
|
||||
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt, sanitizeHTML
|
||||
} from "./common.js";
|
||||
|
||||
// https://cenfun.github.io/turbogrid/api.html
|
||||
@ -250,6 +250,13 @@ const pageCss = `
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-import-failed {
|
||||
background-color: #AA1111;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-install {
|
||||
background-color: black;
|
||||
color: white;
|
||||
@ -872,6 +879,38 @@ export class CustomNodesManager {
|
||||
return this.filter === ShowMode.ALTERNATIVES
|
||||
}
|
||||
|
||||
async handleImportFail(rowItem) {
|
||||
var info;
|
||||
if(rowItem.version == 'unknown'){
|
||||
info = {
|
||||
'url': rowItem.originalData.files[0]
|
||||
};
|
||||
}
|
||||
else{
|
||||
info = {
|
||||
'cnr_id': rowItem.originalData.id
|
||||
};
|
||||
}
|
||||
|
||||
const response = await api.fetchApi(`/customnode/import_fail_info`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(info)
|
||||
});
|
||||
|
||||
let res = await response.json();
|
||||
|
||||
let title = `<FONT COLOR=GREEN><B>Error message occurred while importing the '${rowItem.title}' module.</B></FONT><BR><HR><BR>`
|
||||
|
||||
if(res.code == 400)
|
||||
{
|
||||
show_message(title+'The information is not available.')
|
||||
}
|
||||
else {
|
||||
show_message(title+sanitizeHTML(res['msg']).replace(/ /g, ' ').replace(/\n/g, '<BR>'));
|
||||
}
|
||||
}
|
||||
|
||||
renderGrid() {
|
||||
|
||||
// update theme
|
||||
@ -905,6 +944,7 @@ export class CustomNodesManager {
|
||||
}
|
||||
}
|
||||
|
||||
let self = this;
|
||||
const columns = [{
|
||||
id: 'id',
|
||||
name: 'ID',
|
||||
@ -918,16 +958,29 @@ export class CustomNodesManager {
|
||||
maxWidth: 500,
|
||||
classMap: 'cn-node-name',
|
||||
formatter: (title, rowItem, columnItem) => {
|
||||
var prefix = '';
|
||||
if(rowItem.action === 'invalid-installation') {
|
||||
prefix = '<font color="red"><B>(INVALID)</B></font>';
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
|
||||
else if(rowItem.action === 'import-fail') {
|
||||
prefix = '<font color="red"><B>(IMPORT FAILED)</B></font>';
|
||||
}
|
||||
if (rowItem.action === 'invalid-installation') {
|
||||
const invalidTag = document.createElement('span');
|
||||
invalidTag.style.color = 'red';
|
||||
invalidTag.innerHTML = '<b>(INVALID)</b>';
|
||||
container.appendChild(invalidTag);
|
||||
} else if (rowItem.action === 'import-fail') {
|
||||
const button = document.createElement('button');
|
||||
button.className = 'cn-btn-import-failed';
|
||||
button.innerText = 'IMPORT FAILED ↗';
|
||||
button.onclick = () => self.handleImportFail(rowItem);
|
||||
container.appendChild(button);
|
||||
container.appendChild(document.createElement('br'));
|
||||
}
|
||||
|
||||
return `${prefix}<a href=${rowItem.reference} target="_blank"><b>${title}</b></a>`;
|
||||
const link = document.createElement('a');
|
||||
link.href = rowItem.reference;
|
||||
link.target = '_blank';
|
||||
link.innerHTML = `<b>${title}</b>`;
|
||||
container.appendChild(link);
|
||||
|
||||
return container;
|
||||
}
|
||||
}, {
|
||||
id: 'version',
|
||||
|
||||
@ -9,6 +9,7 @@ import platform
|
||||
import json
|
||||
import ast
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
glob_path = os.path.join(os.path.dirname(__file__), "glob")
|
||||
sys.path.append(glob_path)
|
||||
@ -146,6 +147,48 @@ def process_wrap(cmd_str, cwd_path, handler=None, env=None):
|
||||
return process.wait()
|
||||
|
||||
|
||||
original_stdout = sys.stdout
|
||||
|
||||
|
||||
def try_get_custom_nodes(x):
|
||||
for custom_nodes_dir in folder_paths.get_folder_paths('custom_nodes'):
|
||||
if x.startswith(custom_nodes_dir):
|
||||
relative_path = os.path.relpath(x, custom_nodes_dir)
|
||||
next_segment = relative_path.split(os.sep)[0]
|
||||
if next_segment.lower() != 'comfyui-manager':
|
||||
return next_segment, os.path.join(custom_nodes_dir, next_segment)
|
||||
return None
|
||||
|
||||
|
||||
def extract_origin_module():
|
||||
stack = traceback.extract_stack()[:-2]
|
||||
for frame in reversed(stack):
|
||||
info = try_get_custom_nodes(frame.filename)
|
||||
if info is None:
|
||||
continue
|
||||
else:
|
||||
return info
|
||||
return None
|
||||
|
||||
def extract_origin_module_from_strings(file_paths):
|
||||
for filepath in file_paths:
|
||||
info = try_get_custom_nodes(filepath)
|
||||
if info is None:
|
||||
continue
|
||||
else:
|
||||
return info
|
||||
return None
|
||||
|
||||
|
||||
def finalize_startup():
|
||||
res = {}
|
||||
for k, v in cm_global.error_dict.items():
|
||||
if v['path'] in import_failed_extensions:
|
||||
res[k] = v
|
||||
|
||||
cm_global.error_dict = res
|
||||
|
||||
|
||||
try:
|
||||
if '--port' in sys.argv:
|
||||
port_index = sys.argv.index('--port')
|
||||
@ -225,8 +268,16 @@ try:
|
||||
if match:
|
||||
import_failed_extensions.add(match.group(1).strip())
|
||||
|
||||
if 'Starting server' in message:
|
||||
is_start_mode = False
|
||||
if not self.is_stdout:
|
||||
origin_info = extract_origin_module()
|
||||
if origin_info is not None:
|
||||
name, origin_path = origin_info
|
||||
|
||||
if name != 'comfyui-manager':
|
||||
if name not in cm_global.error_dict:
|
||||
cm_global.error_dict[name] = {'name': name, 'path': origin_path, 'msg': ''}
|
||||
|
||||
cm_global.error_dict[name]['msg'] += message
|
||||
|
||||
if not self.is_stdout:
|
||||
match = re.search(pat_tqdm, message)
|
||||
@ -311,12 +362,34 @@ try:
|
||||
if match:
|
||||
import_failed_extensions.add(match.group(1).strip())
|
||||
|
||||
if 'Traceback' in message:
|
||||
file_lists = self._extract_file_paths(message)
|
||||
origin_info = extract_origin_module_from_strings(file_lists)
|
||||
if origin_info is not None:
|
||||
name, origin_path = origin_info
|
||||
|
||||
if name != 'comfyui-manager':
|
||||
if name not in cm_global.error_dict:
|
||||
cm_global.error_dict[name] = {'name': name, 'path': origin_path, 'msg': ''}
|
||||
|
||||
cm_global.error_dict[name]['msg'] += message
|
||||
|
||||
if 'Starting server' in message:
|
||||
is_start_mode = False
|
||||
finalize_startup()
|
||||
|
||||
if stderr_wrapper:
|
||||
stderr_wrapper.sync_write(message+'\n', file_only=True)
|
||||
|
||||
def _extract_file_paths(self, msg):
|
||||
file_paths = []
|
||||
for line in msg.split('\n'):
|
||||
match = re.findall(r'File \"(.*?)\", line \d+', line)
|
||||
for x in match:
|
||||
if not x.startswith('<'):
|
||||
file_paths.extend(match)
|
||||
return file_paths
|
||||
|
||||
|
||||
logging.getLogger().addHandler(LoggingHandler())
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-manager"
|
||||
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
||||
version = "3.7.6"
|
||||
version = "3.8"
|
||||
license = { file = "LICENSE.txt" }
|
||||
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions"]
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user