import { getHistory, runPrompt } from "../apis/comfyApiCustom"; import { app } from "../utils/comfyapp"; export type WidgetParamConf = { nodeId: number; paramName: string; paramValue: string; } function updatePrompt(prompt_output: any, paramConfigs: WidgetParamConf[]): any { try { if (!prompt_output) { console.error("Invalid prompt_output: ", prompt_output); return prompt_output; } // Apply each parameter configuration for (const config of paramConfigs) { if (!prompt_output[config.nodeId]) { console.error(`Node with ID ${config.nodeId} not found`); continue; } if (!prompt_output[config.nodeId]["inputs"]) { console.error(`Inputs not found for node with ID ${config.nodeId}`); continue; } // Update each parameter for this node prompt_output[config.nodeId]["inputs"][config.paramName] = config.paramValue; } return prompt_output; } catch (error) { console.error("Error updating prompt:", error); return prompt_output; } } export async function queuePrompt(paramConfigs: WidgetParamConf[]): Promise { const prompt = await app.graphToPrompt() const updated_prompt = updatePrompt(prompt.output, paramConfigs) console.log("queuePrompt updated_prompt:", updated_prompt); const request_body = { prompt: updated_prompt, client_id: app.api.clientId, extra_data: { extra_pageinfo: { workflow: prompt.workflow, } } } console.debug("queuePrompt request_body.prompt:", updated_prompt); const response = await runPrompt(request_body); console.debug("queuePrompt response:", response); return response; } function createErrorImage(errorMessage: string): string { // Create a data URL for an error image with the provided error message const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // Set canvas dimensions canvas.width = 512; canvas.height = 512; if (ctx) { // Fill background ctx.fillStyle = '#f8d7da'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw border ctx.strokeStyle = '#dc3545'; ctx.lineWidth = 4; ctx.strokeRect(10, 10, canvas.width - 20, canvas.height - 20); // Draw error icon ctx.fillStyle = '#dc3545'; ctx.beginPath(); ctx.arc(canvas.width / 2, 150, 50, 0, 2 * Math.PI); ctx.fill(); // Draw exclamation mark ctx.fillStyle = 'white'; ctx.font = 'bold 80px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('!', canvas.width / 2, 150); // Draw error text ctx.fillStyle = '#721c24'; ctx.font = '20px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; // Handle multi-line text const maxWidth = canvas.width - 60; const lineHeight = 30; const words = errorMessage.split(' '); let line = ''; let y = 250; for (let i = 0; i < words.length; i++) { const testLine = line + words[i] + ' '; const metrics = ctx.measureText(testLine); const testWidth = metrics.width; if (testWidth > maxWidth && i > 0) { ctx.fillText(line, canvas.width / 2, y); line = words[i] + ' '; y += lineHeight; } else { line = testLine; } } ctx.fillText(line, canvas.width / 2, y); } return canvas.toDataURL('image/png'); } export async function getOutputImagesByPromptId(promptId: string): Promise<{[nodeId: string]: string[]}> { try { if(!promptId || promptId === "") { console.log("No prompt ID provided"); return { "1": [createErrorImage("Fail to generate prompt ID")] }; } // Get the history for the prompt const history = await getHistory(promptId); if (!history || !history[promptId]) { console.log("Not finished for prompt ID:", promptId); return {}; } const promptHistory = history[promptId]; if(promptHistory.status && promptHistory.status.status_str === "error") { console.error("Error for prompt ID:", promptId); const messages = promptHistory.status.messages; if(messages && messages.length > 0) { const lastMessage = messages[messages.length - 1]; if(lastMessage && lastMessage.length > 0) { const errorMessage = lastMessage[0]; // Return an error image with the error message const errorImage = createErrorImage(errorMessage); return { "1": [errorImage] }; // Return with default nodeId of 1 } } } const outputImages: {[nodeId: string]: string[]} = {}; // Process all outputs in the history if (promptHistory.outputs) { for (const nodeId in promptHistory.outputs) { const nodeOutput = promptHistory.outputs[nodeId]; if (nodeOutput && nodeOutput.images) { outputImages[nodeId] = []; // Process each image for this node for (const img of nodeOutput.images) { if (img && img.filename && img.type) { // Construct the image URL using the ComfyUI view endpoint // const imageUrl = `${window.location.origin}/view?filename=${encodeURIComponent(img.filename)}&type=${encodeURIComponent(img.type)}${img.subfolder ? '&subfolder=' + encodeURIComponent(img.subfolder) : ''}`; //app.graph._nodes_by_id[3].imgs[0].currentSrc const urlBase = app?.graph._nodes_by_id?.[nodeId]?.imgs?.[0]?.currentSrc?.split("?")[0]; outputImages[nodeId].push(urlBase + "?filename=" + encodeURIComponent(img.filename) + "&type=" + encodeURIComponent(img.type) + (img.subfolder ? "&subfolder=" + encodeURIComponent(img.subfolder) : "")); } } console.log("outputImages for nodeId:", nodeId, outputImages[nodeId]); } } } return outputImages; } catch (error) { console.error("Error getting output images from prompt:", error); throw error; } } // Define node interface for type safety interface ComfyNode { id: number; type: string; [key: string]: any; } export function getOnlyOneImageNode() { const nodes = Object.values(app.graph._nodes_by_id) as ComfyNode[]; const saveNodeIds = []; const previewNodeIds = []; for(const node of nodes) { if(node.type === "SaveImage") { saveNodeIds.push(node.id); } else if(node.type === "PreviewImage") { previewNodeIds.push(node.id); } } console.log("saveNodeIds:", saveNodeIds); console.log("previewNodeIds:", previewNodeIds); if(saveNodeIds.length === 0 && previewNodeIds.length === 0) { throw new Error("No SaveImage or PreviewImage node found, please add one to the graph"); } if(saveNodeIds.length === 1) { return saveNodeIds[0]; } else if(saveNodeIds.length == 0 && previewNodeIds.length === 1) { return previewNodeIds[0]; } else { // Return all node IDs as an array so the UI can handle selection return null; } } export async function getOutputImageByPromptId(promptId: string, nodeId: Number): Promise { const outputImages = await getOutputImagesByPromptId(promptId); // If no images were found, return an empty array if (Object.keys(outputImages).length === 0) { return null; } // Find the maximum nodeId // const nodeIds = Object.keys(outputImages).map(id => parseInt(id)); // const maxNodeId = Math.max(...nodeIds).toString(); if (outputImages[nodeId.toString()] && outputImages[nodeId.toString()].length > 0) { return outputImages[nodeId.toString()][0]; } return null; }