diff --git a/py/routes/misc_routes.py b/py/routes/misc_routes.py index 90b7d578..f8173133 100644 --- a/py/routes/misc_routes.py +++ b/py/routes/misc_routes.py @@ -4,6 +4,7 @@ import sys import threading import asyncio import subprocess +import re from server import PromptServer # type: ignore from aiohttp import web from ..services.settings_manager import settings @@ -85,6 +86,54 @@ node_registry = NodeRegistry() class MiscRoutes: """Miscellaneous routes for various utility functions""" + @staticmethod + def is_dedicated_example_images_folder(folder_path): + """ + Check if a folder is a dedicated example images folder. + + A dedicated folder should either be: + 1. Empty + 2. Only contain .download_progress.json file and/or folders with valid SHA256 hash names (64 hex characters) + + Args: + folder_path (str): Path to the folder to check + + Returns: + bool: True if the folder is dedicated, False otherwise + """ + try: + if not os.path.exists(folder_path) or not os.path.isdir(folder_path): + return False + + items = os.listdir(folder_path) + + # Empty folder is considered dedicated + if not items: + return True + + # Check each item in the folder + for item in items: + item_path = os.path.join(folder_path, item) + + # Allow .download_progress.json file + if item == '.download_progress.json' and os.path.isfile(item_path): + continue + + # Allow folders with valid SHA256 hash names (64 hex characters) + if os.path.isdir(item_path): + # Check if the folder name is a valid SHA256 hash + if re.match(r'^[a-fA-F0-9]{64}$', item): + continue + + # If we encounter anything else, it's not a dedicated folder + return False + + return True + + except Exception as e: + logger.error(f"Error checking if folder is dedicated: {e}") + return False + @staticmethod def setup_routes(app): """Register miscellaneous routes""" @@ -180,7 +229,7 @@ class MiscRoutes: if value == settings.get(key): # No change, skip continue - # Special handling for example_images_path - verify path exists + # Special handling for example_images_path - verify path exists and is dedicated if key == 'example_images_path' and value: if not os.path.exists(value): return web.json_response({ @@ -188,6 +237,13 @@ class MiscRoutes: 'error': f"Path does not exist: {value}" }) + # Check if folder is dedicated for example images + if not MiscRoutes.is_dedicated_example_images_folder(value): + return web.json_response({ + 'success': False, + 'error': "Please set a dedicated folder for example images." + }) + # Path changed - server restart required for new path to take effect old_path = settings.get('example_images_path') if old_path != value: diff --git a/py/utils/example_images_download_manager.py b/py/utils/example_images_download_manager.py index e3f46244..58df8d72 100644 --- a/py/utils/example_images_download_manager.py +++ b/py/utils/example_images_download_manager.py @@ -40,10 +40,10 @@ class DownloadManager: Expects a JSON body with: { - "output_dir": "path/to/output", # Base directory to save example images "optimize": true, # Whether to optimize images (default: true) "model_types": ["lora", "checkpoint"], # Model types to process (default: both) - "delay": 1.0 # Delay between downloads to avoid rate limiting (default: 1.0) + "delay": 1.0, # Delay between downloads to avoid rate limiting (default: 1.0) + "auto_mode": false # Flag to indicate automatic download (default: false) } """ global download_task, is_downloading, download_progress @@ -64,16 +64,30 @@ class DownloadManager: try: # Parse the request body data = await request.json() - output_dir = data.get('output_dir') + auto_mode = data.get('auto_mode', False) optimize = data.get('optimize', True) model_types = data.get('model_types', ['lora', 'checkpoint']) delay = float(data.get('delay', 0.2)) # Default to 0.2 seconds + # Get output directory from settings + from ..services.service_registry import ServiceRegistry + settings_manager = await ServiceRegistry.get_settings_manager() + output_dir = settings_manager.get('example_images_path') + if not output_dir: - return web.json_response({ - 'success': False, - 'error': 'Missing output_dir parameter' - }, status=400) + error_msg = 'Example images path not configured in settings' + if auto_mode: + # For auto mode, just log and return success to avoid showing error toasts + logger.debug(error_msg) + return web.json_response({ + 'success': True, + 'message': 'Example images path not configured, skipping auto download' + }) + else: + return web.json_response({ + 'success': False, + 'error': error_msg + }, status=400) # Create the output directory os.makedirs(output_dir, exist_ok=True) @@ -426,7 +440,6 @@ class DownloadManager: Expects a JSON body with: { "model_hashes": ["hash1", "hash2", ...], # List of model hashes to download - "output_dir": "path/to/output", # Base directory to save example images "optimize": true, # Whether to optimize images (default: true) "model_types": ["lora", "checkpoint"], # Model types to process (default: both) "delay": 1.0 # Delay between downloads (default: 1.0) @@ -444,7 +457,6 @@ class DownloadManager: # Parse the request body data = await request.json() model_hashes = data.get('model_hashes', []) - output_dir = data.get('output_dir') optimize = data.get('optimize', True) model_types = data.get('model_types', ['lora', 'checkpoint']) delay = float(data.get('delay', 0.2)) # Default to 0.2 seconds @@ -454,11 +466,16 @@ class DownloadManager: 'success': False, 'error': 'Missing model_hashes parameter' }, status=400) - + + # Get output directory from settings + from ..services.service_registry import ServiceRegistry + settings_manager = await ServiceRegistry.get_settings_manager() + output_dir = settings_manager.get('example_images_path') + if not output_dir: return web.json_response({ 'success': False, - 'error': 'Missing output_dir parameter' + 'error': 'Example images path not configured in settings' }, status=400) # Create the output directory diff --git a/static/js/managers/ExampleImagesManager.js b/static/js/managers/ExampleImagesManager.js index 7cb6d2ac..1c710040 100644 --- a/static/js/managers/ExampleImagesManager.js +++ b/static/js/managers/ExampleImagesManager.js @@ -100,9 +100,7 @@ export class ExampleImagesManager { this.updateDownloadButtonState(hasPath); try { await settingsManager.saveSetting('example_images_path', pathInput.value); - if (hasPath) { showToast('toast.exampleImages.pathUpdated', {}, 'success'); - } } catch (error) { console.error('Failed to update example images path:', error); showToast('toast.exampleImages.pathUpdateFailed', { message: error.message }, 'error'); @@ -227,13 +225,6 @@ export class ExampleImagesManager { } try { - const outputDir = document.getElementById('exampleImagesPath').value || ''; - - if (!outputDir) { - showToast('toast.exampleImages.enterLocationFirst', {}, 'warning'); - return; - } - const optimize = state.global.settings.optimizeExampleImages; const response = await fetch('/api/lm/download-example-images', { @@ -242,7 +233,6 @@ export class ExampleImagesManager { 'Content-Type': 'application/json' }, body: JSON.stringify({ - output_dir: outputDir, optimize: optimize, model_types: ['lora', 'checkpoint', 'embedding'] // Example types, adjust as needed }) @@ -691,9 +681,8 @@ export class ExampleImagesManager { return false; } - // Check if download path is set - const pathInput = document.getElementById('exampleImagesPath'); - if (!pathInput || !pathInput.value.trim()) { + // Check if download path is set in settings + if (!state.global.settings.example_images_path) { return false; } @@ -724,7 +713,6 @@ export class ExampleImagesManager { try { console.log('Performing auto download check...'); - const outputDir = document.getElementById('exampleImagesPath').value; const optimize = state.global.settings.optimizeExampleImages; const response = await fetch('/api/lm/download-example-images', { @@ -733,7 +721,6 @@ export class ExampleImagesManager { 'Content-Type': 'application/json' }, body: JSON.stringify({ - output_dir: outputDir, optimize: optimize, model_types: ['lora', 'checkpoint', 'embedding'], auto_mode: true // Flag to indicate this is an automatic download diff --git a/static/js/managers/SettingsManager.js b/static/js/managers/SettingsManager.js index 9de7a7dc..6633c3e1 100644 --- a/static/js/managers/SettingsManager.js +++ b/static/js/managers/SettingsManager.js @@ -140,7 +140,7 @@ export class SettingsManager { proxy_password: '', example_images_path: '', optimizeExampleImages: true, - autoDownloadExampleImages: true + autoDownloadExampleImages: false }; Object.keys(backendDefaults).forEach(key => { @@ -972,10 +972,6 @@ export class SettingsManager { await this.saveSetting('default_embedding_root', value); } else if (settingKey === 'display_density') { await this.saveSetting('displayDensity', value); - - // Also update compactMode for backwards compatibility - state.global.settings.compactMode = (value !== 'default'); - this.saveFrontendSettingsToStorage(); } else if (settingKey === 'card_info_display') { await this.saveSetting('cardInfoDisplay', value); } else if (settingKey === 'proxy_type') {