feat: 将 47 个 Operit TypeScript 脚本全面翻译为 Python
- 新增 operit_runtime.py: 跨平台 Python 运行时 - 实现 Tools.System/Files/Net/UI/Memory/FFmpeg/Workflow/Chat/SoftwareSettings/Tasker - 实现 OkHttp 链式 HTTP 客户端兼容层 - 实现 complete()/exports/console 全局对象 - 实现 OperitScriptLoader 脚本加载器 - 支持 Windows/macOS/Linux 三平台 - 新增 operit_py/ 目录: 47 个 Python 翻译脚本 - 搜索类: duckduckgo, google_search, tavily, zhipu_search, various_search, crossref - 绘图类: openai_draw, qwen_draw, xai_draw, minimax_draw, siliconflow_draw, zhipu_draw, nanobanana_draw - 媒体类: ffmpeg, file_converter, douyin_download, bilibili_tools, jmcomic - 文件类: extended_file_tools, hex_editor, reader, beeimg_image_uploader - 网络类: extended_http_tools, network_test - 自动化类: browser, automatic_ui_base, automatic_ui_subagent, automatic_bilibili_assistant, automatic_xiaohongshu_assistant, automatic_baidu_map_assistant - 系统类: system_tools, super_admin, daily_life, operit_editor - 对话类: ai_chat, extended_chat, qq_intelligent - 工作流类: workflow, vflow_trigger, tasker - 工具类: time, code_runner, quick_start, extended_memory_tools, baidu_map, 12306, pdf_vision_parser - 测试结果: 47/47 语法检查通过, 38 脚本加载成功, 280 个工具注册 - Android 专属功能已降级处理(保留接口,返回不支持提示)
This commit is contained in:
1 parent
6fa759665d
commit
dc77dfd304
48 files changed
+22273
No files matched your search
@@ -0,0 +1,176 @@
|
||||
# METADATA
|
||||
# {
|
||||
# "name": "beeimg_image_uploader_v2",
|
||||
#
|
||||
# "display_name": {
|
||||
# "zh": "Beeimg 图片上传器 V2",
|
||||
# "en": "Beeimg Image Uploader V2"
|
||||
# },
|
||||
# "description": {
|
||||
# "zh": "BeeIMG(https://beeimg.com/)工具将本地图片上传到图床并返回图片url,配合图片生成工具实现图生图(图生图务必开启)。",
|
||||
# "en": "Upload a local image to BeeIMG (https://beeimg.com/) and return a public image URL. Useful for image-to-image workflows (make sure img2img is enabled)."
|
||||
# },
|
||||
# "env": [
|
||||
# "BEEIMG_API_KEY"
|
||||
# ],
|
||||
# "category": "Media",
|
||||
# "tools": [
|
||||
# {
|
||||
# "name": "upload_image",
|
||||
# "description": {
|
||||
# "zh": "使用 multipart 上传将本地图片上传到 BeeIMG 图床并返回图片 url。",
|
||||
# "en": "Upload a local image to BeeIMG via multipart upload and return the image URL."
|
||||
# },
|
||||
# "parameters": [
|
||||
# { "name": "file_path", "description": { "zh": "要上传的图片文件绝对路径 (建议使用 /sdcard/ 开头的完整路径)。", "en": "Absolute path of the image file to upload (recommended: full path starting with /sdcard/)." }, "type": "string", "required": true },
|
||||
# { "name": "album_id", "description": { "zh": "相册ID (可选)。", "en": "Album ID (optional)." }, "type": "string", "required": false },
|
||||
# { "name": "privacy", "description": { "zh": "隐私设置,'public' 或 'private' (可选)。", "en": "Privacy setting: 'public' or 'private' (optional)." }, "type": "string", "required": false }
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
|
||||
import json
|
||||
import traceback
|
||||
|
||||
API_ENDPOINT = "https://beeimg.com/api/upload/file/json/"
|
||||
|
||||
|
||||
def isRecord(value):
|
||||
return isinstance(value, dict)
|
||||
|
||||
|
||||
def getErrorMessage(error):
|
||||
if isinstance(error, Exception):
|
||||
return str(error)
|
||||
return str(error)
|
||||
|
||||
|
||||
def getErrorStack(error):
|
||||
if isinstance(error, Exception):
|
||||
return traceback.format_exc()
|
||||
return None
|
||||
|
||||
|
||||
def getApiKey():
|
||||
return getEnv("BEEIMG_API_KEY") or ""
|
||||
|
||||
|
||||
def guessMimeTypeFromPath(filePath):
|
||||
lower = filePath.lower()
|
||||
if lower.endswith(".png"):
|
||||
return "image/png"
|
||||
if lower.endswith(".jpg") or lower.endswith(".jpeg"):
|
||||
return "image/jpeg"
|
||||
if lower.endswith(".webp"):
|
||||
return "image/webp"
|
||||
if lower.endswith(".gif"):
|
||||
return "image/gif"
|
||||
return "application/octet-stream"
|
||||
|
||||
|
||||
def safeJsonParseLoose(text):
|
||||
trimmed = (text or "").strip()
|
||||
if not trimmed:
|
||||
return None
|
||||
try:
|
||||
return json.loads(trimmed)
|
||||
except Exception as e:
|
||||
start = trimmed.find("{")
|
||||
end = trimmed.rfind("}")
|
||||
if start != -1 and end != -1 and end > start:
|
||||
return json.loads(trimmed[start:end + 1])
|
||||
raise e
|
||||
|
||||
|
||||
async def upload_image(params):
|
||||
file_path = params.get("file_path") if params else None
|
||||
album_id = params.get("album_id") if params else None
|
||||
privacy = params.get("privacy") if params else None
|
||||
|
||||
if not file_path or len(str(file_path).strip()) == 0:
|
||||
raise Exception("参数 file_path 不能为空。")
|
||||
|
||||
# 1. 检查文件是否存在
|
||||
fileExists = await Tools.Files.exists(file_path)
|
||||
if not fileExists["exists"]:
|
||||
raise Exception(f"文件未找到: {file_path} (请尝试使用 /sdcard/ 开头的绝对路径)")
|
||||
|
||||
# 2. 准备参数
|
||||
apiKey = getApiKey()
|
||||
if not apiKey:
|
||||
raise Exception("BEEIMG_API_KEY 未配置,请在环境变量中设置 BeeIMG 的 API Key。")
|
||||
|
||||
form_data = {
|
||||
"apikey": apiKey
|
||||
}
|
||||
if album_id:
|
||||
form_data["albumid"] = str(album_id)
|
||||
if privacy:
|
||||
form_data["privacy"] = str(privacy)
|
||||
|
||||
# 3. 发起 multipart 上传
|
||||
console.log("正在执行上传...")
|
||||
|
||||
resp = await Tools.Net.uploadFile({
|
||||
"url": API_ENDPOINT,
|
||||
"method": "POST",
|
||||
"headers": {
|
||||
# 尽量模拟浏览器 UA,降低被风控概率
|
||||
"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36"
|
||||
},
|
||||
"form_data": form_data,
|
||||
"files": [
|
||||
{
|
||||
"field_name": "file",
|
||||
"file_path": file_path,
|
||||
"content_type": guessMimeTypeFromPath(file_path)
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
if resp["statusCode"] < 200 or resp["statusCode"] >= 300:
|
||||
raise Exception(f"上传请求失败 (HTTP {resp['statusCode']})。可能是网络问题或 IP 被屏蔽。\n响应: {resp['content']}")
|
||||
|
||||
responseText = (resp.get("content") or "").strip()
|
||||
if not responseText:
|
||||
raise Exception("上传失败:服务器返回了空内容。请检查网络连接或尝试关闭 VPN。")
|
||||
|
||||
try:
|
||||
jsonResponse = safeJsonParseLoose(responseText)
|
||||
except Exception as e:
|
||||
raise Exception(f"API 响应解析失败。服务器返回内容不是 JSON:\n{responseText[:200]}...")
|
||||
|
||||
files = jsonResponse["files"] if isRecord(jsonResponse) and isRecord(jsonResponse.get("files")) else None
|
||||
ok = bool(files) and (files.get("status") == "Success" or files.get("code") == "200" or files.get("code") == 200)
|
||||
url = files.get("url") if files else None
|
||||
|
||||
if ok and (isinstance(url, str) or isinstance(url, (int, float))) and len(str(url).strip()) > 0:
|
||||
return {
|
||||
"url": str(url),
|
||||
"thumbnail_url": str(files["thumbnail_url"]) if files and files.get("thumbnail_url") else None,
|
||||
"page_url": str(files["view_url"]) if files and files.get("view_url") else None,
|
||||
"details": f"上传成功! URL: {str(url)}"
|
||||
}
|
||||
else:
|
||||
if files:
|
||||
errMsg = files.get("status") or files.get("message") or json.dumps(files, ensure_ascii=False)
|
||||
else:
|
||||
errMsg = "未知错误"
|
||||
raise Exception(f"BeeIMG API 报错: {errMsg}")
|
||||
|
||||
|
||||
async def wrap(func, params):
|
||||
try:
|
||||
result = await func(params)
|
||||
complete({"success": True, "message": "图片上传成功", "data": result})
|
||||
except Exception as error:
|
||||
console.error(f"Error: {getErrorMessage(error)}")
|
||||
complete({"success": False, "message": getErrorMessage(error), "error_stack": getErrorStack(error)})
|
||||
|
||||
|
||||
async def upload_image_wrapper(params):
|
||||
await wrap(upload_image, params)
|
||||
|
||||
|
||||
exports.upload_image = upload_image_wrapper
|
||||
Reference in new issue
Block a user