$ cat node-template.py
P
Portrait Animation
// Generates a video by animating a portrait image driven by an audio clip. Supports configurable output resolution and reproducible seeds.
Process
Video
template.py
1import os2import sys3import json4import traceback56from gais import Gais78INPUT_DIR = "/data/input"9OUTPUT_DIR = "/data/output"101112def main():13 try:14 input_json = sys.stdin.read()15 execution_input = json.loads(input_json)16 inputs = execution_input.get("inputs", {})1718 image = inputs.get("image", "")19 audio = inputs.get("audio", "")20 resolution = inputs.get("resolution", "768x512")21 seed = inputs.get("seed", 72589)2223 if not image:24 raise ValueError("Input image is required")25 if not audio:26 raise ValueError("Input audio is required")2728 image_path = os.path.join(INPUT_DIR, image)29 audio_path = os.path.join(INPUT_DIR, audio)3031 if not os.path.exists(image_path):32 raise FileNotFoundError(f"Input image not found: {image_path}")33 if not os.path.exists(audio_path):34 raise FileNotFoundError(f"Input audio not found: {audio_path}")3536 os.makedirs(OUTPUT_DIR, exist_ok=True)3738 print(39 f"Requesting speech-driven video: resolution={resolution}, seed={seed}",40 file=sys.stderr,41 )4243 result = Gais.video.create_from_speech(44 image=image_path,45 audio=audio_path,46 resolution=resolution,47 seed=seed,48 )4950 # Save result51 out_filename = "speech_video.mp4"52 out_path = os.path.join(OUTPUT_DIR, out_filename)53 with open(out_path, "wb") as f:54 f.write(result.content)5556 inference_time = result.metadata.get("inference_time_ms", "unknown")57 print(f"Video generated: time={inference_time}ms", file=sys.stderr)5859 # Probe output dimensions and emit aspect_ratio + resolution so60 # downstream nodes can chain.61 try:62 import av63 with av.open(out_path) as _c:64 _s = next(s for s in _c.streams if s.type == "video")65 _w = int(_s.codec_context.width)66 _h = int(_s.codec_context.height)67 except Exception:68 _w, _h = 0, 06970 output = {71 "video": out_filename,72 "aspect_ratio": round(_w / _h, 4) if _h else 0.0,73 "resolution": f"{_w}x{_h}",74 }75 print(json.dumps(output, indent=2))7677 except Exception as e:78 error_output = {79 "error": str(e),80 "errorType": type(e).__name__,81 "traceback": traceback.format_exc(),82 }83 print(json.dumps(error_output), file=sys.stderr)84 sys.exit(1)858687if __name__ == "__main__":88 main()$ git log --oneline
v3.9.0
HEAD
2026-05-07v3.8.02026-04-23
v3.4.02026-03-29
v3.3.02026-03-20