fix(paths): keep business paths forward-slashed on Windows

Four places produced the host's separator (or a mixed path) where the app's
business form is forward-slashed, so on Windows the value could not be compared
with the one the rest of the app stores:

* `ModelScanner._calculate_folder` matched `file_path.startswith(root)` as raw
  strings, so every forward-slashed path resolved to the library root and a
  moved model was filed under the root instead of its folder. It now compares
  root identities and slices the relative part off.
* `normalize_relative_folder` ran `os.path.normpath`, and ntpath folds forward
  slashes back to backslashes: every folder parameter (scoped scan, create,
  rename, delete) came back host-spelled and mismatched the stored folders.
* `_format_model_name_for_comfyui` returned `os.path.relpath` unchanged, so the
  checkpoint/unet nodes and the metadata node emitted `unet\base.safetensors`
  on Windows -- a name no ComfyUI widget list matches.
* `resolve_centralized_dir_for_dir` passed a component that spans two
  directories ("<library>/<component>") whole to `os.path.join`, leaving a mixed
  path ("...\comfyui/loras-1a2b3c4d\sub") that no native path equals.

Also hands the rename re-key callback business paths, like every other absolute
path the move service returns.
This commit is contained in:
Will Miao
2026-10-12 11:35:33 +08:00
parent fd40cd952b
commit 85f79283b6
4 changed files with 53 additions and 17 deletions
+15 -2
View File
@@ -480,6 +480,19 @@ def _legacy_component(root_path: str) -> str:
return f"{sanitize_path_component(os.path.basename(normalized))}-{digest}"
def _join_component(root: str, component: str) -> str:
"""Join a mirror component name onto *root* as a native path.
A component adopted from the pre-identity-map layout is reported as
``<library>/<component>`` -- one *name* spanning two directories. Handing it
whole to ``os.path.join`` left a mixed path on Windows
(``...\\comfyui/loras-1a2b3c4d\\sub``), which no natively built path
compares equal to, so it has to be split first.
"""
return os.path.join(root, *(part for part in component.split("/") if part))
def _list_component_dirs(sidecar_root: str) -> Set[str]:
"""Return the mirror component directories present under ``sidecar_root``.
@@ -556,7 +569,7 @@ def _mirror_dir_overlap(sidecar_root: str, component: str, root: str) -> int:
if not component:
return 0
mirror_names = _subdir_names(os.path.join(sidecar_root, component))
mirror_names = _subdir_names(_join_component(sidecar_root, component))
if not mirror_names:
return 0
root_names = _subdir_names(root)
@@ -958,7 +971,7 @@ def resolve_centralized_dir_for_dir(
if not component:
return None
parts = [root, component]
parts = [_join_component(root, component)]
if rel_dir and rel_dir != os.curdir:
parts.extend(
sanitize_path_component(part)
+4 -2
View File
@@ -303,8 +303,10 @@ def _format_model_name_for_comfyui(file_path: str, model_roots: list[str]) -> st
norm_root += os.sep
if norm_file.startswith(norm_root):
# Use os.path.relpath to get relative path with OS-native separator
return os.path.relpath(file_path, root)
# ComfyUI names a model with forward slashes on every platform
# ("unet/base.safetensors"); os.path.relpath alone returns the
# host's separator, which no ComfyUI widget list matches on Windows.
return os.path.relpath(file_path, root).replace(os.sep, "/")
except (ValueError, TypeError):
continue