chrisgoringe / Comfy-Custom-Node-How-To

An unofficial practical guide to getting started developing custom nodes for ComfyUI
GNU General Public License v3.0
143 stars 5 forks source link

Simpler Javascript communication #21

Open geroldmeisinger opened 1 month ago

geroldmeisinger commented 1 month ago

based on https://github.com/chrisgoringe/Comfy-Custom-Node-How-To/wiki/Passing-control-to-javascript I wanted to know if there is a easier way to pass control to javascript without the hazzle of using proxies, routes and messageholder loops , if the javascript-side only runs synchronous code?

I stumbled upon: https://github.com/comfyanonymous/ComfyUI/pull/2160 which would allow to write files to Comfy and maybe skip adding additional proxy routes and message holders.

GET /userdata/{file} gets a userdata file (or 404 if it doesnt exist)
POST /userdata/{file} writes a user data file with the post data

TODO:

in python:

from uuid import uuid4
from server import PromptServer
import os
import json
import time

nodename = "MyCustomNode"

...

    def execute(self, image):
        id = uuid4().hex
        path = f".../ComfyUI/user/default/{nodename}_{id}" # UserManager.get_request_user_filepath
        #print(path)

        with open(path, 'w') as file:
            file.write('{"status":"pending"}')

        message = { "id": id }
        PromptServer.instance.send_sync(nodename, message)

        status = "pending"
        with open(path, 'r') as file:
            while status == "pending":
                file.seek(0)
                content = file.read()
                #print(content)
                data = json.loads(content)
                status = data["status"]
                if status == "pending":
                    time.sleep(0.1)
        os.unlink(path)

        if status != "done":
            raise Exception

        return (data["content"], )

in js:

import { app } from "../../scripts/app.js"
import { api } from "../../scripts/api.js"

const nodename = "MyCustomNode"
let id = ""

function on_execute(event)
{
    try
    {
        const { type, detail } = event
        console.log(JSON.stringify(event))

        id = detail.id
        api.fetchApi(`/userdata/${id}`,
        {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ status: "done", content: "Hello World" }),
        })
    }
    catch
    {
        cancel_execute("error")
    }
    finally
    {
        id = ""
    }
}

function on_execution_interrupted(event)
{
    cancel_execute("interrupted")
}

function cancel_execute(reason)
{
    if (id)
    {
        try
        {
            api.fetchApi(`/userdata/${id}`,
            {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ status: reason }),
            })
        }
        finally
        {
            id = ""
        }
    }
}

app.registerExtension(
{
    name: nodename,
    async setup()
    {
        api.addEventListener(nodename, on_execute)
        api.addEventListener("execution_interrupted", on_execution_interrupted)
    },
})
chrisgoringe commented 1 month ago

Check out the description at https://www.comfydocs.org/essentials/comms_overview

(I'm not really maintaining this repo any more, but helping develop that site)