Add stack Update, ComfyUI model manager, and slim ComfyUI stack.

Server UI gains Update on stack cards, ComfyUI Models tab with workflow
scan and downloads, and centralized comfyui_config. Model catalog and
download scripts move from stacks/comfyui to server-ui so ComfyUI stays a
minimal Docker wrapper for easier image updates.
This commit is contained in:
tomasz-syn-grzegorza
2026-07-05 18:45:17 +00:00
parent 359afb3a59
commit 73e4fc005e
33 changed files with 3604 additions and 307 deletions
@@ -0,0 +1,65 @@
"""Index ComfyUI-Manager model-list.json for filename → download URL lookup."""
from __future__ import annotations
import json
import logging
from pathlib import Path
from typing import Any
from comfyui_config import host_custom_nodes_dir
log = logging.getLogger(__name__)
MANAGER_REL_PATHS = (
"custom_nodes/ComfyUI-Manager/model-list.json",
"custom_nodes/ComfyUI-Manager/node_db/new/model-list.json",
)
def _manager_list_paths(data_root: Path) -> list[Path]:
base = host_custom_nodes_dir(data_root).parent
paths: list[Path] = []
for rel in MANAGER_REL_PATHS:
path = base / rel
if path.is_file():
paths.append(path)
return paths
def _score_entry(entry: dict[str, Any]) -> tuple[int, int]:
"""Prefer entries with URL and shorter save_path."""
has_url = 0 if entry.get("url") else 1
save_path = str(entry.get("save_path") or "")
return (has_url, len(save_path))
def load_manager_index(data_root: Path) -> dict[str, dict[str, Any]]:
"""Build filename → best matching ComfyUI-Manager entry."""
index: dict[str, dict[str, Any]] = {}
for list_path in _manager_list_paths(data_root):
try:
with list_path.open(encoding="utf-8") as fh:
data = json.load(fh)
except (OSError, json.JSONDecodeError) as exc:
log.warning("Failed to read ComfyUI-Manager list %s: %s", list_path, exc)
continue
models = data.get("models") if isinstance(data, dict) else None
if not isinstance(models, list):
continue
for entry in models:
if not isinstance(entry, dict):
continue
filename = entry.get("filename")
if not isinstance(filename, str) or not filename:
continue
existing = index.get(filename)
if existing is None or _score_entry(entry) < _score_entry(existing):
index[filename] = entry
return index
def lookup_manager_entry(
data_root: Path, filename: str
) -> dict[str, Any] | None:
return load_manager_index(data_root).get(filename)