"doc/git@developer.sourcefind.cn:wangsen/paddle_dbnet.git" did not exist on "ec0de454a16ea3e1ef0a896af8041e55023ae418"
Unverified Commit 9bd7bfa6 authored by pythongosssss's avatar pythongosssss Committed by GitHub
Browse files

Added workflow history

Moved socket output updates to all node executions
Made image rendering on nodes more generic
parent 2816eb23
...@@ -35,7 +35,7 @@ if __name__ == "__main__": ...@@ -35,7 +35,7 @@ if __name__ == "__main__":
import torch import torch
import nodes import nodes
def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}, server=None, unique_id=None): def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}):
valid_inputs = class_def.INPUT_TYPES() valid_inputs = class_def.INPUT_TYPES()
input_data_all = {} input_data_all = {}
for x in inputs: for x in inputs:
...@@ -57,10 +57,6 @@ def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}, serv ...@@ -57,10 +57,6 @@ def get_input_data(inputs, class_def, outputs={}, prompt={}, extra_data={}, serv
if h[x] == "EXTRA_PNGINFO": if h[x] == "EXTRA_PNGINFO":
if "extra_pnginfo" in extra_data: if "extra_pnginfo" in extra_data:
input_data_all[x] = extra_data['extra_pnginfo'] input_data_all[x] = extra_data['extra_pnginfo']
if h[x] == "SERVER":
input_data_all[x] = server
if h[x] == "UNIQUE_ID":
input_data_all[x] = unique_id
return input_data_all return input_data_all
def recursive_execute(server, prompt, outputs, current_item, extra_data={}): def recursive_execute(server, prompt, outputs, current_item, extra_data={}):
...@@ -84,10 +80,12 @@ def recursive_execute(server, prompt, outputs, current_item, extra_data={}): ...@@ -84,10 +80,12 @@ def recursive_execute(server, prompt, outputs, current_item, extra_data={}):
input_data_all = get_input_data(inputs, class_def, outputs, prompt, extra_data, server, unique_id) input_data_all = get_input_data(inputs, class_def, outputs, prompt, extra_data, server, unique_id)
if server.client_id is not None: if server.client_id is not None:
server.send_sync("execute", { "node": unique_id }, server.client_id) server.send_sync("executing", { "node": unique_id }, server.client_id)
obj = class_def() obj = class_def()
outputs[unique_id] = getattr(obj, obj.FUNCTION)(**input_data_all) outputs[unique_id] = getattr(obj, obj.FUNCTION)(**input_data_all)
if "ui" in outputs[unique_id] and server.client_id is not None:
server.send_sync("executed", { "node": unique_id, "output": outputs[unique_id]["ui"] }, server.client_id)
return executed + [unique_id] return executed + [unique_id]
def recursive_will_execute(prompt, outputs, current_item): def recursive_will_execute(prompt, outputs, current_item):
...@@ -195,7 +193,6 @@ class PromptExecutor: ...@@ -195,7 +193,6 @@ class PromptExecutor:
valid = False valid = False
if valid: if valid:
executed += recursive_execute(self.server, prompt, self.outputs, x, extra_data) executed += recursive_execute(self.server, prompt, self.outputs, x, extra_data)
except Exception as e: except Exception as e:
print(traceback.format_exc()) print(traceback.format_exc())
to_delete = [] to_delete = []
...@@ -212,10 +209,9 @@ class PromptExecutor: ...@@ -212,10 +209,9 @@ class PromptExecutor:
executed = set(executed) executed = set(executed)
for x in executed: for x in executed:
self.old_prompt[x] = copy.deepcopy(prompt[x]) self.old_prompt[x] = copy.deepcopy(prompt[x])
finally: finally:
if self.server.client_id is not None: if self.server.client_id is not None:
self.server.send_sync("execute", { "node": None }, self.server.client_id) self.server.send_sync("executing", { "node": None }, self.server.client_id)
torch.cuda.empty_cache() torch.cuda.empty_cache()
...@@ -307,7 +303,7 @@ def prompt_worker(q, server): ...@@ -307,7 +303,7 @@ def prompt_worker(q, server):
while True: while True:
item, item_id = q.get() item, item_id = q.get()
e.execute(item[-2], item[-1]) e.execute(item[-2], item[-1])
q.task_done(item_id) q.task_done(item_id, e.outputs)
class PromptQueue: class PromptQueue:
def __init__(self, server): def __init__(self, server):
...@@ -317,6 +313,7 @@ class PromptQueue: ...@@ -317,6 +313,7 @@ class PromptQueue:
self.task_counter = 0 self.task_counter = 0
self.queue = [] self.queue = []
self.currently_running = {} self.currently_running = {}
self.history = {}
server.prompt_queue = self server.prompt_queue = self
def put(self, item): def put(self, item):
...@@ -336,9 +333,12 @@ class PromptQueue: ...@@ -336,9 +333,12 @@ class PromptQueue:
self.server.queue_updated() self.server.queue_updated()
return (item, i) return (item, i)
def task_done(self, item_id): def task_done(self, item_id, outputs):
with self.mutex: with self.mutex:
self.currently_running.pop(item_id) self.history[item_id] = { "prompt": self.currently_running.pop(item_id), "outputs": {} }
for o in outputs:
if "ui" in outputs[o]:
self.history[item_id]["outputs"][o] = outputs[o]["ui"]
self.server.queue_updated() self.server.queue_updated()
def get_current_queue(self): def get_current_queue(self):
......
...@@ -623,7 +623,7 @@ class SaveImage: ...@@ -623,7 +623,7 @@ class SaveImage:
return {"required": return {"required":
{"images": ("IMAGE", ), {"images": ("IMAGE", ),
"filename_prefix": ("STRING", {"default": "ComfyUI"})}, "filename_prefix": ("STRING", {"default": "ComfyUI"})},
"hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO", "server": "SERVER", "unique_id": "UNIQUE_ID"}, "hidden": {"prompt": "PROMPT", "extra_pnginfo": "EXTRA_PNGINFO"},
} }
RETURN_TYPES = () RETURN_TYPES = ()
...@@ -633,7 +633,7 @@ class SaveImage: ...@@ -633,7 +633,7 @@ class SaveImage:
CATEGORY = "image" CATEGORY = "image"
def save_images(self, images, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None, server=None, unique_id=None): def save_images(self, images, filename_prefix="ComfyUI", prompt=None, extra_pnginfo=None):
def map_filename(filename): def map_filename(filename):
prefix_len = len(filename_prefix) prefix_len = len(filename_prefix)
prefix = filename[:prefix_len + 1] prefix = filename[:prefix_len + 1]
...@@ -662,10 +662,9 @@ class SaveImage: ...@@ -662,10 +662,9 @@ class SaveImage:
metadata.add_text(x, json.dumps(extra_pnginfo[x])) metadata.add_text(x, json.dumps(extra_pnginfo[x]))
file = f"{filename_prefix}_{counter:05}_.png" file = f"{filename_prefix}_{counter:05}_.png"
img.save(os.path.join(self.output_dir, file), pnginfo=metadata, optimize=True) img.save(os.path.join(self.output_dir, file), pnginfo=metadata, optimize=True)
paths.append(f"/view/{file}") paths.append(file)
counter += 1 counter += 1
if server is not None: return { "ui": { "images": paths } }
server.send_sync("image", {"images": paths, "id": unique_id})
class LoadImage: class LoadImage:
input_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "input") input_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "input")
......
...@@ -77,6 +77,10 @@ class PromptServer(): ...@@ -77,6 +77,10 @@ class PromptServer():
out[x] = info out[x] = info
return web.json_response(out) return web.json_response(out)
@routes.get("/history")
async def get_history(request):
return web.json_response(self.prompt_queue.history)
@routes.get("/queue") @routes.get("/queue")
async def get_queue(request): async def get_queue(request):
queue_info = {} queue_info = {}
...@@ -133,6 +137,19 @@ class PromptServer(): ...@@ -133,6 +137,19 @@ class PromptServer():
self.prompt_queue.delete_queue_item(delete_func) self.prompt_queue.delete_queue_item(delete_func)
return web.Response(status=200) return web.Response(status=200)
@routes.post("/history")
async def post_history(request):
json_data = await request.json()
if "clear" in json_data:
if json_data["clear"]:
self.prompt_queue.history = {}
if "delete" in json_data:
to_delete = json_data['delete']
for id_to_delete in to_delete:
self.prompt_queue.history.pop(id_to_delete, None)
return web.Response(status=200)
self.app.add_routes(routes) self.app.add_routes(routes)
self.app.add_routes([ self.app.add_routes([
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
left: 50%; /* Center the modal horizontally */ left: 50%; /* Center the modal horizontally */
top: 50%; /* Center the modal vertically */ top: 50%; /* Center the modal vertically */
transform: translate(-50%, -50%); /* Use this to center the modal */ transform: translate(-50%, -50%); /* Use this to center the modal */
min-width: 50%; /* Set a width for the modal */ width: 50%; /* Set a width for the modal */
height: auto; /* Set a height for the modal */ height: auto; /* Set a height for the modal */
padding: 30px; padding: 30px;
background-color: #ff0000; /* Modal background */ background-color: #ff0000; /* Modal background */
...@@ -59,18 +59,6 @@ ...@@ -59,18 +59,6 @@
white-space: pre-line; /* This will respect line breaks */ white-space: pre-line; /* This will respect line breaks */
margin-bottom: 20px; /* Add some margin between the text and the close button*/ margin-bottom: 20px; /* Add some margin between the text and the close button*/
} }
#modal-text img {
max-width: calc(100vw - 96px - 36px);
max-height: calc(100vh - 96px - 36px);
}
#images img {
width: 100%;
max-height: 300px;
object-fit: contain;
cursor: pointer;
}
</style> </style>
<div id="myErrorModal" class="modal"> <div id="myErrorModal" class="modal">
<div class="modal-content"> <div class="modal-content">
...@@ -78,6 +66,7 @@ ...@@ -78,6 +66,7 @@
<span class="close">CLOSE</span> <span class="close">CLOSE</span>
</div> </div>
</div> </div>
<canvas id='mycanvas' width='1000' height='1000' style='width: 100%; height: 100%;'></canvas> <canvas id='mycanvas' width='1000' height='1000' style='width: 100%; height: 100%;'></canvas>
<script> <script>
...@@ -87,7 +76,7 @@ var canvas = new LGraphCanvas("#mycanvas", graph); ...@@ -87,7 +76,7 @@ var canvas = new LGraphCanvas("#mycanvas", graph);
const ccc = document.getElementById("mycanvas"); const ccc = document.getElementById("mycanvas");
const ctx = ccc.getContext("2d"); const ctx = ccc.getContext("2d");
let images = {} let nodeOutputs = {}
// Resize the canvas to match the size of the canvas element // Resize the canvas to match the size of the canvas element
function resizeCanvas() { function resizeCanvas() {
...@@ -276,47 +265,43 @@ function onObjectInfo(json) { ...@@ -276,47 +265,43 @@ function onObjectInfo(json) {
this.addInput(x, type); this.addInput(x, type);
} }
MyNode.prototype.onDrawBackground = function(ctx) {
if(key === "SaveImage") { const output = nodeOutputs[this.id + ""];
MyNode.prototype.onDrawBackground = function(ctx) { if(output && output.images) {
if(this.id + "" in images) { const src = output.images[0];
const src = images[this.id + ""][0]; if(this.src !== src) {
if(this.src !== src) { this.img = null;
this.img = null; this.src = src;
this.src = src; const img = new Image();
const img = new Image(); img.src = "/view/" + src;
img.src = src; img.onload = () => {
img.onload = () => { graph.setDirtyCanvas(true);
graph.setDirtyCanvas(true); this.img = img;
this.img = img; if(this.size[1] < 100) {
if(this.size[1] < 100) { this.size[1] = 250;
this.size[1] = 250;
}
} }
} }
if(this.img) { }
let w = this.img.naturalWidth; if(this.img) {
let h = this.img.naturalHeight; let w = this.img.naturalWidth;
let dw = this.size[0]; let h = this.img.naturalHeight;
let dh = this.size[1]; let dw = this.size[0];
let dh = this.size[1];
const scaleX = dw / w; const scaleX = dw / w;
const scaleY = dh / h; const scaleY = dh / h;
const scale = Math.min(scaleX, scaleY, 1); const scale = Math.min(scaleX, scaleY, 1);
w *= scale; w *= scale;
h *= scale; h *= scale;
let x = (dw - w) / 2; let x = (dw - w) / 2;
let y = (dh - h) / 2; let y = (dh - h) / 2;
ctx.drawImage(this.img, x, y, w, h); ctx.drawImage(this.img, x, y, w, h);
}
} else {
this.size[1] = 58
} }
}; }
} };
} }
out = j['output']; out = j['output'];
...@@ -446,17 +431,16 @@ function graphToPrompt() { ...@@ -446,17 +431,16 @@ function graphToPrompt() {
function closeModal() { function closeModal() {
var modal = document.getElementById("myErrorModal"); var modal = document.getElementById("myErrorModal");
modal.setAttribute("style", ""); modal.style.display = "none";
} }
function showModal(text) { function showModal(text) {
var modal = document.getElementById("myErrorModal"); var modal = document.getElementById("myErrorModal");
var modalText = document.getElementById("modal-text"); var modalText = document.getElementById("modal-text");
modalText.innerHTML = text; modalText.innerHTML = text;
modal.setAttribute("style", "display: block"); modal.style.display = "block";
var closeBtn = modal.getElementsByClassName("close")[0]; var closeBtn = modal.getElementsByClassName("close")[0];
closeBtn.onclick = function(event) {closeModal();} closeBtn.onclick = function(event) {closeModal();}
return modal
} }
function promptPosted(data) function promptPosted(data)
...@@ -672,25 +656,12 @@ function setRunningNode(id) { ...@@ -672,25 +656,12 @@ function setRunningNode(id) {
updateNodeProgress(data); updateNodeProgress(data);
}); });
ws.on("execute", (data) => { ws.on("executing", (data) => {
setRunningNode(data.node); setRunningNode(data.node);
}); });
ws.on("image", (data) => { ws.on("executed", (data) => {
images[data.id] = data.images; nodeOutputs[data.node] = data.output;
const container = document.getElementById("images");
container.replaceChildren(...Object.values(images).map(src => {
const img = document.createElement("img");
img.src = src;
img.onclick = () => {
const modal = showModal();
const modalText = document.getElementById("modal-text");
modalText.innerHTML = `<img src="${img.src}"/>`
modal.setAttribute("style", modal.getAttribute("style") + "; background: #202020")
}
return img;
}))
}); });
} }
createSocket(); createSocket();
...@@ -755,8 +726,8 @@ document.addEventListener('paste', e=>{ ...@@ -755,8 +726,8 @@ document.addEventListener('paste', e=>{
} }
}); });
function deleteQueueElement(delete_id, then) { function deleteQueueElement(type, delete_id, then) {
fetch('/queue', { fetch('/' + type, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
...@@ -770,22 +741,30 @@ function deleteQueueElement(delete_id, then) { ...@@ -770,22 +741,30 @@ function deleteQueueElement(delete_id, then) {
.catch(error => console.error(error)) .catch(error => console.error(error))
} }
function loadQueue() { function loadQueue() {
fetch('/queue') loadItems("queue")
}
function loadHistory() {
loadItems("history")
}
function loadItems(type) {
fetch('/' + type)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
var queue_div = document.getElementById("queuebutton-content"); var queue_div = document.getElementById(type + "button-content");
queue_div.style.display = 'block'; queue_div.style.display = 'block';
var see_queue_button = document.getElementById("seequeuebutton"); var see_queue_button = document.getElementById("see" + type + "button");
let old_w = see_queue_button.style.width; let old_w = see_queue_button.style.width;
see_queue_button.innerHTML = "Close"; see_queue_button.innerHTML = "Close";
let runningcontents = document.getElementById("runningcontents"); let runningcontents;
runningcontents.innerHTML = ''; if(type === "queue") {
let queuecontents = document.getElementById("queuecontents"); runningcontents = document.getElementById("runningcontents");
runningcontents.innerHTML = '';
}
let queuecontents = document.getElementById(type + "contents");
queuecontents.innerHTML = ''; queuecontents.innerHTML = '';
function append_to_list(list_element, append_to_element, append_delete) { function append_to_list(list_element, append_to_element, append_delete, state) {
let number = list_element[0]; let number = list_element[0];
let id = list_element[1]; let id = list_element[1];
let prompt = list_element[2]; let prompt = list_element[2];
...@@ -799,6 +778,9 @@ function loadQueue() { ...@@ -799,6 +778,9 @@ function loadQueue() {
button.workflow = workflow; button.workflow = workflow;
button.onclick = function(event) { button.onclick = function(event) {
loadGraphData(graph, event.target.workflow); loadGraphData(graph, event.target.workflow);
if(state) {
nodeOutputs = state;
}
}; };
append_to_element.appendChild(button); append_to_element.appendChild(button);
...@@ -808,19 +790,28 @@ function loadQueue() { ...@@ -808,19 +790,28 @@ function loadQueue() {
button.style.fontSize = "10px"; button.style.fontSize = "10px";
button.delete_id = id; button.delete_id = id;
button.onclick = function(event) { button.onclick = function(event) {
deleteQueueElement(event.target.delete_id, loadQueue); deleteQueueElement(type, event.target.delete_id, loadItems);
}; };
append_to_element.appendChild(button); append_to_element.appendChild(button);
} }
append_to_element.appendChild(document.createElement("br")); append_to_element.appendChild(document.createElement("br"));
} }
for (let x in data.queue_running) {
append_to_list(data.queue_running[x], runningcontents, false); if(runningcontents) {
for (let x in data.queue_running) {
append_to_list(data.queue_running[x], runningcontents, false);
}
} }
data.queue_pending.sort((a, b) => a[0] - b[0]); let items;
for (let x in data.queue_pending) { if(type === "queue") {
append_to_list(data.queue_pending[x], queuecontents, true); items = data.queue_pending;
} else {
items = Object.values(data).map(d => d.prompt);
}
items.sort((a, b) => a[0] - b[0]);
for (let i in items) {
append_to_list(items[i], queuecontents, true, type === "queue"? null : data[i].outputs);
} }
}).catch((response) => {console.log(response)}); }).catch((response) => {console.log(response)});
} }
...@@ -833,31 +824,41 @@ function loadQueueIfVisible() ...@@ -833,31 +824,41 @@ function loadQueueIfVisible()
} }
} }
function seeQueue() { function seeItems(type) {
var queue_div = document.getElementById("queuebutton-content"); var queue_div = document.getElementById(type + "button-content");
if (queue_div.style.display == 'block') { if (queue_div.style.display == 'block') {
queue_div.style.display = 'none'; closeItems(type)
var see_queue_button = document.getElementById("seequeuebutton");
see_queue_button.innerHTML = "See Queue"
} else { } else {
loadQueue(); loadItems(type);
} }
} }
function closeQueue() { function seeQueue() {
var queue_div = document.getElementById("queuebutton-content"); closeItems("history")
seeItems("queue")
}
function seeHistory() {
closeItems("queue")
seeItems("history")
}
function closeItems(type) {
var queue_div = document.getElementById(type + "button-content");
queue_div.style.display = 'none'; queue_div.style.display = 'none';
var see_queue_button = document.getElementById("see" + type + "button");
see_queue_button.innerHTML = "See " + type[0].toUpperCase() + type.substr(1)
} }
function clearQueue() { function clearItems(type) {
fetch('/queue', { fetch('/' + type, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
body: JSON.stringify({"clear":true}) body: JSON.stringify({"clear":true})
}).then(data => { }).then(data => {
loadQueue(); loadItems(type);
}) })
.catch(error => console.error(error)); .catch(error => console.error(error));
} }
...@@ -871,6 +872,7 @@ function clearQueue() { ...@@ -871,6 +872,7 @@ function clearQueue() {
<span style="left: 0%;"> <span style="left: 0%;">
<button style="font-size: 10px;" id="queuebutton" onclick="postPrompt(-1)">Queue Front</button> <button style="font-size: 10px;" id="queuebutton" onclick="postPrompt(-1)">Queue Front</button>
<button style="font-size: 10px; width: 50%;" id="seequeuebutton" onclick="seeQueue()">See Queue</button> <button style="font-size: 10px; width: 50%;" id="seequeuebutton" onclick="seeQueue()">See Queue</button>
<button style="font-size: 10px; width: 50%;" id="seehistorybutton" onclick="seeHistory()">See History</button>
<br> <br>
</span> </span>
<div id="queuebutton-content" style="background-color: #e1e1e1;min-width: 160px;display: none;z-index: 101;"> <div id="queuebutton-content" style="background-color: #e1e1e1;min-width: 160px;display: none;z-index: 101;">
...@@ -894,13 +896,21 @@ function clearQueue() { ...@@ -894,13 +896,21 @@ function clearQueue() {
</span> </span>
</div> </div>
<div id="historybutton-content" style="background-color: #e1e1e1;min-width: 160px;display: none;z-index: 101;">
<span style="width:100%;padding: 3px;display:inline-block;">History:</span>
<div id="historycontents" style="overflow-y: scroll;height: 100px;background-color: #d0d0d0;padding: 5px;">
</div>
<span style="padding: 5px;display:inline-block;">
<button style="font-size: 12px;" onclick="clearHistory()">Clear History</button>
<button style="font-size: 12px;" onclick="loadHistory()">Refresh</button>
</span>
</div>
<br> <br>
<button style="font-size: 20px;" onclick="saveGraph()">Save</button><br> <button style="font-size: 20px;" onclick="saveGraph()">Save</button><br>
<button style="font-size: 20px;" onclick="loadGraph()">Load</button> <button style="font-size: 20px;" onclick="loadGraph()">Load</button>
<br> <br>
<button style="font-size: 20px;" onclick="clearGraph()">Clear</button><br> <button style="font-size: 20px;" onclick="clearGraph()">Clear</button><br>
<button style="font-size: 20px;" onclick="loadTxt2Img()">Load Default</button><br> <button style="font-size: 20px;" onclick="loadTxt2Img()">Load Default</button><br>
<div id="images"></div>
</span> </span>
</body> </body>
</html> </html>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment