Compare commits
27 Commits
api/openap
...
draft-v4-c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8159ca5875 | ||
|
|
7fc8ba587e | ||
|
|
7a35bd9d9a | ||
|
|
a76ef49d2d | ||
|
|
bb0fcf6ea6 | ||
|
|
539e0a1534 | ||
|
|
aaae6ce304 | ||
|
|
3c413840d7 | ||
|
|
29ca93fcb4 | ||
|
|
9dc8e630a0 | ||
|
|
10105ad737 | ||
|
|
5738ea861a | ||
|
|
dbd25b0f0a | ||
|
|
0de9d36c28 | ||
|
|
05f1a8ab0d | ||
|
|
5ce170b7ce | ||
|
|
2b47aad076 | ||
|
|
b4dc59331d | ||
|
|
81e84fad78 | ||
|
|
42e8a959dd | ||
|
|
208ca31836 | ||
|
|
a128baf894 | ||
|
|
57b847eebf | ||
|
|
149257e4f1 | ||
|
|
212b8e7ed2 | ||
|
|
01ac9c895a | ||
|
|
ebcb14e6aa |
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PYPI_TOKEN=your-pypi-token
|
||||||
58
.github/workflows/publish-to-pypi.yml
vendored
Normal file
58
.github/workflows/publish-to-pypi.yml
vendored
Normal file
@@ -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
|
||||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -14,7 +14,7 @@ jobs:
|
|||||||
publish-node:
|
publish-node:
|
||||||
name: Publish Custom Node to registry
|
name: Publish Custom Node to registry
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ github.repository_owner == 'ltdrdata' }}
|
if: ${{ github.repository_owner == 'ltdrdata' || github.repository_owner == 'Comfy-Org' }}
|
||||||
steps:
|
steps:
|
||||||
- name: Check out code
|
- name: Check out code
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -17,4 +17,7 @@ github-stats-cache.json
|
|||||||
pip_overrides.json
|
pip_overrides.json
|
||||||
*.json
|
*.json
|
||||||
check2.sh
|
check2.sh
|
||||||
/venv/
|
/venv/
|
||||||
|
build
|
||||||
|
*.egg-info
|
||||||
|
.env
|
||||||
47
CONTRIBUTING.md
Normal file
47
CONTRIBUTING.md
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
## Testing Changes
|
||||||
|
|
||||||
|
1. Activate the ComfyUI environment.
|
||||||
|
|
||||||
|
2. Build package locally after making changes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# from inside the ComfyUI-Manager directory, with the ComfyUI environment activated
|
||||||
|
python -m build
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Install the package locally in the ComfyUI environment.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Uninstall existing package
|
||||||
|
pip uninstall comfyui-manager
|
||||||
|
|
||||||
|
# Install the locale package
|
||||||
|
pip install dist/comfyui-manager-*.whl
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Start ComfyUI.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# after navigating to the ComfyUI directory
|
||||||
|
python main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manually Publish Test Version to PyPi
|
||||||
|
|
||||||
|
1. Set the `PYPI_TOKEN` environment variable in env file.
|
||||||
|
|
||||||
|
2. If manually publishing, you likely want to use a release candidate version, so set the version in [pyproject.toml](pyproject.toml) to something like `0.0.1rc1`.
|
||||||
|
|
||||||
|
3. Build the package.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m build
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Upload the package to PyPi.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m twine upload dist/* --username __token__ --password $PYPI_TOKEN
|
||||||
|
```
|
||||||
|
|
||||||
|
5. View at https://pypi.org/project/comfyui-manager/
|
||||||
7
MANIFEST.in
Normal file
7
MANIFEST.in
Normal file
@@ -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
|
||||||
81
README.md
81
README.md
@@ -5,6 +5,7 @@
|
|||||||

|

|
||||||
|
|
||||||
## NOTICE
|
## 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.16: Support for `uv` has been added. Set `use_uv` in `config.ini`.
|
||||||
* V3.10: `double-click feature` is removed
|
* V3.10: `double-click feature` is removed
|
||||||
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
|
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
|
||||||
@@ -13,78 +14,26 @@
|
|||||||
|
|
||||||
## Installation
|
## 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
|
* See also: https://github.com/Comfy-Org/comfy-cli
|
||||||
|
|
||||||
|
|
||||||
### Installation[method4] (Installation for linux+venv: ComfyUI + ComfyUI-Manager)
|
## Front-end
|
||||||
|
|
||||||
To install ComfyUI with ComfyUI-Manager on Linux using a venv environment, you can follow these steps:
|
* 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).
|
||||||
* **prerequisite: python-is-python3, python3-venv, git**
|
* To enable the legacy front-end, set the environment variable `ENABLE_LEGACY_COMFYUI_MANAGER_FRONT` to `true` before running.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## 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.
|
|
||||||
|
|
||||||
|
|
||||||
## How To Use
|
## How To Use
|
||||||
|
|||||||
25
__init__.py
25
__init__.py
@@ -1,25 +0,0 @@
|
|||||||
"""
|
|
||||||
This file is the entry point for the ComfyUI-Manager package, handling CLI-only mode and initial setup.
|
|
||||||
"""
|
|
||||||
|
|
||||||
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']
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
python cm-cli.py $*
|
python ./comfyui_manager/cm-cli.py $*
|
||||||
|
|||||||
40
comfyui_manager/__init__.py
Normal file
40
comfyui_manager/__init__.py
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
def should_be_disabled(fullpath:str) -> bool:
|
||||||
|
"""
|
||||||
|
1. Disables the legacy ComfyUI-Manager.
|
||||||
|
2. The blocklist can be expanded later based on policies.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not args.disable_manager:
|
||||||
|
# In cases where installation is done via a zip archive, the directory name may not be comfyui-manager, and it may not contain a git repository.
|
||||||
|
# It is assumed that any installed legacy ComfyUI-Manager will have at least 'comfyui-manager' in its directory name.
|
||||||
|
dir_name = os.path.basename(fullpath).lower()
|
||||||
|
if 'comfyui-manager' in dir_name:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
@@ -15,41 +15,36 @@ import git
|
|||||||
import importlib
|
import importlib
|
||||||
|
|
||||||
|
|
||||||
sys.path.append(os.path.dirname(__file__))
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), "glob"))
|
|
||||||
|
|
||||||
import manager_util
|
import manager_util
|
||||||
|
|
||||||
# read env vars
|
# read env vars
|
||||||
# COMFYUI_FOLDERS_BASE_PATH is not required in cm-cli.py
|
# COMFYUI_FOLDERS_BASE_PATH is not required in cm-cli.py
|
||||||
# `comfy_path` should be resolved before importing manager_core
|
# `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, '..', '..'))
|
|
||||||
|
|
||||||
# This should be placed here
|
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||||
|
|
||||||
|
if comfy_path is None:
|
||||||
|
print("[bold red]cm-cli: environment variable 'COMFYUI_PATH' is not specified.[/bold red]")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
sys.path.append(comfy_path)
|
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 utils.extra_config
|
||||||
import cm_global
|
from .glob import cm_global
|
||||||
import manager_core as core
|
from .glob import manager_core as core
|
||||||
from manager_core import unified_manager
|
from .glob.manager_core import unified_manager
|
||||||
import cnr_utils
|
from .glob import cnr_utils
|
||||||
|
|
||||||
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||||
|
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||||
if sys.version_info < (3, 13):
|
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
|
||||||
else:
|
|
||||||
cm_global.pip_overrides = {}
|
|
||||||
|
|
||||||
if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json")):
|
if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json")):
|
||||||
with open(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json"), 'r', encoding="UTF-8", errors="ignore") as json_file:
|
with open(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json"), 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||||
@@ -151,9 +146,7 @@ class Ctx:
|
|||||||
if os.path.exists(core.manager_pip_overrides_path):
|
if os.path.exists(core.manager_pip_overrides_path):
|
||||||
with open(core.manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
with open(core.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 = json.load(json_file)
|
||||||
|
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||||
if sys.version_info < (3, 13):
|
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
|
||||||
|
|
||||||
if os.path.exists(core.manager_pip_blacklist_path):
|
if os.path.exists(core.manager_pip_blacklist_path):
|
||||||
with open(core.manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
with open(core.manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||||
@@ -190,18 +183,13 @@ class Ctx:
|
|||||||
cmd_ctx = Ctx()
|
cmd_ctx = Ctx()
|
||||||
|
|
||||||
|
|
||||||
def install_node(node_spec_str, is_all=False, cnt_msg='', **kwargs):
|
def install_node(node_spec_str, is_all=False, cnt_msg=''):
|
||||||
exit_on_fail = kwargs.get('exit_on_fail', False)
|
|
||||||
print(f"install_node exit on fail:{exit_on_fail}...")
|
|
||||||
|
|
||||||
if core.is_valid_url(node_spec_str):
|
if core.is_valid_url(node_spec_str):
|
||||||
# install via urls
|
# install via urls
|
||||||
res = asyncio.run(core.gitclone_install(node_spec_str, no_deps=cmd_ctx.no_deps))
|
res = asyncio.run(core.gitclone_install(node_spec_str, no_deps=cmd_ctx.no_deps))
|
||||||
if not res.result:
|
if not res.result:
|
||||||
print(res.msg)
|
print(res.msg)
|
||||||
print(f"[bold red]ERROR: An error occurred while installing '{node_spec_str}'.[/bold red]")
|
print(f"[bold red]ERROR: An error occurred while installing '{node_spec_str}'.[/bold red]")
|
||||||
if exit_on_fail:
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
else:
|
||||||
print(f"{cnt_msg} [INSTALLED] {node_spec_str:50}")
|
print(f"{cnt_msg} [INSTALLED] {node_spec_str:50}")
|
||||||
else:
|
else:
|
||||||
@@ -236,8 +224,6 @@ def install_node(node_spec_str, is_all=False, cnt_msg='', **kwargs):
|
|||||||
print("")
|
print("")
|
||||||
else:
|
else:
|
||||||
print(f"[bold red]ERROR: An error occurred while installing '{node_name}'.\n{res.msg}[/bold red]")
|
print(f"[bold red]ERROR: An error occurred while installing '{node_name}'.\n{res.msg}[/bold red]")
|
||||||
if exit_on_fail:
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def reinstall_node(node_spec_str, is_all=False, cnt_msg=''):
|
def reinstall_node(node_spec_str, is_all=False, cnt_msg=''):
|
||||||
@@ -599,7 +585,7 @@ def get_all_installed_node_specs():
|
|||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
def for_each_nodes(nodes, act, allow_all=True, **kwargs):
|
def for_each_nodes(nodes, act, allow_all=True):
|
||||||
is_all = False
|
is_all = False
|
||||||
if allow_all and 'all' in nodes:
|
if allow_all and 'all' in nodes:
|
||||||
is_all = True
|
is_all = True
|
||||||
@@ -611,7 +597,7 @@ def for_each_nodes(nodes, act, allow_all=True, **kwargs):
|
|||||||
i = 1
|
i = 1
|
||||||
for x in nodes:
|
for x in nodes:
|
||||||
try:
|
try:
|
||||||
act(x, is_all=is_all, cnt_msg=f'{i}/{total}', **kwargs)
|
act(x, is_all=is_all, cnt_msg=f'{i}/{total}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"ERROR: {e}")
|
print(f"ERROR: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
@@ -655,17 +641,13 @@ def install(
|
|||||||
None,
|
None,
|
||||||
help="user directory"
|
help="user directory"
|
||||||
),
|
),
|
||||||
exit_on_fail: bool = typer.Option(
|
|
||||||
False,
|
|
||||||
help="Exit on failure"
|
|
||||||
)
|
|
||||||
):
|
):
|
||||||
cmd_ctx.set_user_directory(user_directory)
|
cmd_ctx.set_user_directory(user_directory)
|
||||||
cmd_ctx.set_channel_mode(channel, mode)
|
cmd_ctx.set_channel_mode(channel, mode)
|
||||||
cmd_ctx.set_no_deps(no_deps)
|
cmd_ctx.set_no_deps(no_deps)
|
||||||
|
|
||||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||||
for_each_nodes(nodes, act=install_node, exit_on_fail=exit_on_fail)
|
for_each_nodes(nodes, act=install_node)
|
||||||
pip_fixer.fix_broken()
|
pip_fixer.fix_broken()
|
||||||
|
|
||||||
|
|
||||||
@@ -6,8 +6,9 @@ import time
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
import manager_core
|
from . import manager_core
|
||||||
import manager_util
|
from . import manager_util
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
import toml
|
import toml
|
||||||
|
|
||||||
17
comfyui_manager/glob/enums.py
Normal file
17
comfyui_manager/glob/enums.py
Normal file
@@ -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"
|
||||||
@@ -15,9 +15,12 @@ comfy_path = os.environ.get('COMFYUI_PATH')
|
|||||||
git_exe_path = os.environ.get('GIT_EXE_PATH')
|
git_exe_path = os.environ.get('GIT_EXE_PATH')
|
||||||
|
|
||||||
if comfy_path is None:
|
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)
|
print("git_helper: environment variable 'COMFYUI_PATH' is not specified.")
|
||||||
comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
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):
|
def download_url(url, dest_folder, filename=None):
|
||||||
# Ensure the destination folder exists
|
# Ensure the destination folder exists
|
||||||
@@ -32,18 +32,15 @@ from packaging import version
|
|||||||
|
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
glob_path = os.path.join(os.path.dirname(__file__)) # ComfyUI-Manager/glob
|
from . import cm_global
|
||||||
sys.path.append(glob_path)
|
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
|
||||||
|
|
||||||
import cm_global
|
version_code = [4, 0]
|
||||||
import cnr_utils
|
|
||||||
import manager_util
|
|
||||||
import git_utils
|
|
||||||
import manager_downloader
|
|
||||||
from node_package import InstalledNodePackage
|
|
||||||
|
|
||||||
|
|
||||||
version_code = [3, 32, 3]
|
|
||||||
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||||
|
|
||||||
|
|
||||||
@@ -58,6 +55,7 @@ class InvalidChannel(Exception):
|
|||||||
self.channel = channel
|
self.channel = channel
|
||||||
super().__init__(channel)
|
super().__init__(channel)
|
||||||
|
|
||||||
|
|
||||||
def get_default_custom_nodes_path():
|
def get_default_custom_nodes_path():
|
||||||
global default_custom_nodes_path
|
global default_custom_nodes_path
|
||||||
if default_custom_nodes_path is None:
|
if default_custom_nodes_path is None:
|
||||||
@@ -183,10 +181,11 @@ comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
|||||||
|
|
||||||
if comfy_path is None:
|
if comfy_path is None:
|
||||||
try:
|
try:
|
||||||
import folder_paths
|
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
||||||
comfy_path = os.path.join(os.path.dirname(folder_paths.__file__))
|
os.environ['COMFYUI_PATH'] = comfy_path
|
||||||
except:
|
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:
|
if comfy_base_path is None:
|
||||||
comfy_base_path = comfy_path
|
comfy_base_path = comfy_path
|
||||||
@@ -203,6 +202,7 @@ manager_snapshot_path = None
|
|||||||
manager_pip_overrides_path = None
|
manager_pip_overrides_path = None
|
||||||
manager_pip_blacklist_path = None
|
manager_pip_blacklist_path = None
|
||||||
manager_components_path = None
|
manager_components_path = None
|
||||||
|
manager_batch_history_path = None
|
||||||
|
|
||||||
def update_user_directory(user_dir):
|
def update_user_directory(user_dir):
|
||||||
global manager_files_path
|
global manager_files_path
|
||||||
@@ -213,6 +213,7 @@ def update_user_directory(user_dir):
|
|||||||
global manager_pip_overrides_path
|
global manager_pip_overrides_path
|
||||||
global manager_pip_blacklist_path
|
global manager_pip_blacklist_path
|
||||||
global manager_components_path
|
global manager_components_path
|
||||||
|
global manager_batch_history_path
|
||||||
|
|
||||||
manager_files_path = os.path.abspath(os.path.join(user_dir, 'default', 'ComfyUI-Manager'))
|
manager_files_path = os.path.abspath(os.path.join(user_dir, 'default', 'ComfyUI-Manager'))
|
||||||
if not os.path.exists(manager_files_path):
|
if not os.path.exists(manager_files_path):
|
||||||
@@ -232,10 +233,14 @@ def update_user_directory(user_dir):
|
|||||||
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
||||||
manager_components_path = os.path.join(manager_files_path, "components")
|
manager_components_path = os.path.join(manager_files_path, "components")
|
||||||
manager_util.cache_dir = os.path.join(manager_files_path, "cache")
|
manager_util.cache_dir = os.path.join(manager_files_path, "cache")
|
||||||
|
manager_batch_history_path = os.path.join(manager_files_path, "batch_history")
|
||||||
|
|
||||||
if not os.path.exists(manager_util.cache_dir):
|
if not os.path.exists(manager_util.cache_dir):
|
||||||
os.makedirs(manager_util.cache_dir)
|
os.makedirs(manager_util.cache_dir)
|
||||||
|
|
||||||
|
if not os.path.exists(manager_batch_history_path):
|
||||||
|
os.makedirs(manager_batch_history_path)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import folder_paths
|
import folder_paths
|
||||||
update_user_directory(folder_paths.get_user_directory())
|
update_user_directory(folder_paths.get_user_directory())
|
||||||
@@ -553,7 +558,7 @@ class UnifiedManager:
|
|||||||
ver = str(manager_util.StrictVersion(info['version']))
|
ver = str(manager_util.StrictVersion(info['version']))
|
||||||
return {'id': cnr['id'], 'cnr': cnr, 'ver': ver}
|
return {'id': cnr['id'], 'cnr': cnr, 'ver': ver}
|
||||||
else:
|
else:
|
||||||
return None
|
return {'id': info['id'], 'ver': info['version']}
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -729,7 +734,7 @@ class UnifiedManager:
|
|||||||
|
|
||||||
return latest
|
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.custom_node_map_cache = {}
|
||||||
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
|
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
|
||||||
self.nightly_inactive_nodes = {} # node_id -> fullpath
|
self.nightly_inactive_nodes = {} # node_id -> fullpath
|
||||||
@@ -737,17 +742,18 @@ class UnifiedManager:
|
|||||||
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
||||||
self.active_nodes = {} # node_id -> node_version * 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
|
dont_wait = True
|
||||||
|
|
||||||
# reload 'cnr_map' and 'repo_cnr_map'
|
if update_cnr_map:
|
||||||
cnrs = await cnr_utils.get_cnr_data(cache_mode=cache_mode=='cache', dont_wait=dont_wait)
|
# 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:
|
for x in cnrs:
|
||||||
self.cnr_map[x['id']] = x
|
self.cnr_map[x['id']] = x
|
||||||
if 'repository' in x:
|
if 'repository' in x:
|
||||||
normalized_url = git_utils.normalize_url(x['repository'])
|
normalized_url = git_utils.normalize_url(x['repository'])
|
||||||
self.repo_cnr_map[normalized_url] = x
|
self.repo_cnr_map[normalized_url] = x
|
||||||
|
|
||||||
# reload node status info from custom_nodes/*
|
# reload node status info from custom_nodes/*
|
||||||
for custom_nodes_path in folder_paths.get_folder_paths('custom_nodes'):
|
for custom_nodes_path in folder_paths.get_folder_paths('custom_nodes'):
|
||||||
@@ -868,9 +874,8 @@ class UnifiedManager:
|
|||||||
package_name = remap_pip_package(line.strip())
|
package_name = remap_pip_package(line.strip())
|
||||||
if package_name and not package_name.startswith('#') and package_name not in self.processed_install:
|
if package_name and not package_name.startswith('#') and package_name not in self.processed_install:
|
||||||
self.processed_install.add(package_name)
|
self.processed_install.add(package_name)
|
||||||
clean_package_name = package_name.split('#')[0].strip()
|
install_cmd = manager_util.make_pip_cmd(["install", package_name])
|
||||||
install_cmd = manager_util.make_pip_cmd(["install", clean_package_name])
|
if package_name.strip() != "" and not package_name.startswith('#'):
|
||||||
if clean_package_name != "" and not clean_package_name.startswith('#'):
|
|
||||||
res = res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
|
res = res and try_install_script(url, repo_path, install_cmd, instant_execution=instant_execution)
|
||||||
|
|
||||||
pip_fixer.fix_broken()
|
pip_fixer.fix_broken()
|
||||||
@@ -1442,12 +1447,20 @@ class UnifiedManager:
|
|||||||
return self.unified_enable(node_id, version_spec)
|
return self.unified_enable(node_id, version_spec)
|
||||||
|
|
||||||
elif version_spec == 'unknown' or version_spec == 'nightly':
|
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':
|
if version_spec == 'nightly':
|
||||||
# disable cnr nodes
|
# disable cnr nodes
|
||||||
if self.is_enabled(node_id, 'cnr'):
|
if self.is_enabled(node_id, 'cnr'):
|
||||||
self.unified_disable(node_id, False)
|
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)
|
res = self.repo_install(repo_url, to_path, instant_execution=instant_execution, no_deps=no_deps, return_postinstall=return_postinstall)
|
||||||
if res.result:
|
if res.result:
|
||||||
if version_spec == 'unknown':
|
if version_spec == 'unknown':
|
||||||
@@ -1664,9 +1677,9 @@ def read_config():
|
|||||||
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
||||||
'downgrade_blacklist': default_conf.get('downgrade_blacklist', '').lower(),
|
'downgrade_blacklist': default_conf.get('downgrade_blacklist', '').lower(),
|
||||||
'always_lazy_install': get_bool('always_lazy_install', False),
|
'always_lazy_install': get_bool('always_lazy_install', False),
|
||||||
'network_mode': default_conf.get('network_mode', 'public').lower(),
|
'network_mode': default_conf.get('network_mode', NetworkMode.PUBLIC.value).lower(),
|
||||||
'security_level': default_conf.get('security_level', 'normal').lower(),
|
'security_level': default_conf.get('security_level', SecurityLevel.NORMAL.value).lower(),
|
||||||
'db_mode': default_conf.get('db_mode', 'cache').lower(),
|
'db_mode': default_conf.get('db_mode', DBMode.CACHE.value).lower(),
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -1687,9 +1700,9 @@ def read_config():
|
|||||||
'model_download_by_agent': False,
|
'model_download_by_agent': False,
|
||||||
'downgrade_blacklist': '',
|
'downgrade_blacklist': '',
|
||||||
'always_lazy_install': False,
|
'always_lazy_install': False,
|
||||||
'network_mode': 'public', # public | private | offline
|
'network_mode': NetworkMode.OFFLINE.value,
|
||||||
'security_level': 'normal', # strong | normal | normal- | weak
|
'security_level': SecurityLevel.NORMAL.value,
|
||||||
'db_mode': 'cache', # local | cache | remote
|
'db_mode': DBMode.CACHE.value,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2073,13 +2086,6 @@ def is_valid_url(url):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def extract_url_and_commit_id(s):
|
|
||||||
index = s.rfind('@')
|
|
||||||
if index == -1:
|
|
||||||
return (s, '')
|
|
||||||
else:
|
|
||||||
return (s[:index], s[index+1:])
|
|
||||||
|
|
||||||
async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=False):
|
async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=False):
|
||||||
await unified_manager.reload('cache')
|
await unified_manager.reload('cache')
|
||||||
await unified_manager.get_custom_nodes('default', 'cache')
|
await unified_manager.get_custom_nodes('default', 'cache')
|
||||||
@@ -2097,11 +2103,8 @@ async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=
|
|||||||
cnr = unified_manager.get_cnr_by_repo(url)
|
cnr = unified_manager.get_cnr_by_repo(url)
|
||||||
if cnr:
|
if cnr:
|
||||||
cnr_id = cnr['id']
|
cnr_id = cnr['id']
|
||||||
return await unified_manager.install_by_id(cnr_id, version_spec=None, channel='default', mode='cache')
|
return await unified_manager.install_by_id(cnr_id, version_spec='nightly', channel='default', mode='cache')
|
||||||
else:
|
else:
|
||||||
new_url, commit_id = extract_url_and_commit_id(url)
|
|
||||||
if commit_id != "":
|
|
||||||
url = new_url
|
|
||||||
repo_name = os.path.splitext(os.path.basename(url))[0]
|
repo_name = os.path.splitext(os.path.basename(url))[0]
|
||||||
|
|
||||||
# NOTE: Keep original name as possible if unknown node
|
# NOTE: Keep original name as possible if unknown node
|
||||||
@@ -2134,10 +2137,6 @@ async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=
|
|||||||
return result.fail(f"Failed to clone '{clone_url}' into '{repo_path}'")
|
return result.fail(f"Failed to clone '{clone_url}' into '{repo_path}'")
|
||||||
else:
|
else:
|
||||||
repo = git.Repo.clone_from(clone_url, repo_path, recursive=True, progress=GitProgress())
|
repo = git.Repo.clone_from(clone_url, repo_path, recursive=True, progress=GitProgress())
|
||||||
if commit_id!= "":
|
|
||||||
repo.git.checkout(commit_id)
|
|
||||||
repo.git.submodule('update', '--init', '--recursive')
|
|
||||||
|
|
||||||
repo.git.clear_cache()
|
repo.git.clear_cache()
|
||||||
repo.close()
|
repo.close()
|
||||||
|
|
||||||
@@ -2200,7 +2199,7 @@ async def get_data_by_mode(mode, filename, channel_url=None):
|
|||||||
cache_uri = str(manager_util.simple_hash(uri))+'_'+filename
|
cache_uri = str(manager_util.simple_hash(uri))+'_'+filename
|
||||||
cache_uri = os.path.join(manager_util.cache_dir, cache_uri)
|
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
|
# offline network mode
|
||||||
if os.path.exists(cache_uri):
|
if os.path.exists(cache_uri):
|
||||||
json_obj = await manager_util.get_data(cache_uri)
|
json_obj = await manager_util.get_data(cache_uri)
|
||||||
@@ -2220,7 +2219,7 @@ async def get_data_by_mode(mode, filename, channel_url=None):
|
|||||||
with open(cache_uri, "w", encoding='utf-8') as file:
|
with open(cache_uri, "w", encoding='utf-8') as file:
|
||||||
json.dump(json_obj, file, indent=4, sort_keys=True)
|
json.dump(json_obj, file, indent=4, sort_keys=True)
|
||||||
except Exception as e:
|
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)
|
uri = os.path.join(manager_util.comfyui_manager_path, filename)
|
||||||
json_obj = await manager_util.get_data(uri)
|
json_obj = await manager_util.get_data(uri)
|
||||||
|
|
||||||
@@ -14,18 +14,23 @@ import git
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from server import PromptServer
|
from server import PromptServer
|
||||||
import manager_core as core
|
|
||||||
import manager_util
|
|
||||||
import cm_global
|
|
||||||
import logging
|
import logging
|
||||||
import asyncio
|
import asyncio
|
||||||
import queue
|
from collections import deque
|
||||||
|
|
||||||
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(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 = "-"
|
comfy_ui_hash = "-"
|
||||||
comfyui_tag = None
|
comfyui_tag = None
|
||||||
@@ -155,9 +160,7 @@ class ManagerFuncsInComfyUI(core.ManagerFuncs):
|
|||||||
|
|
||||||
core.manager_funcs = ManagerFuncsInComfyUI()
|
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.comfy_path = os.path.dirname(folder_paths.__file__)
|
||||||
core.js_path = os.path.join(core.comfy_path, "web", "extensions")
|
core.js_path = os.path.join(core.comfy_path, "web", "extensions")
|
||||||
@@ -391,16 +394,73 @@ def nickname_filter(json_obj):
|
|||||||
return json_obj
|
return json_obj
|
||||||
|
|
||||||
|
|
||||||
task_queue = queue.Queue()
|
class TaskBatch:
|
||||||
nodepack_result = {}
|
def __init__(self, batch_json, tasks, failed):
|
||||||
model_result = {}
|
self.nodepack_result = {}
|
||||||
|
self.model_result = {}
|
||||||
|
self.batch_id = batch_json.get('batch_id') if batch_json is not None else None
|
||||||
|
self.batch_json = batch_json
|
||||||
|
self.tasks = tasks
|
||||||
|
self.current_index = 0
|
||||||
|
self.stats = {}
|
||||||
|
self.failed = failed if failed is not None else set()
|
||||||
|
self.is_aborted = False
|
||||||
|
|
||||||
|
def is_done(self):
|
||||||
|
return len(self.tasks) <= self.current_index
|
||||||
|
|
||||||
|
def get_next(self):
|
||||||
|
if self.is_done():
|
||||||
|
return None
|
||||||
|
|
||||||
|
item = self.tasks[self.current_index]
|
||||||
|
self.current_index += 1
|
||||||
|
return item
|
||||||
|
|
||||||
|
def done_count(self):
|
||||||
|
return len(self.nodepack_result) + len(self.model_result)
|
||||||
|
|
||||||
|
def total_count(self):
|
||||||
|
return len(self.tasks)
|
||||||
|
|
||||||
|
def abort(self):
|
||||||
|
self.is_aborted = True
|
||||||
|
|
||||||
|
def finalize(self):
|
||||||
|
if self.batch_id is not None:
|
||||||
|
batch_path = os.path.join(core.manager_batch_history_path, self.batch_id+".json")
|
||||||
|
json_obj = {
|
||||||
|
"batch": self.batch_json,
|
||||||
|
"nodepack_result": self.nodepack_result,
|
||||||
|
"model_result": self.model_result,
|
||||||
|
"failed": list(self.failed)
|
||||||
|
}
|
||||||
|
with open(batch_path, "w") as json_file:
|
||||||
|
json.dump(json_obj, json_file, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
temp_queue_batch = []
|
||||||
|
task_batch_queue = deque()
|
||||||
tasks_in_progress = set()
|
tasks_in_progress = set()
|
||||||
task_worker_lock = threading.Lock()
|
task_worker_lock = threading.Lock()
|
||||||
|
aborted_batch = None
|
||||||
|
|
||||||
|
|
||||||
|
def finalize_temp_queue_batch(batch_json=None, failed=None):
|
||||||
|
"""
|
||||||
|
make temp_queue_batch as a batch snapshot and add to batch_queue
|
||||||
|
"""
|
||||||
|
|
||||||
|
global temp_queue_batch
|
||||||
|
|
||||||
|
if len(temp_queue_batch):
|
||||||
|
batch = TaskBatch(batch_json, temp_queue_batch, failed)
|
||||||
|
task_batch_queue.append(batch)
|
||||||
|
temp_queue_batch = []
|
||||||
|
|
||||||
|
|
||||||
async def task_worker():
|
async def task_worker():
|
||||||
global task_queue
|
global task_queue
|
||||||
global nodepack_result
|
|
||||||
global model_result
|
|
||||||
global tasks_in_progress
|
global tasks_in_progress
|
||||||
|
|
||||||
async def do_install(item) -> str:
|
async def do_install(item) -> str:
|
||||||
@@ -413,8 +473,7 @@ async def task_worker():
|
|||||||
return f"Cannot resolve install target: '{node_spec_str}'"
|
return f"Cannot resolve install target: '{node_spec_str}'"
|
||||||
|
|
||||||
node_name, version_spec, is_specified = node_spec
|
node_name, version_spec, is_specified = node_spec
|
||||||
res = await core.unified_manager.install_by_id(node_name, version_spec, channel, mode, return_postinstall=skip_post_install)
|
res = await core.unified_manager.install_by_id(node_name, version_spec, channel, mode, return_postinstall=skip_post_install) # discard post install if skip_post_install mode
|
||||||
# discard post install if skip_post_install mode
|
|
||||||
|
|
||||||
if res.action not in ['skip', 'enable', 'install-git', 'install-cnr', 'switch-cnr']:
|
if res.action not in ['skip', 'enable', 'install-git', 'install-cnr', 'switch-cnr']:
|
||||||
logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}")
|
logging.error(f"[ComfyUI-Manager] Installation failed:\n{res.msg}")
|
||||||
@@ -429,6 +488,11 @@ async def task_worker():
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return f"Installation failed:\n{node_spec_str}"
|
return f"Installation failed:\n{node_spec_str}"
|
||||||
|
|
||||||
|
async def do_enable(item) -> str:
|
||||||
|
ui_id, cnr_id = item
|
||||||
|
core.unified_manager.unified_enable(cnr_id)
|
||||||
|
return 'success'
|
||||||
|
|
||||||
async def do_update(item):
|
async def do_update(item):
|
||||||
ui_id, node_name, node_ver = item
|
ui_id, node_name, node_ver = item
|
||||||
|
|
||||||
@@ -587,31 +651,45 @@ async def task_worker():
|
|||||||
|
|
||||||
return f"Model installation error: {model_url}"
|
return f"Model installation error: {model_url}"
|
||||||
|
|
||||||
stats = {}
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
done_count = len(nodepack_result) + len(model_result)
|
with task_worker_lock:
|
||||||
total_count = done_count + task_queue.qsize()
|
if len(task_batch_queue) > 0:
|
||||||
|
cur_batch = task_batch_queue[0]
|
||||||
|
else:
|
||||||
|
logging.info("\n[ComfyUI-Manager] All tasks are completed.")
|
||||||
|
logging.info("\nAfter restarting ComfyUI, please refresh the browser.")
|
||||||
|
|
||||||
if task_queue.empty():
|
res = {'status': 'all-done'}
|
||||||
logging.info(f"\n[ComfyUI-Manager] Queued works are completed.\n{stats}")
|
|
||||||
|
|
||||||
logging.info("\nAfter restarting ComfyUI, please refresh the browser.")
|
PromptServer.instance.send_sync("cm-queue-status", res)
|
||||||
PromptServer.instance.send_sync("cm-queue-status",
|
|
||||||
{'status': 'done',
|
return
|
||||||
'nodepack_result': nodepack_result, 'model_result': model_result,
|
|
||||||
'total_count': total_count, 'done_count': done_count})
|
if cur_batch.is_done():
|
||||||
nodepack_result = {}
|
logging.info(f"\n[ComfyUI-Manager] A tasks batch(batch_id={cur_batch.batch_id}) is completed.\nstat={cur_batch.stats}")
|
||||||
task_queue = queue.Queue()
|
|
||||||
return # terminate worker thread
|
res = {'status': 'batch-done',
|
||||||
|
'nodepack_result': cur_batch.nodepack_result,
|
||||||
|
'model_result': cur_batch.model_result,
|
||||||
|
'total_count': cur_batch.total_count(),
|
||||||
|
'done_count': cur_batch.done_count(),
|
||||||
|
'batch_id': cur_batch.batch_id,
|
||||||
|
'remaining_batch_count': len(task_batch_queue) }
|
||||||
|
|
||||||
|
PromptServer.instance.send_sync("cm-queue-status", res)
|
||||||
|
cur_batch.finalize()
|
||||||
|
task_batch_queue.popleft()
|
||||||
|
continue
|
||||||
|
|
||||||
with task_worker_lock:
|
with task_worker_lock:
|
||||||
kind, item = task_queue.get()
|
kind, item = cur_batch.get_next()
|
||||||
tasks_in_progress.add((kind, item[0]))
|
tasks_in_progress.add((kind, item[0]))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if kind == 'install':
|
if kind == 'install':
|
||||||
msg = await do_install(item)
|
msg = await do_install(item)
|
||||||
|
elif kind == 'enable':
|
||||||
|
msg = await do_enable(item)
|
||||||
elif kind == 'install-model':
|
elif kind == 'install-model':
|
||||||
msg = await do_install_model(item)
|
msg = await do_install_model(item)
|
||||||
elif kind == 'update':
|
elif kind == 'update':
|
||||||
@@ -635,31 +713,131 @@ async def task_worker():
|
|||||||
with task_worker_lock:
|
with task_worker_lock:
|
||||||
tasks_in_progress.remove((kind, item[0]))
|
tasks_in_progress.remove((kind, item[0]))
|
||||||
|
|
||||||
ui_id = item[0]
|
ui_id = item[0]
|
||||||
if kind == 'install-model':
|
if kind == 'install-model':
|
||||||
model_result[ui_id] = msg
|
cur_batch.model_result[ui_id] = msg
|
||||||
ui_target = "model_manager"
|
ui_target = "model_manager"
|
||||||
elif kind == 'update-main':
|
elif kind == 'update-main':
|
||||||
nodepack_result[ui_id] = msg
|
cur_batch.nodepack_result[ui_id] = msg
|
||||||
ui_target = "main"
|
ui_target = "main"
|
||||||
elif kind == 'update-comfyui':
|
elif kind == 'update-comfyui':
|
||||||
nodepack_result['comfyui'] = msg
|
cur_batch.nodepack_result['comfyui'] = msg
|
||||||
ui_target = "main"
|
ui_target = "main"
|
||||||
elif kind == 'update':
|
elif kind == 'update':
|
||||||
nodepack_result[ui_id] = msg['msg']
|
cur_batch.nodepack_result[ui_id] = msg['msg']
|
||||||
ui_target = "nodepack_manager"
|
ui_target = "nodepack_manager"
|
||||||
else:
|
else:
|
||||||
nodepack_result[ui_id] = msg
|
cur_batch.nodepack_result[ui_id] = msg
|
||||||
ui_target = "nodepack_manager"
|
ui_target = "nodepack_manager"
|
||||||
|
|
||||||
stats[kind] = stats.get(kind, 0) + 1
|
cur_batch.stats[kind] = cur_batch.stats.get(kind, 0) + 1
|
||||||
|
|
||||||
PromptServer.instance.send_sync("cm-queue-status",
|
PromptServer.instance.send_sync("cm-queue-status",
|
||||||
{'status': 'in_progress', 'target': item[0], 'ui_target': ui_target,
|
{'status': 'in_progress',
|
||||||
'total_count': total_count, 'done_count': done_count})
|
'target': item[0],
|
||||||
|
'batch_id': cur_batch.batch_id,
|
||||||
|
'ui_target': ui_target,
|
||||||
|
'total_count': cur_batch.total_count(),
|
||||||
|
'done_count': cur_batch.done_count()})
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/customnode/getmappings")
|
@routes.post("/v2/manager/queue/batch")
|
||||||
|
async def queue_batch(request):
|
||||||
|
json_data = await request.json()
|
||||||
|
|
||||||
|
failed = set()
|
||||||
|
|
||||||
|
for k, v in json_data.items():
|
||||||
|
if k == 'update_all':
|
||||||
|
await _update_all({'mode': v})
|
||||||
|
|
||||||
|
elif k == 'reinstall':
|
||||||
|
for x in v:
|
||||||
|
res = await _uninstall_custom_node(x)
|
||||||
|
if res.status != 200:
|
||||||
|
failed.add(x[0])
|
||||||
|
else:
|
||||||
|
res = await _install_custom_node(x)
|
||||||
|
if res.status != 200:
|
||||||
|
failed.add(x[0])
|
||||||
|
|
||||||
|
elif k == 'install':
|
||||||
|
for x in v:
|
||||||
|
res = await _install_custom_node(x)
|
||||||
|
if res.status != 200:
|
||||||
|
failed.add(x[0])
|
||||||
|
|
||||||
|
elif k == 'uninstall':
|
||||||
|
for x in v:
|
||||||
|
res = await _uninstall_custom_node(x)
|
||||||
|
if res.status != 200:
|
||||||
|
failed.add(x[0])
|
||||||
|
|
||||||
|
elif k == 'update':
|
||||||
|
for x in v:
|
||||||
|
res = await _update_custom_node(x)
|
||||||
|
if res.status != 200:
|
||||||
|
failed.add(x[0])
|
||||||
|
|
||||||
|
elif k == 'update_comfyui':
|
||||||
|
await update_comfyui(None)
|
||||||
|
|
||||||
|
elif k == 'disable':
|
||||||
|
for x in v:
|
||||||
|
await _disable_node(x)
|
||||||
|
|
||||||
|
elif k == 'install_model':
|
||||||
|
for x in v:
|
||||||
|
res = await _install_model(x)
|
||||||
|
if res.status != 200:
|
||||||
|
failed.add(x[0])
|
||||||
|
|
||||||
|
elif k == 'fix':
|
||||||
|
for x in v:
|
||||||
|
res = await _fix_custom_node(x)
|
||||||
|
if res.status != 200:
|
||||||
|
failed.add(x[0])
|
||||||
|
|
||||||
|
with task_worker_lock:
|
||||||
|
finalize_temp_queue_batch(json_data, failed)
|
||||||
|
_queue_start()
|
||||||
|
|
||||||
|
return web.json_response({"failed": list(failed)}, content_type='application/json')
|
||||||
|
|
||||||
|
|
||||||
|
@routes.get("/v2/manager/queue/history_list")
|
||||||
|
async def get_history_list(request):
|
||||||
|
history_path = core.manager_batch_history_path
|
||||||
|
|
||||||
|
try:
|
||||||
|
files = [os.path.join(history_path, f) for f in os.listdir(history_path) if os.path.isfile(os.path.join(history_path, f))]
|
||||||
|
files.sort(key=lambda x: os.path.getmtime(x), reverse=True)
|
||||||
|
history_ids = [os.path.basename(f)[:-5] for f in files]
|
||||||
|
|
||||||
|
return web.json_response({"ids": list(history_ids)}, content_type='application/json')
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"[ComfyUI-Manager] /v2/manager/queue/history_list - {e}")
|
||||||
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
|
@routes.get("/v2/manager/queue/history")
|
||||||
|
async def get_history(request):
|
||||||
|
try:
|
||||||
|
json_name = request.rel_url.query["id"]+'.json'
|
||||||
|
batch_path = os.path.join(core.manager_batch_history_path, json_name)
|
||||||
|
|
||||||
|
with open(batch_path, 'r', encoding='utf-8') as file:
|
||||||
|
json_str = file.read()
|
||||||
|
json_obj = json.loads(json_str)
|
||||||
|
return web.json_response(json_obj, content_type='application/json')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"[ComfyUI-Manager] /v2/manager/queue/history - {e}")
|
||||||
|
|
||||||
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
|
@routes.get("/v2/customnode/getmappings")
|
||||||
async def fetch_customnode_mappings(request):
|
async def fetch_customnode_mappings(request):
|
||||||
"""
|
"""
|
||||||
provide unified (node -> node pack) mapping list
|
provide unified (node -> node pack) mapping list
|
||||||
@@ -695,7 +873,7 @@ async def fetch_customnode_mappings(request):
|
|||||||
return web.json_response(json_obj, content_type='application/json')
|
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):
|
async def fetch_updates(request):
|
||||||
try:
|
try:
|
||||||
if request.rel_url.query["mode"] == "local":
|
if request.rel_url.query["mode"] == "local":
|
||||||
@@ -722,8 +900,13 @@ async def fetch_updates(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/queue/update_all")
|
@routes.get("/v2/manager/queue/update_all")
|
||||||
async def update_all(request):
|
async def update_all(request):
|
||||||
|
json_data = dict(request.rel_url.query)
|
||||||
|
return await _update_all(json_data)
|
||||||
|
|
||||||
|
|
||||||
|
async def _update_all(json_data):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403)
|
return web.Response(status=403)
|
||||||
@@ -735,13 +918,13 @@ async def update_all(request):
|
|||||||
|
|
||||||
await core.save_snapshot_with_postfix('autosave')
|
await core.save_snapshot_with_postfix('autosave')
|
||||||
|
|
||||||
if request.rel_url.query["mode"] == "local":
|
if json_data["mode"] == "local":
|
||||||
channel = 'local'
|
channel = 'local'
|
||||||
else:
|
else:
|
||||||
channel = core.get_config()['channel_url']
|
channel = core.get_config()['channel_url']
|
||||||
|
|
||||||
await core.unified_manager.reload(request.rel_url.query["mode"])
|
await core.unified_manager.reload(json_data["mode"])
|
||||||
await core.unified_manager.get_custom_nodes(channel, request.rel_url.query["mode"])
|
await core.unified_manager.get_custom_nodes(channel, json_data["mode"])
|
||||||
|
|
||||||
for k, v in core.unified_manager.active_nodes.items():
|
for k, v in core.unified_manager.active_nodes.items():
|
||||||
if k == 'comfyui-manager':
|
if k == 'comfyui-manager':
|
||||||
@@ -750,7 +933,7 @@ async def update_all(request):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
update_item = k, k, v[0]
|
update_item = k, k, v[0]
|
||||||
task_queue.put(("update-main", update_item))
|
temp_queue_batch.append(("update-main", update_item))
|
||||||
|
|
||||||
for k, v in core.unified_manager.unknown_active_nodes.items():
|
for k, v in core.unified_manager.unknown_active_nodes.items():
|
||||||
if k == 'comfyui-manager':
|
if k == 'comfyui-manager':
|
||||||
@@ -759,7 +942,7 @@ async def update_all(request):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
update_item = k, k, 'unknown'
|
update_item = k, k, 'unknown'
|
||||||
task_queue.put(("update-main", update_item))
|
temp_queue_batch.append(("update-main", update_item))
|
||||||
|
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
@@ -810,7 +993,7 @@ def populate_markdown(x):
|
|||||||
|
|
||||||
# freeze imported version
|
# freeze imported version
|
||||||
startup_time_installed_node_packs = core.get_installed_node_packs()
|
startup_time_installed_node_packs = core.get_installed_node_packs()
|
||||||
@routes.get("/customnode/installed")
|
@routes.get("/v2/customnode/installed")
|
||||||
async def installed_list(request):
|
async def installed_list(request):
|
||||||
mode = request.query.get('mode', 'default')
|
mode = request.query.get('mode', 'default')
|
||||||
|
|
||||||
@@ -822,7 +1005,7 @@ async def installed_list(request):
|
|||||||
return web.json_response(res, content_type='application/json')
|
return web.json_response(res, content_type='application/json')
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/customnode/getlist")
|
@routes.get("/v2/customnode/getlist")
|
||||||
async def fetch_customnode_list(request):
|
async def fetch_customnode_list(request):
|
||||||
"""
|
"""
|
||||||
provide unified custom node list
|
provide unified custom node list
|
||||||
@@ -935,7 +1118,7 @@ def check_model_installed(json_obj):
|
|||||||
executor.submit(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):
|
async def fetch_externalmodel_list(request):
|
||||||
# The model list is only allowed in the default channel, yet.
|
# 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')
|
json_obj = await core.get_data_by_mode(request.rel_url.query["mode"], 'model-list.json')
|
||||||
@@ -948,14 +1131,14 @@ async def fetch_externalmodel_list(request):
|
|||||||
return web.json_response(json_obj, content_type='application/json')
|
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):
|
async def get_snapshot_list(request):
|
||||||
items = [f[:-5] for f in os.listdir(core.manager_snapshot_path) if f.endswith('.json')]
|
items = [f[:-5] for f in os.listdir(core.manager_snapshot_path) if f.endswith('.json')]
|
||||||
items.sort(reverse=True)
|
items.sort(reverse=True)
|
||||||
return web.json_response({'items': items}, content_type='application/json')
|
return web.json_response({'items': items}, content_type='application/json')
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/snapshot/remove")
|
@routes.get("/v2/snapshot/remove")
|
||||||
async def remove_snapshot(request):
|
async def remove_snapshot(request):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
@@ -973,7 +1156,7 @@ async def remove_snapshot(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/snapshot/restore")
|
@routes.get("/v2/snapshot/restore")
|
||||||
async def restore_snapshot(request):
|
async def restore_snapshot(request):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
@@ -999,7 +1182,7 @@ async def restore_snapshot(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/snapshot/get_current")
|
@routes.get("/v2/snapshot/get_current")
|
||||||
async def get_current_snapshot_api(request):
|
async def get_current_snapshot_api(request):
|
||||||
try:
|
try:
|
||||||
return web.json_response(await core.get_current_snapshot(), content_type='application/json')
|
return web.json_response(await core.get_current_snapshot(), content_type='application/json')
|
||||||
@@ -1007,7 +1190,7 @@ async def get_current_snapshot_api(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/snapshot/save")
|
@routes.get("/v2/snapshot/save")
|
||||||
async def save_snapshot(request):
|
async def save_snapshot(request):
|
||||||
try:
|
try:
|
||||||
await core.save_snapshot_with_postfix('snapshot')
|
await core.save_snapshot_with_postfix('snapshot')
|
||||||
@@ -1044,82 +1227,7 @@ def unzip_install(files):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def copy_install(files, js_path_name=None):
|
@routes.get("/v2/customnode/versions/{node_name}")
|
||||||
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}")
|
|
||||||
async def get_cnr_versions(request):
|
async def get_cnr_versions(request):
|
||||||
node_name = request.match_info.get("node_name", None)
|
node_name = request.match_info.get("node_name", None)
|
||||||
versions = core.cnr_utils.all_versions_of_node(node_name)
|
versions = core.cnr_utils.all_versions_of_node(node_name)
|
||||||
@@ -1130,7 +1238,7 @@ async def get_cnr_versions(request):
|
|||||||
return web.Response(status=400)
|
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):
|
async def get_disabled_versions(request):
|
||||||
node_name = request.match_info.get("node_name", None)
|
node_name = request.match_info.get("node_name", None)
|
||||||
versions = []
|
versions = []
|
||||||
@@ -1146,7 +1254,7 @@ async def get_disabled_versions(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/customnode/import_fail_info")
|
@routes.post("/v2/customnode/import_fail_info")
|
||||||
async def import_fail_info(request):
|
async def import_fail_info(request):
|
||||||
json_data = await request.json()
|
json_data = await request.json()
|
||||||
|
|
||||||
@@ -1163,42 +1271,74 @@ async def import_fail_info(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/queue/reinstall")
|
@routes.post("/v2/manager/queue/reinstall")
|
||||||
async def reinstall_custom_node(request):
|
async def reinstall_custom_node(request):
|
||||||
await uninstall_custom_node(request)
|
await uninstall_custom_node(request)
|
||||||
await install_custom_node(request)
|
await install_custom_node(request)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/queue/reset")
|
@routes.get("/v2/manager/queue/reset")
|
||||||
async def reset_queue(request):
|
async def reset_queue(request):
|
||||||
global task_queue
|
global task_batch_queue
|
||||||
task_queue = queue.Queue()
|
global temp_queue_batch
|
||||||
|
|
||||||
|
with task_worker_lock:
|
||||||
|
temp_queue_batch = []
|
||||||
|
task_batch_queue = deque()
|
||||||
|
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/queue/status")
|
@routes.get("/v2/manager/queue/abort_current")
|
||||||
|
async def abort_queue(request):
|
||||||
|
global task_batch_queue
|
||||||
|
global temp_queue_batch
|
||||||
|
|
||||||
|
with task_worker_lock:
|
||||||
|
temp_queue_batch = []
|
||||||
|
if len(task_batch_queue) > 0:
|
||||||
|
task_batch_queue[0].abort()
|
||||||
|
task_batch_queue.popleft()
|
||||||
|
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@routes.get("/v2/manager/queue/status")
|
||||||
async def queue_count(request):
|
async def queue_count(request):
|
||||||
global task_queue
|
global task_queue
|
||||||
|
|
||||||
with task_worker_lock:
|
with task_worker_lock:
|
||||||
done_count = len(nodepack_result) + len(model_result)
|
if len(task_batch_queue) > 0:
|
||||||
in_progress_count = len(tasks_in_progress)
|
cur_batch = task_batch_queue[0]
|
||||||
total_count = done_count + in_progress_count + task_queue.qsize()
|
done_count = cur_batch.done_count()
|
||||||
is_processing = task_worker_thread is not None and task_worker_thread.is_alive()
|
total_count = cur_batch.total_count()
|
||||||
|
in_progress_count = len(tasks_in_progress)
|
||||||
|
is_processing = task_worker_thread is not None and task_worker_thread.is_alive()
|
||||||
|
else:
|
||||||
|
done_count = 0
|
||||||
|
total_count = 0
|
||||||
|
in_progress_count = 0
|
||||||
|
is_processing = False
|
||||||
|
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
'total_count': total_count, 'done_count': done_count, 'in_progress_count': in_progress_count,
|
'total_count': total_count,
|
||||||
|
'done_count': done_count,
|
||||||
|
'in_progress_count': in_progress_count,
|
||||||
'is_processing': is_processing})
|
'is_processing': is_processing})
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/queue/install")
|
@routes.post("/v2/manager/queue/install")
|
||||||
async def install_custom_node(request):
|
async def install_custom_node(request):
|
||||||
|
json_data = await request.json()
|
||||||
|
print(f"install={json_data}")
|
||||||
|
return await _install_custom_node(json_data)
|
||||||
|
|
||||||
|
|
||||||
|
async def _install_custom_node(json_data):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
json_data = await request.json()
|
|
||||||
|
|
||||||
# non-nightly cnr is safe
|
# non-nightly cnr is safe
|
||||||
risky_level = None
|
risky_level = None
|
||||||
cnr_id = json_data.get('id')
|
cnr_id = json_data.get('id')
|
||||||
@@ -1210,8 +1350,10 @@ async def install_custom_node(request):
|
|||||||
if json_data['version'] != 'unknown' and selected_version != 'unknown':
|
if json_data['version'] != 'unknown' and selected_version != 'unknown':
|
||||||
if skip_post_install:
|
if skip_post_install:
|
||||||
if cnr_id in core.unified_manager.nightly_inactive_nodes or cnr_id in core.unified_manager.cnr_inactive_nodes:
|
if cnr_id in core.unified_manager.nightly_inactive_nodes or cnr_id in core.unified_manager.cnr_inactive_nodes:
|
||||||
core.unified_manager.unified_enable(cnr_id)
|
enable_item = json_data.get('ui_id'), cnr_id
|
||||||
|
temp_queue_batch.append(("enable", enable_item))
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
elif selected_version is None:
|
elif selected_version is None:
|
||||||
selected_version = 'latest'
|
selected_version = 'latest'
|
||||||
|
|
||||||
@@ -1224,9 +1366,11 @@ async def install_custom_node(request):
|
|||||||
if git_url is None:
|
if git_url is None:
|
||||||
logging.error(f"[ComfyUI-Manager] Following node pack doesn't provide `nightly` version: ${git_url}")
|
logging.error(f"[ComfyUI-Manager] Following node pack doesn't provide `nightly` version: ${git_url}")
|
||||||
return web.Response(status=404, text=f"Following node pack doesn't provide `nightly` version: ${git_url}")
|
return web.Response(status=404, text=f"Following node pack doesn't provide `nightly` version: ${git_url}")
|
||||||
|
|
||||||
elif json_data['version'] != 'unknown' and selected_version == 'unknown':
|
elif json_data['version'] != 'unknown' and selected_version == 'unknown':
|
||||||
logging.error(f"[ComfyUI-Manager] Invalid installation request: {json_data}")
|
logging.error(f"[ComfyUI-Manager] Invalid installation request: {json_data}")
|
||||||
return web.Response(status=400, text="Invalid installation request")
|
return web.Response(status=400, text="Invalid installation request")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# unknown
|
# unknown
|
||||||
unknown_name = os.path.basename(json_data['files'][0])
|
unknown_name = os.path.basename(json_data['files'][0])
|
||||||
@@ -1245,39 +1389,42 @@ async def install_custom_node(request):
|
|||||||
return web.Response(status=404, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=404, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
install_item = json_data.get('ui_id'), node_spec_str, json_data['channel'], json_data['mode'], skip_post_install
|
install_item = json_data.get('ui_id'), node_spec_str, json_data['channel'], json_data['mode'], skip_post_install
|
||||||
task_queue.put(("install", install_item))
|
temp_queue_batch.append(("install", install_item))
|
||||||
|
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
task_worker_thread:threading.Thread = None
|
task_worker_thread:threading.Thread = None
|
||||||
|
|
||||||
@routes.get("/manager/queue/start")
|
@routes.get("/v2/manager/queue/start")
|
||||||
async def queue_start(request):
|
async def queue_start(request):
|
||||||
global nodepack_result
|
with task_worker_lock:
|
||||||
global model_result
|
finalize_temp_queue_batch()
|
||||||
|
return _queue_start()
|
||||||
|
|
||||||
|
def _queue_start():
|
||||||
global task_worker_thread
|
global task_worker_thread
|
||||||
|
|
||||||
if task_worker_thread is not None and task_worker_thread.is_alive():
|
if task_worker_thread is not None and task_worker_thread.is_alive():
|
||||||
return web.Response(status=201) # already in-progress
|
return web.Response(status=201) # already in-progress
|
||||||
|
|
||||||
nodepack_result = {}
|
|
||||||
model_result = {}
|
|
||||||
|
|
||||||
task_worker_thread = threading.Thread(target=lambda: asyncio.run(task_worker()))
|
task_worker_thread = threading.Thread(target=lambda: asyncio.run(task_worker()))
|
||||||
task_worker_thread.start()
|
task_worker_thread.start()
|
||||||
|
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/queue/fix")
|
@routes.post("/v2/manager/queue/fix")
|
||||||
async def fix_custom_node(request):
|
async def fix_custom_node(request):
|
||||||
|
json_data = await request.json()
|
||||||
|
return await _fix_custom_node(json_data)
|
||||||
|
|
||||||
|
|
||||||
|
async def _fix_custom_node(json_data):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_GENERAL)
|
logging.error(SECURITY_MESSAGE_GENERAL)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
json_data = await request.json()
|
|
||||||
|
|
||||||
node_id = json_data.get('id')
|
node_id = json_data.get('id')
|
||||||
node_ver = json_data['version']
|
node_ver = json_data['version']
|
||||||
if node_ver != 'unknown':
|
if node_ver != 'unknown':
|
||||||
@@ -1287,12 +1434,12 @@ async def fix_custom_node(request):
|
|||||||
node_name = os.path.basename(json_data['files'][0])
|
node_name = os.path.basename(json_data['files'][0])
|
||||||
|
|
||||||
update_item = json_data.get('ui_id'), node_name, json_data['version']
|
update_item = json_data.get('ui_id'), node_name, json_data['version']
|
||||||
task_queue.put(("fix", update_item))
|
temp_queue_batch.append(("fix", update_item))
|
||||||
|
|
||||||
return web.Response(status=200)
|
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):
|
async def install_custom_node_git_url(request):
|
||||||
if not is_allowed_security_level('high'):
|
if not is_allowed_security_level('high'):
|
||||||
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
|
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||||
@@ -1312,7 +1459,7 @@ async def install_custom_node_git_url(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/customnode/install/pip")
|
@routes.post("/v2/customnode/install/pip")
|
||||||
async def install_custom_node_pip(request):
|
async def install_custom_node_pip(request):
|
||||||
if not is_allowed_security_level('high'):
|
if not is_allowed_security_level('high'):
|
||||||
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
|
logging.error(SECURITY_MESSAGE_NORMAL_MINUS)
|
||||||
@@ -1324,14 +1471,17 @@ async def install_custom_node_pip(request):
|
|||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/queue/uninstall")
|
@routes.post("/v2/manager/queue/uninstall")
|
||||||
async def uninstall_custom_node(request):
|
async def uninstall_custom_node(request):
|
||||||
|
json_data = await request.json()
|
||||||
|
return await _uninstall_custom_node(json_data)
|
||||||
|
|
||||||
|
|
||||||
|
async def _uninstall_custom_node(json_data):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
json_data = await request.json()
|
|
||||||
|
|
||||||
node_id = json_data.get('id')
|
node_id = json_data.get('id')
|
||||||
if json_data['version'] != 'unknown':
|
if json_data['version'] != 'unknown':
|
||||||
is_unknown = False
|
is_unknown = False
|
||||||
@@ -1342,19 +1492,22 @@ async def uninstall_custom_node(request):
|
|||||||
node_name = os.path.basename(json_data['files'][0])
|
node_name = os.path.basename(json_data['files'][0])
|
||||||
|
|
||||||
uninstall_item = json_data.get('ui_id'), node_name, is_unknown
|
uninstall_item = json_data.get('ui_id'), node_name, is_unknown
|
||||||
task_queue.put(("uninstall", uninstall_item))
|
temp_queue_batch.append(("uninstall", uninstall_item))
|
||||||
|
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/queue/update")
|
@routes.post("/v2/manager/queue/update")
|
||||||
async def update_custom_node(request):
|
async def update_custom_node(request):
|
||||||
|
json_data = await request.json()
|
||||||
|
return await _update_custom_node(json_data)
|
||||||
|
|
||||||
|
|
||||||
|
async def _update_custom_node(json_data):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
json_data = await request.json()
|
|
||||||
|
|
||||||
node_id = json_data.get('id')
|
node_id = json_data.get('id')
|
||||||
if json_data['version'] != 'unknown':
|
if json_data['version'] != 'unknown':
|
||||||
node_name = node_id
|
node_name = node_id
|
||||||
@@ -1363,19 +1516,19 @@ async def update_custom_node(request):
|
|||||||
node_name = os.path.basename(json_data['files'][0])
|
node_name = os.path.basename(json_data['files'][0])
|
||||||
|
|
||||||
update_item = json_data.get('ui_id'), node_name, json_data['version']
|
update_item = json_data.get('ui_id'), node_name, json_data['version']
|
||||||
task_queue.put(("update", update_item))
|
temp_queue_batch.append(("update", update_item))
|
||||||
|
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/queue/update_comfyui")
|
@routes.get("/v2/manager/queue/update_comfyui")
|
||||||
async def update_comfyui(request):
|
async def update_comfyui(request):
|
||||||
is_stable = core.get_config()['update_policy'] != 'nightly-comfyui'
|
is_stable = core.get_config()['update_policy'] != 'nightly-comfyui'
|
||||||
task_queue.put(("update-comfyui", ('comfyui', is_stable)))
|
temp_queue_batch.append(("update-comfyui", ('comfyui', is_stable)))
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/comfyui_manager/comfyui_versions")
|
@routes.get("/v2/comfyui_manager/comfyui_versions")
|
||||||
async def comfyui_versions(request):
|
async def comfyui_versions(request):
|
||||||
try:
|
try:
|
||||||
res, current, latest = core.get_comfyui_versions()
|
res, current, latest = core.get_comfyui_versions()
|
||||||
@@ -1386,7 +1539,7 @@ async def comfyui_versions(request):
|
|||||||
return web.Response(status=400)
|
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):
|
async def comfyui_switch_version(request):
|
||||||
try:
|
try:
|
||||||
if "ver" in request.rel_url.query:
|
if "ver" in request.rel_url.query:
|
||||||
@@ -1399,10 +1552,14 @@ async def comfyui_switch_version(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/queue/disable")
|
@routes.post("/v2/manager/queue/disable")
|
||||||
async def disable_node(request):
|
async def disable_node(request):
|
||||||
json_data = await request.json()
|
json_data = await request.json()
|
||||||
|
await _disable_node(json_data)
|
||||||
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
|
async def _disable_node(json_data):
|
||||||
node_id = json_data.get('id')
|
node_id = json_data.get('id')
|
||||||
if json_data['version'] != 'unknown':
|
if json_data['version'] != 'unknown':
|
||||||
is_unknown = False
|
is_unknown = False
|
||||||
@@ -1413,9 +1570,7 @@ async def disable_node(request):
|
|||||||
node_name = os.path.basename(json_data['files'][0])
|
node_name = os.path.basename(json_data['files'][0])
|
||||||
|
|
||||||
update_item = json_data.get('ui_id'), node_name, is_unknown
|
update_item = json_data.get('ui_id'), node_name, is_unknown
|
||||||
task_queue.put(("disable", update_item))
|
temp_queue_batch.append(("disable", update_item))
|
||||||
|
|
||||||
return web.Response(status=200)
|
|
||||||
|
|
||||||
|
|
||||||
async def check_whitelist_for_model(item):
|
async def check_whitelist_for_model(item):
|
||||||
@@ -1434,10 +1589,13 @@ async def check_whitelist_for_model(item):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/queue/install_model")
|
@routes.post("/v2/manager/queue/install_model")
|
||||||
async def install_model(request):
|
async def install_model(request):
|
||||||
json_data = await request.json()
|
json_data = await request.json()
|
||||||
|
return await _install_model(json_data)
|
||||||
|
|
||||||
|
|
||||||
|
async def _install_model(json_data):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
@@ -1461,12 +1619,12 @@ async def install_model(request):
|
|||||||
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
return web.Response(status=403, text="A security error has occurred. Please check the terminal logs")
|
||||||
|
|
||||||
install_item = json_data.get('ui_id'), json_data
|
install_item = json_data.get('ui_id'), json_data
|
||||||
task_queue.put(("install-model", install_item))
|
temp_queue_batch.append(("install-model", install_item))
|
||||||
|
|
||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/preview_method")
|
@routes.get("/v2/manager/preview_method")
|
||||||
async def preview_method(request):
|
async def preview_method(request):
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
set_preview_method(request.rel_url.query['value'])
|
set_preview_method(request.rel_url.query['value'])
|
||||||
@@ -1477,7 +1635,7 @@ async def preview_method(request):
|
|||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/db_mode")
|
@routes.get("/v2/manager/db_mode")
|
||||||
async def db_mode(request):
|
async def db_mode(request):
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
set_db_mode(request.rel_url.query['value'])
|
set_db_mode(request.rel_url.query['value'])
|
||||||
@@ -1489,7 +1647,7 @@ async def db_mode(request):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/policy/component")
|
@routes.get("/v2/manager/policy/component")
|
||||||
async def component_policy(request):
|
async def component_policy(request):
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
set_component_policy(request.rel_url.query['value'])
|
set_component_policy(request.rel_url.query['value'])
|
||||||
@@ -1500,7 +1658,7 @@ async def component_policy(request):
|
|||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/policy/update")
|
@routes.get("/v2/manager/policy/update")
|
||||||
async def update_policy(request):
|
async def update_policy(request):
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
set_update_policy(request.rel_url.query['value'])
|
set_update_policy(request.rel_url.query['value'])
|
||||||
@@ -1511,7 +1669,7 @@ async def update_policy(request):
|
|||||||
return web.Response(status=200)
|
return web.Response(status=200)
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/channel_url_list")
|
@routes.get("/v2/manager/channel_url_list")
|
||||||
async def channel_url_list(request):
|
async def channel_url_list(request):
|
||||||
channels = core.get_channel_dict()
|
channels = core.get_channel_dict()
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
@@ -1548,7 +1706,7 @@ def add_target_blank(html_text):
|
|||||||
return modified_html
|
return modified_html
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/notice")
|
@routes.get("/v2/manager/notice")
|
||||||
async def get_notice(request):
|
async def get_notice(request):
|
||||||
url = "github.com"
|
url = "github.com"
|
||||||
path = "/ltdrdata/ltdrdata.github.io/wiki/News"
|
path = "/ltdrdata/ltdrdata.github.io/wiki/News"
|
||||||
@@ -1595,7 +1753,13 @@ async def get_notice(request):
|
|||||||
return web.Response(text="Unable to retrieve Notice", status=200)
|
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="""<font color="red">Starting from ComfyUI-Manager V4.0+, it should be installed via pip.<BR><BR>Please remove the ComfyUI-Manager installed in the <font color="white">'custom_nodes'</font> directory.</font>""", status=200)
|
||||||
|
|
||||||
|
|
||||||
|
@routes.get("/v2/manager/reboot")
|
||||||
def restart(self):
|
def restart(self):
|
||||||
if not is_allowed_security_level('middle'):
|
if not is_allowed_security_level('middle'):
|
||||||
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
logging.error(SECURITY_MESSAGE_MIDDLE_OR_BELOW)
|
||||||
@@ -1632,7 +1796,7 @@ def restart(self):
|
|||||||
return os.execv(sys.executable, cmds)
|
return os.execv(sys.executable, cmds)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/component/save")
|
@routes.post("/v2/manager/component/save")
|
||||||
async def save_component(request):
|
async def save_component(request):
|
||||||
try:
|
try:
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
@@ -1662,7 +1826,7 @@ async def save_component(request):
|
|||||||
return web.Response(status=400)
|
return web.Response(status=400)
|
||||||
|
|
||||||
|
|
||||||
@routes.post("/manager/component/loads")
|
@routes.post("/v2/manager/component/loads")
|
||||||
async def load_components(request):
|
async def load_components(request):
|
||||||
if os.path.exists(core.manager_components_path):
|
if os.path.exists(core.manager_components_path):
|
||||||
try:
|
try:
|
||||||
@@ -1687,7 +1851,7 @@ async def load_components(request):
|
|||||||
return web.json_response({})
|
return web.json_response({})
|
||||||
|
|
||||||
|
|
||||||
@routes.get("/manager/version")
|
@routes.get("/v2/manager/version")
|
||||||
async def get_version(request):
|
async def get_version(request):
|
||||||
return web.Response(text=core.version_str, status=200)
|
return web.Response(text=core.version_str, status=200)
|
||||||
|
|
||||||
@@ -1736,7 +1900,7 @@ async def default_cache_update():
|
|||||||
logging.error(f"[ComfyUI-Manager] Failed to perform initial fetching '{filename}': {e}")
|
logging.error(f"[ComfyUI-Manager] Failed to perform initial fetching '{filename}': {e}")
|
||||||
traceback.print_exc()
|
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")
|
a = get_cache("custom-node-list.json")
|
||||||
b = get_cache("extension-node-map.json")
|
b = get_cache("extension-node-map.json")
|
||||||
c = get_cache("model-list.json")
|
c = get_cache("model-list.json")
|
||||||
@@ -1751,6 +1915,8 @@ async def default_cache_update():
|
|||||||
# load at least once
|
# load at least once
|
||||||
await core.unified_manager.reload('remote', dont_wait=False)
|
await core.unified_manager.reload('remote', dont_wait=False)
|
||||||
await core.unified_manager.get_custom_nodes(channel_url, 'remote')
|
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.")
|
logging.info("[ComfyUI-Manager] All startup tasks have been completed.")
|
||||||
|
|
||||||
@@ -1768,4 +1934,3 @@ cm_global.register_extension('ComfyUI-Manager',
|
|||||||
'nodes': {},
|
'nodes': {},
|
||||||
'description': 'This extension provides the ability to manage custom nodes in ComfyUI.', })
|
'description': 'This extension provides the ability to manage custom nodes in ComfyUI.', })
|
||||||
|
|
||||||
|
|
||||||
@@ -15,16 +15,18 @@ import re
|
|||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
import shlex
|
import shlex
|
||||||
import cm_global
|
|
||||||
|
|
||||||
|
|
||||||
cache_lock = threading.Lock()
|
cache_lock = threading.Lock()
|
||||||
|
session_lock = threading.Lock()
|
||||||
|
|
||||||
comfyui_manager_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
comfyui_manager_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
cache_dir = os.path.join(comfyui_manager_path, '.cache') # This path is also updated together in **manager_core.update_user_directory**.
|
cache_dir = os.path.join(comfyui_manager_path, '.cache') # This path is also updated together in **manager_core.update_user_directory**.
|
||||||
|
|
||||||
use_uv = False
|
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():
|
def add_python_path_to_env():
|
||||||
if platform.system() != "Windows":
|
if platform.system() != "Windows":
|
||||||
@@ -257,7 +259,7 @@ def get_installed_packages(renew=False):
|
|||||||
pip_map[normalized_name] = y[1]
|
pip_map[normalized_name] = y[1]
|
||||||
except subprocess.CalledProcessError:
|
except subprocess.CalledProcessError:
|
||||||
logging.error("[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
|
logging.error("[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
|
||||||
return {}
|
return set()
|
||||||
|
|
||||||
return pip_map
|
return pip_map
|
||||||
|
|
||||||
@@ -308,7 +310,6 @@ def parse_requirement_line(line):
|
|||||||
|
|
||||||
|
|
||||||
torch_torchvision_torchaudio_version_map = {
|
torch_torchvision_torchaudio_version_map = {
|
||||||
'2.7.0': ('0.22.0', '2.7.0'),
|
|
||||||
'2.6.0': ('0.21.0', '2.6.0'),
|
'2.6.0': ('0.21.0', '2.6.0'),
|
||||||
'2.5.1': ('0.20.0', '2.5.0'),
|
'2.5.1': ('0.20.0', '2.5.0'),
|
||||||
'2.5.0': ('0.20.0', '2.5.0'),
|
'2.5.0': ('0.20.0', '2.5.0'),
|
||||||
@@ -412,9 +413,8 @@ class PIPFixer:
|
|||||||
|
|
||||||
if len(targets) > 0:
|
if len(targets) > 0:
|
||||||
for x in targets:
|
for x in targets:
|
||||||
if sys.version_info < (3, 13):
|
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}", "numpy<2"])
|
||||||
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}", "numpy<2"])
|
subprocess.check_output(cmd, universal_newlines=True)
|
||||||
subprocess.check_output(cmd, universal_newlines=True)
|
|
||||||
|
|
||||||
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
|
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -422,21 +422,17 @@ class PIPFixer:
|
|||||||
logging.error(e)
|
logging.error(e)
|
||||||
|
|
||||||
# fix numpy
|
# fix numpy
|
||||||
if sys.version_info >= (3, 13):
|
try:
|
||||||
logging.info("[ComfyUI-Manager] In Python 3.13 and above, PIP Fixer does not downgrade `numpy` below version 2.0. If you need to force a downgrade of `numpy`, please use `pip_auto_fix.list`.")
|
np = new_pip_versions.get('numpy')
|
||||||
else:
|
if np is not None:
|
||||||
try:
|
if StrictVersion(np) >= StrictVersion('2'):
|
||||||
np = new_pip_versions.get('numpy')
|
cmd = make_pip_cmd(['install', "numpy<2"])
|
||||||
if cm_global.pip_overrides.get('numpy') == 'numpy<2':
|
subprocess.check_output(cmd , universal_newlines=True)
|
||||||
if np is not None:
|
|
||||||
if StrictVersion(np) >= StrictVersion('2'):
|
|
||||||
cmd = make_pip_cmd(['install', "numpy<2"])
|
|
||||||
subprocess.check_output(cmd , universal_newlines=True)
|
|
||||||
|
|
||||||
logging.info("[ComfyUI-Manager] 'numpy' dependency were fixed")
|
logging.info("[ComfyUI-Manager] 'numpy' dependency were fixed")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("[ComfyUI-Manager] Failed to restore numpy")
|
logging.error("[ComfyUI-Manager] Failed to restore numpy")
|
||||||
logging.error(e)
|
logging.error(e)
|
||||||
|
|
||||||
# fix missing frontend
|
# fix missing frontend
|
||||||
try:
|
try:
|
||||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from git_utils import get_commit_hash
|
from .git_utils import get_commit_hash
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import mimetypes
|
import mimetypes
|
||||||
import manager_core as core
|
from . import manager_core as core
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import aiohttp
|
import aiohttp
|
||||||
@@ -53,7 +54,7 @@ def compute_sha256_checksum(filepath):
|
|||||||
return sha256.hexdigest()
|
return sha256.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
@PromptServer.instance.routes.get("/manager/share_option")
|
@PromptServer.instance.routes.get("/v2/manager/share_option")
|
||||||
async def share_option(request):
|
async def share_option(request):
|
||||||
if "value" in request.rel_url.query:
|
if "value" in request.rel_url.query:
|
||||||
core.get_config()['share_option'] = request.rel_url.query['value']
|
core.get_config()['share_option'] = request.rel_url.query['value']
|
||||||
@@ -122,7 +123,7 @@ def set_youml_settings(settings):
|
|||||||
f.write(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):
|
async def api_get_openart_auth(request):
|
||||||
# print("Getting stored Matrix credentials...")
|
# print("Getting stored Matrix credentials...")
|
||||||
openart_key = get_openart_auth()
|
openart_key = get_openart_auth()
|
||||||
@@ -131,7 +132,7 @@ async def api_get_openart_auth(request):
|
|||||||
return web.json_response({"openart_key": openart_key})
|
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):
|
async def api_set_openart_auth(request):
|
||||||
json_data = await request.json()
|
json_data = await request.json()
|
||||||
openart_key = json_data['openart_key']
|
openart_key = json_data['openart_key']
|
||||||
@@ -140,7 +141,7 @@ async def api_set_openart_auth(request):
|
|||||||
return web.Response(status=200)
|
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):
|
async def api_get_matrix_auth(request):
|
||||||
# print("Getting stored Matrix credentials...")
|
# print("Getting stored Matrix credentials...")
|
||||||
matrix_auth = get_matrix_auth()
|
matrix_auth = get_matrix_auth()
|
||||||
@@ -149,7 +150,7 @@ async def api_get_matrix_auth(request):
|
|||||||
return web.json_response(matrix_auth)
|
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):
|
async def api_get_youml_settings(request):
|
||||||
youml_settings = get_youml_settings()
|
youml_settings = get_youml_settings()
|
||||||
if not 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))
|
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):
|
async def api_set_youml_settings(request):
|
||||||
json_data = await request.json()
|
json_data = await request.json()
|
||||||
set_youml_settings(json.dumps(json_data))
|
set_youml_settings(json.dumps(json_data))
|
||||||
return web.Response(status=200)
|
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):
|
async def api_get_comfyworkflows_auth(request):
|
||||||
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
|
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
|
||||||
# in the same directory as the ComfyUI base folder
|
# 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})
|
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):
|
async def set_esheep_workflow_and_images(request):
|
||||||
json_data = await request.json()
|
json_data = await request.json()
|
||||||
with open(os.path.join(core.manager_files_path, "esheep_share_message.json"), "w", encoding='utf-8') as file:
|
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)
|
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):
|
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:
|
with open(os.path.join(core.manager_files_path, "esheep_share_message.json"), 'r', encoding='utf-8') as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
@@ -211,7 +212,7 @@ def has_provided_comfyworkflows_auth(comfyworkflows_sharekey):
|
|||||||
return comfyworkflows_sharekey.strip()
|
return comfyworkflows_sharekey.strip()
|
||||||
|
|
||||||
|
|
||||||
@PromptServer.instance.routes.post("/manager/share")
|
@PromptServer.instance.routes.post("/v2/manager/share")
|
||||||
async def share_art(request):
|
async def share_art(request):
|
||||||
# get json data
|
# get json data
|
||||||
json_data = await request.json()
|
json_data = await request.json()
|
||||||
@@ -25,7 +25,7 @@ async function tryInstallCustomNode(event) {
|
|||||||
const res = await customConfirm(msg);
|
const res = await customConfirm(msg);
|
||||||
if(res) {
|
if(res) {
|
||||||
if(event.detail.target.installed == 'Disabled') {
|
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',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(event.detail.target)
|
body: JSON.stringify(event.detail.target)
|
||||||
@@ -35,7 +35,7 @@ async function tryInstallCustomNode(event) {
|
|||||||
await sleep(300);
|
await sleep(300);
|
||||||
app.ui.dialog.show(`Installing... '${event.detail.target.title}'`);
|
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',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(event.detail.target)
|
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) {
|
if(response.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
show_message('This action is not allowed with this security level configuration.');
|
||||||
return false;
|
return false;
|
||||||
@@ -14,9 +14,9 @@ import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
|||||||
import {
|
import {
|
||||||
free_models, install_pip, install_via_git_url, manager_instance,
|
free_models, install_pip, install_via_git_url, manager_instance,
|
||||||
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
||||||
infoToast, showTerminal, setNeedRestart
|
infoToast, showTerminal, setNeedRestart, generateUUID
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
import { ComponentBuilderDialog, load_components, set_component_policy } from "./components-manager.js";
|
||||||
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
||||||
import { ModelManager } from "./model-manager.js";
|
import { ModelManager } from "./model-manager.js";
|
||||||
import { SnapshotManager } from "./snapshot.js";
|
import { SnapshotManager } from "./snapshot.js";
|
||||||
@@ -189,8 +189,7 @@ docStyle.innerHTML = `
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function is_legacy_front() {
|
function isBeforeFrontendVersion(compareVersion) {
|
||||||
let compareVersion = '1.2.49';
|
|
||||||
try {
|
try {
|
||||||
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
|
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
|
||||||
if (typeof frontendVersion !== 'string') {
|
if (typeof frontendVersion !== 'string') {
|
||||||
@@ -223,6 +222,9 @@ function is_legacy_front() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const is_legacy_front = () => isBeforeFrontendVersion('1.2.49');
|
||||||
|
const isNotNewManagerUI = () => isBeforeFrontendVersion('1.16.4');
|
||||||
|
|
||||||
document.head.appendChild(docStyle);
|
document.head.appendChild(docStyle);
|
||||||
|
|
||||||
var update_comfyui_button = null;
|
var update_comfyui_button = null;
|
||||||
@@ -232,7 +234,7 @@ var restart_stop_button = null;
|
|||||||
var update_policy_combo = null;
|
var update_policy_combo = null;
|
||||||
|
|
||||||
let share_option = 'all';
|
let share_option = 'all';
|
||||||
var is_updating = false;
|
var batch_id = null;
|
||||||
|
|
||||||
|
|
||||||
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
||||||
@@ -415,7 +417,7 @@ const style = `
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
async function init_share_option() {
|
async function init_share_option() {
|
||||||
api.fetchApi('/manager/share_option')
|
api.fetchApi('/v2/manager/share_option')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
share_option = data || 'all';
|
share_option = data || 'all';
|
||||||
@@ -423,7 +425,7 @@ async function init_share_option() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function init_notice(notice) {
|
async function init_notice(notice) {
|
||||||
api.fetchApi('/manager/notice')
|
api.fetchApi('/v2/manager/notice')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
notice.innerHTML = data;
|
notice.innerHTML = data;
|
||||||
@@ -474,14 +476,19 @@ async function updateComfyUI() {
|
|||||||
let prev_text = update_comfyui_button.innerText;
|
let prev_text = update_comfyui_button.innerText;
|
||||||
update_comfyui_button.innerText = "Updating ComfyUI...";
|
update_comfyui_button.innerText = "Updating ComfyUI...";
|
||||||
|
|
||||||
set_inprogress_mode();
|
// set_inprogress_mode();
|
||||||
|
|
||||||
const response = await api.fetchApi('/manager/queue/update_comfyui');
|
|
||||||
|
|
||||||
showTerminal();
|
showTerminal();
|
||||||
|
|
||||||
is_updating = true;
|
batch_id = generateUUID();
|
||||||
await api.fetchApi('/manager/queue/start');
|
|
||||||
|
let batch = {};
|
||||||
|
batch['batch_id'] = batch_id;
|
||||||
|
batch['update_comfyui'] = true;
|
||||||
|
|
||||||
|
const res = await api.fetchApi(`/v2/manager/queue/batch`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(batch)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function showVersionSelectorDialog(versions, current, onSelect) {
|
function showVersionSelectorDialog(versions, current, onSelect) {
|
||||||
@@ -612,7 +619,7 @@ async function switchComfyUI() {
|
|||||||
switch_comfyui_button.disabled = true;
|
switch_comfyui_button.disabled = true;
|
||||||
switch_comfyui_button.style.backgroundColor = "gray";
|
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.disabled = false;
|
||||||
switch_comfyui_button.style.backgroundColor = "";
|
switch_comfyui_button.style.backgroundColor = "";
|
||||||
@@ -631,14 +638,14 @@ async function switchComfyUI() {
|
|||||||
showVersionSelectorDialog(versions, obj.current, async (selected_version) => {
|
showVersionSelectorDialog(versions, obj.current, async (selected_version) => {
|
||||||
if(selected_version == 'nightly') {
|
if(selected_version == 'nightly') {
|
||||||
update_policy_combo.value = 'nightly-comfyui';
|
update_policy_combo.value = 'nightly-comfyui';
|
||||||
api.fetchApi('/manager/policy/update?value=nightly-comfyui');
|
api.fetchApi('/v2/manager/policy/update?value=nightly-comfyui');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
update_policy_combo.value = 'stable-comfyui';
|
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) {
|
if (response.status == 200) {
|
||||||
infoToast(`ComfyUI version is switched to ${selected_version}`);
|
infoToast(`ComfyUI version is switched to ${selected_version}`);
|
||||||
}
|
}
|
||||||
@@ -656,18 +663,17 @@ async function onQueueStatus(event) {
|
|||||||
const isElectron = 'electronAPI' in window;
|
const isElectron = 'electronAPI' in window;
|
||||||
|
|
||||||
if(event.detail.status == 'in_progress') {
|
if(event.detail.status == 'in_progress') {
|
||||||
set_inprogress_mode();
|
// set_inprogress_mode();
|
||||||
update_all_button.innerText = `in progress.. (${event.detail.done_count}/${event.detail.total_count})`;
|
update_all_button.innerText = `in progress.. (${event.detail.done_count}/${event.detail.total_count})`;
|
||||||
}
|
}
|
||||||
else if(event.detail.status == 'done') {
|
else if(event.detail.status == 'all-done') {
|
||||||
reset_action_buttons();
|
reset_action_buttons();
|
||||||
|
}
|
||||||
if(!is_updating) {
|
else if(event.detail.status == 'batch-done') {
|
||||||
|
if(batch_id != event.detail.batch_id) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
is_updating = false;
|
|
||||||
|
|
||||||
let success_list = [];
|
let success_list = [];
|
||||||
let failed_list = [];
|
let failed_list = [];
|
||||||
let comfyui_state = null;
|
let comfyui_state = null;
|
||||||
@@ -767,41 +773,28 @@ api.addEventListener("cm-queue-status", onQueueStatus);
|
|||||||
async function updateAll(update_comfyui) {
|
async function updateAll(update_comfyui) {
|
||||||
update_all_button.innerText = "Updating...";
|
update_all_button.innerText = "Updating...";
|
||||||
|
|
||||||
set_inprogress_mode();
|
// set_inprogress_mode();
|
||||||
|
|
||||||
var mode = manager_instance.datasrc_combo.value;
|
var mode = manager_instance.datasrc_combo.value;
|
||||||
|
|
||||||
showTerminal();
|
showTerminal();
|
||||||
|
|
||||||
|
batch_id = generateUUID();
|
||||||
|
|
||||||
|
let batch = {};
|
||||||
if(update_comfyui) {
|
if(update_comfyui) {
|
||||||
update_all_button.innerText = "Updating ComfyUI...";
|
update_all_button.innerText = "Updating ComfyUI...";
|
||||||
await api.fetchApi('/manager/queue/update_comfyui');
|
batch['update_comfyui'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = await api.fetchApi(`/manager/queue/update_all?mode=${mode}`);
|
batch['update_all'] = mode;
|
||||||
|
|
||||||
if (response.status == 401) {
|
const res = await api.fetchApi(`/v2/manager/queue/batch`, {
|
||||||
customAlert('Another task is already in progress. Please stop the ongoing task first.');
|
method: 'POST',
|
||||||
}
|
body: JSON.stringify(batch)
|
||||||
else if(response.status == 200) {
|
});
|
||||||
is_updating = true;
|
|
||||||
await api.fetchApi('/manager/queue/start');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function newDOMTokenList(initialTokens) {
|
|
||||||
const tmp = document.createElement(`div`);
|
|
||||||
|
|
||||||
const classList = tmp.classList;
|
|
||||||
if (initialTokens) {
|
|
||||||
initialTokens.forEach(token => {
|
|
||||||
classList.add(token);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return classList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check whether the node is a potential output node (img, gif or video output)
|
* Check whether the node is a potential output node (img, gif or video output)
|
||||||
*/
|
*/
|
||||||
@@ -814,7 +807,7 @@ function restartOrStop() {
|
|||||||
rebootAPI();
|
rebootAPI();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
api.fetchApi('/manager/queue/reset');
|
api.fetchApi('/v2/manager/queue/reset');
|
||||||
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -962,12 +955,12 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
|
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
|
||||||
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, []));
|
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, []));
|
||||||
|
|
||||||
api.fetchApi('/manager/db_mode')
|
api.fetchApi('/v2/manager/db_mode')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => { this.datasrc_combo.value = data; });
|
.then(data => { this.datasrc_combo.value = data; });
|
||||||
|
|
||||||
this.datasrc_combo.addEventListener('change', function (event) {
|
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
|
// preview method
|
||||||
@@ -979,19 +972,19 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Preview method: Latent2RGB (fast)' }, []));
|
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)' }, []));
|
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(response => response.text())
|
||||||
.then(data => { preview_combo.value = data; });
|
.then(data => { preview_combo.value = data; });
|
||||||
|
|
||||||
preview_combo.addEventListener('change', function (event) {
|
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
|
// channel
|
||||||
let channel_combo = document.createElement("select");
|
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.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";
|
channel_combo.className = "cm-menu-combo";
|
||||||
api.fetchApi('/manager/channel_url_list')
|
api.fetchApi('/v2/manager/channel_url_list')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(async data => {
|
.then(async data => {
|
||||||
try {
|
try {
|
||||||
@@ -1004,7 +997,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
channel_combo.addEventListener('change', function (event) {
|
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;
|
channel_combo.value = data.selected;
|
||||||
@@ -1032,7 +1025,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
|
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
|
||||||
}
|
}
|
||||||
|
|
||||||
api.fetchApi('/manager/share_option')
|
api.fetchApi('/v2/manager/share_option')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
share_combo.value = data || 'all';
|
share_combo.value = data || 'all';
|
||||||
@@ -1042,7 +1035,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
share_combo.addEventListener('change', function (event) {
|
share_combo.addEventListener('change', function (event) {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
share_option = value;
|
share_option = value;
|
||||||
api.fetchApi(`/manager/share_option?value=${value}`);
|
api.fetchApi(`/v2/manager/share_option?value=${value}`);
|
||||||
const shareButton = document.getElementById("shareButton");
|
const shareButton = document.getElementById("shareButton");
|
||||||
if (value === 'none') {
|
if (value === 'none') {
|
||||||
shareButton.style.display = "none";
|
shareButton.style.display = "none";
|
||||||
@@ -1057,7 +1050,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: '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: 'higher', text: 'Component: Use higher version' }, []));
|
||||||
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my 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(response => response.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
component_policy_combo.value = data;
|
component_policy_combo.value = data;
|
||||||
@@ -1065,7 +1058,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
});
|
});
|
||||||
|
|
||||||
component_policy_combo.addEventListener('change', function (event) {
|
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);
|
set_component_policy(event.target.value);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1078,14 +1071,14 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
update_policy_combo.className = "cm-menu-combo";
|
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: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, []));
|
||||||
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly 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(response => response.text())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
update_policy_combo.value = data;
|
update_policy_combo.value = data;
|
||||||
});
|
});
|
||||||
|
|
||||||
update_policy_combo.addEventListener('change', function (event) {
|
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 [
|
return [
|
||||||
@@ -1388,12 +1381,12 @@ class ManagerMenuDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getVersion() {
|
async function getVersion() {
|
||||||
let version = await api.fetchApi(`/manager/version`);
|
let version = await api.fetchApi(`/v2/manager/version`);
|
||||||
return await version.text();
|
return await version.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.ManagerMenu",
|
name: "Comfy.Legacy.ManagerMenu",
|
||||||
|
|
||||||
aboutPageBadges: [
|
aboutPageBadges: [
|
||||||
{
|
{
|
||||||
@@ -1525,7 +1518,10 @@ app.registerExtension({
|
|||||||
}).element
|
}).element
|
||||||
);
|
);
|
||||||
|
|
||||||
app.menu?.settingsGroup.element.before(cmGroup.element);
|
const shouldShowLegacyMenuItems = isNotNewManagerUI();
|
||||||
|
if (shouldShowLegacyMenuItems) {
|
||||||
|
app.menu?.settingsGroup.element.before(cmGroup.element);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(exception) {
|
catch(exception) {
|
||||||
console.log('ComfyUI is outdated. New style menu based features are disabled.');
|
console.log('ComfyUI is outdated. New style menu based features are disabled.');
|
||||||
@@ -172,7 +172,7 @@ export const shareToEsheep= () => {
|
|||||||
const nodes = app.graph._nodes
|
const nodes = app.graph._nodes
|
||||||
const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
|
const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
|
||||||
const workflow = prompt['workflow']
|
const workflow = prompt['workflow']
|
||||||
api.fetchApi(`/manager/set_esheep_workflow_and_images`, {
|
api.fetchApi(`/v2/manager/set_esheep_workflow_and_images`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -812,7 +812,7 @@ export class ShareDialog extends ComfyDialog {
|
|||||||
// get the user's existing matrix auth and share key
|
// get the user's existing matrix auth and share key
|
||||||
ShareDialog.matrix_auth = { homeserver: "matrix.org", username: "", password: "" };
|
ShareDialog.matrix_auth = { homeserver: "matrix.org", username: "", password: "" };
|
||||||
try {
|
try {
|
||||||
api.fetchApi(`/manager/get_matrix_auth`)
|
api.fetchApi(`/v2/manager/get_matrix_auth`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
ShareDialog.matrix_auth = data;
|
ShareDialog.matrix_auth = data;
|
||||||
@@ -831,7 +831,7 @@ export class ShareDialog extends ComfyDialog {
|
|||||||
ShareDialog.cw_sharekey = "";
|
ShareDialog.cw_sharekey = "";
|
||||||
try {
|
try {
|
||||||
// console.log("Fetching comfyworkflows share key")
|
// console.log("Fetching comfyworkflows share key")
|
||||||
api.fetchApi(`/manager/get_comfyworkflows_auth`)
|
api.fetchApi(`/v2/manager/get_comfyworkflows_auth`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
ShareDialog.cw_sharekey = data.comfyworkflows_sharekey;
|
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
|
// Change the text of the share button to "Sharing..." to indicate that the share process has started
|
||||||
this.share_button.textContent = "Sharing...";
|
this.share_button.textContent = "Sharing...";
|
||||||
|
|
||||||
const response = await api.fetchApi(`/manager/share`, {
|
const response = await api.fetchApi(`/v2/manager/share`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -67,7 +67,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
|||||||
async readKey() {
|
async readKey() {
|
||||||
let key = ""
|
let key = ""
|
||||||
try {
|
try {
|
||||||
key = await api.fetchApi(`/manager/get_openart_auth`)
|
key = await api.fetchApi(`/v2/manager/get_openart_auth`)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
return data.openart_key;
|
return data.openart_key;
|
||||||
@@ -82,7 +82,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveKey(value) {
|
async saveKey(value) {
|
||||||
await api.fetchApi(`/manager/set_openart_auth`, {
|
await api.fetchApi(`/v2/manager/set_openart_auth`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -399,7 +399,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
|||||||
form.append("file", uploadFile);
|
form.append("file", uploadFile);
|
||||||
try {
|
try {
|
||||||
const res = await this.fetchApi(
|
const res = await this.fetchApi(
|
||||||
`/workflows/upload_thumbnail`,
|
`/v2/workflows/upload_thumbnail`,
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: form,
|
body: form,
|
||||||
@@ -459,7 +459,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
|||||||
throw new Error("Title is required");
|
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())
|
.then(response => response.json())
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
// console.log(error);
|
// console.log(error);
|
||||||
@@ -489,7 +489,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await this.fetchApi(
|
const response = await this.fetchApi(
|
||||||
"/workflows/publish",
|
"/v2/workflows/publish",
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {"Content-Type": "application/json"},
|
headers: {"Content-Type": "application/json"},
|
||||||
@@ -179,7 +179,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
|||||||
async loadToken() {
|
async loadToken() {
|
||||||
let key = ""
|
let key = ""
|
||||||
try {
|
try {
|
||||||
const response = await api.fetchApi(`/manager/youml/settings`)
|
const response = await api.fetchApi(`/v2/manager/youml/settings`)
|
||||||
const settings = await response.json()
|
const settings = await response.json()
|
||||||
return settings.token
|
return settings.token
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -188,7 +188,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async saveToken(value) {
|
async saveToken(value) {
|
||||||
await api.fetchApi(`/manager/youml/settings`, {
|
await api.fetchApi(`/v2/manager/youml/settings`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
@@ -380,7 +380,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
|||||||
try {
|
try {
|
||||||
let snapshotData = null;
|
let snapshotData = null;
|
||||||
try {
|
try {
|
||||||
const snapshot = await api.fetchApi(`/snapshot/get_current`)
|
const snapshot = await api.fetchApi(`/v2/snapshot/get_current`)
|
||||||
snapshotData = await snapshot.json()
|
snapshotData = await snapshot.json()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to get snapshot", e)
|
console.error("Failed to get snapshot", e)
|
||||||
@@ -172,7 +172,7 @@ export function rebootAPI() {
|
|||||||
customConfirm("Are you sure you'd like to reboot the server?").then((isConfirmed) => {
|
customConfirm("Are you sure you'd like to reboot the server?").then((isConfirmed) => {
|
||||||
if (isConfirmed) {
|
if (isConfirmed) {
|
||||||
try {
|
try {
|
||||||
api.fetchApi("/manager/reboot");
|
api.fetchApi("/v2/manager/reboot");
|
||||||
}
|
}
|
||||||
catch(exception) {}
|
catch(exception) {}
|
||||||
}
|
}
|
||||||
@@ -210,7 +210,7 @@ export async function install_pip(packages) {
|
|||||||
if(packages.includes('&'))
|
if(packages.includes('&'))
|
||||||
app.ui.dialog.show(`Invalid PIP package enumeration: '${packages}'`);
|
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",
|
method: "POST",
|
||||||
body: packages,
|
body: packages,
|
||||||
});
|
});
|
||||||
@@ -245,7 +245,7 @@ export async function install_via_git_url(url, manager_dialog) {
|
|||||||
|
|
||||||
show_message(`Wait...<BR><BR>Installing '${url}'`);
|
show_message(`Wait...<BR><BR>Installing '${url}'`);
|
||||||
|
|
||||||
const res = await api.fetchApi("/customnode/install/git_url", {
|
const res = await api.fetchApi("/v2/customnode/install/git_url", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: url,
|
body: url,
|
||||||
});
|
});
|
||||||
@@ -630,6 +630,14 @@ export function showTooltip(target, text, className = 'cn-tooltip', styleMap = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateUUID() {
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||||
|
const r = Math.random() * 16 | 0;
|
||||||
|
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||||
|
return v.toString(16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function initTooltip () {
|
function initTooltip () {
|
||||||
const mouseenterHandler = (e) => {
|
const mouseenterHandler = (e) => {
|
||||||
const target = e.target;
|
const target = e.target;
|
||||||
@@ -64,7 +64,7 @@ function storeGroupNode(name, data, register=true) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function load_components() {
|
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 components = await data.json();
|
||||||
|
|
||||||
let start_time = Date.now();
|
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;
|
pack_map[packname] = component_name;
|
||||||
rpack_map[component_name] = subgraph;
|
rpack_map[component_name] = subgraph;
|
||||||
|
|
||||||
const res = await api.fetchApi('/manager/component/save', {
|
const res = await api.fetchApi('/v2/manager/component/save', {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
@@ -259,7 +259,7 @@ async function import_component(component_name, component, mode) {
|
|||||||
workflow: component
|
workflow: component
|
||||||
};
|
};
|
||||||
|
|
||||||
const res = await api.fetchApi('/manager/component/save', {
|
const res = await api.fetchApi('/v2/manager/component/save', {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json", },
|
headers: { "Content-Type": "application/json", },
|
||||||
body: JSON.stringify(body)
|
body: JSON.stringify(body)
|
||||||
@@ -709,7 +709,7 @@ app.handleFile = handleFile;
|
|||||||
|
|
||||||
let current_component_policy = 'workflow';
|
let current_component_policy = 'workflow';
|
||||||
try {
|
try {
|
||||||
api.fetchApi('/manager/policy/component')
|
api.fetchApi('/v2/manager/policy/component')
|
||||||
.then(response => response.text())
|
.then(response => response.text())
|
||||||
.then(data => { current_component_policy = data; });
|
.then(data => { current_component_policy = data; });
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
|
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
|
||||||
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
|
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
|
||||||
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
|
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
|
||||||
showPopover, hidePopover
|
showPopover, hidePopover, generateUUID
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
|
|
||||||
// https://cenfun.github.io/turbogrid/api.html
|
// https://cenfun.github.io/turbogrid/api.html
|
||||||
@@ -66,7 +66,7 @@ export class CustomNodesManager {
|
|||||||
this.id = "cn-manager";
|
this.id = "cn-manager";
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.CustomNodesManager",
|
name: "Comfy.Legacy.CustomNodesManager",
|
||||||
afterConfigureGraph: (missingNodeTypes) => {
|
afterConfigureGraph: (missingNodeTypes) => {
|
||||||
const item = this.getFilterItem(ShowMode.MISSING);
|
const item = this.getFilterItem(ShowMode.MISSING);
|
||||||
if (item) {
|
if (item) {
|
||||||
@@ -459,7 +459,7 @@ export class CustomNodesManager {
|
|||||||
|
|
||||||
".cn-manager-stop": {
|
".cn-manager-stop": {
|
||||||
click: () => {
|
click: () => {
|
||||||
api.fetchApi('/manager/queue/reset');
|
api.fetchApi('/v2/manager/queue/reset');
|
||||||
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
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',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(info)
|
body: JSON.stringify(info)
|
||||||
@@ -1243,7 +1243,7 @@ export class CustomNodesManager {
|
|||||||
async loadNodes(node_packs) {
|
async loadNodes(node_packs) {
|
||||||
const mode = manager_instance.datasrc_combo.value;
|
const mode = manager_instance.datasrc_combo.value;
|
||||||
this.showStatus(`Loading node mappings (${mode}) ...`);
|
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) {
|
if (res.error) {
|
||||||
console.log(res.error);
|
console.log(res.error);
|
||||||
return;
|
return;
|
||||||
@@ -1395,10 +1395,10 @@ export class CustomNodesManager {
|
|||||||
this.showLoading();
|
this.showLoading();
|
||||||
let res;
|
let res;
|
||||||
if(is_enable) {
|
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 {
|
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();
|
this.hideLoading();
|
||||||
|
|
||||||
@@ -1410,16 +1410,15 @@ export class CustomNodesManager {
|
|||||||
let version_cnt = 0;
|
let version_cnt = 0;
|
||||||
|
|
||||||
if(!is_enable) {
|
if(!is_enable) {
|
||||||
|
|
||||||
if(rowItem.cnr_latest != rowItem.originalData.active_version && obj.length > 0) {
|
|
||||||
versions.push('latest');
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rowItem.originalData.active_version != 'nightly') {
|
if(rowItem.originalData.active_version != 'nightly') {
|
||||||
versions.push('nightly');
|
versions.push('nightly');
|
||||||
default_version = 'nightly';
|
default_version = 'nightly';
|
||||||
version_cnt++;
|
version_cnt++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(rowItem.cnr_latest != rowItem.originalData.active_version && obj.length > 0) {
|
||||||
|
versions.push('latest');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(let v of obj) {
|
for(let v of obj) {
|
||||||
@@ -1440,13 +1439,6 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async installNodes(list, btn, title, selected_version) {
|
async installNodes(list, btn, title, selected_version) {
|
||||||
let stats = await api.fetchApi('/manager/queue/status');
|
|
||||||
stats = await stats.json();
|
|
||||||
if(stats.is_processing) {
|
|
||||||
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { target, label, mode} = btn;
|
const { target, label, mode} = btn;
|
||||||
|
|
||||||
if(mode === "uninstall") {
|
if(mode === "uninstall") {
|
||||||
@@ -1473,10 +1465,10 @@ export class CustomNodesManager {
|
|||||||
let needRestart = false;
|
let needRestart = false;
|
||||||
let errorMsg = "";
|
let errorMsg = "";
|
||||||
|
|
||||||
await api.fetchApi('/manager/queue/reset');
|
|
||||||
|
|
||||||
let target_items = [];
|
let target_items = [];
|
||||||
|
|
||||||
|
let batch = {};
|
||||||
|
|
||||||
for (const hash of list) {
|
for (const hash of list) {
|
||||||
const item = this.grid.getRowItemBy("hash", hash);
|
const item = this.grid.getRowItemBy("hash", hash);
|
||||||
target_items.push(item);
|
target_items.push(item);
|
||||||
@@ -1518,23 +1510,11 @@ export class CustomNodesManager {
|
|||||||
api_mode = 'reinstall';
|
api_mode = 'reinstall';
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await api.fetchApi(`/manager/queue/${api_mode}`, {
|
if(batch[api_mode]) {
|
||||||
method: 'POST',
|
batch[api_mode].push(data);
|
||||||
body: JSON.stringify(data)
|
}
|
||||||
});
|
else {
|
||||||
|
batch[api_mode] = [data];
|
||||||
if (res.status != 200) {
|
|
||||||
errorMsg = `'${item.title}': `;
|
|
||||||
|
|
||||||
if(res.status == 403) {
|
|
||||||
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
|
||||||
} else if(res.status == 404) {
|
|
||||||
errorMsg += `With the current security level configuration, only custom nodes from the <B>"default channel"</B> can be installed.\n`;
|
|
||||||
} else {
|
|
||||||
errorMsg += await res.text() + '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1551,7 +1531,24 @@ export class CustomNodesManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await api.fetchApi('/manager/queue/start');
|
this.batch_id = generateUUID();
|
||||||
|
batch['batch_id'] = this.batch_id;
|
||||||
|
|
||||||
|
const res = await api.fetchApi(`/v2/manager/queue/batch`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(batch)
|
||||||
|
});
|
||||||
|
|
||||||
|
let failed = await res.json();
|
||||||
|
|
||||||
|
if(failed.length > 0) {
|
||||||
|
for(let k in failed) {
|
||||||
|
let hash = failed[k];
|
||||||
|
const item = this.grid.getRowItemBy("hash", hash);
|
||||||
|
errorMsg = `[FAIL] ${item.title}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.showStop();
|
this.showStop();
|
||||||
showTerminal();
|
showTerminal();
|
||||||
}
|
}
|
||||||
@@ -1559,6 +1556,9 @@ export class CustomNodesManager {
|
|||||||
|
|
||||||
async onQueueStatus(event) {
|
async onQueueStatus(event) {
|
||||||
let self = CustomNodesManager.instance;
|
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') {
|
if(event.detail.status == 'in_progress' && event.detail.ui_target == 'nodepack_manager') {
|
||||||
const hash = event.detail.target;
|
const hash = event.detail.target;
|
||||||
|
|
||||||
@@ -1569,7 +1569,7 @@ export class CustomNodesManager {
|
|||||||
self.grid.updateCell(item, "action");
|
self.grid.updateCell(item, "action");
|
||||||
self.grid.setRowSelected(item, false);
|
self.grid.setRowSelected(item, false);
|
||||||
}
|
}
|
||||||
else if(event.detail.status == 'done') {
|
else if(event.detail.status == 'batch-done' && event.detail.batch_id == self.batch_id) {
|
||||||
self.hideStop();
|
self.hideStop();
|
||||||
self.onQueueCompleted(event.detail);
|
self.onQueueCompleted(event.detail);
|
||||||
}
|
}
|
||||||
@@ -1745,7 +1745,7 @@ export class CustomNodesManager {
|
|||||||
async getMissingNodesLegacy(hashMap, missing_nodes) {
|
async getMissingNodesLegacy(hashMap, missing_nodes) {
|
||||||
const mode = manager_instance.datasrc_combo.value;
|
const mode = manager_instance.datasrc_combo.value;
|
||||||
this.showStatus(`Loading missing nodes (${mode}) ...`);
|
this.showStatus(`Loading missing nodes (${mode}) ...`);
|
||||||
const res = await fetchData(`/customnode/getmappings?mode=${mode}`);
|
const res = await fetchData(`/v2/customnode/getmappings?mode=${mode}`);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
this.showError(`Failed to get custom node mappings: ${res.error}`);
|
this.showError(`Failed to get custom node mappings: ${res.error}`);
|
||||||
return;
|
return;
|
||||||
@@ -1860,7 +1860,7 @@ export class CustomNodesManager {
|
|||||||
async getAlternatives() {
|
async getAlternatives() {
|
||||||
const mode = manager_instance.datasrc_combo.value;
|
const mode = manager_instance.datasrc_combo.value;
|
||||||
this.showStatus(`Loading alternatives (${mode}) ...`);
|
this.showStatus(`Loading alternatives (${mode}) ...`);
|
||||||
const res = await fetchData(`/customnode/alternatives?mode=${mode}`);
|
const res = await fetchData(`/v2/customnode/alternatives?mode=${mode}`);
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
this.showError(`Failed to get alternatives: ${res.error}`);
|
this.showError(`Failed to get alternatives: ${res.error}`);
|
||||||
return [];
|
return [];
|
||||||
@@ -1908,7 +1908,7 @@ export class CustomNodesManager {
|
|||||||
infoToast('Fetching updated information. This may take some time if many custom nodes are installed.');
|
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) {
|
if (res.error) {
|
||||||
this.showError("Failed to get custom node list.");
|
this.showError("Failed to get custom node list.");
|
||||||
this.hideLoading();
|
this.hideLoading();
|
||||||
@@ -3,7 +3,7 @@ import { $el } from "../../scripts/ui.js";
|
|||||||
import {
|
import {
|
||||||
manager_instance, rebootAPI,
|
manager_instance, rebootAPI,
|
||||||
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
||||||
storeColumnWidth, restoreColumnWidth, loadCss
|
storeColumnWidth, restoreColumnWidth, loadCss, generateUUID
|
||||||
} from "./common.js";
|
} from "./common.js";
|
||||||
import { api } from "../../scripts/api.js";
|
import { api } from "../../scripts/api.js";
|
||||||
|
|
||||||
@@ -81,13 +81,10 @@ export class ModelManager {
|
|||||||
value: ""
|
value: ""
|
||||||
}, {
|
}, {
|
||||||
label: "Installed",
|
label: "Installed",
|
||||||
value: "installed"
|
value: "True"
|
||||||
}, {
|
}, {
|
||||||
label: "Not Installed",
|
label: "Not Installed",
|
||||||
value: "not_installed"
|
value: "False"
|
||||||
}, {
|
|
||||||
label: "In Workflow",
|
|
||||||
value: "in_workflow"
|
|
||||||
}];
|
}];
|
||||||
|
|
||||||
this.typeList = [{
|
this.typeList = [{
|
||||||
@@ -175,7 +172,7 @@ export class ModelManager {
|
|||||||
|
|
||||||
".cmm-manager-stop": {
|
".cmm-manager-stop": {
|
||||||
click: () => {
|
click: () => {
|
||||||
api.fetchApi('/manager/queue/reset');
|
api.fetchApi('/v2/manager/queue/reset');
|
||||||
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -257,31 +254,12 @@ export class ModelManager {
|
|||||||
rowFilter: (rowItem) => {
|
rowFilter: (rowItem) => {
|
||||||
|
|
||||||
const searchableColumns = ["name", "type", "base", "description", "filename", "save_path"];
|
const searchableColumns = ["name", "type", "base", "description", "filename", "save_path"];
|
||||||
const models_extensions = ['.ckpt', '.pt', '.pt2', '.bin', '.pth', '.safetensors', '.pkl', '.sft'];
|
|
||||||
|
|
||||||
let shouldShown = grid.highlightKeywordsFilter(rowItem, searchableColumns, this.keywords);
|
let shouldShown = grid.highlightKeywordsFilter(rowItem, searchableColumns, this.keywords);
|
||||||
|
|
||||||
if (shouldShown) {
|
if (shouldShown) {
|
||||||
if(this.filter) {
|
if(this.filter && rowItem.installed !== this.filter) {
|
||||||
if (this.filter == "in_workflow") {
|
return false;
|
||||||
rowItem.in_workflow = null;
|
|
||||||
if (Array.isArray(app.graph._nodes)) {
|
|
||||||
app.graph._nodes.forEach((item, i) => {
|
|
||||||
if (Array.isArray(item.widgets_values)) {
|
|
||||||
item.widgets_values.forEach((_item, i) => {
|
|
||||||
if (rowItem.in_workflow === null && _item !== null && models_extensions.includes("." + _item.toString().split('.').pop())) {
|
|
||||||
let filename = _item.match(/([^\/]+)(?=\.\w+$)/)[0];
|
|
||||||
if (grid.highlightKeywordsFilter(rowItem, searchableColumns, filename)) {
|
|
||||||
rowItem.in_workflow = "True";
|
|
||||||
grid.highlightKeywordsFilter(rowItem, searchableColumns, "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ((this.filter == "installed" && rowItem.installed == "True") || (this.filter == "not_installed" && rowItem.installed == "False") || (this.filter == "in_workflow" && rowItem.in_workflow == "True"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(this.type && rowItem.type !== this.type) {
|
if(this.type && rowItem.type !== this.type) {
|
||||||
@@ -435,24 +413,16 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async installModels(list, btn) {
|
async installModels(list, btn) {
|
||||||
let stats = await api.fetchApi('/manager/queue/status');
|
|
||||||
|
|
||||||
stats = await stats.json();
|
|
||||||
if(stats.is_processing) {
|
|
||||||
customAlert(`[ComfyUI-Manager] There are already tasks in progress. Please try again after it is completed. (${stats.done_count}/${stats.total_count})`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
btn.classList.add("cmm-btn-loading");
|
btn.classList.add("cmm-btn-loading");
|
||||||
this.showError("");
|
this.showError("");
|
||||||
|
|
||||||
let needRefresh = false;
|
let needRefresh = false;
|
||||||
let errorMsg = "";
|
let errorMsg = "";
|
||||||
|
|
||||||
await api.fetchApi('/manager/queue/reset');
|
|
||||||
|
|
||||||
let target_items = [];
|
let target_items = [];
|
||||||
|
|
||||||
|
let batch = {};
|
||||||
|
|
||||||
for (const item of list) {
|
for (const item of list) {
|
||||||
this.grid.scrollRowIntoView(item);
|
this.grid.scrollRowIntoView(item);
|
||||||
target_items.push(item);
|
target_items.push(item);
|
||||||
@@ -468,21 +438,12 @@ export class ModelManager {
|
|||||||
const data = item.originalData;
|
const data = item.originalData;
|
||||||
data.ui_id = item.hash;
|
data.ui_id = item.hash;
|
||||||
|
|
||||||
const res = await api.fetchApi(`/manager/queue/install_model`, {
|
|
||||||
method: 'POST',
|
|
||||||
body: JSON.stringify(data)
|
|
||||||
});
|
|
||||||
|
|
||||||
if (res.status != 200) {
|
if(batch['install_model']) {
|
||||||
errorMsg = `'${item.name}': `;
|
batch['install_model'].push(data);
|
||||||
|
}
|
||||||
if(res.status == 403) {
|
else {
|
||||||
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
batch['install_model'] = [data];
|
||||||
} else {
|
|
||||||
errorMsg += await res.text() + '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -499,7 +460,24 @@ export class ModelManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
await api.fetchApi('/manager/queue/start');
|
this.batch_id = generateUUID();
|
||||||
|
batch['batch_id'] = this.batch_id;
|
||||||
|
|
||||||
|
const res = await api.fetchApi(`/v2/manager/queue/batch`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify(batch)
|
||||||
|
});
|
||||||
|
|
||||||
|
let failed = await res.json();
|
||||||
|
|
||||||
|
if(failed.length > 0) {
|
||||||
|
for(let k in failed) {
|
||||||
|
let hash = failed[k];
|
||||||
|
const item = self.grid.getRowItemBy("hash", hash);
|
||||||
|
errorMsg = `[FAIL] ${item.title}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.showStop();
|
this.showStop();
|
||||||
showTerminal();
|
showTerminal();
|
||||||
}
|
}
|
||||||
@@ -519,7 +497,7 @@ export class ModelManager {
|
|||||||
// self.grid.updateCell(item, "tg-column-select");
|
// self.grid.updateCell(item, "tg-column-select");
|
||||||
self.grid.updateRow(item);
|
self.grid.updateRow(item);
|
||||||
}
|
}
|
||||||
else if(event.detail.status == 'done') {
|
else if(event.detail.status == 'batch-done') {
|
||||||
self.hideStop();
|
self.hideStop();
|
||||||
self.onQueueCompleted(event.detail);
|
self.onQueueCompleted(event.detail);
|
||||||
}
|
}
|
||||||
@@ -645,7 +623,7 @@ export class ModelManager {
|
|||||||
|
|
||||||
const mode = manager_instance.datasrc_combo.value;
|
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) {
|
if (res.error) {
|
||||||
this.showError("Failed to get external model list.");
|
this.showError("Failed to get external model list.");
|
||||||
this.hideLoading();
|
this.hideLoading();
|
||||||
@@ -817,4 +795,4 @@ export class ModelManager {
|
|||||||
close() {
|
close() {
|
||||||
this.element.style.display = "none";
|
this.element.style.display = "none";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ function node_info_copy(src, dest, connect_both, copy_shape) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: "Comfy.Manager.NodeFixer",
|
name: "Comfy.Legacy.Manager.NodeFixer",
|
||||||
beforeRegisterNodeDef(nodeType, nodeData, app) {
|
beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||||
addMenuHandler(nodeType, function (_, options) {
|
addMenuHandler(nodeType, function (_, options) {
|
||||||
options.push({
|
options.push({
|
||||||
@@ -7,7 +7,7 @@ import { manager_instance, rebootAPI, show_message } from "./common.js";
|
|||||||
async function restore_snapshot(target) {
|
async function restore_snapshot(target) {
|
||||||
if(SnapshotManager.instance) {
|
if(SnapshotManager.instance) {
|
||||||
try {
|
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) {
|
if(response.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
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) {
|
async function remove_snapshot(target) {
|
||||||
if(SnapshotManager.instance) {
|
if(SnapshotManager.instance) {
|
||||||
try {
|
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) {
|
if(response.status == 403) {
|
||||||
show_message('This action is not allowed with this security level configuration.');
|
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() {
|
async function save_current_snapshot() {
|
||||||
try {
|
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();
|
app.ui.dialog.close();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ async function save_current_snapshot() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getSnapshotList() {
|
async function getSnapshotList() {
|
||||||
const response = await api.fetchApi(`/snapshot/getlist`);
|
const response = await api.fetchApi(`/v2/snapshot/getlist`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -38,7 +38,7 @@ class WorkflowMetadataExtension {
|
|||||||
* enabled is true if the node is enabled, false if it is disabled
|
* enabled is true if the node is enabled, false if it is disabled
|
||||||
*/
|
*/
|
||||||
async getInstalledNodes() {
|
async getInstalledNodes() {
|
||||||
const res = await api.fetchApi("/customnode/installed");
|
const res = await api.fetchApi("/v2/customnode/installed");
|
||||||
return await res.json();
|
return await res.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +70,8 @@ class WorkflowMetadataExtension {
|
|||||||
if (cnr_id === "comfy-core") return; // don't allow hijacking comfy-core name
|
if (cnr_id === "comfy-core") return; // don't allow hijacking comfy-core name
|
||||||
if (cnr_id) nodeProperties.cnr_id = cnr_id;
|
if (cnr_id) nodeProperties.cnr_id = cnr_id;
|
||||||
else nodeProperties.aux_id = aux_id;
|
else nodeProperties.aux_id = aux_id;
|
||||||
if (ver) nodeProperties.ver = ver.trim();
|
if (ver) nodeProperties.ver = ver;
|
||||||
} else if (["nodes", "comfy_extras", "comfy_api_nodes"].includes(moduleType)) {
|
} else if (["nodes", "comfy_extras"].includes(moduleType)) {
|
||||||
nodeProperties.cnr_id = "comfy-core";
|
nodeProperties.cnr_id = "comfy-core";
|
||||||
nodeProperties.ver = this.comfyCoreVersion;
|
nodeProperties.ver = this.comfyCoreVersion;
|
||||||
}
|
}
|
||||||
@@ -12,13 +12,10 @@ import ast
|
|||||||
import logging
|
import logging
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
glob_path = os.path.join(os.path.dirname(__file__), "glob")
|
from .glob import security_check
|
||||||
sys.path.append(glob_path)
|
from .glob import manager_util
|
||||||
|
from .glob import cm_global
|
||||||
import security_check
|
from .glob import manager_downloader
|
||||||
import manager_util
|
|
||||||
import cm_global
|
|
||||||
import manager_downloader
|
|
||||||
import folder_paths
|
import folder_paths
|
||||||
|
|
||||||
manager_util.add_python_path_to_env()
|
manager_util.add_python_path_to_env()
|
||||||
@@ -40,8 +37,8 @@ else:
|
|||||||
|
|
||||||
security_check.security_check()
|
security_check.security_check()
|
||||||
|
|
||||||
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||||
|
|
||||||
|
|
||||||
def skip_pip_spam(x):
|
def skip_pip_spam(x):
|
||||||
@@ -68,15 +65,17 @@ comfy_path = os.environ.get('COMFYUI_PATH')
|
|||||||
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
||||||
|
|
||||||
if comfy_path is None:
|
if comfy_path is None:
|
||||||
# legacy env var
|
try:
|
||||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
||||||
|
os.environ['COMFYUI_PATH'] = comfy_path
|
||||||
if comfy_path is None:
|
except:
|
||||||
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
print("[ComfyUI-Manager] environment variable 'COMFYUI_PATH' is not specified.")
|
||||||
|
exit(-1)
|
||||||
|
|
||||||
if comfy_base_path is None:
|
if comfy_base_path is None:
|
||||||
comfy_base_path = comfy_path
|
comfy_base_path = comfy_path
|
||||||
|
|
||||||
|
|
||||||
sys.__comfyui_manager_register_message_collapse = register_message_collapse
|
sys.__comfyui_manager_register_message_collapse = register_message_collapse
|
||||||
sys.__comfyui_manager_is_import_failed_extension = is_import_failed_extension
|
sys.__comfyui_manager_is_import_failed_extension = is_import_failed_extension
|
||||||
cm_global.register_api('cm.register_message_collapse', register_message_collapse)
|
cm_global.register_api('cm.register_message_collapse', register_message_collapse)
|
||||||
@@ -121,17 +120,11 @@ read_config()
|
|||||||
read_uv_mode()
|
read_uv_mode()
|
||||||
check_file_logging()
|
check_file_logging()
|
||||||
|
|
||||||
if sys.version_info < (3, 13):
|
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
|
||||||
else:
|
|
||||||
cm_global.pip_overrides = {}
|
|
||||||
|
|
||||||
if os.path.exists(manager_pip_overrides_path):
|
if os.path.exists(manager_pip_overrides_path):
|
||||||
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||||
cm_global.pip_overrides = json.load(json_file)
|
cm_global.pip_overrides = json.load(json_file)
|
||||||
|
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
||||||
if sys.version_info < (3, 13):
|
|
||||||
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
|
||||||
|
|
||||||
|
|
||||||
if os.path.exists(manager_pip_blacklist_path):
|
if os.path.exists(manager_pip_blacklist_path):
|
||||||
@@ -398,7 +391,11 @@ try:
|
|||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
global is_start_mode
|
global is_start_mode
|
||||||
|
|
||||||
message = record.getMessage()
|
try:
|
||||||
|
message = record.getMessage()
|
||||||
|
except Exception as e:
|
||||||
|
message = f"<<logging error>>: {record} - {e}"
|
||||||
|
original_stderr.write(message)
|
||||||
|
|
||||||
if is_start_mode:
|
if is_start_mode:
|
||||||
match = re.search(pat_import_fail, message)
|
match = re.search(pat_import_fail, message)
|
||||||
@@ -626,7 +623,6 @@ def execute_lazy_install_script(repo_path, executable):
|
|||||||
lines = manager_util.robust_readlines(requirements_path)
|
lines = manager_util.robust_readlines(requirements_path)
|
||||||
for line in lines:
|
for line in lines:
|
||||||
package_name = remap_pip_package(line.strip())
|
package_name = remap_pip_package(line.strip())
|
||||||
package_name = package_name.split('#')[0].strip()
|
|
||||||
if package_name and not is_installed(package_name):
|
if package_name and not is_installed(package_name):
|
||||||
if '--index-url' in package_name:
|
if '--index-url' in package_name:
|
||||||
s = package_name.split('--index-url')
|
s = package_name.split('--index-url')
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
8940
github-stats.json
8940
github-stats.json
File diff suppressed because it is too large
Load Diff
105
model-list.json
105
model-list.json
@@ -749,8 +749,8 @@
|
|||||||
"save_path": "loras/HyperSD/SDXL",
|
"save_path": "loras/HyperSD/SDXL",
|
||||||
"description": "Hyper-SD LoRA (4steps) - SDXL",
|
"description": "Hyper-SD LoRA (4steps) - SDXL",
|
||||||
"reference": "https://huggingface.co/ByteDance/Hyper-SD",
|
"reference": "https://huggingface.co/ByteDance/Hyper-SD",
|
||||||
"filename": "Hyper-SDXL-4steps-lora.safetensors",
|
"filename": "Hyper-SD15-4steps-lora.safetensors",
|
||||||
"url": "https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-SDXL-4steps-lora.safetensors",
|
"url": "https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-SD15-4steps-lora.safetensors",
|
||||||
"size": "787MB"
|
"size": "787MB"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -4953,107 +4953,6 @@
|
|||||||
"filename": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
"filename": "umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
||||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
||||||
"size": "6.74GB"
|
"size": "6.74GB"
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "lllyasviel/FramePackI2V_HY",
|
|
||||||
"type": "FramePackI2V",
|
|
||||||
"base": "FramePackI2V",
|
|
||||||
"save_path": "diffusers/lllyasviel",
|
|
||||||
"description": "[SNAPSHOT] This is the f1k1_x_g9_f1k1f2k2f16k4_td FramePack for HY. [w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
|
||||||
"reference": "https://huggingface.co/lllyasviel/FramePackI2V_HY",
|
|
||||||
"filename": "<huggingface>",
|
|
||||||
"url": "lllyasviel/FramePackI2V_HY",
|
|
||||||
"size": "25.75GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "LTX-Video Spatial Upscaler v0.9.7",
|
|
||||||
"type": "upscale",
|
|
||||||
"base": "upscale",
|
|
||||||
"save_path": "default",
|
|
||||||
"description": "Spatial upscaler model for LTX-Video. This model enhances the spatial resolution of generated videos.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-spatial-upscaler-0.9.7.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-spatial-upscaler-0.9.7.safetensors",
|
|
||||||
"size": "505MB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video Temporal Upscaler v0.9.7",
|
|
||||||
"type": "upscale",
|
|
||||||
"base": "upscale",
|
|
||||||
"save_path": "default",
|
|
||||||
"description": "Temporal upscaler model for LTX-Video. This model enhances the temporal resolution and smoothness of generated videos.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-temporal-upscaler-0.9.7.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-temporal-upscaler-0.9.7.safetensors",
|
|
||||||
"size": "524MB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "High-resolution quality LTX-Video 13B model.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-dev.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-dev.safetensors",
|
|
||||||
"size": "28.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B FP8 v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "Quantized version of the LTX-Video 13B model, optimized for lower VRAM usage while maintaining high quality.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-dev-fp8.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-dev-fp8.safetensors",
|
|
||||||
"size": "15.7GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B Distilled v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "Distilled version of the LTX-Video 13B model, providing improved efficiency while maintaining high-resolution quality.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-distilled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled.safetensors",
|
|
||||||
"size": "28.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B Distilled FP8 v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "Quantized distilled version of the LTX-Video 13B model, optimized for even lower VRAM usage while maintaining quality.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-distilled-fp8.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-fp8.safetensors",
|
|
||||||
"size": "15.7GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B Distilled LoRA v0.9.7",
|
|
||||||
"type": "lora",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "loras",
|
|
||||||
"description": "A LoRA adapter that transforms the standard LTX-Video 13B model into a distilled version when loaded.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-distilled-lora128.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-lora128.safetensors",
|
|
||||||
"size": "1.33GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Latent Bridge Matching for Image Relighting",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "LBM",
|
|
||||||
"save_path": "diffusion_models/LBM",
|
|
||||||
"description": "Latent Bridge Matching (LBM) Relighting model",
|
|
||||||
"reference": "https://huggingface.co/jasperai/LBM_relighting",
|
|
||||||
"filename": "LBM_relighting.safetensors",
|
|
||||||
"url": "https://huggingface.co/jasperai/LBM_relighting/resolve/main/model.safetensors",
|
|
||||||
"size": "5.02GB"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -10,341 +10,6 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{
|
|
||||||
"author": "syaofox",
|
|
||||||
"title": "ComfyUI_fnodes [REMOVED]",
|
|
||||||
"reference": "https://github.com/syaofox/ComfyUI_fnodes",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/syaofox/ComfyUI_fnodes"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "ComfyUI_fnodes is a collection of custom nodes designed for ComfyUI. These nodes provide additional functionality that can enhance your ComfyUI workflows.\nFile manipulation tools, Image resizing tools, IPAdapter tools, Image processing tools, Mask tools, Face analysis tools, Sampler tools, Miscellaneous tools"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Hangover3832",
|
|
||||||
"title": "ComfyUI-Hangover-Moondream [DEPRECATED]",
|
|
||||||
"reference": "https://github.com/Hangover3832/ComfyUI-Hangover-Moondream",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Hangover3832/ComfyUI-Hangover-Moondream"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Moondream is a lightweight multimodal large language model.\n[w/WARN:Additional python code will be downloaded from huggingface and executed. You have to trust this creator if you want to use this node!]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Hangover3832",
|
|
||||||
"title": "Recognize Anything Model (RAM) for ComfyUI [DEPRECATED]",
|
|
||||||
"reference": "https://github.com/Hangover3832/ComfyUI-Hangover-Recognize_Anything",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Hangover3832/ComfyUI-Hangover-Recognize_Anything"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "This is an image recognition node for ComfyUI based on the RAM++ model from [a/xinyu1205](https://huggingface.co/xinyu1205).\nThis node outputs a string of tags with all the recognized objects and elements in the image in English or Chinese language.\nFor image tagging and captioning."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Hangover3832",
|
|
||||||
"title": "ComfyUI-Hangover-Nodes [DEPRECATED]",
|
|
||||||
"reference": "https://github.com/Hangover3832/ComfyUI-Hangover-Nodes",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Hangover3832/ComfyUI-Hangover-Nodes"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Nodes: MS kosmos-2 Interrogator, Save Image w/o Metadata, Image Scale Bounding Box. An implementation of Microsoft [a/kosmos-2](https://huggingface.co/microsoft/kosmos-2-patch14-224) image to text transformer."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "SirLatore",
|
|
||||||
"title": "ComfyUI-IPAdapterWAN [REMOVED]",
|
|
||||||
"reference": "https://github.com/SirLatore/ComfyUI-IPAdapterWAN",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/SirLatore/ComfyUI-IPAdapterWAN"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "This extension adapts the [a/InstantX IP-Adapter for SD3.5-Large](https://huggingface.co/InstantX/SD3.5-Large-IP-Adapter) to work with Wan 2.1 and other UNet-based video/image models in ComfyUI.\nUnlike the original SD3 version (which depends on joint_blocks from MMDiT), this version performs sampling-time identity conditioning by dynamically injecting into attention layers — making it compatible with models like Wan 2.1, AnimateDiff, and other non-SD3 pipelines."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Jpzz",
|
|
||||||
"title": "ComfyUI-VirtualInteraction [UNSAFE/REMOVED]",
|
|
||||||
"reference": "https://github.com/Jpzz/ComfyUI-VirtualInteraction",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Jpzz/ComfyUI-VirtualInteraction"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: virtual interaction custom node when using generative movie\n[w/This nodepack contains a node which is reading arbitrary excel file.]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "satche",
|
|
||||||
"title": "Prompt Factory [REMOVED]",
|
|
||||||
"reference": "https://github.com/satche/comfyui-prompt-factory",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/satche/comfyui-prompt-factory"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A modular system that adds randomness to prompt generation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "MITCAP",
|
|
||||||
"title": "ComfyUI OpenAI DALL-E 3 Node [REMOVED]",
|
|
||||||
"reference": "https://github.com/MITCAP/OpenAI-ComfyUI",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/MITCAP/OpenAI-ComfyUI"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "This project provides custom nodes for ComfyUI that integrate with OpenAI's DALL-E 3 and GPT-4o models. The nodes allow users to generate images and describe images using OpenAI's API.\nNOTE: The files in the repo are not organized."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "raspie10032",
|
|
||||||
"title": "ComfyUI NAI Prompt Converter [REMOVED]",
|
|
||||||
"reference": "https://github.com/raspie10032/ComfyUI_RS_NAI_Local_Prompt_converter",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/raspie10032/ComfyUI_RS_NAI_Local_Prompt_converter"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A custom node extension for ComfyUI that enables conversion between different prompt formats: NovelAI V4, ComfyUI, and old NovelAI."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "holchan",
|
|
||||||
"title": "ComfyUI-ModelDownloader [REMOVED]",
|
|
||||||
"reference": "https://github.com/holchan/ComfyUI-ModelDownloader",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/holchan/ComfyUI-ModelDownloader"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A ComfyUI node to download models(Checkpoints and LoRA) from external links and act as an output standalone node."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Kur0butiMegane",
|
|
||||||
"title": "Comfyui-StringUtils [DEPRECATED]",
|
|
||||||
"reference": "https://github.com/Kur0butiMegane/Comfyui-StringUtils",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Kur0butiMegane/Comfyui-StringUtils"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: Prompt Normalizer, String Splitter, String Line Selector, Extract Markup Value"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Apache0ne",
|
|
||||||
"title": "ComfyUI-LantentCompose [REMOVED]",
|
|
||||||
"reference": "https://github.com/Apache0ne/ComfyUI-LantentCompose",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Apache0ne/ComfyUI-LantentCompose"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Interpolate sdxl latents using slerp with and without a mask. use with unsample nodes for best effect.\nNOTE: The files in the repo are not organized."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "jax-explorer",
|
|
||||||
"title": "ComfyUI-H-flow [REMOVED]",
|
|
||||||
"reference": "https://github.com/jax-explorer/ComfyUI-H-flow",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/jax-explorer/ComfyUI-H-flow"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: Wan2-1 Image To Video, LLM Task, Save Image, Save Video, Show Text, FluxPro Ultra, IdeogramV2 Turbo, Runway Image To Video, Kling Image To Video, Replace Text, Join Text, Test Image, Test Text"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Apache0ne",
|
|
||||||
"title": "SambaNova [REMOVED]",
|
|
||||||
"id": "SambaNovaAPI",
|
|
||||||
"reference": "https://github.com/Apache0ne/SambaNova",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Apache0ne/SambaNova"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Super Fast LLM's llama3.1-405B,70B,8B and more"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Apache0ne",
|
|
||||||
"title": "ComfyUI-EasyUrlLoader [REMOVED]",
|
|
||||||
"id": "easy-url-loader",
|
|
||||||
"reference": "https://github.com/Apache0ne/ComfyUI-EasyUrlLoader",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Apache0ne/ComfyUI-EasyUrlLoader"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A simple YT downloader node for ComfyUI using video Urls. Can be used with VHS nodes etc."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "nxt5656",
|
|
||||||
"title": "ComfyUI-Image2OSS [REMOVED]",
|
|
||||||
"reference": "https://github.com/nxt5656/ComfyUI-Image2OSS",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/nxt5656/ComfyUI-Image2OSS"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Upload the image to Alibaba Cloud OSS."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "ainewsto",
|
|
||||||
"title": "Comfyui_Comfly",
|
|
||||||
"reference": "https://github.com/ainewsto/Comfyui_Comfly",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/ainewsto/Comfyui_Comfly"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: Comfly_Mj, Comfly_mjstyle, Comfly_upload, Comfly_Mju, Comfly_Mjv, Comfly_kling_videoPreview\nNOTE: Comfyui_Comfly_v2 is introduced."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-to-inpaint",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-to-inpaint",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-to-inpaint"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Send preview image to inpaint workflow."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "magic-quill",
|
|
||||||
"title": "ComfyUI_MagicQuill [NOT MAINTAINED]",
|
|
||||||
"id": "MagicQuill",
|
|
||||||
"reference": "https://github.com/magic-quill/ComfyUI_MagicQuill",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/magic-quill/ComfyUI_MagicQuill"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Towards GPT-4 like large language and visual assistant.\nNOTE: The current version has not been maintained for a long time and does not work. Please use https://github.com/brantje/ComfyUI_MagicQuill instead."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-event-handler [USAFE/REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-event-handler",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-event-handler"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Javascript code will run when an event fires. [w/This node allows you to execute arbitrary JavaScript code as input for the workflow.]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Moooonet",
|
|
||||||
"title": "ComfyUI-ArteMoon [REMOVED]",
|
|
||||||
"reference": "https://github.com/Moooonet/ComfyUI-ArteMoon",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Moooonet/ComfyUI-ArteMoon"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "This plugin works with [a/IF_AI_Tools](https://github.com/if-ai/ComfyUI-IF_AI_tools) to build a workflow in ComfyUI that uses AI to assist in generating prompts."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "ryanontheinside",
|
|
||||||
"title": "ComfyUI-MediaPipe-Vision [REMOVED]",
|
|
||||||
"reference": "https://github.com/ryanontheinside/ComfyUI-MediaPipe-Vision",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/ryanontheinside/ComfyUI-MediaPipe-Vision"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A centralized wrapper of all MediaPipe vision tasks for ComfyUI."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-textarea-command [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-textarea-command",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-textarea-command"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Add command and comment in textarea. (e.g. // Disabled line)"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-parse-image [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-parse-image",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-parse-image"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Extract metadata from image."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "shinich39",
|
|
||||||
"title": "comfyui-put-image [REMOVED]",
|
|
||||||
"reference": "https://github.com/shinich39/comfyui-put-image",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/shinich39/comfyui-put-image"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Load image from directory."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "fredconex",
|
|
||||||
"title": "TripoSG Nodes for ComfyUI [REMOVED]",
|
|
||||||
"reference": "https://github.com/fredconex/ComfyUI-TripoSG",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/fredconex/ComfyUI-TripoSG"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Created by Alfredo Fernandes inspired by Hunyuan3D nodes by Kijai. This extension adds TripoSG 3D mesh generation capabilities to ComfyUI, allowing you to generate 3D meshes from a single image using the TripoSG model."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "fredconex",
|
|
||||||
"title": "ComfyUI-PaintTurbo [REMOVED]",
|
|
||||||
"reference": "https://github.com/fredconex/ComfyUI-PaintTurbo",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/fredconex/ComfyUI-PaintTurbo"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "NODES: Hunyuan3D Texture Mesh"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "zhuanqianfish",
|
|
||||||
"title": "TaesdDecoder [REMOVED]",
|
|
||||||
"reference": "https://github.com/zhuanqianfish/TaesdDecoder",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/zhuanqianfish/TaesdDecoder"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "use TAESD decoded image.you need donwload taesd_decoder.pth and taesdxl_decoder.pth to vae_approx folder first.\n It will result in a slight loss of image quality and a significant decrease in peak video memory during decoding."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "myAiLemon",
|
|
||||||
"title": "MagicAutomaticPicture [REMOVED]",
|
|
||||||
"reference": "https://github.com/myAiLemon/MagicAutomaticPicture",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/myAiLemon/MagicAutomaticPicture"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A comfyui node package that can generate pictures and automatically save positive prompts and eliminate unwanted prompts"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "thisiseddy-ab",
|
|
||||||
"title": "ComfyUI-Edins-Ultimate-Pack [REMOVED]",
|
|
||||||
"reference": "https://github.com/thisiseddy-ab/ComfyUI-Edins-Ultimate-Pack",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/thisiseddy-ab/ComfyUI-Edins-Ultimate-Pack"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Well i needet a Tiled Ksampler that still works for Comfy UI there were none so i made one, in this Package i will put all Nodes i will develop for Comfy Ui still in beta alot will change.."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "Davros666",
|
|
||||||
"title": "safetriggers [REMOVED]",
|
|
||||||
"reference": "https://github.com/Davros666/safetriggers",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Davros666/safetriggers"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "ComfyUI Nodes for READING TRIGGERS, TRIGGER-WORDS, TRIGGER-PHRASES FROM LoRAs"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "cubiq",
|
|
||||||
"title": "Simple Math [REMOVED]",
|
|
||||||
"id": "simplemath",
|
|
||||||
"reference": "https://github.com/cubiq/ComfyUI_SimpleMath",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/cubiq/ComfyUI_SimpleMath"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "custom node for ComfyUI to perform simple math operations"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"author": "lucafoscili",
|
|
||||||
"title": "LF Nodes [DEPRECATED]",
|
|
||||||
"reference": "https://github.com/lucafoscili/comfyui-lf",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/lucafoscili/comfyui-lf"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "Custom nodes with a touch of extra UX, including: history for primitives, JSON manipulation, logic switches with visual feedback, LLM chat... and more!"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "AI2lab",
|
"author": "AI2lab",
|
||||||
"title": "comfyUI-tool-2lab [REMOVED]",
|
"title": "comfyUI-tool-2lab [REMOVED]",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,106 +1,5 @@
|
|||||||
{
|
{
|
||||||
"models": [
|
"models": [
|
||||||
{
|
|
||||||
"name": "Latent Bridge Matching for Image Relighting",
|
|
||||||
"type": "diffusion_model",
|
|
||||||
"base": "LBM",
|
|
||||||
"save_path": "diffusion_models/LBM",
|
|
||||||
"description": "Latent Bridge Matching (LBM) Relighting model",
|
|
||||||
"reference": "https://huggingface.co/jasperai/LBM_relighting",
|
|
||||||
"filename": "LBM_relighting.safetensors",
|
|
||||||
"url": "https://huggingface.co/jasperai/LBM_relighting/resolve/main/model.safetensors",
|
|
||||||
"size": "5.02GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B Distilled v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "Distilled version of the LTX-Video 13B model, providing improved efficiency while maintaining high-resolution quality.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-distilled.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled.safetensors",
|
|
||||||
"size": "28.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B Distilled FP8 v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "Quantized distilled version of the LTX-Video 13B model, optimized for even lower VRAM usage while maintaining quality.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-distilled-fp8.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-fp8.safetensors",
|
|
||||||
"size": "15.7GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B Distilled LoRA v0.9.7",
|
|
||||||
"type": "lora",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "loras",
|
|
||||||
"description": "A LoRA adapter that transforms the standard LTX-Video 13B model into a distilled version when loaded.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-distilled-lora128.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-distilled-lora128.safetensors",
|
|
||||||
"size": "1.33GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "lllyasviel/FramePackI2V_HY",
|
|
||||||
"type": "FramePackI2V",
|
|
||||||
"base": "FramePackI2V",
|
|
||||||
"save_path": "diffusers/lllyasviel",
|
|
||||||
"description": "[SNAPSHOT] This is the f1k1_x_g9_f1k1f2k2f16k4_td FramePack for HY. [w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
|
||||||
"reference": "https://huggingface.co/lllyasviel/FramePackI2V_HY",
|
|
||||||
"filename": "<huggingface>",
|
|
||||||
"url": "lllyasviel/FramePackI2V_HY",
|
|
||||||
"size": "25.75GB"
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"name": "LTX-Video Spatial Upscaler v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "Spatial upscaler model for LTX-Video. This model enhances the spatial resolution of generated videos.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-spatial-upscaler-0.9.7.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-spatial-upscaler-0.9.7.safetensors",
|
|
||||||
"size": "505MB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video Temporal Upscaler v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "Temporal upscaler model for LTX-Video. This model enhances the temporal resolution and smoothness of generated videos.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-temporal-upscaler-0.9.7.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-temporal-upscaler-0.9.7.safetensors",
|
|
||||||
"size": "524MB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "High-resolution quality LTX-Video 13B model.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-dev.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-dev.safetensors",
|
|
||||||
"size": "28.6GB"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "LTX-Video 13B FP8 v0.9.7",
|
|
||||||
"type": "checkpoint",
|
|
||||||
"base": "LTX-Video",
|
|
||||||
"save_path": "checkpoints/LTXV",
|
|
||||||
"description": "Quantized version of the LTX-Video 13B model, optimized for lower VRAM usage while maintaining high quality.",
|
|
||||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
|
||||||
"filename": "ltxv-13b-0.9.7-dev-fp8.safetensors",
|
|
||||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltxv-13b-0.9.7-dev-fp8.safetensors",
|
|
||||||
"size": "15.7GB"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (bf16)",
|
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (bf16)",
|
||||||
"type": "diffusion_model",
|
"type": "diffusion_model",
|
||||||
@@ -691,6 +590,121 @@
|
|||||||
"filename": "sigclip_vision_patch14_384.safetensors",
|
"filename": "sigclip_vision_patch14_384.safetensors",
|
||||||
"url": "https://huggingface.co/Comfy-Org/sigclip_vision_384/resolve/main/sigclip_vision_patch14_384.safetensors",
|
"url": "https://huggingface.co/Comfy-Org/sigclip_vision_384/resolve/main/sigclip_vision_patch14_384.safetensors",
|
||||||
"size": "857MB"
|
"size": "857MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "comfyanonymous/flux_text_encoders - t5xxl (fp16)",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "t5",
|
||||||
|
"save_path": "text_encoders/t5",
|
||||||
|
"description": "Text Encoders for FLUX (fp16)",
|
||||||
|
"reference": "https://huggingface.co/comfyanonymous/flux_text_encoders",
|
||||||
|
"filename": "t5xxl_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/t5xxl_fp16.safetensors",
|
||||||
|
"size": "9.79GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "comfyanonymous/flux_text_encoders - t5xxl (fp8_e4m3fn)",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "t5",
|
||||||
|
"save_path": "text_encoders/t5",
|
||||||
|
"description": "Text Encoders for FLUX (fp8_e4m3fn)",
|
||||||
|
"reference": "https://huggingface.co/comfyanonymous/flux_text_encoders",
|
||||||
|
"filename": "t5xxl_fp8_e4m3fn.safetensors",
|
||||||
|
"url": "https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/t5xxl_fp8_e4m3fn.safetensors",
|
||||||
|
"size": "4.89GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "comfyanonymous/flux_text_encoders - t5xxl (fp8_e4m3fn_scaled)",
|
||||||
|
"type": "clip",
|
||||||
|
"base": "t5",
|
||||||
|
"save_path": "text_encoders/t5",
|
||||||
|
"description": "Text Encoders for FLUX (fp16)",
|
||||||
|
"reference": "https://huggingface.co/comfyanonymous/flux_text_encoders",
|
||||||
|
"filename": "t5xxl_fp8_e4m3fn_scaled.safetensors",
|
||||||
|
"url": "https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/t5xxl_fp8_e4m3fn_scaled.safetensors",
|
||||||
|
"size": "5.16GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "FLUX.1 [Dev] Diffusion model (scaled fp8)",
|
||||||
|
"type": "diffusion_model",
|
||||||
|
"base": "FLUX.1",
|
||||||
|
"save_path": "diffusion_models/FLUX1",
|
||||||
|
"description": "FLUX.1 [Dev] Diffusion model (scaled fp8)[w/Due to the large size of the model, it is recommended to download it through a browser if possible.]",
|
||||||
|
"reference": "https://huggingface.co/comfyanonymous/flux_dev_scaled_fp8_test",
|
||||||
|
"filename": "flux_dev_fp8_scaled_diffusion_model.safetensors",
|
||||||
|
"url": "https://huggingface.co/comfyanonymous/flux_dev_scaled_fp8_test/resolve/main/flux_dev_fp8_scaled_diffusion_model.safetensors",
|
||||||
|
"size": "11.9GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "kijai/MoGe_ViT_L_fp16.safetensors",
|
||||||
|
"type": "MoGe",
|
||||||
|
"base": "MoGe",
|
||||||
|
"save_path": "MoGe",
|
||||||
|
"description": "Safetensors versions of [a/https://github.com/microsoft/MoGe](https://github.com/microsoft/MoGe)",
|
||||||
|
"reference": "https://huggingface.co/Kijai/MoGe_safetensors",
|
||||||
|
"filename": "MoGe_ViT_L_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/MoGe_safetensors/resolve/main/MoGe_ViT_L_fp16.safetensors",
|
||||||
|
"size": "628MB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "kijai/MoGe_ViT_L_fp16.safetensors",
|
||||||
|
"type": "MoGe",
|
||||||
|
"base": "MoGe",
|
||||||
|
"save_path": "MoGe",
|
||||||
|
"description": "Safetensors versions of [a/https://github.com/microsoft/MoGe](https://github.com/microsoft/MoGe)",
|
||||||
|
"reference": "https://huggingface.co/Kijai/MoGe_safetensors",
|
||||||
|
"filename": "MoGe_ViT_L_fp16.safetensors",
|
||||||
|
"url": "https://huggingface.co/Kijai/MoGe_safetensors/resolve/main/MoGe_ViT_L_fp16.safetensors",
|
||||||
|
"size": "1.26GB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "pulid_flux_v0.9.1.safetensors",
|
||||||
|
"type": "PuLID",
|
||||||
|
"base": "FLUX",
|
||||||
|
"save_path": "pulid",
|
||||||
|
"description": "This is required for PuLID (FLUX)",
|
||||||
|
"reference": "https://huggingface.co/guozinan/PuLID",
|
||||||
|
"filename": "pulid_flux_v0.9.1.safetensors",
|
||||||
|
"url": "https://huggingface.co/guozinan/PuLID/resolve/main/pulid_flux_v0.9.1.safetensors",
|
||||||
|
"size": "1.14GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "pulid_v1.1.safetensors",
|
||||||
|
"type": "PuLID",
|
||||||
|
"base": "SDXL",
|
||||||
|
"save_path": "pulid",
|
||||||
|
"description": "This is required for PuLID (SDXL)",
|
||||||
|
"reference": "https://huggingface.co/guozinan/PuLID",
|
||||||
|
"filename": "pulid_v1.1.safetensors",
|
||||||
|
"url": "https://huggingface.co/guozinan/PuLID/resolve/main/pulid_v1.1.safetensors",
|
||||||
|
"size": "984MB"
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "Kolors-IP-Adapter-Plus.bin (Kwai-Kolors/Kolors-IP-Adapter-Plus)",
|
||||||
|
"type": "IP-Adapter",
|
||||||
|
"base": "Kolors",
|
||||||
|
"save_path": "ipadapter",
|
||||||
|
"description": "You can use this model in the [a/ComfyUI IPAdapter plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) extension.",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus",
|
||||||
|
"filename": "Kolors-IP-Adapter-Plus.bin",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus/resolve/main/ip_adapter_plus_general.bin",
|
||||||
|
"size": "1.01GB"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Kolors-IP-Adapter-FaceID-Plus.bin (Kwai-Kolors/Kolors-IP-Adapter-Plus)",
|
||||||
|
"type": "IP-Adapter",
|
||||||
|
"base": "Kolors",
|
||||||
|
"save_path": "ipadapter",
|
||||||
|
"description": "You can use this model in the [a/ComfyUI IPAdapter plus](https://github.com/cubiq/ComfyUI_IPAdapter_plus) extension.",
|
||||||
|
"reference": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-FaceID-Plus",
|
||||||
|
"filename": "Kolors-IP-Adapter-FaceID-Plus.bin",
|
||||||
|
"url": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-FaceID-Plus/resolve/main/ipa-faceid-plus.bin",
|
||||||
|
"size": "2.39GB"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,5 @@
|
|||||||
{
|
{
|
||||||
"custom_nodes": [
|
"custom_nodes": [
|
||||||
{
|
|
||||||
"author": "Comfy-Org",
|
|
||||||
"title": "ComfyUI React Extension Template",
|
|
||||||
"reference": "https://github.com/Comfy-Org/ComfyUI-React-Extension-Template",
|
|
||||||
"files": [
|
|
||||||
"https://github.com/Comfy-Org/ComfyUI-React-Extension-Template"
|
|
||||||
],
|
|
||||||
"install_type": "git-clone",
|
|
||||||
"description": "A minimal template for creating React/TypeScript frontend extensions for ComfyUI, with complete boilerplate setup including internationalization and unit testing."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"author": "Suzie1",
|
"author": "Suzie1",
|
||||||
"title": "Guide To Making Custom Nodes in ComfyUI",
|
"title": "Guide To Making Custom Nodes in ComfyUI",
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
846
openapi.yaml
846
openapi.yaml
@@ -1,846 +0,0 @@
|
|||||||
openapi: 3.1.0
|
|
||||||
info:
|
|
||||||
title: ComfyUI-Manager API
|
|
||||||
description: |
|
|
||||||
API for ComfyUI-Manager, a comprehensive management tool for ComfyUI custom nodes, models, and components.
|
|
||||||
This API enables programmatic access to node management, model downloading, snapshot operations,
|
|
||||||
and overall system configuration.
|
|
||||||
version: "3.32.3"
|
|
||||||
contact:
|
|
||||||
name: ComfyUI-Manager Maintainers
|
|
||||||
servers:
|
|
||||||
- url: '/'
|
|
||||||
description: Default ComfyUI server
|
|
||||||
|
|
||||||
# Common API components
|
|
||||||
components:
|
|
||||||
schemas:
|
|
||||||
Error:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
error:
|
|
||||||
type: string
|
|
||||||
description: Error message
|
|
||||||
|
|
||||||
NodePackageMetadata:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
title:
|
|
||||||
type: string
|
|
||||||
description: Display name of the node package
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
description: Repository/package name
|
|
||||||
files:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
description: Source URLs for the package
|
|
||||||
description:
|
|
||||||
type: string
|
|
||||||
description: Description of the node package functionality
|
|
||||||
install_type:
|
|
||||||
type: string
|
|
||||||
enum: [git, copy, pip]
|
|
||||||
description: Installation method
|
|
||||||
version:
|
|
||||||
type: string
|
|
||||||
description: Version identifier
|
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
description: Unique identifier for the node package
|
|
||||||
ui_id:
|
|
||||||
type: string
|
|
||||||
description: ID for UI reference
|
|
||||||
channel:
|
|
||||||
type: string
|
|
||||||
description: Source channel
|
|
||||||
mode:
|
|
||||||
type: string
|
|
||||||
description: Source mode
|
|
||||||
|
|
||||||
ModelMetadata:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
description: Name of the model
|
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
description: Type of model
|
|
||||||
base:
|
|
||||||
type: string
|
|
||||||
description: Base model type
|
|
||||||
save_path:
|
|
||||||
type: string
|
|
||||||
description: Path for saving the model
|
|
||||||
url:
|
|
||||||
type: string
|
|
||||||
description: Download URL
|
|
||||||
filename:
|
|
||||||
type: string
|
|
||||||
description: Target filename
|
|
||||||
ui_id:
|
|
||||||
type: string
|
|
||||||
description: ID for UI reference
|
|
||||||
|
|
||||||
SnapshotItem:
|
|
||||||
type: string
|
|
||||||
description: Name of the snapshot
|
|
||||||
|
|
||||||
QueueStatus:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
total_count:
|
|
||||||
type: integer
|
|
||||||
description: Total number of tasks
|
|
||||||
done_count:
|
|
||||||
type: integer
|
|
||||||
description: Number of completed tasks
|
|
||||||
in_progress_count:
|
|
||||||
type: integer
|
|
||||||
description: Number of tasks in progress
|
|
||||||
is_processing:
|
|
||||||
type: boolean
|
|
||||||
description: Whether the queue is currently processing
|
|
||||||
|
|
||||||
securitySchemes:
|
|
||||||
securityLevel:
|
|
||||||
type: apiKey
|
|
||||||
in: header
|
|
||||||
name: Security-Level
|
|
||||||
description: Security level for sensitive operations
|
|
||||||
|
|
||||||
parameters:
|
|
||||||
modeParam:
|
|
||||||
name: mode
|
|
||||||
in: query
|
|
||||||
description: Source mode (e.g., "local", "remote")
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
enum: [local, remote, default]
|
|
||||||
|
|
||||||
targetParam:
|
|
||||||
name: target
|
|
||||||
in: query
|
|
||||||
description: Target identifier
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
valueParam:
|
|
||||||
name: value
|
|
||||||
in: query
|
|
||||||
description: New value to set
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
# API Paths
|
|
||||||
paths:
|
|
||||||
# Custom Nodes Endpoints
|
|
||||||
/customnode/getmappings:
|
|
||||||
get:
|
|
||||||
summary: Get node-to-package mappings
|
|
||||||
description: Provides unified mapping between nodes and node packages
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/modeParam'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: array
|
|
||||||
description: Mapping of node packages to node classes
|
|
||||||
|
|
||||||
/customnode/fetch_updates:
|
|
||||||
get:
|
|
||||||
summary: Check for updates
|
|
||||||
description: Fetches updates for custom nodes
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/modeParam'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: No updates available
|
|
||||||
'201':
|
|
||||||
description: Updates found
|
|
||||||
'400':
|
|
||||||
description: Error occurred
|
|
||||||
|
|
||||||
/customnode/installed:
|
|
||||||
get:
|
|
||||||
summary: Get installed custom nodes
|
|
||||||
description: Returns a list of installed node packages
|
|
||||||
parameters:
|
|
||||||
- name: mode
|
|
||||||
in: query
|
|
||||||
description: Lists mode, default or imported
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
enum: [default, imported]
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
$ref: '#/components/schemas/NodePackageMetadata'
|
|
||||||
|
|
||||||
/customnode/getlist:
|
|
||||||
get:
|
|
||||||
summary: Get custom node list
|
|
||||||
description: Provides a list of available custom nodes
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/modeParam'
|
|
||||||
- name: skip_update
|
|
||||||
in: query
|
|
||||||
description: Skip update check
|
|
||||||
schema:
|
|
||||||
type: boolean
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
channel:
|
|
||||||
type: string
|
|
||||||
node_packs:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
$ref: '#/components/schemas/NodePackageMetadata'
|
|
||||||
|
|
||||||
/customnode/alternatives:
|
|
||||||
get:
|
|
||||||
summary: Get alternative node options
|
|
||||||
description: Provides alternatives for nodes
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/modeParam'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
additionalProperties:
|
|
||||||
type: object
|
|
||||||
|
|
||||||
/customnode/versions/{node_name}:
|
|
||||||
get:
|
|
||||||
summary: Get available versions for a node
|
|
||||||
description: Lists all available versions for a specific node
|
|
||||||
parameters:
|
|
||||||
- name: node_name
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
version:
|
|
||||||
type: string
|
|
||||||
'400':
|
|
||||||
description: Node not found
|
|
||||||
|
|
||||||
/customnode/disabled_versions/{node_name}:
|
|
||||||
get:
|
|
||||||
summary: Get disabled versions for a node
|
|
||||||
description: Lists all disabled versions for a specific node
|
|
||||||
parameters:
|
|
||||||
- name: node_name
|
|
||||||
in: path
|
|
||||||
required: true
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
version:
|
|
||||||
type: string
|
|
||||||
'400':
|
|
||||||
description: Node not found
|
|
||||||
|
|
||||||
/customnode/import_fail_info:
|
|
||||||
post:
|
|
||||||
summary: Get import failure information
|
|
||||||
description: Returns information about why a node failed to import
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
cnr_id:
|
|
||||||
type: string
|
|
||||||
url:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
'400':
|
|
||||||
description: No information available
|
|
||||||
|
|
||||||
/customnode/install/git_url:
|
|
||||||
post:
|
|
||||||
summary: Install custom node via Git URL
|
|
||||||
description: Installs a custom node from a Git repository URL
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
text/plain:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Installation successful or already installed
|
|
||||||
'400':
|
|
||||||
description: Installation failed
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
/customnode/install/pip:
|
|
||||||
post:
|
|
||||||
summary: Install custom node dependencies via pip
|
|
||||||
description: Installs Python package dependencies for custom nodes
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
text/plain:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Installation successful
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
# Model Management Endpoints
|
|
||||||
/externalmodel/getlist:
|
|
||||||
get:
|
|
||||||
summary: Get external model list
|
|
||||||
description: Provides a list of available external models
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/modeParam'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
models:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/ModelMetadata'
|
|
||||||
|
|
||||||
# Queue Management Endpoints
|
|
||||||
/manager/queue/update_all:
|
|
||||||
get:
|
|
||||||
summary: Update all custom nodes
|
|
||||||
description: Queues update operations for all installed custom nodes
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/modeParam'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Update queued successfully
|
|
||||||
'401':
|
|
||||||
description: Processing already in progress
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
/manager/queue/reset:
|
|
||||||
get:
|
|
||||||
summary: Reset queue
|
|
||||||
description: Resets the operation queue
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Queue reset successfully
|
|
||||||
|
|
||||||
/manager/queue/status:
|
|
||||||
get:
|
|
||||||
summary: Get queue status
|
|
||||||
description: Returns the current status of the operation queue
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/QueueStatus'
|
|
||||||
|
|
||||||
/manager/queue/install:
|
|
||||||
post:
|
|
||||||
summary: Install custom node
|
|
||||||
description: Queues installation of a custom node
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/NodePackageMetadata'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Installation queued successfully
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
'404':
|
|
||||||
description: Target node not found or security issue
|
|
||||||
|
|
||||||
/manager/queue/start:
|
|
||||||
get:
|
|
||||||
summary: Start queue processing
|
|
||||||
description: Starts processing the operation queue
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Processing started
|
|
||||||
'201':
|
|
||||||
description: Processing already in progress
|
|
||||||
|
|
||||||
/manager/queue/fix:
|
|
||||||
post:
|
|
||||||
summary: Fix custom node
|
|
||||||
description: Attempts to fix a broken custom node installation
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/NodePackageMetadata'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Fix operation queued successfully
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
/manager/queue/reinstall:
|
|
||||||
post:
|
|
||||||
summary: Reinstall custom node
|
|
||||||
description: Uninstalls and then reinstalls a custom node
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/NodePackageMetadata'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Reinstall operation queued successfully
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
/manager/queue/uninstall:
|
|
||||||
post:
|
|
||||||
summary: Uninstall custom node
|
|
||||||
description: Queues uninstallation of a custom node
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/NodePackageMetadata'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Uninstallation queued successfully
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
/manager/queue/update:
|
|
||||||
post:
|
|
||||||
summary: Update custom node
|
|
||||||
description: Queues update of a custom node
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/NodePackageMetadata'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Update queued successfully
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
/manager/queue/disable:
|
|
||||||
post:
|
|
||||||
summary: Disable custom node
|
|
||||||
description: Disables a custom node without uninstalling it
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/NodePackageMetadata'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Disable operation queued successfully
|
|
||||||
|
|
||||||
/manager/queue/update_comfyui:
|
|
||||||
get:
|
|
||||||
summary: Update ComfyUI
|
|
||||||
description: Queues an update operation for ComfyUI itself
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Update queued successfully
|
|
||||||
|
|
||||||
/manager/queue/install_model:
|
|
||||||
post:
|
|
||||||
summary: Install model
|
|
||||||
description: Queues installation of a model
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
$ref: '#/components/schemas/ModelMetadata'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Installation queued successfully
|
|
||||||
'400':
|
|
||||||
description: Invalid model request
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
# Snapshot Management Endpoints
|
|
||||||
/snapshot/getlist:
|
|
||||||
get:
|
|
||||||
summary: Get snapshot list
|
|
||||||
description: Returns a list of available snapshots
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
items:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
$ref: '#/components/schemas/SnapshotItem'
|
|
||||||
|
|
||||||
/snapshot/remove:
|
|
||||||
get:
|
|
||||||
summary: Remove snapshot
|
|
||||||
description: Removes a specified snapshot
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/targetParam'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Snapshot removed successfully
|
|
||||||
'400':
|
|
||||||
description: Error removing snapshot
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
/snapshot/restore:
|
|
||||||
get:
|
|
||||||
summary: Restore snapshot
|
|
||||||
description: Restores a specified snapshot
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
parameters:
|
|
||||||
- $ref: '#/components/parameters/targetParam'
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Snapshot restoration scheduled
|
|
||||||
'400':
|
|
||||||
description: Error restoring snapshot
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
/snapshot/get_current:
|
|
||||||
get:
|
|
||||||
summary: Get current snapshot
|
|
||||||
description: Returns the current system state as a snapshot
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
'400':
|
|
||||||
description: Error creating snapshot
|
|
||||||
|
|
||||||
/snapshot/save:
|
|
||||||
get:
|
|
||||||
summary: Save snapshot
|
|
||||||
description: Saves the current system state as a new snapshot
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Snapshot saved successfully
|
|
||||||
'400':
|
|
||||||
description: Error saving snapshot
|
|
||||||
|
|
||||||
# ComfyUI Management Endpoints
|
|
||||||
/comfyui_manager/comfyui_versions:
|
|
||||||
get:
|
|
||||||
summary: Get ComfyUI versions
|
|
||||||
description: Returns available and current ComfyUI versions
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
versions:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: string
|
|
||||||
current:
|
|
||||||
type: string
|
|
||||||
'400':
|
|
||||||
description: Error retrieving versions
|
|
||||||
|
|
||||||
/comfyui_manager/comfyui_switch_version:
|
|
||||||
get:
|
|
||||||
summary: Switch ComfyUI version
|
|
||||||
description: Switches to a specified ComfyUI version
|
|
||||||
parameters:
|
|
||||||
- name: ver
|
|
||||||
in: query
|
|
||||||
description: Target version
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Version switch successful
|
|
||||||
'400':
|
|
||||||
description: Error switching version
|
|
||||||
|
|
||||||
/manager/reboot:
|
|
||||||
get:
|
|
||||||
summary: Reboot ComfyUI
|
|
||||||
description: Restarts the ComfyUI server
|
|
||||||
security:
|
|
||||||
- securityLevel: []
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Reboot initiated
|
|
||||||
'403':
|
|
||||||
description: Security policy violation
|
|
||||||
|
|
||||||
# Configuration Endpoints
|
|
||||||
/manager/preview_method:
|
|
||||||
get:
|
|
||||||
summary: Get or set preview method
|
|
||||||
description: Gets or sets the latent preview method
|
|
||||||
parameters:
|
|
||||||
- name: value
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
description: New preview method
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
enum: [auto, latent2rgb, taesd, none]
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Setting updated or current value returned
|
|
||||||
content:
|
|
||||||
text/plain:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
/manager/db_mode:
|
|
||||||
get:
|
|
||||||
summary: Get or set database mode
|
|
||||||
description: Gets or sets the database mode
|
|
||||||
parameters:
|
|
||||||
- name: value
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
description: New database mode
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
enum: [channel, local, remote]
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Setting updated or current value returned
|
|
||||||
content:
|
|
||||||
text/plain:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
/manager/policy/component:
|
|
||||||
get:
|
|
||||||
summary: Get or set component policy
|
|
||||||
description: Gets or sets the component policy
|
|
||||||
parameters:
|
|
||||||
- name: value
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
description: New component policy
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Setting updated or current value returned
|
|
||||||
content:
|
|
||||||
text/plain:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
/manager/policy/update:
|
|
||||||
get:
|
|
||||||
summary: Get or set update policy
|
|
||||||
description: Gets or sets the update policy
|
|
||||||
parameters:
|
|
||||||
- name: value
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
description: New update policy
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
enum: [stable, nightly, nightly-comfyui]
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Setting updated or current value returned
|
|
||||||
content:
|
|
||||||
text/plain:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
/manager/channel_url_list:
|
|
||||||
get:
|
|
||||||
summary: Get or set channel URL
|
|
||||||
description: Gets or sets the channel URL for custom node sources
|
|
||||||
parameters:
|
|
||||||
- name: value
|
|
||||||
in: query
|
|
||||||
required: false
|
|
||||||
description: New channel name
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Setting updated or channel list returned
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
selected:
|
|
||||||
type: string
|
|
||||||
list:
|
|
||||||
type: array
|
|
||||||
items:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
url:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
# Component Management Endpoints
|
|
||||||
/manager/component/save:
|
|
||||||
post:
|
|
||||||
summary: Save component
|
|
||||||
description: Saves a reusable workflow component
|
|
||||||
requestBody:
|
|
||||||
required: true
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
properties:
|
|
||||||
name:
|
|
||||||
type: string
|
|
||||||
workflow:
|
|
||||||
type: object
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Component saved successfully
|
|
||||||
content:
|
|
||||||
text/plain:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
'400':
|
|
||||||
description: Error saving component
|
|
||||||
|
|
||||||
/manager/component/loads:
|
|
||||||
post:
|
|
||||||
summary: Load components
|
|
||||||
description: Loads all available workflow components
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Components loaded successfully
|
|
||||||
content:
|
|
||||||
application/json:
|
|
||||||
schema:
|
|
||||||
type: object
|
|
||||||
'400':
|
|
||||||
description: Error loading components
|
|
||||||
|
|
||||||
# Miscellaneous Endpoints
|
|
||||||
/manager/version:
|
|
||||||
get:
|
|
||||||
summary: Get manager version
|
|
||||||
description: Returns the current version of ComfyUI-Manager
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
text/plain:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
|
|
||||||
/manager/notice:
|
|
||||||
get:
|
|
||||||
summary: Get manager notice
|
|
||||||
description: Returns HTML content with notices and version information
|
|
||||||
responses:
|
|
||||||
'200':
|
|
||||||
description: Successful operation
|
|
||||||
content:
|
|
||||||
text/html:
|
|
||||||
schema:
|
|
||||||
type: string
|
|
||||||
@@ -1,15 +1,62 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools >= 61.0"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "comfyui-manager"
|
name = "comfyui-manager"
|
||||||
|
license = { text = "GPL-3.0-only" }
|
||||||
|
version = "4.0.0-beta.1"
|
||||||
|
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."
|
description = "ComfyUI-Manager provides features to install and manage custom nodes for ComfyUI, as well as various functionalities to assist with ComfyUI."
|
||||||
version = "3.32.3"
|
readme = "README.md"
|
||||||
license = { file = "LICENSE.txt" }
|
keywords = ["comfyui", "comfyui-manager"]
|
||||||
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
|
||||||
|
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]
|
[project.urls]
|
||||||
Repository = "https://github.com/ltdrdata/ComfyUI-Manager"
|
Repository = "https://github.com/ltdrdata/ComfyUI-Manager"
|
||||||
# Used by Comfy Registry https://comfyregistry.org
|
|
||||||
|
|
||||||
[tool.comfy]
|
[tool.setuptools.packages.find]
|
||||||
PublisherId = "drltdata"
|
where = ["."]
|
||||||
DisplayName = "ComfyUI-Manager"
|
include = ["comfyui_manager*"]
|
||||||
Icon = ""
|
|
||||||
|
[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)
|
||||||
|
]
|
||||||
|
|||||||
@@ -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)
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user