$ 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. Cloud/hybrid: VEED Fabric 1.0 (480p/720p, audio-driven duration). Local: Sonic.
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 prompt = inputs.get("prompt", "") or ""21 resolution = inputs.get("resolution", "768x512")22 seed = inputs.get("seed", 72589)2324 if not image:25 raise ValueError("Input image is required")26 if not audio:27 raise ValueError("Input audio is required")2829 image_path = os.path.join(INPUT_DIR, image)30 audio_path = os.path.join(INPUT_DIR, audio)3132 if not os.path.exists(image_path):33 raise FileNotFoundError(f"Input image not found: {image_path}")34 if not os.path.exists(audio_path):35 raise FileNotFoundError(f"Input audio not found: {audio_path}")3637 os.makedirs(OUTPUT_DIR, exist_ok=True)3839 print(40 f"Requesting speech-driven video: resolution={resolution}, "41 f"seed={seed}, prompt_len={len(prompt)}",42 file=sys.stderr,43 )4445 result = Gais.video.create_from_speech(46 image=image_path,47 audio=audio_path,48 prompt=prompt,49 resolution=resolution,50 seed=seed,51 )5253 # Save result54 out_filename = "speech_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 inference_time = result.metadata.get("inference_time_ms", "unknown")60 print(f"Video generated: time={inference_time}ms", file=sys.stderr)6162 # Probe output dimensions and emit aspect_ratio + resolution so63 # downstream nodes can chain.64 try:65 import av66 with av.open(out_path) as _c:67 _s = next(s for s in _c.streams if s.type == "video")68 _w = int(_s.codec_context.width)69 _h = int(_s.codec_context.height)70 except Exception:71 _w, _h = 0, 07273 output = {74 "video": out_filename,75 "aspect_ratio": round(_w / _h, 4) if _h else 0.0,76 "resolution": f"{_w}x{_h}",77 }78 print(json.dumps(output, indent=2))7980 except Exception as e:81 error_output = {82 "error": str(e),83 "errorType": type(e).__name__,84 "traceback": traceback.format_exc(),85 }86 print(json.dumps(error_output), file=sys.stderr)87 sys.exit(1)888990if __name__ == "__main__":91 main()$ git log --oneline
v3.12.0
HEAD
2026-08-18v3.9.02026-05-07
v3.8.02026-04-23
v3.4.02026-03-29
v3.3.02026-03-20