feat: introduce dependencies upload

This commit is contained in:
bennykok
2024-02-05 16:47:21 +08:00
parent df46e3a0e5
commit 4348ab45dc
2 changed files with 312 additions and 20 deletions
+178 -20
View File
@@ -1,6 +1,7 @@
import { app } from "./app.js";
import { api } from "./api.js";
import { ComfyWidgets, LGraphNode } from "./widgets.js";
import { generateDependencyGraph } from "https://esm.sh/[email protected]";
/** @typedef {import('../../../web/types/comfy.js').ComfyExtension} ComfyExtension*/
/** @type {ComfyExtension} */
@@ -178,6 +179,71 @@ function showError(title, message) {
);
}
function createDynamicUIHtml(data) {
let html =
'<div style="max-width: 1024px; margin: 14px auto; display: flex; flex-direction: column; gap: 24px;">';
const bgcolor = "var(--comfy-input-bg)";
const textColor = "var(--input-text)";
// Custom Nodes
html += `<div style="background-color: ${bgcolor}; padding: 24px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);">`;
html +=
'<h2 style="margin-top: 0px; font-size: 24px; font-weight: bold; margin-bottom: 16px;">Custom Nodes</h2>';
Object.values(data.custom_nodes).forEach((node) => {
html += `
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 16px;">
<a href="${
node.url
}" target="_blank" style="font-size: 18px; font-weight: semibold; color: white; text-decoration: none;">${
node.name
}</a>
<p style="font-size: 14px; color: #4b5563;">${node.hash}</p>
${
node.warning
? `<p style="font-size: 14px; color: #d69e2e;">${node.warning}</p>`
: ""
}
</div>
`;
});
html += "</div>";
// Models
html += `<div style="background-color: ${bgcolor}; padding: 24px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);">`;
html +=
'<h2 style="margin-top: 0px; font-size: 24px; font-weight: bold; margin-bottom: 16px;">Models</h2>';
const modelSections = ["checkpoints", "ipadapter", "clip_vision"];
modelSections.forEach((section) => {
html += `
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 8px; padding-bottom: 8px;">
<h3 style="font-size: 18px; font-weight: semibold; margin-bottom: 8px;">${
section.charAt(0).toUpperCase() + section.slice(1)
}</h3>
`;
data.models[section].forEach((item) => {
html += `<p style="font-size: 14px; color: ${textColor};">${item.name}</p>`;
});
html += "</div>";
});
html += "</div>";
// Files
html += `<div style="background-color: ${bgcolor}; padding: 24px; border-radius: 8px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);">`;
html +=
'<h2 style="margin-top: 0px; font-size: 24px; font-weight: bold; margin-bottom: 16px;">Files</h2>';
html += `
<div style="border-bottom: 1px solid #e2e8f0; padding-top: 8px; padding-bottom: 8px;">
<h3 style="font-size: 18px; font-weight: semibold; margin-bottom: 8px;">Images</h3>
`;
data.files.images.forEach((image) => {
html += `<p style="font-size: 14px; color: ${textColor};">${image.name}</p>`;
});
html += "</div></div>";
html += "</div>";
return html;
}
function addButton() {
const menu = document.querySelector(".comfy-menu");
@@ -189,8 +255,29 @@ function addButton() {
/** @type {LGraph} */
const graph = app.graph;
let { endpoint, apiKey, displayName } = getData();
if (!endpoint || !apiKey || apiKey === "" || endpoint === "") {
configDialog.show();
return;
}
const ok = await confirmDialog.confirm(
"Confirm deployment -> " + displayName,
`A new version will be deployed, are you conform? <br><br><input id="include-deps" type="checkbox" checked>Include dependence</input>`,
);
if (!ok) return;
const includeDeps = document.getElementById("include-deps").checked;
if (endpoint.endsWith("/")) {
endpoint = endpoint.slice(0, -1);
}
loadingDialog.showLoading("Generating snapshot", "Please wait...");
const snapshot = await fetch("/snapshot/get_current").then((x) => x.json());
// console.log(snapshot);
loadingDialog.close();
if (!snapshot) {
showError(
@@ -224,32 +311,94 @@ function addButton() {
const deployMetaNode = deployMeta[0];
// console.log(deployMetaNode);
const workflow_name = deployMetaNode.widgets[0].value;
const workflow_id = deployMetaNode.widgets[1].value;
console.log(workflow_name, workflow_id);
const prompt = await app.graphToPrompt();
console.log(graph);
console.log(prompt);
let deps = undefined;
// const endpoint = localStorage.getItem("endpoint") ?? "";
// const apiKey = localStorage.getItem("apiKey");
if (includeDeps) {
loadingDialog.showLoading("Fetching existing version", "Please wait...");
const { endpoint, apiKey, displayName } = getData();
const existing_workflow = await fetch(
endpoint + "/api/workflow/" + workflow_id,
{
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + apiKey,
},
},
)
.then((x) => x.json())
.catch(() => {
return {};
});
if (!endpoint || !apiKey || apiKey === "" || endpoint === "") {
configDialog.show();
return;
loadingDialog.close();
loadingDialog.showLoading(
"Generating dependency graph",
"Please wait...",
);
deps = await generateDependencyGraph(
prompt.output,
snapshot,
async (file) => {
console.log(file);
loadingDialog.showLoading("Generating hash", file);
const hash = await fetch(
`/comfyui-deploy/get-file-hash?file_path=${encodeURIComponent(
file,
)}`,
).then((x) => x.json());
loadingDialog.showLoading("Generating hash", file);
console.log(hash);
return hash.file_hash;
},
async (file, hash, prevhash) => {
console.log("Uploading ", file);
loadingDialog.showLoading("Uploading file", file);
try {
const { download_url } = await fetch(
`/comfyui-deploy/upload-file`,
{
method: "POST",
body: JSON.stringify({
file_path: file,
token: apiKey,
url: endpoint + "/api/upload-url",
}),
},
)
.then((x) => x.json())
.catch(() => {
loadingDialog.close();
confirmDialog.confirm("Error", "Unable to upload file " + file);
});
loadingDialog.showLoading("Uploaded file", file);
console.log(download_url);
return download_url;
} catch (error) {
return undefined;
}
},
existing_workflow.dependencies,
);
loadingDialog.close();
const depsOk = await confirmDialog.confirm(
"Check dependencies",
// JSON.stringify(deps, null, 2),
createDynamicUIHtml(deps),
);
if (!depsOk) return;
console.log(deps);
}
const ok = await confirmDialog.confirm(
"Confirm deployment -> " + displayName,
"A new version will be deployed, are you conform?",
);
if (!ok) return;
loadingDialog.showLoading("Deploying...");
title.innerText = "Deploying...";
title.style.color = "orange";
@@ -261,6 +410,8 @@ function addButton() {
endpoint = endpoint.slice(0, -1);
}
// console.log(prompt.workflow);
const apiRoute = endpoint + "/api/workflow";
// const userId = apiKey
try {
@@ -270,6 +421,7 @@ function addButton() {
workflow: prompt.workflow,
workflow_api: prompt.output,
snapshot: snapshot,
dependencies: deps,
};
console.log(body);
let data = await fetch(apiRoute, {
@@ -289,6 +441,8 @@ function addButton() {
data = await data.json();
}
loadingDialog.close();
title.textContent = "Done";
title.style.color = "green";
@@ -305,6 +459,7 @@ function addButton() {
title.style.color = "white";
}, 1000);
} catch (e) {
loadingDialog.close();
app.ui.dialog.show(e);
console.error(e);
title.textContent = "Error";
@@ -381,7 +536,7 @@ export class InfoDialog extends ComfyDialog {
showMessage(title, message) {
this.show(`
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
<div style="width: 100%; max-width: 600px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
<h3 style="margin: 0px;">${title}</h3>
<label>
${message}
@@ -437,7 +592,10 @@ export class LoadingDialog extends ComfyDialog {
showLoading(title, message) {
this.show(`
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
<h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 4px;">${title} ${this.loadingIcon}</h3>
<h3 style="margin: 0px; display: flex; align-items: center; justify-content: center; gap: 12px;">${title} ${
this.loadingIcon
}</h3>
${message ? `<label>${message}</label>` : ""}
</div>
`);
}
@@ -556,7 +714,7 @@ export class ConfirmDialog extends InfoDialog {
return new Promise((resolve, reject) => {
this.callback = resolve;
this.show(`
<div style="width: 400px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
<div style="width: 100%; max-width: 600px; display: flex; gap: 18px; flex-direction: column; overflow: unset">
<h3 style="margin: 0px;">${title}</h3>
<label>
${message}