Compare commits
244 Commits
3.17.2
...
draft-v4-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78269f6467 | ||
|
|
a9fedc0487 | ||
|
|
abfd85602e | ||
|
|
1816bb748e | ||
|
|
05ceab68f8 | ||
|
|
46a37907e6 | ||
|
|
7fc8ba587e | ||
|
|
7a35bd9d9a | ||
|
|
17e5c3d2f5 | ||
|
|
27bfc539f7 | ||
|
|
a76ef49d2d | ||
|
|
bb0fcf6ea6 | ||
|
|
539e0a1534 | ||
|
|
aaae6ce304 | ||
|
|
821fded09d | ||
|
|
ec4a2aa873 | ||
|
|
d6b2d54f3f | ||
|
|
97ae67bb9a | ||
|
|
765514a33f | ||
|
|
e2cdcc96c4 | ||
|
|
3c413840d7 | ||
|
|
29ca93fcb4 | ||
|
|
9dc8e630a0 | ||
|
|
10105ad737 | ||
|
|
5738ea861a | ||
|
|
dbd25b0f0a | ||
|
|
0de9d36c28 | ||
|
|
05f1a8ab0d | ||
|
|
5ce170b7ce | ||
|
|
2b47aad076 | ||
|
|
b4dc59331d | ||
|
|
81e84fad78 | ||
|
|
42e8a959dd | ||
|
|
208ca31836 | ||
|
|
0738b2a73f | ||
|
|
98db79910e | ||
|
|
0b21a05aac | ||
|
|
4b71db54aa | ||
|
|
a6bc890f36 | ||
|
|
76903c39e1 | ||
|
|
cf9ed1c631 | ||
|
|
50fc1389b0 | ||
|
|
c70cb2868b | ||
|
|
12fa571aa2 | ||
|
|
4a3018760f | ||
|
|
d005d06cf8 | ||
|
|
a87e3f9ee9 | ||
|
|
52b9a3f3a0 | ||
|
|
c01a7e41d0 | ||
|
|
fe301bb91a | ||
|
|
a42953e3be | ||
|
|
1899255a69 | ||
|
|
908a1009d2 | ||
|
|
fb9c68fc32 | ||
|
|
d54ec0eb05 | ||
|
|
a386948fd1 | ||
|
|
007b812ede | ||
|
|
0ddb0cec03 | ||
|
|
e687f83fbf | ||
|
|
458c9de70f | ||
|
|
a128baf894 | ||
|
|
57b847eebf | ||
|
|
149257e4f1 | ||
|
|
212b8e7ed2 | ||
|
|
87a652d038 | ||
|
|
d889df4c89 | ||
|
|
a2e72d26aa | ||
|
|
a4fdc874e7 | ||
|
|
dfbe382d60 | ||
|
|
0d56ebb1bf | ||
|
|
01ac9c895a | ||
|
|
ebcb14e6aa | ||
|
|
9e66da174e | ||
|
|
55fcb00168 | ||
|
|
68aa534e1d | ||
|
|
7fd94a401b | ||
|
|
2b9cec50ce | ||
|
|
d1a80cf082 | ||
|
|
fb445aa510 | ||
|
|
4b904934ef | ||
|
|
d6295a00e6 | ||
|
|
3b01673829 | ||
|
|
a5e83a807f | ||
|
|
ddd766ce58 | ||
|
|
a6d2fd36fb | ||
|
|
9156d6bdba | ||
|
|
d18a3ffeff | ||
|
|
e933eaa2b0 | ||
|
|
5393653ddc | ||
|
|
1f3274d3f5 | ||
|
|
39eaa76b8a | ||
|
|
e5396713ce | ||
|
|
79943de808 | ||
|
|
e05f329602 | ||
|
|
eed0e8ebea | ||
|
|
731eb4fcbe | ||
|
|
44a63e4b6d | ||
|
|
7651e5e48b | ||
|
|
2449636d32 | ||
|
|
f9990ca8eb | ||
|
|
c3eed981c0 | ||
|
|
bbb54d4a08 | ||
|
|
4566c585db | ||
|
|
a946338a18 | ||
|
|
0a60a44478 | ||
|
|
cef0ad6707 | ||
|
|
7176f0837a | ||
|
|
6b1f2b2d9d | ||
|
|
38a1a9b320 | ||
|
|
402e2c384f | ||
|
|
9d5faa096c | ||
|
|
97d0dc20f1 | ||
|
|
8d99ff07b6 | ||
|
|
04fa540a8c | ||
|
|
eb41867e04 | ||
|
|
eee5d7d9e8 | ||
|
|
e983f9ed35 | ||
|
|
8b16ef641b | ||
|
|
e87d616b7a | ||
|
|
2220f325fc | ||
|
|
b53ed47ccb | ||
|
|
39df2743fe | ||
|
|
3f729aaf03 | ||
|
|
b7324621e4 | ||
|
|
e8c782c8e1 | ||
|
|
9136505565 | ||
|
|
f406d728cc | ||
|
|
d649ca47c6 | ||
|
|
e8111527b4 | ||
|
|
2af66d7efc | ||
|
|
27706f37f6 | ||
|
|
3de17b2fa6 | ||
|
|
22ecb5de95 | ||
|
|
992b8b3cb5 | ||
|
|
bebc16d5a6 | ||
|
|
ddb719f866 | ||
|
|
0bd1bf2605 | ||
|
|
fd32ba4035 | ||
|
|
22f723b920 | ||
|
|
1248bd0413 | ||
|
|
c150eec2b6 | ||
|
|
c7248c2d47 | ||
|
|
e71e68e298 | ||
|
|
6969557693 | ||
|
|
f6be5ad839 | ||
|
|
cebe3664fd | ||
|
|
cdab465c90 | ||
|
|
144384655c | ||
|
|
0e213d6dab | ||
|
|
21294a4e4a | ||
|
|
3ba4d44d9e | ||
|
|
1f86ef5a37 | ||
|
|
fac60da333 | ||
|
|
5a5a37dfee | ||
|
|
0d487bc14f | ||
|
|
a52b4eb5ed | ||
|
|
f1b7f5f52f | ||
|
|
5ef58652bf | ||
|
|
e26a9e75c6 | ||
|
|
b0035ff4a7 | ||
|
|
94b6f9b2fe | ||
|
|
cad1482b3f | ||
|
|
ea7aafb3e6 | ||
|
|
42b15ad4a5 | ||
|
|
d3d613cca9 | ||
|
|
86893d999a | ||
|
|
4fd17b0bf5 | ||
|
|
76d2206058 | ||
|
|
51e8b608dc | ||
|
|
a68330fb8f | ||
|
|
2449ad5c69 | ||
|
|
064c812df3 | ||
|
|
48d5ec9e66 | ||
|
|
914419fd1e | ||
|
|
f005fc8ca0 | ||
|
|
43b7960de2 | ||
|
|
2ed1e58032 | ||
|
|
c63b212700 | ||
|
|
e9df78c0e7 | ||
|
|
b0daf81185 | ||
|
|
cee4fdcbb0 | ||
|
|
df3cdfccb0 | ||
|
|
894042cd0e | ||
|
|
8123287952 | ||
|
|
bc677705d8 | ||
|
|
5dd8ea8aab | ||
|
|
41172be796 | ||
|
|
ad1b4a9a86 | ||
|
|
e0e3ec02b3 | ||
|
|
a6cc392473 | ||
|
|
36f48b8656 | ||
|
|
3d883ca37d | ||
|
|
34ed81ca64 | ||
|
|
a9e0880572 | ||
|
|
9500e1c3c4 | ||
|
|
d81aa9cbbc | ||
|
|
21d4b25c2d | ||
|
|
0080783a11 | ||
|
|
2c3f44a3f8 | ||
|
|
3ddf414097 | ||
|
|
59fb63f1f7 | ||
|
|
fa1b514440 | ||
|
|
1579c58876 | ||
|
|
153d044331 | ||
|
|
f2496f7054 | ||
|
|
99022f4f3d | ||
|
|
60a5e4f261 | ||
|
|
661586d3b6 | ||
|
|
abc26cf906 | ||
|
|
12351bada7 | ||
|
|
a6816d53d7 | ||
|
|
3b0709f5f2 | ||
|
|
d7af7e2917 | ||
|
|
6516e62d33 | ||
|
|
6b832edd2f | ||
|
|
eebace1652 | ||
|
|
6ff6e05408 | ||
|
|
aaf569ca8c | ||
|
|
31eef6222e | ||
|
|
9963afa558 | ||
|
|
5b2e2fcf9d | ||
|
|
cc746e59a1 | ||
|
|
2cdb1c519d | ||
|
|
426074ded9 | ||
|
|
772a096615 | ||
|
|
e113e011cb | ||
|
|
22266484bd | ||
|
|
559c011420 | ||
|
|
411c0633a3 | ||
|
|
488f023bdf | ||
|
|
22878f4ef8 | ||
|
|
e732a39fea | ||
|
|
62b4bf7af4 | ||
|
|
47a525ddb4 | ||
|
|
f4360725e0 | ||
|
|
b86607cd41 | ||
|
|
bf57de85c3 | ||
|
|
2dd6118ff4 | ||
|
|
816a53a7b1 | ||
|
|
ced93b0525 | ||
|
|
524ff9a4a6 | ||
|
|
f15032f905 | ||
|
|
d7d31a19e5 | ||
|
|
df2a7ddca4 |
1
.env.example
Normal file
1
.env.example
Normal file
@@ -0,0 +1 @@
|
||||
PYPI_TOKEN=your-pypi-token
|
||||
58
.github/workflows/publish-to-pypi.yml
vendored
Normal file
58
.github/workflows/publish-to-pypi.yml
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
name: Publish to PyPI
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- "pyproject.toml"
|
||||
|
||||
jobs:
|
||||
build-and-publish:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'ltdrdata' || github.repository_owner == 'Comfy-Org' }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.9'
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
python -m pip install --upgrade pip
|
||||
python -m pip install build twine
|
||||
|
||||
- name: Get current version
|
||||
id: current_version
|
||||
run: |
|
||||
CURRENT_VERSION=$(grep -oP 'version = "\K[^"]+' pyproject.toml)
|
||||
echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "Current version: $CURRENT_VERSION"
|
||||
|
||||
- name: Build package
|
||||
run: python -m build
|
||||
|
||||
- name: Create GitHub Release
|
||||
id: create_release
|
||||
uses: softprops/action-gh-release@v2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
files: dist/*
|
||||
tag_name: v${{ steps.current_version.outputs.version }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
generate_release_notes: true
|
||||
|
||||
- name: Publish to PyPI
|
||||
uses: pypa/gh-action-pypi-publish@release/v1
|
||||
with:
|
||||
password: ${{ secrets.PYPI_TOKEN }}
|
||||
skip-existing: true
|
||||
verbose: true
|
||||
8
.github/workflows/publish.yml
vendored
8
.github/workflows/publish.yml
vendored
@@ -7,15 +7,19 @@ on:
|
||||
paths:
|
||||
- "pyproject.toml"
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
publish-node:
|
||||
name: Publish Custom Node to registry
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'ltdrdata' || github.repository_owner == 'Comfy-Org' }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
- name: Publish Custom Node
|
||||
uses: Comfy-Org/publish-node-action@main
|
||||
uses: Comfy-Org/publish-node-action@v1
|
||||
with:
|
||||
## Add your own personal access token to your Github Repository secrets and reference it here.
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
|
||||
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -17,4 +17,7 @@ github-stats-cache.json
|
||||
pip_overrides.json
|
||||
*.json
|
||||
check2.sh
|
||||
/venv/
|
||||
/venv/
|
||||
build
|
||||
*.egg-info
|
||||
.env
|
||||
47
CONTRIBUTING.md
Normal file
47
CONTRIBUTING.md
Normal file
@@ -0,0 +1,47 @@
|
||||
## Testing Changes
|
||||
|
||||
1. Activate the ComfyUI environment.
|
||||
|
||||
2. Build package locally after making changes.
|
||||
|
||||
```bash
|
||||
# from inside the ComfyUI-Manager directory, with the ComfyUI environment activated
|
||||
python -m build
|
||||
```
|
||||
|
||||
3. Install the package locally in the ComfyUI environment.
|
||||
|
||||
```bash
|
||||
# Uninstall existing package
|
||||
pip uninstall comfyui-manager
|
||||
|
||||
# Install the locale package
|
||||
pip install dist/comfyui-manager-*.whl
|
||||
```
|
||||
|
||||
4. Start ComfyUI.
|
||||
|
||||
```bash
|
||||
# after navigating to the ComfyUI directory
|
||||
python main.py
|
||||
```
|
||||
|
||||
## Manually Publish Test Version to PyPi
|
||||
|
||||
1. Set the `PYPI_TOKEN` environment variable in env file.
|
||||
|
||||
2. If manually publishing, you likely want to use a release candidate version, so set the version in [pyproject.toml](pyproject.toml) to something like `0.0.1rc1`.
|
||||
|
||||
3. Build the package.
|
||||
|
||||
```bash
|
||||
python -m build
|
||||
```
|
||||
|
||||
4. Upload the package to PyPi.
|
||||
|
||||
```bash
|
||||
python -m twine upload dist/* --username __token__ --password $PYPI_TOKEN
|
||||
```
|
||||
|
||||
5. View at https://pypi.org/project/comfyui-manager/
|
||||
13
MANIFEST.in
Normal file
13
MANIFEST.in
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
127
README.md
127
README.md
@@ -5,6 +5,7 @@
|
||||

|
||||
|
||||
## NOTICE
|
||||
* V4.0: Modify the structure to be installable via pip instead of using git clone.
|
||||
* V3.16: Support for `uv` has been added. Set `use_uv` in `config.ini`.
|
||||
* V3.10: `double-click feature` is removed
|
||||
* This feature has been moved to https://github.com/ltdrdata/comfyui-connection-helper
|
||||
@@ -13,78 +14,26 @@
|
||||
|
||||
## Installation
|
||||
|
||||
### Installation[method1] (General installation method: ComfyUI-Manager only)
|
||||
* When installing the latest ComfyUI, it will be automatically installed as a dependency, so manual installation is no longer necessary.
|
||||
|
||||
To install ComfyUI-Manager in addition to an existing installation of ComfyUI, you can follow the following steps:
|
||||
* Manual installation of the nightly version:
|
||||
* Clone to a temporary directory (**Note:** Do **not** clone into `ComfyUI/custom_nodes`.)
|
||||
```
|
||||
git clone https://github.com/Comfy-Org/ComfyUI-Manager
|
||||
```
|
||||
* Install via pip
|
||||
```
|
||||
cd ComfyUI-Manager
|
||||
pip install .
|
||||
```
|
||||
|
||||
1. goto `ComfyUI/custom_nodes` dir in terminal(cmd)
|
||||
2. `git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager`
|
||||
3. Restart ComfyUI
|
||||
|
||||
|
||||
### Installation[method2] (Installation for portable ComfyUI version: ComfyUI-Manager only)
|
||||
1. install git
|
||||
- https://git-scm.com/download/win
|
||||
- standalone version
|
||||
- select option: use windows default console window
|
||||
2. Download [scripts/install-manager-for-portable-version.bat](https://github.com/ltdrdata/ComfyUI-Manager/raw/main/scripts/install-manager-for-portable-version.bat) into installed `"ComfyUI_windows_portable"` directory
|
||||
- Don't click. Right click the link and use save as...
|
||||
3. double click `install-manager-for-portable-version.bat` batch file
|
||||
|
||||

|
||||
|
||||
|
||||
### Installation[method3] (Installation through comfy-cli: install ComfyUI and ComfyUI-Manager at once.)
|
||||
> RECOMMENDED: comfy-cli provides various features to manage ComfyUI from the CLI.
|
||||
|
||||
* **prerequisite: python 3, git**
|
||||
|
||||
Windows:
|
||||
```commandline
|
||||
python -m venv venv
|
||||
venv\Scripts\activate
|
||||
pip install comfy-cli
|
||||
comfy install
|
||||
```
|
||||
|
||||
Linux/OSX:
|
||||
```commandline
|
||||
python -m venv venv
|
||||
. venv/bin/activate
|
||||
pip install comfy-cli
|
||||
comfy install
|
||||
```
|
||||
* See also: https://github.com/Comfy-Org/comfy-cli
|
||||
|
||||
|
||||
### Installation[method4] (Installation for linux+venv: ComfyUI + ComfyUI-Manager)
|
||||
## Front-end
|
||||
|
||||
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.
|
||||
* The built-in front-end of ComfyUI-Manager is the legacy front-end. The front-end for ComfyUI-Manager is now provided via [ComfyUI Frontend](https://github.com/Comfy-Org/ComfyUI_frontend).
|
||||
* To enable the legacy front-end, set the environment variable `ENABLE_LEGACY_COMFYUI_MANAGER_FRONT` to `true` before running.
|
||||
|
||||
|
||||
## How To Use
|
||||
@@ -149,6 +98,8 @@ In `ComfyUI-Manager` V3.0 and later, configuration files and dynamically generat
|
||||
* Basic config files: `<USER_DIRECTORY>/default/ComfyUI-Manager/config.ini`
|
||||
* Configurable channel lists: `<USER_DIRECTORY>/default/ComfyUI-Manager/channels.ini`
|
||||
* Configurable pip overrides: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_overrides.json`
|
||||
* Configurable pip blacklist: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_blacklist.list`
|
||||
* Configurable pip auto fix: `<USER_DIRECTORY>/default/ComfyUI-Manager/pip_auto_fix.list`
|
||||
* Saved snapshot files: `<USER_DIRECTORY>/default/ComfyUI-Manager/snapshots`
|
||||
* Startup script files: `<USER_DIRECTORY>/default/ComfyUI-Manager/startup-scripts`
|
||||
* Component files: `<USER_DIRECTORY>/default/ComfyUI-Manager/components`
|
||||
@@ -256,16 +207,23 @@ The following settings are applied based on the section marked as `is_default`.
|
||||
[default]
|
||||
git_exe = <Manually specify the path to the git executable. If left empty, the default git executable path will be used.>
|
||||
use_uv = <Use uv instead of pip for dependency installation.>
|
||||
channel_url = https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main
|
||||
default_cache_as_channel_url = <Determines whether to retrieve the DB designated as channel_url at startup>
|
||||
bypass_ssl = <Set to True if SSL errors occur to disable SSL.>
|
||||
file_logging = <Configure whether to create a log file used by ComfyUI-Manager.>
|
||||
windows_selector_event_loop_policy = <If an event loop error occurs on Windows, set this to True.>
|
||||
model_download_by_agent = <When downloading models, use an agent instead of torchvision_download_url.>
|
||||
downgrade_blacklist = <Set a list of packages to prevent downgrades. List them separated by commas.>
|
||||
security_level = <Set the security level.>
|
||||
security_level = <Set the security level => strong|normal|normal-|weak>
|
||||
always_lazy_install = <Whether to perform dependency installation on restart even in environments other than Windows.>
|
||||
network_mode = <Set the network mode => public|private|offline>
|
||||
```
|
||||
|
||||
* network_mode:
|
||||
- public: An environment that uses a typical public network.
|
||||
- private: An environment that uses a closed network, where a private node DB is configured via `channel_url`. (Uses cache if available)
|
||||
- offline: An environment that does not use any external connections when using an offline network. (Uses cache if available)
|
||||
|
||||
|
||||
## Additional Feature
|
||||
* Logging to file feature
|
||||
* This feature is enabled by default and can be disabled by setting `file_logging = False` in the `config.ini`.
|
||||
@@ -294,13 +252,40 @@ The following settings are applied based on the section marked as `is_default`.
|
||||
* Custom pip mapping
|
||||
* When you create the `pip_overrides.json` file, it changes the installation of specific pip packages to installations defined by the user.
|
||||
* Please refer to the `pip_overrides.json.template` file.
|
||||
|
||||
|
||||
* Prevent the installation of specific pip packages
|
||||
* List the package names one per line in the `pip_blacklist.list` file.
|
||||
|
||||
* Automatically Restoring pip Installation
|
||||
* If you list pip spec requirements in `pip_auto_fix.list`, similar to `requirements.txt`, it will automatically restore the specified versions when starting ComfyUI or when versions get mismatched during various custom node installations.
|
||||
* `--index-url` can be used.
|
||||
|
||||
* Use `aria2` as downloader
|
||||
* [howto](docs/en/use_aria2.md)
|
||||
|
||||
* If you add the item `skip_migration_check = True` to `config.ini`, it will not check whether there are nodes that can be migrated at startup.
|
||||
* This option can be used if performance issues occur in a Colab+GDrive environment.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
The following features can be configured using environment variables:
|
||||
|
||||
* **COMFYUI_PATH**: The installation path of ComfyUI
|
||||
* **GITHUB_ENDPOINT**: Reverse proxy configuration for environments with limited access to GitHub
|
||||
* **HF_ENDPOINT**: Reverse proxy configuration for environments with limited access to Hugging Face
|
||||
|
||||
|
||||
### Example 1:
|
||||
Redirecting `https://github.com/ltdrdata/ComfyUI-Impact-Pack` to `https://mirror.ghproxy.com/https://github.com/ltdrdata/ComfyUI-Impact-Pack`
|
||||
|
||||
```
|
||||
GITHUB_ENDPOINT=https://mirror.ghproxy.com/https://github.com
|
||||
```
|
||||
|
||||
#### Example 2:
|
||||
Changing `https://huggingface.co/path/to/somewhere` to `https://some-hf-mirror.com/path/to/somewhere`
|
||||
|
||||
```
|
||||
HF_ENDPOINT=https://some-hf-mirror.com
|
||||
```
|
||||
|
||||
## Scanner
|
||||
When you run the `scan.sh` script:
|
||||
|
||||
18
__init__.py
18
__init__.py
@@ -1,18 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
cli_mode_flag = os.path.join(os.path.dirname(__file__), '.enable-cli-only-mode')
|
||||
|
||||
if not os.path.exists(cli_mode_flag):
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "glob"))
|
||||
import manager_server # noqa: F401
|
||||
import share_3rdparty # noqa: F401
|
||||
WEB_DIRECTORY = "js"
|
||||
else:
|
||||
print("\n[ComfyUI-Manager] !! cli-only-mode is enabled !!\n")
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
__all__ = ['NODE_CLASS_MAPPINGS']
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#!/bin/bash
|
||||
python cm-cli.py $*
|
||||
python ./comfyui_manager/cm-cli.py $*
|
||||
|
||||
43
comfyui_manager/__init__.py
Normal file
43
comfyui_manager/__init__.py
Normal file
@@ -0,0 +1,43 @@
|
||||
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
|
||||
@@ -15,34 +15,35 @@ import git
|
||||
import importlib
|
||||
|
||||
|
||||
sys.path.append(os.path.dirname(__file__))
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), "glob"))
|
||||
|
||||
import manager_util
|
||||
from .common import manager_util
|
||||
|
||||
# read env vars
|
||||
# COMFYUI_FOLDERS_BASE_PATH is not required in cm-cli.py
|
||||
# `comfy_path` should be resolved before importing manager_core
|
||||
|
||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
try:
|
||||
import folder_paths
|
||||
comfy_path = os.path.join(os.path.dirname(folder_paths.__file__))
|
||||
except:
|
||||
print("\n[bold yellow]WARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path.[/bold yellow]", file=sys.stderr)
|
||||
comfy_path = os.path.abspath(os.path.join(manager_util.comfyui_manager_path, '..', '..'))
|
||||
print("[bold red]cm-cli: environment variable 'COMFYUI_PATH' is not specified.[/bold red]")
|
||||
exit(-1)
|
||||
|
||||
sys.path.append(comfy_path)
|
||||
|
||||
if not os.path.exists(os.path.join(comfy_path, 'folder_paths.py')):
|
||||
print("[bold red]cm-cli: '{comfy_path}' is not a valid 'COMFYUI_PATH' location.[/bold red]")
|
||||
exit(-1)
|
||||
|
||||
|
||||
import utils.extra_config
|
||||
import cm_global
|
||||
import manager_core as core
|
||||
from manager_core import unified_manager
|
||||
import cnr_utils
|
||||
from .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
|
||||
|
||||
comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
cm_global.pip_blacklist = ['torch', 'torchsde', 'torchvision']
|
||||
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||
|
||||
@@ -51,14 +52,26 @@ if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_overrides
|
||||
cm_global.pip_overrides = json.load(json_file)
|
||||
|
||||
|
||||
if os.path.exists(os.path.join(manager_util.comfyui_manager_path, "pip_blacklist.list")):
|
||||
with open(os.path.join(manager_util.comfyui_manager_path, "pip_blacklist.list"), 'r', encoding="UTF-8", errors="ignore") as f:
|
||||
for x in f.readlines():
|
||||
y = x.strip()
|
||||
if y != '':
|
||||
cm_global.pip_blacklist.add(y)
|
||||
|
||||
|
||||
def check_comfyui_hash():
|
||||
repo = git.Repo(comfy_path)
|
||||
core.comfy_ui_revision = len(list(repo.iter_commits('HEAD')))
|
||||
try:
|
||||
repo = git.Repo(comfy_path)
|
||||
core.comfy_ui_revision = len(list(repo.iter_commits('HEAD')))
|
||||
core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime
|
||||
except:
|
||||
print('[bold yellow]INFO: Frozen ComfyUI mode.[/bold yellow]')
|
||||
core.comfy_ui_revision = 0
|
||||
core.comfy_ui_commit_datetime = 0
|
||||
|
||||
cm_global.variables['comfyui.revision'] = core.comfy_ui_revision
|
||||
|
||||
core.comfy_ui_commit_datetime = repo.head.commit.committed_datetime
|
||||
|
||||
|
||||
check_comfyui_hash() # This is a preparation step for manager_core
|
||||
core.check_invalid_nodes()
|
||||
@@ -67,8 +80,8 @@ core.check_invalid_nodes()
|
||||
def read_downgrade_blacklist():
|
||||
try:
|
||||
import configparser
|
||||
config = configparser.ConfigParser()
|
||||
config.read(core.manager_config.path)
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read(context.manager_config_path)
|
||||
default_conf = config['default']
|
||||
|
||||
if 'downgrade_blacklist' in default_conf:
|
||||
@@ -129,24 +142,36 @@ class Ctx:
|
||||
if os.path.exists(extra_model_paths_yaml):
|
||||
utils.extra_config.load_extra_path_config(extra_model_paths_yaml)
|
||||
|
||||
core.update_user_directory(user_directory)
|
||||
context.update_user_directory(user_directory)
|
||||
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
for x in f.readlines():
|
||||
y = x.strip()
|
||||
if y != '':
|
||||
cm_global.pip_blacklist.add(y)
|
||||
|
||||
def update_custom_nodes_dir(self, target_dir):
|
||||
import folder_paths
|
||||
a, b = folder_paths.folder_names_and_paths['custom_nodes']
|
||||
folder_paths.folder_names_and_paths['custom_nodes'] = [os.path.abspath(target_dir)], set()
|
||||
|
||||
@staticmethod
|
||||
def get_startup_scripts_path():
|
||||
return os.path.join(core.manager_startup_script_path, "install-scripts.txt")
|
||||
return os.path.join(context.manager_startup_script_path, "install-scripts.txt")
|
||||
|
||||
@staticmethod
|
||||
def get_restore_snapshot_path():
|
||||
return os.path.join(core.manager_startup_script_path, "restore-snapshot.json")
|
||||
return os.path.join(context.manager_startup_script_path, "restore-snapshot.json")
|
||||
|
||||
@staticmethod
|
||||
def get_snapshot_path():
|
||||
return core.manager_snapshot_path
|
||||
return context.manager_snapshot_path
|
||||
|
||||
@staticmethod
|
||||
def get_custom_nodes_paths():
|
||||
@@ -229,7 +254,7 @@ def fix_node(node_spec_str, is_all=False, cnt_msg=''):
|
||||
res = unified_manager.unified_fix(node_name, version_spec, no_deps=cmd_ctx.no_deps)
|
||||
|
||||
if not res.result:
|
||||
print(f"ERROR: f{res.msg}")
|
||||
print(f"[bold red]ERROR: f{res.msg}[/bold red]")
|
||||
|
||||
|
||||
def uninstall_node(node_spec_str: str, is_all: bool = False, cnt_msg: str = ''):
|
||||
@@ -622,7 +647,7 @@ def install(
|
||||
cmd_ctx.set_channel_mode(channel, mode)
|
||||
cmd_ctx.set_no_deps(no_deps)
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
for_each_nodes(nodes, act=install_node)
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
@@ -660,7 +685,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())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
for_each_nodes(nodes, act=reinstall_node)
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
@@ -686,7 +711,7 @@ def uninstall(
|
||||
for_each_nodes(nodes, act=uninstall_node)
|
||||
|
||||
|
||||
@app.command(help="Disable custom nodes")
|
||||
@app.command(help="Update custom nodes")
|
||||
def update(
|
||||
nodes: List[str] = typer.Argument(
|
||||
...,
|
||||
@@ -714,7 +739,7 @@ def update(
|
||||
if 'all' in nodes:
|
||||
asyncio.run(auto_save_snapshot())
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
|
||||
for x in nodes:
|
||||
if x.lower() in ['comfyui', 'comfy', 'all']:
|
||||
@@ -815,7 +840,7 @@ def fix(
|
||||
if 'all' in nodes:
|
||||
asyncio.run(auto_save_snapshot())
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
for_each_nodes(nodes, fix_node, allow_all=True)
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
@@ -1012,17 +1037,34 @@ def save_snapshot(
|
||||
user_directory: str = typer.Option(
|
||||
None,
|
||||
help="user directory"
|
||||
)
|
||||
),
|
||||
full_snapshot: Annotated[
|
||||
bool,
|
||||
typer.Option(
|
||||
show_default=False, help="If the snapshot should include custom node, ComfyUI version and pip versions (default), or only custom node details"
|
||||
),
|
||||
] = True,
|
||||
):
|
||||
cmd_ctx.set_user_directory(user_directory)
|
||||
|
||||
path = asyncio.run(core.save_snapshot_with_postfix('snapshot', output))
|
||||
if output is not None:
|
||||
if(not output.endswith('.json') and not output.endswith('.yaml')):
|
||||
print("[bold red]ERROR: output path should be either '.json' or '.yaml' file.[/bold red]")
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
dir_path = os.path.dirname(output)
|
||||
|
||||
if(dir_path != '' and not os.path.exists(dir_path)):
|
||||
print(f"[bold red]ERROR: {output} path not exists.[/bold red]")
|
||||
raise typer.Exit(code=1)
|
||||
|
||||
path = asyncio.run(core.save_snapshot_with_postfix('snapshot', output, not full_snapshot))
|
||||
print(f"Current snapshot is saved as `{path}`")
|
||||
|
||||
|
||||
@app.command("restore-snapshot", help="Restore snapshot from snapshot file")
|
||||
def restore_snapshot(
|
||||
snapshot_name: str,
|
||||
snapshot_name: str,
|
||||
pip_non_url: Optional[bool] = typer.Option(
|
||||
default=None,
|
||||
show_default=False,
|
||||
@@ -1044,10 +1086,17 @@ def restore_snapshot(
|
||||
user_directory: str = typer.Option(
|
||||
None,
|
||||
help="user directory"
|
||||
),
|
||||
restore_to: Optional[str] = typer.Option(
|
||||
None,
|
||||
help="Manually specify the installation path for the custom node. Ignore user directory."
|
||||
)
|
||||
):
|
||||
cmd_ctx.set_user_directory(user_directory)
|
||||
|
||||
if restore_to:
|
||||
cmd_ctx.update_custom_nodes_dir(restore_to)
|
||||
|
||||
extras = []
|
||||
if pip_non_url:
|
||||
extras.append('--pip-non-url')
|
||||
@@ -1068,7 +1117,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())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
try:
|
||||
asyncio.run(core.restore_snapshot(snapshot_path, extras))
|
||||
except Exception:
|
||||
@@ -1100,7 +1149,7 @@ def restore_dependencies(
|
||||
total = len(node_paths)
|
||||
i = 1
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
for x in node_paths:
|
||||
print("----------------------------------------------------------------------------------------------------")
|
||||
print(f"Restoring [{i}/{total}]: {x}")
|
||||
@@ -1119,7 +1168,7 @@ def post_install(
|
||||
):
|
||||
path = os.path.expanduser(path)
|
||||
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
unified_manager.execute_install_script('', path, instant_execution=True)
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
@@ -1163,8 +1212,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())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, context.manager_files_path)
|
||||
for k in json_obj['custom_nodes'].keys():
|
||||
state = core.simple_check_custom_node(k)
|
||||
if state == 'installed':
|
||||
@@ -1221,20 +1269,6 @@ def export_custom_node_ids(
|
||||
print(f"{x['id']}@unknown", file=output_file)
|
||||
|
||||
|
||||
@app.command(
|
||||
"migrate",
|
||||
help="Migrate legacy node system to new node system",
|
||||
)
|
||||
def migrate(
|
||||
user_directory: str = typer.Option(
|
||||
None,
|
||||
help="user directory"
|
||||
)
|
||||
):
|
||||
cmd_ctx.set_user_directory(user_directory)
|
||||
asyncio.run(unified_manager.migrate_unmanaged_nodes())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(app())
|
||||
@@ -112,4 +112,6 @@ def add_on_revision_detected(k, f):
|
||||
variables['cm.on_revision_detected_handler'].append((k, f))
|
||||
|
||||
|
||||
error_dict = {}
|
||||
error_dict = {}
|
||||
|
||||
disable_front = False
|
||||
@@ -1,12 +1,16 @@
|
||||
import requests
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
import manager_util
|
||||
import toml
|
||||
import os
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
from . import context
|
||||
from . import manager_util
|
||||
|
||||
import requests
|
||||
import toml
|
||||
|
||||
base_url = "https://api.comfy.org"
|
||||
|
||||
@@ -32,9 +36,43 @@ async def _get_cnr_data(cache_mode=True, dont_wait=True):
|
||||
page = 1
|
||||
|
||||
full_nodes = {}
|
||||
|
||||
|
||||
# Determine form factor based on environment and platform
|
||||
is_desktop = bool(os.environ.get('__COMFYUI_DESKTOP_VERSION__'))
|
||||
system = platform.system().lower()
|
||||
is_windows = system == 'windows'
|
||||
is_mac = system == 'darwin'
|
||||
is_linux = system == 'linux'
|
||||
|
||||
# Get ComfyUI version tag
|
||||
if is_desktop:
|
||||
# extract version from pyproject.toml instead of git tag
|
||||
comfyui_ver = context.get_current_comfyui_ver() or 'unknown'
|
||||
else:
|
||||
comfyui_ver = context.get_comfyui_tag() or 'unknown'
|
||||
|
||||
if is_desktop:
|
||||
if is_windows:
|
||||
form_factor = 'desktop-win'
|
||||
elif is_mac:
|
||||
form_factor = 'desktop-mac'
|
||||
else:
|
||||
form_factor = 'other'
|
||||
else:
|
||||
if is_windows:
|
||||
form_factor = 'git-windows'
|
||||
elif is_mac:
|
||||
form_factor = 'git-mac'
|
||||
elif is_linux:
|
||||
form_factor = 'git-linux'
|
||||
else:
|
||||
form_factor = 'other'
|
||||
|
||||
while remained:
|
||||
sub_uri = f'{base_url}/nodes?page={page}&limit=30'
|
||||
sub_json_obj = await asyncio.wait_for(manager_util.get_data_with_cache(sub_uri, cache_mode=False, silent=True), timeout=30)
|
||||
# Add comfyui_version and form_factor to the API request
|
||||
sub_uri = f'{base_url}/nodes?page={page}&limit=30&comfyui_version={comfyui_ver}&form_factor={form_factor}'
|
||||
sub_json_obj = await asyncio.wait_for(manager_util.get_data_with_cache(sub_uri, cache_mode=False, silent=True, dont_cache=True), timeout=30)
|
||||
remained = page < sub_json_obj['totalPages']
|
||||
|
||||
for x in sub_json_obj['nodes']:
|
||||
@@ -173,7 +211,10 @@ def read_cnr_info(fullpath):
|
||||
|
||||
project = data.get('project', {})
|
||||
name = project.get('name').strip().lower()
|
||||
version = project.get('version')
|
||||
|
||||
# normalize version
|
||||
# for example: 2.5 -> 2.5.0
|
||||
version = str(manager_util.StrictVersion(project.get('version')))
|
||||
|
||||
urls = project.get('urls', {})
|
||||
repository = urls.get('Repository')
|
||||
109
comfyui_manager/common/context.py
Normal file
109
comfyui_manager/common/context.py
Normal file
@@ -0,0 +1,109 @@
|
||||
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
|
||||
|
||||
|
||||
17
comfyui_manager/common/enums.py
Normal file
17
comfyui_manager/common/enums.py
Normal file
@@ -0,0 +1,17 @@
|
||||
import enum
|
||||
|
||||
class NetworkMode(enum.Enum):
|
||||
PUBLIC = "public"
|
||||
PRIVATE = "private"
|
||||
OFFLINE = "offline"
|
||||
|
||||
class SecurityLevel(enum.Enum):
|
||||
STRONG = "strong"
|
||||
NORMAL = "normal"
|
||||
NORMAL_MINUS = "normal-minus"
|
||||
WEAK = "weak"
|
||||
|
||||
class DBMode(enum.Enum):
|
||||
LOCAL = "local"
|
||||
CACHE = "cache"
|
||||
REMOTE = "remote"
|
||||
@@ -15,9 +15,12 @@ comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
git_exe_path = os.environ.get('GIT_EXE_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
print("\nWARN: The `COMFYUI_PATH` environment variable is not set. Assuming `custom_nodes/ComfyUI-Manager/../../` as the ComfyUI path.", file=sys.stderr)
|
||||
comfy_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
print("git_helper: environment variable 'COMFYUI_PATH' is not specified.")
|
||||
exit(-1)
|
||||
|
||||
if not os.path.exists(os.path.join(comfy_path, 'folder_paths.py')):
|
||||
print("git_helper: '{comfy_path}' is not a valid 'COMFYUI_PATH' location.")
|
||||
exit(-1)
|
||||
|
||||
def download_url(url, dest_folder, filename=None):
|
||||
# Ensure the destination folder exists
|
||||
@@ -154,14 +157,27 @@ def switch_to_default_branch(repo):
|
||||
repo.git.checkout(default_branch)
|
||||
return True
|
||||
except:
|
||||
# try checkout master
|
||||
# try checkout main if failed
|
||||
try:
|
||||
repo.git.checkout(repo.heads.master)
|
||||
return True
|
||||
except:
|
||||
try:
|
||||
if remote_name is not None:
|
||||
repo.git.checkout('-b', 'master', f'{remote_name}/master')
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
repo.git.checkout(repo.heads.main)
|
||||
return True
|
||||
except:
|
||||
try:
|
||||
if remote_name is not None:
|
||||
repo.git.checkout('-b', 'main', f'{remote_name}/main')
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
print("[ComfyUI Manager] Failed to switch to the default branch")
|
||||
return False
|
||||
@@ -389,12 +405,13 @@ def apply_snapshot(path):
|
||||
git_custom_node_infos = info['git_custom_nodes']
|
||||
file_custom_node_infos = info['file_custom_nodes']
|
||||
|
||||
checkout_comfyui_hash(comfyui_hash)
|
||||
if comfyui_hash:
|
||||
checkout_comfyui_hash(comfyui_hash)
|
||||
checkout_custom_node_hash(git_custom_node_infos)
|
||||
invalidate_custom_node_file(file_custom_node_infos)
|
||||
|
||||
print("APPLY SNAPSHOT: True")
|
||||
if 'pips' in info:
|
||||
if 'pips' in info and info['pips']:
|
||||
return info['pips']
|
||||
else:
|
||||
return None
|
||||
@@ -2,6 +2,9 @@ import os
|
||||
import configparser
|
||||
|
||||
|
||||
GITHUB_ENDPOINT = os.getenv('GITHUB_ENDPOINT')
|
||||
|
||||
|
||||
def is_git_repo(path: str) -> bool:
|
||||
""" Check if the path is a git repository. """
|
||||
# NOTE: Checking it through `git.Repo` must be avoided.
|
||||
@@ -37,7 +40,8 @@ def git_url(fullpath):
|
||||
if not os.path.exists(git_config_path):
|
||||
return None
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
# Set `strict=False` to allow duplicate `vscode-merge-base` sections, addressing <https://github.com/ltdrdata/ComfyUI-Manager/issues/1529>
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read(git_config_path)
|
||||
|
||||
for k, v in config.items():
|
||||
@@ -46,16 +50,36 @@ def git_url(fullpath):
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def normalize_url(url) -> str:
|
||||
url = url.replace("git@github.com:", "https://github.com/")
|
||||
if url.endswith('.git'):
|
||||
url = url[:-4]
|
||||
github_id = normalize_to_github_id(url)
|
||||
if github_id is not None:
|
||||
url = f"https://github.com/{github_id}"
|
||||
|
||||
return url
|
||||
|
||||
def normalize_url_http(url) -> str:
|
||||
url = url.replace("https://github.com/", "git@github.com:")
|
||||
if url.endswith('.git'):
|
||||
url = url[:-4]
|
||||
|
||||
return url
|
||||
def normalize_to_github_id(url) -> str:
|
||||
if 'github' in url or (GITHUB_ENDPOINT is not None and GITHUB_ENDPOINT in url):
|
||||
author = os.path.basename(os.path.dirname(url))
|
||||
|
||||
if author.startswith('git@github.com:'):
|
||||
author = author.split(':')[1]
|
||||
|
||||
repo_name = os.path.basename(url)
|
||||
if repo_name.endswith('.git'):
|
||||
repo_name = repo_name[:-4]
|
||||
|
||||
return f"{author}/{repo_name}"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_url_for_clone(url):
|
||||
url = normalize_url(url)
|
||||
|
||||
if GITHUB_ENDPOINT is not None and url.startswith('https://github.com/'):
|
||||
url = GITHUB_ENDPOINT + url[18:] # url[18:] -> remove `https://github.com`
|
||||
|
||||
return url
|
||||
|
||||
@@ -3,9 +3,15 @@ from urllib.parse import urlparse
|
||||
import urllib
|
||||
import sys
|
||||
import logging
|
||||
import requests
|
||||
from huggingface_hub import HfApi
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
|
||||
aria2 = os.getenv('COMFYUI_MANAGER_ARIA2_SERVER')
|
||||
HF_ENDPOINT = os.getenv('HF_ENDPOINT')
|
||||
|
||||
|
||||
if aria2 is not None:
|
||||
secret = os.getenv('COMFYUI_MANAGER_ARIA2_SECRET')
|
||||
url = urlparse(aria2)
|
||||
@@ -117,3 +123,37 @@ def download_url_with_agent(url, save_path):
|
||||
|
||||
print("Installation was successful.")
|
||||
return True
|
||||
|
||||
# NOTE: snapshot_download doesn't provide file size tqdm.
|
||||
def download_repo_in_bytes(repo_id, local_dir):
|
||||
api = HfApi()
|
||||
repo_info = api.repo_info(repo_id=repo_id, files_metadata=True)
|
||||
|
||||
os.makedirs(local_dir, exist_ok=True)
|
||||
|
||||
total_size = 0
|
||||
for file_info in repo_info.siblings:
|
||||
if file_info.size is not None:
|
||||
total_size += file_info.size
|
||||
|
||||
pbar = tqdm(total=total_size, unit="B", unit_scale=True, desc="Downloading")
|
||||
|
||||
for file_info in repo_info.siblings:
|
||||
out_path = os.path.join(local_dir, file_info.rfilename)
|
||||
os.makedirs(os.path.dirname(out_path), exist_ok=True)
|
||||
|
||||
if file_info.size is None:
|
||||
continue
|
||||
|
||||
download_url = f"https://huggingface.co/{repo_id}/resolve/main/{file_info.rfilename}"
|
||||
|
||||
with requests.get(download_url, stream=True) as r, open(out_path, "wb") as f:
|
||||
r.raise_for_status()
|
||||
for chunk in r.iter_content(chunk_size=65536):
|
||||
if chunk:
|
||||
f.write(chunk)
|
||||
pbar.update(len(chunk))
|
||||
|
||||
pbar.close()
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
description:
|
||||
`manager_util` is the lightest module shared across the prestartup_script, main code, and cm-cli of ComfyUI-Manager.
|
||||
"""
|
||||
import traceback
|
||||
|
||||
import aiohttp
|
||||
import json
|
||||
@@ -12,21 +13,42 @@ import subprocess
|
||||
import sys
|
||||
import re
|
||||
import logging
|
||||
import platform
|
||||
import shlex
|
||||
|
||||
|
||||
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 make_pip_cmd(cmd):
|
||||
if use_uv:
|
||||
return [sys.executable, '-m', 'uv', 'pip'] + cmd
|
||||
else:
|
||||
return [sys.executable, '-m', 'pip'] + cmd
|
||||
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":
|
||||
sep = ':'
|
||||
else:
|
||||
sep = ';'
|
||||
|
||||
os.environ['PATH'] = os.path.dirname(sys.executable)+sep+os.environ['PATH']
|
||||
|
||||
|
||||
def make_pip_cmd(cmd):
|
||||
if 'python_embeded' in sys.executable:
|
||||
if use_uv:
|
||||
return [sys.executable, '-s', '-m', 'uv', 'pip'] + cmd
|
||||
else:
|
||||
return [sys.executable, '-s', '-m', 'pip'] + cmd
|
||||
else:
|
||||
# FIXED: https://github.com/ltdrdata/ComfyUI-Manager/issues/1667
|
||||
if use_uv:
|
||||
return [sys.executable, '-m', 'uv', 'pip'] + cmd
|
||||
else:
|
||||
return [sys.executable, '-m', 'pip'] + cmd
|
||||
|
||||
# DON'T USE StrictVersion - cannot handle pre_release version
|
||||
# try:
|
||||
@@ -130,7 +152,12 @@ async def get_data(uri, silent=False):
|
||||
with open(uri, "r", encoding="utf-8") as f:
|
||||
json_text = f.read()
|
||||
|
||||
json_obj = json.loads(json_text)
|
||||
try:
|
||||
json_obj = json.loads(json_text)
|
||||
except Exception as e:
|
||||
logging.error(f"[ComfyUI-Manager] An error occurred while fetching '{uri}': {e}")
|
||||
|
||||
return {}
|
||||
|
||||
if not silent:
|
||||
print(" [DONE]")
|
||||
@@ -164,7 +191,7 @@ def save_to_cache(uri, json_obj, silent=False):
|
||||
logging.info(f"[ComfyUI-Manager] default cache updated: {uri}")
|
||||
|
||||
|
||||
async def get_data_with_cache(uri, silent=False, cache_mode=True, dont_wait=False):
|
||||
async def get_data_with_cache(uri, silent=False, cache_mode=True, dont_wait=False, dont_cache=False):
|
||||
cache_uri = get_cache_path(uri)
|
||||
|
||||
if cache_mode and dont_wait:
|
||||
@@ -183,11 +210,12 @@ async def get_data_with_cache(uri, silent=False, cache_mode=True, dont_wait=Fals
|
||||
json_obj = await get_data(cache_uri, silent=silent)
|
||||
else:
|
||||
json_obj = await get_data(uri, silent=silent)
|
||||
with cache_lock:
|
||||
with open(cache_uri, "w", encoding='utf-8') as file:
|
||||
json.dump(json_obj, file, indent=4, sort_keys=True)
|
||||
if not silent:
|
||||
logging.info(f"[ComfyUI-Manager] default cache updated: {uri}")
|
||||
if not dont_cache:
|
||||
with cache_lock:
|
||||
with open(cache_uri, "w", encoding='utf-8') as file:
|
||||
json.dump(json_obj, file, indent=4, sort_keys=True)
|
||||
if not silent:
|
||||
logging.info(f"[ComfyUI-Manager] default cache updated: {uri}")
|
||||
|
||||
return json_obj
|
||||
|
||||
@@ -227,7 +255,8 @@ def get_installed_packages(renew=False):
|
||||
if y[0] == 'Package' or y[0].startswith('-'):
|
||||
continue
|
||||
|
||||
pip_map[y[0]] = y[1]
|
||||
normalized_name = y[0].lower().replace('-', '_')
|
||||
pip_map[normalized_name] = y[1]
|
||||
except subprocess.CalledProcessError:
|
||||
logging.error("[ComfyUI-Manager] Failed to retrieve the information of installed pip packages.")
|
||||
return set()
|
||||
@@ -240,6 +269,46 @@ def clear_pip_cache():
|
||||
pip_map = None
|
||||
|
||||
|
||||
def parse_requirement_line(line):
|
||||
tokens = shlex.split(line)
|
||||
if not tokens:
|
||||
return None
|
||||
|
||||
package_spec = tokens[0]
|
||||
|
||||
pattern = re.compile(
|
||||
r'^(?P<package>[A-Za-z0-9_.+-]+)'
|
||||
r'(?P<operator>==|>=|<=|!=|~=|>|<)?'
|
||||
r'(?P<version>[A-Za-z0-9_.+-]*)$'
|
||||
)
|
||||
m = pattern.match(package_spec)
|
||||
if not m:
|
||||
return None
|
||||
|
||||
package = m.group('package')
|
||||
operator = m.group('operator') or None
|
||||
version = m.group('version') or None
|
||||
|
||||
index_url = None
|
||||
if '--index-url' in tokens:
|
||||
idx = tokens.index('--index-url')
|
||||
if idx + 1 < len(tokens):
|
||||
index_url = tokens[idx + 1]
|
||||
|
||||
res = {'package': package}
|
||||
|
||||
if operator is not None:
|
||||
res['operator'] = operator
|
||||
|
||||
if version is not None:
|
||||
res['version'] = StrictVersion(version)
|
||||
|
||||
if index_url is not None:
|
||||
res['index_url'] = index_url
|
||||
|
||||
return res
|
||||
|
||||
|
||||
torch_torchvision_torchaudio_version_map = {
|
||||
'2.6.0': ('0.21.0', '2.6.0'),
|
||||
'2.5.1': ('0.20.0', '2.5.0'),
|
||||
@@ -259,9 +328,12 @@ torch_torchvision_torchaudio_version_map = {
|
||||
}
|
||||
|
||||
|
||||
|
||||
class PIPFixer:
|
||||
def __init__(self, prev_pip_versions):
|
||||
def __init__(self, prev_pip_versions, comfyui_path, manager_files_path):
|
||||
self.prev_pip_versions = { **prev_pip_versions }
|
||||
self.comfyui_path = comfyui_path
|
||||
self.manager_files_path = manager_files_path
|
||||
|
||||
def torch_rollback(self):
|
||||
spec = self.prev_pip_versions['torch'].split('+')
|
||||
@@ -341,7 +413,7 @@ class PIPFixer:
|
||||
|
||||
if len(targets) > 0:
|
||||
for x in targets:
|
||||
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}"])
|
||||
cmd = make_pip_cmd(['install', f"{x}=={versions[0].version_string}", "numpy<2"])
|
||||
subprocess.check_output(cmd, universal_newlines=True)
|
||||
|
||||
logging.info(f"[ComfyUI-Manager] 'opencv' dependencies were fixed: {targets}")
|
||||
@@ -356,10 +428,83 @@ class PIPFixer:
|
||||
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)
|
||||
|
||||
# fix missing frontend
|
||||
try:
|
||||
# NOTE: package name in requirements is 'comfyui-frontend-package'
|
||||
# but, package name from `pip freeze` is 'comfyui_frontend_package'
|
||||
# but, package name from `uv pip freeze` is 'comfyui-frontend-package'
|
||||
#
|
||||
# get_installed_packages returns normalized name (i.e. comfyui_frontend_package)
|
||||
if 'comfyui_frontend_package' not in new_pip_versions:
|
||||
requirements_path = os.path.join(self.comfyui_path, 'requirements.txt')
|
||||
|
||||
with open(requirements_path, 'r') as file:
|
||||
lines = file.readlines()
|
||||
|
||||
front_line = next((line.strip() for line in lines if line.startswith('comfyui-frontend-package')), None)
|
||||
if front_line is None:
|
||||
logging.info("[ComfyUI-Manager] Skipped fixing the 'comfyui-frontend-package' dependency because the ComfyUI is outdated.")
|
||||
else:
|
||||
cmd = make_pip_cmd(['install', front_line])
|
||||
subprocess.check_output(cmd , universal_newlines=True)
|
||||
logging.info("[ComfyUI-Manager] 'comfyui-frontend-package' dependency were fixed")
|
||||
except Exception as e:
|
||||
logging.error("[ComfyUI-Manager] Failed to restore comfyui-frontend-package")
|
||||
logging.error(e)
|
||||
|
||||
# restore based on custom list
|
||||
pip_auto_fix_path = os.path.join(self.manager_files_path, "pip_auto_fix.list")
|
||||
if os.path.exists(pip_auto_fix_path):
|
||||
with open(pip_auto_fix_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||
fixed_list = []
|
||||
|
||||
for x in f.readlines():
|
||||
try:
|
||||
parsed = parse_requirement_line(x)
|
||||
need_to_reinstall = True
|
||||
|
||||
normalized_name = parsed['package'].lower().replace('-', '_')
|
||||
if normalized_name in new_pip_versions:
|
||||
if 'version' in parsed and 'operator' in parsed:
|
||||
cur = StrictVersion(new_pip_versions[parsed['package']])
|
||||
dest = parsed['version']
|
||||
op = parsed['operator']
|
||||
if cur == dest:
|
||||
if op in ['==', '>=', '<=']:
|
||||
need_to_reinstall = False
|
||||
elif cur < dest:
|
||||
if op in ['<=', '<', '~=', '!=']:
|
||||
need_to_reinstall = False
|
||||
elif cur > dest:
|
||||
if op in ['>=', '>', '~=', '!=']:
|
||||
need_to_reinstall = False
|
||||
|
||||
if need_to_reinstall:
|
||||
cmd_args = ['install']
|
||||
if 'version' in parsed and 'operator' in parsed:
|
||||
cmd_args.append(parsed['package']+parsed['operator']+parsed['version'].version_string)
|
||||
|
||||
if 'index_url' in parsed:
|
||||
cmd_args.append('--index-url')
|
||||
cmd_args.append(parsed['index_url'])
|
||||
|
||||
cmd = make_pip_cmd(cmd_args)
|
||||
subprocess.check_output(cmd, universal_newlines=True)
|
||||
|
||||
fixed_list.append(parsed['package'])
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
logging.error(f"[ComfyUI-Manager] Failed to restore '{x}'")
|
||||
logging.error(e)
|
||||
|
||||
if len(fixed_list) > 0:
|
||||
logging.info(f"[ComfyUI-Manager] dependencies in pip_auto_fix.json were fixed: {fixed_list}")
|
||||
|
||||
def sanitize(data):
|
||||
return data.replace("<", "<").replace(">", ">")
|
||||
@@ -368,3 +513,23 @@ def sanitize(data):
|
||||
def sanitize_filename(input_string):
|
||||
result_string = re.sub(r'[^a-zA-Z0-9_]', '_', input_string)
|
||||
return result_string
|
||||
|
||||
|
||||
def robust_readlines(fullpath):
|
||||
import chardet
|
||||
try:
|
||||
with open(fullpath, "r") as f:
|
||||
return f.readlines()
|
||||
except:
|
||||
encoding = None
|
||||
with open(fullpath, "rb") as f:
|
||||
raw_data = f.read()
|
||||
result = chardet.detect(raw_data)
|
||||
encoding = result['encoding']
|
||||
|
||||
if encoding is not None:
|
||||
with open(fullpath, "r", encoding=encoding) as f:
|
||||
return f.readlines()
|
||||
|
||||
print(f"[ComfyUI-Manager] Failed to recognize encoding for: {fullpath}")
|
||||
return []
|
||||
@@ -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
|
||||
0
comfyui_manager/glob/__init__.py
Normal file
0
comfyui_manager/glob/__init__.py
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,7 @@
|
||||
import mimetypes
|
||||
import manager_core as core
|
||||
from ..common import context
|
||||
from . import manager_core as core
|
||||
|
||||
import os
|
||||
from aiohttp import web
|
||||
import aiohttp
|
||||
@@ -53,7 +55,7 @@ def compute_sha256_checksum(filepath):
|
||||
return sha256.hexdigest()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/share_option")
|
||||
@PromptServer.instance.routes.get("/v2/manager/share_option")
|
||||
async def share_option(request):
|
||||
if "value" in request.rel_url.query:
|
||||
core.get_config()['share_option'] = request.rel_url.query['value']
|
||||
@@ -65,10 +67,10 @@ async def share_option(request):
|
||||
|
||||
|
||||
def get_openart_auth():
|
||||
if not os.path.exists(os.path.join(core.manager_files_path, ".openart_key")):
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, ".openart_key")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.manager_files_path, ".openart_key"), "r") as f:
|
||||
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:
|
||||
@@ -76,10 +78,10 @@ def get_openart_auth():
|
||||
|
||||
|
||||
def get_matrix_auth():
|
||||
if not os.path.exists(os.path.join(core.manager_files_path, "matrix_auth")):
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, "matrix_auth")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.manager_files_path, "matrix_auth"), "r") as f:
|
||||
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:
|
||||
@@ -94,10 +96,10 @@ def get_matrix_auth():
|
||||
|
||||
|
||||
def get_comfyworkflows_auth():
|
||||
if not os.path.exists(os.path.join(core.manager_files_path, "comfyworkflows_sharekey")):
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, "comfyworkflows_sharekey")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.manager_files_path, "comfyworkflows_sharekey"), "r") as f:
|
||||
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
|
||||
@@ -107,10 +109,10 @@ def get_comfyworkflows_auth():
|
||||
|
||||
|
||||
def get_youml_settings():
|
||||
if not os.path.exists(os.path.join(core.manager_files_path, ".youml")):
|
||||
if not os.path.exists(os.path.join(context.manager_files_path, ".youml")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(core.manager_files_path, ".youml"), "r") as f:
|
||||
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:
|
||||
@@ -118,11 +120,11 @@ def get_youml_settings():
|
||||
|
||||
|
||||
def set_youml_settings(settings):
|
||||
with open(os.path.join(core.manager_files_path, ".youml"), "w") as f:
|
||||
with open(os.path.join(context.manager_files_path, ".youml"), "w") as f:
|
||||
f.write(settings)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_openart_auth")
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_openart_auth")
|
||||
async def api_get_openart_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
openart_key = get_openart_auth()
|
||||
@@ -131,16 +133,16 @@ async def api_get_openart_auth(request):
|
||||
return web.json_response({"openart_key": openart_key})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/set_openart_auth")
|
||||
@PromptServer.instance.routes.post("/v2/manager/set_openart_auth")
|
||||
async def api_set_openart_auth(request):
|
||||
json_data = await request.json()
|
||||
openart_key = json_data['openart_key']
|
||||
with open(os.path.join(core.manager_files_path, ".openart_key"), "w") as f:
|
||||
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("/manager/get_matrix_auth")
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_matrix_auth")
|
||||
async def api_get_matrix_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
matrix_auth = get_matrix_auth()
|
||||
@@ -149,7 +151,7 @@ async def api_get_matrix_auth(request):
|
||||
return web.json_response(matrix_auth)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/youml/settings")
|
||||
@PromptServer.instance.routes.get("/v2/manager/youml/settings")
|
||||
async def api_get_youml_settings(request):
|
||||
youml_settings = get_youml_settings()
|
||||
if not youml_settings:
|
||||
@@ -157,14 +159,14 @@ async def api_get_youml_settings(request):
|
||||
return web.json_response(json.loads(youml_settings))
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/youml/settings")
|
||||
@PromptServer.instance.routes.post("/v2/manager/youml/settings")
|
||||
async def api_set_youml_settings(request):
|
||||
json_data = await request.json()
|
||||
set_youml_settings(json.dumps(json_data))
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@PromptServer.instance.routes.get("/manager/get_comfyworkflows_auth")
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_comfyworkflows_auth")
|
||||
async def api_get_comfyworkflows_auth(request):
|
||||
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
|
||||
# in the same directory as the ComfyUI base folder
|
||||
@@ -175,17 +177,17 @@ async def api_get_comfyworkflows_auth(request):
|
||||
return web.json_response({"comfyworkflows_sharekey": comfyworkflows_auth})
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/set_esheep_workflow_and_images")
|
||||
@PromptServer.instance.routes.post("/v2/manager/set_esheep_workflow_and_images")
|
||||
async def set_esheep_workflow_and_images(request):
|
||||
json_data = await request.json()
|
||||
with open(os.path.join(core.manager_files_path, "esheep_share_message.json"), "w", encoding='utf-8') as file:
|
||||
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("/manager/get_esheep_workflow_and_images")
|
||||
@PromptServer.instance.routes.get("/v2/manager/get_esheep_workflow_and_images")
|
||||
async def get_esheep_workflow_and_images(request):
|
||||
with open(os.path.join(core.manager_files_path, "esheep_share_message.json"), 'r', encoding='utf-8') as file:
|
||||
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))
|
||||
|
||||
@@ -194,12 +196,12 @@ def set_matrix_auth(json_data):
|
||||
homeserver = json_data['homeserver']
|
||||
username = json_data['username']
|
||||
password = json_data['password']
|
||||
with open(os.path.join(core.manager_files_path, "matrix_auth"), "w") as f:
|
||||
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(core.manager_files_path, "comfyworkflows_sharekey"), "w") as f:
|
||||
with open(os.path.join(context.manager_files_path, "comfyworkflows_sharekey"), "w") as f:
|
||||
f.write(comfyworkflows_sharekey)
|
||||
|
||||
|
||||
@@ -211,7 +213,7 @@ def has_provided_comfyworkflows_auth(comfyworkflows_sharekey):
|
||||
return comfyworkflows_sharekey.strip()
|
||||
|
||||
|
||||
@PromptServer.instance.routes.post("/manager/share")
|
||||
@PromptServer.instance.routes.post("/v2/manager/share")
|
||||
async def share_art(request):
|
||||
# get json data
|
||||
json_data = await request.json()
|
||||
@@ -25,7 +25,7 @@ async function tryInstallCustomNode(event) {
|
||||
const res = await customConfirm(msg);
|
||||
if(res) {
|
||||
if(event.detail.target.installed == 'Disabled') {
|
||||
const response = await api.fetchApi(`/customnode/toggle_active`, {
|
||||
const response = await api.fetchApi(`/v2/customnode/toggle_active`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(event.detail.target)
|
||||
@@ -35,7 +35,7 @@ async function tryInstallCustomNode(event) {
|
||||
await sleep(300);
|
||||
app.ui.dialog.show(`Installing... '${event.detail.target.title}'`);
|
||||
|
||||
const response = await api.fetchApi(`/customnode/install`, {
|
||||
const response = await api.fetchApi(`/v2/customnode/install`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(event.detail.target)
|
||||
@@ -52,7 +52,7 @@ async function tryInstallCustomNode(event) {
|
||||
}
|
||||
}
|
||||
|
||||
let response = await api.fetchApi("/manager/reboot");
|
||||
let response = await api.fetchApi("/v2/manager/reboot");
|
||||
if(response.status == 403) {
|
||||
show_message('This action is not allowed with this security level configuration.');
|
||||
return false;
|
||||
@@ -13,12 +13,16 @@ import {
|
||||
import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
||||
import {
|
||||
free_models, install_pip, install_via_git_url, manager_instance,
|
||||
rebootAPI, migrateAPI, setManagerInstance, show_message, customAlert, customPrompt } from "./common.js";
|
||||
import { ComponentBuilderDialog, getPureName, load_components, set_component_policy } from "./components-manager.js";
|
||||
rebootAPI, setManagerInstance, show_message, customAlert, customPrompt,
|
||||
infoToast, showTerminal, setNeedRestart, generateUUID
|
||||
} from "./common.js";
|
||||
import { ComponentBuilderDialog, 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";
|
||||
|
||||
let manager_version = await getVersion();
|
||||
|
||||
var docStyle = document.createElement('style');
|
||||
docStyle.innerHTML = `
|
||||
.comfy-toast {
|
||||
@@ -40,7 +44,7 @@ docStyle.innerHTML = `
|
||||
|
||||
#cm-manager-dialog {
|
||||
width: 1000px;
|
||||
height: 450px;
|
||||
height: 455px;
|
||||
box-sizing: content-box;
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
@@ -137,7 +141,7 @@ docStyle.innerHTML = `
|
||||
|
||||
.cm-notice-board {
|
||||
width: 290px;
|
||||
height: 210px;
|
||||
height: 230px;
|
||||
overflow: auto;
|
||||
color: var(--input-text);
|
||||
border: 1px solid var(--descrip-text);
|
||||
@@ -185,8 +189,7 @@ docStyle.innerHTML = `
|
||||
}
|
||||
`;
|
||||
|
||||
function is_legacy_front() {
|
||||
let compareVersion = '1.2.49';
|
||||
function isBeforeFrontendVersion(compareVersion) {
|
||||
try {
|
||||
const frontendVersion = window['__COMFYUI_FRONTEND_VERSION__'];
|
||||
if (typeof frontendVersion !== 'string') {
|
||||
@@ -219,13 +222,20 @@ function is_legacy_front() {
|
||||
}
|
||||
}
|
||||
|
||||
const is_legacy_front = () => isBeforeFrontendVersion('1.2.49');
|
||||
const isNotNewManagerUI = () => isBeforeFrontendVersion('1.16.4');
|
||||
|
||||
document.head.appendChild(docStyle);
|
||||
|
||||
var update_comfyui_button = null;
|
||||
var switch_comfyui_button = null;
|
||||
var fetch_updates_button = null;
|
||||
var update_all_button = null;
|
||||
var restart_stop_button = null;
|
||||
var update_policy_combo = null;
|
||||
|
||||
let share_option = 'all';
|
||||
var batch_id = null;
|
||||
|
||||
|
||||
// copied style from https://github.com/pythongosssss/ComfyUI-Custom-Scripts
|
||||
const style = `
|
||||
@@ -407,7 +417,7 @@ const style = `
|
||||
`;
|
||||
|
||||
async function init_share_option() {
|
||||
api.fetchApi('/manager/share_option')
|
||||
api.fetchApi('/v2/manager/share_option')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
share_option = data || 'all';
|
||||
@@ -415,7 +425,7 @@ async function init_share_option() {
|
||||
}
|
||||
|
||||
async function init_notice(notice) {
|
||||
api.fetchApi('/manager/notice')
|
||||
api.fetchApi('/v2/manager/notice')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
notice.innerHTML = data;
|
||||
@@ -424,102 +434,61 @@ async function init_notice(notice) {
|
||||
|
||||
await init_share_option();
|
||||
|
||||
async function fetchNicknames() {
|
||||
const response1 = await api.fetchApi(`/customnode/getmappings?mode=nickname`);
|
||||
const mappings = await response1.json();
|
||||
|
||||
let result = {};
|
||||
let nickname_patterns = [];
|
||||
async function set_inprogress_mode() {
|
||||
update_comfyui_button.disabled = true;
|
||||
update_comfyui_button.style.backgroundColor = "gray";
|
||||
|
||||
for (let i in mappings) {
|
||||
let item = mappings[i];
|
||||
var nickname;
|
||||
if (item[1].nickname) {
|
||||
nickname = item[1].nickname;
|
||||
}
|
||||
else if (item[1].title) {
|
||||
nickname = item[1].title;
|
||||
}
|
||||
else {
|
||||
nickname = item[1].title_aux;
|
||||
}
|
||||
update_all_button.disabled = true;
|
||||
update_all_button.style.backgroundColor = "gray";
|
||||
|
||||
for (let j in item[0]) {
|
||||
result[item[0][j]] = nickname;
|
||||
}
|
||||
switch_comfyui_button.disabled = true;
|
||||
switch_comfyui_button.style.backgroundColor = "gray";
|
||||
|
||||
if(item[1].nodename_pattern) {
|
||||
nickname_patterns.push([item[1].nodename_pattern, nickname]);
|
||||
}
|
||||
}
|
||||
|
||||
return [result, nickname_patterns];
|
||||
restart_stop_button.innerText = 'Stop';
|
||||
}
|
||||
|
||||
const [nicknames, nickname_patterns] = await fetchNicknames();
|
||||
|
||||
function getNickname(node, nodename) {
|
||||
if(node.nickname) {
|
||||
return node.nickname;
|
||||
async function reset_action_buttons() {
|
||||
const isElectron = 'electronAPI' in window;
|
||||
|
||||
if(isElectron) {
|
||||
update_all_button.innerText = "Update All Custom Nodes";
|
||||
}
|
||||
else {
|
||||
if (nicknames[nodename]) {
|
||||
node.nickname = nicknames[nodename];
|
||||
}
|
||||
else if(node.getInnerNodes) {
|
||||
let pure_name = getPureName(node);
|
||||
let groupNode = app.graph.extra?.groupNodes?.[pure_name];
|
||||
if(groupNode) {
|
||||
let packname = groupNode.packname;
|
||||
node.nickname = packname;
|
||||
}
|
||||
return node.nickname;
|
||||
}
|
||||
else {
|
||||
for(let i in nickname_patterns) {
|
||||
let item = nickname_patterns[i];
|
||||
if(nodename.match(item[0])) {
|
||||
node.nickname = item[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return node.nickname;
|
||||
update_all_button.innerText = "Update All";
|
||||
}
|
||||
|
||||
update_comfyui_button.innerText = "Update ComfyUI";
|
||||
switch_comfyui_button.innerText = "Switch ComfyUI";
|
||||
restart_stop_button.innerText = 'Restart';
|
||||
|
||||
update_comfyui_button.disabled = false;
|
||||
update_all_button.disabled = false;
|
||||
switch_comfyui_button.disabled = false;
|
||||
|
||||
update_comfyui_button.style.backgroundColor = "";
|
||||
update_all_button.style.backgroundColor = "";
|
||||
switch_comfyui_button.style.backgroundColor = "";
|
||||
}
|
||||
|
||||
async function updateComfyUI() {
|
||||
let prev_text = update_comfyui_button.innerText;
|
||||
update_comfyui_button.innerText = "Updating ComfyUI...";
|
||||
update_comfyui_button.disabled = true;
|
||||
update_comfyui_button.style.backgroundColor = "gray";
|
||||
|
||||
try {
|
||||
const response = await api.fetchApi('/comfyui_manager/update_comfyui');
|
||||
// set_inprogress_mode();
|
||||
showTerminal();
|
||||
|
||||
if (response.status == 400) {
|
||||
show_message('Failed to update ComfyUI.');
|
||||
return false;
|
||||
}
|
||||
batch_id = generateUUID();
|
||||
|
||||
if (response.status == 201) {
|
||||
show_message('ComfyUI has been successfully updated.');
|
||||
}
|
||||
else {
|
||||
show_message('ComfyUI is already up to date with the latest version.');
|
||||
}
|
||||
let batch = {};
|
||||
batch['batch_id'] = batch_id;
|
||||
batch['update_comfyui'] = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (exception) {
|
||||
show_message(`Failed to update ComfyUI / ${exception}`);
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
update_comfyui_button.disabled = false;
|
||||
update_comfyui_button.innerText = prev_text;
|
||||
update_comfyui_button.style.backgroundColor = "";
|
||||
}
|
||||
const res = await api.fetchApi(`/v2/manager/queue/batch`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(batch)
|
||||
});
|
||||
}
|
||||
|
||||
function showVersionSelectorDialog(versions, current, onSelect) {
|
||||
@@ -647,158 +616,184 @@ function showVersionSelectorDialog(versions, current, onSelect) {
|
||||
}
|
||||
|
||||
async function switchComfyUI() {
|
||||
let res = await api.fetchApi(`/comfyui_manager/comfyui_versions`, { cache: "no-store" });
|
||||
switch_comfyui_button.disabled = true;
|
||||
switch_comfyui_button.style.backgroundColor = "gray";
|
||||
|
||||
let res = await api.fetchApi(`/v2/comfyui_manager/comfyui_versions`, { cache: "no-store" });
|
||||
|
||||
if(res.status == 200) {
|
||||
let obj = await res.json();
|
||||
switch_comfyui_button.disabled = false;
|
||||
switch_comfyui_button.style.backgroundColor = "";
|
||||
|
||||
let versions = [];
|
||||
let default_version;
|
||||
if(res.status == 200) {
|
||||
let obj = await res.json();
|
||||
|
||||
for(let v of obj.versions) {
|
||||
default_version = v;
|
||||
versions.push(v);
|
||||
}
|
||||
let versions = [];
|
||||
let default_version;
|
||||
|
||||
showVersionSelectorDialog(versions, obj.current, (selected_version) => {
|
||||
api.fetchApi(`/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
|
||||
});
|
||||
}
|
||||
else {
|
||||
show_message('Failed to fetch ComfyUI versions.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function fetchUpdates(update_check_checkbox) {
|
||||
let prev_text = fetch_updates_button.innerText;
|
||||
fetch_updates_button.innerText = "Fetching updates...";
|
||||
fetch_updates_button.disabled = true;
|
||||
fetch_updates_button.style.backgroundColor = "gray";
|
||||
|
||||
try {
|
||||
var mode = manager_instance.datasrc_combo.value;
|
||||
|
||||
const response = await api.fetchApi(`/customnode/fetch_updates?mode=${mode}`);
|
||||
|
||||
if (response.status != 200 && response.status != 201) {
|
||||
show_message('Failed to fetch updates.');
|
||||
return false;
|
||||
for(let v of obj.versions) {
|
||||
default_version = v;
|
||||
versions.push(v);
|
||||
}
|
||||
|
||||
if (response.status == 201) {
|
||||
show_message("There is an updated extension available.<BR><BR><P><B>NOTE:<BR>Fetch Updates is not an update.<BR>Please update from <button id='cm-install-customnodes-button'>Install Custom Nodes</button> </B></P>");
|
||||
|
||||
const button = document.getElementById('cm-install-customnodes-button');
|
||||
button.addEventListener("click",
|
||||
async function() {
|
||||
app.ui.dialog.close();
|
||||
|
||||
if(!CustomNodesManager.instance) {
|
||||
CustomNodesManager.instance = new CustomNodesManager(app, self);
|
||||
}
|
||||
await CustomNodesManager.instance.show(CustomNodesManager.ShowMode.UPDATE);
|
||||
}
|
||||
);
|
||||
|
||||
update_check_checkbox.checked = false;
|
||||
}
|
||||
else {
|
||||
show_message('All extensions are already up-to-date with the latest versions.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (exception) {
|
||||
show_message(`Failed to update custom nodes / ${exception}`);
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
fetch_updates_button.disabled = false;
|
||||
fetch_updates_button.innerText = prev_text;
|
||||
fetch_updates_button.style.backgroundColor = "";
|
||||
}
|
||||
}
|
||||
|
||||
async function updateAll(update_check_checkbox, manager_dialog) {
|
||||
let prev_text = update_all_button.innerText;
|
||||
update_all_button.innerText = "Updating all...(ComfyUI)";
|
||||
update_all_button.disabled = true;
|
||||
update_all_button.style.backgroundColor = "gray";
|
||||
|
||||
try {
|
||||
var mode = manager_instance.datasrc_combo.value;
|
||||
|
||||
update_all_button.innerText = "Updating all...";
|
||||
const response1 = await api.fetchApi('/comfyui_manager/update_comfyui');
|
||||
const response2 = await api.fetchApi(`/customnode/update_all?mode=${mode}`);
|
||||
|
||||
if (response2.status == 403) {
|
||||
show_message('This action is not allowed with this security level configuration.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response1.status == 400 || response2.status == 400) {
|
||||
show_message('Failed to update ComfyUI or several extensions.<BR><BR>See terminal log.<BR>');
|
||||
return false;
|
||||
}
|
||||
|
||||
if(response1.status == 201 || response2.status == 201) {
|
||||
const update_info = await response2.json();
|
||||
|
||||
let failed_list = "";
|
||||
if(update_info.failed.length > 0) {
|
||||
failed_list = "<BR>FAILED: "+update_info.failed.join(", ");
|
||||
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');
|
||||
}
|
||||
else {
|
||||
update_policy_combo.value = 'stable-comfyui';
|
||||
api.fetchApi('/v2/manager/policy/update?value=stable-comfyui');
|
||||
}
|
||||
|
||||
let updated_list = "";
|
||||
if(update_info.updated.length > 0) {
|
||||
updated_list = "<BR>UPDATED: "+update_info.updated.join(", ");
|
||||
let response = await api.fetchApi(`/v2/comfyui_manager/comfyui_switch_version?ver=${selected_version}`, { cache: "no-store" });
|
||||
if (response.status == 200) {
|
||||
infoToast(`ComfyUI version is switched to ${selected_version}`);
|
||||
}
|
||||
else {
|
||||
customAlert('Failed to switch ComfyUI version.');
|
||||
}
|
||||
|
||||
show_message(
|
||||
"ComfyUI and all extensions have been updated to the latest version.<BR>To apply the updated custom node, please <button class='cm-small-button' id='cm-reboot-button5'>RESTART</button> ComfyUI. And refresh browser.<BR>"
|
||||
+failed_list
|
||||
+updated_list
|
||||
);
|
||||
|
||||
const rebootButton = document.getElementById('cm-reboot-button5');
|
||||
rebootButton.addEventListener("click",
|
||||
function() {
|
||||
if(rebootAPI()) {
|
||||
manager_dialog.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
show_message('ComfyUI and all extensions are already up-to-date with the latest versions.');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (exception) {
|
||||
show_message(`Failed to update ComfyUI or several extensions / ${exception}`);
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
update_all_button.disabled = false;
|
||||
update_all_button.innerText = prev_text;
|
||||
update_all_button.style.backgroundColor = "";
|
||||
}
|
||||
}
|
||||
|
||||
function newDOMTokenList(initialTokens) {
|
||||
const tmp = document.createElement(`div`);
|
||||
|
||||
const classList = tmp.classList;
|
||||
if (initialTokens) {
|
||||
initialTokens.forEach(token => {
|
||||
classList.add(token);
|
||||
});
|
||||
}
|
||||
|
||||
return classList;
|
||||
else {
|
||||
customAlert('Failed to fetch ComfyUI versions.');
|
||||
}
|
||||
}
|
||||
|
||||
async function onQueueStatus(event) {
|
||||
const isElectron = 'electronAPI' in window;
|
||||
|
||||
if(event.detail.status == 'in_progress') {
|
||||
// set_inprogress_mode();
|
||||
update_all_button.innerText = `in progress.. (${event.detail.done_count}/${event.detail.total_count})`;
|
||||
}
|
||||
else if(event.detail.status == 'all-done') {
|
||||
reset_action_buttons();
|
||||
}
|
||||
else if(event.detail.status == 'batch-done') {
|
||||
if(batch_id != event.detail.batch_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
let success_list = [];
|
||||
let failed_list = [];
|
||||
let comfyui_state = null;
|
||||
|
||||
for(let k in event.detail.nodepack_result){
|
||||
let v = event.detail.nodepack_result[k];
|
||||
|
||||
if(k == 'comfyui') {
|
||||
comfyui_state = v;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(v.msg == 'success') {
|
||||
success_list.push(k);
|
||||
}
|
||||
else if(v.msg != 'skip')
|
||||
failed_list.push(k);
|
||||
}
|
||||
|
||||
let msg = "";
|
||||
|
||||
if(success_list.length == 0 && comfyui_state.startsWith('skip')) {
|
||||
if(failed_list.length == 0) {
|
||||
msg += "You are already up to date.";
|
||||
}
|
||||
}
|
||||
else {
|
||||
msg = "To apply the updates, you need to <button class='cm-small-button' id='cm-reboot-button5'>RESTART</button> ComfyUI.<hr>";
|
||||
|
||||
if(comfyui_state == 'success-nightly') {
|
||||
msg += "ComfyUI has been updated to latest nightly version.<BR><BR>";
|
||||
infoToast("ComfyUI has been updated to the latest nightly version.");
|
||||
}
|
||||
else if(comfyui_state.startsWith('success-stable')) {
|
||||
const ver = comfyui_state.split("-").pop();
|
||||
msg += `ComfyUI has been updated to ${ver}.<BR><BR>`;
|
||||
infoToast(`ComfyUI has been updated to ${ver}`);
|
||||
}
|
||||
else if(comfyui_state == 'skip') {
|
||||
msg += "ComfyUI is already up to date.<BR><BR>"
|
||||
}
|
||||
else if(comfyui_state != null) {
|
||||
msg += "Failed to update ComfyUI.<BR><BR>"
|
||||
}
|
||||
|
||||
if(success_list.length > 0) {
|
||||
msg += "The following custom nodes have been updated:<ul>";
|
||||
for(let x in success_list) {
|
||||
let k = success_list[x];
|
||||
let url = event.detail.nodepack_result[k].url;
|
||||
let title = event.detail.nodepack_result[k].title;
|
||||
if(url) {
|
||||
msg += `<li><a href='${url}' target='_blank'>${title}</a></li>`;
|
||||
}
|
||||
else {
|
||||
msg += `<li>${k}</li>`;
|
||||
}
|
||||
}
|
||||
msg += "</ul>";
|
||||
}
|
||||
|
||||
setNeedRestart(true);
|
||||
}
|
||||
|
||||
if(failed_list.length > 0) {
|
||||
msg += '<br>The update for the following custom nodes has failed:<ul>';
|
||||
for(let x in failed_list) {
|
||||
let k = failed_list[x];
|
||||
let url = event.detail.nodepack_result[k].url;
|
||||
let title = event.detail.nodepack_result[k].title;
|
||||
if(url) {
|
||||
msg += `<li><a href='${url}' target='_blank'>${title}</a></li>`;
|
||||
}
|
||||
else {
|
||||
msg += `<li>${k}</li>`;
|
||||
}
|
||||
}
|
||||
|
||||
msg += '</ul>'
|
||||
}
|
||||
|
||||
show_message(msg);
|
||||
|
||||
const rebootButton = document.getElementById('cm-reboot-button5');
|
||||
rebootButton?.addEventListener("click",
|
||||
function() {
|
||||
if(rebootAPI()) {
|
||||
manager_dialog.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
api.addEventListener("cm-queue-status", onQueueStatus);
|
||||
|
||||
|
||||
async function updateAll(update_comfyui) {
|
||||
update_all_button.innerText = "Updating...";
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
batch['update_all'] = mode;
|
||||
|
||||
const res = await api.fetchApi(`/v2/manager/queue/batch`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(batch)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the node is a potential output node (img, gif or video output)
|
||||
@@ -807,12 +802,22 @@ const isOutputNode = (node) => {
|
||||
return SUPPORTED_OUTPUT_NODE_TYPES.includes(node.type);
|
||||
}
|
||||
|
||||
function restartOrStop() {
|
||||
if(restart_stop_button.innerText == 'Restart'){
|
||||
rebootAPI();
|
||||
}
|
||||
else {
|
||||
api.fetchApi('/v2/manager/queue/reset');
|
||||
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||
}
|
||||
}
|
||||
|
||||
// -----------
|
||||
class ManagerMenuDialog extends ComfyDialog {
|
||||
createControlsMid() {
|
||||
let self = this;
|
||||
const isElectron = 'electronAPI' in window;
|
||||
|
||||
const isElectron = 'electronAPI' in window;
|
||||
|
||||
update_comfyui_button =
|
||||
$el("button.cm-button", {
|
||||
type: "button",
|
||||
@@ -835,21 +840,31 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
() => switchComfyUI()
|
||||
});
|
||||
|
||||
fetch_updates_button =
|
||||
$el("button.cm-button", {
|
||||
restart_stop_button =
|
||||
$el("button.cm-button-red", {
|
||||
type: "button",
|
||||
textContent: "Fetch Updates",
|
||||
onclick:
|
||||
() => fetchUpdates(this.update_check_checkbox)
|
||||
textContent: "Restart",
|
||||
onclick: () => restartOrStop()
|
||||
});
|
||||
|
||||
update_all_button =
|
||||
$el("button.cm-button", {
|
||||
type: "button",
|
||||
textContent: "Update All",
|
||||
onclick:
|
||||
() => updateAll(this.update_check_checkbox, self)
|
||||
});
|
||||
if(isElectron) {
|
||||
update_all_button =
|
||||
$el("button.cm-button", {
|
||||
type: "button",
|
||||
textContent: "Update All Custom Nodes",
|
||||
onclick:
|
||||
() => updateAll(false)
|
||||
});
|
||||
}
|
||||
else {
|
||||
update_all_button =
|
||||
$el("button.cm-button", {
|
||||
type: "button",
|
||||
textContent: "Update All",
|
||||
onclick:
|
||||
() => updateAll(true)
|
||||
});
|
||||
}
|
||||
|
||||
const res =
|
||||
[
|
||||
@@ -877,7 +892,19 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
}),
|
||||
|
||||
$el("button.cm-button", {
|
||||
type: "button",
|
||||
textContent: "Custom Nodes In Workflow",
|
||||
onclick:
|
||||
() => {
|
||||
if(!CustomNodesManager.instance) {
|
||||
CustomNodesManager.instance = new CustomNodesManager(app, self);
|
||||
}
|
||||
CustomNodesManager.instance.show(CustomNodesManager.ShowMode.IN_WORKFLOW);
|
||||
}
|
||||
}),
|
||||
|
||||
$el("br", {}, []),
|
||||
$el("button.cm-button", {
|
||||
type: "button",
|
||||
textContent: "Model Manager",
|
||||
@@ -906,49 +933,19 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
update_all_button,
|
||||
update_comfyui_button,
|
||||
switch_comfyui_button,
|
||||
fetch_updates_button,
|
||||
// fetch_updates_button,
|
||||
|
||||
$el("br", {}, []),
|
||||
$el("button.cm-button-red", {
|
||||
type: "button",
|
||||
textContent: "Restart",
|
||||
onclick: () => rebootAPI()
|
||||
}),
|
||||
restart_stop_button,
|
||||
];
|
||||
|
||||
let migration_btn =
|
||||
$el("button.cm-button-orange", {
|
||||
type: "button",
|
||||
textContent: "Migrate to New Node System",
|
||||
onclick: () => migrateAPI()
|
||||
});
|
||||
|
||||
migration_btn.style.display = 'none';
|
||||
|
||||
res.push(migration_btn);
|
||||
|
||||
api.fetchApi('/manager/need_to_migrate')
|
||||
.then(response => response.text())
|
||||
.then(text => {
|
||||
if (text === 'True') {
|
||||
migration_btn.style.display = 'block';
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error checking migration status:', error);
|
||||
});
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
createControlsLeft() {
|
||||
let self = this;
|
||||
const isElectron = 'electronAPI' in window;
|
||||
|
||||
this.update_check_checkbox = $el("input",{type:'checkbox', id:"skip_update_check"},[])
|
||||
const uc_checkbox_text = $el("label",{for:"skip_update_check"},[" Skip update check"])
|
||||
uc_checkbox_text.style.color = "var(--fg-color)";
|
||||
uc_checkbox_text.style.cursor = "pointer";
|
||||
this.update_check_checkbox.checked = true;
|
||||
let self = this;
|
||||
|
||||
// db mode
|
||||
this.datasrc_combo = document.createElement("select");
|
||||
@@ -958,6 +955,14 @@ 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')
|
||||
.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}`);
|
||||
});
|
||||
|
||||
// preview method
|
||||
let preview_combo = document.createElement("select");
|
||||
preview_combo.setAttribute("title", "Configure how latent variables will be decoded during preview in the sampling process.");
|
||||
@@ -967,19 +972,19 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
preview_combo.appendChild($el('option', { value: 'latent2rgb', text: 'Preview method: Latent2RGB (fast)' }, []));
|
||||
preview_combo.appendChild($el('option', { value: 'none', text: 'Preview method: None (very fast)' }, []));
|
||||
|
||||
api.fetchApi('/manager/preview_method')
|
||||
api.fetchApi('/v2/manager/preview_method')
|
||||
.then(response => response.text())
|
||||
.then(data => { preview_combo.value = data; });
|
||||
|
||||
preview_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/manager/preview_method?value=${event.target.value}`);
|
||||
api.fetchApi(`/v2/manager/preview_method?value=${event.target.value}`);
|
||||
});
|
||||
|
||||
// channel
|
||||
let channel_combo = document.createElement("select");
|
||||
channel_combo.setAttribute("title", "Configure the channel for retrieving data from the Custom Node list (including missing nodes) or the Model list.");
|
||||
channel_combo.className = "cm-menu-combo";
|
||||
api.fetchApi('/manager/channel_url_list')
|
||||
api.fetchApi('/v2/manager/channel_url_list')
|
||||
.then(response => response.json())
|
||||
.then(async data => {
|
||||
try {
|
||||
@@ -992,7 +997,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
channel_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/manager/channel_url_list?value=${event.target.value}`);
|
||||
api.fetchApi(`/v2/manager/channel_url_list?value=${event.target.value}`);
|
||||
});
|
||||
|
||||
channel_combo.value = data.selected;
|
||||
@@ -1020,26 +1025,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
share_combo.appendChild($el('option', { value: option[0], text: `Share: ${option[1]}` }, []));
|
||||
}
|
||||
|
||||
// default ui state
|
||||
let component_policy_combo = document.createElement("select");
|
||||
component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
|
||||
component_policy_combo.className = "cm-menu-combo";
|
||||
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, []));
|
||||
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, []));
|
||||
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, []));
|
||||
api.fetchApi('/manager/component/policy')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
component_policy_combo.value = data;
|
||||
set_component_policy(data);
|
||||
});
|
||||
|
||||
component_policy_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/manager/component/policy?value=${event.target.value}`);
|
||||
set_component_policy(event.target.value);
|
||||
});
|
||||
|
||||
api.fetchApi('/manager/share_option')
|
||||
api.fetchApi('/v2/manager/share_option')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
share_combo.value = data || 'all';
|
||||
@@ -1049,7 +1035,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
share_combo.addEventListener('change', function (event) {
|
||||
const value = event.target.value;
|
||||
share_option = value;
|
||||
api.fetchApi(`/manager/share_option?value=${value}`);
|
||||
api.fetchApi(`/v2/manager/share_option?value=${value}`);
|
||||
const shareButton = document.getElementById("shareButton");
|
||||
if (value === 'none') {
|
||||
shareButton.style.display = "none";
|
||||
@@ -1058,14 +1044,51 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
});
|
||||
|
||||
let component_policy_combo = document.createElement("select");
|
||||
component_policy_combo.setAttribute("title", "When loading the workflow, configure which version of the component to use.");
|
||||
component_policy_combo.className = "cm-menu-combo";
|
||||
component_policy_combo.appendChild($el('option', { value: 'workflow', text: 'Component: Use workflow version' }, []));
|
||||
component_policy_combo.appendChild($el('option', { value: 'higher', text: 'Component: Use higher version' }, []));
|
||||
component_policy_combo.appendChild($el('option', { value: 'mine', text: 'Component: Use my version' }, []));
|
||||
api.fetchApi('/v2/manager/policy/component')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
component_policy_combo.value = data;
|
||||
set_component_policy(data);
|
||||
});
|
||||
|
||||
component_policy_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/v2/manager/policy/component?value=${event.target.value}`);
|
||||
set_component_policy(event.target.value);
|
||||
});
|
||||
|
||||
update_policy_combo = document.createElement("select");
|
||||
|
||||
if(isElectron)
|
||||
update_policy_combo.style.display = 'none';
|
||||
|
||||
update_policy_combo.setAttribute("title", "Sets the policy to be applied when performing an update.");
|
||||
update_policy_combo.className = "cm-menu-combo";
|
||||
update_policy_combo.appendChild($el('option', { value: 'stable-comfyui', text: 'Update: ComfyUI Stable Version' }, []));
|
||||
update_policy_combo.appendChild($el('option', { value: 'nightly-comfyui', text: 'Update: ComfyUI Nightly Version' }, []));
|
||||
api.fetchApi('/v2/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}`);
|
||||
});
|
||||
|
||||
return [
|
||||
$el("div", {}, [this.update_check_checkbox, uc_checkbox_text]),
|
||||
$el("br", {}, []),
|
||||
this.datasrc_combo,
|
||||
channel_combo,
|
||||
preview_combo,
|
||||
share_combo,
|
||||
component_policy_combo,
|
||||
update_policy_combo,
|
||||
$el("br", {}, []),
|
||||
|
||||
$el("br", {}, []),
|
||||
@@ -1092,11 +1115,6 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
install_pip(url, self);
|
||||
}
|
||||
}
|
||||
}),
|
||||
$el("button.cm-experimental-button", {
|
||||
type: "button",
|
||||
textContent: "Unload models",
|
||||
onclick: () => { free_models(); }
|
||||
})
|
||||
]),
|
||||
];
|
||||
@@ -1225,7 +1243,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
$el("div.comfy-modal-content",
|
||||
[
|
||||
$el("tr.cm-title", {}, [
|
||||
$el("font", {size:6, color:"white"}, [`ComfyUI Manager Menu`])]
|
||||
$el("font", {size:6, color:"white"}, [`ComfyUI Manager ${manager_version}`])]
|
||||
),
|
||||
$el("br", {}, []),
|
||||
$el("div.cm-menu-container",
|
||||
@@ -1363,17 +1381,16 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
async function getVersion() {
|
||||
let version = await api.fetchApi(`/manager/version`);
|
||||
let version = await api.fetchApi(`/v2/manager/version`);
|
||||
return await version.text();
|
||||
}
|
||||
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.ManagerMenu",
|
||||
name: "Comfy.Legacy.ManagerMenu",
|
||||
|
||||
aboutPageBadges: [
|
||||
{
|
||||
label: `ComfyUI-Manager ${await getVersion()}`,
|
||||
label: `ComfyUI-Manager ${manager_version}`,
|
||||
url: 'https://github.com/ltdrdata/ComfyUI-Manager',
|
||||
icon: 'pi pi-th-large'
|
||||
}
|
||||
@@ -1501,7 +1518,10 @@ app.registerExtension({
|
||||
}).element
|
||||
);
|
||||
|
||||
app.menu?.settingsGroup.element.before(cmGroup.element);
|
||||
const shouldShowLegacyMenuItems = isNotNewManagerUI();
|
||||
if (shouldShowLegacyMenuItems) {
|
||||
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(`/manager/set_esheep_workflow_and_images`, {
|
||||
api.fetchApi(`/v2/manager/set_esheep_workflow_and_images`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -812,7 +812,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
// get the user's existing matrix auth and share key
|
||||
ShareDialog.matrix_auth = { homeserver: "matrix.org", username: "", password: "" };
|
||||
try {
|
||||
api.fetchApi(`/manager/get_matrix_auth`)
|
||||
api.fetchApi(`/v2/manager/get_matrix_auth`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
ShareDialog.matrix_auth = data;
|
||||
@@ -831,7 +831,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
ShareDialog.cw_sharekey = "";
|
||||
try {
|
||||
// console.log("Fetching comfyworkflows share key")
|
||||
api.fetchApi(`/manager/get_comfyworkflows_auth`)
|
||||
api.fetchApi(`/v2/manager/get_comfyworkflows_auth`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
ShareDialog.cw_sharekey = data.comfyworkflows_sharekey;
|
||||
@@ -891,7 +891,7 @@ export class ShareDialog extends ComfyDialog {
|
||||
// Change the text of the share button to "Sharing..." to indicate that the share process has started
|
||||
this.share_button.textContent = "Sharing...";
|
||||
|
||||
const response = await api.fetchApi(`/manager/share`, {
|
||||
const response = await api.fetchApi(`/v2/manager/share`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
@@ -67,7 +67,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
async readKey() {
|
||||
let key = ""
|
||||
try {
|
||||
key = await api.fetchApi(`/manager/get_openart_auth`)
|
||||
key = await api.fetchApi(`/v2/manager/get_openart_auth`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
return data.openart_key;
|
||||
@@ -82,7 +82,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
async saveKey(value) {
|
||||
await api.fetchApi(`/manager/set_openart_auth`, {
|
||||
await api.fetchApi(`/v2/manager/set_openart_auth`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
@@ -399,7 +399,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
form.append("file", uploadFile);
|
||||
try {
|
||||
const res = await this.fetchApi(
|
||||
`/workflows/upload_thumbnail`,
|
||||
`/v2/workflows/upload_thumbnail`,
|
||||
{
|
||||
method: "POST",
|
||||
body: form,
|
||||
@@ -459,7 +459,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
throw new Error("Title is required");
|
||||
}
|
||||
|
||||
const current_snapshot = await api.fetchApi(`/snapshot/get_current`)
|
||||
const current_snapshot = await api.fetchApi(`/v2/snapshot/get_current`)
|
||||
.then(response => response.json())
|
||||
.catch(error => {
|
||||
// console.log(error);
|
||||
@@ -489,7 +489,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
|
||||
try {
|
||||
const response = await this.fetchApi(
|
||||
"/workflows/publish",
|
||||
"/v2/workflows/publish",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
@@ -179,7 +179,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
||||
async loadToken() {
|
||||
let key = ""
|
||||
try {
|
||||
const response = await api.fetchApi(`/manager/youml/settings`)
|
||||
const response = await api.fetchApi(`/v2/manager/youml/settings`)
|
||||
const settings = await response.json()
|
||||
return settings.token
|
||||
} catch (error) {
|
||||
@@ -188,7 +188,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
||||
}
|
||||
|
||||
async saveToken(value) {
|
||||
await api.fetchApi(`/manager/youml/settings`, {
|
||||
await api.fetchApi(`/v2/manager/youml/settings`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
@@ -380,7 +380,7 @@ export class YouMLShareDialog extends ComfyDialog {
|
||||
try {
|
||||
let snapshotData = null;
|
||||
try {
|
||||
const snapshot = await api.fetchApi(`/snapshot/get_current`)
|
||||
const snapshot = await api.fetchApi(`/v2/snapshot/get_current`)
|
||||
snapshotData = await snapshot.json()
|
||||
} catch (e) {
|
||||
console.error("Failed to get snapshot", e)
|
||||
@@ -1,6 +1,7 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { $el, ComfyDialog } from "../../scripts/ui.js";
|
||||
import { getBestPosition, getPositionStyle, getRect } from './popover-helper.js';
|
||||
|
||||
|
||||
function internalCustomConfirm(message, confirmMessage, cancelMessage) {
|
||||
@@ -171,7 +172,7 @@ export function rebootAPI() {
|
||||
customConfirm("Are you sure you'd like to reboot the server?").then((isConfirmed) => {
|
||||
if (isConfirmed) {
|
||||
try {
|
||||
api.fetchApi("/manager/reboot");
|
||||
api.fetchApi("/v2/manager/reboot");
|
||||
}
|
||||
catch(exception) {}
|
||||
}
|
||||
@@ -181,23 +182,6 @@ export function rebootAPI() {
|
||||
}
|
||||
|
||||
|
||||
export async function migrateAPI() {
|
||||
let confirmed = await customConfirm("When performing a migration, existing installed custom nodes will be renamed and the server will be restarted. Are you sure you want to apply this?\n\n(If you don't perform the migration, ComfyUI-Manager's start-up time will be longer each time due to re-checking during startup.)")
|
||||
if (confirmed) {
|
||||
try {
|
||||
await api.fetchApi("/manager/migrate_unmanaged_nodes");
|
||||
api.fetchApi("/manager/reboot");
|
||||
}
|
||||
catch(exception) {
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
export var manager_instance = null;
|
||||
|
||||
export function setManagerInstance(obj) {
|
||||
@@ -226,7 +210,7 @@ export async function install_pip(packages) {
|
||||
if(packages.includes('&'))
|
||||
app.ui.dialog.show(`Invalid PIP package enumeration: '${packages}'`);
|
||||
|
||||
const res = await api.fetchApi("/customnode/install/pip", {
|
||||
const res = await api.fetchApi("/v2/customnode/install/pip", {
|
||||
method: "POST",
|
||||
body: packages,
|
||||
});
|
||||
@@ -261,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("/customnode/install/git_url", {
|
||||
const res = await api.fetchApi("/v2/customnode/install/git_url", {
|
||||
method: "POST",
|
||||
body: url,
|
||||
});
|
||||
@@ -404,19 +388,275 @@ export async function fetchData(route, options) {
|
||||
}
|
||||
}
|
||||
|
||||
// https://cenfun.github.io/open-icons/
|
||||
export const icons = {
|
||||
search: '<svg viewBox="0 0 24 24" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m21 21-4.486-4.494M19 10.5a8.5 8.5 0 1 1-17 0 8.5 8.5 0 0 1 17 0"/></svg>',
|
||||
extensions: '<svg viewBox="64 64 896 896" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="M843.5 737.4c-12.4-75.2-79.2-129.1-155.3-125.4S550.9 676 546 752c-153.5-4.8-208-40.7-199.1-113.7 3.3-27.3 19.8-41.9 50.1-49 18.4-4.3 38.8-4.9 57.3-3.2 1.7.2 3.5.3 5.2.5 11.3 2.7 22.8 5 34.3 6.8 34.1 5.6 68.8 8.4 101.8 6.6 92.8-5 156-45.9 159.2-132.7 3.1-84.1-54.7-143.7-147.9-183.6-29.9-12.8-61.6-22.7-93.3-30.2-14.3-3.4-26.3-5.7-35.2-7.2-7.9-75.9-71.5-133.8-147.8-134.4S189.7 168 180.5 243.8s40 146.3 114.2 163.9 149.9-23.3 175.7-95.1c9.4 1.7 18.7 3.6 28 5.8 28.2 6.6 56.4 15.4 82.4 26.6 70.7 30.2 109.3 70.1 107.5 119.9-1.6 44.6-33.6 65.2-96.2 68.6-27.5 1.5-57.6-.9-87.3-5.8-8.3-1.4-15.9-2.8-22.6-4.3-3.9-.8-6.6-1.5-7.8-1.8l-3.1-.6c-2.2-.3-5.9-.8-10.7-1.3-25-2.3-52.1-1.5-78.5 4.6-55.2 12.9-93.9 47.2-101.1 105.8-15.7 126.2 78.6 184.7 276 188.9 29.1 70.4 106.4 107.9 179.6 87 73.3-20.9 119.3-93.4 106.9-168.6M329.1 345.2a83.3 83.3 0 1 1 .01-166.61 83.3 83.3 0 0 1-.01 166.61M695.6 845a83.3 83.3 0 1 1 .01-166.61A83.3 83.3 0 0 1 695.6 845"/></svg>',
|
||||
conflicts: '<svg viewBox="0 0 400 400" width="100%" height="100%" pointer-events="none" xmlns="http://www.w3.org/2000/svg"><path fill="currentColor" d="m397.2 350.4.2-.2-180-320-.2.2C213.8 24.2 207.4 20 200 20s-13.8 4.2-17.2 10.4l-.2-.2-180 320 .2.2c-1.6 2.8-2.8 6-2.8 9.6 0 11 9 20 20 20h360c11 0 20-9 20-20 0-3.6-1.2-6.8-2.8-9.6M220 340h-40v-40h40zm0-60h-40V120h40z"/></svg>',
|
||||
passed: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 426.667 426.667"><path fill="#6AC259" d="M213.333,0C95.518,0,0,95.514,0,213.333s95.518,213.333,213.333,213.333c117.828,0,213.333-95.514,213.333-213.333S331.157,0,213.333,0z M174.199,322.918l-93.935-93.931l31.309-31.309l62.626,62.622l140.894-140.898l31.309,31.309L174.199,322.918z"/></svg>',
|
||||
download: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" width="100%" height="100%" viewBox="0 0 32 32"><path fill="currentColor" d="M26 24v4H6v-4H4v4a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-4zm0-10l-1.41-1.41L17 20.17V2h-2v18.17l-7.59-7.58L6 14l10 10l10-10z"></path></svg>'
|
||||
download: '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" width="100%" height="100%" viewBox="0 0 32 32"><path fill="currentColor" d="M26 24v4H6v-4H4v4a2 2 0 0 0 2 2h20a2 2 0 0 0 2-2v-4zm0-10l-1.41-1.41L17 20.17V2h-2v18.17l-7.59-7.58L6 14l10 10l10-10z"></path></svg>',
|
||||
close: '<svg xmlns="http://www.w3.org/2000/svg" pointer-events="none" width="100%" height="100%" viewBox="0 0 16 16"><g fill="currentColor"><path fill-rule="evenodd" clip-rule="evenodd" d="m7.116 8-4.558 4.558.884.884L8 8.884l4.558 4.558.884-.884L8.884 8l4.558-4.558-.884-.884L8 7.116 3.442 2.558l-.884.884L7.116 8z"/></g></svg>',
|
||||
arrowRight: '<svg xmlns="http://www.w3.org/2000/svg" pointer-events="none" width="100%" height="100%" viewBox="0 0 20 20"><path fill="currentColor" fill-rule="evenodd" d="m2.542 2.154 7.254 7.26c.136.14.204.302.204.483a.73.73 0 0 1-.204.5l-7.575 7.398c-.383.317-.724.317-1.022 0-.299-.317-.299-.643 0-.98l7.08-6.918-6.754-6.763c-.237-.343-.215-.654.066-.935.281-.28.598-.295.951-.045Zm9 0 7.254 7.26c.136.14.204.302.204.483a.73.73 0 0 1-.204.5l-7.575 7.398c-.383.317-.724.317-1.022 0-.299-.317-.299-.643 0-.98l7.08-6.918-6.754-6.763c-.237-.343-.215-.654.066-.935.281-.28.598-.295.951-.045Z"/></svg>'
|
||||
}
|
||||
|
||||
export function sanitizeHTML(str) {
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
return str
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
export function showTerminal() {
|
||||
try {
|
||||
const panel = app.extensionManager.bottomPanel;
|
||||
const isTerminalVisible = panel.bottomPanelVisible && panel.activeBottomPanelTab.id === 'logs-terminal';
|
||||
if (!isTerminalVisible)
|
||||
panel.toggleBottomPanelTab('logs-terminal');
|
||||
}
|
||||
catch(exception) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
let need_restart = false;
|
||||
|
||||
export function setNeedRestart(value) {
|
||||
need_restart = value;
|
||||
}
|
||||
|
||||
async function onReconnected(event) {
|
||||
if(need_restart) {
|
||||
setNeedRestart(false);
|
||||
|
||||
const confirmed = await customConfirm("To apply the changes to the node pack's installation status, you need to refresh the browser. Would you like to refresh?");
|
||||
if (!confirmed) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.reload(true);
|
||||
}
|
||||
}
|
||||
|
||||
api.addEventListener('reconnected', onReconnected);
|
||||
|
||||
const storeId = "comfyui-manager-grid";
|
||||
let timeId;
|
||||
export function storeColumnWidth(gridId, columnItem) {
|
||||
clearTimeout(timeId);
|
||||
timeId = setTimeout(() => {
|
||||
let data = {};
|
||||
const dataStr = localStorage.getItem(storeId);
|
||||
if (dataStr) {
|
||||
try {
|
||||
data = JSON.parse(dataStr);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
if (!data[gridId]) {
|
||||
data[gridId] = {};
|
||||
}
|
||||
|
||||
data[gridId][columnItem.id] = columnItem.width;
|
||||
|
||||
localStorage.setItem(storeId, JSON.stringify(data));
|
||||
|
||||
}, 200)
|
||||
}
|
||||
|
||||
export function restoreColumnWidth(gridId, columns) {
|
||||
const dataStr = localStorage.getItem(storeId);
|
||||
if (!dataStr) {
|
||||
return;
|
||||
}
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(dataStr);
|
||||
} catch (e) {}
|
||||
if(!data) {
|
||||
return;
|
||||
}
|
||||
const widthMap = data[gridId];
|
||||
if (!widthMap) {
|
||||
return;
|
||||
}
|
||||
|
||||
columns.forEach(columnItem => {
|
||||
const w = widthMap[columnItem.id];
|
||||
if (w) {
|
||||
columnItem.width = w;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
export function getTimeAgo(dateStr) {
|
||||
const date = new Date(dateStr);
|
||||
|
||||
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
||||
return "";
|
||||
}
|
||||
|
||||
const units = [
|
||||
{ max: 2760000, value: 60000, name: 'minute', past: 'a minute ago', future: 'in a minute' },
|
||||
{ max: 72000000, value: 3600000, name: 'hour', past: 'an hour ago', future: 'in an hour' },
|
||||
{ max: 518400000, value: 86400000, name: 'day', past: 'yesterday', future: 'tomorrow' },
|
||||
{ max: 2419200000, value: 604800000, name: 'week', past: 'last week', future: 'in a week' },
|
||||
{ max: 28512000000, value: 2592000000, name: 'month', past: 'last month', future: 'in a month' }
|
||||
];
|
||||
const diff = Date.now() - date.getTime();
|
||||
// less than a minute
|
||||
if (Math.abs(diff) < 60000)
|
||||
return 'just now';
|
||||
for (let i = 0; i < units.length; i++) {
|
||||
if (Math.abs(diff) < units[i].max) {
|
||||
return format(diff, units[i].value, units[i].name, units[i].past, units[i].future, diff < 0);
|
||||
}
|
||||
}
|
||||
function format(diff, divisor, unit, past, future, isInTheFuture) {
|
||||
const val = Math.round(Math.abs(diff) / divisor);
|
||||
if (isInTheFuture)
|
||||
return val <= 1 ? future : 'in ' + val + ' ' + unit + 's';
|
||||
return val <= 1 ? past : val + ' ' + unit + 's ago';
|
||||
}
|
||||
return format(diff, 31536000000, 'year', 'last year', 'in a year', diff < 0);
|
||||
};
|
||||
|
||||
export const loadCss = (cssFile) => {
|
||||
const cssPath = import.meta.resolve(cssFile);
|
||||
//console.log(cssPath);
|
||||
const $link = document.createElement("link");
|
||||
$link.setAttribute("rel", 'stylesheet');
|
||||
$link.setAttribute("href", cssPath);
|
||||
document.head.appendChild($link);
|
||||
};
|
||||
|
||||
export const copyText = (text) => {
|
||||
return new Promise((resolve) => {
|
||||
let err;
|
||||
try {
|
||||
navigator.clipboard.writeText(text);
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
if (err) {
|
||||
resolve(false);
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function renderPopover($elem, target, options = {}) {
|
||||
// async microtask
|
||||
queueMicrotask(() => {
|
||||
|
||||
const containerRect = getRect(window);
|
||||
const targetRect = getRect(target);
|
||||
const elemRect = getRect($elem);
|
||||
|
||||
const positionInfo = getBestPosition(
|
||||
containerRect,
|
||||
targetRect,
|
||||
elemRect,
|
||||
options.positions
|
||||
);
|
||||
const style = getPositionStyle(positionInfo, {
|
||||
bgColor: options.bgColor,
|
||||
borderColor: options.borderColor,
|
||||
borderRadius: options.borderRadius
|
||||
});
|
||||
|
||||
$elem.style.top = positionInfo.top + "px";
|
||||
$elem.style.left = positionInfo.left + "px";
|
||||
$elem.style.background = style.background;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
let $popover;
|
||||
export function hidePopover() {
|
||||
if ($popover) {
|
||||
$popover.remove();
|
||||
$popover = null;
|
||||
}
|
||||
}
|
||||
export function showPopover(target, text, className, options) {
|
||||
hidePopover();
|
||||
$popover = document.createElement("div");
|
||||
$popover.className = ['cn-popover', className].filter(it => it).join(" ");
|
||||
document.body.appendChild($popover);
|
||||
$popover.innerHTML = text;
|
||||
$popover.style.display = "block";
|
||||
renderPopover($popover, target, {
|
||||
borderRadius: 10,
|
||||
... options
|
||||
});
|
||||
}
|
||||
|
||||
let $tooltip;
|
||||
export function hideTooltip(target) {
|
||||
if ($tooltip) {
|
||||
$tooltip.style.display = "none";
|
||||
$tooltip.innerHTML = "";
|
||||
$tooltip.style.top = "0px";
|
||||
$tooltip.style.left = "0px";
|
||||
}
|
||||
}
|
||||
export function showTooltip(target, text, className = 'cn-tooltip', styleMap = {}) {
|
||||
if (!$tooltip) {
|
||||
$tooltip = document.createElement("div");
|
||||
$tooltip.className = className;
|
||||
$tooltip.style.cssText = `
|
||||
pointer-events: none;
|
||||
position: fixed;
|
||||
z-index: 10001;
|
||||
padding: 20px;
|
||||
color: #1e1e1e;
|
||||
max-width: 350px;
|
||||
filter: drop-shadow(1px 5px 5px rgb(0 0 0 / 30%));
|
||||
${Object.keys(styleMap).map(k=>k+":"+styleMap[k]+";").join("")}
|
||||
`;
|
||||
document.body.appendChild($tooltip);
|
||||
}
|
||||
|
||||
$tooltip.innerHTML = text;
|
||||
$tooltip.style.display = "block";
|
||||
renderPopover($tooltip, target, {
|
||||
positions: ['top', 'bottom', 'right', 'center'],
|
||||
bgColor: "#ffffff",
|
||||
borderColor: "#cccccc",
|
||||
borderRadius: 5
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
const text = target.getAttribute('tooltip');
|
||||
if (text) {
|
||||
showTooltip(target, text);
|
||||
}
|
||||
};
|
||||
const mouseleaveHandler = (e) => {
|
||||
const target = e.target;
|
||||
const text = target.getAttribute('tooltip');
|
||||
if (text) {
|
||||
hideTooltip(target);
|
||||
}
|
||||
};
|
||||
document.body.removeEventListener('mouseenter', mouseenterHandler, true);
|
||||
document.body.removeEventListener('mouseleave', mouseleaveHandler, true);
|
||||
document.body.addEventListener('mouseenter', mouseenterHandler, true);
|
||||
document.body.addEventListener('mouseleave', mouseleaveHandler, true);
|
||||
}
|
||||
|
||||
initTooltip();
|
||||
@@ -64,7 +64,7 @@ function storeGroupNode(name, data, register=true) {
|
||||
}
|
||||
|
||||
export async function load_components() {
|
||||
let data = await api.fetchApi('/manager/component/loads', {method: "POST"});
|
||||
let data = await api.fetchApi('/v2/manager/component/loads', {method: "POST"});
|
||||
let components = await data.json();
|
||||
|
||||
let start_time = Date.now();
|
||||
@@ -222,7 +222,7 @@ async function save_as_component(node, version, author, prefix, nodename, packna
|
||||
pack_map[packname] = component_name;
|
||||
rpack_map[component_name] = subgraph;
|
||||
|
||||
const res = await api.fetchApi('/manager/component/save', {
|
||||
const res = await api.fetchApi('/v2/manager/component/save', {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -259,7 +259,7 @@ async function import_component(component_name, component, mode) {
|
||||
workflow: component
|
||||
};
|
||||
|
||||
const res = await api.fetchApi('/manager/component/save', {
|
||||
const res = await api.fetchApi('/v2/manager/component/save', {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", },
|
||||
body: JSON.stringify(body)
|
||||
@@ -709,7 +709,7 @@ app.handleFile = handleFile;
|
||||
|
||||
let current_component_policy = 'workflow';
|
||||
try {
|
||||
api.fetchApi('/manager/component/policy')
|
||||
api.fetchApi('/v2/manager/policy/component')
|
||||
.then(response => response.text())
|
||||
.then(data => { current_component_policy = data; });
|
||||
}
|
||||
699
comfyui_manager/js/custom-nodes-manager.css
Normal file
699
comfyui_manager/js/custom-nodes-manager.css
Normal file
@@ -0,0 +1,699 @@
|
||||
.cn-manager {
|
||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segue UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
z-index: 1099;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
color: var(--fg-color);
|
||||
font-family: arial, sans-serif;
|
||||
text-underline-offset: 3px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.cn-manager .cn-flex-auto {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.cn-manager button {
|
||||
font-size: 16px;
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid;
|
||||
margin: 0;
|
||||
padding: 4px 8px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.cn-manager button:disabled,
|
||||
.cn-manager input:disabled,
|
||||
.cn-manager select:disabled {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.cn-manager button:disabled {
|
||||
background-color: var(--comfy-input-bg);
|
||||
}
|
||||
|
||||
.cn-manager .cn-manager-restart {
|
||||
display: none;
|
||||
background-color: #500000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-manager-stop {
|
||||
display: none;
|
||||
background-color: #500000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-manager-back {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
margin-right: 5px;
|
||||
transform: translateY(2px);
|
||||
}
|
||||
|
||||
.cn-icon {
|
||||
display: block;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.cn-icon svg {
|
||||
display: block;
|
||||
margin: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cn-manager-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.cn-manager-header label {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cn-manager-filter {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.cn-manager-keywords {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
padding: 0 5px 0 26px;
|
||||
background-size: 16px;
|
||||
background-position: 5px center;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
|
||||
.cn-manager-status {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.cn-manager-grid {
|
||||
flex: auto;
|
||||
border: 1px solid var(--border-color);
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cn-manager-selection {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cn-manager-message {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cn-manager-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cn-manager-grid .tg-turbogrid {
|
||||
font-family: var(--grid-font);
|
||||
font-size: 15px;
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.cn-manager-grid .tg-turbogrid .tg-highlight::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: #80bdff11;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.cn-manager-grid .cn-pack-name a {
|
||||
color: skyblue;
|
||||
text-decoration: none;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.cn-manager-grid .cn-pack-desc a {
|
||||
color: #5555FF;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cn-manager-grid .tg-cell a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cn-manager-grid .cn-pack-version {
|
||||
line-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.cn-manager-grid .cn-pack-nodes {
|
||||
line-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
cursor: pointer;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cn-manager-grid .cn-pack-nodes:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cn-manager-grid .cn-pack-conflicts {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.cn-popover {
|
||||
position: fixed;
|
||||
z-index: 10000;
|
||||
padding: 20px;
|
||||
color: #1e1e1e;
|
||||
filter: drop-shadow(1px 5px 5px rgb(0 0 0 / 30%));
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cn-flyover {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background-color: var(--comfy-menu-bg);
|
||||
animation-duration: 0.2s;
|
||||
animation-fill-mode: both;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.cn-flyover::before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
content: "";
|
||||
z-index: 10;
|
||||
display: block;
|
||||
width: 10px;
|
||||
height: 100%;
|
||||
pointer-events: none;
|
||||
left: -10px;
|
||||
background-image: linear-gradient(to left, rgb(0 0 0 / 20%), rgb(0 0 0 / 0%));
|
||||
}
|
||||
|
||||
.cn-flyover-header {
|
||||
height: 45px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.cn-flyover-close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cn-flyover-close:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.cn-flyover-close svg {
|
||||
display: block;
|
||||
margin: 0;
|
||||
pointer-events: none;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.cn-flyover-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: bold;
|
||||
gap: 10px;
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.cn-flyover-body {
|
||||
height: calc(100% - 45px);
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
background-color: var(--comfy-menu-secondary-bg);
|
||||
}
|
||||
|
||||
@keyframes cn-slide-in-right {
|
||||
from {
|
||||
visibility: visible;
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.cn-slide-in-right {
|
||||
animation-name: cn-slide-in-right;
|
||||
}
|
||||
|
||||
@keyframes cn-slide-out-right {
|
||||
from {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
visibility: hidden;
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.cn-slide-out-right {
|
||||
animation-name: cn-slide-out-right;
|
||||
}
|
||||
|
||||
.cn-nodes-list {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cn-nodes-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.cn-nodes-row:nth-child(odd) {
|
||||
background-color: rgb(0 0 0 / 5%);
|
||||
}
|
||||
|
||||
.cn-nodes-row:hover {
|
||||
background-color: rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.cn-nodes-sn {
|
||||
text-align: right;
|
||||
min-width: 35px;
|
||||
color: var(--drag-text);
|
||||
flex-shrink: 0;
|
||||
font-size: 12px;
|
||||
padding: 8px 5px;
|
||||
}
|
||||
|
||||
.cn-nodes-name {
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
position: relative;
|
||||
padding: 8px 5px;
|
||||
}
|
||||
|
||||
.cn-nodes-name::after {
|
||||
content: attr(action);
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
transform: translate(5px, -50%);
|
||||
font-size: 12px;
|
||||
color: var(--drag-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 3px 8px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cn-nodes-name.action::after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cn-nodes-name:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cn-nodes-conflict .cn-nodes-name,
|
||||
.cn-nodes-conflict .cn-icon {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.cn-conflicts-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.cn-conflicts-list b {
|
||||
font-weight: normal;
|
||||
color: var(--descrip-text);
|
||||
}
|
||||
|
||||
.cn-nodes-pack {
|
||||
cursor: pointer;
|
||||
color: skyblue;
|
||||
}
|
||||
|
||||
.cn-nodes-pack:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cn-pack-badge {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 3px 8px;
|
||||
color: var(--error-text);
|
||||
}
|
||||
|
||||
.cn-preview {
|
||||
min-width: 300px;
|
||||
max-width: 500px;
|
||||
min-height: 120px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
padding: 12px;
|
||||
color: var(--fg-color);
|
||||
}
|
||||
|
||||
.cn-preview-header {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--comfy-input-bg);
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.cn-preview-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: grey;
|
||||
position: relative;
|
||||
filter: drop-shadow(1px 2px 3px rgb(0 0 0 / 30%));
|
||||
}
|
||||
|
||||
.cn-preview-dot.cn-preview-optional::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 50%;
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
}
|
||||
|
||||
.cn-preview-dot.cn-preview-grid {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.cn-preview-dot.cn-preview-grid::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-left: 1px solid var(--comfy-input-bg);
|
||||
border-right: 1px solid var(--comfy-input-bg);
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
left: 2px;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cn-preview-dot.cn-preview-grid::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-top: 1px solid var(--comfy-input-bg);
|
||||
border-bottom: 1px solid var(--comfy-input-bg);
|
||||
width: 100%;
|
||||
height: 4px;
|
||||
left: 0;
|
||||
top: 2px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.cn-preview-name {
|
||||
flex: auto;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.cn-preview-io {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
.cn-preview-column > div {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
height: 18px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.cn-preview-input {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.cn-preview-output {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.cn-preview-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
padding: 0 10px 10px 10px;
|
||||
}
|
||||
|
||||
.cn-preview-switch {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
background: var(--bg-color);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: 10px;
|
||||
text-wrap: nowrap;
|
||||
padding: 2px 20px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.cn-preview-switch::before,
|
||||
.cn-preview-switch::after {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
color: var(--fg-color);
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.cn-preview-switch::before {
|
||||
content: "◀";
|
||||
left: 5px;
|
||||
}
|
||||
|
||||
.cn-preview-switch::after {
|
||||
content: "▶";
|
||||
right: 5px;
|
||||
}
|
||||
|
||||
.cn-preview-value {
|
||||
color: var(--descrip-text);
|
||||
}
|
||||
|
||||
.cn-preview-string {
|
||||
min-height: 30px;
|
||||
max-height: 300px;
|
||||
background: var(--bg-color);
|
||||
color: var(--descrip-text);
|
||||
border-radius: 3px;
|
||||
padding: 3px 5px;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.cn-preview-description {
|
||||
margin: 0px 10px 10px 10px;
|
||||
padding: 6px;
|
||||
background: var(--border-color);
|
||||
color: var(--descrip-text);
|
||||
border-radius: 5px;
|
||||
font-style: italic;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.cn-tag-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.cn-tag-list > div {
|
||||
background-color: var(--border-color);
|
||||
border-radius: 5px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.cn-install-buttons {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
padding: 3px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cn-selected-buttons {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-enable {
|
||||
background-color: #333399;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-disable {
|
||||
background-color: #442277;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-update {
|
||||
background-color: #1155AA;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-try-update {
|
||||
background-color: Gray;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-try-fix {
|
||||
background-color: #6495ED;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-import-failed {
|
||||
background-color: #AA1111;
|
||||
font-size: 10px;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-install {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-try-install {
|
||||
background-color: Gray;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-uninstall {
|
||||
background-color: #993333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-reinstall {
|
||||
background-color: #993333;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cn-manager .cn-btn-switch {
|
||||
background-color: #448833;
|
||||
color: white;
|
||||
|
||||
}
|
||||
|
||||
@keyframes cn-btn-loading-bg {
|
||||
0% {
|
||||
left: 0;
|
||||
}
|
||||
100% {
|
||||
left: -105px;
|
||||
}
|
||||
}
|
||||
|
||||
.cn-manager button.cn-btn-loading {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-color: rgb(0 119 207 / 80%);
|
||||
background-color: var(--comfy-input-bg);
|
||||
}
|
||||
|
||||
.cn-manager button.cn-btn-loading::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 500px;
|
||||
height: 100%;
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
rgb(0 119 207 / 30%),
|
||||
rgb(0 119 207 / 30%) 10px,
|
||||
transparent 10px,
|
||||
transparent 15px
|
||||
);
|
||||
animation: cn-btn-loading-bg 2s linear infinite;
|
||||
}
|
||||
|
||||
.cn-manager-light .cn-pack-name a {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.cn-manager-light .cm-warn-note {
|
||||
background-color: #ccc !important;
|
||||
}
|
||||
|
||||
.cn-manager-light .cn-btn-install {
|
||||
background-color: #333;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
213
comfyui_manager/js/model-manager.css
Normal file
213
comfyui_manager/js/model-manager.css
Normal file
@@ -0,0 +1,213 @@
|
||||
.cmm-manager {
|
||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
z-index: 1099;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
color: var(--fg-color);
|
||||
font-family: arial, sans-serif;
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-flex-auto {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.cmm-manager button {
|
||||
font-size: 16px;
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid;
|
||||
margin: 0;
|
||||
padding: 4px 8px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.cmm-manager button:disabled,
|
||||
.cmm-manager input:disabled,
|
||||
.cmm-manager select:disabled {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.cmm-manager button:disabled {
|
||||
background-color: var(--comfy-input-bg);
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-manager-refresh {
|
||||
display: none;
|
||||
background-color: #000080;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-manager-stop {
|
||||
display: none;
|
||||
background-color: #500000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-manager-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.cmm-manager-header label {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cmm-manager-type,
|
||||
.cmm-manager-base,
|
||||
.cmm-manager-filter {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.cmm-manager-keywords {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
padding: 0 5px 0 26px;
|
||||
background-size: 16px;
|
||||
background-position: 5px center;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20viewBox%3D%220%200%2024%2024%22%20width%3D%22100%25%22%20height%3D%22100%25%22%20pointer-events%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20fill%3D%22none%22%20stroke%3D%22%23888%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%20stroke-width%3D%222%22%20d%3D%22m21%2021-4.486-4.494M19%2010.5a8.5%208.5%200%201%201-17%200%208.5%208.5%200%200%201%2017%200%22%2F%3E%3C%2Fsvg%3E");
|
||||
}
|
||||
|
||||
.cmm-manager-status {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.cmm-manager-grid {
|
||||
flex: auto;
|
||||
border: 1px solid var(--border-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cmm-manager-selection {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cmm-manager-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cmm-manager-grid .tg-turbogrid {
|
||||
font-family: var(--grid-font);
|
||||
font-size: 15px;
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.cmm-manager-grid .cmm-node-name a {
|
||||
color: skyblue;
|
||||
text-decoration: none;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.cmm-manager-grid .cmm-node-desc a {
|
||||
color: #5555FF;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cmm-manager-grid .tg-cell a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cmm-icon-passed {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: calc(50% - 10px);
|
||||
top: calc(50% - 10px);
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-btn-enable {
|
||||
background-color: blue;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-btn-disable {
|
||||
background-color: MediumSlateBlue;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-btn-install {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-btn-download {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
position: absolute;
|
||||
left: calc(50% - 10px);
|
||||
top: calc(50% - 10px);
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cmm-btn-download:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.cmm-manager-light .cmm-btn-download {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
@keyframes cmm-btn-loading-bg {
|
||||
0% {
|
||||
left: 0;
|
||||
}
|
||||
100% {
|
||||
left: -105px;
|
||||
}
|
||||
}
|
||||
|
||||
.cmm-manager button.cmm-btn-loading {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-color: rgb(0 119 207 / 80%);
|
||||
background-color: var(--comfy-input-bg);
|
||||
}
|
||||
|
||||
.cmm-manager button.cmm-btn-loading::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 500px;
|
||||
height: 100%;
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
rgb(0 119 207 / 30%),
|
||||
rgb(0 119 207 / 30%) 10px,
|
||||
transparent 10px,
|
||||
transparent 15px
|
||||
);
|
||||
animation: cmm-btn-loading-bg 2s linear infinite;
|
||||
}
|
||||
|
||||
.cmm-manager-light .cmm-node-name a {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.cmm-manager-light .cm-warn-note {
|
||||
background-color: #ccc !important;
|
||||
}
|
||||
|
||||
.cmm-manager-light .cmm-btn-install {
|
||||
background-color: #333;
|
||||
}
|
||||
@@ -2,233 +2,17 @@ import { app } from "../../scripts/app.js";
|
||||
import { $el } from "../../scripts/ui.js";
|
||||
import {
|
||||
manager_instance, rebootAPI,
|
||||
fetchData, md5, icons, show_message, customAlert, infoToast
|
||||
fetchData, md5, icons, show_message, customAlert, infoToast, showTerminal,
|
||||
storeColumnWidth, restoreColumnWidth, loadCss, generateUUID
|
||||
} from "./common.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
// https://cenfun.github.io/turbogrid/api.html
|
||||
import TG from "./turbogrid.esm.js";
|
||||
|
||||
const pageCss = `
|
||||
.cmm-manager {
|
||||
--grid-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
|
||||
z-index: 1099;
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
color: var(--fg-color);
|
||||
font-family: arial, sans-serif;
|
||||
}
|
||||
loadCss("./model-manager.css");
|
||||
|
||||
.cmm-manager .cmm-flex-auto {
|
||||
flex: auto;
|
||||
}
|
||||
|
||||
.cmm-manager button {
|
||||
font-size: 16px;
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid;
|
||||
margin: 0;
|
||||
padding: 4px 8px;
|
||||
min-width: 100px;
|
||||
}
|
||||
|
||||
.cmm-manager button:disabled,
|
||||
.cmm-manager input:disabled,
|
||||
.cmm-manager select:disabled {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.cmm-manager button:disabled {
|
||||
background-color: var(--comfy-input-bg);
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-manager-refresh {
|
||||
display: none;
|
||||
background-color: #000080;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-manager-stop {
|
||||
display: none;
|
||||
background-color: #500000;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-manager-header {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.cmm-manager-header label {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cmm-manager-type,
|
||||
.cmm-manager-base,
|
||||
.cmm-manager-filter {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
}
|
||||
|
||||
.cmm-manager-keywords {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
padding: 0 5px 0 26px;
|
||||
background-size: 16px;
|
||||
background-position: 5px center;
|
||||
background-repeat: no-repeat;
|
||||
background-image: url("data:image/svg+xml;charset=utf8,${encodeURIComponent(icons.search.replace("currentColor", "#888"))}");
|
||||
}
|
||||
|
||||
.cmm-manager-status {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
.cmm-manager-grid {
|
||||
flex: auto;
|
||||
border: 1px solid var(--border-color);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.cmm-manager-selection {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cmm-manager-message {
|
||||
|
||||
}
|
||||
|
||||
.cmm-manager-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.cmm-manager-grid .tg-turbogrid {
|
||||
font-family: var(--grid-font);
|
||||
font-size: 15px;
|
||||
background: var(--bg-color);
|
||||
}
|
||||
|
||||
.cmm-manager-grid .cmm-node-name a {
|
||||
color: skyblue;
|
||||
text-decoration: none;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.cmm-manager-grid .cmm-node-desc a {
|
||||
color: #5555FF;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.cmm-manager-grid .tg-cell a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cmm-icon-passed {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
position: absolute;
|
||||
left: calc(50% - 10px);
|
||||
top: calc(50% - 10px);
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-btn-enable {
|
||||
background-color: blue;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-btn-disable {
|
||||
background-color: MediumSlateBlue;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-manager .cmm-btn-install {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cmm-btn-download {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
position: absolute;
|
||||
left: calc(50% - 10px);
|
||||
top: calc(50% - 10px);
|
||||
cursor: pointer;
|
||||
opacity: 0.8;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.cmm-btn-download:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.cmm-manager-light .cmm-btn-download {
|
||||
color: #000;
|
||||
}
|
||||
|
||||
@keyframes cmm-btn-loading-bg {
|
||||
0% {
|
||||
left: 0;
|
||||
}
|
||||
100% {
|
||||
left: -105px;
|
||||
}
|
||||
}
|
||||
|
||||
.cmm-manager button.cmm-btn-loading {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-color: rgb(0 119 207 / 80%);
|
||||
background-color: var(--comfy-input-bg);
|
||||
}
|
||||
|
||||
.cmm-manager button.cmm-btn-loading::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
content: "";
|
||||
width: 500px;
|
||||
height: 100%;
|
||||
background-image: repeating-linear-gradient(
|
||||
-45deg,
|
||||
rgb(0 119 207 / 30%),
|
||||
rgb(0 119 207 / 30%) 10px,
|
||||
transparent 10px,
|
||||
transparent 15px
|
||||
);
|
||||
animation: cmm-btn-loading-bg 2s linear infinite;
|
||||
}
|
||||
|
||||
.cmm-manager-light .cmm-node-name a {
|
||||
color: blue;
|
||||
}
|
||||
|
||||
.cmm-manager-light .cm-warn-note {
|
||||
background-color: #ccc !important;
|
||||
}
|
||||
|
||||
.cmm-manager-light .cmm-btn-install {
|
||||
background-color: #333;
|
||||
}
|
||||
|
||||
`;
|
||||
const gridId = "model";
|
||||
|
||||
const pageHtml = `
|
||||
<div class="cmm-manager-header">
|
||||
@@ -280,14 +64,6 @@ export class ModelManager {
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
if (!document.querySelector(`style[context="${this.id}"]`)) {
|
||||
const $style = document.createElement("style");
|
||||
$style.setAttribute("context", this.id);
|
||||
$style.innerHTML = pageCss;
|
||||
document.head.appendChild($style);
|
||||
}
|
||||
|
||||
this.element = $el("div", {
|
||||
parent: document.body,
|
||||
className: "comfy-modal cmm-manager"
|
||||
@@ -396,7 +172,7 @@ export class ModelManager {
|
||||
|
||||
".cmm-manager-stop": {
|
||||
click: () => {
|
||||
api.fetchApi('/manager/queue/reset');
|
||||
api.fetchApi('/v2/manager/queue/reset');
|
||||
infoToast('Cancel', 'Remaining tasks will stop after completing the current task.');
|
||||
}
|
||||
},
|
||||
@@ -438,6 +214,10 @@ export class ModelManager {
|
||||
this.renderSelected();
|
||||
});
|
||||
|
||||
grid.bind("onColumnWidthChanged", (e, columnItem) => {
|
||||
storeColumnWidth(gridId, columnItem)
|
||||
});
|
||||
|
||||
grid.bind('onClick', (e, d) => {
|
||||
const { rowItem } = d;
|
||||
const target = d.e.target;
|
||||
@@ -554,7 +334,7 @@ export class ModelManager {
|
||||
sortable: false,
|
||||
align: 'center',
|
||||
formatter: (url, rowItem, columnItem) => {
|
||||
return `<a class="cmm-btn-download" title="Download file" href="${url}" target="_blank">${icons.download}</a>`;
|
||||
return `<a class="cmm-btn-download" tooltip="Download file" href="${url}" target="_blank">${icons.download}</a>`;
|
||||
}
|
||||
}, {
|
||||
id: 'size',
|
||||
@@ -589,6 +369,8 @@ export class ModelManager {
|
||||
width: 200
|
||||
}];
|
||||
|
||||
restoreColumnWidth(gridId, columns);
|
||||
|
||||
this.grid.setData({
|
||||
options,
|
||||
rows,
|
||||
@@ -631,25 +413,16 @@ 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.showLoading();
|
||||
this.showError("");
|
||||
|
||||
let needRefresh = false;
|
||||
let errorMsg = "";
|
||||
|
||||
await api.fetchApi('/manager/queue/reset');
|
||||
|
||||
let target_items = [];
|
||||
|
||||
let batch = {};
|
||||
|
||||
for (const item of list) {
|
||||
this.grid.scrollRowIntoView(item);
|
||||
target_items.push(item);
|
||||
@@ -665,14 +438,12 @@ 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 (res.status != 200) {
|
||||
errorMsg = `Install failed: ${item.name} ${res.error.message}`;
|
||||
break;
|
||||
if(batch['install_model']) {
|
||||
batch['install_model'].push(data);
|
||||
}
|
||||
else {
|
||||
batch['install_model'] = [data];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -680,17 +451,35 @@ export class ModelManager {
|
||||
|
||||
if(errorMsg) {
|
||||
this.showError(errorMsg);
|
||||
show_message("Installation Error:\n"+errorMsg);
|
||||
show_message("[Installation Errors]\n"+errorMsg);
|
||||
|
||||
// reset
|
||||
for (const hash of list) {
|
||||
const item = this.grid.getRowItemBy("hash", hash);
|
||||
for(let k in target_items) {
|
||||
const item = target_items[k];
|
||||
this.grid.updateCell(item, "installed");
|
||||
}
|
||||
}
|
||||
else {
|
||||
await api.fetchApi('/manager/queue/start');
|
||||
this.batch_id = generateUUID();
|
||||
batch['batch_id'] = this.batch_id;
|
||||
|
||||
const res = await api.fetchApi(`/v2/manager/queue/batch`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify(batch)
|
||||
});
|
||||
|
||||
let failed = await res.json();
|
||||
|
||||
if(failed.length > 0) {
|
||||
for(let k in failed) {
|
||||
let hash = failed[k];
|
||||
const item = self.grid.getRowItemBy("hash", hash);
|
||||
errorMsg = `[FAIL] ${item.title}`;
|
||||
}
|
||||
}
|
||||
|
||||
this.showStop();
|
||||
showTerminal();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -708,7 +497,7 @@ export class ModelManager {
|
||||
// self.grid.updateCell(item, "tg-column-select");
|
||||
self.grid.updateRow(item);
|
||||
}
|
||||
else if(event.detail.status == 'done') {
|
||||
else if(event.detail.status == 'batch-done') {
|
||||
self.hideStop();
|
||||
self.onQueueCompleted(event.detail);
|
||||
}
|
||||
@@ -834,7 +623,7 @@ export class ModelManager {
|
||||
|
||||
const mode = manager_instance.datasrc_combo.value;
|
||||
|
||||
const res = await fetchData(`/externalmodel/getlist?mode=${mode}`);
|
||||
const res = await fetchData(`/v2/externalmodel/getlist?mode=${mode}`);
|
||||
if (res.error) {
|
||||
this.showError("Failed to get external model list.");
|
||||
this.hideLoading();
|
||||
@@ -142,7 +142,7 @@ function node_info_copy(src, dest, connect_both, copy_shape) {
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: "Comfy.Manager.NodeFixer",
|
||||
name: "Comfy.Legacy.Manager.NodeFixer",
|
||||
beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
addMenuHandler(nodeType, function (_, options) {
|
||||
options.push({
|
||||
619
comfyui_manager/js/popover-helper.js
Normal file
619
comfyui_manager/js/popover-helper.js
Normal file
@@ -0,0 +1,619 @@
|
||||
const hasOwn = function(obj, key) {
|
||||
return Object.prototype.hasOwnProperty.call(obj, key);
|
||||
};
|
||||
|
||||
const isNum = function(num) {
|
||||
if (typeof num !== 'number' || isNaN(num)) {
|
||||
return false;
|
||||
}
|
||||
const isInvalid = function(n) {
|
||||
if (n === Number.MAX_VALUE || n === Number.MIN_VALUE || n === Number.NEGATIVE_INFINITY || n === Number.POSITIVE_INFINITY) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
if (isInvalid(num)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const toNum = (num) => {
|
||||
if (typeof (num) !== 'number') {
|
||||
num = parseFloat(num);
|
||||
}
|
||||
if (isNaN(num)) {
|
||||
num = 0;
|
||||
}
|
||||
num = Math.round(num);
|
||||
return num;
|
||||
};
|
||||
|
||||
const clamp = function(value, min, max) {
|
||||
return Math.max(min, Math.min(max, value));
|
||||
};
|
||||
|
||||
const isWindow = (obj) => {
|
||||
return Boolean(obj && obj === obj.window);
|
||||
};
|
||||
|
||||
const isDocument = (obj) => {
|
||||
return Boolean(obj && obj.nodeType === 9);
|
||||
};
|
||||
|
||||
const isElement = (obj) => {
|
||||
return Boolean(obj && obj.nodeType === 1);
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
export const toRect = (obj) => {
|
||||
if (obj) {
|
||||
return {
|
||||
left: toNum(obj.left || obj.x),
|
||||
top: toNum(obj.top || obj.y),
|
||||
width: toNum(obj.width),
|
||||
height: toNum(obj.height)
|
||||
};
|
||||
}
|
||||
return {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
};
|
||||
};
|
||||
|
||||
export const getElement = (selector) => {
|
||||
if (typeof selector === 'string' && selector) {
|
||||
if (selector.startsWith('#')) {
|
||||
return document.getElementById(selector.slice(1));
|
||||
}
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
|
||||
if (isDocument(selector)) {
|
||||
return selector.body;
|
||||
}
|
||||
if (isElement(selector)) {
|
||||
return selector;
|
||||
}
|
||||
};
|
||||
|
||||
export const getRect = (target, fixed) => {
|
||||
if (!target) {
|
||||
return toRect();
|
||||
}
|
||||
|
||||
if (isWindow(target)) {
|
||||
return {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
};
|
||||
}
|
||||
|
||||
const elem = getElement(target);
|
||||
if (!elem) {
|
||||
return toRect(target);
|
||||
}
|
||||
|
||||
const br = elem.getBoundingClientRect();
|
||||
const rect = toRect(br);
|
||||
|
||||
// fix offset
|
||||
if (!fixed) {
|
||||
rect.left += window.scrollX;
|
||||
rect.top += window.scrollY;
|
||||
}
|
||||
|
||||
rect.width = elem.offsetWidth;
|
||||
rect.height = elem.offsetHeight;
|
||||
|
||||
return rect;
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
const calculators = {
|
||||
|
||||
bottom: (info, containerRect, targetRect) => {
|
||||
info.space = containerRect.top + containerRect.height - targetRect.top - targetRect.height - info.height;
|
||||
info.top = targetRect.top + targetRect.height;
|
||||
info.left = Math.round(targetRect.left + targetRect.width * 0.5 - info.width * 0.5);
|
||||
},
|
||||
|
||||
top: (info, containerRect, targetRect) => {
|
||||
info.space = targetRect.top - info.height - containerRect.top;
|
||||
info.top = targetRect.top - info.height;
|
||||
info.left = Math.round(targetRect.left + targetRect.width * 0.5 - info.width * 0.5);
|
||||
},
|
||||
|
||||
right: (info, containerRect, targetRect) => {
|
||||
info.space = containerRect.left + containerRect.width - targetRect.left - targetRect.width - info.width;
|
||||
info.top = Math.round(targetRect.top + targetRect.height * 0.5 - info.height * 0.5);
|
||||
info.left = targetRect.left + targetRect.width;
|
||||
},
|
||||
|
||||
left: (info, containerRect, targetRect) => {
|
||||
info.space = targetRect.left - info.width - containerRect.left;
|
||||
info.top = Math.round(targetRect.top + targetRect.height * 0.5 - info.height * 0.5);
|
||||
info.left = targetRect.left - info.width;
|
||||
}
|
||||
};
|
||||
|
||||
// with order
|
||||
export const getDefaultPositions = () => {
|
||||
return Object.keys(calculators);
|
||||
};
|
||||
|
||||
const calculateSpace = (info, containerRect, targetRect) => {
|
||||
const calculator = calculators[info.position];
|
||||
calculator(info, containerRect, targetRect);
|
||||
if (info.space >= 0) {
|
||||
info.passed += 1;
|
||||
}
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
const calculateAlignOffset = (info, containerRect, targetRect, alignType, sizeType) => {
|
||||
|
||||
const popoverStart = info[alignType];
|
||||
const popoverSize = info[sizeType];
|
||||
|
||||
const containerStart = containerRect[alignType];
|
||||
const containerSize = containerRect[sizeType];
|
||||
|
||||
const targetStart = targetRect[alignType];
|
||||
const targetSize = targetRect[sizeType];
|
||||
|
||||
const targetCenter = targetStart + targetSize * 0.5;
|
||||
|
||||
// size overflow
|
||||
if (popoverSize > containerSize) {
|
||||
const overflow = (popoverSize - containerSize) * 0.5;
|
||||
info[alignType] = containerStart - overflow;
|
||||
info.offset = targetCenter - containerStart + overflow;
|
||||
return;
|
||||
}
|
||||
|
||||
const space1 = popoverStart - containerStart;
|
||||
const space2 = (containerStart + containerSize) - (popoverStart + popoverSize);
|
||||
|
||||
// both side passed, default to center
|
||||
if (space1 >= 0 && space2 >= 0) {
|
||||
if (info.passed) {
|
||||
info.passed += 2;
|
||||
}
|
||||
info.offset = popoverSize * 0.5;
|
||||
return;
|
||||
}
|
||||
|
||||
// one side passed
|
||||
if (info.passed) {
|
||||
info.passed += 1;
|
||||
}
|
||||
|
||||
if (space1 < 0) {
|
||||
const min = containerStart;
|
||||
info[alignType] = min;
|
||||
info.offset = targetCenter - min;
|
||||
return;
|
||||
}
|
||||
|
||||
// space2 < 0
|
||||
const max = containerStart + containerSize - popoverSize;
|
||||
info[alignType] = max;
|
||||
info.offset = targetCenter - max;
|
||||
|
||||
};
|
||||
|
||||
const calculateHV = (info, containerRect) => {
|
||||
if (['top', 'bottom'].includes(info.position)) {
|
||||
info.top = clamp(info.top, containerRect.top, containerRect.top + containerRect.height - info.height);
|
||||
return ['left', 'width'];
|
||||
}
|
||||
info.left = clamp(info.left, containerRect.left, containerRect.left + containerRect.width - info.width);
|
||||
return ['top', 'height'];
|
||||
};
|
||||
|
||||
const calculateOffset = (info, containerRect, targetRect) => {
|
||||
|
||||
const [alignType, sizeType] = calculateHV(info, containerRect);
|
||||
|
||||
calculateAlignOffset(info, containerRect, targetRect, alignType, sizeType);
|
||||
|
||||
info.offset = clamp(info.offset, 0, info[sizeType]);
|
||||
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
const calculateDistance = (info, previousPositionInfo) => {
|
||||
if (!previousPositionInfo) {
|
||||
return;
|
||||
}
|
||||
// no change if position no change with previous
|
||||
if (info.position === previousPositionInfo.position) {
|
||||
return;
|
||||
}
|
||||
const ax = info.left + info.width * 0.5;
|
||||
const ay = info.top + info.height * 0.5;
|
||||
const bx = previousPositionInfo.left + previousPositionInfo.width * 0.5;
|
||||
const by = previousPositionInfo.top + previousPositionInfo.height * 0.5;
|
||||
const dx = Math.abs(ax - bx);
|
||||
const dy = Math.abs(ay - by);
|
||||
info.distance = Math.round(Math.sqrt(dx * dx + dy * dy));
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
const calculatePositionInfo = (info, containerRect, targetRect, previousPositionInfo) => {
|
||||
calculateSpace(info, containerRect, targetRect);
|
||||
calculateOffset(info, containerRect, targetRect);
|
||||
calculateDistance(info, previousPositionInfo);
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
const calculateBestPosition = (containerRect, targetRect, infoMap, withOrder, previousPositionInfo) => {
|
||||
|
||||
// position space: +1
|
||||
// align space:
|
||||
// two side passed: +2
|
||||
// one side passed: +1
|
||||
|
||||
const safePassed = 3;
|
||||
|
||||
if (previousPositionInfo) {
|
||||
const prevInfo = infoMap[previousPositionInfo.position];
|
||||
if (prevInfo) {
|
||||
calculatePositionInfo(prevInfo, containerRect, targetRect);
|
||||
if (prevInfo.passed >= safePassed) {
|
||||
return prevInfo;
|
||||
}
|
||||
prevInfo.calculated = true;
|
||||
}
|
||||
}
|
||||
|
||||
const positionList = [];
|
||||
Object.values(infoMap).forEach((info) => {
|
||||
if (!info.calculated) {
|
||||
calculatePositionInfo(info, containerRect, targetRect, previousPositionInfo);
|
||||
}
|
||||
positionList.push(info);
|
||||
});
|
||||
|
||||
positionList.sort((a, b) => {
|
||||
if (a.passed !== b.passed) {
|
||||
return b.passed - a.passed;
|
||||
}
|
||||
|
||||
if (withOrder && a.passed >= safePassed && b.passed >= safePassed) {
|
||||
return a.index - b.index;
|
||||
}
|
||||
|
||||
if (a.space !== b.space) {
|
||||
return b.space - a.space;
|
||||
}
|
||||
|
||||
return a.index - b.index;
|
||||
});
|
||||
|
||||
// logTable(positionList);
|
||||
|
||||
return positionList[0];
|
||||
};
|
||||
|
||||
// const logTable = (() => {
|
||||
// let time_id;
|
||||
// return (info) => {
|
||||
// clearTimeout(time_id);
|
||||
// time_id = setTimeout(() => {
|
||||
// console.table(info);
|
||||
// }, 10);
|
||||
// };
|
||||
// })();
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
const getAllowPositions = (positions, defaultAllowPositions) => {
|
||||
if (!positions) {
|
||||
return;
|
||||
}
|
||||
if (Array.isArray(positions)) {
|
||||
positions = positions.join(',');
|
||||
}
|
||||
positions = String(positions).split(',').map((it) => it.trim().toLowerCase()).filter((it) => it);
|
||||
positions = positions.filter((it) => defaultAllowPositions.includes(it));
|
||||
if (!positions.length) {
|
||||
return;
|
||||
}
|
||||
return positions;
|
||||
};
|
||||
|
||||
const isPositionChanged = (info, previousPositionInfo) => {
|
||||
if (!previousPositionInfo) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.left !== previousPositionInfo.left) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (info.top !== previousPositionInfo.top) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
// const log = (name, time) => {
|
||||
// if (time > 0.1) {
|
||||
// console.log(name, time);
|
||||
// }
|
||||
// };
|
||||
|
||||
export const getBestPosition = (containerRect, targetRect, popoverRect, positions, previousPositionInfo) => {
|
||||
|
||||
const defaultAllowPositions = getDefaultPositions();
|
||||
let withOrder = true;
|
||||
let allowPositions = getAllowPositions(positions, defaultAllowPositions);
|
||||
if (!allowPositions) {
|
||||
allowPositions = defaultAllowPositions;
|
||||
withOrder = false;
|
||||
}
|
||||
|
||||
// console.log('withOrder', withOrder);
|
||||
|
||||
// const start_time = performance.now();
|
||||
|
||||
const infoMap = {};
|
||||
allowPositions.forEach((k, i) => {
|
||||
infoMap[k] = {
|
||||
position: k,
|
||||
index: i,
|
||||
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: popoverRect.width,
|
||||
height: popoverRect.height,
|
||||
|
||||
space: 0,
|
||||
|
||||
offset: 0,
|
||||
passed: 0,
|
||||
|
||||
distance: 0
|
||||
};
|
||||
});
|
||||
|
||||
// log('infoMap', performance.now() - start_time);
|
||||
|
||||
|
||||
const bestPosition = calculateBestPosition(containerRect, targetRect, infoMap, withOrder, previousPositionInfo);
|
||||
|
||||
// check left/top
|
||||
bestPosition.changed = isPositionChanged(bestPosition, previousPositionInfo);
|
||||
|
||||
return bestPosition;
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
const getTemplatePath = (width, height, arrowOffset, arrowSize, borderRadius) => {
|
||||
const p = (px, py) => {
|
||||
return [px, py].join(',');
|
||||
};
|
||||
|
||||
const px = function(num, alignEnd) {
|
||||
const floor = Math.floor(num);
|
||||
let n = num < floor + 0.5 ? floor + 0.5 : floor + 1.5;
|
||||
if (alignEnd) {
|
||||
n -= 1;
|
||||
}
|
||||
return n;
|
||||
};
|
||||
|
||||
const pxe = function(num) {
|
||||
return px(num, true);
|
||||
};
|
||||
|
||||
const ls = [];
|
||||
|
||||
const innerLeft = px(arrowSize);
|
||||
const innerRight = pxe(width - arrowSize);
|
||||
arrowOffset = clamp(arrowOffset, innerLeft, innerRight);
|
||||
|
||||
const innerTop = px(arrowSize);
|
||||
const innerBottom = pxe(height - arrowSize);
|
||||
|
||||
const startPoint = p(innerLeft, innerTop + borderRadius);
|
||||
const arrowPoint = p(arrowOffset, 1);
|
||||
|
||||
const LT = p(innerLeft, innerTop);
|
||||
const RT = p(innerRight, innerTop);
|
||||
|
||||
const AOT = p(arrowOffset - arrowSize, innerTop);
|
||||
const RRT = p(innerRight - borderRadius, innerTop);
|
||||
|
||||
ls.push(`M${startPoint}`);
|
||||
ls.push(`V${innerBottom - borderRadius}`);
|
||||
ls.push(`Q${p(innerLeft, innerBottom)} ${p(innerLeft + borderRadius, innerBottom)}`);
|
||||
ls.push(`H${innerRight - borderRadius}`);
|
||||
ls.push(`Q${p(innerRight, innerBottom)} ${p(innerRight, innerBottom - borderRadius)}`);
|
||||
ls.push(`V${innerTop + borderRadius}`);
|
||||
|
||||
if (arrowOffset < innerLeft + arrowSize + borderRadius) {
|
||||
ls.push(`Q${RT} ${RRT}`);
|
||||
ls.push(`H${arrowOffset + arrowSize}`);
|
||||
ls.push(`L${arrowPoint}`);
|
||||
if (arrowOffset < innerLeft + arrowSize) {
|
||||
ls.push(`L${LT}`);
|
||||
ls.push(`L${startPoint}`);
|
||||
} else {
|
||||
ls.push(`L${AOT}`);
|
||||
ls.push(`Q${LT} ${startPoint}`);
|
||||
}
|
||||
} else if (arrowOffset > innerRight - arrowSize - borderRadius) {
|
||||
if (arrowOffset > innerRight - arrowSize) {
|
||||
ls.push(`L${RT}`);
|
||||
} else {
|
||||
ls.push(`Q${RT} ${p(arrowOffset + arrowSize, innerTop)}`);
|
||||
}
|
||||
ls.push(`L${arrowPoint}`);
|
||||
ls.push(`L${AOT}`);
|
||||
ls.push(`H${innerLeft + borderRadius}`);
|
||||
ls.push(`Q${LT} ${startPoint}`);
|
||||
} else {
|
||||
ls.push(`Q${RT} ${RRT}`);
|
||||
ls.push(`H${arrowOffset + arrowSize}`);
|
||||
ls.push(`L${arrowPoint}`);
|
||||
ls.push(`L${AOT}`);
|
||||
ls.push(`H${innerLeft + borderRadius}`);
|
||||
ls.push(`Q${LT} ${startPoint}`);
|
||||
}
|
||||
return ls.join('');
|
||||
};
|
||||
|
||||
const getPathData = function(position, width, height, arrowOffset, arrowSize, borderRadius) {
|
||||
|
||||
const handlers = {
|
||||
|
||||
bottom: () => {
|
||||
const d = getTemplatePath(width, height, arrowOffset, arrowSize, borderRadius);
|
||||
return {
|
||||
d,
|
||||
transform: ''
|
||||
};
|
||||
},
|
||||
|
||||
top: () => {
|
||||
const d = getTemplatePath(width, height, width - arrowOffset, arrowSize, borderRadius);
|
||||
return {
|
||||
d,
|
||||
transform: `rotate(180,${width * 0.5},${height * 0.5})`
|
||||
};
|
||||
},
|
||||
|
||||
left: () => {
|
||||
const d = getTemplatePath(height, width, arrowOffset, arrowSize, borderRadius);
|
||||
const x = (width - height) * 0.5;
|
||||
const y = (height - width) * 0.5;
|
||||
return {
|
||||
d,
|
||||
transform: `translate(${x} ${y}) rotate(90,${height * 0.5},${width * 0.5})`
|
||||
};
|
||||
},
|
||||
|
||||
right: () => {
|
||||
const d = getTemplatePath(height, width, height - arrowOffset, arrowSize, borderRadius);
|
||||
const x = (width - height) * 0.5;
|
||||
const y = (height - width) * 0.5;
|
||||
return {
|
||||
d,
|
||||
transform: `translate(${x} ${y}) rotate(-90,${height * 0.5},${width * 0.5})`
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
return handlers[position]();
|
||||
};
|
||||
|
||||
// ===========================================================================================
|
||||
|
||||
// position style cache
|
||||
const styleCache = {
|
||||
// position: '',
|
||||
// top: {},
|
||||
// bottom: {},
|
||||
// left: {},
|
||||
// right: {}
|
||||
};
|
||||
|
||||
export const getPositionStyle = (info, options = {}) => {
|
||||
|
||||
const o = {
|
||||
bgColor: '#fff',
|
||||
borderColor: '#ccc',
|
||||
borderRadius: 5,
|
||||
arrowSize: 10
|
||||
};
|
||||
Object.keys(o).forEach((k) => {
|
||||
|
||||
if (hasOwn(options, k)) {
|
||||
const d = o[k];
|
||||
const v = options[k];
|
||||
|
||||
if (typeof d === 'string') {
|
||||
// string
|
||||
if (typeof v === 'string' && v) {
|
||||
o[k] = v;
|
||||
}
|
||||
} else {
|
||||
// number
|
||||
if (isNum(v) && v >= 0) {
|
||||
o[k] = v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
const key = [
|
||||
info.width,
|
||||
info.height,
|
||||
info.offset,
|
||||
o.arrowSize,
|
||||
o.borderRadius,
|
||||
o.bgColor,
|
||||
o.borderColor
|
||||
].join('-');
|
||||
|
||||
const positionCache = styleCache[info.position];
|
||||
if (positionCache && key === positionCache.key) {
|
||||
const st = positionCache.style;
|
||||
st.changed = styleCache.position !== info.position;
|
||||
styleCache.position = info.position;
|
||||
return st;
|
||||
}
|
||||
|
||||
// console.log(options);
|
||||
|
||||
const data = getPathData(info.position, info.width, info.height, info.offset, o.arrowSize, o.borderRadius);
|
||||
// console.log(data);
|
||||
|
||||
const viewBox = [0, 0, info.width, info.height].join(' ');
|
||||
const svg = [
|
||||
`<svg viewBox="${viewBox}" xmlns="http://www.w3.org/2000/svg">`,
|
||||
`<path d="${data.d}" fill="${o.bgColor}" stroke="${o.borderColor}" transform="${data.transform}" />`,
|
||||
'</svg>'
|
||||
].join('');
|
||||
|
||||
// console.log(svg);
|
||||
const backgroundImage = `url("data:image/svg+xml;charset=utf8,${encodeURIComponent(svg)}")`;
|
||||
|
||||
const background = `${backgroundImage} center no-repeat`;
|
||||
|
||||
const padding = `${o.arrowSize + o.borderRadius}px`;
|
||||
|
||||
const style = {
|
||||
background,
|
||||
backgroundImage,
|
||||
padding,
|
||||
changed: true
|
||||
};
|
||||
|
||||
styleCache.position = info.position;
|
||||
styleCache[info.position] = {
|
||||
key,
|
||||
style
|
||||
};
|
||||
|
||||
return style;
|
||||
};
|
||||
@@ -7,7 +7,7 @@ import { manager_instance, rebootAPI, show_message } from "./common.js";
|
||||
async function restore_snapshot(target) {
|
||||
if(SnapshotManager.instance) {
|
||||
try {
|
||||
const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" });
|
||||
const response = await api.fetchApi(`/v2/snapshot/restore?target=${target}`, { cache: "no-store" });
|
||||
|
||||
if(response.status == 403) {
|
||||
show_message('This action is not allowed with this security level configuration.');
|
||||
@@ -35,7 +35,7 @@ async function restore_snapshot(target) {
|
||||
async function remove_snapshot(target) {
|
||||
if(SnapshotManager.instance) {
|
||||
try {
|
||||
const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" });
|
||||
const response = await api.fetchApi(`/v2/snapshot/remove?target=${target}`, { cache: "no-store" });
|
||||
|
||||
if(response.status == 403) {
|
||||
show_message('This action is not allowed with this security level configuration.');
|
||||
@@ -61,7 +61,7 @@ async function remove_snapshot(target) {
|
||||
|
||||
async function save_current_snapshot() {
|
||||
try {
|
||||
const response = await api.fetchApi('/snapshot/save', { cache: "no-store" });
|
||||
const response = await api.fetchApi('/v2/snapshot/save', { cache: "no-store" });
|
||||
app.ui.dialog.close();
|
||||
return true;
|
||||
}
|
||||
@@ -76,7 +76,7 @@ async function save_current_snapshot() {
|
||||
}
|
||||
|
||||
async function getSnapshotList() {
|
||||
const response = await api.fetchApi(`/snapshot/getlist`);
|
||||
const response = await api.fetchApi(`/v2/snapshot/getlist`);
|
||||
const data = await response.json();
|
||||
return data;
|
||||
}
|
||||
84
comfyui_manager/js/workflow-metadata.js
Normal file
84
comfyui_manager/js/workflow-metadata.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Attaches metadata to the workflow on save
|
||||
* - custom node pack version to all custom nodes used in the workflow
|
||||
*
|
||||
* Example metadata:
|
||||
* "nodes": {
|
||||
* "1": {
|
||||
* type: "CheckpointLoaderSimple",
|
||||
* ...
|
||||
* properties: {
|
||||
* cnr_id: "comfy-core",
|
||||
* version: "0.3.8",
|
||||
* },
|
||||
* },
|
||||
* }
|
||||
*
|
||||
* @typedef {Object} NodeInfo
|
||||
* @property {string} ver - Version (git hash or semantic version)
|
||||
* @property {string} cnr_id - ComfyRegistry node ID
|
||||
* @property {boolean} enabled - Whether the node is enabled
|
||||
*/
|
||||
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
class WorkflowMetadataExtension {
|
||||
constructor() {
|
||||
this.name = "Comfy.CustomNodesManager.WorkflowMetadata";
|
||||
this.installedNodes = {};
|
||||
this.comfyCoreVersion = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the installed nodes info
|
||||
* @returns {Promise<Record<string, NodeInfo>>} The mapping from node name to its info.
|
||||
* ver can either be a git commit hash or a semantic version such as "1.0.0"
|
||||
* cnr_id is the id of the node in the ComfyRegistry
|
||||
* enabled is true if the node is enabled, false if it is disabled
|
||||
*/
|
||||
async getInstalledNodes() {
|
||||
const res = await api.fetchApi("/v2/customnode/installed");
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
async init() {
|
||||
this.installedNodes = await this.getInstalledNodes();
|
||||
this.comfyCoreVersion = (await api.getSystemStats()).system.comfyui_version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when any node is created
|
||||
* @param {LGraphNode} node The newly created node
|
||||
*/
|
||||
nodeCreated(node) {
|
||||
try {
|
||||
// nodeData doesn't exist if node is missing or node is frontend only node
|
||||
if (!node?.constructor?.nodeData?.python_module) return;
|
||||
|
||||
const nodeProperties = (node.properties ??= {});
|
||||
const modules = node.constructor.nodeData.python_module.split(".");
|
||||
const moduleType = modules[0];
|
||||
|
||||
if (moduleType === "custom_nodes") {
|
||||
const nodePackageName = modules[1];
|
||||
const { cnr_id, aux_id, ver } =
|
||||
this.installedNodes[nodePackageName] ??
|
||||
this.installedNodes[nodePackageName.toLowerCase()] ??
|
||||
{};
|
||||
|
||||
if (cnr_id === "comfy-core") return; // don't allow hijacking comfy-core name
|
||||
if (cnr_id) nodeProperties.cnr_id = cnr_id;
|
||||
else nodeProperties.aux_id = aux_id;
|
||||
if (ver) nodeProperties.ver = ver;
|
||||
} else if (["nodes", "comfy_extras"].includes(moduleType)) {
|
||||
nodeProperties.cnr_id = "comfy-core";
|
||||
nodeProperties.ver = this.comfyCoreVersion;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerExtension(new WorkflowMetadataExtension());
|
||||
0
comfyui_manager/legacy/__init__.py
Normal file
0
comfyui_manager/legacy/__init__.py
Normal file
3248
comfyui_manager/legacy/manager_core.py
Normal file
3248
comfyui_manager/legacy/manager_core.py
Normal file
File diff suppressed because it is too large
Load Diff
1946
comfyui_manager/legacy/manager_server.py
Normal file
1946
comfyui_manager/legacy/manager_server.py
Normal file
File diff suppressed because it is too large
Load Diff
386
comfyui_manager/legacy/share_3rdparty.py
Normal file
386
comfyui_manager/legacy/share_3rdparty.py
Normal file
@@ -0,0 +1,386 @@
|
||||
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)
|
||||
@@ -1,4 +1,5 @@
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import atexit
|
||||
@@ -11,29 +12,32 @@ import ast
|
||||
import logging
|
||||
import traceback
|
||||
|
||||
glob_path = os.path.join(os.path.dirname(__file__), "glob")
|
||||
sys.path.append(glob_path)
|
||||
|
||||
import security_check
|
||||
import manager_util
|
||||
import cm_global
|
||||
import manager_downloader
|
||||
from .common import security_check
|
||||
from .common import manager_util
|
||||
from .common import cm_global
|
||||
from .common import manager_downloader
|
||||
import folder_paths
|
||||
|
||||
import datetime
|
||||
if hasattr(datetime, 'datetime'):
|
||||
manager_util.add_python_path_to_env()
|
||||
|
||||
import datetime as dt
|
||||
|
||||
if hasattr(dt, 'datetime'):
|
||||
from datetime import datetime as dt_datetime
|
||||
|
||||
def current_timestamp():
|
||||
return datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
|
||||
return dt_datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
|
||||
else:
|
||||
# NOTE: Occurs in some Mac environments.
|
||||
import time
|
||||
logging.error(f"[ComfyUI-Manager] fallback timestamp mode\n datetime module is invalid: '{datetime.__file__}'")
|
||||
logging.error(f"[ComfyUI-Manager] fallback timestamp mode\n datetime module is invalid: '{dt.__file__}'")
|
||||
|
||||
def current_timestamp():
|
||||
return str(time.time()).split('.')[0]
|
||||
|
||||
security_check.security_check()
|
||||
|
||||
cm_global.pip_blacklist = ['torch', 'torchsde', 'torchvision']
|
||||
cm_global.pip_blacklist = {'torch', 'torchsde', 'torchvision'}
|
||||
cm_global.pip_downgrade_blacklist = ['torch', 'torchsde', 'torchvision', 'transformers', 'safetensors', 'kornia']
|
||||
|
||||
|
||||
@@ -57,35 +61,21 @@ def is_import_failed_extension(name):
|
||||
return name in import_failed_extensions
|
||||
|
||||
|
||||
def check_file_logging():
|
||||
global enable_file_logging
|
||||
try:
|
||||
import configparser
|
||||
config = configparser.ConfigParser()
|
||||
config.read(manager_config_path)
|
||||
default_conf = config['default']
|
||||
|
||||
if 'file_logging' in default_conf and default_conf['file_logging'].lower() == 'false':
|
||||
enable_file_logging = False
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
check_file_logging()
|
||||
|
||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
comfy_base_path = os.environ.get('COMFYUI_FOLDERS_BASE_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
# legacy env var
|
||||
comfy_path = os.environ.get('COMFYUI_PATH')
|
||||
|
||||
if comfy_path is None:
|
||||
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
||||
try:
|
||||
comfy_path = os.path.abspath(os.path.dirname(sys.modules['__main__'].__file__))
|
||||
os.environ['COMFYUI_PATH'] = comfy_path
|
||||
except:
|
||||
print("[ComfyUI-Manager] environment variable 'COMFYUI_PATH' is not specified.")
|
||||
exit(-1)
|
||||
|
||||
if comfy_base_path is None:
|
||||
comfy_base_path = comfy_path
|
||||
|
||||
|
||||
sys.__comfyui_manager_register_message_collapse = register_message_collapse
|
||||
sys.__comfyui_manager_is_import_failed_extension = is_import_failed_extension
|
||||
cm_global.register_api('cm.register_message_collapse', register_message_collapse)
|
||||
@@ -97,6 +87,7 @@ comfyui_manager_path = os.path.abspath(os.path.dirname(__file__))
|
||||
custom_nodes_base_path = folder_paths.get_folder_paths('custom_nodes')[0]
|
||||
manager_files_path = os.path.abspath(os.path.join(folder_paths.get_user_directory(), 'default', 'ComfyUI-Manager'))
|
||||
manager_pip_overrides_path = os.path.join(manager_files_path, "pip_overrides.json")
|
||||
manager_pip_blacklist_path = os.path.join(manager_files_path, "pip_blacklist.list")
|
||||
restore_snapshot_path = os.path.join(manager_files_path, "startup-scripts", "restore-snapshot.json")
|
||||
manager_config_path = os.path.join(manager_files_path, 'config.ini')
|
||||
|
||||
@@ -107,26 +98,41 @@ default_conf = {}
|
||||
|
||||
def read_config():
|
||||
global default_conf
|
||||
import configparser
|
||||
config = configparser.ConfigParser()
|
||||
config.read(manager_config_path)
|
||||
default_conf = config['default']
|
||||
try:
|
||||
import configparser
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read(manager_config_path)
|
||||
default_conf = config['default']
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def read_uv_mode():
|
||||
if 'use_uv' in default_conf:
|
||||
manager_util.use_uv = default_conf['use_uv']
|
||||
manager_util.use_uv = default_conf['use_uv'].lower() == 'true'
|
||||
|
||||
def check_file_logging():
|
||||
global enable_file_logging
|
||||
if 'file_logging' in default_conf and default_conf['file_logging'].lower() == 'false':
|
||||
enable_file_logging = False
|
||||
|
||||
|
||||
read_config()
|
||||
read_uv_mode()
|
||||
check_file_logging()
|
||||
|
||||
|
||||
cm_global.pip_overrides = {'numpy': 'numpy<2', 'ultralytics': 'ultralytics==8.3.40'}
|
||||
cm_global.pip_overrides = {'numpy': 'numpy<2'}
|
||||
if os.path.exists(manager_pip_overrides_path):
|
||||
with open(manager_pip_overrides_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||
cm_global.pip_overrides = json.load(json_file)
|
||||
cm_global.pip_overrides['numpy'] = 'numpy<2'
|
||||
cm_global.pip_overrides['ultralytics'] = 'ultralytics==8.3.40' # for security
|
||||
|
||||
|
||||
if os.path.exists(manager_pip_blacklist_path):
|
||||
with open(manager_pip_blacklist_path, 'r', encoding="UTF-8", errors="ignore") as f:
|
||||
for x in f.readlines():
|
||||
y = x.strip()
|
||||
if y != '':
|
||||
cm_global.pip_blacklist.add(y)
|
||||
|
||||
|
||||
def remap_pip_package(pkg):
|
||||
@@ -385,7 +391,11 @@ try:
|
||||
def emit(self, record):
|
||||
global is_start_mode
|
||||
|
||||
message = record.getMessage()
|
||||
try:
|
||||
message = record.getMessage()
|
||||
except Exception as e:
|
||||
message = f"<<logging error>>: {record} - {e}"
|
||||
original_stderr.write(message)
|
||||
|
||||
if is_start_mode:
|
||||
match = re.search(pat_import_fail, message)
|
||||
@@ -428,28 +438,33 @@ except Exception as e:
|
||||
print(f"[ComfyUI-Manager] Logging failed: {e}")
|
||||
|
||||
|
||||
try:
|
||||
import git # noqa: F401
|
||||
import toml # noqa: F401
|
||||
except ModuleNotFoundError:
|
||||
my_path = os.path.dirname(__file__)
|
||||
requirements_path = os.path.join(my_path, "requirements.txt")
|
||||
|
||||
print("## ComfyUI-Manager: installing dependencies. (GitPython)")
|
||||
def ensure_dependencies():
|
||||
try:
|
||||
result = subprocess.check_output(manager_util.make_pip_cmd(['install', '-r', requirements_path]))
|
||||
except subprocess.CalledProcessError:
|
||||
print("## [ERROR] ComfyUI-Manager: Attempting to reinstall dependencies using an alternative method.")
|
||||
try:
|
||||
result = subprocess.check_output(manager_util.make_pip_cmd(['install', '--user', '-r', requirements_path]))
|
||||
except subprocess.CalledProcessError:
|
||||
print("## [ERROR] ComfyUI-Manager: Failed to install the GitPython package in the correct Python environment. Please install it manually in the appropriate environment. (You can seek help at https://app.element.io/#/room/%23comfyui_space%3Amatrix.org)")
|
||||
import git # noqa: F401
|
||||
import toml # noqa: F401
|
||||
import rich # noqa: F401
|
||||
import chardet # noqa: F401
|
||||
except ModuleNotFoundError:
|
||||
my_path = os.path.dirname(__file__)
|
||||
requirements_path = os.path.join(my_path, "requirements.txt")
|
||||
|
||||
try:
|
||||
print("## ComfyUI-Manager: installing dependencies done.")
|
||||
except:
|
||||
# maybe we should sys.exit() here? there is at least two screens worth of error messages still being pumped after our error messages
|
||||
print("## [ERROR] ComfyUI-Manager: GitPython package seems to be installed, but failed to load somehow. Make sure you have a working git client installed")
|
||||
print("## ComfyUI-Manager: installing dependencies. (GitPython)")
|
||||
try:
|
||||
subprocess.check_output(manager_util.make_pip_cmd(['install', '-r', requirements_path]))
|
||||
except subprocess.CalledProcessError:
|
||||
print("## [ERROR] ComfyUI-Manager: Attempting to reinstall dependencies using an alternative method.")
|
||||
try:
|
||||
subprocess.check_output(manager_util.make_pip_cmd(['install', '--user', '-r', requirements_path]))
|
||||
except subprocess.CalledProcessError:
|
||||
print("## [ERROR] ComfyUI-Manager: Failed to install the GitPython package in the correct Python environment. Please install it manually in the appropriate environment. (You can seek help at https://app.element.io/#/room/%23comfyui_space%3Amatrix.org)")
|
||||
|
||||
try:
|
||||
print("## ComfyUI-Manager: installing dependencies done.")
|
||||
except:
|
||||
# maybe we should sys.exit() here? there is at least two screens worth of error messages still being pumped after our error messages
|
||||
print("## [ERROR] ComfyUI-Manager: GitPython package seems to be installed, but failed to load somehow. Make sure you have a working git client installed")
|
||||
|
||||
ensure_dependencies()
|
||||
|
||||
|
||||
print("** ComfyUI startup time:", current_timestamp())
|
||||
@@ -497,7 +512,7 @@ check_bypass_ssl()
|
||||
# Perform install
|
||||
processed_install = set()
|
||||
script_list_path = os.path.join(folder_paths.user_directory, "default", "ComfyUI-Manager", "startup-scripts", "install-scripts.txt")
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages())
|
||||
pip_fixer = manager_util.PIPFixer(manager_util.get_installed_packages(), comfy_path, manager_files_path)
|
||||
|
||||
|
||||
def is_installed(name):
|
||||
@@ -604,17 +619,18 @@ def execute_lazy_install_script(repo_path, executable):
|
||||
|
||||
if os.path.exists(requirements_path):
|
||||
print(f"Install: pip packages for '{repo_path}'")
|
||||
with open(requirements_path, "r") as requirements_file:
|
||||
for line in requirements_file:
|
||||
package_name = remap_pip_package(line.strip())
|
||||
if package_name and not is_installed(package_name):
|
||||
if '--index-url' in package_name:
|
||||
s = package_name.split('--index-url')
|
||||
install_cmd = manager_util.make_pip_cmd(["install", s[0].strip(), '--index-url', s[1].strip()])
|
||||
else:
|
||||
install_cmd = manager_util.make_pip_cmd(["install", package_name])
|
||||
|
||||
process_wrap(install_cmd, repo_path)
|
||||
lines = manager_util.robust_readlines(requirements_path)
|
||||
for line in lines:
|
||||
package_name = remap_pip_package(line.strip())
|
||||
if package_name and not is_installed(package_name):
|
||||
if '--index-url' in package_name:
|
||||
s = package_name.split('--index-url')
|
||||
install_cmd = manager_util.make_pip_cmd(["install", s[0].strip(), '--index-url', s[1].strip()])
|
||||
else:
|
||||
install_cmd = manager_util.make_pip_cmd(["install", package_name])
|
||||
|
||||
process_wrap(install_cmd, repo_path)
|
||||
|
||||
if os.path.exists(install_script_path) and f'{repo_path}/install.py' not in processed_install:
|
||||
processed_install.add(f'{repo_path}/install.py')
|
||||
@@ -678,19 +694,43 @@ def execute_lazy_cnr_switch(target, zip_url, from_path, to_path, no_deps, custom
|
||||
file.write('\n'.join(list(extracted)))
|
||||
|
||||
|
||||
def execute_migration(moves):
|
||||
import shutil
|
||||
for x in moves:
|
||||
if os.path.exists(x[0]) and not os.path.exists(x[1]):
|
||||
shutil.move(x[0], x[1])
|
||||
print(f"[ComfyUI-Manager] MIGRATION: '{x[0]}' -> '{x[1]}'")
|
||||
script_executed = False
|
||||
|
||||
|
||||
# Check if script_list_path exists
|
||||
if os.path.exists(script_list_path):
|
||||
def execute_startup_script():
|
||||
global script_executed
|
||||
print("\n#######################################################################")
|
||||
print("[ComfyUI-Manager] Starting dependency installation/(de)activation for the extension\n")
|
||||
|
||||
custom_nodelist_cache = None
|
||||
|
||||
def get_custom_node_paths():
|
||||
nonlocal custom_nodelist_cache
|
||||
if custom_nodelist_cache is None:
|
||||
custom_nodelist_cache = set()
|
||||
for base in folder_paths.get_folder_paths('custom_nodes'):
|
||||
for x in os.listdir(base):
|
||||
fullpath = os.path.join(base, x)
|
||||
if os.path.isdir(fullpath):
|
||||
custom_nodelist_cache.add(fullpath)
|
||||
|
||||
return custom_nodelist_cache
|
||||
|
||||
def execute_lazy_delete(path):
|
||||
# Validate to prevent arbitrary paths from being deleted
|
||||
if path not in get_custom_node_paths():
|
||||
logging.error(f"## ComfyUI-Manager: The scheduled '{path}' is not a custom node path, so the deletion has been canceled.")
|
||||
return
|
||||
|
||||
if not os.path.exists(path):
|
||||
logging.info(f"## ComfyUI-Manager: SKIP-DELETE => '{path}' (already deleted)")
|
||||
return
|
||||
|
||||
try:
|
||||
shutil.rmtree(path)
|
||||
logging.info(f"## ComfyUI-Manager: DELETE => '{path}'")
|
||||
except Exception as e:
|
||||
logging.error(f"## ComfyUI-Manager: Failed to delete '{path}' ({e})")
|
||||
|
||||
executed = set()
|
||||
# Read each line from the file and convert it to a list using eval
|
||||
with open(script_list_path, 'r', encoding="UTF-8", errors="ignore") as file:
|
||||
@@ -711,8 +751,8 @@ if os.path.exists(script_list_path):
|
||||
execute_lazy_cnr_switch(script[0], script[2], script[3], script[4], script[5], script[6])
|
||||
execute_lazy_install_script(script[3], script[7])
|
||||
|
||||
elif script[1] == "#LAZY-MIGRATION":
|
||||
execute_migration(script[2])
|
||||
elif script[1] == "#LAZY-DELETE-NODEPACK":
|
||||
execute_lazy_delete(script[2])
|
||||
|
||||
elif os.path.exists(script[0]):
|
||||
if script[1] == "#FORCE":
|
||||
@@ -722,7 +762,7 @@ if os.path.exists(script_list_path):
|
||||
continue
|
||||
|
||||
print(f"\n## ComfyUI-Manager: EXECUTE => {script[1:]}")
|
||||
print(f"\n## Execute install/(de)activation script for '{script[0]}'")
|
||||
print(f"\n## Execute management script for '{script[0]}'")
|
||||
|
||||
new_env = os.environ.copy()
|
||||
if 'COMFYUI_FOLDERS_BASE_PATH' not in new_env:
|
||||
@@ -730,31 +770,64 @@ if os.path.exists(script_list_path):
|
||||
exit_code = process_wrap(script[1:], script[0], env=new_env)
|
||||
|
||||
if exit_code != 0:
|
||||
print(f"install/(de)activation script failed: {script[0]}")
|
||||
print(f"management script failed: {script[0]}")
|
||||
else:
|
||||
print(f"\n## ComfyUI-Manager: CANCELED => {script[1:]}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[ERROR] Failed to execute install/(de)activation script: {line} / {e}")
|
||||
print(f"[ERROR] Failed to execute management script: {line} / {e}")
|
||||
|
||||
# Remove the script_list_path file
|
||||
if os.path.exists(script_list_path):
|
||||
script_executed = True
|
||||
os.remove(script_list_path)
|
||||
|
||||
print("\n[ComfyUI-Manager] Startup script completed.")
|
||||
print("#######################################################################\n")
|
||||
|
||||
|
||||
# Check if script_list_path exists
|
||||
if os.path.exists(script_list_path):
|
||||
execute_startup_script()
|
||||
|
||||
|
||||
pip_fixer.fix_broken()
|
||||
|
||||
del processed_install
|
||||
del pip_fixer
|
||||
manager_util.clear_pip_cache()
|
||||
|
||||
if script_executed:
|
||||
# Restart
|
||||
print("[ComfyUI-Manager] Restarting to reapply dependency installation.")
|
||||
|
||||
if '__COMFY_CLI_SESSION__' in os.environ:
|
||||
with open(os.path.join(os.environ['__COMFY_CLI_SESSION__'] + '.reboot'), 'w'):
|
||||
pass
|
||||
|
||||
print("--------------------------------------------------------------------------\n")
|
||||
exit(0)
|
||||
else:
|
||||
sys_argv = sys.argv.copy()
|
||||
|
||||
if sys_argv[0].endswith("__main__.py"): # this is a python module
|
||||
module_name = os.path.basename(os.path.dirname(sys_argv[0]))
|
||||
cmds = [sys.executable, '-m', module_name] + sys_argv[1:]
|
||||
elif sys.platform.startswith('win32'):
|
||||
cmds = ['"' + sys.executable + '"', '"' + sys_argv[0] + '"'] + sys_argv[1:]
|
||||
else:
|
||||
cmds = [sys.executable] + sys_argv
|
||||
|
||||
print(f"Command: {cmds}", flush=True)
|
||||
print("--------------------------------------------------------------------------\n")
|
||||
|
||||
os.execv(sys.executable, cmds)
|
||||
|
||||
|
||||
def check_windows_event_loop_policy():
|
||||
try:
|
||||
import configparser
|
||||
config = configparser.ConfigParser()
|
||||
config = configparser.ConfigParser(strict=False)
|
||||
config.read(manager_config_path)
|
||||
default_conf = config['default']
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -121,8 +121,9 @@ ComfyUI-Loopchain
|
||||
* If no file exists at the snapshot path, it is implicitly assumed to be in ComfyUI-Manager/snapshots.
|
||||
* `--pip-non-url`: Restore for pip packages registered on PyPI.
|
||||
* `--pip-non-local-url`: Restore for pip packages registered at web URLs.
|
||||
* `--pip-local-url`: Restore for pip packages specified by local paths.
|
||||
|
||||
* `--pip-local-url`: Restore for pip packages specified by local paths.
|
||||
* `--user-directory`: Set the user directory.
|
||||
* `--restore-to`: The path where the restored custom nodes will be installed. (When this option is applied, only the custom nodes installed in the target path are recognized as installed.)
|
||||
|
||||
### 5. CLI Only Mode
|
||||
|
||||
|
||||
@@ -123,7 +123,8 @@ ComfyUI-Loopchain
|
||||
* `--pip-non-url`: PyPI 에 등록된 pip 패키지들에 대해서 복구를 수행
|
||||
* `--pip-non-local-url`: web URL에 등록된 pip 패키지들에 대해서 복구를 수행
|
||||
* `--pip-local-url`: local 경로를 지정하고 있는 pip 패키지들에 대해서 복구를 수행
|
||||
|
||||
* `--user-directory`: 사용자 디렉토리 설정
|
||||
* `--restore-to`: 복구될 커스텀 노드가 설치될 경로. (이 옵션을 적용할 경우 오직 대상 경로에 설치된 custom nodes 만 설치된 것으로 인식함.)
|
||||
|
||||
### 5. CLI only mode
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
9692
github-stats.json
9692
github-stats.json
File diff suppressed because it is too large
Load Diff
@@ -1,94 +0,0 @@
|
||||
/**
|
||||
* Attaches metadata to the workflow on save
|
||||
* - custom node pack version to all custom nodes used in the workflow
|
||||
*
|
||||
* Example metadata:
|
||||
"extra": {
|
||||
"node_versions": {
|
||||
"comfy-core": "v0.3.8-4-g0b2eb7f",
|
||||
"comfyui-easy-use": "1.2.5"
|
||||
}
|
||||
},
|
||||
*/
|
||||
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
class WorkflowMetadataExtension {
|
||||
constructor() {
|
||||
this.name = "Comfy.CustomNodesManager.WorkflowMetadata";
|
||||
this.installedNodes = {};
|
||||
this.comfyCoreVersion = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the installed nodes info
|
||||
* @returns {Promise<Record<string, {ver: string, cnr_id: string, enabled: boolean}>>} The mapping from node name to its info.
|
||||
* ver can either be a git commit hash or a semantic version such as "1.0.0"
|
||||
* cnr_id is the id of the node in the ComfyRegistry
|
||||
* enabled is true if the node is enabled, false if it is disabled
|
||||
*/
|
||||
async getInstalledNodes() {
|
||||
const res = await api.fetchApi("/customnode/installed");
|
||||
return await res.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the node versions for the given graph
|
||||
* @param {LGraph} graph The graph to get the node versions for
|
||||
* @returns {Promise<Record<string, string>>} The mapping from node name to version
|
||||
*/
|
||||
getGraphNodeVersions(graph) {
|
||||
const nodeVersions = {};
|
||||
for (const node of graph.nodes) {
|
||||
const nodeData = node.constructor.nodeData;
|
||||
// Frontend only nodes don't have nodeData
|
||||
if (!nodeData) {
|
||||
continue;
|
||||
}
|
||||
const modules = nodeData.python_module.split(".");
|
||||
|
||||
if (modules[0] === "custom_nodes") {
|
||||
const nodePackageName = modules[1];
|
||||
const nodeInfo =
|
||||
this.installedNodes[nodePackageName] ??
|
||||
this.installedNodes[nodePackageName.toLowerCase()];
|
||||
if (nodeInfo) {
|
||||
nodeVersions[nodePackageName] = nodeInfo.ver;
|
||||
}
|
||||
} else if (["nodes", "comfy_extras"].includes(modules[0])) {
|
||||
nodeVersions["comfy-core"] = this.comfyCoreVersion;
|
||||
} else {
|
||||
console.warn(`Unknown node source: ${nodeData.python_module}`);
|
||||
}
|
||||
}
|
||||
return nodeVersions;
|
||||
}
|
||||
|
||||
async init() {
|
||||
const extension = this;
|
||||
this.installedNodes = await this.getInstalledNodes();
|
||||
this.comfyCoreVersion = (await api.getSystemStats()).system.comfyui_version;
|
||||
|
||||
// Attach metadata when app.graphToPrompt is called.
|
||||
const originalSerialize = LGraph.prototype.serialize;
|
||||
LGraph.prototype.serialize = function () {
|
||||
const workflow = originalSerialize.apply(this, arguments);
|
||||
|
||||
// Add metadata to the workflow
|
||||
if (!workflow.extra) {
|
||||
workflow.extra = {};
|
||||
}
|
||||
const graph = this;
|
||||
try {
|
||||
workflow.extra["node_versions"] = extension.getGraphNodeVersions(graph);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
return workflow;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
app.registerExtension(new WorkflowMetadataExtension());
|
||||
323
model-list.json
323
model-list.json
@@ -209,28 +209,6 @@
|
||||
"url": "https://huggingface.co/stabilityai/stable-diffusion-x4-upscaler/resolve/main/x4-upscaler-ema.safetensors",
|
||||
"size": "3.53GB"
|
||||
},
|
||||
{
|
||||
"name": "Inswapper-fp16 (face swap)",
|
||||
"type": "insightface",
|
||||
"base": "inswapper",
|
||||
"save_path": "insightface",
|
||||
"description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)",
|
||||
"reference": "https://github.com/facefusion/facefusion-assets",
|
||||
"filename": "inswapper_128_fp16.onnx",
|
||||
"url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128_fp16.onnx",
|
||||
"size": "277.7MB"
|
||||
},
|
||||
{
|
||||
"name": "Inswapper (face swap)",
|
||||
"type": "insightface",
|
||||
"base": "inswapper",
|
||||
"save_path": "insightface",
|
||||
"description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)",
|
||||
"reference": "https://github.com/facefusion/facefusion-assets",
|
||||
"filename": "inswapper_128.onnx",
|
||||
"url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx",
|
||||
"size": "555.3MB"
|
||||
},
|
||||
{
|
||||
"name": "Deepbump",
|
||||
"type": "deepbump",
|
||||
@@ -1090,18 +1068,28 @@
|
||||
"size": "19.1GB"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name": "comfyanonymous/clip_l",
|
||||
"name": "Comfy-Org/clip_l",
|
||||
"type": "clip",
|
||||
"base": "clip",
|
||||
"save_path": "default",
|
||||
"description": "clip_l model",
|
||||
"reference": "https://huggingface.co/comfyanonymous/flux_text_encoders/tree/main",
|
||||
"description": "clip_l model (for SD1.x, SD2.x, SDXL, SD3.5, FLUX.1, HunyuanVideo, ...) ",
|
||||
"reference": "https://huggingface.co/Comfy-Org/stable-diffusion-3.5-fp8",
|
||||
"filename": "clip_l.safetensors",
|
||||
"url": "https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/clip_l.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/stable-diffusion-3.5-fp8/resolve/main/text_encoders/clip_l.safetensors",
|
||||
"size": "246MB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/clip_g",
|
||||
"type": "clip",
|
||||
"base": "clip",
|
||||
"save_path": "default",
|
||||
"description": "clip_g model (for SDXL, SD3.5)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/stable-diffusion-3.5-fp8",
|
||||
"filename": "clip_g.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/stable-diffusion-3.5-fp8/resolve/main/text_encoders/clip_g.safetensors",
|
||||
"size": "1.39GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "v1-5-pruned-emaonly.ckpt",
|
||||
@@ -3972,6 +3960,17 @@
|
||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/vae/hunyuan_video_vae_bf16.safetensors",
|
||||
"size": "493MB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||
"type": "diffusion_model",
|
||||
"base": "Hunyuan Video",
|
||||
"save_path": "diffusion_models/hunyuan_video",
|
||||
"description": "Huyuan Video Image2Video diffusion model. repackaged version.",
|
||||
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
||||
"filename": "hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/diffusion_models/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||
"size": "25.6GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/llava_llama3_fp8_scaled.safetensors",
|
||||
@@ -3995,6 +3994,17 @@
|
||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/text_encoders/llava_llama3_fp16.safetensors",
|
||||
"size": "16.1GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/llava_llama3_vision.safetensors",
|
||||
"type": "clip_vision",
|
||||
"base": "LLaVA-Llama-3",
|
||||
"save_path": "text_encoders",
|
||||
"description": "llava_llama3_vision clip vison model. This is required for using Hunyuan Video Image2Video.",
|
||||
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
||||
"filename": "llava_llama3_vision.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/clip_vision/llava_llama3_vision.safetensors",
|
||||
"size": "649MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "FLUX.1 [Schnell] Diffusion model",
|
||||
@@ -4559,6 +4569,17 @@
|
||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.1.safetensors",
|
||||
"size": "5.72GB"
|
||||
},
|
||||
{
|
||||
"name": "LTX-Video 2B v0.9.5 Checkpoint",
|
||||
"type": "checkpoint",
|
||||
"base": "LTX-Video",
|
||||
"save_path": "checkpoints/LTXV",
|
||||
"description": "LTX-Video is the first DiT-based video generation model capable of generating high-quality videos in real-time. It produces 24 FPS videos at a 768x512 resolution faster than they can be watched. Trained on a large-scale dataset of diverse videos, the model generates high-resolution videos with realistic and varied content.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||
"filename": "ltx-video-2b-v0.9.5.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.5.safetensors",
|
||||
"size": "6.34GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "XLabs-AI/flux-canny-controlnet-v3.safetensors",
|
||||
@@ -4684,6 +4705,254 @@
|
||||
"filename": "customnet_inpaint_v1.pt",
|
||||
"url": "https://huggingface.co/TencentARC/CustomNet/resolve/main/customnet_inpaint_v1.pt",
|
||||
"size": "5.71GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "deepseek-ai/Janus-Pro-1B",
|
||||
"type": "Janus-Pro",
|
||||
"base": "Janus-Pro",
|
||||
"save_path": "Janus-Pro",
|
||||
"description": "[SNAPSHOT] Janus-Pro-1B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
||||
"reference": "https://huggingface.co/deepseek-ai/Janus-Pro-1B",
|
||||
"filename": "<huggingface>",
|
||||
"url": "deepseek-ai/Janus-Pro-1B",
|
||||
"size": "7.8GB"
|
||||
},
|
||||
{
|
||||
"name": "deepseek-ai/Janus-Pro-7B",
|
||||
"type": "Janus-Pro",
|
||||
"base": "Janus-Pro",
|
||||
"save_path": "Janus-Pro",
|
||||
"description": "[SNAPSHOT] Janus-Pro-7B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
||||
"reference": "https://huggingface.co/deepseek-ai/Janus-Pro-7B",
|
||||
"filename": "<huggingface>",
|
||||
"url": "deepseek-ai/Janus-Pro-7B",
|
||||
"size": "14.85GB"
|
||||
},
|
||||
{
|
||||
"name": "kolors/vae/diffusion_pytorch_model.fp16.safetensors",
|
||||
"type": "VAE",
|
||||
"base": "Kolors",
|
||||
"save_path": "vae/kolors",
|
||||
"description": "Kolors VAE",
|
||||
"reference": "https://huggingface.co/Kwai-Kolors/Kolors",
|
||||
"filename": "diffusion_pytorch_model.fp16.safetensors",
|
||||
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.fp16.safetensors",
|
||||
"size": "167MB"
|
||||
},
|
||||
{
|
||||
"name": "kolors/vae/diffusion_pytorch_model.safetensors",
|
||||
"type": "VAE",
|
||||
"base": "Kolors",
|
||||
"save_path": "vae/kolors",
|
||||
"description": "Kolors VAE",
|
||||
"reference": "https://huggingface.co/Kwai-Kolors/Kolors",
|
||||
"filename": "diffusion_pytorch_model.safetensors",
|
||||
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.safetensors",
|
||||
"size": "335MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (bf16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 480p 14B (bf16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_480p_14B_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_bf16.safetensors",
|
||||
"size": "32.8GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_480p_14B_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp16.safetensors",
|
||||
"size": "32.8GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_e4m3fn)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp8_e4m3fn)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors",
|
||||
"size": "16.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_scaled)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp8_scaled)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_480p_14B_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_scaled.safetensors",
|
||||
"size": "16.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (bf16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 720p 14B (bf16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_720p_14B_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_bf16.safetensors",
|
||||
"size": "32.8GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_720p_14B_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp16.safetensors",
|
||||
"size": "32.8GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_e4m3fn)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp8_e4m3fn)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors",
|
||||
"size": "16.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_scaled)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp8_scaled)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_720p_14B_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_scaled.safetensors",
|
||||
"size": "16.4GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 1.3B (bf16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 1.3B (bf16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_1.3B_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_bf16.safetensors",
|
||||
"size": "2.84GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 1.3B (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 1.3B (fp16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_1.3B_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_fp16.safetensors",
|
||||
"size": "2.84GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 14B (bf16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 14B (bf16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_14B_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_bf16.safetensors",
|
||||
"size": "28.6GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 14B (fp16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_14B_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp16.safetensors",
|
||||
"size": "28.6GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp8_e4m3fn)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 14B (fp8_e4m3fn)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_14B_fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_e4m3fn.safetensors",
|
||||
"size": "14.3GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp8_scaled)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 14B (fp8_scaled)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_14B_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_scaled.safetensors",
|
||||
"size": "14.3GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 VAE",
|
||||
"type": "vae",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "vae",
|
||||
"description": "Wan2.1 VAE model",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan_2.1_vae.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/vae/wan_2.1_vae.safetensors",
|
||||
"size": "254MB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/clip_vision_h.safetensors",
|
||||
"type": "clip_vision",
|
||||
"base": "clip_vision_h",
|
||||
"save_path": "clip_vision",
|
||||
"description": "clip_vision_h model for Wan2.1",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "clip_vision_h.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/clip_vision/clip_vision_h.safetensors",
|
||||
"size": "1.26GB"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/umt5_xxl_fp16.safetensors",
|
||||
"type": "clip",
|
||||
"base": "umt5_xxl",
|
||||
"save_path": "text_encoders",
|
||||
"description": "umt5_xxl_fp16 text encoder for Wan2.1",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "umt5_xxl_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp16.safetensors",
|
||||
"size": "11.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
||||
"type": "clip",
|
||||
"base": "umt5_xxl",
|
||||
"save_path": "text_encoders",
|
||||
"description": "umt5_xxl_fp8_e4m3fn_scaled text encoder for Wan2.1",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
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": "SanDiegoDude",
|
||||
"title": "ComfyUI-HiDream-Sampler [WIP]",
|
||||
"reference": "https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler",
|
||||
"files": [
|
||||
"https://github.com/SanDiegoDude/ComfyUI-HiDream-Sampler"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A collection of enhanced nodes for ComfyUI that provide powerful additional functionality to your workflows.\nNOTE: The files in the repo are not organized."
|
||||
},
|
||||
{
|
||||
"author": "PramaLLC",
|
||||
"title": "ComfyUI BEN - Background Erase Network",
|
||||
@@ -129,6 +139,16 @@
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A forked version of ComfyUI_ExtraModels. (modified by Efficient-Large-Model)"
|
||||
},
|
||||
{
|
||||
"author": "Pablerdo",
|
||||
"title": "ComfyUI-PSNodes",
|
||||
"reference": "https://github.com/Pablerdo/ComfyUI-PSNodes",
|
||||
"files": [
|
||||
"https://github.com/Pablerdo/ComfyUI-PSNodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A fork of KJNodes for ComfyUI.\nVarious quality of life -nodes for ComfyUI, mostly just visual stuff to improve usability"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -8,12 +8,499 @@
|
||||
"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."
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
"author": "AI2lab",
|
||||
"title": "comfyUI-tool-2lab [REMOVED]",
|
||||
"id": "tool-2lab",
|
||||
"reference": "https://github.com/AI2lab/comfyUI-tool-2lab",
|
||||
"files": [
|
||||
"https://github.com/AI2lab/comfyUI-tool-2lab"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "tool set for developing workflow and publish to web api server"
|
||||
},
|
||||
{
|
||||
"author": "AI2lab",
|
||||
"title": "comfyUI-DeepSeek-2lab [REMOVED]",
|
||||
"id": "deepseek",
|
||||
"reference": "https://github.com/AI2lab/comfyUI-DeepSeek-2lab",
|
||||
"files": [
|
||||
"https://github.com/AI2lab/comfyUI-DeepSeek-2lab"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Unofficial implementation of DeepSeek for ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "AI2lab",
|
||||
"title": "comfyUI-kling-api-2lab [REMOVED]",
|
||||
"reference": "https://github.com/AI2lab/comfyUI-kling-api-2lab",
|
||||
"files": [
|
||||
"https://github.com/AI2lab/comfyUI-kling-api-2lab"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Unofficial implementation of KLing for ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "ZhiHui6",
|
||||
"title": "comfyui_zhihui_nodes [REMOVED]",
|
||||
"reference": "https://github.com/ZhiHui6/comfyui_zhihui_nodes",
|
||||
"files": [
|
||||
"https://github.com/ZhiHui6/comfyui_zhihui_nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: Prompt Preset, Video Batch Loader, Video Combiner"
|
||||
},
|
||||
{
|
||||
"author": "ImagineerNL",
|
||||
"title": "comfyui_potrace_svg [REMOVED]",
|
||||
"reference": "https://github.com/ImagineerNL/comfyui_potrace_svg",
|
||||
"files": [
|
||||
"https://github.com/ImagineerNL/comfyui_potrace_svg"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This project converts raster images into SVG format using the Potrace library."
|
||||
},
|
||||
{
|
||||
"author": "kayselmecnun",
|
||||
"title": "ComfyUI-Qwen-25-VL [REMOVED]",
|
||||
"reference": "https://github.com/kayselmecnun/ComfyUI-Qwen-25-VL",
|
||||
"files": [
|
||||
"https://github.com/kayselmecnun/ComfyUI-Qwen-25-VL"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A custom Comfy UI node for using Qwen2.5-VL-3B/7B-Instruct models"
|
||||
},
|
||||
{
|
||||
"author": "IfnotFr",
|
||||
"title": "⚡ ComfyUI Connect [REMOVED]",
|
||||
"reference": "https://github.com/IfnotFr/ComfyUI-Connect",
|
||||
"files": [
|
||||
"https://github.com/IfnotFr/ComfyUI-Connect"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Transform your ComfyUI into a powerful API, exposing all your saved workflows as ready-to-use HTTP endpoints."
|
||||
},
|
||||
{
|
||||
"author": "ginlov",
|
||||
"title": "segment_to_mask_comfyui [REMOVED]",
|
||||
"reference": "https://github.com/ginlov/segment_to_mask_comfyui",
|
||||
"files": [
|
||||
"https://github.com/ginlov/segment_to_mask_comfyui"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:SegToMask"
|
||||
},
|
||||
{
|
||||
"author": "TGu-97",
|
||||
"title": "TGu Utilities [REMOVED]",
|
||||
"id": "tgu",
|
||||
"reference": "https://github.com/TGu-97/ComfyUI-TGu-utils",
|
||||
"files": [
|
||||
"https://github.com/TGu-97/ComfyUI-TGu-utils"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: MPN Switch, MPN Reroute, PN Switch. This is a set of custom nodes for ComfyUI. Mainly focus on control switches."
|
||||
},
|
||||
{
|
||||
"author": "IfnotFr",
|
||||
"title": "ComfyUI-Connect [REMOVED]",
|
||||
"reference": "https://github.com/IfnotFr/ComfyUI-Connect",
|
||||
"files": [
|
||||
"https://github.com/IfnotFr/ComfyUI-Connect"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Transform your ComfyUI into a powerful API, exposing all your saved workflows as ready-to-use HTTP endpoints."
|
||||
},
|
||||
{
|
||||
"author": "KurtHokke",
|
||||
"title": "ComfyUI_KurtHokke-Nodes [REMOVED]",
|
||||
"reference": "https://github.com/KurtHokke/ComfyUI_KurtHokke-Nodes",
|
||||
"files": [
|
||||
"https://github.com/KurtHokke/ComfyUI_KurtHokke-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI_KurtHokke-Nodes"
|
||||
},
|
||||
{
|
||||
"author": "SpatialDeploy",
|
||||
"title": "ComfyUI-Voxels [REMOVED]",
|
||||
"reference": "https://github.com/SpatialDeploy/ComfyUI-Voxels",
|
||||
"files": [
|
||||
"https://github.com/SpatialDeploy/ComfyUI-Voxels"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Tools for creating voxel based videos"
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-group-selection [REMOVED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-group-selection",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-group-selection"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Create a new group of nodes."
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "connect-from-afar [REMOVED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-connect-from-afar",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-connect-from-afar"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Connect a new link from out of screen."
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-local-db [REMOVED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-local-db",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-local-db"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Store text to Key-Values pair json."
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-model-db [REMOVED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-model-db",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-model-db"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Store settings by model."
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-target-search [REMOVED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-target-search",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-target-search"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Move canvas to target on dragging connection."
|
||||
},
|
||||
{
|
||||
"author": "chrisgoringe",
|
||||
"title": "Image chooser [DEPRECATED]",
|
||||
"id": "image-chooser",
|
||||
"reference": "https://github.com/chrisgoringe/cg-image-picker",
|
||||
"files": [
|
||||
"https://github.com/chrisgoringe/cg-image-picker"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A custom node that pauses the flow while you choose which image (or latent) to pass on to the rest of the workflow."
|
||||
},
|
||||
{
|
||||
"author": "weilin9999",
|
||||
"title": "WeiLin-ComfyUI-prompt-all-in-one [DEPRECATED]",
|
||||
"id": "prompt-all-in-one",
|
||||
"reference": "https://github.com/weilin9999/WeiLin-ComfyUI-prompt-all-in-one",
|
||||
"files": [
|
||||
"https://github.com/weilin9999/WeiLin-ComfyUI-prompt-all-in-one"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Write prompt words like WebUI"
|
||||
},
|
||||
{
|
||||
"author": "svetozarov",
|
||||
"title": "AS_GeminiCaptioning Node [REMOVED]",
|
||||
"reference": "https://github.com/svetozarov/AS_GeminiCaptioning",
|
||||
"files": [
|
||||
"https://github.com/svetozarov/AS_GeminiCaptioning"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A ComfyUI node that combines an image with simple text parameters to create a prompt, sends it to the Google Gemini API via the google-generativeai SDK, and returns the generated text response along with the original prompt and an execution log"
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-load-image-in-seq [REMOVED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-load-image-in-seq",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-load-image-in-seq"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This node is load png image sequentially with metadata. Only supported for PNG format that has been created by ComfyUI.[w/renamed from comfyui-load-image-39. You need to remove previous one and reinstall to this.]"
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-model-metadata [REMOVED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-model-metadata",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-model-metadata"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Print model metadata on note node"
|
||||
},
|
||||
{
|
||||
"author": "shinich39",
|
||||
"title": "comfyui-view-recommendations [REMOVED]",
|
||||
"reference": "https://github.com/shinich39/comfyui-view-recommendations",
|
||||
"files": [
|
||||
"https://github.com/shinich39/comfyui-view-recommendations"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Load model generation data from civitai."
|
||||
},
|
||||
{
|
||||
"author": "jonstreeter",
|
||||
"title": "Comfyui-PySceneDetect [REMOVED]",
|
||||
"reference": "https://github.com/jonstreeter/Comfyui-PySceneDetect",
|
||||
"files": [
|
||||
"https://github.com/jonstreeter/Comfyui-PySceneDetect"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: PySceneDetect Video Processor"
|
||||
},
|
||||
{
|
||||
"author": "muxueChen",
|
||||
"title": "ComfyUI-NTQwen25-VL [REMOVED]",
|
||||
"reference": "https://github.com/muxueChen/ComfyUI-NTQwen25-VL",
|
||||
"files": [
|
||||
"https://github.com/muxueChen/ComfyUI-NTQwen25-VL"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Qwen25-VL is a plugin for ComfyU"
|
||||
},
|
||||
{
|
||||
"author": "Makki_Shizu",
|
||||
"title": "ComfyUI-SaveAnimatedGIF [DEPRECATED]",
|
||||
"id": "SaveAnimatedGIF",
|
||||
"reference": "https://github.com/MakkiShizu/ComfyUI-SaveAnimatedGIF",
|
||||
"files": [
|
||||
"https://github.com/MakkiShizu/ComfyUI-SaveAnimatedGIF"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Save animated GIF format nodes in ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "l1yongch1",
|
||||
"title": "ComfyUI_PhiCaption [REMOVED]",
|
||||
"reference": "https://github.com/l1yongch1/ComfyUI_PhiCaption",
|
||||
"files": [
|
||||
"https://github.com/l1yongch1/ComfyUI_PhiCaption"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "In addition to achieving conventional single-image, single-round reverse engineering, it can also achieve single-image multi-round and multi-image single-round reverse engineering. Moreover, the Phi model has a better understanding of prompts."
|
||||
},
|
||||
{
|
||||
"author": "nova-florealis",
|
||||
"title": "comfyui-alien [REMOVED]",
|
||||
"reference": "https://github.com/nova-florealis/comfyui-alien",
|
||||
"files": [
|
||||
"https://github.com/nova-florealis/comfyui-alien"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: Text to Text (LLM), Text Output, Convert to Markdown, List Display (Debug)"
|
||||
},
|
||||
{
|
||||
"author": "PluMaZero",
|
||||
"title": "ComfyUI-SpaceFlower [REMOVED]",
|
||||
"reference": "https://github.com/PluMaZero/ComfyUI-SpaceFlower",
|
||||
"files": [
|
||||
"https://github.com/PluMaZero/ComfyUI-SpaceFlower"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: SpaceFlower_Prompt, SpaceFlower_HangulPrompt, ..."
|
||||
},
|
||||
{
|
||||
"author": "vahidzxc",
|
||||
"title": "ComfyUI-My-Handy-Nodes [REMOVED]",
|
||||
"reference": "https://github.com/vahidzxc/ComfyUI-My-Handy-Nodes",
|
||||
"files": [
|
||||
"https://github.com/vahidzxc/ComfyUI-My-Handy-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:VahCropImage"
|
||||
},
|
||||
{
|
||||
"author": "Samulebotin",
|
||||
"title": "ComfyUI-FreeVC_wrapper [REMOVED]",
|
||||
"reference": "https://github.com/Samulebotin/ComfyUI-FreeVC_wrapper",
|
||||
"files": [
|
||||
"https://github.com/Samulebotin/ComfyUI-FreeVC_wrapper"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A voice conversion extension node for ComfyUI based on FreeVC, enabling high-quality voice conversion capabilities within the ComfyUI framework."
|
||||
},
|
||||
{
|
||||
"author": "GoingAI1998",
|
||||
"title": "ComfyUI Web Canvas Node [REMOVED]",
|
||||
"reference": "https://github.com/GoingAI1998/Comfyui_imgcanvas",
|
||||
"files": [
|
||||
"https://github.com/GoingAI1998/Comfyui_imgcanvas"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI_imgcanvas At present, I have not used the useful comfyui custom node about layer mixing, and I have written a comfyui runtime automatic pop-up window for layer editing node"
|
||||
},
|
||||
{
|
||||
"author": "807502278",
|
||||
"title": "ComfyUI_TensorRT_Merge [REMOVED]",
|
||||
"reference": "https://github.com/807502278/ComfyUI_TensorRT_Merge",
|
||||
"files": [
|
||||
"https://github.com/807502278/ComfyUI_TensorRT_Merge"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Non diffusion models supported by TensorRT, merged Comfyui plugin, added onnx automatic download and trt model conversion nodes."
|
||||
},
|
||||
{
|
||||
"author": "logtd",
|
||||
"title": "ComfyUI-LTXTricks [DEPRECATED]",
|
||||
"reference": "https://github.com/logtd/ComfyUI-LTXTricks",
|
||||
"files": [
|
||||
"https://github.com/logtd/ComfyUI-LTXTricks"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A set of nodes that provide additional controls for the LTX Video model"
|
||||
},
|
||||
{
|
||||
"author": "JichaoLiang",
|
||||
"title": "Immortal_comfyUI [REMOVED]",
|
||||
"reference": "https://github.com/JichaoLiang/Immortal_comfyUI",
|
||||
"files": [
|
||||
"https://github.com/JichaoLiang/Immortal_comfyUI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:ImNewNode, ImAppendNode, MergeNode, SetProperties, SaveToDirectory, batchNodes, redirectToNode, SetEvent, ..."
|
||||
},
|
||||
{
|
||||
"author": "Rvage0815",
|
||||
"title": "ComfyUI-RvTools [REMOVED]",
|
||||
"reference": "https://github.com/Rvage0815/ComfyUI-RvTools",
|
||||
"files": [
|
||||
"https://github.com/Rvage0815/ComfyUI-RvTools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "this node contains a lot of small little helpers like switches, passers and selectors that i use a lot to build my workflows."
|
||||
},
|
||||
{
|
||||
"author": "Rvage0815",
|
||||
"title": "RvTComfyUI-RvTools_v2 [REMOVED]",
|
||||
"reference": "https://github.com/Rvage0815/ComfyUI-RvTools_v2",
|
||||
"files": [
|
||||
"https://github.com/Rvage0815/ComfyUI-RvTools_v2"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "this node contains a lot of small little helpers like switches, passers and selectors that i use a lot to build my workflows."
|
||||
},
|
||||
{
|
||||
"author": "scottmudge",
|
||||
"title": "ComfyUI_BiscuitNodes [REMOVED]",
|
||||
"reference": "https://github.com/scottmudge/ComfyUI_BiscuitNodes",
|
||||
"files": [
|
||||
"https://github.com/scottmudge/ComfyUI_BiscuitNodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Load Image From Path Using File Selector"
|
||||
},
|
||||
{
|
||||
"author": "thanhduong0213929",
|
||||
"title": "ComfyUI-DeepUnlock [REMOVED]",
|
||||
"reference": "https://github.com/thanhduong0213929/ComfyUI-DeepUnlock",
|
||||
"files": [
|
||||
"https://github.com/thanhduong0213929/ComfyUI-DeepUnlock"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "DeepFuze is a state-of-the-art deep learning tool that seamlessly integrates with ComfyUI to revolutionize facial transformations, lipsyncing, video generation, voice cloning, face swapping, and lipsync translation. Leveraging advanced algorithms, DeepFuze enables users to combine audio and video with unparalleled realism, ensuring perfectly synchronized facial movements. This innovative solution is ideal for content creators, animators, developers, and anyone seeking to elevate their video editing projects with sophisticated AI-driven features."
|
||||
},
|
||||
{
|
||||
"author": "pathway8-sudo",
|
||||
"title": "RMBG [REMOVED]",
|
||||
"reference": "https://github.com/pathway8-sudo/RMBG",
|
||||
"files": [
|
||||
"https://github.com/pathway8-sudo/RMBG"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This repository provides a custom node for ComfyUI, leveraging the BriaRMBG model to remove backgrounds from images and output a transparent PNG."
|
||||
},
|
||||
{
|
||||
"author": "iris-Neko",
|
||||
"title": "ComfyUI_ascii_art [REMOVED]",
|
||||
"reference": "https://github.com/iris-Neko/ComfyUI_ascii_art",
|
||||
"files": [
|
||||
"https://github.com/iris-Neko/ComfyUI_ascii_art"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI node for [a/ASCII art controlnet](https://civitai.com/models/986392)"
|
||||
},
|
||||
{
|
||||
"author": "apesplat",
|
||||
"title": "ezXY scripts and nodes [NOT MAINTAINED]",
|
||||
"id": "ezxy",
|
||||
"reference": "https://github.com/GMapeSplat/ComfyUI_ezXY",
|
||||
"files": [
|
||||
"https://github.com/GMapeSplat/ComfyUI_ezXY"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Extensions/Patches: Enables linking float and integer inputs and ouputs. Values are automatically cast to the correct type and clamped to the correct range. Works with both builtin and custom nodes.[w/NOTE: This repo patches ComfyUI's validate_inputs and map_node_over_list functions while running. May break depending on your version of ComfyUI. Can be deactivated in config.yaml.]Nodes: A collection of nodes for facilitating the generation of XY plots. Capable of plotting changes over most primitive values.[w/Does not work with current version of Comfyui]"
|
||||
},
|
||||
{
|
||||
"author": "mie",
|
||||
"title": "ComfyUI_JanusProCaption [REMOVED]",
|
||||
"reference": "https://github.com/MieMieeeee/ComfyUI-JanusProCaption",
|
||||
"files": [
|
||||
"https://github.com/MieMieeeee/ComfyUI-JanusProCaption"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Describe image or create caption files using Janus Pro Model"
|
||||
},
|
||||
{
|
||||
"author": "Njbx",
|
||||
"title": "ComfyUI-blockswap [REMOVED]",
|
||||
"reference": "https://github.com/Njbx/ComfyUI-blockswap",
|
||||
"files": [
|
||||
"https://github.com/Njbx/ComfyUI-blockswap"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES: Block Swap"
|
||||
},
|
||||
{
|
||||
"author": "T8star1984",
|
||||
"title": "comfyui-purgevram [REMOVED]",
|
||||
"reference": "https://github.com/T8star1984/comfyui-purgevram",
|
||||
"files": [
|
||||
"https://github.com/T8star1984/comfyui-purgevram"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "NODES:PurgeVRAM.\nCan be added after any node to clean up vram and memory"
|
||||
},
|
||||
{
|
||||
"author": "zmwv823",
|
||||
"title": "ComfyUI-VideoDiffusion [REMOVED]",
|
||||
"reference": "https://github.com/zmwv823/ComfyUI-VideoDiffusion",
|
||||
"files": [
|
||||
"https://github.com/zmwv823/ComfyUI-VideoDiffusion"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "[a/LatentSync](https://github.com/bytedance/LatentSync) and [a/Sonic](https://github.com/jixiaozhong/Sonic). [w/Just for study purpose. It's not for directly use, u should know how to fix issues.]"
|
||||
},
|
||||
{
|
||||
"author": "NyaamZ",
|
||||
"title": "Get Booru Tag ExtendeD [REMOVED]",
|
||||
"reference": "https://github.com/NyaamZ/ComfyUI-GetBooruTag-ED",
|
||||
"files": [
|
||||
"https://github.com/NyaamZ/ComfyUI-GetBooruTag-ED"
|
||||
],
|
||||
"description": "Get tag from Booru site.",
|
||||
"install_type": "git-clone"
|
||||
},
|
||||
{
|
||||
"author": "lingha",
|
||||
"title": "comfyui_kj [REMOVED]",
|
||||
"id": "comfyui_kj",
|
||||
"reference": "https://github.com/XieChengYuan/comfyui_kj",
|
||||
"files": [
|
||||
"https://github.com/XieChengYuan/comfyui_kj"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "comfyui_kj, A tool that can package workflows into projects and publish them to a WeChat Mini Program named Kaji, allowing charges to be collected from users."
|
||||
},
|
||||
{
|
||||
"author": "myAiLemon",
|
||||
"title": "MagicGetPromptAutomatically",
|
||||
"title": "MagicGetPromptAutomatically [REMOVED]",
|
||||
"reference": "https://github.com/myAiLemon/MagicGetPromptAutomatically",
|
||||
"files": [
|
||||
"https://github.com/myAiLemon/MagicGetPromptAutomatically"
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
{
|
||||
"models": [
|
||||
{
|
||||
"name": "Inswapper-fp16 (face swap) [REMOVED]",
|
||||
"type": "insightface",
|
||||
"base": "inswapper",
|
||||
"save_path": "insightface",
|
||||
"description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)",
|
||||
"reference": "https://github.com/facefusion/facefusion-assets",
|
||||
"filename": "inswapper_128_fp16.onnx",
|
||||
"url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128_fp16.onnx",
|
||||
"size": "277.7MB"
|
||||
},
|
||||
{
|
||||
"name": "Inswapper (face swap) [REMOVED]",
|
||||
"type": "insightface",
|
||||
"base": "inswapper",
|
||||
"save_path": "insightface",
|
||||
"description": "Checkpoint of the insightface swapper model\n(used by ComfyUI-FaceSwap, comfyui-reactor-node, CharacterFaceSwap,\nComfyUI roop and comfy_mtb)",
|
||||
"reference": "https://github.com/facefusion/facefusion-assets",
|
||||
"filename": "inswapper_128.onnx",
|
||||
"url": "https://github.com/facefusion/facefusion-assets/releases/download/models/inswapper_128.onnx",
|
||||
"size": "555.3MB"
|
||||
},
|
||||
{
|
||||
"name": "pfg-novel-n10.pt",
|
||||
"type": "PFG",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,288 @@
|
||||
{
|
||||
"models": [
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (bf16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 480p 14B (bf16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_480p_14B_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_bf16.safetensors",
|
||||
"size": "32.8GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_480p_14B_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp16.safetensors",
|
||||
"size": "32.8GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_e4m3fn)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp8_e4m3fn)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_e4m3fn.safetensors",
|
||||
"size": "16.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 480p 14B (fp8_scaled)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 480p 14B (fp8_scaled)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_480p_14B_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_480p_14B_fp8_scaled.safetensors",
|
||||
"size": "16.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (bf16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 720p 14B (bf16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_720p_14B_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_bf16.safetensors",
|
||||
"size": "32.8GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_720p_14B_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp16.safetensors",
|
||||
"size": "32.8GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_e4m3fn)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp8_e4m3fn)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_e4m3fn.safetensors",
|
||||
"size": "16.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 i2v 720p 14B (fp8_scaled)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for i2v 720p 14B (fp8_scaled)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_i2v_720p_14B_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_i2v_720p_14B_fp8_scaled.safetensors",
|
||||
"size": "16.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/clip_vision_h.safetensors",
|
||||
"type": "clip_vision",
|
||||
"base": "clip_vision_h",
|
||||
"save_path": "clip_vision",
|
||||
"description": "clip_vision_h model for Wan2.1",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "clip_vision_h.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/clip_vision/clip_vision_h.safetensors",
|
||||
"size": "1.26GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 1.3B (bf16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 1.3B (bf16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_1.3B_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_bf16.safetensors",
|
||||
"size": "2.84GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 1.3B (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 1.3B (fp16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_1.3B_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_1.3B_fp16.safetensors",
|
||||
"size": "2.84GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 14B (bf16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 14B (bf16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_14B_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_bf16.safetensors",
|
||||
"size": "28.6GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 14B (fp16)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_14B_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp16.safetensors",
|
||||
"size": "28.6GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp8_e4m3fn)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 14B (fp8_e4m3fn)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_14B_fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_e4m3fn.safetensors",
|
||||
"size": "14.3GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 t2v 14B (fp8_scaled)",
|
||||
"type": "diffusion_model",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "diffusion_models/Wan2.1",
|
||||
"description": "Wan2.1 difussion model for t2v 14B (fp8_scaled)",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan2.1_t2v_14B_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/diffusion_models/wan2.1_t2v_14B_fp8_scaled.safetensors",
|
||||
"size": "14.3GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/Wan2.1 VAE",
|
||||
"type": "vae",
|
||||
"base": "Wan2.1",
|
||||
"save_path": "vae",
|
||||
"description": "Wan2.1 VAE model",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "wan_2.1_vae.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/vae/wan_2.1_vae.safetensors",
|
||||
"size": "254MB"
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"name": "Comfy-Org/umt5_xxl_fp16.safetensors",
|
||||
"type": "clip",
|
||||
"base": "umt5_xxl",
|
||||
"save_path": "text_encoders",
|
||||
"description": "umt5_xxl_fp16 text encoder for Wan2.1",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"filename": "umt5_xxl_fp16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged/resolve/main/split_files/text_encoders/umt5_xxl_fp16.safetensors",
|
||||
"size": "11.4GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/umt5_xxl_fp8_e4m3fn_scaled.safetensors",
|
||||
"type": "clip",
|
||||
"base": "umt5_xxl",
|
||||
"save_path": "text_encoders",
|
||||
"description": "umt5_xxl_fp8_e4m3fn_scaled text encoder for Wan2.1",
|
||||
"reference": "https://huggingface.co/Comfy-Org/Wan_2.1_ComfyUI_repackaged",
|
||||
"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": "Comfy-Org/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||
"type": "diffusion_model",
|
||||
"base": "Hunyuan Video",
|
||||
"save_path": "diffusion_models/hunyuan_video",
|
||||
"description": "Huyuan Video Image2Video diffusion model. repackaged version.",
|
||||
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
||||
"filename": "hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/diffusion_models/hunyuan_video_image_to_video_720p_bf16.safetensors",
|
||||
"size": "25.6GB"
|
||||
},
|
||||
{
|
||||
"name": "Comfy-Org/llava_llama3_vision.safetensors",
|
||||
"type": "clip_vision",
|
||||
"base": "LLaVA-Llama-3",
|
||||
"save_path": "text_encoders",
|
||||
"description": "llava_llama3_vision clip vison model. This is required for using Hunyuan Video Image2Video.",
|
||||
"reference": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged",
|
||||
"filename": "llava_llama3_vision.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/HunyuanVideo_repackaged/resolve/main/split_files/clip_vision/llava_llama3_vision.safetensors",
|
||||
"size": "649MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "LTX-Video 2B v0.9.5 Checkpoint",
|
||||
"type": "checkpoint",
|
||||
"base": "LTX-Video",
|
||||
"save_path": "checkpoints/LTXV",
|
||||
"description": "LTX-Video is the first DiT-based video generation model capable of generating high-quality videos in real-time. It produces 24 FPS videos at a 768x512 resolution faster than they can be watched. Trained on a large-scale dataset of diverse videos, the model generates high-resolution videos with realistic and varied content.",
|
||||
"reference": "https://huggingface.co/Lightricks/LTX-Video",
|
||||
"filename": "ltx-video-2b-v0.9.5.safetensors",
|
||||
"url": "https://huggingface.co/Lightricks/LTX-Video/resolve/main/ltx-video-2b-v0.9.5.safetensors",
|
||||
"size": "6.34GB"
|
||||
},
|
||||
{
|
||||
"name": "kolors/vae/diffusion_pytorch_model.fp16.safetensors",
|
||||
"type": "VAE",
|
||||
"base": "Kolors",
|
||||
"save_path": "vae/kolors",
|
||||
"description": "Kolors VAE",
|
||||
"reference": "https://huggingface.co/Kwai-Kolors/Kolors",
|
||||
"filename": "diffusion_pytorch_model.fp16.safetensors",
|
||||
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.fp16.safetensors",
|
||||
"size": "167MB"
|
||||
},
|
||||
{
|
||||
"name": "kolors/vae/diffusion_pytorch_model.safetensors",
|
||||
"type": "VAE",
|
||||
"base": "Kolors",
|
||||
"save_path": "vae/kolors",
|
||||
"description": "Kolors VAE",
|
||||
"reference": "https://huggingface.co/Kwai-Kolors/Kolors",
|
||||
"filename": "diffusion_pytorch_model.safetensors",
|
||||
"url": "https://huggingface.co/Kwai-Kolors/Kolors/resolve/main/vae/diffusion_pytorch_model.safetensors",
|
||||
"size": "335MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "deepseek-ai/Janus-Pro-1B",
|
||||
"type": "Janus-Pro",
|
||||
"base": "Janus-Pro",
|
||||
"save_path": "Janus-Pro",
|
||||
"description": "[SNAPSHOT] Janus-Pro-1B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
||||
"reference": "https://huggingface.co/deepseek-ai/Janus-Pro-1B",
|
||||
"filename": "<huggingface>",
|
||||
"url": "deepseek-ai/Janus-Pro-1B",
|
||||
"size": "7.8GB"
|
||||
},
|
||||
{
|
||||
"name": "deepseek-ai/Janus-Pro-7B",
|
||||
"type": "Janus-Pro",
|
||||
"base": "Janus-Pro",
|
||||
"save_path": "Janus-Pro",
|
||||
"description": "[SNAPSHOT] Janus-Pro-7B model.[w/You cannot download this item on ComfyUI-Manager versions below V3.18]",
|
||||
"reference": "https://huggingface.co/deepseek-ai/Janus-Pro-7B",
|
||||
"filename": "<huggingface>",
|
||||
"url": "deepseek-ai/Janus-Pro-7B",
|
||||
"size": "14.85GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Leoxing/pia.ckpt",
|
||||
"type": "animatediff-pia",
|
||||
@@ -422,234 +705,6 @@
|
||||
"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"
|
||||
},
|
||||
{
|
||||
"name": "CLIPVision model (Kwai-Kolors/Kolors-IP-Adapter-Plus/clip-vit-large)",
|
||||
"type": "clip_vision",
|
||||
"base": "ViT-L",
|
||||
"save_path": "clip_vision",
|
||||
"description": "CLIPVision model (This is required in cubiq/ComfyUI_IPAdapter_plus)",
|
||||
"reference": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus",
|
||||
"filename": "clip-vit-large-patch14-336.bin",
|
||||
"url": "https://huggingface.co/Kwai-Kolors/Kolors-IP-Adapter-Plus/resolve/main/image_encoder/pytorch_model.bin",
|
||||
"size": "1.71GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "kijai/lotus depth d model v1.1 (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "lotus",
|
||||
"save_path": "diffusion_models",
|
||||
"description": "lotus depth d model v1.1 (fp16). This model can be used in ComfyUI-Lotus custom nodes.",
|
||||
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||
"filename": "lotus-depth-d-v-1-1-fp16.safetensors",
|
||||
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-depth-d-v-1-1-fp16.safetensors",
|
||||
"size": "1.74GB"
|
||||
},
|
||||
{
|
||||
"name": "kijai/lotus depth g model v1.0 (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "lotus",
|
||||
"save_path": "diffusion_models",
|
||||
"description": "lotus depth g model v1.0 (fp16). This model can be used in ComfyUI-Lotus custom nodes.",
|
||||
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||
"filename": "lotus-depth-g-v1-0-fp16.safetensors",
|
||||
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-depth-g-v1-0-fp16.safetensors",
|
||||
"size": "1.74GB"
|
||||
},
|
||||
{
|
||||
"name": "kijai/lotus depth g model v1.0",
|
||||
"type": "diffusion_model",
|
||||
"base": "lotus",
|
||||
"save_path": "diffusion_models",
|
||||
"description": "lotus depth g model v1.0. This model can be used in ComfyUI-Lotus custom nodes.",
|
||||
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||
"filename": "lotus-depth-g-v1-0.safetensors",
|
||||
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-depth-g-v1-0.safetensors",
|
||||
"size": "3.47GB"
|
||||
},
|
||||
{
|
||||
"name": "kijai/lotus normal d model v1.0 (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "lotus",
|
||||
"save_path": "diffusion_models",
|
||||
"description": "lotus normal d model v1.0 (fp16). This model can be used in ComfyUI-Lotus custom nodes.",
|
||||
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||
"filename": "lotus-normal-d-v1-0-fp16.safetensors",
|
||||
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-normal-d-v1-0-fp16.safetensors",
|
||||
"size": "1.74GB"
|
||||
},
|
||||
{
|
||||
"name": "kijai/lotus normal d model v1.0",
|
||||
"type": "diffusion_model",
|
||||
"base": "lotus",
|
||||
"save_path": "diffusion_models",
|
||||
"description": "lotus normal d model v1.0. This model can be used in ComfyUI-Lotus custom nodes.",
|
||||
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||
"filename": "lotus-normal-d-v1-0.safetensors",
|
||||
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-normal-d-v1-0.safetensors",
|
||||
"size": "3.47GB"
|
||||
},
|
||||
{
|
||||
"name": "kijai/lotus normal g model v1.0 (fp16)",
|
||||
"type": "diffusion_model",
|
||||
"base": "lotus",
|
||||
"save_path": "diffusion_models",
|
||||
"description": "lotus normal g model v1.0 (fp16). This model can be used in ComfyUI-Lotus custom nodes.",
|
||||
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||
"filename": "lotus-normal-g-v1-0-fp16.safetensors",
|
||||
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-normal-g-v1-0-fp16.safetensors",
|
||||
"size": "1.74GB"
|
||||
},
|
||||
{
|
||||
"name": "kijai/lotus normal g model v1.0",
|
||||
"type": "diffusion_model",
|
||||
"base": "lotus",
|
||||
"save_path": "diffusion_models",
|
||||
"description": "lotus normal g model v1.0. This model can be used in ComfyUI-Lotus custom nodes.",
|
||||
"reference": "https://huggingface.co/Kijai/lotus-comfyui",
|
||||
"filename": "lotus-normal-g-v1-0.safetensors",
|
||||
"url": "https://huggingface.co/Kijai/lotus-comfyui/resolve/main/lotus-normal-g-v1-0.safetensors",
|
||||
"size": "3.47GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Depth Pro model",
|
||||
"type": "depth-pro",
|
||||
"base": "depth-pro",
|
||||
"save_path": "depth/ml-depth-pro",
|
||||
"description": "Depth pro model for [a/ComfyUI-Depth-Pro](https://github.com/spacepxl/ComfyUI-Depth-Pro)",
|
||||
"reference": "https://huggingface.co/spacepxl/ml-depth-pro",
|
||||
"filename": "depth_pro.fp16.safetensors",
|
||||
"url": "https://huggingface.co/spacepxl/ml-depth-pro/resolve/main/depth_pro.fp16.safetensors",
|
||||
"size": "1.9GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "jasperai/FLUX.1-dev-Controlnet-Upscaler",
|
||||
"type": "controlnet",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "controlnet/FLUX.1/jasperai-dev-Upscaler",
|
||||
"description": "This is Flux.1-dev ControlNet for low resolution images developed by Jasper research team.",
|
||||
"reference": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Upscaler",
|
||||
"filename": "diffusion_pytorch_model.safetensors",
|
||||
"url": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Upscaler/resolve/main/diffusion_pytorch_model.safetensors",
|
||||
"size": "3.58GB"
|
||||
},
|
||||
{
|
||||
"name": "jasperai/FLUX.1-dev-Controlnet-Depth",
|
||||
"type": "controlnet",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "controlnet/FLUX.1/jasperai-dev-Depth",
|
||||
"description": "This is Flux.1-dev ControlNet for Depth map developed by Jasper research team.",
|
||||
"reference": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Depth",
|
||||
"filename": "diffusion_pytorch_model.safetensors",
|
||||
"url": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Depth/resolve/main/diffusion_pytorch_model.safetensors",
|
||||
"size": "3.58GB"
|
||||
},
|
||||
{
|
||||
"name": "jasperai/Flux.1-dev-Controlnet-Surface-Normals",
|
||||
"type": "controlnet",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "controlnet/FLUX.1/jasperai-dev-Surface-Normals",
|
||||
"description": "This is Flux.1-dev ControlNet for Surface Normals map developed by Jasper research team.",
|
||||
"reference": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Surface-Normals",
|
||||
"filename": "diffusion_pytorch_model.safetensors",
|
||||
"url": "https://huggingface.co/jasperai/Flux.1-dev-Controlnet-Surface-Normals/resolve/main/diffusion_pytorch_model.safetensors",
|
||||
"size": "3.58GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro (fp8_e4m3fn) by Kijai",
|
||||
"type": "controlnet",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "controlnet/FLUX.1",
|
||||
"description": "FLUX.1 [Dev] Union Controlnet. Supports Canny, Tile, Depth, Blur, Pose, Gray, Low Quality\nVersion quantized to fp8_e4m3fn by Kijai",
|
||||
"reference": "https://huggingface.co/Kijai/flux-fp8",
|
||||
"filename": "flux_shakker_labs_union_pro-fp8_e4m3fn.safetensors",
|
||||
"url": "https://huggingface.co/Kijai/flux-fp8/resolve/main/flux_shakker_labs_union_pro-fp8_e4m3fn.safetensors",
|
||||
"size": "3.3GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "ViT-L-14-TEXT-detail-improved-hiT-GmP-HF.safetensors [Long CLIP L]",
|
||||
"type": "clip",
|
||||
"base": "clip",
|
||||
"save_path": "text_encoders/long_clip",
|
||||
"description": "Greatly improved TEXT + Detail (as CLIP-L for Flux.1)",
|
||||
"reference": "https://huggingface.co/zer0int",
|
||||
"filename": "ViT-L-14-TEXT-detail-improved-hiT-GmP-HF.safetensors",
|
||||
"url": "https://huggingface.co/zer0int/CLIP-GmP-ViT-L-14/resolve/main/ViT-L-14-TEXT-detail-improved-hiT-GmP-HF.safetensors",
|
||||
"size": "931MB"
|
||||
},
|
||||
{
|
||||
"name": "ViT-L-14-TEXT-detail-improved-hiT-GmP-HF.safetensors [Long CLIP L]",
|
||||
"type": "clip",
|
||||
"base": "clip",
|
||||
"save_path": "text_encoders/long_clip",
|
||||
"description": "Greatly improved TEXT + Detail (as CLIP-L for Flux.1)",
|
||||
"reference": "https://huggingface.co/zer0int",
|
||||
"filename": "ViT-L-14-TEXT-detail-improved-hiT-GmP-TE-only-HF.safetensors",
|
||||
"url": "https://huggingface.co/zer0int/CLIP-GmP-ViT-L-14/resolve/main/ViT-L-14-TEXT-detail-improved-hiT-GmP-TE-only-HF.safetensors",
|
||||
"size": "323MB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro",
|
||||
"type": "controlnet",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "controlnet/FLUX.1/Shakker-Labs-ControlNet-Union-Pro",
|
||||
"description": "FLUX.1 [Dev] Union Controlnet. Supports Canny, Tile, Depth, Blur, Pose, Gray, Low Quality",
|
||||
"reference": "https://huggingface.co/Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro",
|
||||
"filename": "diffusion_pytorch_model.safetensors",
|
||||
"url": "https://huggingface.co/Shakker-Labs/FLUX.1-dev-ControlNet-Union-Pro/resolve/main/diffusion_pytorch_model.safetensors",
|
||||
"size": "6.6GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "Hyper-SD LoRA (8steps) - FLUX.1 [Dev]",
|
||||
"type": "lora",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "loras/HyperSD/FLUX.1",
|
||||
"description": "Hyper-SD LoRA (8steps) - FLUX.1 [Dev]",
|
||||
"reference": "https://huggingface.co/ByteDance/Hyper-SD",
|
||||
"filename": "Hyper-FLUX.1-dev-8steps-lora.safetensors",
|
||||
"url": "https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-FLUX.1-dev-8steps-lora.safetensors",
|
||||
"size": "1.39GB"
|
||||
},
|
||||
{
|
||||
"name": "Hyper-SD LoRA (16steps) - FLUX.1 [Dev]",
|
||||
"type": "lora",
|
||||
"base": "FLUX.1",
|
||||
"save_path": "loras/HyperSD/FLUX.1",
|
||||
"description": "Hyper-SD LoRA (16steps) - FLUX.1 [Dev]",
|
||||
"reference": "https://huggingface.co/ByteDance/Hyper-SD",
|
||||
"filename": "Hyper-FLUX.1-dev-16steps-lora.safetensors",
|
||||
"url": "https://huggingface.co/ByteDance/Hyper-SD/resolve/main/Hyper-FLUX.1-dev-16steps-lora.safetensors",
|
||||
"size": "1.39GB"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "DMD2 LoRA (4steps)",
|
||||
"type": "lora",
|
||||
"base": "SDXL",
|
||||
"save_path": "loras/DMD2",
|
||||
"description": "DMD2 LoRA (4steps)",
|
||||
"reference": "https://huggingface.co/tianweiy/DMD2",
|
||||
"filename": "dmd2_sdxl_4step_lora.safetensors",
|
||||
"url": "https://huggingface.co/tianweiy/DMD2/resolve/main/dmd2_sdxl_4step_lora.safetensors",
|
||||
"size": "787MB"
|
||||
},
|
||||
{
|
||||
"name": "DMD2 LoRA (4steps/fp16)",
|
||||
"type": "lora",
|
||||
"base": "SDXL",
|
||||
"save_path": "loras/DMD2",
|
||||
"description": "DMD2 LoRA (4steps/fp16)",
|
||||
"reference": "https://huggingface.co/tianweiy/DMD2",
|
||||
"filename": "dmd2_sdxl_4step_lora_fp16.safetensors",
|
||||
"url": "https://huggingface.co/tianweiy/DMD2/resolve/main/dmd2_sdxl_4step_lora_fp16.safetensors",
|
||||
"size": "394MB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -291,6 +291,36 @@
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Example of using ComfyUI Toolbar to Toggle ComfyUI links on/off"
|
||||
},
|
||||
{
|
||||
"author": "xhiroga",
|
||||
"title": "ComfyUI-TypeScript-CustomNode",
|
||||
"reference": "https://github.com/xhiroga/ComfyUI-TypeScript-CustomNode",
|
||||
"files": [
|
||||
"https://github.com/xhiroga/ComfyUI-TypeScript-CustomNode"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This project is generated from xhiroga/ComfyUI-TypeScript-CustomNode"
|
||||
},
|
||||
{
|
||||
"author": "zentrocdot",
|
||||
"title": "ComfyUI-Turtle_Graphics_Demos",
|
||||
"reference": "https://github.com/zentrocdot/ComfyUI-Turtle_Graphics_Demo",
|
||||
"files": [
|
||||
"https://github.com/zentrocdot/ComfyUI-Turtle_Graphics_Demo"
|
||||
],
|
||||
"description": "ComfyUI node for creating some Turtle Graphic demos.",
|
||||
"install_type": "git-clone"
|
||||
},
|
||||
{
|
||||
"author": "cozy-comfyui",
|
||||
"title": "cozy_ex_dynamic",
|
||||
"reference": "https://github.com/cozy-comfyui/cozy_ex_dynamic",
|
||||
"files": [
|
||||
"https://github.com/cozy-comfyui/cozy_ex_dynamic"
|
||||
],
|
||||
"description": "Dynamic Node examples for ComfyUI",
|
||||
"install_type": "git-clone"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,373 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "aaaaaaaaaa"
|
||||
},
|
||||
"source": [
|
||||
"Git clone the repo and install the requirements. (ignore the pip errors about protobuf)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bbbbbbbbbb"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# #@title Environment Setup\n",
|
||||
"\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"OPTIONS = {}\n",
|
||||
"\n",
|
||||
"USE_GOOGLE_DRIVE = True #@param {type:\"boolean\"}\n",
|
||||
"UPDATE_COMFY_UI = True #@param {type:\"boolean\"}\n",
|
||||
"USE_COMFYUI_MANAGER = True #@param {type:\"boolean\"}\n",
|
||||
"INSTALL_CUSTOM_NODES_DEPENDENCIES = True #@param {type:\"boolean\"}\n",
|
||||
"OPTIONS['USE_GOOGLE_DRIVE'] = USE_GOOGLE_DRIVE\n",
|
||||
"OPTIONS['UPDATE_COMFY_UI'] = UPDATE_COMFY_UI\n",
|
||||
"OPTIONS['USE_COMFYUI_MANAGER'] = USE_COMFYUI_MANAGER\n",
|
||||
"OPTIONS['INSTALL_CUSTOM_NODES_DEPENDENCIES'] = INSTALL_CUSTOM_NODES_DEPENDENCIES\n",
|
||||
"\n",
|
||||
"current_dir = !pwd\n",
|
||||
"WORKSPACE = f\"{current_dir[0]}/ComfyUI\"\n",
|
||||
"\n",
|
||||
"if OPTIONS['USE_GOOGLE_DRIVE']:\n",
|
||||
" !echo \"Mounting Google Drive...\"\n",
|
||||
" %cd /\n",
|
||||
"\n",
|
||||
" from google.colab import drive\n",
|
||||
" drive.mount('/content/drive')\n",
|
||||
"\n",
|
||||
" WORKSPACE = \"/content/drive/MyDrive/ComfyUI\"\n",
|
||||
" %cd /content/drive/MyDrive\n",
|
||||
"\n",
|
||||
"![ ! -d $WORKSPACE ] && echo -= Initial setup ComfyUI =- && git clone https://github.com/comfyanonymous/ComfyUI\n",
|
||||
"%cd $WORKSPACE\n",
|
||||
"\n",
|
||||
"if OPTIONS['UPDATE_COMFY_UI']:\n",
|
||||
" !echo -= Updating ComfyUI =-\n",
|
||||
"\n",
|
||||
" # Correction of the issue of permissions being deleted on Google Drive.\n",
|
||||
" ![ -f \".ci/nightly/update_windows/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/nightly/update_windows/update_comfyui_and_python_dependencies.bat\n",
|
||||
" ![ -f \".ci/nightly/windows_base_files/run_nvidia_gpu.bat\" ] && chmod 755 .ci/nightly/windows_base_files/run_nvidia_gpu.bat\n",
|
||||
" ![ -f \".ci/update_windows/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/update_windows/update_comfyui_and_python_dependencies.bat\n",
|
||||
" ![ -f \".ci/update_windows_cu118/update_comfyui_and_python_dependencies.bat\" ] && chmod 755 .ci/update_windows_cu118/update_comfyui_and_python_dependencies.bat\n",
|
||||
" ![ -f \".ci/update_windows/update.py\" ] && chmod 755 .ci/update_windows/update.py\n",
|
||||
" ![ -f \".ci/update_windows/update_comfyui.bat\" ] && chmod 755 .ci/update_windows/update_comfyui.bat\n",
|
||||
" ![ -f \".ci/update_windows/README_VERY_IMPORTANT.txt\" ] && chmod 755 .ci/update_windows/README_VERY_IMPORTANT.txt\n",
|
||||
" ![ -f \".ci/update_windows/run_cpu.bat\" ] && chmod 755 .ci/update_windows/run_cpu.bat\n",
|
||||
" ![ -f \".ci/update_windows/run_nvidia_gpu.bat\" ] && chmod 755 .ci/update_windows/run_nvidia_gpu.bat\n",
|
||||
"\n",
|
||||
" !git pull\n",
|
||||
"\n",
|
||||
"!echo -= Install dependencies =-\n",
|
||||
"!pip3 install accelerate\n",
|
||||
"!pip3 install einops transformers>=4.28.1 safetensors>=0.4.2 aiohttp pyyaml Pillow scipy tqdm psutil tokenizers>=0.13.3\n",
|
||||
"!pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121\n",
|
||||
"!pip3 install torchsde\n",
|
||||
"!pip3 install kornia>=0.7.1 spandrel soundfile sentencepiece\n",
|
||||
"\n",
|
||||
"if OPTIONS['USE_COMFYUI_MANAGER']:\n",
|
||||
" %cd custom_nodes\n",
|
||||
"\n",
|
||||
" # Correction of the issue of permissions being deleted on Google Drive.\n",
|
||||
" ![ -f \"ComfyUI-Manager/check.sh\" ] && chmod 755 ComfyUI-Manager/check.sh\n",
|
||||
" ![ -f \"ComfyUI-Manager/scan.sh\" ] && chmod 755 ComfyUI-Manager/scan.sh\n",
|
||||
" ![ -f \"ComfyUI-Manager/node_db/dev/scan.sh\" ] && chmod 755 ComfyUI-Manager/node_db/dev/scan.sh\n",
|
||||
" ![ -f \"ComfyUI-Manager/node_db/tutorial/scan.sh\" ] && chmod 755 ComfyUI-Manager/node_db/tutorial/scan.sh\n",
|
||||
" ![ -f \"ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh\" ] && chmod 755 ComfyUI-Manager/scripts/install-comfyui-venv-linux.sh\n",
|
||||
" ![ -f \"ComfyUI-Manager/scripts/install-comfyui-venv-win.bat\" ] && chmod 755 ComfyUI-Manager/scripts/install-comfyui-venv-win.bat\n",
|
||||
"\n",
|
||||
" ![ ! -d ComfyUI-Manager ] && echo -= Initial setup ComfyUI-Manager =- && git clone https://github.com/ltdrdata/ComfyUI-Manager\n",
|
||||
" %cd ComfyUI-Manager\n",
|
||||
" !git pull\n",
|
||||
"\n",
|
||||
"%cd $WORKSPACE\n",
|
||||
"\n",
|
||||
"if OPTIONS['INSTALL_CUSTOM_NODES_DEPENDENCIES']:\n",
|
||||
" !echo -= Install custom nodes dependencies =-\n",
|
||||
" !pip install GitPython\n",
|
||||
" !python custom_nodes/ComfyUI-Manager/cm-cli.py restore-dependencies\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cccccccccc"
|
||||
},
|
||||
"source": [
|
||||
"Download some models/checkpoints/vae or custom comfyui nodes (uncomment the commands for the ones you want)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "dddddddddd"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Checkpoints\n",
|
||||
"\n",
|
||||
"### SDXL\n",
|
||||
"### I recommend these workflow examples: https://comfyanonymous.github.io/ComfyUI_examples/sdxl/\n",
|
||||
"\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_base_1.0.safetensors -P ./models/checkpoints/\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-refiner-1.0/resolve/main/sd_xl_refiner_1.0.safetensors -P ./models/checkpoints/\n",
|
||||
"\n",
|
||||
"# SDXL ReVision\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/clip_vision_g/resolve/main/clip_vision_g.safetensors -P ./models/clip_vision/\n",
|
||||
"\n",
|
||||
"# SD1.5\n",
|
||||
"!wget -c https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/v1-5-pruned-emaonly.ckpt -P ./models/checkpoints/\n",
|
||||
"\n",
|
||||
"# SD2\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/stable-diffusion-2-1-base/resolve/main/v2-1_512-ema-pruned.safetensors -P ./models/checkpoints/\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/stable-diffusion-2-1/resolve/main/v2-1_768-ema-pruned.safetensors -P ./models/checkpoints/\n",
|
||||
"\n",
|
||||
"# Some SD1.5 anime style\n",
|
||||
"#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix2/AbyssOrangeMix2_hard.safetensors -P ./models/checkpoints/\n",
|
||||
"#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix3/AOM3A1_orangemixs.safetensors -P ./models/checkpoints/\n",
|
||||
"#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/Models/AbyssOrangeMix3/AOM3A3_orangemixs.safetensors -P ./models/checkpoints/\n",
|
||||
"#!wget -c https://huggingface.co/Linaqruf/anything-v3.0/resolve/main/anything-v3-fp16-pruned.safetensors -P ./models/checkpoints/\n",
|
||||
"\n",
|
||||
"# Waifu Diffusion 1.5 (anime style SD2.x 768-v)\n",
|
||||
"#!wget -c https://huggingface.co/waifu-diffusion/wd-1-5-beta3/resolve/main/wd-illusion-fp16.safetensors -P ./models/checkpoints/\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# unCLIP models\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/illuminatiDiffusionV1_v11_unCLIP/resolve/main/illuminatiDiffusionV1_v11-unclip-h-fp16.safetensors -P ./models/checkpoints/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/wd-1.5-beta2_unCLIP/resolve/main/wd-1-5-beta2-aesthetic-unclip-h-fp16.safetensors -P ./models/checkpoints/\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# VAE\n",
|
||||
"!wget -c https://huggingface.co/stabilityai/sd-vae-ft-mse-original/resolve/main/vae-ft-mse-840000-ema-pruned.safetensors -P ./models/vae/\n",
|
||||
"#!wget -c https://huggingface.co/WarriorMama777/OrangeMixs/resolve/main/VAEs/orangemix.vae.pt -P ./models/vae/\n",
|
||||
"#!wget -c https://huggingface.co/hakurei/waifu-diffusion-v1-4/resolve/main/vae/kl-f8-anime2.ckpt -P ./models/vae/\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Loras\n",
|
||||
"#!wget -c https://civitai.com/api/download/models/10350 -O ./models/loras/theovercomer8sContrastFix_sd21768.safetensors #theovercomer8sContrastFix SD2.x 768-v\n",
|
||||
"#!wget -c https://civitai.com/api/download/models/10638 -O ./models/loras/theovercomer8sContrastFix_sd15.safetensors #theovercomer8sContrastFix SD1.x\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/stable-diffusion-xl-base-1.0/resolve/main/sd_xl_offset_example-lora_1.0.safetensors -P ./models/loras/ #SDXL offset noise lora\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# T2I-Adapter\n",
|
||||
"#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_depth_sd14v1.pth -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_seg_sd14v1.pth -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_sketch_sd14v1.pth -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_keypose_sd14v1.pth -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_openpose_sd14v1.pth -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_color_sd14v1.pth -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_canny_sd14v1.pth -P ./models/controlnet/\n",
|
||||
"\n",
|
||||
"# T2I Styles Model\n",
|
||||
"#!wget -c https://huggingface.co/TencentARC/T2I-Adapter/resolve/main/models/t2iadapter_style_sd14v1.pth -P ./models/style_models/\n",
|
||||
"\n",
|
||||
"# CLIPVision model (needed for styles model)\n",
|
||||
"#!wget -c https://huggingface.co/openai/clip-vit-large-patch14/resolve/main/pytorch_model.bin -O ./models/clip_vision/clip_vit14.bin\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# ControlNet\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11e_sd15_ip2p_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11e_sd15_shuffle_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_canny_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11f1p_sd15_depth_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_inpaint_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_lineart_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_mlsd_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_normalbae_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_openpose_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_scribble_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_seg_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15_softedge_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11p_sd15s2_lineart_anime_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/ControlNet-v1-1_fp16_safetensors/resolve/main/control_v11u_sd15_tile_fp16.safetensors -P ./models/controlnet/\n",
|
||||
"\n",
|
||||
"# ControlNet SDXL\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-canny-rank256.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-depth-rank256.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-recolor-rank256.safetensors -P ./models/controlnet/\n",
|
||||
"#!wget -c https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank256/control-lora-sketch-rank256.safetensors -P ./models/controlnet/\n",
|
||||
"\n",
|
||||
"# Controlnet Preprocessor nodes by Fannovel16\n",
|
||||
"#!cd custom_nodes && git clone https://github.com/Fannovel16/comfy_controlnet_preprocessors; cd comfy_controlnet_preprocessors && python install.py\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# GLIGEN\n",
|
||||
"#!wget -c https://huggingface.co/comfyanonymous/GLIGEN_pruned_safetensors/resolve/main/gligen_sd14_textbox_pruned_fp16.safetensors -P ./models/gligen/\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# ESRGAN upscale model\n",
|
||||
"#!wget -c https://github.com/xinntao/Real-ESRGAN/releases/download/v0.1.0/RealESRGAN_x4plus.pth -P ./models/upscale_models/\n",
|
||||
"#!wget -c https://huggingface.co/sberbank-ai/Real-ESRGAN/resolve/main/RealESRGAN_x2.pth -P ./models/upscale_models/\n",
|
||||
"#!wget -c https://huggingface.co/sberbank-ai/Real-ESRGAN/resolve/main/RealESRGAN_x4.pth -P ./models/upscale_models/\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkkkkkkkkkkkkkk"
|
||||
},
|
||||
"source": [
|
||||
"### Run ComfyUI with cloudflared (Recommended Way)\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jjjjjjjjjjjjjj"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!wget -P ~ https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb\n",
|
||||
"!dpkg -i ~/cloudflared-linux-amd64.deb\n",
|
||||
"\n",
|
||||
"import subprocess\n",
|
||||
"import threading\n",
|
||||
"import time\n",
|
||||
"import socket\n",
|
||||
"import urllib.request\n",
|
||||
"\n",
|
||||
"def iframe_thread(port):\n",
|
||||
" while True:\n",
|
||||
" time.sleep(0.5)\n",
|
||||
" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n",
|
||||
" result = sock.connect_ex(('127.0.0.1', port))\n",
|
||||
" if result == 0:\n",
|
||||
" break\n",
|
||||
" sock.close()\n",
|
||||
" print(\"\\nComfyUI finished loading, trying to launch cloudflared (if it gets stuck here cloudflared is having issues)\\n\")\n",
|
||||
"\n",
|
||||
" p = subprocess.Popen([\"cloudflared\", \"tunnel\", \"--url\", \"http://127.0.0.1:{}\".format(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)\n",
|
||||
" for line in p.stderr:\n",
|
||||
" l = line.decode()\n",
|
||||
" if \"trycloudflare.com \" in l:\n",
|
||||
" print(\"This is the URL to access ComfyUI:\", l[l.find(\"http\"):], end='')\n",
|
||||
" #print(l, end='')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n",
|
||||
"\n",
|
||||
"!python main.py --dont-print-server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kkkkkkkkkkkkkk"
|
||||
},
|
||||
"source": [
|
||||
"### Run ComfyUI with localtunnel\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "jjjjjjjjjjjjj"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"!npm install -g localtunnel\n",
|
||||
"\n",
|
||||
"import subprocess\n",
|
||||
"import threading\n",
|
||||
"import time\n",
|
||||
"import socket\n",
|
||||
"import urllib.request\n",
|
||||
"\n",
|
||||
"def iframe_thread(port):\n",
|
||||
" while True:\n",
|
||||
" time.sleep(0.5)\n",
|
||||
" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n",
|
||||
" result = sock.connect_ex(('127.0.0.1', port))\n",
|
||||
" if result == 0:\n",
|
||||
" break\n",
|
||||
" sock.close()\n",
|
||||
" print(\"\\nComfyUI finished loading, trying to launch localtunnel (if it gets stuck here localtunnel is having issues)\\n\")\n",
|
||||
"\n",
|
||||
" print(\"The password/enpoint ip for localtunnel is:\", urllib.request.urlopen('https://ipv4.icanhazip.com').read().decode('utf8').strip(\"\\n\"))\n",
|
||||
" p = subprocess.Popen([\"lt\", \"--port\", \"{}\".format(port)], stdout=subprocess.PIPE)\n",
|
||||
" for line in p.stdout:\n",
|
||||
" print(line.decode(), end='')\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n",
|
||||
"\n",
|
||||
"!python main.py --dont-print-server"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "gggggggggg"
|
||||
},
|
||||
"source": [
|
||||
"### Run ComfyUI with colab iframe (use only in case the previous way with localtunnel doesn't work)\n",
|
||||
"\n",
|
||||
"You should see the ui appear in an iframe. If you get a 403 error, it's your firefox settings or an extension that's messing things up.\n",
|
||||
"\n",
|
||||
"If you want to open it in another window use the link.\n",
|
||||
"\n",
|
||||
"Note that some UI features like live image previews won't work because the colab iframe blocks websockets."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "hhhhhhhhhh"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import threading\n",
|
||||
"import time\n",
|
||||
"import socket\n",
|
||||
"def iframe_thread(port):\n",
|
||||
" while True:\n",
|
||||
" time.sleep(0.5)\n",
|
||||
" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n",
|
||||
" result = sock.connect_ex(('127.0.0.1', port))\n",
|
||||
" if result == 0:\n",
|
||||
" break\n",
|
||||
" sock.close()\n",
|
||||
" from google.colab import output\n",
|
||||
" output.serve_kernel_port_as_iframe(port, height=1024)\n",
|
||||
" print(\"to open it in a window you can open this link here:\")\n",
|
||||
" output.serve_kernel_port_as_window(port)\n",
|
||||
"\n",
|
||||
"threading.Thread(target=iframe_thread, daemon=True, args=(8188,)).start()\n",
|
||||
"\n",
|
||||
"!python main.py --dont-print-server"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"accelerator": "GPU",
|
||||
"colab": {
|
||||
"provenance": []
|
||||
},
|
||||
"gpuClass": "standard",
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -1,15 +1,62 @@
|
||||
[build-system]
|
||||
requires = ["setuptools >= 61.0"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "comfyui-manager"
|
||||
license = { text = "GPL-3.0-only" }
|
||||
version = "4.0.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."
|
||||
version = "3.17.2"
|
||||
license = { file = "LICENSE.txt" }
|
||||
dependencies = ["GitPython", "PyGithub", "matrix-client==0.4.0", "transformers", "huggingface-hub>0.20", "typer", "rich", "typing-extensions"]
|
||||
readme = "README.md"
|
||||
keywords = ["comfyui", "comfyui-manager"]
|
||||
|
||||
maintainers = [
|
||||
{ name = "Dr.Lt.Data", email = "dr.lt.data@gmail.com" },
|
||||
{ name = "Yoland Yan", email = "yoland@drip.art" },
|
||||
{ name = "James Kwon", email = "hongilkwon316@gmail.com" },
|
||||
{ name = "Robin Huang", email = "robin@drip.art" },
|
||||
]
|
||||
|
||||
classifiers = [
|
||||
"Development Status :: 4 - Beta",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
|
||||
]
|
||||
|
||||
dependencies = [
|
||||
"GitPython",
|
||||
"PyGithub",
|
||||
"matrix-client==0.4.0",
|
||||
"transformers",
|
||||
"huggingface-hub>0.20",
|
||||
"typer",
|
||||
"rich",
|
||||
"typing-extensions",
|
||||
"toml",
|
||||
"uv",
|
||||
"chardet"
|
||||
]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = ["pre-commit", "pytest", "ruff", "pytest-cov"]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/ltdrdata/ComfyUI-Manager"
|
||||
# Used by Comfy Registry https://comfyregistry.org
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "drltdata"
|
||||
DisplayName = "ComfyUI-Manager"
|
||||
Icon = ""
|
||||
[tool.setuptools.packages.find]
|
||||
where = ["."]
|
||||
include = ["comfyui_manager*"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 120
|
||||
target-version = "py39"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"E4", # default
|
||||
"E7", # default
|
||||
"E9", # default
|
||||
"F", # default
|
||||
"I", # isort-like behavior (import statement sorting)
|
||||
]
|
||||
|
||||
@@ -6,4 +6,6 @@ huggingface-hub>0.20
|
||||
typer
|
||||
rich
|
||||
typing-extensions
|
||||
toml
|
||||
toml
|
||||
uv
|
||||
chardet
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_enabled_subdirectories_with_files(base_directory):
|
||||
subdirs_with_files = []
|
||||
for subdir in os.listdir(base_directory):
|
||||
try:
|
||||
full_path = os.path.join(base_directory, subdir)
|
||||
if os.path.isdir(full_path) and not subdir.endswith(".disabled") and not subdir.startswith('.') and subdir != '__pycache__':
|
||||
print(f"## Install dependencies for '{subdir}'")
|
||||
requirements_file = os.path.join(full_path, "requirements.txt")
|
||||
install_script = os.path.join(full_path, "install.py")
|
||||
|
||||
if os.path.exists(requirements_file) or os.path.exists(install_script):
|
||||
subdirs_with_files.append((full_path, requirements_file, install_script))
|
||||
except Exception as e:
|
||||
print(f"EXCEPTION During Dependencies INSTALL on '{subdir}':\n{e}")
|
||||
|
||||
return subdirs_with_files
|
||||
|
||||
|
||||
def install_requirements(requirements_file_path):
|
||||
if os.path.exists(requirements_file_path):
|
||||
subprocess.run(["pip", "install", "-r", requirements_file_path])
|
||||
|
||||
|
||||
def run_install_script(install_script_path):
|
||||
if os.path.exists(install_script_path):
|
||||
subprocess.run(["python", install_script_path])
|
||||
|
||||
|
||||
custom_nodes_directory = "custom_nodes"
|
||||
subdirs_with_files = get_enabled_subdirectories_with_files(custom_nodes_directory)
|
||||
|
||||
|
||||
for subdir, requirements_file, install_script in subdirs_with_files:
|
||||
install_requirements(requirements_file)
|
||||
run_install_script(install_script)
|
||||
@@ -1,21 +0,0 @@
|
||||
git clone https://github.com/comfyanonymous/ComfyUI
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager
|
||||
cd ..
|
||||
python -m venv venv
|
||||
source venv/bin/activate
|
||||
python -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
python -m pip install -r requirements.txt
|
||||
python -m pip install -r custom_nodes/comfyui-manager/requirements.txt
|
||||
cd ..
|
||||
echo "#!/bin/bash" > run_gpu.sh
|
||||
echo "cd ComfyUI" >> run_gpu.sh
|
||||
echo "source venv/bin/activate" >> run_gpu.sh
|
||||
echo "python main.py --preview-method auto" >> run_gpu.sh
|
||||
chmod +x run_gpu.sh
|
||||
|
||||
echo "#!/bin/bash" > run_cpu.sh
|
||||
echo "cd ComfyUI" >> run_cpu.sh
|
||||
echo "source venv/bin/activate" >> run_cpu.sh
|
||||
echo "python main.py --preview-method auto --cpu" >> run_cpu.sh
|
||||
chmod +x run_cpu.sh
|
||||
@@ -1,17 +0,0 @@
|
||||
git clone https://github.com/comfyanonymous/ComfyUI
|
||||
cd ComfyUI/custom_nodes
|
||||
git clone https://github.com/ltdrdata/ComfyUI-Manager comfyui-manager
|
||||
cd ..
|
||||
python -m venv venv
|
||||
call venv/Scripts/activate
|
||||
python -m pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu121
|
||||
python -m pip install -r requirements.txt
|
||||
python -m pip install -r custom_nodes/ComfyUI-Manager/requirements.txt
|
||||
cd ..
|
||||
echo "cd ComfyUI" >> run_gpu.bat
|
||||
echo "call venv/Scripts/activate" >> run_gpu.bat
|
||||
echo "python main.py" >> run_gpu.bat
|
||||
|
||||
echo "cd ComfyUI" >> run_cpu.bat
|
||||
echo "call venv/Scripts/activate" >> run_cpu.bat
|
||||
echo "python main.py --cpu" >> run_cpu.bat
|
||||
@@ -1,2 +0,0 @@
|
||||
.\python_embeded\python.exe -s -m pip install gitpython
|
||||
.\python_embeded\python.exe -c "import git; git.Repo.clone_from('https://github.com/ltdrdata/ComfyUI-Manager', './ComfyUI/custom_nodes/comfyui-manager')"
|
||||
Reference in New Issue
Block a user