从声音,到文字。
记录当下的想法,找回每一句值得留下的话。
把文件放在这里
拖入音频或视频,让 Vox 整理成文字。
MP3 / M4A / WAV / MP4 / MOV 等想法,现在就录下来
停止后先试听,确认再转写。
你的转写 0
上传 → 转码 → 转写 → 完成。处理中的任务会自动更新,完成后点击即可阅读和回听。
文件库
保存过的声音和影像,都在这里。
转写正文
点击任意一句回听系统架构
先看 产品设计、架构与取舍 ↗,或继续深入这条处理链路。
Vox 把一段音频或视频变成可以阅读、可以跳转、可以学习的文字:上传之后自动转码和转写,按句子对齐时间戳,再把文中的生词标出来。
系统架构
用到的技术
服务
- Go
- Gin
- gRPC
- Protocol Buffers
- net/http
对外 HTTP + JSON,服务之间 gRPC。
数据与存储
- PostgreSQL
- pgx 连接池
- goose 迁移
- MinIO
- 阿里云 OSS
每个服务一个库;存储走 S3 兼容接口,预签名 URL 直传。
异步任务
- RabbitMQ
- direct 交换机
- 手动 ack
- prefetch 1
- 竞争消费者
按阶段路由:转码、短转写、长转写各一条队列。
媒体与语音
- ffmpeg
- whisper.cpp
- cgo
- Metal
- ElevenLabs Scribe v2
- ECDICT
同一个 Transcriber 接口,本地和云端两种实现。
前端与入口
- 原生 JavaScript
- nginx 反向代理
- JWT 认证
无框架、无构建;API 同源代理,文件直传和兼容播放依赖对象存储的 CORS 配置。
部署与伸缩
- Docker
- Docker Compose
- Kubernetes
- KEDA
- SSH 隧道
worker 可独立部署;KEDA 示例尚需对齐当前队列,自动扩缩与生产网络隔离需进一步验证。
常见问题
使用上的问题在前,技术选择在后。
使用
支持哪些文件?
常见的音频和视频都可以:mp3、m4a、wav、mp4、mov、mkv、webm 等。上传后会先统一转成 16kHz 单声道 WAV,再交给转写引擎。
要等多久?
处理时间取决于文件长度、队列积压、转写引擎与机器资源。默认将不超过 10 分钟的音频分到云端转写队列,更长的分到本地队列。列表定期查询阶段与结果状态,不代表精确的完成百分比。
支持哪些语言?
默认自动识别,也可以在上传区选择中文、英文、日语等。明确指定语言可帮助引擎选择识别语言,准确度仍受声音质量与模型能力影响。
为什么有的视频在网页里放不了?
浏览器对容器和音轨编码的支持不同。MKV、部分 MOV 或 E-AC3 音轨可能无法直接播放;可以打开原始文件使用本地播放器。普通音频跳转异常时,可尝试播放器下方的“兼容播放”。
生词是怎么挑出来的?
用开源词典 ECDICT 查每个词的考试标签和词频排名,过滤掉中考、高考、四级范围的基础词和最常用的 5000 词,剩下的按出现次数排序。每次打开页面实时计算,不保存。
字幕能导出吗?
可以。阅读页右上角的"更多"里可以下载 SRT 字幕和纯文本,都是根据时间戳在浏览器里当场生成的。
我的文件存在哪里?谁能看到?
文件保存在对象存储中,应用接口按账户校验文件与任务归属。播放时申请有时效的签名链接,过期后可重新获取;持有有效签名链接的人也能访问对应内容,请不要随意分享。
技术
为什么要用消息队列,而不是直接调用?
创建任务后即可返回,耗时的转码与转写由 worker 在后台完成,浏览器无需保持请求等待。队列和消息配置了持久化;数据库与消息一致性、失败重试和死信仍是后续完善项,目前不承诺任务零丢失。
服务之间用 gRPC,为什么生词服务用 HTTP?
协议看调用方。01、02、03 之间是内部的 Go 服务互相调用,gRPC 有强类型接口,性能也好。生词服务要给浏览器和外部程序直接调用,而浏览器不能直接使用 gRPC,所以用 HTTP + JSON。
文件为什么不经过后端服务?
API 和文件服务负责授权与签名,浏览器和 worker 直接读写对象存储。这样大文件字节不用经过 API 网关,业务接口与文件传输分别承担负载;数据库、存储与网络仍各有容量限制。
为什么按时长分成两条队列?
默认以 10 分钟为界,分别为云端与本地转写安排资源,减少长短转写在同一队列中的等待。云端有调用成本,本地有硬件和运行成本;当前分流是可调整的策略,并非经过完整成本与延迟基准验证的最优方案。转码与存储仍是共享环节。
怎么扩容?
转码、短音频转写、长音频转写各有队列和 worker 进程,可以分别增加消费者。实际提升需要结合处理瓶颈、硬件和云端配额验证。仓库里的 KEDA 配置仍是待更新示例,需要先对齐当前队列,再验证自动扩缩容。
Mac 关机了,长任务怎么办?
未被消费的长音频任务会留在持久化队列中,等待对应 worker。重新启动时仍需考虑消息中的签名链接是否有效,以及处理过程是否被中断;自动重试和幂等恢复还需要完善。