Merge pull request #26 from malkuthro/release/v0.1.0-prep
Release/v0.1.0 prep
This commit is contained in:
@@ -10,9 +10,9 @@ assignees: []
|
|||||||
|
|
||||||
- [ ] Node import/startup passes on clean ComfyUI
|
- [ ] Node import/startup passes on clean ComfyUI
|
||||||
- [ ] Core workflows smoke-tested
|
- [ ] Core workflows smoke-tested
|
||||||
- [ ] `third_party/forks_manifest.yaml` reviewed/updated
|
- [ ] `forks/forks_manifest.yaml` reviewed/updated
|
||||||
- [ ] `CHANGELOG.md` updated
|
- [ ] `CHANGELOG.md` updated
|
||||||
- [ ] Release notes drafted (`docs/RELEASE_NOTES_TEMPLATE.md`)
|
- [ ] Release notes drafted (PR body + `CHANGELOG.md`)
|
||||||
|
|
||||||
## Publish
|
## Publish
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ Describe what this PR changes and why.
|
|||||||
- [ ] I updated docs (if needed)
|
- [ ] I updated docs (if needed)
|
||||||
- [ ] I updated `CHANGELOG.md` (if needed)
|
- [ ] I updated `CHANGELOG.md` (if needed)
|
||||||
- [ ] I tested node loading in ComfyUI
|
- [ ] I tested node loading in ComfyUI
|
||||||
- [ ] I documented third-party derivations/changes in `third_party/`
|
- [ ] I documented third-party derivations/changes in `forks/`
|
||||||
- [ ] I verified no secrets/tokens are included
|
- [ ] I verified no secrets/tokens are included
|
||||||
|
|
||||||
## Testing Notes
|
## Testing Notes
|
||||||
|
|||||||
+13
-15
@@ -6,20 +6,18 @@ The format is inspired by Keep a Changelog and SemVer.
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
## [0.1.0] - 2026-04-24
|
||||||
- Initial release/process scaffolding:
|
|
||||||
- `docs/RELEASE_WORKFLOW.md`
|
|
||||||
- `docs/REPO_AUDIT_2026-02-14.md`
|
|
||||||
- `third_party/THIRD_PARTY.md`
|
|
||||||
- `third_party/forks_manifest.yaml`
|
|
||||||
- `tools/report_mods.py`
|
|
||||||
- Contribution/release templates:
|
|
||||||
- `.github/PULL_REQUEST_TEMPLATE.md`
|
|
||||||
- `.github/ISSUE_TEMPLATE/release_checklist.md`
|
|
||||||
- `docs/RELEASE_NOTES_TEMPLATE.md`
|
|
||||||
- RunPod_Comfy scaffold was previously added during iteration and has since been moved to a private management repository.
|
|
||||||
|
|
||||||
## [0.1.0] - 2026-02-14
|
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Project-level release/versioning strategy and third-party tracking scaffold.
|
- Fork tracking moved to `forks/` with centralized workflow documentation.
|
||||||
|
- Versioned Radiance fork package layout under `forks/radiance_koolook/versions/v1_0_1`.
|
||||||
|
- `CLAUDE.md` and `Glossary.md` to keep workflow and naming conventions explicit.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Root node loader now imports the versioned Radiance fork entrypoint.
|
||||||
|
- Release/checklist/template references updated to current fork-based paths.
|
||||||
|
- Introduced compact node ID override for OCIO transform: `k_easy_OCIO_v101`.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Legacy `ACES_workflow/radiance` tree and duplicated tracking paths under `third_party/`.
|
||||||
|
- Deprecated docs/assets/workflow artifacts removed during repository cleanup.
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
# ComfyUI-Koolook Agent Workflow
|
||||||
|
|
||||||
|
## Source-of-truth model
|
||||||
|
|
||||||
|
- MAIN repo: `ComfyUI-Koolook`
|
||||||
|
- External forks root (default): `../ComfyUI-Forks` (relative to MAIN)
|
||||||
|
- External third-party code must remain outside MAIN.
|
||||||
|
|
||||||
|
## Hard rules
|
||||||
|
|
||||||
|
- Do not vendor/copy full third-party repositories into MAIN.
|
||||||
|
- Keep MAIN limited to:
|
||||||
|
- custom Koolook nodes
|
||||||
|
- wrapper loaders under `forks/`
|
||||||
|
- fork tracking docs under `forks/`
|
||||||
|
- For third-party integrations, update:
|
||||||
|
- `forks/forks_manifest.yaml`
|
||||||
|
- `forks/THIRD_PARTY.md`
|
||||||
|
- `forks/README.md` (if workflow conventions change)
|
||||||
|
|
||||||
|
## Radiance v1 policy
|
||||||
|
|
||||||
|
- Package entrypoint: `forks/radiance_koolook/__init__.py`
|
||||||
|
- Modified nodes live in MAIN and are GitHub-tracked:
|
||||||
|
- `forks/radiance_koolook/versions/v1_0_1/nodes_hdr.py`
|
||||||
|
- `forks/radiance_koolook/versions/v1_0_1/nodes_color_management.py`
|
||||||
|
- `forks/radiance_koolook/versions/v1_0_1/nodes_dna.py`
|
||||||
|
- External sibling checkout (`../ComfyUI-Forks/radiance-v1.0.1-koolook`) is raw reference only.
|
||||||
|
- Current baseline pin for raw comparison: `f1b8ae330848fa08aba24c9d3e355cb432d3515b` (`comfyui` tag).
|
||||||
|
- Exposed node IDs must stay namespaced with version suffixes (`__koolook_v1_0_1`) to avoid collisions.
|
||||||
|
- `forks/forks_manifest.yaml` must include `source_ref` + `pinned_commit`.
|
||||||
|
|
||||||
|
## Change management
|
||||||
|
|
||||||
|
- When upgrading external forks (v2, v3, etc.), add a new wrapper/version namespace.
|
||||||
|
- Never rename existing namespaced node IDs that already appear in saved workflows.
|
||||||
+49
@@ -0,0 +1,49 @@
|
|||||||
|
# Glossary
|
||||||
|
|
||||||
|
Short, plain-English definitions for this repo workflow.
|
||||||
|
|
||||||
|
## Root `__init__.py`
|
||||||
|
- The ComfyUI entry file for this plugin.
|
||||||
|
- Its job is to import node mappings and expose `NODE_CLASS_MAPPINGS` / `NODE_DISPLAY_NAME_MAPPINGS`.
|
||||||
|
- Keep it clean and focused on loading nodes.
|
||||||
|
|
||||||
|
## Main Repo
|
||||||
|
- This repository: `ComfyUI-Koolook`.
|
||||||
|
- Stores Koolook-owned code, docs, and lightweight integration wrappers.
|
||||||
|
- Should not store full external third-party repositories.
|
||||||
|
|
||||||
|
## External Forks
|
||||||
|
- Raw upstream/fork reference repos kept outside MAIN.
|
||||||
|
- Default location is a sibling folder: `../ComfyUI-Forks`.
|
||||||
|
- Each external repo should be pinned to a version/tag + exact commit.
|
||||||
|
|
||||||
|
## Wrapper
|
||||||
|
- A small adapter module in `forks/` that loads locally tracked modified nodes and namespaces node IDs.
|
||||||
|
- Purpose: avoid collisions and keep external logic separate from root loader.
|
||||||
|
- For Radiance, the package entrypoint is `forks/radiance_koolook/__init__.py`.
|
||||||
|
|
||||||
|
## Version Folder
|
||||||
|
- A folder under `forks/radiance_koolook/versions/` that stores one maintained codebase snapshot (example: `v1_0_1`).
|
||||||
|
- Keep upstream parity metadata in `UPSTREAM_PIN.yaml` inside each version folder.
|
||||||
|
|
||||||
|
## Namespace Suffix
|
||||||
|
- A suffix added to internal node IDs (example: `__koolook_v1_0_1`).
|
||||||
|
- Prevents collisions between versions (v1, v2, upstream, etc.).
|
||||||
|
- Protects old workflows from breaking.
|
||||||
|
|
||||||
|
## `forks/forks_manifest.yaml`
|
||||||
|
- Machine-readable tracker for external dependencies.
|
||||||
|
- Contains source repo, source ref, pinned commit, and external relative path.
|
||||||
|
- This is the source of truth for reproducible setup on new machines.
|
||||||
|
|
||||||
|
## `forks/THIRD_PARTY.md`
|
||||||
|
- Human-readable attribution and notes.
|
||||||
|
- Explains what was modified and why.
|
||||||
|
|
||||||
|
## Portable Paths
|
||||||
|
- Prefer relative paths so setup works on Windows/macOS/Linux.
|
||||||
|
- Keep external references under `../ComfyUI-Forks` for portable machine setup.
|
||||||
|
|
||||||
|
## Pinned Commit
|
||||||
|
- The exact git commit hash you lock to for reproducibility.
|
||||||
|
- Use this when moving to another computer so behavior matches exactly.
|
||||||
@@ -33,9 +33,67 @@ RunPod management has been moved to a separate private repository for active ope
|
|||||||
|
|
||||||
For production usage, install a pinned stable tag/commit instead of following moving branch heads.
|
For production usage, install a pinned stable tag/commit instead of following moving branch heads.
|
||||||
|
|
||||||
- Release workflow: `docs/RELEASE_WORKFLOW.md`
|
- Release workflow: `forks/README.md`
|
||||||
- Change history: `CHANGELOG.md`
|
- Change history: `CHANGELOG.md`
|
||||||
- Third-party tracking: `third_party/THIRD_PARTY.md`, `third_party/forks_manifest.yaml`
|
- Fork tracking: `forks/THIRD_PARTY.md`, `forks/forks_manifest.yaml`
|
||||||
|
- Fork workflow guide: `forks/README.md`
|
||||||
|
|
||||||
|
### 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`
|
||||||
|
|
||||||
|
## 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`
|
||||||
|
- Version path (current): `forks/radiance_koolook/versions/v1_0_1/`
|
||||||
|
- Modified node source is local and tracked in MAIN:
|
||||||
|
- `forks/radiance_koolook/versions/v1_0_1/nodes_hdr.py`
|
||||||
|
- `forks/radiance_koolook/versions/v1_0_1/nodes_color_management.py`
|
||||||
|
- `forks/radiance_koolook/versions/v1_0_1/nodes_dna.py`
|
||||||
|
- External checkout is reference-only raw upstream:
|
||||||
|
- default location: `../ComfyUI-Forks/radiance-v1.0.1-koolook`
|
||||||
|
- baseline used for comparison: upstream `comfyui` tag (`f1b8ae330848fa08aba24c9d3e355cb432d3515b`) because `v1.0.1` tag is not published upstream
|
||||||
|
- Node IDs are namespaced with version suffixes (current: `__koolook_v1_0_1`) to avoid collisions.
|
||||||
|
- Each version folder should include `UPSTREAM_PIN.yaml` to keep parity with external pinned references.
|
||||||
|
|
||||||
|
### 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`:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
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
|
## Installation
|
||||||
### Via ComfyUI-Manager (Recommended)
|
### Via ComfyUI-Manager (Recommended)
|
||||||
|
|||||||
+8
-14
@@ -1,13 +1,3 @@
|
|||||||
# Import node classes from their respective files
|
|
||||||
from .k_easy_version import Easy_Version
|
|
||||||
from .k_easy_wan22_prompt import EasyWan22Prompt
|
|
||||||
from .k_easy_resize import EasyResize
|
|
||||||
from .k_ai_pipeline import EasyAIPipeline
|
|
||||||
from .k_easy_image_batch import easy_ImageBatch # Added for new VFX batch node
|
|
||||||
|
|
||||||
# Aggregate mappings from each node file
|
|
||||||
# Assuming each node file defines its own NODE_CLASS_MAPPINGS and NODE_DISPLAY_NAME_MAPPINGS
|
|
||||||
|
|
||||||
from .k_easy_version import NODE_CLASS_MAPPINGS as version_mappings
|
from .k_easy_version import NODE_CLASS_MAPPINGS as version_mappings
|
||||||
from .k_easy_version import NODE_DISPLAY_NAME_MAPPINGS as version_display
|
from .k_easy_version import NODE_DISPLAY_NAME_MAPPINGS as version_display
|
||||||
|
|
||||||
@@ -26,14 +16,17 @@ from .k_easy_image_batch import NODE_DISPLAY_NAME_MAPPINGS as batch_display # A
|
|||||||
from .k_easy_track import NODE_CLASS_MAPPINGS as cam_loader_mappings
|
from .k_easy_track import NODE_CLASS_MAPPINGS as cam_loader_mappings
|
||||||
from .k_easy_track import NODE_DISPLAY_NAME_MAPPINGS as cam_loader_display
|
from .k_easy_track import NODE_DISPLAY_NAME_MAPPINGS as cam_loader_display
|
||||||
|
|
||||||
# Merge all mappings
|
from .forks.radiance_koolook import NODE_CLASS_MAPPINGS as radiance_koolook_mappings
|
||||||
|
from .forks.radiance_koolook import NODE_DISPLAY_NAME_MAPPINGS as radiance_koolook_display
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
**version_mappings,
|
**version_mappings,
|
||||||
**wan_mappings,
|
**wan_mappings,
|
||||||
**resize_mappings,
|
**resize_mappings,
|
||||||
**pipeline_mappings,
|
**pipeline_mappings,
|
||||||
**batch_mappings, # Added for new VFX batch node
|
**batch_mappings,
|
||||||
**cam_loader_mappings,
|
**cam_loader_mappings,
|
||||||
|
**radiance_koolook_mappings,
|
||||||
}
|
}
|
||||||
|
|
||||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
@@ -41,9 +34,10 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
**wan_display,
|
**wan_display,
|
||||||
**resize_display,
|
**resize_display,
|
||||||
**pipeline_display,
|
**pipeline_display,
|
||||||
**batch_display, # Added for new VFX batch node
|
**batch_display,
|
||||||
**cam_loader_display,
|
**cam_loader_display,
|
||||||
|
**radiance_koolook_display,
|
||||||
}
|
}
|
||||||
|
|
||||||
WEB_DIRECTORY = "./web"
|
WEB_DIRECTORY = "./web"
|
||||||
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS', 'WEB_DIRECTORY']
|
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS", "WEB_DIRECTORY"]
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
# Release Notes Template
|
|
||||||
|
|
||||||
Use this template when publishing a GitHub Release for a stable tag.
|
|
||||||
|
|
||||||
## Title
|
|
||||||
|
|
||||||
`vX.Y.Z`
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
One short paragraph explaining what changed and why users should upgrade.
|
|
||||||
|
|
||||||
## ✅ Tested Matrix
|
|
||||||
|
|
||||||
- ComfyUI commit/tag:
|
|
||||||
- Python version:
|
|
||||||
- CUDA version:
|
|
||||||
- GPU(s):
|
|
||||||
- OS:
|
|
||||||
|
|
||||||
## Added
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
## Changed
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
## Fixed
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
## Breaking Changes
|
|
||||||
|
|
||||||
- None / list here
|
|
||||||
|
|
||||||
## Known Issues
|
|
||||||
|
|
||||||
-
|
|
||||||
|
|
||||||
## Upgrade Notes
|
|
||||||
|
|
||||||
- Any migration steps for users
|
|
||||||
- Any node behavior changes to watch for
|
|
||||||
|
|
||||||
## Install
|
|
||||||
|
|
||||||
Production recommendation: install by tag/commit, not moving branch heads.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone --branch vX.Y.Z https://github.com/malkuthro/ComfyUI-Koolook.git
|
|
||||||
```
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
# Release Workflow (Stable + Dev)
|
|
||||||
|
|
||||||
This project supports production users, so releases should be predictable.
|
|
||||||
|
|
||||||
## Branches
|
|
||||||
|
|
||||||
- `main` → active development
|
|
||||||
- `release/stable` → tested commits only (optional but recommended)
|
|
||||||
|
|
||||||
If you do not want two long-lived branches yet, keep using `main` and only publish tested tags.
|
|
||||||
|
|
||||||
## Versioning (SemVer)
|
|
||||||
|
|
||||||
Use tags in the format `vMAJOR.MINOR.PATCH`:
|
|
||||||
|
|
||||||
- `v1.0.0` = first stable release
|
|
||||||
- `v1.0.1` = bugfixes only
|
|
||||||
- `v1.1.0` = new backward-compatible features
|
|
||||||
- `v2.0.0` = breaking changes
|
|
||||||
|
|
||||||
## Minimal Release Checklist
|
|
||||||
|
|
||||||
1. Confirm node load on a clean ComfyUI install
|
|
||||||
2. Confirm key workflows still run
|
|
||||||
3. Update `CHANGELOG.md`
|
|
||||||
4. Create and push annotated tag:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git tag -a vX.Y.Z -m "Release vX.Y.Z"
|
|
||||||
git push origin vX.Y.Z
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Publish GitHub release notes (what changed, tested matrix, known issues)
|
|
||||||
- use `docs/RELEASE_NOTES_TEMPLATE.md`
|
|
||||||
|
|
||||||
## Production Install Recommendation
|
|
||||||
|
|
||||||
Users in production should install by tag (not moving branch heads).
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
git clone --branch v1.0.0 https://github.com/malkuthro/ComfyUI-Koolook.git
|
|
||||||
```
|
|
||||||
|
|
||||||
Or in ComfyUI Manager, pin to a known stable tag/commit.
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Repo Audit — 2026-02-14
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
The repository is functional but currently mixes multiple concerns (custom nodes, workflow assets, local environment artifacts, and experiments). This increases maintenance risk for production users.
|
|
||||||
|
|
||||||
## Findings
|
|
||||||
|
|
||||||
1. **Mixed-purpose layout**
|
|
||||||
- Root contains node source, workflows, utility projects, docs, and assets.
|
|
||||||
- Recommendation: gradually separate into clear areas (e.g. `nodes/`, `workflows/`, `runpod/`, `docs/`).
|
|
||||||
|
|
||||||
2. **Environment artifact present (`.venv/`)**
|
|
||||||
- `.venv` is ignored by `.gitignore`, but still exists locally.
|
|
||||||
- Recommendation: keep untracked; never commit virtual env artifacts.
|
|
||||||
|
|
||||||
3. **Release policy not explicit**
|
|
||||||
- No formal SemVer/tag workflow documented.
|
|
||||||
- Addressed by adding `docs/RELEASE_WORKFLOW.md` and `CHANGELOG.md`.
|
|
||||||
|
|
||||||
4. **Third-party modifications not centrally tracked**
|
|
||||||
- Some code is derived/modified from external repos.
|
|
||||||
- Addressed by adding `third_party/THIRD_PARTY.md` and `third_party/forks_manifest.yaml`.
|
|
||||||
|
|
||||||
## Immediate Next Steps
|
|
||||||
|
|
||||||
1. Fill `third_party/forks_manifest.yaml` for each external-derived node.
|
|
||||||
2. Publish first stable tag after verification.
|
|
||||||
3. Add CI release checks later (optional phase).
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# RunPod Management Notice
|
|
||||||
|
|
||||||
RunPod setup, image build/deploy scaffolding, and recipe iteration were moved to a private repository.
|
|
||||||
|
|
||||||
This public repository stays focused on ComfyUI custom node/plugin code.
|
|
||||||
|
|
||||||
If you need RunPod runtime operations, use the private management repository.
|
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
# Forks Workflow
|
||||||
|
|
||||||
|
This folder is the single source for all fork tracking metadata inside MAIN.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
- Keep modified forked code in MAIN under versioned paths (for GitHub tracking).
|
||||||
|
- Keep raw external checkouts outside MAIN under `../ComfyUI-Forks` (for upstream reference/sync).
|
||||||
|
- Keep all fork metadata in one place:
|
||||||
|
- `forks/forks_manifest.yaml`
|
||||||
|
- `forks/THIRD_PARTY.md`
|
||||||
|
|
||||||
|
## Folder Layout in MAIN
|
||||||
|
|
||||||
|
- `forks/radiance_koolook/__init__.py` -> package entrypoint used by root `__init__.py`
|
||||||
|
- `forks/radiance_koolook/versions/v1_0_1/` -> local modified node sources
|
||||||
|
- `forks/radiance_koolook/versions/<version>/UPSTREAM_PIN.yaml` -> exact upstream parity pin
|
||||||
|
|
||||||
|
## External Folder Naming (outside MAIN)
|
||||||
|
|
||||||
|
Root: `../ComfyUI-Forks`
|
||||||
|
|
||||||
|
- Pinned baseline checkout:
|
||||||
|
- `radiance-v1.0.1-koolook`
|
||||||
|
- Rolling upstream checkout:
|
||||||
|
- `radiance-main-upstream`
|
||||||
|
|
||||||
|
These names are referenced from `forks/forks_manifest.yaml`.
|
||||||
|
|
||||||
|
## Referencing System
|
||||||
|
|
||||||
|
For each fork entry in `forks/forks_manifest.yaml`:
|
||||||
|
|
||||||
|
- `source_repo` = canonical upstream URL
|
||||||
|
- `source_ref` = release/tag (or baseline descriptor)
|
||||||
|
- `pinned_commit` = exact reproducible commit hash
|
||||||
|
- `external_checkout.relative_path_from_forks_root` = pinned raw reference folder
|
||||||
|
- `external_checkout.upstream_tracking_path_from_forks_root` = rolling upstream folder
|
||||||
|
- `local_paths` = files tracked in MAIN
|
||||||
|
|
||||||
|
## Tagging Policy (from now on)
|
||||||
|
|
||||||
|
- MAIN repo tags use SemVer: `vMAJOR.MINOR.PATCH`.
|
||||||
|
- Local fork version folders use underscore variant of the same version:
|
||||||
|
- `v1_0_1`, `v2_3_3`, etc.
|
||||||
|
- Node mapping namespace suffixes must match folder versions:
|
||||||
|
- `__koolook_v1_0_1`, `__koolook_v2_3_3`, etc.
|
||||||
|
- Keep `source_ref` + `pinned_commit` in `forks/forks_manifest.yaml` and `UPSTREAM_PIN.yaml`.
|
||||||
|
|
||||||
|
## Upgrade Flow Example (Radiance)
|
||||||
|
|
||||||
|
1. Compare current local version (`forks/radiance_koolook/versions/v1_0_1`) with pinned raw baseline (`../ComfyUI-Forks/radiance-v1.0.1-koolook`).
|
||||||
|
2. Pull latest upstream in `../ComfyUI-Forks/radiance-main-upstream`.
|
||||||
|
3. Create a new local version folder in MAIN, e.g. `forks/radiance_koolook/versions/v2_3_3`.
|
||||||
|
4. Port/validate modifications.
|
||||||
|
5. Namespace node IDs for that version.
|
||||||
|
6. Add new entry/update metadata in `forks/forks_manifest.yaml`.
|
||||||
|
|
||||||
|
## Rule of Thumb
|
||||||
|
|
||||||
|
- If it is modified production code -> keep in MAIN under `forks/.../versions/...`
|
||||||
|
- If it is raw upstream reference -> keep outside MAIN under `../ComfyUI-Forks`
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# Third-Party Code & Attribution
|
||||||
|
|
||||||
|
Use this file to track external repositories, licenses, and local modifications.
|
||||||
|
|
||||||
|
## Entry Template
|
||||||
|
|
||||||
|
- Name:
|
||||||
|
- Upstream repo URL:
|
||||||
|
- Upstream commit/tag used:
|
||||||
|
- License:
|
||||||
|
- Local path(s):
|
||||||
|
- What changed locally:
|
||||||
|
- Why changed:
|
||||||
|
- Last reviewed:
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
- Keep this file updated when syncing upstream changes.
|
||||||
|
- If a node is heavily modified, document behavior differences clearly for users.
|
||||||
|
- For external fork variants, track exact upstream tag and pinned commit in `forks_manifest.yaml`.
|
||||||
|
- Freeze baseline first, then namespace node IDs before introducing newer upstream variants.
|
||||||
|
- Keep paths relative for portability (`../ComfyUI-Forks` by default).
|
||||||
|
- Keep only wrappers/manifests in MAIN; do not vendor large third-party trees here.
|
||||||
|
- Keep modified node source files in MAIN when you want full GitHub tracking and one-repo commits.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Forked/variant node packages managed inside this repository."""
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
version: 1
|
||||||
|
updated_at: "2026-04-24"
|
||||||
|
external_forks:
|
||||||
|
root_relative_to_main: "../ComfyUI-Forks"
|
||||||
|
root_env_var: "KOLOOK_FORKS_DIR"
|
||||||
|
notes: "Keep raw upstream/fork clones here. Modified working nodes live in MAIN and are tracked by this repository."
|
||||||
|
naming_convention:
|
||||||
|
pinned_baseline: "<repo>-v<version>-koolook"
|
||||||
|
rolling_upstream: "<repo>-main-upstream"
|
||||||
|
entries:
|
||||||
|
- id: "radiance_koolook_v1_0_1_baseline"
|
||||||
|
name: "Radiance Koolook v1.0.1 baseline"
|
||||||
|
source_repo: "https://github.com/fxtdstudios/radiance.git"
|
||||||
|
source_ref: "comfyui (closest public baseline; v1.0.1 tag not found upstream)"
|
||||||
|
pinned_commit: "f1b8ae330848fa08aba24c9d3e355cb432d3515b"
|
||||||
|
external_checkout:
|
||||||
|
relative_path_from_forks_root: "radiance-v1.0.1-koolook"
|
||||||
|
upstream_tracking_path_from_forks_root: "radiance-main-upstream"
|
||||||
|
default_branch: "koolook/v1-multiframe"
|
||||||
|
local_paths:
|
||||||
|
- "forks/radiance_koolook/versions/v1_0_1/nodes_hdr.py"
|
||||||
|
- "forks/radiance_koolook/versions/v1_0_1/nodes_color_management.py"
|
||||||
|
- "forks/radiance_koolook/versions/v1_0_1/nodes_dna.py"
|
||||||
|
- "forks/radiance_koolook/versions/v1_0_1/__init__.py"
|
||||||
|
- "forks/radiance_koolook/versions/v1_0_1/UPSTREAM_PIN.yaml"
|
||||||
|
- "forks/radiance_koolook/__init__.py"
|
||||||
|
status: "modified"
|
||||||
|
sync_state: "local-modified-from-upstream"
|
||||||
|
license: "to_verify_at_source_ref"
|
||||||
|
notes: "Local modifications include single-frame to multi-frame behavior changes and are committed in MAIN. External checkout is kept only as raw upstream reference."
|
||||||
|
|
||||||
|
- id: "easy_resize_inspiration"
|
||||||
|
name: "Easy Resize (inspired component)"
|
||||||
|
source_repo: "https://github.com/kijai/ComfyUI-KJNodes"
|
||||||
|
source_ref: "unknown"
|
||||||
|
local_paths:
|
||||||
|
- "k_easy_resize.py"
|
||||||
|
status: "modified"
|
||||||
|
sync_state: "needs-upstream-reference"
|
||||||
|
license: "unknown"
|
||||||
|
notes: "Inspired by Resize Image V2; verify exact upstream commit/license metadata."
|
||||||
|
|
||||||
|
- id: "promptjson_integration_reference"
|
||||||
|
name: "PromptJSON interoperability reference"
|
||||||
|
source_repo: "https://github.com/NeuralSamurAI/ComfyUI-PromptJSON"
|
||||||
|
source_ref: "n/a"
|
||||||
|
local_paths: []
|
||||||
|
status: "reference_only"
|
||||||
|
sync_state: "n/a"
|
||||||
|
license: "unknown"
|
||||||
|
notes: "No vendored code; integration reference in docs/workflows."
|
||||||
|
|
||||||
|
- id: "wan22_parameter_reference"
|
||||||
|
name: "Wan 2.2 parameter docs reference"
|
||||||
|
source_repo: "https://alidocs.dingtalk.com/i/nodes/EpGBa2Lm8aZxe5myC99MelA2WgN7R35y"
|
||||||
|
source_ref: "n/a"
|
||||||
|
local_paths:
|
||||||
|
- "config.json"
|
||||||
|
- "k_easy_wan22_prompt.py"
|
||||||
|
status: "reference_only"
|
||||||
|
sync_state: "manual"
|
||||||
|
license: "unknown"
|
||||||
|
notes: "Prompt parameter taxonomy inspired by external docs; no direct vendored code tracked yet."
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# Radiance Koolook
|
||||||
|
|
||||||
|
Versioned local fork of Radiance nodes.
|
||||||
|
|
||||||
|
- Package entrypoint: `forks/radiance_koolook/__init__.py`
|
||||||
|
- Version folders: `forks/radiance_koolook/versions/<version>/`
|
||||||
|
- Each version folder keeps:
|
||||||
|
- modified node files tracked in MAIN
|
||||||
|
- `UPSTREAM_PIN.yaml` for parity with pinned external raw checkout
|
||||||
|
|
||||||
|
Current active version:
|
||||||
|
- `v1_0_1`
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
"""Radiance Koolook package entrypoint for versioned node sets."""
|
||||||
|
|
||||||
|
from .versions.v1_0_1 import NODE_CLASS_MAPPINGS as V1_0_1_NODE_CLASS_MAPPINGS
|
||||||
|
from .versions.v1_0_1 import NODE_DISPLAY_NAME_MAPPINGS as V1_0_1_NODE_DISPLAY_NAME_MAPPINGS
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {}
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {}
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS.update(V1_0_1_NODE_CLASS_MAPPINGS)
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS.update(V1_0_1_NODE_DISPLAY_NAME_MAPPINGS)
|
||||||
|
|
||||||
|
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
"""Versioned Radiance Koolook node sets."""
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
source_repo: "https://github.com/fxtdstudios/radiance.git"
|
||||||
|
source_ref: "comfyui (closest public baseline; v1.0.1 tag not found upstream)"
|
||||||
|
pinned_commit: "f1b8ae330848fa08aba24c9d3e355cb432d3515b"
|
||||||
|
external_checkout_relative_to_forks_root: "radiance-v1.0.1-koolook"
|
||||||
|
upstream_tracking_relative_to_forks_root: "radiance-main-upstream"
|
||||||
|
external_work_branch: "koolook/v1-multiframe"
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
"""
|
||||||
|
Radiance Koolook v1.0.1 mappings.
|
||||||
|
|
||||||
|
Loads locally tracked modified nodes and namespaces mapping IDs.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .nodes_hdr import (
|
||||||
|
NODE_CLASS_MAPPINGS as HDR_NODE_CLASS_MAPPINGS,
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS as HDR_NODE_DISPLAY_NAME_MAPPINGS,
|
||||||
|
)
|
||||||
|
from .nodes_color_management import (
|
||||||
|
NODE_CLASS_MAPPINGS as COLOR_NODE_CLASS_MAPPINGS,
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS as COLOR_NODE_DISPLAY_NAME_MAPPINGS,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
NAMESPACE_SUFFIX = "__koolook_v1_0_1"
|
||||||
|
DISPLAY_SUFFIX = " (Koolook v1.0.1)"
|
||||||
|
|
||||||
|
# Optional compact IDs for frequently used nodes.
|
||||||
|
# If a node is listed here, this ID is used instead of the default
|
||||||
|
# "<original_id>__koolook_v1_0_1" format.
|
||||||
|
SHORT_ID_OVERRIDES = {
|
||||||
|
"RadianceOCIOColorTransformV2": "k_easy_OCIO_v101",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _namespace_mappings(class_mappings, display_mappings):
|
||||||
|
namespaced_classes = {}
|
||||||
|
namespaced_display = {}
|
||||||
|
|
||||||
|
for node_id, node_cls in class_mappings.items():
|
||||||
|
namespaced_id = SHORT_ID_OVERRIDES.get(node_id, f"{node_id}{NAMESPACE_SUFFIX}")
|
||||||
|
namespaced_classes[namespaced_id] = node_cls
|
||||||
|
base_display = display_mappings.get(node_id, node_id)
|
||||||
|
namespaced_display[namespaced_id] = f"{base_display}{DISPLAY_SUFFIX}"
|
||||||
|
|
||||||
|
return namespaced_classes, namespaced_display
|
||||||
|
|
||||||
|
|
||||||
|
_ALL_CLASSES = {}
|
||||||
|
_ALL_DISPLAY = {}
|
||||||
|
_ALL_CLASSES.update(HDR_NODE_CLASS_MAPPINGS)
|
||||||
|
_ALL_CLASSES.update(COLOR_NODE_CLASS_MAPPINGS)
|
||||||
|
_ALL_DISPLAY.update(HDR_NODE_DISPLAY_NAME_MAPPINGS)
|
||||||
|
_ALL_DISPLAY.update(COLOR_NODE_DISPLAY_NAME_MAPPINGS)
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS = _namespace_mappings(
|
||||||
|
_ALL_CLASSES, _ALL_DISPLAY
|
||||||
|
)
|
||||||
|
|
||||||
|
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
|
||||||
@@ -0,0 +1,240 @@
|
|||||||
|
"""
|
||||||
|
FXTD Radiance - Color Management (OCIO)
|
||||||
|
Professional Color Management using OpenColorIO.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import torch
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# Try importing PyOpenColorIO
|
||||||
|
try:
|
||||||
|
import PyOpenColorIO as OCIO
|
||||||
|
except ImportError:
|
||||||
|
OCIO = None
|
||||||
|
|
||||||
|
class RadianceOCIOColorTransformV2:
|
||||||
|
@staticmethod
|
||||||
|
def _list_colorspaces(config):
|
||||||
|
names = []
|
||||||
|
try:
|
||||||
|
if hasattr(config, "getNumColorSpaces") and hasattr(config, "getColorSpaceNameByIndex"):
|
||||||
|
names = [config.getColorSpaceNameByIndex(i) for i in range(config.getNumColorSpaces())]
|
||||||
|
elif hasattr(config, "getColorSpaces"):
|
||||||
|
names = [cs.getName() if hasattr(cs, "getName") else getattr(cs, "name", "") for cs in config.getColorSpaces()]
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return [n for n in names if n]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"image": ("IMAGE",),
|
||||||
|
"config_file": ("STRING", {"default": "", "multiline": True}),
|
||||||
|
"input_space": ("STRING", {"default": "ACES - ACEScg"}),
|
||||||
|
"output_space": ("STRING", {"default": "Output - sRGB"}),
|
||||||
|
"direction": (["Forward", "Inverse"],),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("IMAGE", "STRING")
|
||||||
|
RETURN_NAMES = ("image", "debug_info")
|
||||||
|
FUNCTION = "transform"
|
||||||
|
CATEGORY = "FXTD Studios/Radiance/Color Management"
|
||||||
|
|
||||||
|
def transform(self, image, config_file, input_space, output_space, direction):
|
||||||
|
if OCIO is None:
|
||||||
|
print("PyOpenColorIO not found inside Radiance nodes. Please install it to use this node: pip install PyOpenColorIO")
|
||||||
|
return (image, "ERROR: PyOpenColorIO not installed")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Sanitize config path (trim whitespace/quotes/newlines)
|
||||||
|
config_file = (config_file or "").strip().strip('"').strip("'")
|
||||||
|
|
||||||
|
# Load config
|
||||||
|
if config_file and os.path.exists(config_file):
|
||||||
|
config = OCIO.Config.CreateFromFile(config_file)
|
||||||
|
config_source = config_file
|
||||||
|
else:
|
||||||
|
if config_file:
|
||||||
|
return (image, f"ERROR: config file not found: {config_file}")
|
||||||
|
try:
|
||||||
|
config = OCIO.Config.CreateFromEnv()
|
||||||
|
config_source = "OCIO env"
|
||||||
|
except Exception:
|
||||||
|
return (image, "ERROR: no config_file provided and OCIO env not set")
|
||||||
|
|
||||||
|
# Verify spaces exist and report available ones
|
||||||
|
available_spaces = self._list_colorspaces(config)
|
||||||
|
if input_space not in available_spaces:
|
||||||
|
return (image, f"ERROR: input colorspace '{input_space}' not found in config ({config_source}). Available: {', '.join(available_spaces[:30])}")
|
||||||
|
if output_space not in available_spaces:
|
||||||
|
return (image, f"ERROR: output colorspace '{output_space}' not found in config ({config_source}). Available: {', '.join(available_spaces[:30])}")
|
||||||
|
|
||||||
|
# Create Transform
|
||||||
|
if direction == "Forward":
|
||||||
|
transform = OCIO.ColorSpaceTransform(src=input_space, dst=output_space)
|
||||||
|
else:
|
||||||
|
transform = OCIO.ColorSpaceTransform(src=output_space, dst=input_space)
|
||||||
|
|
||||||
|
processor = config.getProcessor(transform)
|
||||||
|
cpu = processor.getDefaultCPUProcessor()
|
||||||
|
|
||||||
|
# Process Image
|
||||||
|
# Supports batch x H x W x C; preserves alpha and dtype/device
|
||||||
|
orig_dtype = image.dtype
|
||||||
|
|
||||||
|
img_np = np.ascontiguousarray(image.detach().cpu().numpy())
|
||||||
|
if img_np.ndim != 4 or img_np.shape[-1] < 3:
|
||||||
|
return (image, "WARN: input tensor is not (batch,H,W,C)")
|
||||||
|
|
||||||
|
batch, h, w, c = img_np.shape
|
||||||
|
alpha_np = None
|
||||||
|
if c > 3:
|
||||||
|
alpha_np = img_np[..., 3:].copy()
|
||||||
|
img_np = img_np[..., :3]
|
||||||
|
|
||||||
|
# Flatten all frames and process in one call
|
||||||
|
flat = img_np.reshape(-1, 3).astype(np.float32)
|
||||||
|
cpu.applyRGB(flat)
|
||||||
|
result_np = flat.reshape(batch, h, w, 3)
|
||||||
|
|
||||||
|
if alpha_np is not None:
|
||||||
|
result_np = np.concatenate([result_np, alpha_np], axis=-1)
|
||||||
|
|
||||||
|
result_t = torch.from_numpy(result_np)
|
||||||
|
if result_t.dtype != orig_dtype:
|
||||||
|
result_t = result_t.to(orig_dtype)
|
||||||
|
|
||||||
|
debug_info = f"Config: {config_source} | Batch frames processed: {batch}, shape: {list(result_t.shape)}"
|
||||||
|
return (result_t, debug_info)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Radiance] OCIO Error: {e}")
|
||||||
|
return (image, f"ERROR: {e}")
|
||||||
|
|
||||||
|
class RadianceLogCurveDecode:
|
||||||
|
"""
|
||||||
|
Logarithmic to Linear conversion for specific camera curves.
|
||||||
|
Implements exact mathematical formulas for industry standard curves.
|
||||||
|
"""
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"image": ("IMAGE",),
|
||||||
|
"curve": ([
|
||||||
|
"ARRI LogC3",
|
||||||
|
"ARRI LogC4",
|
||||||
|
"Sony S-Log3",
|
||||||
|
"RED Log3G10",
|
||||||
|
"Canon Log3",
|
||||||
|
"Panasonic V-Log"
|
||||||
|
],),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("IMAGE",)
|
||||||
|
FUNCTION = "decode"
|
||||||
|
CATEGORY = "FXTD Studios/Radiance/Color Management"
|
||||||
|
|
||||||
|
def decode(self, image, curve):
|
||||||
|
t = image.clone()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# ARRI LogC3 (Alexa Wide Gamut)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
if curve == "ARRI LogC3":
|
||||||
|
cut = 0.010591
|
||||||
|
a = 5.555556
|
||||||
|
b = 0.052272
|
||||||
|
c = 0.247190
|
||||||
|
d = 0.385537
|
||||||
|
e = 5.367655
|
||||||
|
f = 0.092809
|
||||||
|
|
||||||
|
mask = t > e * cut + f
|
||||||
|
t[mask] = (torch.pow(10.0, (t[mask] - d) / c) - b) / a
|
||||||
|
t[~mask] = (t[~mask] - f) / e
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# ARRI LogC4 (Alexa 35 REVEAL Color Science)
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
elif curve == "ARRI LogC4":
|
||||||
|
# Official ARRI LogC4 Linearization
|
||||||
|
# Linear = (2^(14.0056 * t + 0.55479) - 1.46788) / 8.68337 for t > 0.092864
|
||||||
|
# Linear = (t - 0.092864) / 5.555556 for t <= 0.092864
|
||||||
|
|
||||||
|
c4_mask = t > 0.092864
|
||||||
|
t[c4_mask] = (torch.pow(2.0, 14.0056 * t[c4_mask] + 0.55479) - 1.46788) / 8.68337
|
||||||
|
t[~c4_mask] = (t[~c4_mask] - 0.092864) / 5.555556
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Sony S-Log3
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
elif curve == "Sony S-Log3":
|
||||||
|
# S-Log3 Decoding
|
||||||
|
# x = ((y * 1023 - 95) / (4 * 261.5)) - 0.01 (Linear part)
|
||||||
|
# x = (0.19) * 10^((y * 1023 - 420) / 261.5) - 0.01 (Log part)
|
||||||
|
|
||||||
|
cut_v = 171.2102946929 / 1023.0
|
||||||
|
mask_s = t >= cut_v
|
||||||
|
|
||||||
|
t[mask_s] = torch.pow(10.0, ((t[mask_s] * 1023.0 - 420.0) / 261.5)) * 0.18 + (0.18 * 0.01 - 0.01) # Refined
|
||||||
|
# Actually standard formula: x = (0.18 + 0.01) * ... - 0.01. Note 0.19 = 0.18+0.01
|
||||||
|
|
||||||
|
t[mask_s] = (0.19) * torch.pow(10.0, ((t[mask_s] * 1023.0 - 420.0) / 261.5)) - 0.01
|
||||||
|
t[~mask_s] = ((t[~mask_s] * 1023.0 - 95.0) / (4.0 * 261.5)) - 0.01
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# RED Log3G10
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
elif curve == "RED Log3G10":
|
||||||
|
# RED Log3G10
|
||||||
|
# A = 0.224282, B = 155.975327, C = 0.01
|
||||||
|
# Linear = (10^((t - A) / 0.25) - 1) / B - C
|
||||||
|
|
||||||
|
# Note: 0.25 comes from the log encoding side (B in encoding).
|
||||||
|
# The encoding B is 0.25.
|
||||||
|
|
||||||
|
t = (torch.pow(10.0, (t - 0.224282) / 0.25) - 1.0) / 155.975327 - 0.01
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Canon Log3
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
elif curve == "Canon Log3":
|
||||||
|
# Canon Log 3 Decoding
|
||||||
|
# if t < 0.073059361:
|
||||||
|
# x = (t - 0.073059361) / 5.765765766
|
||||||
|
# else:
|
||||||
|
# x = (10^((t - 0.073059361)/0.529136) - 1) / 10.1596
|
||||||
|
|
||||||
|
mask_cl = t < 0.073059361
|
||||||
|
t[mask_cl] = (t[mask_cl] - 0.073059361) / 5.765765766
|
||||||
|
t[~mask_cl] = (torch.pow(10.0, (t[~mask_cl] - 0.073059361) / 0.529136) - 1.0) / 10.1596
|
||||||
|
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
# Panasonic V-Log
|
||||||
|
# ---------------------------------------------------------
|
||||||
|
elif curve == "Panasonic V-Log":
|
||||||
|
# Panasonic V-Log Decoding
|
||||||
|
# x = (y - 0.125) / 5.625 (y < 0.181)
|
||||||
|
# x = 10^((y - 0.598206) / 0.241514) - 0.008761 (y >= 0.181)
|
||||||
|
|
||||||
|
mask_v = t < 0.181
|
||||||
|
t[mask_v] = (t[mask_v] - 0.125) / 5.625
|
||||||
|
t[~mask_v] = torch.pow(10.0, (t[~mask_v] - 0.598206) / 0.241514) - 0.008761
|
||||||
|
|
||||||
|
return (t,)
|
||||||
|
|
||||||
|
NODE_CLASS_MAPPINGS = {
|
||||||
|
"RadianceOCIOColorTransformV2": RadianceOCIOColorTransformV2,
|
||||||
|
"RadianceLogCurveDecode": RadianceLogCurveDecode
|
||||||
|
}
|
||||||
|
|
||||||
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||||
|
"RadianceOCIOColorTransformV2": "Radiance OCIO Color Transform v2",
|
||||||
|
"RadianceLogCurveDecode": "Radiance Log Curve Decode"
|
||||||
|
}
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
|
||||||
|
import torch
|
||||||
|
import numpy as np
|
||||||
|
import json
|
||||||
|
import zlib
|
||||||
|
import struct
|
||||||
|
import time
|
||||||
|
from typing import Dict, Any, Tuple, Optional
|
||||||
|
|
||||||
|
# Get version from package
|
||||||
|
try:
|
||||||
|
from . import __version__ as FXTD_VERSION
|
||||||
|
except ImportError:
|
||||||
|
FXTD_VERSION = "3.2.0"
|
||||||
|
|
||||||
|
class FXTD_DigitalDNA:
|
||||||
|
"""
|
||||||
|
Core engine for FXTD Digital DNA (Signature Architecture).
|
||||||
|
Embeds invisible, lossless metadata into 32-bit floating point images.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Magic header to identify FXTD DNA (32 bits)
|
||||||
|
# "FXTD" in ASCII binary: 01000110 01011000 01010100 01000100
|
||||||
|
MAGIC_HEADER = "01000110010110000101010001000100"
|
||||||
|
VERSION = "1.0"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _float_to_int_bits(f_val):
|
||||||
|
"""Reinterpret float32 bits as int32."""
|
||||||
|
s = struct.pack('>f', f_val)
|
||||||
|
return struct.unpack('>I', s)[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _int_bits_to_float(i_val):
|
||||||
|
"""Reinterpret int32 bits as float32."""
|
||||||
|
s = struct.pack('>I', i_val)
|
||||||
|
return struct.unpack('>f', s)[0]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def encode(cls, image: torch.Tensor, metadata: Dict[str, Any]) -> torch.Tensor:
|
||||||
|
"""
|
||||||
|
Embed metadata into the image tensor's LSBs.
|
||||||
|
Returns a new tensor with the embedded signature.
|
||||||
|
"""
|
||||||
|
# Ensure we work with CPU numpy for bit manipulation
|
||||||
|
device = image.device
|
||||||
|
img_np = image.detach().cpu().numpy().astype(np.float32)
|
||||||
|
|
||||||
|
# Prepare payload
|
||||||
|
payload = {
|
||||||
|
"dna_ver": cls.VERSION,
|
||||||
|
"data": metadata
|
||||||
|
}
|
||||||
|
json_str = json.dumps(payload)
|
||||||
|
# Compress to save space and obfuscate
|
||||||
|
compressed = zlib.compress(json_str.encode('utf-8'))
|
||||||
|
|
||||||
|
# Convert bytes to bit string
|
||||||
|
bits = ''.join(f'{byte:08b}' for byte in compressed)
|
||||||
|
|
||||||
|
# Add length prefix (32 bits) so we know when to stop reading
|
||||||
|
length_bin = f'{len(bits):032b}'
|
||||||
|
|
||||||
|
# Full bit stream: Header + Length + Data
|
||||||
|
full_stream = cls.MAGIC_HEADER + length_bin + bits
|
||||||
|
|
||||||
|
# Check capacity
|
||||||
|
total_pixels = img_np.size
|
||||||
|
if len(full_stream) > total_pixels:
|
||||||
|
print(f"[FXTD DNA] Error: Image too small for signature ({len(full_stream)} bits > {total_pixels} pixels)")
|
||||||
|
return image
|
||||||
|
|
||||||
|
# Embed bits
|
||||||
|
# Flatten image for easy indexing
|
||||||
|
flat_img = img_np.reshape(-1)
|
||||||
|
|
||||||
|
# Create a writable copy for modification
|
||||||
|
# We manually manipulate the bits by viewing as int32
|
||||||
|
# Note: In numpy, view() shares memory, but we want to modify values via int representation
|
||||||
|
# Ideally, we do this element-wise or vectorized. Vectorized is faster.
|
||||||
|
|
||||||
|
# Vectorized approach:
|
||||||
|
# 1. Take the first N pixels where N is stream length
|
||||||
|
# 2. Convert to int representation
|
||||||
|
# 3. Set LSB to match stream
|
||||||
|
# 4. Convert back to float
|
||||||
|
|
||||||
|
stream_len = len(full_stream)
|
||||||
|
target_indices = np.arange(stream_len)
|
||||||
|
|
||||||
|
# Get float values
|
||||||
|
float_vals = flat_img[target_indices]
|
||||||
|
|
||||||
|
# Reinterpret as int32 (read-only usually if checking view)
|
||||||
|
# We use frombuffer for safe reinterpretation
|
||||||
|
int_vals = np.frombuffer(float_vals.tobytes(), dtype=np.uint32).copy()
|
||||||
|
|
||||||
|
# Prepare bit mask (clear LSB)
|
||||||
|
# 0xFFFFFFFE = ...11111110
|
||||||
|
clear_mask = np.uint32(0xFFFFFFFE)
|
||||||
|
int_vals &= clear_mask
|
||||||
|
|
||||||
|
# Create additive bits from stream
|
||||||
|
# Map '1' -> 1, '0' -> 0
|
||||||
|
stream_bits = np.array([int(b) for b in full_stream], dtype=np.uint32)
|
||||||
|
|
||||||
|
# Combine
|
||||||
|
int_vals |= stream_bits
|
||||||
|
|
||||||
|
# Convert back to float
|
||||||
|
new_floats = np.frombuffer(int_vals.tobytes(), dtype=np.float32)
|
||||||
|
|
||||||
|
# Apply back to flattened image
|
||||||
|
flat_img[target_indices] = new_floats
|
||||||
|
|
||||||
|
# Reshape back to original
|
||||||
|
result_np = flat_img.reshape(img_np.shape)
|
||||||
|
|
||||||
|
return torch.from_numpy(result_np).to(device)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def decode(cls, image: torch.Tensor) -> Tuple[bool, Optional[Dict[str, Any]]]:
|
||||||
|
"""
|
||||||
|
Attempt to read FXTD DNA from an image.
|
||||||
|
Returns (is_valid, metadata_dict).
|
||||||
|
"""
|
||||||
|
img_np = image.detach().cpu().numpy().astype(np.float32)
|
||||||
|
flat_img = img_np.reshape(-1)
|
||||||
|
|
||||||
|
# Read header bits first
|
||||||
|
header_len = len(cls.MAGIC_HEADER)
|
||||||
|
if flat_img.size < header_len:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Get raw bits from first chunk
|
||||||
|
# Reinterpret logic
|
||||||
|
check_len = header_len + 32 # Header + Length
|
||||||
|
# Safety check
|
||||||
|
if flat_img.size < check_len:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Extract bits
|
||||||
|
float_vals = flat_img[:check_len]
|
||||||
|
int_vals = np.frombuffer(float_vals.tobytes(), dtype=np.uint32)
|
||||||
|
|
||||||
|
# Get LSBs
|
||||||
|
lsbs = int_vals & 1
|
||||||
|
|
||||||
|
# Reconstruct string
|
||||||
|
extracted_bits = "".join(str(b) for b in lsbs)
|
||||||
|
|
||||||
|
# Check specific magic header
|
||||||
|
extracted_header = extracted_bits[:header_len]
|
||||||
|
if extracted_header != cls.MAGIC_HEADER:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Read length
|
||||||
|
length_bin = extracted_bits[header_len:header_len+32]
|
||||||
|
try:
|
||||||
|
payload_len = int(length_bin, 2)
|
||||||
|
except ValueError:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Check if we have enough pixels for the full payload
|
||||||
|
total_len = header_len + 32 + payload_len
|
||||||
|
if flat_img.size < total_len:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
# Now read the full payload
|
||||||
|
# Optimization: We already read the first chunk, but easier to read all needed now
|
||||||
|
float_payload = flat_img[header_len+32 : total_len]
|
||||||
|
int_payload = np.frombuffer(float_payload.tobytes(), dtype=np.uint32)
|
||||||
|
payload_lsbs = int_payload & 1
|
||||||
|
|
||||||
|
payload_bits = "".join(str(b) for b in payload_lsbs)
|
||||||
|
|
||||||
|
# Convert bits back to bytes
|
||||||
|
try:
|
||||||
|
byte_array = bytearray()
|
||||||
|
for i in range(0, len(payload_bits), 8):
|
||||||
|
byte = payload_bits[i:i+8]
|
||||||
|
if len(byte) == 8:
|
||||||
|
byte_array.append(int(byte, 2))
|
||||||
|
|
||||||
|
# Decompress
|
||||||
|
json_str = zlib.decompress(byte_array).decode('utf-8')
|
||||||
|
data = json.loads(json_str)
|
||||||
|
|
||||||
|
return True, data
|
||||||
|
except Exception as e:
|
||||||
|
# print(f"DNA Decode Error: {e}")
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
|
||||||
|
class FXTDSignatureMixin:
|
||||||
|
"""
|
||||||
|
Mixin for ComfyUI Nodes to easily sign their output.
|
||||||
|
"""
|
||||||
|
def sign_image(self, image: torch.Tensor, extra_metadata: Dict[str, Any] = None) -> torch.Tensor:
|
||||||
|
"""Sign the image with this node's signature."""
|
||||||
|
|
||||||
|
# Basic node info
|
||||||
|
node_class = self.__class__.__name__
|
||||||
|
|
||||||
|
metadata = {
|
||||||
|
"created_by": "FXTD Studio",
|
||||||
|
"node": node_class,
|
||||||
|
"timestamp": time.time(),
|
||||||
|
"fxtd_ver": FXTD_VERSION
|
||||||
|
}
|
||||||
|
|
||||||
|
if extra_metadata:
|
||||||
|
metadata.update(extra_metadata)
|
||||||
|
|
||||||
|
return FXTD_DigitalDNA.encode(image, metadata)
|
||||||
|
|
||||||
|
|
||||||
|
# --------------------------------------------------------------------------------
|
||||||
|
# NODES
|
||||||
|
# --------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class FXTDDNAReader:
|
||||||
|
"""
|
||||||
|
Node to verify and read FXTD Digital DNA from images.
|
||||||
|
"""
|
||||||
|
@classmethod
|
||||||
|
def INPUT_TYPES(cls):
|
||||||
|
return {
|
||||||
|
"required": {
|
||||||
|
"image": ("IMAGE",),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RETURN_TYPES = ("BOOLEAN", "STRING")
|
||||||
|
RETURN_NAMES = ("is_signed", "signature_data")
|
||||||
|
FUNCTION = "read_dna"
|
||||||
|
CATEGORY = "FXTD Studios/Radiance/Utilities"
|
||||||
|
DESCRIPTION = "Verifies if an image contains a valid FXTD Digital DNA signature."
|
||||||
|
|
||||||
|
def read_dna(self, image):
|
||||||
|
# Handle batch - just check the first image for now
|
||||||
|
# Ideally we check all, but metadata usually uniform across batch
|
||||||
|
if image.dim() == 4:
|
||||||
|
tgt_image = image[0]
|
||||||
|
else:
|
||||||
|
tgt_image = image
|
||||||
|
|
||||||
|
is_valid, data = FXTD_DigitalDNA.decode(tgt_image)
|
||||||
|
|
||||||
|
if is_valid:
|
||||||
|
info_str = json.dumps(data.get("data", {}), indent=2)
|
||||||
|
print(f"[FXTD DNA] Valid Signature Found: {info_str}")
|
||||||
|
else:
|
||||||
|
info_str = "No valid signature found."
|
||||||
|
|
||||||
|
return (is_valid, info_str)
|
||||||
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "koolook"
|
name = "koolook"
|
||||||
description = "This repository provides custom nodes for ComfyUI tailored for VFX and AI image/video generation workflows"
|
description = "This repository provides custom nodes for ComfyUI tailored for VFX and AI image/video generation workflows"
|
||||||
version = "1.0.1"
|
version = "0.1.0"
|
||||||
license = {file = "LICENSE"}
|
license = {file = "LICENSE"}
|
||||||
# classifiers = [
|
# classifiers = [
|
||||||
# # For OS-independent nodes (works on all operating systems)
|
# # For OS-independent nodes (works on all operating systems)
|
||||||
|
|||||||
Vendored
-19
@@ -1,19 +0,0 @@
|
|||||||
# Third-Party Code & Attribution
|
|
||||||
|
|
||||||
Use this file to track external repositories, licenses, and local modifications.
|
|
||||||
|
|
||||||
## Entry Template
|
|
||||||
|
|
||||||
- Name:
|
|
||||||
- Upstream repo URL:
|
|
||||||
- Upstream commit/tag used:
|
|
||||||
- License:
|
|
||||||
- Local path(s):
|
|
||||||
- What changed locally:
|
|
||||||
- Why changed:
|
|
||||||
- Last reviewed:
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- Keep this file updated when syncing upstream changes.
|
|
||||||
- If a node is heavily modified, document behavior differences clearly for users.
|
|
||||||
Vendored
-35
@@ -1,35 +0,0 @@
|
|||||||
version: 1
|
|
||||||
updated_at: "2026-02-14"
|
|
||||||
entries:
|
|
||||||
- id: "easy_resize_inspiration"
|
|
||||||
name: "Easy Resize (inspired component)"
|
|
||||||
source_repo: "https://github.com/kijai/ComfyUI-KJNodes"
|
|
||||||
source_ref: "unknown"
|
|
||||||
local_paths:
|
|
||||||
- "k_easy_resize.py"
|
|
||||||
status: "modified"
|
|
||||||
sync_state: "needs-upstream-reference"
|
|
||||||
license: "unknown"
|
|
||||||
notes: "Inspired by Resize Image V2; verify exact upstream commit/license metadata."
|
|
||||||
|
|
||||||
- id: "promptjson_integration_reference"
|
|
||||||
name: "PromptJSON interoperability reference"
|
|
||||||
source_repo: "https://github.com/NeuralSamurAI/ComfyUI-PromptJSON"
|
|
||||||
source_ref: "n/a"
|
|
||||||
local_paths: []
|
|
||||||
status: "reference_only"
|
|
||||||
sync_state: "n/a"
|
|
||||||
license: "unknown"
|
|
||||||
notes: "No vendored code; integration reference in docs/workflows."
|
|
||||||
|
|
||||||
- id: "wan22_parameter_reference"
|
|
||||||
name: "Wan 2.2 parameter docs reference"
|
|
||||||
source_repo: "https://alidocs.dingtalk.com/i/nodes/EpGBa2Lm8aZxe5myC99MelA2WgN7R35y"
|
|
||||||
source_ref: "n/a"
|
|
||||||
local_paths:
|
|
||||||
- "config.json"
|
|
||||||
- "k_easy_wan22_prompt.py"
|
|
||||||
status: "reference_only"
|
|
||||||
sync_state: "manual"
|
|
||||||
license: "unknown"
|
|
||||||
notes: "Prompt parameter taxonomy inspired by external docs; no direct vendored code tracked yet."
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Simple report for third-party modification tracking.
|
"""Simple report for third-party modification tracking.
|
||||||
|
|
||||||
Reads third_party/forks_manifest.yaml and prints a short summary for maintainers.
|
Reads forks/forks_manifest.yaml and prints a short summary for maintainers.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ except Exception:
|
|||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
repo_root = Path(__file__).resolve().parents[1]
|
repo_root = Path(__file__).resolve().parents[1]
|
||||||
manifest_path = repo_root / "third_party" / "forks_manifest.yaml"
|
manifest_path = repo_root / "forks" / "forks_manifest.yaml"
|
||||||
|
|
||||||
if not manifest_path.exists():
|
if not manifest_path.exists():
|
||||||
print(f"Manifest not found: {manifest_path}")
|
print(f"Manifest not found: {manifest_path}")
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
https://arxiv.org/html/2504.14899v1
|
|
||||||
|
|
||||||
https://github.com/alibaba-damo-academy/Uni3C
|
|
||||||
Reference in New Issue
Block a user