$ cat node-template.py

V

Video Dub

// Re-dubs a talking video: generates new speech from the prompt in the source speaker's voice, with lips re-rendered to match (LTX 2.5). Duration follows the source video.

Process
Video
template.py
1import os2import sys3import json4import traceback5import random67from gais import Gais89INPUT_DIR = "/data/input"10OUTPUT_DIR = "/data/output"111213def main():14    try:15        execution_input = json.loads(sys.stdin.read())16        inputs = execution_input.get("inputs", {})1718        prompt = inputs.get("prompt", "")19        if not prompt:20            raise ValueError("New dialogue text is required")2122        video_name = inputs.get("video", "")23        if not video_name:24            raise ValueError("A video is required")25        video_path = os.path.join(INPUT_DIR, video_name)26        if not os.path.exists(video_path):27            raise FileNotFoundError(f"Input video not found: {video_path}")2829        reference_strength = float(inputs.get("reference_strength", 1.0) or 1.0)30        reference_strength = max(0.0, min(1.0, reference_strength))3132        seed_mode = inputs.get("seed_mode", "random")33        seed_in = inputs.get("seed", -1)34        seed_input = int(seed_in) if seed_in not in (None, "") else -135        if seed_mode == "fixed" and seed_input >= 0:36            seed_value = seed_input37        else:38            seed_value = random.randint(0, 2**31 - 1)3940        os.makedirs(OUTPUT_DIR, exist_ok=True)41        print(42            f"Requesting video.dub: reference_strength={reference_strength}, "43            f"seed={seed_value}",44            file=sys.stderr,45        )4647        result = Gais.video.dub(48            video=video_path,49            prompt=prompt,50            reference_strength=reference_strength,51            seed=seed_value,52        )5354        out_filename = "dubbed_video.mp4"55        out_path = os.path.join(OUTPUT_DIR, out_filename)56        with open(out_path, "wb") as f:57            f.write(result.content)5859        provider = result.metadata.get("provider_name", "local")60        print(f"dub generated via {provider}, seed={seed_value}", file=sys.stderr)6162        try:63            import av64            with av.open(out_path) as _c:65                _s = next(s for s in _c.streams if s.type == "video")66                _w = int(_s.codec_context.width)67                _h = int(_s.codec_context.height)68        except Exception:69            _w, _h = 0, 07071        print(json.dumps({72            "video": out_filename,73            "aspect_ratio": round(_w / _h, 4) if _h else 0.0,74            "resolution": f"{_w}x{_h}",75        }, indent=2))7677    except Exception as e:78        err = {79            "error": str(e),80            "errorType": type(e).__name__,81        }82        try:83            from gais._errors import GaisError84            is_backend_err = isinstance(e, GaisError)85        except Exception:86            is_backend_err = False87        if not is_backend_err:88            err["traceback"] = traceback.format_exc()89        print(json.dumps(err), file=sys.stderr)90        sys.exit(1)919293if __name__ == "__main__":94    main()

$ git log --oneline

v1.0.0
HEAD
2026-08-18