Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b5c68751aa | ||
|
|
7780bfd671 | ||
|
|
a56970693a | ||
|
|
c8b24fe84b | ||
|
|
798aabf333 | ||
|
|
d38a7aa558 | ||
|
|
0bdf1e47a6 | ||
|
|
a44016e57c | ||
|
|
35a9351e53 | ||
|
|
c1b2112bb8 | ||
|
|
42a3dd261f | ||
|
|
e14487fab1 | ||
|
|
c16df1a473 | ||
|
|
9272844251 | ||
|
|
b30c1d19cc |
+3
-1
@@ -1,2 +1,4 @@
|
||||
__pycache__/
|
||||
https/
|
||||
https/
|
||||
nodes/config.json
|
||||
workflow/my_workflow.json
|
||||
@@ -1,6 +1,5 @@
|
||||
##
|
||||
In progress.
|
||||
!!
|
||||
|
||||

|
||||
|
||||
|
||||
@@ -61,6 +60,14 @@ pip3 install -r requirements.txt
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
> LoadImagesFromLocal
|
||||
|
||||

|
||||
|
||||
[workflow-4](./workflow/4-loadfromlocal-watcher-workflow.json)
|
||||
|
||||
> Consistency Decoder
|
||||
|
||||
[openai Consistency Decoder]( https://github.com/openai/consistencydecoder)
|
||||
@@ -86,6 +93,8 @@ Add edges to an image.
|
||||
#### Thanks:
|
||||
[ComfyUI-CLIPSeg](https://github.com/biegert/ComfyUI-CLIPSeg/tree/main)
|
||||
|
||||
#### discussions:
|
||||
[discussions](https://github.com/shadowcz007/comfyui-mixlab-nodes/discussions)
|
||||
|
||||
### TODO:
|
||||
- vector https://github.com/GeorgLegato/stable-diffusion-webui-vectorstudio
|
||||
|
||||
+128
-14
@@ -2,12 +2,17 @@
|
||||
import os
|
||||
import subprocess
|
||||
import importlib.util
|
||||
import sys
|
||||
import sys,json
|
||||
import urllib
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
python = sys.executable
|
||||
|
||||
|
||||
from server import PromptServer
|
||||
|
||||
try:
|
||||
import aiohttp
|
||||
from aiohttp import web
|
||||
@@ -36,7 +41,7 @@ def is_installed(package, package_overwrite=None):
|
||||
if result.returncode != 0:
|
||||
print(f"Couldn't install\nCommand: {command}\nError code: {result.returncode}")
|
||||
else:
|
||||
print('##pyOpenSSL OK')
|
||||
print(package+'## OK')
|
||||
|
||||
try:
|
||||
import OpenSSL
|
||||
@@ -48,6 +53,19 @@ except ImportError:
|
||||
is_installed('pyOpenSSL')
|
||||
sys.exit()
|
||||
|
||||
try:
|
||||
import watchdog
|
||||
except ImportError:
|
||||
print("Module 'watchdog' not installed. Please install it via:")
|
||||
print("pip install watchdog")
|
||||
print("or")
|
||||
print("pip install -r requirements.txt")
|
||||
is_installed('watchdog')
|
||||
sys.exit()
|
||||
|
||||
|
||||
current_path = os.path.abspath(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def create_key(key_p,crt_p):
|
||||
import OpenSSL
|
||||
@@ -79,23 +97,65 @@ def create_key(key_p,crt_p):
|
||||
f.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, certificate))
|
||||
return
|
||||
|
||||
def create_for_https():
|
||||
current_path = os.path.abspath(os.path.dirname(__file__))
|
||||
# print("#####path::", current_path)
|
||||
|
||||
# TODO 处理路径
|
||||
def create_for_https():
|
||||
# print("#####path::", current_path)
|
||||
https_key_path=os.path.join(current_path, "https")
|
||||
crt=os.path.join(https_key_path, "certificate.crt")
|
||||
key=os.path.join(https_key_path, "private.key")
|
||||
print("##https_key_path", crt,key)
|
||||
# print("##https_key_path", crt,key)
|
||||
if not os.path.exists(https_key_path):
|
||||
# 使用mkdir()方法创建新目录
|
||||
os.mkdir(https_key_path)
|
||||
if not os.path.exists(crt):
|
||||
create_key(key,crt)
|
||||
|
||||
print('https_key OK: ', crt,key)
|
||||
return (crt,key)
|
||||
|
||||
|
||||
# workflow
|
||||
def read_workflow_json_files(folder_path):
|
||||
json_files = []
|
||||
for filename in os.listdir(folder_path):
|
||||
if filename.endswith('.json'):
|
||||
json_files.append(filename)
|
||||
|
||||
data = []
|
||||
for file in json_files:
|
||||
file_path = os.path.join(folder_path, file)
|
||||
try:
|
||||
with open(file_path) as json_file:
|
||||
json_data = json.load(json_file)
|
||||
creation_time=datetime.datetime.fromtimestamp(os.path.getctime(file_path))
|
||||
numeric_timestamp = creation_time.timestamp()
|
||||
file_info = {
|
||||
'filename': file,
|
||||
'data': json_data,
|
||||
'date': numeric_timestamp
|
||||
}
|
||||
data.append(file_info)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sorted_data = sorted(data, key=lambda x: x['date'], reverse=True)
|
||||
return sorted_data
|
||||
|
||||
def get_workflows():
|
||||
# print("#####path::", current_path)
|
||||
workflow_path=os.path.join(current_path, "workflow")
|
||||
print('workflow_path: ',workflow_path)
|
||||
if not os.path.exists(workflow_path):
|
||||
# 使用mkdir()方法创建新目录
|
||||
os.mkdir(workflow_path)
|
||||
workflows=read_workflow_json_files(workflow_path)
|
||||
return workflows
|
||||
|
||||
def save_workflow_json(data):
|
||||
workflow_path=os.path.join(current_path, "workflow/my_workflow.json")
|
||||
with open(workflow_path, 'w') as file:
|
||||
json.dump(data, file)
|
||||
return workflow_path
|
||||
|
||||
# https
|
||||
async def new_start(self, address, port, verbose=True, call_on_start=None):
|
||||
runner = web.AppRunner(self.app, access_log=None)
|
||||
@@ -113,9 +173,10 @@ async def new_start(self, address, port, verbose=True, call_on_start=None):
|
||||
if address == '':
|
||||
address = '0.0.0.0'
|
||||
if verbose:
|
||||
print("Starting server\n")
|
||||
print("To see the GUI go to: http://{}:{}".format(address, port))
|
||||
print("To see the GUI go to: https://{}:{}".format(address, port+1))
|
||||
# print('\033[91mMixlab Nodes: \033[93mLoaded\033[0m')
|
||||
print("\033[93mStarting server\n")
|
||||
print("\033[93mTo see the GUI go to: http://{}:{}".format(address, port))
|
||||
print("\033[93mTo see the GUI go to: https://{}:{}\033[0m".format(address, port+1))
|
||||
if call_on_start is not None:
|
||||
call_on_start(address, port)
|
||||
|
||||
@@ -128,6 +189,57 @@ async def new_start(self, address, port, verbose=True, call_on_start=None):
|
||||
|
||||
PromptServer.start=new_start
|
||||
|
||||
# 创建路由表
|
||||
routes = web.RouteTableDef()
|
||||
|
||||
@routes.post('/mixlab')
|
||||
async def mixlab_hander(request):
|
||||
config=os.path.join(current_path, "nodes/config.json")
|
||||
data={}
|
||||
try:
|
||||
if os.path.exists(config):
|
||||
with open(config, 'r') as f:
|
||||
data = json.load(f)
|
||||
# print(data)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return web.json_response(data)
|
||||
|
||||
@routes.post('/mixlab/workflow')
|
||||
async def mixlab_workflow_hander(request):
|
||||
data = await request.json()
|
||||
result={}
|
||||
try:
|
||||
if 'task' in data:
|
||||
if data['task']=='save':
|
||||
file_path=save_workflow_json(data['data'])
|
||||
result={
|
||||
'status':'success',
|
||||
'file_path':file_path
|
||||
}
|
||||
elif data['task']=='list':
|
||||
result={
|
||||
'data':get_workflows(),
|
||||
'status':'success',
|
||||
}
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
return web.json_response(result)
|
||||
|
||||
def new_add_routes(self):
|
||||
import nodes
|
||||
self.app.add_routes(routes)
|
||||
self.app.add_routes(self.routes)
|
||||
for name, dir in nodes.EXTENSION_WEB_DIRS.items():
|
||||
self.app.add_routes([
|
||||
web.static('/extensions/' + urllib.parse.quote(name), dir, follow_symlinks=True),
|
||||
])
|
||||
self.app.add_routes([
|
||||
web.static('/', self.web_root, follow_symlinks=True),
|
||||
])
|
||||
|
||||
PromptServer.add_routes=new_add_routes
|
||||
|
||||
|
||||
|
||||
@@ -166,8 +278,8 @@ NODE_CLASS_MAPPINGS = {
|
||||
"VAEDecodeConsistencyDecoder":VAEDecode,
|
||||
"ScreenShare":ScreenShareNode,
|
||||
"FloatingVideo":FloatingVideo,
|
||||
"CLIPSeg":CLIPSeg,
|
||||
"CombineMasks":CombineMasks
|
||||
"CLIPSeg_":CLIPSeg,
|
||||
"CombineMasks_":CombineMasks
|
||||
}
|
||||
|
||||
# 一个包含节点友好/可读的标题的字典
|
||||
@@ -175,12 +287,14 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"RandomPrompt": "Random Prompt #Example Node",
|
||||
"SplitLongMask":"Splitting a long image into sections",
|
||||
"VAELoaderConsistencyDecoder":"Consistency Decoder Loader",
|
||||
"VAEDecodeConsistencyDecoder":"Consistency Decoder Decode"
|
||||
"VAEDecodeConsistencyDecoder":"Consistency Decoder Decode",
|
||||
"ScreenShare":"ScreenShare #Mixlab",
|
||||
"FloatingVideo":"FloatingVideo #Mixlab"
|
||||
}
|
||||
|
||||
# web ui的节点功能
|
||||
WEB_DIRECTORY = "./web"
|
||||
|
||||
print('--------------')
|
||||
print('\033[34mMixlab Custom Nodes: \033[92mLoaded\033[0m')
|
||||
print('\033[91mMixlab Nodes: \033[93mLoaded\033[0m')
|
||||
print('--------------')
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 383 KiB |
+93
-38
@@ -1,6 +1,6 @@
|
||||
import numpy as np
|
||||
import torch
|
||||
from PIL import Image, ImageOps,ImageFilter,ImageEnhance,ImageDraw
|
||||
from PIL import Image, ImageOps,ImageFilter,ImageEnhance,ImageDraw,ImageSequence
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
import base64,os
|
||||
from io import BytesIO
|
||||
@@ -9,6 +9,9 @@ import json
|
||||
from comfy.cli_args import args
|
||||
import cv2
|
||||
|
||||
from .Watcher import FolderWatcher
|
||||
|
||||
|
||||
|
||||
MAX_RESOLUTION=8192
|
||||
|
||||
@@ -150,25 +153,48 @@ def get_not_transparent_area(image):
|
||||
return (x, y, w, h)
|
||||
|
||||
|
||||
|
||||
# 读取不了分层
|
||||
def load_psd(image):
|
||||
layers=[]
|
||||
print('load_psd',image.format)
|
||||
if image.format=='PSD':
|
||||
layers = [frame.copy() for frame in ImageSequence.Iterator(image)]
|
||||
print('#PSD',len(layers))
|
||||
else:
|
||||
image = ImageOps.exif_transpose(image) #校对方向
|
||||
layers.append(image)
|
||||
return layers
|
||||
|
||||
|
||||
def load_image(fp,white_bg=False):
|
||||
i = Image.open(fp)
|
||||
i = ImageOps.exif_transpose(i)
|
||||
image = i.convert("RGB")
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
if 'A' in i.getbands():
|
||||
mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0
|
||||
mask = 1. - torch.from_numpy(mask)
|
||||
if white_bg==True:
|
||||
nw = mask.unsqueeze(0).unsqueeze(-1).repeat(1, 1, 1, 3)
|
||||
# 将mask的黑色部分对image进行白色处理
|
||||
image[nw == 1] = 1.0
|
||||
else:
|
||||
mask = torch.zeros((64,64), dtype=torch.float32, device="cpu")
|
||||
return (image,mask)
|
||||
im = Image.open(fp)
|
||||
|
||||
# ims=load_psd(im)
|
||||
im = ImageOps.exif_transpose(im) #校对方向
|
||||
ims=[im]
|
||||
|
||||
images=[]
|
||||
|
||||
for i in ims:
|
||||
image = i.convert("RGB")
|
||||
image = np.array(image).astype(np.float32) / 255.0
|
||||
image = torch.from_numpy(image)[None,]
|
||||
if 'A' in i.getbands():
|
||||
mask = np.array(i.getchannel('A')).astype(np.float32) / 255.0
|
||||
mask = 1. - torch.from_numpy(mask)
|
||||
if white_bg==True:
|
||||
nw = mask.unsqueeze(0).unsqueeze(-1).repeat(1, 1, 1, 3)
|
||||
# 将mask的黑色部分对image进行白色处理
|
||||
image[nw == 1] = 1.0
|
||||
else:
|
||||
mask = torch.zeros((64,64), dtype=torch.float32, device="cpu")
|
||||
|
||||
images.append({
|
||||
"image":image,
|
||||
"mask":mask
|
||||
})
|
||||
|
||||
return images
|
||||
|
||||
|
||||
# 获取图片s
|
||||
@@ -180,23 +206,27 @@ def get_images_filepath(f,white_bg=False):
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
try:
|
||||
(im,mask)=load_image(file_path,white_bg)
|
||||
images.append({
|
||||
"image":im,
|
||||
"mask":mask,
|
||||
"file_path":file_path
|
||||
})
|
||||
imgs=load_image(file_path,white_bg)
|
||||
for img in imgs:
|
||||
images.append({
|
||||
"image":img['image'],
|
||||
"mask":img['mask'],
|
||||
"file_path":file_path,
|
||||
"psd":len(imgs)>1
|
||||
})
|
||||
except:
|
||||
print('非图片',file_path)
|
||||
|
||||
elif os.path.isfile(f):
|
||||
try:
|
||||
(im,mask)=load_image(f,white_bg)
|
||||
images.append({
|
||||
"image":im,
|
||||
"mask":mask,
|
||||
"file_path":f
|
||||
})
|
||||
imgs=load_image(f,white_bg)
|
||||
for img in imgs:
|
||||
images.append({
|
||||
"image":img['image'],
|
||||
"mask":img['mask'],
|
||||
"file_path":file_path,
|
||||
"psd":len(imgs)>1
|
||||
})
|
||||
except:
|
||||
print('非图片',f)
|
||||
else:
|
||||
@@ -555,6 +585,9 @@ class EnhanceImage:
|
||||
|
||||
return (image,)
|
||||
|
||||
|
||||
|
||||
|
||||
'''
|
||||
("STRING",{"multiline": False,"default": "Hello World!"})
|
||||
对应 widgets.js 里:
|
||||
@@ -566,39 +599,60 @@ const multiline = !!inputData[1].multiline;
|
||||
# 支持输出1张
|
||||
#
|
||||
class LoadImagesFromPath:
|
||||
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"file_path": ("STRING",{"multiline": False,"default": ""})
|
||||
"file_path": ("STRING",{"multiline": False,"default": ""}),
|
||||
},
|
||||
"optional":{
|
||||
"white_bg": (["disable","enable"],),
|
||||
"newest_files": (["enable", "disable"],),
|
||||
"index_variable":("INT", {
|
||||
"default": -1,
|
||||
"default": 0,
|
||||
"min": -1, #Minimum value
|
||||
"max": 2048, #Maximum value
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
"watcher":(["disable","enable"],),
|
||||
"result": ("WATCHER",),#为了激活本节点运行
|
||||
"prompt": ("PROMPT",),
|
||||
# "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ('IMAGE','MASK')
|
||||
RETURN_TYPES = ('IMAGE','MASK','STRING')
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "Mixlab/image"
|
||||
|
||||
# INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,True,)
|
||||
OUTPUT_IS_LIST = (True,True,False,)
|
||||
|
||||
global watcher_folder
|
||||
watcher_folder=None
|
||||
|
||||
# 运行的函数
|
||||
def run(self,file_path,white_bg,newest_files,index_variable,seed):
|
||||
print(file_path)
|
||||
def run(self,file_path,white_bg,newest_files,index_variable,watcher,result,prompt):
|
||||
global watcher_folder
|
||||
# print('###监听:',watcher_folder,watcher,file_path,result)
|
||||
|
||||
if watcher=='enable':
|
||||
if watcher_folder==None:
|
||||
watcher_folder = FolderWatcher(file_path)
|
||||
|
||||
# 在这里可以进行其他操作,监听会在后台持续
|
||||
watcher_folder.set_folder_path(file_path)
|
||||
watcher_folder.start()
|
||||
|
||||
else:
|
||||
if watcher_folder!=None:
|
||||
watcher_folder.stop()
|
||||
|
||||
|
||||
images=get_images_filepath(file_path,white_bg=='enable')
|
||||
|
||||
# 排序
|
||||
@@ -616,7 +670,8 @@ class LoadImagesFromPath:
|
||||
imgs=[imgs[index_variable]] if index_variable < len(imgs) else None
|
||||
masks=[masks[index_variable]] if index_variable < len(masks) else None
|
||||
|
||||
return (imgs,masks)
|
||||
print('#prompt::::',prompt)
|
||||
return (imgs,masks,prompt,)
|
||||
|
||||
|
||||
# TODO 扩大选区的功能,重新输出mask
|
||||
|
||||
@@ -49,7 +49,7 @@ class ScreenShareNode:
|
||||
},
|
||||
"optional":{
|
||||
"prompt": ("PROMPT",),
|
||||
"seed": ("INT", {"default": 1, "min": 0, "max": 0xffffffffffffffff}),
|
||||
# "seed": ("INT", {"default": 1, "min": 0, "max": 0xffffffffffffffff}),
|
||||
} }
|
||||
|
||||
RETURN_TYPES = ('IMAGE','MASK','STRING')
|
||||
@@ -62,9 +62,9 @@ class ScreenShareNode:
|
||||
OUTPUT_IS_LIST = (False,False,False)
|
||||
|
||||
# 运行的函数
|
||||
def run(self,image_base64,prompt,seed):
|
||||
def run(self,image_base64,prompt):
|
||||
im,mask=base64_save(image_base64)
|
||||
print('##########prompt',prompt)
|
||||
# print('##########prompt',prompt)
|
||||
return (im,mask,prompt)
|
||||
|
||||
|
||||
@@ -73,9 +73,7 @@ class FloatingVideo:
|
||||
def INPUT_TYPES(s):
|
||||
return { "required":{
|
||||
"images": ("IMAGE",)
|
||||
}, "optional":{
|
||||
"seed": ("INT", {"default": 1, "min": 0, "max": 0xffffffffffffffff}),
|
||||
} }
|
||||
}, }
|
||||
|
||||
# RETURN_TYPES = ('IMAGE','MASK')
|
||||
RETURN_TYPES = ()
|
||||
@@ -89,7 +87,7 @@ class FloatingVideo:
|
||||
# OUTPUT_IS_LIST = (False,False,)
|
||||
|
||||
# 运行的函数
|
||||
def run(self,images,seed):
|
||||
def run(self,images):
|
||||
|
||||
results = list()
|
||||
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
from watchdog.observers import Observer
|
||||
from watchdog.events import FileSystemEventHandler
|
||||
|
||||
import time
|
||||
import json
|
||||
import os
|
||||
# current_directory = os.getcwd()
|
||||
# print('Watcher:',current_directory)
|
||||
|
||||
def save_to_json(file_path, data):
|
||||
try:
|
||||
with open(file_path, 'w') as f:
|
||||
json.dump(data, f)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
def read_from_json(file_path):
|
||||
data={}
|
||||
try:
|
||||
with open(file_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return data
|
||||
|
||||
|
||||
# read_from_json()
|
||||
current_path = os.path.abspath(os.path.dirname(__file__))
|
||||
config_json=os.path.join(current_path,'config.json')
|
||||
# print('Watcher:',config_json)
|
||||
|
||||
def read_config():
|
||||
config={}
|
||||
try:
|
||||
if os.path.exists(config_json):
|
||||
config=read_from_json(config_json)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return config
|
||||
|
||||
|
||||
class FolderWatcher:
|
||||
def __init__(self, folder_path):
|
||||
self.folder_path = folder_path
|
||||
|
||||
if folder_path:
|
||||
config=read_config()
|
||||
config['folder_path']=folder_path
|
||||
save_to_json(config_json,config)
|
||||
|
||||
# self.observer = Observer()
|
||||
self.event_handler = self._create_event_handler()
|
||||
self.status = "Not started"
|
||||
self.event_type='-'
|
||||
|
||||
def _create_event_handler(self):
|
||||
|
||||
class EventHandler(FileSystemEventHandler):
|
||||
def on_any_event(self, event):
|
||||
|
||||
if event.is_directory:
|
||||
return
|
||||
elif event.event_type == 'created':
|
||||
print(f"File created: {event.src_path}")
|
||||
self.event_type=event.event_type+'_'+str(int(time.time()))
|
||||
elif event.event_type == 'deleted':
|
||||
print(f"File deleted: {event.src_path}")
|
||||
self.event_type=event.event_type+'_'+str(int(time.time()))
|
||||
elif event.event_type == 'modified':
|
||||
print(f"File modified: {event.src_path}")
|
||||
self.event_type=event.event_type+'_'+str(int(time.time()))
|
||||
elif event.event_type == 'moved':
|
||||
print(f"File moved: {event.src_path} to {event.dest_path}")
|
||||
self.event_type=event.event_type+'_'+str(int(time.time()))
|
||||
|
||||
print(event.event_type)
|
||||
config=read_config()
|
||||
config['event_type']=self.event_type
|
||||
save_to_json(config_json,config)
|
||||
|
||||
return EventHandler()
|
||||
|
||||
def set_folder_path(self, new_folder_path):
|
||||
self.folder_path = new_folder_path
|
||||
if new_folder_path:
|
||||
config=read_config()
|
||||
config['folder_path']=new_folder_path
|
||||
save_to_json(config_json,config)
|
||||
self.event_type='-'
|
||||
|
||||
def start(self):
|
||||
self.observer = Observer()
|
||||
self.observer.schedule(self.event_handler, self.folder_path, recursive=True)
|
||||
self.observer.start()
|
||||
self.status = "Listening"
|
||||
self.event_type='-'
|
||||
print('Listening')
|
||||
|
||||
def stop(self):
|
||||
self.observer.stop()
|
||||
self.observer.join()
|
||||
self.observer=None
|
||||
self.status = "Stopped"
|
||||
self.event_type='-'
|
||||
print('Stopped')
|
||||
|
||||
|
||||
|
||||
# # 示例用法
|
||||
# if __name__ == "__main__":
|
||||
# folder_path = "C:\\\\Users\\\\38957\\\\Documents\\\\GitHub\\\\extract-anything\\\\outputs" # 替换为您要监听的文件夹路径
|
||||
|
||||
# watcher_folder = FolderWatcher(folder_path)
|
||||
# watcher_folder.start()
|
||||
# watcher_folder.stop()
|
||||
|
||||
# watcher_folder.start()
|
||||
# watcher_folder.stop()
|
||||
|
||||
# while True:
|
||||
# 1
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
numpy
|
||||
pyOpenSSL
|
||||
watchdog
|
||||
opencv-python-headless
|
||||
matplotlib
|
||||
@@ -0,0 +1,39 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
|
||||
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
||||
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
||||
|
||||
const version='v0.2.3'
|
||||
|
||||
|
||||
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const latestVersion = data.tag_name
|
||||
console.log('Latest release version:', latestVersion )
|
||||
|
||||
if(latestVersion!=version){
|
||||
|
||||
app.ui.dialog.show(`<h4 style="font-size: 18px;">${repoName} <br>
|
||||
Latest release version: ${latestVersion}</h4>
|
||||
<p>Please proceed to the official repository to download the latest version.</p>
|
||||
<a style=" color: #2196F3;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 2px;
|
||||
}"
|
||||
href="https://github.com/shadowcz007/comfyui-mixlab-nodes/releases/">https://github.com/shadowcz007/comfyui-mixlab-nodes/releases</a>
|
||||
`)
|
||||
|
||||
// window.alert(
|
||||
// `Please proceed to the official repository to download the latest version.https://github.com/shadowcz007/comfyui-mixlab-nodes/releases/`
|
||||
// )
|
||||
// window.open(
|
||||
// 'https://github.com/shadowcz007/comfyui-mixlab-nodes/releases/'
|
||||
// )
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching release information:', error)
|
||||
})
|
||||
// #MixCopilot
|
||||
+368
-116
@@ -1,5 +1,5 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
import { api } from '../../../scripts/api.js'
|
||||
// import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
@@ -76,7 +76,7 @@ async function uploadFile (file) {
|
||||
|
||||
// alert(navigator.mediaDevices)
|
||||
|
||||
async function shareScreen (webcamVideo, shareBtn, liveBtn) {
|
||||
async function shareScreen (webcamVideo, shareBtn, liveBtn, previewArea) {
|
||||
try {
|
||||
// let webcamVideo = document.createElement('video')
|
||||
const mediaStream = await navigator.mediaDevices.getDisplayMedia({
|
||||
@@ -100,7 +100,7 @@ async function shareScreen (webcamVideo, shareBtn, liveBtn) {
|
||||
|
||||
// 停止共享的回调函数
|
||||
function handleStopSharing () {
|
||||
console.log('用户停止了共享')
|
||||
// console.log('用户停止了共享')
|
||||
// 执行其他操作
|
||||
if (window._mixlab_stopVideo) {
|
||||
window._mixlab_stopVideo()
|
||||
@@ -119,7 +119,6 @@ async function shareScreen (webcamVideo, shareBtn, liveBtn) {
|
||||
|
||||
async function videoTimeUpdateHandler () {
|
||||
if (!window._mixlab_screen_live) return
|
||||
|
||||
createBlobFromVideo(webcamVideo)
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -133,6 +132,7 @@ async function shareScreen (webcamVideo, shareBtn, liveBtn) {
|
||||
webcamVideo.srcObject = null // 清空视频源对象
|
||||
window._mixlab_screen_live = false
|
||||
window._mixlab_screen_blob = null
|
||||
previewArea.innerHTML = ''
|
||||
interrupt()
|
||||
}
|
||||
}
|
||||
@@ -145,26 +145,124 @@ async function sleep (t = 200) {
|
||||
})
|
||||
}
|
||||
|
||||
function createImage (url) {
|
||||
let im = new Image()
|
||||
return new Promise((res, rej) => {
|
||||
im.onload = () => res(im)
|
||||
im.src = url
|
||||
})
|
||||
}
|
||||
|
||||
async function compareImages (threshold, previousImage, currentImage) {
|
||||
// 将 base64 转换为 Image 对象
|
||||
var previousImg = await createImage(previousImage)
|
||||
var currentImg = await createImage(currentImage)
|
||||
|
||||
if (
|
||||
previousImg.naturalWidth != currentImg.naturalWidth ||
|
||||
previousImg.naturalHeight != currentImg.naturalHeight
|
||||
) {
|
||||
return true // 图片有变化
|
||||
}
|
||||
|
||||
// 创建一个 canvas 元素
|
||||
var canvas1 = document.createElement('canvas')
|
||||
canvas1.width = previousImg.naturalWidth
|
||||
canvas1.height = previousImg.naturalHeight
|
||||
var context1 = canvas1.getContext('2d')
|
||||
|
||||
// 将图片绘制到 canvas 上
|
||||
context1.drawImage(previousImg, 0, 0)
|
||||
|
||||
// 获取图片的像素数据
|
||||
var previousData = context1.getImageData(
|
||||
0,
|
||||
0,
|
||||
previousImg.naturalWidth,
|
||||
previousImg.naturalHeight
|
||||
).data
|
||||
|
||||
var canvas2 = document.createElement('canvas')
|
||||
canvas2.width = currentImg.naturalWidth
|
||||
canvas2.height = currentImg.naturalHeight
|
||||
var context2 = canvas2.getContext('2d')
|
||||
context2.drawImage(currentImg, 0, 0)
|
||||
var currentData = context2.getImageData(
|
||||
0,
|
||||
0,
|
||||
currentImg.naturalWidth,
|
||||
currentImg.naturalHeight
|
||||
).data
|
||||
|
||||
// 遍历每个像素点,计算像素差异
|
||||
var pixelDiff = 0
|
||||
for (var i = 0; i < previousData.length; i += 4) {
|
||||
var diffR = Math.abs(previousData[i] - currentData[i])
|
||||
var diffG = Math.abs(previousData[i + 1] - currentData[i + 1])
|
||||
var diffB = Math.abs(previousData[i + 2] - currentData[i + 2])
|
||||
|
||||
// 计算像素差异总和
|
||||
pixelDiff += diffR + diffG + diffB
|
||||
}
|
||||
|
||||
// 计算平均像素差异
|
||||
var averageDiff = pixelDiff / (previousData.length / 4)
|
||||
|
||||
// 判断平均像素差异是否超过阈值
|
||||
// console.log(
|
||||
// pixelDiff,
|
||||
// averageDiff,
|
||||
// threshold,
|
||||
// currentImg.naturalWidth,
|
||||
// currentImg.naturalHeight,previousData,currentData
|
||||
// )
|
||||
if (averageDiff > threshold) {
|
||||
return true // 图片有变化
|
||||
} else {
|
||||
return false // 图片无变化
|
||||
}
|
||||
}
|
||||
|
||||
async function startLive (btn) {
|
||||
if (btn) window._mixlab_screen_live = !window._mixlab_screen_live
|
||||
|
||||
if (btn) btn.innerText = `Stop Live`
|
||||
// console.log('#ML', 'live run', window._mixlab_screen_time)
|
||||
if (window._mixlab_screen_time) {
|
||||
// console.log('#ML', 'live')
|
||||
return
|
||||
}
|
||||
// if (window._mixlab_screen_time) {
|
||||
// // console.log('#ML', 'live')
|
||||
// return
|
||||
// }
|
||||
const { Pending, Running } = await getQueue()
|
||||
// console.log('#ML', Pending, window._mixlab_screen_blob)
|
||||
if (Pending <= 1 && window._mixlab_screen_blob && Running === 0) {
|
||||
window._mixlab_screen_time = true
|
||||
// console.log(file)
|
||||
window._mixlab_screen_imagePath = await blobToBase64(
|
||||
window._mixlab_screen_blob
|
||||
)
|
||||
// window._mixlab_screen_time = true
|
||||
|
||||
const threshold = 1 // 阈值
|
||||
const previousImage = window._mixlab_screen_imagePath // 上一张图片的 base64
|
||||
let currentImage = await blobToBase64(window._mixlab_screen_blob)
|
||||
|
||||
if (previousImage) {
|
||||
// 现在新的图片的 base64
|
||||
const imageChanged = await compareImages(
|
||||
threshold,
|
||||
previousImage,
|
||||
currentImage
|
||||
)
|
||||
// console.log('#图片是否有变化:', imageChanged)
|
||||
|
||||
if (imageChanged) {
|
||||
window._mixlab_screen_imagePath = currentImage
|
||||
document.querySelector('#queue-button').click()
|
||||
}
|
||||
} else {
|
||||
window._mixlab_screen_imagePath = currentImage
|
||||
// console.log(window._mixlab_screen_imagePath)
|
||||
document.querySelector('#queue-button').click()
|
||||
}
|
||||
|
||||
// await uploadFile(file)
|
||||
window._mixlab_screen_time = false
|
||||
document.querySelector('#queue-button').click()
|
||||
// window._mixlab_screen_time = false
|
||||
|
||||
await sleep(window._mixlab_screen_refresh_rate || 200)
|
||||
// console.log('#ML', window._mixlab_screen_imagePath)
|
||||
}
|
||||
@@ -196,7 +294,7 @@ async function createBlobFromVideoForArea (webcamVideo) {
|
||||
type: 'image/jpeg',
|
||||
quality: 1
|
||||
})
|
||||
// imgElement.src = await blobToBase64(blob)
|
||||
|
||||
return blob
|
||||
}
|
||||
|
||||
@@ -208,6 +306,7 @@ async function createBlobFromVideo (webcamVideo) {
|
||||
HEIGHT = window._mixlab_screen_height
|
||||
const canvas = new OffscreenCanvas(WIDTH, HEIGHT)
|
||||
const ctx = canvas.getContext('2d')
|
||||
// console.log('#createBlobFromVideo', WIDTH, HEIGHT)
|
||||
ctx.drawImage(
|
||||
webcamVideo,
|
||||
window._mixlab_screen_x,
|
||||
@@ -226,6 +325,13 @@ async function createBlobFromVideo (webcamVideo) {
|
||||
})
|
||||
// imgElement.src = await blobToBase64(blob)
|
||||
window._mixlab_screen_blob = blob
|
||||
|
||||
console.log('########')
|
||||
// let currentImage = await blobToBase64(blob)
|
||||
// // console.log(window._mixlab_screen_imagePath)
|
||||
// if (!window._mixlab_screen_imagePath) {
|
||||
// window._mixlab_screen_imagePath = currentImage
|
||||
// }
|
||||
}
|
||||
|
||||
async function blobToBase64 (blob) {
|
||||
@@ -296,6 +402,7 @@ const base64Df =
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.image.ScreenShareNode',
|
||||
async getCustomWidgets (app) {
|
||||
console.log('#Mixlab.image.ScreenShareNode', app)
|
||||
return {
|
||||
CHEESE (node, inputName, inputData, app) {
|
||||
// We return an object containing a field CHEESE which has a function (taking node, name, data, app)
|
||||
@@ -313,8 +420,8 @@ app.registerExtension({
|
||||
return window._mixlab_screen_imagePath || base64Df
|
||||
}
|
||||
}
|
||||
// widget.something = something; // maybe adds stuff to it
|
||||
node.addCustomWidget(widget) // adds it to the node
|
||||
// console.log('#Mixlab.image.ScreenShareNode',widget)
|
||||
node.addCustomWidget(widget)
|
||||
return widget // and returns it.
|
||||
},
|
||||
PROMPT (node, inputName, inputData, app) {
|
||||
@@ -333,7 +440,7 @@ app.registerExtension({
|
||||
return window._mixlab_screen_prompt || ''
|
||||
}
|
||||
}
|
||||
console.log('###widget', widget)
|
||||
// console.log('###widget', widget)
|
||||
node.addCustomWidget(widget) // adds it to the node
|
||||
return widget // and returns it.
|
||||
}
|
||||
@@ -361,6 +468,8 @@ app.registerExtension({
|
||||
|
||||
widget.card = $el('div', {})
|
||||
|
||||
widget.previewCard = $el('div', {})
|
||||
|
||||
widget.preview = $el('video', {
|
||||
style: {
|
||||
width: '100%'
|
||||
@@ -419,25 +528,26 @@ app.registerExtension({
|
||||
})
|
||||
|
||||
document.body.appendChild(widget.card)
|
||||
widget.card.appendChild(widget.preview)
|
||||
widget.card.appendChild(widget.previewArea)
|
||||
|
||||
widget.card.appendChild(widget.previewCard)
|
||||
widget.previewCard.appendChild(widget.preview)
|
||||
widget.previewCard.appendChild(widget.previewArea)
|
||||
|
||||
widget.card.appendChild(widget.shareBtn)
|
||||
widget.card.appendChild(widget.openFloatingWinBtn)
|
||||
widget.card.appendChild(widget.refreshInput)
|
||||
widget.card.appendChild(widget.liveBtn)
|
||||
// widget.inputEl.addEventListener('click', () => {
|
||||
// shareScreen(widget.inputEl)
|
||||
// })
|
||||
|
||||
widget.shareBtn.addEventListener('click', async () => {
|
||||
if (widget.preview.paused) {
|
||||
window._mixlab_stopVideo = await shareScreen(
|
||||
widget.preview,
|
||||
widget.shareBtn,
|
||||
widget.liveBtn
|
||||
widget.liveBtn,
|
||||
widget.previewArea
|
||||
)
|
||||
widget.shareBtn.innerText = 'Stop Share'
|
||||
|
||||
console.log('视频已暂停')
|
||||
if (window._mixlab_stopLive) {
|
||||
window._mixlab_stopLive()
|
||||
@@ -477,7 +587,11 @@ app.registerExtension({
|
||||
})
|
||||
|
||||
widget.openFloatingWinBtn.addEventListener('click', async () => {
|
||||
// console.log()
|
||||
// if (window._mixlab_stopLive) {
|
||||
// window._mixlab_stopLive()
|
||||
// window._mixlab_stopLive = null
|
||||
// widget.liveBtn.innerText = 'Live Run'
|
||||
// }
|
||||
let blob = await createBlobFromVideoForArea(
|
||||
window._mixlab_screen_webcamVideo
|
||||
)
|
||||
@@ -497,6 +611,7 @@ app.registerExtension({
|
||||
widget.card.remove()
|
||||
widget.refreshInput.remove()
|
||||
widget.previewArea.remove()
|
||||
widget.previewCard.remove()
|
||||
}
|
||||
this.serialize_widgets = false
|
||||
}
|
||||
@@ -511,7 +626,7 @@ function setArea (src) {
|
||||
<img id='ml_video' style='position: absolute; width: 500px; user-select: none; -webkit-user-drag: none;' />
|
||||
<div id='ml_selection' style='position: absolute; border: 2px dashed red; pointer-events: none;'></div>
|
||||
</div>`
|
||||
|
||||
// document.body.querySelector('#ml_overlay')
|
||||
document.body.appendChild(div)
|
||||
|
||||
let img = div.querySelector('#ml_video')
|
||||
@@ -534,61 +649,6 @@ function setArea (src) {
|
||||
updateSelection(event)
|
||||
start = true
|
||||
} else {
|
||||
// 获取img元素的真实宽度和高度
|
||||
let imgWidth = img.naturalWidth
|
||||
let imgHeight = img.naturalHeight
|
||||
|
||||
// 换算起始坐标
|
||||
let realStartX = (startX / img.offsetWidth) * imgWidth
|
||||
let realStartY = (startY / img.offsetHeight) * imgHeight
|
||||
|
||||
// 换算起始坐标
|
||||
let realEndX = (endX / img.offsetWidth) * imgWidth
|
||||
let realEndY = (endY / img.offsetHeight) * imgHeight
|
||||
|
||||
// 输出结果到控制台
|
||||
// console.log('真实宽度: ' + realWidth)
|
||||
// console.log('真实高度: ' + realHeight)
|
||||
startX = realStartX
|
||||
startY = realStartY
|
||||
endX = realEndX
|
||||
endY = realEndY
|
||||
// Calculate width, height, and coordinates
|
||||
let width = Math.abs(endX - startX)
|
||||
let height = Math.abs(endY - startY)
|
||||
let left = Math.min(startX, endX)
|
||||
let top = Math.min(startY, endY)
|
||||
// Output results to console
|
||||
// console.log('坐标位置: (' + left + ', ' + top + ')')
|
||||
// console.log('宽度: ' + width)
|
||||
// console.log('高度: ' + height)
|
||||
|
||||
img.removeEventListener('mousedown', startSelection)
|
||||
img.removeEventListener('mousemove', updateSelection)
|
||||
img.removeEventListener('mouseup', endSelection)
|
||||
div.remove()
|
||||
window._mixlab_screen_x = left
|
||||
window._mixlab_screen_y = top
|
||||
window._mixlab_screen_width = width
|
||||
window._mixlab_screen_height = height
|
||||
|
||||
try {
|
||||
let area = graph._nodes
|
||||
.filter(n => n.type === 'ScreenShare')[0]
|
||||
.widgets.filter(w => w.name == 'sreen_share')[0].previewArea
|
||||
area.style = `
|
||||
position: absolute;
|
||||
background: red;
|
||||
width:${
|
||||
100 * (_mixlab_screen_width / _mixlab_screen_webcamVideo.videoWidth)
|
||||
}%;
|
||||
height:${
|
||||
100 * (_mixlab_screen_height / _mixlab_screen_webcamVideo.videoWidth)
|
||||
}%;
|
||||
left:${100 * (_mixlab_screen_x / _mixlab_screen_webcamVideo.videoWidth)}%;
|
||||
top:${100 * (_mixlab_screen_y / _mixlab_screen_webcamVideo.videoHeight)}%;
|
||||
`
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,9 +672,112 @@ function setArea (src) {
|
||||
function endSelection (event) {
|
||||
endX = event.clientX
|
||||
endY = event.clientY
|
||||
|
||||
// 获取img元素的真实宽度和高度
|
||||
let imgWidth = img.naturalWidth
|
||||
let imgHeight = img.naturalHeight
|
||||
|
||||
// 换算起始坐标
|
||||
let realStartX = (startX / img.offsetWidth) * imgWidth
|
||||
let realStartY = (startY / img.offsetHeight) * imgHeight
|
||||
|
||||
// 换算起始坐标
|
||||
let realEndX = (endX / img.offsetWidth) * imgWidth
|
||||
let realEndY = (endY / img.offsetHeight) * imgHeight
|
||||
|
||||
// 输出结果到控制台
|
||||
// console.log('真实宽度: ' + realWidth)
|
||||
// console.log('真实高度: ' + realHeight)
|
||||
startX = realStartX
|
||||
startY = realStartY
|
||||
endX = realEndX
|
||||
endY = realEndY
|
||||
// Calculate width, height, and coordinates
|
||||
let width = Math.abs(endX - startX)
|
||||
let height = Math.abs(endY - startY)
|
||||
let left = Math.min(startX, endX)
|
||||
let top = Math.min(startY, endY)
|
||||
// Output results to console
|
||||
// console.log('坐标位置: (' + left + ', ' + top + ')')
|
||||
// console.log('宽度: ' + width)
|
||||
// console.log('高度: ' + height)
|
||||
|
||||
img.removeEventListener('mousedown', startSelection)
|
||||
img.removeEventListener('mousemove', updateSelection)
|
||||
img.removeEventListener('mouseup', endSelection)
|
||||
|
||||
window._mixlab_screen_x = left
|
||||
window._mixlab_screen_y = top
|
||||
window._mixlab_screen_width = width
|
||||
window._mixlab_screen_height = height
|
||||
|
||||
try {
|
||||
let canvas = document.createElement('canvas')
|
||||
canvas.width = _mixlab_screen_webcamVideo.videoWidth
|
||||
canvas.height = _mixlab_screen_webcamVideo.videoHeight
|
||||
let ctx = canvas.getContext('2d')
|
||||
const lineWidth = 1 // Width of the stroke line
|
||||
const strokeColor = 'red' // Color of the stroke
|
||||
|
||||
// Draw the rectangle
|
||||
ctx.strokeStyle = strokeColor // Set the stroke color
|
||||
ctx.lineWidth = lineWidth // Set the stroke line width
|
||||
ctx.strokeRect(
|
||||
_mixlab_screen_x,
|
||||
_mixlab_screen_y,
|
||||
_mixlab_screen_width,
|
||||
_mixlab_screen_height
|
||||
) // Draw the stroked rectangle
|
||||
|
||||
canvas.style.width = '100%'
|
||||
|
||||
let area = graph._nodes
|
||||
.filter(n => n.type === 'ScreenShare')[0]
|
||||
.widgets.filter(w => w.name == 'sreen_share')[0].previewArea
|
||||
area.innerHTML = ''
|
||||
area.appendChild(canvas)
|
||||
area.style = `
|
||||
position: absolute;
|
||||
width:100%%;
|
||||
left:0;
|
||||
top:0;
|
||||
`
|
||||
} catch (error) {}
|
||||
|
||||
div.remove()
|
||||
}
|
||||
}
|
||||
|
||||
async function save_workflow (json) {
|
||||
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||
let api_base = ''
|
||||
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||
|
||||
const res = await fetch(`${url}/mixlab/workflow`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
data: json,
|
||||
task: 'save'
|
||||
})
|
||||
})
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
async function get_my_workflow () {
|
||||
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||
let api_base = ''
|
||||
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||
|
||||
const res = await fetch(`${url}/mixlab/workflow`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
task: 'list'
|
||||
})
|
||||
})
|
||||
let result = await res.json()
|
||||
return result.data
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.image.FloatingVideo',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
@@ -672,21 +835,26 @@ app.registerExtension({
|
||||
|
||||
widget.PictureInPicture.addEventListener('click', async () => {
|
||||
if (window.location.protocol != 'https:') {
|
||||
let http_workflow = app.graph.serialize()
|
||||
await save_workflow(http_workflow)
|
||||
|
||||
window.alert(
|
||||
`About to redirect to HTTPS access.https://${
|
||||
`Redirecting to HTTPS access due to the requirement of the floating window. https://${
|
||||
window.location.hostname
|
||||
}:${~~window.location.port + 1}`
|
||||
)
|
||||
window.open(
|
||||
`https://${window.location.hostname}:${
|
||||
~~window.location.port + 1
|
||||
}`
|
||||
}?workflow=1`
|
||||
)
|
||||
}
|
||||
// Open a Picture-in-Picture window.
|
||||
let w = 360,
|
||||
s = widget.preview.videoWidth / widget.preview.videoHeight,
|
||||
h = w / s
|
||||
h = w / s || w
|
||||
console.log(h)
|
||||
|
||||
const pipWindow = await documentPictureInPicture.requestWindow({
|
||||
width: w,
|
||||
height: Math.round(h) + 120
|
||||
@@ -704,7 +872,7 @@ app.registerExtension({
|
||||
z-index: 9999;
|
||||
left: 0px;
|
||||
width: calc(100% - 24px);
|
||||
margin: 12px;`
|
||||
margin: 12px;`
|
||||
|
||||
// console.log(pipWindow.document)
|
||||
// Move the player to the Picture-in-Picture window.
|
||||
@@ -724,6 +892,15 @@ app.registerExtension({
|
||||
border: 1px solid rgb(91, 91, 91);
|
||||
font-family: sans-serif;
|
||||
`
|
||||
// Create a style element
|
||||
const style = document.createElement('style')
|
||||
// Define the CSS rule for scrollbar width
|
||||
const cssRule = `::-webkit-scrollbar { width: 2px;}`
|
||||
// Add the CSS rule to the style element
|
||||
style.appendChild(document.createTextNode(cssRule))
|
||||
|
||||
// Append the style element to the document head
|
||||
pipWindow.document.head.appendChild(style)
|
||||
|
||||
window._mixlab_screen_prompt =
|
||||
window._mixlab_screen_prompt ||
|
||||
@@ -739,7 +916,7 @@ app.registerExtension({
|
||||
align-items: center;
|
||||
width: 24px;
|
||||
font-size: 16px;
|
||||
margin-right: 6px;`
|
||||
margin-right: 6px;user-select: none;`
|
||||
|
||||
let btn = document.createElement('butotn')
|
||||
btn.innerText = '❤'
|
||||
@@ -787,17 +964,46 @@ app.registerExtension({
|
||||
}
|
||||
})
|
||||
|
||||
let promptFinishBtn = document.createElement('butotn')
|
||||
promptFinishBtn.innerText = '🚀'
|
||||
promptFinishBtn.style = `cursor: pointer;height: 24px;margin:4px;`
|
||||
promptFinishBtn.addEventListener('click', () => {
|
||||
console.log('##更新Prompt')
|
||||
window._mixlab_screen_prompt =
|
||||
window._mixlab_screen_prompt_input || window._mixlab_screen_prompt
|
||||
document.querySelector('#queue-button').click()
|
||||
})
|
||||
|
||||
pipWindow.document.body.append(widget.preview)
|
||||
pipWindow.document.body.append(div)
|
||||
|
||||
div.appendChild(btnDiv)
|
||||
btnDiv.appendChild(btn)
|
||||
btnDiv.appendChild(pauseBtn)
|
||||
btnDiv.appendChild(promptFinishBtn)
|
||||
div.appendChild(input)
|
||||
|
||||
input.addEventListener('input', () => {
|
||||
window._mixlab_screen_prompt = input.value
|
||||
window._mixlab_screen_prompt_input = input.value
|
||||
})
|
||||
|
||||
input.addEventListener('keydown', handleKeyDown)
|
||||
|
||||
function handleKeyDown (event) {
|
||||
if (event.key === 'Enter') {
|
||||
if (!event.shiftKey) {
|
||||
// 回车键被按下且未同时按下Shift键,执行你的操作
|
||||
event.preventDefault() // 阻止默认行为(如提交表单)
|
||||
// 在这里添加你的代码
|
||||
console.log('##更新Prompt')
|
||||
window._mixlab_screen_prompt =
|
||||
window._mixlab_screen_prompt_input ||
|
||||
window._mixlab_screen_prompt
|
||||
document.querySelector('#queue-button').click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move the player back when the Picture-in-Picture window closes.
|
||||
pipWindow.addEventListener('pagehide', event => {
|
||||
widget.card.appendChild(widget.preview)
|
||||
@@ -1208,31 +1414,77 @@ const importWorkflow = my => {
|
||||
app.canvas.pasteFromClipboard()
|
||||
}
|
||||
|
||||
function getURLParameters (url) {
|
||||
var params = {}
|
||||
var paramStr = url.split('?')[1]
|
||||
if (paramStr) {
|
||||
var paramArr = paramStr.split('&')
|
||||
for (var i = 0; i < paramArr.length; i++) {
|
||||
var param = paramArr[i].split('=')
|
||||
var paramName = decodeURIComponent(param[0])
|
||||
var paramValue = decodeURIComponent(param[1] || '')
|
||||
if (paramName) {
|
||||
if (params[paramName]) {
|
||||
params[paramName] = Array.isArray(params[paramName])
|
||||
? [...params[paramName], paramValue]
|
||||
: [params[paramName], paramValue]
|
||||
} else {
|
||||
params[paramName] = paramValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// var url = "http://example.com/page?param1=value1¶m2=value2¶m3=value3";
|
||||
// var params = getURLParameters(url);
|
||||
// console.log(params);
|
||||
|
||||
const node = {
|
||||
name: 'RandomPrompt',
|
||||
async init (app) {
|
||||
// Any initial setup to run as soon as the page loads
|
||||
console.log('[logging]', 'extension init')
|
||||
|
||||
if (window.location.href.match('/?')) {
|
||||
const { workflow } = getURLParameters(window.location.href)
|
||||
if (workflow)
|
||||
get_my_workflow().then(data => {
|
||||
console.log('#get_my_workflow', data)
|
||||
let my_workflow = data.filter(
|
||||
d => d.filename == 'my_workflow.json'
|
||||
)[0]
|
||||
if (my_workflow?.data) {
|
||||
// app.loadGraphData(my_workflow.data)
|
||||
localStorage.setItem('workflow',JSON.stringify(my_workflow.data));
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
async setup (a) {
|
||||
for (const node of app.graph._nodes) {
|
||||
if (node.comfyClass === 'RandomPrompt') {
|
||||
console.log('#setup', node)
|
||||
console.log('#setup', node)
|
||||
if (node.type === 'RandomPrompt') {
|
||||
updateUI(node)
|
||||
}
|
||||
}
|
||||
console.log('[logging]', 'loaded graph node: ', exportGraph(app.graph))
|
||||
// console.log('[logging]', 'loaded graph node: ', exportGraph(app.graph))
|
||||
},
|
||||
addCustomNodeDefs (defs, app) {
|
||||
console.log(
|
||||
'[logging]',
|
||||
'add custom node definitions',
|
||||
'current nodes:',
|
||||
defs
|
||||
)
|
||||
// console.log(
|
||||
// '[logging]',
|
||||
// 'add custom node definitions',
|
||||
// 'current nodes:',
|
||||
// defs
|
||||
// )
|
||||
// 在这里进行 语言切换
|
||||
for (const nodeName in defs) {
|
||||
if (nodeName === 'RandomPrompt') {
|
||||
// defs[nodeName].category
|
||||
// defs[nodeName].display_name
|
||||
}
|
||||
}
|
||||
// for (const nodeName in defs) {
|
||||
// if (nodeName === 'RandomPrompt') {
|
||||
// // defs[nodeName].category
|
||||
// // defs[nodeName].display_name
|
||||
// }
|
||||
// }
|
||||
},
|
||||
loadedGraphNode (node, app) {
|
||||
// Fires for each node when loading/dragging/etc a workflow json or png
|
||||
@@ -1241,11 +1493,11 @@ const node = {
|
||||
// console.log("[logging]", "loaded graph node: ", exportGraph(node.graph));
|
||||
},
|
||||
async nodeCreated (node) {
|
||||
if (node.comfyClass === 'RandomPrompt') {
|
||||
if (node.type === 'RandomPrompt') {
|
||||
updateUI(node)
|
||||
}
|
||||
|
||||
if (node.comfyClass === 'RunWorkflow') {
|
||||
if (node.type === 'RunWorkflow') {
|
||||
const pw = node.widgets.filter(w => w.name === 'workflow')[0]
|
||||
console.log('nodeCreated', pw)
|
||||
// if (pw) {
|
||||
@@ -1265,17 +1517,17 @@ const node = {
|
||||
// 汉化
|
||||
// app.graph._nodes // title ='123'
|
||||
|
||||
if (nodeData.name === 'SaveTransparentImage') {
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
const r = onExecuted?.apply?.(this, arguments)
|
||||
console.log('executed', message)
|
||||
const { image_path } = message
|
||||
if (image_path) {
|
||||
}
|
||||
return r
|
||||
}
|
||||
}
|
||||
// if (nodeData.name === 'SaveTransparentImage') {
|
||||
// const onExecuted = nodeType.prototype.onExecuted
|
||||
// nodeType.prototype.onExecuted = function (message) {
|
||||
// const r = onExecuted?.apply?.(this, arguments)
|
||||
// console.log('executed', message)
|
||||
// const { image_path } = message
|
||||
// if (image_path) {
|
||||
// }
|
||||
// return r
|
||||
// }
|
||||
// }
|
||||
|
||||
if (nodeData.name === 'WSServer') {
|
||||
// Create the button widget for selecting the files
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
|
||||
const missingNodeGithub = missingNodeTypes => {
|
||||
return Array.from(
|
||||
new Set(missingNodeTypes)
|
||||
,n=>{
|
||||
const url = `https://github.com/search?q=${n}&type=code`;
|
||||
|
||||
return `<li style="color: white;
|
||||
background: black;
|
||||
padding: 8px;
|
||||
font-size: 12px;">${n}<a href="${url}" target="_blank"> 🔗</a></li>`;
|
||||
})
|
||||
}
|
||||
|
||||
app.showMissingNodesError = function (missingNodeTypes, hasAddedNodes = true) {
|
||||
// console.log('###MIXLAB', missingNodeTypes, hasAddedNodes)
|
||||
this.ui.dialog.show(
|
||||
`When loading the graph, the following node types were not found: <ul>${ missingNodeGithub(missingNodeTypes)
|
||||
.join('')}</ul>${
|
||||
hasAddedNodes
|
||||
? 'Nodes that have failed to load will show as red on the graph.'
|
||||
: ''
|
||||
}`
|
||||
)
|
||||
this.logging.addEntry('Comfy.App', 'warn', {
|
||||
MissingNodes: missingNodeTypes
|
||||
})
|
||||
}
|
||||
|
||||
// app.ui.dialog.show = function (html) {
|
||||
// console.log('###MIXLAB', html)
|
||||
// if (typeof html === 'string') {
|
||||
// this.textElement.innerHTML = html
|
||||
// } else {
|
||||
// this.textElement.replaceChildren(html)
|
||||
// }
|
||||
// this.element.style.display = 'flex'
|
||||
// }
|
||||
@@ -0,0 +1,176 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
// import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
async function getConfig () {
|
||||
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||
let api_base = ''
|
||||
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||
|
||||
const res = await fetch(`${url}/mixlab`, {
|
||||
method: 'POST'
|
||||
})
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
if (!window._mixlab_screen_prompt)
|
||||
window._mixlab_screen_prompt =
|
||||
'beautiful scenery nature glass bottle landscape,under water'
|
||||
|
||||
function get_position_style (ctx, widget_width, y, node_height) {
|
||||
const MARGIN = 4 // the margin around the html element
|
||||
|
||||
/* Create a transform that deals with all the scrolling and zooming */
|
||||
const elRect = ctx.canvas.getBoundingClientRect()
|
||||
const transform = new DOMMatrix()
|
||||
.scaleSelf(
|
||||
elRect.width / ctx.canvas.width,
|
||||
elRect.height / ctx.canvas.height
|
||||
)
|
||||
.multiplySelf(ctx.getTransform())
|
||||
.translateSelf(MARGIN, MARGIN + y)
|
||||
|
||||
return {
|
||||
transformOrigin: '0 0',
|
||||
transform: transform,
|
||||
left: `0`,
|
||||
top: `0`,
|
||||
cursor: 'pointer',
|
||||
position: 'absolute',
|
||||
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||
width: `${widget_width - MARGIN * 2}px`,
|
||||
height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
background: '#EEEEEE',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'space-around'
|
||||
}
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.image.LoadImagesFromPath',
|
||||
async getCustomWidgets (app) {
|
||||
return {
|
||||
WATCHER (node, inputName, inputData, app) {
|
||||
// console.log('node', inputName, inputData[0])
|
||||
const widget = {
|
||||
type: inputData[0], // the type, CHEESE
|
||||
name: inputName, // the name, slice
|
||||
size: [128, 24], // a default size
|
||||
draw (ctx, node, width, y) {
|
||||
// // 绘制文件图标的函数
|
||||
// function drawFileIcon () {
|
||||
// // 清空画布
|
||||
// // ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
|
||||
// // 绘制文件外框
|
||||
// ctx.fillStyle = '#000'
|
||||
// ctx.fillRect(5, 5, 40, 40)
|
||||
|
||||
// // 绘制文件夹图标
|
||||
// ctx.fillStyle = '#f00'
|
||||
// ctx.fillRect(10, 15, 30, 20)
|
||||
|
||||
// // 绘制监听符号
|
||||
// ctx.beginPath()
|
||||
// ctx.arc(30, 35, 5, 0, 2 * Math.PI)
|
||||
// ctx.fillStyle = '#00f'
|
||||
// ctx.fill()
|
||||
// }
|
||||
|
||||
// // 调用绘制函数
|
||||
// drawFileIcon()
|
||||
},
|
||||
computeSize (...args) {
|
||||
return [128, 24] // a method to compute the current size of the widget
|
||||
},
|
||||
async serializeValue (nodeId, widgetIndex) {
|
||||
return window._mixlab_file_path_watcher || ''
|
||||
}
|
||||
}
|
||||
// widget.something = something; // maybe adds stuff to it
|
||||
node.addCustomWidget(widget) // adds it to the node
|
||||
return widget // and returns it.
|
||||
},
|
||||
PROMPT (node, inputName, inputData, app) {
|
||||
// console.log('node', inputName, inputData[0])
|
||||
const widget = {
|
||||
type: inputData[0], // the type, CHEESE
|
||||
name: inputName, // the name, slice
|
||||
size: [128, 24], // a default size
|
||||
draw (ctx, node, width, y) {
|
||||
// a method to draw the widget (ctx is a CanvasRenderingContext2D)
|
||||
},
|
||||
computeSize (...args) {
|
||||
return [128, 24] // a method to compute the current size of the widget
|
||||
},
|
||||
async serializeValue (nodeId, widgetIndex) {
|
||||
return window._mixlab_screen_prompt || ''
|
||||
}
|
||||
}
|
||||
// widget.something = something; // maybe adds stuff to it
|
||||
node.addCustomWidget(widget) // adds it to the node
|
||||
return widget // and returns it.
|
||||
}
|
||||
}
|
||||
},
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
// console.log(nodeType.comfyClass)
|
||||
if (nodeType.comfyClass == 'LoadImagesFromPath') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
console.log('watch widtget', this.widgets)
|
||||
|
||||
const watcher = this.widgets.filter(w => w.name == 'watcher')[0]
|
||||
|
||||
watcher.callback = () => {
|
||||
if (watcher.value === 'enable') {
|
||||
if (window._mixlab_watcher_t)
|
||||
clearInterval(window._mixlab_watcher_t)
|
||||
window._mixlab_watcher_t = setInterval(() => {
|
||||
// 上次路径填充
|
||||
getConfig().then(json => {
|
||||
console.log(json.event_type)
|
||||
if (json.event_type != window._mixlab_file_path_watcher) {
|
||||
window._mixlab_file_path_watcher = json.event_type
|
||||
// widget.card.innerText = window._mixlab_file_path_watcher || ''
|
||||
//运行
|
||||
document.querySelector('#queue-button').click()
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
} else {
|
||||
if (window._mixlab_watcher_t) {
|
||||
clearInterval(window._mixlab_watcher_t)
|
||||
}
|
||||
window._mixlab_watcher_t = null
|
||||
}
|
||||
}
|
||||
|
||||
// 上次路径填充
|
||||
getConfig().then(json => {
|
||||
let w = this.widgets.filter(w => w.name == 'file_path')[0]
|
||||
if (w) {
|
||||
w.value = json.folder_path || ''
|
||||
}
|
||||
// console.log(json.event_type)
|
||||
window._mixlab_file_path_watcher = json.event_type
|
||||
})
|
||||
|
||||
/*
|
||||
Add the widget, make sure we clean up nicely, and we do not want to be serialized!
|
||||
*/
|
||||
// this.addCustomWidget(widget)
|
||||
this.onRemoved = function () {
|
||||
// widget.card.remove()
|
||||
}
|
||||
this.serialize_widgets = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,3 @@
|
||||
::-webkit-scrollbar {
|
||||
width: 2px;
|
||||
}
|
||||
@@ -0,0 +1,859 @@
|
||||
{
|
||||
"last_node_id": 23,
|
||||
"last_link_id": 48,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 11,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
1643,
|
||||
295
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 35
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 34
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
41
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "KSampler",
|
||||
"pos": [
|
||||
1267,
|
||||
277
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 44
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 40
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 29
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 30
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
35
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
1088992701378297,
|
||||
"randomize",
|
||||
4,
|
||||
1.6,
|
||||
"lcm",
|
||||
"karras",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"type": "Note",
|
||||
"pos": [
|
||||
-470,
|
||||
840
|
||||
],
|
||||
"size": {
|
||||
"0": 430.924072265625,
|
||||
"1": 253.48239135742188
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"properties": {
|
||||
"text": ""
|
||||
},
|
||||
"widgets_values": [
|
||||
"index_variable needs to be set to 0, indicating the selection of the first image.\n\nnewest_files needs to be enabled, with the newest image placed at the top.\n\nwatcher needs to be enabled to automatically monitor file changes in the folder.\n\nNote: After setting the parameters, please make sure the workflow is running.\n\n\n\nindex_variable 需要设置为0 ,表示选取第一张图。\n\nnewest_files 需要开启,最新的图片排在第一位。\n\nwatcher 开启后,自动监听文件夹里的文件变化。\n\n注意:参数设置好后,请确认工作流已运行"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "FloatingVideo",
|
||||
"pos": [
|
||||
233,
|
||||
814
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 41
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatingVideo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "ControlNetApply",
|
||||
"pos": [
|
||||
1285,
|
||||
656
|
||||
],
|
||||
"size": {
|
||||
"0": 317.4000244140625,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 39
|
||||
},
|
||||
{
|
||||
"name": "control_net",
|
||||
"type": "CONTROL_NET",
|
||||
"link": 38
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 27
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
40
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ControlNetApply"
|
||||
},
|
||||
"widgets_values": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "ScribblePreprocessor",
|
||||
"pos": [
|
||||
914,
|
||||
653
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 47
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
27
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ScribblePreprocessor"
|
||||
},
|
||||
"widgets_values": [
|
||||
512
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "ControlNetLoader",
|
||||
"pos": [
|
||||
922,
|
||||
765
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONTROL_NET",
|
||||
"type": "CONTROL_NET",
|
||||
"links": [
|
||||
38
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ControlNetLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"control_scribble-fp16.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
711,
|
||||
173
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 200
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 33
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
29
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "LoraLoader",
|
||||
"pos": [
|
||||
241,
|
||||
-74
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 126
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 31
|
||||
},
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 45
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
44
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoraLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"lcm-lora-sdv1-5.safetensors",
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
93,
|
||||
163
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
31
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
32,
|
||||
33,
|
||||
45
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
34
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"deliberate_v2.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
-9,
|
||||
812
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 46
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
104,
|
||||
329
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
30
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
678,
|
||||
-77
|
||||
],
|
||||
"size": [
|
||||
400,
|
||||
200
|
||||
],
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 32
|
||||
},
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"link": 48,
|
||||
"widget": {
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
39
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"superman,"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"type": "LoadImagesFromPath",
|
||||
"pos": [
|
||||
-462,
|
||||
582
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 238
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
46,
|
||||
47
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 6
|
||||
},
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
48
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoadImagesFromPath"
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
3,
|
||||
6,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
5,
|
||||
8,
|
||||
0,
|
||||
5,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
6,
|
||||
9,
|
||||
0,
|
||||
5,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
7,
|
||||
10,
|
||||
0,
|
||||
6,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
8,
|
||||
10,
|
||||
1,
|
||||
6,
|
||||
1,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
9,
|
||||
5,
|
||||
0,
|
||||
11,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
10,
|
||||
10,
|
||||
2,
|
||||
11,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
11,
|
||||
6,
|
||||
1,
|
||||
7,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
12,
|
||||
6,
|
||||
1,
|
||||
8,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
14,
|
||||
11,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
17,
|
||||
1,
|
||||
2,
|
||||
7,
|
||||
1,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
18,
|
||||
7,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
19,
|
||||
15,
|
||||
0,
|
||||
5,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
20,
|
||||
16,
|
||||
0,
|
||||
15,
|
||||
1,
|
||||
"CONTROL_NET"
|
||||
],
|
||||
[
|
||||
24,
|
||||
1,
|
||||
0,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
25,
|
||||
18,
|
||||
0,
|
||||
15,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
27,
|
||||
18,
|
||||
0,
|
||||
15,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
29,
|
||||
7,
|
||||
0,
|
||||
5,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
30,
|
||||
9,
|
||||
0,
|
||||
5,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
31,
|
||||
10,
|
||||
0,
|
||||
6,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
32,
|
||||
10,
|
||||
1,
|
||||
8,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
33,
|
||||
10,
|
||||
1,
|
||||
7,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
34,
|
||||
10,
|
||||
2,
|
||||
11,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
35,
|
||||
5,
|
||||
0,
|
||||
11,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
38,
|
||||
16,
|
||||
0,
|
||||
15,
|
||||
1,
|
||||
"CONTROL_NET"
|
||||
],
|
||||
[
|
||||
39,
|
||||
8,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
40,
|
||||
15,
|
||||
0,
|
||||
5,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
41,
|
||||
11,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
44,
|
||||
6,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
45,
|
||||
10,
|
||||
1,
|
||||
6,
|
||||
1,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
46,
|
||||
22,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
47,
|
||||
22,
|
||||
0,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
48,
|
||||
22,
|
||||
2,
|
||||
8,
|
||||
1,
|
||||
"STRING"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"title": "Group",
|
||||
"bounding": [
|
||||
-651,
|
||||
510,
|
||||
1315,
|
||||
798
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
Reference in New Issue
Block a user