本项目基本由 VSCode Copilot with GPT5.2 完成(Vibe coding 大法好)
声明:本项目的语音转写能力由 Faster-Whisper提供。
这是一个基于 faster-whisper 的本地网页:上传音频/视频文件,然后在浏览器里拿到转写文本(含时间戳分段)。
- 网页端上传录音文件(
wav/mp3/m4a/flac/mp4等) - 服务端调用
faster_whisper.WhisperModel.transcribe() - 返回:
- 纯文本(拼接)
- 带时间戳的分段列表
- 检测语言、时长信息
建议使用虚拟环境。
- Python 依赖:见
requirements.txt - 系统依赖:建议安装 FFmpeg(用于解码 m4a/mp3 等)
当前 auth.py 强制要求设置以下环境变量,否则服务会启动失败:
APP_USERNAMEAPP_PASSWORDAPP_SESSION_SECRET:建议用随机长字符串(用于 Cookie 签名)
示例(bash):
export APP_USERNAME="admin"
export APP_PASSWORD="admin"
export APP_SESSION_SECRET="please-change-to-a-long-random-string"项目提供了脚本:
run_web.sh:启动 uvicorn(+ 可选 frpc)
当前默认使用
--root-path /audio2text,方便后续挂到站点子路径。
启动后访问(本机):
http://127.0.0.1:8000/audio2text/
服务端可用环境变量:
WHISPER_MODEL_SIZE(默认large-v3)WHISPER_DEVICE(默认cuda,没 GPU 可设成cpu)WHISPER_COMPUTE_TYPE(默认float16,CPU 场景可用int8)
- 打开页面并登录
- 选择音频/视频文件,设置
beam_size、语言、领域关键词 - 点击”上传并转写”——文件自动分块上传(5MB/块),显示实时速度/进度/预估剩余时间
- 上传完成后进入转写阶段,页面轮询任务进度,显示:
- 识别语言/概率/时长
- 转写文本
- 分段时间戳结果
- 转写过程中可点击”终止当前转写”
- 刷新页面会尝试恢复上一次任务结果;若上次上传未完成则自动取消旧任务
适用场景:
- 云服务器(公网、性能弱)跑宝塔/Nginx,只负责 HTTPS/路由
- 内网服务器(性能强)跑本项目 uvicorn
- 通过 frp 把云服务器的某个本地端口转发到内网的 uvicorn
- 云服务器 A:运行
frps(对外/对内网机提供转发表) - 内网服务器 B:运行
frpc,把本机127.0.0.1:8000映射到云服务器 A 的某个端口(例如18000) - 宝塔 Nginx:把
/audio2text/反代到http://127.0.0.1:18000/
- 放行安全组:
7000(frps 控制端口)、18000(转发端口,方案示例) frps.ini示例:
[common]
bind_port = 7000启动 frps 后,等待内网机连接。
本项目目录中的 frpc_web.toml 示例:
serverAddr = "<云服务器A公网IP>"
serverPort = 7000
[[proxies]]
name = "audio2text_18000"
type = "tcp"
localIP = "127.0.0.1"
localPort = 8000
remotePort = 18000内网机上启动:
# 1) 先启动 uvicorn(建议回环 + root-path)
uvicorn app:app --host 127.0.0.1 --port 8000 --root-path /audio2text
# 2) 再启动 frpc
frpc -c ./frpc_web.toml把 /audio2text/ 反代到本机 18000(由 frps 接入并转发到内网机):
location ^~ /audio2text/ {
proxy_pass http://127.0.0.1:18000/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 4096m;
proxy_connect_timeout 6000s;
proxy_send_timeout 6000s;
proxy_read_timeout 6000s;
proxy_buffering off;
client_body_timeout 6000s;
proxy_request_buffering off;
}-
宝塔报 502 且日志含
connect() failed (111: Connection refused):- 说明 Nginx 连接不上 upstream(例如
127.0.0.1:18000没有监听) - 先在云服务器 A 上
ss -ltnp | grep 18000,确认 frps 是否在监听 - 再看 frps/frpc 日志是否已建立 proxy
- 说明 Nginx 连接不上 upstream(例如
-
访问
/audio2text/跳转到/login(不带前缀)导致 404:- 多半是后端重定向写死
/login或未设置--root-path /audio2text - 本项目已修复重定向,并建议始终使用
--root-path /audio2text
- 多半是后端重定向写死
-
上传时报
413 Content Too Large:- 这是 Nginx/宝塔限制了上传体积(不是 FastAPI 报错)
- 在对应
location或server中增大限制,例如:
client_max_body_size 1024m;app.py: FastAPI 后端templates/index.html: 简单前端页面templates/login.html: 登录页auth.py: 简单 Cookie Session(HMAC 签名)progress.py: 内存任务状态(进度/取消/结果)run_web.sh: 启动脚本frpc_web.toml: FRP 客户端示例配置(内网服务器使用)requirements.txt: Python 依赖
GET /:主页面(未登录会重定向到/login)GET /login:登录页
POST /api/login:JSON{username, password}POST /api/logoutGET /api/me:返回当前登录用户及上次任务 ID
Phase 1 — 创建任务(秒返)
POST /api/transcribe_async- JSON body:
{ beam_size, language, initial_prompt, filename } - 返回:
{ task_id }
- JSON body:
Phase 2 — 分块上传
-
POST /api/upload_chunk/{task_id}- form-data:
file(单个 5MB 分块),chunk(块序号) - 返回:
{ ok, chunk }
- form-data:
-
POST /api/upload_done/{task_id}- 无 body
- 组装所有分块 → 启动后台转写线程 → 返回
{ ok }
Phase 3 — 轮询
-
GET /api/task/{task_id}- 返回:
{ task_id, status, progress, message, result? } - status:
queued→awaiting_upload→running→done/error - running 阶段会尽早写入
result.language等 meta 信息
- 返回:
-
POST /api/task/{task_id}/cancel:终止转写(协作式取消)
POST /api/transcribe- form-data:
file,beam_size,language,initial_prompt - 返回:
{ text, segments, language, language_probability, duration, duration_after_vad }
- form-data: