Files
jinchanz-ComfyUI-ADIC/module/node.py
T
2025-06-12 19:01:07 +08:00

402 lines
15 KiB
Python

import json
import requests
from typing import List, Dict, Any
class ADIC_COMMON_API:
"""ADIC Common API"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"params": ("STRING", {"forceInput": True}),
"app_name": ("STRING", {"default": "NHCI"}),
"api_key": ("STRING", {"default": ""}),
"api_endpoint": ("STRING", {"default": "/open/api/v1/ai/imageTranslate"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("response",)
FUNCTION = "translate"
OUTPUT_NODE = True
CATEGORY = "Malette"
def translate(self, params, app_name, api_key, api_endpoint):
try:
# 验证必填参数
if not api_key or api_key.strip() == "":
raise ValueError("API密钥不能为空")
if not api_endpoint or api_endpoint.strip() == "":
raise ValueError("API端点不能为空")
# 解析params参数
try:
if isinstance(params, str):
request_body = json.loads(params)
else:
request_body = params
except json.JSONDecodeError as e:
raise ValueError(f"params参数JSON解析失败: {str(e)}")
# 设置请求头
headers = {
"Content-Type": "application/json",
"Accept": "*/*",
"Authorization": f"Bearer {api_key.strip()}",
"X-App-Name": app_name.strip()
}
print(f"[ADIC_COMMON_API] 请求参数: {json.dumps(request_body, ensure_ascii=False)}")
# 发送POST请求
response = requests.post(
api_endpoint.strip(),
headers=headers,
json=request_body,
timeout=600,
verify=False # 忽略SSL证书验证
)
# 检查响应状态
response.raise_for_status()
# 解析响应
response_data = response.json()
# 检查业务状态
if not response_data.get("success", False):
error_msg = response_data.get("message", "API调用失败")
print(f"[ADIC_COMMON_API] 业务错误: {error_msg}")
return (json.dumps({
"error": error_msg,
"code": response_data.get("code", -1),
"debugInfo": response_data.get("debugInfo")
}, ensure_ascii=False, indent=2),)
# 返回成功结果
result_json = json.dumps(response_data, ensure_ascii=False, indent=2)
print(f"[ADIC_COMMON_API] 请求结果: {result_json}")
return (result_json,)
except requests.exceptions.RequestException as e:
error_msg = f"网络请求失败: {str(e)}"
print(f"[ADIC_COMMON_API] {error_msg}")
return (json.dumps({"error": error_msg}, ensure_ascii=False),)
except json.JSONDecodeError as e:
error_msg = f"JSON 解析失败: {str(e)}"
print(f"[ADIC_COMMON_API] {error_msg}")
return (json.dumps({"error": error_msg}, ensure_ascii=False),)
except Exception as e:
error_msg = f"未知错误: {str(e)}"
print(f"[ADIC_COMMON_API] {error_msg}")
return (json.dumps({"error": error_msg}, ensure_ascii=False),)
class ImageTranslateAPI:
"""阿里云图片翻译API节点"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"params": ("STRING", {"forceInput": True}),
"app_name": ("STRING", {"default": "NHCI"}),
"api_key": ("STRING", {"default": ""}),
"api_endpoint": ("STRING", {"default": "/open/api/v1/ai/imageTranslate"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("response",)
FUNCTION = "translate"
OUTPUT_NODE = True
CATEGORY = "Malette"
def translate(self, params, app_name, api_key, api_endpoint):
try:
# 验证必填参数
if not api_key or api_key.strip() == "":
raise ValueError("API密钥不能为空")
if not api_endpoint or api_endpoint.strip() == "":
raise ValueError("API端点不能为空")
# 解析params参数
try:
if isinstance(params, str):
request_body = json.loads(params)
else:
request_body = params
# 验证params格式
if not isinstance(request_body, dict) or "params" not in request_body:
raise ValueError("params参数格式错误,应该包含params数组")
params_array = request_body["params"]
if not isinstance(params_array, list):
raise ValueError("params.params 应该是一个数组")
if len(params_array) == 0:
raise ValueError("params数组不能为空")
# 验证每个参数项
for i, param in enumerate(params_array):
if not isinstance(param, dict):
raise ValueError(f"params[{i}] 应该是一个对象")
if "url" not in param or not param["url"]:
raise ValueError(f"params[{i}] 缺少必需的url字段")
url = param["url"].strip()
if not (url.startswith('http://') or url.startswith('https://')):
raise ValueError(f"params[{i}] url格式不正确: {url}")
except json.JSONDecodeError as e:
raise ValueError(f"params参数JSON解析失败: {str(e)}")
# 设置请求头
headers = {
"Content-Type": "application/json",
"Accept": "*/*",
"Authorization": f"Bearer {api_key.strip()}",
"X-App-Name": app_name.strip()
}
print(f"[ImageTranslateAPI] 发送翻译请求,图片数量: {len(params_array)}")
print(f"[ImageTranslateAPI] 请求参数: {json.dumps(request_body, ensure_ascii=False)}")
# 发送POST请求
response = requests.post(
api_endpoint.strip(),
headers=headers,
json=request_body,
timeout=600,
verify=False # 忽略SSL证书验证
)
# 检查响应状态
response.raise_for_status()
# 解析响应
response_data = response.json()
# 检查业务状态
if not response_data.get("success", False):
error_msg = response_data.get("message", "API调用失败")
print(f"[ImageTranslateAPI] 业务错误: {error_msg}")
return (json.dumps({
"error": error_msg,
"code": response_data.get("code", -1),
"debugInfo": response_data.get("debugInfo")
}, ensure_ascii=False, indent=2),)
# 返回成功结果
result_json = json.dumps(response_data, ensure_ascii=False, indent=2)
print(f"[ImageTranslateAPI] 翻译完成,处理了 {len(response_data.get('data', []))} 张图片")
print(f"[ImageTranslateAPI] 翻译结果: {result_json}")
return (result_json,)
except requests.exceptions.RequestException as e:
error_msg = f"网络请求失败: {str(e)}"
print(f"[ImageTranslateAPI] {error_msg}")
return (json.dumps({"error": error_msg}, ensure_ascii=False),)
except json.JSONDecodeError as e:
error_msg = f"JSON 解析失败: {str(e)}"
print(f"[ImageTranslateAPI] {error_msg}")
return (json.dumps({"error": error_msg}, ensure_ascii=False),)
except Exception as e:
error_msg = f"未知错误: {str(e)}"
print(f"[ImageTranslateAPI] {error_msg}")
return (json.dumps({"error": error_msg}, ensure_ascii=False),)
class ImageTranslateParamsBuilder:
"""图片翻译参数构建器节点"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"url1": ("STRING", {"default": ""}),
},
"optional": {
"source_language1": (["", "zh", "en", "tr"], {"default": ""}),
"target_language1": (["", "es", "fr", "pt", "ko", "en"], {"default": "en"}),
"url2": ("STRING", {"default": ""}),
"source_language2": (["", "zh", "en", "tr"], {"default": ""}),
"target_language2": (["", "es", "fr", "pt", "ko", "en"], {"default": "en"}),
"url3": ("STRING", {"default": ""}),
"source_language3": (["", "zh", "en", "tr"], {"default": ""}),
"target_language3": (["", "es", "fr", "pt", "ko", "en"], {"default": "en"}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("params_json",)
FUNCTION = "build"
OUTPUT_NODE = False
CATEGORY = "Malette"
def build(self, url1, source_language1="", target_language1="en",
url2="", source_language2="", target_language2="en",
url3="", source_language3="", target_language3="en"):
"""构建符合API要求的params JSON字符串"""
params_array = []
# 处理所有URL和对应的语言设置
url_configs = [
(url1, source_language1, target_language1),
(url2, source_language2, target_language2),
(url3, source_language3, target_language3),
]
for url, source_lang, target_lang in url_configs:
url = url.strip()
if not url:
continue
# 验证URL格式
if not (url.startswith('http://') or url.startswith('https://')):
print(f"[ImageTranslateParamsBuilder] 警告: URL格式不正确: {url}")
continue
param = {"url": url}
# 添加源语言(如果指定了)
if source_lang and source_lang.strip():
param["sourceLanguage"] = source_lang.strip()
# 添加目标语言(如果指定了)
if target_lang and target_lang.strip():
param["targetLanguage"] = target_lang.strip()
params_array.append(param)
if not params_array:
print("[ImageTranslateParamsBuilder] 警告: 没有有效的URL")
return (json.dumps({"params": []}),)
result = {"params": params_array}
result_json = json.dumps(result, ensure_ascii=False, indent=2)
print(f"[ImageTranslateParamsBuilder] 构建了 {len(params_array)} 个翻译参数")
return (result_json,)
class ImageTranslateResultExtractor:
"""图片翻译结果提取器节点"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"api_response": ("STRING", {"forceInput": True}),
},
"optional": {
"extract_type": (["all", "result_urls", "result_jsons", "source_info"], {"default": "result_urls"}),
"index": ("INT", {"default": 0, "min": 0, "max": 100}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("extracted_data",)
FUNCTION = "extract"
OUTPUT_NODE = False
CATEGORY = "Malette"
def extract(self, api_response, extract_type="result_urls", index=0):
"""从API响应中提取特定数据"""
try:
# 解析API响应
response_data = json.loads(api_response)
# 检查是否有错误
if "error" in response_data:
return (api_response,)
# 获取数据列表
data_list = response_data.get("data", [])
if not data_list:
return (json.dumps([], ensure_ascii=False),)
if extract_type == "all":
# 返回完整的数据列表
result = json.dumps(data_list, ensure_ascii=False, indent=2)
elif extract_type == "result_urls":
# 提取所有结果图片URL
urls = [item.get("resultUrl", "") for item in data_list if item.get("resultUrl")]
result = json.dumps(urls, ensure_ascii=False)
elif extract_type == "result_jsons":
# 提取所有结果JSON
jsons = [item.get("resultJson", "") for item in data_list if item.get("resultJson")]
result = json.dumps(jsons, ensure_ascii=False)
elif extract_type == "source_info":
# 提取源信息(URL和语言)
source_info = []
for item in data_list:
info = {
"url": item.get("url", ""),
"sourceLanguage": item.get("sourceLanguage", ""),
"targetLanguage": item.get("targetLanguage", "")
}
source_info.append(info)
result = json.dumps(source_info, ensure_ascii=False, indent=2)
else:
result = json.dumps(data_list, ensure_ascii=False, indent=2)
print(f"[ImageTranslateResultExtractor] 提取类型: {extract_type}, 数据数量: {len(data_list)}")
return (result,)
except json.JSONDecodeError as e:
error_msg = f"JSON 解析失败: {str(e)}"
print(f"[ImageTranslateResultExtractor] {error_msg}")
return (json.dumps({"error": error_msg}, ensure_ascii=False),)
except Exception as e:
error_msg = f"提取过程出错: {str(e)}"
print(f"[ImageTranslateResultExtractor] {error_msg}")
return (json.dumps({"error": error_msg}, ensure_ascii=False),)
# 节点映射
NODE_CLASS_MAPPINGS = {
"ImageTranslateAPI": ImageTranslateAPI,
"ImageTranslateParamsBuilder": ImageTranslateParamsBuilder,
"ImageTranslateResultExtractor": ImageTranslateResultExtractor,
"ADIC_COMMON_API": ADIC_COMMON_API
}
NODE_DISPLAY_NAME_MAPPINGS = {
"ImageTranslateAPI": "图片翻译 API",
"ImageTranslateParamsBuilder": "图片翻译参数构建器",
"ImageTranslateResultExtractor": "图片翻译结果提取器",
"ADIC_COMMON_API": "ADIC Common API"
}