Merge remote-tracking branch 'upstream/main'

This commit is contained in:
kijai 2024-06-20 02:00:56 +03:00
commit a2bce79883
44 changed files with 18296 additions and 7368 deletions

21
.github/workflows/publish.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Publish to Comfy registry
on:
workflow_dispatch:
push:
branches:
- main
paths:
- "pyproject.toml"
jobs:
publish-node:
name: Publish Custom Node to registry
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Publish Custom Node
uses: Comfy-Org/publish-node-action@main
with:
## Add your own personal access token to your Github Repository secrets and reference it here.
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}

View File

@ -5,6 +5,8 @@
![menu](misc/menu.jpg)
## NOTICE
* V2.37 Show a ✅ mark to accounts that have been active on GitHub for more than six months.
* V2.33 Security policy is applied.
* V2.21 [cm-cli](docs/en/cm-cli.md) tool is added.
* V2.18 to V2.18.3 is not functioning due to a severe bug. Users on these versions are advised to promptly update to V2.18.4. Please navigate to the `ComfyUI/custom_nodes/ComfyUI-Manager` directory and execute `git pull` to update.
* You can see whole nodes info on [ComfyUI Nodes Info](https://ltdrdata.github.io/) page.
@ -84,6 +86,7 @@ This repository provides Colab notebooks that allow you to install and use Comfy
* Support for automatically installing dependencies of custom nodes upon restarting Colab notebooks.
## Changes
* **2.38** `Install Custom Nodes` menu is changed to `Custom Nodes Manager`.
* **2.21** [cm-cli](docs/en/cm-cli.md) tool is added.
* **2.4** Copy the connections of the nearest node by double-clicking.
* **2.2.3** Support Components System
@ -191,6 +194,7 @@ This repository provides Colab notebooks that allow you to install and use Comfy
* NOTE: Before submitting the PR after making changes, please check `Use local DB` and ensure that the extension list loads without any issues in the `Install custom nodes` dialog. Occasionally, missing or extra commas can lead to JSON syntax errors.
* The remaining JSON will be updated through scripts in the future, so you don't need to worry about it.
## Custom node support guide
* Currently, the system operates by cloning the git repository and sequentially installing the dependencies listed in requirements.txt using pip, followed by invoking the install.py script. In the future, we plan to discuss and determine the specifications for supporting custom nodes.
@ -275,11 +279,11 @@ NODE_CLASS_MAPPINGS.update({
## Support of missing nodes installation
![missing-menu](misc/missing-menu.png)
![missing-menu](misc/missing-menu.jpg)
* When you click on the ```Install Missing Custom Nodes``` button in the menu, it displays a list of extension nodes that contain nodes not currently present in the workflow.
![missing-list](misc/missing-list.png)
![missing-list](misc/missing-list.jpg)
## Additional Feature
@ -338,6 +342,31 @@ When you run the `scan.sh` script:
* Edit `config.ini` file: add `windows_selector_event_loop_policy = True`
## Security policy
* Edit `config.ini` file: add `security_level = <LEVEL>`
* `strong`
* doesn't allow `high` and `middle` level risky feature
* `normal`
* doesn't allow `high` level risky feature if `--listen` is specified and not starts with `127.`
* `middle` level risky feature is available
* `weak`
* all feature is available
* `high` level risky features
* `Install via git url`, `pip install`
* Installation of custom nodes registered not in the `default channel`.
* Display terminal log
* `middle` level risky features
* Uninstall/Update/Fix custom nodes
* Installation of custom nodes registered in the `default channel`.
* Restore/Remove Snapshot
* Restart
* `low` level risky features
* Update ComfyUI
## TODO: Unconventional form of custom node list
* https://github.com/diontimmer/Sample-Diffusion-ComfyUI-Extension
@ -347,6 +376,10 @@ When you run the `scan.sh` script:
* https://github.com/NielsGercama/comfyui_customsampling
* https://github.com/wrightdaniel2017/ComfyUI-VideoLipSync
* https://github.com/bxdsjs/ComfyUI-Image-preprocessing
* https://github.com/SMUELDigital/ComfyUI-ONSET
* https://github.com/SimithWang/comfyui-renameImages
* https://github.com/icefairy64/comfyui-model-tilt
* https://github.com/andrewharp/ComfyUI-EasyNodes
## Roadmap

View File

@ -28,7 +28,7 @@
{
"id":"https://github.com/BadCafeCode/masquerade-nodes-comfyui",
"tags":"ddetailer",
"description": "This extension provides a way to recognize and enhance masks for faces similar to Impact Pack."
"description": "This extension is a less feature-rich and well-maintained alternative to Impact Pack, but it has fewer dependencies and may be easier to install on abnormal configurations. The author recommends trying Impact Pack first."
},
{
"id":"https://github.com/BlenderNeko/ComfyUI_Cutoff",
@ -126,7 +126,7 @@
"description": "This is a ported version of ComfyUI for the sd-webui-roop-nsfw extension."
},
{
"id":"https://github.com/laksjdjf/attention-couple-ComfyUI",
"id":"https://github.com/laksjdjf/cgem156-ComfyUI",
"tags":"regional prompt, latent couple, prompt",
"description": "This custom nodes provide a functionality similar to regional prompts, offering couple features at the attention level."
},
@ -209,6 +209,16 @@
"id":"https://github.com/AIFSH/ComfyUI-RVC",
"tags":"sonar",
"description": "a comfyui custom node for [a/Retrieval-based-Voice-Conversion-WebUI](https://github.com/RVC-Project/Retrieval-based-Voice-Conversion-WebUI.git), you can Voice-Conversion in comfyui now!"
},
{
"id":"https://github.com/portu-sim/comfyui-bmab",
"tags":"bmab",
"description": "a comfyui custom node for [a/sd-webui-bmab](https://github.com/portu-sim/sd-webui-bmab)"
},
{
"id":"https://github.com/ThereforeGames/ComfyUI-Unprompted",
"tags":"unprompted",
"description": "This extension is a port of [a/unprompted](https://github.com/ThereforeGames/unprompted)"
}
]
}

View File

@ -1,5 +1,8 @@
#!/bin/bash
echo
echo CHECK1
files=(
"custom-node-list.json"
"model-list.json"
@ -26,3 +29,14 @@ files=(
for file in "${files[@]}"; do
python json-checker.py "$file"
done
echo
echo CHECK2
find ~/.tmp/default -name "*.py" -print0 | xargs -0 grep "crypto"
echo
echo CHECK3
find ~/.tmp/default -name "requirements.txt" | xargs grep "^\s*https\\?:"
find ~/.tmp/default -name "requirements.txt" | xargs grep "\.whl"
echo

1010
cm-cli.py

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -336,7 +336,7 @@ def restore_pip_snapshot(pips, options):
non_local_url = []
for k, v in pips.items():
if v == "":
non_url.append(v)
non_url.append(k)
else:
if v.startswith('file:'):
local_url.append(v)

File diff suppressed because it is too large Load Diff

View File

@ -23,7 +23,7 @@ sys.path.append(glob_path)
import cm_global
from manager_util import *
version = [2, 30]
version = [2, 38, 2]
version_str = f"V{version[0]}.{version[1]}" + (f'.{version[2]}' if len(version) > 2 else '')
comfyui_manager_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
@ -204,7 +204,8 @@ def write_config():
'double_click_policy': get_config()['double_click_policy'],
'windows_selector_event_loop_policy': get_config()['windows_selector_event_loop_policy'],
'model_download_by_agent': get_config()['model_download_by_agent'],
'downgrade_blacklist': get_config()['downgrade_blacklist']
'downgrade_blacklist': get_config()['downgrade_blacklist'],
'security_level': get_config()['security_level'],
}
with open(config_path, 'w') as configfile:
config.write(configfile)
@ -216,20 +217,30 @@ def read_config():
config.read(config_path)
default_conf = config['default']
# policy migration: disable_unsecure_features -> security_level
if 'disable_unsecure_features' in default_conf:
if default_conf['disable_unsecure_features'].lower() == 'true':
security_level = 'strong'
else:
security_level = 'normal'
else:
security_level = default_conf['security_level'] if 'security_level' in default_conf else 'normal'
return {
'preview_method': default_conf['preview_method'] if 'preview_method' in default_conf else manager_funcs.get_current_preview_method(),
'badge_mode': default_conf['badge_mode'] if 'badge_mode' in default_conf else 'none',
'git_exe': default_conf['git_exe'] if 'git_exe' in default_conf else '',
'channel_url': default_conf['channel_url'] if 'channel_url' in default_conf else 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main',
'share_option': default_conf['share_option'] if 'share_option' in default_conf else 'all',
'bypass_ssl': default_conf['bypass_ssl'] if 'bypass_ssl' in default_conf else False,
'file_logging': default_conf['file_logging'] if 'file_logging' in default_conf else True,
'bypass_ssl': default_conf['bypass_ssl'].lower() == 'true' if 'bypass_ssl' in default_conf else False,
'file_logging': default_conf['file_logging'].lower() == 'true' if 'file_logging' in default_conf else True,
'default_ui': default_conf['default_ui'] if 'default_ui' in default_conf else 'none',
'component_policy': default_conf['component_policy'] if 'component_policy' in default_conf else 'workflow',
'double_click_policy': default_conf['double_click_policy'] if 'double_click_policy' in default_conf else 'copy-all',
'windows_selector_event_loop_policy': default_conf['windows_selector_event_loop_policy'] if 'windows_selector_event_loop_policy' in default_conf else False,
'model_download_by_agent': default_conf['model_download_by_agent'] if 'model_download_by_agent' in default_conf else False,
'windows_selector_event_loop_policy': default_conf['windows_selector_event_loop_policy'].lower() == 'true' if 'windows_selector_event_loop_policy' in default_conf else False,
'model_download_by_agent': default_conf['model_download_by_agent'].lower() == 'true' if 'model_download_by_agent' in default_conf else False,
'downgrade_blacklist': default_conf['downgrade_blacklist'] if 'downgrade_blacklist' in default_conf else '',
'security_level': security_level
}
except Exception:
@ -246,7 +257,8 @@ def read_config():
'double_click_policy': 'copy-all',
'windows_selector_event_loop_policy': False,
'model_download_by_agent': False,
'downgrade_blacklist': ''
'downgrade_blacklist': '',
'security_level': 'normal',
}
@ -384,7 +396,7 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa
package_name = remap_pip_package(line.strip())
if package_name and not package_name.startswith('#'):
install_cmd = [sys.executable, "-m", "pip", "install", package_name]
if package_name.strip() != "":
if package_name.strip() != "" and not package_name.startswith('#'):
try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
if os.path.exists(install_script_path):
@ -564,7 +576,7 @@ def git_pull(path):
async def get_data(uri, silent=False):
if not silent:
print(f"FETCH DATA from: {uri}")
print(f"FETCH DATA from: {uri}", end="")
if uri.startswith("http"):
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
@ -576,6 +588,8 @@ async def get_data(uri, silent=False):
json_text = f.read()
json_obj = json.loads(json_text)
if not silent:
print(f" [DONE]")
return json_obj
@ -964,7 +978,6 @@ def get_current_snapshot():
# Get ComfyUI hash
repo_path = comfy_path
print(f"comfy_path: {comfy_path}")
if not os.path.exists(os.path.join(repo_path, '.git')):
print(f"ComfyUI update fail: The installed ComfyUI does not have a Git repository.")
return {}
@ -1184,3 +1197,4 @@ def unzip(model_path):
os.remove(model_path)
return True

View File

@ -42,6 +42,36 @@ from comfy.cli_args import args
import latent_preview
is_local_mode = args.listen.startswith('127.')
def is_allowed_security_level(level):
if level == 'high':
if is_local_mode:
return core.get_config()['security_level'].lower() in ['weak', 'normal']
else:
return core.get_config()['security_level'].lower() == 'weak'
elif level == 'middle':
return core.get_config()['security_level'].lower() in ['weak', 'normal']
else:
return True
async def get_risky_level(files):
json_data1 = await core.get_data_by_mode('local', 'custom-node-list.json')
json_data2 = await core.get_data_by_mode('cache', 'custom-node-list.json', channel_url='https://github.com/ltdrdata/ComfyUI-Manager/raw/main/custom-node-list.json')
all_urls = set()
for x in json_data1['custom_nodes'] + json_data2['custom_nodes']:
all_urls.update(x['files'])
for x in files:
if x not in all_urls:
return "high"
return "middle"
class ManagerFuncsInComfyUI(core.ManagerFuncs):
def get_current_preview_method(self):
if args.preview_method == latent_preview.LatentPreviewMethod.Auto:
@ -171,19 +201,18 @@ def print_comfyui_version():
print_comfyui_version()
async def populate_github_stats(json_obj, filename, silent=False):
uri = os.path.join(core.comfyui_manager_path, filename)
with open(uri, "r", encoding='utf-8') as f:
github_stats = json.load(f)
async def populate_github_stats(json_obj, json_obj_github):
if 'custom_nodes' in json_obj:
for i, node in enumerate(json_obj['custom_nodes']):
url = node['reference']
if url in github_stats:
json_obj['custom_nodes'][i]['stars'] = github_stats[url]['stars']
json_obj['custom_nodes'][i]['last_update'] = github_stats[url]['last_update']
if url in json_obj_github:
json_obj['custom_nodes'][i]['stars'] = json_obj_github[url]['stars']
json_obj['custom_nodes'][i]['last_update'] = json_obj_github[url]['last_update']
json_obj['custom_nodes'][i]['trust'] = json_obj_github[url]['author_account_age_days'] > 180
else:
json_obj['custom_nodes'][i]['stars'] = -1
json_obj['custom_nodes'][i]['last_update'] = -1
json_obj['custom_nodes'][i]['trust'] = False
return json_obj
@ -358,6 +387,10 @@ async def fetch_updates(request):
@PromptServer.instance.routes.get("/customnode/update_all")
async def update_all(request):
if not is_allowed_security_level('middle'):
print(f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.")
return web.Response(status=403)
try:
core.save_snapshot_with_postfix('autosave')
@ -439,7 +472,8 @@ async def fetch_customnode_list(request):
channel = core.get_config()['channel_url']
json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'custom-node-list.json')
json_obj = await populate_github_stats(json_obj, "github-stats.json")
json_obj_github = await core.get_data_by_mode(request.rel_url.query["mode"], 'github-stats.json', 'default')
json_obj = await populate_github_stats(json_obj, json_obj_github)
def is_ignored_notice(code):
if code is not None and code.startswith('#NOTICE_'):
@ -473,6 +507,16 @@ async def fetch_customnode_list(request):
return web.json_response(json_obj, content_type='application/json')
@PromptServer.instance.routes.get("/customnode/alternatives")
async def fetch_customnode_alternatives(request):
alter_json = await core.get_data_by_mode(request.rel_url.query["mode"], 'alter-list.json')
for item in alter_json['items']:
populate_markdown(item)
return web.json_response(alter_json, content_type='application/json')
@PromptServer.instance.routes.get("/alternatives/getlist")
async def fetch_alternatives_list(request):
if "skip_update" in request.rel_url.query and request.rel_url.query["skip_update"] == "true":
@ -551,6 +595,10 @@ async def get_snapshot_list(request):
@PromptServer.instance.routes.get("/snapshot/remove")
async def remove_snapshot(request):
if not is_allowed_security_level('middle'):
print(f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.")
return web.Response(status=403)
try:
target = request.rel_url.query["target"]
@ -565,6 +613,10 @@ async def remove_snapshot(request):
@PromptServer.instance.routes.get("/snapshot/restore")
async def remove_snapshot(request):
if not is_allowed_security_level('middle'):
print(f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.")
return web.Response(status=403)
try:
target = request.rel_url.query["target"]
@ -729,8 +781,17 @@ def copy_set_active(files, is_disable, js_path_name='.'):
@PromptServer.instance.routes.post("/customnode/install")
async def install_custom_node(request):
if not is_allowed_security_level('middle'):
print(f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.")
return web.Response(status=403)
json_data = await request.json()
risky_level = await get_risky_level(json_data['files'])
if not is_allowed_security_level(risky_level):
print(f"ERROR: This installation is not allowed in this security_level. Please contact the administrator.")
return web.Response(status=404)
install_type = json_data['install_type']
print(f"Install custom node '{json_data['title']}'")
@ -767,6 +828,10 @@ async def install_custom_node(request):
@PromptServer.instance.routes.post("/customnode/fix")
async def fix_custom_node(request):
if not is_allowed_security_level('middle'):
print(f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.")
return web.Response(status=403)
json_data = await request.json()
install_type = json_data['install_type']
@ -797,6 +862,10 @@ async def fix_custom_node(request):
@PromptServer.instance.routes.post("/customnode/install/git_url")
async def install_custom_node_git_url(request):
if not is_allowed_security_level('high'):
print(f"ERROR: To use this feature, you must set '--listen' to a local IP and set the security level to 'middle' or 'weak'. Please contact the administrator.")
return web.Response(status=403)
url = await request.text()
res = core.gitclone_install([url])
@ -809,6 +878,10 @@ async def install_custom_node_git_url(request):
@PromptServer.instance.routes.post("/customnode/install/pip")
async def install_custom_node_git_url(request):
if not is_allowed_security_level('high'):
print(f"ERROR: To use this feature, you must set '--listen' to a local IP and set the security level to 'middle' or 'weak'. Please contact the administrator.")
return web.Response(status=403)
packages = await request.text()
core.pip_install(packages.split(' '))
@ -817,6 +890,10 @@ async def install_custom_node_git_url(request):
@PromptServer.instance.routes.post("/customnode/uninstall")
async def uninstall_custom_node(request):
if not is_allowed_security_level('middle'):
print(f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.")
return web.Response(status=403)
json_data = await request.json()
install_type = json_data['install_type']
@ -841,6 +918,10 @@ async def uninstall_custom_node(request):
@PromptServer.instance.routes.post("/customnode/update")
async def update_custom_node(request):
if not is_allowed_security_level('middle'):
print(f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.")
return web.Response(status=403)
json_data = await request.json()
install_type = json_data['install_type']
@ -955,6 +1036,10 @@ manager_terminal_hook = ManagerTerminalHook()
@PromptServer.instance.routes.get("/manager/terminal")
async def terminal_mode(request):
if not is_allowed_security_level('high'):
print(f"ERROR: To use this action, a security_level of `weak` is required. Please contact the administrator.")
return web.Response(status=403)
if "mode" in request.rel_url.query:
if request.rel_url.query['mode'] == 'true':
sys.__comfyui_manager_terminal_hook.add_hook('cm', manager_terminal_hook)
@ -1043,6 +1128,19 @@ async def channel_url_list(request):
return web.Response(status=200)
def add_target_blank(html_text):
pattern = r'(<a\s+href="[^"]*"\s*[^>]*)(>)'
def add_target(match):
if 'target=' not in match.group(1):
return match.group(1) + ' target="_blank"' + match.group(2)
return match.group(0)
modified_html = re.sub(pattern, add_target, html_text)
return modified_html
@PromptServer.instance.routes.get("/manager/notice")
async def get_notice(request):
url = "github.com"
@ -1063,6 +1161,8 @@ async def get_notice(request):
# markdown_content += f"<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;()"
markdown_content += f"<BR>Manager: {core.version_str}"
markdown_content = add_target_blank(markdown_content)
try:
if core.comfy_ui_required_commit_datetime.date() > core.comfy_ui_commit_datetime.date():
markdown_content = f'<P style="text-align: center; color:red; background-color:white; font-weight:bold">Your ComfyUI is too OUTDATED!!!</P>' + markdown_content
@ -1078,6 +1178,10 @@ async def get_notice(request):
@PromptServer.instance.routes.get("/manager/reboot")
def restart(self):
if not is_allowed_security_level('middle'):
print(f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.")
return web.Response(status=403)
try:
sys.stdout.close_log()
except Exception as e:
@ -1582,8 +1686,9 @@ async def default_cache_update():
b = get_cache("extension-node-map.json")
c = get_cache("model-list.json")
d = get_cache("alter-list.json")
e = get_cache("github-stats.json")
await asyncio.gather(a, b, c, d)
await asyncio.gather(a, b, c, d, e)
threading.Thread(target=lambda: asyncio.run(default_cache_update())).start()

80
glob/security_check.py Normal file
View File

@ -0,0 +1,80 @@
import sys
import subprocess
import os
def security_check():
print("[START] Security scan")
custom_nodes_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
comfyui_path = os.path.abspath(os.path.join(custom_nodes_path, '..'))
guide = {
"ComfyUI_LLMVISION": """
0.Remove ComfyUI\\custom_nodes\\ComfyUI_LLMVISION.
1.Remove pip packages: openai-1.16.3.dist-info, anthropic-0.21.4.dist-info, openai-1.30.2.dist-info, anthropic-0.26.1.dist-info, %LocalAppData%\\rundll64.exe
(For portable versions, it is recommended to reinstall. If you are using a venv, it is advised to recreate the venv.)
2.Remove these files in your system: lib/browser/admin.py, Cadmino.py, Fadmino.py, VISION-D.exe, BeamNG.UI.exe
3.Check your Windows registry for the key listed above and remove it.
(HKEY_CURRENT_USER\\Software\\OpenAICLI)
4.Run a malware scanner.
5.Change all of your passwords, everywhere.
(Reinstall OS is recommended.)
\n
Detailed information: https://old.reddit.com/r/comfyui/comments/1dbls5n/psa_if_youve_used_the_comfyui_llmvision_node_from/
""",
"lolMiner": """
1. Remove pip packages: lolMiner*
2. Remove files: lolMiner*, 4G_Ethash_Linux_Readme.txt, mine* in ComfyUI dir.
(Reinstall ComfyUI is recommended.)
"""
}
node_blacklist = {"ComfyUI_LLMVISION": "ComfyUI_LLMVISION"}
pip_blacklist = {"AppleBotzz": "ComfyUI_LLMVISION"}
file_blacklist = {
"ComfyUI_LLMVISION": ["%LocalAppData%\\rundll64.exe"],
"lolMiner": [os.path.join(comfyui_path, 'lolMiner')]
}
installed_pips = subprocess.check_output([sys.executable, '-m', "pip", "freeze"], text=True)
detected = set()
for k, v in node_blacklist.items():
if os.path.exists(os.path.join(custom_nodes_path, k)):
print(f"[SECURITY ALERT] custom node '{k}' is dangerous.")
detected.add(v)
for k, v in pip_blacklist.items():
if k in installed_pips:
detected.add(v)
break
for k, v in file_blacklist.items():
for x in v:
if os.path.exists(os.path.expandvars(x)):
detected.add(k)
break
if len(detected) > 0:
for line in installed_pips.split('\n'):
for k, v in pip_blacklist.items():
if k in line:
print(f"[SECURITY ALERT] '{line}' is dangerous.")
print("\n########################################################################")
print(" Malware has been detected, forcibly terminating ComfyUI execution.")
print("########################################################################\n")
for x in detected:
print(f"\n======== TARGET: {x} =========")
print(f"\nTODO:")
print(guide.get(x))
exit(-1)
print("[DONE] Security scan")

View File

@ -1,566 +0,0 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
import { install_checked_custom_node, manager_instance, rebootAPI } from "./common.js";
async function getAlterList() {
var mode = manager_instance.datasrc_combo.value;
var skip_update = "";
if(manager_instance.update_check_checkbox.checked)
skip_update = "&skip_update=true";
const response = await api.fetchApi(`/alternatives/getlist?mode=${mode}${skip_update}`);
const data = await response.json();
return data;
}
export class AlternativesInstaller extends ComfyDialog {
static instance = null;
install_buttons = [];
message_box = null;
data = null;
clear() {
this.install_buttons = [];
this.message_box = null;
this.data = null;
}
constructor(app, manager_dialog) {
super();
this.manager_dialog = manager_dialog;
this.search_keyword = '';
this.element = $el("div.comfy-modal", { parent: document.body }, []);
}
startInstall(target) {
const self = AlternativesInstaller.instance;
self.updateMessage(`<BR><font color="green">Installing '${target.title}'</font>`);
}
disableButtons() {
for(let i in this.install_buttons) {
this.install_buttons[i].disabled = true;
this.install_buttons[i].style.backgroundColor = 'gray';
}
}
apply_searchbox(data) {
let keyword = this.search_box.value.toLowerCase();
for(let i in this.grid_rows) {
let data1 = this.grid_rows[i].data;
let data2 = data1.custom_node;
if(!data2)
continue;
let content = data1.tags.toLowerCase() + data1.description.toLowerCase() + data2.author.toLowerCase() + data2.description.toLowerCase() + data2.title.toLowerCase();
if(this.filter && this.filter != '*') {
if(this.filter != data2.installed) {
this.grid_rows[i].control.style.display = 'none';
continue;
}
}
if(keyword == "")
this.grid_rows[i].control.style.display = null;
else if(content.includes(keyword)) {
this.grid_rows[i].control.style.display = null;
}
else {
this.grid_rows[i].control.style.display = 'none';
}
}
}
async invalidateControl() {
this.clear();
// splash
while (this.element.children.length) {
this.element.removeChild(this.element.children[0]);
}
const msg = $el('div', {id:'custom-message'},
[$el('br'),
'The custom node DB is currently being updated, and updates to custom nodes are being checked for.',
$el('br'),
'NOTE: Update only checks for extensions that have been fetched.',
$el('br')]);
msg.style.height = '100px';
msg.style.verticalAlign = 'middle';
this.element.appendChild(msg);
// invalidate
this.data = (await getAlterList()).items;
this.element.removeChild(msg);
while (this.element.children.length) {
this.element.removeChild(this.element.children[0]);
}
this.createHeaderControls();
await this.createGrid();
this.apply_searchbox(this.data);
this.createBottomControls();
}
updateMessage(msg, btn_id) {
this.message_box.innerHTML = msg;
if(btn_id) {
const rebootButton = document.getElementById(btn_id);
const self = this;
rebootButton.addEventListener("click",
function() {
if(rebootAPI()) {
self.close();
self.manager_dialog.close();
}
});
}
}
invalidate_checks(is_checked, install_state) {
if(is_checked) {
for(let i in this.grid_rows) {
let data = this.grid_rows[i].data;
let checkbox = this.grid_rows[i].checkbox;
let buttons = this.grid_rows[i].buttons;
checkbox.disabled = data.custom_node.installed != install_state;
if(checkbox.disabled) {
for(let j in buttons) {
buttons[j].style.display = 'none';
}
}
else {
for(let j in buttons) {
buttons[j].style.display = null;
}
}
}
this.checkbox_all.disabled = false;
}
else {
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
if(checkbox.check)
return; // do nothing
}
// every checkbox is unchecked -> enable all checkbox
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
let buttons = this.grid_rows[i].buttons;
checkbox.disabled = false;
for(let j in buttons) {
buttons[j].style.display = null;
}
}
this.checkbox_all.checked = false;
this.checkbox_all.disabled = true;
}
}
check_all(is_checked) {
if(is_checked) {
// lookup first checked item's state
let check_state = null;
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
if(checkbox.checked) {
check_state = this.grid_rows[i].data.custom_node.installed;
}
}
if(check_state == null)
return;
// check only same state items
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
if(this.grid_rows[i].data.custom_node.installed == check_state)
checkbox.checked = true;
}
}
else {
// uncheck all
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
let buttons = this.grid_rows[i].buttons;
checkbox.checked = false;
checkbox.disabled = false;
for(let j in buttons) {
buttons[j].style.display = null;
}
}
this.checkbox_all.disabled = true;
}
}
async createGrid() {
var grid = document.createElement('table');
grid.setAttribute('id', 'alternatives-grid');
this.grid_rows = {};
let self = this;
var thead = document.createElement('thead');
var tbody = document.createElement('tbody');
var headerRow = document.createElement('tr');
thead.style.position = "sticky";
thead.style.top = "0px";
thead.style.borderCollapse = "collapse";
thead.style.tableLayout = "fixed";
var header0 = document.createElement('th');
header0.style.width = "20px";
this.checkbox_all = $el("input",{type:'checkbox', id:'check_all'},[]);
header0.appendChild(this.checkbox_all);
this.checkbox_all.checked = false;
this.checkbox_all.disabled = true;
this.checkbox_all.addEventListener('change', function() { self.check_all.call(self, self.checkbox_all.checked); });
var header1 = document.createElement('th');
header1.innerHTML = '&nbsp;&nbsp;ID&nbsp;&nbsp;';
header1.style.width = "20px";
var header2 = document.createElement('th');
header2.innerHTML = 'Tags';
header2.style.width = "10%";
var header3 = document.createElement('th');
header3.innerHTML = 'Author';
header3.style.width = "150px";
var header4 = document.createElement('th');
header4.innerHTML = 'Title';
header4.style.width = "20%";
var header5 = document.createElement('th');
header5.innerHTML = 'Description';
header5.style.width = "50%";
var header6 = document.createElement('th');
header6.innerHTML = 'Install';
header6.style.width = "130px";
header1.style.position = "sticky";
header1.style.top = "0px";
header2.style.position = "sticky";
header2.style.top = "0px";
header3.style.position = "sticky";
header3.style.top = "0px";
header4.style.position = "sticky";
header4.style.top = "0px";
header5.style.position = "sticky";
header5.style.top = "0px";
thead.appendChild(headerRow);
headerRow.appendChild(header0);
headerRow.appendChild(header1);
headerRow.appendChild(header2);
headerRow.appendChild(header3);
headerRow.appendChild(header4);
headerRow.appendChild(header5);
headerRow.appendChild(header6);
headerRow.style.backgroundColor = "Black";
headerRow.style.color = "White";
headerRow.style.textAlign = "center";
headerRow.style.width = "100%";
headerRow.style.padding = "0";
grid.appendChild(thead);
grid.appendChild(tbody);
if(this.data)
for (var i = 0; i < this.data.length; i++) {
const data = this.data[i];
var dataRow = document.createElement('tr');
let data0 = document.createElement('td');
let checkbox = $el("input",{type:'checkbox', id:`check_${i}`},[]);
data0.appendChild(checkbox);
checkbox.checked = false;
checkbox.addEventListener('change', function() { self.invalidate_checks.call(self, checkbox.checked, data.custom_node?.installed); });
var data1 = document.createElement('td');
data1.style.textAlign = "center";
data1.innerHTML = i+1;
var data2 = document.createElement('td');
data2.innerHTML = `&nbsp;${data.tags}`;
var data3 = document.createElement('td');
var data4 = document.createElement('td');
if(data.custom_node) {
data3.innerHTML = `&nbsp;${data.custom_node.author}`;
data4.innerHTML = `&nbsp;<a href=${data.custom_node.reference} target="_blank"><font color="skyblue"><b>${data.custom_node.title}</b></font></a>`;
}
else {
data3.innerHTML = `&nbsp;Unknown`;
data4.innerHTML = `&nbsp;Unknown`;
}
var data5 = document.createElement('td');
data5.innerHTML = data.description;
var data6 = document.createElement('td');
data6.style.textAlign = "center";
var installBtn = document.createElement('button');
var installBtn2 = null;
var installBtn3 = null;
if(data.custom_node) {
this.install_buttons.push(installBtn);
switch(data.custom_node.installed) {
case 'Disabled':
installBtn3 = document.createElement('button');
installBtn3.innerHTML = 'Enable';
installBtn3.style.backgroundColor = 'blue';
installBtn3.style.color = 'white';
this.install_buttons.push(installBtn3);
installBtn.innerHTML = 'Uninstall';
installBtn.style.backgroundColor = 'red';
installBtn.style.color = 'white';
break;
case 'Update':
installBtn2 = document.createElement('button');
installBtn2.innerHTML = 'Update';
installBtn2.style.backgroundColor = 'blue';
installBtn2.style.color = 'white';
this.install_buttons.push(installBtn2);
installBtn3 = document.createElement('button');
installBtn3.innerHTML = 'Disable';
installBtn3.style.backgroundColor = 'MediumSlateBlue';
installBtn3.style.color = 'white';
this.install_buttons.push(installBtn3);
installBtn.innerHTML = 'Uninstall';
installBtn.style.backgroundColor = 'red';
installBtn.style.color = 'white';
break;
case 'True':
installBtn3 = document.createElement('button');
installBtn3.innerHTML = 'Disable';
installBtn3.style.backgroundColor = 'MediumSlateBlue';
installBtn3.style.color = 'white';
this.install_buttons.push(installBtn3);
installBtn.innerHTML = 'Uninstall';
installBtn.style.backgroundColor = 'red';
installBtn.style.color = 'white';
break;
case 'False':
installBtn.innerHTML = 'Install';
installBtn.style.backgroundColor = 'black';
installBtn.style.color = 'white';
break;
default:
installBtn.innerHTML = 'Try Install';
installBtn.style.backgroundColor = 'Gray';
installBtn.style.color = 'white';
}
let j = i;
if(installBtn2 != null) {
installBtn2.style.width = "120px";
installBtn2.addEventListener('click', function() {
install_checked_custom_node(self.grid_rows, j, AlternativesInstaller.instance, 'update');
});
data6.appendChild(installBtn2);
}
if(installBtn3 != null) {
installBtn3.style.width = "120px";
installBtn3.addEventListener('click', function() {
install_checked_custom_node(self.grid_rows, j, AlternativesInstaller.instance, 'toggle_active');
});
data6.appendChild(installBtn3);
}
installBtn.style.width = "120px";
installBtn.addEventListener('click', function() {
if(this.innerHTML == 'Uninstall') {
if (confirm(`Are you sure uninstall ${data.title}?`)) {
install_checked_custom_node(self.grid_rows, j, AlternativesInstaller.instance, 'uninstall');
}
}
else {
install_checked_custom_node(self.grid_rows, j, AlternativesInstaller.instance, 'install');
}
});
data6.appendChild(installBtn);
}
dataRow.style.backgroundColor = "var(--bg-color)";
dataRow.style.color = "var(--fg-color)";
dataRow.style.textAlign = "left";
dataRow.appendChild(data0);
dataRow.appendChild(data1);
dataRow.appendChild(data2);
dataRow.appendChild(data3);
dataRow.appendChild(data4);
dataRow.appendChild(data5);
dataRow.appendChild(data6);
tbody.appendChild(dataRow);
let buttons = [];
if(installBtn) {
buttons.push(installBtn);
}
if(installBtn2) {
buttons.push(installBtn2);
}
if(installBtn3) {
buttons.push(installBtn3);
}
this.grid_rows[i] = {data:data, buttons:buttons, checkbox:checkbox, control:dataRow};
}
const panel = document.createElement('div');
panel.style.width = "100%";
panel.appendChild(grid);
function handleResize() {
const parentHeight = self.element.clientHeight;
const gridHeight = parentHeight - 200;
grid.style.height = gridHeight + "px";
}
window.addEventListener("resize", handleResize);
grid.style.position = "relative";
grid.style.display = "inline-block";
grid.style.width = "100%";
grid.style.height = "100%";
grid.style.overflowY = "scroll";
this.element.style.height = "85%";
this.element.style.width = "80%";
this.element.appendChild(panel);
handleResize();
}
createFilterCombo() {
let combo = document.createElement("select");
combo.style.cssFloat = "left";
combo.style.fontSize = "14px";
combo.style.padding = "4px";
combo.style.background = "black";
combo.style.marginLeft = "2px";
combo.style.width = "199px";
combo.id = `combo-manger-filter`;
combo.style.borderRadius = "15px";
let items =
[
{ value:'*', text:'Filter: all' },
{ value:'Disabled', text:'Filter: disabled' },
{ value:'Update', text:'Filter: update' },
{ value:'True', text:'Filter: installed' },
{ value:'False', text:'Filter: not-installed' },
];
items.forEach(item => {
const option = document.createElement("option");
option.value = item.value;
option.text = item.text;
combo.appendChild(option);
});
let self = this;
combo.addEventListener('change', function(event) {
self.filter = event.target.value;
self.apply_searchbox();
});
if(self.filter) {
combo.value = self.filter;
}
return combo;
}
createHeaderControls() {
let self = this;
this.search_box = $el('input.cm-search-filter', {type:'text', id:'manager-alternode-search-box', placeholder:'input search keyword', value:this.search_keyword}, []);
this.search_box.style.height = "25px";
this.search_box.onkeydown = (event) => {
if (event.key === 'Enter') {
self.search_keyword = self.search_box.value;
self.apply_searchbox();
}
if (event.key === 'Escape') {
self.search_keyword = self.search_box.value;
self.apply_searchbox();
}
};
let search_button = document.createElement("button");
search_button.className = "cm-small-button";
search_button.innerHTML = "Search";
search_button.onclick = () => {
self.search_keyword = self.search_box.value;
self.apply_searchbox();
};
search_button.style.display = "inline-block";
let filter_control = this.createFilterCombo();
filter_control.style.display = "inline-block";
let cell = $el('td', {width:'100%'}, [filter_control, this.search_box, ' ', search_button]);
let search_control = $el('table', {width:'100%'},
[
$el('tr', {}, [cell])
]
);
cell.style.textAlign = "right";
this.element.appendChild(search_control);
}
async createBottomControls() {
var close_button = document.createElement("button");
close_button.className = "cm-small-button";
close_button.innerHTML = "Close";
close_button.onclick = () => { this.close(); }
close_button.style.display = "inline-block";
this.message_box = $el('div', {id:'alternatives-installer-message'}, [$el('br'), '']);
this.message_box.style.height = '60px';
this.message_box.style.verticalAlign = 'middle';
this.element.appendChild(this.message_box);
this.element.appendChild(close_button);
}
async show() {
try {
this.invalidateControl();
this.element.style.display = "block";
this.element.style.zIndex = 10001;
}
catch(exception) {
app.ui.dialog.show(`Failed to get alternatives list. / ${exception}`);
console.error(exception);
}
}
}

View File

@ -41,9 +41,18 @@ async function tryInstallCustomNode(event) {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event.detail.target)
});
if(response.status == 403) {
show_message('This action is not allowed with this security level configuration.');
return false;
}
}
api.fetchApi("/manager/reboot");
let response = await api.fetchApi("/manager/reboot");
if(response.status == 403) {
show_message('This action is not allowed with this security level configuration.');
return false;
}
await sleep(300);

View File

@ -11,11 +11,10 @@ import {
showYouMLShareDialog
} from "./comfyui-share-common.js";
import { OpenArtShareDialog } from "./comfyui-share-openart.js";
import { CustomNodesInstaller } from "./custom-nodes-downloader.js";
import { AlternativesInstaller } from "./a1111-alter-downloader.js";
import { CustomNodesManager } from "./custom-nodes-manager.js";
import { SnapshotManager } from "./snapshot.js";
import { ModelInstaller } from "./model-downloader.js";
import { manager_instance, setManagerInstance, install_via_git_url, install_pip, rebootAPI, free_models } from "./common.js";
import { manager_instance, setManagerInstance, install_via_git_url, install_pip, rebootAPI, free_models, show_message } from "./common.js";
import { ComponentBuilderDialog, load_components, set_component_policy, getPureName } from "./components-manager.js";
import { set_double_click_policy } from "./node_fixer.js";
@ -26,6 +25,7 @@ docStyle.innerHTML = `
height: 520px;
box-sizing: content-box;
z-index: 10000;
overflow-y: auto;
}
.cb-widget {
@ -520,25 +520,21 @@ async function updateComfyUI() {
const response = await api.fetchApi('/comfyui_manager/update_comfyui');
if (response.status == 400) {
app.ui.dialog.show('Failed to update ComfyUI.');
app.ui.dialog.element.style.zIndex = 10010;
show_message('Failed to update ComfyUI.');
return false;
}
if (response.status == 201) {
app.ui.dialog.show('ComfyUI has been successfully updated.');
app.ui.dialog.element.style.zIndex = 10010;
show_message('ComfyUI has been successfully updated.');
}
else {
app.ui.dialog.show('ComfyUI is already up to date with the latest version.');
app.ui.dialog.element.style.zIndex = 10010;
show_message('ComfyUI is already up to date with the latest version.');
}
return true;
}
catch (exception) {
app.ui.dialog.show(`Failed to update ComfyUI / ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Failed to update ComfyUI / ${exception}`);
return false;
}
finally {
@ -560,39 +556,35 @@ async function fetchUpdates(update_check_checkbox) {
const response = await api.fetchApi(`/customnode/fetch_updates?mode=${mode}`);
if (response.status != 200 && response.status != 201) {
app.ui.dialog.show('Failed to fetch updates.');
app.ui.dialog.element.style.zIndex = 10010;
show_message('Failed to fetch updates.');
return false;
}
if (response.status == 201) {
app.ui.dialog.show("There is an updated extension available.<BR><BR><P><B>NOTE:<BR>Fetch Updates is not an update.<BR>Please update from <button id='cm-install-customnodes-button'>Install Custom Nodes</button> </B></P>");
show_message("There is an updated extension available.<BR><BR><P><B>NOTE:<BR>Fetch Updates is not an update.<BR>Please update from <button id='cm-install-customnodes-button'>Install Custom Nodes</button> </B></P>");
const button = document.getElementById('cm-install-customnodes-button');
button.addEventListener("click",
async function() {
app.ui.dialog.close();
if(!CustomNodesInstaller.instance)
CustomNodesInstaller.instance = new CustomNodesInstaller(app, self);
await CustomNodesInstaller.instance.show(CustomNodesInstaller.ShowMode.UPDATE);
if(!CustomNodesManager.instance) {
CustomNodesManager.instance = new CustomNodesManager(app, self);
}
await CustomNodesManager.instance.show(CustomNodesManager.ShowMode.UPDATE);
}
);
app.ui.dialog.element.style.zIndex = 10010;
update_check_checkbox.checked = false;
}
else {
app.ui.dialog.show('All extensions are already up-to-date with the latest versions.');
app.ui.dialog.element.style.zIndex = 10010;
show_message('All extensions are already up-to-date with the latest versions.');
}
return true;
}
catch (exception) {
app.ui.dialog.show(`Failed to update custom nodes / ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Failed to update custom nodes / ${exception}`);
return false;
}
finally {
@ -615,11 +607,16 @@ async function updateAll(update_check_checkbox, manager_dialog) {
const response1 = await api.fetchApi('/comfyui_manager/update_comfyui');
const response2 = await api.fetchApi(`/customnode/update_all?mode=${mode}`);
if (response1.status == 400 || response2.status == 400) {
app.ui.dialog.show('Failed to update ComfyUI or several extensions.<BR><BR>See terminal log.<BR>');
app.ui.dialog.element.style.zIndex = 10010;
if (response2.status == 403) {
show_message('This action is not allowed with this security level configuration.');
return false;
}
if (response1.status == 400 || response2.status == 400) {
show_message('Failed to update ComfyUI or several extensions.<BR><BR>See terminal log.<BR>');
return false;
}
if(response1.status == 201 || response2.status == 201) {
const update_info = await response2.json();
@ -633,7 +630,7 @@ async function updateAll(update_check_checkbox, manager_dialog) {
updated_list = "<BR>UPDATED: "+update_info.updated.join(", ");
}
app.ui.dialog.show(
show_message(
"ComfyUI and all extensions have been updated to the latest version.<BR>To apply the updated custom node, please <button class='cm-small-button' id='cm-reboot-button5'>RESTART</button> ComfyUI. And refresh browser.<BR>"
+failed_list
+updated_list
@ -646,19 +643,15 @@ async function updateAll(update_check_checkbox, manager_dialog) {
manager_dialog.close();
}
});
app.ui.dialog.element.style.zIndex = 10010;
}
else {
app.ui.dialog.show('ComfyUI and all extensions are already up-to-date with the latest versions.');
app.ui.dialog.element.style.zIndex = 10010;
show_message('ComfyUI and all extensions are already up-to-date with the latest versions.');
}
return true;
}
catch (exception) {
app.ui.dialog.show(`Failed to update ComfyUI or several extensions / ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Failed to update ComfyUI or several extensions / ${exception}`);
return false;
}
finally {
@ -721,12 +714,13 @@ class ManagerMenuDialog extends ComfyDialog {
[
$el("button.cm-button", {
type: "button",
textContent: "Install Custom Nodes",
textContent: "Custom Nodes Manager",
onclick:
() => {
if(!CustomNodesInstaller.instance)
CustomNodesInstaller.instance = new CustomNodesInstaller(app, self);
CustomNodesInstaller.instance.show(CustomNodesInstaller.ShowMode.NORMAL);
if(!CustomNodesManager.instance) {
CustomNodesManager.instance = new CustomNodesManager(app, self);
}
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.NORMAL);
}
}),
@ -735,9 +729,10 @@ class ManagerMenuDialog extends ComfyDialog {
textContent: "Install Missing Custom Nodes",
onclick:
() => {
if(!CustomNodesInstaller.instance)
CustomNodesInstaller.instance = new CustomNodesInstaller(app, self);
CustomNodesInstaller.instance.show(CustomNodesInstaller.ShowMode.MISSING_NODES);
if(!CustomNodesManager.instance) {
CustomNodesManager.instance = new CustomNodesManager(app, self);
}
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.MISSING);
}
}),
@ -775,9 +770,10 @@ class ManagerMenuDialog extends ComfyDialog {
textContent: "Alternatives of A1111",
onclick:
() => {
if(!AlternativesInstaller.instance)
AlternativesInstaller.instance = new AlternativesInstaller(app, self);
AlternativesInstaller.instance.show();
if(!CustomNodesManager.instance) {
CustomNodesManager.instance = new CustomNodesManager(app, self);
}
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.ALTERNATIVES);
}
}),

View File

@ -1,6 +1,11 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
export function show_message(msg) {
app.ui.dialog.show(msg);
app.ui.dialog.element.style.zIndex = 10010;
}
export async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
@ -19,58 +24,6 @@ export function rebootAPI() {
return false;
}
export async function install_checked_custom_node(grid_rows, target_i, caller, mode) {
if(caller) {
let failed = '';
caller.disableButtons();
for(let i in grid_rows) {
if(!grid_rows[i].checkbox.checked && i != target_i)
continue;
var target;
if(grid_rows[i].data.custom_node) {
target = grid_rows[i].data.custom_node;
}
else {
target = grid_rows[i].data;
}
caller.startInstall(target);
try {
const response = await api.fetchApi(`/customnode/${mode}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(target)
});
if(response.status == 400) {
show_message(`${mode} failed: ${target.title}`);
continue;
}
const status = await response.json();
app.ui.dialog.close();
target.installed = 'True';
continue;
}
catch(exception) {
failed += `<BR> ${target.title}`;
}
}
if(failed != '') {
show_message(`${mode} failed: ${failed}`);
}
await caller.invalidateControl();
caller.updateMessage("<BR>To apply the installed/updated/disabled/enabled custom node, please <button id='cm-reboot-button1' class='cm-small-button'>RESTART</button> ComfyUI. And refresh browser.", 'cm-reboot-button1');
}
};
export var manager_instance = null;
export function setManagerInstance(obj) {
@ -94,19 +47,21 @@ export async function install_pip(packages) {
body: packages,
});
if(res.status == 403) {
show_message('This action is not allowed with this security level configuration.');
return;
}
if(res.status == 200) {
app.ui.dialog.show(`PIP package installation is processed.<br>To apply the pip packages, please click the <button id='cm-reboot-button3'><font size='3px'>RESTART</font></button> button in ComfyUI.`);
show_message(`PIP package installation is processed.<br>To apply the pip packages, please click the <button id='cm-reboot-button3'><font size='3px'>RESTART</font></button> button in ComfyUI.`);
const rebootButton = document.getElementById('cm-reboot-button3');
const self = this;
rebootButton.addEventListener("click", rebootAPI);
app.ui.dialog.element.style.zIndex = 10010;
}
else {
app.ui.dialog.show(`Failed to install '${packages}'<BR>See terminal log.`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Failed to install '${packages}'<BR>See terminal log.`);
}
}
@ -116,21 +71,24 @@ export async function install_via_git_url(url, manager_dialog) {
}
if(!isValidURL(url)) {
app.ui.dialog.show(`Invalid Git url '${url}'`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Invalid Git url '${url}'`);
return;
}
app.ui.dialog.show(`Wait...<BR><BR>Installing '${url}'`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Wait...<BR><BR>Installing '${url}'`);
const res = await api.fetchApi("/customnode/install/git_url", {
method: "POST",
body: url,
});
if(res.status == 403) {
show_message('This action is not allowed with this security level configuration.');
return;
}
if(res.status == 200) {
app.ui.dialog.show(`'${url}' is installed<BR>To apply the installed custom node, please <button id='cm-reboot-button4'><font size='3px'>RESTART</font></button> ComfyUI.`);
show_message(`'${url}' is installed<BR>To apply the installed custom node, please <button id='cm-reboot-button4'><font size='3px'>RESTART</font></button> ComfyUI.`);
const rebootButton = document.getElementById('cm-reboot-button4');
const self = this;
@ -141,12 +99,9 @@ export async function install_via_git_url(url, manager_dialog) {
manager_dialog.close();
}
});
app.ui.dialog.element.style.zIndex = 10010;
}
else {
app.ui.dialog.show(`Failed to install '${url}'<BR>See terminal log.`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Failed to install '${url}'<BR>See terminal log.`);
}
}
@ -158,15 +113,9 @@ export async function free_models() {
});
if(res.status == 200) {
app.ui.dialog.show('Models have been unloaded.')
show_message('Models have been unloaded.')
}
else {
app.ui.dialog.show('Unloading of models failed.<BR><BR>Installed ComfyUI may be an outdated version.')
show_message('Unloading of models failed.<BR><BR>Installed ComfyUI may be an outdated version.')
}
app.ui.dialog.element.style.zIndex = 10010;
}
export function show_message(msg) {
app.ui.dialog.show(msg);
app.ui.dialog.element.style.zIndex = 10010;
}

View File

@ -1,943 +0,0 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
import { install_checked_custom_node, manager_instance, rebootAPI } from "./common.js";
async function getCustomNodes() {
var mode = manager_instance.datasrc_combo.value;
var skip_update = "";
if(manager_instance.update_check_checkbox.checked)
skip_update = "&skip_update=true";
const response = await api.fetchApi(`/customnode/getlist?mode=${mode}${skip_update}`);
const data = await response.json();
return data;
}
async function getCustomnodeMappings() {
var mode = manager_instance.datasrc_combo.value;
const response = await api.fetchApi(`/customnode/getmappings?mode=${mode}`);
const data = await response.json();
return data;
}
async function getConflictMappings() {
var mode = manager_instance.datasrc_combo.value;
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 = manager_instance.datasrc_combo.value;
const response = await api.fetchApi(`/component/get_unresolved`);
const data = await response.json();
return data.nodes;
}
catch {
return [];
}
}
export class CustomNodesInstaller extends ComfyDialog {
static instance = null;
install_buttons = [];
message_box = null;
data = null;
static ShowMode = {
NORMAL: 0,
MISSING_NODES: 1,
UPDATE: 2,
};
clear() {
this.install_buttons = [];
this.message_box = null;
this.data = null;
}
constructor(app, manager_dialog) {
super();
this.manager_dialog = manager_dialog;
this.search_keyword = '';
this.element = $el("div.comfy-modal", { parent: document.body }, []);
this.currentSortProperty = ''; // The property currently being sorted
this.currentSortAscending = true; // The direction of the current sort
}
startInstall(target) {
const self = CustomNodesInstaller.instance;
self.updateMessage(`<BR><font color="green">Installing '${target.title}'</font>`);
}
disableButtons() {
for(let i in this.install_buttons) {
this.install_buttons[i].disabled = true;
this.install_buttons[i].style.backgroundColor = 'gray';
}
}
apply_searchbox(data) {
let keyword = this.search_box.value.toLowerCase();
for(let i in this.grid_rows) {
let data = this.grid_rows[i].data;
let content = data.author.toLowerCase() + data.description.toLowerCase() + data.title.toLowerCase() + data.reference.toLowerCase();
if(this.filter && this.filter != '*') {
if(this.filter == 'True' && (data.installed == 'Update' || data.installed == 'Fail')) {
this.grid_rows[i].control.style.display = null;
}
else if(this.filter != data.installed) {
this.grid_rows[i].control.style.display = 'none';
continue;
}
}
if(keyword == "")
this.grid_rows[i].control.style.display = null;
else if(content.includes(keyword)) {
this.grid_rows[i].control.style.display = null;
}
else {
this.grid_rows[i].control.style.display = 'none';
}
}
}
async filter_missing_node(data) {
const mappings = await getCustomnodeMappings();
// build regex->url map
const regex_to_url = [];
for (let i in data) {
if(data[i]['nodename_pattern']) {
let item = {regex: new RegExp(data[i].nodename_pattern), url: data[i].files[0]};
regex_to_url.push(item);
}
}
// build name->url map
const name_to_urls = {};
for (const url in mappings) {
const names = mappings[url];
for(const name in names[0]) {
let v = name_to_urls[names[0][name]];
if(v == undefined) {
v = [];
name_to_urls[names[0][name]] = v;
}
v.push(url);
}
}
const registered_nodes = new Set();
for (let i in LiteGraph.registered_node_types) {
registered_nodes.add(LiteGraph.registered_node_types[i].type);
}
const missing_nodes = new Set();
const workflow = app.graph.serialize();
const group_nodes = workflow.extra && workflow.extra.groupNodes ? workflow.extra.groupNodes : [];
let nodes = workflow.nodes;
for (let i in group_nodes) {
let group_node = group_nodes[i];
nodes = nodes.concat(group_node.nodes);
}
for (let i in nodes) {
const node_type = nodes[i].type;
if(node_type.startsWith('workflow/'))
continue;
if (!registered_nodes.has(node_type)) {
const urls = name_to_urls[node_type.trim()];
if(urls)
urls.forEach(url => {
missing_nodes.add(url);
});
else {
for(let j in regex_to_url) {
if(regex_to_url[j].regex.test(node_type)) {
missing_nodes.add(regex_to_url[j].url);
}
}
}
}
}
let unresolved_nodes = await getUnresolvedNodesInComponent();
for (let i in unresolved_nodes) {
let node_type = unresolved_nodes[i];
const url = name_to_urls[node_type];
if(url)
missing_nodes.add(url);
}
return data.filter(node => node.files.some(file => missing_nodes.has(file)));
}
async invalidateControl() {
this.clear();
// splash
while (this.element.children.length) {
this.element.removeChild(this.element.children[0]);
}
const msg = $el('div', {id:'custom-message'},
[$el('br'),
'The custom node DB is currently being updated, and updates to custom nodes are being checked for.',
$el('br'),
'NOTE: Update only checks for extensions that have been fetched.',
$el('br')]);
msg.style.height = '100px';
msg.style.verticalAlign = 'middle';
msg.style.color = "var(--fg-color)";
this.element.appendChild(msg);
// invalidate
let data = await getCustomNodes();
this.data = data.custom_nodes;
this.channel = data.channel;
this.conflict_mappings = await getConflictMappings();
if(this.show_mode == CustomNodesInstaller.ShowMode.MISSING_NODES)
this.data = await this.filter_missing_node(this.data);
this.element.removeChild(msg);
while (this.element.children.length) {
this.element.removeChild(this.element.children[0]);
}
this.createHeaderControls();
await this.createGrid();
this.apply_searchbox(this.data);
this.createBottomControls();
}
updateMessage(msg, btn_id) {
this.message_box.innerHTML = msg;
if(btn_id) {
const rebootButton = document.getElementById(btn_id);
const self = this;
rebootButton.addEventListener("click",
function() {
if(rebootAPI()) {
self.close();
self.manager_dialog.close();
}
});
console.log(rebootButton);
}
}
invalidate_checks(is_checked, install_state) {
if(is_checked) {
for(let i in this.grid_rows) {
let data = this.grid_rows[i].data;
let checkbox = this.grid_rows[i].checkbox;
let buttons = this.grid_rows[i].buttons;
checkbox.disabled = data.installed != install_state;
if(checkbox.disabled) {
for(let j in buttons) {
buttons[j].style.display = 'none';
}
}
else {
for(let j in buttons) {
buttons[j].style.display = null;
}
}
}
this.checkbox_all.disabled = false;
}
else {
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
if(checkbox.check)
return; // do nothing
}
// every checkbox is unchecked -> enable all checkbox
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
let buttons = this.grid_rows[i].buttons;
checkbox.disabled = false;
for(let j in buttons) {
buttons[j].style.display = null;
}
}
this.checkbox_all.checked = false;
this.checkbox_all.disabled = true;
}
}
check_all(is_checked) {
if(is_checked) {
// lookup first checked item's state
let check_state = null;
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
if(checkbox.checked) {
check_state = this.grid_rows[i].data.installed;
}
}
if(check_state == null)
return;
// check only same state items
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
if(this.grid_rows[i].data.installed == check_state)
checkbox.checked = true;
}
}
else {
// uncheck all
for(let i in this.grid_rows) {
let checkbox = this.grid_rows[i].checkbox;
let buttons = this.grid_rows[i].buttons;
checkbox.checked = false;
checkbox.disabled = false;
for(let j in buttons) {
buttons[j].style.display = null;
}
}
this.checkbox_all.disabled = true;
}
}
sortData(property, ascending = true) {
this.data.sort((a, b) => {
// Check if either value is -1 and handle accordingly
if (a[property] === -1) return 1; // Always put a at the end if its value is -1
if (b[property] === -1) return -1; // Always put b at the end if its value is -1
// And be careful here, (-1<'2024-01-01') and (-1>'2024-01-01') are both false! So I handle -1 seperately.
if (a[property] < b[property]) return ascending ? -1 : 1;
if (a[property] > b[property]) return ascending ? 1 : -1;
return 0;
});
}
resetHeaderStyles() {
const headers = ['th_author', 'th_title', 'th_stars', 'th_last_update']; // Add the IDs of all your sortable headers here
headers.forEach(headerId => {
const header = this.element.querySelector(`#${headerId}`);
if (header) {
header.style.backgroundColor = ''; // Reset to default background color
// Add other style resets if necessary
}
});
}
toggleSort(property) {
// If currently sorted by this property, toggle the direction; else, sort ascending
if (this.currentSortProperty === property) {
this.currentSortAscending = !this.currentSortAscending;
} else {
this.currentSortAscending = false;
}
this.currentSortProperty = property;
this.resetHeaderStyles(); // Reset styles of all sortable headers
// Determine the ID of the header based on the property
let headerId = '';
if (property === 'stars') {
headerId = 'th_stars';
} else if (property === 'last_update') {
headerId = 'th_last_update';
} else if (property === 'author') {
headerId = 'th_author';
} else if (property === 'title') {
headerId = 'th_title';
}
// If we have a valid headerId, change its style to indicate it's the active sort column
if (headerId) {
const activeHeader = this.element.querySelector(`#${headerId}`);
if (activeHeader) {
activeHeader.style.backgroundColor = '#222';
// Slightly brighter. Add other style changes if necessary.
}
}
// Call sortData with the current property and direction
this.sortData(property, this.currentSortAscending);
// Refresh the grid to display sorted data
this.createGrid();
this.apply_searchbox(this.data);
}
async createGrid() {
// Remove existing table if present
var grid = this.element.querySelector('#custom-nodes-grid');
var panel;
let self = this;
if (grid) {
grid.querySelector('tbody').remove();
panel = grid.parentNode;
} else {
grid = document.createElement('table');
grid.setAttribute('id', 'custom-nodes-grid');
this.grid_rows = {};
var thead = document.createElement('thead');
var headerRow = document.createElement('tr');
thead.style.position = "sticky";
thead.style.top = "0px";
thead.style.borderCollapse = "collapse";
thead.style.tableLayout = "fixed";
var header0 = document.createElement('th');
header0.style.width = "20px";
this.checkbox_all = $el("input",{type:'checkbox', id:'check_all'},[]);
header0.appendChild(this.checkbox_all);
this.checkbox_all.checked = false;
this.checkbox_all.disabled = true;
this.checkbox_all.addEventListener('change', function() { self.check_all.call(self, self.checkbox_all.checked); });
var header1 = document.createElement('th');
header1.innerHTML = '&nbsp;&nbsp;ID&nbsp;&nbsp;';
header1.style.width = "20px";
var header2 = document.createElement('th');
header2.innerHTML = 'Author';
header2.style.width = "150px";
header2.style.cursor = 'pointer';
header2.setAttribute('id', 'th_author');
header2.onclick = () => this.toggleSort('author');
var header3 = document.createElement('th');
header3.innerHTML = 'Name';
header3.style.width = "20%";
header3.style.cursor = 'pointer';
header3.setAttribute('id', 'th_title');
header3.onclick = () => this.toggleSort('title');
var header4 = document.createElement('th');
header4.innerHTML = 'Description';
header4.style.width = "60%";
// header4.classList.add('expandable-column');
var header5 = document.createElement('th');
header5.innerHTML = '&nbsp;&nbsp;&nbsp;★&nbsp;&nbsp;&nbsp;';
header5.style.width = "130px";
header5.setAttribute('id', 'th_stars');
header5.style.cursor = 'pointer';
header5.onclick = () => this.toggleSort('stars');
var header6 = document.createElement('th');
header6.innerHTML = 'Last Update';
header6.style.width = "130px";
header6.setAttribute('id', 'th_last_update');
header6.style.cursor = 'pointer';
header6.onclick = () => this.toggleSort('last_update');
var header7 = document.createElement('th');
header7.innerHTML = 'Install';
header7.style.width = "130px";
header0.style.position = "sticky";
header0.style.top = "0px";
header1.style.position = "sticky";
header1.style.top = "0px";
header2.style.position = "sticky";
header2.style.top = "0px";
header3.style.position = "sticky";
header3.style.top = "0px";
header4.style.position = "sticky";
header4.style.top = "0px";
header5.style.position = "sticky";
header5.style.top = "0px";
header6.style.position = "sticky";
header6.style.top = "0px";
header7.style.position = "sticky";
header7.style.top = "0px";
thead.appendChild(headerRow);
headerRow.appendChild(header0);
headerRow.appendChild(header1);
headerRow.appendChild(header2);
headerRow.appendChild(header3);
headerRow.appendChild(header4);
headerRow.appendChild(header5);
headerRow.appendChild(header6);
headerRow.appendChild(header7);
headerRow.style.backgroundColor = "Black";
headerRow.style.color = "White";
headerRow.style.textAlign = "center";
headerRow.style.width = "100%";
headerRow.style.padding = "0";
grid.appendChild(thead);
panel = document.createElement('div');
panel.style.width = "100%";
panel.appendChild(grid);
this.element.appendChild(panel);
}
var tbody = document.createElement('tbody');
grid.appendChild(tbody);
if(this.data)
for (var i = 0; i < this.data.length; i++) {
const data = this.data[i];
let dataRow = document.createElement('tr');
let data0 = document.createElement('td');
let checkbox = $el("input",{type:'checkbox', id:`check_${i}`},[]);
data0.appendChild(checkbox);
checkbox.checked = false;
checkbox.addEventListener('change', function() { self.invalidate_checks.call(self, checkbox.checked, data.installed); });
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"
data2.textContent = ` ${data.author}`;
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 = `&nbsp;<a href=${data.reference} target="_blank"><font color="skyblue"><b>${data.title}</b></font></a>`;
if(data.installed == 'Fail')
data3.innerHTML = '&nbsp;<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 = '<p class="cm-conflicted-nodes-text"><B><font color="BLACK">Conflicted Nodes:</FONT></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 += `<B>${node_name}</B> [${extension_name}], `;
}
if(buf.endsWith(', ')) {
buf = buf.slice(0, -2);
}
buf += "</p>";
data4.innerHTML += buf;
}
var data5 = document.createElement('td');
data5.style.maxWidth = "100px";
data5.className = "cm-node-stars"
if(data.stars < 0) {
data5.textContent = 'N/A';
}
else {
data5.textContent = `${data.stars}`;
}
data5.style.whiteSpace = "nowrap";
data5.style.overflow = "hidden";
data5.style.textOverflow = "ellipsis";
data5.style.textAlign = "center";
var lastUpdateDate = new Date();
var data6 = document.createElement('td');
data6.style.maxWidth = "100px";
data6.className = "cm-node-last-update";
if(data.last_update < 0) {
data6.textContent = 'N/A';
}
else {
data6.textContent = `${data.last_update}`.split(' ')[0];
}
data6.style.whiteSpace = "nowrap";
data6.style.overflow = "hidden";
data6.style.textOverflow = "ellipsis";
data6.style.textAlign = "center";
var data7 = document.createElement('td');
data7.style.textAlign = "center";
var installBtn = document.createElement('button');
installBtn.className = "cm-btn-install";
var installBtn2 = null;
var installBtn3 = null;
var installBtn4 = null;
this.install_buttons.push(installBtn);
switch(data.installed) {
case 'Disabled':
installBtn3 = document.createElement('button');
installBtn3.innerHTML = 'Enable';
installBtn3.className = "cm-btn-enable";
installBtn3.style.backgroundColor = 'blue';
installBtn3.style.color = 'white';
this.install_buttons.push(installBtn3);
installBtn.innerHTML = 'Uninstall';
installBtn.style.backgroundColor = 'red';
break;
case 'Update':
installBtn2 = document.createElement('button');
installBtn2.innerHTML = 'Update';
installBtn2.className = "cm-btn-update";
installBtn2.style.backgroundColor = 'blue';
installBtn2.style.color = 'white';
this.install_buttons.push(installBtn2);
installBtn3 = document.createElement('button');
installBtn3.innerHTML = 'Disable';
installBtn3.className = "cm-btn-disable";
installBtn3.style.backgroundColor = 'MediumSlateBlue';
installBtn3.style.color = 'white';
this.install_buttons.push(installBtn3);
installBtn.innerHTML = 'Uninstall';
installBtn.style.backgroundColor = 'red';
break;
case 'Fail':
installBtn4 = document.createElement('button');
installBtn4.innerHTML = 'Try fix';
installBtn4.className = "cm-btn-disable";
installBtn4.style.backgroundColor = '#6495ED';
installBtn4.style.color = 'white';
this.install_buttons.push(installBtn4);
case 'True':
if(manager_instance.update_check_checkbox.checked) {
installBtn2 = document.createElement('button');
installBtn2.innerHTML = 'Try update';
installBtn2.className = "cm-btn-update";
installBtn2.style.backgroundColor = 'Gray';
installBtn2.style.color = 'white';
this.install_buttons.push(installBtn2);
}
installBtn3 = document.createElement('button');
installBtn3.innerHTML = 'Disable';
installBtn3.className = "cm-btn-disable";
installBtn3.style.backgroundColor = 'MediumSlateBlue';
installBtn3.style.color = 'white';
this.install_buttons.push(installBtn3);
installBtn.innerHTML = 'Uninstall';
installBtn.style.backgroundColor = 'red';
break;
case 'False':
installBtn.innerHTML = 'Install';
installBtn.style.backgroundColor = 'black';
installBtn.style.color = 'white';
break;
default:
installBtn.innerHTML = `Try Install`;
installBtn.style.backgroundColor = 'Gray';
installBtn.style.color = 'white';
}
let j = i;
if(installBtn2 != null) {
installBtn2.style.width = "120px";
installBtn2.addEventListener('click', function() {
install_checked_custom_node(self.grid_rows, j, CustomNodesInstaller.instance, 'update');
});
data7.appendChild(installBtn2);
}
if(installBtn3 != null) {
installBtn3.style.width = "120px";
installBtn3.addEventListener('click', function() {
install_checked_custom_node(self.grid_rows, j, CustomNodesInstaller.instance, 'toggle_active');
});
data7.appendChild(installBtn3);
}
if(installBtn4 != null) {
installBtn4.style.width = "120px";
installBtn4.addEventListener('click', function() {
install_checked_custom_node(self.grid_rows, j, CustomNodesInstaller.instance, 'fix');
});
data7.appendChild(installBtn4);
}
installBtn.style.width = "120px";
installBtn.addEventListener('click', function() {
if(this.innerHTML == 'Uninstall') {
if (confirm(`Are you sure uninstall ${data.title}?`)) {
install_checked_custom_node(self.grid_rows, j, CustomNodesInstaller.instance, 'uninstall');
}
}
else {
install_checked_custom_node(self.grid_rows, j, CustomNodesInstaller.instance, 'install');
}
});
if(!data.author.startsWith('#NOTICE')){
data7.appendChild(installBtn);
}
if(data.installed == 'Fail' || data.author.startsWith('#NOTICE'))
dataRow.style.backgroundColor = "#880000";
else
dataRow.style.backgroundColor = "var(--bg-color)";
dataRow.style.color = "var(--fg-color)";
dataRow.style.textAlign = "left";
dataRow.appendChild(data0);
dataRow.appendChild(data1);
dataRow.appendChild(data2);
dataRow.appendChild(data3);
dataRow.appendChild(data4);
dataRow.appendChild(data5);
dataRow.appendChild(data6);
dataRow.appendChild(data7);
tbody.appendChild(dataRow);
let buttons = [];
if(installBtn) {
buttons.push(installBtn);
}
if(installBtn2) {
buttons.push(installBtn2);
}
if(installBtn3) {
buttons.push(installBtn3);
}
this.grid_rows[i] = {data:data, buttons:buttons, checkbox:checkbox, control:dataRow};
}
function handleResize() {
const parentHeight = self.element.clientHeight;
const gridHeight = parentHeight - 200;
grid.style.height = gridHeight + "px";
}
window.addEventListener("resize", handleResize);
grid.style.position = "relative";
grid.style.display = "inline-block";
grid.style.width = "100%";
grid.style.height = "100%";
grid.style.overflowY = "scroll";
this.element.style.height = "85%";
this.element.style.width = "80%";
handleResize();
}
createFilterCombo() {
let combo = document.createElement("select");
combo.style.cssFloat = "left";
combo.style.fontSize = "14px";
combo.style.padding = "4px";
combo.style.background = "black";
combo.style.marginLeft = "2px";
combo.style.width = "199px";
combo.id = `combo-manger-filter`;
combo.style.borderRadius = "15px";
let items =
[
{ value:'*', text:'Filter: all' },
{ value:'Disabled', text:'Filter: disabled' },
{ 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 => {
const option = document.createElement("option");
option.value = item.value;
option.text = item.text;
combo.appendChild(option);
});
if(this.show_mode == CustomNodesInstaller.ShowMode.UPDATE) {
this.filter = 'Update';
}
else if(this.show_mode == CustomNodesInstaller.ShowMode.MISSING_NODES) {
this.filter = '*';
}
let self = this;
combo.addEventListener('change', function(event) {
self.filter = event.target.value;
self.apply_searchbox();
});
if(self.filter) {
combo.value = self.filter;
}
return combo;
}
createHeaderControls() {
let self = this;
this.search_box = $el('input.cm-search-filter', {type:'text', id:'manager-customnode-search-box', placeholder:'input search keyword', value:this.search_keyword}, []);
this.search_box.style.height = "25px";
this.search_box.onkeydown = (event) => {
if (event.key === 'Enter') {
self.search_keyword = self.search_box.value;
self.apply_searchbox();
}
if (event.key === 'Escape') {
self.search_keyword = self.search_box.value;
self.apply_searchbox();
}
};
let search_button = document.createElement("button");
search_button.className = "cm-small-button";
search_button.innerHTML = "Search";
search_button.onclick = () => {
self.search_keyword = self.search_box.value;
self.apply_searchbox();
};
search_button.style.display = "inline-block";
let filter_control = this.createFilterCombo();
filter_control.style.display = "inline-block";
let channel_badge = '';
if(this.channel != 'default') {
channel_badge = $el('span', {id:'cm-channel-badge'}, [`Channel: ${this.channel} (Incomplete list)`]);
}
else {
}
let cell = $el('td', {width:'100%'}, [filter_control, channel_badge, this.search_box, ' ', search_button]);
let search_control = $el('table', {width:'100%'},
[
$el('tr', {}, [cell])
]
);
cell.style.textAlign = "right";
this.element.appendChild(search_control);
}
async createBottomControls() {
var close_button = document.createElement("button");
close_button.className = "cm-small-button";
close_button.innerHTML = "Close";
close_button.onclick = () => { this.close(); }
close_button.style.display = "inline-block";
this.message_box = $el('div', {id:'custom-installer-message'}, [$el('br'), '']);
this.message_box.style.height = '60px';
this.message_box.style.verticalAlign = 'middle';
this.element.appendChild(this.message_box);
this.element.appendChild(close_button);
}
async show(show_mode) {
this.show_mode = show_mode;
if(this.show_mode != CustomNodesInstaller.ShowMode.NORMAL) {
this.search_keyword = '';
}
try {
this.invalidateControl();
this.element.style.display = "block";
this.element.style.zIndex = 10001;
}
catch(exception) {
app.ui.dialog.show(`Failed to get custom node list. / ${exception}`);
}
}
}

1524
js/custom-nodes-manager.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
import { install_checked_custom_node, manager_instance, rebootAPI } from "./common.js";
import { manager_instance, rebootAPI, show_message } from "./common.js";
async function install_model(target) {
if(ModelInstaller.instance) {
@ -20,8 +20,7 @@ async function install_model(target) {
return true;
}
catch(exception) {
app.ui.dialog.show(`Install failed: ${target.title} / ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Install failed: ${target.title} / ${exception}`);
return false;
}
finally {
@ -85,7 +84,7 @@ export class ModelInstaller extends ComfyDialog {
let keyword = this.search_box.value.toLowerCase();
for(let i in this.grid_rows) {
let data = this.grid_rows[i].data;
let content = data.name.toLowerCase() + data.type.toLowerCase() + data.base.toLowerCase() + data.description.toLowerCase();
let content = data.name.toLowerCase() + data.type.toLowerCase() + data.base.toLowerCase() + data.filename.toLowerCase() + data.description.toLowerCase();
if(this.filter && this.filter != '*') {
if(this.filter != data.installed) {

View File

@ -1,24 +1,28 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js"
import { ComfyDialog, $el } from "../../scripts/ui.js";
import { manager_instance, rebootAPI } from "./common.js";
import { manager_instance, rebootAPI, show_message } from "./common.js";
async function restore_snapshot(target) {
if(SnapshotManager.instance) {
try {
const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" });
if(response.status == 403) {
show_message('This action is not allowed with this security level configuration.');
return false;
}
if(response.status == 400) {
app.ui.dialog.show(`Restore snapshot failed: ${target.title} / ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Restore snapshot failed: ${target.title} / ${exception}`);
}
app.ui.dialog.close();
return true;
}
catch(exception) {
app.ui.dialog.show(`Restore snapshot failed: ${target.title} / ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Restore snapshot failed: ${target.title} / ${exception}`);
return false;
}
finally {
@ -32,17 +36,21 @@ async function remove_snapshot(target) {
if(SnapshotManager.instance) {
try {
const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" });
if(response.status == 403) {
show_message('This action is not allowed with this security level configuration.');
return false;
}
if(response.status == 400) {
app.ui.dialog.show(`Remove snapshot failed: ${target.title} / ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Remove snapshot failed: ${target.title} / ${exception}`);
}
app.ui.dialog.close();
return true;
}
catch(exception) {
app.ui.dialog.show(`Restore snapshot failed: ${target.title} / ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Restore snapshot failed: ${target.title} / ${exception}`);
return false;
}
finally {
@ -58,8 +66,7 @@ async function save_current_snapshot() {
return true;
}
catch(exception) {
app.ui.dialog.show(`Backup snapshot failed: ${exception}`);
app.ui.dialog.element.style.zIndex = 10010;
show_message(`Backup snapshot failed: ${exception}`);
return false;
}
finally {

1
js/turbogrid.esm.js Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 91 KiB

BIN
misc/missing-list.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 198 KiB

BIN
misc/missing-menu.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -1,5 +1,25 @@
{
"models": [
{
"name": "TAESD3 Decoder",
"type": "TAESD",
"base": "SDXL",
"save_path": "vae_approx",
"description": "(SD3 Verison) To view the preview in high quality while running samples in ComfyUI, you will need this model.",
"reference": "https://github.com/madebyollin/taesd",
"filename": "taesd3_decoder.pth",
"url": "https://github.com/madebyollin/taesd/raw/main/taesd3_decoder.pth"
},
{
"name": "TAESD3 Encoder",
"type": "TAESD",
"base": "SDXL",
"save_path": "vae_approx",
"description": "(SD3 Verison) To view the preview in high quality while running samples in ComfyUI, you will need this model.",
"reference": "https://github.com/madebyollin/taesd",
"filename": "taesd3_encoder.pth",
"url": "https://github.com/madebyollin/taesd/raw/main/taesd3_encoder.pth"
},
{
"name": "TAESDXL Decoder",
"type": "TAESD",
@ -120,6 +140,16 @@
"filename": "8x_NMKD-Superscale_150000_G.pth",
"url": "https://huggingface.co/uwg/upscaler/resolve/main/ESRGAN/8x_NMKD-Superscale_150000_G.pth"
},
{
"name": "8x_NMKD-Faces_160000_G",
"type": "upscale",
"base": "upscale",
"save_path": "default",
"description": "8x_NMKD-Faces_160000_G upscaler model",
"reference": "https://huggingface.co/gemasai/8x_NMKD-Faces_160000_G/tree/main",
"filename": "8x_NMKD-Faces_160000_G.pth",
"url": "https://huggingface.co/gemasai/8x_NMKD-Faces_160000_G/resolve/main/8x_NMKD-Faces_160000_G.pth"
},
{
"name": "LDSR(Latent Diffusion Super Resolution)",
"type": "upscale",
@ -2611,6 +2641,368 @@
"reference": "https://huggingface.co/lllyasviel/ic-light",
"filename": "iclight_sd15_fcon.safetensors",
"url": "https://huggingface.co/lllyasviel/ic-light/resolve/main/iclight_sd15_fcon.safetensors"
},
{
"name": "ID-Animator/animator.ckpt",
"type": "ID-Animator",
"base": "SD1.5",
"save_path": "custom_nodes/ComfyUI_ID_Animator/models",
"description": "ID-Animator checkpoint",
"reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator",
"filename": "animator.ckpt",
"url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/animator.ckpt"
},
{
"name": "ID-Animator/mm_sd_v15_v2.ckpt",
"type": "ID-Animator",
"base": "SD1.5",
"save_path": "custom_nodes/ComfyUI_ID_Animator/models/animatediff_models",
"description": "AnimateDiff checkpoint for ID-Animator",
"reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator",
"filename": "mm_sd_v15_v2.ckpt",
"url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/mm_sd_v15_v2.ckpt"
},
{
"name": "ID-Animator/image_encoder",
"type": "ID-Animator",
"base": "SD1.5",
"save_path": "custom_nodes/ComfyUI_ID_Animator/models/image_encoder",
"description": "CLIP Image encoder for ID-Animator",
"reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator",
"filename": "model.safetensors",
"url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/image_encoder/model.safetensors"
},
{
"name": "TencentARC/CustomNet",
"type": "CustomNet",
"base": "CustomNet",
"save_path": "custom_nodes/ComfyUI_CustomNet/pretrain",
"description": "CustomNet pretrained model for ComfyUI_CustomNet",
"reference": "https://huggingface.co/TencentARC/CustomNet/tree/main",
"filename": "customnet_v1.pt",
"url": "https://huggingface.co/TencentARC/CustomNet/resolve/main/customnet_v1.pt"
},
{
"name": "TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic v2 (fp16)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[2.5GB] Controlnet SDXL Tile model realistic version.",
"reference": "https://huggingface.co/TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic",
"filename": "TTPLANET_Controlnet_Tile_realistic_v2_fp16.safetensors",
"url": "https://huggingface.co/TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic/resolve/main/TTPLANET_Controlnet_Tile_realistic_v2_fp16.safetensors"
},
{
"name": "TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic v2 (rank256)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] Controlnet SDXL Tile model realistic version.",
"reference": "https://huggingface.co/TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic",
"filename": "TTPLANET_Controlnet_Tile_realistic_v2_rank256.safetensors",
"url": "https://huggingface.co/TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic/resolve/main/TTPLANET_Controlnet_Tile_realistic_v2_rank256.safetensors"
},
{
"name": "ViperYX/RGT_x2.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT",
"description": "[180MB] RGT x2 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_x2.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT/RGT_x2.pth"
},
{
"name": "ViperYX/RGT_x3.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT",
"description": "[180MB] RGT x3 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_x3.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT/RGT_x3.pth"
},
{
"name": "ViperYX/RGT_x4.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT",
"description": "[180MB] RGT_S x4 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_x4.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT/RGT_x4.pth"
},
{
"name": "ViperYX/RGT_S_x2.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT_S",
"description": "[135MB] RGT_S x2 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_S_x2.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT_S/RGT_S_x2.pth"
},
{
"name": "ViperYX/RGT_S_x3.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT_S",
"description": "[136MB] RGT_S x3 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_S_x3.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT_S/RGT_S_x3.pth"
},
{
"name": "ViperYX/RGT_S_x4.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT_S",
"description": "[136MB] RGT_S x4 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_S_x4.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT_S/RGT_S_x4.pth"
},
{
"name": "Doubiiu/ToonCrafter model checkpoint",
"type": "checkpoint",
"base": "ToonCrafter",
"save_path": "custom_nodes/ComfyUI-ToonCrafter/ToonCrafter/checkpoints/tooncrafter_512_interp_v1",
"description": "[10.5GB] ToonCrafter checkpoint model for ComfyUI-ToonCrafter",
"reference": "https://huggingface.co/Doubiiu/ToonCrafter/tree/main",
"filename": "model.ckpt",
"url": "https://huggingface.co/Doubiiu/ToonCrafter/resolve/main/model.ckpt"
},
{
"name": "xinsir/Controlnet-Scribble-Sdxl-1.0",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-scribble-sdxl-1.0",
"description": "[2.5GB] Controlnet SDXL Scribble model.",
"reference": "https://huggingface.co/xinsir/controlnet-scribble-sdxl-1.0",
"filename": "diffusion_pytorch_model.safetensors",
"url": "https://huggingface.co/xinsir/controlnet-scribble-sdxl-1.0/resolve/main/diffusion_pytorch_model.safetensors"
},
{
"name": "xinsir/Controlnet-Canny-Sdxl-1.0 (V2)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-canny-sdxl-1.0",
"description": "[2.5GB] Controlnet SDXL Canny model.",
"reference": "https://huggingface.co/xinsir/controlnet-canny-sdxl-1.0",
"filename": "diffusion_pytorch_model_V2.safetensors",
"url": "https://huggingface.co/xinsir/controlnet-canny-sdxl-1.0/resolve/main/diffusion_pytorch_model_V2.safetensors"
},
{
"name": "xinsir/Controlnet-Openpose-Sdxl-1.0",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-openpose-sdxl-1.0",
"description": "[2.5GB] Controlnet SDXL Openpose model.",
"reference": "https://huggingface.co/xinsir/controlnet-openpose-sdxl-1.0",
"filename": "diffusion_pytorch_model.safetensors",
"url": "https://huggingface.co/xinsir/controlnet-openpose-sdxl-1.0/resolve/main/diffusion_pytorch_model.safetensors"
},
{
"name": "xinsir/Controlnet-Openpose-Sdxl-1.0 (Ver. twins)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-openpose-sdxl-1.0",
"description": "[2.5GB] Controlnet SDXL Openpose model. (Ver. twins)",
"reference": "https://huggingface.co/xinsir/controlnet-openpose-sdxl-1.0",
"filename": "diffusion_pytorch_model_twins.safetensors",
"url": "https://huggingface.co/xinsir/controlnet-openpose-sdxl-1.0/resolve/main/diffusion_pytorch_model_twins.safetensors"
},
{
"name": "xinsir/Controlnet-Scribble-Sdxl-1.0-Anime",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-scribble-sdxl-1.0-anime",
"description": "[2.5GB] Controlnet SDXL Scribble model. (Ver. anime)",
"reference": "https://huggingface.co/xinsir/anime-painter",
"filename": "diffusion_pytorch_model.safetensors",
"url": "https://huggingface.co/xinsir/anime-painter/resolve/main/diffusion_pytorch_model.safetensors"
},
{
"name": "Kijai/ToonCrafter model checkpoint (interpolation fp16)",
"type": "checkpoint",
"base": "ToonCrafter",
"save_path": "checkpoints/ToonCrafter",
"description": "[5.25GB] ToonCrafter checkpoint model for ComfyUI-DynamiCrafterWrapper",
"reference": "https://huggingface.co/Kijai/DynamiCrafter_pruned",
"filename": "tooncrafter_512_interp-fp16.safetensors",
"url": "https://huggingface.co/Kijai/DynamiCrafter_pruned/resolve/main/tooncrafter_512_interp-fp16.safetensors"
},
{
"name": "CN-anytest_v4-marged.safetensors",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[2.5GB] AnyTest Controlnet. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged.safetensors"
},
{
"name": "CN-anytest_v4-marged_am_dim256.safetensors (dim256/Animagine)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] AnyTest Controlnet Lora (dim256) for Animagine. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged_am_dim256.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged_am_dim256.safetensors"
},
{
"name": "CN-anytest_v4-marged_am_dim128.safetensors (dim128/Animagine)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[396MB] AnyTest Controlnet Lora (dim128) for Animagine. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged_am_dim128.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged_am_dim128.safetensors"
},
{
"name": "CN-anytest_v4-marged_pn_dim256.safetensors (dim256/Pony)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] AnyTest Controlnet Lora (dim256) for Pony. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged_pn_dim256.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged_pn_dim256.safetensors"
},
{
"name": "CN-anytest_v4-marged_pn_dim128.safetensors (dim128/Pony)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[396MB] AnyTest Controlnet Lora (dim128) for Pony. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged_pn_dim128.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged_pn_dim128.safetensors"
},
{
"name": "CN-anytest_v3-50000_fp16.safetensors (fp16)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[2.5GB] AnyTest Controlnet. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_fp16.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_fp16.safetensors"
},
{
"name": "CN-anytest_v3-50000_am_dim256.safetensors (dim256/Animagine)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] AnyTest Controlnet Lora (dim256) for Animagine. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_am_dim256.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_am_dim256.safetensors"
},
{
"name": "CN-anytest_v3-50000_am_dim128.safetensors (dim128/Animagine)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[396MB] AnyTest Controlnet Lora (dim128) for Animagine. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_am_dim128.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_am_dim128.safetensors"
},
{
"name": "CN-anytest_v3-50000_pn_dim256.safetensors (dim256/Pony)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] AnyTest Controlnet Lora (dim256) for Pony. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_pn_dim256.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_pn_dim256.safetensors"
},
{
"name": "CN-anytest_v3-50000_pn_dim128.safetensors (dim128/Pony)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[396MB] AnyTest Controlnet Lora (dim128) for Pony. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_pn_dim128.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_pn_dim128.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitb/fp16)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[195MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vitb_fp16.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vitb_fp16.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitb/fp32)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[390MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vitb_fp32.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vitb_fp32.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitl/fp16)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[671MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vitl_fp16.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vitl_fp16.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitl/fp32)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[195MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vitl_fp32.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vitl_fp32.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vits/fp16)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[49.6MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vits_fp16.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vits_fp16.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitb/fp32)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[99.2MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vits_fp32.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vits_fp32.safetensors"
}
]
}

View File

@ -8,36 +8,655 @@
"install_type": "git-clone",
"description": "If you see this message, your ComfyUI-Manager is outdated.\nDev channel provides only the list of the developing nodes. If you want to find the complete node list, please go to the Default channel."
},
{
"author": "huchenlei",
"title": "ComfyUI-IC-Light [WIP]",
"reference": "https://github.com/huchenlei/ComfyUI-IC-Light",
"author": "zhulu111",
"title": "ComfyUI_Bxb [UNSAFE]",
"id": "ComfyUI_Bxb",
"reference": "https://github.com/zhulu111/ComfyUI_Bxb",
"files": [
"https://github.com/huchenlei/ComfyUI-IC-Light"
"https://github.com/zhulu111/ComfyUI_Bxb"
],
"install_type": "git-clone",
"description": "ComfyUI-IC-Light"
"description": "sdBxb, a tool that converts ComfyUI workflows into WeChat Mini Program, Douyin Mini Program, and H5 with one click, and supports payments."
},
{
"author": "Levy1417",
"title": "Universal-Data-Processing-Kit [UNSAFE]",
"reference": "https://github.com/Levy1417/Universal-Data-Processing-Kit",
"author": "mingqizhang",
"title": "ComfyUI_tool_zmq",
"id": "tool-zmq",
"reference": "https://github.com/mingqizhang/ComfyUI_tool_zmq",
"files": [
"https://github.com/Levy1417/Universal-Data-Processing-Kit"
"https://github.com/mingqizhang/ComfyUI_tool_zmq"
],
"install_type": "git-clone",
"description": "Nodes:DPK - Any Eval, DPK - Extract Array, DPK - Run External Program, DPK - Any Literals, DPK - Set Node States, DPK - Realtime Text Preview, DPK - Dynamic Action, DPK - Object To Json, DPK - Json To Object\n[w/This extension includes the ability to execute arbitrary code and programs.]"
"description": "Nodes:ImageConcat, ImageFlip, DWposeTransform, PoseFilter, DrawPose, ReplaceImgae, LoadImageFromPath."
},
{
"author": "runtime44",
"title": "Runtime44 ComfyUI Nodes",
"reference": "https://github.com/runtime44/comfyui_r44_nodes",
"author": "kijai",
"title": "ComfyUI-LuminaWrapper [WIP]",
"id": "luminawrapper",
"reference": "https://github.com/kijai/ComfyUI-LuminaWrapper",
"files": [
"https://github.com/runtime44/comfyui_r44_nodes"
"https://github.com/kijai/ComfyUI-LuminaWrapper"
],
"install_type": "git-clone",
"description": "Nodes: Runtime44Upscaler, Runtime44ColorMatch, Runtime44DynamicKSampler, Runtime44ImageOverlay, Runtime44ImageResizer, Runtime44ImageToNoise, Runtime44MaskSampler, Runtime44TiledMaskSampler, Runtime44IterativeUpscaleFactor, Runtime44ImageEnhance"
"description": "NOTE:Currently requires flash_attn!"
},
{
"author": "pzzmyc",
"title": "comfyui-sd3-simple-simpletuner",
"id": "simpletuner",
"reference": "https://github.com/pzzmyc/comfyui-sd3-simple-simpletuner",
"files": [
"https://github.com/pzzmyc/comfyui-sd3-simple-simpletuner"
],
"install_type": "git-clone",
"description": "Nodes:sd3 simple simpletuner by hhy."
},
{
"author": "horidream",
"title": "ComfyUI-Horidream",
"id": "horidream",
"reference": "https://github.com/horidream/ComfyUI-Horidream",
"files": [
"https://github.com/horidream/ComfyUI-Horidream"
],
"install_type": "git-clone",
"description": "Nodes:Pass Through With Sound."
},
{
"author": "kijai",
"title": "ComfyUI-DiffusersSD3Wrapper",
"id": "diffusers-sd3-wrapper",
"reference": "https://github.com/kijai/ComfyUI-DiffusersSD3Wrapper",
"files": [
"https://github.com/kijai/ComfyUI-DiffusersSD3Wrapper"
],
"install_type": "git-clone",
"description": "Nodes:Load SD3DiffusersPipeline, SD3 ControlNet Sampler"
},
{
"author": "AustinMroz",
"title": "ComfyUI-SD3-Medium-CN-Diffusers [WIP]",
"reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-SD3-Medium-CN-Diffusers",
"files": [
"https://github.com/AustinMroz/ComfyUI-WorkflowCheckpointing"
],
"install_type": "git-clone",
"description": "ComfyUI SD3-Medium ControlNet (Diffusers)"
},
{
"author": "redhottensors",
"title": "ComfyUI-ODE",
"id": "ode",
"reference": "https://github.com/redhottensors/ComfyUI-ODE",
"files": [
"https://github.com/redhottensors/ComfyUI-ODE"
],
"install_type": "git-clone",
"description": "ODE Solvers for ComfyUI\nThis node enables use of torchdiffeq ODE solvers with models. Intended for use with Stable Diffusion 3 and similar flow models."
},
{
"author": "maruhidd",
"title": "Transparent Background for ComfyUI",
"id": "transparent-bg",
"reference": "https://github.com/maruhidd/ComfyUI_Transparent-Background",
"files": [
"https://github.com/maruhidd/ComfyUI_Transparent-Background"
],
"install_type": "git-clone",
"description": "Nodes:Remove Background, Fill Transparent"
},
{
"author": "baicai99",
"title": "ComfyUI-FrameSkipping",
"id": "frame-skipping",
"reference": "https://github.com/baicai99/ComfyUI-FrameSkipping",
"files": [
"https://github.com/baicai99/ComfyUI-FrameSkipping"
],
"install_type": "git-clone",
"description": "This plugin can precisely control the rendering between frames, completing the synthesis of multiple frames in a single load. My homepage includes my attached workflow."
},
{
"author": "ejektaflex",
"title": "ComfyUI - Ty",
"id": "ty-nodes",
"reference": "https://github.com/ejektaflex/ComfyUI-Ty",
"files": [
"https://github.com/ejektaflex/ComfyUI-Ty"
],
"install_type": "git-clone",
"description": "Nodes:Lora Block Weight Regex Loader"
},
{
"author": "jtydhr88",
"title": "ComfyUI-Unique3D [WIP]",
"id": "unique3d",
"reference": "https://github.com/jtydhr88/ComfyUI-Unique3D",
"files": [
"https://github.com/jtydhr88/ComfyUI-Unique3D"
],
"install_type": "git-clone",
"description": "ComfyUI Unique3D is custom nodes that running [a/AiuniAI/Unique3D](https://github.com/AiuniAI/Unique3D) into ComfyUI."
},
{
"author": "kycg",
"title": "comfyui-Kwtoolset",
"id": "kwtoolset",
"reference": "https://github.com/kycg/comfyui-Kwtoolset",
"files": [
"https://github.com/kycg/comfyui-Kwtoolset"
],
"install_type": "git-clone",
"description": "Nodes:KwtoolsetLoraLoaderwithpreview, KwtoolsetCheckpointLoaderwithpreview, KwtoolsetLoadCheckpointsBatch, KwtoolsetGrowMaskPlus, KwtoolsetGetHipMask, KwtoolsetGetHipMasktest, KwtoolsetGetImageSize, KWPositiveString, KWNagetiveString, KWanywhereString, KwtoolsetChangeOpenpose, ..."
},
{
"author": "mashb1t",
"title": "ComfyUI mashb1t nodes",
"id": "mashb1t",
"reference": "https://github.com/mashb1t/comfyui-nodes-mashb1t",
"files": [
"https://github.com/mashb1t/comfyui-nodes-mashb1t"
],
"install_type": "git-clone",
"description": "This Python script is an optional add-on to the Comfy UI stable diffusion client."
},
{
"author": "immersiveexperience",
"title": "ie-comfyui-color-nodes",
"reference": "https://github.com/immersiveexperience/ie-comfyui-color-nodes",
"files": [
"https://github.com/immersiveexperience/ie-comfyui-color-nodes"
],
"install_type": "git-clone",
"description": "Custom ComfyUI nodes for simple color correction."
},
{
"author": "LZpenguin",
"title": "ComfyUI-Text",
"id": "comfy-text",
"reference": "https://github.com/LZpenguin/ComfyUI-Text",
"files": [
"https://github.com/LZpenguin/ComfyUI-Text"
],
"install_type": "git-clone",
"description": "Nodes:Add_text_by_mask.[w/This custom node cannot be installed simultaneously as it has the same repository name as MarkoCa1/ComfyUI-Text.]"
},
{
"author": "yushan777",
"title": "Y7 Nodes for ComfyUI",
"id": "y7nodes",
"reference": "https://github.com/yushan777/ComfyUI-Y7Nodes",
"files": [
"https://github.com/yushan777/ComfyUI-Y7Nodes"
],
"install_type": "git-clone",
"description": "Nodes:Count_Tokens_(Y7)"
},
{
"author": "norgeous",
"title": "UI Builder [WIP]",
"id": "norgeous",
"reference": "https://github.com/norgeous/ComfyUI-UI-Builder",
"files": [
"https://github.com/norgeous/ComfyUI-UI-Builder"
],
"install_type": "git-clone",
"description": "Alternative configurable React UI overlay for Comfy UI."
},
{
"author": "Shinsplat",
"title": "ComfyUI-Shinsplat [UNSAFE]",
"id": "shinsplat",
"reference": "https://github.com/Shinsplat/ComfyUI-Shinsplat",
"files": [
"https://github.com/Shinsplat/ComfyUI-Shinsplat"
],
"install_type": "git-clone",
"description": "Nodes: Clip Text Encode (Shinsplat), Clip Text Encode SDXL (Shinsplat), Lora Loader (Shinsplat).\n[w/This extension poses a risk of executing arbitrary commands through workflow execution. Please be cautious.]"
},
{
"author": "NitramDom",
"title": "ComfyUI_FacialFlip",
"id": "facialflip",
"reference": "https://github.com/NitramDom/ComfyUI_FacialFlip",
"files": [
"https://github.com/NitramDom/ComfyUI_FacialFlip"
],
"install_type": "git-clone",
"description": "Nodes:Swapper"
},
{
"author": "hy134300",
"title": "comfyui-hydit",
"reference": "https://github.com/hy134300/comfyui-hydit",
"files": [
"https://github.com/hy134300/comfyui-hydit"
],
"install_type": "git-clone",
"description": "This repository contains a customized node and workflow designed specifically for HunYuan DIT. The official tests conducted on DDPM, DDIM, and DPMMS have consistently yielded results that align with those obtained through the Diffusers library. However, it's important to note that we cannot assure the consistency of results from other ComfyUI native samplers with the Diffusers inference. We cordially invite users to explore our workflow and are open to receiving any inquiries or suggestions you may have."
},
{
"author": "corbin-hayden13",
"title": "ComfyUI-Better-Dimensions",
"id": "better-dim",
"reference": "https://github.com/corbin-hayden13/ComfyUI-Better-Dimensions",
"files": [
"https://github.com/corbin-hayden13/ComfyUI-Better-Dimensions"
],
"install_type": "git-clone",
"description": "Nodes:BetterImageDimensions, SDXLDimensions, PureRatio"
},
{
"author": "endman100",
"title": "ComfyUI Nodes: SaveConditioning and LoadConditioning",
"id": "save-load-conditioning",
"reference": "https://github.com/endman100/ComfyUI-SaveAndLoadPromptCondition",
"files": [
"https://github.com/endman100/ComfyUI-SaveAndLoadPromptCondition"
],
"install_type": "git-clone",
"description": "The SaveConditioning node is designed to save conditioning data to binary files. This is useful for storing and reusing conditioning information across different sessions or applications.\n[w/This node can only handle very limited conditioning at the text prompt level.]"
},
{
"author": "marduk191",
"title": "comfyui-marnodes",
"id": "marnodes",
"reference": "https://github.com/marduk191/comfyui-marnodes",
"files": [
"https://github.com/marduk191/comfyui-marnodes"
],
"install_type": "git-clone",
"description": "Nodes:marduk191_workflow_settings"
},
{
"author": "kijai",
"title": "ComfyUI-CV-VAE",
"id": "cv-vae",
"reference": "https://github.com/kijai/ComfyUI-CV-VAE",
"files": [
"https://github.com/kijai/ComfyUI-CV-VAE"
],
"install_type": "git-clone",
"description": "Nodes:CV_VAE_Load, CV_VAE_Encode, CV_VAE_Decode"
},
{
"author": "GentlemanHu",
"title": "ComfyUI Notifier",
"id": "notifier",
"reference": "https://github.com/GentlemanHu/ComfyUI-Notifier",
"files": [
"https://github.com/GentlemanHu/ComfyUI-Notifier"
],
"install_type": "git-clone",
"description": "Nodes:GentlemanHu_Notifier"
},
{
"author": "jimmm-ai",
"title": "TimeUi a ComfyUI Timeline Node System [WIP]",
"id": "timeline",
"reference": "https://github.com/jimmm-ai/TimeUi-a-ComfyUi-Timeline-Node",
"files": [
"https://github.com/jimmm-ai/TimeUi-a-ComfyUi-Timeline-Node"
],
"install_type": "git-clone",
"description": "I've been working on the UX/UI of a timeline custom node system for ComfyUI over the past two weeks. The goal is to create a timeline similar to video/animation editing tools, without relying on traditional timeframe code. You can effortlessly add, delete, or rearrange rows, providing a streamlined user experience."
},
{
"author": "jh-leon-kim",
"title": "ComfyUI-JHK-utils",
"id": "jhk",
"reference": "https://github.com/jh-leon-kim/ComfyUI-JHK-utils",
"files": [
"https://github.com/jh-leon-kim/ComfyUI-JHK-utils"
],
"install_type": "git-clone",
"description": "Nodes:JHK_Utils_LoadEmbed, JHK_Utils_string_merge, JHK_Utils_ImageRemoveBackground"
},
{
"author": "StartHua",
"title": "Comfyui_CXH_CRM",
"id": "cxh-crm",
"reference": "https://github.com/StartHua/Comfyui_CXH_CRM",
"files": [
"https://github.com/StartHua/Comfyui_CXH_CRM"
],
"install_type": "git-clone",
"description": "Nodes:CRM"
},
{
"author": "comfypod",
"title": "ComfyUI-Comflow",
"id": "comflow",
"reference": "https://github.com/comfypod/ComfyUI-Comflow",
"files": [
"https://github.com/comfypod/ComfyUI-Comflow"
],
"install_type": "git-clone",
"description": "ComfyUI-Comflow."
},
{
"author": "AI2lab",
"title": "comfyUI-tool-2lab",
"id": "tool2lab",
"reference": "https://github.com/AI2lab/comfyUI-tool-2lab",
"files": [
"https://github.com/AI2lab/comfyUI-tool-2lab"
],
"install_type": "git-clone",
"description": "simple tool set for developing workflow and publish to web api server"
},
{
"author": "pamparamm",
"title": "ComfyUI-ppm",
"id": "ppm",
"reference": "https://github.com/pamparamm/ComfyUI-ppm",
"files": [
"https://github.com/pamparamm/ComfyUI-ppm"
],
"install_type": "git-clone",
"description": "Nodes:Empty Latent Image (Aspect Ratio), Token Counter, Random Prompt Generator, StableCascade_AutoCompLatent, CLIPTextEncode, CLIPMicroConditioning, CLIPNegPip"
},
{
"author": "Scorpinaus",
"title": "ComfyUI-DiffusersLoader",
"id": "diffusersloader",
"reference": "https://github.com/Scorpinaus/ComfyUI-DiffusersLoader",
"files": [
"https://github.com/Scorpinaus/ComfyUI-DiffusersLoader"
],
"install_type": "git-clone",
"description": "The purpose of this package is to understand how loading of diffusers format checkpoints are done in comfyUI and to create a loader that works for SDXL."
},
{
"author": "FoundD-oka",
"title": "ComfyUI KISEKAE-OOTD",
"id": "kisekae-ootd",
"reference": "https://github.com/FoundD-oka/ComfyUI-kisekae-OOTD",
"files": [
"https://github.com/FoundD-oka/ComfyUI-kisekae-OOTD"
],
"install_type": "git-clone",
"description": "Nodes:LoadOOTDPipeline, LoadOOTDPipelineHub, LoadOOTDPipelineHub."
},
{
"author": "bruce007lee",
"title": "comfyui-tiny-utils",
"id": "tiny-utils",
"reference": "https://github.com/bruce007lee/comfyui-tiny-utils",
"files": [
"https://github.com/bruce007lee/comfyui-tiny-utils"
],
"install_type": "git-clone",
"description": "Nodes:FaceAlign, FaceAlignImageProcess, FaceAlignMaskProcess"
},
{
"author": "zmwv823",
"title": "ComfyUI-AnyText [UNSTABLE]",
"id": "anytext",
"reference": "https://github.com/zmwv823/ComfyUI-AnyText",
"files": [
"https://github.com/zmwv823/ComfyUI-AnyText"
],
"install_type": "git-clone",
"description": "Unofficial Simple And Rough Implementation Of [a/AnyText](https://github.com/tyxsspa/AnyText)"
},
{
"author": "brycegoh",
"title": "brycegoh/comfyui-custom-nodes",
"reference": "https://github.com/brycegoh/comfyui-custom-nodes",
"files": [
"https://github.com/brycegoh/comfyui-custom-nodes"
],
"install_type": "git-clone",
"description": "Nodes:MaskAreaComparisonSegment, FillMaskedArea, OCRAndMask, CombineTwoImageIntoOne"
},
{
"author": "LykosAI",
"title": "ComfyUI Nodes for Inference.Core",
"id": "inference-core",
"reference": "https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes",
"files": [
"https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes"
],
"install_type": "git-clone",
"description": "Primary Nodes for Inference.Core and Stability Matrix. With a focus on not impacting startup performance and using fully qualified Node names. [w/This custom node is likely to conflict with many other nodes.]"
},
{
"author": "blepping",
"title": "comfyui_overly_complicated_sampling",
"reference": "https://github.com/blepping/comfyui_overly_complicated_sampling",
"files": [
"https://github.com/blepping/comfyui_overly_complicated_sampling"
],
"install_type": "git-clone",
"description": "Very unstable, experimental and mathematically unsound sampling for ComfyUI.\nCurrent status: In flux, not suitable for general use."
},
{
"author": "tracerstar",
"title": "comfyui-p5js-node",
"id": "p5js",
"reference": "https://github.com/tracerstar/comfyui-p5js-node",
"files": [
"https://github.com/tracerstar/comfyui-p5js-node"
],
"install_type": "git-clone",
"description": "A simple proof of concept node to pass a p5js canvas through ComfyUI for img2img generation use."
},
{
"author": "chaojie",
"title": "ComfyUI-mobvoi-openapi",
"id": "mobvoi-openapi",
"reference": "https://github.com/chaojie/ComfyUI-mobvoi-openapi",
"files": [
"https://github.com/chaojie/ComfyUI-mobvoi-openapi"
],
"install_type": "git-clone",
"description": "Nodes:MobvoiOpenapiMetamanText, MobvoiOpenapiMetamanAudio, MobvoiOpenapiTts, HtmlViewer, OssUploadImage, OssUploadAudio"
},
{
"author": "immersiveexperience",
"title": "ie-comfyui-color-nodes",
"id": "ie-color-nodes",
"reference": "https://github.com/immersiveexperience/ie-comfyui-color-nodes",
"files": [
"https://github.com/immersiveexperience/ie-comfyui-color-nodes"
],
"install_type": "git-clone",
"description": "Custom ComfyUI nodes for simple color correction."
},
{
"author": "beyastard",
"title": "ComfyUI_BeySoft",
"reference": "https://github.com/beyastard/ComfyUI_BeySoft",
"files": [
"https://github.com/beyastard/ComfyUI_BeySoft"
],
"install_type": "git-clone",
"description": "Nodes:BeySoft"
},
{
"author": "christian-byrne",
"title": "🌌 Infinite Parallax Nodes [WIP]",
"reference": "https://github.com/christian-byrne/infinite-zoom-parallax-nodes",
"files": [
"https://github.com/christian-byrne/infinite-zoom-parallax-nodes"
],
"install_type": "git-clone",
"description": "Nodes:Parallax Config, Load Parallax Frame, Save Parallax Object Layers, Layer Shifter for Parallax Outpainting, Save Parallax Frame, Shrink and Pad for Outpainting, Create Infinite Zoom Video"
},
{
"author": "flyingdogsoftware",
"title": "Gyre for ComfyUI",
"id": "gyre",
"reference": "https://github.com/flyingdogsoftware/gyre_for_comfyui",
"files": [
"https://github.com/flyingdogsoftware/gyre_for_comfyui"
],
"install_type": "git-clone",
"description": "Nodes:GyreLoopStart, GyreLoopEnd, GyreIfElse"
},
{
"author": "githubYiheng",
"title": "comfyui_median_filter",
"id": "median-filter",
"reference": "https://github.com/githubYiheng/comfyui_median_filter",
"files": [
"https://github.com/githubYiheng/comfyui_median_filter"
],
"install_type": "git-clone",
"description": "Nodes:Apply Median Filter. [w/This has been updated to be equivalent to the comfyui_kmeans_filter node. Mistake?]"
},
{
"author": "nat-chan",
"title": "comfyui-eval [UNSAFE]",
"id": "evalnode",
"reference": "https://github.com/nat-chan/comfyui-eval",
"files": [
"https://github.com/nat-chan/comfyui-eval"
],
"install_type": "git-clone",
"description": "Nodes:EvalNode [w/Please do not use the node that executes arbitrary code and outputs in any type, as it is dangerous.]"
},
{
"author": "haofanwang",
"title": "ComfyUI-InstantStyle",
"id": "instantstyle",
"reference": "https://github.com/haofanwang/ComfyUI-InstantStyle",
"files": [
"https://github.com/haofanwang/ComfyUI-InstantStyle"
],
"install_type": "git-clone",
"description": "Nodes:PromptLoader, BaseModelLoader, InstantStyleLoader, InstantStyleGenerationNode"
},
{
"author": "jp0215",
"title": "comfyUI_padding-resize_node",
"reference": "https://github.com/jp0215/comfyUI_padding-resize_node",
"files": [
"https://github.com/jp0215/comfyUI_padding-resize_node/raw/main/PaddingNode.py",
"https://github.com/jp0215/comfyUI_padding-resize_node/raw/main/ResizeNode.py"
],
"install_type": "copy",
"description": "Padding image to 8x: input image and mask, if the side length is not an integer multiple of 8, expand the side length to the smallest multiple of 8 greater than the original side length. Output padding image and mask. Resize to the origin: input the generated image and the original image, crop the generated image to the size of the original image, output the cropped image."
},
{
"author": "Quasimondo",
"title": "ComfyUI-QuasimondoNodes [WIP]",
"id": "quasimondo-nodes",
"reference": "https://github.com/Quasimondo/ComfyUI-QuasimondoNodes",
"files": [
"https://github.com/Quasimondo/ComfyUI-QuasimondoNodes"
],
"install_type": "git-clone",
"description": "Nodes:Custom Shader, Spring Mesh"
},
{
"author": "TSFSean",
"title": "ComfyUI-TSFNodes",
"id": "tsfnodes",
"reference": "https://github.com/TSFSean/ComfyUI-TSFNodes",
"files": [
"https://github.com/TSFSean/ComfyUI-TSFNodes"
],
"install_type": "git-clone",
"description": "Nodes:GyroOSC"
},
{
"author": "blib-la",
"title": "ComfyUI-Captain-Extensions",
"id": "captain",
"reference": "https://github.com/blib-la/ComfyUI-Captain-Extensions",
"files": [
"https://github.com/blib-la/ComfyUI-Captain-Extensions"
],
"install_type": "git-clone",
"description": "ComfyUI extensions for better [a/Captain](https://github.com/blib-la/captain) integration."
},
{
"author": "ejektaflex",
"title": "ComfyUI-Ty",
"reference": "https://github.com/ejektaflex/ComfyUI-Ty",
"files": [
"https://github.com/ejektaflex/ComfyUI-Ty"
],
"install_type": "git-clone",
"description": "Nodes:Lora Block Weight Regex Loader"
},
{
"author": "christian-byrne",
"title": "Python Interpreter ComfyUI Node [UNSAFE]",
"reference": "https://github.com/christian-byrne/python-interpreter-node",
"files": [
"https://github.com/christian-byrne/python-interpreter-node"
],
"install_type": "git-clone",
"description": "For debugging, parsing data, generating random values, converting types, testing custom nodes faster.\nReference and use variables in the code using the same names as the inputs in the UI\nWrapper class around tensors with operator overloading for doing common image manipulation tasks.I might remove this aspect\n[w/This extension allows you to run programs through Python code in your workflow, which may not be secure. Use with caution.]"
},
{
"author": "sofakid",
"title": "dandy [UNSAFE]",
"reference": "https://github.com/sofakid/dandy",
"files": [
"https://github.com/sofakid/dandy"
],
"install_type": "git-clone",
"description": "Dandy is a JavaScript bridge for ComfyUI. It includes everything you need to make JavaScript enabled extensions, or just load and code in little editor nodes right in ComfyUI.[w/This code can cause security issues because it allows for the execution of arbitrary JavaScript input.]"
},
{
"author": "tachyon-beep",
"title": "comfyui-simplefeed [UNSAFE]",
"reference": "https://github.com/tachyon-beep/comfyui-simplefeed",
"files": [
"https://github.com/tachyon-beep/comfyui-simplefeed"
],
"install_type": "git-clone",
"description": "A simple image feed for comfyUI which is easily configurable and easily extensible.\nUse the filter button to select which nodes write to the feed. Under settings, there are options that allow you: Position the feed. Set a max iamge count for the feed. Set oldest to newest or newest to oldest."
},
{
"author": "shadowcz007",
"title": "ComfyUI-PuLID [TEST]",
"reference": "https://github.com/shadowcz007/ComfyUI-PuLID-Test",
"files": [
"https://github.com/shadowcz007/ComfyUI-PuLID-Test"
],
"install_type": "git-clone",
"description": "[a/PuLID](https://github.com/ToTheBeginning/PuLID) ComfyUI native implementation."
},
{
"author": "sangeet",
"title": "comfyui-testui [TEST]",
"reference": "https://github.com/sangeet/comfyui-testui",
"files": [
"https://github.com/sangeet/comfyui-testui"
],
"install_type": "git-clone",
"description": "Simple Frontend For ComfyUI workflow"
},
{
"author": "Elawphant",
"title": "ComfyUI-MusicGen [WIP]",
"id": "musicgen",
"reference": "https://github.com/Elawphant/ComfyUI-MusicGen",
"files": [
"https://github.com/Elawphant/ComfyUI-MusicGen"
],
"install_type": "git-clone",
"description": "ComfyUI for Meta MusicGen."
},
{
"author": "jtscmw01",
"title": "ComfyUI-DiffBIR",
"id": "diffbir",
"reference": "https://github.com/jtscmw01/ComfyUI-DiffBIR",
"files": [
"https://github.com/jtscmw01/ComfyUI-DiffBIR"
],
"install_type": "git-clone",
"description": "This extension provides [a/DiffBIR](https://github.com/XPixelGroup/DiffBIR) feature."
},
{
"author": "ericbeyer",
@ -329,16 +948,6 @@
"install_type": "git-clone",
"description": "Generator for StyleGAN 3"
},
{
"author": "christian-byrne",
"title": "elimination-nodes",
"reference": "https://github.com/christian-byrne/elimination-nodes",
"files": [
"https://github.com/christian-byrne/elimination-nodes"
],
"install_type": "git-clone",
"description": "Nodes:Paste Cutout on Base Image"
},
{
"author": "A719689614",
"title": "ComfyUI_AC_FUNV8Beta1",
@ -519,16 +1128,6 @@
"install_type": "git-clone",
"description": "Download [a/CLIPSeg](https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main), move to : models/clipseg"
},
{
"author": "dezi-ai",
"title": "ComfyUI Animate LCM",
"reference": "https://github.com/dezi-ai/ComfyUI-AnimateLCM",
"files": [
"https://github.com/dezi-ai/ComfyUI-AnimateLCM"
],
"install_type": "git-clone",
"description": "ComfyUI implementation for [a/AnimateLCM](https://animatelcm.github.io/) [[a/paper](https://arxiv.org/abs/2402.00769)].\b[w/This extension includes a large number of nodes imported from the existing custom nodes, increasing the likelihood of conflicts.]"
},
{
"author": "stutya",
"title": "ComfyUI-Terminal [UNSAFE]",
@ -839,16 +1438,6 @@
"install_type": "git-clone",
"description": "After discovering @storyicon implementation here of Segment Anything, I realized its potential as a powerful tool for ComfyUI if implemented correctly. I delved into the SAM and Dino models. The following is my own adaptation of sam_hq for ComfyUI."
},
{
"author": "phineas-pta",
"title": "comfy-trt-test [WIP]",
"reference": "https://github.com/phineas-pta/comfy-trt-test",
"files": [
"https://github.com/phineas-pta/comfy-trt-test"
],
"install_type": "git-clone",
"description": "Test project for ComfyUI TensorRT Support.\nNOT WORKING YET.\nnot automatic yet, do not use ComfyUI-Manager to install !!!.\nnot beginner-friendly yet, still intended to technical users\nNOTE: The reason for registration in the Manager is for guidance, and for detailed installation instructions, please visit the repository."
},
{
"author": "Brandelan",
"title": "ComfyUI_bd_customNodes",

View File

@ -171,10 +171,17 @@
[
"> Clear Text",
"> Float to Int",
"> Get Mean Color",
"> Int",
"> Int to Text",
"> Light Source Mask",
"> Load Image",
"> Load Image From Folder",
"> Mask Curves",
"> NIKSampler",
"> Noise From Image",
"> Normal Map Lighting",
"> RGB Color",
"> Resolution by Aspect Ratio",
"> Rotate Image",
"> Save Image",
@ -280,6 +287,15 @@
"title_aux": "ComfyUI_LineBreakInserter"
}
],
"https://github.com/Elawphant/ComfyUI-MusicGen": [
[
"AudioLoader",
"MusicGen"
],
{
"title_aux": "ComfyUI-MusicGen [WIP]"
}
],
"https://github.com/ExponentialML/ComfyUI_LiveDirector": [
[
"LiveDirector"
@ -313,6 +329,29 @@
"title_aux": "ComfyUI-Notifier"
}
],
"https://github.com/GraftingRayman/ComfyUI_GR_PromptSelector": [
[
"GR Flip Tile Random Inverted",
"GR Flip Tile Random Red Ring",
"GR Image Details Displayer",
"GR Image Details Saver",
"GR Image Resize",
"GR Image Resize Methods",
"GR Image Size",
"GR Mask Create",
"GR Mask Create Random",
"GR Mask Resize",
"GR Multi Mask Create",
"GR Prompt Selector",
"GR Prompt Selector Multi",
"GR Stack Image",
"GR Tile and Border Image",
"GR Tile and Border Image Random Flip"
],
{
"title_aux": "ComfyUI-GR"
}
],
"https://github.com/GrindHouse66/ComfyUI-GH_Tools": [
[
"GHImg_Sizer",
@ -354,6 +393,14 @@
"title_aux": "comfy-consistency-vae"
}
],
"https://github.com/LZpenguin/ComfyUI-Text": [
[
"Add_text_by_mask"
],
{
"title_aux": "ComfyUI-Text"
}
],
"https://github.com/LarryJane491/ComfyUI-ModelUnloader": [
[
"Model Unloader"
@ -370,6 +417,79 @@
"title_aux": "ComfyUI simple ChatGPT completion [UNSAFE]"
}
],
"https://github.com/LykosAI/ComfyUI-Inference-Core-Nodes": [
[
"AIO_Preprocessor",
"AnimalPosePreprocessor",
"AnimeFace_SemSegPreprocessor",
"AnimeLineArtPreprocessor",
"BAE-NormalMapPreprocessor",
"BinaryPreprocessor",
"CannyEdgePreprocessor",
"ColorPreprocessor",
"DWPreprocessor",
"DensePosePreprocessor",
"DepthAnythingPreprocessor",
"DiffusionEdge_Preprocessor",
"FacialPartColoringFromPoseKps",
"FakeScribblePreprocessor",
"HEDPreprocessor",
"HintImageEnchance",
"ImageGenResolutionFromImage",
"ImageGenResolutionFromLatent",
"ImageIntensityDetector",
"ImageLuminanceDetector",
"InpaintPreprocessor",
"LayeredDiffusionApply",
"LayeredDiffusionCondApply",
"LayeredDiffusionCondJointApply",
"LayeredDiffusionDecode",
"LayeredDiffusionDecodeRGBA",
"LayeredDiffusionDecodeSplit",
"LayeredDiffusionDiffApply",
"LayeredDiffusionJointApply",
"LeReS-DepthMapPreprocessor",
"LineArtPreprocessor",
"LineartStandardPreprocessor",
"M-LSDPreprocessor",
"Manga2Anime_LineArt_Preprocessor",
"MaskOptFlow",
"MediaPipe-FaceMeshPreprocessor",
"MeshGraphormer-DepthMapPreprocessor",
"MiDaS-DepthMapPreprocessor",
"MiDaS-NormalMapPreprocessor",
"ModelMergeBlockNumber",
"ModelMergeSDXL",
"ModelMergeSDXLDetailedTransformers",
"ModelMergeSDXLTransformers",
"ModelSamplerTonemapNoiseTest",
"OneFormer-ADE20K-SemSegPreprocessor",
"OneFormer-COCO-SemSegPreprocessor",
"OpenposePreprocessor",
"PiDiNetPreprocessor",
"PixelPerfectResolution",
"PromptExpansion",
"ReferenceOnlySimple",
"RescaleClassifierFreeGuidanceTest",
"SAMPreprocessor",
"SavePoseKpsAsJsonFile",
"ScribblePreprocessor",
"Scribble_XDoG_Preprocessor",
"SemSegPreprocessor",
"ShufflePreprocessor",
"TEEDPreprocessor",
"TilePreprocessor",
"TonemapNoiseWithRescaleCFG",
"UniFormer-SemSegPreprocessor",
"Unimatch_OptFlowPreprocessor",
"Zoe-DepthMapPreprocessor",
"Zoe_DepthAnythingPreprocessor"
],
{
"author": "tstandley",
"title_aux": "ComfyUI Nodes for Inference.Core"
}
],
"https://github.com/MrAdamBlack/CheckProgress": [
[
"CHECK_PROGRESS"
@ -386,6 +506,14 @@
"title_aux": "DJZ-Nodes"
}
],
"https://github.com/NitramDom/ComfyUI_FacialFlip": [
[
"Swapper"
],
{
"title_aux": "ComfyUI_FacialFlip"
}
],
"https://github.com/PluMaZero/ComfyUI-SpaceFlower": [
[
"SpaceFlower_HangulPrompt",
@ -395,6 +523,22 @@
"title_aux": "ComfyUI-SpaceFlower"
}
],
"https://github.com/Quasimondo/ComfyUI-QuasimondoNodes": [
[
"Color Match",
"Custom Shader",
"Folder Queue Manager",
"Image Blend by Mask (Batch)",
"Image Noise Generator",
"Perlin Noise Generator",
"Random Image Generator",
"Spring Mesh",
"Video Queue Manager"
],
{
"title_aux": "ComfyUI-QuasimondoNodes [WIP]"
}
],
"https://github.com/SadaleNet/ComfyUI-Prompt-To-Prompt": [
[
"CLIPTextEncodePromptToPrompt",
@ -421,15 +565,73 @@
"title_aux": "ComfyUI-MS-Nodes [WIP]"
}
],
"https://github.com/Scorpinaus/ComfyUI-DiffusersLoader": [
[
"CombinedDiffusersLoader",
"CombinedDiffusersSD15Loader",
"CombinedDiffusersSDXLLoader",
"DiffusersClipLoader",
"DiffusersUNETLoader",
"DiffusersVAELoader",
"SD15CLIPLoader",
"SD15UNETLoader",
"SD15VAELoader",
"SDXLCLIPLoader",
"SDXLUNETLoader",
"SDXLVAELoader"
],
{
"title_aux": "ComfyUI-DiffusersLoader"
}
],
"https://github.com/SeedV/ComfyUI-SeedV-Nodes": [
[
"CheckpointLoaderSimpleShared //SeedV",
"ControlNetLoaderAdvancedShared",
"LoraLoader //SeedV",
"Script"
],
{
"title_aux": "ComfyUI-SeedV-Nodes [UNSAFE]"
}
],
"https://github.com/Shinsplat/ComfyUI-Shinsplat": [
[
"Clip Text Encode (Shinsplat)",
"Clip Text Encode SD3 (Shinsplat)",
"Clip Text Encode SDXL (Shinsplat)",
"Green Box (Shinsplat)",
"Lora Loader (Shinsplat)",
"Python (Shinsplat)",
"Python - More Inputs (Shinsplat)",
"String Interpolated (Shinsplat)",
"Sum Wrap (Shinsplat)",
"Variables (Shinsplat)"
],
{
"author": "Shinsplat",
"description": "",
"nickname": "shinsplat",
"title": "ComfyUI-Shinsplat",
"title_aux": "ComfyUI-Shinsplat [UNSAFE]"
}
],
"https://github.com/StartHua/Comfyui_CXH_CRM": [
[
"CRM"
],
{
"title_aux": "Comfyui_CXH_CRM"
}
],
"https://github.com/TSFSean/ComfyUI-TSFNodes": [
[
"GyroOSC"
],
{
"title_aux": "ComfyUI-TSFNodes"
}
],
"https://github.com/Video3DGenResearch/comfyui-batch-input-node": [
[
"BatchImageAndPrompt",
@ -472,6 +674,14 @@
"title_aux": "ComfyUI-AnyText [WIP]"
}
],
"https://github.com/ZHO-ZHO-ZHO/ComfyUI-PuLID-ZHO": [
[
"PuLID_Zho"
],
{
"title_aux": "ComfyUI-PuLID-ZHO [WIP]"
}
],
"https://github.com/alt-key-project/comfyui-dream-video-batches": [
[
"Blended Transition [DVB]",
@ -512,6 +722,26 @@
"title_aux": "Dream Project Video Batches [WIP]"
}
],
"https://github.com/baicai99/ComfyUI-FrameSkipping": [
[
"FrameSkipping",
"FrameTruncating",
"IntOperationsNode",
"MaskFrameSkipping",
"WhiteMaskGenerator"
],
{
"title_aux": "ComfyUI-FrameSkipping"
}
],
"https://github.com/beyastard/ComfyUI_BeySoft": [
[
"BeySoft"
],
{
"title_aux": "ComfyUI_BeySoft"
}
],
"https://github.com/birnam/ComfyUI-GenData-Pack": [
[
"Checkpoint From String \ud83d\udc69\u200d\ud83d\udcbb",
@ -552,6 +782,15 @@
"title_aux": "Gen Data Tester [WIP]"
}
],
"https://github.com/blepping/comfyui_overly_complicated_sampling": [
[
"ComposableSampler",
"ComposableStepSampler"
],
{
"title_aux": "comfyui_overly_complicated_sampling"
}
],
"https://github.com/bruce007lee/comfyui-cleaner": [
[
"cleaner"
@ -560,6 +799,29 @@
"title_aux": "comfyui-cleaner"
}
],
"https://github.com/bruce007lee/comfyui-tiny-utils": [
[
"CropImageByMask",
"FaceAlign",
"FaceAlignImageProcess",
"FaceAlignMaskProcess",
"ImageFillColorByMask"
],
{
"title_aux": "comfyui-tiny-utils"
}
],
"https://github.com/brycegoh/comfyui-custom-nodes": [
[
"CombineTwoImageIntoOne",
"FillMaskedArea",
"MaskAreaComparisonSegment",
"OCRAndMask"
],
{
"title_aux": "brycegoh/comfyui-custom-nodes"
}
],
"https://github.com/chaojie/ComfyUI-DynamiCrafter": [
[
"DynamiCrafter Simple",
@ -571,6 +833,42 @@
"title_aux": "ComfyUI DynamiCrafter"
}
],
"https://github.com/chaojie/ComfyUI-mobvoi-openapi": [
[
"HtmlViewer",
"MobvoiOpenapiMetamanAudio",
"MobvoiOpenapiMetamanText",
"MobvoiOpenapiTts",
"OssUploadAudio",
"OssUploadImage"
],
{
"title_aux": "ComfyUI-mobvoi-openapi"
}
],
"https://github.com/christian-byrne/infinite-zoom-parallax-nodes": [
[
"Create Parallax Video",
"Layer Shifter for Parallax Outpainting",
"Load Most Recent Image in Folder",
"Load Parallax Frame",
"Load Random Image-Pose Pair",
"Parallax Config",
"Save Parallax Frame",
"Shrink and Pad for Outpainting"
],
{
"title_aux": "\ud83c\udf0c Infinite Parallax Nodes [WIP]"
}
],
"https://github.com/christian-byrne/python-interpreter-node": [
[
"Exec Python Code Script"
],
{
"title_aux": "Python Interpreter ComfyUI Node [UNSAFE]"
}
],
"https://github.com/comfyanonymous/ComfyUI": [
[
"AddNoise",
@ -587,6 +885,7 @@
"CLIPSetLastLayer",
"CLIPTextEncode",
"CLIPTextEncodeControlnet",
"CLIPTextEncodeSD3",
"CLIPTextEncodeSDXL",
"CLIPTextEncodeSDXLRefiner",
"CLIPVisionEncode",
@ -616,6 +915,7 @@
"DualCLIPLoader",
"EmptyImage",
"EmptyLatentImage",
"EmptySD3LatentImage",
"ExponentialScheduler",
"FeatherMask",
"FlipSigmas",
@ -683,6 +983,7 @@
"ModelMergeSubtract",
"ModelSamplingContinuousEDM",
"ModelSamplingDiscrete",
"ModelSamplingSD3",
"ModelSamplingStableCascade",
"Morphology",
"PatchModelAddDownscale",
@ -711,6 +1012,7 @@
"SamplerDPMPP_3M_SDE",
"SamplerDPMPP_SDE",
"SamplerEulerAncestral",
"SamplerLCMUpscale",
"SamplerLMS",
"SaveAnimatedPNG",
"SaveAnimatedWEBP",
@ -722,6 +1024,7 @@
"SolidMask",
"SplitImageWithAlpha",
"SplitSigmas",
"SplitSigmasDenoise",
"StableCascade_EmptyLatentImage",
"StableCascade_StageB_Conditioning",
"StableCascade_StageC_VAEEncode",
@ -732,6 +1035,7 @@
"StyleModelLoader",
"ThresholdMask",
"TomePatchModel",
"TripleCLIPLoader",
"UNETLoader",
"UNetCrossAttentionMultiply",
"UNetSelfAttentionMultiply",
@ -747,6 +1051,7 @@
"VPScheduler",
"VideoLinearCFGGuidance",
"VideoTriangleCFGGuidance",
"WebcamCapture",
"unCLIPCheckpointLoader",
"unCLIPConditioning"
],
@ -754,51 +1059,38 @@
"title_aux": "ComfyUI"
}
],
"https://github.com/dezi-ai/ComfyUI-AnimateLCM": [
"https://github.com/comfypod/ComfyUI-Comflow": [
[
"ADE_AdjustPEFullStretch",
"ADE_AdjustPEManual",
"ADE_AdjustPESweetspotStretch",
"ADE_AnimateDiffCombine",
"ADE_AnimateDiffKeyframe",
"ADE_AnimateDiffLoRALoader",
"ADE_AnimateDiffLoaderGen1",
"ADE_AnimateDiffLoaderV1Advanced",
"ADE_AnimateDiffLoaderWithContext",
"ADE_AnimateDiffModelSettings",
"ADE_AnimateDiffModelSettingsAdvancedAttnStrengths",
"ADE_AnimateDiffModelSettingsSimple",
"ADE_AnimateDiffModelSettings_Release",
"ADE_AnimateDiffSamplingSettings",
"ADE_AnimateDiffSettings",
"ADE_AnimateDiffUniformContextOptions",
"ADE_AnimateDiffUnload",
"ADE_ApplyAnimateDiffModel",
"ADE_ApplyAnimateDiffModelSimple",
"ADE_BatchedContextOptions",
"ADE_EmptyLatentImageLarge",
"ADE_IterationOptsDefault",
"ADE_IterationOptsFreeInit",
"ADE_LoadAnimateDiffModel",
"ADE_LoopedUniformContextOptions",
"ADE_LoopedUniformViewOptions",
"ADE_MaskedLoadLora",
"ADE_MultivalDynamic",
"ADE_MultivalScaledMask",
"ADE_NoiseLayerAdd",
"ADE_NoiseLayerAddWeighted",
"ADE_NoiseLayerReplace",
"ADE_StandardStaticContextOptions",
"ADE_StandardStaticViewOptions",
"ADE_StandardUniformContextOptions",
"ADE_StandardUniformViewOptions",
"ADE_UseEvolvedSampling",
"ADE_ViewsOnlyContextOptions",
"AnimateDiffLoaderV1",
"CheckpointLoaderSimpleWithNoiseSelect"
"ComflowInputBoolean",
"ComflowInputCheckpoint",
"ComflowInputImage",
"ComflowInputImageAlpha",
"ComflowInputImageBatch",
"ComflowInputLora",
"ComflowInputNumber",
"ComflowInputNumberInt",
"ComflowInputNumberSlider",
"ComflowInputText",
"ComflowInputVid",
"ComflowInputVideo",
"ComflowWebsocketImageInput",
"ComflowWebsocketImageOutput"
],
{
"title_aux": "ComfyUI Animate LCM"
"description": "",
"nickname": "Comflow",
"title": "comflow",
"title_aux": "ComfyUI-Comflow"
}
],
"https://github.com/corbin-hayden13/ComfyUI-Better-Dimensions": [
[
"BetterImageDimensions",
"PureRatio",
"SDXLDimensions"
],
{
"title_aux": "ComfyUI-Better-Dimensions"
}
],
"https://github.com/dfl/comfyui-stylegan": [
@ -880,6 +1172,31 @@
"title_aux": "ComfyUI-audio"
}
],
"https://github.com/ejektaflex/ComfyUI-Ty": [
[
"Lora Block Weight Regex Loader // Ty"
],
{
"title_aux": "ComfyUI-Ty"
}
],
"https://github.com/endman100/ComfyUI-SaveAndLoadPromptCondition": [
[
"LoadContditioning",
"SaveConditioning"
],
{
"title_aux": "ComfyUI Nodes: SaveConditioning and LoadConditioning"
}
],
"https://github.com/ericbeyer/guidance_interval": [
[
"Guidance Interval"
],
{
"title_aux": "guidance_interval"
}
],
"https://github.com/flowtyone/comfyui-flowty-lcm": [
[
"LCMSampler"
@ -888,6 +1205,17 @@
"title_aux": "comfyui-flowty-lcm"
}
],
"https://github.com/flyingdogsoftware/gyre_for_comfyui": [
[
"BackgroundRemoval",
"GyreIfElse",
"GyreLoopEnd",
"GyreLoopStart"
],
{
"title_aux": "Gyre for ComfyUI"
}
],
"https://github.com/foglerek/comfyui-cem-tools": [
[
"ProcessImageBatch"
@ -907,6 +1235,25 @@
"title_aux": "ComfyUI_stable_fast"
}
],
"https://github.com/githubYiheng/comfyui_median_filter": [
[
"ImageMedianFilter"
],
{
"title_aux": "comfyui_median_filter"
}
],
"https://github.com/haofanwang/ComfyUI-InstantStyle": [
[
"BaseModelLoader",
"InstantStyleGenerationNode",
"InstantStyleLoader",
"PromptLoader"
],
{
"title_aux": "ComfyUI-InstantStyle"
}
],
"https://github.com/houdinii/comfy-magick": [
[
"AdaptiveBlur",
@ -972,6 +1319,18 @@
"title_aux": "comfyui-hb-node"
}
],
"https://github.com/hy134300/comfyui-hydit": [
[
"DiffusersClipTextEncode",
"DiffusersModelMakeup",
"DiffusersPipelineLoader",
"DiffusersSampler",
"DiffusersSchedulerLoader"
],
{
"title_aux": "comfyui-hydit"
}
],
"https://github.com/ilovejohnwhite/UncleBillyGoncho": [
[
"CannyEdgePreprocessor",
@ -992,6 +1351,36 @@
"title_aux": "TatToolkit"
}
],
"https://github.com/immersiveexperience/ie-comfyui-color-nodes": [
[
"Average Color",
"Complementary Color",
"Hex Color to Image",
"Hex to Color Name",
"Random String"
],
{
"title_aux": "ie-comfyui-color-nodes"
}
],
"https://github.com/jh-leon-kim/ComfyUI-JHK-utils": [
[
"JHK_Utils_ImageRemoveBackground",
"JHK_Utils_LoadEmbed",
"JHK_Utils_string_merge"
],
{
"title_aux": "ComfyUI-JHK-utils"
}
],
"https://github.com/jimmm-ai/TimeUi-a-ComfyUi-Timeline-Node": [
[
"utils/TimelineUI"
],
{
"title_aux": "TimeUi a ComfyUI Timeline Node System [WIP]"
}
],
"https://github.com/jn-jairo/jn_node_suite_comfyui": [
[
"JN_AreaInfo",
@ -1069,6 +1458,35 @@
"title_aux": "jn_node_suite_comfyui [WIP]"
}
],
"https://github.com/jp0215/comfyUI_padding-resize_node/raw/main/PaddingNode.py": [
[
"function"
],
{
"title_aux": "comfyUI_padding-resize_node"
}
],
"https://github.com/jtscmw01/ComfyUI-DiffBIR": [
[
"DiffBIR_sample",
"DiffBIR_sample_advanced",
"Simple_load",
"Stage1_load",
"Stage2_load"
],
{
"title_aux": "ComfyUI-DiffBIR"
}
],
"https://github.com/jtydhr88/ComfyUI-Unique3D": [
[
"Unique3DLoadPipeline",
"Unique3DRun"
],
{
"title_aux": "ComfyUI-Unique3D"
}
],
"https://github.com/kadirnar/ComfyUI-Adapter": [
[
"GarmentSegLoader"
@ -1116,6 +1534,16 @@
"title_aux": "ComfyUI_Usability (WIP)"
}
],
"https://github.com/kijai/ComfyUI-CV-VAE": [
[
"CV_VAE_Decode",
"CV_VAE_Encode",
"CV_VAE_Load"
],
{
"title_aux": "ComfyUI-CV-VAE"
}
],
"https://github.com/kijai/ComfyUI-DeepSeek-VL": [
[
"deepseek_vl_inference",
@ -1134,6 +1562,25 @@
"title_aux": "ComfyUI_Prompt_Template_CustomNodes"
}
],
"https://github.com/kycg/comfyui-Kwtoolset": [
[
"KWNagetiveString",
"KWPositiveString",
"KWanywhereString",
"KwtoolsetChangeOpenpose",
"KwtoolsetCheckpointLoaderwithpreview",
"KwtoolsetGetHipMask",
"KwtoolsetGetHipMasktest",
"KwtoolsetGetImageSize",
"KwtoolsetGrowMaskPlus",
"KwtoolsetLoadCheckpointsBatch",
"KwtoolsetLoraLoaderwithpreview",
"LatentMatch"
],
{
"title_aux": "comfyui-Kwtoolset"
}
],
"https://github.com/laksjdjf/ssd-1b-comfyui": [
[
"SSD-1B-Loader"
@ -1152,6 +1599,15 @@
"title_aux": "ComfyUI-MotionThiefExperiment"
}
],
"https://github.com/longgui0318/comfyui-one-more-step": [
[
"Calculate More Step Latent",
"Load More Step Model"
],
{
"title_aux": "comfyui-one-more-step [WIP]"
}
],
"https://github.com/ltdrdata/ComfyUI-Workflow-Component": [
[
"ComboToString",
@ -1178,6 +1634,37 @@
"title_aux": "ComfyUI-BuildPath"
}
],
"https://github.com/marduk191/comfyui-marnodes": [
[
"marduk191_5_text_string",
"marduk191_5way_text_switch",
"marduk191_workflow_settings"
],
{
"author": "\u02f6marduk191",
"description": "A node to set workflow settings.",
"nickname": "marduk191 workflow settings",
"title": "marduk191 workflow settings",
"title_aux": "comfyui-marnodes"
}
],
"https://github.com/maruhidd/ComfyUI_Transparent-Background": [
[
"FillTransparentNode",
"RemoveBackgroundNode"
],
{
"title_aux": "Transparent Background for ComfyUI"
}
],
"https://github.com/mashb1t/comfyui-nodes-mashb1t": [
[
"mashb1t: LoadImage"
],
{
"title_aux": "ComfyUI mashb1t nodes"
}
],
"https://github.com/mut-ex/comfyui-gligengui-node": [
[
"GLIGEN_GUI"
@ -1239,18 +1726,32 @@
"title_aux": "ComfyUI-TrollSuite"
}
],
"https://github.com/phineas-pta/comfy-trt-test": [
"https://github.com/oztrkoguz/ComfyUI_Kosmos2_BBox_Cutter": [
[
"TRT_Lora_Loader",
"TRT_Torch_Compile",
"TRT_Unet_Loader"
"Kosmos2SamplerSimple",
"KosmosLoader",
"Write"
],
{
"author": "PTA",
"description": "attempt to use TensorRT with ComfyUI, not yet compatible with ComfyUI-Manager, see README for instructions",
"nickname": "comfy trt test",
"title": "TensorRT with ComfyUI (work-in-progress)",
"title_aux": "comfy-trt-test [WIP]"
"title_aux": "Kosmos2_BBox_Cutter Models"
}
],
"https://github.com/pamparamm/ComfyUI-ppm": [
[
"AttentionCouplePPM",
"CLIPMicroConditioning",
"CLIPNegPip",
"CLIPTextEncodeBREAK",
"CLIPTokenCounter",
"EmptyLatentImageAR",
"EmptyLatentImageARAdvanced",
"LatentToMaskBB",
"LatentToWidthHeight",
"RandomPromptGenerator",
"StableCascade_AutoCompLatent"
],
{
"title_aux": "ComfyUI-ppm"
}
],
"https://github.com/poisenbery/NudeNet-Detector-Provider": [
@ -1269,6 +1770,51 @@
"title_aux": "prism-tools"
}
],
"https://github.com/redhottensors/ComfyUI-ODE": [
[
"Blended Transition [DVB]",
"Calculation [DVB]",
"Create Frame Set [DVB]",
"Divide [DVB]",
"Fade From Black [DVB]",
"Fade To Black [DVB]",
"Float Input [DVB]",
"For Each Done [DVB]",
"For Each Filename [DVB]",
"Frame Set Append [DVB]",
"Frame Set Frame Dimensions Scaled [DVB]",
"Frame Set Index Offset [DVB]",
"Frame Set Merger [DVB]",
"Frame Set Reindex [DVB]",
"Frame Set Repeat [DVB]",
"Frame Set Reverse [DVB]",
"Frame Set Split Beginning [DVB]",
"Frame Set Split End [DVB]",
"Frame Set Splitter [DVB]",
"Generate Inbetween Frames [DVB]",
"Int Input [DVB]",
"Linear Camera Pan [DVB]",
"Linear Camera Roll [DVB]",
"Linear Camera Zoom [DVB]",
"Load Image From Path [DVB]",
"Multiply [DVB]",
"ODESamplerSelect",
"Sine Camera Pan [DVB]",
"Sine Camera Roll [DVB]",
"Sine Camera Zoom [DVB]",
"String Input [DVB]",
"Text Input [DVB]",
"Trace Memory Allocation [DVB]",
"Unwrap Frame Set [DVB]"
],
{
"author": "RedHotTensors",
"description": "Adaptive ODE Solvers for ComfyUI",
"nickname": "ComfyUI-ODE",
"title": "ComfyUI-ODE",
"title_aux": "ComfyUI-ODE"
}
],
"https://github.com/sdfxai/SDFXBridgeForComfyUI": [
[
"SDFXClipTextEncode"
@ -1277,6 +1823,17 @@
"title_aux": "SDFXBridgeForComfyUI - ComfyUI Custom Node for SDFX Integration"
}
],
"https://github.com/shadowcz007/ComfyUI-PuLID-Test": [
[
"ApplyPulid",
"PulidEvaClipLoader",
"PulidInsightFaceLoader",
"PulidModelLoader"
],
{
"title_aux": "ComfyUI-PuLID [TEST]"
}
],
"https://github.com/shadowcz007/comfyui-CLIPSeg": [
[
"CLIPSeg_",
@ -1288,7 +1845,9 @@
],
"https://github.com/shadowcz007/comfyui-musicgen": [
[
"Musicgen"
"AudioPlay",
"Musicgen_",
"StableAudio_"
],
{
"title_aux": "comfyui-musicgen"
@ -1302,6 +1861,50 @@
"title_aux": "CLIPTextEncodeAndEnhancev4 (shirazdesigner)"
}
],
"https://github.com/sofakid/dandy": [
[
"DandyBooleanCollector",
"DandyBooleanPreview",
"DandyBooleanSplitter",
"DandyCss",
"DandyCssLoader",
"DandyEditorSettings",
"DandyFloatCollector",
"DandyFloatPreview",
"DandyFloatSplitter",
"DandyGradient",
"DandyHtml",
"DandyHtmlLoader",
"DandyImageCollector",
"DandyIntCollector",
"DandyIntPreview",
"DandyIntSplitter",
"DandyJs",
"DandyJsLoader",
"DandyJson",
"DandyJsonLoader",
"DandyLand",
"DandyMaskCollector",
"DandyP5JsDraw",
"DandyP5JsLoader",
"DandyP5JsSetup",
"DandyPixelsJs",
"DandyPixiJs",
"DandyPrompt",
"DandyString",
"DandyStringArrayCollector",
"DandyStringArraySplitter",
"DandyStringCatCollector",
"DandyStringPreview",
"DandyUrlLoader",
"DandyWasmLoader",
"DandyYaml",
"DandyYamlLoader"
],
{
"title_aux": "dandy [UNSAFE]"
}
],
"https://github.com/stutya/ComfyUI-Terminal": [
[
"Terminal"
@ -1327,6 +1930,14 @@
"title_aux": "my-useful-comfyui-custom-nodes"
}
],
"https://github.com/tracerstar/comfyui-p5js-node": [
[
"HYPE_P5JSImage"
],
{
"title_aux": "comfyui-p5js-node"
}
],
"https://github.com/tuckerdarby/ComfyUI-TDNodes": [
[
"HandTrackerNode",
@ -1389,5 +2000,13 @@
{
"title_aux": "comfyui-wormley-nodes"
}
],
"https://github.com/yushan777/ComfyUI-Y7Nodes": [
[
"Count_Tokens_(Y7)"
],
{
"title_aux": "Y7 Nodes for ComfyUI"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,15 @@
{
"custom_nodes": [
{
"author": "meimeilook",
"title": "ComfyUI_IPAdapter_plus.old [backward compatbility]",
"reference": "https://github.com/meimeilook/ComfyUI_IPAdapter_plus.old",
"files": [
"https://github.com/meimeilook/ComfyUI_IPAdapter_plus.old"
],
"install_type": "git-clone",
"description": "This repo is created to provide backward compatibility for workflows configured with the old IPAdapter."
},
{
"author": "ZHO-ZHO-ZHO",
"title": "Dr.Lt.Data/ComfyUI-YoloWorld-EfficientSAM",

View File

@ -10,6 +10,165 @@
},
{
"author": "udi0510",
"title": "comfyui-slicer [REMOVED]",
"id": "slicer",
"reference": "https://github.com/udi0510/comfyui-slicer",
"files": [
"https://github.com/udi0510/comfyui-slicer"
],
"install_type": "git-clone",
"description": "Nodes:SlicerNode"
},
{
"author": "logtd",
"title": "ComfyUI-FLATTEN [REMOVED]",
"id": "flatten",
"reference": "https://github.com/logtd/ComfyUI-FlattenFlow",
"files": [
"https://github.com/logtd/ComfyUI-FlattenFlow"
],
"install_type": "git-clone",
"description": "An alternate trajectory processor for ComfyUI-FLATTEN\nNOTE:When using this trajectory type FLATTEN will use roughly 1/4 VRAM and be ~20% faster at the cost of some consistency (especially when injection_steps are low)."
},
{
"author": "MackinationsAi",
"title": "ComfyUi_Stuctured-Outputs [REMOVED]",
"id": "struct-output",
"reference": "https://github.com/MackinationsAi/ComfyUi_Stuctured-Outputs",
"files": [
"https://github.com/MackinationsAi/ComfyUi_Stuctured-Outputs"
],
"install_type": "git-clone",
"description": "This repository contains a custom node for ComfyUI that allows users to save generative image outputs with custom filenames and folder structures. The filenames are padded to four digits, and the positive and negative prompts are embedded in the image metadata."
},
{
"author": "laksjdjf",
"title": "attention-couple-ComfyUI [DEPRECATED]",
"id": "attention-couple",
"reference": "https://github.com/laksjdjf/attention-couple-ComfyUI",
"files": [
"https://github.com/laksjdjf/attention-couple-ComfyUI"
],
"install_type": "git-clone",
"description": "Nodes:Attention couple. This is a custom node that manipulates region-specific prompts. While vanilla ComfyUI employs an area specification method based on latent couples, this node divides regions using attention layers within UNet.\nNOTE: This has been integrated with cgem156-ComfyUI."
},
{
"author": "phineas-pta",
"title": "comfy-trt-test [DEPRECATED]",
"reference": "https://github.com/phineas-pta/comfy-trt-test",
"files": [
"https://github.com/phineas-pta/comfy-trt-test"
],
"install_type": "git-clone",
"description": "Test project for ComfyUI TensorRT Support.\nNOT WORKING YET.\nnot automatic yet, do not use ComfyUI-Manager to install !!!.\nnot beginner-friendly yet, still intended to technical users\nNOTE: The reason for registration in the Manager is for guidance, and for detailed installation instructions, please visit the repository.\nNOTE: Use 'TensorRT Node for ComfyUI' instead of this."
},
{
"author": "dezi-ai",
"title": "ComfyUI Animate LCM [NOT MAINTAINED]",
"reference": "https://github.com/dezi-ai/ComfyUI-AnimateLCM",
"files": [
"https://github.com/dezi-ai/ComfyUI-AnimateLCM"
],
"install_type": "git-clone",
"description": "ComfyUI implementation for [a/AnimateLCM](https://animatelcm.github.io/) [[a/paper](https://arxiv.org/abs/2402.00769)].\b[w/This extension includes a large number of nodes imported from the existing custom nodes, increasing the likelihood of conflicts.]"
},
{
"author": "christian-byrne",
"title": "elimination-nodes [REMOVED]",
"reference": "https://github.com/christian-byrne/elimination-nodes",
"files": [
"https://github.com/christian-byrne/elimination-nodes"
],
"install_type": "git-clone",
"description": "Nodes:Paste Cutout on Base Image"
},
{
"author": "Levy1417",
"title": "Universal-Data-Processing-Kit [UNSAFE] [REMOVED]",
"reference": "https://github.com/Levy1417/Universal-Data-Processing-Kit",
"files": [
"https://github.com/Levy1417/Universal-Data-Processing-Kit"
],
"install_type": "git-clone",
"description": "Nodes:DPK - Any Eval, DPK - Extract Array, DPK - Run External Program, DPK - Any Literals, DPK - Set Node States, DPK - Realtime Text Preview, DPK - Dynamic Action, DPK - Object To Json, DPK - Json To Object\n[w/This extension includes the ability to execute arbitrary code and programs.]"
},
{
"author": "liusida",
"title": "ComfyUI-Sida-Remove-Image [UNSAFE] [REMOVED]",
"reference": "https://github.com/liusida/ComfyUI-Sida-Remove-Image",
"files": [
"https://github.com/liusida/ComfyUI-Sida-Remove-Image"
],
"install_type": "git-clone",
"description": "Nodes: LoadImageWithPrivacy, RemoveImage.[w/This extension is not secure because it provides the capability to delete files from arbitrary paths.]"
},
{
"author": "88IO",
"title": "ComfyUI Image Reordering Plugins [REMOVED]",
"reference": "https://github.com/88IO/ComfyUI-ImageReorder",
"files": [
"https://github.com/88IO/ComfyUI-ImageReorder"
],
"install_type": "git-clone",
"description": "A custom node reorder multiple image frames based on indexes or curves."
},
{
"author": "jtydhr88",
"title": "ComfyUI-InstantMesh [DEPRECATED]",
"id": "instant-mesh",
"reference": "https://github.com/jtydhr88/ComfyUI-InstantMesh",
"files": [
"https://github.com/jtydhr88/ComfyUI-InstantMesh"
],
"install_type": "git-clone",
"description": "ComfyUI InstantMesh is custom nodes that running TencentARC/InstantMesh into ComfyUI, this extension depends on ComfyUI-3D-Pack. Please refer to Readme carefully to install.\nNOTE: This repo is archived due to ComfyUI-3D-Pack supports InstantMesh, please check 3D-Pack directly if you need it"
},
{
"author": "biegert",
"title": "CLIPSeg [NOT MAINTAINED]",
"id": "clipseg",
"reference": "https://github.com/biegert/ComfyUI-CLIPSeg",
"files": [
"https://github.com/biegert/ComfyUI-CLIPSeg/raw/main/custom_nodes/clipseg.py"
],
"install_type": "copy",
"description": "The CLIPSeg node generates a binary mask for a given input image and text prompt."
},
{
"author": "tankucc1no",
"title": "ComfyUI-Dragdiffusion [REMOVED]",
"id": "dragdiffusion",
"reference": "https://github.com/tankucc1no/ComfyUI-Dragdiffusion",
"files": [
"https://github.com/tankucc1no/ComfyUI-Dragdiffusion"
],
"install_type": "git-clone",
"description": "Implementation of [a/Dragdiffusion](https://github.com/Yujun-Shi/DragDiffusion) in ComfyUI."
},
{
"author": "wibur0620",
"title": "ComfyUI Ollama (wibur) [REMOVED]",
"id": "ollama-wibur",
"reference": "https://github.com/wibur0620/comfyui-ollama-wibur",
"files": [
"https://github.com/wibur0620/comfyui-ollama-wibur"
],
"install_type": "git-clone",
"description": "Custom ComfyUI Nodes for interacting with [a/Ollama](https://ollama.com/) using the ollama python client.\nIntegrate the power of LLMs into ComfyUI workflows easily or just experiment with GPT.\nNOTE: To use this properly, you would need a running Ollama server reachable from the host that is running ComfyUI."
},
{
"author": "IKHOR",
"title": "ikhor-nodes [REMOVED]",
"reference": "https://github.com/IKHOR/ComfyUI-IKHOR-Jam-Nodes",
"files": [
"https://github.com/IKHOR/ComfyUI-IKHOR-Jam-Nodes"
],
"install_type": "git-clone",
"description": "Nodes:LoadFromS3, LoadBatchFromS3, SaveToS3, SaveBatchToS3"
},
{
"author": "kijai",
"title": "ComfyUI wrapper nodes for IC-light [DEPRECATED]",
@ -60,16 +219,6 @@
"install_type": "git-clone",
"description": "Please note that the ImageTextOverlay project is no longer supported and has been moved to a new repository. For ongoing developments, contributions, and issues, please refer to the new repository at: [a/https://github.com/Big-Idea-Technology/ComfyUI-Book-Tools](https://github.com/Big-Idea-Technology/ComfyUI-Book-Tools)"
},
{
"author": "meimeilook",
"title": "ComfyUI_IPAdapter_plus.old [backward compatbility]",
"reference": "https://github.com/meimeilook/ComfyUI_IPAdapter_plus.old",
"files": [
"https://github.com/meimeilook/ComfyUI_IPAdapter_plus.old"
],
"install_type": "git-clone",
"description": "This repo is created to provide backward compatibility for workflows configured with the old IPAdapter."
},
{
"author": "mlinmg",
"title": "LaMa Preprocessor [DEPRECATED]",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,388 @@
{
"models": [
{
"name": "TAESD3 Decoder",
"type": "TAESD",
"base": "SDXL",
"save_path": "vae_approx",
"description": "(SD3 Verison) To view the preview in high quality while running samples in ComfyUI, you will need this model.",
"reference": "https://github.com/madebyollin/taesd",
"filename": "taesd3_decoder.pth",
"url": "https://github.com/madebyollin/taesd/raw/main/taesd3_decoder.pth"
},
{
"name": "TAESD3 Encoder",
"type": "TAESD",
"base": "SDXL",
"save_path": "vae_approx",
"description": "(SD3 Verison) To view the preview in high quality while running samples in ComfyUI, you will need this model.",
"reference": "https://github.com/madebyollin/taesd",
"filename": "taesd3_encoder.pth",
"url": "https://github.com/madebyollin/taesd/raw/main/taesd3_encoder.pth"
},
{
"name": "TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic v2 (rank256)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] Controlnet SDXL Tile model realistic version.",
"reference": "https://huggingface.co/TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic",
"filename": "TTPLANET_Controlnet_Tile_realistic_v2_rank256.safetensors",
"url": "https://huggingface.co/TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic/resolve/main/TTPLANET_Controlnet_Tile_realistic_v2_rank256.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitb/fp16)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[195MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vitb_fp16.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vitb_fp16.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitb/fp32)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[390MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vitb_fp32.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vitb_fp32.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitl/fp16)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[671MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vitl_fp16.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vitl_fp16.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitl/fp32)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[195MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vitl_fp32.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vitl_fp32.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vits/fp16)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[49.6MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vits_fp16.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vits_fp16.safetensors"
},
{
"name": "kijai/DepthAnythingV2 (vitb/fp32)",
"type": "depthanything",
"base": "depthanything",
"save_path": "depthanything",
"description": "[99.2MB] DepthAnythingV2 model",
"reference": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/tree/main",
"filename": "depth_anything_v2_vits_fp32.safetensors",
"url": "https://huggingface.co/Kijai/DepthAnythingV2-safetensors/resolve/main/depth_anything_v2_vits_fp32.safetensors"
},
{
"name": "CN-anytest_v4-marged.safetensors",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[2.5GB] AnyTest Controlnet. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged.safetensors"
},
{
"name": "CN-anytest_v4-marged_am_dim256.safetensors (dim256/Animagine)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] AnyTest Controlnet Lora (dim256) for Animagine. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged_am_dim256.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged_am_dim256.safetensors"
},
{
"name": "CN-anytest_v4-marged_am_dim128.safetensors (dim128/Animagine)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[396MB] AnyTest Controlnet Lora (dim128) for Animagine. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged_am_dim128.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged_am_dim128.safetensors"
},
{
"name": "CN-anytest_v4-marged_pn_dim256.safetensors (dim256/Pony)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] AnyTest Controlnet Lora (dim256) for Pony. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged_pn_dim256.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged_pn_dim256.safetensors"
},
{
"name": "CN-anytest_v4-marged_pn_dim128.safetensors (dim128/Pony)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[396MB] AnyTest Controlnet Lora (dim128) for Pony. A model for style transfer.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v4-marged_pn_dim128.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v4-marged_pn_dim128.safetensors"
},
{
"name": "CN-anytest_v3-50000_fp16.safetensors (fp16)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[2.5GB] AnyTest Controlnet. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_fp16.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_fp16.safetensors"
},
{
"name": "CN-anytest_v3-50000_am_dim256.safetensors (dim256/Animagine)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] AnyTest Controlnet Lora (dim256) for Animagine. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_am_dim256.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_am_dim256.safetensors"
},
{
"name": "CN-anytest_v3-50000_am_dim128.safetensors (dim128/Animagine)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[396MB] AnyTest Controlnet Lora (dim128) for Animagine. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_am_dim128.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_am_dim128.safetensors"
},
{
"name": "CN-anytest_v3-50000_pn_dim256.safetensors (dim256/Pony)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[774MB] AnyTest Controlnet Lora (dim256) for Pony. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_pn_dim256.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_pn_dim256.safetensors"
},
{
"name": "CN-anytest_v3-50000_pn_dim128.safetensors (dim128/Pony)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[396MB] AnyTest Controlnet Lora (dim128) for Pony. A strict control model.",
"reference": "https://huggingface.co/2vXpSwA7/iroiro-lora/tree/main",
"filename": "CN-anytest_v3-50000_pn_dim128.safetensors",
"url": "https://huggingface.co/2vXpSwA7/iroiro-lora/resolve/main/test_controlnet2/CN-anytest_v3-50000_pn_dim128.safetensors"
},
{
"name": "Kijai/ToonCrafter model checkpoint (interpolation fp16)",
"type": "checkpoint",
"base": "ToonCrafter",
"save_path": "checkpoints/ToonCrafter",
"description": "[5.25GB] ToonCrafter checkpoint model for ComfyUI-DynamiCrafterWrapper",
"reference": "https://huggingface.co/Kijai/DynamiCrafter_pruned",
"filename": "tooncrafter_512_interp-fp16.safetensors",
"url": "https://huggingface.co/Kijai/DynamiCrafter_pruned/resolve/main/tooncrafter_512_interp-fp16.safetensors"
},
{
"name": "xinsir/Controlnet-Scribble-Sdxl-1.0",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-scribble-sdxl-1.0",
"description": "[2.5GB] Controlnet SDXL Scribble model.",
"reference": "https://huggingface.co/xinsir/controlnet-scribble-sdxl-1.0",
"filename": "diffusion_pytorch_model.safetensors",
"url": "https://huggingface.co/xinsir/controlnet-scribble-sdxl-1.0/resolve/main/diffusion_pytorch_model.safetensors"
},
{
"name": "xinsir/Controlnet-Canny-Sdxl-1.0 (V2)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-canny-sdxl-1.0",
"description": "[2.5GB] Controlnet SDXL Canny model.",
"reference": "https://huggingface.co/xinsir/controlnet-canny-sdxl-1.0",
"filename": "diffusion_pytorch_model_V2.safetensors",
"url": "https://huggingface.co/xinsir/controlnet-canny-sdxl-1.0/resolve/main/diffusion_pytorch_model_V2.safetensors"
},
{
"name": "xinsir/Controlnet-Openpose-Sdxl-1.0",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-openpose-sdxl-1.0",
"description": "[2.5GB] Controlnet SDXL Openpose model.",
"reference": "https://huggingface.co/xinsir/controlnet-openpose-sdxl-1.0",
"filename": "diffusion_pytorch_model.safetensors",
"url": "https://huggingface.co/xinsir/controlnet-openpose-sdxl-1.0/resolve/main/diffusion_pytorch_model.safetensors"
},
{
"name": "xinsir/Controlnet-Openpose-Sdxl-1.0 (Ver. twins)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-openpose-sdxl-1.0",
"description": "[2.5GB] Controlnet SDXL Openpose model. (Ver. twins)",
"reference": "https://huggingface.co/xinsir/controlnet-openpose-sdxl-1.0",
"filename": "diffusion_pytorch_model_twins.safetensors",
"url": "https://huggingface.co/xinsir/controlnet-openpose-sdxl-1.0/resolve/main/diffusion_pytorch_model_twins.safetensors"
},
{
"name": "xinsir/Controlnet-Scribble-Sdxl-1.0-Anime",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL/controlnet-scribble-sdxl-1.0-anime",
"description": "[2.5GB] Controlnet SDXL Scribble model. (Ver. anime)",
"reference": "https://huggingface.co/xinsir/anime-painter",
"filename": "diffusion_pytorch_model.safetensors",
"url": "https://huggingface.co/xinsir/anime-painter/resolve/main/diffusion_pytorch_model.safetensors"
},
{
"name": "Doubiiu/ToonCrafter model checkpoint",
"type": "checkpoint",
"base": "ToonCrafter",
"save_path": "custom_nodes/ComfyUI-ToonCrafter/ToonCrafter/checkpoints/tooncrafter_512_interp_v1",
"description": "[10.5GB] ToonCrafter checkpoint model for ComfyUI-ToonCrafter",
"reference": "https://huggingface.co/Doubiiu/ToonCrafter/tree/main",
"filename": "model.ckpt",
"url": "https://huggingface.co/Doubiiu/ToonCrafter/resolve/main/model.ckpt"
},
{
"name": "ViperYX/RGT_S_x2.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT_S",
"description": "[135MB] RGT_S x2 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_S_x2.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT_S/RGT_S_x2.pth"
},
{
"name": "ViperYX/RGT_S_x3.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT_S",
"description": "[136MB] RGT_S x3 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_S_x3.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT_S/RGT_S_x3.pth"
},
{
"name": "ViperYX/RGT_S_x4.pth",
"type": "RGT",
"base": "RGT",
"save_path": "RGT/RGT_S",
"description": "[136MB] RGT_S x4 upscale model for ComfyUI-RGT",
"reference": "https://huggingface.co/ViperYX/RGT/tree/main",
"filename": "RGT_S_x4.pth",
"url": "https://huggingface.co/ViperYX/RGT/resolve/main/RGT_S/RGT_S_x4.pth"
},
{
"name": "TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic v2 (fp16)",
"type": "controlnet",
"base": "SDXL",
"save_path": "controlnet/SDXL",
"description": "[2.5GB] Controlnet SDXL Tile model realistic version.",
"reference": "https://huggingface.co/TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic",
"filename": "TTPLANET_Controlnet_Tile_realistic_v2_fp16.safetensors",
"url": "https://huggingface.co/TTPlanet/TTPLanet_SDXL_Controlnet_Tile_Realistic/resolve/main/TTPLANET_Controlnet_Tile_realistic_v2_fp16.safetensors"
},
{
"name": "TencentARC/CustomNet",
"type": "CustomNet",
"base": "CustomNet",
"save_path": "custom_nodes/ComfyUI_CustomNet/pretrain",
"description": "CustomNet pretrained model for ComfyUI_CustomNet",
"reference": "https://huggingface.co/TencentARC/CustomNet/tree/main",
"filename": "customnet_v1.pt",
"url": "https://huggingface.co/TencentARC/CustomNet/resolve/main/customnet_v1.pt"
},
{
"name": "ID-Animator/animator.ckpt",
"type": "ID-Animator",
"base": "SD1.5",
"save_path": "custom_nodes/ComfyUI_ID_Animator/models",
"description": "ID-Animator checkpoint",
"reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator",
"filename": "animator.ckpt",
"url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/animator.ckpt"
},
{
"name": "ID-Animator/mm_sd_v15_v2.ckpt",
"type": "ID-Animator",
"base": "SD1.5",
"save_path": "custom_nodes/ComfyUI_ID_Animator/models/animatediff_models",
"description": "AnimateDiff checkpoint for ID-Animator",
"reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator",
"filename": "mm_sd_v15_v2.ckpt",
"url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/mm_sd_v15_v2.ckpt"
},
{
"name": "ID-Animator/image_encoder",
"type": "ID-Animator",
"base": "SD1.5",
"save_path": "custom_nodes/ComfyUI_ID_Animator/models/image_encoder",
"description": "CLIP Image encoder for ID-Animator",
"reference": "https://huggingface.co/spaces/ID-Animator/ID-Animator",
"filename": "model.safetensors",
"url": "https://huggingface.co/spaces/ID-Animator/ID-Animator/resolve/main/image_encoder/model.safetensors"
},
{
"name": "IC-Light/fc",
"type": "IC-Light",
"base": "SD1.5",
"save_path": "unet/IC-Light",
"description": "The default relighting model, conditioned on text and foreground",
"reference": "https://huggingface.co/lllyasviel/ic-light",
"filename": "iclight_sd15_fc.safetensors",
"url": "https://huggingface.co/lllyasviel/ic-light/resolve/main/iclight_sd15_fc.safetensors"
},
{
"name": "IC-Light/fbc",
"type": "IC-Light",
"base": "SD1.5",
"save_path": "unet/IC-Light",
"description": "Relighting model conditioned with text, foreground, and background",
"reference": "https://huggingface.co/lllyasviel/ic-light",
"filename": "iclight_sd15_fbc.safetensors",
"url": "https://huggingface.co/lllyasviel/ic-light/resolve/main/iclight_sd15_fbc.safetensors"
},
{
"name": "IC-Light/fcon",
"type": "IC-Light",
"base": "SD1.5",
"save_path": "unet/IC-Light",
"description": "Same as iclight_sd15_fc.safetensors, but trained with offset noise",
"reference": "https://huggingface.co/lllyasviel/ic-light",
"filename": "iclight_sd15_fcon.safetensors",
"url": "https://huggingface.co/lllyasviel/ic-light/resolve/main/iclight_sd15_fcon.safetensors"
},
{
"name": "MonsterMMORPG/insightface (for InstantID)",
"type": "insightface",
@ -293,414 +676,6 @@
"reference": "https://huggingface.co/camenduru/YoloWorld-EfficientSAM/tree/main",
"filename": "efficient_sam_s_gpu.jit",
"url": "https://huggingface.co/camenduru/YoloWorld-EfficientSAM/resolve/main/efficient_sam_s_gpu.jit"
},
{
"name": "stabilityai/comfyui_checkpoints/stable_cascade_stage_b.safetensors",
"type": "checkpoints",
"base": "Stable Cascade",
"save_path": "checkpoints/Stable-Cascade",
"description": "[4.55GB] Stable Cascade stage_b checkpoints",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stable_cascade_stage_b.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/comfyui_checkpoints/stable_cascade_stage_b.safetensors"
},
{
"name": "stabilityai/comfyui_checkpoints/stable_cascade_stage_c.safetensors",
"type": "checkpoints",
"base": "Stable Cascade",
"save_path": "checkpoints/Stable-Cascade",
"description": "[9.22GB] Stable Cascade stage_c checkpoints",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stable_cascade_stage_c.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/comfyui_checkpoints/stable_cascade_stage_c.safetensors"
},
{
"name": "stabilityai/Stable Cascade: effnet_encoder.safetensors (VAE)",
"type": "VAE",
"base": "Stable Cascade",
"save_path": "vae/Stable-Cascade",
"description": "[81.5MB] Stable Cascade: effnet_encoder.\nVAE encoder for stage_c latent.",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "effnet_encoder.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/effnet_encoder.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_a.safetensors (VAE)",
"type": "VAE",
"base": "Stable Cascade",
"save_path": "vae/Stable-Cascade",
"description": "[73.7MB] Stable Cascade: stage_a",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_a.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_a.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_b.safetensors (UNET)",
"type": "unet",
"base": "Stable Cascade",
"save_path": "unet/Stable-Cascade",
"description": "[6.25GB] Stable Cascade: stage_b",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_b.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_b.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_b_bf16.safetensors (UNET)",
"type": "unet",
"base": "Stable Cascade",
"save_path": "unet/Stable-Cascade",
"description": "[3.13GB] Stable Cascade: stage_b/bf16",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_b_bf16.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_b_bf16.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_b_lite.safetensors (UNET)",
"type": "unet",
"base": "Stable Cascade",
"save_path": "unet/Stable-Cascade",
"description": "[2.8GB] Stable Cascade: stage_b/lite",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_b_lite.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_b_lite.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_b_lite.safetensors (UNET)",
"type": "unet",
"base": "Stable Cascade",
"save_path": "unet/Stable-Cascade",
"description": "[1.4GB] Stable Cascade: stage_b/bf16,lite",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_b_lite_bf16.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_b_lite_bf16.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_c.safetensors (UNET)",
"type": "unet",
"base": "Stable Cascade",
"save_path": "unet/Stable-Cascade",
"description": "[14.4GB] Stable Cascade: stage_c",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_c.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_c.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_c_bf16.safetensors (UNET)",
"type": "unet",
"base": "Stable Cascade",
"save_path": "unet/Stable-Cascade",
"description": "[7.18GB] Stable Cascade: stage_c/bf16",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_c_bf16.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_c_bf16.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_c_lite.safetensors (UNET)",
"type": "unet",
"base": "Stable Cascade",
"save_path": "unet/Stable-Cascade",
"description": "[4.12GB] Stable Cascade: stage_c/lite",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_c_lite.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_c_lite.safetensors"
},
{
"name": "stabilityai/Stable Cascade: stage_c_lite.safetensors (UNET)",
"type": "unet",
"base": "Stable Cascade",
"save_path": "unet/Stable-Cascade",
"description": "[2.06GB] Stable Cascade: stage_c/bf16,lite",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "stage_c_lite_bf16.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/stage_c_lite_bf16.safetensors"
},
{
"name": "stabilityai/Stable Cascade: text_encoder (CLIP)",
"type": "clip",
"base": "Stable Cascade",
"save_path": "clip/Stable-Cascade",
"description": "[1.39GB] Stable Cascade: text_encoder",
"reference": "https://huggingface.co/stabilityai/stable-cascade",
"filename": "model.safetensors",
"url": "https://huggingface.co/stabilityai/stable-cascade/resolve/main/text_encoder/model.safetensors"
},
{
"name": "1k3d68.onnx",
"type": "insightface",
"base": "inswapper",
"save_path": "insightface/models/antelopev2",
"description": "Antelopev2 1k3d68.onnx model for InstantId. (InstantId needs all Antelopev2 models)",
"reference": "https://github.com/cubiq/ComfyUI_InstantID#installation",
"filename": "1k3d68.onnx",
"url": "https://huggingface.co/MonsterMMORPG/tools/resolve/main/1k3d68.onnx"
},
{
"name": "2d106det.onnx",
"type": "insightface",
"base": "inswapper",
"save_path": "insightface/models/antelopev2",
"description": "Antelopev2 2d106det.onnx model for InstantId. (InstantId needs all Antelopev2 models)",
"reference": "https://github.com/cubiq/ComfyUI_InstantID#installation",
"filename": "2d106det.onnx",
"url": "https://huggingface.co/MonsterMMORPG/tools/resolve/main/2d106det.onnx"
},
{
"name": "genderage.onnx",
"type": "insightface",
"base": "inswapper",
"save_path": "insightface/models/antelopev2",
"description": "Antelopev2 genderage.onnx model for InstantId. (InstantId needs all Antelopev2 models)",
"reference": "https://github.com/cubiq/ComfyUI_InstantID#installation",
"filename": "genderage.onnx",
"url": "https://huggingface.co/MonsterMMORPG/tools/resolve/main/genderage.onnx"
},
{
"name": "glintr100.onnx",
"type": "insightface",
"base": "inswapper",
"save_path": "insightface/models/antelopev2",
"description": "Antelopev2 glintr100.onnx model for InstantId. (InstantId needs all Antelopev2 models)",
"reference": "https://github.com/cubiq/ComfyUI_InstantID#installation",
"filename": "glintr100.onnx",
"url": "https://huggingface.co/MonsterMMORPG/tools/resolve/main/glintr100.onnx"
},
{
"name": "scrfd_10g_bnkps.onnx",
"type": "insightface",
"base": "inswapper",
"save_path": "insightface/models/antelopev2",
"description": "Antelopev2 scrfd_10g_bnkps.onnx model for InstantId. (InstantId needs all Antelopev2 models)",
"reference": "https://github.com/cubiq/ComfyUI_InstantID#installation",
"filename": "scrfd_10g_bnkps.onnx",
"url": "https://huggingface.co/MonsterMMORPG/tools/resolve/main/scrfd_10g_bnkps.onnx"
},
{
"name": "photomaker-v1.bin",
"type": "photomaker",
"base": "SDXL",
"save_path": "photomaker",
"description": "PhotoMaker model. This model is compatible with SDXL.",
"reference": "https://huggingface.co/TencentARC/PhotoMaker",
"filename": "photomaker-v1.bin",
"url": "https://huggingface.co/TencentARC/PhotoMaker/resolve/main/photomaker-v1.bin"
},
{
"name": "ip-adapter-faceid_sdxl.bin",
"type": "IP-Adapter",
"base": "SD1.5",
"save_path": "ipadapter",
"description": "IP-Adapter-FaceID Model (SDXL) [ipadapter]",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid_sdxl.bin",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sdxl.bin"
},
{
"name": "ip-adapter-faceid-plusv2_sdxl.bin",
"type": "IP-Adapter",
"base": "SD1.5",
"save_path": "ipadapter",
"description": "IP-Adapter-FaceID Plus Model (SDXL) [ipadapter]",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid-plusv2_sdxl.bin",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sdxl.bin"
},
{
"name": "ip-adapter-faceid_sdxl_lora.safetensors",
"type": "lora",
"base": "SDXL",
"save_path": "loras/ipadapter",
"description": "IP-Adapter-FaceID LoRA Model (SDXL) [ipadapter]",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid_sdxl_lora.safetensors",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sdxl_lora.safetensors"
},
{
"name": "ip-adapter-faceid-plusv2_sdxl_lora.safetensors",
"type": "lora",
"base": "SDXL",
"save_path": "loras/ipadapter",
"description": "IP-Adapter-FaceID-Plus V2 LoRA Model (SDXL) [ipadapter]",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid-plusv2_sdxl_lora.safetensors",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sdxl_lora.safetensors"
},
{
"name": "TencentARC/motionctrl.pth",
"type": "checkpoints",
"base": "MotionCtrl",
"save_path": "checkpoints/motionctrl",
"description": "To use the ComfyUI-MotionCtrl extension, downloading this model is required.",
"reference": "https://huggingface.co/TencentARC/MotionCtrl",
"filename": "motionctrl.pth",
"url": "https://huggingface.co/TencentARC/MotionCtrl/resolve/main/motionctrl.pth"
},
{
"name": "ip-adapter-faceid-plusv2_sd15.bin",
"type": "IP-Adapter",
"base": "SD1.5",
"save_path": "ipadapter",
"description": "IP-Adapter-FaceID-Plus V2 Model (SD1.5)",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid-plusv2_sd15.bin",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sd15.bin"
},
{
"name": "ip-adapter-faceid-plusv2_sd15_lora.safetensors",
"type": "lora",
"base": "SD1.5",
"save_path": "loras/ipadapter",
"description": "IP-Adapter-FaceID-Plus V2 LoRA Model (SD1.5)",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid-plusv2_sd15_lora.safetensors",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sd15_lora.safetensors"
},
{
"name": "ip-adapter-faceid-plus_sd15_lora.safetensors",
"type": "lora",
"base": "SD1.5",
"save_path": "loras/ipadapter",
"description": "IP-Adapter-FaceID Plus LoRA Model (SD1.5) [ipadapter]",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid-plus_sd15_lora.safetensors",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plus_sd15_lora.safetensors"
},
{
"name": "ControlNet-HandRefiner-pruned (inpaint-depth-hand; fp16)",
"type": "controlnet",
"base": "SD1.5",
"save_path": "default",
"description": "This inpaint-depth controlnet model is specialized for the hand refiner.",
"reference": "https://huggingface.co/hr16/ControlNet-HandRefiner-pruned",
"filename": "control_sd15_inpaint_depth_hand_fp16.safetensors",
"url": "https://huggingface.co/hr16/ControlNet-HandRefiner-pruned/resolve/main/control_sd15_inpaint_depth_hand_fp16.safetensors"
},
{
"name": "stabilityai/stable-diffusion-x4-upscaler",
"type": "checkpoints",
"base": "upscale",
"save_path": "checkpoints/upscale",
"description": "[3.53GB] This upscaling model is a latent text-guided diffusion model and should be used with SD_4XUpscale_Conditioning and KSampler.",
"reference": "https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler",
"filename": "x4-upscaler-ema.safetensors",
"url": "https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler/resolve/main/x4-upscaler-ema.safetensors"
},
{
"name": "LDSR(Latent Diffusion Super Resolution)",
"type": "upscale",
"base": "upscale",
"save_path": "upscale_models/ldsr",
"description": "LDSR upscale model. Through the [a/ComfyUI-Flowty-LDSR](https://github.com/flowtyone/ComfyUI-Flowty-LDSR) extension, the upscale model can be utilized.",
"reference": "https://github.com/CompVis/latent-diffusion",
"filename": "last.ckpt",
"url": "https://heibox.uni-heidelberg.de/f/578df07c8fc04ffbadf3/?dl=1"
},
{
"name": "control_boxdepth_LooseControlfp16 (fp16)",
"type": "controlnet",
"base": "SD1.5",
"save_path": "default",
"description": "Loose ControlNet model",
"reference": "https://huggingface.co/ioclab/LooseControl_WebUICombine",
"filename": "control_boxdepth_LooseControlfp16.safetensors",
"url": "https://huggingface.co/ioclab/LooseControl_WebUICombine/resolve/main/control_boxdepth_LooseControlfp16.safetensors"
},
{
"name": "ip-adapter-faceid-portrait_sd15.bin",
"type": "IP-Adapter",
"base": "SD1.5",
"save_path": "ipadapter",
"description": "IP-Adapter-FaceID Portrait Model (SD1.5) [ipadapter]",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid-portrait_sd15.bin",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-portrait_sd15.bin"
},
{
"name": "ip-adapter-faceid-plus_sd15.bin",
"type": "IP-Adapter",
"base": "SD1.5",
"save_path": "ipadapter",
"description": "IP-Adapter-FaceID Plus Model (SD1.5) [ipadapter]",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid-plus_sd15.bin",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plus_sd15.bin"
},
{
"name": "ip-adapter-faceid_sd15.bin",
"type": "IP-Adapter",
"base": "SD1.5",
"save_path": "ipadapter",
"description": "IP-Adapter-FaceID Model (SD1.5)",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid_sd15.bin",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sd15.bin"
},
{
"name": "ip-adapter-faceid_sd15_lora.safetensors",
"type": "lora",
"base": "SD1.5",
"save_path": "loras/ipadapter",
"description": "IP-Adapter-FaceID LoRA Model (SD1.5)",
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
"filename": "ip-adapter-faceid_sd15_lora.safetensors",
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sd15_lora.safetensors"
},
{
"name": "LongAnimatediff/lt_long_mm_16_64_frames_v1.1.ckpt (ComfyUI-AnimateDiff-Evolved) (Updated path)",
"type": "animatediff",
"base": "SD1.x",
"save_path": "animatediff_models",
"description": "Pressing 'install' directly downloads the model from the Kosinkadink/ComfyUI-AnimateDiff-Evolved extension node.",
"reference": "https://huggingface.co/Lightricks/LongAnimateDiff",
"filename": "lt_long_mm_16_64_frames_v1.1.ckpt",
"url": "https://huggingface.co/Lightricks/LongAnimateDiff/resolve/main/lt_long_mm_16_64_frames_v1.1.ckpt"
},
{
"name": "animatediff/v3_sd15_sparsectrl_rgb.ckpt (ComfyUI-AnimateDiff-Evolved)",
"type": "controlnet",
"base": "SD1.x",
"save_path": "controlnet/SD1.5/animatediff",
"description": "AnimateDiff SparseCtrl RGB ControlNet model",
"reference": "https://huggingface.co/guoyww/animatediff",
"filename": "v3_sd15_sparsectrl_rgb.ckpt",
"url": "https://huggingface.co/guoyww/animatediff/resolve/main/v3_sd15_sparsectrl_rgb.ckpt"
},
{
"name": "animatediff/v3_sd15_sparsectrl_scribble.ckpt",
"type": "controlnet",
"base": "SD1.x",
"save_path": "controlnet/SD1.5/animatediff",
"description": "AnimateDiff SparseCtrl Scribble ControlNet model",
"reference": "https://huggingface.co/guoyww/animatediff",
"filename": "v3_sd15_sparsectrl_scribble.ckpt",
"url": "https://huggingface.co/guoyww/animatediff/resolve/main/v3_sd15_sparsectrl_scribble.ckpt"
},
{
"name": "animatediff/v3_sd15_mm.ckpt (ComfyUI-AnimateDiff-Evolved)",
"type": "animatediff",
"base": "SD1.x",
"save_path": "custom_nodes/ComfyUI-AnimateDiff-Evolved/models",
"description": "Pressing 'install' directly downloads the model from the Kosinkadink/ComfyUI-AnimateDiff-Evolved extension node. (Note: Requires ComfyUI-Manager V0.24 or above)",
"reference": "https://huggingface.co/guoyww/animatediff",
"filename": "v3_sd15_mm.ckpt",
"url": "https://huggingface.co/guoyww/animatediff/resolve/main/v3_sd15_mm.ckpt"
},
{
"name": "animatediff/v3_sd15_adapter.ckpt",
"type": "lora",
"base": "SD1.x",
"save_path": "loras/SD1.5/animatediff",
"description": "AnimateDiff Adapter LoRA (SD1.5)",
"reference": "https://huggingface.co/guoyww/animatediff",
"filename": "v3_sd15_adapter.ckpt",
"url": "https://huggingface.co/guoyww/animatediff/resolve/main/v3_sd15_adapter.ckpt"
}
]
}

View File

@ -80,16 +80,6 @@
"install_type": "git-clone",
"description": "Tutorial nodes"
},
{
"author": "GraftingRayman",
"title": "ComfyUI-Trajectory",
"reference": "https://github.com/GraftingRayman/ComfyUI-Trajectory",
"files": [
"https://github.com/GraftingRayman/ComfyUI-Trajectory"
],
"install_type": "git-clone",
"description": "Nodes:GR Trajectory"
},
{
"author": "wailovet",
"title": "ComfyUI-WW",
@ -188,17 +178,37 @@
"https://github.com/nilor-corp/nilor-nodes"
],
"install_type": "git-clone",
"description": "Custom utility nodes for ComfyUI"
"description": "Nodes:Nilor Floats, Nilor Int To List Of Bools, Nilor Bool From List Of Bools, Nilor Int From List Of Ints, Nilor List of Ints, Nilor Count Images In Directory"
},
{
"author": "gonzalu",
"title": "ComfyUI YouFunnyGuys Comical Custom Nodes",
"reference": "https://github.com/gonzalu/ComfyUI_YFG_Comical",
"author": "OuticNZ",
"title": "ComfyUI-Simple-Of-Complex",
"reference": "https://github.com/OuticNZ/ComfyUI-Simple-Of-Complex",
"files": [
"https://github.com/gonzalu/ComfyUI_YFG_Comical"
"https://github.com/OuticNZ/ComfyUI-Simple-Of-Complex"
],
"install_type": "git-clone",
"description": "This is a repository of ComfyUI custom nodes. I was looking for a node that provided some functionality and did not find one. So I decided to create one myself in order to have the functionality I desired as well as way to teach myself how to code."
"description": "Keeping it simple for starting. Single branch for now and will add development branch later."
},
{
"author": "jtong",
"title": "comfyui-jtong-workflow",
"reference": "https://github.com/jtong/comfyui-jtong-workflow",
"files": [
"https://github.com/jtong/comfyui-jtong-workflow"
],
"install_type": "git-clone",
"description": "Nodes:jtong.Highway, Example"
},
{
"author": "thinkthinking",
"title": "ComfyUI-Ye",
"reference": "https://github.com/thinkthinking/ComfyUI-Ye",
"files": [
"https://github.com/thinkthinking/ComfyUI-Ye"
],
"install_type": "git-clone",
"description": "Nodes:Signature|Ye, CheckpointLoader|Ye, PrintHelloWorld|Ye."
}
]
}

View File

@ -67,8 +67,9 @@
"!echo -= Install dependencies =-\n",
"!pip3 install accelerate\n",
"!pip3 install einops transformers>=4.25.1 safetensors>=0.3.0 aiohttp pyyaml Pillow scipy tqdm psutil\n",
"!pip3 install xformers!=0.0.18 torch==2.1.0 torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121\n",
"!pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121\n",
"!pip3 install torchsde\n",
"!pip3 install kornia>=0.7.1 spandrel\n",
"\n",
"if OPTIONS['USE_COMFYUI_MANAGER']:\n",
" %cd custom_nodes\n",

View File

@ -8,14 +8,16 @@ import re
import locale
import platform
import json
import ast
glob_path = os.path.join(os.path.dirname(__file__), "glob")
sys.path.append(glob_path)
import security_check
from manager_util import *
import cm_global
security_check.security_check()
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
@ -212,6 +214,9 @@ try:
# Handle error
raise ValueError("The object does not have a fileno method")
def isatty(self):
return False
def write(self, message):
global is_start_mode
@ -427,7 +432,20 @@ def is_installed(name):
print(f"[ComfyUI-Manager] skip black listed pip installation: '{name}'")
return True
return name.lower() in get_installed_packages()
pkg = get_installed_packages().get(name.lower())
if pkg is None:
return False # update if not installed
if match is None:
return True # don't update if version is not specified
if match.group(2) in ['>', '>=']:
if StrictVersion(pkg) < StrictVersion(match.group(3)):
return False
elif StrictVersion(pkg) > StrictVersion(match.group(3)):
print(f"[SKIP] Downgrading pip package isn't allowed: {name.lower()} (cur={pkg})")
return True # prevent downgrade
if os.path.exists(restore_snapshot_path):
@ -476,8 +494,9 @@ if os.path.exists(restore_snapshot_path):
for line in file:
package_name = remap_pip_package(line.strip())
if package_name and not is_installed(package_name):
install_cmd = [sys.executable, "-m", "pip", "install", package_name]
this_exit_code += process_wrap(install_cmd, repo_path)
if not package_name.startswith('#'):
install_cmd = [sys.executable, "-m", "pip", "install", package_name]
this_exit_code += process_wrap(install_cmd, repo_path)
if os.path.exists(install_script_path) and f'{repo_path}/install.py' not in processed_install:
processed_install.add(f'{repo_path}/install.py')
@ -541,7 +560,7 @@ if os.path.exists(script_list_path):
executed.add(line)
try:
script = eval(line)
script = ast.literal_eval(line)
if script[1].startswith('#') and script[1] != '#FORCE':
if script[1] == "#LAZY-INSTALL-SCRIPT":

15
pyproject.toml Normal file
View File

@ -0,0 +1,15 @@
[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 = "2.38.2"
license = "LICENSE"
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions"]
[project.urls]
Repository = "https://github.com/ltdrdata/ComfyUI-Manager"
# Used by Comfy Registry https://comfyregistry.org
[tool.comfy]
PublisherId = "drltdata"
DisplayName = "ComfyUI-Manager"
Icon = ""

View File

@ -2,4 +2,7 @@ GitPython
PyGithub
matrix-client==0.4.0
transformers
huggingface-hub>0.20
huggingface-hub>0.20
typer
rich
typing-extensions

View File

@ -3,9 +3,10 @@ import re
import os
import json
from git import Repo
from torchvision.datasets.utils import download_url
import concurrent
import datetime
import concurrent.futures
import requests
builtin_nodes = set()
@ -15,6 +16,29 @@ from urllib.parse import urlparse
from github import Github
def download_url(url, dest_folder, filename=None):
# Ensure the destination folder exists
if not os.path.exists(dest_folder):
os.makedirs(dest_folder)
# Extract filename from URL if not provided
if filename is None:
filename = os.path.basename(url)
# Full path to save the file
dest_path = os.path.join(dest_folder, filename)
# Download the file
response = requests.get(url, stream=True)
if response.status_code == 200:
with open(dest_path, 'wb') as file:
for chunk in response.iter_content(chunk_size=1024):
if chunk:
file.write(chunk)
else:
raise Exception(f"Failed to download file from {url}")
# prepare temp dir
if len(sys.argv) > 1:
temp_dir = sys.argv[1]
@ -37,12 +61,22 @@ else:
print(f"TEMP DIR: {temp_dir}")
parse_cnt = 0
def extract_nodes(code_text):
global parse_cnt
try:
if parse_cnt % 100 == 0:
print(f".", end="", flush=True)
parse_cnt += 1
code_text = re.sub(r'\\[^"\']', '', code_text)
parsed_code = ast.parse(code_text)
assignments = (node for node in parsed_code.body if isinstance(node, ast.Assign))
for assignment in assignments:
if isinstance(assignment.targets[0], ast.Name) and assignment.targets[0].id in ['NODE_CONFIG', 'NODE_CLASS_MAPPINGS']:
node_class_mappings = assignment.value
@ -51,7 +85,12 @@ def extract_nodes(code_text):
node_class_mappings = None
if node_class_mappings:
s = set([key.s.strip() for key in node_class_mappings.keys if key is not None])
s = set()
for key in node_class_mappings.keys:
if key is not None and isinstance(key.value, str):
s.add(key.value.strip())
return s
else:
return set()
@ -77,21 +116,26 @@ def scan_in_file(filename, is_builtin=False):
class_dict = {}
nodes |= extract_nodes(code)
code = re.sub(r'^#.*?$', '', code, flags=re.MULTILINE)
pattern2 = r'^[^=]*_CLASS_MAPPINGS\["(.*?)"\]'
keys = re.findall(pattern2, code)
for key in keys:
nodes.add(key.strip())
def extract_keys(pattern, code):
keys = re.findall(pattern, code)
return {key.strip() for key in keys}
pattern3 = r'^[^=]*_CLASS_MAPPINGS\[\'(.*?)\'\]'
keys = re.findall(pattern3, code)
for key in keys:
nodes.add(key.strip())
def update_nodes(nodes, new_keys):
nodes |= new_keys
pattern4 = r'@register_node\("(.+)",\s*\".+"\)'
keys = re.findall(pattern4, code)
for key in keys:
nodes.add(key.strip())
patterns = [
r'^[^=]*_CLASS_MAPPINGS\["(.*?)"\]',
r'^[^=]*_CLASS_MAPPINGS\[\'(.*?)\'\]',
r'@register_node\("(.+)",\s*\".+"\)',
r'"(\w+)"\s*:\s*{"class":\s*\w+\s*'
]
with concurrent.futures.ThreadPoolExecutor() as executor:
futures = {executor.submit(extract_keys, pattern, code): pattern for pattern in patterns}
for future in concurrent.futures.as_completed(futures):
update_nodes(nodes, future.result())
matches = regex.findall(code)
for match in matches:
@ -259,6 +303,9 @@ def update_custom_nodes():
if is_rate_limit_exceeded():
return
if 'github.com' not in url:
return None
print('.', end="")
sys.stdout.flush()
try:
@ -270,24 +317,37 @@ def update_custom_nodes():
if len(path_parts) >= 2 and domain == "github.com":
owner_repo = "/".join(path_parts[-2:])
repo = g.get_repo(owner_repo)
owner = repo.owner
now = datetime.datetime.now(datetime.timezone.utc)
author_time_diff = now - owner.created_at
last_update = repo.pushed_at.strftime("%Y-%m-%d %H:%M:%S") if repo.pushed_at else 'N/A'
github_stats[url] = {
item = {
"stars": repo.stargazers_count,
"last_update": last_update,
"cached_time": datetime.datetime.now().timestamp(),
"cached_time": now.timestamp(),
"author_account_age_days": author_time_diff.days,
}
with open(GITHUB_STATS_CACHE_FILENAME, 'w', encoding='utf-8') as file:
json.dump(github_stats, file, ensure_ascii=False, indent=4)
return url, item
else:
print(f"\nInvalid URL format for GitHub repository: {url}\n")
except Exception as e:
print(f"\nERROR on {url}\n{e}")
return None
# resolve unresolved urls
for url, title, preemptions, node_pattern in git_url_titles_preemptions:
if url not in github_stats:
renew_stat(url)
with concurrent.futures.ThreadPoolExecutor(11) as executor:
futures = []
for url, title, preemptions, node_pattern in git_url_titles_preemptions:
if url not in github_stats:
futures.append(executor.submit(renew_stat, url))
for future in concurrent.futures.as_completed(futures):
url_item = future.result()
if url_item is not None:
url, item = url_item
github_stats[url] = item
# renew outdated cache
outdated_urls = []
@ -296,22 +356,34 @@ def update_custom_nodes():
if elapsed > 60*60*12: # 12 hours
outdated_urls.append(k)
for url in outdated_urls:
renew_stat(url)
with concurrent.futures.ThreadPoolExecutor(11) as executor:
for url in outdated_urls:
futures.append(executor.submit(renew_stat, url))
for future in concurrent.futures.as_completed(futures):
url_item = future.result()
if url_item is not None:
url, item = url_item
github_stats[url] = item
with open('github-stats-cache.json', 'w', encoding='utf-8') as file:
json.dump(github_stats, file, ensure_ascii=False, indent=4)
with open(GITHUB_STATS_FILENAME, 'w', encoding='utf-8') as file:
for v in github_stats.values():
if "cached_time" in v:
del v["cached_time"]
github_stats = dict(sorted(github_stats.items()))
json.dump(github_stats, file, ensure_ascii=False, indent=4)
print(f"Successfully written to {GITHUB_STATS_FILENAME}.")
with concurrent.futures.ThreadPoolExecutor(11) as executor:
if not skip_stat_update:
executor.submit(process_git_stats, git_url_titles_preemptions) # One single thread for `process_git_stats()`. Runs concurrently with `process_git_url_title()`.
if not skip_stat_update:
process_git_stats(git_url_titles_preemptions)
with concurrent.futures.ThreadPoolExecutor(11) as executor:
for url, title, preemptions, node_pattern in git_url_titles_preemptions:
executor.submit(process_git_url_title, url, title, preemptions, node_pattern)
@ -409,7 +481,13 @@ def gen_json(node_info):
git_url, title, preemptions, node_pattern = node_info[extension]
with open(node_list_json_path, 'r', encoding='utf-8') as f:
node_list_json = json.load(f)
try:
node_list_json = json.load(f)
except Exception as e:
print(f"\nERROR: Invalid json format '{node_list_json_path}'")
print("------------------------------------------------------")
print(e)
print("------------------------------------------------------")
metadata_in_url = {}
if git_url not in data:
@ -446,3 +524,4 @@ updated_node_info = update_custom_nodes()
print("\n# 'extension-node-map.json' file is generated.\n")
gen_json(updated_node_info)
print("\nDONE.\n")