Files
DemonGatanjieu-Anomalous_Mo…/api/manager_catalog.py
T
DemonGatanjieuandClaude Opus 5.5 c62fe4fb5e Model Check: ComfyUI-Manager's place in the rules
A download by name says where the list's file is; an entry of no stated type no longer
fits a node of known type; without the Manager, the page says it would be one more
source. docs/model-check-rules.md gets a section on the Manager.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 23:08:29 +08:00

109 lines
4.4 KiB
Python

"""Where ComfyUI-Manager's model list says a model file comes from, for Model Check's downloads
(download_sources.find_source). The list (model-list.json, a few hundred common models: base
models, VAEs, text encoders, upscalers, ControlNets… mostly on Hugging Face) is read from the
Manager installed here; without it there is nothing to find. A list entry is only a file of the
same name: download_sources checks it against the workflow's fingerprint, or offers it as such.
"""
import importlib.util
import json
import os
import folder_paths
# The list's model types and save paths -> ComfyUI folder types.
TYPE_FOLDERS = {
"checkpoint": "checkpoints", "checkpoints": "checkpoints", "lora": "loras", "loras": "loras",
"vae": "vae", "taesd": "vae_approx", "vae_approx": "vae_approx", "clip": "text_encoders",
"text_encoders": "text_encoders", "clip_vision": "clip_vision", "unet": "diffusion_models",
"diffusion_model": "diffusion_models", "diffusion_models": "diffusion_models", "controlnet": "controlnet",
"upscale": "upscale_models", "upscale_models": "upscale_models", "embeddings": "embeddings",
"embedding": "embeddings", "gligen": "gligen", "style_models": "style_models",
}
# ComfyUI's older names for the same folders.
SAME_FOLDER = {"unet": "diffusion_models", "clip": "text_encoders"}
_cache = {"path": "", "mtime": 0.0, "index": {}}
def list_path():
"""The Manager's model-list.json on this computer, or ''."""
roots = []
try:
roots = list(folder_paths.get_folder_paths("custom_nodes") or [])
except Exception:
pass
for root in roots:
try:
names = sorted(os.listdir(root))
except OSError:
continue
for name in names:
path = os.path.join(root, name, "model-list.json")
if "manager" in name.lower() and os.path.isfile(path):
return path
try: # the Manager installed as a Python package
spec = importlib.util.find_spec("comfyui_manager")
for location in (spec.submodule_search_locations or []) if spec else []:
path = os.path.join(location, "model-list.json")
if os.path.isfile(path):
return path
except (ImportError, ValueError):
pass
return ""
def site_of(url):
"""Where a list entry's file is: the host and, on Hugging Face or GitHub, the repository."""
parts = str(url or "").split("/")
if len(parts) < 3:
return ""
host = parts[2].lower()
return "/".join([host, *parts[3:5]]) if host in ("huggingface.co", "hf-mirror.com", "github.com") and len(parts) >= 5 else host
def _folder(entry):
path = str(entry.get("save_path") or "").replace("\\", "/").strip("/")
first = path.split("/")[0].lower() if path and path != "default" else ""
return TYPE_FOLDERS.get(first) or TYPE_FOLDERS.get(str(entry.get("type") or "").lower(), "")
def _index():
path = list_path()
if not path:
return {}
try:
mtime = os.path.getmtime(path)
except OSError:
return {}
if path != _cache["path"] or mtime != _cache["mtime"]:
index = {}
try:
with open(path, encoding="utf-8") as source:
models = json.load(source).get("models") or []
except (OSError, ValueError, AttributeError):
models = []
for entry in models:
if not isinstance(entry, dict) or not entry.get("filename") or not str(entry.get("url", "")).startswith("https://"):
continue
index.setdefault(str(entry["filename"]).lower(), []).append({
"name": str(entry.get("name") or entry["filename"]), "url": entry["url"],
"folder": _folder(entry), "base": str(entry.get("base") or ""),
})
_cache.update(path=path, mtime=mtime, index=index)
return _cache["index"]
def candidates(file_name, folder_type=""):
"""The list's entries for a file of this name (any case) that fit `folder_type`, one per URL.
When the node's folder type is known, an entry whose type the list does not say does not fit."""
wanted = SAME_FOLDER.get(folder_type, folder_type)
seen, found = set(), []
for entry in _index().get(os.path.basename(str(file_name)).lower(), []):
if wanted and SAME_FOLDER.get(entry["folder"], entry["folder"]) != wanted:
continue
if entry["url"] not in seen:
seen.add(entry["url"])
found.append(entry)
return found