Nodes/ComfyUI_zura_nodes/Zura · Select Words and Face
ComfyUI Node

Zura · Select Words and Face

Zura Select Words and Face

By ZURAVFX·Created about a month ago·Updated a day ago· 2
Zura · Select Words and Face
    • face_frames
    • reference_audio
    • padded_frames
    • fps
    • width
    • height
    • edit
    ◄source►
    ◄word_start1.145►
    ◄word_end1.565►
    ◄context_start0.000►
    ◄context_end2.267►
    ◄selection{}►
    ◄render_size512►
    ◄track_facetrue►
    ◄stabilize_cropfalse►

    What it's really doing

    You'd think the first node of a video dialogue edit just loads a file. This one does something more specific, and understanding it is the difference between "the mouth swap worked" and "why is there a floating jaw on my actor."

    It is also the node that says no a lot. Constant frame rate or nothing. Zero start timestamp or nothing. Even dimensions or nothing. A confirmed mouth placement or nothing. Every one of those guards exists because a silent bad crop produces a subtly wrong render rather than an error, and subtle wrongness is the worst kind of failure.

    The mechanism, in order

    It opens the file with OpenCV and validates the source cadence with ffprobe - packet timestamps must be uniform within 0.2 ms and the clip must start at zero. Variable frame rate sources get rejected by name.

    Then it bounds the work. context_start/context_end is your requested window; the node converts it to frames and enforces a 241-frame ceiling on the selected span. If the context is too long but the words are short it trims context symmetrically - at most 0.75 s per side - while keeping every selected frame. Selected words longer than 241 frames get refused outright ("Split this replacement into shorter selections").

    Face tracking is classical, not learned: goodFeaturesToTrack seeds up to 100 points on the upper face - masked away from the mouth, so the articulating lips don't corrupt the motion estimate - then pyramidal Lucas-Kanade optical flow tracks them both directions from your reference frame with RANSAC-fitted partial affine transforms. Fewer than four surviving points and it says tracking lost the subject. track_face off means a static crop, which is fine for a locked-off shot.

    Crops are letterboxed to the LTX aspect (fit_reference, multiples of 64) rather than stretched, so faces don't skew. The edit payload records generation at 2× the base edge - the refinement stage render_size's tooltip describes, where 512 base becomes 1024 refined. Audio comes out as 48 kHz stereo, cut to the exact frame window and zero-padded if the window runs past the end.

    Inputs and outputs

    Set these:

    • source - the picker fills it; the file must live inside ComfyUI's input/output/temp folders.
    • word_start / word_end - the original words being replaced.
    • context_start / context_end - the window around them the model sees.
    • selection - a JSON string the picker writes: face box, mouth ellipse, reference frame, mouth_confirmed.
    • render_size - 128–768, step 64. Quality path is 512 → refines to 1024, area-downsampled back into the source frame. Preview is 384 → 768.
    • track_face - on by default; turn it off for a locked shot.
    • stabilize_crop (optional) - a moving reference crop. The docs say fixed crops are the tested default; mouth tracking still follows the speaker either way.

    Outputs: face_frames and reference_audio and the numbers padded_frames, fps, width, height all go into the LTX Dub-It subgraph, in that order in the stock template. edit goes to both that subgraph and ZuraDialogueFinish - it's the memory of what you prepared.

    Install

    cd ComfyUI/custom_nodes
    git clone https://github.com/ZURAVFX/ComfyUI_zura_nodes
    cd ComfyUI_zura_nodes && pip install -r requirements.txt
    

    or Zura Nodes in ComfyUI Manager. You need ffmpeg/ffprobe on PATH (the cadence check uses ffprobe directly) and opencv, which your ComfyUI install almost certainly has. Everything else is weights, downloaded separately: LTX 2.5 distilled INT8, the video and audio VAEs, the latent upscaler, Gemma 4 encoder, the Dub-It LoRA, plus ComfyUI-LTXVideo.

    Where people get burned

    Variable frame rate. Phone footage and anything screen-recorded is usually VFR. "Convert the variable-rate clip before selecting words" means run it through ffmpeg first - this is a constant-rate editor and no amount of retrying fixes it.

    The mouth wasn't confirmed. The picker refuses to sample until you choose Draw mouth area and place the oval on the lips - the old auto-guess at 65% of the crop is detected and rejected. An older saved graph will need the mouth re-placed.

    Odd dimensions. The H.264 profile needs even width and height. Crop by one pixel rather than arguing.

    Tracking died mid-clip. Fast head turns, a hand over the face, a crop that's mostly background. Enlarge the crop, pick a clearer reference frame, or turn tracking off.

    One last thing about render_size: 384 is a preview, not a quality setting. The base edge is what the model actually paints and the refinement doubles it, so a blurry result is usually your render_size, not the model.

    CategoryZura/Dialogue

    Inputs (9)

    NameTypeDefaultDescription
    sourceSTRINGUpload a local video with the picker.
    word_startFLOAT1.145—
    word_endFLOAT1.565—
    context_startFLOAT0.000—
    context_endFLOAT2.267—
    selectionSTRING{}—
    render_sizeINT512128–768Base longest edge. The face fits automatically; refinement doubles this up to 1536. Output retains the source size.
    track_faceBOOLEANtrue—
    stabilize_cropoptBOOLEANfalseOptional moving reference crop. Fixed reference crops are the tested default; mouth tracking still follows the speaker.

    Outputs (7)

    NameTypeDescription
    face_framesIMAGE—
    reference_audioAUDIO—
    padded_framesINT—
    fpsFLOAT—
    widthINT—
    heightINT—
    editZURA_DIALOGUE_EDIT—