Spaces:
Running
Running
import { app } from '../../../scripts/app.js' | |
import { api } from '../../../scripts/api.js' | |
function offsetDOMWidget( | |
widget, | |
ctx, | |
node, | |
widgetWidth, | |
widgetY, | |
height | |
) { | |
const margin = 10 | |
const elRect = ctx.canvas.getBoundingClientRect() | |
const transform = new DOMMatrix() | |
.scaleSelf( | |
elRect.width / ctx.canvas.width, | |
elRect.height / ctx.canvas.height | |
) | |
.multiplySelf(ctx.getTransform()) | |
.translateSelf(0, widgetY + margin) | |
const scale = new DOMMatrix().scaleSelf(transform.a, transform.d) | |
Object.assign(widget.inputEl.style, { | |
transformOrigin: '0 0', | |
transform: scale, | |
left: `${transform.e}px`, | |
top: `${transform.d + transform.f}px`, | |
width: `${widgetWidth}px`, | |
height: `${(height || widget.parent?.inputHeight || 32) - margin}px`, | |
position: 'absolute', | |
background: !node.color ? '' : node.color, | |
color: !node.color ? '' : 'white', | |
zIndex: 5, //app.graph._nodes.indexOf(node), | |
}) | |
} | |
export const hasWidgets = (node) => { | |
if (!node.widgets || !node.widgets?.[Symbol.iterator]) { | |
return false | |
} | |
return true | |
} | |
export const cleanupNode = (node) => { | |
if (!hasWidgets(node)) { | |
return | |
} | |
for (const w of node.widgets) { | |
if (w.canvas) { | |
w.canvas.remove() | |
} | |
if (w.inputEl) { | |
w.inputEl.remove() | |
} | |
// calls the widget remove callback | |
w.onRemoved?.() | |
} | |
} | |
const CreatePreviewElement = (name, val, format) => { | |
const [type] = format.split('/') | |
const w = { | |
name, | |
type, | |
value: val, | |
draw: function (ctx, node, widgetWidth, widgetY, height) { | |
const [cw, ch] = this.computeSize(widgetWidth) | |
offsetDOMWidget(this, ctx, node, widgetWidth, widgetY, ch) | |
}, | |
computeSize: function (_) { | |
const ratio = this.inputRatio || 1 | |
const width = Math.max(220, this.parent.size[0]) | |
return [width, (width / ratio + 10)] | |
}, | |
onRemoved: function () { | |
if (this.inputEl) { | |
this.inputEl.remove() | |
} | |
}, | |
} | |
w.inputEl = document.createElement(type === 'video' ? 'video' : 'img') | |
w.inputEl.src = w.value | |
if (type === 'video') { | |
w.inputEl.setAttribute('type', 'video/webm'); | |
w.inputEl.autoplay = true | |
w.inputEl.loop = true | |
w.inputEl.controls = false; | |
} | |
w.inputEl.onload = function () { | |
w.inputRatio = w.inputEl.naturalWidth / w.inputEl.naturalHeight | |
} | |
document.body.appendChild(w.inputEl) | |
return w | |
} | |
const gif_preview = { | |
name: 'AnimateDiff.gif_preview', | |
async beforeRegisterNodeDef(nodeType, nodeData, app) { | |
switch (nodeData.name) { | |
case 'ADE_AnimateDiffCombine':{ | |
const onExecuted = nodeType.prototype.onExecuted | |
nodeType.prototype.onExecuted = function (message) { | |
const prefix = 'ad_gif_preview_' | |
const r = onExecuted ? onExecuted.apply(this, message) : undefined | |
if (this.widgets) { | |
const pos = this.widgets.findIndex((w) => w.name === `${prefix}_0`) | |
if (pos !== -1) { | |
for (let i = pos; i < this.widgets.length; i++) { | |
this.widgets[i].onRemoved?.() | |
} | |
this.widgets.length = pos | |
} | |
if (message?.gifs) { | |
message.gifs.forEach((params, i) => { | |
const previewUrl = api.apiURL( | |
'/view?' + new URLSearchParams(params).toString() | |
) | |
const w = this.addCustomWidget( | |
CreatePreviewElement(`${prefix}_${i}`, previewUrl, params.format || 'image/gif') | |
) | |
w.parent = this | |
}) | |
} | |
const onRemoved = this.onRemoved | |
this.onRemoved = () => { | |
cleanupNode(this) | |
return onRemoved?.() | |
} | |
} | |
this.setSize([this.size[0], this.computeSize([this.size[0], this.size[1]])[1]]) | |
return r | |
} | |
break | |
} | |
} | |
} | |
} | |
app.registerExtension(gif_preview) | |