$ cat node-template.py
V
Video Image Overlay
// Overlays a static image onto a video at a specified position and size.
Process
Video
template.py
1import os2import sys3import json4import subprocess5import traceback67INPUT_DIR = "/data/input"8OUTPUT_DIR = "/data/output"910FFMPEG_IMAGE = f"emblema/ffmpeg:{os.getenv('EMBLEMA_VERSION', 'dev')}"11SVG_CONVERTER_IMAGE = "emblema/svg-converter:latest"121314def main():15 execution_input = json.loads(sys.stdin.read())16 inputs = execution_input.get("inputs", {})1718 x = inputs.get("x", 10)19 y = inputs.get("y", 10)20 z = inputs.get("z", 100)21 _opacity_in = inputs.get("opacity")22 opacity_pct = 100 if _opacity_in in (None, "") else int(_opacity_in)23 opacity = max(0.0, min(1.0, opacity_pct / 100.0))2425 os.makedirs(OUTPUT_DIR, exist_ok=True)2627 filter_complex = (28 f"[1:v]scale={z}:-1,format=rgba,colorchannelmixer=aa={opacity}[ovrl];"29 f"[0:v][ovrl]overlay={x}:{y}"30 )3132 # Use host staging paths (set by executor unconditionally)33 # so the sibling ffmpeg container mounts the same host directories.34 host_input = os.environ.get("HOST_STAGING_INPUT", INPUT_DIR)35 host_output = os.environ.get("HOST_STAGING_OUTPUT", OUTPUT_DIR)3637 image_file = inputs['image']38 image_input_path = f"/data/input/{image_file}"3940 if image_file.lower().endswith('.svg'):41 # Container 1: Convert SVG to transparent PNG via rsvg-convert42 svg_cmd = [43 "docker", "run", "--rm",44 "--network", "none",45 "--memory", "512m",46 "--cpus", "1.0",47 "-v", f"{host_input}:/data/input:ro",48 "-v", f"{host_output}:/data/output:rw",49 SVG_CONVERTER_IMAGE,50 "-w", str(z),51 "-a",52 f"/data/input/{image_file}",53 "-o", "/data/output/_overlay.png",54 ]55 result = subprocess.run(svg_cmd, capture_output=True, text=True)56 if result.returncode != 0:57 raise RuntimeError(f"rsvg-convert failed: {result.stderr}")58 image_input_path = "/data/output/_overlay.png"5960 video_path = f"/data/input/{inputs['video']}"61 shell_script = (62 "set -e\n"63 f"HAS_AUDIO=$(ffprobe -v quiet -select_streams a -show_entries stream=codec_type -of csv=p=0 '{video_path}' | head -1)\n"64 f"if [ -n \"$HAS_AUDIO\" ]; then\n"65 f" ffmpeg -y -i '{video_path}' -i '{image_input_path}' -filter_complex '{filter_complex}' -codec:a copy /data/output/output.mp4\n"66 f"else\n"67 f" ffmpeg -y -i '{video_path}' -i '{image_input_path}' -filter_complex '{filter_complex}' /data/output/output.mp4\n"68 f"fi\n"69 )7071 cmd = [72 "docker", "run", "--rm",73 "--network", "none",74 "--memory", "2g",75 "--cpus", "2.0",76 "-v", f"{host_input}:/data/input:ro",77 "-v", f"{host_output}:/data/output:rw",78 "--entrypoint", "sh",79 FFMPEG_IMAGE,80 "-c", shell_script,81 ]8283 result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800)84 if result.returncode != 0:85 raise RuntimeError(f"ffmpeg failed (exit {result.returncode}): {result.stderr[-2000:]}")8687 print(json.dumps({"video": "output.mp4"}, indent=2))888990if __name__ == "__main__":91 try:92 main()93 except Exception as e:94 print(json.dumps({95 "error": str(e),96 "errorType": type(e).__name__,97 "traceback": traceback.format_exc(),98 }), file=sys.stderr)99 sys.exit(1)$ git log --oneline
v1.2.1
HEAD
2026-08-18v1.1.22026-05-07
v1.1.12026-04-23
v1.1.02026-04-22
v1.0.02026-04-09