- Appended `ProjectPlanLoader` class to `loop.py`.
- Followed codebase conventions by placing imports (`os`, `json`, `folder_paths`) locally inside the `load_plan` method.
- Registered the node's display name ("📂 Project Plan Loader") in `__init__.py`.
- Inject global state rules into DynamicSceneDirector to act as a chunk Calculator.
- Protect IncrementalVideoStitcher from accumulating frames in chunk mode.
- Anchor AudioBatchSelector file selection to global_batch_index instead of loop index.
- Remove erroneous global_batch_index reset from SequentialLoopStart on scene resets.
Centralizes flow control using global_loop_index as the single source of truth.
Removes legacy batch reset logic from loaders and passive nodes.
Strips VRAM memory clearing hacks from Trigger node, delegating entirely to dedicated defragmenter.
Updates DynamicSceneDirector and AudioBatchSelector to passive observer logic.
- Replace `direct_scene` in `DynamicSceneDirector` to implement a strict 1 cycle = 1 scene state machine.
- Read plan from cached JSON file after cycle 0 to avoid redundant LLM calls.
- Remove obsolete `global_audio_offset_frames` logic, simplifying the preproduction flow where audio sync is unnecessary.
This commit fixes a bug in `loop.py` where `safe_port` was not defined if the input port matched the system port. By declaring `safe_port = system_port` before the comparison block, `safe_port` is always assigned properly, preventing an `UnboundLocalError` when the variables are logged in the print statements.
In addition, as per the user's direct request, the file `test_import.py` has been removed.
Removed phase routing from DynamicSceneDirector, IncrementalVideoStitcher, and SaveSceneKeyframe.
Deleted Meisoft_LazyGate and removed references from __init__.py.
- Strict boolean evaluation for `is_flux_phase` applied in `IncrementalVideoStitcher` (loop.py).
- Strict boolean evaluation for `is_flux_phase` applied in `SaveSceneKeyframe` (tools.py).
- Added `Meisoft_LazyGate` node to `tools.py` for conditional branch evaluation using ComfyUI's lazy loading API (`check_lazy_status`).
- Added `AnyType` implementation to support `ANY` wire connections on the lazy gate.
- Registered the new node correctly in `__init__.py`.
- Injected DynamicSceneDirector node logic at the end of loop.py
- Registered node name in __init__.py
- Removed tests directory and test files to clean up the repository for production
This resolves an issue where the sequential batcher would remember
indexes or completion states from previous disconnected flows due to
leftover global cache when processing dynamic text lists.
A hash-based flow detection was implemented in `loop.py` to identify
fresh runs and decisively reset `global_batch_index` and
`global_is_batch_advancing` to zero, preventing array out-of-bounds
errors or missing the initial phrases of a new prompt.
- Updated SequentialAudioBatchLoader to dynamically evaluate global_has_more_batches rather than statically assigning False.
- Refactored IncrementalVideoStitcher to calculate is_absolute_final only when both the current chunk is complete and there are no more batches in queue.
- Updated return blocks in stitch method to propagate new is_absolute_final logic.
Introduced `SequentialAudioBatchLoader` to `loop.py`, which loads audio files from a directory one-by-one per cycle. It utilizes `extract_and_standardize_audio` from `video.py` to ensure proper formatting (44100Hz, stereo). If the directory is empty or invalid, it falls back to a 0.1-second silent audio tensor while maintaining 0-indexed loop synchronization. This node correctly overrides `loop` module global variables (`global_step_by_chunk`, `global_source_frame_count`, etc.) to control the iteration logic seamlessly. Also added it to `NODE_CLASS_MAPPINGS` in `__init__.py`.
Added `IS_CHANGED` class methods to calculation nodes (`AutoLoopCalculator`, `AutoLoopCalculatorTTS`, `AutoLoopCalculatorWan`, `AutoLoopCalculatorLTX`, `AutoLoopCalculatorTTSBatch`) and `LazySessionCache` to force ComfyUI to re-evaluate them continuously, resolving a false positive cache issue that prevented global Python state variables from updating correctly across batch boundaries.
Replaces `get_index` in `SequentialLoopStart` to keep `global_is_batch_advancing = True`
during the 0th cycle so that downstream nodes (like Telegram Suite) can read
its state correctly. Includes a fail-fast pre-flight check to prevent SSRF vulnerabilities.
The AutoLoopCalculatorTTSBatch node was returning its output variables
wrapped in single-item lists (e.g., `[current_chunk_text]`) instead of as
raw primitive values. This caused downstream nodes expecting raw types
(like standard string replacement or string concatenation nodes) to crash,
since Python lists do not have `.replace()` or `.join()` methods.
The brackets were removed from the `return` statement so the node now properly
outputs the exact primitive values.
The `OUTPUT_IS_LIST = (True, True, True, True, True)` assignment in `AutoLoopCalculatorTTSBatch` was preventing ComfyUI from automatically unwrapping 1-item lists, causing downstream nodes to crash with TypeErrors (expecting raw strings/integers but getting lists). By removing this attribute while keeping `INPUT_IS_LIST = True`, ComfyUI can now safely process inputs as batches while unwrapping and forwarding primitive values to the next nodes.
Restored INPUT_IS_LIST = True to prevent ComfyUI from mapping the node
in parallel when given multiple files, and added OUTPUT_IS_LIST with
five values to properly unpack outputs to the downstream nodes. Also
ensured that the return statement yields a tuple of lists.
- Added `file_names` to INPUT_TYPES and `current_file_name` to RETURN_TYPES.
- Removed `INPUT_IS_LIST = True` to avoid double nesting in ComfyUI list processing.
- Cleaned up temp files (`test_loop_syntax.py`, test `.sh` scripts).
- Verified `BatchTextFileReader` does not exist in the repository.
- Removed `INPUT_IS_LIST = True` to prevent ComfyUI from implicitly converting input values to lists and expecting lists as outputs.
- Refactored `calculate` to handle list inputs natively using `isinstance` while correctly interacting with the standard Text Reader's native Python lists.
- Changed the return signature to emit native primitive values (`str`, `int`) instead of wrapping them in lists, correctly accommodating downstream integration with TTS models (e.g. Fish Speech) that expect pure strings.
ComfyUI's `INPUT_IS_LIST = True` behavior wraps incoming data in a list. When an upstream node (like `BatchTextFileReader`) already returns a list, ComfyUI wraps it again, creating a list of lists (e.g., `[["file1", "file2"]]`).
This patch updates `AutoLoopCalculatorTTSBatch.calculate` to robustly detect and unpack this double-wrapping before extracting the target texts, preventing a downstream `AttributeError` during string manipulation.
- Added global tracking variables (`global_batch_index`, `global_has_more_batches`, `global_is_batch_advancing`) to `loop.py` to maintain batch state
- Updated `SequentialLoopStart` reset logic to prevent loop index reset on automatic batch advances
- Implemented the `AutoLoopCalculatorTTSBatch` node with `INPUT_IS_LIST = True` returning list-wrapped chunks to bypass native ComfyUI parallelization
- Updated `SequentialLoopTrigger` requeue logic to handle batch completion correctly using `reset_loop=True` with `loop_idx=0` when `has_more_batches` is truthy
The `SequentialLoopTrigger` node was mutating seeds with `0xffffffff`, which exceeded the maximum value for a signed 32-bit integer. This caused a validation error in strict nodes like `OllamaOptionsV2` during loop iterations. This commit lowers the maximum generated random seed value to `0x7fffffff` to remain within safe limits.
Replaces the single `urlopen` call with a resilient retry loop (up to 3 attempts, 10s timeout, 1s delay) when checking the ComfyUI server status. Now raises a strict `RuntimeError` if all attempts fail, halting the execution queue and surfacing the error on the UI.
Added input validations (`source_frame_count <= 0` or `target_frames_per_loop <= 0`) to `AutoLoopCalculatorLTX` (in `loop.py`), and `AutoLoopCalculator`, `AutoLoopCalculatorWan` (in `video.py`). When conditions are met, a safe default is returned to prevent math domain errors. Also included a new comprehensive unit test suite in `tests/test_loop_calculator.py`.
- Introduce `global_step_by_chunk` ghost variable initialized to `False` in `loop.py`
- Set `loop.global_step_by_chunk = True` in `AutoLoopCalculatorTTS` to indicate chunk mode
- Reset `loop.global_step_by_chunk = False` in video calculators (`AutoLoopCalculator`, `AutoLoopCalculatorWan`, `AutoLoopCalculatorLTX`) to ensure state resets
- Update `SessionImageSender.send_image` timeline management to branch logic, using `is_chunk_mode` to calculate progress differently for text chunks versus video frames
Replaced destructive `del inputs["on_true"]` and `del inputs["on_false"]` with simple state update `inputs["is_final_cycle"] = is_next_final` to rely on MasterSwitch's native lazy evaluation (`check_lazy_status`), fixing a `NodeInputError` bug during looping.
Update `SequentialLoopTrigger` in `loop.py` to correctly identify
`AutoLoopCalculatorTTS` and set `estimated_chunk` to 1 instead of falling
back to a default of 50. This prevents the trigger from incorrectly guessing
when the final loop occurs and mutating the graph prematurely, which caused
errors during sequential TTS tasks.
Merged two sequential loops over `prompt.items()` in `SequentialLoopTrigger.trigger_next` into a single pass.
The first pass now simultaneously finds the `estimated_chunk` from `AutoLoopCalculator` and performs mutations for seeds and loop indices.
`MasterSwitch` mutations are deferred to a small secondary pass after `is_next_final` is calculated using the gathered chunk size.
This change reduces the overhead of iterating over large prompt dictionaries and yielded a ~26.8% performance improvement in synthetic benchmarks.
Replaced artificial safe limit in AutoLoopCalculatorWan (video.py) and
AutoLoopCalculatorLTX (loop.py) with actual physical source frame limit.
This prevents trigger nodes from falsely requesting frames past the EOF
which resulted in "RuntimeError: No frames generated".
- Added VAESafeFramePadder to `tools.py` which clones the last frame
of an image tensor to satisfy mathematical rules (4n for WanVideo,
8n+1 for LTX) avoiding audio clipping and VAE crashes.
- Registered the new tool in `__init__.py` under NODE_DISPLAY_NAME_MAPPINGS.
- Updated `AutoLoopCalculatorWan` in `video.py` to pad and round up to a
multiple of 4.
- Updated `AutoLoopCalculatorLTX` in `loop.py` to pad and round up to an
8n+1 multiple.
Removes the zeroing of `global_loop_index` and `global_accumulated_frames` from the `SequentialLoopTrigger` node.
This architectural shift solves a race condition where the stitcher would process the final chunk based on freshly reset counters (0) instead of the actual completed value, resulting in empty frames or structural bypassing. State cleanup is now exclusively handled by the entry start node.
This commit patches the `SessionImageSender`, `AutoLoopCalculatorWan`,
and `AutoLoopCalculatorLTX` nodes to prevent infinite loops caused by
extreme truncation at the end of video sequences.
- `image.py`: Ensures `SessionImageSender` accepts the entire batch if no
faces are detected, rather than defaulting to frame 0, which previously
trapped the workflow in a 1-frame micro-cycle loop.
- `video.py`: Updates `AutoLoopCalculatorWan` to always round the final
chunk size up to the nearest multiple of 4, removing the strict
cropping logic that broke the 3D VAE rule.
- `loop.py`: Updates `AutoLoopCalculatorLTX` to always round the final
chunk size up to the nearest `8n + 1` value, removing the strict
cropping logic that broke the DiT rule.
- Created `MeisoftVRAMDefragmenter` in `vram_node.py` as a passthrough to force model unloading, Python garbage collection, and CUDA cache defragmentation.
- Registered the new node in `__init__.py`.
- Added light memory cleanup on intermediate batch loops in `loop.py`, alongside a heavy cleanup (including ctypes `malloc_trim`) at the end.
- Added `unload_detector_after_analysis` boolean flag to `VideoAnalyzerWithAudio` in `video.py` to allow releasing the BBOX detector model immediately after use.
- Implement `switch_node.py` with `MasterSwitch` handling native lazy evaluation.
- Register `MasterSwitch` in `__init__.py` using clean names.
- Intercept and apply Hot Graph Surgery in `loop.py` to strip out unneeded nodes in auto loop cycles for reduced VRAM usage.
- Patches `AutoLoopCalculator` in `video.py` to add base proportional adjustment without VAE constraints.
- Patches `AutoLoopCalculatorLTX` in `loop.py` to enforce the strict 8n + 1 frame rule for LTX models, matching its calculation mathematically correctly for proportional chunking.
- Fixes `__pycache__` to be excluded correctly.