From eb02561906c2c673f054975e7b199aa8b9bfb2cd Mon Sep 17 00:00:00 2001 From: space-nuko <24979496+space-nuko@users.noreply.github.com> Date: Mon, 5 Jun 2023 15:45:08 -0500 Subject: [PATCH] Basic preview support (as of latest PR commit) --- src/lib/api.ts | 97 ++++++++++++------- src/lib/components/ComfyApp.ts | 4 + .../components/ComfyBoxWorkflowsView.svelte | 3 + src/lib/nodes/widgets/ComfyGalleryNode.ts | 6 +- src/lib/stores/layoutStates.ts | 8 ++ src/lib/stores/queueState.ts | 37 +++++++ src/lib/stores/uiState.ts | 2 + src/lib/widgets/GalleryWidget.svelte | 77 ++++++++++++++- 8 files changed, 198 insertions(+), 36 deletions(-) diff --git a/src/lib/api.ts b/src/lib/api.ts index 492aa2b..ec54872 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -101,6 +101,7 @@ export type ComfyUIPromptExtraData = { } type ComfyAPIEvents = { + // JSON status: (status: ComfyAPIStatusResponse | null, error?: Error | null) => void, progress: (progress: Progress) => void, reconnecting: () => void, @@ -111,6 +112,9 @@ type ComfyAPIEvents = { execution_cached: (promptID: PromptID, nodes: ComfyNodeID[]) => void, execution_interrupted: (error: ComfyInterruptedError) => void, execution_error: (error: ComfyExecutionError) => void, + + // Binary + b_preview: (imageBlob: Blob) => void } export default class ComfyAPI { @@ -126,7 +130,7 @@ export default class ComfyAPI { } /** - * Poll status for colab and other things that don't support websockets. + * Poll status for colab and other things that don't support websockets. */ private pollQueue() { setInterval(async () => { @@ -176,6 +180,7 @@ export default class ComfyAPI { this.socket = new WebSocket( `ws${window.location.protocol === "https:" ? "s" : ""}://${hostname}:${port}/ws${existingSession}` ); + this.socket.binaryType = "arraybuffer"; this.socket.addEventListener("open", () => { opened = true; @@ -204,38 +209,64 @@ export default class ComfyAPI { this.socket.addEventListener("message", (event) => { try { - const msg = JSON.parse(event.data); - switch (msg.type) { - case "status": - if (msg.data.sid) { - this.clientId = msg.data.sid; - sessionStorage["Comfy.SessionId"] = this.clientId; - } - this.eventBus.emit("status", { execInfo: { queueRemaining: msg.data.status.exec_info.queue_remaining } }); - break; - case "progress": - this.eventBus.emit("progress", msg.data as Progress); - break; - case "executing": - this.eventBus.emit("executing", msg.data.prompt_id, msg.data.node); - break; - case "executed": - this.eventBus.emit("executed", msg.data.prompt_id, msg.data.node, msg.data.output); - break; - case "execution_start": - this.eventBus.emit("execution_start", msg.data.prompt_id); - break; - case "execution_cached": - this.eventBus.emit("execution_cached", msg.data.prompt_id, msg.data.nodes); - break; - case "execution_interrupted": - this.eventBus.emit("execution_interrupted", msg.data); - break; - case "execution_error": - this.eventBus.emit("execution_error", msg.data); - break; - default: - console.warn("Unhandled message:", event.data); + if (event.data instanceof ArrayBuffer) { + const view = new DataView(event.data); + const eventType = view.getUint32(0); + const buffer = event.data.slice(4); + switch (eventType) { + case 1: + const view2 = new DataView(event.data); + const imageType = view2.getUint32(0) + let imageMime: string + switch (imageType) { + case 1: + default: + imageMime = "image/jpeg"; + break; + case 2: + imageMime = "image/png" + } + const imageBlob = new Blob([buffer.slice(4)], { type: imageMime }); + this.eventBus.emit("b_preview", imageBlob); + break; + default: + throw new Error(`Unknown binary websocket message of type ${eventType}`); + } + } + else { + const msg = JSON.parse(event.data); + switch (msg.type) { + case "status": + if (msg.data.sid) { + this.clientId = msg.data.sid; + sessionStorage["Comfy.SessionId"] = this.clientId; + } + this.eventBus.emit("status", { execInfo: { queueRemaining: msg.data.status.exec_info.queue_remaining } }); + break; + case "progress": + this.eventBus.emit("progress", msg.data as Progress); + break; + case "executing": + this.eventBus.emit("executing", msg.data.prompt_id, msg.data.node); + break; + case "executed": + this.eventBus.emit("executed", msg.data.prompt_id, msg.data.node, msg.data.output); + break; + case "execution_start": + this.eventBus.emit("execution_start", msg.data.prompt_id); + break; + case "execution_cached": + this.eventBus.emit("execution_cached", msg.data.prompt_id, msg.data.nodes); + break; + case "execution_interrupted": + this.eventBus.emit("execution_interrupted", msg.data); + break; + case "execution_error": + this.eventBus.emit("execution_error", msg.data); + break; + default: + console.warn("Unhandled message:", event.data); + } } } catch (error) { console.error("Error handling message", event.data, error); diff --git a/src/lib/components/ComfyApp.ts b/src/lib/components/ComfyApp.ts index 97abc08..0f0369c 100644 --- a/src/lib/components/ComfyApp.ts +++ b/src/lib/components/ComfyApp.ts @@ -651,6 +651,10 @@ export default class ComfyApp { } }); + this.api.addEventListener("b_preview", (imageBlob: Blob) => { + queueState.previewUpdated(imageBlob); + }); + const config = get(configState); if (config.pollSystemStatsInterval > 0) { diff --git a/src/lib/components/ComfyBoxWorkflowsView.svelte b/src/lib/components/ComfyBoxWorkflowsView.svelte index 4fd5430..722a1dd 100644 --- a/src/lib/components/ComfyBoxWorkflowsView.svelte +++ b/src/lib/components/ComfyBoxWorkflowsView.svelte @@ -386,6 +386,9 @@ + + +