Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5423b4ee6f | ||
|
|
2c1656756d | ||
|
|
ac843527d9 | ||
|
|
f39d216326 | ||
|
|
40ec37e58f | ||
|
|
1d63b21643 | ||
|
|
0e3baf22df | ||
|
|
1837065ed2 | ||
|
|
9a8f4795d1 | ||
|
|
c0c617c5d2 | ||
|
|
1e33435ae5 | ||
|
|
04161071f2 | ||
|
|
32d574475c | ||
|
|
1a017ee6a3 | ||
|
|
603223741a | ||
|
|
2bd8b23c60 | ||
|
|
a82e315d6c | ||
|
|
7fdfba6b6e | ||
|
|
0779136134 | ||
|
|
fe116a4655 | ||
|
|
7dd8a7e67e | ||
|
|
778e6fefe6 | ||
|
|
fd310e8478 | ||
|
|
3a3b93d564 | ||
|
|
82c564228d | ||
|
|
ad0a23434b | ||
|
|
292f77f06b | ||
|
|
a139424b91 | ||
|
|
44a91d2093 | ||
|
|
7cff930861 | ||
|
|
ce464c6ce4 | ||
|
|
1c7998c554 | ||
|
|
66d1e42409 | ||
|
|
8882f4983c | ||
|
|
492b81c340 | ||
|
|
8b05ed26c9 | ||
|
|
ce67604926 | ||
|
|
c115c22a91 | ||
|
|
2f33bcf497 | ||
|
|
bcf466c472 | ||
|
|
f812d9d698 | ||
|
|
101b6cca57 | ||
|
|
ae68aae011 | ||
|
|
07926158f0 | ||
|
|
ce92dd0570 | ||
|
|
79650f48d0 | ||
|
|
0e70db4013 | ||
|
|
06805e310d | ||
|
|
4cd7d7a8f9 |
@@ -64,32 +64,42 @@ class ComfyUIDeployExternalLora:
|
||||
import os
|
||||
import uuid
|
||||
|
||||
if lora_url and lora_url.startswith("http"):
|
||||
if lora_save_name:
|
||||
existing_loras = folder_paths.get_filename_list("loras")
|
||||
# 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,)
|
||||
if lora_url:
|
||||
if lora_url.startswith("http"):
|
||||
if lora_save_name:
|
||||
existing_loras = folder_paths.get_filename_list("loras")
|
||||
# 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])
|
||||
destination_path = os.path.join(
|
||||
folder_paths.folder_names_and_paths["loras"][0][0], lora_save_name
|
||||
)
|
||||
print(destination_path)
|
||||
print(
|
||||
"Downloading external lora - "
|
||||
+ lora_url
|
||||
+ " to "
|
||||
+ destination_path
|
||||
)
|
||||
response = requests.get(
|
||||
lora_url,
|
||||
headers={"User-Agent": "Mozilla/5.0"},
|
||||
allow_redirects=True,
|
||||
)
|
||||
with open(destination_path, "wb") as out_file:
|
||||
out_file.write(response.content)
|
||||
print(f"Ext Lora loading: {lora_url} to {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])
|
||||
destination_path = os.path.join(
|
||||
folder_paths.folder_names_and_paths["loras"][0][0], lora_save_name
|
||||
)
|
||||
print(destination_path)
|
||||
print("Downloading external lora - " + lora_url + " to " + destination_path)
|
||||
response = requests.get(
|
||||
lora_url,
|
||||
headers={"User-Agent": "Mozilla/5.0"},
|
||||
allow_redirects=True,
|
||||
)
|
||||
with open(destination_path, "wb") as out_file:
|
||||
out_file.write(response.content)
|
||||
return (lora_save_name,)
|
||||
print(f"Ext Lora loading: {lora_url}")
|
||||
return (lora_url,)
|
||||
else:
|
||||
print(f"using lora: {default_lora_name}")
|
||||
print(f"Ext Lora loading: {default_lora_name}")
|
||||
return (default_lora_name,)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import re
|
||||
|
||||
|
||||
class StringFunction:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"action": (["append", "replace"], {}),
|
||||
"tidy_tags": (["yes", "no"], {}),
|
||||
},
|
||||
"optional": {
|
||||
"text_a": ("STRING", {"multiline": True, "dynamicPrompts": False}),
|
||||
"text_b": ("STRING", {"multiline": True, "dynamicPrompts": False}),
|
||||
"text_c": ("STRING", {"multiline": True, "dynamicPrompts": False}),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "exec"
|
||||
CATEGORY = "utils"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def exec(self, action, tidy_tags, text_a="", text_b="", text_c=""):
|
||||
tidy_tags = tidy_tags == "yes"
|
||||
out = ""
|
||||
if action == "append":
|
||||
out = (", " if tidy_tags else "").join(
|
||||
filter(None, [text_a, text_b, text_c])
|
||||
)
|
||||
else:
|
||||
if text_c is None:
|
||||
text_c = ""
|
||||
if text_b.startswith("/") and text_b.endswith("/"):
|
||||
regex = text_b[1:-1]
|
||||
out = re.sub(regex, text_c, text_a)
|
||||
else:
|
||||
out = text_a.replace(text_b, text_c)
|
||||
if tidy_tags:
|
||||
out = re.sub(r"\s{2,}", " ", out)
|
||||
out = out.replace(" ,", ",")
|
||||
out = re.sub(r",{2,}", ",", out)
|
||||
out = out.strip()
|
||||
return {"ui": {"text": (out,)}, "result": (out,)}
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"ComfyUIDeployStringCombine": StringFunction,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"ComfyUIDeployStringCombine": "String Combine (ComfyUI Deploy)",
|
||||
}
|
||||
@@ -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)"}
|
||||
@@ -0,0 +1,60 @@
|
||||
import folder_paths
|
||||
class AnyType(str):
|
||||
def __ne__(self, __value: object) -> bool:
|
||||
return False
|
||||
|
||||
from os import walk
|
||||
|
||||
WILDCARD = AnyType("*")
|
||||
|
||||
MODEL_EXTENSIONS = {
|
||||
"safetensors": "SafeTensors file format",
|
||||
"ckpt": "Checkpoint file",
|
||||
"pth": "PyTorch serialized file",
|
||||
"pkl": "Pickle file",
|
||||
"onnx": "ONNX file",
|
||||
}
|
||||
|
||||
def fetch_files(path):
|
||||
for (dirpath, dirnames, filenames) in walk(path):
|
||||
fs = []
|
||||
if len(dirnames) > 0:
|
||||
for dirname in dirnames:
|
||||
fs.extend(fetch_files(f"{dirpath}/{dirname}"))
|
||||
for filename in filenames:
|
||||
# Remove "./models/" from the beginning of dirpath
|
||||
relative_dirpath = dirpath.replace("./models/", "", 1)
|
||||
file_path = f"{relative_dirpath}/{filename}"
|
||||
|
||||
# Only add files that are known model extensions
|
||||
file_extension = filename.split('.')[-1].lower()
|
||||
if file_extension in MODEL_EXTENSIONS:
|
||||
fs.append(file_path)
|
||||
|
||||
return fs
|
||||
allModels = fetch_files("./models")
|
||||
|
||||
class ComfyUIDeployModalList:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"model": (allModels, ),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (WILDCARD,)
|
||||
RETURN_NAMES = ("model",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "model"
|
||||
|
||||
def run(self, model=""):
|
||||
# Split the model path by '/' and select the last item
|
||||
model_name = model.split('/')[-1]
|
||||
return [model_name]
|
||||
|
||||
|
||||
NODE_CLASS_MAPPINGS = {"ComfyUIDeployModelList": ComfyUIDeployModalList}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {"ComfyUIDeployModelList": "Model List (ComfyUI Deploy)"}
|
||||
+839
-412
File diff suppressed because it is too large
Load Diff
+35
-18
@@ -6,10 +6,12 @@ from PIL import Image, ImageOps
|
||||
from io import BytesIO
|
||||
from pydantic import BaseModel as PydanticBaseModel
|
||||
|
||||
|
||||
class BaseModel(PydanticBaseModel):
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
|
||||
|
||||
class Status(Enum):
|
||||
NOT_STARTED = "not-started"
|
||||
RUNNING = "running"
|
||||
@@ -17,6 +19,7 @@ class Status(Enum):
|
||||
FAILED = "failed"
|
||||
UPLOADING = "uploading"
|
||||
|
||||
|
||||
class StreamingPrompt(BaseModel):
|
||||
workflow_api: Any
|
||||
auth_token: str
|
||||
@@ -25,44 +28,51 @@ class StreamingPrompt(BaseModel):
|
||||
status_endpoint: Optional[str]
|
||||
file_upload_endpoint: Optional[str]
|
||||
workflow: Any
|
||||
|
||||
gpu_event_id: Optional[str] = None
|
||||
|
||||
|
||||
class SimplePrompt(BaseModel):
|
||||
status_endpoint: Optional[str]
|
||||
file_upload_endpoint: Optional[str]
|
||||
|
||||
|
||||
token: Optional[str]
|
||||
|
||||
|
||||
workflow_api: dict
|
||||
status: Status = Status.NOT_STARTED
|
||||
progress: set = set()
|
||||
last_updated_node: Optional[str] = None,
|
||||
last_updated_node: Optional[str] = None
|
||||
uploading_nodes: set = set()
|
||||
done: bool = False
|
||||
is_realtime: bool = False,
|
||||
start_time: Optional[float] = None,
|
||||
is_realtime: bool = False
|
||||
start_time: Optional[float] = None
|
||||
gpu_event_id: Optional[str] = None
|
||||
|
||||
|
||||
sockets = dict()
|
||||
prompt_metadata: dict[str, SimplePrompt] = {}
|
||||
streaming_prompt_metadata: dict[str, StreamingPrompt] = {}
|
||||
|
||||
|
||||
class BinaryEventTypes:
|
||||
PREVIEW_IMAGE = 1
|
||||
UNENCODED_PREVIEW_IMAGE = 2
|
||||
|
||||
|
||||
|
||||
max_output_id_length = 24
|
||||
|
||||
async def send_image(image_data, sid=None, output_id:str = None):
|
||||
|
||||
async def send_image(image_data, sid=None, output_id: str = None):
|
||||
max_length = max_output_id_length
|
||||
output_id = output_id[:max_length]
|
||||
padded_output_id = output_id.ljust(max_length, '\x00')
|
||||
encoded_output_id = padded_output_id.encode('ascii', 'replace')
|
||||
|
||||
padded_output_id = output_id.ljust(max_length, "\x00")
|
||||
encoded_output_id = padded_output_id.encode("ascii", "replace")
|
||||
|
||||
image_type = image_data[0]
|
||||
image = image_data[1]
|
||||
max_size = image_data[2]
|
||||
quality = image_data[3]
|
||||
if max_size is not None:
|
||||
if hasattr(Image, 'Resampling'):
|
||||
if hasattr(Image, "Resampling"):
|
||||
resampling = Image.Resampling.BILINEAR
|
||||
else:
|
||||
resampling = Image.ANTIALIAS
|
||||
@@ -86,17 +96,23 @@ async def send_image(image_data, sid=None, output_id:str = None):
|
||||
position_after = bytesIO.tell()
|
||||
bytes_written = position_after - position_before
|
||||
print(f"Bytes written: {bytes_written}")
|
||||
|
||||
|
||||
image.save(bytesIO, format=image_type, quality=quality, compress_level=1)
|
||||
preview_bytes = bytesIO.getvalue()
|
||||
await send_bytes(BinaryEventTypes.PREVIEW_IMAGE, preview_bytes, sid=sid)
|
||||
|
||||
|
||||
|
||||
async def send_socket_catch_exception(function, message):
|
||||
try:
|
||||
await function(message)
|
||||
except (aiohttp.ClientError, aiohttp.ClientPayloadError, ConnectionResetError) as err:
|
||||
except (
|
||||
aiohttp.ClientError,
|
||||
aiohttp.ClientPayloadError,
|
||||
ConnectionResetError,
|
||||
) as err:
|
||||
print("send error:", err)
|
||||
|
||||
|
||||
def encode_bytes(event, data):
|
||||
if not isinstance(event, int):
|
||||
raise RuntimeError(f"Binary event types must be integers, got {event}")
|
||||
@@ -106,9 +122,10 @@ def encode_bytes(event, data):
|
||||
message.extend(data)
|
||||
return message
|
||||
|
||||
|
||||
async def send_bytes(event, data, sid=None):
|
||||
message = encode_bytes(event, data)
|
||||
|
||||
|
||||
print("sending image to ", event, sid)
|
||||
|
||||
if sid is None:
|
||||
@@ -116,4 +133,4 @@ async def send_bytes(event, data, sid=None):
|
||||
for ws in _sockets:
|
||||
await send_socket_catch_exception(ws.send_bytes, message)
|
||||
elif sid in sockets:
|
||||
await send_socket_catch_exception(sockets[sid].send_bytes, message)
|
||||
await send_socket_catch_exception(sockets[sid].send_bytes, message)
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "comfyui-deploy"
|
||||
description = "Open source comfyui deployment platform, a vercel for generative workflow infra."
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
license = "LICENSE"
|
||||
dependencies = ["aiofiles", "pydantic", "opencv-python", "imageio-ffmpeg"]
|
||||
|
||||
|
||||
@@ -3,4 +3,5 @@ pydantic
|
||||
opencv-python
|
||||
imageio-ffmpeg
|
||||
brotli
|
||||
tabulate
|
||||
# logfire
|
||||
@@ -1,4 +0,0 @@
|
||||
/** @typedef {import('../../../web/scripts/api.js').api} API*/
|
||||
import { api as _api } from '../../scripts/api.js';
|
||||
/** @type {API} */
|
||||
export const api = _api;
|
||||
@@ -1,4 +0,0 @@
|
||||
/** @typedef {import('../../../web/scripts/app.js').ComfyApp} ComfyApp*/
|
||||
import { app as _app } from '../../scripts/app.js';
|
||||
/** @type {ComfyApp} */
|
||||
export const app = _app;
|
||||
+367
-30
@@ -1,8 +1,44 @@
|
||||
import { app } from "./app.js";
|
||||
import { api } from "./api.js";
|
||||
import { ComfyWidgets, LGraphNode } from "./widgets.js";
|
||||
import { app } from "../../scripts/app.js";
|
||||
import { api } from "../../scripts/api.js";
|
||||
// import { LGraphNode } from "../../scripts/widgets.js";
|
||||
LGraphNode = LiteGraph.LGraphNode;
|
||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||
|
||||
import { generateDependencyGraph } from "https://esm.sh/[email protected]";
|
||||
import { ComfyDeploy } from "https://esm.sh/comfydeploy@0.0.19-beta.30";
|
||||
import { ComfyDeploy } from "https://esm.sh/comfydeploy@2.0.0-beta.69";
|
||||
|
||||
const styles = `
|
||||
.comfydeploy-menu-item {
|
||||
background: linear-gradient(to right, rgba(74, 144, 226, 0.9), rgba(103, 178, 111, 0.9)) !important;
|
||||
color: white !important;
|
||||
position: relative !important;
|
||||
padding-left: 20px !important;
|
||||
}
|
||||
|
||||
.comfydeploy-menu-item:hover {
|
||||
filter: brightness(1.1) !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.comfydeploy-menu-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-image: url('https://www.comfydeploy.com/icon.svg');
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
}
|
||||
`;
|
||||
|
||||
// Add stylesheet to document
|
||||
const styleSheet = document.createElement("style");
|
||||
styleSheet.textContent = styles;
|
||||
document.head.appendChild(styleSheet);
|
||||
|
||||
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>`;
|
||||
|
||||
@@ -92,19 +128,183 @@ const context = {
|
||||
// native_run_api_endpoint: "http://localhost:3011/api/run",
|
||||
// };
|
||||
|
||||
function getSelectedWorkflowInfo() {
|
||||
return context.selectedWorkflowInfo;
|
||||
async function getSelectedWorkflowInfo() {
|
||||
const workflow_info_promise = new Promise((resolve) => {
|
||||
try {
|
||||
const handleMessage = (event) => {
|
||||
try {
|
||||
const message = JSON.parse(event.data);
|
||||
if (message.type === "workflow_info") {
|
||||
resolve(message.data);
|
||||
window.removeEventListener("message", handleMessage);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
resolve(undefined);
|
||||
}
|
||||
};
|
||||
window.addEventListener("message", handleMessage);
|
||||
sendEventToCD("workflow_info");
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
resolve(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
return workflow_info_promise;
|
||||
}
|
||||
|
||||
function setSelectedWorkflowInfo(info) {
|
||||
context.selectedWorkflowInfo = info;
|
||||
}
|
||||
|
||||
const VALID_TYPES = [
|
||||
"STRING",
|
||||
"combo",
|
||||
"number",
|
||||
"toggle",
|
||||
"BOOLEAN",
|
||||
"text",
|
||||
"string",
|
||||
];
|
||||
|
||||
function hideWidget(node, widget, suffix = "") {
|
||||
if (widget.type?.startsWith(CONVERTED_TYPE)) return;
|
||||
widget.origType = widget.type;
|
||||
widget.origComputeSize = widget.computeSize;
|
||||
widget.origSerializeValue = widget.serializeValue;
|
||||
widget.computeSize = () => [0, -4];
|
||||
widget.type = CONVERTED_TYPE + suffix;
|
||||
widget.serializeValue = () => {
|
||||
if (!node.inputs) {
|
||||
return void 0;
|
||||
}
|
||||
let node_input = node.inputs.find((i) => i.widget?.name === widget.name);
|
||||
if (!node_input || !node_input.link) {
|
||||
return void 0;
|
||||
}
|
||||
return widget.origSerializeValue
|
||||
? widget.origSerializeValue()
|
||||
: widget.value;
|
||||
};
|
||||
if (widget.linkedWidgets) {
|
||||
for (const w of widget.linkedWidgets) {
|
||||
hideWidget(node, w, ":" + widget.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getWidgetType(config) {
|
||||
let type = config[0];
|
||||
if (type instanceof Array) {
|
||||
type = "COMBO";
|
||||
}
|
||||
return { type };
|
||||
}
|
||||
|
||||
const GET_CONFIG = Symbol();
|
||||
|
||||
function convertToInput(node, widget, config) {
|
||||
console.log(node);
|
||||
if (node.type == "LoadImage") {
|
||||
var inputNode = LiteGraph.createNode("ComfyUIDeployExternalImage");
|
||||
console.log(widget);
|
||||
|
||||
const currentOutputsLinks = node.outputs[0].links;
|
||||
|
||||
// const index = node.inputs.findIndex((x) => x.name == widget.name);
|
||||
// console.log(node.widgets_values, index);
|
||||
// inputNode.configure({
|
||||
// widgets_values: ["input_text", widget.value],
|
||||
// });
|
||||
inputNode.pos = node.pos;
|
||||
inputNode.id = ++app.graph.last_node_id;
|
||||
// inputNode.pos[0] += node.size[0] + 40;
|
||||
node.pos[0] -= inputNode.size[0] + 20;
|
||||
console.log(inputNode);
|
||||
console.log(app.graph);
|
||||
app.graph.add(inputNode);
|
||||
|
||||
const links = app.graph.links;
|
||||
|
||||
console.log(currentOutputsLinks);
|
||||
|
||||
for (let i = 0; i < currentOutputsLinks.length; i++) {
|
||||
const link = currentOutputsLinks[i];
|
||||
const llink = links[link];
|
||||
console.log(links[link]);
|
||||
setTimeout(
|
||||
() => inputNode.connect(0, llink.target_id, llink.target_slot),
|
||||
100,
|
||||
);
|
||||
}
|
||||
|
||||
node.connect(0, inputNode, 0);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
hideWidget(node, widget);
|
||||
const { type } = getWidgetType(config);
|
||||
const sz = node.size;
|
||||
const inputIsOptional = !!widget.options?.inputIsOptional;
|
||||
const input = node.addInput(widget.name, type, {
|
||||
widget: { name: widget.name, [GET_CONFIG]: () => config },
|
||||
...(inputIsOptional ? { shape: LiteGraph.SlotShape.HollowCircle } : {}),
|
||||
});
|
||||
for (const widget2 of node.widgets) {
|
||||
widget2.last_y += LiteGraph.NODE_SLOT_HEIGHT;
|
||||
}
|
||||
node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])]);
|
||||
|
||||
if (type == "STRING") {
|
||||
var inputNode = LiteGraph.createNode("ComfyUIDeployExternalText");
|
||||
console.log(widget);
|
||||
const index = node.inputs.findIndex((x) => x.name == widget.name);
|
||||
console.log(node.widgets_values, index);
|
||||
inputNode.configure({
|
||||
widgets_values: ["input_text", widget.value],
|
||||
});
|
||||
inputNode.id = ++app.graph.last_node_id;
|
||||
inputNode.pos = node.pos;
|
||||
inputNode.pos[0] -= node.size[0] + 40;
|
||||
console.log(inputNode);
|
||||
console.log(app.graph);
|
||||
app.graph.add(inputNode);
|
||||
inputNode.connect(0, node, index);
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
const CONVERTED_TYPE = "converted-widget";
|
||||
|
||||
function getConfig(widgetName) {
|
||||
const { nodeData } = this.constructor;
|
||||
return (
|
||||
nodeData?.input?.required?.[widgetName] ??
|
||||
nodeData?.input?.optional?.[widgetName]
|
||||
);
|
||||
}
|
||||
|
||||
function isConvertibleWidget(widget, config) {
|
||||
return (
|
||||
(VALID_TYPES.includes(widget.type) || VALID_TYPES.includes(config[0])) &&
|
||||
!widget.options?.forceInput
|
||||
);
|
||||
}
|
||||
|
||||
var __defProp = Object.defineProperty;
|
||||
var __name = (target, value) =>
|
||||
__defProp(target, "name", { value, configurable: true });
|
||||
|
||||
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
|
||||
/** @type {ComfyExtension} */
|
||||
const ext = {
|
||||
name: "BennyKok.ComfyUIDeploy",
|
||||
|
||||
native_mode: false,
|
||||
|
||||
init(app) {
|
||||
addButton();
|
||||
|
||||
@@ -114,6 +314,7 @@ const ext = {
|
||||
const org_display = queryParams.get("org_display");
|
||||
const origin = queryParams.get("origin");
|
||||
const workspace_mode = queryParams.get("workspace_mode");
|
||||
this.native_mode = queryParams.get("native_mode") === "true";
|
||||
|
||||
if (workspace_mode) {
|
||||
document.querySelector(".comfy-menu").style.display = "none";
|
||||
@@ -207,6 +408,118 @@ const ext = {
|
||||
}
|
||||
},
|
||||
|
||||
async beforeRegisterNodeDef(nodeType, nodeData, app2) {
|
||||
const origGetExtraMenuOptions = nodeType.prototype.getExtraMenuOptions;
|
||||
nodeType.prototype.getExtraMenuOptions = function (_, options) {
|
||||
const r = origGetExtraMenuOptions
|
||||
? origGetExtraMenuOptions.apply(this, arguments)
|
||||
: void 0;
|
||||
if (this.widgets) {
|
||||
let toInput = [];
|
||||
let toWidget = [];
|
||||
for (const w of this.widgets) {
|
||||
if (w.options?.forceInput) {
|
||||
continue;
|
||||
}
|
||||
if (w.type === CONVERTED_TYPE) {
|
||||
toWidget.push({
|
||||
content: `Convert ${w.name} to widget`,
|
||||
callback: /* @__PURE__ */ __name(
|
||||
() => convertToWidget(this, w),
|
||||
"callback",
|
||||
),
|
||||
});
|
||||
} else {
|
||||
const config = getConfig.call(this, w.name) ?? [
|
||||
w.type,
|
||||
w.options || {},
|
||||
];
|
||||
if (isConvertibleWidget(w, config)) {
|
||||
toInput.push({
|
||||
content: `Convert ${w.name} to external input`,
|
||||
callback: /* @__PURE__ */ __name(
|
||||
() => convertToInput(this, w, config),
|
||||
"callback",
|
||||
),
|
||||
className: "comfydeploy-menu-item",
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if (toInput.length) {
|
||||
if (true) {
|
||||
options.push();
|
||||
|
||||
let optionIndex = options.findIndex((o) => o.content === "Outputs");
|
||||
if (optionIndex === -1) optionIndex = options.length;
|
||||
else optionIndex++;
|
||||
options.splice(
|
||||
0,
|
||||
0,
|
||||
// {
|
||||
// content: "[ComfyDeploy] Convert to External Input",
|
||||
// submenu: {
|
||||
// options: toInput,
|
||||
// },
|
||||
// className: "comfydeploy-menu-item"
|
||||
// },
|
||||
...toInput,
|
||||
null,
|
||||
);
|
||||
} else {
|
||||
options.push(...toInput, null);
|
||||
}
|
||||
}
|
||||
// if (toWidget.length) {
|
||||
// if (useConversionSubmenusSetting.value) {
|
||||
// options.push({
|
||||
// content: "Convert Input to Widget",
|
||||
// submenu: {
|
||||
// options: toWidget,
|
||||
// },
|
||||
// });
|
||||
// } else {
|
||||
// options.push(...toWidget, null);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
return r;
|
||||
};
|
||||
|
||||
// const origonNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
// nodeType.prototype.onNodeCreated = function () {
|
||||
// const r = origonNodeCreated
|
||||
// ? origonNodeCreated.apply(this, arguments)
|
||||
// : void 0;
|
||||
|
||||
// if (!this.widgets) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
// console.log(this.widgets);
|
||||
|
||||
// this.widgets.forEach(element => {
|
||||
// if (element.type != "customtext") return
|
||||
|
||||
// console.log(element.element);
|
||||
|
||||
// const parent = element.element.parentElement
|
||||
|
||||
// console.log(element.element.parentElement)
|
||||
// const btn = document.createElement("button");
|
||||
// // const div = document.createElement("div");
|
||||
// // parent.removeChild(element.element)
|
||||
// // div.appendChild(element.element)
|
||||
// // parent.appendChild(div)
|
||||
// // element.element = div
|
||||
// // console.log(element.element);
|
||||
// // btn.style = element.element.style
|
||||
// });
|
||||
|
||||
// return r
|
||||
// };
|
||||
},
|
||||
|
||||
registerCustomNodes() {
|
||||
/** @type {LGraphNode}*/
|
||||
class ComfyDeploy extends LGraphNode {
|
||||
@@ -319,7 +632,10 @@ const ext = {
|
||||
false,
|
||||
);
|
||||
} catch (error) {
|
||||
console.warning("Error setting validation to false, is fine to ignore this", error);
|
||||
console.warning(
|
||||
"Error setting validation to false, is fine to ignore this",
|
||||
error,
|
||||
);
|
||||
}
|
||||
console.log("loadGraphData");
|
||||
app.loadGraphData(comfyUIWorkflow);
|
||||
@@ -429,6 +745,25 @@ const ext = {
|
||||
// }
|
||||
});
|
||||
|
||||
if (this.native_mode) {
|
||||
// console.log("native mode", window, window.app);
|
||||
try {
|
||||
await app.ui.settings.setSettingValueAsync("Comfy.UseNewMenu", "Top");
|
||||
await app.ui.settings.setSettingValueAsync(
|
||||
"Comfy.Sidebar.Size",
|
||||
"small",
|
||||
);
|
||||
await app.ui.settings.setSettingValueAsync(
|
||||
"Comfy.Sidebar.Location",
|
||||
"right",
|
||||
);
|
||||
localStorage.setItem("Comfy.MenuPosition.Docked", "true");
|
||||
console.log("native mode manmanman");
|
||||
} catch (error) {
|
||||
console.error("Error setting validation to false", error);
|
||||
}
|
||||
}
|
||||
|
||||
app.graph.onAfterChange = ((originalFunction) =>
|
||||
async function () {
|
||||
const prompt = await app.graphToPrompt();
|
||||
@@ -955,8 +1290,6 @@ function addButton() {
|
||||
|
||||
app.registerExtension(ext);
|
||||
|
||||
import { ComfyDialog, $el } from "../../scripts/ui.js";
|
||||
|
||||
export class InfoDialog extends ComfyDialog {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -1527,30 +1860,34 @@ async function loadWorkflowApi(versionId) {
|
||||
|
||||
const orginal_fetch_api = api.fetchApi;
|
||||
api.fetchApi = async (route, options) => {
|
||||
console.log("Fetch API called with args:", route, options);
|
||||
console.log("Fetch API called with args:", route, options, ext.native_mode);
|
||||
|
||||
const info = getSelectedWorkflowInfo();
|
||||
if (info && route.startsWith("/prompt")) {
|
||||
const body = JSON.parse(options.body);
|
||||
if (route.startsWith("/prompt") && ext.native_mode) {
|
||||
const info = await getSelectedWorkflowInfo();
|
||||
console.log("info", info);
|
||||
if (info) {
|
||||
const body = JSON.parse(options.body);
|
||||
|
||||
const data = {
|
||||
client_id: body.client_id,
|
||||
workflow_api_json: body.prompt,
|
||||
workflow: body?.extra_data?.extra_pnginfo?.workflow,
|
||||
is_native_run: true,
|
||||
machine_id: info.machine_id,
|
||||
workflow_id: info.workflow_id,
|
||||
native_run_api_endpoint: info.native_run_api_endpoint,
|
||||
};
|
||||
const data = {
|
||||
client_id: body.client_id,
|
||||
workflow_api_json: body.prompt,
|
||||
workflow: body?.extra_data?.extra_pnginfo?.workflow,
|
||||
is_native_run: true,
|
||||
machine_id: info.machine_id,
|
||||
workflow_id: info.workflow_id,
|
||||
native_run_api_endpoint: info.native_run_api_endpoint,
|
||||
gpu_event_id: info.gpu_event_id,
|
||||
};
|
||||
|
||||
return await fetch("/comfyui-deploy/run", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${info.cd_token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
return await fetch("/comfyui-deploy/run", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${info.cd_token}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return await orginal_fetch_api.call(api, route, options);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
// /** @typedef {import('../../../web/scripts/api.js').api} API*/
|
||||
// import { api as _api } from "../../scripts/api.js";
|
||||
// /** @type {API} */
|
||||
// export const api = _api;
|
||||
|
||||
/** @typedef {typeof import('../../../web/scripts/widgets.js').ComfyWidgets} Widgets*/
|
||||
import { ComfyWidgets as _ComfyWidgets } from "../../scripts/widgets.js";
|
||||
|
||||
/**
|
||||
* @type {Widgets}
|
||||
*/
|
||||
export const ComfyWidgets = _ComfyWidgets;
|
||||
|
||||
// import { LGraphNode as _LGraphNode } from "../../types/litegraph.js";
|
||||
|
||||
/** @typedef {typeof import('../../../web/types/litegraph.js').LGraphNode} LGraphNode*/
|
||||
/** @type {LGraphNode}*/
|
||||
export const LGraphNode = LiteGraph.LGraphNode;
|
||||
Reference in New Issue
Block a user