Add recursive folder scanning; remove unused web upload code
This commit is contained in:
@@ -6,7 +6,7 @@ ComfyUI 自定义节点:从文件夹批量加载并编码 TXT 提示词文件
|
||||
|
||||
## 功能特性
|
||||
|
||||
- 📁 从指定文件夹加载所有 `.txt` 提示词文件(自动按文件名排序)
|
||||
- 📁 从指定文件夹**递归**加载所有子文件夹中的 `.txt` 提示词文件(自动按路径排序)
|
||||
- 🎲 四种索引模式:`fixed`(固定)/ `increment`(递增)/ `decrement`(递减)/ `random`(随机)
|
||||
- 🔁 内置自动递增:每次 Queue 后自动切换下一个提示词,无需额外种子节点
|
||||
- 🧠 智能记忆:记录上次读取位置,重启 ComfyUI 后从上次位置继续
|
||||
@@ -45,7 +45,7 @@ git clone https://github.com/ReSerendipity/ComfyUI-BatchPromptLoader.git
|
||||
|
||||
## 使用方法
|
||||
|
||||
1. 准备提示词文件夹(如 `C:\prompts\SFW`),每个提示词一个 `.txt` 文件:
|
||||
1. 准备提示词文件夹(如 `C:\prompts\SFW`),每个提示词一个 `.txt` 文件(支持放在子文件夹中,会被递归扫描):
|
||||
|
||||
```
|
||||
positive: 一位成熟的大学教授站在讲台上
|
||||
|
||||
@@ -51,8 +51,13 @@ class BatchPromptReaderWithClip:
|
||||
os.makedirs(target_dir, exist_ok=True)
|
||||
|
||||
try:
|
||||
all_files = os.listdir(target_dir)
|
||||
txt_files = [f for f in all_files if f.endswith('.txt')]
|
||||
# 递归扫描所有子文件夹中的 .txt 文件
|
||||
txt_files = []
|
||||
for root, dirs, files in os.walk(target_dir):
|
||||
for f in files:
|
||||
if f.endswith('.txt'):
|
||||
rel_path = os.path.relpath(os.path.join(root, f), target_dir)
|
||||
txt_files.append(rel_path)
|
||||
txt_files.sort(key=lambda x: x.lower())
|
||||
except Exception as e:
|
||||
raise Exception(f"Failed to read folder: {target_dir}\nError: {str(e)}")
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
(function() {
|
||||
// Wait for ComfyUI to be ready
|
||||
function initDragAndDrop() {
|
||||
const app = window.comfyApp;
|
||||
|
||||
if (!app || !app.canvas) {
|
||||
setTimeout(initDragAndDrop, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('[BatchPromptLoader] Initializing drag and drop...');
|
||||
|
||||
// Get the main canvas element
|
||||
const canvas = app.canvas.canvas;
|
||||
const container = canvas.closest('.graphcanvas');
|
||||
|
||||
if (!container) {
|
||||
console.warn('[BatchPromptLoader] Container not found, retrying...');
|
||||
setTimeout(initDragAndDrop, 200);
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent default drag behaviors
|
||||
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
|
||||
container.addEventListener(eventName, preventDefaults, false);
|
||||
document.body.addEventListener(eventName, preventDefaults, false);
|
||||
});
|
||||
|
||||
function preventDefaults(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
// Highlight drop area when dragging over
|
||||
let highlightTimeout;
|
||||
container.addEventListener('dragenter', () => {
|
||||
container.style.border = '3px dashed #4a90d9';
|
||||
highlightTimeout = setTimeout(() => {
|
||||
container.style.background = 'rgba(74, 144, 217, 0.1)';
|
||||
}, 100);
|
||||
});
|
||||
|
||||
container.addEventListener('dragleave', () => {
|
||||
clearTimeout(highlightTimeout);
|
||||
container.style.border = '';
|
||||
container.style.background = '';
|
||||
});
|
||||
|
||||
container.addEventListener('dragover', (e) => {
|
||||
e.dataTransfer.dropEffect = 'copy';
|
||||
});
|
||||
|
||||
// Handle dropped files
|
||||
container.addEventListener('drop', async (e) => {
|
||||
clearTimeout(highlightTimeout);
|
||||
container.style.border = '';
|
||||
container.style.background = '';
|
||||
|
||||
const files = e.dataTransfer.files;
|
||||
if (files.length === 0) return;
|
||||
|
||||
// Filter for txt files
|
||||
const txtFiles = Array.from(files).filter(file =>
|
||||
file.name.toLowerCase().endsWith('.txt')
|
||||
);
|
||||
|
||||
if (txtFiles.length === 0) {
|
||||
alert('⚠️ 请拖拽 TXT 文件!\n\n只支持 .txt 格式的提示词文件。');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Show loading
|
||||
const originalTitle = document.title;
|
||||
document.title = `正在上传 ${txtFiles.length} 个文件...`;
|
||||
|
||||
// Read all files
|
||||
const prompts = [];
|
||||
for (let file of txtFiles) {
|
||||
const text = await file.text();
|
||||
prompts.push({
|
||||
name: file.name,
|
||||
content: text
|
||||
});
|
||||
}
|
||||
|
||||
// Send to server via API
|
||||
const response = await fetch('/batchprompt/upload', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
prompts: prompts,
|
||||
target_dir: 'input/batch_temp'
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`上传失败:${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
// Show success message
|
||||
alert(`✅ 成功上传 ${result.count} 个提示词文件!\n\n文件位置:${result.target_dir}\n\n现在可以修改"当前第几张"来批量生成了!`);
|
||||
|
||||
document.title = originalTitle;
|
||||
|
||||
} catch (error) {
|
||||
console.error('[BatchPromptLoader] Upload error:', error);
|
||||
alert(`❌ 上传失败\n\n错误信息:${error.message}\n\n请检查控制台获取详细信息。`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('[BatchPromptLoader] Drag and drop initialized successfully!');
|
||||
}
|
||||
|
||||
// Start initialization
|
||||
initDragAndDrop();
|
||||
})();
|
||||
@@ -1,76 +0,0 @@
|
||||
// Batch Prompt Loader - Web Widget for file selection
|
||||
(function() {
|
||||
const app = window.comfyApp;
|
||||
|
||||
// Register custom widget for BatchPromptLoader
|
||||
const originalCreateGraphWidgets = LiteGraph.LGraphNode.prototype.createWidgets;
|
||||
LiteGraph.LGraphNode.prototype.createWidgets = function() {
|
||||
const result = originalCreateGraphWidgets.apply(this, arguments);
|
||||
|
||||
if (this.type === "BatchPromptLoader") {
|
||||
// Add a button widget for file selection
|
||||
const uploadBtn = this.addWidget("button", "📁 选择提示词文件", null, () => {
|
||||
// Trigger file dialog via custom message
|
||||
app.ui.settings.addSetting({
|
||||
id: "BatchPromptUpload",
|
||||
name: "Batch Prompt Upload",
|
||||
type: "custom",
|
||||
render: (props) => React.createElement('div', null)
|
||||
});
|
||||
|
||||
// Open file picker using ComfyUI's API
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.multiple = true;
|
||||
input.accept = '.txt';
|
||||
|
||||
input.onchange = async (e) => {
|
||||
const files = e.target.files;
|
||||
if (files.length === 0) return;
|
||||
|
||||
// Read all files
|
||||
const prompts = [];
|
||||
for (let file of files) {
|
||||
const text = await file.text();
|
||||
prompts.push({
|
||||
filename: file.name,
|
||||
content: text
|
||||
});
|
||||
}
|
||||
|
||||
// Store in node data
|
||||
this.prompts_data = prompts;
|
||||
this.updateDisplay();
|
||||
|
||||
// Queue prompt to refresh
|
||||
app.queuePrompt(0);
|
||||
};
|
||||
|
||||
input.click();
|
||||
});
|
||||
|
||||
// Display widget to show loaded files count
|
||||
this.displayWidget = this.addWidget("text", "已加载:0 个文件", "", (val) => {}, {
|
||||
serialize: false
|
||||
});
|
||||
|
||||
this.updateDisplay = function() {
|
||||
const count = this.prompts_data ? this.prompts_data.length : 0;
|
||||
this.displayWidget.value = `已加载:${count} 个文件`;
|
||||
};
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
// Override onExecuted to update display after execution
|
||||
const originalOnExecuted = LiteGraph.LGraphNode.prototype.onExecuted;
|
||||
LiteGraph.LGraphNode.prototype.onExecuted = function(message) {
|
||||
if (this.type === "BatchPromptLoader" && message.file_count !== undefined) {
|
||||
this.widgets.find(w => w.name === "已加载:0 个文件")?.setValue(`已加载:${message.file_count} 个文件`);
|
||||
}
|
||||
if (originalOnExecuted) {
|
||||
return originalOnExecuted.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
})();
|
||||
Reference in New Issue
Block a user