"""API client for daVinci-MagiHuman WebUI. Sends generation requests to the remote Router. No retry — if the router rejects (503 busy), the error is returned immediately. Configure via environment variables: ROUTER_URL Router endpoint (e.g. http://your-server:7860) ROUTER_TIMEOUT Request timeout in seconds (default 660) """ import base64 import io import os import random import uuid import requests from PIL import Image ROUTER_URL = os.environ.get("ROUTER_URL", "http://localhost:7860").rstrip("/") ROUTER_TIMEOUT = int(os.environ.get("ROUTER_TIMEOUT", "660")) def _pil_to_base64(image: Image.Image) -> str: """Encode a PIL Image to a base64 string (PNG format).""" buf = io.BytesIO() image.save(buf, format="PNG") return base64.b64encode(buf.getvalue()).decode("utf-8") def generate( image: Image.Image, video_prompt: str, seed: int = -1, output_dir: str = "./outputs", seconds: int = 5, ) -> dict: """ Send a generation request to the router and download the video. No retry. If the router returns 503 (busy), we immediately return the error. Returns: dict with keys: video_path (local), seed, error """ if seed == -1: seed = random.randint(0, 2**31 - 1) os.makedirs(output_dir, exist_ok=True) image_base64 = _pil_to_base64(image) payload = { "task": "ti2av", "prompt": video_prompt, "image_base64": image_base64, "seed": seed, "output_dir": "/tmp/magihuman_outputs", "seconds": seconds, "sr_resolution": "540p", } result = { "video_path": "", "seed": seed, "error": None, } try: resp = requests.post( f"{ROUTER_URL}/generate/file", json=payload, timeout=ROUTER_TIMEOUT, ) resp.raise_for_status() # Save mp4 bytes to local file local_path = os.path.join(output_dir, f"magihuman_{uuid.uuid4().hex[:8]}.mp4") with open(local_path, "wb") as f: f.write(resp.content) result["video_path"] = local_path except requests.HTTPError as e: detail = "" try: detail = e.response.json().get("detail", "") except Exception: detail = e.response.text[:200] if e.response else "" if e.response is not None and e.response.status_code == 503: result["error"] = "All servers are busy. Please try again later." else: result["error"] = f"HTTP {e.response.status_code}: {detail}" if e.response else str(e) except requests.ConnectionError: result["error"] = "Cannot reach the generation server. Please try again later." except requests.Timeout: result["error"] = "Request timed out. The server may be overloaded." except Exception as e: result["error"] = str(e) return result