#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 将 16 型 MBTI 占位 PNG 下载到 soul-api/static,供 Gin r.Static(\"/static\", \"./static\") 提供, 管理端与小程序访问 /static/mbti-avatars-png-male/{TYPE}.png 等路径时不再 404。 素材:DiceBear 9.x notionists/png(HTTPS API,可按 seed 复现)。 许可见 https://www.dicebear.com/licenses/ ,商用请以官网为准;可随后在管理端替换为自有 OSS URL。 用法(仓库根目录):: python scripts/fetch_mbti_png_to_soul_api_static.py """ from __future__ import annotations import ssl import time import urllib.error import urllib.parse import urllib.request from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] OUT_BASE = REPO_ROOT / "soul-api" / "static" MBTI = [ "INTJ", "INTP", "ENTJ", "ENTP", "INFJ", "INFP", "ENFJ", "ENFP", "ISTJ", "ISFJ", "ESTJ", "ESFJ", "ISTP", "ISFP", "ESTP", "ESFP", ] # DiceBear PNG 限流约 10/s,保守间隔 DELAY_SEC = 0.2 def fetch_one(url: str, dest: Path) -> None: ctx = ssl.create_default_context() req = urllib.request.Request(url, headers={"User-Agent": "SoulMbtiPngPrefetch/1.0 (+internal)"}) with urllib.request.urlopen(req, timeout=90, context=ctx) as resp: data = resp.read() if len(data) < 100: raise RuntimeError(f"{url}: response too small ({len(data)} bytes)") dest.write_bytes(data) def main() -> None: packs: list[tuple[str, str, str]] = [ ("male", "mbti-avatars-png-male", "男版占位(DiceBear notionists)"), ("female", "mbti-avatars-png-female", "女版占位(DiceBear notionists,seed 区分)"), ] base = "https://api.dicebear.com/9.x/notionists/png" total = 0 for gender_key, dirname, subtitle in packs: out_dir = OUT_BASE / dirname out_dir.mkdir(parents=True, exist_ok=True) readme = out_dir / "README.txt" lines = [ "MBTI 默认头像 PNG(本目录文件名 = 四字母类型)", subtitle, "由脚本 scripts/fetch_mbti_png_to_soul_api_static.py 生成,可商用请自行核对 DiceBear 许可。", "部署 soul-api 时须随二进制一同带上 static/,否则前端会显示占位缩写。", "", ] for code in MBTI: seed = f"{code}-{gender_key}" url = f"{base}?seed={urllib.parse.quote(seed)}&size=128" dest = out_dir / f"{code}.png" print(f"{dest.relative_to(REPO_ROOT)} ...") try: fetch_one(url, dest) except urllib.error.HTTPError as e: raise SystemExit(f"HTTP {e.code} {url}") from e total += 1 lines.append(f"{code}.png <- {seed}") time.sleep(DELAY_SEC) readme.write_text("\n".join(lines) + "\n", encoding="utf-8") print(f"ok {dirname} ({len(MBTI)} files)") print(f"done: {total} png -> {OUT_BASE.relative_to(REPO_ROOT)}") if __name__ == "__main__": main()