mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-03-21 21:22:11 -03:00
Enhance file monitoring for LoRA files
- Added a method to map symbolic links back to actual paths in the Config class. - Improved file creation handling in LoraFileHandler to check for file size and existence before processing. - Introduced handling for file modification events to update the ignore list and schedule updates. - Increased debounce delay in _process_changes to allow for file downloads to complete. - Enhanced action processing to prioritize 'add' actions and verify file existence before adding to cache.
This commit is contained in:
11
py/config.py
11
py/config.py
@@ -85,6 +85,17 @@ class Config:
|
|||||||
mapped_path = normalized_path.replace(target_path, link_path, 1)
|
mapped_path = normalized_path.replace(target_path, link_path, 1)
|
||||||
return mapped_path
|
return mapped_path
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
def map_link_to_path(self, link_path: str) -> str:
|
||||||
|
"""将符号链接路径映射回实际路径"""
|
||||||
|
normalized_link = os.path.normpath(link_path).replace(os.sep, '/')
|
||||||
|
# 检查路径是否包含在任何映射的目标路径中
|
||||||
|
for target_path, link_path in self._path_mappings.items():
|
||||||
|
if normalized_link.startswith(target_path):
|
||||||
|
# 如果路径以目标路径开头,则替换为实际路径
|
||||||
|
mapped_path = normalized_link.replace(target_path, link_path, 1)
|
||||||
|
return mapped_path
|
||||||
|
return link_path
|
||||||
|
|
||||||
def _init_lora_paths(self) -> List[str]:
|
def _init_lora_paths(self) -> List[str]:
|
||||||
"""Initialize and validate LoRA paths from ComfyUI settings"""
|
"""Initialize and validate LoRA paths from ComfyUI settings"""
|
||||||
|
|||||||
@@ -94,17 +94,59 @@ class LoraFileHandler(FileSystemEventHandler):
|
|||||||
return
|
return
|
||||||
if self._should_ignore(event.src_path):
|
if self._should_ignore(event.src_path):
|
||||||
return
|
return
|
||||||
logger.info(f"LoRA file created: {event.src_path}")
|
|
||||||
self._schedule_update('add', event.src_path)
|
# Check if file is still being downloaded
|
||||||
|
try:
|
||||||
|
file_size = os.path.getsize(event.src_path)
|
||||||
|
# Record the file path and size to handle potential deletion during download
|
||||||
|
self.add_ignore_path(event.src_path, file_size)
|
||||||
|
|
||||||
|
# Only process file if it exists and has non-zero size
|
||||||
|
if os.path.exists(event.src_path) and file_size > 0:
|
||||||
|
logger.info(f"LoRA file created: {event.src_path} (size: {file_size} bytes)")
|
||||||
|
self._schedule_update('add', event.src_path)
|
||||||
|
else:
|
||||||
|
logger.debug(f"Ignoring empty or non-existent file: {event.src_path}")
|
||||||
|
except FileNotFoundError:
|
||||||
|
# File disappeared between event and our check - likely a temporary download file
|
||||||
|
logger.debug(f"File disappeared before processing: {event.src_path}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error processing create event for {event.src_path}: {str(e)}")
|
||||||
|
|
||||||
def on_deleted(self, event):
|
def on_deleted(self, event):
|
||||||
if event.is_directory or not event.src_path.endswith('.safetensors'):
|
if event.is_directory or not event.src_path.endswith('.safetensors'):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# If this path is in our ignore list, it might be part of a download process
|
||||||
|
# Don't remove it from the cache yet
|
||||||
if self._should_ignore(event.src_path):
|
if self._should_ignore(event.src_path):
|
||||||
|
logger.debug(f"Ignoring delete event for in-progress download: {event.src_path}")
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.info(f"LoRA file deleted: {event.src_path}")
|
logger.info(f"LoRA file deleted: {event.src_path}")
|
||||||
self._schedule_update('remove', event.src_path)
|
self._schedule_update('remove', event.src_path)
|
||||||
|
|
||||||
|
def on_modified(self, event):
|
||||||
|
if event.is_directory or not event.src_path.endswith('.safetensors'):
|
||||||
|
return
|
||||||
|
if self._should_ignore(event.src_path):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
# File modification could indicate download completion
|
||||||
|
file_size = os.path.getsize(event.src_path)
|
||||||
|
if file_size > 0:
|
||||||
|
logger.debug(f"LoRA file modified: {event.src_path} (size: {file_size} bytes)")
|
||||||
|
# Update the ignore timeout based on the new size
|
||||||
|
self.add_ignore_path(event.src_path, file_size)
|
||||||
|
# Schedule an update to add the file once the ignore period expires
|
||||||
|
self._schedule_update('add', event.src_path)
|
||||||
|
except FileNotFoundError:
|
||||||
|
# File disappeared - ignore
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error processing modify event for {event.src_path}: {str(e)}")
|
||||||
|
|
||||||
def _schedule_update(self, action: str, file_path: str): #file_path is a real path
|
def _schedule_update(self, action: str, file_path: str): #file_path is a real path
|
||||||
"""Schedule a cache update"""
|
"""Schedule a cache update"""
|
||||||
with self.lock:
|
with self.lock:
|
||||||
@@ -120,8 +162,8 @@ class LoraFileHandler(FileSystemEventHandler):
|
|||||||
if self.update_task is None or self.update_task.done():
|
if self.update_task is None or self.update_task.done():
|
||||||
self.update_task = asyncio.create_task(self._process_changes())
|
self.update_task = asyncio.create_task(self._process_changes())
|
||||||
|
|
||||||
async def _process_changes(self, delay: float = 2.0):
|
async def _process_changes(self, delay: float = 5.0):
|
||||||
"""Process pending changes with debouncing"""
|
"""Process pending changes with debouncing - increased delay to allow downloads to complete"""
|
||||||
await asyncio.sleep(delay)
|
await asyncio.sleep(delay)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -134,13 +176,34 @@ class LoraFileHandler(FileSystemEventHandler):
|
|||||||
|
|
||||||
logger.info(f"Processing {len(changes)} file changes")
|
logger.info(f"Processing {len(changes)} file changes")
|
||||||
|
|
||||||
|
# First collect all actions by file path to handle contradicting events
|
||||||
|
actions_by_path = {}
|
||||||
|
for action, file_path in changes:
|
||||||
|
# For the same file path, 'add' takes precedence over 'remove'
|
||||||
|
if file_path not in actions_by_path or action == 'add':
|
||||||
|
actions_by_path[file_path] = action
|
||||||
|
|
||||||
|
# Process the final actions
|
||||||
cache = await self.scanner.get_cached_data()
|
cache = await self.scanner.get_cached_data()
|
||||||
needs_resort = False
|
needs_resort = False
|
||||||
new_folders = set()
|
new_folders = set()
|
||||||
|
|
||||||
for action, file_path in changes:
|
for file_path, action in actions_by_path.items():
|
||||||
try:
|
try:
|
||||||
|
# For 'add' actions, verify the file still exists and is complete
|
||||||
if action == 'add':
|
if action == 'add':
|
||||||
|
# Convert to real path for file system operations
|
||||||
|
real_path = config.map_link_to_path(file_path)
|
||||||
|
|
||||||
|
if not os.path.exists(real_path):
|
||||||
|
logger.warning(f"Skipping add for non-existent file: {real_path}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
file_size = os.path.getsize(real_path)
|
||||||
|
if file_size == 0:
|
||||||
|
logger.warning(f"Skipping add for empty file: {real_path}")
|
||||||
|
continue
|
||||||
|
|
||||||
# Scan new file
|
# Scan new file
|
||||||
lora_data = await self.scanner.scan_single_lora(file_path)
|
lora_data = await self.scanner.scan_single_lora(file_path)
|
||||||
if lora_data:
|
if lora_data:
|
||||||
@@ -157,6 +220,7 @@ class LoraFileHandler(FileSystemEventHandler):
|
|||||||
lora_data['file_path']
|
lora_data['file_path']
|
||||||
)
|
)
|
||||||
needs_resort = True
|
needs_resort = True
|
||||||
|
logger.info(f"Added LoRA to cache: {file_path}")
|
||||||
|
|
||||||
elif action == 'remove':
|
elif action == 'remove':
|
||||||
# Find the lora to remove so we can update tags count
|
# Find the lora to remove so we can update tags count
|
||||||
|
|||||||
Reference in New Issue
Block a user