Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b857b557f3 | ||
|
|
1666f78311 |
@@ -8,16 +8,6 @@ class ComfyUIDeployExternalBoolean:
|
|||||||
{"multiline": False, "default": "input_bool"},
|
{"multiline": False, "default": "input_bool"},
|
||||||
),
|
),
|
||||||
"default_value": ("BOOLEAN", {"default": False})
|
"default_value": ("BOOLEAN", {"default": False})
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,7 +16,7 @@ class ComfyUIDeployExternalBoolean:
|
|||||||
|
|
||||||
FUNCTION = "run"
|
FUNCTION = "run"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None, display_name=None, description=None):
|
def run(self, input_id, default_value=None):
|
||||||
print(f"Node '{input_id}' processing with switch set to {default_value}")
|
print(f"Node '{input_id}' processing with switch set to {default_value}")
|
||||||
return [default_value]
|
return [default_value]
|
||||||
|
|
||||||
|
|||||||
@@ -23,14 +23,6 @@ class ComfyUIDeployExternalCheckpoint:
|
|||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"default_value": (folder_paths.get_filename_list("checkpoints"), ),
|
"default_value": (folder_paths.get_filename_list("checkpoints"), ),
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,7 +33,7 @@ class ComfyUIDeployExternalCheckpoint:
|
|||||||
|
|
||||||
CATEGORY = "deploy"
|
CATEGORY = "deploy"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None, display_name=None, description=None):
|
def run(self, input_id, default_value=None):
|
||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|||||||
@@ -15,14 +15,6 @@ class ComfyUIDeployExternalImage:
|
|||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"default_value": ("IMAGE",),
|
"default_value": ("IMAGE",),
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +25,7 @@ class ComfyUIDeployExternalImage:
|
|||||||
|
|
||||||
CATEGORY = "image"
|
CATEGORY = "image"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None, display_name=None, description=None):
|
def run(self, input_id, default_value=None):
|
||||||
image = default_value
|
image = default_value
|
||||||
try:
|
try:
|
||||||
if input_id.startswith('http'):
|
if input_id.startswith('http'):
|
||||||
|
|||||||
@@ -15,14 +15,6 @@ class ComfyUIDeployExternalImageAlpha:
|
|||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"default_value": ("IMAGE",),
|
"default_value": ("IMAGE",),
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +25,7 @@ class ComfyUIDeployExternalImageAlpha:
|
|||||||
|
|
||||||
CATEGORY = "image"
|
CATEGORY = "image"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None, display_name=None, description=None):
|
def run(self, input_id, default_value=None):
|
||||||
image = default_value
|
image = default_value
|
||||||
try:
|
try:
|
||||||
if input_id.startswith('http'):
|
if input_id.startswith('http'):
|
||||||
|
|||||||
@@ -21,14 +21,6 @@ class ComfyUIDeployExternalImageBatch:
|
|||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"default_value": ("IMAGE",),
|
"default_value": ("IMAGE",),
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,7 +31,7 @@ class ComfyUIDeployExternalImageBatch:
|
|||||||
|
|
||||||
CATEGORY = "image"
|
CATEGORY = "image"
|
||||||
|
|
||||||
def run(self, input_id, images=None, default_value=None, display_name=None, description=None):
|
def run(self, input_id, images=None, default_value=None):
|
||||||
processed_images = []
|
processed_images = []
|
||||||
try:
|
try:
|
||||||
images_list = json.loads(images) # Assuming images is a JSON array string
|
images_list = json.loads(images) # Assuming images is a JSON array string
|
||||||
|
|||||||
@@ -4,15 +4,12 @@ import numpy as np
|
|||||||
import torch
|
import torch
|
||||||
import folder_paths
|
import folder_paths
|
||||||
|
|
||||||
|
|
||||||
class AnyType(str):
|
class AnyType(str):
|
||||||
def __ne__(self, __value: object) -> bool:
|
def __ne__(self, __value: object) -> bool:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
WILDCARD = AnyType("*")
|
WILDCARD = AnyType("*")
|
||||||
|
|
||||||
|
|
||||||
class ComfyUIDeployExternalLora:
|
class ComfyUIDeployExternalLora:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
@@ -25,22 +22,6 @@ class ComfyUIDeployExternalLora:
|
|||||||
},
|
},
|
||||||
"optional": {
|
"optional": {
|
||||||
"default_lora_name": (folder_paths.get_filename_list("loras"),),
|
"default_lora_name": (folder_paths.get_filename_list("loras"),),
|
||||||
"lora_save_name": ( # if `default_lora_name` is a link to download a file, we will attempt to save it with this name
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
"lora_url": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,43 +32,28 @@ class ComfyUIDeployExternalLora:
|
|||||||
|
|
||||||
CATEGORY = "deploy"
|
CATEGORY = "deploy"
|
||||||
|
|
||||||
def run(
|
def run(self, input_id, default_lora_name=None):
|
||||||
self,
|
|
||||||
input_id,
|
|
||||||
default_lora_name=None,
|
|
||||||
lora_save_name=None,
|
|
||||||
display_name=None,
|
|
||||||
description=None,
|
|
||||||
lora_url=None,
|
|
||||||
):
|
|
||||||
import requests
|
import requests
|
||||||
import os
|
import os
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
if lora_url and lora_url.startswith("http"):
|
if default_lora_name.startswith("http"):
|
||||||
if lora_save_name:
|
unique_filename = str(uuid.uuid4()) + ".safetensors"
|
||||||
existing_loras = folder_paths.get_filename_list("loras")
|
print(unique_filename)
|
||||||
# Check if lora_save_name exists in the list
|
|
||||||
if lora_save_name in existing_loras:
|
|
||||||
print(f"using lora: {lora_save_name}")
|
|
||||||
return (lora_save_name,)
|
|
||||||
else:
|
|
||||||
lora_save_name = str(uuid.uuid4()) + ".safetensors"
|
|
||||||
print(lora_save_name)
|
|
||||||
print(folder_paths.folder_names_and_paths["loras"][0][0])
|
print(folder_paths.folder_names_and_paths["loras"][0][0])
|
||||||
destination_path = os.path.join(
|
destination_path = os.path.join(
|
||||||
folder_paths.folder_names_and_paths["loras"][0][0], lora_save_name
|
folder_paths.folder_names_and_paths["loras"][0][0], unique_filename
|
||||||
)
|
)
|
||||||
print(destination_path)
|
print(destination_path)
|
||||||
print("Downloading external lora - " + lora_url + " to " + destination_path)
|
print("Downloading external lora - " + input_id + " to " + destination_path)
|
||||||
response = requests.get(
|
response = requests.get(
|
||||||
lora_url,
|
input_id,
|
||||||
headers={"User-Agent": "Mozilla/5.0"},
|
headers={"User-Agent": "Mozilla/5.0"},
|
||||||
allow_redirects=True,
|
allow_redirects=True,
|
||||||
)
|
)
|
||||||
with open(destination_path, "wb") as out_file:
|
with open(destination_path, "wb") as out_file:
|
||||||
out_file.write(response.content)
|
out_file.write(response.content)
|
||||||
return (lora_save_name,)
|
return (unique_filename,)
|
||||||
else:
|
else:
|
||||||
print(f"using lora: {default_lora_name}")
|
print(f"using lora: {default_lora_name}")
|
||||||
return (default_lora_name,)
|
return (default_lora_name,)
|
||||||
|
|||||||
@@ -16,15 +16,7 @@ class ComfyUIDeployExternalNumber:
|
|||||||
"optional": {
|
"optional": {
|
||||||
"default_value": (
|
"default_value": (
|
||||||
"FLOAT",
|
"FLOAT",
|
||||||
{"multiline": True, "display": "number", "default": 0, "min": -2147483647, "max": 2147483647, "step": 0.01},
|
{"multiline": True, "display": "number", "default": 0, "step": 0.01},
|
||||||
),
|
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,7 +28,7 @@ class ComfyUIDeployExternalNumber:
|
|||||||
|
|
||||||
CATEGORY = "number"
|
CATEGORY = "number"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None, display_name=None, description=None):
|
def run(self, input_id, default_value=None):
|
||||||
try:
|
try:
|
||||||
float_value = float(input_id)
|
float_value = float(input_id)
|
||||||
print("my number", float_value)
|
print("my number", float_value)
|
||||||
|
|||||||
@@ -16,15 +16,7 @@ class ComfyUIDeployExternalNumberInt:
|
|||||||
"optional": {
|
"optional": {
|
||||||
"default_value": (
|
"default_value": (
|
||||||
"INT",
|
"INT",
|
||||||
{"multiline": True, "display": "number", "min": -2147483647, "max": 2147483647, "default": 0},
|
{"multiline": True, "display": "number", "default": 0},
|
||||||
),
|
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,7 +28,7 @@ class ComfyUIDeployExternalNumberInt:
|
|||||||
|
|
||||||
CATEGORY = "number"
|
CATEGORY = "number"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None, display_name=None, description=None):
|
def run(self, input_id, default_value=None):
|
||||||
if not input_id or (isinstance(input_id, str) and not input_id.strip().isdigit()):
|
if not input_id or (isinstance(input_id, str) and not input_id.strip().isdigit()):
|
||||||
return [default_value]
|
return [default_value]
|
||||||
return [int(input_id)]
|
return [int(input_id)]
|
||||||
|
|||||||
@@ -11,23 +11,15 @@ class ComfyUIDeployExternalNumberSlider:
|
|||||||
"optional": {
|
"optional": {
|
||||||
"default_value": (
|
"default_value": (
|
||||||
"FLOAT",
|
"FLOAT",
|
||||||
{"multiline": True, "display": "number", "min": -2147483647, "max": 2147483647, "default": 0.5, "step": 0.01},
|
{"multiline": True, "display": "number", "default": 0.5, "step": 0.01},
|
||||||
),
|
),
|
||||||
"min_value": (
|
"min_value": (
|
||||||
"FLOAT",
|
"FLOAT",
|
||||||
{"multiline": True, "display": "number", "min": -2147483647, "max": 2147483647, "default": 0, "step": 0.01},
|
{"multiline": True, "display": "number", "default": 0, "step": 0.01},
|
||||||
),
|
),
|
||||||
"max_value": (
|
"max_value": (
|
||||||
"FLOAT",
|
"FLOAT",
|
||||||
{"multiline": True, "display": "number", "min": -2147483647, "max": 2147483647, "default": 1, "step": 0.01},
|
{"multiline": True, "display": "number", "default": 1, "step": 0.01},
|
||||||
),
|
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -39,7 +31,7 @@ class ComfyUIDeployExternalNumberSlider:
|
|||||||
|
|
||||||
CATEGORY = "number"
|
CATEGORY = "number"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None, min_value=0, max_value=1, display_name=None, description=None):
|
def run(self, input_id, default_value=None, min_value=0, max_value=1):
|
||||||
try:
|
try:
|
||||||
float_value = float(input_id)
|
float_value = float(input_id)
|
||||||
if min_value <= float_value <= max_value:
|
if min_value <= float_value <= max_value:
|
||||||
|
|||||||
@@ -18,14 +18,6 @@ class ComfyUIDeployExternalText:
|
|||||||
"STRING",
|
"STRING",
|
||||||
{"multiline": True, "default": ""},
|
{"multiline": True, "default": ""},
|
||||||
),
|
),
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,7 +28,7 @@ class ComfyUIDeployExternalText:
|
|||||||
|
|
||||||
CATEGORY = "text"
|
CATEGORY = "text"
|
||||||
|
|
||||||
def run(self, input_id, default_value=None, display_name=None, description=None):
|
def run(self, input_id, default_value=None):
|
||||||
return [default_value]
|
return [default_value]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
import folder_paths
|
|
||||||
from PIL import Image, ImageOps
|
|
||||||
import numpy as np
|
|
||||||
import torch
|
|
||||||
import json
|
|
||||||
|
|
||||||
class ComfyUIDeployExternalTextList:
|
|
||||||
@classmethod
|
|
||||||
def INPUT_TYPES(s):
|
|
||||||
return {
|
|
||||||
"required": {
|
|
||||||
"input_id": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": 'input_text_list'},
|
|
||||||
),
|
|
||||||
"text": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": "[]"},
|
|
||||||
),
|
|
||||||
},
|
|
||||||
"optional": {
|
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = ("STRING",)
|
|
||||||
RETURN_NAMES = ("text",)
|
|
||||||
|
|
||||||
OUTPUT_IS_LIST = (True,)
|
|
||||||
|
|
||||||
FUNCTION = "run"
|
|
||||||
|
|
||||||
CATEGORY = "text"
|
|
||||||
|
|
||||||
def run(self, input_id, text=None, display_name=None, description=None):
|
|
||||||
text_list = []
|
|
||||||
try:
|
|
||||||
text_list = json.loads(text) # Assuming text is a JSON array string
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error processing images: {e}")
|
|
||||||
pass
|
|
||||||
return ([text_list],)
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalTextList": ComfyUIDeployExternalTextList}
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyUIDeployExternalTextList": "External Text List (ComfyUI Deploy)"}
|
|
||||||
@@ -764,15 +764,7 @@ class ComfyUIDeployExternalVideo:
|
|||||||
"optional": {
|
"optional": {
|
||||||
"meta_batch": ("VHS_BatchManager",),
|
"meta_batch": ("VHS_BatchManager",),
|
||||||
"vae": ("VAE",),
|
"vae": ("VAE",),
|
||||||
"default_video": (sorted(files),),
|
"default_value": (sorted(files),),
|
||||||
"display_name": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": False, "default": ""},
|
|
||||||
),
|
|
||||||
"description": (
|
|
||||||
"STRING",
|
|
||||||
{"multiline": True, "default": ""},
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
"hidden": {
|
"hidden": {
|
||||||
"unique_id": "UNIQUE_ID"
|
"unique_id": "UNIQUE_ID"
|
||||||
@@ -804,6 +796,8 @@ class ComfyUIDeployExternalVideo:
|
|||||||
meta_batch = kwargs.get("meta_batch")
|
meta_batch = kwargs.get("meta_batch")
|
||||||
unique_id = kwargs.get("unique_id")
|
unique_id = kwargs.get("unique_id")
|
||||||
|
|
||||||
|
video = kwargs.get("default_value")
|
||||||
|
video_path = folder_paths.get_annotated_filepath(video.strip('"'))
|
||||||
|
|
||||||
input_dir = folder_paths.get_input_directory()
|
input_dir = folder_paths.get_input_directory()
|
||||||
if input_id.startswith("http"):
|
if input_id.startswith("http"):
|
||||||
@@ -833,11 +827,8 @@ class ComfyUIDeployExternalVideo:
|
|||||||
leave=True,
|
leave=True,
|
||||||
):
|
):
|
||||||
out_file.write(chunk)
|
out_file.write(chunk)
|
||||||
else:
|
|
||||||
video = kwargs.get("default_video", None)
|
print("video path: ", video_path)
|
||||||
if video is None:
|
|
||||||
raise "No default video given and no external video provided"
|
|
||||||
video_path = folder_paths.get_annotated_filepath(video.strip('"'))
|
|
||||||
|
|
||||||
return load_video_cv(
|
return load_video_cv(
|
||||||
video=video_path,
|
video=video_path,
|
||||||
|
|||||||
+50
-187
@@ -1,5 +1,4 @@
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pprint import pprint
|
|
||||||
from aiohttp import web
|
from aiohttp import web
|
||||||
import os
|
import os
|
||||||
import requests
|
import requests
|
||||||
@@ -18,13 +17,12 @@ from urllib.parse import quote
|
|||||||
import threading
|
import threading
|
||||||
import hashlib
|
import hashlib
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from aiohttp import ClientSession, web
|
|
||||||
import aiofiles
|
import aiofiles
|
||||||
from typing import Dict, List, Union, Any, Optional
|
from typing import Dict, List, Union, Any, Optional
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
import copy
|
import copy
|
||||||
import struct
|
import struct
|
||||||
from aiohttp import web, ClientSession, ClientError, ClientTimeout
|
from aiohttp import ClientError
|
||||||
import atexit
|
import atexit
|
||||||
|
|
||||||
# Global session
|
# Global session
|
||||||
@@ -52,65 +50,28 @@ def exit_handler():
|
|||||||
|
|
||||||
atexit.register(exit_handler)
|
atexit.register(exit_handler)
|
||||||
|
|
||||||
max_retries = int(os.environ.get('MAX_RETRIES', '5'))
|
max_retries = int(os.environ.get('MAX_RETRIES', '3'))
|
||||||
retry_delay_multiplier = float(os.environ.get('RETRY_DELAY_MULTIPLIER', '2'))
|
retry_delay_multiplier = float(os.environ.get('RETRY_DELAY_MULTIPLIER', '2'))
|
||||||
|
|
||||||
print(f"max_retries: {max_retries}, retry_delay_multiplier: {retry_delay_multiplier}")
|
print(f"max_retries: {max_retries}, retry_delay_multiplier: {retry_delay_multiplier}")
|
||||||
|
|
||||||
import time
|
async def async_request_with_retry(method, url, **kwargs):
|
||||||
|
|
||||||
async def async_request_with_retry(method, url, disable_timeout=False, **kwargs):
|
|
||||||
global client_session
|
global client_session
|
||||||
await ensure_client_session()
|
await ensure_client_session()
|
||||||
retry_delay = 1 # Start with 1 second delay
|
retry_delay = 1 # Start with 1 second delay
|
||||||
initial_timeout = 5 # 5 seconds timeout for the initial connection
|
|
||||||
|
|
||||||
start_time = time.time()
|
|
||||||
for attempt in range(max_retries):
|
for attempt in range(max_retries):
|
||||||
try:
|
try:
|
||||||
if not disable_timeout:
|
|
||||||
timeout = ClientTimeout(total=None, connect=initial_timeout)
|
|
||||||
kwargs['timeout'] = timeout
|
|
||||||
|
|
||||||
request_start = time.time()
|
|
||||||
async with client_session.request(method, url, **kwargs) as response:
|
async with client_session.request(method, url, **kwargs) as response:
|
||||||
request_end = time.time()
|
|
||||||
logger.info(f"Request attempt {attempt + 1} took {request_end - request_start:.2f} seconds")
|
|
||||||
|
|
||||||
if response.status != 200:
|
|
||||||
error_body = await response.text()
|
|
||||||
logger.error(f"Request failed with status {response.status} and body {error_body}")
|
|
||||||
# raise Exception(f"Request failed with status {response.status}")
|
|
||||||
|
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
if method.upper() == 'GET':
|
|
||||||
await response.read()
|
|
||||||
|
|
||||||
total_time = time.time() - start_time
|
|
||||||
logger.info(f"Request succeeded after {total_time:.2f} seconds (attempt {attempt + 1}/{max_retries})")
|
|
||||||
return response
|
return response
|
||||||
except asyncio.TimeoutError:
|
|
||||||
logger.warning(f"Request timed out after {initial_timeout} seconds (attempt {attempt + 1}/{max_retries})")
|
|
||||||
except ClientError as e:
|
except ClientError as e:
|
||||||
end_time = time.time()
|
|
||||||
logger.error(f"Request failed (attempt {attempt + 1}/{max_retries}): {e}")
|
|
||||||
logger.error(f"Time taken for failed attempt: {end_time - request_start:.2f} seconds")
|
|
||||||
logger.error(f"Total time elapsed: {end_time - start_time:.2f} seconds")
|
|
||||||
|
|
||||||
# Log the response body for ClientError as well
|
|
||||||
if hasattr(e, 'response') and e.response is not None:
|
|
||||||
error_body = await e.response.text()
|
|
||||||
logger.error(f"Error response body: {error_body}")
|
|
||||||
|
|
||||||
if attempt == max_retries - 1:
|
if attempt == max_retries - 1:
|
||||||
logger.error(f"Request failed after {max_retries} attempts: {e}")
|
logger.error(f"Request failed after {max_retries} attempts: {e}")
|
||||||
raise
|
# raise
|
||||||
|
logger.warning(f"Request failed (attempt {attempt + 1}/{max_retries}): {e}")
|
||||||
await asyncio.sleep(retry_delay)
|
await asyncio.sleep(retry_delay)
|
||||||
retry_delay *= retry_delay_multiplier
|
retry_delay *= retry_delay_multiplier # Exponential backoff
|
||||||
|
|
||||||
total_time = time.time() - start_time
|
|
||||||
raise Exception(f"Request failed after {max_retries} attempts and {total_time:.2f} seconds")
|
|
||||||
|
|
||||||
from logging import basicConfig, getLogger
|
from logging import basicConfig, getLogger
|
||||||
|
|
||||||
@@ -147,8 +108,7 @@ def log_span(name):
|
|||||||
if use_logfire:
|
if use_logfire:
|
||||||
with logger.span(name):
|
with logger.span(name):
|
||||||
yield
|
yield
|
||||||
else:
|
# else:
|
||||||
yield
|
|
||||||
# logger.info(f"Start: {name}")
|
# logger.info(f"Start: {name}")
|
||||||
# yield
|
# yield
|
||||||
# logger.info(f"End: {name}")
|
# logger.info(f"End: {name}")
|
||||||
@@ -256,30 +216,13 @@ def apply_random_seed_to_workflow(workflow_api):
|
|||||||
workflow_api (dict): The workflow API dictionary to modify.
|
workflow_api (dict): The workflow API dictionary to modify.
|
||||||
"""
|
"""
|
||||||
for key in workflow_api:
|
for key in workflow_api:
|
||||||
if 'inputs' in workflow_api[key]:
|
if 'inputs' in workflow_api[key] and 'seed' in workflow_api[key]['inputs']:
|
||||||
if 'seed' in workflow_api[key]['inputs']:
|
if isinstance(workflow_api[key]['inputs']['seed'], list):
|
||||||
if isinstance(workflow_api[key]['inputs']['seed'], list):
|
continue
|
||||||
continue
|
if workflow_api[key]['class_type'] == "PromptExpansion":
|
||||||
if workflow_api[key]['class_type'] == "PromptExpansion":
|
workflow_api[key]['inputs']['seed'] = randomSeed(8);
|
||||||
workflow_api[key]['inputs']['seed'] = randomSeed(8)
|
continue
|
||||||
logger.info(f"Applied random seed {workflow_api[key]['inputs']['seed']} to PromptExpansion")
|
workflow_api[key]['inputs']['seed'] = randomSeed();
|
||||||
continue
|
|
||||||
workflow_api[key]['inputs']['seed'] = randomSeed()
|
|
||||||
logger.info(f"Applied random seed {workflow_api[key]['inputs']['seed']} to {workflow_api[key]['class_type']}")
|
|
||||||
|
|
||||||
if 'noise_seed' in workflow_api[key]['inputs']:
|
|
||||||
if workflow_api[key]['class_type'] == "RandomNoise":
|
|
||||||
workflow_api[key]['inputs']['noise_seed'] = randomSeed()
|
|
||||||
logger.info(f"Applied random noise_seed {workflow_api[key]['inputs']['noise_seed']} to RandomNoise")
|
|
||||||
continue
|
|
||||||
if workflow_api[key]['class_type'] == "KSamplerAdvanced":
|
|
||||||
workflow_api[key]['inputs']['noise_seed'] = randomSeed()
|
|
||||||
logger.info(f"Applied random noise_seed {workflow_api[key]['inputs']['noise_seed']} to KSamplerAdvanced")
|
|
||||||
continue
|
|
||||||
if workflow_api[key]['class_type'] == "SamplerCustom":
|
|
||||||
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):
|
||||||
# Loop through each of the inputs and replace them
|
# Loop through each of the inputs and replace them
|
||||||
@@ -314,7 +257,7 @@ def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None):
|
|||||||
value['inputs']["images"] = new_value
|
value['inputs']["images"] = new_value
|
||||||
|
|
||||||
if value["class_type"] == "ComfyUIDeployExternalLora":
|
if value["class_type"] == "ComfyUIDeployExternalLora":
|
||||||
value["inputs"]["lora_url"] = new_value
|
value["inputs"]["default_lora_name"] = new_value
|
||||||
|
|
||||||
if value["class_type"] == "ComfyUIDeployExternalSlider":
|
if value["class_type"] == "ComfyUIDeployExternalSlider":
|
||||||
value["inputs"]["default_value"] = new_value
|
value["inputs"]["default_value"] = new_value
|
||||||
@@ -405,7 +348,7 @@ async def comfy_deploy_run(request):
|
|||||||
|
|
||||||
status = 200
|
status = 200
|
||||||
|
|
||||||
if "node_errors" in res and res["node_errors"] is not None and len(res["node_errors"]) > 0:
|
if "node_errors" in res and res["node_errors"]:
|
||||||
# Even tho there are node_errors it can still be run
|
# Even tho there are node_errors it can still be run
|
||||||
status = 400
|
status = 400
|
||||||
await update_run_with_output(prompt_id, {
|
await update_run_with_output(prompt_id, {
|
||||||
@@ -443,7 +386,7 @@ async def stream_prompt(data):
|
|||||||
workflow_api=workflow_api
|
workflow_api=workflow_api
|
||||||
)
|
)
|
||||||
|
|
||||||
# log('info', "Begin prompt", prompt=prompt)
|
log('info', "Begin prompt", prompt=prompt)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = post_prompt(prompt)
|
res = post_prompt(prompt)
|
||||||
@@ -466,7 +409,7 @@ async def stream_prompt(data):
|
|||||||
|
|
||||||
status = 200
|
status = 200
|
||||||
|
|
||||||
if "node_errors" in res and res["node_errors"] is not None and len(res["node_errors"]) > 0:
|
if "node_errors" in res and res["node_errors"]:
|
||||||
# Even tho there are node_errors it can still be run
|
# Even tho there are node_errors it can still be run
|
||||||
status = 400
|
status = 400
|
||||||
await update_run_with_output(prompt_id, {
|
await update_run_with_output(prompt_id, {
|
||||||
@@ -510,7 +453,7 @@ async def stream_response(request):
|
|||||||
if not comfy_message_queues[prompt_id].empty():
|
if not comfy_message_queues[prompt_id].empty():
|
||||||
data = await comfy_message_queues[prompt_id].get()
|
data = await comfy_message_queues[prompt_id].get()
|
||||||
|
|
||||||
# log('info', data["event"], data=json.dumps(data))
|
log('info', data["event"], data=json.dumps(data))
|
||||||
# logger.info("listener", data)
|
# logger.info("listener", data)
|
||||||
await response.write(f"event: event_update\ndata: {json.dumps(data)}\n\n".encode('utf-8'))
|
await response.write(f"event: event_update\ndata: {json.dumps(data)}\n\n".encode('utf-8'))
|
||||||
await response.drain() # Ensure the buffer is flushed
|
await response.drain() # Ensure the buffer is flushed
|
||||||
@@ -629,10 +572,9 @@ async def upload_file_endpoint(request):
|
|||||||
with open(file_path, 'rb') as f:
|
with open(file_path, 'rb') as f:
|
||||||
headers = {
|
headers = {
|
||||||
"Content-Type": file_type,
|
"Content-Type": file_type,
|
||||||
# "Content-Length": str(file_size)
|
# "x-amz-acl": "public-read",
|
||||||
|
"Content-Length": str(file_size)
|
||||||
}
|
}
|
||||||
if content.get('include_acl') is True:
|
|
||||||
headers["x-amz-acl"] = "public-read"
|
|
||||||
upload_response = await async_request_with_retry('PUT', upload_url, data=f, headers=headers)
|
upload_response = await async_request_with_retry('PUT', upload_url, data=f, headers=headers)
|
||||||
if upload_response.status == 200:
|
if upload_response.status == 200:
|
||||||
return web.json_response({
|
return web.json_response({
|
||||||
@@ -879,50 +821,6 @@ async def send(event, data, sid=None):
|
|||||||
logger.info(f"Exception: {e}")
|
logger.info(f"Exception: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
@server.PromptServer.instance.routes.get('/comfydeploy/{tail:.*}')
|
|
||||||
@server.PromptServer.instance.routes.post('/comfydeploy/{tail:.*}')
|
|
||||||
async def proxy_to_comfydeploy(request):
|
|
||||||
# Get the base URL
|
|
||||||
base_url = f'https://www.comfydeploy.com/{request.match_info["tail"]}'
|
|
||||||
|
|
||||||
# Get all query parameters
|
|
||||||
query_params = request.query_string
|
|
||||||
|
|
||||||
# Construct the full target URL with query parameters
|
|
||||||
target_url = f"{base_url}?{query_params}" if query_params else base_url
|
|
||||||
|
|
||||||
# print(f"Proxying request to: {target_url}")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Create a new ClientSession for each request
|
|
||||||
async with ClientSession() as client_session:
|
|
||||||
# Forward the request
|
|
||||||
client_req = await client_session.request(
|
|
||||||
method=request.method,
|
|
||||||
url=target_url,
|
|
||||||
headers={k: v for k, v in request.headers.items() if k.lower() not in ('host', 'content-length')},
|
|
||||||
data=await request.read(),
|
|
||||||
allow_redirects=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Read the entire response content
|
|
||||||
content = await client_req.read()
|
|
||||||
|
|
||||||
# Try to decode the content as JSON
|
|
||||||
try:
|
|
||||||
json_data = json.loads(content)
|
|
||||||
# If successful, return a JSON response
|
|
||||||
return web.json_response(json_data, status=client_req.status)
|
|
||||||
except json.JSONDecodeError:
|
|
||||||
# If it's not valid JSON, return the content as-is
|
|
||||||
return web.Response(body=content, status=client_req.status, headers=client_req.headers)
|
|
||||||
|
|
||||||
except ClientError as e:
|
|
||||||
print(f"Client error occurred while proxying request: {str(e)}")
|
|
||||||
return web.Response(status=502, text=f"Bad Gateway: {str(e)}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error occurred while proxying request: {str(e)}")
|
|
||||||
return web.Response(status=500, text=f"Internal Server Error: {str(e)}")
|
|
||||||
|
|
||||||
|
|
||||||
prompt_server = server.PromptServer.instance
|
prompt_server = server.PromptServer.instance
|
||||||
@@ -1015,22 +913,17 @@ async def send_json_override(self, event, data, sid=None):
|
|||||||
# await update_run_with_output(prompt_id, data)
|
# await update_run_with_output(prompt_id, data)
|
||||||
|
|
||||||
if event == 'executed' and 'node' in data and 'output' in data:
|
if event == 'executed' and 'node' in data and 'output' in data:
|
||||||
node_meta = None
|
|
||||||
if prompt_id in prompt_metadata:
|
if prompt_id in prompt_metadata:
|
||||||
node = data.get('node')
|
node = data.get('node')
|
||||||
class_type = prompt_metadata[prompt_id].workflow_api[node]['class_type']
|
class_type = prompt_metadata[prompt_id].workflow_api[node]['class_type']
|
||||||
logger.info(f"Executed {class_type} {data}")
|
logger.info(f"Executed {class_type} {data}")
|
||||||
node_meta = {
|
|
||||||
"node_id": node,
|
|
||||||
"node_class": class_type,
|
|
||||||
}
|
|
||||||
if class_type == "PreviewImage":
|
if class_type == "PreviewImage":
|
||||||
logger.info("Skipping preview image")
|
logger.info("Skipping preview image")
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
logger.info(f"Executed {data}")
|
logger.info(f"Executed {data}")
|
||||||
|
|
||||||
await update_run_with_output(prompt_id, data.get('output'), node_id=data.get('node'), node_meta=node_meta)
|
await update_run_with_output(prompt_id, data.get('output'), node_id=data.get('node'))
|
||||||
# await update_run_with_output(prompt_id, data.get('output'), node_id=data.get('node'))
|
# await update_run_with_output(prompt_id, data.get('output'), node_id=data.get('node'))
|
||||||
# update_run_with_output(prompt_id, data.get('output'))
|
# update_run_with_output(prompt_id, data.get('output'))
|
||||||
|
|
||||||
@@ -1150,7 +1043,7 @@ async def update_run(prompt_id: str, status: Status):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
async def upload_file(prompt_id, filename, subfolder=None, content_type="image/png", type="output", item=None):
|
async def upload_file(prompt_id, filename, subfolder=None, content_type="image/png", type="output"):
|
||||||
"""
|
"""
|
||||||
Uploads file to S3 bucket using S3 client object
|
Uploads file to S3 bucket using S3 client object
|
||||||
:return: None
|
:return: None
|
||||||
@@ -1185,41 +1078,29 @@ async def upload_file(prompt_id, filename, subfolder=None, content_type="image/p
|
|||||||
prompt_id = quote(prompt_id)
|
prompt_id = quote(prompt_id)
|
||||||
content_type = quote(content_type)
|
content_type = quote(content_type)
|
||||||
|
|
||||||
async with aiofiles.open(file, 'rb') as f:
|
target_url = f"{file_upload_endpoint}?file_name={filename}&run_id={prompt_id}&type={content_type}"
|
||||||
data = await f.read()
|
|
||||||
size = str(len(data))
|
|
||||||
target_url = f"{file_upload_endpoint}?file_name={filename}&run_id={prompt_id}&type={content_type}&version=v2"
|
|
||||||
|
|
||||||
start_time = time.time() # Start timing here
|
start_time = time.time() # Start timing here
|
||||||
logger.info(f"Target URL: {target_url}")
|
result = requests.get(target_url)
|
||||||
result = await async_request_with_retry("GET", target_url, disable_timeout=True)
|
end_time = time.time() # End timing after the request is complete
|
||||||
end_time = time.time() # End timing after the request is complete
|
logger.info("Time taken for getting file upload endpoint: {:.2f} seconds".format(end_time - start_time))
|
||||||
logger.info("Time taken for getting file upload endpoint: {:.2f} seconds".format(end_time - start_time))
|
ok = result.json()
|
||||||
ok = await result.json()
|
|
||||||
|
|
||||||
logger.info(f"Result: {ok}")
|
start_time = time.time() # Start timing here
|
||||||
|
|
||||||
start_time = time.time() # Start timing here
|
with open(file, 'rb') as f:
|
||||||
|
data = f.read()
|
||||||
headers = {
|
headers = {
|
||||||
|
# "x-amz-acl": "public-read",
|
||||||
"Content-Type": content_type,
|
"Content-Type": content_type,
|
||||||
# "Content-Length": size,
|
"Content-Length": str(len(data)),
|
||||||
}
|
}
|
||||||
|
|
||||||
if ok.get('include_acl') is True:
|
|
||||||
headers["x-amz-acl"] = "public-read"
|
|
||||||
|
|
||||||
# response = requests.put(ok.get("url"), headers=headers, data=data)
|
# response = requests.put(ok.get("url"), headers=headers, data=data)
|
||||||
response = await async_request_with_retry('PUT', ok.get("url"), headers=headers, data=data)
|
response = await async_request_with_retry('PUT', ok.get("url"), headers=headers, data=data)
|
||||||
logger.info(f"Upload file response status: {response.status}, status text: {response.reason}")
|
logger.info(f"Upload file response status: {response.status}, status text: {response.reason}")
|
||||||
end_time = time.time() # End timing after the request is complete
|
end_time = time.time() # End timing after the request is complete
|
||||||
logger.info("Upload time: {:.2f} seconds".format(end_time - start_time))
|
logger.info("Upload time: {:.2f} seconds".format(end_time - start_time))
|
||||||
|
|
||||||
if item is not None:
|
|
||||||
file_download_url = ok.get("download_url")
|
|
||||||
if file_download_url is not None:
|
|
||||||
item["url"] = file_download_url
|
|
||||||
item["upload_duration"] = end_time - start_time
|
|
||||||
|
|
||||||
def have_pending_upload(prompt_id):
|
def have_pending_upload(prompt_id):
|
||||||
if prompt_id in prompt_metadata and len(prompt_metadata[prompt_id].uploading_nodes) > 0:
|
if prompt_id in prompt_metadata and len(prompt_metadata[prompt_id].uploading_nodes) > 0:
|
||||||
logger.info(f"Have pending upload {len(prompt_metadata[prompt_id].uploading_nodes)}")
|
logger.info(f"Have pending upload {len(prompt_metadata[prompt_id].uploading_nodes)}")
|
||||||
@@ -1311,10 +1192,8 @@ async def update_file_status(prompt_id: str, data, uploading, have_error=False,
|
|||||||
|
|
||||||
async def handle_upload(prompt_id: str, data, key: str, content_type_key: str, default_content_type: str):
|
async def handle_upload(prompt_id: str, data, key: str, content_type_key: str, default_content_type: str):
|
||||||
items = data.get(key, [])
|
items = data.get(key, [])
|
||||||
upload_tasks = []
|
|
||||||
|
|
||||||
for item in items:
|
for item in items:
|
||||||
# Skipping temp files
|
# # Skipping temp files
|
||||||
if item.get("type") == "temp":
|
if item.get("type") == "temp":
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -1327,45 +1206,29 @@ async def handle_upload(prompt_id: str, data, key: str, content_type_key: str, d
|
|||||||
elif file_extension == '.webp':
|
elif file_extension == '.webp':
|
||||||
file_type = 'image/webp'
|
file_type = 'image/webp'
|
||||||
|
|
||||||
upload_tasks.append(upload_file(
|
await upload_file(
|
||||||
prompt_id,
|
prompt_id,
|
||||||
item.get("filename"),
|
item.get("filename"),
|
||||||
subfolder=item.get("subfolder"),
|
subfolder=item.get("subfolder"),
|
||||||
type=item.get("type"),
|
type=item.get("type"),
|
||||||
content_type=file_type,
|
content_type=file_type
|
||||||
item=item
|
)
|
||||||
))
|
|
||||||
|
|
||||||
# Execute all upload tasks concurrently
|
|
||||||
await asyncio.gather(*upload_tasks)
|
|
||||||
|
|
||||||
# Upload files in the background
|
# Upload files in the background
|
||||||
async def upload_in_background(prompt_id: str, data, node_id=None, have_upload=True, node_meta=None):
|
async def upload_in_background(prompt_id: str, data, node_id=None, have_upload=True):
|
||||||
try:
|
try:
|
||||||
upload_tasks = [
|
await handle_upload(prompt_id, data, 'images', "content_type", "image/png")
|
||||||
handle_upload(prompt_id, data, 'images', "content_type", "image/png"),
|
await handle_upload(prompt_id, data, 'files', "content_type", "image/png")
|
||||||
handle_upload(prompt_id, data, 'files', "content_type", "image/png"),
|
# This will also be mp4
|
||||||
handle_upload(prompt_id, data, 'gifs', "format", "image/gif"),
|
await handle_upload(prompt_id, data, 'gifs', "format", "image/gif")
|
||||||
handle_upload(prompt_id, data, 'mesh', "format", "application/octet-stream")
|
await handle_upload(prompt_id, data, 'mesh', "format", "application/octet-stream")
|
||||||
]
|
|
||||||
|
|
||||||
await asyncio.gather(*upload_tasks)
|
|
||||||
|
|
||||||
status_endpoint = prompt_metadata[prompt_id].status_endpoint
|
|
||||||
if have_upload:
|
if have_upload:
|
||||||
if status_endpoint is not None:
|
|
||||||
body = {
|
|
||||||
"run_id": prompt_id,
|
|
||||||
"output_data": data,
|
|
||||||
"node_meta": node_meta,
|
|
||||||
}
|
|
||||||
# pprint(body)
|
|
||||||
await async_request_with_retry('POST', status_endpoint, json=body)
|
|
||||||
await update_file_status(prompt_id, data, False, node_id=node_id)
|
await update_file_status(prompt_id, data, False, node_id=node_id)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await handle_error(prompt_id, data, e)
|
await handle_error(prompt_id, data, e)
|
||||||
|
|
||||||
async def update_run_with_output(prompt_id, data, node_id=None, node_meta=None):
|
async def update_run_with_output(prompt_id, data, node_id=None):
|
||||||
if prompt_id not in prompt_metadata:
|
if prompt_id not in prompt_metadata:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1376,8 +1239,7 @@ async def update_run_with_output(prompt_id, data, node_id=None, node_meta=None):
|
|||||||
|
|
||||||
body = {
|
body = {
|
||||||
"run_id": prompt_id,
|
"run_id": prompt_id,
|
||||||
"output_data": data,
|
"output_data": data
|
||||||
"node_meta": node_meta,
|
|
||||||
}
|
}
|
||||||
have_upload_media = 'images' in data or 'files' in data or 'gifs' in data or 'mesh' in data
|
have_upload_media = 'images' in data or 'files' in data or 'gifs' in data or 'mesh' in data
|
||||||
if bypass_upload and have_upload_media:
|
if bypass_upload and have_upload_media:
|
||||||
@@ -1391,13 +1253,14 @@ async def update_run_with_output(prompt_id, data, node_id=None, node_meta=None):
|
|||||||
if have_upload_media:
|
if have_upload_media:
|
||||||
await update_file_status(prompt_id, data, True, node_id=node_id)
|
await update_file_status(prompt_id, data, True, node_id=node_id)
|
||||||
|
|
||||||
asyncio.create_task(upload_in_background(prompt_id, data, node_id=node_id, have_upload=have_upload_media, node_meta=node_meta))
|
asyncio.create_task(upload_in_background(prompt_id, data, node_id=node_id, have_upload=have_upload_media))
|
||||||
# await upload_in_background(prompt_id, data, node_id=node_id, have_upload=have_upload)
|
# await upload_in_background(prompt_id, data, node_id=node_id, have_upload=have_upload)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await handle_error(prompt_id, data, e)
|
await handle_error(prompt_id, data, e)
|
||||||
|
|
||||||
# requests.post(status_endpoint, json=body)
|
# requests.post(status_endpoint, json=body)
|
||||||
elif status_endpoint is not None:
|
if status_endpoint is not None:
|
||||||
await async_request_with_retry('POST', status_endpoint, json=body)
|
await async_request_with_retry('POST', status_endpoint, json=body)
|
||||||
|
|
||||||
await send('outputs_uploaded', {
|
await send('outputs_uploaded', {
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ aiofiles
|
|||||||
pydantic
|
pydantic
|
||||||
opencv-python
|
opencv-python
|
||||||
imageio-ffmpeg
|
imageio-ffmpeg
|
||||||
brotli
|
|
||||||
# logfire
|
# logfire
|
||||||
+61
-352
@@ -2,7 +2,6 @@ import { app } from "./app.js";
|
|||||||
import { api } from "./api.js";
|
import { api } from "./api.js";
|
||||||
import { ComfyWidgets, LGraphNode } from "./widgets.js";
|
import { ComfyWidgets, LGraphNode } from "./widgets.js";
|
||||||
import { generateDependencyGraph } from "https://esm.sh/[email protected]";
|
import { generateDependencyGraph } from "https://esm.sh/[email protected]";
|
||||||
import { ComfyDeploy } from "https://esm.sh/[email protected]";
|
|
||||||
|
|
||||||
const loadingIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none" stroke="#888888" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg>`;
|
const loadingIcon = `<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24"><g fill="none" stroke="#888888" stroke-linecap="round" stroke-width="2"><path stroke-dasharray="60" stroke-dashoffset="60" stroke-opacity=".3" d="M12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12C3 7.02944 7.02944 3 12 3Z"><animate fill="freeze" attributeName="stroke-dashoffset" dur="1.3s" values="60;0"/></path><path stroke-dasharray="15" stroke-dashoffset="15" d="M12 3C16.9706 3 21 7.02944 21 12"><animate fill="freeze" attributeName="stroke-dashoffset" dur="0.3s" values="15;0"/><animateTransform attributeName="transform" dur="1.5s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></g></svg>`;
|
||||||
|
|
||||||
@@ -192,13 +191,11 @@ const ext = {
|
|||||||
|
|
||||||
registerCustomNodes() {
|
registerCustomNodes() {
|
||||||
/** @type {LGraphNode}*/
|
/** @type {LGraphNode}*/
|
||||||
class ComfyDeploy extends LGraphNode {
|
class ComfyDeploy {
|
||||||
|
color = LGraphCanvas.node_colors.yellow.color;
|
||||||
|
bgcolor = LGraphCanvas.node_colors.yellow.bgcolor;
|
||||||
|
groupcolor = LGraphCanvas.node_colors.yellow.groupcolor;
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
|
||||||
this.color = LGraphCanvas.node_colors.yellow.color;
|
|
||||||
this.bgcolor = LGraphCanvas.node_colors.yellow.bgcolor;
|
|
||||||
this.groupcolor = LGraphCanvas.node_colors.yellow.groupcolor;
|
|
||||||
|
|
||||||
if (!this.properties) {
|
if (!this.properties) {
|
||||||
this.properties = {};
|
this.properties = {};
|
||||||
this.properties.workflow_name = "";
|
this.properties.workflow_name = "";
|
||||||
@@ -206,75 +203,53 @@ const ext = {
|
|||||||
this.properties.version = "";
|
this.properties.version = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addWidget(
|
ComfyWidgets.STRING(
|
||||||
"text",
|
this,
|
||||||
"workflow_name",
|
"workflow_name",
|
||||||
this.properties.workflow_name,
|
["", { default: this.properties.workflow_name, multiline: false }],
|
||||||
(v) => {
|
app,
|
||||||
this.properties.workflow_name = v;
|
|
||||||
},
|
|
||||||
{ multiline: false }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addWidget(
|
ComfyWidgets.STRING(
|
||||||
"text",
|
this,
|
||||||
"workflow_id",
|
"workflow_id",
|
||||||
this.properties.workflow_id,
|
["", { default: this.properties.workflow_id, multiline: false }],
|
||||||
(v) => {
|
app,
|
||||||
this.properties.workflow_id = v;
|
|
||||||
},
|
|
||||||
{ multiline: false }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addWidget(
|
ComfyWidgets.STRING(
|
||||||
"text",
|
this,
|
||||||
"version",
|
"version",
|
||||||
this.properties.version,
|
["", { default: this.properties.version, multiline: false }],
|
||||||
(v) => {
|
app,
|
||||||
this.properties.version = v;
|
|
||||||
},
|
|
||||||
{ multiline: false }
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// this.widgets.forEach((w) => {
|
||||||
|
// // w.computeSize = () => [200,10]
|
||||||
|
// w.computedHeight = 2;
|
||||||
|
// })
|
||||||
|
|
||||||
this.widgets_start_y = 10;
|
this.widgets_start_y = 10;
|
||||||
|
this.setSize(this.computeSize());
|
||||||
|
|
||||||
|
// const config = { };
|
||||||
|
|
||||||
|
// console.log(this);
|
||||||
this.serialize_widgets = true;
|
this.serialize_widgets = true;
|
||||||
this.isVirtualNode = true;
|
this.isVirtualNode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
onExecute() {
|
|
||||||
// This method is called when the node is executed
|
|
||||||
// You can add any necessary logic here
|
|
||||||
}
|
|
||||||
|
|
||||||
onSerialize(o) {
|
|
||||||
// This method is called when the node is being serialized
|
|
||||||
// Ensure all necessary data is saved
|
|
||||||
if (!o.properties) {
|
|
||||||
o.properties = {};
|
|
||||||
}
|
|
||||||
o.properties.workflow_name = this.properties.workflow_name;
|
|
||||||
o.properties.workflow_id = this.properties.workflow_id;
|
|
||||||
o.properties.version = this.properties.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
onConfigure(o) {
|
|
||||||
// This method is called when the node is being configured (e.g., when loading a saved graph)
|
|
||||||
// Ensure all necessary data is restored
|
|
||||||
if (o.properties) {
|
|
||||||
this.properties = { ...this.properties, ...o.properties };
|
|
||||||
this.widgets[0].value = this.properties.workflow_name || "";
|
|
||||||
this.widgets[1].value = this.properties.workflow_id || "";
|
|
||||||
this.widgets[2].value = this.properties.version || "1";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register the node type
|
// Load default visibility
|
||||||
LiteGraph.registerNodeType("ComfyDeploy", Object.assign(ComfyDeploy, {
|
|
||||||
title: "Comfy Deploy",
|
LiteGraph.registerNodeType(
|
||||||
title_mode: LiteGraph.NORMAL_TITLE,
|
"ComfyDeploy",
|
||||||
collapsable: true,
|
Object.assign(ComfyDeploy, {
|
||||||
}));
|
title_mode: LiteGraph.NORMAL_TITLE,
|
||||||
|
title: "Comfy Deploy",
|
||||||
|
collapsable: true,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
ComfyDeploy.category = "deploy";
|
ComfyDeploy.category = "deploy";
|
||||||
},
|
},
|
||||||
@@ -303,11 +278,7 @@ const ext = {
|
|||||||
sendEventToCD("cd_plugin_onDeployChanges", prompt);
|
sendEventToCD("cd_plugin_onDeployChanges", prompt);
|
||||||
} else if (message.type === "queue_prompt") {
|
} else if (message.type === "queue_prompt") {
|
||||||
const prompt = await app.graphToPrompt();
|
const prompt = await app.graphToPrompt();
|
||||||
if (typeof api.handlePromptGenerated === "function") {
|
api.handlePromptGenerated(prompt);
|
||||||
api.handlePromptGenerated(prompt);
|
|
||||||
} else {
|
|
||||||
console.warn("api.handlePromptGenerated is not a function");
|
|
||||||
}
|
|
||||||
sendEventToCD("cd_plugin_onQueuePrompt", prompt);
|
sendEventToCD("cd_plugin_onQueuePrompt", prompt);
|
||||||
} else if (message.type === "get_prompt") {
|
} else if (message.type === "get_prompt") {
|
||||||
const prompt = await app.graphToPrompt();
|
const prompt = await app.graphToPrompt();
|
||||||
@@ -330,56 +301,6 @@ const ext = {
|
|||||||
|
|
||||||
app.graph.add(node);
|
app.graph.add(node);
|
||||||
app.graph.afterChange();
|
app.graph.afterChange();
|
||||||
} else if (message.type === "zoom_to_node") {
|
|
||||||
const nodeId = message.data.nodeId;
|
|
||||||
const position = message.data.position;
|
|
||||||
|
|
||||||
const node = app.graph.getNodeById(nodeId);
|
|
||||||
if (!node) return;
|
|
||||||
|
|
||||||
const canvas = app.canvas;
|
|
||||||
const targetScale = 1;
|
|
||||||
const targetOffsetX =
|
|
||||||
canvas.canvas.width / 4 - position[0] - node.size[0] / 2;
|
|
||||||
const targetOffsetY =
|
|
||||||
canvas.canvas.height / 4 - position[1] - node.size[1] / 2;
|
|
||||||
|
|
||||||
const startScale = canvas.ds.scale;
|
|
||||||
const startOffsetX = canvas.ds.offset[0];
|
|
||||||
const startOffsetY = canvas.ds.offset[1];
|
|
||||||
|
|
||||||
const duration = 400; // Animation duration in milliseconds
|
|
||||||
const startTime = Date.now();
|
|
||||||
|
|
||||||
function easeOutCubic(t) {
|
|
||||||
return 1 - Math.pow(1 - t, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
function lerp(start, end, t) {
|
|
||||||
return start * (1 - t) + end * t;
|
|
||||||
}
|
|
||||||
|
|
||||||
function animate() {
|
|
||||||
const currentTime = Date.now();
|
|
||||||
const elapsedTime = currentTime - startTime;
|
|
||||||
const t = Math.min(elapsedTime / duration, 1);
|
|
||||||
|
|
||||||
const easedT = easeOutCubic(t);
|
|
||||||
|
|
||||||
const currentScale = lerp(startScale, targetScale, easedT);
|
|
||||||
const currentOffsetX = lerp(startOffsetX, targetOffsetX, easedT);
|
|
||||||
const currentOffsetY = lerp(startOffsetY, targetOffsetY, easedT);
|
|
||||||
|
|
||||||
canvas.setZoom(currentScale);
|
|
||||||
canvas.ds.offset = [currentOffsetX, currentOffsetY];
|
|
||||||
canvas.draw(true, true);
|
|
||||||
|
|
||||||
if (t < 1) {
|
|
||||||
requestAnimationFrame(animate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
animate();
|
|
||||||
}
|
}
|
||||||
// else if (message.type === "refresh") {
|
// else if (message.type === "refresh") {
|
||||||
// sendEventToCD("cd_plugin_onRefresh");
|
// sendEventToCD("cd_plugin_onRefresh");
|
||||||
@@ -505,7 +426,6 @@ function createDynamicUIHtml(data) {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify the existing deployWorkflow function
|
|
||||||
async function deployWorkflow() {
|
async function deployWorkflow() {
|
||||||
const deploy = document.getElementById("deploy-button");
|
const deploy = document.getElementById("deploy-button");
|
||||||
|
|
||||||
@@ -652,30 +572,30 @@ async function deployWorkflow() {
|
|||||||
console.log(hash);
|
console.log(hash);
|
||||||
return hash.file_hash;
|
return hash.file_hash;
|
||||||
},
|
},
|
||||||
// handleFileUpload: async (file, hash, prevhash) => {
|
handleFileUpload: async (file, hash, prevhash) => {
|
||||||
// console.log("Uploading ", file);
|
console.log("Uploading ", file);
|
||||||
// loadingDialog.showLoading("Uploading file", file);
|
loadingDialog.showLoading("Uploading file", file);
|
||||||
// try {
|
try {
|
||||||
// const { download_url } = await fetch(`/comfyui-deploy/upload-file`, {
|
const { download_url } = await fetch(`/comfyui-deploy/upload-file`, {
|
||||||
// method: "POST",
|
method: "POST",
|
||||||
// body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
// file_path: file,
|
file_path: file,
|
||||||
// token: apiKey,
|
token: apiKey,
|
||||||
// url: endpoint + "/api/upload-url",
|
url: endpoint + "/api/upload-url",
|
||||||
// }),
|
}),
|
||||||
// })
|
})
|
||||||
// .then((x) => x.json())
|
.then((x) => x.json())
|
||||||
// .catch(() => {
|
.catch(() => {
|
||||||
// loadingDialog.close();
|
loadingDialog.close();
|
||||||
// confirmDialog.confirm("Error", "Unable to upload file " + file);
|
confirmDialog.confirm("Error", "Unable to upload file " + file);
|
||||||
// });
|
});
|
||||||
// loadingDialog.showLoading("Uploaded file", file);
|
loadingDialog.showLoading("Uploaded file", file);
|
||||||
// console.log(download_url);
|
console.log(download_url);
|
||||||
// return download_url;
|
return download_url;
|
||||||
// } catch (error) {
|
} catch (error) {
|
||||||
// return undefined;
|
return undefined;
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
existingDependencies: existing_workflow.dependencies,
|
existingDependencies: existing_workflow.dependencies,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -700,15 +620,6 @@ async function deployWorkflow() {
|
|||||||
"Check dependencies",
|
"Check dependencies",
|
||||||
// JSON.stringify(deps, null, 2),
|
// JSON.stringify(deps, null, 2),
|
||||||
`
|
`
|
||||||
<div>
|
|
||||||
You will need to create a cloud machine with the following configuration on ComfyDeploy
|
|
||||||
<ol style="text-align: left; margin-top: 10px;">
|
|
||||||
<li>Review the dependencies listed in the graph below</li>
|
|
||||||
<li>Create a new cloud machine with the required configuration</li>
|
|
||||||
<li>Install missing models and check missing files</li>
|
|
||||||
<li>Deploy your workflow to the newly created machine</li>
|
|
||||||
</ol>
|
|
||||||
</div>
|
|
||||||
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">${loadingIcon}</div>
|
<div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);">${loadingIcon}</div>
|
||||||
<iframe
|
<iframe
|
||||||
style="z-index: 10; min-width: 600px; max-width: 1024px; min-height: 600px; border: none; background-color: transparent;"
|
style="z-index: 10; min-width: 600px; max-width: 1024px; min-height: 600px; border: none; background-color: transparent;"
|
||||||
@@ -778,14 +689,6 @@ async function deployWorkflow() {
|
|||||||
`<span style="color:green;">Deployed successfully!</span> <a style="color:white;" target="_blank" href=${endpoint}/workflows/${data.workflow_id}>-> View here</a> <br/> <br/> Workflow ID: ${data.workflow_id} <br/> Workflow Name: ${workflow_name} <br/> Workflow Version: ${data.version} <br/>`,
|
`<span style="color:green;">Deployed successfully!</span> <a style="color:white;" target="_blank" href=${endpoint}/workflows/${data.workflow_id}>-> View here</a> <br/> <br/> Workflow ID: ${data.workflow_id} <br/> Workflow Name: ${workflow_name} <br/> Workflow Version: ${data.version} <br/>`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// // Refresh the workflows list in the sidebar
|
|
||||||
// const sidebarEl = document.querySelector(
|
|
||||||
// '.comfy-sidebar-tab[data-id="search"]',
|
|
||||||
// );
|
|
||||||
// if (sidebarEl) {
|
|
||||||
// refreshWorkflowsList(sidebarEl);
|
|
||||||
// }
|
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
title.textContent = "Deploy";
|
title.textContent = "Deploy";
|
||||||
title.style.color = "white";
|
title.style.color = "white";
|
||||||
@@ -803,85 +706,6 @@ async function deployWorkflow() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add this function to refresh the workflows list
|
|
||||||
function refreshWorkflowsList(el) {
|
|
||||||
const workflowsList = el.querySelector("#workflows-list");
|
|
||||||
const workflowsLoading = el.querySelector("#workflows-loading");
|
|
||||||
|
|
||||||
workflowsLoading.style.display = "flex";
|
|
||||||
workflowsList.style.display = "none";
|
|
||||||
workflowsList.innerHTML = "";
|
|
||||||
|
|
||||||
client.workflows
|
|
||||||
.getAll({
|
|
||||||
page: "1",
|
|
||||||
pageSize: "10",
|
|
||||||
})
|
|
||||||
.then((result) => {
|
|
||||||
workflowsLoading.style.display = "none";
|
|
||||||
workflowsList.style.display = "block";
|
|
||||||
|
|
||||||
if (result.length === 0) {
|
|
||||||
workflowsList.innerHTML =
|
|
||||||
"<li style='color: #bdbdbd;'>No workflows found</li>";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
result.forEach((workflow) => {
|
|
||||||
const li = document.createElement("li");
|
|
||||||
li.style.marginBottom = "15px";
|
|
||||||
li.style.padding = "15px";
|
|
||||||
li.style.backgroundColor = "#2a2a2a";
|
|
||||||
li.style.borderRadius = "8px";
|
|
||||||
li.style.boxShadow = "0 2px 4px rgba(0,0,0,0.1)";
|
|
||||||
|
|
||||||
const lastRun = workflow.runs[0];
|
|
||||||
const lastRunStatus = lastRun ? lastRun.status : "No runs";
|
|
||||||
const statusColor =
|
|
||||||
lastRunStatus === "success"
|
|
||||||
? "#4CAF50"
|
|
||||||
: lastRunStatus === "error"
|
|
||||||
? "#F44336"
|
|
||||||
: "#FFC107";
|
|
||||||
|
|
||||||
const timeAgo = getTimeAgo(new Date(workflow.updatedAt));
|
|
||||||
|
|
||||||
li.innerHTML = `
|
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px;">
|
|
||||||
<div style="flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
|
|
||||||
<strong style="font-size: 18px; color: #e0e0e0;">${workflow.name}</strong>
|
|
||||||
</div>
|
|
||||||
<span style="font-size: 12px; color: ${statusColor}; margin-left: 10px;">Last run: ${lastRunStatus}</span>
|
|
||||||
</div>
|
|
||||||
<div style="font-size: 14px; color: #bdbdbd; margin-bottom: 10px;">Last updated ${timeAgo}</div>
|
|
||||||
<div style="display: flex; gap: 10px;">
|
|
||||||
<button class="open-cloud-btn" style="padding: 5px 10px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">Open in Cloud</button>
|
|
||||||
<button class="load-api-btn" style="padding: 5px 10px; background-color: #2196F3; color: white; border: none; border-radius: 4px; cursor: pointer;">Load Workflow</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const openCloudBtn = li.querySelector(".open-cloud-btn");
|
|
||||||
openCloudBtn.onclick = () =>
|
|
||||||
window.open(
|
|
||||||
`${getData().endpoint}/workflows/${workflow.id}?workspace=true`,
|
|
||||||
"_blank",
|
|
||||||
);
|
|
||||||
|
|
||||||
const loadApiBtn = li.querySelector(".load-api-btn");
|
|
||||||
loadApiBtn.onclick = () => loadWorkflowApi(workflow.versions[0].id);
|
|
||||||
|
|
||||||
workflowsList.appendChild(li);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error fetching workflows:", error);
|
|
||||||
workflowsLoading.style.display = "none";
|
|
||||||
workflowsList.style.display = "block";
|
|
||||||
workflowsList.innerHTML =
|
|
||||||
"<li style='color: #F44336;'>Error fetching workflows</li>";
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addButton() {
|
function addButton() {
|
||||||
const menu = document.querySelector(".comfy-menu");
|
const menu = document.querySelector(".comfy-menu");
|
||||||
|
|
||||||
@@ -1368,118 +1192,3 @@ export class ConfigDialog extends ComfyDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const configDialog = new ConfigDialog();
|
export const configDialog = new ConfigDialog();
|
||||||
|
|
||||||
const currentOrigin = window.location.origin;
|
|
||||||
const client = new ComfyDeploy({
|
|
||||||
bearerAuth: getData().apiKey,
|
|
||||||
serverURL: `${currentOrigin}/comfydeploy/api/`,
|
|
||||||
});
|
|
||||||
|
|
||||||
app.extensionManager.registerSidebarTab({
|
|
||||||
id: "search",
|
|
||||||
icon: "pi pi-cloud-upload",
|
|
||||||
title: "Deploy",
|
|
||||||
tooltip: "Deploy and Configure",
|
|
||||||
type: "custom",
|
|
||||||
render: (el) => {
|
|
||||||
el.innerHTML = `
|
|
||||||
<div style="padding: 20px;">
|
|
||||||
<h3>Comfy Deploy</h3>
|
|
||||||
<div id="deploy-container" style="margin-bottom: 20px;"></div>
|
|
||||||
<div id="workflows-container">
|
|
||||||
<h4>Your Workflows</h4>
|
|
||||||
<div id="workflows-loading" style="display: flex; justify-content: center; align-items: center; height: 100px;">
|
|
||||||
${loadingIcon}
|
|
||||||
</div>
|
|
||||||
<ul id="workflows-list" style="list-style-type: none; padding: 0; display: none;"></ul>
|
|
||||||
</div>
|
|
||||||
<div id="config-container"></div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Add deploy button
|
|
||||||
const deployContainer = el.querySelector("#deploy-container");
|
|
||||||
const deployButton = document.createElement("button");
|
|
||||||
deployButton.id = "sidebar-deploy-button";
|
|
||||||
deployButton.style.display = "flex";
|
|
||||||
deployButton.style.alignItems = "center";
|
|
||||||
deployButton.style.justifyContent = "center";
|
|
||||||
deployButton.style.width = "100%";
|
|
||||||
deployButton.style.marginBottom = "10px";
|
|
||||||
deployButton.style.padding = "10px";
|
|
||||||
deployButton.style.fontSize = "16px";
|
|
||||||
deployButton.style.fontWeight = "bold";
|
|
||||||
deployButton.style.backgroundColor = "#4CAF50";
|
|
||||||
deployButton.style.color = "white";
|
|
||||||
deployButton.style.border = "none";
|
|
||||||
deployButton.style.borderRadius = "5px";
|
|
||||||
deployButton.style.cursor = "pointer";
|
|
||||||
deployButton.innerHTML = `<i class="pi pi-cloud-upload" style="margin-right: 8px;"></i><div id='sidebar-button-title'>Deploy</div>`;
|
|
||||||
deployButton.onclick = async () => {
|
|
||||||
await deployWorkflow();
|
|
||||||
// Refresh the workflows list after deployment
|
|
||||||
refreshWorkflowsList(el);
|
|
||||||
};
|
|
||||||
deployContainer.appendChild(deployButton);
|
|
||||||
|
|
||||||
// Add config button
|
|
||||||
const configContainer = el.querySelector("#config-container");
|
|
||||||
const configButton = document.createElement("button");
|
|
||||||
configButton.style.display = "flex";
|
|
||||||
configButton.style.alignItems = "center";
|
|
||||||
configButton.style.justifyContent = "center";
|
|
||||||
configButton.style.width = "100%";
|
|
||||||
configButton.style.padding = "8px";
|
|
||||||
configButton.style.fontSize = "14px";
|
|
||||||
configButton.style.backgroundColor = "#f0f0f0";
|
|
||||||
configButton.style.color = "#333";
|
|
||||||
configButton.style.border = "1px solid #ccc";
|
|
||||||
configButton.style.borderRadius = "5px";
|
|
||||||
configButton.style.cursor = "pointer";
|
|
||||||
configButton.innerHTML = `<i class="pi pi-cog" style="margin-right: 8px;"></i>Configure`;
|
|
||||||
configButton.onclick = () => {
|
|
||||||
configDialog.show();
|
|
||||||
};
|
|
||||||
deployContainer.appendChild(configButton);
|
|
||||||
|
|
||||||
// Fetch and display workflows
|
|
||||||
const workflowsList = el.querySelector("#workflows-list");
|
|
||||||
const workflowsLoading = el.querySelector("#workflows-loading");
|
|
||||||
|
|
||||||
refreshWorkflowsList(el);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
function getTimeAgo(date) {
|
|
||||||
const seconds = Math.floor((new Date() - date) / 1000);
|
|
||||||
let interval = seconds / 31536000;
|
|
||||||
if (interval > 1) return Math.floor(interval) + " years ago";
|
|
||||||
interval = seconds / 2592000;
|
|
||||||
if (interval > 1) return Math.floor(interval) + " months ago";
|
|
||||||
interval = seconds / 86400;
|
|
||||||
if (interval > 1) return Math.floor(interval) + " days ago";
|
|
||||||
interval = seconds / 3600;
|
|
||||||
if (interval > 1) return Math.floor(interval) + " hours ago";
|
|
||||||
interval = seconds / 60;
|
|
||||||
if (interval > 1) return Math.floor(interval) + " minutes ago";
|
|
||||||
return Math.floor(seconds) + " seconds ago";
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadWorkflowApi(versionId) {
|
|
||||||
try {
|
|
||||||
const response = await client.comfyui.getWorkflowVersionVersionId({
|
|
||||||
versionId: versionId,
|
|
||||||
});
|
|
||||||
// Implement the logic to load the workflow API into the ComfyUI interface
|
|
||||||
console.log("Workflow API loaded:", response);
|
|
||||||
await window["app"].ui.settings.setSettingValueAsync(
|
|
||||||
"Comfy.Validation.Workflows",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
app.loadGraphData(response.workflow);
|
|
||||||
// You might want to update the UI or trigger some action in ComfyUI here
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Error loading workflow API:", error);
|
|
||||||
// Show an error message to the user
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -51,9 +51,7 @@ const createRunRoute = createRoute({
|
|||||||
export const registerCreateRunRoute = (app: App) => {
|
export const registerCreateRunRoute = (app: App) => {
|
||||||
app.openapi(createRunRoute, async (c) => {
|
app.openapi(createRunRoute, async (c) => {
|
||||||
const data = c.req.valid("json");
|
const data = c.req.valid("json");
|
||||||
const proto = c.req.headers.get('x-forwarded-proto') || "http";
|
const origin = new URL(c.req.url).origin;
|
||||||
const host = c.req.headers.get('x-forwarded-host') || c.req.headers.get('host');
|
|
||||||
const origin = `${proto}://${host}` || new URL(c.req.url).origin;
|
|
||||||
const apiKeyTokenData = c.get("apiKeyTokenData")!;
|
const apiKeyTokenData = c.get("apiKeyTokenData")!;
|
||||||
|
|
||||||
const { deployment_id, inputs } = data;
|
const { deployment_id, inputs } = data;
|
||||||
|
|||||||
Reference in New Issue
Block a user