Files
xiaoli-cli/operit_py/beeimg_image_uploader.py
T
shuiyu1123 dc77dfd304 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 专属功能已降级处理(保留接口,返回不支持提示)
2026-06-23 12:27:18 +00:00

177 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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