ComfyUI with API: Difference between revisions

From A Technological Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 10: Line 10:
<syntaxhighlight lang="python">
<syntaxhighlight lang="python">
import json
import json
from urllib import request, parse
import os
import random
import random
import argparse
import uuid
import time
from urllib import request, parse, error
from PIL import Image


# load the workflow from file, assign it to variable named prompt_workflow
# --- Argument parsing ---
promptWorkflow = json.load(open('workflow_api.json'))
parser = argparse.ArgumentParser(description="Run ComfyUI workflow via API")
parser.add_argument("--source-image", required=True, help="Absolute path to the source image")
parser.add_argument("--prompt", required=True, help="Positive text prompt for generation")
args = parser.parse_args()


# give some easy-to-remember names to the nodes
# --- Check source image path validity ---
loadImageNode = promptWorkflow["14"]
if not args.source_image.startswith("/"):
upscaleImageNode = promptWorkflow["71"]
    print("Error: --source-image must be an absolute path (it should start with '/')")
wanImageToVideoNode = promptWorkflow["10"]
    exit(1)
promptPositiveNode = promptWorkflow["13"]
KSamplerAdvancedNode = promptWorkflow["3"]


### Set workflow values from user input (parameter --source-image)
if not os.path.isfile(args.source_image):
### @TODO: check that file exists before proceeding. If not, display error message
    print(f"Error: Source image not found at '{args.source_image}'")
loadImageNode["inputs"]["image"] = @TODO: get from --source-image
    exit(1)
sourceImageWidth = @TODO: get from --source-image
sourceImageHeight = @TODO: get from --source-image


# --- Load the workflow from file ---
prompt_workflow = json.load(open("workflow.json"))


# get origin image sizes and use them for the video output
# --- Node references (adjust if your JSON IDs differ) ---
upscaleImagNode["inputs"]["width"] = sourceImageWidth
loadImageNode = prompt_workflow["14"]
upscaleImagNode["inputs"]["height"] = sourceImageHeight
upscaleImageNode = prompt_workflow["71"]
wanImageToVideoNode = prompt_workflow["10"]
promptPositiveNode = prompt_workflow["13"]
KSamplerAdvancedNode = prompt_workflow["3"]
 
# --- Set workflow values ---
loadImageNode["inputs"]["image"] = args.source_image
 
with Image.open(args.source_image) as img:
    sourceImageWidth, sourceImageHeight = img.size
 
upscaleImageNode["inputs"]["width"] = sourceImageWidth
upscaleImageNode["inputs"]["height"] = sourceImageHeight
wanImageToVideoNode["inputs"]["width"] = sourceImageWidth
wanImageToVideoNode["inputs"]["width"] = sourceImageWidth
wanImageToVideoNode["inputs"]["height"] = sourceImageHeight
wanImageToVideoNode["inputs"]["height"] = sourceImageHeight


# append the input prompt to the prompt list (parameter --prompt)
promptPositiveNode["inputs"]["text"] = args.prompt
prompt_list = []
KSamplerAdvancedNode["inputs"]["noise_seed"] = random.randint(1, 18446744073709551614)
prompt_list.append(@TODO: get from --prompt)
 
# --- Function: queue the workflow ---
def queue_prompt(prompt_workflow):
    payload = {
        "prompt": prompt_workflow,
        "client_id": str(uuid.uuid4())
    }
 
    data = json.dumps(payload).encode("utf-8")
    req = request.Request(
        "http://127.0.0.1:8188/prompt",
        data=data,
        headers={"Content-Type": "application/json"}
    )
 
    try:
        with request.urlopen(req) as response:
            res = json.loads(response.read().decode("utf-8"))
            print("Workflow queued:", res)
            return payload["client_id"]
    except error.HTTPError as e:
        print("HTTP error:", e.code, e.reason)
        print(e.read().decode())
        exit(1)
    except Exception as e:
        print("Unexpected error:", e)
        exit(1)
 
# --- Function: poll the queue every 5 seconds ---
def poll_queue_status():
    queue_url = "http://127.0.0.1:8188/queue"
    print("\nPolling queue status every 5 seconds...")
    printed_header="false"
    while True:
        try:
            with request.urlopen(queue_url) as res:
                data = json.loads(res.read().decode("utf-8"))
                queue_running = data.get("queue_running", [])
                queue_pending = data.get("queue_pending", [])
                if printed_header=="false":
                    print(f"Running: {len(queue_running)} | Pending: {len(queue_pending)} ")
                    printed_header="true"
                else:
                    print(".")
                if not queue_running and not queue_pending:
                    print("Processing complete!")
                    break
        except Exception as e:
            print("Error polling queue:", e)
 
        time.sleep(5)


# Set a random noise seed for the sampler
# --- Run ---
ksampler_node["inputs"]["noise_seed"] = random.randint(1, 18446744073709551614)
client_id = queue_prompt(prompt_workflow)
poll_queue_status()


# Queue prompt
queue_prompt(prompt_workflow)
</syntaxhighlight>
</syntaxhighlight>
[[Category:AI: Generate media locally]]
[[Category:AI: Generate media locally]]

Revision as of 15:26, 3 November 2025

ComfyUI allows to execute workflows from the command line using API workflows

Enable API workflows

You have to activate dev mode. In version 0.3.59 you have to click on comfyUI icon at top left, settings. Search for "dev mode" and activate it

Export a workflow in API version

Click on comfyUI icon at top left, File, Export (API)

Script for executing the workflow

import json
import os
import random
import argparse
import uuid
import time
from urllib import request, parse, error
from PIL import Image

# --- Argument parsing ---
parser = argparse.ArgumentParser(description="Run ComfyUI workflow via API")
parser.add_argument("--source-image", required=True, help="Absolute path to the source image")
parser.add_argument("--prompt", required=True, help="Positive text prompt for generation")
args = parser.parse_args()

# --- Check source image path validity ---
if not args.source_image.startswith("/"):
    print("Error: --source-image must be an absolute path (it should start with '/')")
    exit(1)

if not os.path.isfile(args.source_image):
    print(f"Error: Source image not found at '{args.source_image}'")
    exit(1)

# --- Load the workflow from file ---
prompt_workflow = json.load(open("workflow.json"))

# --- Node references (adjust if your JSON IDs differ) ---
loadImageNode = prompt_workflow["14"]
upscaleImageNode = prompt_workflow["71"]
wanImageToVideoNode = prompt_workflow["10"]
promptPositiveNode = prompt_workflow["13"]
KSamplerAdvancedNode = prompt_workflow["3"]

# --- Set workflow values ---
loadImageNode["inputs"]["image"] = args.source_image

with Image.open(args.source_image) as img:
    sourceImageWidth, sourceImageHeight = img.size

upscaleImageNode["inputs"]["width"] = sourceImageWidth
upscaleImageNode["inputs"]["height"] = sourceImageHeight
wanImageToVideoNode["inputs"]["width"] = sourceImageWidth
wanImageToVideoNode["inputs"]["height"] = sourceImageHeight

promptPositiveNode["inputs"]["text"] = args.prompt
KSamplerAdvancedNode["inputs"]["noise_seed"] = random.randint(1, 18446744073709551614)

# --- Function: queue the workflow ---
def queue_prompt(prompt_workflow):
    payload = {
        "prompt": prompt_workflow,
        "client_id": str(uuid.uuid4())
    }

    data = json.dumps(payload).encode("utf-8")
    req = request.Request(
        "http://127.0.0.1:8188/prompt",
        data=data,
        headers={"Content-Type": "application/json"}
    )

    try:
        with request.urlopen(req) as response:
            res = json.loads(response.read().decode("utf-8"))
            print("Workflow queued:", res)
            return payload["client_id"]
    except error.HTTPError as e:
        print("HTTP error:", e.code, e.reason)
        print(e.read().decode())
        exit(1)
    except Exception as e:
        print("Unexpected error:", e)
        exit(1)

# --- Function: poll the queue every 5 seconds ---
def poll_queue_status():
    queue_url = "http://127.0.0.1:8188/queue"
    print("\nPolling queue status every 5 seconds...")
    printed_header="false"
    while True:
        try:
            with request.urlopen(queue_url) as res:
                data = json.loads(res.read().decode("utf-8"))
                queue_running = data.get("queue_running", [])
                queue_pending = data.get("queue_pending", [])
                if printed_header=="false":
                    print(f"Running: {len(queue_running)} | Pending: {len(queue_pending)} ")
                    printed_header="true"
                else: 
                    print(".")
                if not queue_running and not queue_pending:
                    print("Processing complete!")
                    break
        except Exception as e:
            print("Error polling queue:", e)

        time.sleep(5)

# --- Run ---
client_id = queue_prompt(prompt_workflow)
poll_queue_status()