Initiliazing Runware ComfyUI Nodes Alpha 0.8

This commit is contained in:
Jack
2024-12-04 17:39:06 +00:00
parent e7e7417669
commit 08791ef84c
31 changed files with 1883 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
import { app } from "../../scripts/app.js";
import { promptEnhanceHandler, syncDimensionsNodeHandler, searchNodeHandler, APIKeyHandler } from "./utils.js";
import { RUNWARE_NODE_TYPES, RUNWARE_NODE_PROPS, SEARCH_TERMS } from "./types.js";
app.registerExtension({
name: "runware.ai",
async setup() {
// pass
},
async nodeCreated(node) {
const nodeClass = node.comfyClass;
let crNodeProps = false;
if(typeof nodeClass === "string" && Object.values(RUNWARE_NODE_TYPES).includes(nodeClass)) {
crNodeProps = RUNWARE_NODE_PROPS[nodeClass];
} else {
return;
}
node.bgcolor = crNodeProps.bgColor;
if(nodeClass === RUNWARE_NODE_TYPES.APIMANAGER) {
APIKeyHandler(node);
}
if(crNodeProps.colorModeOnly === true) return;
const nodeWidgets = node.widgets;
if(nodeWidgets.length <= 0) return;
for(const nodeWidget of nodeWidgets) {
const widgetName = nodeWidget.name;
const widgetType = nodeWidget.type;
if(crNodeProps.promptEnhancer === true && widgetType === "customtext") {
if (widgetName === "positivePrompt" || widgetName === "negativePrompt") {
nodeWidget.inputEl.addEventListener('keydown', promptEnhanceHandler);
}
} else if(crNodeProps.liveDimensions === true && widgetType === "combo" && widgetName === "dimensions") {
syncDimensionsNodeHandler(node, nodeWidget);
}
if(crNodeProps.liveSearch === true) {
if(widgetType === "text" && SEARCH_TERMS.includes(widgetName)) {
searchNodeHandler(node, nodeWidget);
}
}
}
}
})
+141
View File
@@ -0,0 +1,141 @@
const DEFAULT_BGCOLOR = "#6c5ce7";
const DEFAULT_DIMENSIONS_LIST = {
"Square (512x512)": "512x512",
"Square HD (1024x1024)": "1024x1024",
"Portrait 3:4 (768x1024)": "768x1024",
"Portrait 9:16 (576x1024)": "576x1024",
"Landscape 4:3 (1024x768)": "1024x768",
"Landscape 16:9 (1024x576)": "1024x576"
};
const DEFAULT_MODELS_ARCH_LIST = {
"All": "all",
"FLUX.1-Schnell": "flux1s",
"FLUX.1-Dev": "flux1d",
"Pony": "pony",
"SD 1.5": "sd1x",
"SD 1.5 Hyper": "sdhyper",
"SD 1.5 LCM": "sd1xlcm",
"SD 3": "sd3",
"SDXL 1.0": "sdxl",
"SDXL 1.0 LCM": "sdxllcm",
"SDXL Distilled": "sdxldistilled",
"SDXL Hyper": "sdxlhyper",
"SDXL Lightning": "sdxllightning",
"SDXL Turbo": "sdxlturbo",
};
const DEFAULT_CONTROLNET_CONDITIONING_LIST = {
"All": "all",
"Canny": "canny",
"Depth": "depth",
"MLSD": "mlsd",
"Normal BAE": "normalbae",
"Open Pose": "openpose",
"Tile": "tile",
"Seg": "seg",
"Line Art": "lineart",
"Line Art Anime": "lineart_anime",
"Shuffle": "shuffle",
"Scribble": "scribble",
"Soft Edge": "softedge",
};
const SEARCH_TERMS = ["Model Search", "Lora Search", "ControlNet Search"];
const MODEL_TYPES_TERMS = ["ModelType", "LoraType", "ControlNetType"];
const MODEL_LIST_TERMS = ["ModelList", "LoraList", "ControlNetList"];
const RUNWARE_NODE_TYPES = {
IMAGEINFERENCE: "Runware Image Inference",
PHOTOMAKER: "Runware PhotoMaker V2",
MODELSEARCH: "Runware Model Search",
LORASEARCH: "Runware Lora Search",
CONTROLNET: "Runware ControlNet",
BGREMOVAL: "Runware Background Removal",
UPSCALER: "Runware Image Upscaler",
REFINER: "Runware Refiner",
LORACOMBINE: "Runware Lora Combine",
CONTROLNETCOMBINE: "Runware ControlNet Combine",
IMAGEMASKING: "Runware Image Masking",
CONTROLNETPREPROCESSING: "Runware ControlNet PreProcessor",
APIMANAGER: "Runware API Manager",
IMAGECAPTION: "Runware Image Caption",
};
const RUNWARE_NODE_PROPS = {
[RUNWARE_NODE_TYPES.IMAGEINFERENCE]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.PHOTOMAKER]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
promptEnhancer: true,
},
[RUNWARE_NODE_TYPES.MODELSEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.LORASEARCH]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.CONTROLNET]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETCONDITIONING]: {
bgColor: DEFAULT_BGCOLOR,
liveSearch: true,
},
[RUNWARE_NODE_TYPES.BGREMOVAL]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.UPSCALER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.REFINER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.LORACOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETCOMBINE]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGEMASKING]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.CONTROLNETPREPROCESSING]: {
bgColor: DEFAULT_BGCOLOR,
liveDimensions: true,
},
[RUNWARE_NODE_TYPES.APIMANAGER]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
[RUNWARE_NODE_TYPES.IMAGECAPTION]: {
bgColor: DEFAULT_BGCOLOR,
colorModeOnly: true,
},
};
export {
DEFAULT_BGCOLOR,
DEFAULT_DIMENSIONS_LIST,
DEFAULT_MODELS_ARCH_LIST,
DEFAULT_CONTROLNET_CONDITIONING_LIST,
SEARCH_TERMS,
MODEL_TYPES_TERMS,
MODEL_LIST_TERMS,
RUNWARE_NODE_TYPES,
RUNWARE_NODE_PROPS
}
+249
View File
@@ -0,0 +1,249 @@
import { app } from "../../scripts/app.js";
import { api } from "../../scripts/api.js";
import { DEFAULT_DIMENSIONS_LIST, DEFAULT_MODELS_ARCH_LIST, SEARCH_TERMS,
DEFAULT_CONTROLNET_CONDITIONING_LIST, RUNWARE_NODE_TYPES, MODEL_TYPES_TERMS, MODEL_LIST_TERMS
} from "./types.js";
async function enhancePrompt(userPrompt) {
const resp = await api.fetchApi('/promptEnhance', {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
userPrompt: userPrompt
})
});
const enhancePromptResults = await resp.json();
return enhancePromptResults;
}
async function setAPIKey(apiKey) {
const resp = await api.fetchApi('/setAPIKey', {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
apiKey: apiKey
})
});
const setAPIKeyResults = await resp.json();
return setAPIKeyResults;
}
async function modelSearch(modelQuery = "", modelArch = "all", modelType = "base", modelCat = "checkpoint", condtioning = "") {
const resp = await api.fetchApi('/modelSearch', {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
modelQuery: modelQuery,
modelArch: modelArch,
modelType: modelType,
modelCat: modelCat,
condtioning: condtioning
})
});
const modelSearchResults = await resp.json();
return modelSearchResults;
}
function notifyUser(message, type="info", title = "Runware", life = 4.5) {
app.extensionManager.toast.add({
severity: type, // 'info', 'success', 'warn', 'error' \\
summary: title,
detail: message,
life: Math.floor(life * 1000)
});
}
async function promptEnhanceHandler(e) {
if(e.altKey && e.shiftKey && e.key === 'E') {
const userPrompt = e.target.value.trim();
if(userPrompt.length <= 1) {
notifyUser("Prompt Is Too Short!", "error", "Runware Prompt Enhancer");
return;
} else if(userPrompt.length > 300) {
notifyUser("Prompt Must Not Exceed 300 Characters!", "error", "Runware Prompt Enhancer");
return;
}
const enhanceResults = await enhancePrompt(userPrompt);
if(enhanceResults.success) {
e.target.value = enhanceResults.enhancedPrompt;
} else {
notifyUser(enhanceResults.error, "error", "Runware Prompt Enhancer");
}
}
}
function remNode(node) {
app.graph.remove(node);
app.graph.setDirtyCanvas(true,true);
}
async function APIKeyHandler(apiManagerNode) {
const widgets = apiManagerNode.widgets;
for(const widget of widgets) {
if(widget.name === "API Key") {
appendWidgetCB(widget, async function(...args) {
const apiKey = args[0].trim();
if(apiKey.length < 30) {
notifyUser("Invalid API Key Set, Please Try Again!", "error", "Runware API Manager");
return;
} else {
const setAPIKeyResults = await setAPIKey(apiKey);
if(setAPIKeyResults.success) {
remNode(apiManagerNode);
notifyUser("API Key Set Successfully!", "success", "Runware API Manager");
} else {
notifyUser(setAPIKeyResults.error, "error", "Runware API Manager");
}
}
});
break;
}
}
}
function appendWidgetCB(node, newCB) {
const oldNodeCB = node.callback;
if(typeof oldNodeCB === "function") {
node.callback = function(...args) {
newCB?.apply(this, args);
oldNodeCB?.apply(this, args);
}
} else {
node.callback = newCB;
}
}
async function syncDimensionsNodeHandler(node, dimensionsWidget) {
const nodeWidgets = node.widgets;
let widthWidget = false, heightWidget = false;
for(const nodeWidget of nodeWidgets) {
const widgetName = nodeWidget.name;
const widgetType = nodeWidget.type;
if(widgetName === "width" && widgetType === "number") widthWidget = nodeWidget;
if(widgetName === "height" && widgetType === "number") heightWidget = nodeWidget;
}
appendWidgetCB(dimensionsWidget, function(...args) {
const chosenDimension = args[0];
if(chosenDimension === "Custom") return;
const dimensionValue = DEFAULT_DIMENSIONS_LIST[chosenDimension];
const [width, height] = dimensionValue.split("x");
widthWidget.callback(width, "customSetOperation");
heightWidget.callback(height, "customSetOperation");
});
if(widthWidget !== false) {
appendWidgetCB(widthWidget, function(...args) {
if(args[1] === "customSetOperation") return;
dimensionsWidget.value = "Custom";
});
}
if(heightWidget !== false) {
appendWidgetCB(heightWidget, function(...args) {
if(args[1] === "customSetOperation") return;
dimensionsWidget.value = "Custom";
});
}
}
async function searchNodeHandler(searchNode, searchInputWidget) {
const searchNodeWidgets = searchNode.widgets;
let modelArchWidget = false, modelTypeWidget = false, modelListWidget = false, defaultWidgetValues = {};
let isLora = false, isControlNet = false, modelTypeValue;
if(searchNode.comfyClass === RUNWARE_NODE_TYPES.LORASEARCH) isLora = true;
if(searchNode.comfyClass === RUNWARE_NODE_TYPES.CONTROLNET) isControlNet = true;
for(const searchWidget of searchNodeWidgets) {
const widgetName = searchWidget.name;
const widgetType = searchWidget.type;
if(widgetName === "Model Architecture" && widgetType === "combo") {
modelArchWidget = searchWidget;
defaultWidgetValues["modelArch"] = searchWidget.options.values;
}
if(MODEL_TYPES_TERMS.includes(widgetName) && widgetType === "combo") {
modelTypeWidget = searchWidget;
defaultWidgetValues["modelType"] = searchWidget.options.values;
}
if(MODEL_LIST_TERMS.includes(widgetName) && widgetType === "combo") {
modelListWidget = searchWidget;
defaultWidgetValues["modelList"] = searchWidget.options.values;
}
}
function resetValues() {
if(modelArchWidget) {
modelArchWidget.options.values = defaultWidgetValues["modelArch"];
modelArchWidget.value = defaultWidgetValues["modelArch"][0];
}
if(modelTypeWidget) {
modelTypeWidget.options.values = defaultWidgetValues["modelType"];
modelTypeWidget.value = defaultWidgetValues["modelType"][0];
}
if(modelListWidget) {
modelListWidget.options.values = defaultWidgetValues["modelList"];
modelListWidget.value = defaultWidgetValues["modelList"][0];
}
}
async function searchModels() {
const searchQuery = searchInputWidget.value.trim();
const modelArchValue = DEFAULT_MODELS_ARCH_LIST[modelArchWidget.value];
if(isControlNet) {
modelTypeValue = DEFAULT_CONTROLNET_CONDITIONING_LIST[modelTypeWidget.value];
} else {
modelTypeValue = modelTypeWidget.value.toLowerCase().replace("model", "").trim();
}
let modelSearchResults = null;
if(isControlNet) {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, "", "controlnet", modelTypeValue);
} else if(isLora) {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, "", modelTypeValue);
} else {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, modelTypeValue);
}
if(modelSearchResults.success) {
const modelListArray = [];
modelSearchResults.modelList.forEach(modelObj => {
modelListArray.push(`${modelObj.air} (${modelObj.name} ${modelObj.version})`);
});
modelListWidget.options.values = modelListArray;
modelListWidget.value = modelListArray[0];
} else {
notifyUser(modelSearchResults.error, "error", "Runware Search");
}
}
appendWidgetCB(searchInputWidget, async function(...args) {
const searchQuery = args[0].trim();
if(searchQuery.length === 0) {
resetValues();
} else if(searchQuery.length < 2 || searchQuery.length > 32) {
notifyUser("Invalid Search Value, Search Query must be between 2 and 32 characters!", "error", "Runware Search");
return;
} else {
await searchModels();
}
});
appendWidgetCB(modelArchWidget, async function(...args) {
await searchModels();
});
if(!isLora){
appendWidgetCB(modelTypeWidget, async function(...args) {
await searchModels();
});
}
}
export {
enhancePrompt,
modelSearch,
notifyUser,
promptEnhanceHandler,
syncDimensionsNodeHandler,
searchNodeHandler,
APIKeyHandler,
};