mirror of
https://git.datalinker.icu/ltdrdata/ComfyUI-Manager
synced 2025-12-09 06:04:31 +08:00
Merge branch 'feat/various'
This commit is contained in:
commit
204b975074
@ -193,14 +193,14 @@ NODE_CLASS_MAPPINGS.update({
|
||||
|
||||
## Roadmap
|
||||
|
||||
- [ ] System displaying information about failed custom nodes import.
|
||||
- [ ] Guide for missing nodes in ComfyUI vanilla nodes.
|
||||
- [x] System displaying information about failed custom nodes import.
|
||||
- [x] Guide for missing nodes in ComfyUI vanilla nodes.
|
||||
- [x] Collision checking system for nodes with the same ID across extensions.
|
||||
- [ ] Auto migration for custom nodes with changed structures.
|
||||
- [ ] Version control feature for nodes.
|
||||
- [ ] List of currently used custom nodes.
|
||||
- [ ] Template sharing system.
|
||||
- [ ] 3rd party API system.
|
||||
- [ ] Collision checking system for nodes with the same ID across extensions.
|
||||
|
||||
|
||||
# Disclaimer
|
||||
|
||||
83
__init__.py
83
__init__.py
@ -13,8 +13,10 @@ from tqdm.auto import tqdm
|
||||
import concurrent
|
||||
import ssl
|
||||
from urllib.parse import urlparse
|
||||
import http.client
|
||||
import re
|
||||
|
||||
version = "V1.3"
|
||||
version = "V1.4"
|
||||
print(f"### Loading: ComfyUI-Manager ({version})")
|
||||
|
||||
|
||||
@ -84,6 +86,7 @@ from torchvision.datasets.utils import download_url
|
||||
|
||||
comfy_ui_required_revision = 1240
|
||||
comfy_ui_revision = "Unknown"
|
||||
comfy_ui_commit_date = ""
|
||||
|
||||
comfy_path = os.path.dirname(folder_paths.__file__)
|
||||
custom_nodes_path = os.path.join(comfy_path, 'custom_nodes')
|
||||
@ -247,6 +250,8 @@ def try_install_script(url, repo_path, install_cmd):
|
||||
|
||||
def print_comfyui_version():
|
||||
global comfy_ui_revision
|
||||
global comfy_ui_commit_date
|
||||
|
||||
try:
|
||||
repo = git.Repo(os.path.dirname(folder_paths.__file__))
|
||||
|
||||
@ -260,10 +265,11 @@ def print_comfyui_version():
|
||||
except:
|
||||
pass
|
||||
|
||||
comfy_ui_commit_date = repo.head.commit.committed_datetime.date()
|
||||
if current_branch == "master":
|
||||
print(f"### ComfyUI Revision: {comfy_ui_revision} [{git_hash[:8]}] | Released on '{repo.head.commit.committed_datetime.date()}'")
|
||||
print(f"### ComfyUI Revision: {comfy_ui_revision} [{git_hash[:8]}] | Released on '{comfy_ui_commit_date}'")
|
||||
else:
|
||||
print(f"### ComfyUI Revision: {comfy_ui_revision} on '{current_branch}' [{git_hash[:8]}] | Released on '{repo.head.commit.committed_datetime.date()}'")
|
||||
print(f"### ComfyUI Revision: {comfy_ui_revision} on '{current_branch}' [{git_hash[:8]}] | Released on '{comfy_ui_commit_date}'")
|
||||
except:
|
||||
print("### ComfyUI Revision: UNKNOWN (The currently installed ComfyUI is not a Git repository)")
|
||||
|
||||
@ -413,7 +419,7 @@ def git_pull(path):
|
||||
origin = repo.remote(name='origin')
|
||||
origin.pull()
|
||||
repo.git.submodule('update', '--init', '--recursive')
|
||||
|
||||
|
||||
repo.close()
|
||||
|
||||
return True
|
||||
@ -534,10 +540,15 @@ def check_a_custom_node_installed(item, do_fetch=False, do_update_check=True, do
|
||||
try:
|
||||
if do_update_check and git_repo_has_updates(dir_path, do_fetch, do_update):
|
||||
item['installed'] = 'Update'
|
||||
elif sys.__comfyui_manager_is_import_failed_extension(dir_name):
|
||||
item['installed'] = 'Fail'
|
||||
else:
|
||||
item['installed'] = 'True'
|
||||
except:
|
||||
item['installed'] = 'True'
|
||||
if sys.__comfyui_manager_is_import_failed_extension(dir_name):
|
||||
item['installed'] = 'Fail'
|
||||
else:
|
||||
item['installed'] = 'True'
|
||||
|
||||
elif os.path.exists(dir_path + ".disabled"):
|
||||
item['installed'] = 'Disabled'
|
||||
@ -557,7 +568,10 @@ def check_a_custom_node_installed(item, do_fetch=False, do_update_check=True, do
|
||||
|
||||
file_path = os.path.join(base_path, dir_name)
|
||||
if os.path.exists(file_path):
|
||||
item['installed'] = 'True'
|
||||
if sys.__comfyui_manager_is_import_failed_extension(dir_name):
|
||||
item['installed'] = 'Fail'
|
||||
else:
|
||||
item['installed'] = 'True'
|
||||
elif os.path.exists(file_path + ".disabled"):
|
||||
item['installed'] = 'Disabled'
|
||||
else:
|
||||
@ -826,14 +840,14 @@ def get_current_snapshot():
|
||||
|
||||
|
||||
def save_snapshot_with_postfix(postfix):
|
||||
now = datetime.datetime.now()
|
||||
now = datetime.datetime.now()
|
||||
|
||||
date_time_format = now.strftime("%Y-%m-%d_%H-%M-%S")
|
||||
file_name = f"{date_time_format}_{postfix}"
|
||||
date_time_format = now.strftime("%Y-%m-%d_%H-%M-%S")
|
||||
file_name = f"{date_time_format}_{postfix}"
|
||||
|
||||
path = os.path.join(os.path.dirname(__file__), 'snapshots', f"{file_name}.json")
|
||||
with open(path, "w") as json_file:
|
||||
json.dump(get_current_snapshot(), json_file, indent=4)
|
||||
path = os.path.join(os.path.dirname(__file__), 'snapshots', f"{file_name}.json")
|
||||
with open(path, "w") as json_file:
|
||||
json.dump(get_current_snapshot(), json_file, indent=4)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/snapshot/get_current")
|
||||
@ -1368,7 +1382,7 @@ async def install_model(request):
|
||||
if json_data['url'].startswith('https://github.com') or json_data['url'].startswith('https://huggingface.co'):
|
||||
model_dir = get_model_dir(json_data)
|
||||
download_url(json_data['url'], model_dir)
|
||||
|
||||
|
||||
return web.json_response({}, content_type='application/json')
|
||||
else:
|
||||
res = download_url_with_agent(json_data['url'], model_path)
|
||||
@ -1433,6 +1447,37 @@ async def channel_url_list(request):
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/notice")
|
||||
async def get_notice(request):
|
||||
url = "github.com"
|
||||
path = "/ltdrdata/ltdrdata.github.io/wiki/News"
|
||||
|
||||
conn = http.client.HTTPSConnection(url)
|
||||
conn.request("GET", path)
|
||||
|
||||
response = conn.getresponse()
|
||||
|
||||
try:
|
||||
if response.status == 200:
|
||||
html_content = response.read().decode('utf-8')
|
||||
|
||||
pattern = re.compile(r'<div class="markdown-body">([\s\S]*?)</div>')
|
||||
match = pattern.search(html_content)
|
||||
|
||||
if match:
|
||||
markdown_content = match.group(1)
|
||||
markdown_content += f"<HR>ComfyUI: {comfy_ui_revision} ({comfy_ui_commit_date})"
|
||||
markdown_content += f"<BR>Manager: {version}"
|
||||
return web.Response(text=markdown_content, status=200)
|
||||
else:
|
||||
return web.Response(text="Unable to retrieve Notice", status=200)
|
||||
else:
|
||||
return web.Response(text="Unable to retrieve Notice", status=200)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/share_option")
|
||||
async def share_option(request):
|
||||
if "value" in request.rel_url.query:
|
||||
@ -1554,13 +1599,13 @@ async def share_art(request):
|
||||
prompt = json_data['prompt']
|
||||
potential_outputs = json_data['potential_outputs']
|
||||
selected_output_index = json_data['selected_output_index']
|
||||
|
||||
|
||||
try:
|
||||
output_to_share = potential_outputs[int(selected_output_index)]
|
||||
except:
|
||||
# for now, pick the first output
|
||||
output_to_share = potential_outputs[0]
|
||||
|
||||
|
||||
assert output_to_share['type'] in ('image', 'output')
|
||||
output_dir = folder_paths.get_output_directory()
|
||||
|
||||
@ -1585,7 +1630,7 @@ async def share_art(request):
|
||||
if "comfyworkflows" in share_destinations:
|
||||
share_website_host = "https://comfyworkflows.com"
|
||||
share_endpoint = f"{share_website_host}/api"
|
||||
|
||||
|
||||
# get presigned urls
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.post(
|
||||
@ -1655,7 +1700,7 @@ async def share_art(request):
|
||||
homeserver = homeserver.replace("http://", "https://")
|
||||
if not homeserver.startswith("https://"):
|
||||
homeserver = "https://" + homeserver
|
||||
|
||||
|
||||
client = MatrixClient(homeserver)
|
||||
try:
|
||||
token = client.login(username=matrix_auth['username'], password=matrix_auth['password'])
|
||||
@ -1667,7 +1712,7 @@ async def share_art(request):
|
||||
matrix = MatrixHttpApi(homeserver, token=token)
|
||||
with open(asset_filepath, 'rb') as f:
|
||||
mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri']
|
||||
|
||||
|
||||
workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri']
|
||||
|
||||
text_content = ""
|
||||
@ -1684,7 +1729,7 @@ async def share_art(request):
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return web.json_response({"error" : "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
|
||||
|
||||
|
||||
return web.json_response({
|
||||
"comfyworkflows" : {
|
||||
"url" : None if "comfyworkflows" not in share_destinations else f"{share_website_host}/workflows/{workflowId}",
|
||||
|
||||
@ -2565,6 +2565,138 @@
|
||||
"title_aux": "comfy-nodes"
|
||||
}
|
||||
],
|
||||
"https://github.com/comfyanonymous/ComfyUI": [
|
||||
[
|
||||
"BasicScheduler",
|
||||
"CLIPLoader",
|
||||
"CLIPMergeSimple",
|
||||
"CLIPSave",
|
||||
"CLIPSetLastLayer",
|
||||
"CLIPTextEncode",
|
||||
"CLIPTextEncodeSDXL",
|
||||
"CLIPTextEncodeSDXLRefiner",
|
||||
"CLIPVisionEncode",
|
||||
"CLIPVisionLoader",
|
||||
"Canny",
|
||||
"CheckpointLoader",
|
||||
"CheckpointLoaderSimple",
|
||||
"CheckpointSave",
|
||||
"ConditioningAverage",
|
||||
"ConditioningCombine",
|
||||
"ConditioningConcat",
|
||||
"ConditioningSetArea",
|
||||
"ConditioningSetAreaPercentage",
|
||||
"ConditioningSetMask",
|
||||
"ConditioningSetTimestepRange",
|
||||
"ConditioningZeroOut",
|
||||
"ControlNetApply",
|
||||
"ControlNetApplyAdvanced",
|
||||
"ControlNetLoader",
|
||||
"CropMask",
|
||||
"DiffControlNetLoader",
|
||||
"DiffusersLoader",
|
||||
"DualCLIPLoader",
|
||||
"EmptyImage",
|
||||
"EmptyLatentImage",
|
||||
"ExponentialScheduler",
|
||||
"FeatherMask",
|
||||
"FlipSigmas",
|
||||
"FreeU",
|
||||
"FreeU_V2",
|
||||
"GLIGENLoader",
|
||||
"GLIGENTextBoxApply",
|
||||
"GrowMask",
|
||||
"HyperTile",
|
||||
"HypernetworkLoader",
|
||||
"ImageBatch",
|
||||
"ImageBlend",
|
||||
"ImageBlur",
|
||||
"ImageColorToMask",
|
||||
"ImageCompositeMasked",
|
||||
"ImageCrop",
|
||||
"ImageInvert",
|
||||
"ImageOnlyCheckpointLoader",
|
||||
"ImagePadForOutpaint",
|
||||
"ImageQuantize",
|
||||
"ImageScale",
|
||||
"ImageScaleBy",
|
||||
"ImageScaleToTotalPixels",
|
||||
"ImageSharpen",
|
||||
"ImageToMask",
|
||||
"ImageUpscaleWithModel",
|
||||
"InvertMask",
|
||||
"JoinImageWithAlpha",
|
||||
"KSampler",
|
||||
"KSamplerAdvanced",
|
||||
"KSamplerSelect",
|
||||
"KarrasScheduler",
|
||||
"LatentAdd",
|
||||
"LatentBlend",
|
||||
"LatentComposite",
|
||||
"LatentCompositeMasked",
|
||||
"LatentCrop",
|
||||
"LatentFlip",
|
||||
"LatentFromBatch",
|
||||
"LatentInterpolate",
|
||||
"LatentMultiply",
|
||||
"LatentRotate",
|
||||
"LatentSubtract",
|
||||
"LatentUpscale",
|
||||
"LatentUpscaleBy",
|
||||
"LoadImage",
|
||||
"LoadImageMask",
|
||||
"LoadLatent",
|
||||
"LoraLoader",
|
||||
"LoraLoaderModelOnly",
|
||||
"MaskComposite",
|
||||
"MaskToImage",
|
||||
"ModelMergeAdd",
|
||||
"ModelMergeBlocks",
|
||||
"ModelMergeSimple",
|
||||
"ModelMergeSubtract",
|
||||
"ModelSamplingContinuousEDM",
|
||||
"ModelSamplingDiscrete",
|
||||
"PatchModelAddDownscale",
|
||||
"PolyexponentialScheduler",
|
||||
"PorterDuffImageComposite",
|
||||
"PreviewImage",
|
||||
"RebatchLatents",
|
||||
"RepeatImageBatch",
|
||||
"RepeatLatentBatch",
|
||||
"RescaleCFG",
|
||||
"SVD_img2vid_Conditioning",
|
||||
"SamplerCustom",
|
||||
"SamplerDPMPP_2M_SDE",
|
||||
"SamplerDPMPP_SDE",
|
||||
"SaveAnimatedPNG",
|
||||
"SaveAnimatedWEBP",
|
||||
"SaveImage",
|
||||
"SaveLatent",
|
||||
"SetLatentNoiseMask",
|
||||
"SolidMask",
|
||||
"SplitImageWithAlpha",
|
||||
"SplitSigmas",
|
||||
"StyleModelApply",
|
||||
"StyleModelLoader",
|
||||
"TomePatchModel",
|
||||
"UNETLoader",
|
||||
"UpscaleModelLoader",
|
||||
"VAEDecode",
|
||||
"VAEDecodeTiled",
|
||||
"VAEEncode",
|
||||
"VAEEncodeForInpaint",
|
||||
"VAEEncodeTiled",
|
||||
"VAELoader",
|
||||
"VAESave",
|
||||
"VPScheduler",
|
||||
"VideoLinearCFGGuidance",
|
||||
"unCLIPCheckpointLoader",
|
||||
"unCLIPConditioning"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI"
|
||||
}
|
||||
],
|
||||
"https://github.com/comfyanonymous/ComfyUI_experiments": [
|
||||
[
|
||||
"ModelMergeBlockNumber",
|
||||
@ -4274,6 +4406,7 @@
|
||||
[
|
||||
"Auto Merge Block Weighted",
|
||||
"CLIPMergeSimple",
|
||||
"CheckpointSave",
|
||||
"ModelMergeBlocks",
|
||||
"ModelMergeSimple"
|
||||
],
|
||||
|
||||
@ -29,6 +29,16 @@ docStyle.innerHTML = `
|
||||
text-align: center;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.cm-notice-board {
|
||||
width: 250px;
|
||||
height: 160px;
|
||||
overflow: auto;
|
||||
color: var(--input-text);
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
overflow-x: hidden;
|
||||
};
|
||||
`;
|
||||
|
||||
document.head.appendChild(docStyle);
|
||||
@ -89,6 +99,14 @@ async function init_share_option() {
|
||||
});
|
||||
}
|
||||
|
||||
async function init_notice(notice) {
|
||||
api.fetchApi('/manager/notice')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
notice.innerHTML = data;
|
||||
})
|
||||
}
|
||||
|
||||
await init_badge_mode();
|
||||
await init_share_option();
|
||||
|
||||
@ -492,7 +510,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
createControlsRight() {
|
||||
return [
|
||||
const elts = [
|
||||
$el("button", {
|
||||
type: "button",
|
||||
textContent: "ComfyUI Community Manual",
|
||||
@ -570,7 +588,16 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
textContent: "ComfyUI Nodes Info",
|
||||
onclick: () => { window.open("https://ltdrdata.github.io/", "comfyui-node-info"); }
|
||||
}),
|
||||
$el("br", {}, []),
|
||||
];
|
||||
|
||||
var textarea = document.createElement("div");
|
||||
textarea.className = "cm-notice-board";
|
||||
elts.push(textarea);
|
||||
|
||||
init_notice(textarea);
|
||||
|
||||
return elts;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
@ -603,7 +630,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
|
||||
this.element = $el("div.comfy-modal", { parent: document.body }, [ content ]);
|
||||
this.element.style.width = '1000px';
|
||||
this.element.style.height = '400px';
|
||||
this.element.style.height = '420px';
|
||||
this.element.style.zIndex = 10000;
|
||||
}
|
||||
|
||||
@ -638,6 +665,7 @@ app.registerExtension({
|
||||
}
|
||||
menu.append(managerButton);
|
||||
|
||||
|
||||
const shareButton = document.createElement("button");
|
||||
shareButton.id = "shareButton";
|
||||
shareButton.textContent = "Share";
|
||||
@ -680,6 +708,10 @@ app.registerExtension({
|
||||
if (nicknames[nodeData.name.trim()]) {
|
||||
let nick = nicknames[nodeData.name.trim()];
|
||||
|
||||
if (nick == 'ComfyUI') {
|
||||
nick = "🦊"
|
||||
}
|
||||
|
||||
if (nick.length > 25) {
|
||||
text += nick.substring(0, 23) + "..";
|
||||
}
|
||||
@ -726,6 +758,10 @@ app.registerExtension({
|
||||
if (nicknames[node.type.trim()]) {
|
||||
let nick = nicknames[node.type.trim()];
|
||||
|
||||
if (nick == 'ComfyUI') {
|
||||
nick = "🦊"
|
||||
}
|
||||
|
||||
if (nick.length > 25) {
|
||||
text += nick.substring(0, 23) + "..";
|
||||
}
|
||||
|
||||
@ -29,6 +29,53 @@ async function getCustomnodeMappings() {
|
||||
return data;
|
||||
}
|
||||
|
||||
async function getConflictMappings() {
|
||||
var mode = "url";
|
||||
if(manager_instance.local_mode_checkbox.checked)
|
||||
mode = "local";
|
||||
|
||||
const response = await api.fetchApi(`/customnode/getmappings?mode=${mode}`);
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
let node_to_extensions_map = {};
|
||||
|
||||
for(let k in data) {
|
||||
for(let i in data[k][0]) {
|
||||
let node = data[k][0][i];
|
||||
let l = node_to_extensions_map[node];
|
||||
if(!l) {
|
||||
l = [];
|
||||
node_to_extensions_map[node] = l;
|
||||
}
|
||||
l.push(k);
|
||||
}
|
||||
}
|
||||
|
||||
let conflict_map = {};
|
||||
for(let node in node_to_extensions_map) {
|
||||
if(node_to_extensions_map[node].length > 1) {
|
||||
for(let i in node_to_extensions_map[node]) {
|
||||
let extension = node_to_extensions_map[node][i];
|
||||
let l = conflict_map[extension];
|
||||
|
||||
if(!l) {
|
||||
l = [];
|
||||
conflict_map[extension] = l;
|
||||
}
|
||||
|
||||
for(let j in node_to_extensions_map[node]) {
|
||||
let extension2 = node_to_extensions_map[node][j];
|
||||
if(extension != extension2)
|
||||
l.push([node, extension2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return conflict_map;
|
||||
}
|
||||
|
||||
async function getUnresolvedNodesInComponent() {
|
||||
try {
|
||||
var mode = "url";
|
||||
@ -180,6 +227,8 @@ export class CustomNodesInstaller extends ComfyDialog {
|
||||
// invalidate
|
||||
this.data = (await getCustomNodes()).custom_nodes;
|
||||
|
||||
this.conflict_mappings = await getConflictMappings();
|
||||
|
||||
if(this.is_missing_node_mode)
|
||||
this.data = await this.filter_missing_node(this.data);
|
||||
|
||||
@ -369,6 +418,7 @@ export class CustomNodesInstaller extends ComfyDialog {
|
||||
var data1 = document.createElement('td');
|
||||
data1.style.textAlign = "center";
|
||||
data1.innerHTML = i+1;
|
||||
|
||||
var data2 = document.createElement('td');
|
||||
data2.style.maxWidth = "100px";
|
||||
data2.className = "cm-node-author"
|
||||
@ -376,14 +426,43 @@ export class CustomNodesInstaller extends ComfyDialog {
|
||||
data2.style.whiteSpace = "nowrap";
|
||||
data2.style.overflow = "hidden";
|
||||
data2.style.textOverflow = "ellipsis";
|
||||
|
||||
var data3 = document.createElement('td');
|
||||
data3.style.maxWidth = "200px";
|
||||
data3.style.wordWrap = "break-word";
|
||||
data3.className = "cm-node-name"
|
||||
data3.innerHTML = ` <a href=${data.reference} target="_blank"><font color="skyblue"><b>${data.title}</b></font></a>`;
|
||||
if(data.installed == 'Fail')
|
||||
data3.innerHTML = ' <font color="BLACK"><B>(IMPORT FAILED)</B></font>' + data3.innerHTML;
|
||||
|
||||
var data4 = document.createElement('td');
|
||||
data4.innerHTML = data.description;
|
||||
data4.className = "cm-node-desc"
|
||||
|
||||
let conflicts = this.conflict_mappings[data.files[0]];
|
||||
if(conflicts) {
|
||||
let buf = '<BR><BR><FONT color="#AA3333"><B>Conflicted Nodes:</B><BR>';
|
||||
for(let k in conflicts) {
|
||||
let node_name = conflicts[k][0];
|
||||
|
||||
let extension_name = conflicts[k][1].split('/').pop();
|
||||
if(extension_name.endsWith('/')) {
|
||||
extension_name = extension_name.slice(0, -1);
|
||||
}
|
||||
if(node_name.endsWith('.git')) {
|
||||
extension_name = extension_name.slice(0, -4);
|
||||
}
|
||||
|
||||
buf += `${node_name} [${extension_name}], `;
|
||||
}
|
||||
|
||||
if(buf.endsWith(', ')) {
|
||||
buf = buf.slice(0, -2);
|
||||
}
|
||||
buf += "</FONT>";
|
||||
data4.innerHTML += buf;
|
||||
}
|
||||
|
||||
var data5 = document.createElement('td');
|
||||
data5.style.textAlign = "center";
|
||||
|
||||
@ -424,6 +503,7 @@ export class CustomNodesInstaller extends ComfyDialog {
|
||||
installBtn.innerHTML = 'Uninstall';
|
||||
installBtn.style.backgroundColor = 'red';
|
||||
break;
|
||||
case 'Fail':
|
||||
case 'True':
|
||||
installBtn3 = document.createElement('button');
|
||||
installBtn3.innerHTML = 'Disable';
|
||||
@ -441,7 +521,7 @@ export class CustomNodesInstaller extends ComfyDialog {
|
||||
installBtn.style.color = 'white';
|
||||
break;
|
||||
default:
|
||||
installBtn.innerHTML = 'Try Install';
|
||||
installBtn.innerHTML = `Try Install${data.installed}`;
|
||||
installBtn.style.backgroundColor = 'Gray';
|
||||
installBtn.style.color = 'white';
|
||||
}
|
||||
@ -479,7 +559,10 @@ export class CustomNodesInstaller extends ComfyDialog {
|
||||
|
||||
data5.appendChild(installBtn);
|
||||
|
||||
dataRow.style.backgroundColor = "var(--bg-color)";
|
||||
if(data.installed == 'Fail')
|
||||
dataRow.style.backgroundColor = "#880000";
|
||||
else
|
||||
dataRow.style.backgroundColor = "var(--bg-color)";
|
||||
dataRow.style.color = "var(--fg-color)";
|
||||
dataRow.style.textAlign = "left";
|
||||
|
||||
@ -548,6 +631,7 @@ export class CustomNodesInstaller extends ComfyDialog {
|
||||
{ value:'Update', text:'Filter: update' },
|
||||
{ value:'True', text:'Filter: installed' },
|
||||
{ value:'False', text:'Filter: not-installed' },
|
||||
{ value:'Fail', text:'Filter: import failed' },
|
||||
];
|
||||
|
||||
items.forEach(item => {
|
||||
|
||||
@ -2565,6 +2565,138 @@
|
||||
"title_aux": "comfy-nodes"
|
||||
}
|
||||
],
|
||||
"https://github.com/comfyanonymous/ComfyUI": [
|
||||
[
|
||||
"BasicScheduler",
|
||||
"CLIPLoader",
|
||||
"CLIPMergeSimple",
|
||||
"CLIPSave",
|
||||
"CLIPSetLastLayer",
|
||||
"CLIPTextEncode",
|
||||
"CLIPTextEncodeSDXL",
|
||||
"CLIPTextEncodeSDXLRefiner",
|
||||
"CLIPVisionEncode",
|
||||
"CLIPVisionLoader",
|
||||
"Canny",
|
||||
"CheckpointLoader",
|
||||
"CheckpointLoaderSimple",
|
||||
"CheckpointSave",
|
||||
"ConditioningAverage",
|
||||
"ConditioningCombine",
|
||||
"ConditioningConcat",
|
||||
"ConditioningSetArea",
|
||||
"ConditioningSetAreaPercentage",
|
||||
"ConditioningSetMask",
|
||||
"ConditioningSetTimestepRange",
|
||||
"ConditioningZeroOut",
|
||||
"ControlNetApply",
|
||||
"ControlNetApplyAdvanced",
|
||||
"ControlNetLoader",
|
||||
"CropMask",
|
||||
"DiffControlNetLoader",
|
||||
"DiffusersLoader",
|
||||
"DualCLIPLoader",
|
||||
"EmptyImage",
|
||||
"EmptyLatentImage",
|
||||
"ExponentialScheduler",
|
||||
"FeatherMask",
|
||||
"FlipSigmas",
|
||||
"FreeU",
|
||||
"FreeU_V2",
|
||||
"GLIGENLoader",
|
||||
"GLIGENTextBoxApply",
|
||||
"GrowMask",
|
||||
"HyperTile",
|
||||
"HypernetworkLoader",
|
||||
"ImageBatch",
|
||||
"ImageBlend",
|
||||
"ImageBlur",
|
||||
"ImageColorToMask",
|
||||
"ImageCompositeMasked",
|
||||
"ImageCrop",
|
||||
"ImageInvert",
|
||||
"ImageOnlyCheckpointLoader",
|
||||
"ImagePadForOutpaint",
|
||||
"ImageQuantize",
|
||||
"ImageScale",
|
||||
"ImageScaleBy",
|
||||
"ImageScaleToTotalPixels",
|
||||
"ImageSharpen",
|
||||
"ImageToMask",
|
||||
"ImageUpscaleWithModel",
|
||||
"InvertMask",
|
||||
"JoinImageWithAlpha",
|
||||
"KSampler",
|
||||
"KSamplerAdvanced",
|
||||
"KSamplerSelect",
|
||||
"KarrasScheduler",
|
||||
"LatentAdd",
|
||||
"LatentBlend",
|
||||
"LatentComposite",
|
||||
"LatentCompositeMasked",
|
||||
"LatentCrop",
|
||||
"LatentFlip",
|
||||
"LatentFromBatch",
|
||||
"LatentInterpolate",
|
||||
"LatentMultiply",
|
||||
"LatentRotate",
|
||||
"LatentSubtract",
|
||||
"LatentUpscale",
|
||||
"LatentUpscaleBy",
|
||||
"LoadImage",
|
||||
"LoadImageMask",
|
||||
"LoadLatent",
|
||||
"LoraLoader",
|
||||
"LoraLoaderModelOnly",
|
||||
"MaskComposite",
|
||||
"MaskToImage",
|
||||
"ModelMergeAdd",
|
||||
"ModelMergeBlocks",
|
||||
"ModelMergeSimple",
|
||||
"ModelMergeSubtract",
|
||||
"ModelSamplingContinuousEDM",
|
||||
"ModelSamplingDiscrete",
|
||||
"PatchModelAddDownscale",
|
||||
"PolyexponentialScheduler",
|
||||
"PorterDuffImageComposite",
|
||||
"PreviewImage",
|
||||
"RebatchLatents",
|
||||
"RepeatImageBatch",
|
||||
"RepeatLatentBatch",
|
||||
"RescaleCFG",
|
||||
"SVD_img2vid_Conditioning",
|
||||
"SamplerCustom",
|
||||
"SamplerDPMPP_2M_SDE",
|
||||
"SamplerDPMPP_SDE",
|
||||
"SaveAnimatedPNG",
|
||||
"SaveAnimatedWEBP",
|
||||
"SaveImage",
|
||||
"SaveLatent",
|
||||
"SetLatentNoiseMask",
|
||||
"SolidMask",
|
||||
"SplitImageWithAlpha",
|
||||
"SplitSigmas",
|
||||
"StyleModelApply",
|
||||
"StyleModelLoader",
|
||||
"TomePatchModel",
|
||||
"UNETLoader",
|
||||
"UpscaleModelLoader",
|
||||
"VAEDecode",
|
||||
"VAEDecodeTiled",
|
||||
"VAEEncode",
|
||||
"VAEEncodeForInpaint",
|
||||
"VAEEncodeTiled",
|
||||
"VAELoader",
|
||||
"VAESave",
|
||||
"VPScheduler",
|
||||
"VideoLinearCFGGuidance",
|
||||
"unCLIPCheckpointLoader",
|
||||
"unCLIPConditioning"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI"
|
||||
}
|
||||
],
|
||||
"https://github.com/comfyanonymous/ComfyUI_experiments": [
|
||||
[
|
||||
"ModelMergeBlockNumber",
|
||||
@ -4274,6 +4406,7 @@
|
||||
[
|
||||
"Auto Merge Block Weighted",
|
||||
"CLIPMergeSimple",
|
||||
"CheckpointSave",
|
||||
"ModelMergeBlocks",
|
||||
"ModelMergeSimple"
|
||||
],
|
||||
|
||||
@ -9,6 +9,7 @@ import locale
|
||||
|
||||
|
||||
message_collapses = []
|
||||
import_failed_extensions = set()
|
||||
|
||||
|
||||
def register_message_collapse(f):
|
||||
@ -16,10 +17,16 @@ def register_message_collapse(f):
|
||||
message_collapses.append(f)
|
||||
|
||||
|
||||
def is_import_failed_extension(x):
|
||||
global import_failed_extensions
|
||||
return x in import_failed_extensions
|
||||
|
||||
|
||||
sys.__comfyui_manager_register_message_collapse = register_message_collapse
|
||||
sys.__comfyui_manager_is_import_failed_extension = is_import_failed_extension
|
||||
|
||||
comfyui_manager_path = os.path.dirname(__file__)
|
||||
custom_nodes_path = os.path.join(comfyui_manager_path, "..")
|
||||
custom_nodes_path = os.path.abspath(os.path.join(comfyui_manager_path, ".."))
|
||||
startup_script_path = os.path.join(comfyui_manager_path, "startup-scripts")
|
||||
restore_snapshot_path = os.path.join(startup_script_path, "restore-snapshot.json")
|
||||
git_script_path = os.path.join(comfyui_manager_path, "git_helper.py")
|
||||
@ -78,7 +85,12 @@ try:
|
||||
original_stdout = sys.stdout
|
||||
original_stderr = sys.stderr
|
||||
|
||||
tqdm = r'\d+%.*\[(.*?)\]'
|
||||
pat_tqdm = r'\d+%.*\[(.*?)\]'
|
||||
pat_import_fail = r'seconds \(IMPORT FAILED\):'
|
||||
pat_custom_node = r'[/\\]custom_nodes[/\\](.*)$'
|
||||
|
||||
is_start_mode = True
|
||||
is_import_fail_mode = False
|
||||
|
||||
log_file = open(f"comfyui{postfix}.log", "w", encoding="utf-8")
|
||||
log_lock = threading.Lock()
|
||||
@ -99,11 +111,30 @@ try:
|
||||
raise ValueError("The object does not have a fileno method")
|
||||
|
||||
def write(self, message):
|
||||
global is_start_mode
|
||||
global is_import_fail_mode
|
||||
|
||||
if any(f(message) for f in message_collapses):
|
||||
return
|
||||
|
||||
if is_start_mode:
|
||||
if is_import_fail_mode:
|
||||
match = re.search(pat_custom_node, message)
|
||||
if match:
|
||||
import_failed_extensions.add(match.group(1))
|
||||
is_import_fail_mode = False
|
||||
else:
|
||||
match = re.search(pat_import_fail, message)
|
||||
if match:
|
||||
is_import_fail_mode = True
|
||||
else:
|
||||
is_import_fail_mode = False
|
||||
|
||||
if 'Starting server' in message:
|
||||
is_start_mode = False
|
||||
|
||||
if not self.is_stdout:
|
||||
match = re.search(tqdm, message)
|
||||
match = re.search(pat_tqdm, message)
|
||||
if match:
|
||||
message = re.sub(r'([#|])\d', r'\1▌', message)
|
||||
message = re.sub('#', '█', message)
|
||||
|
||||
28
scanner.py
28
scanner.py
@ -5,9 +5,12 @@ from git import Repo
|
||||
from torchvision.datasets.utils import download_url
|
||||
import concurrent
|
||||
|
||||
builtin_nodes = ["KSampler", "CheckpointSave"]
|
||||
builtin_nodes = set()
|
||||
|
||||
|
||||
def scan_in_file(filename, is_builtin=False):
|
||||
global builtin_nodes
|
||||
|
||||
def scan_in_file(filename):
|
||||
try:
|
||||
with open(filename, encoding='utf-8') as file:
|
||||
code = file.read()
|
||||
@ -63,9 +66,12 @@ def scan_in_file(filename):
|
||||
key, value = line[1:].strip().split(':')
|
||||
metadata[key.strip()] = value.strip()
|
||||
|
||||
for x in builtin_nodes:
|
||||
if x in nodes:
|
||||
nodes.remove(x)
|
||||
if is_builtin:
|
||||
builtin_nodes += set(nodes)
|
||||
else:
|
||||
for x in builtin_nodes:
|
||||
if x in nodes:
|
||||
nodes.remove(x)
|
||||
|
||||
return nodes, metadata
|
||||
|
||||
@ -113,7 +119,9 @@ def get_git_urls_from_json(json_file):
|
||||
if node.get('install_type') == 'git-clone':
|
||||
files = node.get('files', [])
|
||||
if files:
|
||||
git_clone_files.append((files[0],node.get('title')))
|
||||
git_clone_files.append((files[0], node.get('title')))
|
||||
|
||||
git_clone_files.append(("https://github.com/comfyanonymous/ComfyUI", "ComfyUI"))
|
||||
|
||||
return git_clone_files
|
||||
|
||||
@ -190,7 +198,7 @@ def update_custom_nodes():
|
||||
|
||||
with concurrent.futures.ThreadPoolExecutor(10) as executor:
|
||||
executor.map(download_and_store_info, py_url_titles)
|
||||
|
||||
|
||||
return node_info
|
||||
|
||||
|
||||
@ -198,6 +206,10 @@ def gen_json(node_info):
|
||||
# scan from .py file
|
||||
node_files, node_dirs = get_nodes(".tmp")
|
||||
|
||||
comfyui_path = os.path.abspath(os.path.join('.tmp', "ComfyUI"))
|
||||
node_dirs.remove(comfyui_path)
|
||||
node_dirs = [comfyui_path] + node_dirs
|
||||
|
||||
data = {}
|
||||
for dirname in node_dirs:
|
||||
py_files = get_py_file_paths(dirname)
|
||||
@ -205,7 +217,7 @@ def gen_json(node_info):
|
||||
|
||||
nodes = set()
|
||||
for py in py_files:
|
||||
nodes_in_file, metadata_in_file = scan_in_file(py)
|
||||
nodes_in_file, metadata_in_file = scan_in_file(py, dirname == "ComfyUI")
|
||||
nodes.update(nodes_in_file)
|
||||
metadata.update(metadata_in_file)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user