29 Commits
Author SHA1 Message Date
yemiancheng 03dd9b0001 docs(core): put usage 2025-06-09 16:05:21 +08:00
yemiancheng 406a6723ef docs(core): put note 2025-06-05 15:15:20 +08:00
yemiancheng 2e0519590c docs(core): put note 2025-06-05 15:12:04 +08:00
yemiancheng 0d4bc73cfd build(core): use category in itself 2025-06-05 15:11:57 +08:00
yemiancheng c4b6c0fb46 build(core): use cutoff as filename 2025-06-05 15:10:28 +08:00
yemiancheng 703ff3e07c feat(core): use suite/text/region 2025-06-05 15:08:34 +08:00
yemiancheng ea3b1a893c feat(core): use suite/text/io 2025-06-05 15:07:26 +08:00
yemiancheng 634bc4c8a8 feat(core): use suite/text/plain 2025-06-05 15:02:31 +08:00
yemiancheng 04e306cf87 feat(core): rename category for reading 2025-06-05 14:58:56 +08:00
yemiancheng 155d3da12d build(core): check node in comfy 2025-06-05 14:27:55 +08:00
yemiancheng 62539f80be build(core): add publish to comfy workflow 2025-06-05 13:59:23 +08:00
yemiancheng 73da0b966f docs(core): put note 2025-06-05 13:46:06 +08:00
yemiancheng 63a46a8e71 chore(core): gen feat of from source code 2025-06-05 13:44:46 +08:00
yemiancheng d05cd5a605 build(core): use shotscreen 2025-06-05 13:39:18 +08:00
yemiancheng 6f8729c9f5 refactor(core): put dirs and category 2025-06-05 13:23:16 +08:00
yemiancheng 0cc642dd60 build(core): use node setup helper 2025-06-03 20:55:27 +08:00
yemiancheng 8b399da4c3 build(core): use node setup helper 2025-06-03 20:54:33 +08:00
yemiancheng a4e03cc8a2 build(core): comment unused package 2025-06-03 20:21:43 +08:00
yemiancheng dd6b847298 build(core): use lf as eof 2025-06-03 20:19:45 +08:00
yemiancheng 45d7a61241 refactor(core): rename files 2025-06-03 20:18:07 +08:00
yemiancheng 853e00fe82 docs(core): put readme to note git for this repo 2023-12-27 21:44:37 +08:00
yemiancheng b8173975ba docs(core): put readme to readable 2023-12-27 21:44:29 +08:00
yemiancheng 2986bb3f82 docs(core): put readme to note git for this repo 2023-12-27 21:41:24 +08:00
yemiancheng fb59959bef docs(core): put readme to readable 2023-12-27 21:41:20 +08:00
yemiancheng 499886dbab docs(core): put readme to note git for this repo 2023-12-27 21:39:59 +08:00
yemiancheng 6fe090cc9c docs(core): put readme to note git for this repo 2023-12-27 21:37:57 +08:00
yemiancheng 44d9b6672d docs(core): put readme to note git for this repo 2023-12-27 21:32:16 +08:00
yemiancheng c91e903cda docs(core): add changelog info 2023-12-27 21:31:27 +08:00
yemiancheng 5c68912153 feat(core): text save - rename node name for issue 3 2023-12-27 20:05:38 +08:00
25 changed files with 2269 additions and 2386 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ root = true
[*]
indent_style = space
indent_size = 4
end_of_line = crlf
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false
+69
View File
@@ -0,0 +1,69 @@
# s: set *.js and other as js file for js project
# disable other with linguist-detectable for ps1
*.ps1 linguist-language=false
*.md linguist-detectable=false
*.py linguist-detectable=true
*.html linguist-detectable=false
*.js linguist-detectable=false
*.css linguist-detectable=false
# s: set *.js and other as js file for js project
# refer:
# https://huggingface.co/guoyww/animatediff/blob/main/.gitattributes
# https://huggingface.co/datasets/hdparmar/itt_specdata/blob/main/.gitattributes
# https://github.com/Richienb/git-files/blob/master/files/.gitattributes
*.7z filter=lfs diff=lfs merge=lfs -text
*.arrow filter=lfs diff=lfs merge=lfs -text
*.bin filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.ckpt filter=lfs diff=lfs merge=lfs -text
*.ftz filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.h5 filter=lfs diff=lfs merge=lfs -text
*.joblib filter=lfs diff=lfs merge=lfs -text
*.lfs.* filter=lfs diff=lfs merge=lfs -text
*.mlmodel filter=lfs diff=lfs merge=lfs -text
*.model filter=lfs diff=lfs merge=lfs -text
*.msgpack filter=lfs diff=lfs merge=lfs -text
*.npy filter=lfs diff=lfs merge=lfs -text
*.npz filter=lfs diff=lfs merge=lfs -text
*.onnx filter=lfs diff=lfs merge=lfs -text
*.ot filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.pb filter=lfs diff=lfs merge=lfs -text
*.pickle filter=lfs diff=lfs merge=lfs -text
*.pkl filter=lfs diff=lfs merge=lfs -text
*.pt filter=lfs diff=lfs merge=lfs -text
*.pth filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.safetensors filter=lfs diff=lfs merge=lfs -text
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
*.tar.* filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.tflite filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.wasm filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.zst filter=lfs diff=lfs merge=lfs -text
*tfevents* filter=lfs diff=lfs merge=lfs -text
# Audio files - uncompressed
*.pcm filter=lfs diff=lfs merge=lfs -text
*.sam filter=lfs diff=lfs merge=lfs -text
*.raw filter=lfs diff=lfs merge=lfs -text
# Audio files - compressed
*.aac filter=lfs diff=lfs merge=lfs -text
*.flac filter=lfs diff=lfs merge=lfs -text
*.mp3 filter=lfs diff=lfs merge=lfs -text
*.ogg filter=lfs diff=lfs merge=lfs -text
*.wav filter=lfs diff=lfs merge=lfs -text
# Image files - uncompressed
*.bmp filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.tiff filter=lfs diff=lfs merge=lfs -text
# Image files - compressed
*.jpg filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
+32
View File
@@ -0,0 +1,32 @@
name: Publish to Comfy registry
on:
workflow_dispatch:
push:
branches:
- main
paths:
- "pyproject.toml"
# only run when tags are pushed
# tags:
# - '*'
jobs:
publish-node:
name: Publish Custom Node to registry
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v4
- name: Check if Node Exists in Registry
run: |
curl -s -X GET "https://registry.comfy.org/nodes/ymc-node-suite-comfyui" -H "Authorization: Bearer ${{ secrets.COMFY_REGISTRY_TOKEN }}" \
| jq '.claimed_by // empty' > claimed_status.txt
if [ -s claimed_status.txt ]; then
echo "Node already exists and is claimed. Skipping publish step."
exit 0
fi
- name: Publish Custom Node
uses: Comfy-Org/publish-node-action@main
with:
## Add your own personal access token to your Github Repository secrets and reference it here.
personal_access_token: ${{ secrets.COMFY_REGISTRY_TOKEN }}
+37
View File
@@ -0,0 +1,37 @@
- feat(core): NodePlainTextList - input text preset
- feat(core): NodePlainTextListAdv - input text preset - adv
- feat(core): NodePlainTextListJoinTextAdv - join text to text preset - adv
- feat(core): NodePlainTextJoin - join text
- feat(core): NodePlainTextRandom - random text in list
- feat(core): NodePlainTextLoop - get text in list (loop)
- feat(core): NodePlainTextReplace - replace text
- feat(core): do nothing when action no
- feat(core): replace text
- feat(core): NodeNumStrToNum - text to number
- feat(core): NodeNumStrToBool - text to bool
- feat(core): NodePlainTextSwitchWithBool - text switch with bool
- feat(core): NodePlainTextSwitchWithText - text switch with text
- feat(core): NodeTextShow - show text
- feat(core): NodeTextCalcSize - cacluate width and height
- feat(core): NodePromptAdd - prompt add some text
- feat(core): NodePromptAdvAdd - prompt add some text (adv)
- feat(core): NodePromptSearch - prompt search some text
- feat(core): NodePromptDup - prompt dup some text
- feat(core): NodePromptAdvDup - prompt dup some text (adv)
- feat(core): NodePromptShuffle - prompt shuffle some text
- feat(core): NodePromptDel - prompt del some text
- feat(core): NodePromptStd - prompt std
- feat(core): NodePromptInput - prompt input
- feat(core): NodePromptUnweight - prompt unweight
- feat(core): NodePromptJoin - prompt join
- feat(core): NodePromptRandom - prompt random
- feat(core): NodePromptWeight - prompt add weight
- feat(core): NodePyioFilenameListDetect - pyio - detect filename list
- feat(core): NodePyioFilenameListOutmDetect - pyio - detect filename list (outm)
- feat(core): IoUtilFileListGetText - io - get file list and read as text
- feat(core): NodeRegionCropLocationBurnNumz - num - burn crop location (center & size)
- feat(core): NodeRegionCropLocationBurnTxtz - txt - burn crop location (center & size)
- feat(core): NodeRegionCropLocationBurnLtwh - int - burn crop location (lt & size)
- feat(core): NodeRegionPaddingSquareLocationBurn - int - burn padding location as square (size)
- feat(core): NodeCutoffRegionTextBurn - cutoff - region text burn
- feat(core): NodeCutoffRegionColorTextPreset - cutoff - color preset of region text
-239
View File
@@ -1,239 +0,0 @@
## basic do
- [x] set git default branch
```bash
git config --global init.defaultBranch main
```
- [x] ini git repo
```bash
[ ! -e .git ] && git init;
```
- [x] set user name and email
```bash
git config --global user.name "yemiancheng" ;git config --global user.email "ymc.github@gmail.com";
```
- [ ] change the master bransh to "main" from "master"
```bash
git branch -M main
```
- [x] add github repo (in plan)
```bash
# copy token files to current project
# cp /d/code/shell/github-cli/secrets/* ./secrets
workspace=$(pwd);
cd /d/code/shell/github-cli;shfile=./index.sh;
repourl="ymc-github/ymc-node-suite-comfyui";repodesc="custom nodes for comfyui,like AI painting in comfyui";
# add repo with default desc
$shfile $repourl add # (in plan)
# put repo desc
# $shfile "$repourl" put "$repodesc" # (process)
# $shfile $repourl del
cd "$workspace"
```
- [x] add remote repo url
```bash
# git remote add origin https://github.com/YMC-GitHub/ymc-node-suite-comfyui.git
git remote add origin git@github.com:YMC-GitHub/ymc-node-suite-comfyui.git
```
- [x] set gitignore and commit it
```bash
git add .gitignore;git commit -m "build(core): add gitignore";
git add .gitignore;git commit -m "build(core): set note for go allow list";
git add .gitignore;git commit -m "build(core): put gitignore to design project dirs";
```
[fefer: some gitignore template](https://github.com/github/gitignore)
- [x] set edifotconfig and commit it
```bash
git add .editorconfig ; git commit -m "build(core): add editor config";
```
- [x] set license and commit it
```bash
git add LICENSE ; git commit -m "build(core): add license";
```
- [x] set readmd and commit it
```bash
git add README.md ; git commit -m "docs(core): add readme";
```
- [ ] get time with format
```bash
format="+%Y-%m-%d %H:%M:%S";time="2021-05-18 13:12:19";date -d "$time" "$format";
```
- [ ] git commit files with custom date
```bash
git commit -m "chore(core): update readme in github action" --date "$(date "+%Y-%m-%d %H:%M:%S" -d "+8 hour") +0800"
```
- [ ] design project dirs
```bash
git add release_win/.gitignore;git commit -m "build(core): add gitignore to design project dirs";
git add release_win/README.md;git commit -m "docs(core): output *.exe files here";
git add release_win/*.ico;git commit -m "build(core): output icon files here";
git add icons/.gitignore;git commit -m "build(core): add gitignore to design project dirs";
git add icons/README.md;git commit -m "docs(core): put icon files here as resources";
git add icons/*.ico;git commit -m "build(core): add resources files icon";
git add .vscode/.gitignore;git commit -m "build(core): add gitignore to design project dirs";
git add .vscode/README.md;git commit -m "docs(core): put vscode editor config files of project level here";
```
- [ ] add docs about how to setup this
```bash
git add README.01-SETUP.md ; git commit -m "docs(core): add readme to setup this";
```
- [ ] add docs about how to code before coding
```bash
git add README.02-BEFORE-CODE.md ; git commit -m "docs(core): add readme to code this";
```
- [ ] add docs about some libs i noted
```bash
git add README.03-LIB-NOTE.md ; git commit -m "docs(core): add readme to note libs";
```
- [ ] add go code snippets in project level
```bash
git add .vscode/.gitignore;git commit -m "build(core): add go code snippets in project level";
git add .vscode/go.code-snippets;git commit -m "docs(core): add go code snippets";
```
- [ ] add hello world in root dirs
```bash
git add main.go;git commit -m "build(core): add hello world in root dirs";
```
- [ ] add go mod and workspace
```bash
git add go.mod go.sum go.work.sum;git commit -m "build(core): add mod and workspace";
```
- [ ] add mod chapter01
```bash
git add src/chapter01/higo;git commit -m "build(core): add mod chapter01";
```
- [x] push to github repo (the first)
```bash
git push -u origin main
```
- [ ] replace git protocol with https when failed
```bash
# set
git config --global url."https://".insteadof git://
# unset
# git config --global --unset url."https://".insteadof
```
- [x] rename github repo remote name from "origin" to "github"
```bash
git remote rename origin github
```
- [ ] remove github repo remote
```bash
git remote remove github
```
- [ ] set remote repo url from https to git
```bash
git remote set-url origin git@github.com:YMC-GitHub/ymc-node-suite-comfyui.git
# git remote set-url github git@github.com:YMC-GitHub/ymc-node-suite-comfyui.git
```
- [x] speedup github for `git clone`
```bash
# set
git config --global url."https://ghproxy.com/https://github.com".insteadOf "https://github.com"
# unset
# git config --global --unset url."https://ghproxy.com/https://github.com".insteadOf
# get
# git config --global url."https://ghproxy.com/https://github.com".insteadOf
```
- [x] unset speedup github for `git clone` when pushing
```bash
# unset
git config --global --unset url."https://ghproxy.com/https://github.com".insteadOf
```
- [x] mgnt git credential
```powershell
# git config --global credential.helper manager
# git config --global credential.helper cache
# git config --global --unset credential.helper
# https://zhuanlan.zhihu.com/p/157751660
```
- [x] add docs about using git for this repo i noted
```bash
git add README.04-GIT.md ; git commit -m "docs(core): add readme to note git for this repo";
git add README.04-GIT.md ; git commit -m "docs(core): put readme to note git for this repo";
```
- [ ] add docs about using ssh in github i noted
```bash
git add README.05-SSH.md ; git commit -m "docs(core): add readme to note ssh";
```
- [x] set pull action for this repo
```bash
# zero:knowledge:s:pull-action
# you must know .
# git config pull.rebase false # merge
git config pull.rebase true # rebase
# git config pull.ff only # fast-forward only
# zero:knowledge:e:pull-action
```
- [x] keep workspace clean before pulling
```bash
# git stash
```
- [x] pull remote github repo before pushing again
```bash
git pull origin main
# git pull github main
```
- [x] push remote github repo (no the first time)
```bash
git push origin main
```
- [x] add requirements
```bash
git add requirements.txt ; git commit -m "build(core): add requirements nodes";
```
- [x] add current custom nodes
```bash
git add __init__.py YMC_Node_Suite.py; git commit -m "feat(core): add custom nodes";
```
+61
View File
@@ -2,6 +2,45 @@
ymc 's nodes for comfyui
## Usage
- you can find it in search box : double click + typing keywords( eg. `ymc suite` ,`text` ,`cutoff`,`region`)
- you can find it in right mouse menu : `ymc suite`
- ~~you can find it in right mouse menu : `ymc/utils` (as alias)~~
## Nodes
- nodes show in console:
<div style="text-align: center;">
<img src="./shotscreen/nodes.console.png" alt="console" width="256">
<!-- <img src="./shotscreen/nodes.right.menu.png" alt="right mouse menu" width="256"> -->
</div>
- nodes show in right mouse menu:
<div style="text-align: center;">
<!-- <img src="./shotscreen/nodes.console.png" alt="console" width="256"> -->
<img src="./shotscreen/nodes.right.menu.png" alt="right mouse menu" width="256">
</div>
## Based-on
- pypi package [yors_comfyui_node_setup](https://pypi.org/project/yors_comfyui_node_setup/) - setup comfyui custom nodes easily
- pypi package [yors_pano_ansi_color](https://pypi.org/project/yors_pano_ansi_color/) - info msg in console with color in your comfyui custom nodes easily
- pypi package [yors_pano_zero_field](https://pypi.org/project/yors_pano_zero_field/) - set nodes input field to be HQ in your comfyui custom nodes easily
- pypi package [yors_pano_list_util](https://pypi.org/project/yors_pano_list_util/) - enjoy python list for python starter in your comfyui custom nodes easily.
## Published to Comfy registry
- get more details in [publish_to_comfy.yml](.github/workflows/publish_to_comfy.yml)
- [docs for publishing to comfy registey](https://docs.comfy.org/registry/overview)
- installed with comfy-cli ? ~~`comfy node registry-install ymc-node-suite-comfyui` ~~ (409,Unclaimed)
## Author
name|email|desciption
@@ -12,3 +51,25 @@ WASasquatch|<jordanslost@gmail.com>|refer to its code [was-node-suite-comfyui](h
## License
MIT
## Changelog
### 3.0.0
- [x] refator(core): rename project dirs and all nodes name for hight qulity nodes.
### 2.0.0
- [x] feat(core): rename 'Save Text File' to io-text-save for issue 3
### 1.1.0
- [x] feat(core): add feat that noted in issue 1
### 1.0.0
~~get more on [changelog of feat](./CHANGELOG.FEAT.md)~~
## todo
todo: add details of feat in markdown file and show node list in image
-2125
View File
File diff suppressed because it is too large Load Diff
+70 -2
View File
@@ -1,3 +1,71 @@
from .YMC_Node_Suite import NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS
# v1
# from yors_comfyui_node_setup import entry,node_install_requirements # global
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
# # install requirements
# node_install_requirements(__file__)
# # export comfyui node vars
# __all__,NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,NODE_MENU_NAMES = entry(__name__,__file__)
# v4
import os
from yors_comfyui_node_setup import custom_nodes_get_path,custom_nodes_add_to_sys,import_custom_node_module,node_install_requirements,get_module_name_contains_x_in_sys,get_node_class_in_sys_modules,register_node_list_local
from yors_pano_ansi_color import info_status,info_step,msg_padd,log_msg
import importlib
info_step("add comfui/custom_nodes to sys.path")
custom_nodes_path = [custom_nodes_get_path(__file__,'../../custom_nodes')]
custom_nodes_add_to_sys(custom_nodes_path)
info_step("import modules in sub diretory modules")
import_custom_node_module(os.path.dirname(__file__),__name__,"modules")
# info_step(f"install deps (not installed) in requirements.txt if file exits")
# node_install_requirements(__file__)
# info_step(f"read name in sys.modules if name including name")
# - custom_node_module_class_name_list = read_module_class_name(__name__)
# custom_node_module_class_name_list = get_module_name_contains_x_in_sys(__name__)
info_step(f"read valid node class if key in sys.modules including this module name")
# - NodeClassList,NodeClassNameList = read_module_valid_node_class(__name__)
NodeClassList,NodeClassNameList = get_node_class_in_sys_modules(__name__)
info_step(f"make node_class_mappings and node_display_name_mappings {__name__}")
NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,NODE_MENU_NAMES = register_node_list_local(NodeClassList)
info_step(f"print node name of {__name__}")
NODE_MENU_NAMES.sort()
# print("\n".join(NODE_MENU_NAMES))
# register node with default category
# - NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,NODE_MENU_NAMES = register_node_list(this_module_all_classes,False)
# NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,NODE_MENU_NAMES = register_node_list_local(this_module_all_classes,False)
# addtional register node with custom category
# NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,NODE_MENU_NAMES = register_node_list_local(this_module_all_classes,True,"ymc/suite")
# print(NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,NODE_MENU_NAMES)
# print("\n".join(NODE_MENU_NAMES))
# info_step(f"define __all__ (not web ext) {__name__}")
# __all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
info_step(f"define __all__ (support web ext) {__name__}")
WEB_DIRECTORY = "./web"
# docs(core): export NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,WEB_DIRECTORY
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
# get version from pyproject.toml
# ...
version="3.0.0"
log_msg(msg_padd("=",60,"="))
log_msg(msg_padd("welocme to ymc_node_suite_comfyui",60,"="))
log_msg(f'version: {version}')
log_msg(f'node counts:{len(NODE_MENU_NAMES)}')
log_msg(f'node menu names:')
for node_name in NODE_MENU_NAMES:
# log_msg(f'node name:{node_name}')
info_status(f'{node_name}',0)
log_msg(msg_padd("=",60,"="))
+141
View File
@@ -0,0 +1,141 @@
以下是代码中定义的 ComfyUI 节点(Nodes)及其功能的解读:
---
### **1. IO 相关节点**
- **`io-text-save` (save text file)**
- **功能**:将文本保存到文件中,支持路径和文件名生成规则(如时间戳、自定义前缀等)。
- **输入**:文本内容、路径、文件名前缀、扩展名等。
- **输出**:保存的文本内容、文件名(不含扩展名)、扩展名。
- **特点**:支持历史记录管理,路径自动创建。
- **`io-util-file-list-get`**
- **功能**:获取指定目录下匹配特定模式的文件列表(如 `*.txt`)。
- **输入**:目录路径、文件匹配模式(如 `*.txt`)。
- **输出**:文件列表(每行一个文件名或绝对路径)。
- **`io-util-file-list-get-text`**
- **功能**:读取指定目录下所有匹配文件的文本内容并合并。
- **输入**:目录路径、文件匹配模式。
- **输出**:合并后的多行文本。
---
### **2. 文本处理节点**
- **`text-util-join-text`**
- **功能**:将多个输入文本用分隔符连接成一个字符串。
- **输入**:分隔符和最多 7 个文本字段。
- **输出**:合并后的字符串。
- **`text-util-search-text`**
- **功能**:在文本中搜索并替换内容。
- **输入**:原始文本、搜索字符串、替换字符串。
- **输出**:替换后的文本。
- **`text-util-random-text`**
- **功能**:从分隔符(如 `|`)分隔的文本中随机选择一项。
- **输入**:文本、随机种子、分隔符。
- **输出**:随机选择的文本和种子。
- **`text-util-loop-text`**
- **功能**:按顺序循环选择分隔符分隔的文本项(基于种子)。
- **输入**:文本、种子、分隔符。
- **输出**:选择的文本、种子、当前索引。
- **`text-util-switch-text`**
- **功能**:根据布尔值切换输出两个文本之一。
- **输入**:两个文本、布尔值(0 或 1)。
- **输出**:选择的文本。
---
### **3. 提示词(Prompt)处理节点**
- **`text-util-prompt-join`**
- **功能**:合并多个提示词,自动去重和标准化格式。
- **输入**:最多 7 个提示词字段。
- **输出**:合并后的提示词。
- **`text-util-prompt-del`**
- **功能**:从提示词中删除指定内容。
- **输入**:原始提示词、待删除内容。
- **输出**:清理后的提示词。
- **`text-util-prompt-std`**
- **功能**:标准化提示词(去除多余空格、换行等)。
- **输入**:原始提示词。
- **输出**:标准化后的提示词。
- **`text-util-prompt-shuffle`**
- **功能**:随机打乱提示词顺序(可选保留前 N 项)。
- **输入**:提示词、随机种子、保留项数。
- **输出**:打乱后的提示词。
---
### **4. 区域(Region)工具节点**
- **`region-util-get-by-center-and-size`**
- **功能**:根据中心点坐标和宽高计算裁剪区域的边界(左、上、右、下)。
- **输入**:中心点坐标 `(cx, cy)`、宽 `w`、高 `h`。
- **输出**:边界坐标和中心点信息。
- **`region-util-get-by-lt`**
- **功能**:根据左上角坐标和宽高计算裁剪区域的边界。
- **输入**:左上角坐标 `(left, top)`、宽 `w`、高 `h`。
- **输出**:边界坐标和中心点信息。
- **`region-util-get-pad-out-location-by-size`**
- **功能**:根据宽高计算填充(Padding)后的边界坐标(用于图像扩展)。
- **输入**:宽 `w`、高 `h`。
- **输出**:填充后的边界坐标。
---
### **5. 画布(Canvas)工具节点**
- **`canvas-util-cal-size`**
- **功能**:根据宽高比例(如 `1:1`、`9:16`)调整画布尺寸。
- **输入**:原始宽高、比例、锁定模式(固定宽或高)。
- **输出**:调整后的宽高。
---
### **6. 图像处理节点**
- **`img-util-get-image-size`**
- **功能**:获取图像的宽度和高度。
- **输入**:图像张量。
- **输出**:宽、高。
- **`img-util-switch-input-image`**
- **功能**:根据布尔值切换输出两张图像之一。
- **输入**:两张图像、布尔值。
- **输出**:选择的图像。
---
### **7. 条件(Conditioning)工具节点**
- **`conditioning-util-input-switch`**
- **功能**:根据布尔值切换输出两个条件(Conditioning)之一。
- **输入**:两个条件、布尔值。
- **输出**:选择的条件。
---
### **8. 其他工具节点**
- **`number-util-random-num`**
- **功能**:生成随机数(整数、浮点数或布尔值)。
- **输入**:范围、随机种子。
- **输出**:随机数。
- **`xyz-util-txt-to-int`**
- **功能**:将文本描述的数值范围(如 `1-10[5]`)解析为整数或浮点数列表。
- **输入**:文本范围、键名(如 `Steps`)。
- **输出**:解析后的数值列表和网格标注文本。
- **`pipe-util-to-basic-pipe`**
- **功能**:将模型、CLIP、VAE、条件等组合成基础管道(Basic Pipe)。
- **输入**:模型、CLIP、VAE、正负条件。
- **输出**:基础管道及各组件。
---
### **总结**
这些节点覆盖了文本处理、图像操作、区域计算、条件控制、随机生成等常见需求,适合在 ComfyUI 工作流中实现复杂的逻辑组合。每个节点均支持 ComfyUI 的输入输出规范,可直接与其他节点连接使用。
+33
View File
@@ -0,0 +1,33 @@
## attempted relative import with no known parent package
### 终端输出内容
```terminal
Traceback (most recent call last):
File "D:\code\python\panz\comfyui-nodes\ymc-node-suite-comfyui\node_demo.py", line 1, in <module>
from .conf import CURRENT_CATEGORY,CURRENT_FUNCTION
ImportError: attempted relative import with no known parent package
```
### 错误解释
- `Traceback (most recent call last)`:这是 Python 错误堆栈跟踪的起始行,表明 Python 开始回溯错误发生的位置。
- `File "D:\code\python\panz\comfyui-nodes\ymc-node-suite-comfyui\node_demo.py", line 1, in <module>`:指出错误发生在 `node_demo.py` 文件的第 1 行。
- `from .conf import CURRENT_CATEGORY,CURRENT_FUNCTION`:这是出错的代码行,尝试使用相对导入从 `conf` 模块导入 `CURRENT_CATEGORY` 和 `CURRENT_FUNCTION`。
- `ImportError: attempted relative import with no known parent package`:错误类型为 `ImportError`,提示使用相对导入时没有找到已知的父包。相对导入需要在包的上下文中使用,当直接运行脚本时,Python 不会将其视为包的一部分,所以会报错。
### 修复建议
#### 方法一:将项目作为包运行
如果你想使用相对导入,可以将项目作为包运行。在项目根目录下使用 `-m` 选项:
```powershell
python -m ymc-node-suite-comfyui.node_demo
```
#### 方法二:使用绝对导入
修改 `node_demo.py` 文件,将相对导入改为绝对导入:
```python:d:\code\python\panz\comfyui-nodes\ymc-node-suite-comfyui\node_demo.py
# 修改前
# from .conf import CURRENT_CATEGORY,CURRENT_FUNCTION
# 修改后
from ymc_node_suite_comfyui.conf import CURRENT_CATEGORY,CURRENT_FUNCTION
```
请根据实际的包名调整绝对导入路径。
+22
View File
@@ -0,0 +1,22 @@
CURRENT_CATEGORY="ymc/suite"
CURRENT_FUNCTION="exec"
class TextInput:
@classmethod
def INPUT_TYPES(s):
return {"required": {
"text": ("STRING",{"multiline": True,"default": ""}),
},
}
RETURN_TYPES = ("STRING",)
CATEGORY = CURRENT_CATEGORY
FUNCTION = CURRENT_FUNCTION
NODE_DESC = "text - input with multiline input"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
def exec(self,text):
return (text,)
+230
View File
@@ -0,0 +1,230 @@
```powershell
# 1
python __init__.py
python nodes.py
#
python -m main; # run module main done
python main.py; # run module main done
# python node_demo.py
python -m ymc_node_suite_comfyui.node_demo
# yours touch docs/ask.for.ai.py.faqs.01.md
# yours touch docs/ask.for.ai.py.faqs.02.md
yours touch docs/ask.for.ai.node-01-the-old-nodes.md
yours touch docs/ask.for.ai.node-02-refactor-nodes.md
yours touch nodes/text/__init__.py
yours touch nodes/text/conf.py
yours touch nodes/text/join.py
yours touch nodes/text/search.py
yours touch nodes/text/random.py
yours touch nodes/text/loop.py
yours touch nodes/text/switch.py
yours touch nodes/text/prompts.py #join,std,del,dup,shuffle
yours touch nodes/text/input.py
yours touch nodes/area/__init__.py
yours touch nodes/area/conf.py
yours touch nodes/area/from_center_and_size.py
yours touch nodes/area/from_lefttop_and_size.py
yours touch nodes/area/padd.py
# canvas cal size
yours touch nodes/image/__init__.py
yours touch nodes/image/conf.py
yours touch nodes/image/get_image_size.py
yours touch nodes/image/switch_image.py
yours touch nodes/utils/__init__.py
yours touch nodes/utils/conf.py
yours touch nodes/utils/random_num.py
yours touch nodes/utils/text_to_int.py
yours touch nodes/utils/basic_pipe.py
yours touch nodes/file/__init__.py
yours touch nodes/file/conf.py
yours touch nodes/file/get_file_list.py
yours touch nodes/file/get_text_of_file_list.py
yours touch nodes/cutoff/__init__.py
sh -c "cp nodes/text/__init__.py nodes/cutoff/__init__.py"
sh -c "cp nodes/text/conf.py nodes/cutoff/conf.py"
yours touch nodes/cutoff/text_from.py
yours touch nodes/cutoff/text_list_color.py
python -c "import sys;print(sys.path)"
# sh -c "rm -rf nodes/text/{input,join,loop,random,search,switch}.py"
# sh -c "rm -rf nodes/cutoff"
# sh -c "rm -rf nodes/cutoff"
```
## fags
- ImportError: attempted relative import with no known parent package
## depoy to comfyui (local + debug)
```bash
# del local nodes in comfyui custom_nodes
sh -c "rm -rf /o//app/ComfyUI/custom_nodes/ymc-node-suite-comfyui"
# copy local nodes to comfyui custom_nodes
sh -c "cp -r /d/code/python/panz/comfyui-nodes/ymc-node-suite-comfyui /o/app/ComfyUI/custom_nodes/"
# conda activate sdwa;cd O:\app\ComfyUI;python main.py;
```
## opv - features
```bash
# use ymc suite/text/cutoff
git add modules/text/node_cutoff_region.py ; git commit -m "feat(core): rename category for reading"
# use ymc suite/text/plain
git add modules/text/node_plain.py ; git commit -m "feat(core): use suite/text/plain"
git add modules/text/node_pyio.py ; git commit -m "feat(core): use suite/text/io"
git add modules/text/node_region.py ; git commit -m "feat(core): use suite/text/region"
git add modules/text/node_cutoff*.py ; git commit -m "build(core): use cutoff as filename"
git add modules/text/node_prompt*.py ; git commit -m "build(core): use category in itself"
```
## opv - docs
```bash
git add README.md ; git commit -m "docs(core): put usage"
git add . ; git commit -m "build(core): use shotscreen"
git add docs/opv.*.md ; git commit -m "docs(core): put note"
git add . ; git commit -m "refactor(core): reset dirs"
git add . ; git commit -m "refactor(core): rename nodes map"
git add . ; git commit -m "refactor(core): rename nodes display name"
git add . ; git commit -m "refactor(core): put dirs and category"
# get git msg log with oneline and short hash and msg head and time
git log --pretty=format:"%h %ad %s" --date=short -n 5
git log --oneline -n 5
git rm CHANGELOG.*.md
git rm conf.py
git mv node_demo.py docs/node_demo.py.md
git rm README.*.md
```
## opv - shotscreen
```bash
sh -c "mkdir -p shotscreen"
sh -c "cp -r /o/app/ComfyUI/my_workflows/nodes*.png shotscreen/"
```
## changelog
## clog - gen feat of from source code
```powershell
# yours edit-docs -h
yours feat --field feat --src-file modules/text/node_plain.py --out-file CHANGELOG.md --mode overide
yours feat --field feat --src-file modules/text/node_prompt.py --out-file CHANGELOG.md --mode tail
yours feat --field feat --src-file modules/text/node_pyio.py --out-file CHANGELOG.md --mode tail
yours feat --field feat --src-file modules/text/node_region.py --out-file CHANGELOG.md --mode tail
yours feat --field feat --src-file modules/text/node_cutoff_region.py --out-file CHANGELOG.md --mode tail
# add commit msg for this work:
# git add CHANGELOG.md ; git commit -m "chore(core): gen feat of from source code"
```
## clog - code size/feat/docs - name
```powershell
# set description in markdown file ?
# yours edit-docs --tpl-file docs.template.md --data "$pkgdesc" --label "<!-- inject-desc -->" --out-file README.md --workspace "$wsroot/$pkgsloc/$name"
yours edit-docs --tpl-file docs.template.md --data "$pkgdesc" --label "{description}" --out-file README.md --workspace "$wsroot/$pkgsloc/$name"
# set file size in markdown file ?
# yours edit-docs --tpl-file README.md --data-file lib-size.md --label "<!-- inject-file-size -->" --out-file README.md --workspace "$wsroot/$pkgsloc/$name"
# set features in markdown file ?
yours edit-docs --tpl-file README.md --data-file CHANGELOG.md --label "<!-- inject-features -->" --out-file README.md --workspace "$wsroot/$pkgsloc/$name"
# set installing package in markdown file ?
yours edit-docs --tpl-file README.md --data "$name" --label "{package}" --out-file README.md --workspace "$wsroot/$pkgsloc/$name"
# set demo in markdown file ?
# yours edit-docs --tpl-file README.md --data-file examples/main.py --label "<!-- inject-demo -->" --out-file README.md --workspace "$wsroot/$pkgsloc/$name"
# set demo in markdown file ? (demo.md)
yours edit-docs --tpl-file README.md --data-file demo.md --label "<!-- inject-demo -->" --out-file README.md --workspace "$wsroot/$pkgsloc/$name"
# add and set msg for this
# git add "packages/$name/*.md" ; git commit -m "docs(core): gen docs";
# git add "packages/$name/*.md" ; git commit -m "docs(core): put docs";
```
## opv - tags
```powershell
# add tags
git tag -a v1.0.0 -m "v1.0.0"
# git push ghg v1.0.0
git log --oneline
git tag
#$ver=yours version/bump --file pyproject.toml --name "tool.poetry.version" --method minor
#$ver=yours version/bump --file pyproject.toml --name "project.version" --method minor
$ver="3.0.0"
# add tags to some hash
git tag v$ver 73da0b9;
git tag v$ver HEAD;
# add/commit/tag
git add . ; git commit -m "build(core): put verison";git tag v$ver HEAD;
# git add . ; git commit -m "v$ver";git tag v$ver HEAD;
# add/commit with version? do
git add . ; git commit -m "$ver";
# git push ghg main --tags --force
# git push ghg main --tags --force
# git push ghg v$ver
```
## opv - workflow files
```powershell
# touch
yours touch .github/workflows/publish_to_comfy.yml
# edit
# ...
# add secret form comfy
# ...
git add .github/workflows/publish_to_comfy.yml; git commit -m "build(core): add publish to comfy workflow";
git add .github/workflows/publish_to_comfy.yml; git commit -m "build(core): check node in comfy";
git tag -d v$ver
```
+5
View File
@@ -0,0 +1,5 @@
from yors_comfyui_node_setup import entry,node_install_requirements
node_install_requirements(__file__)
__all__,NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,NODE_MENU_NAMES = entry(__name__,__file__,False)
# from . import *
+5
View File
@@ -0,0 +1,5 @@
from yors_comfyui_node_setup import entry,node_install_requirements
node_install_requirements(__file__)
__all__,NODE_CLASS_MAPPINGS,NODE_DISPLAY_NAME_MAPPINGS,NODE_MENU_NAMES = entry(__name__,__file__,False)
# from . import *
+2
View File
@@ -0,0 +1,2 @@
CURRENT_CATEGORY="ymc suite/text"
CURRENT_FUNCTION="exec"
+102
View File
@@ -0,0 +1,102 @@
# import random
from yors_pano_list_util import ListDelExclude,ListFillOne,ListToTupe,ListShuffle
# from .conf import CURRENT_CATEGORY,CURRENT_FUNCTION
# feat(core): use ymc suite/text/cutoff as category
CURRENT_CATEGORY="ymc suite/text/cutoff"
CURRENT_FUNCTION="exec"
def StrSetUndefinedAsEmpty(text,deft:str=""):
"""
target_text=StrSetUndefinedAsEmpty(target_text)
when text is "undefined", return ""
"""
if text == "undefined":
return deft
return text
# code(core): about shift + tap and tap in vscode
def CutoffRegionTextBurn(main_text, target_text, weight):
"""
CutoffRegionTextBurn(main_text,target_text,weight)
burn region text
"""
# Converted inputs are sent as the string of 'undefined' if not connected
target_text = StrSetUndefinedAsEmpty(target_text)
main_text = StrSetUndefinedAsEmpty(main_text)
region_text = ""
if weight is None or weight == "" or weight == "1.0":
region_text = (" ").join(filter(None, [target_text, main_text]))
else:
# region_text = (" ").join(filter(None, ["(",target_text, main_text,":",weight,")"]))
region_text = (" ").join(filter(None, [target_text, main_text]))
region_text = ("").join(filter(None, ["(", region_text, ":", weight, ")"]))
# return {"ui": {"text": (region_text,)}, "result": (region_text,)}
return region_text, target_text, weight
# feat(core): NodeCutoffRegionTextBurn - region text burn
class NodeCutoffRegionTextBurn:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"main_text": ("STRING", {"default": "dress", "multiline": True}),
"target_text":("STRING", {"default": "white", "multiline": False}),
"weight": ("STRING", {"default": "1.0", "multiline": False}),
}
}
RETURN_TYPES = ("STRING","STRING","STRING",)
RETURN_NAMES = ('region_text','target_text','prompt_weight')
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "region text burn"
OUTPUT_NODE = True
def exec(self, main_text,target_text,weight):
return CutoffRegionTextBurn(main_text,target_text,weight)
TEXT_BASE_COLORS=['red','orange','yellow','green','cyan','blue','purple','black','white','pink','custom']
TEXT_BASE_COLORS_COUNT=len(TEXT_BASE_COLORS)
def CutoffColorTextPreset(custom:str="sliver"):
"""
CutoffColorTextList(custom)
color text preset will update the last one will custom
"""
# return red,orange,yellow,green,cyan,blue,purple,black,white,pink,custom
TEXT_BASE_COLORS_VALUE = TEXT_BASE_COLORS.copy()
TEXT_BASE_COLORS_VALUE[TEXT_BASE_COLORS_COUNT-1]=custom
return ListToTupe(TEXT_BASE_COLORS_VALUE)
# feat(core): NodeCutoffRegionColorTextPreset - color text preset
class NodeCutoffRegionColorTextPreset:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"custom": ("STRING", {"default": "red", "multiline": False})
}
}
# RETURN_TYPES = ("STRING","STRING","STRING","STRING","STRING","STRING","STRING","STRING","STRING","STRING","STRING",)
RETURN_TYPES = ListToTupe(ListFillOne('STRING',TEXT_BASE_COLORS_COUNT))
RETURN_NAMES = ListToTupe(TEXT_BASE_COLORS)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "color text preset"
OUTPUT_NODE = True
def exec(self, custom):
# Converted inputs are sent as the string of 'undefined' if not connected
custom=StrSetUndefinedAsEmpty(custom,"sliver")
return CutoffColorTextPreset(custom)
+507
View File
@@ -0,0 +1,507 @@
import random
from yors_pano_list_util import ListDelExclude,ListFillOne,ListToTupe,ListShuffle
# from .conf import CURRENT_CATEGORY,CURRENT_FUNCTION
# feat(core): use ymc suite/text/plain as category
CURRENT_CATEGORY="ymc suite/text/plain"
CURRENT_FUNCTION="exec"
def TextListRandom(text:str, seed,char='|'):
"""
item,seed = TextListRandom(text,seed,'|')
TextListRandom(text,seed,',')
"""
iteml = text.split(char)
random.seed(seed)
choice = random.choice(iteml)
return ListToTupe([choice,seed])
def range_safe(i:int,s:int,e:int):
'''
ensure i is in [s,e]
'''
res=i
if res <s:
res=s
if res >e:
res=e
return res
def range_safe_loop(i:int,s:int,e:int):
'''
ensure i is in [s,e] for loop
'''
res=i
if res <s:
res=s
if res >=e:
res = res % e
res = s + res
return res
def TextListLoop(text, seed,char='|'):
iteml = text.split(char)
# get loop index with de/cr seed
length=len(iteml)
index=range_safe_loop(seed-1,0,length)
choice = iteml[index]
# print(maxindex,seed,index,choice)
return ListToTupe([choice,seed,str(index+1),length])
def TextReplace(text, find, replace):
"""
text replace find with re.sub
"""
import re
result = re.sub(find, replace, text)
return result
TEXT_LIST_RETURN_NAMES=['a','b','c','d','e','f','g']
TEXT_LIST_COUNT=len(TEXT_LIST_RETURN_NAMES)
TEXT_LIST_ADV_RETURN_NAMES=TEXT_LIST_RETURN_NAMES.copy()
TEXT_LIST_ADV_RETURN_NAMES.insert(0,"prefix")
# feat(core): NodePlainTextList - input text preset
class NodePlainTextList:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"a": ("STRING",{"forceInput":False}),
"b": ("STRING",{"forceInput":False}),
"c": ("STRING",{"forceInput":False}),
"d": ("STRING",{"forceInput":False}),
"e": ("STRING",{"forceInput":False}),
"f": ("STRING",{"forceInput":False}),
"g": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('STRING',TEXT_LIST_COUNT))
RETURN_NAMES = ListToTupe(TEXT_LIST_RETURN_NAMES)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "input text preset"
OUTPUT_NODE = True
def exec(self, a,b,c,d,e,f,g):
return ListToTupe([a,b,c,d,e,f,g])
# feat(core): NodePlainTextListAdv - input text preset - adv
class NodePlainTextListAdv:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"prefix": ("STRING",{"forceInput":False}),
"a": ("STRING",{"forceInput":False}),
"b": ("STRING",{"forceInput":False}),
"c": ("STRING",{"forceInput":False}),
"d": ("STRING",{"forceInput":False}),
"e": ("STRING",{"forceInput":False}),
"f": ("STRING",{"forceInput":False}),
"g": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('STRING',TEXT_LIST_COUNT+1))
RETURN_NAMES = ListToTupe(TEXT_LIST_ADV_RETURN_NAMES)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "input text preset - adv"
OUTPUT_NODE = True
def exec(self,prefix, a,b,c,d,e,f,g):
joined = list(map(lambda x: prefix+x,[a,b,c,d,e,f,g]))
import datetime
# import time
joined.insert(0,prefix)
joined = list(map(lambda x: datetime.datetime.now().strftime(x),joined)) # replace time exp like %Y %M %D
return ListToTupe(joined)
# feat(core): NodePlainTextListJoinTextAdv - join text to text preset - adv
class NodePlainTextListJoinTextAdv:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
},
"optional": {
"text": ("STRING",{"forceInput": True}),
"a": ("STRING",{"forceInput":True}),
"b": ("STRING",{"forceInput":True}),
"c": ("STRING",{"forceInput":True}),
"d": ("STRING",{"forceInput":True}),
"e": ("STRING",{"forceInput":True}),
"f": ("STRING",{"forceInput":True}),
"g": ("STRING",{"forceInput":True}),
"action": (["none","text-as-head","text-as-tail"],),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('STRING',TEXT_LIST_COUNT+1))
RETURN_NAMES = ListToTupe(['text','a','b','c','d','e','f','g'])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "join text to text preset - adv"
OUTPUT_NODE = True
def exec(self,text, a,b,c,d,e,f,g,action="none"):
joined=[]
if "none" in action:
joined = [a,b,c,d,e,f,g]
elif "text-as-head" in action:
joined = list(map(lambda x: text+x,[a,b,c,d,e,f,g]))
elif "text-as-tail" in action:
joined = list(map(lambda x: x+text,[a,b,c,d,e,f,g]))
import datetime
# import time
joined.insert(0,text)
joined = list(map(lambda x: datetime.datetime.now().strftime(x),joined)) # replace time exp like %Y %M %D
return ListToTupe(joined)
# feat(core): NodePlainTextJoin - join text
class NodePlainTextJoin:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"splitchar": ("STRING", {"default": "_", "multiline": False}),
"a": ("STRING",{"forceInput":False}),
"b": ("STRING",{"forceInput":False}),
"c": ("STRING",{"forceInput":False}),
"d": ("STRING",{"forceInput":False}),
"e": ("STRING",{"forceInput":False}),
"f": ("STRING",{"forceInput":False}),
"g": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('STRING',1))
RETURN_NAMES = ListToTupe(['text'])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "join text"
OUTPUT_NODE = True
def exec(self, splitchar,a,b,c,d,e,f,g):
# args to list
ls=[a,b,c,d,e,f,g]
# ignore none
ls = list(filter(None, ls))
# join with splitChar
joinedtxt=splitchar.join(ls)
return ListToTupe([joinedtxt])
# feat(core): NodePlainTextRandom - random text in list
class NodePlainTextRandom:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"default": '', "multiline": False}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"char": ("STRING", {"default": '|', "multiline": False}),
}
}
RETURN_TYPES = ("STRING",'INT',)
RETURN_NAMES = ListToTupe(['text','seed'])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "random text in list"
def exec(self, text, seed,char='|'):
return TextListRandom(text,seed,char)
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN")
# feat(core): NodePlainTextLoop - get text in list (loop)
class NodePlainTextLoop:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"default": '', "multiline": False}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
"char": ("STRING", {"default": '|', "multiline": False}),
}
}
RETURN_TYPES = ("STRING",'INT','STRING','INT')
RETURN_NAMES = ListToTupe(['text','seed','index','count'])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "get text in list (loop)"
def exec(self, text, seed,char='|'):
return TextListLoop(text, seed,char)
@classmethod
def IS_CHANGED(cls, **kwargs):
return float("NaN")
# feat(core): NodePlainTextReplace - replace text
class NodePlainTextReplace:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"default": '', "multiline": False}),
"find": ("STRING", {"default": '', "multiline": False}),
"replace": ("STRING", {"default": '', "multiline": False}),
"action": (["yes",'no'],),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ListToTupe(['text'])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "replace"
def exec(self, text, find, replace,action='yes'):
# feat(core): do nothing when action no
if 'no' in action:
return ListToTupe([text])
# feat(core): replace text
return ListToTupe([TextReplace(text, find, replace)])
def NumStrToNumInt(str:str="1",roundit:bool=True):
"""
NumStrToNumInt(0.5)
NumStrToNumInt(float(str))
"""
if roundit == True:
return int(round(str))
return int(str)
def NumStrIsRoundToA(a='1', isRoundTo='1'):
"""
NumStrIsRoundToA("0.8","1")
NumStrIsRoundToA("0.4","1")
"""
if NumStrToNumInt(a) == NumStrToNumInt(isRoundTo):
return True
else:
return False
def BoolStr2BoolNum(str:str="true"):
"""
BoolStr2BoolNum("TRUE")
BoolStr2BoolNum("false")
"""
# true,TRUE
if str=="true":
return 1
else:
return 0
# feat(core): NodeNumStrToNum - text to number
class NodeNumStrToNum:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"default": '', "multiline": False}),
}
}
# RETURN_TYPES = ('NUMBER',)
RETURN_TYPES = ("NUMBER", "FLOAT", "INT")
RETURN_NAMES = ListToTupe(["number", "float", "roundInt"])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "to number"
def exec(self, text=''):
strInt = int(text)
strFlo = float(text)
strRoundInt = int(round(strFlo))
return ListToTupe([strInt,strFlo,strRoundInt])
# feat(core): NodeNumStrToBool - text to bool
class NodeNumStrToBool:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"text": ("STRING", {"default": '1', "multiline": False}),
"isRoundTo":("STRING", {"default": '1', "multiline": False}),
}
}
RETURN_TYPES = ('STRING',)
RETURN_NAMES = ListToTupe(["boolean"])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "is round to x"
def exec(self, a='1', isRoundTo='1'):
return ListToTupe([NumStrIsRoundToA(a,isRoundTo)])
# feat(core): NodePlainTextSwitchWithBool - text switch with bool
class NodePlainTextSwitchWithBool:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"a": ("STRING", {"default": '', "multiline": False}),
"b": ("STRING", {"default": '', "multiline": False}),
"boolean": ("BOOL",),
}
}
RETURN_TYPES = ('STRING',)
RETURN_NAMES = ListToTupe(["text"])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "switch a and b"
def exec(self, a='', b='', boolean:bool=True):
if boolean==True:
return (a, )
else:
return (b, )
# feat(core): NodePlainTextSwitchWithText - text switch with text
class NodePlainTextSwitchWithText:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"a": ("STRING", {"default": '', "multiline": False}),
"b": ("STRING", {"default": '', "multiline": False}),
"text": ("STRING", {"default": '1', "multiline": False}),
"isRoundTo":("STRING", {"default": '1', "multiline": False}),
}
}
RETURN_TYPES = ('STRING',)
RETURN_NAMES = ListToTupe(["text"])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "switch a and b (isRoundTo)"
def exec(self, a='', b='', text='1',isRoundTo='1'):
if NumStrIsRoundToA(text,isRoundTo):
return (a, )
else:
return (b, )
# class NodeTextShow:
# @classmethod
# def INPUT_TYPES(s):
# return {"required": {
# "text": ("STRING", {"forceInput": True}),
# }}
# # INPUT_IS_LIST = True
# FUNCTION = CURRENT_FUNCTION
# CATEGORY = CURRENT_CATEGORY
# NODE_DESC = "show text"
# OUTPUT_NODE = True
# RETURN_TYPES = ('STRING',)
# # OUTPUT_IS_LIST = (True,)
# def exec(self, text):
# return {"ui": {"text": text}, "result": (text,)}
# refer: pythongosssss /ComfyUI-Custom-Scripts/py/show_text.py
# feat(core): NodeTextShow - show text
class NodeTextShow:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING", {"forceInput": True}),
},
"hidden": {
"unique_id": "UNIQUE_ID",
"extra_pnginfo": "EXTRA_PNGINFO",
},
}
INPUT_IS_LIST = True
RETURN_TYPES = ("STRING",)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "show text"
OUTPUT_NODE = True
OUTPUT_IS_LIST = (True,)
def exec(self, text, unique_id = None, extra_pnginfo=None):
if unique_id and extra_pnginfo and "workflow" in extra_pnginfo[0]:
workflow = extra_pnginfo[0]["workflow"]
node = next((x for x in workflow["nodes"] if str(x["id"]) == unique_id[0]), None)
if node:
node["widgets_values"] = [text]
return {"ui": {"text": text}, "result": (text,)}
# feat(core): NodeTextCalcSize - cacluate width and height
class NodeTextCalcSize:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"w": ("INT", {"default":512, "max": 10000000, "min":0, "step":1}),
"h": ("INT", {"default":512, "max": 10000000, "min":0, "step":1}),
"lockoff": (["false", "w","h"],),
# "rate": (["1:1", "2:3","9:16"],),
"rate": ("STRING", {"default": "1:1", "multiline": False}),
"reverse": (["false", "true"],),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('INT',2))
RETURN_NAMES = ListToTupe(['w','h'])
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "cacluate width and height"
OUTPUT_NODE = True
def exec(self,w=512, h=512,lockoff='false',rate='1:1',reverse='false'):
rw=w
rh=h
# get h or w with lockoff
if lockoff != "false":
ratel=list(map(int,rate.split(":")))
raten=(ratel[0]/ratel[1])
if lockoff == "w":
rh=int(w/raten)
if lockoff == "h":
rw=int(h*raten)
floatl=[rw,rh]
# reverse result of w h
if reverse == "true":
floatl=[rh,rw]
intl=list(map(int,floatl))
return ListToTupe(intl)
+615
View File
@@ -0,0 +1,615 @@
import re
import random
from yors_pano_list_util import ListDelExclude,ListFillOne,ListToTupe,ListShuffle
# use current_category and current_function from con.py in current directory
# from .conf import CURRENT_CATEGORY,CURRENT_FUNCTION
# feat(core): use ymc suite/text as category
CURRENT_CATEGORY="ymc suite/text"
CURRENT_FUNCTION="exec"
def tags_listify(s:str):
ls = s.split(",")
ls = list(filter(None, ls))
return ls
def tags_strify(l:list[str]):
return ','.join(l)
def tag_trim(s:str):
trimedtag=s
trimedtag=re.sub(r'^ *', '', trimedtag,0)
trimedtag=re.sub(r' *$', '', trimedtag,0)
return trimedtag
def tag_ms2os(s:str):
return re.sub(re.compile(r' {1,}'), ' ', s,0)
def tag_unweight(s:str):
# unweight,trim,
unwdtag=s.replace("(","").replace(")","")
unwdtag=re.sub(r':\d.*', '', unwdtag,0)
unwdtag=tag_trim(unwdtag)
unwdtag=tag_ms2os(unwdtag)
return unwdtag
def tags_unweight(s:str):
ls=tags_listify(s)
nls=[]
for tag in ls:
stdedtag=tag_unweight(tag)
if stdedtag !="":
nls.append(stdedtag)
return ",".join(nls)
def tags_onelineify(s:str):
return re.sub(r'\n', ',', s,0)
def tag_std(s:str):
# trim,ms2os
stdedtag=s
stdedtag=tag_trim(stdedtag)
stdedtag=tag_ms2os(stdedtag)
return stdedtag
def tags_dup(s:str):
nls=tags_listify(s)
nls=list(dict.fromkeys(nls))
return ",".join(nls)
def tags_std(s:str):
ls=tags_listify(s)
nls=[]
for tag in ls:
stdedtag=tag_std(tag)
if stdedtag !="":
nls.append(stdedtag)
return ",".join(nls)
def tags_ignore(s:str,i:str):
ls=tags_listify(s)
li=tags_listify(i)
nls=[]
for tag in ls:
stdedtag=tag_unweight(tag)
if stdedtag !="" and not(stdedtag in li):
nls.append(tag)
return ",".join(nls)
def tags_replace(s:str,i:str,r:str):
ls=tags_listify(s)
li=tags_listify(i)
# lr=tags_listify(r)
nls=[]
for tag in ls:
stdedtag=tag_unweight(tag)
if stdedtag !="":
if stdedtag in li:
# replace found with r
nls.append(r)
else:
nls.append(tag)
return ",".join(nls)
def tags_search(s:str,i:str):
ls=tags_listify(s)
li=tags_listify(i)
nls=[]
for tag in ls:
stdedtag=tag_unweight(tag)
if stdedtag !="" :
for pattern in li:
if pattern in stdedtag:
nls.append(tag)
return ",".join(nls)
def tags_head(s:str,i:str):
"""
move some tags to head
"""
head=tags_listify(i)
tails=tags_listify(tags_ignore(s,i))
return ",".join(head+tails)
def tags_tail(s:str,i:str):
"""
move some tags to head
"""
tails=tags_listify(i)
head=tags_listify(tags_ignore(s,i))
return ",".join(head+tails)
# feat(core): NodePromptAdd - prompt add some text
class NodePromptAdd:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"forceInput":False}),
"toadd": ("STRING",{"forceInput":False}),
"action": (["add2head",'add2tail','del|add2head','del|add2tail','none'],),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - add"
OUTPUT_NODE = True
def exec(self, text='',toadd='',action='add2head'):
# if action =="add2head":
# return list_2_tupe([tags_head(text,prev)])
# if action =="add2tail":
# return list_2_tupe([tags_tail(text,prev)])
if 'none' in action:
return ListToTupe([text])
prev=toadd
if 'del' in action:
prev=tags_ignore(text,toadd)
if 'add2head' in action:
prev=tags_head(text,prev)
if 'add2tail' in action:
prev=tags_tail(text,prev)
return ListToTupe([prev])
# feat(core): NodePromptAdvAdd - prompt add some text (adv)
class NodePromptAdvSearch:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"forceInput":False}),
"search": ("STRING",{"forceInput":False}),
"action": (['none',"del", "move2head",'move2tail',"replace","search"],),
"replace": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - search (adv)"
OUTPUT_NODE = True
def exec(self, text='',search='',action='none',replace=''):
if action == "none":
return ListToTupe([text])
if action == "search":
return ListToTupe([tags_search(text,search)])
searchedtags=tags_search(text,search)
if action =="replace":
return ListToTupe([tags_replace(text,searchedtags,replace)])
if action =="del":
return ListToTupe([tags_ignore(text,searchedtags)])
if action =="move2head":
return ListToTupe([tags_head(text,searchedtags)])
if action =="move2tail":
return ListToTupe([tags_tail(text,searchedtags)])
# feat(core): NodePromptSearch - prompt search some text
class NodePromptSearch:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"forceInput":False}),
"search": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - search"
OUTPUT_NODE = True
def exec(self, text='',search=''):
return ListToTupe([tags_search(text,search)])
# feat(core): NodePromptDup - prompt dup some text
class NodePromptDup:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - dup"
OUTPUT_NODE = True
def exec(self, text):
return ListToTupe([tags_dup(text)])
# feat(core): NodePromptAdvDup - prompt dup some text (adv)
class NodePromptAdvDup:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"forceInput":False}),
# "mode": (["alline", "in-each-line"],),
}
}
RETURN_TYPES = ("STRING","STRING",)
RETURN_NAMES = ('STRING',"STRING",)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - dup (adv)"
OUTPUT_NODE = True
def exec(self, text):
a=''
res=[]
textl=text.split("\n")
for line in textl:
res.append(tags_dup(line))
a='\n'.join(res)
tags_dup(tags_onelineify(text))
return ListToTupe([tags_dup(tags_onelineify(a)),a])
# feat(core): NodePromptShuffle - prompt shuffle some text
class NodePromptShuffle:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"forceInput":False}),
"keep_n_token": ("STRING", {"default": "0", "multiline": False}),
"seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - shuffle"
OUTPUT_NODE = True
def exec(self, text='',keep_n_token='0',seed=0):
random.seed(seed)
textl=tags_listify(tags_onelineify(text))
el=ListShuffle(textl,int(keep_n_token))
return ListToTupe([','.join(el)])
# feat(core): NodePromptDel - prompt del some text
class NodePromptDel:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"search": ("STRING",{"forceInput":False}),
"todel": ("STRING",{"forceInput":False}),
"action": (["yes",'no'],),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - del"
OUTPUT_NODE = True
def exec(self, search='',todel='',action='yes'):
# https://www.geeksforgeeks.org/python-remove-empty-strings-from-list-of-strings/
if 'no' in action:
return ListToTupe([search])
ca=tags_ignore(search,tags_unweight(todel))
return ListToTupe([ca])
# feat(core): NodePromptStd - prompt std
class NodePromptStd:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - std"
OUTPUT_NODE = True
def exec(self, text):
return ListToTupe([tags_std(text)])
# feat(core): NodePromptInput - prompt input
class NodePromptInput:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"default": "","multiline":True}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('text',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - input"
OUTPUT_NODE = True
def exec(self, text=''):
return ListToTupe([tags_std(text)])
# feat(core): NodePromptUnweight - prompt unweight
class NodePromptUnweight:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - unweight"
OUTPUT_NODE = True
def exec(self, text):
return ListToTupe([tags_unweight(text)])
# feat(core): NodePromptJoin - prompt join
class NodePromptJoin:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"a": ("STRING",{"forceInput":False}),
"b": ("STRING",{"forceInput":False}),
"c": ("STRING",{"forceInput":False}),
"d": ("STRING",{"forceInput":False}),
"e": ("STRING",{"forceInput":False}),
"f": ("STRING",{"forceInput":False}),
"g": ("STRING",{"forceInput":False}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - join"
OUTPUT_NODE = True
def exec(self, a,b,c,d,e,f,g):
vl=[a,b,c,d,e,f,g]
vs = (",").join(vl)
vs=tags_std(vs)
# vs = vs.replace(" ", " ").replace(" ,", ",").replace(", ", ",").replace(",,", ",").replace(",,", ",")
return {"ui": {"text": (vs,)}, "result": (vs,)}
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("*")
default_prompt1='''Swing
Slide
Climbing frame
Sandbox
See-saw
Merry-go-round
Jungle gym
Trampoline
Monkey bars
Rocking horse
Playhouse
Hopscotch
Balance beam
Spring rider
Water play area
Ball pit
Tunnel
Zip line
Basketball hoop
Bicycle rack
Spinner
Climbing wall
Rope ladder
Tetherball
Flying fox
Swinging bridge
Spiral slide
Water sprinkler
Pedal go-kart
Miniature golf course
'''
default_prompt1="\n".join([p.strip() for p in default_prompt1.split('\n') if p.strip()!=''])
# refer comfyui-mixlab-nodes
# feat(core): NodePromptRandom - prompt random
class NodePromptRandom:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"max_count": ("INT", {"default": 9, "min": 1, "max": 1000}),
# "image_field": ("IMAGE",),
"mutable_prompt": ("STRING",
{
"multiline": True,
"default": default_prompt1
}),
"immutable_prompt": ("STRING",
{
"multiline": True,
"default": 'sticker, Cartoon, ``'
}),
"random_sample": (["enable", "disable"],),
# "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff, "step": 1}),
},
"optional":{
"seed": (any_type, {"default": 0, "min": 0, "max": 0xffffffffffffffff}),
}
}
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - random"
RETURN_TYPES = ("STRING",)
# CATEGORY = "♾️Mixlab/Prompt"
OUTPUT_IS_LIST = (True,)
OUTPUT_NODE = True
def exec(self,max_count,mutable_prompt,immutable_prompt,random_sample,seed=0):
# Split the text into an array of words
words1 = mutable_prompt.split("\n")
# Split the text into an array of words
words2 = immutable_prompt.split("\n")
# progress bar
# pbar = comfy.utils.ProgressBar(len(words1)*len(words2))
# Select a random word from the array
# random_word = random.choice(words)
prompts=[]
for w1 in words1:
w1=w1.strip()
for w2 in words2:
w2=w2.strip()
if '``' not in w2:
if w2=="":
w2='``'
else:
w2=w2+',``'
if w1!='' and w2!='':
prompts.append(w2.replace('``', w1))
# pbar.update(1)
if len(prompts)==0:
prompts.append(immutable_prompt)
if random_sample=='enable':
# random - get x-count element in array
prompts = random.sample(prompts, min(max_count,len(prompts)))
else:
prompts = prompts[:min(max_count,len(prompts))]
prompts= [elem.strip() for elem in prompts if elem.strip()]
# return (new_prompt)
return {"ui": {"prompts": prompts}, "result": (prompts,)}
def addWeight(text, weight=1):
if weight == 1:
return text
else:
return f"({text}:{round(weight,3)})"
# feat(core): NodePromptWeight - prompt add weight
class NodePromptWeight:
@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"
# }),
}
}
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "prompt - add weight"
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("prompt",)
# FUNCTION = "run"
# CATEGORY = "♾️Mixlab/Prompt"
INPUT_IS_LIST = False
OUTPUT_IS_LIST = (False,)
OUTPUT_NODE = False
def exec(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,)
+162
View File
@@ -0,0 +1,162 @@
import glob
import hashlib
# import json
# import numpy as np
# from numba import jit
import os
from pathlib import Path
from yors_pano_list_util import ListDelExclude,ListFillOne,ListToTupe,ListShuffle
# feat(core): use ymc suite/text/io as category
CURRENT_CATEGORY="ymc suite/text/io"
CURRENT_FUNCTION="exec"
def pyioReadDirectory(dir='',pattern='*.txt'):
"""
pyio - get path at dir and pattern
"""
res=[]
for filename in glob.glob(os.path.join(dir,pattern)):
res.append(filename)
return res
def pyioReadAsText(loc):
"""
pyio - read text at location
"""
res=''
with open(loc) as f:
res = f.read()
f.close()
return res
def pyioGetFilesInDir(input_dir):
"""
files = pyioGetFilesInDir(input_dir)
"""
return [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))]
def pyioParseFilename(filename:str):
"""
pyioParseFilename('config.txt')
"""
name, suffix = os.path.splitext(filename)
return (name, suffix)
def pyioGetFilename(filename:str):
"""
pyioGetFilename('config.txt')
"""
name, suffix = os.path.splitext(filename)
return name
def pyioPlibParseFilename(filename:str):
"""
pyioPlibParseFilename('config.txt')
"""
p=Path(filename)
return (p.suffix, p.stem)
def StrmFromList(txtl:list[str],eof:str='\n'):
"""
strm - from text list
StrmFromList(fileAbsPathList)
"""
return eof.join(txtl)
# feat(core): NodePyioFilenameListDetect - pyio - detect filename list
class NodePyioFilenameListDetect:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"path": ("STRING", {"default": "", "multiline": False}),
"pattern": ("STRING", {"default": "*.txt", "multiline": False}),
# "mode": ("STRING", {"default": "filename", "multiline": False}),
"mode": (["filename", "abspath","filename-no-ext"],),
}
}
RETURN_TYPES = ("STRING",'INT')
RETURN_NAMES = ('STRING','count')
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "pyio - detect filename list"
OUTPUT_NODE = True
def exec(self, path='',pattern='*.txt',mode='filename'):
fileAbsPathList=pyioReadDirectory(path,pattern)
resl = fileAbsPathList
if mode=='filename':
fileNameList=list(map(os.path.basename,fileAbsPathList))
resl=fileNameList
if mode=='filename-no-ext':
filenameNoExtList=list(map(pyioGetFilename,fileNameList))
resl=filenameNoExtList
# resl='\n'.join(resl)
# fileAbsPathListStrm = StrmFromList(fileAbsPathList)
# fileNameListStrm = StrmFromList(fileNameList)
# filenmeWithoutSuffixList=StrmFromList(filenameNoExtList)
return ListToTupe([StrmFromList(resl),len(resl)])
# feat(core): NodePyioFilenameListOutmDetect - pyio - detect filename list (outm)
class NodePyioFilenameListOutmDetect:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"path": ("STRING", {"default": "", "multiline": False}),
"pattern": ("STRING", {"default": "*.txt", "multiline": False}),
}
}
RETURN_TYPES = ("STRING","STRING","STRING","INT")
RETURN_NAMES = ('absPath','name','nameNoExt','count')
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "pyio - detect filename list (outm)"
OUTPUT_NODE = True
def exec(self, path='',pattern='*.txt'):
absPathList=pyioReadDirectory(path,pattern)
nameList=list(map(os.path.basename,absPathList))
nameNoExtList=list(map(pyioGetFilename,nameList))
return ListToTupe([StrmFromList(absPathList),StrmFromList(nameList),StrmFromList(nameNoExtList),len(absPathList)])
# feat(core): IoUtilFileListGetText - io - get file list and read as text
class IoUtilFileListGetText:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"path": ("STRING", {"default": "", "multiline": False}),
"pattern": ("STRING", {"default": "*.txt", "multiline": False}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ('STRING',)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
OUTPUT_NODE = True
def exec(self, path='',pattern='*.txt'):
plist=pyioReadDirectory(path,pattern)
text=[]
for loc in plist:
text.append(pyioReadAsText(loc))
# to multi line
text='\n'.join(text)
return ListToTupe([text])
+150
View File
@@ -0,0 +1,150 @@
from yors_pano_list_util import ListDelExclude,ListFillOne,ListToTupe,ListShuffle
# feat(core): use ymc suite/text/region as category
CURRENT_CATEGORY="ymc suite/text/region"
CURRENT_FUNCTION="exec"
def region_strify(l:list):
tl=list(map(str,l))
tl=list(filter(None, tl))
return ','.join(tl)
def region_listfy(s:str):
tl=s.split(",")
tl=list(filter(None, tl))
return tl
def region_intify(l:list):
tl=list(map(int,l))
return tl
def region_get_location_from_center_size(cx=128, cy=128, w=256, h=256):
left=cx-w/2
top=cy-h/2
right=cx+w/2
bottom=cy+h/2
floatl=[left,top,right,bottom,cx,cy,w,h]
intl=list(map(int,floatl))
return intl
def region_burn_corp_location_as_square(w=256, h=256):
c=0
if w<h:
c=1
left=-1*c*(w-h)/2
right=left
c=0
if w>=h:
c=1
top=c*(w-h)/2
bottom=top
floatl=[left,top,right,bottom]
intl=list(map(int,floatl))
return ListToTupe(intl)
def region_burn_corp_location_from_lt_and_size(left=0, top=0, w=256, h=256):
cx=left+w/2
cy=top+h/2
right=cx+w/2
bottom=cy+h/2
floatl=[left,top,right,bottom,cx,cy,w,h]
intl=list(map(int,floatl))
return ListToTupe(intl)
REGION_GET_CROP_LOCATION_BY_CENTER_RETURN_NAMES=['left','top','right','bottom','cx','cy','w','h']
REGION_GET_CROP_LOCATION_BY_CENTER_RETURN_NAMES_COUNT=len(REGION_GET_CROP_LOCATION_BY_CENTER_RETURN_NAMES)
# feat(core): NodeRegionCropLocationBurnNumz - num - burn crop location (center & size)
class NodeRegionCropLocationBurnNumz:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"cx": ("INT", {"default":128, "max": 10000000, "min":0, "step":1}),
"cy": ("INT", {"default":128, "max": 10000000, "min":0, "step":1}),
"w": ("INT", {"default":256, "max": 10000000, "min":0, "step":1}),
"h": ("INT", {"default":256, "max": 10000000, "min":0, "step":1}),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('INT',REGION_GET_CROP_LOCATION_BY_CENTER_RETURN_NAMES_COUNT))
RETURN_NAMES = ListToTupe(REGION_GET_CROP_LOCATION_BY_CENTER_RETURN_NAMES)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "num - burn crop location (center & size)"
OUTPUT_NODE = True
def exec(self, cx=128, cy=128, w=256, h=256):
intl=region_get_location_from_center_size(cx,cy,w,h)
return ListToTupe(intl)
# feat(core): NodeRegionCropLocationBurnTxtz - txt - burn crop location (center & size)
class NodeRegionCropLocationBurnTxtz:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"cxcy": ("STRING", {"default": '128,128', "multiline": False}),
"size": ("STRING", {"default": '256,256', "multiline": False}),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('INT',REGION_GET_CROP_LOCATION_BY_CENTER_RETURN_NAMES_COUNT))
RETURN_NAMES = ListToTupe(REGION_GET_CROP_LOCATION_BY_CENTER_RETURN_NAMES)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "txt - burn crop location (cxcy & size)"
OUTPUT_NODE = True
def exec(self, cxcy='128,128',size='256,256'):
cx,cy=region_intify(region_listfy(cxcy))
w,h=region_intify(region_listfy(size))
intl=region_get_location_from_center_size(cx,cy,w,h)
return ListToTupe(intl)
REGION_GET_CROP_LOCATION_BY_LT_RETURN_NAMES=['left','top','right','bottom','cx','cy','w','h']
REGION_GET_CROP_LOCATION_BY_LT_RETURN_NAMES_COUNT=len(REGION_GET_CROP_LOCATION_BY_LT_RETURN_NAMES)
# feat(core): NodeRegionCropLocationBurnLtwh - int - burn crop location (lt & size)
class NodeRegionCropLocationBurnLtwh:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"left": ("INT", {"default":0, "max": 10000000, "min":0, "step":1}),
"top": ("INT", {"default":0, "max": 10000000, "min":0, "step":1}),
"w": ("INT", {"default":256, "max": 10000000, "min":0, "step":1}),
"h": ("INT", {"default":256, "max": 10000000, "min":0, "step":1}),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('INT',REGION_GET_CROP_LOCATION_BY_LT_RETURN_NAMES_COUNT))
RETURN_NAMES = ListToTupe(REGION_GET_CROP_LOCATION_BY_LT_RETURN_NAMES)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "int - burn crop location (lt & size)"
OUTPUT_NODE = True
def exec(self, left=0, top=0, w=256, h=256):
return region_burn_corp_location_from_lt_and_size(left,top,w,h)
REGION_LOCATION_RETURN_NAMES=['left','top','right','bottom']
# feat(core): NodeRegionPaddingSquareLocationBurn - int - burn padding location as square (size)
class NodeRegionPaddingSquareLocationBurn:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"w": ("INT", {"default":256, "max": 10000000, "min":0, "step":1}),
"h": ("INT", {"default":256, "max": 10000000, "min":0, "step":1}),
}
}
RETURN_TYPES = ListToTupe(ListFillOne('INT',len(REGION_LOCATION_RETURN_NAMES)))
RETURN_NAMES = ListToTupe(REGION_LOCATION_RETURN_NAMES)
FUNCTION = CURRENT_FUNCTION
CATEGORY = CURRENT_CATEGORY
NODE_DESC = "int - burn padding location as square (size)"
OUTPUT_NODE = True
def exec(self, w=256, h=256):
return region_burn_corp_location_as_square(w,h)
+15
View File
@@ -0,0 +1,15 @@
[project]
name = "ymc-node-suite-comfyui"
description = "custom nodes for comfyui,like AI painting in comfyui"
version = "3.0.0"
[project.license]
file = "LICENSE"
[project.urls]
Repository = "https://github.com/ymc-github/ymc-node-suite-comfyui"
[tool.comfy]
PublisherId = "ymc-github"
DisplayName = "ymc-node-suite-comfyui"
Icon = ""
+4 -19
View File
@@ -1,19 +1,4 @@
cmake
fairscale>=0.4.4
git+https://github.com/WASasquatch/img2texture.git
gitpython
imageio
joblib
matplotlib
numba
numpy<1.24>=1.18
opencv-python-headless[ffmpeg]<=4.7.0.72
pilgram
git+https://github.com/WASasquatch/ffmpy.git
rembg
scikit-image==0.20.0
scikit-learn
scipy
timm>=0.4.12
tqdm
transformers==4.26.1
yors_comfyui_node_setup
yors_pano_ansi_color
yors_pano_zero_field
yors_pano_list_util
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d1a847354bab2a5e949f420cea7774979bf59cd0ecd711b1f59b4010870849ab
size 145662
+3
View File
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:645c0214b0f25a9f6b2bb150a8291678fac96d762cbbf0b9db06abd81b00129d
size 99853