diff --git a/README.md b/README.md index 92def87..5f3ee18 100644 --- a/README.md +++ b/README.md @@ -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: 一位成熟的大学教授站在讲台上 diff --git a/batch_prompt_loader.py b/batch_prompt_loader.py index 4611d74..3264659 100644 --- a/batch_prompt_loader.py +++ b/batch_prompt_loader.py @@ -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)}") diff --git a/web/batch_prompt_drop.js b/web/batch_prompt_drop.js deleted file mode 100644 index 0272ae1..0000000 --- a/web/batch_prompt_drop.js +++ /dev/null @@ -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(); -})(); diff --git a/web/batch_prompt_widget.js b/web/batch_prompt_widget.js deleted file mode 100644 index 3c79b49..0000000 --- a/web/batch_prompt_widget.js +++ /dev/null @@ -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); - } - }; -})();