Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d55cbdfde5 | ||
|
|
00cef5f37f | ||
|
|
0be859f0ee | ||
|
|
14b9c3697b | ||
|
|
96b66a57bb | ||
|
|
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 | ||
|
|
2e228e8db5 | ||
|
|
5c9dd80370 | ||
|
|
727f5f2e48 | ||
|
|
1c238f7697 | ||
|
|
119d7cce15 | ||
|
|
4afc8f6083 | ||
|
|
a9ec3af066 | ||
|
|
9edae81fee | ||
|
|
b941b12f12 | ||
|
|
3069de188a | ||
|
|
a968f08abd | ||
|
|
4153d3e5ff | ||
|
|
9d9c1a6c84 | ||
|
|
16ef10a4d9 | ||
|
|
b3766e440a | ||
|
|
6359c3f70f | ||
|
|
efe73fb965 | ||
|
|
c45a962fcc | ||
|
|
f98a03e2e9 | ||
|
|
5b6257814d | ||
|
|
69a445d4ed | ||
|
|
e82c786b8a | ||
|
|
eec2225c89 | ||
|
|
f7355e0b71 | ||
|
|
6c6a99cfe4 | ||
|
|
b4634e2e0d | ||
|
|
3f4cba0612 | ||
|
|
38db99cc75 | ||
|
|
4d5906394b | ||
|
|
2fc212b156 | ||
|
|
53fbb5b027 | ||
|
|
4f24721450 | ||
|
|
83043727b5 | ||
|
|
2d336afb85 | ||
|
|
4d309435c8 | ||
|
|
099ce9cdfd | ||
|
|
8914e60cb8 | ||
|
|
dbd30a40e9 | ||
|
|
c9a598fd59 | ||
|
|
e331e588cf | ||
|
|
2011557771 | ||
|
|
f0ba45d14e | ||
|
|
8352a521b7 | ||
|
|
aa3d4d79f8 | ||
|
|
4f650d760c | ||
|
|
ea4b792627 | ||
|
|
883605239a | ||
|
|
6a56be3a9b | ||
|
|
5df9fbf50d | ||
|
|
ff961155c9 | ||
|
|
14838d06a8 |
@@ -1,4 +1,6 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
https/
|
https/
|
||||||
nodes/config.json
|
nodes/config.json
|
||||||
workflow/my_workflow.json
|
workflow/my_workflow.json
|
||||||
|
workflow/my_workflow_app.json
|
||||||
|
app/*
|
||||||
@@ -1,18 +1,46 @@
|
|||||||
##
|
> 适配了最新版comfyui的py3.11 ,torch 2.1.2+cu121
|
||||||
v0.4.0 🚀🚗🚚🏃
|
|
||||||
- Add "help" option to the context menu for each node.
|
|
||||||
- Add "find the node" option to the global context menu.
|
|
||||||
- Optimize the 3D Image node and add workflow.
|
|
||||||
|
|
||||||
### 3D
|
> [discord](https://discord.gg/cXs9vZSqeK)
|
||||||

|
|
||||||
[workflow](./workflow/3D-workflow.json)
|
## 🚀🚗🚚🏃 Workflow-to-APP
|
||||||
|
- 新增AppInfo节点,可以通过简单的配置,把workflow转变为一个Web APP。
|
||||||
|
- 支持多个web app 切换
|
||||||
|
- 发布为app的workflow,可以在右键里再次编辑了
|
||||||
|
- web app可以设置分类,在comfyui右键菜单可以编辑更新web app
|
||||||
|
|
||||||
|
|
||||||
### ScreenShareNode & FloatingVideoNode
|
- Support multiple web app switching.
|
||||||
> 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! 💻🌐
|
- 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_3.json)
|
||||||
|
- [image-to-image](./example/Image-to-Image_2.json)
|
||||||
|
- text-to-text
|
||||||
|
|
||||||
|
> 暂时支持8种节点作为界面上的输入节点:Load Image、CLIPTextEncode、PromptSlide、TextInput_、Color、FloatSlider、IntNumber、CheckpointLoaderSimple、LoraLoader
|
||||||
|
|
||||||
|
> 输出节点:PreviewImage 、SaveImage、ShowTextForGPT、VHS_VideoCombine、PromptImage
|
||||||
|
|
||||||
|
> seed统一输入控件,支持:SamplerCustom、KSampler
|
||||||
|
|
||||||
|
|
||||||
|
## 🏃🚗🚚🚀 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! 💻🌐
|
||||||
|
|
||||||
>
|
|
||||||

|

|
||||||
|
|
||||||
https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43e-410a-ab3a-1952b7b4e7da
|
https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43e-410a-ab3a-1952b7b4e7da
|
||||||
@@ -23,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).
|
!! Please use the address with HTTPS (https://127.0.0.1).
|
||||||
|
|
||||||
|
|
||||||
### SpeechRecognition & SpeechSynthesis
|
### SpeechRecognition & SpeechSynthesis
|
||||||

|

|
||||||
|
|
||||||
@@ -31,17 +60,30 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
|||||||
### GPT
|
### 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
|
> 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)
|
[workflow-5](./workflow/5-gpt-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.
|
|
||||||
|
|
||||||

|
## Prompt
|
||||||
|
> PromptSlide
|
||||||
|

|
||||||
|
|
||||||
[workflow-4](./workflow/4-loadfromlocal-watcher-workflow.json)
|
<!--  -->
|
||||||
|
|
||||||
|
> 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
|
### Layers
|
||||||
@@ -51,6 +93,36 @@ https://github.com/shadowcz007/comfyui-mixlab-nodes/assets/12645064/e7e77f90-e43
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
[workflow-4](./workflow/4-loadfromlocal-watcher-workflow.json)
|
||||||
|
|
||||||
|
### LoadImagesFromURL
|
||||||
|
> Conveniently load images from a fixed address on the internet to ensure that default images in the workflow can be executed.
|
||||||
|
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
|
- [添加了DynamicDelayByText功能,可以根据输入文本的长度进行延迟执行。](./workflow/audio-chatgpt-workflow.json)
|
||||||
|
|
||||||
|
- [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
|
## Other Nodes
|
||||||
|
|
||||||

|

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

|
|
||||||
|
|
||||||
> TransparentImage
|
> TransparentImage
|
||||||
|
|
||||||
@@ -83,11 +153,15 @@ Add edges to an image.
|
|||||||

|

|
||||||
|
|
||||||
|
|
||||||
|
> LaMaInpainting
|
||||||
|
|
||||||
|
from [simple-lama-inpainting](https://github.com/enesmsahin/simple-lama-inpainting)
|
||||||
|
|
||||||
|
|
||||||
### Improvement
|
### Improvement
|
||||||
|
|
||||||
- Add "help" option to the context menu for each node.
|
- Add "help" option to the context menu for each node.
|
||||||
- Add "find the node" option to the global context menu.
|
- Add "Nodes Map" option to the global context menu.
|
||||||
|
|
||||||
An improvement has been made to directly redirect to GitHub to search for missing nodes when loading the graph.
|
An improvement has been made to directly redirect to GitHub to search for missing nodes when loading the graph.
|
||||||
|
|
||||||
@@ -97,12 +171,18 @@ An improvement has been made to directly redirect to GitHub to search for missin
|
|||||||
|
|
||||||
|
|
||||||
### Models
|
### Models
|
||||||
[Download CLIPSeg](https://huggingface.co/CIDAS/clipseg-rd64-refined/tree/main), move to : model/clipseg
|
|
||||||
|
|
||||||
<!-- ### Workflow
|
[Download rembg Models](https://github.com/danielgatis/rembg/tree/main#Models),move to:models/rembg
|
||||||
[Workflow](./workflow.md) -->
|
|
||||||
|
|
||||||
|
[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
|
## Installation
|
||||||
|
|
||||||
@@ -137,16 +217,12 @@ pip3 install -r requirements.txt
|
|||||||
访问 [www.mixcomfy.com](https://www.mixcomfy.com),获得更多内测功能,关注微信公众号:Mixlab无界社区
|
访问 [www.mixcomfy.com](https://www.mixcomfy.com),获得更多内测功能,关注微信公众号:Mixlab无界社区
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### Thanks:
|
#### Thanks:
|
||||||
[ComfyUI-CLIPSeg](https://github.com/biegert/ComfyUI-CLIPSeg/tree/main)
|
[ComfyUI-CLIPSeg](https://github.com/biegert/ComfyUI-CLIPSeg/tree/main)
|
||||||
|
|
||||||
#### discussions:
|
#### discussions:
|
||||||
[discussions](https://github.com/shadowcz007/comfyui-mixlab-nodes/discussions)
|
[discussions](https://github.com/shadowcz007/comfyui-mixlab-nodes/discussions)
|
||||||
|
|
||||||
### TODO:
|
|
||||||
- 音频播放节点:带可视化、支持多音轨、可配置音轨音量
|
|
||||||
- vector https://github.com/GeorgLegato/stable-diffusion-webui-vectorstudio
|
|
||||||
|
|
||||||
|
|
||||||
<picture>
|
<picture>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import subprocess
|
|||||||
import importlib.util
|
import importlib.util
|
||||||
import sys,json
|
import sys,json
|
||||||
import urllib
|
import urllib
|
||||||
|
import hashlib
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
|
|
||||||
@@ -79,6 +79,13 @@ install_openai()
|
|||||||
current_path = os.path.abspath(os.path.dirname(__file__))
|
current_path = os.path.abspath(os.path.dirname(__file__))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_md5(string):
|
||||||
|
encoded_string = string.encode()
|
||||||
|
md5_hash = hashlib.md5(encoded_string).hexdigest()
|
||||||
|
return md5_hash
|
||||||
|
|
||||||
|
|
||||||
def create_key(key_p,crt_p):
|
def create_key(key_p,crt_p):
|
||||||
import OpenSSL
|
import OpenSSL
|
||||||
# 生成自签名证书
|
# 生成自签名证书
|
||||||
@@ -126,8 +133,38 @@ def create_for_https():
|
|||||||
return (crt,key)
|
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
|
# workflow
|
||||||
def read_workflow_json_files(folder_path):
|
def read_workflow_json_files(folder_path ):
|
||||||
json_files = []
|
json_files = []
|
||||||
for filename in os.listdir(folder_path):
|
for filename in os.listdir(folder_path):
|
||||||
if filename.endswith('.json'):
|
if filename.endswith('.json'):
|
||||||
@@ -155,19 +192,148 @@ def read_workflow_json_files(folder_path):
|
|||||||
def get_workflows():
|
def get_workflows():
|
||||||
# print("#####path::", current_path)
|
# print("#####path::", current_path)
|
||||||
workflow_path=os.path.join(current_path, "workflow")
|
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):
|
if not os.path.exists(workflow_path):
|
||||||
# 使用mkdir()方法创建新目录
|
# 使用mkdir()方法创建新目录
|
||||||
os.mkdir(workflow_path)
|
os.mkdir(workflow_path)
|
||||||
workflows=read_workflow_json_files(workflow_path)
|
workflows=read_workflow_json_files(workflow_path)
|
||||||
return workflows
|
return workflows
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
#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"],
|
||||||
|
})
|
||||||
|
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"]
|
||||||
|
})
|
||||||
|
i+=1
|
||||||
|
except Exception as e:
|
||||||
|
print("发生异常:", str(e))
|
||||||
|
else:
|
||||||
|
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:
|
||||||
|
apps = [{
|
||||||
|
'filename':filename,
|
||||||
|
'data':json.load(json_file)
|
||||||
|
}]
|
||||||
|
except Exception as e:
|
||||||
|
print("发生异常:", str(e))
|
||||||
|
|
||||||
|
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"])
|
||||||
|
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"]
|
||||||
|
})
|
||||||
|
|
||||||
|
return apps
|
||||||
|
|
||||||
def save_workflow_json(data):
|
def save_workflow_json(data):
|
||||||
workflow_path=os.path.join(current_path, "workflow/my_workflow.json")
|
workflow_path=os.path.join(current_path, "workflow/my_workflow.json")
|
||||||
with open(workflow_path, 'w') as file:
|
with open(workflow_path, 'w') as file:
|
||||||
json.dump(data, file)
|
json.dump(data, file)
|
||||||
return workflow_path
|
return workflow_path
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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'])
|
||||||
|
data['app']['id']=calculate_md5(output_str)
|
||||||
|
# id=data['app']['id']
|
||||||
|
except Exception as e:
|
||||||
|
print("发生异常:", str(e))
|
||||||
|
|
||||||
|
with open(app_workflow_path, 'w') as file:
|
||||||
|
json.dump(data, file)
|
||||||
|
return filename
|
||||||
|
|
||||||
def get_nodes_map():
|
def get_nodes_map():
|
||||||
# print("#####path::", current_path)
|
# print("#####path::", current_path)
|
||||||
@@ -201,33 +367,62 @@ async def new_request(self, method, url, *args, **kwargs):
|
|||||||
|
|
||||||
# 应用 Monkey Patch
|
# 应用 Monkey Patch
|
||||||
aiohttp.ClientSession._request = new_request
|
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
|
# https
|
||||||
async def new_start(self, address, port, verbose=True, call_on_start=None):
|
async def new_start(self, address, port, verbose=True, call_on_start=None):
|
||||||
|
try:
|
||||||
|
|
||||||
runner = web.AppRunner(self.app, access_log=None)
|
runner = web.AppRunner(self.app, access_log=None)
|
||||||
await runner.setup()
|
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)
|
site = web.TCPSite(runner, address, port)
|
||||||
await site.start()
|
await site.start()
|
||||||
|
|
||||||
|
PromptServer.instance.port=port
|
||||||
|
|
||||||
|
|
||||||
import ssl
|
import ssl
|
||||||
crt,key=create_for_https()
|
crt, key = create_for_https()
|
||||||
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
|
||||||
ssl_context.load_cert_chain(crt,key)
|
ssl_context.load_cert_chain(crt, key)
|
||||||
site2 = web.TCPSite(runner, address, port+1,ssl_context=ssl_context)
|
|
||||||
await site2.start()
|
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 == '':
|
if address == '':
|
||||||
address = '0.0.0.0'
|
address = '0.0.0.0'
|
||||||
if verbose:
|
if verbose:
|
||||||
# print('\033[91mMixlab Nodes: \033[93mLoaded\033[0m')
|
|
||||||
print("\033[93mStarting server\n")
|
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: 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:
|
if call_on_start is not None:
|
||||||
call_on_start(address, port)
|
call_on_start(address, port)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error starting the server: {e}")
|
||||||
|
|
||||||
# import webbrowser
|
# import webbrowser
|
||||||
# if os.name == 'nt' and address == '0.0.0.0':
|
# if os.name == 'nt' and address == '0.0.0.0':
|
||||||
# address = '127.0.0.1'
|
# address = '127.0.0.1'
|
||||||
@@ -238,7 +433,7 @@ async def new_start(self, address, port, verbose=True, call_on_start=None):
|
|||||||
PromptServer.start=new_start
|
PromptServer.start=new_start
|
||||||
|
|
||||||
# 创建路由表
|
# 创建路由表
|
||||||
routes = web.RouteTableDef()
|
routes = PromptServer.instance.routes
|
||||||
|
|
||||||
@routes.post('/mixlab')
|
@routes.post('/mixlab')
|
||||||
async def mixlab_hander(request):
|
async def mixlab_hander(request):
|
||||||
@@ -253,6 +448,18 @@ async def mixlab_hander(request):
|
|||||||
print(e)
|
print(e)
|
||||||
return web.json_response(data)
|
return web.json_response(data)
|
||||||
|
|
||||||
|
|
||||||
|
@routes.get('/mixlab/app')
|
||||||
|
async def mixlab_app_handler(request):
|
||||||
|
html_file = os.path.join(current_path, "web/index.html")
|
||||||
|
if os.path.exists(html_file):
|
||||||
|
with open(html_file, 'r', encoding='utf-8', errors='ignore') as f:
|
||||||
|
html_data = f.read()
|
||||||
|
return web.Response(text=html_data, content_type='text/html')
|
||||||
|
else:
|
||||||
|
return web.Response(text="HTML file not found", status=404)
|
||||||
|
|
||||||
|
|
||||||
@routes.post('/mixlab/workflow')
|
@routes.post('/mixlab/workflow')
|
||||||
async def mixlab_workflow_hander(request):
|
async def mixlab_workflow_hander(request):
|
||||||
data = await request.json()
|
data = await request.json()
|
||||||
@@ -265,7 +472,31 @@ async def mixlab_workflow_hander(request):
|
|||||||
'status':'success',
|
'status':'success',
|
||||||
'file_path':file_path
|
'file_path':file_path
|
||||||
}
|
}
|
||||||
|
elif data['task']=='save_app':
|
||||||
|
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,category,admin),
|
||||||
|
'status':'success',
|
||||||
|
}
|
||||||
elif data['task']=='list':
|
elif data['task']=='list':
|
||||||
|
# 暂时没有用到
|
||||||
result={
|
result={
|
||||||
'data':get_workflows(),
|
'data':get_workflows(),
|
||||||
'status':'success',
|
'status':'success',
|
||||||
@@ -289,21 +520,6 @@ async def nodes_map_hander(request):
|
|||||||
|
|
||||||
return web.json_response(result)
|
return web.json_response(result)
|
||||||
|
|
||||||
def new_add_routes(self):
|
|
||||||
import nodes
|
|
||||||
self.app.add_routes(routes)
|
|
||||||
self.app.add_routes(self.routes)
|
|
||||||
for name, dir in nodes.EXTENSION_WEB_DIRS.items():
|
|
||||||
self.app.add_routes([
|
|
||||||
web.static('/extensions/' + urllib.parse.quote(name), dir, follow_symlinks=True),
|
|
||||||
])
|
|
||||||
self.app.add_routes([
|
|
||||||
web.static('/', self.web_root, follow_symlinks=True),
|
|
||||||
])
|
|
||||||
|
|
||||||
PromptServer.add_routes=new_add_routes
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 扩展api接口
|
# 扩展api接口
|
||||||
# from server import PromptServer
|
# from server import PromptServer
|
||||||
@@ -317,29 +533,45 @@ PromptServer.add_routes=new_add_routes
|
|||||||
|
|
||||||
|
|
||||||
# 导入节点
|
# 导入节点
|
||||||
from .nodes.PromptNode import RandomPrompt
|
from .nodes.PromptNode import EmbeddingPrompt,RandomPrompt,PromptSlide,PromptSimplification,PromptImage,JoinWithDelimiter
|
||||||
from .nodes.ImageNode import TransparentImage,LoadImagesFromPath,ResizeImage,TextImage,SvgImage,Image3D,EmptyLayer,ShowLayer,NewLayer,MergeLayers,AreaToMask,SmoothMask,FeatheredMask,SplitLongMask,ImageCropByAlpha,EnhanceImage,FaceToMask
|
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.Vae import VAELoader,VAEDecode
|
||||||
from .nodes.ScreenShareNode import ScreenShareNode,FloatingVideo
|
from .nodes.ScreenShareNode import ScreenShareNode,FloatingVideo
|
||||||
from .nodes.Clipseg import CLIPSeg,CombineMasks
|
from .nodes.Clipseg import CLIPSeg,CombineMasks
|
||||||
from .nodes.ChatGPT import ChatGPTNode,ShowTextForGPT,CharacterInText
|
from .nodes.ChatGPT import ChatGPTNode,ShowTextForGPT,CharacterInText
|
||||||
from .nodes.Audio import GamePal,SpeechRecognition,SpeechSynthesis
|
from .nodes.Audio import GamePal,SpeechRecognition,SpeechSynthesis
|
||||||
from .nodes.Utils import ColorInput,FontInput,TextToNumber,DynamicDelayProcessor
|
from .nodes.Utils import CreateLoraNames,CreateSampler_names,CreateCkptNames,CreateSeedNode,TESTNODE_,IntNumber,FloatSlider,TextInput,ColorInput,FontInput,TextToNumber,DynamicDelayProcessor,LimitNumber,SwitchByIndex,MultiplicationNode
|
||||||
|
from .nodes.Mask import OutlineMask,FeatheredMask
|
||||||
|
from .nodes.App import AppInfo,AppNode
|
||||||
|
|
||||||
# 要导出的所有节点及其名称的字典
|
# 要导出的所有节点及其名称的字典
|
||||||
# 注意:名称应全局唯一
|
# 注意:名称应全局唯一
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"AppInfo":AppInfo,
|
||||||
|
"AppNode":AppNode,
|
||||||
|
"TESTNODE_":TESTNODE_,
|
||||||
"RandomPrompt":RandomPrompt,
|
"RandomPrompt":RandomPrompt,
|
||||||
|
"EmbeddingPrompt":EmbeddingPrompt,
|
||||||
|
"PromptSlide":PromptSlide,
|
||||||
|
"PromptSimplification":PromptSimplification,
|
||||||
|
"PromptImage":PromptImage,
|
||||||
|
"MirroredImage":MirroredImage,
|
||||||
|
"NoiseImage":NoiseImage,
|
||||||
|
"GradientImage":GradientImage,
|
||||||
"TransparentImage":TransparentImage,
|
"TransparentImage":TransparentImage,
|
||||||
"ResizeImageMixlab":ResizeImage,
|
"ResizeImageMixlab":ResizeImage,
|
||||||
"LoadImagesFromPath":LoadImagesFromPath,
|
"LoadImagesFromPath":LoadImagesFromPath,
|
||||||
|
"LoadImagesFromURL":LoadImagesFromURL,
|
||||||
"TextImage":TextImage,
|
"TextImage":TextImage,
|
||||||
"EnhanceImage":EnhanceImage,
|
"EnhanceImage":EnhanceImage,
|
||||||
"SvgImage":SvgImage,
|
"SvgImage":SvgImage,
|
||||||
"3DImage":Image3D,
|
"3DImage":Image3D,
|
||||||
|
"ImageColorTransfer":ImageColorTransfer,
|
||||||
"ShowLayer":ShowLayer,
|
"ShowLayer":ShowLayer,
|
||||||
"NewLayer":NewLayer,
|
"NewLayer":NewLayer,
|
||||||
|
"SplitImage":SplitImage,
|
||||||
|
"CenterImage":CenterImage,
|
||||||
|
"GridOutput":GridOutput,
|
||||||
"MergeLayers":MergeLayers,
|
"MergeLayers":MergeLayers,
|
||||||
"SplitLongMask":SplitLongMask,
|
"SplitLongMask":SplitLongMask,
|
||||||
"FeatheredMask":FeatheredMask,
|
"FeatheredMask":FeatheredMask,
|
||||||
@@ -348,6 +580,7 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"AreaToMask":AreaToMask,
|
"AreaToMask":AreaToMask,
|
||||||
"ImageCropByAlpha":ImageCropByAlpha,
|
"ImageCropByAlpha":ImageCropByAlpha,
|
||||||
"VAELoaderConsistencyDecoder":VAELoader,
|
"VAELoaderConsistencyDecoder":VAELoader,
|
||||||
|
"SaveImageToLocal":SaveImageToLocal,
|
||||||
"VAEDecodeConsistencyDecoder":VAEDecode,
|
"VAEDecodeConsistencyDecoder":VAEDecode,
|
||||||
"ScreenShare":ScreenShareNode,
|
"ScreenShare":ScreenShareNode,
|
||||||
"FloatingVideo":FloatingVideo,
|
"FloatingVideo":FloatingVideo,
|
||||||
@@ -359,15 +592,30 @@ NODE_CLASS_MAPPINGS = {
|
|||||||
"SpeechRecognition":SpeechRecognition,
|
"SpeechRecognition":SpeechRecognition,
|
||||||
"SpeechSynthesis":SpeechSynthesis,
|
"SpeechSynthesis":SpeechSynthesis,
|
||||||
"Color":ColorInput,
|
"Color":ColorInput,
|
||||||
|
"FloatSlider":FloatSlider,
|
||||||
|
"IntNumber":IntNumber,
|
||||||
|
"TextInput_":TextInput,
|
||||||
"Font":FontInput,
|
"Font":FontInput,
|
||||||
"TextToNumber":TextToNumber,
|
"TextToNumber":TextToNumber,
|
||||||
"DynamicDelayProcessor":DynamicDelayProcessor
|
"DynamicDelayProcessor":DynamicDelayProcessor,
|
||||||
|
"MultiplicationNode":MultiplicationNode,
|
||||||
|
"GetImageSize_":GetImageSize_,
|
||||||
|
"SwitchByIndex":SwitchByIndex,
|
||||||
|
"LimitNumber":LimitNumber,
|
||||||
|
"OutlineMask":OutlineMask,
|
||||||
|
"JoinWithDelimiter":JoinWithDelimiter,
|
||||||
|
"Seed_":CreateSeedNode,
|
||||||
|
"CkptNames_":CreateCkptNames,
|
||||||
|
"SamplerNames_":CreateSampler_names,
|
||||||
|
"LoraNames_":CreateLoraNames
|
||||||
|
# "LaMaInpainting":LaMaInpainting
|
||||||
# "GamePal":GamePal
|
# "GamePal":GamePal
|
||||||
}
|
}
|
||||||
|
|
||||||
# 一个包含节点友好/可读的标题的字典
|
# 一个包含节点友好/可读的标题的字典
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
"ResizeImageMixlab":"ResizeImage",
|
"AppInfo":"AppInfo ♾️Mixlab",
|
||||||
|
"ResizeImageMixlab":"ResizeImage ♾️Mixlab",
|
||||||
"RandomPrompt": "Random Prompt ♾️Mixlab",
|
"RandomPrompt": "Random Prompt ♾️Mixlab",
|
||||||
"SplitLongMask":"Splitting a long image into sections",
|
"SplitLongMask":"Splitting a long image into sections",
|
||||||
"VAELoaderConsistencyDecoder":"Consistency Decoder Loader",
|
"VAELoaderConsistencyDecoder":"Consistency Decoder Loader",
|
||||||
@@ -380,14 +628,55 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"SpeechSynthesis":"SpeechSynthesis ♾️Mixlab",
|
"SpeechSynthesis":"SpeechSynthesis ♾️Mixlab",
|
||||||
"SpeechRecognition":"SpeechRecognition ♾️Mixlab",
|
"SpeechRecognition":"SpeechRecognition ♾️Mixlab",
|
||||||
"3DImage":"3DImage ♾️Mixlab",
|
"3DImage":"3DImage ♾️Mixlab",
|
||||||
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab"
|
"DynamicDelayProcessor":"DynamicDelayByText ♾️Mixlab",
|
||||||
|
"LaMaInpainting":"LaMaInpainting ♾️Mixlab",
|
||||||
# "GamePal":"GamePal ♾️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 ui的节点功能
|
||||||
WEB_DIRECTORY = "./web"
|
WEB_DIRECTORY = "./web"
|
||||||
|
|
||||||
print('--------------')
|
print('--------------')
|
||||||
print('\033[91mMixlab Nodes: \033[93mLoaded\033[0m')
|
print('\033[91m ### Mixlab Nodes: \033[93mLoaded')
|
||||||
print('--------------')
|
|
||||||
|
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')
|
||||||
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 2.4 MiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 240 KiB |
|
After Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 7.4 MiB After Width: | Height: | Size: 7.1 MiB |
|
Before Width: | Height: | Size: 8.7 MiB After Width: | Height: | Size: 9.9 MiB |
|
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
|
||||||
@@ -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,26 +4761,46 @@
|
|||||||
],
|
],
|
||||||
"https://github.com/shadowcz007/comfyui-mixlab-nodes": [
|
"https://github.com/shadowcz007/comfyui-mixlab-nodes": [
|
||||||
[
|
[
|
||||||
|
"GridOutput",
|
||||||
|
"SplitImage",
|
||||||
|
"PromptGenerate_Mix",
|
||||||
|
"JoinWithDelimiter",
|
||||||
|
"ChinesePrompt_Mix",
|
||||||
"3DImage",
|
"3DImage",
|
||||||
|
"AppInfo",
|
||||||
|
"AppNode",
|
||||||
|
"IntNumber",
|
||||||
|
"FloatSlider",
|
||||||
|
"ResizeImage",
|
||||||
|
"NoiseImage",
|
||||||
|
"PromptImage",
|
||||||
|
"SaveImageToLocal",
|
||||||
"AreaToMask",
|
"AreaToMask",
|
||||||
"CLIPSeg",
|
|
||||||
"CLIPSeg_",
|
"CLIPSeg_",
|
||||||
"CharacterInText",
|
"CharacterInText",
|
||||||
"ChatGPTOpenAI",
|
"ChatGPTOpenAI",
|
||||||
"Color",
|
"Color",
|
||||||
|
"Seed_",
|
||||||
|
"CkptNames_",
|
||||||
|
"SamplerNames_",
|
||||||
|
"LoraNames_",
|
||||||
"CombineMasks_",
|
"CombineMasks_",
|
||||||
"CombineSegMasks",
|
|
||||||
"EmptyLayer",
|
|
||||||
"EnhanceImage",
|
"EnhanceImage",
|
||||||
|
"GradientImage",
|
||||||
"FaceToMask",
|
"FaceToMask",
|
||||||
"FeatheredMask",
|
"FeatheredMask",
|
||||||
"FloatingVideo",
|
"FloatingVideo",
|
||||||
"Font",
|
"Font",
|
||||||
"ImageCropByAlpha",
|
"ImageCropByAlpha",
|
||||||
"LoadImagesFromPath",
|
"LoadImagesFromPath",
|
||||||
|
"LoadImagesFromURL",
|
||||||
"MergeLayers",
|
"MergeLayers",
|
||||||
"NewLayer",
|
"NewLayer",
|
||||||
|
"CenterImage",
|
||||||
"RandomPrompt",
|
"RandomPrompt",
|
||||||
|
"PromptSlide",
|
||||||
|
"PromptSimplification",
|
||||||
|
"ClipInterrogator",
|
||||||
"ScreenShare",
|
"ScreenShare",
|
||||||
"ShowLayer",
|
"ShowLayer",
|
||||||
"ShowTextForGPT",
|
"ShowTextForGPT",
|
||||||
@@ -4790,12 +4810,18 @@
|
|||||||
"SplitLongMask",
|
"SplitLongMask",
|
||||||
"SvgImage",
|
"SvgImage",
|
||||||
"TextImage",
|
"TextImage",
|
||||||
|
"ResizeImageMixlab",
|
||||||
"TransparentImage",
|
"TransparentImage",
|
||||||
"VAEDecodeConsistencyDecoder",
|
"VAEDecodeConsistencyDecoder",
|
||||||
"VAELoaderConsistencyDecoder"
|
"VAELoaderConsistencyDecoder",
|
||||||
|
"TextToNumber",
|
||||||
|
"TextInput_",
|
||||||
|
"DynamicDelayProcessor",
|
||||||
|
"LaMaInpainting",
|
||||||
|
"Moondream"
|
||||||
],
|
],
|
||||||
{
|
{
|
||||||
"title_aux": "comfyui-mixlab-nodes [WIP]"
|
"title_aux": "comfyui-mixlab-nodes"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"https://github.com/shiimizu/ComfyUI_smZNodes": [
|
"https://github.com/shiimizu/ComfyUI_smZNodes": [
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,397 @@
|
|||||||
|
import os,sys
|
||||||
|
# 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 os
|
||||||
|
import json
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
import folder_paths
|
||||||
|
from server import PromptServer
|
||||||
|
import importlib.util
|
||||||
|
|
||||||
|
|
||||||
|
def is_installed(package):
|
||||||
|
try:
|
||||||
|
spec = importlib.util.find_spec(package)
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
return False
|
||||||
|
return spec is not None
|
||||||
|
|
||||||
|
|
||||||
|
class AnyType(str):
|
||||||
|
"""A special class that is always equal in not equal comparisons. Credit to pythongosssss"""
|
||||||
|
|
||||||
|
def __ne__(self, __value: object) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
any_type = AnyType("*")
|
||||||
|
|
||||||
|
app_path = os.path.abspath(os.path.join(os.path.dirname(__file__),'../app'))
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
option=os.path.basename(os.path.dirname(file_path))+'/'+os.path.basename(file_path)
|
||||||
|
if os.path.dirname(file_path) == folder_path:
|
||||||
|
option=os.path.basename(file_path)
|
||||||
|
|
||||||
|
file_info = {
|
||||||
|
'filename': os.path.basename(file_path),
|
||||||
|
'category': os.path.dirname(file_path),
|
||||||
|
'data': json_data,
|
||||||
|
'date': numeric_timestamp,
|
||||||
|
"option":option
|
||||||
|
}
|
||||||
|
data.append(file_info)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
|
||||||
|
sorted_data = sorted(data, key=lambda x: x['date'], reverse=True)
|
||||||
|
return sorted_data
|
||||||
|
|
||||||
|
def tensor2pil(image):
|
||||||
|
return Image.fromarray(np.clip(255. * image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
||||||
|
|
||||||
|
|
||||||
|
def create_temp_file(image):
|
||||||
|
output_dir = folder_paths.get_temp_directory()
|
||||||
|
|
||||||
|
(
|
||||||
|
full_output_folder,
|
||||||
|
filename,
|
||||||
|
counter,
|
||||||
|
subfolder,
|
||||||
|
_,
|
||||||
|
) = folder_paths.get_save_image_path('tmp', output_dir)
|
||||||
|
|
||||||
|
|
||||||
|
im=tensor2pil(image)
|
||||||
|
|
||||||
|
image_file = f"{filename}_{counter:05}.png"
|
||||||
|
|
||||||
|
image_path=os.path.join(full_output_folder, image_file)
|
||||||
|
|
||||||
|
im.save(image_path,compress_level=4)
|
||||||
|
|
||||||
|
return [{
|
||||||
|
"filename": image_file,
|
||||||
|
"subfolder": subfolder,
|
||||||
|
"type": "temp"
|
||||||
|
}]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
if is_installed('websocket')==False:
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
# 安装
|
||||||
|
print('#pip install websocket-client')
|
||||||
|
|
||||||
|
result = subprocess.run([sys.executable, '-s', '-m', 'pip', 'install', 'websocket-client'], capture_output=True, text=True)
|
||||||
|
|
||||||
|
#检查命令执行结果
|
||||||
|
if result.returncode == 0:
|
||||||
|
print("#install success")
|
||||||
|
import websocket
|
||||||
|
else:
|
||||||
|
print("#install error")
|
||||||
|
|
||||||
|
else:
|
||||||
|
import websocket
|
||||||
|
# NOTE: websocket-client (https://github.com/websocket-client/websocket-client)
|
||||||
|
|
||||||
|
except:
|
||||||
|
print("#websocket-client error")
|
||||||
|
#This is an example that uses the websockets api to know when a prompt execution is done
|
||||||
|
#Once the prompt execution is done it downloads the images using the /history endpoint
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
|
server_address = "127.0.0.1:8188"
|
||||||
|
client_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
def queue_prompt(prompt):
|
||||||
|
p = {"prompt": prompt, "client_id": client_id}
|
||||||
|
data = json.dumps(p).encode('utf-8')
|
||||||
|
req = urllib.request.Request("http://{}/prompt".format(server_address), data=data)
|
||||||
|
return json.loads(urllib.request.urlopen(req).read())
|
||||||
|
|
||||||
|
def get_image(filename, subfolder, folder_type):
|
||||||
|
data = {"filename": filename, "subfolder": subfolder, "type": folder_type}
|
||||||
|
url_values = urllib.parse.urlencode(data)
|
||||||
|
with urllib.request.urlopen("http://{}/view?{}".format(server_address, url_values)) as response:
|
||||||
|
return response.read()
|
||||||
|
|
||||||
|
def get_history(prompt_id):
|
||||||
|
with urllib.request.urlopen("http://{}/history/{}".format(server_address, prompt_id)) as response:
|
||||||
|
return json.loads(response.read())
|
||||||
|
|
||||||
|
def get_images(ws, prompt):
|
||||||
|
prompt_id = queue_prompt(prompt)['prompt_id']
|
||||||
|
output_images = {}
|
||||||
|
while True:
|
||||||
|
out = ws.recv()
|
||||||
|
if isinstance(out, str):
|
||||||
|
message = json.loads(out)
|
||||||
|
if message['type'] == 'executing':
|
||||||
|
data = message['data']
|
||||||
|
if data['node'] is None and data['prompt_id'] == prompt_id:
|
||||||
|
break #Execution is done
|
||||||
|
else:
|
||||||
|
continue #previews are binary data
|
||||||
|
|
||||||
|
history = get_history(prompt_id)[prompt_id]
|
||||||
|
for o in history['outputs']:
|
||||||
|
for node_id in history['outputs']:
|
||||||
|
node_output = history['outputs'][node_id]
|
||||||
|
if 'images' in node_output:
|
||||||
|
images_output = []
|
||||||
|
for image in node_output['images']:
|
||||||
|
image_data = get_image(image['filename'], image['subfolder'], image['type'])
|
||||||
|
images_output.append(image_data)
|
||||||
|
output_images[node_id] = images_output
|
||||||
|
|
||||||
|
return output_images
|
||||||
|
|
||||||
|
prompt_text = """
|
||||||
|
{
|
||||||
|
"3": {
|
||||||
|
"class_type": "KSampler",
|
||||||
|
"inputs": {
|
||||||
|
"cfg": 8,
|
||||||
|
"denoise": 1,
|
||||||
|
"latent_image": [
|
||||||
|
"5",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"model": [
|
||||||
|
"4",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"negative": [
|
||||||
|
"7",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"positive": [
|
||||||
|
"6",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"sampler_name": "euler",
|
||||||
|
"scheduler": "normal",
|
||||||
|
"seed": 8566257,
|
||||||
|
"steps": 20
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
"class_type": "CheckpointLoaderSimple",
|
||||||
|
"inputs": {
|
||||||
|
"ckpt_name": "v1-5-pruned-emaonly.ckpt"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
"class_type": "EmptyLatentImage",
|
||||||
|
"inputs": {
|
||||||
|
"batch_size": 1,
|
||||||
|
"height": 512,
|
||||||
|
"width": 512
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"6": {
|
||||||
|
"class_type": "CLIPTextEncode",
|
||||||
|
"inputs": {
|
||||||
|
"clip": [
|
||||||
|
"4",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"text": "masterpiece best quality girl"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"7": {
|
||||||
|
"class_type": "CLIPTextEncode",
|
||||||
|
"inputs": {
|
||||||
|
"clip": [
|
||||||
|
"4",
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"text": "bad hands"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
"class_type": "VAEDecode",
|
||||||
|
"inputs": {
|
||||||
|
"samples": [
|
||||||
|
"3",
|
||||||
|
0
|
||||||
|
],
|
||||||
|
"vae": [
|
||||||
|
"4",
|
||||||
|
2
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"9": {
|
||||||
|
"class_type": "SaveImage",
|
||||||
|
"inputs": {
|
||||||
|
"filename_prefix": "ComfyUI",
|
||||||
|
"images": [
|
||||||
|
"8",
|
||||||
|
0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
# prompt = json.loads(prompt_text)
|
||||||
|
# #set the text prompt for our positive CLIPTextEncode
|
||||||
|
# prompt["6"]["inputs"]["text"] = "masterpiece best quality man"
|
||||||
|
|
||||||
|
# #set the seed for our KSampler node
|
||||||
|
# prompt["3"]["inputs"]["seed"] = 5
|
||||||
|
|
||||||
|
# ws = websocket.WebSocket()
|
||||||
|
# ws.connect("ws://{}/ws?clientId={}".format(server_address, client_id))
|
||||||
|
# images = get_images(ws, prompt)
|
||||||
|
|
||||||
|
# #Commented out code to display the output images:
|
||||||
|
|
||||||
|
# # for node_id in images:
|
||||||
|
# # for image_data in images[node_id]:
|
||||||
|
# # from PIL import Image
|
||||||
|
# # import io
|
||||||
|
# # image = Image.open(io.BytesIO(image_data))
|
||||||
|
# # image.show()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# app 配置节点
|
||||||
|
class AppInfo:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"name": ("STRING",{"multiline": False,"default": "Mixlab-App","dynamicPrompts": False}),
|
||||||
|
"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,
|
||||||
|
"min": 1,
|
||||||
|
"max": 10000,
|
||||||
|
"step": 1,
|
||||||
|
"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 = ()
|
||||||
|
# RETURN_NAMES = ("IMAGE",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "♾️Mixlab"
|
||||||
|
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
INPUT_IS_LIST = True
|
||||||
|
# OUTPUT_IS_LIST = (True,)
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
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,link,category]}, "result": ()}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# app可以当成节点运行
|
||||||
|
class AppNode:
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"name": ([x['option'] for x in read_workflow_json_files_all(app_path)],),
|
||||||
|
"image":("IMAGE",),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = (any_type,)
|
||||||
|
RETURN_NAMES = ("output",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "♾️Mixlab"
|
||||||
|
|
||||||
|
OUTPUT_NODE = True
|
||||||
|
INPUT_IS_LIST = False
|
||||||
|
OUTPUT_IS_LIST = (False,)
|
||||||
|
|
||||||
|
|
||||||
|
def run(self,name,input):
|
||||||
|
print('#app_path',input)
|
||||||
|
print(PromptServer.instance.port)
|
||||||
|
|
||||||
|
return (name,)
|
||||||
|
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ class SpeechRecognition:
|
|||||||
|
|
||||||
FUNCTION = "run"
|
FUNCTION = "run"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/audio"
|
CATEGORY = "♾️Mixlab/Audio"
|
||||||
|
|
||||||
INPUT_IS_LIST = False
|
INPUT_IS_LIST = False
|
||||||
OUTPUT_IS_LIST = (False,)
|
OUTPUT_IS_LIST = (False,)
|
||||||
@@ -48,7 +48,7 @@ class SpeechSynthesis:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
OUTPUT_IS_LIST = (True,)
|
OUTPUT_IS_LIST = (True,)
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/audio"
|
CATEGORY = "♾️Mixlab/Audio"
|
||||||
|
|
||||||
def run(self, text):
|
def run(self, text):
|
||||||
# print(session_history)
|
# print(session_history)
|
||||||
@@ -82,7 +82,7 @@ class GamePal:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
OUTPUT_IS_LIST = (False,)
|
OUTPUT_IS_LIST = (False,)
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/audio"
|
CATEGORY = "♾️Mixlab/Audio"
|
||||||
|
|
||||||
def run(self, input_text,input_num,python_code):
|
def run(self, input_text,input_num,python_code):
|
||||||
exec(python_code)
|
exec(python_code)
|
||||||
|
|||||||
@@ -1,7 +1,15 @@
|
|||||||
import openai
|
import openai
|
||||||
import time
|
import time
|
||||||
import urllib.error
|
import urllib.error
|
||||||
import re,json
|
import re,json,os,string,random
|
||||||
|
import folder_paths
|
||||||
|
|
||||||
|
|
||||||
|
def generate_random_string(length):
|
||||||
|
letters = string.ascii_letters + string.digits
|
||||||
|
return ''.join(random.choice(letters) for _ in range(length))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 判断是否是azure服务
|
# 判断是否是azure服务
|
||||||
def is_azure_url(url):
|
def is_azure_url(url):
|
||||||
@@ -73,17 +81,17 @@ class ChatGPTNode:
|
|||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"api_key":("KEY", {"default": "", "multiline": True}),
|
"api_key":("KEY", {"default": "", "multiline": True,"dynamicPrompts": False}),
|
||||||
"api_url":("URL", {"default": "", "multiline": True}),
|
"api_url":("URL", {"default": "", "multiline": True,"dynamicPrompts": False}),
|
||||||
"prompt": ("STRING", {"multiline": True}),
|
"prompt": ("STRING", {"multiline": True,"dynamicPrompts": False}),
|
||||||
"system_content": ("STRING",
|
"system_content": ("STRING",
|
||||||
{
|
{
|
||||||
"default": "You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.",
|
"default": "You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.",
|
||||||
"multiline": True
|
"multiline": True,"dynamicPrompts": False
|
||||||
}),
|
}),
|
||||||
"model": (["gpt-3.5-turbo","gpt-35-turbo","gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613", "gpt-4-0613","gpt-4-1106-preview"],
|
"model": (["gpt-3.5-turbo","gpt-35-turbo","gpt-3.5-turbo-16k", "gpt-3.5-turbo-16k-0613", "gpt-4-0613","gpt-4-1106-preview"],
|
||||||
{"default": "gpt-3.5-turbo"}),
|
{"default": "gpt-3.5-turbo"}),
|
||||||
"seed": ("INT", {"default": 0, "min": 0, "max": 10000, "step": 1}),
|
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "step": 1}),
|
||||||
"context_size":("INT", {"default": 1, "min": 0, "max":30, "step": 1}),
|
"context_size":("INT", {"default": 1, "min": 0, "max":30, "step": 1}),
|
||||||
},
|
},
|
||||||
"hidden": {
|
"hidden": {
|
||||||
@@ -167,8 +175,11 @@ class ShowTextForGPT:
|
|||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"text": ("STRING", {"forceInput": True}),
|
"text": ("STRING", {"forceInput": True,"dynamicPrompts": False}),
|
||||||
}
|
},
|
||||||
|
"optional":{
|
||||||
|
"output_dir": ("STRING",{"default": "","multiline": True,"dynamicPrompts": False}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INPUT_IS_LIST = True
|
INPUT_IS_LIST = True
|
||||||
@@ -179,9 +190,19 @@ class ShowTextForGPT:
|
|||||||
|
|
||||||
CATEGORY = "♾️Mixlab/GPT"
|
CATEGORY = "♾️Mixlab/GPT"
|
||||||
|
|
||||||
def run(self, text):
|
def run(self, text,output_dir):
|
||||||
# print(session_history)
|
output_dir=output_dir[0]
|
||||||
|
filename=generate_random_string(4)+'.txt'
|
||||||
|
|
||||||
|
if output_dir=='':
|
||||||
|
output_dir = folder_paths.get_temp_directory()
|
||||||
|
|
||||||
|
save_to_dirpath=os.path.join(output_dir,filename)
|
||||||
|
with open(save_to_dirpath, 'w') as file:
|
||||||
|
file.write("\n".join(text))
|
||||||
|
# print(text)
|
||||||
return {"ui": {"text": text}, "result": (text,)}
|
return {"ui": {"text": text}, "result": (text,)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CharacterInText:
|
class CharacterInText:
|
||||||
@@ -189,8 +210,8 @@ class CharacterInText:
|
|||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"text": ("STRING", {"multiline": True}),
|
"text": ("STRING", {"multiline": True,"dynamicPrompts": False}),
|
||||||
"character": ("STRING", {"multiline": True}),
|
"character": ("STRING", {"multiline": True,"dynamicPrompts": False}),
|
||||||
"start_index": ("INT", {
|
"start_index": ("INT", {
|
||||||
"default": 1,
|
"default": 1,
|
||||||
"min": 0, #Minimum value
|
"min": 0, #Minimum value
|
||||||
|
|||||||
@@ -0,0 +1,278 @@
|
|||||||
|
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")
|
||||||
|
if not os.path.exists(cache_path):
|
||||||
|
os.mkdir(cache_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)
|
||||||
|
|
||||||
|
|
||||||
|
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,)}
|
||||||
@@ -31,10 +31,20 @@ logger = logging.getLogger('CLIPSeg nodes')
|
|||||||
clipseg_model_dir = os.path.join(folder_paths.models_dir, "clipseg")
|
clipseg_model_dir = os.path.join(folder_paths.models_dir, "clipseg")
|
||||||
|
|
||||||
if not os.path.exists(clipseg_model_dir):
|
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'
|
clipseg_model_dir='CIDAS/clipseg-rd64-refined'
|
||||||
|
|
||||||
"""Helper methods for CLIPSeg nodes"""
|
"""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))
|
||||||
|
|
||||||
|
# Convert PIL to Tensor
|
||||||
|
def pil2tensor(image):
|
||||||
|
return torch.from_numpy(np.array(image).astype(np.float32) / 255.0).unsqueeze(0)
|
||||||
|
|
||||||
|
|
||||||
def tensor_to_numpy(tensor: torch.Tensor) -> np.ndarray:
|
def tensor_to_numpy(tensor: torch.Tensor) -> np.ndarray:
|
||||||
"""Convert a tensor to a numpy array and scale its values to 0-255."""
|
"""Convert a tensor to a numpy array and scale its values to 0-255."""
|
||||||
array = tensor.numpy().squeeze()
|
array = tensor.numpy().squeeze()
|
||||||
@@ -91,23 +101,23 @@ class CLIPSeg:
|
|||||||
return {"required":
|
return {"required":
|
||||||
{
|
{
|
||||||
"image": ("IMAGE",),
|
"image": ("IMAGE",),
|
||||||
"text": ("STRING", {"multiline": False}),
|
"text": ("STRING", {"multiline": False,"dynamicPrompts": False}),
|
||||||
|
|
||||||
},
|
},
|
||||||
"optional":
|
"optional":
|
||||||
{
|
{
|
||||||
"blur": ("FLOAT", {"min": 0, "max": 15, "step": 0.1, "default": 7}),
|
"blur": ("FLOAT", {"min": 0, "max": 15, "step": 0.1, "default": 3}),
|
||||||
"threshold": ("FLOAT", {"min": 0, "max": 1, "step": 0.05, "default": 0.4}),
|
"threshold": ("FLOAT", {"min": 0, "max": 1, "step": 0.05, "default": 0.3}),
|
||||||
"dilation_factor": ("INT", {"min": 0, "max": 10, "step": 1, "default": 4}),
|
"dilation_factor": ("INT", {"min": 0, "max": 10, "step": 1, "default": 4}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/mask"
|
CATEGORY = "♾️Mixlab/Mask"
|
||||||
RETURN_TYPES = ("MASK", "IMAGE", "IMAGE",)
|
RETURN_TYPES = ("MASK", "IMAGE", "IMAGE",)
|
||||||
RETURN_NAMES = ("Mask","Heatmap Mask", "BW Mask")
|
RETURN_NAMES = ("Mask","Heatmap Mask", "BW Mask")
|
||||||
|
|
||||||
# INPUT_IS_LIST = True
|
# INPUT_IS_LIST = True
|
||||||
# OUTPUT_IS_LIST = (True,)
|
OUTPUT_IS_LIST = (False,False,False,)
|
||||||
|
|
||||||
FUNCTION = "segment_image"
|
FUNCTION = "segment_image"
|
||||||
def segment_image(self, image: torch.Tensor, text: str, blur: float, threshold: float, dilation_factor: int) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
def segment_image(self, image: torch.Tensor, text: str, blur: float, threshold: float, dilation_factor: int) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
|
||||||
@@ -180,12 +190,13 @@ class CLIPSeg:
|
|||||||
binary_mask_image = Image.fromarray(binary_mask_resized[..., 0])
|
binary_mask_image = Image.fromarray(binary_mask_resized[..., 0])
|
||||||
|
|
||||||
# convert PIL image to numpy array
|
# convert PIL image to numpy array
|
||||||
tensor_bw = binary_mask_image.convert("RGB")
|
tensor_bw = binary_mask_image.convert("L")
|
||||||
tensor_bw = np.array(tensor_bw).astype(np.float32) / 255.0
|
tensor_bw=pil2tensor(tensor_bw)
|
||||||
tensor_bw = torch.from_numpy(tensor_bw)[None,]
|
# tensor_bw = np.array(tensor_bw).astype(np.float32) / 255.0
|
||||||
tensor_bw = tensor_bw.squeeze(0)[..., 0]
|
# tensor_bw = torch.from_numpy(tensor_bw)[None,]
|
||||||
|
# tensor_bw = tensor_bw.squeeze(0)[..., 0]
|
||||||
|
|
||||||
return tensor_bw, image_out_heatmap, image_out_binary
|
return (tensor_bw, image_out_heatmap, image_out_binary,)
|
||||||
|
|
||||||
#OUTPUT_NODE = False
|
#OUTPUT_NODE = False
|
||||||
|
|
||||||
@@ -207,7 +218,7 @@ class CombineMasks:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/mask"
|
CATEGORY = "♾️Mixlab/Mask"
|
||||||
RETURN_TYPES = ("MASK", "IMAGE", "IMAGE",)
|
RETURN_TYPES = ("MASK", "IMAGE", "IMAGE",)
|
||||||
RETURN_NAMES = ("Combined Mask","Heatmap Mask", "BW Mask")
|
RETURN_NAMES = ("Combined Mask","Heatmap Mask", "BW Mask")
|
||||||
|
|
||||||
@@ -235,7 +246,7 @@ class CombineMasks:
|
|||||||
|
|
||||||
# Resize heatmap and binary mask to match the original image dimensions
|
# Resize heatmap and binary mask to match the original image dimensions
|
||||||
dimensions = (image_np.shape[1], image_np.shape[0])
|
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:
|
if dimensions is None or dimensions[0] == 0 or dimensions[1] == 0:
|
||||||
raise ValueError("Invalid dimensions")
|
raise ValueError("Invalid dimensions")
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
lama_path=os.path.join(folder_paths.models_dir, "lama")
|
||||||
|
if not os.path.exists(lama_path):
|
||||||
|
os.mkdir(lama_path)
|
||||||
|
|
||||||
|
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,)
|
||||||
@@ -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,)
|
||||||
@@ -1,15 +1,40 @@
|
|||||||
import random
|
import random
|
||||||
import comfy.utils
|
import comfy.utils
|
||||||
import json
|
import os
|
||||||
|
import numpy as np
|
||||||
from urllib import request, parse
|
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):
|
def __ne__(self, __value: object) -> bool:
|
||||||
p = {"prompt": prompt_workflow}
|
return False
|
||||||
data = json.dumps(p).encode('utf-8')
|
|
||||||
req = request.Request("http://127.0.0.1:8188/prompt", data=data)
|
any_type = AnyType("*")
|
||||||
request.urlopen(req)
|
|
||||||
|
|
||||||
|
|
||||||
default_prompt1='''Swing
|
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()!=''])
|
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:
|
class RandomPrompt:
|
||||||
|
|
||||||
'''
|
'''
|
||||||
@@ -70,6 +321,10 @@ class RandomPrompt:
|
|||||||
"default": 'sticker, Cartoon, ``'
|
"default": 'sticker, Cartoon, ``'
|
||||||
}),
|
}),
|
||||||
"random_sample": (["enable", "disable"],),
|
"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"
|
FUNCTION = "run"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/prompt"
|
CATEGORY = "♾️Mixlab/Prompt"
|
||||||
|
|
||||||
OUTPUT_IS_LIST = (True,)
|
OUTPUT_IS_LIST = (True,)
|
||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
|
|
||||||
|
|
||||||
# 运行的函数
|
# 运行的函数
|
||||||
def run(self,max_count,mutable_prompt,immutable_prompt,random_sample):
|
def run(self,max_count,mutable_prompt,immutable_prompt,random_sample,seed=0):
|
||||||
print('#运行的函数',mutable_prompt,immutable_prompt,max_count,random_sample)
|
# print('#运行的函数',mutable_prompt,immutable_prompt,max_count,random_sample)
|
||||||
|
|
||||||
# Split the text into an array of words
|
# Split the text into an array of words
|
||||||
words1 = mutable_prompt.split("\n")
|
words1 = mutable_prompt.split("\n")
|
||||||
@@ -106,6 +361,11 @@ class RandomPrompt:
|
|||||||
w1=w1.strip()
|
w1=w1.strip()
|
||||||
for w2 in words2:
|
for w2 in words2:
|
||||||
w2=w2.strip()
|
w2=w2.strip()
|
||||||
|
if '``' not in w2:
|
||||||
|
if w2=="":
|
||||||
|
w2='``'
|
||||||
|
else:
|
||||||
|
w2=w2+',``'
|
||||||
if w1!='' and w2!='':
|
if w1!='' and w2!='':
|
||||||
prompts.append(w2.replace('``', w1))
|
prompts.append(w2.replace('``', w1))
|
||||||
pbar.update(1)
|
pbar.update(1)
|
||||||
@@ -119,6 +379,8 @@ class RandomPrompt:
|
|||||||
else:
|
else:
|
||||||
prompts = prompts[:min(max_count,len(prompts))]
|
prompts = prompts[:min(max_count,len(prompts))]
|
||||||
|
|
||||||
|
prompts= [elem.strip() for elem in prompts if elem.strip()]
|
||||||
|
|
||||||
# return (new_prompt)
|
# return (new_prompt)
|
||||||
return {"ui": {"prompts": prompts}, "result": (prompts,)}
|
return {"ui": {"prompts": prompts}, "result": (prompts,)}
|
||||||
|
|
||||||
@@ -126,62 +388,64 @@ class RandomPrompt:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class RunWorkflow:
|
|
||||||
|
class EmbeddingPrompt:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
return {
|
return {
|
||||||
"required": {
|
"required": {
|
||||||
"workflow": ("STRING", {
|
"embedding":(get_files_with_extension(embeddings_path,'.pt'),),
|
||||||
"multiline": False,
|
"weight": ("FLOAT", {"default": 1, "min": -2, "max": 2,"step":0.01 ,"display": "slider"}),
|
||||||
"default": ''
|
|
||||||
}),
|
|
||||||
"prompt": ("STRING", {
|
|
||||||
"multiline": False,
|
|
||||||
"default": ''
|
|
||||||
}),
|
|
||||||
"image": ("IMAGE",),
|
|
||||||
"input_node": ("STRING", {
|
|
||||||
"multiline": False,
|
|
||||||
"default": ''
|
|
||||||
}),
|
|
||||||
"output_node": ("STRING", {
|
|
||||||
"multiline": False,
|
|
||||||
"default": ''
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
|
||||||
RETURN_TYPES = ("IMAGE","STRING",)
|
|
||||||
|
|
||||||
FUNCTION = "run"
|
FUNCTION = "run"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/workflow"
|
CATEGORY = "♾️Mixlab/Prompt"
|
||||||
|
|
||||||
OUTPUT_IS_LIST = (True,)
|
|
||||||
OUTPUT_NODE = True
|
|
||||||
|
|
||||||
|
OUTPUT_IS_LIST = (False,)
|
||||||
|
# OUTPUT_NODE = True
|
||||||
|
|
||||||
# 运行的函数
|
# 运行的函数
|
||||||
def run(self,workflow,prompt,image,input_node,output_node):
|
def run(self,embedding,weight):
|
||||||
print('#运行的函数',prompt,image,input_node,output_node)
|
weight = round(weight, 3)
|
||||||
workflow=json.loads(workflow)
|
prompt='embedding:'+embedding
|
||||||
input_node=input_node.split(".")
|
if weight!=1:
|
||||||
workflow[input_node[0]][input_node[1]][input_node[2]]=prompt
|
prompt='('+prompt+':'+str(weight)+')'
|
||||||
|
prompt=" "+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]])
|
|
||||||
|
|
||||||
# return (new_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")
|
RETURN_NAMES = ("IMAGE","PROMPT","FLOAT","INT")
|
||||||
FUNCTION = "run"
|
FUNCTION = "run"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/image"
|
CATEGORY = "♾️Mixlab/Image"
|
||||||
|
|
||||||
# INPUT_IS_LIST = True
|
# INPUT_IS_LIST = True
|
||||||
OUTPUT_IS_LIST = (False,False,False,False)
|
OUTPUT_IS_LIST = (False,False,False,False)
|
||||||
@@ -118,7 +118,7 @@ class FloatingVideo:
|
|||||||
OUTPUT_NODE = True
|
OUTPUT_NODE = True
|
||||||
FUNCTION = "run"
|
FUNCTION = "run"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/image"
|
CATEGORY = "♾️Mixlab/Image"
|
||||||
|
|
||||||
# INPUT_IS_LIST = True
|
# INPUT_IS_LIST = True
|
||||||
# OUTPUT_IS_LIST = (False,False,)
|
# 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,)}
|
||||||
@@ -1,9 +1,70 @@
|
|||||||
import os
|
import os,platform
|
||||||
import re,random
|
import re,random,json
|
||||||
|
|
||||||
# FONT_PATH= os.path.abspath(os.path.join(os.path.dirname(__file__),'../assets/王汉宗颜楷体繁.ttf'))
|
# 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
|
||||||
|
|
||||||
import matplotlib.font_manager as fm
|
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
# def get_json_hash(json_content):
|
||||||
|
# json_string = json.dumps(json_content, sort_keys=True)
|
||||||
|
# hash_object = hashlib.sha256(json_string.encode())
|
||||||
|
# hash_value = hash_object.hexdigest()
|
||||||
|
# return hash_value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_font_files(directory):
|
def get_font_files(directory):
|
||||||
font_files = {}
|
font_files = {}
|
||||||
|
|
||||||
@@ -16,14 +77,14 @@ def get_font_files(directory):
|
|||||||
|
|
||||||
# 尝试获取系统字体
|
# 尝试获取系统字体
|
||||||
try:
|
try:
|
||||||
font_paths = fm.findSystemFonts()
|
font_paths = get_system_font_path()
|
||||||
for path in font_paths:
|
for file in font_paths:
|
||||||
try:
|
try:
|
||||||
font_prop = fm.FontProperties(fname=path)
|
font_name = os.path.splitext(file)[0]
|
||||||
font_name = font_prop.get_name()
|
font_path = file
|
||||||
font_files[font_name] = path
|
font_files[font_name] = os.path.abspath(font_path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error processing font {path}: {e}")
|
print(f"Error processing font {file}: {e}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error finding system fonts: {e}")
|
print(f"Error finding system fonts: {e}")
|
||||||
|
|
||||||
@@ -35,6 +96,21 @@ font_files = get_font_files(r_directory)
|
|||||||
# print(font_files)
|
# 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:
|
class ColorInput:
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def INPUT_TYPES(s):
|
||||||
@@ -44,18 +120,23 @@ class ColorInput:
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ("STRING",)
|
RETURN_TYPES = ("STRING","INT","INT","INT","FLOAT",)
|
||||||
# RETURN_NAMES = ("WIDTH","HEIGHT","X","Y",)
|
RETURN_NAMES = ("hex","r","g","b","a",)
|
||||||
|
|
||||||
FUNCTION = "run"
|
FUNCTION = "run"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/utils"
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
INPUT_IS_LIST = False
|
INPUT_IS_LIST = False
|
||||||
OUTPUT_IS_LIST = (False,False,)
|
OUTPUT_IS_LIST = (False,False,False,False,False,)
|
||||||
|
|
||||||
def run(self,color):
|
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,)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -73,13 +154,13 @@ class FontInput:
|
|||||||
|
|
||||||
FUNCTION = "run"
|
FUNCTION = "run"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/utils"
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
INPUT_IS_LIST = False
|
INPUT_IS_LIST = False
|
||||||
OUTPUT_IS_LIST = (False,False,)
|
OUTPUT_IS_LIST = (False,)
|
||||||
|
|
||||||
def run(self,font):
|
def run(self,font):
|
||||||
|
|
||||||
return (font_files[font],)
|
return (font_files[font],)
|
||||||
|
|
||||||
class TextToNumber:
|
class TextToNumber:
|
||||||
@@ -88,14 +169,17 @@ class TextToNumber:
|
|||||||
return {"required": {
|
return {"required": {
|
||||||
"text": ("STRING",{"multiline": False,"default": "1"}),
|
"text": ("STRING",{"multiline": False,"default": "1"}),
|
||||||
"random_number": (["enable", "disable"],),
|
"random_number": (["enable", "disable"],),
|
||||||
"number":("INT", {
|
"max_num":("INT", {
|
||||||
"default": 0,
|
"default": 10,
|
||||||
"min": 0, #Minimum value
|
"min":2, #Minimum value
|
||||||
"max": 10000000000, #Maximum value
|
"max": 10000000000, #Maximum value
|
||||||
"step": 1, #Slider's step
|
"step": 1, #Slider's step
|
||||||
"display": "number" # Cosmetic only: display as "number" or "slider"
|
"display": "number" # Cosmetic only: display as "number" or "slider"
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
"optional":{
|
||||||
|
"seed": (any_type, {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_TYPES = ("INT",)
|
RETURN_TYPES = ("INT",)
|
||||||
@@ -103,12 +187,12 @@ class TextToNumber:
|
|||||||
|
|
||||||
FUNCTION = "run"
|
FUNCTION = "run"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/utils"
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
INPUT_IS_LIST = False
|
INPUT_IS_LIST = False
|
||||||
OUTPUT_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)
|
numbers = re.findall(r'\d+', text)
|
||||||
result=0
|
result=0
|
||||||
@@ -117,9 +201,172 @@ class TextToNumber:
|
|||||||
# print(result)
|
# print(result)
|
||||||
|
|
||||||
if random_number=='enable' and result>0:
|
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,)}
|
return {"ui": {"text": [text],"num":[result]}, "result": (result,)}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class FloatSlider:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"number":("FLOAT", {
|
||||||
|
"default": 0,
|
||||||
|
"min": 0, #Minimum 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"
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("FLOAT",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
|
INPUT_IS_LIST = False
|
||||||
|
OUTPUT_IS_LIST = (False,)
|
||||||
|
|
||||||
|
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:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"number":("INT", {
|
||||||
|
"default": 0,
|
||||||
|
"min": -1, #Minimum value
|
||||||
|
"max": 0xffffffffffffffff,
|
||||||
|
"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"
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("INT",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
|
INPUT_IS_LIST = False
|
||||||
|
OUTPUT_IS_LIST = (False,)
|
||||||
|
|
||||||
|
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:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"numberA":(any_type,),
|
||||||
|
"multiply_by":("FLOAT", {
|
||||||
|
"default": 0,
|
||||||
|
"min": -2, #Minimum value
|
||||||
|
"max": 0xffffffffffffffff,
|
||||||
|
"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"
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("FLOAT","INT",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
|
INPUT_IS_LIST = False
|
||||||
|
OUTPUT_IS_LIST = (False,False,)
|
||||||
|
|
||||||
|
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:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(s):
|
||||||
|
return {"required": {
|
||||||
|
"text": ("STRING",{"multiline": True,"default": ""}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("STRING",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
|
INPUT_IS_LIST = False
|
||||||
|
OUTPUT_IS_LIST = (False,)
|
||||||
|
|
||||||
|
def run(self,text):
|
||||||
|
|
||||||
|
return (text,)
|
||||||
|
|
||||||
# 接收一个值,然后根据字符串或数值长度计算延迟时间,用户可以自定义延迟"字/s",延迟之后将转化
|
# 接收一个值,然后根据字符串或数值长度计算延迟时间,用户可以自定义延迟"字/s",延迟之后将转化
|
||||||
|
|
||||||
import comfy.samplers
|
import comfy.samplers
|
||||||
@@ -135,13 +382,14 @@ class AnyType(str):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
any_type = AnyType("*")
|
any_type = AnyType("*")
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class DynamicDelayProcessor:
|
class DynamicDelayProcessor:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(cls):
|
def INPUT_TYPES(cls):
|
||||||
print("print INPUT_TYPES",cls)
|
# print("print INPUT_TYPES",cls)
|
||||||
return {
|
return {
|
||||||
"required":{
|
"required":{
|
||||||
"delay_seconds":("INT",{
|
"delay_seconds":("INT",{
|
||||||
@@ -152,7 +400,7 @@ class DynamicDelayProcessor:
|
|||||||
},
|
},
|
||||||
"optional":{
|
"optional":{
|
||||||
"any_input":(any_type,),
|
"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?"}),
|
"words_per_seconds":("FLOAT",{ "default":1.50,"min": 0.0,"max": 1000.00,"display":"Chars per second?"}),
|
||||||
"replace_output": (["disable","enable"],),
|
"replace_output": (["disable","enable"],),
|
||||||
"replace_value":("INT",{ "default":-1,"min": 0,"max": 1000000,"display":"Replacement value"})
|
"replace_value":("INT",{ "default":-1,"min": 0,"max": 1000000,"display":"Replacement value"})
|
||||||
@@ -185,7 +433,7 @@ class DynamicDelayProcessor:
|
|||||||
RETURN_TYPES = (any_type,)
|
RETURN_TYPES = (any_type,)
|
||||||
RETURN_NAMES = ('output',)
|
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):
|
def run(self,any_input,delay_seconds,delay_by_text,words_per_seconds,replace_output,replace_value):
|
||||||
# print(f"Delay text:",delay_by_text )
|
# print(f"Delay text:",delay_by_text )
|
||||||
# 获取开始时间戳
|
# 获取开始时间戳
|
||||||
@@ -210,3 +458,283 @@ class DynamicDelayProcessor:
|
|||||||
# 根据 replace_output 决定输出值
|
# 根据 replace_output 决定输出值
|
||||||
return (max(0, replace_value),) if replace_output == "enable" else (any_input,)
|
return (max(0, replace_value),) if replace_output == "enable" else (any_input,)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class SwitchByIndex:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"A":(any_type,),
|
||||||
|
"B":(any_type,),
|
||||||
|
"index":("INT", {
|
||||||
|
"default": -1,
|
||||||
|
"min": -1,
|
||||||
|
"max": 1000,
|
||||||
|
"step": 1,
|
||||||
|
"display": "number"
|
||||||
|
}),
|
||||||
|
"flat": (['off',"on"],),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = (any_type,)
|
||||||
|
RETURN_NAMES = ("C",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
|
INPUT_IS_LIST = True
|
||||||
|
OUTPUT_IS_LIST = (True,)
|
||||||
|
|
||||||
|
def run(self, A,B,index,flat):
|
||||||
|
|
||||||
|
flat=flat[0]
|
||||||
|
|
||||||
|
C=[]
|
||||||
|
index=index[0]
|
||||||
|
|
||||||
|
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,)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LimitNumber:
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"number":(any_type,),
|
||||||
|
"min_value":("INT", {
|
||||||
|
"default": 0,
|
||||||
|
"min": 0,
|
||||||
|
"max": 0xffffffffffffffff,
|
||||||
|
"step": 1,
|
||||||
|
"display": "number"
|
||||||
|
}),
|
||||||
|
"max_value":("INT", {
|
||||||
|
"default": 1,
|
||||||
|
"min": 1,
|
||||||
|
"max": 0xffffffffffffffff,
|
||||||
|
"step": 1,
|
||||||
|
"display": "number"
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = (any_type,)
|
||||||
|
RETURN_NAMES = ("number",)
|
||||||
|
|
||||||
|
FUNCTION = "run"
|
||||||
|
|
||||||
|
CATEGORY = "♾️Mixlab/Utils"
|
||||||
|
|
||||||
|
INPUT_IS_LIST = False
|
||||||
|
OUTPUT_IS_LIST = (False,)
|
||||||
|
|
||||||
|
def run(self, number, min_value, max_value):
|
||||||
|
nn=number
|
||||||
|
|
||||||
|
if isinstance(number, int):
|
||||||
|
min_value=int(min_value)
|
||||||
|
max_value=int(max_value)
|
||||||
|
if isinstance(number, float):
|
||||||
|
min_value=float(min_value)
|
||||||
|
max_value=float(max_value)
|
||||||
|
|
||||||
|
if number < min_value:
|
||||||
|
nn= min_value
|
||||||
|
elif number > max_value:
|
||||||
|
nn= max_value
|
||||||
|
|
||||||
|
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,)
|
||||||
@@ -145,7 +145,7 @@ class VAELoader:
|
|||||||
RETURN_TYPES = ("VAE",)
|
RETURN_TYPES = ("VAE",)
|
||||||
FUNCTION = "load_vae"
|
FUNCTION = "load_vae"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/_test"
|
CATEGORY = "♾️Mixlab/__TEST"
|
||||||
|
|
||||||
#TODO: scale factor?
|
#TODO: scale factor?
|
||||||
def load_vae(self, vae_name):
|
def load_vae(self, vae_name):
|
||||||
@@ -165,7 +165,7 @@ class VAEDecode:
|
|||||||
RETURN_TYPES = ("IMAGE",)
|
RETURN_TYPES = ("IMAGE",)
|
||||||
FUNCTION = "decode"
|
FUNCTION = "decode"
|
||||||
|
|
||||||
CATEGORY = "♾️Mixlab/_test"
|
CATEGORY = "♾️Mixlab/__TEST"
|
||||||
|
|
||||||
def decode(self, vae, samples):
|
def decode(self, vae, samples):
|
||||||
image = vae.decode(samples["samples"].to("cuda:0"))
|
image = vae.decode(samples["samples"].to("cuda:0"))
|
||||||
|
|||||||
@@ -3,4 +3,6 @@ pyOpenSSL
|
|||||||
watchdog
|
watchdog
|
||||||
opencv-python-headless
|
opencv-python-headless
|
||||||
matplotlib
|
matplotlib
|
||||||
openai
|
openai
|
||||||
|
simple-lama-inpainting
|
||||||
|
clip-interrogator==0.6.0
|
||||||
@@ -187,20 +187,25 @@ app.registerExtension({
|
|||||||
},
|
},
|
||||||
async serializeValue (nodeId, widgetIndex) {
|
async serializeValue (nodeId, widgetIndex) {
|
||||||
let d = getLocalData('_mixlab_3d_image')
|
let d = getLocalData('_mixlab_3d_image')
|
||||||
console.log('serializeValue', node)
|
// console.log('serializeValue', node)
|
||||||
if (d && d[node.id]) {
|
if (d && d[node.id]) {
|
||||||
let { url, bg, material } = d[node.id]
|
let { url, bg, material } = d[node.id]
|
||||||
let base64 = await parseImage(url)
|
let data = {}
|
||||||
let bg_base64 = await parseImage(bg)
|
if (url) {
|
||||||
let material_base64 = await parseImage(material)
|
data.image = await parseImage(url)
|
||||||
|
}
|
||||||
|
if (bg) {
|
||||||
|
data.bg_image = await parseImage(bg)
|
||||||
|
if (!data.bg_image.match('data:image/')) {
|
||||||
|
delete data.bg_image
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return JSON.parse(
|
if (material) {
|
||||||
JSON.stringify({
|
data.material = await parseImage(material)
|
||||||
image: base64,
|
}
|
||||||
bg_image: bg_base64,
|
|
||||||
material: material_base64
|
return JSON.parse(JSON.stringify(data))
|
||||||
})
|
|
||||||
)
|
|
||||||
} else {
|
} else {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
@@ -281,6 +286,8 @@ app.registerExtension({
|
|||||||
<div>Material: <select class="material"></select></div>
|
<div>Material: <select class="material"></select></div>
|
||||||
<div>Material: <div class="material_img"> </div></div>
|
<div>Material: <div class="material_img"> </div></div>
|
||||||
<div><button class="bg">BG</button></div>
|
<div><button class="bg">BG</button></div>
|
||||||
|
<div><button class="export">Export GLB</button></div>
|
||||||
|
|
||||||
</div></model-viewer>`
|
</div></model-viewer>`
|
||||||
|
|
||||||
preview.innerHTML = html
|
preview.innerHTML = html
|
||||||
@@ -294,6 +301,7 @@ app.registerExtension({
|
|||||||
const selectMaterial = preview.querySelector('.material')
|
const selectMaterial = preview.querySelector('.material')
|
||||||
const material_img = preview.querySelector('.material_img')
|
const material_img = preview.querySelector('.material_img')
|
||||||
const bg = preview.querySelector('.bg')
|
const bg = preview.querySelector('.bg')
|
||||||
|
const exportGLB = preview.querySelector('.export')
|
||||||
|
|
||||||
if (modelViewerVariants) {
|
if (modelViewerVariants) {
|
||||||
modelViewerVariants.style.width = `${that.size[0] - 24}px`
|
modelViewerVariants.style.width = `${that.size[0] - 24}px`
|
||||||
@@ -341,21 +349,26 @@ app.registerExtension({
|
|||||||
let url = await uploadImage(blob, '.png')
|
let url = await uploadImage(blob, '.png')
|
||||||
// console.log(url)
|
// console.log(url)
|
||||||
|
|
||||||
// 材质贴图
|
|
||||||
let thumbUrl = material_img.getAttribute('src')
|
|
||||||
let tb = await base64ToBlobFromURL(thumbUrl)
|
|
||||||
let tUrl = await uploadImage(tb, '.png')
|
|
||||||
|
|
||||||
// console.log(tUrl)
|
|
||||||
|
|
||||||
let bg_blob = await base64ToBlobFromURL(
|
let bg_blob = await base64ToBlobFromURL(
|
||||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mN88uXrPQAFwwK/6xJ6CQAAAABJRU5ErkJggg=='
|
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mN88uXrPQAFwwK/6xJ6CQAAAABJRU5ErkJggg=='
|
||||||
)
|
)
|
||||||
let url_bg = await uploadImage(bg_blob, '.png')
|
let url_bg = await uploadImage(bg_blob, '.png')
|
||||||
|
// console.log('url_bg',url_bg)
|
||||||
|
|
||||||
if (!dd[that.id])
|
if (!dd[that.id]) {
|
||||||
dd[that.id] = { url, bg: url_bg, material: tUrl }
|
dd[that.id] = { url, bg: url_bg }
|
||||||
dd[that.id] = { ...dd[that.id], url, material: tUrl }
|
} else {
|
||||||
|
dd[that.id] = { ...dd[that.id], url }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 材质贴图
|
||||||
|
let thumbUrl = material_img.getAttribute('src')
|
||||||
|
if (thumbUrl) {
|
||||||
|
let tb = await base64ToBlobFromURL(thumbUrl)
|
||||||
|
let tUrl = await uploadImage(tb, '.png')
|
||||||
|
// console.log('材质贴图', tUrl, thumbUrl)
|
||||||
|
dd[that.id].material = tUrl
|
||||||
|
}
|
||||||
|
|
||||||
setLocalDataOfWin(key, dd)
|
setLocalDataOfWin(key, dd)
|
||||||
}
|
}
|
||||||
@@ -367,11 +380,11 @@ app.registerExtension({
|
|||||||
|
|
||||||
modelViewerVariants.addEventListener('camera-change', startTimer)
|
modelViewerVariants.addEventListener('camera-change', startTimer)
|
||||||
|
|
||||||
select.addEventListener('input', event => {
|
select.addEventListener('input', async event => {
|
||||||
modelViewerVariants.variantName =
|
modelViewerVariants.variantName =
|
||||||
event.target.value === 'default' ? null : event.target.value
|
event.target.value === 'default' ? null : event.target.value
|
||||||
// 材质
|
// 材质
|
||||||
extractMaterial(
|
await extractMaterial(
|
||||||
modelViewerVariants,
|
modelViewerVariants,
|
||||||
selectMaterial,
|
selectMaterial,
|
||||||
material_img
|
material_img
|
||||||
@@ -456,6 +469,15 @@ app.registerExtension({
|
|||||||
input.click()
|
input.click()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
exportGLB.addEventListener('click', async () => {
|
||||||
|
const glTF = await modelViewerVariants.exportScene()
|
||||||
|
const file = new File([glTF], 'export.glb')
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.download = file.name
|
||||||
|
link.href = URL.createObjectURL(file)
|
||||||
|
link.click()
|
||||||
|
})
|
||||||
|
|
||||||
uploadWidget.value = await uploadWidget.serializeValue()
|
uploadWidget.value = await uploadWidget.serializeValue()
|
||||||
|
|
||||||
// 更新尺寸
|
// 更新尺寸
|
||||||
|
|||||||
@@ -0,0 +1,502 @@
|
|||||||
|
import { app } from '../../../scripts/app.js'
|
||||||
|
import { $el } from '../../../scripts/ui.js'
|
||||||
|
import { api } from '../../../scripts/api.js'
|
||||||
|
|
||||||
|
function get_position_style (ctx, widget_width, y, node_height) {
|
||||||
|
const MARGIN = 12 // the margin around the html element
|
||||||
|
|
||||||
|
/* Create a transform that deals with all the scrolling and zooming */
|
||||||
|
const elRect = ctx.canvas.getBoundingClientRect()
|
||||||
|
const transform = new DOMMatrix()
|
||||||
|
.scaleSelf(
|
||||||
|
elRect.width / ctx.canvas.width,
|
||||||
|
elRect.height / ctx.canvas.height
|
||||||
|
)
|
||||||
|
.multiplySelf(ctx.getTransform())
|
||||||
|
.translateSelf(MARGIN, MARGIN + y)
|
||||||
|
|
||||||
|
return {
|
||||||
|
transformOrigin: '0 0',
|
||||||
|
transform: transform,
|
||||||
|
left: `0`,
|
||||||
|
top: `0`,
|
||||||
|
cursor: 'pointer',
|
||||||
|
position: 'absolute',
|
||||||
|
maxWidth: `${widget_width - MARGIN * 2}px`,
|
||||||
|
// maxHeight: `${node_height - MARGIN * 2}px`, // we're assuming we have the whole height of the node
|
||||||
|
width: `${widget_width - MARGIN * 2}px`,
|
||||||
|
// height: `${node_height * 0.3 - MARGIN * 2}px`,
|
||||||
|
// background: '#EEEEEE',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'row',
|
||||||
|
// alignItems: 'center',
|
||||||
|
justifyContent: 'flex-start'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function drawImageToCanvas (imageUrl) {
|
||||||
|
var canvas = document.createElement('canvas')
|
||||||
|
var ctx = canvas.getContext('2d')
|
||||||
|
var img = new Image()
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
img.onload = function () {
|
||||||
|
var scaleFactor = 320 / img.width
|
||||||
|
var canvasWidth = img.width * scaleFactor
|
||||||
|
var canvasHeight = img.height * scaleFactor
|
||||||
|
|
||||||
|
canvas.width = canvasWidth
|
||||||
|
canvas.height = canvasHeight
|
||||||
|
|
||||||
|
ctx.drawImage(img, 0, 0, canvasWidth, canvasHeight)
|
||||||
|
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
img.onerror = function () {
|
||||||
|
reject(new Error('Failed to load image'))
|
||||||
|
}
|
||||||
|
|
||||||
|
img.src = imageUrl
|
||||||
|
})
|
||||||
|
|
||||||
|
var base64 = canvas.toDataURL('image/jpeg')
|
||||||
|
// console.log(base64); // 输出Base64数据
|
||||||
|
return base64
|
||||||
|
// 可以在这里执行其他操作,比如将Base64数据保存到服务器或显示在页面上
|
||||||
|
}
|
||||||
|
|
||||||
|
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 options = []
|
||||||
|
// 模型
|
||||||
|
try {
|
||||||
|
if (node.type === 'CheckpointLoaderSimple') {
|
||||||
|
options = node.widgets.filter(w => w.name === 'ckpt_name')[0]
|
||||||
|
.options.values
|
||||||
|
} else if (node.type === 'LoraLoader') {
|
||||||
|
options = node.widgets.filter(w => w.name === 'lora_name')[0]
|
||||||
|
.options.values
|
||||||
|
}
|
||||||
|
} 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,
|
||||||
|
id,
|
||||||
|
options
|
||||||
|
}
|
||||||
|
// input.push()
|
||||||
|
}
|
||||||
|
if (outputIds.includes(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, seed }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUrl () {
|
||||||
|
let api_host = `${window.location.hostname}:${window.location.port}`
|
||||||
|
let api_base = ''
|
||||||
|
let url = `${window.location.protocol}//${api_host}${api_base}`
|
||||||
|
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()
|
||||||
|
|
||||||
|
const res = await fetch(`${url}/mixlab/workflow`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
data: json,
|
||||||
|
task: 'save_app',
|
||||||
|
filename: json.app.filename,
|
||||||
|
category: json.app.category
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return await res.json()
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadJsonFile (jsonData, fileName = 'mix_app.json') {
|
||||||
|
const dataString = JSON.stringify(jsonData)
|
||||||
|
const blob = new Blob([dataString], { type: 'application/json' })
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = fileName
|
||||||
|
link.click()
|
||||||
|
|
||||||
|
// 释放URL对象
|
||||||
|
setTimeout(() => {
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
const iconData = json[1][0]
|
||||||
|
let { filename, subfolder, type } = iconData
|
||||||
|
let iconUrl = api.apiURL(
|
||||||
|
`/view?filename=${encodeURIComponent(
|
||||||
|
filename
|
||||||
|
)}&type=${type}&subfolder=${subfolder}${app.getPreviewFormatParam()}${app.getRandParam()}`
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
let data = await app.graphToPrompt()
|
||||||
|
|
||||||
|
const { input, output, seed } = extractInputAndOutputData(
|
||||||
|
data.output,
|
||||||
|
inputIds,
|
||||||
|
outputIds
|
||||||
|
)
|
||||||
|
|
||||||
|
data.app = {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
version,
|
||||||
|
input,
|
||||||
|
output,
|
||||||
|
seed, //控制是fixed 还是random
|
||||||
|
share_prefix,
|
||||||
|
link,
|
||||||
|
category,
|
||||||
|
filename: `${name}_${version}.json`
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
data.app.icon = await drawImageToCanvas(iconUrl)
|
||||||
|
} catch (error) {}
|
||||||
|
// console.log(data.app)
|
||||||
|
// let http_workflow = app.graph.serialize()
|
||||||
|
await save_app(data)
|
||||||
|
if (download) {
|
||||||
|
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
|
||||||
|
)}&category=${encodeURIComponent(data.app.category)}`
|
||||||
|
)
|
||||||
|
if (open)
|
||||||
|
window.open(
|
||||||
|
`${getUrl()}/mixlab/app?filename=${encodeURIComponent(
|
||||||
|
data.app.filename
|
||||||
|
)}&category=${encodeURIComponent(data.app.category)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
app.registerExtension({
|
||||||
|
name: 'Mixlab.app.AppInfo',
|
||||||
|
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||||
|
if (nodeType.comfyClass == 'AppInfo') {
|
||||||
|
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||||
|
nodeType.prototype.onNodeCreated = function () {
|
||||||
|
orig_nodeCreated?.apply(this, arguments)
|
||||||
|
// 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',
|
||||||
|
draw (ctx, node, widget_width, y, widget_height) {
|
||||||
|
Object.assign(
|
||||||
|
this.div.style,
|
||||||
|
get_position_style(
|
||||||
|
ctx,
|
||||||
|
widget_width,
|
||||||
|
node.size[1] - widget_height,
|
||||||
|
node.size[1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const style = `
|
||||||
|
flex-direction: row;
|
||||||
|
background-color: var(--comfy-input-bg);
|
||||||
|
border-radius: 8px;
|
||||||
|
border-color: var(--border-color);
|
||||||
|
border-style: solid;
|
||||||
|
color: var(--descrip-text);`
|
||||||
|
|
||||||
|
widget.div = $el('div', {})
|
||||||
|
|
||||||
|
const btn = document.createElement('button')
|
||||||
|
btn.innerText = 'Save & Open'
|
||||||
|
btn.style = style
|
||||||
|
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
// console.log('hahhah')
|
||||||
|
if (window._mixlab_app_json) {
|
||||||
|
save(window._mixlab_app_json)
|
||||||
|
} else {
|
||||||
|
alert('Please run the workflow before saving')
|
||||||
|
// app.queuePrompt(0, 1)
|
||||||
|
this.widgets.filter(w => w.name === 'version')[0].value += 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const download = document.createElement('button')
|
||||||
|
download.innerText = 'Download For App'
|
||||||
|
download.style = style
|
||||||
|
download.style.marginLeft = '12px'
|
||||||
|
|
||||||
|
download.addEventListener('click', () => {
|
||||||
|
// console.log('hahhah')
|
||||||
|
if (window._mixlab_app_json) {
|
||||||
|
save(window._mixlab_app_json, true)
|
||||||
|
} else {
|
||||||
|
alert('Please run the workflow before saving')
|
||||||
|
// app.queuePrompt(0, 1)
|
||||||
|
this.widgets.filter(w => w.name === 'version')[0].value += 1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
document.body.appendChild(widget.div)
|
||||||
|
widget.div.appendChild(btn)
|
||||||
|
widget.div.appendChild(download)
|
||||||
|
|
||||||
|
this.addCustomWidget(widget)
|
||||||
|
|
||||||
|
const onRemoved = this.onRemoved
|
||||||
|
this.onRemoved = () => {
|
||||||
|
widget.div.remove()
|
||||||
|
return onRemoved?.()
|
||||||
|
}
|
||||||
|
|
||||||
|
this.serialize_widgets = true //需要保存参数
|
||||||
|
|
||||||
|
window._mixlab_app_json = null
|
||||||
|
}
|
||||||
|
|
||||||
|
const onExecuted = nodeType.prototype.onExecuted
|
||||||
|
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'),
|
||||||
|
b => (b.style.background = 'yellow')
|
||||||
|
)
|
||||||
|
} 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
app.registerExtension({
|
||||||
|
name: 'Mixlab.app.AppNode',
|
||||||
|
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||||
|
if (nodeType.comfyClass == 'AppNode') {
|
||||||
|
const orig_nodeCreated = nodeType.prototype.onNodeCreated
|
||||||
|
nodeType.prototype.onNodeCreated = function () {
|
||||||
|
orig_nodeCreated?.apply(this, arguments)
|
||||||
|
console.log('#orig_nodeCreated', this)
|
||||||
|
let node=this;
|
||||||
|
|
||||||
|
const name = this.widgets.filter(w => w.name === 'name')[0]
|
||||||
|
|
||||||
|
name.callback = async e => {
|
||||||
|
let es = e.split('/')
|
||||||
|
let filename = '',
|
||||||
|
category = ''
|
||||||
|
if (es.length == 2) {
|
||||||
|
category = es[0]
|
||||||
|
filename = es[1]
|
||||||
|
} else if (es.length === 1) {
|
||||||
|
filename = e
|
||||||
|
}
|
||||||
|
const res = await api.fetchApi('/mixlab/workflow', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
task: 'my_app',
|
||||||
|
filename,
|
||||||
|
category
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
if (res.status !== 200) {
|
||||||
|
throw {
|
||||||
|
response: await res.json()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let result=await res.json()
|
||||||
|
const {app,output:workflow,}=result.data[0].data;
|
||||||
|
let input=app.input
|
||||||
|
console.log(input)
|
||||||
|
|
||||||
|
if(input.length==1){
|
||||||
|
let widget = node.inputs.filter(w => w.name === 'input')[0];
|
||||||
|
let sc=input[0];
|
||||||
|
LGraphCanvas.prototype._createNodeForInput(
|
||||||
|
node, //当前node
|
||||||
|
widget, //当前node里需要自动连线的widget
|
||||||
|
sc.class_type, //作为input的node type
|
||||||
|
'IMAGE' // 作为input的node的outputs的name. the input slot type of the target node
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
this.serialize_widgets = true //需要保存参数
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadedGraphNode (node, app) {}
|
||||||
|
})
|
||||||
@@ -3,7 +3,7 @@ import { app } from '../../../scripts/app.js'
|
|||||||
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
const repoOwner = 'shadowcz007' // 替换为仓库的所有者
|
||||||
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
const repoName = 'comfyui-mixlab-nodes' // 替换为仓库的名称
|
||||||
|
|
||||||
const version = 'v0.4.2'
|
const version = 'v0.15.0'
|
||||||
|
|
||||||
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/releases/latest`)
|
||||||
.then(response => response.json())
|
.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;
|
const onExecuted = nodeType.prototype.onExecuted;
|
||||||
nodeType.prototype.onExecuted = function (message) {
|
nodeType.prototype.onExecuted = function (message) {
|
||||||
onExecuted?.apply(this, arguments);
|
onExecuted?.apply(this, arguments);
|
||||||
|
console.log('##',message.text)
|
||||||
populate.call(this, message.text);
|
populate.call(this, message.text);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -193,6 +193,11 @@ const parseSvg = async svgContent => {
|
|||||||
|
|
||||||
svgWidth = viewBox.width
|
svgWidth = viewBox.width
|
||||||
svgHeight = viewBox.height
|
svgHeight = viewBox.height
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
svgWidth = ~~svgWidth.replace('px', '')
|
||||||
|
svgHeight = ~~svgHeight.replace('px', '')
|
||||||
|
} catch (error) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建一个新的canvas元素
|
// 创建一个新的canvas元素
|
||||||
@@ -356,7 +361,7 @@ app.registerExtension({
|
|||||||
setLocalDataOfWin(key, dd)
|
setLocalDataOfWin(key, dd)
|
||||||
// console.log(this.id, ip.value.trim())
|
// console.log(this.id, ip.value.trim())
|
||||||
|
|
||||||
svgElement.style = `width: 90%;padding: 5%;`
|
svgElement.style = `width: 90%;padding: 5%;height: auto;`
|
||||||
// 将提取的SVG元素显示在页面上
|
// 将提取的SVG元素显示在页面上
|
||||||
|
|
||||||
svgContainer.innerHTML = ''
|
svgContainer.innerHTML = ''
|
||||||
@@ -401,7 +406,9 @@ app.registerExtension({
|
|||||||
|
|
||||||
this.serialize_widgets = true //需要保存参数
|
this.serialize_widgets = true //需要保存参数
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
async loadedGraphNode (node, app) {
|
async loadedGraphNode (node, app) {
|
||||||
// Fires every time a node is constructed
|
// Fires every time a node is constructed
|
||||||
@@ -424,7 +431,7 @@ app.registerExtension({
|
|||||||
let svgStr = await dt.text()
|
let svgStr = await dt.text()
|
||||||
|
|
||||||
const { svgElement, data, image } = await parseSvg(svgStr)
|
const { svgElement, data, image } = await parseSvg(svgStr)
|
||||||
svgElement.style = `width: 90%;padding: 5%;`
|
svgElement.style = `width: 90%;padding: 5%;height:auto`
|
||||||
// 将提取的SVG元素显示在页面上
|
// 将提取的SVG元素显示在页面上
|
||||||
|
|
||||||
widget.div.querySelector('.preview').innerHTML = ''
|
widget.div.querySelector('.preview').innerHTML = ''
|
||||||
@@ -432,7 +439,6 @@ app.registerExtension({
|
|||||||
|
|
||||||
const uploadWidget = node.widgets.filter(w => w.name == 'upload')[0]
|
const uploadWidget = node.widgets.filter(w => w.name == 'upload')[0]
|
||||||
uploadWidget.value = await uploadWidget.serializeValue()
|
uploadWidget.value = await uploadWidget.serializeValue()
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -156,8 +156,34 @@ const parseSvg = async svgContent => {
|
|||||||
return { data, image: base64, svgElement }
|
return { data, image: base64, svgElement }
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setArea (cw, ch, base64, data, fn) {
|
|
||||||
let displayHeight = Math.round(window.screen.availHeight * 0.6)
|
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')
|
let div = document.createElement('div')
|
||||||
div.innerHTML = `
|
div.innerHTML = `
|
||||||
<div id='ml_overlay' style='position: absolute;top:0;background: #251f1fc4;
|
<div id='ml_overlay' style='position: absolute;top:0;background: #251f1fc4;
|
||||||
@@ -170,8 +196,13 @@ async function setArea (cw, ch, base64, data, fn) {
|
|||||||
outline: 2px solid #eaeaea;
|
outline: 2px solid #eaeaea;
|
||||||
box-shadow: 8px 9px 17px #575757;' />
|
box-shadow: 8px 9px 17px #575757;' />
|
||||||
<div id='ml_selection' style='position: absolute;
|
<div id='ml_selection' style='position: absolute;
|
||||||
border: 2px dashed red;
|
border: 2px dashed red;
|
||||||
pointer-events: none;'></div>
|
pointer-events: none;
|
||||||
|
background-image: url("${topBase64}");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
'></div>
|
||||||
|
<div class="mx_close"> X </div>
|
||||||
</div>`
|
</div>`
|
||||||
// document.body.querySelector('#ml_overlay')
|
// document.body.querySelector('#ml_overlay')
|
||||||
document.body.appendChild(div)
|
document.body.appendChild(div)
|
||||||
@@ -181,13 +212,25 @@ async function setArea (cw, ch, base64, data, fn) {
|
|||||||
// canvas.height = ch
|
// canvas.height = ch
|
||||||
|
|
||||||
let img = div.querySelector('#ml_video')
|
let img = div.querySelector('#ml_video')
|
||||||
let overlay = div.querySelector('#ml_overlay')
|
// let overlay = div.querySelector('#ml_overlay')
|
||||||
let selection = div.querySelector('#ml_selection')
|
let selection = div.querySelector('#ml_selection')
|
||||||
|
let close = div.querySelector('.mx_close')
|
||||||
let startX, startY, endX, endY
|
let startX, startY, endX, endY
|
||||||
let start = false
|
let start = false
|
||||||
|
let setDone = false
|
||||||
// Set video source
|
// Set video source
|
||||||
img.src = base64
|
img.src = base64
|
||||||
// canvas.toDataURL();
|
// canvas.toDataURL();
|
||||||
|
close.style = `cursor: pointer;
|
||||||
|
position: fixed;
|
||||||
|
left: 12px;
|
||||||
|
top: 12px;
|
||||||
|
z-index: 99999999;
|
||||||
|
background: black;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 44px;`
|
||||||
|
|
||||||
// init area
|
// init area
|
||||||
// const data = getSetAreaData()
|
// const data = getSetAreaData()
|
||||||
@@ -216,14 +259,37 @@ async function setArea (cw, ch, base64, data, fn) {
|
|||||||
img.addEventListener('mousedown', startSelection)
|
img.addEventListener('mousedown', startSelection)
|
||||||
img.addEventListener('mousemove', updateSelection)
|
img.addEventListener('mousemove', updateSelection)
|
||||||
img.addEventListener('mouseup', endSelection)
|
img.addEventListener('mouseup', endSelection)
|
||||||
overlay.addEventListener('click', remove)
|
|
||||||
|
|
||||||
function remove () {
|
const removeDiv = () => {
|
||||||
overlay.removeEventListener('click', remove)
|
div.remove()
|
||||||
|
close.removeEventListener('click', removeDiv)
|
||||||
img.removeEventListener('mousedown', startSelection)
|
img.removeEventListener('mousedown', startSelection)
|
||||||
img.removeEventListener('mousemove', updateSelection)
|
img.removeEventListener('mousemove', updateSelection)
|
||||||
img.removeEventListener('mouseup', endSelection)
|
img.removeEventListener('mouseup', endSelection)
|
||||||
div.remove()
|
img.removeEventListener('mousedown', setDoneCheck)
|
||||||
|
}
|
||||||
|
close.addEventListener('click', removeDiv)
|
||||||
|
|
||||||
|
const setDoneCheck = event => {
|
||||||
|
console.log(setDone)
|
||||||
|
if (setDone) {
|
||||||
|
img.addEventListener('mousedown', startSelection)
|
||||||
|
img.addEventListener('mousemove', updateSelection)
|
||||||
|
img.addEventListener('mouseup', endSelection)
|
||||||
|
setDone = false
|
||||||
|
start = false
|
||||||
|
startX = event.clientX
|
||||||
|
startY = event.clientY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
img.addEventListener('mousedown', setDoneCheck)
|
||||||
|
|
||||||
|
function remove () {
|
||||||
|
img.removeEventListener('mousedown', startSelection)
|
||||||
|
img.removeEventListener('mousemove', updateSelection)
|
||||||
|
img.removeEventListener('mouseup', endSelection)
|
||||||
|
setDone = true
|
||||||
|
// div.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
function startSelection (event) {
|
function startSelection (event) {
|
||||||
@@ -531,13 +597,31 @@ app.registerExtension({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
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 linkId = this.inputs[3].link
|
||||||
let nodeId = app.graph.links[linkId].origin_id
|
let nodeId = app.graph.links[linkId].origin_id
|
||||||
// console.log(linkId,this.inputs)
|
// console.log(linkId,this.inputs)
|
||||||
let im = app.graph.getNodeById(nodeId).imgs[0]
|
let imgs2=findImages(nodeId)
|
||||||
let src = im.src
|
let im = imgs2[0]
|
||||||
setArea(im.naturalWidth, im.naturalHeight, src, data, updateValue)
|
console.log(topIm,im)
|
||||||
} catch (error) {}
|
// let src = im.src
|
||||||
|
setArea(
|
||||||
|
im.naturalWidth,
|
||||||
|
im.naturalHeight,
|
||||||
|
topIm.src,
|
||||||
|
im.src,
|
||||||
|
data,
|
||||||
|
updateValue
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1331,7 +1331,13 @@ app.registerExtension({
|
|||||||
let w = 360,
|
let w = 360,
|
||||||
s = widget.preview.videoWidth / widget.preview.videoHeight,
|
s = widget.preview.videoWidth / widget.preview.videoHeight,
|
||||||
h = w / s || w
|
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({
|
const pipWindow = await documentPictureInPicture.requestWindow({
|
||||||
width: w,
|
width: w,
|
||||||
@@ -1800,6 +1806,14 @@ const updateUI = node => {
|
|||||||
pw.inputEl.title = `Total of ${prompts.length} prompts`
|
pw.inputEl.title = `Total of ${prompts.length} prompts`
|
||||||
} else {
|
} else {
|
||||||
// 动态添加
|
// 动态添加
|
||||||
|
// console.log('ComfyWidgets',ComfyWidgets.STRING(
|
||||||
|
// node,
|
||||||
|
// 'prompts',
|
||||||
|
// ['STRING', { multiline: true }]
|
||||||
|
// ))
|
||||||
|
|
||||||
|
// ComfyWidgets.STRING(this, "", ["", {default:this.properties.text, multiline: true}], app)
|
||||||
|
|
||||||
const w = ComfyWidgets.STRING(
|
const w = ComfyWidgets.STRING(
|
||||||
node,
|
node,
|
||||||
'prompts',
|
'prompts',
|
||||||
@@ -2085,13 +2099,13 @@ const node = {
|
|||||||
name: 'RandomPrompt',
|
name: 'RandomPrompt',
|
||||||
async init (app) {
|
async init (app) {
|
||||||
// Any initial setup to run as soon as the page loads
|
// 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('/?')) {
|
if (window.location.href.match('/?')) {
|
||||||
const { workflow } = getURLParameters(window.location.href)
|
const { workflow } = getURLParameters(window.location.href)
|
||||||
if (workflow)
|
if (workflow)
|
||||||
get_my_workflow().then(data => {
|
get_my_workflow().then(data => {
|
||||||
console.log('#get_my_workflow', data)
|
// console.log('#get_my_workflow', data)
|
||||||
let my_workflow = data.filter(
|
let my_workflow = data.filter(
|
||||||
d => d.filename == 'my_workflow.json'
|
d => d.filename == 'my_workflow.json'
|
||||||
)[0]
|
)[0]
|
||||||
@@ -2127,10 +2141,15 @@ const node = {
|
|||||||
// }
|
// }
|
||||||
},
|
},
|
||||||
loadedGraphNode (node, app) {
|
loadedGraphNode (node, app) {
|
||||||
// Fires for each node when loading/dragging/etc a workflow json or png
|
if (node.type === 'RandomPrompt') {
|
||||||
// If you break something in the backend and want to patch workflows in the frontend
|
try {
|
||||||
// This is the place to do this
|
let max_count = node.widgets.filter(w => w.name === 'max_count')[0]
|
||||||
// console.log("[logging]", "loaded graph node: ", exportGraph(node.graph));
|
max_count.value = node.widgets_values[0]
|
||||||
|
// console.log('RandomPrompt',max_count,node.widgets_values[0])
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async nodeCreated (node) {
|
async nodeCreated (node) {
|
||||||
if (node.type === 'RandomPrompt') {
|
if (node.type === 'RandomPrompt') {
|
||||||
@@ -2223,7 +2242,7 @@ const node = {
|
|||||||
const r = onExecuted?.apply?.(this, arguments)
|
const r = onExecuted?.apply?.(this, arguments)
|
||||||
|
|
||||||
let prompts = message.prompts
|
let prompts = message.prompts
|
||||||
console.log('executed', message)
|
// console.log('executed', message)
|
||||||
// console.log('#RandomPrompt', this.widgets)
|
// console.log('#RandomPrompt', this.widgets)
|
||||||
const pw = this.widgets.filter(w => w.name === 'prompts')[0]
|
const pw = this.widgets.filter(w => w.name === 'prompts')[0]
|
||||||
|
|
||||||
@@ -2234,7 +2253,7 @@ const node = {
|
|||||||
} else {
|
} else {
|
||||||
// 动态添加
|
// 动态添加
|
||||||
const w = ComfyWidgets.STRING(
|
const w = ComfyWidgets.STRING(
|
||||||
node,
|
this,
|
||||||
'prompts',
|
'prompts',
|
||||||
['STRING', { multiline: true }],
|
['STRING', { multiline: true }],
|
||||||
app
|
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
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ import { app } from '../../../scripts/app.js'
|
|||||||
import { api } from '../../../scripts/api.js'
|
import { api } from '../../../scripts/api.js'
|
||||||
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
import { ComfyWidgets } from '../../../scripts/widgets.js'
|
||||||
import { $el } from '../../../scripts/ui.js'
|
import { $el } from '../../../scripts/ui.js'
|
||||||
import { addValueControlWidget } from "../../../scripts/widgets.js";
|
import { addValueControlWidget } from '../../../scripts/widgets.js'
|
||||||
|
|
||||||
const getLocalData = key => {
|
const getLocalData = key => {
|
||||||
let data = {}
|
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({
|
app.registerExtension({
|
||||||
name: 'Mixlab.utils.Color',
|
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) {
|
async getCustomWidgets (app) {
|
||||||
return {
|
return {
|
||||||
TCOLOR (node, inputName, inputData, app) {
|
TCOLOR (node, inputName, inputData, app) {
|
||||||
@@ -60,8 +99,17 @@ app.registerExtension({
|
|||||||
return [128, 32] // a method to compute the current size of the widget
|
return [128, 32] // a method to compute the current size of the widget
|
||||||
},
|
},
|
||||||
async serializeValue (nodeId, widgetIndex) {
|
async serializeValue (nodeId, widgetIndex) {
|
||||||
let data = getLocalData('_mixlab_utils_color')
|
// let data = getLocalData('_mixlab_utils_color')
|
||||||
return data[node.id] || '#000000'
|
// 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
|
// widget.something = something; // maybe adds stuff to it
|
||||||
@@ -77,7 +125,7 @@ app.registerExtension({
|
|||||||
nodeType.prototype.onNodeCreated = function () {
|
nodeType.prototype.onNodeCreated = function () {
|
||||||
orig_nodeCreated?.apply(this, arguments)
|
orig_nodeCreated?.apply(this, arguments)
|
||||||
|
|
||||||
console.log('Color nodeData', this.widgets)
|
// console.log('Color nodeData', this.widgets)
|
||||||
|
|
||||||
const widget = {
|
const widget = {
|
||||||
type: 'div',
|
type: 'div',
|
||||||
@@ -87,6 +135,7 @@ app.registerExtension({
|
|||||||
this.div.style,
|
this.div.style,
|
||||||
get_position_style(ctx, widget_width, 44, node.size[1])
|
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)
|
document.body.appendChild(widget.div)
|
||||||
|
|
||||||
const inputDiv = (key, placeholder, value) => {
|
const inputDiv = () => {
|
||||||
let div = document.createElement('div')
|
let div = document.createElement('div')
|
||||||
const ip = document.createElement('input')
|
div.id = `color_picker_${this.id}`
|
||||||
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())
|
|
||||||
})
|
|
||||||
return div
|
return div
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,10 +155,85 @@ app.registerExtension({
|
|||||||
|
|
||||||
this.addCustomWidget(widget)
|
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
|
const onRemoved = this.onRemoved
|
||||||
this.onRemoved = () => {
|
this.onRemoved = () => {
|
||||||
inputColor.remove()
|
inputColor.remove()
|
||||||
widget.div.remove()
|
widget.div.remove()
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.pickr.destroyAndRemove()
|
||||||
|
this.pickr = null
|
||||||
|
document.removeEventListener('wheel', handleMouseWheel)
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
return onRemoved?.()
|
return onRemoved?.()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,34 +246,119 @@ app.registerExtension({
|
|||||||
// You can modify widgets/add handlers/etc here
|
// You can modify widgets/add handlers/etc here
|
||||||
|
|
||||||
if (node.type === 'Color') {
|
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')
|
setTimeout(() => node.pickr.setColor(TCOLOR.value || '#000000'), 1000)
|
||||||
|
} catch (error) {}
|
||||||
let id = node.id
|
|
||||||
|
|
||||||
widget.div.querySelector('.Color').value = data[id] || '#000000'
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
app.registerExtension({
|
app.registerExtension({
|
||||||
name: 'Mixlab.utils.TextToNumber',
|
name: 'Mixlab.utils.TextToNumber',
|
||||||
|
|
||||||
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
async beforeRegisterNodeDef (nodeType, nodeData, app) {
|
||||||
if (nodeType.comfyClass == 'TextToNumber') {
|
if (nodeType.comfyClass == 'TextToNumber') {
|
||||||
|
|
||||||
const onExecuted = nodeType.prototype.onExecuted
|
const onExecuted = nodeType.prototype.onExecuted
|
||||||
nodeType.prototype.onExecuted = function (message) {
|
nodeType.prototype.onExecuted = function (message) {
|
||||||
onExecuted?.apply(this, arguments)
|
onExecuted?.apply(this, arguments)
|
||||||
const random_number=this.widgets.filter(w=>w.name==='random_number')[0]
|
const random_number = this.widgets.filter(
|
||||||
if(random_number.value==='enable'){
|
w => w.name === 'random_number'
|
||||||
const n=this.widgets.filter(w=>w.name==='number')[0]
|
)[0]
|
||||||
n.value=message.num[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)
|
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
|
type: inputData[0], // the type, CHEESE
|
||||||
name: inputName, // the name, slice
|
name: inputName, // the name, slice
|
||||||
size: [128, 24], // a default size
|
size: [128, 24], // a default size
|
||||||
draw (ctx, node, width, y) {
|
draw (ctx, node, width, y) {},
|
||||||
// // 绘制文件图标的函数
|
|
||||||
// function drawFileIcon () {
|
|
||||||
// // 清空画布
|
|
||||||
// // ctx.clearRect(0, 0, canvas.width, canvas.height)
|
|
||||||
|
|
||||||
// // 绘制文件外框
|
|
||||||
// ctx.fillStyle = '#000'
|
|
||||||
// ctx.fillRect(5, 5, 40, 40)
|
|
||||||
|
|
||||||
// // 绘制文件夹图标
|
|
||||||
// ctx.fillStyle = '#f00'
|
|
||||||
// ctx.fillRect(10, 15, 30, 20)
|
|
||||||
|
|
||||||
// // 绘制监听符号
|
|
||||||
// ctx.beginPath()
|
|
||||||
// ctx.arc(30, 35, 5, 0, 2 * Math.PI)
|
|
||||||
// ctx.fillStyle = '#00f'
|
|
||||||
// ctx.fill()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 调用绘制函数
|
|
||||||
// drawFileIcon()
|
|
||||||
},
|
|
||||||
computeSize (...args) {
|
computeSize (...args) {
|
||||||
return [128, 24] // a method to compute the current size of the widget
|
return [128, 24] // a method to compute the current size of the widget
|
||||||
},
|
},
|
||||||
@@ -124,11 +101,19 @@ app.registerExtension({
|
|||||||
nodeType.prototype.onNodeCreated = function () {
|
nodeType.prototype.onNodeCreated = function () {
|
||||||
orig_nodeCreated?.apply(this, arguments)
|
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]
|
const watcher = this.widgets.filter(w => w.name == 'watcher')[0]
|
||||||
|
|
||||||
watcher.callback = () => {
|
watcher.callback = () => {
|
||||||
|
console.log('watcher', watcher.value)
|
||||||
if (watcher.value === 'enable') {
|
if (watcher.value === 'enable') {
|
||||||
if (window._mixlab_watcher_t)
|
if (window._mixlab_watcher_t)
|
||||||
clearInterval(window._mixlab_watcher_t)
|
clearInterval(window._mixlab_watcher_t)
|
||||||
@@ -140,7 +125,7 @@ app.registerExtension({
|
|||||||
window._mixlab_file_path_watcher = json.event_type
|
window._mixlab_file_path_watcher = json.event_type
|
||||||
// widget.card.innerText = window._mixlab_file_path_watcher || ''
|
// widget.card.innerText = window._mixlab_file_path_watcher || ''
|
||||||
//运行
|
//运行
|
||||||
document.querySelector('#queue-button').click()
|
// document.querySelector('#queue-button').click()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, 1000)
|
}, 1000)
|
||||||
@@ -162,15 +147,59 @@ app.registerExtension({
|
|||||||
window._mixlab_file_path_watcher = json.event_type
|
window._mixlab_file_path_watcher = json.event_type
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
|
||||||
Add the widget, make sure we clean up nicely, and we do not want to be serialized!
|
|
||||||
*/
|
|
||||||
// this.addCustomWidget(widget)
|
|
||||||
this.onRemoved = function () {
|
this.onRemoved = function () {
|
||||||
// widget.card.remove()
|
// widget.card.remove()
|
||||||
}
|
}
|
||||||
this.serialize_widgets = true
|
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) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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}
|
||||||
@@ -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_node_id": 22,
|
||||||
"last_link_id": 51,
|
"last_link_id": 48,
|
||||||
"nodes": [
|
"nodes": [
|
||||||
{
|
{
|
||||||
"id": 7,
|
"id": 7,
|
||||||
@@ -105,7 +105,7 @@
|
|||||||
{
|
{
|
||||||
"name": "text",
|
"name": "text",
|
||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
"link": 51,
|
"link": 48,
|
||||||
"widget": {
|
"widget": {
|
||||||
"name": "text"
|
"name": "text"
|
||||||
}
|
}
|
||||||
@@ -295,7 +295,7 @@
|
|||||||
"Node name for S&R": "KSampler"
|
"Node name for S&R": "KSampler"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
482859286431021,
|
644769503212755,
|
||||||
"randomize",
|
"randomize",
|
||||||
4,
|
4,
|
||||||
1.6,
|
1.6,
|
||||||
@@ -316,19 +316,47 @@
|
|||||||
"1": 246
|
"1": 246
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"order": 7,
|
"order": 6,
|
||||||
"mode": 0,
|
"mode": 0,
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
"name": "images",
|
"name": "images",
|
||||||
"type": "IMAGE",
|
"type": "IMAGE",
|
||||||
"link": 50
|
"link": 46
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"Node name for S&R": "PreviewImage"
|
"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,
|
"id": 6,
|
||||||
"type": "LoraLoader",
|
"type": "LoraLoader",
|
||||||
@@ -445,13 +473,13 @@
|
|||||||
"1": 58
|
"1": 58
|
||||||
},
|
},
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"order": 6,
|
"order": 7,
|
||||||
"mode": 0,
|
"mode": 0,
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
"name": "image",
|
"name": "image",
|
||||||
"type": "IMAGE",
|
"type": "IMAGE",
|
||||||
"link": 49
|
"link": 47
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"outputs": [
|
"outputs": [
|
||||||
@@ -473,43 +501,15 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 20,
|
"id": 22,
|
||||||
"type": "FloatingVideo",
|
"type": "ScreenShare",
|
||||||
"pos": [
|
"pos": [
|
||||||
1928,
|
-111,
|
||||||
295
|
427
|
||||||
],
|
],
|
||||||
"size": [
|
"size": [
|
||||||
315,
|
315,
|
||||||
58
|
644
|
||||||
],
|
|
||||||
"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
|
|
||||||
],
|
],
|
||||||
"flags": {},
|
"flags": {},
|
||||||
"order": 3,
|
"order": 3,
|
||||||
@@ -519,8 +519,8 @@
|
|||||||
"name": "IMAGE",
|
"name": "IMAGE",
|
||||||
"type": "IMAGE",
|
"type": "IMAGE",
|
||||||
"links": [
|
"links": [
|
||||||
49,
|
46,
|
||||||
50
|
47
|
||||||
],
|
],
|
||||||
"shape": 3,
|
"shape": 3,
|
||||||
"slot_index": 0
|
"slot_index": 0
|
||||||
@@ -529,7 +529,7 @@
|
|||||||
"name": "PROMPT",
|
"name": "PROMPT",
|
||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
"links": [
|
"links": [
|
||||||
51
|
48
|
||||||
],
|
],
|
||||||
"shape": 3,
|
"shape": 3,
|
||||||
"slot_index": 1
|
"slot_index": 1
|
||||||
@@ -552,7 +552,7 @@
|
|||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
null,
|
null,
|
||||||
2018,
|
500,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
@@ -810,24 +810,24 @@
|
|||||||
"CLIP"
|
"CLIP"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
49,
|
46,
|
||||||
23,
|
22,
|
||||||
0,
|
|
||||||
18,
|
|
||||||
0,
|
|
||||||
"IMAGE"
|
|
||||||
],
|
|
||||||
[
|
|
||||||
50,
|
|
||||||
23,
|
|
||||||
0,
|
0,
|
||||||
2,
|
2,
|
||||||
0,
|
0,
|
||||||
"IMAGE"
|
"IMAGE"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
51,
|
47,
|
||||||
23,
|
22,
|
||||||
|
0,
|
||||||
|
18,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
48,
|
||||||
|
22,
|
||||||
1,
|
1,
|
||||||
8,
|
8,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -0,0 +1,719 @@
|
|||||||
|
{
|
||||||
|
"last_node_id": 26,
|
||||||
|
"last_link_id": 34,
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
1029,
|
||||||
|
-2149
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 425.27801513671875,
|
||||||
|
"1": 180.6060791015625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 7,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 6
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
3
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "负向prompt",
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"text, watermark"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"type": "EnhanceImage",
|
||||||
|
"pos": [
|
||||||
|
2591,
|
||||||
|
-2531
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 58
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 12,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "image",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 34
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "IMAGE",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"links": [
|
||||||
|
18
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "EnhanceImage"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
1.1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 24,
|
||||||
|
"type": "LimitNumber",
|
||||||
|
"pos": [
|
||||||
|
665,
|
||||||
|
-2958
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 82
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 4,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "number",
|
||||||
|
"type": "*",
|
||||||
|
"link": 29
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "number",
|
||||||
|
"type": "*",
|
||||||
|
"links": [
|
||||||
|
30
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "LimitNumber"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
512,
|
||||||
|
4089
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 25,
|
||||||
|
"type": "LimitNumber",
|
||||||
|
"pos": [
|
||||||
|
648,
|
||||||
|
-2659
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 82
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 5,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "number",
|
||||||
|
"type": "*",
|
||||||
|
"link": 31
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "number",
|
||||||
|
"type": "*",
|
||||||
|
"links": [
|
||||||
|
32
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "LimitNumber"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
512,
|
||||||
|
4089
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"type": "IntNumber",
|
||||||
|
"pos": [
|
||||||
|
302,
|
||||||
|
-2758
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 130
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 0,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "INT",
|
||||||
|
"type": "INT",
|
||||||
|
"links": [
|
||||||
|
29
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Width",
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "IntNumber"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"type": "IntNumber",
|
||||||
|
"pos": [
|
||||||
|
299,
|
||||||
|
-2601
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 130
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 1,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "INT",
|
||||||
|
"type": "INT",
|
||||||
|
"links": [
|
||||||
|
31
|
||||||
|
],
|
||||||
|
"shape": 3,
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Height",
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "IntNumber"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"type": "CheckpointLoaderSimple",
|
||||||
|
"pos": [
|
||||||
|
567,
|
||||||
|
-2422
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 98
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 2,
|
||||||
|
"mode": 0,
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "MODEL",
|
||||||
|
"type": "MODEL",
|
||||||
|
"links": [
|
||||||
|
1
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CLIP",
|
||||||
|
"type": "CLIP",
|
||||||
|
"links": [
|
||||||
|
5,
|
||||||
|
6
|
||||||
|
],
|
||||||
|
"slot_index": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "VAE",
|
||||||
|
"type": "VAE",
|
||||||
|
"links": [
|
||||||
|
8
|
||||||
|
],
|
||||||
|
"slot_index": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Model",
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CheckpointLoaderSimple"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"deliberate_v2.safetensors"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"type": "KSampler",
|
||||||
|
"pos": [
|
||||||
|
1509,
|
||||||
|
-2394
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 315,
|
||||||
|
"1": 262
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 9,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "model",
|
||||||
|
"type": "MODEL",
|
||||||
|
"link": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "positive",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "negative",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"link": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "latent_image",
|
||||||
|
"type": "LATENT",
|
||||||
|
"link": 26,
|
||||||
|
"slot_index": 3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "denoise",
|
||||||
|
"type": "FLOAT",
|
||||||
|
"link": 16,
|
||||||
|
"widget": {
|
||||||
|
"name": "denoise"
|
||||||
|
},
|
||||||
|
"slot_index": 4
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "LATENT",
|
||||||
|
"type": "LATENT",
|
||||||
|
"links": [
|
||||||
|
7
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "KSampler"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
52336089190516,
|
||||||
|
"randomize",
|
||||||
|
15,
|
||||||
|
6.9,
|
||||||
|
"euler",
|
||||||
|
"karras",
|
||||||
|
0.59
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"type": "PreviewImage",
|
||||||
|
"pos": [
|
||||||
|
2949,
|
||||||
|
-2615
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 210,
|
||||||
|
"1": 246
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 13,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "images",
|
||||||
|
"type": "IMAGE",
|
||||||
|
"link": 18
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "PreviewImage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"type": "CLIPTextEncode",
|
||||||
|
"pos": [
|
||||||
|
1022,
|
||||||
|
-2375
|
||||||
|
],
|
||||||
|
"size": {
|
||||||
|
"0": 422.84503173828125,
|
||||||
|
"1": 164.31304931640625
|
||||||
|
},
|
||||||
|
"flags": {},
|
||||||
|
"order": 6,
|
||||||
|
"mode": 0,
|
||||||
|
"inputs": [
|
||||||
|
{
|
||||||
|
"name": "clip",
|
||||||
|
"type": "CLIP",
|
||||||
|
"link": 5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"name": "CONDITIONING",
|
||||||
|
"type": "CONDITIONING",
|
||||||
|
"links": [
|
||||||
|
2
|
||||||
|
],
|
||||||
|
"slot_index": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "prompt",
|
||||||
|
"properties": {
|
||||||
|
"Node name for S&R": "CLIPTextEncode"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"superman,fat cat"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"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": [
|
||||||
|
33,
|
||||||
|
34
|
||||||
|
],
|
||||||
|
"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"
|
||||||
|
},
|
||||||
|
"widgets_values": [
|
||||||
|
"Text-to-Image",
|
||||||
|
"4\n22\n23\n2\n\n\n",
|
||||||
|
"16",
|
||||||
|
"演示基本的文生图流程",
|
||||||
|
1,
|
||||||
|
"#comfyui-mixlab-nodes# ",
|
||||||
|
"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
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"links": [
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
"MODEL"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
2,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
3,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
"CONDITIONING"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
5,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
6,
|
||||||
|
2,
|
||||||
|
1,
|
||||||
|
5,
|
||||||
|
0,
|
||||||
|
"CLIP"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
7,
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
8,
|
||||||
|
2,
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
1,
|
||||||
|
"VAE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
16,
|
||||||
|
14,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
4,
|
||||||
|
"FLOAT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
18,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
16,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
26,
|
||||||
|
21,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
3,
|
||||||
|
"LATENT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
29,
|
||||||
|
22,
|
||||||
|
0,
|
||||||
|
24,
|
||||||
|
0,
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
30,
|
||||||
|
24,
|
||||||
|
0,
|
||||||
|
21,
|
||||||
|
0,
|
||||||
|
"INT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
31,
|
||||||
|
23,
|
||||||
|
0,
|
||||||
|
25,
|
||||||
|
0,
|
||||||
|
"*"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
32,
|
||||||
|
25,
|
||||||
|
0,
|
||||||
|
21,
|
||||||
|
1,
|
||||||
|
"INT"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
33,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
26,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
34,
|
||||||
|
6,
|
||||||
|
0,
|
||||||
|
15,
|
||||||
|
0,
|
||||||
|
"IMAGE"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"groups": [],
|
||||||
|
"config": {},
|
||||||
|
"extra": {},
|
||||||
|
"version": 0.4
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 2.5 MiB |
@@ -42,7 +42,7 @@
|
|||||||
"Node name for S&R": "SpeechSynthesis"
|
"Node name for S&R": "SpeechSynthesis"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"如果您想购买工程车,您需要考虑以下几个因素:\n\n1. 预算:确定您有多少钱可以用来购买工程车。\n\n2. 用途:确定您购买工程车的目的,这将帮助您选择合适的车型和配置。\n\n3. 品牌和型号:选择一个知名品牌和适合您需求的型号。\n\n4. 购买地点:考虑在哪个地区购买,这将影响价格和可用性。\n\n5. 保养和维修:考虑车辆的保养和维修成本,以确保购买后的维护成本合理。\n\n请告诉我您需要更多的哪方面的信息,我将能更好地帮助您。"
|
"作为一个人工智能助手,我没有性别,也无法进行社交互动。我的主要任务是为用户提供帮助和信息。请问有什么问题我可以为您解答吗?"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -133,10 +133,10 @@
|
|||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
"买",
|
"跟你一样泡妞",
|
||||||
"You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.",
|
"You are ChatGPT, a large language model trained by OpenAI. Answer as concisely as possible.",
|
||||||
"gpt-3.5-turbo",
|
"gpt-3.5-turbo",
|
||||||
8034,
|
354,
|
||||||
"randomize",
|
"randomize",
|
||||||
1,
|
1,
|
||||||
null
|
null
|
||||||
@@ -178,7 +178,7 @@
|
|||||||
"Node name for S&R": "ShowTextForGPT"
|
"Node name for S&R": "ShowTextForGPT"
|
||||||
},
|
},
|
||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
"工程车子什么"
|
"纽斯"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
"widgets_values": [
|
"widgets_values": [
|
||||||
1,
|
1,
|
||||||
"",
|
"",
|
||||||
0.5,
|
2.5,
|
||||||
"enable",
|
"enable",
|
||||||
1
|
1
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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 |
@@ -1,2 +0,0 @@
|
|||||||

|
|
||||||

|
|
||||||
|
|||||||