Compare commits
78
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1486f97be1 | ||
|
|
7d65d23f84 | ||
|
|
a91edf6371 | ||
|
|
967709dd07 | ||
|
|
07a827d56b | ||
|
|
7f6eea361e | ||
|
|
14829a2b12 | ||
|
|
03c4e2d85d | ||
|
|
bad8b1104f | ||
|
|
ac8779dc54 | ||
|
|
2a5223f2f0 | ||
|
|
f02aef4cb3 | ||
|
|
22458a1cd6 | ||
|
|
0a58eba554 | ||
|
|
def54df9c2 | ||
|
|
32a950afe8 | ||
|
|
8130779d94 | ||
|
|
4cbd2a8225 | ||
|
|
d6fb2daeff | ||
|
|
a91effe3c8 | ||
|
|
c015b710fe | ||
|
|
2738d1913a | ||
|
|
3f4c11e3f1 | ||
|
|
089bad5560 | ||
|
|
46010e1dd5 | ||
|
|
52d876fa67 | ||
|
|
f7e7eb19d0 | ||
|
|
cb03b6718e | ||
|
|
7b734c415a | ||
|
|
64d3ec6b45 | ||
|
|
c47865ec26 | ||
|
|
b889f79baf | ||
|
|
1d8fed3534 | ||
|
|
a557788e70 | ||
|
|
05cccaffa2 | ||
|
|
85af9dd68f | ||
|
|
233615ea25 | ||
|
|
8b6aabbfaa | ||
|
|
c72539078c | ||
|
|
95fc642782 | ||
|
|
d88ca5b748 | ||
|
|
e86a484160 | ||
|
|
5d85bfd38f | ||
|
|
77eb9f9805 | ||
|
|
266e9d1024 | ||
|
|
dc7234640c | ||
|
|
4e8417d501 | ||
|
|
62609f9c6a | ||
|
|
438401b8c7 | ||
|
|
9beac36d0f | ||
|
|
313ce956fd | ||
|
|
e18d980b77 | ||
|
|
fe2d9b82b5 | ||
|
|
22858abb31 | ||
|
|
99a5f71b2c | ||
|
|
1810d4ecdb | ||
|
|
3593f0b37e | ||
|
|
858a3bcda4 | ||
|
|
484147f5b4 | ||
|
|
61a8f1123e | ||
|
|
46b056290d | ||
|
|
2cf0497823 | ||
|
|
4b5eec4e4c | ||
|
|
17c48c7d4f | ||
|
|
cd3a2ff547 | ||
|
|
62f8e388bb | ||
|
|
f86c08baed | ||
|
|
7f64bcc3ae | ||
|
|
8359d1c783 | ||
|
|
dca27fe2ba | ||
|
|
0ae70835c4 | ||
|
|
99cda529a9 | ||
|
|
7e640a0da4 | ||
|
|
f19b80a25d | ||
|
|
1ba7640d7f | ||
|
|
6ecf9c782b | ||
|
|
1904b4bdbf | ||
|
|
4112e0ec8c |
@@ -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 }}
|
||||
|
||||
@@ -96,10 +96,6 @@ Major areas
|
||||
|
||||
# Self Hosting with Vercel
|
||||
|
||||
[](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
@@ -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"]
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import os
|
||||
import io
|
||||
import torchaudio
|
||||
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 {
|
||||
@@ -28,30 +28,55 @@ class ComfyUIDeployExternalAudio:
|
||||
"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):
|
||||
if audio_file and audio_file != "":
|
||||
if audio_file.startswith(('http://', 'https://')):
|
||||
# Handle URL input
|
||||
import requests
|
||||
response = requests.get(audio_file)
|
||||
audio_data = io.BytesIO(response.content)
|
||||
waveform, sample_rate = torchaudio.load(audio_data)
|
||||
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:
|
||||
# Handle local file
|
||||
audio_path = get_annotated_filepath(audio_file)
|
||||
waveform, sample_rate = torchaudio.load(audio_path)
|
||||
|
||||
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)"}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ComfyUIDeployExternalAudio": "External Audio (ComfyUI Deploy)"
|
||||
}
|
||||
|
||||
@@ -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}")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
@@ -10,6 +10,7 @@ class ComfyUIDeployExternalEXR:
|
||||
RETURN_TYPES = ("IMAGE", "MASK")
|
||||
RETURN_NAMES = ("image", "mask")
|
||||
FUNCTION = "load_exr"
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
|
||||
@@ -48,10 +48,8 @@ class ComfyUIDeployExternalFaceModel:
|
||||
|
||||
RETURN_TYPES = (WILDCARD,)
|
||||
RETURN_NAMES = ("path",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "deploy"
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
|
||||
def run(
|
||||
self,
|
||||
|
||||
@@ -29,10 +29,8 @@ class ComfyUIDeployExternalImage:
|
||||
|
||||
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, default_value_url=None):
|
||||
image = default_value
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()):
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
class ComfyUIDeployExternalNumberSliderInt:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"input_id": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "input_number_slider_int"},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"default_value": (
|
||||
"INT",
|
||||
{"multiline": True, "display": "number", "min": -2147483647, "max": 2147483647, "default": 1, "step": 1},
|
||||
),
|
||||
"min_value": (
|
||||
"INT",
|
||||
{"multiline": True, "display": "number", "min": -2147483647, "max": 2147483647, "default": 0, "step": 1},
|
||||
),
|
||||
"max_value": (
|
||||
"INT",
|
||||
{"multiline": True, "display": "number", "min": -2147483647, "max": 2147483647, "default": 10, "step": 1},
|
||||
),
|
||||
"display_name": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": ""},
|
||||
),
|
||||
"description": (
|
||||
"STRING",
|
||||
{"multiline": True, "default": ""},
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("value",)
|
||||
FUNCTION = "run"
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
|
||||
def run(self, input_id, default_value=None, min_value=0, max_value=10, display_name=None, description=None):
|
||||
try:
|
||||
int_value = int(round(float(input_id)))
|
||||
if min_value <= int_value <= max_value:
|
||||
print("my integer", int_value)
|
||||
return [int_value]
|
||||
else:
|
||||
print("Integer out of range. Returning default value:", default_value)
|
||||
return [default_value]
|
||||
except (ValueError, TypeError):
|
||||
print("Invalid input. Returning default value:", default_value)
|
||||
return [default_value]
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalNumberSliderInt": ComfyUIDeployExternalNumberSliderInt}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyUIDeployExternalNumberSliderInt": "External Number Slider Int (ComfyUI Deploy)"}
|
||||
@@ -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)"
|
||||
}
|
||||
@@ -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=""):
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -748,36 +748,64 @@ class ComfyUIDeployExternalVideo:
|
||||
file_parts = f.split(".")
|
||||
if len(file_parts) > 1 and (file_parts[-1] in video_extensions):
|
||||
files.append(f)
|
||||
return {"required": {
|
||||
"input_id": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "input_video"},
|
||||
),
|
||||
"force_rate": ("INT", {"default": 0, "min": 0, "max": 60, "step": 1}),
|
||||
"force_size": (["Disabled", "Custom Height", "Custom Width", "Custom", "256x?", "?x256", "256x256", "512x?", "?x512", "512x512"],),
|
||||
"custom_width": ("INT", {"default": 512, "min": 0, "max": DIMMAX, "step": 8}),
|
||||
"custom_height": ("INT", {"default": 512, "min": 0, "max": DIMMAX, "step": 8}),
|
||||
"frame_load_cap": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}),
|
||||
"skip_first_frames": ("INT", {"default": 0, "min": 0, "max": BIGMAX, "step": 1}),
|
||||
"select_every_nth": ("INT", {"default": 1, "min": 1, "max": BIGMAX, "step": 1}),
|
||||
},
|
||||
"optional": {
|
||||
"meta_batch": ("VHS_BatchManager",),
|
||||
"vae": ("VAE",),
|
||||
"default_video": (sorted(files),),
|
||||
"display_name": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": ""},
|
||||
),
|
||||
"description": (
|
||||
"STRING",
|
||||
{"multiline": True, "default": ""},
|
||||
),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID"
|
||||
},
|
||||
}
|
||||
return {
|
||||
"required": {
|
||||
"input_id": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": "input_video"},
|
||||
),
|
||||
"force_rate": ("INT", {"default": 0, "min": 0, "max": 60, "step": 1}),
|
||||
"force_size": (
|
||||
[
|
||||
"Disabled",
|
||||
"Custom Height",
|
||||
"Custom Width",
|
||||
"Custom",
|
||||
"256x?",
|
||||
"?x256",
|
||||
"256x256",
|
||||
"512x?",
|
||||
"?x512",
|
||||
"512x512",
|
||||
],
|
||||
),
|
||||
"custom_width": (
|
||||
"INT",
|
||||
{"default": 512, "min": 0, "max": DIMMAX, "step": 8},
|
||||
),
|
||||
"custom_height": (
|
||||
"INT",
|
||||
{"default": 512, "min": 0, "max": DIMMAX, "step": 8},
|
||||
),
|
||||
"frame_load_cap": (
|
||||
"INT",
|
||||
{"default": 0, "min": 0, "max": BIGMAX, "step": 1},
|
||||
),
|
||||
"skip_first_frames": (
|
||||
"INT",
|
||||
{"default": 0, "min": 0, "max": BIGMAX, "step": 1},
|
||||
),
|
||||
"select_every_nth": (
|
||||
"INT",
|
||||
{"default": 1, "min": 1, "max": BIGMAX, "step": 1},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"meta_batch": ("VHS_BatchManager",),
|
||||
"vae": ("VAE",),
|
||||
"default_video": (sorted(files),),
|
||||
"display_name": (
|
||||
"STRING",
|
||||
{"multiline": False, "default": ""},
|
||||
),
|
||||
"description": (
|
||||
"STRING",
|
||||
{"multiline": True, "default": ""},
|
||||
),
|
||||
"default_value_url": ("STRING", {"image_preview": True, "default": ""}),
|
||||
},
|
||||
"hidden": {"unique_id": "UNIQUE_ID"},
|
||||
}
|
||||
|
||||
CATEGORY = "Video Helper Suite 🎥🅥🅗🅢"
|
||||
|
||||
@@ -791,6 +819,7 @@ class ComfyUIDeployExternalVideo:
|
||||
)
|
||||
|
||||
FUNCTION = "load_video"
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
|
||||
def load_video(self, **kwargs):
|
||||
input_id = kwargs.get("input_id")
|
||||
@@ -803,16 +832,21 @@ class ComfyUIDeployExternalVideo:
|
||||
select_every_nth = kwargs.get("select_every_nth")
|
||||
meta_batch = kwargs.get("meta_batch")
|
||||
unique_id = kwargs.get("unique_id")
|
||||
|
||||
default_value_url = kwargs.get("default_value_url")
|
||||
|
||||
input_dir = folder_paths.get_input_directory()
|
||||
if input_id.startswith("http"):
|
||||
if input_id.startswith("http") or (
|
||||
default_value_url and default_value_url.startswith("http")
|
||||
):
|
||||
import requests
|
||||
|
||||
print("Fetching video from URL: ", input_id)
|
||||
response = requests.get(input_id, stream=True)
|
||||
# Use input_id if it's a URL, otherwise use default_value_url
|
||||
url = input_id if input_id.startswith("http") else default_value_url
|
||||
|
||||
print("Fetching video from URL: ", url)
|
||||
response = requests.get(url, stream=True)
|
||||
file_size = int(response.headers.get("Content-Length", 0))
|
||||
file_extension = input_id.split(".")[-1].split("?")[
|
||||
file_extension = url.split(".")[-1].split("?")[
|
||||
0
|
||||
] # Extract extension and handle URLs with parameters
|
||||
if file_extension not in video_extensions:
|
||||
|
||||
@@ -33,6 +33,7 @@ class ComfyDeployWebscoketImageInput:
|
||||
RETURN_NAMES = ("images",)
|
||||
|
||||
FUNCTION = "run"
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(s, input_id):
|
||||
|
||||
@@ -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)"}
|
||||
@@ -0,0 +1,78 @@
|
||||
# In file: comfyui-deploy/comfy-nodes/output_exr.py
|
||||
|
||||
import os
|
||||
import numpy as np
|
||||
import folder_paths
|
||||
|
||||
# Try to set up OpenCV for EXR writing.
|
||||
try:
|
||||
os.environ["OPENCV_IO_ENABLE_OPENEXR"] = "1"
|
||||
import cv2
|
||||
OPENCV_AVAILABLE = True
|
||||
except ImportError:
|
||||
print("Warning: OpenCV not found for ComfyDeployOutputEXR. Please add opencv-python-headless to requirements.txt")
|
||||
OPENCV_AVAILABLE = False
|
||||
|
||||
# ALIGNED: Renamed class to match project conventions
|
||||
class ComfyDeployOutputEXR:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE", ),
|
||||
"filename_prefix": ("STRING", {"default": "ComfyDeploy_EXR"})
|
||||
},
|
||||
# ADDED: Optional output_id for consistency with other ComfyDeploy nodes
|
||||
"optional": {
|
||||
"output_id": ("STRING", {"multiline": False, "default": "output_exr"}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
# ALIGNED: Changed function name to 'run'
|
||||
FUNCTION = "run"
|
||||
OUTPUT_NODE = True
|
||||
# ALIGNED: Matched the category name
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
DESCRIPTION = "Saves the input images as EXR (HDR) files."
|
||||
|
||||
def run(self, images, filename_prefix="ComfyDeploy_EXR", output_id="output_exr"):
|
||||
if not OPENCV_AVAILABLE:
|
||||
raise ImportError("OpenCV is required to save EXR files. Please ensure opencv-python-headless is in requirements.txt.")
|
||||
|
||||
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 image in images:
|
||||
image_np = image.cpu().numpy()
|
||||
|
||||
if image_np.dtype != np.float32:
|
||||
image_np = image_np.astype(np.float32)
|
||||
|
||||
file = f"{filename}_{counter:05}.exr"
|
||||
file_path = os.path.join(full_output_folder, file)
|
||||
|
||||
image_np_bgr = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR)
|
||||
cv2.imwrite(file_path, image_np_bgr)
|
||||
|
||||
results.append({
|
||||
"filename": file,
|
||||
"subfolder": subfolder,
|
||||
"type": self.type,
|
||||
"output_id": output_id, # ADDED
|
||||
})
|
||||
counter += 1
|
||||
|
||||
return {"ui": {"images": results}}
|
||||
|
||||
# ALIGNED: Mappings are defined at the bottom of the node file in this project
|
||||
NODE_CLASS_MAPPINGS = {"ComfyDeployOutputEXR": ComfyDeployOutputEXR}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyDeployOutputEXR": "EXR Output (ComfyDeploy)"}
|
||||
@@ -27,6 +27,8 @@ class ComfyDeployOutputImage:
|
||||
),
|
||||
"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"},
|
||||
@@ -39,8 +41,7 @@ class ComfyDeployOutputImage:
|
||||
FUNCTION = "run"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
CATEGORY = "output"
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
DESCRIPTION = "Saves the input images to your ComfyUI output directory."
|
||||
|
||||
def run(
|
||||
|
||||
@@ -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)"}
|
||||
@@ -33,10 +33,8 @@ class ComfyDeployWebscoketImageOutput:
|
||||
|
||||
RETURN_TYPES = ()
|
||||
RETURN_NAMES = ("text",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "output"
|
||||
CATEGORY = "🔗ComfyDeploy"
|
||||
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(s, output_id):
|
||||
|
||||
+608
-113
@@ -31,6 +31,7 @@ import torch
|
||||
import psutil
|
||||
from collections import OrderedDict
|
||||
import io
|
||||
from urllib.parse import urlencode
|
||||
|
||||
# Global session
|
||||
client_session = None
|
||||
@@ -139,7 +140,9 @@ async def async_request_with_retry(
|
||||
logger.error(f"Error response body: {error_body}")
|
||||
|
||||
if attempt == max_retries - 1:
|
||||
logger.error(f"Request failed after {max_retries} attempts: {e}")
|
||||
logger.error(
|
||||
f"Request {method} : {url} failed after {max_retries} attempts: {e}"
|
||||
)
|
||||
raise
|
||||
|
||||
await asyncio.sleep(retry_delay)
|
||||
@@ -147,7 +150,7 @@ async def async_request_with_retry(
|
||||
|
||||
total_time = time.time() - start_time
|
||||
raise Exception(
|
||||
f"Request failed after {max_retries} attempts and {total_time:.2f} seconds"
|
||||
f"Request {method} : {url} failed after {max_retries} attempts and {total_time:.2f} seconds"
|
||||
)
|
||||
|
||||
|
||||
@@ -156,6 +159,8 @@ from logging import basicConfig, getLogger
|
||||
# Check for an environment variable to enable/disable Logfire
|
||||
use_logfire = os.environ.get("USE_LOGFIRE", "false").lower() == "true"
|
||||
|
||||
API_KEY_COMFY_ORG = os.environ.get("API_KEY_COMFY_ORG", None)
|
||||
|
||||
if use_logfire:
|
||||
try:
|
||||
import logfire
|
||||
@@ -260,7 +265,7 @@ def clear_current_prompt(sid):
|
||||
streaming_prompt_metadata[sid].running_prompt_ids.clear()
|
||||
|
||||
|
||||
def post_prompt(json_data):
|
||||
async def post_prompt(json_data):
|
||||
prompt_server = server.PromptServer.instance
|
||||
json_data = prompt_server.trigger_on_prompt(json_data)
|
||||
|
||||
@@ -276,16 +281,58 @@ def post_prompt(json_data):
|
||||
|
||||
if "prompt" in json_data:
|
||||
prompt = json_data["prompt"]
|
||||
valid = execution.validate_prompt(prompt)
|
||||
prompt_id = json_data.get("prompt_id") or str(uuid.uuid4())
|
||||
|
||||
partial_execution_targets = None
|
||||
if "partial_execution_targets" in json_data:
|
||||
partial_execution_targets = json_data["partial_execution_targets"]
|
||||
|
||||
# Handle different validate_prompt signatures (newest to oldest)
|
||||
valid = None
|
||||
last_error = None
|
||||
|
||||
# v0.3.48 (3 args)
|
||||
try:
|
||||
valid = await execution.validate_prompt(
|
||||
prompt_id, prompt, partial_execution_targets
|
||||
)
|
||||
except TypeError as e:
|
||||
last_error = e
|
||||
logger.debug(
|
||||
f"validate_prompt with 3 params not supported, trying with 2. Debug: {last_error}"
|
||||
)
|
||||
|
||||
# v0.3.45 - 0.3.47 (2 args)
|
||||
if valid is None:
|
||||
try:
|
||||
valid = await execution.validate_prompt(prompt_id, prompt)
|
||||
except TypeError as e:
|
||||
last_error = e
|
||||
logger.debug(
|
||||
f"validate_prompt with 2 params not supported, trying legacy signature. Debug: {last_error}"
|
||||
)
|
||||
|
||||
# v0.3.44 or older (1 arg)
|
||||
if valid is None:
|
||||
try:
|
||||
valid = execution.validate_prompt(prompt)
|
||||
except TypeError as e:
|
||||
last_error = e
|
||||
logger.error(
|
||||
f"validate_prompt failed with all signatures. Last error: {last_error}"
|
||||
)
|
||||
raise
|
||||
|
||||
extra_data = {}
|
||||
if "extra_data" in json_data:
|
||||
extra_data = json_data["extra_data"]
|
||||
|
||||
if API_KEY_COMFY_ORG is not None:
|
||||
extra_data["api_key_comfy_org"] = API_KEY_COMFY_ORG
|
||||
|
||||
if "client_id" in json_data:
|
||||
extra_data["client_id"] = json_data["client_id"]
|
||||
if valid[0]:
|
||||
# if the prompt id is provided
|
||||
prompt_id = json_data.get("prompt_id") or str(uuid.uuid4())
|
||||
outputs_to_execute = valid[2]
|
||||
prompt_server.prompt_queue.put(
|
||||
(number, prompt_id, prompt, extra_data, outputs_to_execute)
|
||||
@@ -304,12 +351,17 @@ def post_prompt(json_data):
|
||||
|
||||
|
||||
def randomSeed(num_digits=15):
|
||||
# Special case for SONICSampler which uses np.int32
|
||||
if num_digits == "sonic":
|
||||
return random.randint(0, 2147483647) # np.iinfo(np.int32).max
|
||||
|
||||
# Original logic for other cases
|
||||
range_start = 10 ** (num_digits - 1)
|
||||
range_end = (10**num_digits) - 1
|
||||
return random.randint(range_start, range_end)
|
||||
|
||||
|
||||
def apply_random_seed_to_workflow(workflow_api):
|
||||
def apply_random_seed_to_workflow(workflow_api, workflow):
|
||||
"""
|
||||
Applies a random seed to each element in the workflow_api that has a 'seed' input.
|
||||
|
||||
@@ -322,6 +374,48 @@ def apply_random_seed_to_workflow(workflow_api):
|
||||
# If seed is a list, it's an input from another node (generally `external number int`)
|
||||
if isinstance(workflow_api[key]["inputs"]["seed"], list):
|
||||
continue
|
||||
|
||||
# Check node type in workflow to determine if we should randomize
|
||||
node_id = key
|
||||
should_skip = (
|
||||
False # Add a flag to track if we should skip randomization
|
||||
)
|
||||
|
||||
for node in workflow["nodes"]:
|
||||
if str(node["id"]) == node_id and node["type"] == "KSampler":
|
||||
# Check if this node has widgets_values and if seed setting is not "fixed"
|
||||
if "widgets_values" in node and len(node["widgets_values"]) > 1:
|
||||
seed_mode = node["widgets_values"][1]
|
||||
if seed_mode == "fixed":
|
||||
# Skip randomization for fixed seeds
|
||||
logger.info(
|
||||
f"Skipping random seed for KSampler (node {node_id}) as it's set to fixed"
|
||||
)
|
||||
should_skip = True # Set the flag to skip randomization
|
||||
break # Exit the inner loop
|
||||
|
||||
# Apply random seed for non-fixed seeds (randomize, iter, etc.)
|
||||
workflow_api[key]["inputs"]["seed"] = randomSeed()
|
||||
logger.info(
|
||||
f"Applied random seed {workflow_api[key]['inputs']['seed']} to KSampler (node {node_id})"
|
||||
)
|
||||
should_skip = (
|
||||
True # Set the flag to skip default randomization
|
||||
)
|
||||
break # Exit the inner loop
|
||||
break # This break will skip checking other nodes if widgets_values doesn't exist
|
||||
|
||||
# Skip the rest of the code for this key if we already handled it
|
||||
if should_skip:
|
||||
continue
|
||||
|
||||
# Special case for SONICSampler
|
||||
if workflow_api[key]["class_type"] == "SONICSampler":
|
||||
workflow_api[key]["inputs"]["seed"] = randomSeed("sonic")
|
||||
logger.info(
|
||||
f"Applied random seed {workflow_api[key]['inputs']['seed']} to SONICSampler"
|
||||
)
|
||||
continue
|
||||
if workflow_api[key]["class_type"] == "PromptExpansion":
|
||||
workflow_api[key]["inputs"]["seed"] = randomSeed(8)
|
||||
logger.info(
|
||||
@@ -355,6 +449,12 @@ def apply_random_seed_to_workflow(workflow_api):
|
||||
f"Applied random noise_seed {workflow_api[key]['inputs']['noise_seed']} to SamplerCustom"
|
||||
)
|
||||
continue
|
||||
if workflow_api[key]["class_type"] == "XlabsSampler":
|
||||
workflow_api[key]["inputs"]["noise_seed"] = randomSeed()
|
||||
logger.info(
|
||||
f"Applied random noise_seed {workflow_api[key]['inputs']['noise_seed']} to SamplerCustom"
|
||||
)
|
||||
continue
|
||||
|
||||
|
||||
def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None):
|
||||
@@ -395,6 +495,9 @@ def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None):
|
||||
if value["class_type"] == "ComfyUIDeployExternalImageBatch":
|
||||
value["inputs"]["images"] = new_value
|
||||
|
||||
if value["class_type"] == "ComfyUIDeployExternalEnum":
|
||||
value["inputs"]["default_value"] = new_value
|
||||
|
||||
if value["class_type"] == "ComfyUIDeployExternalLora":
|
||||
value["inputs"]["lora_url"] = new_value
|
||||
|
||||
@@ -413,6 +516,12 @@ def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None):
|
||||
if value["class_type"] == "ComfyUIDeployExternalEXR":
|
||||
value["inputs"]["exr_file"] = new_value
|
||||
|
||||
if value["class_type"] == "ComfyUIDeployExternalSeed":
|
||||
logger.info(
|
||||
f"Applied random seed {new_value} to {value['class_type']}"
|
||||
)
|
||||
value["inputs"]["default_value"] = new_value
|
||||
|
||||
|
||||
def send_prompt(sid: str, inputs: StreamingPrompt):
|
||||
# workflow_api = inputs.workflow_api
|
||||
@@ -420,7 +529,7 @@ def send_prompt(sid: str, inputs: StreamingPrompt):
|
||||
workflow = copy.deepcopy(inputs.workflow)
|
||||
|
||||
# Random seed
|
||||
apply_random_seed_to_workflow(workflow_api)
|
||||
apply_random_seed_to_workflow(workflow_api, workflow)
|
||||
|
||||
logger.info("getting inputs", inputs.inputs)
|
||||
|
||||
@@ -430,15 +539,15 @@ def send_prompt(sid: str, inputs: StreamingPrompt):
|
||||
|
||||
prompt_id = str(uuid.uuid4())
|
||||
|
||||
prompt = {
|
||||
"prompt": workflow_api,
|
||||
"client_id": sid, # "comfy_deploy_instance", #api.client_id
|
||||
"prompt_id": prompt_id,
|
||||
"extra_data": {"extra_pnginfo": {"workflow": workflow}},
|
||||
}
|
||||
# prompt = {
|
||||
# "prompt": workflow_api,
|
||||
# "client_id": sid, # "comfy_deploy_instance", #api.client_id
|
||||
# "prompt_id": prompt_id,
|
||||
# "extra_data": {"extra_pnginfo": {"workflow": workflow}},
|
||||
# }
|
||||
|
||||
try:
|
||||
res = post_prompt(prompt)
|
||||
# res = post_prompt(prompt)
|
||||
inputs.running_prompt_ids.add(prompt_id)
|
||||
prompt_metadata[prompt_id] = SimplePrompt(
|
||||
status_endpoint=inputs.status_endpoint,
|
||||
@@ -449,7 +558,7 @@ def send_prompt(sid: str, inputs: StreamingPrompt):
|
||||
except Exception as e:
|
||||
error_type = type(e).__name__
|
||||
stack_trace_short = traceback.format_exc().strip().split("\n")[-2]
|
||||
stack_trace = traceback.format_exc().strip()
|
||||
# stack_trace = traceback.format_exc().strip()
|
||||
logger.info(f"error: {error_type}, {e}")
|
||||
logger.info(f"stack trace: {stack_trace_short}")
|
||||
|
||||
@@ -506,7 +615,7 @@ async def comfy_deploy_run(request):
|
||||
workflow = data.get("workflow")
|
||||
|
||||
# Now it handles directly in here
|
||||
apply_random_seed_to_workflow(workflow_api)
|
||||
apply_random_seed_to_workflow(workflow_api, workflow)
|
||||
apply_inputs_to_workflow(workflow_api, inputs)
|
||||
|
||||
prompt = {
|
||||
@@ -525,7 +634,7 @@ async def comfy_deploy_run(request):
|
||||
)
|
||||
|
||||
try:
|
||||
res = post_prompt(prompt)
|
||||
res = await post_prompt(prompt)
|
||||
except Exception as e:
|
||||
error_type = type(e).__name__
|
||||
stack_trace_short = traceback.format_exc().strip().split("\n")[-2]
|
||||
@@ -563,6 +672,14 @@ async def comfy_deploy_run(request):
|
||||
return web.json_response(res, status=status)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/comfyui-deploy/interrupt")
|
||||
async def interrupt_prompt(request):
|
||||
data = await request.json()
|
||||
prompt_id = data.get("prompt_id")
|
||||
await update_run(prompt_id, Status.CANCELLED)
|
||||
return web.json_response({"message": "Prompt interrupted"}, status=200)
|
||||
|
||||
|
||||
async def stream_prompt(data, token):
|
||||
# In older version, we use workflow_api, but this has inputs already swapped in nextjs frontend, which is tricky
|
||||
workflow_api = data.get("workflow_api_raw")
|
||||
@@ -573,7 +690,7 @@ async def stream_prompt(data, token):
|
||||
gpu_event_id = data.get("gpu_event_id", None)
|
||||
|
||||
# Now it handles directly in here
|
||||
apply_random_seed_to_workflow(workflow_api)
|
||||
apply_random_seed_to_workflow(workflow_api, workflow)
|
||||
apply_inputs_to_workflow(workflow_api, inputs)
|
||||
|
||||
prompt = {
|
||||
@@ -594,7 +711,7 @@ async def stream_prompt(data, token):
|
||||
# log('info', "Begin prompt", prompt=prompt)
|
||||
|
||||
try:
|
||||
res = post_prompt(prompt)
|
||||
res = await post_prompt(prompt)
|
||||
except Exception as e:
|
||||
error_type = type(e).__name__
|
||||
stack_trace_short = traceback.format_exc().strip().split("\n")[-2]
|
||||
@@ -1196,22 +1313,11 @@ def handle_execute(class_type, last_node_id, prompt_id, server, unique_id):
|
||||
|
||||
try:
|
||||
origin_execute = execution.execute
|
||||
is_async = asyncio.iscoroutinefunction(origin_execute)
|
||||
|
||||
def swizzle_execute(
|
||||
server,
|
||||
dynprompt,
|
||||
caches,
|
||||
current_item,
|
||||
extra_data,
|
||||
executed,
|
||||
prompt_id,
|
||||
execution_list,
|
||||
pending_subgraph_results,
|
||||
):
|
||||
unique_id = current_item
|
||||
class_type = dynprompt.get_node(unique_id)["class_type"]
|
||||
last_node_id = server.last_node_id
|
||||
result = origin_execute(
|
||||
if is_async:
|
||||
|
||||
async def swizzle_execute(
|
||||
server,
|
||||
dynprompt,
|
||||
caches,
|
||||
@@ -1221,12 +1327,61 @@ try:
|
||||
prompt_id,
|
||||
execution_list,
|
||||
pending_subgraph_results,
|
||||
)
|
||||
handle_execute(class_type, last_node_id, prompt_id, server, unique_id)
|
||||
return result
|
||||
pending_async_nodes,
|
||||
):
|
||||
unique_id = current_item
|
||||
class_type = dynprompt.get_node(unique_id)["class_type"]
|
||||
last_node_id = server.last_node_id
|
||||
|
||||
result = await origin_execute(
|
||||
server,
|
||||
dynprompt,
|
||||
caches,
|
||||
current_item,
|
||||
extra_data,
|
||||
executed,
|
||||
prompt_id,
|
||||
execution_list,
|
||||
pending_subgraph_results,
|
||||
pending_async_nodes,
|
||||
)
|
||||
|
||||
handle_execute(class_type, last_node_id, prompt_id, server, unique_id)
|
||||
return result
|
||||
else:
|
||||
|
||||
def swizzle_execute(
|
||||
server,
|
||||
dynprompt,
|
||||
caches,
|
||||
current_item,
|
||||
extra_data,
|
||||
executed,
|
||||
prompt_id,
|
||||
execution_list,
|
||||
pending_subgraph_results,
|
||||
):
|
||||
unique_id = current_item
|
||||
class_type = dynprompt.get_node(unique_id)["class_type"]
|
||||
last_node_id = server.last_node_id
|
||||
|
||||
result = origin_execute(
|
||||
server,
|
||||
dynprompt,
|
||||
caches,
|
||||
current_item,
|
||||
extra_data,
|
||||
executed,
|
||||
prompt_id,
|
||||
execution_list,
|
||||
pending_subgraph_results,
|
||||
)
|
||||
|
||||
handle_execute(class_type, last_node_id, prompt_id, server, unique_id)
|
||||
return result
|
||||
|
||||
execution.execute = swizzle_execute
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@@ -1292,7 +1447,7 @@ send_json = prompt_server.send_json
|
||||
|
||||
|
||||
async def send_json_override(self, event, data, sid=None):
|
||||
# logger.info("INTERNAL:", event, data, sid)
|
||||
# logger.info(f"INTERNAL: event={event}, data={data}, sid={sid}")
|
||||
prompt_id = data.get("prompt_id")
|
||||
|
||||
target_sid = sid
|
||||
@@ -1370,16 +1525,15 @@ async def send_json_override(self, event, data, sid=None):
|
||||
logger.info(format_table(headers, table_data))
|
||||
# print("========================\n")
|
||||
|
||||
timeline = format_execution_timeline(NODE_EXECUTION_TIMES)
|
||||
logger.info(f"\nNode Execution Timeline:\n{timeline}")
|
||||
# Clear the execution times for the next run
|
||||
|
||||
# the last executing event is none, then the workflow is finished
|
||||
if event == "executing" and data.get("node") is None:
|
||||
mark_prompt_done(prompt_id=prompt_id)
|
||||
# We will now rely on the UploadQueue worker to set the final SUCCESS status
|
||||
# after all uploads are confirmed complete.
|
||||
|
||||
if not have_pending_upload(prompt_id):
|
||||
await update_run(prompt_id, Status.SUCCESS)
|
||||
if prompt_id in prompt_metadata:
|
||||
# await update_run(prompt_id, Status.SUCCESS) # <-- REMOVE/COMMENT OUT
|
||||
if prompt_id in prompt_metadata: # <-- REMOVE/COMMENT OUT THIS BLOCK
|
||||
current_time = time.perf_counter()
|
||||
if prompt_metadata[prompt_id].start_time is not None:
|
||||
elapsed_time = current_time - prompt_metadata[prompt_id].start_time
|
||||
@@ -1820,12 +1974,13 @@ async def upload_file(
|
||||
|
||||
|
||||
def have_pending_upload(prompt_id):
|
||||
# Check if there are pending uploads in the queue
|
||||
if (
|
||||
prompt_id in prompt_metadata
|
||||
and len(prompt_metadata[prompt_id].uploading_nodes) > 0
|
||||
prompt_id in upload_queue.pending_uploads
|
||||
and upload_queue.pending_uploads[prompt_id]
|
||||
):
|
||||
logger.info(
|
||||
f"Have pending upload {len(prompt_metadata[prompt_id].uploading_nodes)}"
|
||||
f"Have pending upload {len(upload_queue.pending_uploads[prompt_id])}"
|
||||
)
|
||||
return True
|
||||
|
||||
@@ -1880,16 +2035,11 @@ async def handle_error(prompt_id, data, e: Exception):
|
||||
async def update_file_status(
|
||||
prompt_id: str, data, uploading, have_error=False, node_id=None
|
||||
):
|
||||
# if 'uploading_nodes' not in prompt_metadata[prompt_id]:
|
||||
# prompt_metadata[prompt_id]['uploading_nodes'] = set()
|
||||
# We're using upload_queue as the single source of truth for tracking uploads
|
||||
# The upload_queue.pending_uploads is managed by the UploadQueue class itself
|
||||
# We no longer need to track uploading_nodes in prompt_metadata
|
||||
|
||||
if node_id is not None:
|
||||
if uploading:
|
||||
prompt_metadata[prompt_id].uploading_nodes.add(node_id)
|
||||
else:
|
||||
prompt_metadata[prompt_id].uploading_nodes.discard(node_id)
|
||||
|
||||
# logger.info(f"Remaining uploads: {prompt_metadata[prompt_id].uploading_nodes}")
|
||||
# logger.info(f"Pending uploads in queue: {upload_queue.pending_uploads.get(prompt_id, set())}")
|
||||
# Update the remote status
|
||||
|
||||
if have_error:
|
||||
@@ -1903,15 +2053,15 @@ async def update_file_status(
|
||||
return
|
||||
|
||||
# if there are still nodes that are uploading, then we set the status to uploading
|
||||
if uploading:
|
||||
if prompt_metadata[prompt_id].status != Status.UPLOADING:
|
||||
await update_run(prompt_id, Status.UPLOADING)
|
||||
await send(
|
||||
"uploading",
|
||||
{
|
||||
"prompt_id": prompt_id,
|
||||
},
|
||||
)
|
||||
# if uploading:
|
||||
# if prompt_metadata[prompt_id].status != Status.UPLOADING:
|
||||
# await update_run(prompt_id, Status.UPLOADING)
|
||||
# await send(
|
||||
# "uploading",
|
||||
# {
|
||||
# "prompt_id": prompt_id,
|
||||
# },
|
||||
# )
|
||||
|
||||
# if there are no nodes that are uploading, then we set the status to success
|
||||
elif (
|
||||
@@ -1937,7 +2087,7 @@ async def handle_upload(
|
||||
|
||||
for item in items:
|
||||
# Skipping temp files
|
||||
if item.get("type") == "temp":
|
||||
if isinstance(item, dict) and item.get("type") == "temp":
|
||||
continue
|
||||
|
||||
file_type = item.get(content_type_key, default_content_type)
|
||||
@@ -1988,22 +2138,30 @@ async def upload_in_background(
|
||||
("files", "content_type", "image/png"),
|
||||
("gifs", "format", "image/gif"),
|
||||
("model_file", "format", "application/octet-stream"),
|
||||
("result", "format", "application/octet-stream"),
|
||||
("text_file", "format", "text/plain"),
|
||||
("audio", "format", "audio/mpeg"),
|
||||
]:
|
||||
items = data.get(file_type, [])
|
||||
|
||||
for item in items:
|
||||
# if is model_file, just add it to the data
|
||||
if file_type == "model_file":
|
||||
if file_type == "model_file" or file_type == "result":
|
||||
if isinstance(item, str):
|
||||
filename = os.path.basename(item)
|
||||
# Extract folder name from the path
|
||||
folder_path = os.path.dirname(item)
|
||||
subfolder = (
|
||||
os.path.basename(folder_path) if folder_path else ""
|
||||
)
|
||||
item = {
|
||||
"filename": filename,
|
||||
"subfolder": "",
|
||||
"subfolder": subfolder,
|
||||
"type": "output",
|
||||
}
|
||||
|
||||
# Skip temp files
|
||||
if item.get("type") == "temp":
|
||||
if isinstance(item, dict) and item.get("type") == "temp":
|
||||
continue
|
||||
|
||||
# Add to the upload queue instead of uploading immediately
|
||||
@@ -2068,6 +2226,9 @@ async def update_run_with_output(
|
||||
or "files" in data
|
||||
or "gifs" in data
|
||||
or "model_file" in data
|
||||
or "result" in data
|
||||
or "text_file" in data
|
||||
or "audio" in data
|
||||
)
|
||||
if bypass_upload and have_upload_media:
|
||||
print(
|
||||
@@ -2378,6 +2539,11 @@ class UploadQueue:
|
||||
logger.warning(f"No upload endpoint for prompt ID: {prompt_id}")
|
||||
return
|
||||
|
||||
# Check if file_info is a valid dictionary with a filename
|
||||
if not isinstance(file_info, dict) or "filename" not in file_info:
|
||||
logger.warning(f"Invalid file_info for prompt ID {prompt_id}: {file_info}")
|
||||
return
|
||||
|
||||
filename = file_info.get("filename")
|
||||
subfolder = file_info.get("subfolder")
|
||||
file_type = file_info.get("type", "output")
|
||||
@@ -2528,8 +2694,12 @@ class UploadQueue:
|
||||
# If this was the last file for this prompt, show the stats summary
|
||||
if (
|
||||
prompt_id in self.pending_uploads
|
||||
# We now rely on the worker's finally block for the final SUCCESS update.
|
||||
# Check if the set becomes empty *after* removal in the worker.
|
||||
and len(self.pending_uploads[prompt_id]) == 1
|
||||
):
|
||||
# await update_run(prompt_id, Status.SUCCESS) # <-- REMOVE/COMMENT OUT
|
||||
|
||||
self._log_upload_stats(prompt_id)
|
||||
# Clean up stats
|
||||
del self.upload_stats[prompt_id]
|
||||
@@ -2650,6 +2820,8 @@ class UploadQueue:
|
||||
node_id = upload_task["node_id"]
|
||||
upload_id = upload_task["upload_id"]
|
||||
|
||||
print(file_info)
|
||||
|
||||
try:
|
||||
# Coordinate the actual start of the upload
|
||||
async with self.upload_lock:
|
||||
@@ -2665,51 +2837,57 @@ class UploadQueue:
|
||||
logger.error(f"Upload failed: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
finally:
|
||||
# Remove this upload from tracking
|
||||
if prompt_id in self.pending_uploads:
|
||||
self.pending_uploads[prompt_id].discard(upload_id)
|
||||
# Remove from node tracking if applicable
|
||||
if (
|
||||
node_id
|
||||
and prompt_id in self.node_uploads
|
||||
and node_id in self.node_uploads[prompt_id]
|
||||
):
|
||||
self.node_uploads[prompt_id][node_id].discard(upload_id)
|
||||
async with self.lock: # Acquire lock to protect shared dict access
|
||||
if prompt_id in self.pending_uploads:
|
||||
self.pending_uploads[prompt_id].discard(upload_id)
|
||||
|
||||
# If this was the last upload for this node, clean up node data
|
||||
if not self.node_uploads[prompt_id][node_id]:
|
||||
del self.node_uploads[prompt_id][node_id]
|
||||
if self.node_output_data[prompt_id][node_id]["data"]:
|
||||
# Send final node data to API before cleanup
|
||||
if prompt_metadata[prompt_id].status_endpoint:
|
||||
body = {
|
||||
"run_id": prompt_id,
|
||||
"output_data": self.node_output_data[
|
||||
prompt_id
|
||||
][node_id]["data"],
|
||||
"node_meta": {"node_id": node_id},
|
||||
}
|
||||
try:
|
||||
await async_request_with_retry(
|
||||
"POST",
|
||||
prompt_metadata[
|
||||
prompt_id
|
||||
].status_endpoint,
|
||||
token=prompt_metadata[prompt_id].token,
|
||||
json=body,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to send final node data: {str(e)}"
|
||||
)
|
||||
del self.node_output_data[prompt_id][node_id]
|
||||
if (
|
||||
node_id
|
||||
and prompt_id in self.node_uploads
|
||||
and node_id in self.node_uploads[prompt_id]
|
||||
):
|
||||
self.node_uploads[prompt_id][node_id].discard(upload_id)
|
||||
|
||||
# Send status update
|
||||
await self.update_queue_status(prompt_id)
|
||||
if not self.node_uploads[prompt_id][node_id]:
|
||||
del self.node_uploads[prompt_id][node_id]
|
||||
|
||||
if (
|
||||
prompt_id in self.node_output_data
|
||||
and node_id in self.node_output_data[prompt_id]
|
||||
):
|
||||
node_data = self.node_output_data[prompt_id][
|
||||
node_id
|
||||
]
|
||||
if node_data["data"]:
|
||||
body = {
|
||||
"run_id": prompt_id,
|
||||
"output_data": node_data["data"],
|
||||
"node_meta": {"node_id": node_id},
|
||||
}
|
||||
try:
|
||||
await async_request_with_retry(
|
||||
"POST",
|
||||
prompt_metadata[
|
||||
prompt_id
|
||||
].status_endpoint,
|
||||
token=prompt_metadata[
|
||||
prompt_id
|
||||
].token,
|
||||
json=body,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(
|
||||
f"Failed to send final node data: {str(e)}"
|
||||
)
|
||||
|
||||
# Safe to delete now (re-check not strictly needed with lock, but harmless)
|
||||
del self.node_output_data[prompt_id][node_id]
|
||||
|
||||
# If no more pending uploads for this prompt and it's done, update status
|
||||
if not self.pending_uploads[prompt_id] and is_prompt_done(
|
||||
prompt_id
|
||||
if (
|
||||
prompt_id in self.pending_uploads
|
||||
and not self.pending_uploads[prompt_id]
|
||||
and is_prompt_done(prompt_id)
|
||||
):
|
||||
# Clean up all data for this prompt
|
||||
if prompt_id in self.node_uploads:
|
||||
@@ -2722,9 +2900,12 @@ class UploadQueue:
|
||||
loop.create_task(update_run(prompt_id, Status.SUCCESS))
|
||||
loop.create_task(send("success", {"prompt_id": prompt_id}))
|
||||
|
||||
# Mark task as done
|
||||
# Mark task as done (outside lock to avoid holding it unnecessarily)
|
||||
self.queue.task_done()
|
||||
|
||||
# Send status update (also outside lock)
|
||||
await self.update_queue_status(prompt_id)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in upload worker: {str(e)}")
|
||||
logger.error(traceback.format_exc())
|
||||
@@ -2795,3 +2976,317 @@ def format_execution_timeline(execution_times):
|
||||
current_time += duration
|
||||
|
||||
return format_table(headers, rows)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/comfyui-deploy/auth-response")
|
||||
async def auth_response_proxy(request):
|
||||
request_id = request.rel_url.query.get("request_id")
|
||||
api_url = request.rel_url.query.get("api_url", "https://api.comfydeploy.com")
|
||||
|
||||
if not request_id:
|
||||
return web.json_response({"error": "request_id is required"}, status=400)
|
||||
|
||||
target_url = f"{api_url}/api/platform/comfyui/auth-response?request_id={request_id}"
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.get(target_url) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/comfyui-deploy/workflow")
|
||||
async def create_workflow_proxy(request):
|
||||
data = await request.json()
|
||||
name = data.get("name")
|
||||
workflow_json = data.get("workflow_json")
|
||||
workflow_api = data.get("workflow_api")
|
||||
machine_id = data.get("machine_id")
|
||||
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||
|
||||
auth_header = request.headers.get("Authorization")
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
if not name or not workflow_json or not workflow_api:
|
||||
return web.json_response(
|
||||
{"error": "name, workflow_json, workflow_api are required"}, status=400
|
||||
)
|
||||
|
||||
target_url = f"{api_url}/api/workflow"
|
||||
|
||||
request_body = {
|
||||
"name": name,
|
||||
"workflow_json": json.dumps(workflow_json),
|
||||
"workflow_api": json.dumps(workflow_api),
|
||||
"machine_id": machine_id,
|
||||
}
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.post(
|
||||
target_url,
|
||||
json=request_body,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": auth_header,
|
||||
},
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/comfyui-deploy/workflow/version")
|
||||
async def create_workflow_version_proxy(request):
|
||||
data = await request.json()
|
||||
workflow_id = data.get("workflow_id")
|
||||
workflow = data.get("workflow")
|
||||
workflow_api = data.get("workflow_api")
|
||||
comment = data.get("comment", "")
|
||||
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||
|
||||
auth_header = request.headers.get("Authorization")
|
||||
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
target_url = f"{api_url}/api/workflow/{workflow_id}/version"
|
||||
|
||||
request_body = {
|
||||
"workflow": workflow,
|
||||
"workflow_api": workflow_api,
|
||||
"comment": comment,
|
||||
}
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.post(
|
||||
target_url,
|
||||
json=request_body,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": auth_header,
|
||||
},
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.get("/comfyui-deploy/workflows")
|
||||
async def get_workflows_proxy(request):
|
||||
api_url = request.rel_url.query.get("api_url", "https://api.comfydeploy.com")
|
||||
search = request.rel_url.query.get("search", "")
|
||||
limit = request.rel_url.query.get("limit", 10)
|
||||
offset = request.rel_url.query.get("offset", 0)
|
||||
auth_header = request.headers.get("Authorization")
|
||||
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
# Build query parameters properly
|
||||
params = {}
|
||||
if search:
|
||||
params["search"] = search
|
||||
if limit:
|
||||
params["limit"] = limit
|
||||
if offset:
|
||||
params["offset"] = offset
|
||||
|
||||
target_url = f"{api_url}/api/workflows"
|
||||
if params:
|
||||
target_url += f"?{urlencode(params)}"
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.get(
|
||||
target_url,
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": auth_header,
|
||||
},
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
# for getting a workflow by id
|
||||
@server.PromptServer.instance.routes.get("/comfyui-deploy/workflow")
|
||||
async def get_workflow_proxy(request):
|
||||
workflow_id = request.rel_url.query.get("workflow_id")
|
||||
api_url = request.rel_url.query.get("api_url", "https://api.comfydeploy.com")
|
||||
auth_header = request.headers.get("Authorization")
|
||||
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
target_url = f"{api_url}/api/workflow/{workflow_id}"
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.get(
|
||||
target_url, headers={"Authorization": auth_header}
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
# for getting a machine by id
|
||||
@server.PromptServer.instance.routes.get("/comfyui-deploy/machine")
|
||||
async def get_machine_proxy(request):
|
||||
machine_id = request.rel_url.query.get("machine_id")
|
||||
api_url = request.rel_url.query.get("api_url", "https://api.comfydeploy.com")
|
||||
auth_header = request.headers.get("Authorization")
|
||||
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
target_url = f"{api_url}/api/machine/{machine_id}"
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.get(
|
||||
target_url, headers={"Authorization": auth_header}
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
# for fetching docker steps from current snapshot
|
||||
@server.PromptServer.instance.routes.post("/comfyui-deploy/snapshot-to-docker")
|
||||
async def snapshot_to_docker_proxy(request):
|
||||
data = await request.json()
|
||||
snapshot = data.get("snapshot")
|
||||
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||
auth_header = request.headers.get("Authorization")
|
||||
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
target_url = f"{api_url}/api/snapshot-to-docker"
|
||||
|
||||
request_body = snapshot
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.post(
|
||||
target_url, json=request_body, headers={"Authorization": auth_header}
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
# update a serverless machine with machine id
|
||||
@server.PromptServer.instance.routes.post("/comfyui-deploy/machine/update")
|
||||
async def update_machine_proxy(request):
|
||||
data = await request.json()
|
||||
machine_id = data.get("machine_id")
|
||||
comfyui_version = data.get("comfyui_version", None)
|
||||
docker_steps = data.get("docker_steps")
|
||||
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||
auth_header = request.headers.get("Authorization")
|
||||
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
target_url = f"{api_url}/api/machine/serverless/{machine_id}"
|
||||
|
||||
request_body = {"docker_command_steps": docker_steps}
|
||||
|
||||
if comfyui_version:
|
||||
request_body["comfyui_version"] = comfyui_version
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.patch(
|
||||
target_url, json=request_body, headers={"Authorization": auth_header}
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
@server.PromptServer.instance.routes.post("/comfyui-deploy/machine/create")
|
||||
async def create_machine_proxy(request):
|
||||
data = await request.json()
|
||||
name = data.get("name")
|
||||
docker_command_steps = data.get("docker_command_steps")
|
||||
comfyui_version = data.get("comfyui_version")
|
||||
api_url = data.get("api_url", "https://api.comfydeploy.com")
|
||||
auth_header = request.headers.get("Authorization")
|
||||
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
target_url = f"{api_url}/api/machine/serverless"
|
||||
|
||||
request_body = {
|
||||
"name": name,
|
||||
"docker_command_steps": docker_command_steps,
|
||||
"comfyui_version": comfyui_version,
|
||||
"gpu": "A10G",
|
||||
}
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.post(
|
||||
target_url, json=request_body, headers={"Authorization": auth_header}
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
|
||||
# get latest comfyui version
|
||||
@server.PromptServer.instance.routes.get("/comfyui-deploy/comfyui-version")
|
||||
async def get_comfyui_version_proxy(request):
|
||||
api_url = request.rel_url.query.get("api_url", "https://api.comfydeploy.com")
|
||||
auth_header = request.headers.get("Authorization")
|
||||
|
||||
if not auth_header:
|
||||
return web.json_response(
|
||||
{"error": "Authorization header is required"}, status=401
|
||||
)
|
||||
|
||||
target_url = f"{api_url}/api/latest-hashes"
|
||||
|
||||
try:
|
||||
await ensure_client_session()
|
||||
async with client_session.get(
|
||||
target_url, headers={"Authorization": auth_header}
|
||||
) as response:
|
||||
json_data = await response.json()
|
||||
return web.json_response(json_data, status=response.status)
|
||||
except Exception as e:
|
||||
return web.json_response({"error": str(e)}, status=500)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 156 KiB |
@@ -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 |
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -18,6 +18,7 @@ class Status(Enum):
|
||||
SUCCESS = "success"
|
||||
FAILED = "failed"
|
||||
UPLOADING = "uploading"
|
||||
CANCELLED = "cancelled"
|
||||
|
||||
|
||||
class StreamingPrompt(BaseModel):
|
||||
|
||||
+2
-2
@@ -1,9 +1,9 @@
|
||||
[project]
|
||||
name = "comfyui-deploy"
|
||||
description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
|
||||
version = "1.1.0"
|
||||
version = "2.3.3"
|
||||
license = { file = "LICENSE" }
|
||||
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg"]
|
||||
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"]
|
||||
|
||||
[project.urls]
|
||||
Repository = "https://github.com/BennyKok/comfyui-deploy"
|
||||
|
||||
+1394
-606
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
// Snapshot Utilities
|
||||
// Centralized snapshot fetching with ComfyUI version fallback
|
||||
|
||||
/**
|
||||
* Fetches the current snapshot with ComfyUI version fallback
|
||||
* If the snapshot response has null comfyui field, it will fetch the latest ComfyUI version
|
||||
* and update the snapshot with the comfyui_hash
|
||||
*
|
||||
* @param {Function} getDataFn - Function that returns { apiKey, apiUrl } for ComfyUI version API calls
|
||||
* @returns {Promise<Object>} - The snapshot data with comfyui field populated
|
||||
*/
|
||||
export async function fetchSnapshot(getDataFn = null) {
|
||||
try {
|
||||
// Fetch the current snapshot
|
||||
const response = await fetch("/snapshot/get_current");
|
||||
if (!response.ok) {
|
||||
throw new Error(`Snapshot fetch failed: ${response.status}`);
|
||||
}
|
||||
|
||||
const snapshot = await response.json();
|
||||
|
||||
// Check if comfyui field is null and we have getDataFn for fallback
|
||||
if (snapshot.comfyui === null && getDataFn) {
|
||||
console.log(
|
||||
"ComfyUI version is null in snapshot, fetching latest version..."
|
||||
);
|
||||
|
||||
try {
|
||||
const data = getDataFn();
|
||||
if (data && data.apiKey) {
|
||||
const comfyuiVersionResponse = await fetch(
|
||||
`/comfyui-deploy/comfyui-version?api_url=${encodeURIComponent(
|
||||
data.apiUrl || "https://api.comfydeploy.com"
|
||||
)}`,
|
||||
{
|
||||
headers: {
|
||||
Authorization: `Bearer ${data.apiKey}`,
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
if (comfyuiVersionResponse.ok) {
|
||||
const versionData = await comfyuiVersionResponse.json();
|
||||
if (versionData.comfyui_hash) {
|
||||
console.log(
|
||||
`Using ComfyUI hash from API: ${versionData.comfyui_hash}`
|
||||
);
|
||||
snapshot.comfyui = versionData.comfyui_hash;
|
||||
}
|
||||
} else {
|
||||
console.warn(
|
||||
"Failed to fetch ComfyUI version from API:",
|
||||
comfyuiVersionResponse.status
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn("Error fetching ComfyUI version fallback:", error);
|
||||
// Continue with original snapshot even if fallback fails
|
||||
}
|
||||
}
|
||||
|
||||
return snapshot;
|
||||
} catch (error) {
|
||||
console.error("Error fetching snapshot:", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple snapshot fetch without ComfyUI version fallback
|
||||
* Use this when you don't need the ComfyUI version fallback logic
|
||||
*
|
||||
* @returns {Promise<Object>} - The snapshot data as-is
|
||||
*/
|
||||
export async function fetchSnapshotSimple() {
|
||||
const response = await fetch("/snapshot/get_current");
|
||||
if (!response.ok) {
|
||||
throw new Error(`Snapshot fetch failed: ${response.status}`);
|
||||
}
|
||||
return response.json();
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
// Workflow list management
|
||||
let workflowsState = {
|
||||
workflows: [],
|
||||
offset: 0,
|
||||
limit: 20,
|
||||
loading: false,
|
||||
hasMore: true,
|
||||
initialized: false,
|
||||
currentSearch: "",
|
||||
};
|
||||
|
||||
// Make workflowsState accessible globally
|
||||
window.workflowsState = workflowsState;
|
||||
|
||||
async function fetchWorkflows(getData, offset = 0, limit = 20, search = "") {
|
||||
try {
|
||||
const data = getData();
|
||||
if (!data.apiKey) {
|
||||
throw new Error("API key not configured");
|
||||
}
|
||||
|
||||
const params = new URLSearchParams({
|
||||
offset: offset.toString(),
|
||||
limit: limit.toString(),
|
||||
api_url: data.apiUrl || "https://api.comfydeploy.com",
|
||||
...(search && { search }),
|
||||
});
|
||||
|
||||
const response = await fetch(`/comfyui-deploy/workflows?${params}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${data.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch workflows: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log("result", result);
|
||||
return Array.isArray(result) ? result : [];
|
||||
} catch (error) {
|
||||
console.error("Error fetching workflows:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function createWorkflowItem(workflow, getTimeAgo, getData) {
|
||||
const li = document.createElement("li");
|
||||
let loadingToast = null;
|
||||
li.style.cssText = `
|
||||
border-bottom: 1px solid #444;
|
||||
background: transparent;
|
||||
transition: all 0.2s ease;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
li.addEventListener("mouseenter", () => {
|
||||
li.style.background = "#333";
|
||||
});
|
||||
|
||||
li.addEventListener("mouseleave", () => {
|
||||
li.style.background = "transparent";
|
||||
});
|
||||
|
||||
// Add click handler to fetch and load workflow data
|
||||
li.addEventListener("click", async () => {
|
||||
try {
|
||||
const data = getData();
|
||||
if (!data.apiKey) {
|
||||
console.error("No API key configured");
|
||||
return;
|
||||
}
|
||||
|
||||
// Show loading toast
|
||||
loadingToast = window.app.extensionManager.toast.add({
|
||||
severity: "info",
|
||||
summary: "Loading workflow...",
|
||||
detail: `Loading "${workflow.name}"`,
|
||||
life: 3000,
|
||||
});
|
||||
|
||||
const params = new URLSearchParams({
|
||||
workflow_id: workflow.id,
|
||||
api_url: data.apiUrl || "https://api.comfydeploy.com",
|
||||
});
|
||||
|
||||
const response = await fetch(`/comfyui-deploy/workflow?${params}`, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
Authorization: `Bearer ${data.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch workflow: ${response.status}`);
|
||||
}
|
||||
|
||||
const workflowData = await response.json();
|
||||
console.log("Workflow data:", workflowData);
|
||||
|
||||
// Load the workflow into the graph
|
||||
if (workflowData.versions && workflowData.versions.length > 0) {
|
||||
const latestVersion = workflowData.versions[0];
|
||||
if (latestVersion.workflow && window.app) {
|
||||
// Load the workflow
|
||||
window.app.loadGraphData(latestVersion.workflow);
|
||||
|
||||
// Wait a bit for the graph to fully load before checking for ComfyDeploy node
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
|
||||
// Check if ComfyDeploy node exists, if not add it back
|
||||
const graph = window.app.graph;
|
||||
let deployMeta = graph.findNodesByType("ComfyDeploy");
|
||||
|
||||
if (deployMeta.length === 0) {
|
||||
// Add ComfyDeploy node with workflow metadata
|
||||
graph.beforeChange();
|
||||
const node = LiteGraph.createNode("ComfyDeploy");
|
||||
node.configure({
|
||||
widgets_values: [
|
||||
workflow.name, // workflow_name
|
||||
workflow.id, // workflow_id
|
||||
latestVersion.version, // version
|
||||
],
|
||||
});
|
||||
node.pos = [0, 0];
|
||||
graph.add(node);
|
||||
graph.afterChange();
|
||||
|
||||
console.log(
|
||||
`Added ComfyDeploy node with: name="${workflow.name}", id="${workflow.id}", version="${latestVersion.version}"`
|
||||
);
|
||||
}
|
||||
|
||||
// Show success toast
|
||||
window.app.extensionManager.toast.add({
|
||||
severity: "success",
|
||||
summary: "Workflow loaded successfully",
|
||||
detail: `Loaded "${workflow.name}" v${latestVersion.version}`,
|
||||
life: 3000,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading workflow:", error);
|
||||
// Show error toast
|
||||
window.app.extensionManager.toast.add({
|
||||
severity: "error",
|
||||
summary: "Failed to load workflow",
|
||||
detail: error.message,
|
||||
life: 5000,
|
||||
});
|
||||
} finally {
|
||||
if (loadingToast) {
|
||||
loadingToast.close();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const updatedDate = new Date(workflow.updated_at);
|
||||
const timeAgo = getTimeAgo(updatedDate);
|
||||
|
||||
li.innerHTML = `
|
||||
<div style="padding: 12px 16px;">
|
||||
<div style="display: flex; align-items: flex-start; gap: 12px;">
|
||||
${
|
||||
workflow.cover_image
|
||||
? `<img src="${workflow.cover_image}"
|
||||
style="width: 40px; height: 40px; border-radius: 4px; object-fit: cover; flex-shrink: 0;"
|
||||
onerror="this.style.display='none'">`
|
||||
: `<div style="width: 40px; height: 40px; border-radius: 4px; background: #444; flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 14px; color: #888;">
|
||||
${workflow.name.charAt(0).toUpperCase()}
|
||||
</div>`
|
||||
}
|
||||
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 4px;">
|
||||
<h4 style="margin: 0; font-size: 14px; font-weight: 400; color: #fff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;">
|
||||
${workflow.name}
|
||||
</h4>
|
||||
${
|
||||
workflow.pinned
|
||||
? `<span style="color: #ffd700; font-size: 12px;">📌</span>`
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
|
||||
${
|
||||
workflow.description
|
||||
? `<p style="margin: 0 0 8px 0; font-size: 12px; color: #bbb; line-height: 1.3; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;">
|
||||
${workflow.description}
|
||||
</p>`
|
||||
: ""
|
||||
}
|
||||
|
||||
<div style="display: flex; align-items: center; gap: 8px; margin-top: 8px;">
|
||||
<img src="${workflow.user_icon}"
|
||||
style="width: 16px; height: 16px; border-radius: 50%;"
|
||||
onerror="this.style.display='none'">
|
||||
<span style="font-size: 11px; color: #888;">
|
||||
${workflow.user_name} • Updated ${timeAgo}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return li;
|
||||
}
|
||||
|
||||
async function loadMoreWorkflows(element, getData, getTimeAgo) {
|
||||
if (workflowsState.loading || !workflowsState.hasMore) return;
|
||||
|
||||
workflowsState.loading = true;
|
||||
|
||||
const workflowsList = element.querySelector("#workflows-list");
|
||||
const workflowsLoading = element.querySelector("#workflows-loading");
|
||||
|
||||
// Show loading indicator
|
||||
workflowsLoading.style.display = "flex";
|
||||
|
||||
try {
|
||||
const newWorkflows = await fetchWorkflows(
|
||||
getData,
|
||||
workflowsState.offset,
|
||||
workflowsState.limit,
|
||||
workflowsState.currentSearch
|
||||
);
|
||||
|
||||
if (newWorkflows.length === 0) {
|
||||
workflowsState.hasMore = false;
|
||||
} else {
|
||||
workflowsState.workflows.push(...newWorkflows);
|
||||
workflowsState.offset += newWorkflows.length;
|
||||
|
||||
// Render new workflow items
|
||||
newWorkflows.forEach((workflow) => {
|
||||
const workflowItem = createWorkflowItem(workflow, getTimeAgo, getData);
|
||||
workflowsList.appendChild(workflowItem);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error loading more workflows:", error);
|
||||
} finally {
|
||||
workflowsState.loading = false;
|
||||
workflowsLoading.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function setupInfiniteScroll(container, element, getData, getTimeAgo) {
|
||||
let isScrolling = false;
|
||||
|
||||
container.addEventListener("scroll", () => {
|
||||
if (isScrolling) return;
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = container;
|
||||
|
||||
// Load more when scrolled to bottom (with 100px threshold)
|
||||
if (scrollTop + clientHeight >= scrollHeight - 100) {
|
||||
isScrolling = true;
|
||||
loadMoreWorkflows(element, getData, getTimeAgo).finally(() => {
|
||||
isScrolling = false;
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function initializeWorkflowsList(element, getData, getTimeAgo) {
|
||||
const workflowsContainer = element.querySelector("#workflows-container");
|
||||
const workflowsList = element.querySelector("#workflows-list");
|
||||
const workflowsLoading = element.querySelector("#workflows-loading");
|
||||
|
||||
// Check if already initialized AND the DOM elements still exist
|
||||
if (
|
||||
workflowsState.initialized &&
|
||||
workflowsList &&
|
||||
workflowsList.children.length > 0
|
||||
)
|
||||
return;
|
||||
|
||||
try {
|
||||
// Reset state (always reset when reinitializing)
|
||||
workflowsState = {
|
||||
workflows: [],
|
||||
offset: 0,
|
||||
limit: 20,
|
||||
loading: false,
|
||||
hasMore: true,
|
||||
initialized: true,
|
||||
currentSearch: "",
|
||||
};
|
||||
|
||||
// Clear existing content in case of reinitialization
|
||||
if (workflowsList) {
|
||||
workflowsList.innerHTML = "";
|
||||
}
|
||||
|
||||
// Show container and loading
|
||||
workflowsContainer.style.display = "block";
|
||||
workflowsLoading.style.display = "flex";
|
||||
|
||||
// Style the workflows list for full height scrolling
|
||||
workflowsList.style.cssText = `
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: calc(100vh - 550px);
|
||||
overflow-y: auto;
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #666 transparent;
|
||||
border-top: 1px solid #444;
|
||||
`;
|
||||
|
||||
// Add webkit scrollbar styles
|
||||
const style = document.createElement("style");
|
||||
style.textContent = `
|
||||
#workflows-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
#workflows-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
#workflows-list::-webkit-scrollbar-thumb {
|
||||
background: #666;
|
||||
border-radius: 3px;
|
||||
}
|
||||
#workflows-list::-webkit-scrollbar-thumb:hover {
|
||||
background: #777;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
|
||||
// Setup infinite scroll
|
||||
setupInfiniteScroll(workflowsList, element, getData, getTimeAgo);
|
||||
|
||||
// Load initial workflows
|
||||
await loadMoreWorkflows(element, getData, getTimeAgo);
|
||||
|
||||
// Show the list
|
||||
workflowsList.style.display = "block";
|
||||
} catch (error) {
|
||||
console.error("Error initializing workflows list:", error);
|
||||
workflowsLoading.innerHTML = `
|
||||
<div style="text-align: center; color: #e74c3c; font-size: 12px; padding: 20px;">
|
||||
<div>Failed to load workflows</div>
|
||||
<button onclick="initializeWorkflowsList(this.closest('.comfy-menu'), getData, getTimeAgo)"
|
||||
style="margin-top: 8px; padding: 4px 8px; font-size: 11px; background: #f0f0f0; border: 1px solid #ccc; border-radius: 4px; cursor: pointer;">
|
||||
Retry
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Search functionality
|
||||
function addWorkflowSearch(element, getData, getTimeAgo) {
|
||||
const workflowsContainer = element.querySelector("#workflows-container");
|
||||
const h4 = workflowsContainer.querySelector("h4");
|
||||
|
||||
const searchContainer = document.createElement("div");
|
||||
searchContainer.style.cssText = "margin-bottom: 12px;";
|
||||
|
||||
const searchInput = document.createElement("input");
|
||||
searchInput.type = "text";
|
||||
searchInput.placeholder = "Search workflows...";
|
||||
searchInput.style.cssText = `
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid #555;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
box-sizing: border-box;
|
||||
background: #333;
|
||||
color: #fff;
|
||||
`;
|
||||
|
||||
let searchTimeout;
|
||||
searchInput.addEventListener("input", (e) => {
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(async () => {
|
||||
const searchTerm = e.target.value.trim();
|
||||
|
||||
// Update the tracked search term
|
||||
workflowsState.currentSearch = searchTerm;
|
||||
|
||||
// Reset state for new search
|
||||
workflowsState.workflows = [];
|
||||
workflowsState.offset = 0;
|
||||
workflowsState.hasMore = true;
|
||||
|
||||
// Clear current list
|
||||
const workflowsList = element.querySelector("#workflows-list");
|
||||
workflowsList.innerHTML = "";
|
||||
|
||||
// Load with search term
|
||||
workflowsState.loading = false;
|
||||
await loadMoreWorkflows(element, getData, getTimeAgo);
|
||||
}, 300);
|
||||
});
|
||||
|
||||
searchContainer.appendChild(searchInput);
|
||||
h4.after(searchContainer);
|
||||
}
|
||||
|
||||
// Export the functions
|
||||
export {
|
||||
initializeWorkflowsList,
|
||||
addWorkflowSearch,
|
||||
workflowsState,
|
||||
fetchWorkflows,
|
||||
loadMoreWorkflows,
|
||||
};
|
||||
Reference in New Issue
Block a user