mirror of
https://github.com/Comfy-Org/ComfyUI-Manager.git
synced 2025-12-16 18:02:58 +08:00
Merge branch 'main' into fix-notice-board-padding
This commit is contained in:
commit
27dd2a95fe
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,6 +7,7 @@ config.ini
|
||||
snapshots/**
|
||||
startup-scripts/**
|
||||
.openart_key
|
||||
.youml
|
||||
matrix_auth
|
||||
channels.list
|
||||
comfyworkflows_sharekey
|
||||
|
||||
45
README.md
45
README.md
@ -63,6 +63,7 @@ This repository provides Colab notebooks that allow you to install and use Comfy
|
||||
* Support for automatically installing dependencies of custom nodes upon restarting Colab notebooks.
|
||||
|
||||
## Changes
|
||||
* **2.2.3** Support Components System
|
||||
* **0.29** Add `Update all` feature
|
||||
* **0.25** support db channel
|
||||
* You can directly modify the db channel settings in the `config.ini` file.
|
||||
@ -133,7 +134,9 @@ This repository provides Colab notebooks that allow you to install and use Comfy
|
||||
 
|
||||
|
||||
* You can share the workflow by clicking the Share button at the bottom of the main menu or selecting Share Output from the Context Menu of the Image node.
|
||||
* Currently, it supports sharing via [https://comfyworkflows.com/](https://comfyworkflows.com/) and [https://openart.ai](https://openart.ai/workflows/dev), as well as through the Matrix channel.
|
||||
* Currently, it supports sharing via [https://comfyworkflows.com/](https://comfyworkflows.com/),
|
||||
[https://openart.ai](https://openart.ai/workflows/dev), [https://youml.com](https://youml.com)
|
||||
as well as through the Matrix channel.
|
||||
|
||||

|
||||
|
||||
@ -206,6 +209,41 @@ NODE_CLASS_MAPPINGS.update({
|
||||
* **All scripts are executed from the root path of the corresponding custom node.**
|
||||
|
||||
|
||||
## Component Sharing
|
||||
* **Copy & Paste**
|
||||
* [Demo Page](https://ltdrdata.github.io/component-demo/)
|
||||
* When pasting a component from the clipboard, it supports text in the following JSON format. (text/plain)
|
||||
```
|
||||
{
|
||||
"kind": "ComfyUI Components",
|
||||
"timestamp": <current timestamp>,
|
||||
"components":
|
||||
{
|
||||
<component name>: <component nodedata>
|
||||
}
|
||||
}
|
||||
```
|
||||
* `<current timestamp>` Ensure that the timestamp is always unique.
|
||||
* "components" should have the same structure as the content of the file stored in ComfyUI-Manager/components.
|
||||
* `<component name>`: The name should be in the format `<prefix>::<node name>`.
|
||||
* `<compnent nodeata>`: In the nodedata of the group node.
|
||||
* `<version>`: Only two formats are allowed: `major.minor.patch` or `major.minor`. (e.g. `1.0`, `2.2.1`)
|
||||
* `<datetime>`: Saved time
|
||||
* `<packname>`: If the packname is not empty, the category becomes packname/workflow, and it is saved in the <packname>.pack file in ComfyUI-Manager/components.
|
||||
* `<category>`: If there is neither a category nor a packname, it is saved in the components category.
|
||||
```
|
||||
"version":"1.0",
|
||||
"datetime": 1705390656516,
|
||||
"packname": "mypack",
|
||||
"category": "util/pipe",
|
||||
```
|
||||
* **Drag & Drop**
|
||||
* Dragging and dropping a `.pack` or `.json` file will add the corresponding components.
|
||||
* Example pack: [Impact.pack](misc/Impact.pack)
|
||||
|
||||
* Dragging and dropping or pasting a single component will add a node. However, when adding multiple components, nodes will not be added.
|
||||
|
||||
|
||||
## Support of missing nodes installation
|
||||
|
||||

|
||||
@ -230,6 +268,7 @@ NODE_CLASS_MAPPINGS.update({
|
||||
* https://github.com/senshilabs/NINJA-plugin
|
||||
* https://github.com/MockbaTheBorg/Nodes
|
||||
* https://github.com/StartHua/Comfyui_GPT_Story
|
||||
* https://github.com/NielsGercama/comfyui_customsampling
|
||||
|
||||
|
||||
## Roadmap
|
||||
@ -237,11 +276,11 @@ NODE_CLASS_MAPPINGS.update({
|
||||
- [x] System displaying information about failed custom nodes import.
|
||||
- [x] Guide for missing nodes in ComfyUI vanilla nodes.
|
||||
- [x] Collision checking system for nodes with the same ID across extensions.
|
||||
- [x] Template sharing system. (-> Component system based on Group Nodes)
|
||||
- [x] 3rd party API system.
|
||||
- [ ] Auto migration for custom nodes with changed structures.
|
||||
- [ ] Version control feature for nodes.
|
||||
- [ ] List of currently used custom nodes.
|
||||
- [ ] Template sharing system.
|
||||
- [ ] 3rd party API system.
|
||||
- [ ] Download support multiple model download.
|
||||
- [ ] Model download via url.
|
||||
- [ ] List sorting.
|
||||
|
||||
135
__init__.py
135
__init__.py
@ -28,12 +28,11 @@ except:
|
||||
print(f"[WARN] ComfyUI-Manager: Your ComfyUI version is outdated. Please update to the latest version.")
|
||||
|
||||
|
||||
version = [1, 25, 3]
|
||||
version = [2, 2, 3]
|
||||
version_str = f"V{version[0]}.{version[1]}" + (f'.{version[2]}' if len(version) > 2 else '')
|
||||
print(f"### Loading: ComfyUI-Manager ({version_str})")
|
||||
|
||||
|
||||
required_comfyui_revision = 1793
|
||||
comfy_ui_hash = "-"
|
||||
|
||||
cache_lock = threading.Lock()
|
||||
@ -118,6 +117,7 @@ local_db_alter = os.path.join(comfyui_manager_path, "alter-list.json")
|
||||
local_db_custom_node_list = os.path.join(comfyui_manager_path, "custom-node-list.json")
|
||||
local_db_extension_node_mappings = os.path.join(comfyui_manager_path, "extension-node-map.json")
|
||||
git_script_path = os.path.join(os.path.dirname(__file__), "git_helper.py")
|
||||
components_path = os.path.join(comfyui_manager_path, 'components')
|
||||
|
||||
startup_script_path = os.path.join(comfyui_manager_path, "startup-scripts")
|
||||
config_path = os.path.join(os.path.dirname(__file__), "config.ini")
|
||||
@ -169,7 +169,9 @@ def write_config():
|
||||
'git_exe': get_config()['git_exe'],
|
||||
'channel_url': get_config()['channel_url'],
|
||||
'share_option': get_config()['share_option'],
|
||||
'bypass_ssl': get_config()['bypass_ssl']
|
||||
'bypass_ssl': get_config()['bypass_ssl'],
|
||||
'default_ui': get_config()['default_ui'],
|
||||
'component_policy': get_config()['component_policy'],
|
||||
}
|
||||
with open(config_path, 'w') as configfile:
|
||||
config.write(configfile)
|
||||
@ -188,6 +190,8 @@ def read_config():
|
||||
'channel_url': default_conf['channel_url'] if 'channel_url' in default_conf else 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main',
|
||||
'share_option': default_conf['share_option'] if 'share_option' in default_conf else 'all',
|
||||
'bypass_ssl': default_conf['bypass_ssl'] if 'bypass_ssl' in default_conf else False,
|
||||
'default_ui': default_conf['default_ui'] if 'default_ui' in default_conf else 'none',
|
||||
'component_policy': default_conf['component_policy'] if 'component_policy' in default_conf else 'workflow',
|
||||
}
|
||||
|
||||
except Exception:
|
||||
@ -197,7 +201,9 @@ def read_config():
|
||||
'git_exe': '',
|
||||
'channel_url': 'https://raw.githubusercontent.com/ltdrdata/ComfyUI-Manager/main',
|
||||
'share_option': 'all',
|
||||
'bypass_ssl': False
|
||||
'bypass_ssl': False,
|
||||
'default_ui': 'none',
|
||||
'component_policy': 'workflow'
|
||||
}
|
||||
|
||||
|
||||
@ -234,11 +240,19 @@ def set_preview_method(method):
|
||||
get_config()['preview_method'] = args.preview_method
|
||||
|
||||
|
||||
set_preview_method(get_config()['preview_method'])
|
||||
|
||||
|
||||
def set_badge_mode(mode):
|
||||
get_config()['badge_mode'] = mode
|
||||
|
||||
|
||||
set_preview_method(get_config()['preview_method'])
|
||||
def set_default_ui_mode(mode):
|
||||
get_config()['default_ui'] = mode
|
||||
|
||||
|
||||
def set_component_policy(mode):
|
||||
get_config()['component_policy'] = mode
|
||||
|
||||
|
||||
def try_install_script(url, repo_path, install_cmd):
|
||||
@ -1749,6 +1763,28 @@ async def badge_mode(request):
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/default_ui")
|
||||
async def default_ui_mode(request):
|
||||
if "value" in request.rel_url.query:
|
||||
set_default_ui_mode(request.rel_url.query['value'])
|
||||
write_config()
|
||||
else:
|
||||
return web.Response(text=get_config()['default_ui'], status=200)
|
||||
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/component/policy")
|
||||
async def component_policy(request):
|
||||
if "value" in request.rel_url.query:
|
||||
set_component_policy(request.rel_url.query['value'])
|
||||
write_config()
|
||||
else:
|
||||
return web.Response(text=get_config()['component_policy'], status=200)
|
||||
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/channel_url_list")
|
||||
async def channel_url_list(request):
|
||||
channels = get_channel_dict()
|
||||
@ -1821,6 +1857,64 @@ def restart(self):
|
||||
return os.execv(sys.executable, [sys.executable] + sys.argv)
|
||||
|
||||
|
||||
def sanitize_filename(input_string):
|
||||
# 알파벳, 숫자, 및 밑줄 이외의 문자를 밑줄로 대체
|
||||
result_string = re.sub(r'[^a-zA-Z0-9_]', '_', input_string)
|
||||
return result_string
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/manager/component/save")
|
||||
async def save_component(request):
|
||||
try:
|
||||
data = await request.json()
|
||||
name = data['name']
|
||||
workflow = data['workflow']
|
||||
|
||||
if not os.path.exists(components_path):
|
||||
os.mkdir(components_path)
|
||||
|
||||
if 'packname' in workflow and workflow['packname'] != '':
|
||||
sanitized_name = sanitize_filename(workflow['packname'])+'.pack'
|
||||
else:
|
||||
sanitized_name = sanitize_filename(name)+'.json'
|
||||
|
||||
filepath = os.path.join(components_path, sanitized_name)
|
||||
components = {}
|
||||
if os.path.exists(filepath):
|
||||
with open(filepath) as f:
|
||||
components = json.load(f)
|
||||
|
||||
components[name] = workflow
|
||||
|
||||
with open(filepath, 'w') as f:
|
||||
json.dump(components, f, indent=4, sort_keys=True)
|
||||
return web.Response(text=filepath, status=200)
|
||||
except:
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/manager/component/loads")
|
||||
async def load_components(request):
|
||||
try:
|
||||
json_files = [f for f in os.listdir(components_path) if f.endswith('.json')]
|
||||
pack_files = [f for f in os.listdir(components_path) if f.endswith('.pack')]
|
||||
|
||||
components = {}
|
||||
for json_file in json_files + pack_files:
|
||||
file_path = os.path.join(components_path, json_file)
|
||||
with open(file_path, 'r') as file:
|
||||
try:
|
||||
# When there is a conflict between the .pack and the .json, the pack takes precedence and overrides.
|
||||
components.update(json.load(file))
|
||||
except json.JSONDecodeError as e:
|
||||
print(f"[ComfyUI-Manager] Error decoding component file in file {json_file}: {e}")
|
||||
|
||||
return web.json_response(components)
|
||||
except Exception as e:
|
||||
print(f"[ComfyUI-Manager] failed to load components\n{e}")
|
||||
return web.Response(status=400)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/share_option")
|
||||
async def share_option(request):
|
||||
if "value" in request.rel_url.query:
|
||||
@ -1874,6 +1968,22 @@ def get_comfyworkflows_auth():
|
||||
return None
|
||||
|
||||
|
||||
def get_youml_settings():
|
||||
if not os.path.exists(os.path.join(comfyui_manager_path, ".youml")):
|
||||
return None
|
||||
try:
|
||||
with open(os.path.join(comfyui_manager_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(comfyui_manager_path, ".youml"), "w") as f:
|
||||
f.write(settings)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/get_openart_auth")
|
||||
async def api_get_openart_auth(request):
|
||||
# print("Getting stored Matrix credentials...")
|
||||
@ -1901,6 +2011,21 @@ async def api_get_matrix_auth(request):
|
||||
return web.json_response(matrix_auth)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/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))
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/manager/youml/settings")
|
||||
async def api_set_youml_settings(request):
|
||||
json_data = await request.json()
|
||||
set_youml_settings(json.dumps(json_data))
|
||||
return web.Response(status=200)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/manager/get_comfyworkflows_auth")
|
||||
async def api_get_comfyworkflows_auth(request):
|
||||
# Check if the user has provided Matrix credentials in a file called 'matrix_accesstoken'
|
||||
|
||||
@ -194,6 +194,11 @@
|
||||
"id":"https://github.com/shiimizu/ComfyUI-TiledDiffusion",
|
||||
"tags":"multidiffusion",
|
||||
"description": "This extension provides custom nodes for [a/Mixture of Diffusers](https://github.com/albarji/mixture-of-diffusers) and [a/MultiDiffusion](https://github.com/omerbt/MultiDiffusion)"
|
||||
},
|
||||
{
|
||||
"id":"https://github.com/abyz22/image_control",
|
||||
"tags":"BMAB",
|
||||
"description": "This extension provides some alternative functionalities of the [a/sd-webui-bmab](https://github.com/portu-sim/sd-webui-bmab) extension."
|
||||
}
|
||||
]
|
||||
}
|
||||
2
components/.gitignore
vendored
Normal file
2
components/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.json
|
||||
*.pack
|
||||
@ -2395,7 +2395,7 @@
|
||||
"https://github.com/THtianhao/ComfyUI-FaceChain"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:FC FaceFusion, FC StyleLoraLoad, FC FaceDetection, FC CropMask, FC ReplaceImage, FC CropBottom, FC CropAndPaste, FC MaskOP"
|
||||
"description": "The official ComfyUI version of facechain greatly improves the speed of reasoning and has great custom process controls."
|
||||
},
|
||||
{
|
||||
"author": "zer0TF",
|
||||
@ -2417,6 +2417,16 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "A text-to-speech plugin used under ComfyUI. It utilizes the Microsoft Speech TTS interface to convert text content into MP3 format audio files."
|
||||
},
|
||||
{
|
||||
"author": "chflame163",
|
||||
"title": "ComfyUI_WordCloud",
|
||||
"reference": "https://github.com/chflame163/ComfyUI_WordCloud",
|
||||
"files": [
|
||||
"https://github.com/chflame163/ComfyUI_WordCloud"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:Word Cloud, Load Text File"
|
||||
},
|
||||
{
|
||||
"author": "drustan-hawk",
|
||||
"title": "primitive-types",
|
||||
@ -2858,7 +2868,7 @@
|
||||
"https://github.com/GTSuya-Studio/ComfyUI-Gtsuya-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI-GTSuya-Nodes is a ComyUI extension designed to add several wildcards supports into ComfyUI. Wildcards allow you to use __name__ syntax in your prompt to get a random line from a file named name.txt in a wildcards directory."
|
||||
"description": "ComfyUI-GTSuya-Nodes is a ComfyUI extension designed to add several wildcards supports into ComfyUI. Wildcards allow you to use __name__ syntax in your prompt to get a random line from a file named name.txt in a wildcards directory."
|
||||
},
|
||||
{
|
||||
"author": "oyvindg",
|
||||
@ -2970,6 +2980,16 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:XL DreamBooth LoRA, S3 Bucket LoRA"
|
||||
},
|
||||
{
|
||||
"author": "komojini",
|
||||
"title": "komojini-comfyui-nodes",
|
||||
"reference": "https://github.com/komojini/komojini-comfyui-nodes",
|
||||
"files": [
|
||||
"https://github.com/komojini/komojini-comfyui-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:YouTube Video Loader. Custom ComfyUI Nodes for video generation"
|
||||
},
|
||||
{
|
||||
"author": "ZHO-ZHO-ZHO",
|
||||
"title": "ComfyUI-Text_Image-Composite [WIP]",
|
||||
@ -3509,7 +3529,7 @@
|
||||
"https://github.com/vienteck/ComfyUI-Chat-GPT-Integration"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
|
||||
|
||||
"description": "This extension is a reimagined version based on the [a/ComfyUI-QualityOfLifeSuit_Omar92](https://github.com/omar92/ComfyUI-QualityOfLifeSuit_Omar92) extension, and it supports integration with ChatGPT through the new OpenAI API.\nNOTE: See detailed installation instructions on the [a/repository](https://github.com/vienteck/ComfyUI-Chat-GPT-Integration)."
|
||||
},
|
||||
{
|
||||
@ -4034,6 +4054,66 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Download the weights of MotionCtrl [a/motionctrl.pth](https://huggingface.co/TencentARC/MotionCtrl/blob/main/motionctrl.pth) and put it to ComfyUI/models/checkpoints"
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-Motion-Vector-Extractor",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-Motion-Vector-Extractor",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-Motion-Vector-Extractor"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: that we currently provide the package only for x86-64 linux, such as Ubuntu or Debian, and Python 3.8, 3.9, and 3.10."
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-MotionCtrl-SVD",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-MotionCtrl-SVD",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-MotionCtrl-SVD"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Download the weights of MotionCtrl-SVD [a/motionctrl_svd.ckpt](https://huggingface.co/TencentARC/MotionCtrl/blob/main/motionctrl_svd.ckpt) and put it to ComfyUI/models/checkpoints"
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-DragNUWA",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-DragNUWA",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-DragNUWA"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Download the weights of DragNUWA [a/drag_nuwa_svd.pth](https://drive.google.com/file/d/1Z4JOley0SJCb35kFF4PCc6N6P1ftfX4i/view) and put it to ComfyUI/models/checkpoints/drag_nuwa_svd.pth\n[w/Due to changes in the torch package and versions of many other packages, it may disrupt your installation environment.]"
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-Moore-AnimateAnyone",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-Moore-AnimateAnyone",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-Moore-AnimateAnyone"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Run python tools/download_weights.py first to download weights automatically"
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-I2VGEN-XL",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-I2VGEN-XL",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-I2VGEN-XL"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is an implementation of [a/i2vgen-xl](https://github.com/ali-vilab/i2vgen-xl)"
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-LightGlue",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-LightGlue",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-LightGlue"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is an ComfyUI implementation of LightGlue to generate motion brush"
|
||||
},
|
||||
{
|
||||
"author": "alexopus",
|
||||
"title": "ComfyUI Image Saver",
|
||||
@ -4052,7 +4132,7 @@
|
||||
"https://github.com/kft334/Knodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Image(s) To Websocket (Base64)"
|
||||
"description": "Nodes: Image(s) To Websocket (Base64), Load Image (Base64),Load Images (Base64)"
|
||||
},
|
||||
{
|
||||
"author": "MrForExample",
|
||||
@ -4064,6 +4144,16 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "An extensive node suite that enables ComfyUI to process 3D inputs (Mesh & UV Texture, etc) using cutting edge algorithms (3DGS, NeRF, etc.)"
|
||||
},
|
||||
{
|
||||
"author": "Mr.ForExample",
|
||||
"title": "ComfyUI-AnimateAnyone-Evolved",
|
||||
"reference": "https://github.com/MrForExample/ComfyUI-AnimateAnyone-Evolved",
|
||||
"files": [
|
||||
"https://github.com/MrForExample/ComfyUI-AnimateAnyone-Evolved"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Improved AnimateAnyone implementation that allows you to use the opse image sequence and reference image to generate stylized video.\nThe current goal of this project is to achieve desired pose2video result with 1+FPS on GPUs that are equal to or better than RTX 3080!🚀\n[w/The torch environment may be compromised due to version issues as some torch-related packages are being reinstalled.]"
|
||||
},
|
||||
{
|
||||
"author": "Hangover3832",
|
||||
"title": "ComfyUI-Hangover-Nodes",
|
||||
@ -4124,6 +4214,230 @@
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:SegToMask"
|
||||
},
|
||||
{
|
||||
"author": "glowcone",
|
||||
"title": "Load Image From Base64 URI",
|
||||
"reference": "https://github.com/glowcone/comfyui-base64-to-image",
|
||||
"files": [
|
||||
"https://github.com/glowcone/comfyui-base64-to-image"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: LoadImageFromBase64. Loads an image and its transparency mask from a base64-encoded data URI for easy API connection."
|
||||
},
|
||||
{
|
||||
"author": "AInseven",
|
||||
"title": "ComfyUI-fastblend",
|
||||
"reference": "https://github.com/AInseven/ComfyUI-fastblend",
|
||||
"files": [
|
||||
"https://github.com/AInseven/ComfyUI-fastblend"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "fastblend for comfyui, and other nodes that I write for video2video. rebatch image, my openpose"
|
||||
},
|
||||
{
|
||||
"author": "HebelHuber",
|
||||
"title": "comfyui-enhanced-save-node",
|
||||
"reference": "https://github.com/HebelHuber/comfyui-enhanced-save-node",
|
||||
"files": [
|
||||
"https://github.com/HebelHuber/comfyui-enhanced-save-node"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:Enhanced Save Node"
|
||||
},
|
||||
{
|
||||
"author": "LarryJane491",
|
||||
"title": "Lora-Training-in-Comfy",
|
||||
"reference": "https://github.com/LarryJane491/Lora-Training-in-Comfy",
|
||||
"files": [
|
||||
"https://github.com/LarryJane491/Lora-Training-in-Comfy"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "If you see this message, your ComfyUI-Manager is outdated.\nRecent channel provides only the list of the latest nodes. If you want to find the complete node list, please go to the Default channel.\nMaking LoRA has never been easier!"
|
||||
},
|
||||
{
|
||||
"author": "LarryJane491",
|
||||
"title": "Image-Captioning-in-ComfyUI",
|
||||
"reference": "https://github.com/LarryJane491/Image-Captioning-in-ComfyUI",
|
||||
"files": [
|
||||
"https://github.com/LarryJane491/Image-Captioning-in-ComfyUI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "The LoRA Caption custom nodes, just like their name suggests, allow you to caption images so they are ready for LoRA training."
|
||||
},
|
||||
{
|
||||
"author": "Layer-norm",
|
||||
"title": "Comfyui lama remover",
|
||||
"reference": "https://github.com/Layer-norm/comfyui-lama-remover",
|
||||
"files": [
|
||||
"https://github.com/Layer-norm/comfyui-lama-remover"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A very simple ComfyUI node to remove item with mask."
|
||||
},
|
||||
{
|
||||
"author": "Taremin",
|
||||
"title": "ComfyUI Prompt ExtraNetworks",
|
||||
"reference": "https://github.com/Taremin/comfyui-prompt-extranetworks",
|
||||
"files": [
|
||||
"https://github.com/Taremin/comfyui-prompt-extranetworks"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Instead of LoraLoader or HypernetworkLoader, it receives a prompt and loads and applies LoRA or HN based on the specifications within the prompt. The main purpose of this custom node is to allow changes without reconnecting the LoraLoader node when the prompt is randomly altered, etc."
|
||||
},
|
||||
{
|
||||
"author": "Taremin",
|
||||
"title": "ComfyUI String Tools",
|
||||
"reference": "https://github.com/Taremin/comfyui-string-tools",
|
||||
"files": [
|
||||
"https://github.com/Taremin/comfyui-string-tools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": " This extension provides the StringToolsConcat node, which concatenates multiple texts, and the StringToolsRandomChoice node, which selects one randomly from multiple texts."
|
||||
},
|
||||
{
|
||||
"author": "Taremin",
|
||||
"title": "WebUI Monaco Prompt",
|
||||
"reference": "https://github.com/Taremin/webui-monaco-prompt",
|
||||
"files": [
|
||||
"https://github.com/Taremin/webui-monaco-prompt"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Make it possible to edit the prompt using the Monaco Editor, an editor implementation used in VSCode.\nNOTE: This extension supports both ComfyUI and A1111 simultaneously."
|
||||
},
|
||||
{
|
||||
"author": "foxtrot-roger",
|
||||
"title": "RF Nodes",
|
||||
"reference": "https://github.com/foxtrot-roger/comfyui-rf-nodes",
|
||||
"files": [
|
||||
"https://github.com/foxtrot-roger/comfyui-rf-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A bunch of nodes that can be useful to manipulate primitive types (numbers, text, ...) Also some helpers to generate text and timestamps."
|
||||
},
|
||||
{
|
||||
"author": "abyz22",
|
||||
"title": "image_control",
|
||||
"reference": "https://github.com/abyz22/image_control",
|
||||
"files": [
|
||||
"https://github.com/abyz22/image_control"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:abyz22_Padding Image, abyz22_ImpactWildcardEncode, abyz22_setimageinfo, abyz22_SaveImage, abyz22_ImpactWildcardEncode_GetPrompt, abyz22_SetQueue, abyz22_drawmask, abyz22_FirstNonNull, abyz22_blendimages, abyz22_blend_onecolor"
|
||||
},
|
||||
{
|
||||
"author": "HAL41",
|
||||
"title": "ComfyUI aichemy nodes",
|
||||
"reference": "https://github.com/HAL41/ComfyUI-aichemy-nodes",
|
||||
"files": [
|
||||
"https://github.com/HAL41/ComfyUI-aichemy-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Simple node to handle scaling of YOLOv8 segmentation masks"
|
||||
},
|
||||
{
|
||||
"author": "nkchocoai",
|
||||
"title": "ComfyUI-SizeFromPresets",
|
||||
"reference": "https://github.com/nkchocoai/ComfyUI-SizeFromPresets",
|
||||
"files": [
|
||||
"https://github.com/nkchocoai/ComfyUI-SizeFromPresets"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Add a node that outputs width and height of the size selected from the preset (.csv)."
|
||||
},
|
||||
{
|
||||
"author": "JaredTherriault",
|
||||
"title": "ComfyUI-JNodes",
|
||||
"reference": "https://github.com/JaredTherriault/ComfyUI-JNodes",
|
||||
"files": [
|
||||
"https://github.com/JaredTherriault/ComfyUI-JNodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "python and web UX improvements for ComfyUI.\n[w/'DynamicPrompts.js' and 'EditAttention.js' from the core, along with 'ImageFeed.js' and 'favicon.js' from the custom scripts of pythongosssss, are not compatible. Therefore, manual deletion of these files is required to use this web extension.]"
|
||||
},
|
||||
{
|
||||
"author": "prozacgod",
|
||||
"title": "ComfyUI Multi-Workspace",
|
||||
"reference": "https://github.com/prozacgod/comfyui-pzc-multiworkspace",
|
||||
"files": [
|
||||
"https://github.com/prozacgod/comfyui-pzc-multiworkspace"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A simple, quick, and dirty implementation of multiple workspaces within ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "Siberpone",
|
||||
"title": "Lazy Pony Prompter",
|
||||
"reference": "https://github.com/Siberpone/lazy-pony-prompter",
|
||||
"files": [
|
||||
"https://github.com/Siberpone/lazy-pony-prompter"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A pony prompt helper extension for AUTOMATIC1111's Stable Diffusion Web UI and ComfyUI that utilizes the full power of your favorite booru query syntax. Currently supports [a/Derpibooru](https://derpibooru/org) and [a/E621](https://e621.net/)."
|
||||
},
|
||||
{
|
||||
"author": "kijai",
|
||||
"title": "ComfyUI-DDColor",
|
||||
"reference": "https://github.com/kijai/ComfyUI-DDColor",
|
||||
"files": [
|
||||
"https://github.com/kijai/ComfyUI-DDColor"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Node to use [a/DDColor](https://github.com/piddnad/DDColor) in ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "chflame163",
|
||||
"title": "ComfyUI Layer Style",
|
||||
"reference": "https://github.com/chflame163/ComfyUI_LayerStyle",
|
||||
"files": [
|
||||
"https://github.com/chflame163/ComfyUI_LayerStyle"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A set of nodes for ComfyUI it generate image like Adobe Photoshop's Layer Style. the Drop Shadow is first completed node, and follow-up work is in progress."
|
||||
},
|
||||
{
|
||||
"author": "dave-palt",
|
||||
"title": "comfyui_DSP_imagehelpers",
|
||||
"reference": "https://github.com/dave-palt/comfyui_DSP_imagehelpers",
|
||||
"files": [
|
||||
"https://github.com/dave-palt/comfyui_DSP_imagehelpers"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: DSP Image Concat"
|
||||
},
|
||||
{
|
||||
"author": "Inzaniak",
|
||||
"title": "Ranbooru for ComfyUI",
|
||||
"reference": "https://github.com/Inzaniak/comfyui-ranbooru",
|
||||
"files": [
|
||||
"https://github.com/Inzaniak/comfyui-ranbooru"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Ranbooru is an extension for the comfyUI. The purpose of this extension is to add a node that gets a random set of tags from boorus pictures. This is mostly being used to help me test my checkpoints on a large variety of"
|
||||
},
|
||||
{
|
||||
"author": "Miosp",
|
||||
"title": "ComfyUI-FBCNN",
|
||||
"reference": "https://github.com/Miosp/ComfyUI-FBCNN",
|
||||
"files": [
|
||||
"https://github.com/Miosp/ComfyUI-FBCNN"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI implementation of [a/FBCNN](https://github.com/jiaxi-jiang/FBCNN). This is a JPEG artifact/compression removal tool, which can work automatically or with the help of a user."
|
||||
},
|
||||
{
|
||||
"author": "JcandZero",
|
||||
"title": "ComfyUI_GLM4Node",
|
||||
"reference": "https://github.com/JcandZero/ComfyUI_GLM4Node",
|
||||
"files": [
|
||||
"https://github.com/JcandZero/ComfyUI_GLM4Node"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "GLM4 Vision Integration"
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
{
|
||||
"author": "Ser-Hilary",
|
||||
@ -4386,6 +4700,16 @@
|
||||
"install_type": "copy",
|
||||
"description": "Nodes:Simple Gooogle Translator Client, Simple Mustache Parameter Switcher, Simple Latent Resolution Solver."
|
||||
},
|
||||
{
|
||||
"author": "underclockeddev",
|
||||
"title": "Preview Subselection Node for ComfyUI",
|
||||
"reference": "https://github.com/underclockeddev/ComfyUI-PreviewSubselection-Node",
|
||||
"files": [
|
||||
"https://github.com/underclockeddev/ComfyUI-PreviewSubselection-Node/raw/master/preview_subselection.py"
|
||||
],
|
||||
"install_type": "copy",
|
||||
"description": "A node which takes in x, y, width, height, total width, and total height, in order to accurately represent the area of an image which is covered by area-based conditioning."
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,23 +1,61 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js"
|
||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||
import { ShareDialog, SUPPORTED_OUTPUT_NODE_TYPES, getPotentialOutputsAndOutputNodes, ShareDialogChooser, showOpenArtShareDialog, showShareDialog } from "./comfyui-share-common.js";
|
||||
import {
|
||||
ShareDialog,
|
||||
SUPPORTED_OUTPUT_NODE_TYPES,
|
||||
getPotentialOutputsAndOutputNodes,
|
||||
ShareDialogChooser,
|
||||
showOpenArtShareDialog,
|
||||
showShareDialog,
|
||||
showYouMLShareDialog
|
||||
} from "./comfyui-share-common.js";
|
||||
import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
||||
import { CustomNodesInstaller } from "./custom-nodes-downloader.js";
|
||||
import { AlternativesInstaller } from "./a1111-alter-downloader.js";
|
||||
import { SnapshotManager } from "./snapshot.js";
|
||||
import { ModelInstaller } from "./model-downloader.js";
|
||||
import { manager_instance, setManagerInstance, install_via_git_url, install_pip, rebootAPI, free_models } from "./common.js";
|
||||
import { manager_instance, setManagerInstance, install_via_git_url, install_pip, rebootAPI, free_models } from "./common.js";
|
||||
import { ComponentBuilderDialog, load_components, set_component_policy, getPureName } from "./components-manager.js";
|
||||
|
||||
var docStyle = document.createElement('style');
|
||||
docStyle.innerHTML = `
|
||||
#cm-manager-dialog {
|
||||
width: 1000px;
|
||||
height: 465px;
|
||||
height: 495px;
|
||||
box-sizing: content-box;
|
||||
z-index: 10000;
|
||||
}
|
||||
|
||||
.cb-widget {
|
||||
width: 400px;
|
||||
height: 25px;
|
||||
box-sizing: border-box;
|
||||
z-index: 10000;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.cb-widget-input {
|
||||
width: 305px;
|
||||
height: 25px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.cb-widget-input:disabled {
|
||||
background-color: #444444;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.cb-widget-input-label {
|
||||
width: 90px;
|
||||
height: 25px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
text-align: right;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.cm-menu-container {
|
||||
column-gap: 20px;
|
||||
display: flex;
|
||||
@ -63,6 +101,39 @@ docStyle.innerHTML = `
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#custom-nodes-grid a {
|
||||
color: #5555FF;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#custom-nodes-grid a:hover {
|
||||
color: #7777FF;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#external-models-grid a {
|
||||
color: #5555FF;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#external-models-grid a:hover {
|
||||
color: #7777FF;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#alternatives-grid a {
|
||||
color: #5555FF;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#alternatives-grid a:hover {
|
||||
color: #7777FF;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cm-notice-board {
|
||||
width: 290px;
|
||||
height: 230px;
|
||||
@ -220,6 +291,16 @@ const style = `
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cb-node-label {
|
||||
width: 400px;
|
||||
height:28px;
|
||||
color: black;
|
||||
background-color: #777777;
|
||||
font-size: 18px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#cm-close-button {
|
||||
width: calc(100% - 65px);
|
||||
bottom: 10px;
|
||||
@ -227,6 +308,16 @@ const style = `
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#cm-save-button {
|
||||
width: calc(100% - 65px);
|
||||
bottom:40px;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
}
|
||||
#cm-save-button:disabled {
|
||||
background-color: #444444;
|
||||
}
|
||||
|
||||
.pysssss-workflow-arrow-2 {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -327,6 +418,15 @@ function getNickname(node, nodename) {
|
||||
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];
|
||||
@ -641,6 +741,18 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
}),
|
||||
|
||||
$el("button.cm-button", {
|
||||
type: "button",
|
||||
textContent: "Install via Git URL",
|
||||
onclick: () => {
|
||||
var url = prompt("Please enter the URL of the Git repository to install", "");
|
||||
|
||||
if (url !== null) {
|
||||
install_via_git_url(url, self);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
$el("br", {}, []),
|
||||
update_all_button,
|
||||
update_comfyui_button,
|
||||
@ -673,6 +785,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
|
||||
// db mode
|
||||
this.datasrc_combo = document.createElement("select");
|
||||
this.datasrc_combo.setAttribute("title", "Configure where to retrieve node/model information. If set to 'local,' the channel is ignored, and if set to 'channel (remote),' it fetches the latest information each time the list is opened.");
|
||||
this.datasrc_combo.className = "cm-menu-combo";
|
||||
this.datasrc_combo.appendChild($el('option', { value: 'cache', text: 'DB: Channel (1day cache)' }, []));
|
||||
this.datasrc_combo.appendChild($el('option', { value: 'local', text: 'DB: Local' }, []));
|
||||
@ -680,6 +793,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
|
||||
// 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.");
|
||||
preview_combo.className = "cm-menu-combo";
|
||||
preview_combo.appendChild($el('option', { value: 'auto', text: 'Preview method: Auto' }, []));
|
||||
preview_combo.appendChild($el('option', { value: 'taesd', text: 'Preview method: TAESD (slow)' }, []));
|
||||
@ -688,7 +802,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
|
||||
api.fetchApi('/manager/preview_method')
|
||||
.then(response => response.text())
|
||||
.then(data => { preview_combo.value = data; })
|
||||
.then(data => { preview_combo.value = data; });
|
||||
|
||||
preview_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/manager/preview_method?value=${event.target.value}`);
|
||||
@ -696,6 +810,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
|
||||
// nickname
|
||||
let badge_combo = document.createElement("select");
|
||||
badge_combo.setAttribute("title", "Configure the content to be displayed on the badge at the top right corner of the node. The ID is the identifier of the node. If 'hide built-in' is selected, both unknown nodes and built-in nodes will be omitted, making them indistinguishable");
|
||||
badge_combo.className = "cm-menu-combo";
|
||||
badge_combo.appendChild($el('option', { value: 'none', text: 'Badge: None' }, []));
|
||||
badge_combo.appendChild($el('option', { value: 'nick', text: 'Badge: Nickname' }, []));
|
||||
@ -715,6 +830,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
|
||||
// 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. Note that the badge utilizes local information.");
|
||||
channel_combo.className = "cm-menu-combo";
|
||||
api.fetchApi('/manager/channel_url_list')
|
||||
.then(response => response.json())
|
||||
@ -739,12 +855,30 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
});
|
||||
|
||||
// default ui state
|
||||
let default_ui_combo = document.createElement("select");
|
||||
default_ui_combo.setAttribute("title", "Set the default state to be displayed in the main menu when the browser starts.");
|
||||
default_ui_combo.className = "cm-menu-combo";
|
||||
default_ui_combo.appendChild($el('option', { value: 'none', text: 'Default UI: None' }, []));
|
||||
default_ui_combo.appendChild($el('option', { value: 'history', text: 'Default UI: History' }, []));
|
||||
default_ui_combo.appendChild($el('option', { value: 'queue', text: 'Default UI: Queue' }, []));
|
||||
api.fetchApi('/manager/default_ui')
|
||||
.then(response => response.text())
|
||||
.then(data => { default_ui_combo.value = data; });
|
||||
|
||||
default_ui_combo.addEventListener('change', function (event) {
|
||||
api.fetchApi(`/manager/default_ui?value=${event.target.value}`);
|
||||
});
|
||||
|
||||
|
||||
// share
|
||||
let share_combo = document.createElement("select");
|
||||
share_combo.setAttribute("title", "Hide the share button in the main menu or set the default action upon clicking it. Additionally, configure the default share site when sharing via the context menu's share button.");
|
||||
share_combo.className = "cm-menu-combo";
|
||||
const share_options = [
|
||||
['none', 'None'],
|
||||
['openart', 'OpenArt AI'],
|
||||
['youml', 'YouML'],
|
||||
['matrix', 'Matrix Server'],
|
||||
['comfyworkflows', 'ComfyWorkflows'],
|
||||
['all', 'All'],
|
||||
@ -753,6 +887,25 @@ 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')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
@ -779,19 +932,10 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
channel_combo,
|
||||
preview_combo,
|
||||
badge_combo,
|
||||
default_ui_combo,
|
||||
share_combo,
|
||||
component_policy_combo,
|
||||
$el("br", {}, []),
|
||||
$el("button.cm-button", {
|
||||
type: "button",
|
||||
textContent: "Install via Git URL",
|
||||
onclick: () => {
|
||||
var url = prompt("Please enter the URL of the Git repository to install", "");
|
||||
|
||||
if (url !== null) {
|
||||
install_via_git_url(url, self);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
$el("br", {}, []),
|
||||
$el("filedset.cm-experimental", {}, [
|
||||
@ -825,7 +969,7 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
})
|
||||
]),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
createControlsRight() {
|
||||
const elts = [
|
||||
@ -877,12 +1021,12 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
]),
|
||||
|
||||
$el("button", {
|
||||
id: 'workflowgallery-button',
|
||||
type: "button",
|
||||
id: 'workflowgallery-button',
|
||||
type: "button",
|
||||
style: {
|
||||
...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {})
|
||||
...(localStorage.getItem("wg_last_visited") ? {height: '50px'} : {})
|
||||
},
|
||||
onclick: (e) => {
|
||||
onclick: (e) => {
|
||||
const last_visited_site = localStorage.getItem("wg_last_visited")
|
||||
if (!!last_visited_site) {
|
||||
window.open(last_visited_site, "comfyui-workflow-gallery");
|
||||
@ -892,15 +1036,15 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
},
|
||||
}, [
|
||||
$el("p", {
|
||||
textContent: 'Workflow Gallery',
|
||||
style: {
|
||||
'text-align': 'center',
|
||||
'color': 'white',
|
||||
'font-size': '18px',
|
||||
'margin': 0,
|
||||
'padding': 0,
|
||||
}
|
||||
}, [
|
||||
textContent: 'Workflow Gallery',
|
||||
style: {
|
||||
'text-align': 'center',
|
||||
'color': 'white',
|
||||
'font-size': '18px',
|
||||
'margin': 0,
|
||||
'padding': 0,
|
||||
}
|
||||
}, [
|
||||
$el("p", {
|
||||
id: 'workflowgallery-button-last-visited-label',
|
||||
textContent: `(${localStorage.getItem("wg_last_visited") ? localStorage.getItem("wg_last_visited").split('/')[2] : ''})`,
|
||||
@ -913,10 +1057,10 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
}
|
||||
})
|
||||
]),
|
||||
$el("div.pysssss-workflow-arrow-2", {
|
||||
id: `comfyworkflows-button-arrow`,
|
||||
onclick: this.handleWorkflowGalleryButtonClick
|
||||
})
|
||||
$el("div.pysssss-workflow-arrow-2", {
|
||||
id: `comfyworkflows-button-arrow`,
|
||||
onclick: this.handleWorkflowGalleryButtonClick
|
||||
})
|
||||
]),
|
||||
|
||||
$el("button.cm-button", {
|
||||
@ -971,10 +1115,10 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
this.element.style.display = "block";
|
||||
}
|
||||
|
||||
handleWorkflowGalleryButtonClick(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
LiteGraph.closeAllContextMenus();
|
||||
handleWorkflowGalleryButtonClick(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
LiteGraph.closeAllContextMenus();
|
||||
|
||||
// Modify the style of the button so that the UI can indicate the last
|
||||
// visited site right away.
|
||||
@ -985,60 +1129,72 @@ class ManagerMenuDialog extends ComfyDialog {
|
||||
lastVisitedLabel.textContent = `(${url.split('/')[2]})`;
|
||||
}
|
||||
|
||||
const menu = new LiteGraph.ContextMenu(
|
||||
[
|
||||
{
|
||||
title: "Share your art",
|
||||
callback: () => {
|
||||
if (share_option === 'openart') {
|
||||
showOpenArtShareDialog();
|
||||
return;
|
||||
} else if (share_option === 'matrix' || share_option === 'comfyworkflows') {
|
||||
showShareDialog(share_option);
|
||||
return;
|
||||
}
|
||||
const menu = new LiteGraph.ContextMenu(
|
||||
[
|
||||
{
|
||||
title: "Share your art",
|
||||
callback: () => {
|
||||
if (share_option === 'openart') {
|
||||
showOpenArtShareDialog();
|
||||
return;
|
||||
} else if (share_option === 'matrix' || share_option === 'comfyworkflows') {
|
||||
showShareDialog(share_option);
|
||||
return;
|
||||
} else if (share_option === 'youml') {
|
||||
showYouMLShareDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ShareDialogChooser.instance) {
|
||||
ShareDialogChooser.instance = new ShareDialogChooser();
|
||||
}
|
||||
ShareDialogChooser.instance.show();
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Open 'openart.ai'",
|
||||
if (!ShareDialogChooser.instance) {
|
||||
ShareDialogChooser.instance = new ShareDialogChooser();
|
||||
}
|
||||
ShareDialogChooser.instance.show();
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Open 'openart.ai'",
|
||||
callback: () => {
|
||||
const url = "https://openart.ai/workflows/dev";
|
||||
localStorage.setItem("wg_last_visited", url);
|
||||
window.open(url, "comfyui-workflow-gallery");
|
||||
modifyButtonStyle(url);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Open 'comfyworkflows.com'",
|
||||
callback: () => {
|
||||
},
|
||||
{
|
||||
title: "Open 'youml.com'",
|
||||
callback: () => {
|
||||
const url = "https://youml.com/?from=comfyui-share";
|
||||
localStorage.setItem("wg_last_visited", url);
|
||||
window.open(url, "comfyui-workflow-gallery");
|
||||
modifyButtonStyle(url);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Open 'comfyworkflows.com'",
|
||||
callback: () => {
|
||||
const url = "https://comfyworkflows.com/";
|
||||
localStorage.setItem("wg_last_visited", url);
|
||||
window.open(url, "comfyui-workflow-gallery");
|
||||
window.open(url, "comfyui-workflow-gallery");
|
||||
modifyButtonStyle(url);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Close",
|
||||
callback: () => {
|
||||
LiteGraph.closeAllContextMenus();
|
||||
},
|
||||
}
|
||||
],
|
||||
{
|
||||
event: e,
|
||||
scale: 1.3,
|
||||
},
|
||||
window
|
||||
);
|
||||
// set the id so that we can override the context menu's z-index to be above the comfyui manager menu
|
||||
menu.root.id = "workflowgallery-button-menu";
|
||||
menu.root.classList.add("pysssss-workflow-popup-2");
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Close",
|
||||
callback: () => {
|
||||
LiteGraph.closeAllContextMenus();
|
||||
},
|
||||
}
|
||||
],
|
||||
{
|
||||
event: e,
|
||||
scale: 1.3,
|
||||
},
|
||||
window
|
||||
);
|
||||
// set the id so that we can override the context menu's z-index to be above the comfyui manager menu
|
||||
menu.root.id = "workflowgallery-button-menu";
|
||||
menu.root.classList.add("pysssss-workflow-popup-2");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1051,6 +1207,14 @@ app.registerExtension({
|
||||
});
|
||||
},
|
||||
async setup() {
|
||||
let orig_clear = app.graph.clear;
|
||||
app.graph.clear = function () {
|
||||
orig_clear.call(app.graph);
|
||||
load_components();
|
||||
};
|
||||
|
||||
load_components();
|
||||
|
||||
const menu = document.querySelector(".comfy-menu");
|
||||
const separator = document.createElement("hr");
|
||||
|
||||
@ -1078,6 +1242,9 @@ app.registerExtension({
|
||||
} else if (share_option === 'matrix' || share_option === 'comfyworkflows') {
|
||||
showShareDialog(share_option);
|
||||
return;
|
||||
} else if (share_option === 'youml') {
|
||||
showYouMLShareDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!ShareDialogChooser.instance) {
|
||||
@ -1100,6 +1267,7 @@ app.registerExtension({
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
this._addExtraNodeContextMenu(nodeType, app);
|
||||
},
|
||||
|
||||
async nodeCreated(node, app) {
|
||||
if(!node.badge_enabled) {
|
||||
node.getNickname = function () { return getNickname(node, node.comfyClass.trim()) };
|
||||
@ -1110,6 +1278,7 @@ app.registerExtension({
|
||||
node.badge_enabled = true;
|
||||
}
|
||||
},
|
||||
|
||||
async loadedGraphNode(node, app) {
|
||||
if(!node.badge_enabled) {
|
||||
const orig = node.onDrawForeground;
|
||||
@ -1120,8 +1289,23 @@ app.registerExtension({
|
||||
|
||||
_addExtraNodeContextMenu(node, app) {
|
||||
const origGetExtraMenuOptions = node.prototype.getExtraMenuOptions;
|
||||
node.prototype.cm_menu_added = true;
|
||||
node.prototype.getExtraMenuOptions = function (_, options) {
|
||||
origGetExtraMenuOptions?.apply?.(this, arguments);
|
||||
|
||||
if (node.category.startsWith('group nodes/')) {
|
||||
options.push({
|
||||
content: "Save As Component",
|
||||
callback: (obj) => {
|
||||
if (!ComponentBuilderDialog.instance) {
|
||||
ComponentBuilderDialog.instance = new ComponentBuilderDialog();
|
||||
}
|
||||
ComponentBuilderDialog.instance.target_node = node;
|
||||
ComponentBuilderDialog.instance.show();
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
|
||||
if (isOutputNode(node)) {
|
||||
const { potential_outputs } = getPotentialOutputsAndOutputNodes([this]);
|
||||
const hasOutput = potential_outputs.length > 0;
|
||||
@ -1158,3 +1342,27 @@ app.registerExtension({
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
async function set_default_ui()
|
||||
{
|
||||
let res = await api.fetchApi('/manager/default_ui');
|
||||
if(res.status == 200) {
|
||||
let mode = await res.text();
|
||||
switch(mode) {
|
||||
case 'history':
|
||||
app.ui.queue.hide();
|
||||
app.ui.history.show();
|
||||
break;
|
||||
case 'queue':
|
||||
app.ui.queue.show();
|
||||
app.ui.history.hide();
|
||||
break;
|
||||
default:
|
||||
// do nothing
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_default_ui();
|
||||
@ -2,6 +2,7 @@ import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||
import { OpenArtShareDialog } from "./comfyui-share-openart.js";
|
||||
import { YouMLShareDialog } from "./comfyui-share-youml.js";
|
||||
|
||||
export const SUPPORTED_OUTPUT_NODE_TYPES = [
|
||||
"PreviewImage",
|
||||
@ -179,6 +180,23 @@ export const showOpenArtShareDialog = () => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const showYouMLShareDialog = () => {
|
||||
if (!YouMLShareDialog.instance) {
|
||||
YouMLShareDialog.instance = new YouMLShareDialog();
|
||||
}
|
||||
|
||||
return app.graphToPrompt()
|
||||
.then(prompt => {
|
||||
return app.graph._nodes;
|
||||
})
|
||||
.then(nodes => {
|
||||
const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes);
|
||||
YouMLShareDialog.instance.show(potential_outputs, potential_output_nodes);
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
export const showShareDialog = async (share_option) => {
|
||||
if (!ShareDialog.instance) {
|
||||
ShareDialog.instance = new ShareDialog(share_option);
|
||||
@ -233,6 +251,16 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "youml",
|
||||
textContent: "YouML",
|
||||
website: "https://youml.com",
|
||||
description: "Share your workflow or transform it into an interactive app on YouML.com",
|
||||
onclick: () => {
|
||||
showYouMLShareDialog();
|
||||
this.close();
|
||||
}
|
||||
},
|
||||
{
|
||||
key: "matrix",
|
||||
textContent: "Matrix Server",
|
||||
@ -264,7 +292,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
display: "flex",
|
||||
'flex-wrap': 'wrap',
|
||||
'justify-content': 'space-around',
|
||||
'padding': '20px',
|
||||
'padding': '10px',
|
||||
}
|
||||
});
|
||||
|
||||
@ -297,7 +325,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
'text-align': 'left',
|
||||
color: 'white',
|
||||
'font-size': '14px',
|
||||
'margin-bottom': '10px',
|
||||
'margin-bottom': '0',
|
||||
},
|
||||
});
|
||||
|
||||
@ -335,7 +363,7 @@ export class ShareDialogChooser extends ComfyDialog {
|
||||
style: {
|
||||
'flex-basis': '100%',
|
||||
'margin': '10px',
|
||||
'padding': '20px',
|
||||
'padding': '10px 20px',
|
||||
'border': '1px solid #ddd',
|
||||
'border-radius': '5px',
|
||||
'box-shadow': '0 2px 4px rgba(0, 0, 0, 0.1)',
|
||||
|
||||
@ -264,9 +264,10 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
]);
|
||||
|
||||
// OpenArt Contest Section
|
||||
/*
|
||||
this.joinContestCheckbox = $el("input", {
|
||||
type: 'checkbox',
|
||||
id: "join_contest"
|
||||
id: "join_contest"s
|
||||
}, [])
|
||||
this.joinContestDescription = $el("a", {
|
||||
style: {
|
||||
@ -290,6 +291,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
const contestSection = $el("div", {style: sectionStyle}, [
|
||||
this.joinContestLabel,
|
||||
]);
|
||||
*/
|
||||
|
||||
// Message Section
|
||||
this.message = $el(
|
||||
@ -349,7 +351,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
outputUploadSection,
|
||||
this.outputsSection,
|
||||
additionalInputsSection,
|
||||
contestSection,
|
||||
// contestSection,
|
||||
this.message,
|
||||
buttonsSection,
|
||||
];
|
||||
@ -486,7 +488,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
}
|
||||
}
|
||||
|
||||
const join_contest = this.joinContestCheckbox.checked;
|
||||
// const join_contest = this.joinContestCheckbox.checked;
|
||||
|
||||
try {
|
||||
const response = await this.fetchApi(
|
||||
@ -502,7 +504,7 @@ export class OpenArtShareDialog extends ComfyDialog {
|
||||
workflow_api_json: workflowAPIJSON,
|
||||
snapshot: current_snapshot,
|
||||
},
|
||||
join_contest,
|
||||
// join_contest,
|
||||
}),
|
||||
},
|
||||
"Uploading workflow..."
|
||||
|
||||
568
js/comfyui-share-youml.js
Normal file
568
js/comfyui-share-youml.js
Normal file
@ -0,0 +1,568 @@
|
||||
import {app} from "../../scripts/app.js";
|
||||
import {api} from "../../scripts/api.js";
|
||||
import {ComfyDialog, $el} from "../../scripts/ui.js";
|
||||
|
||||
const BASE_URL = "https://youml.com";
|
||||
//const BASE_URL = "http://localhost:3000";
|
||||
const DEFAULT_HOMEPAGE_URL = `${BASE_URL}/?from=comfyui`;
|
||||
const TOKEN_PAGE_URL = `${BASE_URL}/my-token`;
|
||||
const API_ENDPOINT = `${BASE_URL}/api`;
|
||||
|
||||
const style = `
|
||||
.youml-share-dialog {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.youml-share-dialog .dialog-header {
|
||||
text-align: center;
|
||||
color: white;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.youml-share-dialog .dialog-section {
|
||||
margin-bottom: 0;
|
||||
padding: 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
.youml-share-dialog input, .youml-share-dialog textarea {
|
||||
display: block;
|
||||
min-width: 500px;
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.youml-share-dialog textarea {
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
}
|
||||
.youml-share-dialog .workflow-description {
|
||||
min-height: 75px;
|
||||
}
|
||||
.youml-share-dialog label {
|
||||
color: #f8f8f8;
|
||||
display: block;
|
||||
margin: 5px 0 0 0;
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
}
|
||||
.youml-share-dialog .action-button {
|
||||
padding: 10px 80px;
|
||||
margin: 10px 5px;
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.youml-share-dialog .share-button {
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
}
|
||||
.youml-share-dialog .close-button {
|
||||
background-color: none;
|
||||
}
|
||||
.youml-share-dialog .action-button-panel {
|
||||
text-align: right;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.youml-share-dialog .status-message {
|
||||
color: #fd7909;
|
||||
text-align: center;
|
||||
padding: 5px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.youml-share-dialog .status-message a {
|
||||
color: white;
|
||||
}
|
||||
.youml-share-dialog .output-panel {
|
||||
overflow: auto;
|
||||
max-height: 180px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
|
||||
grid-template-rows: auto;
|
||||
grid-column-gap: 10px;
|
||||
grid-row-gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
.youml-share-dialog .output-panel .output-image {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
objectFit: cover;
|
||||
borderRadius: 5px;
|
||||
}
|
||||
|
||||
.youml-share-dialog .output-panel .radio-button {
|
||||
color:var(--fg-color);
|
||||
}
|
||||
.youml-share-dialog .output-panel .radio-text {
|
||||
color: gray;
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
max-width: 100px;
|
||||
}
|
||||
.youml-share-dialog .output-panel .node-id {
|
||||
color: #FBFBFD;
|
||||
display: block;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
font-size: 12px;
|
||||
overflow-x: hidden;
|
||||
padding: 2px 3px;
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
max-width: 100px;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
left: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.youml-share-dialog .output-panel .output-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border: 5px solid transparent;
|
||||
}
|
||||
.youml-share-dialog .output-panel .output-label:hover {
|
||||
border: 5px solid #007bff;
|
||||
}
|
||||
.youml-share-dialog .output-panel .output-label.checked {
|
||||
border: 5px solid #007bff;
|
||||
}
|
||||
.youml-share-dialog .missing-output-message{
|
||||
color: #fd7909;
|
||||
font-size: 16px;
|
||||
margin-bottom:10px
|
||||
}
|
||||
.youml-share-dialog .select-output-message{
|
||||
color: white;
|
||||
margin-bottom:5px
|
||||
}
|
||||
`;
|
||||
|
||||
export class YouMLShareDialog extends ComfyDialog {
|
||||
static instance = null;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
$el("style", {
|
||||
textContent: style,
|
||||
parent: document.head,
|
||||
});
|
||||
this.element = $el(
|
||||
"div.comfy-modal.youml-share-dialog",
|
||||
{
|
||||
parent: document.body,
|
||||
},
|
||||
[$el("div.comfy-modal-content", {}, [...this.createLayout()])]
|
||||
);
|
||||
this.selectedOutputIndex = 0;
|
||||
this.selectedNodeId = null;
|
||||
this.uploadedImages = [];
|
||||
this.selectedFile = null;
|
||||
}
|
||||
|
||||
async loadToken() {
|
||||
let key = ""
|
||||
try {
|
||||
const response = await api.fetchApi(`/manager/youml/settings`)
|
||||
const settings = await response.json()
|
||||
return settings.token
|
||||
} catch (error) {
|
||||
}
|
||||
return key || "";
|
||||
}
|
||||
|
||||
async saveToken(value) {
|
||||
await api.fetchApi(`/manager/youml/settings`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
token: value
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
createLayout() {
|
||||
// Header Section
|
||||
const headerSection = $el("h3.dialog-header", {
|
||||
textContent: "Share your workflow to YouML.com",
|
||||
size: 3,
|
||||
});
|
||||
|
||||
// Workflow Info Section
|
||||
this.nameInput = $el("input", {
|
||||
type: "text",
|
||||
placeholder: "Name (required)",
|
||||
});
|
||||
this.descriptionInput = $el("textarea.workflow-description", {
|
||||
placeholder: "Description (optional, markdown supported)",
|
||||
});
|
||||
const workflowMetadata = $el("div.dialog-section", {}, [
|
||||
$el("label", {}, ["Workflow info"]),
|
||||
this.nameInput,
|
||||
this.descriptionInput,
|
||||
]);
|
||||
|
||||
// Outputs Section
|
||||
this.outputsSection = $el("div.dialog-section", {
|
||||
id: "selectOutputs",
|
||||
}, []);
|
||||
|
||||
const outputUploadSection = $el("div.dialog-section", {}, [
|
||||
$el("label", {}, ["Thumbnail"]),
|
||||
this.outputsSection,
|
||||
]);
|
||||
|
||||
// API Token Section
|
||||
this.apiTokenInput = $el("input", {
|
||||
type: "password",
|
||||
placeholder: "Copy & paste your API token",
|
||||
});
|
||||
const getAPITokenButton = $el("button", {
|
||||
href: DEFAULT_HOMEPAGE_URL,
|
||||
target: "_blank",
|
||||
onclick: () => window.open(TOKEN_PAGE_URL, "_blank"),
|
||||
}, ["Get your API Token"])
|
||||
|
||||
const apiTokenSection = $el("div.dialog-section", {}, [
|
||||
$el("label", {}, ["YouML API Token"]),
|
||||
this.apiTokenInput,
|
||||
getAPITokenButton,
|
||||
]);
|
||||
|
||||
// Message Section
|
||||
this.message = $el("div.status-message", {}, []);
|
||||
|
||||
// Share and Close Buttons
|
||||
this.shareButton = $el("button.action-button.share-button", {
|
||||
type: "submit",
|
||||
textContent: "Share",
|
||||
onclick: () => {
|
||||
this.handleShareButtonClick();
|
||||
},
|
||||
});
|
||||
|
||||
const buttonsSection = $el(
|
||||
"div.action-button-panel",
|
||||
{},
|
||||
[
|
||||
$el("button.action-button.close-button", {
|
||||
type: "button",
|
||||
textContent: "Close",
|
||||
onclick: () => {
|
||||
this.close();
|
||||
},
|
||||
}),
|
||||
this.shareButton,
|
||||
]
|
||||
);
|
||||
|
||||
// Composing the full layout
|
||||
const layout = [
|
||||
headerSection,
|
||||
workflowMetadata,
|
||||
outputUploadSection,
|
||||
apiTokenSection,
|
||||
this.message,
|
||||
buttonsSection,
|
||||
];
|
||||
|
||||
return layout;
|
||||
}
|
||||
|
||||
async fetchYoumlApi(path, options, statusText) {
|
||||
if (statusText) {
|
||||
this.message.textContent = statusText;
|
||||
}
|
||||
|
||||
const fullPath = new URL(API_ENDPOINT + path)
|
||||
|
||||
const fetchOptions = Object.assign({}, options)
|
||||
|
||||
fetchOptions.headers = {
|
||||
...fetchOptions.headers,
|
||||
"Authorization": `Bearer ${this.apiTokenInput.value}`,
|
||||
"User-Agent": "ComfyUI-Manager-Youml/1.0.0",
|
||||
}
|
||||
|
||||
const response = await fetch(fullPath, fetchOptions);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText + " " + (await response.text()));
|
||||
}
|
||||
|
||||
if (statusText) {
|
||||
this.message.textContent = "";
|
||||
}
|
||||
const data = await response.json();
|
||||
return {
|
||||
ok: response.ok,
|
||||
statusText: response.statusText,
|
||||
status: response.status,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
async uploadThumbnail(uploadFile, recipeId) {
|
||||
const form = new FormData();
|
||||
form.append("file", uploadFile, uploadFile.name);
|
||||
try {
|
||||
const res = await this.fetchYoumlApi(
|
||||
`/v1/comfy/recipes/${recipeId}/thumbnail`,
|
||||
{
|
||||
method: "POST",
|
||||
body: form,
|
||||
},
|
||||
"Uploading thumbnail..."
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
if (e?.response?.status === 413) {
|
||||
throw new Error("File size is too large (max 20MB)");
|
||||
} else {
|
||||
throw new Error("Error uploading thumbnail: " + e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async handleShareButtonClick() {
|
||||
this.message.textContent = "";
|
||||
await this.saveToken(this.apiTokenInput.value);
|
||||
try {
|
||||
this.shareButton.disabled = true;
|
||||
this.shareButton.textContent = "Sharing...";
|
||||
await this.share();
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
} finally {
|
||||
this.shareButton.disabled = false;
|
||||
this.shareButton.textContent = "Share";
|
||||
}
|
||||
}
|
||||
|
||||
async share() {
|
||||
const prompt = await app.graphToPrompt();
|
||||
const workflowJSON = prompt["workflow"];
|
||||
const workflowAPIJSON = prompt["output"];
|
||||
const form_values = {
|
||||
name: this.nameInput.value,
|
||||
description: this.descriptionInput.value,
|
||||
};
|
||||
|
||||
if (!this.apiTokenInput.value) {
|
||||
throw new Error("API token is required");
|
||||
}
|
||||
|
||||
if (!this.selectedFile) {
|
||||
throw new Error("Thumbnail is required");
|
||||
}
|
||||
|
||||
if (!form_values.name) {
|
||||
throw new Error("Title is required");
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
let snapshotData = null;
|
||||
try {
|
||||
const snapshot = await api.fetchApi(`/snapshot/get_current`)
|
||||
snapshotData = await snapshot.json()
|
||||
} catch (e) {
|
||||
console.error("Failed to get snapshot", e)
|
||||
}
|
||||
|
||||
const request = {
|
||||
name: this.nameInput.value,
|
||||
description: this.descriptionInput.value,
|
||||
workflowUiJson: JSON.stringify(workflowJSON),
|
||||
workflowApiJson: JSON.stringify(workflowAPIJSON),
|
||||
}
|
||||
|
||||
if (snapshotData) {
|
||||
request.snapshotJson = JSON.stringify(snapshotData)
|
||||
}
|
||||
|
||||
const response = await this.fetchYoumlApi(
|
||||
"/v1/comfy/recipes",
|
||||
{
|
||||
method: "POST",
|
||||
headers: {"Content-Type": "application/json"},
|
||||
body: JSON.stringify(request),
|
||||
},
|
||||
"Uploading workflow..."
|
||||
);
|
||||
|
||||
if (response.ok) {
|
||||
const {id, recipePageUrl, editorPageUrl} = response.data;
|
||||
if (id) {
|
||||
let messagePrefix = "Workflow has been shared."
|
||||
if (this.selectedFile) {
|
||||
try {
|
||||
await this.uploadThumbnail(this.selectedFile, id);
|
||||
} catch (e) {
|
||||
console.error("Thumbnail upload failed: ", e);
|
||||
messagePrefix = "Workflow has been shared, but thumbnail upload failed. You can create a thumbnail on YouML later."
|
||||
}
|
||||
}
|
||||
this.message.innerHTML = `${messagePrefix} To turn your workflow into an interactive app, ` +
|
||||
`<a href="${recipePageUrl}" target="_blank">visit it on YouML</a>`;
|
||||
|
||||
this.uploadedImages = [];
|
||||
this.nameInput.value = "";
|
||||
this.descriptionInput.value = "";
|
||||
this.radioButtons.forEach((ele) => {
|
||||
ele.checked = false;
|
||||
ele.parentElement.classList.remove("checked");
|
||||
});
|
||||
this.selectedOutputIndex = 0;
|
||||
this.selectedNodeId = null;
|
||||
this.selectedFile = null;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
throw new Error("Error sharing workflow: " + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
async fetchImageBlob(url) {
|
||||
const response = await fetch(url);
|
||||
const blob = await response.blob();
|
||||
return blob;
|
||||
}
|
||||
|
||||
async show(potentialOutputs, potentialOutputNodes) {
|
||||
const potentialOutputsToOrder = {};
|
||||
potentialOutputNodes.forEach((node, index) => {
|
||||
if (node.id in potentialOutputsToOrder) {
|
||||
potentialOutputsToOrder[node.id][1].push(potentialOutputs[index]);
|
||||
} else {
|
||||
potentialOutputsToOrder[node.id] = [node, [potentialOutputs[index]]];
|
||||
}
|
||||
})
|
||||
const sortedPotentialOutputsToOrder = Object.fromEntries(
|
||||
Object.entries(potentialOutputsToOrder).sort((a, b) => a[0].id - b[0].id)
|
||||
);
|
||||
const sortedPotentialOutputs = []
|
||||
const sortedPotentiaOutputNodes = []
|
||||
for (const [key, value] of Object.entries(sortedPotentialOutputsToOrder)) {
|
||||
sortedPotentiaOutputNodes.push(value[0]);
|
||||
sortedPotentialOutputs.push(...value[1]);
|
||||
}
|
||||
potentialOutputNodes = sortedPotentiaOutputNodes;
|
||||
potentialOutputs = sortedPotentialOutputs;
|
||||
|
||||
|
||||
// If `selectedNodeId` is provided, we will select the corresponding radio
|
||||
// button for the node. In addition, we move the selected radio button to
|
||||
// the top of the list.
|
||||
if (this.selectedNodeId) {
|
||||
const index = potentialOutputNodes.findIndex(node => node.id === this.selectedNodeId);
|
||||
if (index >= 0) {
|
||||
this.selectedOutputIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
this.radioButtons = [];
|
||||
const newRadioButtons = $el("div.output-panel",
|
||||
{
|
||||
id: "selectOutput-Options",
|
||||
},
|
||||
potentialOutputs.map((output, index) => {
|
||||
const {node_id: nodeId} = output;
|
||||
const radioButton = $el("input.radio-button", {
|
||||
type: "radio",
|
||||
name: "selectOutputImages",
|
||||
value: index,
|
||||
required: index === 0
|
||||
}, [])
|
||||
let radioButtonImage;
|
||||
let filename;
|
||||
if (output.type === "image" || output.type === "temp") {
|
||||
radioButtonImage = $el("img.output-image", {
|
||||
src: `/view?filename=${output.image.filename}&subfolder=${output.image.subfolder}&type=${output.image.type}`,
|
||||
}, []);
|
||||
filename = output.image.filename
|
||||
} else if (output.type === "output") {
|
||||
radioButtonImage = $el("img.output-image", {
|
||||
src: output.output.value,
|
||||
}, []);
|
||||
filename = output.output.filename
|
||||
} else {
|
||||
radioButtonImage = $el("img.output-image", {
|
||||
src: "",
|
||||
}, []);
|
||||
}
|
||||
const radioButtonText = $el("span.radio-text", {}, [output.title])
|
||||
const nodeIdChip = $el("span.node-id", {}, [`Node: ${nodeId}`])
|
||||
radioButton.checked = this.selectedOutputIndex === index;
|
||||
|
||||
radioButton.onchange = async () => {
|
||||
this.selectedOutputIndex = parseInt(radioButton.value);
|
||||
|
||||
// Remove the "checked" class from all radio buttons
|
||||
this.radioButtons.forEach((ele) => {
|
||||
ele.parentElement.classList.remove("checked");
|
||||
});
|
||||
radioButton.parentElement.classList.add("checked");
|
||||
|
||||
this.fetchImageBlob(radioButtonImage.src).then((blob) => {
|
||||
const file = new File([blob], filename, {
|
||||
type: blob.type,
|
||||
});
|
||||
this.selectedFile = file;
|
||||
})
|
||||
};
|
||||
|
||||
if (radioButton.checked) {
|
||||
this.fetchImageBlob(radioButtonImage.src).then((blob) => {
|
||||
const file = new File([blob], filename, {
|
||||
type: blob.type,
|
||||
});
|
||||
this.selectedFile = file;
|
||||
})
|
||||
}
|
||||
|
||||
this.radioButtons.push(radioButton);
|
||||
|
||||
return $el(`label.output-label${radioButton.checked ? '.checked' : ''}`, {},
|
||||
[radioButtonImage, radioButtonText, radioButton, nodeIdChip]);
|
||||
})
|
||||
);
|
||||
|
||||
let header;
|
||||
if (this.radioButtons.length === 0) {
|
||||
header = $el("div.missing-output-message", {textContent: "Queue Prompt to see the outputs and select a thumbnail"}, [])
|
||||
} else {
|
||||
header = $el("div.select-output-message", {textContent: "Choose one from the outputs (scroll to see all)"}, [])
|
||||
}
|
||||
|
||||
this.outputsSection.innerHTML = "";
|
||||
this.outputsSection.appendChild(header);
|
||||
if (this.radioButtons.length > 0) {
|
||||
this.outputsSection.appendChild(newRadioButtons);
|
||||
}
|
||||
|
||||
this.message.innerHTML = "";
|
||||
this.message.textContent = "";
|
||||
|
||||
const token = await this.loadToken();
|
||||
this.apiTokenInput.value = token;
|
||||
this.uploadedImages = [];
|
||||
|
||||
this.element.style.display = "block";
|
||||
}
|
||||
}
|
||||
13
js/common.js
13
js/common.js
@ -1,5 +1,5 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js"
|
||||
import { api } from "../../scripts/api.js";
|
||||
|
||||
export async function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
@ -48,8 +48,7 @@ export async function install_checked_custom_node(grid_rows, target_i, caller, m
|
||||
});
|
||||
|
||||
if(response.status == 400) {
|
||||
app.ui.dialog.show(`${mode} failed: ${target.title}`);
|
||||
app.ui.dialog.element.style.zIndex = 10010;
|
||||
show_message(`${mode} failed: ${target.title}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -64,8 +63,7 @@ export async function install_checked_custom_node(grid_rows, target_i, caller, m
|
||||
}
|
||||
|
||||
if(failed != '') {
|
||||
app.ui.dialog.show(`${mode} failed: ${failed}`);
|
||||
app.ui.dialog.element.style.zIndex = 10010;
|
||||
show_message(`${mode} failed: ${failed}`);
|
||||
}
|
||||
|
||||
await caller.invalidateControl();
|
||||
@ -160,4 +158,9 @@ export async function free_models() {
|
||||
app.ui.dialog.show('Unloading of models failed.<BR><BR>Installed ComfyUI may be an outdated version.')
|
||||
}
|
||||
app.ui.dialog.element.style.zIndex = 10010;
|
||||
}
|
||||
|
||||
export function show_message(msg) {
|
||||
app.ui.dialog.show(msg);
|
||||
app.ui.dialog.element.style.zIndex = 10010;
|
||||
}
|
||||
801
js/components-manager.js
Normal file
801
js/components-manager.js
Normal file
@ -0,0 +1,801 @@
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js"
|
||||
import { sleep, show_message } from "./common.js";
|
||||
import { GroupNodeConfig, GroupNodeHandler } from "../../extensions/core/groupNode.js";
|
||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||
|
||||
let pack_map = {};
|
||||
let rpack_map = {};
|
||||
|
||||
export function getPureName(node) {
|
||||
// group nodes/
|
||||
let category = null;
|
||||
if(node.category) {
|
||||
category = node.category.substring(12);
|
||||
}
|
||||
else {
|
||||
category = node.constructor.category?.substring(12);
|
||||
}
|
||||
if(category) {
|
||||
let purename = node.comfyClass.substring(category.length+1);
|
||||
return purename;
|
||||
}
|
||||
else if(node.comfyClass.startsWith('workflow/')) {
|
||||
return node.comfyClass.substring(9);
|
||||
}
|
||||
else {
|
||||
return node.comfyClass;
|
||||
}
|
||||
}
|
||||
|
||||
function isValidVersionString(version) {
|
||||
const versionPattern = /^(\d+)\.(\d+)(\.(\d+))?$/;
|
||||
|
||||
const match = version.match(versionPattern);
|
||||
|
||||
return match !== null &&
|
||||
parseInt(match[1], 10) >= 0 &&
|
||||
parseInt(match[2], 10) >= 0 &&
|
||||
(!match[3] || parseInt(match[4], 10) >= 0);
|
||||
}
|
||||
|
||||
function register_pack_map(name, data) {
|
||||
if(data.packname) {
|
||||
pack_map[data.packname] = name;
|
||||
rpack_map[name] = data;
|
||||
}
|
||||
else {
|
||||
rpack_map[name] = data;
|
||||
}
|
||||
}
|
||||
|
||||
function storeGroupNode(name, data, register=true) {
|
||||
let extra = app.graph.extra;
|
||||
if (!extra) app.graph.extra = extra = {};
|
||||
let groupNodes = extra.groupNodes;
|
||||
if (!groupNodes) extra.groupNodes = groupNodes = {};
|
||||
groupNodes[name] = data;
|
||||
|
||||
if(register) {
|
||||
register_pack_map(name, data);
|
||||
}
|
||||
}
|
||||
|
||||
export async function load_components() {
|
||||
let data = await api.fetchApi('/manager/component/loads', {method: "POST"});
|
||||
let components = await data.json();
|
||||
|
||||
let start_time = Date.now();
|
||||
let failed = [];
|
||||
let failed2 = [];
|
||||
|
||||
for(let name in components) {
|
||||
if(app.graph.extra?.groupNodes?.[name]) {
|
||||
if(data) {
|
||||
let data = components[name];
|
||||
|
||||
let category = data.packname;
|
||||
if(data.category) {
|
||||
category += "/" + data.category;
|
||||
}
|
||||
if(category == '') {
|
||||
category = 'components';
|
||||
}
|
||||
|
||||
const config = new GroupNodeConfig(name, data);
|
||||
await config.registerType(category);
|
||||
|
||||
register_pack_map(name, data);
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
let nodeData = components[name];
|
||||
|
||||
storeGroupNode(name, nodeData);
|
||||
|
||||
const config = new GroupNodeConfig(name, nodeData);
|
||||
|
||||
while(true) {
|
||||
try {
|
||||
let category = nodeData.packname;
|
||||
if(nodeData.category) {
|
||||
category += "/" + nodeData.category;
|
||||
}
|
||||
if(category == '') {
|
||||
category = 'components';
|
||||
}
|
||||
|
||||
await config.registerType(category);
|
||||
register_pack_map(name, nodeData);
|
||||
break;
|
||||
}
|
||||
catch {
|
||||
let elapsed_time = Date.now() - start_time;
|
||||
if (elapsed_time > 5000) {
|
||||
failed.push(name);
|
||||
break;
|
||||
} else {
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallback1
|
||||
for(let i in failed) {
|
||||
let name = failed[i];
|
||||
|
||||
if(app.graph.extra?.groupNodes?.[name]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let nodeData = components[name];
|
||||
|
||||
storeGroupNode(name, nodeData);
|
||||
|
||||
const config = new GroupNodeConfig(name, nodeData);
|
||||
while(true) {
|
||||
try {
|
||||
let category = nodeData.packname;
|
||||
if(nodeData.workflow.category) {
|
||||
category += "/" + nodeData.category;
|
||||
}
|
||||
if(category == '') {
|
||||
category = 'components';
|
||||
}
|
||||
|
||||
await config.registerType(category);
|
||||
register_pack_map(name, nodeData);
|
||||
break;
|
||||
}
|
||||
catch {
|
||||
let elapsed_time = Date.now() - start_time;
|
||||
if (elapsed_time > 10000) {
|
||||
failed2.push(name);
|
||||
break;
|
||||
} else {
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fallback2
|
||||
for(let name in failed2) {
|
||||
let name = failed2[i];
|
||||
|
||||
let nodeData = components[name];
|
||||
|
||||
storeGroupNode(name, nodeData);
|
||||
|
||||
const config = new GroupNodeConfig(name, nodeData);
|
||||
while(true) {
|
||||
try {
|
||||
let category = nodeData.workflow.packname;
|
||||
if(nodeData.workflow.category) {
|
||||
category += "/" + nodeData.category;
|
||||
}
|
||||
if(category == '') {
|
||||
category = 'components';
|
||||
}
|
||||
|
||||
await config.registerType(category);
|
||||
register_pack_map(name, nodeData);
|
||||
break;
|
||||
}
|
||||
catch {
|
||||
let elapsed_time = Date.now() - start_time;
|
||||
if (elapsed_time > 30000) {
|
||||
failed.push(name);
|
||||
break;
|
||||
} else {
|
||||
await sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function save_as_component(node, version, prefix, nodename, packname, category) {
|
||||
let component_name = `${prefix}::${nodename}`;
|
||||
|
||||
let subgraph = app.graph.extra?.groupNodes?.[component_name];
|
||||
if(!subgraph) {
|
||||
subgraph = app.graph.extra?.groupNodes?.[getPureName(node)];
|
||||
}
|
||||
|
||||
subgraph.version = version;
|
||||
subgraph.datetime = Date.now();
|
||||
subgraph.packname = packname;
|
||||
subgraph.category = category;
|
||||
|
||||
let body =
|
||||
{
|
||||
name: component_name,
|
||||
workflow: subgraph
|
||||
};
|
||||
|
||||
pack_map[packname] = component_name;
|
||||
rpack_map[component_name] = subgraph;
|
||||
|
||||
const res = await api.fetchApi('/manager/component/save', {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
if(res.status == 200) {
|
||||
storeGroupNode(component_name, subgraph);
|
||||
const config = new GroupNodeConfig(component_name, subgraph);
|
||||
|
||||
let category = body.workflow.packname;
|
||||
if(body.workflow.category) {
|
||||
category += "/" + body.workflow.category;
|
||||
}
|
||||
if(category == '') {
|
||||
category = 'components';
|
||||
}
|
||||
|
||||
await config.registerType(category);
|
||||
|
||||
let path = await res.text();
|
||||
show_message(`Component '${component_name}' is saved into:\n${path}`);
|
||||
}
|
||||
else
|
||||
show_message(`Failed to save component.`);
|
||||
}
|
||||
|
||||
async function import_component(component_name, component, mode) {
|
||||
if(mode) {
|
||||
let body =
|
||||
{
|
||||
name: component_name,
|
||||
workflow: component
|
||||
};
|
||||
|
||||
const res = await api.fetchApi('/manager/component/save', {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json", },
|
||||
body: JSON.stringify(body)
|
||||
});
|
||||
}
|
||||
|
||||
let category = component.packname;
|
||||
if(component.category) {
|
||||
category += "/" + component.category;
|
||||
}
|
||||
if(category == '') {
|
||||
category = 'components';
|
||||
}
|
||||
|
||||
storeGroupNode(component_name, component);
|
||||
const config = new GroupNodeConfig(component_name, component);
|
||||
await config.registerType(category);
|
||||
}
|
||||
|
||||
function restore_to_loaded_component(component_name) {
|
||||
if(rpack_map[component_name]) {
|
||||
let component = rpack_map[component_name];
|
||||
storeGroupNode(component_name, component, false);
|
||||
const config = new GroupNodeConfig(component_name, component);
|
||||
config.registerType(component.category);
|
||||
}
|
||||
}
|
||||
|
||||
// Using a timestamp prevents duplicate pastes and ensures the prevention of re-deletion of litegrapheditor_clipboard.
|
||||
let last_paste_timestamp = null;
|
||||
|
||||
function versionCompare(v1, v2) {
|
||||
let ver1;
|
||||
let ver2;
|
||||
if(v1 && v1 != '') {
|
||||
ver1 = v1.split('.');
|
||||
ver1[0] = parseInt(ver1[0]);
|
||||
ver1[1] = parseInt(ver1[1]);
|
||||
if(ver1.length == 2)
|
||||
ver1.push(0);
|
||||
else
|
||||
ver1[2] = parseInt(ver2[2]);
|
||||
}
|
||||
else {
|
||||
ver1 = [0,0,0];
|
||||
}
|
||||
|
||||
if(v2 && v2 != '') {
|
||||
ver2 = v2.split('.');
|
||||
ver2[0] = parseInt(ver2[0]);
|
||||
ver2[1] = parseInt(ver2[1]);
|
||||
if(ver2.length == 2)
|
||||
ver2.push(0);
|
||||
else
|
||||
ver2[2] = parseInt(ver2[2]);
|
||||
}
|
||||
else {
|
||||
ver2 = [0,0,0];
|
||||
}
|
||||
|
||||
if(ver1[0] > ver2[0])
|
||||
return -1;
|
||||
else if(ver1[0] < ver2[0])
|
||||
return 1;
|
||||
|
||||
if(ver1[1] > ver2[1])
|
||||
return -1;
|
||||
else if(ver1[1] < ver2[1])
|
||||
return 1;
|
||||
|
||||
if(ver1[2] > ver2[2])
|
||||
return -1;
|
||||
else if(ver1[2] < ver2[2])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function checkVersion(name, component) {
|
||||
let msg = '';
|
||||
if(rpack_map[name]) {
|
||||
let old_version = rpack_map[name].version;
|
||||
if(!old_version || old_version == '') {
|
||||
msg = ` '${name}' Upgrade (V0.0 -> V${component.version})`;
|
||||
}
|
||||
else {
|
||||
let c = versionCompare(old_version, component.version);
|
||||
if(c < 0) {
|
||||
msg = ` '${name}' Downgrade (V${old_version} -> V${component.version})`;
|
||||
}
|
||||
else if(c > 0) {
|
||||
msg = ` '${name}' Upgrade (V${old_version} -> V${component.version})`;
|
||||
}
|
||||
else {
|
||||
msg = ` '${name}' Same version (V${component.version})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
msg = `'${name}' NEW (V${component.version})`;
|
||||
}
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
function handle_import_components(components) {
|
||||
let msg = 'Components:\n';
|
||||
let cnt = 0;
|
||||
for(let name in components) {
|
||||
let component = components[name];
|
||||
let v = checkVersion(name, component);
|
||||
|
||||
if(cnt < 10) {
|
||||
msg += v + '\n';
|
||||
}
|
||||
else if (cnt == 10) {
|
||||
msg += '...\n';
|
||||
}
|
||||
else {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
let last_name = null;
|
||||
msg += '\nWill you load components?\n';
|
||||
if(confirm(msg)) {
|
||||
let mode = confirm('\nWill you save components?\n(cancel=load without save)');
|
||||
|
||||
for(let name in components) {
|
||||
let component = components[name];
|
||||
import_component(name, component, mode);
|
||||
last_name = name;
|
||||
}
|
||||
|
||||
if(mode) {
|
||||
show_message('Components are saved.');
|
||||
}
|
||||
else {
|
||||
show_message('Components are loaded.');
|
||||
}
|
||||
}
|
||||
|
||||
if(cnt == 1 && last_name) {
|
||||
const node = LiteGraph.createNode(`workflow/${last_name}`);
|
||||
node.pos = [app.canvas.graph_mouse[0], app.canvas.graph_mouse[1]];
|
||||
app.canvas.graph.add(node, false);
|
||||
}
|
||||
}
|
||||
|
||||
function handlePaste(e) {
|
||||
let data = (e.clipboardData || window.clipboardData);
|
||||
const items = data.items;
|
||||
for(const item of items) {
|
||||
if(item.kind == 'string' && item.type == 'text/plain') {
|
||||
data = data.getData("text/plain");
|
||||
try {
|
||||
let json_data = JSON.parse(data);
|
||||
if(json_data.kind == 'ComfyUI Components' && last_paste_timestamp != json_data.timestamp) {
|
||||
last_paste_timestamp = json_data.timestamp;
|
||||
handle_import_components(json_data.components);
|
||||
|
||||
// disable paste node
|
||||
localStorage.removeItem("litegrapheditor_clipboard", null);
|
||||
}
|
||||
else {
|
||||
console.log('This components are already pasted: ignored');
|
||||
}
|
||||
}
|
||||
catch {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("paste", handlePaste);
|
||||
|
||||
|
||||
export class ComponentBuilderDialog extends ComfyDialog {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
clear() {
|
||||
while (this.element.children.length) {
|
||||
this.element.removeChild(this.element.children[0]);
|
||||
}
|
||||
}
|
||||
|
||||
show() {
|
||||
this.invalidateControl();
|
||||
|
||||
this.element.style.display = "block";
|
||||
this.element.style.zIndex = 10001;
|
||||
this.element.style.width = "500px";
|
||||
this.element.style.height = "450px";
|
||||
}
|
||||
|
||||
invalidateControl() {
|
||||
this.clear();
|
||||
|
||||
let self = this;
|
||||
|
||||
const close_button = $el("button", { id: "cm-close-button", type: "button", textContent: "Close", onclick: () => self.close() });
|
||||
this.save_button = $el("button",
|
||||
{ id: "cm-save-button", type: "button", textContent: "Save", onclick: () =>
|
||||
{
|
||||
save_as_component(self.target_node, self.version_string.value.trim(), self.node_prefix.value.trim(),
|
||||
self.getNodeName(), self.getPackName(), self.category.value.trim());
|
||||
}
|
||||
});
|
||||
|
||||
let default_nodename = getPureName(this.target_node).trim();
|
||||
|
||||
let groupNode = app.graph.extra.groupNodes[default_nodename];
|
||||
let default_packname = groupNode.packname;
|
||||
|
||||
if(!default_packname) {
|
||||
default_packname = '';
|
||||
}
|
||||
|
||||
let default_category = groupNode.category;
|
||||
if(!default_category) {
|
||||
default_category = '';
|
||||
}
|
||||
|
||||
this.default_ver = groupNode.version;
|
||||
if(!this.default_ver) {
|
||||
this.default_ver = '0.0';
|
||||
}
|
||||
|
||||
let delimiterIndex = default_nodename.indexOf('::');
|
||||
let default_prefix = "";
|
||||
if(delimiterIndex != -1) {
|
||||
default_prefix = default_nodename.substring(0, delimiterIndex);
|
||||
default_nodename = default_nodename.substring(delimiterIndex + 2);
|
||||
}
|
||||
|
||||
if(!default_prefix) {
|
||||
this.save_button.disabled = true;
|
||||
}
|
||||
|
||||
this.pack_list = this.createPackListCombo();
|
||||
|
||||
let version_string = this.createLabeledInput('input version (e.g. 1.0)', '*Version : ', this.default_ver);
|
||||
this.version_string = version_string[1];
|
||||
this.version_string.disabled = true;
|
||||
|
||||
let node_prefix = this.createLabeledInput('input node prefix (e.g. mypack)', '*Prefix : ', default_prefix);
|
||||
this.node_prefix = node_prefix[1];
|
||||
|
||||
let manual_nodename = this.createLabeledInput('input node name (e.g. MAKE_BASIC_PIPE)', 'Nodename : ', default_nodename);
|
||||
this.manual_nodename = manual_nodename[1];
|
||||
|
||||
let manual_packname = this.createLabeledInput('input pack name (e.g. mypack)', 'Packname : ', default_packname);
|
||||
this.manual_packname = manual_packname[1];
|
||||
|
||||
let category = this.createLabeledInput('input category (e.g. util/pipe)', 'Category : ', default_category);
|
||||
this.category = category[1];
|
||||
|
||||
this.node_label = this.createNodeLabel();
|
||||
|
||||
let author_mode = this.createAuthorModeCheck();
|
||||
this.author_mode = author_mode[0];
|
||||
|
||||
const content =
|
||||
$el("div.comfy-modal-content",
|
||||
[
|
||||
$el("tr.cm-title", {}, [
|
||||
$el("font", {size:6, color:"white"}, [`ComfyUI-Manager: Component Builder`])]
|
||||
),
|
||||
$el("br", {}, []),
|
||||
$el("div.cm-menu-container",
|
||||
[
|
||||
author_mode[0],
|
||||
author_mode[1],
|
||||
category[0],
|
||||
node_prefix[0],
|
||||
manual_nodename[0],
|
||||
manual_packname[0],
|
||||
version_string[0],
|
||||
this.pack_list,
|
||||
$el("br", {}, []),
|
||||
this.node_label
|
||||
]),
|
||||
|
||||
$el("br", {}, []),
|
||||
this.save_button,
|
||||
close_button,
|
||||
]
|
||||
);
|
||||
|
||||
content.style.width = '100%';
|
||||
content.style.height = '100%';
|
||||
|
||||
this.element = $el("div.comfy-modal", { id:'cm-manager-dialog', parent: document.body }, [ content ]);
|
||||
}
|
||||
|
||||
validateInput() {
|
||||
let msg = "";
|
||||
|
||||
if(!isValidVersionString(this.version_string.value)) {
|
||||
msg += 'Invalid version string: '+event.value+"\n";
|
||||
}
|
||||
|
||||
if(this.node_prefix.value.trim() == '') {
|
||||
msg += 'Node prefix cannot be empty\n';
|
||||
}
|
||||
|
||||
if(this.manual_nodename.value.trim() == '') {
|
||||
msg += 'Node name cannot be empty\n';
|
||||
}
|
||||
|
||||
if(msg != '') {
|
||||
// alert(msg);
|
||||
}
|
||||
|
||||
this.save_button.disabled = msg != "";
|
||||
}
|
||||
|
||||
getPackName() {
|
||||
if(this.pack_list.selectedIndex == 0) {
|
||||
return this.manual_packname.value.trim();
|
||||
}
|
||||
|
||||
return this.pack_list.value.trim();
|
||||
}
|
||||
|
||||
getNodeName() {
|
||||
if(this.manual_nodename.value.trim() != '') {
|
||||
return this.manual_nodename.value.trim();
|
||||
}
|
||||
|
||||
return getPureName(this.target_node);
|
||||
}
|
||||
|
||||
createAuthorModeCheck() {
|
||||
let check = $el("input",{type:'checkbox', id:"author-mode"},[])
|
||||
const check_label = $el("label",{for:"author-mode"},["Enable author mode"]);
|
||||
check_label.style.color = "var(--fg-color)";
|
||||
check_label.style.cursor = "pointer";
|
||||
check.checked = false;
|
||||
|
||||
let self = this;
|
||||
check.onchange = () => {
|
||||
self.version_string.disabled = !check.checked;
|
||||
|
||||
if(!check.checked) {
|
||||
self.version_string.value = self.default_ver;
|
||||
}
|
||||
else {
|
||||
alert('If you are not the author, it is not recommended to change the version, as it may cause component update issues.');
|
||||
}
|
||||
};
|
||||
|
||||
return [check, check_label];
|
||||
}
|
||||
|
||||
createNodeLabel() {
|
||||
let label = $el('p');
|
||||
label.className = 'cb-node-label';
|
||||
if(this.target_node.comfyClass.includes('::'))
|
||||
label.textContent = getPureName(this.target_node);
|
||||
else
|
||||
label.textContent = " _::" + getPureName(this.target_node);
|
||||
return label;
|
||||
}
|
||||
|
||||
createLabeledInput(placeholder, label, value) {
|
||||
let textbox = $el('input.cb-widget-input', {type:'text', placeholder:placeholder, value:value}, []);
|
||||
|
||||
let self = this;
|
||||
textbox.onchange = () => {
|
||||
this.validateInput.call(self);
|
||||
this.node_label.textContent = this.node_prefix.value + "::" + this.manual_nodename.value;
|
||||
}
|
||||
let row = $el('span.cb-widget', {}, [ $el('span.cb-widget-input-label', label), textbox]);
|
||||
|
||||
return [row, textbox];
|
||||
}
|
||||
|
||||
createPackListCombo() {
|
||||
let combo = document.createElement("select");
|
||||
combo.className = "cb-widget";
|
||||
let default_packname_option = { value: '##manual', text: 'Packname: Manual' };
|
||||
|
||||
combo.appendChild($el('option', default_packname_option, []));
|
||||
for(let name in pack_map) {
|
||||
combo.appendChild($el('option', { value: name, text: 'Packname: '+ name }, []));
|
||||
}
|
||||
|
||||
let self = this;
|
||||
combo.onchange = function () {
|
||||
if(combo.selectedIndex == 0) {
|
||||
self.manual_packname.disabled = false;
|
||||
}
|
||||
else {
|
||||
self.manual_packname.disabled = true;
|
||||
}
|
||||
};
|
||||
|
||||
return combo;
|
||||
}
|
||||
}
|
||||
|
||||
let orig_handleFile = app.handleFile;
|
||||
|
||||
function handleFile(file) {
|
||||
if (file.name?.endsWith(".json") || file.name?.endsWith(".pack")) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
let is_component = false;
|
||||
const jsonContent = JSON.parse(reader.result);
|
||||
for(let name in jsonContent) {
|
||||
let cand = jsonContent[name];
|
||||
is_component = cand.datetime && cand.version;
|
||||
break;
|
||||
}
|
||||
|
||||
if(is_component) {
|
||||
handle_import_components(jsonContent);
|
||||
}
|
||||
else {
|
||||
orig_handleFile.call(app, file);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
orig_handleFile.call(app, file);
|
||||
}
|
||||
|
||||
app.handleFile = handleFile;
|
||||
|
||||
let current_component_policy = 'workflow';
|
||||
try {
|
||||
api.fetchApi('/manager/component/policy')
|
||||
.then(response => response.text())
|
||||
.then(data => { current_component_policy = data; });
|
||||
}
|
||||
catch {}
|
||||
|
||||
function getChangedVersion(groupNodes) {
|
||||
if(!Object.keys(pack_map).length || !groupNodes)
|
||||
return null;
|
||||
|
||||
let res = {};
|
||||
for(let component_name in groupNodes) {
|
||||
let data = groupNodes[component_name];
|
||||
|
||||
if(rpack_map[component_name]) {
|
||||
let v = versionCompare(data.version, rpack_map[component_name].version);
|
||||
res[component_name] = v;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
const loadGraphData = app.loadGraphData;
|
||||
app.loadGraphData = async function () {
|
||||
if(arguments.length == 0)
|
||||
return await loadGraphData.apply(this, arguments);
|
||||
|
||||
let graphData = arguments[0];
|
||||
let groupNodes = graphData.extra?.groupNodes;
|
||||
let res = getChangedVersion(groupNodes);
|
||||
|
||||
if(res) {
|
||||
let target_components = null;
|
||||
switch(current_component_policy) {
|
||||
case 'higher':
|
||||
target_components = Object.keys(res).filter(key => res[key] == 1);
|
||||
break;
|
||||
|
||||
case 'mine':
|
||||
target_components = Object.keys(res);
|
||||
break;
|
||||
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
|
||||
if(target_components) {
|
||||
for(let i in target_components) {
|
||||
let component_name = target_components[i];
|
||||
let component = rpack_map[component_name];
|
||||
if(component && graphData.extra?.groupNodes) {
|
||||
graphData.extra.groupNodes[component_name] = component;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log('Empty components: policy ignored');
|
||||
}
|
||||
|
||||
arguments[0] = graphData;
|
||||
return await loadGraphData.apply(this, arguments);
|
||||
};
|
||||
|
||||
export function set_component_policy(v) {
|
||||
current_component_policy = v;
|
||||
}
|
||||
|
||||
let graphToPrompt = app.graphToPrompt;
|
||||
app.graphToPrompt = async function () {
|
||||
let p = await graphToPrompt.call(app);
|
||||
try {
|
||||
let groupNodes = p.workflow.extra?.groupNodes;
|
||||
if(groupNodes) {
|
||||
p.workflow.extra = { ... p.workflow.extra};
|
||||
|
||||
// get used group nodes
|
||||
let used_group_nodes = new Set();
|
||||
for(let node of p.workflow.nodes) {
|
||||
if(node.type.startsWith('workflow/')) {
|
||||
used_group_nodes.add(node.type.substring(9));
|
||||
}
|
||||
}
|
||||
|
||||
// remove unused group nodes
|
||||
let new_groupNodes = {};
|
||||
for (let key in p.workflow.extra.groupNodes) {
|
||||
if (used_group_nodes.has(key)) {
|
||||
new_groupNodes[key] = p.workflow.extra.groupNodes[key];
|
||||
}
|
||||
}
|
||||
p.workflow.extra.groupNodes = new_groupNodes;
|
||||
}
|
||||
}
|
||||
catch(e) {
|
||||
console.log(`Failed to filtering group nodes: ${e}`);
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
@ -6,7 +6,7 @@ import { manager_instance, rebootAPI } from "./common.js";
|
||||
|
||||
async function restore_snapshot(target) {
|
||||
if(SnapshotManager.instance) {
|
||||
try {
|
||||
try {
|
||||
const response = await api.fetchApi(`/snapshot/restore?target=${target}`, { cache: "no-store" });
|
||||
if(response.status == 400) {
|
||||
app.ui.dialog.show(`Restore snapshot failed: ${target.title} / ${exception}`);
|
||||
@ -30,7 +30,7 @@ async function restore_snapshot(target) {
|
||||
|
||||
async function remove_snapshot(target) {
|
||||
if(SnapshotManager.instance) {
|
||||
try {
|
||||
try {
|
||||
const response = await api.fetchApi(`/snapshot/remove?target=${target}`, { cache: "no-store" });
|
||||
if(response.status == 400) {
|
||||
app.ui.dialog.show(`Remove snapshot failed: ${target.title} / ${exception}`);
|
||||
@ -52,20 +52,20 @@ async function remove_snapshot(target) {
|
||||
}
|
||||
|
||||
async function save_current_snapshot() {
|
||||
try {
|
||||
const response = await api.fetchApi('/snapshot/save', { cache: "no-store" });
|
||||
app.ui.dialog.close();
|
||||
return true;
|
||||
}
|
||||
catch(exception) {
|
||||
app.ui.dialog.show(`Backup snapshot failed: ${exception}`);
|
||||
app.ui.dialog.element.style.zIndex = 10010;
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
await SnapshotManager.instance.invalidateControl();
|
||||
SnapshotManager.instance.updateMessage("<BR>Current snapshot saved.");
|
||||
}
|
||||
try {
|
||||
const response = await api.fetchApi('/snapshot/save', { cache: "no-store" });
|
||||
app.ui.dialog.close();
|
||||
return true;
|
||||
}
|
||||
catch(exception) {
|
||||
app.ui.dialog.show(`Backup snapshot failed: ${exception}`);
|
||||
app.ui.dialog.element.style.zIndex = 10010;
|
||||
return false;
|
||||
}
|
||||
finally {
|
||||
await SnapshotManager.instance.invalidateControl();
|
||||
SnapshotManager.instance.updateMessage("<BR>Current snapshot saved.");
|
||||
}
|
||||
}
|
||||
|
||||
async function getSnapshotList() {
|
||||
@ -97,7 +97,7 @@ export class SnapshotManager extends ComfyDialog {
|
||||
async remove_item() {
|
||||
caller.disableButtons();
|
||||
|
||||
await caller.invalidateControl();
|
||||
await caller.invalidateControl();
|
||||
}
|
||||
|
||||
createControls() {
|
||||
@ -151,14 +151,14 @@ export class SnapshotManager extends ComfyDialog {
|
||||
var grid = document.createElement('table');
|
||||
grid.setAttribute('id', 'snapshot-list-grid');
|
||||
|
||||
var thead = document.createElement('thead');
|
||||
var tbody = document.createElement('tbody');
|
||||
var thead = document.createElement('thead');
|
||||
var tbody = document.createElement('tbody');
|
||||
|
||||
var headerRow = document.createElement('tr');
|
||||
thead.style.position = "sticky";
|
||||
thead.style.top = "0px";
|
||||
thead.style.borderCollapse = "collapse";
|
||||
thead.style.tableLayout = "fixed";
|
||||
thead.style.borderCollapse = "collapse";
|
||||
thead.style.tableLayout = "fixed";
|
||||
|
||||
var header1 = document.createElement('th');
|
||||
header1.innerHTML = ' ID ';
|
||||
@ -170,7 +170,7 @@ export class SnapshotManager extends ComfyDialog {
|
||||
header_button.innerHTML = 'Action';
|
||||
header_button.style.width = "100px";
|
||||
|
||||
thead.appendChild(headerRow);
|
||||
thead.appendChild(headerRow);
|
||||
headerRow.appendChild(header1);
|
||||
headerRow.appendChild(header2);
|
||||
headerRow.appendChild(header_button);
|
||||
@ -231,17 +231,17 @@ export class SnapshotManager extends ComfyDialog {
|
||||
this.grid_rows[i] = {data:data, control:dataRow};
|
||||
}
|
||||
|
||||
let self = this;
|
||||
let self = this;
|
||||
const panel = document.createElement('div');
|
||||
panel.style.width = "100%";
|
||||
panel.style.width = "100%";
|
||||
panel.appendChild(grid);
|
||||
|
||||
function handleResize() {
|
||||
const parentHeight = self.element.clientHeight;
|
||||
const gridHeight = parentHeight - 200;
|
||||
function handleResize() {
|
||||
const parentHeight = self.element.clientHeight;
|
||||
const gridHeight = parentHeight - 200;
|
||||
|
||||
grid.style.height = gridHeight + "px";
|
||||
}
|
||||
grid.style.height = gridHeight + "px";
|
||||
}
|
||||
window.addEventListener("resize", handleResize);
|
||||
|
||||
grid.style.position = "relative";
|
||||
@ -253,7 +253,7 @@ export class SnapshotManager extends ComfyDialog {
|
||||
this.element.style.width = "80%";
|
||||
this.element.appendChild(panel);
|
||||
|
||||
handleResize();
|
||||
handleResize();
|
||||
}
|
||||
|
||||
async createBottomControls() {
|
||||
|
||||
444
misc/Impact.pack
Normal file
444
misc/Impact.pack
Normal file
@ -0,0 +1,444 @@
|
||||
{
|
||||
"Impact::MAKE_BASIC_PIPE": {
|
||||
"category": "",
|
||||
"config": {
|
||||
"1": {
|
||||
"input": {
|
||||
"text": {
|
||||
"name": "Positive prompt"
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"input": {
|
||||
"text": {
|
||||
"name": "Negative prompt"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"datetime": 1705418802481,
|
||||
"external": [],
|
||||
"links": [
|
||||
[
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
1,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
1,
|
||||
1,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
0,
|
||||
2,
|
||||
3,
|
||||
2,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
1,
|
||||
0,
|
||||
3,
|
||||
3,
|
||||
3,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
2,
|
||||
0,
|
||||
3,
|
||||
4,
|
||||
4,
|
||||
"CONDITIONING"
|
||||
]
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"flags": {},
|
||||
"index": 0,
|
||||
"mode": 0,
|
||||
"order": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"links": [],
|
||||
"name": "MODEL",
|
||||
"shape": 3,
|
||||
"slot_index": 0,
|
||||
"type": "MODEL"
|
||||
},
|
||||
{
|
||||
"links": [],
|
||||
"name": "CLIP",
|
||||
"shape": 3,
|
||||
"slot_index": 1,
|
||||
"type": "CLIP"
|
||||
},
|
||||
{
|
||||
"links": [],
|
||||
"name": "VAE",
|
||||
"shape": 3,
|
||||
"slot_index": 2,
|
||||
"type": "VAE"
|
||||
}
|
||||
],
|
||||
"pos": [
|
||||
550,
|
||||
360
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 98
|
||||
},
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"widgets_values": [
|
||||
"SDXL/sd_xl_base_1.0_0.9vae.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"flags": {},
|
||||
"index": 1,
|
||||
"inputs": [
|
||||
{
|
||||
"link": null,
|
||||
"name": "clip",
|
||||
"type": "CLIP"
|
||||
}
|
||||
],
|
||||
"mode": 0,
|
||||
"order": 1,
|
||||
"outputs": [
|
||||
{
|
||||
"links": [],
|
||||
"name": "CONDITIONING",
|
||||
"shape": 3,
|
||||
"slot_index": 0,
|
||||
"type": "CONDITIONING"
|
||||
}
|
||||
],
|
||||
"pos": [
|
||||
940,
|
||||
480
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"size": {
|
||||
"0": 263,
|
||||
"1": 99
|
||||
},
|
||||
"title": "Positive",
|
||||
"type": "CLIPTextEncode",
|
||||
"widgets_values": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"flags": {},
|
||||
"index": 2,
|
||||
"inputs": [
|
||||
{
|
||||
"link": null,
|
||||
"name": "clip",
|
||||
"type": "CLIP"
|
||||
}
|
||||
],
|
||||
"mode": 0,
|
||||
"order": 2,
|
||||
"outputs": [
|
||||
{
|
||||
"links": [],
|
||||
"name": "CONDITIONING",
|
||||
"shape": 3,
|
||||
"slot_index": 0,
|
||||
"type": "CONDITIONING"
|
||||
}
|
||||
],
|
||||
"pos": [
|
||||
940,
|
||||
640
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"size": {
|
||||
"0": 263,
|
||||
"1": 99
|
||||
},
|
||||
"title": "Negative",
|
||||
"type": "CLIPTextEncode",
|
||||
"widgets_values": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"flags": {},
|
||||
"index": 3,
|
||||
"inputs": [
|
||||
{
|
||||
"link": null,
|
||||
"name": "model",
|
||||
"type": "MODEL"
|
||||
},
|
||||
{
|
||||
"link": null,
|
||||
"name": "clip",
|
||||
"type": "CLIP"
|
||||
},
|
||||
{
|
||||
"link": null,
|
||||
"name": "vae",
|
||||
"type": "VAE"
|
||||
},
|
||||
{
|
||||
"link": null,
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING"
|
||||
},
|
||||
{
|
||||
"link": null,
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING"
|
||||
}
|
||||
],
|
||||
"mode": 0,
|
||||
"order": 3,
|
||||
"outputs": [
|
||||
{
|
||||
"links": null,
|
||||
"name": "basic_pipe",
|
||||
"shape": 3,
|
||||
"slot_index": 0,
|
||||
"type": "BASIC_PIPE"
|
||||
}
|
||||
],
|
||||
"pos": [
|
||||
1320,
|
||||
360
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ToBasicPipe"
|
||||
},
|
||||
"size": {
|
||||
"0": 241.79998779296875,
|
||||
"1": 106
|
||||
},
|
||||
"type": "ToBasicPipe"
|
||||
}
|
||||
],
|
||||
"packname": "Impact",
|
||||
"version": "1.0"
|
||||
},
|
||||
"Impact::SIMPLE_DETAILER_PIPE": {
|
||||
"category": "",
|
||||
"config": {
|
||||
"0": {
|
||||
"output": {
|
||||
"0": {
|
||||
"visible": false
|
||||
},
|
||||
"1": {
|
||||
"visible": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"2": {
|
||||
"input": {
|
||||
"Select to add LoRA": {
|
||||
"visible": false
|
||||
},
|
||||
"Select to add Wildcard": {
|
||||
"visible": false
|
||||
},
|
||||
"wildcard": {
|
||||
"visible": false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"datetime": 1705419147116,
|
||||
"external": [],
|
||||
"links": [
|
||||
[
|
||||
null,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
6,
|
||||
"BASIC_PIPE"
|
||||
],
|
||||
[
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
1,
|
||||
13,
|
||||
"BBOX_DETECTOR"
|
||||
],
|
||||
[
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
2,
|
||||
15,
|
||||
"SAM_MODEL"
|
||||
]
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"flags": {},
|
||||
"index": 0,
|
||||
"mode": 0,
|
||||
"order": 2,
|
||||
"outputs": [
|
||||
{
|
||||
"links": [],
|
||||
"name": "BBOX_DETECTOR",
|
||||
"shape": 3,
|
||||
"type": "BBOX_DETECTOR"
|
||||
},
|
||||
{
|
||||
"links": null,
|
||||
"name": "SEGM_DETECTOR",
|
||||
"shape": 3,
|
||||
"type": "SEGM_DETECTOR"
|
||||
}
|
||||
],
|
||||
"pos": [
|
||||
590,
|
||||
830
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "UltralyticsDetectorProvider"
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 78
|
||||
},
|
||||
"type": "UltralyticsDetectorProvider",
|
||||
"widgets_values": [
|
||||
"bbox/Eyeful_v1.pt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"flags": {},
|
||||
"index": 1,
|
||||
"mode": 0,
|
||||
"order": 3,
|
||||
"outputs": [
|
||||
{
|
||||
"links": [],
|
||||
"name": "SAM_MODEL",
|
||||
"shape": 3,
|
||||
"type": "SAM_MODEL"
|
||||
}
|
||||
],
|
||||
"pos": [
|
||||
590,
|
||||
960
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "SAMLoader"
|
||||
},
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 82
|
||||
},
|
||||
"type": "SAMLoader",
|
||||
"widgets_values": [
|
||||
"sam_vit_b_01ec64.pth",
|
||||
"AUTO"
|
||||
]
|
||||
},
|
||||
{
|
||||
"flags": {},
|
||||
"index": 2,
|
||||
"inputs": [
|
||||
{
|
||||
"link": null,
|
||||
"name": "basic_pipe",
|
||||
"type": "BASIC_PIPE"
|
||||
},
|
||||
{
|
||||
"link": null,
|
||||
"name": "bbox_detector",
|
||||
"slot_index": 1,
|
||||
"type": "BBOX_DETECTOR"
|
||||
},
|
||||
{
|
||||
"link": null,
|
||||
"name": "sam_model_opt",
|
||||
"slot_index": 2,
|
||||
"type": "SAM_MODEL"
|
||||
},
|
||||
{
|
||||
"link": null,
|
||||
"name": "segm_detector_opt",
|
||||
"type": "SEGM_DETECTOR"
|
||||
},
|
||||
{
|
||||
"link": null,
|
||||
"name": "detailer_hook",
|
||||
"type": "DETAILER_HOOK"
|
||||
}
|
||||
],
|
||||
"mode": 0,
|
||||
"order": 5,
|
||||
"outputs": [
|
||||
{
|
||||
"links": null,
|
||||
"name": "detailer_pipe",
|
||||
"shape": 3,
|
||||
"type": "DETAILER_PIPE"
|
||||
}
|
||||
],
|
||||
"pos": [
|
||||
1044,
|
||||
812
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "BasicPipeToDetailerPipe"
|
||||
},
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 204
|
||||
},
|
||||
"type": "BasicPipeToDetailerPipe",
|
||||
"widgets_values": [
|
||||
"",
|
||||
"Select the LoRA to add to the text",
|
||||
"Select the Wildcard to add to the text"
|
||||
]
|
||||
}
|
||||
],
|
||||
"packname": "Impact",
|
||||
"version": "1.0"
|
||||
}
|
||||
}
|
||||
@ -132,7 +132,7 @@
|
||||
},
|
||||
{
|
||||
"name": "stabilityai/stable-diffusion-x4-upscaler",
|
||||
"type": "checkpoint",
|
||||
"type": "checkpoints",
|
||||
"base": "upscale",
|
||||
"save_path": "checkpoints/upscale",
|
||||
"description": "[3.53GB] This upscaling model is a latent text-guided diffusion model and should be used with SD_4XUpscale_Conditioning and KSampler.",
|
||||
@ -532,7 +532,7 @@
|
||||
},
|
||||
{
|
||||
"name": "Segmind-Vega",
|
||||
"type": "checkpoint",
|
||||
"type": "checkpoints",
|
||||
"base": "segmind-vega",
|
||||
"save_path": "checkpoints/segmind-vega",
|
||||
"description": "The Segmind-Vega Model is a distilled version of the Stable Diffusion XL (SDXL), offering a remarkable 70% reduction in size and an impressive 100% speedup while retaining high-quality text-to-image generation capabilities.",
|
||||
@ -1590,6 +1590,26 @@
|
||||
"filename": "ip-adapter-faceid_sd15_lora.safetensors",
|
||||
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid_sd15_lora.safetensors"
|
||||
},
|
||||
{
|
||||
"name": "ip-adapter-faceid-plusv2_sd15.bin",
|
||||
"type": "IP-Adapter",
|
||||
"base": "SD1.5",
|
||||
"save_path": "ipadapter",
|
||||
"description": "IP-Adapter-FaceID-Plus V2 Model (SD1.5)",
|
||||
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
|
||||
"filename": "ip-adapter-faceid-plusv2_sd15.bin",
|
||||
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sd15.bin"
|
||||
},
|
||||
{
|
||||
"name": "ip-adapter-faceid-plusv2_sd15_lora.safetensors",
|
||||
"type": "lora",
|
||||
"base": "SD1.5",
|
||||
"save_path": "loras/ipadapter",
|
||||
"description": "IP-Adapter-FaceID-Plus V2 LoRA Model (SD1.5)",
|
||||
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
|
||||
"filename": "ip-adapter-faceid-plusv2_sd15_lora.safetensors",
|
||||
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sd15_lora.safetensors"
|
||||
},
|
||||
{
|
||||
"name": "ip-adapter_sdxl.safetensors",
|
||||
"type": "IP-Adapter",
|
||||
|
||||
@ -9,10 +9,89 @@
|
||||
"description": "If you see this message, your ComfyUI-Manager is outdated.\nDev channel provides only the list of the developing nodes. If you want to find the complete node list, please go to the Default channel."
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
"author": "prismwastaken",
|
||||
"title": "prism-tools",
|
||||
"reference": "https://github.com/prismwastaken/comfyui-tools",
|
||||
"files": [
|
||||
"https://github.com/prismwastaken/comfyui-tools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "prism-tools"
|
||||
},
|
||||
{
|
||||
"author": "DimaChaichan",
|
||||
"title": "LAizypainter-Exporter-ComfyUI [WIP]",
|
||||
"reference": "https://github.com/DimaChaichan/LAizypainter-Exporter-ComfyUI",
|
||||
"files": [
|
||||
"https://github.com/DimaChaichan/LAizypainter-Exporter-ComfyUI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "WIP"
|
||||
},
|
||||
{
|
||||
"author": "ZHO-ZHO-ZHO",
|
||||
"title": "ComfyUI PhotoMaker (WIP)",
|
||||
"reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-PhotoMaker",
|
||||
"files": [
|
||||
"https://github.com/ZHO-ZHO-ZHO/ComfyUI-PhotoMaker"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Unofficial implementation of [a/PhotoMaker](https://github.com/TencentARC/PhotoMaker) for ComfyUI(WIP) Testing……"
|
||||
},
|
||||
{
|
||||
"author": "poisenbery",
|
||||
"title": "NudeNet-Detector-Provider [WIP]",
|
||||
"reference": "https://github.com/poisenbery/NudeNet-Detector-Provider",
|
||||
"files": [
|
||||
"https://github.com/poisenbery/NudeNet-Detector-Provider"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "BBOX Detector Provider for NudeNet. Bethesda version of NudeNet V3 detector provider to work with Impact Pack ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "LarryJane491",
|
||||
"title": "ComfyUI-ModelUnloader",
|
||||
"reference": "https://github.com/LarryJane491/ComfyUI-ModelUnloader",
|
||||
"files": [
|
||||
"https://github.com/LarryJane491/ComfyUI-ModelUnloader"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A simple custom node that unloads all models. Useful for developers or users who want to free some memory."
|
||||
},
|
||||
{
|
||||
"author": "AIGODLIKE",
|
||||
"title": "AIGODLIKE/ComfyUI-Model-Manager [WIP]",
|
||||
"reference": "https://github.com/AIGODLIKE/ComfyUI-Studio",
|
||||
"files": [
|
||||
"https://github.com/AIGODLIKE/ComfyUI-Studio"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "WIP"
|
||||
},
|
||||
{
|
||||
"author": "kadirnar",
|
||||
"title": "ComfyUI-Transformers",
|
||||
"reference": "https://github.com/kadirnar/ComfyUI-Transformers",
|
||||
"files": [
|
||||
"https://github.com/kadirnar/ComfyUI-Transformers"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:DepthEstimation."
|
||||
},
|
||||
{
|
||||
"author": "MrAdamBlack",
|
||||
"title": "CheckProgress [WIP]",
|
||||
"reference": "https://github.com/MrAdamBlack/CheckProgress",
|
||||
"files": [
|
||||
"https://github.com/MrAdamBlack/CheckProgress"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "I was looking for a node to put in place to ensure my prompt etc where going as expected before the rest of the flow executed. To end the session, I just return the input image as None (see expected error). Recommend using it alongside PreviewImage, then output to the rest of the flow and Save Image."
|
||||
},
|
||||
{
|
||||
"author": "11cafe",
|
||||
"title": "ComfyUI Model Manager [WIP]",
|
||||
"title": "11cafe/ComfyUI Model Manager [WIP]",
|
||||
"reference": "https://github.com/11cafe/model-manager-comfyui",
|
||||
"files": [
|
||||
"https://github.com/11cafe/model-manager-comfyui"
|
||||
|
||||
@ -61,6 +61,22 @@
|
||||
"title_aux": "comfy-consistency-vae"
|
||||
}
|
||||
],
|
||||
"https://github.com/LarryJane491/ComfyUI-ModelUnloader": [
|
||||
[
|
||||
"Model Unloader"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI-ModelUnloader"
|
||||
}
|
||||
],
|
||||
"https://github.com/MrAdamBlack/CheckProgress": [
|
||||
[
|
||||
"CHECK_PROGRESS"
|
||||
],
|
||||
{
|
||||
"title_aux": "CheckProgress [WIP]"
|
||||
}
|
||||
],
|
||||
"https://github.com/PluMaZero/ComfyUI-SpaceFlower": [
|
||||
[
|
||||
"SpaceFlower_HangulPrompt",
|
||||
@ -98,6 +114,23 @@
|
||||
"title_aux": "pre-comfyui-stablsr"
|
||||
}
|
||||
],
|
||||
"https://github.com/ZHO-ZHO-ZHO/ComfyUI-AnyText": [
|
||||
[
|
||||
"AnyTextNode_Zho"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI-AnyText\uff08WIP\uff09"
|
||||
}
|
||||
],
|
||||
"https://github.com/ZHO-ZHO-ZHO/ComfyUI-PhotoMaker": [
|
||||
[
|
||||
"PhotoMaker_Batch_Zho",
|
||||
"PhotoMaker_Zho"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI PhotoMaker (WIP)"
|
||||
}
|
||||
],
|
||||
"https://github.com/alt-key-project/comfyui-dream-video-batches": [
|
||||
[
|
||||
"Blended Transition [DVB]",
|
||||
@ -138,6 +171,30 @@
|
||||
"title_aux": "Dream Project Video Batches [WIP]"
|
||||
}
|
||||
],
|
||||
"https://github.com/birnam/ComfyUI-GenData-Pack": [
|
||||
[
|
||||
"Checkpoint From String \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Checkpoint Rerouter \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Checkpoint Selector Stacker \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Checkpoint Selector \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Checkpoint to String \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Decode GenData \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Encode GenData \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"GenData Stacker \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"LoRA Stack to String \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"LoRA Stacker From Prompt \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Load Checkpoints From File \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Load GenData From Dir \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Parse GenData \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"Save Image From GenData \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"VAE From String \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"VAE to String \ud83d\udc69\u200d\ud83d\udcbb",
|
||||
"\u00d7 Product CheckpointXGenDatas \ud83d\udc69\u200d\ud83d\udcbb"
|
||||
],
|
||||
{
|
||||
"title_aux": "Gen Data Tester [WIP]"
|
||||
}
|
||||
],
|
||||
"https://github.com/comfyanonymous/ComfyUI": [
|
||||
[
|
||||
"BasicScheduler",
|
||||
@ -197,6 +254,7 @@
|
||||
"ImageSharpen",
|
||||
"ImageToMask",
|
||||
"ImageUpscaleWithModel",
|
||||
"InpaintModelConditioning",
|
||||
"InvertMask",
|
||||
"JoinImageWithAlpha",
|
||||
"KSampler",
|
||||
@ -412,6 +470,7 @@
|
||||
"JN_KSamplerAdvancedParams",
|
||||
"JN_KSamplerFaceRestoreParams",
|
||||
"JN_KSamplerResizeInputParams",
|
||||
"JN_KSamplerResizeMaskAreaParams",
|
||||
"JN_KSamplerResizeOutputParams",
|
||||
"JN_KSamplerSeamlessParams",
|
||||
"JN_KSamplerTileParams",
|
||||
@ -454,11 +513,22 @@
|
||||
"title_aux": "jn_node_suite_comfyui [WIP]"
|
||||
}
|
||||
],
|
||||
"https://github.com/kadirnar/ComfyUI-Transformers": [
|
||||
[
|
||||
"DepthEstimationPipeline"
|
||||
],
|
||||
{
|
||||
"title_aux": "ComfyUI-Transformers"
|
||||
}
|
||||
],
|
||||
"https://github.com/kadirnar/comfyui_helpers": [
|
||||
[
|
||||
"CLIPSeg",
|
||||
"CircularVAEDecode",
|
||||
"CombineMasks",
|
||||
"CustomKSamplerAdvancedTile",
|
||||
"ImageLoaderAndProcessor",
|
||||
"ImageToContrastMask",
|
||||
"JDC_AutoContrast",
|
||||
"JDC_BlendImages",
|
||||
"JDC_BrownNoise",
|
||||
@ -571,6 +641,14 @@
|
||||
"title_aux": "comfy-trt-test [WIP]"
|
||||
}
|
||||
],
|
||||
"https://github.com/poisenbery/NudeNet-Detector-Provider": [
|
||||
[
|
||||
"NudeNetDetectorProvider"
|
||||
],
|
||||
{
|
||||
"title_aux": "NudeNet-Detector-Provider [WIP]"
|
||||
}
|
||||
],
|
||||
"https://github.com/solarpush/comfyui_sendimage_node": [
|
||||
[
|
||||
"Send_To_Pod"
|
||||
|
||||
@ -6,10 +6,321 @@
|
||||
"reference": "https://github.com/ltdrdata/ComfyUI-Manager",
|
||||
"files": [],
|
||||
"install_type": "git-clone",
|
||||
"description": "If you see this message, your ComfyUI-Manager is outdated.\nRecent channel provides only the list of the latest nodes. If you want to find the complete node list, please go to the Default channel."
|
||||
"description": "If you see this message, your ComfyUI-Manager is outdated.\nRecent channel provides only the list of the latest nodes. If you want to find the complete node list, please go to the Default channel.\nMaking LoRA has never been easier!"
|
||||
},
|
||||
|
||||
|
||||
|
||||
{
|
||||
"author": "Taremin",
|
||||
"title": "WebUI Monaco Prompt",
|
||||
"reference": "https://github.com/Taremin/webui-monaco-prompt",
|
||||
"files": [
|
||||
"https://github.com/Taremin/webui-monaco-prompt"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Make it possible to edit the prompt using the Monaco Editor, an editor implementation used in VSCode.\nNOTE: This extension supports both ComfyUI and A1111 simultaneously."
|
||||
},
|
||||
{
|
||||
"author": "JcandZero",
|
||||
"title": "ComfyUI_GLM4Node",
|
||||
"reference": "https://github.com/JcandZero/ComfyUI_GLM4Node",
|
||||
"files": [
|
||||
"https://github.com/JcandZero/ComfyUI_GLM4Node"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "GLM4 Vision Integration"
|
||||
},
|
||||
{
|
||||
"author": "Miosp",
|
||||
"title": "ComfyUI-FBCNN",
|
||||
"reference": "https://github.com/Miosp/ComfyUI-FBCNN",
|
||||
"files": [
|
||||
"https://github.com/Miosp/ComfyUI-FBCNN"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI implementation of [a/FBCNN](https://github.com/jiaxi-jiang/FBCNN). This is a JPEG artifact/compression removal tool, which can work automatically or with the help of a user."
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-LightGlue",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-LightGlue",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-LightGlue"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is an ComfyUI implementation of LightGlue to generate motion brush"
|
||||
},
|
||||
{
|
||||
"author": "Mr.ForExample",
|
||||
"title": "ComfyUI-AnimateAnyone-Evolved",
|
||||
"reference": "https://github.com/MrForExample/ComfyUI-AnimateAnyone-Evolved",
|
||||
"files": [
|
||||
"https://github.com/MrForExample/ComfyUI-AnimateAnyone-Evolved"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Improved AnimateAnyone implementation that allows you to use the opse image sequence and reference image to generate stylized video.\nThe current goal of this project is to achieve desired pose2video result with 1+FPS on GPUs that are equal to or better than RTX 3080!🚀\n[w/The torch environment may be compromised due to version issues as some torch-related packages are being reinstalled.]"
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-I2VGEN-XL",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-I2VGEN-XL",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-I2VGEN-XL"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is an implementation of [a/i2vgen-xl](https://github.com/ali-vilab/i2vgen-xl)"
|
||||
},
|
||||
{
|
||||
"author": "Inzaniak",
|
||||
"title": "Ranbooru for ComfyUI",
|
||||
"reference": "https://github.com/Inzaniak/comfyui-ranbooru",
|
||||
"files": [
|
||||
"https://github.com/Inzaniak/comfyui-ranbooru"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Ranbooru is an extension for the comfyUI. The purpose of this extension is to add a node that gets a random set of tags from boorus pictures. This is mostly being used to help me test my checkpoints on a large variety of"
|
||||
},
|
||||
{
|
||||
"author": "Taremin",
|
||||
"title": "ComfyUI String Tools",
|
||||
"reference": "https://github.com/Taremin/comfyui-string-tools",
|
||||
"files": [
|
||||
"https://github.com/Taremin/comfyui-string-tools"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": " This extension provides the StringToolsConcat node, which concatenates multiple texts, and the StringToolsRandomChoice node, which selects one randomly from multiple texts."
|
||||
},
|
||||
{
|
||||
"author": "dave-palt",
|
||||
"title": "comfyui_DSP_imagehelpers",
|
||||
"reference": "https://github.com/dave-palt/comfyui_DSP_imagehelpers",
|
||||
"files": [
|
||||
"https://github.com/dave-palt/comfyui_DSP_imagehelpers"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: DSP Image Concat"
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-Moore-AnimateAnyone",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-Moore-AnimateAnyone",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-Moore-AnimateAnyone"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Run python tools/download_weights.py first to download weights automatically"
|
||||
},
|
||||
{
|
||||
"author": "chflame163",
|
||||
"title": "ComfyUI Layer Style",
|
||||
"reference": "https://github.com/chflame163/ComfyUI_LayerStyle",
|
||||
"files": [
|
||||
"https://github.com/chflame163/ComfyUI_LayerStyle"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A set of nodes for ComfyUI it generate image like Adobe Photoshop's Layer Style. the Drop Shadow is first completed node, and follow-up work is in progress."
|
||||
},
|
||||
{
|
||||
"author": "kijai",
|
||||
"title": "ComfyUI-DDColor",
|
||||
"reference": "https://github.com/kijai/ComfyUI-DDColor",
|
||||
"files": [
|
||||
"https://github.com/kijai/ComfyUI-DDColor"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Node to use [a/DDColor](https://github.com/piddnad/DDColor) in ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "prozacgod",
|
||||
"title": "ComfyUI Multi-Workspace",
|
||||
"reference": "https://github.com/prozacgod/comfyui-pzc-multiworkspace",
|
||||
"files": [
|
||||
"https://github.com/prozacgod/comfyui-pzc-multiworkspace"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A simple, quick, and dirty implementation of multiple workspaces within ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "Siberpone",
|
||||
"title": "Lazy Pony Prompter",
|
||||
"reference": "https://github.com/Siberpone/lazy-pony-prompter",
|
||||
"files": [
|
||||
"https://github.com/Siberpone/lazy-pony-prompter"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A pony prompt helper extension for AUTOMATIC1111's Stable Diffusion Web UI and ComfyUI that utilizes the full power of your favorite booru query syntax. Currently supports [a/Derpibooru](https://derpibooru/org) and [a/E621](https://e621.net/)."
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-MotionCtrl-SVD",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-MotionCtrl-SVD",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-MotionCtrl-SVD"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Download the weights of MotionCtrl-SVD [a/motionctrl_svd.ckpt](https://huggingface.co/TencentARC/MotionCtrl/blob/main/motionctrl_svd.ckpt) and put it to ComfyUI/models/checkpoints"
|
||||
},
|
||||
{
|
||||
"author": "JaredTherriault",
|
||||
"title": "ComfyUI-JNodes",
|
||||
"reference": "https://github.com/JaredTherriault/ComfyUI-JNodes",
|
||||
"files": [
|
||||
"https://github.com/JaredTherriault/ComfyUI-JNodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "python and web UX improvements for ComfyUI.\n[w/'DynamicPrompts.js' and 'EditAttention.js' from the core, along with 'ImageFeed.js' and 'favicon.js' from the custom scripts of pythongosssss, are not compatible. Therefore, manual deletion of these files is required to use this web extension.]"
|
||||
},
|
||||
{
|
||||
"author": "nkchocoai",
|
||||
"title": "ComfyUI-SizeFromPresets",
|
||||
"reference": "https://github.com/nkchocoai/ComfyUI-SizeFromPresets",
|
||||
"files": [
|
||||
"https://github.com/nkchocoai/ComfyUI-SizeFromPresets"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Add a node that outputs width and height of the size selected from the preset (.csv)."
|
||||
},
|
||||
{
|
||||
"author": "HAL41",
|
||||
"title": "ComfyUI aichemy nodes",
|
||||
"reference": "https://github.com/HAL41/ComfyUI-aichemy-nodes",
|
||||
"files": [
|
||||
"https://github.com/HAL41/ComfyUI-aichemy-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Simple node to handle scaling of YOLOv8 segmentation masks"
|
||||
},
|
||||
{
|
||||
"author": "abyz22",
|
||||
"title": "image_control",
|
||||
"reference": "https://github.com/abyz22/image_control",
|
||||
"files": [
|
||||
"https://github.com/abyz22/image_control"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:abyz22_Padding Image, abyz22_ImpactWildcardEncode, abyz22_setimageinfo, abyz22_SaveImage, abyz22_ImpactWildcardEncode_GetPrompt, abyz22_SetQueue, abyz22_drawmask, abyz22_FirstNonNull, abyz22_blendimages, abyz22_blend_onecolor"
|
||||
},
|
||||
{
|
||||
"author": "foxtrot-roger",
|
||||
"title": "RF Nodes",
|
||||
"reference": "https://github.com/foxtrot-roger/comfyui-rf-nodes",
|
||||
"files": [
|
||||
"https://github.com/foxtrot-roger/comfyui-rf-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A bunch of nodes that can be useful to manipulate primitive types (numbers, text, ...) Also some helpers to generate text and timestamps."
|
||||
},
|
||||
{
|
||||
"author": "LarryJane491",
|
||||
"title": "Lora-Training-in-Comfy",
|
||||
"reference": "https://github.com/LarryJane491/Lora-Training-in-Comfy",
|
||||
"files": [
|
||||
"https://github.com/LarryJane491/Lora-Training-in-Comfy"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This custom node lets you train LoRA directly in ComfyUI! By default, it saves directly in your ComfyUI lora folder. That means you just have to refresh after training (...and select the LoRA) to test it!"
|
||||
},
|
||||
{
|
||||
"author": "Taremin",
|
||||
"title": "ComfyUI Prompt ExtraNetworks",
|
||||
"reference": "https://github.com/Taremin/comfyui-prompt-extranetworks",
|
||||
"files": [
|
||||
"https://github.com/Taremin/comfyui-prompt-extranetworks"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Instead of LoraLoader or HypernetworkLoader, it receives a prompt and loads and applies LoRA or HN based on the specifications within the prompt. The main purpose of this custom node is to allow changes without reconnecting the LoraLoader node when the prompt is randomly altered, etc."
|
||||
},
|
||||
{
|
||||
"author": "Layer-norm",
|
||||
"title": "Comfyui lama remover",
|
||||
"reference": "https://github.com/Layer-norm/comfyui-lama-remover",
|
||||
"files": [
|
||||
"https://github.com/Layer-norm/comfyui-lama-remover"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A very simple ComfyUI node to remove item with mask."
|
||||
},
|
||||
{
|
||||
"author": "komojini",
|
||||
"title": "komojini-comfyui-nodes",
|
||||
"reference": "https://github.com/komojini/komojini-comfyui-nodes",
|
||||
"files": [
|
||||
"https://github.com/komojini/komojini-comfyui-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:YouTube Video Loader. Custom ComfyUI Nodes for video generation"
|
||||
},
|
||||
{
|
||||
"author": "LarryJane491",
|
||||
"title": "Image-Captioning-in-ComfyUI",
|
||||
"reference": "https://github.com/LarryJane491/Image-Captioning-in-ComfyUI",
|
||||
"files": [
|
||||
"https://github.com/LarryJane491/Image-Captioning-in-ComfyUI"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "The LoRA Caption custom nodes, just like their name suggests, allow you to caption images so they are ready for LoRA training."
|
||||
},
|
||||
{
|
||||
"author": "HebelHuber",
|
||||
"title": "comfyui-enhanced-save-node",
|
||||
"reference": "https://github.com/HebelHuber/comfyui-enhanced-save-node",
|
||||
"files": [
|
||||
"https://github.com/HebelHuber/comfyui-enhanced-save-node"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:Enhanced Save Node"
|
||||
},
|
||||
{
|
||||
"author": "chaojie",
|
||||
"title": "ComfyUI-DragNUWA",
|
||||
"reference": "https://github.com/chaojie/ComfyUI-DragNUWA",
|
||||
"files": [
|
||||
"https://github.com/chaojie/ComfyUI-DragNUWA"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Download the weights of DragNUWA [a/drag_nuwa_svd.pth](https://drive.google.com/file/d/1Z4JOley0SJCb35kFF4PCc6N6P1ftfX4i/view) and put it to ComfyUI/models/checkpoints/drag_nuwa_svd.pth\n[w/Due to changes in the torch package and versions of many other packages, it may disrupt your installation environment.]"
|
||||
},
|
||||
{
|
||||
"author": "chflame163",
|
||||
"title": "ComfyUI_WordCloud",
|
||||
"reference": "https://github.com/chflame163/ComfyUI_WordCloud",
|
||||
"files": [
|
||||
"https://github.com/chflame163/ComfyUI_WordCloud"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:Word Cloud, Load Text File"
|
||||
},
|
||||
{
|
||||
"author": "underclockeddev",
|
||||
"title": "Preview Subselection Node for ComfyUI",
|
||||
"reference": "https://github.com/underclockeddev/ComfyUI-PreviewSubselection-Node",
|
||||
"files": [
|
||||
"https://github.com/underclockeddev/ComfyUI-PreviewSubselection-Node/raw/master/preview_subselection.py"
|
||||
],
|
||||
"install_type": "copy",
|
||||
"description": "A node which takes in x, y, width, height, total width, and total height, in order to accurately represent the area of an image which is covered by area-based conditioning."
|
||||
},
|
||||
{
|
||||
"author": "AInseven",
|
||||
"title": "ComfyUI-fastblend",
|
||||
"reference": "https://github.com/AInseven/ComfyUI-fastblend",
|
||||
"files": [
|
||||
"https://github.com/AInseven/ComfyUI-fastblend"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "fastblend for comfyui, and other nodes that I write for video2video. rebatch image, my openpose"
|
||||
},
|
||||
{
|
||||
"author": "glowcone",
|
||||
"title": "Load Image From Base64 URI",
|
||||
"reference": "https://github.com/glowcone/comfyui-base64-to-image",
|
||||
"files": [
|
||||
"https://github.com/glowcone/comfyui-base64-to-image"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: LoadImageFromBase64. Loads an image and its transparency mask from a base64-encoded data URI for easy API connection."
|
||||
},
|
||||
{
|
||||
"author": "shiimizu",
|
||||
"title": "Tiled Diffusion & VAE for ComfyUI",
|
||||
@ -120,7 +431,7 @@
|
||||
"https://github.com/kft334/Knodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Image(s) To Websocket (Base64)"
|
||||
"description": "Nodes: Image(s) To Websocket (Base64), Load Image (Base64),Load Images (Base64)"
|
||||
},
|
||||
{
|
||||
"author": "alexopus",
|
||||
@ -392,307 +703,6 @@
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This extension provides nodes for [a/DeepCache: Accelerating Diffusion Models for Free](https://arxiv.org/abs/2312.00858)\nNOTE:Original code can be found [a/here](https://gist.github.com/laksjdjf/435c512bc19636e9c9af4ee7bea9eb86). Full credit to laksjdjf for sharing the code. "
|
||||
},
|
||||
{
|
||||
"author": "edenartlab",
|
||||
"title": "eden_comfy_pipelines",
|
||||
"reference": "https://github.com/edenartlab/eden_comfy_pipelines",
|
||||
"files": [
|
||||
"https://github.com/edenartlab/eden_comfy_pipelines"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:CLIP Interrogator."
|
||||
},
|
||||
{
|
||||
"author": "Limitex",
|
||||
"title": "ComfyUI-Diffusers",
|
||||
"reference": "https://github.com/Limitex/ComfyUI-Diffusers",
|
||||
"files": [
|
||||
"https://github.com/Limitex/ComfyUI-Diffusers"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This extension enables the use of the diffuser pipeline in ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "Limitex",
|
||||
"title": "ComfyUI-Calculation",
|
||||
"reference": "https://github.com/Limitex/ComfyUI-Calculation",
|
||||
"files": [
|
||||
"https://github.com/Limitex/ComfyUI-Calculation"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Center Calculation. Improved Numerical Calculation for ComfyUI"
|
||||
},
|
||||
{
|
||||
"author": "Comfyui_GPT_Story",
|
||||
"title": "junglehu",
|
||||
"reference": "https://github.com/junglehu/Comfyui_Story_LLmA",
|
||||
"files": [
|
||||
"https://github.com/junglehu/Comfyui_Story_LLmA"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "GPT+Comfyui Generate coherent pictures"
|
||||
},
|
||||
{
|
||||
"author": "HarroweD and quadmoon",
|
||||
"title": "Harronode",
|
||||
"reference": "https://github.com/NotHarroweD/Harronode",
|
||||
"nodename_pattern": "Harronode",
|
||||
"files": [
|
||||
"https://github.com/NotHarroweD/Harronode.git"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Harronode is a custom node designed to build prompts easily for use with the Harrlogos SDXL LoRA. This Node simplifies the process of crafting prompts and makes all built in activation terms available at your fingertips."
|
||||
},
|
||||
{
|
||||
"author": "styler00dollar",
|
||||
"title": "ComfyUI-sudo-latent-upscale",
|
||||
"reference": "https://github.com/styler00dollar/ComfyUI-sudo-latent-upscale",
|
||||
"files": [
|
||||
"https://github.com/styler00dollar/ComfyUI-sudo-latent-upscale"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Directly upscaling inside the latent space. Model was trained for SD1.5 and drawn content. Might add new architectures or update models at some point. This took heavy inspriration from [city96/SD-Latent-Upscaler](https://github.com/city96/SD-Latent-Upscaler) and [Ttl/ComfyUi_NNLatentUpscale](https://github.com/Ttl/ComfyUi_NNLatentUpscale). "
|
||||
},
|
||||
{
|
||||
"author": "thecooltechguy",
|
||||
"title": "ComfyUI-ComfyRun",
|
||||
"reference": "https://github.com/thecooltechguy/ComfyUI-ComfyRun",
|
||||
"files": [
|
||||
"https://github.com/thecooltechguy/ComfyUI-ComfyRun"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "The easiest way to run & share any ComfyUI workflow [a/https://comfyrun.com](https://comfyrun.com)"
|
||||
},
|
||||
{
|
||||
"author": "ceruleandeep",
|
||||
"title": "ComfyUI LLaVA Captioner",
|
||||
"reference": "https://github.com/ceruleandeep/ComfyUI-LLaVA-Captioner",
|
||||
"files": [
|
||||
"https://github.com/ceruleandeep/ComfyUI-LLaVA-Captioner"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A ComfyUI extension for chatting with your images. Runs on your own system, no external services used, no filter. Uses the [a/LLaVA multimodal LLM](https://llava-vl.github.io/) so you can give instructions or ask questions in natural language. It's maybe as smart as GPT3.5, and it can see."
|
||||
},
|
||||
{
|
||||
"author": "jitcoder",
|
||||
"title": "LoraInfo",
|
||||
"reference": "https://github.com/jitcoder/lora-info",
|
||||
"files": [
|
||||
"https://github.com/jitcoder/lora-info"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Shows Lora information from CivitAI and outputs trigger words and example prompt"
|
||||
},
|
||||
{
|
||||
"author": "ttulttul",
|
||||
"title": "ComfyUI Iterative Mixing Nodes",
|
||||
"reference": "https://github.com/ttulttul/ComfyUI-Iterative-Mixer",
|
||||
"files": [
|
||||
"https://github.com/ttulttul/ComfyUI-Iterative-Mixer"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Iterative Mixing KSampler, Batch Unsampler, Iterative Mixing KSampler Advanced"
|
||||
},
|
||||
{
|
||||
"author": "OpenArt-AI",
|
||||
"title": "ComfyUI Assistant",
|
||||
"reference": "https://github.com/OpenArt-AI/ComfyUI-Assistant",
|
||||
"files": [
|
||||
"https://github.com/OpenArt-AI/ComfyUI-Assistant"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI Assistant is your one stop plugin for everything you need to get started with comfy-ui. Now it provides useful courses, tutorials, and basic templates."
|
||||
},
|
||||
{
|
||||
"author": "shockz0rz",
|
||||
"title": "comfy-easy-grids",
|
||||
"reference": "https://github.com/shockz0rz/comfy-easy-grids",
|
||||
"files": [
|
||||
"https://github.com/shockz0rz/comfy-easy-grids"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A set of custom nodes for creating image grids, sequences, and batches in ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "CosmicLaca",
|
||||
"title": "Primere nodes for ComfyUI",
|
||||
"reference": "https://github.com/CosmicLaca/ComfyUI_Primere_Nodes",
|
||||
"files": [
|
||||
"https://github.com/CosmicLaca/ComfyUI_Primere_Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This extension provides various utility nodes. Inputs(prompt, styles, dynamic, merger, ...), Outputs(style pile), Dashboard(selectors, loader, switch, ...), Networks(LORA, Embedding, Hypernetwork), Visuals(visual selectors, )"
|
||||
},
|
||||
{
|
||||
"author": "ZHO-ZHO-ZHO",
|
||||
"title": "ComfyUI-Gemini",
|
||||
"reference": "https://github.com/ZHO-ZHO-ZHO/ComfyUI-Gemini",
|
||||
"files": [
|
||||
"https://github.com/ZHO-ZHO-ZHO/ComfyUI-Gemini"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Using Gemini-pro & Gemini-pro-vision in ComfyUI."
|
||||
},
|
||||
{
|
||||
"author": "ZHO-ZHO-ZHO",
|
||||
"title": "comfyui-portrait-master-zh-cn",
|
||||
"reference": "https://github.com/ZHO-ZHO-ZHO/comfyui-portrait-master-zh-cn",
|
||||
"files": [
|
||||
"https://github.com/ZHO-ZHO-ZHO/comfyui-portrait-master-zh-cn"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI Portrait Master 简体中文版."
|
||||
},
|
||||
{
|
||||
"author": "Continue7777",
|
||||
"title": "comfyui-easyapi-nodes",
|
||||
"reference": "https://github.com/Continue7777/comfyui-yoy",
|
||||
"files": [
|
||||
"https://github.com/Continue7777/comfyui-yoy"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This plugin gives you the ability to generate a real Product rendering, commonly referred to as the mockup.The most important is that we can provide it for you,cause we have a complete supply chain.You can buy products with your designs or sell them. We can provide you dropshipping services."
|
||||
},
|
||||
{
|
||||
"author": "RenderRift",
|
||||
"title": "ComfyUI-RenderRiftNodes",
|
||||
"reference": "https://github.com/RenderRift/ComfyUI-RenderRiftNodes",
|
||||
"files": [
|
||||
"https://github.com/RenderRift/ComfyUI-RenderRiftNodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:RR_Date_Folder_Format, RR_Image_Metadata_Overlay, RR_VideoPathMetaExtraction, RR_DisplayMetaOptions. This extension provides nodes designed to enhance the Animatediff workflow."
|
||||
},
|
||||
{
|
||||
"author": "Haoming02",
|
||||
"title": "ComfyUI Floodgate",
|
||||
"reference": "https://github.com/Haoming02/comfyui-floodgate",
|
||||
"files": [
|
||||
"https://github.com/Haoming02/comfyui-floodgate"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This is an Extension for ComfyUI, which allows you to control the logic flow with just one click!"
|
||||
},
|
||||
{
|
||||
"author": "Trung0246",
|
||||
"title": "ComfyUI-0246",
|
||||
"reference": "https://github.com/Trung0246/ComfyUI-0246",
|
||||
"files": [
|
||||
"https://github.com/Trung0246/ComfyUI-0246"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Random nodes for ComfyUI I made to solve my struggle with ComfyUI (ex: pipe, process). Have varying quality."
|
||||
},
|
||||
{
|
||||
"author": "violet-chen",
|
||||
"title": "comfyui-psd2png",
|
||||
"reference": "https://github.com/violet-chen/comfyui-psd2png",
|
||||
"files": [
|
||||
"https://github.com/violet-chen/comfyui-psd2png"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Psd2Png."
|
||||
},
|
||||
{
|
||||
"author": "IDGallagher",
|
||||
"title": "IG Interpolation Nodes",
|
||||
"reference": "https://github.com/IDGallagher/ComfyUI-IG-Nodes",
|
||||
"files": [
|
||||
"https://github.com/IDGallagher/ComfyUI-IG-Nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Custom nodes to aid in the exploration of Latent Space"
|
||||
},
|
||||
{
|
||||
"author": "rcfcu2000",
|
||||
"title": "zhihuige-nodes-comfyui",
|
||||
"reference": "https://github.com/rcfcu2000/zhihuige-nodes-comfyui",
|
||||
"files": [
|
||||
"https://github.com/rcfcu2000/zhihuige-nodes-comfyui"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: Combine ZHGMasks, Cover ZHGMasks, ZHG FaceIndex, ZHG SaveImage, ZHG SmoothEdge, ZHG GetMaskArea, ..."
|
||||
},
|
||||
{
|
||||
"author": "rcsaquino",
|
||||
"title": "rcsaquino/comfyui-custom-nodes",
|
||||
"reference": "https://github.com/rcsaquino/comfyui-custom-nodes",
|
||||
"files": [
|
||||
"https://github.com/rcsaquino/comfyui-custom-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes: VAE Processor, VAE Loader, Background Remover"
|
||||
},
|
||||
{
|
||||
"author": "mozman",
|
||||
"title": "ComfyUI_mozman_nodes",
|
||||
"reference": "https://github.com/mozman/ComfyUI_mozman_nodes",
|
||||
"files": [
|
||||
"https://github.com/mozman/ComfyUI_mozman_nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This extension provides styler nodes for SDXL.\n\nNOTE: Due to the dynamic nature of node name definitions, ComfyUI-Manager cannot recognize the node list from this extension. The Missing nodes and Badge features are not available for this extension."
|
||||
},
|
||||
{
|
||||
"author": "meap158",
|
||||
"title": "ComfyUI-Background-Replacement",
|
||||
"reference": "https://github.com/meap158/ComfyUI-Background-Replacement",
|
||||
"files": [
|
||||
"https://github.com/meap158/ComfyUI-Background-Replacement"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Instantly replace your image's background."
|
||||
},
|
||||
{
|
||||
"author": "florestefano1975",
|
||||
"title": "comfyui-portrait-master",
|
||||
"reference": "https://github.com/florestefano1975/comfyui-portrait-master",
|
||||
"files": [
|
||||
"https://github.com/florestefano1975/comfyui-portrait-master"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI Portrait Master. A node designed to help AI image creators to generate prompts for human portraits."
|
||||
},
|
||||
{
|
||||
"author": "deroberon",
|
||||
"title": "StableZero123-comfyui",
|
||||
"reference": "https://github.com/deroberon/StableZero123-comfyui",
|
||||
"files": [
|
||||
"https://github.com/deroberon/StableZero123-comfyui"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "StableZero123 is a node wrapper that uses the model and technique provided [here](https://github.com/SUDO-AI-3D/zero123plus/). It uses the Zero123plus model to generate 3D views using just one image."
|
||||
},
|
||||
{
|
||||
"author": "dmarx",
|
||||
"title": "ComfyUI-Keyframed",
|
||||
"reference": "https://github.com/dmarx/ComfyUI-Keyframed",
|
||||
"files": [
|
||||
"https://github.com/dmarx/ComfyUI-Keyframed"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "ComfyUI nodes to facilitate parameter/prompt keyframing using comfyui nodes for defining and manipulating parameter curves. Essentially provides a ComfyUI interface to the [a/keyframed](https://github.com/dmarx/keyframed) library."
|
||||
},
|
||||
{
|
||||
"author": "TripleHeadedMonkey",
|
||||
"title": "ComfyUI_MileHighStyler",
|
||||
"reference": "https://github.com/TripleHeadedMonkey/ComfyUI_MileHighStyler",
|
||||
"files": [
|
||||
"https://github.com/TripleHeadedMonkey/ComfyUI_MileHighStyler"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This extension provides various SDXL Prompt Stylers. See: [a/youtube](https://youtu.be/WBHI-2uww7o?si=dijvDaUI4nmx4VkF)"
|
||||
},
|
||||
{
|
||||
"author": "Extraltodeus",
|
||||
"title": "sigmas_tools_and_the_golden_scheduler",
|
||||
"reference": "https://github.com/Extraltodeus/sigmas_tools_and_the_golden_scheduler",
|
||||
"files": [
|
||||
"https://github.com/Extraltodeus/sigmas_tools_and_the_golden_scheduler"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "A few nodes to mix sigmas and a custom scheduler that uses phi, then one using eval() to be able to schedule with custom formulas."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -11,6 +11,27 @@
|
||||
"url": "https://huggingface.co/TencentARC/MotionCtrl/resolve/main/motionctrl.pth"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "ip-adapter-faceid-plusv2_sd15.bin",
|
||||
"type": "IP-Adapter",
|
||||
"base": "SD1.5",
|
||||
"save_path": "ipadapter",
|
||||
"description": "IP-Adapter-FaceID-Plus V2 Model (SD1.5)",
|
||||
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
|
||||
"filename": "ip-adapter-faceid-plusv2_sd15.bin",
|
||||
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sd15.bin"
|
||||
},
|
||||
{
|
||||
"name": "ip-adapter-faceid-plusv2_sd15_lora.safetensors",
|
||||
"type": "lora",
|
||||
"base": "SD1.5",
|
||||
"save_path": "loras/ipadapter",
|
||||
"description": "IP-Adapter-FaceID-Plus V2 LoRA Model (SD1.5)",
|
||||
"reference": "https://huggingface.co/h94/IP-Adapter-FaceID",
|
||||
"filename": "ip-adapter-faceid-plusv2_sd15_lora.safetensors",
|
||||
"url": "https://huggingface.co/h94/IP-Adapter-FaceID/resolve/main/ip-adapter-faceid-plusv2_sd15_lora.safetensors"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "ControlNet-HandRefiner-pruned (inpaint-depth-hand; fp16)",
|
||||
"type": "controlnet",
|
||||
@ -23,7 +44,7 @@
|
||||
},
|
||||
{
|
||||
"name": "stabilityai/stable-diffusion-x4-upscaler",
|
||||
"type": "checkpoint",
|
||||
"type": "checkpoints",
|
||||
"base": "upscale",
|
||||
"save_path": "checkpoints/upscale",
|
||||
"description": "[3.53GB] This upscaling model is a latent text-guided diffusion model and should be used with SD_4XUpscale_Conditioning and KSampler.",
|
||||
@ -127,7 +148,7 @@
|
||||
|
||||
{
|
||||
"name": "Segmind-Vega",
|
||||
"type": "checkpoint",
|
||||
"type": "checkpoints",
|
||||
"base": "segmind-vega",
|
||||
"save_path": "checkpoints/segmind-vega",
|
||||
"description": "The Segmind-Vega Model is a distilled version of the Stable Diffusion XL (SDXL), offering a remarkable 70% reduction in size and an impressive 100% speedup while retaining high-quality text-to-image generation capabilities.",
|
||||
@ -676,47 +697,6 @@
|
||||
"reference": "https://huggingface.co/h94/IP-Adapter",
|
||||
"filename": "pytorch_model.bin",
|
||||
"url": "https://huggingface.co/h94/IP-Adapter/resolve/main/sdxl_models/image_encoder/pytorch_model.bin"
|
||||
},
|
||||
|
||||
{
|
||||
"name": "stabilityai/control-lora-canny-rank128.safetensors",
|
||||
"type": "controlnet",
|
||||
"base": "SDXL",
|
||||
"save_path": "default",
|
||||
"description": "Control-LoRA: canny rank128",
|
||||
"reference": "https://huggingface.co/stabilityai/control-lora",
|
||||
"filename": "control-lora-canny-rank128.safetensors",
|
||||
"url": "https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank128/control-lora-canny-rank128.safetensors"
|
||||
},
|
||||
{
|
||||
"name": "stabilityai/control-lora-depth-rank128.safetensors",
|
||||
"type": "controlnet",
|
||||
"base": "SDXL",
|
||||
"save_path": "default",
|
||||
"description": "Control-LoRA: depth rank128",
|
||||
"reference": "https://huggingface.co/stabilityai/control-lora",
|
||||
"filename": "control-lora-depth-rank128.safetensors",
|
||||
"url": "https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank128/control-lora-depth-rank128.safetensors"
|
||||
},
|
||||
{
|
||||
"name": "stabilityai/control-lora-recolor-rank128.safetensors",
|
||||
"type": "controlnet",
|
||||
"base": "SDXL",
|
||||
"save_path": "default",
|
||||
"description": "Control-LoRA: recolor rank128",
|
||||
"reference": "https://huggingface.co/stabilityai/control-lora",
|
||||
"filename": "control-lora-recolor-rank128.safetensors",
|
||||
"url": "https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank128/control-lora-recolor-rank128.safetensors"
|
||||
},
|
||||
{
|
||||
"name": "stabilityai/control-lora-sketch-rank128-metadata.safetensors",
|
||||
"type": "controlnet",
|
||||
"base": "SDXL",
|
||||
"save_path": "default",
|
||||
"description": "Control-LoRA: sketch rank128 metadata",
|
||||
"reference": "https://huggingface.co/stabilityai/control-lora",
|
||||
"filename": "control-lora-sketch-rank128-metadata.safetensors",
|
||||
"url": "https://huggingface.co/stabilityai/control-lora/resolve/main/control-LoRAs-rank128/control-lora-sketch-rank128-metadata.safetensors"
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@ -29,6 +29,46 @@
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:ElGogh Positive Prompt, ElGogh NEGATIVE Prompt, ElGogh Empty Latent Image, ElGogh Checkpoint Loader Simple"
|
||||
},
|
||||
{
|
||||
"author": "LarryJane491",
|
||||
"title": "Custom-Node-Base",
|
||||
"reference": "https://github.com/LarryJane491/Custom-Node-Base",
|
||||
"files": [
|
||||
"https://github.com/LarryJane491/Custom-Node-Base"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "This project is an `empty` custom node that is already in its own folder. It serves as a base to build any custom node. Whenever you want to create a custom node, you can download that, put it in custom_nodes, then you just have to change the names and fill it with code!"
|
||||
},
|
||||
{
|
||||
"author": "foxtrot-roger",
|
||||
"title": "comfyui-custom-nodes",
|
||||
"reference": "https://github.com/foxtrot-roger/comfyui-custom-nodes",
|
||||
"files": [
|
||||
"https://github.com/foxtrot-roger/comfyui-custom-nodes"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Tutorial nodes"
|
||||
},
|
||||
{
|
||||
"author": "GraftingRayman",
|
||||
"title": "ComfyUI-Trajectory",
|
||||
"reference": "https://github.com/GraftingRayman/ComfyUI-Trajectory",
|
||||
"files": [
|
||||
"https://github.com/GraftingRayman/ComfyUI-Trajectory"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:GR Trajectory"
|
||||
},
|
||||
{
|
||||
"author": "wailovet",
|
||||
"title": "ComfyUI-WW",
|
||||
"reference": "https://github.com/wailovet/ComfyUI-WW",
|
||||
"files": [
|
||||
"https://github.com/wailovet/ComfyUI-WW"
|
||||
],
|
||||
"install_type": "git-clone",
|
||||
"description": "Nodes:WW_ImageResize"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -135,7 +135,7 @@ try:
|
||||
is_start_mode = True
|
||||
is_import_fail_mode = False
|
||||
|
||||
log_file = open(f"comfyui{postfix}.log", "w", encoding="utf-8")
|
||||
log_file = open(f"comfyui{postfix}.log", "w", encoding="utf-8", errors="ignore")
|
||||
log_lock = threading.Lock()
|
||||
|
||||
class ComfyUIManagerLogger:
|
||||
@ -338,7 +338,7 @@ if os.path.exists(restore_snapshot_path):
|
||||
cmd_str = [sys.executable, git_script_path, '--apply-snapshot', restore_snapshot_path]
|
||||
exit_code = process_wrap(cmd_str, custom_nodes_path, handler=msg_capture)
|
||||
|
||||
with open(restore_snapshot_path, 'r', encoding="UTF-8") as json_file:
|
||||
with open(restore_snapshot_path, 'r', encoding="UTF-8", errors="ignore") as json_file:
|
||||
info = json.load(json_file)
|
||||
for url in cloned_repos:
|
||||
try:
|
||||
@ -352,7 +352,7 @@ if os.path.exists(restore_snapshot_path):
|
||||
this_exit_code = 0
|
||||
|
||||
if os.path.exists(requirements_path):
|
||||
with open(requirements_path, 'r', encoding="UTF-8") as file:
|
||||
with open(requirements_path, 'r', encoding="UTF-8", errors="ignore") as file:
|
||||
for line in file:
|
||||
package_name = line.strip()
|
||||
if package_name and not is_installed(package_name):
|
||||
@ -413,7 +413,7 @@ if os.path.exists(script_list_path):
|
||||
|
||||
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") as file:
|
||||
with open(script_list_path, 'r', encoding="UTF-8", errors="ignore") as file:
|
||||
for line in file:
|
||||
if line in executed:
|
||||
continue
|
||||
@ -456,3 +456,12 @@ if os.path.exists(script_list_path):
|
||||
|
||||
del processed_install
|
||||
del pip_list
|
||||
|
||||
if platform.system() == 'Windows':
|
||||
try:
|
||||
import asyncio
|
||||
import asyncio.windows_events
|
||||
asyncio.set_event_loop_policy(asyncio.windows_events.WindowsSelectorEventLoopPolicy())
|
||||
print(f"[ComfyUI-Manager] Windows event loop policy mode enabled")
|
||||
except Exception as e:
|
||||
print(f"[ComfyUI-Manager] WARN: Windows initialization fail: {e}")
|
||||
Loading…
Reference in New Issue
Block a user