$ cat node-template.py

Video Add Image Inside Canvas

// Overlays a static image onto a video at a specified position and size using ffmpeg.

Process
Video
template.py
1import os2import sys3import json4import subprocess5import traceback67INPUT_DIR = "/data/input"8OUTPUT_DIR = "/data/output"910FFMPEG_IMAGE = "jrottenberg/ffmpeg:7-ubuntu"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)2122    os.makedirs(OUTPUT_DIR, exist_ok=True)2324    filter_complex = f"[1:v]scale={z}:-1[ovrl];[0:v][ovrl]overlay={x}:{y}"2526    # Use host staging paths (set by executor unconditionally)27    # so the sibling ffmpeg container mounts the same host directories.28    host_input = os.environ.get("HOST_STAGING_INPUT", INPUT_DIR)29    host_output = os.environ.get("HOST_STAGING_OUTPUT", OUTPUT_DIR)3031    image_file = inputs['image']32    image_input_path = f"/data/input/{image_file}"3334    if image_file.lower().endswith('.svg'):35        # Container 1: Convert SVG to transparent PNG via rsvg-convert36        svg_cmd = [37            "docker", "run", "--rm",38            "--network", "none",39            "--memory", "512m",40            "--cpus", "1.0",41            "-v", f"{host_input}:/data/input:ro",42            "-v", f"{host_output}:/data/output:rw",43            SVG_CONVERTER_IMAGE,44            "-w", str(z),45            "-a",46            f"/data/input/{image_file}",47            "-o", "/data/output/_overlay.png",48        ]49        result = subprocess.run(svg_cmd, capture_output=True, text=True)50        if result.returncode != 0:51            raise RuntimeError(f"rsvg-convert failed: {result.stderr}")52        image_input_path = "/data/output/_overlay.png"5354    video_path = f"/data/input/{inputs['video']}"55    shell_script = (56        "set -e\n"57        f"HAS_AUDIO=$(ffprobe -v quiet -select_streams a -show_entries stream=codec_type -of csv=p=0 '{video_path}' | head -1)\n"58        f"if [ -n \"$HAS_AUDIO\" ]; then\n"59        f"  ffmpeg -y -i '{video_path}' -i '{image_input_path}' -filter_complex '{filter_complex}' -codec:a copy /data/output/output.mp4\n"60        f"else\n"61        f"  ffmpeg -y -i '{video_path}' -i '{image_input_path}' -filter_complex '{filter_complex}' /data/output/output.mp4\n"62        f"fi\n"63    )6465    cmd = [66        "docker", "run", "--rm",67        "--network", "none",68        "--memory", "2g",69        "--cpus", "2.0",70        "-v", f"{host_input}:/data/input:ro",71        "-v", f"{host_output}:/data/output:rw",72        "--entrypoint", "sh",73        FFMPEG_IMAGE,74        "-c", shell_script,75    ]7677    result = subprocess.run(cmd, capture_output=True, text=True, timeout=1800)78    if result.returncode != 0:79        raise RuntimeError(f"ffmpeg failed (exit {result.returncode}): {result.stderr[-2000:]}")8081    print(json.dumps({"video": "output.mp4"}, indent=2))828384if __name__ == "__main__":85    try:86        main()87    except Exception as e:88        print(json.dumps({89            "error": str(e),90            "errorType": type(e).__name__,91            "traceback": traceback.format_exc(),92        }), file=sys.stderr)93        sys.exit(1)