# boardshadow
**Repository Path**: x7h66/boardshadow
## Basic Information
- **Project Name**: boardshadow
- **Description**: 多学科板书AI视频生成工具
- **Primary Language**: Python
- **License**: Apache-2.0
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-05-19
- **Last Updated**: 2026-05-20
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# BoardShadow 多学科板书 AI 视频生成工具
本项目已调整为纯 Python 后端技术栈:FastAPI + MySQL + 本地/线上大模型 + HyperFrames帧生成 + Pillow 板书帧渲染 + FFmpeg 视频合成。视频生成引擎已合并到 `backend/app/engine.py`,后端不再依赖其他语言服务。
## 目录
- `backend/`:FastAPI 后端、MySQL 数据访问、鉴权、任务编排、视频生成引擎
- `backend/app/engine.py`:本地离线生成引擎,负责脚本、板书帧、音轨和 MP4
- `backend/schema.sql`:MySQL 建表与种子数据
- `web/`:Vue3 + Vite Web 前端
- `miniapp/`:uni-app 微信小程序端
- `storage/`:生成任务产物目录
-
-
-
## 本地依赖
- Python 3.10+
- MySQL 8
- FFmpeg,并加入 `PATH`
- Node.js 18+,用于 Web 和小程序端
- 可选:本地 Ollama,默认地址 `http://127.0.0.1:11434`
- 可选:OpenAI 兼容 API,可接线上模型,也可接 LM Studio / vLLM / xinference 等本地离线服务
- 可选:开源 TTS,后端已内置 xiaozhi provider 运行子集与 OmniVoice 声音克隆推理包
未启动大模型或未配置开源 TTS 时,系统会使用内置学科模板和静音占位音轨,仍可生成 MP4。
## 一键本地启动
首次部署可直接执行:
```bash
cd /boardshadow
./scripts/deploy.sh
```
如果只想启动已安装好的环境,可直接执行 `./scripts/start-stack.sh`。
如果要停止本地服务,可执行 `./scripts/stop-stack.sh`。
查看状态可执行 `./scripts/status.sh`,备份可执行 `./scripts/backup.sh`,恢复可执行 `./scripts/restore.sh /path/to/backup-dir`。
启动后访问 `http://127.0.0.1:5173`。
部署验收可执行:
```bash
python3 scripts/smoke-test.py --base-url http://127.0.0.1:5173
python3 scripts/smoke-test.py --base-url http://127.0.0.1:5173 --video-fps 60
```
如果 Vite 自动换了端口,把 `--base-url` 改成实际 PC 前端地址,例如 `http://127.0.0.1:5177`。
默认登录:
- 账号:`teacher`
- 密码:`boardshadow123`
- 角色:`admin`
生产部署可在 `.env.local` 中通过 `BOARDSHADOW_DEFAULT_ADMIN_USERNAME`、`BOARDSHADOW_DEFAULT_ADMIN_PASSWORD`、`BOARDSHADOW_DEFAULT_ADMIN_DISPLAY_NAME` 覆盖默认管理员。后端启动初始化数据库时会同步该账号;如需强制每次启动重置默认管理员密码,可设置 `BOARDSHADOW_RESET_DEFAULT_ADMIN_PASSWORD=1`。
## 安装后端依赖
```bash
cd /boardshadow/backend
pip3 install -r requirements.txt
```
## 数据库
方式一,使用 Docker:
```bash
cd /solo/boardshadow
docker compose up -d mysql
```
方式二,使用本机 MySQL:
```bash
mysql -h127.0.0.1 -uroot -p12345678 < backend/schema.sql
```
默认账号:
- 用户名:`teacher`
- 密码:`boardshadow123`
- 角色:`admin`
如果已设置 `BOARDSHADOW_DEFAULT_ADMIN_*` 环境变量,以环境变量为准。默认情况下不会在每次启动时重写该账号密码,除非显式开启 `BOARDSHADOW_RESET_DEFAULT_ADMIN_PASSWORD=1`。
## 启动后端
```bash
cd /boardshadow
./scripts/start-backend.sh
```
等价命令:
```bash
cd /boardshadow/backend
python3 -m uvicorn app.main:app --host 0.0.0.0 --port 8088 --reload
```
常用环境变量:
```bash
export MYSQL_HOST=127.0.0.1
export MYSQL_PORT=3306
export MYSQL_DATABASE=boardshadow
export MYSQL_USERNAME=root
export MYSQL_PASSWORD=12345678
export OLLAMA_BASE_URL=http://127.0.0.1:11434
export BOARDSHADOW_STORAGE_DIR=/boardshadow/storage
export BOARDSHADOW_CORS_ORIGIN_REGEX='https?://(localhost|127\.0\.0\.1|0\.0\.0\.0)(:\d+)?'
```
也可以复制 `.env.local.example` 为 `.env.local`,`scripts/start-backend.sh` 和 `scripts/start-stack.sh` 会自动读取。
`scripts/deploy.sh` 会自动安装 Python / Web / 小程序依赖并启动服务,适合全新机器。
## 运维脚本
- `./scripts/doctor.sh`:检查本机依赖、Ollama/TTS、端口和存储目录
- `./scripts/status.sh`:查看本地或容器运行状态和健康检查
- `python3 scripts/smoke-test.py --base-url http://127.0.0.1:5173 --video-fps 30|60`:跑登录、模板加载、视频生成、下载接口、TTS重复句和实际 MP4 帧率验收
- `./scripts/backup.sh`:备份数据库和 `storage/tasks`、`storage/logs`
- `./scripts/restore.sh /path/to/backup-dir`:恢复数据库;追加 `--storage` 可恢复任务产物和日志
## 容器化部署
如果机器上已安装 Docker,可以直接:
```bash
cd /boardshadow
./scripts/deploy-docker.sh
```
浏览器访问 `http://127.0.0.1:5173`。
如果希望 Ollama 也由 Docker 一起启动并自动拉取默认模型:
```bash
./scripts/deploy-docker-ai.sh
```
等价地,也可以通过统一入口启用 AI 容器:
```bash
BOARDSHADOW_DEPLOY_WITH_AI=1 ./scripts/deploy.sh
```
停止容器:
```bash
./scripts/stop-stack.sh
```
容器版会同时启动 MySQL、Python 后端和 Web 静态站点,适合正式部署。
状态查看、备份和恢复脚本同样适用于容器版。
如果要让容器内后端连接宿主机上的 Ollama 或开源 TTS,请在 `.env.local` 里设置对应的容器可访问地址和命令,不要直接复用只在宿主机有效的路径。
如果使用 `deploy-docker-ai.sh`,后端会改连 compose 内的 `ollama` 服务。
## 大模型与开源 TTS
更完整的本地 AI 安装说明见 [docs/local-ai-setup.md](docs/local-ai-setup.md)。
默认使用本地 Ollama,安装并拉取默认模型:
```bash
cd /boardshadow
./scripts/install-local-ai.sh
```
也可以在 Web 管理面板或 `.env.local` 里切换大模型来源:
```bash
# 默认本地 Ollama
BOARDSHADOW_LLM_PROVIDER=ollama
OLLAMA_BASE_URL=http://127.0.0.1:11434
# OpenAI 兼容 API,可用于线上模型或本地 LM Studio/vLLM/xinference
BOARDSHADOW_LLM_PROVIDER=api
BOARDSHADOW_LLM_API_BASE_URL=https://api.example.com/v1
BOARDSHADOW_LLM_API_KEY=sk-...
BOARDSHADOW_LLM_API_MODEL=qwen-plus
BOARDSHADOW_LLM_API_TIMEOUT=45
# 不调用大模型,仅用内置小学模板兜底
BOARDSHADOW_LLM_PROVIDER=template
```
xiaozhi TTS 命令可用模板接入:
```bash
./scripts/setup-xiaozhi-tts.sh "/path/to/tts --text {text} --out {output} --speed {speed} --mode {mode}"
```
Web 和小程序生成配置默认使用 `xiaozhi TTS`,也可切换到 `OmniVoice 克隆`。TTS 运行代码已内置到后端:xiaozhi 默认目录为 `./backend/vendor/xiaozhi-server`,OmniVoice 默认目录为 `./backend/vendor/omnivoice`,模型配置为 `OMNIVOICE_MODEL=k2-fsa/OmniVoice`。
生成页还提供小学课堂参数:
- `年级层次`:小学低年级 / 中年级 / 高年级,影响用词难度和讲解深度。
- `教学方式`:引导互动 / 例题练习 / 故事导入,影响脚本组织方式。
- `视频节奏`:默认舒缓,控制板书逐行出现的速度。
- `视频帧率`:支持 30 FPS 和 60 FPS,默认 30 FPS。
- `语音语速`:传给开源 TTS,并影响音频合成速度。
## 启动 Web
```bash
cd /boardshadow/web
npm install
npm run dev
```
Vite 会把 `/api` 代理到 `http://127.0.0.1:8088`。
## 启动小程序
```bash
cd /boardshadow/miniapp
npm install
npm run dev:mp-weixin
```
小程序登录页可填写后端地址。微信开发者工具里调本机后端时,通常需要关闭域名校验;真机调试需把地址改成同局域网可访问 IP。
生产构建可执行 `npm run build:mp-weixin`,产物位于 `miniapp/dist/build/mp-weixin`。
## 生成引擎单独验证
```bash
cd /boardshadow
./scripts/run-engine-demo.sh
```
输出文件位于 `storage/tasks/demo/boardshadow.mp4`。
## API 摘要
- `POST /api/auth/login`:登录,返回本地 token,兼容 `satoken` 请求头
- `POST /api/auth/register`:注册普通教师账号,注册成功后直接返回登录 token
- `GET /api/auth/me`:当前用户
- `POST /api/auth/password`:登录用户修改自己的密码
- `GET /api/catalog/subjects`:学科列表
- `GET /api/catalog/templates?subject=math`:模板列表
- `POST /api/catalog/templates`:新增当前用户自定义模板
- `PUT /api/catalog/templates/{code}`:编辑模板,普通教师仅可维护自己创建的模板
- `DELETE /api/catalog/templates/{code}`:软删除模板,普通教师仅可删除自己创建的模板
- `GET /api/voice-profiles`:查看当前用户录入的声音样本
- `POST /api/voice-profiles?name=王老师&referenceText=...`:上传原始音频样本,用于 OmniVoice 克隆
- `DELETE /api/voice-profiles/{id}`:删除当前用户的声音样本
- `GET /api/admin/config`:管理员查看模型、TTS、FFmpeg 等运行配置
- `PUT /api/admin/config`:管理员更新运行配置
- `GET /api/admin/migrations`:管理员查看数据库迁移版本
- `GET /api/admin/dashboard`:管理员查看任务、用户、学科和存储看板
- `GET /api/admin/users?page=1&pageSize=20&q=teacher`:管理员分页检索账号
- `POST /api/admin/users`:管理员创建教师或管理员账号
- `PUT /api/admin/users/{id}`:管理员修改显示名称、角色和启用状态
- `POST /api/admin/users/{id}/password`:管理员重置账号密码
- `GET /api/admin/audit-logs?page=1&pageSize=30&q=USER`:管理员查看审计日志
- `GET /api/admin/audit-logs/export`:管理员导出审计日志 CSV,最多 5000 行
- `GET /api/admin/videos?page=1&pageSize=30&q=teacher`:管理员全局检索所有用户任务
- `GET /api/admin/videos/export`:管理员导出全局任务 CSV,最多 5000 行
- `GET /api/admin/storage`:管理员查看存储目录、产物体积和任务状态分布
- `POST /api/admin/storage/cleanup`:管理员按任务状态和保留天数预演或执行产物清理
- `POST /api/admin/scheduler/pause`:管理员暂停任务队列接收新执行
- `POST /api/admin/scheduler/resume`:管理员恢复任务队列并重新入队未完成任务
- `GET /api/admin/logs/tail?lines=200`:管理员查看系统日志尾部
- `GET /api/admin/logs/download`:管理员下载当前系统日志文件
- `GET /api/admin/videos/{taskNo}`:管理员查看任意用户任务详情
- `GET /api/admin/videos/{taskNo}/events`:管理员查看任意用户任务事件
- `GET /api/admin/videos/{taskNo}/stream`:管理员预览任意用户 MP4
- `GET /api/admin/videos/{taskNo}/download`:管理员下载任意用户 MP4
- `GET /api/admin/videos/{taskNo}/subtitles`:管理员下载任意用户字幕
- `GET /api/admin/videos/{taskNo}/assets`:管理员下载任意用户素材包
- `POST /api/admin/videos/{taskNo}/cancel`:管理员取消任意用户任务
- `POST /api/admin/videos/{taskNo}/retry`:管理员重试任意用户终态任务
- `POST /api/admin/videos/{taskNo}/mark-failed`:管理员将卡住任务强制标记失败
- `DELETE /api/admin/videos/{taskNo}`:管理员删除任意用户终态任务记录
- `POST /api/videos/generate`:创建生成任务,支持 `Idempotency-Key`,受每日额度和活跃任务上限控制
- `POST /api/videos/batch-generate`:批量创建生成任务,数量上限由 `task.max_batch_size` 配置控制,支持 `Idempotency-Key`
- `GET /api/videos?page=1&pageSize=30&q=关键词&subject=math&status=COMPLETED`:分页检索历史记录
- `GET /api/videos/quota`:当前用户今日额度、活跃任务和剩余额度
- `GET /api/videos/{taskNo}`:查询任务状态
- `GET /api/videos/{taskNo}/events`:查询任务审计事件
- `GET /api/videos/{taskNo}/stream`:预览 MP4
- `GET /api/videos/{taskNo}/download`:下载 MP4
- `GET /api/videos/{taskNo}/subtitles`:下载 SRT 字幕
- `GET /api/videos/{taskNo}/assets`:下载二次剪辑素材包 ZIP
- `POST /api/videos/{taskNo}/cancel`:取消排队或运行中任务
- `POST /api/videos/{taskNo}/retry`:重新生成
- `DELETE /api/videos/{taskNo}`:删除历史记录
- `GET /api/system/diagnostics`:检查 FFmpeg、Ollama、TTS、存储目录和任务调度器状态
## 当前实现边界
- 鉴权使用 Python 本地 HMAC token,但请求头仍使用 `satoken` 这个兼容名称,前端无需改接口;管理接口使用 `admin` 角色保护。
- Web 和小程序登录页均展示默认账号密码,并支持注册普通教师账号;默认管理员账号默认是 `teacher / boardshadow123`,生产部署可用 `BOARDSHADOW_DEFAULT_ADMIN_*` 覆盖。
- 管理员可以在 Web 管理面板创建教师/管理员、启停账号、修改角色和重置密码;后端会阻止删除最后一个启用管理员权限。
- 登录用户可在 Web 顶部自助修改密码,密码修改会进入审计日志。
- Web 生成区会展示当前用户今日任务额度和活跃任务上限,生成后自动刷新。
- Web 模板管理支持新增、编辑和删除当前选中模板;系统模板有独立标识,普通教师不能改写内置模板或其他用户模板。
- 管理员操作会写入 `boardshadow_audit_log`,用于追踪配置修改、用户创建、角色调整和密码重置。
- Web 管理面板包含运行运维区,可查看全局任务、存储用量,暂停/恢复调度器,并对失败/取消/完成任务产物做清理预演或执行。
- Web 管理面板支持导出全局任务 CSV、导出审计 CSV、查看系统日志尾部和下载当前日志文件。
- 管理员可在全局任务列表接管任务,执行查看、预览、下载、查看事件、取消、重试、强制失败和删除,所有写操作进入审计日志。
- 运行配置保存在 MySQL 的 `boardshadow_app_config`,Web 管理面板可修改模型、Ollama、TTS、FFmpeg、批量任务上限、每日任务额度和活跃任务上限,生成任务会读取最新配置。
- 建表脚本和启动自检会维护 `boardshadow_schema_migration`、`boardshadow_app_config`、`boardshadow_idempotency_key`、`boardshadow_audit_log` 等生产运行表,并记录模板归属、系统模板标记和软删除状态。
- HyperFrames 以 `backend/app/engine.py` 内的 Pillow 渲染器实现,已按学科绘制不同板书符号和排版。
- 自定义模板的 `promptScaffold` 会进入本地 Ollama 提示词;Ollama 不可用时也会作为兜底脚本的板书骨架。
- 支持横版 `16:9` 与竖版 `9:16`,竖版用于短视频平台。
- TTS 已切换为开源接口;每个任务默认使用 xiaozhi,也可选择 OmniVoice。xiaozhi 使用 `XIAOZHI_TTS_CMD` / xiaozhi provider;OmniVoice 需要先录入并选择声音;未配置时生成同步静音 WAV。
- 视频默认面向小学课堂优化:板书逐行出现、节奏偏舒缓、无横线遮挡、无重点标记,脚本按课堂目标、一起观察、老师讲解、跟我练习、课堂小结组织,并可按小学低/中/高年级与教学方式调整。
- 每次生成会输出 `boardshadow.mp4`、`cover.png`、`subtitles.srt`、`script.json` 和 `manifest.json`,并可导出包含帧图与音频片段的 `boardshadow_assets.zip`,方便二次剪辑。
- 视频任务异步执行,Web 和小程序通过轮询状态更新预览;任务支持排队取消、运行中取消标记、失败/完成/取消后重试与删除。
- Web 支持批量模式,按空行分隔多个题目或知识点后批量创建任务。
- Web 历史记录支持服务端分页、关键词、学科和状态筛选。
- Web 支持新增自定义模板,保存后可直接用于当前学科生成;模板创建、更新和删除会写入审计日志。
- 后端使用进程级任务调度器执行生成任务,支持并发控制、服务启动后恢复未完成任务、任务事件审计和滚动日志文件。
## 生产化运行参数
```bash
export BOARDSHADOW_TASK_WORKERS=2
export BOARDSHADOW_RECOVER_TASKS=1
export BOARDSHADOW_LOG_DIR=/boardshadow/storage/logs
```
日志文件:
```text
storage/logs/boardshadow.log
```