merge tianlang0704/multiuser
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
<<<<<<< HEAD:sdppp_python/comfy_nodes_legacy.py
|
||||
import numpy as np
|
||||
from nodes import LoadImage
|
||||
from PIL import Image
|
||||
@@ -198,3 +199,168 @@ def define_comfyui_nodes_legacy(sdppp):
|
||||
'ImageTimesOpacity': ImageTimesOpacity,
|
||||
'MaskTimesOpacity': MaskTimesOpacity,
|
||||
}
|
||||
=======
|
||||
import numpy as np
|
||||
import time
|
||||
from nodes import LoadImage
|
||||
from .apis import cache_images
|
||||
from .photoshop_manager import PhotoshopManager
|
||||
from server import PromptServer
|
||||
prompt_server = PromptServer.instance
|
||||
|
||||
def define_comfyui_nodes(sdppp):
|
||||
def call_async_func_in_server_thread(coro, dontwait = False):
|
||||
handle = {
|
||||
'done': False,
|
||||
'result': None
|
||||
}
|
||||
loop = sdppp.loop
|
||||
async def do_call():
|
||||
handle['result'] = await coro
|
||||
handle['done'] = True
|
||||
loop.call_soon(lambda: loop.create_task(do_call()))
|
||||
|
||||
if not dontwait:
|
||||
while not handle['done']:
|
||||
pass
|
||||
return handle['result']
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
class GetImageFromPhotoshopLayerNode:
|
||||
# !!!!do not validate because there's no client_id when validating, so we validate manually before execution!!!!
|
||||
# @classmethod
|
||||
# def VALIDATE_INPUTS():
|
||||
# pass
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(self, layer, use_layer_bounds):
|
||||
photoshopInstance = PhotoshopManager.instance().instance_from_client_id(prompt_server.client_id)
|
||||
if not photoshopInstance:
|
||||
return np.random.rand()
|
||||
else:
|
||||
id = photoshopInstance.layer_name_to_id(layer)
|
||||
bounds_id = photoshopInstance.layer_name_to_id(use_layer_bounds, id)
|
||||
start = time.time()
|
||||
is_changed, history_state_id = call_async_func_in_server_thread(photoshopInstance.check_layer_bounds_combo_changed(id, bounds_id))
|
||||
if is_changed and history_state_id is None:
|
||||
return photoshopInstance.update_comfyui_last_value(id, bounds_id, np.random.rand())
|
||||
return photoshopInstance.update_comfyui_last_value(id, bounds_id, history_state_id)
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
layer_strs = []
|
||||
bounds_strs = []
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"layer": (layer_strs, {"default": layer_strs[0] if len(layer_strs) > 0 else None}),
|
||||
"use_layer_bounds": (bounds_strs, {"default": bounds_strs[0] if len(bounds_strs) > 0 else None}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE", "MASK", "FLOAT")
|
||||
RETURN_NAMES = ("image_out", "mask_out", "layer_opacity")
|
||||
FUNCTION = "get_image"
|
||||
CATEGORY = "Photoshop"
|
||||
|
||||
def get_image(self, layer, use_layer_bounds):
|
||||
photoshopInstance = PhotoshopManager.instance().instance_from_client_id(prompt_server.client_id)
|
||||
if not photoshopInstance:
|
||||
raise ValueError('Photoshop is not connected')
|
||||
layer_strs = photoshopInstance.get_base_layers()
|
||||
if layer not in layer_strs:
|
||||
raise ValueError(f"Layer {layer} not found in Photoshop")
|
||||
bounds_strs = photoshopInstance.get_bounds_layers()
|
||||
if use_layer_bounds not in bounds_strs:
|
||||
raise ValueError(f"Layer {use_layer_bounds} not found in Photoshop")
|
||||
|
||||
id = photoshopInstance.layer_name_to_id(layer)
|
||||
bounds_id = photoshopInstance.layer_name_to_id(use_layer_bounds, id)
|
||||
|
||||
start = time.time()
|
||||
image_id, layer_opacity = call_async_func_in_server_thread(photoshopInstance.get_image(layer_id=id, bounds_id=bounds_id))
|
||||
|
||||
loadImage = LoadImage()
|
||||
(output_image, output_mask) = loadImage.load_image(image_id)
|
||||
return (output_image, output_mask, layer_opacity / 100)
|
||||
|
||||
|
||||
class SendImageToPhotoshopLayerNode:
|
||||
# !!!!do not validate because there's no client_id when validating, so we validate manually before execution!!!!
|
||||
# @classmethod
|
||||
# def VALIDATE_INPUTS():
|
||||
# pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
layer_strs = []
|
||||
|
||||
return {
|
||||
"required": {},
|
||||
"optional": {
|
||||
"images": ("IMAGE", ),
|
||||
"layer": (layer_strs, {"default": layer_strs[0] if len(layer_strs) > 0 else None}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
FUNCTION = "send_image"
|
||||
CATEGORY = "Photoshop"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
def send_image(self, images, layer):
|
||||
photoshopInstance = PhotoshopManager.instance().instance_from_client_id(prompt_server.client_id)
|
||||
if not photoshopInstance:
|
||||
raise ValueError('Photoshop is not connected')
|
||||
|
||||
ret = cache_images(images)
|
||||
layer_id = photoshopInstance.layer_name_to_id(layer)
|
||||
call_async_func_in_server_thread(photoshopInstance.send_images(image_ids=ret, layer_id=layer_id), True)
|
||||
|
||||
return (None,)
|
||||
|
||||
class ImageTimesOpacity:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"images": ("IMAGE", ),
|
||||
"opacity": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 1.0, "step": 0.01}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "image_times_opacity"
|
||||
CATEGORY = "Photoshop"
|
||||
|
||||
def image_times_opacity(self, images, opacity):
|
||||
image_out = images * opacity
|
||||
return (image_out,)
|
||||
|
||||
class MaskTimesOpacity:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"masks": ("MASK", ),
|
||||
"opacity": ("FLOAT", {"default": 1.0, "min": 0.01, "max": 1.0, "step": 0.01}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK",)
|
||||
FUNCTION = "mask_times_opacity"
|
||||
CATEGORY = "Photoshop"
|
||||
|
||||
def mask_times_opacity(self, masks, opacity):
|
||||
mask_out = masks * opacity
|
||||
return (mask_out,)
|
||||
|
||||
return {
|
||||
'GetImageFromPhotoshopLayerNode': GetImageFromPhotoshopLayerNode,
|
||||
'SendImageToPhotoshopLayerNode': SendImageToPhotoshopLayerNode,
|
||||
'ImageTimesOpacity': ImageTimesOpacity,
|
||||
'MaskTimesOpacity': MaskTimesOpacity,
|
||||
}
|
||||
>>>>>>> tl/develop_sio_multi_user:comfy/nodes.py
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import asyncio
|
||||
from .photoshop_instance import PhotoshopInstance
|
||||
DEFAULT_ID = 0
|
||||
|
||||
class PhotoshopManager:
|
||||
_instance = None
|
||||
|
||||
@classmethod
|
||||
def instance(cls) -> 'PhotoshopManager':
|
||||
if cls._instance is None:
|
||||
cls._instance = PhotoshopManager()
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
# client id info
|
||||
self.ip_to_client_id_list = {}
|
||||
self.client_id_to_userid = {}
|
||||
# ps instance info
|
||||
self.ip_to_ps_instance_list = {}
|
||||
# final match
|
||||
self.client_id_to_ps_instance = {}
|
||||
|
||||
# get instance from client information, match new instance if not exist
|
||||
def instance_from_client_info(self, ip, client_id, user_id) -> PhotoshopInstance:
|
||||
if not ip: return None
|
||||
if not client_id: user_id = 0
|
||||
if not user_id: user_id = 0
|
||||
instance = self.instance_from_client_id(client_id)
|
||||
if instance is not None:
|
||||
return instance
|
||||
self._add_new_client(ip, client_id, user_id)
|
||||
self._client_match_ps(ip, client_id, user_id)
|
||||
return self.instance_from_client_id(client_id)
|
||||
|
||||
# get instance from client id
|
||||
def instance_from_client_id(self, client_id) -> PhotoshopInstance:
|
||||
return self.client_id_to_ps_instance.get(client_id, None)
|
||||
|
||||
# create new instance and record
|
||||
async def new_ps_instance(self, sdppp, sid, ip, user_id) -> PhotoshopInstance:
|
||||
if not ip: return None
|
||||
if not user_id: user_id = 0
|
||||
# create new instance
|
||||
instance = PhotoshopInstance(sdppp, sid, user_id)
|
||||
instance.on_destroy = self._on_instance_destroy
|
||||
# record new instance
|
||||
await self._add_new_ps_instance(ip, instance)
|
||||
return instance
|
||||
|
||||
#------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
# callback when instance is destroyed and remove from record
|
||||
def _on_instance_destroy(self, instance):
|
||||
self._remove_instance(instance)
|
||||
|
||||
# record new ps instance for matching
|
||||
async def _add_new_ps_instance(self, ip, instance):
|
||||
# find old one and destroy
|
||||
same_ip_list = self.ip_to_ps_instance_list.get(ip, [])
|
||||
for check_instance in same_ip_list:
|
||||
if check_instance.uid == instance.uid:
|
||||
await check_instance.destroy()
|
||||
# record ip to instance
|
||||
ps_instance_list = self.ip_to_ps_instance_list.get(ip, [])
|
||||
if instance not in ps_instance_list:
|
||||
ps_instance_list.append(instance)
|
||||
self.ip_to_ps_instance_list[ip] = ps_instance_list
|
||||
|
||||
# record new client id
|
||||
def _add_new_client(self, ip, client_id, user_id):
|
||||
# record client id, if exist, do nothing
|
||||
client_id_list = self.ip_to_client_id_list.get(ip, [])
|
||||
if client_id in client_id_list:
|
||||
return
|
||||
client_id_list.append(client_id)
|
||||
self.ip_to_client_id_list[ip] = client_id_list
|
||||
# record user_id
|
||||
self.client_id_to_userid[client_id] = user_id
|
||||
|
||||
# match client id to ps instance for prompt use
|
||||
# there are several ways to match:
|
||||
# 1. one to one match(local network most likely): same ip client and ps instance, assign to it
|
||||
# 2. multiple ps instance under the same ip(remote service most likely), check user id
|
||||
def _client_match_ps(self, ip, client_id, user_id):
|
||||
# check existing pair
|
||||
instance = self.client_id_to_ps_instance.get(client_id, None)
|
||||
if instance is not None:
|
||||
return
|
||||
# check same ip ps instance
|
||||
ps_instance_list = self.ip_to_ps_instance_list.get(ip, [])
|
||||
count = len(ps_instance_list)
|
||||
if count <= 0:
|
||||
return
|
||||
# if only one same ip ps instance, assign client id
|
||||
if count == 1:
|
||||
self.client_id_to_ps_instance[client_id] = ps_instance_list[0]
|
||||
return
|
||||
# if more than one same ip ps instance, check same user_id, same ip + same user id = same ps instance
|
||||
ps_user_id_list = [instance.uid for instance in ps_instance_list]
|
||||
try:
|
||||
check_index = ps_user_id_list.index(user_id)
|
||||
except:
|
||||
check_index = None
|
||||
if check_index is None:
|
||||
return
|
||||
self.client_id_to_ps_instance[client_id] = ps_instance_list[check_index]
|
||||
|
||||
def _remove_instance(self, instance):
|
||||
# remove id from ip to instance list
|
||||
for ip, ps_instance_list in self.ip_to_ps_instance_list.items():
|
||||
if instance in ps_instance_list:
|
||||
ps_instance_list.remove(instance)
|
||||
# remove instance from client id to instance
|
||||
for client_id in list(self.client_id_to_ps_instance.keys()):
|
||||
check_instance = self.client_id_to_ps_instance.get(client_id, None)
|
||||
if check_instance == instance:
|
||||
self.client_id_to_ps_instance.pop(client_id, None)
|
||||
Reference in New Issue
Block a user