tripocli v2.1.0:新增图片4K超分(img-upscale)、智能抠图(img-split)、图片删除与nanobanana免费模型别名

This commit is contained in:
Mrpzx committed 2026-10-13 20:56:21 +08:00
1 parent 671fe2f55a
commit b416a743f5
10 files changed
+206 -6

No files matched your search

+7 -1
View File
@@ -149,10 +149,16 @@ tripocli project <project_id> # 查看项目详情、模型直链与已重定
### 3. 生成命令
```bash
# 文生图(支持模型: nano-banana-2 / nano-banana-pro / gpt-image-2 / gpt-image-2.5 / midjourney)
# 文生图(免会员默认模型: nanobanana / nano-banana-2;会员模型: nano-banana-pro / gpt-image-2 / midjourney)
tripocli img -p "a stylized treasure chest, game asset" --amount 1 --scale 1:1 --resolution 2K --wait -o ./refs
tripocli img -p "anime knight" -m nanobanana --t-pose --wait -o ./knight.png
tripocli img --templates # 查看官方提示词模板
# 图片 4K 超分放大与智能抠图
tripocli img-upscale -a <asset_id> --wait -o ./4k.png # 4K 高清放大(对应网页 4K 按钮)
tripocli img-split -a <asset_id> --wait -o ./cutout.webp # 智能抠图透明背景(对应网页剪刀)
tripocli imgs --delete <asset_id> # 删除图片资产(对应网页垃圾桶)
# 单图转 4 视角参考图
tripocli imgmv -i ./refs/image_0.png --wait -o ./views
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "tripocli"
version = "2.0.0"
version = "2.1.0"
description = "Tripo AI 3D 生成与加工 CLI(面向 Agent 与开发者,纯标准库零依赖)"
readme = "README.md"
requires-python = ">=3.8"
+1 -1
View File
@@ -6,4 +6,4 @@ file. ``tripo/client.py`` holds the HTTP plumbing, ``tripo/config.py`` the
credentials, and ``tripo/cli.py`` wires the feature modules into an argparse CLI.
"""
__version__ = "2.0.0"
__version__ = "2.1.0"
+1 -1
View File
@@ -30,7 +30,7 @@ def _request(api_base, cookie, device_id=None, timeout=30):
"Accept": "application/json",
"Origin": ORIGIN,
"Referer": ORIGIN + "/",
"User-Agent": "tripocli/2.0.0",
"User-Agent": "tripocli/2.1.0",
"Cookie": cookie,
}
if device_id:
+1 -1
View File
@@ -26,7 +26,7 @@ RETRY_ATTEMPTS = 4
RETRY_BACKOFF = 1.5
ORIGIN = "https://studio.tripo3d.ai"
USER_AGENT = "tripocli/2.0.0 (Mozilla/5.0; AI Agent Client)"
USER_AGENT = "tripocli/2.1.0 (Mozilla/5.0; AI Agent Client)"
# Model formats the studio's "上传 3D 模型" importer accepts (UI: OBJ/FBX/STL/GLB ≤150MB).
MODEL_FORMATS = ("glb", "obj", "fbx", "stl")
+4
View File
@@ -20,7 +20,9 @@ from . import (
image_assets,
image_gen,
image_multiview,
image_split,
image_to_3d,
image_upscale,
import_model,
inspect_model,
mesh_edit,
@@ -51,6 +53,8 @@ MODULES = [
text_to_3d, # text
image_gen, # img (text to image)
image_assets, # imgs (list image assets)
image_upscale, # img-upscale (4K image upscale)
image_split, # img-split (auto cutout / background removal)
image_multiview, # imgmv (one image -> 4 views)
image_to_3d, # image
import_model, # import (upload own glb/obj/fbx/stl)
+8
View File
@@ -13,10 +13,18 @@ HELP = "列出我的图片资产(生成过的参考图)"
def add_arguments(parser):
parser.add_argument("--page", type=int, default=1, help="页码(默认 1)")
parser.add_argument("--size", type=int, default=8, help="每页数量(默认 8)")
parser.add_argument("--delete", help="删除指定的图片资产 asset_id(对应网页端垃圾桶按钮)")
def run(ctx):
args = ctx.args
if args.delete:
target_id = args.delete.strip()
body = {"items": [{"asset_id": target_id, "keys": []}]}
ctx.client.get_data("POST", "/v2/studio/image/batch_delete", body=body)
ctx.out.line(f"🗑️ 已成功删除图片资产: {target_id}")
return {"deleted": target_id}
data = ctx.client.get_data(
"POST", "/v2/studio/image/image_assets",
body={"page_num": args.page, "page_size": args.size},
+5 -1
View File
@@ -26,12 +26,16 @@ HELP = "文生图:用文本生成参考图(游戏资产流程的第一步)
# 界面上的模型名 → API 的 model_version(逐个在页面上选中并抓包确认)
IMAGE_MODELS = {
"nano-banana-2": "gemini_3.1_flash_image_preview",
"nanobanana": "gemini_3.1_flash_image_preview",
"nano-banana": "gemini_3.1_flash_image_preview",
"banana": "gemini_3.1_flash_image_preview",
"nano-banana-pro": "gemini_3_pro_image_preview",
"nanobanana-pro": "gemini_3_pro_image_preview",
"gpt-image-2": "gpt_image_2",
"gpt-image-2.5": "gpt_image_2.5_sunburst",
"midjourney": "midjourney",
}
DEFAULT_MODEL = "nano-banana-2"
DEFAULT_MODEL = "nano-banana-2" # 免费/非会员默认可用模型 (Nano Banana 2 / Gemini 3.1 Flash)
SCALES = ["1:1", "16:9", "9:16", "4:3", "3:4"]
RESOLUTIONS = ["1K", "2K", "4K"]
+75
View File
@@ -0,0 +1,75 @@
"""图片智能抠图/切分 — auto cutout / split image.
tripocli img-split -a <asset_id> --wait -o ./cutout.png
tripocli img-split (自动取最新一张参考图抠图)
实测流程(页面上剪刀图标抓到的请求):
POST /v2/studio/image/split
{"asset_id": "...", "resource_key": "generate-image/..."}
→ {"task_id": "...", "asset_id": "..."}
轮询:
POST /v2/studio/image/get_image_asset {"asset_id": "..."}
→ status == "success", type == "split_image", output.data[0].url
"""
import os
import time
from ..errors import TripoAPIError, TripoTimeout
from .image_upscale import resolve_asset_and_key
NAME = "img-split"
HELP = "图片智能抠图/切分:主体背景去除与透明化(对应网页端剪刀按钮)"
def add_arguments(parser):
parser.add_argument("-a", "--asset", help="图片资产 ID(默认自动取最近生成的一张)")
parser.add_argument("--key", help="指定图片 resource_key(默认自动解析)")
parser.add_argument("-w", "--wait", action="store_true", default=True, help="等待抠图完成(默认等待)")
parser.add_argument("--no-wait", dest="wait", action="store_false", help="不等待,提交后直接退出")
parser.add_argument("-o", "--output", help="抠图后的图片保存路径")
parser.add_argument("--timeout", type=int, default=180, help="等待超时秒数(默认 180)")
def run(ctx):
args = ctx.args
asset_id, key = resolve_asset_and_key(ctx, args.asset, args.key)
ctx.out.line(f"✂️ 提交智能抠图任务: asset={asset_id}")
resp = ctx.client.get_data("POST", "/v2/studio/image/split", body={
"asset_id": asset_id,
"resource_key": key,
}) or {}
new_asset_id = resp.get("asset_id") or resp.get("task_id")
if not new_asset_id:
raise TripoAPIError(f"split 未返回 asset_id: {resp}")
if not args.wait:
ctx.out.data(resp, human=lambda: ctx.out.line(f"✅ 任务已提交,asset_id: {new_asset_id}"))
return resp
ctx.out.line(f"⏳ 等待智能抠图完成: {new_asset_id}")
start = time.time()
while time.time() - start < args.timeout:
info = ctx.client.get_data("POST", "/v2/studio/image/get_image_asset", body={"asset_id": new_asset_id}) or {}
status = info.get("status")
if status == "success":
ctx.out.line("🎉 智能抠图完成!")
out_imgs = (info.get("output") or {}).get("data") or []
url = out_imgs[0].get("url") if out_imgs else None
if url:
ctx.out.line(f"抠图结果地址: {url}")
if args.output:
save_path = args.output
if os.path.isdir(save_path):
save_path = os.path.join(save_path, f"{new_asset_id}_cutout.webp")
ctx.client.download_file(url, save_path)
ctx.out.line(f"💾 已保存到: {save_path}")
return info
if status in ("failed", "expired"):
raise TripoAPIError(f"智能抠图失败: status={status}")
time.sleep(2)
raise TripoTimeout(f"智能抠图超时(超过 {args.timeout} 秒)")
+103
View File
@@ -0,0 +1,103 @@
"""图片 4K 高清放大 — upscale reference image to 4K.
tripocli img-upscale -a <asset_id> --wait -o ./upscaled.png
tripocli img-upscale (自动取最新一张参考图放大)
实测流程(页面上 4K 图标抓到的请求):
POST /v2/studio/image/upscale
{"asset_id": "...", "resource_key": "generate-image/..."}
→ {"task_id": "...", "asset_id": "..."}
轮询:
POST /v2/studio/image/get_image_asset {"asset_id": "..."}
→ status == "success", type == "upscale_image", output.data[0].url
"""
import os
import time
from ..errors import TripoAPIError, TripoTimeout
NAME = "img-upscale"
HELP = "图片 4K 高清放大:提升参考图分辨率(对应网页端 4K 按钮)"
def add_arguments(parser):
parser.add_argument("-a", "--asset", help="图片资产 ID(默认自动取最近生成的一张)")
parser.add_argument("--key", help="指定图片 resource_key(默认自动解析)")
parser.add_argument("-w", "--wait", action="store_true", default=True, help="等待放大完成(默认等待)")
parser.add_argument("--no-wait", dest="wait", action="store_false", help="不等待,提交后直接退出")
parser.add_argument("-o", "--output", help="放大后的图片保存路径")
parser.add_argument("--timeout", type=int, default=180, help="等待超时秒数(默认 180)")
def resolve_asset_and_key(ctx, asset_id, resource_key):
if not asset_id:
# 取最新的一张图片资产
data = ctx.client.get_data(
"POST", "/v2/studio/image/image_assets",
body={"page_num": 1, "page_size": 1},
) or {}
assets = data.get("assets") or []
if not assets:
raise TripoAPIError("未找到任何图片资产,请用 -a 指定 asset_id")
asset_id = assets[0].get("asset_id")
if not resource_key:
info = ctx.client.get_data("POST", "/v2/studio/image/get_image_asset", body={"asset_id": asset_id}) or {}
out_data = (info.get("output") or {}).get("data") or []
if out_data and out_data[0].get("key"):
resource_key = out_data[0].get("key")
else:
in_imgs = (info.get("input") or {}).get("prompt_image") or []
if in_imgs and in_imgs[0].get("key"):
resource_key = in_imgs[0].get("key")
if not resource_key:
raise TripoAPIError(f"无法从图片资产 {asset_id} 解析 resource_key,请用 --key 显式传入")
return asset_id, resource_key
def run(ctx):
args = ctx.args
asset_id, key = resolve_asset_and_key(ctx, args.asset, args.key)
ctx.out.line(f"🔍 提交 4K 放大任务: asset={asset_id}")
resp = ctx.client.get_data("POST", "/v2/studio/image/upscale", body={
"asset_id": asset_id,
"resource_key": key,
}) or {}
new_asset_id = resp.get("asset_id") or resp.get("task_id")
if not new_asset_id:
raise TripoAPIError(f"upscale 未返回 asset_id: {resp}")
if not args.wait:
ctx.out.data(resp, human=lambda: ctx.out.line(f"✅ 任务已提交,asset_id: {new_asset_id}"))
return resp
# 轮询直到完成
ctx.out.line(f"⏳ 等待 4K 放大完成: {new_asset_id}")
start = time.time()
while time.time() - start < args.timeout:
info = ctx.client.get_data("POST", "/v2/studio/image/get_image_asset", body={"asset_id": new_asset_id}) or {}
status = info.get("status")
if status == "success":
ctx.out.line("🎉 4K 放大完成!")
out_imgs = (info.get("output") or {}).get("data") or []
url = out_imgs[0].get("url") if out_imgs else None
if url:
ctx.out.line(f"图片地址: {url}")
if args.output:
save_path = args.output
if os.path.isdir(save_path):
save_path = os.path.join(save_path, f"{new_asset_id}_4k.png")
ctx.client.download_file(url, save_path)
ctx.out.line(f"💾 已保存到: {save_path}")
return info
if status in ("failed", "expired"):
raise TripoAPIError(f"4K 放大失败: status={status}")
time.sleep(2)
raise TripoTimeout(f"4K 放大超时(超过 {args.timeout} 秒)")