Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f13701c489 | ||
|
|
31515b810e | ||
|
|
29e84e08a4 | ||
|
|
0ac9ad9757 | ||
|
|
9a432e0608 | ||
|
|
c83ba5fe7f | ||
|
|
1b55c743ea | ||
|
|
a93579376c | ||
|
|
eba49f3c68 | ||
|
|
3a3da49c69 | ||
|
|
3d68e48219 | ||
|
|
4351fa6a0e | ||
|
|
77222d2808 | ||
|
|
36db7e5a9a | ||
|
|
3572368f16 | ||
|
|
0755dc1462 | ||
|
|
94f81b7102 | ||
|
|
08f8fe3d7e | ||
|
|
a6cd383d67 | ||
|
|
10face6ab0 | ||
|
|
a6259ff600 | ||
|
|
d7d9e6cbfe | ||
|
|
8263609470 | ||
|
|
fc2367de76 | ||
|
|
9a4f2ebc70 | ||
|
|
812879610a | ||
|
|
c5e521ccc1 | ||
|
|
bc1c8fa351 | ||
|
|
7271fcf9c1 | ||
|
|
ace3b7707b | ||
|
|
9eb65cc4ee | ||
|
|
c5c2bc779c | ||
|
|
ae1751d9c0 | ||
|
|
d17583ef7d | ||
|
|
a363713ae0 | ||
|
|
1566165bd4 | ||
|
|
fe065fa318 | ||
|
|
202d5cf071 | ||
|
|
b785a9dc5b | ||
|
|
73bc658b2f | ||
|
|
0b94216138 | ||
|
|
86eec2b4cc | ||
|
|
c99b531d28 | ||
|
|
74a4338cb5 | ||
|
|
d691c52e49 | ||
|
|
bd3e9e4b3c | ||
|
|
3c3ca5fb9c | ||
|
|
e4ff4fce1c | ||
|
|
2918d4b07d | ||
|
|
c40e49be46 | ||
|
|
4ccda20975 | ||
|
|
09957617d3 | ||
|
|
c703aa7058 | ||
|
|
64d366d323 | ||
|
|
333e0a2faa | ||
|
|
a677d95bc8 | ||
|
|
aafd87e84b | ||
|
|
efa3bae54b | ||
|
|
8e4362689d | ||
|
|
b86634284e | ||
|
|
b3293ddccd | ||
|
|
4b40831b83 | ||
|
|
e426b0521c | ||
|
|
1777bf6e06 | ||
|
|
dafc892f0f | ||
|
|
fea0cfd5dd | ||
|
|
a7e158db6d | ||
|
|
455ac4abd3 | ||
|
|
778dfa2cf5 | ||
|
|
329f2e6f81 | ||
|
|
63ad6d97d7 | ||
|
|
8db56db7cf | ||
|
|
bd542f1e0b | ||
|
|
a162e53dea | ||
|
|
a8a4c848ed | ||
|
|
bddd38996a | ||
|
|
9f084eae94 | ||
|
|
c54c635161 | ||
|
|
fa8b42e05e | ||
|
|
9c3c323884 | ||
|
|
c8a46439be | ||
|
|
b7ec701259 | ||
|
|
a2cd0e0a38 | ||
|
|
7bccc0e236 | ||
|
|
a3a649a79f | ||
|
|
2a14d30552 | ||
|
|
313bef0609 | ||
|
|
960a80aeca | ||
|
|
20e6d50a98 | ||
|
|
507c3417d6 | ||
|
|
d1adb8d4ed | ||
|
|
915ff12747 | ||
|
|
fbade79137 | ||
|
|
a240a677d0 | ||
|
|
ab64cf31f6 | ||
|
|
67f2e32dae | ||
|
|
0dc40fe052 | ||
|
|
b7225be552 | ||
|
|
629e00ec94 | ||
|
|
6d033c9314 | ||
|
|
4f8926ed00 | ||
|
|
1daa1a4603 | ||
|
|
062773d929 | ||
|
|
57decadaef | ||
|
|
ec804ab7c9 | ||
|
|
3ee7533098 | ||
|
|
0d383ccc1f | ||
|
|
e2f2257c34 | ||
|
|
d62b9fc4c6 | ||
|
|
be7ad0c7fb | ||
|
|
156864cc8b | ||
|
|
7240e496cc | ||
|
|
8acdf4018d | ||
|
|
1ea7c3e203 | ||
|
|
e54aeb6125 | ||
|
|
d59f51fbcf | ||
|
|
a667eb6982 | ||
|
|
8d72732247 | ||
|
|
f0f3b30a62 | ||
|
|
d99fe24542 | ||
|
|
ea4c7381bd | ||
|
|
e900d20641 | ||
|
|
8a46647d8c | ||
|
|
968178bf57 | ||
|
|
406a255db0 | ||
|
|
574557810e | ||
|
|
998a02c3a4 | ||
|
|
c6f964c921 | ||
|
|
efb0e147c5 | ||
|
|
9cf7356f98 | ||
|
|
af05c43174 | ||
|
|
380c68ff2b | ||
|
|
068b00b99f | ||
|
|
cd6a42ab64 | ||
|
|
f115abec92 | ||
|
|
f0e23cf878 | ||
|
|
a94f11d809 | ||
|
|
38972bea5f | ||
|
|
a761ff552a | ||
|
|
dbeb84ea9a | ||
|
|
0a4938f39a | ||
|
|
b2182c716d | ||
|
|
8253be73f6 | ||
|
|
20318e296e | ||
|
|
cea1b69286 | ||
|
|
ab8aa69389 | ||
|
|
681491f1d0 | ||
|
|
c2fb815074 | ||
|
|
fffa14dc44 | ||
|
|
df37166d42 | ||
|
|
25fa3a8f6a | ||
|
|
b11507c5e6 | ||
|
|
f06d02489f | ||
|
|
c203af2f71 | ||
|
|
c715155a70 | ||
|
|
8163133294 | ||
|
|
765be5dab4 | ||
|
|
7c1523389d | ||
|
|
7a2b1ba166 | ||
|
|
45b4dcfcd0 | ||
|
|
3b2e535566 | ||
|
|
db556d13a3 | ||
|
|
a987063c68 | ||
|
|
4ce30ef899 | ||
|
|
d988282d98 | ||
|
|
695fdf7ceb | ||
|
|
0befe164cc | ||
|
|
d506c68a80 | ||
|
|
c59c429b75 | ||
|
|
c726b6e4a2 | ||
|
|
96075ad4e1 | ||
|
|
7a8dc07a8a | ||
|
|
1fdac0bc09 | ||
|
|
804b942a36 | ||
|
|
6335d4378b | ||
|
|
cfc2189616 | ||
|
|
e06e032701 | ||
|
|
c9499c2c79 | ||
|
|
2bf43541bb | ||
|
|
0386c7266d | ||
|
|
7aa6ed9a5d | ||
|
|
b71325afa5 | ||
|
|
2cc29bdf77 | ||
|
|
b5d602abc4 | ||
|
|
33c45637ac | ||
|
|
662d4478d0 | ||
|
|
31d3809572 | ||
|
|
024ff4a309 | ||
|
|
9ae8d30b6b | ||
|
|
44349c10b0 | ||
|
|
506520a3c4 | ||
|
|
50f7020977 | ||
|
|
02a27a03cc | ||
|
|
4ad6bacf7b | ||
|
|
26ecc0fa44 | ||
|
|
2619befca6 | ||
|
|
ea24f52b13 | ||
|
|
4abfc47346 | ||
|
|
3b95010d06 | ||
|
|
b3c1b96088 | ||
|
|
a9f1326873 | ||
|
|
75a696fb64 | ||
|
|
24aaacba6d | ||
|
|
e3cd7d5f91 | ||
|
|
6359c3f70f |
@@ -1,38 +1,46 @@
|
||||
##
|
||||
v0.7.0 🚀🚗🚚🏃 Workflow-to-APP
|
||||
- 支持多个web app 切换
|
||||
> 适配了最新版comfyui的py3.11 ,torch 2.1.2+cu121
|
||||
|
||||
> [discord](https://discord.gg/cXs9vZSqeK)
|
||||
|
||||
## 🚀🚗🚚🏃 Workflow-to-APP
|
||||
- 新增AppInfo节点,可以通过简单的配置,把workflow转变为一个Web APP。
|
||||
- 支持多个web app 切换
|
||||
- 发布为app的workflow,可以在右键里再次编辑了
|
||||
- web app可以设置分类,在comfyui右键菜单可以编辑更新web app
|
||||
|
||||
|
||||
- Support multiple web app switching.
|
||||
- Add the AppInfo node, which allows you to transform the workflow into a web app by simple configuration.
|
||||
- The workflow, which is now released as an app, can also be edited again by right-clicking.
|
||||
- The web app can be configured with categories, and the web app can be edited and updated in the right-click menu of ComfyUI.
|
||||
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
Example:
|
||||
- workflow
|
||||

|
||||
[text-to-image](./workflow/Text-to-Image-app.json)
|
||||
|
||||
APP-JSON:
|
||||
- [text-to-image](./example/text-to-image_1_Wed%20Dec%2027%202023.json)
|
||||
- [image-to-image](./example/image-to-image_1_Wed%20Dec%2027%202023.json)
|
||||
- [text-to-image](./example/Text-to-Image_3.json)
|
||||
- [image-to-image](./example/Image-to-Image_2.json)
|
||||
- text-to-text
|
||||
|
||||
> 暂时支持6种节点作为界面上的输入节点:Load Image、CLIPTextEncode、TextInput_、FloatSlider、IntNumber、CheckpointLoaderSimple、LoraLoader
|
||||
> 暂时支持8种节点作为界面上的输入节点:Load Image、CLIPTextEncode、PromptSlide、TextInput_、Color、FloatSlider、IntNumber、CheckpointLoaderSimple、LoraLoader
|
||||
|
||||
> 输出节点:PreviewImage 、SaveImage、ShowTextForGPT、VHS_VideoCombine
|
||||
> 输出节点:PreviewImage 、SaveImage、ShowTextForGPT、VHS_VideoCombine、PromptImage
|
||||
|
||||
> seed统一输入控件,支持:SamplerCustom、KSampler
|
||||
|
||||
|
||||
### 3D
|
||||

|
||||
[workflow](./workflow/3D-workflow.json)
|
||||
## 🏃🚗🚚🚀 Real-time Design
|
||||
> ScreenShareNode & FloatingVideoNode. Now comfyui supports capturing screen pixel streams from any software and can be used for LCM-Lora integration. Let's get started with implementation and design! 💻🌐
|
||||
|
||||
|
||||
### ScreenShareNode & FloatingVideoNode
|
||||
> Now comfyui supports capturing screen pixel streams from any software and can be used for LCM-Lora integration. Let's get started with implementation and design! 💻🌐
|
||||
|
||||
>
|
||||

|
||||
|
||||
https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43e-410a-ab3a-1952b7b4e7da
|
||||
@@ -43,6 +51,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
|
||||
!! Please use the address with HTTPS (https://127.0.0.1).
|
||||
|
||||
|
||||
### SpeechRecognition & SpeechSynthesis
|
||||

|
||||
|
||||
@@ -51,11 +60,45 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
### GPT
|
||||
> Support for calling multiple GPTs.ChatGPT、ChatGLM3 , Some code provided by rui. If you are using OpenAI's service, fill in https://api.openai.com/v1 . If you are using a local LLM service, fill in http://127.0.0.1:xxxx/v1 . Azure OpenAI:https://xxxx.openai.azure.com
|
||||
|
||||
|
||||

|
||||
|
||||
[workflow-5](./workflow/5-gpt-workflow.json)
|
||||
|
||||
|
||||
## Prompt
|
||||
> PromptSlide
|
||||

|
||||
|
||||
<!--  -->
|
||||
|
||||
> randomPrompt
|
||||
|
||||

|
||||
|
||||
> ClipInterrogator
|
||||
|
||||
[add clip-interrogator](https://github.com/pharmapsychotic/clip-interrogator)
|
||||
|
||||
> PromptImage & PromptSimplification,Assist in simplifying prompt words, comparing images and prompt word nodes.
|
||||
|
||||
> ChinesePrompt && PromptGenerate,中文prompt节点,直接用中文书写你的prompt
|
||||
|
||||

|
||||
|
||||
|
||||
### Layers
|
||||
> A new layer class node has been added, allowing you to separate the image into layers. After merging the images, you can input the controlnet for further processing.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
### 3D
|
||||

|
||||
[workflow](./workflow/3D-workflow.json)
|
||||
|
||||
|
||||
### LoadImagesFromLocal
|
||||
> Monitor changes to images in a local folder, and trigger real-time execution of workflows, supporting common image formats, especially PSD format, in conjunction with Photoshop.
|
||||
|
||||
@@ -67,13 +110,6 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
> Conveniently load images from a fixed address on the internet to ensure that default images in the workflow can be executed.
|
||||
|
||||
|
||||
### Layers
|
||||
> A new layer class node has been added, allowing you to separate the image into layers. After merging the images, you can input the controlnet for further processing.
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Utils
|
||||
> The Color node provides a color picker for easy color selection, the Font node offers built-in font selection for use with TextImage to generate text images, and the DynamicDelayByText node allows delayed execution based on the length of the input text.
|
||||
|
||||
@@ -81,6 +117,12 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
|
||||
- [Added DynamicDelayByText, enabling delayed execution based on input text length.](./workflow/audio-chatgpt-workflow.json)
|
||||
|
||||
- [使用CkptNames 对比不同的模型效果](./workflow/ckpts-image-workflow.json)
|
||||
|
||||
- [CkptNames compare the effects of different models.](./workflow/ckpts-image-workflow.json)
|
||||
|
||||
|
||||
|
||||
## Other Nodes
|
||||
|
||||

|
||||
@@ -88,9 +130,7 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
||||
|
||||
[workflow-1](./workflow/1-workflow.json)
|
||||
|
||||
> randomPrompt
|
||||
|
||||

|
||||
|
||||
> TransparentImage
|
||||
|
||||
@@ -113,6 +153,10 @@ Add edges to an image.
|
||||

|
||||
|
||||
|
||||
> LaMaInpainting
|
||||
|
||||
from [simple-lama-inpainting](https://github.com/enesmsahin/simple-lama-inpainting)
|
||||
|
||||
|
||||
### Improvement
|
||||
|
||||
@@ -127,12 +171,18 @@ An improvement has been made to directly redirect to GitHub to search for missin
|
||||
|
||||
|
||||
### Models
|
||||
[Download CLIPSeg](https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main), move to : model/clipseg
|
||||
|
||||
<!-- ### Workflow
|
||||
[Workflow](./workflow.md) -->
|
||||
[Download rembg Models](https://github.com/danielgatis/rembg/tree/main#Models),move to:models/rembg
|
||||
|
||||
[Download CLIPSeg](https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main), move to : models/clipseg
|
||||
|
||||
[Download lama](https://github.com/enesmsahin/simple-lama-inpainting/releases/download/v0.1.0/big-lama.pt), move to : models/lama
|
||||
|
||||
[Download Salesforce/blip-image-captioning-base](https://huggingface.co/Salesforce/blip-image-captioning-base), move to : models/clip_interrogator/Salesforce/blip-image-captioning-base
|
||||
|
||||
[Download succinctly/text2image-prompt-generator](https://huggingface.co/succinctly/text2image-prompt-generator/tree/main),move to:prompt_generator/text2image-prompt-generator
|
||||
|
||||
[Download Helsinki-NLP/opus-mt-zh-en](https://huggingface.co/Helsinki-NLP/opus-mt-zh-en/tree/main),move to:prompt_generator/opus-mt-zh-en
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -167,16 +217,12 @@ pip3 install -r requirements.txt
|
||||
访问 [www.mixcomfy.com](https://www.mixcomfy.com),获得更多内测功能,关注微信公众号:Mixlab无界社区
|
||||
|
||||
|
||||
|
||||
#### 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
|
||||
|
||||
|
||||
<picture>
|
||||
|
||||
+199
-46
@@ -133,8 +133,38 @@ def create_for_https():
|
||||
return (crt,key)
|
||||
|
||||
|
||||
|
||||
# workflow 目录下的所有json
|
||||
def read_workflow_json_files_all(folder_path):
|
||||
print('#read_workflow_json_files_all',folder_path)
|
||||
json_files = []
|
||||
for root, dirs, files in os.walk(folder_path):
|
||||
for file in files:
|
||||
if file.endswith('.json'):
|
||||
json_files.append(os.path.join(root, file))
|
||||
|
||||
data = []
|
||||
for file_path in json_files:
|
||||
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': os.path.basename(file_path),
|
||||
'category': os.path.dirname(file_path),
|
||||
'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
|
||||
|
||||
# workflow
|
||||
def read_workflow_json_files(folder_path):
|
||||
def read_workflow_json_files(folder_path ):
|
||||
json_files = []
|
||||
for filename in os.listdir(folder_path):
|
||||
if filename.endswith('.json'):
|
||||
@@ -162,41 +192,66 @@ def read_workflow_json_files(folder_path):
|
||||
def get_workflows():
|
||||
# print("#####path::", current_path)
|
||||
workflow_path=os.path.join(current_path, "workflow")
|
||||
print('workflow_path: ',workflow_path)
|
||||
# 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 get_my_workflow_for_app(filename="my_workflow_app.json"):
|
||||
def get_my_workflow_for_app(filename="my_workflow_app.json",category="",is_all=False):
|
||||
app_path=os.path.join(current_path, "app")
|
||||
if not os.path.exists(app_path):
|
||||
os.mkdir(app_path)
|
||||
|
||||
category_path=os.path.join(app_path,category)
|
||||
if not os.path.exists(category_path):
|
||||
os.mkdir(category_path)
|
||||
|
||||
apps=[]
|
||||
if filename==None:
|
||||
data=read_workflow_json_files(app_path)
|
||||
|
||||
#TODO 支持目录内遍历
|
||||
if is_all:
|
||||
data=read_workflow_json_files_all(category_path)
|
||||
else:
|
||||
data=read_workflow_json_files(category_path)
|
||||
|
||||
i=0
|
||||
for item in data:
|
||||
# print(item)
|
||||
try:
|
||||
x=item["data"]
|
||||
if i==0:
|
||||
apps.append({
|
||||
"filename":item["filename"],
|
||||
# "category":item['category'],
|
||||
"data":x,
|
||||
"date":item["date"]
|
||||
"date":item["date"],
|
||||
})
|
||||
else:
|
||||
category=''
|
||||
input=None
|
||||
output=None
|
||||
if 'category' in x['app']:
|
||||
category=x['app']['category']
|
||||
if 'input' in x['app']:
|
||||
input=x['app']['input']
|
||||
if 'output' in x['app']:
|
||||
output=x['app']['output']
|
||||
apps.append({
|
||||
"filename":item["filename"],
|
||||
"category":category,
|
||||
"data":{
|
||||
"app":{
|
||||
"category":category,
|
||||
"description":x['app']['description'],
|
||||
"filename":(x['app']['filename'] if 'filename' in x['app'] else "") ,
|
||||
"icon":(x['app']['icon'] if 'icon' in x['app'] else None),
|
||||
"name":x['app']['name'],
|
||||
"version":x['app']['version'],
|
||||
"input":input,
|
||||
"output":output
|
||||
}
|
||||
},
|
||||
"date":item["date"]
|
||||
@@ -205,7 +260,7 @@ def get_my_workflow_for_app(filename="my_workflow_app.json"):
|
||||
except Exception as e:
|
||||
print("发生异常:", str(e))
|
||||
else:
|
||||
app_workflow_path=os.path.join(app_path, filename)
|
||||
app_workflow_path=os.path.join(category_path, filename)
|
||||
# print('app_workflow_path: ',app_workflow_path)
|
||||
try:
|
||||
with open(app_workflow_path) as json_file:
|
||||
@@ -216,22 +271,35 @@ def get_my_workflow_for_app(filename="my_workflow_app.json"):
|
||||
except Exception as e:
|
||||
print("发生异常:", str(e))
|
||||
|
||||
if len(apps)==1:
|
||||
data=read_workflow_json_files(app_path)
|
||||
if len(apps)==1 and category!='' and category!=None:
|
||||
data=read_workflow_json_files(category_path)
|
||||
|
||||
for item in data:
|
||||
x=item["data"]
|
||||
print(apps[0]['filename'] ,item["filename"])
|
||||
# print(apps[0]['filename'] ,item["filename"])
|
||||
if apps[0]['filename']!=item["filename"]:
|
||||
category=''
|
||||
input=None
|
||||
output=None
|
||||
if 'category' in x['app']:
|
||||
category=x['app']['category']
|
||||
if 'input' in x['app']:
|
||||
input=x['app']['input']
|
||||
if 'output' in x['app']:
|
||||
output=x['app']['output']
|
||||
apps.append({
|
||||
"filename":item["filename"],
|
||||
# "category":category,
|
||||
"data":{
|
||||
"app":{
|
||||
"category":category,
|
||||
"description":x['app']['description'],
|
||||
"filename":(x['app']['filename'] if 'filename' in x['app'] else "") ,
|
||||
"icon":(x['app']['icon'] if 'icon' in x['app'] else None),
|
||||
"name":x['app']['name'],
|
||||
"version":x['app']['version'],
|
||||
"input":input,
|
||||
"output":output
|
||||
}
|
||||
},
|
||||
"date":item["date"]
|
||||
@@ -245,11 +313,16 @@ def save_workflow_json(data):
|
||||
json.dump(data, file)
|
||||
return workflow_path
|
||||
|
||||
def save_workflow_for_app(data,filename="my_workflow_app.json"):
|
||||
def save_workflow_for_app(data,filename="my_workflow_app.json",category=""):
|
||||
app_path=os.path.join(current_path, "app")
|
||||
if not os.path.exists(app_path):
|
||||
os.mkdir(app_path)
|
||||
app_workflow_path=os.path.join(app_path, filename)
|
||||
|
||||
category_path=os.path.join(app_path,category)
|
||||
if not os.path.exists(category_path):
|
||||
os.mkdir(category_path)
|
||||
|
||||
app_workflow_path=os.path.join(category_path, filename)
|
||||
|
||||
try:
|
||||
output_str = json.dumps(data['output'])
|
||||
@@ -294,33 +367,59 @@ async def new_request(self, method, url, *args, **kwargs):
|
||||
|
||||
# 应用 Monkey Patch
|
||||
aiohttp.ClientSession._request = new_request
|
||||
import socket
|
||||
|
||||
async def check_port_available(address, port):
|
||||
#检查端口是否可用
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
try:
|
||||
sock.bind((address, port))
|
||||
return True
|
||||
except socket.error:
|
||||
return False
|
||||
|
||||
# https
|
||||
async def new_start(self, address, port, verbose=True, call_on_start=None):
|
||||
|
||||
|
||||
try:
|
||||
runner = web.AppRunner(self.app, access_log=None)
|
||||
await runner.setup()
|
||||
|
||||
if not await check_port_available(address, port):
|
||||
raise RuntimeError(f"Port {port} is already in use.")
|
||||
|
||||
site = web.TCPSite(runner, address, port)
|
||||
await site.start()
|
||||
|
||||
import ssl
|
||||
crt,key=create_for_https()
|
||||
crt, key = create_for_https()
|
||||
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||
ssl_context.load_cert_chain(crt,key)
|
||||
site2 = web.TCPSite(runner, address, port+1,ssl_context=ssl_context)
|
||||
await site2.start()
|
||||
ssl_context.load_cert_chain(crt, key)
|
||||
|
||||
success = False
|
||||
for i in range(10): # 尝试最多10次
|
||||
if await check_port_available(address, port + 1 + i):
|
||||
https_port = port + 1 + i
|
||||
site2 = web.TCPSite(runner, address, https_port, ssl_context=ssl_context)
|
||||
await site2.start()
|
||||
success = True
|
||||
break
|
||||
|
||||
if not success:
|
||||
raise RuntimeError(f"Ports {port + 1} to {port + 10} are all in use.")
|
||||
|
||||
if address == '':
|
||||
address = '0.0.0.0'
|
||||
if verbose:
|
||||
# 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))
|
||||
print("\033[93mTo see the GUI go to: https://{}:{}\033[0m".format(address, https_port))
|
||||
if call_on_start is not None:
|
||||
call_on_start(address, port)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error starting the server: {e}")
|
||||
|
||||
# import webbrowser
|
||||
# if os.name == 'nt' and address == '0.0.0.0':
|
||||
# address = '127.0.0.1'
|
||||
@@ -331,7 +430,7 @@ async def new_start(self, address, port, verbose=True, call_on_start=None):
|
||||
PromptServer.start=new_start
|
||||
|
||||
# 创建路由表
|
||||
routes = web.RouteTableDef()
|
||||
routes = PromptServer.instance.routes
|
||||
|
||||
@routes.post('/mixlab')
|
||||
async def mixlab_hander(request):
|
||||
@@ -371,17 +470,26 @@ async def mixlab_workflow_hander(request):
|
||||
'file_path':file_path
|
||||
}
|
||||
elif data['task']=='save_app':
|
||||
file_path=save_workflow_for_app(data['data'],data['filename'])
|
||||
category=""
|
||||
if "category" in data:
|
||||
category=data['category']
|
||||
file_path=save_workflow_for_app(data['data'],data['filename'],category)
|
||||
result={
|
||||
'status':'success',
|
||||
'file_path':file_path
|
||||
}
|
||||
elif data['task']=='my_app':
|
||||
filename=None
|
||||
category=""
|
||||
admin=False
|
||||
if 'filename' in data:
|
||||
filename=data['filename']
|
||||
if 'category' in data:
|
||||
category=data['category']
|
||||
if 'admin' in data:
|
||||
admin=data['admin']
|
||||
result={
|
||||
'data':get_my_workflow_for_app(filename),
|
||||
'data':get_my_workflow_for_app(filename,category,admin),
|
||||
'status':'success',
|
||||
}
|
||||
elif data['task']=='list':
|
||||
@@ -408,22 +516,6 @@ async def nodes_map_hander(request):
|
||||
|
||||
return web.json_response(result)
|
||||
|
||||
# 把插件自定义的路由添加到comfyui server里
|
||||
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
|
||||
|
||||
|
||||
|
||||
# 扩展api接口
|
||||
# from server import PromptServer
|
||||
@@ -437,22 +529,30 @@ PromptServer.add_routes=new_add_routes
|
||||
|
||||
|
||||
# 导入节点
|
||||
from .nodes.PromptNode import RandomPrompt
|
||||
from .nodes.ImageNode import NoiseImage,TransparentImage,LoadImagesFromPath,LoadImagesFromURL,ResizeImage,TextImage,SvgImage,Image3D,ShowLayer,NewLayer,MergeLayers,AreaToMask,SmoothMask,FeatheredMask,SplitLongMask,ImageCropByAlpha,EnhanceImage,FaceToMask
|
||||
from .nodes.PromptNode import EmbeddingPrompt,RandomPrompt,PromptSlide,PromptSimplification,PromptImage,JoinWithDelimiter
|
||||
from .nodes.ImageNode import SaveImageToLocal,SplitImage,GridOutput,GetImageSize_,MirroredImage,ImageColorTransfer,NoiseImage,TransparentImage,GradientImage,LoadImagesFromPath,LoadImagesFromURL,ResizeImage,TextImage,SvgImage,Image3D,ShowLayer,NewLayer,MergeLayers,CenterImage,AreaToMask,SmoothMask,SplitLongMask,ImageCropByAlpha,EnhanceImage,FaceToMask
|
||||
from .nodes.Vae import VAELoader,VAEDecode
|
||||
from .nodes.ScreenShareNode import ScreenShareNode,FloatingVideo
|
||||
from .nodes.Clipseg import CLIPSeg,CombineMasks
|
||||
from .nodes.ChatGPT import ChatGPTNode,ShowTextForGPT,CharacterInText
|
||||
from .nodes.Audio import GamePal,SpeechRecognition,SpeechSynthesis
|
||||
from .nodes.Utils import AppInfo,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,GetImageSize_,MultiplicationNode
|
||||
from .nodes.Utils import CreateLoraNames,CreateSampler_names,CreateCkptNames,CreateSeedNode,TESTNODE_,AppInfo,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,MultiplicationNode
|
||||
from .nodes.Mask import OutlineMask,FeatheredMask
|
||||
|
||||
|
||||
# 要导出的所有节点及其名称的字典
|
||||
# 注意:名称应全局唯一
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"AppInfo":AppInfo,
|
||||
"TESTNODE_":TESTNODE_,
|
||||
"RandomPrompt":RandomPrompt,
|
||||
"EmbeddingPrompt":EmbeddingPrompt,
|
||||
"PromptSlide":PromptSlide,
|
||||
"PromptSimplification":PromptSimplification,
|
||||
"PromptImage":PromptImage,
|
||||
"MirroredImage":MirroredImage,
|
||||
"NoiseImage":NoiseImage,
|
||||
"GradientImage":GradientImage,
|
||||
"TransparentImage":TransparentImage,
|
||||
"ResizeImageMixlab":ResizeImage,
|
||||
"LoadImagesFromPath":LoadImagesFromPath,
|
||||
@@ -461,8 +561,12 @@ NODE_CLASS_MAPPINGS = {
|
||||
"EnhanceImage":EnhanceImage,
|
||||
"SvgImage":SvgImage,
|
||||
"3DImage":Image3D,
|
||||
"ImageColorTransfer":ImageColorTransfer,
|
||||
"ShowLayer":ShowLayer,
|
||||
"NewLayer":NewLayer,
|
||||
"SplitImage":SplitImage,
|
||||
"CenterImage":CenterImage,
|
||||
"GridOutput":GridOutput,
|
||||
"MergeLayers":MergeLayers,
|
||||
"SplitLongMask":SplitLongMask,
|
||||
"FeatheredMask":FeatheredMask,
|
||||
@@ -471,6 +575,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
"AreaToMask":AreaToMask,
|
||||
"ImageCropByAlpha":ImageCropByAlpha,
|
||||
"VAELoaderConsistencyDecoder":VAELoader,
|
||||
"SaveImageToLocal":SaveImageToLocal,
|
||||
"VAEDecodeConsistencyDecoder":VAEDecode,
|
||||
"ScreenShare":ScreenShareNode,
|
||||
"FloatingVideo":FloatingVideo,
|
||||
@@ -492,6 +597,13 @@ NODE_CLASS_MAPPINGS = {
|
||||
"GetImageSize_":GetImageSize_,
|
||||
"SwitchByIndex":SwitchByIndex,
|
||||
"LimitNumber":LimitNumber,
|
||||
"OutlineMask":OutlineMask,
|
||||
"JoinWithDelimiter":JoinWithDelimiter,
|
||||
"Seed_":CreateSeedNode,
|
||||
"CkptNames_":CreateCkptNames,
|
||||
"SamplerNames_":CreateSampler_names,
|
||||
"LoraNames_":CreateLoraNames
|
||||
# "LaMaInpainting":LaMaInpainting
|
||||
# "GamePal":GamePal
|
||||
}
|
||||
|
||||
@@ -511,14 +623,55 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SpeechSynthesis":"SpeechSynthesis ♾️Mixlab",
|
||||
"SpeechRecognition":"SpeechRecognition ♾️Mixlab",
|
||||
"3DImage":"3DImage ♾️Mixlab",
|
||||
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab"
|
||||
|
||||
# "GamePal":"GamePal ♾️Mixlab"
|
||||
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab",
|
||||
"LaMaInpainting":"LaMaInpainting ♾️Mixlab",
|
||||
"PromptSlide":"PromptSlide ♾️Mixlab",
|
||||
"PromptGenerate_Mix":"PromptGenerate ♾️Mixlab",
|
||||
"ChinesePrompt_Mix":"ChinesePrompt ♾️Mixlab",
|
||||
"GamePal":"GamePal ♾️Mixlab",
|
||||
"RembgNode_Mix":"Removebg",
|
||||
"LoraNames_":"LoraName_TriggerWords.safetensors"
|
||||
}
|
||||
|
||||
# web ui的节点功能
|
||||
WEB_DIRECTORY = "./web"
|
||||
|
||||
print('--------------')
|
||||
print('\033[91mMixlab Nodes: \033[93mLoaded\033[0m')
|
||||
print('--------------')
|
||||
print('\033[91m ### Mixlab Nodes: \033[93mLoaded')
|
||||
|
||||
try:
|
||||
from .nodes.Lama import LaMaInpainting
|
||||
print('LaMaInpainting.available',LaMaInpainting.available)
|
||||
if LaMaInpainting.available:
|
||||
NODE_CLASS_MAPPINGS['LaMaInpainting']=LaMaInpainting
|
||||
except Exception as e:
|
||||
print('LaMaInpainting.available',False,e)
|
||||
|
||||
try:
|
||||
from .nodes.ClipInterrogator import ClipInterrogator
|
||||
print('ClipInterrogator.available',ClipInterrogator.available)
|
||||
if ClipInterrogator.available:
|
||||
NODE_CLASS_MAPPINGS['ClipInterrogator']=ClipInterrogator
|
||||
except Exception as e:
|
||||
print('ClipInterrogator.available',False,e)
|
||||
|
||||
try:
|
||||
from .nodes.TextGenerateNode import PromptGenerate,ChinesePrompt
|
||||
print('PromptGenerate.available',PromptGenerate.available)
|
||||
if PromptGenerate.available:
|
||||
NODE_CLASS_MAPPINGS['PromptGenerate_Mix']=PromptGenerate
|
||||
print('ChinesePrompt.available',ChinesePrompt.available)
|
||||
if ChinesePrompt.available:
|
||||
NODE_CLASS_MAPPINGS['ChinesePrompt_Mix']=ChinesePrompt
|
||||
except Exception as e:
|
||||
print('TextGenerateNode.available',False,e)
|
||||
|
||||
try:
|
||||
from .nodes.RembgNode import RembgNode_
|
||||
print('RembgNode_.available',RembgNode_.available)
|
||||
if RembgNode_.available:
|
||||
NODE_CLASS_MAPPINGS['RembgNode_Mix']=RembgNode_
|
||||
except Exception as e:
|
||||
print('RembgNode_.available',False,e)
|
||||
|
||||
print('\033[93m -------------- \033[0m')
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 2.4 MiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 477 KiB |
@@ -0,0 +1,30 @@
|
||||
Jony Ive
|
||||
Dieter Rams
|
||||
Philippe Starck
|
||||
Karim Rashid
|
||||
Yves Béhar
|
||||
Marc Newson
|
||||
Naoto Fukasawa
|
||||
Jonathan Adler
|
||||
Patricia Urquiola
|
||||
Ross Lovegrove
|
||||
Tom Dixon
|
||||
Jasper Morrison
|
||||
Charles Eames
|
||||
Ray Eames
|
||||
Achille Castiglioni
|
||||
Ron Arad
|
||||
Konstantin Grcic
|
||||
Marcel Wanders
|
||||
Maarten Baas
|
||||
Stefan Sagmeister
|
||||
Ingo Maurer
|
||||
Hella Jongerius
|
||||
Sam Hecht
|
||||
Kim Colin
|
||||
Jaime Hayon
|
||||
Michael Anastassiades
|
||||
Nendo
|
||||
Oki Sato
|
||||
Matali Crasset
|
||||
Tokujin Yoshioka
|
||||
@@ -0,0 +1,10 @@
|
||||
Chibi Anime Style
|
||||
Gakuen Anime Style
|
||||
Gekiga Anime Style
|
||||
Jidaimono Anime Style
|
||||
Kawaii Anime Style
|
||||
Mecha Anime Style
|
||||
Realistic Anime Style
|
||||
Semi-Realistic Anime Style
|
||||
Shoji Anime Style
|
||||
Kemonomimi Anime Style
|
||||
+2052
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
||||
GoPro
|
||||
Drone
|
||||
polaroid
|
||||
black and white film
|
||||
Kodachrome
|
||||
shot on 8mm
|
||||
shot on 16mm
|
||||
shot on 35mm
|
||||
Microscopic
|
||||
Fisheye Lens
|
||||
Wide Angle
|
||||
Ultra-Wide Angle
|
||||
Panorama
|
||||
Short Exposure
|
||||
Long Exposure
|
||||
Double Exposure
|
||||
f2.8
|
||||
Depth of Field
|
||||
Soft Focus
|
||||
Deep Focus
|
||||
Shallow Focus
|
||||
Vanishing Point
|
||||
Vantage Point
|
||||
@@ -0,0 +1,30 @@
|
||||
Elegant evening gown
|
||||
Casual jeans and t-shirt
|
||||
Formal black suit
|
||||
Stylish leather jacket
|
||||
Flowy bohemian dress
|
||||
Sporty tracksuit
|
||||
Chic little black dress
|
||||
Trendy ripped jeans
|
||||
Classic white button-down shirt
|
||||
Cozy oversized sweater
|
||||
Sophisticated tailored blazer
|
||||
Quirky patterned leggings
|
||||
Striped sailor top
|
||||
Polished knee-length skirt
|
||||
Vintage-inspired floral dress
|
||||
Edgy motorcycle jacket
|
||||
Preppy polo shirt
|
||||
Boho maxi skirt
|
||||
Professional pinstripe suit
|
||||
Relaxed denim shorts
|
||||
Glamorous sequined dress
|
||||
Athletic running shoes
|
||||
Formal bow tie
|
||||
Casual baseball cap
|
||||
Stylish fedora hat
|
||||
Warm woolen scarf
|
||||
Comfortable cotton socks
|
||||
Trendy ankle boots
|
||||
Cute summer sandals
|
||||
Cozy pajama set
|
||||
@@ -0,0 +1,30 @@
|
||||
Happy
|
||||
Sad
|
||||
Angry
|
||||
Surprised
|
||||
Excited
|
||||
Worried
|
||||
Confused
|
||||
Disgusted
|
||||
Amused
|
||||
Bored
|
||||
Curious
|
||||
Embarrassed
|
||||
Frustrated
|
||||
Nervous
|
||||
Pleased
|
||||
Relieved
|
||||
Shy
|
||||
Tired
|
||||
Serious
|
||||
Silly
|
||||
Proud
|
||||
Grumpy
|
||||
Smug
|
||||
Sarcastic
|
||||
Flirty
|
||||
Skeptical
|
||||
Shocked
|
||||
Blissful
|
||||
Envious
|
||||
Mischievous
|
||||
@@ -4761,19 +4761,31 @@
|
||||
],
|
||||
"https://github.com/shadowcz007/comfyui-mixlab-nodes": [
|
||||
[
|
||||
"GridOutput",
|
||||
"SplitImage",
|
||||
"PromptGenerate_Mix",
|
||||
"JoinWithDelimiter",
|
||||
"ChinesePrompt_Mix",
|
||||
"3DImage",
|
||||
"AppInfo",
|
||||
"IntNumber",
|
||||
"FloatSlider",
|
||||
"ResizeImage",
|
||||
"NoiseImage",
|
||||
"PromptImage",
|
||||
"SaveImageToLocal",
|
||||
"AreaToMask",
|
||||
"CLIPSeg_",
|
||||
"CharacterInText",
|
||||
"ChatGPTOpenAI",
|
||||
"Color",
|
||||
"Seed_",
|
||||
"CkptNames_",
|
||||
"SamplerNames_",
|
||||
"LoraNames_",
|
||||
"CombineMasks_",
|
||||
"EnhanceImage",
|
||||
"GradientImage",
|
||||
"FaceToMask",
|
||||
"FeatheredMask",
|
||||
"FloatingVideo",
|
||||
@@ -4783,7 +4795,11 @@
|
||||
"LoadImagesFromURL",
|
||||
"MergeLayers",
|
||||
"NewLayer",
|
||||
"CenterImage",
|
||||
"RandomPrompt",
|
||||
"PromptSlide",
|
||||
"PromptSimplification",
|
||||
"ClipInterrogator",
|
||||
"ScreenShare",
|
||||
"ShowLayer",
|
||||
"ShowTextForGPT",
|
||||
@@ -4793,12 +4809,15 @@
|
||||
"SplitLongMask",
|
||||
"SvgImage",
|
||||
"TextImage",
|
||||
"ResizeImageMixlab",
|
||||
"TransparentImage",
|
||||
"VAEDecodeConsistencyDecoder",
|
||||
"VAELoaderConsistencyDecoder",
|
||||
"TextToNumber",
|
||||
"TextInput_",
|
||||
"DynamicDelayProcessor"
|
||||
"DynamicDelayProcessor",
|
||||
"LaMaInpainting",
|
||||
"Moondream"
|
||||
],
|
||||
{
|
||||
"title_aux": "comfyui-mixlab-nodes"
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
Mood Lighting
|
||||
Moody Lighting
|
||||
Studio Lighting
|
||||
Cove Lighting
|
||||
Soft Lighting
|
||||
Hard Lighting
|
||||
Volumetric Lighting
|
||||
Low-Key Lighting
|
||||
High-Key Lighting
|
||||
Epic Light
|
||||
Rembrandt Lighting
|
||||
Contre-Jour
|
||||
Veiling Flare
|
||||
Crepuscular Rays
|
||||
Rays of Shimmering Light
|
||||
Godrays
|
||||
@@ -0,0 +1,132 @@
|
||||
Aaron Siskind
|
||||
Alessio Albi
|
||||
Alfred Eisenstaedt
|
||||
Alfred Stieglitz
|
||||
Alyssa Monks
|
||||
André Kertész
|
||||
Andreas Gursky
|
||||
Andrew Wyeth
|
||||
Anne Geddes
|
||||
Annie Leibovitz
|
||||
Ansel Adams
|
||||
Arnold Newman
|
||||
August Sander
|
||||
Balthus
|
||||
Berenice Abbott
|
||||
Bill Brandt
|
||||
Bill Henson
|
||||
Brassaï (Gyula Halász)
|
||||
Brooke Shaden
|
||||
Bruce Davidson
|
||||
Bruce Weber
|
||||
Bunny Yeager
|
||||
Carleton Watkins
|
||||
Carrie Mae Weems
|
||||
Chuck Close
|
||||
Cindy Sherman
|
||||
Clarence H. White
|
||||
Claude Cahun
|
||||
Danny Lyon
|
||||
David LaChapelle
|
||||
Dawoud Bey
|
||||
Diane Arbus
|
||||
Don McCullin
|
||||
Dora Maar
|
||||
Dorothea Lange
|
||||
Duane Michals
|
||||
Eadweard Muybridge
|
||||
Edward Burtynsky
|
||||
Edward Curtis
|
||||
Edward Ruscha
|
||||
Edward Steichen
|
||||
Edward Weston
|
||||
Elliott Erwitt
|
||||
Ernst Haas
|
||||
Eugene Atget
|
||||
Fan Ho
|
||||
Francesca Woodman
|
||||
Frans Lanting
|
||||
Garry Winogrand
|
||||
Georges Melies
|
||||
Gerda Taro
|
||||
Gertrude Käsebier
|
||||
Gordon Parks
|
||||
Graciela Iturbide
|
||||
Gregory Crewdson
|
||||
Harold Edgerton
|
||||
Helen Levitt
|
||||
Helmut Newton
|
||||
Hendrik Kerstens
|
||||
Henri Cartier-Bresson
|
||||
Hugh Kretschmer
|
||||
Irving Penn
|
||||
Jacques Henri Lartigue
|
||||
James Nachtwey
|
||||
James Van Der Zee
|
||||
Jay Maisel
|
||||
Jerry Uelsmann
|
||||
Joel Peter Witkin
|
||||
Joel Sartore
|
||||
John Frederick William Herschel
|
||||
Josef Sudek
|
||||
Julia Margaret Cameron
|
||||
Karl Blossfeldt
|
||||
Larry Burrows
|
||||
László Moholy-Nagy (photography)
|
||||
Lee Jeffries
|
||||
Lewis Hine
|
||||
Lorna Simpson
|
||||
Lynsey Addario
|
||||
Margaret Bourke-White
|
||||
Mario Testino
|
||||
Martin Parr
|
||||
Martin Schoeller
|
||||
Mary Ellen Mark
|
||||
Mathew B. Brady
|
||||
Méret Oppenheim
|
||||
Meryl McMaster
|
||||
Mick Rock
|
||||
Miles Aldridge
|
||||
Minor Martin White
|
||||
Nan Goldin
|
||||
Nathan Wirth
|
||||
Olive Cotton
|
||||
Olivier Rousteing
|
||||
Patrick Demarchelier
|
||||
Paul Nicklen
|
||||
Paul Outerbridge
|
||||
Paul Strand
|
||||
Pete Souza
|
||||
Peter Dombrovskis
|
||||
Peter Henry Emerson
|
||||
Peter Lik
|
||||
Peter Lindbergh
|
||||
Philip-Lorca diCorcia
|
||||
Philippe Halsman
|
||||
Ralph Gibson
|
||||
Richard Avedon
|
||||
Robert Adams
|
||||
Robert Bechtle
|
||||
Robert Capa
|
||||
Robert Frank
|
||||
Robert Mapplethorpe
|
||||
Roger Fenton
|
||||
Ruth Bernhard
|
||||
Sally Mann
|
||||
Sebastião Salgado
|
||||
Shirin Neshat
|
||||
Stefan Gesell
|
||||
Steven Meisel
|
||||
Susan Meiselas
|
||||
Vivian Maier
|
||||
Vivian Maier
|
||||
Viviane Sassen
|
||||
Walker Evans
|
||||
Wes Anderson
|
||||
William Eggleston
|
||||
William Eugene Smith
|
||||
William Henry Fox Talbot
|
||||
Yinka Shonibare
|
||||
Yousuf Karsh
|
||||
Man Ray
|
||||
Robert Mapplethorpe
|
||||
@@ -0,0 +1,101 @@
|
||||
Doctor
|
||||
Teacher
|
||||
Engineer
|
||||
Lawyer
|
||||
Accountant
|
||||
Nurse
|
||||
Architect
|
||||
Chef
|
||||
Pilot
|
||||
Scientist
|
||||
Artist
|
||||
Writer
|
||||
Musician
|
||||
Actor
|
||||
Photographer
|
||||
Police officer
|
||||
Firefighter
|
||||
Dentist
|
||||
Pharmacist
|
||||
Veterinarian
|
||||
Electrician
|
||||
Plumber
|
||||
Carpenter
|
||||
Mechanic
|
||||
Farmer
|
||||
Astronaut
|
||||
Athlete
|
||||
Journalist
|
||||
Politician
|
||||
Economist
|
||||
Psychologist
|
||||
Social worker
|
||||
Librarian
|
||||
Translator
|
||||
Salesperson
|
||||
Entrepreneur
|
||||
Financial advisor
|
||||
Graphic designer
|
||||
Web developer
|
||||
Marketing manager
|
||||
Human resources manager
|
||||
Project manager
|
||||
Event planner
|
||||
Fashion designer
|
||||
Interior decorator
|
||||
Real estate agent
|
||||
Archaeologist
|
||||
Biologist
|
||||
Chemist
|
||||
Geologist
|
||||
Physicist
|
||||
Mathematician
|
||||
Historian
|
||||
Geographer
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
Economist
|
||||
Sociologist
|
||||
Anthropologist
|
||||
Archaeologist
|
||||
Linguist
|
||||
Philosopher
|
||||
Geographer
|
||||
Historian
|
||||
#MixCopilot
|
||||
@@ -0,0 +1,58 @@
|
||||
Residential space
|
||||
Apartment building
|
||||
Villa
|
||||
Bungalow
|
||||
Condominium
|
||||
Commercial space
|
||||
Shopping mall
|
||||
Supermarket
|
||||
Restaurant
|
||||
Store
|
||||
Market
|
||||
Office space
|
||||
Office building
|
||||
Office
|
||||
Meeting room
|
||||
Co-working space
|
||||
Educational space
|
||||
School
|
||||
University
|
||||
Training institution
|
||||
Library
|
||||
Laboratory
|
||||
Medical space
|
||||
Hospital
|
||||
Clinic
|
||||
Pharmacy
|
||||
Nursing home
|
||||
Rehabilitation center
|
||||
Cultural space
|
||||
Museum
|
||||
Library
|
||||
Theater
|
||||
Concert hall
|
||||
Gallery
|
||||
Sports space
|
||||
Sports stadium
|
||||
Gym
|
||||
Swimming pool
|
||||
Basketball court
|
||||
Football field
|
||||
Transportation space
|
||||
Airport
|
||||
Train station
|
||||
Subway station
|
||||
Bus stop
|
||||
Parking lot
|
||||
Public space
|
||||
Park
|
||||
Square
|
||||
Street
|
||||
Pedestrian street
|
||||
Community center
|
||||
Industrial space
|
||||
Factory
|
||||
Warehouse
|
||||
Production workshop
|
||||
Mine
|
||||
Power plant
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
Vintage
|
||||
Grain
|
||||
Sepia
|
||||
High Key
|
||||
Low Key
|
||||
High Dynamic Range
|
||||
Cross Process
|
||||
Radial Blur
|
||||
Infrared
|
||||
Lomo
|
||||
Photocopy
|
||||
Pencil Sketch
|
||||
Pop Art
|
||||
Orton
|
||||
Mosaic
|
||||
Selective Black and White
|
||||
Torn Paper
|
||||
Tilt-Shift
|
||||
Double Exposure
|
||||
Polaroid
|
||||
Liquid Ink
|
||||
Color Splash
|
||||
Sketch
|
||||
Water Drops
|
||||
Polarizer
|
||||
Chinese Painting
|
||||
Water Droplets
|
||||
Polarization
|
||||
Color Inversion
|
||||
Fish-eye
|
||||
Soft Focus
|
||||
Solarization
|
||||
Posterize
|
||||
Comic Book
|
||||
Duotone
|
||||
Gradient Map
|
||||
Edge Detection
|
||||
Oil Painting
|
||||
Reflection
|
||||
Mirror
|
||||
ASCII Art
|
||||
Glitch
|
||||
Time-Lapse
|
||||
Day to Night
|
||||
Surreal
|
||||
Black and White
|
||||
Sepia Tone
|
||||
Vintage Film
|
||||
Grainy Texture
|
||||
High Key Lighting
|
||||
Low Key Lighting
|
||||
Cross Processed Film
|
||||
Infrared Photography
|
||||
Photocopy
|
||||
Pencil Drawing
|
||||
Pop Art Filter
|
||||
Mosaic Filter
|
||||
Selective Desaturation
|
||||
Torn Paper
|
||||
Tilt-Shift Photography
|
||||
Double Exposure
|
||||
Polaroid Style Frame
|
||||
Water Drops Texture
|
||||
Polarizer
|
||||
Chinese Painting
|
||||
Water Droplets Texture
|
||||
Polarization
|
||||
Color Inversion
|
||||
Fish-eye Lens
|
||||
Soft Focus
|
||||
Solarize Filter
|
||||
Edge Detection
|
||||
Oil Painting
|
||||
Reflection
|
||||
Mirror Image
|
||||
Time-Lapse Photography
|
||||
Day to Night Transition
|
||||
Surreal Art Style
|
||||
Abstract Expressionism
|
||||
Acrylic Painting
|
||||
Anime
|
||||
Art Deco
|
||||
Biomorphic Abstraction
|
||||
Black and White Photograph
|
||||
Cartoon
|
||||
Charcoal Sketch
|
||||
Chibi Anime
|
||||
Chinese Painting
|
||||
Classicist Painting
|
||||
Collage
|
||||
Concept Art
|
||||
Cyberpunk
|
||||
Dada Art
|
||||
Digital Art
|
||||
Fantasy Art
|
||||
Fashion Art
|
||||
Fashion Sketch
|
||||
Fish-Eye lens Photograph
|
||||
Goth Art
|
||||
Graffiti
|
||||
Harlem Renaissance
|
||||
High Key Photograph
|
||||
Hyperrealist Pencil Sketch
|
||||
Impressionist Painting
|
||||
Josei Anime
|
||||
Long Exposure Photograph
|
||||
Low Key Photograph
|
||||
Macro Photograph
|
||||
Manga
|
||||
Metal Sculpture
|
||||
Mid Century Modern Illustration
|
||||
Mixed Media
|
||||
Modern Art
|
||||
Moe Anime
|
||||
Nihonga
|
||||
Origami
|
||||
Paper Mache
|
||||
Pen and Ink
|
||||
Pencil Sketch
|
||||
Photograph
|
||||
Photorealism
|
||||
Pinup Art
|
||||
Romanticist Painting
|
||||
Sci-Fi Art
|
||||
Semi Realistic Fantasy Art
|
||||
Semi Realistic Cyberpunk Art
|
||||
Shallow Depth of Field Photograph
|
||||
Steam Punk Art
|
||||
Stone Sculpture
|
||||
Superhero Comic
|
||||
Surrealist Art
|
||||
Tempura Painting
|
||||
Underground Comic
|
||||
Watercolor Painting
|
||||
Zulu Urban Art
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+3
-3
@@ -24,7 +24,7 @@ class SpeechRecognition:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/audio"
|
||||
CATEGORY = "♾️Mixlab/Audio"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
@@ -48,7 +48,7 @@ class SpeechSynthesis:
|
||||
OUTPUT_NODE = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
CATEGORY = "♾️Mixlab/audio"
|
||||
CATEGORY = "♾️Mixlab/Audio"
|
||||
|
||||
def run(self, text):
|
||||
# print(session_history)
|
||||
@@ -82,7 +82,7 @@ class GamePal:
|
||||
OUTPUT_NODE = True
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
CATEGORY = "♾️Mixlab/audio"
|
||||
CATEGORY = "♾️Mixlab/Audio"
|
||||
|
||||
def run(self, input_text,input_num,python_code):
|
||||
exec(python_code)
|
||||
|
||||
+2
-1
@@ -180,8 +180,9 @@ class ShowTextForGPT:
|
||||
CATEGORY = "♾️Mixlab/GPT"
|
||||
|
||||
def run(self, text):
|
||||
# print(session_history)
|
||||
# print(text)
|
||||
return {"ui": {"text": text}, "result": (text,)}
|
||||
|
||||
|
||||
|
||||
class CharacterInText:
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
import os,sys
|
||||
import folder_paths
|
||||
|
||||
from PIL import Image
|
||||
import importlib.util
|
||||
|
||||
import comfy.utils
|
||||
import numpy as np
|
||||
import json
|
||||
import torch
|
||||
import random
|
||||
|
||||
|
||||
# from clip_interrogator import Config, Interrogator
|
||||
|
||||
|
||||
global _available
|
||||
_available=False
|
||||
|
||||
def is_installed(package):
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
return spec is not None
|
||||
|
||||
|
||||
try:
|
||||
if is_installed('clip_interrogator')==False:
|
||||
import subprocess
|
||||
|
||||
# 安装
|
||||
print('#pip install clip-interrogator==0.6.0')
|
||||
|
||||
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'clip-interrogator==0.6.0'], capture_output=True, text=True)
|
||||
|
||||
#检查命令执行结果
|
||||
if result.returncode == 0:
|
||||
print("#install success")
|
||||
from clip_interrogator import Config, Interrogator
|
||||
_available=True
|
||||
else:
|
||||
print("#install error")
|
||||
|
||||
else:
|
||||
from clip_interrogator import Config, Interrogator
|
||||
_available=True
|
||||
|
||||
except:
|
||||
_available=False
|
||||
|
||||
try:
|
||||
from transformers import AutoProcessor, BlipForConditionalGeneration
|
||||
except:
|
||||
_available=False
|
||||
print('pls check transformers.__version__>=4.36.0:: AutoProcessor, BlipForConditionalGeneration')
|
||||
|
||||
|
||||
|
||||
def load_caption_model(model_path,config,t='blip-base'):
|
||||
dtype=torch.float16 if config.device == 'cuda' else torch.float32
|
||||
caption_model = BlipForConditionalGeneration.from_pretrained(model_path, torch_dtype=dtype)
|
||||
|
||||
caption_processor = AutoProcessor.from_pretrained(model_path)
|
||||
|
||||
caption_model.eval()
|
||||
if not config.caption_offload:
|
||||
caption_model = caption_model.to(config.device)
|
||||
|
||||
return (caption_model,caption_processor)
|
||||
|
||||
|
||||
|
||||
caption_model_path=os.path.join(folder_paths.models_dir, "clip_interrogator/Salesforce/blip-image-captioning-base")
|
||||
if not os.path.exists(caption_model_path):
|
||||
print(f"## clip_interrogator_model not found: {caption_model_path}, pls download from https://huggingface.co/Salesforce/blip-image-captioning-base")
|
||||
caption_model_path='Salesforce/blip-image-captioning-base'
|
||||
|
||||
cache_path=os.path.join(folder_paths.models_dir, "clip_interrogator")
|
||||
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
# Convert PIL to Tensor
|
||||
def pil2tensor(image):
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
|
||||
def image_analysis_fn(ci,image):
|
||||
image = image.convert('RGB')
|
||||
image_features = ci.image_to_features(image)
|
||||
|
||||
top_mediums = ci.mediums.rank(image_features, 5)
|
||||
top_artists = ci.artists.rank(image_features, 5)
|
||||
top_movements = ci.movements.rank(image_features, 5)
|
||||
top_trendings = ci.trendings.rank(image_features, 5)
|
||||
top_flavors = ci.flavors.rank(image_features, 5)
|
||||
|
||||
medium_ranks = {medium: sim for medium, sim in zip(top_mediums, ci.similarities(image_features, top_mediums))}
|
||||
artist_ranks = {artist: sim for artist, sim in zip(top_artists, ci.similarities(image_features, top_artists))}
|
||||
movement_ranks = {movement: sim for movement, sim in zip(top_movements, ci.similarities(image_features, top_movements))}
|
||||
trending_ranks = {trending: sim for trending, sim in zip(top_trendings, ci.similarities(image_features, top_trendings))}
|
||||
flavor_ranks = {flavor: sim for flavor, sim in zip(top_flavors, ci.similarities(image_features, top_flavors))}
|
||||
|
||||
return {
|
||||
"medium_ranks":medium_ranks,
|
||||
"artist_ranks":artist_ranks,
|
||||
"movement_ranks":movement_ranks,
|
||||
"trending_ranks":trending_ranks,
|
||||
"flavor_ranks":flavor_ranks
|
||||
}
|
||||
|
||||
|
||||
def generate_sentences(data):
|
||||
sentences = []
|
||||
|
||||
# Get the length of data
|
||||
data_length = len(data)
|
||||
|
||||
# Use a recursive function to handle variable-length data
|
||||
def generate_recursive(index, current_sentence, current_score):
|
||||
# Check if recursion is complete
|
||||
if index == data_length:
|
||||
sentences.append({"sentence": current_sentence, "score": current_score})
|
||||
return
|
||||
|
||||
# Get the current level data
|
||||
current_data = data[index]
|
||||
|
||||
# Iterate through the current level data
|
||||
for phrase in current_data:
|
||||
sentence = current_sentence + ("," if current_sentence.strip() else "") + phrase
|
||||
score = current_score + current_data[phrase]
|
||||
generate_recursive(index + 1, sentence, score)
|
||||
|
||||
# Start recursive generation of sentences
|
||||
generate_recursive(0, "", 0)
|
||||
|
||||
# Sort the generated sentences by score in descending order
|
||||
sentences.sort(key=lambda x: x["score"], reverse=True)
|
||||
|
||||
def get_random_elements(elements, num):
|
||||
return random.sample(elements, num)
|
||||
|
||||
ps = get_random_elements(sentences, 5)
|
||||
ps = [s["sentence"] for s in sorted(ps, key=lambda x: x["score"], reverse=True)]
|
||||
|
||||
return ps
|
||||
|
||||
|
||||
|
||||
|
||||
def image_to_prompt(ci,image, mode):
|
||||
ci.config.chunk_size = 2048 if ci.config.clip_model_name == "ViT-L-14/openai" else 1024
|
||||
ci.config.flavor_intermediate_count = 2048 if ci.config.clip_model_name == "ViT-L-14/openai" else 1024
|
||||
image = image.convert('RGB')
|
||||
if mode == 'best':
|
||||
return ci.interrogate(image)
|
||||
elif mode == 'classic':
|
||||
return ci.interrogate_classic(image)
|
||||
elif mode == 'fast':
|
||||
return ci.interrogate_fast(image)
|
||||
elif mode == 'negative':
|
||||
return ci.interrogate_negative(image)
|
||||
|
||||
# image = Image.open(image_path).convert('RGB')
|
||||
# ci = Interrogator(Config(clip_model_name="ViT-L-14/openai"))
|
||||
# print(ci.interrogate(image))
|
||||
|
||||
|
||||
class ClipInterrogator:
|
||||
|
||||
global _available
|
||||
available=_available
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"image": ("IMAGE",),
|
||||
"prompt_mode": (['fast','classic','best','negative'],),
|
||||
"image_analysis": (["off","on"],),
|
||||
},
|
||||
|
||||
# "optional":{
|
||||
# "output":("CLIPINTERROGATOR", {"multiline": True,"default": "", "dynamicPrompts": False})
|
||||
# },
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING","STRING",)
|
||||
RETURN_NAMES = ("prompt","random_samples",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,True,)
|
||||
global ci
|
||||
ci = None
|
||||
def run(self,image,prompt_mode,image_analysis):
|
||||
global ci
|
||||
|
||||
prompt_mode=prompt_mode[0]
|
||||
analysis=image_analysis[0]
|
||||
|
||||
prompt_result=[]
|
||||
analysis_result=[]
|
||||
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(image)*(2 if analysis=='on' else 1))
|
||||
|
||||
if ci==None:
|
||||
config=Config(
|
||||
clip_model_name="ViT-L-14/openai",
|
||||
device="cuda" if torch.cuda.is_available() else "cpu",
|
||||
download_cache=True,
|
||||
clip_model_path=cache_path,
|
||||
cache_path=cache_path
|
||||
)
|
||||
config.apply_low_vram_defaults()
|
||||
|
||||
caption_model,caption_processor=load_caption_model(caption_model_path,config)
|
||||
|
||||
config.caption_model= caption_model
|
||||
config.caption_processor= caption_processor
|
||||
|
||||
ci = Interrogator(config)
|
||||
# else:
|
||||
# simple_lama.model.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
for i in range(len(image)):
|
||||
im=image[i]
|
||||
|
||||
im=tensor2pil(im)
|
||||
im=im.convert('RGB')
|
||||
|
||||
if analysis=='on':
|
||||
analysis_res=image_analysis_fn(ci,im)
|
||||
analysis_result.append( analysis_res )
|
||||
pbar.update(1)
|
||||
|
||||
prompt=image_to_prompt(ci,im,prompt_mode)
|
||||
pbar.update(1)
|
||||
prompt_result.append(prompt)
|
||||
|
||||
|
||||
# result.save("inpainted.png")
|
||||
if ci.config.clip_offload and not ci.clip_offloaded:
|
||||
ci.clip_model = ci.clip_model.to('cpu')
|
||||
ci.clip_offloaded = True
|
||||
|
||||
if ci.config.caption_offload and not ci.caption_offloaded:
|
||||
ci.caption_model = ci.caption_model.to('cpu')
|
||||
ci.caption_offloaded = True
|
||||
|
||||
# analysis_result=[]
|
||||
# items = app.graph.getNodeById(31).widgets[2].value["items"]
|
||||
|
||||
random_samples=[]
|
||||
|
||||
for r in analysis_result:
|
||||
random_sample = generate_sentences([r['medium_ranks'], r['artist_ranks'],r['movement_ranks'],r['trending_ranks'],r['flavor_ranks']])
|
||||
for s in random_sample:
|
||||
random_samples.append(s)
|
||||
# print(len(random_samples))
|
||||
# print('-----')
|
||||
# print( random_samples)
|
||||
return {
|
||||
"ui":{
|
||||
"prompt": prompt_result,
|
||||
"analysis":analysis_result,
|
||||
"random_samples":random_samples
|
||||
},
|
||||
"result": (prompt_result,random_samples,)}
|
||||
+7
-7
@@ -31,11 +31,11 @@ logger = logging.getLogger('CLIPSeg nodes')
|
||||
clipseg_model_dir = os.path.join(folder_paths.models_dir, "clipseg")
|
||||
|
||||
if not os.path.exists(clipseg_model_dir):
|
||||
print(f"## clipseg model not found: {clipseg_model_dir},pls download from https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main")
|
||||
clipseg_model_dir='CIDAS/clipseg-rd64-refined'
|
||||
|
||||
"""Helper methods for CLIPSeg nodes"""
|
||||
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
@@ -101,18 +101,18 @@ class CLIPSeg:
|
||||
return {"required":
|
||||
{
|
||||
"image": ("IMAGE",),
|
||||
"text": ("STRING", {"multiline": False}),
|
||||
"text": ("STRING", {"multiline": False,"dynamicPrompts": False}),
|
||||
|
||||
},
|
||||
"optional":
|
||||
{
|
||||
"blur": ("FLOAT", {"min": 0, "max": 15, "step": 0.1, "default": 7}),
|
||||
"threshold": ("FLOAT", {"min": 0, "max": 1, "step": 0.05, "default": 0.4}),
|
||||
"blur": ("FLOAT", {"min": 0, "max": 15, "step": 0.1, "default": 3}),
|
||||
"threshold": ("FLOAT", {"min": 0, "max": 1, "step": 0.05, "default": 0.3}),
|
||||
"dilation_factor": ("INT", {"min": 0, "max": 10, "step": 1, "default": 4}),
|
||||
}
|
||||
}
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
RETURN_TYPES = ("MASK", "IMAGE", "IMAGE",)
|
||||
RETURN_NAMES = ("Mask","Heatmap Mask", "BW Mask")
|
||||
|
||||
@@ -218,7 +218,7 @@ class CombineMasks:
|
||||
},
|
||||
}
|
||||
|
||||
CATEGORY = "♾️Mixlab/mask"
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
RETURN_TYPES = ("MASK", "IMAGE", "IMAGE",)
|
||||
RETURN_NAMES = ("Combined Mask","Heatmap Mask", "BW Mask")
|
||||
|
||||
@@ -246,7 +246,7 @@ class CombineMasks:
|
||||
|
||||
# Resize heatmap and binary mask to match the original image dimensions
|
||||
dimensions = (image_np.shape[1], image_np.shape[0])
|
||||
print('heatmap',heatmap)
|
||||
# print('heatmap',heatmap)
|
||||
if dimensions is None or dimensions[0] == 0 or dimensions[1] == 0:
|
||||
raise ValueError("Invalid dimensions")
|
||||
|
||||
|
||||
+908
-185
File diff suppressed because it is too large
Load Diff
+120
@@ -0,0 +1,120 @@
|
||||
import os,sys
|
||||
import folder_paths
|
||||
|
||||
from PIL import Image
|
||||
import importlib.util
|
||||
import numpy as np
|
||||
|
||||
import torch
|
||||
|
||||
global _available
|
||||
_available=False
|
||||
|
||||
def is_installed(package):
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
return spec is not None
|
||||
|
||||
if is_installed('simple_lama_inpainting')==False:
|
||||
import subprocess
|
||||
from packaging import version
|
||||
|
||||
if version.parse(torch.__version__)>=version.parse('2.1'):
|
||||
# 安装
|
||||
print('#pip install simple_lama_inpainting')
|
||||
|
||||
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'simple_lama_inpainting'], capture_output=True, text=True)
|
||||
|
||||
#检查命令执行结果
|
||||
if result.returncode == 0:
|
||||
print("#install success")
|
||||
from simple_lama_inpainting import SimpleLama
|
||||
_available=True
|
||||
else:
|
||||
print("#install error")
|
||||
else:
|
||||
print('#pls check your torch version >= 2.1')
|
||||
|
||||
else:
|
||||
from simple_lama_inpainting import SimpleLama
|
||||
_available=True
|
||||
|
||||
|
||||
|
||||
llma_model_path=os.path.join(folder_paths.models_dir, "lama/big-lama.pt")
|
||||
if not os.path.exists(llma_model_path):
|
||||
os.environ['LAMA_MODEL']=''
|
||||
print(f"## lama torchscript model not found: {llma_model_path},pls download from https://github.com/enesmsahin/simple-lama-inpainting/releases/download/v0.1.0/big-lama.pt")
|
||||
else:
|
||||
os.environ['LAMA_MODEL'] = llma_model_path
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
# Convert PIL to Tensor
|
||||
def pil2tensor(image):
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
|
||||
# simple_lama = SimpleLama()
|
||||
|
||||
# img_path = "image.png"
|
||||
# mask_path = "mask.png"
|
||||
|
||||
# image = Image.open(img_path)
|
||||
# mask = Image.open(mask_path).convert('L')
|
||||
|
||||
# result = simple_lama(image, mask)
|
||||
# result.save("inpainted.png")
|
||||
|
||||
|
||||
class LaMaInpainting:
|
||||
global _available
|
||||
available=_available
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"image": ("IMAGE",),
|
||||
"mask": ("MASK",),
|
||||
},
|
||||
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
global simple_lama
|
||||
simple_lama = None
|
||||
def run(self,image,mask):
|
||||
global simple_lama
|
||||
|
||||
result=[]
|
||||
if simple_lama==None:
|
||||
simple_lama = SimpleLama()
|
||||
else:
|
||||
simple_lama.model.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
for i in range(len(image)):
|
||||
im=image[i]
|
||||
ma=mask[i]
|
||||
im=tensor2pil(im)
|
||||
ma=tensor2pil(ma)
|
||||
ma =ma.convert('L')
|
||||
|
||||
res = simple_lama(im, ma)
|
||||
res=pil2tensor(res)
|
||||
result.append(res)
|
||||
# result.save("inpainted.png")
|
||||
if simple_lama.device=='cuda':
|
||||
simple_lama.model.to('cpu')
|
||||
|
||||
return (result,)
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
|
||||
import scipy.ndimage
|
||||
import torch
|
||||
|
||||
from nodes import MAX_RESOLUTION
|
||||
|
||||
import numpy as np
|
||||
# from PIL import Image, ImageDraw
|
||||
from PIL import Image, ImageOps
|
||||
|
||||
from comfy.cli_args import args
|
||||
import cv2
|
||||
|
||||
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
# Convert PIL to Tensor
|
||||
def pil2tensor(image):
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
|
||||
def grow(mask, expand, tapered_corners):
|
||||
c = 0 if tapered_corners else 1
|
||||
kernel = np.array([[c, 1, c],
|
||||
[1, 1, 1],
|
||||
[c, 1, c]])
|
||||
mask = mask.reshape((-1, mask.shape[-2], mask.shape[-1]))
|
||||
out = []
|
||||
for m in mask:
|
||||
output = m.numpy()
|
||||
for _ in range(abs(expand)):
|
||||
if expand < 0:
|
||||
output = scipy.ndimage.grey_erosion(output, footprint=kernel)
|
||||
else:
|
||||
output = scipy.ndimage.grey_dilation(output, footprint=kernel)
|
||||
output = torch.from_numpy(output)
|
||||
out.append(output)
|
||||
return torch.stack(out, dim=0)
|
||||
|
||||
def combine(destination, source, x, y):
|
||||
output = destination.reshape((-1, destination.shape[-2], destination.shape[-1])).clone()
|
||||
source = source.reshape((-1, source.shape[-2], source.shape[-1]))
|
||||
|
||||
left, top = (x, y,)
|
||||
right, bottom = (min(left + source.shape[-1], destination.shape[-1]), min(top + source.shape[-2], destination.shape[-2]))
|
||||
visible_width, visible_height = (right - left, bottom - top,)
|
||||
|
||||
source_portion = source[:, :visible_height, :visible_width]
|
||||
destination_portion = destination[:, top:bottom, left:right]
|
||||
|
||||
#operation == "subtract":
|
||||
output[:, top:bottom, left:right] = destination_portion - source_portion
|
||||
|
||||
output = torch.clamp(output, 0.0, 1.0)
|
||||
|
||||
return output
|
||||
|
||||
class OutlineMask:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK",),
|
||||
"outline_width":("INT", {"default": 10,"min": 1, "max": MAX_RESOLUTION, "step": 1}),
|
||||
"tapered_corners": ("BOOLEAN", {"default": True}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ('MASK',)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
# 运行的函数
|
||||
def run(self, mask, outline_width, tapered_corners):
|
||||
|
||||
m1=grow(mask,outline_width,tapered_corners)
|
||||
m2=grow(mask,-outline_width,tapered_corners)
|
||||
|
||||
m3=combine(m1,m2,0,0)
|
||||
|
||||
return (m3,)
|
||||
|
||||
|
||||
|
||||
|
||||
class FeatheredMask:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"mask": ("MASK",),
|
||||
"start_offset":("INT", {"default": 1,
|
||||
"min": -150,
|
||||
"max": 150,
|
||||
"step": 1,
|
||||
"display": "slider"}),
|
||||
"feathering_weight":("FLOAT", {"default": 0.1,
|
||||
"min": 0.0,
|
||||
"max": 1,
|
||||
"step": 0.1,
|
||||
"display": "slider"})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ('MASK',)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
# 运行的函数
|
||||
def run(self,mask,start_offset, feathering_weight):
|
||||
# print(mask.shape,mask.size())
|
||||
|
||||
num,_,_=mask.size()
|
||||
|
||||
masks=[]
|
||||
|
||||
for i in range(num):
|
||||
mm=mask[i]
|
||||
image=tensor2pil(mm)
|
||||
|
||||
# Open the image using PIL
|
||||
image = image.convert("L")
|
||||
if start_offset>0:
|
||||
image=ImageOps.invert(image)
|
||||
|
||||
# Convert the image to a numpy array
|
||||
image_np = np.array(image)
|
||||
|
||||
# Use Canny edge detection to get black contours
|
||||
edges = cv2.Canny(image_np, 30, 150)
|
||||
|
||||
for i in range(0,abs(start_offset)):
|
||||
# int(100*feathering_weight)
|
||||
a=int(abs(start_offset)*0.1*i)
|
||||
# Dilate the black contours to make them wider
|
||||
kernel = np.ones((a, a), np.uint8)
|
||||
|
||||
dilated_edges = cv2.dilate(edges, kernel, iterations=1)
|
||||
# dilated_edges = cv2.erode(edges, kernel, iterations=1)
|
||||
# Smooth the dilated edges using Gaussian blur
|
||||
smoothed_edges = cv2.GaussianBlur(dilated_edges, (5, 5), 0)
|
||||
|
||||
# Adjust the feathering weight
|
||||
feathering_weight = max(0, min(feathering_weight, 1))
|
||||
|
||||
# Blend the smoothed edges with the original image to achieve feathering effect
|
||||
image_np = cv2.addWeighted(image_np, 1, smoothed_edges, feathering_weight, feathering_weight)
|
||||
|
||||
# Convert the result back to PIL image
|
||||
result_image = Image.fromarray(np.uint8(image_np))
|
||||
result_image=result_image.convert("L")
|
||||
|
||||
if start_offset>0:
|
||||
result_image=ImageOps.invert(result_image)
|
||||
|
||||
result_image=result_image.convert("L")
|
||||
mt=pil2tensor(result_image)
|
||||
masks.append(mt)
|
||||
|
||||
# print( mt.size())
|
||||
return (masks,)
|
||||
+316
-52
@@ -1,15 +1,40 @@
|
||||
import random
|
||||
import comfy.utils
|
||||
import json
|
||||
import os
|
||||
import numpy as np
|
||||
from urllib import request, parse
|
||||
import folder_paths
|
||||
from PIL import Image, ImageOps,ImageFilter,ImageEnhance,ImageDraw,ImageSequence, ImageFont
|
||||
from PIL.PngImagePlugin import PngInfo
|
||||
# def queue_prompt(prompt_workflow):
|
||||
# p = {"prompt": prompt_workflow}
|
||||
# data = json.dumps(p).encode('utf-8')
|
||||
# req = request.Request("http://127.0.0.1:8188/prompt", data=data)
|
||||
# request.urlopen(req)
|
||||
|
||||
embeddings_path=os.path.join(folder_paths.models_dir, "embeddings")
|
||||
|
||||
def get_files_with_extension(directory, extension):
|
||||
file_list = []
|
||||
for root, dirs, files in os.walk(directory):
|
||||
for file in files:
|
||||
if file.endswith(extension):
|
||||
file_name = os.path.splitext(file)[0]
|
||||
file_list.append(file_name)
|
||||
return file_list
|
||||
|
||||
def join_with_(text_list,delimiter):
|
||||
joined_text = delimiter.join(text_list)
|
||||
return joined_text
|
||||
|
||||
|
||||
class AnyType(str):
|
||||
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
||||
|
||||
def queue_prompt(prompt_workflow):
|
||||
p = {"prompt": prompt_workflow}
|
||||
data = json.dumps(p).encode('utf-8')
|
||||
req = request.Request("http://127.0.0.1:8188/prompt", data=data)
|
||||
request.urlopen(req)
|
||||
def __ne__(self, __value: object) -> bool:
|
||||
return False
|
||||
|
||||
any_type = AnyType("*")
|
||||
|
||||
|
||||
default_prompt1='''Swing
|
||||
@@ -45,6 +70,232 @@ default_prompt1='''Swing
|
||||
'''
|
||||
default_prompt1="\n".join([p.strip() for p in default_prompt1.split('\n') if p.strip()!=''])
|
||||
|
||||
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
def addWeight(text, weight=1):
|
||||
if weight == 1:
|
||||
return text
|
||||
else:
|
||||
return f"({text}:{round(weight,3)})"
|
||||
|
||||
def prompt_delete_words(sentence, new_words_length):
|
||||
# 使用逗号分割句子,并去除空格
|
||||
words = [word.strip() for word in sentence.split(",")]
|
||||
|
||||
# 计算需要删除的单词数量
|
||||
num_to_delete = len(words) - new_words_length
|
||||
|
||||
words_to=[w for w in words]
|
||||
|
||||
# 逐个删除单词并存储在新列表中
|
||||
new_words = []
|
||||
for i in range(len(words)):
|
||||
if num_to_delete > 0:
|
||||
num_to_delete -= 1
|
||||
else:
|
||||
words_to.pop()
|
||||
if len(words_to)>0:
|
||||
new_words.append(", ".join(words_to))
|
||||
|
||||
return new_words
|
||||
|
||||
# # 测试方法
|
||||
# sentence = "a computer, a glass tablet with a keyboard on a dark background, 3d illustration, reflection, cgi 8k, clear glass, archaic, cut-away, white outline"
|
||||
# new_words_length = 5
|
||||
# result = prompt_delete_words(sentence, new_words_length)
|
||||
# print(result)
|
||||
|
||||
class PromptImage:
|
||||
def __init__(self):
|
||||
self.output_dir = folder_paths.get_output_directory()
|
||||
self.type = "output"
|
||||
self.prefix_append = "PromptImage"
|
||||
self.compress_level = 4
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"prompts": ("STRING",
|
||||
{
|
||||
"multiline": True,
|
||||
"default": '',
|
||||
"dynamicPrompts": False
|
||||
}),
|
||||
|
||||
"images": ("IMAGE",{"default": None}),
|
||||
"save_to_image": (["enable", "disable"],),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ()
|
||||
|
||||
OUTPUT_NODE = True
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
# 运行的函数
|
||||
def run(self,prompts,images,save_to_image):
|
||||
filename_prefix="mixlab_"
|
||||
filename_prefix += self.prefix_append
|
||||
full_output_folder, filename, counter, subfolder, filename_prefix = folder_paths.get_save_image_path(
|
||||
filename_prefix, self.output_dir, images[0].shape[1], images[0].shape[0])
|
||||
|
||||
results = list()
|
||||
|
||||
save_to_image=save_to_image[0]=='enable'
|
||||
|
||||
for index in range(len(images)):
|
||||
res=[]
|
||||
imgs=images[index]
|
||||
|
||||
for image in imgs:
|
||||
img=tensor2pil(image)
|
||||
|
||||
metadata = None
|
||||
if save_to_image:
|
||||
metadata = PngInfo()
|
||||
prompt_text=prompts[index]
|
||||
if prompt_text is not None:
|
||||
metadata.add_text("prompt_text", prompt_text)
|
||||
|
||||
file = f"{filename}_{index}_{counter:05}_.png"
|
||||
img.save(os.path.join(full_output_folder, file), pnginfo=metadata, compress_level=self.compress_level)
|
||||
res.append({
|
||||
"filename": file,
|
||||
"subfolder": subfolder,
|
||||
"type": self.type
|
||||
})
|
||||
counter += 1
|
||||
results.append(res)
|
||||
|
||||
return { "ui": { "_images": results,"prompts":prompts } }
|
||||
|
||||
|
||||
|
||||
|
||||
class PromptSimplification:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"prompt": ("STRING",
|
||||
{
|
||||
"multiline": True,
|
||||
"default": '',
|
||||
"dynamicPrompts": False
|
||||
}),
|
||||
|
||||
"length":("INT", {"default": 5, "min": 1,"max":100, "step": 1, "display": "number"}),
|
||||
|
||||
# "min_value":("FLOAT", {
|
||||
# "default": -2,
|
||||
# "min": -10,
|
||||
# "max": 0xffffffffffffffff,
|
||||
# "step": 0.01,
|
||||
# "display": "number"
|
||||
# }),
|
||||
# "max_value":("FLOAT", {
|
||||
# "default": 2,
|
||||
# "min": -10,
|
||||
# "max": 0xffffffffffffffff,
|
||||
# "step": 0.01,
|
||||
# "display": "number"
|
||||
# }),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompts",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
OUTPUT_NODE = True
|
||||
|
||||
# 运行的函数
|
||||
def run(self,prompt,length):
|
||||
length=length[0]
|
||||
result=[]
|
||||
for p in prompt:
|
||||
nps=prompt_delete_words(p,length)
|
||||
for n in nps:
|
||||
result.append(n)
|
||||
|
||||
result= [elem.strip() for elem in result if elem.strip()]
|
||||
|
||||
return {"ui": {"prompts": result}, "result": (result,)}
|
||||
|
||||
|
||||
|
||||
class PromptSlide:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
|
||||
"prompt_keyword": ("STRING",
|
||||
{
|
||||
"multiline": False,
|
||||
"default": '',
|
||||
"dynamicPrompts": False
|
||||
}),
|
||||
|
||||
"weight":("FLOAT", {"default": 1, "min": -3,"max": 3,
|
||||
"step": 0.01,
|
||||
"display": "slider"}),
|
||||
|
||||
# "min_value":("FLOAT", {
|
||||
# "default": -2,
|
||||
# "min": -10,
|
||||
# "max": 0xffffffffffffffff,
|
||||
# "step": 0.01,
|
||||
# "display": "number"
|
||||
# }),
|
||||
# "max_value":("FLOAT", {
|
||||
# "default": 2,
|
||||
# "min": -10,
|
||||
# "max": 0xffffffffffffffff,
|
||||
# "step": 0.01,
|
||||
# "display": "number"
|
||||
# }),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
OUTPUT_NODE = False
|
||||
|
||||
# 运行的函数
|
||||
def run(self,prompt_keyword,weight):
|
||||
# if weight < min_value:
|
||||
# weight= min_value
|
||||
# elif weight > max_value:
|
||||
# weight= max_value
|
||||
p=addWeight(prompt_keyword,weight)
|
||||
return (p,)
|
||||
|
||||
|
||||
|
||||
|
||||
class RandomPrompt:
|
||||
|
||||
'''
|
||||
@@ -70,6 +321,10 @@ class RandomPrompt:
|
||||
"default": 'sticker, Cartoon, ``'
|
||||
}),
|
||||
"random_sample": (["enable", "disable"],),
|
||||
# "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "step": 1}),
|
||||
},
|
||||
"optional":{
|
||||
"seed": (any_type, {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,15 +334,15 @@ class RandomPrompt:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/prompt"
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
OUTPUT_NODE = True
|
||||
|
||||
|
||||
# 运行的函数
|
||||
def run(self,max_count,mutable_prompt,immutable_prompt,random_sample):
|
||||
print('#运行的函数',mutable_prompt,immutable_prompt,max_count,random_sample)
|
||||
def run(self,max_count,mutable_prompt,immutable_prompt,random_sample,seed=0):
|
||||
# print('#运行的函数',mutable_prompt,immutable_prompt,max_count,random_sample)
|
||||
|
||||
# Split the text into an array of words
|
||||
words1 = mutable_prompt.split("\n")
|
||||
@@ -106,6 +361,11 @@ class RandomPrompt:
|
||||
w1=w1.strip()
|
||||
for w2 in words2:
|
||||
w2=w2.strip()
|
||||
if '``' not in w2:
|
||||
if w2=="":
|
||||
w2='``'
|
||||
else:
|
||||
w2=w2+',``'
|
||||
if w1!='' and w2!='':
|
||||
prompts.append(w2.replace('``', w1))
|
||||
pbar.update(1)
|
||||
@@ -119,6 +379,8 @@ class RandomPrompt:
|
||||
else:
|
||||
prompts = prompts[:min(max_count,len(prompts))]
|
||||
|
||||
prompts= [elem.strip() for elem in prompts if elem.strip()]
|
||||
|
||||
# return (new_prompt)
|
||||
return {"ui": {"prompts": prompts}, "result": (prompts,)}
|
||||
|
||||
@@ -126,62 +388,64 @@ class RandomPrompt:
|
||||
|
||||
|
||||
|
||||
class RunWorkflow:
|
||||
|
||||
class EmbeddingPrompt:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {
|
||||
"required": {
|
||||
"workflow": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
}),
|
||||
"prompt": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
}),
|
||||
"image": ("IMAGE",),
|
||||
"input_node": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
}),
|
||||
"output_node": ("STRING", {
|
||||
"multiline": False,
|
||||
"default": ''
|
||||
}),
|
||||
"embedding":(get_files_with_extension(embeddings_path,'.pt'),),
|
||||
"weight": ("FLOAT", {"default": 1, "min": -2, "max": 2,"step":0.01 ,"display": "slider"}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
RETURN_TYPES = ("IMAGE","STRING",)
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/workflow"
|
||||
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
OUTPUT_NODE = True
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
# OUTPUT_NODE = True
|
||||
|
||||
# 运行的函数
|
||||
def run(self,workflow,prompt,image,input_node,output_node):
|
||||
print('#运行的函数',prompt,image,input_node,output_node)
|
||||
workflow=json.loads(workflow)
|
||||
input_node=input_node.split(".")
|
||||
workflow[input_node[0]][input_node[1]][input_node[2]]=prompt
|
||||
|
||||
workflow_new={}
|
||||
# 遍历,seed设为随机
|
||||
for key, value in workflow.items():
|
||||
if 'inputs' in value:
|
||||
if 'seed' in value['inputs']:
|
||||
value['inputs']['seed']= random.randint(1, 18446744073709551614)
|
||||
workflow_new[key]=value
|
||||
|
||||
queue_prompt(workflow_new)
|
||||
print('#运行的函数',workflow_new[input_node[0]])
|
||||
|
||||
def run(self,embedding,weight):
|
||||
weight = round(weight, 3)
|
||||
prompt='embedding:'+embedding
|
||||
if weight!=1:
|
||||
prompt='('+prompt+':'+str(weight)+')'
|
||||
prompt=" "+prompt+' '
|
||||
# return (new_prompt)
|
||||
return {"ui":{"images": []},"result": ([image],['text'],)}
|
||||
return (prompt,)
|
||||
|
||||
RETURN_TYPES = (any_type,)
|
||||
|
||||
class JoinWithDelimiter:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text_list": (any_type,),
|
||||
"delimiter":(["newline","comma"],),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
|
||||
INPUT_IS_LIST = True # 当true的时候,输入时list,当false的时候,如果输入是list,则会自动包一层for循环调用
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,text_list,delimiter):
|
||||
delimiter=delimiter[0]
|
||||
if delimiter =='newline':
|
||||
delimiter='\n'
|
||||
elif delimiter=='comma':
|
||||
delimiter=','
|
||||
t=''
|
||||
if isinstance(text_list, list):
|
||||
t=join_with_(text_list,delimiter)
|
||||
return (t,)
|
||||
@@ -0,0 +1,164 @@
|
||||
import os,sys
|
||||
import folder_paths
|
||||
|
||||
from PIL import Image
|
||||
import importlib.util
|
||||
|
||||
import comfy.utils
|
||||
import numpy as np
|
||||
import torch
|
||||
|
||||
|
||||
U2NET_HOME=os.path.join(folder_paths.models_dir, "rembg")
|
||||
os.environ["U2NET_HOME"] = U2NET_HOME
|
||||
|
||||
global _available
|
||||
_available=False
|
||||
|
||||
def is_installed(package):
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
return spec is not None
|
||||
|
||||
|
||||
try:
|
||||
if is_installed('rembg')==False:
|
||||
import subprocess
|
||||
|
||||
# 安装
|
||||
print('#pip install rembg[gpu]')
|
||||
|
||||
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'rembg[gpu]'], capture_output=True, text=True)
|
||||
|
||||
#检查命令执行结果
|
||||
if result.returncode == 0:
|
||||
print("#install success")
|
||||
from rembg import new_session, remove
|
||||
_available=True
|
||||
else:
|
||||
print("#install error")
|
||||
|
||||
else:
|
||||
from rembg import new_session, remove
|
||||
_available=True
|
||||
|
||||
except:
|
||||
_available=False
|
||||
|
||||
|
||||
def run_bg(model_name= "unet",images=[]):
|
||||
# model_name = "unet" # "isnet-general-use"
|
||||
rembg_session = new_session(model_name)
|
||||
masks=[]
|
||||
rgba_images=[]
|
||||
rgb_images=[]
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(images) )
|
||||
for img in images:
|
||||
# use the post_process_mask argument to post process the mask to get better results.
|
||||
mask = remove(img, session=rembg_session,only_mask=True,post_process_mask=True)
|
||||
# mask=mask.convert('L')
|
||||
# masks.append(mask)
|
||||
if model_name=="u2net_cloth_seg":
|
||||
width, original_height = mask.size
|
||||
num_slices = original_height // img.height
|
||||
for i in range(num_slices):
|
||||
top = i * img.height
|
||||
bottom = (i + 1) * img.height
|
||||
slice_image = mask.crop((0, top, width, bottom))
|
||||
slice_mask=slice_image.convert('L')
|
||||
masks.append(slice_mask)
|
||||
|
||||
# rgba图
|
||||
image_rgba = img.convert("RGBA")
|
||||
image_rgba.putalpha(slice_mask)
|
||||
rgba_images.append(image_rgba)
|
||||
|
||||
#rgb
|
||||
rgb_image = Image.new("RGB", image_rgba.size, (0, 0, 0))
|
||||
rgb_image.paste(image_rgba, mask=image_rgba.split()[3])
|
||||
rgb_images.append(rgb_image)
|
||||
|
||||
else:
|
||||
mask=mask.convert('L')
|
||||
# mask.save(output_path)
|
||||
masks.append(mask)
|
||||
|
||||
# rgba图
|
||||
image_rgba = img.convert("RGBA")
|
||||
image_rgba.putalpha(mask)
|
||||
rgba_images.append(image_rgba)
|
||||
|
||||
#rgb
|
||||
rgb_image = Image.new("RGB", image_rgba.size, (0, 0, 0))
|
||||
rgb_image.paste(image_rgba, mask=image_rgba.split()[3])
|
||||
rgb_images.append(rgb_image)
|
||||
|
||||
pbar.update(1)
|
||||
return (masks,rgba_images,rgb_images)
|
||||
|
||||
|
||||
# Tensor to PIL
|
||||
def tensor2pil(image):
|
||||
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||
|
||||
# Convert PIL to Tensor
|
||||
def pil2tensor(image):
|
||||
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||
|
||||
|
||||
class RembgNode_:
|
||||
|
||||
global _available
|
||||
available=_available
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"image": ("IMAGE",),
|
||||
"model_name": (["u2net",
|
||||
"u2netp",
|
||||
"u2net_human_seg",
|
||||
"u2net_cloth_seg",
|
||||
"silueta",
|
||||
"isnet-general-use",
|
||||
"isnet-anime",
|
||||
# "sam"
|
||||
],),
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("MASK","IMAGE","RGBA",)
|
||||
RETURN_NAMES = ("masks","images","RGBAs")
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Mask"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,True,True,)
|
||||
|
||||
def run(self,image,model_name):
|
||||
# 兼容list输入和batch输入
|
||||
|
||||
model_name=model_name[0]
|
||||
|
||||
images=[]
|
||||
|
||||
for ims in image:
|
||||
for im in ims:
|
||||
im=tensor2pil(im)
|
||||
images.append(im)
|
||||
|
||||
masks,rgba_images,rgb_images=run_bg(model_name,images)
|
||||
|
||||
masks=[pil2tensor(m) for m in masks]
|
||||
|
||||
rgba_images=[pil2tensor(m) for m in rgba_images]
|
||||
|
||||
rgb_images=[pil2tensor(m) for m in rgb_images]
|
||||
|
||||
return (masks,rgb_images,rgba_images,)
|
||||
@@ -93,7 +93,7 @@ class ScreenShareNode:
|
||||
RETURN_NAMES = ("IMAGE","PROMPT","FLOAT","INT")
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (False,False,False,False)
|
||||
@@ -118,7 +118,7 @@ class FloatingVideo:
|
||||
OUTPUT_NODE = True
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/image"
|
||||
CATEGORY = "♾️Mixlab/Image"
|
||||
|
||||
# INPUT_IS_LIST = True
|
||||
# OUTPUT_IS_LIST = (False,False,)
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
from transformers import pipeline, set_seed,AutoTokenizer, AutoModelForSeq2SeqLM
|
||||
import random
|
||||
import re
|
||||
|
||||
import os,sys
|
||||
import folder_paths
|
||||
|
||||
# from PIL import Image
|
||||
import importlib.util
|
||||
|
||||
import comfy.utils
|
||||
# import numpy as np
|
||||
import torch
|
||||
import random
|
||||
|
||||
global _available
|
||||
_available=True
|
||||
|
||||
|
||||
text_generator_model_path=os.path.join(folder_paths.models_dir, "prompt_generator/text2image-prompt-generator")
|
||||
if not os.path.exists(text_generator_model_path):
|
||||
print(f"## text_generator_model not found: {text_generator_model_path}, pls download from https://huggingface.co/succinctly/text2image-prompt-generator/tree/main")
|
||||
text_generator_model_path='succinctly/text2image-prompt-generator'
|
||||
|
||||
zh_en_model_path=os.path.join(folder_paths.models_dir, "prompt_generator/opus-mt-zh-en")
|
||||
if not os.path.exists(zh_en_model_path):
|
||||
print(f"## zh_en_model not found: {zh_en_model_path}, pls download from https://huggingface.co/Helsinki-NLP/opus-mt-zh-en/tree/main")
|
||||
zh_en_model_path='Helsinki-NLP/opus-mt-zh-en'
|
||||
|
||||
|
||||
|
||||
def is_installed(package):
|
||||
try:
|
||||
spec = importlib.util.find_spec(package)
|
||||
except ModuleNotFoundError:
|
||||
return False
|
||||
return spec is not None
|
||||
|
||||
|
||||
try:
|
||||
if is_installed('sentencepiece')==False:
|
||||
import subprocess
|
||||
|
||||
# 安装
|
||||
print('#pip install sentencepiece')
|
||||
|
||||
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'sentencepiece'], capture_output=True, text=True)
|
||||
|
||||
#检查命令执行结果
|
||||
if result.returncode == 0 and is_installed('sentencepiece'):
|
||||
print("#install success")
|
||||
_available=True
|
||||
else:
|
||||
print("#install error")
|
||||
_available=False
|
||||
|
||||
else:
|
||||
_available=True
|
||||
|
||||
except:
|
||||
_available=False
|
||||
|
||||
|
||||
|
||||
def translate(zh_en_tokenizer,zh_en_model,text):
|
||||
with torch.no_grad():
|
||||
encoded = zh_en_tokenizer([text], return_tensors="pt")
|
||||
encoded.to(zh_en_model.device)
|
||||
sequences = zh_en_model.generate(**encoded)
|
||||
return zh_en_tokenizer.batch_decode(sequences, skip_special_tokens=True)[0]
|
||||
|
||||
# input = "青春不能回头,所以青春没有终点。 ——《火影忍者》"
|
||||
# print(input, translate(input))
|
||||
|
||||
|
||||
def text_generate(text_pipe,input,seed=None):
|
||||
|
||||
if seed==None:
|
||||
seed = random.randint(100, 1000000)
|
||||
|
||||
set_seed(seed)
|
||||
|
||||
for count in range(6):
|
||||
sequences = text_pipe(input, max_length=random.randint(60, 90), num_return_sequences=8)
|
||||
list = []
|
||||
for sequence in sequences:
|
||||
line = sequence['generated_text'].strip()
|
||||
if line != input and len(line) > (len(input) + 4) and line.endswith((":", "-", "—")) is False:
|
||||
list.append(line)
|
||||
|
||||
result = "\n".join(list)
|
||||
result = re.sub('[^ ]+\.[^ ]+','', result)
|
||||
result = result.replace("<", "").replace(">", "")
|
||||
if result != "":
|
||||
return result
|
||||
if count == 5:
|
||||
return result
|
||||
|
||||
# input = "Youth can't turn back, so there's no end to youth."
|
||||
# print(input, text_generate(input))
|
||||
|
||||
|
||||
import re
|
||||
|
||||
def correct_prompt_syntax(prompt):
|
||||
|
||||
print("input prompt",prompt)
|
||||
corrected_elements = []
|
||||
# 处理成统一的英文标点
|
||||
prompt = prompt.replace('(', '(').replace(')', ')').replace(',', ',').replace(';', ',').replace('。', '.').replace(':',':')
|
||||
# 删除多余的空格
|
||||
prompt = re.sub(r'\s+', ' ', prompt).strip()
|
||||
|
||||
# 分词
|
||||
prompt_elements = prompt.split(',')
|
||||
|
||||
for element in prompt_elements:
|
||||
element = element.strip()
|
||||
|
||||
# 处理空元素
|
||||
if not element:
|
||||
continue
|
||||
|
||||
# 检查并处理圆括号、方括号、尖括号
|
||||
if element[0] in '([':
|
||||
corrected_element = balance_brackets(element, '(', ')') if element[0] == '(' else balance_brackets(element, '[', ']')
|
||||
elif element[0] == '<':
|
||||
corrected_element = balance_brackets(element, '<', '>')
|
||||
else:
|
||||
# 删除开头的右括号或右方括号
|
||||
corrected_element = element.lstrip(')]')
|
||||
|
||||
corrected_elements.append(corrected_element)
|
||||
|
||||
# 重组修正后的prompt
|
||||
corrected_prompt = ', '.join(corrected_elements)
|
||||
print("output prompt",corrected_prompt)
|
||||
return corrected_prompt
|
||||
|
||||
def balance_brackets(element, open_bracket, close_bracket):
|
||||
open_brackets_count = element.count(open_bracket)
|
||||
close_brackets_count = element.count(close_bracket)
|
||||
return element + close_bracket * (open_brackets_count - close_brackets_count)
|
||||
|
||||
# # 示例使用
|
||||
# test_prompt = "((middle-century castles)), [forsaken: 0.8], (mystery dragons: 1.3, mist forests, sunsets, quiet; (((dummy)), [fisting city: 0.5] background, radiant, soft and flavoured,] promising mountains, ((starry: 1.6), [[crowds], [middle-century castle: urban landscapes of the future: 0.5], [yellow: bright sun: 0.7], overlooking"
|
||||
# corrected_prompt = correct_prompt_syntax(test_prompt)
|
||||
# print(corrected_prompt)
|
||||
|
||||
|
||||
|
||||
class ChinesePrompt:
|
||||
|
||||
global _available
|
||||
available=_available
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text": ("STRING",{"multiline": True,"default": "", "dynamicPrompts": False}),
|
||||
"generation": (["on","off"],{"default": "off"}),
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 1000000}),
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
global text_pipe,zh_en_model,zh_en_tokenizer
|
||||
|
||||
text_pipe= None
|
||||
zh_en_model=None
|
||||
zh_en_tokenizer=None
|
||||
|
||||
def run(self,text,seed,generation):
|
||||
global text_pipe,zh_en_model,zh_en_tokenizer
|
||||
|
||||
seed=seed[0]
|
||||
generation=generation[0]
|
||||
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(text)+1)
|
||||
|
||||
texts = [correct_prompt_syntax(t) for t in text]
|
||||
print('correct_prompt_syntax::',texts)
|
||||
if zh_en_model==None:
|
||||
zh_en_model = AutoModelForSeq2SeqLM.from_pretrained(zh_en_model_path).eval()
|
||||
zh_en_tokenizer = AutoTokenizer.from_pretrained(zh_en_model_path,padding=True, truncation=True)
|
||||
|
||||
zh_en_model.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
# zh_en_tokenizer.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
text_pipe=pipeline('text-generation', model=text_generator_model_path,device="cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
# text_pipe.model.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
prompt_result=[]
|
||||
|
||||
# print('zh_en_model device',zh_en_model.device,text_pipe.model.device,torch.cuda.current_device() )
|
||||
en_texts=[]
|
||||
for t in texts:
|
||||
en_text=translate(zh_en_tokenizer,zh_en_model,t)
|
||||
en_texts.append(en_text)
|
||||
|
||||
zh_en_model.to('cpu')
|
||||
print("test en_text",en_texts)
|
||||
# en_text.to("cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
pbar.update(1)
|
||||
for t in en_texts:
|
||||
if generation=='on':
|
||||
prompt =text_generate(text_pipe,t,seed)
|
||||
# 多条,还是单条
|
||||
lines = prompt.split("\n")
|
||||
longest_line = max(lines, key=len)
|
||||
# print(longest_line)
|
||||
prompt_result.append(longest_line)
|
||||
else:
|
||||
prompt_result.append(t)
|
||||
pbar.update(1)
|
||||
|
||||
text_pipe.model.to('cpu')
|
||||
prompt_result = [correct_prompt_syntax(p) for p in prompt_result]
|
||||
|
||||
return {
|
||||
"ui":{
|
||||
"prompt": prompt_result
|
||||
},
|
||||
"result": (prompt_result,)}
|
||||
|
||||
|
||||
class PromptGenerate:
|
||||
|
||||
global _available
|
||||
available=_available
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"text": ("STRING",{"multiline": True,"default": "", "dynamicPrompts": False}),
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"multiple": (["off","on"],),
|
||||
"seed":("INT", {"default": 100, "min": 100, "max": 1000000}),
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("prompt",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Prompt"
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
global text_pipe
|
||||
|
||||
text_pipe= None
|
||||
#
|
||||
|
||||
def run(self,text,multiple,seed):
|
||||
global text_pipe
|
||||
|
||||
seed=seed[0]
|
||||
|
||||
multiple=multiple[0]
|
||||
|
||||
# 进度条
|
||||
pbar = comfy.utils.ProgressBar(len(text))
|
||||
|
||||
text_pipe=pipeline('text-generation', model=text_generator_model_path,device="cuda" if torch.cuda.is_available() else "cpu")
|
||||
|
||||
prompt_result=[]
|
||||
|
||||
for t in text:
|
||||
prompt =text_generate(text_pipe,t,seed)
|
||||
prompt = prompt.split("\n")
|
||||
if multiple=='off':
|
||||
prompt = [max(prompt, key=len)]
|
||||
|
||||
for p in prompt:
|
||||
prompt_result.append(p)
|
||||
pbar.update(1)
|
||||
|
||||
text_pipe.model.to('cpu')
|
||||
|
||||
return {
|
||||
"ui":{
|
||||
"prompt": prompt_result
|
||||
},
|
||||
"result": (prompt_result,)}
|
||||
+383
-77
@@ -1,10 +1,58 @@
|
||||
import os
|
||||
import re,random
|
||||
import os,platform
|
||||
import re,random,json
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
# FONT_PATH= os.path.abspath(os.path.join(os.path.dirname(__file__),'../assets/王汉宗颜楷体繁.ttf'))
|
||||
import folder_paths
|
||||
import matplotlib.font_manager as fm
|
||||
import torch
|
||||
|
||||
|
||||
|
||||
def recursive_search(directory, excluded_dir_names=None):
|
||||
if not os.path.isdir(directory):
|
||||
return [], {}
|
||||
|
||||
if excluded_dir_names is None:
|
||||
excluded_dir_names = []
|
||||
|
||||
result = []
|
||||
dirs = {directory: os.path.getmtime(directory)}
|
||||
for dirpath, subdirs, filenames in os.walk(directory, followlinks=True, topdown=True):
|
||||
subdirs[:] = [d for d in subdirs if d not in excluded_dir_names]
|
||||
for file_name in filenames:
|
||||
relative_path = os.path.relpath(os.path.join(dirpath, file_name), directory)
|
||||
result.append(relative_path)
|
||||
for d in subdirs:
|
||||
path = os.path.join(dirpath, d)
|
||||
dirs[path] = os.path.getmtime(path)
|
||||
return result, dirs
|
||||
|
||||
def filter_files_extensions(files, extensions):
|
||||
return sorted(list(filter(lambda a: os.path.splitext(a)[-1].lower() in extensions or len(extensions) == 0, files)))
|
||||
|
||||
|
||||
def get_system_font_path():
|
||||
ps=[]
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
ps.append(os.path.join(os.environ["WINDIR"], "Fonts"))
|
||||
elif system == "Darwin":
|
||||
ps.append(os.path.join("/Library", "Fonts"))
|
||||
elif system == "Linux":
|
||||
ps.append(os.path.join("/usr", "share", "fonts"))
|
||||
ps.append(os.path.join("/usr", "local", "share", "fonts"))
|
||||
ps=[p for p in ps if os.path.exists(p)]
|
||||
file_paths=[]
|
||||
for f in ps:
|
||||
result, dirs=recursive_search(f)
|
||||
for r in result:
|
||||
file_paths.append(r)
|
||||
file_paths=filter_files_extensions(file_paths,[".otf", ".ttf"])
|
||||
|
||||
return file_paths
|
||||
|
||||
|
||||
|
||||
# import json
|
||||
# import hashlib
|
||||
@@ -34,13 +82,13 @@ def create_temp_file(image):
|
||||
) = folder_paths.get_save_image_path('tmp', output_dir)
|
||||
|
||||
|
||||
image=tensor2pil(image)
|
||||
im=tensor2pil(image)
|
||||
|
||||
image_file = f"{filename}_{counter:05}.png"
|
||||
|
||||
image_path=os.path.join(full_output_folder, image_file)
|
||||
|
||||
image.save(image_path,compress_level=4)
|
||||
im.save(image_path,compress_level=4)
|
||||
|
||||
return [{
|
||||
"filename": image_file,
|
||||
@@ -60,14 +108,14 @@ def get_font_files(directory):
|
||||
|
||||
# 尝试获取系统字体
|
||||
try:
|
||||
font_paths = fm.findSystemFonts()
|
||||
for path in font_paths:
|
||||
font_paths = get_system_font_path()
|
||||
for file in font_paths:
|
||||
try:
|
||||
font_prop = fm.FontProperties(fname=path)
|
||||
font_name = font_prop.get_name()
|
||||
font_files[font_name] = path
|
||||
font_name = os.path.splitext(file)[0]
|
||||
font_path = file
|
||||
font_files[font_name] = os.path.abspath(font_path)
|
||||
except Exception as e:
|
||||
print(f"Error processing font {path}: {e}")
|
||||
print(f"Error processing font {file}: {e}")
|
||||
except Exception as e:
|
||||
print(f"Error finding system fonts: {e}")
|
||||
|
||||
@@ -79,6 +127,21 @@ font_files = get_font_files(r_directory)
|
||||
# print(font_files)
|
||||
|
||||
|
||||
def flatten_list(nested_list):
|
||||
flat_list = []
|
||||
for item in nested_list:
|
||||
if isinstance(item, list):
|
||||
flat_list.extend(flatten_list(item))
|
||||
else:
|
||||
if torch.is_tensor(item):
|
||||
print('item.shape',item.shape)
|
||||
for i in range(item.shape[0]):
|
||||
flat_list.append(item[i:i + 1, ...])
|
||||
else:
|
||||
flat_list.append(item)
|
||||
return flat_list
|
||||
|
||||
|
||||
class ColorInput:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
@@ -88,18 +151,23 @@ class ColorInput:
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
# RETURN_NAMES = ("WIDTH","HEIGHT","X","Y",)
|
||||
RETURN_TYPES = ("STRING","INT","INT","INT","FLOAT",)
|
||||
RETURN_NAMES = ("hex","r","g","b","a",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,)
|
||||
OUTPUT_IS_LIST = (False,False,False,False,False,)
|
||||
|
||||
def run(self,color):
|
||||
return (color,)
|
||||
h=color['hex']
|
||||
r=color['r']
|
||||
g=color['g']
|
||||
b=color['b']
|
||||
a=color['a']
|
||||
return (h,r,g,b,a,)
|
||||
|
||||
|
||||
|
||||
@@ -117,13 +185,13 @@ class FontInput:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,)
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,font):
|
||||
|
||||
|
||||
return (font_files[font],)
|
||||
|
||||
class TextToNumber:
|
||||
@@ -132,14 +200,17 @@ class TextToNumber:
|
||||
return {"required": {
|
||||
"text": ("STRING",{"multiline": False,"default": "1"}),
|
||||
"random_number": (["enable", "disable"],),
|
||||
"number":("INT", {
|
||||
"default": 0,
|
||||
"min": 0, #Minimum value
|
||||
"max_num":("INT", {
|
||||
"default": 10,
|
||||
"min":2, #Minimum value
|
||||
"max": 10000000000, #Maximum value
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
},
|
||||
"optional":{
|
||||
"seed": (any_type, {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
@@ -147,12 +218,12 @@ class TextToNumber:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,text,random_number,number):
|
||||
def run(self,text,random_number,max_num,seed=0):
|
||||
|
||||
numbers = re.findall(r'\d+', text)
|
||||
result=0
|
||||
@@ -161,7 +232,7 @@ class TextToNumber:
|
||||
# print(result)
|
||||
|
||||
if random_number=='enable' and result>0:
|
||||
result= random.randint(1, 10000000000)
|
||||
result= random.randint(1, max_num)
|
||||
return {"ui": {"text": [text],"num":[result]}, "result": (result,)}
|
||||
|
||||
|
||||
@@ -173,10 +244,31 @@ class FloatSlider:
|
||||
"number":("FLOAT", {
|
||||
"default": 0,
|
||||
"min": 0, #Minimum value
|
||||
"max": 1, #Maximum value
|
||||
"max": 0xffffffffffffffff, #Maximum value
|
||||
"step": 0.001, #Slider's step
|
||||
"display": "slider" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"min_value":("FLOAT", {
|
||||
"default": 0,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.001,
|
||||
"display": "number"
|
||||
}),
|
||||
"max_value":("FLOAT", {
|
||||
"default": 1,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.001,
|
||||
"display": "number"
|
||||
}),
|
||||
"step":("FLOAT", {
|
||||
"default": 0.001,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.001,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -184,14 +276,18 @@ class FloatSlider:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,number):
|
||||
|
||||
return (number,)
|
||||
def run(self, number, min_value, max_value, step):
|
||||
if number < min_value:
|
||||
number = min_value
|
||||
elif number > max_value:
|
||||
number = max_value
|
||||
scaled_number = (number - min_value) / (max_value - min_value)
|
||||
return (scaled_number,)
|
||||
|
||||
|
||||
class IntNumber:
|
||||
@@ -202,9 +298,30 @@ class IntNumber:
|
||||
"default": 0,
|
||||
"min": -1, #Minimum value
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 1, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"min_value":("INT", {
|
||||
"default": 0,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"max_value":("INT", {
|
||||
"default": 1,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"step":("INT", {
|
||||
"default": 1,
|
||||
"min": -0xffffffffffffffff,
|
||||
"max": 0xffffffffffffffff,
|
||||
"step":1,
|
||||
"display": "number"
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -212,13 +329,16 @@ class IntNumber:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
|
||||
def run(self,number):
|
||||
|
||||
def run(self,number,min_value,max_value,step):
|
||||
if number < min_value:
|
||||
number= min_value
|
||||
elif number > max_value:
|
||||
number= max_value
|
||||
return (number,)
|
||||
|
||||
class MultiplicationNode:
|
||||
@@ -226,11 +346,18 @@ class MultiplicationNode:
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"numberA":(any_type,),
|
||||
"numberB":("FLOAT", {
|
||||
"multiply_by":("FLOAT", {
|
||||
"default": 0,
|
||||
"min": -1, #Minimum value
|
||||
"min": -2, #Minimum value
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.1, #Slider's step
|
||||
"step": 0.01, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
}),
|
||||
"add_by":("FLOAT", {
|
||||
"default": 0,
|
||||
"min": -2000, #Minimum value
|
||||
"max": 0xffffffffffffffff,
|
||||
"step": 0.01, #Slider's step
|
||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||
})
|
||||
},
|
||||
@@ -240,14 +367,14 @@ class MultiplicationNode:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,False,)
|
||||
|
||||
def run(self,numberA,numberB):
|
||||
b=int(numberA*numberB)
|
||||
a=float(numberA*numberB)
|
||||
def run(self,numberA,multiply_by,add_by):
|
||||
b=int(numberA*multiply_by+add_by)
|
||||
a=float(numberA*multiply_by+add_by)
|
||||
return (a,b,)
|
||||
|
||||
class TextInput:
|
||||
@@ -262,7 +389,7 @@ class TextInput:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
@@ -303,7 +430,7 @@ class DynamicDelayProcessor:
|
||||
},
|
||||
"optional":{
|
||||
"any_input":(any_type,),
|
||||
"delay_by_text":("STRING",{"multiline":True,}),
|
||||
"delay_by_text":("STRING",{"multiline":True,"dynamicPrompts": False,}),
|
||||
"words_per_seconds":("FLOAT",{ "default":1.50,"min": 0.0,"max": 1000.00,"display":"Chars per second?"}),
|
||||
"replace_output": (["disable","enable"],),
|
||||
"replace_value":("INT",{ "default":-1,"min": 0,"max": 1000000,"display":"Replacement value"})
|
||||
@@ -336,7 +463,7 @@ class DynamicDelayProcessor:
|
||||
RETURN_TYPES = (any_type,)
|
||||
RETURN_NAMES = ('output',)
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
def run(self,any_input,delay_seconds,delay_by_text,words_per_seconds,replace_output,replace_value):
|
||||
# print(f"Delay text:",delay_by_text )
|
||||
# 获取开始时间戳
|
||||
@@ -370,12 +497,12 @@ class AppInfo:
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {
|
||||
"name": ("STRING",{"multiline": False,"default": "Mixlab-App","dynamicPrompts": False}),
|
||||
"image": ("IMAGE",),
|
||||
"input_ids":("STRING",{"multiline": True,"default": "\n".join(["1","2","3"]),"dynamicPrompts": False}),
|
||||
"output_ids":("STRING",{"multiline": True,"default": "\n".join(["5","9"]),"dynamicPrompts": False}),
|
||||
},
|
||||
|
||||
"optional":{
|
||||
"IMAGE": ("IMAGE",),
|
||||
"description":("STRING",{"multiline": True,"default": "","dynamicPrompts": False}),
|
||||
"version":("INT", {
|
||||
"default": 1,
|
||||
@@ -385,52 +512,49 @@ class AppInfo:
|
||||
"display": "number"
|
||||
}),
|
||||
"share_prefix":("STRING",{"multiline": False,"default": "","dynamicPrompts": False}),
|
||||
"link":("STRING",{"multiline": False,"default": "https://","dynamicPrompts": False}),
|
||||
"category":("STRING",{"multiline": False,"default": "","dynamicPrompts": False}),
|
||||
"auto_save": (["enable","disable"],),
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("IMAGE",)
|
||||
RETURN_TYPES = ()
|
||||
# RETURN_NAMES = ("IMAGE",)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
# OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,name,image,input_ids,output_ids,description,version,share_prefix):
|
||||
def run(self,name,input_ids,output_ids,IMAGE,description,version,share_prefix,link,category,auto_save):
|
||||
name=name[0]
|
||||
|
||||
im=None
|
||||
if IMAGE:
|
||||
im=IMAGE[0][0]
|
||||
#TODO batch 的方式需要处理
|
||||
im=create_temp_file(im)
|
||||
# image [img,] img[batch,w,h,a] 列表里面是batch,
|
||||
|
||||
im=create_temp_file(image)
|
||||
input_ids=input_ids[0]
|
||||
output_ids=output_ids[0]
|
||||
description=description[0]
|
||||
version=version[0]
|
||||
share_prefix=share_prefix[0]
|
||||
link=link[0]
|
||||
category=category[0]
|
||||
|
||||
# id=get_json_hash([name,im,input_ids,output_ids,description,version])
|
||||
|
||||
return {"ui": {"json": [name,im,input_ids,output_ids,description,version,share_prefix]}, "result": (image,)}
|
||||
return {"ui": {"json": [name,im,input_ids,output_ids,description,version,share_prefix,link,category]}, "result": ()}
|
||||
|
||||
|
||||
|
||||
|
||||
class GetImageSize_:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"image": ("IMAGE",),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT", "INT")
|
||||
RETURN_NAMES = ("width", "height")
|
||||
|
||||
FUNCTION = "get_size"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
|
||||
def get_size(self, image):
|
||||
_, height, width, _ = image.shape
|
||||
return (width, height)
|
||||
|
||||
|
||||
|
||||
class SwitchByIndex:
|
||||
@classmethod
|
||||
@@ -446,6 +570,7 @@ class SwitchByIndex:
|
||||
"step": 1,
|
||||
"display": "number"
|
||||
}),
|
||||
"flat": (['off',"on"],),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,23 +579,32 @@ class SwitchByIndex:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self, A,B,index):
|
||||
def run(self, A,B,index,flat):
|
||||
|
||||
flat=flat[0]
|
||||
|
||||
C=[]
|
||||
index=index[0]
|
||||
for a in A:
|
||||
|
||||
for a in A:
|
||||
C.append(a)
|
||||
for b in B:
|
||||
C.append(b)
|
||||
|
||||
if flat=='on':
|
||||
C=flatten_list(C)
|
||||
|
||||
if index>-1:
|
||||
try:
|
||||
C=[C[index]]
|
||||
except Exception as e:
|
||||
C=[]
|
||||
|
||||
return (C,)
|
||||
|
||||
|
||||
@@ -503,7 +637,7 @@ class LimitNumber:
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/utils"
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (False,)
|
||||
@@ -525,3 +659,175 @@ class LimitNumber:
|
||||
|
||||
return (nn,)
|
||||
|
||||
|
||||
|
||||
class ListStatistics:
|
||||
@staticmethod
|
||||
def count_types(lst):
|
||||
type_count = {}
|
||||
|
||||
for item in lst:
|
||||
item_type = type(item).__name__
|
||||
if item_type not in type_count:
|
||||
type_count[item_type] = []
|
||||
|
||||
if item_type in ['dict', 'str', 'int', 'float']:
|
||||
type_count[item_type].append(item)
|
||||
|
||||
return type_count
|
||||
|
||||
# # 示例列表
|
||||
# my_list = [1, 'hello', {'name': 'John'}, 3.14, {'age': 25}, 'world', 10]
|
||||
|
||||
# # 创建ListStatistics对象
|
||||
# list_stats = ListStatistics()
|
||||
|
||||
# # 调用count_types方法进行统计
|
||||
# result = list_stats.count_types(my_list)
|
||||
|
||||
# # 输出结果
|
||||
# for item_type, values in result.items():
|
||||
# print(item_type + ':')
|
||||
# for value in values:
|
||||
# print(value)
|
||||
# print('---')
|
||||
|
||||
class TESTNODE_:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": { "ANY":(any_type,), },
|
||||
}
|
||||
|
||||
RETURN_TYPES = (any_type,)
|
||||
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/__TEST"
|
||||
|
||||
OUTPUT_NODE = True
|
||||
INPUT_IS_LIST = True
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
def run(self,ANY):
|
||||
print(ANY)
|
||||
# data=ANY
|
||||
list_stats = ListStatistics()
|
||||
|
||||
# 调用count_types方法进行统计
|
||||
result = list_stats.count_types(ANY)
|
||||
|
||||
return {"ui": {"data": result,"type":[str(type(ANY[0]))]}, "result": (ANY,)}
|
||||
|
||||
|
||||
|
||||
class CreateSeedNode:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("INT",)
|
||||
RETURN_NAMES = ("seed",)
|
||||
|
||||
OUTPUT_NODE = True
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
def run(self, seed):
|
||||
return (seed,)
|
||||
|
||||
|
||||
class CreateCkptNames:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"ckpt_names": ("STRING",{"multiline": True,"default": "\n".join(folder_paths.get_filename_list("checkpoints")),"dynamicPrompts": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (any_type,)
|
||||
RETURN_NAMES = ("ckpt_names",)
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
# OUTPUT_NODE = True
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
def run(self, ckpt_names):
|
||||
ckpt_names=ckpt_names.split('\n')
|
||||
ckpt_names = [name for name in ckpt_names if name.strip()]
|
||||
return (ckpt_names,)
|
||||
|
||||
|
||||
class CreateLoraNames:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"lora_names": ("STRING",{"multiline": True,"default": "\n".join(folder_paths.get_filename_list("loras")),"dynamicPrompts": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (any_type,"STRING",)
|
||||
RETURN_NAMES = ("lora_names","prompt",)
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (True,True,)
|
||||
|
||||
# OUTPUT_NODE = True
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
def run(self, lora_names):
|
||||
lora_names=lora_names.split('\n')
|
||||
lora_names = [name for name in lora_names if name.strip()]
|
||||
prompts=[os.path.splitext(n)[0] for n in lora_names]
|
||||
return (lora_names,prompts,)
|
||||
|
||||
|
||||
|
||||
class CreateSampler_names:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"sampler_names": ("STRING",{"multiline": True,"default": "\n".join(comfy.samplers.KSampler.SAMPLERS),"dynamicPrompts": False}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = (any_type,)
|
||||
RETURN_NAMES = ("sampler_names",)
|
||||
|
||||
INPUT_IS_LIST = False
|
||||
OUTPUT_IS_LIST = (True,)
|
||||
|
||||
# OUTPUT_NODE = True
|
||||
FUNCTION = "run"
|
||||
|
||||
CATEGORY = "♾️Mixlab/Utils"
|
||||
|
||||
def run(self, sampler_names):
|
||||
sampler_names=sampler_names.split('\n')
|
||||
sampler_names = [name for name in sampler_names if name.strip()]
|
||||
return (sampler_names,)
|
||||
+2
-2
@@ -145,7 +145,7 @@ class VAELoader:
|
||||
RETURN_TYPES = ("VAE",)
|
||||
FUNCTION = "load_vae"
|
||||
|
||||
CATEGORY = "♾️Mixlab/_test"
|
||||
CATEGORY = "♾️Mixlab/__TEST"
|
||||
|
||||
#TODO: scale factor?
|
||||
def load_vae(self, vae_name):
|
||||
@@ -165,7 +165,7 @@ class VAEDecode:
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
FUNCTION = "decode"
|
||||
|
||||
CATEGORY = "♾️Mixlab/_test"
|
||||
CATEGORY = "♾️Mixlab/__TEST"
|
||||
|
||||
def decode(self, vae, samples):
|
||||
image = vae.decode(samples["samples"].to("cuda:0"))
|
||||
|
||||
+3
-1
@@ -3,4 +3,6 @@ pyOpenSSL
|
||||
watchdog
|
||||
opencv-python-headless
|
||||
matplotlib
|
||||
openai
|
||||
openai
|
||||
simple-lama-inpainting
|
||||
clip-interrogator==0.6.0
|
||||
+1115
-120
File diff suppressed because it is too large
Load Diff
@@ -196,6 +196,9 @@ app.registerExtension({
|
||||
}
|
||||
if (bg) {
|
||||
data.bg_image = await parseImage(bg)
|
||||
if (!data.bg_image.match('data:image/')) {
|
||||
delete data.bg_image
|
||||
}
|
||||
}
|
||||
|
||||
if (material) {
|
||||
|
||||
+155
-23
@@ -70,11 +70,13 @@ function extractInputAndOutputData (jsonData, inputIds = [], outputIds = []) {
|
||||
const data = jsonData
|
||||
const input = []
|
||||
const output = []
|
||||
const seed = {}
|
||||
|
||||
for (const id in data) {
|
||||
if (data.hasOwnProperty(id)) {
|
||||
let node = app.graph.getNodeById(id)
|
||||
if (inputIds.includes(id)) {
|
||||
let node = app.graph.getNodeById(id)
|
||||
// let node = app.graph.getNodeById(id)
|
||||
let options = []
|
||||
// 模型
|
||||
try {
|
||||
@@ -87,6 +89,30 @@ function extractInputAndOutputData (jsonData, inputIds = [], outputIds = []) {
|
||||
}
|
||||
} catch (error) {}
|
||||
|
||||
if (node.type == 'IntNumber' || node.type == 'FloatSlider') {
|
||||
// min max step
|
||||
let [v, min, max, step] = Array.from(node.widgets, w => w.value)
|
||||
options = { min, max, step }
|
||||
// node.widgets.filter(w => w.type === 'number')[0].options
|
||||
}
|
||||
|
||||
if (node.type == 'PromptSlide') {
|
||||
// min max step
|
||||
options = node.widgets.filter(w => w.type === 'slider')[0].options
|
||||
// 备选的keywords清单
|
||||
try {
|
||||
let keywords = node.widgets.filter(w => w.name === 'upload')[0]
|
||||
.value
|
||||
keywords = JSON.parse(keywords)
|
||||
options.keywords = keywords
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
if (node.type == 'Color') {
|
||||
}
|
||||
|
||||
input[inputIds.indexOf(id)] = {
|
||||
...data[id],
|
||||
title: node.title,
|
||||
@@ -96,14 +122,23 @@ function extractInputAndOutputData (jsonData, inputIds = [], outputIds = []) {
|
||||
// input.push()
|
||||
}
|
||||
if (outputIds.includes(id)) {
|
||||
let node = app.graph.getNodeById(id)
|
||||
// let node = app.graph.getNodeById(id)
|
||||
// output.push()
|
||||
output[outputIds.indexOf(id)] = { ...data[id], title: node.title, id }
|
||||
}
|
||||
|
||||
if (node.type === 'KSampler' || node.type == 'SamplerCustom') {
|
||||
// seed 的类型收集
|
||||
try {
|
||||
seed[id] = node.widgets.filter(
|
||||
w => w.name === 'seed' || w.name == 'noise_seed'
|
||||
)[0].linkedWidgets[0].value
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { input, output }
|
||||
return { input, output, seed }
|
||||
}
|
||||
|
||||
function getUrl () {
|
||||
@@ -113,6 +148,16 @@ function getUrl () {
|
||||
return url
|
||||
}
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
try {
|
||||
data = JSON.parse(localStorage.getItem(key)) || {}
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
async function save_app (json) {
|
||||
let url = getUrl()
|
||||
|
||||
@@ -121,7 +166,8 @@ async function save_app (json) {
|
||||
body: JSON.stringify({
|
||||
data: json,
|
||||
task: 'save_app',
|
||||
filename: json.app.filename
|
||||
filename: json.app.filename,
|
||||
category: json.app.category
|
||||
})
|
||||
})
|
||||
return await res.json()
|
||||
@@ -143,10 +189,13 @@ function downloadJsonFile (jsonData, fileName = 'mix_app.json') {
|
||||
}, 0)
|
||||
}
|
||||
|
||||
async function save (json, download = false) {
|
||||
async function save (json, download = false, showInfo = true) {
|
||||
console.log('####SAVE', json[0])
|
||||
const name = json[0],
|
||||
version = json[5],
|
||||
share_prefix = json[6], //用于分享的功能扩展
|
||||
link = json[7], //用于创建界面上的跳转链接
|
||||
category = json[8] || '', //用于分类
|
||||
description = json[4],
|
||||
inputIds = json[2].split('\n').filter(f => f),
|
||||
outputIds = json[3].split('\n').filter(f => f)
|
||||
@@ -162,7 +211,7 @@ async function save (json, download = false) {
|
||||
try {
|
||||
let data = await app.graphToPrompt()
|
||||
|
||||
const { input, output } = extractInputAndOutputData(
|
||||
const { input, output, seed } = extractInputAndOutputData(
|
||||
data.output,
|
||||
inputIds,
|
||||
outputIds
|
||||
@@ -174,8 +223,11 @@ async function save (json, download = false) {
|
||||
version,
|
||||
input,
|
||||
output,
|
||||
seed, //控制是fixed 还是random
|
||||
share_prefix,
|
||||
filename: `${name}_${version}_${new Date().toDateString()}.json`
|
||||
link,
|
||||
category,
|
||||
filename: `${name}_${version}.json`
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -183,27 +235,54 @@ async function save (json, download = false) {
|
||||
} catch (error) {}
|
||||
// console.log(data.app)
|
||||
// let http_workflow = app.graph.serialize()
|
||||
|
||||
await save_app(data)
|
||||
if (download) {
|
||||
await save_app(data)
|
||||
await downloadJsonFile(data, data.app.filename)
|
||||
await downloadJsonFile(data, data.app.filename)
|
||||
}
|
||||
|
||||
if (showInfo) {
|
||||
let open = window.confirm(
|
||||
`You can now access the standalone application on a new page!\n${getUrl()}/mixlab/app?filename=${encodeURIComponent(data.app.filename)}`
|
||||
`You can now access the standalone application on a new page!\n${getUrl()}/mixlab/app?filename=${encodeURIComponent(
|
||||
data.app.filename
|
||||
)}&category=${encodeURIComponent(data.app.category)}`
|
||||
)
|
||||
if (open)
|
||||
window.open(
|
||||
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(data.app.filename)}`
|
||||
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(
|
||||
data.app.filename
|
||||
)}&category=${encodeURIComponent(data.app.category)}`
|
||||
)
|
||||
} else {
|
||||
await save_app(data)
|
||||
|
||||
let open = window.confirm(
|
||||
`You can now access the standalone application on a new page!\n${getUrl()}/mixlab/app`
|
||||
)
|
||||
if (open) window.open(`${getUrl()}/mixlab/app`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('###SpeechRecognition', error)
|
||||
console.log('###error', error)
|
||||
}
|
||||
}
|
||||
|
||||
function getInputsAndOutputs () {
|
||||
const inputs =
|
||||
`LoadImage CLIPTextEncode PromptSlide TextInput_ Color FloatSlider IntNumber CheckpointLoaderSimple LoraLoader`.split(
|
||||
' '
|
||||
),
|
||||
outputs = `PreviewImage SaveImage ShowTextForGPT VHS_VideoCombine`.split(
|
||||
' '
|
||||
)
|
||||
|
||||
let inputsId = [],
|
||||
outputsId = []
|
||||
|
||||
for (let node of app.graph._nodes) {
|
||||
if (inputs.includes(node.type)) {
|
||||
inputsId.push(node.id)
|
||||
}
|
||||
|
||||
if (outputs.includes(node.type)) {
|
||||
outputsId.push(node.id)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
input: inputsId,
|
||||
output: outputsId
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +293,16 @@ app.registerExtension({
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
console.log('#orig_nodeCreated', this)
|
||||
// console.log('#orig_nodeCreated', this)
|
||||
|
||||
// 自动计算workflow里哪些节点支持
|
||||
let input_ids = this.widgets.filter(w => w.name == 'input_ids')[0],
|
||||
output_ids = this.widgets.filter(w => w.name == 'output_ids')[0]
|
||||
|
||||
const { input, output } = getInputsAndOutputs()
|
||||
input_ids.value = input.join('\n')
|
||||
output_ids.value = output.join('\n')
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'AppInfoRun',
|
||||
@@ -285,15 +373,22 @@ app.registerExtension({
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
|
||||
window._mixlab_app_json = null
|
||||
}
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = async function (message) {
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
console.log(message.json)
|
||||
|
||||
window._mixlab_app_json = message.json
|
||||
try {
|
||||
let a = this.widgets.filter(w => w.name === 'AppInfoRun')[0]
|
||||
if (a) {
|
||||
if (!a.value) a.value = 0
|
||||
a.value += 1
|
||||
}
|
||||
|
||||
const div = this.widgets.filter(w => w.div)[0].div
|
||||
Array.from(
|
||||
div.querySelectorAll('button'),
|
||||
@@ -302,5 +397,42 @@ app.registerExtension({
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
// console.log('#loadedGraphNode1111')
|
||||
window._mixlab_app_json = null //切换workflow需要清空
|
||||
if (node.type === 'AppInfo') {
|
||||
let auto_save = node.widgets.filter(w => w.name == 'auto_save')[0]
|
||||
if (auto_save) {
|
||||
if (!['enable', 'disable'].includes(auto_save.value)) {
|
||||
auto_save.value = 'enable'
|
||||
}
|
||||
}
|
||||
|
||||
// app.canvas.centerOnNode(node)
|
||||
// app.canvas.setZoom(0.45)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
api.addEventListener('execution_start', async ({ detail }) => {
|
||||
console.log('#execution_start', detail)
|
||||
window._mixlab_app_json = null
|
||||
})
|
||||
|
||||
api.addEventListener('executed', async ({ detail }) => {
|
||||
console.log('#executed', detail)
|
||||
// window._mixlab_app_json=null;
|
||||
const { output } = getInputsAndOutputs()
|
||||
if (output.includes(parseInt(detail.node))) {
|
||||
let appinfo = app.graph.findNodesByType('AppInfo')[0]
|
||||
if (appinfo) {
|
||||
let auto_save = appinfo.widgets.filter(w => w.name == 'auto_save')[0]
|
||||
if (auto_save?.value === 'enable') {
|
||||
// 自动保存
|
||||
console.log('auto_save')
|
||||
if (window._mixlab_app_json) save(window._mixlab_app_json, false, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
|
||||
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
||||
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
||||
|
||||
const version = 'v0.7.0'
|
||||
const version = 'v0.15.0'
|
||||
|
||||
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
||||
.then(response => response.json())
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
// import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
function getRandomElements (arr, num) {
|
||||
var result = []
|
||||
var len = arr.length
|
||||
|
||||
for (var i = 0; i < num; i++) {
|
||||
var randomIndex = Math.floor(Math.random() * len)
|
||||
result.push(arr[randomIndex])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
const createPrompt = (node, prompts, items, sample) => {
|
||||
const w = ComfyWidgets['STRING'](
|
||||
node,
|
||||
'text',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget
|
||||
w.inputEl.readOnly = true
|
||||
w.inputEl.style.opacity = 0.6
|
||||
|
||||
w.value = typeof prompts === 'string' ? prompts : prompts.join('\n\n')
|
||||
|
||||
const w2 = ComfyWidgets['STRING'](
|
||||
node,
|
||||
'text',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget
|
||||
w2.inputEl.readOnly = true
|
||||
w2.inputEl.style.opacity = 0.6
|
||||
|
||||
w2.value = typeof items === 'string' ? items : JSON.stringify(items, null, 2)
|
||||
|
||||
const w3 = ComfyWidgets['STRING'](
|
||||
node,
|
||||
'text',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget
|
||||
w3.inputEl.readOnly = true
|
||||
w3.inputEl.style.opacity = 0.6
|
||||
w3.value = typeof sample === 'string' ? sample : sample.join('\n\n')
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.prompt.ClipInterrogator',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeData.name === 'ClipInterrogator') {
|
||||
function populate (prompts, items, random_samples) {
|
||||
if (this.widgets) {
|
||||
for (let i = 0; i < this.widgets.length; i++) {
|
||||
if (this.widgets[i].type !== 'combo') this.widgets[i].onRemove?.()
|
||||
}
|
||||
this.widgets.length = 2
|
||||
}
|
||||
|
||||
createPrompt(this, prompts, items, random_samples)
|
||||
|
||||
// console.log('ClipInterrogator', w, w2)
|
||||
requestAnimationFrame(() => {
|
||||
const sz = this.computeSize()
|
||||
if (sz[0] < this.size[0]) {
|
||||
sz[0] = this.size[0]
|
||||
}
|
||||
if (sz[1] < this.size[1]) {
|
||||
sz[1] = this.size[1]
|
||||
}
|
||||
this.onResize?.(sz)
|
||||
app.graph.setDirtyCanvas(true, false)
|
||||
})
|
||||
}
|
||||
|
||||
// When the node is executed we will be sent the input text, display this in the widget
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
// console.log('##', message)
|
||||
populate.call(
|
||||
this,
|
||||
message.prompt,
|
||||
message.analysis,
|
||||
message.random_samples
|
||||
)
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
// Fires every time a node is constructed
|
||||
// You can modify widgets/add handlers/etc here
|
||||
|
||||
if (node.type === 'ClipInterrogator') {
|
||||
try {
|
||||
|
||||
let widgets_values = node.widgets_values
|
||||
console.log(widgets_values )
|
||||
try {
|
||||
if (widgets_values[2] && widgets_values[3] && widgets_values[4])
|
||||
createPrompt(
|
||||
node,
|
||||
widgets_values[2],
|
||||
widgets_values[3],
|
||||
widgets_values[4]
|
||||
)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -256,6 +256,7 @@ app.registerExtension({
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments);
|
||||
console.log('##',message.text)
|
||||
populate.call(this, message.text);
|
||||
};
|
||||
|
||||
|
||||
@@ -156,6 +156,32 @@ const parseSvg = async svgContent => {
|
||||
return { data, image: base64, svgElement }
|
||||
}
|
||||
|
||||
|
||||
function findImages(nodeId) {
|
||||
// 检查当前节点是否有 imgs 字段
|
||||
const n = app.graph.getNodeById(nodeId)
|
||||
if (n.imgs) {
|
||||
return n.imgs;
|
||||
}
|
||||
|
||||
// 检查当前节点的 inputs 是否有 image 字段
|
||||
if (n.inputs) {
|
||||
for (let i = 0; i < n.inputs.length; i++) {
|
||||
if (n.inputs[i].name==='image'||n.inputs[i].name==='images') {
|
||||
// 获取新的 nodeId,并递归调用 findImages 函数
|
||||
var linkId = n.inputs[i]?.link;
|
||||
var origin_id = app.graph.links[linkId].origin_id
|
||||
return findImages(origin_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到 imgs 字段或者 image 字段,则返回 null
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function setArea (cw, ch, topBase64, base64, data, fn) {
|
||||
let displayHeight = Math.round(window.screen.availHeight * 0.8)
|
||||
let div = document.createElement('div')
|
||||
@@ -571,15 +597,19 @@ app.registerExtension({
|
||||
}
|
||||
}
|
||||
try {
|
||||
console.log('this.inputs', this.inputs)
|
||||
let topLinkId = this.inputs[0].link
|
||||
let topNodeId = app.graph.links[topLinkId].origin_id
|
||||
let topIm = app.graph.getNodeById(topNodeId).imgs[0]
|
||||
console.log('this.inputs', this.id)
|
||||
let imgs=findImages(this.id)
|
||||
|
||||
// let topLinkId = this.inputs[0].link
|
||||
// let topNodeId = app.graph.links[topLinkId].origin_id
|
||||
let topIm = imgs[0]
|
||||
|
||||
let linkId = this.inputs[3].link
|
||||
let nodeId = app.graph.links[linkId].origin_id
|
||||
// console.log(linkId,this.inputs)
|
||||
let im = app.graph.getNodeById(nodeId).imgs[0]
|
||||
let imgs2=findImages(nodeId)
|
||||
let im = imgs2[0]
|
||||
console.log(topIm,im)
|
||||
// let src = im.src
|
||||
setArea(
|
||||
im.naturalWidth,
|
||||
@@ -589,7 +619,9 @@ app.registerExtension({
|
||||
data,
|
||||
updateValue
|
||||
)
|
||||
} catch (error) {}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1331,7 +1331,13 @@ app.registerExtension({
|
||||
let w = 360,
|
||||
s = widget.preview.videoWidth / widget.preview.videoHeight,
|
||||
h = w / s || w
|
||||
console.log(h)
|
||||
// console.log(h)
|
||||
|
||||
if (!window.documentPictureInPicture) {
|
||||
window.alert(
|
||||
'This feature is available only in secure contexts (HTTPS), in some or all supporting browsers. https://developer.mozilla.org/en-US/docs/Web/API/Document_Picture-in-Picture_API'
|
||||
)
|
||||
}
|
||||
|
||||
const pipWindow = await documentPictureInPicture.requestWindow({
|
||||
width: w,
|
||||
@@ -1800,15 +1806,19 @@ const updateUI = node => {
|
||||
pw.inputEl.title = `Total of ${prompts.length} prompts`
|
||||
} else {
|
||||
// 动态添加
|
||||
console.log('ComfyWidgets',ComfyWidgets.STRING(
|
||||
node,
|
||||
'prompts',
|
||||
['STRING', { multiline: true }]
|
||||
))
|
||||
// console.log('ComfyWidgets',ComfyWidgets.STRING(
|
||||
// node,
|
||||
// 'prompts',
|
||||
// ['STRING', { multiline: true }]
|
||||
// ))
|
||||
|
||||
// ComfyWidgets.STRING(this, "", ["", {default:this.properties.text, multiline: true}], app)
|
||||
|
||||
const w = ComfyWidgets.STRING(
|
||||
node,
|
||||
'prompts',
|
||||
['STRING', { multiline: true }]
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
).widget
|
||||
w.inputEl.readOnly = true
|
||||
w.inputEl.style.opacity = 0.6
|
||||
@@ -2089,13 +2099,13 @@ const node = {
|
||||
name: 'RandomPrompt',
|
||||
async init (app) {
|
||||
// Any initial setup to run as soon as the page loads
|
||||
console.log('[logging]', 'extension init')
|
||||
// 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)
|
||||
// console.log('#get_my_workflow', data)
|
||||
let my_workflow = data.filter(
|
||||
d => d.filename == 'my_workflow.json'
|
||||
)[0]
|
||||
@@ -2131,10 +2141,15 @@ const node = {
|
||||
// }
|
||||
},
|
||||
loadedGraphNode (node, app) {
|
||||
// Fires for each node when loading/dragging/etc a workflow json or png
|
||||
// If you break something in the backend and want to patch workflows in the frontend
|
||||
// This is the place to do this
|
||||
// console.log("[logging]", "loaded graph node: ", exportGraph(node.graph));
|
||||
if (node.type === 'RandomPrompt') {
|
||||
try {
|
||||
let max_count = node.widgets.filter(w => w.name === 'max_count')[0]
|
||||
max_count.value = node.widgets_values[0]
|
||||
// console.log('RandomPrompt',max_count,node.widgets_values[0])
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
},
|
||||
async nodeCreated (node) {
|
||||
if (node.type === 'RandomPrompt') {
|
||||
@@ -2227,7 +2242,7 @@ const node = {
|
||||
const r = onExecuted?.apply?.(this, arguments)
|
||||
|
||||
let prompts = message.prompts
|
||||
console.log('executed', message)
|
||||
// console.log('executed', message)
|
||||
// console.log('#RandomPrompt', this.widgets)
|
||||
const pw = this.widgets.filter(w => w.name === 'prompts')[0]
|
||||
|
||||
@@ -2238,7 +2253,7 @@ const node = {
|
||||
} else {
|
||||
// 动态添加
|
||||
const w = ComfyWidgets.STRING(
|
||||
node,
|
||||
this,
|
||||
'prompts',
|
||||
['STRING', { multiline: true }],
|
||||
app
|
||||
|
||||
@@ -0,0 +1,566 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
|
||||
import PhotoSwipeLightbox from '/extensions/comfyui-mixlab-nodes/lib/photoswipe-lightbox.esm.min.js'
|
||||
function loadCSS (url) {
|
||||
var link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.type = 'text/css'
|
||||
link.href = url
|
||||
document.getElementsByTagName('head')[0].appendChild(link)
|
||||
|
||||
// Create a style element
|
||||
const style = document.createElement('style')
|
||||
// Define the CSS rule for scrollbar width
|
||||
const cssRule = `.pswp__custom-caption {
|
||||
background: rgb(20 27 70);
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
width: calc(100% - 32px);
|
||||
max-width: 980px;
|
||||
padding: 2px 8px;
|
||||
border-radius: 4px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 16px;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.pswp__custom-caption a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.hidden-caption-content {
|
||||
display: none;
|
||||
}`
|
||||
// Add the CSS rule to the style element
|
||||
style.appendChild(document.createTextNode(cssRule))
|
||||
|
||||
// Append the style element to the document head
|
||||
document.head.appendChild(style)
|
||||
}
|
||||
loadCSS('/extensions/comfyui-mixlab-nodes/lib/photoswipe.min.css')
|
||||
|
||||
function initLightBox () {
|
||||
const lightbox = new PhotoSwipeLightbox({
|
||||
gallery: '.prompt_image_output',
|
||||
children: 'a',
|
||||
pswpModule: () =>
|
||||
import('/extensions/comfyui-mixlab-nodes/lib/photoswipe.esm.min.js')
|
||||
})
|
||||
|
||||
lightbox.on('uiRegister', function () {
|
||||
lightbox.pswp.ui.registerElement({
|
||||
name: 'custom-caption',
|
||||
order: 9,
|
||||
isButton: false,
|
||||
appendTo: 'root',
|
||||
html: 'Caption text',
|
||||
onInit: (el, pswp) => {
|
||||
lightbox.pswp.on('change', () => {
|
||||
const currSlideElement = lightbox.pswp.currSlide.data.element
|
||||
let captionHTML = ''
|
||||
if (currSlideElement) {
|
||||
const hiddenCaption = currSlideElement.querySelector(
|
||||
'.hidden-caption-content'
|
||||
)
|
||||
if (hiddenCaption) {
|
||||
// get caption from element with class hidden-caption-content
|
||||
captionHTML = hiddenCaption.innerHTML
|
||||
} else {
|
||||
// get caption from alt attribute
|
||||
captionHTML = currSlideElement
|
||||
.querySelector('img')
|
||||
.getAttribute('alt')
|
||||
}
|
||||
}
|
||||
el.innerHTML = captionHTML || ''
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
lightbox.init()
|
||||
}
|
||||
|
||||
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 - 24}px`,
|
||||
// height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||
// background: '#EEEEEE',
|
||||
paddingLeft: '12px',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
// alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
}
|
||||
}
|
||||
function createImage (url) {
|
||||
let im = new Image()
|
||||
return new Promise((res, rej) => {
|
||||
im.onload = () => res(im)
|
||||
im.src = url
|
||||
})
|
||||
}
|
||||
|
||||
async function fetchImage (url) {
|
||||
try {
|
||||
const response = await fetch(url)
|
||||
const blob = await response.blob()
|
||||
|
||||
return blob
|
||||
} catch (error) {
|
||||
console.error('出现错误:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
try {
|
||||
data = JSON.parse(localStorage.getItem(key)) || {}
|
||||
} catch (error) {
|
||||
return {}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
const setLocalDataOfWin = (key, value) => {
|
||||
localStorage.setItem(key, JSON.stringify(value))
|
||||
// window[key] = value
|
||||
}
|
||||
|
||||
const createSelect = (select, opts, targetWidget) => {
|
||||
select.style.display = 'block'
|
||||
let html = ''
|
||||
let isMatch = false
|
||||
for (const opt of opts) {
|
||||
html += `<option value='${opt}' ${
|
||||
targetWidget.value === opt ? 'selected' : ''
|
||||
}>${opt}</option>`
|
||||
if (targetWidget.value === opt) isMatch = true
|
||||
}
|
||||
select.innerHTML = html
|
||||
if (!isMatch) targetWidget.value = opts[0]
|
||||
// 添加change事件监听器
|
||||
select.addEventListener('change', function () {
|
||||
// 获取选中的选项的值
|
||||
var selectedOption = select.options[select.selectedIndex].value
|
||||
targetWidget.value = selectedOption
|
||||
// console.log(widget,selectedOption)
|
||||
})
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.prompt.RandomPrompt',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass == 'RandomPrompt') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
const mutable_prompt = this.widgets.filter(
|
||||
w => w.name == 'mutable_prompt'
|
||||
)[0]
|
||||
// console.log('PromptSlide nodeData', prompt_keyword)
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'upload',
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, y, node.size[1])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
widget.div = $el('div', {})
|
||||
|
||||
const btn = document.createElement('button')
|
||||
btn.innerText = 'Upload Keywords'
|
||||
|
||||
btn.style = `cursor: pointer;
|
||||
font-weight: 300;
|
||||
margin: 2px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid; height: 30px;min-width: 122px;
|
||||
`
|
||||
|
||||
// const btn=document.createElement('button');
|
||||
// btn.innerText='Upload'
|
||||
btn.addEventListener('click', () => {
|
||||
let inp = document.createElement('input')
|
||||
inp.type = 'file'
|
||||
inp.accept = '.txt'
|
||||
inp.click()
|
||||
inp.addEventListener('change', event => {
|
||||
// 获取选择的文件
|
||||
const file = event.target.files[0]
|
||||
this.title = file.name.split('.')[0]
|
||||
|
||||
// console.log(file.name.split('.')[0])
|
||||
// 创建文件读取器
|
||||
const reader = new FileReader()
|
||||
|
||||
// 定义读取完成事件的回调函数
|
||||
reader.onload = event => {
|
||||
// 读取完成后的文本内容
|
||||
const fileContent = event.target.result.split('\n')
|
||||
const keywords = Array.from(fileContent, f => f.trim()).filter(
|
||||
f => f
|
||||
)
|
||||
// 打印文件内容
|
||||
// console.log(keywords)
|
||||
|
||||
mutable_prompt.value = keywords.join('\n')
|
||||
|
||||
inp.remove()
|
||||
}
|
||||
|
||||
// 以文本方式读取文件
|
||||
reader.readAsText(file)
|
||||
})
|
||||
})
|
||||
|
||||
widget.div.appendChild(btn)
|
||||
document.body.appendChild(widget.div)
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
const onRemoved = this.onRemoved
|
||||
this.onRemoved = () => {
|
||||
widget.div.remove()
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
if (this.onResize) {
|
||||
this.onResize(this.size)
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'RandomPrompt') {
|
||||
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.prompt.PromptSlide',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass == 'PromptSlide') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = async function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
const prompt_keyword = this.widgets.filter(
|
||||
w => w.name == 'prompt_keyword'
|
||||
)[0]
|
||||
// console.log('PromptSlide nodeData', prompt_keyword)
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'upload',
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, y, node.size[1])
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
widget.div = $el('div', {})
|
||||
|
||||
const btn = document.createElement('button')
|
||||
btn.innerText = 'Upload Keywords'
|
||||
|
||||
btn.style = `cursor: pointer;
|
||||
font-weight: 300;
|
||||
margin: 2px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid; height: 30px;min-width: 122px;
|
||||
`
|
||||
|
||||
const select = document.createElement('select')
|
||||
select.style = `display:none;cursor: pointer;
|
||||
font-weight: 300;
|
||||
margin: 2px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-radius: 8px;
|
||||
border-color: var(--border-color);
|
||||
border-style: solid; height: 30px;min-width: 100px;
|
||||
`
|
||||
widget.select = select
|
||||
|
||||
// const btn=document.createElement('button');
|
||||
// btn.innerText='Upload'
|
||||
btn.addEventListener('click', () => {
|
||||
let inp = document.createElement('input')
|
||||
inp.type = 'file'
|
||||
inp.accept = '.txt'
|
||||
inp.click()
|
||||
inp.addEventListener('change', event => {
|
||||
// 获取选择的文件
|
||||
const file = event.target.files[0]
|
||||
this.title = file.name.split('.')[0]
|
||||
|
||||
// console.log(file.name.split('.')[0])
|
||||
// 创建文件读取器
|
||||
const reader = new FileReader()
|
||||
|
||||
// 定义读取完成事件的回调函数
|
||||
reader.onload = event => {
|
||||
// 读取完成后的文本内容
|
||||
const fileContent = event.target.result.split('\n')
|
||||
const keywords = Array.from(fileContent, f => f.trim()).filter(
|
||||
f => f
|
||||
)
|
||||
// 打印文件内容
|
||||
// console.log(keywords)
|
||||
|
||||
widget.value = JSON.stringify(keywords)
|
||||
|
||||
// let ks = getLocalData(`_mixlab_PromptSlide`)
|
||||
// ks[this.id] = keywords
|
||||
// setLocalDataOfWin(`_mixlab_PromptSlide`, ks)
|
||||
|
||||
createSelect(select, keywords, prompt_keyword)
|
||||
|
||||
inp.remove()
|
||||
}
|
||||
|
||||
// 以文本方式读取文件
|
||||
reader.readAsText(file)
|
||||
})
|
||||
})
|
||||
|
||||
widget.div.appendChild(btn)
|
||||
widget.div.appendChild(select)
|
||||
document.body.appendChild(widget.div)
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
const onRemoved = this.onRemoved
|
||||
this.onRemoved = () => {
|
||||
widget.div.remove()
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
if (this.onResize) {
|
||||
this.onResize(this.size)
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'PromptSlide') {
|
||||
try {
|
||||
let prompt = node.widgets.filter(w => w.name === 'prompt_keyword')[0]
|
||||
// let ks = getLocalData(`_mixlab_PromptSlide`)
|
||||
let uploadWidget = node.widgets.filter(w => w.name == 'upload')[0]
|
||||
// console.log('##widget', uploadWidget.value)
|
||||
let keywords = JSON.parse(uploadWidget.value)
|
||||
// console.log('keywords',keywords)
|
||||
let widget = node.widgets.filter(w => w.select)[0]
|
||||
if (keywords && keywords[0]) {
|
||||
widget.select.style.display = 'block'
|
||||
createSelect(widget.select, keywords, prompt)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const _createResult = async (node, widget, message) => {
|
||||
widget.div.innerHTML = ``
|
||||
|
||||
const width = node.size[0] * 0.5 - 12
|
||||
|
||||
let height_add = 0
|
||||
|
||||
for (let index = 0; index < message._images.length; index++) {
|
||||
const imgs = message._images[index]
|
||||
|
||||
for (const img of imgs) {
|
||||
let url = api.apiURL(
|
||||
`/view?filename=${encodeURIComponent(img.filename)}&type=${
|
||||
img.type
|
||||
}&subfolder=${
|
||||
img.subfolder
|
||||
}${app.getPreviewFormatParam()}${app.getRandParam()}`
|
||||
)
|
||||
|
||||
let image = await createImage(url)
|
||||
|
||||
// 创建card
|
||||
let div = document.createElement('div')
|
||||
div.className = 'card'
|
||||
div.draggable = true
|
||||
|
||||
div.ondragend = async event => {
|
||||
console.log('拖动停止')
|
||||
let url = div.querySelector('img').src
|
||||
|
||||
let blob = await fetchImage(url)
|
||||
|
||||
let imageNode = null
|
||||
// No image node selected: add a new one
|
||||
if (!imageNode) {
|
||||
const newNode = LiteGraph.createNode('LoadImage')
|
||||
newNode.pos = [...app.canvas.graph_mouse]
|
||||
imageNode = app.graph.add(newNode)
|
||||
app.graph.change()
|
||||
}
|
||||
|
||||
// const blob = item.getAsFile();
|
||||
imageNode.pasteFile(blob)
|
||||
}
|
||||
|
||||
div.setAttribute('data-scale', image.naturalHeight / image.naturalWidth)
|
||||
|
||||
let h = (image.naturalHeight * width) / image.naturalWidth
|
||||
if (index % 2 === 0) height_add += h
|
||||
div.style = `width: ${width}px;height:${h}px;position: relative;margin: 4px;`
|
||||
|
||||
div.innerHTML = `<a href="${url}"
|
||||
data-pswp-width="${image.naturalWidth}"
|
||||
data-pswp-height="${image.naturalHeight}"
|
||||
target="_blank">
|
||||
<img src="${url}" style='width: 100%' alt="${message.prompts[index]}"/>
|
||||
</a>
|
||||
<p style="position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: 0.6;
|
||||
background-color: var(--comfy-input-bg);
|
||||
color: var(--descrip-text);
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
padding: 5px;
|
||||
text-align: left;">${message.prompts[index]}</p>`
|
||||
widget.div.appendChild(div)
|
||||
}
|
||||
}
|
||||
|
||||
node.size[1] = 98 + height_add
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.prompt.PromptImage',
|
||||
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass == 'PromptImage') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
console.log('#orig_nodeCreated', this)
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'result',
|
||||
draw (ctx, node, widget_width, y, widget_height) {
|
||||
Object.assign(this.div.style, {
|
||||
...get_position_style(ctx, widget_width, y, node.size[1]),
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'space-between',
|
||||
// outline: '1px solid red',
|
||||
paddingLeft: '0px',
|
||||
width: widget_width + 'px'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
widget.div = $el('div', {})
|
||||
widget.div.className = 'prompt_image_output'
|
||||
|
||||
document.body.appendChild(widget.div)
|
||||
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
initLightBox()
|
||||
|
||||
const onRemoved = this.onRemoved
|
||||
this.onRemoved = () => {
|
||||
widget.div.remove()
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
const onResize = this.onResize
|
||||
this.onResize = function () {
|
||||
// 缩放发生
|
||||
// console.log('##缩放发生', this.size)
|
||||
let w = this.size[0] * 0.5 - 12
|
||||
Array.from(widget.div.querySelectorAll('.card'), card => {
|
||||
card.style.width = `${w}px`
|
||||
card.style.height = `${
|
||||
w * parseFloat(card.getAttribute('data-scale'))
|
||||
}px`
|
||||
})
|
||||
return onResize?.apply(this, arguments)
|
||||
}
|
||||
|
||||
// this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = async function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
console.log('#PromptImage', message.prompts, message._images)
|
||||
// window._mixlab_app_json = message.json
|
||||
try {
|
||||
let widget = this.widgets.filter(w => w.name === 'result')[0]
|
||||
widget.value = message
|
||||
_createResult(this, widget, { ...message })
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
}
|
||||
|
||||
this.serialize_widgets = true //需要保存参数
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'PromptImage') {
|
||||
// await sleep(0)
|
||||
let widget = node.widgets.filter(w => w.name === 'result')[0]
|
||||
console.log('widget.value', widget.value)
|
||||
|
||||
initLightBox()
|
||||
|
||||
let cards = widget.div.querySelectorAll('.card')
|
||||
if (cards.length == 0) node.size = [280, 120]
|
||||
|
||||
_createResult(node, widget, widget.value)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,302 @@
|
||||
const smart_connect_config_input = [
|
||||
{
|
||||
node_type: 'CLIPTextEncode',
|
||||
node_widget_name: 'text',
|
||||
inputNodeName: 'RandomPrompt',
|
||||
inputNode_output_name: 'STRING'
|
||||
},
|
||||
{
|
||||
node_type: 'CLIPTextEncode',
|
||||
node_widget_name: 'text',
|
||||
inputNodeName: 'EmbeddingPrompt',
|
||||
inputNode_output_name: 'STRING'
|
||||
},
|
||||
{
|
||||
node_type: 'CLIPTextEncode',
|
||||
node_widget_name: 'text',
|
||||
inputNodeName: 'ChinesePrompt_Mix',
|
||||
inputNode_output_name: 'prompt'
|
||||
},
|
||||
{
|
||||
node_type: 'CheckpointLoaderSimple',
|
||||
node_widget_name: 'ckpt_name',
|
||||
inputNodeName: 'CkptNames_',
|
||||
inputNode_output_name: 'ckpt_names'
|
||||
},
|
||||
{
|
||||
node_type: 'KSampler',
|
||||
node_widget_name: 'sampler_name',
|
||||
inputNodeName: 'SamplerNames_',
|
||||
inputNode_output_name: 'sampler_names'
|
||||
},
|
||||
{
|
||||
node_type: 'LoraLoaderModelOnly',
|
||||
node_widget_name: 'lora_name',
|
||||
inputNodeName: 'LoraNames_',
|
||||
inputNode_output_name: 'lora_names'
|
||||
},
|
||||
{
|
||||
node_type: 'LoadLoRA',
|
||||
node_widget_name: 'lora_name',
|
||||
inputNodeName: 'LoraNames_',
|
||||
inputNode_output_name: 'lora_names'
|
||||
},
|
||||
{
|
||||
node_type: 'Moondream',
|
||||
node_widget_name: 'image',
|
||||
inputNodeName: 'LoadImage',
|
||||
inputNode_output_name: 'IMAGE'
|
||||
}
|
||||
]
|
||||
|
||||
const smart_connect_config_output = [
|
||||
{
|
||||
node_type: 'LoadImage',
|
||||
node_output_name: 'IMAGE',
|
||||
outputNodeName: 'ClipInterrogator',
|
||||
outputNode_input_name: 'image'
|
||||
},
|
||||
{
|
||||
node_type: 'VAEDecode',
|
||||
node_output_name: 'IMAGE',
|
||||
outputNodeName: 'PromptImage',
|
||||
outputNode_input_name: 'images'
|
||||
},
|
||||
{
|
||||
node_type: 'VAEDecode',
|
||||
node_output_name: 'IMAGE',
|
||||
outputNodeName: 'PreviewImage',
|
||||
outputNode_input_name: 'images'
|
||||
},
|
||||
{
|
||||
node_type: 'VAEDecode',
|
||||
node_output_name: 'IMAGE',
|
||||
outputNodeName: 'SaveImage',
|
||||
outputNode_input_name: 'images'
|
||||
},
|
||||
{
|
||||
node_type: 'Moondream',
|
||||
node_output_name: 'STRING',
|
||||
outputNodeName: 'ShowTextForGPT',
|
||||
outputNode_input_name: 'text'
|
||||
}
|
||||
]
|
||||
|
||||
// import {
|
||||
// convertToInput,
|
||||
// getConfig,
|
||||
// isConvertableWidget
|
||||
// } from '../../../extensions/core/widgetInputs.js'
|
||||
|
||||
const CONVERTED_TYPE = 'converted-widget'
|
||||
const GET_CONFIG = Symbol()
|
||||
|
||||
function getConfig (widgetName) {
|
||||
const { nodeData } = this.constructor
|
||||
return (
|
||||
nodeData?.input?.required[widgetName] ??
|
||||
nodeData?.input?.optional?.[widgetName]
|
||||
)
|
||||
}
|
||||
|
||||
function hideWidget (node, widget, suffix = '') {
|
||||
widget.origType = widget.type
|
||||
widget.origComputeSize = widget.computeSize
|
||||
widget.origSerializeValue = widget.serializeValue
|
||||
widget.computeSize = () => [0, -4] // -4 is due to the gap litegraph adds between widgets automatically
|
||||
widget.type = CONVERTED_TYPE + suffix
|
||||
widget.serializeValue = () => {
|
||||
// Prevent serializing the widget if we have no input linked
|
||||
if (!node.inputs) {
|
||||
return undefined
|
||||
}
|
||||
let node_input = node.inputs.find(i => i.widget?.name === widget.name)
|
||||
|
||||
if (!node_input || !node_input.link) {
|
||||
return undefined
|
||||
}
|
||||
return widget.origSerializeValue
|
||||
? widget.origSerializeValue()
|
||||
: widget.value
|
||||
}
|
||||
|
||||
// Hide any linked widgets, e.g. seed+seedControl
|
||||
if (widget.linkedWidgets) {
|
||||
for (const w of widget.linkedWidgets) {
|
||||
hideWidget(node, w, ':' + widget.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertToInput (node, widget, config) {
|
||||
hideWidget(node, widget)
|
||||
|
||||
const type = config[0]
|
||||
|
||||
// Add input and store widget config for creating on primitive node
|
||||
const sz = node.size
|
||||
node.addInput(widget.name, type, {
|
||||
widget: { name: widget.name, [GET_CONFIG]: () => config }
|
||||
})
|
||||
|
||||
for (const widget of node.widgets) {
|
||||
widget.last_y += LiteGraph.NODE_SLOT_HEIGHT
|
||||
}
|
||||
|
||||
// Restore original size but grow if needed
|
||||
node.setSize([Math.max(sz[0], node.size[0]), Math.max(sz[1], node.size[1])])
|
||||
}
|
||||
|
||||
export function smart_init () {
|
||||
LGraphCanvas.prototype._createNodeForInput = function (
|
||||
node,
|
||||
widget,
|
||||
inputNodeName,
|
||||
inputNode_slot
|
||||
) {
|
||||
// console.log(node.pos)
|
||||
|
||||
// var widget = node.widgets.filter(w => w.name === node_widget_name)[0]
|
||||
if (widget) {
|
||||
// 如果有存在的,没有连线输出的,自动连,不新建
|
||||
let input_node = null
|
||||
|
||||
Array.from(app.graph.findNodesByType(inputNodeName), n => {
|
||||
var links = n.outputs.filter(o => o.name === inputNode_slot)[0].links
|
||||
// console.log(links)
|
||||
if (!links || links?.length === 0) input_node = n
|
||||
})
|
||||
// 新建
|
||||
if (!input_node) {
|
||||
input_node = LiteGraph.createNode(inputNodeName)
|
||||
input_node.pos = [node.pos[0] - node.size[0] - 24, node.pos[1] - 48]
|
||||
app.canvas.graph.add(input_node, false)
|
||||
} else {
|
||||
input_node.pos = [node.pos[0] - node.size[0] - 24, node.pos[1] - 48]
|
||||
}
|
||||
|
||||
const config = getConfig.call(node, widget.name) ?? [
|
||||
widget.type,
|
||||
widget.options || {}
|
||||
]
|
||||
let node_slotType = config[0]
|
||||
// 如果input没有,则创建
|
||||
if (!node.inputs?.filter(inp => inp.name === widget.name)[0]||!node.inputs)
|
||||
convertToInput(node, widget, config)
|
||||
input_node.connectByType(inputNode_slot, node, node_slotType)
|
||||
}
|
||||
}
|
||||
|
||||
LGraphCanvas.prototype._createNodeForOutput = function (
|
||||
node,
|
||||
widget,
|
||||
outputNodeName,
|
||||
outputNode_slot
|
||||
) {
|
||||
if (widget) {
|
||||
let output_node
|
||||
Array.from(app.graph.findNodesByType(outputNodeName), n => {
|
||||
var links = n.inputs.filter(o => o.name === outputNode_slot)[0].links
|
||||
// console.log(links)
|
||||
if (!links || links?.length === 0) output_node = n
|
||||
})
|
||||
console.log('output_node', output_node, widget.name)
|
||||
|
||||
if (!output_node) {
|
||||
// 新建
|
||||
output_node = LiteGraph.createNode(outputNodeName)
|
||||
output_node.pos = [node.pos[0] + node.size[0] + 24, node.pos[1] - 48]
|
||||
app.canvas.graph.add(output_node, false)
|
||||
} else {
|
||||
output_node.pos = [node.pos[0] + node.size[0] + 24, node.pos[1] - 48]
|
||||
}
|
||||
|
||||
const config = getConfig.call(node, widget.name) ?? [
|
||||
widget.type,
|
||||
widget.options || {}
|
||||
]
|
||||
let node_slotType = config[0]
|
||||
console.log(node_slotType, output_node, outputNode_slot)
|
||||
let type = output_node.inputs.filter(
|
||||
inp => inp.name == outputNode_slot
|
||||
)[0].type
|
||||
node.connectByType(node_slotType, output_node, type)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function addSmartMenu (options, node) {
|
||||
let sopts = []
|
||||
|
||||
for (const sc of smart_connect_config_input) {
|
||||
// 有智能推荐,则出现
|
||||
if (node.type === sc.node_type) {
|
||||
// console.log('smart',node)
|
||||
// 则出现 randomPrompt
|
||||
// CLIPTextEncode 的widget ,name== 'text'
|
||||
let node_widget_name = sc.node_widget_name
|
||||
let widget = node.widgets.filter(w => w.name === node_widget_name)[0]
|
||||
if (!widget) {
|
||||
// 控件没有,则查找inputs
|
||||
widget = node.inputs.filter(w => w.name === node_widget_name)[0]
|
||||
}
|
||||
|
||||
let isLinkNull = true
|
||||
// 如果input里已经有,但是link为空
|
||||
if (node.inputs?.filter(inp => inp.name === node_widget_name)[0]) {
|
||||
isLinkNull =
|
||||
node.inputs.filter(inp => inp.name === node_widget_name)[0].link ===
|
||||
null
|
||||
}
|
||||
|
||||
if (widget && isLinkNull) {
|
||||
sopts.push({
|
||||
content: sc.inputNodeName.split('_')[0] + '➡️',
|
||||
callback: () => {
|
||||
LGraphCanvas.prototype._createNodeForInput(
|
||||
node, //当前node
|
||||
widget, //当前node里需要自动连线的widget
|
||||
sc.inputNodeName, //作为input的node type
|
||||
sc.inputNode_output_name // 作为input的node的outputs的name. the input slot type of the target node
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const sc of smart_connect_config_output) {
|
||||
if (node.type === sc.node_type) {
|
||||
let node_output_name = sc.node_output_name
|
||||
const widget = node.outputs.filter(w => w.name === node_output_name)[0]
|
||||
|
||||
let isLinkNull = true
|
||||
// 如果output里 link为空
|
||||
if (node.outputs?.filter(inp => inp.name === node_output_name)[0]) {
|
||||
isLinkNull =
|
||||
node.outputs.filter(inp => inp.name === node_output_name)[0].links
|
||||
?.length === 0
|
||||
if (!node.outputs.filter(inp => inp.name === node_output_name)[0].links)
|
||||
isLinkNull = true
|
||||
}
|
||||
|
||||
if (widget && isLinkNull) {
|
||||
sopts.push({
|
||||
content: '➡️' + sc.outputNodeName.split('_')[0],
|
||||
callback: () => {
|
||||
LGraphCanvas.prototype._createNodeForOutput(
|
||||
node, //当前node
|
||||
widget, //当前node里需要自动连线的widget
|
||||
sc.outputNodeName, //作为input的node type
|
||||
sc.outputNode_input_name // 作为input的node的outputs的name. the input slot type of the target node
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sopts.length > 0) options = [...sopts, null, ...options]
|
||||
|
||||
return options
|
||||
}
|
||||
+514
-167
@@ -1,7 +1,4 @@
|
||||
import { app } from '../../../scripts/app.js'
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
import { closeIcon } from './svg_icons.js'
|
||||
|
||||
import {
|
||||
@@ -9,6 +6,67 @@ import {
|
||||
GroupNodeHandler
|
||||
} from '../../../extensions/core/groupNode.js'
|
||||
|
||||
import { smart_init, addSmartMenu } from './smart_connect.js'
|
||||
|
||||
function copyNodeValues (src, dest) {
|
||||
// title
|
||||
dest.title = src.title
|
||||
|
||||
// copy input connections
|
||||
|
||||
for (let i in src.inputs) {
|
||||
let input = src.inputs[i]
|
||||
if (input.link) {
|
||||
let link = app.graph.links[input.link]
|
||||
let src_node = app.graph.getNodeById(link.origin_id)
|
||||
if (dest.inputs.filter(inp => inp.name === input.name).length === 0) {
|
||||
// 没有,name换了
|
||||
let dInp = dest.inputs.filter(inp => inp.type === input.type)
|
||||
if (dInp.length === 1) {
|
||||
src_node.connect(link.origin_slot, dest.id, dInp[0].name)
|
||||
}
|
||||
} else {
|
||||
src_node.connect(link.origin_slot, dest.id, input.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy output connections
|
||||
let output_links = {}
|
||||
for (let i in src.outputs) {
|
||||
let output = src.outputs[i]
|
||||
if (output.links) {
|
||||
let links = []
|
||||
for (let j in output.links) {
|
||||
links.push(app.graph.links[output.links[j]])
|
||||
}
|
||||
output_links[output.name] = links
|
||||
}
|
||||
}
|
||||
|
||||
for (let i in dest.outputs) {
|
||||
let links = output_links[dest.outputs[i].name]
|
||||
if (links) {
|
||||
for (let j in links) {
|
||||
let link = links[j]
|
||||
let target_node = app.graph.getNodeById(link.target_id)
|
||||
dest.connect(parseInt(i), target_node, link.target_slot)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy widgets
|
||||
for (const w of src.widgets) {
|
||||
for (const d of dest.widgets) {
|
||||
if (w.name === d.name) {
|
||||
d.value = w.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.graph.afterChange()
|
||||
}
|
||||
|
||||
function deepEqual (obj1, obj2) {
|
||||
if (typeof obj1 !== typeof obj2) {
|
||||
return false
|
||||
@@ -48,6 +106,40 @@ async function get_nodes_map () {
|
||||
return await res.json()
|
||||
}
|
||||
|
||||
function get_url () {
|
||||
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||
let api_base = ''
|
||||
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||
return url
|
||||
}
|
||||
|
||||
async function get_my_app (filename = null, category = '') {
|
||||
let url = get_url()
|
||||
const res = await fetch(`${url}/mixlab/workflow`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
task: 'my_app',
|
||||
filename,
|
||||
category,
|
||||
admin: true
|
||||
})
|
||||
})
|
||||
let result = await res.json()
|
||||
let data = []
|
||||
try {
|
||||
for (const res of result.data) {
|
||||
let { app, workflow } = res.data
|
||||
if (app.filename)
|
||||
data.push({
|
||||
...app,
|
||||
data: workflow,
|
||||
date: res.date
|
||||
})
|
||||
}
|
||||
} catch (error) {}
|
||||
return data
|
||||
}
|
||||
|
||||
function loadCSS (url) {
|
||||
var link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
@@ -541,22 +633,71 @@ app.registerExtension({
|
||||
}
|
||||
}
|
||||
|
||||
LGraphCanvas.prototype.fixTheNode = function (node) {
|
||||
let new_node = LiteGraph.createNode(node.comfyClass)
|
||||
new_node.pos = [node.pos[0], node.pos[1]]
|
||||
app.canvas.graph.add(new_node, false)
|
||||
copyNodeValues(node, new_node)
|
||||
app.canvas.graph.remove(node)
|
||||
}
|
||||
|
||||
smart_init()
|
||||
|
||||
const getNodeMenuOptions = LGraphCanvas.prototype.getNodeMenuOptions // store the existing method
|
||||
LGraphCanvas.prototype.getNodeMenuOptions = function (node) {
|
||||
// replace it
|
||||
const options = getNodeMenuOptions.apply(this, arguments) // start by calling the stored one
|
||||
node.setDirtyCanvas(true, true) // force a redraw of (foreground, background)
|
||||
console.log('getNodeMenuOptions', node.type == 'CLIPTextEncode')
|
||||
|
||||
return [
|
||||
let opts = [
|
||||
{
|
||||
content: 'Help ♾️Mixlab', // with a name
|
||||
callback: () => {
|
||||
LGraphCanvas.prototype.helpAboutNode(node)
|
||||
} // and the callback
|
||||
},
|
||||
null,
|
||||
...options
|
||||
] // and return the options
|
||||
{
|
||||
content: 'Fix node v2', // with a name
|
||||
callback: () => {
|
||||
LGraphCanvas.prototype.fixTheNode(node)
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
opts = addSmartMenu(opts,node)
|
||||
|
||||
// if (node.type == 'CLIPTextEncode') {
|
||||
// // 则出现 randomPrompt
|
||||
// // CLIPTextEncode 的widget ,name== 'text'
|
||||
// let node_widget_name = 'text'
|
||||
// const widget = node.widgets.filter(w => w.name === node_widget_name)[0]
|
||||
|
||||
// let mixlab_nodes_smart_connect= [{node_type:'CLIPTextEncode',
|
||||
// node_widget_name:'text',
|
||||
// inputNodeName:'RandomPrompt',
|
||||
// inputNode_output_type:'STRING'}]
|
||||
|
||||
// if (widget) {
|
||||
// opts = [
|
||||
// {
|
||||
// content: 'RandomPrompt',
|
||||
// callback: () => {
|
||||
// LGraphCanvas.prototype._createNodeForInput(
|
||||
// node, //当前node
|
||||
// widget,//当前node里需要自动连线的widget
|
||||
// 'RandomPrompt',//作为input的node type
|
||||
// 'STRING'// 作为input的node的outputs的type. the input slot type of the target node
|
||||
// )
|
||||
// }
|
||||
// },
|
||||
// null,
|
||||
// ...opts
|
||||
// ]
|
||||
// }
|
||||
// }
|
||||
|
||||
return [...opts, null, ...options] // and return the options
|
||||
}
|
||||
|
||||
const getGroupMenuOptions = LGraphCanvas.prototype.getGroupMenuOptions // store the existing method
|
||||
@@ -663,182 +804,388 @@ app.registerExtension({
|
||||
})
|
||||
} // and the callback
|
||||
},
|
||||
{
|
||||
content: `Remove Group&Nodes ♾️Mixlab`, // with a name
|
||||
callback: async (value, opts, e, menu, group) => {
|
||||
// console.log(group)
|
||||
let nodes = group._nodes
|
||||
for (const node of nodes) {
|
||||
app.graph.remove(node)
|
||||
}
|
||||
app.graph.remove(group)
|
||||
} // and the callback
|
||||
},
|
||||
null,
|
||||
...options
|
||||
] // and return the options
|
||||
}
|
||||
LGraphCanvas.prototype.centerOnNode = function (node) {
|
||||
// console.log(node)
|
||||
var dpr = window.devicePixelRatio || 1 // 获取设备像素比
|
||||
this.ds.offset[0] =
|
||||
-node.pos[0] -
|
||||
node.size[0] * 0.5 +
|
||||
(this.canvas.width * 0.5) / (this.ds.scale * dpr) // 考虑设备像素比
|
||||
this.ds.offset[1] =
|
||||
-node.pos[1] -
|
||||
node.size[1] * 0.5 +
|
||||
(this.canvas.height * 0.5) / (this.ds.scale * dpr) // 考虑设备像素比
|
||||
this.setDirty(true, true)
|
||||
}
|
||||
},
|
||||
async setup () {
|
||||
// Add canvas menu options
|
||||
const orig = LGraphCanvas.prototype.getCanvasMenuOptions
|
||||
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
|
||||
const options = orig.apply(this, arguments)
|
||||
setup () {
|
||||
setTimeout(async () => {
|
||||
// Add canvas menu options
|
||||
const orig = LGraphCanvas.prototype.getCanvasMenuOptions
|
||||
|
||||
options.push(null, {
|
||||
content: `Nodes Map ♾️Mixlab`,
|
||||
disabled: false, // or a function determining whether to disable
|
||||
callback: async () => {
|
||||
nodesMap =
|
||||
nodesMap && Object.keys(nodesMap).length > 0
|
||||
? nodesMap
|
||||
: await getCustomnodeMappings('url')
|
||||
const apps = await get_my_app()
|
||||
|
||||
const nodesDiv = document.createDocumentFragment()
|
||||
const nodes = (await app.graphToPrompt()).output
|
||||
let apps_map = { 0: [] }
|
||||
|
||||
// console.log('[Mixlab]', 'loaded graph node: ', app)
|
||||
let div =
|
||||
document.querySelector('#mixlab_find_the_node') ||
|
||||
document.createElement('div')
|
||||
div.id = 'mixlab_find_the_node'
|
||||
div.style = `
|
||||
flex-direction: column;
|
||||
align-items: end;
|
||||
display:flex;position: absolute;
|
||||
top: 50px; left: 50px; width: 200px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
padding: 10px;
|
||||
border: 1px solid black;z-index: 999999999;padding-top: 0;`
|
||||
for (const app of apps) {
|
||||
if (app.category) {
|
||||
if (!apps_map[app.category]) apps_map[app.category] = []
|
||||
apps_map[app.category].push(app)
|
||||
} else {
|
||||
apps_map['0'].push(app)
|
||||
}
|
||||
}
|
||||
|
||||
div.innerHTML = ''
|
||||
|
||||
let btn = document.createElement('div')
|
||||
btn.style = `display: flex;
|
||||
width: calc(100% - 24px);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
height: 44px;`
|
||||
let btnB = document.createElement('button')
|
||||
let textB = document.createElement('p')
|
||||
btn.appendChild(textB)
|
||||
btn.appendChild(btnB)
|
||||
textB.style.fontSize = '12px'
|
||||
textB.innerText = `Locate and navigate nodes ♾️Mixlab`
|
||||
|
||||
btnB.style = `float: right; border: none; color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
|
||||
btnB.addEventListener('click', () => {
|
||||
div.style.display = 'none'
|
||||
})
|
||||
btnB.innerText = 'X'
|
||||
|
||||
// 悬浮框拖动事件
|
||||
div.addEventListener('mousedown', function (e) {
|
||||
var startX = e.clientX
|
||||
var startY = e.clientY
|
||||
var offsetX = div.offsetLeft
|
||||
var offsetY = div.offsetTop
|
||||
|
||||
function moveBox (e) {
|
||||
var newX = e.clientX
|
||||
var newY = e.clientY
|
||||
var deltaX = newX - startX
|
||||
var deltaY = newY - startY
|
||||
div.style.left = offsetX + deltaX + 'px'
|
||||
div.style.top = offsetY + deltaY + 'px'
|
||||
}
|
||||
|
||||
function stopMoving () {
|
||||
document.removeEventListener('mousemove', moveBox)
|
||||
document.removeEventListener('mouseup', stopMoving)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', moveBox)
|
||||
document.addEventListener('mouseup', stopMoving)
|
||||
})
|
||||
|
||||
div.appendChild(btn)
|
||||
|
||||
const updateNodes = (ns, nd) => {
|
||||
for (let nodeId in ns) {
|
||||
let n = ns[nodeId].class_type
|
||||
if (nodesMap[n]) {
|
||||
const { url, title } = nodesMap[n]
|
||||
let d = document.createElement('button')
|
||||
d.style = `text-align: left;margin:6px;color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
|
||||
d.addEventListener('click', () => {
|
||||
const node = app.graph.getNodeById(nodeId)
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(1)
|
||||
})
|
||||
d.addEventListener('mouseover', async () => {
|
||||
// console.log('mouseover')
|
||||
let n = (await app.graphToPrompt()).output
|
||||
if (!deepEqual(n, ns)) {
|
||||
nd.innerHTML = ''
|
||||
updateNodes(n, nd)
|
||||
}
|
||||
})
|
||||
|
||||
d.innerHTML = `
|
||||
<span>${'#' + nodeId} ${n}</span>
|
||||
<a href="${url}" target="_blank" style="text-decoration: none;">🔗</a>
|
||||
`
|
||||
d.title = title
|
||||
|
||||
nd.appendChild(d)
|
||||
let apps_opts = []
|
||||
for (const category in apps_map) {
|
||||
console.log('category', typeof category)
|
||||
if (category === '0') {
|
||||
apps_opts.push(
|
||||
...Array.from(apps_map[category], a => {
|
||||
// console.log('#1级',a)
|
||||
return {
|
||||
content: `${a.name}_${a.version}`,
|
||||
has_submenu: false,
|
||||
callback: async () => {
|
||||
try {
|
||||
let item = (await get_my_app(a.filename))[0]
|
||||
if (item) {
|
||||
// console.log(item.data)
|
||||
app.loadGraphData(item.data)
|
||||
setTimeout(() => {
|
||||
const node = app.graph._nodes_in_order[0]
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(0.5)
|
||||
}, 1000)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let nodesDivv = document.createElement('div')
|
||||
|
||||
for (let nodeId in nodes) {
|
||||
let n = nodes[nodeId].class_type
|
||||
if (nodesMap[n]) {
|
||||
const { url, title: _title } = nodesMap[n]
|
||||
let title = app.graph.getNodeById(nodeId).title || _title
|
||||
let d = document.createElement('button')
|
||||
d.style = `text-align: left;margin:6px;color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
|
||||
d.addEventListener('click', () => {
|
||||
const node = app.graph.getNodeById(nodeId)
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(1)
|
||||
})
|
||||
d.addEventListener('mouseover', async () => {
|
||||
console.log('mouseover')
|
||||
let n = (await app.graphToPrompt()).output
|
||||
if (!deepEqual(n, nodes)) {
|
||||
nodesDivv.innerHTML = ''
|
||||
updateNodes(n, nodesDivv)
|
||||
})
|
||||
)
|
||||
} else {
|
||||
// 二级
|
||||
apps_opts.push({
|
||||
content: '🚀 ' + category,
|
||||
has_submenu: true,
|
||||
disabled: false,
|
||||
submenu: {
|
||||
options: Array.from(apps_map[category], a => {
|
||||
// console.log('#二级',a)
|
||||
return {
|
||||
content: `${a.name}_${a.version}`,
|
||||
callback: async () => {
|
||||
try {
|
||||
let item = (await get_my_app(a.filename, a.category))[0]
|
||||
if (item) {
|
||||
// console.log(item.data)
|
||||
app.loadGraphData(item.data)
|
||||
setTimeout(() => {
|
||||
const node = app.graph._nodes_in_order[0]
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(0.5)
|
||||
}, 1000)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
d.innerHTML = `
|
||||
<span>${'#' + nodeId} ${title}</span>
|
||||
<a href="${url}" target="_blank" style="text-decoration: none;">🔗</a>
|
||||
`
|
||||
d.title = n
|
||||
// console.log('apps',apps_map, apps_opts,apps)
|
||||
LGraphCanvas.prototype.getCanvasMenuOptions = function () {
|
||||
const options = orig.apply(this, arguments)
|
||||
|
||||
nodesDiv.appendChild(d)
|
||||
options.push(
|
||||
null,
|
||||
{
|
||||
content: `Nodes Map ♾️Mixlab`,
|
||||
disabled: false,
|
||||
callback: async () => {
|
||||
nodesMap =
|
||||
nodesMap && Object.keys(nodesMap).length > 0
|
||||
? nodesMap
|
||||
: await getCustomnodeMappings('url')
|
||||
|
||||
const nodesDiv = document.createDocumentFragment()
|
||||
const nodes = (await app.graphToPrompt()).output
|
||||
|
||||
// console.log('[Mixlab]', 'loaded graph node: ', app)
|
||||
let div =
|
||||
document.querySelector('#mixlab_find_the_node') ||
|
||||
document.createElement('div')
|
||||
div.id = 'mixlab_find_the_node'
|
||||
div.style = `
|
||||
flex-direction: column;
|
||||
align-items: end;
|
||||
display:flex;position: absolute;
|
||||
top: 50px; left: 50px; width: 200px;
|
||||
color: var(--descrip-text);
|
||||
background-color: var(--comfy-menu-bg);
|
||||
padding: 10px;
|
||||
border: 1px solid black;z-index: 999999999;padding-top: 0;`
|
||||
|
||||
div.innerHTML = ''
|
||||
|
||||
let btn = document.createElement('div')
|
||||
btn.style = `display: flex;
|
||||
width: calc(100% - 24px);
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 12px;
|
||||
height: 44px;`
|
||||
let btnB = document.createElement('button')
|
||||
let textB = document.createElement('p')
|
||||
btn.appendChild(textB)
|
||||
btn.appendChild(btnB)
|
||||
textB.style.fontSize = '12px'
|
||||
textB.innerText = `Locate and navigate nodes ♾️Mixlab`
|
||||
|
||||
btnB.style = `float: right; border: none; color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg); border-color: var(--border-color);cursor: pointer;`
|
||||
btnB.addEventListener('click', () => {
|
||||
div.style.display = 'none'
|
||||
})
|
||||
btnB.innerText = 'X'
|
||||
|
||||
// 悬浮框拖动事件
|
||||
div.addEventListener('mousedown', function (e) {
|
||||
var startX = e.clientX
|
||||
var startY = e.clientY
|
||||
var offsetX = div.offsetLeft
|
||||
var offsetY = div.offsetTop
|
||||
|
||||
function moveBox (e) {
|
||||
var newX = e.clientX
|
||||
var newY = e.clientY
|
||||
var deltaX = newX - startX
|
||||
var deltaY = newY - startY
|
||||
div.style.left = offsetX + deltaX + 'px'
|
||||
div.style.top = offsetY + deltaY + 'px'
|
||||
}
|
||||
|
||||
function stopMoving () {
|
||||
document.removeEventListener('mousemove', moveBox)
|
||||
document.removeEventListener('mouseup', stopMoving)
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', moveBox)
|
||||
document.addEventListener('mouseup', stopMoving)
|
||||
})
|
||||
|
||||
div.appendChild(btn)
|
||||
|
||||
const updateNodes = (ns, nd) => {
|
||||
let appInfoNodes = {}
|
||||
try {
|
||||
let appInfo = app.graph._nodes.filter(
|
||||
n => n.type === 'AppInfo'
|
||||
)[0]
|
||||
if (appInfo) {
|
||||
appInfoNodes[appInfo.id] = 2
|
||||
for (const id of appInfo.widgets[1].value.split('\n')) {
|
||||
if (id && id.trim() && parseInt(id)) {
|
||||
appInfoNodes[id] = 0
|
||||
}
|
||||
}
|
||||
for (const id of app.graph._nodes
|
||||
.filter(n => n.type === 'AppInfo')[0]
|
||||
.widgets[2].value.split('\n')) {
|
||||
if (id && id.trim() && parseInt(id)) {
|
||||
appInfoNodes[id] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
for (let nodeId in ns) {
|
||||
let n = ns[nodeId].title || ns[nodeId].class_type
|
||||
if (nodesMap[n]) {
|
||||
const { url, title } = nodesMap[n]
|
||||
let d = document.createElement('button')
|
||||
d.style = `text-align: left;
|
||||
margin:6px;
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-color: ${
|
||||
appInfoNodes[nodeId] >= 0
|
||||
? appInfoNodes[nodeId] === 1
|
||||
? 'blue'
|
||||
: 'red'
|
||||
: 'var(--border-color)'
|
||||
};
|
||||
cursor: pointer;`
|
||||
|
||||
if (appInfoNodes[nodeId] === 2) {
|
||||
// appinfo
|
||||
d.style.backgroundColor = '#326328'
|
||||
d.style.color = '#ffffff'
|
||||
d.style.borderColor = 'transparent'
|
||||
}
|
||||
|
||||
d.addEventListener('click', () => {
|
||||
// console.log('node')
|
||||
const node = app.graph.getNodeById(nodeId)
|
||||
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(1)
|
||||
})
|
||||
d.addEventListener('mouseover', async () => {
|
||||
// console.log('mouseover')
|
||||
let n = (await app.graphToPrompt()).output
|
||||
if (!deepEqual(n, ns)) {
|
||||
nd.innerHTML = ''
|
||||
updateNodes(n, nd)
|
||||
}
|
||||
})
|
||||
|
||||
d.innerHTML = `
|
||||
<span>${'#' + nodeId} ${n}</span>
|
||||
<a href="${url}" target="_blank" style="text-decoration: none;">🔗</a>
|
||||
`
|
||||
d.title = title
|
||||
|
||||
nd.appendChild(d)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let nodesDivv = document.createElement('div')
|
||||
let appInfoNodes = {}
|
||||
try {
|
||||
let appInfo = app.graph._nodes.filter(
|
||||
n => n.type === 'AppInfo'
|
||||
)[0]
|
||||
if (appInfo) {
|
||||
appInfoNodes[appInfo.id] = 2
|
||||
for (const id of appInfo.widgets[1].value.split('\n')) {
|
||||
if (id && id.trim() && parseInt(id)) {
|
||||
appInfoNodes[id] = 0
|
||||
}
|
||||
}
|
||||
for (const id of app.graph._nodes
|
||||
.filter(n => n.type === 'AppInfo')[0]
|
||||
.widgets[2].value.split('\n')) {
|
||||
if (id && id.trim() && parseInt(id)) {
|
||||
appInfoNodes[id] = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
for (let nodeId in nodes) {
|
||||
let n = nodes[nodeId].class_type
|
||||
if (nodesMap[n]) {
|
||||
const { url, title: _title } = nodesMap[n]
|
||||
let title = app.graph.getNodeById(nodeId).title || _title
|
||||
let d = document.createElement('button')
|
||||
d.style = `text-align: left;
|
||||
margin:6px;
|
||||
color: var(--input-text);
|
||||
background-color: var(--comfy-input-bg);
|
||||
border-color: ${
|
||||
appInfoNodes[nodeId] >= 0
|
||||
? appInfoNodes[nodeId] === 1
|
||||
? 'blue'
|
||||
: 'red'
|
||||
: 'var(--border-color)'
|
||||
};
|
||||
cursor: pointer;`
|
||||
|
||||
if (appInfoNodes[nodeId] === 2) {
|
||||
// appinfo
|
||||
d.style.backgroundColor = '#326328'
|
||||
d.style.color = '#ffffff'
|
||||
d.style.borderColor = 'transparent'
|
||||
}
|
||||
|
||||
d.addEventListener('click', () => {
|
||||
console.log('click')
|
||||
const node = app.graph.getNodeById(nodeId)
|
||||
if (!node) return
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(1)
|
||||
})
|
||||
d.addEventListener('mouseover', async () => {
|
||||
// console.log('mouseover')
|
||||
let n = (await app.graphToPrompt()).output
|
||||
if (!deepEqual(n, nodes)) {
|
||||
nodesDivv.innerHTML = ''
|
||||
updateNodes(n, nodesDivv)
|
||||
}
|
||||
})
|
||||
|
||||
d.innerHTML = `
|
||||
<span>${'#' + nodeId} ${title}</span>
|
||||
<a href="${url}" target="_blank" style="text-decoration: none;">🔗</a>
|
||||
`
|
||||
d.title = n
|
||||
|
||||
nodesDiv.appendChild(d)
|
||||
}
|
||||
}
|
||||
|
||||
nodesDivv.appendChild(nodesDiv)
|
||||
nodesDivv.style = `overflow: scroll;
|
||||
height: 70vh;width: 100%;`
|
||||
|
||||
div.appendChild(nodesDivv)
|
||||
|
||||
if (!document.querySelector('#mixlab_find_the_node'))
|
||||
document.body.appendChild(div)
|
||||
}
|
||||
},
|
||||
{
|
||||
content: 'Workflow App ♾️Mixlab',
|
||||
has_submenu: true,
|
||||
disabled: false,
|
||||
submenu: {
|
||||
options: apps_opts
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
nodesDivv.appendChild(nodesDiv)
|
||||
nodesDivv.style = `overflow: scroll;
|
||||
height: 70vh;width: 100%;`
|
||||
|
||||
div.appendChild(nodesDivv)
|
||||
|
||||
if (!document.querySelector('#mixlab_find_the_node'))
|
||||
document.body.appendChild(div)
|
||||
}
|
||||
})
|
||||
|
||||
// options.push({
|
||||
// content: `Save For App ♾️Mixlab`,
|
||||
// disabled: false, // or a function determining whether to disable
|
||||
// callback: async () => {
|
||||
|
||||
// }
|
||||
// })
|
||||
return options
|
||||
}
|
||||
return options
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
// console.log(
|
||||
// '#ui init',
|
||||
// app.graph._nodes[app.graph._nodes.length - 1].id,
|
||||
// node.id
|
||||
// )
|
||||
try {
|
||||
// 用来居中显示节点
|
||||
if ((app.graph._nodes[app.graph._nodes.length - 1].id, node.id)) {
|
||||
app.canvas.centerOnNode(node)
|
||||
app.canvas.setZoom(0.45)
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
})
|
||||
|
||||
+228
-45
@@ -2,7 +2,7 @@ import { app } from '../../../scripts/app.js'
|
||||
import { api } from '../../../scripts/api.js'
|
||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||
import { $el } from '../../../scripts/ui.js'
|
||||
import { addValueControlWidget } from "../../../scripts/widgets.js";
|
||||
import { addValueControlWidget } from '../../../scripts/widgets.js'
|
||||
|
||||
const getLocalData = key => {
|
||||
let data = {}
|
||||
@@ -45,8 +45,47 @@ function get_position_style (ctx, widget_width, y, node_height) {
|
||||
}
|
||||
}
|
||||
|
||||
function hexToRGBA (hexColor) {
|
||||
var hex = hexColor.replace('#', '')
|
||||
var r = parseInt(hex.substring(0, 2), 16)
|
||||
var g = parseInt(hex.substring(2, 4), 16)
|
||||
var b = parseInt(hex.substring(4, 6), 16)
|
||||
|
||||
// 获取透明度的十六进制值
|
||||
var alphaHex = hex.substring(6)
|
||||
|
||||
// 将透明度的十六进制值转换为十进制值
|
||||
var alpha = parseInt(alphaHex, 16) / 255
|
||||
|
||||
return [r, g, b, alpha]
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.Color',
|
||||
init () {
|
||||
$el('link', {
|
||||
rel: 'stylesheet',
|
||||
href: '/extensions/comfyui-mixlab-nodes/lib/classic.min.css',
|
||||
parent: document.head
|
||||
})
|
||||
|
||||
$el('style', {
|
||||
textContent: `
|
||||
.pickr{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.pickr .pcr-button {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
outline: 1px solid white;
|
||||
}
|
||||
|
||||
`,
|
||||
parent: document.body
|
||||
})
|
||||
},
|
||||
async getCustomWidgets (app) {
|
||||
return {
|
||||
TCOLOR (node, inputName, inputData, app) {
|
||||
@@ -60,8 +99,17 @@ app.registerExtension({
|
||||
return [128, 32] // a method to compute the current size of the widget
|
||||
},
|
||||
async serializeValue (nodeId, widgetIndex) {
|
||||
let data = getLocalData('_mixlab_utils_color')
|
||||
return data[node.id] || '#000000'
|
||||
// let data = getLocalData('_mixlab_utils_color')
|
||||
// let hex = data[node.id] || '#000000'
|
||||
let hex = widget.value || '#000000'
|
||||
let [r, g, b, a] = hexToRGBA(hex)
|
||||
return {
|
||||
hex,
|
||||
r,
|
||||
g,
|
||||
b,
|
||||
a
|
||||
}
|
||||
}
|
||||
}
|
||||
// widget.something = something; // maybe adds stuff to it
|
||||
@@ -77,7 +125,7 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
console.log('Color nodeData', this.widgets)
|
||||
// console.log('Color nodeData', this.widgets)
|
||||
|
||||
const widget = {
|
||||
type: 'div',
|
||||
@@ -87,6 +135,7 @@ app.registerExtension({
|
||||
this.div.style,
|
||||
get_position_style(ctx, widget_width, 44, node.size[1])
|
||||
)
|
||||
// console.log('draw',y,node.widgets[0].last_y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,35 +143,9 @@ app.registerExtension({
|
||||
|
||||
document.body.appendChild(widget.div)
|
||||
|
||||
const inputDiv = (key, placeholder, value) => {
|
||||
const inputDiv = () => {
|
||||
let div = document.createElement('div')
|
||||
const ip = document.createElement('input')
|
||||
ip.type = 'color'
|
||||
ip.className = `${'comfy-multiline-input'} ${placeholder}`
|
||||
div.style = `display: flex;
|
||||
align-items: center;
|
||||
margin: 6px 8px;
|
||||
margin-top: 0;`
|
||||
ip.placeholder = placeholder
|
||||
ip.value = value
|
||||
|
||||
ip.style = `outline: none;
|
||||
border: none;
|
||||
padding: 4px;
|
||||
width: 100%;cursor: pointer;
|
||||
height: 32px;`
|
||||
const label = document.createElement('label')
|
||||
label.style = 'font-size: 10px;min-width:32px'
|
||||
label.innerText = placeholder
|
||||
div.appendChild(label)
|
||||
div.appendChild(ip)
|
||||
|
||||
ip.addEventListener('change', () => {
|
||||
let data = getLocalData(key)
|
||||
data[this.id] = ip.value.trim()
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
// console.log(this.id, ip.value.trim())
|
||||
})
|
||||
div.id = `color_picker_${this.id}`
|
||||
return div
|
||||
}
|
||||
|
||||
@@ -132,10 +155,85 @@ app.registerExtension({
|
||||
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
const pickr = Pickr.create({
|
||||
el: `#${inputColor.id}`,
|
||||
theme: 'classic', // or 'monolith', or 'nano'
|
||||
// closeOnScroll: true,
|
||||
default: '#000000',
|
||||
swatches: [
|
||||
'rgba(244, 67, 54, 1)',
|
||||
'rgba(233, 30, 99, 0.95)',
|
||||
'rgba(156, 39, 176, 0.9)',
|
||||
'rgba(103, 58, 183, 0.85)',
|
||||
'rgba(63, 81, 181, 0.8)',
|
||||
'rgba(33, 150, 243, 0.75)',
|
||||
'rgba(3, 169, 244, 0.7)',
|
||||
'rgba(0, 188, 212, 0.7)',
|
||||
'rgba(0, 150, 136, 0.75)',
|
||||
'rgba(76, 175, 80, 0.8)',
|
||||
'rgba(139, 195, 74, 0.85)',
|
||||
'rgba(205, 220, 57, 0.9)',
|
||||
'rgba(255, 235, 59, 0.95)',
|
||||
'rgba(255, 193, 7, 1)'
|
||||
],
|
||||
|
||||
components: {
|
||||
// Main components
|
||||
preview: true,
|
||||
opacity: true,
|
||||
hue: true,
|
||||
// Input / output Options
|
||||
interaction: {
|
||||
hex: true,
|
||||
rgba: true,
|
||||
hsla: true,
|
||||
hsva: true,
|
||||
cmyk: true,
|
||||
input: true,
|
||||
// clear: true,
|
||||
save: true,
|
||||
cancel: true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
pickr
|
||||
.on('save', (color, instance) => {
|
||||
// console.log('Event: "save"', color.toHEXA().toString())
|
||||
// let data = getLocalData('_mixlab_utils_color')
|
||||
// data[this.id] = color.toHEXA().toString()
|
||||
// localStorage.setItem('_mixlab_utils_color', JSON.stringify(data))
|
||||
|
||||
try {
|
||||
let tc = this.widgets.filter(w => w.type == 'TCOLOR')[0]
|
||||
tc.value = color.toHEXA().toString()
|
||||
} catch (error) {}
|
||||
})
|
||||
.on('cancel', instance => {
|
||||
pickr && pickr.hide()
|
||||
})
|
||||
this.pickr = pickr
|
||||
const handleMouseWheel = () => {
|
||||
try {
|
||||
this.pickr && this.pickr.hide()
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
document.addEventListener('wheel', handleMouseWheel)
|
||||
|
||||
const onRemoved = this.onRemoved
|
||||
this.onRemoved = () => {
|
||||
inputColor.remove()
|
||||
widget.div.remove()
|
||||
|
||||
try {
|
||||
this.pickr.destroyAndRemove()
|
||||
this.pickr = null
|
||||
document.removeEventListener('wheel', handleMouseWheel)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
return onRemoved?.()
|
||||
}
|
||||
|
||||
@@ -148,34 +246,119 @@ app.registerExtension({
|
||||
// You can modify widgets/add handlers/etc here
|
||||
|
||||
if (node.type === 'Color') {
|
||||
let widget = node.widgets.filter(w => w.div)[0]
|
||||
try {
|
||||
let TCOLOR = node.widgets.filter(w => w.type == 'TCOLOR')[0]
|
||||
|
||||
let data = getLocalData('_mixlab_utils_color')
|
||||
|
||||
let id = node.id
|
||||
|
||||
widget.div.querySelector('.Color').value = data[id] || '#000000'
|
||||
setTimeout(() => node.pickr.setColor(TCOLOR.value || '#000000'), 1000)
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.TextToNumber',
|
||||
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
if (nodeType.comfyClass == 'TextToNumber') {
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
const random_number=this.widgets.filter(w=>w.name==='random_number')[0]
|
||||
if(random_number.value==='enable'){
|
||||
const n=this.widgets.filter(w=>w.name==='number')[0]
|
||||
n.value=message.num[0]
|
||||
const random_number = this.widgets.filter(
|
||||
w => w.name === 'random_number'
|
||||
)[0]
|
||||
if (random_number.value === 'enable') {
|
||||
const n = this.widgets.filter(w => w.name === 'number')[0]
|
||||
n.value = message.num[0]
|
||||
}
|
||||
|
||||
console.log('TextToNumber', random_number.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const min_max = node => {
|
||||
if(node.widgets){
|
||||
const min_value = node.widgets.filter(w => w.name === 'min_value')[0]
|
||||
const max_value = node.widgets.filter(w => w.name === 'max_value')[0]
|
||||
|
||||
const number = node.widgets.filter(w => w.name === 'number')[0]
|
||||
if (number) {
|
||||
number.options.min = min_value.value
|
||||
number.options.max = max_value.value
|
||||
|
||||
number.value = Math.min(number.options.max, number.value)
|
||||
number.value = Math.max(number.options.min, number.value)
|
||||
}
|
||||
|
||||
if (min_value)
|
||||
min_value.callback = e => {
|
||||
number.options.min = e
|
||||
number.value = e
|
||||
}
|
||||
if (max_value)
|
||||
max_value.callback = e => {
|
||||
number.options.max = e
|
||||
number.value = e
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.FloatSlider',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
|
||||
if (nodeType.comfyClass == 'FloatSlider') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated;
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
min_max(this)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'FloatSlider') {
|
||||
min_max(node)
|
||||
}
|
||||
}
|
||||
})
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.IntNumber',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
|
||||
if (nodeType.comfyClass == 'IntNumber') {
|
||||
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
min_max(this)
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'IntNumber') {
|
||||
min_max(node)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
app.registerExtension({
|
||||
name: 'Mixlab.utils.TESTNODE_',
|
||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||
|
||||
if (nodeType.comfyClass == 'TESTNODE_') {
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted;
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments);
|
||||
console.log('##',message)
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
})
|
||||
|
||||
@@ -60,30 +60,7 @@ app.registerExtension({
|
||||
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()
|
||||
},
|
||||
draw (ctx, node, width, y) {},
|
||||
computeSize (...args) {
|
||||
return [128, 24] // a method to compute the current size of the widget
|
||||
},
|
||||
@@ -124,11 +101,19 @@ app.registerExtension({
|
||||
nodeType.prototype.onNodeCreated = function () {
|
||||
orig_nodeCreated?.apply(this, arguments)
|
||||
|
||||
console.log('watch widtget', this.widgets)
|
||||
// 虚拟的widget,用于更新节点,让其每次都运行
|
||||
const widget = {
|
||||
type: 'div',
|
||||
name: 'seed',
|
||||
draw (ctx, node, widget_width, y, widget_height) {}
|
||||
}
|
||||
|
||||
this.addCustomWidget(widget)
|
||||
|
||||
const watcher = this.widgets.filter(w => w.name == 'watcher')[0]
|
||||
|
||||
watcher.callback = () => {
|
||||
console.log('watcher', watcher.value)
|
||||
if (watcher.value === 'enable') {
|
||||
if (window._mixlab_watcher_t)
|
||||
clearInterval(window._mixlab_watcher_t)
|
||||
@@ -140,7 +125,7 @@ app.registerExtension({
|
||||
window._mixlab_file_path_watcher = json.event_type
|
||||
// widget.card.innerText = window._mixlab_file_path_watcher || ''
|
||||
//运行
|
||||
document.querySelector('#queue-button').click()
|
||||
// document.querySelector('#queue-button').click()
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
@@ -162,15 +147,59 @@ app.registerExtension({
|
||||
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 = true
|
||||
}
|
||||
|
||||
const onExecuted = nodeType.prototype.onExecuted
|
||||
nodeType.prototype.onExecuted = function (message) {
|
||||
onExecuted?.apply(this, arguments)
|
||||
console.log(message)
|
||||
try {
|
||||
let seed = this.widgets.filter(w => w.name === 'seed')[0]
|
||||
if (seed) {
|
||||
if (!seed.value) seed.value = 0
|
||||
seed.value += 1
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadedGraphNode (node, app) {
|
||||
if (node.type === 'LoadImagesFromPath') {
|
||||
const watcher = node.widgets.filter(w => w.name == 'watcher')[0]
|
||||
if (watcher) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
let seed = node.widgets.filter(w => w.name === 'seed')[0]
|
||||
if (seed) {
|
||||
if (!seed.value) seed.value = 0
|
||||
seed.value += 1
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Vendored
+2
File diff suppressed because one or more lines are too long
+8
File diff suppressed because one or more lines are too long
+5
File diff suppressed because one or more lines are too long
Vendored
+5
File diff suppressed because one or more lines are too long
Vendored
+1
@@ -0,0 +1 @@
|
||||
/*! PhotoSwipe main CSS by Dmytro Semenov | photoswipe.com */.pswp{--pswp-bg:#000;--pswp-placeholder-bg:#222;--pswp-root-z-index:100000;--pswp-preloader-color:rgba(79, 79, 79, 0.4);--pswp-preloader-color-secondary:rgba(255, 255, 255, 0.9);--pswp-icon-color:#fff;--pswp-icon-color-secondary:#4f4f4f;--pswp-icon-stroke-color:#4f4f4f;--pswp-icon-stroke-width:2px;--pswp-error-text-color:var(--pswp-icon-color)}.pswp{position:fixed;top:0;left:0;width:100%;height:100%;z-index:var(--pswp-root-z-index);display:none;touch-action:none;outline:0;opacity:.003;contain:layout style size;-webkit-tap-highlight-color:transparent}.pswp:focus{outline:0}.pswp *{box-sizing:border-box}.pswp img{max-width:none}.pswp--open{display:block}.pswp,.pswp__bg{transform:translateZ(0);will-change:opacity}.pswp__bg{opacity:.005;background:var(--pswp-bg)}.pswp,.pswp__scroll-wrap{overflow:hidden}.pswp__bg,.pswp__container,.pswp__content,.pswp__img,.pswp__item,.pswp__scroll-wrap,.pswp__zoom-wrap{position:absolute;top:0;left:0;width:100%;height:100%}.pswp__img,.pswp__zoom-wrap{width:auto;height:auto}.pswp--click-to-zoom.pswp--zoom-allowed .pswp__img{cursor:-webkit-zoom-in;cursor:-moz-zoom-in;cursor:zoom-in}.pswp--click-to-zoom.pswp--zoomed-in .pswp__img{cursor:move;cursor:-webkit-grab;cursor:-moz-grab;cursor:grab}.pswp--click-to-zoom.pswp--zoomed-in .pswp__img:active{cursor:-webkit-grabbing;cursor:-moz-grabbing;cursor:grabbing}.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img,.pswp--no-mouse-drag.pswp--zoomed-in .pswp__img:active,.pswp__img{cursor:-webkit-zoom-out;cursor:-moz-zoom-out;cursor:zoom-out}.pswp__button,.pswp__container,.pswp__counter,.pswp__img{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.pswp__item{z-index:1;overflow:hidden}.pswp__hidden{display:none!important}.pswp__content{pointer-events:none}.pswp__content>*{pointer-events:auto}.pswp__error-msg-container{display:grid}.pswp__error-msg{margin:auto;font-size:1em;line-height:1;color:var(--pswp-error-text-color)}.pswp .pswp__hide-on-close{opacity:.005;will-change:opacity;transition:opacity var(--pswp-transition-duration) cubic-bezier(.4,0,.22,1);z-index:10;pointer-events:none}.pswp--ui-visible .pswp__hide-on-close{opacity:1;pointer-events:auto}.pswp__button{position:relative;display:block;width:50px;height:60px;padding:0;margin:0;overflow:hidden;cursor:pointer;background:0 0;border:0;box-shadow:none;opacity:.85;-webkit-appearance:none;-webkit-touch-callout:none}.pswp__button:active,.pswp__button:focus,.pswp__button:hover{transition:none;padding:0;background:0 0;border:0;box-shadow:none;opacity:1}.pswp__button:disabled{opacity:.3;cursor:auto}.pswp__icn{fill:var(--pswp-icon-color);color:var(--pswp-icon-color-secondary)}.pswp__icn{position:absolute;top:14px;left:9px;width:32px;height:32px;overflow:hidden;pointer-events:none}.pswp__icn-shadow{stroke:var(--pswp-icon-stroke-color);stroke-width:var(--pswp-icon-stroke-width);fill:none}.pswp__icn:focus{outline:0}.pswp__img--with-bg,div.pswp__img--placeholder{background:var(--pswp-placeholder-bg)}.pswp__top-bar{position:absolute;left:0;top:0;width:100%;height:60px;display:flex;flex-direction:row;justify-content:flex-end;z-index:10;pointer-events:none!important}.pswp__top-bar>*{pointer-events:auto;will-change:opacity}.pswp__button--close{margin-right:6px}.pswp__button--arrow{position:absolute;top:0;width:75px;height:100px;top:50%;margin-top:-50px}.pswp__button--arrow:disabled{display:none;cursor:default}.pswp__button--arrow .pswp__icn{top:50%;margin-top:-30px;width:60px;height:60px;background:0 0;border-radius:0}.pswp--one-slide .pswp__button--arrow{display:none}.pswp--touch .pswp__button--arrow{visibility:hidden}.pswp--has_mouse .pswp__button--arrow{visibility:visible}.pswp__button--arrow--prev{right:auto;left:0}.pswp__button--arrow--next{right:0}.pswp__button--arrow--next .pswp__icn{left:auto;right:14px;transform:scale(-1,1)}.pswp__button--zoom{display:none}.pswp--zoom-allowed .pswp__button--zoom{display:block}.pswp--zoomed-in .pswp__zoom-icn-bar-v{display:none}.pswp__preloader{position:relative;overflow:hidden;width:50px;height:60px;margin-right:auto}.pswp__preloader .pswp__icn{opacity:0;transition:opacity .2s linear;animation:pswp-clockwise .6s linear infinite}.pswp__preloader--active .pswp__icn{opacity:.85}@keyframes pswp-clockwise{0%{transform:rotate(0)}100%{transform:rotate(360deg)}}.pswp__counter{height:30px;margin-top:15px;margin-inline-start:20px;font-size:14px;line-height:30px;color:var(--pswp-icon-color);text-shadow:1px 1px 3px var(--pswp-icon-color-secondary);opacity:.85}.pswp--one-slide .pswp__counter{display:none}
|
||||
Vendored
+3
File diff suppressed because one or more lines are too long
+2423
-705
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,736 @@
|
||||
{
|
||||
"last_node_id": 29,
|
||||
"last_link_id": 32,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 7,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
108,
|
||||
316
|
||||
],
|
||||
"size": {
|
||||
"0": 425.27801513671875,
|
||||
"1": 180.6060791015625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 16
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
6
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"text, watermark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "ControlNetApply",
|
||||
"pos": [
|
||||
485,
|
||||
792
|
||||
],
|
||||
"size": {
|
||||
"0": 317.4000244140625,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 22,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "control_net",
|
||||
"type": "CONTROL_NET",
|
||||
"link": 18,
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 26
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
21
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ControlNetApply"
|
||||
},
|
||||
"widgets_values": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
117,
|
||||
95
|
||||
],
|
||||
"size": {
|
||||
"0": 422.84503173828125,
|
||||
"1": 164.31304931640625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 15
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
22
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"a future city,buiding,future,magic,under water"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "LoraLoader",
|
||||
"pos": [
|
||||
116,
|
||||
-113
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 126
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 13
|
||||
},
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 14
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
12,
|
||||
23
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
15,
|
||||
16
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LoraLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"lcm-lora-sdv1-5.safetensors",
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
-380,
|
||||
185
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
13
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
14
|
||||
],
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
8
|
||||
],
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"deliberate_v2.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 19,
|
||||
"type": "DiffControlNetLoader",
|
||||
"pos": [
|
||||
40,
|
||||
792
|
||||
],
|
||||
"size": {
|
||||
"0": 367.8165283203125,
|
||||
"1": 58.083831787109375
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 23,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONTROL_NET",
|
||||
"type": "CONTROL_NET",
|
||||
"links": [
|
||||
18
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "DiffControlNetLoader"
|
||||
},
|
||||
"widgets_values": [
|
||||
"control_v11f1p_sd15_depth.pth"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
65,
|
||||
573
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 25,
|
||||
"type": "LeReS-DepthMapPreprocessor",
|
||||
"pos": [
|
||||
47,
|
||||
904
|
||||
],
|
||||
"size": {
|
||||
"0": 369.6000061035156,
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 31
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
26,
|
||||
27
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "LeReS-DepthMapPreprocessor"
|
||||
},
|
||||
"widgets_values": [
|
||||
0.1,
|
||||
0,
|
||||
"disable",
|
||||
512
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "KSampler",
|
||||
"pos": [
|
||||
1100,
|
||||
-97
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 12,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 21
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 6
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 2
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
7
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
170633013599955,
|
||||
"fixed",
|
||||
4,
|
||||
1.6,
|
||||
"lcm",
|
||||
"simple",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
1100,
|
||||
-241
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 7
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 8
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
28
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
492,
|
||||
955
|
||||
],
|
||||
"size": {
|
||||
"0": 526.9624633789062,
|
||||
"1": 367.3833923339844
|
||||
},
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 27
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": "FloatingVideo",
|
||||
"pos": [
|
||||
1565,
|
||||
-181
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 28
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatingVideo"
|
||||
},
|
||||
"widgets_values": [
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
-241,
|
||||
953
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 32
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 29,
|
||||
"type": "ScreenShare",
|
||||
"pos": [
|
||||
-639,
|
||||
366
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
644
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
31,
|
||||
32
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "PROMPT",
|
||||
"type": "STRING",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "FLOAT",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "INT",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ScreenShare"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
500,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
2,
|
||||
5,
|
||||
0,
|
||||
3,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
6,
|
||||
7,
|
||||
0,
|
||||
3,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
7,
|
||||
3,
|
||||
0,
|
||||
8,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
8,
|
||||
4,
|
||||
2,
|
||||
8,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
12,
|
||||
15,
|
||||
0,
|
||||
3,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
13,
|
||||
4,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
14,
|
||||
4,
|
||||
1,
|
||||
15,
|
||||
1,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
15,
|
||||
15,
|
||||
1,
|
||||
6,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
16,
|
||||
15,
|
||||
1,
|
||||
7,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
18,
|
||||
19,
|
||||
0,
|
||||
18,
|
||||
1,
|
||||
"CONTROL_NET"
|
||||
],
|
||||
[
|
||||
21,
|
||||
18,
|
||||
0,
|
||||
3,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
22,
|
||||
6,
|
||||
0,
|
||||
18,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
23,
|
||||
15,
|
||||
0,
|
||||
19,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
26,
|
||||
25,
|
||||
0,
|
||||
18,
|
||||
2,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
27,
|
||||
25,
|
||||
0,
|
||||
20,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
28,
|
||||
8,
|
||||
0,
|
||||
26,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
31,
|
||||
29,
|
||||
0,
|
||||
25,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
32,
|
||||
29,
|
||||
0,
|
||||
28,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 23,
|
||||
"last_link_id": 51,
|
||||
"last_node_id": 22,
|
||||
"last_link_id": 48,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 7,
|
||||
@@ -105,7 +105,7 @@
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"link": 51,
|
||||
"link": 48,
|
||||
"widget": {
|
||||
"name": "text"
|
||||
}
|
||||
@@ -295,7 +295,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
482859286431021,
|
||||
644769503212755,
|
||||
"randomize",
|
||||
4,
|
||||
1.6,
|
||||
@@ -316,19 +316,47 @@
|
||||
"1": 246
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 50
|
||||
"link": 46
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "FloatingVideo",
|
||||
"pos": [
|
||||
2041,
|
||||
277
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 41
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatingVideo"
|
||||
},
|
||||
"widgets_values": [
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "LoraLoader",
|
||||
@@ -445,13 +473,13 @@
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 49
|
||||
"link": 47
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
@@ -473,43 +501,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 20,
|
||||
"type": "FloatingVideo",
|
||||
"id": 22,
|
||||
"type": "ScreenShare",
|
||||
"pos": [
|
||||
1928,
|
||||
295
|
||||
-111,
|
||||
427
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 41
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatingVideo"
|
||||
},
|
||||
"widgets_values": [
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"type": "ScreenShare",
|
||||
"pos": [
|
||||
-65,
|
||||
446
|
||||
],
|
||||
"size": [
|
||||
312.78457519531213,
|
||||
606.2132135620109
|
||||
644
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
@@ -519,8 +519,8 @@
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
49,
|
||||
50
|
||||
46,
|
||||
47
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
@@ -529,7 +529,7 @@
|
||||
"name": "PROMPT",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
51
|
||||
48
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
@@ -552,7 +552,7 @@
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
2018,
|
||||
500,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
@@ -810,24 +810,24 @@
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
49,
|
||||
23,
|
||||
0,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
50,
|
||||
23,
|
||||
46,
|
||||
22,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
51,
|
||||
23,
|
||||
47,
|
||||
22,
|
||||
0,
|
||||
18,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
48,
|
||||
22,
|
||||
1,
|
||||
8,
|
||||
1,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,721 @@
|
||||
{
|
||||
"last_node_id": 23,
|
||||
"last_link_id": 25,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 9,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
2070,
|
||||
830
|
||||
],
|
||||
"size": {
|
||||
"0": 425.27801513671875,
|
||||
"1": 180.6060791015625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 7
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
4
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"text, watermark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
2070,
|
||||
1060
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
5
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
1640,
|
||||
920
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
2
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
6,
|
||||
7
|
||||
],
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
9
|
||||
],
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"deliberate_v2.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
2080,
|
||||
630
|
||||
],
|
||||
"size": {
|
||||
"0": 422.84503173828125,
|
||||
"1": 164.31304931640625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 6
|
||||
},
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"link": 16,
|
||||
"widget": {
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
3
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "ShowTextForGPT",
|
||||
"pos": [
|
||||
3288,
|
||||
-323
|
||||
],
|
||||
"size": {
|
||||
"0": 449.1168212890625,
|
||||
"1": 76
|
||||
},
|
||||
"flags": {},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"link": 20,
|
||||
"widget": {
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": null,
|
||||
"shape": 6
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "ShowTextForGPT"
|
||||
},
|
||||
"widgets_values": [
|
||||
"a girl face,super,(Pop Art:1.26),(Black and White:1.26)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "KSampler",
|
||||
"pos": [
|
||||
2520,
|
||||
630
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 2
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 3
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 4
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 5
|
||||
},
|
||||
{
|
||||
"name": "seed",
|
||||
"type": "INT",
|
||||
"link": 21,
|
||||
"widget": {
|
||||
"name": "seed"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
8
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
1063141893699118,
|
||||
"fixed",
|
||||
20,
|
||||
8,
|
||||
"euler",
|
||||
"normal",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 18,
|
||||
"type": "IntNumber",
|
||||
"pos": [
|
||||
2852,
|
||||
275
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "INT",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
21
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"title": "Seed",
|
||||
"properties": {
|
||||
"Node name for S&R": "IntNumber"
|
||||
},
|
||||
"widgets_values": [
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 22,
|
||||
"type": "PromptSlide",
|
||||
"pos": [
|
||||
2364,
|
||||
-294
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
23
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PromptSlide"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Pop Art",
|
||||
1.26,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"type": "PromptSlide",
|
||||
"pos": [
|
||||
2360,
|
||||
-544
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "prompt",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
22
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PromptSlide"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Black and White",
|
||||
1.26,
|
||||
null
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 13,
|
||||
"type": "RandomPrompt",
|
||||
"pos": [
|
||||
2840,
|
||||
-330
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 224
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "mutable_prompt",
|
||||
"type": "STRING",
|
||||
"link": 22,
|
||||
"widget": {
|
||||
"name": "mutable_prompt"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "immutable_prompt",
|
||||
"type": "STRING",
|
||||
"link": 23,
|
||||
"widget": {
|
||||
"name": "immutable_prompt"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
19
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "RandomPrompt"
|
||||
},
|
||||
"widgets_values": [
|
||||
1,
|
||||
"",
|
||||
" ``",
|
||||
"disable",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "RandomPrompt",
|
||||
"pos": [
|
||||
2840,
|
||||
-30
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 224
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "mutable_prompt",
|
||||
"type": "STRING",
|
||||
"link": 19,
|
||||
"widget": {
|
||||
"name": "mutable_prompt"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
16,
|
||||
20
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "RandomPrompt"
|
||||
},
|
||||
"widgets_values": [
|
||||
1,
|
||||
"",
|
||||
" a girl face,super,``",
|
||||
"disable",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 11,
|
||||
"type": "PreviewImage",
|
||||
"pos": [
|
||||
3449,
|
||||
-102
|
||||
],
|
||||
"size": {
|
||||
"0": 435.8727111816406,
|
||||
"1": 511.8609619140625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 24
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PreviewImage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 10,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
2870,
|
||||
630
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 8
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 9
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
24,
|
||||
25
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 23,
|
||||
"type": "AppInfo",
|
||||
"pos": [
|
||||
3363.0014990624995,
|
||||
551.2261418945309
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 344
|
||||
},
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "LOGO",
|
||||
"type": "IMAGE",
|
||||
"link": 25
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "AppInfo"
|
||||
},
|
||||
"widgets_values": [
|
||||
"Prompt-weight-test",
|
||||
"21\n22\n18",
|
||||
"11\n16",
|
||||
"",
|
||||
1,
|
||||
"",
|
||||
"https://",
|
||||
"",
|
||||
"enable",
|
||||
1
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
2,
|
||||
6,
|
||||
0,
|
||||
5,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
3,
|
||||
8,
|
||||
0,
|
||||
5,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
4,
|
||||
9,
|
||||
0,
|
||||
5,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
5,
|
||||
7,
|
||||
0,
|
||||
5,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
6,
|
||||
6,
|
||||
1,
|
||||
8,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
7,
|
||||
6,
|
||||
1,
|
||||
9,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
8,
|
||||
5,
|
||||
0,
|
||||
10,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
9,
|
||||
6,
|
||||
2,
|
||||
10,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
16,
|
||||
15,
|
||||
0,
|
||||
8,
|
||||
1,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
19,
|
||||
13,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
20,
|
||||
15,
|
||||
0,
|
||||
16,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
21,
|
||||
18,
|
||||
0,
|
||||
5,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
22,
|
||||
21,
|
||||
0,
|
||||
13,
|
||||
0,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
23,
|
||||
22,
|
||||
0,
|
||||
13,
|
||||
1,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
24,
|
||||
10,
|
||||
0,
|
||||
11,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
25,
|
||||
10,
|
||||
0,
|
||||
23,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
+166
-164
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 25,
|
||||
"last_link_id": 32,
|
||||
"last_node_id": 26,
|
||||
"last_link_id": 34,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 5,
|
||||
@@ -41,48 +41,6 @@
|
||||
"text, watermark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
1867,
|
||||
-2378
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 7
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 8
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
9
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 15,
|
||||
"type": "EnhanceImage",
|
||||
@@ -101,7 +59,7 @@
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 25
|
||||
"link": 34
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
@@ -122,57 +80,6 @@
|
||||
1.1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
998,
|
||||
-2674
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
106
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 30,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 32,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
26
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 24,
|
||||
"type": "LimitNumber",
|
||||
@@ -262,7 +169,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
@@ -283,7 +190,10 @@
|
||||
"Node name for S&R": "IntNumber"
|
||||
},
|
||||
"widgets_values": [
|
||||
512
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -295,7 +205,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
@@ -316,7 +226,10 @@
|
||||
"Node name for S&R": "IntNumber"
|
||||
},
|
||||
"widgets_values": [
|
||||
512
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -428,7 +341,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
730250870715434,
|
||||
52336089190516,
|
||||
"randomize",
|
||||
15,
|
||||
6.9,
|
||||
@@ -437,39 +350,6 @@
|
||||
0.59
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "FloatSlider",
|
||||
"pos": [
|
||||
1007,
|
||||
-2819
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 58
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "FLOAT",
|
||||
"type": "FLOAT",
|
||||
"links": [
|
||||
16
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"title": "denoise",
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatSlider"
|
||||
},
|
||||
"widgets_values": [
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 16,
|
||||
"type": "PreviewImage",
|
||||
@@ -535,24 +415,31 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"type": "AppInfo",
|
||||
"id": 6,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
2134,
|
||||
-2528
|
||||
1867,
|
||||
-2378
|
||||
],
|
||||
"size": {
|
||||
"0": 408.4201965332031,
|
||||
"1": 406.9195861816406
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 9
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 7
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 8
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
@@ -560,12 +447,37 @@
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
25
|
||||
33,
|
||||
34
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 26,
|
||||
"type": "AppInfo",
|
||||
"pos": [
|
||||
2134,
|
||||
-2264
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 344.00006103515625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "LOGO",
|
||||
"type": "IMAGE",
|
||||
"link": 33
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "AppInfo"
|
||||
},
|
||||
@@ -576,7 +488,97 @@
|
||||
"演示基本的文生图流程",
|
||||
1,
|
||||
"#comfyui-mixlab-nodes# ",
|
||||
null
|
||||
"https://",
|
||||
"",
|
||||
"enable",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 14,
|
||||
"type": "FloatSlider",
|
||||
"pos": [
|
||||
1007,
|
||||
-2860
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 130
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "FLOAT",
|
||||
"type": "FLOAT",
|
||||
"links": [
|
||||
16
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"title": "denoise",
|
||||
"properties": {
|
||||
"Node name for S&R": "FloatSlider"
|
||||
},
|
||||
"widgets_values": [
|
||||
1,
|
||||
0,
|
||||
1,
|
||||
0.001
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 21,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
1004,
|
||||
-2633
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 30,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 32,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
26
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
}
|
||||
],
|
||||
@@ -637,14 +639,6 @@
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
9,
|
||||
6,
|
||||
0,
|
||||
7,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
16,
|
||||
14,
|
||||
@@ -661,14 +655,6 @@
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
25,
|
||||
7,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
26,
|
||||
21,
|
||||
@@ -708,6 +694,22 @@
|
||||
21,
|
||||
1,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
33,
|
||||
6,
|
||||
0,
|
||||
26,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
34,
|
||||
6,
|
||||
0,
|
||||
15,
|
||||
0,
|
||||
"IMAGE"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
@@ -0,0 +1,558 @@
|
||||
{
|
||||
"last_node_id": 69,
|
||||
"last_link_id": 72,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 37,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
6705,
|
||||
-216
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 200
|
||||
},
|
||||
"flags": {},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 33
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
34
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"beautiful scenery nature glass bottle landscape, , purple galaxy bottle,"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
6693,
|
||||
61
|
||||
],
|
||||
"size": {
|
||||
"0": 425.27801513671875,
|
||||
"1": 180.6060791015625
|
||||
},
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 6
|
||||
},
|
||||
{
|
||||
"name": "text",
|
||||
"type": "STRING",
|
||||
"link": 25,
|
||||
"widget": {
|
||||
"name": "text"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
3
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CLIPTextEncode"
|
||||
},
|
||||
"widgets_values": [
|
||||
"text, watermark"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"type": "EmptyLatentImage",
|
||||
"pos": [
|
||||
6689,
|
||||
306
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 106
|
||||
},
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
4
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmptyLatentImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
512,
|
||||
512,
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 27,
|
||||
"type": "EmbeddingPrompt",
|
||||
"pos": [
|
||||
6104,
|
||||
23
|
||||
],
|
||||
"size": {
|
||||
"0": 399.6408996582031,
|
||||
"1": 82
|
||||
},
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "STRING",
|
||||
"type": "STRING",
|
||||
"links": [
|
||||
25
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "EmbeddingPrompt"
|
||||
},
|
||||
"widgets_values": [
|
||||
"negative-embed-verybadimagenegative_v1.3",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 67,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
7551,
|
||||
-184
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 70
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 69
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
71
|
||||
],
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"type": "KSampler",
|
||||
"pos": [
|
||||
7187,
|
||||
-145
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 1
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 34
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 3
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 4
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
70
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
408562451564429,
|
||||
"fixed",
|
||||
20,
|
||||
8,
|
||||
"euler",
|
||||
"normal",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 61,
|
||||
"type": "PromptImage",
|
||||
"pos": [
|
||||
7853,
|
||||
-238
|
||||
],
|
||||
"size": [
|
||||
465.6378949342379,
|
||||
760.4568424013569
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 71
|
||||
},
|
||||
{
|
||||
"name": "prompts",
|
||||
"type": "STRING",
|
||||
"link": 72,
|
||||
"widget": {
|
||||
"name": "prompts"
|
||||
}
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "PromptImage"
|
||||
},
|
||||
"widgets_values": [
|
||||
"",
|
||||
"disable",
|
||||
{
|
||||
"_images": [
|
||||
[
|
||||
{
|
||||
"filename": "mixlab_PromptImage_0_00027_.png",
|
||||
"subfolder": "",
|
||||
"type": "output"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"filename": "mixlab_PromptImage_1_00028_.png",
|
||||
"subfolder": "",
|
||||
"type": "output"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"filename": "mixlab_PromptImage_2_00029_.png",
|
||||
"subfolder": "",
|
||||
"type": "output"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"filename": "mixlab_PromptImage_3_00030_.png",
|
||||
"subfolder": "",
|
||||
"type": "output"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"filename": "mixlab_PromptImage_4_00031_.png",
|
||||
"subfolder": "",
|
||||
"type": "output"
|
||||
}
|
||||
],
|
||||
[
|
||||
{
|
||||
"filename": "mixlab_PromptImage_5_00032_.png",
|
||||
"subfolder": "",
|
||||
"type": "output"
|
||||
}
|
||||
]
|
||||
],
|
||||
"prompts": [
|
||||
"512-inpainting-ema.safetensors",
|
||||
"SSD-1B.safetensors",
|
||||
"awportrait_v12.safetensors",
|
||||
"cardosAnime_v20.safetensors",
|
||||
"deliberate_v2.safetensors",
|
||||
"gameIconInstitute_v40.safetensors"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"type": "CheckpointLoaderSimple",
|
||||
"pos": [
|
||||
6105,
|
||||
-139
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 98
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "ckpt_name",
|
||||
"type": [
|
||||
"512-inpainting-ema.safetensors",
|
||||
"SSD-1B.safetensors",
|
||||
"awportrait_v12.safetensors",
|
||||
"cardosAnime_v20.safetensors",
|
||||
"deliberate_v2.safetensors",
|
||||
"gameIconInstitute_v40.safetensors",
|
||||
"illuminatiDiffusionV1_v11-unclip-h-fp16.safetensors",
|
||||
"sd_xl_turbo_1.0_fp16.safetensors",
|
||||
"svd.safetensors"
|
||||
],
|
||||
"link": 64,
|
||||
"widget": {
|
||||
"name": "ckpt_name"
|
||||
}
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
1
|
||||
],
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
6,
|
||||
33
|
||||
],
|
||||
"slot_index": 1
|
||||
},
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
69
|
||||
],
|
||||
"slot_index": 2
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CheckpointLoaderSimple"
|
||||
},
|
||||
"widgets_values": [
|
||||
"deliberate_v2.safetensors"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 56,
|
||||
"type": "CkptNames_",
|
||||
"pos": [
|
||||
7860,
|
||||
-494
|
||||
],
|
||||
"size": {
|
||||
"0": 400,
|
||||
"1": 200
|
||||
},
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"outputs": [
|
||||
{
|
||||
"name": "ckpt_names",
|
||||
"type": "*",
|
||||
"links": [
|
||||
64,
|
||||
72
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "CkptNames_"
|
||||
},
|
||||
"widgets_values": [
|
||||
"512-inpainting-ema.safetensors\nSSD-1B.safetensors\nawportrait_v12.safetensors\ncardosAnime_v20.safetensors\ndeliberate_v2.safetensors\ngameIconInstitute_v40.safetensors"
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
1,
|
||||
2,
|
||||
0,
|
||||
1,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
3,
|
||||
5,
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
4,
|
||||
3,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
6,
|
||||
2,
|
||||
1,
|
||||
5,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
25,
|
||||
27,
|
||||
0,
|
||||
5,
|
||||
1,
|
||||
"STRING"
|
||||
],
|
||||
[
|
||||
33,
|
||||
2,
|
||||
1,
|
||||
37,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
34,
|
||||
37,
|
||||
0,
|
||||
1,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
64,
|
||||
56,
|
||||
0,
|
||||
2,
|
||||
0,
|
||||
[
|
||||
"512-inpainting-ema.safetensors",
|
||||
"SSD-1B.safetensors",
|
||||
"awportrait_v12.safetensors",
|
||||
"cardosAnime_v20.safetensors",
|
||||
"deliberate_v2.safetensors",
|
||||
"gameIconInstitute_v40.safetensors",
|
||||
"illuminatiDiffusionV1_v11-unclip-h-fp16.safetensors",
|
||||
"sd_xl_turbo_1.0_fp16.safetensors",
|
||||
"svd.safetensors"
|
||||
]
|
||||
],
|
||||
[
|
||||
69,
|
||||
2,
|
||||
2,
|
||||
67,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
70,
|
||||
1,
|
||||
0,
|
||||
67,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
71,
|
||||
67,
|
||||
0,
|
||||
61,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
72,
|
||||
56,
|
||||
0,
|
||||
61,
|
||||
1,
|
||||
"STRING"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
"config": {},
|
||||
"extra": {},
|
||||
"version": 0.4
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"last_node_id": 133,
|
||||
"last_link_id": 289,
|
||||
"last_node_id": 134,
|
||||
"last_link_id": 296,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 42,
|
||||
@@ -178,7 +178,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
487152733962296,
|
||||
981322491518329,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -284,7 +284,7 @@
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 91,
|
||||
"link": 291,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
@@ -292,7 +292,7 @@
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 92,
|
||||
"link": 292,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
@@ -554,7 +554,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
1031291986804327,
|
||||
836429510062488,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -824,7 +824,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
715240655341851,
|
||||
24900528323969,
|
||||
"randomize",
|
||||
12,
|
||||
8,
|
||||
@@ -875,67 +875,6 @@
|
||||
"Node name for S&R": "VAEDecode"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"type": "NewLayer",
|
||||
"pos": [
|
||||
1012.7987748686522,
|
||||
1330
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 32,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 78
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 79
|
||||
},
|
||||
{
|
||||
"name": "layers",
|
||||
"type": "LAYER",
|
||||
"link": 227,
|
||||
"slot_index": 2
|
||||
},
|
||||
{
|
||||
"name": "canvas",
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "layers",
|
||||
"type": "LAYER",
|
||||
"links": [
|
||||
228
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "NewLayer"
|
||||
},
|
||||
"widgets_values": [
|
||||
179,
|
||||
134,
|
||||
156,
|
||||
146,
|
||||
4,
|
||||
"width",
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 65,
|
||||
"type": "NewLayer",
|
||||
@@ -1221,42 +1160,6 @@
|
||||
""
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": "Reroute",
|
||||
"pos": [
|
||||
616,
|
||||
1515
|
||||
],
|
||||
"size": [
|
||||
75,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "*",
|
||||
"link": 48
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
90
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"showOutputText": false,
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 96,
|
||||
"type": "Reroute",
|
||||
@@ -1294,55 +1197,6 @@
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 49,
|
||||
"type": "GetImageSize",
|
||||
"pos": [
|
||||
999,
|
||||
1189
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 24,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 90
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
91,
|
||||
238,
|
||||
248
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
92,
|
||||
239,
|
||||
249
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "GetImageSize"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 118,
|
||||
"type": "NewLayer",
|
||||
@@ -1376,7 +1230,7 @@
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 248,
|
||||
"link": 295,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
@@ -1384,7 +1238,7 @@
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 249,
|
||||
"link": 296,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
@@ -1572,7 +1426,7 @@
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"link": 238,
|
||||
"link": 293,
|
||||
"widget": {
|
||||
"name": "width"
|
||||
}
|
||||
@@ -1580,7 +1434,7 @@
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"link": 239,
|
||||
"link": 294,
|
||||
"widget": {
|
||||
"name": "height"
|
||||
}
|
||||
@@ -1955,7 +1809,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
397643785545040,
|
||||
371831550273153,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -2164,7 +2018,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
590319162197911,
|
||||
996820396025175,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -2310,7 +2164,7 @@
|
||||
"Node name for S&R": "KSampler"
|
||||
},
|
||||
"widgets_values": [
|
||||
457217754867837,
|
||||
763274893004397,
|
||||
"randomize",
|
||||
13,
|
||||
8,
|
||||
@@ -2780,7 +2634,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 234
|
||||
"1": 240
|
||||
},
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
@@ -2814,7 +2668,8 @@
|
||||
100,
|
||||
13,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -2986,10 +2841,10 @@
|
||||
100,
|
||||
706
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
234
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 234
|
||||
},
|
||||
"flags": {},
|
||||
"order": 15,
|
||||
"mode": 0,
|
||||
@@ -3034,7 +2889,8 @@
|
||||
100,
|
||||
13,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3044,10 +2900,10 @@
|
||||
100,
|
||||
1330
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
234
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 234
|
||||
},
|
||||
"flags": {},
|
||||
"order": 16,
|
||||
"mode": 0,
|
||||
@@ -3091,7 +2947,8 @@
|
||||
100,
|
||||
13,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3101,10 +2958,10 @@
|
||||
100,
|
||||
1922
|
||||
],
|
||||
"size": [
|
||||
315,
|
||||
234
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 234
|
||||
},
|
||||
"flags": {},
|
||||
"order": 17,
|
||||
"mode": 0,
|
||||
@@ -3147,7 +3004,8 @@
|
||||
100,
|
||||
13,
|
||||
null,
|
||||
true
|
||||
true,
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -3159,7 +3017,7 @@
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 70
|
||||
"1": 174
|
||||
},
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
@@ -3175,13 +3033,37 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "r",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "g",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"type": "INT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
},
|
||||
{
|
||||
"name": "a",
|
||||
"type": "FLOAT",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "Color"
|
||||
},
|
||||
"widgets_values": [
|
||||
null,
|
||||
"#000000",
|
||||
null
|
||||
]
|
||||
},
|
||||
@@ -3221,6 +3103,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -3263,6 +3151,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -3306,6 +3200,12 @@
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "MASK",
|
||||
"type": "MASK",
|
||||
"links": null,
|
||||
"shape": 3
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@@ -3334,6 +3234,152 @@
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 134,
|
||||
"type": "GetImageSize_",
|
||||
"pos": [
|
||||
734,
|
||||
1628
|
||||
],
|
||||
"size": {
|
||||
"0": 210,
|
||||
"1": 46
|
||||
},
|
||||
"flags": {},
|
||||
"order": 24,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 290
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "width",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
291,
|
||||
293,
|
||||
295
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 0
|
||||
},
|
||||
{
|
||||
"name": "height",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
292,
|
||||
294,
|
||||
296
|
||||
],
|
||||
"shape": 3,
|
||||
"slot_index": 1
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "GetImageSize_"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 28,
|
||||
"type": "Reroute",
|
||||
"pos": [
|
||||
597,
|
||||
1627
|
||||
],
|
||||
"size": [
|
||||
75,
|
||||
26
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "*",
|
||||
"link": 48
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
290
|
||||
],
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"showOutputText": false,
|
||||
"horizontal": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 46,
|
||||
"type": "NewLayer",
|
||||
"pos": [
|
||||
1056,
|
||||
1247
|
||||
],
|
||||
"size": {
|
||||
"0": 315,
|
||||
"1": 262
|
||||
},
|
||||
"flags": {},
|
||||
"order": 32,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "image",
|
||||
"type": "IMAGE",
|
||||
"link": 78
|
||||
},
|
||||
{
|
||||
"name": "mask",
|
||||
"type": "MASK",
|
||||
"link": 79
|
||||
},
|
||||
{
|
||||
"name": "layers",
|
||||
"type": "LAYER",
|
||||
"link": 227,
|
||||
"slot_index": 2
|
||||
},
|
||||
{
|
||||
"name": "canvas",
|
||||
"type": "IMAGE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "layers",
|
||||
"type": "LAYER",
|
||||
"links": [
|
||||
228
|
||||
],
|
||||
"shape": 6,
|
||||
"slot_index": 0
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"Node name for S&R": "NewLayer"
|
||||
},
|
||||
"widgets_values": [
|
||||
179,
|
||||
134,
|
||||
156,
|
||||
146,
|
||||
4,
|
||||
"width",
|
||||
""
|
||||
]
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
@@ -3433,30 +3479,6 @@
|
||||
1,
|
||||
"MASK"
|
||||
],
|
||||
[
|
||||
90,
|
||||
28,
|
||||
0,
|
||||
49,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
91,
|
||||
49,
|
||||
0,
|
||||
50,
|
||||
0,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
92,
|
||||
49,
|
||||
1,
|
||||
50,
|
||||
1,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
111,
|
||||
63,
|
||||
@@ -4001,22 +4023,6 @@
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
238,
|
||||
49,
|
||||
0,
|
||||
116,
|
||||
3,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
239,
|
||||
49,
|
||||
1,
|
||||
116,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
247,
|
||||
116,
|
||||
@@ -4025,22 +4031,6 @@
|
||||
2,
|
||||
"LAYER"
|
||||
],
|
||||
[
|
||||
248,
|
||||
49,
|
||||
0,
|
||||
118,
|
||||
3,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
249,
|
||||
49,
|
||||
1,
|
||||
118,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
251,
|
||||
119,
|
||||
@@ -4312,6 +4302,62 @@
|
||||
119,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
290,
|
||||
28,
|
||||
0,
|
||||
134,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
291,
|
||||
134,
|
||||
0,
|
||||
50,
|
||||
0,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
292,
|
||||
134,
|
||||
1,
|
||||
50,
|
||||
1,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
293,
|
||||
134,
|
||||
0,
|
||||
116,
|
||||
3,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
294,
|
||||
134,
|
||||
1,
|
||||
116,
|
||||
4,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
295,
|
||||
134,
|
||||
0,
|
||||
118,
|
||||
3,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
296,
|
||||
134,
|
||||
1,
|
||||
118,
|
||||
4,
|
||||
"INT"
|
||||
]
|
||||
],
|
||||
"groups": [],
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" ?><svg id="Layer_1" style="enable-background:new 0 0 50 50;" version="1.1" viewBox="0 0 50 50" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g id="Layer_1_1_"><path d="M18.293,31.707h6.414l24-24l-6.414-6.414l-24,24V31.707z M45.879,7.707l-3.586,3.586l-3.586-3.586l3.586-3.586 L45.879,7.707z M20.293,26.121l17-17l3.586,3.586l-17,17h-3.586V26.121z"/><polygon points="43.293,19.707 41.293,19.707 41.293,46.707 3.293,46.707 3.293,8.707 31.293,8.707 31.293,6.707 1.293,6.707 1.293,48.707 43.293,48.707 "/></g></svg>
|
||||
|
After Width: | Height: | Size: 589 B |
Reference in New Issue
Block a user