| """Extract six mode-specific workflow templates from the master LTX 2.3 All-In-One workflow.
|
|
|
| Each ComfyUI group whose title starts with a number (e.g. "01 Text to Video") becomes
|
| a mode template containing only that group's nodes plus shared scaffolding (Models,
|
| Lora, Setting, Prompt, Load Audio/Image/Video, Output groups).
|
|
|
| Group title -> output filename mapping:
|
| 01 -> t2v.json
|
| 02 -> a2v.json
|
| 03 -> i2v.json
|
| 04 -> lipsync.json
|
| 05 -> keyframe.json
|
| 06 -> style.json
|
| """
|
|
|
| from __future__ import annotations
|
|
|
| import argparse
|
| import json
|
| import pathlib
|
| import sys
|
| from collections.abc import Iterable
|
|
|
| GROUP_TO_FILENAME: dict[str, str] = {
|
| "01": "t2v.json",
|
| "02": "a2v.json",
|
| "03": "i2v.json",
|
| "04": "lipsync.json",
|
| "05": "keyframe.json",
|
| "06": "style.json",
|
| }
|
|
|
| SHARED_GROUP_PREFIXES: tuple[str, ...] = (
|
| "Models",
|
| "Lora",
|
| "Setting",
|
| "Prompt",
|
| "Load Audio",
|
| "Load Image",
|
| "Load Images",
|
| "Load Video",
|
| "Output",
|
| )
|
|
|
|
|
| def _node_in_group(node: dict, group: dict) -> bool:
|
| """Test whether a node's position lies inside a group's bounding box."""
|
| if "pos" not in node or "bounding" not in group:
|
| return False
|
| nx, ny = node["pos"][0], node["pos"][1]
|
| gx, gy, gw, gh = group["bounding"]
|
| return (gx <= nx <= gx + gw) and (gy <= ny <= gy + gh)
|
|
|
|
|
| def _select_groups(master: dict, mode_prefix: str) -> list[dict]:
|
| """Pick the mode group plus all shared groups."""
|
| selected: list[dict] = []
|
| for g in master.get("groups", []):
|
| title = (g.get("title") or "").strip()
|
| if title.startswith(mode_prefix + " "):
|
| selected.append(g)
|
| elif any(title.startswith(p) for p in SHARED_GROUP_PREFIXES):
|
| selected.append(g)
|
| return selected
|
|
|
|
|
| def _collect_nodes(master: dict, groups: Iterable[dict]) -> list[dict]:
|
| """Return all nodes lying inside any of the given groups."""
|
| groups_list = list(groups)
|
| keep: list[dict] = []
|
| for node in master.get("nodes", []):
|
| if any(_node_in_group(node, g) for g in groups_list):
|
| keep.append(node)
|
| return keep
|
|
|
|
|
| def _collect_links(master: dict, kept_node_ids: set[int]) -> list[list]:
|
| """Keep only links where both endpoints are in the surviving node set."""
|
| return [
|
| link
|
| for link in master.get("links", [])
|
|
|
| if link[1] in kept_node_ids and link[3] in kept_node_ids
|
| ]
|
|
|
|
|
| def extract_mode(master: dict, mode_prefix: str) -> dict:
|
| """Build a focused workflow JSON for the given mode group prefix."""
|
| groups = _select_groups(master, mode_prefix)
|
| nodes = _collect_nodes(master, groups)
|
| kept_ids = {n["id"] for n in nodes}
|
| links = _collect_links(master, kept_ids)
|
|
|
| return {
|
| "id": f"ltx23-aio-{mode_prefix}",
|
| "revision": 0,
|
| "last_node_id": max(kept_ids, default=0),
|
| "last_link_id": max((link[0] for link in links), default=0),
|
| "nodes": nodes,
|
| "links": links,
|
| "groups": groups,
|
| "definitions": master.get("definitions", {}),
|
| "config": master.get("config", {}),
|
| "extra": master.get("extra", {}),
|
| "version": master.get("version", 0.4),
|
| }
|
|
|
|
|
| def main(argv: list[str] | None = None) -> int:
|
| parser = argparse.ArgumentParser(description=__doc__)
|
| parser.add_argument("--master", type=pathlib.Path, required=True)
|
| parser.add_argument("--out", type=pathlib.Path, required=True)
|
| args = parser.parse_args(argv)
|
|
|
| master = json.loads(args.master.read_text())
|
| args.out.mkdir(parents=True, exist_ok=True)
|
|
|
| for prefix, filename in GROUP_TO_FILENAME.items():
|
| wf = extract_mode(master, prefix)
|
| out_path = args.out / filename
|
| out_path.write_text(json.dumps(wf, indent=2))
|
| print(f" -> wrote {out_path} ({len(wf['nodes'])} nodes, {len(wf['links'])} links)")
|
|
|
| return 0
|
|
|
|
|
| if __name__ == "__main__":
|
| sys.exit(main())
|
|
|