添加DD TXT文件合并器节点
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# ComfyUI-DD-Nodes Developer Guide
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
This is a **ComfyUI extension** with hybrid Python/JavaScript architecture:
|
||||
- **Backend**: Python nodes in `/node/` (image processing, AI workflows)
|
||||
- **Frontend**: JavaScript extensions in `/extensions/` (UI enhancements, animations, APIs)
|
||||
- **Localization**: Bilingual support via `/locales/zh/` and `/locales/en/`
|
||||
|
||||
## Key Development Patterns
|
||||
|
||||
### Adding New Nodes
|
||||
1. Create Python class in `/node/` with ComfyUI conventions:
|
||||
```python
|
||||
class YourNode:
|
||||
@classmethod
|
||||
def INPUT_TYPES(s):
|
||||
return {"required": {"input": ("TYPE",)}}
|
||||
|
||||
RETURN_TYPES = ("OUTPUT_TYPE",)
|
||||
FUNCTION = "process"
|
||||
CATEGORY = "🍺DD系列节点"
|
||||
```
|
||||
|
||||
2. Export in `NODE_CLASS_MAPPINGS = {"DD-YourNode": YourNode}`
|
||||
3. Import in `__init__.py` and add to main `NODE_CLASS_MAPPINGS`
|
||||
4. Add Chinese translations to `locales/zh/nodeDefs.json`
|
||||
|
||||
### Frontend Extensions
|
||||
Create extension in `/extensions/YourExt/`:
|
||||
```javascript
|
||||
import { app } from "/scripts/app.js";
|
||||
|
||||
app.registerExtension({
|
||||
name: "ComfyUI.YourExtension",
|
||||
setup() {
|
||||
// Initialization code
|
||||
},
|
||||
beforeRegisterNodeDef(nodeType, nodeData, app) {
|
||||
// Hook into node creation
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Adding API Endpoints
|
||||
In Python modules, use ComfyUI's PromptServer:
|
||||
```python
|
||||
from server import PromptServer
|
||||
|
||||
@PromptServer.instance.routes.post("/your_api/endpoint")
|
||||
async def your_handler(request):
|
||||
return web.json_response({"success": True})
|
||||
```
|
||||
|
||||
## Critical Conventions
|
||||
|
||||
### Canvas Monkey-Patching
|
||||
When modifying ComfyUI's canvas behavior, **always preserve original methods**:
|
||||
```javascript
|
||||
const originalMethod = app.canvas.originalMethod;
|
||||
app.canvas.originalMethod = function(...args) {
|
||||
// Your enhancement
|
||||
return originalMethod?.call(this, ...args);
|
||||
};
|
||||
```
|
||||
|
||||
### Settings Integration
|
||||
Use ComfyUI's built-in settings system:
|
||||
```javascript
|
||||
app.extensionManager.setting.get("YourExt.setting") ?? defaultValue
|
||||
app.extensionManager.setting.onChange("YourExt.setting", callback)
|
||||
```
|
||||
|
||||
### Theme Architecture
|
||||
Themes must implement interface: `{init, applyTheme, flipCoin, cleanup, name}`
|
||||
Register via: `registerTheme('theme-id', themeModule)`
|
||||
|
||||
## Performance Patterns
|
||||
|
||||
### Spatial Optimization
|
||||
For canvas operations affecting many nodes, use spatial partitioning (see `StaticCircuitBoardStyle.js`):
|
||||
- Build spatial grid index for O(1) lookups
|
||||
- Use Bresenham line algorithm for collision detection
|
||||
- Dynamic grid sizing based on node count
|
||||
|
||||
### Lazy Loading
|
||||
Extensions use singleton patterns:
|
||||
```javascript
|
||||
function getOrCreateInstance() {
|
||||
if (!app.canvas._yourInstance) {
|
||||
app.canvas._yourInstance = new YourClass();
|
||||
}
|
||||
return app.canvas._yourInstance;
|
||||
}
|
||||
```
|
||||
|
||||
## File Structure Conventions
|
||||
|
||||
```
|
||||
/node/ # Python backend nodes
|
||||
/extensions/ # Frontend JavaScript modules
|
||||
/YourExt/
|
||||
YourExtExt.js # Extension entry (imported in index.js)
|
||||
YourExtCore.js # Core functionality
|
||||
/styles/ # UI styling and utilities
|
||||
/locales/zh/ # Chinese translations
|
||||
nodeDefs.json # Node translations
|
||||
main.json # UI translations
|
||||
```
|
||||
|
||||
## Development Workflow
|
||||
|
||||
1. **No build system** - Direct ES6 modules, hot-reload friendly
|
||||
2. **Testing**: Restart ComfyUI to test Python changes, refresh for JS changes
|
||||
3. **Dependencies**: Add to `requirements.txt`, use npm-style imports for JS
|
||||
4. **Debugging**: Use browser DevTools for frontend, Python print/logging for backend
|
||||
|
||||
## Integration Points
|
||||
|
||||
- Node widgets auto-added via `beforeRegisterNodeDef` hooks
|
||||
- Settings persist via ComfyUI's storage system
|
||||
- API calls use `/scripts/api.js` from ComfyUI core
|
||||
- Internationalization follows ComfyUI's i18n system
|
||||
- Extension loading via `WEB_DIRECTORY = "extensions"` in `__init__.py`
|
||||
|
||||
When adding features, follow existing patterns in `/extensions/Prompt_Manager/` (API integration) or `/extensions/Online_Animation/` (canvas enhancement) as reference implementations.
|
||||
+8
-3
@@ -31,6 +31,9 @@
|
||||
| **DD Image Size Limiter** | Smart image size limiter ensuring images are within specified maximum and minimum size ranges, preventing memory overflow and performance issues |  |
|
||||
| **DD Switcher Series** | Includes condition switcher, latent switcher, model switcher and other switching nodes, simplifying workflows and improving processing flexibility |  |
|
||||
| **DD Video First Last Frame** | Professional video frame extraction tool that can precisely extract the first and last frames of videos, providing convenience for video processing workflows |  |
|
||||
| **DD Image Splitter** | Intelligent image splitting tool that supports splitting images into multiple parts by custom ratios. Supports horizontal (left-right) or vertical (top-bottom) splitting, supports unequal ratio splitting (like 2:1:3), and can output specified split results |  |
|
||||
| **DD Aspect Ratio Selector** | Intelligent aspect ratio selection tool that provides recommended resolutions for different AI models (such as Qwen-image, Wan2.2). Supports three ratio categories: landscape, portrait, and square, automatically providing the most suitable size parameters for the model |  |
|
||||
| **DD TXT File Merger** | Powerful text file merging tool that can recursively scan all TXT files in specified folders and subfolders, merging them into a single text content. Automatically adds file path identifiers and supports multiple text encoding formats |  |
|
||||
|
||||
## 🎯 Extension Functions Overview
|
||||
|
||||
@@ -62,9 +65,11 @@ This extension now supports ComfyUI's native internationalization (i18n) system
|
||||
## 📈 Version History
|
||||
|
||||
### Latest Version
|
||||
- **v2.6.0** (2025-07-06)
|
||||
- 🔧 Attempted to fix multi-system compatibility bugs in interface layout panel
|
||||
- ➕ Added video first and last frame output node
|
||||
- **v3.1.0** (2025-09-21)
|
||||
- Added DD TXT File Merger node, supports recursive scanning of all TXT files in folders and subfolders and merging them into single text content
|
||||
|
||||
- **v3.0.0** (2025-09-05)
|
||||
- Added DD Image Splitter node, supports splitting images by custom ratios and outputting specified parts
|
||||
|
||||
### Important Updates
|
||||
- **v2.5.1** (2025-07-02)
|
||||
|
||||
@@ -41,6 +41,9 @@
|
||||
| **DD 限制图像大小** | 智能图像尺寸限制器,确保图像在指定的最大和最小尺寸范围内,防止内存溢出和性能问题 |  |
|
||||
| **DD 切换器系列** | 包含条件切换器、Latent切换器、模型切换器等多种切换节点,简化工作流程,提高处理灵活性 |  |
|
||||
| **DD 视频首尾帧输出** | 专业的视频帧提取工具,可以精确提取视频的第一帧和最后一帧,为视频处理工作流提供便利 |  |
|
||||
| **DD 图像切分器** | 智能图像切分工具,支持按自定义比例将图像切分为多个部分。可选择左右切分或上下切分,支持不等比例切分(如2:1:3),并可输出指定位置的切分结果 |  |
|
||||
| **DD 比例选择器** | 智能的宽高比选择工具,针对不同AI模型(如Qwen-image、Wan2.2)提供推荐分辨率。支持横屏、竖屏、方形三种比例类别,自动提供最适合模型的尺寸参数 |  |
|
||||
| **DD TXT文件合并器** | 强大的文本文件合并工具,可以递归扫描指定文件夹及其子文件夹中的所有TXT文件,并将其合并为单个文本内容。自动添加文件路径标识,支持多种文本编码格式 |  |
|
||||
|
||||
## 🔧 安装方法
|
||||
|
||||
@@ -64,8 +67,11 @@
|
||||
## 📈 版本历史
|
||||
|
||||
### 最新版本
|
||||
- **v3.1.0**(2025-09-21)
|
||||
- 添加DD TXT文件合并器节点,支持递归扫描文件夹及子文件夹中的所有TXT文件并合并为单个文本内容
|
||||
|
||||
- **v3.0.0**(2025-09-05)
|
||||
- 添加比例切分器节点
|
||||
- 添加DD 图像切分器节点,支持按自定义比例切分图像并输出指定部分
|
||||
|
||||
- **v2.9.1**(2025-09-04)
|
||||
- 删除DD采样器节点,它已经不适用最新的CommyUI了
|
||||
|
||||
@@ -12,6 +12,7 @@ from .node.condition_switcher import NODE_CLASS_MAPPINGS as CONDITION_SWITCHER_N
|
||||
from .node.latent_switcher import NODE_CLASS_MAPPINGS as LATENT_SWITCHER_NODES
|
||||
from .node.image_stroke import NODE_CLASS_MAPPINGS as IMAGE_STROKE_NODES
|
||||
from .node.image_splitter import NODE_CLASS_MAPPINGS as IMAGE_SPLITTER_NODES
|
||||
from .node.txt_file_merger import NODE_CLASS_MAPPINGS as TXT_MERGER_NODES
|
||||
|
||||
# 导入Qwen-MT翻译节点
|
||||
from .extensions.Qwen_MT.nodes import NODE_CLASS_MAPPINGS as QWEN_MT_NODES
|
||||
@@ -39,6 +40,7 @@ NODE_CLASS_MAPPINGS = {
|
||||
**LATENT_SWITCHER_NODES,
|
||||
**IMAGE_STROKE_NODES,
|
||||
**IMAGE_SPLITTER_NODES,
|
||||
**TXT_MERGER_NODES,
|
||||
**QWEN_MT_NODES,
|
||||
**ASPECT_RATIO_NODES,
|
||||
}
|
||||
@@ -59,6 +61,7 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DD-LatentSwitcher": "DD Latent Switcher",
|
||||
"DD-ImageStroke": "DD Image Stroke",
|
||||
"DD-ImageSplitter": "DD Image Splitter",
|
||||
"DD-TxtFileMerger": "DD TXT File Merger",
|
||||
"DD-QwenMTTranslator": "DD Qwen-MT",
|
||||
"DD-AspectRatioSelector": "DD Aspect Ratio Selector",
|
||||
}
|
||||
|
||||
@@ -382,5 +382,21 @@
|
||||
"tooltip": "Extracted single frame image"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DD-TxtFileMerger": {
|
||||
"display_name": "DD TXT File Merger",
|
||||
"description": "Merge all TXT files from a folder and its subfolders into a single text content",
|
||||
"inputs": {
|
||||
"input_folder": {
|
||||
"name": "Input Folder",
|
||||
"tooltip": "Folder path containing TXT files, will recursively scan all subfolders"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"name": "Merged Text",
|
||||
"tooltip": "Complete merged text content with file path identifiers"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,5 +410,21 @@
|
||||
"tooltip": "输出的图像高度"
|
||||
}
|
||||
}
|
||||
},
|
||||
"DD-TxtFileMerger": {
|
||||
"display_name": "DD TXT文件合并器",
|
||||
"description": "将指定文件夹及其子文件夹中的所有TXT文件合并成一个文本内容",
|
||||
"inputs": {
|
||||
"input_folder": {
|
||||
"name": "输入文件夹",
|
||||
"tooltip": "包含TXT文件的文件夹路径,将递归扫描所有子文件夹"
|
||||
}
|
||||
},
|
||||
"outputs": {
|
||||
"0": {
|
||||
"name": "合并文本",
|
||||
"tooltip": "合并后的完整文本内容,包含文件路径标识"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class DDTxtFileMerger:
|
||||
"""
|
||||
DD TXT文件合并器
|
||||
将指定文件夹及其子文件夹中的所有TXT文件合并成一个文本内容
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
return {
|
||||
"required": {
|
||||
"input_folder": ("STRING", {
|
||||
"default": "",
|
||||
"multiline": False,
|
||||
"placeholder": "输入文件夹路径,例如: C:\\texts"
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("STRING",)
|
||||
RETURN_NAMES = ("merged_text",)
|
||||
FUNCTION = "merge_txt_files"
|
||||
CATEGORY = "🍺DD系列节点/文本处理"
|
||||
|
||||
def merge_txt_files(self, input_folder):
|
||||
"""
|
||||
合并文件夹中的所有TXT文件,返回合并后的文本内容
|
||||
"""
|
||||
try:
|
||||
# 验证输入文件夹
|
||||
if not input_folder or not os.path.exists(input_folder):
|
||||
return (f"错误:输入文件夹不存在: {input_folder}",)
|
||||
|
||||
# 递归查找所有txt文件
|
||||
txt_files = []
|
||||
for root, dirs, files in os.walk(input_folder):
|
||||
for file in files:
|
||||
if file.lower().endswith('.txt'):
|
||||
txt_files.append(os.path.join(root, file))
|
||||
|
||||
if not txt_files:
|
||||
return (f"警告:在文件夹 {input_folder} 中未找到任何TXT文件",)
|
||||
|
||||
# 按路径排序,确保合并顺序一致
|
||||
txt_files.sort()
|
||||
|
||||
# 合并文件内容
|
||||
merged_content = []
|
||||
processed_files = 0
|
||||
|
||||
for txt_file in txt_files:
|
||||
try:
|
||||
# 尝试以UTF-8编码读取文件,失败时尝试其他编码
|
||||
content = None
|
||||
for encoding in ['utf-8', 'gbk', 'gb2312', 'utf-8-sig']:
|
||||
try:
|
||||
with open(txt_file, 'r', encoding=encoding, errors='ignore') as f:
|
||||
content = f.read().strip()
|
||||
break
|
||||
except UnicodeDecodeError:
|
||||
continue
|
||||
|
||||
if content: # 只处理非空文件
|
||||
# 获取相对于输入文件夹的相对路径
|
||||
relative_path = os.path.relpath(txt_file, input_folder)
|
||||
merged_content.append(f"=== {relative_path} ===")
|
||||
merged_content.append(content)
|
||||
merged_content.append("") # 空行分隔
|
||||
processed_files += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"警告:无法读取文件 {txt_file}: {str(e)}")
|
||||
continue
|
||||
|
||||
if not merged_content:
|
||||
return (f"错误:所有TXT文件都为空或无法读取",)
|
||||
|
||||
# 合并所有内容为一个字符串
|
||||
final_text = '\n'.join(merged_content)
|
||||
|
||||
success_msg = f"成功合并 {processed_files} 个TXT文件"
|
||||
print(success_msg)
|
||||
|
||||
return (final_text,)
|
||||
|
||||
except Exception as e:
|
||||
return (f"错误:处理过程中发生异常: {str(e)}",)
|
||||
|
||||
|
||||
# 导出节点映射
|
||||
NODE_CLASS_MAPPINGS = {
|
||||
"DD-TxtFileMerger": DDTxtFileMerger,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
"DD-TxtFileMerger": "DD TXT File Merger",
|
||||
}
|
||||
Reference in New Issue
Block a user