malkuthroandClaude Opus 4.7 93363cc625 feat(sidebar): snapshot library v2 — configurable filesystem path via KFORGELABS_PRESETS (#68)
* feat(sidebar): snapshot library — named presets for picks + workflows

Implements #46 item 1 (custom save location + cross-machine portability)
as a preset library: each preset is one named JSON file under
/userdata/koolook-presets/, capturing the full Kforge Labs state.

Use cases addressed
-------------------
- **Personal portability:** copy `koolook-presets/` between machines, or
  sync ComfyUI's userdata folder via Dropbox/iCloud/Drive — your kit
  follows.
- **Team / facility shared library:** multiple workstations on the same
  ComfyUI server share `/userdata/koolook-presets/` natively. One person
  publishes "facility kit", everyone else loads it.
- **One-off transfer (different servers):** Manage tab's Download button
  saves a snapshot to local disk; Upload imports it into another machine's
  library.

UI
--
New top-level **Snapshot** action row above the search field, mirroring
the Nodes/Workflows action-row pattern. Three icon buttons (Load / Save /
Manage), each opens the same modal at a different starting tab.

- Load tab: lists every preset with metadata (workflow count · pick
  count · export date). Click a row → "Replace current state?" confirm
  → restores both picks and workflows.
- Save tab: input field defaults to `preset YYYY-MM-DD`, fully editable.
  Existing-name save prompts to overwrite.
- Manage tab: per-row Download / Delete buttons + a top-of-tab Upload
  action (file picker imports a JSON snapshot from disk into the library).

Schema
------
```json
{
  "kind": "koolook-snapshot",
  "version": 1,
  "name": "Wan video kit",
  "exportedAt": "2026-05-05T08:42:13.000Z",
  "picks": [...],
  "workflows": { "directories": {...} }
}
```
`kind` + `version` for future schema migrations. Anything without
`kind === "koolook-snapshot"` is rejected on import.

Module shape
------------
- `web/sidebar/snapshot.js` (new) — server I/O on the preset directory,
  schema validation, gather/apply, download/upload helpers. UI-free.
- `web/sidebar/modals.js` — new `showSnapshotModal({initialTab, ...})`
  that takes capability functions instead of importing snapshot.js
  directly, so the UI module stays UI-only.
- `web/sidebar/workflows_store.js` — new `replaceAllWorkflows(rawStore)`
  for bulk-replace (bypasses persistMutation since the entire cache is
  being swapped) + `getAllWorkflowsForExport()` for the gather flow.
- `web/sidebar/picks_store.js` — new `setAllPicks(picks)` exported
  helper.
- `web/sidebar/tree.js` — new top action row + 3 toolbar buttons +
  one wiring helper.
- `web/sidebar/constants.js` — CSS for tab strip, list rows, manage
  buttons.

Deliberate scope cuts (per the design discussion)
-------------------------------------------------
- No continuous sync. Each save is a deliberate act.
- No conflict resolution. Same preset name written from two machines
  on the same server = last-write-wins. Documented as a known limit;
  revisit when it bites.
- No selective import (preset = full state). Merge can be a v2.
- No custom userdata path config. The `koolook-presets/` location is
  fixed; portability comes from the snapshot files themselves, not
  from rerouting storage.

Verification
------------
- `node --input-type=module --check` on all modified + new JS files —
  clean.
- `python tools/preflight_release.py --skip manager-meta -v` PASS.
- Synced to live install on Mac for browser smoke-test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(sidebar): snapshot library now uses configurable filesystem path

Reworks PR #67 (which served snapshots from /userdata/koolook-presets/)
to expose the library through Koolook's own server-side endpoints,
making the storage location configurable via an env var. The userdata
namespace is per-install — fine for personal use, useless for the
"facility shared library" use case the user explicitly called out.

Architectural change
--------------------
- The client no longer talks to ComfyUI's `/userdata/*` API for snapshot
  files. New JS-side endpoints: `/koolook/presets/{info,list,file}`.
- Server side: new `koolook_routes.py` registers aiohttp routes on
  ComfyUI's PromptServer at custom-node load time. Path-traversal
  protection at the route boundary via a strict filename whitelist
  regex (`^[A-Za-z0-9 _.()\-]+\.json$`).
- Storage location is read from the `KFORGELABS_PRESETS` env var on
  every request (so re-launching ComfyUI with a new value picks it
  up without any cache fuss). Default fallback:
  `<comfyui-userdata>/koolook-presets/`.
- Auto-creates the configured directory on first save (parent must
  already exist — for NFS/SMB mounts the mount must be present).

Use cases this enables vs. PR #67
---------------------------------
| Use case                | PR #67 | This PR |
|-------------------------|--------|---------|
| Personal, single-machine | ✓     | ✓       |
| Personal cross-machine  | symlink-or-sync userdata | point env var at Dropbox folder |
| NFS/SMB facility share  | ✗ (locked to userdata) | ✓ point env var at mount |
| Multiple parallel libs  | ✗     | per-instance env var |
| Read-only distribution  | ✗     | ✓ (server detects, save fails cleanly) |

UI difference vs. PR #67
------------------------
- The Manage tab gains a small `i` icon at the right edge of the
  upload row. Hover tooltip shows the current library path, source
  (default vs `KFORGELABS_PRESETS` override), and writability status.
  No header line, no extra UI weight.

Files added/changed
-------------------
- `koolook_routes.py` (new, ~180 lines) — aiohttp routes + env-var
  config + filename validation. Loaded by `__init__.py` at startup;
  failure is non-fatal (the rest of Koolook still works, just the
  snapshot feature is unavailable).
- `__init__.py` — registers `koolook_routes` import-time side-effect.
- `scripts/sync_to_dev.py` — `koolook_routes.py` added to
  `RUNTIME_PATHS` so dev-sync includes it.
- `web/sidebar/snapshot.js` — server I/O section rewritten to call
  `/koolook/presets/*` instead of `/userdata/koolook-presets/*`. Drops
  the URL-encoding workaround (we own the route now, so the
  `name=<file>` query param is clean and the server validates).
- `web/sidebar/modals.js` — `showSnapshotModal` accepts a
  `getLibraryInfo` capability function; the Manage tab calls it and
  populates the info-icon tooltip.
- `web/sidebar/tree.js` — passes `getLibraryInfo` from `snapshot.js`
  to the modal.
- `.env.example` — new `KFORGELABS_PRESETS` block with macOS / Linux /
  Windows examples for personal-sync, NFS facility share, and SMB
  facility share patterns.
- `CHANGELOG.md` — Added entry rewritten for the env-var design;
  Internal entry on the new server-side module.

Why a separate v2 PR (instead of patching #67)
----------------------------------------------
The userdata-namespace design in #67 was the wrong architecture for
what the user actually wanted. Patching it to the env-var design
would have meant a near-total rewrite of #67's server I/O paths
mid-review. Cleaner: branch fresh from main, cherry-pick #67's UI
+ stores work as a base, layer the new endpoint design on top.
PR #67 will be closed as superseded once this lands.

Verification
------------
- `node --input-type=module --check` on every modified JS file —
  clean.
- `python -c "import ast; ast.parse(...)"` on `koolook_routes.py`
  + `__init__.py` — clean.
- `python tools/preflight_release.py --skip manager-meta -v` PASS.
- Synced to live install on Mac. User to smoke-test against a real
  ComfyUI session (the previous URL-encoding 405 should be gone
  since the new route handlers own the URL space).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(sidebar): redesign snapshot UX — single-click Save, real Settings panel

User feedback on #68's first cut: tabbed modal is too complicated, can't
see existing presets in the Save flow, the env var alone isn't a real
settings UI, the default-path text was stale (still claimed
`/userdata/koolook-presets/` after the configurable-path landed).

UX changes
----------
- **Save button → one click.** If a preset is currently loaded
  (tracked in localStorage as `koolook.snapshot.currentPresetName.v1`,
  survives reloads), prompts "Save over '<name>'?" with three options:
  Save (overwrite) / Save as new… (rename) / Cancel. If nothing is
  loaded yet, prompts for a name with `preset YYYY-MM-DD` default,
  fully editable.
- **Load → single dialog.** Preset list with per-row × Delete button.
  Header line shows the current library path so the user can see where
  they're loading from. No tabs.
- **Cog → real Settings panel.** Single field for the library path
  (text input, absolute path). Save / Reset to default / Close. Read-
  only line shows the currently-resolved path + source label
  (settings panel / env var / built-in default). The "Manage" tab and
  its Download / Upload sub-actions are gone — the user can already
  reach the file on disk after a save, no in-app file ops needed.

Server side
-----------
- New `GET /koolook/presets/settings` returns the saved library path +
  resolved path + source.
- New `POST /koolook/presets/settings` writes the saved library path
  to `<comfyui-userdata>/koolook-settings.json`. Empty value clears
  the override.
- Resolution chain in `_configured_dir()`:
    settings file > KFORGELABS_PRESETS env var > built-in default
  Reads each on every call; no module-load caching.
- `info` endpoint gains a `source` field ("settings" | "env" | "default")
  the UI uses for the readout line.

Client side
-----------
- `showSnapshotModal` (tabbed) replaced by three exports:
    showSaveSnapshotDialog, showLoadSnapshotDialog,
    showSnapshotSettingsDialog.
  Each is self-contained, no shared state.
- `snapshot.js` adds `getSettings`/`saveSettings` for the Settings
  dialog and `getCurrentPresetName`/`setCurrentPresetName` for the
  one-click Save tracking. Settings ride on the new server endpoints;
  current-preset tracking is browser-local.
- `applySnapshot` now also updates the current-preset tracker (via
  the dialog wrapper, not the function itself) on successful load.
- `tree.js` toolbar wires three direct callbacks instead of one
  `openSnapshotModal(initialTab)` helper.

Stale text fix
--------------
- The Save dialog's hint text in #68's first cut hardcoded
  "/userdata/koolook-presets/<name>.json" — stale from the userdata-
  era PR #67. The redesigned Save flow doesn't show that text at all
  (the path is no longer relevant to the user-typed name); the Load
  dialog's header line and the Settings dialog's read-only field are
  the canonical surfaces for "where my presets actually live."

Deliberately out of scope
-------------------------
- No per-save path picker. The library is one location at a time, set
  in advance via Settings or env var. Saving to ad-hoc locations would
  fragment the library and break the "facility shared library" use case.
- No in-app Download / Upload (was in #68's first cut). Files are on
  disk at the configured path; the OS file manager handles transfer.

Verification
------------
- `node --input-type=module --check` on every modified JS file —
  clean.
- `python -c "import ast; ast.parse(...)"` on `koolook_routes.py` —
  clean.
- `python tools/preflight_release.py --skip manager-meta -v` PASS.
- Synced to live install on Mac. User to smoke-test against a real
  ComfyUI session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(sidebar): address PR #68 review — atomicity, security, error UX

Multi-agent review surfaced 2 BLOCKING + 5 HIGH + 4 MEDIUM + 3 LOW.
Validated the BLOCKINGs against the actual code (both real) and
applied the bundle of fixes that hold up. Modal-size HIGH (#7 file
size) deferred to a follow-up — that's a wider refactor.

BLOCKING fixes
--------------
- **`applySnapshot` partial-failure tracker gating** (modals.js Load
  dialog). `setCurrentPresetName(...)` was called unconditionally
  before checking `{picksOk, workflowsOk}` from the apply. Partial
  apply (one of picks/workflows persisted, the other didn't) left
  the tracker pointing at the preset, so the next Save would offer
  "Save over '<name>'?" and silently overwrite the on-disk preset
  with the corrupted half-state. Now: tracker is set only on
  `picksOk && workflowsOk`; partial failure clears it (and the toast
  tells the user "Reload to recover prior state. Tracker cleared.").

- **`replaceAllWorkflows` non-atomic rebind** (workflows_store.js).
  Rebinds `workflowsCache` before awaiting persist, with no rollback.
  Now: snapshots the cache via the existing `snapshotCache()`
  primitive that `persistMutation` uses; on persist failure, restore
  the prior cache and return false. Load is fully atomic.

HIGH fixes
----------
- **Workflows-store invariant block updated.** Lists `replaceAllWorkflows`
  as the fourth legitimate rebind site (with rollback semantics).
  "Fifth rebind" warning bumped to "sixth."
- **Explicit HEAD route on `/koolook/presets/file`** that just does
  `is_file()` + status, no body work. The auto-synthesis path was
  reading the full file from disk just to discard it — wasteful on
  multi-MB snapshots over Dropbox/iCloud/NFS.
- **Client `sanitizeName` mirrors server whitelist regex**
  (`^[A-Za-z0-9 _.()\-]+$`). Characters outside the whitelist (`+`,
  `&`, `'`, accented letters, emoji, etc.) collapse to `_` rather
  than triggering an opaque HTTP 400 at save time.
- **Error-reason surfacing via `await resp.text()`.** RFC 7540 strips
  HTTP/2 reason phrases; behind any HTTP/2-terminating proxy
  `resp.statusText` is empty. Reading the body always carries
  aiohttp's `reason=` text. Used by writePreset, deletePreset,
  saveSettings, readPreset.

MEDIUM fixes
------------
- **Symlink follow-through hardening.** `_resolve_within_base` helper
  computes `(base / name).resolve()` and asserts it's still under
  `base.resolve()`. Defends against a symlink planted in a facility-
  shared library dir redirecting reads/writes off-share.
- **Atomic settings write.** `_write_settings` writes to `<tmp>` then
  `os.replace`, so an interrupted process can't truncate the file
  and silently revert the user's saved library path on next load.
- **`__init__.py` import wrapped in try/except.** The "non-fatal"
  comment promised that an import failure wouldn't break node
  registration; previously only `install()` returning False was
  guarded. Now any module-load exception is caught and logged with
  the `[Koolook]` prefix, and the rest of the plugin loads.

LOW housekeeping
----------------
- Dropped dead exports `downloadSnapshotAsFile` + `readSnapshotFile`
  (left over from an earlier iteration that had Download/Upload UI).
- Dropped 5 dead CSS rules (`.koolook-snapshot-tabs*`,
  `.koolook-snapshot-tab-pane`) from when the modal was tabbed.
- `currentPresetName` cleared when the user deletes the currently-
  loaded preset (Load dialog's promptDelete now checks via the new
  `getCurrentPresetName` capability).
- `loadPreview` warns to console on `kind` mismatch — non-snapshot
  JSON files in the library directory are now visible in DevTools
  rather than silently filtered.
- `presetExists` is tri-state (`true` / `false` / `null`) — the
  Save flow refuses to write when the library is unreachable rather
  than silently skipping the overwrite-confirm prompt.

Deferred
--------
- modals.js size (1080 lines) — extracting the snapshot dialogs into
  their own file is a follow-up refactor. Not blocking merge.
- Doc drift on maintainer docs / README env-var convention — separate
  documentation sweep.

Verification
------------
- `python -c "import ast; ast.parse(...)"` on all .py files — clean.
- `node --input-type=module --check` on every modified JS file —
  clean.
- `python tools/preflight_release.py --skip manager-meta -v` PASS.
- `grep -rn "downloadSnapshotAsFile\\|readSnapshotFile\\|koolook-snapshot-tab" web/`
  — zero hits (dead code gone).
- Synced to live install for browser smoke-test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci: nudge — re-trigger CI after webhook miss on d6d2bf8

The CI workflow didn't fire on the prior push (Socket Security ran but
GitHub Actions Lint/Smoke/Pre-flight/Security Gate didn't). Empty
commit to refire the pull_request webhook.

* fix(routes): drop unnecessary f-string prefixes flagged by ruff (F541)

Two adjacent string literals in the symlink-escape error message had
an `f` prefix despite containing no placeholders. CI's ruff check
fails on F541 — strip the prefixes so the lint gate passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 15:06:46 +02:00
2025-09-28 14:48:07 +02:00

ComfyUI-Koolook-Nodes

This repository provides custom nodes for ComfyUI tailored for VFX and AI image/video generation workflows.

For change history see CHANGELOG.md. For workflow conventions and naming see CLAUDE.md and the glossary. Full project documentation lives under docs/.

Koolook nodes (root package)

Node Display name Category Source
EasyWan22Prompt Wan 2.2 Easy Prompt (Koolook) Koolook/Wan_Video k_easy_wan22_prompt.py
EasyResize_Koolook Easy Resize (Koolook) Koolook/Image k_easy_resize.py
EasyAIPipeline Easy AI Pipeline (Koolook) Koolook/Pipeline k_ai_pipeline.py
easy_ImageBatch Easy Image Batch (Koolook) Koolook/Image k_easy_image_batch.py
KoolookLoadCameraPosesAbsolute Koolook Load Camera Poses (Absolute Path) Koolook/Camera k_easy_track.py

Brief descriptions:

  • Wan 2.2 Easy Prompt: Dynamic prompt builder driven by config.json (light source, lighting type, time of day, etc.). Outputs a comma-separated prompt string and optional CLIP conditioning.
  • Easy Resize (Koolook) — EasyResize_Koolook: Aspect-aware image resize that enforces divisibility (e.g., 32) for model compatibility. Modes: stretch, letterbox, pillarbox; padding and cropping supported. Originally inspired by the Resize Image V2 node from kijai/ComfyUI-KJNodes (GPL-3.0 — see forks/THIRD_PARTY.md for the full attribution + change log). The legacy bare-name EasyResize ID is kept as a deprecated alias for saved-workflow compatibility.
  • Easy AI Pipeline: Aggregates VFX shot parameters (shot duration, seed, base path, shot name, AI method, version) into a fully formatted output file path with overwrite protection. For organized writes from Save EXR / Save Image.
  • Easy Image Batch: Builds an IMAGE + MASK batch of length total_frames from 1–4 keyframe images placed at explicit frame indices (with start_frame offset). Empty frames are filled with black/gray and the mask marks empty=white / occupied=black. Useful for sparse keyframe control with Wan-style video models.
  • Koolook Load Camera Poses (Absolute Path): Loads RealEstate10k-style CAMERACTRL_POSES from any absolute path (no ComfyUI path stripping), with overrideable pose width/height. Pairs with AnimateDiff CameraCtrl downstream.

Radiance Koolook nodes (v2_3_3 fork — video-friendly VAE)

These come from forks/radiance_koolook/versions/v2_3_3/ and load through the root __init__.py. They are slim, rank-agnostic re-implementations of the upstream Radiance v2.3.3 VAE encoder/decoder that work correctly with video VAEs (Wan 2.2, Hunyuan, CogVideoX, LTX) — they skip the upstream's 4K cosine-blend tile engine, which conflicts with video VAEs that already handle their own internal stitching.

Node ID Display name Category
Easy_hdr_VAE_encode Easy HDR VAE Encode (Koolook) Koolook/VAE
Easy_hdr_VAE_decode Easy HDR VAE Decode (Koolook) Koolook/VAE

History note: the v0.1.0–v0.1.4 releases also shipped a much larger versions/v1_0_1/ fork that wrapped 26 Radiance v1.0 classes (HDR, EXR, OCIO, log curves, etc.) under the __koolook_v1_0_1 namespace. Those wrappers were vestigial — never used by Koolook authors — and were removed in v0.1.5. Users who want that functionality should install upstream Radiance directly. See forks/THIRD_PARTY.md for full attribution and removal rationale.

See forks/README.md for the full fork workflow.

Features

  • Dynamic Inputs for Prompts: Fields and dropdowns in Wan 2.2 Easy Prompt are defined in config.json, allowing easy customization without code changes—great for VFX artists iterating on lighting or camera parameters.
  • Image Resizing with Aspect Control: Easy Resize ensures precise resolutions for AI models, with options for keep_proportion preservation, padding colors, and device selection (CPU/CUDA). Supports masks for compositing in VFX.
  • Pipeline Path Generation: Easy AI Pipeline assembles VFX-style output paths and filenames from shot/version inputs, with optional version disabling and overwrite protection.
  • Sparse Keyframe Batches: Easy Image Batch constructs aligned IMAGE + MASK batches from up to four keyframe images, ready for sparse video-model control.
  • Camera Pose Loading: Koolook Load Camera Poses (Absolute Path) reads RealEstate10k pose TXT files from any disk location and emits CAMERACTRL_POSES.
  • Startup Loading: Nodes load configurations at ComfyUI launch; edit config.json and restart to update.
  • Output Formatting: Wan 2.2 Easy Prompt outputs comma-separated strings (skipping "none"), compatible with prompt builders. Easy Resize outputs resized images, masks, and width/height integers.
  • Error Handling: Fallbacks for missing/invalid JSON in Wan 2.2 Easy Prompt; validation for aspect ratios and divisibility in Easy Resize.
  • Compatibility: Works with ComfyUI-Manager for easy installation via Git URL. The Koolook root nodes and the slim v2_3_3 VAE wrappers have no extra Python dependencies beyond torch (already required by ComfyUI itself).

RunPod

RunPod management has been moved to a separate private repository for active operations and iteration.

  • Runtime/deployment scaffolding is no longer maintained in this public repo.
  • This public repo remains focused on the ComfyUI nodes/plugin code.

Release & Stability

For production usage, install a pinned stable tag/commit instead of following moving branch heads.

Tagging Policy (from now on)

  • Repo releases: vMAJOR.MINOR.PATCH (example: v0.2.0).
  • Fork code versions in paths use underscore format: v1_0_1, v2_3_3.
  • Comfy node ID namespace suffixes match fork version paths: __koolook_v1_0_1.
  • External raw checkout naming:
    • pinned: <repo>-v<version>-koolook
    • rolling upstream: <repo>-main-upstream

Sibling Projects

Some related work lives in sibling folders/repositories outside MAIN. They are consulted (read-only knowledge sources, references, etc.) but never imported by MAIN at runtime. To keep the public repo free of machine-specific absolute paths, every sibling is referenced through an environment variable.

Conventions

  • Variable prefix: KOLOOK_* (matches the existing KOLOOK_FORKS_DIR).
  • Real paths live in a local, gitignored .env file. The committed .env.example declares the variable names with placeholder values.
  • Public docs and committed code reference siblings only via the env var name (and an optional portable relative default like ../<folder>). Never hardcode an absolute path with a username.
  • A sibling project is a runtime-optional reference. If the variable is unset and no default is documented, agents/code must treat the sibling as unavailable and continue gracefully.

Registered siblings

Role Env var Portable default Status
External forks root (raw upstream clones) KOLOOK_FORKS_DIR ../ComfyUI-Forks documented in forks/forks_manifest.yaml
ComfyUI knowledge database KOLOOK_COMFYUI_KB_DIR ../ComfyUI_knowledge___DB read-only knowledge source
Personal knowledge database KOLOOK_PERSONAL_KB_DIR (no default — user-specific) optional

Local setup

cp .env.example .env
# Edit .env to point at the real paths on your machine.

Do not commit .env. The default .gitignore already excludes .env, .env.local, and .env.*.local.

External Fork Workflow (No Vendoring in MAIN)

This repository is the MAIN control repo. Large third-party repositories must stay outside MAIN.

  • Default external forks root (relative): ../ComfyUI-Forks
  • MAIN stores only:
    • wrappers under forks/
    • provenance/tracking under forks/
  • MAIN does not store full external trees (no vendored copies).

Radiance Koolook Version Layout

  • Package entry path: forks/radiance_koolook/__init__.py
  • Active version path: forks/radiance_koolook/versions/v2_3_3/
  • Modified node source is local and tracked in MAIN:
    • forks/radiance_koolook/versions/v2_3_3/nodes_vae.py
  • External checkout is reference-only raw upstream:
    • default location: ../ComfyUI-Forks/radiance-v2.3.3-koolook
    • baseline used for comparison: upstream commit f262f47ddfda01ece154bf80c22769b1e4cef795 (the v2.3.3 release commit; the cleanest tag pointing at it is the typo'd radinace2.3.3, so we pin by SHA)
  • Node IDs in the v2_3_3 fork are exposed verbatim as Easy_hdr_VAE_encode / Easy_hdr_VAE_decode (the __koolook_v2_3_3 namespace suffix is skipped via SKIP_VERSION_SUFFIX in versions/v2_3_3/__init__.py because the Koolook-specific names already differentiate them from upstream).
  • Each version folder should include UPSTREAM_PIN.yaml to keep parity with external pinned references.
  • The historical v1_0_1 folder was removed in v0.1.5; see forks/THIRD_PARTY.md.

Version Pinning (Portable Across PCs/macOS)

  • Track every external repo in forks/forks_manifest.yaml with:
    • source_ref (tag/version)
    • pinned_commit (exact hash used in production)
    • external_checkout.relative_path_from_forks_root
  • Keep checkout folder names versioned, e.g.:
    • radiance-v1.0.1-koolook
    • radiance-v2.3.3-koolook

Machine Setup Example

From the parent directory that contains ComfyUI-Koolook:

mkdir -p ../ComfyUI-Forks
cd ../ComfyUI-Forks
git clone https://github.com/fxtdstudios/radiance.git radiance-v1.0.1-koolook
cd radiance-v1.0.1-koolook
git checkout f1b8ae330848fa08aba24c9d3e355cb432d3515b

This external folder is for upstream comparison/sync work only.

Installation

  1. Open ComfyUI and access the Manager interface.
  2. Go to "Install Custom Nodes" > "Install via Git URL".
  3. Paste the repository URL: https://github.com/malkuthro/ComfyUI-Koolook.git.
  4. Install and restart ComfyUI.
  5. Nodes appear under these categories in the node search:
    • Koolook/Camera — Koolook Load Camera Poses (Absolute Path)
    • Koolook/Image — Easy Resize (Koolook), Easy Image Batch (Koolook)
    • Koolook/Pipeline — Easy AI Pipeline (Koolook)
    • Koolook/VAE — Easy HDR VAE Encode/Decode (Koolook)
    • Koolook/Wan_Video — Wan 2.2 Easy Prompt (Koolook)

Manual Installation

  1. Clone this repository into your ComfyUI/custom_nodes/ directory:
    git clone https://github.com/malkuthro/ComfyUI-Koolook.git
    
  2. Ensure __init__.py and config.json are in the cloned folder.
  3. Restart ComfyUI.

The root Koolook nodes have no additional Python dependencies. To enable the Radiance Koolook v1.0.1 nodes that handle EXR / OCIO / HDR work, install the optional packages: OpenEXR, Imath, PyOpenColorIO, colour-science, opencv-python, and imageio.

Usage

Wan 2.2 Easy Prompt

  1. Add the "Wan 2.2 Easy Prompt" node to your workflow under "Koolook/Wan_Video".
  2. Select options from the dropdowns (e.g., Light Source: "Sunny lighting", Lighting Type: "Soft lighting").
  3. Add custom text in the "body" field for main prompt content.
  4. Optionally connect a CLIP for conditioning output.
  5. Connect the STRING outputs (combined_prompt or fields_only) to a prompt input, such as the "prompt" slot in the PromptJSON node (from NeuralSamurAI/ComfyUI-PromptJSON).
  6. To customize fields:
    • Edit config.json (see example below).
    • Restart ComfyUI to apply changes.
  7. VFX Tip: Use for generating consistent lighting/shot descriptions in video generation workflows, like with AnimateDiff or SVD.

Example config.json

This is the default configuration based on Wan 2.2 parameters:

{
  "category": "Koolook/Wan_Video",
  "node_name": "Wan_2.2",
  "fields": [
    {
      "name": "light_source",
      "type": "combo",
      "options": ["none", "Sunny lighting", "Artificial lighting", "Moonlighting", "Practical lighting", "Firelighting", "Fluorescent lighting", "Overcast lighting", "Mixed lighting"],
      "default": "none"
    },
    {
      "name": "lighting_type",
      "type": "combo",
      "options": ["none", "Soft lighting", "Hard lighting", "Top lighting", "Side lighting", "Underlighting", "Edge lighting", "Silhouette lighting", "Low contrast lighting", "High contrast lighting"],
      "default": "none"
    },
    // ... (additional fields like time_of_day, shot_size, etc.)
  ],
  "output_type": "STRING"
}

Easy Resize (Koolook)

  1. Add the "Easy Resize (Koolook)" node (ID EasyResize_Koolook) to your workflow under "Koolook/Image".
  2. Connect an IMAGE (and optional MASK) input.
  3. Set the base dimension (Width or Height), base size, aspect ratio (e.g., "16:9" for landscape, "9:16" for portrait), and other options like keep_proportion (stretch, letterbox, pillarbox), pad_color (e.g., "0, 0, 0" for black), crop_position, divisible_by, and device.
  4. Connect the outputs: IMAGE and MASK for further processing, INT width/height for resolution-aware nodes like samplers or upscalers.
  5. VFX Tip: Use for preprocessing footage in multi-resolution pipelines, ensuring divisibility for efficient GPU processing in tools like IPAdapter or ControlNet.

Node Code Overview

The core logic is split across files for modularity:

Root Koolook nodes:

  • k_easy_wan22_prompt.py — loads config.json at import time, builds inputs dynamically (combos for dropdowns, strings for text), and formats selections into prompt strings with optional CLIP conditioning.
  • k_easy_resize.py — parses aspect ratios, computes divisible dimensions, resizes with common_upscale, and handles padding/cropping. Inspired by Resize Image V2 from ComfyUI-KJNodes.
  • k_ai_pipeline.py — EasyAIPipeline builds VFX output paths/filenames from shot/version inputs and creates the output directory.
  • k_easy_image_batch.py — easy_ImageBatch builds aligned IMAGE+MASK keyframe batches with placeholder fill.
  • k_easy_track.py — KoolookLoadCameraPosesAbsolute loads RealEstate10k pose TXT files from any path.

Forked/wrapped nodes:

  • forks/radiance_koolook/ — version-namespaced wrapper for the Radiance fork. Currently exposes the v2_3_3 set; modified node sources live in forks/radiance_koolook/versions/v2_3_3/nodes_vae.py. The wrapper supports the __koolook_vX_Y_Z ID suffix pattern but lets specific Koolook-named IDs (like Easy_hdr_VAE_encode/decode) ship without the suffix via the SKIP_VERSION_SUFFIX set.
  • forks/forks_manifest.yaml, forks/THIRD_PARTY.md, forks/README.md — fork tracking metadata.

Glue and frontend:

  • __init__.py — imports all node packages, merges NODE_CLASS_MAPPINGS / NODE_DISPLAY_NAME_MAPPINGS, and exposes WEB_DIRECTORY = "./web".
  • web/ — JavaScript extensions loaded by ComfyUI for the Koolook nodes (e.g., ai_pipeline.js).

For advanced customizations (e.g., different output formats or additional resize modes), modify the execute or adjust_to_aspect methods in the respective files.

Troubleshooting

  • ComfyUI Won't Start: Check console logs for errors (run python main.py). Common: Missing config.json or invalid JSON—use a validator like jsonlint.com.
  • No Fields Visible in Wan 2.2 Easy Prompt: Indicates JSON load failure; the node falls back to empty inputs.
  • Errors in Easy Resize: Ensure aspect ratio is "w:h" format, divisible_by >=1, and pad_color is "r, g, b" floats (0.0-1.0).
  • Updates: Pull latest from GitHub and restart.

Credits

License

GPL-3.0 (see LICENSE).

This package incorporates and adapts code from fxtdstudios/radiance (also GPL-3.0) under forks/radiance_koolook/. Per GPL-3.0 §5(c), the entire work is licensed as GPL-3.0. Attribution and per-fork modification notes are tracked in forks/THIRD_PARTY.md and forks/forks_manifest.yaml.

Note for downstream users: any work that incorporates, links to, or derives from ComfyUI-Koolook must itself be distributed under GPL-3.0 (or a compatible license). If you need a non-copyleft license for any piece of this code, contact the maintainers — some Koolook-original nodes (those with no upstream Radiance lineage) may be available under a more permissive license on request.

S
Description
No description provided
Readme GPL-3.0
220 MiB
Languages
Python 67.9%
JavaScript 30.3%
HTML 1%
CSS 0.5%
Shell 0.2%
Other 0.1%