feat: 经文数据外置 sutras/*.json,加减经不改代码
- 7部经文迁移至 sutras/ 目录,/api/sutras 实时扫描返回清单 - 前端改为按需懒加载经文 JSON(selectToken 防快速切换乱序) - add_sutra.sh:校验+上传/删除 一键管理,免重启 - 修正生僻异体字(𫗪→喂、𫄨→絺、𦈎→繟、鬪→斗) - run.md 同步新架构与经文管理文档 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Executable
+88
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env bash
|
||||
# 管理服务器上的经文:加经 / 减经。
|
||||
# 目录是实时扫描的,无需重启服务,操作完即生效。
|
||||
#
|
||||
# 用法:
|
||||
# ./add_sutra.sh <经文.json> # 加经(校验 → 上传 → 验证)
|
||||
# ./add_sutra.sh --remove <id> # 减经(如 ./add_sutra.sh -d qingjing)
|
||||
# FJ_PASS=xxx ./add_sutra.sh ... # 密码登录(需 sshpass);已配 SSH key 则不需要
|
||||
#
|
||||
# JSON 格式见 sutras/ 下现有文件:id/order/name/cat/title/seal/translator/source/sourceUrl/pageSize/segments[{h?,g?,t}]
|
||||
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
HOST=154.8.162.18
|
||||
USER=root
|
||||
DEST=/opt/fj/sutras
|
||||
|
||||
SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=15"
|
||||
SSH_CMD="ssh $SSH_OPTS"
|
||||
if [ -n "${FJ_PASS:-}" ]; then
|
||||
command -v sshpass >/dev/null || { echo "缺少 sshpass:brew install sshpass 或改用 SSH key"; exit 1; }
|
||||
SSH_CMD="sshpass -p $FJ_PASS ssh $SSH_OPTS"
|
||||
fi
|
||||
|
||||
list_remote() {
|
||||
curl -s --max-time 15 https://fj.all8ai.top/api/sutras | python3 -c "
|
||||
import json, sys
|
||||
items = {s['id']: s['name'] for s in json.load(sys.stdin)['sutras']}
|
||||
print(' 当前目录:' + ('、'.join(items.values()) or '(空)'))
|
||||
"
|
||||
}
|
||||
|
||||
# ---- 减经 ----
|
||||
if [ "${1:-}" = "--remove" ] || [ "${1:-}" = "-d" ]; then
|
||||
ID="${2:-}"
|
||||
if [ -z "$ID" ]; then
|
||||
echo "用法:$0 --remove <id>(如 $0 -d qingjing)"; exit 1
|
||||
fi
|
||||
echo "==> 删除 $USER@$HOST:$DEST/ 中 id=$ID 的经文"
|
||||
# 按文件名或 JSON 内 id 字段匹配,命中即删
|
||||
$SSH_CMD "$USER@$HOST" "python3 -c 'import json,sys,pathlib
|
||||
d=pathlib.Path(\"$DEST\")
|
||||
i=\"$ID\"
|
||||
for f in sorted(d.glob(\"*.json\")):
|
||||
try: meta=json.loads(f.read_text(encoding=\"utf-8\"))
|
||||
except Exception: continue
|
||||
if f.stem==i or meta.get(\"id\")==i:
|
||||
f.unlink(); print(\"已删除\", f.name); break
|
||||
else:
|
||||
print(\"不存在:\", i); sys.exit(1)'"
|
||||
echo "==> 验证目录接口"
|
||||
sleep 1
|
||||
list_remote
|
||||
echo "完成。该经的 TTS 缓存文件仍在 cache/(无害,复用文本时仍可命中)。"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ---- 加经 ----
|
||||
FILE="${1:-}"
|
||||
if [ -z "$FILE" ] || [ ! -f "$FILE" ]; then
|
||||
echo "用法:$0 <经文.json> 或 $0 --remove <id>"; exit 1
|
||||
fi
|
||||
|
||||
# 基本校验:合法 JSON + 必填字段齐全 + segments 非空且每段有 t
|
||||
echo "==> 校验 $FILE"
|
||||
python3 - "$FILE" <<'PY'
|
||||
import json, sys
|
||||
d = json.load(open(sys.argv[1], encoding="utf-8"))
|
||||
req = ["id", "name", "cat", "title", "seal", "translator", "source", "sourceUrl", "pageSize", "segments"]
|
||||
missing = [k for k in req if k not in d]
|
||||
assert not missing, f"缺少字段: {missing}"
|
||||
segs = d["segments"]
|
||||
assert isinstance(segs, list) and segs, "segments 必须是非空数组"
|
||||
assert all(s.get("t") for s in segs), "存在缺 t 字段的段"
|
||||
long_seg = [i for i, s in enumerate(segs) if len(s["t"]) > 800]
|
||||
assert not long_seg, f"第 {long_seg} 段超过 800 字上限,请拆分"
|
||||
print(f" 《{d['name']}》 {len(segs)} 段, 分类 {d['cat']}, order {d.get('order', 99)} —— 校验通过")
|
||||
PY
|
||||
|
||||
NAME=$(basename "$FILE")
|
||||
echo "==> 上传到 $USER@$HOST:$DEST/$NAME"
|
||||
rsync -az --timeout=30 -e "$SSH_CMD" "$FILE" "$USER@$HOST:$DEST/$NAME"
|
||||
|
||||
echo "==> 验证目录接口"
|
||||
sleep 1
|
||||
list_remote
|
||||
echo "完成。刷新 https://fj.all8ai.top 即可在下拉框看到。"
|
||||
Reference in New Issue
Block a user