From 6d16a2ff9b9e7129d13b6217be8e047901571a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E9=9B=AA=E5=B3=B0?= Date: Sat, 28 Sep 2024 16:01:55 +0800 Subject: [PATCH] top menu adaptation newui 1.3.x --- README.md | 10 ++++- easyapi/UtilNode.py | 80 +++++++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- static/js/workflows.js | 85 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 169 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1570fef..81599f7 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,9 @@ 针对api接口开发补充的一些自定义节点和功能。 转成base64的节点都是输出节点,websocket消息中会包含base64Images和base64Type属性(具体格式请查看ImageNode.py中的ImageToBase64Advanced类源代码,或者自己搭建简单流程运行在浏览器开发者工具-->网络中查看) + +Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带宽可能也会有瓶颈,条件允许可以把图片上传到OSS服务器得到URL,然后用LoadImageFromUrl加载,由于无相关OSS账号,上传OSS节点需自行编写,暂不支持。 + ## 安装 - 方式1:通过ComfyUI-Manager安装 - 方式2:在ComfyUI安装目录根目录下打开命令行终端,执行以下命令 @@ -60,8 +63,8 @@ | StringArea | 字符串文本框(多行输入区域) | | | ForEachOpen | 循环开始节点 | | | ForEachClose | 循环结束节点 | | - -Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带宽可能也会有瓶颈,条件允许可以把图片上传到OSS服务器得到URL,然后用LoadImageFromUrl加载,由于无相关OSS账号,上传OSS节点需自行编写,暂不支持。 +| LoadJsonStrToList | json字符串转换为对象列表 | | | +| GetValueFromJsonObj | 从对象中获取指定key的值 | | ### 示例 ![save api extended](docs/example_note.png) @@ -72,6 +75,9 @@ Tips: base64格式字符串比较长,会导致界面卡顿,接口请求带 ![save api extended](example/example_3.png) ## 更新记录 +### 2024-09-26 +- 新增节点:GetValueFromJsonObj、 LoadJsonStrToList + ### 2024-09-25 [示例](example/example_4.png) - 新增节点:ForEachOpen、 ForEachClose diff --git a/easyapi/UtilNode.py b/easyapi/UtilNode.py index 79d6849..a9f8915 100644 --- a/easyapi/UtilNode.py +++ b/easyapi/UtilNode.py @@ -1,3 +1,4 @@ +import simplejson import torch from comfy.model_patcher import ModelPatcher @@ -521,6 +522,79 @@ def execute(self, value): return (value,) +class ConvertTypeToAny: + @classmethod + def INPUT_TYPES(s): + return { + "required": {"any": (any_type, {"forceInput": True})}, + } + + RETURN_TYPES = (any_type,) + RETURN_NAMES = ("any",) + + FUNCTION = "execute" + + CATEGORY = "EasyApi/Utils" + + def execute(self, any): + return (any,) + + +class LoadJsonStrToList: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "json_str": ("STRING", {"default": "", "multiline": True}), + } + } + + RETURN_TYPES = ("LIST",) + CATEGORY = "EasyApi/Utils" + FUNCTION = "load_json" + + def load_json(self, json_str: str): + if len(json_str.strip()) == 0: + return ([{}],) + json = simplejson.loads(json_str) + if isinstance(json, list): + return (json,) + else: + return ([json],) + + +class GetValueFromJsonObj: + @classmethod + def INPUT_TYPES(s): + return { + "required": { + "json": (any_type, {"forceInput": True, "tooltip": "json对象(非数组类型)"}), + "key": ("STRING", {"default": ""}), + "to_type": (["default", "str", "int", "float", "bool"], {"default": "default"}), + }, + } + + RETURN_TYPES = (any_type,) + RETURN_NAMES = ("any",) + + FUNCTION = "execute" + + CATEGORY = "EasyApi/Utils" + + def execute(self, json, key, to_type): + if isinstance(json, dict) and key in json: + if to_type == "str": + return (str(json[key]),) + if to_type == "int": + return (int(json[key]),) + if to_type == "float": + return (float(json[key]),) + if to_type == "bool": + return (bool(json[key]),) + return (json[key],) + return (None,) + + NODE_CLASS_MAPPINGS = { "GetImageBatchSize": GetImageBatchSize, "JoinList": JoinList, @@ -542,6 +616,9 @@ def execute(self, value): "IndexOfList": IndexOfList, "IndexesOfList": IndexesOfList, "StringArea": StringArea, + "ConvertTypeToAny": ConvertTypeToAny, + "GetValueFromJsonObj": GetValueFromJsonObj, + "LoadJsonStrToList": LoadJsonStrToList, } # A dictionary that contains the friendly/humanly readable titles for the nodes @@ -566,4 +643,7 @@ def execute(self, value): "IndexOfList": "IndexOfList", "IndexesOfList": "IndexesOfList", "StringArea": "StringArea", + "ConvertTypeToAny": "ConvertTypeToAny", + "GetValueFromJsonObj": "GetValueFromJsonObj", + "LoadJsonStrToList": "LoadJsonStrToList", } diff --git a/pyproject.toml b/pyproject.toml index d564fdb..003e60c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "comfyui-easyapi-nodes" description = "Provides some features and nodes related to API calls." -version = "1.0.4" +version = "1.0.5" license = { file = "LICENSE" } dependencies = ["segment_anything", "simple_lama_inpainting", "insightface"] diff --git a/static/js/workflows.js b/static/js/workflows.js index 15ff4a7..b66c80b 100644 --- a/static/js/workflows.js +++ b/static/js/workflows.js @@ -14,6 +14,71 @@ import('/scripts/ui/components/splitButton.js').then(module => { // console.error('模块导入失败:', error); }); +/** + * v2是否大于v1 + *
+ * 1.0.0-alpha < 1.0.0-alpha.1 < 1.0.0-beta < 1.0.0-beta.2 < 1.0.0-beta.11 < 1.0.0-rc.1 < 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1 + *
+ * code from: https://segmentfault.com/a/1190000044959793 + * @param v1 + * @param v2 + * @returns {boolean} + */ +function compareVersion(v1, v2) { + function* getStepVersion(v) { + const matchReg = /(\.|\-)/; // 匹配到任意一项,则返回分段的版本号 + // 将先行版本号,转换成数字,直接比大小 + const specialVersionTransObj = { + alpha: -3, + beta: -2, + rc: -1, + }; + let tmp = ""; // 存储每段版本的值 + for (let i = 0; i <= v.length - 1; i++) { + let item = v[i]; + if (matchReg.test(item)) { + yield specialVersionTransObj[tmp] || tmp; + tmp = ""; + } else { + tmp += item; + } + } + + // 遍历至最后一项,没有匹配到.-也要返回结果 + if (tmp) { + yield specialVersionTransObj[tmp] || tmp; + } + } + + const v1Iterator = getStepVersion(v1); + const v2Iterator = getStepVersion(v2); + + let isEnd = false; + let isHighLevel = false; + // 每次遍历相当于,2个版本号分段间的比较 + while (!isEnd) { + const item1 = v1Iterator.next(); + const item2 = v2Iterator.next(); + item1.value = item1.value || 0; + item2.value = item2.value || 0; + + if (item1.done && item2.done) { + // 代表版本号一致 + isEnd = true; + } else { + if (Number(item2.value) > Number(item1.value)) { + // v2 版本大于 v1 + isHighLevel = true; + isEnd = true; + } else if (Number(item2.value) < Number(item1.value)) { + // v2 版本小于 v1 + isHighLevel = false; + isEnd = true; + } + } + } + return isHighLevel; +} const style = ` @@ -236,7 +301,7 @@ class EasyApiWorkflows { let copyButton = new ComfySplitButton( { primary: getCopyButton(), - mode: "hover", + mode: !!app.menu.saveButton ? "hover" : "click", position: "absolute", }, getCopyButton("Copy"), @@ -272,9 +337,13 @@ class EasyApiWorkflows { app, }), ); - app.menu.saveButton.element.after(copyButton.element); + if (!!app.menu.saveButton) { + app.menu.saveButton.element.after(copyButton.element); + } else { + // version>=1.3.0 + app.menu.actionsGroup.append(copyButton.element); + } - let oldItems = app.menu.saveButton.items; let exportEasyApiButton = new ComfyButton({ icon: "api", content: "Export to Base64 (API Format)", @@ -309,8 +378,14 @@ class EasyApiWorkflows { visibilitySetting: { id: "Comfy.DevMode", showValue: true }, app, }) - oldItems.push(exportEasyApiButton); - app.menu.saveButton.items = oldItems; + if (!!app.menu.saveButton) { + let oldItems = app.menu.saveButton.items; + oldItems.push(exportEasyApiButton); + app.menu.saveButton.items = oldItems; + } else { + // version>=1.3.0 + // app.menu.actionsGroup.append(copyButton.element); + } } const handleFile = app.handleFile;