初始发布: 21 个 skills (Claude Code / Codex / DSH)
This commit is contained in:
Executable
+149
@@ -0,0 +1,149 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""GLM-Image 文生图调用脚本(智谱 GLM-Image 模型)。
|
||||
|
||||
用法:
|
||||
python3 generate.py "提示词" [-s SIZE] [-o OUTPUT] [--open] [--json]
|
||||
|
||||
示例:
|
||||
python3 generate.py "一只可爱的小猫咪,坐在阳光明媚的窗台上" -s 1280x1280
|
||||
python3 generate.py "商业海报:新品上市" -s 1056x1568 -o poster.png --open
|
||||
|
||||
默认 size=1280x1280,输出到当前目录 glm-image-<时间戳>.png
|
||||
API Key 优先读环境变量 GLM_API_KEY,否则用内置默认 key。
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
API_ENDPOINT = "https://open.bigmodel.cn/api/paas/v4/images/generations"
|
||||
DEFAULT_KEY = "" # 服务器版不含内置 key,请设置环境变量 GLM_API_KEY
|
||||
|
||||
RECOMMENDED_SIZES = [
|
||||
"1280x1280", "1568x1056", "1056x1568",
|
||||
"1472x1088", "1088x1472", "1728x960", "960x1728",
|
||||
]
|
||||
|
||||
|
||||
def parse_size(size):
|
||||
"""校验 size,返回 (w, h)。规则:512-2048,且为 32 的整数倍。"""
|
||||
try:
|
||||
w, h = size.lower().split("x")
|
||||
w, h = int(w), int(h)
|
||||
except ValueError:
|
||||
raise ValueError(f"size 格式错误:'{size}',应为 WxH,如 1280x1280")
|
||||
for v, name in ((w, "宽"), (h, "高")):
|
||||
if v < 512 or v > 2048:
|
||||
raise ValueError(f"{name}={v} 不在 512-2048 范围内")
|
||||
if v % 32 != 0:
|
||||
raise ValueError(f"{name}={v} 不是 32 的整数倍")
|
||||
return w, h
|
||||
|
||||
|
||||
def generate(prompt, size="1280x1280", api_key=None, timeout=120, watermark=True):
|
||||
"""调用 GLM-Image 接口,返回图片 URL。
|
||||
|
||||
watermark=False 关闭 AI 水印,需账号已在「个人中心-安全管理-去水印管理」签署免责声明。
|
||||
"""
|
||||
parse_size(size) # 校验
|
||||
key = api_key or os.environ.get("GLM_API_KEY") or DEFAULT_KEY
|
||||
payload = {
|
||||
"model": "glm-image",
|
||||
"prompt": prompt,
|
||||
"size": size,
|
||||
"watermark_enabled": watermark,
|
||||
}
|
||||
data = json.dumps(payload).encode("utf-8")
|
||||
req = urllib.request.Request(
|
||||
API_ENDPOINT,
|
||||
data=data,
|
||||
headers={
|
||||
"Authorization": f"Bearer {key}",
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
},
|
||||
method="POST",
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
||||
body = resp.read().decode("utf-8")
|
||||
except urllib.error.HTTPError as e:
|
||||
err = e.read().decode("utf-8", errors="replace")
|
||||
raise RuntimeError(f"HTTP {e.code}: {err}") from None
|
||||
except urllib.error.URLError as e:
|
||||
raise RuntimeError(f"网络错误: {e.reason}") from None
|
||||
|
||||
obj = json.loads(body)
|
||||
if not obj.get("data"):
|
||||
raise RuntimeError(f"返回无 data 字段: {body}")
|
||||
url = obj["data"][0].get("url")
|
||||
if not url:
|
||||
raise RuntimeError(f"返回无 url: {body}")
|
||||
return url, obj
|
||||
|
||||
|
||||
def download(url, output):
|
||||
"""下载图片 URL 到 output,返回输出路径。"""
|
||||
with urllib.request.urlopen(url, timeout=120) as resp:
|
||||
content = resp.read()
|
||||
with open(output, "wb") as f:
|
||||
f.write(content)
|
||||
return output
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description="GLM-Image 文生图")
|
||||
ap.add_argument("prompt", help="生成提示词(最多 1000 字符)")
|
||||
ap.add_argument("-s", "--size", default="1280x1280",
|
||||
help=f"图片尺寸 WxH(默认 1280x1280)。推荐: {', '.join(RECOMMENDED_SIZES)}")
|
||||
ap.add_argument("-o", "--output", default=None,
|
||||
help="输出文件路径(默认 glm-image-<时间戳>.png)")
|
||||
ap.add_argument("--open", action="store_true", help="生成后在 Finder 中打开")
|
||||
ap.add_argument("--json", action="store_true", help="打印完整 JSON 返回")
|
||||
ap.add_argument("--no-download", action="store_true", help="只返回 URL,不下载")
|
||||
ap.add_argument("--no-watermark", action="store_true",
|
||||
help="关闭 AI 水印(需账号已在「个人中心-安全管理-去水印管理」签署免责声明)")
|
||||
args = ap.parse_args()
|
||||
|
||||
if len(args.prompt) > 1000:
|
||||
sys.exit(f"错误: 提示词 {len(args.prompt)} 字符,超过 1000 上限")
|
||||
|
||||
try:
|
||||
parse_size(args.size)
|
||||
except ValueError as e:
|
||||
sys.exit(f"错误: {e}")
|
||||
|
||||
print(f"→ 调用 GLM-Image(size={args.size}, watermark={'off' if args.no_watermark else 'on'})...", file=sys.stderr)
|
||||
t0 = time.time()
|
||||
try:
|
||||
url, obj = generate(args.prompt, args.size, watermark=not args.no_watermark)
|
||||
except RuntimeError as e:
|
||||
sys.exit(f"错误: {e}")
|
||||
elapsed = time.time() - t0
|
||||
print(f"✓ 生成成功({elapsed:.1f}s): {url}", file=sys.stderr)
|
||||
|
||||
if args.json:
|
||||
print(json.dumps(obj, ensure_ascii=False, indent=2))
|
||||
|
||||
if args.no_download:
|
||||
print(url)
|
||||
return
|
||||
|
||||
output = args.output or f"glm-image-{int(time.time())}.png"
|
||||
try:
|
||||
download(url, output)
|
||||
except Exception as e:
|
||||
sys.exit(f"下载失败: {e}\n图片 URL: {url}")
|
||||
print(f"✓ 已保存: {output}", file=sys.stderr)
|
||||
print(output)
|
||||
|
||||
if args.open:
|
||||
os.system(f'open "{output}"')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user