Compare commits

..
Author SHA1 Message Date
BennyKok a557788e70 fix 2025-05-11 10:57:07 +08:00
BennyKok 05cccaffa2 support for API_KEY_COMFY_ORG 2025-05-11 10:41:10 +08:00
KarrixLee 85af9dd68f Test (#91)
* feat: enhance apply_random_seed_to_workflow function to support KSampler node type and handle fixed seed settings

* refactor: add flag to skip randomization in apply_random_seed_to_workflow for KSampler nodes

* tweak
2025-05-10 13:13:02 +08:00
KarrixLee 233615ea25 Karrix/external seed (#90)
* feat: add ComfyUIDeployExternalSeed node for generating random seeds with configurable limits

* refactor: update ComfyUIDeployExternalSeed to use control options for seed generation

* feat: add default_value input for ComfyUIDeployExternalSeed node to enhance seed configuration

* test

* refactor: rename lower_limit and upper_limit to min_value and max_value in ComfyUIDeployExternalSeed for clarity

* refactor: update default_value handling and control options in ComfyUIDeployExternalSeed for improved seed generation logic

* refactor: enhance seed generation logic in ComfyUIDeployExternalSeed by refining control handling and default_value checks

* refactor: update description for default_value in ComfyUIDeployExternalSeed to clarify usage and behavior
2025-05-10 03:39:27 +08:00
KarrixLee 8b6aabbfaa feat: add support for 'result' file type in upload process 2025-05-07 21:42:13 +08:00
Nick Kao c72539078c Merge pull request #89 from BennyKok/nick/private-lora-download
feat: add bearer token param in external lora
2025-05-03 10:28:30 -07:00
KarrixLee 95fc642782 Karrix/fix preview image upload queue (#88)
* refactor: update upload completion logic to rely on UploadQueue worker for final SUCCESS status

* refactor: clean up commented-out code in upload completion logic
2025-04-28 15:56:58 +08:00
KarrixLee d88ca5b748 refactor: rename 'text' to 'text_file' for consistency in file handling 2025-04-25 20:03:02 +08:00
KarrixLee e86a484160 refactor: improve logging format and add validation for file_info in upload process 2025-04-25 13:40:38 +08:00
KarrixLee 5d85bfd38f fix: ensure temp file check handles non-dictionary items 2025-04-25 13:29:34 +08:00
BennyKok 77eb9f9805 Merge branch 'benny/fix-preview-image-stuck' 2025-04-24 15:29:16 +08:00
KarrixLee 266e9d1024 refactor: enhance audio loading with error handling and import checks 2025-04-23 13:36:01 +08:00
KarrixLee dc7234640c tweak 2025-04-22 18:30:34 +08:00
KarrixLee 4e8417d501 add: output text node 2025-04-22 18:15:44 +08:00
BennyKok 62609f9c6a Benny/fix preview image stuck (#87)
* Fix preview image loading and add support for 3D model uploads

* skip: sending the upload status, make sure, we are also sending the success status after all files is uploaded
2025-04-22 12:59:20 +08:00
BennyKok 438401b8c7 fix: external enum node value replace 2025-04-22 10:27:55 +08:00
BennyKok 9beac36d0f skip: sending the upload status, make sure, we are also sending the success status after all files is uploaded 2025-04-21 14:18:18 +08:00
BennyKok 313ce956fd Fix preview image loading and add support for 3D model uploads 2025-04-21 14:08:56 +08:00
BennyKok e18d980b77 Benny/fix 3d upload (#85)
* fix 3d upload with subfolder support and enhanced logging

* Use parent folder name as subfolder for model file uploads
2025-04-20 22:54:51 +08:00
bennykok fe2d9b82b5 fix: extra options form enum list for some node 2025-04-16 17:09:51 +08:00
bennykok 22858abb31 feat: add external enum node 2025-04-16 16:18:56 +08:00
bennykok 99a5f71b2c feat: add external enum node 2025-04-16 11:52:20 +08:00
bennykok 1810d4ecdb feat: improvement to convert external inputs system 2025-04-16 01:12:28 +08:00
bennykok 3593f0b37e fix: convert external inputs layout issues 2025-04-16 00:10:39 +08:00
bennykok 858a3bcda4 fix rendering problems 2025-04-14 18:08:42 +08:00
bennykok 484147f5b4 fix convert shortcut for string type 2025-04-14 17:59:57 +08:00
Vivek 61a8f1123e fixed a minor issue related to external inputs (#84) 2025-04-14 17:56:27 +08:00
EmmanuelMr18 46b056290d fix(workflows): correct typo in masks example workflow name 2025-04-07 01:31:17 -06:00
EmmanuelMr18 2cf0497823 chore(workflows): convert example workflow previews to JPG format 2025-04-07 01:27:01 -06:00
EmmanuelMr18 4b5eec4e4c feat(workflows): add example workflows for ComfyUI templates 2025-04-07 01:20:46 -06:00
Emmanuel Morales 17c48c7d4f chore: remove model_list custom node
This node was created just for an experiment in the previous year, but never was a core feature.

I'm removing it because looks that is braking the import in local machines because i'm seeing this error locally:

```
  File "F:\ComfyUI\custom_nodes\comfyui-deploy\comfy-nodes\model_list.py", line 35, in <module>
    allModels = fetch_files("./models")
                ^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\ComfyUI\custom_nodes\comfyui-deploy\comfy-nodes\model_list.py", line 23, in fetch_files
    fs.extend(fetch_files(f"{dirpath}/{dirname}"))
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "F:\ComfyUI\custom_nodes\comfyui-deploy\comfy-nodes\model_list.py", line 23, in fetch_files
    fs.extend(fetch_files(f"{dirpath}/{dirname}"))
TypeError: 'NoneType' object is not iterable

Cannot import F:\ComfyUI\custom_nodes\comfyui-deploy module for custom nodes: 'NoneType' object is not iterable
```


I'm not sure why, previously was working but was a long time ago and this node was just an experiment, so i'm deleting it
2025-04-05 21:28:37 -06:00
BennyKok cd3a2ff547 Update custom_routes.py
random seed for XlabsSampler
2025-04-03 16:58:49 +02:00
bennykok 62f8e388bb update version and toml file 2025-04-01 12:56:55 +02:00
Robin Huangandsnomiao f86c08baed chore(publish): update GitHub Actions workflow for node publishing (#83)
- Add permissions to allow issue writing
- Update action version from `main` to `v1`
- Add condition to run job only for 'BennyKok' repository owner

Co-authored-by: snomiao <snomiao+comfy-pr@gmail.com>
2025-04-01 12:55:27 +02:00
bennykok 7f64bcc3ae fix: ensure validate workflow 2025-03-31 11:46:58 +02:00
bennykok 8359d1c783 reenable ws event 2025-03-30 22:28:44 +02:00
bennykok dca27fe2ba fix: random seed for sonic node 2025-03-29 09:22:11 +01:00
BennyKok 0ae70835c4 remove sending ws event, since its not used. 2025-03-27 00:04:34 +08:00
BennyKok 99cda529a9 chore: clearer log when request time out 2025-03-26 23:55:47 +08:00
bennykok 7e640a0da4 bump version 2025-03-24 09:44:00 +08:00
BennyKok f19b80a25d Update README.md
Removed the videos since its outdated
2025-03-16 10:31:00 +08:00
karrix 1ba7640d7f refactor: image output id optional 2025-03-13 21:15:14 +08:00
Nick Kao 6ecf9c782b Merge pull request #81 from Jeremy8776/main
Fix: Organised Node List
2025-03-07 10:26:46 -08:00
Jeremy 1904b4bdbf Fix: Organised Node List
All CD nodes under one group in the node list.

Not sure what subdir are wanted.
2025-03-07 18:12:56 +00:00
karrix 4112e0ec8c feat: example workflows 2025-03-06 20:11:52 +08:00
karrix 171a227856 add: 3d upload support 2025-03-02 03:55:18 +08:00
karrix 051db3c394 add: model_file 2025-03-02 02:40:31 +08:00
bennykok 757868deaf prevent duplicated output / run 2025-03-02 00:30:23 +08:00
bennykok 7b1e7afb62 change to 50ms stagger delay 2025-03-01 22:19:40 +08:00
bennykok 9f65ce72a5 remove log 2025-03-01 22:19:04 +08:00
bennykok a424e391d1 also log Node Execution Timeline as graph 2025-03-01 19:18:11 +08:00
bennykok 6add68599b feat: add upload queue, stagger upload 2025-03-01 19:02:12 +08:00
nick b3df94d1af update: don't randomize noise_seed if it's an input from another node 2025-02-24 09:44:59 -08:00
karrix 26b149553b tweak 2025-02-19 05:13:56 +08:00
karrix 17683d353c Revert "add: return output id"
This reverts commit 7552353e30.
2025-02-19 04:53:07 +08:00
karrix 7552353e30 add: return output id 2025-02-19 04:48:50 +08:00
karrix d619ad7f3b tweak 2025-02-19 04:34:26 +08:00
karrix f0ed0ad8f7 feat: output image id 2025-02-19 03:52:21 +08:00
BennyKok 7fce0b4976 Update README.md 2025-02-15 16:52:50 +08:00
ImpactFrames 0e218752ca Add external_exr.py (#79)
* Create external_exr.py

adds a node for loading exr images from url

* Update custom_routes.py

Adds EXR to the custom routes
2025-02-14 11:13:35 +08:00
bennykok 4073a43d3d use torch audio 2025-02-07 23:14:16 +08:00
bennykok 3d6a554f7f feat: add external audio node based on VHS node 2025-02-07 21:42:44 +08:00
KarrixLee ce939fbe1b add: gpu in info (#78) 2025-02-06 15:41:56 +08:00
bennykok 48f5ce15d7 fix: fallback to default api runs 2025-02-05 17:58:57 +08:00
karrix 9512437573 feat: send back event if the graph is loading properly 2025-02-05 14:41:35 +08:00
bennykok 649e431227 feat: configure_menu_buttons 2025-01-23 13:44:31 +08:00
EmmanuelMr18 411db66d81 Revert "chore: refresh models when getting object_info"
This reverts commit 67f25b2353.
2025-01-20 01:52:52 -05:00
Emmanuel Morales 67f25b2353 chore: refresh models when getting object_info
This is a WIP that will be used to refresh the models when execution comfyUI without having to stop the server and start a new one
2025-01-19 17:26:41 -06:00
bennykok ce3b0dbe84 chore: log prompt_id on start 2025-01-19 12:39:24 +08:00
bennykok fc36a8ad0f feat: add output image node 2025-01-19 12:39:06 +08:00
Robin Huangandsnomiao 638e625d72 chore(licence-update): Update PyProject Toml - License (#77)
Co-authored-by: snomiao <snomiao+comfy-pr@gmail.com>
2025-01-10 15:44:27 +08:00
EmmanuelMr18 230cee40d2 fix: add container to the buttons injected into the right menu 2025-01-10 01:08:42 -06:00
EmmanuelMr18 73853a60ff feat: inject buttons in the right position of the comfyui menu 2025-01-07 23:49:03 -06:00
bennykok 413115571b chore: add event for updating widget 2025-01-07 21:36:12 +08:00
bennykok bf00580562 feat: update external image node to have default value 2025-01-07 21:03:52 +08:00
bennykok 6ed468d7d4 feat: drag drop proxy + inject button to toolbar 2025-01-06 13:01:39 +08:00
bennykok 5423b4ee6f fix: simply js import 2025-01-05 14:00:42 +08:00
Emmanuel Morales 2c1656756d fix(updates): make updates async to avoid blocking execution (#75)
I tracked the time and takes ~200ms everytime that we send the "Executing <NODE NAME> n%".
So this means that if you have 10 custom nodes we are adding 2 extra seconds to the execution.
200 * 10 = 2,000.
Some workflows are more complext and have more custom nodes, so this only keeps increasing.
2025-01-03 16:25:04 +08:00
46 changed files with 6487 additions and 414 deletions
+6 -2
View File
@@ -7,15 +7,19 @@ on:
paths:
- "pyproject.toml"
permissions:
issues: write
jobs:
publish-node:
name: Publish Custom Node to registry
runs-on: ubuntu-latest
if: ${{ github.repository_owner == 'BennyKok' }}
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Publish Custom Node
uses: Comfy-Org/publish-node-action@main
uses: Comfy-Org/publish-node-action@v1
with:
## Add your own personal access token to your Github Repository secrets and reference it here.
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
personal_access_token: ${{ secrets.REGISTRY_ACCESS_TOKEN }}
+2 -1
View File
@@ -1,2 +1,3 @@
__pycache__
.DS_Store
.DS_Store
file-hash-cache.json
+3 -4
View File
@@ -2,6 +2,9 @@
Open source comfyui deployment platform, a `vercel` for generative workflow infra. (serverless hosted gpu with vertical intergation with comfyui)
> [!NOTE]
> Im looking for creative hacker to join ComfyDeploy's core team! DM me on [twitter](https://x.com/BennyKokMusic)
Join [Discord](https://discord.gg/EEYcQmdYZw) to chat more or visit [Comfy Deploy](https://comfydeploy.com/) to get started!
Check out our latest [nextjs starter kit](https://github.com/BennyKok/comfyui-deploy-next-example) with Comfy Deploy
@@ -93,10 +96,6 @@ Major areas
# Self Hosting with Vercel
[![Video](https://img.mytsi.org/i/nFOG479.png)](https://www.youtube.com/watch?v=hWvsEY1cS2M)
Tutorial Created by [Ross](https://github.com/rossman22590) and [Syn](https://github.com/mortlsyn)
Build command
```
+37 -1
View File
@@ -2,8 +2,9 @@
@author: BennyKok
@title: comfyui-deploy
@nickname: Comfy Deploy
@description:
@description:
"""
import os
import sys
@@ -17,19 +18,23 @@ import requests
import folder_paths
from folder_paths import add_model_folder_path, get_filename_list, get_folder_paths
from tqdm import tqdm
import re
from . import custom_routes
# import routes
ag_path = os.path.join(os.path.dirname(__file__))
def get_python_files(path):
return [f[:-3] for f in os.listdir(path) if f.endswith(".py")]
def append_to_sys_path(path):
if path not in sys.path:
sys.path.append(path)
paths = ["comfy-nodes"]
files = []
@@ -41,14 +46,45 @@ for path in paths:
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
def split_camel_case(name):
# Split on underscores first, then split each part on camelCase
parts = []
for part in name.split("_"):
# Find all camelCase boundaries
words = re.findall("[A-Z][^A-Z]*", part)
if not words: # If no camelCase found, use the whole part
words = [part]
parts.extend(words)
return parts
# Import all the modules and append their mappings
for file in files:
module = importlib.import_module(file)
# Check if the module has explicit mappings
if hasattr(module, "NODE_CLASS_MAPPINGS"):
NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS)
if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS"):
NODE_DISPLAY_NAME_MAPPINGS.update(module.NODE_DISPLAY_NAME_MAPPINGS)
# Auto-discover classes with ComfyUI node attributes
for name, obj in inspect.getmembers(module):
# Check if it's a class and has the required ComfyUI node attributes
if (
inspect.isclass(obj)
and hasattr(obj, "INPUT_TYPES")
and hasattr(obj, "RETURN_TYPES")
):
# Use the class name as the key if not already in mappings
if name not in NODE_CLASS_MAPPINGS:
NODE_CLASS_MAPPINGS[name] = obj
# Create a display name by converting camelCase to Title Case with spaces
words = split_camel_case(name.replace("ComfyUIDeploy", ""))
display_name = " ".join(word.capitalize() for word in words)
# print(display_name, name)
NODE_DISPLAY_NAME_MAPPINGS[name] = display_name
WEB_DIRECTORY = "web-plugin"
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
+82
View File
@@ -0,0 +1,82 @@
import io
from folder_paths import get_annotated_filepath
class ComfyUIDeployExternalAudio:
RETURN_TYPES = ("AUDIO",)
RETURN_NAMES = ("audio",)
FUNCTION = "load_audio"
CATEGORY = "🔗ComfyDeploy"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_id": (
"STRING",
{"multiline": False, "default": "input_audio"},
),
"audio_file": ("STRING", {"default": ""}),
},
"optional": {
"default_value": ("AUDIO",),
"display_name": (
"STRING",
{"multiline": False, "default": ""},
),
"description": (
"STRING",
{"multiline": False, "default": ""},
),
},
}
@classmethod
def VALIDATE_INPUTS(s, audio_file, **kwargs):
return True
def load_audio(
self,
input_id,
audio_file,
default_value=None,
display_name=None,
description=None,
):
try:
import torchaudio
if audio_file and audio_file != "":
if audio_file.startswith(("http://", "https://")):
# Handle URL input
try:
import requests
response = requests.get(audio_file)
audio_data = io.BytesIO(response.content)
waveform, sample_rate = torchaudio.load(audio_data)
except Exception as e:
print(f"Error loading audio from URL: {e}")
return (default_value,)
else:
# Handle local file
try:
audio_path = get_annotated_filepath(audio_file)
waveform, sample_rate = torchaudio.load(audio_path)
except Exception as e:
print(f"Error loading local audio file: {e}")
return (default_value,)
audio = {"waveform": waveform.unsqueeze(0), "sample_rate": sample_rate}
return (audio,)
else:
return (default_value,)
except ImportError as e:
print(f"Error: torchaudio not installed or cannot be imported: {e}")
return (default_value,)
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalAudio": ComfyUIDeployExternalAudio}
NODE_DISPLAY_NAME_MAPPINGS = {
"ComfyUIDeployExternalAudio": "External Audio (ComfyUI Deploy)"
}
+2 -1
View File
@@ -23,8 +23,9 @@ class ComfyUIDeployExternalBoolean:
RETURN_TYPES = ("BOOLEAN",)
RETURN_NAMES = ("bool_value",)
FUNCTION = "run"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, default_value=None, display_name=None, description=None):
print(f"Node '{input_id}' processing with switch set to {default_value}")
+2 -1
View File
@@ -36,10 +36,11 @@ class ComfyUIDeployExternalCheckpoint:
RETURN_TYPES = (WILDCARD,)
RETURN_NAMES = ("path",)
FUNCTION = "run"
CATEGORY = "deploy"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, default_value=None, display_name=None, description=None):
import requests
+46
View File
@@ -0,0 +1,46 @@
class AnyType(str):
def __ne__(self, __value: object) -> bool:
return False
WILDCARD = AnyType("*")
class ComfyUIDeployExternalEnum:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_id": (
"STRING",
{"multiline": False, "default": "input_enum"},
),
},
"optional": {
"default_value": (
"STRING",
{"multiline": False, "default": "", "dynamic_enum": True},
),
"options": (
"STRING",
{"multiline": True, "default": ""},
),
"display_name": (
"STRING",
{"multiline": False, "default": ""},
),
"description": (
"STRING",
{"multiline": True, "default": ""},
),
}
}
RETURN_TYPES = (WILDCARD,)
RETURN_NAMES = ("text",)
FUNCTION = "run"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, options=None, default_value=None, display_name=None, description=None):
return [default_value]
+110
View File
@@ -0,0 +1,110 @@
import os
import io
import cv2 as cv
import numpy as np
import torch
import requests
from folder_paths import get_annotated_filepath
class ComfyUIDeployExternalEXR:
RETURN_TYPES = ("IMAGE", "MASK")
RETURN_NAMES = ("image", "mask")
FUNCTION = "load_exr"
CATEGORY = "🔗ComfyDeploy"
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"input_id": (
"STRING",
{"multiline": False, "default": "input_exr"},
),
"exr_file": ("STRING", {"default": ""}),
"tonemap": (["linear", "sRGB", "Reinhard"], {"default": "sRGB"}),
},
"optional": {
"default_image": ("IMAGE",),
"default_mask": ("MASK",),
"display_name": (
"STRING",
{"multiline": False, "default": ""},
),
"description": (
"STRING",
{"multiline": False, "default": ""},
),
}
}
@classmethod
def VALIDATE_INPUTS(s, exr_file, **kwargs):
return True
def sRGBtoLinear(self, npArray):
less = npArray <= 0.0404482362771082
npArray[less] = npArray[less] / 12.92
npArray[~less] = np.power((npArray[~less] + 0.055) / 1.055, 2.4)
def linearToSRGB(self, npArray):
less = npArray <= 0.0031308
npArray[less] = npArray[less] * 12.92
npArray[~less] = np.power(npArray[~less], 1/2.4) * 1.055 - 0.055
def load_exr(self, input_id, exr_file, tonemap="sRGB",
default_image=None, default_mask=None,
display_name=None, description=None):
try:
if exr_file and exr_file != "":
if exr_file.startswith(('http://', 'https://')):
# Handle URL input
response = requests.get(exr_file)
# Write to temp buffer
buffer = io.BytesIO(response.content)
nparr = np.frombuffer(buffer.getvalue(), np.uint8)
image = cv.imdecode(nparr, cv.IMREAD_UNCHANGED).astype(np.float32)
else:
# Handle local file
exr_path = get_annotated_filepath(exr_file)
image = cv.imread(exr_path, cv.IMREAD_UNCHANGED).astype(np.float32)
if len(image.shape) == 2:
image = np.repeat(image[..., np.newaxis], 3, axis=2)
# Extract RGB and flip channels
rgb = np.flip(image[:,:,:3], 2).copy()
# Apply tonemapping
if tonemap == "sRGB":
self.linearToSRGB(rgb)
rgb = np.clip(rgb, 0, 1)
elif tonemap == "Reinhard":
rgb = np.clip(rgb, 0, None)
rgb = rgb / (rgb + 1)
self.linearToSRGB(rgb)
rgb = np.clip(rgb, 0, 1)
rgb = torch.unsqueeze(torch.from_numpy(rgb), 0)
# Handle alpha/mask
mask = torch.zeros((1, image.shape[0], image.shape[1]), dtype=torch.float32)
if image.shape[2] > 3:
mask[0] = torch.from_numpy(np.clip(image[:,:,3], 0, 1))
return (rgb, mask)
else:
# Return defaults if no file provided
return (default_image, default_mask)
except Exception as e:
print(f"Error loading EXR: {str(e)}")
# Return defaults on error
return (default_image, default_mask)
NODE_CLASS_MAPPINGS = {
"ComfyUIDeployExternalEXR": ComfyUIDeployExternalEXR
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ComfyUIDeployExternalEXR": "External EXR (ComfyUI Deploy)"
}
+1 -3
View File
@@ -48,10 +48,8 @@ class ComfyUIDeployExternalFaceModel:
RETURN_TYPES = (WILDCARD,)
RETURN_NAMES = ("path",)
FUNCTION = "run"
CATEGORY = "deploy"
CATEGORY = "🔗ComfyDeploy"
def run(
self,
+40 -29
View File
@@ -21,44 +21,55 @@ class ComfyUIDeployExternalImage:
),
"description": (
"STRING",
{"multiline": True, "default": ""},
{"multiline": False, "default": ""},
),
"default_value_url": ("STRING", {"image_preview": True, "default": ""}),
}
}
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "run"
CATEGORY = "🔗ComfyDeploy"
CATEGORY = "image"
def run(self, input_id, default_value=None, display_name=None, description=None):
def run(self, input_id, default_value=None, display_name=None, description=None, default_value_url=None):
image = default_value
try:
if input_id.startswith('http'):
import requests
from io import BytesIO
print("Fetching image from url: ", input_id)
response = requests.get(input_id)
image = Image.open(BytesIO(response.content))
elif input_id.startswith('data:image/png;base64,') or input_id.startswith('data:image/jpeg;base64,') or input_id.startswith('data:image/jpg;base64,'):
import base64
from io import BytesIO
print("Decoding base64 image")
base64_image = input_id[input_id.find(",")+1:]
decoded_image = base64.b64decode(base64_image)
image = Image.open(BytesIO(decoded_image))
else:
raise ValueError("Invalid image url provided.")
image = ImageOps.exif_transpose(image)
image = image.convert("RGB")
image = np.array(image).astype(np.float32) / 255.0
image = torch.from_numpy(image)[None,]
return [image]
except:
return [image]
# Try both input_id and default_value_url
urls_to_try = [url for url in [input_id, default_value_url] if url]
print(default_value_url)
for url in urls_to_try:
try:
if url.startswith('http'):
import requests
from io import BytesIO
print(f"Fetching image from url: {url}")
response = requests.get(url)
image = Image.open(BytesIO(response.content))
break
elif url.startswith(('data:image/png;base64,', 'data:image/jpeg;base64,', 'data:image/jpg;base64,')):
import base64
from io import BytesIO
print("Decoding base64 image")
base64_image = url[url.find(",")+1:]
decoded_image = base64.b64decode(base64_image)
image = Image.open(BytesIO(decoded_image))
break
except:
continue
if image is not None:
try:
image = ImageOps.exif_transpose(image)
image = image.convert("RGB")
image = np.array(image).astype(np.float32) / 255.0
image = torch.from_numpy(image)[None,]
except:
pass
return [image]
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalImage": ComfyUIDeployExternalImage}
+1 -3
View File
@@ -28,10 +28,8 @@ class ComfyUIDeployExternalImageAlpha:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "run"
CATEGORY = "image"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, default_value=None, display_name=None, description=None):
image = default_value
+1 -3
View File
@@ -34,10 +34,8 @@ class ComfyUIDeployExternalImageBatch:
RETURN_TYPES = ("IMAGE",)
RETURN_NAMES = ("image",)
FUNCTION = "run"
CATEGORY = "image"
CATEGORY = "🔗ComfyDeploy"
def process_image(self, image):
image = ImageOps.exif_transpose(image)
+11 -8
View File
@@ -1,8 +1,4 @@
import folder_paths
from PIL import Image, ImageOps
import numpy as np
import torch
import folder_paths
class AnyType(str):
@@ -41,15 +37,17 @@ class ComfyUIDeployExternalLora:
"STRING",
{"multiline": False, "default": ""},
),
"bearer_token": (
"STRING",
{"multiline": False, "default": ""},
),
},
}
RETURN_TYPES = (WILDCARD,)
RETURN_NAMES = ("path",)
FUNCTION = "run"
CATEGORY = "deploy"
CATEGORY = "🔗ComfyDeploy"
def run(
self,
@@ -59,6 +57,7 @@ class ComfyUIDeployExternalLora:
display_name=None,
description=None,
lora_url=None,
bearer_token=None,
):
import requests
import os
@@ -86,9 +85,13 @@ class ComfyUIDeployExternalLora:
+ " to "
+ destination_path
)
headers = {"User-Agent": "Mozilla/5.0"}
if bearer_token:
headers["Authorization"] = f"Bearer {bearer_token}"
print("using bearer token")
response = requests.get(
lora_url,
headers={"User-Agent": "Mozilla/5.0"},
headers=headers,
allow_redirects=True,
)
with open(destination_path, "wb") as out_file:
+1 -3
View File
@@ -31,10 +31,8 @@ class ComfyUIDeployExternalNumber:
RETURN_TYPES = ("FLOAT",)
RETURN_NAMES = ("value",)
FUNCTION = "run"
CATEGORY = "number"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, default_value=None, display_name=None, description=None):
try:
+1 -3
View File
@@ -31,10 +31,8 @@ class ComfyUIDeployExternalNumberInt:
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("value",)
FUNCTION = "run"
CATEGORY = "number"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, default_value=None, display_name=None, description=None):
if not input_id or (isinstance(input_id, str) and not input_id.strip().isdigit()):
+1 -3
View File
@@ -34,10 +34,8 @@ class ComfyUIDeployExternalNumberSlider:
RETURN_TYPES = ("FLOAT",)
RETURN_NAMES = ("value",)
FUNCTION = "run"
CATEGORY = "number"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, default_value=None, min_value=0, max_value=1, display_name=None, description=None):
try:
+116
View File
@@ -0,0 +1,116 @@
import random
class ComfyUIDeployExternalSeed:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"input_id": (
"STRING",
{"multiline": False, "default": "input_seed"},
),
"default_value": (
"INT",
{"default": -1},
),
"min_value": (
"INT",
{"default": 1, "min": 1, "max": 999999999999999},
),
"max_value": (
"INT",
{"default": 4294967295, "min": 1, "max": 999999999999999},
),
},
"optional": {
"display_name": (
"STRING",
{"multiline": False, "default": ""},
),
"description": (
"STRING",
{
"multiline": True,
"default": 'For default value:\n"-1" (i.e. not in range): Randomize within the min and max value range. \nin range: Fixed, always the same value\n',
},
),
},
}
RETURN_TYPES = ("INT",)
RETURN_NAMES = ("seed",)
FUNCTION = "run"
CATEGORY = "🔗ComfyDeploy"
# Limits
_MAX_LIMIT = 999_999_999_999_999 # 15 digits
# Store cached seed when fixed flag is enabled
_cached_seed = None
@classmethod
def IS_CHANGED(
cls,
input_id,
min_value,
max_value,
default_value=None,
**kwargs,
):
"""Inform ComfyUI whether the node output should be considered changed.
If default_value is within range (Fixed mode), we return the inputs tuple
so the cached result is reused until the user changes something.
For Randomize mode, we force re-execution each queue.
"""
# Clamp values to allowed range for check
min_value = max(1, min_value)
max_value = min(cls._MAX_LIMIT, max_value)
# Fixed mode when default_value is within range
if (
default_value is not None
and default_value >= min_value
and default_value <= max_value
):
return (input_id, default_value)
# For Randomize (default_value is -1 or out of range) we force re-execution
import random as _rnd
return _rnd.random()
def run(
self,
input_id,
min_value: int,
max_value: int,
display_name=None,
description=None,
default_value: int = -1,
):
# Clamp values to allowed range
min_value = max(1, min_value)
max_value = min(self._MAX_LIMIT, max_value)
# Ensure limits are in correct order after clamping
if min_value > max_value:
min_value, max_value = max_value, min_value
# Fixed mode: default_value is within range
if default_value >= min_value and default_value <= max_value:
seed = int(default_value)
self._cached_seed = seed
return [seed]
# Randomize mode: default_value is -1 or out of range
seed = random.randint(min_value, max_value)
self._cached_seed = seed
return [seed]
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalSeed": ComfyUIDeployExternalSeed}
NODE_DISPLAY_NAME_MAPPINGS = {
"ComfyUIDeployExternalSeed": "External Seed (ComfyUI Deploy)"
}
+1 -1
View File
@@ -18,7 +18,7 @@ class StringFunction:
RETURN_TYPES = ("STRING",)
FUNCTION = "exec"
CATEGORY = "utils"
CATEGORY = "🔗ComfyDeploy"
OUTPUT_NODE = True
def exec(self, action, tidy_tags, text_a="", text_b="", text_c=""):
+1 -1
View File
@@ -34,7 +34,7 @@ class ComfyUIDeployExternalText:
FUNCTION = "run"
CATEGORY = "text"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, default_value=None, display_name=None, description=None):
return [default_value]
+1 -1
View File
@@ -36,7 +36,7 @@ class ComfyUIDeployExternalTextAny:
FUNCTION = "run"
CATEGORY = "text"
CATEGORY = "🔗ComfyDeploy"
def run(self, input_id, default_value=None, display_name=None, description=None):
return [default_value]
+1
View File
@@ -36,6 +36,7 @@ class ComfyUIDeployExternalVideo:
RETURN_NAMES = ("video")
FUNCTION = "load_video"
CATEGORY = "🔗ComfyDeploy"
def load_video(self, input_id, default_value):
input_dir = folder_paths.get_input_directory()
+1
View File
@@ -791,6 +791,7 @@ class ComfyUIDeployExternalVideo:
)
FUNCTION = "load_video"
CATEGORY = "🔗ComfyDeploy"
def load_video(self, **kwargs):
input_id = kwargs.get("input_id")
+1
View File
@@ -33,6 +33,7 @@ class ComfyDeployWebscoketImageInput:
RETURN_NAMES = ("images",)
FUNCTION = "run"
CATEGORY = "🔗ComfyDeploy"
@classmethod
def VALIDATE_INPUTS(s, input_id):
-60
View File
@@ -1,60 +0,0 @@
import folder_paths
class AnyType(str):
def __ne__(self, __value: object) -> bool:
return False
from os import walk
WILDCARD = AnyType("*")
MODEL_EXTENSIONS = {
"safetensors": "SafeTensors file format",
"ckpt": "Checkpoint file",
"pth": "PyTorch serialized file",
"pkl": "Pickle file",
"onnx": "ONNX file",
}
def fetch_files(path):
for (dirpath, dirnames, filenames) in walk(path):
fs = []
if len(dirnames) > 0:
for dirname in dirnames:
fs.extend(fetch_files(f"{dirpath}/{dirname}"))
for filename in filenames:
# Remove "./models/" from the beginning of dirpath
relative_dirpath = dirpath.replace("./models/", "", 1)
file_path = f"{relative_dirpath}/{filename}"
# Only add files that are known model extensions
file_extension = filename.split('.')[-1].lower()
if file_extension in MODEL_EXTENSIONS:
fs.append(file_path)
return fs
allModels = fetch_files("./models")
class ComfyUIDeployModalList:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"model": (allModels, ),
}
}
RETURN_TYPES = (WILDCARD,)
RETURN_NAMES = ("model",)
FUNCTION = "run"
CATEGORY = "model"
def run(self, model=""):
# Split the model path by '/' and select the last item
model_name = model.split('/')[-1]
return [model_name]
NODE_CLASS_MAPPINGS = {"ComfyUIDeployModelList": ComfyUIDeployModalList}
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyUIDeployModelList": "Model List (ComfyUI Deploy)"}
+101
View File
@@ -0,0 +1,101 @@
import os
import json
import numpy as np
from PIL import Image
from PIL.PngImagePlugin import PngInfo
import folder_paths
class ComfyDeployOutputImage:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
self.type = "output"
self.prefix_append = ""
self.compress_level = 4
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"images": ("IMAGE", {"tooltip": "The images to save."}),
"filename_prefix": (
"STRING",
{
"default": "ComfyUI",
"tooltip": "The prefix for the file to save. This may include formatting information such as %date:yyyy-MM-dd% or %Empty Latent Image.width% to include values from nodes.",
},
),
"file_type": (["png", "jpg", "webp"], {"default": "webp"}),
"quality": ("INT", {"default": 80, "min": 1, "max": 100, "step": 1}),
},
"optional": {
"output_id": (
"STRING",
{"multiline": False, "default": "output_images"},
),
},
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
}
RETURN_TYPES = ()
FUNCTION = "run"
OUTPUT_NODE = True
CATEGORY = "🔗ComfyDeploy"
DESCRIPTION = "Saves the input images to your ComfyUI output directory."
def run(
self,
images,
filename_prefix="ComfyUI",
file_type="png",
quality=80,
output_id="output_images",
prompt=None,
extra_pnginfo=None,
):
filename_prefix += self.prefix_append
full_output_folder, filename, counter, subfolder, filename_prefix = (
folder_paths.get_save_image_path(
filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0]
)
)
results = list()
for batch_number, image in enumerate(images):
i = 255.0 * image.cpu().numpy()
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
metadata = PngInfo()
if prompt is not None:
metadata.add_text("prompt", json.dumps(prompt))
if extra_pnginfo is not None:
for x in extra_pnginfo:
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
filename_with_batch_num = filename.replace("%batch_num%", str(batch_number))
file = f"{filename_with_batch_num}_{counter:05}_.{file_type}"
file_path = os.path.join(full_output_folder, file)
if file_type == "png":
img.save(
file_path, pnginfo=metadata, compress_level=self.compress_level
)
elif file_type == "jpg":
img.save(file_path, quality=quality, optimize=True)
elif file_type == "webp":
img.save(file_path, quality=quality)
results.append(
{
"filename": file,
"subfolder": subfolder,
"type": self.type,
"output_id": output_id,
}
)
counter += 1
return {"ui": {"images": results}}
NODE_CLASS_MAPPINGS = {"ComfyDeployOutputImage": ComfyDeployOutputImage}
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyDeployOutputImage": "Image Output (ComfyDeploy)"}
+99
View File
@@ -0,0 +1,99 @@
import os
import json
import folder_paths
class ComfyDeployOutputText:
def __init__(self):
self.output_dir = folder_paths.get_output_directory()
self.type = "output"
self.prefix_append = ""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": (
"STRING",
{
"multiline": True,
"forceInput": True,
"tooltip": "The text to save.",
},
),
"filename_prefix": (
"STRING",
{
"default": "ComfyUI",
"tooltip": "The prefix for the file to save. This may include formatting information such as %date:yyyy-MM-dd% to include values from nodes.",
},
),
"file_type": (["txt", "json", "md"], {"default": "txt"}),
},
"optional": {
"output_id": (
"STRING",
{"multiline": False, "default": "output_text"},
),
},
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
}
RETURN_TYPES = ()
FUNCTION = "run"
OUTPUT_NODE = True
CATEGORY = "🔗ComfyDeploy"
DESCRIPTION = "Saves the input text to your ComfyUI output directory."
def run(
self,
text,
filename_prefix="ComfyUI",
file_type="txt",
output_id="output_text",
prompt=None,
extra_pnginfo=None,
):
filename_prefix += self.prefix_append
# For text, we don't need dimensions, so pass 0, 0
full_output_folder, filename, counter, subfolder, filename_prefix = (
folder_paths.get_save_image_path(filename_prefix, self.output_dir, 0, 0)
)
results = list()
# Create file path
file = f"{filename}_{counter:05}_.{file_type}"
file_path = os.path.join(full_output_folder, file)
# Save the text based on file type
if file_type == "json":
try:
# Try to save as JSON if the text is valid JSON
json_data = json.loads(text) if isinstance(text, str) else text
with open(file_path, "w", encoding="utf-8") as f:
json.dump(json_data, f, indent=2)
except json.JSONDecodeError:
# Fall back to saving as plain text if not valid JSON
with open(file_path, "w", encoding="utf-8") as f:
f.write(text)
else:
# Save as plain text for txt and md
with open(file_path, "w", encoding="utf-8") as f:
f.write(text)
results.append(
{
"filename": file,
"subfolder": subfolder,
"type": self.type,
"output_id": output_id,
}
)
return {"ui": {"text_file": results}}
NODE_CLASS_MAPPINGS = {"ComfyDeployOutputText": ComfyDeployOutputText}
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyDeployOutputText": "Text Output (ComfyDeploy)"}
+1 -3
View File
@@ -33,10 +33,8 @@ class ComfyDeployWebscoketImageOutput:
RETURN_TYPES = ()
RETURN_NAMES = ("text",)
FUNCTION = "run"
CATEGORY = "output"
CATEGORY = "🔗ComfyDeploy"
@classmethod
def VALIDATE_INPUTS(s, output_id):
+989 -157
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

+152
View File
@@ -0,0 +1,152 @@
{
"id": "ed93ac94-4f26-4ed3-a57b-73cd8f4d3494",
"revision": 0,
"last_node_id": 5,
"last_link_id": 1,
"nodes": [
{
"id": 2,
"type": "LoraLoader",
"pos": [
736.646728515625,
628.3823852539062
],
"size": [
315,
126
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"name": "model",
"type": "MODEL",
"link": null
},
{
"name": "clip",
"type": "CLIP",
"link": null
},
{
"name": "lora_name",
"type": "COMBO",
"widget": {
"name": "lora_name"
},
"link": 1
}
],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": null
},
{
"name": "CLIP",
"type": "CLIP",
"links": null
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "LoraLoader"
},
"widgets_values": [
"1-292.safetensors",
1,
1
]
},
{
"id": 1,
"type": "ComfyUIDeployExternalLora",
"pos": [
299.6898498535156,
624.7929077148438
],
"size": [
400,
208
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "path",
"type": "*",
"links": [
1
]
}
],
"properties": {
"cnr_id": "comfyui-deploy",
"ver": "cd3a2ff5471828f9c840e746551592e882c05aa4",
"Node name for S&R": "ComfyUIDeployExternalLora"
},
"widgets_values": [
"input_lora",
"HyperSD\\FLUX.1\\Hyper-FLUX.1-dev-16steps-lora.safetensors",
"",
"",
"",
""
]
},
{
"id": 5,
"type": "Note",
"pos": [
302.09033203125,
401.2951965332031
],
"size": [
479.4894104003906,
161.61924743652344
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {},
"widgets_values": [
"\"External Lora\" node will let you to use different loras from the Comfy Deploy UI or even via API.\n\n- lora_url:\n url that will be used to download your LoRA model in execution time\n\n- lora_save_name:\n when we download your model, this will be saved in your private storage, \n give it a good name :D"
],
"color": "#432",
"bgcolor": "#653"
}
],
"links": [
[
1,
1,
0,
2,
2,
"COMBO"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 1.167184107045006,
"offset": [
298.431389807788,
-207.58877445762934
]
},
"VHS_latentpreview": false,
"VHS_latentpreviewrate": 0,
"VHS_MetadataImage": true,
"VHS_KeepIntermediate": true
},
"version": 0.4
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

+873
View File
@@ -0,0 +1,873 @@
{
"id": "351f402b-62f2-4f62-8a5e-0b9d3510e8f9",
"revision": 0,
"last_node_id": 29,
"last_link_id": 28,
"nodes": [
{
"id": 11,
"type": "JoinImageWithAlpha",
"pos": [
814.478271484375,
419.3052062988281
],
"size": [
264.5999755859375,
46
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 10
},
{
"name": "alpha",
"type": "MASK",
"link": 12
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
11
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "JoinImageWithAlpha"
},
"widgets_values": []
},
{
"id": 15,
"type": "PreviewImage",
"pos": [
1950,
640
],
"size": [
210,
246
],
"flags": {},
"order": 18,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 16
}
],
"outputs": [],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 10,
"type": "LoadImage",
"pos": [
467.8168640136719,
422.453857421875
],
"size": [
315,
314
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
10
]
},
{
"name": "MASK",
"type": "MASK",
"links": [
12
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"Bob-Minion-Background-PNG-Image.png",
"image",
""
]
},
{
"id": 18,
"type": "Note",
"pos": [
460.8001708984375,
263.64251708984375
],
"size": [
379.4292297363281,
88
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {},
"widgets_values": [
"Option 1: CREATE THE MASK FROM THE ALPHA CHANNEL (Useful for example to generate the background of an image)\n\nMake sure that you are using \"External Image Alpha\". \n"
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 21,
"type": "LoadImage",
"pos": [
469.57025146484375,
1506.3018798828125
],
"size": [
315,
314
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
18
]
},
{
"name": "MASK",
"type": "MASK",
"links": null
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"ComfyUI_temp_otmos_00005_.png",
"image",
""
]
},
{
"id": 25,
"type": "MaskToImage",
"pos": [
1283.1668701171875,
1579.603271484375
],
"size": [
176.39999389648438,
26
],
"flags": {},
"order": 13,
"mode": 0,
"inputs": [
{
"name": "mask",
"type": "MASK",
"link": 28
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
21
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "MaskToImage"
},
"widgets_values": []
},
{
"id": 13,
"type": "SplitImageWithAlpha",
"pos": [
1602.5518798828125,
417.59869384765625
],
"size": [
277.20001220703125,
46
],
"flags": {},
"order": 11,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 13
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
14,
25
]
},
{
"name": "MASK",
"type": "MASK",
"links": [
15,
26
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "SplitImageWithAlpha"
},
"widgets_values": []
},
{
"id": 29,
"type": "VAEEncodeForInpaint",
"pos": [
2220.89453125,
401.0885009765625
],
"size": [
340.20001220703125,
98
],
"flags": {},
"order": 16,
"mode": 0,
"inputs": [
{
"name": "pixels",
"type": "IMAGE",
"link": 25
},
{
"name": "vae",
"type": "VAE",
"link": null
},
{
"name": "mask",
"type": "MASK",
"link": 26
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "VAEEncodeForInpaint"
},
"widgets_values": [
6
]
},
{
"id": 14,
"type": "PreviewImage",
"pos": [
1950,
350
],
"size": [
210,
246
],
"flags": {},
"order": 14,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 14
}
],
"outputs": [],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 17,
"type": "MaskToImage",
"pos": [
1752.33203125,
572.061767578125
],
"size": [
176.39999389648438,
26
],
"flags": {},
"order": 15,
"mode": 0,
"inputs": [
{
"name": "mask",
"type": "MASK",
"link": 15
}
],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
16
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "MaskToImage"
},
"widgets_values": []
},
{
"id": 27,
"type": "PreviewImage",
"pos": [
1329.2960205078125,
1132.759765625
],
"size": [
210,
246
],
"flags": {},
"order": 10,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 22
}
],
"outputs": [],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 20,
"type": "LoadImage",
"pos": [
468.1963806152344,
1128.9498291015625
],
"size": [
315,
314
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [],
"outputs": [
{
"name": "IMAGE",
"type": "IMAGE",
"links": [
17
]
},
{
"name": "MASK",
"type": "MASK",
"links": []
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "LoadImage"
},
"widgets_values": [
"clipspace/clipspace-mask-1126817.300000012.png [input]",
"image",
""
]
},
{
"id": 24,
"type": "ImageToMask",
"pos": [
1255.560302734375,
1468.347900390625
],
"size": [
315,
58
],
"flags": {},
"order": 9,
"mode": 0,
"inputs": [
{
"name": "image",
"type": "IMAGE",
"link": 19
}
],
"outputs": [
{
"name": "MASK",
"type": "MASK",
"links": [
24,
28
]
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "ImageToMask"
},
"widgets_values": [
"red"
]
},
{
"id": 26,
"type": "PreviewImage",
"pos": [
1488.1925048828125,
1579.146728515625
],
"size": [
210,
246
],
"flags": {},
"order": 17,
"mode": 0,
"inputs": [
{
"name": "images",
"type": "IMAGE",
"link": 21
}
],
"outputs": [],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
},
{
"id": 28,
"type": "VAEEncodeForInpaint",
"pos": [
1637.603271484375,
1182.81298828125
],
"size": [
340.20001220703125,
98
],
"flags": {},
"order": 12,
"mode": 0,
"inputs": [
{
"name": "pixels",
"type": "IMAGE",
"link": 23
},
{
"name": "vae",
"type": "VAE",
"link": null
},
{
"name": "mask",
"type": "MASK",
"link": 24
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": null
}
],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.3.27",
"Node name for S&R": "VAEEncodeForInpaint"
},
"widgets_values": [
6
]
},
{
"id": 19,
"type": "Note",
"pos": [
467.4615173339844,
985.1439819335938
],
"size": [
379.4292297363281,
88
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {},
"widgets_values": [
"Option 2: UPLOAD THE MASK AS OTHER IMAGE (useful if you require the image that is below the mask to do inpainting, in this case to generate new glasses)"
],
"color": "#432",
"bgcolor": "#653"
},
{
"id": 22,
"type": "ComfyUIDeployExternalImage",
"pos": [
910.8199462890625,
1132.306396484375
],
"size": [
390.5999755859375,
154
],
"flags": {},
"order": 7,
"mode": 0,
"inputs": [
{
"name": "default_value",
"shape": 7,
"type": "IMAGE",
"link": 17
}
],
"outputs": [
{
"name": "image",
"type": "IMAGE",
"links": [
22,
23
]
}
],
"properties": {
"cnr_id": "comfyui-deploy",
"ver": "cd3a2ff5471828f9c840e746551592e882c05aa4",
"Node name for S&R": "ComfyUIDeployExternalImage"
},
"widgets_values": [
"input_image",
"",
"",
"",
""
]
},
{
"id": 23,
"type": "ComfyUIDeployExternalImage",
"pos": [
843.6348876953125,
1467.8876953125
],
"size": [
390.5999755859375,
154
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"name": "default_value",
"shape": 7,
"type": "IMAGE",
"link": 18
}
],
"outputs": [
{
"name": "image",
"type": "IMAGE",
"links": [
19
]
}
],
"properties": {
"cnr_id": "comfyui-deploy",
"ver": "cd3a2ff5471828f9c840e746551592e882c05aa4",
"Node name for S&R": "ComfyUIDeployExternalImage"
},
"widgets_values": [
"input_image_mask",
"",
"",
"",
""
]
},
{
"id": 12,
"type": "ComfyUIDeployExternalImageAlpha",
"pos": [
1107.891357421875,
418.2679138183594
],
"size": [
466.1999816894531,
200
],
"flags": {},
"order": 8,
"mode": 0,
"inputs": [
{
"name": "default_value",
"shape": 7,
"type": "IMAGE",
"link": 11
}
],
"outputs": [
{
"name": "image",
"type": "IMAGE",
"links": [
13
]
}
],
"properties": {
"cnr_id": "comfyui-deploy",
"ver": "cd3a2ff5471828f9c840e746551592e882c05aa4",
"Node name for S&R": "ComfyUIDeployExternalImageAlpha"
},
"widgets_values": [
"input_image_alpha",
"",
""
]
}
],
"links": [
[
10,
10,
0,
11,
0,
"IMAGE"
],
[
11,
11,
0,
12,
0,
"IMAGE"
],
[
12,
10,
1,
11,
1,
"MASK"
],
[
13,
12,
0,
13,
0,
"IMAGE"
],
[
14,
13,
0,
14,
0,
"IMAGE"
],
[
15,
13,
1,
17,
0,
"MASK"
],
[
16,
17,
0,
15,
0,
"IMAGE"
],
[
17,
20,
0,
22,
0,
"IMAGE"
],
[
18,
21,
0,
23,
0,
"IMAGE"
],
[
19,
23,
0,
24,
0,
"IMAGE"
],
[
21,
25,
0,
26,
0,
"IMAGE"
],
[
22,
22,
0,
27,
0,
"IMAGE"
],
[
23,
22,
0,
28,
0,
"IMAGE"
],
[
24,
24,
0,
28,
2,
"MASK"
],
[
25,
13,
0,
29,
0,
"IMAGE"
],
[
26,
13,
1,
29,
2,
"MASK"
],
[
28,
24,
0,
25,
0,
"MASK"
]
],
"groups": [],
"config": {},
"extra": {
"ds": {
"scale": 0.9646149645000013,
"offset": [
48.66905973637718,
-817.5683540167485
]
},
"VHS_latentpreview": false,
"VHS_latentpreviewrate": 0,
"VHS_MetadataImage": true,
"VHS_KeepIntermediate": true
},
"version": 0.4
}
+359
View File
@@ -0,0 +1,359 @@
{
"extra": {
"ds": {
"scale": 0.6010518407212623,
"offset": [815.5938895649746, 84.94304700477853]
},
"node_versions": {
"comfy-core": "0.3.19",
"comfyui-deploy": "b3df94d1affcf7ce05ee7eeda99989194bcd9159"
}
},
"links": [
[9, 8, 0, 9, 0, "IMAGE"],
[45, 30, 1, 6, 0, "CLIP"],
[46, 30, 2, 8, 1, "VAE"],
[47, 30, 0, 31, 0, "MODEL"],
[51, 27, 0, 31, 3, "LATENT"],
[52, 31, 0, 8, 0, "LATENT"],
[54, 30, 1, 33, 0, "CLIP"],
[55, 33, 0, 31, 2, "CONDITIONING"],
[56, 6, 0, 35, 0, "CONDITIONING"],
[57, 35, 0, 31, 1, "CONDITIONING"],
[58, 38, 0, 6, 1, "STRING"],
[59, 40, 0, 27, 0, "INT"],
[60, 39, 0, 27, 1, "INT"]
],
"nodes": [
{
"id": 6,
"pos": [384, 192],
"mode": 0,
"size": [422.8500061035156, 164.30999755859375],
"type": "CLIPTextEncode",
"color": "#232",
"flags": {},
"order": 7,
"title": "CLIP Text Encode (Positive Prompt)",
"inputs": [
{ "link": 45, "name": "clip", "type": "CLIP" },
{
"pos": [10, 36],
"link": 58,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#353",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [56],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"cute anime girl with massive fluffy fennec ears and a big fluffy tail blonde messy long hair blue eyes wearing a maid outfit with a long black gold leaf pattern dress and a white apron mouth open placing a fancy black forest cake with candles on top of a dinner table of an old dark Victorian mansion lit by candlelight with a bright window to the foggy forest and very expensive stuff everywhere there are paintings on the walls"
]
},
{
"id": 8,
"pos": [1151, 195],
"mode": 0,
"size": [210, 46],
"type": "VAEDecode",
"flags": {},
"order": 11,
"inputs": [
{ "link": 52, "name": "samples", "type": "LATENT" },
{ "link": 46, "name": "vae", "type": "VAE" }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [9], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecode" },
"widgets_values": []
},
{
"id": 9,
"pos": [1375, 194],
"mode": 0,
"size": [985.2999877929688, 1060.3800048828125],
"type": "SaveImage",
"flags": {},
"order": 12,
"inputs": [{ "link": 9, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": {},
"widgets_values": ["ComfyUI"]
},
{
"id": 31,
"pos": [816, 192],
"mode": 0,
"size": [315, 262],
"type": "KSampler",
"flags": {},
"order": 10,
"inputs": [
{ "link": 47, "name": "model", "type": "MODEL" },
{ "link": 57, "name": "positive", "type": "CONDITIONING" },
{ "link": 55, "name": "negative", "type": "CONDITIONING" },
{ "link": 51, "name": "latent_image", "type": "LATENT" }
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [52],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "KSampler" },
"widgets_values": [
1024035737089801,
"randomize",
20,
1,
"euler",
"simple",
1
]
},
{
"id": 35,
"pos": [576, 96],
"mode": 0,
"size": [211.60000610351562, 58],
"type": "FluxGuidance",
"flags": {},
"order": 9,
"inputs": [
{ "link": 56, "name": "conditioning", "type": "CONDITIONING" }
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [57],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "FluxGuidance" },
"widgets_values": [3.5]
},
{
"id": 37,
"pos": [60, 345],
"mode": 0,
"size": [225, 88],
"type": "MarkdownNote",
"color": "#432",
"flags": {},
"order": 0,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": {},
"widgets_values": [
"🛈 [Learn more about this workflow](https://comfyanonymous.github.io/ComfyUI_examples/flux/#flux-dev-1)"
]
},
{
"id": 34,
"pos": [825, 510],
"mode": 0,
"size": [282.8599853515625, 164.0800018310547],
"type": "Note",
"color": "#432",
"flags": {},
"order": 1,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": { "text": "" },
"widgets_values": [
"Note that Flux dev and schnell do not have any negative prompt so CFG should be set to 1.0. Setting CFG to 1.0 means the negative prompt is ignored."
]
},
{
"id": 30,
"pos": [48, 192],
"mode": 0,
"size": [315, 98],
"type": "CheckpointLoaderSimple",
"flags": {},
"order": 2,
"inputs": [],
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [47],
"shape": 3,
"slot_index": 0
},
{
"name": "CLIP",
"type": "CLIP",
"links": [45, 54],
"shape": 3,
"slot_index": 1
},
{
"name": "VAE",
"type": "VAE",
"links": [46],
"shape": 3,
"slot_index": 2
}
],
"properties": { "Node name for S&R": "CheckpointLoaderSimple" },
"widgets_values": ["FLUX1/flux1-dev-fp8.safetensors"]
},
{
"id": 33,
"pos": [390, 400],
"mode": 0,
"size": [422.8500061035156, 164.30999755859375],
"type": "CLIPTextEncode",
"color": "#322",
"flags": { "collapsed": true },
"order": 6,
"title": "CLIP Text Encode (Negative Prompt)",
"inputs": [
{ "link": 54, "name": "clip", "type": "CLIP", "slot_index": 0 }
],
"bgcolor": "#533",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [55],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [""]
},
{
"id": 27,
"pos": [461.8629455566406, 460.2491149902344],
"mode": 0,
"size": [315, 126],
"type": "EmptySD3LatentImage",
"color": "#323",
"flags": {},
"order": 8,
"inputs": [
{
"pos": [10, 60],
"link": 59,
"name": "height",
"type": "INT",
"widget": { "name": "height" }
},
{
"pos": [10, 36],
"link": 60,
"name": "width",
"type": "INT",
"widget": { "name": "width" }
}
],
"bgcolor": "#535",
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [51],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "EmptySD3LatentImage" },
"widgets_values": [1024, 1024, 1]
},
{
"id": 38,
"pos": [-497.3238525390625, 306.5517578125],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 3,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [58] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"prompt",
"cute anime girl with massive fluffy fennec ears and a big fluffy tail blonde messy long hair blue eyes wearing a maid outfit with a long black gold leaf pattern dress and a white apron mouth open placing a fancy black forest cake with candles on top of a dinner table of an old dark Victorian mansion lit by candlelight with a bright window to the foggy forest and very expensive stuff everywhere there are paintings on the walls",
"Prompt",
"The prompt to generate an image from."
]
},
{
"id": 39,
"pos": [-507.4543762207031, 612.9552001953125],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 4,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [60], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["width", 1024, "Width", "The width of the image."]
},
{
"id": 40,
"pos": [-497.8695068359375, 864.68994140625],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 5,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [59], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["height", 1024, "Height", "The height of the image."]
}
],
"config": {},
"groups": [
{
"id": 1,
"color": "#3f789e",
"flags": {},
"title": "Input",
"bounding": [
-508.010986328125, 186.76593017578125, 453.23040771484375,
334.3218994140625
],
"font_size": 24
},
{
"id": 2,
"color": "#A88",
"flags": {},
"title": "Additional",
"bounding": [
-517.1227416992188, 537.53759765625, 644.4954223632812,
599.2609252929688
],
"font_size": 24
}
],
"version": 0.4,
"last_link_id": 60,
"last_node_id": 40
}
+423
View File
@@ -0,0 +1,423 @@
{
"extra": {
"ds": {
"scale": 0.6830134553650709,
"offset": [750.2474149300585, 120.48924647647146]
},
"node_versions": {
"comfy-core": "0.3.19",
"comfyui-deploy": "171a227856bd5f31e97828d89f83f3741004d05e"
}
},
"links": [
[9, 8, 0, 9, 0, "IMAGE"],
[10, 11, 0, 6, 0, "CLIP"],
[12, 10, 0, 8, 1, "VAE"],
[19, 16, 0, 13, 2, "SAMPLER"],
[20, 17, 0, 13, 3, "SIGMAS"],
[23, 5, 0, 13, 4, "LATENT"],
[24, 13, 0, 8, 0, "LATENT"],
[30, 22, 0, 13, 1, "GUIDER"],
[37, 25, 0, 13, 0, "NOISE"],
[38, 12, 0, 17, 0, "MODEL"],
[39, 12, 0, 22, 0, "MODEL"],
[40, 6, 0, 22, 1, "CONDITIONING"],
[41, 28, 0, 6, 1, "STRING"],
[44, 29, 0, 5, 0, "INT"],
[45, 30, 0, 5, 1, "INT"]
],
"nodes": [
{
"id": 13,
"pos": [842, 215],
"mode": 0,
"size": [355.20001220703125, 106],
"type": "SamplerCustomAdvanced",
"flags": {},
"order": 14,
"inputs": [
{ "link": 37, "name": "noise", "type": "NOISE", "slot_index": 0 },
{ "link": 30, "name": "guider", "type": "GUIDER", "slot_index": 1 },
{ "link": 19, "name": "sampler", "type": "SAMPLER", "slot_index": 2 },
{ "link": 20, "name": "sigmas", "type": "SIGMAS", "slot_index": 3 },
{
"link": 23,
"name": "latent_image",
"type": "LATENT",
"slot_index": 4
}
],
"outputs": [
{
"name": "output",
"type": "LATENT",
"links": [24],
"shape": 3,
"slot_index": 0
},
{
"name": "denoised_output",
"type": "LATENT",
"links": null,
"shape": 3
}
],
"properties": { "Node name for S&R": "SamplerCustomAdvanced" },
"widgets_values": []
},
{
"id": 8,
"pos": [1248, 192],
"mode": 0,
"size": [210, 46],
"type": "VAEDecode",
"flags": {},
"order": 15,
"inputs": [
{ "link": 24, "name": "samples", "type": "LATENT" },
{ "link": 12, "name": "vae", "type": "VAE" }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [9], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecode" },
"widgets_values": []
},
{
"id": 22,
"pos": [559, 125],
"mode": 0,
"size": [241.79998779296875, 46],
"type": "BasicGuider",
"flags": {},
"order": 13,
"inputs": [
{ "link": 39, "name": "model", "type": "MODEL", "slot_index": 0 },
{
"link": 40,
"name": "conditioning",
"type": "CONDITIONING",
"slot_index": 1
}
],
"outputs": [
{
"name": "GUIDER",
"type": "GUIDER",
"links": [30],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "BasicGuider" },
"widgets_values": []
},
{
"id": 16,
"pos": [480, 720],
"mode": 0,
"size": [315, 58],
"type": "KSamplerSelect",
"flags": {},
"order": 0,
"inputs": [],
"outputs": [
{ "name": "SAMPLER", "type": "SAMPLER", "links": [19], "shape": 3 }
],
"properties": { "Node name for S&R": "KSamplerSelect" },
"widgets_values": ["euler"]
},
{
"id": 17,
"pos": [480, 816],
"mode": 0,
"size": [315, 106],
"type": "BasicScheduler",
"flags": {},
"order": 10,
"inputs": [
{ "link": 38, "name": "model", "type": "MODEL", "slot_index": 0 }
],
"outputs": [
{ "name": "SIGMAS", "type": "SIGMAS", "links": [20], "shape": 3 }
],
"properties": { "Node name for S&R": "BasicScheduler" },
"widgets_values": ["simple", 4, 1]
},
{
"id": 27,
"pos": [480, 960],
"mode": 0,
"size": [311.3529052734375, 131.16229248046875],
"type": "Note",
"color": "#432",
"flags": {},
"order": 1,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": { "text": "" },
"widgets_values": [
"The schnell model is a distilled model that can generate a good image with only 4 steps."
]
},
{
"id": 9,
"pos": [1488, 192],
"mode": 0,
"size": [985.3012084960938, 1060.3828125],
"type": "SaveImage",
"flags": {},
"order": 16,
"inputs": [{ "link": 9, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": {},
"widgets_values": ["ComfyUI"]
},
{
"id": 25,
"pos": [480, 587.7890625],
"mode": 0,
"size": [315, 82],
"type": "RandomNoise",
"color": "#2a363b",
"flags": {},
"order": 2,
"inputs": [],
"bgcolor": "#3f5159",
"outputs": [
{ "name": "NOISE", "type": "NOISE", "links": [37], "shape": 3 }
],
"properties": { "Node name for S&R": "RandomNoise" },
"widgets_values": [393845863554716, "randomize"]
},
{
"id": 5,
"pos": [471.7746276855469, 423.91571044921875],
"mode": 0,
"size": [315, 126],
"type": "EmptyLatentImage",
"color": "#323",
"flags": {},
"order": 12,
"inputs": [
{
"pos": [10, 36],
"link": 44,
"name": "width",
"type": "INT",
"widget": { "name": "width" }
},
{
"pos": [10, 60],
"link": 45,
"name": "height",
"type": "INT",
"widget": { "name": "height" }
}
],
"bgcolor": "#535",
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [23], "slot_index": 0 }
],
"properties": { "Node name for S&R": "EmptyLatentImage" },
"widgets_values": [1024, 1024, 1]
},
{
"id": 26,
"pos": [41.60395812988281, 517.059326171875],
"mode": 0,
"size": [336, 288],
"type": "Note",
"color": "#432",
"flags": {},
"order": 3,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": { "text": "" },
"widgets_values": [
"If you get an error in any of the nodes above make sure the files are in the correct directories.\n\nSee the top of the examples page for the links : https://comfyanonymous.github.io/ComfyUI_examples/flux/\n\nflux1-schnell.safetensors goes in: ComfyUI/models/unet/\n\nt5xxl_fp16.safetensors and clip_l.safetensors go in: ComfyUI/models/clip/\n\nae.safetensors goes in: ComfyUI/models/vae/\n\n\nTip: You can set the weight_dtype above to one of the fp8 types if you have memory issues."
]
},
{
"id": 11,
"pos": [45.584651947021484, 229.13525390625],
"mode": 0,
"size": [315, 122],
"type": "DualCLIPLoader",
"flags": {},
"order": 4,
"inputs": [],
"outputs": [
{
"name": "CLIP",
"type": "CLIP",
"links": [10],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "DualCLIPLoader" },
"widgets_values": [
"t5xxl_fp16.safetensors",
"clip_l.safetensors",
"flux",
"default"
]
},
{
"id": 12,
"pos": [45.92215347290039, 95.6536865234375],
"mode": 0,
"size": [315, 82],
"type": "UNETLoader",
"color": "#223",
"flags": { "pinned": true },
"order": 5,
"inputs": [],
"bgcolor": "#335",
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [38, 39],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "UNETLoader" },
"widgets_values": ["flux1-schnell.safetensors", "default"]
},
{
"id": 6,
"pos": [385.87054443359375, 216.75625610351562],
"mode": 0,
"size": [422.84503173828125, 164.31304931640625],
"type": "CLIPTextEncode",
"color": "#232",
"flags": {},
"order": 11,
"inputs": [
{ "link": 10, "name": "clip", "type": "CLIP" },
{
"pos": [10, 36],
"link": 41,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#353",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [40],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"a bottle with a beautiful rainbow galaxy inside it on top of a wooden table in the middle of a modern kitchen beside a plate of vegetables and mushrooms and a wine glasse that contains a planet earth with a plate with a half eaten apple pie on it"
]
},
{
"id": 10,
"pos": [42.45906066894531, 405.2187805175781],
"mode": 0,
"size": [315, 58],
"type": "VAELoader",
"flags": {},
"order": 6,
"inputs": [],
"outputs": [
{
"name": "VAE",
"type": "VAE",
"links": [12],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "VAELoader" },
"widgets_values": ["ae.sft"]
},
{
"id": 28,
"pos": [-620.3922119140625, 199.8013916015625],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 7,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [41] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"prompt",
"a bottle with a beautiful rainbow galaxy inside it on top of a wooden table in the middle of a modern kitchen beside a plate of vegetables and mushrooms and a wine glasse that contains a planet earth with a plate with a half eaten apple pie on it",
"Prompt",
"The prompt to generate an image from."
]
},
{
"id": 29,
"pos": [-677.4290161132812, 597.3389282226562],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 8,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [44], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["width", 1024, "Width", "The width of the image."]
},
{
"id": 30,
"pos": [-670.0703735351562, 848.3707885742188],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 9,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [45], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["height", 1024, "Height", "The height of the image."]
}
],
"config": {},
"groups": [
{
"id": 1,
"color": "#3f789e",
"flags": {},
"title": "Input",
"bounding": [
-652.184326171875, 99.11231994628906, 565.7923583984375,
362.8800354003906
],
"font_size": 24
},
{
"id": 2,
"color": "#A88",
"flags": {},
"title": "Additional",
"bounding": [
-720.3274536132812, 508.1206970214844, 703.0422973632812,
576.5584716796875
],
"font_size": 24
}
],
"version": 0.4,
"last_link_id": 45,
"last_node_id": 30
}
+482
View File
@@ -0,0 +1,482 @@
{
"extra": {
"ds": {
"scale": 0.6830134553650709,
"offset": [1030.9627343190857, 159.73178253107423]
},
"groupNodes": {},
"node_versions": {
"comfy-core": "0.3.12",
"comfyui-deploy": "b3df94d1affcf7ce05ee7eeda99989194bcd9159"
}
},
"links": [
[19, 16, 0, 13, 2, "SAMPLER"],
[20, 17, 0, 13, 3, "SIGMAS"],
[30, 22, 0, 13, 1, "GUIDER"],
[37, 25, 0, 13, 0, "NOISE"],
[129, 26, 0, 22, 1, "CONDITIONING"],
[175, 44, 0, 26, 0, "CONDITIONING"],
[180, 45, 0, 13, 4, "LATENT"],
[181, 13, 0, 8, 0, "LATENT"],
[190, 12, 0, 17, 0, "MODEL"],
[195, 67, 0, 22, 0, "MODEL"],
[205, 11, 0, 44, 0, "CLIP"],
[206, 10, 0, 8, 1, "VAE"],
[209, 12, 0, 67, 0, "MODEL"],
[210, 13, 0, 73, 0, "LATENT"],
[211, 10, 0, 73, 1, "VAE"],
[215, 73, 0, 75, 0, "IMAGE"],
[216, 78, 0, 44, 1, "STRING"],
[217, 79, 0, 45, 0, "INT"],
[218, 83, 0, 45, 1, "INT"]
],
"nodes": [
{
"id": 16,
"pos": [484, 751],
"mode": 0,
"size": [315, 58],
"type": "KSamplerSelect",
"flags": {},
"order": 0,
"inputs": [],
"outputs": [
{ "name": "SAMPLER", "type": "SAMPLER", "links": [19], "shape": 3 }
],
"properties": { "Node name for S&R": "KSamplerSelect" },
"widgets_values": ["euler"]
},
{
"id": 17,
"pos": [478, 860],
"mode": 0,
"size": [315, 106],
"type": "BasicScheduler",
"flags": {},
"order": 10,
"inputs": [
{ "link": 190, "name": "model", "type": "MODEL", "slot_index": 0 }
],
"outputs": [
{ "name": "SIGMAS", "type": "SIGMAS", "links": [20], "shape": 3 }
],
"properties": { "Node name for S&R": "BasicScheduler" },
"widgets_values": ["simple", 20, 1]
},
{
"id": 26,
"pos": [520, 100],
"mode": 0,
"size": [317.4000244140625, 58],
"type": "FluxGuidance",
"color": "#233",
"flags": {},
"order": 14,
"inputs": [
{ "link": 175, "name": "conditioning", "type": "CONDITIONING" }
],
"bgcolor": "#355",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [129],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "FluxGuidance" },
"widgets_values": [6]
},
{
"id": 45,
"pos": [475.540771484375, 432.673583984375],
"mode": 0,
"size": [315, 130],
"type": "EmptyHunyuanLatentVideo",
"flags": {},
"order": 13,
"inputs": [
{
"link": 217,
"name": "width",
"type": "INT",
"widget": { "name": "width" }
},
{
"link": 218,
"name": "height",
"type": "INT",
"widget": { "name": "height" }
}
],
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [180], "slot_index": 0 }
],
"properties": { "Node name for S&R": "EmptyHunyuanLatentVideo" },
"widgets_values": [848, 480, 73, 1]
},
{
"id": 22,
"pos": [600, 0],
"mode": 0,
"size": [222.3482666015625, 46],
"type": "BasicGuider",
"flags": {},
"order": 15,
"inputs": [
{ "link": 195, "name": "model", "type": "MODEL", "slot_index": 0 },
{
"link": 129,
"name": "conditioning",
"type": "CONDITIONING",
"slot_index": 1
}
],
"outputs": [
{
"name": "GUIDER",
"type": "GUIDER",
"links": [30],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "BasicGuider" },
"widgets_values": []
},
{
"id": 67,
"pos": [360, 0],
"mode": 0,
"size": [210, 58],
"type": "ModelSamplingSD3",
"flags": {},
"order": 11,
"inputs": [{ "link": 209, "name": "model", "type": "MODEL" }],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [195], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ModelSamplingSD3" },
"widgets_values": [7]
},
{
"id": 73,
"pos": [1150, 200],
"mode": 0,
"size": [210, 150],
"type": "VAEDecodeTiled",
"flags": {},
"order": 18,
"inputs": [
{ "link": 210, "name": "samples", "type": "LATENT" },
{ "link": 211, "name": "vae", "type": "VAE" }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [215], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecodeTiled" },
"widgets_values": [256, 64, 64, 8]
},
{
"id": 8,
"pos": [1150, 90],
"mode": 2,
"size": [210, 46],
"type": "VAEDecode",
"flags": {},
"order": 17,
"inputs": [
{ "link": 181, "name": "samples", "type": "LATENT" },
{ "link": 206, "name": "vae", "type": "VAE" }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecode" },
"widgets_values": []
},
{
"id": 77,
"pos": [0, 0],
"mode": 0,
"size": [350, 110],
"type": "Note",
"color": "#432",
"flags": {},
"order": 1,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": {},
"widgets_values": [
"Select a fp8 weight_dtype if you are running out of memory."
]
},
{
"id": 13,
"pos": [860, 200],
"mode": 0,
"size": [272.3617858886719, 124.53733825683594],
"type": "SamplerCustomAdvanced",
"flags": {},
"order": 16,
"inputs": [
{ "link": 37, "name": "noise", "type": "NOISE", "slot_index": 0 },
{ "link": 30, "name": "guider", "type": "GUIDER", "slot_index": 1 },
{ "link": 19, "name": "sampler", "type": "SAMPLER", "slot_index": 2 },
{ "link": 20, "name": "sigmas", "type": "SIGMAS", "slot_index": 3 },
{
"link": 180,
"name": "latent_image",
"type": "LATENT",
"slot_index": 4
}
],
"outputs": [
{
"name": "output",
"type": "LATENT",
"links": [181, 210],
"shape": 3,
"slot_index": 0
},
{
"name": "denoised_output",
"type": "LATENT",
"links": null,
"shape": 3
}
],
"properties": { "Node name for S&R": "SamplerCustomAdvanced" },
"widgets_values": []
},
{
"id": 75,
"pos": [1410, 200],
"mode": 0,
"size": [315, 366],
"type": "SaveAnimatedWEBP",
"flags": {},
"order": 19,
"inputs": [{ "link": 215, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": {},
"widgets_values": ["ComfyUI", 24, false, 80, "default"]
},
{
"id": 25,
"pos": [479, 618],
"mode": 0,
"size": [315, 82],
"type": "RandomNoise",
"color": "#2a363b",
"flags": {},
"order": 2,
"inputs": [],
"bgcolor": "#3f5159",
"outputs": [
{ "name": "NOISE", "type": "NOISE", "links": [37], "shape": 3 }
],
"properties": { "Node name for S&R": "RandomNoise" },
"widgets_values": [1, "randomize"]
},
{
"id": 12,
"pos": [0, 150],
"mode": 0,
"size": [350, 82],
"type": "UNETLoader",
"color": "#223",
"flags": {},
"order": 3,
"inputs": [],
"bgcolor": "#335",
"outputs": [
{
"name": "MODEL",
"type": "MODEL",
"links": [190, 209],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "UNETLoader" },
"widgets_values": ["hunyuan_video_t2v_720p_bf16.safetensors", "default"]
},
{
"id": 10,
"pos": [0, 420],
"mode": 0,
"size": [350, 60],
"type": "VAELoader",
"flags": {},
"order": 4,
"inputs": [],
"outputs": [
{
"name": "VAE",
"type": "VAE",
"links": [206, 211],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "VAELoader" },
"widgets_values": ["hunyuan_video_vae_bf16.safetensors"]
},
{
"id": 11,
"pos": [0, 270],
"mode": 0,
"size": [350, 106],
"type": "DualCLIPLoader",
"flags": {},
"order": 5,
"inputs": [],
"outputs": [
{
"name": "CLIP",
"type": "CLIP",
"links": [205],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "DualCLIPLoader" },
"widgets_values": [
"clip_l.safetensors",
"llava_llama3_fp8_scaled.safetensors",
"hunyuan_video",
"default"
]
},
{
"id": 44,
"pos": [459.0518798828125, 226.60147094726562],
"mode": 0,
"size": [285.6000061035156, 54],
"type": "CLIPTextEncode",
"color": "#232",
"flags": {},
"order": 12,
"title": "CLIP Text Encode (Positive Prompt)",
"inputs": [
{ "link": 205, "name": "clip", "type": "CLIP" },
{
"link": 216,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#353",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [175],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"anime style anime girl with massive fennec ears and one big fluffy tail, she has blonde hair long hair blue eyes wearing a pink sweater and a long blue skirt walking in a beautiful outdoor scenery with snow mountains in the background"
]
},
{
"id": 83,
"pos": [-591.1870727539062, 751.6737670898438],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 6,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [218], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["height", 480, "Height", "The height of the video."]
},
{
"id": 74,
"pos": [1151.89599609375, 402.439697265625],
"mode": 0,
"size": [210, 170],
"type": "Note",
"color": "#432",
"flags": {},
"order": 7,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": {},
"widgets_values": [
"Use the tiled decode node by default because most people will need it.\n\nLower the tile_size and overlap if you run out of memory."
]
},
{
"id": 78,
"pos": [-560.058837890625, 155.3986358642578],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 8,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [216] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"prompt",
"anime style anime girl with massive fennec ears and one big fluffy tail, she has blonde hair long hair blue eyes wearing a pink sweater and a long blue skirt walking in a beautiful outdoor scenery with snow mountains in the background",
"Prompt",
"The prompt to generate the video from."
]
},
{
"id": 79,
"pos": [-588.2138061523438, 493.3861389160156],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 9,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [217], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["width", 848, "Width", "The width of the video."]
}
],
"config": {},
"groups": [
{
"id": 1,
"color": "#3f789e",
"flags": {},
"title": "Input",
"bounding": [
-662.9779052734375, 14.93773078918457, 575.2218627929688,
384.41424560546875
],
"font_size": 24
},
{
"id": 2,
"color": "#b06634",
"flags": {},
"title": "Additional",
"bounding": [
-659.7621459960938, 414.3213195800781, 570.7182006835938,
594.72216796875
],
"font_size": 24
}
],
"version": 0.4,
"last_link_id": 218,
"last_node_id": 83
}
+240
View File
@@ -0,0 +1,240 @@
{
"extra": {
"ds": { "scale": 1, "offset": { "0": 0, "1": 0 } },
"node_versions": {
"comfy-core": "0.3.12",
"comfyui-deploy": "171a227856bd5f31e97828d89f83f3741004d05e"
}
},
"links": [
[1, 4, 0, 3, 0, "MODEL"],
[2, 5, 0, 3, 3, "LATENT"],
[3, 4, 1, 6, 0, "CLIP"],
[4, 6, 0, 3, 1, "CONDITIONING"],
[5, 4, 1, 7, 0, "CLIP"],
[6, 7, 0, 3, 2, "CONDITIONING"],
[7, 3, 0, 8, 0, "LATENT"],
[8, 4, 2, 8, 1, "VAE"],
[9, 8, 0, 9, 0, "IMAGE"],
[10, 12, 0, 6, 1, "STRING"],
[11, 13, 0, 7, 1, "STRING"]
],
"nodes": [
{
"id": 5,
"pos": [473, 609],
"mode": 0,
"size": [315, 106],
"type": "EmptyLatentImage",
"flags": {},
"order": 0,
"inputs": [],
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [2], "slot_index": 0 }
],
"properties": { "Node name for S&R": "EmptyLatentImage" },
"widgets_values": [512, 512, 1]
},
{
"id": 3,
"pos": [863, 186],
"mode": 0,
"size": [315, 262],
"type": "KSampler",
"flags": {},
"order": 7,
"inputs": [
{ "link": 1, "name": "model", "type": "MODEL" },
{ "link": 4, "name": "positive", "type": "CONDITIONING" },
{ "link": 6, "name": "negative", "type": "CONDITIONING" },
{ "link": 2, "name": "latent_image", "type": "LATENT" }
],
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [7], "slot_index": 0 }
],
"properties": { "Node name for S&R": "KSampler" },
"widgets_values": [
156680208700286,
"randomize",
20,
8,
"euler",
"normal",
1
]
},
{
"id": 8,
"pos": [1209, 188],
"mode": 0,
"size": [210, 46],
"type": "VAEDecode",
"flags": {},
"order": 8,
"inputs": [
{ "link": 7, "name": "samples", "type": "LATENT" },
{ "link": 8, "name": "vae", "type": "VAE" }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [9], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecode" },
"widgets_values": []
},
{
"id": 9,
"pos": [1451, 189],
"mode": 0,
"size": [210, 58],
"type": "SaveImage",
"flags": {},
"order": 9,
"inputs": [{ "link": 9, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": {},
"widgets_values": ["ComfyUI"]
},
{
"id": 4,
"pos": [26, 474],
"mode": 0,
"size": [315, 98],
"type": "CheckpointLoaderSimple",
"flags": {},
"order": 1,
"inputs": [],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [1], "slot_index": 0 },
{ "name": "CLIP", "type": "CLIP", "links": [3, 5], "slot_index": 1 },
{ "name": "VAE", "type": "VAE", "links": [8], "slot_index": 2 }
],
"properties": { "Node name for S&R": "CheckpointLoaderSimple" },
"widgets_values": ["v1-5-pruned-emaonly.ckpt"]
},
{
"id": 6,
"pos": [415, 186],
"mode": 0,
"size": [422.84503173828125, 164.31304931640625],
"type": "CLIPTextEncode",
"flags": {},
"order": 6,
"inputs": [
{ "link": 3, "name": "clip", "type": "CLIP" },
{
"link": 10,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [4],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
]
},
{
"id": 7,
"pos": [413, 389],
"mode": 0,
"size": [425.27801513671875, 180.6060791015625],
"type": "CLIPTextEncode",
"flags": {},
"order": 5,
"inputs": [
{ "link": 5, "name": "clip", "type": "CLIP" },
{
"link": 11,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [6],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": ["text, watermark"]
},
{
"id": 13,
"pos": [-70, -60],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 2,
"inputs": [],
"outputs": [
{
"name": "text",
"type": "STRING",
"links": [11],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": ["negative_prompt", "text, watermark", "", ""]
},
{
"id": 12,
"pos": [-72, 193],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 3,
"inputs": [],
"outputs": [
{
"name": "text",
"type": "STRING",
"links": [10],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"positive_prompt",
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,",
"",
""
]
},
{
"id": 14,
"pos": [419, 23],
"mode": 0,
"size": [210, 96],
"type": "ComfyDeploy",
"color": "#432",
"flags": {},
"order": 4,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": { "version": "", "workflow_id": "", "workflow_name": "" },
"widgets_values": ["", "", "1"]
}
],
"config": {},
"groups": [],
"version": 0.4,
"last_link_id": 11,
"last_node_id": 14
}
+409
View File
@@ -0,0 +1,409 @@
{
"extra": {
"ds": {
"scale": 0.620921323059155,
"offset": [1535.1422819333457, 285.0502099350181]
},
"node_versions": {
"comfy-core": "0.3.12",
"comfyui-deploy": "171a227856bd5f31e97828d89f83f3741004d05e"
}
},
"links": [
[7, 3, 0, 8, 0, "LATENT"],
[21, 16, 0, 3, 1, "CONDITIONING"],
[51, 8, 0, 9, 0, "IMAGE"],
[53, 4, 2, 8, 1, "VAE"],
[80, 40, 0, 3, 2, "CONDITIONING"],
[99, 4, 0, 3, 0, "MODEL"],
[100, 53, 0, 3, 3, "LATENT"],
[103, 43, 0, 16, 0, "CLIP"],
[104, 43, 0, 40, 0, "CLIP"],
[105, 54, 0, 16, 1, "STRING"],
[106, 55, 0, 40, 1, "STRING"],
[107, 56, 0, 53, 0, "INT"],
[108, 57, 0, 53, 1, "INT"]
],
"nodes": [
{
"id": 8,
"pos": [1200, 96],
"mode": 0,
"size": [210, 46],
"type": "VAEDecode",
"flags": {},
"order": 14,
"inputs": [
{ "link": 7, "name": "samples", "type": "LATENT" },
{ "link": 53, "name": "vae", "type": "VAE", "slot_index": 1 }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [51], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecode" },
"widgets_values": []
},
{
"id": 9,
"pos": [1440, 96],
"mode": 0,
"size": [952.5112915039062, 1007.9328002929688],
"type": "SaveImage",
"flags": {},
"order": 15,
"inputs": [
{ "link": 51, "name": "images", "type": "IMAGE", "slot_index": 0 }
],
"outputs": [],
"properties": {},
"widgets_values": ["ComfyUI"]
},
{
"id": 53,
"pos": [480, 576],
"mode": 0,
"size": [315, 106],
"type": "EmptySD3LatentImage",
"flags": {},
"order": 10,
"inputs": [
{
"link": 107,
"name": "width",
"type": "INT",
"widget": { "name": "width" }
},
{
"link": 108,
"name": "height",
"type": "INT",
"widget": { "name": "height" }
}
],
"outputs": [
{
"name": "LATENT",
"type": "LATENT",
"links": [100],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "EmptySD3LatentImage" },
"widgets_values": [1024, 1024, 1]
},
{
"id": 50,
"pos": [-384, 144],
"mode": 0,
"size": [223.34756469726562, 254.37765502929688],
"type": "Note",
"color": "#432",
"flags": {},
"order": 0,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": { "text": "" },
"widgets_values": [
"SD3 supports different text encoder configurations, you can see how to load them here.\n\n\nMake sure to put these files:\nclip_g.safetensors\nclip_l.safetensors\nt5xxl_fp16.safetensors\n\n\nIn the ComfyUI/models/clip directory"
]
},
{
"id": 41,
"pos": [-96, 0],
"mode": 0,
"size": [315, 82],
"type": "CLIPLoader",
"flags": {},
"order": 1,
"inputs": [],
"outputs": [
{
"name": "CLIP",
"type": "CLIP",
"links": [],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPLoader" },
"widgets_values": ["t5xxl_fp16.safetensors", "sd3", "default"]
},
{
"id": 4,
"pos": [-96, 480],
"mode": 0,
"size": [384.75592041015625, 98],
"type": "CheckpointLoaderSimple",
"flags": {},
"order": 2,
"inputs": [],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [99], "slot_index": 0 },
{ "name": "CLIP", "type": "CLIP", "links": [], "slot_index": 1 },
{ "name": "VAE", "type": "VAE", "links": [53], "slot_index": 2 }
],
"properties": { "Node name for S&R": "CheckpointLoaderSimple" },
"widgets_values": ["sd3.5_large.safetensors"]
},
{
"id": 3,
"pos": [864, 96],
"mode": 0,
"size": [315, 474],
"type": "KSampler",
"flags": {},
"order": 13,
"inputs": [
{ "link": 99, "name": "model", "type": "MODEL", "slot_index": 0 },
{ "link": 21, "name": "positive", "type": "CONDITIONING" },
{ "link": 80, "name": "negative", "type": "CONDITIONING" },
{ "link": 100, "name": "latent_image", "type": "LATENT" }
],
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [7], "slot_index": 0 }
],
"properties": { "Node name for S&R": "KSampler" },
"widgets_values": [
797639488625463,
"randomize",
20,
5.45,
"euler",
"sgm_uniform",
1
]
},
{
"id": 51,
"pos": [-96, 624],
"mode": 0,
"size": [384, 192],
"type": "Note",
"color": "#432",
"flags": {},
"order": 3,
"inputs": [],
"bgcolor": "#653",
"outputs": [],
"properties": { "text": "" },
"widgets_values": [
"sd3.5_large.safetensors and sd3.5_medium.safetensors are files that do not contain any CLIP/text encoder weights so you need to load them separately.\n\nThey go in the ComfyUI/models/checkpoints directory."
]
},
{
"id": 43,
"pos": [-96, 288],
"mode": 0,
"size": [315, 106],
"type": "TripleCLIPLoader",
"flags": {},
"order": 4,
"inputs": [],
"outputs": [
{
"name": "CLIP",
"type": "CLIP",
"links": [103, 104],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "TripleCLIPLoader" },
"widgets_values": [
"clip_l.safetensors",
"clip_g.safetensors",
"t5xxl_fp16.safetensors"
]
},
{
"id": 42,
"pos": [-96, 144],
"mode": 0,
"size": [315, 106],
"type": "DualCLIPLoader",
"flags": {},
"order": 5,
"inputs": [],
"outputs": [
{
"name": "CLIP",
"type": "CLIP",
"links": [],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "DualCLIPLoader" },
"widgets_values": [
"clip_l.safetensors",
"clip_g.safetensors",
"sd3",
"default"
]
},
{
"id": 40,
"pos": [399.0046081542969, 337.009765625],
"mode": 0,
"size": [397.45977783203125, 151.2898406982422],
"type": "CLIPTextEncode",
"color": "#322",
"flags": {},
"order": 11,
"title": "Negative Prompt",
"inputs": [
{ "link": 104, "name": "clip", "type": "CLIP" },
{
"link": 106,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#533",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [80],
"shape": 3,
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [""]
},
{
"id": 16,
"pos": [398.5880432128906, 108.27188110351562],
"mode": 0,
"size": [397.21484375, 158.671875],
"type": "CLIPTextEncode",
"color": "#232",
"flags": {},
"order": 12,
"title": "Positive Prompt",
"inputs": [
{ "link": 103, "name": "clip", "type": "CLIP" },
{
"link": 105,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#353",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [21],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"a bottle with a rainbow galaxy inside it on top of a wooden table on a snowy mountain top with the ocean and clouds in the background with a shot glass beside containing darkness beside a snow sculpture in the shape of a fox"
]
},
{
"id": 57,
"pos": [-1015.1683349609375, 980.396240234375],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 6,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [108], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["height", 1024, "Height", "The height of the image. "]
},
{
"id": 56,
"pos": [-1014.532470703125, 732.8568725585938],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 7,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [107], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["width", 1024, "Width", "The width of the image. "]
},
{
"id": 55,
"pos": [-1010.8035278320312, 487.404052734375],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 8,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [106] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": ["negative_prompt", "", "Negative Prompt", ""]
},
{
"id": 54,
"pos": [-1059.1072998046875, 127.12823486328125],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 9,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [105] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"prompt",
"a bottle with a rainbow galaxy inside it on top of a wooden table on a snowy mountain top with the ocean and clouds in the background with a shot glass beside containing darkness beside a snow sculpture in the shape of a fox",
"Prompt",
"The prompt to generate an image from."
]
}
],
"config": {},
"groups": [
{
"id": 1,
"color": "#3f789e",
"flags": {},
"title": "Different Text Encoder Configurations",
"bounding": [-144, -96, 480, 528],
"font_size": 24
},
{
"id": 2,
"color": "#b06634",
"flags": {},
"title": "Additional",
"bounding": [
-1194.5233154296875, 388.3871154785156, 722.5393676757812,
824.6746215820312
],
"font_size": 24
},
{
"id": 3,
"color": "#3f789e",
"flags": {},
"title": "Input",
"bounding": [
-1148.487060546875, 30.449382781982422, 552.3358764648438,
329.3829040527344
],
"font_size": 24
}
],
"version": 0.4,
"last_link_id": 108,
"last_node_id": 57
}
+452
View File
@@ -0,0 +1,452 @@
{
"extra": {
"ds": {
"scale": 0.6934334949441662,
"offset": [276.7941771713029, -7.923387380923855]
},
"node_versions": {
"comfy-core": "0.3.18",
"comfyui-deploy": "b3df94d1affcf7ce05ee7eeda99989194bcd9159"
}
},
"links": [
[35, 3, 0, 8, 0, "LATENT"],
[56, 8, 0, 28, 0, "IMAGE"],
[74, 38, 0, 6, 0, "CLIP"],
[75, 38, 0, 7, 0, "CLIP"],
[76, 39, 0, 8, 1, "VAE"],
[93, 8, 0, 47, 0, "IMAGE"],
[94, 49, 0, 51, 0, "CLIP_VISION"],
[97, 6, 0, 50, 0, "CONDITIONING"],
[98, 7, 0, 50, 1, "CONDITIONING"],
[99, 39, 0, 50, 2, "VAE"],
[101, 50, 0, 3, 1, "CONDITIONING"],
[102, 50, 1, 3, 2, "CONDITIONING"],
[103, 50, 2, 3, 3, "LATENT"],
[107, 51, 0, 50, 3, "CLIP_VISION_OUTPUT"],
[110, 37, 0, 54, 0, "MODEL"],
[111, 54, 0, 3, 0, "MODEL"],
[112, 55, 0, 6, 1, "STRING"],
[113, 56, 0, 7, 1, "STRING"],
[115, 57, 0, 50, 4, "IMAGE"],
[116, 57, 0, 51, 1, "IMAGE"],
[117, 58, 0, 50, 5, "INT"],
[118, 59, 0, 50, 6, "INT"]
],
"nodes": [
{
"id": 8,
"pos": [1210, 190],
"mode": 0,
"size": [210, 46],
"type": "VAEDecode",
"flags": {},
"order": 15,
"inputs": [
{ "link": 35, "name": "samples", "type": "LATENT" },
{ "link": 76, "name": "vae", "type": "VAE" }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [56, 93], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecode" },
"widgets_values": []
},
{
"id": 39,
"pos": [866.3932495117188, 499.18597412109375],
"mode": 0,
"size": [306.36004638671875, 58],
"type": "VAELoader",
"flags": {},
"order": 0,
"inputs": [],
"outputs": [
{ "name": "VAE", "type": "VAE", "links": [76, 99], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAELoader" },
"widgets_values": ["wan_2.1_vae.safetensors"]
},
{
"id": 28,
"pos": [1460, 190],
"mode": 0,
"size": [870.8511352539062, 643.7430419921875],
"type": "SaveAnimatedWEBP",
"flags": {},
"order": 16,
"inputs": [{ "link": 56, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": {},
"widgets_values": ["ComfyUI", 16, false, 90, "default", ""]
},
{
"id": 47,
"pos": [2367.213134765625, 193.6114959716797],
"mode": 4,
"size": [315, 130],
"type": "SaveWEBM",
"flags": {},
"order": 17,
"inputs": [{ "link": 93, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": { "Node name for S&R": "SaveWEBM" },
"widgets_values": ["ComfyUI", "vp9", 24, 32]
},
{
"id": 50,
"pos": [673.0507202148438, 627.272705078125],
"mode": 0,
"size": [342.5999755859375, 250],
"type": "WanImageToVideo",
"flags": {},
"order": 13,
"inputs": [
{ "link": 97, "name": "positive", "type": "CONDITIONING" },
{ "link": 98, "name": "negative", "type": "CONDITIONING" },
{ "link": 99, "name": "vae", "type": "VAE" },
{
"link": 107,
"name": "clip_vision_output",
"type": "CLIP_VISION_OUTPUT",
"shape": 7
},
{ "link": 115, "name": "start_image", "type": "IMAGE", "shape": 7 },
{
"pos": [10, 116],
"link": 117,
"name": "width",
"type": "INT",
"widget": { "name": "width" }
},
{
"pos": [10, 140],
"link": 118,
"name": "height",
"type": "INT",
"widget": { "name": "height" }
}
],
"outputs": [
{
"name": "positive",
"type": "CONDITIONING",
"links": [101],
"slot_index": 0
},
{
"name": "negative",
"type": "CONDITIONING",
"links": [102],
"slot_index": 1
},
{ "name": "latent", "type": "LATENT", "links": [103], "slot_index": 2 }
],
"properties": { "Node name for S&R": "WanImageToVideo" },
"widgets_values": [512, 512, 33, 1]
},
{
"id": 3,
"pos": [863, 187],
"mode": 0,
"size": [315, 262],
"type": "KSampler",
"flags": {},
"order": 14,
"inputs": [
{ "link": 111, "name": "model", "type": "MODEL" },
{ "link": 101, "name": "positive", "type": "CONDITIONING" },
{ "link": 102, "name": "negative", "type": "CONDITIONING" },
{ "link": 103, "name": "latent_image", "type": "LATENT" }
],
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [35], "slot_index": 0 }
],
"properties": { "Node name for S&R": "KSampler" },
"widgets_values": [
55104841546759,
"randomize",
20,
6,
"uni_pc",
"simple",
1
]
},
{
"id": 51,
"pos": [360, 640],
"mode": 0,
"size": [253.60000610351562, 78],
"type": "CLIPVisionEncode",
"flags": {},
"order": 12,
"inputs": [
{ "link": 94, "name": "clip_vision", "type": "CLIP_VISION" },
{ "link": 116, "name": "image", "type": "IMAGE" }
],
"outputs": [
{
"name": "CLIP_VISION_OUTPUT",
"type": "CLIP_VISION_OUTPUT",
"links": [107],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPVisionEncode" },
"widgets_values": ["none"]
},
{
"id": 54,
"pos": [510, 70],
"mode": 0,
"size": [315, 58],
"type": "ModelSamplingSD3",
"flags": {},
"order": 9,
"inputs": [{ "link": 110, "name": "model", "type": "MODEL" }],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [111], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ModelSamplingSD3" },
"widgets_values": [8]
},
{
"id": 6,
"pos": [415, 186],
"mode": 0,
"size": [422.84503173828125, 164.31304931640625],
"type": "CLIPTextEncode",
"color": "#232",
"flags": {},
"order": 10,
"title": "CLIP Text Encode (Positive Prompt)",
"inputs": [
{ "link": 74, "name": "clip", "type": "CLIP" },
{
"pos": [10, 36],
"link": 112,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#353",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [97],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"a cute anime girl with massive fennec ears and a big fluffy tail wearing a maid outfit turning around"
]
},
{
"id": 55,
"pos": [-672.6411743164062, 140.167724609375],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 1,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [112] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"positive_prompt",
"a cute anime girl with massive fennec ears and a big fluffy tail wearing a maid outfit turning around",
"Prompt",
"The text prompt to guide video generation."
]
},
{
"id": 57,
"pos": [-664.3828125, 405.5113525390625],
"mode": 0,
"size": [390.5999755859375, 366],
"type": "ComfyUIDeployExternalImage",
"flags": {},
"order": 2,
"inputs": [
{ "link": null, "name": "default_value", "type": "IMAGE", "shape": 7 }
],
"outputs": [{ "name": "image", "type": "IMAGE", "links": [115, 116] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalImage" },
"widgets_values": [
"image_url",
"Image Url",
"URL of the input image.",
"https://comfy-deploy-output.s3.us-east-2.amazonaws.com/assets/img_GZMJYXDnLbYjWybu.png",
""
]
},
{
"id": 56,
"pos": [-674.42333984375, 953.0498046875],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 3,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [113] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"negative_prompt",
"色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走",
"Negative Prompt",
"The negative prompt to use. Use it to address details that you don't want in the video. This could be colors, objects, scenery and even the small details (e.g. moustache, blurry, low resolution). "
]
},
{
"id": 58,
"pos": [-668.314453125, 1208.204833984375],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 4,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [117], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["width", 512, "Width", "The width of the video. "]
},
{
"id": 59,
"pos": [-665.8831787109375, 1461.255126953125],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 5,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [118], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["height", 512, "Height", "The Height of the video."]
},
{
"id": 37,
"pos": [-3.3700287342071533, 81.69654846191406],
"mode": 0,
"size": [346.7470703125, 82],
"type": "UNETLoader",
"flags": {},
"order": 6,
"inputs": [],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [110], "slot_index": 0 }
],
"properties": { "Node name for S&R": "UNETLoader" },
"widgets_values": ["wan2.1_i2v_480p_14B_bf16.safetensors", "default"]
},
{
"id": 38,
"pos": [-3.7316977977752686, 230.76495361328125],
"mode": 0,
"size": [390, 98],
"type": "CLIPLoader",
"flags": {},
"order": 7,
"inputs": [],
"outputs": [
{ "name": "CLIP", "type": "CLIP", "links": [74, 75], "slot_index": 0 }
],
"properties": { "Node name for S&R": "CLIPLoader" },
"widgets_values": [
"umt5_xxl_fp8_e4m3fn_scaled.safetensors",
"wan",
"default"
]
},
{
"id": 49,
"pos": [16.57183265686035, 640.22314453125],
"mode": 0,
"size": [315, 58],
"type": "CLIPVisionLoader",
"flags": {},
"order": 8,
"inputs": [],
"outputs": [
{
"name": "CLIP_VISION",
"type": "CLIP_VISION",
"links": [94],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPVisionLoader" },
"widgets_values": ["clip_vision_h.safetensors"]
},
{
"id": 7,
"pos": [414.1811828613281, 393.8824768066406],
"mode": 0,
"size": [425.27801513671875, 180.6060791015625],
"type": "CLIPTextEncode",
"color": "#322",
"flags": {},
"order": 11,
"title": "CLIP Text Encode (Negative Prompt)",
"inputs": [
{ "link": 75, "name": "clip", "type": "CLIP" },
{
"pos": [10, 36],
"link": 113,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#533",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [98],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
]
}
],
"config": {},
"groups": [
{
"id": 1,
"color": "#3f789e",
"flags": {},
"title": "Input",
"bounding": [
-708.2940063476562, 47.549102783203125, 617.7969360351562,
761.303955078125
],
"font_size": 24
},
{
"id": 2,
"color": "#b06634",
"flags": {},
"title": "Additional",
"bounding": [
-703.6196899414062, 861.310791015625, 625.998779296875,
845.0675048828125
],
"font_size": 24
}
],
"version": 0.4,
"last_link_id": 118,
"last_node_id": 59
}
+452
View File
@@ -0,0 +1,452 @@
{
"extra": {
"ds": {
"scale": 0.6303940863128795,
"offset": [-209.31226203277674, -32.62073943929215]
},
"node_versions": {
"comfy-core": "0.3.18",
"comfyui-deploy": "b3df94d1affcf7ce05ee7eeda99989194bcd9159"
}
},
"links": [
[1, 6, 0, 1, 0, "LATENT"],
[2, 2, 0, 1, 1, "VAE"],
[3, 1, 0, 3, 0, "IMAGE"],
[4, 1, 0, 4, 0, "IMAGE"],
[5, 9, 0, 5, 0, "CONDITIONING"],
[6, 18, 0, 5, 1, "CONDITIONING"],
[7, 2, 0, 5, 2, "VAE"],
[8, 7, 0, 5, 3, "CLIP_VISION_OUTPUT"],
[9, 11, 0, 5, 4, "IMAGE"],
[10, 13, 0, 5, 5, "INT"],
[11, 14, 0, 5, 6, "INT"],
[12, 8, 0, 6, 0, "MODEL"],
[13, 5, 0, 6, 1, "CONDITIONING"],
[14, 5, 1, 6, 2, "CONDITIONING"],
[15, 5, 2, 6, 3, "LATENT"],
[16, 17, 0, 7, 0, "CLIP_VISION"],
[17, 11, 0, 7, 1, "IMAGE"],
[18, 15, 0, 8, 0, "MODEL"],
[19, 16, 0, 9, 0, "CLIP"],
[20, 10, 0, 9, 1, "STRING"],
[21, 16, 0, 18, 0, "CLIP"],
[22, 12, 0, 18, 1, "STRING"]
],
"nodes": [
{
"id": 1,
"pos": [1823.98876953125, 702.6244506835938],
"mode": 0,
"size": [210, 46],
"type": "VAEDecode",
"flags": {},
"order": 15,
"inputs": [
{ "link": 1, "name": "samples", "type": "LATENT" },
{ "link": 2, "name": "vae", "type": "VAE" }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [3, 4], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecode" },
"widgets_values": []
},
{
"id": 2,
"pos": [1480.382080078125, 1011.8104248046875],
"mode": 0,
"size": [306.36004638671875, 58],
"type": "VAELoader",
"flags": {},
"order": 0,
"inputs": [],
"outputs": [
{ "name": "VAE", "type": "VAE", "links": [2, 7], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAELoader" },
"widgets_values": ["wan_2.1_vae.safetensors"]
},
{
"id": 3,
"pos": [2073.98876953125, 702.6244506835938],
"mode": 0,
"size": [870.8511352539062, 643.7430419921875],
"type": "SaveAnimatedWEBP",
"flags": {},
"order": 16,
"inputs": [{ "link": 3, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": {},
"widgets_values": ["ComfyUI", 16, false, 90, "default", ""]
},
{
"id": 4,
"pos": [2981.201904296875, 706.2359008789062],
"mode": 4,
"size": [315, 130],
"type": "SaveWEBM",
"flags": {},
"order": 17,
"inputs": [{ "link": 4, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": { "Node name for S&R": "SaveWEBM" },
"widgets_values": ["ComfyUI", "vp9", 24, 32]
},
{
"id": 5,
"pos": [1287.03955078125, 1139.8970947265625],
"mode": 0,
"size": [342.5999755859375, 250],
"type": "WanImageToVideo",
"flags": {},
"order": 13,
"inputs": [
{ "link": 5, "name": "positive", "type": "CONDITIONING" },
{ "link": 6, "name": "negative", "type": "CONDITIONING" },
{ "link": 7, "name": "vae", "type": "VAE" },
{
"link": 8,
"name": "clip_vision_output",
"type": "CLIP_VISION_OUTPUT",
"shape": 7
},
{ "link": 9, "name": "start_image", "type": "IMAGE", "shape": 7 },
{
"pos": [10, 116],
"link": 10,
"name": "width",
"type": "INT",
"widget": { "name": "width" }
},
{
"pos": [10, 140],
"link": 11,
"name": "height",
"type": "INT",
"widget": { "name": "height" }
}
],
"outputs": [
{
"name": "positive",
"type": "CONDITIONING",
"links": [13],
"slot_index": 0
},
{
"name": "negative",
"type": "CONDITIONING",
"links": [14],
"slot_index": 1
},
{ "name": "latent", "type": "LATENT", "links": [15], "slot_index": 2 }
],
"properties": { "Node name for S&R": "WanImageToVideo" },
"widgets_values": [512, 512, 33, 1]
},
{
"id": 6,
"pos": [1476.98876953125, 699.6244506835938],
"mode": 0,
"size": [315, 262],
"type": "KSampler",
"flags": {},
"order": 14,
"inputs": [
{ "link": 12, "name": "model", "type": "MODEL" },
{ "link": 13, "name": "positive", "type": "CONDITIONING" },
{ "link": 14, "name": "negative", "type": "CONDITIONING" },
{ "link": 15, "name": "latent_image", "type": "LATENT" }
],
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [1], "slot_index": 0 }
],
"properties": { "Node name for S&R": "KSampler" },
"widgets_values": [
625624384968868,
"randomize",
20,
6,
"uni_pc",
"simple",
1
]
},
{
"id": 7,
"pos": [973.98876953125, 1152.6243896484375],
"mode": 0,
"size": [253.60000610351562, 78],
"type": "CLIPVisionEncode",
"flags": {},
"order": 10,
"inputs": [
{ "link": 16, "name": "clip_vision", "type": "CLIP_VISION" },
{ "link": 17, "name": "image", "type": "IMAGE" }
],
"outputs": [
{
"name": "CLIP_VISION_OUTPUT",
"type": "CLIP_VISION_OUTPUT",
"links": [8],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPVisionEncode" },
"widgets_values": ["none"]
},
{
"id": 8,
"pos": [1123.98876953125, 582.6244506835938],
"mode": 0,
"size": [315, 58],
"type": "ModelSamplingSD3",
"flags": {},
"order": 11,
"inputs": [{ "link": 18, "name": "model", "type": "MODEL" }],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [12], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ModelSamplingSD3" },
"widgets_values": [8]
},
{
"id": 9,
"pos": [1028.98876953125, 698.6244506835938],
"mode": 0,
"size": [422.84503173828125, 164.31304931640625],
"type": "CLIPTextEncode",
"color": "#232",
"flags": {},
"order": 12,
"title": "CLIP Text Encode (Positive Prompt)",
"inputs": [
{ "link": 19, "name": "clip", "type": "CLIP" },
{
"pos": [10, 36],
"link": 20,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#353",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [5],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"a cute anime girl with massive fennec ears and a big fluffy tail wearing a maid outfit turning around"
]
},
{
"id": 11,
"pos": [-50.3940315246582, 918.1358032226562],
"mode": 0,
"size": [390.5999755859375, 366],
"type": "ComfyUIDeployExternalImage",
"flags": {},
"order": 1,
"inputs": [
{ "link": null, "name": "default_value", "type": "IMAGE", "shape": 7 }
],
"outputs": [{ "name": "image", "type": "IMAGE", "links": [9, 17] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalImage" },
"widgets_values": [
"image_url",
"Image Url",
"URL of the input image.",
"https://comfy-deploy-output.s3.us-east-2.amazonaws.com/assets/img_GZMJYXDnLbYjWybu.png",
""
]
},
{
"id": 12,
"pos": [-60.4345588684082, 1465.6741943359375],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 2,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [22] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"negative_prompt",
"色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走",
"Negative Prompt",
"The negative prompt to use. Use it to address details that you don't want in the video. This could be colors, objects, scenery and even the small details (e.g. moustache, blurry, low resolution). "
]
},
{
"id": 13,
"pos": [-54.3256721496582, 1720.8292236328125],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 3,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [10], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["width", 512, "Width", "The width of the video. "]
},
{
"id": 14,
"pos": [-51.8943977355957, 1973.8795166015625],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 4,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [11], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["height", 512, "Height", "The Height of the video."]
},
{
"id": 16,
"pos": [610.257080078125, 743.389404296875],
"mode": 0,
"size": [390, 98],
"type": "CLIPLoader",
"flags": {},
"order": 5,
"inputs": [],
"outputs": [
{ "name": "CLIP", "type": "CLIP", "links": [19, 21], "slot_index": 0 }
],
"properties": { "Node name for S&R": "CLIPLoader" },
"widgets_values": [
"umt5_xxl_fp8_e4m3fn_scaled.safetensors",
"wan",
"default"
]
},
{
"id": 17,
"pos": [630.5606079101562, 1152.8475341796875],
"mode": 0,
"size": [315, 58],
"type": "CLIPVisionLoader",
"flags": {},
"order": 6,
"inputs": [],
"outputs": [
{
"name": "CLIP_VISION",
"type": "CLIP_VISION",
"links": [16],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPVisionLoader" },
"widgets_values": ["clip_vision_h.safetensors"]
},
{
"id": 18,
"pos": [1028.169921875, 906.5068969726562],
"mode": 0,
"size": [425.27801513671875, 180.6060791015625],
"type": "CLIPTextEncode",
"color": "#322",
"flags": {},
"order": 9,
"title": "CLIP Text Encode (Negative Prompt)",
"inputs": [
{ "link": 21, "name": "clip", "type": "CLIP" },
{
"pos": [10, 36],
"link": 22,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#533",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [6],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
]
},
{
"id": 15,
"pos": [610.6187744140625, 594.3209838867188],
"mode": 0,
"size": [346.7470703125, 82],
"type": "UNETLoader",
"flags": {},
"order": 7,
"inputs": [],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [18], "slot_index": 0 }
],
"properties": { "Node name for S&R": "UNETLoader" },
"widgets_values": ["wan2.1_i2v_720p_14B_bf16.safetensors", "default"]
},
{
"id": 10,
"pos": [-60.696964263916016, 649.5515747070312],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 8,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [20] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"positive_prompt",
"a cute anime girl with massive fennec ears and a big fluffy tail wearing a maid outfit running towards front happily",
"Prompt",
"The text prompt to guide video generation."
]
}
],
"config": {},
"groups": [
{
"id": 1,
"color": "#3f789e",
"flags": {},
"title": "Input",
"bounding": [
-94.30522155761719, 560.1735229492188, 617.7969360351562,
761.303955078125
],
"font_size": 24
},
{
"id": 2,
"color": "#b06634",
"flags": {},
"title": "Additional",
"bounding": [
-89.63090515136719, 1373.9351806640625, 625.998779296875,
845.0675048828125
],
"font_size": 24
}
],
"version": 0.4,
"last_link_id": 22,
"last_node_id": 18
}
+359
View File
@@ -0,0 +1,359 @@
{
"extra": {
"ds": {
"scale": 0.8390545288824369,
"offset": [814.3725295729478, -347.90757575249455]
},
"node_versions": {
"comfy-core": "0.3.18",
"comfyui-deploy": "b3df94d1affcf7ce05ee7eeda99989194bcd9159"
}
},
"links": [
[35, 3, 0, 8, 0, "LATENT"],
[46, 6, 0, 3, 1, "CONDITIONING"],
[52, 7, 0, 3, 2, "CONDITIONING"],
[56, 8, 0, 28, 0, "IMAGE"],
[74, 38, 0, 6, 0, "CLIP"],
[75, 38, 0, 7, 0, "CLIP"],
[76, 39, 0, 8, 1, "VAE"],
[91, 40, 0, 3, 3, "LATENT"],
[93, 8, 0, 47, 0, "IMAGE"],
[94, 37, 0, 48, 0, "MODEL"],
[95, 48, 0, 3, 0, "MODEL"],
[96, 49, 0, 6, 1, "STRING"],
[97, 50, 0, 7, 1, "STRING"],
[99, 52, 0, 40, 1, "INT"],
[100, 51, 0, 40, 0, "INT"]
],
"nodes": [
{
"id": 8,
"pos": [1210, 190],
"mode": 0,
"size": [210, 46],
"type": "VAEDecode",
"flags": {},
"order": 12,
"inputs": [
{ "link": 35, "name": "samples", "type": "LATENT" },
{ "link": 76, "name": "vae", "type": "VAE" }
],
"outputs": [
{ "name": "IMAGE", "type": "IMAGE", "links": [56, 93], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAEDecode" },
"widgets_values": []
},
{
"id": 39,
"pos": [866.3932495117188, 499.18597412109375],
"mode": 0,
"size": [306.36004638671875, 58],
"type": "VAELoader",
"flags": {},
"order": 0,
"inputs": [],
"outputs": [
{ "name": "VAE", "type": "VAE", "links": [76], "slot_index": 0 }
],
"properties": { "Node name for S&R": "VAELoader" },
"widgets_values": ["wan_2.1_vae.safetensors"]
},
{
"id": 47,
"pos": [2367.213134765625, 193.6114959716797],
"mode": 4,
"size": [315, 130],
"type": "SaveWEBM",
"flags": {},
"order": 14,
"inputs": [{ "link": 93, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": { "Node name for S&R": "SaveWEBM" },
"widgets_values": ["ComfyUI", "vp9", 24, 32]
},
{
"id": 3,
"pos": [863, 187],
"mode": 0,
"size": [315, 262],
"type": "KSampler",
"flags": {},
"order": 11,
"inputs": [
{ "link": 95, "name": "model", "type": "MODEL" },
{ "link": 46, "name": "positive", "type": "CONDITIONING" },
{ "link": 52, "name": "negative", "type": "CONDITIONING" },
{ "link": 91, "name": "latent_image", "type": "LATENT" }
],
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [35], "slot_index": 0 }
],
"properties": { "Node name for S&R": "KSampler" },
"widgets_values": [
577746309562741,
"randomize",
30,
6,
"uni_pc",
"simple",
1
]
},
{
"id": 48,
"pos": [440, 50],
"mode": 0,
"size": [210, 58],
"type": "ModelSamplingSD3",
"flags": {},
"order": 7,
"inputs": [{ "link": 94, "name": "model", "type": "MODEL" }],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [95], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ModelSamplingSD3" },
"widgets_values": [8]
},
{
"id": 37,
"pos": [20, 40],
"mode": 0,
"size": [346.7470703125, 82],
"type": "UNETLoader",
"flags": {},
"order": 1,
"inputs": [],
"outputs": [
{ "name": "MODEL", "type": "MODEL", "links": [94], "slot_index": 0 }
],
"properties": { "Node name for S&R": "UNETLoader" },
"widgets_values": ["wan2.1_t2v_1.3B_fp16.safetensors", "default"]
},
{
"id": 6,
"pos": [415, 186],
"mode": 0,
"size": [422.84503173828125, 164.31304931640625],
"type": "CLIPTextEncode",
"color": "#232",
"flags": {},
"order": 8,
"title": "CLIP Text Encode (Positive Prompt)",
"inputs": [
{ "link": 74, "name": "clip", "type": "CLIP" },
{
"pos": [10, 36],
"link": 96,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#353",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [46],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"a fox moving quickly in a beautiful winter scenery nature trees mountains daytime tracking camera"
]
},
{
"id": 7,
"pos": [413, 389],
"mode": 0,
"size": [425.27801513671875, 180.6060791015625],
"type": "CLIPTextEncode",
"color": "#322",
"flags": {},
"order": 9,
"title": "CLIP Text Encode (Negative Prompt)",
"inputs": [
{ "link": 75, "name": "clip", "type": "CLIP" },
{
"pos": [10, 36],
"link": 97,
"name": "text",
"type": "STRING",
"widget": { "name": "text" }
}
],
"bgcolor": "#533",
"outputs": [
{
"name": "CONDITIONING",
"type": "CONDITIONING",
"links": [52],
"slot_index": 0
}
],
"properties": { "Node name for S&R": "CLIPTextEncode" },
"widgets_values": [
"色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走"
]
},
{
"id": 38,
"pos": [-10.047812461853027, 187.37384033203125],
"mode": 0,
"size": [390, 98],
"type": "CLIPLoader",
"flags": {},
"order": 2,
"inputs": [],
"outputs": [
{ "name": "CLIP", "type": "CLIP", "links": [74, 75], "slot_index": 0 }
],
"properties": { "Node name for S&R": "CLIPLoader" },
"widgets_values": [
"umt5_xxl_fp8_e4m3fn_scaled.safetensors",
"wan",
"default"
]
},
{
"id": 49,
"pos": [-535.2967529296875, 342.3277587890625],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 3,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [96] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"positive_prompt",
"a fox moving quickly in a beautiful winter scenery nature trees mountains daytime tracking camera",
"Prompt",
"The text prompt to guide video generation. "
]
},
{
"id": 50,
"pos": [-526.2716064453125, 703.8343505859375],
"mode": 0,
"size": [400, 200],
"type": "ComfyUIDeployExternalText",
"flags": {},
"order": 4,
"inputs": [],
"outputs": [{ "name": "text", "type": "STRING", "links": [97] }],
"properties": { "Node name for S&R": "ComfyUIDeployExternalText" },
"widgets_values": [
"negative_prompt",
"色调艳丽,过曝,静态,细节模糊不清,字幕,风格,作品,画作,画面,静止,整体发灰,最差质量,低质量,JPEG压缩残留,丑陋的,残缺的,多余的手指,画得不好的手部,画得不好的脸部,畸形的,毁容的,形态畸形的肢体,手指融合,静止不动的画面,杂乱的背景,三条腿,背景人很多,倒着走",
"Negative Prompt",
"The negative prompt to use. Use it to address details that you don't want in the image. This could be colors, objects, scenery and even the small details (e.g. moustache, blurry, low resolution). "
]
},
{
"id": 40,
"pos": [516.926513671875, 619.59716796875],
"mode": 0,
"size": [315, 150],
"type": "EmptyHunyuanLatentVideo",
"flags": {},
"order": 10,
"inputs": [
{
"pos": [10, 36],
"link": 100,
"name": "width",
"type": "INT",
"widget": { "name": "width" }
},
{
"pos": [10, 60],
"link": 99,
"name": "height",
"type": "INT",
"widget": { "name": "height" }
}
],
"outputs": [
{ "name": "LATENT", "type": "LATENT", "links": [91], "slot_index": 0 }
],
"properties": { "Node name for S&R": "EmptyHunyuanLatentVideo" },
"widgets_values": [832, 480, 33, 1]
},
{
"id": 28,
"pos": [1460, 190],
"mode": 0,
"size": [870.8511352539062, 643.7430419921875],
"type": "SaveAnimatedWEBP",
"flags": {},
"order": 13,
"inputs": [{ "link": 56, "name": "images", "type": "IMAGE" }],
"outputs": [],
"properties": {},
"widgets_values": ["ComfyUI", 16, false, 90, "default"]
},
{
"id": 51,
"pos": [-522.7415161132812, 959.3386840820312],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 5,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [100], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["width", 832, "Width", "The Width of the Video. "]
},
{
"id": 52,
"pos": [-518.9917602539062, 1207.9444580078125],
"mode": 0,
"size": [453.5999755859375, 200],
"type": "ComfyUIDeployExternalNumberInt",
"flags": {},
"order": 6,
"inputs": [],
"outputs": [
{ "name": "value", "type": "INT", "links": [99], "slot_index": 0 }
],
"properties": { "Node name for S&R": "ComfyUIDeployExternalNumberInt" },
"widgets_values": ["height", 480, "Height", "The Height of the Video. "]
}
],
"config": {},
"groups": [
{
"id": 1,
"color": "#3f789e",
"flags": {},
"title": "Inputs",
"bounding": [
-560.9110717773438, 255.1485595703125, 500.94989013671875,
333.4786682128906
],
"font_size": 24
},
{
"id": 2,
"color": "#b06634",
"flags": {},
"title": "Additional",
"bounding": [
-556.5305786132812, 619.87548828125, 761.2673950195312,
811.6837768554688
],
"font_size": 24
}
],
"version": 0.4,
"last_link_id": 100,
"last_node_id": 52
}
+3 -3
View File
@@ -1,9 +1,9 @@
[project]
name = "comfyui-deploy"
description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
version = "1.1.0"
license = "LICENSE"
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg"]
version = "2.1.0"
license = { file = "LICENSE" }
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"]
[project.urls]
Repository = "https://github.com/BennyKok/comfyui-deploy"
-4
View File
@@ -1,4 +0,0 @@
/** @typedef {import('../../../web/scripts/api.js').api} API*/
import { api as _api } from '../../scripts/api.js';
/** @type {API} */
export const api = _api;
-4
View File
@@ -1,4 +0,0 @@
/** @typedef {import('../../../web/scripts/app.js').ComfyApp} ComfyApp*/
import { app as _app } from '../../scripts/app.js';
/** @type {ComfyApp} */
export const app = _app;
+624 -97
View File
@@ -1,8 +1,11 @@
import { app } from "./app.js";
import { api } from "./api.js";
import { ComfyWidgets, LGraphNode } from "./widgets.js";
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
// import { LGraphNode } from "../../scripts/widgets.js";
LGraphNode = LiteGraph.LGraphNode;
import { ComfyDialog, $el } from "../../scripts/ui.js";
import { generateDependencyGraph } from "https://esm.sh/comfyui-json@0.1.25";
import { ComfyDeploy } from "https://esm.sh/comfydeploy@0.0.19-beta.30";
import { ComfyDeploy } from "https://esm.sh/comfydeploy@2.0.0-beta.69";
const styles = `
.comfydeploy-menu-item {
@@ -47,6 +50,14 @@ function sendEventToCD(event, data) {
window.parent.postMessage(JSON.stringify(message), "*");
}
function sendDirectEventToCD(event, data) {
const message = {
type: event,
data: data,
};
window.parent.postMessage(message, "*");
}
function dispatchAPIEventData(data) {
const msg = JSON.parse(data);
@@ -157,12 +168,12 @@ function setSelectedWorkflowInfo(info) {
const VALID_TYPES = [
"STRING",
"combo",
"number",
"toggle",
"BOOLEAN",
"text",
"string",
"combo",
];
function hideWidget(node, widget, suffix = "") {
@@ -170,7 +181,9 @@ function hideWidget(node, widget, suffix = "") {
widget.origType = widget.type;
widget.origComputeSize = widget.computeSize;
widget.origSerializeValue = widget.serializeValue;
widget.computeSize = () => [0, -4];
// console.log(widget.origComputeSize);
// console.log(LiteGraph.NODE_SLOT_HEIGHT);
// widget.computeSize = () => [0, 0];
widget.type = CONVERTED_TYPE + suffix;
widget.serializeValue = () => {
if (!node.inputs) {
@@ -199,10 +212,37 @@ function getWidgetType(config) {
return { type };
}
const GET_CONFIG = Symbol();
async function convertToInput(node, widget, config) {
const { type } = getWidgetType(config);
console.log(node, widget, config);
const result = await app.extensionManager.dialog.prompt(
{
title: "Convert " + widget.name + " to external input",
message: "Input name",
defaultValue: widget.name,
}
);
if (!result) return;
// Check for duplicate input IDs across existing external input nodes
const existingInputIds = Object.values(app.graph.nodes)
.filter(n => n.type.startsWith("ComfyUIDeployExternal"))
.map(n => n.widgets_values?.[0])
.filter(Boolean);
if (existingInputIds.includes(result)) {
app.extensionManager.toast.add({
severity: 'error',
summary: 'Input ID already exists',
detail: 'Please choose a different name.',
life: 3000
});
return;
}
function convertToInput(node, widget, config) {
console.log(node);
if (node.type == "LoadImage") {
var inputNode = LiteGraph.createNode("ComfyUIDeployExternalImage");
console.log(widget);
@@ -224,54 +264,94 @@ function convertToInput(node, widget, config) {
const links = app.graph.links;
console.log(currentOutputsLinks);
// console.log(currentOutputsLinks);
for (let i = 0; i < currentOutputsLinks.length; i++) {
const link = currentOutputsLinks[i];
const llink = links[link];
console.log(links[link]);
setTimeout(
() => inputNode.connect(0, llink.target_id, llink.target_slot),
100,
);
}
if (currentOutputsLinks)
for (let i = 0; i < currentOutputsLinks.length; i++) {
const link = currentOutputsLinks[i];
const llink = links[link];
console.log(links[link]);
setTimeout(
() => inputNode.connect(0, llink.target_id, llink.target_slot),
100,
);
}
node.connect(0, inputNode, 0);
return null;
}
hideWidget(node, widget);
const { type } = getWidgetType(config);
const sz = node.size;
const inputIsOptional = !!widget.options?.inputIsOptional;
const input = node.addInput(widget.name, type, {
widget: { name: widget.name, [GET_CONFIG]: () => config },
...(inputIsOptional ? { shape: LiteGraph.SlotShape.HollowCircle } : {}),
});
for (const widget2 of node.widgets) {
widget2.last_y += LiteGraph.NODE_SLOT_HEIGHT;
}
node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]);
let externalNode = "";
let inputId = result;
if (type == "STRING") {
var inputNode = LiteGraph.createNode("ComfyUIDeployExternalText");
console.log(widget);
const index = node.inputs.findIndex((x) => x.name == widget.name);
console.log(node.widgets_values, index);
if (type === "INT") {
externalNode = "ComfyUIDeployExternalNumberInt";
// inputId = "input_number";
}
if (type === "FLOAT") {
externalNode = "ComfyUIDeployExternalNumberSlider";
// inputId = "input_number";
}
if (type === "STRING") {
externalNode = "ComfyUIDeployExternalText";
// inputId = "input_text";
}
if (type === "COMBO") {
externalNode = "ComfyUIDeployExternalEnum";
// inputId = "input_enum";
}
if (!externalNode || !inputId) return;
node.convertWidgetToInput(widget);
var inputNode = LiteGraph.createNode(externalNode, "External Input: " + inputId);
// if (type === "COMBO") {
// inputNode = LiteGraph.createNode(externalNode, "External Input: " + inputId, {
// dynamic_enum_options: config[0],
// });
// console.log(inputNode);
// const options = config[0];
// console.log(options);
// } else {
// inputNode = LiteGraph.createNode(externalNode, "External Input: " + inputId);
// }
var options;
const index = node.inputs.findIndex((x) => x.name == widget.name);
if (type === "COMBO") {
options = widget.options?.values ?? config[0];
inputNode.configure({
widgets_values: ["input_text", widget.value],
widgets_values: [inputId, widget.value, JSON.stringify(options)],
});
} else
{
inputNode.configure({
widgets_values: [inputId, widget.value],
});
inputNode.id = ++app.graph.last_node_id;
inputNode.pos = node.pos;
inputNode.pos[0] -= node.size[0] + 40;
console.log(inputNode);
console.log(app.graph);
app.graph.add(inputNode);
inputNode.connect(0, node, index);
}
inputNode.id = ++app.graph.last_node_id;
inputNode.pos = node.pos;
inputNode.pos[0] -= node.size[0] + 160;
return input;
if (type === "COMBO") {
console.log(inputNode);
console.log(options);
inputNode.widgets.find((x) => x.name == "default_value").options.values = options;
}
app.graph.add(inputNode);
inputNode.connect(0, node, index);
app.graph.setDirtyCanvas(true, true);
return node.inputs.find((x) => x.name == widget.name);
}
const CONVERTED_TYPE = "converted-widget";
@@ -284,7 +364,12 @@ function getConfig(widgetName) {
);
}
function isConvertibleWidget(widget, config) {
function isConvertibleWidget(node, widget, config) {
// console.log(config);
if (node.type === "LoadImage" && widget.type === "combo" && widget.name == "image") {
return true;
}
return (
(VALID_TYPES.includes(widget.type) || VALID_TYPES.includes(config[0])) &&
!widget.options?.forceInput
@@ -431,11 +516,11 @@ const ext = {
w.type,
w.options || {},
];
if (isConvertibleWidget(w, config)) {
if (isConvertibleWidget(this, w, config)) {
toInput.push({
content: `Convert ${w.name} to external input`,
callback: /* @__PURE__ */ __name(
() => convertToInput(this, w, config),
async () => convertToInput(this, w, config),
"callback",
),
className: "comfydeploy-menu-item",
@@ -483,6 +568,20 @@ const ext = {
return r;
};
if (
nodeData?.input?.optional?.default_value_url?.[1]?.image_preview === true
) {
nodeData.input.optional.default_value_url = ["IMAGEPREVIEW"];
console.log(nodeData.input.optional.default_value_url);
}
if (
nodeData?.input?.optional?.default_value?.[1]?.dynamic_enum === true
) {
nodeData.input.optional.default_value = ["DYNAMIC_ENUM"];
// console.log(nodeData.input.optional.default_value);
}
// const origonNodeCreated = nodeType.prototype.onNodeCreated;
// nodeType.prototype.onNodeCreated = function () {
// const r = origonNodeCreated
@@ -516,6 +615,10 @@ const ext = {
// return r
// };
},
// async nodeCreated(node) {
// },
registerCustomNodes() {
/** @type {LGraphNode}*/
@@ -609,6 +712,106 @@ const ext = {
ComfyDeploy.category = "deploy";
},
getCustomWidgets() {
return {
IMAGEPREVIEW(node, inputName, inputData) {
// Find or create the URL input widget
const urlWidget = node.addWidget(
"string",
inputName,
/* value=*/ "",
() => { },
{ serialize: true },
);
const buttonWidget = node.addWidget(
"button",
"Open Assets Browser",
/* value=*/ "",
() => {
sendEventToCD("assets", {
node: node.id,
inputName: inputName,
});
// console.log("load image");
},
{ serialize: false },
);
console.log(node.widgets);
console.log("urlWidget", urlWidget);
// Add image preview functionality
function showImage(url) {
const img = new Image();
img.onload = () => {
node.imgs = [img];
app.graph.setDirtyCanvas(true);
node.setSizeForImage?.();
};
img.onerror = () => {
node.imgs = [];
app.graph.setDirtyCanvas(true);
};
img.src = url;
}
// Set up URL widget value handling
let default_value = urlWidget.value;
Object.defineProperty(urlWidget, "value", {
set: function (value) {
this._real_value = value;
// Preview image when URL changes
if (value) {
showImage(value);
}
},
get: function () {
return this._real_value || default_value;
},
});
// Show initial image if URL exists
requestAnimationFrame(() => {
if (urlWidget.value) {
showImage(urlWidget.value);
}
});
return { widget: urlWidget };
},
DYNAMIC_ENUM(node, inputName, inputData) {
// console.log("DYNAMIC_ENUM", JSON.parse(JSON.stringify(node)), inputName, inputData);
const enumWidget = node.addWidget(
"combo",
inputName,
"",
{ serialize: true, values: [] },
);
return { widget: enumWidget };
},
};
},
async afterConfigureGraph() {
app.graph.nodes.forEach(node => {
if (node.type === "ComfyUIDeployExternalEnum") {
const default_value_index = node.widgets.findIndex(x => x.name === "default_value");
const options_index = node.widgets.findIndex(x => x.name === "options");
var dynamic_enum_options = [node.widgets[default_value_index].value];
if (node.widgets[options_index].value) {
dynamic_enum_options = JSON.parse(node.widgets[options_index].value);
}
// console.log("dynamic_enum_options", dynamic_enum_options);
node.widgets[default_value_index].options.values = dynamic_enum_options;
}
});
},
async setup() {
// const graphCanvas = document.getElementById("graph-canvas");
@@ -626,7 +829,7 @@ const ext = {
try {
await window["app"].ui.settings.setSettingValueAsync(
"Comfy.Validation.Workflows",
false,
true,
);
} catch (error) {
console.warning(
@@ -636,6 +839,7 @@ const ext = {
}
console.log("loadGraphData");
app.loadGraphData(comfyUIWorkflow);
sendEventToCD("graph_loaded");
}
} else if (message.type === "deploy") {
// deployWorkflow();
@@ -650,11 +854,35 @@ const ext = {
console.warn("api.handlePromptGenerated is not a function");
}
sendEventToCD("cd_plugin_onQueuePrompt", prompt);
} else if (message.type === "configure_queue_buttons") {
addQueueButtons(message.data);
} else if (message.type === "configure_menu_right_buttons") {
addMenuRightButtons(message.data);
} else if (message.type === "configure_menu_buttons") {
addMenuButtons(message.data);
} else if (message.type === "get_prompt") {
const prompt = await app.graphToPrompt();
sendEventToCD("cd_plugin_onGetPrompt", prompt);
} else if (message.type === "event") {
dispatchAPIEventData(message.data);
} else if (message.type === "update_widget") {
// New handler for updating widget values
const { nodeId, widgetName, value } = message.data;
const node = app.graph.getNodeById(nodeId);
if (!node) {
console.warn(`Node with ID ${nodeId} not found`);
return;
}
const widget = node.widgets?.find((w) => w.name === widgetName);
if (!widget) {
console.warn(`Widget ${widgetName} not found in node ${nodeId}`);
return;
}
widget.value = value;
app.graph.setDirtyCanvas(true);
} else if (message.type === "add_node") {
console.log("add node", message.data);
app.graph.beforeChange();
@@ -752,10 +980,9 @@ const ext = {
);
await app.ui.settings.setSettingValueAsync(
"Comfy.Sidebar.Location",
"right",
"left",
);
localStorage.setItem("Comfy.MenuPosition.Docked", "true");
window.location.reload();
// localStorage.setItem("Comfy.MenuPosition.Docked", "true");
console.log("native mode manmanman");
} catch (error) {
console.error("Error setting validation to false", error);
@@ -772,6 +999,7 @@ const ext = {
}
})(app.graph.onAfterChange);
sendEventToCD("cd_plugin_setup");
},
};
@@ -806,10 +1034,10 @@ function createDynamicUIHtml(data) {
<h3 style="font-size: 14px; font-weight: semibold; margin-bottom: 8px;">Missing Nodes</h3>
<p style="font-size: 12px;">These nodes are not found with any matching custom_nodes in the ComfyUI Manager Database</p>
${data.missing_nodes
.map((node) => {
return `<p style="font-size: 14px; color: #d69e2e;">${node}</p>`;
})
.join("")}
.map((node) => {
return `<p style="font-size: 14px; color: #d69e2e;">${node}</p>`;
})
.join("")}
</div>
`;
}
@@ -817,17 +1045,14 @@ function createDynamicUIHtml(data) {
Object.values(data.custom_nodes).forEach((node) => {
html += `
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 16px;">
<a href="${
node.url
}" target="_blank" style="font-size: 18px; font-weight: semibold; color: white; text-decoration: none;">${
node.name
}</a>
<a href="${node.url
}" target="_blank" style="font-size: 18px; font-weight: semibold; color: white; text-decoration: none;">${node.name
}</a>
<p style="font-size: 14px; color: #4b5563;">${node.hash}</p>
${
node.warning
? `<p style="font-size: 14px; color: #d69e2e;">${node.warning}</p>`
: ""
}
${node.warning
? `<p style="font-size: 14px; color: #d69e2e;">${node.warning}</p>`
: ""
}
</div>
`;
});
@@ -841,9 +1066,8 @@ function createDynamicUIHtml(data) {
Object.entries(data.models).forEach(([section, items]) => {
html += `
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 8px; padding-bottom: 8px;">
<h3 style="font-size: 18px; font-weight: semibold; margin-bottom: 8px;">${
section.charAt(0).toUpperCase() + section.slice(1)
}</h3>`;
<h3 style="font-size: 18px; font-weight: semibold; margin-bottom: 8px;">${section.charAt(0).toUpperCase() + section.slice(1)
}</h3>`;
items.forEach((item) => {
html += `<p style="font-size: 14px; color: ${textColor};">${item.name}</p>`;
});
@@ -859,9 +1083,8 @@ function createDynamicUIHtml(data) {
Object.entries(data.files).forEach(([section, items]) => {
html += `
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 8px; padding-bottom: 8px;">
<h3 style="font-size: 18px; font-weight: semibold; margin-bottom: 8px;">${
section.charAt(0).toUpperCase() + section.slice(1)
}</h3>`;
<h3 style="font-size: 18px; font-weight: semibold; margin-bottom: 8px;">${section.charAt(0).toUpperCase() + section.slice(1)
}</h3>`;
items.forEach((item) => {
html += `<p style="font-size: 14px; color: ${textColor};">${item.name}</p>`;
});
@@ -962,6 +1185,8 @@ async function deployWorkflow() {
const prompt = await app.graphToPrompt();
let deps = undefined;
console.log(prompt);
if (includeDeps) {
loadingDialog.showLoading("Fetching existing version");
@@ -1288,8 +1513,6 @@ function addButton() {
app.registerExtension(ext);
import { ComfyDialog, $el } from "../../scripts/ui.js";
export class InfoDialog extends ComfyDialog {
constructor() {
super();
@@ -1381,14 +1604,12 @@ export class LoadingDialog extends ComfyDialog {
showLoading(title, message) {
this.show(`
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
<h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 12px;">${title} ${
this.loadingIcon
}</h3>
${
message
? `<label style="max-width: 100%; white-space: pre-wrap; word-wrap: break-word;">${message}</label>`
: ""
}
<h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 12px;">${title} ${this.loadingIcon
}</h3>
${message
? `<label style="max-width: 100%; white-space: pre-wrap; word-wrap: break-word;">${message}</label>`
: ""
}
</div>
`);
}
@@ -1654,21 +1875,17 @@ export class ConfigDialog extends ComfyDialog {
</label>
<label style="color: white; width: 100%;">
Endpoint:
<input id="endpoint" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="text" value="${
data.endpoint
}">
<input id="endpoint" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="text" value="${data.endpoint
}">
</label>
<div style="color: white;">
API Key: User / Org <button style="font-size: 18px;">${
data.displayName ?? ""
}</button>
<input id="apiKey" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="password" value="${
data.apiKey
}">
API Key: User / Org <button style="font-size: 18px;">${data.displayName ?? ""
}</button>
<input id="apiKey" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;" type="password" value="${data.apiKey
}">
<button id="loginButton" style="margin-top: 8px; width: 100%; height:40px; box-sizing: border-box; padding: 0px 6px;">
${
data.apiKey ? "Re-login with ComfyDeploy" : "Login with ComfyDeploy"
}
${data.apiKey ? "Re-login with ComfyDeploy" : "Login with ComfyDeploy"
}
</button>
</div>
</div>
@@ -1760,7 +1977,7 @@ app.extensionManager.registerSidebarTab({
<div style="padding: 20px;">
<h3>Comfy Deploy</h3>
<div id="deploy-container" style="margin-bottom: 20px;"></div>
<div id="workflows-container">
<div id="workflows-container" style="display: none;">
<h4>Your Workflows</h4>
<div id="workflows-loading" style="display: flex; justify-content: center; align-items: center; height: 100px;">
${loadingIcon}
@@ -1848,7 +2065,7 @@ async function loadWorkflowApi(versionId) {
console.log("Workflow API loaded:", response);
await window["app"].ui.settings.setSettingValueAsync(
"Comfy.Validation.Workflows",
false,
true,
);
app.loadGraphData(response.workflow);
// You might want to update the UI or trigger some action in ComfyUI here
@@ -1860,10 +2077,16 @@ async function loadWorkflowApi(versionId) {
const orginal_fetch_api = api.fetchApi;
api.fetchApi = async (route, options) => {
console.log("Fetch API called with args:", route, options, ext.native_mode);
// console.log("Fetch API called with args:", route, options, ext.native_mode);
if (route.startsWith("/prompt") && ext.native_mode) {
const info = await getSelectedWorkflowInfo();
if (!info.workflow_id) {
console.log("No workflow id found, fallback to original fetch");
return await orginal_fetch_api.call(api, route, options);
}
console.log("info", info);
if (info) {
const body = JSON.parse(options.body);
@@ -1877,6 +2100,7 @@ api.fetchApi = async (route, options) => {
workflow_id: info.workflow_id,
native_run_api_endpoint: info.native_run_api_endpoint,
gpu_event_id: info.gpu_event_id,
gpu: info.gpu,
};
return await fetch("/comfyui-deploy/run", {
@@ -1892,3 +2116,306 @@ api.fetchApi = async (route, options) => {
return await orginal_fetch_api.call(api, route, options);
};
// Intercept window drag and drop events
const originalDropHandler = document.ondrop;
document.ondrop = async (e) => {
console.log("Drop event intercepted:", e);
// Prevent default browser behavior
e.preventDefault();
// Handle files if present
if (e.dataTransfer?.files?.length > 0) {
const files = Array.from(e.dataTransfer.files);
// Send file data to parent directly as JSON
sendDirectEventToCD("file_drop", {
files: files,
x: e.clientX,
y: e.clientY,
timestamp: Date.now(),
});
}
// Call original handler if exists
if (originalDropHandler) {
originalDropHandler(e);
}
};
const originalDragEnterHandler = document.ondragenter;
document.ondragenter = (e) => {
// Prevent default to allow drop
e.preventDefault();
// Send dragenter event to parent directly as JSON
sendDirectEventToCD("file_dragenter", {
x: e.clientX,
y: e.clientY,
timestamp: Date.now(),
});
if (originalDragEnterHandler) {
originalDragEnterHandler(e);
}
};
const originalDragLeaveHandler = document.ondragleave;
document.ondragleave = (e) => {
// Prevent default to allow drop
e.preventDefault();
// Send dragleave event to parent directly as JSON
sendDirectEventToCD("file_dragleave", {
x: e.clientX,
y: e.clientY,
timestamp: Date.now(),
});
if (originalDragLeaveHandler) {
originalDragLeaveHandler(e);
}
};
const originalDragOverHandler = document.ondragover;
document.ondragover = (e) => {
// Prevent default to allow drop
e.preventDefault();
// Send dragover event to parent directly as JSON
sendDirectEventToCD("file_dragover", {
x: e.clientX,
y: e.clientY,
timestamp: Date.now(),
});
if (originalDragOverHandler) {
originalDragOverHandler(e);
}
};
// Function to create a single button
function createQueueButton(config) {
const button = document.createElement("button");
button.id = `cd-button-${config.id}`;
button.className =
"p-button p-component p-button-icon-only p-button-secondary p-button-text";
button.innerHTML = `
<span class="p-button-icon pi ${config.icon}"></span>
<span class="p-button-label">&nbsp;</span>
`;
button.onclick = () => {
const eventData =
typeof config.eventData === "function"
? config.eventData()
: config.eventData || {};
sendEventToCD(config.event, eventData);
};
button.setAttribute("data-pd-tooltip", config.tooltip);
return button;
}
// Function to add buttons to queue group
function addQueueButtons(buttonConfigs = DEFAULT_BUTTONS) {
const queueButtonGroup = document.querySelector(".queue-button-group.flex");
if (!queueButtonGroup) return;
// Remove any existing CD buttons
const existingButtons =
queueButtonGroup.querySelectorAll('[id^="cd-button-"]');
existingButtons.forEach((button) => button.remove());
// Add new buttons
buttonConfigs.forEach((config) => {
const button = createQueueButton(config);
queueButtonGroup.appendChild(button);
});
}
// addMenuRightButtons([
// {
// id: "cd-button-save-image",
// icon: "pi-save",
// label: "Snapshot",
// tooltip: "Save the current image to your output directory.",
// event: "save_image",
// eventData: () => ({}),
// },
// ]);
// addMenuLeftButtons([
// {
// id: "cd-button-back",
// icon: `<svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
// <path d="M15 18L9 12L15 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
// </svg>`,
// tooltip: "Go back to the previous page.",
// event: "back",
// eventData: () => ({}),
// },
// ]);
// addMenuButtons({
// containerSelector: "body > div.comfyui-body-top > div",
// buttonConfigs: [
// {
// id: "cd-button-workflow-1",
// icon: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m16 3l4 4l-4 4m-6-4h10M8 13l-4 4l4 4m-4-4h9"/></svg>`,
// label: "Workflow",
// tooltip: "Go to Workflow 1",
// event: "workflow_1",
// // btnClasses: "",
// eventData: () => ({}),
// },
// {
// id: "cd-button-workflow-3",
// // icon: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m16 3l4 4l-4 4m-6-4h10M8 13l-4 4l4 4m-4-4h9"/></svg>`,
// label: "v1",
// tooltip: "Go to Workflow 1",
// event: "workflow_1",
// // btnClasses: "",
// eventData: () => ({}),
// },
// {
// id: "cd-button-workflow-2",
// icon: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="M12 3v6"/><circle cx="12" cy="12" r="3"/><path d="M12 15v6"/></g></svg>`,
// label: "Commit",
// tooltip: "Commit the current workflow",
// event: "commit",
// style: {
// backgroundColor: "oklch(.476 .114 61.907)",
// },
// eventData: () => ({}),
// },
// ],
// buttonIdPrefix: "cd-button-workflow-",
// insertBefore:
// "body > div.comfyui-body-top > div > div.flex-grow.min-w-0.app-drag.h-full",
// // containerStyle: { order: "3" }
// });
// addMenuButtons({
// containerSelector:
// "body > div.comfyui-body-top > div > div.flex-grow.min-w-0.app-drag.h-full",
// clearContainer: true,
// buttonConfigs: [],
// buttonIdPrefix: "cd-button-p-",
// containerStyle: { order: "-1" },
// });
// Function to add buttons to a menu container
function addMenuButtons(options) {
const {
containerSelector,
buttonConfigs,
buttonIdPrefix = "cd-button-",
containerClass = "comfyui-button-group",
containerStyle = {},
clearContainer = false,
insertBefore = null, // New option to specify selector for insertion point
} = options;
const menuContainer = document.querySelector(containerSelector);
if (!menuContainer) return;
// Remove any existing CD buttons
const existingButtons = document.querySelectorAll(
`[id^="${buttonIdPrefix}"]`,
);
existingButtons.forEach((button) => button.remove());
const container = document.createElement("div");
container.className = containerClass;
// Apply container styles
Object.assign(container.style, containerStyle);
// Clear existing content if specified
if (clearContainer) {
menuContainer.innerHTML = "";
}
// Create and add buttons
buttonConfigs.forEach((config) => {
const button = createMenuButton({
...config,
idPrefix: buttonIdPrefix,
});
container.appendChild(button);
});
// Insert before specified element if provided, otherwise append
if (insertBefore) {
const targetElement = menuContainer.querySelector(insertBefore);
if (targetElement) {
menuContainer.insertBefore(container, targetElement);
} else {
menuContainer.appendChild(container);
}
} else {
menuContainer.appendChild(container);
}
}
function createMenuButton(config) {
const {
id,
icon,
label,
btnClasses = "",
tooltip,
event,
eventData,
idPrefix,
style = {},
} = config;
const button = document.createElement("button");
button.id = `${idPrefix}${id}`;
button.className = `comfyui-button ${btnClasses}`;
Object.assign(button.style, style);
// Only add icon if provided
const iconHtml = icon
? icon.startsWith("<svg")
? icon
: `<span class="p-button-icon pi ${icon}"></span>`
: "";
button.innerHTML = `
${iconHtml}
${label ? `<span class="p-button-label text-sm">${label}</span>` : ""}
`;
button.onclick = () => {
const data =
typeof eventData === "function" ? eventData() : eventData || {};
sendEventToCD(event, data);
};
if (tooltip) {
button.setAttribute("data-pd-tooltip", tooltip);
}
return button;
}
// Refactored menu button functions
function addMenuLeftButtons(buttonConfigs) {
addMenuButtons({
containerSelector: "body > div.comfyui-body-top > div",
buttonConfigs,
buttonIdPrefix: "cd-button-left-",
containerStyle: { order: "-1" },
});
}
function addMenuRightButtons(buttonConfigs) {
addMenuButtons({
containerSelector: ".comfyui-menu-right .flex",
buttonConfigs,
buttonIdPrefix: "cd-button-",
containerStyle: {},
});
}
-18
View File
@@ -1,18 +0,0 @@
// /** @typedef {import('../../../web/scripts/api.js').api} API*/
// import { api as _api } from "../../scripts/api.js";
// /** @type {API} */
// export const api = _api;
/** @typedef {typeof import('../../../web/scripts/widgets.js').ComfyWidgets} Widgets*/
import { ComfyWidgets as _ComfyWidgets } from "../../scripts/widgets.js";
/**
* @type {Widgets}
*/
export const ComfyWidgets = _ComfyWidgets;
// import { LGraphNode as _LGraphNode } from "../../types/litegraph.js";
/** @typedef {typeof import('../../../web/types/litegraph.js').LGraphNode} LGraphNode*/
/** @type {LGraphNode}*/
export const LGraphNode = LiteGraph.LGraphNode;