Merge pull request #93 from ComfyAssets/feature/issue-76-gallery-scan-directory
feat(settings): add configurable gallery scan directory
This commit is contained in:
@@ -1300,14 +1300,36 @@ class PromptManagerAPI:
|
||||
async def get_settings(self, request):
|
||||
"""Get current settings."""
|
||||
try:
|
||||
from .config import PromptManagerConfig
|
||||
|
||||
from .config import PromptManagerConfig, GalleryConfig
|
||||
|
||||
# Get monitored directories from image monitor singleton if available
|
||||
monitored_dirs = []
|
||||
try:
|
||||
import sys
|
||||
# Check for the module in sys.modules (handles different import paths)
|
||||
monitor_module = None
|
||||
for mod_name in list(sys.modules.keys()):
|
||||
if 'image_monitor' in mod_name and hasattr(sys.modules[mod_name], '_monitor_instance'):
|
||||
monitor_module = sys.modules[mod_name]
|
||||
break
|
||||
|
||||
if monitor_module and monitor_module._monitor_instance is not None:
|
||||
monitored_dirs = getattr(monitor_module._monitor_instance, 'monitored_directories', [])
|
||||
elif GalleryConfig.MONITORING_DIRECTORIES:
|
||||
monitored_dirs = GalleryConfig.MONITORING_DIRECTORIES
|
||||
except Exception:
|
||||
# Fallback to config
|
||||
if GalleryConfig.MONITORING_DIRECTORIES:
|
||||
monitored_dirs = GalleryConfig.MONITORING_DIRECTORIES
|
||||
|
||||
# Return actual configuration settings
|
||||
return web.json_response({
|
||||
"success": True,
|
||||
"success": True,
|
||||
"settings": {
|
||||
"result_timeout": PromptManagerConfig.RESULT_TIMEOUT,
|
||||
"webui_display_mode": PromptManagerConfig.WEBUI_DISPLAY_MODE
|
||||
"webui_display_mode": PromptManagerConfig.WEBUI_DISPLAY_MODE,
|
||||
"gallery_root_path": GalleryConfig.MONITORING_DIRECTORIES[0] if GalleryConfig.MONITORING_DIRECTORIES else "",
|
||||
"monitored_directories": monitored_dirs
|
||||
}
|
||||
})
|
||||
except Exception as e:
|
||||
@@ -1319,12 +1341,59 @@ class PromptManagerAPI:
|
||||
async def save_settings(self, request):
|
||||
"""Save settings."""
|
||||
try:
|
||||
from .config import PromptManagerConfig, GalleryConfig
|
||||
import json
|
||||
import os
|
||||
|
||||
data = await request.json()
|
||||
# For now, just acknowledge the save
|
||||
# In the future, we could store this in database or config file
|
||||
return web.json_response(
|
||||
{"success": True, "message": "Settings saved successfully"}
|
||||
)
|
||||
restart_required = False
|
||||
|
||||
# Update in-memory config
|
||||
if 'result_timeout' in data:
|
||||
PromptManagerConfig.RESULT_TIMEOUT = data['result_timeout']
|
||||
if 'webui_display_mode' in data:
|
||||
PromptManagerConfig.WEBUI_DISPLAY_MODE = data['webui_display_mode']
|
||||
|
||||
# Handle gallery root path
|
||||
if 'gallery_root_path' in data:
|
||||
new_path = data['gallery_root_path'].strip()
|
||||
old_path = GalleryConfig.MONITORING_DIRECTORIES[0] if GalleryConfig.MONITORING_DIRECTORIES else ""
|
||||
|
||||
if new_path != old_path:
|
||||
if new_path:
|
||||
GalleryConfig.MONITORING_DIRECTORIES = [new_path]
|
||||
else:
|
||||
GalleryConfig.MONITORING_DIRECTORIES = [] # Reset to auto-detect
|
||||
restart_required = True
|
||||
|
||||
# Save to config file for persistence
|
||||
config_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
config_file = os.path.join(config_dir, 'config.json')
|
||||
|
||||
config_data = {
|
||||
'web_ui': {
|
||||
'result_timeout': PromptManagerConfig.RESULT_TIMEOUT,
|
||||
'webui_display_mode': PromptManagerConfig.WEBUI_DISPLAY_MODE
|
||||
},
|
||||
'gallery': {
|
||||
'monitoring': {
|
||||
'directories': GalleryConfig.MONITORING_DIRECTORIES
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try:
|
||||
with open(config_file, 'w') as f:
|
||||
json.dump(config_data, f, indent=2)
|
||||
self.logger.info(f"Settings saved to {config_file}")
|
||||
except Exception as save_err:
|
||||
self.logger.warning(f"Could not save config file: {save_err}")
|
||||
|
||||
return web.json_response({
|
||||
"success": True,
|
||||
"message": "Settings saved successfully",
|
||||
"restart_required": restart_required
|
||||
})
|
||||
except Exception as e:
|
||||
return web.json_response(
|
||||
{"success": False, "error": f"Failed to save settings: {str(e)}"},
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[project]
|
||||
name = "promptmanager"
|
||||
description = "A powerful ComfyUI custom node that extends the standard text encoder with persistent prompt storage, advanced search capabilities, and an automatic image gallery system using SQLite."
|
||||
version = "3.0.28"
|
||||
version = "3.0.29"
|
||||
license = {file = "LICENSE"}
|
||||
dependencies = ["# Core dependencies for PromptManager", "# Note: Most dependencies are already included with ComfyUI", "# Already included with Python standard library:", "# - sqlite3", "# - hashlib", "# - json", "# - datetime", "# - os", "# - typing", "# - threading", "# - uuid", "# Required for gallery functionality:", "watchdog>=2.1.0 # For file system monitoring", "Pillow>=8.0.0 # For image metadata extraction (usually included with ComfyUI)", "# Optional dependencies for enhanced search functionality:", "# fuzzywuzzy[speedup]>=0.18.0 # For fuzzy string matching (optional)", "# sqlalchemy>=1.4.0 # For advanced ORM features (optional)", "# Development dependencies (optional):", "# pytest>=6.0.0 # For running tests", "# black>=22.0.0 # For code formatting", "# flake8>=4.0.0 # For linting", "# mypy>=0.910 # For type checking"]
|
||||
|
||||
|
||||
+17
-6
@@ -271,19 +271,30 @@ class ImageMonitor:
|
||||
def start_monitoring(self, output_directories: Optional[list] = None):
|
||||
"""
|
||||
Start monitoring ComfyUI output directories for new images.
|
||||
|
||||
|
||||
Begins filesystem watching on the specified directories. If no directories
|
||||
are provided, the system will attempt to auto-detect ComfyUI output locations.
|
||||
are provided, the system will first check GalleryConfig.MONITORING_DIRECTORIES,
|
||||
then fall back to auto-detecting ComfyUI output locations.
|
||||
All monitoring is done recursively to catch images in subdirectories.
|
||||
|
||||
|
||||
Args:
|
||||
output_directories: List of directory paths to monitor. If None, uses auto-detection.
|
||||
output_directories: List of directory paths to monitor. If None, uses config or auto-detection.
|
||||
"""
|
||||
if self.observer:
|
||||
self.logger.warning("Image monitoring already running")
|
||||
return
|
||||
|
||||
# Auto-detect ComfyUI output directory if none provided
|
||||
|
||||
# Check config first, then auto-detect if not configured
|
||||
if not output_directories:
|
||||
try:
|
||||
from py.config import GalleryConfig
|
||||
if GalleryConfig.MONITORING_DIRECTORIES:
|
||||
output_directories = GalleryConfig.MONITORING_DIRECTORIES
|
||||
self.logger.info(f"Using configured monitoring directories: {output_directories}")
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
# Auto-detect ComfyUI output directory if still none
|
||||
if not output_directories:
|
||||
output_directories = self.detect_comfyui_output_dirs()
|
||||
|
||||
|
||||
+78
-20
@@ -421,25 +421,52 @@
|
||||
|
||||
<!-- Settings Modal -->
|
||||
<div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50">
|
||||
<div class="bg-gray-800 rounded-xl p-6 max-w-md w-full mx-4 border border-gray-700">
|
||||
<div class="bg-gray-800 rounded-xl p-6 max-w-lg w-full mx-4 border border-gray-700 max-h-[90vh] overflow-y-auto">
|
||||
<h3 class="text-xl font-semibold text-gray-100 mb-6">⚙️ Settings</h3>
|
||||
|
||||
|
||||
<div class="space-y-6">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Search Results Auto-Hide (seconds)</label>
|
||||
<input type="number" id="resultTimeout" min="0" max="300" value="30"
|
||||
class="w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-100">
|
||||
<p class="text-xs text-gray-500 mt-2">Set to 0 to disable auto-hide. Controls how long search results stay visible in the ComfyUI node.</p>
|
||||
<!-- Gallery Settings Section -->
|
||||
<div class="border-b border-gray-700 pb-4">
|
||||
<h4 class="text-sm font-semibold text-gray-300 mb-4 uppercase tracking-wide">📁 Gallery Settings</h4>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Image Scan Directory</label>
|
||||
<input type="text" id="galleryRootPath" placeholder="Leave empty for auto-detect"
|
||||
class="w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-100">
|
||||
<p class="text-xs text-gray-500 mt-2">Custom path to scan for images. Leave empty to use ComfyUI's default output directory.</p>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<label class="block text-sm font-medium text-gray-400">Currently Monitoring</label>
|
||||
<button id="refreshMonitoringStatus" class="text-xs text-blue-400 hover:text-blue-300">🔄 Refresh</button>
|
||||
</div>
|
||||
<div id="monitoringStatus" class="mt-2 px-3 py-2 bg-gray-900 rounded-lg text-xs text-gray-400 font-mono">
|
||||
Loading...
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Web UI Display Mode</label>
|
||||
<select id="webuiDisplayMode"
|
||||
class="w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-100">
|
||||
<option value="popup">Popup Window (Current)</option>
|
||||
<option value="newtab">New Tab</option>
|
||||
</select>
|
||||
<p class="text-xs text-gray-500 mt-2">Choose how the Web UI opens when clicked from ComfyUI nodes.</p>
|
||||
|
||||
<!-- UI Settings Section -->
|
||||
<div class="border-b border-gray-700 pb-4">
|
||||
<h4 class="text-sm font-semibold text-gray-300 mb-4 uppercase tracking-wide">🖥️ UI Settings</h4>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Search Results Auto-Hide (seconds)</label>
|
||||
<input type="number" id="resultTimeout" min="0" max="300" value="30"
|
||||
class="w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-100">
|
||||
<p class="text-xs text-gray-500 mt-2">Set to 0 to disable auto-hide. Controls how long search results stay visible in the ComfyUI node.</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-400 mb-2">Web UI Display Mode</label>
|
||||
<select id="webuiDisplayMode"
|
||||
class="w-full px-4 py-3 bg-gray-700 border border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-gray-100">
|
||||
<option value="popup">Popup Window (Current)</option>
|
||||
<option value="newtab">New Tab</option>
|
||||
</select>
|
||||
<p class="text-xs text-gray-500 mt-2">Choose how the Web UI opens when clicked from ComfyUI nodes.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1407,6 +1434,7 @@ No sentences, no commentary, no captions. Only tags. Keep length short but descr
|
||||
// Settings modal
|
||||
document.getElementById("saveSettings").addEventListener("click", () => this.saveSettings());
|
||||
document.getElementById("cancelSettings").addEventListener("click", () => this.hideModal("settingsModal"));
|
||||
document.getElementById("refreshMonitoringStatus").addEventListener("click", () => this.updateMonitoringStatus());
|
||||
|
||||
// Bulk tag modal
|
||||
document.getElementById("confirmBulkTag").addEventListener("click", () => this.confirmBulkTag());
|
||||
@@ -1499,6 +1527,8 @@ No sentences, no commentary, no captions. Only tags. Keep length short but descr
|
||||
if (data.success && data.settings) {
|
||||
this.settings.resultTimeout = data.settings.result_timeout || 5;
|
||||
this.settings.webuiDisplayMode = data.settings.webui_display_mode || 'popup';
|
||||
this.settings.galleryRootPath = data.settings.gallery_root_path || '';
|
||||
this.settings.monitoredDirectories = data.settings.monitored_directories || [];
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -1942,28 +1972,56 @@ No sentences, no commentary, no captions. Only tags. Keep length short but descr
|
||||
showSettingsModal() {
|
||||
document.getElementById("resultTimeout").value = this.settings.resultTimeout;
|
||||
document.getElementById("webuiDisplayMode").value = this.settings.webuiDisplayMode;
|
||||
document.getElementById("galleryRootPath").value = this.settings.galleryRootPath || '';
|
||||
this.updateMonitoringStatus();
|
||||
this.showModal("settingsModal");
|
||||
}
|
||||
|
||||
async updateMonitoringStatus() {
|
||||
const statusEl = document.getElementById("monitoringStatus");
|
||||
try {
|
||||
const response = await fetch("/prompt_manager/settings");
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const dirs = data.settings?.monitored_directories || [];
|
||||
if (dirs.length > 0) {
|
||||
statusEl.innerHTML = dirs.map(d => `<div class="truncate" title="${d}">✓ ${d}</div>`).join('');
|
||||
} else {
|
||||
statusEl.textContent = 'No directories being monitored (auto-detect on restart)';
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
statusEl.textContent = 'Unable to fetch status';
|
||||
}
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
const timeout = parseInt(document.getElementById("resultTimeout").value);
|
||||
const displayMode = document.getElementById("webuiDisplayMode").value;
|
||||
|
||||
const galleryPath = document.getElementById("galleryRootPath").value.trim();
|
||||
|
||||
this.settings.resultTimeout = timeout;
|
||||
this.settings.webuiDisplayMode = displayMode;
|
||||
this.settings.galleryRootPath = galleryPath;
|
||||
|
||||
try {
|
||||
const response = await fetch("/prompt_manager/settings", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
body: JSON.stringify({
|
||||
result_timeout: timeout,
|
||||
webui_display_mode: displayMode
|
||||
webui_display_mode: displayMode,
|
||||
gallery_root_path: galleryPath
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
this.showNotification("Settings saved successfully", "success");
|
||||
const data = await response.json();
|
||||
if (data.restart_required) {
|
||||
this.showNotification("Settings saved. Restart ComfyUI for gallery path changes to take effect.", "warning");
|
||||
} else {
|
||||
this.showNotification("Settings saved successfully", "success");
|
||||
}
|
||||
this.hideModal("settingsModal");
|
||||
} else {
|
||||
throw new Error("Failed to save settings");
|
||||
|
||||
Reference in New Issue
Block a user