diff --git a/.github/workflows/publish-to-pypi.yml b/.github/workflows/publish-to-pypi.yml
new file mode 100644
index 000000000..10b97e597
--- /dev/null
+++ b/.github/workflows/publish-to-pypi.yml
@@ -0,0 +1,58 @@
+name: Publish to PyPI
+
+on:
+ workflow_dispatch:
+ push:
+ branches:
+ - main
+ paths:
+ - "pyproject.toml"
+
+jobs:
+ build-and-publish:
+ runs-on: ubuntu-latest
+ if: ${{ github.repository_owner == 'ltdrdata' || github.repository_owner == 'Comfy-Org' }}
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Set up Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.9'
+
+ - name: Install build dependencies
+ run: |
+ python -m pip install --upgrade pip
+ python -m pip install build twine
+
+ - name: Get current version
+ id: current_version
+ run: |
+ CURRENT_VERSION=$(grep -oP 'version = "\K[^"]+' pyproject.toml)
+ echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
+ echo "Current version: $CURRENT_VERSION"
+
+ - name: Build package
+ run: python -m build
+
+ - name: Create GitHub Release
+ id: create_release
+ uses: softprops/action-gh-release@v2
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ files: dist/*
+ tag_name: v${{ steps.current_version.outputs.version }}
+ draft: false
+ prerelease: false
+ generate_release_notes: true
+
+ - name: Publish to PyPI
+ uses: pypa/gh-action-pypi-publish@release/v1
+ with:
+ password: ${{ secrets.PYPI_TOKEN }}
+ skip-existing: true
+ verbose: true
\ No newline at end of file
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 463ecca9e..e47764afd 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -14,7 +14,7 @@ jobs:
publish-node:
name: Publish Custom Node to registry
runs-on: ubuntu-latest
- if: ${{ github.repository_owner == 'ltdrdata' }}
+ if: ${{ github.repository_owner == 'ltdrdata' || github.repository_owner == 'Comfy-Org' }}
steps:
- name: Check out code
uses: actions/checkout@v4
diff --git a/.gitignore b/.gitignore
index 33ee743b7..5bf40b47a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,4 +17,6 @@ github-stats-cache.json
pip_overrides.json
*.json
check2.sh
-/venv/
\ No newline at end of file
+/venv/
+build
+*.egg-info
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 000000000..8af7bc576
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,7 @@
+include comfyui_manager/js/*
+include comfyui_manager/*.json
+include comfyui_manager/glob/*
+include LICENSE.txt
+include README.md
+include requirements.txt
+include pyproject.toml
\ No newline at end of file
diff --git a/README.md b/README.md
index 7c3e6644b..cafdae902 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,7 @@

## NOTICE
+* V4.0: Modify the structure to be installable via pip instead of using git clone.
* V3.16: Support for `uv` has been added. Set `use_uv` in `config.ini`.
* V3.10: `double-click feature` is removed
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
@@ -13,78 +14,26 @@
## Installation
-### Installation[method1] (General installation method: ComfyUI-Manager only)
+* When installing the latest ComfyUI, it will be automatically installed as a dependency, so manual installation is no longer necessary.
-To install ComfyUI-Manager in addition to an existing installation of ComfyUI, you can follow the following steps:
+* Manual installation of the nightly version:
+ * Clone to a temporary directory (**Note:** Do **not** clone into `ComfyUI/custom_nodes`.)
+ ```
+ git clone https://github.com/Comfy-Org/ComfyUI-Manager
+ ```
+ * Install via pip
+ ```
+ cd ComfyUI-Manager
+ pip install .
+ ```
-1. goto `ComfyUI/custom_nodes` dir in terminal(cmd)
-2. `git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager`
-3. Restart ComfyUI
-
-
-### Installation[method2] (Installation for portable ComfyUI version: ComfyUI-Manager only)
-1. install git
-- https://git-scm.com/download/win
-- standalone version
-- select option: use windows default console window
-2. Download [scripts/install-manager-for-portable-version.bat](https://github.com/ltdrdata/ComfyUI-Manager/raw/main/scripts/install-manager-for-portable-version.bat) into installed `"ComfyUI_windows_portable"` directory
-- Don't click. Right click the link and use save as...
-3. double click `install-manager-for-portable-version.bat` batch file
-
-
-
-
-### Installation[method3] (Installation through comfy-cli: install ComfyUI and ComfyUI-Manager at once.)
-> RECOMMENDED: comfy-cli provides various features to manage ComfyUI from the CLI.
-
-* **prerequisite: python 3, git**
-
-Windows:
-```commandline
-python -m venv venv
-venv\Scripts\activate
-pip install comfy-cli
-comfy install
-```
-
-Linux/OSX:
-```commandline
-python -m venv venv
-. venv/bin/activate
-pip install comfy-cli
-comfy install
-```
* See also: https://github.com/Comfy-Org/comfy-cli
-### Installation[method4] (Installation for linux+venv: ComfyUI + ComfyUI-Manager)
-
-To install ComfyUI with ComfyUI-Manager on Linux using a venv environment, you can follow these steps:
-* **prerequisite: python-is-python3, python3-venv, git**
-
-1. Download [scripts/install-comfyui-venv-linux.sh](https://github.com/ltdrdata/ComfyUI-Manager/raw/main/scripts/install-comfyui-venv-linux.sh) into empty install directory
-- Don't click. Right click the link and use save as...
-- ComfyUI will be installed in the subdirectory of the specified directory, and the directory will contain the generated executable script.
-2. `chmod +x install-comfyui-venv-linux.sh`
-3. `./install-comfyui-venv-linux.sh`
-
-### Installation Precautions
-* **DO**: `ComfyUI-Manager` files must be accurately located in the path `ComfyUI/custom_nodes/comfyui-manager`
- * Installing in a compressed file format is not recommended.
-* **DON'T**: Decompress directly into the `ComfyUI/custom_nodes` location, resulting in the Manager contents like `__init__.py` being placed directly in that directory.
- * You have to remove all ComfyUI-Manager files from `ComfyUI/custom_nodes`
-* **DON'T**: In a form where decompression occurs in a path such as `ComfyUI/custom_nodes/ComfyUI-Manager/ComfyUI-Manager`.
-* **DON'T**: In a form where decompression occurs in a path such as `ComfyUI/custom_nodes/ComfyUI-Manager-main`.
- * In such cases, `ComfyUI-Manager` may operate, but it won't be recognized within `ComfyUI-Manager`, and updates cannot be performed. It also poses the risk of duplicate installations. Remove it and install properly via `git clone` method.
-
-
-You can execute ComfyUI by running either `./run_gpu.sh` or `./run_cpu.sh` depending on your system configuration.
+## Front-end
-## Colab Notebook
-This repository provides Colab notebooks that allow you to install and use ComfyUI, including ComfyUI-Manager. To use ComfyUI, [click on this link](https://colab.research.google.com/github/ltdrdata/ComfyUI-Manager/blob/main/notebooks/comfyui_colab_with_manager.ipynb).
-* Support for installing ComfyUI
-* Support for basic installation of ComfyUI-Manager
-* Support for automatically installing dependencies of custom nodes upon restarting Colab notebooks.
+* The built-in front-end of ComfyUI-Manager is the legacy front-end. The front-end for ComfyUI-Manager is now provided via [ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend).
+* To enable the legacy front-end, set the environment variable `ENABLE_LEGACY_COMFYUI_MANAGER_FRONT` to `true` before running.
## How To Use
diff --git a/__init__.py b/__init__.py
deleted file mode 100644
index f966524b3..000000000
--- a/__init__.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import os
-import sys
-
-cli_mode_flag = os.path.join(os.path.dirname(__file__), '.enable-cli-only-mode')
-
-if not os.path.exists(cli_mode_flag):
- sys.path.append(os.path.join(os.path.dirname(__file__), "glob"))
- import manager_server # noqa: F401
- import share_3rdparty # noqa: F401
- import cm_global
-
- if not cm_global.disable_front and not 'DISABLE_COMFYUI_MANAGER_FRONT' in os.environ:
- WEB_DIRECTORY = "js"
-else:
- print("\n[ComfyUI-Manager] !! cli-only-mode is enabled !!\n")
-
-NODE_CLASS_MAPPINGS = {}
-__all__ = ['NODE_CLASS_MAPPINGS']
-
-
-
diff --git a/cm-cli.sh b/cm-cli.sh
index b1a21ca55..1afaf9269 100755
--- a/cm-cli.sh
+++ b/cm-cli.sh
@@ -1,2 +1,2 @@
#!/bin/bash
-python cm-cli.py $*
+python ./comfyui_manager/cm-cli.py $*
diff --git a/comfyui_manager/__init__.py b/comfyui_manager/__init__.py
new file mode 100644
index 000000000..3fe040e05
--- /dev/null
+++ b/comfyui_manager/__init__.py
@@ -0,0 +1,24 @@
+import os
+import logging
+from comfy.cli_args import args
+
+ENABLE_LEGACY_COMFYUI_MANAGER_FRONT_DEFAULT = True # Enable legacy ComfyUI Manager frontend while new UI is in beta phase
+
+def prestartup():
+ from . import prestartup_script # noqa: F401
+ logging.info('[PRE] ComfyUI-Manager')
+
+
+def start():
+ logging.info('[START] ComfyUI-Manager')
+ from .glob import manager_server # noqa: F401
+ from .glob import share_3rdparty # noqa: F401
+ from .glob import cm_global # noqa: F401
+
+ should_show_legacy_manager_front = os.environ.get('ENABLE_LEGACY_COMFYUI_MANAGER_FRONT', 'false') == 'true' or ENABLE_LEGACY_COMFYUI_MANAGER_FRONT_DEFAULT
+ if not args.disable_manager and should_show_legacy_manager_front:
+ try:
+ import nodes
+ nodes.EXTENSION_WEB_DIRS['comfyui-manager-legacy'] = os.path.join(os.path.dirname(__file__), 'js')
+ except Exception as e:
+ print("Error enabling legacy ComfyUI Manager frontend:", e)
diff --git a/cm-cli.py b/comfyui_manager/cm-cli.py
similarity index 98%
rename from cm-cli.py
rename to comfyui_manager/cm-cli.py
index ad00a1085..e589e58e9 100644
--- a/cm-cli.py
+++ b/comfyui_manager/cm-cli.py
@@ -15,31 +15,30 @@
import importlib
-sys.path.append(os.path.dirname(__file__))
-sys.path.append(os.path.join(os.path.dirname(__file__), "glob"))
-
import manager_util
# read env vars
# COMFYUI_FOLDERS_BASE_PATH is not required in cm-cli.py
# `comfy_path` should be resolved before importing manager_core
+
comfy_path = os.environ.get('COMFYUI_PATH')
+
if comfy_path is None:
- try:
- import folder_paths
- comfy_path = os.path.join(os.path.dirname(folder_paths.__file__))
- except:
- 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, '..', '..'))
+ print("[bold red]cm-cli: environment variable 'COMFYUI_PATH' is not specified.[/bold red]")
+ exit(-1)
-# This should be placed here
sys.path.append(comfy_path)
+if not os.path.exists(os.path.join(comfy_path, 'folder_paths.py')):
+ print("[bold red]cm-cli: '{comfy_path}' is not a valid 'COMFYUI_PATH' location.[/bold red]")
+ exit(-1)
+
+
import utils.extra_config
-import cm_global
-import manager_core as core
-from manager_core import unified_manager
-import cnr_utils
+from .glob import cm_global
+from .glob import manager_core as core
+from .glob.manager_core import unified_manager
+from .glob import cnr_utils
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
diff --git a/glob/cm_global.py b/comfyui_manager/glob/cm_global.py
similarity index 100%
rename from glob/cm_global.py
rename to comfyui_manager/glob/cm_global.py
diff --git a/glob/cnr_utils.py b/comfyui_manager/glob/cnr_utils.py
similarity index 99%
rename from glob/cnr_utils.py
rename to comfyui_manager/glob/cnr_utils.py
index 6a61aaa35..bb4845a3e 100644
--- a/glob/cnr_utils.py
+++ b/comfyui_manager/glob/cnr_utils.py
@@ -6,8 +6,9 @@
from dataclasses import dataclass
from typing import List
-import manager_core
-import manager_util
+from . import manager_core
+from . import manager_util
+
import requests
import toml
diff --git a/comfyui_manager/glob/enums.py b/comfyui_manager/glob/enums.py
new file mode 100644
index 000000000..bd4a57f1c
--- /dev/null
+++ b/comfyui_manager/glob/enums.py
@@ -0,0 +1,17 @@
+import enum
+
+class NetworkMode(enum.Enum):
+ PUBLIC = "public"
+ PRIVATE = "private"
+ OFFLINE = "offline"
+
+class SecurityLevel(enum.Enum):
+ STRONG = "strong"
+ NORMAL = "normal"
+ NORMAL_MINUS = "normal-minus"
+ WEAK = "weak"
+
+class DBMode(enum.Enum):
+ LOCAL = "local"
+ CACHE = "cache"
+ REMOTE = "remote"
\ No newline at end of file
diff --git a/git_helper.py b/comfyui_manager/glob/git_helper.py
similarity index 98%
rename from git_helper.py
rename to comfyui_manager/glob/git_helper.py
index e79b43a60..54031dd14 100644
--- a/git_helper.py
+++ b/comfyui_manager/glob/git_helper.py
@@ -15,9 +15,12 @@
git_exe_path = os.environ.get('GIT_EXE_PATH')
if comfy_path is None:
- print("\nWARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path.", file=sys.stderr)
- comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
+ print("git_helper: environment variable 'COMFYUI_PATH' is not specified.")
+ exit(-1)
+if not os.path.exists(os.path.join(comfy_path, 'folder_paths.py')):
+ print("git_helper: '{comfy_path}' is not a valid 'COMFYUI_PATH' location.")
+ exit(-1)
def download_url(url, dest_folder, filename=None):
# Ensure the destination folder exists
diff --git a/glob/git_utils.py b/comfyui_manager/glob/git_utils.py
similarity index 100%
rename from glob/git_utils.py
rename to comfyui_manager/glob/git_utils.py
diff --git a/glob/manager_core.py b/comfyui_manager/glob/manager_core.py
similarity index 98%
rename from glob/manager_core.py
rename to comfyui_manager/glob/manager_core.py
index e9af16bc4..a839e51ec 100644
--- a/glob/manager_core.py
+++ b/comfyui_manager/glob/manager_core.py
@@ -32,18 +32,15 @@
import uuid
-glob_path = os.path.join(os.path.dirname(__file__)) # ComfyUI-Manager/glob
-sys.path.append(glob_path)
-
-import cm_global
-import cnr_utils
-import manager_util
-import git_utils
-import manager_downloader
-from node_package import InstalledNodePackage
-
-
-version_code = [3, 31, 9]
+from . import cm_global
+from . import cnr_utils
+from . import manager_util
+from . import git_utils
+from . import manager_downloader
+from .node_package import InstalledNodePackage
+from .enums import NetworkMode, SecurityLevel, DBMode
+
+version_code = [4, 0]
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
@@ -58,6 +55,7 @@ def __init__(self, channel):
self.channel = channel
super().__init__(channel)
+
def get_default_custom_nodes_path():
global default_custom_nodes_path
if default_custom_nodes_path is None:
@@ -183,10 +181,11 @@ def check(root):
if comfy_path is None:
try:
- import folder_paths
- comfy_path = os.path.join(os.path.dirname(folder_paths.__file__))
+ comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
+ os.environ['COMFYUI_PATH'] = comfy_path
except:
- comfy_path = os.path.abspath(os.path.join(manager_util.comfyui_manager_path, '..', '..'))
+ logging.error("[ComfyUI-Manager] environment variable 'COMFYUI_PATH' is not specified.")
+ exit(-1)
if comfy_base_path is None:
comfy_base_path = comfy_path
@@ -553,7 +552,7 @@ def resolve_from_path(self, fullpath):
ver = str(manager_util.StrictVersion(info['version']))
return {'id': cnr['id'], 'cnr': cnr, 'ver': ver}
else:
- return None
+ return {'id': info['id'], 'ver': info['version']}
else:
return None
@@ -729,7 +728,7 @@ def get_from_cnr_inactive_nodes(self, node_id, ver=None):
return latest
- async def reload(self, cache_mode, dont_wait=True):
+ async def reload(self, cache_mode, dont_wait=True, update_cnr_map=True):
self.custom_node_map_cache = {}
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
self.nightly_inactive_nodes = {} # node_id -> fullpath
@@ -737,17 +736,18 @@ async def reload(self, cache_mode, dont_wait=True):
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
self.active_nodes = {} # node_id -> node_version * fullpath
- if get_config()['network_mode'] != 'public':
+ if get_config()['network_mode'] != 'public' or manager_util.is_manager_pip_package():
dont_wait = True
- # reload 'cnr_map' and 'repo_cnr_map'
- cnrs = await cnr_utils.get_cnr_data(cache_mode=cache_mode=='cache', dont_wait=dont_wait)
+ if update_cnr_map:
+ # reload 'cnr_map' and 'repo_cnr_map'
+ cnrs = await cnr_utils.get_cnr_data(cache_mode=cache_mode=='cache', dont_wait=dont_wait)
- for x in cnrs:
- self.cnr_map[x['id']] = x
- if 'repository' in x:
- normalized_url = git_utils.normalize_url(x['repository'])
- self.repo_cnr_map[normalized_url] = x
+ for x in cnrs:
+ self.cnr_map[x['id']] = x
+ if 'repository' in x:
+ normalized_url = git_utils.normalize_url(x['repository'])
+ self.repo_cnr_map[normalized_url] = x
# reload node status info from custom_nodes/*
for custom_nodes_path in folder_paths.get_folder_paths('custom_nodes'):
@@ -1442,12 +1442,20 @@ async def install_by_id(self, node_id: str, version_spec=None, channel=None, mod
return self.unified_enable(node_id, version_spec)
elif version_spec == 'unknown' or version_spec == 'nightly':
+ to_path = os.path.abspath(os.path.join(get_default_custom_nodes_path(), node_id))
+
if version_spec == 'nightly':
# disable cnr nodes
if self.is_enabled(node_id, 'cnr'):
self.unified_disable(node_id, False)
- to_path = os.path.abspath(os.path.join(get_default_custom_nodes_path(), node_id))
+ # use `repo name` as a dir name instead of `cnr id` if system added nodepack (i.e. publisher is null)
+ cnr = self.cnr_map.get(node_id)
+
+ if cnr is not None and cnr.get('publisher') is None:
+ repo_name = os.path.basename(git_utils.normalize_url(repo_url))
+ to_path = os.path.abspath(os.path.join(get_default_custom_nodes_path(), repo_name))
+
res = self.repo_install(repo_url, to_path, instant_execution=instant_execution, no_deps=no_deps, return_postinstall=return_postinstall)
if res.result:
if version_spec == 'unknown':
@@ -1664,9 +1672,9 @@ def get_bool(key, default_value):
'model_download_by_agent': get_bool('model_download_by_agent', False),
'downgrade_blacklist': default_conf.get('downgrade_blacklist', '').lower(),
'always_lazy_install': get_bool('always_lazy_install', False),
- 'network_mode': default_conf.get('network_mode', 'public').lower(),
- 'security_level': default_conf.get('security_level', 'normal').lower(),
- 'db_mode': default_conf.get('db_mode', 'cache').lower(),
+ 'network_mode': default_conf.get('network_mode', NetworkMode.PUBLIC.value).lower(),
+ 'security_level': default_conf.get('security_level', SecurityLevel.NORMAL.value).lower(),
+ 'db_mode': default_conf.get('db_mode', DBMode.CACHE.value).lower(),
}
except Exception:
@@ -1687,9 +1695,9 @@ def get_bool(key, default_value):
'model_download_by_agent': False,
'downgrade_blacklist': '',
'always_lazy_install': False,
- 'network_mode': 'public', # public | private | offline
- 'security_level': 'normal', # strong | normal | normal- | weak
- 'db_mode': 'cache', # local | cache | remote
+ 'network_mode': NetworkMode.OFFLINE.value,
+ 'security_level': SecurityLevel.NORMAL.value,
+ 'db_mode': DBMode.CACHE.value,
}
@@ -2186,7 +2194,7 @@ async def get_data_by_mode(mode, filename, channel_url=None):
cache_uri = str(manager_util.simple_hash(uri))+'_'+filename
cache_uri = os.path.join(manager_util.cache_dir, cache_uri)
- if get_config()['network_mode'] == 'offline':
+ if get_config()['network_mode'] == 'offline' or manager_util.is_manager_pip_package():
# offline network mode
if os.path.exists(cache_uri):
json_obj = await manager_util.get_data(cache_uri)
@@ -2206,7 +2214,7 @@ async def get_data_by_mode(mode, filename, channel_url=None):
with open(cache_uri, "w", encoding='utf-8') as file:
json.dump(json_obj, file, indent=4, sort_keys=True)
except Exception as e:
- print(f"[ComfyUI-Manager] Due to a network error, switching to local mode.\n=> {filename}\n=> {e}")
+ print(f"[ComfyUI-Manager] Due to a network error, switching to local mode.\n=> {filename} @ {channel_url}/{mode}\n=> {e}")
uri = os.path.join(manager_util.comfyui_manager_path, filename)
json_obj = await manager_util.get_data(uri)
diff --git a/glob/manager_downloader.py b/comfyui_manager/glob/manager_downloader.py
similarity index 100%
rename from glob/manager_downloader.py
rename to comfyui_manager/glob/manager_downloader.py
diff --git a/glob/manager_server.py b/comfyui_manager/glob/manager_server.py
similarity index 93%
rename from glob/manager_server.py
rename to comfyui_manager/glob/manager_server.py
index 979498325..b70f7bb54 100644
--- a/glob/manager_server.py
+++ b/comfyui_manager/glob/manager_server.py
@@ -14,18 +14,23 @@
from datetime import datetime
from server import PromptServer
-import manager_core as core
-import manager_util
-import cm_global
import logging
import asyncio
import queue
-import manager_downloader
+from . import manager_core as core
+from . import manager_util
+from . import cm_global
+from . import manager_downloader
logging.info(f"### Loading: ComfyUI-Manager ({core.version_str})")
-logging.info("[ComfyUI-Manager] network_mode: " + core.get_config()['network_mode'])
+
+if not manager_util.is_manager_pip_package():
+ network_mode_description = "offline"
+else:
+ network_mode_description = core.get_config()['network_mode']
+logging.info("[ComfyUI-Manager] network_mode: " + network_mode_description)
comfy_ui_hash = "-"
comfyui_tag = None
@@ -155,9 +160,7 @@ def run_script(self, cmd, cwd='.'):
core.manager_funcs = ManagerFuncsInComfyUI()
-sys.path.append('../..')
-
-from manager_downloader import download_url, download_url_with_agent
+from .manager_downloader import download_url, download_url_with_agent
core.comfy_path = os.path.dirname(folder_paths.__file__)
core.js_path = os.path.join(core.comfy_path, "web", "extensions")
@@ -659,7 +662,7 @@ async def do_install_model(item) -> str:
'total_count': total_count, 'done_count': done_count})
-@routes.get("/customnode/getmappings")
+@routes.get("/v2/customnode/getmappings")
async def fetch_customnode_mappings(request):
"""
provide unified (node -> node pack) mapping list
@@ -695,7 +698,7 @@ async def fetch_customnode_mappings(request):
return web.json_response(json_obj, content_type='application/json')
-@routes.get("/customnode/fetch_updates")
+@routes.get("/v2/customnode/fetch_updates")
async def fetch_updates(request):
try:
if request.rel_url.query["mode"] == "local":
@@ -722,7 +725,7 @@ async def fetch_updates(request):
return web.Response(status=400)
-@routes.get("/manager/queue/update_all")
+@routes.get("/v2/manager/queue/update_all")
async def update_all(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -810,7 +813,7 @@ def populate_markdown(x):
# freeze imported version
startup_time_installed_node_packs = core.get_installed_node_packs()
-@routes.get("/customnode/installed")
+@routes.get("/v2/customnode/installed")
async def installed_list(request):
mode = request.query.get('mode', 'default')
@@ -822,7 +825,7 @@ async def installed_list(request):
return web.json_response(res, content_type='application/json')
-@routes.get("/customnode/getlist")
+@routes.get("/v2/customnode/getlist")
async def fetch_customnode_list(request):
"""
provide unified custom node list
@@ -935,7 +938,7 @@ def process_model_phase(item):
executor.submit(process_model_phase, item)
-@routes.get("/externalmodel/getlist")
+@routes.get("/v2/externalmodel/getlist")
async def fetch_externalmodel_list(request):
# The model list is only allowed in the default channel, yet.
json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'model-list.json')
@@ -948,14 +951,14 @@ async def fetch_externalmodel_list(request):
return web.json_response(json_obj, content_type='application/json')
-@PromptServer.instance.routes.get("/snapshot/getlist")
+@PromptServer.instance.routes.get("/v2/snapshot/getlist")
async def get_snapshot_list(request):
items = [f[:-5] for f in os.listdir(core.manager_snapshot_path) if f.endswith('.json')]
items.sort(reverse=True)
return web.json_response({'items': items}, content_type='application/json')
-@routes.get("/snapshot/remove")
+@routes.get("/v2/snapshot/remove")
async def remove_snapshot(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -973,7 +976,7 @@ async def remove_snapshot(request):
return web.Response(status=400)
-@routes.get("/snapshot/restore")
+@routes.get("/v2/snapshot/restore")
async def restore_snapshot(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -999,7 +1002,7 @@ async def restore_snapshot(request):
return web.Response(status=400)
-@routes.get("/snapshot/get_current")
+@routes.get("/v2/snapshot/get_current")
async def get_current_snapshot_api(request):
try:
return web.json_response(await core.get_current_snapshot(), content_type='application/json')
@@ -1007,7 +1010,7 @@ async def get_current_snapshot_api(request):
return web.Response(status=400)
-@routes.get("/snapshot/save")
+@routes.get("/v2/snapshot/save")
async def save_snapshot(request):
try:
await core.save_snapshot_with_postfix('snapshot')
@@ -1044,82 +1047,7 @@ def unzip_install(files):
return True
-def copy_install(files, js_path_name=None):
- for url in files:
- if url.endswith("/"):
- url = url[:-1]
- try:
- filename = os.path.basename(url)
- if url.endswith(".py"):
- download_url(url, core.get_default_custom_nodes_path(), filename)
- else:
- path = os.path.join(core.js_path, js_path_name) if js_path_name is not None else core.js_path
- if not os.path.exists(path):
- os.makedirs(path)
- download_url(url, path, filename)
-
- except Exception as e:
- logging.error(f"Install(copy) error: {url} / {e}", file=sys.stderr)
- return False
-
- logging.info("Installation was successful.")
- return True
-
-
-def copy_uninstall(files, js_path_name='.'):
- for url in files:
- if url.endswith("/"):
- url = url[:-1]
- dir_name = os.path.basename(url)
- base_path = core.get_default_custom_nodes_path() if url.endswith('.py') else os.path.join(core.js_path, js_path_name)
- file_path = os.path.join(base_path, dir_name)
-
- try:
- if os.path.exists(file_path):
- os.remove(file_path)
- elif os.path.exists(file_path + ".disabled"):
- os.remove(file_path + ".disabled")
- except Exception as e:
- logging.error(f"Uninstall(copy) error: {url} / {e}", file=sys.stderr)
- return False
-
- logging.info("Uninstallation was successful.")
- return True
-
-
-def copy_set_active(files, is_disable, js_path_name='.'):
- if is_disable:
- action_name = "Disable"
- else:
- action_name = "Enable"
-
- for url in files:
- if url.endswith("/"):
- url = url[:-1]
- dir_name = os.path.basename(url)
- base_path = core.get_default_custom_nodes_path() if url.endswith('.py') else os.path.join(core.js_path, js_path_name)
- file_path = os.path.join(base_path, dir_name)
-
- try:
- if is_disable:
- current_name = file_path
- new_name = file_path + ".disabled"
- else:
- current_name = file_path + ".disabled"
- new_name = file_path
-
- os.rename(current_name, new_name)
-
- except Exception as e:
- logging.error(f"{action_name}(copy) error: {url} / {e}", file=sys.stderr)
-
- return False
-
- logging.info(f"{action_name} was successful.")
- return True
-
-
-@routes.get("/customnode/versions/{node_name}")
+@routes.get("/v2/customnode/versions/{node_name}")
async def get_cnr_versions(request):
node_name = request.match_info.get("node_name", None)
versions = core.cnr_utils.all_versions_of_node(node_name)
@@ -1130,7 +1058,7 @@ async def get_cnr_versions(request):
return web.Response(status=400)
-@routes.get("/customnode/disabled_versions/{node_name}")
+@routes.get("/v2/customnode/disabled_versions/{node_name}")
async def get_disabled_versions(request):
node_name = request.match_info.get("node_name", None)
versions = []
@@ -1146,7 +1074,7 @@ async def get_disabled_versions(request):
return web.Response(status=400)
-@routes.post("/customnode/import_fail_info")
+@routes.post("/v2/customnode/import_fail_info")
async def import_fail_info(request):
json_data = await request.json()
@@ -1163,20 +1091,20 @@ async def import_fail_info(request):
return web.Response(status=400)
-@routes.post("/manager/queue/reinstall")
+@routes.post("/v2/manager/queue/reinstall")
async def reinstall_custom_node(request):
await uninstall_custom_node(request)
await install_custom_node(request)
-@routes.get("/manager/queue/reset")
+@routes.get("/v2/manager/queue/reset")
async def reset_queue(request):
global task_queue
task_queue = queue.Queue()
return web.Response(status=200)
-@routes.get("/manager/queue/status")
+@routes.get("/v2/manager/queue/status")
async def queue_count(request):
global task_queue
@@ -1191,7 +1119,7 @@ async def queue_count(request):
'is_processing': is_processing})
-@routes.post("/manager/queue/install")
+@routes.post("/v2/manager/queue/install")
async def install_custom_node(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -1252,7 +1180,7 @@ async def install_custom_node(request):
task_worker_thread:threading.Thread = None
-@routes.get("/manager/queue/start")
+@routes.get("/v2/manager/queue/start")
async def queue_start(request):
global nodepack_result
global model_result
@@ -1270,7 +1198,7 @@ async def queue_start(request):
return web.Response(status=200)
-@routes.post("/manager/queue/fix")
+@routes.post("/v2/manager/queue/fix")
async def fix_custom_node(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_GENERAL)
@@ -1292,7 +1220,7 @@ async def fix_custom_node(request):
return web.Response(status=200)
-@routes.post("/customnode/install/git_url")
+@routes.post("/v2/customnode/install/git_url")
async def install_custom_node_git_url(request):
if not is_allowed_security_level('high'):
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
@@ -1312,7 +1240,7 @@ async def install_custom_node_git_url(request):
return web.Response(status=400)
-@routes.post("/customnode/install/pip")
+@routes.post("/v2/customnode/install/pip")
async def install_custom_node_pip(request):
if not is_allowed_security_level('high'):
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
@@ -1324,7 +1252,7 @@ async def install_custom_node_pip(request):
return web.Response(status=200)
-@routes.post("/manager/queue/uninstall")
+@routes.post("/v2/manager/queue/uninstall")
async def uninstall_custom_node(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -1347,7 +1275,7 @@ async def uninstall_custom_node(request):
return web.Response(status=200)
-@routes.post("/manager/queue/update")
+@routes.post("/v2/manager/queue/update")
async def update_custom_node(request):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -1368,14 +1296,14 @@ async def update_custom_node(request):
return web.Response(status=200)
-@routes.get("/manager/queue/update_comfyui")
+@routes.get("/v2/manager/queue/update_comfyui")
async def update_comfyui(request):
is_stable = core.get_config()['update_policy'] != 'nightly-comfyui'
task_queue.put(("update-comfyui", ('comfyui', is_stable)))
return web.Response(status=200)
-@routes.get("/comfyui_manager/comfyui_versions")
+@routes.get("/v2/comfyui_manager/comfyui_versions")
async def comfyui_versions(request):
try:
res, current, latest = core.get_comfyui_versions()
@@ -1386,7 +1314,7 @@ async def comfyui_versions(request):
return web.Response(status=400)
-@routes.get("/comfyui_manager/comfyui_switch_version")
+@routes.get("/v2/comfyui_manager/comfyui_switch_version")
async def comfyui_switch_version(request):
try:
if "ver" in request.rel_url.query:
@@ -1399,7 +1327,7 @@ async def comfyui_switch_version(request):
return web.Response(status=400)
-@routes.post("/manager/queue/disable")
+@routes.post("/v2/manager/queue/disable")
async def disable_node(request):
json_data = await request.json()
@@ -1434,7 +1362,7 @@ async def check_whitelist_for_model(item):
return False
-@routes.post("/manager/queue/install_model")
+@routes.post("/v2/manager/queue/install_model")
async def install_model(request):
json_data = await request.json()
@@ -1466,7 +1394,7 @@ async def install_model(request):
return web.Response(status=200)
-@routes.get("/manager/preview_method")
+@routes.get("/v2/manager/preview_method")
async def preview_method(request):
if "value" in request.rel_url.query:
set_preview_method(request.rel_url.query['value'])
@@ -1477,7 +1405,7 @@ async def preview_method(request):
return web.Response(status=200)
-@routes.get("/manager/db_mode")
+@routes.get("/v2/manager/db_mode")
async def db_mode(request):
if "value" in request.rel_url.query:
set_db_mode(request.rel_url.query['value'])
@@ -1489,7 +1417,7 @@ async def db_mode(request):
-@routes.get("/manager/policy/component")
+@routes.get("/v2/manager/policy/component")
async def component_policy(request):
if "value" in request.rel_url.query:
set_component_policy(request.rel_url.query['value'])
@@ -1500,7 +1428,7 @@ async def component_policy(request):
return web.Response(status=200)
-@routes.get("/manager/policy/update")
+@routes.get("/v2/manager/policy/update")
async def update_policy(request):
if "value" in request.rel_url.query:
set_update_policy(request.rel_url.query['value'])
@@ -1511,7 +1439,7 @@ async def update_policy(request):
return web.Response(status=200)
-@routes.get("/manager/channel_url_list")
+@routes.get("/v2/manager/channel_url_list")
async def channel_url_list(request):
channels = core.get_channel_dict()
if "value" in request.rel_url.query:
@@ -1548,7 +1476,7 @@ def add_target(match):
return modified_html
-@routes.get("/manager/notice")
+@routes.get("/v2/manager/notice")
async def get_notice(request):
url = "github.com"
path = "/ltdrdata/ltdrdata.github.io/wiki/News"
@@ -1595,7 +1523,13 @@ async def get_notice(request):
return web.Response(text="Unable to retrieve Notice", status=200)
-@routes.get("/manager/reboot")
+# legacy /manager/notice
+@routes.get("/manager/notice")
+async def get_notice_legacy(request):
+ return web.Response(text="""Starting from ComfyUI-Manager V4.0+, it should be installed via pip.
Please remove the ComfyUI-Manager installed in the 'custom_nodes' directory.""", status=200)
+
+
+@routes.get("/v2/manager/reboot")
def restart(self):
if not is_allowed_security_level('middle'):
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
@@ -1632,7 +1566,7 @@ def restart(self):
return os.execv(sys.executable, cmds)
-@routes.post("/manager/component/save")
+@routes.post("/v2/manager/component/save")
async def save_component(request):
try:
data = await request.json()
@@ -1662,7 +1596,7 @@ async def save_component(request):
return web.Response(status=400)
-@routes.post("/manager/component/loads")
+@routes.post("/v2/manager/component/loads")
async def load_components(request):
if os.path.exists(core.manager_components_path):
try:
@@ -1687,7 +1621,7 @@ async def load_components(request):
return web.json_response({})
-@routes.get("/manager/version")
+@routes.get("/v2/manager/version")
async def get_version(request):
return web.Response(text=core.version_str, status=200)
@@ -1736,7 +1670,7 @@ async def get_cache(filename):
logging.error(f"[ComfyUI-Manager] Failed to perform initial fetching '{filename}': {e}")
traceback.print_exc()
- if core.get_config()['network_mode'] != 'offline':
+ if core.get_config()['network_mode'] != 'offline' and not manager_util.is_manager_pip_package():
a = get_cache("custom-node-list.json")
b = get_cache("extension-node-map.json")
c = get_cache("model-list.json")
@@ -1751,6 +1685,8 @@ async def get_cache(filename):
# load at least once
await core.unified_manager.reload('remote', dont_wait=False)
await core.unified_manager.get_custom_nodes(channel_url, 'remote')
+ else:
+ await core.unified_manager.reload('remote', dont_wait=False, update_cnr_map=False)
logging.info("[ComfyUI-Manager] All startup tasks have been completed.")
@@ -1768,4 +1704,3 @@ async def get_cache(filename):
'nodes': {},
'description': 'This extension provides the ability to manage custom nodes in ComfyUI.', })
-
diff --git a/glob/manager_util.py b/comfyui_manager/glob/manager_util.py
similarity index 99%
rename from glob/manager_util.py
rename to comfyui_manager/glob/manager_util.py
index cb65cc5bc..ae63bdd86 100644
--- a/glob/manager_util.py
+++ b/comfyui_manager/glob/manager_util.py
@@ -18,12 +18,15 @@
cache_lock = threading.Lock()
+session_lock = threading.Lock()
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**.
use_uv = False
+def is_manager_pip_package():
+ return not os.path.exists(os.path.join(comfyui_manager_path, '..', 'custom_nodes'))
def add_python_path_to_env():
if platform.system() != "Windows":
diff --git a/glob/node_package.py b/comfyui_manager/glob/node_package.py
similarity index 97%
rename from glob/node_package.py
rename to comfyui_manager/glob/node_package.py
index d199fa308..6ec53a9ad 100644
--- a/glob/node_package.py
+++ b/comfyui_manager/glob/node_package.py
@@ -3,7 +3,7 @@
from dataclasses import dataclass
import os
-from git_utils import get_commit_hash
+from .git_utils import get_commit_hash
@dataclass
diff --git a/glob/security_check.py b/comfyui_manager/glob/security_check.py
similarity index 100%
rename from glob/security_check.py
rename to comfyui_manager/glob/security_check.py
diff --git a/glob/share_3rdparty.py b/comfyui_manager/glob/share_3rdparty.py
similarity index 95%
rename from glob/share_3rdparty.py
rename to comfyui_manager/glob/share_3rdparty.py
index c6cfcb1e7..e6d31ba44 100644
--- a/glob/share_3rdparty.py
+++ b/comfyui_manager/glob/share_3rdparty.py
@@ -1,5 +1,6 @@
import mimetypes
-import manager_core as core
+from . import manager_core as core
+
import os
from aiohttp import web
import aiohttp
@@ -53,7 +54,7 @@ def compute_sha256_checksum(filepath):
return sha256.hexdigest()
-@PromptServer.instance.routes.get("/manager/share_option")
+@PromptServer.instance.routes.get("/v2/manager/share_option")
async def share_option(request):
if "value" in request.rel_url.query:
core.get_config()['share_option'] = request.rel_url.query['value']
@@ -122,7 +123,7 @@ def set_youml_settings(settings):
f.write(settings)
-@PromptServer.instance.routes.get("/manager/get_openart_auth")
+@PromptServer.instance.routes.get("/v2/manager/get_openart_auth")
async def api_get_openart_auth(request):
# print("Getting stored Matrix credentials...")
openart_key = get_openart_auth()
@@ -131,7 +132,7 @@ async def api_get_openart_auth(request):
return web.json_response({"openart_key": openart_key})
-@PromptServer.instance.routes.post("/manager/set_openart_auth")
+@PromptServer.instance.routes.post("/v2/manager/set_openart_auth")
async def api_set_openart_auth(request):
json_data = await request.json()
openart_key = json_data['openart_key']
@@ -140,7 +141,7 @@ async def api_set_openart_auth(request):
return web.Response(status=200)
-@PromptServer.instance.routes.get("/manager/get_matrix_auth")
+@PromptServer.instance.routes.get("/v2/manager/get_matrix_auth")
async def api_get_matrix_auth(request):
# print("Getting stored Matrix credentials...")
matrix_auth = get_matrix_auth()
@@ -149,7 +150,7 @@ async def api_get_matrix_auth(request):
return web.json_response(matrix_auth)
-@PromptServer.instance.routes.get("/manager/youml/settings")
+@PromptServer.instance.routes.get("/v2/manager/youml/settings")
async def api_get_youml_settings(request):
youml_settings = get_youml_settings()
if not youml_settings:
@@ -157,14 +158,14 @@ async def api_get_youml_settings(request):
return web.json_response(json.loads(youml_settings))
-@PromptServer.instance.routes.post("/manager/youml/settings")
+@PromptServer.instance.routes.post("/v2/manager/youml/settings")
async def api_set_youml_settings(request):
json_data = await request.json()
set_youml_settings(json.dumps(json_data))
return web.Response(status=200)
-@PromptServer.instance.routes.get("/manager/get_comfyworkflows_auth")
+@PromptServer.instance.routes.get("/v2/manager/get_comfyworkflows_auth")
async def api_get_comfyworkflows_auth(request):
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
# in the same directory as the ComfyUI base folder
@@ -175,7 +176,7 @@ async def api_get_comfyworkflows_auth(request):
return web.json_response({"comfyworkflows_sharekey": comfyworkflows_auth})
-@PromptServer.instance.routes.post("/manager/set_esheep_workflow_and_images")
+@PromptServer.instance.routes.post("/v2/manager/set_esheep_workflow_and_images")
async def set_esheep_workflow_and_images(request):
json_data = await request.json()
with open(os.path.join(core.manager_files_path, "esheep_share_message.json"), "w", encoding='utf-8') as file:
@@ -183,7 +184,7 @@ async def set_esheep_workflow_and_images(request):
return web.Response(status=200)
-@PromptServer.instance.routes.get("/manager/get_esheep_workflow_and_images")
+@PromptServer.instance.routes.get("/v2/manager/get_esheep_workflow_and_images")
async def get_esheep_workflow_and_images(request):
with open(os.path.join(core.manager_files_path, "esheep_share_message.json"), 'r', encoding='utf-8') as file:
data = json.load(file)
@@ -211,7 +212,7 @@ def has_provided_comfyworkflows_auth(comfyworkflows_sharekey):
return comfyworkflows_sharekey.strip()
-@PromptServer.instance.routes.post("/manager/share")
+@PromptServer.instance.routes.post("/v2/manager/share")
async def share_art(request):
# get json data
json_data = await request.json()
diff --git a/js/cm-api.js b/comfyui_manager/js/cm-api.js
similarity index 90%
rename from js/cm-api.js
rename to comfyui_manager/js/cm-api.js
index dabc6f1dd..21e9f705e 100644
--- a/js/cm-api.js
+++ b/comfyui_manager/js/cm-api.js
@@ -25,7 +25,7 @@ async function tryInstallCustomNode(event) {
const res = await customConfirm(msg);
if(res) {
if(event.detail.target.installed == 'Disabled') {
- const response = await api.fetchApi(`/customnode/toggle_active`, {
+ const response = await api.fetchApi(`/v2/customnode/toggle_active`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event.detail.target)
@@ -35,7 +35,7 @@ async function tryInstallCustomNode(event) {
await sleep(300);
app.ui.dialog.show(`Installing... '${event.detail.target.title}'`);
- const response = await api.fetchApi(`/customnode/install`, {
+ const response = await api.fetchApi(`/v2/customnode/install`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(event.detail.target)
@@ -52,7 +52,7 @@ async function tryInstallCustomNode(event) {
}
}
- let response = await api.fetchApi("/manager/reboot");
+ let response = await api.fetchApi("/v2/manager/reboot");
if(response.status == 403) {
show_message('This action is not allowed with this security level configuration.');
return false;
diff --git a/js/comfyui-manager.js b/comfyui_manager/js/comfyui-manager.js
similarity index 95%
rename from js/comfyui-manager.js
rename to comfyui_manager/js/comfyui-manager.js
index 6fc504b1f..df7d19de1 100644
--- a/js/comfyui-manager.js
+++ b/comfyui_manager/js/comfyui-manager.js
@@ -189,8 +189,7 @@ docStyle.innerHTML = `
}
`;
-function is_legacy_front() {
- let compareVersion = '1.2.49';
+function isBeforeFrontendVersion(compareVersion) {
try {
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
if (typeof frontendVersion !== 'string') {
@@ -223,6 +222,9 @@ function is_legacy_front() {
}
}
+const is_legacy_front = () => isBeforeFrontendVersion('1.2.49');
+const isNewManagerUI = () => isBeforeFrontendVersion('1.16.4');
+
document.head.appendChild(docStyle);
var update_comfyui_button = null;
@@ -415,7 +417,7 @@ const style = `
`;
async function init_share_option() {
- api.fetchApi('/manager/share_option')
+ api.fetchApi('/v2/manager/share_option')
.then(response => response.text())
.then(data => {
share_option = data || 'all';
@@ -423,7 +425,7 @@ async function init_share_option() {
}
async function init_notice(notice) {
- api.fetchApi('/manager/notice')
+ api.fetchApi('/v2/manager/notice')
.then(response => response.text())
.then(data => {
notice.innerHTML = data;
@@ -476,12 +478,12 @@ async function updateComfyUI() {
set_inprogress_mode();
- const response = await api.fetchApi('/manager/queue/update_comfyui');
+ const response = await api.fetchApi('/v2/manager/queue/update_comfyui');
showTerminal();
is_updating = true;
- await api.fetchApi('/manager/queue/start');
+ await api.fetchApi('/v2/manager/queue/start');
}
function showVersionSelectorDialog(versions, current, onSelect) {
@@ -612,7 +614,7 @@ async function switchComfyUI() {
switch_comfyui_button.disabled = true;
switch_comfyui_button.style.backgroundColor = "gray";
- let res = await api.fetchApi(`/comfyui_manager/comfyui_versions`, { cache: "no-store" });
+ let res = await api.fetchApi(`/v2/comfyui_manager/comfyui_versions`, { cache: "no-store" });
switch_comfyui_button.disabled = false;
switch_comfyui_button.style.backgroundColor = "";
@@ -631,14 +633,14 @@ async function switchComfyUI() {
showVersionSelectorDialog(versions, obj.current, async (selected_version) => {
if(selected_version == 'nightly') {
update_policy_combo.value = 'nightly-comfyui';
- api.fetchApi('/manager/policy/update?value=nightly-comfyui');
+ api.fetchApi('/v2/manager/policy/update?value=nightly-comfyui');
}
else {
update_policy_combo.value = 'stable-comfyui';
- api.fetchApi('/manager/policy/update?value=stable-comfyui');
+ api.fetchApi('/v2/manager/policy/update?value=stable-comfyui');
}
- let response = await api.fetchApi(`/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
+ let response = await api.fetchApi(`/v2/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
if (response.status == 200) {
infoToast(`ComfyUI version is switched to ${selected_version}`);
}
@@ -775,17 +777,17 @@ async function updateAll(update_comfyui) {
if(update_comfyui) {
update_all_button.innerText = "Updating ComfyUI...";
- await api.fetchApi('/manager/queue/update_comfyui');
+ await api.fetchApi('/v2/manager/queue/update_comfyui');
}
- const response = await api.fetchApi(`/manager/queue/update_all?mode=${mode}`);
+ const response = await api.fetchApi(`/v2/manager/queue/update_all?mode=${mode}`);
if (response.status == 401) {
customAlert('Another task is already in progress. Please stop the ongoing task first.');
}
else if(response.status == 200) {
is_updating = true;
- await api.fetchApi('/manager/queue/start');
+ await api.fetchApi('/v2/manager/queue/start');
}
}
@@ -814,7 +816,7 @@ function restartOrStop() {
rebootAPI();
}
else {
- api.fetchApi('/manager/queue/reset');
+ api.fetchApi('/v2/manager/queue/reset');
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
}
@@ -962,12 +964,12 @@ class ManagerMenuDialog extends ComfyDialog {
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, []));
- api.fetchApi('/manager/db_mode')
+ api.fetchApi('/v2/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}`);
+ api.fetchApi(`/v2/manager/db_mode?value=${event.target.value}`);
});
// preview method
@@ -979,19 +981,19 @@ class ManagerMenuDialog extends ComfyDialog {
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Preview method: Latent2RGB (fast)' }, []));
preview_combo.appendChild($el('option', { value: 'none', text: 'Preview method: None (very fast)' }, []));
- api.fetchApi('/manager/preview_method')
+ api.fetchApi('/v2/manager/preview_method')
.then(response => response.text())
.then(data => { preview_combo.value = data; });
preview_combo.addEventListener('change', function (event) {
- api.fetchApi(`/manager/preview_method?value=${event.target.value}`);
+ api.fetchApi(`/v2/manager/preview_method?value=${event.target.value}`);
});
// channel
let channel_combo = document.createElement("select");
channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list.");
channel_combo.className = "cm-menu-combo";
- api.fetchApi('/manager/channel_url_list')
+ api.fetchApi('/v2/manager/channel_url_list')
.then(response => response.json())
.then(async data => {
try {
@@ -1004,7 +1006,7 @@ class ManagerMenuDialog extends ComfyDialog {
}
channel_combo.addEventListener('change', function (event) {
- api.fetchApi(`/manager/channel_url_list?value=${event.target.value}`);
+ api.fetchApi(`/v2/manager/channel_url_list?value=${event.target.value}`);
});
channel_combo.value = data.selected;
@@ -1032,7 +1034,7 @@ class ManagerMenuDialog extends ComfyDialog {
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
}
- api.fetchApi('/manager/share_option')
+ api.fetchApi('/v2/manager/share_option')
.then(response => response.text())
.then(data => {
share_combo.value = data || 'all';
@@ -1042,7 +1044,7 @@ class ManagerMenuDialog extends ComfyDialog {
share_combo.addEventListener('change', function (event) {
const value = event.target.value;
share_option = value;
- api.fetchApi(`/manager/share_option?value=${value}`);
+ api.fetchApi(`/v2/manager/share_option?value=${value}`);
const shareButton = document.getElementById("shareButton");
if (value === 'none') {
shareButton.style.display = "none";
@@ -1057,7 +1059,7 @@ class ManagerMenuDialog extends ComfyDialog {
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')
+ api.fetchApi('/v2/manager/policy/component')
.then(response => response.text())
.then(data => {
component_policy_combo.value = data;
@@ -1065,7 +1067,7 @@ class ManagerMenuDialog extends ComfyDialog {
});
component_policy_combo.addEventListener('change', function (event) {
- api.fetchApi(`/manager/policy/component?value=${event.target.value}`);
+ api.fetchApi(`/v2/manager/policy/component?value=${event.target.value}`);
set_component_policy(event.target.value);
});
@@ -1078,14 +1080,14 @@ class ManagerMenuDialog extends ComfyDialog {
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')
+ api.fetchApi('/v2/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}`);
+ api.fetchApi(`/v2/manager/policy/update?value=${event.target.value}`);
});
return [
@@ -1388,12 +1390,12 @@ class ManagerMenuDialog extends ComfyDialog {
}
async function getVersion() {
- let version = await api.fetchApi(`/manager/version`);
+ let version = await api.fetchApi(`/v2/manager/version`);
return await version.text();
}
app.registerExtension({
- name: "Comfy.ManagerMenu",
+ name: "Comfy.Legacy.ManagerMenu",
aboutPageBadges: [
{
@@ -1525,7 +1527,10 @@ app.registerExtension({
}).element
);
- app.menu?.settingsGroup.element.before(cmGroup.element);
+ const shouldShowLegacyMenuItems = !isNewManagerUI();
+ if (shouldShowLegacyMenuItems) {
+ app.menu?.settingsGroup.element.before(cmGroup.element);
+ }
}
catch(exception) {
console.log('ComfyUI is outdated. New style menu based features are disabled.');
diff --git a/js/comfyui-share-common.js b/comfyui_manager/js/comfyui-share-common.js
similarity index 99%
rename from js/comfyui-share-common.js
rename to comfyui_manager/js/comfyui-share-common.js
index e6f3e1039..9b570b8a0 100644
--- a/js/comfyui-share-common.js
+++ b/comfyui_manager/js/comfyui-share-common.js
@@ -172,7 +172,7 @@ export const shareToEsheep= () => {
const nodes = app.graph._nodes
const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
const workflow = prompt['workflow']
- api.fetchApi(`/manager/set_esheep_workflow_and_images`, {
+ api.fetchApi(`/v2/manager/set_esheep_workflow_and_images`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
@@ -812,7 +812,7 @@ export class ShareDialog extends ComfyDialog {
// get the user's existing matrix auth and share key
ShareDialog.matrix_auth = { homeserver: "matrix.org", username: "", password: "" };
try {
- api.fetchApi(`/manager/get_matrix_auth`)
+ api.fetchApi(`/v2/manager/get_matrix_auth`)
.then(response => response.json())
.then(data => {
ShareDialog.matrix_auth = data;
@@ -831,7 +831,7 @@ export class ShareDialog extends ComfyDialog {
ShareDialog.cw_sharekey = "";
try {
// console.log("Fetching comfyworkflows share key")
- api.fetchApi(`/manager/get_comfyworkflows_auth`)
+ api.fetchApi(`/v2/manager/get_comfyworkflows_auth`)
.then(response => response.json())
.then(data => {
ShareDialog.cw_sharekey = data.comfyworkflows_sharekey;
@@ -891,7 +891,7 @@ export class ShareDialog extends ComfyDialog {
// Change the text of the share button to "Sharing..." to indicate that the share process has started
this.share_button.textContent = "Sharing...";
- const response = await api.fetchApi(`/manager/share`, {
+ const response = await api.fetchApi(`/v2/manager/share`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
diff --git a/js/comfyui-share-copus.js b/comfyui_manager/js/comfyui-share-copus.js
similarity index 100%
rename from js/comfyui-share-copus.js
rename to comfyui_manager/js/comfyui-share-copus.js
diff --git a/js/comfyui-share-openart.js b/comfyui_manager/js/comfyui-share-openart.js
similarity index 98%
rename from js/comfyui-share-openart.js
rename to comfyui_manager/js/comfyui-share-openart.js
index 1c96a8c73..d867d4d79 100644
--- a/js/comfyui-share-openart.js
+++ b/comfyui_manager/js/comfyui-share-openart.js
@@ -67,7 +67,7 @@ export class OpenArtShareDialog extends ComfyDialog {
async readKey() {
let key = ""
try {
- key = await api.fetchApi(`/manager/get_openart_auth`)
+ key = await api.fetchApi(`/v2/manager/get_openart_auth`)
.then(response => response.json())
.then(data => {
return data.openart_key;
@@ -82,7 +82,7 @@ export class OpenArtShareDialog extends ComfyDialog {
}
async saveKey(value) {
- await api.fetchApi(`/manager/set_openart_auth`, {
+ await api.fetchApi(`/v2/manager/set_openart_auth`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
@@ -399,7 +399,7 @@ export class OpenArtShareDialog extends ComfyDialog {
form.append("file", uploadFile);
try {
const res = await this.fetchApi(
- `/workflows/upload_thumbnail`,
+ `/v2/workflows/upload_thumbnail`,
{
method: "POST",
body: form,
@@ -459,7 +459,7 @@ export class OpenArtShareDialog extends ComfyDialog {
throw new Error("Title is required");
}
- const current_snapshot = await api.fetchApi(`/snapshot/get_current`)
+ const current_snapshot = await api.fetchApi(`/v2/snapshot/get_current`)
.then(response => response.json())
.catch(error => {
// console.log(error);
@@ -489,7 +489,7 @@ export class OpenArtShareDialog extends ComfyDialog {
try {
const response = await this.fetchApi(
- "/workflows/publish",
+ "/v2/workflows/publish",
{
method: "POST",
headers: {"Content-Type": "application/json"},
diff --git a/js/comfyui-share-youml.js b/comfyui_manager/js/comfyui-share-youml.js
similarity index 98%
rename from js/comfyui-share-youml.js
rename to comfyui_manager/js/comfyui-share-youml.js
index efd8916f6..20dc8a2e4 100644
--- a/js/comfyui-share-youml.js
+++ b/comfyui_manager/js/comfyui-share-youml.js
@@ -179,7 +179,7 @@ export class YouMLShareDialog extends ComfyDialog {
async loadToken() {
let key = ""
try {
- const response = await api.fetchApi(`/manager/youml/settings`)
+ const response = await api.fetchApi(`/v2/manager/youml/settings`)
const settings = await response.json()
return settings.token
} catch (error) {
@@ -188,7 +188,7 @@ export class YouMLShareDialog extends ComfyDialog {
}
async saveToken(value) {
- await api.fetchApi(`/manager/youml/settings`, {
+ await api.fetchApi(`/v2/manager/youml/settings`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
@@ -380,7 +380,7 @@ export class YouMLShareDialog extends ComfyDialog {
try {
let snapshotData = null;
try {
- const snapshot = await api.fetchApi(`/snapshot/get_current`)
+ const snapshot = await api.fetchApi(`/v2/snapshot/get_current`)
snapshotData = await snapshot.json()
} catch (e) {
console.error("Failed to get snapshot", e)
diff --git a/js/common.js b/comfyui_manager/js/common.js
similarity index 99%
rename from js/common.js
rename to comfyui_manager/js/common.js
index 71cf58ea5..7617ab59a 100644
--- a/js/common.js
+++ b/comfyui_manager/js/common.js
@@ -172,7 +172,7 @@ export function rebootAPI() {
customConfirm("Are you sure you'd like to reboot the server?").then((isConfirmed) => {
if (isConfirmed) {
try {
- api.fetchApi("/manager/reboot");
+ api.fetchApi("/v2/manager/reboot");
}
catch(exception) {}
}
@@ -210,7 +210,7 @@ export async function install_pip(packages) {
if(packages.includes('&'))
app.ui.dialog.show(`Invalid PIP package enumeration: '${packages}'`);
- const res = await api.fetchApi("/customnode/install/pip", {
+ const res = await api.fetchApi("/v2/customnode/install/pip", {
method: "POST",
body: packages,
});
@@ -245,7 +245,7 @@ export async function install_via_git_url(url, manager_dialog) {
show_message(`Wait...
Installing '${url}'`);
- const res = await api.fetchApi("/customnode/install/git_url", {
+ const res = await api.fetchApi("/v2/customnode/install/git_url", {
method: "POST",
body: url,
});
diff --git a/js/components-manager.js b/comfyui_manager/js/components-manager.js
similarity index 98%
rename from js/components-manager.js
rename to comfyui_manager/js/components-manager.js
index 9244d2a48..551c11f50 100644
--- a/js/components-manager.js
+++ b/comfyui_manager/js/components-manager.js
@@ -64,7 +64,7 @@ function storeGroupNode(name, data, register=true) {
}
export async function load_components() {
- let data = await api.fetchApi('/manager/component/loads', {method: "POST"});
+ let data = await api.fetchApi('/v2/manager/component/loads', {method: "POST"});
let components = await data.json();
let start_time = Date.now();
@@ -222,7 +222,7 @@ async function save_as_component(node, version, author, prefix, nodename, packna
pack_map[packname] = component_name;
rpack_map[component_name] = subgraph;
- const res = await api.fetchApi('/manager/component/save', {
+ const res = await api.fetchApi('/v2/manager/component/save', {
method: "POST",
headers: {
"Content-Type": "application/json",
@@ -259,7 +259,7 @@ async function import_component(component_name, component, mode) {
workflow: component
};
- const res = await api.fetchApi('/manager/component/save', {
+ const res = await api.fetchApi('/v2/manager/component/save', {
method: "POST",
headers: { "Content-Type": "application/json", },
body: JSON.stringify(body)
@@ -709,7 +709,7 @@ app.handleFile = handleFile;
let current_component_policy = 'workflow';
try {
- api.fetchApi('/manager/policy/component')
+ api.fetchApi('/v2/manager/policy/component')
.then(response => response.text())
.then(data => { current_component_policy = data; });
}
diff --git a/js/custom-nodes-manager.css b/comfyui_manager/js/custom-nodes-manager.css
similarity index 100%
rename from js/custom-nodes-manager.css
rename to comfyui_manager/js/custom-nodes-manager.css
diff --git a/js/custom-nodes-manager.js b/comfyui_manager/js/custom-nodes-manager.js
similarity index 98%
rename from js/custom-nodes-manager.js
rename to comfyui_manager/js/custom-nodes-manager.js
index a42e018e6..7c11aa91e 100644
--- a/js/custom-nodes-manager.js
+++ b/comfyui_manager/js/custom-nodes-manager.js
@@ -66,7 +66,7 @@ export class CustomNodesManager {
this.id = "cn-manager";
app.registerExtension({
- name: "Comfy.CustomNodesManager",
+ name: "Comfy.Legacy.CustomNodesManager",
afterConfigureGraph: (missingNodeTypes) => {
const item = this.getFilterItem(ShowMode.MISSING);
if (item) {
@@ -459,7 +459,7 @@ export class CustomNodesManager {
".cn-manager-stop": {
click: () => {
- api.fetchApi('/manager/queue/reset');
+ api.fetchApi('/v2/manager/queue/reset');
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
},
@@ -635,7 +635,7 @@ export class CustomNodesManager {
};
}
- const response = await api.fetchApi(`/customnode/import_fail_info`, {
+ const response = await api.fetchApi(`/v2/customnode/import_fail_info`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(info)
@@ -1243,7 +1243,7 @@ export class CustomNodesManager {
async loadNodes(node_packs) {
const mode = manager_instance.datasrc_combo.value;
this.showStatus(`Loading node mappings (${mode}) ...`);
- const res = await fetchData(`/customnode/getmappings?mode=${mode}`);
+ const res = await fetchData(`/v2/customnode/getmappings?mode=${mode}`);
if (res.error) {
console.log(res.error);
return;
@@ -1395,10 +1395,10 @@ export class CustomNodesManager {
this.showLoading();
let res;
if(is_enable) {
- res = await api.fetchApi(`/customnode/disabled_versions/${node_id}`, { cache: "no-store" });
+ res = await api.fetchApi(`/v2/customnode/disabled_versions/${node_id}`, { cache: "no-store" });
}
else {
- res = await api.fetchApi(`/customnode/versions/${node_id}`, { cache: "no-store" });
+ res = await api.fetchApi(`/v2/customnode/versions/${node_id}`, { cache: "no-store" });
}
this.hideLoading();
@@ -1439,7 +1439,7 @@ export class CustomNodesManager {
}
async installNodes(list, btn, title, selected_version) {
- let stats = await api.fetchApi('/manager/queue/status');
+ let stats = await api.fetchApi('/v2/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})`);
@@ -1472,7 +1472,7 @@ export class CustomNodesManager {
let needRestart = false;
let errorMsg = "";
- await api.fetchApi('/manager/queue/reset');
+ await api.fetchApi('/v2/manager/queue/reset');
let target_items = [];
@@ -1517,7 +1517,7 @@ export class CustomNodesManager {
api_mode = 'reinstall';
}
- const res = await api.fetchApi(`/manager/queue/${api_mode}`, {
+ const res = await api.fetchApi(`/v2/manager/queue/${api_mode}`, {
method: 'POST',
body: JSON.stringify(data)
});
@@ -1550,7 +1550,7 @@ export class CustomNodesManager {
}
}
else {
- await api.fetchApi('/manager/queue/start');
+ await api.fetchApi('/v2/manager/queue/start');
this.showStop();
showTerminal();
}
@@ -1558,6 +1558,9 @@ export class CustomNodesManager {
async onQueueStatus(event) {
let self = CustomNodesManager.instance;
+ // If legacy manager front is not open, return early (using new manager front)
+ if (self.element?.style.display === 'none') return
+
if(event.detail.status == 'in_progress' && event.detail.ui_target == 'nodepack_manager') {
const hash = event.detail.target;
@@ -1744,7 +1747,7 @@ export class CustomNodesManager {
async getMissingNodesLegacy(hashMap, missing_nodes) {
const mode = manager_instance.datasrc_combo.value;
this.showStatus(`Loading missing nodes (${mode}) ...`);
- const res = await fetchData(`/customnode/getmappings?mode=${mode}`);
+ const res = await fetchData(`/v2/customnode/getmappings?mode=${mode}`);
if (res.error) {
this.showError(`Failed to get custom node mappings: ${res.error}`);
return;
@@ -1859,7 +1862,7 @@ export class CustomNodesManager {
async getAlternatives() {
const mode = manager_instance.datasrc_combo.value;
this.showStatus(`Loading alternatives (${mode}) ...`);
- const res = await fetchData(`/customnode/alternatives?mode=${mode}`);
+ const res = await fetchData(`/v2/customnode/alternatives?mode=${mode}`);
if (res.error) {
this.showError(`Failed to get alternatives: ${res.error}`);
return [];
@@ -1907,7 +1910,7 @@ export class CustomNodesManager {
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(`/v2/customnode/getlist?mode=${mode}${skip_update}`);
if (res.error) {
this.showError("Failed to get custom node list.");
this.hideLoading();
diff --git a/js/model-manager.css b/comfyui_manager/js/model-manager.css
similarity index 100%
rename from js/model-manager.css
rename to comfyui_manager/js/model-manager.css
diff --git a/js/model-manager.js b/comfyui_manager/js/model-manager.js
similarity index 98%
rename from js/model-manager.js
rename to comfyui_manager/js/model-manager.js
index d744a34df..f7586f1e5 100644
--- a/js/model-manager.js
+++ b/comfyui_manager/js/model-manager.js
@@ -172,7 +172,7 @@ export class ModelManager {
".cmm-manager-stop": {
click: () => {
- api.fetchApi('/manager/queue/reset');
+ api.fetchApi('/v2/manager/queue/reset');
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
}
},
@@ -413,7 +413,7 @@ export class ModelManager {
}
async installModels(list, btn) {
- let stats = await api.fetchApi('/manager/queue/status');
+ let stats = await api.fetchApi('/v2/manager/queue/status');
stats = await stats.json();
if(stats.is_processing) {
@@ -427,7 +427,7 @@ export class ModelManager {
let needRefresh = false;
let errorMsg = "";
- await api.fetchApi('/manager/queue/reset');
+ await api.fetchApi('/v2/manager/queue/reset');
let target_items = [];
@@ -446,7 +446,7 @@ export class ModelManager {
const data = item.originalData;
data.ui_id = item.hash;
- const res = await api.fetchApi(`/manager/queue/install_model`, {
+ const res = await api.fetchApi(`/v2/manager/queue/install_model`, {
method: 'POST',
body: JSON.stringify(data)
});
@@ -477,7 +477,7 @@ export class ModelManager {
}
}
else {
- await api.fetchApi('/manager/queue/start');
+ await api.fetchApi('/v2/manager/queue/start');
this.showStop();
showTerminal();
}
@@ -623,7 +623,7 @@ export class ModelManager {
const mode = manager_instance.datasrc_combo.value;
- const res = await fetchData(`/externalmodel/getlist?mode=${mode}`);
+ const res = await fetchData(`/v2/externalmodel/getlist?mode=${mode}`);
if (res.error) {
this.showError("Failed to get external model list.");
this.hideLoading();
diff --git a/js/node_fixer.js b/comfyui_manager/js/node_fixer.js
similarity index 98%
rename from js/node_fixer.js
rename to comfyui_manager/js/node_fixer.js
index feec1cad5..e7ae2e3d8 100644
--- a/js/node_fixer.js
+++ b/comfyui_manager/js/node_fixer.js
@@ -142,7 +142,7 @@ function node_info_copy(src, dest, connect_both, copy_shape) {
}
app.registerExtension({
- name: "Comfy.Manager.NodeFixer",
+ name: "Comfy.Legacy.Manager.NodeFixer",
beforeRegisterNodeDef(nodeType, nodeData, app) {
addMenuHandler(nodeType, function (_, options) {
options.push({
diff --git a/js/popover-helper.js b/comfyui_manager/js/popover-helper.js
similarity index 100%
rename from js/popover-helper.js
rename to comfyui_manager/js/popover-helper.js
diff --git a/js/snapshot.js b/comfyui_manager/js/snapshot.js
similarity index 95%
rename from js/snapshot.js
rename to comfyui_manager/js/snapshot.js
index 520ca6150..9a5835f2d 100644
--- a/js/snapshot.js
+++ b/comfyui_manager/js/snapshot.js
@@ -7,7 +7,7 @@ import { manager_instance, rebootAPI, show_message } from "./common.js";
async function restore_snapshot(target) {
if(SnapshotManager.instance) {
try {
- const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" });
+ const response = await api.fetchApi(`/v2/snapshot/restore?target=${target}`, { cache: "no-store" });
if(response.status == 403) {
show_message('This action is not allowed with this security level configuration.');
@@ -35,7 +35,7 @@ async function restore_snapshot(target) {
async function remove_snapshot(target) {
if(SnapshotManager.instance) {
try {
- const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" });
+ const response = await api.fetchApi(`/v2/snapshot/remove?target=${target}`, { cache: "no-store" });
if(response.status == 403) {
show_message('This action is not allowed with this security level configuration.');
@@ -61,7 +61,7 @@ async function remove_snapshot(target) {
async function save_current_snapshot() {
try {
- const response = await api.fetchApi('/snapshot/save', { cache: "no-store" });
+ const response = await api.fetchApi('/v2/snapshot/save', { cache: "no-store" });
app.ui.dialog.close();
return true;
}
@@ -76,7 +76,7 @@ async function save_current_snapshot() {
}
async function getSnapshotList() {
- const response = await api.fetchApi(`/snapshot/getlist`);
+ const response = await api.fetchApi(`/v2/snapshot/getlist`);
const data = await response.json();
return data;
}
diff --git a/js/turbogrid.esm.js b/comfyui_manager/js/turbogrid.esm.js
similarity index 100%
rename from js/turbogrid.esm.js
rename to comfyui_manager/js/turbogrid.esm.js
diff --git a/js/workflow-metadata.js b/comfyui_manager/js/workflow-metadata.js
similarity index 97%
rename from js/workflow-metadata.js
rename to comfyui_manager/js/workflow-metadata.js
index 9bbf690b6..0f650df72 100644
--- a/js/workflow-metadata.js
+++ b/comfyui_manager/js/workflow-metadata.js
@@ -38,7 +38,7 @@ class WorkflowMetadataExtension {
* enabled is true if the node is enabled, false if it is disabled
*/
async getInstalledNodes() {
- const res = await api.fetchApi("/customnode/installed");
+ const res = await api.fetchApi("/v2/customnode/installed");
return await res.json();
}
diff --git a/prestartup_script.py b/comfyui_manager/prestartup_script.py
similarity index 97%
rename from prestartup_script.py
rename to comfyui_manager/prestartup_script.py
index e90f5d9d1..2e29ed65e 100644
--- a/prestartup_script.py
+++ b/comfyui_manager/prestartup_script.py
@@ -12,13 +12,10 @@
import logging
import traceback
-glob_path = os.path.join(os.path.dirname(__file__), "glob")
-sys.path.append(glob_path)
-
-import security_check
-import manager_util
-import cm_global
-import manager_downloader
+from .glob import security_check
+from .glob import manager_util
+from .glob import cm_global
+from .glob import manager_downloader
import folder_paths
manager_util.add_python_path_to_env()
@@ -68,15 +65,17 @@ def is_import_failed_extension(name):
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
if comfy_path is None:
- # legacy env var
- comfy_path = os.environ.get('COMFYUI_PATH')
-
-if comfy_path is None:
- comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
+ try:
+ comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
+ os.environ['COMFYUI_PATH'] = comfy_path
+ except:
+ print("[ComfyUI-Manager] environment variable 'COMFYUI_PATH' is not specified.")
+ exit(-1)
if comfy_base_path is None:
comfy_base_path = comfy_path
+
sys.__comfyui_manager_register_message_collapse = register_message_collapse
sys.__comfyui_manager_is_import_failed_extension = is_import_failed_extension
cm_global.register_api('cm.register_message_collapse', register_message_collapse)
@@ -121,12 +120,11 @@ def check_file_logging():
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'}
if os.path.exists(manager_pip_overrides_path):
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
cm_global.pip_overrides = json.load(json_file)
cm_global.pip_overrides['numpy'] = 'numpy<2'
- cm_global.pip_overrides['ultralytics'] = 'ultralytics==8.3.40' # for security
if os.path.exists(manager_pip_blacklist_path):
@@ -393,7 +391,11 @@ class LoggingHandler(logging.Handler):
def emit(self, record):
global is_start_mode
- message = record.getMessage()
+ try:
+ message = record.getMessage()
+ except Exception as e:
+ message = f"<>: {record} - {e}"
+ original_stderr.write(message)
if is_start_mode:
match = re.search(pat_import_fail, message)
diff --git a/notebooks/comfyui_colab_with_manager.ipynb b/notebooks/comfyui_colab_with_manager.ipynb
deleted file mode 100644
index 3cfa484b9..000000000
--- a/notebooks/comfyui_colab_with_manager.ipynb
+++ /dev/null
@@ -1,373 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "aaaaaaaaaa"
- },
- "source": [
- "Git clone the repo and install the requirements. (ignore the pip errors about protobuf)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "bbbbbbbbbb"
- },
- "outputs": [],
- "source": [
- "# #@title Environment Setup\n",
- "\n",
- "from pathlib import Path\n",
- "\n",
- "OPTIONS = {}\n",
- "\n",
- "USE_GOOGLE_DRIVE = True #@param {type:\"boolean\"}\n",
- "UPDATE_COMFY_UI = True #@param {type:\"boolean\"}\n",
- "USE_COMFYUI_MANAGER = True #@param {type:\"boolean\"}\n",
- "INSTALL_CUSTOM_NODES_DEPENDENCIES = True #@param {type:\"boolean\"}\n",
- "OPTIONS['USE_GOOGLE_DRIVE'] = USE_GOOGLE_DRIVE\n",
- "OPTIONS['UPDATE_COMFY_UI'] = UPDATE_COMFY_UI\n",
- "OPTIONS['USE_COMFYUI_MANAGER'] = USE_COMFYUI_MANAGER\n",
- "OPTIONS['INSTALL_CUSTOM_NODES_DEPENDENCIES'] = INSTALL_CUSTOM_NODES_DEPENDENCIES\n",
- "\n",
- "current_dir = !pwd\n",
- "WORKSPACE = f\"{current_dir[0]}/ComfyUI\"\n",
- "\n",
- "if OPTIONS['USE_GOOGLE_DRIVE']:\n",
- " !echo \"Mounting Google Drive...\"\n",
- " %cd /\n",
- "\n",
- " from google.colab import drive\n",
- " drive.mount('/content/drive')\n",
- "\n",
- " WORKSPACE = \"/content/drive/MyDrive/ComfyUI\"\n",
- " %cd /content/drive/MyDrive\n",
- "\n",
- "![ ! -d $WORKSPACE ] && echo -= Initial setup ComfyUI =- && git clone https://github.com/comfyanonymous/ComfyUI\n",
- "%cd $WORKSPACE\n",
- "\n",
- "if OPTIONS['UPDATE_COMFY_UI']:\n",
- " !echo -= Updating ComfyUI =-\n",
- "\n",
- " # Correction of the issue of permissions being deleted on Google Drive.\n",
- " ![ -f \".ci/nightly/update_windows/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/nightly/update_windows/update_comfyui_and_python_dependencies.bat\n",
- " ![ -f \".ci/nightly/windows_base_files/run_nvidia_gpu.bat\" ] && chmod 755 .ci/nightly/windows_base_files/run_nvidia_gpu.bat\n",
- " ![ -f \".ci/update_windows/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/update_windows/update_comfyui_and_python_dependencies.bat\n",
- " ![ -f \".ci/update_windows_cu118/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/update_windows_cu118/update_comfyui_and_python_dependencies.bat\n",
- " ![ -f \".ci/update_windows/update.py\" ] && chmod 755 .ci/update_windows/update.py\n",
- " ![ -f \".ci/update_windows/update_comfyui.bat\" ] && chmod 755 .ci/update_windows/update_comfyui.bat\n",
- " ![ -f \".ci/update_windows/README_VERY_IMPORTANT.txt\" ] && chmod 755 .ci/update_windows/README_VERY_IMPORTANT.txt\n",
- " ![ -f \".ci/update_windows/run_cpu.bat\" ] && chmod 755 .ci/update_windows/run_cpu.bat\n",
- " ![ -f \".ci/update_windows/run_nvidia_gpu.bat\" ] && chmod 755 .ci/update_windows/run_nvidia_gpu.bat\n",
- "\n",
- " !git pull\n",
- "\n",
- "!echo -= Install dependencies =-\n",
- "!pip3 install accelerate\n",
- "!pip3 install einops transformers>=4.28.1 safetensors>=0.4.2 aiohttp pyyaml Pillow scipy tqdm psutil tokenizers>=0.13.3\n",
- "!pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121\n",
- "!pip3 install torchsde\n",
- "!pip3 install kornia>=0.7.1 spandrel soundfile sentencepiece\n",
- "\n",
- "if OPTIONS['USE_COMFYUI_MANAGER']:\n",
- " %cd custom_nodes\n",
- "\n",
- " # Correction of the issue of permissions being deleted on Google Drive.\n",
- " ![ -f \"ComfyUI-Manager/check.sh\" ] && chmod 755 ComfyUI-Manager/check.sh\n",
- " ![ -f \"ComfyUI-Manager/scan.sh\" ] && chmod 755 ComfyUI-Manager/scan.sh\n",
- " ![ -f \"ComfyUI-Manager/node_db/dev/scan.sh\" ] && chmod 755 ComfyUI-Manager/node_db/dev/scan.sh\n",
- " ![ -f \"ComfyUI-Manager/node_db/tutorial/scan.sh\" ] && chmod 755 ComfyUI-Manager/node_db/tutorial/scan.sh\n",
- " ![ -f \"ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh\" ] && chmod 755 ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh\n",
- " ![ -f \"ComfyUI-Manager/scripts/install-comfyui-venv-win.bat\" ] && chmod 755 ComfyUI-Manager/scripts/install-comfyui-venv-win.bat\n",
- "\n",
- " ![ ! -d ComfyUI-Manager ] && echo -= Initial setup ComfyUI-Manager =- && git clone https://github.com/ltdrdata/ComfyUI-Manager\n",
- " %cd ComfyUI-Manager\n",
- " !git pull\n",
- "\n",
- "%cd $WORKSPACE\n",
- "\n",
- "if OPTIONS['INSTALL_CUSTOM_NODES_DEPENDENCIES']:\n",
- " !echo -= Install custom nodes dependencies =-\n",
- " !pip install GitPython\n",
- " !python custom_nodes/ComfyUI-Manager/cm-cli.py restore-dependencies\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "cccccccccc"
- },
- "source": [
- "Download some models/checkpoints/vae or custom comfyui nodes (uncomment the commands for the ones you want)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "dddddddddd"
- },
- "outputs": [],
- "source": [
- "# Checkpoints\n",
- "\n",
- "### SDXL\n",
- "### I recommend these workflow examples: https://comfyanonymous.github.io/ComfyUI_examples/sdxl/\n",
- "\n",
- "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors -P ./models/checkpoints/\n",
- "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0/resolve/main/sd_xl_refiner_1.0.safetensors -P ./models/checkpoints/\n",
- "\n",
- "# SDXL ReVision\n",
- "#!wget -c https://huggingface.co/comfyanonymous/clip_vision_g/resolve/main/clip_vision_g.safetensors -P ./models/clip_vision/\n",
- "\n",
- "# SD1.5\n",
- "!wget -c https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.ckpt -P ./models/checkpoints/\n",
- "\n",
- "# SD2\n",
- "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-2-1-base/resolve/main/v2-1_512-ema-pruned.safetensors -P ./models/checkpoints/\n",
- "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/v2-1_768-ema-pruned.safetensors -P ./models/checkpoints/\n",
- "\n",
- "# Some SD1.5 anime style\n",
- "#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix2/AbyssOrangeMix2_hard.safetensors -P ./models/checkpoints/\n",
- "#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix3/AOM3A1_orangemixs.safetensors -P ./models/checkpoints/\n",
- "#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix3/AOM3A3_orangemixs.safetensors -P ./models/checkpoints/\n",
- "#!wget -c https://huggingface.co/Linaqruf/anything-v3.0/resolve/main/anything-v3-fp16-pruned.safetensors -P ./models/checkpoints/\n",
- "\n",
- "# Waifu Diffusion 1.5 (anime style SD2.x 768-v)\n",
- "#!wget -c https://huggingface.co/waifu-diffusion/wd-1-5-beta3/resolve/main/wd-illusion-fp16.safetensors -P ./models/checkpoints/\n",
- "\n",
- "\n",
- "# unCLIP models\n",
- "#!wget -c https://huggingface.co/comfyanonymous/illuminatiDiffusionV1_v11_unCLIP/resolve/main/illuminatiDiffusionV1_v11-unclip-h-fp16.safetensors -P ./models/checkpoints/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/wd-1.5-beta2_unCLIP/resolve/main/wd-1-5-beta2-aesthetic-unclip-h-fp16.safetensors -P ./models/checkpoints/\n",
- "\n",
- "\n",
- "# VAE\n",
- "!wget -c https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors -P ./models/vae/\n",
- "#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt -P ./models/vae/\n",
- "#!wget -c https://huggingface.co/hakurei/waifu-diffusion-v1-4/resolve/main/vae/kl-f8-anime2.ckpt -P ./models/vae/\n",
- "\n",
- "\n",
- "# Loras\n",
- "#!wget -c https://civitai.com/api/download/models/10350 -O ./models/loras/theovercomer8sContrastFix_sd21768.safetensors #theovercomer8sContrastFix SD2.x 768-v\n",
- "#!wget -c https://civitai.com/api/download/models/10638 -O ./models/loras/theovercomer8sContrastFix_sd15.safetensors #theovercomer8sContrastFix SD1.x\n",
- "#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_offset_example-lora_1.0.safetensors -P ./models/loras/ #SDXL offset noise lora\n",
- "\n",
- "\n",
- "# T2I-Adapter\n",
- "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_depth_sd14v1.pth -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_seg_sd14v1.pth -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_sketch_sd14v1.pth -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_keypose_sd14v1.pth -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_openpose_sd14v1.pth -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_color_sd14v1.pth -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_canny_sd14v1.pth -P ./models/controlnet/\n",
- "\n",
- "# T2I Styles Model\n",
- "#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_style_sd14v1.pth -P ./models/style_models/\n",
- "\n",
- "# CLIPVision model (needed for styles model)\n",
- "#!wget -c https://huggingface.co/openai/clip-vit-large-patch14/resolve/main/pytorch_model.bin -O ./models/clip_vision/clip_vit14.bin\n",
- "\n",
- "\n",
- "# ControlNet\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11e_sd15_ip2p_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11e_sd15_shuffle_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_canny_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11f1p_sd15_depth_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_inpaint_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_lineart_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_mlsd_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_normalbae_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_openpose_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_scribble_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_seg_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_softedge_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15s2_lineart_anime_fp16.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11u_sd15_tile_fp16.safetensors -P ./models/controlnet/\n",
- "\n",
- "# ControlNet SDXL\n",
- "#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-canny-rank256.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-depth-rank256.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-recolor-rank256.safetensors -P ./models/controlnet/\n",
- "#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-sketch-rank256.safetensors -P ./models/controlnet/\n",
- "\n",
- "# Controlnet Preprocessor nodes by Fannovel16\n",
- "#!cd custom_nodes && git clone https://github.com/Fannovel16/comfy_controlnet_preprocessors; cd comfy_controlnet_preprocessors && python install.py\n",
- "\n",
- "\n",
- "# GLIGEN\n",
- "#!wget -c https://huggingface.co/comfyanonymous/GLIGEN_pruned_safetensors/resolve/main/gligen_sd14_textbox_pruned_fp16.safetensors -P ./models/gligen/\n",
- "\n",
- "\n",
- "# ESRGAN upscale model\n",
- "#!wget -c https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth -P ./models/upscale_models/\n",
- "#!wget -c https://huggingface.co/sberbank-ai/Real-ESRGAN/resolve/main/RealESRGAN_x2.pth -P ./models/upscale_models/\n",
- "#!wget -c https://huggingface.co/sberbank-ai/Real-ESRGAN/resolve/main/RealESRGAN_x4.pth -P ./models/upscale_models/\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "kkkkkkkkkkkkkkk"
- },
- "source": [
- "### Run ComfyUI with cloudflared (Recommended Way)\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "jjjjjjjjjjjjjj"
- },
- "outputs": [],
- "source": [
- "!wget -P ~ https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n",
- "!dpkg -i ~/cloudflared-linux-amd64.deb\n",
- "\n",
- "import subprocess\n",
- "import threading\n",
- "import time\n",
- "import socket\n",
- "import urllib.request\n",
- "\n",
- "def iframe_thread(port):\n",
- " while True:\n",
- " time.sleep(0.5)\n",
- " sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n",
- " result = sock.connect_ex(('127.0.0.1', port))\n",
- " if result == 0:\n",
- " break\n",
- " sock.close()\n",
- " print(\"\\nComfyUI finished loading, trying to launch cloudflared (if it gets stuck here cloudflared is having issues)\\n\")\n",
- "\n",
- " p = subprocess.Popen([\"cloudflared\", \"tunnel\", \"--url\", \"http://127.0.0.1:{}\".format(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
- " for line in p.stderr:\n",
- " l = line.decode()\n",
- " if \"trycloudflare.com \" in l:\n",
- " print(\"This is the URL to access ComfyUI:\", l[l.find(\"http\"):], end='')\n",
- " #print(l, end='')\n",
- "\n",
- "\n",
- "threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n",
- "\n",
- "!python main.py --dont-print-server"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "kkkkkkkkkkkkkk"
- },
- "source": [
- "### Run ComfyUI with localtunnel\n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "jjjjjjjjjjjjj"
- },
- "outputs": [],
- "source": [
- "!npm install -g localtunnel\n",
- "\n",
- "import subprocess\n",
- "import threading\n",
- "import time\n",
- "import socket\n",
- "import urllib.request\n",
- "\n",
- "def iframe_thread(port):\n",
- " while True:\n",
- " time.sleep(0.5)\n",
- " sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n",
- " result = sock.connect_ex(('127.0.0.1', port))\n",
- " if result == 0:\n",
- " break\n",
- " sock.close()\n",
- " print(\"\\nComfyUI finished loading, trying to launch localtunnel (if it gets stuck here localtunnel is having issues)\\n\")\n",
- "\n",
- " print(\"The password/enpoint ip for localtunnel is:\", urllib.request.urlopen('https://ipv4.icanhazip.com').read().decode('utf8').strip(\"\\n\"))\n",
- " p = subprocess.Popen([\"lt\", \"--port\", \"{}\".format(port)], stdout=subprocess.PIPE)\n",
- " for line in p.stdout:\n",
- " print(line.decode(), end='')\n",
- "\n",
- "\n",
- "threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n",
- "\n",
- "!python main.py --dont-print-server"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "id": "gggggggggg"
- },
- "source": [
- "### Run ComfyUI with colab iframe (use only in case the previous way with localtunnel doesn't work)\n",
- "\n",
- "You should see the ui appear in an iframe. If you get a 403 error, it's your firefox settings or an extension that's messing things up.\n",
- "\n",
- "If you want to open it in another window use the link.\n",
- "\n",
- "Note that some UI features like live image previews won't work because the colab iframe blocks websockets."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "id": "hhhhhhhhhh"
- },
- "outputs": [],
- "source": [
- "import threading\n",
- "import time\n",
- "import socket\n",
- "def iframe_thread(port):\n",
- " while True:\n",
- " time.sleep(0.5)\n",
- " sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n",
- " result = sock.connect_ex(('127.0.0.1', port))\n",
- " if result == 0:\n",
- " break\n",
- " sock.close()\n",
- " from google.colab import output\n",
- " output.serve_kernel_port_as_iframe(port, height=1024)\n",
- " print(\"to open it in a window you can open this link here:\")\n",
- " output.serve_kernel_port_as_window(port)\n",
- "\n",
- "threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n",
- "\n",
- "!python main.py --dont-print-server"
- ]
- }
- ],
- "metadata": {
- "accelerator": "GPU",
- "colab": {
- "provenance": []
- },
- "gpuClass": "standard",
- "kernelspec": {
- "display_name": "Python 3",
- "name": "python3"
- },
- "language_info": {
- "name": "python"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/pyproject.toml b/pyproject.toml
index 96948d17f..f970c4d93 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,15 +1,62 @@
+[build-system]
+requires = ["setuptools >= 61.0"]
+build-backend = "setuptools.build_meta"
+
[project]
name = "comfyui-manager"
+license = { text = "GPL-3.0-only" }
+version = "4.0"
+requires-python = ">= 3.9"
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
-version = "3.31.9"
-license = { file = "LICENSE.txt" }
-dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
+readme = "README.md"
+keywords = ["comfyui", "comfyui-manager"]
+
+maintainers = [
+ { name = "Dr.Lt.Data", email = "dr.lt.data@gmail.com" },
+ { name = "Yoland Yan", email = "yoland@drip.art" },
+ { name = "James Kwon", email = "hongilkwon316@gmail.com" },
+ { name = "Robin Huang", email = "robin@drip.art" },
+]
+
+classifiers = [
+ "Development Status :: 4 - Beta",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
+]
+
+dependencies = [
+ "GitPython",
+ "PyGithub",
+ "matrix-client==0.4.0",
+ "transformers",
+ "huggingface-hub>0.20",
+ "typer",
+ "rich",
+ "typing-extensions",
+ "toml",
+ "uv",
+ "chardet"
+]
+
+[project.optional-dependencies]
+dev = ["pre-commit", "pytest", "ruff", "pytest-cov"]
[project.urls]
Repository = "https://github.com/ltdrdata/ComfyUI-Manager"
-# Used by Comfy Registry https://comfyregistry.org
-[tool.comfy]
-PublisherId = "drltdata"
-DisplayName = "ComfyUI-Manager"
-Icon = ""
+[tool.setuptools.packages.find]
+where = ["."]
+include = ["comfyui_manager*"]
+
+[tool.ruff]
+line-length = 120
+target-version = "py39"
+
+[tool.ruff.lint]
+select = [
+ "E4", # default
+ "E7", # default
+ "E9", # default
+ "F", # default
+ "I", # isort-like behavior (import statement sorting)
+]
diff --git a/scripts/colab-dependencies.py b/scripts/colab-dependencies.py
deleted file mode 100644
index d5a70ed6d..000000000
--- a/scripts/colab-dependencies.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import os
-import subprocess
-
-
-def get_enabled_subdirectories_with_files(base_directory):
- subdirs_with_files = []
- for subdir in os.listdir(base_directory):
- try:
- full_path = os.path.join(base_directory, subdir)
- if os.path.isdir(full_path) and not subdir.endswith(".disabled") and not subdir.startswith('.') and subdir != '__pycache__':
- print(f"## Install dependencies for '{subdir}'")
- requirements_file = os.path.join(full_path, "requirements.txt")
- install_script = os.path.join(full_path, "install.py")
-
- if os.path.exists(requirements_file) or os.path.exists(install_script):
- subdirs_with_files.append((full_path, requirements_file, install_script))
- except Exception as e:
- print(f"EXCEPTION During Dependencies INSTALL on '{subdir}':\n{e}")
-
- return subdirs_with_files
-
-
-def install_requirements(requirements_file_path):
- if os.path.exists(requirements_file_path):
- subprocess.run(["pip", "install", "-r", requirements_file_path])
-
-
-def run_install_script(install_script_path):
- if os.path.exists(install_script_path):
- subprocess.run(["python", install_script_path])
-
-
-custom_nodes_directory = "custom_nodes"
-subdirs_with_files = get_enabled_subdirectories_with_files(custom_nodes_directory)
-
-
-for subdir, requirements_file, install_script in subdirs_with_files:
- install_requirements(requirements_file)
- run_install_script(install_script)
diff --git a/scripts/install-comfyui-venv-linux.sh b/scripts/install-comfyui-venv-linux.sh
deleted file mode 100755
index 5a736ef1b..000000000
--- a/scripts/install-comfyui-venv-linux.sh
+++ /dev/null
@@ -1,21 +0,0 @@
-git clone https://github.com/comfyanonymous/ComfyUI
-cd ComfyUI/custom_nodes
-git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager
-cd ..
-python -m venv venv
-source venv/bin/activate
-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 custom_nodes/comfyui-manager/requirements.txt
-cd ..
-echo "#!/bin/bash" > run_gpu.sh
-echo "cd ComfyUI" >> run_gpu.sh
-echo "source venv/bin/activate" >> run_gpu.sh
-echo "python main.py --preview-method auto" >> run_gpu.sh
-chmod +x run_gpu.sh
-
-echo "#!/bin/bash" > run_cpu.sh
-echo "cd ComfyUI" >> run_cpu.sh
-echo "source venv/bin/activate" >> run_cpu.sh
-echo "python main.py --preview-method auto --cpu" >> run_cpu.sh
-chmod +x run_cpu.sh
diff --git a/scripts/install-comfyui-venv-win.bat b/scripts/install-comfyui-venv-win.bat
deleted file mode 100755
index 47470098b..000000000
--- a/scripts/install-comfyui-venv-win.bat
+++ /dev/null
@@ -1,17 +0,0 @@
-git clone https://github.com/comfyanonymous/ComfyUI
-cd ComfyUI/custom_nodes
-git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager
-cd ..
-python -m venv venv
-call venv/Scripts/activate
-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 custom_nodes/comfyui-manager/requirements.txt
-cd ..
-echo "cd ComfyUI" >> run_gpu.bat
-echo "call venv/Scripts/activate" >> run_gpu.bat
-echo "python main.py" >> run_gpu.bat
-
-echo "cd ComfyUI" >> run_cpu.bat
-echo "call venv/Scripts/activate" >> run_cpu.bat
-echo "python main.py --cpu" >> run_cpu.bat
diff --git a/scripts/install-manager-for-portable-version.bat b/scripts/install-manager-for-portable-version.bat
deleted file mode 100644
index 6eb58b741..000000000
--- a/scripts/install-manager-for-portable-version.bat
+++ /dev/null
@@ -1,3 +0,0 @@
-.\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 -m pip install -r ./ComfyUI/custom_nodes/comfyui-manager/requirements.txt
diff --git a/snapshots/the_snapshot_files_are_located_here b/snapshots/the_snapshot_files_are_located_here
deleted file mode 100644
index e69de29bb..000000000