Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de07ba5679 | ||
|
|
592558d396 | ||
|
|
03f26f2b63 | ||
|
|
5da620cc7c | ||
|
|
d608a9fcf7 | ||
|
|
5260eecd60 | ||
|
|
ca3216f936 | ||
|
|
83379f6ac6 | ||
|
|
7c9c534cfe | ||
|
|
0a503675d0 |
@@ -18,6 +18,9 @@
|
||||
- `Batch Image Loop Open SunxAI`:读取图像批次,按索引逐张送出
|
||||
- `Batch Image Loop Close SunxAI`:接收每次循环结果,自动拼接最终结果
|
||||
|
||||
|
||||

|
||||
|
||||
📌 **特点:**
|
||||
|
||||
- 🚀 **逐帧处理**:每一张图像会被完整处理后,才进入下一张循环(不是在采样时同时执行多帧)
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
|
||||
# ☀️ 如何创建自定义 ComfyUI 节点模块 (`nodes/`)
|
||||
|
||||
## 🧩 文件结构约定
|
||||
|
||||
每一个节点模块放在 `custom_nodes/comfyui_sun_nodes/nodes/` 目录下,每个 `.py` 文件建议只放一组相关节点。
|
||||
|
||||
## 📄 新建节点文件
|
||||
|
||||
例如,你想写一个名为 `mynode_node.py` 的节点:
|
||||
|
||||
```bash
|
||||
touch nodes/mynode_node.py
|
||||
```
|
||||
|
||||
## ✨ 在 `mynode_node.py` 中写入如下格式:
|
||||
|
||||
```python
|
||||
class MyNode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"text": ("STRING", {"default": "Hello"})
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
FUNCTION = "run"
|
||||
CATEGORY = "SunX🌞"
|
||||
|
||||
def run(self, text):
|
||||
return (f"你输入的是: {text}",)
|
||||
|
||||
# 固定后缀写法(前缀随意)
|
||||
mynode_CLASS_MAPPINGS = {
|
||||
"MyNode": MyNode,
|
||||
}
|
||||
|
||||
mynode_DISPLAY_NAME_MAPPINGS = {
|
||||
"MyNode": "🌞 My Custom Node",
|
||||
}
|
||||
```
|
||||
|
||||
## 🧠 命名规范建议
|
||||
|
||||
| 内容 | 建议写法 |
|
||||
| ------------ | ------------------------------------------------------------ |
|
||||
| 类名 | 使用大驼峰,例如 `MyNode` |
|
||||
| 内部节点名 | 不含空格、驼峰或下划线 |
|
||||
| 显示节点名 | 可加 Emoji,中文描述更友好 |
|
||||
| MAPPINGS 变量名 | `xxx_CLASS_MAPPINGS` 和 `xxx_DISPLAY_NAME_MAPPINGS`,前缀随意,后缀固定 |
|
||||
|
||||
---
|
||||
|
||||
## 🧩 在 `__init__.py` 中自动导入所有节点
|
||||
|
||||
路径:`custom_nodes/comfyui_sun_nodes/__init__.py`
|
||||
|
||||
```python
|
||||
import os
|
||||
import importlib
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
|
||||
nodes_path = os.path.join(os.path.dirname(__file__), "nodes")
|
||||
|
||||
for filename in os.listdir(nodes_path):
|
||||
if filename.endswith(".py") and filename != "__init__.py":
|
||||
module_name = f"{__name__}.nodes.{filename[:-3]}"
|
||||
module = importlib.import_module(module_name)
|
||||
|
||||
for attr in dir(module):
|
||||
if attr.endswith("_CLASS_MAPPINGS"):
|
||||
NODE_CLASS_MAPPINGS.update(getattr(module, attr))
|
||||
elif attr.endswith("_DISPLAY_NAME_MAPPINGS"):
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(getattr(module, attr))
|
||||
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 示例效果
|
||||
|
||||
你创建的节点文件如下:
|
||||
|
||||
```bash
|
||||
custom_nodes/
|
||||
└── comfyui_sun_nodes/
|
||||
├── __init__.py
|
||||
└── nodes/
|
||||
├── mynode_node.py ✅
|
||||
└── another_node.py ✅
|
||||
```
|
||||
|
||||
ComfyUI 启动后会自动加载并注册这些节点,不需手动添加字典。
|
||||
|
||||
|
||||
+9
-16
@@ -1,20 +1,13 @@
|
||||
import os
|
||||
import importlib
|
||||
from .nodes.loop_images import BatchImageLoopOpenSun, BatchImageLoopCloseSun
|
||||
|
||||
NODE_CLASS_MAPPINGS = {}
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"SunxAI_BatchImageLoopOpenChen": BatchImageLoopOpenSun,
|
||||
"SunxAI_BatchImageLoopCloseChen": BatchImageLoopCloseSun,
|
||||
}
|
||||
|
||||
nodes_path = os.path.join(os.path.dirname(__file__), "nodes")
|
||||
|
||||
for filename in os.listdir(nodes_path):
|
||||
if filename.endswith(".py") and filename != "__init__.py":
|
||||
module_name = f"{__name__}.nodes.{filename[:-3]}"
|
||||
module = importlib.import_module(module_name)
|
||||
|
||||
for attr in dir(module):
|
||||
if attr.endswith("_CLASS_MAPPINGS"):
|
||||
NODE_CLASS_MAPPINGS.update(getattr(module, attr))
|
||||
elif attr.endswith("_DISPLAY_NAME_MAPPINGS"):
|
||||
NODE_DISPLAY_NAME_MAPPINGS.update(getattr(module, attr))
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"SunxAI_BatchImageLoopOpenChen": "Batch Image Loop Open SunxAI",
|
||||
"SunxAI_BatchImageLoopCloseChen": "Batch Image Loop Close SunxAI",
|
||||
}
|
||||
|
||||
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||
|
||||
@@ -0,0 +1,998 @@
|
||||
{
|
||||
"id": "7cbcec68-7fa6-47bb-a38a-da689949a001",
|
||||
"revision": 0,
|
||||
"last_node_id": 301,
|
||||
"last_link_id": 519,
|
||||
"nodes": [
|
||||
{
|
||||
"id": 6,
|
||||
"type": "CLIPTextEncode",
|
||||
"pos": [
|
||||
13.008308410644531,
|
||||
63.21992492675781
|
||||
],
|
||||
"size": [
|
||||
280.2037048339844,
|
||||
122.77008056640625
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 5,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "clip",
|
||||
"type": "CLIP",
|
||||
"link": 59
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
237,
|
||||
514
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "CLIP Text Encode (Positive Prompt)",
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.38",
|
||||
"Node name for S&R": "CLIPTextEncode",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"Change to anime style while maintaining the original composition"
|
||||
],
|
||||
"color": "#232",
|
||||
"bgcolor": "#353"
|
||||
},
|
||||
{
|
||||
"id": 8,
|
||||
"type": "VAEDecode",
|
||||
"pos": [
|
||||
1239.2454833984375,
|
||||
68.62554931640625
|
||||
],
|
||||
"size": [
|
||||
190,
|
||||
46
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 12,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "samples",
|
||||
"type": "LATENT",
|
||||
"link": 52
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 61
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
513
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.38",
|
||||
"Node name for S&R": "VAEDecode",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 31,
|
||||
"type": "KSampler",
|
||||
"pos": [
|
||||
833.25634765625,
|
||||
43.657920837402344
|
||||
],
|
||||
"size": [
|
||||
304.3830871582031,
|
||||
474
|
||||
],
|
||||
"flags": {},
|
||||
"order": 11,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "model",
|
||||
"type": "MODEL",
|
||||
"link": 300
|
||||
},
|
||||
{
|
||||
"name": "positive",
|
||||
"type": "CONDITIONING",
|
||||
"link": 464
|
||||
},
|
||||
{
|
||||
"name": "negative",
|
||||
"type": "CONDITIONING",
|
||||
"link": 515
|
||||
},
|
||||
{
|
||||
"name": "latent_image",
|
||||
"type": "LATENT",
|
||||
"link": 519
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
52
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.38",
|
||||
"Node name for S&R": "KSampler",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
323524225475238,
|
||||
"randomize",
|
||||
20,
|
||||
1,
|
||||
"euler",
|
||||
"simple",
|
||||
1
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 35,
|
||||
"type": "FluxGuidance",
|
||||
"pos": [
|
||||
505.7422790527344,
|
||||
65.77576446533203
|
||||
],
|
||||
"size": [
|
||||
210,
|
||||
58
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 10,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 292
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"slot_index": 0,
|
||||
"links": [
|
||||
464
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.38",
|
||||
"Node name for S&R": "FluxGuidance",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
2.5
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 37,
|
||||
"type": "UNETLoader",
|
||||
"pos": [
|
||||
-525.5791015625,
|
||||
114.7538070678711
|
||||
],
|
||||
"size": [
|
||||
337.76861572265625,
|
||||
82
|
||||
],
|
||||
"flags": {},
|
||||
"order": 2,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "MODEL",
|
||||
"type": "MODEL",
|
||||
"links": [
|
||||
300
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.38",
|
||||
"Node name for S&R": "UNETLoader",
|
||||
"models": [
|
||||
{
|
||||
"name": "flux1-dev-kontext_fp8_scaled.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/flux1-kontext-dev_ComfyUI/resolve/main/split_files/diffusion_models/flux1-dev-kontext_fp8_scaled.safetensors",
|
||||
"directory": "diffusion_models"
|
||||
}
|
||||
],
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"flux1-kontext-dev.safetensors",
|
||||
"fp8_e4m3fn"
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
},
|
||||
{
|
||||
"id": 38,
|
||||
"type": "DualCLIPLoader",
|
||||
"pos": [
|
||||
-525.5791015625,
|
||||
244.75389099121094
|
||||
],
|
||||
"size": [
|
||||
337.76861572265625,
|
||||
130
|
||||
],
|
||||
"flags": {},
|
||||
"order": 0,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CLIP",
|
||||
"type": "CLIP",
|
||||
"links": [
|
||||
59
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.38",
|
||||
"Node name for S&R": "DualCLIPLoader",
|
||||
"models": [
|
||||
{
|
||||
"name": "clip_l.safetensors",
|
||||
"url": "https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/clip_l.safetensors",
|
||||
"directory": "text_encoders"
|
||||
},
|
||||
{
|
||||
"name": "t5xxl_fp8_e4m3fn_scaled.safetensors",
|
||||
"url": "https://huggingface.co/comfyanonymous/flux_text_encoders/resolve/main/t5xxl_fp8_e4m3fn_scaled.safetensors",
|
||||
"directory": "text_encoders"
|
||||
}
|
||||
],
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"clip_l.safetensors",
|
||||
"t5xxl_fp8_e4m3fn.safetensors",
|
||||
"flux",
|
||||
"default"
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
},
|
||||
{
|
||||
"id": 39,
|
||||
"type": "VAELoader",
|
||||
"pos": [
|
||||
-525.5791015625,
|
||||
424.7539367675781
|
||||
],
|
||||
"size": [
|
||||
337.76861572265625,
|
||||
58
|
||||
],
|
||||
"flags": {},
|
||||
"order": 1,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "VAE",
|
||||
"type": "VAE",
|
||||
"links": [
|
||||
61,
|
||||
517
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.38",
|
||||
"Node name for S&R": "VAELoader",
|
||||
"models": [
|
||||
{
|
||||
"name": "ae.safetensors",
|
||||
"url": "https://huggingface.co/Comfy-Org/Lumina_Image_2.0_Repackaged/resolve/main/split_files/vae/ae.safetensors",
|
||||
"directory": "vae"
|
||||
}
|
||||
],
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"FLUX/flux_vae.safetensors"
|
||||
],
|
||||
"color": "#322",
|
||||
"bgcolor": "#533"
|
||||
},
|
||||
{
|
||||
"id": 135,
|
||||
"type": "ConditioningZeroOut",
|
||||
"pos": [
|
||||
543.3994750976562,
|
||||
167.55950927734375
|
||||
],
|
||||
"size": [
|
||||
240,
|
||||
26
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": true
|
||||
},
|
||||
"order": 7,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 237
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
515
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.39",
|
||||
"Node name for S&R": "ConditioningZeroOut",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 177,
|
||||
"type": "ReferenceLatent",
|
||||
"pos": [
|
||||
333.2911376953125,
|
||||
70.3294906616211
|
||||
],
|
||||
"size": [
|
||||
143.41659545898438,
|
||||
46
|
||||
],
|
||||
"flags": {
|
||||
"collapsed": false
|
||||
},
|
||||
"order": 9,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "conditioning",
|
||||
"type": "CONDITIONING",
|
||||
"link": 514
|
||||
},
|
||||
{
|
||||
"name": "latent",
|
||||
"shape": 7,
|
||||
"type": "LATENT",
|
||||
"link": 518
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "CONDITIONING",
|
||||
"type": "CONDITIONING",
|
||||
"links": [
|
||||
292
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.41",
|
||||
"Node name for S&R": "ReferenceLatent",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 184,
|
||||
"type": "MarkdownNote",
|
||||
"pos": [
|
||||
-1111.9537353515625,
|
||||
66.36994934082031
|
||||
],
|
||||
"size": [
|
||||
510.0960998535156,
|
||||
116.33981323242188
|
||||
],
|
||||
"flags": {},
|
||||
"order": 4,
|
||||
"mode": 0,
|
||||
"inputs": [],
|
||||
"outputs": [],
|
||||
"title": "Model links",
|
||||
"properties": {
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
"A单图处理,会将单图处理完毕再进行下一张,不会将多张都卡在采样器,\n\n循环你可以加载视频或者图片序列都行。"
|
||||
],
|
||||
"color": "#432",
|
||||
"bgcolor": "#653"
|
||||
},
|
||||
{
|
||||
"id": 210,
|
||||
"type": "VHS_LoadVideo",
|
||||
"pos": [
|
||||
-550.1968994140625,
|
||||
655.0445556640625
|
||||
],
|
||||
"size": [
|
||||
252.056640625,
|
||||
672.0849609375
|
||||
],
|
||||
"flags": {},
|
||||
"order": 3,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "meta_batch",
|
||||
"shape": 7,
|
||||
"type": "VHS_BatchManager",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"shape": 7,
|
||||
"type": "VAE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "IMAGE",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
511
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "frame_count",
|
||||
"type": "INT",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "audio",
|
||||
"type": "AUDIO",
|
||||
"links": null
|
||||
},
|
||||
{
|
||||
"name": "video_info",
|
||||
"type": "VHS_VIDEOINFO",
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"aux_id": "upseem/comfyui_sun_nodes",
|
||||
"ver": "972c87da577b47211c4e9aeed30dc38c7bae607f",
|
||||
"Node name for S&R": "VHS_LoadVideo",
|
||||
"cnr_id": "comfyui-videohelpersuite",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": {
|
||||
"video": "1.mp4",
|
||||
"force_rate": 0,
|
||||
"custom_width": 0,
|
||||
"custom_height": 0,
|
||||
"frame_load_cap": 0,
|
||||
"skip_first_frames": 0,
|
||||
"select_every_nth": 1,
|
||||
"format": "AnimateDiff",
|
||||
"choose video to upload": "image",
|
||||
"videopreview": {
|
||||
"hidden": false,
|
||||
"paused": false,
|
||||
"params": {
|
||||
"filename": "1.mp4",
|
||||
"type": "input",
|
||||
"format": "video/mp4",
|
||||
"force_rate": 0,
|
||||
"custom_width": 0,
|
||||
"custom_height": 0,
|
||||
"frame_load_cap": 0,
|
||||
"skip_first_frames": 0,
|
||||
"select_every_nth": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 275,
|
||||
"type": "VHS_VideoCombine",
|
||||
"pos": [
|
||||
1217.2215576171875,
|
||||
634.74658203125
|
||||
],
|
||||
"size": [
|
||||
219.3603515625,
|
||||
647.04052734375
|
||||
],
|
||||
"flags": {},
|
||||
"order": 14,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"label": "images",
|
||||
"name": "images",
|
||||
"type": "IMAGE",
|
||||
"link": 507
|
||||
},
|
||||
{
|
||||
"label": "audio",
|
||||
"name": "audio",
|
||||
"shape": 7,
|
||||
"type": "AUDIO",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "meta_batch",
|
||||
"name": "meta_batch",
|
||||
"shape": 7,
|
||||
"type": "VHS_BatchManager",
|
||||
"link": null
|
||||
},
|
||||
{
|
||||
"label": "vae",
|
||||
"name": "vae",
|
||||
"shape": 7,
|
||||
"type": "VAE",
|
||||
"link": null
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"label": "Filenames",
|
||||
"name": "Filenames",
|
||||
"type": "VHS_FILENAMES",
|
||||
"slot_index": 0,
|
||||
"links": null
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"aux_id": "upseem/comfyui_sun_nodes",
|
||||
"ver": "1.5.8",
|
||||
"Node name for S&R": "VHS_VideoCombine",
|
||||
"cnr_id": "comfyui-videohelpersuite",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": {
|
||||
"frame_rate": 30,
|
||||
"loop_count": 0,
|
||||
"filename_prefix": "mp4/test",
|
||||
"format": "video/h264-mp4",
|
||||
"pix_fmt": "yuv420p",
|
||||
"crf": 18,
|
||||
"save_metadata": true,
|
||||
"trim_to_audio": false,
|
||||
"pingpong": false,
|
||||
"save_output": true,
|
||||
"videopreview": {
|
||||
"hidden": false,
|
||||
"paused": false,
|
||||
"params": {
|
||||
"filename": "aa_00101.mp4",
|
||||
"subfolder": "mp4",
|
||||
"type": "output",
|
||||
"format": "video/h264-mp4",
|
||||
"frame_rate": 3,
|
||||
"workflow": "aa_00101.png",
|
||||
"fullpath": "/root/autodl-tmp/ComfyUI/output/mp4/aa_00101.mp4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": 298,
|
||||
"type": "SunxAI_BatchImageLoopOpenChen",
|
||||
"pos": [
|
||||
-136.82192993164062,
|
||||
654.65673828125
|
||||
],
|
||||
"size": [
|
||||
249.73828125,
|
||||
86
|
||||
],
|
||||
"flags": {},
|
||||
"order": 6,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "segmented_images",
|
||||
"type": "IMAGE",
|
||||
"link": 511
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "FLOW_CONTROL",
|
||||
"type": "FLOW_CONTROL",
|
||||
"links": [
|
||||
503
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "current_image",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
516
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "max_iterations",
|
||||
"type": "INT",
|
||||
"links": [
|
||||
504
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "iteration_count",
|
||||
"type": "INT",
|
||||
"links": []
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"aux_id": "upseem/comfyui_sun_nodes",
|
||||
"ver": "107ce434ed1cebb9dd9d881fd5b2460d4d74db7c",
|
||||
"Node name for S&R": "SunxAI_BatchImageLoopOpenChen",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": []
|
||||
},
|
||||
{
|
||||
"id": 299,
|
||||
"type": "SunxAI_BatchImageLoopCloseChen",
|
||||
"pos": [
|
||||
847.4674072265625,
|
||||
638.669921875
|
||||
],
|
||||
"size": [
|
||||
302.2437438964844,
|
||||
98
|
||||
],
|
||||
"flags": {},
|
||||
"order": 13,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "flow_control",
|
||||
"type": "FLOW_CONTROL",
|
||||
"link": 503
|
||||
},
|
||||
{
|
||||
"name": "current_image",
|
||||
"type": "IMAGE",
|
||||
"link": 513
|
||||
},
|
||||
{
|
||||
"name": "max_iterations",
|
||||
"type": "INT",
|
||||
"link": 504
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "result_images",
|
||||
"type": "IMAGE",
|
||||
"links": [
|
||||
507
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"aux_id": "upseem/comfyui_sun_nodes",
|
||||
"ver": "107ce434ed1cebb9dd9d881fd5b2460d4d74db7c",
|
||||
"Node name for S&R": "SunxAI_BatchImageLoopCloseChen",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": [
|
||||
false
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 300,
|
||||
"type": "VAEEncode",
|
||||
"pos": [
|
||||
119.16875457763672,
|
||||
266.8257141113281
|
||||
],
|
||||
"size": [
|
||||
140,
|
||||
46
|
||||
],
|
||||
"flags": {},
|
||||
"order": 8,
|
||||
"mode": 0,
|
||||
"inputs": [
|
||||
{
|
||||
"name": "pixels",
|
||||
"type": "IMAGE",
|
||||
"link": 516
|
||||
},
|
||||
{
|
||||
"name": "vae",
|
||||
"type": "VAE",
|
||||
"link": 517
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "LATENT",
|
||||
"type": "LATENT",
|
||||
"links": [
|
||||
518,
|
||||
519
|
||||
]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
"cnr_id": "comfy-core",
|
||||
"ver": "0.3.42",
|
||||
"Node name for S&R": "VAEEncode",
|
||||
"widget_ue_connectable": {}
|
||||
},
|
||||
"widgets_values": []
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
[
|
||||
52,
|
||||
31,
|
||||
0,
|
||||
8,
|
||||
0,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
59,
|
||||
38,
|
||||
0,
|
||||
6,
|
||||
0,
|
||||
"CLIP"
|
||||
],
|
||||
[
|
||||
61,
|
||||
39,
|
||||
0,
|
||||
8,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
237,
|
||||
6,
|
||||
0,
|
||||
135,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
292,
|
||||
177,
|
||||
0,
|
||||
35,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
300,
|
||||
37,
|
||||
0,
|
||||
31,
|
||||
0,
|
||||
"MODEL"
|
||||
],
|
||||
[
|
||||
464,
|
||||
35,
|
||||
0,
|
||||
31,
|
||||
1,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
503,
|
||||
298,
|
||||
0,
|
||||
299,
|
||||
0,
|
||||
"FLOW_CONTROL"
|
||||
],
|
||||
[
|
||||
504,
|
||||
298,
|
||||
2,
|
||||
299,
|
||||
2,
|
||||
"INT"
|
||||
],
|
||||
[
|
||||
507,
|
||||
299,
|
||||
0,
|
||||
275,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
511,
|
||||
210,
|
||||
0,
|
||||
298,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
513,
|
||||
8,
|
||||
0,
|
||||
299,
|
||||
1,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
514,
|
||||
6,
|
||||
0,
|
||||
177,
|
||||
0,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
515,
|
||||
135,
|
||||
0,
|
||||
31,
|
||||
2,
|
||||
"CONDITIONING"
|
||||
],
|
||||
[
|
||||
516,
|
||||
298,
|
||||
1,
|
||||
300,
|
||||
0,
|
||||
"IMAGE"
|
||||
],
|
||||
[
|
||||
517,
|
||||
39,
|
||||
0,
|
||||
300,
|
||||
1,
|
||||
"VAE"
|
||||
],
|
||||
[
|
||||
518,
|
||||
300,
|
||||
0,
|
||||
177,
|
||||
1,
|
||||
"LATENT"
|
||||
],
|
||||
[
|
||||
519,
|
||||
300,
|
||||
0,
|
||||
31,
|
||||
3,
|
||||
"LATENT"
|
||||
]
|
||||
],
|
||||
"groups": [
|
||||
{
|
||||
"id": 1,
|
||||
"title": "Step 1- Load models",
|
||||
"bounding": [
|
||||
-535.5791015625,
|
||||
44.753883361816406,
|
||||
360,
|
||||
450
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"title": "循环执行",
|
||||
"bounding": [
|
||||
-588.2969970703125,
|
||||
564.1963500976562,
|
||||
2152.915283203125,
|
||||
792.60986328125
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"title": "A:处理单张图片流程",
|
||||
"bounding": [
|
||||
-578.388916015625,
|
||||
-9.562874794006348,
|
||||
2136.165283203125,
|
||||
532.2862548828125
|
||||
],
|
||||
"color": "#3f789e",
|
||||
"font_size": 24,
|
||||
"flags": {}
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"title": "✅SUNXAI 循环节点",
|
||||
"bounding": [
|
||||
-565.919921875,
|
||||
-272.7617492675781,
|
||||
2125.043701171875,
|
||||
224.5164031982422
|
||||
],
|
||||
"color": "#a1309b",
|
||||
"font_size": 160,
|
||||
"flags": {}
|
||||
}
|
||||
],
|
||||
"config": {},
|
||||
"extra": {
|
||||
"ds": {
|
||||
"scale": 0.45000000000000245,
|
||||
"offset": [
|
||||
1211.9537353515625,
|
||||
56.342079162597656
|
||||
]
|
||||
},
|
||||
"frontendVersion": "1.23.4",
|
||||
"groupNodes": {},
|
||||
"VHS_latentpreview": false,
|
||||
"VHS_latentpreviewrate": 0,
|
||||
"VHS_MetadataImage": true,
|
||||
"VHS_KeepIntermediate": true,
|
||||
"ue_links": [],
|
||||
"links_added_by_ue": []
|
||||
},
|
||||
"version": 0.4
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
@@ -0,0 +1,4 @@
|
||||
from .loop_images import BatchImageLoopOpenSun, BatchImageLoopCloseSun
|
||||
|
||||
__all__ = ["BatchImageLoopOpenSun", "BatchImageLoopCloseSun"]
|
||||
|
||||
+58
-44
@@ -1,5 +1,11 @@
|
||||
import os
|
||||
import uuid
|
||||
from PIL import Image
|
||||
import numpy as np
|
||||
|
||||
from comfy_execution.graph_utils import GraphBuilder, is_link
|
||||
import folder_paths
|
||||
|
||||
from ..tools.tools import VariantSupport
|
||||
import torch.nn.functional as F
|
||||
import torch
|
||||
@@ -16,17 +22,19 @@ class BatchImageLoopOpenSun:
|
||||
inputs = {
|
||||
"required": {
|
||||
"segmented_images": ("IMAGE", {"forceInput": True}),
|
||||
"output_dir": ("STRING", {"default": ""}),
|
||||
},
|
||||
"hidden": {
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"iteration_count": ("INT", {"default": 0}),
|
||||
"previous_image": ("IMAGE",),
|
||||
"batch_id": ("STRING",),
|
||||
}
|
||||
}
|
||||
return inputs
|
||||
|
||||
RETURN_TYPES = ("FLOW_CONTROL", "IMAGE", "INT", "INT")
|
||||
RETURN_NAMES = ("FLOW_CONTROL", "current_image", "max_iterations", "iteration_count")
|
||||
RETURN_TYPES = ("FLOW_CONTROL", "IMAGE", "INT", "INT", "STRING")
|
||||
RETURN_NAMES = ("FLOW_CONTROL", "current_image", "max_iterations", "iteration_count", "batch_path")
|
||||
FUNCTION = "while_loop_open"
|
||||
CATEGORY = "CyberEveLoop🐰·Chen定制"
|
||||
|
||||
@@ -47,12 +55,25 @@ class BatchImageLoopOpenSun:
|
||||
image = image.permute(0, 2, 3, 1)
|
||||
return image
|
||||
|
||||
def while_loop_open(self, segmented_images, unique_id=None, iteration_count=0, previous_image=None):
|
||||
def while_loop_open(self, segmented_images, output_dir="", unique_id=None, iteration_count=0, previous_image=None, batch_id=None):
|
||||
print(f"[chen] Loop iteration: {iteration_count}")
|
||||
|
||||
images = self.standardize_images(segmented_images)
|
||||
max_iterations = images.shape[0]
|
||||
|
||||
if batch_id is None:
|
||||
batch_id = uuid.uuid4().hex[:8]
|
||||
|
||||
base_output = folder_paths.get_output_directory()
|
||||
if output_dir:
|
||||
batch_path = os.path.join(base_output, output_dir, batch_id)
|
||||
else:
|
||||
batch_path = os.path.join(base_output, "loop_batch", batch_id)
|
||||
|
||||
if iteration_count == 0:
|
||||
os.makedirs(batch_path, exist_ok=True)
|
||||
print(f"[chen] Created batch directory: {batch_path}")
|
||||
|
||||
if iteration_count >= max_iterations:
|
||||
raise ValueError(f"[chen] Iteration {iteration_count} exceeds max {max_iterations}")
|
||||
|
||||
@@ -62,7 +83,7 @@ class BatchImageLoopOpenSun:
|
||||
images[idx:idx+1] = previous_image
|
||||
|
||||
current_image = images[iteration_count:iteration_count+1]
|
||||
return ("stub", current_image, max_iterations, iteration_count)
|
||||
return ("stub", current_image, max_iterations, iteration_count, batch_path)
|
||||
|
||||
|
||||
@VariantSupport()
|
||||
@@ -77,6 +98,8 @@ class BatchImageLoopCloseSun:
|
||||
"flow_control": ("FLOW_CONTROL", {"rawLink": True}),
|
||||
"current_image": ("IMAGE",),
|
||||
"max_iterations": ("INT", {"forceInput": True}),
|
||||
"iteration_count": ("INT", {"forceInput": True}),
|
||||
"batch_path": ("STRING", {"forceInput": True}),
|
||||
},
|
||||
"optional": {
|
||||
"pass_back": ("BOOLEAN", {"default": False}),
|
||||
@@ -84,13 +107,11 @@ class BatchImageLoopCloseSun:
|
||||
"hidden": {
|
||||
"dynprompt": "DYNPROMPT",
|
||||
"unique_id": "UNIQUE_ID",
|
||||
"result_images": ("IMAGE",),
|
||||
"iteration_count": ("INT", {"default": 0}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("IMAGE",)
|
||||
RETURN_NAMES = ("result_images",)
|
||||
RETURN_TYPES = ("IMAGE", "STRING")
|
||||
RETURN_NAMES = ("result_images", "output_path")
|
||||
FUNCTION = "while_loop_close"
|
||||
CATEGORY = "CyberEveLoop🐰·Chen定制"
|
||||
|
||||
@@ -100,37 +121,44 @@ class BatchImageLoopCloseSun:
|
||||
assert len(image.shape) == 4, f"Image must be 4D [B,H,W,C], got {image.shape}"
|
||||
return image
|
||||
|
||||
def initialize_results(self, max_iterations, current_image):
|
||||
assert len(current_image.shape) == 4
|
||||
return torch.zeros(
|
||||
(max_iterations, *current_image.shape[1:]),
|
||||
dtype=current_image.dtype,
|
||||
device=current_image.device
|
||||
)
|
||||
def save_image(self, image, batch_path, index):
|
||||
filepath = os.path.join(batch_path, f"{index:05d}.png")
|
||||
if os.path.exists(filepath):
|
||||
print(f"[chen] Skip (exists): {filepath}")
|
||||
return
|
||||
img_np = (image.squeeze(0).cpu().numpy() * 255).clip(0, 255).astype(np.uint8)
|
||||
img_pil = Image.fromarray(img_np)
|
||||
img_pil.save(filepath)
|
||||
print(f"[chen] Saved: {filepath}")
|
||||
|
||||
def while_loop_close(self, flow_control, current_image, max_iterations,
|
||||
pass_back=False, iteration_count=0,
|
||||
result_images=None, dynprompt=None, unique_id=None):
|
||||
def load_all_images(self, batch_path, max_iterations, device):
|
||||
results = []
|
||||
for i in range(max_iterations):
|
||||
filepath = os.path.join(batch_path, f"{i:05d}.png")
|
||||
img_pil = Image.open(filepath).convert("RGB")
|
||||
img_np = np.array(img_pil).astype(np.float32) / 255.0
|
||||
img_tensor = torch.from_numpy(img_np).unsqueeze(0)
|
||||
results.append(img_tensor)
|
||||
return torch.cat(results, dim=0).to(device)
|
||||
|
||||
def while_loop_close(self, flow_control, current_image, max_iterations, batch_path,
|
||||
iteration_count, pass_back=False,
|
||||
dynprompt=None, unique_id=None):
|
||||
print(f"[chen] Iteration {iteration_count} / {max_iterations}")
|
||||
|
||||
current_image = self.standardize_image(current_image)
|
||||
device = current_image.device
|
||||
|
||||
if iteration_count >= max_iterations:
|
||||
raise ValueError(f"[chen] Iteration {iteration_count} exceeds max {max_iterations}")
|
||||
|
||||
if result_images is None:
|
||||
result_images = self.initialize_results(max_iterations, current_image)
|
||||
else:
|
||||
assert result_images.shape[0] == max_iterations
|
||||
|
||||
result_images[iteration_count:iteration_count+1] = current_image
|
||||
self.save_image(current_image, batch_path, iteration_count)
|
||||
|
||||
if iteration_count == max_iterations - 1:
|
||||
print(f"[chen] Loop finished")
|
||||
return (result_images,)
|
||||
print(f"[chen] Loop finished, loading results from {batch_path}")
|
||||
result_images = self.load_all_images(batch_path, max_iterations, device)
|
||||
return (result_images, batch_path)
|
||||
|
||||
# 构建图用于下一轮迭代
|
||||
this_node = dynprompt.get_node(unique_id)
|
||||
open_node = flow_control[0]
|
||||
|
||||
upstream = {}
|
||||
@@ -175,15 +203,15 @@ class BatchImageLoopCloseSun:
|
||||
|
||||
my_clone = graph.lookup_node("Recurse")
|
||||
my_clone.set_input("iteration_count", iteration_count + 1)
|
||||
my_clone.set_input("result_images", result_images)
|
||||
|
||||
new_open = graph.lookup_node(open_node)
|
||||
new_open.set_input("iteration_count", iteration_count + 1)
|
||||
new_open.set_input("batch_id", os.path.basename(batch_path))
|
||||
if pass_back:
|
||||
new_open.set_input("previous_image", current_image)
|
||||
|
||||
return {
|
||||
"result": (my_clone.out(0),),
|
||||
"result": (my_clone.out(0), my_clone.out(1)),
|
||||
"expand": graph.finalize()
|
||||
}
|
||||
|
||||
@@ -222,17 +250,3 @@ class BatchImageLoopCloseSun:
|
||||
if child_id not in contained:
|
||||
contained[child_id] = True
|
||||
self.collect_contained(child_id, upstream, contained)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Loop_CLASS_MAPPINGS = {
|
||||
"SunxAI_BatchImageLoopOpenChen": BatchImageLoopOpenSun,
|
||||
"SunxAI_BatchImageLoopCloseChen": BatchImageLoopCloseSun,
|
||||
}
|
||||
|
||||
Loop_DISPLAY_NAME_MAPPINGS = {
|
||||
"SunxAI_BatchImageLoopOpenChen": "Batch Image Loop Open SunxAI",
|
||||
"SunxAI_BatchImageLoopCloseChen": "Batch Image Loop Close SunxAI",
|
||||
}
|
||||
|
||||
+10
-35
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "comfyui_sun_nodes"
|
||||
version = "0.0.5"
|
||||
version = "0.1.6"
|
||||
description = "Custom ComfyUI nodes by SunX.AI"
|
||||
authors = [
|
||||
{name = "SunX AI", email = "sunx.ai@hotmail.com"}
|
||||
@@ -12,20 +12,14 @@ authors = [
|
||||
readme = "README.md"
|
||||
license = {text = "MIT license"}
|
||||
classifiers = []
|
||||
dependencies = [
|
||||
|
||||
]
|
||||
|
||||
|
||||
dependencies = []
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"bump-my-version",
|
||||
"coverage", # testing
|
||||
"mypy", # linting
|
||||
"pre-commit", # runs linting on commit
|
||||
"pytest", # testing
|
||||
"ruff", # linting
|
||||
"mypy",
|
||||
"pre-commit",
|
||||
"ruff",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
@@ -33,50 +27,31 @@ bugs = "https://github.com/upseem/comfyui_sun_nodes/issues"
|
||||
homepage = "https://github.com/upseem/comfyui_sun_nodes"
|
||||
Repository = "https://github.com/upseem/comfyui_sun_nodes"
|
||||
|
||||
|
||||
[tool.comfy]
|
||||
PublisherId = "sunxai"
|
||||
DisplayName = "comfyui_Sun_nodes"
|
||||
DisplayName = "ComfyUI Sun Nodes"
|
||||
Icon = "https://avatars.githubusercontent.com/u/124853686"
|
||||
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
"*" = ["*.*"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
minversion = "8.0"
|
||||
testpaths = [
|
||||
"tests",
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
files = "."
|
||||
|
||||
# Use strict defaults
|
||||
strict = true
|
||||
warn_unreachable = true
|
||||
warn_no_return = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
# Don't require test functions to include types
|
||||
module = "tests.*"
|
||||
allow_untyped_defs = true
|
||||
disable_error_code = "attr-defined"
|
||||
|
||||
[tool.ruff]
|
||||
# extend-exclude = ["static", "ci/templates"]
|
||||
line-length = 140
|
||||
src = ["src", "tests"]
|
||||
src = ["nodes", "tools"]
|
||||
target-version = "py39"
|
||||
|
||||
# Add rules to ban exec/eval
|
||||
[tool.ruff.lint]
|
||||
select = [
|
||||
"S102", # exec-builtin
|
||||
"S307", # eval-used
|
||||
"S102",
|
||||
"S307",
|
||||
"W293",
|
||||
"F", # The "F" series in Ruff stands for "Pyflakes" rules, which catch various Python syntax errors and undefined names.
|
||||
# See all rules here: https://docs.astral.sh/ruff/rules/#pyflakes-f
|
||||
"F",
|
||||
]
|
||||
|
||||
[tool.ruff.lint.flake8-quotes]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
"""Unit test package for comfyui_sun_nodes."""
|
||||
@@ -1,6 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
# Add the project root directory to Python path
|
||||
# This allows the tests to import the project
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
||||
@@ -1,4 +0,0 @@
|
||||
[pytest]
|
||||
testpaths = . # Run tests in the current directory
|
||||
python_files = test_*.py # Run tests in files that start with "test_"
|
||||
norecursedirs = .. # Don't run tests in the parent directory
|
||||
@@ -1,21 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""Tests for `comfyui_sun_nodes` package."""
|
||||
|
||||
import pytest
|
||||
from src.comfyui_sun_nodes.nodes import Example
|
||||
|
||||
@pytest.fixture
|
||||
def example_node():
|
||||
"""Fixture to create an Example node instance."""
|
||||
return Example()
|
||||
|
||||
def test_example_node_initialization(example_node):
|
||||
"""Test that the node can be instantiated."""
|
||||
assert isinstance(example_node, Example)
|
||||
|
||||
def test_return_types():
|
||||
"""Test the node's metadata."""
|
||||
assert Example.RETURN_TYPES == ("IMAGE",)
|
||||
assert Example.FUNCTION == "test"
|
||||
assert Example.CATEGORY == "Example"
|
||||
Reference in New Issue
Block a user