diff --git a/src/extensions/core/uploadImage.ts b/src/extensions/core/uploadImage.ts index 41e87c1c47..58e43bb218 100644 --- a/src/extensions/core/uploadImage.ts +++ b/src/extensions/core/uploadImage.ts @@ -2,27 +2,13 @@ import type { LGraphNode } from '@/lib/litegraph/src/LGraphNode' import { type ComfyNodeDef, type InputSpec, - isComboInputSpecV1 + isMediaUploadComboInput } from '@/schemas/nodeDefSchema' import { app } from '../../scripts/app' // Adds an upload button to the nodes -const isMediaUploadComboInput = (inputSpec: InputSpec) => { - const [inputName, inputOptions] = inputSpec - if (!inputOptions) return false - - const isUploadInput = - inputOptions['image_upload'] === true || - inputOptions['video_upload'] === true || - inputOptions['animated_image_upload'] === true - - return ( - isUploadInput && (isComboInputSpecV1(inputSpec) || inputName === 'COMBO') - ) -} - const createUploadInput = ( imageInputName: string, imageInputOptions: InputSpec diff --git a/src/schemas/nodeDefSchema.ts b/src/schemas/nodeDefSchema.ts index 466711fa27..2fdfeeada9 100644 --- a/src/schemas/nodeDefSchema.ts +++ b/src/schemas/nodeDefSchema.ts @@ -158,6 +158,20 @@ export function isComboInputSpec( return isComboInputSpecV1(inputSpec) || isComboInputSpecV2(inputSpec) } +export function isMediaUploadComboInput(inputSpec: InputSpec): boolean { + const [inputName, inputOptions] = inputSpec + if (!inputOptions) return false + + const isUploadInput = + inputOptions['image_upload'] === true || + inputOptions['video_upload'] === true || + inputOptions['animated_image_upload'] === true + + return ( + isUploadInput && (isComboInputSpecV1(inputSpec) || inputName === 'COMBO') + ) +} + /** * Get the type of an input spec. * diff --git a/src/scripts/app.ts b/src/scripts/app.ts index 888d422dce..f2ac4c9695 100644 --- a/src/scripts/app.ts +++ b/src/scripts/app.ts @@ -89,7 +89,8 @@ import { executeWidgetsCallback, createNode, fixLinkInputSlots, - isImageNode + isImageNode, + isVideoNode } from '@/utils/litegraphUtil' import { createSharedObjectUrl, @@ -1822,6 +1823,7 @@ export class ComfyApp { this.registerNodeDef(nodeId, defs[nodeId]) } // Refresh combo widgets in all nodes including those in subgraphs + const nodeOutputStore = useNodeOutputStore() forEachNode(this.rootGraph, (node) => { const def = defs[node.type] // Allow primitive nodes to handle refresh @@ -1854,6 +1856,12 @@ export class ComfyApp { } } } + + // Re-trigger previews on media nodes (e.g. LoadImage) + // to bust browser cache when files are edited externally + if (isImageNode(node) || isVideoNode(node)) { + nodeOutputStore.refreshNodeOutputs(node) + } }) await useExtensionService().invokeExtensionsAsync( diff --git a/src/stores/imagePreviewStore.ts b/src/stores/imagePreviewStore.ts index eea5063ca8..c5fee420a9 100644 --- a/src/stores/imagePreviewStore.ts +++ b/src/stores/imagePreviewStore.ts @@ -388,6 +388,16 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => { } } + function refreshNodeOutputs(node: LGraphNode) { + const locatorId = nodeToNodeLocatorId(node) + if (!locatorId) return + + const outputs = app.nodeOutputs[locatorId] + if (!outputs) return + + nodeOutputs.value[locatorId] = { ...outputs } + } + function resetAllOutputsAndPreviews() { app.nodeOutputs = {} nodeOutputs.value = {} @@ -433,6 +443,7 @@ export const useNodeOutputStore = defineStore('nodeOutput', () => { setNodePreviewsByExecutionId, setNodePreviewsByNodeId, updateNodeImages, + refreshNodeOutputs, syncLegacyNodeImgs, // Cleanup