mirror of
https://github.com/willmiao/ComfyUI-Lora-Manager.git
synced 2026-10-08 10:32:13 -03:00
fix(sidebar): keep the recipes storage dir out of folder management
The default recipes dir (<first lora root>/recipes) is auto-created by the recipe scanner and recorded by the model-root walks as an empty folder, so it showed up in the folder sidebar — and since recipe files are not model weight files, the folder delete endpoint's model_count check let a right-click delete wipe the whole recipe library via shutil.rmtree. - scan walks (full scan, reconcile, all-folders backfill) no longer descend into the effective recipes dir, and get_all_folders() filters it out of snapshots persisted before the exclusion existed - delete_folder/rename_folder refuse the recipes dir and any ancestor of it with code "protected" - the delete modal renders a protected state (confirm hidden, per-row checkbox disabled) instead of a deletable 'no models' prediction - RecipeScanner.recipes_dir now shares the side-effect-free resolver in py/utils/recipes_paths.py
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""Resolution of the recipe library's storage directory.
|
||||
|
||||
Kept side-effect free (no directory creation): model scanners and file
|
||||
services consult these helpers on hot paths, where silently materializing
|
||||
the folder would be a surprise. ``RecipeScanner.recipes_dir`` remains the
|
||||
write endpoint and adds the ``makedirs`` itself.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
|
||||
def get_effective_recipes_dir() -> str:
|
||||
"""Resolve the directory recipe data lives in. May not exist on disk.
|
||||
|
||||
Mirrors ``RecipeScanner.recipes_dir`` minus the auto-create: the
|
||||
``recipes_path`` setting wins; otherwise the default is ``recipes``
|
||||
under the first configured lora root (``config.loras_roots`` is already
|
||||
sorted case-insensitively). Returns ``""`` when no lora root exists.
|
||||
"""
|
||||
# Local imports: services -> config -> utils dependency direction must
|
||||
# not pick up a utils -> services edge at module import time.
|
||||
from ..services.settings_manager import get_settings_manager
|
||||
from ..config import config
|
||||
|
||||
custom_recipes_dir = get_settings_manager().get("recipes_path", "")
|
||||
if isinstance(custom_recipes_dir, str) and custom_recipes_dir.strip():
|
||||
return os.path.abspath(
|
||||
os.path.normpath(os.path.expanduser(custom_recipes_dir.strip()))
|
||||
)
|
||||
|
||||
lora_roots = [
|
||||
path
|
||||
for path in (getattr(config, "loras_roots", None) or [])
|
||||
if isinstance(path, str) and path.strip()
|
||||
]
|
||||
if not lora_roots:
|
||||
return ""
|
||||
|
||||
return os.path.abspath(os.path.join(lora_roots[0], "recipes"))
|
||||
|
||||
|
||||
def normalized_recipes_dir_key() -> str:
|
||||
"""Comparison key (normcase + abspath) for the recipes dir; "" if unknown."""
|
||||
try:
|
||||
recipes_dir = get_effective_recipes_dir()
|
||||
except Exception: # pragma: no cover - never break a scan over settings access
|
||||
return ""
|
||||
if not recipes_dir:
|
||||
return ""
|
||||
return os.path.normcase(os.path.abspath(recipes_dir))
|
||||
|
||||
|
||||
def path_is_or_contains_recipes_dir(path: str) -> bool:
|
||||
"""True when *path* is the recipes dir or one of its ancestors.
|
||||
|
||||
Deleting or renaming an ancestor wipes the recipe library just the same,
|
||||
so folder operations must refuse both shapes.
|
||||
"""
|
||||
key = normalized_recipes_dir_key()
|
||||
if not key:
|
||||
return False
|
||||
candidate = os.path.normcase(os.path.abspath(path))
|
||||
return candidate == key or key.startswith(candidate + os.sep)
|
||||
Reference in New Issue
Block a user