diff --git a/comfy-nodes/external_image.py b/comfy-nodes/external_image.py
index 1fc2258..679ebee 100644
--- a/comfy-nodes/external_image.py
+++ b/comfy-nodes/external_image.py
@@ -21,8 +21,9 @@ class ComfyUIDeployExternalImage:
),
"description": (
"STRING",
- {"multiline": True, "default": ""},
+ {"multiline": False, "default": ""},
),
+ "default_value_url": ("STRING", {"image_preview": True, "default": ""}),
}
}
@@ -33,32 +34,44 @@ class ComfyUIDeployExternalImage:
CATEGORY = "image"
- def run(self, input_id, default_value=None, display_name=None, description=None):
+ def run(self, input_id, default_value=None, display_name=None, description=None, default_value_url=None):
image = default_value
- try:
- if input_id.startswith('http'):
- import requests
- from io import BytesIO
- print("Fetching image from url: ", input_id)
- response = requests.get(input_id)
- image = Image.open(BytesIO(response.content))
- elif input_id.startswith('data:image/png;base64,') or input_id.startswith('data:image/jpeg;base64,') or input_id.startswith('data:image/jpg;base64,'):
- import base64
- from io import BytesIO
- print("Decoding base64 image")
- base64_image = input_id[input_id.find(",")+1:]
- decoded_image = base64.b64decode(base64_image)
- image = Image.open(BytesIO(decoded_image))
- else:
- raise ValueError("Invalid image url provided.")
-
- image = ImageOps.exif_transpose(image)
- image = image.convert("RGB")
- image = np.array(image).astype(np.float32) / 255.0
- image = torch.from_numpy(image)[None,]
- return [image]
- except:
- return [image]
+
+ # Try both input_id and default_value_url
+ urls_to_try = [url for url in [input_id, default_value_url] if url]
+
+ print(default_value_url)
+
+ for url in urls_to_try:
+ try:
+ if url.startswith('http'):
+ import requests
+ from io import BytesIO
+ print(f"Fetching image from url: {url}")
+ response = requests.get(url)
+ image = Image.open(BytesIO(response.content))
+ break
+ elif url.startswith(('data:image/png;base64,', 'data:image/jpeg;base64,', 'data:image/jpg;base64,')):
+ import base64
+ from io import BytesIO
+ print("Decoding base64 image")
+ base64_image = url[url.find(",")+1:]
+ decoded_image = base64.b64decode(base64_image)
+ image = Image.open(BytesIO(decoded_image))
+ break
+ except:
+ continue
+
+ if image is not None:
+ try:
+ image = ImageOps.exif_transpose(image)
+ image = image.convert("RGB")
+ image = np.array(image).astype(np.float32) / 255.0
+ image = torch.from_numpy(image)[None,]
+ except:
+ pass
+
+ return [image]
NODE_CLASS_MAPPINGS = {"ComfyUIDeployExternalImage": ComfyUIDeployExternalImage}
diff --git a/web-plugin/index.js b/web-plugin/index.js
index cd4847c..873169c 100644
--- a/web-plugin/index.js
+++ b/web-plugin/index.js
@@ -53,9 +53,9 @@ function sendEventToCD(event, data) {
function sendDirectEventToCD(event, data) {
const message = {
type: event,
- data: data
- }
- window.parent.postMessage(message, '*')
+ data: data,
+ };
+ window.parent.postMessage(message, "*");
}
function dispatchAPIEventData(data) {
@@ -494,6 +494,13 @@ const ext = {
return r;
};
+ if (
+ nodeData?.input?.optional?.default_value_url?.[1]?.image_preview === true
+ ) {
+ nodeData.input.optional.default_value_url = ["IMAGEPREVIEW"];
+ console.log(nodeData.input.optional.default_value_url);
+ }
+
// const origonNodeCreated = nodeType.prototype.onNodeCreated;
// nodeType.prototype.onNodeCreated = function () {
// const r = origonNodeCreated
@@ -620,6 +627,78 @@ const ext = {
ComfyDeploy.category = "deploy";
},
+ getCustomWidgets() {
+ return {
+ IMAGEPREVIEW(node, inputName, inputData) {
+ // Find or create the URL input widget
+ const urlWidget = node.addWidget(
+ "string",
+ inputName,
+ /* value=*/ "",
+ () => {},
+ { serialize: true },
+ );
+
+ const buttonWidget = node.addWidget(
+ "button",
+ "Open Assets Browser",
+ /* value=*/ "",
+ () => {
+ sendEventToCD("assets", {
+ node: node.id,
+ inputName: inputName,
+ })
+ // console.log("load image");
+ },
+ { serialize: false },
+ );
+
+ console.log(node.widgets);
+
+ console.log("urlWidget", urlWidget);
+
+ // Add image preview functionality
+ function showImage(url) {
+ const img = new Image();
+ img.onload = () => {
+ node.imgs = [img];
+ app.graph.setDirtyCanvas(true);
+ node.setSizeForImage?.();
+ };
+ img.onerror = () => {
+ node.imgs = [];
+ app.graph.setDirtyCanvas(true);
+ };
+ img.src = url;
+ }
+
+ // Set up URL widget value handling
+ let default_value = urlWidget.value;
+ Object.defineProperty(urlWidget, "value", {
+ set: function (value) {
+ this._real_value = value;
+ // Preview image when URL changes
+ if (value) {
+ showImage(value);
+ }
+ },
+ get: function () {
+ return this._real_value || default_value;
+ },
+ });
+
+ // Show initial image if URL exists
+ requestAnimationFrame(() => {
+ if (urlWidget.value) {
+ showImage(urlWidget.value);
+ }
+ });
+
+ return { widget: urlWidget };
+ },
+ };
+ },
+
async setup() {
// const graphCanvas = document.getElementById("graph-canvas");
@@ -661,8 +740,8 @@ const ext = {
console.warn("api.handlePromptGenerated is not a function");
}
sendEventToCD("cd_plugin_onQueuePrompt", prompt);
- } else if (message.type === 'configure_queue_buttons') {
- addQueueButtons(message.data)
+ } else if (message.type === "configure_queue_buttons") {
+ addQueueButtons(message.data);
} else if (message.type === "get_prompt") {
const prompt = await app.graphToPrompt();
sendEventToCD("cd_plugin_onGetPrompt", prompt);
@@ -1903,117 +1982,118 @@ api.fetchApi = async (route, options) => {
return await orginal_fetch_api.call(api, route, options);
};
-
-
// Intercept window drag and drop events
-const originalDropHandler = document.ondrop
+const originalDropHandler = document.ondrop;
document.ondrop = async (e) => {
- console.log('Drop event intercepted:', e)
-
+ console.log("Drop event intercepted:", e);
+
// Prevent default browser behavior
- e.preventDefault()
-
+ e.preventDefault();
+
// Handle files if present
if (e.dataTransfer?.files?.length > 0) {
- const files = Array.from(e.dataTransfer.files)
-
+ const files = Array.from(e.dataTransfer.files);
+
// Send file data to parent directly as JSON
- sendDirectEventToCD('file_drop', {
+ sendDirectEventToCD("file_drop", {
files: files,
x: e.clientX,
y: e.clientY,
- timestamp: Date.now()
- })
+ timestamp: Date.now(),
+ });
}
// Call original handler if exists
if (originalDropHandler) {
- originalDropHandler(e)
+ originalDropHandler(e);
}
-}
+};
-const originalDragEnterHandler = document.ondragenter
+const originalDragEnterHandler = document.ondragenter;
document.ondragenter = (e) => {
// Prevent default to allow drop
- e.preventDefault()
-
+ e.preventDefault();
+
// Send dragenter event to parent directly as JSON
- sendDirectEventToCD('file_dragenter', {
+ sendDirectEventToCD("file_dragenter", {
x: e.clientX,
y: e.clientY,
- timestamp: Date.now()
- })
+ timestamp: Date.now(),
+ });
if (originalDragEnterHandler) {
- originalDragEnterHandler(e)
+ originalDragEnterHandler(e);
}
-}
+};
-const originalDragLeaveHandler = document.ondragleave
+const originalDragLeaveHandler = document.ondragleave;
document.ondragleave = (e) => {
// Prevent default to allow drop
- e.preventDefault()
-
+ e.preventDefault();
+
// Send dragleave event to parent directly as JSON
- sendDirectEventToCD('file_dragleave', {
+ sendDirectEventToCD("file_dragleave", {
x: e.clientX,
y: e.clientY,
- timestamp: Date.now()
- })
+ timestamp: Date.now(),
+ });
if (originalDragLeaveHandler) {
- originalDragLeaveHandler(e)
+ originalDragLeaveHandler(e);
}
-}
+};
-const originalDragOverHandler = document.ondragover
+const originalDragOverHandler = document.ondragover;
document.ondragover = (e) => {
// Prevent default to allow drop
- e.preventDefault()
-
+ e.preventDefault();
+
// Send dragover event to parent directly as JSON
- sendDirectEventToCD('file_dragover', {
+ sendDirectEventToCD("file_dragover", {
x: e.clientX,
y: e.clientY,
- timestamp: Date.now()
- })
+ timestamp: Date.now(),
+ });
if (originalDragOverHandler) {
- originalDragOverHandler(e)
+ originalDragOverHandler(e);
}
-}
+};
// Function to create a single button
function createQueueButton(config) {
- const button = document.createElement('button')
- button.id = `cd-button-${config.id}`
- button.className = 'p-button p-component p-button-icon-only p-button-secondary p-button-text'
+ const button = document.createElement("button");
+ button.id = `cd-button-${config.id}`;
+ button.className =
+ "p-button p-component p-button-icon-only p-button-secondary p-button-text";
button.innerHTML = `
- `
+ `;
button.onclick = () => {
- const eventData = typeof config.eventData === 'function' ?
- config.eventData() :
- config.eventData || {}
- sendEventToCD(config.event, eventData)
- }
- button.setAttribute('data-pd-tooltip', config.tooltip)
- return button
+ const eventData =
+ typeof config.eventData === "function"
+ ? config.eventData()
+ : config.eventData || {};
+ sendEventToCD(config.event, eventData);
+ };
+ button.setAttribute("data-pd-tooltip", config.tooltip);
+ return button;
}
// Function to add buttons to queue group
function addQueueButtons(buttonConfigs = DEFAULT_BUTTONS) {
- const queueButtonGroup = document.querySelector('.queue-button-group.flex')
- if (!queueButtonGroup) return
+ const queueButtonGroup = document.querySelector(".queue-button-group.flex");
+ if (!queueButtonGroup) return;
// Remove any existing CD buttons
- const existingButtons = queueButtonGroup.querySelectorAll('[id^="cd-button-"]')
- existingButtons.forEach(button => button.remove())
+ const existingButtons =
+ queueButtonGroup.querySelectorAll('[id^="cd-button-"]');
+ existingButtons.forEach((button) => button.remove());
// Add new buttons
- buttonConfigs.forEach(config => {
- const button = createQueueButton(config)
- queueButtonGroup.appendChild(button)
- })
-}
\ No newline at end of file
+ buttonConfigs.forEach((config) => {
+ const button = createQueueButton(config);
+ queueButtonGroup.appendChild(button);
+ });
+}