initial commit

This commit is contained in:
2025-07-30 17:38:50 +08:00
commit 4435a00dec
4 changed files with 473 additions and 0 deletions

234
__init__.py Normal file
View File

@@ -0,0 +1,234 @@
import os
import json
import re
import urllib
import aiohttp
from aiohttp import web
from server import PromptServer
# 获取当前插件的目录
NODE_DIR = os.path.dirname(os.path.abspath(__file__))
CONFIG_FILE = os.path.join(NODE_DIR, "config.json")
# 默认配置
DEFAULT_CONFIG = {
"api_url": ""
}
def load_config():
"""加载配置文件,如果文件不存在则创建"""
if not os.path.exists(CONFIG_FILE):
with open(CONFIG_FILE, 'w') as f:
json.dump(DEFAULT_CONFIG, f)
return DEFAULT_CONFIG
with open(CONFIG_FILE, 'r') as f:
return json.load(f)
def save_config(config_data):
"""保存配置到文件"""
with open(CONFIG_FILE, 'w') as f:
json.dump(config_data, f, indent=4)
# 创建一个虚拟节点类虽然它不会出现在图表中但这是ComfyUI加载自定义节点的标准方式
class WorkflowPublisherNode:
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
# 这个节点实际上是UI扩展不处理任何输入输出但需要定义这些方法
return {
"required": {},
}
RETURN_TYPES = ()
FUNCTION = "do_nothing"
OUTPUT_NODE = True
CATEGORY = "utilities"
def do_nothing(self):
return ()
# -----------------
# API 端点定义
# -----------------
# 添加自定义API路由
@PromptServer.instance.routes.get("/publisher/settings")
async def get_publisher_settings(request):
"""获取发布器设置"""
config = load_config()
return web.json_response(config)
@PromptServer.instance.routes.post("/publisher/settings")
async def save_publisher_settings(request):
"""保存发布器设置"""
try:
data = await request.json()
api_url = data.get("api_url", "")
config = load_config()
config["api_url"] = api_url
save_config(config)
return web.json_response({"status": "success", "message": "Settings saved"})
except Exception as e:
return web.json_response({"status": "error", "message": str(e)}, status=500)
@PromptServer.instance.routes.post("/publisher/publish")
async def publish_workflow_handler(request):
"""处理工作流发布请求"""
try:
data = await request.json()
workflow = data.get("workflow")
workflow_name = data.get("name")
if not workflow or not workflow_name:
return web.json_response({"status": "error", "message": "Missing workflow data or name"}, status=400)
config = load_config()
target_api_url = config.get("api_url")
if not target_api_url:
return web.json_response({"status": "error", "message": "API URL not configured"}, status=400)
# 准备要发送到目标API的数据
payload = {
"name": workflow_name,
"workflow": workflow
}
headers = {'Content-Type': 'application/json'}
# 使用 aiohttp 发送异步请求
async with aiohttp.ClientSession() as session:
async with session.post(target_api_url, json=payload, headers=headers) as response:
response_text = await response.text()
if response.status == 200:
return web.json_response(
{"status": "success", "message": "Workflow published successfully!", "details": response_text})
else:
return web.json_response({
"status": "error",
"message": f"Failed to publish workflow. Target API returned status {response.status}",
"details": response_text
}, status=500)
except Exception as e:
import traceback
traceback.print_exc()
return web.json_response({"status": "error", "message": str(e)}, status=500)
@PromptServer.instance.routes.get("/publisher/workflows")
async def get_workflows_from_server(request):
"""从目标服务器获取工作流列表,并按基础名称进行分组"""
try:
config = load_config()
target_api_url = config.get("api_url")
if not target_api_url:
return web.json_response({"status": "error", "message": "API URL not configured"}, status=400)
async with aiohttp.ClientSession() as session:
async with session.get(target_api_url) as response:
if response.status != 200:
response_text = await response.text()
return web.json_response({
"status": "error",
"message": f"Failed to fetch workflows. Target API returned status {response.status}",
"details": response_text
}, status=500)
workflows = await response.json()
# --- [核心逻辑] 对工作流进行分组和版本化 ---
grouped_workflows = {}
# 正则表达式匹配 '任意字符 [YYYYMMDDHHMMSS]' 格式
version_pattern = re.compile(r"^(.*) \[(20\d{12})\]$")
for wf in workflows:
match = version_pattern.match(wf.get("name", ""))
if match:
base_name = match.group(1).strip()
version = match.group(2)
else:
# 对于没有版本号的旧工作流,将整个名称作为基础名称
base_name = wf.get("name", "Unnamed Workflow")
version = "N/A" # 无版本信息
if base_name not in grouped_workflows:
grouped_workflows[base_name] = []
grouped_workflows[base_name].append({
"version": version,
"workflow": wf.get("workflow")
})
# 对每个工作流的版本进行降序排序(最新版本在前)
for base_name in grouped_workflows:
grouped_workflows[base_name].sort(key=lambda x: x['version'], reverse=True)
return web.json_response(grouped_workflows)
except Exception as e:
import traceback
traceback.print_exc()
return web.json_response({"status": "error", "message": str(e)}, status=500)
@PromptServer.instance.routes.post("/publisher/workflow/delete")
async def delete_workflow_version(request):
"""接收前端的删除请求并将其转发到目标API服务器"""
try:
data = await request.json()
workflow_name = data.get("name")
if not workflow_name:
return web.json_response({"status": "error", "message": "Missing workflow name"}, status=400)
config = load_config()
target_api_url = config.get("api_url")
if not target_api_url:
return web.json_response({"status": "error", "message": "API URL not configured"}, status=400)
# 构建目标URL需要对工作流名称进行URL编码
delete_url = f"{target_api_url}/{urllib.parse.quote(workflow_name)}"
async with aiohttp.ClientSession() as session:
async with session.delete(delete_url) as response:
if response.status == 200:
return web.json_response({"status": "success", "message": "Workflow version deleted"})
else:
details = await response.text()
return web.json_response({
"status": "error",
"message": f"Target API failed to delete. Status: {response.status}",
"details": details
}, status=response.status)
except Exception as e:
return web.json_response({"status": "error", "message": str(e)}, status=500)
# -----------------
# ComfyUI 注册
# -----------------
# 告诉 ComfyUI 我们有一个包含JS文件的web目录
WEB_DIRECTORY = "js"
# 节点映射
NODE_CLASS_MAPPINGS = {
# "WorkflowPublisher": WorkflowPublisherNode
}
# 节点显示名称映射
NODE_DISPLAY_NAME_MAPPINGS = {
# "WorkflowPublisher": "Workflow Publisher (UI)"
}
print("✅ Loaded Workflow Publisher Node")