Compare commits
27
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3ddbd3d4d | ||
|
|
fbb89c9fb9 | ||
|
|
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 |
+37
-1
@@ -2,8 +2,9 @@
|
|||||||
@author: BennyKok
|
@author: BennyKok
|
||||||
@title: comfyui-deploy
|
@title: comfyui-deploy
|
||||||
@nickname: Comfy Deploy
|
@nickname: Comfy Deploy
|
||||||
@description:
|
@description:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -17,19 +18,23 @@ import requests
|
|||||||
import folder_paths
|
import folder_paths
|
||||||
from folder_paths import add_model_folder_path, get_filename_list, get_folder_paths
|
from folder_paths import add_model_folder_path, get_filename_list, get_folder_paths
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
import re
|
||||||
|
|
||||||
from . import custom_routes
|
from . import custom_routes
|
||||||
# import routes
|
# import routes
|
||||||
|
|
||||||
ag_path = os.path.join(os.path.dirname(__file__))
|
ag_path = os.path.join(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
def get_python_files(path):
|
def get_python_files(path):
|
||||||
return [f[:-3] for f in os.listdir(path) if f.endswith(".py")]
|
return [f[:-3] for f in os.listdir(path) if f.endswith(".py")]
|
||||||
|
|
||||||
|
|
||||||
def append_to_sys_path(path):
|
def append_to_sys_path(path):
|
||||||
if path not in sys.path:
|
if path not in sys.path:
|
||||||
sys.path.append(path)
|
sys.path.append(path)
|
||||||
|
|
||||||
|
|
||||||
paths = ["comfy-nodes"]
|
paths = ["comfy-nodes"]
|
||||||
files = []
|
files = []
|
||||||
|
|
||||||
@@ -41,14 +46,45 @@ for path in paths:
|
|||||||
NODE_CLASS_MAPPINGS = {}
|
NODE_CLASS_MAPPINGS = {}
|
||||||
NODE_DISPLAY_NAME_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
|
# Import all the modules and append their mappings
|
||||||
for file in files:
|
for file in files:
|
||||||
module = importlib.import_module(file)
|
module = importlib.import_module(file)
|
||||||
|
|
||||||
|
# Check if the module has explicit mappings
|
||||||
if hasattr(module, "NODE_CLASS_MAPPINGS"):
|
if hasattr(module, "NODE_CLASS_MAPPINGS"):
|
||||||
NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS)
|
NODE_CLASS_MAPPINGS.update(module.NODE_CLASS_MAPPINGS)
|
||||||
if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS"):
|
if hasattr(module, "NODE_DISPLAY_NAME_MAPPINGS"):
|
||||||
NODE_DISPLAY_NAME_MAPPINGS.update(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"
|
WEB_DIRECTORY = "web-plugin"
|
||||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import os
|
|
||||||
import io
|
import io
|
||||||
import torchaudio
|
|
||||||
from folder_paths import get_annotated_filepath
|
from folder_paths import get_annotated_filepath
|
||||||
|
|
||||||
|
|
||||||
class ComfyUIDeployExternalAudio:
|
class ComfyUIDeployExternalAudio:
|
||||||
RETURN_TYPES = ("AUDIO",)
|
RETURN_TYPES = ("AUDIO",)
|
||||||
RETURN_NAMES = ("audio",)
|
RETURN_NAMES = ("audio",)
|
||||||
FUNCTION = "load_audio"
|
FUNCTION = "load_audio"
|
||||||
CATEGORY = "🔗ComfyDeploy"
|
CATEGORY = "🔗ComfyDeploy"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
@@ -29,30 +28,55 @@ class ComfyUIDeployExternalAudio:
|
|||||||
"STRING",
|
"STRING",
|
||||||
{"multiline": False, "default": ""},
|
{"multiline": False, "default": ""},
|
||||||
),
|
),
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def VALIDATE_INPUTS(s, audio_file, **kwargs):
|
def VALIDATE_INPUTS(s, audio_file, **kwargs):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def load_audio(self, input_id, audio_file, default_value=None, display_name=None, description=None):
|
def load_audio(
|
||||||
if audio_file and audio_file != "":
|
self,
|
||||||
if audio_file.startswith(('http://', 'https://')):
|
input_id,
|
||||||
# Handle URL input
|
audio_file,
|
||||||
import requests
|
default_value=None,
|
||||||
response = requests.get(audio_file)
|
display_name=None,
|
||||||
audio_data = io.BytesIO(response.content)
|
description=None,
|
||||||
waveform, sample_rate = torchaudio.load(audio_data)
|
):
|
||||||
|
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:
|
else:
|
||||||
# Handle local file
|
return (default_value,)
|
||||||
audio_path = get_annotated_filepath(audio_file)
|
except ImportError as e:
|
||||||
waveform, sample_rate = torchaudio.load(audio_path)
|
print(f"Error: torchaudio not installed or cannot be imported: {e}")
|
||||||
|
|
||||||
audio = {"waveform": waveform.unsqueeze(0), "sample_rate": sample_rate}
|
|
||||||
return (audio,)
|
|
||||||
else:
|
|
||||||
return (default_value,)
|
return (default_value,)
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalAudio": ComfyUIDeployExternalAudio}
|
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalAudio": ComfyUIDeployExternalAudio}
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyUIDeployExternalAudio": "External Audio (ComfyUI Deploy)"}
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"ComfyUIDeployExternalAudio": "External Audio (ComfyUI Deploy)"
|
||||||
|
}
|
||||||
|
|||||||
@@ -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]
|
||||||
@@ -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 = "🔗ComfyDeploy"
|
|
||||||
|
|
||||||
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,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)"}
|
||||||
+56
-28
@@ -139,7 +139,9 @@ async def async_request_with_retry(
|
|||||||
logger.error(f"Error response body: {error_body}")
|
logger.error(f"Error response body: {error_body}")
|
||||||
|
|
||||||
if attempt == max_retries - 1:
|
if attempt == max_retries - 1:
|
||||||
logger.error(f"Request {method} : {url} failed after {max_retries} attempts: {e}")
|
logger.error(
|
||||||
|
f"Request {method} : {url} failed after {max_retries} attempts: {e}"
|
||||||
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
await asyncio.sleep(retry_delay)
|
await asyncio.sleep(retry_delay)
|
||||||
@@ -367,6 +369,12 @@ def apply_random_seed_to_workflow(workflow_api):
|
|||||||
f"Applied random noise_seed {workflow_api[key]['inputs']['noise_seed']} to SamplerCustom"
|
f"Applied random noise_seed {workflow_api[key]['inputs']['noise_seed']} to SamplerCustom"
|
||||||
)
|
)
|
||||||
continue
|
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):
|
def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None):
|
||||||
@@ -407,6 +415,9 @@ def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None):
|
|||||||
if value["class_type"] == "ComfyUIDeployExternalImageBatch":
|
if value["class_type"] == "ComfyUIDeployExternalImageBatch":
|
||||||
value["inputs"]["images"] = new_value
|
value["inputs"]["images"] = new_value
|
||||||
|
|
||||||
|
if value["class_type"] == "ComfyUIDeployExternalEnum":
|
||||||
|
value["inputs"]["default_value"] = new_value
|
||||||
|
|
||||||
if value["class_type"] == "ComfyUIDeployExternalLora":
|
if value["class_type"] == "ComfyUIDeployExternalLora":
|
||||||
value["inputs"]["lora_url"] = new_value
|
value["inputs"]["lora_url"] = new_value
|
||||||
|
|
||||||
@@ -1304,7 +1315,7 @@ send_json = prompt_server.send_json
|
|||||||
|
|
||||||
|
|
||||||
async def send_json_override(self, event, data, sid=None):
|
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")
|
prompt_id = data.get("prompt_id")
|
||||||
|
|
||||||
target_sid = sid
|
target_sid = sid
|
||||||
@@ -1389,9 +1400,12 @@ async def send_json_override(self, event, data, sid=None):
|
|||||||
# the last executing event is none, then the workflow is finished
|
# the last executing event is none, then the workflow is finished
|
||||||
if event == "executing" and data.get("node") is None:
|
if event == "executing" and data.get("node") is None:
|
||||||
mark_prompt_done(prompt_id=prompt_id)
|
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):
|
if not have_pending_upload(prompt_id):
|
||||||
await update_run(prompt_id, Status.SUCCESS)
|
# await update_run(prompt_id, Status.SUCCESS) # <-- REMOVE/COMMENT OUT
|
||||||
if prompt_id in prompt_metadata:
|
if prompt_id in prompt_metadata: # <-- REMOVE/COMMENT OUT THIS BLOCK
|
||||||
current_time = time.perf_counter()
|
current_time = time.perf_counter()
|
||||||
if prompt_metadata[prompt_id].start_time is not None:
|
if prompt_metadata[prompt_id].start_time is not None:
|
||||||
elapsed_time = current_time - prompt_metadata[prompt_id].start_time
|
elapsed_time = current_time - prompt_metadata[prompt_id].start_time
|
||||||
@@ -1832,12 +1846,13 @@ async def upload_file(
|
|||||||
|
|
||||||
|
|
||||||
def have_pending_upload(prompt_id):
|
def have_pending_upload(prompt_id):
|
||||||
|
# Check if there are pending uploads in the queue
|
||||||
if (
|
if (
|
||||||
prompt_id in prompt_metadata
|
prompt_id in upload_queue.pending_uploads
|
||||||
and len(prompt_metadata[prompt_id].uploading_nodes) > 0
|
and upload_queue.pending_uploads[prompt_id]
|
||||||
):
|
):
|
||||||
logger.info(
|
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
|
return True
|
||||||
|
|
||||||
@@ -1892,16 +1907,11 @@ async def handle_error(prompt_id, data, e: Exception):
|
|||||||
async def update_file_status(
|
async def update_file_status(
|
||||||
prompt_id: str, data, uploading, have_error=False, node_id=None
|
prompt_id: str, data, uploading, have_error=False, node_id=None
|
||||||
):
|
):
|
||||||
# if 'uploading_nodes' not in prompt_metadata[prompt_id]:
|
# We're using upload_queue as the single source of truth for tracking uploads
|
||||||
# prompt_metadata[prompt_id]['uploading_nodes'] = set()
|
# 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:
|
# logger.info(f"Pending uploads in queue: {upload_queue.pending_uploads.get(prompt_id, set())}")
|
||||||
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}")
|
|
||||||
# Update the remote status
|
# Update the remote status
|
||||||
|
|
||||||
if have_error:
|
if have_error:
|
||||||
@@ -1915,15 +1925,15 @@ async def update_file_status(
|
|||||||
return
|
return
|
||||||
|
|
||||||
# if there are still nodes that are uploading, then we set the status to uploading
|
# if there are still nodes that are uploading, then we set the status to uploading
|
||||||
if uploading:
|
# if uploading:
|
||||||
if prompt_metadata[prompt_id].status != Status.UPLOADING:
|
# if prompt_metadata[prompt_id].status != Status.UPLOADING:
|
||||||
await update_run(prompt_id, Status.UPLOADING)
|
# await update_run(prompt_id, Status.UPLOADING)
|
||||||
await send(
|
# await send(
|
||||||
"uploading",
|
# "uploading",
|
||||||
{
|
# {
|
||||||
"prompt_id": prompt_id,
|
# "prompt_id": prompt_id,
|
||||||
},
|
# },
|
||||||
)
|
# )
|
||||||
|
|
||||||
# if there are no nodes that are uploading, then we set the status to success
|
# if there are no nodes that are uploading, then we set the status to success
|
||||||
elif (
|
elif (
|
||||||
@@ -1949,7 +1959,7 @@ async def handle_upload(
|
|||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
# Skipping temp files
|
# Skipping temp files
|
||||||
if item.get("type") == "temp":
|
if isinstance(item, dict) and item.get("type") == "temp":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
file_type = item.get(content_type_key, default_content_type)
|
file_type = item.get(content_type_key, default_content_type)
|
||||||
@@ -2000,6 +2010,7 @@ async def upload_in_background(
|
|||||||
("files", "content_type", "image/png"),
|
("files", "content_type", "image/png"),
|
||||||
("gifs", "format", "image/gif"),
|
("gifs", "format", "image/gif"),
|
||||||
("model_file", "format", "application/octet-stream"),
|
("model_file", "format", "application/octet-stream"),
|
||||||
|
("text_file", "format", "text/plain"),
|
||||||
]:
|
]:
|
||||||
items = data.get(file_type, [])
|
items = data.get(file_type, [])
|
||||||
|
|
||||||
@@ -2008,14 +2019,19 @@ async def upload_in_background(
|
|||||||
if file_type == "model_file":
|
if file_type == "model_file":
|
||||||
if isinstance(item, str):
|
if isinstance(item, str):
|
||||||
filename = os.path.basename(item)
|
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 = {
|
item = {
|
||||||
"filename": filename,
|
"filename": filename,
|
||||||
"subfolder": "",
|
"subfolder": subfolder,
|
||||||
"type": "output",
|
"type": "output",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Skip temp files
|
# Skip temp files
|
||||||
if item.get("type") == "temp":
|
if isinstance(item, dict) and item.get("type") == "temp":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Add to the upload queue instead of uploading immediately
|
# Add to the upload queue instead of uploading immediately
|
||||||
@@ -2080,6 +2096,7 @@ async def update_run_with_output(
|
|||||||
or "files" in data
|
or "files" in data
|
||||||
or "gifs" in data
|
or "gifs" in data
|
||||||
or "model_file" in data
|
or "model_file" in data
|
||||||
|
or "text_file" in data
|
||||||
)
|
)
|
||||||
if bypass_upload and have_upload_media:
|
if bypass_upload and have_upload_media:
|
||||||
print(
|
print(
|
||||||
@@ -2390,6 +2407,11 @@ class UploadQueue:
|
|||||||
logger.warning(f"No upload endpoint for prompt ID: {prompt_id}")
|
logger.warning(f"No upload endpoint for prompt ID: {prompt_id}")
|
||||||
return
|
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")
|
filename = file_info.get("filename")
|
||||||
subfolder = file_info.get("subfolder")
|
subfolder = file_info.get("subfolder")
|
||||||
file_type = file_info.get("type", "output")
|
file_type = file_info.get("type", "output")
|
||||||
@@ -2540,8 +2562,12 @@ class UploadQueue:
|
|||||||
# If this was the last file for this prompt, show the stats summary
|
# If this was the last file for this prompt, show the stats summary
|
||||||
if (
|
if (
|
||||||
prompt_id in self.pending_uploads
|
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
|
and len(self.pending_uploads[prompt_id]) == 1
|
||||||
):
|
):
|
||||||
|
# await update_run(prompt_id, Status.SUCCESS) # <-- REMOVE/COMMENT OUT
|
||||||
|
|
||||||
self._log_upload_stats(prompt_id)
|
self._log_upload_stats(prompt_id)
|
||||||
# Clean up stats
|
# Clean up stats
|
||||||
del self.upload_stats[prompt_id]
|
del self.upload_stats[prompt_id]
|
||||||
@@ -2662,6 +2688,8 @@ class UploadQueue:
|
|||||||
node_id = upload_task["node_id"]
|
node_id = upload_task["node_id"]
|
||||||
upload_id = upload_task["upload_id"]
|
upload_id = upload_task["upload_id"]
|
||||||
|
|
||||||
|
print(file_info)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Coordinate the actual start of the upload
|
# Coordinate the actual start of the upload
|
||||||
async with self.upload_lock:
|
async with self.upload_lock:
|
||||||
|
|||||||
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
|
||||||
|
}
|
||||||
+160
-44
@@ -168,12 +168,12 @@ function setSelectedWorkflowInfo(info) {
|
|||||||
|
|
||||||
const VALID_TYPES = [
|
const VALID_TYPES = [
|
||||||
"STRING",
|
"STRING",
|
||||||
"combo",
|
|
||||||
"number",
|
"number",
|
||||||
"toggle",
|
"toggle",
|
||||||
"BOOLEAN",
|
"BOOLEAN",
|
||||||
"text",
|
"text",
|
||||||
"string",
|
"string",
|
||||||
|
"combo",
|
||||||
];
|
];
|
||||||
|
|
||||||
function hideWidget(node, widget, suffix = "") {
|
function hideWidget(node, widget, suffix = "") {
|
||||||
@@ -181,7 +181,9 @@ function hideWidget(node, widget, suffix = "") {
|
|||||||
widget.origType = widget.type;
|
widget.origType = widget.type;
|
||||||
widget.origComputeSize = widget.computeSize;
|
widget.origComputeSize = widget.computeSize;
|
||||||
widget.origSerializeValue = widget.serializeValue;
|
widget.origSerializeValue = widget.serializeValue;
|
||||||
widget.computeSize = () => [0, -4];
|
// console.log(widget.origComputeSize);
|
||||||
|
// console.log(LiteGraph.NODE_SLOT_HEIGHT);
|
||||||
|
// widget.computeSize = () => [0, 0];
|
||||||
widget.type = CONVERTED_TYPE + suffix;
|
widget.type = CONVERTED_TYPE + suffix;
|
||||||
widget.serializeValue = () => {
|
widget.serializeValue = () => {
|
||||||
if (!node.inputs) {
|
if (!node.inputs) {
|
||||||
@@ -210,10 +212,37 @@ function getWidgetType(config) {
|
|||||||
return { type };
|
return { type };
|
||||||
}
|
}
|
||||||
|
|
||||||
const GET_CONFIG = Symbol();
|
async function convertToInput(node, widget, config) {
|
||||||
|
const { type } = getWidgetType(config);
|
||||||
|
|
||||||
|
console.log(node, widget, config);
|
||||||
|
|
||||||
|
const result = await app.extensionManager.dialog.prompt(
|
||||||
|
{
|
||||||
|
title: "Convert " + widget.name + " to external input",
|
||||||
|
message: "Input name",
|
||||||
|
defaultValue: widget.name,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result) return;
|
||||||
|
|
||||||
|
// Check for duplicate input IDs across existing external input nodes
|
||||||
|
const existingInputIds = Object.values(app.graph.nodes)
|
||||||
|
.filter(n => n.type.startsWith("ComfyUIDeployExternal"))
|
||||||
|
.map(n => n.widgets_values?.[0])
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (existingInputIds.includes(result)) {
|
||||||
|
app.extensionManager.toast.add({
|
||||||
|
severity: 'error',
|
||||||
|
summary: 'Input ID already exists',
|
||||||
|
detail: 'Please choose a different name.',
|
||||||
|
life: 3000
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function convertToInput(node, widget, config) {
|
|
||||||
console.log(node);
|
|
||||||
if (node.type == "LoadImage") {
|
if (node.type == "LoadImage") {
|
||||||
var inputNode = LiteGraph.createNode("ComfyUIDeployExternalImage");
|
var inputNode = LiteGraph.createNode("ComfyUIDeployExternalImage");
|
||||||
console.log(widget);
|
console.log(widget);
|
||||||
@@ -235,54 +264,94 @@ function convertToInput(node, widget, config) {
|
|||||||
|
|
||||||
const links = app.graph.links;
|
const links = app.graph.links;
|
||||||
|
|
||||||
console.log(currentOutputsLinks);
|
// console.log(currentOutputsLinks);
|
||||||
|
|
||||||
for (let i = 0; i < currentOutputsLinks.length; i++) {
|
if (currentOutputsLinks)
|
||||||
const link = currentOutputsLinks[i];
|
for (let i = 0; i < currentOutputsLinks.length; i++) {
|
||||||
const llink = links[link];
|
const link = currentOutputsLinks[i];
|
||||||
console.log(links[link]);
|
const llink = links[link];
|
||||||
setTimeout(
|
console.log(links[link]);
|
||||||
() => inputNode.connect(0, llink.target_id, llink.target_slot),
|
setTimeout(
|
||||||
100,
|
() => inputNode.connect(0, llink.target_id, llink.target_slot),
|
||||||
);
|
100,
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
|
||||||
node.connect(0, inputNode, 0);
|
node.connect(0, inputNode, 0);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
hideWidget(node, widget);
|
let externalNode = "";
|
||||||
const { type } = getWidgetType(config);
|
let inputId = result;
|
||||||
const sz = node.size;
|
|
||||||
const inputIsOptional = !!widget.options?.inputIsOptional;
|
|
||||||
const input = node.addInput(widget.name, type, {
|
|
||||||
widget: { name: widget.name, [GET_CONFIG]: () => config },
|
|
||||||
...(inputIsOptional ? { shape: LiteGraph.SlotShape.HollowCircle } : {}),
|
|
||||||
});
|
|
||||||
for (const widget2 of node.widgets) {
|
|
||||||
widget2.last_y += LiteGraph.NODE_SLOT_HEIGHT;
|
|
||||||
}
|
|
||||||
node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]);
|
|
||||||
|
|
||||||
if (type == "STRING") {
|
if (type === "INT") {
|
||||||
var inputNode = LiteGraph.createNode("ComfyUIDeployExternalText");
|
externalNode = "ComfyUIDeployExternalNumberInt";
|
||||||
console.log(widget);
|
// inputId = "input_number";
|
||||||
const index = node.inputs.findIndex((x) => x.name == widget.name);
|
}
|
||||||
console.log(node.widgets_values, index);
|
|
||||||
|
if (type === "FLOAT") {
|
||||||
|
externalNode = "ComfyUIDeployExternalNumberSlider";
|
||||||
|
// inputId = "input_number";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "STRING") {
|
||||||
|
externalNode = "ComfyUIDeployExternalText";
|
||||||
|
// inputId = "input_text";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === "COMBO") {
|
||||||
|
externalNode = "ComfyUIDeployExternalEnum";
|
||||||
|
// inputId = "input_enum";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!externalNode || !inputId) return;
|
||||||
|
|
||||||
|
node.convertWidgetToInput(widget);
|
||||||
|
|
||||||
|
var inputNode = LiteGraph.createNode(externalNode, "External Input: " + inputId);
|
||||||
|
|
||||||
|
// if (type === "COMBO") {
|
||||||
|
// inputNode = LiteGraph.createNode(externalNode, "External Input: " + inputId, {
|
||||||
|
// dynamic_enum_options: config[0],
|
||||||
|
// });
|
||||||
|
// console.log(inputNode);
|
||||||
|
// const options = config[0];
|
||||||
|
// console.log(options);
|
||||||
|
// } else {
|
||||||
|
// inputNode = LiteGraph.createNode(externalNode, "External Input: " + inputId);
|
||||||
|
// }
|
||||||
|
|
||||||
|
var options;
|
||||||
|
|
||||||
|
const index = node.inputs.findIndex((x) => x.name == widget.name);
|
||||||
|
if (type === "COMBO") {
|
||||||
|
options = widget.options?.values ?? config[0];
|
||||||
inputNode.configure({
|
inputNode.configure({
|
||||||
widgets_values: ["input_text", widget.value],
|
widgets_values: [inputId, widget.value, JSON.stringify(options)],
|
||||||
|
});
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
inputNode.configure({
|
||||||
|
widgets_values: [inputId, widget.value],
|
||||||
});
|
});
|
||||||
inputNode.id = ++app.graph.last_node_id;
|
|
||||||
inputNode.pos = node.pos;
|
|
||||||
inputNode.pos[0] -= node.size[0] + 40;
|
|
||||||
console.log(inputNode);
|
|
||||||
console.log(app.graph);
|
|
||||||
app.graph.add(inputNode);
|
|
||||||
inputNode.connect(0, node, index);
|
|
||||||
}
|
}
|
||||||
|
inputNode.id = ++app.graph.last_node_id;
|
||||||
|
inputNode.pos = node.pos;
|
||||||
|
inputNode.pos[0] -= node.size[0] + 160;
|
||||||
|
|
||||||
return input;
|
if (type === "COMBO") {
|
||||||
|
console.log(inputNode);
|
||||||
|
console.log(options);
|
||||||
|
inputNode.widgets.find((x) => x.name == "default_value").options.values = options;
|
||||||
|
}
|
||||||
|
|
||||||
|
app.graph.add(inputNode);
|
||||||
|
inputNode.connect(0, node, index);
|
||||||
|
|
||||||
|
app.graph.setDirtyCanvas(true, true);
|
||||||
|
|
||||||
|
return node.inputs.find((x) => x.name == widget.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONVERTED_TYPE = "converted-widget";
|
const CONVERTED_TYPE = "converted-widget";
|
||||||
@@ -295,7 +364,12 @@ function getConfig(widgetName) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isConvertibleWidget(widget, config) {
|
function isConvertibleWidget(node, widget, config) {
|
||||||
|
// console.log(config);
|
||||||
|
if (node.type === "LoadImage" && widget.type === "combo" && widget.name == "image") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
(VALID_TYPES.includes(widget.type) || VALID_TYPES.includes(config[0])) &&
|
(VALID_TYPES.includes(widget.type) || VALID_TYPES.includes(config[0])) &&
|
||||||
!widget.options?.forceInput
|
!widget.options?.forceInput
|
||||||
@@ -442,11 +516,11 @@ const ext = {
|
|||||||
w.type,
|
w.type,
|
||||||
w.options || {},
|
w.options || {},
|
||||||
];
|
];
|
||||||
if (isConvertibleWidget(w, config)) {
|
if (isConvertibleWidget(this, w, config)) {
|
||||||
toInput.push({
|
toInput.push({
|
||||||
content: `Convert ${w.name} to external input`,
|
content: `Convert ${w.name} to external input`,
|
||||||
callback: /* @__PURE__ */ __name(
|
callback: /* @__PURE__ */ __name(
|
||||||
() => convertToInput(this, w, config),
|
async () => convertToInput(this, w, config),
|
||||||
"callback",
|
"callback",
|
||||||
),
|
),
|
||||||
className: "comfydeploy-menu-item",
|
className: "comfydeploy-menu-item",
|
||||||
@@ -501,6 +575,13 @@ const ext = {
|
|||||||
console.log(nodeData.input.optional.default_value_url);
|
console.log(nodeData.input.optional.default_value_url);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
nodeData?.input?.optional?.default_value?.[1]?.dynamic_enum === true
|
||||||
|
) {
|
||||||
|
nodeData.input.optional.default_value = ["DYNAMIC_ENUM"];
|
||||||
|
// console.log(nodeData.input.optional.default_value);
|
||||||
|
}
|
||||||
|
|
||||||
// const origonNodeCreated = nodeType.prototype.onNodeCreated;
|
// const origonNodeCreated = nodeType.prototype.onNodeCreated;
|
||||||
// nodeType.prototype.onNodeCreated = function () {
|
// nodeType.prototype.onNodeCreated = function () {
|
||||||
// const r = origonNodeCreated
|
// const r = origonNodeCreated
|
||||||
@@ -534,6 +615,10 @@ const ext = {
|
|||||||
// return r
|
// return r
|
||||||
// };
|
// };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// async nodeCreated(node) {
|
||||||
|
|
||||||
|
// },
|
||||||
|
|
||||||
registerCustomNodes() {
|
registerCustomNodes() {
|
||||||
/** @type {LGraphNode}*/
|
/** @type {LGraphNode}*/
|
||||||
@@ -696,9 +781,37 @@ const ext = {
|
|||||||
|
|
||||||
return { widget: urlWidget };
|
return { widget: urlWidget };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
DYNAMIC_ENUM(node, inputName, inputData) {
|
||||||
|
// console.log("DYNAMIC_ENUM", JSON.parse(JSON.stringify(node)), inputName, inputData);
|
||||||
|
const enumWidget = node.addWidget(
|
||||||
|
"combo",
|
||||||
|
inputName,
|
||||||
|
"",
|
||||||
|
{ serialize: true, values: [] },
|
||||||
|
);
|
||||||
|
|
||||||
|
return { widget: enumWidget };
|
||||||
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async afterConfigureGraph() {
|
||||||
|
app.graph.nodes.forEach(node => {
|
||||||
|
if (node.type === "ComfyUIDeployExternalEnum") {
|
||||||
|
const default_value_index = node.widgets.findIndex(x => x.name === "default_value");
|
||||||
|
const options_index = node.widgets.findIndex(x => x.name === "options");
|
||||||
|
|
||||||
|
var dynamic_enum_options = [node.widgets[default_value_index].value];
|
||||||
|
if (node.widgets[options_index].value) {
|
||||||
|
dynamic_enum_options = JSON.parse(node.widgets[options_index].value);
|
||||||
|
}
|
||||||
|
// console.log("dynamic_enum_options", dynamic_enum_options);
|
||||||
|
node.widgets[default_value_index].options.values = dynamic_enum_options;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
async setup() {
|
async setup() {
|
||||||
// const graphCanvas = document.getElementById("graph-canvas");
|
// const graphCanvas = document.getElementById("graph-canvas");
|
||||||
|
|
||||||
@@ -886,6 +999,7 @@ const ext = {
|
|||||||
}
|
}
|
||||||
})(app.graph.onAfterChange);
|
})(app.graph.onAfterChange);
|
||||||
|
|
||||||
|
|
||||||
sendEventToCD("cd_plugin_setup");
|
sendEventToCD("cd_plugin_setup");
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -1071,6 +1185,8 @@ async function deployWorkflow() {
|
|||||||
const prompt = await app.graphToPrompt();
|
const prompt = await app.graphToPrompt();
|
||||||
let deps = undefined;
|
let deps = undefined;
|
||||||
|
|
||||||
|
console.log(prompt);
|
||||||
|
|
||||||
if (includeDeps) {
|
if (includeDeps) {
|
||||||
loadingDialog.showLoading("Fetching existing version");
|
loadingDialog.showLoading("Fetching existing version");
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user