download_missing_models: plan IPAdapterUnifiedLoader's implicit clip_vision/ipadapter files (preset -> filename mapping)
Browse files- download_missing_models.sh +50 -2
download_missing_models.sh
CHANGED
|
@@ -192,6 +192,52 @@ def extract_models(data):
|
|
| 192 |
scan_values(list(n["inputs"].values()))
|
| 193 |
return refs
|
| 194 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 195 |
# ---------------- custom-node dependency extraction ----------------
|
| 196 |
|
| 197 |
def norm_name(s):
|
|
@@ -736,8 +782,10 @@ def main():
|
|
| 736 |
data = json.load(fh)
|
| 737 |
chosen_data.append((os.path.basename(f), data))
|
| 738 |
refs = extract_models(data)
|
| 739 |
-
|
| 740 |
-
|
|
|
|
|
|
|
| 741 |
wanted.setdefault(r, []).append(os.path.basename(f))
|
| 742 |
|
| 743 |
# -------- custom-node packs --------
|
|
|
|
| 192 |
scan_values(list(n["inputs"].values()))
|
| 193 |
return refs
|
| 194 |
|
| 195 |
+
# ---------------- implicit models (IPAdapterUnifiedLoader) ----------------
|
| 196 |
+
#
|
| 197 |
+
# IPAdapterUnifiedLoader never names its model files in the workflow JSON: at
|
| 198 |
+
# runtime it scans models/clip_vision and models/ipadapter for filenames
|
| 199 |
+
# hardcoded per preset (see ComfyUI_IPAdapter_plus/utils.py) and raises
|
| 200 |
+
# "ClipVision model not found" if they're absent — so the widget scan above
|
| 201 |
+
# can't see them. Map each preset to the files the loader will look for.
|
| 202 |
+
# Both SD1.5 and SDXL variants are listed where they exist, because the
|
| 203 |
+
# checkpoint architecture is only known at runtime; the unused variant costs
|
| 204 |
+
# little (SD1.5 ipadapters are ~100 MB).
|
| 205 |
+
CLIP_VIT_H = "clip_vision/CLIP-ViT-H-14-laion2B-s32B-b79K.safetensors"
|
| 206 |
+
CLIP_VIT_BIGG = "clip_vision/CLIP-ViT-bigG-14-laion2B-39B-b160k.safetensors"
|
| 207 |
+
UNIFIED_LOADER_PRESETS = {
|
| 208 |
+
"LIGHT - SD1.5 only (low strength)": [CLIP_VIT_H, "ipadapter/ip-adapter_sd15_light_v11.bin"],
|
| 209 |
+
"STANDARD (medium strength)": [CLIP_VIT_H, "ipadapter/ip-adapter_sd15.safetensors",
|
| 210 |
+
"ipadapter/ip-adapter_sdxl_vit-h.safetensors"],
|
| 211 |
+
"VIT-G (medium strength)": [CLIP_VIT_BIGG, "ipadapter/ip-adapter_sd15_vit-G.safetensors",
|
| 212 |
+
"ipadapter/ip-adapter_sdxl.safetensors"],
|
| 213 |
+
"PLUS (high strength)": [CLIP_VIT_H, "ipadapter/ip-adapter-plus_sd15.safetensors",
|
| 214 |
+
"ipadapter/ip-adapter-plus_sdxl_vit-h.safetensors"],
|
| 215 |
+
"PLUS FACE (portraits)": [CLIP_VIT_H, "ipadapter/ip-adapter-plus-face_sd15.safetensors",
|
| 216 |
+
"ipadapter/ip-adapter-plus-face_sdxl_vit-h.safetensors"],
|
| 217 |
+
"FULL FACE - SD1.5 only (portraits stronger)": [CLIP_VIT_H, "ipadapter/ip-adapter-full-face_sd15.safetensors"],
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
def extract_implicit_models(data):
|
| 221 |
+
"""Refs for models that nodes locate on disk at runtime without naming
|
| 222 |
+
them in the JSON. Currently covers IPAdapterUnifiedLoader; the FaceID
|
| 223 |
+
unified loader (insightface + lora files) is not handled."""
|
| 224 |
+
refs = set()
|
| 225 |
+
for n in iter_graph_nodes(data):
|
| 226 |
+
if (n.get("type") or n.get("class_type")) != "IPAdapterUnifiedLoader":
|
| 227 |
+
continue
|
| 228 |
+
inputs = n.get("inputs")
|
| 229 |
+
if isinstance(inputs, dict) and isinstance(inputs.get("preset"), str):
|
| 230 |
+
preset = inputs["preset"] # API format
|
| 231 |
+
else: # UI format: the preset is one of the widget values
|
| 232 |
+
wv = n.get("widgets_values") or []
|
| 233 |
+
preset = next((v for v in wv if isinstance(v, str) and v in UNIFIED_LOADER_PRESETS), None)
|
| 234 |
+
if preset in UNIFIED_LOADER_PRESETS:
|
| 235 |
+
refs.update(UNIFIED_LOADER_PRESETS[preset])
|
| 236 |
+
else:
|
| 237 |
+
print(f" {C_YELLOW}⚠ IPAdapterUnifiedLoader with unrecognized preset {preset!r} — "
|
| 238 |
+
f"cannot plan its clip_vision/ipadapter files{C_RESET}")
|
| 239 |
+
return refs
|
| 240 |
+
|
| 241 |
# ---------------- custom-node dependency extraction ----------------
|
| 242 |
|
| 243 |
def norm_name(s):
|
|
|
|
| 782 |
data = json.load(fh)
|
| 783 |
chosen_data.append((os.path.basename(f), data))
|
| 784 |
refs = extract_models(data)
|
| 785 |
+
implicit = extract_implicit_models(data) - refs
|
| 786 |
+
suffix = f" + {len(implicit)} implicit via IPAdapterUnifiedLoader" if implicit else ""
|
| 787 |
+
print(f" • {os.path.basename(f)} ({len(refs)} model refs{suffix})")
|
| 788 |
+
for r in refs | implicit:
|
| 789 |
wanted.setdefault(r, []).append(os.path.basename(f))
|
| 790 |
|
| 791 |
# -------- custom-node packs --------
|