improve folder path resolution with identity-based deduping and directory preference logic

This commit is contained in:
Dariusz L
2026-06-23 03:50:02 +02:00
parent 48bc3bf5f1
commit 7cd57c05cb
+168 -25
View File
@@ -996,6 +996,129 @@ class ModelResolverExtension:
if download_available:
def get_local_path_abs(path_value):
import os
try:
return os.path.abspath(str(path_value or ""))
except (OSError, ValueError):
return str(path_value or "")
def get_local_path_key(path_value):
import os
if not path_value:
return ""
try:
return os.path.normcase(os.path.abspath(path_value))
except (OSError, ValueError):
return os.path.normcase(str(path_value or ""))
def get_local_path_identity(path_value):
import os
if not path_value:
return ""
try:
return os.path.normcase(
os.path.realpath(os.path.abspath(path_value))
)
except (OSError, ValueError):
return get_local_path_key(path_value)
def get_comfy_root_path(folder_paths_module):
import os
try:
module_file = getattr(folder_paths_module, "__file__", "")
return os.path.dirname(os.path.abspath(module_file))
except Exception:
return ""
def is_local_path_within(path_value, root_value):
import os
if not path_value or not root_value:
return False
try:
path_key = get_local_path_key(path_value)
root_key = get_local_path_key(root_value)
return os.path.commonpath([path_key, root_key]) == root_key
except Exception:
return False
def prefer_local_base_directory(
candidate,
current,
preferred_directory="",
comfy_root="",
):
if not current:
return True
if not candidate:
return False
candidate_key = get_local_path_key(candidate)
current_key = get_local_path_key(current)
preferred_key = get_local_path_key(preferred_directory)
if preferred_key:
if candidate_key == preferred_key and current_key != preferred_key:
return True
if current_key == preferred_key and candidate_key != preferred_key:
return False
if comfy_root:
candidate_is_external = not is_local_path_within(
candidate, comfy_root
)
current_is_external = not is_local_path_within(
current, comfy_root
)
if candidate_is_external != current_is_external:
return candidate_is_external
candidate_is_canonical = (
candidate_key == get_local_path_identity(candidate)
)
current_is_canonical = current_key == get_local_path_identity(
current
)
if candidate_is_canonical != current_is_canonical:
return candidate_is_canonical
return False
def dedupe_local_base_directories(
paths,
preferred_directory="",
comfy_root="",
):
import os
by_identity = {}
ordered_identities = []
for path in paths or []:
if not path or not os.path.isdir(path):
continue
path_abs = get_local_path_abs(path)
path_identity = get_local_path_identity(path_abs)
if not path_identity:
continue
current = by_identity.get(path_identity)
if not current:
by_identity[path_identity] = path_abs
ordered_identities.append(path_identity)
elif prefer_local_base_directory(
path_abs,
current,
preferred_directory,
comfy_root,
):
by_identity[path_identity] = path_abs
return [by_identity[key] for key in ordered_identities]
def cleanup_search_progress(max_age_seconds=300):
now = time.time()
with self.search_progress_lock:
@@ -2509,6 +2632,8 @@ class ModelResolverExtension:
"upscale_models",
]
roots = {}
settings = load_resolver_settings()
comfy_root = get_comfy_root_path(folder_paths)
for cat in categories:
folder_key = normalize_download_category(cat)
candidate_keys = [folder_key]
@@ -2519,17 +2644,16 @@ class ModelResolverExtension:
paths = []
for candidate_key in candidate_keys:
paths.extend(folder_paths.get_folder_paths(candidate_key) or [])
normalized_paths = []
seen = set()
for path in paths:
if not path:
continue
path_abs = os.path.abspath(path)
path_key = os.path.normcase(path_abs)
if path_key in seen:
continue
seen.add(path_key)
normalized_paths.append(path_abs)
preferred_directory = (
get_default_root_for_category(folder_key, settings)
or get_download_directory(folder_key)
or ""
)
normalized_paths = dedupe_local_base_directories(
paths,
preferred_directory=preferred_directory,
comfy_root=comfy_root,
)
roots[folder_key] = normalized_paths
return web.json_response(roots)
@@ -2608,6 +2732,13 @@ class ModelResolverExtension:
return web.json_response([])
subfolders = {}
settings = load_resolver_settings()
comfy_root = get_comfy_root_path(folder_paths)
preferred_directory = (
get_default_root_for_category(category, settings)
or get_download_directory(category)
or ""
)
def add_subfolder(rel_path, base_dir=""):
rel_path = os.path.normpath(str(rel_path or "")).replace(
@@ -2616,12 +2747,23 @@ class ModelResolverExtension:
if not rel_path or rel_path == ".":
return
base_dir = os.path.abspath(base_dir) if base_dir else ""
key = (rel_path.lower(), os.path.normcase(base_dir))
base_identity = (
get_local_path_identity(base_dir) if base_dir else ""
)
key = (rel_path.lower(), base_identity)
base_label = (
os.path.basename(os.path.normpath(base_dir))
if base_dir
else ""
)
current = subfolders.get(key)
if current and not prefer_local_base_directory(
base_dir,
current.get("base_directory", ""),
preferred_directory,
comfy_root,
):
return
subfolders[key] = {
"value": rel_path,
"label": rel_path,
@@ -2629,31 +2771,32 @@ class ModelResolverExtension:
"base_directory": base_dir,
}
base_dirs = []
seen_base_dirs = set()
raw_base_dirs = []
for folder_key in available_folder_keys:
for base_dir in folder_paths.get_folder_paths(folder_key) or []:
if not base_dir or not os.path.isdir(base_dir):
continue
base_abs = os.path.abspath(base_dir)
base_key = os.path.normcase(base_abs)
if base_key in seen_base_dirs:
continue
seen_base_dirs.add(base_key)
base_dirs.append(base_abs)
raw_base_dirs.append(base_dir)
base_dirs = dedupe_local_base_directories(
raw_base_dirs,
preferred_directory=preferred_directory,
comfy_root=comfy_root,
)
def find_base_dir(full_path):
if not full_path:
return ""
full_path = os.path.abspath(full_path)
full_path_identity = get_local_path_identity(full_path)
for base_dir in base_dirs:
base_abs = os.path.abspath(base_dir)
base_identity = get_local_path_identity(base_dir)
try:
if (
os.path.commonpath([full_path, base_abs])
== base_abs
os.path.commonpath(
[full_path_identity, base_identity]
)
== base_identity
):
return base_abs
return base_dir
except Exception:
continue
return ""