Update to 1.3
1.3
This commit is contained in:
+26
-15
@@ -2,7 +2,7 @@ from server import PromptServer
|
||||
from aiohttp import web
|
||||
from nodes import PreviewImage, LoadImage
|
||||
from comfy.model_management import InterruptProcessingException, throw_exception_if_processing_interrupted
|
||||
import time, os
|
||||
import time, os, random
|
||||
import torch
|
||||
|
||||
REQUEST_RESHOW = "-1"
|
||||
@@ -32,27 +32,30 @@ async def cg_image_filter_message(request):
|
||||
print(f"Ignoring response {response} as current response is {Message.data}")
|
||||
return web.json_response({})
|
||||
|
||||
def wait(secs, uid):
|
||||
def wait(secs, uid, unique):
|
||||
Message.setdata(None, "start of wait")
|
||||
end_time = time.monotonic() + secs
|
||||
while(time.monotonic() < end_time and Message.data is None):
|
||||
throw_exception_if_processing_interrupted()
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", {"tick": int(end_time - time.monotonic()), "uid": uid})
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", {"tick": int(end_time - time.monotonic()), "uid": uid, "unique":unique})
|
||||
time.sleep(0.2)
|
||||
response = Message.data
|
||||
Message.setdata(None, "read response")
|
||||
if response is None:
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", {"timeout": True, "uid": uid})
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", {"timeout": True, "uid": uid, "unique":unique})
|
||||
|
||||
return response
|
||||
|
||||
def send_with_resend(payload, timeout, uid):
|
||||
def send_with_resend(payload, timeout, uid, unique):
|
||||
response = WAITING_FOR_RESPONSE
|
||||
payload['unique'] = unique
|
||||
while response in SPECIALS:
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", payload)
|
||||
response = wait(timeout, uid)
|
||||
response = wait(timeout, uid, unique)
|
||||
if response == CANCEL:
|
||||
raise InterruptProcessingException()
|
||||
if Message.unique != unique:
|
||||
print("Mismatched uniques...")
|
||||
return response
|
||||
|
||||
def mask_to_image(mask:torch.Tensor):
|
||||
@@ -60,10 +63,17 @@ def mask_to_image(mask:torch.Tensor):
|
||||
|
||||
class Message:
|
||||
data:str = None
|
||||
unique:str = None
|
||||
@classmethod
|
||||
def setdata(cls, v, comment):
|
||||
#print(f"Message.data set to {v} (${comment})")
|
||||
cls.data = v
|
||||
if v:
|
||||
chop = v.split('!unique!')
|
||||
cls.data = chop[0]
|
||||
cls.unique = chop[1] if len(chop)>1 else None
|
||||
else:
|
||||
cls.data = None
|
||||
cls.unique = None
|
||||
|
||||
HIDDEN = {
|
||||
"prompt": "PROMPT",
|
||||
@@ -87,6 +97,7 @@ class ImageFilter(PreviewImage):
|
||||
"images" : ("IMAGE", ),
|
||||
"timeout": ("INT", {"default": 600, "tooltip": "Timeout in seconds."}),
|
||||
"ontimeout": (["send none", "send all", "send first", "send last"], {}),
|
||||
"node_identifier": ("INT", {"default":0, "max":99999999}),
|
||||
},
|
||||
"optional": {
|
||||
"latents" : ("LATENT", {"tooltip": "Optional - if provided, will be output"}),
|
||||
@@ -104,7 +115,7 @@ class ImageFilter(PreviewImage):
|
||||
def IS_CHANGED(cls, pick_list, **kwargs):
|
||||
return pick_list or float("NaN")
|
||||
|
||||
def func(self, images, timeout, ontimeout, uid, tip="", extra1="", extra2="", extra3="", latents=None, masks=None, pick_list:str="", **kwargs):
|
||||
def func(self, images, timeout, ontimeout, uid, node_identifier, tip="", extra1="", extra2="", extra3="", latents=None, masks=None, pick_list:str="", **kwargs):
|
||||
e1, e2, e3 = extra1, extra2, extra3
|
||||
B = images.shape[0]
|
||||
|
||||
@@ -118,7 +129,7 @@ class ImageFilter(PreviewImage):
|
||||
urls:list[str] = self.save_images(images=images, **kwargs)['ui']['images']
|
||||
payload = {"uid": uid, "urls":urls, "allsame":all_the_same, "extras":[extra1, extra2, extra3], "tip":tip}
|
||||
|
||||
response = send_with_resend(payload, timeout, uid)
|
||||
response = send_with_resend(payload, timeout, uid, node_identifier)
|
||||
|
||||
if response:
|
||||
response, e1, e2, e3 = response.split("|||")
|
||||
@@ -151,7 +162,7 @@ class TextImageFilterWithExtras(PreviewImage):
|
||||
"image" : ("IMAGE", ),
|
||||
"text" : ("STRING", {"default":""}),
|
||||
"timeout": ("INT", {"default": 600, "tooltip": "Timeout in seconds."}),
|
||||
},
|
||||
"node_identifier": ("INT", {"default":0, "max":99999999}), },
|
||||
"optional": {
|
||||
"mask" : ("MASK", {"tooltip": "Optional - if provided, will be overlaid on image"}),
|
||||
"tip" : ("STRING", {"default":"", "tooltip": "Optional - if provided, will be displayed in popup window"}),
|
||||
@@ -167,13 +178,13 @@ class TextImageFilterWithExtras(PreviewImage):
|
||||
def IS_CHANGED(cls, **kwargs):
|
||||
return float("NaN")
|
||||
|
||||
def func(self, image, text, timeout, uid, extra1="", extra2="", extra3="", mask=None, tip="", textareaheight=None, **kwargs):
|
||||
def func(self, image, text, timeout, uid, node_identifier, extra1="", extra2="", extra3="", mask=None, tip="", textareaheight=None, **kwargs):
|
||||
urls:list[str] = self.save_images(images=image, **kwargs)['ui']['images']
|
||||
payload = {"uid": uid, "urls":urls, "text":text, "extras":[extra1, extra2, extra3], "tip":tip}
|
||||
if textareaheight is not None: payload['textareaheight'] = textareaheight
|
||||
if mask is not None: payload['mask_urls'] = self.save_images(images=mask_to_image(mask), **kwargs)['ui']['images']
|
||||
|
||||
response = send_with_resend(payload, timeout, uid)
|
||||
response = send_with_resend(payload, timeout, uid, node_identifier)
|
||||
response = response.split("|||") if response else [text, extra1, extra2, extra3]
|
||||
|
||||
return (image, *response)
|
||||
@@ -193,7 +204,7 @@ class MaskImageFilter(PreviewImage, LoadImage):
|
||||
"image" : ("IMAGE", ),
|
||||
"timeout": ("INT", {"default": 600, "tooltip": "Timeout in seconds."}),
|
||||
"if_no_mask": (["cancel", "send blank"], {}),
|
||||
},
|
||||
"node_identifier": ("INT", {"default":0, "max":99999999}), },
|
||||
"optional": {
|
||||
"mask" : ("MASK", {"tooltip":"optional initial mask"})
|
||||
},
|
||||
@@ -207,7 +218,7 @@ class MaskImageFilter(PreviewImage, LoadImage):
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, **kwargs): return True
|
||||
|
||||
def func(self, image, timeout, uid, if_no_mask, mask=None, **kwargs):
|
||||
def func(self, image, timeout, uid, if_no_mask, node_identifier, mask=None, **kwargs):
|
||||
if mask is not None and mask.shape[:3] == image.shape[:3] and not torch.all(mask==0):
|
||||
saveable = torch.cat((image, mask.unsqueeze(-1)), dim=-1)
|
||||
else:
|
||||
@@ -215,7 +226,7 @@ class MaskImageFilter(PreviewImage, LoadImage):
|
||||
|
||||
urls:list[str] = self.save_images(images=saveable, **kwargs)['ui']['images']
|
||||
payload = {"uid": uid, "urls":urls, "maskedit":True}
|
||||
response = send_with_resend(payload, timeout, uid)
|
||||
response = send_with_resend(payload, timeout, uid, node_identifier)
|
||||
|
||||
if (response):
|
||||
try:
|
||||
|
||||
@@ -126,6 +126,22 @@
|
||||
|
||||
}
|
||||
|
||||
.cg_popup .zoomed_number {
|
||||
position: absolute;
|
||||
top: 40%;
|
||||
left: 30px;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
z-index: 110000;
|
||||
background-color: rgba(128, 128, 128, 0.5);
|
||||
/*text-align: center;*/
|
||||
}
|
||||
|
||||
.cg_popup .zoomed.highlighted .zoomed_image {
|
||||
border: 3px solid green;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.cg_popup .buttons button {
|
||||
margin: 5px 0 5px 0;
|
||||
padding: 4px;
|
||||
|
||||
+24
-1
@@ -3,6 +3,9 @@ import { api } from "../../scripts/api.js";
|
||||
|
||||
import { create } from "./utils.js";
|
||||
import { popup } from "./popup.js";
|
||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||
|
||||
const FILTER_TYPES = ["Image Filter","Text Image Filter","Text Image Filter with Extras","Mask Image Filter"]
|
||||
|
||||
app.registerExtension({
|
||||
name: "cg.image_filter",
|
||||
@@ -59,5 +62,25 @@ app.registerExtension({
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (FILTER_TYPES.includes(nodeType.comfyClass )) {
|
||||
nodeType.prototype.choose_id = function() {
|
||||
const v = Math.floor(Math.random() * 1000000)
|
||||
this.widgets.find((n)=>n.name=='node_identifier').value = v
|
||||
console.log(`Chose ${v}`)
|
||||
}
|
||||
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
const idx = this.widgets.findIndex((n)=>n.name=='node_identifier')
|
||||
const old_widget = (idx>=0) ? this.widgets.splice(idx,1)[0] : null
|
||||
const new_widget = ComfyWidgets["INT"](this, "node_identifier", ["INT", { "default":0 }], app).widget
|
||||
new_widget.label = new_widget.name
|
||||
new_widget.type = 'hidden'
|
||||
|
||||
this.choose_id()
|
||||
return onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
})
|
||||
+62
-16
@@ -43,6 +43,10 @@ class State {
|
||||
if (value) item.classList.remove('hidden')
|
||||
else item.classList.add('hidden')
|
||||
}
|
||||
static highlighted(item, value) {
|
||||
if (value) item.classList.add('highlighted')
|
||||
else item.classList.remove('highlighted')
|
||||
}
|
||||
|
||||
static render(popup) {
|
||||
const state = popup.state
|
||||
@@ -53,6 +57,12 @@ class State {
|
||||
State.visible(popup.text_edit, state==State.TEXT)
|
||||
State.visible(popup.send_button, true /*!app.ui.settings.getSettingValue("ImageFilter.ClickSends")*/)
|
||||
|
||||
if (state==State.ZOOMED) {
|
||||
const img_index = popup.zoomed_image_holder.image_index
|
||||
State.highlighted(popup.zoomed, popup.picked.has(`${img_index}`))
|
||||
popup.zoomed_number.innerHTML = `${img_index+1}/${popup.n_images}`
|
||||
}
|
||||
|
||||
if (document.getElementById('maskEditor') && state!=State.MASK) {
|
||||
document.getElementById('maskEditor').style.display = 'none'
|
||||
}
|
||||
@@ -70,6 +80,7 @@ class Popup extends HTMLSpanElement {
|
||||
this.overlaygrid = create('span', 'grid overlaygrid', this)
|
||||
this.zoomed = create('span', 'zoomed', this)
|
||||
this.zoomed_image = create('img', 'zoomed_image', this.zoomed)
|
||||
this.zoomed_number = create('span', 'zoomed_number', this.zoomed)
|
||||
this.text_edit = create('textarea', 'text_edit', this)
|
||||
this.title_bar = create('span', 'title', this)
|
||||
this.buttons = create('span', 'buttons', this)
|
||||
@@ -93,7 +104,7 @@ class Popup extends HTMLSpanElement {
|
||||
this.cancel_button.addEventListener('click', this.send_cancel.bind(this) )
|
||||
this.counter_reset_button.addEventListener('click', this.request_reset.bind(this) )
|
||||
|
||||
document.addEventListener("keypress", this.on_key_press.bind(this))
|
||||
document.addEventListener("keydown", this.on_key_down.bind(this))
|
||||
|
||||
document.body.appendChild(this)
|
||||
this.last_response_sent = 0
|
||||
@@ -114,6 +125,7 @@ class Popup extends HTMLSpanElement {
|
||||
|
||||
try {
|
||||
const body = new FormData();
|
||||
msg = `${msg}!unique!${this.unique}`
|
||||
body.append('response', msg);
|
||||
api.fetchApi("/cg-image-filter-message", { method: "POST", body, });
|
||||
Log.message_out(msg)
|
||||
@@ -130,6 +142,7 @@ class Popup extends HTMLSpanElement {
|
||||
|
||||
close() {
|
||||
this.state = State.INACTIVE
|
||||
this.node.choose_id()
|
||||
State.render(this)
|
||||
}
|
||||
|
||||
@@ -153,6 +166,22 @@ class Popup extends HTMLSpanElement {
|
||||
|
||||
_handle_message(message, use_saved) {
|
||||
|
||||
const uid = message.detail.uid
|
||||
const unique = message.detail.unique
|
||||
const node = app.graph._nodes_by_id[uid]
|
||||
|
||||
if (!node) {
|
||||
console.log(`Message was for ${uid} which doesn't exist`)
|
||||
return
|
||||
}
|
||||
|
||||
if (node.widgets?.find((n)=>n.label=='node_identifier')?.value != message.detail.unique) {
|
||||
console.log(`Message unique id wasn't mine`)
|
||||
return
|
||||
}
|
||||
|
||||
this.node = node
|
||||
|
||||
this.allsame = message.detail.allsame || false
|
||||
|
||||
if (this.state==State.INACTIVE && message.detail.urls && app.ui.settings.getSettingValue("ImageFilter.SmallWindow") && !use_saved && !this.autosend()) {
|
||||
@@ -170,16 +199,9 @@ class Popup extends HTMLSpanElement {
|
||||
try {
|
||||
const detail = message.detail
|
||||
|
||||
if (detail.uid) {
|
||||
const node = app.graph._nodes_by_id[detail.uid]
|
||||
if (!node) return `Node ${detail.uid} not found`
|
||||
if (!EXTENSION_NODES.includes(node.type)) return `Node ${detail.uid} is not an image filter node`
|
||||
} else {
|
||||
return `No uid in message`
|
||||
}
|
||||
|
||||
if (detail.tick) {
|
||||
this.counter_text.innerText = `${detail.tick}s`
|
||||
if (this.state==State.INACTIVE) this.request_reset()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -300,16 +322,40 @@ class Popup extends HTMLSpanElement {
|
||||
this.redraw()
|
||||
}
|
||||
|
||||
on_key_press(e) {
|
||||
if (e.key==' ') {
|
||||
if (this.state==State.ZOOMED) {
|
||||
this.state = State.FILTER
|
||||
} else if (this.mouse_is_over) {
|
||||
on_key_down(e) {
|
||||
var used_keypress = false;
|
||||
|
||||
if (this.state==State.FILTER) {
|
||||
if (e.key==' ' && this.mouse_is_over) {
|
||||
this.state = State.ZOOMED
|
||||
this.zoomed_image.src = this.mouse_is_over.src
|
||||
this.zoomed_image_holder = this.mouse_is_over
|
||||
this.on_mouse_out(this.mouse_is_over)
|
||||
used_keypress = true
|
||||
}
|
||||
State.render(this)
|
||||
} else if (this.state==State.ZOOMED) {
|
||||
if (e.key==' ') {
|
||||
this.state = State.FILTER
|
||||
this.zoomed_image_holder = null
|
||||
used_keypress = true
|
||||
} else if (e.key=='ArrowUp') {
|
||||
const fake_event = { target:this.zoomed_image_holder}
|
||||
this.on_click(fake_event)
|
||||
} else if (e.key=='ArrowDown') {
|
||||
// select or unselect
|
||||
} else if (e.key=='ArrowRight') {
|
||||
this.zoomed_image_holder = this.zoomed_image_holder.nextSibling || this.zoomed_image_holder.parentNode.firstChild
|
||||
used_keypress = true
|
||||
} else if (e.key=='ArrowLeft') {
|
||||
this.zoomed_image_holder = this.zoomed_image_holder.previousSibling || this.zoomed_image_holder.parentNode.lastChild
|
||||
used_keypress = true
|
||||
}
|
||||
}
|
||||
|
||||
if (used_keypress) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
if (this.zoomed_image_holder) this.zoomed_image.src = this.zoomed_image_holder.src
|
||||
State.render(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user