Added Global Output Format and Output Quality Settings + Some Bug Fixes

This commit is contained in:
Jack
2025-02-21 02:29:19 +02:00
parent 7bb7a67f91
commit 7ace0b8196
10 changed files with 163 additions and 98 deletions
+80 -76
View File
@@ -4,60 +4,36 @@ import { DEFAULT_DIMENSIONS_LIST, DEFAULT_MODELS_ARCH_LIST, DEFAULT_CONTROLNET_C
RUNWARE_NODE_TYPES, MODEL_TYPES_TERMS, MODEL_LIST_TERMS
} from "./types.js";
const TIMEOUT_RANGE = { min: 5, default: 90, max: 99 };
const OUTPUT_QUALITY_RANGE = { min: 20, default: 95, max: 99 };
let openDialog = false;
let lastTimeout = false;
let lastOutputFormat = false;
let lastOutputQuality = false;
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 queryLocalAPI(endpoint, data) {
try {
const resp = await api.fetchApi(`/${endpoint}`, {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(data)
});
return await resp.json();
} catch (err) {
return false;
}
}
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 setTimeout(maxTimeout) {
const resp = await api.fetchApi('/setMaxTimeout', {
method: "POST",
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
maxTimeout: maxTimeout
})
});
const setTimeoutResults = await resp.json();
return setTimeoutResults;
}
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;
}
const runwareLocalAPI = {
enhancePrompt: (userPrompt) => queryLocalAPI('promptEnhance', { userPrompt }),
setAPIKey: (apiKey) => queryLocalAPI('setAPIKey', { apiKey }),
setTimeout: (maxTimeout) => queryLocalAPI('setMaxTimeout', { maxTimeout }),
setOutputFormat: (outputFormat) => queryLocalAPI('setOutputFormat', { outputFormat }),
setOutputQuality: (outputQuality) => queryLocalAPI('setOutputQuality', { outputQuality }),
modelSearch: (modelQuery = "", modelArch = "all", modelType = "base", modelCat = "checkpoint", condtioning = "") =>
queryLocalAPI('modelSearch', { modelQuery, modelArch, modelType, modelCat, condtioning })
};
function captionNodeHandler(msgEvent) {
const captionData = msgEvent.detail;
@@ -92,7 +68,7 @@ async function promptEnhanceHandler(e) {
notifyUser("Prompt Must Not Exceed 300 Characters!", "error", "Runware Prompt Enhancer");
return;
}
const enhanceResults = await enhancePrompt(userPrompt);
const enhanceResults = await runwareLocalAPI.enhancePrompt(userPrompt);
if(enhanceResults.success) {
e.target.value = enhanceResults.enhancedPrompt;
} else {
@@ -116,32 +92,61 @@ async function APIKeyHandler(apiManagerNode) {
if(apiKey.length < 30) {
notifyUser("Invalid API Key Set, Please Try Again!", "error", "Runware API Manager");
return;
}
const setAPIKeyResults = await runwareLocalAPI.setAPIKey(apiKey);
if(setAPIKeyResults.success) {
remNode(apiManagerNode);
notifyUser("API Key Set Successfully!", "success", "Runware API Manager");
} 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");
}
notifyUser(setAPIKeyResults.error, "error", "Runware API Manager");
}
});
} else if(widget.name === "Max Timeout") {
if(lastTimeout) widget.value = lastTimeout;
appendWidgetCB(widget, async function(...args) {
const maxTimeout = args[0];
if(maxTimeout < 5 || maxTimeout > 99) {
notifyUser("Invalid Timeout Value, Timeout must be between 5 and 99 seconds!", "error", "Runware API Manager");
const maxTimeout = parseInt(args[0]);
if(isNaN(maxTimeout) || maxTimeout < TIMEOUT_RANGE.min || maxTimeout > TIMEOUT_RANGE.max) {
notifyUser(`Invalid Timeout Value! Must be between ${TIMEOUT_RANGE.min} and ${TIMEOUT_RANGE.max} seconds.`, "error", "Runware API Manager");
widget.value = lastTimeout || TIMEOUT_RANGE.default;
return;
}
const setTimeoutResults = await runwareLocalAPI.setTimeout(maxTimeout);
if(setTimeoutResults.success) {
notifyUser("Timeout Set Successfully!", "success", "Runware API Manager");
lastTimeout = maxTimeout;
} else {
const setTimeoutResults = await setTimeout(maxTimeout);
if(setTimeoutResults.success) {
remNode(apiManagerNode);
notifyUser("Timeout Set Successfully!", "success", "Runware API Manager");
lastTimeout = maxTimeout;
} else {
notifyUser(setTimeoutResults.error, "error", "Runware API Manager");
}
widget.value = lastTimeout || TIMEOUT_RANGE.default;
notifyUser(setTimeoutResults.error, "error", "Runware API Manager");
}
});
} else if(widget.name === "Image Output Format") {
if(lastOutputFormat) widget.value = lastOutputFormat;
appendWidgetCB(widget, async function(...args) {
const outputFormat = args[0].trim().toUpperCase();
const setFormatResults = await runwareLocalAPI.setOutputFormat(outputFormat);
if(setFormatResults.success) {
notifyUser("Default Image Output Format Set Successfully!", "success", "Runware API Manager");
lastOutputFormat = outputFormat;
} else {
notifyUser(setFormatResults.error, "error", "Runware API Manager");
}
});
} else if(widget.name === "Image Output Quality") {
if(lastOutputQuality) widget.value = lastOutputQuality;
appendWidgetCB(widget, async function(...args) {
const outputQuality = parseInt(args[0]);
if(isNaN(outputQuality) || outputQuality < OUTPUT_QUALITY_RANGE.min || outputQuality > OUTPUT_QUALITY_RANGE.max) {
notifyUser(`Invalid Quality Value! Must be between ${OUTPUT_QUALITY_RANGE.min} and ${OUTPUT_QUALITY_RANGE.max}.`, "error", "Runware API Manager");
widget.value = lastOutputQuality || OUTPUT_QUALITY_RANGE.default;
return;
}
const setQualityResults = await runwareLocalAPI.setOutputQuality(outputQuality);
if(setQualityResults.success) {
notifyUser("Default Image Output Quality Set Successfully!", "success", "Runware API Manager");
lastOutputQuality = outputQuality;
} else {
widget.value = lastOutputQuality || OUTPUT_QUALITY_RANGE.default;
notifyUser(setQualityResults.error, "error", "Runware API Manager");
}
});
}
@@ -197,6 +202,7 @@ function handleCustomErrors(errObj) {
dialog.element.addEventListener('close', () => {
openDialog = false;
});
const runwareAPIForm = document.getElementById("runwareAPIKey");
const apiKeyInput = document.getElementById("apiKey");
const getAPIKeyBTN = document.getElementById("getAPIKeyBTN");
@@ -217,7 +223,7 @@ function handleCustomErrors(errObj) {
// notifyUser("Invalid API Key Set, Please Try Again!", "error", "Runware API Manager");
// return;
} else {
const setAPIKeyResults = await setAPIKey(apiKey);
const setAPIKeyResults = await runwareLocalAPI.setAPIKey(apiKey);
if(setAPIKeyResults.success) {
dialog.close();
openDialog = false;
@@ -242,8 +248,8 @@ function appendWidgetCB(node, newCB) {
const oldNodeCB = node.callback;
if(typeof oldNodeCB === "function") {
node.callback = function(...args) {
newCB?.apply(this, args);
oldNodeCB?.apply(this, args);
newCB?.apply(this, args);
}
} else {
node.callback = newCB;
@@ -358,11 +364,11 @@ async function searchNodeHandler(searchNode, searchInputWidget) {
let modelSearchResults = null;
if(isControlNet) {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, "", "controlnet", modelTypeValue);
modelSearchResults = await runwareLocalAPI.modelSearch(searchQuery, modelArchValue, "", "controlnet", modelTypeValue);
} else if(isLora || isEmbedding || isVAE) {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, "", modelTypeValue);
modelSearchResults = await runwareLocalAPI.modelSearch(searchQuery, modelArchValue, "", modelTypeValue);
} else {
modelSearchResults = await modelSearch(searchQuery, modelArchValue, modelTypeValue);
modelSearchResults = await runwareLocalAPI.modelSearch(searchQuery, modelArchValue, modelTypeValue);
}
if(modelSearchResults.success) {
@@ -524,13 +530,11 @@ async function searchNodeHandler(searchNode, searchInputWidget) {
}
export {
enhancePrompt,
modelSearch,
notifyUser,
promptEnhanceHandler,
syncDimensionsNodeHandler,
searchNodeHandler,
captionNodeHandler,
handleCustomErrors,
APIKeyHandler
APIKeyHandler,
};
+12 -2
View File
@@ -9,14 +9,24 @@ class apiManager:
"tooltip": "Define Or Change Your Runware API Key To Start Using Runware Image Inference Services.\n\n(e.g; BcVr1JVQM8FGzrRwb3GsbCq5ww1QwabV)\n\nIf You Don't Have One, You Can Get It From: https://my.runware.ai/keys",
}),
"Max Timeout": ("INT", {
"tooltip": "Define Or Change Runware API Timeout In Seconds.\n\n(e.g; 90 Seconds).",
"tooltip": "Change Runware API Request Timeout In Seconds.\n\n(e.g; 90 Seconds).",
"min": 5,
"max": 99,
"default": int(rwUtils.getTimeout()),
}),
"Image Output Quality": ("INT", {
"tooltip": "Sets the compression quality of the output image. Higher values preserve more quality but increase file size, lower values reduce file size but decrease quality.",
"min": 20,
"max": 99,
"default": int(rwUtils.getOutputQuality()),
}),
"Image Output Format": (["WEBP", "PNG", "JPEG"], {
"tooltip": "Change the Default Image Output Format.",
"default": rwUtils.getOutputFormat(),
}),
},
}
DESCRIPTION = "API Managers is a Runware Utility That helps you define or change your API Key or Session Timeout From The ComfyUI Interface Without having to adjust the env file locally."
DESCRIPTION = "API Managers is a Runware Utility That helps you define or change your API Key, Session Timeout, or Image Output Format & Quality From The ComfyUI Interface Without having to adjust the env file locally."
CATEGORY = "Runware"
RETURN_TYPES = ()
+1
View File
@@ -74,6 +74,7 @@ class bgremoval:
"returnOnlyMask": returnOnlyMask,
"alphaMatting": alphaMatting,
"outputFormat": outputFormat,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"outputType": "base64Data",
}
]
+24
View File
@@ -34,6 +34,30 @@ async def setMaxTimeout(reqPayload):
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/setOutputFormat')
async def setOutputFormat(reqPayload):
reqData = await reqPayload.json()
outputFormat = reqData.get('outputFormat', 'WEBP')
if outputFormat not in ['WEBP', 'PNG', 'JPEG']:
return web.json_response({'success': False, 'error': 'Invalid Output Format!'})
try:
rwUtils.setOutputFormat(outputFormat)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/setOutputQuality')
async def setOutputQuality(reqPayload):
reqData = await reqPayload.json()
outputQuality = reqData.get('outputQuality', 95)
if not isinstance(outputQuality, int) or outputQuality < 20 or outputQuality > 99:
return web.json_response({'success': False, 'error': 'Invalid Output Quality!'})
try:
rwUtils.setOutputQuality(outputQuality)
except Exception as e:
return web.json_response({'success': False, 'error': str(e)})
return web.json_response({'success': True})
@routes.post('/promptEnhance')
async def promptEnhance(reqPayload):
reqData = await reqPayload.json()
+2 -1
View File
@@ -96,7 +96,8 @@ class controlNetPreprocessor:
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"preProcessorType": preProcessorType,
"outputFormat": "WEBP",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"outputType": "base64Data",
}
]
+2 -3
View File
@@ -122,7 +122,6 @@ class txt2img:
"VAE": ("RUNWAREVAE", {
"tooltip": "Connect a Runware VAE Node to help the model generate images that align with the desired structure.",
}),
**rwUtils.RUNWARE_OUTPUT_FOMRATS,
}
}
@@ -164,7 +163,6 @@ class txt2img:
cfgScale = kwargs.get("cfgScale", 6.5)
seed = kwargs.get("seed")
batchSize = kwargs.get("batchSize", 1)
outputFormat = kwargs.get("outputFormat", "WEBP")
if (maskImage is not None and seedImage is None):
raise Exception("Mask Image Requires Seed Image To Be Provided!")
@@ -182,8 +180,9 @@ class txt2img:
"scheduler": scheduler,
"clipSkip": clipSkip,
"seed": seed,
"outputFormat": outputFormat,
"outputType": "base64Data",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"numberResults": batchSize,
}
]
+2 -3
View File
@@ -42,7 +42,6 @@ class imageMasking:
"min": 0,
"max": 20,
}),
**rwUtils.RUNWARE_OUTPUT_FOMRATS,
},
}
@@ -60,7 +59,6 @@ class imageMasking:
maxDetections = kwargs.get("Max Detections")
maskPadding = kwargs.get("Mask Padding")
maskBlur = kwargs.get("Mask Blur")
outputFormat = kwargs.get("outputFormat", "WEBP")
model_mapping = {
"face_yolov8n": "runware:35@1",
@@ -89,8 +87,9 @@ class imageMasking:
"model": detectionModel,
"confidence": confidence,
"maxDetections": maxDetections,
"outputFormat": outputFormat,
"outputType": "base64Data",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
}
]
+2 -3
View File
@@ -96,7 +96,6 @@ class photoMaker:
"min": 1,
"max": 10,
}),
**rwUtils.RUNWARE_OUTPUT_FOMRATS,
},
"optional": {
"Image 2": ("IMAGE", {
@@ -146,7 +145,6 @@ class photoMaker:
clipSkip = kwargs.get("clipSkip", 0)
seed = kwargs.get("seed")
batchSize = kwargs.get("batchSize", 1)
outputFormat = kwargs.get("outputFormat", "WEBP")
imageList = [rwUtils.convertTensor2IMG(image1)]
if (image2 is not None):
@@ -173,8 +171,9 @@ class photoMaker:
"CFGScale": cfgScale,
"clipSkip": clipSkip,
"numberResults": batchSize,
"outputFormat": outputFormat,
"outputType": "base64Data",
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
}
]
+2 -3
View File
@@ -14,7 +14,6 @@ class upscaler:
"min": 2,
"max": 4,
}),
**rwUtils.RUNWARE_OUTPUT_FOMRATS,
},
}
@@ -26,7 +25,6 @@ class upscaler:
def upscale(self, **kwargs):
image = kwargs.get("Image")
upscaleFactor = kwargs.get("upscaleFactor", 2)
outputFormat = kwargs.get("outputFormat", "WEBP")
genConfig = [
{
@@ -34,7 +32,8 @@ class upscaler:
"taskUUID": rwUtils.genRandUUID(),
"inputImage": rwUtils.convertTensor2IMG(image),
"upscaleFactor": upscaleFactor,
"outputFormat": outputFormat,
"outputFormat": rwUtils.OUTPUT_FORMAT,
"outputQuality": rwUtils.OUTPUT_QUALITY,
"outputType": "base64Data",
}
]
+36 -7
View File
@@ -15,13 +15,6 @@ import io
load_dotenv()
RUNWARE_OUTPUT_FOMRATS = {
"outputFormat": (["WEBP", "PNG", "JPG"], {
"default": "WEBP",
"tooltip": "Choose the output image format."
})
}
RUNWARE_REMBG_OUTPUT_FORMATS = {
"outputFormat": (["WEBP", "PNG"], {
"default": "WEBP",
@@ -50,8 +43,28 @@ def getTimeout():
os.environ["RUNWARE_TIMEOUT"] = str(timeout)
return timeout
def getOutputQuality():
output_quality = os.getenv("RUNWARE_OUTPUT_QUALITY")
if output_quality and isinstance(output_quality, str) and output_quality.isdigit():
return int(output_quality)
else:
output_quality = 95
os.environ["RUNWARE_OUTPUT_QUALITY"] = str(output_quality)
return output_quality
def getOutputFormat():
output_format = os.getenv("RUNWARE_OUTPUT_FORMAT")
if output_format and isinstance(output_format, str):
return output_format
else:
output_format = "WEBP"
os.environ["RUNWARE_OUTPUT_FORMAT"] = output_format
return output_format
SESSION_TIMEOUT = getTimeout()
RUNWARE_API_KEY = getAPIKey()
OUTPUT_FORMAT = getOutputFormat()
OUTPUT_QUALITY = getOutputQuality()
def setEnvKey(keyName, keyValue):
comfyNodeRoot = Path(__file__).parent.parent.parent
@@ -90,6 +103,22 @@ def setTimeout(timeout: int):
os.environ["RUNWARE_TIMEOUT"] = str(timeout)
return True
def setOutputFormat(format: str):
envSetRes = setEnvKey("RUNWARE_OUTPUT_FORMAT", format)
if envSetRes:
global OUTPUT_FORMAT
OUTPUT_FORMAT = format
os.environ["RUNWARE_OUTPUT_FORMAT"] = format
return True
def setOutputQuality(quality: int):
envSetRes = setEnvKey("RUNWARE_OUTPUT_QUALITY", str(quality))
if envSetRes:
global OUTPUT_QUALITY
OUTPUT_QUALITY = quality
os.environ["RUNWARE_OUTPUT_QUALITY"] = str(quality)
return True
def genRandSeed(minSeed = 1000, maxSeed = 9223372036854776000):
return random.randint(minSeed, maxSeed)