diff --git a/__init__.py b/__init__.py index f00d4dd..e7a2a24 100644 --- a/__init__.py +++ b/__init__.py @@ -1286,14 +1286,16 @@ async def share_art(request): # for now, pick the first output output_to_share = potential_outputs[0] - assert output_to_share['type'] in ('image',) + assert output_to_share['type'] in ('image', 'output') output_dir = folder_paths.get_output_directory() - asset_filename = output_to_share['image']['filename'] - asset_subfolder = output_to_share['image']['subfolder'] - asset_output_type = output_to_share['image']['type'] - assert asset_output_type == "output" + if output_to_share['type'] == 'image': + asset_filename = output_to_share['image']['filename'] + asset_subfolder = output_to_share['image']['subfolder'] + else: + asset_filename = output_to_share['output']['filename'] + asset_subfolder = output_to_share['output']['subfolder'] if asset_subfolder: asset_filepath = os.path.join(output_dir, asset_subfolder, asset_filename) @@ -1350,7 +1352,7 @@ async def share_art(request): workflowId = upload_workflow_json["workflowId"] # check if the user has provided Matrix credentials - if "matrix" in share_destinations: + if "matrix" in share_destinations: comfyui_share_room_id = '!LGYSoacpJPhIfBqVfb:matrix.org' filename = os.path.basename(asset_filepath) content_type = assetFileType @@ -1377,6 +1379,9 @@ async def share_art(request): matrix = MatrixHttpApi(homeserver, token=token) with open(asset_filepath, 'rb') as f: mxc_url = matrix.media_upload(f.read(), content_type, filename=filename)['content_uri'] + + workflow_json_mxc_url = matrix.media_upload(prompt['workflow'], 'application/json', filename='workflow.json')['content_uri'] + text_content = "" if title: text_content += f"{title}\n" @@ -1386,6 +1391,7 @@ async def share_art(request): text_content += f"\ncredits: {credits}\n" response = matrix.send_message(comfyui_share_room_id, text_content) response = matrix.send_content(comfyui_share_room_id, mxc_url, filename, 'm.image') + response = matrix.send_content(comfyui_share_room_id, workflow_json_mxc_url, 'workflow.json', 'm.file') except: import traceback traceback.print_exc() diff --git a/js/comfyui-manager.js b/js/comfyui-manager.js index 4e25826..857acb6 100644 --- a/js/comfyui-manager.js +++ b/js/comfyui-manager.js @@ -2,7 +2,7 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js" import { ComfyDialog, $el } from "../../scripts/ui.js"; import { ComfyWidgets } from "../../scripts/widgets.js"; -import { ShareDialog } from "./comfyui-share.js"; +import { ShareDialog, SUPPORTED_OUTPUT_NODE_TYPES, parseURLPath, getPotentialOutputsAndOutputNodes } from "./comfyui-share.js"; var update_comfyui_button = null; var fetch_updates_button = null; @@ -2059,6 +2059,7 @@ class ManagerMenuDialog extends ComfyDialog { } } + app.registerExtension({ name: "Comfy.ManagerMenu", init() { @@ -2093,39 +2094,17 @@ app.registerExtension({ } app.graphToPrompt().then(prompt => { - console.log({prompt}) + // console.log({ prompt }) return app.graph._nodes; }).then(nodes => { - console.log({nodes}); - - const potential_outputs = []; - const potential_output_nodes = []; - - // iterate over the array of nodes to find the ones that are marked as SaveImage - // TODO: Add support for AnimateDiffCombine, etc. nodes that save videos/gifs, etc. - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - if (node.type !== "SaveImage") { - continue; - } - - if (node.type === "SaveImage") { - potential_output_nodes.push(node); - - // check if node has an 'images' array property - if (node.hasOwnProperty("images") && Array.isArray(node.images)) { - // iterate over the images array and add each image to the potential_outputs array - for (let j = 0; j < node.images.length; j++) { - potential_outputs.push({ "type": "image", "image": node.images[j] }); - } - } - } - } + // console.log({ nodes }); + const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes); if (potential_outputs.length === 0) { if (potential_output_nodes.length === 0) { // todo: add support for other output node types (animatediff combine, etc.) - alert("No SaveImage node found. To share this workflow, please run a SaveImage node to your graph and re-run your prompt."); + const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", "); + alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`); } else { alert("To share this, first run a prompt. Once it's done, click 'Share'."); } @@ -2197,7 +2176,6 @@ app.registerExtension({ ctx.restore(); } } - return r; }; }, diff --git a/js/comfyui-share.js b/js/comfyui-share.js index 5407862..36670ec 100644 --- a/js/comfyui-share.js +++ b/js/comfyui-share.js @@ -2,7 +2,102 @@ import { app } from "../../scripts/app.js"; import { api } from "../../scripts/api.js" import { ComfyDialog, $el } from "../../scripts/ui.js"; -const VALID_OUTPUT_TYPES = ["SaveImage", "VHS_VideoCombine"]; +export const SUPPORTED_OUTPUT_NODE_TYPES = [ + "SaveImage", + "VHS_VideoCombine", + "ADE_AnimateDiffCombine", +] + +export function getPotentialOutputsAndOutputNodes(nodes) { + const potential_outputs = []; + const potential_output_nodes = []; + + // iterate over the array of nodes to find the ones that are marked as SaveImage + // TODO: Add support for AnimateDiffCombine, etc. nodes that save videos/gifs, etc. + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + if (!SUPPORTED_OUTPUT_NODE_TYPES.includes(node.type)) { + continue; + } + + if (node.type === "SaveImage") { + potential_output_nodes.push(node); + + // check if node has an 'images' array property + if (node.hasOwnProperty("images") && Array.isArray(node.images)) { + // iterate over the images array and add each image to the potential_outputs array + for (let j = 0; j < node.images.length; j++) { + potential_outputs.push({ "type": "image", "image": node.images[j], "title": node.title }); + } + } + } + else if (node.type === "VHS_VideoCombine") { + potential_output_nodes.push(node); + + // check if node has a 'widgets' array property, with type 'image' + if (node.hasOwnProperty("widgets") && Array.isArray(node.widgets)) { + // iterate over the widgets array and add each image to the potential_outputs array + for (let j = 0; j < node.widgets.length; j++) { + if (node.widgets[j].type === "image") { + const widgetValue = node.widgets[j].value; + const parsedURLVals = parseURLPath(widgetValue); + + // ensure that the parsedURLVals have 'filename', 'subfolder', 'type', and 'format' properties + if (parsedURLVals.hasOwnProperty("filename") && parsedURLVals.hasOwnProperty("subfolder") && parsedURLVals.hasOwnProperty("type") && parsedURLVals.hasOwnProperty("format")) { + if (parsedURLVals.type !== "output") { + // TODO + } + potential_outputs.push({ "type": "output", 'title' : node.title, "output": { "filename": parsedURLVals.filename, "subfolder": parsedURLVals.subfolder, "value": widgetValue, "format": parsedURLVals.format } }); + } + } + } + } + } + else if (node.type === "ADE_AnimateDiffCombine") { + potential_output_nodes.push(node); + + // check if node has a 'widgets' array property, with type 'image' + if (node.hasOwnProperty("widgets") && Array.isArray(node.widgets)) { + // iterate over the widgets array and add each image to the potential_outputs array + for (let j = 0; j < node.widgets.length; j++) { + if (node.widgets[j].type === "image") { + const widgetValue = node.widgets[j].value; + const parsedURLVals = parseURLPath(widgetValue); + // ensure that the parsedURLVals have 'filename', 'subfolder', 'type', and 'format' properties + if (parsedURLVals.hasOwnProperty("filename") && parsedURLVals.hasOwnProperty("subfolder") && parsedURLVals.hasOwnProperty("type") && parsedURLVals.hasOwnProperty("format")) { + if (parsedURLVals.type !== "output") { + // TODO + continue; + } + potential_outputs.push({ "type": "output", 'title' : node.title, "output": { "filename": parsedURLVals.filename, "subfolder": parsedURLVals.subfolder, "type": parsedURLVals.type, "value": widgetValue, "format": parsedURLVals.format } }); + } + } + } + } + } + } + return { potential_outputs, potential_output_nodes }; +} + + +export function parseURLPath(urlPath) { + // Extract the query string from the URL path + var queryString = urlPath.split('?')[1]; + + // Use the URLSearchParams API to parse the query string + var params = new URLSearchParams(queryString); + + // Create an object to store the parsed parameters + var parsedParams = {}; + + // Iterate over each parameter and add it to the object + for (var pair of params.entries()) { + parsedParams[pair[0]] = pair[1]; + } + + // Return the object with the parsed parameters + return parsedParams; +} export class ShareDialog extends ComfyDialog { static instance = null; @@ -24,7 +119,6 @@ export class ShareDialog extends ComfyDialog { } createButtons() { - this.radio_buttons = $el("div", { id: "selectOutputImages", }, []); @@ -112,11 +206,10 @@ export class ShareDialog extends ComfyDialog { } this.share_button.onclick = async () => { - alert("Clicked"); const prompt = await app.graphToPrompt(); const nodes = app.graph._nodes; - console.log({ prompt, nodes }); + // console.log({ prompt, nodes }); const destinations = []; if (this.matrix_destination_checkbox.checked) { @@ -139,41 +232,15 @@ export class ShareDialog extends ComfyDialog { return; } - const potential_outputs = []; - const potential_output_nodes = []; - - // iterate over the array of nodes to find the ones that are marked as SaveImage - // TODO: Add support for AnimateDiffCombine, etc. nodes that save videos/gifs, etc. - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - console.log({ node }); - - if (!VALID_OUTPUT_TYPES.includes(node.type)) { - continue; - } + const { potential_outputs, potential_output_nodes } = getPotentialOutputsAndOutputNodes(nodes); - if (node.type === "SaveImage") { - potential_output_nodes.push(node); - - // check if node has an 'images' array property - if (node.hasOwnProperty("images") && Array.isArray(node.images)) { - // iterate over the images array and add each image to the potential_outputs array - for (let j = 0; j < node.images.length; j++) { - potential_outputs.push({ "type": "image", "image": node.images[j] }); - } - } - } - else if (node.type === "VHS_VideoCombine") { - potential_output_nodes.push(node); - } - } - - console.log({ potential_outputs, potential_output_nodes }) + // console.log({ potential_outputs, potential_output_nodes }) if (potential_outputs.length === 0) { if (potential_output_nodes.length === 0) { // todo: add support for other output node types (animatediff combine, etc.) - alert("No SaveImage node found. To share this workflow, please run a SaveImage node to your graph and re-run your prompt."); + const supported_nodes_string = SUPPORTED_OUTPUT_NODE_TYPES.join(", "); + alert(`No supported output node found (${supported_nodes_string}). To share this workflow, please add an output node to your graph and re-run your prompt.`); } else { alert("To share this, first run a prompt. Once it's done, click 'Share'."); } @@ -425,16 +492,27 @@ export class ShareDialog extends ComfyDialog { } show({ potential_outputs, potential_output_nodes }) { + // console.log({ potential_outputs, potential_output_nodes }) this.radio_buttons.innerHTML = ""; // clear the radio buttons const new_radio_buttons = $el("div", { - id: "selectOutputImages-Options", + id: "selectOutput-Options", style: { 'overflow-y': 'auto', 'max-height': '400px', } - }, potential_output_nodes.map((output, index) => { + }, potential_outputs.map((output, index) => { const radio_button = $el("input", { type: 'radio', name: "selectOutputImages", value: index, required: index === 0 }, []) - const radio_button_img = $el("img", { src: output.imgs[0].src, style: { width: "auto", height: "100px" } }, []); + let radio_button_img; + if (output.type === "image") { + radio_button_img = $el("img", { src: `/view?filename=${output.image.filename}&subfolder=${output.image.subfolder}&type=${output.image.type}`, style: { width: "auto", height: "100px" } }, []); + } else if (output.type === "output") { + radio_button_img = $el("img", { src: output.output.value, style: { width: "auto", height: "100px" } }, []); + } else { + // unsupported output type + // this should never happen + // TODO + radio_button_img = $el("img", { src: "", style: { width: "auto", height: "100px" } }, []); + } const radio_button_text = $el("label", { // style: { // color: 'white' @@ -455,6 +533,7 @@ export class ShareDialog extends ComfyDialog { display: "flex", 'align-items': 'center', 'justify-content': 'space-between', + 'margin-bottom': '10px', } }, [radio_button, radio_button_text, radio_button_img]); }));