chore(release): merge release/2.2.0 back into develop

This commit is contained in:
huangjianwu
2026-05-09 14:43:48 +08:00
4 changed files with 96 additions and 25 deletions

View File

@@ -2,6 +2,37 @@
本项目所有重要变更记录于此。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
## [2.2.0] - 2026-05-09
主线:浏览器插件功能与 web 端 NoteForm 完整对齐;桌面客户端 UX 与错误恢复一波重炼。
### Added — 浏览器插件
- 笔记选项与 web 端 NoteForm 完整对齐:
- `style` 由自由文本改成 9 个预设下拉minimal / detailed / academic / tutorial / xiaohongshu / life_journal / task_oriented / business / meeting_minutes与 backend `prompt_builder.note_styles` 严格匹配(之前自由文本不命中 enum 等于没传——隐性 bug
- `format` 完整 4 个 checkboxtoc / link / screenshot / summary原来只有 screenshot/link
- `extras` 文本框:拼接到 prompt 末尾的 ad-hoc 提示
- 多模态视频理解:`video_understanding` 开关 + `video_interval`1-30 秒)+ `grid_size`[r,c]1-10抽帧拼图喂视觉模型提示需选视觉模型才生效
### Added — 桌面客户端
- **首启 4 步引导**`/onboarding`):后端连通性自检 → LLM 供应商 + 模型 → 转写引擎选择(默认推荐 Groq→ Cookie 同步说明。完成后 `localStorage('bilinote-onboarded')` 标记,纯 web 端不打扰
- **Sidecar 健康度面板**:右下角浮动状态点(绿/黄/红5s 轮询 `/sys_health`),点开抽屉看最近 200 行后端日志、一键重启后端(新增 Tauri command `restart_backend_sidecar`)、复制日志
- **启动期路径诊断**Tauri `setup` 中检测安装路径含非 ASCII / 含空格 / 父目录不可写时emit `backend-warning` 让前端顶端横幅显式告警,主动暴露 README 长期文字警告但无防御的"中文路径"等坑
### Changed
- Whisper 默认模型 size 从 `medium`~1.5GB)改为 `tiny`~75MB新装用户没主动设置时不再卡在首次大模型下载高精度可在「音频转写配置」页主动切
- 切到 `fast-whisper` / `mlx-whisper` 且当前 size 未下载时,「音频转写配置」页保存前 confirm 体积提示,并推荐改用在线引擎
- Tauri sidecar 启动逻辑抽出 `spawn_backend_sidecar()`child handle 存进 `SidecarHandle` state 以支持后续 restart
- sidecar stdout/stderr emit 时不再用 `format!("'{}'", ...)` 包引号,原文直传(前端 hook 兼容旧格式兜底剥引号)
### Fixed
- WhisperTranscriber 在半成品模型目录上死循环报 `Unable to open file 'model.bin'`:判定从「目录存在」改为「`model.bin` 落盘」半成品目录会被识别并重新下载PR `fix/backend-deploy-resilience`
- `/api/deploy_status` 在没装 torch 的部署上 `ModuleNotFoundError: No module named 'torch'` 500torch 改 try/except未装时返回 `{available: false, torch_installed: false}`transcriber 配置 + ffmpeg 也都裹 try单项失败不再打死整个监控页同上 PR
- `routers/config._check_whisper_model_exists` 同步改用 `model.bin` 判定,避免「已下载」状态在监控页误报
## [2.1.4] - 2026-05-07
CI 工程化修复,无运行时行为变化。

View File

@@ -3,7 +3,7 @@
<p align="center">
<img src="./doc/icon.svg" alt="BiliNote Banner" width="50" height="50" />
</p>
<h1 align="center" > BiliNote v2.1.4</h1>
<h1 align="center" > BiliNote v2.2.0</h1>
</div>
<p align="center"><i>AI 视频笔记生成工具 让 AI 为你的视频做笔记</i></p>
@@ -53,6 +53,16 @@ BiliNote 是一个开源的 AI 视频笔记助手支持通过哔哩哔哩、Y
- 笔记顶部视频封面 Banner 展示
- 工作区和生成历史面板支持折叠/展开
### v2.2.0 新增
- **浏览器插件**笔记选项与 web 端完整对齐style 9 个预设下拉、format 4 个 checkbox、extras 文本框、多模态视频理解开关
- **桌面客户端**首启 4 步引导(连通自检 → 供应商/模型 → 转写引擎 → Cookie 提示)
- **桌面客户端**右下角后端运行状态指示,点开看日志、一键重启
- **桌面客户端**启动期主动检测中文 / 空格 / 不可写安装路径,弹横幅告警
- Whisper 默认 size 从 medium~1.5GB)改为 tiny~75MB切大模型时显式 confirm
-whisper 半成品模型目录死循环;`/deploy_status` 在没装 torch 的部署 500
- 详见 [CHANGELOG.md](./CHANGELOG.md)
### v2.1.4 修订
- CI桌面端 Tauri 构建去掉 Linux17m+ 慢线退役Linux 用户继续走 Docker 镜像)

View File

@@ -87,10 +87,13 @@ _downloading: dict[str, str] = {} # model_size -> status ("downloading" | "done
def _check_whisper_model_exists(model_size: str, subdir: str = "whisper") -> bool:
"""检查指定 whisper 模型是否已下载到本地。"""
"""检查指定 whisper 模型是否已完整下载到本地。
只看目录会把"上次下载中断剩下的空目录"误判为已下载;以 model.bin 落盘为准。
"""
model_dir = get_model_dir(subdir)
model_path = os.path.join(model_dir, f"whisper-{model_size}")
return Path(model_path).exists()
model_path = Path(os.path.join(model_dir, f"whisper-{model_size}"))
return (model_path / "model.bin").exists()
@router.get("/transcriber_models_status")
@@ -227,30 +230,49 @@ async def sys_check():
@router.get("/deploy_status")
async def deploy_status():
"""返回部署监控所需的所有状态信息"""
import torch
"""返回部署监控所需的所有状态信息
每一项都做容错torch 没装 / Whisper 配置读取失败 / FFmpeg 不可用 都不应让整个
endpoint 500 把监控页打死。
"""
import os
# CUDA 状态
cuda_available = torch.cuda.is_available()
cuda_info = {
"available": cuda_available,
"version": torch.version.cuda if cuda_available else None,
"gpu_name": torch.cuda.get_device_name(0) if cuda_available else None,
}
# Whisper 模型状态(从配置文件读取,与前端设置同步)
transcriber_cfg = transcriber_config_manager.get_config()
model_size = transcriber_cfg["whisper_model_size"]
transcriber_type = transcriber_cfg["transcriber_type"]
# CUDA 状态torch 是 fast-whisper 路径才需要的依赖;轻量部署可能没装
cuda_info = {"available": False, "version": None, "gpu_name": None, "torch_installed": False}
try:
import torch
cuda_info["torch_installed"] = True
cuda_available = torch.cuda.is_available()
cuda_info["available"] = cuda_available
if cuda_available:
cuda_info["version"] = torch.version.cuda
try:
cuda_info["gpu_name"] = torch.cuda.get_device_name(0)
except Exception as e:
logger.warning(f"读取 GPU 名称失败: {e}")
except ImportError:
# torch 未安装:保持 available=False插件 / web 监控页能识别
pass
except Exception as e:
logger.warning(f"CUDA 状态检测失败: {e}")
# Whisper 配置(任何失败回落到空,监控页不应被这个打死)
model_size = None
transcriber_type = None
try:
transcriber_cfg = transcriber_config_manager.get_config()
model_size = transcriber_cfg.get("whisper_model_size")
transcriber_type = transcriber_cfg.get("transcriber_type")
except Exception as e:
logger.warning(f"读取转写器配置失败: {e}")
# FFmpeg 状态
try:
ensure_ffmpeg_or_raise()
ffmpeg_ok = True
except:
except Exception:
ffmpeg_ok = False
return R.success(data={
"backend": {"status": "running", "port": int(os.getenv("BACKEND_PORT", 8483))},
"cuda": cuda_info,

View File

@@ -50,12 +50,20 @@ class WhisperTranscriber(Transcriber):
model_dir = get_model_dir("whisper")
model_path = os.path.join(model_dir, f"whisper-{model_size}")
if not Path(model_path).exists():
logger.info(f"模型 whisper-{model_size} 不存在,开始下载...")
# 仅看目录存在不够:一次失败的 / 中断的下载会留下空 / 半成品目录,
# 后面 WhisperModel 加载时会以 'Unable to open file model.bin' 抛错。
# 必须以 model.bin 这个核心权重文件落盘为准。
model_bin = Path(model_path) / "model.bin"
if not model_bin.exists():
if Path(model_path).exists():
logger.warning(
f"检测到 {model_path} 目录存在但缺少 model.bin可能上次下载中断将重新拉取"
)
else:
logger.info(f"模型 whisper-{model_size} 不存在,开始下载...")
repo_id = MODEL_MAP[model_size]
model_path = snapshot_download(
repo_id,
local_dir=model_path,
)
logger.info("模型下载完成")