Merge remote-tracking branch 'upstream/main'
21
.github/workflows/publish.yml
vendored
Normal 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 }}
|
||||
37
README.md
@ -5,6 +5,8 @@
|
||||

|
||||
|
||||
## 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
|
||||
|
||||

|
||||

|
||||
|
||||
* 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.
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
@ -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)"
|
||||
}
|
||||
]
|
||||
}
|
||||
14
check.sh
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
7983
github-stats.json
@ -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
|
||||
|
||||
|
||||
@ -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> ()"
|
||||
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
@ -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")
|
||||
@ -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 = ' ID ';
|
||||
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 = ` ${data.tags}`;
|
||||
var data3 = document.createElement('td');
|
||||
var data4 = document.createElement('td');
|
||||
if(data.custom_node) {
|
||||
data3.innerHTML = ` ${data.custom_node.author}`;
|
||||
data4.innerHTML = ` <a href=${data.custom_node.reference} target="_blank"><font color="skyblue"><b>${data.custom_node.title}</b></font></a>`;
|
||||
}
|
||||
else {
|
||||
data3.innerHTML = ` Unknown`;
|
||||
data4.innerHTML = ` 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
js/cm-api.js
@ -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);
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}),
|
||||
|
||||
|
||||
97
js/common.js
@ -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;
|
||||
}
|
||||
@ -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 = ' ID ';
|
||||
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 = ' ★ ';
|
||||
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 = ` <a href=${data.reference} target="_blank"><font color="skyblue"><b>${data.title}</b></font></a>`;
|
||||
if(data.installed == 'Fail')
|
||||
data3.innerHTML = ' <font color="BLACK"><B>(IMPORT FAILED)</B></font>' + data3.innerHTML;
|
||||
|
||||
var data4 = document.createElement('td');
|
||||
data4.innerHTML = data.description;
|
||||
data4.className = "cm-node-desc"
|
||||
|
||||
let conflicts = this.conflict_mappings[data.files[0]];
|
||||
if(conflicts) {
|
||||
let buf = '<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
@ -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) {
|
||||
|
||||
@ -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
|
Before Width: | Height: | Size: 193 KiB After Width: | Height: | Size: 160 KiB |
BIN
misc/main.png
|
Before Width: | Height: | Size: 28 KiB |
BIN
misc/menu.jpg
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 91 KiB |
BIN
misc/missing-list.jpg
Normal file
|
After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 198 KiB |
BIN
misc/missing-menu.jpg
Normal file
|
After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 165 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 29 KiB |
392
model-list.json
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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",
|
||||
|
||||
@ -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]",
|
||||
|
||||
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@ -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."
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -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",
|
||||
|
||||
@ -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
@ -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 = ""
|
||||
@ -2,4 +2,7 @@ GitPython
|
||||
PyGithub
|
||||
matrix-client==0.4.0
|
||||
transformers
|
||||
huggingface-hub>0.20
|
||||
huggingface-hub>0.20
|
||||
typer
|
||||
rich
|
||||
typing-extensions
|
||||
139
scanner.py
@ -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")
|
||||