Compare commits
315 Commits
draft-v4-a
...
3.33.8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2595cc5ed7 | ||
|
|
2f62190c6f | ||
|
|
577314984c | ||
|
|
f0346b955b | ||
|
|
70139ded4a | ||
|
|
bf379900e1 | ||
|
|
9bafc90f5e | ||
|
|
fce0d9e88e | ||
|
|
2b3b154989 | ||
|
|
948d2440a1 | ||
|
|
5adbe1ce7a | ||
|
|
8157d34ffa | ||
|
|
3ec8cb2204 | ||
|
|
0daa826543 | ||
|
|
a66028da58 | ||
|
|
807c9e6872 | ||
|
|
e71f3774ba | ||
|
|
dd7314bf10 | ||
|
|
f33bc127dc | ||
|
|
db92b87782 | ||
|
|
eba41c8693 | ||
|
|
c855308162 | ||
|
|
73d971bed8 | ||
|
|
bcfe0c2874 | ||
|
|
931ff666ae | ||
|
|
18b6d86cc4 | ||
|
|
3c5efa0662 | ||
|
|
9b739bcbbf | ||
|
|
db89076e48 | ||
|
|
19b341ef18 | ||
|
|
be3713b1a3 | ||
|
|
99c4415cfb | ||
|
|
7b311f2ccf | ||
|
|
4aeabfe0a7 | ||
|
|
431ed02194 | ||
|
|
07f587ed83 | ||
|
|
0408341d82 | ||
|
|
5b3c9432f3 | ||
|
|
4a197e63f9 | ||
|
|
0876a12fe9 | ||
|
|
c43c7ecc03 | ||
|
|
4a6dee3044 | ||
|
|
019acdd840 | ||
|
|
1c98512720 | ||
|
|
23a09ad546 | ||
|
|
0836e8fe7c | ||
|
|
90196af8f8 | ||
|
|
566fe05772 | ||
|
|
18772c6292 | ||
|
|
6278bddc9b | ||
|
|
f74bf71735 | ||
|
|
efe9ed68b2 | ||
|
|
7c1e75865d | ||
|
|
a0aee41f1a | ||
|
|
2049dd75f4 | ||
|
|
0864c35ba9 | ||
|
|
92c9f66671 | ||
|
|
815784e809 | ||
|
|
2795d00d1e | ||
|
|
86dd0b4963 | ||
|
|
77a4f4819f | ||
|
|
b63d603482 | ||
|
|
e569b4e613 | ||
|
|
8a70997546 | ||
|
|
80d0a0f882 | ||
|
|
70b3997874 | ||
|
|
e8e4311068 | ||
|
|
c58b93ff51 | ||
|
|
7d8ebfe91b | ||
|
|
810381eab2 | ||
|
|
61dc6cf2de | ||
|
|
0205ebad2a | ||
|
|
09a94133ac | ||
|
|
1eb3c3b219 | ||
|
|
457845bb51 | ||
|
|
0c11b46585 | ||
|
|
c35100d9e9 | ||
|
|
847031cb04 | ||
|
|
f8d87bb452 | ||
|
|
f60b3505e0 | ||
|
|
addefbc511 | ||
|
|
c4314b25a3 | ||
|
|
921bb86127 | ||
|
|
b3a7fb9c3e | ||
|
|
c143c81a7e | ||
|
|
dd389ba0f8 | ||
|
|
46b1649ab8 | ||
|
|
89710412e4 | ||
|
|
931973b632 | ||
|
|
60aaa838e3 | ||
|
|
1246538bbb | ||
|
|
80518abf9d | ||
|
|
fc1ae2a18e | ||
|
|
3fd8d2049c | ||
|
|
35a6bcf20c | ||
|
|
0d75fc331e | ||
|
|
0a23e793e3 | ||
|
|
2c1c03e063 | ||
|
|
64059d2949 | ||
|
|
648aa7c4d3 | ||
|
|
274bb81a08 | ||
|
|
e2c90b4681 | ||
|
|
fa0a98ac6e | ||
|
|
e6e7b42415 | ||
|
|
0b7ef2e1d4 | ||
|
|
2fac67a9f9 | ||
|
|
8b9892de2e | ||
|
|
b3290dc909 | ||
|
|
3e3176eddb | ||
|
|
b1ef84894a | ||
|
|
c6cffc92c4 | ||
|
|
efb9fd2712 | ||
|
|
94b294ff93 | ||
|
|
99a9e33648 | ||
|
|
055d94a919 | ||
|
|
0978005240 | ||
|
|
1f796581ec | ||
|
|
f3a1716dad | ||
|
|
a1c3a0db1f | ||
|
|
9f80cc8a6b | ||
|
|
133786846e | ||
|
|
bdf297a5c6 | ||
|
|
6767254eb0 | ||
|
|
691cebd479 | ||
|
|
f3932cbf29 | ||
|
|
3f73a97037 | ||
|
|
226f1f5be4 | ||
|
|
7e45c07660 | ||
|
|
0c815036b9 | ||
|
|
ae9fdd0255 | ||
|
|
b3874ee6fd | ||
|
|
62af4891f3 | ||
|
|
2176e0c0ad | ||
|
|
cac105b0d5 | ||
|
|
cd7c42cc23 | ||
|
|
a3fb847773 | ||
|
|
5c2f4f9e4b | ||
|
|
0a511d5b87 | ||
|
|
efe1aad5db | ||
|
|
eed4c53df0 | ||
|
|
9c08a6314b | ||
|
|
a6b2d2c722 | ||
|
|
3c6b5300e5 | ||
|
|
f084c30b20 | ||
|
|
206004fc1f | ||
|
|
d9641cbff8 | ||
|
|
13b272052a | ||
|
|
c79e0d26d8 | ||
|
|
ec4a4c2cfc | ||
|
|
9a9491bff9 | ||
|
|
5b5155819f | ||
|
|
1b941c6b29 | ||
|
|
9b9665d2e9 | ||
|
|
4cceb46641 | ||
|
|
19cf83cce6 | ||
|
|
bb60d399fc | ||
|
|
1a9f1dd0ae | ||
|
|
586c465aaa | ||
|
|
50ceb974d9 | ||
|
|
27cf40d392 | ||
|
|
bbb6005634 | ||
|
|
8dbd996558 | ||
|
|
8605345499 | ||
|
|
3671ddbd4b | ||
|
|
5bc1ceacb2 | ||
|
|
47b9fa3651 | ||
|
|
6062b87771 | ||
|
|
213152aa43 | ||
|
|
ea8047344f | ||
|
|
a7bc167d53 | ||
|
|
18e78ee2c2 | ||
|
|
754236e35b | ||
|
|
2645d62991 | ||
|
|
e55d9416dc | ||
|
|
24d35eec54 | ||
|
|
ee053f50b4 | ||
|
|
3593c9ed3e | ||
|
|
93f548696d | ||
|
|
cecb952add | ||
|
|
596571bb38 | ||
|
|
85a6fb75b8 | ||
|
|
7dea42433b | ||
|
|
ec5e4af6b7 | ||
|
|
0048754fe8 | ||
|
|
5c0bd0f79c | ||
|
|
669cdffe08 | ||
|
|
3cd553301b | ||
|
|
db7ef4f253 | ||
|
|
a09704567c | ||
|
|
21fe577a2e | ||
|
|
9f258f5c9c | ||
|
|
9cd088feb0 | ||
|
|
89e3828138 | ||
|
|
731c89dc27 | ||
|
|
3d920cab4d | ||
|
|
470b8c1fb8 | ||
|
|
dbf988fd5a | ||
|
|
0031743ad4 | ||
|
|
0f2c0ab65d | ||
|
|
53244b794f | ||
|
|
416122d61d | ||
|
|
d3c625e791 | ||
|
|
ca2c41783c | ||
|
|
e2a6446585 | ||
|
|
839790b5ab | ||
|
|
58b9946936 | ||
|
|
a19ba22eaf | ||
|
|
117715aa22 | ||
|
|
891a5a85ee | ||
|
|
166debfabb | ||
|
|
7258a09fe5 | ||
|
|
058a436187 | ||
|
|
1950802c55 | ||
|
|
eb52a03372 | ||
|
|
f8aa428be3 | ||
|
|
ec0893f136 | ||
|
|
92b99ea963 | ||
|
|
02cd52bb65 | ||
|
|
af1ec2c87b | ||
|
|
41006c3a33 | ||
|
|
116a6d500d | ||
|
|
87d0ac807f | ||
|
|
fc943172eb | ||
|
|
9daa5a2fbd | ||
|
|
b7b2746a61 | ||
|
|
d66a4fbfc8 | ||
|
|
683a172ad8 | ||
|
|
6e12358f5a | ||
|
|
8bcf16dc90 | ||
|
|
65c0a2a1f5 | ||
|
|
115236eb9c | ||
|
|
08de942abe | ||
|
|
e9dff83290 | ||
|
|
3bc6c7584d | ||
|
|
22a2bf1584 | ||
|
|
79ece5f72c | ||
|
|
5da6fe1373 | ||
|
|
48c10d0b95 | ||
|
|
9bb56b1457 | ||
|
|
83420fd828 | ||
|
|
52f4b9506f | ||
|
|
b501e9b20b | ||
|
|
1f7ae5319a | ||
|
|
68c201239d | ||
|
|
6e4e43f612 | ||
|
|
81c3708f39 | ||
|
|
f4d2bbde34 | ||
|
|
d14b42a42c | ||
|
|
0e9c32344c | ||
|
|
30c4ea06af | ||
|
|
8211264993 | ||
|
|
67cf5b49e1 | ||
|
|
8e7ba18e05 | ||
|
|
8359e1063e | ||
|
|
ca078e54b9 | ||
|
|
f7e930c5a2 | ||
|
|
479d95e1c8 | ||
|
|
2b0ff08eef | ||
|
|
67a487db15 | ||
|
|
2488cb3458 | ||
|
|
157e6336fa | ||
|
|
d808a1f406 | ||
|
|
2bb4d8cd63 | ||
|
|
a8164e1631 | ||
|
|
a31d286945 | ||
|
|
12eeef4cf0 | ||
|
|
ce8e6dc36e | ||
|
|
7a32e544a7 | ||
|
|
e16e9d7a0e | ||
|
|
821f908dbc | ||
|
|
e007e6f897 | ||
|
|
94f496fd65 | ||
|
|
d2ce35d2e6 | ||
|
|
2eeebb32dc | ||
|
|
f6d636d82f | ||
|
|
0cd397623e | ||
|
|
5978b6c9ee | ||
|
|
9e132811bc | ||
|
|
3a3b5c1f92 | ||
|
|
26be01ff82 | ||
|
|
8f6dd92374 | ||
|
|
d50b71a887 | ||
|
|
3bc9cbc767 | ||
|
|
b6f6b4fd8a | ||
|
|
a66bada8a3 | ||
|
|
a804f7de19 | ||
|
|
72a61a9966 | ||
|
|
b08bb658ea | ||
|
|
7b28bf608b | ||
|
|
b57747fdf1 | ||
|
|
0735271b10 | ||
|
|
770cd0f9f5 | ||
|
|
32b6266dd9 | ||
|
|
2a8412a2bf | ||
|
|
0c4d289002 | ||
|
|
cee01fec25 | ||
|
|
f00686f3f2 | ||
|
|
bd33f7726e | ||
|
|
22ab526b0c | ||
|
|
af269d198d | ||
|
|
995ef6356e | ||
|
|
aa3bf77c28 | ||
|
|
15667c1259 | ||
|
|
c7b6b565da | ||
|
|
3214ab52c6 | ||
|
|
e3062ff613 | ||
|
|
036b63efe7 | ||
|
|
8d3e1d60d0 | ||
|
|
59876452f4 | ||
|
|
04972ad87f | ||
|
|
c7e69f4e26 | ||
|
|
7a59b6d0d9 | ||
|
|
d227ad97a4 | ||
|
|
b93a474dae | ||
|
|
a5fe075bf3 |
@@ -1 +0,0 @@
|
||||
PYPI_TOKEN=your-pypi-token
|
||||
58
.github/workflows/publish-to-pypi.yml
vendored
58
.github/workflows/publish-to-pypi.yml
vendored
@@ -1,58 +0,0 @@
|
||||
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:
|
||||
name: Publish Custom Node to registry
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'ltdrdata' || github.repository_owner == 'Comfy-Org' }}
|
||||
if: ${{ github.repository_owner == 'ltdrdata' }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -17,7 +17,4 @@ github-stats-cache.json
|
||||
pip_overrides.json
|
||||
*.json
|
||||
check2.sh
|
||||
/venv/
|
||||
build
|
||||
*.egg-info
|
||||
.env
|
||||
/venv/
|
||||
@@ -1,47 +0,0 @@
|
||||
## 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/
|
||||
13
MANIFEST.in
13
MANIFEST.in
@@ -1,13 +0,0 @@
|
||||
include comfyui_manager/js/*
|
||||
include comfyui_manager/*.json
|
||||
include comfyui_manager/glob/*
|
||||
include LICENSE.txt
|
||||
include README.md
|
||||
include requirements.txt
|
||||
include pyproject.toml
|
||||
include custom-node-list.json
|
||||
include extension-node-list.json
|
||||
include extras.json
|
||||
include github-stats.json
|
||||
include model-list.json
|
||||
include alter-list.json
|
||||
83
README.md
83
README.md
@@ -5,35 +5,86 @@
|
||||

|
||||
|
||||
## NOTICE
|
||||
* V4.0: Modify the structure to be installable via pip instead of using git clone.
|
||||
* V3.16: Support for `uv` has been added. Set `use_uv` in `config.ini`.
|
||||
* V3.10: `double-click feature` is removed
|
||||
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
|
||||
* V3.3.2: Overhauled. Officially supports [https://comfyregistry.org/](https://comfyregistry.org/).
|
||||
* V3.3.2: Overhauled. Officially supports [https://registry.comfy.org/](https://registry.comfy.org/).
|
||||
* You can see whole nodes info on [ComfyUI Nodes Info](https://ltdrdata.github.io/) page.
|
||||
|
||||
## Installation
|
||||
|
||||
* When installing the latest ComfyUI, it will be automatically installed as a dependency, so manual installation is no longer necessary.
|
||||
### Installation[method1] (General installation method: ComfyUI-Manager only)
|
||||
|
||||
* 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 .
|
||||
```
|
||||
To install ComfyUI-Manager in addition to an existing installation of ComfyUI, you can follow the following steps:
|
||||
|
||||
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
|
||||
|
||||
|
||||
## Front-end
|
||||
### Installation[method4] (Installation for linux+venv: ComfyUI + ComfyUI-Manager)
|
||||
|
||||
* The built-in front-end of ComfyUI-Manager is the legacy front-end. The front-end for ComfyUI-Manager is now provided via [ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend).
|
||||
* To enable the legacy front-end, set the environment variable `ENABLE_LEGACY_COMFYUI_MANAGER_FRONT` to `true` before running.
|
||||
To install ComfyUI with ComfyUI-Manager on Linux using a venv environment, you can follow these steps:
|
||||
* **prerequisite: python-is-python3, python3-venv, git**
|
||||
|
||||
1. Download [scripts/install-comfyui-venv-linux.sh](https://github.com/ltdrdata/ComfyUI-Manager/raw/main/scripts/install-comfyui-venv-linux.sh) into empty install directory
|
||||
- Don't click. Right click the link and use save as...
|
||||
- ComfyUI will be installed in the subdirectory of the specified directory, and the directory will contain the generated executable script.
|
||||
2. `chmod +x install-comfyui-venv-linux.sh`
|
||||
3. `./install-comfyui-venv-linux.sh`
|
||||
|
||||
### Installation Precautions
|
||||
* **DO**: `ComfyUI-Manager` files must be accurately located in the path `ComfyUI/custom_nodes/comfyui-manager`
|
||||
* Installing in a compressed file format is not recommended.
|
||||
* **DON'T**: Decompress directly into the `ComfyUI/custom_nodes` location, resulting in the Manager contents like `__init__.py` being placed directly in that directory.
|
||||
* You have to remove all ComfyUI-Manager files from `ComfyUI/custom_nodes`
|
||||
* **DON'T**: In a form where decompression occurs in a path such as `ComfyUI/custom_nodes/ComfyUI-Manager/ComfyUI-Manager`.
|
||||
* **DON'T**: In a form where decompression occurs in a path such as `ComfyUI/custom_nodes/ComfyUI-Manager-main`.
|
||||
* In such cases, `ComfyUI-Manager` may operate, but it won't be recognized within `ComfyUI-Manager`, and updates cannot be performed. It also poses the risk of duplicate installations. Remove it and install properly via `git clone` method.
|
||||
|
||||
|
||||
You can execute ComfyUI by running either `./run_gpu.sh` or `./run_cpu.sh` depending on your system configuration.
|
||||
|
||||
## 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
|
||||
|
||||
25
__init__.py
Normal file
25
__init__.py
Normal file
@@ -0,0 +1,25 @@
|
||||
"""
|
||||
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']
|
||||
|
||||
|
||||
|
||||
@@ -15,37 +15,41 @@ import git
|
||||
import importlib
|
||||
|
||||
|
||||
from .common import manager_util
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "glob"))
|
||||
|
||||
import manager_util
|
||||
|
||||
# read env vars
|
||||
# COMFYUI_FOLDERS_BASE_PATH is not required in cm-cli.py
|
||||
# `comfy_path` should be resolved before importing manager_core
|
||||
|
||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
print("[bold red]cm-cli: environment variable 'COMFYUI_PATH' is not specified.[/bold red]")
|
||||
exit(-1)
|
||||
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
|
||||
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
|
||||
from .common import cm_global
|
||||
from .glob import manager_core as core
|
||||
from .common import context
|
||||
from .glob.manager_core import unified_manager
|
||||
from .common import cnr_utils
|
||||
import cm_global
|
||||
import manager_core as core
|
||||
from manager_core import unified_manager
|
||||
import cnr_utils
|
||||
|
||||
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
|
||||
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")):
|
||||
with open(os.path.join(manager_util.comfyui_manager_path, "pip_overrides.json"), 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||
@@ -81,7 +85,7 @@ def read_downgrade_blacklist():
|
||||
try:
|
||||
import configparser
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read(context.manager_config_path)
|
||||
config.read(core.manager_config.path)
|
||||
default_conf = config['default']
|
||||
|
||||
if 'downgrade_blacklist' in default_conf:
|
||||
@@ -142,15 +146,17 @@ class Ctx:
|
||||
if os.path.exists(extra_model_paths_yaml):
|
||||
utils.extra_config.load_extra_path_config(extra_model_paths_yaml)
|
||||
|
||||
context.update_user_directory(user_directory)
|
||||
core.update_user_directory(user_directory)
|
||||
|
||||
if os.path.exists(context.manager_pip_overrides_path):
|
||||
with open(context.manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||
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:
|
||||
cm_global.pip_overrides = json.load(json_file)
|
||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||
|
||||
if os.path.exists(context.manager_pip_blacklist_path):
|
||||
with open(context.manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||
if sys.version_info < (3, 13):
|
||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||
|
||||
if os.path.exists(core.manager_pip_blacklist_path):
|
||||
with open(core.manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||
for x in f.readlines():
|
||||
y = x.strip()
|
||||
if y != '':
|
||||
@@ -163,15 +169,15 @@ class Ctx:
|
||||
|
||||
@staticmethod
|
||||
def get_startup_scripts_path():
|
||||
return os.path.join(context.manager_startup_script_path, "install-scripts.txt")
|
||||
return os.path.join(core.manager_startup_script_path, "install-scripts.txt")
|
||||
|
||||
@staticmethod
|
||||
def get_restore_snapshot_path():
|
||||
return os.path.join(context.manager_startup_script_path, "restore-snapshot.json")
|
||||
return os.path.join(core.manager_startup_script_path, "restore-snapshot.json")
|
||||
|
||||
@staticmethod
|
||||
def get_snapshot_path():
|
||||
return context.manager_snapshot_path
|
||||
return core.manager_snapshot_path
|
||||
|
||||
@staticmethod
|
||||
def get_custom_nodes_paths():
|
||||
@@ -184,13 +190,18 @@ class Ctx:
|
||||
cmd_ctx = Ctx()
|
||||
|
||||
|
||||
def install_node(node_spec_str, is_all=False, cnt_msg=''):
|
||||
def install_node(node_spec_str, is_all=False, cnt_msg='', **kwargs):
|
||||
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):
|
||||
# install via urls
|
||||
res = asyncio.run(core.gitclone_install(node_spec_str, no_deps=cmd_ctx.no_deps))
|
||||
if not res.result:
|
||||
print(res.msg)
|
||||
print(f"[bold red]ERROR: An error occurred while installing '{node_spec_str}'.[/bold red]")
|
||||
if exit_on_fail:
|
||||
sys.exit(1)
|
||||
else:
|
||||
print(f"{cnt_msg} [INSTALLED] {node_spec_str:50}")
|
||||
else:
|
||||
@@ -225,6 +236,8 @@ def install_node(node_spec_str, is_all=False, cnt_msg=''):
|
||||
print("")
|
||||
else:
|
||||
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=''):
|
||||
@@ -586,7 +599,7 @@ def get_all_installed_node_specs():
|
||||
return res
|
||||
|
||||
|
||||
def for_each_nodes(nodes, act, allow_all=True):
|
||||
def for_each_nodes(nodes, act, allow_all=True, **kwargs):
|
||||
is_all = False
|
||||
if allow_all and 'all' in nodes:
|
||||
is_all = True
|
||||
@@ -598,7 +611,7 @@ def for_each_nodes(nodes, act, allow_all=True):
|
||||
i = 1
|
||||
for x in nodes:
|
||||
try:
|
||||
act(x, is_all=is_all, cnt_msg=f'{i}/{total}')
|
||||
act(x, is_all=is_all, cnt_msg=f'{i}/{total}', **kwargs)
|
||||
except Exception as e:
|
||||
print(f"ERROR: {e}")
|
||||
traceback.print_exc()
|
||||
@@ -642,13 +655,17 @@ def install(
|
||||
None,
|
||||
help="user directory"
|
||||
),
|
||||
exit_on_fail: bool = typer.Option(
|
||||
False,
|
||||
help="Exit on failure"
|
||||
)
|
||||
):
|
||||
cmd_ctx.set_user_directory(user_directory)
|
||||
cmd_ctx.set_channel_mode(channel, mode)
|
||||
cmd_ctx.set_no_deps(no_deps)
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
for_each_nodes(nodes, act=install_node)
|
||||
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)
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
|
||||
@@ -685,7 +702,7 @@ def reinstall(
|
||||
cmd_ctx.set_channel_mode(channel, mode)
|
||||
cmd_ctx.set_no_deps(no_deps)
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||
for_each_nodes(nodes, act=reinstall_node)
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
@@ -739,7 +756,7 @@ def update(
|
||||
if 'all' in nodes:
|
||||
asyncio.run(auto_save_snapshot())
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||
|
||||
for x in nodes:
|
||||
if x.lower() in ['comfyui', 'comfy', 'all']:
|
||||
@@ -840,7 +857,7 @@ def fix(
|
||||
if 'all' in nodes:
|
||||
asyncio.run(auto_save_snapshot())
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||
for_each_nodes(nodes, fix_node, allow_all=True)
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
@@ -1117,7 +1134,7 @@ def restore_snapshot(
|
||||
print(f"[bold red]ERROR: `{snapshot_path}` is not exists.[/bold red]")
|
||||
exit(1)
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||
try:
|
||||
asyncio.run(core.restore_snapshot(snapshot_path, extras))
|
||||
except Exception:
|
||||
@@ -1149,7 +1166,7 @@ def restore_dependencies(
|
||||
total = len(node_paths)
|
||||
i = 1
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||
for x in node_paths:
|
||||
print("----------------------------------------------------------------------------------------------------")
|
||||
print(f"Restoring [{i}/{total}]: {x}")
|
||||
@@ -1168,7 +1185,7 @@ def post_install(
|
||||
):
|
||||
path = os.path.expanduser(path)
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||
unified_manager.execute_install_script('', path, instant_execution=True)
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
@@ -1212,7 +1229,7 @@ def install_deps(
|
||||
print(f"[bold red]Invalid json file: {deps}[/bold red]")
|
||||
exit(1)
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, core.manager_files_path)
|
||||
for k in json_obj['custom_nodes'].keys():
|
||||
state = core.simple_check_custom_node(k)
|
||||
if state == 'installed':
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
python ./comfyui_manager/cm-cli.py $*
|
||||
python cm-cli.py $*
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
import os
|
||||
import logging
|
||||
from comfy.cli_args import args
|
||||
|
||||
def prestartup():
|
||||
from . import prestartup_script # noqa: F401
|
||||
logging.info('[PRE] ComfyUI-Manager')
|
||||
|
||||
|
||||
def start():
|
||||
logging.info('[START] ComfyUI-Manager')
|
||||
from .common import cm_global # noqa: F401
|
||||
|
||||
if not args.disable_manager:
|
||||
if args.enable_manager_legacy_ui:
|
||||
try:
|
||||
from .legacy import manager_server # noqa: F401
|
||||
from .legacy import share_3rdparty # noqa: F401
|
||||
import nodes
|
||||
|
||||
logging.info("[ComfyUI-Manager] Legacy UI is enabled.")
|
||||
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)
|
||||
else:
|
||||
from .glob import manager_server # noqa: F401
|
||||
from .glob import share_3rdparty # noqa: F401
|
||||
|
||||
|
||||
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
|
||||
@@ -1,109 +0,0 @@
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
from . import manager_util
|
||||
import toml
|
||||
import git
|
||||
|
||||
|
||||
# read env vars
|
||||
comfy_path: str = os.environ.get('COMFYUI_PATH')
|
||||
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
try:
|
||||
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
||||
os.environ['COMFYUI_PATH'] = comfy_path
|
||||
except:
|
||||
logging.error("[ComfyUI-Manager] environment variable 'COMFYUI_PATH' is not specified.")
|
||||
exit(-1)
|
||||
|
||||
if comfy_base_path is None:
|
||||
comfy_base_path = comfy_path
|
||||
|
||||
channel_list_template_path = os.path.join(manager_util.comfyui_manager_path, 'channels.list.template')
|
||||
git_script_path = os.path.join(manager_util.comfyui_manager_path, "git_helper.py")
|
||||
|
||||
manager_files_path = None
|
||||
manager_config_path = None
|
||||
manager_channel_list_path = None
|
||||
manager_startup_script_path:str = None
|
||||
manager_snapshot_path = None
|
||||
manager_pip_overrides_path = None
|
||||
manager_pip_blacklist_path = None
|
||||
manager_components_path = None
|
||||
manager_batch_history_path = None
|
||||
|
||||
def update_user_directory(user_dir):
|
||||
global manager_files_path
|
||||
global manager_config_path
|
||||
global manager_channel_list_path
|
||||
global manager_startup_script_path
|
||||
global manager_snapshot_path
|
||||
global manager_pip_overrides_path
|
||||
global manager_pip_blacklist_path
|
||||
global manager_components_path
|
||||
global manager_batch_history_path
|
||||
|
||||
manager_files_path = os.path.abspath(os.path.join(user_dir, 'default', 'ComfyUI-Manager'))
|
||||
if not os.path.exists(manager_files_path):
|
||||
os.makedirs(manager_files_path)
|
||||
|
||||
manager_snapshot_path = os.path.join(manager_files_path, "snapshots")
|
||||
if not os.path.exists(manager_snapshot_path):
|
||||
os.makedirs(manager_snapshot_path)
|
||||
|
||||
manager_startup_script_path = os.path.join(manager_files_path, "startup-scripts")
|
||||
if not os.path.exists(manager_startup_script_path):
|
||||
os.makedirs(manager_startup_script_path)
|
||||
|
||||
manager_config_path = os.path.join(manager_files_path, 'config.ini')
|
||||
manager_channel_list_path = os.path.join(manager_files_path, 'channels.list')
|
||||
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
||||
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
||||
manager_components_path = os.path.join(manager_files_path, "components")
|
||||
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):
|
||||
os.makedirs(manager_util.cache_dir)
|
||||
|
||||
if not os.path.exists(manager_batch_history_path):
|
||||
os.makedirs(manager_batch_history_path)
|
||||
|
||||
try:
|
||||
import folder_paths
|
||||
update_user_directory(folder_paths.get_user_directory())
|
||||
|
||||
except Exception:
|
||||
# fallback:
|
||||
# This case is only possible when running with cm-cli, and in practice, this case is not actually used.
|
||||
update_user_directory(os.path.abspath(manager_util.comfyui_manager_path))
|
||||
|
||||
|
||||
def get_current_comfyui_ver():
|
||||
"""
|
||||
Extract version from pyproject.toml
|
||||
"""
|
||||
toml_path = os.path.join(comfy_path, 'pyproject.toml')
|
||||
if not os.path.exists(toml_path):
|
||||
return None
|
||||
else:
|
||||
try:
|
||||
with open(toml_path, "r", encoding="utf-8") as f:
|
||||
data = toml.load(f)
|
||||
|
||||
project = data.get('project', {})
|
||||
return project.get('version')
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_comfyui_tag():
|
||||
try:
|
||||
with git.Repo(comfy_path) as repo:
|
||||
return repo.git.describe('--tags')
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
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"
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,386 +0,0 @@
|
||||
import mimetypes
|
||||
from ..common import context
|
||||
from . import manager_core as core
|
||||
|
||||
import os
|
||||
from aiohttp import web
|
||||
import aiohttp
|
||||
import json
|
||||
import hashlib
|
||||
|
||||
import folder_paths
|
||||
from server import PromptServer
|
||||
|
||||
|
||||
def extract_model_file_names(json_data):
|
||||
"""Extract unique file names from the input JSON data."""
|
||||
file_names = set()
|
||||
model_filename_extensions = {'.safetensors', '.ckpt', '.pt', '.pth', '.bin'}
|
||||
|
||||
# Recursively search for file names in the JSON data
|
||||
def recursive_search(data):
|
||||
if isinstance(data, dict):
|
||||
for value in data.values():
|
||||
recursive_search(value)
|
||||
elif isinstance(data, list):
|
||||
for item in data:
|
||||
recursive_search(item)
|
||||
elif isinstance(data, str) and '.' in data:
|
||||
file_names.add(os.path.basename(data)) # file_names.add(data)
|
||||
|
||||
recursive_search(json_data)
|
||||
return [f for f in list(file_names) if os.path.splitext(f)[1] in model_filename_extensions]
|
||||
|
||||
|
||||
def find_file_paths(base_dir, file_names):
|
||||
"""Find the paths of the files in the base directory."""
|
||||
file_paths = {}
|
||||
|
||||
for root, dirs, files in os.walk(base_dir):
|
||||
# Exclude certain directories
|
||||
dirs[:] = [d for d in dirs if d not in ['.git']]
|
||||
|
||||
for file in files:
|
||||
if file in file_names:
|
||||
file_paths[file] = os.path.join(root, file)
|
||||
return file_paths
|
||||
|
||||
|
||||
def compute_sha256_checksum(filepath):
|
||||
"""Compute the SHA256 checksum of a file, in chunks"""
|
||||
sha256 = hashlib.sha256()
|
||||
with open(filepath, 'rb') as f:
|
||||
for chunk in iter(lambda: f.read(4096), b''):
|
||||
sha256.update(chunk)
|
||||
return sha256.hexdigest()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/share_option")
|
||||
async def share_option(request):
|
||||
if "value" in request.rel_url.query:
|
||||
core.get_config()['share_option'] = request.rel_url.query['value']
|
||||
core.write_config()
|
||||
else:
|
||||
return web.Response(text=core.get_config()['share_option'], status=200)
|
||||
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
def get_openart_auth():
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, ".openart_key")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(context.manager_files_path, ".openart_key"), "r") as f:
|
||||
openart_key = f.read().strip()
|
||||
return openart_key if openart_key else None
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_matrix_auth():
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, "matrix_auth")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(context.manager_files_path, "matrix_auth"), "r") as f:
|
||||
matrix_auth = f.read()
|
||||
homeserver, username, password = matrix_auth.strip().split("\n")
|
||||
if not homeserver or not username or not password:
|
||||
return None
|
||||
return {
|
||||
"homeserver": homeserver,
|
||||
"username": username,
|
||||
"password": password,
|
||||
}
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_comfyworkflows_auth():
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, "comfyworkflows_sharekey")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(context.manager_files_path, "comfyworkflows_sharekey"), "r") as f:
|
||||
share_key = f.read()
|
||||
if not share_key.strip():
|
||||
return None
|
||||
return share_key
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_youml_settings():
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, ".youml")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(context.manager_files_path, ".youml"), "r") as f:
|
||||
youml_settings = f.read().strip()
|
||||
return youml_settings if youml_settings else None
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def set_youml_settings(settings):
|
||||
with open(os.path.join(context.manager_files_path, ".youml"), "w") as f:
|
||||
f.write(settings)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_openart_auth")
|
||||
async def api_get_openart_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
openart_key = get_openart_auth()
|
||||
if not openart_key:
|
||||
return web.Response(status=404)
|
||||
return web.json_response({"openart_key": openart_key})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/v2/manager/set_openart_auth")
|
||||
async def api_set_openart_auth(request):
|
||||
json_data = await request.json()
|
||||
openart_key = json_data['openart_key']
|
||||
with open(os.path.join(context.manager_files_path, ".openart_key"), "w") as f:
|
||||
f.write(openart_key)
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_matrix_auth")
|
||||
async def api_get_matrix_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
matrix_auth = get_matrix_auth()
|
||||
if not matrix_auth:
|
||||
return web.Response(status=404)
|
||||
return web.json_response(matrix_auth)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/youml/settings")
|
||||
async def api_get_youml_settings(request):
|
||||
youml_settings = get_youml_settings()
|
||||
if not youml_settings:
|
||||
return web.Response(status=404)
|
||||
return web.json_response(json.loads(youml_settings))
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/v2/manager/youml/settings")
|
||||
async def api_set_youml_settings(request):
|
||||
json_data = await request.json()
|
||||
set_youml_settings(json.dumps(json_data))
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_comfyworkflows_auth")
|
||||
async def api_get_comfyworkflows_auth(request):
|
||||
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
|
||||
# in the same directory as the ComfyUI base folder
|
||||
# print("Getting stored Comfyworkflows.com auth...")
|
||||
comfyworkflows_auth = get_comfyworkflows_auth()
|
||||
if not comfyworkflows_auth:
|
||||
return web.Response(status=404)
|
||||
return web.json_response({"comfyworkflows_sharekey": comfyworkflows_auth})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/v2/manager/set_esheep_workflow_and_images")
|
||||
async def set_esheep_workflow_and_images(request):
|
||||
json_data = await request.json()
|
||||
with open(os.path.join(context.manager_files_path, "esheep_share_message.json"), "w", encoding='utf-8') as file:
|
||||
json.dump(json_data, file, indent=4)
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_esheep_workflow_and_images")
|
||||
async def get_esheep_workflow_and_images(request):
|
||||
with open(os.path.join(context.manager_files_path, "esheep_share_message.json"), 'r', encoding='utf-8') as file:
|
||||
data = json.load(file)
|
||||
return web.Response(status=200, text=json.dumps(data))
|
||||
|
||||
|
||||
def set_matrix_auth(json_data):
|
||||
homeserver = json_data['homeserver']
|
||||
username = json_data['username']
|
||||
password = json_data['password']
|
||||
with open(os.path.join(context.manager_files_path, "matrix_auth"), "w") as f:
|
||||
f.write("\n".join([homeserver, username, password]))
|
||||
|
||||
|
||||
def set_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
with open(os.path.join(context.manager_files_path, "comfyworkflows_sharekey"), "w") as f:
|
||||
f.write(comfyworkflows_sharekey)
|
||||
|
||||
|
||||
def has_provided_matrix_auth(matrix_auth):
|
||||
return matrix_auth['homeserver'].strip() and matrix_auth['username'].strip() and matrix_auth['password'].strip()
|
||||
|
||||
|
||||
def has_provided_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
return comfyworkflows_sharekey.strip()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/v2/manager/share")
|
||||
async def share_art(request):
|
||||
# get json data
|
||||
json_data = await request.json()
|
||||
|
||||
matrix_auth = json_data['matrix_auth']
|
||||
comfyworkflows_sharekey = json_data['cw_auth']['cw_sharekey']
|
||||
|
||||
set_matrix_auth(matrix_auth)
|
||||
set_comfyworkflows_auth(comfyworkflows_sharekey)
|
||||
|
||||
share_destinations = json_data['share_destinations']
|
||||
credits = json_data['credits']
|
||||
title = json_data['title']
|
||||
description = json_data['description']
|
||||
is_nsfw = json_data['is_nsfw']
|
||||
prompt = json_data['prompt']
|
||||
potential_outputs = json_data['potential_outputs']
|
||||
selected_output_index = json_data['selected_output_index']
|
||||
|
||||
try:
|
||||
output_to_share = potential_outputs[int(selected_output_index)]
|
||||
except:
|
||||
# for now, pick the first output
|
||||
output_to_share = potential_outputs[0]
|
||||
|
||||
assert output_to_share['type'] in ('image', 'output')
|
||||
output_dir = folder_paths.get_output_directory()
|
||||
|
||||
if output_to_share['type'] == 'image':
|
||||
asset_filename = output_to_share['image']['filename']
|
||||
asset_subfolder = output_to_share['image']['subfolder']
|
||||
|
||||
if output_to_share['image']['type'] == 'temp':
|
||||
output_dir = folder_paths.get_temp_directory()
|
||||
else:
|
||||
asset_filename = output_to_share['output']['filename']
|
||||
asset_subfolder = output_to_share['output']['subfolder']
|
||||
|
||||
if asset_subfolder:
|
||||
asset_filepath = os.path.join(output_dir, asset_subfolder, asset_filename)
|
||||
else:
|
||||
asset_filepath = os.path.join(output_dir, asset_filename)
|
||||
|
||||
# get the mime type of the asset
|
||||
assetFileType = mimetypes.guess_type(asset_filepath)[0]
|
||||
|
||||
share_website_host = "UNKNOWN"
|
||||
if "comfyworkflows" in share_destinations:
|
||||
share_website_host = "https://comfyworkflows.com"
|
||||
share_endpoint = f"{share_website_host}/api"
|
||||
|
||||
# get presigned urls
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.post(
|
||||
f"{share_endpoint}/get_presigned_urls",
|
||||
json={
|
||||
"assetFileName": asset_filename,
|
||||
"assetFileType": assetFileType,
|
||||
"workflowJsonFileName": 'workflow.json',
|
||||
"workflowJsonFileType": 'application/json',
|
||||
},
|
||||
) as resp:
|
||||
assert resp.status == 200
|
||||
presigned_urls_json = await resp.json()
|
||||
assetFilePresignedUrl = presigned_urls_json["assetFilePresignedUrl"]
|
||||
assetFileKey = presigned_urls_json["assetFileKey"]
|
||||
workflowJsonFilePresignedUrl = presigned_urls_json["workflowJsonFilePresignedUrl"]
|
||||
workflowJsonFileKey = presigned_urls_json["workflowJsonFileKey"]
|
||||
|
||||
# upload asset
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.put(assetFilePresignedUrl, data=open(asset_filepath, "rb")) as resp:
|
||||
assert resp.status == 200
|
||||
|
||||
# upload workflow json
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
async with session.put(workflowJsonFilePresignedUrl, data=json.dumps(prompt['workflow']).encode('utf-8')) as resp:
|
||||
assert resp.status == 200
|
||||
|
||||
model_filenames = extract_model_file_names(prompt['workflow'])
|
||||
model_file_paths = find_file_paths(folder_paths.base_path, model_filenames)
|
||||
|
||||
models_info = {}
|
||||
for filename, filepath in model_file_paths.items():
|
||||
models_info[filename] = {
|
||||
"filename": filename,
|
||||
"sha256_checksum": compute_sha256_checksum(filepath),
|
||||
"relative_path": os.path.relpath(filepath, folder_paths.base_path),
|
||||
}
|
||||
|
||||
# make a POST request to /api/upload_workflow with form data key values
|
||||
async with aiohttp.ClientSession(trust_env=True, connector=aiohttp.TCPConnector(verify_ssl=False)) as session:
|
||||
form = aiohttp.FormData()
|
||||
if comfyworkflows_sharekey:
|
||||
form.add_field("shareKey", comfyworkflows_sharekey)
|
||||
form.add_field("source", "comfyui_manager")
|
||||
form.add_field("assetFileKey", assetFileKey)
|
||||
form.add_field("assetFileType", assetFileType)
|
||||
form.add_field("workflowJsonFileKey", workflowJsonFileKey)
|
||||
form.add_field("sharedWorkflowWorkflowJsonString", json.dumps(prompt['workflow']))
|
||||
form.add_field("sharedWorkflowPromptJsonString", json.dumps(prompt['output']))
|
||||
form.add_field("shareWorkflowCredits", credits)
|
||||
form.add_field("shareWorkflowTitle", title)
|
||||
form.add_field("shareWorkflowDescription", description)
|
||||
form.add_field("shareWorkflowIsNSFW", str(is_nsfw).lower())
|
||||
form.add_field("currentSnapshot", json.dumps(await core.get_current_snapshot()))
|
||||
form.add_field("modelsInfo", json.dumps(models_info))
|
||||
|
||||
async with session.post(
|
||||
f"{share_endpoint}/upload_workflow",
|
||||
data=form,
|
||||
) as resp:
|
||||
assert resp.status == 200
|
||||
upload_workflow_json = await resp.json()
|
||||
workflowId = upload_workflow_json["workflowId"]
|
||||
|
||||
# check if the user has provided Matrix credentials
|
||||
if "matrix" in share_destinations:
|
||||
comfyui_share_room_id = '!LGYSoacpJPhIfBqVfb:matrix.org'
|
||||
filename = os.path.basename(asset_filepath)
|
||||
content_type = assetFileType
|
||||
|
||||
try:
|
||||
from matrix_client.api import MatrixHttpApi
|
||||
from matrix_client.client import MatrixClient
|
||||
|
||||
homeserver = 'matrix.org'
|
||||
if matrix_auth:
|
||||
homeserver = matrix_auth.get('homeserver', 'matrix.org')
|
||||
homeserver = homeserver.replace("http://", "https://")
|
||||
if not homeserver.startswith("https://"):
|
||||
homeserver = "https://" + homeserver
|
||||
|
||||
client = MatrixClient(homeserver)
|
||||
try:
|
||||
token = client.login(username=matrix_auth['username'], password=matrix_auth['password'])
|
||||
if not token:
|
||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
||||
except:
|
||||
return web.json_response({"error": "Invalid Matrix credentials."}, content_type='application/json', status=400)
|
||||
|
||||
matrix = MatrixHttpApi(homeserver, token=token)
|
||||
with open(asset_filepath, 'rb') as f:
|
||||
mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri']
|
||||
|
||||
workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri']
|
||||
|
||||
text_content = ""
|
||||
if title:
|
||||
text_content += f"{title}\n"
|
||||
if description:
|
||||
text_content += f"{description}\n"
|
||||
if credits:
|
||||
text_content += f"\ncredits: {credits}\n"
|
||||
matrix.send_message(comfyui_share_room_id, text_content)
|
||||
matrix.send_content(comfyui_share_room_id, mxc_url, filename, 'm.image')
|
||||
matrix.send_content(comfyui_share_room_id, workflow_json_mxc_url, 'workflow.json', 'm.file')
|
||||
except:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return web.json_response({"error": "An error occurred when sharing your art to Matrix."}, content_type='application/json', status=500)
|
||||
|
||||
return web.json_response({
|
||||
"comfyworkflows": {
|
||||
"url": None if "comfyworkflows" not in share_destinations else f"{share_website_host}/workflows/{workflowId}",
|
||||
},
|
||||
"matrix": {
|
||||
"success": None if "matrix" not in share_destinations else True
|
||||
}
|
||||
}, content_type='application/json', status=200)
|
||||
File diff suppressed because it is too large
Load Diff
41
docs/README.md
Normal file
41
docs/README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# ComfyUI-Manager: Documentation
|
||||
|
||||
This directory contains documentation for the ComfyUI-Manager, providing guides and tutorials for users in multiple languages.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
The documentation is organized into language-specific directories:
|
||||
|
||||
- **en/**: English documentation
|
||||
- **ko/**: Korean documentation
|
||||
|
||||
## Core Documentation Files
|
||||
|
||||
### Command-Line Interface
|
||||
|
||||
- **cm-cli.md**: Documentation for the ComfyUI-Manager Command Line Interface (CLI), which allows using manager functionality without the UI.
|
||||
|
||||
### Advanced Features
|
||||
|
||||
- **use_aria2.md**: Guide for using the aria2 download accelerator with ComfyUI-Manager for faster model downloads.
|
||||
|
||||
## Documentation Standards
|
||||
|
||||
The documentation follows these standards:
|
||||
|
||||
1. **Markdown Format**: All documentation is written in Markdown for easy rendering on GitHub and other platforms
|
||||
2. **Language-specific Directories**: Content is separated by language to facilitate localization
|
||||
3. **Feature-focused Documentation**: Each major feature has its own documentation file
|
||||
4. **Updated with Releases**: Documentation is kept in sync with software releases
|
||||
|
||||
## Contributing to Documentation
|
||||
|
||||
When contributing new documentation:
|
||||
|
||||
1. Place files in the appropriate language directory
|
||||
2. Use clear, concise language appropriate for the target audience
|
||||
3. Include examples where helpful
|
||||
4. Consider adding screenshots or diagrams for complex features
|
||||
5. Maintain consistent formatting with existing documentation
|
||||
|
||||
This documentation directory will continue to grow to support the expanding feature set of ComfyUI-Manager.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,12 +15,9 @@ comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
git_exe_path = os.environ.get('GIT_EXE_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
print("git_helper: environment variable 'COMFYUI_PATH' is not specified.")
|
||||
exit(-1)
|
||||
print("\nWARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path.", file=sys.stderr)
|
||||
comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
|
||||
if not os.path.exists(os.path.join(comfy_path, 'folder_paths.py')):
|
||||
print("git_helper: '{comfy_path}' is not a valid 'COMFYUI_PATH' location.")
|
||||
exit(-1)
|
||||
|
||||
def download_url(url, dest_folder, filename=None):
|
||||
# Ensure the destination folder exists
|
||||
11131
github-stats.json
11131
github-stats.json
File diff suppressed because it is too large
Load Diff
53
glob/README.md
Normal file
53
glob/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# ComfyUI-Manager: Core Backend (glob)
|
||||
|
||||
This directory contains the Python backend modules that power ComfyUI-Manager, handling the core functionality of node management, downloading, security, and server operations.
|
||||
|
||||
## Core Modules
|
||||
|
||||
- **manager_core.py**: The central implementation of management functions, handling configuration, installation, updates, and node management.
|
||||
- **manager_server.py**: Implements server functionality and API endpoints for the web interface to interact with the backend.
|
||||
- **manager_downloader.py**: Handles downloading operations for models, extensions, and other resources.
|
||||
- **manager_util.py**: Provides utility functions used throughout the system.
|
||||
|
||||
## Specialized Modules
|
||||
|
||||
- **cm_global.py**: Maintains global variables and state management across the system.
|
||||
- **cnr_utils.py**: Helper utilities for interacting with the custom node registry (CNR).
|
||||
- **git_utils.py**: Git-specific utilities for repository operations.
|
||||
- **node_package.py**: Handles the packaging and installation of node extensions.
|
||||
- **security_check.py**: Implements the multi-level security system for installation safety.
|
||||
- **share_3rdparty.py**: Manages integration with third-party sharing platforms.
|
||||
|
||||
## Architecture
|
||||
|
||||
The backend follows a modular design pattern with clear separation of concerns:
|
||||
|
||||
1. **Core Layer**: Manager modules provide the primary API and business logic
|
||||
2. **Utility Layer**: Helper modules provide specialized functionality
|
||||
3. **Integration Layer**: Modules that connect to external systems
|
||||
|
||||
## Security Model
|
||||
|
||||
The system implements a comprehensive security framework with multiple levels:
|
||||
|
||||
- **Block**: Highest security - blocks most remote operations
|
||||
- **High**: Allows only specific trusted operations
|
||||
- **Middle**: Standard security for most users
|
||||
- **Normal-**: More permissive for advanced users
|
||||
- **Weak**: Lowest security for development environments
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The backend is designed to work seamlessly with ComfyUI
|
||||
- Asynchronous task queuing is implemented for background operations
|
||||
- The system supports multiple installation modes
|
||||
- Error handling and risk assessment are integrated throughout the codebase
|
||||
|
||||
## API Integration
|
||||
|
||||
The backend exposes a REST API via `manager_server.py` that enables:
|
||||
- Custom node management (install, update, disable, remove)
|
||||
- Model downloading and organization
|
||||
- System configuration
|
||||
- Snapshot management
|
||||
- Workflow component handling
|
||||
@@ -6,9 +6,8 @@ import time
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
from . import context
|
||||
from . import manager_util
|
||||
|
||||
import manager_core
|
||||
import manager_util
|
||||
import requests
|
||||
import toml
|
||||
|
||||
@@ -48,9 +47,9 @@ async def _get_cnr_data(cache_mode=True, dont_wait=True):
|
||||
# Get ComfyUI version tag
|
||||
if is_desktop:
|
||||
# extract version from pyproject.toml instead of git tag
|
||||
comfyui_ver = context.get_current_comfyui_ver() or 'unknown'
|
||||
comfyui_ver = manager_core.get_current_comfyui_ver() or 'unknown'
|
||||
else:
|
||||
comfyui_ver = context.get_comfyui_tag() or 'unknown'
|
||||
comfyui_ver = manager_core.get_comfyui_tag() or 'unknown'
|
||||
|
||||
if is_desktop:
|
||||
if is_windows:
|
||||
@@ -46,6 +46,8 @@ def git_url(fullpath):
|
||||
|
||||
for k, v in config.items():
|
||||
if k.startswith('remote ') and 'url' in v:
|
||||
if 'Comfy-Org/ComfyUI-Manager' in v['url']:
|
||||
return "https://github.com/ltdrdata/ComfyUI-Manager"
|
||||
return v['url']
|
||||
|
||||
return None
|
||||
@@ -23,6 +23,7 @@ import yaml
|
||||
import zipfile
|
||||
import traceback
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
import toml
|
||||
|
||||
orig_print = print
|
||||
|
||||
@@ -31,17 +32,18 @@ from packaging import version
|
||||
|
||||
import uuid
|
||||
|
||||
from ..common import cm_global
|
||||
from ..common import cnr_utils
|
||||
from ..common import manager_util
|
||||
from ..common import git_utils
|
||||
from ..common import manager_downloader
|
||||
from ..common.node_package import InstalledNodePackage
|
||||
from ..common.enums import NetworkMode, SecurityLevel, DBMode
|
||||
from ..common import context
|
||||
glob_path = os.path.join(os.path.dirname(__file__)) # ComfyUI-Manager/glob
|
||||
sys.path.append(glob_path)
|
||||
|
||||
import cm_global
|
||||
import cnr_utils
|
||||
import manager_util
|
||||
import git_utils
|
||||
import manager_downloader
|
||||
from node_package import InstalledNodePackage
|
||||
|
||||
|
||||
version_code = [4, 0]
|
||||
version_code = [3, 33, 8]
|
||||
version_str = f"V{version_code[0]}.{version_code[1]}" + (f'.{version_code[2]}' if len(version_code) > 2 else '')
|
||||
|
||||
|
||||
@@ -56,7 +58,6 @@ class InvalidChannel(Exception):
|
||||
self.channel = channel
|
||||
super().__init__(channel)
|
||||
|
||||
|
||||
def get_default_custom_nodes_path():
|
||||
global default_custom_nodes_path
|
||||
if default_custom_nodes_path is None:
|
||||
@@ -78,6 +79,32 @@ def get_custom_nodes_paths():
|
||||
return [custom_nodes_path]
|
||||
|
||||
|
||||
def get_comfyui_tag():
|
||||
try:
|
||||
repo = git.Repo(comfy_path)
|
||||
return repo.git.describe('--tags')
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_current_comfyui_ver():
|
||||
"""
|
||||
Extract version from pyproject.toml
|
||||
"""
|
||||
toml_path = os.path.join(comfy_path, 'pyproject.toml')
|
||||
if not os.path.exists(toml_path):
|
||||
return None
|
||||
else:
|
||||
try:
|
||||
with open(toml_path, "r", encoding="utf-8") as f:
|
||||
data = toml.load(f)
|
||||
|
||||
project = data.get('project', {})
|
||||
return project.get('version')
|
||||
except:
|
||||
return None
|
||||
|
||||
|
||||
def get_script_env():
|
||||
new_env = os.environ.copy()
|
||||
git_exe = get_config().get('git_exe')
|
||||
@@ -85,10 +112,10 @@ def get_script_env():
|
||||
new_env['GIT_EXE_PATH'] = git_exe
|
||||
|
||||
if 'COMFYUI_PATH' not in new_env:
|
||||
new_env['COMFYUI_PATH'] = context.comfy_path
|
||||
new_env['COMFYUI_PATH'] = comfy_path
|
||||
|
||||
if 'COMFYUI_FOLDERS_BASE_PATH' not in new_env:
|
||||
new_env['COMFYUI_FOLDERS_BASE_PATH'] = context.comfy_path
|
||||
new_env['COMFYUI_FOLDERS_BASE_PATH'] = comfy_path
|
||||
|
||||
return new_env
|
||||
|
||||
@@ -112,10 +139,10 @@ def check_invalid_nodes():
|
||||
import folder_paths
|
||||
except:
|
||||
try:
|
||||
sys.path.append(context.comfy_path)
|
||||
sys.path.append(comfy_path)
|
||||
import folder_paths
|
||||
except:
|
||||
raise Exception(f"Invalid COMFYUI_FOLDERS_BASE_PATH: {context.comfy_path}")
|
||||
raise Exception(f"Invalid COMFYUI_FOLDERS_BASE_PATH: {comfy_path}")
|
||||
|
||||
def check(root):
|
||||
global invalid_nodes
|
||||
@@ -150,6 +177,75 @@ def check_invalid_nodes():
|
||||
print("\n---------------------------------------------------------------------------\n")
|
||||
|
||||
|
||||
# read env vars
|
||||
comfy_path: str = os.environ.get('COMFYUI_PATH')
|
||||
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
try:
|
||||
import folder_paths
|
||||
comfy_path = os.path.join(os.path.dirname(folder_paths.__file__))
|
||||
except:
|
||||
comfy_path = os.path.abspath(os.path.join(manager_util.comfyui_manager_path, '..', '..'))
|
||||
|
||||
if comfy_base_path is None:
|
||||
comfy_base_path = comfy_path
|
||||
|
||||
|
||||
channel_list_template_path = os.path.join(manager_util.comfyui_manager_path, 'channels.list.template')
|
||||
git_script_path = os.path.join(manager_util.comfyui_manager_path, "git_helper.py")
|
||||
|
||||
manager_files_path = None
|
||||
manager_config_path = None
|
||||
manager_channel_list_path = None
|
||||
manager_startup_script_path:str = None
|
||||
manager_snapshot_path = None
|
||||
manager_pip_overrides_path = None
|
||||
manager_pip_blacklist_path = None
|
||||
manager_components_path = None
|
||||
|
||||
def update_user_directory(user_dir):
|
||||
global manager_files_path
|
||||
global manager_config_path
|
||||
global manager_channel_list_path
|
||||
global manager_startup_script_path
|
||||
global manager_snapshot_path
|
||||
global manager_pip_overrides_path
|
||||
global manager_pip_blacklist_path
|
||||
global manager_components_path
|
||||
|
||||
manager_files_path = os.path.abspath(os.path.join(user_dir, 'default', 'ComfyUI-Manager'))
|
||||
if not os.path.exists(manager_files_path):
|
||||
os.makedirs(manager_files_path)
|
||||
|
||||
manager_snapshot_path = os.path.join(manager_files_path, "snapshots")
|
||||
if not os.path.exists(manager_snapshot_path):
|
||||
os.makedirs(manager_snapshot_path)
|
||||
|
||||
manager_startup_script_path = os.path.join(manager_files_path, "startup-scripts")
|
||||
if not os.path.exists(manager_startup_script_path):
|
||||
os.makedirs(manager_startup_script_path)
|
||||
|
||||
manager_config_path = os.path.join(manager_files_path, 'config.ini')
|
||||
manager_channel_list_path = os.path.join(manager_files_path, 'channels.list')
|
||||
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
||||
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
||||
manager_components_path = os.path.join(manager_files_path, "components")
|
||||
manager_util.cache_dir = os.path.join(manager_files_path, "cache")
|
||||
|
||||
if not os.path.exists(manager_util.cache_dir):
|
||||
os.makedirs(manager_util.cache_dir)
|
||||
|
||||
try:
|
||||
import folder_paths
|
||||
update_user_directory(folder_paths.get_user_directory())
|
||||
|
||||
except Exception:
|
||||
# fallback:
|
||||
# This case is only possible when running with cm-cli, and in practice, this case is not actually used.
|
||||
update_user_directory(os.path.abspath(manager_util.comfyui_manager_path))
|
||||
|
||||
|
||||
cached_config = None
|
||||
js_path = None
|
||||
|
||||
@@ -304,18 +400,86 @@ class ManagedResult:
|
||||
return self
|
||||
|
||||
|
||||
class NormalizedKeyDict:
|
||||
def __init__(self):
|
||||
self._store = {}
|
||||
self._key_map = {}
|
||||
|
||||
def _normalize_key(self, key):
|
||||
if isinstance(key, str):
|
||||
return key.strip().lower()
|
||||
return key
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
norm_key = self._normalize_key(key)
|
||||
self._key_map[norm_key] = key
|
||||
self._store[key] = value
|
||||
|
||||
def __getitem__(self, key):
|
||||
norm_key = self._normalize_key(key)
|
||||
original_key = self._key_map[norm_key]
|
||||
return self._store[original_key]
|
||||
|
||||
def __delitem__(self, key):
|
||||
norm_key = self._normalize_key(key)
|
||||
original_key = self._key_map.pop(norm_key)
|
||||
del self._store[original_key]
|
||||
|
||||
def __contains__(self, key):
|
||||
return self._normalize_key(key) in self._key_map
|
||||
|
||||
def get(self, key, default=None):
|
||||
return self[key] if key in self else default
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
if key in self:
|
||||
return self[key]
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def pop(self, key, default=None):
|
||||
if key in self:
|
||||
val = self[key]
|
||||
del self[key]
|
||||
return val
|
||||
if default is not None:
|
||||
return default
|
||||
raise KeyError(key)
|
||||
|
||||
def keys(self):
|
||||
return self._store.keys()
|
||||
|
||||
def values(self):
|
||||
return self._store.values()
|
||||
|
||||
def items(self):
|
||||
return self._store.items()
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._store)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._store)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._store)
|
||||
|
||||
def to_dict(self):
|
||||
return dict(self._store)
|
||||
|
||||
|
||||
class UnifiedManager:
|
||||
def __init__(self):
|
||||
self.installed_node_packages: dict[str, InstalledNodePackage] = {}
|
||||
|
||||
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
|
||||
self.nightly_inactive_nodes = {} # node_id -> fullpath
|
||||
self.unknown_inactive_nodes = {} # node_id -> repo url * fullpath
|
||||
self.active_nodes = {} # node_id -> node_version * fullpath
|
||||
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
||||
self.cnr_map = {} # node_id -> cnr info
|
||||
self.repo_cnr_map = {} # repo_url -> cnr info
|
||||
self.custom_node_map_cache = {} # (channel, mode) -> augmented custom node list json
|
||||
self.cnr_inactive_nodes = NormalizedKeyDict() # node_id -> node_version -> fullpath
|
||||
self.nightly_inactive_nodes = NormalizedKeyDict() # node_id -> fullpath
|
||||
self.unknown_inactive_nodes = {} # node_id -> repo url * fullpath
|
||||
self.active_nodes = NormalizedKeyDict() # node_id -> node_version * fullpath
|
||||
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
||||
self.cnr_map = NormalizedKeyDict() # node_id -> cnr info
|
||||
self.repo_cnr_map = {} # repo_url -> cnr info
|
||||
self.custom_node_map_cache = {} # (channel, mode) -> augmented custom node list json
|
||||
self.processed_install = set()
|
||||
|
||||
def get_module_name(self, x):
|
||||
@@ -457,7 +621,7 @@ class UnifiedManager:
|
||||
ver = str(manager_util.StrictVersion(info['version']))
|
||||
return {'id': cnr['id'], 'cnr': cnr, 'ver': ver}
|
||||
else:
|
||||
return {'id': info['id'], 'ver': info['version']}
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
@@ -633,9 +797,7 @@ class UnifiedManager:
|
||||
|
||||
return latest
|
||||
|
||||
async def reload(self, cache_mode, dont_wait=True, update_cnr_map=True):
|
||||
import folder_paths
|
||||
|
||||
async def reload(self, cache_mode, dont_wait=True):
|
||||
self.custom_node_map_cache = {}
|
||||
self.cnr_inactive_nodes = {} # node_id -> node_version -> fullpath
|
||||
self.nightly_inactive_nodes = {} # node_id -> fullpath
|
||||
@@ -643,18 +805,17 @@ class UnifiedManager:
|
||||
self.unknown_active_nodes = {} # node_id -> repo url * fullpath
|
||||
self.active_nodes = {} # node_id -> node_version * fullpath
|
||||
|
||||
if get_config()['network_mode'] != 'public' or manager_util.is_manager_pip_package():
|
||||
if get_config()['network_mode'] != 'public':
|
||||
dont_wait = True
|
||||
|
||||
if update_cnr_map:
|
||||
# reload 'cnr_map' and 'repo_cnr_map'
|
||||
cnrs = await cnr_utils.get_cnr_data(cache_mode=cache_mode=='cache', dont_wait=dont_wait)
|
||||
# reload 'cnr_map' and 'repo_cnr_map'
|
||||
cnrs = await cnr_utils.get_cnr_data(cache_mode=cache_mode=='cache', dont_wait=dont_wait)
|
||||
|
||||
for x in cnrs:
|
||||
self.cnr_map[x['id']] = x
|
||||
if 'repository' in x:
|
||||
normalized_url = git_utils.normalize_url(x['repository'])
|
||||
self.repo_cnr_map[normalized_url] = x
|
||||
for x in cnrs:
|
||||
self.cnr_map[x['id']] = x
|
||||
if 'repository' in x:
|
||||
normalized_url = git_utils.normalize_url(x['repository'])
|
||||
self.repo_cnr_map[normalized_url] = x
|
||||
|
||||
# reload node status info from custom_nodes/*
|
||||
for custom_nodes_path in folder_paths.get_folder_paths('custom_nodes'):
|
||||
@@ -721,7 +882,7 @@ class UnifiedManager:
|
||||
channel = normalize_channel(channel)
|
||||
nodes = await self.load_nightly(channel, mode)
|
||||
|
||||
res = {}
|
||||
res = NormalizedKeyDict()
|
||||
added_cnr = set()
|
||||
for v in nodes.values():
|
||||
v = v[0]
|
||||
@@ -769,14 +930,15 @@ class UnifiedManager:
|
||||
else:
|
||||
if os.path.exists(requirements_path) and not no_deps:
|
||||
print("Install: pip packages")
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), context.comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
|
||||
lines = manager_util.robust_readlines(requirements_path)
|
||||
for line in lines:
|
||||
package_name = remap_pip_package(line.strip())
|
||||
if package_name and not package_name.startswith('#') and package_name not in self.processed_install:
|
||||
self.processed_install.add(package_name)
|
||||
install_cmd = manager_util.make_pip_cmd(["install", package_name])
|
||||
if package_name.strip() != "" and not package_name.startswith('#'):
|
||||
clean_package_name = package_name.split('#')[0].strip()
|
||||
install_cmd = manager_util.make_pip_cmd(["install", clean_package_name])
|
||||
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)
|
||||
|
||||
pip_fixer.fix_broken()
|
||||
@@ -790,7 +952,7 @@ class UnifiedManager:
|
||||
return res
|
||||
|
||||
def reserve_cnr_switch(self, target, zip_url, from_path, to_path, no_deps):
|
||||
script_path = os.path.join(context.manager_startup_script_path, "install-scripts.txt")
|
||||
script_path = os.path.join(manager_startup_script_path, "install-scripts.txt")
|
||||
with open(script_path, "a") as file:
|
||||
obj = [target, "#LAZY-CNR-SWITCH-SCRIPT", zip_url, from_path, to_path, no_deps, get_default_custom_nodes_path(), sys.executable]
|
||||
file.write(f"{obj}\n")
|
||||
@@ -1196,7 +1358,7 @@ class UnifiedManager:
|
||||
print(f"Download: git clone '{clone_url}'")
|
||||
|
||||
if not instant_execution and platform.system() == 'Windows':
|
||||
res = manager_funcs.run_script([sys.executable, context.git_script_path, "--clone", get_default_custom_nodes_path(), clone_url, repo_path], cwd=get_default_custom_nodes_path())
|
||||
res = manager_funcs.run_script([sys.executable, git_script_path, "--clone", get_default_custom_nodes_path(), clone_url, repo_path], cwd=get_default_custom_nodes_path())
|
||||
if res != 0:
|
||||
return result.fail(f"Failed to clone repo: {clone_url}")
|
||||
else:
|
||||
@@ -1348,20 +1510,12 @@ class UnifiedManager:
|
||||
return self.unified_enable(node_id, version_spec)
|
||||
|
||||
elif version_spec == 'unknown' or version_spec == 'nightly':
|
||||
to_path = os.path.abspath(os.path.join(get_default_custom_nodes_path(), node_id))
|
||||
|
||||
if version_spec == 'nightly':
|
||||
# disable cnr nodes
|
||||
if self.is_enabled(node_id, 'cnr'):
|
||||
self.unified_disable(node_id, False)
|
||||
|
||||
# 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))
|
||||
|
||||
to_path = os.path.abspath(os.path.join(get_default_custom_nodes_path(), node_id))
|
||||
res = self.repo_install(repo_url, to_path, instant_execution=instant_execution, no_deps=no_deps, return_postinstall=return_postinstall)
|
||||
if res.result:
|
||||
if version_spec == 'unknown':
|
||||
@@ -1477,10 +1631,10 @@ def get_channel_dict():
|
||||
if channel_dict is None:
|
||||
channel_dict = {}
|
||||
|
||||
if not os.path.exists(context.manager_channel_list_path):
|
||||
shutil.copy(context.channel_list_template_path, context.manager_channel_list_path)
|
||||
if not os.path.exists(manager_channel_list_path):
|
||||
shutil.copy(channel_list_template_path, manager_channel_list_path)
|
||||
|
||||
with open(context.manager_channel_list_path, 'r') as file:
|
||||
with open(manager_channel_list_path, 'r') as file:
|
||||
channels = file.read()
|
||||
for x in channels.split('\n'):
|
||||
channel_info = x.split("::")
|
||||
@@ -1544,18 +1698,18 @@ def write_config():
|
||||
'db_mode': get_config()['db_mode'],
|
||||
}
|
||||
|
||||
directory = os.path.dirname(context.manager_config_path)
|
||||
directory = os.path.dirname(manager_config_path)
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
|
||||
with open(context.manager_config_path, 'w') as configfile:
|
||||
with open(manager_config_path, 'w') as configfile:
|
||||
config.write(configfile)
|
||||
|
||||
|
||||
def read_config():
|
||||
try:
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read(context.manager_config_path)
|
||||
config.read(manager_config_path)
|
||||
default_conf = config['default']
|
||||
manager_util.use_uv = default_conf['use_uv'].lower() == 'true' if 'use_uv' in default_conf else False
|
||||
|
||||
@@ -1578,18 +1732,21 @@ def read_config():
|
||||
'model_download_by_agent': get_bool('model_download_by_agent', False),
|
||||
'downgrade_blacklist': default_conf.get('downgrade_blacklist', '').lower(),
|
||||
'always_lazy_install': get_bool('always_lazy_install', False),
|
||||
'network_mode': default_conf.get('network_mode', NetworkMode.PUBLIC.value).lower(),
|
||||
'security_level': default_conf.get('security_level', SecurityLevel.NORMAL.value).lower(),
|
||||
'db_mode': default_conf.get('db_mode', DBMode.CACHE.value).lower(),
|
||||
'network_mode': default_conf.get('network_mode', 'public').lower(),
|
||||
'security_level': default_conf.get('security_level', 'normal').lower(),
|
||||
'db_mode': default_conf.get('db_mode', 'cache').lower(),
|
||||
}
|
||||
|
||||
except Exception:
|
||||
manager_util.use_uv = False
|
||||
import importlib.util
|
||||
# temporary disable `uv` on Windows by default (https://github.com/Comfy-Org/ComfyUI-Manager/issues/1969)
|
||||
manager_util.use_uv = importlib.util.find_spec("uv") is not None and platform.system() != "Windows"
|
||||
|
||||
return {
|
||||
'http_channel_enabled': False,
|
||||
'preview_method': manager_funcs.get_current_preview_method(),
|
||||
'git_exe': '',
|
||||
'use_uv': False,
|
||||
'use_uv': manager_util.use_uv,
|
||||
'channel_url': DEFAULT_CHANNEL,
|
||||
'default_cache_as_channel_url': False,
|
||||
'share_option': 'all',
|
||||
@@ -1601,9 +1758,9 @@ def read_config():
|
||||
'model_download_by_agent': False,
|
||||
'downgrade_blacklist': '',
|
||||
'always_lazy_install': False,
|
||||
'network_mode': NetworkMode.OFFLINE.value,
|
||||
'security_level': SecurityLevel.NORMAL.value,
|
||||
'db_mode': DBMode.CACHE.value,
|
||||
'network_mode': 'public', # public | private | offline
|
||||
'security_level': 'normal', # strong | normal | normal- | weak
|
||||
'db_mode': 'cache', # local | cache | remote
|
||||
}
|
||||
|
||||
|
||||
@@ -1675,10 +1832,10 @@ def switch_to_default_branch(repo):
|
||||
|
||||
|
||||
def reserve_script(repo_path, install_cmds):
|
||||
if not os.path.exists(context.manager_startup_script_path):
|
||||
os.makedirs(context.manager_startup_script_path)
|
||||
if not os.path.exists(manager_startup_script_path):
|
||||
os.makedirs(manager_startup_script_path)
|
||||
|
||||
script_path = os.path.join(context.manager_startup_script_path, "install-scripts.txt")
|
||||
script_path = os.path.join(manager_startup_script_path, "install-scripts.txt")
|
||||
with open(script_path, "a") as file:
|
||||
obj = [repo_path] + install_cmds
|
||||
file.write(f"{obj}\n")
|
||||
@@ -1733,11 +1890,11 @@ def try_install_script(url, repo_path, install_cmd, instant_execution=False):
|
||||
# use subprocess to avoid file system lock by git (Windows)
|
||||
def __win_check_git_update(path, do_fetch=False, do_update=False):
|
||||
if do_fetch:
|
||||
command = [sys.executable, context.git_script_path, "--fetch", path]
|
||||
command = [sys.executable, git_script_path, "--fetch", path]
|
||||
elif do_update:
|
||||
command = [sys.executable, context.git_script_path, "--pull", path]
|
||||
command = [sys.executable, git_script_path, "--pull", path]
|
||||
else:
|
||||
command = [sys.executable, context.git_script_path, "--check", path]
|
||||
command = [sys.executable, git_script_path, "--check", path]
|
||||
|
||||
new_env = get_script_env()
|
||||
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=get_default_custom_nodes_path(), env=new_env)
|
||||
@@ -1791,7 +1948,7 @@ def __win_check_git_update(path, do_fetch=False, do_update=False):
|
||||
|
||||
|
||||
def __win_check_git_pull(path):
|
||||
command = [sys.executable, context.git_script_path, "--pull", path]
|
||||
command = [sys.executable, git_script_path, "--pull", path]
|
||||
process = subprocess.Popen(command, env=get_script_env(), cwd=get_default_custom_nodes_path())
|
||||
process.wait()
|
||||
|
||||
@@ -1807,7 +1964,7 @@ def execute_install_script(url, repo_path, lazy_mode=False, instant_execution=Fa
|
||||
else:
|
||||
if os.path.exists(requirements_path) and not no_deps:
|
||||
print("Install: pip packages")
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), context.comfy_path, context.manager_files_path)
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
|
||||
with open(requirements_path, "r") as requirements_file:
|
||||
for line in requirements_file:
|
||||
#handle comments
|
||||
@@ -1987,6 +2144,13 @@ def is_valid_url(url):
|
||||
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):
|
||||
await unified_manager.reload('cache')
|
||||
await unified_manager.get_custom_nodes('default', 'cache')
|
||||
@@ -2004,8 +2168,11 @@ async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=
|
||||
cnr = unified_manager.get_cnr_by_repo(url)
|
||||
if cnr:
|
||||
cnr_id = cnr['id']
|
||||
return await unified_manager.install_by_id(cnr_id, version_spec='nightly', channel='default', mode='cache')
|
||||
return await unified_manager.install_by_id(cnr_id, version_spec=None, channel='default', mode='cache')
|
||||
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]
|
||||
|
||||
# NOTE: Keep original name as possible if unknown node
|
||||
@@ -2033,11 +2200,15 @@ async def gitclone_install(url, instant_execution=False, msg_prefix='', no_deps=
|
||||
clone_url = git_utils.get_url_for_clone(url)
|
||||
|
||||
if not instant_execution and platform.system() == 'Windows':
|
||||
res = manager_funcs.run_script([sys.executable, context.git_script_path, "--clone", get_default_custom_nodes_path(), clone_url, repo_path], cwd=get_default_custom_nodes_path())
|
||||
res = manager_funcs.run_script([sys.executable, git_script_path, "--clone", get_default_custom_nodes_path(), clone_url, repo_path], cwd=get_default_custom_nodes_path())
|
||||
if res != 0:
|
||||
return result.fail(f"Failed to clone '{clone_url}' into '{repo_path}'")
|
||||
else:
|
||||
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.close()
|
||||
|
||||
@@ -2100,7 +2271,7 @@ async def get_data_by_mode(mode, filename, channel_url=None):
|
||||
cache_uri = str(manager_util.simple_hash(uri))+'_'+filename
|
||||
cache_uri = os.path.join(manager_util.cache_dir, cache_uri)
|
||||
|
||||
if get_config()['network_mode'] == 'offline' or manager_util.is_manager_pip_package():
|
||||
if get_config()['network_mode'] == 'offline':
|
||||
# offline network mode
|
||||
if os.path.exists(cache_uri):
|
||||
json_obj = await manager_util.get_data(cache_uri)
|
||||
@@ -2120,7 +2291,7 @@ async def get_data_by_mode(mode, filename, channel_url=None):
|
||||
with open(cache_uri, "w", encoding='utf-8') as file:
|
||||
json.dump(json_obj, file, indent=4, sort_keys=True)
|
||||
except Exception as e:
|
||||
print(f"[ComfyUI-Manager] Due to a network error, switching to local mode.\n=> {filename} @ {channel_url}/{mode}\n=> {e}")
|
||||
print(f"[ComfyUI-Manager] Due to a network error, switching to local mode.\n=> {filename}\n=> {e}")
|
||||
uri = os.path.join(manager_util.comfyui_manager_path, filename)
|
||||
json_obj = await manager_util.get_data(uri)
|
||||
|
||||
@@ -2191,7 +2362,7 @@ def gitclone_uninstall(files):
|
||||
url = url[:-1]
|
||||
try:
|
||||
for custom_nodes_dir in get_custom_nodes_paths():
|
||||
dir_name:str = os.path.splitext(os.path.basename(url))[0].replace(".git", "")
|
||||
dir_name = os.path.splitext(os.path.basename(url))[0].replace(".git", "")
|
||||
dir_path = os.path.join(custom_nodes_dir, dir_name)
|
||||
|
||||
# safety check
|
||||
@@ -2239,7 +2410,7 @@ def gitclone_set_active(files, is_disable):
|
||||
url = url[:-1]
|
||||
try:
|
||||
for custom_nodes_dir in get_custom_nodes_paths():
|
||||
dir_name:str = os.path.splitext(os.path.basename(url))[0].replace(".git", "")
|
||||
dir_name = os.path.splitext(os.path.basename(url))[0].replace(".git", "")
|
||||
dir_path = os.path.join(custom_nodes_dir, dir_name)
|
||||
|
||||
# safety check
|
||||
@@ -2512,7 +2683,7 @@ async def get_current_snapshot(custom_nodes_only = False):
|
||||
await unified_manager.get_custom_nodes('default', 'cache')
|
||||
|
||||
# Get ComfyUI hash
|
||||
repo_path = context.comfy_path
|
||||
repo_path = comfy_path
|
||||
|
||||
comfyui_commit_hash = None
|
||||
if not custom_nodes_only:
|
||||
@@ -2588,7 +2759,7 @@ async def save_snapshot_with_postfix(postfix, path=None, custom_nodes_only = Fal
|
||||
date_time_format = now.strftime("%Y-%m-%d_%H-%M-%S")
|
||||
file_name = f"{date_time_format}_{postfix}"
|
||||
|
||||
path = os.path.join(context.manager_snapshot_path, f"{file_name}.json")
|
||||
path = os.path.join(manager_snapshot_path, f"{file_name}.json")
|
||||
else:
|
||||
file_name = path.replace('\\', '/').split('/')[-1]
|
||||
file_name = file_name.split('.')[-2]
|
||||
@@ -2776,7 +2947,7 @@ async def get_unified_total_nodes(channel, mode, regsitry_cache_mode='cache'):
|
||||
|
||||
if cnr_id is not None:
|
||||
# cnr or nightly version
|
||||
cnr_ids.remove(cnr_id)
|
||||
cnr_ids.discard(cnr_id)
|
||||
updatable = False
|
||||
cnr = unified_manager.cnr_map[cnr_id]
|
||||
|
||||
@@ -3170,7 +3341,7 @@ async def restore_snapshot(snapshot_path, git_helper_extras=None):
|
||||
|
||||
def get_comfyui_versions(repo=None):
|
||||
if repo is None:
|
||||
repo = git.Repo(context.comfy_path)
|
||||
repo = git.Repo(comfy_path)
|
||||
|
||||
try:
|
||||
remote = get_remote_name(repo)
|
||||
@@ -3204,7 +3375,7 @@ def get_comfyui_versions(repo=None):
|
||||
|
||||
|
||||
def switch_comfyui(tag):
|
||||
repo = git.Repo(context.comfy_path)
|
||||
repo = git.Repo(comfy_path)
|
||||
|
||||
if tag == 'nightly':
|
||||
repo.git.checkout('master')
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,18 +15,16 @@ import re
|
||||
import logging
|
||||
import platform
|
||||
import shlex
|
||||
import cm_global
|
||||
|
||||
|
||||
cache_lock = threading.Lock()
|
||||
session_lock = threading.Lock()
|
||||
|
||||
comfyui_manager_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||
cache_dir = os.path.join(comfyui_manager_path, '.cache') # This path is also updated together in **manager_core.update_user_directory**.
|
||||
|
||||
use_uv = False
|
||||
|
||||
def is_manager_pip_package():
|
||||
return not os.path.exists(os.path.join(comfyui_manager_path, '..', 'custom_nodes'))
|
||||
|
||||
def add_python_path_to_env():
|
||||
if platform.system() != "Windows":
|
||||
@@ -259,7 +257,7 @@ def get_installed_packages(renew=False):
|
||||
pip_map[normalized_name] = y[1]
|
||||
except subprocess.CalledProcessError:
|
||||
logging.error("[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
|
||||
return set()
|
||||
return {}
|
||||
|
||||
return pip_map
|
||||
|
||||
@@ -310,6 +308,7 @@ def parse_requirement_line(line):
|
||||
|
||||
|
||||
torch_torchvision_torchaudio_version_map = {
|
||||
'2.7.0': ('0.22.0', '2.7.0'),
|
||||
'2.6.0': ('0.21.0', '2.6.0'),
|
||||
'2.5.1': ('0.20.0', '2.5.0'),
|
||||
'2.5.0': ('0.20.0', '2.5.0'),
|
||||
@@ -413,8 +412,9 @@ class PIPFixer:
|
||||
|
||||
if len(targets) > 0:
|
||||
for x in targets:
|
||||
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}", "numpy<2"])
|
||||
subprocess.check_output(cmd, universal_newlines=True)
|
||||
if sys.version_info < (3, 13):
|
||||
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}", "numpy<2"])
|
||||
subprocess.check_output(cmd, universal_newlines=True)
|
||||
|
||||
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
|
||||
except Exception as e:
|
||||
@@ -422,17 +422,21 @@ class PIPFixer:
|
||||
logging.error(e)
|
||||
|
||||
# fix numpy
|
||||
try:
|
||||
np = new_pip_versions.get('numpy')
|
||||
if np is not None:
|
||||
if StrictVersion(np) >= StrictVersion('2'):
|
||||
cmd = make_pip_cmd(['install', "numpy<2"])
|
||||
subprocess.check_output(cmd , universal_newlines=True)
|
||||
if sys.version_info >= (3, 13):
|
||||
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`.")
|
||||
else:
|
||||
try:
|
||||
np = new_pip_versions.get('numpy')
|
||||
if cm_global.pip_overrides.get('numpy') == 'numpy<2':
|
||||
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")
|
||||
except Exception as e:
|
||||
logging.error("[ComfyUI-Manager] Failed to restore numpy")
|
||||
logging.error(e)
|
||||
logging.info("[ComfyUI-Manager] 'numpy' dependency were fixed")
|
||||
except Exception as e:
|
||||
logging.error("[ComfyUI-Manager] Failed to restore numpy")
|
||||
logging.error(e)
|
||||
|
||||
# fix missing frontend
|
||||
try:
|
||||
@@ -472,7 +476,7 @@ class PIPFixer:
|
||||
normalized_name = parsed['package'].lower().replace('-', '_')
|
||||
if normalized_name in new_pip_versions:
|
||||
if 'version' in parsed and 'operator' in parsed:
|
||||
cur = StrictVersion(new_pip_versions[parsed['package']])
|
||||
cur = StrictVersion(new_pip_versions[normalized_name])
|
||||
dest = parsed['version']
|
||||
op = parsed['operator']
|
||||
if cur == dest:
|
||||
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import os
|
||||
|
||||
from .git_utils import get_commit_hash
|
||||
from git_utils import get_commit_hash
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -2,6 +2,8 @@ import sys
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
import manager_util
|
||||
|
||||
|
||||
def security_check():
|
||||
print("[START] Security scan")
|
||||
@@ -66,11 +68,11 @@ https://blog.comfy.org/comfyui-statement-on-the-ultralytics-crypto-miner-situati
|
||||
"lolMiner": [os.path.join(comfyui_path, 'lolMiner')]
|
||||
}
|
||||
|
||||
installed_pips = subprocess.check_output([sys.executable, '-m', "pip", "freeze"], text=True)
|
||||
installed_pips = subprocess.check_output(manager_util.make_pip_cmd(["freeze"]), text=True)
|
||||
|
||||
detected = set()
|
||||
try:
|
||||
anthropic_info = subprocess.check_output([sys.executable, '-m', "pip", "show", "anthropic"], text=True, stderr=subprocess.DEVNULL)
|
||||
anthropic_info = subprocess.check_output(manager_util.make_pip_cmd(["show", "anthropic"]), text=True, stderr=subprocess.DEVNULL)
|
||||
anthropic_reqs = [x for x in anthropic_info.split('\n') if x.startswith("Requires")][0].split(': ')[1]
|
||||
if "pycrypto" in anthropic_reqs:
|
||||
location = [x for x in anthropic_info.split('\n') if x.startswith("Location")][0].split(': ')[1]
|
||||
@@ -1,7 +1,5 @@
|
||||
import mimetypes
|
||||
from ..common import context
|
||||
from . import manager_core as core
|
||||
|
||||
import manager_core as core
|
||||
import os
|
||||
from aiohttp import web
|
||||
import aiohttp
|
||||
@@ -55,7 +53,7 @@ def compute_sha256_checksum(filepath):
|
||||
return sha256.hexdigest()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/share_option")
|
||||
@PromptServer.instance.routes.get("/manager/share_option")
|
||||
async def share_option(request):
|
||||
if "value" in request.rel_url.query:
|
||||
core.get_config()['share_option'] = request.rel_url.query['value']
|
||||
@@ -67,10 +65,10 @@ async def share_option(request):
|
||||
|
||||
|
||||
def get_openart_auth():
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, ".openart_key")):
|
||||
if not os.path.exists(os.path.join(core.manager_files_path, ".openart_key")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(context.manager_files_path, ".openart_key"), "r") as f:
|
||||
with open(os.path.join(core.manager_files_path, ".openart_key"), "r") as f:
|
||||
openart_key = f.read().strip()
|
||||
return openart_key if openart_key else None
|
||||
except:
|
||||
@@ -78,10 +76,10 @@ def get_openart_auth():
|
||||
|
||||
|
||||
def get_matrix_auth():
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, "matrix_auth")):
|
||||
if not os.path.exists(os.path.join(core.manager_files_path, "matrix_auth")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(context.manager_files_path, "matrix_auth"), "r") as f:
|
||||
with open(os.path.join(core.manager_files_path, "matrix_auth"), "r") as f:
|
||||
matrix_auth = f.read()
|
||||
homeserver, username, password = matrix_auth.strip().split("\n")
|
||||
if not homeserver or not username or not password:
|
||||
@@ -96,10 +94,10 @@ def get_matrix_auth():
|
||||
|
||||
|
||||
def get_comfyworkflows_auth():
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, "comfyworkflows_sharekey")):
|
||||
if not os.path.exists(os.path.join(core.manager_files_path, "comfyworkflows_sharekey")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(context.manager_files_path, "comfyworkflows_sharekey"), "r") as f:
|
||||
with open(os.path.join(core.manager_files_path, "comfyworkflows_sharekey"), "r") as f:
|
||||
share_key = f.read()
|
||||
if not share_key.strip():
|
||||
return None
|
||||
@@ -109,10 +107,10 @@ def get_comfyworkflows_auth():
|
||||
|
||||
|
||||
def get_youml_settings():
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, ".youml")):
|
||||
if not os.path.exists(os.path.join(core.manager_files_path, ".youml")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(context.manager_files_path, ".youml"), "r") as f:
|
||||
with open(os.path.join(core.manager_files_path, ".youml"), "r") as f:
|
||||
youml_settings = f.read().strip()
|
||||
return youml_settings if youml_settings else None
|
||||
except:
|
||||
@@ -120,11 +118,11 @@ def get_youml_settings():
|
||||
|
||||
|
||||
def set_youml_settings(settings):
|
||||
with open(os.path.join(context.manager_files_path, ".youml"), "w") as f:
|
||||
with open(os.path.join(core.manager_files_path, ".youml"), "w") as f:
|
||||
f.write(settings)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_openart_auth")
|
||||
@PromptServer.instance.routes.get("/manager/get_openart_auth")
|
||||
async def api_get_openart_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
openart_key = get_openart_auth()
|
||||
@@ -133,16 +131,16 @@ async def api_get_openart_auth(request):
|
||||
return web.json_response({"openart_key": openart_key})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/v2/manager/set_openart_auth")
|
||||
@PromptServer.instance.routes.post("/manager/set_openart_auth")
|
||||
async def api_set_openart_auth(request):
|
||||
json_data = await request.json()
|
||||
openart_key = json_data['openart_key']
|
||||
with open(os.path.join(context.manager_files_path, ".openart_key"), "w") as f:
|
||||
with open(os.path.join(core.manager_files_path, ".openart_key"), "w") as f:
|
||||
f.write(openart_key)
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_matrix_auth")
|
||||
@PromptServer.instance.routes.get("/manager/get_matrix_auth")
|
||||
async def api_get_matrix_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
matrix_auth = get_matrix_auth()
|
||||
@@ -151,7 +149,7 @@ async def api_get_matrix_auth(request):
|
||||
return web.json_response(matrix_auth)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/youml/settings")
|
||||
@PromptServer.instance.routes.get("/manager/youml/settings")
|
||||
async def api_get_youml_settings(request):
|
||||
youml_settings = get_youml_settings()
|
||||
if not youml_settings:
|
||||
@@ -159,14 +157,14 @@ async def api_get_youml_settings(request):
|
||||
return web.json_response(json.loads(youml_settings))
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/v2/manager/youml/settings")
|
||||
@PromptServer.instance.routes.post("/manager/youml/settings")
|
||||
async def api_set_youml_settings(request):
|
||||
json_data = await request.json()
|
||||
set_youml_settings(json.dumps(json_data))
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_comfyworkflows_auth")
|
||||
@PromptServer.instance.routes.get("/manager/get_comfyworkflows_auth")
|
||||
async def api_get_comfyworkflows_auth(request):
|
||||
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
|
||||
# in the same directory as the ComfyUI base folder
|
||||
@@ -177,17 +175,17 @@ async def api_get_comfyworkflows_auth(request):
|
||||
return web.json_response({"comfyworkflows_sharekey": comfyworkflows_auth})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/v2/manager/set_esheep_workflow_and_images")
|
||||
@PromptServer.instance.routes.post("/manager/set_esheep_workflow_and_images")
|
||||
async def set_esheep_workflow_and_images(request):
|
||||
json_data = await request.json()
|
||||
with open(os.path.join(context.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:
|
||||
json.dump(json_data, file, indent=4)
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_esheep_workflow_and_images")
|
||||
@PromptServer.instance.routes.get("/manager/get_esheep_workflow_and_images")
|
||||
async def get_esheep_workflow_and_images(request):
|
||||
with open(os.path.join(context.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)
|
||||
return web.Response(status=200, text=json.dumps(data))
|
||||
|
||||
@@ -196,12 +194,12 @@ def set_matrix_auth(json_data):
|
||||
homeserver = json_data['homeserver']
|
||||
username = json_data['username']
|
||||
password = json_data['password']
|
||||
with open(os.path.join(context.manager_files_path, "matrix_auth"), "w") as f:
|
||||
with open(os.path.join(core.manager_files_path, "matrix_auth"), "w") as f:
|
||||
f.write("\n".join([homeserver, username, password]))
|
||||
|
||||
|
||||
def set_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
with open(os.path.join(context.manager_files_path, "comfyworkflows_sharekey"), "w") as f:
|
||||
with open(os.path.join(core.manager_files_path, "comfyworkflows_sharekey"), "w") as f:
|
||||
f.write(comfyworkflows_sharekey)
|
||||
|
||||
|
||||
@@ -213,7 +211,7 @@ def has_provided_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
return comfyworkflows_sharekey.strip()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/v2/manager/share")
|
||||
@PromptServer.instance.routes.post("/manager/share")
|
||||
async def share_art(request):
|
||||
# get json data
|
||||
json_data = await request.json()
|
||||
50
js/README.md
Normal file
50
js/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# ComfyUI-Manager: Frontend (js)
|
||||
|
||||
This directory contains the JavaScript frontend implementation for ComfyUI-Manager, providing the user interface components that interact with the backend API.
|
||||
|
||||
## Core Components
|
||||
|
||||
- **comfyui-manager.js**: Main entry point that initializes the manager UI and integrates with ComfyUI.
|
||||
- **custom-nodes-manager.js**: Implements the UI for browsing, installing, and managing custom nodes.
|
||||
- **model-manager.js**: Handles the model management interface for downloading and organizing AI models.
|
||||
- **components-manager.js**: Manages reusable workflow components system.
|
||||
- **snapshot.js**: Implements the snapshot system for backing up and restoring installations.
|
||||
|
||||
## Sharing Components
|
||||
|
||||
- **comfyui-share-common.js**: Base functionality for workflow sharing features.
|
||||
- **comfyui-share-copus.js**: Integration with the ComfyUI Opus sharing platform.
|
||||
- **comfyui-share-openart.js**: Integration with the OpenArt sharing platform.
|
||||
- **comfyui-share-youml.js**: Integration with the YouML sharing platform.
|
||||
|
||||
## Utility Components
|
||||
|
||||
- **cm-api.js**: Client-side API wrapper for communication with the backend.
|
||||
- **common.js**: Shared utilities and helper functions used across the frontend.
|
||||
- **node_fixer.js**: Utilities for fixing disconnected links and repairing malformed nodes by recreating them while preserving connections.
|
||||
- **popover-helper.js**: UI component for popup tooltips and contextual information.
|
||||
- **turbogrid.esm.js**: Grid component library - https://github.com/cenfun/turbogrid
|
||||
- **workflow-metadata.js**: Handles workflow metadata parsing, validation and cross-repository compatibility including versioning, dependencies tracking, and resource management.
|
||||
|
||||
## Architecture
|
||||
|
||||
The frontend follows a modular component-based architecture:
|
||||
|
||||
1. **Integration Layer**: Connects with ComfyUI's existing UI system
|
||||
2. **Manager Components**: Individual functional UI components (node manager, model manager, etc.)
|
||||
3. **Sharing Components**: Platform-specific sharing implementations
|
||||
4. **Utility Layer**: Reusable UI components and helpers
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The frontend integrates directly with ComfyUI's UI system through `app.js`
|
||||
- Dialog-based UI for most manager functions to avoid cluttering the main interface
|
||||
- Asynchronous API calls to handle backend operations without blocking the UI
|
||||
|
||||
## Styling
|
||||
|
||||
CSS files are included for specific components:
|
||||
- **custom-nodes-manager.css**: Styling for the node management UI
|
||||
- **model-manager.css**: Styling for the model management UI
|
||||
|
||||
This frontend implementation provides a comprehensive yet user-friendly interface for managing the ComfyUI ecosystem.
|
||||
@@ -25,7 +25,7 @@ async function tryInstallCustomNode(event) {
|
||||
const res = await customConfirm(msg);
|
||||
if(res) {
|
||||
if(event.detail.target.installed == 'Disabled') {
|
||||
const response = await api.fetchApi(`/v2/customnode/toggle_active`, {
|
||||
const response = await api.fetchApi(`/customnode/toggle_active`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(event.detail.target)
|
||||
@@ -35,7 +35,7 @@ async function tryInstallCustomNode(event) {
|
||||
await sleep(300);
|
||||
app.ui.dialog.show(`Installing... '${event.detail.target.title}'`);
|
||||
|
||||
const response = await api.fetchApi(`/v2/customnode/install`, {
|
||||
const response = await api.fetchApi(`/customnode/install`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(event.detail.target)
|
||||
@@ -52,7 +52,7 @@ async function tryInstallCustomNode(event) {
|
||||
}
|
||||
}
|
||||
|
||||
let response = await api.fetchApi("/v2/manager/reboot");
|
||||
let response = await api.fetchApi("/manager/reboot");
|
||||
if(response.status == 403) {
|
||||
show_message('This action is not allowed with this security level configuration.');
|
||||
return false;
|
||||
@@ -14,9 +14,9 @@ import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
||||
import {
|
||||
free_models, install_pip, install_via_git_url, manager_instance,
|
||||
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
||||
infoToast, showTerminal, setNeedRestart, generateUUID
|
||||
infoToast, showTerminal, setNeedRestart
|
||||
} from "./common.js";
|
||||
import { ComponentBuilderDialog, load_components, set_component_policy } from "./components-manager.js";
|
||||
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
||||
import { CustomNodesManager } from "./custom-nodes-manager.js";
|
||||
import { ModelManager } from "./model-manager.js";
|
||||
import { SnapshotManager } from "./snapshot.js";
|
||||
@@ -189,7 +189,8 @@ docStyle.innerHTML = `
|
||||
}
|
||||
`;
|
||||
|
||||
function isBeforeFrontendVersion(compareVersion) {
|
||||
function is_legacy_front() {
|
||||
let compareVersion = '1.2.49';
|
||||
try {
|
||||
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
|
||||
if (typeof frontendVersion !== 'string') {
|
||||
@@ -222,9 +223,6 @@ function isBeforeFrontendVersion(compareVersion) {
|
||||
}
|
||||
}
|
||||
|
||||
const is_legacy_front = () => isBeforeFrontendVersion('1.2.49');
|
||||
const isNotNewManagerUI = () => isBeforeFrontendVersion('1.16.4');
|
||||
|
||||
document.head.appendChild(docStyle);
|
||||
|
||||
var update_comfyui_button = null;
|
||||
@@ -234,7 +232,7 @@ var restart_stop_button = null;
|
||||
var update_policy_combo = null;
|
||||
|
||||
let share_option = 'all';
|
||||
var batch_id = null;
|
||||
var is_updating = false;
|
||||
|
||||
|
||||
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
||||
@@ -417,7 +415,7 @@ const style = `
|
||||
`;
|
||||
|
||||
async function init_share_option() {
|
||||
api.fetchApi('/v2/manager/share_option')
|
||||
api.fetchApi('/manager/share_option')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
share_option = data || 'all';
|
||||
@@ -425,7 +423,7 @@ async function init_share_option() {
|
||||
}
|
||||
|
||||
async function init_notice(notice) {
|
||||
api.fetchApi('/v2/manager/notice')
|
||||
api.fetchApi('/manager/notice')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
notice.innerHTML = data;
|
||||
@@ -476,19 +474,14 @@ async function updateComfyUI() {
|
||||
let prev_text = update_comfyui_button.innerText;
|
||||
update_comfyui_button.innerText = "Updating ComfyUI...";
|
||||
|
||||
// set_inprogress_mode();
|
||||
set_inprogress_mode();
|
||||
|
||||
const response = await api.fetchApi('/manager/queue/update_comfyui');
|
||||
|
||||
showTerminal();
|
||||
|
||||
batch_id = generateUUID();
|
||||
|
||||
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)
|
||||
});
|
||||
is_updating = true;
|
||||
await api.fetchApi('/manager/queue/start');
|
||||
}
|
||||
|
||||
function showVersionSelectorDialog(versions, current, onSelect) {
|
||||
@@ -619,7 +612,7 @@ async function switchComfyUI() {
|
||||
switch_comfyui_button.disabled = true;
|
||||
switch_comfyui_button.style.backgroundColor = "gray";
|
||||
|
||||
let res = await api.fetchApi(`/v2/comfyui_manager/comfyui_versions`, { cache: "no-store" });
|
||||
let res = await api.fetchApi(`/comfyui_manager/comfyui_versions`, { cache: "no-store" });
|
||||
|
||||
switch_comfyui_button.disabled = false;
|
||||
switch_comfyui_button.style.backgroundColor = "";
|
||||
@@ -638,14 +631,14 @@ async function switchComfyUI() {
|
||||
showVersionSelectorDialog(versions, obj.current, async (selected_version) => {
|
||||
if(selected_version == 'nightly') {
|
||||
update_policy_combo.value = 'nightly-comfyui';
|
||||
api.fetchApi('/v2/manager/policy/update?value=nightly-comfyui');
|
||||
api.fetchApi('/manager/policy/update?value=nightly-comfyui');
|
||||
}
|
||||
else {
|
||||
update_policy_combo.value = 'stable-comfyui';
|
||||
api.fetchApi('/v2/manager/policy/update?value=stable-comfyui');
|
||||
api.fetchApi('/manager/policy/update?value=stable-comfyui');
|
||||
}
|
||||
|
||||
let response = await api.fetchApi(`/v2/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
|
||||
let response = await api.fetchApi(`/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
|
||||
if (response.status == 200) {
|
||||
infoToast(`ComfyUI version is switched to ${selected_version}`);
|
||||
}
|
||||
@@ -663,17 +656,18 @@ async function onQueueStatus(event) {
|
||||
const isElectron = 'electronAPI' in window;
|
||||
|
||||
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})`;
|
||||
}
|
||||
else if(event.detail.status == 'all-done') {
|
||||
else if(event.detail.status == 'done') {
|
||||
reset_action_buttons();
|
||||
}
|
||||
else if(event.detail.status == 'batch-done') {
|
||||
if(batch_id != event.detail.batch_id) {
|
||||
|
||||
if(!is_updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
is_updating = false;
|
||||
|
||||
let success_list = [];
|
||||
let failed_list = [];
|
||||
let comfyui_state = null;
|
||||
@@ -773,28 +767,41 @@ api.addEventListener("cm-queue-status", onQueueStatus);
|
||||
async function updateAll(update_comfyui) {
|
||||
update_all_button.innerText = "Updating...";
|
||||
|
||||
// set_inprogress_mode();
|
||||
set_inprogress_mode();
|
||||
|
||||
var mode = manager_instance.datasrc_combo.value;
|
||||
|
||||
showTerminal();
|
||||
|
||||
batch_id = generateUUID();
|
||||
|
||||
let batch = {};
|
||||
if(update_comfyui) {
|
||||
update_all_button.innerText = "Updating ComfyUI...";
|
||||
batch['update_comfyui'] = true;
|
||||
await api.fetchApi('/manager/queue/update_comfyui');
|
||||
}
|
||||
|
||||
batch['update_all'] = mode;
|
||||
const response = await api.fetchApi(`/manager/queue/update_all?mode=${mode}`);
|
||||
|
||||
const res = await api.fetchApi(`/v2/manager/queue/batch`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(batch)
|
||||
});
|
||||
if (response.status == 401) {
|
||||
customAlert('Another task is already in progress. Please stop the ongoing task first.');
|
||||
}
|
||||
else if(response.status == 200) {
|
||||
is_updating = true;
|
||||
await api.fetchApi('/manager/queue/start');
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
*/
|
||||
@@ -807,7 +814,7 @@ function restartOrStop() {
|
||||
rebootAPI();
|
||||
}
|
||||
else {
|
||||
api.fetchApi('/v2/manager/queue/reset');
|
||||
api.fetchApi('/manager/queue/reset');
|
||||
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||
}
|
||||
}
|
||||
@@ -955,12 +962,12 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
|
||||
this.datasrc_combo.appendChild($el('option', { value: 'remote', text: 'DB: Channel (remote)' }, []));
|
||||
|
||||
api.fetchApi('/v2/manager/db_mode')
|
||||
api.fetchApi('/manager/db_mode')
|
||||
.then(response => response.text())
|
||||
.then(data => { this.datasrc_combo.value = data; });
|
||||
|
||||
this.datasrc_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/v2/manager/db_mode?value=${event.target.value}`);
|
||||
api.fetchApi(`/manager/db_mode?value=${event.target.value}`);
|
||||
});
|
||||
|
||||
// preview method
|
||||
@@ -972,19 +979,19 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Preview method: Latent2RGB (fast)' }, []));
|
||||
preview_combo.appendChild($el('option', { value: 'none', text: 'Preview method: None (very fast)' }, []));
|
||||
|
||||
api.fetchApi('/v2/manager/preview_method')
|
||||
api.fetchApi('/manager/preview_method')
|
||||
.then(response => response.text())
|
||||
.then(data => { preview_combo.value = data; });
|
||||
|
||||
preview_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/v2/manager/preview_method?value=${event.target.value}`);
|
||||
api.fetchApi(`/manager/preview_method?value=${event.target.value}`);
|
||||
});
|
||||
|
||||
// channel
|
||||
let channel_combo = document.createElement("select");
|
||||
channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list.");
|
||||
channel_combo.className = "cm-menu-combo";
|
||||
api.fetchApi('/v2/manager/channel_url_list')
|
||||
api.fetchApi('/manager/channel_url_list')
|
||||
.then(response => response.json())
|
||||
.then(async data => {
|
||||
try {
|
||||
@@ -997,7 +1004,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
channel_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/v2/manager/channel_url_list?value=${event.target.value}`);
|
||||
api.fetchApi(`/manager/channel_url_list?value=${event.target.value}`);
|
||||
});
|
||||
|
||||
channel_combo.value = data.selected;
|
||||
@@ -1025,7 +1032,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
|
||||
}
|
||||
|
||||
api.fetchApi('/v2/manager/share_option')
|
||||
api.fetchApi('/manager/share_option')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
share_combo.value = data || 'all';
|
||||
@@ -1035,7 +1042,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
share_combo.addEventListener('change', function (event) {
|
||||
const value = event.target.value;
|
||||
share_option = value;
|
||||
api.fetchApi(`/v2/manager/share_option?value=${value}`);
|
||||
api.fetchApi(`/manager/share_option?value=${value}`);
|
||||
const shareButton = document.getElementById("shareButton");
|
||||
if (value === 'none') {
|
||||
shareButton.style.display = "none";
|
||||
@@ -1050,7 +1057,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, []));
|
||||
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, []));
|
||||
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, []));
|
||||
api.fetchApi('/v2/manager/policy/component')
|
||||
api.fetchApi('/manager/policy/component')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
component_policy_combo.value = data;
|
||||
@@ -1058,7 +1065,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
});
|
||||
|
||||
component_policy_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/v2/manager/policy/component?value=${event.target.value}`);
|
||||
api.fetchApi(`/manager/policy/component?value=${event.target.value}`);
|
||||
set_component_policy(event.target.value);
|
||||
});
|
||||
|
||||
@@ -1071,14 +1078,14 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
update_policy_combo.className = "cm-menu-combo";
|
||||
update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, []));
|
||||
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly Version' }, []));
|
||||
api.fetchApi('/v2/manager/policy/update')
|
||||
api.fetchApi('/manager/policy/update')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
update_policy_combo.value = data;
|
||||
});
|
||||
|
||||
update_policy_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/v2/manager/policy/update?value=${event.target.value}`);
|
||||
api.fetchApi(`/manager/policy/update?value=${event.target.value}`);
|
||||
});
|
||||
|
||||
return [
|
||||
@@ -1381,12 +1388,12 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
async function getVersion() {
|
||||
let version = await api.fetchApi(`/v2/manager/version`);
|
||||
let version = await api.fetchApi(`/manager/version`);
|
||||
return await version.text();
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Legacy.ManagerMenu",
|
||||
name: "Comfy.ManagerMenu",
|
||||
|
||||
aboutPageBadges: [
|
||||
{
|
||||
@@ -1518,10 +1525,7 @@ app.registerExtension({
|
||||
}).element
|
||||
);
|
||||
|
||||
const shouldShowLegacyMenuItems = isNotNewManagerUI();
|
||||
if (shouldShowLegacyMenuItems) {
|
||||
app.menu?.settingsGroup.element.before(cmGroup.element);
|
||||
}
|
||||
app.menu?.settingsGroup.element.before(cmGroup.element);
|
||||
}
|
||||
catch(exception) {
|
||||
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 { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
|
||||
const workflow = prompt['workflow']
|
||||
api.fetchApi(`/v2/manager/set_esheep_workflow_and_images`, {
|
||||
api.fetchApi(`/manager/set_esheep_workflow_and_images`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -812,7 +812,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
// get the user's existing matrix auth and share key
|
||||
ShareDialog.matrix_auth = { homeserver: "matrix.org", username: "", password: "" };
|
||||
try {
|
||||
api.fetchApi(`/v2/manager/get_matrix_auth`)
|
||||
api.fetchApi(`/manager/get_matrix_auth`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
ShareDialog.matrix_auth = data;
|
||||
@@ -831,7 +831,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
ShareDialog.cw_sharekey = "";
|
||||
try {
|
||||
// console.log("Fetching comfyworkflows share key")
|
||||
api.fetchApi(`/v2/manager/get_comfyworkflows_auth`)
|
||||
api.fetchApi(`/manager/get_comfyworkflows_auth`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
ShareDialog.cw_sharekey = data.comfyworkflows_sharekey;
|
||||
@@ -891,7 +891,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
// Change the text of the share button to "Sharing..." to indicate that the share process has started
|
||||
this.share_button.textContent = "Sharing...";
|
||||
|
||||
const response = await api.fetchApi(`/v2/manager/share`, {
|
||||
const response = await api.fetchApi(`/manager/share`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -71,7 +71,7 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
this.allFiles = [];
|
||||
this.titleNum = 0;
|
||||
}
|
||||
|
||||
|
||||
createButtons() {
|
||||
const inputStyle = {
|
||||
display: "block",
|
||||
@@ -202,7 +202,7 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
this.LockInput = $el("input", {
|
||||
type: "text",
|
||||
placeholder: "",
|
||||
style: {
|
||||
style: {
|
||||
width: "100px",
|
||||
padding: "7px",
|
||||
borderRadius: "4px",
|
||||
@@ -301,7 +301,7 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
|
||||
const titleNumDom = $el(
|
||||
"label",
|
||||
{
|
||||
@@ -342,15 +342,11 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
["0/70"]
|
||||
);
|
||||
// Additional Inputs Section
|
||||
const additionalInputsSection = $el(
|
||||
"div",
|
||||
{ style: { ...sectionStyle, } },
|
||||
[
|
||||
$el("label", { style: labelStyle }, ["3️⃣ Title "]),
|
||||
this.TitleInput,
|
||||
titleNumDom,
|
||||
]
|
||||
);
|
||||
const additionalInputsSection = $el("div", { style: { ...sectionStyle } }, [
|
||||
$el("label", { style: labelStyle }, ["3️⃣ Title "]),
|
||||
this.TitleInput,
|
||||
titleNumDom,
|
||||
]);
|
||||
const SubtitleSection = $el("div", { style: sectionStyle }, [
|
||||
$el("label", { style: labelStyle }, ["4️⃣ Subtitle "]),
|
||||
this.SubTitleInput,
|
||||
@@ -392,11 +388,31 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
},
|
||||
[
|
||||
this.radioButtonsCheck_lock,
|
||||
$el("div", { style: { marginLeft: "5px" ,display:'flex',alignItems:'center'} }, [
|
||||
$el("span", { style: { marginLeft: "5px" } }, ["ON"]),
|
||||
$el("span", { style: { marginLeft: "20px",marginRight:'10px' ,color:'#fff'} }, ["Price US$"]),
|
||||
this.LockInput
|
||||
]),
|
||||
$el(
|
||||
"div",
|
||||
{
|
||||
style: {
|
||||
marginLeft: "5px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
},
|
||||
[
|
||||
$el("span", { style: { marginLeft: "5px" } }, ["ON"]),
|
||||
$el(
|
||||
"span",
|
||||
{
|
||||
style: {
|
||||
marginLeft: "20px",
|
||||
marginRight: "10px",
|
||||
color: "#fff",
|
||||
},
|
||||
},
|
||||
["Price US$"]
|
||||
),
|
||||
this.LockInput,
|
||||
]
|
||||
),
|
||||
]
|
||||
),
|
||||
$el(
|
||||
@@ -404,14 +420,28 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
||||
[
|
||||
this.radioButtonsCheckOff_lock,
|
||||
$el("span", { style: { marginLeft: "5px" } }, ["OFF"]),
|
||||
$el(
|
||||
"div",
|
||||
{
|
||||
style: {
|
||||
marginLeft: "5px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
},
|
||||
[
|
||||
$el("span", { style: { marginLeft: "5px" } }, ["OFF"]),
|
||||
]
|
||||
),
|
||||
]
|
||||
),
|
||||
|
||||
|
||||
$el(
|
||||
"p",
|
||||
{ style: { fontSize: "16px", color: "#fff", margin: "10px 0 0 0" } },
|
||||
["Get paid from your workflow. You can change the price and withdraw your earnings on Copus."]
|
||||
[
|
||||
"Get paid from your workflow. You can change the price and withdraw your earnings on Copus.",
|
||||
]
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -432,7 +462,7 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
});
|
||||
|
||||
const blockChainSection = $el("div", { style: sectionStyle }, [
|
||||
$el("label", { style: labelStyle }, ["7️⃣ Store on blockchain "]),
|
||||
$el("label", { style: labelStyle }, ["8️⃣ Store on blockchain "]),
|
||||
$el(
|
||||
"label",
|
||||
{
|
||||
@@ -463,6 +493,139 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
),
|
||||
]);
|
||||
|
||||
this.ratingRadioButtonsCheck0 = $el("input", {
|
||||
type: "radio",
|
||||
name: "content_rating",
|
||||
value: "0",
|
||||
id: "content_rating0",
|
||||
});
|
||||
this.ratingRadioButtonsCheck1 = $el("input", {
|
||||
type: "radio",
|
||||
name: "content_rating",
|
||||
value: "1",
|
||||
id: "content_rating1",
|
||||
});
|
||||
this.ratingRadioButtonsCheck2 = $el("input", {
|
||||
type: "radio",
|
||||
name: "content_rating",
|
||||
value: "2",
|
||||
id: "content_rating2",
|
||||
});
|
||||
this.ratingRadioButtonsCheck_1 = $el("input", {
|
||||
type: "radio",
|
||||
name: "content_rating",
|
||||
value: "-1",
|
||||
id: "content_rating_1",
|
||||
checked: true,
|
||||
});
|
||||
|
||||
// content rating
|
||||
const contentRatingSection = $el("div", { style: sectionStyle }, [
|
||||
$el("label", { style: labelStyle }, ["7️⃣ Content rating "]),
|
||||
$el(
|
||||
"label",
|
||||
{
|
||||
style: {
|
||||
marginTop: "10px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
},
|
||||
},
|
||||
[
|
||||
this.ratingRadioButtonsCheck0,
|
||||
$el("img", {
|
||||
style: {
|
||||
width: "12px",
|
||||
height: "12px",
|
||||
marginLeft: "5px",
|
||||
},
|
||||
src: "https://static.copus.io/images/client/202507/test/b9f17da83b054d53cd0cb4508c2c30dc.png",
|
||||
}),
|
||||
$el("span", { style: { marginLeft: "5px", color: "#fff" } }, [
|
||||
"All ages",
|
||||
]),
|
||||
]
|
||||
),
|
||||
$el(
|
||||
"p",
|
||||
{ style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } },
|
||||
["Safe for all viewers; no profanity, violence, or mature themes."]
|
||||
),
|
||||
$el(
|
||||
"label",
|
||||
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
||||
[
|
||||
this.ratingRadioButtonsCheck1,
|
||||
$el("img", {
|
||||
style: {
|
||||
width: "12px",
|
||||
height: "12px",
|
||||
marginLeft: "5px",
|
||||
},
|
||||
src: "https://static.copus.io/images/client/202507/test/7848bc0d3690671df21c7cf00c4cfc81.png",
|
||||
}),
|
||||
$el("span", { style: { marginLeft: "5px", color: "#fff" } }, [
|
||||
"13+ (Teen)",
|
||||
]),
|
||||
]
|
||||
),
|
||||
$el(
|
||||
"p",
|
||||
{ style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } },
|
||||
[
|
||||
"Mild language, light themes, or cartoon violence; no explicit content. ",
|
||||
]
|
||||
),
|
||||
$el(
|
||||
"label",
|
||||
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
||||
[
|
||||
this.ratingRadioButtonsCheck2,
|
||||
$el("img", {
|
||||
style: {
|
||||
width: "12px",
|
||||
height: "12px",
|
||||
marginLeft: "5px",
|
||||
},
|
||||
src: "https://static.copus.io/images/client/202507/test/bc51839c208d68d91173e43c23bff039.png",
|
||||
}),
|
||||
$el("span", { style: { marginLeft: "5px", color: "#fff" } }, [
|
||||
"18+ (Explicit)",
|
||||
]),
|
||||
]
|
||||
),
|
||||
$el(
|
||||
"p",
|
||||
{ style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } },
|
||||
[
|
||||
"Explicit content, including sexual content, strong violence, or intense themes. ",
|
||||
]
|
||||
),
|
||||
$el(
|
||||
"label",
|
||||
{ style: { display: "flex", alignItems: "center", cursor: "pointer" } },
|
||||
[
|
||||
this.ratingRadioButtonsCheck_1,
|
||||
$el("img", {
|
||||
style: {
|
||||
width: "12px",
|
||||
height: "12px",
|
||||
marginLeft: "5px",
|
||||
},
|
||||
src: "https://static.copus.io/images/client/202507/test/5c802fdcaaea4e7bbed37393eec0d5ba.png",
|
||||
}),
|
||||
$el("span", { style: { marginLeft: "5px", color: "#fff" } }, [
|
||||
"Not Rated",
|
||||
]),
|
||||
]
|
||||
),
|
||||
$el(
|
||||
"p",
|
||||
{ style: { fontSize: "10px", color: "#fff", marginLeft: "20px" } },
|
||||
["No age rating provided."]
|
||||
),
|
||||
]);
|
||||
|
||||
// Message Section
|
||||
this.message = $el(
|
||||
@@ -526,6 +689,7 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
DescriptionSection,
|
||||
// contestSection,
|
||||
blockChainSection_lock,
|
||||
contentRatingSection,
|
||||
blockChainSection,
|
||||
this.message,
|
||||
buttonsSection,
|
||||
@@ -534,7 +698,7 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
return layout;
|
||||
}
|
||||
/**
|
||||
* api
|
||||
* api
|
||||
* @param {url} path
|
||||
* @param {params} options
|
||||
* @param {statusText} statusText
|
||||
@@ -587,7 +751,9 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
url: data,
|
||||
});
|
||||
} else {
|
||||
throw new Error("make sure your API key is correct and try again later");
|
||||
throw new Error(
|
||||
"make sure your API key is correct and try again later"
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e?.response?.status === 413) {
|
||||
@@ -628,8 +794,15 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
subTitle: this.SubTitleInput.value,
|
||||
content: this.descriptionInput.value,
|
||||
storeOnChain: this.radioButtonsCheck.checked ? true : false,
|
||||
lockState:this.radioButtonsCheck_lock.checked ? 2 : 0,
|
||||
unlockPrice:this.LockInput.value,
|
||||
lockState: this.radioButtonsCheck_lock.checked ? 2 : 0,
|
||||
unlockPrice: this.LockInput.value,
|
||||
rating: this.ratingRadioButtonsCheck0.checked
|
||||
? 0
|
||||
: this.ratingRadioButtonsCheck1.checked
|
||||
? 1
|
||||
: this.ratingRadioButtonsCheck2.checked
|
||||
? 2
|
||||
: -1,
|
||||
};
|
||||
|
||||
if (!this.keyInput.value) {
|
||||
@@ -644,8 +817,8 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
throw new Error("Title is required");
|
||||
}
|
||||
|
||||
if(this.radioButtonsCheck_lock.checked){
|
||||
if (!this.LockInput.value){
|
||||
if (this.radioButtonsCheck_lock.checked) {
|
||||
if (!this.LockInput.value) {
|
||||
throw new Error("Price is required");
|
||||
}
|
||||
}
|
||||
@@ -695,23 +868,23 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
"Uploading workflow..."
|
||||
);
|
||||
|
||||
if (res.status && res.data.status && res.data) {
|
||||
localStorage.setItem("copus_token",this.keyInput.value);
|
||||
const { data } = res.data;
|
||||
if (data) {
|
||||
const url = `${DEFAULT_HOMEPAGE_URL}/work/${data}`;
|
||||
this.message.innerHTML = `Workflow has been shared successfully. <a href="${url}" target="_blank">Click here to view it.</a>`;
|
||||
this.previewImage.src = "";
|
||||
this.previewImage.style.display = "none";
|
||||
this.uploadedImages = [];
|
||||
this.allFilesImages = [];
|
||||
this.allFiles = [];
|
||||
this.TitleInput.value = "";
|
||||
this.SubTitleInput.value = "";
|
||||
this.descriptionInput.value = "";
|
||||
this.selectedFile = null;
|
||||
}
|
||||
}
|
||||
if (res.status && res.data.status && res.data) {
|
||||
localStorage.setItem("copus_token", this.keyInput.value);
|
||||
const { data } = res.data;
|
||||
if (data) {
|
||||
const url = `${DEFAULT_HOMEPAGE_URL}/work/${data}`;
|
||||
this.message.innerHTML = `Workflow has been shared successfully. <a href="${url}" target="_blank">Click here to view it.</a>`;
|
||||
this.previewImage.src = "";
|
||||
this.previewImage.style.display = "none";
|
||||
this.uploadedImages = [];
|
||||
this.allFilesImages = [];
|
||||
this.allFiles = [];
|
||||
this.TitleInput.value = "";
|
||||
this.SubTitleInput.value = "";
|
||||
this.descriptionInput.value = "";
|
||||
this.selectedFile = null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error("Error sharing workflow: " + e.message);
|
||||
}
|
||||
@@ -757,7 +930,7 @@ export class CopusShareDialog extends ComfyDialog {
|
||||
this.element.style.display = "block";
|
||||
this.previewImage.src = "";
|
||||
this.previewImage.style.display = "none";
|
||||
this.keyInput.value = apiToken!=null?apiToken:"";
|
||||
this.keyInput.value = apiToken != null ? apiToken : "";
|
||||
this.uploadedImages = [];
|
||||
this.allFilesImages = [];
|
||||
this.allFiles = [];
|
||||
@@ -67,7 +67,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
async readKey() {
|
||||
let key = ""
|
||||
try {
|
||||
key = await api.fetchApi(`/v2/manager/get_openart_auth`)
|
||||
key = await api.fetchApi(`/manager/get_openart_auth`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
return data.openart_key;
|
||||
@@ -82,7 +82,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
async saveKey(value) {
|
||||
await api.fetchApi(`/v2/manager/set_openart_auth`, {
|
||||
await api.fetchApi(`/manager/set_openart_auth`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
@@ -399,7 +399,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
form.append("file", uploadFile);
|
||||
try {
|
||||
const res = await this.fetchApi(
|
||||
`/v2/workflows/upload_thumbnail`,
|
||||
`/workflows/upload_thumbnail`,
|
||||
{
|
||||
method: "POST",
|
||||
body: form,
|
||||
@@ -459,7 +459,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
throw new Error("Title is required");
|
||||
}
|
||||
|
||||
const current_snapshot = await api.fetchApi(`/v2/snapshot/get_current`)
|
||||
const current_snapshot = await api.fetchApi(`/snapshot/get_current`)
|
||||
.then(response => response.json())
|
||||
.catch(error => {
|
||||
// console.log(error);
|
||||
@@ -489,7 +489,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
|
||||
try {
|
||||
const response = await this.fetchApi(
|
||||
"/v2/workflows/publish",
|
||||
"/workflows/publish",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
@@ -179,7 +179,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
||||
async loadToken() {
|
||||
let key = ""
|
||||
try {
|
||||
const response = await api.fetchApi(`/v2/manager/youml/settings`)
|
||||
const response = await api.fetchApi(`/manager/youml/settings`)
|
||||
const settings = await response.json()
|
||||
return settings.token
|
||||
} catch (error) {
|
||||
@@ -188,7 +188,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
async saveToken(value) {
|
||||
await api.fetchApi(`/v2/manager/youml/settings`, {
|
||||
await api.fetchApi(`/manager/youml/settings`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
@@ -380,7 +380,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
||||
try {
|
||||
let snapshotData = null;
|
||||
try {
|
||||
const snapshot = await api.fetchApi(`/v2/snapshot/get_current`)
|
||||
const snapshot = await api.fetchApi(`/snapshot/get_current`)
|
||||
snapshotData = await snapshot.json()
|
||||
} catch (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) => {
|
||||
if (isConfirmed) {
|
||||
try {
|
||||
api.fetchApi("/v2/manager/reboot");
|
||||
api.fetchApi("/manager/reboot");
|
||||
}
|
||||
catch(exception) {}
|
||||
}
|
||||
@@ -210,7 +210,7 @@ export async function install_pip(packages) {
|
||||
if(packages.includes('&'))
|
||||
app.ui.dialog.show(`Invalid PIP package enumeration: '${packages}'`);
|
||||
|
||||
const res = await api.fetchApi("/v2/customnode/install/pip", {
|
||||
const res = await api.fetchApi("/customnode/install/pip", {
|
||||
method: "POST",
|
||||
body: packages,
|
||||
});
|
||||
@@ -245,7 +245,7 @@ export async function install_via_git_url(url, manager_dialog) {
|
||||
|
||||
show_message(`Wait...<BR><BR>Installing '${url}'`);
|
||||
|
||||
const res = await api.fetchApi("/v2/customnode/install/git_url", {
|
||||
const res = await api.fetchApi("/customnode/install/git_url", {
|
||||
method: "POST",
|
||||
body: url,
|
||||
});
|
||||
@@ -630,14 +630,6 @@ 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 () {
|
||||
const mouseenterHandler = (e) => {
|
||||
const target = e.target;
|
||||
@@ -64,7 +64,7 @@ function storeGroupNode(name, data, register=true) {
|
||||
}
|
||||
|
||||
export async function load_components() {
|
||||
let data = await api.fetchApi('/v2/manager/component/loads', {method: "POST"});
|
||||
let data = await api.fetchApi('/manager/component/loads', {method: "POST"});
|
||||
let components = await data.json();
|
||||
|
||||
let start_time = Date.now();
|
||||
@@ -222,7 +222,7 @@ async function save_as_component(node, version, author, prefix, nodename, packna
|
||||
pack_map[packname] = component_name;
|
||||
rpack_map[component_name] = subgraph;
|
||||
|
||||
const res = await api.fetchApi('/v2/manager/component/save', {
|
||||
const res = await api.fetchApi('/manager/component/save', {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -259,7 +259,7 @@ async function import_component(component_name, component, mode) {
|
||||
workflow: component
|
||||
};
|
||||
|
||||
const res = await api.fetchApi('/v2/manager/component/save', {
|
||||
const res = await api.fetchApi('/manager/component/save', {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", },
|
||||
body: JSON.stringify(body)
|
||||
@@ -709,7 +709,7 @@ app.handleFile = handleFile;
|
||||
|
||||
let current_component_policy = 'workflow';
|
||||
try {
|
||||
api.fetchApi('/v2/manager/policy/component')
|
||||
api.fetchApi('/manager/policy/component')
|
||||
.then(response => response.text())
|
||||
.then(data => { current_component_policy = data; });
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
|
||||
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
|
||||
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
|
||||
showPopover, hidePopover, generateUUID
|
||||
showPopover, hidePopover
|
||||
} from "./common.js";
|
||||
|
||||
// https://cenfun.github.io/turbogrid/api.html
|
||||
@@ -66,7 +66,7 @@ export class CustomNodesManager {
|
||||
this.id = "cn-manager";
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Legacy.CustomNodesManager",
|
||||
name: "Comfy.CustomNodesManager",
|
||||
afterConfigureGraph: (missingNodeTypes) => {
|
||||
const item = this.getFilterItem(ShowMode.MISSING);
|
||||
if (item) {
|
||||
@@ -459,7 +459,7 @@ export class CustomNodesManager {
|
||||
|
||||
".cn-manager-stop": {
|
||||
click: () => {
|
||||
api.fetchApi('/v2/manager/queue/reset');
|
||||
api.fetchApi('/manager/queue/reset');
|
||||
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||
}
|
||||
},
|
||||
@@ -635,7 +635,7 @@ export class CustomNodesManager {
|
||||
};
|
||||
}
|
||||
|
||||
const response = await api.fetchApi(`/v2/customnode/import_fail_info`, {
|
||||
const response = await api.fetchApi(`/customnode/import_fail_info`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(info)
|
||||
@@ -714,6 +714,7 @@ export class CustomNodesManager {
|
||||
link.href = rowItem.reference;
|
||||
link.target = '_blank';
|
||||
link.innerHTML = `<b>${title}</b>`;
|
||||
link.title = rowItem.originalData.id;
|
||||
container.appendChild(link);
|
||||
|
||||
return container;
|
||||
@@ -1243,7 +1244,7 @@ export class CustomNodesManager {
|
||||
async loadNodes(node_packs) {
|
||||
const mode = manager_instance.datasrc_combo.value;
|
||||
this.showStatus(`Loading node mappings (${mode}) ...`);
|
||||
const res = await fetchData(`/v2/customnode/getmappings?mode=${mode}`);
|
||||
const res = await fetchData(`/customnode/getmappings?mode=${mode}`);
|
||||
if (res.error) {
|
||||
console.log(res.error);
|
||||
return;
|
||||
@@ -1395,10 +1396,10 @@ export class CustomNodesManager {
|
||||
this.showLoading();
|
||||
let res;
|
||||
if(is_enable) {
|
||||
res = await api.fetchApi(`/v2/customnode/disabled_versions/${node_id}`, { cache: "no-store" });
|
||||
res = await api.fetchApi(`/customnode/disabled_versions/${node_id}`, { cache: "no-store" });
|
||||
}
|
||||
else {
|
||||
res = await api.fetchApi(`/v2/customnode/versions/${node_id}`, { cache: "no-store" });
|
||||
res = await api.fetchApi(`/customnode/versions/${node_id}`, { cache: "no-store" });
|
||||
}
|
||||
this.hideLoading();
|
||||
|
||||
@@ -1410,15 +1411,16 @@ export class CustomNodesManager {
|
||||
let version_cnt = 0;
|
||||
|
||||
if(!is_enable) {
|
||||
|
||||
if(rowItem.cnr_latest != rowItem.originalData.active_version && obj.length > 0) {
|
||||
versions.push('latest');
|
||||
}
|
||||
|
||||
if(rowItem.originalData.active_version != 'nightly') {
|
||||
versions.push('nightly');
|
||||
default_version = 'nightly';
|
||||
version_cnt++;
|
||||
}
|
||||
|
||||
if(rowItem.cnr_latest != rowItem.originalData.active_version && obj.length > 0) {
|
||||
versions.push('latest');
|
||||
}
|
||||
}
|
||||
|
||||
for(let v of obj) {
|
||||
@@ -1439,6 +1441,13 @@ export class CustomNodesManager {
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if(mode === "uninstall") {
|
||||
@@ -1465,9 +1474,9 @@ export class CustomNodesManager {
|
||||
let needRestart = false;
|
||||
let errorMsg = "";
|
||||
|
||||
let target_items = [];
|
||||
await api.fetchApi('/manager/queue/reset');
|
||||
|
||||
let batch = {};
|
||||
let target_items = [];
|
||||
|
||||
for (const hash of list) {
|
||||
const item = this.grid.getRowItemBy("hash", hash);
|
||||
@@ -1510,11 +1519,23 @@ export class CustomNodesManager {
|
||||
api_mode = 'reinstall';
|
||||
}
|
||||
|
||||
if(batch[api_mode]) {
|
||||
batch[api_mode].push(data);
|
||||
}
|
||||
else {
|
||||
batch[api_mode] = [data];
|
||||
const res = await api.fetchApi(`/manager/queue/${api_mode}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1531,24 +1552,7 @@ export class CustomNodesManager {
|
||||
}
|
||||
}
|
||||
else {
|
||||
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}`;
|
||||
}
|
||||
}
|
||||
|
||||
await api.fetchApi('/manager/queue/start');
|
||||
this.showStop();
|
||||
showTerminal();
|
||||
}
|
||||
@@ -1556,9 +1560,6 @@ export class CustomNodesManager {
|
||||
|
||||
async onQueueStatus(event) {
|
||||
let self = CustomNodesManager.instance;
|
||||
// If legacy manager front is not open, return early (using new manager front)
|
||||
if (self.element?.style.display === 'none') return
|
||||
|
||||
if(event.detail.status == 'in_progress' && event.detail.ui_target == 'nodepack_manager') {
|
||||
const hash = event.detail.target;
|
||||
|
||||
@@ -1569,7 +1570,7 @@ export class CustomNodesManager {
|
||||
self.grid.updateCell(item, "action");
|
||||
self.grid.setRowSelected(item, false);
|
||||
}
|
||||
else if(event.detail.status == 'batch-done' && event.detail.batch_id == self.batch_id) {
|
||||
else if(event.detail.status == 'done') {
|
||||
self.hideStop();
|
||||
self.onQueueCompleted(event.detail);
|
||||
}
|
||||
@@ -1745,7 +1746,7 @@ export class CustomNodesManager {
|
||||
async getMissingNodesLegacy(hashMap, missing_nodes) {
|
||||
const mode = manager_instance.datasrc_combo.value;
|
||||
this.showStatus(`Loading missing nodes (${mode}) ...`);
|
||||
const res = await fetchData(`/v2/customnode/getmappings?mode=${mode}`);
|
||||
const res = await fetchData(`/customnode/getmappings?mode=${mode}`);
|
||||
if (res.error) {
|
||||
this.showError(`Failed to get custom node mappings: ${res.error}`);
|
||||
return;
|
||||
@@ -1860,7 +1861,7 @@ export class CustomNodesManager {
|
||||
async getAlternatives() {
|
||||
const mode = manager_instance.datasrc_combo.value;
|
||||
this.showStatus(`Loading alternatives (${mode}) ...`);
|
||||
const res = await fetchData(`/v2/customnode/alternatives?mode=${mode}`);
|
||||
const res = await fetchData(`/customnode/alternatives?mode=${mode}`);
|
||||
if (res.error) {
|
||||
this.showError(`Failed to get alternatives: ${res.error}`);
|
||||
return [];
|
||||
@@ -1908,7 +1909,7 @@ export class CustomNodesManager {
|
||||
infoToast('Fetching updated information. This may take some time if many custom nodes are installed.');
|
||||
}
|
||||
|
||||
const res = await fetchData(`/v2/customnode/getlist?mode=${mode}${skip_update}`);
|
||||
const res = await fetchData(`/customnode/getlist?mode=${mode}${skip_update}`);
|
||||
if (res.error) {
|
||||
this.showError("Failed to get custom node list.");
|
||||
this.hideLoading();
|
||||
@@ -3,7 +3,7 @@ import { $el } from "../../scripts/ui.js";
|
||||
import {
|
||||
manager_instance, rebootAPI,
|
||||
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
||||
storeColumnWidth, restoreColumnWidth, loadCss, generateUUID
|
||||
storeColumnWidth, restoreColumnWidth, loadCss
|
||||
} from "./common.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
@@ -81,10 +81,13 @@ export class ModelManager {
|
||||
value: ""
|
||||
}, {
|
||||
label: "Installed",
|
||||
value: "True"
|
||||
value: "installed"
|
||||
}, {
|
||||
label: "Not Installed",
|
||||
value: "False"
|
||||
value: "not_installed"
|
||||
}, {
|
||||
label: "In Workflow",
|
||||
value: "in_workflow"
|
||||
}];
|
||||
|
||||
this.typeList = [{
|
||||
@@ -172,7 +175,7 @@ export class ModelManager {
|
||||
|
||||
".cmm-manager-stop": {
|
||||
click: () => {
|
||||
api.fetchApi('/v2/manager/queue/reset');
|
||||
api.fetchApi('/manager/queue/reset');
|
||||
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||
}
|
||||
},
|
||||
@@ -254,12 +257,31 @@ export class ModelManager {
|
||||
rowFilter: (rowItem) => {
|
||||
|
||||
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);
|
||||
|
||||
if (shouldShown) {
|
||||
if(this.filter && rowItem.installed !== this.filter) {
|
||||
return false;
|
||||
if(this.filter) {
|
||||
if (this.filter == "in_workflow") {
|
||||
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) {
|
||||
@@ -413,15 +435,23 @@ export class ModelManager {
|
||||
}
|
||||
|
||||
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");
|
||||
this.showError("");
|
||||
|
||||
let needRefresh = false;
|
||||
let errorMsg = "";
|
||||
|
||||
let target_items = [];
|
||||
await api.fetchApi('/manager/queue/reset');
|
||||
|
||||
let batch = {};
|
||||
let target_items = [];
|
||||
|
||||
for (const item of list) {
|
||||
this.grid.scrollRowIntoView(item);
|
||||
@@ -438,12 +468,21 @@ export class ModelManager {
|
||||
const data = item.originalData;
|
||||
data.ui_id = item.hash;
|
||||
|
||||
const res = await api.fetchApi(`/manager/queue/install_model`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
if(batch['install_model']) {
|
||||
batch['install_model'].push(data);
|
||||
}
|
||||
else {
|
||||
batch['install_model'] = [data];
|
||||
if (res.status != 200) {
|
||||
errorMsg = `'${item.name}': `;
|
||||
|
||||
if(res.status == 403) {
|
||||
errorMsg += `This action is not allowed with this security level configuration.\n`;
|
||||
} else {
|
||||
errorMsg += await res.text() + '\n';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,24 +499,7 @@ export class ModelManager {
|
||||
}
|
||||
}
|
||||
else {
|
||||
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}`;
|
||||
}
|
||||
}
|
||||
|
||||
await api.fetchApi('/manager/queue/start');
|
||||
this.showStop();
|
||||
showTerminal();
|
||||
}
|
||||
@@ -497,7 +519,7 @@ export class ModelManager {
|
||||
// self.grid.updateCell(item, "tg-column-select");
|
||||
self.grid.updateRow(item);
|
||||
}
|
||||
else if(event.detail.status == 'batch-done') {
|
||||
else if(event.detail.status == 'done') {
|
||||
self.hideStop();
|
||||
self.onQueueCompleted(event.detail);
|
||||
}
|
||||
@@ -623,7 +645,7 @@ export class ModelManager {
|
||||
|
||||
const mode = manager_instance.datasrc_combo.value;
|
||||
|
||||
const res = await fetchData(`/v2/externalmodel/getlist?mode=${mode}`);
|
||||
const res = await fetchData(`/externalmodel/getlist?mode=${mode}`);
|
||||
if (res.error) {
|
||||
this.showError("Failed to get external model list.");
|
||||
this.hideLoading();
|
||||
@@ -795,4 +817,4 @@ export class ModelManager {
|
||||
close() {
|
||||
this.element.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -142,7 +142,7 @@ function node_info_copy(src, dest, connect_both, copy_shape) {
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Legacy.Manager.NodeFixer",
|
||||
name: "Comfy.Manager.NodeFixer",
|
||||
beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
options.push({
|
||||
@@ -153,6 +153,7 @@ app.registerExtension({
|
||||
app.canvas.graph.add(new_node, false);
|
||||
node_info_copy(this, new_node, true);
|
||||
app.canvas.graph.remove(this);
|
||||
requestAnimationFrame(() => app.canvas.setDirty(true, true))
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,7 @@ import { manager_instance, rebootAPI, show_message } from "./common.js";
|
||||
async function restore_snapshot(target) {
|
||||
if(SnapshotManager.instance) {
|
||||
try {
|
||||
const response = await api.fetchApi(`/v2/snapshot/restore?target=${target}`, { cache: "no-store" });
|
||||
const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" });
|
||||
|
||||
if(response.status == 403) {
|
||||
show_message('This action is not allowed with this security level configuration.');
|
||||
@@ -35,7 +35,7 @@ async function restore_snapshot(target) {
|
||||
async function remove_snapshot(target) {
|
||||
if(SnapshotManager.instance) {
|
||||
try {
|
||||
const response = await api.fetchApi(`/v2/snapshot/remove?target=${target}`, { cache: "no-store" });
|
||||
const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" });
|
||||
|
||||
if(response.status == 403) {
|
||||
show_message('This action is not allowed with this security level configuration.');
|
||||
@@ -61,7 +61,7 @@ async function remove_snapshot(target) {
|
||||
|
||||
async function save_current_snapshot() {
|
||||
try {
|
||||
const response = await api.fetchApi('/v2/snapshot/save', { cache: "no-store" });
|
||||
const response = await api.fetchApi('/snapshot/save', { cache: "no-store" });
|
||||
app.ui.dialog.close();
|
||||
return true;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ async function save_current_snapshot() {
|
||||
}
|
||||
|
||||
async function getSnapshotList() {
|
||||
const response = await api.fetchApi(`/v2/snapshot/getlist`);
|
||||
const response = await api.fetchApi(`/snapshot/getlist`);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
@@ -38,7 +38,7 @@ class WorkflowMetadataExtension {
|
||||
* enabled is true if the node is enabled, false if it is disabled
|
||||
*/
|
||||
async getInstalledNodes() {
|
||||
const res = await api.fetchApi("/v2/customnode/installed");
|
||||
const res = await api.fetchApi("/customnode/installed");
|
||||
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) nodeProperties.cnr_id = cnr_id;
|
||||
else nodeProperties.aux_id = aux_id;
|
||||
if (ver) nodeProperties.ver = ver;
|
||||
} else if (["nodes", "comfy_extras"].includes(moduleType)) {
|
||||
if (ver) nodeProperties.ver = ver.trim();
|
||||
} else if (["nodes", "comfy_extras", "comfy_api_nodes"].includes(moduleType)) {
|
||||
nodeProperties.cnr_id = "comfy-core";
|
||||
nodeProperties.ver = this.comfyCoreVersion;
|
||||
}
|
||||
221
model-list.json
221
model-list.json
@@ -749,8 +749,8 @@
|
||||
"save_path": "loras/HyperSD/SDXL",
|
||||
"description": "Hyper-SD LoRA (4steps) - SDXL",
|
||||
"reference": "https://huggingface.co/ByteDance/Hyper-SD",
|
||||
"filename": "Hyper-SD15-4steps-lora.safetensors",
|
||||
"url": "https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-SD15-4steps-lora.safetensors",
|
||||
"filename": "Hyper-SDXL-4steps-lora.safetensors",
|
||||
"url": "https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-SDXL-4steps-lora.safetensors",
|
||||
"size": "787MB"
|
||||
},
|
||||
{
|
||||
@@ -1973,6 +1973,97 @@
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth",
|
||||
"size": "375.0MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sam2.1_hiera_tiny.pt",
|
||||
"type": "sam2.1",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2.1 hiera model (tiny)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2.1_hiera_tiny.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_tiny.pt",
|
||||
"size": "149.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2.1_hiera_small.pt",
|
||||
"type": "sam2.1",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2.1 hiera model (small)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2.1_hiera_small.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_small.pt",
|
||||
"size": "176.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2.1_hiera_base_plus.pt",
|
||||
"type": "sam2.1",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2.1 hiera model (base+)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2.1_hiera_base_plus.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_base_plus.pt",
|
||||
"size": "309.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2.1_hiera_large.pt",
|
||||
"type": "sam2.1",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2.1 hiera model (large)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2.1_hiera_large.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_large.pt",
|
||||
"size": "857.0MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sam2_hiera_tiny.pt",
|
||||
"type": "sam2",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2 hiera model (tiny)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2_hiera_tiny.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_tiny.pt",
|
||||
"size": "149.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2_hiera_small.pt",
|
||||
"type": "sam2",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2 hiera model (small)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2_hiera_small.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_small.pt",
|
||||
"size": "176.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2_hiera_base_plus.pt",
|
||||
"type": "sam2",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2 hiera model (base+)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2_hiera_base_plus.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_base_plus.pt",
|
||||
"size": "309.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2_hiera_large.pt",
|
||||
"type": "sam2",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2 hiera model (large)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2_hiera_large.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt",
|
||||
"size": "857.0MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "seecoder v1.0",
|
||||
"type": "seecoder",
|
||||
@@ -4006,6 +4097,29 @@
|
||||
"size": "649MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/omnigen2_fp16.safetensors",
|
||||
"type": "diffusion_model",
|
||||
"base": "OmniGen2",
|
||||
"save_path": "default",
|
||||
"description": "OmniGen2 diffusion model. This is required for using OmniGen2.",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged",
|
||||
"filename": "omnigen2_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/omnigen2_fp16.safetensors",
|
||||
"size": "7.93GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/qwen_2.5_vl_fp16.safetensors",
|
||||
"type": "clip",
|
||||
"base": "qwen-2.5",
|
||||
"save_path": "default",
|
||||
"description": "text encoder for OmniGen2",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged",
|
||||
"filename": "qwen_2.5_vl_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/text_encoders/qwen_2.5_vl_fp16.safetensors",
|
||||
"size": "7.51GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "FLUX.1 [Schnell] Diffusion model",
|
||||
"type": "diffusion_model",
|
||||
@@ -4023,7 +4137,7 @@
|
||||
"type": "VAE",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "vae/FLUX1",
|
||||
"description": "FLUX.1 VAE model",
|
||||
"description": "FLUX.1 VAE model\nNOTE: This VAE model can also be used for image generation with OmniGen2.",
|
||||
"reference": "https://huggingface.co/black-forest-labs/FLUX.1-schnell",
|
||||
"filename": "ae.safetensors",
|
||||
"url": "https://huggingface.co/black-forest-labs/FLUX.1-schnell/resolve/main/ae.safetensors",
|
||||
@@ -4953,6 +5067,107 @@
|
||||
"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",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
95
node_db/README.md
Normal file
95
node_db/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# ComfyUI-Manager: Node Database (node_db)
|
||||
|
||||
This directory contains the JSON database files that power ComfyUI-Manager's legacy node registry system. While the manager is gradually transitioning to the online Custom Node Registry (CNR), these local JSON files continue to provide important metadata about custom nodes, models, and their integrations.
|
||||
|
||||
## Directory Structure
|
||||
|
||||
The node_db directory is organized into several subdirectories, each serving a specific purpose:
|
||||
|
||||
- **dev/**: Development channel files with latest additions and experimental nodes
|
||||
- **legacy/**: Historical/legacy nodes that may require special handling
|
||||
- **new/**: New nodes that have passed initial verification but are still being evaluated
|
||||
- **forked/**: Forks of existing nodes with modifications
|
||||
- **tutorial/**: Example and tutorial nodes designed for learning purposes
|
||||
|
||||
## Core Database Files
|
||||
|
||||
Each subdirectory contains a standard set of JSON files:
|
||||
|
||||
- **custom-node-list.json**: Primary database of custom nodes with metadata
|
||||
- **extension-node-map.json**: Maps between extensions and individual nodes they provide
|
||||
- **model-list.json**: Catalog of models that can be downloaded through the manager
|
||||
- **alter-list.json**: Alternative implementations of nodes for compatibility or functionality
|
||||
- **github-stats.json**: GitHub repository statistics for node popularity metrics
|
||||
|
||||
## Database Schema
|
||||
|
||||
### custom-node-list.json
|
||||
```json
|
||||
{
|
||||
"custom_nodes": [
|
||||
{
|
||||
"title": "Node display name",
|
||||
"name": "Repository name",
|
||||
"reference": "Original repository if forked",
|
||||
"files": ["GitHub URL or other source location"],
|
||||
"install_type": "git",
|
||||
"description": "Description of the node's functionality",
|
||||
"pip": ["optional pip dependencies"],
|
||||
"js": ["optional JavaScript files"],
|
||||
"tags": ["categorization tags"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### extension-node-map.json
|
||||
```json
|
||||
{
|
||||
"extension-id": [
|
||||
["list", "of", "node", "classes"],
|
||||
{
|
||||
"author": "Author name",
|
||||
"description": "Extension description",
|
||||
"nodename_pattern": "Optional regex pattern for node name matching"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Transition to Custom Node Registry (CNR)
|
||||
|
||||
This local database system is being progressively replaced by the online Custom Node Registry (CNR), which provides:
|
||||
- Real-time updates without manual JSON maintenance
|
||||
- Improved versioning support
|
||||
- Better security validation
|
||||
- Enhanced metadata
|
||||
|
||||
The Manager supports both systems simultaneously during the transition period.
|
||||
|
||||
## Implementation Details
|
||||
|
||||
- The database follows a channel-based architecture for different sources
|
||||
- Multiple database modes are supported: Channel, Local, and Remote
|
||||
- The system supports differential updates to minimize bandwidth usage
|
||||
- Security levels are enforced for different node installations based on source
|
||||
|
||||
## Usage in the Application
|
||||
|
||||
The Manager's backend uses these database files to:
|
||||
|
||||
1. Provide browsable lists of available nodes and models
|
||||
2. Resolve dependencies for installation
|
||||
3. Track updates and new versions
|
||||
4. Map node classes to their source repositories
|
||||
5. Assess risk levels for installation security
|
||||
|
||||
## Maintenance Scripts
|
||||
|
||||
Each subdirectory contains a `scan.sh` script that assists with:
|
||||
- Scanning repositories for new nodes
|
||||
- Updating metadata
|
||||
- Validating database integrity
|
||||
- Generating proper JSON structures
|
||||
|
||||
This database system enables a flexible, secure, and comprehensive management system for the ComfyUI ecosystem while the transition to CNR continues.
|
||||
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
@@ -1,5 +1,15 @@
|
||||
{
|
||||
"custom_nodes": [
|
||||
{
|
||||
"author": "joaomede",
|
||||
"title": "ComfyUI-Unload-Model-Fork",
|
||||
"reference": "https://github.com/joaomede/ComfyUI-Unload-Model-Fork",
|
||||
"files": [
|
||||
"https://github.com/joaomede/ComfyUI-Unload-Model-Fork"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "For unloading a model or all models, using the memory management that is already present in ComfyUI. Copied from [a/https://github.com/willblaschko/ComfyUI-Unload-Models](https://github.com/willblaschko/ComfyUI-Unload-Models) but without the unnecessary extra stuff."
|
||||
},
|
||||
{
|
||||
"author": "SanDiegoDude",
|
||||
"title": "ComfyUI-HiDream-Sampler [WIP]",
|
||||
|
||||
@@ -1,15 +1,737 @@
|
||||
{
|
||||
"custom_nodes": [
|
||||
{
|
||||
"author": "#NOTICE_1.13",
|
||||
"title": "NOTICE: This channel is not the default channel.",
|
||||
"reference": "https://github.com/ltdrdata/ComfyUI-Manager",
|
||||
"files": [],
|
||||
"author": "MitoshiroPJ",
|
||||
"title": "ComfyUI Slothful Attention [REMOVED]",
|
||||
"reference": "https://github.com/MitoshiroPJ/comfyui_slothful_attention",
|
||||
"files": [
|
||||
"https://github.com/MitoshiroPJ/comfyui_slothful_attention"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "If you see this message, your ComfyUI-Manager is outdated.\nLegacy channel provides only the list of the deprecated nodes. If you want to find the complete node list, please go to the Default channel."
|
||||
"description": "This custom node allow controlling output without training. The reducing method is similar to [a/Spatial-Reduction Attention](https://paperswithcode.com/method/spatial-reduction-attention)."
|
||||
},
|
||||
{
|
||||
"author": "MitoshiroPJ",
|
||||
"title": "comfyui_focal_sampler [REMOVED]",
|
||||
"reference": "https://github.com/MitoshiroPJ/comfyui_focal_sampler",
|
||||
"files": [
|
||||
"https://github.com/MitoshiroPJ/comfyui_focal_sampler"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Apply additional sampling to specific area"
|
||||
},
|
||||
{
|
||||
"author": "manifestations",
|
||||
"title": "ComfyUI Ethnic Outfit & Prompt Enhancer Nodes [REMOVED]",
|
||||
"reference": "https://github.com/manifestations/comfyui-indian-outfit",
|
||||
"files": [
|
||||
"https://github.com/manifestations/comfyui-indian-outfit"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Features:\n* Extensive options for Indian, Indonesian, and international clothing, jewelry, accessories, and styles\n* Multiple jewelry and accessory fields (with material support: gold, diamond, silver, leather, beads, etc.)\n* Support for tattoos, henna, hair styles, poses, shot types, lighting, and photography genres\n* Seamless prompt expansion using your own Ollama LLM instance\n* Modular, extensible JSON data files for easy customization"
|
||||
},
|
||||
{
|
||||
"author": "coVISIONSld",
|
||||
"title": "ComfyUI-OmniGen2 [REMOVED]",
|
||||
"reference": "https://github.com/coVISIONSld/ComfyUI-OmniGen2",
|
||||
"files": [
|
||||
"https://github.com/coVISIONSld/ComfyUI-OmniGen2"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI-OmniGen2 is a custom node package for the OmniGen2 model, enabling advanced text-to-image generation and visual understanding."
|
||||
},
|
||||
{
|
||||
"author": "S4MUEL-404",
|
||||
"title": "ComfyUI-S4Tool-Image-Overlay [REMOVED]",
|
||||
"reference": "https://github.com/S4MUEL-404/ComfyUI-S4Tool-Image-Overlay",
|
||||
"files": [
|
||||
"https://github.com/S4MUEL-404/ComfyUI-S4Tool-Image-Overlay"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Quickly set up image overlay effects"
|
||||
},
|
||||
{
|
||||
"author": "akspa0",
|
||||
"title": "ComfyUI-FapMixPlus [REMOVED]",
|
||||
"reference": "https://github.com/akspa0/ComfyUI-FapMixPlus",
|
||||
"files": [
|
||||
"https://github.com/akspa0/ComfyUI-FapMixPlus"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is an audio processing script that applies soft limiting, optional loudness normalization, and optional slicing for transcription. It can also produce stereo-mixed outputs with optional audio appended to the end. The script organizes processed files into structured folders with sanitized filenames and retains original timestamps for continuity."
|
||||
},
|
||||
{
|
||||
"author": "RedmondAI",
|
||||
"title": "comfyui-tools [UNSAFE]",
|
||||
"reference": "https://github.com/RedmondAI/comfyui-tools",
|
||||
"files": [
|
||||
"https://github.com/RedmondAI/comfyui-tools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Custom extensions for ComfyUI used by the Redmond3D VFX team.[w/This node pack has a vulnerability that allows it to create files at arbitrary paths.]"
|
||||
},
|
||||
{
|
||||
"author": "S4MUEL-404",
|
||||
"title": "Image Position Blend [REMOVED]",
|
||||
"id": "ComfyUI-Image-Position-Blend",
|
||||
"version": "1.1",
|
||||
"reference": "https://github.com/S4MUEL-404/ComfyUI-Image-Position-Blend",
|
||||
"files": [
|
||||
"https://github.com/S4MUEL-404/ComfyUI-Image-Position-Blend"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A custom node for conveniently adjusting the overlay position of two images."
|
||||
},
|
||||
{
|
||||
"author": "S4MUEL-404",
|
||||
"title": "ComfyUI-Text-On-Image [REMOVED]",
|
||||
"id": "ComfyUI-Text-On-Image",
|
||||
"reference": "https://github.com/S4MUEL-404/ComfyUI-Text-On-Image",
|
||||
"files": [
|
||||
"https://github.com/S4MUEL-404/ComfyUI-Text-On-Image"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A custom node for ComfyUI that allows users to add text overlays to images with customizable size, font, position, and shadow."
|
||||
},
|
||||
{
|
||||
"author": "S4MUEL-404",
|
||||
"title": "ComfyUI-Prompts-Selector [REMOVED]",
|
||||
"reference": "https://github.com/S4MUEL-404/ComfyUI-Prompts-Selector",
|
||||
"files": [
|
||||
"https://github.com/S4MUEL-404/ComfyUI-Prompts-Selector"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Quickly select preset prompts and merge them"
|
||||
},
|
||||
{
|
||||
"author": "juntaosun",
|
||||
"title": "ComfyUI_open_nodes [REMOVED]",
|
||||
"reference": "https://github.com/juntaosun/ComfyUI_open_nodes",
|
||||
"files": [
|
||||
"https://github.com/juntaosun/ComfyUI_open_nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI open nodes by juntaosun."
|
||||
},
|
||||
{
|
||||
"author": "perilli",
|
||||
"title": "apw_nodes [DEPRECATED]",
|
||||
"reference": "https://github.com/alessandroperilli/apw_nodes",
|
||||
"files": [
|
||||
"https://github.com/alessandroperilli/apw_nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A custom node suite to augment the capabilities of the [a/AP Workflows for ComfyUI](https://perilli.com/ai/comfyui/)[w/'APW_Nodes' has been newly added in place of 'apw_nodes'.]"
|
||||
},
|
||||
{
|
||||
"author": "markuryy",
|
||||
"title": "ComfyUI Spiritparticle Nodes [REMOVED]",
|
||||
"reference": "https://github.com/markuryy/comfyui-spiritparticle",
|
||||
"files": [
|
||||
"https://github.com/markuryy/comfyui-spiritparticle"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A node pack by spiritparticle."
|
||||
},
|
||||
{
|
||||
"author": "SpaceKendo",
|
||||
"title": "Text to video for Stable Video Diffusion in ComfyUI [REMOVED]",
|
||||
"id": "svd-txt2vid",
|
||||
"reference": "https://github.com/SpaceKendo/ComfyUI-svd_txt2vid",
|
||||
"files": [
|
||||
"https://github.com/SpaceKendo/ComfyUI-svd_txt2vid"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is node replaces the init_image conditioning for the [a/Stable Video Diffusion](https://github.com/Stability-AI/generative-models) image to video model with text embeds, together with a conditioning frame. The conditioning frame is a set of latents."
|
||||
},
|
||||
{
|
||||
"author": "vovler",
|
||||
"title": "ComfyUI Civitai Helper Extension [REMOVED]",
|
||||
"reference": "https://github.com/vovler/comfyui-civitaihelper",
|
||||
"files": [
|
||||
"https://github.com/vovler/comfyui-civitaihelper"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI extension for parsing Civitai PNG workflows and automatically downloading missing models"
|
||||
},
|
||||
{
|
||||
"author": "DriftJohnson",
|
||||
"title": "DJZ-Nodes [REMOVED]",
|
||||
"id": "DJZ-Nodes",
|
||||
"reference": "https://github.com/MushroomFleet/DJZ-Nodes",
|
||||
"files": [
|
||||
"https://github.com/MushroomFleet/DJZ-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "AspectSize and other nodes"
|
||||
},
|
||||
{
|
||||
"author": "DriftJohnson",
|
||||
"title": "KokoroTTS Node [REMOVED]",
|
||||
"reference": "https://github.com/MushroomFleet/DJZ-KokoroTTS",
|
||||
"files": [
|
||||
"https://github.com/MushroomFleet/DJZ-KokoroTTS"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This node provides advanced text-to-speech functionality powered by KokoroTTS. Follow the instructions below to install, configure, and use the node within your portable ComfyUI installation."
|
||||
},
|
||||
{
|
||||
"author": "MushroomFleet",
|
||||
"title": "DJZ-Pedalboard [REMOVED]",
|
||||
"reference": "https://github.com/MushroomFleet/DJZ-Pedalboard",
|
||||
"files": [
|
||||
"https://github.com/MushroomFleet/DJZ-Pedalboard"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This project provides a collection of custom nodes designed for enhanced audio effects in ComfyUI. With an intuitive pedalboard interface, users can easily integrate and manipulate various audio effects within their workflows."
|
||||
},
|
||||
{
|
||||
"author": "MushroomFleet",
|
||||
"title": "SVG Suite for ComfyUI [REMOVED]",
|
||||
"reference": "https://github.com/MushroomFleet/svg-suite",
|
||||
"files": [
|
||||
"https://github.com/MushroomFleet/svg-suite"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "SVG Suite is an advanced set of nodes for converting images to SVG in ComfyUI, expanding upon the functionality of ComfyUI-ToSVG."
|
||||
},
|
||||
{
|
||||
"author": "joeriben",
|
||||
"title": "AI4ArtsEd Ollama Prompt Node [DEPRECATED]",
|
||||
"reference": "https://github.com/joeriben/ai4artsed_comfyui",
|
||||
"files": [
|
||||
"https://github.com/joeriben/ai4artsed_comfyui"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Experimental nodes for ComfyUI. For more, see [a/https://kubi-meta.de/ai4artsed](https://kubi-meta.de/ai4artsed) A custom ComfyUI node for stylistic and cultural transformation of input text using local LLMs served via Ollama. This node allows you to combine a free-form prompt (e.g. translation, poetic recoding, genre shift) with externally supplied text in the ComfyUI graph. The result is processed via an Ollama-hosted model and returned as plain text."
|
||||
},
|
||||
{
|
||||
"author": "bento234",
|
||||
"title": "ComfyUI-bento-toolbox [REMOVED]",
|
||||
"reference": "https://github.com/bento234/ComfyUI-bento-toolbox",
|
||||
"files": [
|
||||
"https://github.com/bento234/ComfyUI-bento-toolbox"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: Tile Prompt Distributor"
|
||||
},
|
||||
{
|
||||
"author": "yichengup",
|
||||
"title": "ComfyUI-VideoBlender [REMOVED]",
|
||||
"reference": "https://github.com/yichengup/ComfyUI-VideoBlender",
|
||||
"files": [
|
||||
"https://github.com/yichengup/ComfyUI-VideoBlender"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Video clip mixing"
|
||||
},
|
||||
{
|
||||
"author": "xl0",
|
||||
"title": "latent-tools [REMOVED]",
|
||||
"reference": "https://github.com/xl0/latent-tools",
|
||||
"files": [
|
||||
"https://github.com/xl0/latent-tools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Visualize and manipulate the latent space in ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "Conor-Collins",
|
||||
"title": "ComfyUI-CoCoTools [REMOVED]",
|
||||
"reference": "https://github.com/Conor-Collins/coco_tools",
|
||||
"files": [
|
||||
"https://github.com/Conor-Collins/coco_tools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A set of custom nodes for ComfyUI providing advanced image processing, file handling, and utility functions."
|
||||
},
|
||||
{
|
||||
"author": "theUpsider",
|
||||
"title": "ComfyUI-Logic [DEPRECATED]",
|
||||
"id": "comfy-logic",
|
||||
"reference": "https://github.com/theUpsider/ComfyUI-Logic",
|
||||
"files": [
|
||||
"https://github.com/theUpsider/ComfyUI-Logic"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "An extension to ComfyUI that introduces logic nodes and conditional rendering capabilities."
|
||||
},
|
||||
{
|
||||
"author": "Malloc-pix",
|
||||
"title": "comfyui_qwen2.4_vl_node [REMOVED]",
|
||||
"reference": "https://github.com/Malloc-pix/comfyui_qwen2.4_vl_node",
|
||||
"files": [
|
||||
"https://github.com/Malloc-pix/comfyui_qwen2.4_vl_node"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: CogVLM2 Captioner, CLIP Dynamic Text Encode(cy)"
|
||||
},
|
||||
{
|
||||
"author": "inyourdreams-studio",
|
||||
"title": "ComfyUI-RBLM [REMOVED]",
|
||||
"reference": "https://github.com/inyourdreams-studio/comfyui-rblm",
|
||||
"files": [
|
||||
"https://github.com/inyourdreams-studio/comfyui-rblm"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A custom node pack for ComfyUI that provides text manipulation nodes."
|
||||
},
|
||||
{
|
||||
"author": "dream-computing",
|
||||
"title": "SyntaxNodes - Image Processing Effects for ComfyUI [REMOVED]",
|
||||
"reference": "https://github.com/dream-computing/syntax-nodes",
|
||||
"files": [
|
||||
"https://github.com/dream-computing/syntax-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A collection of custom nodes for ComfyUI designed to apply various image processing effects, stylizations, and analyses."
|
||||
},
|
||||
{
|
||||
"author": "UD1sto",
|
||||
"title": "plugin-utils-nodes [DEPRECATED]",
|
||||
"reference": "https://github.com/its-DeFine/plugin-utils-nodes",
|
||||
"files": [
|
||||
"https://github.com/its-DeFine/plugin-utils-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: Compare Images (SimHash), Image Selector, Temporal Consistency, Update Image Reference, Frame Blend."
|
||||
},
|
||||
{
|
||||
"author": "hanyingcho",
|
||||
"title": "ComfyUI LLM Promp [REMOVED]",
|
||||
"reference": "https://github.com/hanyingcho/comfyui-llmprompt",
|
||||
"files": [
|
||||
"https://github.com/hanyingcho/comfyui-llmprompt"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: Load llm, Generate Text with LLM, Inference Qwen2VL, Inference Qwen2"
|
||||
},
|
||||
{
|
||||
"author": "WASasquatch",
|
||||
"title": "WAS Node Suite [DEPRECATED]",
|
||||
"id": "was",
|
||||
"reference": "https://github.com/WASasquatch/was-node-suite-comfyui",
|
||||
"pip": ["numba"],
|
||||
"files": [
|
||||
"https://github.com/WASasquatch/was-node-suite-comfyui"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A node suite for ComfyUI with many new nodes, such as image processing, text processing, and more."
|
||||
},
|
||||
{
|
||||
"author": "TOM1063",
|
||||
"title": "ComfyUI-SamuraiTools [REMOVED]",
|
||||
"reference": "https://github.com/TOM1063/ComfyUI-SamuraiTools",
|
||||
"files": [
|
||||
"https://github.com/TOM1063/ComfyUI-SamuraiTools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI custom node for switching integer values based on boolean conditions"
|
||||
},
|
||||
{
|
||||
"author": "whitemoney293",
|
||||
"title": "ComfyUI-MediaUtilities [REMOVED]",
|
||||
"reference": "https://github.com/ThanaritKanjanametawatAU/ComfyUI-MediaUtilities",
|
||||
"files": [
|
||||
"https://github.com/ThanaritKanjanametawatAU/ComfyUI-MediaUtilities"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Custom nodes for loading and previewing media from URLs in ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "pureexe",
|
||||
"title": "DiffusionLight-ComfyUI [REMOVED]",
|
||||
"reference": "https://github.com/pureexe/DiffusionLight-ComfyUI",
|
||||
"files": [
|
||||
"https://github.com/pureexe/DiffusionLight-ComfyUI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "DiffusionLight (Turbo) implemented in ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "gondar-software",
|
||||
"title": "comfyui-custom-padding [REMOVED]",
|
||||
"reference": "https://github.com/gondar-software/comfyui-custom-padding",
|
||||
"files": [
|
||||
"https://github.com/gondar-software/comfyui-custom-padding"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: Adaptive image padding, Adaptive image unpadding"
|
||||
},
|
||||
{
|
||||
"author": "Charonartist",
|
||||
"title": "ComfyUI-EagleExporter [REMOVED]",
|
||||
"reference": "https://github.com/Charonartist/ComfyUI-EagleExporter",
|
||||
"files": [
|
||||
"https://github.com/Charonartist/ComfyUI-EagleExporter"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is an extension that automatically saves video files generated with ComfyUI's 'video combine' extension to the Eagle library."
|
||||
},
|
||||
{
|
||||
"author": "pomePLaszlo-collablyu",
|
||||
"title": "comfyui_ejam [REMOVED]",
|
||||
"reference": "https://github.com/PLaszlo-collab/comfyui_ejam",
|
||||
"files": [
|
||||
"https://github.com/PLaszlo-collab/comfyui_ejam"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Ejam nodes for comfyui"
|
||||
},
|
||||
{
|
||||
"author": "jonnydolake",
|
||||
"title": "ComfyUI-AIR-Nodes [REMOVED]",
|
||||
"reference": "https://github.com/jonnydolake/ComfyUI-AIR-Nodes",
|
||||
"files": [
|
||||
"https://github.com/jonnydolake/ComfyUI-AIR-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: String List To Prompt Schedule, Force Minimum Batch Size, Target Location (Crop), Target Location (Paste), Image Composite Chained, Match Image Count To Mask Count, Random Character Prompts, Parallax Test, Easy Parallax, Parallax GPU Test"
|
||||
},
|
||||
{
|
||||
"author": "solution9th",
|
||||
"title": "Comfyui_mobilesam [REMOVED]",
|
||||
"reference": "https://github.com/solution9th/Comfyui_mobilesam",
|
||||
"files": [
|
||||
"https://github.com/solution9th/Comfyui_mobilesam"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: Mobile SAM Model Loader, Mobile SAM Detector, Mobile SAM Predictor"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"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,5 +1,219 @@
|
||||
{
|
||||
"models": [
|
||||
{
|
||||
"name": "sam2.1_hiera_tiny.pt",
|
||||
"type": "sam2.1",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2.1 hiera model (tiny)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2.1_hiera_tiny.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_tiny.pt",
|
||||
"size": "149.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2.1_hiera_small.pt",
|
||||
"type": "sam2.1",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2.1 hiera model (small)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2.1_hiera_small.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_small.pt",
|
||||
"size": "176.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2.1_hiera_base_plus.pt",
|
||||
"type": "sam2.1",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2.1 hiera model (base+)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2.1_hiera_base_plus.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_base_plus.pt",
|
||||
"size": "309.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2.1_hiera_large.pt",
|
||||
"type": "sam2.1",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2.1 hiera model (large)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2.1_hiera_large.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/092824/sam2.1_hiera_large.pt",
|
||||
"size": "857.0MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "sam2_hiera_tiny.pt",
|
||||
"type": "sam2",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2 hiera model (tiny)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2_hiera_tiny.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_tiny.pt",
|
||||
"size": "149.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2_hiera_small.pt",
|
||||
"type": "sam2",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2 hiera model (small)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2_hiera_small.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_small.pt",
|
||||
"size": "176.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2_hiera_base_plus.pt",
|
||||
"type": "sam2",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2 hiera model (base+)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2_hiera_base_plus.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_base_plus.pt",
|
||||
"size": "309.0MB"
|
||||
},
|
||||
{
|
||||
"name": "sam2_hiera_large.pt",
|
||||
"type": "sam2",
|
||||
"base": "SAM",
|
||||
"save_path": "sams",
|
||||
"description": "Segmenty Anything SAM 2 hiera model (large)",
|
||||
"reference": "https://github.com/facebookresearch/sam2#model-description",
|
||||
"filename": "sam2_hiera_large.pt",
|
||||
"url": "https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt",
|
||||
"size": "857.0MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/omnigen2_fp16.safetensors",
|
||||
"type": "diffusion_model",
|
||||
"base": "OmniGen2",
|
||||
"save_path": "default",
|
||||
"description": "OmniGen2 diffusion model. This is required for using OmniGen2.",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged",
|
||||
"filename": "omnigen2_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/omnigen2_fp16.safetensors",
|
||||
"size": "7.93GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/qwen_2.5_vl_fp16.safetensors",
|
||||
"type": "clip",
|
||||
"base": "qwen-2.5",
|
||||
"save_path": "default",
|
||||
"description": "text encoder for OmniGen2",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged",
|
||||
"filename": "qwen_2.5_vl_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Omnigen2_ComfyUI_repackaged/resolve/main/split_files/text_encoders/qwen_2.5_vl_fp16.safetensors",
|
||||
"size": "7.51GB"
|
||||
},
|
||||
|
||||
{
|
||||
"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)",
|
||||
"type": "diffusion_model",
|
||||
@@ -475,236 +689,6 @@
|
||||
"filename": "flux-hed-controlnet-v3.safetensors",
|
||||
"url": "https://huggingface.co/XLabs-AI/flux-controlnet-collections/resolve/main/flux-hed-controlnet-v3.safetensors",
|
||||
"size": "1.49GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "XLabs-AI/realism_lora.safetensors",
|
||||
"type": "lora",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "xlabs/loras",
|
||||
"description": "A checkpoint with trained LoRAs for FLUX.1-dev model by Black Forest Labs",
|
||||
"reference": "https://huggingface.co/XLabs-AI/flux-lora-collection",
|
||||
"filename": "realism_lora.safetensors",
|
||||
"url": "https://huggingface.co/XLabs-AI/flux-lora-collection/resolve/main/realism_lora.safetensors",
|
||||
"size": "44.8MB"
|
||||
},
|
||||
{
|
||||
"name": "XLabs-AI/art_lora.safetensors",
|
||||
"type": "lora",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "xlabs/loras",
|
||||
"description": "A checkpoint with trained LoRAs for FLUX.1-dev model by Black Forest Labs",
|
||||
"reference": "https://huggingface.co/XLabs-AI/flux-lora-collection",
|
||||
"filename": "art_lora.safetensors",
|
||||
"url": "https://huggingface.co/XLabs-AI/flux-lora-collection/resolve/main/scenery_lora.safetensors",
|
||||
"size": "44.8MB"
|
||||
},
|
||||
{
|
||||
"name": "XLabs-AI/mjv6_lora.safetensors",
|
||||
"type": "lora",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "xlabs/loras",
|
||||
"description": "A checkpoint with trained LoRAs for FLUX.1-dev model by Black Forest Labs",
|
||||
"reference": "https://huggingface.co/XLabs-AI/flux-lora-collection",
|
||||
"filename": "mjv6_lora.safetensors",
|
||||
"url": "https://huggingface.co/XLabs-AI/flux-lora-collection/resolve/main/mjv6_lora.safetensors",
|
||||
"size": "44.8MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "XLabs-AI/flux-ip-adapter",
|
||||
"type": "lora",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "xlabs/ipadapters",
|
||||
"description": "A checkpoint with trained LoRAs for FLUX.1-dev model by Black Forest Labs",
|
||||
"reference": "https://huggingface.co/XLabs-AI/flux-ip-adapter",
|
||||
"filename": "ip_adapter.safetensors",
|
||||
"url": "https://huggingface.co/XLabs-AI/flux-ip-adapter/resolve/main/ip_adapter.safetensors",
|
||||
"size": "982MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "stabilityai/SD3.5-Large-Controlnet-Blur",
|
||||
"type": "controlnet",
|
||||
"base": "SD3.5",
|
||||
"save_path": "controlnet/SD3.5",
|
||||
"description": "Blur Controlnet model for SD3.5 Large",
|
||||
"reference": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets",
|
||||
"filename": "sd3.5_large_controlnet_blur.safetensors",
|
||||
"url": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets/resolve/main/sd3.5_large_controlnet_blur.safetensors",
|
||||
"size": "8.65GB"
|
||||
},
|
||||
{
|
||||
"name": "stabilityai/SD3.5-Large-Controlnet-Canny",
|
||||
"type": "controlnet",
|
||||
"base": "SD3.5",
|
||||
"save_path": "controlnet/SD3.5",
|
||||
"description": "Canny Controlnet model for SD3.5 Large",
|
||||
"reference": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets",
|
||||
"filename": "sd3.5_large_controlnet_canny.safetensors",
|
||||
"url": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets/resolve/main/sd3.5_large_controlnet_canny.safetensors",
|
||||
"size": "8.65GB"
|
||||
},
|
||||
{
|
||||
"name": "stabilityai/SD3.5-Large-Controlnet-Depth",
|
||||
"type": "controlnet",
|
||||
"base": "SD3.5",
|
||||
"save_path": "controlnet/SD3.5",
|
||||
"description": "Depth Controlnet model for SD3.5 Large",
|
||||
"reference": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets",
|
||||
"filename": "sd3.5_large_controlnet_depth.safetensors",
|
||||
"url": "https://huggingface.co/stabilityai/stable-diffusion-3.5-controlnets/resolve/main/sd3.5_large_controlnet_depth.safetensors",
|
||||
"size": "8.65GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "LTX-Video 2B v0.9 Checkpoint",
|
||||
"type": "checkpoint",
|
||||
"base": "LTX-Video",
|
||||
"save_path": "checkpoints/LTXV",
|
||||
"description": "LTX-Video is the first DiT-based video generation model capable of generating high-quality videos in real-time. It produces 24 FPS videos at a 768x512 resolution faster than they can be watched. Trained on a large-scale dataset of diverse videos, the model generates high-resolution videos with realistic and varied content.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||
"filename": "ltx-video-2b-v0.9.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.safetensors",
|
||||
"size": "9.37GB"
|
||||
},
|
||||
{
|
||||
"name": "InstantX/FLUX.1-dev-IP-Adapter",
|
||||
"type": "IP-Adapter",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "ipadapter-flux",
|
||||
"description": "FLUX.1-dev-IP-Adapter",
|
||||
"reference": "https://huggingface.co/InstantX/FLUX.1-dev-IP-Adapter",
|
||||
"filename": "ip-adapter.bin",
|
||||
"url": "https://huggingface.co/InstantX/FLUX.1-dev-IP-Adapter/resolve/main/ip-adapter.bin",
|
||||
"size": "5.29GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/sigclip_vision_384 (patch14_384)",
|
||||
"type": "clip_vision",
|
||||
"base": "sigclip",
|
||||
"save_path": "clip_vision",
|
||||
"description": "This clip vision model is required for FLUX.1 Redux.",
|
||||
"reference": "https://huggingface.co/Comfy-Org/sigclip_vision_384/tree/main",
|
||||
"filename": "sigclip_vision_patch14_384.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/sigclip_vision_384/resolve/main/sigclip_vision_patch14_384.safetensors",
|
||||
"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,5 +1,15 @@
|
||||
{
|
||||
"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",
|
||||
"title": "Guide To Making Custom Nodes in ComfyUI",
|
||||
|
||||
373
notebooks/comfyui_colab_with_manager.ipynb
Normal file
373
notebooks/comfyui_colab_with_manager.ipynb
Normal file
@@ -0,0 +1,373 @@
|
||||
{
|
||||
"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
Normal file
846
openapi.yaml
Normal file
@@ -0,0 +1,846 @@
|
||||
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
|
||||
@@ -12,10 +12,13 @@ import ast
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
from .common import security_check
|
||||
from .common import manager_util
|
||||
from .common import cm_global
|
||||
from .common import manager_downloader
|
||||
glob_path = os.path.join(os.path.dirname(__file__), "glob")
|
||||
sys.path.append(glob_path)
|
||||
|
||||
import security_check
|
||||
import manager_util
|
||||
import cm_global
|
||||
import manager_downloader
|
||||
import folder_paths
|
||||
|
||||
manager_util.add_python_path_to_env()
|
||||
@@ -35,10 +38,9 @@ else:
|
||||
def current_timestamp():
|
||||
return str(time.time()).split('.')[0]
|
||||
|
||||
security_check.security_check()
|
||||
|
||||
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
cm_global.pip_blacklist = {'torch', 'torchaudio', 'torchsde', 'torchvision'}
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchaudio', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
|
||||
|
||||
def skip_pip_spam(x):
|
||||
@@ -65,17 +67,15 @@ comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
try:
|
||||
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
||||
os.environ['COMFYUI_PATH'] = comfy_path
|
||||
except:
|
||||
print("[ComfyUI-Manager] environment variable 'COMFYUI_PATH' is not specified.")
|
||||
exit(-1)
|
||||
# legacy env var
|
||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
||||
|
||||
if comfy_base_path is None:
|
||||
comfy_base_path = comfy_path
|
||||
|
||||
|
||||
sys.__comfyui_manager_register_message_collapse = register_message_collapse
|
||||
sys.__comfyui_manager_is_import_failed_extension = is_import_failed_extension
|
||||
cm_global.register_api('cm.register_message_collapse', register_message_collapse)
|
||||
@@ -118,13 +118,20 @@ def check_file_logging():
|
||||
|
||||
read_config()
|
||||
read_uv_mode()
|
||||
security_check.security_check()
|
||||
check_file_logging()
|
||||
|
||||
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(manager_pip_overrides_path):
|
||||
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||
cm_global.pip_overrides = json.load(json_file)
|
||||
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
||||
|
||||
if sys.version_info < (3, 13):
|
||||
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
||||
|
||||
|
||||
if os.path.exists(manager_pip_blacklist_path):
|
||||
@@ -337,7 +344,12 @@ try:
|
||||
log_file.write(message)
|
||||
else:
|
||||
log_file.write(f"[{timestamp}] {message}")
|
||||
log_file.flush()
|
||||
|
||||
try:
|
||||
log_file.flush()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
self.last_char = message if message == '' else message[-1]
|
||||
|
||||
if not file_only:
|
||||
@@ -350,7 +362,10 @@ try:
|
||||
original_stderr.flush()
|
||||
|
||||
def flush(self):
|
||||
log_file.flush()
|
||||
try:
|
||||
log_file.flush()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
with std_log_lock:
|
||||
if self.is_stdout:
|
||||
@@ -391,11 +406,7 @@ try:
|
||||
def emit(self, record):
|
||||
global is_start_mode
|
||||
|
||||
try:
|
||||
message = record.getMessage()
|
||||
except Exception as e:
|
||||
message = f"<<logging error>>: {record} - {e}"
|
||||
original_stderr.write(message)
|
||||
message = record.getMessage()
|
||||
|
||||
if is_start_mode:
|
||||
match = re.search(pat_import_fail, message)
|
||||
@@ -623,6 +634,7 @@ def execute_lazy_install_script(repo_path, executable):
|
||||
lines = manager_util.robust_readlines(requirements_path)
|
||||
for line in lines:
|
||||
package_name = remap_pip_package(line.strip())
|
||||
package_name = package_name.split('#')[0].strip()
|
||||
if package_name and not is_installed(package_name):
|
||||
if '--index-url' in package_name:
|
||||
s = package_name.split('--index-url')
|
||||
@@ -1,62 +1,15 @@
|
||||
[build-system]
|
||||
requires = ["setuptools >= 61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
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."
|
||||
readme = "README.md"
|
||||
keywords = ["comfyui", "comfyui-manager"]
|
||||
|
||||
maintainers = [
|
||||
{ name = "Dr.Lt.Data", email = "dr.lt.data@gmail.com" },
|
||||
{ name = "Yoland Yan", email = "yoland@drip.art" },
|
||||
{ name = "James Kwon", email = "hongilkwon316@gmail.com" },
|
||||
{ name = "Robin Huang", email = "robin@drip.art" },
|
||||
]
|
||||
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"GitPython",
|
||||
"PyGithub",
|
||||
"matrix-client==0.4.0",
|
||||
"transformers",
|
||||
"huggingface-hub>0.20",
|
||||
"typer",
|
||||
"rich",
|
||||
"typing-extensions",
|
||||
"toml",
|
||||
"uv",
|
||||
"chardet"
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["pre-commit", "pytest", "ruff", "pytest-cov"]
|
||||
version = "3.33.8"
|
||||
license = { file = "LICENSE.txt" }
|
||||
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions", "toml", "uv", "chardet"]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/ltdrdata/ComfyUI-Manager"
|
||||
# Used by Comfy Registry https://comfyregistry.org
|
||||
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["."]
|
||||
include = ["comfyui_manager*"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
target-version = "py39"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"E4", # default
|
||||
"E7", # default
|
||||
"E9", # default
|
||||
"F", # default
|
||||
"I", # isort-like behavior (import statement sorting)
|
||||
]
|
||||
[tool.comfy]
|
||||
PublisherId = "drltdata"
|
||||
DisplayName = "ComfyUI-Manager"
|
||||
Icon = ""
|
||||
|
||||
10
scanner.py
10
scanner.py
@@ -102,12 +102,8 @@ def extract_nodes(code_text):
|
||||
def scan_in_file(filename, is_builtin=False):
|
||||
global builtin_nodes
|
||||
|
||||
try:
|
||||
with open(filename, encoding='utf-8') as file:
|
||||
code = file.read()
|
||||
except UnicodeDecodeError:
|
||||
with open(filename, encoding='cp949') as file:
|
||||
code = file.read()
|
||||
with open(filename, encoding='utf-8', errors='ignore') as file:
|
||||
code = file.read()
|
||||
|
||||
pattern = r"_CLASS_MAPPINGS\s*=\s*{([^}]*)}"
|
||||
regex = re.compile(pattern, re.MULTILINE | re.DOTALL)
|
||||
@@ -297,7 +293,7 @@ def update_custom_nodes():
|
||||
pass
|
||||
|
||||
def is_rate_limit_exceeded():
|
||||
return g.rate_limiting[0] == 0
|
||||
return g.rate_limiting[0] <= 20
|
||||
|
||||
if is_rate_limit_exceeded():
|
||||
print(f"GitHub API Rate Limit Exceeded: remained - {(g.rate_limiting_resettime - datetime.datetime.now().timestamp())/60:.2f} min")
|
||||
|
||||
39
scripts/colab-dependencies.py
Normal file
39
scripts/colab-dependencies.py
Normal file
@@ -0,0 +1,39 @@
|
||||
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)
|
||||
21
scripts/install-comfyui-venv-linux.sh
Executable file
21
scripts/install-comfyui-venv-linux.sh
Executable file
@@ -0,0 +1,21 @@
|
||||
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
|
||||
17
scripts/install-comfyui-venv-win.bat
Executable file
17
scripts/install-comfyui-venv-win.bat
Executable file
@@ -0,0 +1,17 @@
|
||||
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
|
||||
3
scripts/install-manager-for-portable-version.bat
Normal file
3
scripts/install-manager-for-portable-version.bat
Normal file
@@ -0,0 +1,3 @@
|
||||
.\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