#!/usr/bin/env python3 import argparse import logging import os import socket import sys import threading import time from server import start_server from src.api.serverproxy_manager import ServerProxyManager logger = logging.getLogger() def main(): parser = argparse.ArgumentParser( description="Zordon CLI tool for preparing/submitting a render job", formatter_class=argparse.ArgumentDefaultsHelpFormatter ) # Required arguments parser.add_argument("scene_file", help="Path to the scene file (e.g., .blend, .max, .mp4)") parser.add_argument("engine", help="Desired render engine", choices=['blender', 'ffmpeg']) # Frame range parser.add_argument("--start", type=int, default=1, help="Start frame") parser.add_argument("--end", type=int, default=1, help="End frame") # Job metadata parser.add_argument("--name", default=None, help="Job name") # Output parser.add_argument("--output", default="", help="Output path/pattern (e.g., /renders/frame_####.exr)") # Target OS and Engine Version parser.add_argument( "--os", choices=["any", "windows", "linux", "macos"], default="any", help="Target operating system for render workers" ) parser.add_argument( "--engine-version", default="latest", help="Required renderer/engine version number (e.g., '4.2', '5.0')" ) # Optional flags parser.add_argument("--dry-run", action="store_true", help="Print job details without submitting") args = parser.parse_args() # Basic validation if not os.path.exists(args.scene_file): print(f"Error: Scene file '{args.scene_file}' not found!", file=sys.stderr) sys.exit(1) if args.start > args.end: print("Error: Start frame cannot be greater than end frame!", file=sys.stderr) sys.exit(1) # Calculate total frames total_frames = len(range(args.start, args.end + 1)) job_name = args.name or os.path.basename(args.scene_file) file_path = os.path.abspath(args.scene_file) # Print job summary print("Render Job Summary:") print(f" Job Name : {job_name}") print(f" Scene File : {file_path}") print(f" Engine : {args.engine}") print(f" Frames : {args.start}-{args.end} → {total_frames} frames") print(f" Output Path : {args.output or '(default from scene)'}") print(f" Target OS : {args.os}") print(f" Engine Version : {args.engine_version}") if args.dry_run: print("\nDry run complete (no submission performed).") return local_hostname = socket.gethostname() local_hostname = local_hostname + (".local" if not local_hostname.endswith(".local") else "") found_proxy = ServerProxyManager.get_proxy_for_hostname(local_hostname) is_connected = found_proxy.check_connection() if not is_connected: local_server_thread = threading.Thread(target=start_server, args=[True], daemon=True) local_server_thread.start() while not is_connected: # todo: add timeout # is_connected = found_proxy.check_connection() time.sleep(1) new_job = {"name": job_name, "renderer": args.engine} response = found_proxy.post_job_to_server(file_path, [new_job]) if response and response.ok: print(f"Uploaded to {found_proxy.hostname} successfully!") running_job_data = response.json()[0] job_id = running_job_data.get('id') print(f"Job {job_id} Summary:") print(f" Status : {running_job_data.get('status')}") print(f" Engine : {running_job_data.get('renderer')}-{running_job_data.get('renderer_version')}") print("\nWaiting for render to complete...") percent_complete = 0.0 while percent_complete < 1.0: # add checks for errors time.sleep(1) running_job_data = found_proxy.get_job_info(job_id) percent_complete = running_job_data['percent_complete'] sys.stdout.write("\x1b[1A") # Move up 1 sys.stdout.write("\x1b[0J") # Clear from cursor to end of screen (optional) print(f"Percent Complete: {percent_complete:.2%}") sys.stdout.flush() print("Finished rendering successfully!") if __name__ == "__main__": main()