Spaces:
Runtime error
Runtime error
File size: 5,896 Bytes
e26a977 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
import { app } from "../../scripts/app.js"
import { api } from "../../scripts/api.js"
function splitFilePath(path) {
const folder_separator = path.lastIndexOf("/")
if (folder_separator === -1) {
return ["", path]
}
return [
path.substring(0, folder_separator),
path.substring(folder_separator + 1)
]
}
function getResourceURL(subfolder, filename, type = "input") {
const params = [
"filename=" + encodeURIComponent(filename),
"type=" + type,
"subfolder=" + subfolder,
app.getRandParam().substring(1)
].join("&")
return `/view?${params}`
}
async function uploadFile(
audioWidget,
audioUIWidget,
file,
updateNode,
pasted = false
) {
try {
// Wrap file in formdata so it includes filename
const body = new FormData()
body.append("image", file)
if (pasted) body.append("subfolder", "pasted")
const resp = await api.fetchApi("/upload/image", {
method: "POST",
body
})
if (resp.status === 200) {
const data = await resp.json()
// Add the file to the dropdown list and update the widget value
let path = data.name
if (data.subfolder) path = data.subfolder + "/" + path
if (!audioWidget.options.values.includes(path)) {
audioWidget.options.values.push(path)
}
if (updateNode) {
audioUIWidget.element.src = api.apiURL(
getResourceURL(...splitFilePath(path))
)
audioWidget.value = path
}
} else {
alert(resp.status + " - " + resp.statusText)
}
} catch (error) {
alert(error)
}
}
// AudioWidget MUST be registered first, as AUDIOUPLOAD depends on AUDIO_UI to be
// present.
app.registerExtension({
name: "Comfy.AudioWidget",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (["LoadAudio", "SaveAudio", "PreviewAudio"].includes(nodeType.comfyClass)) {
nodeData.input.required.audioUI = ["AUDIO_UI"]
}
},
getCustomWidgets() {
return {
AUDIO_UI(node, inputName) {
const audio = document.createElement("audio")
audio.controls = true
audio.classList.add("comfy-audio")
audio.setAttribute("name", "media")
const audioUIWidget = node.addDOMWidget(
inputName,
/* name=*/ "audioUI",
audio
)
// @ts-ignore
// TODO: Sort out the DOMWidget type.
audioUIWidget.serialize = false
const isOutputNode = node.constructor.nodeData.output_node
if (isOutputNode) {
// Hide the audio widget when there is no audio initially.
audioUIWidget.element.classList.add("empty-audio-widget")
// Populate the audio widget UI on node execution.
const onExecuted = node.onExecuted
node.onExecuted = function(message) {
onExecuted?.apply(this, arguments)
const audios = message.audio
if (!audios) return
const audio = audios[0]
audioUIWidget.element.src = api.apiURL(
getResourceURL(audio.subfolder, audio.filename, audio.type)
)
audioUIWidget.element.classList.remove("empty-audio-widget")
}
}
return { widget: audioUIWidget }
}
}
},
onNodeOutputsUpdated(nodeOutputs) {
for (const [nodeId, output] of Object.entries(nodeOutputs)) {
const node = app.graph.getNodeById(Number.parseInt(nodeId));
if ("audio" in output) {
const audioUIWidget = node.widgets.find((w) => w.name === "audioUI");
const audio = output.audio[0];
audioUIWidget.element.src = api.apiURL(getResourceURL(audio.subfolder, audio.filename, audio.type));
audioUIWidget.element.classList.remove("empty-audio-widget");
}
}
},
})
app.registerExtension({
name: "Comfy.UploadAudio",
async beforeRegisterNodeDef(nodeType, nodeData) {
if (nodeData?.input?.required?.audio?.[1]?.audio_upload === true) {
nodeData.input.required.upload = ["AUDIOUPLOAD"]
}
},
getCustomWidgets() {
return {
AUDIOUPLOAD(node, inputName) {
// The widget that allows user to select file.
const audioWidget = node.widgets.find(w => w.name === "audio")
const audioUIWidget = node.widgets.find(w => w.name === "audioUI")
const onAudioWidgetUpdate = () => {
audioUIWidget.element.src = api.apiURL(
getResourceURL(...splitFilePath(audioWidget.value))
)
}
// Initially load default audio file to audioUIWidget.
if (audioWidget.value) {
onAudioWidgetUpdate()
}
audioWidget.callback = onAudioWidgetUpdate
// Load saved audio file widget values if restoring from workflow
const onGraphConfigured = node.onGraphConfigured;
node.onGraphConfigured = function() {
onGraphConfigured?.apply(this, arguments)
if (audioWidget.value) {
onAudioWidgetUpdate()
}
}
const fileInput = document.createElement("input")
fileInput.type = "file"
fileInput.accept = "audio/*"
fileInput.style.display = "none"
fileInput.onchange = () => {
if (fileInput.files.length) {
uploadFile(audioWidget, audioUIWidget, fileInput.files[0], true)
}
}
// The widget to pop up the upload dialog.
const uploadWidget = node.addWidget(
"button",
inputName,
/* value=*/ "",
() => {
fileInput.click()
}
)
uploadWidget.label = "choose file to upload"
uploadWidget.serialize = false
return { widget: uploadWidget }
}
}
}
})
|