dependabot/github_actions/github-actions-30f7f1d3b7
22
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
3608ab5a21 |
fix(ltx): scrub base64 posters from persisted Director timelines (draft-quota toast storms) (#277)
* fix(ltx): scrub base64 posters from persisted Director timelines The vendored upstream 2.0.2 timeline editor stamps a base64 poster JPEG onto every video segment (canvas.toDataURL) and kept it across serialize round-trips, so timeline_data - and with it the workflow draft ComfyUI 1.44+ writes to localStorage ~512ms after every edit - grew by ~50-150k chars per video segment. Large timelines then exceeded the origin quota no matter how many old drafts koolook_draft_guard.js evicted, and the frontend latched its session-wide storage-unavailable flag: every edit toasted "Failed to save workflow draft" until reload. Reinstate a minimal media scrub as a third documented Koolook delta, applied at all six persist/restore maps (commitChanges + parseInitial): - video / motion_video posters with a backing file (imageFile on the main track, videoFile on the motion track) are dropped; the seeked handler re-derives them from the video element on load - legacy pre-2.0.2 image segments carrying full inline data-URLs are rewritten to the /view URL form 2.0.2 itself writes (loadMedia renders image previews from imageB64 only - deleting would blank them) - legacy inline audioB64 (raw base64) is dropped when audioFile exists; playback only falls back to it when no file resolves - blobs with no backing file are left untouched Disk Save / Save As payloads stay unscrubbed on purpose: exported .json keeps upstream's format and meets the restore scrub on load. On-screen previews are unaffected - only persisted copies are scrubbed. Covered by Node behavior tests that evaluate the delta block against a stubbed api, plus a source contract that all six maps route through the scrub. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ltx): persist the scrubbed timeline on restore; review follow-ups Addresses the verified findings on #277. The restore-time scrub only updated the editor's in-memory timeline. The node's timeline_data widget kept the original payload, and that is what onSerialize and LiteGraph's widgets_values read - so an already-fat saved workflow still fed the draft autosave that fires on load, before any edit triggers commitChanges. Both restore paths (the onConfigure sync block and the editor constructor) now rewrite the widget from the scrubbed timeline. The round-trip is lossless: parseInitial whitelists exactly the keys commitChanges persists. Chose this over calling commitChanges during configure, which would also recompute the prompt/length/strength widgets on every node's configure - far wider blast radius than the defect warrants. Also: - keep the data-URL when a malformed imageFile yields no /view URL, instead of blanking the preview with an empty string - rewrite the stale provenance-header paragraph that still claimed the base64-scrub deltas were obsolete (it now contradicted the third delta) - update the v2.0.2 entry in forks/forks_manifest.yaml from "two retargeting edits" to the three current Koolook edits, with the scrub's rationale - note the audioB64 leg and the widget rewrite in the CHANGELOG entry The bare catch in parseInitial is left alone: pre-existing upstream behavior, and the scrub introduces no realistic new throw. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(ltx): trigger the widget rewrite on legacy raw-base64 audio too The restore-time widget rewrite gated on the payload containing "data:", which catches the poster/image blobs that caused the quota storms but not the scrub's third removal: legacy audioB64, which is raw base64 with no "data:" prefix. An audio-only fat timeline would have its memory scrubbed while the widget stayed fat until the next commitChanges. Gate on either token instead, so the trigger covers everything the scrub actually removes. A false positive still costs only a lossless rewrite. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
961a95b9bd |
feat(ltx-director): reference-image identity — Ghost Mask + schedulable guide attention (#264)
* feat(ltx-director): Ghost Mask reference image (REF / character sheet) Add a no-LoRA reference-image identity anchor to LTXDirector__koolook, the way a WAN VACE reference works: attach reference image(s) and the model keeps the character's face / mouth consistent even when keyframes show a closed (or different) mouth than the audio-driven motion needs. Targets the mouth / identity drift seen in the talking-goat lip-sync workflow. Mechanism (Ghost Mask): the reference image(s) are appended as masked guide frames *after* the clean video region, so the transformer attends to them for identity but they never appear in the output; the trailing reference frames are then sliced off the result. - LTXDirector__koolook: new optional `reference_images` (IMAGE) + `reference_strength` inputs, and appended `clean_latent_frames` / `clean_pixel_frames` outputs. All additive/optional -> saved workflows unaffected. Refs are appended AFTER snap/ease so they are never grid-snapped or ease-expanded; the auto latent is grown to hold them. ltx_director.py is under ruff's forks/ exclude. - reference_ghost.py (new, Koolook-original, pure stdlib): trailing-reference geometry (insert frames, grown latent length, slice bounds). Unit-tested. - k_clean_latent_slice.py (new root node `CleanLatentSlice`): torch.narrow slice to drop the trailing reference frames (start=0, length=clean_latent_frames). torch imported lazily; clamp math unit-tested, slice path tested when real torch is present (skips under the CI stub). - Registered the node; added to sync_to_dev RUNTIME_PATHS. - Attribution: the Ghost Mask APPROACH is adapted (idea only, reimplemented) from CGlide/WhatDreamsCost-CSGlide (main_cs @ 21dd076, GPL-3.0, license verified 2026-06-24, GPL->GPL OK). THIRD_PARTY.md, forks_manifest.yaml, UPSTREAM_PIN.yaml, CHANGELOG updated. Phase 1: Ghost Mask only. The LoRA-backed Licon MSR prefix mode and the editor's character-slot UI are intentionally NOT ported. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(sidebar): action-led star footer ("Give a ★ on GitHub") Reword the GitHub discovery footer from the passive "Help creators find us on GitHub" to an action-led "Help creators find us! Give a ★ on GitHub", with the star as the explicit ask. Render the sentence as one inline text flow (not separate flex children) so the footer's `gap` doesn't space out the words; the ★ glyph keeps its gold styling inline and now carries role="img"/aria-label="star" so it is announced. Verified rendering in an isolated harness. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ltx-director): Ghost Mask broke motion in the append-and-crop pipeline The upstream guide pipeline (LTXDirectorGuide + LTXDirectorCropGuides) appends every guide_data image as an extra latent frame and crops them back off via `exact_crop_frames = latent.shape[2] - initial_latent_length`. The Ghost Mask code (modeled on CGlide's CleanLatentSlice flow) ALSO pre-grew the video latent by n_refs, so initial_latent_length became clean+n_refs: CropGuides then left n_refs frames uncropped AND the audio latent stayed at the clean length, desyncing the A/V joint transformer and breaking motion. Fix: don't pre-grow the latent. References are added to guide_data exactly like timeline keyframes and ride the same append-and-crop path — the guide node appends them and CropGuides removes them, audio untouched. Auto latent is sized to the clean length again. CleanLatentSlice/clean_latent_frames remain available for bare pipelines that don't use LTXDirectorCropGuides. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(ltx): LTX Guide Reference Strength — per-stage reference boost Adds a small Koolook node that rewrites the strength of ONLY the Ghost Mask reference frames in guide_data (the trailing entries the Director now tags with `reference_count`), leaving the keyframe pins untouched. Insert it before a stage-2 refinement guide and set ~0.9 to lock identity during the low-denoise pass without disturbing the motion set in stage 1 — the deterministic form of "more reference in stage 2". Chosen over vendoring the 600-line upstream LTXDirectorGuide (which would force a node-ID rename + graph rewire): this is one small node inserted on the stage-2 guide_data wire. The sigma-ramp alternative (ramp reference up at low sigma) is deferred — the reference hook is noise_mask + a core-model attention term, so it needs a live-model spike, not a clean static patch. - Director tags guide_data["reference_count"] = n_refs. - k_ltx_guide_reference_strength.py: pure strength rewrite (absolute / multiply, clamped), unit-tested; registered; added to sync paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(ltx): LTX Reference Bind Schedule — sigma-ramp the reference attention Confirmed against core (comfy/ldm/lightricks/model.py): the reference's guide attention strength is turned into a log-space additive self-attention bias in `_build_guide_self_attention_mask`, which is rebuilt EVERY denoise step and explicitly supports strength > 1.0 to amplify. That's a clean per-step hook — so the "inverse sigma curve" idea is buildable, not just a stage knob. New node mirrors LTXAVBindSchedule: a model_function_wrapper captures the per-step sigma and stashes a gain in transformer_options; an object-patch on diffusion_model._build_guide_self_attention_mask multiplies the strength of ONLY the trailing `num_references` guide entries (references are appended after keyframes) by a smoothstep ramp — neutral early (motion forms), rising to peak_strength late (identity locks during refinement). Keyframe pins and noise-mask freezing untouched; orthogonal to reference_strength. One pass, no re-noise, no LoRA. - ref_gain ramp + _boosted_entries unit-tested (11 tests); model wiring validated by live render. - Registered; added to sync paths. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ltx): mark Reference Bind Schedule inert without an IC-LoRA Live diagnostic on the AV model showed the builder is reached and the sigma gain flows in, but resolved_guide_entries=None despite 3276 guide tokens. Root cause: LTXDirectorGuide only records guide_attention_entries when an IC-LoRA is active (`if is_lora_active:` gates _append_guide_attention_entry). With no LoRA there is no per-guide attention bias to scale, so the attention-ramp lever does not exist in the Ghost Mask path — it can only attenuate/amplify entries that an IC-LoRA created. Rather than silently no-op, the node now logs a clear INERT warning when the entry list is empty, and the docstring/DESCRIPTION/CHANGELOG state the IC-LoRA requirement. The node remains correct for the IC-LoRA path (its partner). No-LoRA reference strengthening stays on the noise-mask lever (reference_strength / LTX Guide Reference Strength). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(ltx): Reference Bind Schedule works without an IC-LoRA (fabricates entries) The attention-bias identity channel (_build_guide_self_attention_mask) is base-model functionality, but LTXDirectorGuide only populates the per-guide entries when an IC-LoRA is active, so the node was inert in the no-LoRA Ghost Mask path. Now, when entries are missing, the node FABRICATES them for the trailing reference tokens: it splits num_guide_tokens across (num_keyframes + num_references) guide frames and assigns the sigma-ramped strength to the reference share, then hands off to the core builder. This gives the reference the same amplified-attention channel an IC-LoRA would use — inside the Director, no guide-node vendor, no graph rewire (the node is already in the user's setup). When an IC-LoRA IS active it scales the real entries as before. - New `fabricate_reference_entries(num_guide_tokens, num_references, num_keyframes, strength)` (pure, unit-tested) + `num_keyframes` input. - Wrapper now does scale-or-fabricate; clearer one-shot diagnostics. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ltx): keep num_keyframes last so pre-existing RefBind widgets stay aligned num_keyframes was inserted mid widget-order, which shifted ramp_start/ramp_end for workflows saved before it existed. Move it to the end of the optional inputs so saved [num_references, peak_strength, ramp_start, ramp_end] values keep mapping correctly; num_keyframes just defaults to 0 on reload. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(test): drop unused pytest import (ruff F401) The guide-reference-strength test uses plain asserts; the stray `import pytest` tripped ruff F401 and reddened the Lint & Smoke CI job. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ltx-director): default keyframe guidance 0.8 (smooth), not 1.0 The timeline editor auto-built the guide_strength string from each clip's per-segment guideStrength, filling 1.0 for any clip left unset (and the Python fallback matched). 1.0 is a hard pin, so a clip you forgot to lower snapped instead of transitioning smoothly. Unset clips now default to 0.8 in the editor (display + serialization) and the node; explicit per-clip values still override. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(ltx): Keyframe Soften Schedule — smooth motion early, sharp poses late New model-patcher node LTXKeyframeSoftenSchedule: sigma-schedules the LTX keyframe denoise-mask pins so the model forms smooth motion between poses early (pins loosened at high sigma) and resolves sharp poses late (pins restored by a crossover). Uses the sampler's per-step denoise_mask_function hook — no IC-LoRA required — and adds no guide frames (so it can't fabricate extra poses). Audio-safe: only the video half of the NestedTensor A/V mask is softened; the audio mask is untouched, so lip-sync is unaffected. Two normalized 0-1 widgets map onto the sensitive useful range: max_soften (ease, ->0-0.20) and crossover (invention window, ->0-0.05). Pure soften_gain ramp unit-tested (11 tests); model wiring render-validated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(review): chain denoise hook, optional_latent clean-length, audio test, CGlide manifest Addresses PR #264 review findings: - MEDIUM (Soften node): chain a pre-existing denoise_mask_function instead of clobbering it (mirrors the sibling LTX patchers' wrapper-chaining), so the node composes with anything else that sets that hook. - HIGH #2 (Director): clean_pixel_frames/clean_latent_frames now derive from the ACTUAL optional_latent shape when one is connected, so Ghost-Mask reference geometry and the reported counts follow the real latent instead of the timeline duration (with a safe fallback + warning). - LOW (Soften node): reconcile docstring (crossover is sigma-progress, maps to internal 0-0.05), one-time warn on the sigma-read fallback, and extract a testable soften_denoise_mask() helper. - LOW: lock the audio-safety guarantee with NestedTensor-stub tests — video mask softened, audio mask untouched, part-ordering asserted. - #6: add a formal forks_manifest.yaml entry for the CSGlide Ghost Mask idea-only adaptation (was prose-only in another entry's notes; THIRD_PARTY.md already carries the full attribution). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: AI Machine <ai.machine@x.baconcph.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
ba37ec3d41 |
feat: LTX Director 2.0.2 fork, keyframe grid-snap, and audio→video bind schedule (#259)
* feat(forks): LTX Director v2_0_2 fork + keyframe latent-grid snap (#258) Bump the WhatDreamsCost Koolook fork to upstream 2.0.2 (pinned fe09f73) in a new versions/v2_0_2 namespace, and add `snap_keyframes_to_grid` (default on) to fix hard-pinned image keyframes "jumping" at latent-bucket edges. Why: a Director image keyframe is injected as a single latent frame at the segment's left edge, quantized to the model temporal stride s (L(f)=0 if f==0 else 1+(f-1)//s). Pins near a bucket boundary round ambiguously and two pins in one bucket collide. Snapping each pin to its bucket center removes the ambiguity without changing which latent frame it lands on; colliding pins are bumped to the next free bucket with a warning. - forks/whatdreamscost_koolook/versions/v2_0_2/: vendored 2.0.2 sources with the v1.3.9 Koolook deltas carried forward (relay_overrides, audio_transcript_json, per-segment sigma — build_segments is byte-identical 1.3.9->2.0.2 so the sigma patch drops in verbatim), plus new pure keyframe_grid.py snapper wired into the insert_frames build. - Canonical LTXDirector__koolook rolls to 2.0.2 (entrypoint loads v2_0_2 last); LTXDirector__koolook_v1_3_2 alias preserved; v1.3.9 kept on disk. - LICENSE re-verified GPL-3.0 (2026-06-22). Manifest + THIRD_PARTY + CHANGELOG + audio-lipsync findings/CHEATSHEET updated. - 37 new unit tests for the snapper; 107 fork tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(forks): append Koolook LTX Director widgets last for clean load Move the v2_0_2 Koolook widgets (relay_overrides, audio_transcript_json, snap_keyframes_to_grid) to the end of the schema, after override_audio. They were inserted after img_compression, which shifted the last upstream widget when an upstream-2.0.x-saved workflow is loaded onto LTXDirector__koolook. With them last, an upstream graph's positional widgets_values map onto the upstream widgets untouched and only the Koolook widgets append with defaults (snap_keyframes_to_grid=on). Execution is unaffected (kwargs by name). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * refactor(forks): make v2_0_2 a clean upstream-2.0.2 replica + grid-snap only Per maintainer direction: the v2_0_2 Koolook LTX Director should faithfully replicate upstream 2.0.2 (so all latest features work) rather than carry the older customizations, which are unused. - Drop relay_overrides, audio_transcript_json, and the per-segment sigma patch. prompt_relay.py and patches.py are now vendored VERBATIM from upstream 2.0.2; _relay_overrides.py removed. - ltx_director.py is upstream 2.0.2 verbatim except two additive changes: the namespaced node ID LTXDirector__koolook, and the snap_keyframes_to_grid latent-bucket keyframe snap (issue #258), appended last in the schema. - v2_0_2 registers only LTXDirector__koolook; the legacy LTXDirector__koolook_v1_3_2 ID stays backed by the v1_3_9 package. v1.3.9 (with the older customizations) remains on disk for rollback. - Manifest / THIRD_PARTY / UPSTREAM_PIN / CHANGELOG updated to the clean scope. grid-snap unit tests unchanged (107 fork tests pass). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(forks): vendor upstream 2.0.2 timeline editor JS for LTXDirector__koolook The v2_0_2 Python was upstream-exact but the node still rendered with the old 1.3.9-era timeline editor (fewer toolbar actions, no IC-LoRA track, leaked *_second widgets, frame_rate NaN). Replace web/whatdreamscost_koolook/ ltx_director.js with upstream 2.0.2's js/ltx_director.js vendored verbatim, retargeted with only two edits: unique extension name 'LTXDirector_Koolook' and node guard nodeData.name === 'LTXDirector__koolook'. UX now matches upstream 2.0.2 exactly; only the Python differs (grid-snap). The old koolook base64-scrub deltas are dropped as obsolete — upstream 2.0.2 persists timeline media via imageFile/audioFile + /api/view URLs (saved timelines are already lean), and the localStorage quota guard stays in the global web/koolook_draft_guard.js (upstream does not override localStorage). Rewrote tests/sidebar/test_ltx_director_draft_guard.py to the new contract (no embedded guard, retargeted to the koolook node, pinned-upstream provenance). 110 fork+contract tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(scripts): resolve .env from main repo when dev-sync runs in a worktree Unrelated to the LTX work: plain `dev-sync` failed with "KOLOOK_COMFYUI_DEV_PATH not set" from any worktree because scripts/sync_to_dev.py only loaded `.env` from the worktree root, where the committed `.env` does not exist (it lives in the main checkout). sync_to_dev_audio.py already had the worktree->main fallback, which is why dev-sync-audio worked. Move that fallback into a shared sync_to_dev.find_dotenv() and use it in both scripts so they can't drift. Adds regression tests for the worktree fallback, the worktree-own-.env preference, and the not-a-worktree case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: gitignore root-level generated workflow JSONs Node-swapped/converted workflow copies (e.g. *.koolook.json) are dropped at the repo root during dev iteration; they're scratch deliverables, not package content. Ignore root-level *.json only (leading `/`), with a `!config.json` exception, so the 30+ tracked JSON in subdirs (test fixtures, web/, .claude/, docs/runs/*/workflow.json) stay tracked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: LTX A/V Bind Schedule node — gate audio→video binding by sigma New Koolook node LTXAVBindSchedule decouples big motion from lip-sync in LTX 2.3. The AV transformer (comfy/ldm/lightricks/av_model.py, BasicAVTransformerBlock) adds audio into video via a gated `audio_to_video_attn` per block, run only when transformer_options["a2v_cross_attn"] is set. This node patches each block's audio_to_video_attn.forward to scale its output by a per-step gain, and a unet-function-wrapper computes that gain from the current sigma: early/high-sigma steps run near `early_gain` (coarse motion settles audio-blind), ramping (smoothstep) to full at low sigma (lips bind onto settled motion). One pass, no re-noise — the fix for keyframe transitions jumping on audio peaks. early_gain=1.0 reproduces stock behavior; a graph without the node is unchanged. Registered as a root node + added to RUNTIME_PATHS so dev-sync ships it. Pure ramp (a2v_gain) unit-tested (9 cases); model wiring validated by live render. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: LTX A/V Bind Schedule — add transition-window mode (lips synced + smooth) The global sigma ramp decoupled motion from lips but desynced the mouth (lip timing is coarse motion too, so holding audio out globally moved the mouth audio-blind). Add a WINDOWED mode: audio binds at full strength everywhere (lips sync across the whole clip) EXCEPT a +/-transition_window band around each keyframe transition, where the sigma ramp applies — so only those moments are motion-protected early and rejoined late. Implemented as a per-latent-frame gain on a2v_out: read grid_sizes from transformer_options to map frame-major video tokens -> latent frames, build a gain vector (1.0 outside bands, smoothstep-tapered to sigma_gain at each transition center), expand per-frame -> per-token, scale. New optional inputs: transition_frames, total_frames, transition_window. Empty transition_frames = prior global-ramp behavior. windowed_frame_gains unit-tested (7 cases). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat: LTXAVBindSchedule — one-shot diagnostic log (shapes/grid/mode) Logs out.shape, grid_sizes, transition_fracs, and whether the per-frame mask fired vs fell back to scalar — so a single render reveals whether the a2v patch hooks the model and whether the windowed token->frame mapping aligns, instead of guessing blind. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(audio-lipsync): record validated a2v bind-schedule knee (0.35/0.25/0.55) Promote the finding that LTX 2.3's audio->video coupling must be sigma-ramped (not full-on) to keep motion smooth AND lips synced, with the validated knee early_gain=0.35 / bind_start=0.25 / bind_end=0.55 on the 8-step base pass, plus the trade-axis table and the per-frame-mode caveat (grid_sizes None + appended guide frames -> scalar fallback). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(forks): keyframe ease — strength-ramped neighbor pins to de-robotic transitions Adds `keyframe_ease` / `ease_falloff` to the Koolook LTX Director (default off). Each hard image pin keeps its exact center pose, but emits same-pose neighbor pins one latent bucket apart at decreasing strength (center*falloff**k), so the model glides into and out of the locked pose instead of snapping to a dead stop — softening the "stop/dissolve/stop" robotic feel without softening the pose. Implemented as a pure list expansion (`expand_keyframe_ease` in keyframe_grid.py, 6 unit tests) applied to guide_data after grid-snap, so the upstream guide node is untouched (no fork). Center stays exact; neighbors that fall off-clip or onto another center are skipped. Costs extra guide frames (more compute), hence opt-in. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(audio-lipsync): downgrade a2v knee to 'promising' — note lows-placement confound The 0.35/0.25/0.55 win was measured on a clip whose transitions sit on audio lows, so 'no jumps' may be the placement, not the node. Mark the controlled on-peak test (early_gain 1.0 vs 0.35 on the same on-peak layout) as still pending. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: mark vendored WhatDreamsCost fork files linguist-vendored The koolook Director ships upstream 2.0.2 code (the 8.5k-line timeline-editor JS + verbatim prompt_relay/patches) so the pack runs and distributes, but it's upstream, not authored here. Mark those linguist-vendored (drop from language stats) and the JS -diff (collapse the large blob in PR diffs). Our edited/new files stay attributed to us. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ltx-av): drop unused `import math` (ruff F401) a2v_gain uses an explicit smoothstep polynomial, not math.*, so the import was dead. Unblocks the Lint & Smoke CI gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * ci(codeql): advanced setup that excludes vendored upstream code CodeQL default setup cannot read a config file, so it scanned the verbatim-vendored WhatDreamsCost editor (web/whatdreamscost_koolook/ ltx_director.js) and raised 4 high-sev DOM-XSS alerts in upstream code we do not own. Switch to advanced setup with a committed config whose paths-ignore mirrors the .gitattributes linguist-vendored block, so the scanner treats vendored upstream as out-of-scope while still fully scanning our own nodes and sidebar JS. Languages match the prior default-setup matrix (actions, javascript-typescript, python). NOTE: requires disabling "default setup" once in repo Settings before this workflow can run (GitHub rejects advanced runs while default setup is enabled). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(ci): scope vendored fork exclusions to v2_0_2 * fix(forks): preserve final LTX keyframe snap bucket --------- Co-authored-by: AI Machine <ai.machine@x.baconcph.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
63f5c1db7b |
fix(audio-loop): capture card metadata and Director pins (#193)
* fix(audio-loop): capture card metadata and Director pins * fix(audio-loop): address card metadata review * fix(audio-loop): remove duplicate card import |
||
|
|
1d9d40ab70 |
Add audio transcript timeline node (#189)
* Add audio transcript timeline node * Hook audio transcripts into Koolook Director * Add modular timeline audio timing workflow * fix(frontend): guard Comfy workflow draft cache * fix(audio): compose transcript timeline prompts * fix(audio): address transcript review findings * fix(ci): keep transcript tests lightweight * fix(timeline): restore image previews from saved paths * fix(timeline): scrub restored preview blobs |
||
|
|
031af80f43 |
feat(forks): upgrade Koolook Director to stable v1.3.9 (#187)
* feat(forks): upgrade Koolook Director to stable v1.3.9 * docs(forks): clarify Director upgrade compatibility |
||
|
|
4739dd4203 |
feat: Easy_Utility, Easy_LoadVideo, and preview fixes (#184)
* feat(nodes): add Easy_Utility — INT → padded STRING dispatcher First mode `int_to_padded_string`: takes an INT widget (with up/down arrows) and emits a zero-padded STRING (default `001`, no `v` prefix). The downstream `koolook_versioning.resolve_version_token` prepends `v` and re-pads, so wiring this into `EasyAIPipeline.version` / `Easy_VideoCombine.version` yields the standard `vNNN` token at execution. Designed for extension: the `mode` widget shipped from day one so adding further conversions (string→int, formatting variants, etc.) doesn't rewire saved workflows. Note: the previously-attempted JS preview-resolver refactor that aimed to fix the "preview shows stale version" bug was reverted in this worktree before commit — it didn't actually fix the bug (the real cause is the JS resolver not descending into ComfyUI subgraphs) and it broke shot_name resolution in the process. The preview-resolver work is NOT in this commit; only the Python node + tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(nodes): guard version widget against pysssss .replace crash PR #180 added a STRING `version` widget to EasyAIPipeline and Easy_VideoCombine. Three workflow states leave widget.value undefined or non-string when graphToPrompt iterates widgets: - Pre-#180 workflows replay an INT (e.g. `1`) into the new STRING slot. - Widget-to-input conversion voids the value in newer LiteGraph. - Easy_VideoCombine's dict-based widgets_values omits the new key entirely on workflows saved before #180. With pysssss/ComfyUI-Custom-Scripts installed, presetText.js patches every STRING TextWidget.serializeValue to run callbacks that do `value.replace(...)` for `{variable}` substitution. Any of the states above crashes graphToPrompt and turns Run into a no-op for the whole workflow ("Cannot read properties of undefined (reading 'replace')"). Fix: trap `widget.value` reads/writes on the version widget via `Object.defineProperty` so the property is always coerced to a string regardless of caller. `configurable: true` so downstream code can still override the descriptor if it needs to. Tests: 231 passing; both JS files parse OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ai-pipeline): resolve subgraph version preview * feat(video): add path-aware load video node * fix(ai-pipeline): preview connected text names * fix(pr184): address review cleanup --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
9834c10087 |
feat(nodes): single STRING version input + strict v001 naming (#180)
* feat(nodes): single STRING version input + strict v001 naming - koolook_versioning.py: shared version-token helper (bare number -> vNNN, else verbatim; disable_versioning is the master off-switch) - EasyAIPipeline: version is now ONE STRING field (typed or wired from a global version node), replacing the INT widget - Easy_VideoCombine: new STRING 'version' (replaces keep_silent_intermediate) + enable_overwrite; strict mode strips VHS's _NNNNN counter -> <name>_v001.<ext>; empty keeps the legacy counter - Fix empty JSON sidecar: record prompt graph as 'workflow' fallback when extra_pnginfo is absent (headless/API path) - forks: pin VHS dependency (1.7.7 / 0edce8e) - sync_to_dev: add koolook_versioning.py to RUNTIME_PATHS Tests: 199 passing; preflight_release all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(video-combine): JSON sidecar is a drag-loadable workflow Write the litegraph workflow graph at the top level of the metadata JSON (CreationTime tucked into graph 'extra') so dropping the sidecar onto the ComfyUI canvas reloads the workflow, instead of a non-loadable wrapper. Falls back to {CreationTime, prompt} when no graph is available (headless). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
9f063345f7 |
feat(investigations): LTX23 audio-file lip-sync scaffold (#177) (#178)
* feat(investigations): scaffold #177 LTX23 audio-file lip-sync investigation Reorganize docs/investigations/ into numbered topic folders, each self-contained with narrative + upstream pin + patches + per-run snapshots. Add a generic sync helper that mirrors the dev-sync pattern for any investigation's third-party node modifications. Structure (new): docs/investigations/ 00_LTX23-base-1-step/README.md (rename of ltx-director-4k-transitions.md) 01_LTX23-audio-file-lipsync/ README.md -- problem, mechanism, patches, reversion, promotion upstream.json -- repo + version + pinned commit + sync target env + files patches/ -- source of truth for modified upstream files ltx_director.py -- adds `relay_overrides` multiline JSON input prompt_relay.py -- per-segment sigma per Prompt-Relay paper formula runs/ LOOP.md -- iteration protocol log.md -- rolling table (empty header for now) Sync helper: scripts/sync_investigation_patches.py <NN>_<topic> [--dry-run] - Reads upstream.json for target env var + file list - Backs up the target's current state once per day (<file>.bak.<YYYYMMDD>) - Worktree-aware: falls back to MAIN's .env via git rev-parse --git-common-dir - Prints a two-line report matching scripts/sync_to_dev.py shape - Per-investigation trigger phrase via `trigger:` key (sync-audio for #177) Env: .env.example -- documents new KOLOOK_WHATDREAMSCOST_PATH .env -- local path set Glossary updates (docs/reference/glossary.md): + Investigation folder + JSON file folder + Loop + Run + Investigation patches + sync_investigation_patches.py Closes the workflow gap from #177: MAIN is now the source of truth for investigation patches; the live install is a sync destination only. No manual file copying between iterations — `sync-audio` does it. * chore(ruff): exclude docs/investigations from lint The investigation patches under docs/investigations/*/patches/ are vendored upstream source (WhatDreamsCost-ComfyUI v1.3.2) carried so sync_investigation_patches.py can mirror them into the live install and so iterations can be diff'd against the pinned upstream commit. Linting them would force divergence from upstream and defeat that purpose. Mirrors the existing forks/ exclude precedent. Fixes the CI Lint & Smoke failure on PR #178. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor: per-task automation modules + WhatDreamsCost Koolook fork Pivot docs/automations/ from "one folder per model" to "one folder per automation module" so the same diffusion model can host several independent tasks side by side. Replace the parallel docs/investigations/ tree with a fork of WhatDreamsCost-ComfyUI under forks/. Automation modules (new layout) - docs/automations/LTX-2.3/base-1step/ the existing single-stage automation, moved from docs/automations/LTX-2.3/ with its README, findings, and handoff-checklist rescoped to the task. The 4k-transitions backstory that produced the module moves here as backstory/4k-transitions.md. The earlier docs/investigations/experiments/ card PNGs/HTML land in example-cards/. - docs/automations/LTX-2.3/audio-lipsync/ the audio-file lip-sync module previously living under docs/investigations/01_…/. README, handoff, findings stub, backstory/, and runs/{LOOP,log}.md, all referencing the new fork. - docs/automations/README.md + CHEATSHEET.md updated to describe the module-per-task pivot and the new tree shape. - docs/automations/CONVENTIONS.md unchanged (cross-cutting loop contract still holds). WhatDreamsCost Koolook fork (new) - forks/whatdreamscost_koolook/versions/v1_3_2/ UPSTREAM_PIN.yaml pinned to upstream e81223a (v1.3.2), GPL-3.0 verified 2026-05-28. ltx_director.py MODIFIED — adds the relay_overrides multiline JSON widget on LTXDirector. Moved from docs/investigations/01_…/patches/. prompt_relay.py MODIFIED — per-segment σ per the Prompt-Relay paper formula σ = (L − w_eff) / (2 · √ln(1/ε)). Moved from docs/investigations/01_…/patches/. patches.py UNMODIFIED — vendored verbatim from upstream because ltx_director.py imports detect_model_type and apply_patches from it (same precedent as radiance_koolook's color_helpers.py). - Registered as LTXDirector__koolook_v1_3_2 ("LTX Director (Koolook v1.3.2)") so it coexists with an installed copy of upstream WhatDreamsCost-ComfyUI. Wired into the root __init__.py NODE_CLASS_MAPPINGS merge. - forks/forks_manifest.yaml + forks/THIRD_PARTY.md updated with the new entry (attribution, change log, license verified date, exposed node IDs). Removed - docs/investigations/ entire tree retired; its contents either moved to automation modules or to forks/. - scripts/sync_investigation_patches.py no longer needed — fork code lives in the Koolook tree and is synced via the standard dev-sync flow. - KOLOOK_WHATDREAMSCOST_PATH in .env.example same reason. - docs/investigations in pyproject.toml's ruff extend-exclude added in |
||
|
|
ff79b0f418 |
feat(video): add Easy_VideoCombine (#154)
* feat(video): add Easy_VideoCombine with absolute-path support Adds a Koolook subclass of VHS VideoCombine that bypasses ComfyUI's output-directory sandbox when filename_prefix is an absolute path — the VFX-pipeline missing piece for writing renders into a project tree (e.g. E:/projects/shot01/v003) instead of ComfyUI/output/. Implementation: - k_video_combine.py: ~60 LoC subclass + a pure _resolve_abs_target helper. Scoped-patches folder_paths.get_save_image_path for the duration of one combine_video() call so upstream's encoder, audio mux, batch manager, and progress bar run unchanged. No VHS code copied. Self-skips registration when VHS isn't importable (clean one-line stderr notice). - Discriminator: os.path.isabs(filename_prefix). No new input pin; pattern borrowed from spacepxl/ComfyUI-HQ-Image-Save's SaveEXR. - Optional create_path_if_missing BOOLEAN (default False) so typos surface as FileNotFoundError instead of spawning stray dirs. License audit (2026-05-16): - VHS upstream: GPL-3.0 (verified via raw LICENSE fetch). - HQ-Image-Save pattern source: MIT, copyright 2023 spacepxl. - Koolook: already GPL-3.0; both upstreams are compatible. - No source code from either upstream is incorporated. Verification: 18 pytest cases green (12 existing + 6 new covering relative passthrough, absolute resolution, create-on/off branches, trailing-separator normalization, and empty-basename rejection). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(starter-preset): pre-favourite Easy_VideoCombine Adds the new node to the shipped starter_preset.json picks so fresh installs surface it in the Kforge Labs sidebar's Nodes section without the user having to discover it via Comfy's stock search first. Pairs naturally with the existing LoadEXR / SaveEXRFrames picks from HQ-Image-Save. Existing installs with non-empty picks are unaffected (the seeder skips them by design); they can either Load -> "starter" in the Snapshot dialog or hit "+" once in the Nodes section to pull it in. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(dev-sync): include k_video_combine.py in RUNTIME_PATHS Without this entry the new Easy_VideoCombine module is never copied into the dev ComfyUI install, so the node fails to register on restart with a silent ImportError. Caught when the maintainer restarted ComfyUI and couldn't find the node in the search palette. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(starter-preset): include all Koolook nodes in default picks Fresh installs should land on a populated Nodes (favorites) panel that immediately surfaces what Koolook provides — otherwise new users see only third-party picks and have no entry point into the Koolook-native workflow. Adds the 8 currently-registered Koolook node IDs to the shipped starter preset alongside the existing third-party essentials: EasyAIPipeline, EasyResize_Koolook, EasyWan22Prompt, Easy_Pattern, Easy_VideoCombine, Easy_hdr_VAE_decode, Easy_hdr_VAE_encode, KoolookLoadCameraPosesAbsolute, easy_ImageBatch Also adds Easy_VideoCombine to the README's Included Nodes table. Existing installs with non-empty picks are unaffected by the seeder (by design). To pull the Koolook defaults into an existing install, Quick Save first, then Snapshot -> Load -> "starter". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(video): reach VHS_VideoCombine via nodes.NODE_CLASS_MAPPINGS The previous `from videohelpersuite.nodes import VideoCombine` fails inside ComfyUI's loader context because VHS uses relative imports throughout (`from .logger ...`, `from .utils ...`) that only resolve when VHS is loaded through ComfyUI's importlib spec — not through a plain absolute import path. The error surfaced as No module named 'utils.install_util'; 'utils' is not a package at Koolook load time, which caused the node to self-skip with an empty NODE_CLASS_MAPPINGS — invisible in ComfyUI's search palette. Fix: look up VHS_VideoCombine through `nodes.NODE_CLASS_MAPPINGS`, which is the documented cross-pack interface. ComfyUI's loader (custom_nodes/.../__init__.py exec_module → merge into global) runs to completion for each pack before moving to the next, and VHS sorts before Koolook alphabetically in every layout we've seen, so by the time Koolook's __init__.py runs the global registry already has VHS_VideoCombine in it. Verified against ComfyUI's nodes.py load_custom_node() at load_custom_node() lines 2196 (exec_module) and 2225-2228 (merge), which guarantee this ordering. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(video): add output_directory input for split path/name editing Overloading filename_prefix for absolute paths means losing the "just type a name" affordance — once you put the path there, there's nowhere to specify just the file root. Across many renders into the same project tree this becomes tedious: every iteration retypes the whole path. Adds an optional `output_directory` STRING input. When set: - filename_prefix is treated as just the filename root; any path components are stripped (so accidental typos like "sub/clip" don't double-up under the directory). - Absolute output_directory writes directly to that location. - Relative output_directory joins under ComfyUI's output/. When output_directory is empty, the original overloaded filename_prefix behavior is preserved unchanged (back-compat for the one-field flow). Implementation: - New pure helper `_compose_prefix(filename_prefix, output_directory)` combines the two fields into one effective prefix. The existing isabs discrimination then handles sandboxed vs absolute writes uniformly downstream. - 6 new tests cover all combinations: empty/abs/relative directory, path stripping from filename_prefix, trailing-separator trim, empty-name fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(video): coerce frontend 'undefined' STRING values to empty The ComfyUI frontend serializes a STRING widget with empty default as the literal string "undefined" when left untouched (a known long-running quirk). Without defensive normalization, an untouched output_directory was treated as a real directory name and prepended to the user's path: filename_prefix = "E:/renders/clip_v003" (typed by user) output_directory = "undefined" (frontend default, not typed) _compose_prefix(...) -> "undefined/clip_v003" (relative) _resolve_abs_target -> None (not absolute) super().combine_video -> writes to <output>/undefined/clip_v003_* The QuickTime ended up at <ComfyUI>/output/undefined/<name>.mov with a spurious `undefined/` subfolder, instead of at the absolute path the user actually specified. Fix: a `_normalize_text_input()` helper coerces the literal strings "undefined" / "null" / "None" (case-insensitive, post-strip) to "". Applied to both filename_prefix and output_directory at the top of combine_video() before any path composition. 4 new tests cover the sentinels and a regression test for the exact path-composition behavior that produced the bug. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(video-fe): wire format-specific widgets for Easy_VideoCombine VHS's frontend extension (web/js/VHS.core.js:2030) gates its format-widget machinery on `nodeData?.name == "VHS_VideoCombine"` — strict-equality on the registered node ID. Our subclass uses a different ID (`Easy_VideoCombine`), so none of VHS's per-format widget rendering ran. The user saw: [VideoHelperSuite] - WARNING - Missing input for p has been set to hq on every execution (profile defaulting to "hq" for ProRes), and had no way to pick anything other than the default — because the profile dropdown was never rendered. Fix: a minimal frontend extension at web/easy_video_combine.js (~80 LoC) that replicates `addFormatWidgets` for our node name. Hooks the format dropdown's callback to add/remove per-format widgets the same way upstream does. Self-contained — does not import from VHS's module (their helpers aren't exported as ES imports). Verifies via the actual ComfyUI install after dev-sync; static preview servers can't exercise it (depends on LiteGraph's registered_node_types at runtime). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(video): strip 'undefined' from anywhere + one-shot debug log The previous normalization only caught the EXACT literal "undefined" / "null" / "None" at field-level. Adds two stronger defenses for the case where a sentinel still slips through: 1. _normalize_text_input now also strips surrounding quotes (paste- from-Explorer artifact) and handles embedded newlines by taking the first non-empty non-sentinel line (defends against multi-line Text Multiline inputs with trailing blank or junk lines). 2. New _strip_sentinel_components helper drops any path component that is exactly a sentinel string from the composed effective_ prefix — belt-and-suspenders for any path that somehow carries `\undefined\` as a literal segment by the time it reaches us. Also adds a one-shot print() of fp_raw, od_raw, normalized values, and effective_prefix at the top of combine_video. The user is seeing an undefined/ folder under their custom path despite the field-level normalization, and pure code tracing doesn't reproduce it — the log will surface what kwargs actually contains at run time so we can fix the real cause if defensive stripping doesn't already handle it. To be removed in a follow-up once root cause is known. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(video): trim node docstring + tooltips, remove debug print - Class docstring collapsed from ~20 lines to a single sentence (the frontend was showing it as the node description on right-click, cluttering the UI). - Widget tooltips trimmed to one short sentence each — same intent, no walls of text. - Debug print() added in |
||
|
|
1bde57bf4e |
docs: fix stale fork READMEs + replace outdated copilot-instructions (#115)
* docs: fix stale fork READMEs + retire outdated copilot-instructions forks/README.md and forks/radiance_koolook/README.md both still pointed at v1_0_1 as the active version — v1_0_1 was removed in v0.1.5; the only active fork is v2_3_3. Layout examples and the Upgrade Flow Example pivot forward (v2_3_3 → hypothetical v2_4_0), with a note about SKIP_VERSION_SUFFIX for stable user-facing IDs across upgrades. .github/copilot-instructions.md was severely out of date — referenced a nonexistent k_easy_version.py, knew nothing about the Kforge Labs sidebar (now the main feature), and had no mention of koolook_routes.py, dev-sync, or the three-axis versioning model. Replaced with a thin pointer to CLAUDE.md plus the high-stability orientation Copilot needs to place a request before following links — file map, "things that bite", redirect. Nice-to-have follow-ups filed as #113 (markdown mirror of the visual sidebar guide) and #114 (small cross-ref / glossary polish). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(forks): fix v2.3.3 namespace-suffix bullet in THIRD_PARTY.md The bullet claimed all exposed v2.3.3 node IDs are namespaced with `__koolook_v2_3_3`, but both currently-registered IDs (`Easy_hdr_VAE_encode`, `Easy_hdr_VAE_decode`) are Koolook-original names that opt out via `SKIP_VERSION_SUFFIX` in versions/v2_3_3/__init__.py. The new wording names the actual exposed IDs, points at the opt-out set, and explains the reserved use of the `__koolook_v2_3_3` suffix for future ports of upstream-named classes. Caught in PR #115 review — the linked target (THIRD_PARTY.md) was contradicting the new forks/README.md and CLAUDE.md text on the same mechanism. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
27705be037 |
feat(sidebar): add onboarding guide (#107)
* feat(sidebar): add onboarding guide * docs(guide): clarify tag search casing * docs(guide): clarify workflow save copy * docs(guide): refine sidebar visuals * docs(guide): refresh save snapshot visual * docs(guide): refresh load snapshot visual * docs(guide): refresh module save visual * docs(guide): use real sidebar screenshot * docs(guide): render sidebar overview screenshot directly * docs(guide): soften sidebar overview annotations * docs(guide): clarify nodes action bar callout * docs(guide): clarify workflow action bar callout * docs(guide): simplify sidebar overview markers * docs(guide): align sidebar overview intro * docs(guide): use real snapshot recipe screenshots * docs(guide): simplify project workflow diagram * docs(guide): make function cards scannable * docs(guide): sketch module recipe icon focus * docs(guide): use real icon screenshot for module recipe * docs(guide): add cropped workflow action screenshot * docs(guide): use full toolbar screenshot for module recipe * docs(guide): use real search screenshot * docs(guide): use real node search screenshot * docs(guide): standardize screenshot recipe cards * docs(guide): tighten recipe callout styling * docs: require visual QA for UI work * docs(guide): address pass-2 review nits |
||
|
|
e95c12f6a9 |
docs(forks): finalize de-vendoring audit trail (closes #28) (#55)
* docs(forks): finalize de-vendoring audit trail (closes #28) The v0.1.4 / v0.1.5 release cycle untracked six vendored third-party trees from MAIN's git index (`git rm -r --cached`, then added to `.gitignore`), which fixed the Comfy Registry's static-scanner miscount (44 -> 8 nodes in ComfyUI-Manager) and shrank the published archive by ~3.7 MB. That part of issue #28 was already shipped. Issue #28's remaining acceptance criteria were the audit-trail bits: - list each removed tree in `forks/THIRD_PARTY.md` with upstream URL + license, and - record any modified files that were promoted into proper fork wrappers under `forks/<package>/versions/<vX_Y_Z>/`. This commit adds a "De-vendored upstream code (untracked in v0.1.4 / v0.1.5)" section to `forks/THIRD_PARTY.md` covering all six trees: - `upscaler_FIX/github_repos/ComfyUI-SuperUltimateVaceTools/` - `upscaler_FIX/github_repos/ComfyUI-multigpu/` - `upscaler_FIX/github_repos/debugg/` - `nuke_CAM_exporter/_Utils-CAM-track/Github-Repos/Comfyui-Animatedfiff-evolved/` - `nuke_CAM_exporter/_Utils-CAM-track/Github-Repos/Comfyui-kjnodes/` - `nuke_CAM_exporter/wrapper_FIX/` (kijai/ComfyUI-WanVideoWrapper) None of the six were promoted to tracked fork wrappers — the `wrapper_FIX/` work in particular was incomplete (token-count alignment fix between Wan 14B and FunCamera) and the upstream pin was never recorded, so it'll be redone from scratch against current upstream when needed. Pure docs change. No package code touched. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(forks): full #28 closure — add manifest entries + verbatim-paths note Addressing /review-pr findings on the previous commit b029570: HIGH (CONFIRMED): "Closes #28" was overstated against issue #28's literal acceptance criterion (2) — registering each removed tree in `forks/forks_manifest.yaml` with `source_repo`/`source_ref`/ `pinned_commit`/`local_paths`. The previous commit only delivered the THIRD_PARTY.md audit table (criterion (3)). This commit adds six `status: "removed"` entries to `forks/forks_manifest.yaml` (one per de-vendored tree) with: - `source_repo`: known upstream URL where determinable, "unknown" with vendor-time-no-pin caveat otherwise - `source_ref` / `pinned_commit: null`: honest about missing pins - `local_paths`: actual former paths (matches v0.1.0 git tree) - `removed_at: 2026-05-03`, `removed_in_release: "0.1.4 / 0.1.5"` - `license`: mirrors THIRD_PARTY.md ("unverified" / "GPL-3.0" / "n/a" / "assumed GPL-3.0" — last verified 2026-05-03 for kjnodes) - `notes`: cross-link back to THIRD_PARTY.md "De-vendored upstream code" section LOW (CONFIRMED but no-action): typo `Comfyui-Animatedfiff-evolved` and mixed `Comfyui-` / `ComfyUI-` casing on rows 114-115 of THIRD_PARTY.md. Verifier confirmed both reflect actual former on-disk folder names (via `git ls-tree v0.1.0`) and are intentional historical record. Added a one-paragraph preamble note above the table making this explicit so future readers don't try to "fix" the typo and break the audit trail. HIGH (DOWNGRADED to LOW by verifier): "License unverified shipped to PyPI / Comfy Registry" claim was a false-positive premise — verifier checked `gh run list --workflow=publish.yml` and found every push-to- main publish for v0.1.0/v0.1.1/v0.1.3 failed; the first successful Comfy Registry publish was the v0.1.4-prep dispatch on 2026-05-03, post-untrack. The residual concern (GitHub release source tarballs) is real but standard for any post-untrack project; preamble now states this explicitly so future readers don't mis-read "License unverified" as "shipped to users without a license check". Pure docs / metadata. No package code touched. Closes #28. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
f24838a6ce |
feat(vae): sequence dispatch + cinema color surface + alpha output (#42)
Combines two changes to Easy_hdr_VAE_encode/decode (Koolook v2.3.3 fork): 1. Rank-aware dispatch + 5D-output normalization. 5D video tensors paired with a 2D image VAE are iterated frame-by-frame on encode (stack on dim=2) and per-frame decoded on decode (cat on dim=0). 5D output from 3D-aware video VAEs (Wan 2.2, Hunyuan, CogVideoX, LTX — exposed via `vae.latent_dim == 3`) is reshaped (-1, H, W, C) to ComfyUI's standard IMAGE batch, mirroring stock VAEDecode at nodes.py:303-304. Fixes the "1056x41 count=1" symptom where 41-frame Wan 2.2 sequences collapsed to a single squashed image; SaveImage now writes one PNG per frame. 2. Cinema color surface ported from upstream Radiance v2.3.3 (without the broken 4K cosine-blend tile engine that motivated the slim fork in the first place). 12 source/target spaces including six cinema log curves (ARRI LogC3/LogC4, Sony S-Log3, Panasonic V-Log, DaVinci Intermediate, RED Log3G10) plus ACES 2065-1 and Rec.2020 Linear. Compress (Log) HDR mode pairs encode-side log-curve encoding with decode-side per-profile soft-shoulder + log-domain highlight denoise for HDR-clean roundtrips. New latent_sampling parameter (sample/mean/mode). Alpha now surfaces as a real third IMAGE return on encode (the previous flag was a no-op). Exposure is now applied in linear space for all non-Raw source spaces; Raw source still does a raw-domain multiplication so users who know what they are feeding the VAE keep their bytes intact. Helpers extracted to forks/radiance_koolook/versions/v2_3_3/color_helpers.py (log curves, soft-shoulder, log-denoise, sampling-mode helper, color matrices, dispatch tables) so nodes_vae.py stays focused on node wiring. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |
||
|
|
1098c17f5a |
fix(vae): wrap Easy_hdr_VAE_encode output in LATENT dict (#39)
vae.encode() returns a raw torch.Tensor, but ComfyUI's LATENT type is
contractually `{"samples": tensor}`. The encoder was returning the bare
tensor, so wiring it into KSampler triggered:
IndexError: too many indices for tensor of dimension 5
at `latent_image = latent["samples"]` in common_ksampler. Python 3.11+
traceback markers point right at the subscript. With Wan 2.2's 5-D
(B, C, T, H, W) latent, the string-on-tensor index goes through PyTorch's
__getitem__ and surfaces as IndexError rather than TypeError.
Fix matches upstream ComfyUI's VAEEncode pattern:
return ({"samples": t}, )
The decoder side already destructures `samples["samples"]` correctly,
so it's unaffected.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
b7a17b570b |
chore(categories): split Koolook/VFX into Pipeline / Image / VAE + fix VAE display names (#38)
Three small UX fixes from the v0.1.7 post-release inspection:
1. Categories: the catch-all `Koolook/VFX` subfolder is replaced with
three more granular ones:
- Easy_Version, EasyAIPipeline -> Koolook/Pipeline
- easy_ImageBatch -> Koolook/Image (joins EasyResize)
- Easy_hdr_VAE_encode/decode -> Koolook/VAE
2. Easy_hdr_VAE_encode / Easy_hdr_VAE_decode display names: previously
the display equalled the node ID (`Easy_hdr_VAE_encode`) so the user
couldn't find them by typing `koolook` in the node-add search filter
— they were registered correctly but invisible to the filter. Now:
"Easy_hdr_VAE_encode" -> "Easy HDR VAE Encode (Koolook)"
"Easy_hdr_VAE_decode" -> "Easy HDR VAE Decode (Koolook)"
Matches the title-case " (Koolook)"-suffixed convention used by the
rest of the pack since v0.1.7.
3. README node tables + categories list updated to match.
Class IDs unchanged everywhere -- saved workflows that reference any of
these IDs continue to load and run unchanged. CATEGORY is purely a UI
hint and only affects the node-add menu hierarchy.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
|
||
|
|
f57ea38b71 |
feat(EasyResize): rename to EasyResize_Koolook + proper KJ Nodes attribution (#35)
Resolves the genuine `EasyResize` node-ID collision with `ComfyUI-EasyFilePaths` (orange-triangle warning #8 in ComfyUI-Manager). Both packs were registering the bare class name `EasyResize` — load order determined which one a user actually got. Changes: - k_easy_resize.py: - Add SPDX-License-Identifier: GPL-3.0-or-later header. - Add proper attribution block crediting kijai/ComfyUI-KJNodes (Resize Image V2, GPL-3.0) as the original interface inspiration, with a per-feature list of how the Koolook implementation materially extended the surface (aspect-ratio parser, keep_proportion modes, mask + composed outputs, device selection, target/original W/H reporting, color-panel passthrough). - Register both `EasyResize_Koolook` (canonical, new) AND `EasyResize` (deprecated alias) IDs pointing at the same class. Saved workflows using the legacy `EasyResize` ID continue to load and run; new workflows pick `EasyResize_Koolook` from the search and avoid the conflict. - Display name on legacy alias reads `Easy Resize (deprecated, use 'Easy Resize (Koolook)')` to nudge users to switch. - forks/THIRD_PARTY.md: full attribution entry for KJ Nodes (was missing — manifest had it as `license: "unknown"` / `sync_state: "needs-upstream-reference"`). License verified as GPL-3.0 against upstream `LICENSE` file (same as our pack — no relicense pressure). - forks/forks_manifest.yaml: KJ Nodes entry upgraded from `license: "unknown"` to `license: "GPL-3.0"` + `license_verified_at`, `status: "inspired_reimplementation"` (more accurate than `modified`), and the two exposed_node_ids are now recorded explicitly. - README.md: node table, category list, "Easy Resize" section, and Wan-related descriptions all updated to use the new canonical `EasyResize_Koolook` ID and display name. - pyproject.toml: 0.1.5 -> 0.1.6. - CHANGELOG.md: [0.1.6] entry covering the rename + attribution work. Follows the node-versioning rules from the `node-versioning` discussion in PR review: rename via alias, never break saved workflows, document the deprecation in CHANGELOG. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
60e90ec6c8 |
Release v0.1.5: registry hygiene + drop unused v1_0_1 Radiance fork (44 nodes → 8) (#34)
* chore: remove maintainer dev workspaces from git tracking + bump 0.1.4 ComfyUI-Manager was reporting "44 nodes / 13 conflicts" for this pack because the Comfy Registry's static scanner walks every `*.py` in the published archive and reads `NODE_CLASS_MAPPINGS` dicts — including the ones inside vendored third-party clones sitting in two folders that were never meant to be part of the package: upscaler_FIX/github_repos/ComfyUI-SuperUltimateVaceTools/ upscaler_FIX/github_repos/ComfyUI-multigpu/ nuke_CAM_exporter/wrapper_FIX/wrapper_fun_camera_*-code/ nuke_CAM_exporter/_Utils-CAM-track/Github-Repos/... These folders were the maintainer's local debugging / camera-tracking scratchpads, accidentally committed in Dec 2025 commits |
||
|
|
5881e8a808 |
Release v0.1.3: GPL-3.0 relicense, Easy_hdr_VAE_encode/decode (v2.3 fork), license/fork skills (#33)
* chore: relicense to GPL-3.0 and backfill upstream attribution ComfyUI-Koolook v0.1.0 and v0.1.1 shipped under a claimed MIT license while already incorporating GPL-3.0-derived code from fxtdstudios/radiance under forks/radiance_koolook/. GPL-3.0 §5(c) requires the entire combined work to be GPL-3.0 — relicensing the whole package is the simplest honest fix and aligns with the dominant license posture of the ComfyUI custom-node ecosystem. Changes: - Add full GPL-3.0 LICENSE text at repo root (pyproject.toml already referenced LICENSE but the file did not exist). - Update README.md "## License" section to declare GPL-3.0 and explain the §5(c) implication for downstream users. - Backfill forks/THIRD_PARTY.md with full attribution entries for the Radiance v1.0.1 baseline (already in the repo) and the upcoming Radiance v2.3.3 VAE subset (scaffolded in v0.1.2). - Update forks/forks_manifest.yaml: set the existing entry's license field to "GPL-3.0" (was "to_verify_at_source_ref"), add a license_verified_at field, and pre-register the v2.3.3 VAE entry with verified license metadata. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(skills): add license-pre-check + port add-external-fork to .claude/skills/ Two skills to prevent the v0.1.0/v0.1.1 license-incompatibility class of bug from happening again on future forks: - license-pre-check: blocking compatibility audit run BEFORE any code is copied or written based on upstream. Reads the upstream LICENSE, compares against ours via an SPDX matrix, refuses to proceed on incompatible combinations, and records the result for the manifest. - add-external-fork: ports the existing .cursor/skills/add-external-fork procedure to .claude/skills/ so Claude Code can run it directly. The port adds a mandatory Phase 0 ("run license-pre-check first") and expands Phase 3c to require GPL §5(a) modification headers on derived files. The .cursor/ copy is left in place for Cursor users; the two should be kept in sync manually until a unifying mechanism is adopted. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * feat(forks): port v2.3 VAE Encode/Decode as slim video-friendly wrapper Adds forks/radiance_koolook/versions/v2_3_3/ — a slim re-implementation of RadianceVAEEncode / RadianceVAEDecode under the namespace suffix __koolook_v2_3_3. Mirrors the input surface of upstream's RadianceVAE4KEncode / RadianceVAE4KDecode (fxtdstudios/radiance v2.3.3, GPL-3.0) but deliberately skips the 4K cosine-blend tile engine. Why skip the tile engine: that pipeline errors with `size of tensor a (192) must match the size of tensor b (132) at non-singleton dimension 4` when chained into Wan 2.2 video workflows, because the tiler's spatial layout disagrees with the video VAE's internal temporal-aware encoding. Modern video VAEs (Wan 2.2, Hunyuan, CogVideoX, LTX) handle their own internal stitching, so the tiler is redundant and actively breaks the chain. What ships: - forks/radiance_koolook/versions/v2_3_3/nodes_vae.py — RadianceVAEEncode and RadianceVAEDecode classes (~330 lines), rank-agnostic, with a STRING `debug_info` second return that mirrors the pattern from v1's RadianceOCIOColorTransformV2. - forks/radiance_koolook/versions/v2_3_3/__init__.py — applies __koolook_v2_3_3 namespace suffix and " (Koolook v2.3.3)" display suffix, mirroring the v1_0_1 wrapper layout. - forks/radiance_koolook/versions/v2_3_3/UPSTREAM_PIN.yaml — pinned upstream commit f262f47 + license metadata. - forks/radiance_koolook/__init__.py — merges v1_0_1 + v2_3_3 mappings. - pyproject.toml — version bumped to 0.1.2. - CHANGELOG.md — full [0.1.2] entry covering the license relicense, the new v2_3_3 fork, the two new skills, and the carry-over RELEASING.md docs that landed on main since 0.1.1. Existing __koolook_v1_0_1 nodes are unchanged and remain available for backward compatibility with saved workflows. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * refactor(forks/v2_3_3): rename VAE nodes to Easy_hdr_VAE_encode/decode + bump 0.1.3 Per user direction, the new video-friendly VAE wrappers are now exposed under clean Koolook-style IDs and display names — no more `__koolook_v2_3_3` suffix in the ComfyUI node-add UI: - `Easy_hdr_VAE_encode` (was `RadianceVAEEncode__koolook_v2_3_3`) - `Easy_hdr_VAE_decode` (was `RadianceVAEDecode__koolook_v2_3_3`) Why: when both upstream Radiance v2.3.3 and our pack are installed, both expose `Radiance VAE Encode` in the search — the Koolook-suffixed display name still buried "Koolook" at the end of a long line, so users were likely to mis-pick. The new names lead with the Koolook brand and the Easy_* family convention used elsewhere in this pack (Easy_Version, EasyAIPipeline, EasyResize, etc.). Mechanism: new `SKIP_VERSION_SUFFIX = {...}` set in `versions/v2_3_3/__init__.py` opts specific IDs out of the auto-suffix. The version is still tracked structurally (file path) and textually (file header, UPSTREAM_PIN.yaml, forks/THIRD_PARTY.md). Future Koolook-original nodes in this fork can opt out the same way; ports of upstream-named classes still get the suffix by default for collision safety. 0.1.2 was test-published to the registry with the previous IDs but never tagged or formally released; bumping to 0.1.3 publishes the renamed version cleanly. Old 0.1.2 entry retained in CHANGELOG with a "superseded" note for transparency. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|
|
c529a1f947 |
Prepare v0.1.0 release metadata and node naming.
Set project version to 0.1.0, align changelog entries for the new fork/versioning baseline, and include the compact OCIO node ID override for the v1_0_1 Radiance set. Made-with: Cursor |
||
|
|
52712f05b6 |
Align release references and define clean tagging policy.
Remove stale links to deleted docs, document forward tagging conventions for repo/fork/version namespaces, and keep release checklist aligned with current workflow. Made-with: Cursor |
||
|
|
7f8eda4809 |
Restructure fork tracking and versioned Radiance layout.
Consolidate fork metadata into forks/, move Radiance custom code into versioned folders, and align external reference naming so upgrades and cross-machine parity are easier to manage. Made-with: Cursor |