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
|
||||
- [ ] Core workflows smoke-tested
|
||||
- [ ] `third_party/forks_manifest.yaml` reviewed/updated
|
||||
- [ ] `forks/forks_manifest.yaml` reviewed/updated
|
||||
- [ ] `CHANGELOG.md` updated
|
||||
- [ ] Release notes drafted (`docs/RELEASE_NOTES_TEMPLATE.md`)
|
||||
- [ ] Release notes drafted (PR body + `CHANGELOG.md`)
|
||||
|
||||
## Publish
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ Describe what this PR changes and why.
|
||||
- [ ] I updated docs (if needed)
|
||||
- [ ] I updated `CHANGELOG.md` (if needed)
|
||||
- [ ] 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
|
||||
|
||||
## Testing Notes
|
||||
|
||||
+13
-15
@@ -6,20 +6,18 @@ The format is inspired by Keep a Changelog and SemVer.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- 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
|
||||
## [0.1.0] - 2026-04-24
|
||||
|
||||
### 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.
|
||||
|
||||
- Release workflow: `docs/RELEASE_WORKFLOW.md`
|
||||
- Release workflow: `forks/README.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
|
||||
### 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_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_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 = {
|
||||
**version_mappings,
|
||||
**wan_mappings,
|
||||
**resize_mappings,
|
||||
**pipeline_mappings,
|
||||
**batch_mappings, # Added for new VFX batch node
|
||||
**batch_mappings,
|
||||
**cam_loader_mappings,
|
||||
**radiance_koolook_mappings,
|
||||
}
|
||||
|
||||
NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
@@ -41,9 +34,10 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
||||
**wan_display,
|
||||
**resize_display,
|
||||
**pipeline_display,
|
||||
**batch_display, # Added for new VFX batch node
|
||||
**batch_display,
|
||||
**cam_loader_display,
|
||||
**radiance_koolook_display,
|
||||
}
|
||||
|
||||
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]
|
||||
name = "koolook"
|
||||
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"}
|
||||
# classifiers = [
|
||||
# # 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
|
||||
"""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
|
||||
|
||||
@@ -17,7 +17,7 @@ except Exception:
|
||||
|
||||
def main() -> int:
|
||||
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():
|
||||
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