$ cat node-template.py

Video Upscale

// Upscales, denoises, or deblurs a video using NVIDIA RTX Video Super Resolution. Supports up to 4x upscaling, AI denoising, and deblurring using NVIDIA Tensor Cores. Outputs the processed video.

Process
Video
template.py
1import os2import sys3import json4import subprocess5import time6import traceback78try:9    import requests10except ImportError:11    subprocess.check_call([sys.executable, "-m", "pip", "install", "requests"])12    import requests1314NATIVE_VIDEO_UPSCALE_SERVICE_URL = os.getenv(15    "NATIVE_VIDEO_UPSCALE_SERVICE_URL", "http://native-video-upscale-service:8113"16)17_EMBLEMA_VERSION = os.getenv("EMBLEMA_VERSION", "dev")18NATIVE_VIDEO_UPSCALE_SERVICE_IMAGE = os.getenv(19    "NATIVE_VIDEO_UPSCALE_SERVICE_IMAGE",20    f"emblema/native-video-upscale-service:{_EMBLEMA_VERSION}",21)22CONTAINER_NAME = "native-video-upscale-service"23INPUT_DIR = "/data/input"24OUTPUT_DIR = "/data/output"2526# Quality level mapping: (mode, quality) → nvvfx QualityLevel int27QUALITY_MAP = {28    ("upscale", "low"): 1,29    ("upscale", "medium"): 2,30    ("upscale", "high"): 3,31    ("upscale", "ultra"): 4,32    ("upscale_hbr", "low"): 16,33    ("upscale_hbr", "medium"): 17,34    ("upscale_hbr", "high"): 18,35    ("upscale_hbr", "ultra"): 19,36    ("denoise", "low"): 8,37    ("denoise", "medium"): 9,38    ("denoise", "high"): 10,39    ("denoise", "ultra"): 11,40    ("deblur", "low"): 12,41    ("deblur", "medium"): 13,42    ("deblur", "high"): 14,43    ("deblur", "ultra"): 15,44}454647def start_container():48    """Create and start native-video-upscale-service, removing any stale container first."""49    subprocess.run(50        ["docker", "rm", "-f", CONTAINER_NAME],51        capture_output=True, text=True52    )5354    print(f"Creating container {CONTAINER_NAME}...", file=sys.stderr)55    run_cmd = [56        "docker", "run", "-d",57        "--name", CONTAINER_NAME,58        "--network", "emblema",59        "--gpus", "all",60        "-e", "PORT=8113",61        "-e", "DEVICE=cuda",62        NATIVE_VIDEO_UPSCALE_SERVICE_IMAGE,63    ]64    result = subprocess.run(run_cmd, capture_output=True, text=True)65    if result.returncode != 0:66        print(f"docker run failed (exit {result.returncode}): {result.stderr}", file=sys.stderr)67        raise RuntimeError(f"Failed to start container: {result.stderr}")6869    # Poll health endpoint (120s — nvidia-vfx bundles models, no download needed)70    timeout = 12071    interval = 372    elapsed = 073    health_url = f"{NATIVE_VIDEO_UPSCALE_SERVICE_URL}/health"74    while elapsed < timeout:75        try:76            r = requests.get(health_url, timeout=5)77            if r.status_code == 200:78                print(f"Container healthy (waited {elapsed}s).", file=sys.stderr)79                return80        except requests.ConnectionError:81            pass82        time.sleep(interval)83        elapsed += interval8485    raise RuntimeError(f"Container did not become healthy within {timeout}s")868788def stop_container():89    """Remove the container."""90    try:91        subprocess.run(92            ["docker", "rm", "-f", CONTAINER_NAME],93            capture_output=True, text=True, timeout=3094        )95        print(f"Container {CONTAINER_NAME} removed.", file=sys.stderr)96    except Exception as e:97        print(f"Warning: failed to remove container: {e}", file=sys.stderr)9899100def main():101    try:102        input_json = sys.stdin.read()103        execution_input = json.loads(input_json)104        inputs = execution_input.get("inputs", {})105106        video = inputs.get("video", "")107        mode = inputs.get("mode", "upscale")108        quality = inputs.get("quality", "high")109        scale_factor = inputs.get("scale_factor", 2.0)110111        if not video:112            raise ValueError("Video input is required")113114        video_path = os.path.join(INPUT_DIR, video)115        if not os.path.exists(video_path):116            raise FileNotFoundError(f"Input video not found: {video_path}")117118        # Map mode + quality to nvvfx QualityLevel int119        quality_int = QUALITY_MAP.get((mode, quality))120        if quality_int is None:121            raise ValueError(f"Invalid mode/quality combination: {mode}/{quality}")122123        # For denoise/deblur, scale_factor is irrelevant (set to 1.0)124        if mode in ("denoise", "deblur"):125            scale_factor = 1.0126127        os.makedirs(OUTPUT_DIR, exist_ok=True)128129        # Start the container130        start_container()131132        try:133            print(134                f"Processing: mode={mode}, quality={quality} (level={quality_int}), "135                f"scale_factor={scale_factor}",136                file=sys.stderr,137            )138139            # Send video to native service140            with open(video_path, "rb") as f:141                resp = requests.post(142                    f"{NATIVE_VIDEO_UPSCALE_SERVICE_URL}/upscale",143                    files={"video": (os.path.basename(video_path), f, "video/mp4")},144                    data={145                        "quality": quality_int,146                        "scale_factor": scale_factor,147                    },148                    timeout=1800,149                )150151            if resp.status_code != 200:152                try:153                    error_detail = resp.json()154                except Exception:155                    error_detail = resp.text[:500]156                raise RuntimeError(157                    f"Native video upscale service returned {resp.status_code}: {error_detail}"158                )159160            # Save output video161            base_name = os.path.splitext(os.path.basename(video))[0]162            out_filename = f"upscaled_{base_name}.mp4"163            out_path = os.path.join(OUTPUT_DIR, out_filename)164            with open(out_path, "wb") as f:165                f.write(resp.content)166167            # Log metadata168            inference_ms = resp.headers.get("X-Inference-Time-Ms", "unknown")169            resolution = resp.headers.get("X-Resolution", "unknown")170            frame_count = resp.headers.get("X-Frame-Count", "unknown")171            print(172                f"Done: resolution={resolution}, frames={frame_count}, "173                f"inference_time={inference_ms}ms",174                file=sys.stderr,175            )176177            # Flat output — keys match OUTPUT_SCHEMA178            output = {179                "video": out_filename,180            }181            print(json.dumps(output, indent=2))182183        finally:184            stop_container()185186    except Exception as e:187        error_output = {188            "error": str(e),189            "errorType": type(e).__name__,190            "traceback": traceback.format_exc(),191        }192        print(json.dumps(error_output), file=sys.stderr)193        sys.exit(1)194195196if __name__ == "__main__":197    main()