Skip to content

Repository files navigation

mcp-video-transcript

视频/音频转成纯文本的 MCP 服务。给一个媒体 URL,返回转写文本 —— 就这一件事,不含任何业务逻辑。

SenseVoice(阿里 FunASR)做中文识别,ffmpeg 抽音频, 全程本地推理,不调任何云 API。Apple 芯片自动走 MPS(Metal)加速。

为什么是 MCP + 异步

MCP 是请求/响应模型,客户端超时通常只有 30~120s,而一条视频转写要几分钟到几十分钟。 所以这里用异步 submit / poll:

submit_transcribe(media_url)   -> {"job_id": "..."}     # 秒回,后台排队跑
get_transcribe_result(job_id)  -> {"status": "running"} # 轮询
                               -> {"status": "done", "text": "...", "asr_seconds": 42.3}

任何 MCP 客户端都能用(Claude Desktop、自建流水线…)。

工具

工具 说明
submit_transcribe(media_url, title?, engine?) 提交任务,立即返回 job_id
get_transcribe_result(job_id) 查状态/取结果
cancel_transcribe(job_id) 取消尚未开跑的任务
transcribe_status() 服务状态:引擎/设备/队列积压(连通性检查)

media_url 支持 http(s)://(可配 API key 鉴权)与 file:// 本地路径。

安装

brew install ffmpeg           # Ubuntu: apt install ffmpeg
python3.12 -m venv venv       # 需要 Python ≥3.10
./venv/bin/pip install -r requirements.txt
cp config.toml.example config.toml   # 按需改

首次运行会自动下载 SenseVoice 模型(约 1GB)。离线环境请预先下载好模型缓存。

运行

推荐:一键脚本(自动经 launchd 管理,并智能处理端口占用):

./start.sh     # 启动/重启:端口被本程序占用→停旧重启;被别的程序占用→报错不动它
./stop.sh      # 停止(只停本程序,别的程序不动)
./status.sh    # 查看运行状态(launchd 托管 / 端口 / 是否本程序)

start.sh 逻辑:端口空闲→启动;被本程序占用→bootout+kill 旧实例后重启;被其它程序占用→打印占用它的 PID/命令并退出(绝不误杀)。装了 launchd(见下)就走 launchd(开机自启+崩溃自愈),否则后台直接跑。

手动前台跑(调试用;跑前先 ./stop.sh 释放端口):

./venv/bin/python server.py                    # 127.0.0.1:8010
./venv/bin/python server.py --host 0.0.0.0 --preload   # 对局域网开放 + 启动即加载模型

--preload 让模型在启动时加载,首个任务不必等模型初始化。

开机自启(macOS)

cp deploy/com.weft.video-transcript.plist ~/Library/LaunchAgents/   # plist 里路径按本机改
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.weft.video-transcript.plist  # 加载+启动
launchctl list | grep video-transcript      # 查看 PID / 上次退出码(左列是数字=在跑)
tail -f ~/Library/Logs/video-transcript.log # 看日志
launchctl bootout gui/$(id -u)/com.weft.video-transcript  # 停用

装好后日常直接用 ./start.sh / ./stop.sh 即可(内部就走上面这些 launchctl)。

KeepAlive 会在进程崩溃/被杀后自动拉起(ThrottleInterval=30 秒退避)。两个坑:

  • plist 里要显式设 PATH 包含 /opt/homebrew/bin——launchd 默认 PATH 不含它,否则找不到 ffmpeg。
  • 日志路径别放 ~/Documents/:macOS TCC(文稿文件夹隐私保护)会让后台 launchd 打不开该目录下的文件写日志 → job 每次启动立即 退出 78 且不写任何日志(手动跑却正常)。故日志放 ~/Library/Logs/

配置

config.toml(或环境变量 VT_*,优先级更高):

默认 说明
host / port 127.0.0.1 / 8010 监听地址
weft_key 拉取媒体时带的 X-Weft-Key
engine sensevoice ASR 引擎
concurrency 1 同时转写几条
device auto auto/mps/cpu/cuda:0
job_timeout_s 3600 单条最长处理时间

换引擎

engines/ 是可插拔的:实现 load() / transcribe(wav_path) / info(),再在 engines/__init__.py_REGISTRY 登记一行即可。已预留 whisper_cpp (Apple 芯片上 Metal 极快、无 Python/torch 依赖)与 faster-whisper 的位置。

实测性能(Apple M5 / 10 核 / MPS)

指标 实测
模型加载(一次性) ~43s → 所以服务常驻、--preload 启动即加载
转写实时率 0.041x(140.5s 音频 → 5.7s)
折算 1 小时音频 ≈ 2.4 分钟

短音频(<10s)因固定开销 RTF 会差很多(实测 0.23x),长音频才是真实水平。

已知坑

  • macOS 系统代理会拦 127.0.0.1:MCP 客户端(httpx)默认 trust_env=True,本机连本机会被代理 劫持成 502。跑客户端时设 NO_PROXY=127.0.0.1,localhost,或在自己的客户端里用 trust_env=False。 (Linux 服务器一般没这问题。)
  • 不要把模块命名成 queue.py:会 shadow 标准库 queue,依赖链里任何 import queue 都会拿到你的模块。 本项目用的是 jobqueue.py

注意

  • 仅供内网/本机使用,不要直接暴露到公网(没有内建鉴权,鉴权应由反代或调用方网络边界负责)。
  • 转写是 CPU/GPU 密集型;跑在与其它服务同机时建议 concurrency=1 并配合 nice 降优先级。
  • 请遵守内容来源平台的服务条款与版权规定,仅将本工具用于你有权处理的内容。

About

视频文件转写文本MCP服务

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages