125
This commit is contained in:
+8
-7
@@ -9,7 +9,7 @@ HIDDEN = {
|
||||
"prompt": "PROMPT",
|
||||
"extra_pnginfo": "EXTRA_PNGINFO",
|
||||
"uid":"UNIQUE_ID",
|
||||
"node_identifier": "NID",
|
||||
|
||||
}
|
||||
|
||||
class ImageFilter(PreviewImage):
|
||||
@@ -38,6 +38,7 @@ class ImageFilter(PreviewImage):
|
||||
"pick_list_start" : ("INT", {"default":0, "tooltip":"The number used in pick_list for the first image"}),
|
||||
"pick_list" : ("STRING", {"default":"", "tooltip":"If a comma separated list of integers is provided, the images with these indices will be selected automatically."}),
|
||||
"video_frames" : ("INT", {"default":1, "min":1, "tooltip": "treat each block of n images as a video"}),
|
||||
"graph_id": ("STRING", {"default":""}),
|
||||
},
|
||||
"hidden": HIDDEN,
|
||||
}
|
||||
@@ -46,7 +47,7 @@ class ImageFilter(PreviewImage):
|
||||
def IS_CHANGED(cls, pick_list, **kwargs):
|
||||
return pick_list or float("NaN")
|
||||
|
||||
def func(self, images, timeout, ontimeout, uid, node_identifier, tip="", extra1="", extra2="", extra3="", latents=None, masks=None, pick_list_start:int=0, pick_list:str="", video_frames:int=1, **kwargs):
|
||||
def func(self, images, timeout, ontimeout, uid, graph_id, tip="", extra1="", extra2="", extra3="", latents=None, masks=None, pick_list_start:int=0, pick_list:str="", video_frames:int=1, **kwargs):
|
||||
e1, e2, e3 = extra1, extra2, extra3
|
||||
B = images.shape[0]
|
||||
|
||||
@@ -63,7 +64,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, "video_frames":video_frames}
|
||||
|
||||
response:Response = send_and_wait(payload, timeout, uid, node_identifier)
|
||||
response:Response = send_and_wait(payload, timeout, uid, graph_id)
|
||||
|
||||
if isinstance(response, TimeoutResponse):
|
||||
if ontimeout=='send none': images_to_return = []
|
||||
@@ -118,13 +119,13 @@ class TextImageFilterWithExtras(PreviewImage):
|
||||
def IS_CHANGED(cls, **kwargs):
|
||||
return float("NaN")
|
||||
|
||||
def func(self, image, text, timeout, uid, node_identifier, extra1="", extra2="", extra3="", mask=None, tip="", textareaheight=None, **kwargs):
|
||||
def func(self, image, text, timeout, uid, graph_id, 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_and_wait(payload, timeout, uid, node_identifier)
|
||||
response = send_and_wait(payload, timeout, uid, graph_id)
|
||||
if isinstance(response, TimeoutResponse):
|
||||
return (image, text, extra1, extra2, extra3)
|
||||
|
||||
@@ -165,7 +166,7 @@ class MaskImageFilter(PreviewImage, LoadImage):
|
||||
@classmethod
|
||||
def VALIDATE_INPUTS(cls, *args, **kwargs): return True
|
||||
|
||||
def func(self, image, timeout, uid, if_no_mask, node_identifier, mask=None, extra1="", extra2="", extra3="", tip="", **kwargs):
|
||||
def func(self, image, timeout, uid, if_no_mask, graph_id, mask=None, extra1="", extra2="", extra3="", tip="", **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:
|
||||
@@ -173,7 +174,7 @@ class MaskImageFilter(PreviewImage, LoadImage):
|
||||
|
||||
urls:list[dict[str,str]] = self.save_images(images=saveable, **kwargs)['ui']['images']
|
||||
payload = {"uid": uid, "urls":urls, "maskedit":True, "extras":[extra1, extra2, extra3], "tip":tip}
|
||||
response = send_and_wait(payload, timeout, uid, node_identifier)
|
||||
response = send_and_wait(payload, timeout, uid, graph_id)
|
||||
|
||||
if (response.masked_image):
|
||||
try:
|
||||
|
||||
+13
-13
@@ -27,11 +27,11 @@ class RequestResponse(Response): pass
|
||||
|
||||
class MessageState:
|
||||
_latest:'Optional[MessageState]' = None
|
||||
unique_expected = None
|
||||
graph_id_expected = None
|
||||
|
||||
def __init__(self, data:dict|str={}):
|
||||
data_dict:dict = data if isinstance(data,dict) else json.loads(data)
|
||||
self.unique:str = data_dict.pop('unique', None)
|
||||
self.graph_id:str = data_dict.pop('graph_id', None)
|
||||
self.special:Optional[str] = data_dict.pop('special',None)
|
||||
self.response:Response = Response(**data_dict)
|
||||
|
||||
@@ -51,9 +51,9 @@ class MessageState:
|
||||
def request_state(cls): return MessageState(data={'special':REQUEST_RESHOW})
|
||||
|
||||
@classmethod
|
||||
def start_waiting(cls, unique):
|
||||
def start_waiting(cls, graph_id):
|
||||
cls._latest = cls.waiting_state()
|
||||
cls.unique_expected = unique
|
||||
cls.graph_id_expected = graph_id
|
||||
|
||||
@classmethod
|
||||
def get_response(cls) -> Response:
|
||||
@@ -85,7 +85,7 @@ async def cg_image_filter_message(request):
|
||||
response = post.get("response")
|
||||
message = MessageState(response)
|
||||
|
||||
if str(MessageState.unique_expected)==str(message.unique):
|
||||
if str(MessageState.graph_id_expected)==str(message.graph_id):
|
||||
if (MessageState.waiting()):
|
||||
MessageState.set_latest(message)
|
||||
else:
|
||||
@@ -95,26 +95,26 @@ async def cg_image_filter_message(request):
|
||||
|
||||
return web.json_response({})
|
||||
|
||||
def wait_for_response(secs, uid, unique) -> Response:
|
||||
MessageState.start_waiting(unique)
|
||||
def wait_for_response(secs, uid, graph_id) -> Response:
|
||||
MessageState.start_waiting(graph_id)
|
||||
try:
|
||||
end_time = time.monotonic() + secs
|
||||
while(time.monotonic() < end_time and MessageState.waiting()):
|
||||
throw_exception_if_processing_interrupted()
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", {"tick": int(end_time - time.monotonic()), "uid": uid, "unique":unique})
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", {"tick": int(end_time - time.monotonic()), "uid": uid, "graph_id":graph_id})
|
||||
time.sleep(0.5)
|
||||
if MessageState.waiting():
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", {"timeout": True, "uid": uid, "unique":unique})
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", {"timeout": True, "uid": uid, "graph_id":graph_id})
|
||||
return MessageState.get_response()
|
||||
finally: MessageState.stop_waiting()
|
||||
|
||||
def send_and_wait(payload, timeout, uid, unique) -> Response:
|
||||
payload['uid'] = uid
|
||||
payload['unique'] = unique
|
||||
def send_and_wait(payload, timeout, uid, graph_id) -> Response:
|
||||
payload['uid'] = uid
|
||||
payload['graph_id'] = graph_id
|
||||
|
||||
while True:
|
||||
PromptServer.instance.send_sync("cg-image-filter-images", payload)
|
||||
r = wait_for_response(timeout, uid, unique)
|
||||
r = wait_for_response(timeout, uid, graph_id)
|
||||
if isinstance(r,CancelledResponse): raise InterruptProcessingException()
|
||||
if (not isinstance(r, RequestResponse)): return r
|
||||
|
||||
+24
-22
@@ -3,8 +3,8 @@ import { api } from "../../scripts/api.js";
|
||||
|
||||
import { create } from "./utils.js";
|
||||
import { popup } from "./popup.js";
|
||||
import { ComfyWidgets } from "../../scripts/widgets.js";
|
||||
import { unique_to_tab } from "./weak_map.js";
|
||||
import { graph_id_to_tab } from "./weak_map.js";
|
||||
import { Log } from "./log.js";
|
||||
|
||||
const FILTER_TYPES = ["Image Filter","Text Image Filter","Text Image Filter with Extras","Mask Image Filter"]
|
||||
|
||||
@@ -107,32 +107,34 @@ app.registerExtension({
|
||||
if (FILTER_TYPES.includes(nodeType.comfyClass )) {
|
||||
const onNodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
this._ni_widget = this.widgets.find((n)=>n.name=='node_identifier')
|
||||
if (!(this._ni_widget)) {
|
||||
this._ni_widget = ComfyWidgets["STRING"](this, "node_identifier", ["STRING", { "default":"" }], app).widget
|
||||
}
|
||||
this._ni_widget.hidden = true
|
||||
this._ni_widget.computeSize = () => [0,0]
|
||||
|
||||
|
||||
|
||||
set_graph_id_widget(this)
|
||||
return onNodeCreated ? onNodeCreated.apply(this, arguments) : undefined;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
afterConfigureGraph() {
|
||||
setTimeout( ()=> {
|
||||
const checked = document.getElementsByClassName('p-togglebutton-checked')[0]
|
||||
app.graph.nodes.forEach( (node)=> {
|
||||
const unique_id_widget = node.widgets?.find((n)=>n.name=='node_identifier')
|
||||
if (unique_id_widget) {
|
||||
unique_id_widget.value = `${node.graph.id}:${node.id}`
|
||||
unique_to_tab.set(unique_id_widget.value, checked )
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
setTimeout( ()=> { app.graph.nodes.forEach( set_graph_id_widget ) }, 1000 )
|
||||
link_to_tab(3)
|
||||
}
|
||||
})
|
||||
|
||||
function set_graph_id_widget(node) {
|
||||
const graph_id_widget = node.widgets?.find((n)=>n.name=='graph_id')
|
||||
if (graph_id_widget) {
|
||||
graph_id_widget.hidden = true
|
||||
graph_id_widget.value = `${app.graph.id}` // app.graph.id is unique per tab, regardless of subgraph
|
||||
graph_id_widget.computeSize = () => [0,0]
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
function link_to_tab(tries) {
|
||||
const tab = document.getElementsByClassName('p-togglebutton-checked')[0]
|
||||
if (tab && app.graph.id) {
|
||||
graph_id_to_tab.set(app.graph.id, tab )
|
||||
} else if (tries>0) {
|
||||
setTimeout( ()=> { link_to_tab(tries-1) }, 500 )
|
||||
} else {
|
||||
Log.log(`cg-image-filter: could not link graph to tab`)
|
||||
}
|
||||
}
|
||||
+49
-50
@@ -5,7 +5,7 @@ import { mask_editor_listen_for_cancel, mask_editor_showing, hide_mask_editor, p
|
||||
import { Log } from "./log.js";
|
||||
import { create } from "./utils.js";
|
||||
import { FloatingWindow } from "./floating_window.js";
|
||||
import { unique_to_tab } from "./weak_map.js";
|
||||
import { graph_id_to_tab } from "./weak_map.js";
|
||||
|
||||
//const EXTENSION_NODES = ["Image Filter", "Text Image Filter", "Mask Image Filter", "Text Image Filter with Extras",]
|
||||
const POPUP_NODES = ["Image Filter", "Text Image Filter", "Text Image Filter with Extras",]
|
||||
@@ -169,7 +169,7 @@ class Popup extends HTMLElement {
|
||||
special (int)
|
||||
masked_image (string)
|
||||
*extras (list of strings)
|
||||
*unique (string)
|
||||
*graph_id (string)
|
||||
(*) are added
|
||||
*/
|
||||
if (Date.now()-this.last_response_sent < 1000) {
|
||||
@@ -177,13 +177,7 @@ class Popup extends HTMLElement {
|
||||
return
|
||||
}
|
||||
|
||||
const unique = this.node?._ni_widget?.value
|
||||
if (!unique) {
|
||||
if (this.node) Log.error(`Node ${this.node.id} has no _ni_widget when trying to send ${msg}`)
|
||||
else Log.error(`No node when trying to send ${msg}`)
|
||||
return
|
||||
}
|
||||
msg.unique = `${unique}`
|
||||
msg.graph_id = `${app.graph.id}`
|
||||
|
||||
if (!msg.special) {
|
||||
if (this.n_extras>0) {
|
||||
@@ -230,13 +224,13 @@ class Popup extends HTMLElement {
|
||||
|
||||
handle_message(message) {
|
||||
Log.message_in(message)
|
||||
Log.log( this._handle_message(message, false) )
|
||||
this._handle_message(message, false)
|
||||
this.render()
|
||||
}
|
||||
|
||||
handle_deferred_message(e) {
|
||||
Log.message_in(this.saved_message, "(deferred)")
|
||||
Log.log( this._handle_message(this.saved_message, true) )
|
||||
this._handle_message(this.saved_message, true)
|
||||
this.render()
|
||||
}
|
||||
|
||||
@@ -267,24 +261,31 @@ class Popup extends HTMLElement {
|
||||
return node
|
||||
}
|
||||
|
||||
_flash_tab(graph_id) {
|
||||
const tab = graph_id_to_tab.get(graph_id)
|
||||
if (tab) {
|
||||
const element = tab.firstElementChild?.firstElementChild?.firstElementChild || tab
|
||||
if (this.tab_orginal_background===undefined) this.tab_orginal_background = element.style.backgroundColor
|
||||
element.style.backgroundColor = '#ffff0040'
|
||||
setTimeout( ()=>{ element.style.backgroundColor = this.tab_orginal_background }, 100 )
|
||||
}
|
||||
}
|
||||
|
||||
_handle_message(message, using_saved) {
|
||||
const detail = message.detail
|
||||
const uid = detail.uid
|
||||
const the_node = this.find_node(uid)
|
||||
const graph_id = message.detail.graph_id
|
||||
|
||||
if (graph_id != app.graph.id) {
|
||||
this._flash_tab(message.detail.graph_id)
|
||||
Log.detail(`Message for different tab`)
|
||||
return
|
||||
}
|
||||
|
||||
if (!the_node) Log.log(`No node found with uid ${uid}. Maybe it's been removed. Continuing with caution`)
|
||||
|
||||
if (this.node!=the_node) this.on_new_node(the_node)
|
||||
const tab = unique_to_tab.get(message.detail.unique)
|
||||
|
||||
if (!this.node || this.node._ni_widget?.value != message.detail.unique) {
|
||||
if (tab) {
|
||||
if (!tab.innerHTML.endsWith('! ')) tab.innerHTML += '! '
|
||||
}
|
||||
return console.log(`Message unique id wasn't mine`)
|
||||
} else {
|
||||
if (tab) {
|
||||
if (tab.innerHTML.endsWith('! ')) tab.innerHTML = tab.innerHTML.substring(0, tab.innerHTML.length-7)
|
||||
}
|
||||
}
|
||||
|
||||
if (detail.tick) {
|
||||
this.counter_text.innerText = `${detail.tick}s`
|
||||
@@ -294,12 +295,12 @@ class Popup extends HTMLElement {
|
||||
|
||||
if (detail.timeout) {
|
||||
this.close()
|
||||
return `Timeout`
|
||||
return Log.log(`Image Filter Timeout`)
|
||||
}
|
||||
|
||||
if (this.handling_message) return `Ignoring message because we're already handling a message`
|
||||
if (this.handling_message) return Log.detail(`Ignoring message because we're already handling a message`)
|
||||
|
||||
this.set_title(this.node.title ?? "Image Filter")
|
||||
this.set_title(this.node?.title ?? "Image Filter")
|
||||
this.allsame = detail.allsame || false
|
||||
if (detail.tip) this.tip_row.innerHTML = detail.tip.replace(/(?:\r\n|\r|\n)/g, '<br/>')
|
||||
else this.tip_row.innerHTML = ""
|
||||
@@ -326,8 +327,6 @@ class Popup extends HTMLElement {
|
||||
} finally { this.handling_message = false }
|
||||
}
|
||||
|
||||
|
||||
|
||||
window_not_showing(uid) {
|
||||
const node = this.find_node(uid)
|
||||
return (
|
||||
@@ -346,18 +345,21 @@ class Popup extends HTMLElement {
|
||||
}
|
||||
|
||||
handle_maskedit(detail) {
|
||||
this.state = State.MASK
|
||||
|
||||
//this.node = this.find_node(detail.uid)
|
||||
this.node.imgs = []
|
||||
detail.urls.forEach((url, i)=>{
|
||||
this.node.imgs.push( new Image() );
|
||||
this.node.imgs[i].src = api.apiURL( `/view?filename=${encodeURIComponent(url.filename)}&type=${url.type}&subfolder=${url.subfolder}`)
|
||||
})
|
||||
ComfyApp.copyToClipspace(this.node)
|
||||
ComfyApp.clipspace_return_node = this.node
|
||||
ComfyApp.open_maskeditor()
|
||||
this.seen_editor = false
|
||||
if (!this.node) {
|
||||
Log.log(`No node to handle maskedit - maybe it's been removed`)
|
||||
this.seen_editor = true
|
||||
} else {
|
||||
this.state = State.MASK
|
||||
this.node.imgs = []
|
||||
detail.urls.forEach((url, i)=>{
|
||||
this.node.imgs.push( new Image() );
|
||||
this.node.imgs[i].src = api.apiURL( `/view?filename=${encodeURIComponent(url.filename)}&type=${url.type}&subfolder=${url.subfolder}`)
|
||||
})
|
||||
ComfyApp.copyToClipspace(this.node)
|
||||
ComfyApp.clipspace_return_node = this.node
|
||||
ComfyApp.open_maskeditor()
|
||||
this.seen_editor = false
|
||||
}
|
||||
setTimeout(this.wait_while_mask_editing.bind(this), 200)
|
||||
}
|
||||
|
||||
@@ -401,31 +403,27 @@ class Popup extends HTMLElement {
|
||||
}
|
||||
|
||||
this.n_images = (this.video_frames<=1) ? detail.urls.length : Math.ceil(detail.urls.length / this.video_frames)
|
||||
|
||||
this.laidOut = -1
|
||||
|
||||
this.picked = new Set()
|
||||
if (this.n_images==1) this.picked.add('0')
|
||||
|
||||
this.grid.innerHTML = ''
|
||||
this.overlaygrid.innerHTML = ''
|
||||
var latestImage = null
|
||||
|
||||
var latestImage
|
||||
detail.urls.forEach((url, i)=>{
|
||||
console.log(url)
|
||||
Log.log(url)
|
||||
if (i%this.video_frames == 0) {
|
||||
const thisImage = create('img', null, this.grid, {src:get_full_url(url)})
|
||||
latestImage = thisImage
|
||||
latestImage = create('img', null, this.grid, {src:get_full_url(url)})
|
||||
latestImage.onload = this.layout.bind(this)
|
||||
latestImage.image_index = i/this.video_frames
|
||||
latestImage.addEventListener('mouseover', (e)=>this.on_mouse_enter(thisImage))
|
||||
latestImage.addEventListener('mouseout', (e)=>this.on_mouse_out(thisImage))
|
||||
latestImage.addEventListener('mouseover', (e)=>this.on_mouse_enter(latestImage))
|
||||
latestImage.addEventListener('mouseout', (e)=>this.on_mouse_out(latestImage))
|
||||
latestImage.frames = [get_full_url(url),]
|
||||
} else {
|
||||
latestImage.frames.push(get_full_url(url))
|
||||
}
|
||||
if (detail.mask_urls) { create('img', null, this.overlaygrid, {src:get_full_url(detail.mask_urls[i])})}
|
||||
|
||||
if (detail.mask_urls && this.video_frames==1) { create('img', null, this.overlaygrid, {src:get_full_url(detail.mask_urls[i])}) }
|
||||
})
|
||||
|
||||
this.layout()
|
||||
@@ -435,6 +433,7 @@ class Popup extends HTMLElement {
|
||||
setTimeout(this.advance_videos.bind(this), 1000)
|
||||
}
|
||||
|
||||
/* cooldown to prevent us catching a click that was intended for an element we are now covering */
|
||||
this.in_cooldown = true
|
||||
setTimeout(()=>{this.in_cooldown = false}, 500)
|
||||
|
||||
|
||||
+1
-1
@@ -13,4 +13,4 @@ class WeakMap {
|
||||
}
|
||||
}
|
||||
|
||||
export const unique_to_tab = new WeakMap()
|
||||
export const graph_id_to_tab = new WeakMap()
|
||||
Reference in New Issue
Block a user