$ cat node-template.py
M
Motion Transfer
// Makes the character from an image perform the motion of a reference video (LTX 2.5 motion cloning). Best results when the character's pose and framing match the reference's first frame.
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 def _req_input(name, label):19 v = (inputs.get(name) or "").strip()20 if not v:21 raise ValueError(f"{label} is required")22 p = os.path.join(INPUT_DIR, v)23 if not os.path.exists(p):24 raise FileNotFoundError(f"Input file for {name!r} not found: {p}")25 return p2627 image_path = _req_input("image", "A character image")28 video_path = _req_input("video", "A reference video")2930 prompt = inputs.get("prompt", "")31 control_type = (inputs.get("control_type") or "pose").strip()32 image_strength = max(0.0, min(1.0, float(inputs.get("image_strength", 1.0) or 1.0)))33 control_strength = max(0.0, min(1.0, float(inputs.get("control_strength", 1.0) or 1.0)))3435 seed_mode = inputs.get("seed_mode", "random")36 seed_in = inputs.get("seed", -1)37 seed_input = int(seed_in) if seed_in not in (None, "") else -138 if seed_mode == "fixed" and seed_input >= 0:39 seed_value = seed_input40 else:41 seed_value = random.randint(0, 2**31 - 1)4243 os.makedirs(OUTPUT_DIR, exist_ok=True)44 print(45 f"Requesting video.motion_transfer: control={control_type}, "46 f"image_strength={image_strength}, control_strength={control_strength}, "47 f"seed={seed_value}",48 file=sys.stderr,49 )5051 result = Gais.video.motion_transfer(52 image=image_path,53 reference_video=video_path,54 prompt=prompt,55 control_type=control_type,56 image_strength=image_strength,57 control_strength=control_strength,58 seed=seed_value,59 )6061 out_filename = "motion_transfer_video.mp4"62 out_path = os.path.join(OUTPUT_DIR, out_filename)63 with open(out_path, "wb") as f:64 f.write(result.content)6566 provider = result.metadata.get("provider_name", "local")67 print(f"motion transfer generated via {provider}, seed={seed_value}", file=sys.stderr)6869 try:70 import av71 with av.open(out_path) as _c:72 _s = next(s for s in _c.streams if s.type == "video")73 _w = int(_s.codec_context.width)74 _h = int(_s.codec_context.height)75 except Exception:76 _w, _h = 0, 07778 print(json.dumps({79 "video": out_filename,80 "aspect_ratio": round(_w / _h, 4) if _h else 0.0,81 "resolution": f"{_w}x{_h}",82 }, indent=2))8384 except Exception as e:85 err = {86 "error": str(e),87 "errorType": type(e).__name__,88 }89 try:90 from gais._errors import GaisError91 is_backend_err = isinstance(e, GaisError)92 except Exception:93 is_backend_err = False94 if not is_backend_err:95 err["traceback"] = traceback.format_exc()96 print(json.dumps(err), file=sys.stderr)97 sys.exit(1)9899100if __name__ == "__main__":101 main()$ git log --oneline
v1.0.0
HEAD
2026-08-18