Files
Isala Piyarisi 796bb396c0 Initial commit: ComfyUI Workflow API
HTTP API for ComfyUI workflow submission with:
- POST /api/webhook endpoint for workflow submission
- Real-time progress events via callback URL
- Automatic output collection (no re-encoding)
- Input/Output nodes for webhook data access
2026-01-17 18:30:08 +05:30

154 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""
Example client for comfy-workflow-api (Image-to-Image).
Submits an image + prompt to ComfyUI and receives the transformed image via webhook.
Usage:
python example_client.py <image_path> [prompt]
Examples:
python example_client.py input.png "make it look like a painting"
python example_client.py photo.jpg "add snow to the scene"
Requirements:
pip install requests
Make sure ComfyUI is running on http://localhost:8188
"""
import gzip
import json
import sys
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
from pathlib import Path
import requests
# Configuration
COMFYUI_URL = "http://localhost:8188"
CALLBACK_PORT = 5001
OUTPUT_DIR = Path("./outputs")
WORKFLOW_FILE = Path(__file__).parent / "example_workflow.json"
class WebhookHandler(BaseHTTPRequestHandler):
"""Handles webhook callbacks from ComfyUI."""
files_received = []
done = threading.Event()
def log_message(self, *args):
pass # Suppress logging
def do_POST(self):
content_type = self.headers.get("Content-Type", "")
content_encoding = self.headers.get("Content-Encoding", "")
length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(length)
# Decompress gzip if needed (WebhookSend uses gzip compression)
if "gzip" in content_encoding:
body = gzip.decompress(body)
if "application/json" in content_type:
data = json.loads(body)
event = data.get("event")
if event == "workflow.progress":
progress = data.get("progress", {})
print(f" Progress: {progress.get('value')}/{progress.get('max')}")
elif event == "workflow.completed":
print(f" Completed in {data.get('execution_time_ms', 'N/A')}ms")
elif event == "workflow.error":
print(f" Error: {data.get('error')}")
WebhookHandler.done.set()
elif "multipart" in content_type:
# Parse multipart and save files
boundary = content_type.split("boundary=")[1]
parts = body.split(f"--{boundary}".encode())
OUTPUT_DIR.mkdir(exist_ok=True)
for part in parts:
if b"filename=" in part and b"\r\n\r\n" in part:
header, content = part.split(b"\r\n\r\n", 1)
filename = header.decode().split('filename="')[1].split('"')[0]
content = content.rstrip(b"\r\n--")
path = OUTPUT_DIR / filename
path.write_bytes(content)
WebhookHandler.files_received.append(str(path))
print(f" Saved: {path}")
WebhookHandler.done.set()
self.send_response(200)
self.end_headers()
self.wfile.write(b"OK")
def main():
if len(sys.argv) < 2:
print("Usage: python example_client.py <image_path> [prompt]")
print("Example: python example_client.py input.png \"make it look like a painting\"")
sys.exit(1)
image_path = Path(sys.argv[1])
if not image_path.exists():
print(f"Error: Image not found: {image_path}")
sys.exit(1)
prompt = " ".join(sys.argv[2:]) if len(sys.argv) > 2 else "enhance the image"
print(f"Image: {image_path}")
print(f"Prompt: {prompt}")
print(f"Output: {OUTPUT_DIR.absolute()}\n")
# Load workflow
workflow = json.loads(WORKFLOW_FILE.read_text())
# Load image
image_data = image_path.read_bytes()
image_mime = "image/png" if image_path.suffix.lower() == ".png" else "image/jpeg"
# Start webhook server
server = HTTPServer(("localhost", CALLBACK_PORT), WebhookHandler)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
callback_url = f"http://localhost:{CALLBACK_PORT}/webhook"
# Submit workflow with image and prompt
print("Submitting workflow...")
response = requests.post(
f"{COMFYUI_URL}/api/webhook",
files={
"workflow": (None, json.dumps(workflow), "application/json"),
"callback_url": (None, callback_url),
"prompt": (None, prompt),
"image": (image_path.name, image_data, image_mime),
}
)
result = response.json()
print(f" Request ID: {result.get('request_id')}")
print(f" Prompt ID: {result.get('prompt_id')}\n")
# Wait for completion
print("Processing...")
WebhookHandler.done.wait(timeout=300)
server.shutdown()
if WebhookHandler.files_received:
print(f"\nDone! Files saved to {OUTPUT_DIR.absolute()}")
else:
print("\nNo files received.")
if __name__ == "__main__":
main()