mirror of
https://github.com/JefferyHcool/BiliNote.git
synced 2026-05-18 15:37:36 +08:00
两处部署反馈来的问题:
1. WhisperTranscriber 反复抛 'Unable to open file model.bin in
model whisper-base'
· 原因:__init__ 只看目录是否存在判定模型已下载(Path(model_path).exists()),
但首次下载若中断 / 网络异常会留下空 / 半成品目录,下次启动绕过下载分支直接
进 WhisperModel 加载,于是死循环报错
· 修:判定条件换成 'model.bin' 落盘存在;目录在但 model.bin 缺失时打 warn
并触发重新下载
· routers/config.py 的 _check_whisper_model_exists 同步改用 model.bin 判定,
避免「已下载」状态在监控页误报
2. /api/deploy_status 在没装 torch 的部署上 500
ModuleNotFoundError: No module named 'torch'
· 原因:endpoint 顶部直接 import torch,仅 fast-whisper 才用得到的依赖被强制为
全局必需。轻量部署 / 用户切到 Groq / 必剪 / 快手 在线引擎时无 torch 也合理
· 修:torch 改为 try/except,未装或 cuda 检测异常时返回
{available: false, torch_installed: false};同时把 transcriber 配置 +
ffmpeg 都包在 try 里,保证整个监控 endpoint 不会被任一子项打死
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
137 lines
4.6 KiB
Python
137 lines
4.6 KiB
Python
from faster_whisper import WhisperModel
|
||
|
||
from app.decorators.timeit import timeit
|
||
from app.models.transcriber_model import TranscriptSegment, TranscriptResult
|
||
from app.transcriber.base import Transcriber
|
||
from app.utils.env_checker import is_cuda_available, is_torch_installed
|
||
from app.utils.logger import get_logger
|
||
from app.utils.path_helper import get_model_dir
|
||
|
||
from events import transcription_finished
|
||
from pathlib import Path
|
||
import os
|
||
from tqdm import tqdm
|
||
from modelscope import snapshot_download
|
||
|
||
|
||
'''
|
||
Size of the model to use (tiny, tiny.en, base, base.en, small, small.en, distil-small.en, medium, medium.en, distil-medium.en, large-v1, large-v2, large-v3, large, distil-large-v2, distil-large-v3, large-v3-turbo, or turbo
|
||
'''
|
||
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',
|
||
}
|
||
|
||
class WhisperTranscriber(Transcriber):
|
||
# TODO:修改为可配置
|
||
def __init__(
|
||
self,
|
||
model_size: str = "base",
|
||
device: str = 'cpu',
|
||
compute_type: str = None,
|
||
cpu_threads: int = 1,
|
||
):
|
||
if device == 'cpu' or device is None:
|
||
self.device = 'cpu'
|
||
else:
|
||
self.device = "cuda" if self.is_cuda() else "cpu"
|
||
if device == 'cuda' and self.device == 'cpu':
|
||
print('没有 cuda 使用 cpu进行计算')
|
||
|
||
self.compute_type = compute_type or ("float16" if self.device == "cuda" else "int8")
|
||
|
||
model_dir = get_model_dir("whisper")
|
||
model_path = os.path.join(model_dir, 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("模型下载完成")
|
||
|
||
self.model = WhisperModel(
|
||
model_size_or_path=model_path,
|
||
device=self.device,
|
||
compute_type=self.compute_type,
|
||
download_root=model_dir
|
||
)
|
||
@staticmethod
|
||
def is_torch_installed() -> bool:
|
||
try:
|
||
import torch
|
||
return True
|
||
except ImportError:
|
||
return False
|
||
|
||
@staticmethod
|
||
def is_cuda() -> bool:
|
||
try:
|
||
if is_cuda_available():
|
||
print(" CUDA 可用,使用 GPU")
|
||
return True
|
||
elif is_torch_installed():
|
||
print(" 只装了 torch,但没有 CUDA,用 CPU")
|
||
return False
|
||
else:
|
||
print(" 还没有安装 torch,请先安装")
|
||
return False
|
||
|
||
except ImportError:
|
||
return False
|
||
|
||
@timeit
|
||
def transcript(self, file_path: str) -> TranscriptResult:
|
||
try:
|
||
|
||
segments_raw, info = self.model.transcribe(file_path)
|
||
|
||
segments = []
|
||
full_text = ""
|
||
|
||
for seg in segments_raw:
|
||
text = seg.text.strip()
|
||
full_text += text + " "
|
||
segments.append(TranscriptSegment(
|
||
start=seg.start,
|
||
end=seg.end,
|
||
text=text
|
||
))
|
||
|
||
result= TranscriptResult(
|
||
language=info.language,
|
||
full_text=full_text.strip(),
|
||
segments=segments,
|
||
raw=info
|
||
)
|
||
# self.on_finish(file_path, result)
|
||
return result
|
||
except Exception as e:
|
||
print(f"转写失败:{e}")
|
||
|
||
|
||
def on_finish(self,video_path:str,result: TranscriptResult)->None:
|
||
print("转写完成")
|
||
transcription_finished.send({
|
||
"file_path": video_path,
|
||
})
|
||
|