- 调度文本
- Matched_list
Video prompting has a rhythm problem: you want "a girl walking under an umbrella" for frames 1–30, "rain starting, wind" for 31–60, and a completely different scene after that. The clean way to express that is a single text block with per-segment markers, and a node that picks the right segment for the current frame. text_filter is that node. It's the pack's per-frame prompt scheduler: give it one multiline text, a filter rule, and the current frame number, and it returns the segment that should be active at that moment - plus the full list of matches so you can see what it's choosing between.
The author's framing: "text filter preset" - and the presets are exactly the marker styles you've probably already been typing. @text@, "text", 'text', (text), {text}, text @, @text, or a custom rule. It wraps the regex so you don't have to.
What it actually does
Normalizes your text, builds a regex from the filter rule, extracts all matches, and then:
- match_all = off (default) → returns the first match... unless you use the trick below.
- match_all = on → joins all matches with newlines into one output.
- reverse_filter = on → inverts the filter, returning everything outside the markers.
The frame-scheduling magic is in the current_frame input: when match_all is off, the node cycles through the matches with frame_index % len(match_list). So a single text block like [sunny] cloudy stormy rain with rule [text] gives you a different segment per frame automatically. That's your per-frame prompt sequence with one node.
The inputs that matter
- text_input - required, multiline. Your marked-up prompt block.
- filter_rule - the marker style:
None(no filter),@text@,@text,text @,"text",'text',{text},(text), orcustom. - custom_rule - when
custom, you define the brackets:[text]extracts inside,[textextracts after,text]extracts before. - current_frame - the frame counter from your video pipeline. This is what turns it into a scheduler.
- match_all - return every match joined, or single-select.
- reverse_filter - return the non-matching text instead.
The outputs that matter
调度文本 (scheduled text) - the string for the current frame, ready to feed a CLIP encoder or text_converter. Matched_list - the full list of extracted segments, so you can debug what the node is cycling through.
Wiring it in
For video: feed it the frame number (most video workflows expose one), take 调度文本 → text_converter → sum_TextEncode → stack → sampler. For static prompts it works the same but the frame stays 0.
Installing it
cd ComfyUI/custom_nodes
git clone https://github.com/cardenluo/ComfyUI-Apt_Preset.git
cd ComfyUI-Apt_Preset
pip install -r requirements.txt # Windows: double-click install.bat
Or ComfyUI Manager → ComfyUI-Apt_Preset.
Common issues
The failure modes are all about marker discipline. If your matches come back empty, the marker characters aren't in the text - note the rule names are literal templates ("text" means double-quote-delimited, not the word "text"). And empty or fully-filtered input returns the cheerful ❌ No valid content after filtering string, which will happily get encoded as a prompt if you don't watch for it - if your video suddenly goes wonky mid-run, check whether a segment got filtered to nothing. One more: current_frame with match_all off wraps around (modulo), so frame 999 on a 3-item list silently repeats item one - fine for loops, surprising for anything else.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| text_input | STRING | — | |
| filter_rule | COMBO | None | 9 options: None, custom, @text@, @text, text @, "text", +3 |
| custom_rule | STRING | — | |
| current_frame | INT | 00–18446744073709550000 | — |
| match_allopt | BOOLEAN | false | — |
| reverse_filteropt | BOOLEAN | false | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| 调度文本 | STRING | — |
| Matched_list | LIST | — |