Compare commits
206 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d7777fb0b | ||
|
|
ffc095a3e5 | ||
|
|
8db932afd9 | ||
|
|
45c2cfd92e | ||
|
|
8bb5eecf0d | ||
|
|
5dc4cf7206 | ||
|
|
e73d66d65a | ||
|
|
40b2050e71 | ||
|
|
0cc279b109 | ||
|
|
86e13b6ee1 | ||
|
|
131d2dae3c | ||
|
|
de3cd9fe72 | ||
|
|
b8389e81a1 | ||
|
|
3e4ea1662c | ||
|
|
bc63b2cd3f | ||
|
|
3701246fb1 | ||
|
|
0e4b47c13d | ||
|
|
79b4136403 | ||
|
|
91425aea62 | ||
|
|
6d61491a5b | ||
|
|
efbb251635 | ||
|
|
18b66c7835 | ||
|
|
edb77c24ad | ||
|
|
4e01e70ef5 | ||
|
|
0e16c0cb24 | ||
|
|
7e777c5460 | ||
|
|
07402c7a90 | ||
|
|
1c19fa9e38 | ||
|
|
8c799dbf5c | ||
|
|
65f26ae443 | ||
|
|
b690e71ecb | ||
|
|
4ba4ef3c7d | ||
|
|
704a73888a | ||
|
|
d2bf1112ad | ||
|
|
33e3da1f12 | ||
|
|
0314610a95 | ||
|
|
98f6da3222 | ||
|
|
19c660c965 | ||
|
|
6ed23c7abe | ||
|
|
5a87326518 | ||
|
|
584c500247 | ||
|
|
bd790a2cd4 | ||
|
|
7e27275eae | ||
|
|
89ca98e84f | ||
|
|
342bb62635 | ||
|
|
7da0bf5a2e | ||
|
|
ce874d5c62 | ||
|
|
25d47ac7d0 | ||
|
|
a2be700a87 | ||
|
|
e396d48488 | ||
|
|
0c57379dfe | ||
|
|
b885100dfe | ||
|
|
3d0d201208 | ||
|
|
2e4d1d51e5 | ||
|
|
a18f6045a3 | ||
|
|
9f08900064 | ||
|
|
e0b88ce42a | ||
|
|
1ff2ec760b | ||
|
|
216214625a | ||
|
|
740b763e78 | ||
|
|
cd9b9a8ab8 | ||
|
|
faf1600721 | ||
|
|
f75384ecdd | ||
|
|
bfb9a7b855 | ||
|
|
78314d9529 | ||
|
|
019cce0203 | ||
|
|
2911861db8 | ||
|
|
0f679ac99c | ||
|
|
33bfddeba9 | ||
|
|
0486f5a294 | ||
|
|
b94c06f81c | ||
|
|
d7170c0264 | ||
|
|
60405fcfbc | ||
|
|
c0cc37787a | ||
|
|
d4812c09a4 | ||
|
|
600c8117a3 | ||
|
|
1a156b1c75 | ||
|
|
f5d656c87d | ||
|
|
f22a7d29dd | ||
|
|
a7bde44ea9 | ||
|
|
2783a1da1b | ||
|
|
5c504ca9f4 | ||
|
|
fe44dd08cc | ||
|
|
9077f683ae | ||
|
|
9413c3e100 | ||
|
|
8c2563e64a | ||
|
|
7d8a279a12 | ||
|
|
382498e01d | ||
|
|
820598cdb8 | ||
|
|
42f33a2dca | ||
|
|
ca0765ac00 | ||
|
|
49aee6f291 | ||
|
|
5e20b74dcc | ||
|
|
fa87ebd9a7 | ||
|
|
b0b1505777 | ||
|
|
0d85c2e88a | ||
|
|
61da8de828 | ||
|
|
61ee956043 | ||
|
|
efd081a2c5 | ||
|
|
c9134d1eeb | ||
|
|
36de48302d | ||
|
|
f74481cb53 | ||
|
|
4c17839831 | ||
|
|
ea7e44e122 | ||
|
|
bc02161d56 | ||
|
|
e8d5c92cb3 | ||
|
|
c6c35115e1 | ||
|
|
029a597a31 | ||
|
|
6619b9b98b | ||
|
|
225c3e3a20 | ||
|
|
596316536e | ||
|
|
521e92796b | ||
|
|
2a50beb9ee | ||
|
|
e310072782 | ||
|
|
622c449a86 | ||
|
|
b4aa41cac1 | ||
|
|
6b7c4d6330 | ||
|
|
f5d997bbbb | ||
|
|
fea911c3d7 | ||
|
|
5222c277e6 | ||
|
|
5835a1da5c | ||
|
|
2120d76250 | ||
|
|
79f132c23b | ||
|
|
4b92288f7b | ||
|
|
b9c667cdcc | ||
|
|
c4227b17e5 | ||
|
|
7538169251 | ||
|
|
31e300e4e8 | ||
|
|
599bf78f20 | ||
|
|
f53fdb8d7a | ||
|
|
3b4bfeab22 | ||
|
|
8c1f828c1f | ||
|
|
f896755a31 | ||
|
|
e406f8be81 | ||
|
|
84107d2b65 | ||
|
|
d890984c70 | ||
|
|
72e4c84f8a | ||
|
|
acd41d985c | ||
|
|
1312657aca | ||
|
|
a502bb9529 | ||
|
|
5947dee9f9 | ||
|
|
e68d59098f | ||
|
|
61efd60681 | ||
|
|
d3654b2ee4 | ||
|
|
ef943a588d | ||
|
|
8c6ebc665d | ||
|
|
2d2df3fc2c | ||
|
|
958ddcb49c | ||
|
|
5e9e988a96 | ||
|
|
34f3409f9b | ||
|
|
49c548494e | ||
|
|
7ba41fbb7d | ||
|
|
d95b974941 | ||
|
|
74bf39ab27 | ||
|
|
8897b9e0f7 | ||
|
|
d6de8644c0 | ||
|
|
190f3b1684 | ||
|
|
ae5961daf4 | ||
|
|
68b51b387a | ||
|
|
9097319c4b | ||
|
|
49a7db074d | ||
|
|
7f7ed04a80 | ||
|
|
ab5f42cc65 | ||
|
|
b1fd8fd51b | ||
|
|
16b98576c7 | ||
|
|
78f5d86f89 | ||
|
|
beec803eff | ||
|
|
3e0b55e8dc | ||
|
|
70db90f25b | ||
|
|
d84b79bee9 | ||
|
|
b491f51a04 | ||
|
|
3892e3f5e7 | ||
|
|
bd6dc08030 | ||
|
|
596cdbecd6 | ||
|
|
7798367348 | ||
|
|
e775fe78ca | ||
|
|
e1eddb336d | ||
|
|
b6bfb66c71 | ||
|
|
bbc28ccef8 | ||
|
|
d57738b0a8 | ||
|
|
6b1a3c874d | ||
|
|
9932010025 | ||
|
|
7cd73bca3c | ||
|
|
580abf8608 | ||
|
|
d845c4f832 | ||
|
|
cc09a166da | ||
|
|
78f6ee1428 | ||
|
|
590e9b3906 | ||
|
|
18f69d379f | ||
|
|
8b121e1352 | ||
|
|
5959f54b6c | ||
|
|
f7d320df30 | ||
|
|
31d7fcc8ba | ||
|
|
8b649ae0d9 | ||
|
|
61675061a0 | ||
|
|
35285dd74b | ||
|
|
6048092d93 | ||
|
|
28b90f412a | ||
|
|
09e7c4a4f2 | ||
|
|
dcdeb668df | ||
|
|
7ac8b1fdcb | ||
|
|
2eb1020b35 | ||
|
|
55cb4c3d0a | ||
|
|
050f391f89 | ||
|
|
3d5c6889b8 | ||
|
|
085f2cd064 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -12,4 +12,6 @@ matrix_auth
|
||||
channels.list
|
||||
comfyworkflows_sharekey
|
||||
github-stats-cache.json
|
||||
pip_overrides.json
|
||||
pip_overrides.json
|
||||
*.json
|
||||
check2.sh
|
||||
|
||||
@@ -204,7 +204,6 @@ This repository provides Colab notebooks that allow you to install and use Comfy
|
||||
* Please submit a pull request to update either the custom-node-list.json or model-list.json file.
|
||||
|
||||
* The scanner currently provides a detection function for missing nodes, which is capable of detecting nodes described by the following two patterns.
|
||||
* Or you can provide manually `node_list.json` file.
|
||||
|
||||
```
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
@@ -218,6 +217,7 @@ NODE_CLASS_MAPPINGS.update({
|
||||
"SemSegPreprocessor": Uniformer_SemSegPreprocessor,
|
||||
})
|
||||
```
|
||||
* Or you can provide manually `node_list.json` file.
|
||||
|
||||
* When you write a docstring in the header of the .py file for the Node as follows, it will be used for managing the database in the Manager.
|
||||
* Currently, only the `nickname` is being used, but other parts will also be utilized in the future.
|
||||
|
||||
@@ -4,6 +4,7 @@ cli_mode_flag = os.path.join(os.path.dirname(__file__), '.enable-cli-only-mode')
|
||||
|
||||
if not os.path.exists(cli_mode_flag):
|
||||
from .glob import manager_server
|
||||
from .glob import share_3rdparty
|
||||
WEB_DIRECTORY = "js"
|
||||
else:
|
||||
print(f"\n[ComfyUI-Manager] !! cli-only-mode is enabled !!\n")
|
||||
|
||||
@@ -35,6 +35,7 @@ restore_snapshot_path = os.path.join(startup_script_path, "restore-snapshot.json
|
||||
pip_overrides_path = os.path.join(comfyui_manager_path, "pip_overrides.json")
|
||||
git_script_path = os.path.join(comfyui_manager_path, "git_helper.py")
|
||||
|
||||
cm_global.pip_blacklist = ['torch', 'torchsde', 'torchvision']
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
cm_global.pip_overrides = {}
|
||||
if os.path.exists(pip_overrides_path):
|
||||
@@ -108,7 +109,7 @@ class Ctx:
|
||||
install_script_path = os.path.join(repo_path, 'install.py')
|
||||
|
||||
if os.path.exists(requirements_path):
|
||||
with (open(requirements_path, 'r', encoding="UTF-8", errors="ignore") as file):
|
||||
with open(requirements_path, 'r', encoding="UTF-8", errors="ignore") as file:
|
||||
for line in file:
|
||||
package_name = core.remap_pip_package(line.strip())
|
||||
if package_name and not core.is_installed(package_name):
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
6004
github-stats.json
6004
github-stats.json
File diff suppressed because it is too large
Load Diff
@@ -23,7 +23,7 @@ sys.path.append(glob_path)
|
||||
import cm_global
|
||||
from manager_util import *
|
||||
|
||||
version = [2, 48, 4]
|
||||
version = [2, 51, 1]
|
||||
version_str = f"V{version[0]}.{version[1]}" + (f'.{version[2]}' if len(version) > 2 else '')
|
||||
|
||||
|
||||
@@ -97,12 +97,15 @@ def clear_pip_cache():
|
||||
def is_blacklisted(name):
|
||||
name = name.strip()
|
||||
|
||||
pattern = r'([^<>!=]+)([<>!=]=?)(.*)'
|
||||
pattern = r'([^<>!=]+)([<>!=]=?)([^ ]*)'
|
||||
match = re.search(pattern, name)
|
||||
|
||||
if match:
|
||||
name = match.group(1)
|
||||
|
||||
if name in cm_global.pip_blacklist:
|
||||
return True
|
||||
|
||||
if name in cm_global.pip_downgrade_blacklist:
|
||||
pips = get_installed_packages()
|
||||
|
||||
@@ -123,12 +126,15 @@ def is_installed(name):
|
||||
if name.startswith('#'):
|
||||
return True
|
||||
|
||||
pattern = r'([^<>!=]+)([<>!=]=?)(.*)'
|
||||
pattern = r'([^<>!=]+)([<>!=]=?)([0-9.a-zA-Z]*)'
|
||||
match = re.search(pattern, name)
|
||||
|
||||
if match:
|
||||
name = match.group(1)
|
||||
|
||||
if name in cm_global.pip_blacklist:
|
||||
return True
|
||||
|
||||
if name in cm_global.pip_downgrade_blacklist:
|
||||
pips = get_installed_packages()
|
||||
|
||||
@@ -404,6 +410,14 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa
|
||||
print("Install: pip packages")
|
||||
with open(requirements_path, "r") as requirements_file:
|
||||
for line in requirements_file:
|
||||
#handle comments
|
||||
if '#' in line:
|
||||
if line.strip()[0] == '#':
|
||||
print("Line is comment...skipping")
|
||||
continue
|
||||
else:
|
||||
line = line.split('#')[0].strip()
|
||||
|
||||
package_name = remap_pip_package(line.strip())
|
||||
|
||||
if package_name and not package_name.startswith('#'):
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import mimetypes
|
||||
import traceback
|
||||
|
||||
import folder_paths
|
||||
@@ -6,7 +5,6 @@ import locale
|
||||
import subprocess # don't remove this
|
||||
import concurrent
|
||||
import nodes
|
||||
import hashlib
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
@@ -22,6 +20,9 @@ print(f"### Loading: ComfyUI-Manager ({core.version_str})")
|
||||
|
||||
comfy_ui_hash = "-"
|
||||
|
||||
SECURITY_MESSAGE_MIDDLE_OR_BELOW = f"ERROR: To use this action, a security_level of `middle or below` is required. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
||||
SECURITY_MESSAGE_NORMAL_MINUS = f"ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
||||
SECURITY_MESSAGE_GENERAL = f"ERROR: This installation is not allowed in this security_level. Please contact the administrator.\nReference: https://github.com/ltdrdata/ComfyUI-Manager#security-policy"
|
||||
|
||||
def handle_stream(stream, prefix):
|
||||
stream.reconfigure(encoding=locale.getpreferredencoding(), errors='replace')
|
||||
@@ -46,7 +47,9 @@ is_local_mode = args.listen.startswith('127.') or args.listen.startswith('local.
|
||||
|
||||
|
||||
def is_allowed_security_level(level):
|
||||
if level == 'high':
|
||||
if level == 'block':
|
||||
return False
|
||||
elif level == 'high':
|
||||
if is_local_mode:
|
||||
return core.get_config()['security_level'].lower() in ['weak', 'normal-']
|
||||
else:
|
||||
@@ -57,7 +60,7 @@ def is_allowed_security_level(level):
|
||||
return True
|
||||
|
||||
|
||||
async def get_risky_level(files):
|
||||
async def get_risky_level(files, pip_packages):
|
||||
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')
|
||||
|
||||
@@ -69,6 +72,15 @@ async def get_risky_level(files):
|
||||
if x not in all_urls:
|
||||
return "high"
|
||||
|
||||
all_pip_packages = set()
|
||||
for x in json_data1['custom_nodes'] + json_data2['custom_nodes']:
|
||||
if "pip" in x:
|
||||
all_pip_packages.update(x['pip'])
|
||||
|
||||
for p in pip_packages:
|
||||
if p not in all_pip_packages:
|
||||
return "block"
|
||||
|
||||
return "middle"
|
||||
|
||||
|
||||
@@ -239,7 +251,7 @@ def get_model_dir(data):
|
||||
if data['save_path'] != 'default':
|
||||
if '..' in data['save_path'] or data['save_path'].startswith('/'):
|
||||
print(f"[WARN] '{data['save_path']}' is not allowed path. So it will be saved into 'models/etc'.")
|
||||
base_model = "etc"
|
||||
base_model = os.path.join(folder_paths.models_dir, "etc")
|
||||
else:
|
||||
if data['save_path'].startswith("custom_nodes"):
|
||||
base_model = os.path.join(core.comfy_path, data['save_path'])
|
||||
@@ -247,10 +259,12 @@ def get_model_dir(data):
|
||||
base_model = os.path.join(folder_paths.models_dir, data['save_path'])
|
||||
else:
|
||||
model_type = data['type']
|
||||
if model_type == "checkpoints":
|
||||
if model_type == "checkpoints" or model_type == "checkpoint":
|
||||
base_model = folder_paths.folder_names_and_paths["checkpoints"][0][0]
|
||||
elif model_type == "unclip":
|
||||
base_model = folder_paths.folder_names_and_paths["checkpoints"][0][0]
|
||||
elif model_type == "clip":
|
||||
base_model = folder_paths.folder_names_and_paths["clip"][0][0]
|
||||
elif model_type == "VAE":
|
||||
base_model = folder_paths.folder_names_and_paths["vae"][0][0]
|
||||
elif model_type == "lora":
|
||||
@@ -269,8 +283,14 @@ def get_model_dir(data):
|
||||
base_model = folder_paths.folder_names_and_paths["upscale_models"][0][0]
|
||||
elif model_type == "embeddings":
|
||||
base_model = folder_paths.folder_names_and_paths["embeddings"][0][0]
|
||||
elif model_type == "unet" or model_type == "diffusion_model":
|
||||
if folder_paths.folder_names_and_paths.get("diffusion_models"):
|
||||
base_model = folder_paths.folder_names_and_paths["diffusion_models"][0][1]
|
||||
else:
|
||||
print(f"[ComfyUI-Manager] Your ComfyUI is outdated version.")
|
||||
base_model = folder_paths.folder_names_and_paths["unet"][0][0] # outdated version
|
||||
else:
|
||||
base_model = "etc"
|
||||
base_model = os.path.join(folder_paths.models_dir, "etc")
|
||||
|
||||
return base_model
|
||||
|
||||
@@ -388,7 +408,7 @@ 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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
try:
|
||||
@@ -590,7 +610,7 @@ 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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
try:
|
||||
@@ -608,7 +628,7 @@ 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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
try:
|
||||
@@ -777,14 +797,14 @@ 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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
json_data = await request.json()
|
||||
|
||||
risky_level = await get_risky_level(json_data['files'])
|
||||
risky_level = await get_risky_level(json_data['files'], json_data.get('pip', []))
|
||||
if not is_allowed_security_level(risky_level):
|
||||
print(f"ERROR: This installation is not allowed in this security_level. Please contact the administrator.")
|
||||
print(SECURITY_MESSAGE_GENERAL)
|
||||
return web.Response(status=404)
|
||||
|
||||
install_type = json_data['install_type']
|
||||
@@ -824,7 +844,7 @@ 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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
json_data = await request.json()
|
||||
@@ -848,6 +868,10 @@ async def fix_custom_node(request):
|
||||
install_cmd = [sys.executable, "-m", "pip", "install", '-U', pname]
|
||||
core.try_install_script(json_data['files'][0], ".", install_cmd)
|
||||
|
||||
# HOTFIX: force downgrade to numpy<2
|
||||
install_cmd = [sys.executable, "-m", "pip", "install", "numpy<2"]
|
||||
core.try_install_script(json_data['files'][0], ".", install_cmd)
|
||||
|
||||
if res:
|
||||
print(f"After restarting ComfyUI, please refresh the browser.")
|
||||
return web.json_response({}, content_type='application/json')
|
||||
@@ -858,7 +882,7 @@ 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 either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.")
|
||||
print(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||
return web.Response(status=403)
|
||||
|
||||
url = await request.text()
|
||||
@@ -874,7 +898,7 @@ 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 either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.")
|
||||
print(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||
return web.Response(status=403)
|
||||
|
||||
packages = await request.text()
|
||||
@@ -886,7 +910,7 @@ 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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
json_data = await request.json()
|
||||
@@ -914,7 +938,7 @@ 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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
json_data = await request.json()
|
||||
@@ -986,7 +1010,7 @@ async def install_model(request):
|
||||
model_path = get_model_path(json_data)
|
||||
|
||||
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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
if not json_data['filename'].endswith('.safetensors') and not is_allowed_security_level('high'):
|
||||
@@ -999,7 +1023,7 @@ async def install_model(request):
|
||||
break
|
||||
|
||||
if not is_belongs_to_whitelist:
|
||||
print(f"ERROR: To use this feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.")
|
||||
print(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||
return web.Response(status=403)
|
||||
|
||||
res = False
|
||||
@@ -1049,7 +1073,7 @@ 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 feature, you must either set '--listen' to a local IP and set the security level to 'normal-' or lower, or set the security level to 'middle' or 'weak'. Please contact the administrator.")
|
||||
print(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||
return web.Response(status=403)
|
||||
|
||||
if "mode" in request.rel_url.query:
|
||||
@@ -1191,7 +1215,7 @@ 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.")
|
||||
print(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||
return web.Response(status=403)
|
||||
|
||||
try:
|
||||
@@ -1271,128 +1295,6 @@ async def load_components(request):
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/share_option")
|
||||
async def share_option(request):
|
||||
if "value" in request.rel_url.query:
|
||||
core.get_config()['share_option'] = request.rel_url.query['value']
|
||||
core.write_config()
|
||||
else:
|
||||
return web.Response(text=core.get_config()['share_option'], status=200)
|
||||
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
def get_openart_auth():
|
||||
if not os.path.exists(os.path.join(core.comfyui_manager_path, ".openart_key")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.comfyui_manager_path, ".openart_key"), "r") as f:
|
||||
openart_key = f.read().strip()
|
||||
return openart_key if openart_key else None
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_matrix_auth():
|
||||
if not os.path.exists(os.path.join(core.comfyui_manager_path, "matrix_auth")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.comfyui_manager_path, "matrix_auth"), "r") as f:
|
||||
matrix_auth = f.read()
|
||||
homeserver, username, password = matrix_auth.strip().split("\n")
|
||||
if not homeserver or not username or not password:
|
||||
return None
|
||||
return {
|
||||
"homeserver": homeserver,
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_comfyworkflows_auth():
|
||||
if not os.path.exists(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey"), "r") as f:
|
||||
share_key = f.read()
|
||||
if not share_key.strip():
|
||||
return None
|
||||
return share_key
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_youml_settings():
|
||||
if not os.path.exists(os.path.join(core.comfyui_manager_path, ".youml")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.comfyui_manager_path, ".youml"), "r") as f:
|
||||
youml_settings = f.read().strip()
|
||||
return youml_settings if youml_settings else None
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def set_youml_settings(settings):
|
||||
with open(os.path.join(core.comfyui_manager_path, ".youml"), "w") as f:
|
||||
f.write(settings)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_openart_auth")
|
||||
async def api_get_openart_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
openart_key = get_openart_auth()
|
||||
if not openart_key:
|
||||
return web.Response(status=404)
|
||||
return web.json_response({"openart_key": openart_key})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/set_openart_auth")
|
||||
async def api_set_openart_auth(request):
|
||||
json_data = await request.json()
|
||||
openart_key = json_data['openart_key']
|
||||
with open(os.path.join(core.comfyui_manager_path, ".openart_key"), "w") as f:
|
||||
f.write(openart_key)
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_matrix_auth")
|
||||
async def api_get_matrix_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
matrix_auth = get_matrix_auth()
|
||||
if not matrix_auth:
|
||||
return web.Response(status=404)
|
||||
return web.json_response(matrix_auth)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/youml/settings")
|
||||
async def api_get_youml_settings(request):
|
||||
youml_settings = get_youml_settings()
|
||||
if not youml_settings:
|
||||
return web.Response(status=404)
|
||||
return web.json_response(json.loads(youml_settings))
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/youml/settings")
|
||||
async def api_set_youml_settings(request):
|
||||
json_data = await request.json()
|
||||
set_youml_settings(json.dumps(json_data))
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_comfyworkflows_auth")
|
||||
async def api_get_comfyworkflows_auth(request):
|
||||
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
|
||||
# in the same directory as the ComfyUI base folder
|
||||
# print("Getting stored Comfyworkflows.com auth...")
|
||||
comfyworkflows_auth = get_comfyworkflows_auth()
|
||||
if not comfyworkflows_auth:
|
||||
return web.Response(status=404)
|
||||
return web.json_response({"comfyworkflows_sharekey": comfyworkflows_auth})
|
||||
|
||||
|
||||
args.enable_cors_header = "*"
|
||||
if hasattr(PromptServer.instance, "app"):
|
||||
app = PromptServer.instance.app
|
||||
@@ -1400,260 +1302,6 @@ if hasattr(PromptServer.instance, "app"):
|
||||
app.middlewares.append(cors_middleware)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/set_esheep_workflow_and_images")
|
||||
async def set_esheep_workflow_and_images(request):
|
||||
json_data = await request.json()
|
||||
current_workflow = json_data['workflow']
|
||||
images = json_data['images']
|
||||
with open(os.path.join(core.comfyui_manager_path, "esheep_share_message.json"), "w", encoding='utf-8') as file:
|
||||
json.dump(json_data, file, indent=4)
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_esheep_workflow_and_images")
|
||||
async def get_esheep_workflow_and_images(request):
|
||||
with open(os.path.join(core.comfyui_manager_path, "esheep_share_message.json"), 'r', encoding='utf-8') as file:
|
||||
data = json.load(file)
|
||||
return web.Response(status=200, text=json.dumps(data))
|
||||
|
||||
|
||||
def set_matrix_auth(json_data):
|
||||
homeserver = json_data['homeserver']
|
||||
username = json_data['username']
|
||||
password = json_data['password']
|
||||
with open(os.path.join(core.comfyui_manager_path, "matrix_auth"), "w") as f:
|
||||
f.write("\n".join([homeserver, username, password]))
|
||||
|
||||
|
||||
def set_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
with open(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey"), "w") as f:
|
||||
f.write(comfyworkflows_sharekey)
|
||||
|
||||
|
||||
def has_provided_matrix_auth(matrix_auth):
|
||||
return matrix_auth['homeserver'].strip() and matrix_auth['username'].strip() and matrix_auth['password'].strip()
|
||||
|
||||
|
||||
def has_provided_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
return comfyworkflows_sharekey.strip()
|
||||
|
||||
|
||||
def extract_model_file_names(json_data):
|
||||
"""Extract unique file names from the input JSON data."""
|
||||
file_names = set()
|
||||
model_filename_extensions = {'.safetensors', '.ckpt', '.pt', '.pth', '.bin'}
|
||||
|
||||
# Recursively search for file names in the JSON data
|
||||
def recursive_search(data):
|
||||
if isinstance(data, dict):
|
||||
for value in data.values():
|
||||
recursive_search(value)
|
||||
elif isinstance(data, list):
|
||||
for item in data:
|
||||
recursive_search(item)
|
||||
elif isinstance(data, str) and '.' in data:
|
||||
file_names.add(os.path.basename(data)) # file_names.add(data)
|
||||
|
||||
recursive_search(json_data)
|
||||
return [f for f in list(file_names) if os.path.splitext(f)[1] in model_filename_extensions]
|
||||
|
||||
|
||||
def find_file_paths(base_dir, file_names):
|
||||
"""Find the paths of the files in the base directory."""
|
||||
file_paths = {}
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
# Exclude certain directories
|
||||
dirs[:] = [d for d in dirs if d not in ['.git']]
|
||||
|
||||
for file in files:
|
||||
if file in file_names:
|
||||
file_paths[file] = os.path.join(root, file)
|
||||
return file_paths
|
||||
|
||||
|
||||
def compute_sha256_checksum(filepath):
|
||||
"""Compute the SHA256 checksum of a file, in chunks"""
|
||||
sha256 = hashlib.sha256()
|
||||
with open(filepath, 'rb') as f:
|
||||
for chunk in iter(lambda: f.read(4096), b''):
|
||||
sha256.update(chunk)
|
||||
return sha256.hexdigest()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/share")
|
||||
async def share_art(request):
|
||||
# get json data
|
||||
json_data = await request.json()
|
||||
|
||||
matrix_auth = json_data['matrix_auth']
|
||||
comfyworkflows_sharekey = json_data['cw_auth']['cw_sharekey']
|
||||
|
||||
set_matrix_auth(matrix_auth)
|
||||
set_comfyworkflows_auth(comfyworkflows_sharekey)
|
||||
|
||||
share_destinations = json_data['share_destinations']
|
||||
credits = json_data['credits']
|
||||
title = json_data['title']
|
||||
description = json_data['description']
|
||||
is_nsfw = json_data['is_nsfw']
|
||||
prompt = json_data['prompt']
|
||||
potential_outputs = json_data['potential_outputs']
|
||||
selected_output_index = json_data['selected_output_index']
|
||||
|
||||
try:
|
||||
output_to_share = potential_outputs[int(selected_output_index)]
|
||||
except:
|
||||
# for now, pick the first output
|
||||
output_to_share = potential_outputs[0]
|
||||
|
||||
assert output_to_share['type'] in ('image', 'output')
|
||||
output_dir = folder_paths.get_output_directory()
|
||||
|
||||
if output_to_share['type'] == 'image':
|
||||
asset_filename = output_to_share['image']['filename']
|
||||
asset_subfolder = output_to_share['image']['subfolder']
|
||||
|
||||
if output_to_share['image']['type'] == 'temp':
|
||||
output_dir = folder_paths.get_temp_directory()
|
||||
else:
|
||||
asset_filename = output_to_share['output']['filename']
|
||||
asset_subfolder = output_to_share['output']['subfolder']
|
||||
|
||||
if asset_subfolder:
|
||||
asset_filepath = os.path.join(output_dir, asset_subfolder, asset_filename)
|
||||
else:
|
||||
asset_filepath = os.path.join(output_dir, asset_filename)
|
||||
|
||||
# get the mime type of the asset
|
||||
assetFileType = mimetypes.guess_type(asset_filepath)[0]
|
||||
|
||||
share_website_host = "UNKNOWN"
|
||||
if "comfyworkflows" in share_destinations:
|
||||
share_website_host = "https://comfyworkflows.com"
|
||||
share_endpoint = f"{share_website_host}/api"
|
||||
|
||||
# get presigned urls
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.post(
|
||||
f"{share_endpoint}/get_presigned_urls",
|
||||
json={
|
||||
"assetFileName": asset_filename,
|
||||
"assetFileType": assetFileType,
|
||||
"workflowJsonFileName": 'workflow.json',
|
||||
"workflowJsonFileType": 'application/json',
|
||||
},
|
||||
) as resp:
|
||||
assert resp.status == 200
|
||||
presigned_urls_json = await resp.json()
|
||||
assetFilePresignedUrl = presigned_urls_json["assetFilePresignedUrl"]
|
||||
assetFileKey = presigned_urls_json["assetFileKey"]
|
||||
workflowJsonFilePresignedUrl = presigned_urls_json["workflowJsonFilePresignedUrl"]
|
||||
workflowJsonFileKey = presigned_urls_json["workflowJsonFileKey"]
|
||||
|
||||
# upload asset
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.put(assetFilePresignedUrl, data=open(asset_filepath, "rb")) as resp:
|
||||
assert resp.status == 200
|
||||
|
||||
# upload workflow json
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.put(workflowJsonFilePresignedUrl, data=json.dumps(prompt['workflow']).encode('utf-8')) as resp:
|
||||
assert resp.status == 200
|
||||
|
||||
model_filenames = extract_model_file_names(prompt['workflow'])
|
||||
model_file_paths = find_file_paths(folder_paths.base_path, model_filenames)
|
||||
|
||||
models_info = {}
|
||||
for filename, filepath in model_file_paths.items():
|
||||
models_info[filename] = {
|
||||
"filename": filename,
|
||||
"sha256_checksum": compute_sha256_checksum(filepath),
|
||||
"relative_path": os.path.relpath(filepath, folder_paths.base_path),
|
||||
}
|
||||
|
||||
# make a POST request to /api/upload_workflow with form data key values
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
form = aiohttp.FormData()
|
||||
if comfyworkflows_sharekey:
|
||||
form.add_field("shareKey", comfyworkflows_sharekey)
|
||||
form.add_field("source", "comfyui_manager")
|
||||
form.add_field("assetFileKey", assetFileKey)
|
||||
form.add_field("assetFileType", assetFileType)
|
||||
form.add_field("workflowJsonFileKey", workflowJsonFileKey)
|
||||
form.add_field("sharedWorkflowWorkflowJsonString", json.dumps(prompt['workflow']))
|
||||
form.add_field("sharedWorkflowPromptJsonString", json.dumps(prompt['output']))
|
||||
form.add_field("shareWorkflowCredits", credits)
|
||||
form.add_field("shareWorkflowTitle", title)
|
||||
form.add_field("shareWorkflowDescription", description)
|
||||
form.add_field("shareWorkflowIsNSFW", str(is_nsfw).lower())
|
||||
form.add_field("currentSnapshot", json.dumps(core.get_current_snapshot()))
|
||||
form.add_field("modelsInfo", json.dumps(models_info))
|
||||
|
||||
async with session.post(
|
||||
f"{share_endpoint}/upload_workflow",
|
||||
data=form,
|
||||
) as resp:
|
||||
assert resp.status == 200
|
||||
upload_workflow_json = await resp.json()
|
||||
workflowId = upload_workflow_json["workflowId"]
|
||||
|
||||
# check if the user has provided Matrix credentials
|
||||
if "matrix" in share_destinations:
|
||||
comfyui_share_room_id = '!LGYSoacpJPhIfBqVfb:matrix.org'
|
||||
filename = os.path.basename(asset_filepath)
|
||||
content_type = assetFileType
|
||||
|
||||
try:
|
||||
from matrix_client.api import MatrixHttpApi
|
||||
from matrix_client.client import MatrixClient
|
||||
|
||||
homeserver = 'matrix.org'
|
||||
if matrix_auth:
|
||||
homeserver = matrix_auth.get('homeserver', 'matrix.org')
|
||||
homeserver = homeserver.replace("http://", "https://")
|
||||
if not homeserver.startswith("https://"):
|
||||
homeserver = "https://" + homeserver
|
||||
|
||||
client = MatrixClient(homeserver)
|
||||
try:
|
||||
token = client.login(username=matrix_auth['username'], password=matrix_auth['password'])
|
||||
if not token:
|
||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
||||
except:
|
||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
||||
|
||||
matrix = MatrixHttpApi(homeserver, token=token)
|
||||
with open(asset_filepath, 'rb') as f:
|
||||
mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri']
|
||||
|
||||
workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri']
|
||||
|
||||
text_content = ""
|
||||
if title:
|
||||
text_content += f"{title}\n"
|
||||
if description:
|
||||
text_content += f"{description}\n"
|
||||
if credits:
|
||||
text_content += f"\ncredits: {credits}\n"
|
||||
response = matrix.send_message(comfyui_share_room_id, text_content)
|
||||
response = matrix.send_content(comfyui_share_room_id, mxc_url, filename, 'm.image')
|
||||
response = matrix.send_content(comfyui_share_room_id, workflow_json_mxc_url, 'workflow.json', 'm.file')
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return web.json_response({"error": "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
|
||||
|
||||
return web.json_response({
|
||||
"comfyworkflows": {
|
||||
"url": None if "comfyworkflows" not in share_destinations else f"{share_website_host}/workflows/{workflowId}",
|
||||
},
|
||||
"matrix": {
|
||||
"success": None if "matrix" not in share_destinations else True
|
||||
}
|
||||
}, content_type='application/json', status=200)
|
||||
|
||||
|
||||
def sanitize(data):
|
||||
return data.replace("<", "<").replace(">", ">")
|
||||
|
||||
|
||||
@@ -1,63 +1,64 @@
|
||||
try:
|
||||
from distutils.version import StrictVersion
|
||||
except:
|
||||
print(f"[ComfyUI-Manager] 'distutils' package not found. Activating fallback mode for compatibility.")
|
||||
class StrictVersion:
|
||||
def __init__(self, version_string):
|
||||
self.version_string = version_string
|
||||
self.major = 0
|
||||
self.minor = 0
|
||||
self.patch = 0
|
||||
self.pre_release = None
|
||||
self.parse_version_string()
|
||||
# DON'T USE StrictVersion - cannot handle pre_release version
|
||||
# try:
|
||||
# from distutils.version import StrictVersion
|
||||
# except:
|
||||
# print(f"[ComfyUI-Manager] 'distutils' package not found. Activating fallback mode for compatibility.")
|
||||
class StrictVersion:
|
||||
def __init__(self, version_string):
|
||||
self.version_string = version_string
|
||||
self.major = 0
|
||||
self.minor = 0
|
||||
self.patch = 0
|
||||
self.pre_release = None
|
||||
self.parse_version_string()
|
||||
|
||||
def parse_version_string(self):
|
||||
parts = self.version_string.split('.')
|
||||
if not parts:
|
||||
raise ValueError("Version string must not be empty")
|
||||
def parse_version_string(self):
|
||||
parts = self.version_string.split('.')
|
||||
if not parts:
|
||||
raise ValueError("Version string must not be empty")
|
||||
|
||||
self.major = int(parts[0])
|
||||
self.minor = int(parts[1]) if len(parts) > 1 else 0
|
||||
self.patch = int(parts[2]) if len(parts) > 2 else 0
|
||||
self.major = int(parts[0])
|
||||
self.minor = int(parts[1]) if len(parts) > 1 else 0
|
||||
self.patch = int(parts[2]) if len(parts) > 2 else 0
|
||||
|
||||
# Handling pre-release versions if present
|
||||
if len(parts) > 3:
|
||||
self.pre_release = parts[3]
|
||||
# Handling pre-release versions if present
|
||||
if len(parts) > 3:
|
||||
self.pre_release = parts[3]
|
||||
|
||||
def __str__(self):
|
||||
version = f"{self.major}.{self.minor}.{self.patch}"
|
||||
if self.pre_release:
|
||||
version += f"-{self.pre_release}"
|
||||
return version
|
||||
def __str__(self):
|
||||
version = f"{self.major}.{self.minor}.{self.patch}"
|
||||
if self.pre_release:
|
||||
version += f"-{self.pre_release}"
|
||||
return version
|
||||
|
||||
def __eq__(self, other):
|
||||
return (self.major, self.minor, self.patch, self.pre_release) == \
|
||||
(other.major, other.minor, other.patch, other.pre_release)
|
||||
def __eq__(self, other):
|
||||
return (self.major, self.minor, self.patch, self.pre_release) == \
|
||||
(other.major, other.minor, other.patch, other.pre_release)
|
||||
|
||||
def __lt__(self, other):
|
||||
if (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch):
|
||||
return self.pre_release_compare(self.pre_release, other.pre_release) < 0
|
||||
return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)
|
||||
def __lt__(self, other):
|
||||
if (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch):
|
||||
return self.pre_release_compare(self.pre_release, other.pre_release) < 0
|
||||
return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch)
|
||||
|
||||
@staticmethod
|
||||
def pre_release_compare(pre1, pre2):
|
||||
if pre1 == pre2:
|
||||
return 0
|
||||
if pre1 is None:
|
||||
return 1
|
||||
if pre2 is None:
|
||||
return -1
|
||||
return -1 if pre1 < pre2 else 1
|
||||
@staticmethod
|
||||
def pre_release_compare(pre1, pre2):
|
||||
if pre1 == pre2:
|
||||
return 0
|
||||
if pre1 is None:
|
||||
return 1
|
||||
if pre2 is None:
|
||||
return -1
|
||||
return -1 if pre1 < pre2 else 1
|
||||
|
||||
def __le__(self, other):
|
||||
return self == other or self < other
|
||||
def __le__(self, other):
|
||||
return self == other or self < other
|
||||
|
||||
def __gt__(self, other):
|
||||
return not self <= other
|
||||
def __gt__(self, other):
|
||||
return not self <= other
|
||||
|
||||
def __ge__(self, other):
|
||||
return not self < other
|
||||
def __ge__(self, other):
|
||||
return not self < other
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
|
||||
386
glob/share_3rdparty.py
Normal file
386
glob/share_3rdparty.py
Normal file
@@ -0,0 +1,386 @@
|
||||
import mimetypes
|
||||
import manager_core as core
|
||||
import os
|
||||
from aiohttp import web
|
||||
import aiohttp
|
||||
import json
|
||||
import hashlib
|
||||
|
||||
import folder_paths
|
||||
from server import PromptServer
|
||||
|
||||
|
||||
def extract_model_file_names(json_data):
|
||||
"""Extract unique file names from the input JSON data."""
|
||||
file_names = set()
|
||||
model_filename_extensions = {'.safetensors', '.ckpt', '.pt', '.pth', '.bin'}
|
||||
|
||||
# Recursively search for file names in the JSON data
|
||||
def recursive_search(data):
|
||||
if isinstance(data, dict):
|
||||
for value in data.values():
|
||||
recursive_search(value)
|
||||
elif isinstance(data, list):
|
||||
for item in data:
|
||||
recursive_search(item)
|
||||
elif isinstance(data, str) and '.' in data:
|
||||
file_names.add(os.path.basename(data)) # file_names.add(data)
|
||||
|
||||
recursive_search(json_data)
|
||||
return [f for f in list(file_names) if os.path.splitext(f)[1] in model_filename_extensions]
|
||||
|
||||
|
||||
def find_file_paths(base_dir, file_names):
|
||||
"""Find the paths of the files in the base directory."""
|
||||
file_paths = {}
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
# Exclude certain directories
|
||||
dirs[:] = [d for d in dirs if d not in ['.git']]
|
||||
|
||||
for file in files:
|
||||
if file in file_names:
|
||||
file_paths[file] = os.path.join(root, file)
|
||||
return file_paths
|
||||
|
||||
|
||||
def compute_sha256_checksum(filepath):
|
||||
"""Compute the SHA256 checksum of a file, in chunks"""
|
||||
sha256 = hashlib.sha256()
|
||||
with open(filepath, 'rb') as f:
|
||||
for chunk in iter(lambda: f.read(4096), b''):
|
||||
sha256.update(chunk)
|
||||
return sha256.hexdigest()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/share_option")
|
||||
async def share_option(request):
|
||||
if "value" in request.rel_url.query:
|
||||
core.get_config()['share_option'] = request.rel_url.query['value']
|
||||
core.write_config()
|
||||
else:
|
||||
return web.Response(text=core.get_config()['share_option'], status=200)
|
||||
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
def get_openart_auth():
|
||||
if not os.path.exists(os.path.join(core.comfyui_manager_path, ".openart_key")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.comfyui_manager_path, ".openart_key"), "r") as f:
|
||||
openart_key = f.read().strip()
|
||||
return openart_key if openart_key else None
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_matrix_auth():
|
||||
if not os.path.exists(os.path.join(core.comfyui_manager_path, "matrix_auth")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.comfyui_manager_path, "matrix_auth"), "r") as f:
|
||||
matrix_auth = f.read()
|
||||
homeserver, username, password = matrix_auth.strip().split("\n")
|
||||
if not homeserver or not username or not password:
|
||||
return None
|
||||
return {
|
||||
"homeserver": homeserver,
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_comfyworkflows_auth():
|
||||
if not os.path.exists(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey"), "r") as f:
|
||||
share_key = f.read()
|
||||
if not share_key.strip():
|
||||
return None
|
||||
return share_key
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_youml_settings():
|
||||
if not os.path.exists(os.path.join(core.comfyui_manager_path, ".youml")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.comfyui_manager_path, ".youml"), "r") as f:
|
||||
youml_settings = f.read().strip()
|
||||
return youml_settings if youml_settings else None
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def set_youml_settings(settings):
|
||||
with open(os.path.join(core.comfyui_manager_path, ".youml"), "w") as f:
|
||||
f.write(settings)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_openart_auth")
|
||||
async def api_get_openart_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
openart_key = get_openart_auth()
|
||||
if not openart_key:
|
||||
return web.Response(status=404)
|
||||
return web.json_response({"openart_key": openart_key})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/set_openart_auth")
|
||||
async def api_set_openart_auth(request):
|
||||
json_data = await request.json()
|
||||
openart_key = json_data['openart_key']
|
||||
with open(os.path.join(core.comfyui_manager_path, ".openart_key"), "w") as f:
|
||||
f.write(openart_key)
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_matrix_auth")
|
||||
async def api_get_matrix_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
matrix_auth = get_matrix_auth()
|
||||
if not matrix_auth:
|
||||
return web.Response(status=404)
|
||||
return web.json_response(matrix_auth)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/youml/settings")
|
||||
async def api_get_youml_settings(request):
|
||||
youml_settings = get_youml_settings()
|
||||
if not youml_settings:
|
||||
return web.Response(status=404)
|
||||
return web.json_response(json.loads(youml_settings))
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/youml/settings")
|
||||
async def api_set_youml_settings(request):
|
||||
json_data = await request.json()
|
||||
set_youml_settings(json.dumps(json_data))
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_comfyworkflows_auth")
|
||||
async def api_get_comfyworkflows_auth(request):
|
||||
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
|
||||
# in the same directory as the ComfyUI base folder
|
||||
# print("Getting stored Comfyworkflows.com auth...")
|
||||
comfyworkflows_auth = get_comfyworkflows_auth()
|
||||
if not comfyworkflows_auth:
|
||||
return web.Response(status=404)
|
||||
return web.json_response({"comfyworkflows_sharekey": comfyworkflows_auth})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/set_esheep_workflow_and_images")
|
||||
async def set_esheep_workflow_and_images(request):
|
||||
json_data = await request.json()
|
||||
current_workflow = json_data['workflow']
|
||||
images = json_data['images']
|
||||
with open(os.path.join(core.comfyui_manager_path, "esheep_share_message.json"), "w", encoding='utf-8') as file:
|
||||
json.dump(json_data, file, indent=4)
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_esheep_workflow_and_images")
|
||||
async def get_esheep_workflow_and_images(request):
|
||||
with open(os.path.join(core.comfyui_manager_path, "esheep_share_message.json"), 'r', encoding='utf-8') as file:
|
||||
data = json.load(file)
|
||||
return web.Response(status=200, text=json.dumps(data))
|
||||
|
||||
|
||||
def set_matrix_auth(json_data):
|
||||
homeserver = json_data['homeserver']
|
||||
username = json_data['username']
|
||||
password = json_data['password']
|
||||
with open(os.path.join(core.comfyui_manager_path, "matrix_auth"), "w") as f:
|
||||
f.write("\n".join([homeserver, username, password]))
|
||||
|
||||
|
||||
def set_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
with open(os.path.join(core.comfyui_manager_path, "comfyworkflows_sharekey"), "w") as f:
|
||||
f.write(comfyworkflows_sharekey)
|
||||
|
||||
|
||||
def has_provided_matrix_auth(matrix_auth):
|
||||
return matrix_auth['homeserver'].strip() and matrix_auth['username'].strip() and matrix_auth['password'].strip()
|
||||
|
||||
|
||||
def has_provided_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
return comfyworkflows_sharekey.strip()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/share")
|
||||
async def share_art(request):
|
||||
# get json data
|
||||
json_data = await request.json()
|
||||
|
||||
matrix_auth = json_data['matrix_auth']
|
||||
comfyworkflows_sharekey = json_data['cw_auth']['cw_sharekey']
|
||||
|
||||
set_matrix_auth(matrix_auth)
|
||||
set_comfyworkflows_auth(comfyworkflows_sharekey)
|
||||
|
||||
share_destinations = json_data['share_destinations']
|
||||
credits = json_data['credits']
|
||||
title = json_data['title']
|
||||
description = json_data['description']
|
||||
is_nsfw = json_data['is_nsfw']
|
||||
prompt = json_data['prompt']
|
||||
potential_outputs = json_data['potential_outputs']
|
||||
selected_output_index = json_data['selected_output_index']
|
||||
|
||||
try:
|
||||
output_to_share = potential_outputs[int(selected_output_index)]
|
||||
except:
|
||||
# for now, pick the first output
|
||||
output_to_share = potential_outputs[0]
|
||||
|
||||
assert output_to_share['type'] in ('image', 'output')
|
||||
output_dir = folder_paths.get_output_directory()
|
||||
|
||||
if output_to_share['type'] == 'image':
|
||||
asset_filename = output_to_share['image']['filename']
|
||||
asset_subfolder = output_to_share['image']['subfolder']
|
||||
|
||||
if output_to_share['image']['type'] == 'temp':
|
||||
output_dir = folder_paths.get_temp_directory()
|
||||
else:
|
||||
asset_filename = output_to_share['output']['filename']
|
||||
asset_subfolder = output_to_share['output']['subfolder']
|
||||
|
||||
if asset_subfolder:
|
||||
asset_filepath = os.path.join(output_dir, asset_subfolder, asset_filename)
|
||||
else:
|
||||
asset_filepath = os.path.join(output_dir, asset_filename)
|
||||
|
||||
# get the mime type of the asset
|
||||
assetFileType = mimetypes.guess_type(asset_filepath)[0]
|
||||
|
||||
share_website_host = "UNKNOWN"
|
||||
if "comfyworkflows" in share_destinations:
|
||||
share_website_host = "https://comfyworkflows.com"
|
||||
share_endpoint = f"{share_website_host}/api"
|
||||
|
||||
# get presigned urls
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.post(
|
||||
f"{share_endpoint}/get_presigned_urls",
|
||||
json={
|
||||
"assetFileName": asset_filename,
|
||||
"assetFileType": assetFileType,
|
||||
"workflowJsonFileName": 'workflow.json',
|
||||
"workflowJsonFileType": 'application/json',
|
||||
},
|
||||
) as resp:
|
||||
assert resp.status == 200
|
||||
presigned_urls_json = await resp.json()
|
||||
assetFilePresignedUrl = presigned_urls_json["assetFilePresignedUrl"]
|
||||
assetFileKey = presigned_urls_json["assetFileKey"]
|
||||
workflowJsonFilePresignedUrl = presigned_urls_json["workflowJsonFilePresignedUrl"]
|
||||
workflowJsonFileKey = presigned_urls_json["workflowJsonFileKey"]
|
||||
|
||||
# upload asset
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.put(assetFilePresignedUrl, data=open(asset_filepath, "rb")) as resp:
|
||||
assert resp.status == 200
|
||||
|
||||
# upload workflow json
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.put(workflowJsonFilePresignedUrl, data=json.dumps(prompt['workflow']).encode('utf-8')) as resp:
|
||||
assert resp.status == 200
|
||||
|
||||
model_filenames = extract_model_file_names(prompt['workflow'])
|
||||
model_file_paths = find_file_paths(folder_paths.base_path, model_filenames)
|
||||
|
||||
models_info = {}
|
||||
for filename, filepath in model_file_paths.items():
|
||||
models_info[filename] = {
|
||||
"filename": filename,
|
||||
"sha256_checksum": compute_sha256_checksum(filepath),
|
||||
"relative_path": os.path.relpath(filepath, folder_paths.base_path),
|
||||
}
|
||||
|
||||
# make a POST request to /api/upload_workflow with form data key values
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
form = aiohttp.FormData()
|
||||
if comfyworkflows_sharekey:
|
||||
form.add_field("shareKey", comfyworkflows_sharekey)
|
||||
form.add_field("source", "comfyui_manager")
|
||||
form.add_field("assetFileKey", assetFileKey)
|
||||
form.add_field("assetFileType", assetFileType)
|
||||
form.add_field("workflowJsonFileKey", workflowJsonFileKey)
|
||||
form.add_field("sharedWorkflowWorkflowJsonString", json.dumps(prompt['workflow']))
|
||||
form.add_field("sharedWorkflowPromptJsonString", json.dumps(prompt['output']))
|
||||
form.add_field("shareWorkflowCredits", credits)
|
||||
form.add_field("shareWorkflowTitle", title)
|
||||
form.add_field("shareWorkflowDescription", description)
|
||||
form.add_field("shareWorkflowIsNSFW", str(is_nsfw).lower())
|
||||
form.add_field("currentSnapshot", json.dumps(core.get_current_snapshot()))
|
||||
form.add_field("modelsInfo", json.dumps(models_info))
|
||||
|
||||
async with session.post(
|
||||
f"{share_endpoint}/upload_workflow",
|
||||
data=form,
|
||||
) as resp:
|
||||
assert resp.status == 200
|
||||
upload_workflow_json = await resp.json()
|
||||
workflowId = upload_workflow_json["workflowId"]
|
||||
|
||||
# check if the user has provided Matrix credentials
|
||||
if "matrix" in share_destinations:
|
||||
comfyui_share_room_id = '!LGYSoacpJPhIfBqVfb:matrix.org'
|
||||
filename = os.path.basename(asset_filepath)
|
||||
content_type = assetFileType
|
||||
|
||||
try:
|
||||
from matrix_client.api import MatrixHttpApi
|
||||
from matrix_client.client import MatrixClient
|
||||
|
||||
homeserver = 'matrix.org'
|
||||
if matrix_auth:
|
||||
homeserver = matrix_auth.get('homeserver', 'matrix.org')
|
||||
homeserver = homeserver.replace("http://", "https://")
|
||||
if not homeserver.startswith("https://"):
|
||||
homeserver = "https://" + homeserver
|
||||
|
||||
client = MatrixClient(homeserver)
|
||||
try:
|
||||
token = client.login(username=matrix_auth['username'], password=matrix_auth['password'])
|
||||
if not token:
|
||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
||||
except:
|
||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
||||
|
||||
matrix = MatrixHttpApi(homeserver, token=token)
|
||||
with open(asset_filepath, 'rb') as f:
|
||||
mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri']
|
||||
|
||||
workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri']
|
||||
|
||||
text_content = ""
|
||||
if title:
|
||||
text_content += f"{title}\n"
|
||||
if description:
|
||||
text_content += f"{description}\n"
|
||||
if credits:
|
||||
text_content += f"\ncredits: {credits}\n"
|
||||
response = matrix.send_message(comfyui_share_room_id, text_content)
|
||||
response = matrix.send_content(comfyui_share_room_id, mxc_url, filename, 'm.image')
|
||||
response = matrix.send_content(comfyui_share_room_id, workflow_json_mxc_url, 'workflow.json', 'm.file')
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return web.json_response({"error": "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
|
||||
|
||||
return web.json_response({
|
||||
"comfyworkflows": {
|
||||
"url": None if "comfyworkflows" not in share_destinations else f"{share_website_host}/workflows/{workflowId}",
|
||||
},
|
||||
"matrix": {
|
||||
"success": None if "matrix" not in share_destinations else True
|
||||
}
|
||||
}, content_type='application/json', status=200)
|
||||
@@ -4,7 +4,7 @@ import { sleep } from "./common.js";
|
||||
|
||||
async function tryInstallCustomNode(event) {
|
||||
let msg = '-= [ComfyUI Manager] extension installation request =-\n\n';
|
||||
msg += `The '${event.detail.sender}' extension requires the installation of the '${event.detail.title}' extension. `;
|
||||
msg += `The '${event.detail.sender}' extension requires the installation of the '${event.detail.target.title}' extension. `;
|
||||
|
||||
if(event.detail.target.installed == 'Disabled') {
|
||||
msg += 'However, the extension is currently disabled. Would you like to enable it and reboot?'
|
||||
|
||||
@@ -202,6 +202,40 @@ docStyle.innerHTML = `
|
||||
}
|
||||
`;
|
||||
|
||||
function is_legacy_front() {
|
||||
let compareVersion = '1.2.49';
|
||||
try {
|
||||
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
|
||||
if (typeof frontendVersion !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
function parseVersion(versionString) {
|
||||
const parts = versionString.split('.').map(Number);
|
||||
return parts.length === 3 && parts.every(part => !isNaN(part)) ? parts : null;
|
||||
}
|
||||
|
||||
const currentVersion = parseVersion(frontendVersion);
|
||||
const comparisonVersion = parseVersion(compareVersion);
|
||||
|
||||
if (!currentVersion || !comparisonVersion) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
if (currentVersion[i] > comparisonVersion[i]) {
|
||||
return false;
|
||||
} else if (currentVersion[i] < comparisonVersion[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
document.head.appendChild(docStyle);
|
||||
|
||||
var update_comfyui_button = null;
|
||||
@@ -842,24 +876,27 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
});
|
||||
|
||||
// nickname
|
||||
let badge_combo = document.createElement("select");
|
||||
badge_combo.setAttribute("title", "Configure the content to be displayed on the badge at the top right corner of the node. The ID is the identifier of the node. If 'hide built-in' is selected, both unknown nodes and built-in nodes will be omitted, making them indistinguishable");
|
||||
badge_combo.className = "cm-menu-combo";
|
||||
badge_combo.appendChild($el('option', { value: 'none', text: 'Badge: None' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'nick', text: 'Badge: Nickname' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'nick_hide', text: 'Badge: Nickname (hide built-in)' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'id_nick', text: 'Badge: #ID Nickname' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'id_nick_hide', text: 'Badge: #ID Nickname (hide built-in)' }, []));
|
||||
let badge_combo = "";
|
||||
if(is_legacy_front()) {
|
||||
badge_combo = document.createElement("select");
|
||||
badge_combo.setAttribute("title", "Configure the content to be displayed on the badge at the top right corner of the node. The ID is the identifier of the node. If 'hide built-in' is selected, both unknown nodes and built-in nodes will be omitted, making them indistinguishable");
|
||||
badge_combo.className = "cm-menu-combo";
|
||||
badge_combo.appendChild($el('option', { value: 'none', text: 'Badge: None' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'nick', text: 'Badge: Nickname' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'nick_hide', text: 'Badge: Nickname (hide built-in)' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'id_nick', text: 'Badge: #ID Nickname' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'id_nick_hide', text: 'Badge: #ID Nickname (hide built-in)' }, []));
|
||||
|
||||
api.fetchApi('/manager/badge_mode')
|
||||
.then(response => response.text())
|
||||
.then(data => { badge_combo.value = data; badge_mode = data; });
|
||||
api.fetchApi('/manager/badge_mode')
|
||||
.then(response => response.text())
|
||||
.then(data => { badge_combo.value = data; badge_mode = data; });
|
||||
|
||||
badge_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/manager/badge_mode?value=${event.target.value}`);
|
||||
badge_mode = event.target.value;
|
||||
app.graph.setDirtyCanvas(true);
|
||||
});
|
||||
badge_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/manager/badge_mode?value=${event.target.value}`);
|
||||
badge_mode = event.target.value;
|
||||
app.graph.setDirtyCanvas(true);
|
||||
});
|
||||
}
|
||||
|
||||
// channel
|
||||
let channel_combo = document.createElement("select");
|
||||
@@ -1095,7 +1132,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
textContent: 'Workflow Gallery',
|
||||
style: {
|
||||
'text-align': 'center',
|
||||
'color': 'white',
|
||||
'color': 'var(--input-text)',
|
||||
'font-size': '18px',
|
||||
'margin': 0,
|
||||
'padding': 0,
|
||||
@@ -1106,7 +1143,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : ''})`,
|
||||
style: {
|
||||
'text-align': 'center',
|
||||
'color': 'white',
|
||||
'color': 'var(--input-text)',
|
||||
'font-size': '12px',
|
||||
'margin': 0,
|
||||
'padding': 0,
|
||||
@@ -1411,24 +1448,28 @@ app.registerExtension({
|
||||
},
|
||||
|
||||
async nodeCreated(node, app) {
|
||||
if(!node.badge_enabled) {
|
||||
node.getNickname = function () { return getNickname(node, node.comfyClass.trim()) };
|
||||
let orig = node.onDrawForeground;
|
||||
if(!orig)
|
||||
orig = node.__proto__.onDrawForeground;
|
||||
if(is_legacy_front()) {
|
||||
if(!node.badge_enabled) {
|
||||
node.getNickname = function () { return getNickname(node, node.comfyClass.trim()) };
|
||||
let orig = node.onDrawForeground;
|
||||
if(!orig)
|
||||
orig = node.__proto__.onDrawForeground;
|
||||
|
||||
node.onDrawForeground = function (ctx) {
|
||||
drawBadge(node, orig, arguments)
|
||||
};
|
||||
node.badge_enabled = true;
|
||||
node.onDrawForeground = function (ctx) {
|
||||
drawBadge(node, orig, arguments)
|
||||
};
|
||||
node.badge_enabled = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
async loadedGraphNode(node, app) {
|
||||
if(!node.badge_enabled) {
|
||||
const orig = node.onDrawForeground;
|
||||
node.getNickname = function () { return getNickname(node, node.type.trim()) };
|
||||
node.onDrawForeground = function (ctx) { drawBadge(node, orig, arguments) };
|
||||
if(is_legacy_front()) {
|
||||
if(!node.badge_enabled) {
|
||||
const orig = node.onDrawForeground;
|
||||
node.getNickname = function () { return getNickname(node, node.type.trim()) };
|
||||
node.onDrawForeground = function (ctx) { drawBadge(node, orig, arguments) };
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -315,7 +315,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
key: "comfyworkflows",
|
||||
textContent: "ComfyWorkflows",
|
||||
website: "https://comfyworkflows.com",
|
||||
description: "Share & browse thousands of ComfyUI workflows and art 🎨<br/><br/><a style='color:white;' href='https://comfyworkflows.com' target='_blank'>ComfyWorkflows.com</a>",
|
||||
description: "Share & browse thousands of ComfyUI workflows and art 🎨<br/><br/><a style='color:var(--input-text);' href='https://comfyworkflows.com' target='_blank'>ComfyWorkflows.com</a>",
|
||||
onclick: () => {
|
||||
showShareDialog('comfyworkflows').then((suc) => {
|
||||
suc && this.close();
|
||||
@@ -326,7 +326,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
key: "esheep",
|
||||
textContent: "eSheep",
|
||||
website: "https://www.esheep.com",
|
||||
description: "Share & download thousands of ComfyUI workflows on <a style='color:white;' href='https://www.esheep.com' target='_blank'>esheep.com</a>",
|
||||
description: "Share & download thousands of ComfyUI workflows on <a style='color:var(--input-text);' href='https://www.esheep.com' target='_blank'>esheep.com</a>",
|
||||
onclick: () => {
|
||||
shareToEsheep();
|
||||
this.close();
|
||||
@@ -336,7 +336,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
key: "Copus",
|
||||
textContent: "Copus",
|
||||
website: "https://www.copus.io",
|
||||
description: "🔴 Permanently store and secure ownership of your workflow on the open-source platform: <a style='color:white;' href='https://copus.io' target='_blank'>Copus.io</a>",
|
||||
description: "🔴 Permanently store and secure ownership of your workflow on the open-source platform: <a style='color:var(--input-text);' href='https://copus.io' target='_blank'>Copus.io</a>",
|
||||
onclick: () => {
|
||||
showCopusShareDialog();
|
||||
this.close();
|
||||
@@ -382,7 +382,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
innerHTML: b.description,
|
||||
style: {
|
||||
'text-align': 'left',
|
||||
color: 'white',
|
||||
color: 'var(--input-text)',
|
||||
'font-size': '14px',
|
||||
'margin-bottom': '0',
|
||||
},
|
||||
@@ -393,7 +393,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
href: b.website,
|
||||
target: "_blank",
|
||||
style: {
|
||||
color: 'white',
|
||||
color: 'var(--input-text)',
|
||||
'margin-left': '10px',
|
||||
'font-size': '12px',
|
||||
'text-decoration': 'none',
|
||||
@@ -440,7 +440,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
textContent: 'Choose a platform to share your workflow',
|
||||
style: {
|
||||
'text-align': 'center',
|
||||
'color': 'white',
|
||||
'color': 'var(--input-text)',
|
||||
'font-size': '18px',
|
||||
'margin-bottom': '10px',
|
||||
},
|
||||
@@ -686,7 +686,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
$el("div", {}, [
|
||||
$el("p", {
|
||||
size: 3, color: "white", style: {
|
||||
color: 'white'
|
||||
color: 'var(--input-text)'
|
||||
}
|
||||
}, [`Select where to share your art:`]),
|
||||
this.matrix_destination_checkbox,
|
||||
@@ -701,7 +701,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
size: 3,
|
||||
color: "white",
|
||||
style: {
|
||||
color: 'white'
|
||||
color: 'var(--input-text)'
|
||||
}
|
||||
}, []),
|
||||
this.credits_input,
|
||||
@@ -712,7 +712,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
size: 3,
|
||||
color: "white",
|
||||
style: {
|
||||
color: 'white'
|
||||
color: 'var(--input-text)'
|
||||
}
|
||||
}, []),
|
||||
this.title_input,
|
||||
@@ -723,7 +723,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
size: 3,
|
||||
color: "white",
|
||||
style: {
|
||||
color: 'white'
|
||||
color: 'var(--input-text)'
|
||||
}
|
||||
}, []),
|
||||
this.description_input,
|
||||
@@ -989,7 +989,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
}
|
||||
const radio_button_text = $el("label", {
|
||||
// style: {
|
||||
// color: 'white'
|
||||
// color: 'var(--input-text)'
|
||||
// }
|
||||
}, [output.title])
|
||||
radio_button.style.color = "var(--fg-color)";
|
||||
@@ -1028,7 +1028,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
color: "white",
|
||||
style: {
|
||||
'text-align': 'center',
|
||||
color: 'white',
|
||||
color: 'var(--input-text)',
|
||||
backgroundColor: 'black',
|
||||
padding: '10px',
|
||||
'margin-top': '0px',
|
||||
@@ -1040,7 +1040,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
color: "white",
|
||||
style: {
|
||||
'text-align': 'center',
|
||||
color: 'white',
|
||||
color: 'var(--input-text)',
|
||||
'margin-bottom': '5px',
|
||||
'font-style': 'italic',
|
||||
'font-size': '12px',
|
||||
|
||||
@@ -199,7 +199,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
color: "white",
|
||||
style: {
|
||||
'text-align': 'center',
|
||||
color: 'white',
|
||||
color: 'var(--input-text)',
|
||||
margin: '0 0 10px 0',
|
||||
}
|
||||
});
|
||||
@@ -733,7 +733,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
size: 2,
|
||||
color: "white",
|
||||
style: {
|
||||
color: 'white',
|
||||
color: 'var(--input-text)',
|
||||
margin: '0 0 5px 0',
|
||||
fontSize: '12px',
|
||||
},
|
||||
|
||||
@@ -9,11 +9,13 @@ app.registerExtension({
|
||||
name: "Comfy.Manager.Terminal",
|
||||
|
||||
registerCustomNodes() {
|
||||
class TerminalNode {
|
||||
class TerminalNode extends LiteGraph.LGraphNode {
|
||||
color = "#222222";
|
||||
bgcolor = "#000000";
|
||||
groupcolor = LGraphCanvas.node_colors.black.groupcolor;
|
||||
constructor() {
|
||||
super();
|
||||
this.title = "Terminal Log (Manager)";
|
||||
this.logs = [];
|
||||
|
||||
if (!this.properties) {
|
||||
|
||||
920
model-list.json
920
model-list.json
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,539 @@
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"author": "jordancoult",
|
||||
"title": "ComfyUI_HelpfulNodes",
|
||||
"reference": "https://github.com/jordancoult/ComfyUI_HelpfulNodes",
|
||||
"files": [
|
||||
"https://github.com/jordancoult/ComfyUI_HelpfulNodes/raw/refs/heads/main/prep_crop_around_kps.py"
|
||||
],
|
||||
"install_type": "copy",
|
||||
"description": "NODES: Prepare Crop Around Keypoints"
|
||||
},
|
||||
{
|
||||
"author": "io-club",
|
||||
"title": "ComfyUI-LuminaNext [WIP]",
|
||||
"reference": "https://github.com/io-club/ComfyUI-LuminaNext",
|
||||
"files": [
|
||||
"https://github.com/io-club/ComfyUI-LuminaNext"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: GemmaClipLoader"
|
||||
},
|
||||
{
|
||||
"author": "shadowcz007",
|
||||
"title": "Comfyui-EzAudio",
|
||||
"reference": "https://github.com/shadowcz007/Comfyui-EzAudio",
|
||||
"files": [
|
||||
"https://github.com/shadowcz007/Comfyui-EzAudio"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: EZ Generate Audio, EZ Load Model\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "neo0801",
|
||||
"title": "my-comfy-node",
|
||||
"reference": "https://github.com/neo0801/my-comfy-node",
|
||||
"files": [
|
||||
"https://github.com/neo0801/my-comfy-node"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:Deep Mosaic Get Image Mosaic Mask, Deep Mosaic Get Video Mosaic Mask, Deep Mosaic Remove Image Mosaic, Deep Mosaic Remove Video Mosaic"
|
||||
},
|
||||
{
|
||||
"author": "nikkuexe",
|
||||
"title": "List Data Helper Nodes",
|
||||
"reference": "https://github.com/paulhoux/Smart-Prompting",
|
||||
"files": [
|
||||
"https://github.com/paulhoux/Smart-Prompting"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Custom nodes for ComfyUI, allowing you to more easily manipulate text and create good prompts.[w/The use of outdated front extension techniques results in remnants being left behind during uninstallation.]"
|
||||
},
|
||||
{
|
||||
"author": "nikkuexe",
|
||||
"title": "List Data Helper Nodes",
|
||||
"reference": "https://github.com/nikkuexe/ComfyUI-ListDataHelpers",
|
||||
"files": [
|
||||
"https://github.com/nikkuexe/ComfyUI-ListDataHelpers"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A set of custom nodes for handling lists in ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "Fannovel16",
|
||||
"title": "ComfyUI-AppIO",
|
||||
"reference": "https://github.com/Fannovel16/ComfyUI-AppIO",
|
||||
"files": [
|
||||
"https://github.com/Fannovel16/ComfyUI-AppIO"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:AppIO_StringInput, AppIO_ImageInput, AppIO_StringOutput, AppIO_ImageOutput"
|
||||
},
|
||||
{
|
||||
"author": "wilzamguerrero",
|
||||
"title": "Comfyui-DownZ",
|
||||
"reference": "https://github.com/wilzamguerrero/Comfyui-DownZ",
|
||||
"files": [
|
||||
"https://github.com/wilzamguerrero/Comfyui-DownZ"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:Download Z, Compress Z, Move Z"
|
||||
},
|
||||
{
|
||||
"author": "SoftMeng",
|
||||
"title": "ComfyUI-PIL",
|
||||
"reference": "https://github.com/SoftMeng/ComfyUI-PIL",
|
||||
"files": [
|
||||
"https://github.com/SoftMeng/ComfyUI-PIL"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "PIL Nodes"
|
||||
},
|
||||
{
|
||||
"author": "seancheung",
|
||||
"title": "comfyui-creative-nodes",
|
||||
"reference": "https://github.com/seancheung/comfyui-creative-nodes",
|
||||
"files": [
|
||||
"https://github.com/seancheung/comfyui-creative-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:Stop Flow, Skip From Flow, Skip To Flow, Resolution Selector, ResolutionXL Selector"
|
||||
},
|
||||
{
|
||||
"author": "AlexXi19",
|
||||
"title": "ComfyUI-OpenAINode",
|
||||
"reference": "https://github.com/AlexXi19/ComfyUI-OpenAINode",
|
||||
"files": [
|
||||
"https://github.com/AlexXi19/ComfyUI-OpenAINode"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI-OpenAINode is a user-friendly node that serves as an interface to the OpenAI Models.[w/Repo name conflict with Electrofried/ComfyUI-OpenAINode]"
|
||||
},
|
||||
{
|
||||
"author": "hgabha",
|
||||
"title": "WWAA-CustomNodes",
|
||||
"reference": "https://github.com/hgabha/WWAA-CustomNodes",
|
||||
"files": [
|
||||
"https://github.com/hgabha/WWAA-CustomNodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Custom Nodes for ComfyUI made by the team at [a/WeirdWonderfulAI.Art](https://weirdwonderfulai.art/) These are developed based on the needs where there was a gap to make our workflows better. You are welcome to use it as you see fit."
|
||||
},
|
||||
{
|
||||
"author": "IgPoly",
|
||||
"title": "ComfyUI-igTools",
|
||||
"reference": "https://github.com/IgPoly/ComfyUI-igTools",
|
||||
"files": [
|
||||
"https://github.com/IgPoly/ComfyUI-igTools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:IGT Simple Tiles Calc"
|
||||
},
|
||||
{
|
||||
"author": "Ryota",
|
||||
"title": "Ryota's Nodes",
|
||||
"reference": "https://github.com/lichenhao/Comfyui_Ryota",
|
||||
"files": [
|
||||
"https://github.com/lichenhao/Comfyui_Ryota"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:CombineTexts, FontLoader, DrawText, TxtFileLoader, SaveTxtFile, SwitchModelClip, SwitchAnyInputs, Reroute2, Reroute3"
|
||||
},
|
||||
{
|
||||
"author": "Soppatorsk",
|
||||
"title": "comfyui_img_to_ascii [WIP]",
|
||||
"reference": "https://github.com/Soppatorsk/comfyui_img_to_ascii",
|
||||
"files": [
|
||||
"https://github.com/Soppatorsk/comfyui_img_to_ascii"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Basic functionality for converting an image to ASCII art returned as a png image based on [a/ascii_magic](https://github.com/LeandroBarone/python-ascii_magic)"
|
||||
},
|
||||
{
|
||||
"author": "AIFSH",
|
||||
"title": "HivisionIDPhotos-ComfyUI",
|
||||
"reference": "https://github.com/AIFSH/HivisionIDPhotos-ComfyUI",
|
||||
"files": [
|
||||
"https://github.com/AIFSH/HivisionIDPhotos-ComfyUI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "a custom node for [a/HivisionIDPhotos](https://github.com/Zeyi-Lin/HivisionIDPhotos).\nNOTE: Unsuitable for international users"
|
||||
},
|
||||
{
|
||||
"author": "lu64k",
|
||||
"title": "SK-Nodes",
|
||||
"reference": "https://github.com/lu64k/SK-Nodes",
|
||||
"files": [
|
||||
"https://github.com/lu64k/SK-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:image select, Load AnyLLM, Ask LLM, OpenAI DAlle Node, SK Text_String, SK Random File Name"
|
||||
},
|
||||
{
|
||||
"author": "Cardoso-topdev",
|
||||
"title": "comfyui_meshanything_v1 [WIP]",
|
||||
"reference": "https://github.com/Cardoso-topdev/comfyui_meshanything_v1",
|
||||
"files": [
|
||||
"https://github.com/Cardoso-topdev/comfyui_meshanything_v1"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "MeshAnything V2: Artist-Created Mesh Generation With Adjacent Mesh Tokenization"
|
||||
},
|
||||
{
|
||||
"author": "Lilien86",
|
||||
"title": "lauger NodePack for ComfyUI",
|
||||
"reference": "https://github.com/Lilien86/lauger_NP_comfyui",
|
||||
"files": [
|
||||
"https://github.com/Lilien86/lauger_NP_comfyui"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Hey everyone it's my Custom ComfyUI Nodes Pack repository! This project contains a collection of custom nodes designed to extend the functionality of ComfyUI. These nodes offer capabilities and new creative possibilities, especially in the realms of latent space manipulation and interpolation.\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "haodman",
|
||||
"title": "ComfyUI_Rain",
|
||||
"reference": "https://github.com/haodman/ComfyUI_Rain",
|
||||
"files": [
|
||||
"https://github.com/haodman/ComfyUI_Rain"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:Rain_ValueSwitch, Rain_Math, Rain_IntToFloat, Rain_ImageSize."
|
||||
},
|
||||
{
|
||||
"author": "bananasss00",
|
||||
"title": "Comfyui-PyExec [UNSAFE]",
|
||||
"reference": "https://github.com/bananasss00/Comfyui-PyExec",
|
||||
"files": [
|
||||
"https://github.com/bananasss00/Comfyui-PyExec"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:PyExec.[w/This node allows access to arbitrary files through the workflow, which could pose a security threat.]"
|
||||
},
|
||||
{
|
||||
"author": "jgbrblmd",
|
||||
"title": "ComfyUI-ComfyFluxSize [WIP]",
|
||||
"reference": "https://github.com/jgbrblmd/ComfyUI-ComfyFluxSize",
|
||||
"files": [
|
||||
"https://github.com/jgbrblmd/ComfyUI-ComfyFluxSize"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:ComfyFlux Size\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "yojimbodayne",
|
||||
"title": "ComfyUI-Dropbox-API [WIP]",
|
||||
"reference": "https://github.com/yojimbodayne/ComfyUI-Dropbox-API",
|
||||
"files": [
|
||||
"https://github.com/yojimbodayne/ComfyUI-Dropbox-API"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This custom node package for ComfyUI allows users to interact with Dropbox API, enabling image, text, and video uploads, downloads, and management directly from ComfyUI workflows.\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "ilovejohnwhite",
|
||||
"title": "Kolors Awesome Prompts [WIP]",
|
||||
"reference": "https://github.com/ilovejohnwhite/Tracer",
|
||||
"files": [
|
||||
"https://github.com/ilovejohnwhite/Tracer"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:Image Load TTK, SuckerPunch, LinkMasterNode, PixelPerfectResolution, ImageGenResolutionFromImage, ImageGenResolutionFromLatent, HintImageEnchance\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "shuanshtalon468uan",
|
||||
"title": "ComfyUI-Rpg-Architect [WIP]",
|
||||
"reference": "https://github.com/talon468/ComfyUI-Rpg-Architect",
|
||||
"files": [
|
||||
"https://github.com/talon468/ComfyUI-Rpg-Architect"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Custom Node for ComfyUI to create RPG Characters\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "shuanshuan",
|
||||
"title": "ComfyUI_CheckPointLoader_Ext [WIP]",
|
||||
"reference": "https://github.com/shuanshuan/ComfyUI_CheckPointLoader_Ext",
|
||||
"files": [
|
||||
"https://github.com/shuanshuan/ComfyUI_CheckPointLoader_Ext"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:Checkpoint Loader Ext"
|
||||
},
|
||||
{
|
||||
"author": "123jimin",
|
||||
"title": "ComfyUI MobileForm [WIP]",
|
||||
"reference": "https://github.com/123jimin/ComfyUI-MobileForm",
|
||||
"files": [
|
||||
"https://github.com/123jimin/ComfyUI-MobileForm"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "MobileForm is an extension for ComfyUI, providing simple form for any workflows, suitable for use on mobile phones.[w/Currently MobileForm is in a PoC state; expect bugs and breaking changes.]"
|
||||
},
|
||||
{
|
||||
"author": "go-package-lab",
|
||||
"title": "ComfyUI-Tools-Video-Combine [WIP]",
|
||||
"reference": "https://github.com/go-package-lab/ComfyUI-Tools-Video-Combine",
|
||||
"files": [
|
||||
"https://github.com/go-package-lab/ComfyUI-Tools-Video-Combine"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:LoadAudioUrl, VideoWatermark"
|
||||
},
|
||||
{
|
||||
"author": "zhongpei",
|
||||
"title": "Comfyui_image2prompt",
|
||||
"id": "img2prompt",
|
||||
"reference": "https://github.com/zhongpei/Comfyui_image2prompt",
|
||||
"files": [
|
||||
"https://github.com/zhongpei/Comfyui_image2prompt"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:Image to Text, Loader Image to Text Model.[w/This custom node may break dependencies by reinstalling the torch package.]"
|
||||
},
|
||||
{
|
||||
"author": "APZmedia",
|
||||
"title": "comfyui-textools [WIP]",
|
||||
"reference": "https://github.com/APZmedia/comfyui-textools",
|
||||
"files": [
|
||||
"https://github.com/APZmedia/comfyui-textools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI-textools is a collection of custom nodes designed for use with ComfyUI. These nodes enhance text processing capabilities, including applying rich text overlays on images and cleaning file names for safe and consistent file management.\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-event-handler [USAFE]",
|
||||
"reference": "https://github.com/shinich39/comfyui-event-handler",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-event-handler"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Javascript code will run when an event fires. [w/This node allows you to execute arbitrary JavaScript code as input for the workflow.]"
|
||||
},
|
||||
{
|
||||
"author": "Comfy Org",
|
||||
"title": "ComfyUI_devtools [WIP]",
|
||||
"reference": "https://github.com/Comfy-Org/ComfyUI_devtools",
|
||||
"files": [
|
||||
"https://github.com/Comfy-Org/ComfyUI_devtools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI developer tools (Custom Node)"
|
||||
},
|
||||
{
|
||||
"author": "Sakura-nee",
|
||||
"title": "ComfyUI_Save2Discord",
|
||||
"reference": "https://github.com/Sakura-nee/ComfyUI_Save2Discord",
|
||||
"files": [
|
||||
"https://github.com/Sakura-nee/ComfyUI_Save2Discord"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:Send Generated Image To Discord Webhook.\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "ThisModernDay",
|
||||
"title": "ComfyUI Instructor Ollama",
|
||||
"reference": "https://github.com/ThisModernDay/ComfyUI-InstructorOllama",
|
||||
"files": [
|
||||
"https://github.com/ThisModernDay/ComfyUI-InstructorOllama"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Custom ComfyUI Nodes for interacting with Ollama using the Instructor. Library to provide structured output from your LLM. To use this properly, you would need a running Ollama server reachable from the host that is running ComfyUI.\nNOTE: The files in the repo are not organized, which may lead to update issues."
|
||||
},
|
||||
{
|
||||
"author": "gioferreira",
|
||||
"title": "ComfyUI-Molde-Utils",
|
||||
"reference": "https://github.com/gioferreira/ComfyUI-Molde-Utils",
|
||||
"files": [
|
||||
"https://github.com/gioferreira/ComfyUI-Molde-Utils"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI-Molde-Utils is a utility library designed to provide various helper functions for working with UI elements. This project includes modules for handling bezier curves and color conversions.\nNOTE: The files in the repo are not organized, which may lead to update issues."
|
||||
},
|
||||
{
|
||||
"author": "kijai",
|
||||
"title": "ComfyUI nodes for VEnhancer [WIP]",
|
||||
"reference": "https://github.com/kijai/ComfyUI-VEnhancer",
|
||||
"files": [
|
||||
"https://github.com/kijai/ComfyUI-VEnhancer"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Original repo: [a/https://github.com/Vchitect/VEnhancer](https://github.com/Vchitect/VEnhancer)"
|
||||
},
|
||||
{
|
||||
"author": "jimstudt",
|
||||
"title": "Jim's ComfyUI Nodes [WIP]",
|
||||
"reference": "https://github.com/jimstudt/ComfyUI-Jims-Nodes",
|
||||
"files": [
|
||||
"https://github.com/jimstudt/ComfyUI-Jims-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Zoom and Enhance Nodes, Text Dictionary Nodes"
|
||||
},
|
||||
{
|
||||
"author": "hananbeer",
|
||||
"title": "node_dev - ComfyUI Node Development Helper",
|
||||
"reference": "https://github.com/hananbeer/node_dev",
|
||||
"files": [
|
||||
"https://github.com/hananbeer/node_dev"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Browse to this endpoint to reload custom nodes for more streamlined development:\nhttp://127.0.0.1:8188/node_dev/reload/<module_name>"
|
||||
},
|
||||
{
|
||||
"author": "Weixuanf",
|
||||
"title": "ComfyUI extra model folder helper [WIP]",
|
||||
"reference": "https://github.com/Weixuanf/extra-model-helper",
|
||||
"files": [
|
||||
"https://github.com/Weixuanf/extra-model-helper"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "this will automaticlaly read the subfolders like 'checkpoints', 'loras' under the extra model folder path you specify in extra_model_paths.yaml and add them to folder paths, so you don't need to define those subfolders one by one.\nNOTE: invalid pyproject.toml"
|
||||
},
|
||||
{
|
||||
"author": "Weixuanf",
|
||||
"title": "ComfyUI File Manager for nodecafe.co [WIP]",
|
||||
"reference": "https://github.com/Weixuanf/nodecafe-file-manager",
|
||||
"files": [
|
||||
"https://github.com/Weixuanf/nodecafe-file-manager"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "To view and download files in ComfyUI\nNOTE: invalid pyproject.toml"
|
||||
},
|
||||
{
|
||||
"author": "sebord",
|
||||
"title": "ComfyUI-LMCQ [WIP]",
|
||||
"reference": "https://github.com/sebord/ComfyUI-LMCQ",
|
||||
"files": [
|
||||
"https://github.com/sebord/ComfyUI-LMCQ"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI small node toolkit, this toolkit is mainly to update some practical small nodes, to make a contribution to the comfyui ecosystem, PS: 'LMCQ' is the abbreviation of the team name\nNOTE: The files in the repo are not organized, which may lead to update issues."
|
||||
},
|
||||
{
|
||||
"author": "ChrisColeTech",
|
||||
"title": "ComfyUI-Get-Random-File [UNSAFE]",
|
||||
"reference": "https://github.com/ChrisColeTech/ComfyUI-Get-Random-File",
|
||||
"files": [
|
||||
"https://github.com/ChrisColeTech/ComfyUI-Get-Random-File"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Gets a random file from a directory. Returns the filpath as a STRING. [w/This node allows access to arbitrary files through the workflow, which could pose a security threat.]"
|
||||
},
|
||||
{
|
||||
"author": "logtd",
|
||||
"title": "ComfyUI-Fluxtapoz [WIP]",
|
||||
"reference": "https://github.com/logtd/ComfyUI-Fluxtapoz",
|
||||
"files": [
|
||||
"https://github.com/logtd/ComfyUI-Fluxtapoz"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A set of nodes for editing images using Flux in ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "neeltheninja",
|
||||
"title": "ComfyUI-ControlNeXt [WIP]",
|
||||
"reference": "https://github.com/neverbiasu/ComfyUI-ControlNeXt",
|
||||
"files": [
|
||||
"https://github.com/neverbiasu/ComfyUI-ControlNeXt"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "In progress🚧"
|
||||
},
|
||||
{
|
||||
"author": "neeltheninja",
|
||||
"title": "ComfyUI-TextOverlay",
|
||||
"reference": "https://github.com/neeltheninja/ComfyUI-TextOverlay",
|
||||
"files": [
|
||||
"https://github.com/neeltheninja/ComfyUI-TextOverlay"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A custom node for ComfyUI that adds text overlay to images, with options for text and background color, opacity, vertical positioning, and custom font selection. [w/Name conflict with munkyfoot/ComfyUI-TextOverlay. Cannot install simulatenously.]"
|
||||
},
|
||||
{
|
||||
"author": "comfyanonymous",
|
||||
"title": "ComfyUI_bitsandbytes_NF4 [EXPERIMENTAL]",
|
||||
"reference": "https://github.com/comfyanonymous/ComfyUI_bitsandbytes_NF4",
|
||||
"files": [
|
||||
"https://github.com/comfyanonymous/ComfyUI_bitsandbytes_NF4"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A quickly written custom node that uses code from Forge to support the nf4 flux dev checkpoint and nf4 flux schnell checkpoint.\nRequires installing bitsandbytes.\nMake sure your ComfyUI is updated.\nThe node is: CheckpointLoaderNF4, just plug it in your flux workflow instead of the regular one.[w/NF4 checckpoint doesn't support LoRA.]"
|
||||
},
|
||||
{
|
||||
"author": "kijai",
|
||||
"title": "ComfyUI-EasyAnimateWrapper [WIP]",
|
||||
"reference": "https://github.com/kijai/ComfyUI-EasyAnimateWrapper",
|
||||
"files": [
|
||||
"https://github.com/kijai/ComfyUI-EasyAnimateWrapper"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "EasyAnimateWrapper for ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "logtd",
|
||||
"title": "ComfyUI-Veevee [WIP]",
|
||||
"reference": "https://github.com/logtd/ComfyUI-Veevee",
|
||||
"files": [
|
||||
"https://github.com/logtd/ComfyUI-Veevee"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A Video2Video framework for text2image models in ComfyUI. Supports SD1.5 and SDXL."
|
||||
},
|
||||
{
|
||||
"author": "IuvenisSapiens",
|
||||
"title": "ComfyUI_MiniCPM-V-2_6-int4",
|
||||
"id": "minicpm-v-2_6-int4",
|
||||
"reference": "https://github.com/IuvenisSapiens/ComfyUI_MiniCPM-V-2_6-int4",
|
||||
"files": [
|
||||
"https://github.com/IuvenisSapiens/ComfyUI_MiniCPM-V-2_6-int4"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is an implementation of [a/MiniCPM-V-2_6-int4](https://github.com/OpenBMB/MiniCPM-V) by [a/ComfyUI](https://github.com/comfyanonymous/ComfyUI), including support for text-based queries, video queries, single-image queries, and multi-image queries to generate captions or responses."
|
||||
},
|
||||
{
|
||||
"author": "chrisdreid",
|
||||
"title": "ComfyUI_EnvAutopsyAPI [UNSAFE]",
|
||||
"reference": "https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI",
|
||||
"files": [
|
||||
"https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI_EnvAutopsyAPI is a powerful debugging tool designed for ComfyUI that provides in-depth analysis of your environment and dependencies through an API interface. This tool allows you to inspect environment variables, pip packages, and dependency trees, making it easier to diagnose and resolve issues in your ComfyUI setup.[w/This tool may expose sensitive system information if used on a public server. MUST READ [a/THIS](https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI#%EF%B8%8F-warning-security-risk-%EF%B8%8F) before install.]"
|
||||
},
|
||||
{
|
||||
"author": "neuratech-ai",
|
||||
"title": "ComfyUI-MultiGPU",
|
||||
"reference": "https://github.com/neuratech-ai/ComfyUI-MultiGPU",
|
||||
"files": [
|
||||
"https://github.com/neuratech-ai/ComfyUI-MultiGPU"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Experimental nodes for using multiple GPUs in a single ComfyUI workflow.\nThis extension adds new nodes for model loading that allow you to specify the GPU to use for each model. It monkey patches the memory management of ComfyUI in a hacky way and is neither a comprehensive solution nor a well-tested one. Use at your own risk.\nNote that this does not add parallelism. The workflow steps are still executed sequentially just on different GPUs. Any potential speedup comes from not having to constantly load and unload models from VRAM."
|
||||
},
|
||||
{
|
||||
"author": "Isi-dev",
|
||||
"title": "Isi-dev/ComfyUI-UniAnimate",
|
||||
"reference": "https://github.com/Isi-dev/ComfyUI-UniAnimate",
|
||||
"files": [
|
||||
"https://github.com/Isi-dev/ComfyUI-UniAnimate"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is my ComfyUi-windows implementation for the image animation project ▶ UniAnimate: Taming Unified Video Diffusion Models for Consistent Human Image Animation[w/This node cannot be installed simultaneously with ComfyUI-UniAnimate by AIFSH because it has the same name as that custom node.]"
|
||||
},
|
||||
{
|
||||
"author": "Futureversecom",
|
||||
"title": "ComfyUI-JEN",
|
||||
"reference": "https://github.com/futureversecom/ComfyUI-JEN",
|
||||
"files": [
|
||||
"https://github.com/futureversecom/ComfyUI-JEN"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Comfy UI custom nodes for JEN music generation powered by Futureverse"
|
||||
},
|
||||
{
|
||||
"author": "denislov",
|
||||
"title": "Comfyui_AutoSurvey",
|
||||
@@ -32,16 +565,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "This node allows ComfyUI to easily integrate with Midjourney, utilizing the ultra-high quality of Midjourney and the powerful control of SD to provide more convenient capabilities for AIGC.\nNOTE: This node relies on the midjourney proxy project and requires API deployment in advance. For detailed installation, please refer to the instructions of the project. https://github.com/novicezk/midjourney-proxy"
|
||||
},
|
||||
{
|
||||
"author": "hy134300",
|
||||
"title": "ComfyUI-PhotoMaker-V2",
|
||||
"reference": "https://github.com/hy134300/ComfyUI-PhotoMaker-V2",
|
||||
"files": [
|
||||
"https://github.com/hy134300/ComfyUI-PhotoMaker-V2"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes for PhotoMaker-V2"
|
||||
},
|
||||
{
|
||||
"author": "kijai",
|
||||
"title": "ComfyUI-FollowYourEmojiWrapper [WIP]",
|
||||
@@ -62,16 +585,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "[a/SadTalker](https://github.com/OpenTalker/SadTalker) for ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "maepopi",
|
||||
"title": "Diffusers-in-ComfyUI",
|
||||
"reference": "https://github.com/maepopi/Diffusers-in-ComfyUI",
|
||||
"files": [
|
||||
"https://github.com/maepopi/Diffusers-in-ComfyUI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is a custom node allowing the use of the diffusers pipeline into ComfyUI. Based on [a/Limitex/ComfyUI-Diffusers](https://github.com/Limitex/ComfyUI-Diffusers)"
|
||||
},
|
||||
{
|
||||
"author": "hotpizzatactics",
|
||||
"title": "ComfyUI-WaterMark-Detector",
|
||||
@@ -83,16 +596,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:CLAHE Enhancement, High Pass Filter, Edge Detection, Combine Enhancements, Adaptive Thresholding, Morphological Operations, Gray Color Enhancement, Improved Gray Color Enhancement, Texture Enhancement, Denoising Filter, Flexible Combine Enhancements."
|
||||
},
|
||||
{
|
||||
"author": "drmbt",
|
||||
"title": "comfyui-dreambait-nodes",
|
||||
"reference": "https://github.com/drmbt/comfyui-dreambait-nodes",
|
||||
"files": [
|
||||
"https://github.com/drmbt/comfyui-dreambait-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:Aspect Pad Image For Outpainting"
|
||||
},
|
||||
{
|
||||
"author": "AIFSH",
|
||||
"title": "IMAGDressing-ComfyUI",
|
||||
@@ -248,16 +751,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "If you wish to incorporate these changes into your repo, feel free to open an issue and ask. The commits should be pretty clear, and I also label almost all changes with #HACK so a full text search will work too.\nPlease let me know if you decide to incorporate any of these changes into your LivePortrait implementation so I can direct people to you repository. I do not intend to maintain this repo.\nSome operations are simply not supported on MPS and I didn't rewrite them. Most of my changes are just to .cuda calls and that sort of thing. Many operations are still done on CPU, so don't expect awesome performance."
|
||||
},
|
||||
{
|
||||
"author": "justUmen",
|
||||
"title": "Bjornulf_custom_nodes [WIP]",
|
||||
"reference": "https://github.com/justUmen/Bjornulf_custom_nodes",
|
||||
"files": [
|
||||
"https://github.com/justUmen/Bjornulf_custom_nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: ollamaLoader, ShowText, ShowInt, LoopTexts, LoopFloat, LoopInteger, ..."
|
||||
},
|
||||
{
|
||||
"author": "thderoo",
|
||||
"title": "_topfun_s_nodes",
|
||||
@@ -278,16 +771,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "This repository provides developers with a way to better manage their ComfyUI model memory. It includes nodes that allow developers to either unload all models or unload one model at a time. These nodes are designed as pass-through nodes, so they can be used anywhere in the flow. The nodes can be found in the 'Unload Model' section.[w/These are massive hammers, and it could be possible to break things, please don't use them if you need finesse.]"
|
||||
},
|
||||
{
|
||||
"author": "GeekyGhost",
|
||||
"title": "ComfyUI-GeekyRemB v2",
|
||||
"reference": "https://github.com/GeekyGhost/ComfyUI-GeekyRemB",
|
||||
"files": [
|
||||
"https://github.com/GeekyGhost/ComfyUI-GeekyRemB/raw/SketchUITest/GeekyRembv2.py"
|
||||
],
|
||||
"install_type": "copy",
|
||||
"description": "GeekyRemB Node Description: GeekyRemB is a powerful and versatile image processing node for ComfyUI, designed to remove backgrounds from images with advanced customization options. This node leverages the rembg library and offers a wide range of features for fine-tuning the background removal process and enhancing the resulting images."
|
||||
},
|
||||
{
|
||||
"author": "AIFSH",
|
||||
"title": "ComfyUI-OpenDIT [WIP]",
|
||||
@@ -310,16 +793,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Custom ComfyUI nodes to run [a/fal-ai/AuraSR](https://huggingface.co/fal-ai/AuraSR) model.[w/This node cannot be installed simultaneously with AIFSH/ComfyUI-AuraSR due to overlapping repository names.]"
|
||||
},
|
||||
{
|
||||
"author": "m-ai-studio",
|
||||
"title": "mai-prompt-progress",
|
||||
"reference": "https://github.com/m-ai-studio/mai-prompt-progress",
|
||||
"files": [
|
||||
"https://github.com/m-ai-studio/mai-prompt-progress"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "mai-prompt-progress"
|
||||
},
|
||||
{
|
||||
"author": "linhusyung",
|
||||
"title": "ComfyUI Build and Train Your Network [WIP]",
|
||||
@@ -700,17 +1173,6 @@
|
||||
"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",
|
||||
@@ -744,17 +1206,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Fixed AttentionCouple/NegPip(negative weights in prompts), more CFG++ samplers, etc."
|
||||
},
|
||||
{
|
||||
"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",
|
||||
@@ -775,7 +1226,7 @@
|
||||
"https://github.com/bruce007lee/comfyui-tiny-utils"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:FaceAlign, FaceAlignImageProcess, FaceAlignMaskProcess"
|
||||
"description": "Nodes:FaceAlign, FaceAlignImageProcess, FaceAlignMaskProcess, ImageFillColorByMask, CropImageByMask, LoadImageAdvance, ImageTransposeAdvance, ImageSAMMask"
|
||||
},
|
||||
{
|
||||
"author": "brycegoh",
|
||||
@@ -798,16 +1249,6 @@
|
||||
"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",
|
||||
@@ -979,16 +1420,6 @@
|
||||
"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]",
|
||||
@@ -1181,16 +1612,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Experimental method to use reference video to drive motion in generations without training in ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "logtd",
|
||||
"title": "ComfyUI-MotionThiefExperiment",
|
||||
"reference": "https://github.com/logtd/ComfyUI-MotionThiefExperiment",
|
||||
"files": [
|
||||
"https://github.com/logtd/ComfyUI-MotionThiefExperiment"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:This is an experimental node pack to test using reference videos for their motion.\nIt isn't compatible with a lot of things as this is a hacky implementation for experiments only."
|
||||
},
|
||||
{
|
||||
"author": "hy134300",
|
||||
"title": "comfyui-hb-node",
|
||||
@@ -1601,16 +2022,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "A simple custom node that unloads all models. Useful for developers or users who want to free some memory."
|
||||
},
|
||||
{
|
||||
"author": "AIGODLIKE",
|
||||
"title": "AIGODLIKE/ComfyUI-Model-Manager [WIP]",
|
||||
"reference": "https://github.com/AIGODLIKE/ComfyUI-Studio",
|
||||
"files": [
|
||||
"https://github.com/AIGODLIKE/ComfyUI-Studio"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "WIP"
|
||||
},
|
||||
{
|
||||
"author": "MrAdamBlack",
|
||||
"title": "CheckProgress [WIP]",
|
||||
@@ -1631,16 +2042,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "This answers the itch for being able to easily paste [a/CivitAI.com](https://civitai.com/) generated data (or other simple metadata) into Comfy in a way that makes it easy to test with multiple checkpoints."
|
||||
},
|
||||
{
|
||||
"author": "ZHO-ZHO-ZHO",
|
||||
"title": "ComfyUI-AnyText [WIP]",
|
||||
"reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-AnyText",
|
||||
"files": [
|
||||
"https://github.com/ZHO-ZHO-ZHO/ComfyUI-AnyText"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Unofficial implementation of [a/AnyText](https://github.com/tyxsspa/AnyText/tree/825bcc54687206b15bd7e28ba1a8b095989d58e3) for ComfyUI(EXP)"
|
||||
},
|
||||
{
|
||||
"author": "nidefawl",
|
||||
"title": "ComfyUI-nidefawl [UNSAFE]",
|
||||
@@ -1731,16 +2132,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Node Jumper. Various quality of life testing nodes"
|
||||
},
|
||||
{
|
||||
"author": "ilovejohnwhite",
|
||||
"title": "TatToolkit",
|
||||
"reference": "https://github.com/ilovejohnwhite/UncleBillyGoncho",
|
||||
"files": [
|
||||
"https://github.com/ilovejohnwhite/UncleBillyGoncho"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:UWU TTK Preprocessor, Pixel Perfect Resolution, Generation Resolution From Image, Generation Resolution From Latent, Enchance And Resize Hint Images, ..."
|
||||
},
|
||||
{
|
||||
"author": "IvanZhd",
|
||||
"title": "comfyui-codeformer [WIP]",
|
||||
@@ -1791,16 +2182,6 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: CheckpointVAELoaderSimpleText, CheckpointVAESelectorText, LoRA_Tag_To_Stack"
|
||||
},
|
||||
{
|
||||
"author": "dnl13",
|
||||
"title": "ComfyUI-dnl13-seg",
|
||||
"reference": "https://github.com/dnl13/ComfyUI-dnl13-seg",
|
||||
"files": [
|
||||
"https://github.com/dnl13/ComfyUI-dnl13-seg"
|
||||
],
|
||||
"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": "Brandelan",
|
||||
"title": "ComfyUI_bd_customNodes",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,25 @@
|
||||
{
|
||||
"custom_nodes": [
|
||||
{
|
||||
"author": "jags111",
|
||||
"title": "NyaamZ/ComfyUI-Long-CLIP",
|
||||
"reference": "https://github.com/NyaamZ/efficiency-nodes-ED",
|
||||
"files": [
|
||||
"https://github.com/NyaamZ/efficiency-nodes-ED"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This forked repo supports efficiency-nodes-comfyui. Additional features."
|
||||
},
|
||||
{
|
||||
"author": "SeaArtLab",
|
||||
"title": "zer0int/ComfyUI-Long-CLIP",
|
||||
"reference": "https://github.com/zer0int/ComfyUI-Long-CLIP",
|
||||
"files": [
|
||||
"https://github.com/zer0int/ComfyUI-Long-CLIP"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This forked repo supports FLUX.1 not only SD1.5, SDXL."
|
||||
},
|
||||
{
|
||||
"author": "meimeilook",
|
||||
"title": "ComfyUI_IPAdapter_plus.old [backward compatbility]",
|
||||
|
||||
@@ -10,6 +10,202 @@
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
"author": "AIFSH",
|
||||
"title": "SeedVC-ComfyUI [REMOVED]",
|
||||
"reference": "https://github.com/AIFSH/SeedVC-ComfyUI",
|
||||
"files": [
|
||||
"https://github.com/AIFSH/SeedVC-ComfyUI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "a custom node for [a/seed-vc](https://github.com/Plachtaa/seed-vc)"
|
||||
},
|
||||
{
|
||||
"author": "jazhang00",
|
||||
"title": "ComfyUI Node for Slicedit [REMOVED]",
|
||||
"reference": "https://github.com/jazhang00/ComfyUI-Slicedit",
|
||||
"files": [
|
||||
"https://github.com/jazhang00/ComfyUI-Slicedit"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Slicedit main page: [a/https://matankleiner.github.io/slicedit/](https://matankleiner.github.io/slicedit/). Use Slicedit with ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "rklaffehn",
|
||||
"title": "rk-comfy-nodes [REMOVED]",
|
||||
"id": "rknodes",
|
||||
"reference": "https://github.com/rklaffehn/rk-comfy-nodes",
|
||||
"files": [
|
||||
"https://github.com/rklaffehn/rk-comfy-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: RK_CivitAIMetaChecker, RK_CivitAIAddHashes."
|
||||
},
|
||||
{
|
||||
"author": "Extraltodeus",
|
||||
"title": "CLIP-Token-Injection [REMOVED]",
|
||||
"reference": "https://github.com/Extraltodeus/CLIP-Token-Injection",
|
||||
"files": [
|
||||
"https://github.com/Extraltodeus/CLIP-Token-Injection"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "These nodes are to edit the text vectors of CLIP models, so to customize how the prompts will be interpreted. You could see it as either customisation, 'one token prompt' up to some limitation and a way to mess with how the text will be interpreted. The edited CLIP can then be saved, or as well the edited tokens themselves. The shared example weights does not contain any image-knowledge but the text vector of the words affected."
|
||||
},
|
||||
{
|
||||
"author": "openart",
|
||||
"title": "openart-comfyui-deploy [REMOVED]",
|
||||
"id": "openart-comfyui-deploy",
|
||||
"reference": "https://github.com/kulsisme/openart-comfyui-deploy",
|
||||
"files": [
|
||||
"https://github.com/kulsisme/openart-comfyui-deploy"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: External Boolean (ComfyUI Deploy), External Checkpoint (ComfyUI Deploy), External Image (ComfyUI Deploy), External Video (ComfyUI Deploy x VHS), OpenArt Text, Image Websocket Output (ComfyDeploy), ..."
|
||||
},
|
||||
{
|
||||
"author": "mittimi",
|
||||
"title": "ComfyUI_mittimiLoadPreset [DEPRECATED]",
|
||||
"id": "comfyui-mittimi-load-preset",
|
||||
"reference": "https://github.com/mittimi/ComfyUI_mittimiLoadPreset",
|
||||
"files": [
|
||||
"https://github.com/mittimi/ComfyUI_mittimiLoadPreset"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "The system selects and loads preset."
|
||||
},
|
||||
{
|
||||
"author": "jinljin",
|
||||
"title": "ComfyUI-Talking-Head [REMOVED]",
|
||||
"reference": "https://github.com/jinljin/ComfyUI-ElevenlabsAndDID-Combine",
|
||||
"files": [
|
||||
"https://github.com/jinljin/ComfyUI-ElevenlabsAndDID-Combine"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI-Talking-Head"
|
||||
},
|
||||
{
|
||||
"author": "yvann-ba",
|
||||
"title": "ComfyUI_Yvann-Nodes [REMOVED]",
|
||||
"reference": "https://github.com/yvann-ba/ComfyUI_Yvann-Nodes",
|
||||
"files": [
|
||||
"https://github.com/yvann-ba/ComfyUI_Yvann-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Audio reactivity nodes for AI animations 🔊 Analyze audio, extract drums and vocals. Generate reactive masks and weights. Create audio-driven visuals. Produce weight graphs and audio masks. Compatible with IPAdapter, ControlNets and more. Features audio scheduling and waveform analysis. Tutorials to use this pack: [a/Yvann Youtube](https://www.youtube.com/@yvann.mp4)"
|
||||
},
|
||||
{
|
||||
"author": "jh-leon-kim",
|
||||
"title": "ComfyUI-JHK-utils [REMOVED]",
|
||||
"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": "ilovejohnwhite",
|
||||
"title": "TatToolkit [REMOVED]",
|
||||
"reference": "https://github.com/ilovejohnwhite/UncleBillyGoncho",
|
||||
"files": [
|
||||
"https://github.com/ilovejohnwhite/UncleBillyGoncho"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:UWU TTK Preprocessor, Pixel Perfect Resolution, Generation Resolution From Image, Generation Resolution From Latent, Enchance And Resize Hint Images, ..."
|
||||
},
|
||||
{
|
||||
"author": "hzchet",
|
||||
"title": "ComfyUI_QueueGeneration [REMOVED]",
|
||||
"reference": "https://github.com/hzchet/ComfyUI_QueueGeneration",
|
||||
"files": [
|
||||
"https://github.com/hzchet/ComfyUI_QueueGeneration"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:Queue Img2Vid Generation"
|
||||
},
|
||||
{
|
||||
"author": "ader47",
|
||||
"title": "ComfyUI-MeshHamer [REMOVED]",
|
||||
"reference": "https://github.com/ader47/comfyui_meshhamer",
|
||||
"files": [
|
||||
"https://github.com/ader47/comfyui_meshhamer"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:MeshHamer Hand Refiner. See also: [a/HaMeR: Hand Mesh Recovery](https://github.com/geopavlakos/hamer/tree/main)"
|
||||
},
|
||||
{
|
||||
"author": "SEkINVR",
|
||||
"title": "ComfyUI-Animator",
|
||||
"reference": "https://github.com/SEkINVR/ComfyUI-Animator [REMOVED]",
|
||||
"files": [
|
||||
"https://github.com/SEkINVR/ComfyUI-Animator"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This custom node for ComfyUI provides full-body animation capabilities, including facial rigging, various lighting styles, and green screen output."
|
||||
},
|
||||
{
|
||||
"author": "m-ai-studio",
|
||||
"title": "mai-prompt-progress [REMOVED]",
|
||||
"reference": "https://github.com/m-ai-studio/mai-prompt-progress",
|
||||
"files": [
|
||||
"https://github.com/m-ai-studio/mai-prompt-progress"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "mai-prompt-progress"
|
||||
},
|
||||
{
|
||||
"author": "ZHO-ZHO-ZHO",
|
||||
"title": "ComfyUI-AnyText [NOT MAINTAINED]",
|
||||
"reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-AnyText",
|
||||
"files": [
|
||||
"https://github.com/ZHO-ZHO-ZHO/ComfyUI-AnyText"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Unofficial implementation of [a/AnyText](https://github.com/tyxsspa/AnyText/tree/825bcc54687206b15bd7e28ba1a8b095989d58e3) for ComfyUI(EXP)"
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-pkg39 [DEPRECATED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-pkg39",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-pkg39"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This package has created for generate image from generated image and embedded workflow."
|
||||
},
|
||||
{
|
||||
"author": "dnl13",
|
||||
"title": "ComfyUI-dnl13-seg [DEPRECATED]",
|
||||
"reference": "https://github.com/dnl13/ComfyUI-dnl13-seg",
|
||||
"files": [
|
||||
"https://github.com/dnl13/ComfyUI-dnl13-seg"
|
||||
],
|
||||
"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": "1038lab",
|
||||
"title": "ComfyUI-latentSizeSelector [REMOVED]",
|
||||
"id": "ComfyUI-latentSizeSelector",
|
||||
"reference": "https://github.com/1038lab/ComfyUI_LatentSizeSelector",
|
||||
"files": [
|
||||
"https://github.com/1038lab/ComfyUI_LatentSizeSelector"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "You'll get a new node Latent Size Selector, you can pick the x and y sizes from a list."
|
||||
},
|
||||
{
|
||||
"author": "hy134300",
|
||||
"title": "ComfyUI-PhotoMaker-V2 [REMOVED]",
|
||||
"reference": "https://github.com/hy134300/ComfyUI-PhotoMaker-V2",
|
||||
"files": [
|
||||
"https://github.com/hy134300/ComfyUI-PhotoMaker-V2"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes for PhotoMaker-V2"
|
||||
},
|
||||
{
|
||||
"author": "neverbiasu",
|
||||
"title": "ComfyUI ImageCaptioner [REMOVED]",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -66,10 +66,10 @@
|
||||
"\n",
|
||||
"!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 einops transformers>=4.28.1 safetensors>=0.4.2 aiohttp pyyaml Pillow scipy tqdm psutil tokenizers>=0.13.3\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",
|
||||
"!pip3 install kornia>=0.7.1 spandrel soundfile sentencepiece\n",
|
||||
"\n",
|
||||
"if OPTIONS['USE_COMFYUI_MANAGER']:\n",
|
||||
" %cd custom_nodes\n",
|
||||
@@ -228,8 +228,8 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n",
|
||||
"!dpkg -i cloudflared-linux-amd64.deb\n",
|
||||
"!wget -P ~ https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n",
|
||||
"!dpkg -i ~/cloudflared-linux-amd64.deb\n",
|
||||
"\n",
|
||||
"import subprocess\n",
|
||||
"import threading\n",
|
||||
@@ -369,4 +369,4 @@
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import cm_global
|
||||
|
||||
security_check.security_check()
|
||||
|
||||
cm_global.pip_blacklist = ['torch', 'torchsde', 'torchvision']
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
|
||||
|
||||
@@ -448,12 +449,15 @@ def is_installed(name):
|
||||
if name.startswith('#'):
|
||||
return True
|
||||
|
||||
pattern = r'([^<>!=]+)([<>!=]=?)(.*)'
|
||||
pattern = r'([^<>!=]+)([<>!=]=?)([0-9.a-zA-Z]*)'
|
||||
match = re.search(pattern, name)
|
||||
|
||||
if match:
|
||||
name = match.group(1)
|
||||
|
||||
if name in cm_global.pip_blacklist:
|
||||
return True
|
||||
|
||||
if name in cm_global.pip_downgrade_blacklist:
|
||||
pips = get_installed_packages()
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-manager"
|
||||
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
||||
version = "2.48.4"
|
||||
version = "2.51.1"
|
||||
license = { file = "LICENSE.txt" }
|
||||
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions"]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user