#!/usr/bin/env bash
# setup_env.sh —— html-explainer 一次性环境自检/安装(可反复跑)。
# 适配:Windows(Git Bash) / macOS / Linux。全部装用户级,不需要管理员。
#
# 用法:
# bash setup_env.sh # 自检,缺什么报告什么
# bash setup_env.sh --install # 自检 + 联网安装缺的依赖
#
# 依赖清单:
# Python ≥3.9 + edge-tts(7.2.8 钉死) numpy pillow imageio-ffmpeg
# Node ≥18(渲染器 .mjs)
# playwright-core(装在技能 node/ 下,纯 JS,可随技能打包带走)
# 浏览器:Chrome 或 Edge(几乎必有);都没有才下载 playwright chromium
# ffmpeg:PATH 有就用;没有则用 imageio-ffmpeg 的静态二进制(自动回退)
set -u
SKILL_ROOT="$(cd "$(dirname "$0")" && pwd)"
INSTALL=0
[ "${1:-}" = "--install" ] && INSTALL=1
# 计数口径:MISSING 表示「自检结束时仍缺的项」。
# 安装成功的项不计数 —— 所以先尝试装、装完再判定,不要在安装前就 miss。
MISSING=0
ok() { echo " ✓ $1"; }
miss() { echo " ✗ $1"; MISSING=$((MISSING + 1)); }
warn() { echo " ⚠ $1"; }
info() { echo " → $1"; }
echo "== html-explainer 环境自检 =="
# ---------- Python ----------
# 候选顺序:WorkBuddy 托管 venv(本机既有依赖,优先复用)→ 系统 python3 / python。
# 前两个是 WorkBuddy 专属路径,其它平台探不到就自动跳过,不影响使用。
PY=""
for c in "$HOME/.workbuddy/binaries/python/envs/default/Scripts/python.exe" \
"$HOME/.workbuddy/binaries/python/envs/default/bin/python" \
python3 python; do
if command -v "$c" >/dev/null 2>&1 || [ -x "$c" ]; then PY="$c"; break; fi
done
if [ -z "$PY" ]; then
miss "找不到 Python ≥3.9"
else
ok "Python: $PY ($("$PY" -V 2>&1))"
if ! "$PY" -c "import edge_tts, numpy, PIL, imageio_ffmpeg" >/dev/null 2>&1; then
if [ $INSTALL -eq 1 ]; then
info "pip 安装 Python 依赖(钉死 edge-tts==7.2.8:7.x 升级常有破坏性)…"
"$PY" -m pip install --user "edge-tts==7.2.8" numpy pillow imageio-ffmpeg >/dev/null 2>&1 \
|| "$PY" -m pip install "edge-tts==7.2.8" numpy pillow imageio-ffmpeg >/dev/null 2>&1 \
|| warn "pip 安装失败,请手动装"
fi
# 装完再判定
if "$PY" -c "import edge_tts, numpy, PIL, imageio_ffmpeg" >/dev/null 2>&1; then
ok "Python 依赖齐全(edge-tts / numpy / pillow / imageio-ffmpeg)"
else
miss "Python 依赖缺(edge-tts numpy pillow imageio-ffmpeg)"
fi
else
ok "Python 依赖齐全(edge-tts / numpy / pillow / imageio-ffmpeg)"
fi
fi
# ---------- Node ----------
node_major() { node -v 2>/dev/null | sed 's/^v//' | cut -d. -f1; }
if command -v node >/dev/null 2>&1; then
NV="$(node_major)"
if [ "${NV:-0}" -ge 18 ]; then ok "Node: $(node -v)"; else miss "Node ≥18 需要,当前 $(node -v)"; fi
else
# 兜底:WorkBuddy 托管的 Node(非标准安装,路径带版本号)。仅本机可用,其它平台跳过。
for d in "$HOME/.workbuddy/binaries/node/versions"/*/; do
if [ -x "$d/node.exe" ] || [ -x "$d/node" ]; then
export PATH="$d:$PATH"; ok "Node(托管): $(node -v)"; break
fi
done
command -v node >/dev/null 2>&1 || miss "找不到 Node ≥18(渲染器需要)"
fi
# ---------- playwright-core ----------
if [ ! -d "$SKILL_ROOT/node/node_modules/playwright-core" ] && [ $INSTALL -eq 1 ]; then
if command -v npm >/dev/null 2>&1; then
info "npm 安装 playwright-core(npmmirror 优先)…"
(cd "$SKILL_ROOT/node" && \
npm install playwright-core --registry https://registry.npmmirror.com --no-audit --no-fund) || \
(cd "$SKILL_ROOT/node" && npm install playwright-core --no-audit --no-fund) || \
warn "npm 安装失败(检查 npm/corepack 或换 registry)"
else
warn "没有 npm —— 无法自动安装 playwright-core"
fi
fi
# 装完再判定
if [ -d "$SKILL_ROOT/node/node_modules/playwright-core" ]; then
ok "playwright-core(技能内置 node/)"
else
miss "playwright-core 缺失"
fi
# ---------- 浏览器 ----------
BROWSER=""
for p in "/c/Program Files/Google/Chrome/Application/chrome.exe" \
"/c/Program Files (x86)/Google/Chrome/Application/chrome.exe" \
"$LOCALAPPDATA/Google/Chrome/Application/chrome.exe" \
"/c/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" \
"/c/Program Files/Microsoft/Edge/Application/msedge.exe" \
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" \
"/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" \
"/usr/bin/google-chrome" "/usr/bin/chromium" "/usr/bin/chromium-browser"; do
[ -f "$p" ] && BROWSER="$p" && break
done
if [ -n "$BROWSER" ]; then
ok "浏览器: $BROWSER(渲染直接用,无需下载)"
else
warn "没找到本机 Chrome/Edge —— 渲染时会回退 playwright 自带 chromium(首次 ~115MB 下载)"
if [ $INSTALL -eq 1 ]; then
echo " → 可预下载:npx playwright install chromium(或设 BROWSER_PATH 指向任一 Chromium 系浏览器)"
fi
fi
# ---------- ffmpeg ----------
if command -v ffmpeg >/dev/null 2>&1; then
ok "ffmpeg: $(command -v ffmpeg)"
elif [ -n "${PY:-}" ] && "$PY" -c "import imageio_ffmpeg" >/dev/null 2>&1; then
ok "ffmpeg 回退: imageio-ffmpeg 静态二进制($("$PY" -c "import imageio_ffmpeg;print(imageio_ffmpeg.get_ffmpeg_exe())" 2>/dev/null | head -1))"
else
miss "ffmpeg(PATH 或 imageio-ffmpeg 二者其一)"
fi
# ---------- GSAP ----------
if [ -f "$SKILL_ROOT/assets/gsap.min.js" ]; then
ok "GSAP 内置: assets/gsap.min.js(离线可用)"
else
miss "assets/gsap.min.js(被打包脚本删了?从 gsap npm 包 dist/ 恢复)"
fi
echo "----------------------------------------"
if [ $MISSING -eq 0 ]; then
echo "环境就绪 ✓(重跑加 --install 可自动装缺项)"
else
echo "缺 $MISSING 项 —— bash setup_env.sh --install 联网安装"
exit 1
fi