aleph65 commited on
Commit
f8a7528
·
verified ·
1 Parent(s): b526a12

download_missing_models: plan IPAdapterUnifiedLoader's implicit clip_vision/ipadapter files (preset -> filename mapping)

Browse files
Files changed (1) hide show
  1. 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
- print(f" • {os.path.basename(f)} ({len(refs)} model refs)")
740
- for r in refs:
 
 
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 --------