Compare commits
169 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14b82d97ae | ||
|
|
c3eed981c0 | ||
|
|
bbb54d4a08 | ||
|
|
4566c585db | ||
|
|
a946338a18 | ||
|
|
0a60a44478 | ||
|
|
cef0ad6707 | ||
|
|
7176f0837a | ||
|
|
6b1f2b2d9d | ||
|
|
38a1a9b320 | ||
|
|
402e2c384f | ||
|
|
9d5faa096c | ||
|
|
97d0dc20f1 | ||
|
|
8d99ff07b6 | ||
|
|
04fa540a8c | ||
|
|
eb41867e04 | ||
|
|
eee5d7d9e8 | ||
|
|
e983f9ed35 | ||
|
|
8b16ef641b | ||
|
|
e87d616b7a | ||
|
|
2220f325fc | ||
|
|
b53ed47ccb | ||
|
|
39df2743fe | ||
|
|
3f729aaf03 | ||
|
|
b7324621e4 | ||
|
|
e8c782c8e1 | ||
|
|
9136505565 | ||
|
|
f406d728cc | ||
|
|
d649ca47c6 | ||
|
|
e8111527b4 | ||
|
|
2af66d7efc | ||
|
|
27706f37f6 | ||
|
|
3de17b2fa6 | ||
|
|
22ecb5de95 | ||
|
|
992b8b3cb5 | ||
|
|
bebc16d5a6 | ||
|
|
ddb719f866 | ||
|
|
0bd1bf2605 | ||
|
|
fd32ba4035 | ||
|
|
22f723b920 | ||
|
|
1248bd0413 | ||
|
|
c150eec2b6 | ||
|
|
c7248c2d47 | ||
|
|
e71e68e298 | ||
|
|
6969557693 | ||
|
|
f6be5ad839 | ||
|
|
cebe3664fd | ||
|
|
cdab465c90 | ||
|
|
144384655c | ||
|
|
0e213d6dab | ||
|
|
21294a4e4a | ||
|
|
3ba4d44d9e | ||
|
|
1f86ef5a37 | ||
|
|
fac60da333 | ||
|
|
5a5a37dfee | ||
|
|
0d487bc14f | ||
|
|
a52b4eb5ed | ||
|
|
f1b7f5f52f | ||
|
|
5ef58652bf | ||
|
|
e26a9e75c6 | ||
|
|
b0035ff4a7 | ||
|
|
94b6f9b2fe | ||
|
|
cad1482b3f | ||
|
|
ea7aafb3e6 | ||
|
|
42b15ad4a5 | ||
|
|
d3d613cca9 | ||
|
|
86893d999a | ||
|
|
4fd17b0bf5 | ||
|
|
76d2206058 | ||
|
|
51e8b608dc | ||
|
|
a68330fb8f | ||
|
|
2449ad5c69 | ||
|
|
064c812df3 | ||
|
|
48d5ec9e66 | ||
|
|
914419fd1e | ||
|
|
f005fc8ca0 | ||
|
|
43b7960de2 | ||
|
|
2ed1e58032 | ||
|
|
c63b212700 | ||
|
|
e9df78c0e7 | ||
|
|
b0daf81185 | ||
|
|
cee4fdcbb0 | ||
|
|
df3cdfccb0 | ||
|
|
894042cd0e | ||
|
|
8123287952 | ||
|
|
bc677705d8 | ||
|
|
5dd8ea8aab | ||
|
|
41172be796 | ||
|
|
ad1b4a9a86 | ||
|
|
e0e3ec02b3 | ||
|
|
a6cc392473 | ||
|
|
36f48b8656 | ||
|
|
3d883ca37d | ||
|
|
34ed81ca64 | ||
|
|
a9e0880572 | ||
|
|
9500e1c3c4 | ||
|
|
d81aa9cbbc | ||
|
|
21d4b25c2d | ||
|
|
0080783a11 | ||
|
|
2c3f44a3f8 | ||
|
|
3ddf414097 | ||
|
|
59fb63f1f7 | ||
|
|
fa1b514440 | ||
|
|
1579c58876 | ||
|
|
153d044331 | ||
|
|
f2496f7054 | ||
|
|
99022f4f3d | ||
|
|
60a5e4f261 | ||
|
|
661586d3b6 | ||
|
|
abc26cf906 | ||
|
|
12351bada7 | ||
|
|
a6816d53d7 | ||
|
|
3b0709f5f2 | ||
|
|
d7af7e2917 | ||
|
|
6516e62d33 | ||
|
|
6b832edd2f | ||
|
|
eebace1652 | ||
|
|
6ff6e05408 | ||
|
|
aaf569ca8c | ||
|
|
31eef6222e | ||
|
|
9963afa558 | ||
|
|
5b2e2fcf9d | ||
|
|
cc746e59a1 | ||
|
|
2cdb1c519d | ||
|
|
426074ded9 | ||
|
|
772a096615 | ||
|
|
e113e011cb | ||
|
|
22266484bd | ||
|
|
559c011420 | ||
|
|
411c0633a3 | ||
|
|
488f023bdf | ||
|
|
22878f4ef8 | ||
|
|
e732a39fea | ||
|
|
62b4bf7af4 | ||
|
|
47a525ddb4 | ||
|
|
f4360725e0 | ||
|
|
b86607cd41 | ||
|
|
bf57de85c3 | ||
|
|
2dd6118ff4 | ||
|
|
816a53a7b1 | ||
|
|
ced93b0525 | ||
|
|
524ff9a4a6 | ||
|
|
f15032f905 | ||
|
|
d7d31a19e5 | ||
|
|
df2a7ddca4 | ||
|
|
ba9c71ffa4 | ||
|
|
21b6c6569c | ||
|
|
92aba9565a | ||
|
|
6ea0aebb0b | ||
|
|
b5cdcb75b4 | ||
|
|
bd9aae40b8 | ||
|
|
33f931c0a4 | ||
|
|
ede8279c17 | ||
|
|
268b84a2b6 | ||
|
|
0a67145d80 | ||
|
|
2e55bc470c | ||
|
|
cf0d038978 | ||
|
|
92e7db1082 | ||
|
|
c45c47f935 | ||
|
|
341e27f9a3 | ||
|
|
ab167175c9 | ||
|
|
3c2933338f | ||
|
|
829784fa50 | ||
|
|
3c45f8dc91 | ||
|
|
f8ebf7c6ad | ||
|
|
510c364607 | ||
|
|
a3d6fcccb7 | ||
|
|
42c8082edd | ||
|
|
1a7edf7f0e |
59
README.md
59
README.md
@@ -5,6 +5,7 @@
|
|||||||

|

|
||||||
|
|
||||||
## NOTICE
|
## NOTICE
|
||||||
|
* V3.16: Support for `uv` has been added. Set `use_uv` in `config.ini`.
|
||||||
* V3.10: `double-click feature` is removed
|
* V3.10: `double-click feature` is removed
|
||||||
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
|
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
|
||||||
* V3.3.2: Overhauled. Officially supports [https://comfyregistry.org/](https://comfyregistry.org/).
|
* V3.3.2: Overhauled. Officially supports [https://comfyregistry.org/](https://comfyregistry.org/).
|
||||||
@@ -148,6 +149,8 @@ In `ComfyUI-Manager` V3.0 and later, configuration files and dynamically generat
|
|||||||
* Basic config files: `<USER_DIRECTORY>/default/ComfyUI-Manager/config.ini`
|
* Basic config files: `<USER_DIRECTORY>/default/ComfyUI-Manager/config.ini`
|
||||||
* Configurable channel lists: `<USER_DIRECTORY>/default/ComfyUI-Manager/channels.ini`
|
* Configurable channel lists: `<USER_DIRECTORY>/default/ComfyUI-Manager/channels.ini`
|
||||||
* Configurable pip overrides: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_overrides.json`
|
* Configurable pip overrides: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_overrides.json`
|
||||||
|
* Configurable pip blacklist: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_blacklist.list`
|
||||||
|
* Configurable pip auto fix: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_auto_fix.list`
|
||||||
* Saved snapshot files: `<USER_DIRECTORY>/default/ComfyUI-Manager/snapshots`
|
* Saved snapshot files: `<USER_DIRECTORY>/default/ComfyUI-Manager/snapshots`
|
||||||
* Startup script files: `<USER_DIRECTORY>/default/ComfyUI-Manager/startup-scripts`
|
* Startup script files: `<USER_DIRECTORY>/default/ComfyUI-Manager/startup-scripts`
|
||||||
* Component files: `<USER_DIRECTORY>/default/ComfyUI-Manager/components`
|
* Component files: `<USER_DIRECTORY>/default/ComfyUI-Manager/components`
|
||||||
@@ -246,6 +249,32 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
# Config
|
||||||
|
* You can modify the `config.ini` file to apply the settings for ComfyUI-Manager.
|
||||||
|
* The path to the `config.ini` used by ComfyUI-Manager is displayed in the startup log messages.
|
||||||
|
* See also: [https://github.com/ltdrdata/ComfyUI-Manager#paths]
|
||||||
|
* Configuration options:
|
||||||
|
```
|
||||||
|
[default]
|
||||||
|
git_exe = <Manually specify the path to the git executable. If left empty, the default git executable path will be used.>
|
||||||
|
use_uv = <Use uv instead of pip for dependency installation.>
|
||||||
|
default_cache_as_channel_url = <Determines whether to retrieve the DB designated as channel_url at startup>
|
||||||
|
bypass_ssl = <Set to True if SSL errors occur to disable SSL.>
|
||||||
|
file_logging = <Configure whether to create a log file used by ComfyUI-Manager.>
|
||||||
|
windows_selector_event_loop_policy = <If an event loop error occurs on Windows, set this to True.>
|
||||||
|
model_download_by_agent = <When downloading models, use an agent instead of torchvision_download_url.>
|
||||||
|
downgrade_blacklist = <Set a list of packages to prevent downgrades. List them separated by commas.>
|
||||||
|
security_level = <Set the security level => strong|normal|normal-|weak>
|
||||||
|
always_lazy_install = <Whether to perform dependency installation on restart even in environments other than Windows.>
|
||||||
|
network_mode = <Set the network mode => public|private|offline>
|
||||||
|
```
|
||||||
|
|
||||||
|
* network_mode:
|
||||||
|
- public: An environment that uses a typical public network.
|
||||||
|
- private: An environment that uses a closed network, where a private node DB is configured via `channel_url`. (Uses cache if available)
|
||||||
|
- offline: An environment that does not use any external connections when using an offline network. (Uses cache if available)
|
||||||
|
|
||||||
|
|
||||||
## Additional Feature
|
## Additional Feature
|
||||||
* Logging to file feature
|
* Logging to file feature
|
||||||
* This feature is enabled by default and can be disabled by setting `file_logging = False` in the `config.ini`.
|
* This feature is enabled by default and can be disabled by setting `file_logging = False` in the `config.ini`.
|
||||||
@@ -275,6 +304,13 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||
* When you create the `pip_overrides.json` file, it changes the installation of specific pip packages to installations defined by the user.
|
* When you create the `pip_overrides.json` file, it changes the installation of specific pip packages to installations defined by the user.
|
||||||
* Please refer to the `pip_overrides.json.template` file.
|
* Please refer to the `pip_overrides.json.template` file.
|
||||||
|
|
||||||
|
* Prevent the installation of specific pip packages
|
||||||
|
* List the package names one per line in the `pip_blacklist.list` file.
|
||||||
|
|
||||||
|
* Automatically Restoring pip Installation
|
||||||
|
* If you list pip spec requirements in `pip_auto_fix.list`, similar to `requirements.txt`, it will automatically restore the specified versions when starting ComfyUI or when versions get mismatched during various custom node installations.
|
||||||
|
* `--index-url` can be used.
|
||||||
|
|
||||||
* Use `aria2` as downloader
|
* Use `aria2` as downloader
|
||||||
* [howto](docs/en/use_aria2.md)
|
* [howto](docs/en/use_aria2.md)
|
||||||
|
|
||||||
@@ -282,6 +318,29 @@ The following settings are applied based on the section marked as `is_default`.
|
|||||||
* This option can be used if performance issues occur in a Colab+GDrive environment.
|
* This option can be used if performance issues occur in a Colab+GDrive environment.
|
||||||
|
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
The following features can be configured using environment variables:
|
||||||
|
|
||||||
|
* **COMFYUI_PATH**: The installation path of ComfyUI
|
||||||
|
* **GITHUB_ENDPOINT**: Reverse proxy configuration for environments with limited access to GitHub
|
||||||
|
* **HF_ENDPOINT**: Reverse proxy configuration for environments with limited access to Hugging Face
|
||||||
|
|
||||||
|
|
||||||
|
### Example 1:
|
||||||
|
Redirecting `https://github.com/ltdrdata/ComfyUI-Impact-Pack` to `https://mirror.ghproxy.com/https://github.com/ltdrdata/ComfyUI-Impact-Pack`
|
||||||
|
|
||||||
|
```
|
||||||
|
GITHUB_ENDPOINT=https://mirror.ghproxy.com/https://github.com
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Example 2:
|
||||||
|
Changing `https://huggingface.co/path/to/somewhere` to `https://some-hf-mirror.com/path/to/somewhere`
|
||||||
|
|
||||||
|
```
|
||||||
|
HF_ENDPOINT=https://some-hf-mirror.com
|
||||||
|
```
|
||||||
|
|
||||||
## Scanner
|
## Scanner
|
||||||
When you run the `scan.sh` script:
|
When you run the `scan.sh` script:
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,10 @@ if not os.path.exists(cli_mode_flag):
|
|||||||
sys.path.append(os.path.join(os.path.dirname(__file__), "glob"))
|
sys.path.append(os.path.join(os.path.dirname(__file__), "glob"))
|
||||||
import manager_server # noqa: F401
|
import manager_server # noqa: F401
|
||||||
import share_3rdparty # noqa: F401
|
import share_3rdparty # noqa: F401
|
||||||
WEB_DIRECTORY = "js"
|
import cm_global
|
||||||
|
|
||||||
|
if not cm_global.disable_front and not 'DISABLE_COMFYUI_MANAGER_FRONT' in os.environ:
|
||||||
|
WEB_DIRECTORY = "js"
|
||||||
else:
|
else:
|
||||||
print("\n[ComfyUI-Manager] !! cli-only-mode is enabled !!\n")
|
print("\n[ComfyUI-Manager] !! cli-only-mode is enabled !!\n")
|
||||||
|
|
||||||
|
|||||||
88
cm-cli.py
88
cm-cli.py
@@ -32,6 +32,7 @@ if comfy_path is None:
|
|||||||
print("\n[bold yellow]WARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path.[/bold yellow]", file=sys.stderr)
|
print("\n[bold yellow]WARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path.[/bold yellow]", file=sys.stderr)
|
||||||
comfy_path = os.path.abspath(os.path.join(manager_util.comfyui_manager_path, '..', '..'))
|
comfy_path = os.path.abspath(os.path.join(manager_util.comfyui_manager_path, '..', '..'))
|
||||||
|
|
||||||
|
# This should be placed here
|
||||||
sys.path.append(comfy_path)
|
sys.path.append(comfy_path)
|
||||||
|
|
||||||
import utils.extra_config
|
import utils.extra_config
|
||||||
@@ -42,7 +43,7 @@ import cnr_utils
|
|||||||
|
|
||||||
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
cm_global.pip_blacklist = ['torch', 'torchsde', 'torchvision']
|
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||||
|
|
||||||
@@ -51,14 +52,26 @@ if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides
|
|||||||
cm_global.pip_overrides = json.load(json_file)
|
cm_global.pip_overrides = json.load(json_file)
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_blacklist.list")):
|
||||||
|
with open(os.path.join(manager_util.comfyui_manager_path, "pip_blacklist.list"), 'r', encoding="UTF-8", errors="ignore") as f:
|
||||||
|
for x in f.readlines():
|
||||||
|
y = x.strip()
|
||||||
|
if y != '':
|
||||||
|
cm_global.pip_blacklist.add(y)
|
||||||
|
|
||||||
|
|
||||||
def check_comfyui_hash():
|
def check_comfyui_hash():
|
||||||
repo = git.Repo(comfy_path)
|
try:
|
||||||
core.comfy_ui_revision = len(list(repo.iter_commits('HEAD')))
|
repo = git.Repo(comfy_path)
|
||||||
|
core.comfy_ui_revision = len(list(repo.iter_commits('HEAD')))
|
||||||
|
core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime
|
||||||
|
except:
|
||||||
|
print('[bold yellow]INFO: Frozen ComfyUI mode.[/bold yellow]')
|
||||||
|
core.comfy_ui_revision = 0
|
||||||
|
core.comfy_ui_commit_datetime = 0
|
||||||
|
|
||||||
cm_global.variables['comfyui.revision'] = core.comfy_ui_revision
|
cm_global.variables['comfyui.revision'] = core.comfy_ui_revision
|
||||||
|
|
||||||
core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime
|
|
||||||
|
|
||||||
|
|
||||||
check_comfyui_hash() # This is a preparation step for manager_core
|
check_comfyui_hash() # This is a preparation step for manager_core
|
||||||
core.check_invalid_nodes()
|
core.check_invalid_nodes()
|
||||||
@@ -67,7 +80,7 @@ core.check_invalid_nodes()
|
|||||||
def read_downgrade_blacklist():
|
def read_downgrade_blacklist():
|
||||||
try:
|
try:
|
||||||
import configparser
|
import configparser
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser(strict=False)
|
||||||
config.read(core.manager_config.path)
|
config.read(core.manager_config.path)
|
||||||
default_conf = config['default']
|
default_conf = config['default']
|
||||||
|
|
||||||
@@ -136,6 +149,18 @@ class Ctx:
|
|||||||
cm_global.pip_overrides = json.load(json_file)
|
cm_global.pip_overrides = json.load(json_file)
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||||
|
|
||||||
|
if os.path.exists(core.manager_pip_blacklist_path):
|
||||||
|
with open(core.manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||||
|
for x in f.readlines():
|
||||||
|
y = x.strip()
|
||||||
|
if y != '':
|
||||||
|
cm_global.pip_blacklist.add(y)
|
||||||
|
|
||||||
|
def update_custom_nodes_dir(self, target_dir):
|
||||||
|
import folder_paths
|
||||||
|
a, b = folder_paths.folder_names_and_paths['custom_nodes']
|
||||||
|
folder_paths.folder_names_and_paths['custom_nodes'] = [os.path.abspath(target_dir)], set()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_startup_scripts_path():
|
def get_startup_scripts_path():
|
||||||
return os.path.join(core.manager_startup_script_path, "install-scripts.txt")
|
return os.path.join(core.manager_startup_script_path, "install-scripts.txt")
|
||||||
@@ -229,7 +254,7 @@ def fix_node(node_spec_str, is_all=False, cnt_msg=''):
|
|||||||
res = unified_manager.unified_fix(node_name, version_spec, no_deps=cmd_ctx.no_deps)
|
res = unified_manager.unified_fix(node_name, version_spec, no_deps=cmd_ctx.no_deps)
|
||||||
|
|
||||||
if not res.result:
|
if not res.result:
|
||||||
print(f"ERROR: f{res.msg}")
|
print(f"[bold red]ERROR: f{res.msg}[/bold red]")
|
||||||
|
|
||||||
|
|
||||||
def uninstall_node(node_spec_str: str, is_all: bool = False, cnt_msg: str = ''):
|
def uninstall_node(node_spec_str: str, is_all: bool = False, cnt_msg: str = ''):
|
||||||
@@ -622,7 +647,7 @@ def install(
|
|||||||
cmd_ctx.set_channel_mode(channel, mode)
|
cmd_ctx.set_channel_mode(channel, mode)
|
||||||
cmd_ctx.set_no_deps(no_deps)
|
cmd_ctx.set_no_deps(no_deps)
|
||||||
|
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
for_each_nodes(nodes, act=install_node)
|
for_each_nodes(nodes, act=install_node)
|
||||||
pip_fixer.fix_broken()
|
pip_fixer.fix_broken()
|
||||||
|
|
||||||
@@ -660,7 +685,7 @@ def reinstall(
|
|||||||
cmd_ctx.set_channel_mode(channel, mode)
|
cmd_ctx.set_channel_mode(channel, mode)
|
||||||
cmd_ctx.set_no_deps(no_deps)
|
cmd_ctx.set_no_deps(no_deps)
|
||||||
|
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
for_each_nodes(nodes, act=reinstall_node)
|
for_each_nodes(nodes, act=reinstall_node)
|
||||||
pip_fixer.fix_broken()
|
pip_fixer.fix_broken()
|
||||||
|
|
||||||
@@ -686,7 +711,7 @@ def uninstall(
|
|||||||
for_each_nodes(nodes, act=uninstall_node)
|
for_each_nodes(nodes, act=uninstall_node)
|
||||||
|
|
||||||
|
|
||||||
@app.command(help="Disable custom nodes")
|
@app.command(help="Update custom nodes")
|
||||||
def update(
|
def update(
|
||||||
nodes: List[str] = typer.Argument(
|
nodes: List[str] = typer.Argument(
|
||||||
...,
|
...,
|
||||||
@@ -714,7 +739,7 @@ def update(
|
|||||||
if 'all' in nodes:
|
if 'all' in nodes:
|
||||||
asyncio.run(auto_save_snapshot())
|
asyncio.run(auto_save_snapshot())
|
||||||
|
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
|
|
||||||
for x in nodes:
|
for x in nodes:
|
||||||
if x.lower() in ['comfyui', 'comfy', 'all']:
|
if x.lower() in ['comfyui', 'comfy', 'all']:
|
||||||
@@ -815,7 +840,7 @@ def fix(
|
|||||||
if 'all' in nodes:
|
if 'all' in nodes:
|
||||||
asyncio.run(auto_save_snapshot())
|
asyncio.run(auto_save_snapshot())
|
||||||
|
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
for_each_nodes(nodes, fix_node, allow_all=True)
|
for_each_nodes(nodes, fix_node, allow_all=True)
|
||||||
pip_fixer.fix_broken()
|
pip_fixer.fix_broken()
|
||||||
|
|
||||||
@@ -1012,11 +1037,30 @@ def save_snapshot(
|
|||||||
user_directory: str = typer.Option(
|
user_directory: str = typer.Option(
|
||||||
None,
|
None,
|
||||||
help="user directory"
|
help="user directory"
|
||||||
)
|
),
|
||||||
|
full_snapshot: Annotated[
|
||||||
|
bool,
|
||||||
|
typer.Option(
|
||||||
|
show_default=False, help="If the snapshot should include custom node, ComfyUI version and pip versions (default), or only custom node details"
|
||||||
|
),
|
||||||
|
] = True,
|
||||||
):
|
):
|
||||||
cmd_ctx.set_user_directory(user_directory)
|
cmd_ctx.set_user_directory(user_directory)
|
||||||
|
|
||||||
path = asyncio.run(core.save_snapshot_with_postfix('snapshot', output))
|
if output is None:
|
||||||
|
print("[bold red]ERROR: missing output path[/bold red]")
|
||||||
|
raise typer.Exit(code=1)
|
||||||
|
|
||||||
|
if(not output.endswith('.json') and not output.endswith('.yaml')):
|
||||||
|
print("[bold red]ERROR: output path should be either '.json' or '.yaml' file.[/bold red]")
|
||||||
|
raise typer.Exit(code=1)
|
||||||
|
|
||||||
|
dir_path = os.path.dirname(output)
|
||||||
|
if(dir_path != '' and not os.path.exists(dir_path)):
|
||||||
|
print(f"[bold red]ERROR: {output} path not exists.[/bold red]")
|
||||||
|
raise typer.Exit(code=1)
|
||||||
|
|
||||||
|
path = asyncio.run(core.save_snapshot_with_postfix('snapshot', output, not full_snapshot))
|
||||||
print(f"Current snapshot is saved as `{path}`")
|
print(f"Current snapshot is saved as `{path}`")
|
||||||
|
|
||||||
|
|
||||||
@@ -1044,10 +1088,17 @@ def restore_snapshot(
|
|||||||
user_directory: str = typer.Option(
|
user_directory: str = typer.Option(
|
||||||
None,
|
None,
|
||||||
help="user directory"
|
help="user directory"
|
||||||
|
),
|
||||||
|
restore_to: Optional[str] = typer.Option(
|
||||||
|
None,
|
||||||
|
help="Manually specify the installation path for the custom node. Ignore user directory."
|
||||||
)
|
)
|
||||||
):
|
):
|
||||||
cmd_ctx.set_user_directory(user_directory)
|
cmd_ctx.set_user_directory(user_directory)
|
||||||
|
|
||||||
|
if restore_to:
|
||||||
|
cmd_ctx.update_custom_nodes_dir(restore_to)
|
||||||
|
|
||||||
extras = []
|
extras = []
|
||||||
if pip_non_url:
|
if pip_non_url:
|
||||||
extras.append('--pip-non-url')
|
extras.append('--pip-non-url')
|
||||||
@@ -1068,7 +1119,7 @@ def restore_snapshot(
|
|||||||
print(f"[bold red]ERROR: `{snapshot_path}` is not exists.[/bold red]")
|
print(f"[bold red]ERROR: `{snapshot_path}` is not exists.[/bold red]")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
try:
|
try:
|
||||||
asyncio.run(core.restore_snapshot(snapshot_path, extras))
|
asyncio.run(core.restore_snapshot(snapshot_path, extras))
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -1100,7 +1151,7 @@ def restore_dependencies(
|
|||||||
total = len(node_paths)
|
total = len(node_paths)
|
||||||
i = 1
|
i = 1
|
||||||
|
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
for x in node_paths:
|
for x in node_paths:
|
||||||
print("----------------------------------------------------------------------------------------------------")
|
print("----------------------------------------------------------------------------------------------------")
|
||||||
print(f"Restoring [{i}/{total}]: {x}")
|
print(f"Restoring [{i}/{total}]: {x}")
|
||||||
@@ -1119,7 +1170,7 @@ def post_install(
|
|||||||
):
|
):
|
||||||
path = os.path.expanduser(path)
|
path = os.path.expanduser(path)
|
||||||
|
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
unified_manager.execute_install_script('', path, instant_execution=True)
|
unified_manager.execute_install_script('', path, instant_execution=True)
|
||||||
pip_fixer.fix_broken()
|
pip_fixer.fix_broken()
|
||||||
|
|
||||||
@@ -1163,8 +1214,7 @@ def install_deps(
|
|||||||
print(f"[bold red]Invalid json file: {deps}[/bold red]")
|
print(f"[bold red]Invalid json file: {deps}[/bold red]")
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
|
||||||
for k in json_obj['custom_nodes'].keys():
|
for k in json_obj['custom_nodes'].keys():
|
||||||
state = core.simple_check_custom_node(k)
|
state = core.simple_check_custom_node(k)
|
||||||
if state == 'installed':
|
if state == 'installed':
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -122,7 +122,8 @@ ComfyUI-Loopchain
|
|||||||
* `--pip-non-url`: Restore for pip packages registered on PyPI.
|
* `--pip-non-url`: Restore for pip packages registered on PyPI.
|
||||||
* `--pip-non-local-url`: Restore for pip packages registered at web URLs.
|
* `--pip-non-local-url`: Restore for pip packages registered at web URLs.
|
||||||
* `--pip-local-url`: Restore for pip packages specified by local paths.
|
* `--pip-local-url`: Restore for pip packages specified by local paths.
|
||||||
|
* `--user-directory`: Set the user directory.
|
||||||
|
* `--restore-to`: The path where the restored custom nodes will be installed. (When this option is applied, only the custom nodes installed in the target path are recognized as installed.)
|
||||||
|
|
||||||
### 5. CLI Only Mode
|
### 5. CLI Only Mode
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,8 @@ ComfyUI-Loopchain
|
|||||||
* `--pip-non-url`: PyPI 에 등록된 pip 패키지들에 대해서 복구를 수행
|
* `--pip-non-url`: PyPI 에 등록된 pip 패키지들에 대해서 복구를 수행
|
||||||
* `--pip-non-local-url`: web URL에 등록된 pip 패키지들에 대해서 복구를 수행
|
* `--pip-non-local-url`: web URL에 등록된 pip 패키지들에 대해서 복구를 수행
|
||||||
* `--pip-local-url`: local 경로를 지정하고 있는 pip 패키지들에 대해서 복구를 수행
|
* `--pip-local-url`: local 경로를 지정하고 있는 pip 패키지들에 대해서 복구를 수행
|
||||||
|
* `--user-directory`: 사용자 디렉토리 설정
|
||||||
|
* `--restore-to`: 복구될 커스텀 노드가 설치될 경로. (이 옵션을 적용할 경우 오직 대상 경로에 설치된 custom nodes 만 설치된 것으로 인식함.)
|
||||||
|
|
||||||
### 5. CLI only mode
|
### 5. CLI only mode
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -154,14 +154,27 @@ def switch_to_default_branch(repo):
|
|||||||
repo.git.checkout(default_branch)
|
repo.git.checkout(default_branch)
|
||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
|
# try checkout master
|
||||||
|
# try checkout main if failed
|
||||||
try:
|
try:
|
||||||
repo.git.checkout(repo.heads.master)
|
repo.git.checkout(repo.heads.master)
|
||||||
|
return True
|
||||||
except:
|
except:
|
||||||
try:
|
try:
|
||||||
if remote_name is not None:
|
if remote_name is not None:
|
||||||
repo.git.checkout('-b', 'master', f'{remote_name}/master')
|
repo.git.checkout('-b', 'master', f'{remote_name}/master')
|
||||||
|
return True
|
||||||
except:
|
except:
|
||||||
pass
|
try:
|
||||||
|
repo.git.checkout(repo.heads.main)
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
try:
|
||||||
|
if remote_name is not None:
|
||||||
|
repo.git.checkout('-b', 'main', f'{remote_name}/main')
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
print("[ComfyUI Manager] Failed to switch to the default branch")
|
print("[ComfyUI Manager] Failed to switch to the default branch")
|
||||||
return False
|
return False
|
||||||
@@ -389,12 +402,13 @@ def apply_snapshot(path):
|
|||||||
git_custom_node_infos = info['git_custom_nodes']
|
git_custom_node_infos = info['git_custom_nodes']
|
||||||
file_custom_node_infos = info['file_custom_nodes']
|
file_custom_node_infos = info['file_custom_nodes']
|
||||||
|
|
||||||
checkout_comfyui_hash(comfyui_hash)
|
if comfyui_hash:
|
||||||
|
checkout_comfyui_hash(comfyui_hash)
|
||||||
checkout_custom_node_hash(git_custom_node_infos)
|
checkout_custom_node_hash(git_custom_node_infos)
|
||||||
invalidate_custom_node_file(file_custom_node_infos)
|
invalidate_custom_node_file(file_custom_node_infos)
|
||||||
|
|
||||||
print("APPLY SNAPSHOT: True")
|
print("APPLY SNAPSHOT: True")
|
||||||
if 'pips' in info:
|
if 'pips' in info and info['pips']:
|
||||||
return info['pips']
|
return info['pips']
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|||||||
8123
github-stats.json
8123
github-stats.json
File diff suppressed because it is too large
Load Diff
@@ -113,3 +113,5 @@ def add_on_revision_detected(k, f):
|
|||||||
|
|
||||||
|
|
||||||
error_dict = {}
|
error_dict = {}
|
||||||
|
|
||||||
|
disable_front = False
|
||||||
@@ -1,12 +1,15 @@
|
|||||||
import requests
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from typing import List
|
|
||||||
import manager_util
|
|
||||||
import toml
|
|
||||||
import os
|
|
||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
import time
|
import time
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
import manager_core
|
||||||
|
import manager_util
|
||||||
|
import requests
|
||||||
|
import toml
|
||||||
|
|
||||||
base_url = "https://api.comfy.org"
|
base_url = "https://api.comfy.org"
|
||||||
|
|
||||||
@@ -32,9 +35,44 @@ async def _get_cnr_data(cache_mode=True, dont_wait=True):
|
|||||||
page = 1
|
page = 1
|
||||||
|
|
||||||
full_nodes = {}
|
full_nodes = {}
|
||||||
|
|
||||||
|
|
||||||
|
# Determine form factor based on environment and platform
|
||||||
|
is_desktop = bool(os.environ.get('__COMFYUI_DESKTOP_VERSION__'))
|
||||||
|
system = platform.system().lower()
|
||||||
|
is_windows = system == 'windows'
|
||||||
|
is_mac = system == 'darwin'
|
||||||
|
is_linux = system == 'linux'
|
||||||
|
|
||||||
|
# Get ComfyUI version tag
|
||||||
|
if is_desktop:
|
||||||
|
# extract version from pyproject.toml instead of git tag
|
||||||
|
comfyui_ver = manager_core.get_current_comfyui_ver() or 'unknown'
|
||||||
|
else:
|
||||||
|
comfyui_ver = manager_core.get_comfyui_tag() or 'unknown'
|
||||||
|
|
||||||
|
if is_desktop:
|
||||||
|
if is_windows:
|
||||||
|
form_factor = 'desktop-win'
|
||||||
|
elif is_mac:
|
||||||
|
form_factor = 'desktop-mac'
|
||||||
|
else:
|
||||||
|
form_factor = 'other'
|
||||||
|
else:
|
||||||
|
if is_windows:
|
||||||
|
form_factor = 'git-windows'
|
||||||
|
elif is_mac:
|
||||||
|
form_factor = 'git-mac'
|
||||||
|
elif is_linux:
|
||||||
|
form_factor = 'git-linux'
|
||||||
|
else:
|
||||||
|
form_factor = 'other'
|
||||||
|
|
||||||
|
print(f"form_factor: {form_factor}")
|
||||||
while remained:
|
while remained:
|
||||||
sub_uri = f'{base_url}/nodes?page={page}&limit=30'
|
# Add comfyui_version and form_factor to the API request
|
||||||
sub_json_obj = await asyncio.wait_for(manager_util.get_data_with_cache(sub_uri, cache_mode=False, silent=True), timeout=30)
|
sub_uri = f'{base_url}/nodes?page={page}&limit=30&comfyui_version={comfyui_ver}&form_factor={form_factor}'
|
||||||
|
sub_json_obj = await asyncio.wait_for(manager_util.get_data_with_cache(sub_uri, cache_mode=False, silent=True, dont_cache=True), timeout=30)
|
||||||
remained = page < sub_json_obj['totalPages']
|
remained = page < sub_json_obj['totalPages']
|
||||||
|
|
||||||
for x in sub_json_obj['nodes']:
|
for x in sub_json_obj['nodes']:
|
||||||
@@ -173,7 +211,10 @@ def read_cnr_info(fullpath):
|
|||||||
|
|
||||||
project = data.get('project', {})
|
project = data.get('project', {})
|
||||||
name = project.get('name').strip().lower()
|
name = project.get('name').strip().lower()
|
||||||
version = project.get('version')
|
|
||||||
|
# normalize version
|
||||||
|
# for example: 2.5 -> 2.5.0
|
||||||
|
version = str(manager_util.StrictVersion(project.get('version')))
|
||||||
|
|
||||||
urls = project.get('urls', {})
|
urls = project.get('urls', {})
|
||||||
repository = urls.get('Repository')
|
repository = urls.get('Repository')
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import os
|
|||||||
import configparser
|
import configparser
|
||||||
|
|
||||||
|
|
||||||
|
GITHUB_ENDPOINT = os.getenv('GITHUB_ENDPOINT')
|
||||||
|
|
||||||
|
|
||||||
def is_git_repo(path: str) -> bool:
|
def is_git_repo(path: str) -> bool:
|
||||||
""" Check if the path is a git repository. """
|
""" Check if the path is a git repository. """
|
||||||
# NOTE: Checking it through `git.Repo` must be avoided.
|
# NOTE: Checking it through `git.Repo` must be avoided.
|
||||||
@@ -37,7 +40,8 @@ def git_url(fullpath):
|
|||||||
if not os.path.exists(git_config_path):
|
if not os.path.exists(git_config_path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
config = configparser.ConfigParser()
|
# Set `strict=False` to allow duplicate `vscode-merge-base` sections, addressing <https://github.com/ltdrdata/ComfyUI-Manager/issues/1529>
|
||||||
|
config = configparser.ConfigParser(strict=False)
|
||||||
config.read(git_config_path)
|
config.read(git_config_path)
|
||||||
|
|
||||||
for k, v in config.items():
|
for k, v in config.items():
|
||||||
@@ -46,16 +50,36 @@ def git_url(fullpath):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def normalize_url(url) -> str:
|
def normalize_url(url) -> str:
|
||||||
url = url.replace("git@github.com:", "https://github.com/")
|
github_id = normalize_to_github_id(url)
|
||||||
if url.endswith('.git'):
|
if github_id is not None:
|
||||||
url = url[:-4]
|
url = f"https://github.com/{github_id}"
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
def normalize_url_http(url) -> str:
|
|
||||||
url = url.replace("https://github.com/", "git@github.com:")
|
def normalize_to_github_id(url) -> str:
|
||||||
if url.endswith('.git'):
|
if 'github' in url or (GITHUB_ENDPOINT is not None and GITHUB_ENDPOINT in url):
|
||||||
url = url[:-4]
|
author = os.path.basename(os.path.dirname(url))
|
||||||
|
|
||||||
|
if author.startswith('git@github.com:'):
|
||||||
|
author = author.split(':')[1]
|
||||||
|
|
||||||
|
repo_name = os.path.basename(url)
|
||||||
|
if repo_name.endswith('.git'):
|
||||||
|
repo_name = repo_name[:-4]
|
||||||
|
|
||||||
|
return f"{author}/{repo_name}"
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_url_for_clone(url):
|
||||||
|
url = normalize_url(url)
|
||||||
|
|
||||||
|
if GITHUB_ENDPOINT is not None and url.startswith('https://github.com/'):
|
||||||
|
url = GITHUB_ENDPOINT + url[18:] # url[18:] -> remove `https://github.com`
|
||||||
|
|
||||||
return url
|
return url
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,15 @@ from urllib.parse import urlparse
|
|||||||
import urllib
|
import urllib
|
||||||
import sys
|
import sys
|
||||||
import logging
|
import logging
|
||||||
|
import requests
|
||||||
|
from huggingface_hub import HfApi
|
||||||
|
from tqdm.auto import tqdm
|
||||||
|
|
||||||
|
|
||||||
aria2 = os.getenv('COMFYUI_MANAGER_ARIA2_SERVER')
|
aria2 = os.getenv('COMFYUI_MANAGER_ARIA2_SERVER')
|
||||||
HF_ENDPOINT = os.getenv('HF_ENDPOINT')
|
HF_ENDPOINT = os.getenv('HF_ENDPOINT')
|
||||||
|
|
||||||
|
|
||||||
if aria2 is not None:
|
if aria2 is not None:
|
||||||
secret = os.getenv('COMFYUI_MANAGER_ARIA2_SECRET')
|
secret = os.getenv('COMFYUI_MANAGER_ARIA2_SECRET')
|
||||||
url = urlparse(aria2)
|
url = urlparse(aria2)
|
||||||
@@ -117,3 +123,37 @@ def download_url_with_agent(url, save_path):
|
|||||||
|
|
||||||
print("Installation was successful.")
|
print("Installation was successful.")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# NOTE: snapshot_download doesn't provide file size tqdm.
|
||||||
|
def download_repo_in_bytes(repo_id, local_dir):
|
||||||
|
api = HfApi()
|
||||||
|
repo_info = api.repo_info(repo_id=repo_id, files_metadata=True)
|
||||||
|
|
||||||
|
os.makedirs(local_dir, exist_ok=True)
|
||||||
|
|
||||||
|
total_size = 0
|
||||||
|
for file_info in repo_info.siblings:
|
||||||
|
if file_info.size is not None:
|
||||||
|
total_size += file_info.size
|
||||||
|
|
||||||
|
pbar = tqdm(total=total_size, unit="B", unit_scale=True, desc="Downloading")
|
||||||
|
|
||||||
|
for file_info in repo_info.siblings:
|
||||||
|
out_path = os.path.join(local_dir, file_info.rfilename)
|
||||||
|
os.makedirs(os.path.dirname(out_path), exist_ok=True)
|
||||||
|
|
||||||
|
if file_info.size is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
download_url = f"https://huggingface.co/{repo_id}/resolve/main/{file_info.rfilename}"
|
||||||
|
|
||||||
|
with requests.get(download_url, stream=True) as r, open(out_path, "wb") as f:
|
||||||
|
r.raise_for_status()
|
||||||
|
for chunk in r.iter_content(chunk_size=65536):
|
||||||
|
if chunk:
|
||||||
|
f.write(chunk)
|
||||||
|
pbar.update(len(chunk))
|
||||||
|
|
||||||
|
pbar.close()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,7 @@
|
|||||||
description:
|
description:
|
||||||
`manager_util` is the lightest module shared across the prestartup_script, main code, and cm-cli of ComfyUI-Manager.
|
`manager_util` is the lightest module shared across the prestartup_script, main code, and cm-cli of ComfyUI-Manager.
|
||||||
"""
|
"""
|
||||||
|
import traceback
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import json
|
import json
|
||||||
@@ -12,6 +13,8 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import logging
|
import logging
|
||||||
|
import platform
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
|
||||||
cache_lock = threading.Lock()
|
cache_lock = threading.Lock()
|
||||||
@@ -19,6 +22,24 @@ cache_lock = threading.Lock()
|
|||||||
comfyui_manager_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
comfyui_manager_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
cache_dir = os.path.join(comfyui_manager_path, '.cache') # This path is also updated together in **manager_core.update_user_directory**.
|
cache_dir = os.path.join(comfyui_manager_path, '.cache') # This path is also updated together in **manager_core.update_user_directory**.
|
||||||
|
|
||||||
|
use_uv = False
|
||||||
|
|
||||||
|
|
||||||
|
def add_python_path_to_env():
|
||||||
|
if platform.system() != "Windows":
|
||||||
|
sep = ':'
|
||||||
|
else:
|
||||||
|
sep = ';'
|
||||||
|
|
||||||
|
os.environ['PATH'] = os.path.dirname(sys.executable)+sep+os.environ['PATH']
|
||||||
|
|
||||||
|
|
||||||
|
def make_pip_cmd(cmd):
|
||||||
|
if use_uv:
|
||||||
|
return [sys.executable, '-m', 'uv', 'pip'] + cmd
|
||||||
|
else:
|
||||||
|
return [sys.executable, '-m', 'pip'] + cmd
|
||||||
|
|
||||||
|
|
||||||
# DON'T USE StrictVersion - cannot handle pre_release version
|
# DON'T USE StrictVersion - cannot handle pre_release version
|
||||||
# try:
|
# try:
|
||||||
@@ -122,7 +143,12 @@ async def get_data(uri, silent=False):
|
|||||||
with open(uri, "r", encoding="utf-8") as f:
|
with open(uri, "r", encoding="utf-8") as f:
|
||||||
json_text = f.read()
|
json_text = f.read()
|
||||||
|
|
||||||
json_obj = json.loads(json_text)
|
try:
|
||||||
|
json_obj = json.loads(json_text)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"[ComfyUI-Manager] An error occurred while fetching '{uri}': {e}")
|
||||||
|
|
||||||
|
return {}
|
||||||
|
|
||||||
if not silent:
|
if not silent:
|
||||||
print(" [DONE]")
|
print(" [DONE]")
|
||||||
@@ -156,7 +182,7 @@ def save_to_cache(uri, json_obj, silent=False):
|
|||||||
logging.info(f"[ComfyUI-Manager] default cache updated: {uri}")
|
logging.info(f"[ComfyUI-Manager] default cache updated: {uri}")
|
||||||
|
|
||||||
|
|
||||||
async def get_data_with_cache(uri, silent=False, cache_mode=True, dont_wait=False):
|
async def get_data_with_cache(uri, silent=False, cache_mode=True, dont_wait=False, dont_cache=False):
|
||||||
cache_uri = get_cache_path(uri)
|
cache_uri = get_cache_path(uri)
|
||||||
|
|
||||||
if cache_mode and dont_wait:
|
if cache_mode and dont_wait:
|
||||||
@@ -175,11 +201,12 @@ async def get_data_with_cache(uri, silent=False, cache_mode=True, dont_wait=Fals
|
|||||||
json_obj = await get_data(cache_uri, silent=silent)
|
json_obj = await get_data(cache_uri, silent=silent)
|
||||||
else:
|
else:
|
||||||
json_obj = await get_data(uri, silent=silent)
|
json_obj = await get_data(uri, silent=silent)
|
||||||
with cache_lock:
|
if not dont_cache:
|
||||||
with open(cache_uri, "w", encoding='utf-8') as file:
|
with cache_lock:
|
||||||
json.dump(json_obj, file, indent=4, sort_keys=True)
|
with open(cache_uri, "w", encoding='utf-8') as file:
|
||||||
if not silent:
|
json.dump(json_obj, file, indent=4, sort_keys=True)
|
||||||
logging.info(f"[ComfyUI-Manager] default cache updated: {uri}")
|
if not silent:
|
||||||
|
logging.info(f"[ComfyUI-Manager] default cache updated: {uri}")
|
||||||
|
|
||||||
return json_obj
|
return json_obj
|
||||||
|
|
||||||
@@ -209,7 +236,7 @@ def get_installed_packages(renew=False):
|
|||||||
|
|
||||||
if renew or pip_map is None:
|
if renew or pip_map is None:
|
||||||
try:
|
try:
|
||||||
result = subprocess.check_output([sys.executable, '-m', 'pip', 'list'], universal_newlines=True)
|
result = subprocess.check_output(make_pip_cmd(['list']), universal_newlines=True)
|
||||||
|
|
||||||
pip_map = {}
|
pip_map = {}
|
||||||
for line in result.split('\n'):
|
for line in result.split('\n'):
|
||||||
@@ -219,7 +246,8 @@ def get_installed_packages(renew=False):
|
|||||||
if y[0] == 'Package' or y[0].startswith('-'):
|
if y[0] == 'Package' or y[0].startswith('-'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
pip_map[y[0]] = y[1]
|
normalized_name = y[0].lower().replace('-', '_')
|
||||||
|
pip_map[normalized_name] = y[1]
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
logging.error("[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
|
logging.error("[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
|
||||||
return set()
|
return set()
|
||||||
@@ -232,6 +260,46 @@ def clear_pip_cache():
|
|||||||
pip_map = None
|
pip_map = None
|
||||||
|
|
||||||
|
|
||||||
|
def parse_requirement_line(line):
|
||||||
|
tokens = shlex.split(line)
|
||||||
|
if not tokens:
|
||||||
|
return None
|
||||||
|
|
||||||
|
package_spec = tokens[0]
|
||||||
|
|
||||||
|
pattern = re.compile(
|
||||||
|
r'^(?P<package>[A-Za-z0-9_.+-]+)'
|
||||||
|
r'(?P<operator>==|>=|<=|!=|~=|>|<)?'
|
||||||
|
r'(?P<version>[A-Za-z0-9_.+-]*)$'
|
||||||
|
)
|
||||||
|
m = pattern.match(package_spec)
|
||||||
|
if not m:
|
||||||
|
return None
|
||||||
|
|
||||||
|
package = m.group('package')
|
||||||
|
operator = m.group('operator') or None
|
||||||
|
version = m.group('version') or None
|
||||||
|
|
||||||
|
index_url = None
|
||||||
|
if '--index-url' in tokens:
|
||||||
|
idx = tokens.index('--index-url')
|
||||||
|
if idx + 1 < len(tokens):
|
||||||
|
index_url = tokens[idx + 1]
|
||||||
|
|
||||||
|
res = {'package': package}
|
||||||
|
|
||||||
|
if operator is not None:
|
||||||
|
res['operator'] = operator
|
||||||
|
|
||||||
|
if version is not None:
|
||||||
|
res['version'] = StrictVersion(version)
|
||||||
|
|
||||||
|
if index_url is not None:
|
||||||
|
res['index_url'] = index_url
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
torch_torchvision_torchaudio_version_map = {
|
torch_torchvision_torchaudio_version_map = {
|
||||||
'2.6.0': ('0.21.0', '2.6.0'),
|
'2.6.0': ('0.21.0', '2.6.0'),
|
||||||
'2.5.1': ('0.20.0', '2.5.0'),
|
'2.5.1': ('0.20.0', '2.5.0'),
|
||||||
@@ -251,16 +319,19 @@ torch_torchvision_torchaudio_version_map = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class PIPFixer:
|
class PIPFixer:
|
||||||
def __init__(self, prev_pip_versions):
|
def __init__(self, prev_pip_versions, comfyui_path, manager_files_path):
|
||||||
self.prev_pip_versions = { **prev_pip_versions }
|
self.prev_pip_versions = { **prev_pip_versions }
|
||||||
|
self.comfyui_path = comfyui_path
|
||||||
|
self.manager_files_path = manager_files_path
|
||||||
|
|
||||||
def torch_rollback(self):
|
def torch_rollback(self):
|
||||||
spec = self.prev_pip_versions['torch'].split('+')
|
spec = self.prev_pip_versions['torch'].split('+')
|
||||||
if len(spec) > 0:
|
if len(spec) > 0:
|
||||||
platform = spec[1]
|
platform = spec[1]
|
||||||
else:
|
else:
|
||||||
cmd = [sys.executable, '-m', 'pip', 'install', '--force', 'torch', 'torchvision', 'torchaudio']
|
cmd = make_pip_cmd(['install', '--force', 'torch', 'torchvision', 'torchaudio'])
|
||||||
subprocess.check_output(cmd, universal_newlines=True)
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
logging.error(cmd)
|
logging.error(cmd)
|
||||||
return
|
return
|
||||||
@@ -270,15 +341,13 @@ class PIPFixer:
|
|||||||
torch_torchvision_torchaudio_ver = torch_torchvision_torchaudio_version_map.get(torch_ver)
|
torch_torchvision_torchaudio_ver = torch_torchvision_torchaudio_version_map.get(torch_ver)
|
||||||
|
|
||||||
if torch_torchvision_torchaudio_ver is None:
|
if torch_torchvision_torchaudio_ver is None:
|
||||||
cmd = [sys.executable, '-m', 'pip', 'install', '--pre',
|
cmd = make_pip_cmd(['install', '--pre', 'torch', 'torchvision', 'torchaudio',
|
||||||
'torch', 'torchvision', 'torchaudio',
|
'--index-url', f"https://download.pytorch.org/whl/nightly/{platform}"])
|
||||||
'--index-url', f"https://download.pytorch.org/whl/nightly/{platform}"]
|
|
||||||
logging.info("[ComfyUI-Manager] restore PyTorch to nightly version")
|
logging.info("[ComfyUI-Manager] restore PyTorch to nightly version")
|
||||||
else:
|
else:
|
||||||
torchvision_ver, torchaudio_ver = torch_torchvision_torchaudio_ver
|
torchvision_ver, torchaudio_ver = torch_torchvision_torchaudio_ver
|
||||||
cmd = [sys.executable, '-m', 'pip', 'install',
|
cmd = make_pip_cmd(['install', f'torch=={torch_ver}', f'torchvision=={torchvision_ver}', f"torchaudio=={torchaudio_ver}",
|
||||||
f'torch=={torch_ver}', f'torchvision=={torchvision_ver}', f"torchaudio=={torchaudio_ver}",
|
'--index-url', f"https://download.pytorch.org/whl/{platform}"])
|
||||||
'--index-url', f"https://download.pytorch.org/whl/{platform}"]
|
|
||||||
logging.info(f"[ComfyUI-Manager] restore PyTorch to {torch_ver}+{platform}")
|
logging.info(f"[ComfyUI-Manager] restore PyTorch to {torch_ver}+{platform}")
|
||||||
|
|
||||||
subprocess.check_output(cmd, universal_newlines=True)
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
@@ -289,7 +358,7 @@ class PIPFixer:
|
|||||||
# remove `comfy` python package
|
# remove `comfy` python package
|
||||||
try:
|
try:
|
||||||
if 'comfy' in new_pip_versions:
|
if 'comfy' in new_pip_versions:
|
||||||
cmd = [sys.executable, '-m', 'pip', 'uninstall', 'comfy']
|
cmd = make_pip_cmd(['uninstall', 'comfy'])
|
||||||
subprocess.check_output(cmd, universal_newlines=True)
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
|
||||||
logging.warning("[ComfyUI-Manager] 'comfy' python package is uninstalled.\nWARN: The 'comfy' package is completely unrelated to ComfyUI and should never be installed as it causes conflicts with ComfyUI.")
|
logging.warning("[ComfyUI-Manager] 'comfy' python package is uninstalled.\nWARN: The 'comfy' package is completely unrelated to ComfyUI and should never be installed as it causes conflicts with ComfyUI.")
|
||||||
@@ -335,7 +404,7 @@ class PIPFixer:
|
|||||||
|
|
||||||
if len(targets) > 0:
|
if len(targets) > 0:
|
||||||
for x in targets:
|
for x in targets:
|
||||||
cmd = [sys.executable, '-m', 'pip', 'install', f"{x}=={versions[0].version_string}"]
|
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}", "numpy<2"])
|
||||||
subprocess.check_output(cmd, universal_newlines=True)
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
|
||||||
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
|
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
|
||||||
@@ -348,11 +417,83 @@ class PIPFixer:
|
|||||||
np = new_pip_versions.get('numpy')
|
np = new_pip_versions.get('numpy')
|
||||||
if np is not None:
|
if np is not None:
|
||||||
if StrictVersion(np) >= StrictVersion('2'):
|
if StrictVersion(np) >= StrictVersion('2'):
|
||||||
subprocess.check_output([sys.executable, '-m', 'pip', 'install', "numpy<2"], universal_newlines=True)
|
cmd = make_pip_cmd(['install', "numpy<2"])
|
||||||
|
subprocess.check_output(cmd , universal_newlines=True)
|
||||||
|
|
||||||
|
logging.info("[ComfyUI-Manager] 'numpy' dependency were fixed")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("[ComfyUI-Manager] Failed to restore numpy")
|
logging.error("[ComfyUI-Manager] Failed to restore numpy")
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
|
|
||||||
|
# fix missing frontend
|
||||||
|
try:
|
||||||
|
# NOTE: package name in requirements is 'comfyui-frontend-package'
|
||||||
|
# but, package name from `pip freeze` is 'comfyui_frontend_package'
|
||||||
|
# but, package name from `uv pip freeze` is 'comfyui-frontend-package'
|
||||||
|
#
|
||||||
|
# get_installed_packages returns normalized name (i.e. comfyui_frontend_package)
|
||||||
|
if 'comfyui_frontend_package' not in new_pip_versions:
|
||||||
|
requirements_path = os.path.join(self.comfyui_path, 'requirements.txt')
|
||||||
|
|
||||||
|
with open(requirements_path, 'r') as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
|
||||||
|
front_line = next((line.strip() for line in lines if line.startswith('comfyui-frontend-package')), None)
|
||||||
|
cmd = make_pip_cmd(['install', front_line])
|
||||||
|
subprocess.check_output(cmd , universal_newlines=True)
|
||||||
|
|
||||||
|
logging.info("[ComfyUI-Manager] 'comfyui-frontend-package' dependency were fixed")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error("[ComfyUI-Manager] Failed to restore comfyui-frontend-package")
|
||||||
|
logging.error(e)
|
||||||
|
|
||||||
|
# restore based on custom list
|
||||||
|
pip_auto_fix_path = os.path.join(self.manager_files_path, "pip_auto_fix.list")
|
||||||
|
if os.path.exists(pip_auto_fix_path):
|
||||||
|
with open(pip_auto_fix_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||||
|
fixed_list = []
|
||||||
|
|
||||||
|
for x in f.readlines():
|
||||||
|
try:
|
||||||
|
parsed = parse_requirement_line(x)
|
||||||
|
need_to_reinstall = True
|
||||||
|
|
||||||
|
normalized_name = parsed['package'].lower().replace('-', '_')
|
||||||
|
if normalized_name in new_pip_versions:
|
||||||
|
if 'version' in parsed and 'operator' in parsed:
|
||||||
|
cur = StrictVersion(new_pip_versions[parsed['package']])
|
||||||
|
dest = parsed['version']
|
||||||
|
op = parsed['operator']
|
||||||
|
if cur == dest:
|
||||||
|
if op in ['==', '>=', '<=']:
|
||||||
|
need_to_reinstall = False
|
||||||
|
elif cur < dest:
|
||||||
|
if op in ['<=', '<', '~=', '!=']:
|
||||||
|
need_to_reinstall = False
|
||||||
|
elif cur > dest:
|
||||||
|
if op in ['>=', '>', '~=', '!=']:
|
||||||
|
need_to_reinstall = False
|
||||||
|
|
||||||
|
if need_to_reinstall:
|
||||||
|
cmd_args = ['install']
|
||||||
|
if 'version' in parsed and 'operator' in parsed:
|
||||||
|
cmd_args.append(parsed['package']+parsed['operator']+parsed['version'].version_string)
|
||||||
|
|
||||||
|
if 'index_url' in parsed:
|
||||||
|
cmd_args.append('--index-url')
|
||||||
|
cmd_args.append(parsed['index_url'])
|
||||||
|
|
||||||
|
cmd = make_pip_cmd(cmd_args)
|
||||||
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
|
|
||||||
|
fixed_list.append(parsed['package'])
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
logging.error(f"[ComfyUI-Manager] Failed to restore '{x}'")
|
||||||
|
logging.error(e)
|
||||||
|
|
||||||
|
if len(fixed_list) > 0:
|
||||||
|
logging.info(f"[ComfyUI-Manager] dependencies in pip_auto_fix.json were fixed: {fixed_list}")
|
||||||
|
|
||||||
def sanitize(data):
|
def sanitize(data):
|
||||||
return data.replace("<", "<").replace(">", ">")
|
return data.replace("<", "<").replace(">", ">")
|
||||||
@@ -361,3 +502,23 @@ def sanitize(data):
|
|||||||
def sanitize_filename(input_string):
|
def sanitize_filename(input_string):
|
||||||
result_string = re.sub(r'[^a-zA-Z0-9_]', '_', input_string)
|
result_string = re.sub(r'[^a-zA-Z0-9_]', '_', input_string)
|
||||||
return result_string
|
return result_string
|
||||||
|
|
||||||
|
|
||||||
|
def robust_readlines(fullpath):
|
||||||
|
import chardet
|
||||||
|
try:
|
||||||
|
with open(fullpath, "r") as f:
|
||||||
|
return f.readlines()
|
||||||
|
except:
|
||||||
|
encoding = None
|
||||||
|
with open(fullpath, "rb") as f:
|
||||||
|
raw_data = f.read()
|
||||||
|
result = chardet.detect(raw_data)
|
||||||
|
encoding = result['encoding']
|
||||||
|
|
||||||
|
if encoding is not None:
|
||||||
|
with open(fullpath, "r", encoding=encoding) as f:
|
||||||
|
return f.readlines()
|
||||||
|
|
||||||
|
print(f"[ComfyUI-Manager] Failed to recognize encoding for: {fullpath}")
|
||||||
|
return []
|
||||||
|
|||||||
@@ -13,12 +13,16 @@ import {
|
|||||||
import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
||||||
import {
|
import {
|
||||||
free_models, install_pip, install_via_git_url, manager_instance,
|
free_models, install_pip, install_via_git_url, manager_instance,
|
||||||
rebootAPI, migrateAPI, setManagerInstance, show_message, customAlert, customPrompt } from "./common.js";
|
rebootAPI, migrateAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
||||||
|
infoToast, showTerminal, setNeedRestart
|
||||||
|
} from "./common.js";
|
||||||
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
||||||
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
||||||
import { ModelManager } from "./model-manager.js";
|
import { ModelManager } from "./model-manager.js";
|
||||||
import { SnapshotManager } from "./snapshot.js";
|
import { SnapshotManager } from "./snapshot.js";
|
||||||
|
|
||||||
|
let manager_version = await getVersion();
|
||||||
|
|
||||||
var docStyle = document.createElement('style');
|
var docStyle = document.createElement('style');
|
||||||
docStyle.innerHTML = `
|
docStyle.innerHTML = `
|
||||||
.comfy-toast {
|
.comfy-toast {
|
||||||
@@ -40,7 +44,7 @@ docStyle.innerHTML = `
|
|||||||
|
|
||||||
#cm-manager-dialog {
|
#cm-manager-dialog {
|
||||||
width: 1000px;
|
width: 1000px;
|
||||||
height: 520px;
|
height: 455px;
|
||||||
box-sizing: content-box;
|
box-sizing: content-box;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
@@ -137,7 +141,7 @@ docStyle.innerHTML = `
|
|||||||
|
|
||||||
.cm-notice-board {
|
.cm-notice-board {
|
||||||
width: 290px;
|
width: 290px;
|
||||||
height: 270px;
|
height: 230px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
color: var(--input-text);
|
color: var(--input-text);
|
||||||
border: 1px solid var(--descrip-text);
|
border: 1px solid var(--descrip-text);
|
||||||
@@ -223,9 +227,13 @@ document.head.appendChild(docStyle);
|
|||||||
|
|
||||||
var update_comfyui_button = null;
|
var update_comfyui_button = null;
|
||||||
var switch_comfyui_button = null;
|
var switch_comfyui_button = null;
|
||||||
var fetch_updates_button = null;
|
|
||||||
var update_all_button = null;
|
var update_all_button = null;
|
||||||
|
var restart_stop_button = null;
|
||||||
|
var update_policy_combo = null;
|
||||||
|
|
||||||
let share_option = 'all';
|
let share_option = 'all';
|
||||||
|
var is_updating = false;
|
||||||
|
|
||||||
|
|
||||||
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
||||||
const style = `
|
const style = `
|
||||||
@@ -424,102 +432,56 @@ async function init_notice(notice) {
|
|||||||
|
|
||||||
await init_share_option();
|
await init_share_option();
|
||||||
|
|
||||||
async function fetchNicknames() {
|
|
||||||
const response1 = await api.fetchApi(`/customnode/getmappings?mode=nickname`);
|
|
||||||
const mappings = await response1.json();
|
|
||||||
|
|
||||||
let result = {};
|
async function set_inprogress_mode() {
|
||||||
let nickname_patterns = [];
|
update_comfyui_button.disabled = true;
|
||||||
|
update_comfyui_button.style.backgroundColor = "gray";
|
||||||
|
|
||||||
for (let i in mappings) {
|
update_all_button.disabled = true;
|
||||||
let item = mappings[i];
|
update_all_button.style.backgroundColor = "gray";
|
||||||
var nickname;
|
|
||||||
if (item[1].nickname) {
|
|
||||||
nickname = item[1].nickname;
|
|
||||||
}
|
|
||||||
else if (item[1].title) {
|
|
||||||
nickname = item[1].title;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nickname = item[1].title_aux;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let j in item[0]) {
|
switch_comfyui_button.disabled = true;
|
||||||
result[item[0][j]] = nickname;
|
switch_comfyui_button.style.backgroundColor = "gray";
|
||||||
}
|
|
||||||
|
|
||||||
if(item[1].nodename_pattern) {
|
restart_stop_button.innerText = 'Stop';
|
||||||
nickname_patterns.push([item[1].nodename_pattern, nickname]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [result, nickname_patterns];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const [nicknames, nickname_patterns] = await fetchNicknames();
|
|
||||||
|
|
||||||
function getNickname(node, nodename) {
|
async function reset_action_buttons() {
|
||||||
if(node.nickname) {
|
const isElectron = 'electronAPI' in window;
|
||||||
return node.nickname;
|
|
||||||
|
if(isElectron) {
|
||||||
|
update_all_button.innerText = "Update All Custom Nodes";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (nicknames[nodename]) {
|
update_all_button.innerText = "Update All";
|
||||||
node.nickname = nicknames[nodename];
|
|
||||||
}
|
|
||||||
else if(node.getInnerNodes) {
|
|
||||||
let pure_name = getPureName(node);
|
|
||||||
let groupNode = app.graph.extra?.groupNodes?.[pure_name];
|
|
||||||
if(groupNode) {
|
|
||||||
let packname = groupNode.packname;
|
|
||||||
node.nickname = packname;
|
|
||||||
}
|
|
||||||
return node.nickname;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for(let i in nickname_patterns) {
|
|
||||||
let item = nickname_patterns[i];
|
|
||||||
if(nodename.match(item[0])) {
|
|
||||||
node.nickname = item[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return node.nickname;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
update_comfyui_button.innerText = "Update ComfyUI";
|
||||||
|
switch_comfyui_button.innerText = "Switch ComfyUI";
|
||||||
|
restart_stop_button.innerText = 'Restart';
|
||||||
|
|
||||||
|
update_comfyui_button.disabled = false;
|
||||||
|
update_all_button.disabled = false;
|
||||||
|
switch_comfyui_button.disabled = false;
|
||||||
|
|
||||||
|
update_comfyui_button.style.backgroundColor = "";
|
||||||
|
update_all_button.style.backgroundColor = "";
|
||||||
|
switch_comfyui_button.style.backgroundColor = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateComfyUI() {
|
async function updateComfyUI() {
|
||||||
let prev_text = update_comfyui_button.innerText;
|
let prev_text = update_comfyui_button.innerText;
|
||||||
update_comfyui_button.innerText = "Updating ComfyUI...";
|
update_comfyui_button.innerText = "Updating ComfyUI...";
|
||||||
update_comfyui_button.disabled = true;
|
|
||||||
update_comfyui_button.style.backgroundColor = "gray";
|
|
||||||
|
|
||||||
try {
|
set_inprogress_mode();
|
||||||
const response = await api.fetchApi('/comfyui_manager/update_comfyui');
|
|
||||||
|
|
||||||
if (response.status == 400) {
|
const response = await api.fetchApi('/manager/queue/update_comfyui');
|
||||||
show_message('Failed to update ComfyUI.');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.status == 201) {
|
showTerminal();
|
||||||
show_message('ComfyUI has been successfully updated.');
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
show_message('ComfyUI is already up to date with the latest version.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
is_updating = true;
|
||||||
}
|
await api.fetchApi('/manager/queue/start');
|
||||||
catch (exception) {
|
|
||||||
show_message(`Failed to update ComfyUI / ${exception}`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
update_comfyui_button.disabled = false;
|
|
||||||
update_comfyui_button.innerText = prev_text;
|
|
||||||
update_comfyui_button.style.backgroundColor = "";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showVersionSelectorDialog(versions, current, onSelect) {
|
function showVersionSelectorDialog(versions, current, onSelect) {
|
||||||
@@ -647,143 +609,183 @@ function showVersionSelectorDialog(versions, current, onSelect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function switchComfyUI() {
|
async function switchComfyUI() {
|
||||||
let res = await api.fetchApi(`/comfyui_manager/comfyui_versions`, { cache: "no-store" });
|
switch_comfyui_button.disabled = true;
|
||||||
|
switch_comfyui_button.style.backgroundColor = "gray";
|
||||||
|
|
||||||
if(res.status == 200) {
|
let res = await api.fetchApi(`/comfyui_manager/comfyui_versions`, { cache: "no-store" });
|
||||||
let obj = await res.json();
|
|
||||||
|
|
||||||
let versions = [];
|
switch_comfyui_button.disabled = false;
|
||||||
let default_version;
|
switch_comfyui_button.style.backgroundColor = "";
|
||||||
|
|
||||||
for(let v of obj.versions) {
|
if(res.status == 200) {
|
||||||
default_version = v;
|
let obj = await res.json();
|
||||||
versions.push(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
showVersionSelectorDialog(versions, obj.current, (selected_version) => {
|
let versions = [];
|
||||||
api.fetchApi(`/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
|
let default_version;
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
show_message('Failed to fetch ComfyUI versions.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for(let v of obj.versions) {
|
||||||
async function fetchUpdates(update_check_checkbox) {
|
default_version = v;
|
||||||
let prev_text = fetch_updates_button.innerText;
|
versions.push(v);
|
||||||
fetch_updates_button.innerText = "Fetching updates...";
|
|
||||||
fetch_updates_button.disabled = true;
|
|
||||||
fetch_updates_button.style.backgroundColor = "gray";
|
|
||||||
|
|
||||||
try {
|
|
||||||
var mode = manager_instance.datasrc_combo.value;
|
|
||||||
|
|
||||||
const response = await api.fetchApi(`/customnode/fetch_updates?mode=${mode}`);
|
|
||||||
|
|
||||||
if (response.status != 200 && response.status != 201) {
|
|
||||||
show_message('Failed to fetch updates.');
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.status == 201) {
|
showVersionSelectorDialog(versions, obj.current, async (selected_version) => {
|
||||||
show_message("There is an updated extension available.<BR><BR><P><B>NOTE:<BR>Fetch Updates is not an update.<BR>Please update from <button id='cm-install-customnodes-button'>Install Custom Nodes</button> </B></P>");
|
if(selected_version == 'nightly') {
|
||||||
|
update_policy_combo.value = 'nightly-comfyui';
|
||||||
|
api.fetchApi('/manager/policy/update?value=nightly-comfyui');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
update_policy_combo.value = 'stable-comfyui';
|
||||||
|
api.fetchApi('/manager/policy/update?value=stable-comfyui');
|
||||||
|
}
|
||||||
|
|
||||||
const button = document.getElementById('cm-install-customnodes-button');
|
let response = await api.fetchApi(`/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
|
||||||
button.addEventListener("click",
|
if (response.status == 200) {
|
||||||
async function() {
|
infoToast(`ComfyUI version is switched to ${selected_version}`);
|
||||||
app.ui.dialog.close();
|
}
|
||||||
|
else {
|
||||||
|
customAlert('Failed to switch ComfyUI version.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
customAlert('Failed to fetch ComfyUI versions.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(!CustomNodesManager.instance) {
|
async function onQueueStatus(event) {
|
||||||
CustomNodesManager.instance = new CustomNodesManager(app, self);
|
const isElectron = 'electronAPI' in window;
|
||||||
|
|
||||||
|
if(event.detail.status == 'in_progress') {
|
||||||
|
set_inprogress_mode();
|
||||||
|
update_all_button.innerText = `in progress.. (${event.detail.done_count}/${event.detail.total_count})`;
|
||||||
|
}
|
||||||
|
else if(event.detail.status == 'done') {
|
||||||
|
reset_action_buttons();
|
||||||
|
|
||||||
|
if(!is_updating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_updating = false;
|
||||||
|
|
||||||
|
let success_list = [];
|
||||||
|
let failed_list = [];
|
||||||
|
let comfyui_state = null;
|
||||||
|
|
||||||
|
for(let k in event.detail.nodepack_result){
|
||||||
|
let v = event.detail.nodepack_result[k];
|
||||||
|
|
||||||
|
if(k == 'comfyui') {
|
||||||
|
comfyui_state = v;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(v.msg == 'success') {
|
||||||
|
success_list.push(k);
|
||||||
|
}
|
||||||
|
else if(v.msg != 'skip')
|
||||||
|
failed_list.push(k);
|
||||||
|
}
|
||||||
|
|
||||||
|
let msg = "";
|
||||||
|
|
||||||
|
if(success_list.length == 0 && comfyui_state.startsWith('skip')) {
|
||||||
|
if(failed_list.length == 0) {
|
||||||
|
msg += "You are already up to date.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg = "To apply the updates, you need to <button class='cm-small-button' id='cm-reboot-button5'>RESTART</button> ComfyUI.<hr>";
|
||||||
|
|
||||||
|
if(comfyui_state == 'success-nightly') {
|
||||||
|
msg += "ComfyUI has been updated to latest nightly version.<BR><BR>";
|
||||||
|
infoToast("ComfyUI has been updated to the latest nightly version.");
|
||||||
|
}
|
||||||
|
else if(comfyui_state.startsWith('success-stable')) {
|
||||||
|
const ver = comfyui_state.split("-").pop();
|
||||||
|
msg += `ComfyUI has been updated to ${ver}.<BR><BR>`;
|
||||||
|
infoToast(`ComfyUI has been updated to ${ver}`);
|
||||||
|
}
|
||||||
|
else if(comfyui_state == 'skip') {
|
||||||
|
msg += "ComfyUI is already up to date.<BR><BR>"
|
||||||
|
}
|
||||||
|
else if(comfyui_state != null) {
|
||||||
|
msg += "Failed to update ComfyUI.<BR><BR>"
|
||||||
|
}
|
||||||
|
|
||||||
|
if(success_list.length > 0) {
|
||||||
|
msg += "The following custom nodes have been updated:<ul>";
|
||||||
|
for(let x in success_list) {
|
||||||
|
let k = success_list[x];
|
||||||
|
let url = event.detail.nodepack_result[k].url;
|
||||||
|
let title = event.detail.nodepack_result[k].title;
|
||||||
|
if(url) {
|
||||||
|
msg += `<li><a href='${url}' target='_blank'>${title}</a></li>`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
msg += `<li>${k}</li>`;
|
||||||
}
|
}
|
||||||
await CustomNodesManager.instance.show(CustomNodesManager.ShowMode.UPDATE);
|
|
||||||
}
|
}
|
||||||
);
|
msg += "</ul>";
|
||||||
|
}
|
||||||
|
|
||||||
update_check_checkbox.checked = false;
|
setNeedRestart(true);
|
||||||
}
|
|
||||||
else {
|
|
||||||
show_message('All extensions are already up-to-date with the latest versions.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
if(failed_list.length > 0) {
|
||||||
}
|
msg += '<br>The update for the following custom nodes has failed:<ul>';
|
||||||
catch (exception) {
|
for(let x in failed_list) {
|
||||||
show_message(`Failed to update custom nodes / ${exception}`);
|
let k = failed_list[x];
|
||||||
return false;
|
let url = event.detail.nodepack_result[k].url;
|
||||||
}
|
let title = event.detail.nodepack_result[k].title;
|
||||||
finally {
|
if(url) {
|
||||||
fetch_updates_button.disabled = false;
|
msg += `<li><a href='${url}' target='_blank'>${title}</a></li>`;
|
||||||
fetch_updates_button.innerText = prev_text;
|
}
|
||||||
fetch_updates_button.style.backgroundColor = "";
|
else {
|
||||||
|
msg += `<li>${k}</li>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += '</ul>'
|
||||||
|
}
|
||||||
|
|
||||||
|
show_message(msg);
|
||||||
|
|
||||||
|
const rebootButton = document.getElementById('cm-reboot-button5');
|
||||||
|
rebootButton?.addEventListener("click",
|
||||||
|
function() {
|
||||||
|
if(rebootAPI()) {
|
||||||
|
manager_dialog.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateAll(update_check_checkbox, manager_dialog) {
|
api.addEventListener("cm-queue-status", onQueueStatus);
|
||||||
let prev_text = update_all_button.innerText;
|
|
||||||
update_all_button.innerText = "Updating all...(ComfyUI)";
|
|
||||||
update_all_button.disabled = true;
|
|
||||||
update_all_button.style.backgroundColor = "gray";
|
|
||||||
|
|
||||||
try {
|
|
||||||
var mode = manager_instance.datasrc_combo.value;
|
|
||||||
|
|
||||||
update_all_button.innerText = "Updating all...";
|
async function updateAll(update_comfyui) {
|
||||||
const response1 = await api.fetchApi('/comfyui_manager/update_comfyui');
|
update_all_button.innerText = "Updating...";
|
||||||
const response2 = await api.fetchApi(`/customnode/update_all?mode=${mode}`);
|
|
||||||
|
|
||||||
if (response2.status == 403) {
|
set_inprogress_mode();
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response1.status == 400 || response2.status == 400) {
|
var mode = manager_instance.datasrc_combo.value;
|
||||||
show_message('Failed to update ComfyUI or several extensions.<BR><BR>See terminal log.<BR>');
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(response1.status == 201 || response2.status == 201) {
|
showTerminal();
|
||||||
const update_info = await response2.json();
|
|
||||||
|
|
||||||
let failed_list = "";
|
if(update_comfyui) {
|
||||||
if(update_info.failed.length > 0) {
|
update_all_button.innerText = "Updating ComfyUI...";
|
||||||
failed_list = "<BR>FAILED: "+update_info.failed.join(", ");
|
await api.fetchApi('/manager/queue/update_comfyui');
|
||||||
}
|
|
||||||
|
|
||||||
let updated_list = "";
|
|
||||||
if(update_info.updated.length > 0) {
|
|
||||||
updated_list = "<BR>UPDATED: "+update_info.updated.join(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
show_message(
|
|
||||||
"ComfyUI and all extensions have been updated to the latest version.<BR>To apply the updated custom node, please <button class='cm-small-button' id='cm-reboot-button5'>RESTART</button> ComfyUI. And refresh browser.<BR>"
|
|
||||||
+failed_list
|
|
||||||
+updated_list
|
|
||||||
);
|
|
||||||
|
|
||||||
const rebootButton = document.getElementById('cm-reboot-button5');
|
|
||||||
rebootButton.addEventListener("click",
|
|
||||||
function() {
|
|
||||||
if(rebootAPI()) {
|
|
||||||
manager_dialog.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
show_message('ComfyUI and all extensions are already up-to-date with the latest versions.');
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
catch (exception) {
|
|
||||||
show_message(`Failed to update ComfyUI or several extensions / ${exception}`);
|
const response = await api.fetchApi(`/manager/queue/update_all?mode=${mode}`);
|
||||||
return false;
|
|
||||||
|
if (response.status == 401) {
|
||||||
|
customAlert('Another task is already in progress. Please stop the ongoing task first.');
|
||||||
}
|
}
|
||||||
finally {
|
else if(response.status == 200) {
|
||||||
update_all_button.disabled = false;
|
is_updating = true;
|
||||||
update_all_button.innerText = prev_text;
|
await api.fetchApi('/manager/queue/start');
|
||||||
update_all_button.style.backgroundColor = "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,15 +809,29 @@ const isOutputNode = (node) => {
|
|||||||
return SUPPORTED_OUTPUT_NODE_TYPES.includes(node.type);
|
return SUPPORTED_OUTPUT_NODE_TYPES.includes(node.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function restartOrStop() {
|
||||||
|
if(restart_stop_button.innerText == 'Restart'){
|
||||||
|
rebootAPI();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
api.fetchApi('/manager/queue/reset');
|
||||||
|
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -----------
|
// -----------
|
||||||
class ManagerMenuDialog extends ComfyDialog {
|
class ManagerMenuDialog extends ComfyDialog {
|
||||||
createControlsMid() {
|
createControlsMid() {
|
||||||
let self = this;
|
let self = this;
|
||||||
|
const isElectron = 'electronAPI' in window;
|
||||||
|
|
||||||
update_comfyui_button =
|
update_comfyui_button =
|
||||||
$el("button.cm-button", {
|
$el("button.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Update ComfyUI",
|
textContent: "Update ComfyUI",
|
||||||
|
style: {
|
||||||
|
display: isElectron ? 'none' : 'block'
|
||||||
|
},
|
||||||
onclick:
|
onclick:
|
||||||
() => updateComfyUI()
|
() => updateComfyUI()
|
||||||
});
|
});
|
||||||
@@ -824,25 +840,38 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
$el("button.cm-button", {
|
$el("button.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Switch ComfyUI",
|
textContent: "Switch ComfyUI",
|
||||||
|
style: {
|
||||||
|
display: isElectron ? 'none' : 'block'
|
||||||
|
},
|
||||||
onclick:
|
onclick:
|
||||||
() => switchComfyUI()
|
() => switchComfyUI()
|
||||||
});
|
});
|
||||||
|
|
||||||
fetch_updates_button =
|
restart_stop_button =
|
||||||
$el("button.cm-button", {
|
$el("button.cm-button-red", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Fetch Updates",
|
textContent: "Restart",
|
||||||
onclick:
|
onclick: () => restartOrStop()
|
||||||
() => fetchUpdates(this.update_check_checkbox)
|
|
||||||
});
|
});
|
||||||
|
|
||||||
update_all_button =
|
if(isElectron) {
|
||||||
$el("button.cm-button", {
|
update_all_button =
|
||||||
type: "button",
|
$el("button.cm-button", {
|
||||||
textContent: "Update All",
|
type: "button",
|
||||||
onclick:
|
textContent: "Update All Custom Nodes",
|
||||||
() => updateAll(this.update_check_checkbox, self)
|
onclick:
|
||||||
});
|
() => updateAll(false)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
update_all_button =
|
||||||
|
$el("button.cm-button", {
|
||||||
|
type: "button",
|
||||||
|
textContent: "Update All",
|
||||||
|
onclick:
|
||||||
|
() => updateAll(true)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const res =
|
const res =
|
||||||
[
|
[
|
||||||
@@ -870,7 +899,19 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
$el("button.cm-button", {
|
||||||
|
type: "button",
|
||||||
|
textContent: "Custom Nodes In Workflow",
|
||||||
|
onclick:
|
||||||
|
() => {
|
||||||
|
if(!CustomNodesManager.instance) {
|
||||||
|
CustomNodesManager.instance = new CustomNodesManager(app, self);
|
||||||
|
}
|
||||||
|
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.IN_WORKFLOW);
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
$el("br", {}, []),
|
||||||
$el("button.cm-button", {
|
$el("button.cm-button", {
|
||||||
type: "button",
|
type: "button",
|
||||||
textContent: "Model Manager",
|
textContent: "Model Manager",
|
||||||
@@ -899,27 +940,10 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
update_all_button,
|
update_all_button,
|
||||||
update_comfyui_button,
|
update_comfyui_button,
|
||||||
switch_comfyui_button,
|
switch_comfyui_button,
|
||||||
fetch_updates_button,
|
// fetch_updates_button,
|
||||||
|
|
||||||
$el("br", {}, []),
|
$el("br", {}, []),
|
||||||
$el("button.cm-button", {
|
restart_stop_button,
|
||||||
type: "button",
|
|
||||||
textContent: "Alternatives of A1111",
|
|
||||||
onclick:
|
|
||||||
() => {
|
|
||||||
if(!CustomNodesManager.instance) {
|
|
||||||
CustomNodesManager.instance = new CustomNodesManager(app, self);
|
|
||||||
}
|
|
||||||
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.ALTERNATIVES);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
$el("br", {}, []),
|
|
||||||
$el("button.cm-button-red", {
|
|
||||||
type: "button",
|
|
||||||
textContent: "Restart",
|
|
||||||
onclick: () => rebootAPI()
|
|
||||||
}),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let migration_btn =
|
let migration_btn =
|
||||||
@@ -948,13 +972,9 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
createControlsLeft() {
|
createControlsLeft() {
|
||||||
let self = this;
|
const isElectron = 'electronAPI' in window;
|
||||||
|
|
||||||
this.update_check_checkbox = $el("input",{type:'checkbox', id:"skip_update_check"},[])
|
let self = this;
|
||||||
const uc_checkbox_text = $el("label",{for:"skip_update_check"},[" Skip update check"])
|
|
||||||
uc_checkbox_text.style.color = "var(--fg-color)";
|
|
||||||
uc_checkbox_text.style.cursor = "pointer";
|
|
||||||
this.update_check_checkbox.checked = true;
|
|
||||||
|
|
||||||
// db mode
|
// db mode
|
||||||
this.datasrc_combo = document.createElement("select");
|
this.datasrc_combo = document.createElement("select");
|
||||||
@@ -964,6 +984,14 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
|
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
|
||||||
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, []));
|
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, []));
|
||||||
|
|
||||||
|
api.fetchApi('/manager/db_mode')
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(data => { this.datasrc_combo.value = data; });
|
||||||
|
|
||||||
|
this.datasrc_combo.addEventListener('change', function (event) {
|
||||||
|
api.fetchApi(`/manager/db_mode?value=${event.target.value}`);
|
||||||
|
});
|
||||||
|
|
||||||
// preview method
|
// preview method
|
||||||
let preview_combo = document.createElement("select");
|
let preview_combo = document.createElement("select");
|
||||||
preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process.");
|
preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process.");
|
||||||
@@ -1008,21 +1036,6 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// default ui state
|
|
||||||
let default_ui_combo = document.createElement("select");
|
|
||||||
default_ui_combo.setAttribute("title", "Set the default state to be displayed in the main menu when the browser starts.");
|
|
||||||
default_ui_combo.className = "cm-menu-combo";
|
|
||||||
default_ui_combo.appendChild($el('option', { value: 'none', text: 'Default UI: None' }, []));
|
|
||||||
default_ui_combo.appendChild($el('option', { value: 'history', text: 'Default UI: History' }, []));
|
|
||||||
default_ui_combo.appendChild($el('option', { value: 'queue', text: 'Default UI: Queue' }, []));
|
|
||||||
api.fetchApi('/manager/default_ui')
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(data => { default_ui_combo.value = data; });
|
|
||||||
|
|
||||||
default_ui_combo.addEventListener('change', function (event) {
|
|
||||||
api.fetchApi(`/manager/default_ui?value=${event.target.value}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// share
|
// share
|
||||||
let share_combo = document.createElement("select");
|
let share_combo = document.createElement("select");
|
||||||
@@ -1041,25 +1054,6 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
|
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
|
||||||
}
|
}
|
||||||
|
|
||||||
// default ui state
|
|
||||||
let component_policy_combo = document.createElement("select");
|
|
||||||
component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
|
|
||||||
component_policy_combo.className = "cm-menu-combo";
|
|
||||||
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, []));
|
|
||||||
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, []));
|
|
||||||
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, []));
|
|
||||||
api.fetchApi('/manager/component/policy')
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(data => {
|
|
||||||
component_policy_combo.value = data;
|
|
||||||
set_component_policy(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
component_policy_combo.addEventListener('change', function (event) {
|
|
||||||
api.fetchApi(`/manager/component/policy?value=${event.target.value}`);
|
|
||||||
set_component_policy(event.target.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
api.fetchApi('/manager/share_option')
|
api.fetchApi('/manager/share_option')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
@@ -1079,15 +1073,51 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let component_policy_combo = document.createElement("select");
|
||||||
|
component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
|
||||||
|
component_policy_combo.className = "cm-menu-combo";
|
||||||
|
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, []));
|
||||||
|
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, []));
|
||||||
|
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, []));
|
||||||
|
api.fetchApi('/manager/policy/component')
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(data => {
|
||||||
|
component_policy_combo.value = data;
|
||||||
|
set_component_policy(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
component_policy_combo.addEventListener('change', function (event) {
|
||||||
|
api.fetchApi(`/manager/policy/component?value=${event.target.value}`);
|
||||||
|
set_component_policy(event.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
update_policy_combo = document.createElement("select");
|
||||||
|
|
||||||
|
if(isElectron)
|
||||||
|
update_policy_combo.style.display = 'none';
|
||||||
|
|
||||||
|
update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update.");
|
||||||
|
update_policy_combo.className = "cm-menu-combo";
|
||||||
|
update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, []));
|
||||||
|
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly Version' }, []));
|
||||||
|
api.fetchApi('/manager/policy/update')
|
||||||
|
.then(response => response.text())
|
||||||
|
.then(data => {
|
||||||
|
update_policy_combo.value = data;
|
||||||
|
});
|
||||||
|
|
||||||
|
update_policy_combo.addEventListener('change', function (event) {
|
||||||
|
api.fetchApi(`/manager/policy/update?value=${event.target.value}`);
|
||||||
|
});
|
||||||
|
|
||||||
return [
|
return [
|
||||||
$el("div", {}, [this.update_check_checkbox, uc_checkbox_text]),
|
|
||||||
$el("br", {}, []),
|
$el("br", {}, []),
|
||||||
this.datasrc_combo,
|
this.datasrc_combo,
|
||||||
channel_combo,
|
channel_combo,
|
||||||
preview_combo,
|
preview_combo,
|
||||||
default_ui_combo,
|
|
||||||
share_combo,
|
share_combo,
|
||||||
component_policy_combo,
|
component_policy_combo,
|
||||||
|
update_policy_combo,
|
||||||
$el("br", {}, []),
|
$el("br", {}, []),
|
||||||
|
|
||||||
$el("br", {}, []),
|
$el("br", {}, []),
|
||||||
@@ -1114,11 +1144,6 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
install_pip(url, self);
|
install_pip(url, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
|
||||||
$el("button.cm-experimental-button", {
|
|
||||||
type: "button",
|
|
||||||
textContent: "Unload models",
|
|
||||||
onclick: () => { free_models(); }
|
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
];
|
];
|
||||||
@@ -1247,7 +1272,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
$el("div.comfy-modal-content",
|
$el("div.comfy-modal-content",
|
||||||
[
|
[
|
||||||
$el("tr.cm-title", {}, [
|
$el("tr.cm-title", {}, [
|
||||||
$el("font", {size:6, color:"white"}, [`ComfyUI Manager Menu`])]
|
$el("font", {size:6, color:"white"}, [`ComfyUI Manager ${manager_version}`])]
|
||||||
),
|
),
|
||||||
$el("br", {}, []),
|
$el("br", {}, []),
|
||||||
$el("div.cm-menu-container",
|
$el("div.cm-menu-container",
|
||||||
@@ -1268,10 +1293,22 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]);
|
this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isVisible() {
|
||||||
|
return this.element?.style?.display !== "none";
|
||||||
|
}
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
this.element.style.display = "block";
|
this.element.style.display = "block";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toggleVisibility() {
|
||||||
|
if (this.isVisible) {
|
||||||
|
this.close();
|
||||||
|
} else {
|
||||||
|
this.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handleWorkflowGalleryButtonClick(e) {
|
handleWorkflowGalleryButtonClick(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@@ -1377,18 +1414,52 @@ async function getVersion() {
|
|||||||
return await version.text();
|
return await version.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.ManagerMenu",
|
name: "Comfy.ManagerMenu",
|
||||||
|
|
||||||
aboutPageBadges: [
|
aboutPageBadges: [
|
||||||
{
|
{
|
||||||
label: `ComfyUI-Manager ${await getVersion()}`,
|
label: `ComfyUI-Manager ${manager_version}`,
|
||||||
url: 'https://github.com/ltdrdata/ComfyUI-Manager',
|
url: 'https://github.com/ltdrdata/ComfyUI-Manager',
|
||||||
icon: 'pi pi-th-large'
|
icon: 'pi pi-th-large'
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
id: "Comfy.Manager.Menu.ToggleVisibility",
|
||||||
|
label: "Toggle Manager Menu Visibility",
|
||||||
|
icon: "mdi mdi-puzzle",
|
||||||
|
function: () => {
|
||||||
|
if (!manager_instance) {
|
||||||
|
setManagerInstance(new ManagerMenuDialog());
|
||||||
|
manager_instance.show();
|
||||||
|
} else {
|
||||||
|
manager_instance.toggleVisibility();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "Comfy.Manager.CustomNodesManager.ToggleVisibility",
|
||||||
|
label: "Toggle Custom Nodes Manager Visibility",
|
||||||
|
icon: "pi pi-server",
|
||||||
|
function: () => {
|
||||||
|
if (CustomNodesManager.instance?.isVisible) {
|
||||||
|
CustomNodesManager.instance.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manager_instance) {
|
||||||
|
setManagerInstance(new ManagerMenuDialog());
|
||||||
|
}
|
||||||
|
if (!CustomNodesManager.instance) {
|
||||||
|
CustomNodesManager.instance = new CustomNodesManager(app, self);
|
||||||
|
}
|
||||||
|
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.NORMAL);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
$el("style", {
|
$el("style", {
|
||||||
textContent: style,
|
textContent: style,
|
||||||
@@ -1583,27 +1654,3 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
async function set_default_ui()
|
|
||||||
{
|
|
||||||
let res = await api.fetchApi('/manager/default_ui');
|
|
||||||
if(res.status == 200) {
|
|
||||||
let mode = await res.text();
|
|
||||||
switch(mode) {
|
|
||||||
case 'history':
|
|
||||||
app.ui.queue.hide();
|
|
||||||
app.ui.history.show();
|
|
||||||
break;
|
|
||||||
case 'queue':
|
|
||||||
app.ui.queue.show();
|
|
||||||
app.ui.history.hide();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// do nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_default_ui();
|
|
||||||
95
js/common.js
95
js/common.js
@@ -413,10 +413,93 @@ export const icons = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function sanitizeHTML(str) {
|
export function sanitizeHTML(str) {
|
||||||
return str
|
return str
|
||||||
.replace(/&/g, "&")
|
.replace(/&/g, "&")
|
||||||
.replace(/</g, "<")
|
.replace(/</g, "<")
|
||||||
.replace(/>/g, ">")
|
.replace(/>/g, ">")
|
||||||
.replace(/"/g, """)
|
.replace(/"/g, """)
|
||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showTerminal() {
|
||||||
|
try {
|
||||||
|
const panel = app.extensionManager.bottomPanel;
|
||||||
|
const isTerminalVisible = panel.bottomPanelVisible && panel.activeBottomPanelTab.id === 'logs-terminal';
|
||||||
|
if (!isTerminalVisible)
|
||||||
|
panel.toggleBottomPanelTab('logs-terminal');
|
||||||
|
}
|
||||||
|
catch(exception) {
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let need_restart = false;
|
||||||
|
|
||||||
|
export function setNeedRestart(value) {
|
||||||
|
need_restart = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onReconnected(event) {
|
||||||
|
if(need_restart) {
|
||||||
|
setNeedRestart(false);
|
||||||
|
|
||||||
|
const confirmed = await customConfirm("To apply the changes to the node pack's installation status, you need to refresh the browser. Would you like to refresh?");
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.reload(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api.addEventListener('reconnected', onReconnected);
|
||||||
|
|
||||||
|
const storeId = "comfyui-manager-grid";
|
||||||
|
let timeId;
|
||||||
|
export function storeColumnWidth(gridId, columnItem) {
|
||||||
|
clearTimeout(timeId);
|
||||||
|
timeId = setTimeout(() => {
|
||||||
|
let data = {};
|
||||||
|
const dataStr = localStorage.getItem(storeId);
|
||||||
|
if (dataStr) {
|
||||||
|
try {
|
||||||
|
data = JSON.parse(dataStr);
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!data[gridId]) {
|
||||||
|
data[gridId] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
data[gridId][columnItem.id] = columnItem.width;
|
||||||
|
|
||||||
|
localStorage.setItem(storeId, JSON.stringify(data));
|
||||||
|
|
||||||
|
}, 200)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function restoreColumnWidth(gridId, columns) {
|
||||||
|
const dataStr = localStorage.getItem(storeId);
|
||||||
|
if (!dataStr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = JSON.parse(dataStr);
|
||||||
|
} catch (e) {}
|
||||||
|
if(!data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const widthMap = data[gridId];
|
||||||
|
if (!widthMap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
columns.forEach(columnItem => {
|
||||||
|
const w = widthMap[columnItem.id];
|
||||||
|
if (w) {
|
||||||
|
columnItem.width = w;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -709,7 +709,7 @@ app.handleFile = handleFile;
|
|||||||
|
|
||||||
let current_component_policy = 'workflow';
|
let current_component_policy = 'workflow';
|
||||||
try {
|
try {
|
||||||
api.fetchApi('/manager/component/policy')
|
api.fetchApi('/manager/policy/component')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => { current_component_policy = data; });
|
.then(data => { current_component_policy = data; });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,16 @@ import { api } from "../../scripts/api.js";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
manager_instance, rebootAPI, install_via_git_url,
|
manager_instance, rebootAPI, install_via_git_url,
|
||||||
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt, sanitizeHTML, infoToast
|
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
|
||||||
|
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
|
||||||
|
storeColumnWidth, restoreColumnWidth
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
|
|
||||||
// https://cenfun.github.io/turbogrid/api.html
|
// https://cenfun.github.io/turbogrid/api.html
|
||||||
import TG from "./turbogrid.esm.js";
|
import TG from "./turbogrid.esm.js";
|
||||||
|
|
||||||
|
const gridId = "node";
|
||||||
|
|
||||||
const pageCss = `
|
const pageCss = `
|
||||||
.cn-manager {
|
.cn-manager {
|
||||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segue UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
--grid-font: -apple-system, BlinkMacSystemFont, "Segue UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
@@ -55,6 +59,12 @@ const pageCss = `
|
|||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cn-manager .cn-manager-stop {
|
||||||
|
display: none;
|
||||||
|
background-color: #500000;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.cn-manager .cn-manager-back {
|
.cn-manager .cn-manager-back {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -344,14 +354,16 @@ const pageHtml = `
|
|||||||
<div class="cn-manager-selection"></div>
|
<div class="cn-manager-selection"></div>
|
||||||
<div class="cn-manager-message"></div>
|
<div class="cn-manager-message"></div>
|
||||||
<div class="cn-manager-footer">
|
<div class="cn-manager-footer">
|
||||||
<button class="cn-manager-back">
|
<button class="cn-manager-back">
|
||||||
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
</svg>
|
</svg>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button class="cn-manager-restart">Restart</button>
|
<button class="cn-manager-restart">Restart</button>
|
||||||
|
<button class="cn-manager-stop">Stop</button>
|
||||||
<div class="cn-flex-auto"></div>
|
<div class="cn-flex-auto"></div>
|
||||||
|
<button class="cn-manager-used-in-workflow">Used In Workflow</button>
|
||||||
<button class="cn-manager-check-update">Check Update</button>
|
<button class="cn-manager-check-update">Check Update</button>
|
||||||
<button class="cn-manager-check-missing">Check Missing</button>
|
<button class="cn-manager-check-missing">Check Missing</button>
|
||||||
<button class="cn-manager-install-url">Install via Git URL</button>
|
<button class="cn-manager-install-url">Install via Git URL</button>
|
||||||
@@ -363,7 +375,8 @@ const ShowMode = {
|
|||||||
UPDATE: "Update",
|
UPDATE: "Update",
|
||||||
MISSING: "Missing",
|
MISSING: "Missing",
|
||||||
FAVORITES: "Favorites",
|
FAVORITES: "Favorites",
|
||||||
ALTERNATIVES: "Alternatives"
|
ALTERNATIVES: "Alternatives",
|
||||||
|
IN_WORKFLOW: "In Workflow",
|
||||||
};
|
};
|
||||||
|
|
||||||
export class CustomNodesManager {
|
export class CustomNodesManager {
|
||||||
@@ -392,7 +405,7 @@ export class CustomNodesManager {
|
|||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
api.addEventListener("cm-install-status", this.onInstallStatus);
|
api.addEventListener("cm-queue-status", this.onQueueStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -575,6 +588,10 @@ export class CustomNodesManager {
|
|||||||
label: "Update",
|
label: "Update",
|
||||||
value: ShowMode.UPDATE,
|
value: ShowMode.UPDATE,
|
||||||
hasData: false
|
hasData: false
|
||||||
|
}, {
|
||||||
|
label: "In Workflow",
|
||||||
|
value: ShowMode.IN_WORKFLOW,
|
||||||
|
hasData: false
|
||||||
}, {
|
}, {
|
||||||
label: "Missing",
|
label: "Missing",
|
||||||
value: ShowMode.MISSING,
|
value: ShowMode.MISSING,
|
||||||
@@ -659,7 +676,7 @@ export class CustomNodesManager {
|
|||||||
"invalid-installation": ["reinstall"],
|
"invalid-installation": ["reinstall"],
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!manager_instance.update_check_checkbox.checked) {
|
if (!installGroups.updatable) {
|
||||||
installGroups.enabled = installGroups.enabled.filter(it => it !== "try-update");
|
installGroups.enabled = installGroups.enabled.filter(it => it !== "try-update");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -715,7 +732,7 @@ export class CustomNodesManager {
|
|||||||
const value = e.target.value
|
const value = e.target.value
|
||||||
this.filter = value;
|
this.filter = value;
|
||||||
const item = this.getFilterItem(value);
|
const item = this.getFilterItem(value);
|
||||||
if (item && !item.hasData) {
|
if (item && (!item.hasData)) {
|
||||||
this.loadData(value);
|
this.loadData(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -755,10 +772,24 @@ export class CustomNodesManager {
|
|||||||
|
|
||||||
".cn-manager-restart": {
|
".cn-manager-restart": {
|
||||||
click: () => {
|
click: () => {
|
||||||
if(rebootAPI()) {
|
this.close();
|
||||||
this.close();
|
this.manager_dialog.close();
|
||||||
this.manager_dialog.close();
|
rebootAPI();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
".cn-manager-stop": {
|
||||||
|
click: () => {
|
||||||
|
api.fetchApi('/manager/queue/reset');
|
||||||
|
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
".cn-manager-used-in-workflow": {
|
||||||
|
click: (e) => {
|
||||||
|
e.target.classList.add("cn-btn-loading");
|
||||||
|
this.setFilter(ShowMode.IN_WORKFLOW);
|
||||||
|
this.loadData(ShowMode.IN_WORKFLOW);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -818,6 +849,10 @@ export class CustomNodesManager {
|
|||||||
this.renderSelected();
|
this.renderSelected();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
grid.bind("onColumnWidthChanged", (e, columnItem) => {
|
||||||
|
storeColumnWidth(gridId, columnItem)
|
||||||
|
});
|
||||||
|
|
||||||
grid.bind('onClick', (e, d) => {
|
grid.bind('onClick', (e, d) => {
|
||||||
const btn = this.getButton(d.e.target);
|
const btn = this.getButton(d.e.target);
|
||||||
if (btn) {
|
if (btn) {
|
||||||
@@ -1145,6 +1180,8 @@ export class CustomNodesManager {
|
|||||||
return 0;
|
return 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
restoreColumnWidth(gridId, columns);
|
||||||
|
|
||||||
this.grid.setData({
|
this.grid.setData({
|
||||||
options: options,
|
options: options,
|
||||||
rows: rows_values,
|
rows: rows_values,
|
||||||
@@ -1271,9 +1308,9 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async installNodes(list, btn, title, selected_version) {
|
async installNodes(list, btn, title, selected_version) {
|
||||||
let stats = await api.fetchApi('/customnode/queue/count');
|
let stats = await api.fetchApi('/manager/queue/status');
|
||||||
stats = await stats.json();
|
stats = await stats.json();
|
||||||
if(stats.total_count > 0) {
|
if(stats.is_processing) {
|
||||||
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
|
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1304,11 +1341,13 @@ export class CustomNodesManager {
|
|||||||
let needRestart = false;
|
let needRestart = false;
|
||||||
let errorMsg = "";
|
let errorMsg = "";
|
||||||
|
|
||||||
await api.fetchApi('/customnode/queue/reset');
|
await api.fetchApi('/manager/queue/reset');
|
||||||
this.install_context = btn;
|
|
||||||
|
let target_items = [];
|
||||||
|
|
||||||
for (const hash of list) {
|
for (const hash of list) {
|
||||||
const item = this.grid.getRowItemBy("hash", hash);
|
const item = this.grid.getRowItemBy("hash", hash);
|
||||||
|
target_items.push(item);
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
errorMsg = `Not found custom node: ${hash}`;
|
errorMsg = `Not found custom node: ${hash}`;
|
||||||
@@ -1347,38 +1386,48 @@ export class CustomNodesManager {
|
|||||||
api_mode = 'reinstall';
|
api_mode = 'reinstall';
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await api.fetchApi(`/customnode/queue/${api_mode}`, {
|
const res = await api.fetchApi(`/manager/queue/${api_mode}`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status != 200) {
|
if (res.status != 200) {
|
||||||
errorMsg = `${item.title} ${mode} failed: `;
|
errorMsg = `'${item.title}': `;
|
||||||
|
|
||||||
if(res.status == 403) {
|
if(res.status == 403) {
|
||||||
errorMsg += `This action is not allowed with this security level configuration.`;
|
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
||||||
} else if(res.status == 404) {
|
} else if(res.status == 404) {
|
||||||
errorMsg += `With the current security level configuration, only custom nodes from the <B>"default channel"</B> can be installed.`;
|
errorMsg += `With the current security level configuration, only custom nodes from the <B>"default channel"</B> can be installed.\n`;
|
||||||
} else {
|
} else {
|
||||||
errorMsg += await res.text();
|
errorMsg += await res.text() + '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.install_context = {btn: btn, targets: target_items};
|
||||||
|
|
||||||
if(errorMsg) {
|
if(errorMsg) {
|
||||||
this.showError(errorMsg);
|
this.showError(errorMsg);
|
||||||
show_message("Installation Error:\n"+errorMsg);
|
show_message("[Installation Errors]\n"+errorMsg);
|
||||||
|
|
||||||
|
// reset
|
||||||
|
for(let k in target_items) {
|
||||||
|
const item = target_items[k];
|
||||||
|
this.grid.updateCell(item, "action");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await api.fetchApi('/customnode/queue/start');
|
await api.fetchApi('/manager/queue/start');
|
||||||
|
this.showStop();
|
||||||
|
showTerminal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onInstallStatus(event) {
|
async onQueueStatus(event) {
|
||||||
let self = CustomNodesManager.instance;
|
let self = CustomNodesManager.instance;
|
||||||
if(event.detail.status == 'in_progress') {
|
if(event.detail.status == 'in_progress' && event.detail.ui_target == 'nodepack_manager') {
|
||||||
const hash = event.detail.target;
|
const hash = event.detail.target;
|
||||||
|
|
||||||
const item = self.grid.getRowItemBy("hash", hash);
|
const item = self.grid.getRowItemBy("hash", hash);
|
||||||
@@ -1386,14 +1435,20 @@ export class CustomNodesManager {
|
|||||||
item.restart = true;
|
item.restart = true;
|
||||||
self.restartMap[item.hash] = true;
|
self.restartMap[item.hash] = true;
|
||||||
self.grid.updateCell(item, "action");
|
self.grid.updateCell(item, "action");
|
||||||
|
self.grid.setRowSelected(item, false);
|
||||||
}
|
}
|
||||||
else if(event.detail.status == 'done') {
|
else if(event.detail.status == 'done') {
|
||||||
self.onInstallCompleted(event.detail);
|
self.hideStop();
|
||||||
|
self.onQueueCompleted(event.detail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onInstallCompleted(info) {
|
async onQueueCompleted(info) {
|
||||||
let result = info.result;
|
let result = info.nodepack_result;
|
||||||
|
|
||||||
|
if(result.length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let self = CustomNodesManager.instance;
|
let self = CustomNodesManager.instance;
|
||||||
|
|
||||||
@@ -1401,7 +1456,7 @@ export class CustomNodesManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { target, label, mode } = self.install_context;
|
const { target, label, mode } = self.install_context.btn;
|
||||||
target.classList.remove("cn-btn-loading");
|
target.classList.remove("cn-btn-loading");
|
||||||
|
|
||||||
let errorMsg = "";
|
let errorMsg = "";
|
||||||
@@ -1409,11 +1464,13 @@ export class CustomNodesManager {
|
|||||||
for(let hash in result){
|
for(let hash in result){
|
||||||
let v = result[hash];
|
let v = result[hash];
|
||||||
|
|
||||||
const item = self.grid.getRowItemBy("hash", hash);
|
if(v != 'success' && v != 'skip')
|
||||||
self.grid.setRowSelected(item, false);
|
errorMsg += v+'\n';
|
||||||
|
}
|
||||||
|
|
||||||
if(v != 'success')
|
for(let k in self.install_context.targets) {
|
||||||
errorMsg += v;
|
let item = self.install_context.targets[k];
|
||||||
|
self.grid.updateCell(item, "action");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMsg) {
|
if (errorMsg) {
|
||||||
@@ -1426,7 +1483,7 @@ export class CustomNodesManager {
|
|||||||
self.showRestart();
|
self.showRestart();
|
||||||
self.showMessage(`To apply the installed/updated/disabled/enabled custom node, please restart ComfyUI. And refresh browser.`, "red");
|
self.showMessage(`To apply the installed/updated/disabled/enabled custom node, please restart ComfyUI. And refresh browser.`, "red");
|
||||||
|
|
||||||
infoToast(`[ComfyUI-Manager] All tasks in the queue have been completed.\n${info.done_count}/${info.total_count}`);
|
infoToast(`[ComfyUI-Manager] All node pack tasks in the queue have been completed.\n${info.done_count}/${info.total_count}`);
|
||||||
self.install_context = undefined;
|
self.install_context = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1486,7 +1543,128 @@ export class CustomNodesManager {
|
|||||||
return extension_mappings;
|
return extension_mappings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getNodesInWorkflow() {
|
||||||
|
let usedGroupNodes = new Set();
|
||||||
|
let allUsedNodes = {};
|
||||||
|
|
||||||
|
for(let k in app.graph._nodes) {
|
||||||
|
let node = app.graph._nodes[k];
|
||||||
|
|
||||||
|
if(node.type.startsWith('workflow>')) {
|
||||||
|
usedGroupNodes.add(node.type.slice(9));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
allUsedNodes[node.type] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let k of usedGroupNodes) {
|
||||||
|
let subnodes = app.graph.extra.groupNodes[k]?.nodes;
|
||||||
|
|
||||||
|
if(subnodes) {
|
||||||
|
for(let k2 in subnodes) {
|
||||||
|
let node = subnodes[k2];
|
||||||
|
allUsedNodes[node.type] = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allUsedNodes;
|
||||||
|
}
|
||||||
|
|
||||||
async getMissingNodes() {
|
async getMissingNodes() {
|
||||||
|
let unresolved_missing_nodes = new Set();
|
||||||
|
let hashMap = {};
|
||||||
|
let allUsedNodes = this.getNodesInWorkflow();
|
||||||
|
|
||||||
|
const registered_nodes = new Set();
|
||||||
|
for (let i in LiteGraph.registered_node_types) {
|
||||||
|
registered_nodes.add(LiteGraph.registered_node_types[i].type);
|
||||||
|
}
|
||||||
|
|
||||||
|
let unresolved_aux_ids = {};
|
||||||
|
let outdated_comfyui = false;
|
||||||
|
let unresolved_cnr_list = [];
|
||||||
|
|
||||||
|
for(let k in allUsedNodes) {
|
||||||
|
let node = allUsedNodes[k];
|
||||||
|
|
||||||
|
if(!registered_nodes.has(node.type)) {
|
||||||
|
// missing node
|
||||||
|
if(node.properties.cnr_id) {
|
||||||
|
if(node.properties.cnr_id == 'comfy-core') {
|
||||||
|
outdated_comfyui = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
let item = this.custom_nodes[node.properties.cnr_id];
|
||||||
|
if(item) {
|
||||||
|
hashMap[item.hash] = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log(`CM: cannot find '${node.properties.cnr_id}' from cnr list.`);
|
||||||
|
unresolved_aux_ids[node.properties.cnr_id] = node.type;
|
||||||
|
unresolved_cnr_list.push(node.properties.cnr_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(node.properties.aux_id) {
|
||||||
|
unresolved_aux_ids[node.properties.aux_id] = node.type;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unresolved_missing_nodes.add(node.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(unresolved_cnr_list.length > 0) {
|
||||||
|
let error_msg = "Failed to find the following ComfyRegistry list.\nThe cache may be outdated, or the nodes may have been removed from ComfyRegistry.<HR>";
|
||||||
|
for(let i in unresolved_cnr_list) {
|
||||||
|
error_msg += '<li>'+unresolved_cnr_list[i]+'</li>';
|
||||||
|
}
|
||||||
|
|
||||||
|
show_message(error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(outdated_comfyui) {
|
||||||
|
customAlert('ComfyUI is outdated, so some built-in nodes cannot be used.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Object.keys(unresolved_aux_ids).length > 0) {
|
||||||
|
// building aux_id to nodepack map
|
||||||
|
let aux_id_to_pack = {};
|
||||||
|
for(let k in this.custom_nodes) {
|
||||||
|
let nodepack = this.custom_nodes[k];
|
||||||
|
let aux_id;
|
||||||
|
if(nodepack.repository?.startsWith('https://github.com')) {
|
||||||
|
aux_id = nodepack.repository.split('/').slice(-2).join('/');
|
||||||
|
aux_id_to_pack[aux_id] = nodepack;
|
||||||
|
}
|
||||||
|
else if(nodepack.repository) {
|
||||||
|
aux_id = nodepack.repository.split('/').slice(-1);
|
||||||
|
aux_id_to_pack[aux_id] = nodepack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolving aux_id
|
||||||
|
for(let k in unresolved_aux_ids) {
|
||||||
|
let nodepack = aux_id_to_pack[k];
|
||||||
|
if(nodepack) {
|
||||||
|
hashMap[nodepack.hash] = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unresolved_missing_nodes.add(unresolved_aux_ids[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(unresolved_missing_nodes.size > 0) {
|
||||||
|
await this.getMissingNodesLegacy(hashMap, unresolved_missing_nodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMissingNodesLegacy(hashMap, missing_nodes) {
|
||||||
const mode = manager_instance.datasrc_combo.value;
|
const mode = manager_instance.datasrc_combo.value;
|
||||||
this.showStatus(`Loading missing nodes (${mode}) ...`);
|
this.showStatus(`Loading missing nodes (${mode}) ...`);
|
||||||
const res = await fetchData(`/customnode/getmappings?mode=${mode}`);
|
const res = await fetchData(`/customnode/getmappings?mode=${mode}`);
|
||||||
@@ -1525,50 +1703,29 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const registered_nodes = new Set();
|
let unresolved_missing_nodes = new Set();
|
||||||
for (let i in LiteGraph.registered_node_types) {
|
for (let node_type of missing_nodes) {
|
||||||
registered_nodes.add(LiteGraph.registered_node_types[i].type);
|
const packs = name_to_packs[node_type.trim()];
|
||||||
}
|
if(packs)
|
||||||
|
packs.forEach(url => {
|
||||||
const missing_nodes = new Set();
|
unresolved_missing_nodes.add(url);
|
||||||
const workflow = app.graph.serialize();
|
});
|
||||||
const group_nodes = workflow.extra && workflow.extra.groupNodes ? workflow.extra.groupNodes : [];
|
else {
|
||||||
let nodes = workflow.nodes;
|
for(let j in regex_to_pack) {
|
||||||
|
if(regex_to_pack[j].regex.test(node_type)) {
|
||||||
for (let i in group_nodes) {
|
unresolved_missing_nodes.add(regex_to_pack[j].url);
|
||||||
let group_node = group_nodes[i];
|
|
||||||
nodes = nodes.concat(group_node.nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i in nodes) {
|
|
||||||
const node_type = nodes[i].type;
|
|
||||||
if(node_type.startsWith('workflow/') || node_type.startsWith('workflow>'))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!registered_nodes.has(node_type)) {
|
|
||||||
const packs = name_to_packs[node_type.trim()];
|
|
||||||
if(packs)
|
|
||||||
packs.forEach(url => {
|
|
||||||
missing_nodes.add(url);
|
|
||||||
});
|
|
||||||
else {
|
|
||||||
for(let j in regex_to_pack) {
|
|
||||||
if(regex_to_pack[j].regex.test(node_type)) {
|
|
||||||
missing_nodes.add(regex_to_pack[j].url);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const hashMap = {};
|
|
||||||
for(let k in this.custom_nodes) {
|
for(let k in this.custom_nodes) {
|
||||||
let item = this.custom_nodes[k];
|
let item = this.custom_nodes[k];
|
||||||
|
|
||||||
if(missing_nodes.has(item.id)) {
|
if(unresolved_missing_nodes.has(item.id)) {
|
||||||
hashMap[item.hash] = true;
|
hashMap[item.hash] = true;
|
||||||
}
|
}
|
||||||
else if (item.files?.some(file => missing_nodes.has(file))) {
|
else if (item.files?.some(file => unresolved_missing_nodes.has(file))) {
|
||||||
hashMap[item.hash] = true;
|
hashMap[item.hash] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1587,6 +1744,41 @@ export class CustomNodesManager {
|
|||||||
return hashMap;
|
return hashMap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getNodepackInWorkflow() {
|
||||||
|
let allUsedNodes = this.getNodesInWorkflow();
|
||||||
|
|
||||||
|
// building aux_id to nodepack map
|
||||||
|
let aux_id_to_pack = {};
|
||||||
|
for(let k in this.custom_nodes) {
|
||||||
|
let nodepack = this.custom_nodes[k];
|
||||||
|
let aux_id;
|
||||||
|
if(nodepack.repository?.startsWith('https://github.com')) {
|
||||||
|
aux_id = nodepack.repository.split('/').slice(-2).join('/');
|
||||||
|
aux_id_to_pack[aux_id] = nodepack;
|
||||||
|
}
|
||||||
|
else if(nodepack.repository) {
|
||||||
|
aux_id = nodepack.repository.split('/').slice(-1);
|
||||||
|
aux_id_to_pack[aux_id] = nodepack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const hashMap = {};
|
||||||
|
for(let k in allUsedNodes) {
|
||||||
|
var item;
|
||||||
|
if(allUsedNodes[k].properties.cnr_id) {
|
||||||
|
item = this.custom_nodes[allUsedNodes[k].properties.cnr_id];
|
||||||
|
}
|
||||||
|
else if(allUsedNodes[k].properties.aux_id) {
|
||||||
|
item = aux_id_to_pack[allUsedNodes[k].properties.aux_id];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(item)
|
||||||
|
hashMap[item.hash] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashMap;
|
||||||
|
}
|
||||||
|
|
||||||
async getAlternatives() {
|
async getAlternatives() {
|
||||||
const mode = manager_instance.datasrc_combo.value;
|
const mode = manager_instance.datasrc_combo.value;
|
||||||
this.showStatus(`Loading alternatives (${mode}) ...`);
|
this.showStatus(`Loading alternatives (${mode}) ...`);
|
||||||
@@ -1622,6 +1814,8 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async loadData(show_mode = ShowMode.NORMAL) {
|
async loadData(show_mode = ShowMode.NORMAL) {
|
||||||
|
const isElectron = 'electronAPI' in window;
|
||||||
|
|
||||||
this.show_mode = show_mode;
|
this.show_mode = show_mode;
|
||||||
console.log("Show mode:", show_mode);
|
console.log("Show mode:", show_mode);
|
||||||
|
|
||||||
@@ -1633,14 +1827,24 @@ export class CustomNodesManager {
|
|||||||
this.showStatus(`Loading custom nodes (${mode}) ...`);
|
this.showStatus(`Loading custom nodes (${mode}) ...`);
|
||||||
|
|
||||||
const skip_update = this.show_mode === ShowMode.UPDATE ? "" : "&skip_update=true";
|
const skip_update = this.show_mode === ShowMode.UPDATE ? "" : "&skip_update=true";
|
||||||
|
|
||||||
|
if(this.show_mode === ShowMode.UPDATE) {
|
||||||
|
infoToast('Fetching updated information. This may take some time if many custom nodes are installed.');
|
||||||
|
}
|
||||||
|
|
||||||
const res = await fetchData(`/customnode/getlist?mode=${mode}${skip_update}`);
|
const res = await fetchData(`/customnode/getlist?mode=${mode}${skip_update}`);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
this.showError("Failed to get custom node list.");
|
this.showError("Failed to get custom node list.");
|
||||||
this.hideLoading();
|
this.hideLoading();
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { channel, node_packs } = res.data;
|
const { channel, node_packs } = res.data;
|
||||||
|
|
||||||
|
if(isElectron) {
|
||||||
|
delete node_packs['comfyui-manager'];
|
||||||
|
}
|
||||||
|
|
||||||
this.channel = channel;
|
this.channel = channel;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.custom_nodes = node_packs;
|
this.custom_nodes = node_packs;
|
||||||
@@ -1675,9 +1879,14 @@ export class CustomNodesManager {
|
|||||||
hashMap = await this.getAlternatives();
|
hashMap = await this.getAlternatives();
|
||||||
} else if(this.show_mode == ShowMode.FAVORITES) {
|
} else if(this.show_mode == ShowMode.FAVORITES) {
|
||||||
hashMap = await this.getFavorites();
|
hashMap = await this.getFavorites();
|
||||||
|
} else if(this.show_mode == ShowMode.IN_WORKFLOW) {
|
||||||
|
hashMap = await this.getNodepackInWorkflow();
|
||||||
}
|
}
|
||||||
filterItem.hashMap = hashMap;
|
filterItem.hashMap = hashMap;
|
||||||
filterItem.hasData = true;
|
|
||||||
|
if(this.show_mode != ShowMode.IN_WORKFLOW) {
|
||||||
|
filterItem.hasData = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(let k in node_packs) {
|
for(let k in node_packs) {
|
||||||
@@ -1729,7 +1938,6 @@ export class CustomNodesManager {
|
|||||||
case "disabled":
|
case "disabled":
|
||||||
filterTypes.add("installed");
|
filterTypes.add("installed");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "not-installed":
|
case "not-installed":
|
||||||
filterTypes.add("not-installed");
|
filterTypes.add("not-installed");
|
||||||
break;
|
break;
|
||||||
@@ -1808,9 +2016,9 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDisabled(disabled) {
|
setDisabled(disabled) {
|
||||||
|
|
||||||
const $close = this.element.querySelector(".cn-manager-close");
|
const $close = this.element.querySelector(".cn-manager-close");
|
||||||
const $restart = this.element.querySelector(".cn-manager-restart");
|
const $restart = this.element.querySelector(".cn-manager-restart");
|
||||||
|
const $stop = this.element.querySelector(".cn-manager-stop");
|
||||||
|
|
||||||
const list = [
|
const list = [
|
||||||
".cn-manager-header input",
|
".cn-manager-header input",
|
||||||
@@ -1822,7 +2030,7 @@ export class CustomNodesManager {
|
|||||||
})
|
})
|
||||||
.flat()
|
.flat()
|
||||||
.filter(it => {
|
.filter(it => {
|
||||||
return it !== $close && it !== $restart;
|
return it !== $close && it !== $restart && it !== $stop;
|
||||||
});
|
});
|
||||||
|
|
||||||
list.forEach($elem => {
|
list.forEach($elem => {
|
||||||
@@ -1841,6 +2049,15 @@ export class CustomNodesManager {
|
|||||||
|
|
||||||
showRestart() {
|
showRestart() {
|
||||||
this.element.querySelector(".cn-manager-restart").style.display = "block";
|
this.element.querySelector(".cn-manager-restart").style.display = "block";
|
||||||
|
setNeedRestart(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
showStop() {
|
||||||
|
this.element.querySelector(".cn-manager-stop").style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
hideStop() {
|
||||||
|
this.element.querySelector(".cn-manager-stop").style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
setFilter(filterValue) {
|
setFilter(filterValue) {
|
||||||
@@ -1870,4 +2087,8 @@ export class CustomNodesManager {
|
|||||||
close() {
|
close() {
|
||||||
this.element.style.display = "none";
|
this.element.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get isVisible() {
|
||||||
|
return this.element?.style?.display !== "none";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
|
import { app } from "../../scripts/app.js";
|
||||||
import { $el } from "../../scripts/ui.js";
|
import { $el } from "../../scripts/ui.js";
|
||||||
import {
|
import {
|
||||||
manager_instance, rebootAPI,
|
manager_instance, rebootAPI,
|
||||||
fetchData, md5, icons
|
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
||||||
|
storeColumnWidth, restoreColumnWidth
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
|
import { api } from "../../scripts/api.js";
|
||||||
|
|
||||||
// https://cenfun.github.io/turbogrid/api.html
|
// https://cenfun.github.io/turbogrid/api.html
|
||||||
import TG from "./turbogrid.esm.js";
|
import TG from "./turbogrid.esm.js";
|
||||||
|
|
||||||
|
const gridId = "model";
|
||||||
|
|
||||||
const pageCss = `
|
const pageCss = `
|
||||||
.cmm-manager {
|
.cmm-manager {
|
||||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
@@ -46,6 +51,18 @@ const pageCss = `
|
|||||||
background-color: var(--comfy-input-bg);
|
background-color: var(--comfy-input-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cmm-manager .cmm-manager-refresh {
|
||||||
|
display: none;
|
||||||
|
background-color: #000080;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cmm-manager .cmm-manager-stop {
|
||||||
|
display: none;
|
||||||
|
background-color: #500000;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.cmm-manager-header {
|
.cmm-manager-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
@@ -235,7 +252,14 @@ const pageHtml = `
|
|||||||
<div class="cmm-manager-selection"></div>
|
<div class="cmm-manager-selection"></div>
|
||||||
<div class="cmm-manager-message"></div>
|
<div class="cmm-manager-message"></div>
|
||||||
<div class="cmm-manager-footer">
|
<div class="cmm-manager-footer">
|
||||||
<button class="cmm-manager-back">Back</button>
|
<button class="cmm-manager-back">
|
||||||
|
<svg class="arrow-icon" width="14" height="14" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2 8H18M2 8L8 2M2 8L8 14" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
Back
|
||||||
|
</button>
|
||||||
|
<button class="cmm-manager-refresh">Refresh</button>
|
||||||
|
<button class="cmm-manager-stop">Stop</button>
|
||||||
<div class="cmm-flex-auto"></div>
|
<div class="cmm-flex-auto"></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@@ -254,6 +278,8 @@ export class ModelManager {
|
|||||||
this.keywords = '';
|
this.keywords = '';
|
||||||
|
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
|
api.addEventListener("cm-queue-status", this.onQueueStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -365,12 +391,25 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
".cmm-manager-refresh": {
|
||||||
|
click: () => {
|
||||||
|
app.refreshComboInNodes();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
".cmm-manager-stop": {
|
||||||
|
click: () => {
|
||||||
|
api.fetchApi('/manager/queue/reset');
|
||||||
|
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
".cmm-manager-back": {
|
".cmm-manager-back": {
|
||||||
click: (e) => {
|
click: (e) => {
|
||||||
this.close()
|
this.close()
|
||||||
manager_instance.show();
|
manager_instance.show();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
Object.keys(eventsMap).forEach(selector => {
|
Object.keys(eventsMap).forEach(selector => {
|
||||||
const target = this.element.querySelector(selector);
|
const target = this.element.querySelector(selector);
|
||||||
@@ -402,6 +441,10 @@ export class ModelManager {
|
|||||||
this.renderSelected();
|
this.renderSelected();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
grid.bind("onColumnWidthChanged", (e, columnItem) => {
|
||||||
|
storeColumnWidth(gridId, columnItem)
|
||||||
|
});
|
||||||
|
|
||||||
grid.bind('onClick', (e, d) => {
|
grid.bind('onClick', (e, d) => {
|
||||||
const { rowItem } = d;
|
const { rowItem } = d;
|
||||||
const target = d.e.target;
|
const target = d.e.target;
|
||||||
@@ -553,6 +596,8 @@ export class ModelManager {
|
|||||||
width: 200
|
width: 200
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
restoreColumnWidth(gridId, columns);
|
||||||
|
|
||||||
this.grid.setData({
|
this.grid.setData({
|
||||||
options,
|
options,
|
||||||
rows,
|
rows,
|
||||||
@@ -595,17 +640,27 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async installModels(list, btn) {
|
async installModels(list, btn) {
|
||||||
|
let stats = await api.fetchApi('/manager/queue/status');
|
||||||
|
|
||||||
|
stats = await stats.json();
|
||||||
|
if(stats.is_processing) {
|
||||||
|
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
btn.classList.add("cmm-btn-loading");
|
btn.classList.add("cmm-btn-loading");
|
||||||
this.showLoading();
|
|
||||||
this.showError("");
|
this.showError("");
|
||||||
|
|
||||||
let needRestart = false;
|
let needRefresh = false;
|
||||||
let errorMsg = "";
|
let errorMsg = "";
|
||||||
|
|
||||||
for (const item of list) {
|
await api.fetchApi('/manager/queue/reset');
|
||||||
|
|
||||||
|
let target_items = [];
|
||||||
|
|
||||||
|
for (const item of list) {
|
||||||
this.grid.scrollRowIntoView(item);
|
this.grid.scrollRowIntoView(item);
|
||||||
|
target_items.push(item);
|
||||||
|
|
||||||
if (!this.focusInstall(item)) {
|
if (!this.focusInstall(item)) {
|
||||||
this.grid.onNextUpdated(() => {
|
this.grid.onNextUpdated(() => {
|
||||||
@@ -616,48 +671,112 @@ export class ModelManager {
|
|||||||
this.showStatus(`Install ${item.name} ...`);
|
this.showStatus(`Install ${item.name} ...`);
|
||||||
|
|
||||||
const data = item.originalData;
|
const data = item.originalData;
|
||||||
const res = await fetchData('/model/install', {
|
data.ui_id = item.hash;
|
||||||
|
|
||||||
|
const res = await api.fetchApi(`/manager/queue/install_model`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (res.status != 200) {
|
||||||
|
errorMsg = `'${item.name}': `;
|
||||||
|
|
||||||
if (res.error) {
|
if(res.status == 403) {
|
||||||
errorMsg = `Install failed: ${item.name} ${res.error.message}`;
|
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
||||||
break;;
|
} else {
|
||||||
|
errorMsg += await res.text() + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
needRestart = true;
|
this.install_context = {btn: btn, targets: target_items};
|
||||||
|
|
||||||
this.grid.setRowSelected(item, false);
|
if(errorMsg) {
|
||||||
|
this.showError(errorMsg);
|
||||||
|
show_message("[Installation Errors]\n"+errorMsg);
|
||||||
|
|
||||||
|
// reset
|
||||||
|
for(let k in target_items) {
|
||||||
|
const item = target_items[k];
|
||||||
|
this.grid.updateCell(item, "installed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await api.fetchApi('/manager/queue/start');
|
||||||
|
this.showStop();
|
||||||
|
showTerminal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async onQueueStatus(event) {
|
||||||
|
let self = ModelManager.instance;
|
||||||
|
|
||||||
|
if(event.detail.status == 'in_progress' && event.detail.ui_target == 'model_manager') {
|
||||||
|
const hash = event.detail.target;
|
||||||
|
|
||||||
|
const item = self.grid.getRowItemBy("hash", hash);
|
||||||
|
|
||||||
item.refresh = true;
|
item.refresh = true;
|
||||||
|
self.grid.setRowSelected(item, false);
|
||||||
item.selectable = false;
|
item.selectable = false;
|
||||||
this.grid.updateCell(item, "installed");
|
// self.grid.updateCell(item, "tg-column-select");
|
||||||
this.grid.updateCell(item, "tg-column-select");
|
self.grid.updateRow(item);
|
||||||
|
}
|
||||||
|
else if(event.detail.status == 'done') {
|
||||||
|
self.hideStop();
|
||||||
|
self.onQueueCompleted(event.detail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.showStatus(`Install ${item.name} successfully`);
|
async onQueueCompleted(info) {
|
||||||
|
let result = info.model_result;
|
||||||
|
|
||||||
|
if(result.length == 0) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.hideLoading();
|
let self = ModelManager.instance;
|
||||||
|
|
||||||
|
if(!self.install_context) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let btn = self.install_context.btn;
|
||||||
|
|
||||||
|
self.hideLoading();
|
||||||
btn.classList.remove("cmm-btn-loading");
|
btn.classList.remove("cmm-btn-loading");
|
||||||
|
|
||||||
|
let errorMsg = "";
|
||||||
|
|
||||||
|
for(let hash in result){
|
||||||
|
let v = result[hash];
|
||||||
|
|
||||||
|
if(v != 'success')
|
||||||
|
errorMsg += v + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
for(let k in self.install_context.targets) {
|
||||||
|
let item = self.install_context.targets[k];
|
||||||
|
self.grid.updateCell(item, "installed");
|
||||||
|
}
|
||||||
|
|
||||||
if (errorMsg) {
|
if (errorMsg) {
|
||||||
this.showError(errorMsg);
|
self.showError(errorMsg);
|
||||||
|
show_message("Installation Error:\n"+errorMsg);
|
||||||
} else {
|
} else {
|
||||||
this.showStatus(`Install ${list.length} models successfully`);
|
self.showStatus(`Install ${result.length} models successfully`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needRestart) {
|
self.showRefresh();
|
||||||
this.showMessage(`To apply the installed model, please click the 'Refresh' button on the main menu.`, "red")
|
self.showMessage(`To apply the installed model, please click the 'Refresh' button.`, "red")
|
||||||
}
|
|
||||||
|
|
||||||
|
infoToast('Tasks done', `[ComfyUI-Manager] All model downloading tasks in the queue have been completed.\n${info.done_count}/${info.total_count}`);
|
||||||
|
self.install_context = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
getModelList(models) {
|
getModelList(models) {
|
||||||
|
|
||||||
const typeMap = new Map();
|
const typeMap = new Map();
|
||||||
const baseMap = new Map();
|
const baseMap = new Map();
|
||||||
|
|
||||||
@@ -826,7 +945,7 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showLoading() {
|
showLoading() {
|
||||||
this.setDisabled(true);
|
// this.setDisabled(true);
|
||||||
if (this.grid) {
|
if (this.grid) {
|
||||||
this.grid.showLoading();
|
this.grid.showLoading();
|
||||||
this.grid.showMask({
|
this.grid.showMask({
|
||||||
@@ -836,7 +955,7 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hideLoading() {
|
hideLoading() {
|
||||||
this.setDisabled(false);
|
// this.setDisabled(false);
|
||||||
if (this.grid) {
|
if (this.grid) {
|
||||||
this.grid.hideLoading();
|
this.grid.hideLoading();
|
||||||
this.grid.hideMask();
|
this.grid.hideMask();
|
||||||
@@ -844,8 +963,9 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setDisabled(disabled) {
|
setDisabled(disabled) {
|
||||||
|
|
||||||
const $close = this.element.querySelector(".cmm-manager-close");
|
const $close = this.element.querySelector(".cmm-manager-close");
|
||||||
|
const $refresh = this.element.querySelector(".cmm-manager-refresh");
|
||||||
|
const $stop = this.element.querySelector(".cmm-manager-stop");
|
||||||
|
|
||||||
const list = [
|
const list = [
|
||||||
".cmm-manager-header input",
|
".cmm-manager-header input",
|
||||||
@@ -857,7 +977,7 @@ export class ModelManager {
|
|||||||
})
|
})
|
||||||
.flat()
|
.flat()
|
||||||
.filter(it => {
|
.filter(it => {
|
||||||
return it !== $close;
|
return it !== $close && it !== $refresh && it !== $stop;
|
||||||
});
|
});
|
||||||
|
|
||||||
list.forEach($elem => {
|
list.forEach($elem => {
|
||||||
@@ -874,6 +994,18 @@ export class ModelManager {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
showRefresh() {
|
||||||
|
this.element.querySelector(".cmm-manager-refresh").style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
showStop() {
|
||||||
|
this.element.querySelector(".cmm-manager-stop").style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
hideStop() {
|
||||||
|
this.element.querySelector(".cmm-manager-stop").style.display = "none";
|
||||||
|
}
|
||||||
|
|
||||||
setKeywords(keywords = "") {
|
setKeywords(keywords = "") {
|
||||||
this.keywords = keywords;
|
this.keywords = keywords;
|
||||||
this.element.querySelector(".cmm-manager-keywords").value = keywords;
|
this.element.querySelector(".cmm-manager-keywords").value = keywords;
|
||||||
|
|||||||
@@ -3,12 +3,21 @@
|
|||||||
* - custom node pack version to all custom nodes used in the workflow
|
* - custom node pack version to all custom nodes used in the workflow
|
||||||
*
|
*
|
||||||
* Example metadata:
|
* Example metadata:
|
||||||
"extra": {
|
* "nodes": {
|
||||||
"node_versions": {
|
* "1": {
|
||||||
"comfy-core": "v0.3.8-4-g0b2eb7f",
|
* type: "CheckpointLoaderSimple",
|
||||||
"comfyui-easy-use": "1.2.5"
|
* ...
|
||||||
}
|
* properties: {
|
||||||
},
|
* cnr_id: "comfy-core",
|
||||||
|
* version: "0.3.8",
|
||||||
|
* },
|
||||||
|
* },
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @typedef {Object} NodeInfo
|
||||||
|
* @property {string} ver - Version (git hash or semantic version)
|
||||||
|
* @property {string} cnr_id - ComfyRegistry node ID
|
||||||
|
* @property {boolean} enabled - Whether the node is enabled
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { app } from "../../scripts/app.js";
|
import { app } from "../../scripts/app.js";
|
||||||
@@ -23,7 +32,7 @@ class WorkflowMetadataExtension {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the installed nodes info
|
* Get the installed nodes info
|
||||||
* @returns {Promise<Record<string, {ver: string, cnr_id: string, enabled: boolean}>>} The mapping from node name to its info.
|
* @returns {Promise<Record<string, NodeInfo>>} The mapping from node name to its info.
|
||||||
* ver can either be a git commit hash or a semantic version such as "1.0.0"
|
* ver can either be a git commit hash or a semantic version such as "1.0.0"
|
||||||
* cnr_id is the id of the node in the ComfyRegistry
|
* cnr_id is the id of the node in the ComfyRegistry
|
||||||
* enabled is true if the node is enabled, false if it is disabled
|
* enabled is true if the node is enabled, false if it is disabled
|
||||||
@@ -33,61 +42,42 @@ class WorkflowMetadataExtension {
|
|||||||
return await res.json();
|
return await res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the node versions for the given graph
|
|
||||||
* @param {LGraph} graph The graph to get the node versions for
|
|
||||||
* @returns {Promise<Record<string, string>>} The mapping from node name to version
|
|
||||||
*/
|
|
||||||
getGraphNodeVersions(graph) {
|
|
||||||
const nodeVersions = {};
|
|
||||||
for (const node of graph.nodes) {
|
|
||||||
const nodeData = node.constructor.nodeData;
|
|
||||||
// Frontend only nodes don't have nodeData
|
|
||||||
if (!nodeData) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const modules = nodeData.python_module.split(".");
|
|
||||||
|
|
||||||
if (modules[0] === "custom_nodes") {
|
|
||||||
const nodePackageName = modules[1];
|
|
||||||
const nodeInfo =
|
|
||||||
this.installedNodes[nodePackageName] ??
|
|
||||||
this.installedNodes[nodePackageName.toLowerCase()];
|
|
||||||
if (nodeInfo) {
|
|
||||||
nodeVersions[nodePackageName] = nodeInfo.ver;
|
|
||||||
}
|
|
||||||
} else if (["nodes", "comfy_extras"].includes(modules[0])) {
|
|
||||||
nodeVersions["comfy-core"] = this.comfyCoreVersion;
|
|
||||||
} else {
|
|
||||||
console.warn(`Unknown node source: ${nodeData.python_module}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nodeVersions;
|
|
||||||
}
|
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
const extension = this;
|
|
||||||
this.installedNodes = await this.getInstalledNodes();
|
this.installedNodes = await this.getInstalledNodes();
|
||||||
this.comfyCoreVersion = (await api.getSystemStats()).system.comfyui_version;
|
this.comfyCoreVersion = (await api.getSystemStats()).system.comfyui_version;
|
||||||
|
}
|
||||||
|
|
||||||
// Attach metadata when app.graphToPrompt is called.
|
/**
|
||||||
const originalSerialize = LGraph.prototype.serialize;
|
* Called when any node is created
|
||||||
LGraph.prototype.serialize = function () {
|
* @param {LGraphNode} node The newly created node
|
||||||
const workflow = originalSerialize.apply(this, arguments);
|
*/
|
||||||
|
nodeCreated(node) {
|
||||||
|
try {
|
||||||
|
// nodeData doesn't exist if node is missing or node is frontend only node
|
||||||
|
if (!node?.constructor?.nodeData?.python_module) return;
|
||||||
|
|
||||||
// Add metadata to the workflow
|
const nodeProperties = (node.properties ??= {});
|
||||||
if (!workflow.extra) {
|
const modules = node.constructor.nodeData.python_module.split(".");
|
||||||
workflow.extra = {};
|
const moduleType = modules[0];
|
||||||
|
|
||||||
|
if (moduleType === "custom_nodes") {
|
||||||
|
const nodePackageName = modules[1];
|
||||||
|
const { cnr_id, aux_id, ver } =
|
||||||
|
this.installedNodes[nodePackageName] ??
|
||||||
|
this.installedNodes[nodePackageName.toLowerCase()] ??
|
||||||
|
{};
|
||||||
|
|
||||||
|
if (cnr_id === "comfy-core") return; // don't allow hijacking comfy-core name
|
||||||
|
if (cnr_id) nodeProperties.cnr_id = cnr_id;
|
||||||
|
else nodeProperties.aux_id = aux_id;
|
||||||
|
if (ver) nodeProperties.ver = ver;
|
||||||
|
} else if (["nodes", "comfy_extras"].includes(moduleType)) {
|
||||||
|
nodeProperties.cnr_id = "comfy-core";
|
||||||
|
nodeProperties.ver = this.comfyCoreVersion;
|
||||||
}
|
}
|
||||||
const graph = this;
|
} catch (e) {
|
||||||
try {
|
console.error(e);
|
||||||
workflow.extra["node_versions"] = extension.getGraphNodeVersions(graph);
|
}
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return workflow;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
120
model-list.json
120
model-list.json
@@ -209,28 +209,6 @@
|
|||||||
"url": "https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler/resolve/main/x4-upscaler-ema.safetensors",
|
"url": "https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler/resolve/main/x4-upscaler-ema.safetensors",
|
||||||
"size": "3.53GB"
|
"size": "3.53GB"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Inswapper-fp16 (face swap)",
|
|
||||||
"type": "insightface",
|
|
||||||
"base": "inswapper",
|
|
||||||
"save_path": "insightface",
|
|
||||||
"description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)",
|
|
||||||
"reference": "https://github.com/facefusion/facefusion-assets",
|
|
||||||
"filename": "inswapper_128_fp16.onnx",
|
|
||||||
"url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128_fp16.onnx",
|
|
||||||
"size": "277.7MB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Inswapper (face swap)",
|
|
||||||
"type": "insightface",
|
|
||||||
"base": "inswapper",
|
|
||||||
"save_path": "insightface",
|
|
||||||
"description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)",
|
|
||||||
"reference": "https://github.com/facefusion/facefusion-assets",
|
|
||||||
"filename": "inswapper_128.onnx",
|
|
||||||
"url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx",
|
|
||||||
"size": "555.3MB"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Deepbump",
|
"name": "Deepbump",
|
||||||
"type": "deepbump",
|
"type": "deepbump",
|
||||||
@@ -1090,18 +1068,28 @@
|
|||||||
"size": "19.1GB"
|
"size": "19.1GB"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "comfyanonymous/clip_l",
|
"name": "Comfy-Org/clip_l",
|
||||||
"type": "clip",
|
"type": "clip",
|
||||||
"base": "clip",
|
"base": "clip",
|
||||||
"save_path": "default",
|
"save_path": "default",
|
||||||
"description": "clip_l model",
|
"description": "clip_l model (for SD1.x, SD2.x, SDXL, SD3.5, FLUX.1, HunyuanVideo, ...) ",
|
||||||
"reference": "https://huggingface.co/comfyanonymous/flux_text_encoders/tree/main",
|
"reference": "https://huggingface.co/Comfy-Org/stable-diffusion-3.5-fp8",
|
||||||
"filename": "clip_l.safetensors",
|
"filename": "clip_l.safetensors",
|
||||||
"url": "https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/clip_l.safetensors",
|
"url": "https://huggingface.co/Comfy-Org/stable-diffusion-3.5-fp8/resolve/main/text_encoders/clip_l.safetensors",
|
||||||
"size": "246MB"
|
"size": "246MB"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/clip_g",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "clip",
|
||||||
|
"save_path": "default",
|
||||||
|
"description": "clip_g model (for SDXL, SD3.5)",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/stable-diffusion-3.5-fp8",
|
||||||
|
"filename": "clip_g.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/stable-diffusion-3.5-fp8/resolve/main/text_encoders/clip_g.safetensors",
|
||||||
|
"size": "1.39GB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "v1-5-pruned-emaonly.ckpt",
|
"name": "v1-5-pruned-emaonly.ckpt",
|
||||||
@@ -3972,6 +3960,17 @@
|
|||||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/vae/hunyuan_video_vae_bf16.safetensors",
|
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/vae/hunyuan_video_vae_bf16.safetensors",
|
||||||
"size": "493MB"
|
"size": "493MB"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Hunyuan Video",
|
||||||
|
"save_path": "diffusion_models/hunyuan_video",
|
||||||
|
"description": "Huyuan Video Image2Video diffusion model. repackaged version.",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
||||||
|
"filename": "hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/diffusion_models/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||||
|
"size": "25.6GB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Comfy-Org/llava_llama3_fp8_scaled.safetensors",
|
"name": "Comfy-Org/llava_llama3_fp8_scaled.safetensors",
|
||||||
@@ -3995,6 +3994,17 @@
|
|||||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/text_encoders/llava_llama3_fp16.safetensors",
|
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/text_encoders/llava_llama3_fp16.safetensors",
|
||||||
"size": "16.1GB"
|
"size": "16.1GB"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/llava_llama3_vision.safetensors",
|
||||||
|
"type": "clip_vision",
|
||||||
|
"base": "LLaVA-Llama-3",
|
||||||
|
"save_path": "text_encoders",
|
||||||
|
"description": "llava_llama3_vision clip vison model. This is required for using Hunyuan Video Image2Video.",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
||||||
|
"filename": "llava_llama3_vision.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/clip_vision/llava_llama3_vision.safetensors",
|
||||||
|
"size": "649MB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "FLUX.1 [Schnell] Diffusion model",
|
"name": "FLUX.1 [Schnell] Diffusion model",
|
||||||
@@ -4559,6 +4569,17 @@
|
|||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.1.safetensors",
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.1.safetensors",
|
||||||
"size": "5.72GB"
|
"size": "5.72GB"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "LTX-Video 2B v0.9.5 Checkpoint",
|
||||||
|
"type": "checkpoint",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "checkpoints/LTXV",
|
||||||
|
"description": "LTX-Video is the first DiT-based video generation model capable of generating high-quality videos in real-time. It produces 24 FPS videos at a 768x512 resolution faster than they can be watched. Trained on a large-scale dataset of diverse videos, the model generates high-resolution videos with realistic and varied content.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||||
|
"filename": "ltx-video-2b-v0.9.5.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.5.safetensors",
|
||||||
|
"size": "6.34GB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "XLabs-AI/flux-canny-controlnet-v3.safetensors",
|
"name": "XLabs-AI/flux-canny-controlnet-v3.safetensors",
|
||||||
@@ -4684,6 +4705,51 @@
|
|||||||
"filename": "customnet_inpaint_v1.pt",
|
"filename": "customnet_inpaint_v1.pt",
|
||||||
"url": "https://huggingface.co/TencentARC/CustomNet/resolve/main/customnet_inpaint_v1.pt",
|
"url": "https://huggingface.co/TencentARC/CustomNet/resolve/main/customnet_inpaint_v1.pt",
|
||||||
"size": "5.71GB"
|
"size": "5.71GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "deepseek-ai/Janus-Pro-1B",
|
||||||
|
"type": "Janus-Pro",
|
||||||
|
"base": "Janus-Pro",
|
||||||
|
"save_path": "Janus-Pro",
|
||||||
|
"description": "[SNAPSHOT] Janus-Pro-1B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
||||||
|
"reference": "https://huggingface.co/deepseek-ai/Janus-Pro-1B",
|
||||||
|
"filename": "<huggingface>",
|
||||||
|
"url": "deepseek-ai/Janus-Pro-1B",
|
||||||
|
"size": "7.8GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deepseek-ai/Janus-Pro-7B",
|
||||||
|
"type": "Janus-Pro",
|
||||||
|
"base": "Janus-Pro",
|
||||||
|
"save_path": "Janus-Pro",
|
||||||
|
"description": "[SNAPSHOT] Janus-Pro-7B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
||||||
|
"reference": "https://huggingface.co/deepseek-ai/Janus-Pro-7B",
|
||||||
|
"filename": "<huggingface>",
|
||||||
|
"url": "deepseek-ai/Janus-Pro-7B",
|
||||||
|
"size": "14.85GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kolors/vae/diffusion_pytorch_model.fp16.safetensors",
|
||||||
|
"type": "VAE",
|
||||||
|
"base": "Kolors",
|
||||||
|
"save_path": "vae/kolors",
|
||||||
|
"description": "Kolors VAE",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors",
|
||||||
|
"filename": "diffusion_pytorch_model.fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.fp16.safetensors",
|
||||||
|
"size": "167MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kolors/vae/diffusion_pytorch_model.safetensors",
|
||||||
|
"type": "VAE",
|
||||||
|
"base": "Kolors",
|
||||||
|
"save_path": "vae/kolors",
|
||||||
|
"description": "Kolors VAE",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors",
|
||||||
|
"filename": "diffusion_pytorch_model.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.safetensors",
|
||||||
|
"size": "335MB"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,709 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"author": "longzoho",
|
||||||
|
"title": "ComfyUI-Qdrant-Saver",
|
||||||
|
"reference": "https://github.com/longzoho/ComfyUI-Qdrant-Saver",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/longzoho/ComfyUI-Qdrant-Saver"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: QDrant Saver Node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "nova-florealis",
|
||||||
|
"title": "comfyui-alien",
|
||||||
|
"reference": "https://github.com/nova-florealis/comfyui-alien",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/nova-florealis/comfyui-alien"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Text to Text (LLM), Text Output, Convert to Markdown, List Display (Debug)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "RUFFY-369",
|
||||||
|
"title": "ComfyUI-FeatureBank",
|
||||||
|
"reference": "https://github.com/RUFFY-369/ComfyUI-FeatureBank",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/RUFFY-369/ComfyUI-FeatureBank"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: FeatureBankAttentionProcessor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Pablerdo",
|
||||||
|
"title": "ComfyUI-Sa2VAWrapper",
|
||||||
|
"reference": "https://github.com/Pablerdo/ComfyUI-Sa2VAWrapper",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Pablerdo/ComfyUI-Sa2VAWrapper"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Wrapper for the Sa2VA model"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "S4MUEL-404",
|
||||||
|
"title": "ComfyUI-Folder-Images-Preview [UNSAFE]",
|
||||||
|
"reference": "https://github.com/S4MUEL-404/ComfyUI-Folder-Images-Preview",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/S4MUEL-404/ComfyUI-Folder-Images-Preview"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A ComfyUI nodes , Generate a picture and quickly preview the pictures in the folder and the picture file name\n[w/This custom node has a path traversal vulnerability.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "aria1th",
|
||||||
|
"title": "ComfyUI-camietagger-onnx",
|
||||||
|
"reference": "https://github.com/aria1th/ComfyUI-camietagger-onnx",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/aria1th/ComfyUI-camietagger-onnx"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Camie Tagger"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "zjkhurry",
|
||||||
|
"title": "comfyui_MetalFX [WIP]",
|
||||||
|
"reference": "https://github.com/zjkhurry/comfyui_MetalFX",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/zjkhurry/comfyui_MetalFX"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A custom node for ComfyUI that enables high-quality image and video upscaling using Apple MetalFX technology.\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "IfnotFr",
|
||||||
|
"title": "ComfyUI-Connect [WIP]",
|
||||||
|
"reference": "https://github.com/IfnotFr/ComfyUI-Connect",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/IfnotFr/ComfyUI-Connect"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Transform your ComfyUI into a powerful API, exposing all your saved workflows as ready-to-use HTTP endpoints."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "RoyKillington",
|
||||||
|
"title": "Miscomfy Nodes [WIP]",
|
||||||
|
"reference": "https://github.com/RoyKillington/miscomfy-nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/RoyKillington/miscomfy-nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A repo of custom nodes for ComfyUI, from interacting with certain APIs to whatever other miscellanea I end up making"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "xmarked-ai",
|
||||||
|
"title": "ComfyUI_misc",
|
||||||
|
"reference": "https://github.com/xmarked-ai/ComfyUI_misc",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/xmarked-ai/ComfyUI_misc"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Ace IntegerX, Ace FloatX, Ace Color FixX, White Balance X, Depth Displace X, Empty Latent X, KSampler Combo X, ..."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Elypha",
|
||||||
|
"title": "ComfyUI-Prompt-Helper [WIP]",
|
||||||
|
"reference": "https://github.com/Elypha/ComfyUI-Prompt-Helper",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Elypha/ComfyUI-Prompt-Helper"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Concat conditions and prompts for ComfyUI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "StoryWalker",
|
||||||
|
"title": "comfyui_flux_collection_advanced [WIP]",
|
||||||
|
"reference": "https://github.com/StoryWalker/comfyui_flux_collection_advanced",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/StoryWalker/comfyui_flux_collection_advanced"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This is a collection focused in give a little more flexibility in the use of Flux models."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "KurtHokke",
|
||||||
|
"title": "ComfyUI_KurtHokke-Nodes",
|
||||||
|
"reference": "https://github.com/KurtHokke/ComfyUI_KurtHokke-Nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/KurtHokke/ComfyUI_KurtHokke-Nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI_KurtHokke-Nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "OSAnimate",
|
||||||
|
"title": "ComfyUI-SpriteSheetMaker [WIP]",
|
||||||
|
"reference": "https://github.com/OSAnimate/ComfyUI-SpriteSheetMaker",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/OSAnimate/ComfyUI-SpriteSheetMaker"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "The sprite sheet maker node is a simple way to create sprite sheets and image grids.\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "BuffMcBigHuge",
|
||||||
|
"title": "ComfyUI-Buff-Nodes [WIP]",
|
||||||
|
"reference": "https://github.com/BuffMcBigHuge/ComfyUI-Buff-Nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/BuffMcBigHuge/ComfyUI-Buff-Nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Assorted Nodes by BuffMcBigHuge"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "ritikvirus",
|
||||||
|
"title": "ComfyUI Terminal Command Node [UNSAFE]",
|
||||||
|
"reference": "https://github.com/ritikvirus/comfyui-terminal-modal-node",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/ritikvirus/comfyui-terminal-modal-node"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This repository provides a custom ComfyUI node that lets you execute arbitrary terminal commands directly from the ComfyUI interface. [w/This extension allows remote command execution.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "pixuai",
|
||||||
|
"title": "ComfyUI-PixuAI",
|
||||||
|
"reference": "https://github.com/pixuai/ComfyUI-PixuAI",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/pixuai/ComfyUI-PixuAI"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A collection of ComfyUI nodes designed to streamline prompt creation, organization, and discovery - making your workflows faster and more intuitive."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "techidsk",
|
||||||
|
"title": "comfyui_molook_nodes [WIP]",
|
||||||
|
"reference": "https://github.com/techidsk/comfyui_molook_nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/techidsk/comfyui_molook_nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Some extra nodes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Northerner1",
|
||||||
|
"title": "ComfyUI_North_Noise [WIP]",
|
||||||
|
"reference": "https://github.com/Northerner1/ComfyUI_North_Noise",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Northerner1/ComfyUI_North_Noise"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: North Noise"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "ManuShamil",
|
||||||
|
"title": "ComfyUI_BodyEstimation_Nodes",
|
||||||
|
"reference": "https://github.com/ManuShamil/ComfyUI_BodyEstimation_Nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/ManuShamil/ComfyUI_BodyEstimation_Nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: CogitareLabsPoseIDExtractor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "MockbaTheBorg",
|
||||||
|
"title": "ComfyUI-Mockba",
|
||||||
|
"reference": "https://github.com/MockbaTheBorg/ComfyUI-Mockba",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MockbaTheBorg/ComfyUI-Mockba"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Image Batch/Flip/Rotate/Subtract/Dither, Barcode, Select, ..."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "jcomeme",
|
||||||
|
"title": "AsunaroTools",
|
||||||
|
"reference": "https://github.com/jcomeme/ComfyUI-AsunaroTools",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/jcomeme/ComfyUI-AsunaroTools"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A collection of custom nodes for ComfyUI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "ZHO-ZHO-ZHO",
|
||||||
|
"title": "ComfyUI Wan2.1 [WIP]",
|
||||||
|
"reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Wan-ZHO",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/ZHO-ZHO-ZHO/ComfyUI-Wan-ZHO"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "It’s estimated that ComfyUI itself will support it soon, so go ahead and give it a try!"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "kijai",
|
||||||
|
"title": "ComfyUI-WanVideoWrapper [WIP]",
|
||||||
|
"reference": "https://github.com/kijai/ComfyUI-WanVideoWrapper",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/kijai/ComfyUI-WanVideoWrapper"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI diffusers wrapper nodes for WanVideo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "ltdrdata",
|
||||||
|
"title": "comfyui-unsafe-torch [UNSAFE]",
|
||||||
|
"reference": "https://github.com/ltdrdata/comfyui-unsafe-torch",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/ltdrdata/comfyui-unsafe-torch"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "disable torch.load's `weigths_only`"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "IfnotFr",
|
||||||
|
"title": "⚡ ComfyUI Connect [WIP]",
|
||||||
|
"reference": "https://github.com/IfnotFr/ComfyUI-Connect",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/IfnotFr/ComfyUI-Connect"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Transform your ComfyUI into a powerful API, exposing all your saved workflows as ready-to-use HTTP endpoints."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "muvich3n",
|
||||||
|
"title": "ComfyUI-Crop-Border",
|
||||||
|
"reference": "https://github.com/muvich3n/ComfyUI-Crop-Border",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/muvich3n/ComfyUI-Crop-Border"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Crop Image Borders"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "masmullin2000",
|
||||||
|
"title": "ComfyUI-MMYolo",
|
||||||
|
"reference": "https://github.com/masmullin2000/ComfyUI-MMYolo",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/masmullin2000/ComfyUI-MMYolo"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A comfy node to find faces and output a mask"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Yeonri",
|
||||||
|
"title": "ComfyUI_LLM_Are_You_Listening [WIP]",
|
||||||
|
"reference": "https://github.com/Yeonri/ComfyUI_LLM_Are_You_Listening",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Yeonri/ComfyUI_LLM_Are_You_Listening"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: AYL_Node, AYL_GGUF_Node, AYL_API_Node\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "altkeyproject",
|
||||||
|
"title": "Dream Painter [WIP]",
|
||||||
|
"reference": "https://github.com/alt-key-project/comfyui-dream-painter",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/alt-key-project/comfyui-dream-painter"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Provide utilities for 2D image generation and processing."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "kimara-ai",
|
||||||
|
"title": "ComfyUI-Kimara-AI-Image-From-URL [WIP]",
|
||||||
|
"reference": "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Load image from URL and downscale to desired megapixels. Set megapixels to 0 for no downscaling."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "krisshen2021",
|
||||||
|
"title": "comfyui_OpenRouterNodes [WIP]",
|
||||||
|
"reference": "https://github.com/krisshen2021/comfyui_OpenRouterNodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/krisshen2021/comfyui_OpenRouterNodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "LLM custom nodes for comfyui\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Velour-Fog",
|
||||||
|
"title": "comfy-latent-nodes [UNSAFE]",
|
||||||
|
"reference": "https://github.com/Velour-Fog/comfy-latent-nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Velour-Fog/comfy-latent-nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI nodes to save and load a latent to a specified directory. Saves time for doing operations on a latent such as upscaling without having to re-trigger the creation of the original latent.[w/This node can write files to an arbitrary path.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "jgbyte",
|
||||||
|
"title": "ComfyUI-RandomCube [WIP]",
|
||||||
|
"reference": "https://github.com/jgbyte/ComfyUI-RandomCube",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/jgbyte/ComfyUI-RandomCube"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: RandomCubeGrid"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "thot-experiment",
|
||||||
|
"title": "comfy-live-preview [WIP]",
|
||||||
|
"reference": "https://github.com/thot-experiment/comfy-live-preview",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/thot-experiment/comfy-live-preview"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "external live preview plugin for ComfyUI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "AhBumm",
|
||||||
|
"title": "ComfyUI-Upscayl",
|
||||||
|
"reference": "https://github.com/AhBumm/ComfyUI-Upscayl",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/AhBumm/ComfyUI-Upscayl"
|
||||||
|
],
|
||||||
|
"nodename_pattern": "\\(BillBum\\)$",
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Upscayl Upscaler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "NEZHA625",
|
||||||
|
"title": "ComfyUI-tools-by-dong [UNSAFE]",
|
||||||
|
"reference": "https://github.com/NEZHA625/ComfyUI-tools-by-dong",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/NEZHA625/ComfyUI-tools-by-dong"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: HuggingFaceUploadNode, ImageDownloader, LoraIterator, FileMoveNode, InputDetectionNode, ...\nNOTE: The files in the repo are not organized.[w/This node pack includes nodes that can modify arbitrary files.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "if-ai",
|
||||||
|
"title": "ComfyUI-IF_Zonos [WIP]",
|
||||||
|
"reference": "https://github.com/if-ai/ComfyUI-IF_Zonos",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/if-ai/ComfyUI-IF_Zonos"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Zonos for ComfyUI"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "grinlau18",
|
||||||
|
"title": "Xiser_Nodes [WIP]",
|
||||||
|
"reference": "https://github.com/grinlau18/ComfyUI_XISER_Nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/grinlau18/ComfyUI_XISER_Nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A collection of custom nodes for ComfyUI\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "LAOGOU-666",
|
||||||
|
"title": "Comfyui_StartPatch [UNSAFE]",
|
||||||
|
"reference": "https://github.com/LAOGOU-666/Comfyui_StartPatch",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/LAOGOU-666/Comfyui_StartPatch"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This patch plugin optimizes the node information processing mechanism of the ComfyUI server, significantly improving server performance and response speed. It greatly reduces the browser page initialization waiting time. [w/Since this patch modifies key functions of ComfyUI, it is highly likely to cause compatibility issues.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "badmike",
|
||||||
|
"title": "Prompt Factory [CONFLICT]",
|
||||||
|
"reference": "https://github.com/badmike/comfyui-prompt-factory",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/badmike/comfyui-prompt-factory"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A modular system that adds randomness to prompt generation [w/This node pack is causing a name conflict with https://github.com/satche/comfyui-prompt-factory]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "owengillett",
|
||||||
|
"title": "ComfyUI-tilefusion",
|
||||||
|
"reference": "https://github.com/owengillett/ComfyUI-tilefusion",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/owengillett/ComfyUI-tilefusion"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Helper nodes for generating seamless tiles."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Scaryplasmon",
|
||||||
|
"title": "ComfTrellis [WIP]",
|
||||||
|
"reference": "https://github.com/Scaryplasmon/ComfTrellis",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Scaryplasmon/ComfTrellis"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "1 click install to run Trellis in ComfyUI\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "fangziheng2321",
|
||||||
|
"title": "comfyuinode_chopmask [WIP]",
|
||||||
|
"reference": "https://github.com/fangziheng2321/comfyuinode_chopmask",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/fangziheng2321/comfyuinode_chopmask"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "a custom comfyui node for '/fooocusinpaint_upload'\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "D1-3105",
|
||||||
|
"title": "ComfyUI-VideoStream",
|
||||||
|
"reference": "https://github.com/D1-3105/ComfyUI-VideoStream",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/D1-3105/ComfyUI-VideoStream"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: FloWWeaverExportSingleFrameGRPC"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "gmorks",
|
||||||
|
"title": "ComfyUI Animagine prompt [WIP]",
|
||||||
|
"reference": "https://github.com/gmorks/ComfyUI-Animagine-Prompt",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/gmorks/ComfyUI-Animagine-Prompt"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Comfy UI node to prompt build for [a/https://huggingface.co/cagliostrolab/animagine-xl-4.0](https://huggingface.co/cagliostrolab/animagine-xl-4.0) model\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "wirytiox",
|
||||||
|
"title": "ComfyUI-Qwen [CONFLICT]",
|
||||||
|
"reference": "https://github.com/mr-krak3n/ComfyUI-Qwen",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/mr-krak3n/ComfyUI-Qwen"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This repository contains custom nodes for ComfyUI, designed to facilitate working with language models such as Qwen2.5 and DeepSeek. [w/This node pack is causing a name conflict with https://github.com/ZHO-ZHO-ZHO/ComfyUI-Qwen]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "hiusdev",
|
||||||
|
"title": "ComfyUI_Lah_Toffee",
|
||||||
|
"reference": "https://github.com/hiusdev/ComfyUI_Lah_Toffee",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/hiusdev/ComfyUI_Lah_Toffee"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Lah LoadVideoRandom"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "hdfhssg",
|
||||||
|
"title": "ComfyUI_pxtool [WIP]",
|
||||||
|
"reference": "https://github.com/hdfhssg/ComfyUI_pxtool",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/hdfhssg/ComfyUI_pxtool"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This is a custom plugin node for ComfyUI that modifies and extends some features from existing projects. The main implementations include:\n* Reproducing some features of the [a/Stable-Diffusion-Webui-Civitai-Helper](https://github.com/zixaphir/Stable-Diffusion-Webui-Civitai-Helper) project within ComfyUI\n* Implementing a feature to randomly generate related prompt words by referencing the [a/noob-wiki dataset](https://huggingface.co/datasets/Laxhar/noob-wiki/tree/main)\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "franky519",
|
||||||
|
"title": "comfyui-redux-style",
|
||||||
|
"reference": "https://github.com/franky519/comfyui-redux-style",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/franky519/comfyui-redux-style"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Style Model Grid, Style Model Apply, Style Model Advanced"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "rishipandey125",
|
||||||
|
"title": "ComfyUI-FramePacking [WIP]",
|
||||||
|
"reference": "https://github.com/rishipandey125/ComfyUI-FramePacking",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/rishipandey125/ComfyUI-FramePacking"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Add Grid Boundaries, Pack Frames, Unpack Frames, Resize Frame"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Northerner1",
|
||||||
|
"title": "ComfyUI_North_Noise [WIP]",
|
||||||
|
"reference": "https://github.com/Northerner1/ComfyUI_North_Noise",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Northerner1/ComfyUI_North_Noise"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Unsampler"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "kimara-ai",
|
||||||
|
"title": "ComfyUI-Kimara-AI-Image-From-URL [WIP]",
|
||||||
|
"reference": "https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/kimara-ai/ComfyUI-Kimara-AI-Image-From-URL"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Load image from URL and downscale to desired megapixels. Set megapixels to 0 for no downscaling."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "tc8M4lF3s88",
|
||||||
|
"title": "comfy-tif-support",
|
||||||
|
"reference": "https://github.com/M4lF3s/comfy-tif-support",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/M4lF3s/comfy-tif-support"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Load TIFF"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "tc888",
|
||||||
|
"title": "ComfyUI_Save_Flux_Image",
|
||||||
|
"reference": "https://github.com/tc888/ComfyUI_Save_Flux_Image",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/tc888/ComfyUI_Save_Flux_Image"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Customized version of comfyui-image-save tailored for saving Flux images"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "var1ableX",
|
||||||
|
"title": "ComfyUI_Accessories",
|
||||||
|
"reference": "https://github.com/var1ableX/ComfyUI_Accessories",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/var1ableX/ComfyUI_Accessories"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Get Mask Dimensions, Get Random Dimensions, Is Mask Empty/Image, Any Cast, Make List From Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "xinyiSS",
|
||||||
|
"title": "CombineMasksNode",
|
||||||
|
"reference": "https://github.com/xinyiSS/CombineMasksNode",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/xinyiSS/CombineMasksNode"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Combine Masks Node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "osuiso-depot",
|
||||||
|
"title": "comfyui-keshigom_custom",
|
||||||
|
"reference": "https://github.com/osuiso-depot/comfyui-keshigom_custom",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/osuiso-depot/comfyui-keshigom_custom"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: RegexpChopper, FLIP-W/H Selector, FLIP-W/H SelectorConst, TextFind, ckpt_Loader_Simple, True-or-False, myStringNode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "LucipherDev",
|
||||||
|
"title": "ComfyUI-Sentinel [WIP]",
|
||||||
|
"reference": "https://github.com/LucipherDev/ComfyUI-Sentinel",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/LucipherDev/ComfyUI-Sentinel"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI Extension for Advanced Security. Implements login, multi-user registration, IP filtering, and user-specific input/output directories.[w/WARN:While ComfyUI Sentinel enhances security for ComfyUI, it does not guarantee absolute protection. Security is about risk mitigation, not elimination. Users are responsible for implementing their own security measures.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "threadedblue",
|
||||||
|
"title": "MLXnodes [WIP]",
|
||||||
|
"reference": "https://github.com/threadedblue/MLXnodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/threadedblue/MLXnodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A port of MLX Examples to ComfyUI custom_nodes. These are intended to run on a macOS M1.\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "jschoormans",
|
||||||
|
"title": "Comfy-InterestingPixels [WIP]",
|
||||||
|
"reference": "https://github.com/jschoormans/Comfy-InterestingPixels",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/jschoormans/Comfy-InterestingPixels"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Shareable Image Slider, Random Palette\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "jschoormans",
|
||||||
|
"title": "ComfyUI-TexturePacker [WIP]",
|
||||||
|
"reference": "https://github.com/kijai/ComfyUI-TexturePacker",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/jschoormans/Comfy-InterestingPixels"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI node to use PyTexturePacker\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "lum3on",
|
||||||
|
"title": "comfyui_LLM_Polymath [WIP]",
|
||||||
|
"reference": "https://github.com/lum3on/comfyui_LLM_Polymath",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/lum3on/comfyui_LLM_Polymath"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "An advanced chat node, that integrates large language models to automate data processes and enhance prompt responses through real-time web search and image handling. It supports both OpenAI's GPT-like models and a local Ollama API. Custom node finder and smart assistant tools provide tailored workflow recommendations for efficient integration. Additionally, the node dynamically augments prompts and offers flexible output compression options.\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "MickeyJ",
|
||||||
|
"title": "ComfyUI_mickster_nodes [WIP]",
|
||||||
|
"reference": "https://github.com/MickeyJ/ComfyUI_mickster_nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MickeyJ/ComfyUI_mickster_nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A collection of custom nodes for ComfyUI, focusing on image handling and LoRA training."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "thedivergentai",
|
||||||
|
"title": "Divergent Nodes [WIP]",
|
||||||
|
"reference": "https://github.com/thedivergentai/divergent_nodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/thedivergentai/divergent_nodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A ComfyUI custom node for counting CLIP tokens in text input."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "gold24park",
|
||||||
|
"title": "loki-comfyui-node",
|
||||||
|
"reference": "https://github.com/gold24park/loki-comfyui-node",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/gold24park/loki-comfyui-node"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Get Image Luminance, Get Dominant Color, Overlay Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "hayden-fr",
|
||||||
|
"title": "ComfyUI-Image-Browsing [USAFE]",
|
||||||
|
"id": "image-browsing",
|
||||||
|
"reference": "https://github.com/hayden-fr/ComfyUI-Image-Browsing",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/hayden-fr/ComfyUI-Image-Browsing"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Image Browsing: browsing, download and delete."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "molbal",
|
||||||
|
"title": "comfy-url-fetcher [WIP]",
|
||||||
|
"reference": "https://github.com/molbal/comfy-url-fetcher",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/molbal/comfy-url-fetcher"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Fetches URLs"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "myAiLemon",
|
||||||
|
"title": "MagicAutomaticPicture [WIP]",
|
||||||
|
"reference": "https://github.com/myAiLemon/MagicAutomaticPicture",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/myAiLemon/MagicAutomaticPicture"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A comfyui node package that can generate pictures and automatically save positive prompts and eliminate unwanted prompts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "neverbiasu",
|
||||||
|
"title": "ComfyUI_Output_as_Input",
|
||||||
|
"reference": "https://github.com/a-und-b/ComfyUI_Output_as_Input",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/a-und-b/ComfyUI_Output_as_Input"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This is a simple custom ComfyUI node that allows you to easily use recent output images as input in your workflows. It does not allow image uploads on purpose and does not require any additional dependencies.\nNOTE: The files in the repo are not organized."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "neverbiasu",
|
"author": "neverbiasu",
|
||||||
"title": "ComfyUI-DeepSeek",
|
"title": "ComfyUI-DeepSeek",
|
||||||
@@ -63,13 +766,13 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "HuangYuChuh",
|
"author": "HuangYuChuh",
|
||||||
"title": "ComfyUI-DeepSeek_Toolkit [WIP]",
|
"title": "ComfyUI-DeepSeek-Toolkit [WIP]",
|
||||||
"reference": "https://github.com/HuangYuChuh/ComfyUI-DeepSeek_Toolkit",
|
"reference": "https://github.com/HuangYuChuh/ComfyUI-DeepSeek-Toolkit",
|
||||||
"files": [
|
"files": [
|
||||||
"https://github.com/HuangYuChuh/ComfyUI-DeepSeek_Toolkit"
|
"https://github.com/HuangYuChuh/ComfyUI-DeepSeek-Toolkit"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "ComfyUI-DeepSeek_Toolkit is a deep learning toolkit for ComfyUI that integrates the DeepSeek Janus model, offering functionalities for image generation and image understanding.\nNOTE: The files in the repo are not organized."
|
"description": "ComfyUI-DeepSeek-Toolkit is a deep learning toolkit for ComfyUI that integrates the DeepSeek Janus model, offering functionalities for image generation and image understanding.\nNOTE: The files in the repo are not organized."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "comfyuiblog",
|
"author": "comfyuiblog",
|
||||||
@@ -139,7 +842,7 @@
|
|||||||
"https://github.com/7BEII/Comfyui_PDuse"
|
"https://github.com/7BEII/Comfyui_PDuse"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES:PD_groupfontsize unnify, PD_grownumber-JSON, PD_add or delete words, PD_Image Crop Location, PD_Image centerCrop, PD_GetImageSize\nNOTE: The files in the repo are not organized."
|
"description": "NODES: PD_json_group_fontsize, PD_Incremental_JSON, PD_removeword, PD_Image Crop Location, PD_ImageConcanate, PD_FileName_refixer\nNOTE: The files in the repo are not organized."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "RLW-Chars",
|
"author": "RLW-Chars",
|
||||||
@@ -199,17 +902,7 @@
|
|||||||
"https://github.com/yanhuifair/comfyui-deepseek"
|
"https://github.com/yanhuifair/comfyui-deepseek"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "Implement deepseek API call [a/https://api-docs.deepseek.com/](Implement deepseek API call https://api-docs.deepseek.com/)\nNOTE: The files in the repo are not organized."
|
"description": "nodes for deepseek api\nNOTE: The files in the repo are not organized."
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "807502278",
|
|
||||||
"title": "ComfyUI_TensorRT_Merge [WIP]",
|
|
||||||
"reference": "https://github.com/807502278/ComfyUI_TensorRT_Merge",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/807502278/ComfyUI_TensorRT_Merge"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Non diffusion models supported by TensorRT, merged Comfyui plugin, added onnx automatic download and trt model conversion nodes."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "IfnotFr",
|
"author": "IfnotFr",
|
||||||
@@ -221,16 +914,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES: Face Crop, [w/A pack of custom nodes used in my projects. Not intended to be used by other persons as the usage is not documented. But if something interests you in this repository, go for it !]"
|
"description": "NODES: Face Crop, [w/A pack of custom nodes used in my projects. Not intended to be used by other persons as the usage is not documented. But if something interests you in this repository, go for it !]"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "zmwv823",
|
|
||||||
"title": "ComfyUI-VideoDiffusion",
|
|
||||||
"reference": "https://github.com/zmwv823/ComfyUI-VideoDiffusion",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/zmwv823/ComfyUI-VideoDiffusion"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "[a/LatentSync](https://github.com/bytedance/LatentSync) and [a/Sonic](https://github.com/jixiaozhong/Sonic). [w/Just for study purpose. It's not for directly use, u should know how to fix issues.]"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "KihongK",
|
"author": "KihongK",
|
||||||
"title": "ComfyUI-RoysNodes [WIP]",
|
"title": "ComfyUI-RoysNodes [WIP]",
|
||||||
@@ -341,16 +1024,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "Tools for creating voxel based videos"
|
"description": "Tools for creating voxel based videos"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "Njbx",
|
|
||||||
"title": "ComfyUI-blockswap",
|
|
||||||
"reference": "https://github.com/Njbx/ComfyUI-blockswap",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Njbx/ComfyUI-blockswap"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: Block Swap"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "PATATAJEC",
|
"author": "PATATAJEC",
|
||||||
"title": "Patatajec-Nodes [WIP]",
|
"title": "Patatajec-Nodes [WIP]",
|
||||||
@@ -499,7 +1172,7 @@
|
|||||||
"https://github.com/yanhuifair/ComfyUI-FairLab"
|
"https://github.com/yanhuifair/ComfyUI-FairLab"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES: CLIP Text Encode Translated, Translate String, Load Image From Folder, Save String To Folder, Fix UTF-8 String, String Combine, String Field, Download Image, Save Images To Folder, Save Image To Folder, Image Resize"
|
"description": "NODES: CLIP Text Encode Translated, Translate String, Load Image From Folder, Save String To Folder, Fix UTF-8 String, String Combine, String Field, Download Image, Save Images To Folder, Save Image To Folder, Image Resize, ..."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "nomcycle",
|
"author": "nomcycle",
|
||||||
@@ -579,7 +1252,7 @@
|
|||||||
"https://github.com/DraconicDragon/ComfyUI_e621_booru_toolkit"
|
"https://github.com/DraconicDragon/ComfyUI_e621_booru_toolkit"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "for now: just custom node(s) to fetch tags from a given danbooru (soon e621 too) post link\ncurrently only supports danbooru-style urls + api response formats\nthis repo is a rewrite of: [a/https://github.com/yffyhk/comfyui_auto_danbooru](https://github.com/yffyhk/comfyui_auto_danbooru)"
|
"description": "WIP. Nodes: Fetch e621/danbooru image and/or tags etc from a given URL; Get the Wiki entry for a tag through a button press."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "Grey3016",
|
"author": "Grey3016",
|
||||||
@@ -732,16 +1405,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES: File Mv, File Path, File Dir.\n[w/This is dangerous as it provides the ability to manipulate arbitrary user files.]"
|
"description": "NODES: File Mv, File Path, File Dir.\n[w/This is dangerous as it provides the ability to manipulate arbitrary user files.]"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "scottmudge",
|
|
||||||
"title": "ComfyUI_BiscuitNodes",
|
|
||||||
"reference": "https://github.com/scottmudge/ComfyUI_BiscuitNodes",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/scottmudge/ComfyUI_BiscuitNodes"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Load Image From Path Using File Selector"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "JissiChoi",
|
"author": "JissiChoi",
|
||||||
"title": "ComfyUI-Jissi-List [WIP]",
|
"title": "ComfyUI-Jissi-List [WIP]",
|
||||||
@@ -1378,16 +2041,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES:Mask Size Calculator (MagicAI), Universal Mask Converter (MagicAI), Python Execution (MagicAI), Extract JSON From Text Node(MagicAI)\n[w/This extension allows the execution of arbitrary Python code from a workflow.]"
|
"description": "NODES:Mask Size Calculator (MagicAI), Universal Mask Converter (MagicAI), Python Execution (MagicAI), Extract JSON From Text Node(MagicAI)\n[w/This extension allows the execution of arbitrary Python code from a workflow.]"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "T8star1984",
|
|
||||||
"title": "comfyui-purgevram",
|
|
||||||
"reference": "https://github.com/T8star1984/comfyui-purgevram",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/T8star1984/comfyui-purgevram"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES:PurgeVRAM.\nCan be added after any node to clean up vram and memory"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "Laser-one",
|
"author": "Laser-one",
|
||||||
"title": "ComfyUI-align-pose",
|
"title": "ComfyUI-align-pose",
|
||||||
@@ -1408,16 +2061,6 @@
|
|||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "To use stepfun's library, you need an official api that supports multimodal inputs such as video and pictures [a/https://platform.stepfun.com/request-restriction](https://platform.stepfun.com/request-restriction)"
|
"description": "To use stepfun's library, you need an official api that supports multimodal inputs such as video and pictures [a/https://platform.stepfun.com/request-restriction](https://platform.stepfun.com/request-restriction)"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "attashe",
|
|
||||||
"title": "ComfyUI-FluxRegionAttention [WIP]",
|
|
||||||
"reference": "https://github.com/attashe/ComfyUI-FluxRegionAttention",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/attashe/ComfyUI-FluxRegionAttention"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Implement Region Attention for Flux model"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "aria1th",
|
"author": "aria1th",
|
||||||
"title": "ComfyUI-SkipCFGSigmas",
|
"title": "ComfyUI-SkipCFGSigmas",
|
||||||
@@ -1426,7 +2069,7 @@
|
|||||||
"https://github.com/aria1th/ComfyUI-SkipCFGSigmas"
|
"https://github.com/aria1th/ComfyUI-SkipCFGSigmas"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES:CFGControl_SKIPCFG"
|
"description": "NODES: CFGControl_SKIPCFG"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "Clelstyn",
|
"author": "Clelstyn",
|
||||||
@@ -1476,7 +2119,7 @@
|
|||||||
"https://github.com/oshtz/ComfyUI-oshtz-nodes"
|
"https://github.com/oshtz/ComfyUI-oshtz-nodes"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "Custom nodes for ComfyUI created for some of my workflows.\nLLM All-in-One Node, String Splitter Node, LoRA Switcher Node, Image Overlay Node"
|
"description": "Custom nodes for ComfyUI created for some of my workflows.\nLLM All-in-One Node, String Splitter Node, LoRA Switcher Node, Image Overlay Node\nNOTE: The files in the repo are not organized."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "m-ai-studio",
|
"author": "m-ai-studio",
|
||||||
@@ -1596,7 +2239,7 @@
|
|||||||
"https://github.com/rouxianmantou/comfyui-rxmt-nodes"
|
"https://github.com/rouxianmantou/comfyui-rxmt-nodes"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES:Check Value Type"
|
"description": "NODES:Check Value Type, Why Prompt Text"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "SirVeggie",
|
"author": "SirVeggie",
|
||||||
@@ -1646,7 +2289,7 @@
|
|||||||
"https://github.com/fablestudio/ComfyUI-Showrunner-Utils"
|
"https://github.com/fablestudio/ComfyUI-Showrunner-Utils"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "NODES:Align Face, Generate Timestamp"
|
"description": "NODES: Align Face, Generate Timestamp, GetMostCommonColors, Alpha Crop and Position Image, Shrink Image"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "monate0615",
|
"author": "monate0615",
|
||||||
@@ -1980,16 +2623,6 @@
|
|||||||
"install_type": "git-clone",
|
"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]"
|
"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/). Line Count, Join String, Dither Image, Image Batch Loader"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "IgPoly",
|
"author": "IgPoly",
|
||||||
"title": "ComfyUI-igTools",
|
"title": "ComfyUI-igTools",
|
||||||
@@ -2324,13 +2957,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "chrisdreid",
|
"author": "chrisdreid",
|
||||||
"title": "ComfyUI_EnvAutopsyAPI [UNSAFE]",
|
"title": "ComfyUI_EnvAutopsyAPI Debugger [UNSAFE]",
|
||||||
|
"id": "chrisdreid",
|
||||||
"reference": "https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI",
|
"reference": "https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI",
|
||||||
"files": [
|
"files": [
|
||||||
"https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI"
|
"https://github.com/chrisdreid/ComfyUI_EnvAutopsyAPI"
|
||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"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.]"
|
"description": "A powerful debugging tool designed to provide in-depth analysis of your environment and dependencies by exposing API endpoints. This tool allows you to inspect environment variables, pip packages, python info 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]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"author": "Futureversecom",
|
"author": "Futureversecom",
|
||||||
@@ -2445,16 +3079,6 @@
|
|||||||
"install_type":"git-clone",
|
"install_type":"git-clone",
|
||||||
"description":"The ComfyUI code is under review in the official repository. Meanwhile, a temporary version is available below for immediate community use. We welcome users to try our workflow and appreciate any inquiries or suggestions."
|
"description":"The ComfyUI code is under review in the official repository. Meanwhile, a temporary version is available below for immediate community use. We welcome users to try our workflow and appreciate any inquiries or suggestions."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"author": "JichaoLiang",
|
|
||||||
"title": "Immortal_comfyUI",
|
|
||||||
"reference": "https://github.com/JichaoLiang/Immortal_comfyUI",
|
|
||||||
"files":[
|
|
||||||
"https://github.com/JichaoLiang/Immortal_comfyUI"
|
|
||||||
],
|
|
||||||
"install_type":"git-clone",
|
|
||||||
"description":"Nodes: NewNode, AppendNode, MergeNode, SetProperties, SaveToDirectory, ..."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "melMass",
|
"author": "melMass",
|
||||||
"title": "ComfyUI-Lygia",
|
"title": "ComfyUI-Lygia",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,15 @@
|
|||||||
{
|
{
|
||||||
"custom_nodes": [
|
"custom_nodes": [
|
||||||
|
{
|
||||||
|
"author": "PramaLLC",
|
||||||
|
"title": "ComfyUI BEN - Background Erase Network",
|
||||||
|
"reference": "https://github.com/PramaLLC/BEN2_ComfyUI",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/PramaLLC/BEN2_ComfyUI"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Remove backgrounds from images with [a/BEN2](https://huggingface.co/PramaLLC/BEN2) in ComfyUI\nOriginal repo: [a/https://github.com/DoctorDiffusion/ComfyUI-BEN](https://github.com/DoctorDiffusion/ComfyUI-BEN)"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "BlenderNeko",
|
"author": "BlenderNeko",
|
||||||
"title": "ltdrdata/ComfyUI_TiledKSampler",
|
"title": "ltdrdata/ComfyUI_TiledKSampler",
|
||||||
@@ -119,6 +129,16 @@
|
|||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "A forked version of ComfyUI_ExtraModels. (modified by Efficient-Large-Model)"
|
"description": "A forked version of ComfyUI_ExtraModels. (modified by Efficient-Large-Model)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Pablerdo",
|
||||||
|
"title": "ComfyUI-PSNodes",
|
||||||
|
"reference": "https://github.com/Pablerdo/ComfyUI-PSNodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Pablerdo/ComfyUI-PSNodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A fork of KJNodes for ComfyUI.\nVarious quality of life -nodes for ComfyUI, mostly just visual stuff to improve usability"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,198 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
"author": "Samulebotin",
|
||||||
|
"title": "ComfyUI-FreeVC_wrapper [REMOVED]",
|
||||||
|
"reference": "https://github.com/Samulebotin/ComfyUI-FreeVC_wrapper",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Samulebotin/ComfyUI-FreeVC_wrapper"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A voice conversion extension node for ComfyUI based on FreeVC, enabling high-quality voice conversion capabilities within the ComfyUI framework."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "GoingAI1998",
|
||||||
|
"title": "ComfyUI Web Canvas Node [REMOVED]",
|
||||||
|
"reference": "https://github.com/GoingAI1998/Comfyui_imgcanvas",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/GoingAI1998/Comfyui_imgcanvas"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI_imgcanvas At present, I have not used the useful comfyui custom node about layer mixing, and I have written a comfyui runtime automatic pop-up window for layer editing node"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "807502278",
|
||||||
|
"title": "ComfyUI_TensorRT_Merge [REMOVED]",
|
||||||
|
"reference": "https://github.com/807502278/ComfyUI_TensorRT_Merge",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/807502278/ComfyUI_TensorRT_Merge"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Non diffusion models supported by TensorRT, merged Comfyui plugin, added onnx automatic download and trt model conversion nodes."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "logtd",
|
||||||
|
"title": "ComfyUI-LTXTricks [DEPRECATED]",
|
||||||
|
"reference": "https://github.com/logtd/ComfyUI-LTXTricks",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/logtd/ComfyUI-LTXTricks"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A set of nodes that provide additional controls for the LTX Video model"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "JichaoLiang",
|
||||||
|
"title": "Immortal_comfyUI [REMOVED]",
|
||||||
|
"reference": "https://github.com/JichaoLiang/Immortal_comfyUI",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/JichaoLiang/Immortal_comfyUI"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES:ImNewNode, ImAppendNode, MergeNode, SetProperties, SaveToDirectory, batchNodes, redirectToNode, SetEvent, ..."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Rvage0815",
|
||||||
|
"title": "ComfyUI-RvTools [REMOVED]",
|
||||||
|
"reference": "https://github.com/Rvage0815/ComfyUI-RvTools",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Rvage0815/ComfyUI-RvTools"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "this node contains a lot of small little helpers like switches, passers and selectors that i use a lot to build my workflows."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Rvage0815",
|
||||||
|
"title": "RvTComfyUI-RvTools_v2 [REMOVED]",
|
||||||
|
"reference": "https://github.com/Rvage0815/ComfyUI-RvTools_v2",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Rvage0815/ComfyUI-RvTools_v2"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "this node contains a lot of small little helpers like switches, passers and selectors that i use a lot to build my workflows."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "scottmudge",
|
||||||
|
"title": "ComfyUI_BiscuitNodes [REMOVED]",
|
||||||
|
"reference": "https://github.com/scottmudge/ComfyUI_BiscuitNodes",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/scottmudge/ComfyUI_BiscuitNodes"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Load Image From Path Using File Selector"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "thanhduong0213929",
|
||||||
|
"title": "ComfyUI-DeepUnlock [REMOVED]",
|
||||||
|
"reference": "https://github.com/thanhduong0213929/ComfyUI-DeepUnlock",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/thanhduong0213929/ComfyUI-DeepUnlock"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "DeepFuze is a state-of-the-art deep learning tool that seamlessly integrates with ComfyUI to revolutionize facial transformations, lipsyncing, video generation, voice cloning, face swapping, and lipsync translation. Leveraging advanced algorithms, DeepFuze enables users to combine audio and video with unparalleled realism, ensuring perfectly synchronized facial movements. This innovative solution is ideal for content creators, animators, developers, and anyone seeking to elevate their video editing projects with sophisticated AI-driven features."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "pathway8-sudo",
|
||||||
|
"title": "RMBG [REMOVED]",
|
||||||
|
"reference": "https://github.com/pathway8-sudo/RMBG",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/pathway8-sudo/RMBG"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This repository provides a custom node for ComfyUI, leveraging the BriaRMBG model to remove backgrounds from images and output a transparent PNG."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "iris-Neko",
|
||||||
|
"title": "ComfyUI_ascii_art [REMOVED]",
|
||||||
|
"reference": "https://github.com/iris-Neko/ComfyUI_ascii_art",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/iris-Neko/ComfyUI_ascii_art"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "ComfyUI node for [a/ASCII art controlnet](https://civitai.com/models/986392)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "apesplat",
|
||||||
|
"title": "ezXY scripts and nodes [NOT MAINTAINED]",
|
||||||
|
"id": "ezxy",
|
||||||
|
"reference": "https://github.com/GMapeSplat/ComfyUI_ezXY",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/GMapeSplat/ComfyUI_ezXY"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Extensions/Patches: Enables linking float and integer inputs and ouputs. Values are automatically cast to the correct type and clamped to the correct range. Works with both builtin and custom nodes.[w/NOTE: This repo patches ComfyUI's validate_inputs and map_node_over_list functions while running. May break depending on your version of ComfyUI. Can be deactivated in config.yaml.]Nodes: A collection of nodes for facilitating the generation of XY plots. Capable of plotting changes over most primitive values.[w/Does not work with current version of Comfyui]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "mie",
|
||||||
|
"title": "ComfyUI_JanusProCaption [REMOVED]",
|
||||||
|
"reference": "https://github.com/MieMieeeee/ComfyUI-JanusProCaption",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/MieMieeeee/ComfyUI-JanusProCaption"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "Describe image or create caption files using Janus Pro Model"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "Njbx",
|
||||||
|
"title": "ComfyUI-blockswap [REMOVED]",
|
||||||
|
"reference": "https://github.com/Njbx/ComfyUI-blockswap",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/Njbx/ComfyUI-blockswap"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES: Block Swap"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "T8star1984",
|
||||||
|
"title": "comfyui-purgevram [REMOVED]",
|
||||||
|
"reference": "https://github.com/T8star1984/comfyui-purgevram",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/T8star1984/comfyui-purgevram"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "NODES:PurgeVRAM.\nCan be added after any node to clean up vram and memory"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "zmwv823",
|
||||||
|
"title": "ComfyUI-VideoDiffusion [REMOVED]",
|
||||||
|
"reference": "https://github.com/zmwv823/ComfyUI-VideoDiffusion",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/zmwv823/ComfyUI-VideoDiffusion"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "[a/LatentSync](https://github.com/bytedance/LatentSync) and [a/Sonic](https://github.com/jixiaozhong/Sonic). [w/Just for study purpose. It's not for directly use, u should know how to fix issues.]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "NyaamZ",
|
||||||
|
"title": "Get Booru Tag ExtendeD [REMOVED]",
|
||||||
|
"reference": "https://github.com/NyaamZ/ComfyUI-GetBooruTag-ED",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/NyaamZ/ComfyUI-GetBooruTag-ED"
|
||||||
|
],
|
||||||
|
"description": "Get tag from Booru site.",
|
||||||
|
"install_type": "git-clone"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "lingha",
|
||||||
|
"title": "comfyui_kj [REMOVED]",
|
||||||
|
"id": "comfyui_kj",
|
||||||
|
"reference": "https://github.com/XieChengYuan/comfyui_kj",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/XieChengYuan/comfyui_kj"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "comfyui_kj, A tool that can package workflows into projects and publish them to a WeChat Mini Program named Kaji, allowing charges to be collected from users."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "myAiLemon",
|
||||||
|
"title": "MagicGetPromptAutomatically [REMOVED]",
|
||||||
|
"reference": "https://github.com/myAiLemon/MagicGetPromptAutomatically",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/myAiLemon/MagicGetPromptAutomatically"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "A plug-in that can automatically generate pictures and save txt files in comfyui"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"author": "ryanontheinside",
|
"author": "ryanontheinside",
|
||||||
"title": "ComfyUI_ScavengerHunt [REMOVED]",
|
"title": "ComfyUI_ScavengerHunt [REMOVED]",
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
{
|
{
|
||||||
"models": [
|
"models": [
|
||||||
|
{
|
||||||
|
"name": "Inswapper-fp16 (face swap) [REMOVED]",
|
||||||
|
"type": "insightface",
|
||||||
|
"base": "inswapper",
|
||||||
|
"save_path": "insightface",
|
||||||
|
"description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)",
|
||||||
|
"reference": "https://github.com/facefusion/facefusion-assets",
|
||||||
|
"filename": "inswapper_128_fp16.onnx",
|
||||||
|
"url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128_fp16.onnx",
|
||||||
|
"size": "277.7MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Inswapper (face swap) [REMOVED]",
|
||||||
|
"type": "insightface",
|
||||||
|
"base": "inswapper",
|
||||||
|
"save_path": "insightface",
|
||||||
|
"description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)",
|
||||||
|
"reference": "https://github.com/facefusion/facefusion-assets",
|
||||||
|
"filename": "inswapper_128.onnx",
|
||||||
|
"url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx",
|
||||||
|
"size": "555.3MB"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "pfg-novel-n10.pt",
|
"name": "pfg-novel-n10.pt",
|
||||||
"type": "PFG",
|
"type": "PFG",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,85 @@
|
|||||||
{
|
{
|
||||||
"models": [
|
"models": [
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "Hunyuan Video",
|
||||||
|
"save_path": "diffusion_models/hunyuan_video",
|
||||||
|
"description": "Huyuan Video Image2Video diffusion model. repackaged version.",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
||||||
|
"filename": "hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/diffusion_models/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||||
|
"size": "25.6GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Comfy-Org/llava_llama3_vision.safetensors",
|
||||||
|
"type": "clip_vision",
|
||||||
|
"base": "LLaVA-Llama-3",
|
||||||
|
"save_path": "text_encoders",
|
||||||
|
"description": "llava_llama3_vision clip vison model. This is required for using Hunyuan Video Image2Video.",
|
||||||
|
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
||||||
|
"filename": "llava_llama3_vision.safetensors",
|
||||||
|
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/clip_vision/llava_llama3_vision.safetensors",
|
||||||
|
"size": "649MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "LTX-Video 2B v0.9.5 Checkpoint",
|
||||||
|
"type": "checkpoint",
|
||||||
|
"base": "LTX-Video",
|
||||||
|
"save_path": "checkpoints/LTXV",
|
||||||
|
"description": "LTX-Video is the first DiT-based video generation model capable of generating high-quality videos in real-time. It produces 24 FPS videos at a 768x512 resolution faster than they can be watched. Trained on a large-scale dataset of diverse videos, the model generates high-resolution videos with realistic and varied content.",
|
||||||
|
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||||
|
"filename": "ltx-video-2b-v0.9.5.safetensors",
|
||||||
|
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.5.safetensors",
|
||||||
|
"size": "6.34GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kolors/vae/diffusion_pytorch_model.fp16.safetensors",
|
||||||
|
"type": "VAE",
|
||||||
|
"base": "Kolors",
|
||||||
|
"save_path": "vae/kolors",
|
||||||
|
"description": "Kolors VAE",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors",
|
||||||
|
"filename": "diffusion_pytorch_model.fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.fp16.safetensors",
|
||||||
|
"size": "167MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kolors/vae/diffusion_pytorch_model.safetensors",
|
||||||
|
"type": "VAE",
|
||||||
|
"base": "Kolors",
|
||||||
|
"save_path": "vae/kolors",
|
||||||
|
"description": "Kolors VAE",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors",
|
||||||
|
"filename": "diffusion_pytorch_model.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.safetensors",
|
||||||
|
"size": "335MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "deepseek-ai/Janus-Pro-1B",
|
||||||
|
"type": "Janus-Pro",
|
||||||
|
"base": "Janus-Pro",
|
||||||
|
"save_path": "Janus-Pro",
|
||||||
|
"description": "[SNAPSHOT] Janus-Pro-1B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
||||||
|
"reference": "https://huggingface.co/deepseek-ai/Janus-Pro-1B",
|
||||||
|
"filename": "<huggingface>",
|
||||||
|
"url": "deepseek-ai/Janus-Pro-1B",
|
||||||
|
"size": "7.8GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "deepseek-ai/Janus-Pro-7B",
|
||||||
|
"type": "Janus-Pro",
|
||||||
|
"base": "Janus-Pro",
|
||||||
|
"save_path": "Janus-Pro",
|
||||||
|
"description": "[SNAPSHOT] Janus-Pro-7B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
||||||
|
"reference": "https://huggingface.co/deepseek-ai/Janus-Pro-7B",
|
||||||
|
"filename": "<huggingface>",
|
||||||
|
"url": "deepseek-ai/Janus-Pro-7B",
|
||||||
|
"size": "14.85GB"
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "Leoxing/pia.ckpt",
|
"name": "Leoxing/pia.ckpt",
|
||||||
"type": "animatediff-pia",
|
"type": "animatediff-pia",
|
||||||
|
|||||||
@@ -291,6 +291,26 @@
|
|||||||
],
|
],
|
||||||
"install_type": "git-clone",
|
"install_type": "git-clone",
|
||||||
"description": "Example of using ComfyUI Toolbar to Toggle ComfyUI links on/off"
|
"description": "Example of using ComfyUI Toolbar to Toggle ComfyUI links on/off"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "xhiroga",
|
||||||
|
"title": "ComfyUI-TypeScript-CustomNode",
|
||||||
|
"reference": "https://github.com/xhiroga/ComfyUI-TypeScript-CustomNode",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/xhiroga/ComfyUI-TypeScript-CustomNode"
|
||||||
|
],
|
||||||
|
"install_type": "git-clone",
|
||||||
|
"description": "This project is generated from xhiroga/ComfyUI-TypeScript-CustomNode"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"author": "zentrocdot",
|
||||||
|
"title": "ComfyUI-Turtle_Graphics_Demos",
|
||||||
|
"reference": "https://github.com/zentrocdot/ComfyUI-Turtle_Graphics_Demo",
|
||||||
|
"files": [
|
||||||
|
"https://github.com/zentrocdot/ComfyUI-Turtle_Graphics_Demo"
|
||||||
|
],
|
||||||
|
"description": "ComfyUI node for creating some Turtle Graphic demos.",
|
||||||
|
"install_type": "git-clone"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import atexit
|
import atexit
|
||||||
@@ -20,20 +21,23 @@ import cm_global
|
|||||||
import manager_downloader
|
import manager_downloader
|
||||||
import folder_paths
|
import folder_paths
|
||||||
|
|
||||||
try:
|
import datetime
|
||||||
|
if hasattr(datetime, 'datetime'):
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
def current_timestamp():
|
def current_timestamp():
|
||||||
return datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
|
return datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
|
||||||
except:
|
else:
|
||||||
|
# NOTE: Occurs in some Mac environments.
|
||||||
import time
|
import time
|
||||||
import datetime
|
|
||||||
logging.error(f"[ComfyUI-Manager] fallback timestamp mode\n datetime module is invalid: '{datetime.__file__}'")
|
logging.error(f"[ComfyUI-Manager] fallback timestamp mode\n datetime module is invalid: '{datetime.__file__}'")
|
||||||
def current_timestamp():
|
def current_timestamp():
|
||||||
return str(time.time()).split('.')[0]
|
return str(time.time()).split('.')[0]
|
||||||
|
|
||||||
security_check.security_check()
|
security_check.security_check()
|
||||||
|
|
||||||
cm_global.pip_blacklist = ['torch', 'torchsde', 'torchvision']
|
manager_util.add_python_path_to_env()
|
||||||
|
|
||||||
|
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||||
|
|
||||||
|
|
||||||
@@ -57,22 +61,6 @@ def is_import_failed_extension(name):
|
|||||||
return name in import_failed_extensions
|
return name in import_failed_extensions
|
||||||
|
|
||||||
|
|
||||||
def check_file_logging():
|
|
||||||
global enable_file_logging
|
|
||||||
try:
|
|
||||||
import configparser
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(manager_config_path)
|
|
||||||
default_conf = config['default']
|
|
||||||
|
|
||||||
if 'file_logging' in default_conf and default_conf['file_logging'].lower() == 'false':
|
|
||||||
enable_file_logging = False
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
check_file_logging()
|
|
||||||
|
|
||||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||||
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
||||||
|
|
||||||
@@ -97,12 +85,39 @@ comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
|||||||
custom_nodes_base_path = folder_paths.get_folder_paths('custom_nodes')[0]
|
custom_nodes_base_path = folder_paths.get_folder_paths('custom_nodes')[0]
|
||||||
manager_files_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), 'default', 'ComfyUI-Manager'))
|
manager_files_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), 'default', 'ComfyUI-Manager'))
|
||||||
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
||||||
|
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
||||||
restore_snapshot_path = os.path.join(manager_files_path, "startup-scripts", "restore-snapshot.json")
|
restore_snapshot_path = os.path.join(manager_files_path, "startup-scripts", "restore-snapshot.json")
|
||||||
manager_config_path = os.path.join(manager_files_path, 'config.ini')
|
manager_config_path = os.path.join(manager_files_path, 'config.ini')
|
||||||
|
|
||||||
cm_cli_path = os.path.join(comfyui_manager_path, "cm-cli.py")
|
cm_cli_path = os.path.join(comfyui_manager_path, "cm-cli.py")
|
||||||
|
|
||||||
|
|
||||||
|
default_conf = {}
|
||||||
|
|
||||||
|
def read_config():
|
||||||
|
global default_conf
|
||||||
|
try:
|
||||||
|
import configparser
|
||||||
|
config = configparser.ConfigParser(strict=False)
|
||||||
|
config.read(manager_config_path)
|
||||||
|
default_conf = config['default']
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def read_uv_mode():
|
||||||
|
if 'use_uv' in default_conf:
|
||||||
|
manager_util.use_uv = default_conf['use_uv'].lower() == 'true'
|
||||||
|
|
||||||
|
def check_file_logging():
|
||||||
|
global enable_file_logging
|
||||||
|
if 'file_logging' in default_conf and default_conf['file_logging'].lower() == 'false':
|
||||||
|
enable_file_logging = False
|
||||||
|
|
||||||
|
|
||||||
|
read_config()
|
||||||
|
read_uv_mode()
|
||||||
|
check_file_logging()
|
||||||
|
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2', 'ultralytics': 'ultralytics==8.3.40'}
|
cm_global.pip_overrides = {'numpy': 'numpy<2', 'ultralytics': 'ultralytics==8.3.40'}
|
||||||
if os.path.exists(manager_pip_overrides_path):
|
if os.path.exists(manager_pip_overrides_path):
|
||||||
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||||
@@ -111,6 +126,14 @@ if os.path.exists(manager_pip_overrides_path):
|
|||||||
cm_global.pip_overrides['ultralytics'] = 'ultralytics==8.3.40' # for security
|
cm_global.pip_overrides['ultralytics'] = 'ultralytics==8.3.40' # for security
|
||||||
|
|
||||||
|
|
||||||
|
if os.path.exists(manager_pip_blacklist_path):
|
||||||
|
with open(manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||||
|
for x in f.readlines():
|
||||||
|
y = x.strip()
|
||||||
|
if y != '':
|
||||||
|
cm_global.pip_blacklist.add(y)
|
||||||
|
|
||||||
|
|
||||||
def remap_pip_package(pkg):
|
def remap_pip_package(pkg):
|
||||||
if pkg in cm_global.pip_overrides:
|
if pkg in cm_global.pip_overrides:
|
||||||
res = cm_global.pip_overrides[pkg]
|
res = cm_global.pip_overrides[pkg]
|
||||||
@@ -410,28 +433,33 @@ except Exception as e:
|
|||||||
print(f"[ComfyUI-Manager] Logging failed: {e}")
|
print(f"[ComfyUI-Manager] Logging failed: {e}")
|
||||||
|
|
||||||
|
|
||||||
try:
|
def ensure_dependencies():
|
||||||
import git # noqa: F401
|
|
||||||
import toml # noqa: F401
|
|
||||||
except ModuleNotFoundError:
|
|
||||||
my_path = os.path.dirname(__file__)
|
|
||||||
requirements_path = os.path.join(my_path, "requirements.txt")
|
|
||||||
|
|
||||||
print("## ComfyUI-Manager: installing dependencies. (GitPython)")
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.check_output([sys.executable, '-s', '-m', 'pip', 'install', '-r', requirements_path])
|
import git # noqa: F401
|
||||||
except subprocess.CalledProcessError:
|
import toml # noqa: F401
|
||||||
print("## [ERROR] ComfyUI-Manager: Attempting to reinstall dependencies using an alternative method.")
|
import rich # noqa: F401
|
||||||
try:
|
import chardet # noqa: F401
|
||||||
result = subprocess.check_output([sys.executable, '-s', '-m', 'pip', 'install', '--user', '-r', requirements_path])
|
except ModuleNotFoundError:
|
||||||
except subprocess.CalledProcessError:
|
my_path = os.path.dirname(__file__)
|
||||||
print("## [ERROR] ComfyUI-Manager: Failed to install the GitPython package in the correct Python environment. Please install it manually in the appropriate environment. (You can seek help at https://app.element.io/#/room/%23comfyui_space%3Amatrix.org)")
|
requirements_path = os.path.join(my_path, "requirements.txt")
|
||||||
|
|
||||||
try:
|
print("## ComfyUI-Manager: installing dependencies. (GitPython)")
|
||||||
print("## ComfyUI-Manager: installing dependencies done.")
|
try:
|
||||||
except:
|
subprocess.check_output(manager_util.make_pip_cmd(['install', '-r', requirements_path]))
|
||||||
# maybe we should sys.exit() here? there is at least two screens worth of error messages still being pumped after our error messages
|
except subprocess.CalledProcessError:
|
||||||
print("## [ERROR] ComfyUI-Manager: GitPython package seems to be installed, but failed to load somehow. Make sure you have a working git client installed")
|
print("## [ERROR] ComfyUI-Manager: Attempting to reinstall dependencies using an alternative method.")
|
||||||
|
try:
|
||||||
|
subprocess.check_output(manager_util.make_pip_cmd(['install', '--user', '-r', requirements_path]))
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
print("## [ERROR] ComfyUI-Manager: Failed to install the GitPython package in the correct Python environment. Please install it manually in the appropriate environment. (You can seek help at https://app.element.io/#/room/%23comfyui_space%3Amatrix.org)")
|
||||||
|
|
||||||
|
try:
|
||||||
|
print("## ComfyUI-Manager: installing dependencies done.")
|
||||||
|
except:
|
||||||
|
# maybe we should sys.exit() here? there is at least two screens worth of error messages still being pumped after our error messages
|
||||||
|
print("## [ERROR] ComfyUI-Manager: GitPython package seems to be installed, but failed to load somehow. Make sure you have a working git client installed")
|
||||||
|
|
||||||
|
ensure_dependencies()
|
||||||
|
|
||||||
|
|
||||||
print("** ComfyUI startup time:", current_timestamp())
|
print("** ComfyUI startup time:", current_timestamp())
|
||||||
@@ -452,11 +480,6 @@ else:
|
|||||||
|
|
||||||
def read_downgrade_blacklist():
|
def read_downgrade_blacklist():
|
||||||
try:
|
try:
|
||||||
import configparser
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(manager_config_path)
|
|
||||||
default_conf = config['default']
|
|
||||||
|
|
||||||
if 'downgrade_blacklist' in default_conf:
|
if 'downgrade_blacklist' in default_conf:
|
||||||
items = default_conf['downgrade_blacklist'].split(',')
|
items = default_conf['downgrade_blacklist'].split(',')
|
||||||
items = [x.strip() for x in items if x != '']
|
items = [x.strip() for x in items if x != '']
|
||||||
@@ -471,26 +494,20 @@ read_downgrade_blacklist()
|
|||||||
|
|
||||||
def check_bypass_ssl():
|
def check_bypass_ssl():
|
||||||
try:
|
try:
|
||||||
import configparser
|
|
||||||
import ssl
|
import ssl
|
||||||
config = configparser.ConfigParser()
|
|
||||||
config.read(manager_config_path)
|
|
||||||
default_conf = config['default']
|
|
||||||
|
|
||||||
if 'bypass_ssl' in default_conf and default_conf['bypass_ssl'].lower() == 'true':
|
if 'bypass_ssl' in default_conf and default_conf['bypass_ssl'].lower() == 'true':
|
||||||
print(f"[ComfyUI-Manager] WARN: Unsafe - SSL verification bypass option is Enabled. (see {manager_config_path})")
|
print(f"[ComfyUI-Manager] WARN: Unsafe - SSL verification bypass option is Enabled. (see {manager_config_path})")
|
||||||
ssl._create_default_https_context = ssl._create_unverified_context # SSL certificate error fix.
|
ssl._create_default_https_context = ssl._create_unverified_context # SSL certificate error fix.
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
check_bypass_ssl()
|
check_bypass_ssl()
|
||||||
|
|
||||||
|
|
||||||
# Perform install
|
# Perform install
|
||||||
processed_install = set()
|
processed_install = set()
|
||||||
script_list_path = os.path.join(folder_paths.user_directory, "default", "ComfyUI-Manager", "startup-scripts", "install-scripts.txt")
|
script_list_path = os.path.join(folder_paths.user_directory, "default", "ComfyUI-Manager", "startup-scripts", "install-scripts.txt")
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
|
||||||
|
|
||||||
|
|
||||||
def is_installed(name):
|
def is_installed(name):
|
||||||
@@ -597,17 +614,18 @@ def execute_lazy_install_script(repo_path, executable):
|
|||||||
|
|
||||||
if os.path.exists(requirements_path):
|
if os.path.exists(requirements_path):
|
||||||
print(f"Install: pip packages for '{repo_path}'")
|
print(f"Install: pip packages for '{repo_path}'")
|
||||||
with open(requirements_path, "r") as requirements_file:
|
|
||||||
for line in requirements_file:
|
|
||||||
package_name = remap_pip_package(line.strip())
|
|
||||||
if package_name and not is_installed(package_name):
|
|
||||||
if '--index-url' in package_name:
|
|
||||||
s = package_name.split('--index-url')
|
|
||||||
install_cmd = [sys.executable, "-m", "pip", "install", s[0].strip(), '--index-url', s[1].strip()]
|
|
||||||
else:
|
|
||||||
install_cmd = [sys.executable, "-m", "pip", "install", package_name]
|
|
||||||
|
|
||||||
process_wrap(install_cmd, repo_path)
|
lines = manager_util.robust_readlines(requirements_path)
|
||||||
|
for line in lines:
|
||||||
|
package_name = remap_pip_package(line.strip())
|
||||||
|
if package_name and not is_installed(package_name):
|
||||||
|
if '--index-url' in package_name:
|
||||||
|
s = package_name.split('--index-url')
|
||||||
|
install_cmd = manager_util.make_pip_cmd(["install", s[0].strip(), '--index-url', s[1].strip()])
|
||||||
|
else:
|
||||||
|
install_cmd = manager_util.make_pip_cmd(["install", package_name])
|
||||||
|
|
||||||
|
process_wrap(install_cmd, repo_path)
|
||||||
|
|
||||||
if os.path.exists(install_script_path) and f'{repo_path}/install.py' not in processed_install:
|
if os.path.exists(install_script_path) and f'{repo_path}/install.py' not in processed_install:
|
||||||
processed_install.add(f'{repo_path}/install.py')
|
processed_install.add(f'{repo_path}/install.py')
|
||||||
@@ -679,11 +697,43 @@ def execute_migration(moves):
|
|||||||
print(f"[ComfyUI-Manager] MIGRATION: '{x[0]}' -> '{x[1]}'")
|
print(f"[ComfyUI-Manager] MIGRATION: '{x[0]}' -> '{x[1]}'")
|
||||||
|
|
||||||
|
|
||||||
# Check if script_list_path exists
|
script_executed = False
|
||||||
if os.path.exists(script_list_path):
|
|
||||||
|
def execute_startup_script():
|
||||||
|
global script_executed
|
||||||
print("\n#######################################################################")
|
print("\n#######################################################################")
|
||||||
print("[ComfyUI-Manager] Starting dependency installation/(de)activation for the extension\n")
|
print("[ComfyUI-Manager] Starting dependency installation/(de)activation for the extension\n")
|
||||||
|
|
||||||
|
custom_nodelist_cache = None
|
||||||
|
|
||||||
|
def get_custom_node_paths():
|
||||||
|
nonlocal custom_nodelist_cache
|
||||||
|
if custom_nodelist_cache is None:
|
||||||
|
custom_nodelist_cache = set()
|
||||||
|
for base in folder_paths.get_folder_paths('custom_nodes'):
|
||||||
|
for x in os.listdir(base):
|
||||||
|
fullpath = os.path.join(base, x)
|
||||||
|
if os.path.isdir(fullpath):
|
||||||
|
custom_nodelist_cache.add(fullpath)
|
||||||
|
|
||||||
|
return custom_nodelist_cache
|
||||||
|
|
||||||
|
def execute_lazy_delete(path):
|
||||||
|
# Validate to prevent arbitrary paths from being deleted
|
||||||
|
if path not in get_custom_node_paths():
|
||||||
|
logging.error(f"## ComfyUI-Manager: The scheduled '{path}' is not a custom node path, so the deletion has been canceled.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
logging.info(f"## ComfyUI-Manager: SKIP-DELETE => '{path}' (already deleted)")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.rmtree(path)
|
||||||
|
logging.info(f"## ComfyUI-Manager: DELETE => '{path}'")
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"## ComfyUI-Manager: Failed to delete '{path}' ({e})")
|
||||||
|
|
||||||
executed = set()
|
executed = set()
|
||||||
# Read each line from the file and convert it to a list using eval
|
# Read each line from the file and convert it to a list using eval
|
||||||
with open(script_list_path, 'r', encoding="UTF-8", errors="ignore") as file:
|
with open(script_list_path, 'r', encoding="UTF-8", errors="ignore") as file:
|
||||||
@@ -707,6 +757,9 @@ if os.path.exists(script_list_path):
|
|||||||
elif script[1] == "#LAZY-MIGRATION":
|
elif script[1] == "#LAZY-MIGRATION":
|
||||||
execute_migration(script[2])
|
execute_migration(script[2])
|
||||||
|
|
||||||
|
elif script[1] == "#LAZY-DELETE-NODEPACK":
|
||||||
|
execute_lazy_delete(script[2])
|
||||||
|
|
||||||
elif os.path.exists(script[0]):
|
elif os.path.exists(script[0]):
|
||||||
if script[1] == "#FORCE":
|
if script[1] == "#FORCE":
|
||||||
del script[1]
|
del script[1]
|
||||||
@@ -715,7 +768,7 @@ if os.path.exists(script_list_path):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
print(f"\n## ComfyUI-Manager: EXECUTE => {script[1:]}")
|
print(f"\n## ComfyUI-Manager: EXECUTE => {script[1:]}")
|
||||||
print(f"\n## Execute install/(de)activation script for '{script[0]}'")
|
print(f"\n## Execute management script for '{script[0]}'")
|
||||||
|
|
||||||
new_env = os.environ.copy()
|
new_env = os.environ.copy()
|
||||||
if 'COMFYUI_FOLDERS_BASE_PATH' not in new_env:
|
if 'COMFYUI_FOLDERS_BASE_PATH' not in new_env:
|
||||||
@@ -723,31 +776,64 @@ if os.path.exists(script_list_path):
|
|||||||
exit_code = process_wrap(script[1:], script[0], env=new_env)
|
exit_code = process_wrap(script[1:], script[0], env=new_env)
|
||||||
|
|
||||||
if exit_code != 0:
|
if exit_code != 0:
|
||||||
print(f"install/(de)activation script failed: {script[0]}")
|
print(f"management script failed: {script[0]}")
|
||||||
else:
|
else:
|
||||||
print(f"\n## ComfyUI-Manager: CANCELED => {script[1:]}")
|
print(f"\n## ComfyUI-Manager: CANCELED => {script[1:]}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[ERROR] Failed to execute install/(de)activation script: {line} / {e}")
|
print(f"[ERROR] Failed to execute management script: {line} / {e}")
|
||||||
|
|
||||||
# Remove the script_list_path file
|
# Remove the script_list_path file
|
||||||
if os.path.exists(script_list_path):
|
if os.path.exists(script_list_path):
|
||||||
|
script_executed = True
|
||||||
os.remove(script_list_path)
|
os.remove(script_list_path)
|
||||||
|
|
||||||
print("\n[ComfyUI-Manager] Startup script completed.")
|
print("\n[ComfyUI-Manager] Startup script completed.")
|
||||||
print("#######################################################################\n")
|
print("#######################################################################\n")
|
||||||
|
|
||||||
|
|
||||||
|
# Check if script_list_path exists
|
||||||
|
if os.path.exists(script_list_path):
|
||||||
|
execute_startup_script()
|
||||||
|
|
||||||
|
|
||||||
pip_fixer.fix_broken()
|
pip_fixer.fix_broken()
|
||||||
|
|
||||||
del processed_install
|
del processed_install
|
||||||
del pip_fixer
|
del pip_fixer
|
||||||
manager_util.clear_pip_cache()
|
manager_util.clear_pip_cache()
|
||||||
|
|
||||||
|
if script_executed:
|
||||||
|
# Restart
|
||||||
|
print("[ComfyUI-Manager] Restarting to reapply dependency installation.")
|
||||||
|
|
||||||
|
if '__COMFY_CLI_SESSION__' in os.environ:
|
||||||
|
with open(os.path.join(os.environ['__COMFY_CLI_SESSION__'] + '.reboot'), 'w'):
|
||||||
|
pass
|
||||||
|
|
||||||
|
print("--------------------------------------------------------------------------\n")
|
||||||
|
exit(0)
|
||||||
|
else:
|
||||||
|
sys_argv = sys.argv.copy()
|
||||||
|
|
||||||
|
if sys_argv[0].endswith("__main__.py"): # this is a python module
|
||||||
|
module_name = os.path.basename(os.path.dirname(sys_argv[0]))
|
||||||
|
cmds = [sys.executable, '-m', module_name] + sys_argv[1:]
|
||||||
|
elif sys.platform.startswith('win32'):
|
||||||
|
cmds = ['"' + sys.executable + '"', '"' + sys_argv[0] + '"'] + sys_argv[1:]
|
||||||
|
else:
|
||||||
|
cmds = [sys.executable] + sys_argv
|
||||||
|
|
||||||
|
print(f"Command: {cmds}", flush=True)
|
||||||
|
print("--------------------------------------------------------------------------\n")
|
||||||
|
|
||||||
|
os.execv(sys.executable, cmds)
|
||||||
|
|
||||||
|
|
||||||
def check_windows_event_loop_policy():
|
def check_windows_event_loop_policy():
|
||||||
try:
|
try:
|
||||||
import configparser
|
import configparser
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser(strict=False)
|
||||||
config.read(manager_config_path)
|
config.read(manager_config_path)
|
||||||
default_conf = config['default']
|
default_conf = config['default']
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-manager"
|
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."
|
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
||||||
version = "3.12"
|
version = "3.30.9"
|
||||||
license = { file = "LICENSE.txt" }
|
license = { file = "LICENSE.txt" }
|
||||||
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions"]
|
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
||||||
|
|
||||||
[project.urls]
|
[project.urls]
|
||||||
Repository = "https://github.com/ltdrdata/ComfyUI-Manager"
|
Repository = "https://github.com/ltdrdata/ComfyUI-Manager"
|
||||||
|
|||||||
@@ -7,3 +7,5 @@ typer
|
|||||||
rich
|
rich
|
||||||
typing-extensions
|
typing-extensions
|
||||||
toml
|
toml
|
||||||
|
uv
|
||||||
|
chardet
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ python -m venv venv
|
|||||||
call venv/Scripts/activate
|
call venv/Scripts/activate
|
||||||
python -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu121
|
python -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu121
|
||||||
python -m pip install -r requirements.txt
|
python -m pip install -r requirements.txt
|
||||||
python -m pip install -r custom_nodes/ComfyUI-Manager/requirements.txt
|
python -m pip install -r custom_nodes/comfyui-manager/requirements.txt
|
||||||
cd ..
|
cd ..
|
||||||
echo "cd ComfyUI" >> run_gpu.bat
|
echo "cd ComfyUI" >> run_gpu.bat
|
||||||
echo "call venv/Scripts/activate" >> run_gpu.bat
|
echo "call venv/Scripts/activate" >> run_gpu.bat
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
.\python_embeded\python.exe -s -m pip install gitpython
|
.\python_embeded\python.exe -s -m pip install gitpython
|
||||||
.\python_embeded\python.exe -c "import git; git.Repo.clone_from('https://github.com/ltdrdata/ComfyUI-Manager', './ComfyUI/custom_nodes/comfyui-manager')"
|
.\python_embeded\python.exe -c "import git; git.Repo.clone_from('https://github.com/ltdrdata/ComfyUI-Manager', './ComfyUI/custom_nodes/comfyui-manager')"
|
||||||
|
.\python_embeded\python.exe -m pip install -r ./ComfyUI/custom_nodes/comfyui-manager/requirements.txt
|
||||||
|
|||||||
Reference in New Issue
Block a user