Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98c1aca606 | ||
|
|
09eab3202f | ||
|
|
f66adfa04f | ||
|
|
0ff751b60f | ||
|
|
a4e3bc2571 | ||
|
|
bf8e2a17b9 | ||
|
|
60c93b65f4 | ||
|
|
5d2ade4fd4 | ||
|
|
1c0a4d5950 | ||
|
|
1a8e089c7d | ||
|
|
557799cdb1 | ||
|
|
d5f52131db | ||
|
|
755399a255 | ||
|
|
ee3717b4b3 | ||
|
|
c13828af56 |
@@ -4,6 +4,8 @@ Open source comfyui deployment platform, a `vercel` for generative workflow infr
|
|||||||
|
|
||||||
Check out our latest lcoal demo -> https://github.com/comfy-deploy/comfyui-api-comfydeploy
|
Check out our latest lcoal demo -> https://github.com/comfy-deploy/comfyui-api-comfydeploy
|
||||||
|
|
||||||
|
Full backend and frontend is here -> https://github.com/comfy-deploy/comfydeploy
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
> Im looking for creative hacker to join ComfyDeploy's core team! DM me on [twitter](https://x.com/BennyKokMusic)
|
> Im looking for creative hacker to join ComfyDeploy's core team! DM me on [twitter](https://x.com/BennyKokMusic)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,137 @@
|
|||||||
|
import folder_paths
|
||||||
|
|
||||||
|
|
||||||
|
class AnyType(str):
|
||||||
|
def __ne__(self, __value: object) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
WILDCARD = AnyType("*")
|
||||||
|
|
||||||
|
|
||||||
|
class ComfyUIDeployExternalFile:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"input_id": (
|
||||||
|
"STRING",
|
||||||
|
{"multiline": False, "default": "input_file"},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"display_name": (
|
||||||
|
"STRING",
|
||||||
|
{"multiline": False, "default": ""},
|
||||||
|
),
|
||||||
|
"description": (
|
||||||
|
"STRING",
|
||||||
|
{"multiline": True, "default": ""},
|
||||||
|
),
|
||||||
|
"file_url": (
|
||||||
|
"STRING",
|
||||||
|
{"multiline": False, "default": ""},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = (WILDCARD,)
|
||||||
|
RETURN_NAMES = ("path",)
|
||||||
|
FUNCTION = "run"
|
||||||
|
CATEGORY = "🔗ComfyDeploy"
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self,
|
||||||
|
input_id,
|
||||||
|
display_name=None,
|
||||||
|
description=None,
|
||||||
|
file_url=None,
|
||||||
|
):
|
||||||
|
import requests
|
||||||
|
import os
|
||||||
|
import uuid
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
if file_url:
|
||||||
|
if file_url.startswith("http"):
|
||||||
|
# Use cache directory for saving files
|
||||||
|
cache_dir = folder_paths.get_temp_directory()
|
||||||
|
if not os.path.exists(cache_dir):
|
||||||
|
os.makedirs(cache_dir)
|
||||||
|
|
||||||
|
# Always generate random filename to avoid conflicts
|
||||||
|
parsed_url = urlparse(file_url)
|
||||||
|
original_filename = os.path.basename(parsed_url.path)
|
||||||
|
|
||||||
|
# Extract file extension from original filename if available
|
||||||
|
file_extension = ""
|
||||||
|
if original_filename and "." in original_filename:
|
||||||
|
file_extension = os.path.splitext(original_filename)[1]
|
||||||
|
else:
|
||||||
|
# Try to determine extension from content-type if no extension found
|
||||||
|
file_extension = ".bin"
|
||||||
|
|
||||||
|
# Generate random filename with preserved extension
|
||||||
|
filename = str(uuid.uuid4()) + file_extension
|
||||||
|
|
||||||
|
destination_path = os.path.join(cache_dir, filename)
|
||||||
|
print(f"Cache directory: {cache_dir}")
|
||||||
|
print(f"Destination path: {destination_path}")
|
||||||
|
print(
|
||||||
|
"Downloading external file - "
|
||||||
|
+ file_url
|
||||||
|
+ " to "
|
||||||
|
+ destination_path
|
||||||
|
)
|
||||||
|
|
||||||
|
headers = {"User-Agent": "Mozilla/5.0"}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
file_url,
|
||||||
|
headers=headers,
|
||||||
|
allow_redirects=True,
|
||||||
|
timeout=30, # Add timeout to prevent hanging
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
|
||||||
|
with open(destination_path, "wb") as out_file:
|
||||||
|
out_file.write(response.content)
|
||||||
|
print(f"External file downloaded: {file_url} to {destination_path}")
|
||||||
|
return (destination_path,)
|
||||||
|
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
error_msg = f"HTTP Error {e.response.status_code}: {e.response.reason} for URL: {file_url}"
|
||||||
|
print(f"⚠️ Download failed - {error_msg}")
|
||||||
|
if e.response.status_code == 404:
|
||||||
|
print(
|
||||||
|
"💡 This URL might have expired or the file may have been deleted"
|
||||||
|
)
|
||||||
|
# Return empty string instead of crashing
|
||||||
|
return ("",)
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
error_msg = (
|
||||||
|
f"Network error downloading file from {file_url}: {str(e)}"
|
||||||
|
)
|
||||||
|
print(f"⚠️ Download failed - {error_msg}")
|
||||||
|
return ("",)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = (
|
||||||
|
f"Unexpected error downloading file from {file_url}: {str(e)}"
|
||||||
|
)
|
||||||
|
print(f"⚠️ Download failed - {error_msg}")
|
||||||
|
return ("",)
|
||||||
|
else:
|
||||||
|
print(f"External file loading: {file_url}")
|
||||||
|
return (file_url,)
|
||||||
|
else:
|
||||||
|
print(f"No file URL provided")
|
||||||
|
return ("",)
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalFile": ComfyUIDeployExternalFile}
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"ComfyUIDeployExternalFile": "External File (ComfyUI Deploy)"
|
||||||
|
}
|
||||||
@@ -0,0 +1,168 @@
|
|||||||
|
import folder_paths
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import uuid
|
||||||
|
|
||||||
|
|
||||||
|
class AnyType(str):
|
||||||
|
def __ne__(self, __value: object) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
WILDCARD = AnyType("*")
|
||||||
|
|
||||||
|
|
||||||
|
class ComfyDeployOutputFile:
|
||||||
|
def __init__(self):
|
||||||
|
self.output_dir = folder_paths.get_output_directory()
|
||||||
|
self.type = "output"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"file_path": (
|
||||||
|
"STRING",
|
||||||
|
{
|
||||||
|
"forceInput": True,
|
||||||
|
"tooltip": "Path to the file to output and upload.",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
"optional": {
|
||||||
|
"output_id": (
|
||||||
|
"STRING",
|
||||||
|
{"multiline": False, "default": "output_file"},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ()
|
||||||
|
FUNCTION = "run"
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
CATEGORY = "🔗ComfyDeploy"
|
||||||
|
DESCRIPTION = "Outputs any file by path for upload to ComfyDeploy."
|
||||||
|
|
||||||
|
def run(self, file_path, output_id="output_file"):
|
||||||
|
if not file_path or not os.path.exists(file_path):
|
||||||
|
print(f"⚠️ File not found: {file_path}")
|
||||||
|
return {"ui": {"files": []}}
|
||||||
|
|
||||||
|
# Security checks - ensure file is within safe ComfyUI paths
|
||||||
|
try:
|
||||||
|
# Get absolute paths for comparison
|
||||||
|
file_abs_path = os.path.abspath(file_path)
|
||||||
|
base_path = folder_paths.base_path
|
||||||
|
temp_dir = folder_paths.get_temp_directory()
|
||||||
|
|
||||||
|
# Check if file is within ComfyUI base path or temp directory
|
||||||
|
if not (
|
||||||
|
file_abs_path.startswith(os.path.abspath(base_path))
|
||||||
|
or file_abs_path.startswith(os.path.abspath(temp_dir))
|
||||||
|
):
|
||||||
|
print(f"⚠️ Security: File outside allowed ComfyUI paths: {file_path}")
|
||||||
|
return {"ui": {"files": []}}
|
||||||
|
|
||||||
|
# Check for path traversal attempts (but allow absolute paths within ComfyUI)
|
||||||
|
if ".." in file_path:
|
||||||
|
print(f"⚠️ Security: Path traversal attempt detected: {file_path}")
|
||||||
|
return {"ui": {"files": []}}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Security check failed: {str(e)}")
|
||||||
|
return {"ui": {"files": []}}
|
||||||
|
|
||||||
|
# Get the original filename and extension
|
||||||
|
original_filename = os.path.basename(file_path)
|
||||||
|
file_extension = os.path.splitext(original_filename)[1]
|
||||||
|
|
||||||
|
# Additional filename security check
|
||||||
|
if ".." in original_filename:
|
||||||
|
print(f"⚠️ Security: Insecure filename: {original_filename}")
|
||||||
|
return {"ui": {"files": []}}
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
# Check if file is in output folder, if not, symlink it there
|
||||||
|
try:
|
||||||
|
if file_path.startswith(self.output_dir):
|
||||||
|
# File is already in output directory - use as is
|
||||||
|
relative_path = os.path.relpath(file_path, self.output_dir)
|
||||||
|
path_parts = relative_path.split(os.sep)
|
||||||
|
|
||||||
|
if len(path_parts) > 1:
|
||||||
|
subfolder = os.sep.join(path_parts[:-1])
|
||||||
|
else:
|
||||||
|
subfolder = ""
|
||||||
|
|
||||||
|
filename = path_parts[-1]
|
||||||
|
file_type = self.type
|
||||||
|
else:
|
||||||
|
# File is not in output folder - symlink it to output/temp
|
||||||
|
print(
|
||||||
|
f"File is not in output folder, symlinking to output/temp: {file_path}"
|
||||||
|
)
|
||||||
|
output_temp_dir = os.path.join(self.output_dir, "temp")
|
||||||
|
if not os.path.exists(output_temp_dir):
|
||||||
|
os.makedirs(output_temp_dir)
|
||||||
|
|
||||||
|
# Use the existing filename but with UUID prefix to avoid conflicts
|
||||||
|
file_ext = os.path.splitext(original_filename)[1]
|
||||||
|
temp_filename = f"{uuid.uuid4()}{file_ext}"
|
||||||
|
temp_path = os.path.join(output_temp_dir, temp_filename)
|
||||||
|
|
||||||
|
# Create symlink to file in output/temp directory where upload system expects it
|
||||||
|
try:
|
||||||
|
# Remove existing symlink if it exists
|
||||||
|
if os.path.exists(temp_path):
|
||||||
|
os.remove(temp_path)
|
||||||
|
os.symlink(file_path, temp_path)
|
||||||
|
print(f"File symlinked to output/temp: {temp_path} -> {file_path}")
|
||||||
|
except OSError as e:
|
||||||
|
# Fall back to copying if symlink fails
|
||||||
|
print(f"Symlink failed ({e}), falling back to copy")
|
||||||
|
shutil.copy2(file_path, temp_path)
|
||||||
|
print(f"File copied to output/temp: {temp_path}")
|
||||||
|
|
||||||
|
# Use output/temp directory structure for upload
|
||||||
|
subfolder = "temp"
|
||||||
|
filename = temp_filename
|
||||||
|
file_type = self.type
|
||||||
|
|
||||||
|
results.append(
|
||||||
|
{
|
||||||
|
"filename": filename,
|
||||||
|
"subfolder": subfolder,
|
||||||
|
"type": file_type,
|
||||||
|
"output_id": output_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"⚠️ Error processing file path: {str(e)}")
|
||||||
|
return {"ui": {"files": []}}
|
||||||
|
|
||||||
|
# Determine the appropriate UI key based on file type
|
||||||
|
file_ext = file_extension.lower()
|
||||||
|
if file_ext in [".png", ".jpg", ".jpeg", ".webp", ".gif", ".bmp", ".tiff"]:
|
||||||
|
ui_key = "images"
|
||||||
|
elif file_ext in [".mp3", ".wav", ".flac", ".aac", ".ogg"]:
|
||||||
|
ui_key = "audio"
|
||||||
|
elif file_ext in [".txt", ".json", ".md", ".csv"]:
|
||||||
|
ui_key = "text_file"
|
||||||
|
elif file_ext in [".exr", ".hdr"]:
|
||||||
|
ui_key = "images" # EXR files are still images
|
||||||
|
elif file_ext in [".zip", ".psb", ".psd"]:
|
||||||
|
ui_key = "files" # Archives and Photoshop project files
|
||||||
|
else:
|
||||||
|
ui_key = "files" # Generic files
|
||||||
|
|
||||||
|
return {"ui": {ui_key: results}}
|
||||||
|
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"ComfyDeployOutputFile": ComfyDeployOutputFile,
|
||||||
|
}
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"ComfyDeployOutputFile": "File Output (ComfyDeploy)",
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import numpy as np
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
from PIL.PngImagePlugin import PngInfo
|
from PIL.PngImagePlugin import PngInfo
|
||||||
import folder_paths
|
import folder_paths
|
||||||
|
from comfy.cli_args import args
|
||||||
|
|
||||||
|
|
||||||
class ComfyDeployOutputImage:
|
class ComfyDeployOutputImage:
|
||||||
@@ -64,12 +65,14 @@ class ComfyDeployOutputImage:
|
|||||||
for batch_number, image in enumerate(images):
|
for batch_number, image in enumerate(images):
|
||||||
i = 255.0 * image.cpu().numpy()
|
i = 255.0 * image.cpu().numpy()
|
||||||
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
img = Image.fromarray(np.clip(i, 0, 255).astype(np.uint8))
|
||||||
metadata = PngInfo()
|
metadata = None
|
||||||
if prompt is not None:
|
if not args.disable_metadata:
|
||||||
metadata.add_text("prompt", json.dumps(prompt))
|
metadata = PngInfo()
|
||||||
if extra_pnginfo is not None:
|
if prompt is not None:
|
||||||
for x in extra_pnginfo:
|
metadata.add_text("prompt", json.dumps(prompt))
|
||||||
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
if extra_pnginfo is not None:
|
||||||
|
for x in extra_pnginfo:
|
||||||
|
metadata.add_text(x, json.dumps(extra_pnginfo[x]))
|
||||||
|
|
||||||
filename_with_batch_num = filename.replace("%batch_num%", str(batch_number))
|
filename_with_batch_num = filename.replace("%batch_num%", str(batch_number))
|
||||||
file = f"{filename_with_batch_num}_{counter:05}_.{file_type}"
|
file = f"{filename_with_batch_num}_{counter:05}_.{file_type}"
|
||||||
|
|||||||
+70
-33
@@ -1,8 +1,5 @@
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from pprint import pprint
|
|
||||||
from aiohttp import web
|
|
||||||
import os
|
import os
|
||||||
import requests
|
|
||||||
import folder_paths
|
import folder_paths
|
||||||
import json
|
import json
|
||||||
import server
|
import server
|
||||||
@@ -13,34 +10,24 @@ import random
|
|||||||
import traceback
|
import traceback
|
||||||
import uuid
|
import uuid
|
||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import inspect
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote, urlencode
|
||||||
import threading
|
import threading
|
||||||
import hashlib
|
import hashlib
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from aiohttp import ClientSession, web
|
from aiohttp import web, ClientSession, ClientError, ClientTimeout
|
||||||
import aiofiles
|
import aiofiles
|
||||||
from typing import Dict, List, Union, Any, Optional
|
from typing import Dict, Any
|
||||||
from PIL import Image
|
|
||||||
import copy
|
import copy
|
||||||
import struct
|
import struct
|
||||||
from aiohttp import web, ClientSession, ClientError, ClientTimeout, ClientResponseError
|
|
||||||
import atexit
|
import atexit
|
||||||
from model_management import get_torch_device
|
from model_management import get_torch_device
|
||||||
import torch
|
import torch
|
||||||
import psutil
|
import psutil
|
||||||
from collections import OrderedDict
|
|
||||||
import io
|
|
||||||
from urllib.parse import urlencode
|
|
||||||
|
|
||||||
# Global session
|
# Global session
|
||||||
client_session = None
|
client_session = None
|
||||||
|
|
||||||
# def create_client_session():
|
|
||||||
# global client_session
|
|
||||||
# if client_session is None:
|
|
||||||
# client_session = aiohttp.ClientSession()
|
|
||||||
|
|
||||||
|
|
||||||
async def ensure_client_session():
|
async def ensure_client_session():
|
||||||
global client_session
|
global client_session
|
||||||
@@ -82,8 +69,6 @@ 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(
|
async def async_request_with_retry(
|
||||||
method, url, disable_timeout=False, token=None, **kwargs
|
method, url, disable_timeout=False, token=None, **kwargs
|
||||||
@@ -334,9 +319,28 @@ async def post_prompt(json_data):
|
|||||||
extra_data["client_id"] = json_data["client_id"]
|
extra_data["client_id"] = json_data["client_id"]
|
||||||
if valid[0]:
|
if valid[0]:
|
||||||
outputs_to_execute = valid[2]
|
outputs_to_execute = valid[2]
|
||||||
prompt_server.prompt_queue.put(
|
# Backward compatibility: sensitive data handling added in newer ComfyUI
|
||||||
(number, prompt_id, prompt, extra_data, outputs_to_execute)
|
sensitive_keys = getattr(execution, "SENSITIVE_EXTRA_DATA_KEYS", None)
|
||||||
)
|
if sensitive_keys:
|
||||||
|
sensitive = {}
|
||||||
|
for sensitive_val in sensitive_keys:
|
||||||
|
if sensitive_val in extra_data:
|
||||||
|
sensitive[sensitive_val] = extra_data.pop(sensitive_val)
|
||||||
|
prompt_server.prompt_queue.put(
|
||||||
|
(
|
||||||
|
number,
|
||||||
|
prompt_id,
|
||||||
|
prompt,
|
||||||
|
extra_data,
|
||||||
|
outputs_to_execute,
|
||||||
|
sensitive,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# Old ComfyUI version without sensitive data support
|
||||||
|
prompt_server.prompt_queue.put(
|
||||||
|
(number, prompt_id, prompt, extra_data, outputs_to_execute)
|
||||||
|
)
|
||||||
response = {
|
response = {
|
||||||
"prompt_id": prompt_id,
|
"prompt_id": prompt_id,
|
||||||
"number": number,
|
"number": number,
|
||||||
@@ -516,6 +520,9 @@ def apply_inputs_to_workflow(workflow_api: Any, inputs: Any, sid: str = None):
|
|||||||
if value["class_type"] == "ComfyUIDeployExternalEXR":
|
if value["class_type"] == "ComfyUIDeployExternalEXR":
|
||||||
value["inputs"]["exr_file"] = new_value
|
value["inputs"]["exr_file"] = new_value
|
||||||
|
|
||||||
|
if value["class_type"] == "ComfyUIDeployExternalFile":
|
||||||
|
value["inputs"]["file_url"] = new_value
|
||||||
|
|
||||||
if value["class_type"] == "ComfyUIDeployExternalSeed":
|
if value["class_type"] == "ComfyUIDeployExternalSeed":
|
||||||
logger.info(
|
logger.info(
|
||||||
f"Applied random seed {new_value} to {value['class_type']}"
|
f"Applied random seed {new_value} to {value['class_type']}"
|
||||||
@@ -896,6 +903,10 @@ async def upload_file_endpoint(request):
|
|||||||
file_type = "image/png"
|
file_type = "image/png"
|
||||||
elif file_extension == ".webp":
|
elif file_extension == ".webp":
|
||||||
file_type = "image/webp"
|
file_type = "image/webp"
|
||||||
|
elif file_extension == ".zip":
|
||||||
|
file_type = "application/zip"
|
||||||
|
elif file_extension in [".psd", ".psb"]:
|
||||||
|
file_type = "image/vnd.adobe.photoshop"
|
||||||
else:
|
else:
|
||||||
file_type = (
|
file_type = (
|
||||||
"application/octet-stream" # Default to binary file type if unknown
|
"application/octet-stream" # Default to binary file type if unknown
|
||||||
@@ -1316,6 +1327,9 @@ try:
|
|||||||
is_async = asyncio.iscoroutinefunction(origin_execute)
|
is_async = asyncio.iscoroutinefunction(origin_execute)
|
||||||
|
|
||||||
if is_async:
|
if is_async:
|
||||||
|
# Check signature for backward compatibility (v0.3.67 has 10 params, v0.3.68+ has 11)
|
||||||
|
sig = inspect.signature(origin_execute)
|
||||||
|
has_ui_outputs = len(sig.parameters) >= 11
|
||||||
|
|
||||||
async def swizzle_execute(
|
async def swizzle_execute(
|
||||||
server,
|
server,
|
||||||
@@ -1328,12 +1342,14 @@ try:
|
|||||||
execution_list,
|
execution_list,
|
||||||
pending_subgraph_results,
|
pending_subgraph_results,
|
||||||
pending_async_nodes,
|
pending_async_nodes,
|
||||||
|
ui_outputs=None,
|
||||||
):
|
):
|
||||||
unique_id = current_item
|
unique_id = current_item
|
||||||
class_type = dynprompt.get_node(unique_id)["class_type"]
|
class_type = dynprompt.get_node(unique_id)["class_type"]
|
||||||
last_node_id = server.last_node_id
|
last_node_id = server.last_node_id
|
||||||
|
|
||||||
result = await origin_execute(
|
# Build args list - add ui_outputs only for v0.3.68+
|
||||||
|
args = [
|
||||||
server,
|
server,
|
||||||
dynprompt,
|
dynprompt,
|
||||||
caches,
|
caches,
|
||||||
@@ -1344,12 +1360,16 @@ try:
|
|||||||
execution_list,
|
execution_list,
|
||||||
pending_subgraph_results,
|
pending_subgraph_results,
|
||||||
pending_async_nodes,
|
pending_async_nodes,
|
||||||
)
|
]
|
||||||
|
if has_ui_outputs:
|
||||||
|
args.append(ui_outputs)
|
||||||
|
|
||||||
|
result = await origin_execute(*args)
|
||||||
|
|
||||||
handle_execute(class_type, last_node_id, prompt_id, server, unique_id)
|
handle_execute(class_type, last_node_id, prompt_id, server, unique_id)
|
||||||
return result
|
return result
|
||||||
else:
|
else:
|
||||||
|
# Sync version for very old ComfyUI versions
|
||||||
def swizzle_execute(
|
def swizzle_execute(
|
||||||
server,
|
server,
|
||||||
dynprompt,
|
dynprompt,
|
||||||
@@ -1547,18 +1567,26 @@ async def send_json_override(self, event, data, sid=None):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if event == "executing" and data.get("node") is not None:
|
if event == "executing" and data.get("node") is not None:
|
||||||
node = data.get("node")
|
raw_node = data.get("node")
|
||||||
|
node = str(raw_node)
|
||||||
|
|
||||||
if prompt_id in prompt_metadata:
|
if prompt_id in prompt_metadata:
|
||||||
# if 'progress' not in prompt_metadata[prompt_id]:
|
wf_api = prompt_metadata[prompt_id].workflow_api
|
||||||
# prompt_metadata[prompt_id]["progress"] = set()
|
|
||||||
|
# Normalize dotted display ids like "23.0.0.1" to base "23"
|
||||||
|
if node not in wf_api and "." in node:
|
||||||
|
base = node.split(".")[0]
|
||||||
|
if base in wf_api:
|
||||||
|
node = base
|
||||||
|
|
||||||
|
# If still unknown, skip safely
|
||||||
|
if node not in wf_api:
|
||||||
|
logger.info(f"Skipping unknown node id in 'executing': {raw_node}")
|
||||||
|
return
|
||||||
|
|
||||||
prompt_metadata[prompt_id].progress.add(node)
|
prompt_metadata[prompt_id].progress.add(node)
|
||||||
calculated_progress = len(prompt_metadata[prompt_id].progress) / len(
|
calculated_progress = len(prompt_metadata[prompt_id].progress) / len(wf_api)
|
||||||
prompt_metadata[prompt_id].workflow_api
|
|
||||||
)
|
|
||||||
calculated_progress = round(calculated_progress, 2)
|
calculated_progress = round(calculated_progress, 2)
|
||||||
# logger.info("calculated_progress", calculated_progress)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
prompt_metadata[prompt_id].last_updated_node is not None
|
prompt_metadata[prompt_id].last_updated_node is not None
|
||||||
@@ -1566,7 +1594,8 @@ async def send_json_override(self, event, data, sid=None):
|
|||||||
):
|
):
|
||||||
return
|
return
|
||||||
prompt_metadata[prompt_id].last_updated_node = node
|
prompt_metadata[prompt_id].last_updated_node = node
|
||||||
class_type = prompt_metadata[prompt_id].workflow_api[node]["class_type"]
|
|
||||||
|
class_type = wf_api[node]["class_type"]
|
||||||
logger.info(f"At: {round(calculated_progress * 100)}% - {class_type}")
|
logger.info(f"At: {round(calculated_progress * 100)}% - {class_type}")
|
||||||
asyncio.create_task(
|
asyncio.create_task(
|
||||||
send(
|
send(
|
||||||
@@ -2098,6 +2127,10 @@ async def handle_upload(
|
|||||||
file_type = "image/png"
|
file_type = "image/png"
|
||||||
elif file_extension == ".webp":
|
elif file_extension == ".webp":
|
||||||
file_type = "image/webp"
|
file_type = "image/webp"
|
||||||
|
elif file_extension == ".zip":
|
||||||
|
file_type = "application/zip"
|
||||||
|
elif file_extension in [".psd", ".psb"]:
|
||||||
|
file_type = "image/vnd.adobe.photoshop"
|
||||||
|
|
||||||
upload_tasks.append(
|
upload_tasks.append(
|
||||||
upload_file(
|
upload_file(
|
||||||
@@ -2563,6 +2596,10 @@ class UploadQueue:
|
|||||||
content_type = "image/webp"
|
content_type = "image/webp"
|
||||||
elif file_extension == ".gif":
|
elif file_extension == ".gif":
|
||||||
content_type = "image/gif"
|
content_type = "image/gif"
|
||||||
|
elif file_extension == ".zip":
|
||||||
|
content_type = "application/zip"
|
||||||
|
elif file_extension in [".psd", ".psb"]:
|
||||||
|
content_type = "image/vnd.adobe.photoshop"
|
||||||
else:
|
else:
|
||||||
content_type = file_info.get("content_type", "application/octet-stream")
|
content_type = file_info.get("content_type", "application/octet-stream")
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "comfyui-deploy"
|
name = "comfyui-deploy"
|
||||||
description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
|
description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
|
||||||
version = "2.3.5"
|
version = "2.3.10"
|
||||||
license = { file = "LICENSE" }
|
license = { file = "LICENSE" }
|
||||||
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"]
|
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg", "tabulate", "brotli"]
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
} from "./workflow-list.js";
|
} from "./workflow-list.js";
|
||||||
import { initializeMachineManager } from "./machine-manager.js";
|
import { initializeMachineManager } from "./machine-manager.js";
|
||||||
import { initializeModelManager } from "./model-manager.js";
|
import { initializeModelManager } from "./model-manager.js";
|
||||||
import { fetchSnapshotSimple } from "./snapshot-utils.js";
|
|
||||||
|
|
||||||
const styles = `
|
const styles = `
|
||||||
.comfydeploy-menu-item {
|
.comfydeploy-menu-item {
|
||||||
@@ -1355,19 +1354,6 @@ async function deployWorkflow() {
|
|||||||
if (endpoint.endsWith("/")) {
|
if (endpoint.endsWith("/")) {
|
||||||
endpoint = endpoint.slice(0, -1);
|
endpoint = endpoint.slice(0, -1);
|
||||||
}
|
}
|
||||||
loadingDialog.showLoading("Generating snapshot");
|
|
||||||
|
|
||||||
const snapshot = await fetchSnapshotSimple();
|
|
||||||
console.log(snapshot);
|
|
||||||
loadingDialog.close();
|
|
||||||
|
|
||||||
if (!snapshot) {
|
|
||||||
showError(
|
|
||||||
"Error when deploying",
|
|
||||||
"Unable to generate snapshot, please install ComfyUI Manager"
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const title = deploy.querySelector("#button-title");
|
const title = deploy.querySelector("#button-title");
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Machine Manager Script
|
// Machine Manager Script
|
||||||
// Handles local storage machine ID functionality
|
// Handles local storage machine ID functionality
|
||||||
|
|
||||||
import { fetchSnapshot, fetchSnapshotSimple } from "./snapshot-utils.js";
|
import { fetchSnapshot } from "./snapshot-utils.js";
|
||||||
|
|
||||||
const MACHINE_STORAGE_KEY = "comfy_deploy_machine_id";
|
const MACHINE_STORAGE_KEY = "comfy_deploy_machine_id";
|
||||||
|
|
||||||
|
|||||||
@@ -66,17 +66,3 @@ export async function fetchSnapshot(getDataFn = null) {
|
|||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Simple snapshot fetch without ComfyUI version fallback
|
|
||||||
* Use this when you don't need the ComfyUI version fallback logic
|
|
||||||
*
|
|
||||||
* @returns {Promise<Object>} - The snapshot data as-is
|
|
||||||
*/
|
|
||||||
export async function fetchSnapshotSimple() {
|
|
||||||
const response = await fetch("/snapshot/get_current");
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Snapshot fetch failed: ${response.status}`);
|
|
||||||
}
|
|
||||||
return response.json();
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user