Eji-Sensei14's picture
Upload folder using huggingface_hub
c6535db verified
import { app, api} from './comfyAPI.js'
import {useNodeCanvasImagePreview} from "@/composable/node/canvasImagePreview.js";
import {useNodeVideoPreview} from "@/composable/node/videoPreview.js";
const IMAGE_NODE_PROPERTY = 'image_upload'
const VIDEO_NODE_PROPERTY = 'video_upload'
const getNodeData = (node) => node.constructor?.nodeData
const getInputSpecsFromData = (inputData) =>{
if (!inputData) return []
const { required, optional } = inputData
const inputSpecs = []
if (required) {
for (const value of Object.values(required)) {
inputSpecs.push(value)
}
}
if (optional) {
for (const value of Object.values(optional)) {
inputSpecs.push(value)
}
}
return inputSpecs
}
const hasInputProperty = (node, property
) => {
if (!node) return false
const nodeData = getNodeData(node)
if (!nodeData?.input) return false
const inputs = getInputSpecsFromData(nodeData.input)
return inputs.some((input) => input?.[1]?.[property])
}
const hasImageElements = imgs => Array.isArray(imgs) && imgs.some(img => img instanceof HTMLImageElement)
export function isImageNode(node) {
if (!node) return false
if (node.imgs?.length && hasImageElements(node.imgs)) return true
if (!node.widgets) return false
return hasInputProperty(node, IMAGE_NODE_PROPERTY)
}
export function isVideoNode(node) {
if (!node) return false
if (node.videoContainer) return true
if (!node.widgets) return false
return hasInputProperty(node, VIDEO_NODE_PROPERTY)
}
const getNodeId = (node) => node?.id.toString()
const getNodeOutputs = (node) => {
return app.nodeOutputs[getNodeId(node)]
}
const getNodePreviews = (node) => {
return app.nodePreviewImages[getNodeId(node)]
}
const getPreviewParam = (node) => {
if (node.animatedImages) return ''
return app.getPreviewFormatParam()
}
function getNodeImageUrls(node) {
const previews = getNodePreviews(node)
const outputs = getNodeOutputs(node)
// If the node is running, return the previews
if (previews?.length && !node.isOutputFinal) return previews
if (!outputs?.images?.length) {
if(previews?.length) return previews
return
}
const rand = app.getRandParam()
const previewParam = getPreviewParam(node)
return outputs.images.map((image) => {
const imgUrlPart = new URLSearchParams(image)
return api.apiURL(`/view?${imgUrlPart}${previewParam}${rand}`)
})
}
const VIDEO_WIDGET_NAME = 'video-preview'
const VIDEO_DEFAULT_OPTIONS = {
playsInline: true,
controls: true,
loop: true
}
const MEDIA_LOAD_TIMEOUT = 8192
const MAX_RETRIES = 1
const VIDEO_PIXEL_OFFSET = 64
const createContainer = () => {
const container = document.createElement('div')
container.classList.add('comfy-img-preview')
return container
}
const createTimeout = (ms) => new Promise((resolve) => setTimeout(() => resolve(null), ms))
const getVideoRealURL = obj => {
return api.apiURL(`/view?filename=${encodeURIComponent(obj.filename)}&type=${obj.type}&subfolder=${obj.subfolder}&rand=${Math.random()}`)
}
export const useNodePreview = (node, options) => {
const { loadElement, onLoaded, onFailedLoading } = options
const loadElementWithTimeout = async (url, retryCount = 0) => {
const result = await Promise.race([
loadElement(url),
createTimeout(MEDIA_LOAD_TIMEOUT)
])
if (result === null && retryCount < MAX_RETRIES) {
return loadElementWithTimeout(url, retryCount + 1)
}
return result
}
const loadElements = async (urls) =>
Promise.all(urls.map((url) => loadElementWithTimeout(url)))
// const render = () => {
// node.graph?.setDirtyCanvas(true)
// }
/**
* Displays media element(s) on the node.
*/
function showPreview() {
if (node.isLoading) return
const outputUrls = node.videos?.length>0 ? node.videos.map(cate=> getVideoRealURL(cate)) : getNodeImageUrls(node)
if (!outputUrls?.length) return
if (options?.block) node.isLoading = true
loadElements(outputUrls)
.then((elements) => {
const validElements = elements.filter(
(el) => el !== null
)
if (validElements.length) {
onLoaded?.(validElements)
// render()
}
})
.catch(() => {
onFailedLoading?.()
})
.finally(() => {
node.isLoading = false
})
}
return {
showPreview
}
}
export const useNodeImage = (node) => {
node.previewMediaType = 'image'
const loadElement = (url) =>
new Promise((resolve) => {
const img = new Image()
img.onload = () => resolve(img)
img.onerror = () => resolve(null)
img.src = url
})
const onLoaded = (elements) => {
node.imageIndex = null
node.imgs = elements
}
return useNodePreview(node, {
loadElement,
onLoaded,
onFailedLoading: () => {
node.imgs = undefined
}
})
}
export const useNodeVideo = (node) => {
node.previewMediaType = 'video'
const loadElement = (url) =>
new Promise((resolve) => {
const video = document.createElement('video')
Object.assign(video, VIDEO_DEFAULT_OPTIONS)
video.onloadeddata = () => resolve(video)
video.onerror = () => resolve(null)
video.src = url
})
const addVideoDomWidget = (container) => {
const hasWidget = node.widgets?.some((w) => w.name === VIDEO_WIDGET_NAME)
if (!hasWidget) {
node.addDOMWidget(VIDEO_WIDGET_NAME, 'video', container, {
hideOnZoom: false,
serialize: false
})
}
}
const onLoaded = (videoElements) => {
const videoElement = videoElements[0]
if (!videoElement) return
if (!node.videoContainer) {
if(node.imgs) node.imgs = undefined
node.videoContainer = createContainer()
node.videoContainer.style.pointerEvents = 'auto'
addVideoDomWidget(node.videoContainer)
}
node.videoContainer.replaceChildren(videoElement)
node.imageOffset = VIDEO_PIXEL_OFFSET
}
return useNodePreview(node, {
loadElement,
onLoaded,
onFailedLoading: () => {
node.videoContainer = undefined
}
})
}
export function unsafeDrawBackground(_this, ctx) {
if (_this.flags.collapsed) return
const { showCanvasImagePreview, removeCanvasImagePreview } = useNodeCanvasImagePreview()
const { showVideoPreview, removeVideoPreview} = useNodeVideoPreview()
const output = getNodeOutputs(_this)
const preview = getNodePreviews(_this)
const isNewOutput = output && (_this.images !== output.images || _this.videos !== output.videos)
const isNewPreview = preview && _this.preview !== preview
if (isNewPreview) {
_this.isOutputFinal = false
if(_this.isTwiceRendered) _this.isTwiceRendered = false
_this.preview = preview
}
if (isNewOutput) {
_this.isOutputFinal = true
if(output.images) _this.images = output.images
if(output.videos) _this.videos = output.videos
}
if (isNewOutput || isNewPreview) {
_this.animatedImages = output?.animated
const isAnimatedWebp = _this.animatedImages && output.images.some((img) => img.filename?.includes('webp'))
const isVideo = (_this.animatedImages && !isAnimatedWebp) || isVideoNode(this)
if (isNewOutput && output?.videos){
useNodeVideo(_this).showPreview()
}
else if (isVideo) {
useNodeVideo(_this).showPreview()
} else {
useNodeImage(_this).showPreview()
}
}
// 二次渲染
if(_this.id != app.runningNodeId && !_this.isTwiceRendered && _this.isOutputFinal){
if(_this.videos) useNodeVideo(_this.videos)
else useNodeImage(_this).showPreview()
_this.isTwiceRendered = true
}
// Nothing to do
if (!_this.imgs?.length) return
if (_this.animatedImages || (isNewOutput && output?.videos)) {
removeCanvasImagePreview(_this)
showVideoPreview(_this)
} else {
removeVideoPreview(_this)
showCanvasImagePreview(_this)
}
}