Update comfy_engine.py
Browse files- comfy_engine.py +55 -59
comfy_engine.py
CHANGED
|
@@ -1,35 +1,39 @@
|
|
| 1 |
-
import json
|
| 2 |
-
import uuid
|
| 3 |
-
import os
|
| 4 |
-
import asyncio
|
| 5 |
import httpx
|
| 6 |
import websockets
|
| 7 |
from urllib.parse import urlencode
|
| 8 |
|
| 9 |
COMFY_HOST = os.getenv("COMFY_HOST", "134.199.132.159")
|
| 10 |
|
| 11 |
-
# वर्कफ़्लो लोड करें
|
| 12 |
with open("workflow.json", "r", encoding="utf-8") as f:
|
| 13 |
WORKFLOW_TEMPLATE = json.load(f)
|
| 14 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
def inject_params(req: dict) -> dict:
|
| 16 |
p = json.loads(json.dumps(WORKFLOW_TEMPLATE))
|
| 17 |
-
|
|
|
|
| 18 |
p["89"]["inputs"]["text"] = req.get("prompt", "")
|
| 19 |
-
if req.get("negative"):
|
| 20 |
-
p["72"]["inputs"]["text"] = req.get("negative")
|
| 21 |
|
| 22 |
-
#
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
p["88"]["inputs"]["fps"] = req.get("fps", 16)
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
return p
|
| 35 |
|
|
@@ -41,8 +45,7 @@ def extract_video_url(history: dict, token: str) -> str:
|
|
| 41 |
it = node_out[key][0]
|
| 42 |
if all(k in it for k in ("filename", "subfolder", "type")):
|
| 43 |
q = urlencode(it)
|
| 44 |
-
|
| 45 |
-
return f"/api/video?{q}&token={token}"
|
| 46 |
raise RuntimeError("No video file found in history")
|
| 47 |
|
| 48 |
async def generate_video_stream(req: dict):
|
|
@@ -50,62 +53,55 @@ async def generate_video_stream(req: dict):
|
|
| 50 |
client_id = str(uuid.uuid4())
|
| 51 |
prompt = inject_params(req)
|
| 52 |
|
| 53 |
-
# 1. API को प्रॉम्प्ट भेजें
|
| 54 |
async with httpx.AsyncClient() as client:
|
| 55 |
resp = await client.post(f"http://{COMFY_HOST}/prompt?token={token}", json={"prompt": prompt, "client_id": client_id}, timeout=60.0)
|
| 56 |
data = resp.json()
|
| 57 |
-
|
| 58 |
-
|
|
|
|
| 59 |
return
|
| 60 |
-
prompt_id = data["prompt_id"]
|
| 61 |
|
| 62 |
total_nodes = max(1, len(prompt))
|
| 63 |
seen = set()
|
|
|
|
|
|
|
| 64 |
|
| 65 |
-
# 2. WebSocket से लाइव प्रोग्रेस ट्रैक करें (With Ping to keep alive)
|
| 66 |
ws_url = f"ws://{COMFY_HOST}/ws?clientId={client_id}&token={token}"
|
| 67 |
try:
|
| 68 |
-
async with websockets.connect(ws_url, ping_interval=20
|
| 69 |
while True:
|
| 70 |
out = await ws.recv()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
if isinstance(out, str):
|
| 72 |
msg = json.loads(out)
|
| 73 |
if msg.get("type") == "executing":
|
| 74 |
-
|
| 75 |
-
if data.get("prompt_id") != prompt_id:
|
| 76 |
-
continue
|
| 77 |
-
node = data.get("node")
|
| 78 |
if node is None:
|
| 79 |
-
break #
|
| 80 |
if node not in seen:
|
| 81 |
seen.add(node)
|
| 82 |
-
|
| 83 |
-
|
|
|
|
| 84 |
except Exception as e:
|
| 85 |
-
|
| 86 |
-
print(f"⚠️ WebSocket Disconnected: {e}. Switching to polling mode...")
|
| 87 |
-
yield f"data: {json.dumps({'progress': 99, 'status': 'Finalizing Video...'})}\n\n"
|
| 88 |
-
|
| 89 |
-
# 3. बैकअप प्लान (Fallback Polling) - अगर WS टूट गया तो इतिहास चेक करेंगे
|
| 90 |
-
max_retries = 60 # 60 * 3 = 180 seconds (3 mins max wait)
|
| 91 |
-
for _ in range(max_retries):
|
| 92 |
-
await asyncio.sleep(3) # 3 सेकंड रुकें
|
| 93 |
-
try:
|
| 94 |
-
async with httpx.AsyncClient() as client:
|
| 95 |
-
hist_resp = await client.get(f"http://{COMFY_HOST}/history/{prompt_id}?token={token}", timeout=30.0)
|
| 96 |
-
if hist_resp.status_code == 200:
|
| 97 |
-
history_data = hist_resp.json()
|
| 98 |
-
if prompt_id in history_data:
|
| 99 |
-
history = history_data[prompt_id]
|
| 100 |
-
try:
|
| 101 |
-
final_url = extract_video_url(history, token)
|
| 102 |
-
yield f"data: {json.dumps({'progress': 100, 'status': 'done', 'url': final_url})}\n\n"
|
| 103 |
-
return # काम खतम!
|
| 104 |
-
except Exception:
|
| 105 |
-
pass # अभी वीडियो फाइल सेव हो रही है
|
| 106 |
-
except Exception as e:
|
| 107 |
-
print(f"Polling error: {e}")
|
| 108 |
-
pass # नेटवर्क ग्लिच, इग्नोर करें और फिर ट्राई करें
|
| 109 |
|
| 110 |
-
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import json, uuid, os, asyncio, time
|
|
|
|
|
|
|
|
|
|
| 2 |
import httpx
|
| 3 |
import websockets
|
| 4 |
from urllib.parse import urlencode
|
| 5 |
|
| 6 |
COMFY_HOST = os.getenv("COMFY_HOST", "134.199.132.159")
|
| 7 |
|
|
|
|
| 8 |
with open("workflow.json", "r", encoding="utf-8") as f:
|
| 9 |
WORKFLOW_TEMPLATE = json.load(f)
|
| 10 |
|
| 11 |
+
# 🧠 स्मार्ट रेसोल्यूशन डिक्शनरी (मल्टीपल्स ऑफ़ 16)
|
| 12 |
+
RESOLUTIONS = {
|
| 13 |
+
"360p": {"16:9": (640, 360), "9:16": (360, 640), "1:1": (512, 512)},
|
| 14 |
+
"480p": {"16:9": (848, 480), "9:16": (480, 848), "1:1": (640, 640)},
|
| 15 |
+
"720p": {"16:9": (1280, 720), "9:16": (720, 1280), "1:1": (768, 768)}
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
def inject_params(req: dict) -> dict:
|
| 19 |
p = json.loads(json.dumps(WORKFLOW_TEMPLATE))
|
| 20 |
+
|
| 21 |
+
# प्रॉम्प्ट
|
| 22 |
p["89"]["inputs"]["text"] = req.get("prompt", "")
|
|
|
|
|
|
|
| 23 |
|
| 24 |
+
# स्मार्ट डायमेंशन्स कैलकुलेशन
|
| 25 |
+
ratio = req.get("aspect_ratio", "16:9")
|
| 26 |
+
quality = req.get("quality", "480p")
|
| 27 |
+
width, height = RESOLUTIONS.get(quality, {}).get(ratio, (848, 480))
|
|
|
|
| 28 |
|
| 29 |
+
p["74"]["inputs"]["width"] = width
|
| 30 |
+
p["74"]["inputs"]["height"] = height
|
| 31 |
+
|
| 32 |
+
# फिक्स और हार्डकोडेड सेटिंग्स (बेस्ट क्वालिटी के लिए)
|
| 33 |
+
p["74"]["inputs"]["length"] = 81 # 81 फ्रेम्स
|
| 34 |
+
p["88"]["inputs"]["fps"] = 16 # 16 FPS (Smooth Slow-mo)
|
| 35 |
+
p["78"]["inputs"]["steps"] = 4 # 4 Steps
|
| 36 |
+
p["78"]["inputs"]["cfg"] = 5.0 # 5.0 CFG
|
| 37 |
|
| 38 |
return p
|
| 39 |
|
|
|
|
| 45 |
it = node_out[key][0]
|
| 46 |
if all(k in it for k in ("filename", "subfolder", "type")):
|
| 47 |
q = urlencode(it)
|
| 48 |
+
return f"/api/video?{q}&token={token}" # Proxy URL
|
|
|
|
| 49 |
raise RuntimeError("No video file found in history")
|
| 50 |
|
| 51 |
async def generate_video_stream(req: dict):
|
|
|
|
| 53 |
client_id = str(uuid.uuid4())
|
| 54 |
prompt = inject_params(req)
|
| 55 |
|
|
|
|
| 56 |
async with httpx.AsyncClient() as client:
|
| 57 |
resp = await client.post(f"http://{COMFY_HOST}/prompt?token={token}", json={"prompt": prompt, "client_id": client_id}, timeout=60.0)
|
| 58 |
data = resp.json()
|
| 59 |
+
prompt_id = data.get("prompt_id")
|
| 60 |
+
if not prompt_id:
|
| 61 |
+
yield f"data: {json.dumps({'error': 'Failed'})}\n\n"
|
| 62 |
return
|
|
|
|
| 63 |
|
| 64 |
total_nodes = max(1, len(prompt))
|
| 65 |
seen = set()
|
| 66 |
+
start_time = time.time()
|
| 67 |
+
p_fake = 0
|
| 68 |
|
|
|
|
| 69 |
ws_url = f"ws://{COMFY_HOST}/ws?clientId={client_id}&token={token}"
|
| 70 |
try:
|
| 71 |
+
async with websockets.connect(ws_url, ping_interval=20) as ws:
|
| 72 |
while True:
|
| 73 |
out = await ws.recv()
|
| 74 |
+
|
| 75 |
+
# 🚀 THE MAGIC HACK (Bytes Handling from your Gradio code)
|
| 76 |
+
if isinstance(out, (bytes, bytearray)):
|
| 77 |
+
if p_fake < 95 and time.time() - start_time > 2:
|
| 78 |
+
p_fake = min(95, p_fake + 1)
|
| 79 |
+
yield f"data: {json.dumps({'progress': p_fake, 'status': 'rendering'})}\n\n"
|
| 80 |
+
continue
|
| 81 |
+
|
| 82 |
+
# Normal JSON handling
|
| 83 |
if isinstance(out, str):
|
| 84 |
msg = json.loads(out)
|
| 85 |
if msg.get("type") == "executing":
|
| 86 |
+
node = msg.get("data", {}).get("node")
|
|
|
|
|
|
|
|
|
|
| 87 |
if node is None:
|
| 88 |
+
break # डन!
|
| 89 |
if node not in seen:
|
| 90 |
seen.add(node)
|
| 91 |
+
p_real = min(99, int((len(seen) / total_nodes) * 100))
|
| 92 |
+
p_fake = max(p_fake, p_real) # जो भी ज़्यादा हो वो दिखाओ
|
| 93 |
+
yield f"data: {json.dumps({'progress': p_fake, 'status': 'rendering'})}\n\n"
|
| 94 |
except Exception as e:
|
| 95 |
+
print(f"WS Disconnected: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
+
# हिस्ट्री से वीडियो निकालें
|
| 98 |
+
await asyncio.sleep(2)
|
| 99 |
+
async with httpx.AsyncClient() as client:
|
| 100 |
+
hist_resp = await client.get(f"http://{COMFY_HOST}/history/{prompt_id}?token={token}", timeout=60.0)
|
| 101 |
+
history = hist_resp.json().get(prompt_id, {})
|
| 102 |
+
|
| 103 |
+
try:
|
| 104 |
+
final_url = extract_video_url(history, token)
|
| 105 |
+
yield f"data: {json.dumps({'progress': 100, 'status': 'done', 'url': final_url})}\n\n"
|
| 106 |
+
except Exception as e:
|
| 107 |
+
yield f"data: {json.dumps({'error': str(e)})}\n\n"
|