diff --git a/comfyui_integration.py b/comfyui_integration.py index 6f710de..3e65d17 100644 --- a/comfyui_integration.py +++ b/comfyui_integration.py @@ -2,24 +2,24 @@ NeonChat ComfyUI Integration — Profilbild-Generierung für Characters """ -import json, os, time, urllib.request, urllib.error +import json, os, time, shutil from pathlib import Path +import httpx COMFYUI_URL = os.environ.get("COMFYUI_URL", "http://localhost:8188") -COMFYUI_OUTPUT_DIR = Path(os.environ.get("COMFYUI_OUTPUT", str(Path.home() / "comfy" / "output"))) +COMFYUI_OUTPUT_DIR = Path(os.environ.get("COMFYUI_OUTPUT", str(Path.home() / "comfy" / "ComfyUI" / "output"))) CHARACTERS_DIR = Path(__file__).parent / "characters" AVATARS_DIR = Path(__file__).parent / "static" / "avatars" def is_comfyui_running() -> bool: try: - req = urllib.request.Request(f"{COMFYUI_URL}/system_stats", method="GET") - with urllib.request.urlopen(req, timeout=3) as resp: - return resp.status == 200 + with httpx.Client(timeout=3) as client: + resp = client.get(f"{COMFYUI_URL}/system_stats") + return resp.status_code == 200 except: return False def build_portrait_workflow(prompt: str, negative: str = "", width: int = 512, height: int = 512) -> dict: - """Baut einen ComfyUI Workflow für ein Porträtfoto.""" return { "3": { "class_type": "KSampler", @@ -38,7 +38,7 @@ def build_portrait_workflow(prompt: str, negative: str = "", width: int = 512, h }, "4": { "class_type": "CheckpointLoaderSimple", - "inputs": {"ckpt_name": "noobaiXLNAIFP_vPred10Version.safetensors"} + "inputs": {"ckpt_name": "NoobAI-XL-v1.1.safetensors"} }, "5": { "class_type": "EmptyLatentImage", @@ -69,29 +69,20 @@ def build_portrait_workflow(prompt: str, negative: str = "", width: int = 512, h } def queue_prompt(workflow: dict) -> str: - """Schickt den Workflow an ComfyUI und gibt die Prompt-ID zurück.""" - data = json.dumps({"prompt": workflow}).encode("utf-8") - req = urllib.request.Request( - f"{COMFYUI_URL}/prompt", - data=data, - headers={"Content-Type": "application/json"}, - method="POST" - ) - with urllib.request.urlopen(req, timeout=10) as resp: - result = json.loads(resp.read()) + with httpx.Client(timeout=10) as client: + resp = client.post(f"{COMFYUI_URL}/prompt", json={"prompt": workflow}) + result = resp.json() return result.get("prompt_id", "") def get_history(prompt_id: str) -> dict: - """Holt den Verlauf eines Prompts um das fertige Bild zu finden.""" try: - req = urllib.request.Request(f"{COMFYUI_URL}/history/{prompt_id}", method="GET") - with urllib.request.urlopen(req, timeout=5) as resp: - return json.loads(resp.read()) + with httpx.Client(timeout=5) as client: + resp = client.get(f"{COMFYUI_URL}/history/{prompt_id}") + return resp.json() except: return {} -def wait_for_image(prompt_id: str, timeout: int = 120) -> str | None: - """Wartet bis das Bild fertig ist und gibt den Dateinamen zurück.""" +def wait_for_image(prompt_id: str, timeout: int = 180) -> str | None: start = time.time() while time.time() - start < timeout: history = get_history(prompt_id) @@ -101,20 +92,15 @@ def wait_for_image(prompt_id: str, timeout: int = 120) -> str | None: images = outputs["9"].get("images", []) if images: return images[0].get("filename", "") - time.sleep(2) + time.sleep(3) return None def generate_portrait(character_name: str, description: str) -> str | None: - """ - Generiert ein Profilbild für einen Character via ComfyUI. - Gibt den Pfad zur gespeicherten Datei zurück, oder None bei Fehler. - """ if not is_comfyui_running(): return None AVATARS_DIR.mkdir(parents=True, exist_ok=True) - # Build prompt from character description prompt = f"anime style, {description}, simple background, headshot" negative = "blurry, deformed, ugly, realistic" @@ -125,22 +111,19 @@ def generate_portrait(character_name: str, description: str) -> str | None: if not prompt_id: return None - filename = wait_for_image(prompt_id, timeout=120) + filename = wait_for_image(prompt_id, timeout=180) if not filename: return None - # Find the image in ComfyUI output comfy_output = COMFYUI_OUTPUT_DIR / filename if not comfy_output.exists(): return None - # Copy to avatars dir safe_name = character_name.lower().replace(" ", "_") dest = AVATARS_DIR / f"{safe_name}.png" - import shutil shutil.copy2(str(comfy_output), str(dest)) return f"/static/avatars/{safe_name}.png" except Exception as e: print(f"ComfyUI error: {e}") - return None \ No newline at end of file + return None diff --git a/static/avatars/mara.png b/static/avatars/mara.png new file mode 100644 index 0000000..3a092ae Binary files /dev/null and b/static/avatars/mara.png differ