| import json, uuid, os, asyncio, time |
| import httpx |
| import websockets |
| from urllib.parse import urlencode |
| from datetime import datetime |
|
|
| |
| COMFY_HOST = os.getenv("COMFY_HOST", "134.199.132.159") |
|
|
| with open("workflow.json", "r", encoding="utf-8") as f: |
| WORKFLOW_TEMPLATE = json.load(f) |
|
|
| RESOLUTIONS = { |
| "360p": {"16:9": (640, 360), "9:16": (360, 640), "1:1": (512, 512), "21:9": (640, 272), "3:4": (384, 512)}, |
| "480p": {"16:9": (848, 480), "9:16": (480, 848), "1:1": (640, 640), "21:9": (848, 360), "3:4": (480, 640)}, |
| "720p": {"16:9": (1280, 720), "9:16": (720, 1280), "1:1": (768, 768), "21:9": (1280, 544), "3:4": (768, 1024)} |
| } |
|
|
| |
| |
| |
| def load_history(): |
| if os.path.exists("history.json"): |
| try: |
| with open("history.json", "r", encoding="utf-8") as f: |
| return json.load(f) |
| except: pass |
| return [] |
|
|
| def save_history(data): |
| with open("history.json", "w", encoding="utf-8") as f: |
| json.dump(data[:50], f, indent=4) |
|
|
| def init_task(task_id, prompt): |
| data = load_history() |
| |
| new_entry = { |
| "id": task_id, |
| "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
| "prompt": prompt, |
| "status": "processing", |
| "progress": 0, |
| "url": None |
| } |
| data.insert(0, new_entry) |
| save_history(data) |
|
|
| def update_task_progress(task_id, progress): |
| data = load_history() |
| for item in data: |
| if item["id"] == task_id: |
| item["progress"] = progress |
| break |
| save_history(data) |
|
|
| def complete_task(task_id, video_url=None, error=None): |
| data = load_history() |
| for item in data: |
| if item["id"] == task_id: |
| if error: |
| item["status"] = "failed" |
| item["error"] = error |
| else: |
| item["status"] = "done" |
| item["progress"] = 100 |
| item["url"] = video_url |
| break |
| save_history(data) |
|
|
| |
| |
| |
| def inject_params(req: dict) -> dict: |
| p = json.loads(json.dumps(WORKFLOW_TEMPLATE)) |
| p["89"]["inputs"]["text"] = req.get("prompt", "") |
| ratio = req.get("aspect_ratio", "16:9") |
| quality = req.get("quality", "480p") |
| width, height = RESOLUTIONS.get(quality, {}).get(ratio, (848, 480)) |
| p["74"]["inputs"]["width"] = width |
| p["74"]["inputs"]["height"] = height |
| p["74"]["inputs"]["length"] = req.get("frames", 81) |
| p["88"]["inputs"]["fps"] = req.get("fps", 16) |
| |
| |
| |
| return p |
|
|
| def extract_video_url(history: dict, token: str) -> str: |
| outputs = history.get("outputs", {}) |
| for _, node_out in outputs.items(): |
| for key in ("videos", "images", "files"): |
| if key in node_out and node_out[key]: |
| it = node_out[key][0] |
| q = urlencode(it) |
| return f"/api/video?{q}&token={token}" |
| raise RuntimeError("Video not found") |
|
|
| async def queue_prompt(req: dict): |
| token = str(uuid.uuid4()) |
| client_id = str(uuid.uuid4()) |
| prompt_data = inject_params(req) |
| |
| async with httpx.AsyncClient() as client: |
| resp = await client.post(f"http://{COMFY_HOST}/prompt?token={token}", |
| json={"prompt": prompt_data, "client_id": client_id}, |
| timeout=30.0) |
| prompt_id = resp.json().get("prompt_id") |
| if not prompt_id: raise Exception("Failed to queue on AMD Server") |
| |
| return prompt_id, client_id, token, len(prompt_data) |
|
|
| |
| async def background_watcher(task_id, client_id, token, total_nodes): |
| seen = set() |
| start_t = time.time() |
| progress_fake = 0 |
| ws_url = f"ws://{COMFY_HOST}/ws?clientId={client_id}&token={token}" |
| |
| try: |
| async with websockets.connect(ws_url, ping_interval=20) as ws: |
| while True: |
| msg_raw = await ws.recv() |
| if isinstance(msg_raw, (bytes, bytearray)): |
| if progress_fake < 95 and (time.time() - start_t) > 2: |
| progress_fake = min(95, progress_fake + 1) |
| update_task_progress(task_id, progress_fake) |
| continue |
| |
| msg = json.loads(msg_raw) |
| if msg.get("type") == "executing": |
| node = msg.get("data", {}).get("node") |
| if node is None: break |
| if node not in seen: |
| seen.add(node) |
| p_real = int((len(seen) / total_nodes) * 100) |
| progress_fake = max(progress_fake, p_real) |
| update_task_progress(task_id, progress_fake) |
| except Exception: |
| pass |
|
|
| |
| await asyncio.sleep(2) |
| try: |
| async with httpx.AsyncClient() as client: |
| h_resp = await client.get(f"http://{COMFY_HOST}/history/{task_id}?token={token}", timeout=30.0) |
| history = h_resp.json().get(task_id, {}) |
| v_url = extract_video_url(history, token) |
| complete_task(task_id, video_url=v_url) |
| except Exception as e: |
| complete_task(task_id, error=str(e)) |