The audit compared every node against ComfyUI core and the major packs; this lands its outcomes in one branch. On-node visual editing (the old Codex-pack interaction, rebuilt): - New Load Image + Pad: image loader with whole-edge pad dragging on the node canvas, align-to-multiple rounding, and a megapixel target that rescales the source before padding so the mask seam stays binary. - Pad Image gains the same handle canvas, fed by an execution-time snapshot of its input; wireframe with diagnostic empty states before the first run. Widgets, outputs, and saved workflows unchanged. - Image Crop + Rotate + Pad's compact panel is now a live stage - crop, pad, and rotate handles on the node, one shared implementation with the full-screen editor so the two surfaces cannot drift. Feature gaps closed against the ecosystem: - Crop For Inpaint: target_megapixels, selectable rescale_algorithm (recorded in the stitcher for the paste-back), and extend_* inputs that grow the frame itself for outpainting. - Save Video: format choice - mp4 h264, mp4 h265, webm vp9 with Opus (resampled to 48 kHz); CRF drives all three. - Load Video: every_nth thinning (fps output divides to match) and a max_frames cap that ends the decode early. - LoRA Loader: saved stack templates behind a bar button, and a console warning when a LoRA's base-model family disagrees with the checkpoint. - LM Studio Chat: history in/out for multi-turn chains and a json_schema widget for structured replies. - Drop Shadow: multiply blend and a shadow_mask output. Align Image: offset outputs for un-aligning after sampling. Color Match: first_frame reference mode as a one-node video flicker fix. Removed (backed up in git history): Select Frame Range and the Video Bundle family - core ImageFromBatch and the core VIDEO wire now cover both jobs. Dead NODE_ID module variables cleaned out of every node file. 455 Python tests, 216 JS tests, validate_nodes.py green on 19 node files.
40 lines
1.2 KiB
Python
40 lines
1.2 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
import unittest
|
|
|
|
import torch
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT))
|
|
if "nodes" in sys.modules and not hasattr(sys.modules["nodes"], "__path__"):
|
|
del sys.modules["nodes"]
|
|
|
|
from nodes._batch_helpers import select_one_based_frame
|
|
|
|
|
|
def numbered_batch(count: int) -> torch.Tensor:
|
|
"""A BHWC batch where every frame is filled with its one-based number."""
|
|
batch = torch.zeros((count, 2, 2, 3), dtype=torch.float32)
|
|
for index in range(count):
|
|
batch[index] = float(index + 1)
|
|
return batch
|
|
|
|
|
|
class SelectFrameTests(unittest.TestCase):
|
|
def test_one_based_selection_returns_the_right_frame(self):
|
|
frames = numbered_batch(5)
|
|
self.assertEqual(float(select_one_based_frame(frames, 1)[0, 0, 0, 0]), 1.0)
|
|
self.assertEqual(float(select_one_based_frame(frames, 5)[0, 0, 0, 0]), 5.0)
|
|
|
|
def test_out_of_range_reports_the_available_range(self):
|
|
frames = numbered_batch(3)
|
|
for number in (0, 4):
|
|
with self.assertRaisesRegex(ValueError, "frames 1 through 3"):
|
|
select_one_based_frame(frames, number)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|