12 Commits

Author SHA1 Message Date
huangjianwu
db556b8991 Merge branch 'release/2.3.3'
v2.3.3:预构建镜像持久化数据库/笔记,修升级丢配置与历史。
2026-05-22 14:07:00 +08:00
huangjianwu
b431db545a docs: v2.3.3 CHANGELOG + README + tauri 版本
Docker 镜像数据持久化修复版本。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 14:07:00 +08:00
huangjianwu
25face4b67 Merge branch 'fix/docker-data-persistence' into develop
v2.3.3:预构建镜像持久化数据库/笔记,修升级丢配置与历史。
2026-05-22 14:06:24 +08:00
huangjianwu
edfd6e4765 fix(docker): 预构建镜像持久化数据库/笔记,修升级丢配置与历史
README 的 docker run 只挂了 -v …:/app/backend/data(仅媒体缓存),但
SQLite 库在 /app/backend/bili_note.db、笔记在 /app/backend/note_results
都不在该卷下 → 用户删容器重建(如 docker pull 升级镜像)会丢失 LLM
供应商配置和全部笔记历史。

- Dockerfile.complete:DATABASE_URL 重定向到 /app/backend/data/bili_note.db、
  NOTE_OUTPUT_DIR 改到 data/note_results,并预建 /app/backend/data 目录;
  两条 supervisord environment 行同步(兜底默认值 + %(ENV_*)s 透传)。
- README:docker run 改挂 data(库+笔记)/ config(供应商·Cookie·转写配置)/
  static(截图)/ models(模型缓存,可选)四个数据卷,并警告不要挂整个
  /app/backend(命名卷会固化镜像内代码,导致 docker pull 升级不生效)。
- docker-build.yml:同步发布说明里 echo 的 run 命令。

截图仍留在 /app/backend/static(static_dir 硬编码服务,不能重定向),单独挂卷。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 13:30:42 +08:00
huangjianwu
b53cafda5a Merge branch 'release/2.3.2' into develop
回合并 v2.3.2 版本号。
2026-05-22 11:41:17 +08:00
huangjianwu
adda5fd240 Merge branch 'release/2.3.2'
v2.3.2:修后端启动崩溃(ctranslate2 4.6.0) + whisper 模型路径 + 桌面端版本号。
2026-05-22 11:41:04 +08:00
huangjianwu
3e28f1fe38 docs: v2.3.2 CHANGELOG + README + tauri 版本
docker 启动崩溃 / whisper 模型路径 / 桌面端版本号 修复版本。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 11:40:55 +08:00
huangjianwu
bffa285cd0 Merge branch 'fix/docker-startup-and-qr' into develop
v2.3.2 修复集:
- ctranslate2 4.5.0→4.6.0 修后端启动崩溃(可执行栈 / glibc 2.41)
- whisper 模型下载/加载统一 HF cache 布局
- 桌面端构建版本号从 tag 注入(修产物恒为 2.0.0)
2026-05-22 11:40:21 +08:00
huangjianwu
b740e70068 fix(desktop): 构建时从 tag 注入版本号,修产物版本恒为 2.0.0
桌面端构建产物(.dmg/.msi 文件名 + app 内部版本)一直是 2.0.0:
Tauri 取 tauri.conf.json 的静态 version 字段作为产物版本,而 Release
工作流只把 tag 名用作 Release 标题,没同步到 conf → 产物版本与 Release
版本错位。

在 pnpm tauri build 前新增 Sync version 步骤,从 github.ref_name(形如
v2.3.2,去掉前缀 v)注入版本到 tauri.conf.json。以后每次 tag 发版自动
对齐;workflow_dispatch 手动构建无 tag 时跳过,保留静态值不破坏。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 11:40:04 +08:00
huangjianwu
261c95cf12 fix(transcriber): whisper 模型下载/加载统一走 HF cache 布局
此前用 modelscope 下到自定义目录 whisper-{size}/ 再把该路径传给
WhisperModel。但 faster-whisper 1.1.1 只要 path 含 '/' 就当成 HF
repo_id 处理,没有「本地目录直接返回」分支 → 在线请求失败后 fallback
local_files_only,又因 modelscope 布局命不中 HF cache → LocalEntryNotFound,
误导用户以为是「离线模式」。

改为下载与加载路径对齐:
- 下载:huggingface_hub.snapshot_download(cache_dir=model_dir),落到 HF
  cache 布局 models--Systran--faster-whisper-{size}/snapshots/<hash>/
- 加载:WhisperModel(model_size_or_path=size, download_root=model_dir),
  让 faster-whisper 自己映射到 Systran/faster-whisper-* 并命中同一 cache
- 完整性检测 / 损坏自愈(_purge_cache) 同步按 HF cache 布局,并兼容老
  modelscope 目录(向后兼容已下载的老用户)

HF_ENDPOINT 已在 Dockerfile 指向 hf-mirror.com,国内可用。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 11:27:03 +08:00
huangjianwu
1cc7f38e14 fix(backend): 升级 ctranslate2 4.5.0→4.6.0 修复后端启动崩溃
Docker 容器反复重启,启动即报:
  ImportError: libctranslate2-*.so.4.5.0: cannot enable executable
  stack as shared object requires: Invalid argument

根因:ctranslate2 4.5.0 预编译 wheel 把共享库标记为「需要可执行栈」,
新内核 / glibc 2.41+ 加载时拒绝并返回 EINVAL。faster-whisper 在
whisper.py 顶层 import,import 失败直接拖垮整个后端启动 → 重启死循环。

ctranslate2 4.6.0 加入 noexecstack 链接标志(OpenNMT/CTranslate2 #1852、
#1861)从 wheel 层根治。faster-whisper 1.1.1 依赖 ctranslate2<5,>=4.0,
4.6.0 兼容;同时覆盖 web / GPU / 桌面 三条构建链。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-22 11:26:49 +08:00
huangjianwu
7fffd6873b Merge branch 'release/2.3.1' into develop
回合并 v2.3.1 版本号(CHANGELOG + README)。
2026-05-22 10:51:49 +08:00
9 changed files with 130 additions and 71 deletions

View File

@@ -66,6 +66,9 @@ jobs:
echo "Run the container:"
echo " docker run -d -p 80:80 \\"
echo " -v bilinote-data:/app/backend/data \\"
echo " -v bilinote-config:/app/backend/config \\"
echo " -v bilinote-static:/app/backend/static \\"
echo " -v bilinote-models:/app/backend/models \\"
echo " --name bilinote \\"
echo " ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest"
echo ""

View File

@@ -79,6 +79,19 @@ jobs:
key: ${{ runner.os }}-cargo-${{ hashFiles('BillNote_frontend/src-tauri/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
# 从 tag 注入版本号到 tauri.conf.jsonTauri 取该文件的静态 version 作为
# 产物版本,不同步的话构建产物会恒为 conf 里写死的值(此前的 2.0.0)。
# github.ref_name 形如 v2.3.2,去掉前缀 v。workflow_dispatch无 tag时跳过保留静态值。
- name: Sync version from tag
if: startsWith(github.ref, 'refs/tags/v')
working-directory: BillNote_frontend
shell: bash
run: |
VERSION="${GITHUB_REF_NAME#v}"
echo "Injecting version $VERSION into tauri.conf.json"
node -e "const f='src-tauri/tauri.conf.json'; const fs=require('fs'); const j=JSON.parse(fs.readFileSync(f,'utf8')); j.version=process.argv[1]; fs.writeFileSync(f, JSON.stringify(j,null,2)+'\n');" "$VERSION"
node -e "console.log('tauri.conf.json version =', require('./src-tauri/tauri.conf.json').version)"
# 打包 Tauri 应用
- name: Build Tauri App
working-directory: BillNote_frontend

View File

@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "BiliNote",
"version": "2.0.0",
"version": "2.3.3",
"identifier": "com.jefferyhuang.bilinote",
"build": {
"frontendDist": "../dist",

View File

@@ -2,6 +2,20 @@
本项目所有重要变更记录于此。格式参考 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.1.0/),遵循 [语义化版本](https://semver.org/lang/zh-CN/)。
## [2.3.3] - 2026-05-22
### Fixed
- **预构建 Docker 镜像数据持久化**:文档的 `docker run` 只挂了 `data/`(媒体缓存),而 SQLite 数据库LLM 供应商配置 + 笔记历史)和笔记文件不在该卷下,导致删除 / 升级容器时丢失配置与历史。现将数据库重定向到 `/app/backend/data/bili_note.db`、笔记到 `data/note_results`(随 data 卷持久化README 更新为挂载 `data` / `config` / `static` / `models` 四个数据卷,并提示**勿**挂整个 `/app/backend`(命名卷会固化镜像内代码,导致 `docker pull` 升级不生效)。`docker-compose` 路径本就正确(`./backend:/app` 整目录绑挂),未受影响。
## [2.3.2] - 2026-05-22
### Fixed
- **后端启动崩溃Docker**`python:3.11-slim` 基础镜像升级到 Debian 13 / glibc 2.41 后,`ctranslate2` 4.5.0 预编译库带「可执行栈」标记被 glibc 拒绝加载(`cannot enable executable stack ... Invalid argument`)。由于 `from faster_whisper import WhisperModel` 在顶层 importimport 失败直接拖垮整个后端启动 → 容器反复重启。升级 `ctranslate2` 4.5.0→4.6.0wheel 加入 `noexecstack` 链接标志,从二进制层根治)
- **whisper 模型误报「离线模式找不到模型」**下载modelscope 自定义目录与加载faster-whisper HF cache布局不一致导致命不中缓存。统一为下载 / 加载 / 完整性检测 / 损坏自愈都走 HF cache 布局,并向后兼容老 modelscope 目录
- **桌面端构建产物版本恒为 2.0.0**Release 工作流在 `pnpm tauri build` 前从 git tag 注入版本号到 `tauri.conf.json`,使产物版本与 Release 版本对齐
## [2.3.1] - 2026-05-22
### Changed

View File

@@ -95,14 +95,16 @@ COPY ./nginx/default.conf /etc/nginx/conf.d/default.conf
# 在 [supervisord] 块用 environment= 设兜底默认值;在 [program:backend] 用
# %(ENV_*)s 显式引用,等价于「把 host 通过 docker run -e 或 env_file 传进来的
# 变量再透传给 python main.py」。漏掉这一步就是用户「改 .env 没反应」的根因。
RUN mkdir -p /var/log/supervisor
# /app/backend/data 用于持久化数据库与笔记(见下方 DATABASE_URL / NOTE_OUTPUT_DIR
# 预建好目录,避免不挂卷启动时 sqlite 因父目录不存在而创建库失败。
RUN mkdir -p /var/log/supervisor /app/backend/data
COPY <<EOF /etc/supervisor/conf.d/supervisord.conf
[supervisord]
nodaemon=true
user=root
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
environment=BACKEND_PORT="8483",BACKEND_HOST="0.0.0.0",TRANSCRIBER_TYPE="fast-whisper",WHISPER_MODEL_SIZE="tiny",FFMPEG_BIN_PATH="",HF_ENDPOINT="https://hf-mirror.com",STATIC="/static",OUT_DIR="./static/screenshots",DATA_DIR="data",NOTE_OUTPUT_DIR="note_results",IMAGE_BASE_URL="/static/screenshots",ENV="production",GROQ_TRANSCRIBER_MODEL="whisper-large-v3-turbo"
environment=BACKEND_PORT="8483",BACKEND_HOST="0.0.0.0",TRANSCRIBER_TYPE="fast-whisper",WHISPER_MODEL_SIZE="tiny",FFMPEG_BIN_PATH="",HF_ENDPOINT="https://hf-mirror.com",STATIC="/static",OUT_DIR="./static/screenshots",DATA_DIR="data",NOTE_OUTPUT_DIR="data/note_results",DATABASE_URL="sqlite:////app/backend/data/bili_note.db",IMAGE_BASE_URL="/static/screenshots",ENV="production",GROQ_TRANSCRIBER_MODEL="whisper-large-v3-turbo"
[program:nginx]
command=nginx -g "daemon off;"
@@ -118,7 +120,7 @@ stdout_logfile=/var/log/supervisor/backend.log
stderr_logfile=/var/log/supervisor/backend.log
autorestart=true
priority=20
environment=BACKEND_PORT="%(ENV_BACKEND_PORT)s",BACKEND_HOST="%(ENV_BACKEND_HOST)s",TRANSCRIBER_TYPE="%(ENV_TRANSCRIBER_TYPE)s",WHISPER_MODEL_SIZE="%(ENV_WHISPER_MODEL_SIZE)s",FFMPEG_BIN_PATH="%(ENV_FFMPEG_BIN_PATH)s",HF_ENDPOINT="%(ENV_HF_ENDPOINT)s",STATIC="%(ENV_STATIC)s",OUT_DIR="%(ENV_OUT_DIR)s",DATA_DIR="%(ENV_DATA_DIR)s",NOTE_OUTPUT_DIR="%(ENV_NOTE_OUTPUT_DIR)s",IMAGE_BASE_URL="%(ENV_IMAGE_BASE_URL)s",ENV="%(ENV_ENV)s",GROQ_TRANSCRIBER_MODEL="%(ENV_GROQ_TRANSCRIBER_MODEL)s"
environment=BACKEND_PORT="%(ENV_BACKEND_PORT)s",BACKEND_HOST="%(ENV_BACKEND_HOST)s",TRANSCRIBER_TYPE="%(ENV_TRANSCRIBER_TYPE)s",WHISPER_MODEL_SIZE="%(ENV_WHISPER_MODEL_SIZE)s",FFMPEG_BIN_PATH="%(ENV_FFMPEG_BIN_PATH)s",HF_ENDPOINT="%(ENV_HF_ENDPOINT)s",STATIC="%(ENV_STATIC)s",OUT_DIR="%(ENV_OUT_DIR)s",DATA_DIR="%(ENV_DATA_DIR)s",NOTE_OUTPUT_DIR="%(ENV_NOTE_OUTPUT_DIR)s",DATABASE_URL="%(ENV_DATABASE_URL)s",IMAGE_BASE_URL="%(ENV_IMAGE_BASE_URL)s",ENV="%(ENV_ENV)s",GROQ_TRANSCRIBER_MODEL="%(ENV_GROQ_TRANSCRIBER_MODEL)s"
EOF
# 修改 nginx 配置以使用本地 backend

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.3.1</h1>
<h1 align="center" > BiliNote v2.3.3</h1>
</div>
<p align="center"><i>AI 视频笔记生成工具 让 AI 为你的视频做笔记</i></p>
@@ -163,10 +163,17 @@ docker pull ghcr.io/jefferyhcool/bilinote:latest
docker run -d -p 80:80 \
-v bilinote-data:/app/backend/data \
-v bilinote-config:/app/backend/config \
-v bilinote-static:/app/backend/static \
-v bilinote-models:/app/backend/models \
--name bilinote \
ghcr.io/jefferyhcool/bilinote:latest
```
上面四个卷分别持久化:`data`SQLite 数据库 + 生成的笔记)、`config`LLM 供应商配置 / Cookie / 转写设置)、`static`(笔记引用的视频截图)、`models`Whisper 模型缓存,可选,避免每次重新下载)。这样 `docker pull` 升级新镜像、删旧容器重建后,配置和历史都不会丢。
> ⚠️ **不要**用 `-v 卷名:/app/backend` 挂整个后端目录——命名卷会用首次启动时的镜像内容固化,之后 `docker pull` 升级也会被旧代码盖住,导致「升级不生效」。只挂上面这些数据子目录即可。
访问:`http://localhost`
也可以使用 docker-compose 本地构建:
@@ -302,10 +309,17 @@ docker pull ghcr.io/jefferyhcool/bilinote:latest
# 运行容器
docker run -d -p 80:80 \
-v bilinote-data:/app/backend/data \
-v bilinote-config:/app/backend/config \
-v bilinote-static:/app/backend/static \
-v bilinote-models:/app/backend/models \
--name bilinote \
ghcr.io/jefferyhcool/bilinote:latest
```
上面四个卷分别持久化:`data`SQLite 数据库 + 生成的笔记)、`config`LLM 供应商配置 / Cookie / 转写设置)、`static`(笔记引用的视频截图)、`models`Whisper 模型缓存,可选,避免每次重新下载)。这样 `docker pull` 升级新镜像、删旧容器重建后,配置和历史都不会丢。
> ⚠️ **不要**用 `-v 卷名:/app/backend` 挂整个后端目录——命名卷会用首次启动时的镜像内容固化,之后 `docker pull` 升级也会被旧代码盖住,导致「升级不生效」。只挂上面这些数据子目录即可。
访问:`http://localhost`
也可以使用 docker-compose 本地构建:

View File

@@ -119,12 +119,21 @@ _downloading: dict[str, str] = {} # model_size -> status ("downloading" | "done
def _check_whisper_model_exists(model_size: str, subdir: str = "whisper") -> bool:
"""检查指定 whisper 模型是否已下载完整到本地。
必须 model.bin 落盘才算完成,仅有空目录或半成品不能算「已下载」——
否则监控页会显示绿勾但加载时报「Unable to open file 'model.bin'」。
faster-whisper 把模型缓存在 HF cache 布局下:
<model_dir>/models--Systran--faster-whisper-{size}/snapshots/<hash>/model.bin
必须能在某个 snapshot 目录里找到 model.bin 才算完成。
(历史 modelscope 布局 <model_dir>/whisper-{size}/model.bin 也兼容识别。)
"""
model_dir = get_model_dir(subdir)
model_path = os.path.join(model_dir, f"whisper-{model_size}")
return (Path(model_path) / "model.bin").exists()
model_dir = Path(get_model_dir(subdir))
# HF cache 布局
hf_repo_dir = model_dir / f"models--Systran--faster-whisper-{model_size}" / "snapshots"
if hf_repo_dir.exists():
for snapshot in hf_repo_dir.iterdir():
if (snapshot / "model.bin").exists():
return True
# 历史 modelscope 布局(向后兼容老用户)
legacy = model_dir / f"whisper-{model_size}" / "model.bin"
return legacy.exists()
def _check_mlx_whisper_model_exists(model_size: str) -> bool:
@@ -189,24 +198,37 @@ class ModelDownloadRequest(BaseModel):
def _do_download_whisper(model_size: str):
"""后台下载 faster-whisper 模型。"""
from app.transcriber.whisper import MODEL_MAP
from modelscope import snapshot_download
"""后台下载 faster-whisper 模型。
直接走 huggingface_hub.snapshot_download把模型放到 HF cache 布局里——
这样 faster-whisper 加载时WhisperModel(model_size_or_path=size_name,
download_root=model_dir))能直接命中缓存,跟加载路径完全对齐。
"""
from huggingface_hub import snapshot_download
try:
_downloading[model_size] = "downloading"
model_dir = get_model_dir("whisper")
model_path = os.path.join(model_dir, f"whisper-{model_size}")
# 用 model.bin 判定而非目录存在:半成品目录不能算「已下载」
if (Path(model_path) / "model.bin").exists():
# 已经下好就不重复下
if _check_whisper_model_exists(model_size, "whisper"):
_downloading[model_size] = "done"
return
repo_id = MODEL_MAP.get(model_size)
if not repo_id:
_downloading[model_size] = "failed"
return
logger.info(f"开始下载 whisper 模型: {model_size}")
snapshot_download(repo_id, local_dir=model_path)
repo_id = f"Systran/faster-whisper-{model_size}"
logger.info(f"开始下载 whisper 模型: {repo_id}")
# 跟 faster-whisper utils.py 用同样的 allow_patterns避免多下无关文件
# 不传 local_dir 让它走 HF 默认 cache 布局(与加载逻辑对齐)
snapshot_download(
repo_id,
cache_dir=model_dir,
allow_patterns=[
"config.json",
"preprocessor_config.json",
"model.bin",
"tokenizer.json",
"vocabulary.*",
],
)
logger.info(f"whisper 模型下载完成: {model_size}")
_downloading[model_size] = "done"
except Exception as e:

View File

@@ -11,8 +11,6 @@ from events import transcription_finished
from pathlib import Path
import os
import shutil
from tqdm import tqdm
from modelscope import snapshot_download
'''
@@ -20,19 +18,16 @@ from modelscope import snapshot_download
'''
logger=get_logger(__name__)
MODEL_MAP={
"tiny": "pengzhendong/faster-whisper-tiny",
'base':'pengzhendong/faster-whisper-base',
'small':'pengzhendong/faster-whisper-small',
'medium':'pengzhendong/faster-whisper-medium',
'large-v1':'pengzhendong/faster-whisper-large-v1',
'large-v2':'pengzhendong/faster-whisper-large-v2',
'large-v3':'pengzhendong/faster-whisper-large-v3',
'large-v3-turbo':'pengzhendong/faster-whisper-large-v3-turbo',
}
# 历史遗留:之前用 modelscope 下载到自定义目录然后把路径传给 WhisperModel。
# 但 faster-whisper 1.1.1 的 download_modelutils.py:76逻辑是
# 只要 size_or_id 里含 "/" 就当 HF repo_id 处理,没有「本地目录直接返回」分支。
# 我们传 /app/models/whisper/whisper-tiny 进去 → 被当成不存在的 HF repo →
# 在线请求失败 → fallback local_files_only=True → HF cache 找不到(因为是
# modelscope 目录布局不是 HF→ LocalEntryNotFoundError误导说"离线模式"。
# 解法:彻底让 faster-whisper 自己处理下载——传 size name配 download_root
# 作为 HF cache 根目录HF_ENDPOINT 已经在 Dockerfile 里指到 hf-mirror.com
# 国内能用。删掉 modelscope 那一套,避免布局不匹配。
class WhisperTranscriber(Transcriber):
# TODO:修改为可配置
def __init__(
self,
model_size: str = "base",
@@ -48,44 +43,40 @@ class WhisperTranscriber(Transcriber):
print('没有 cuda 使用 cpu进行计算')
self.compute_type = compute_type or ("float16" if self.device == "cuda" else "int8")
self.model_size = model_size
model_dir = get_model_dir("whisper")
model_path = os.path.join(model_dir, f"whisper-{model_size}")
repo_id = MODEL_MAP[model_size]
# 第一步:目录 / model.bin 不在 → 下载。
# 关键判据用 model.bin 而不是目录存在:首次下载若被打断(网络中断 / 磁盘满 /
# 容器被 kill会留下半成品目录只看目录存在会跳过下载。
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} 不存在,开始下载...")
model_path = snapshot_download(repo_id, local_dir=model_path)
logger.info("模型下载完成")
# 第二步加载。model.bin 可能存在但【内容截断】(下载到一半被 kill
# 此时 WhisperModel() 会抛 "File model.bin is incomplete: failed to read a buffer..."。
# 捕获后删掉损坏目录、重新下载、再试一次——自愈,避免 500 死循环。
try:
self.model = WhisperModel(
model_size_or_path=model_path,
device=self.device,
compute_type=self.compute_type,
download_root=model_dir,
)
self.model = self._build_model(model_size, model_dir)
except Exception as e:
logger.warning(f"加载 whisper-{model_size} 失败(疑似模型文件损坏 / 截断):{e};删除后重新下载")
shutil.rmtree(model_path, ignore_errors=True)
model_path = snapshot_download(repo_id, local_dir=model_path)
logger.info("模型重新下载完成,重试加载")
self.model = WhisperModel(
model_size_or_path=model_path,
device=self.device,
compute_type=self.compute_type,
download_root=model_dir,
)
# 自愈:损坏 / 截断 / 半成品 cache → 删掉对应 HF cache 重下一次
logger.warning(f"加载 whisper-{model_size} 失败:{e};清理 cache 后重新下载")
self._purge_cache(model_dir, model_size)
self.model = self._build_model(model_size, model_dir)
def _build_model(self, model_size: str, model_dir: str) -> WhisperModel:
return WhisperModel(
model_size_or_path=model_size, # 传 size name让 faster-whisper 自己映射到 Systran/faster-whisper-*
device=self.device,
compute_type=self.compute_type,
download_root=model_dir,
)
@staticmethod
def _purge_cache(model_dir: str, model_size: str) -> None:
"""删掉 HF cache 里这个 size 对应的 snapshot 目录,强制下次重新下载。
HF cache 布局:<model_dir>/models--Systran--faster-whisper-{size}/
没找到也不报错——可能用户改了 endpoint 或者 cache 布局变了。
"""
candidates = [
Path(model_dir) / f"models--Systran--faster-whisper-{model_size}",
Path(model_dir) / f"whisper-{model_size}", # 历史 modelscope 目录,顺手清掉
]
for path in candidates:
if path.exists():
logger.info(f"清理损坏 cache: {path}")
shutil.rmtree(path, ignore_errors=True)
@staticmethod
def is_torch_installed() -> bool:
try:

View File

@@ -24,7 +24,7 @@ click-repl==0.3.0
colorama==0.4.6
coloredlogs==15.0.1
cssselect2==0.8.0
ctranslate2==4.5.0
ctranslate2==4.6.0
distro==1.9.0
dnspython==2.7.0
email_validator==2.2.0