Skip to content

About

🎬 浏览器内本地 Whisper 语音识别提取视频字幕,并自动总结内容导出文本文档(纯前端,视频不上传)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

🎬 Video2Text Studio

导入视频 → 本地语音识别提取字幕 → 自动总结内容 → 导出文本文档。 纯前端、零后端、零 API 费用。视频不会上传到任何服务器,语音识别在你的浏览器里完成。

Deploy to GitHub Pages License: MIT

🌐 在线使用:https://polaris929-cloud.github.io/Video2Text-Studio/


✨ 功能

功能 说明
🎥 视频导入 拖拽或选择本地视频/音频文件,支持 MP4 / MOV / WebM / MP3 / M4A / WAV 等浏览器可解码的格式
🗣️ 语音识别 基于 Whisper(transformers.js + ONNX Runtime Web),在浏览器内本地推理,无需 API Key
🌍 多语言 中文、英文、日语、韩语、粤语等 15 种语言,可自动检测
⚡ 硬件加速 自动优先使用 WebGPU,不支持时回退 WASM(CPU);所选精度缺失时会自动换用其它精度,不会卡住
📝 字幕文稿 带时间戳的字幕列表,点击任意一条可跳转到视频对应位置播放,支持搜索
🧠 内容总结 默认本地抽取式摘要(TextRank/MMR + 关键词,免配置);也可接入任意 OpenAI 兼容接口获得 AI 生成式摘要
📄 导出文档 一键导出 Markdown 文本文档、纯文本 TXT、字幕 SRT / VTT、结构化 JSON
🔒 隐私优先 视频、音频、字幕全程留在本机;唯一的联网行为是下载模型与可选的 AI 摘要
💾 断点保留 识别结果自动存在浏览器本地,刷新页面不会丢;模型也会被浏览器缓存,第二次使用无需重新下载

🚀 快速开始

方式一:直接用在线版(推荐)

打开 https://polaris929-cloud.github.io/Video2Text-Studio/ 即可,无需安装任何东西。

首次使用需要联网下载语音模型(Tiny 约 40 MB / Base 约 80 MB),之后会被浏览器缓存。

方式二:本地运行

git clone https://github.com/Polaris929-cloud/Video2Text-Studio.git
cd Video2Text-Studio
npm install
npm run dev          # 打开 http://127.0.0.1:5173

构建静态产物:

npm run build        # 类型检查 + 构建,输出到 dist/
npm run preview      # 本地预览构建结果
npm test             # 运行纯算法自测(导出格式 / 摘要算法)

dist/ 是纯静态文件,可以丢到任意静态托管(GitHub Pages / Vercel / Netlify / Nginx)。

如果 vite preview 在你的机器上不可用,仓库还带了一个零依赖的静态服务器:

node scripts/serve.mjs 4173 dist   # 然后打开 http://127.0.0.1:4173/

方式三:发布 / 更新自己的仓库

node scripts/github-deploy.mjs <owner>/<repo>   # 通过 GitHub API 建仓并推送(需要 gh 已登录)
node scripts/verify-pages.mjs  <owner>/<repo>   # 轮询 Actions 与 Pages 构建结果

之所以提供这两个脚本:本机 hosts 把 github.com 指向了 127.0.0.1,git/curl 直连不可用, 只有 gh 的 API 通道能出去,所以用 Git Data API 完成了首次推送。 配好 gh auth setup-git 之后,日常更新直接用 git push 即可。


📖 使用步骤

  1. 打开网站,把视频文件拖进虚线框(或点「选择视频文件」)。
  2. 按需在 ⚙ 识别设置 里选模型和语言:
    • 想快 → Tiny 或 Base(默认 Base,日常够用)
    • 中文准确率优先 → Small / Large-v3-Turbo(体积和耗时更大)
    • 明确选择视频语言(而不是「自动检测」)会更快更准
  3. 点 开始识别,进度条会显示「提取音轨 → 加载模型 → 语音识别」三个阶段。
  4. 识别完成后:
    • 左侧自动出现 内容总结(要点 + 关键词);
    • 右侧是 字幕文稿,点击任意一条可让视频跳到那一句;
    • 用右侧导出按钮下载 文本文档 (.md)、.txt、.srt、.vtt、.json。
  5. (可选)想用大模型生成更漂亮的摘要:点 ✨ AI 摘要,启用后填入服务商的 Base URL / 模型 / API Key,再点「用 AI 重新总结」。

🤖 关于 AI 摘要(可选)

默认的本地摘要是抽取式的——它从文稿里挑出最有信息量的句子并去冗余,不联网、不花钱,但没有"重新组织语言"的能力。

如果你有任意一家大模型的 API Key,可以在 ✨ AI 摘要 里接入,支持所有 OpenAI 兼容的 /chat/completions 接口,内置了这些预设:

  • OpenAI(gpt-4o-mini 等)
  • DeepSeek(deepseek-chat)
  • 阿里云百炼 / 通义千问(qwen-plus)
  • 月之暗面 Kimi
  • 智谱 GLM
  • 本地 Ollama(http://localhost:11434/v1,完全离线)

配置后长文稿会自动「分段总结 → 汇总」处理,不会超出上下文长度。

⚠️ 安全提示:API Key 只保存在你自己浏览器的 localStorage 中。本项目是纯静态站点,没有后端,任何人都收不到你的 Key。但请注意:启用 AI 摘要后,字幕文本会被发送给你所配置的服务商。介意的话就不要启用,用本地摘要即可。


🧱 技术实现

src/
├─ App.tsx                 主界面(导入 / 进度 / 字幕 / 摘要 / 导出)
├─ types.ts                共享类型
├─ workers/
│  └─ asr.worker.ts        Web Worker:加载 Whisper 并执行识别
└─ lib/
   ├─ audio.ts             音轨提取 + 16 kHz 单声道重采样
   ├─ transcribe.ts        Worker 客户端封装、时间戳归一化
   ├─ modelSources.ts      模型来源候选链与地址拼装(纯逻辑,可单测)
   ├─ summarize.ts         本地抽取式摘要(TextRank/MMR + 关键词)
   ├─ llm.ts               OpenAI 兼容接口的 AI 摘要
   ├─ format.ts            SRT / VTT / TXT / Markdown / JSON 导出
   ├─ constants.ts         模型与语言清单、默认设置
   └─ storage.ts           localStorage 持久化
scripts/
└─ fetch-models.mjs        构建时把内置模型下载到 public/models/

几个关键设计:

  • 不需要 ffmpeg.wasm。 音轨提取直接用浏览器内置的 decodeAudioData + OfflineAudioContext 重采样, 省掉约 30 MB 依赖,也就不需要配置 SharedArrayBuffer 所需的 COOP/COEP 响应头(GitHub Pages 无法自定义响应头)。
  • Whisper 单次窗口是 30 秒,所以长视频必须切片。这里用的是带重叠的滑窗:下一片从「上一片最后一条字幕的结束时间」继续, 并在拼接时做尾部/头部去重,因此不会在切片边界丢词,也不会整段重复。
  • 识别跑在 Web Worker 里,界面不会卡死;音频用 Transferable 传递,避免大数组拷贝。
  • 模型走国内 CDN:首选魔搭 ModelScope(国内直连,实测 8~11MB/s),官方源与本站同源(public/models/)兜底; 下载进度会显示已下载体积 / 实时速度 / 预计剩余。原因见下面「模型从哪里来」。
  • 构建用相对路径(base: './'),因此产物可以直接部署在 /Video2Text-Studio/ 这样的子路径下。

模型从哪里来(为什么这么绕)

浏览器端加载模型踩过三个坑,这也是本项目最容易被"卡住"的地方:

  1. huggingface.co 在国内连不上(TCP 被阻断)。transformers.js 内部的 fetch 默认没有超时, 于是一个请求都发不出去时,界面会永远停在 0%,看起来就像死机。
  2. HF 镜像站(hf-mirror 等)在浏览器里不可用 —— 它们返回的 Access-Control-Allow-Origin 只允许 https://huggingface.co,第三方站点跨域取模型会被 CORS 直接拦掉(对方响应头决定,客户端改不了)。 ⚠️ 用 curl / Node 测这些地址会显示 acao=* 看似正常,因为不带 Origin 头时它们的响应不一样 —— 这类问题必须用真实浏览器验证,否则会误判成"镜像可用"。
  3. GitHub Pages 国内下载太慢(实测约 100KB/s,76MB 的 Base 模型要好几分钟); 而 jsDelivr 这类 CDN 只能服务仓库内的文件,模型单文件 29~51MB,既超限也不适合入库。

最终方案:首选魔搭 ModelScope。它的路径形态正好是 https://www.modelscope.cn/models/<模型id>/resolve/master/<文件>,允许跨域、支持 Range 分片, 背后是国内 LFS CDN,实测 8~11MB/s(73MB 的 Base 模型约 8 秒); 官方源、本站同源模型、镜像依次兜底。在此之上还加了一层保护:

  • src/lib/modelSources.ts:模型来源候选链(魔搭 ModelScope → 本站同源 → 官方 → 镜像 → 自定义)、 每个来源专用的路径模板、精度回退链(纯逻辑,可在 Node 单测);
  • src/workers/asr.worker.ts:给模型请求装上首字节超时 / 停滞超时 / 看门狗,每秒发一次心跳进度, 任何阶段"没动静"都会主动中止并换下一个来源,最终给出可读的错误而不是无限等待; 下载进度带上「已下载 / 总量 · 实时速度 · 预计剩余」,编译初始化阶段改用动态条纹 + "编译中…" (这段没有可上报的百分比,用假百分比会让人以为卡死);
  • 探测不等人:来源探测一旦探到可用来源就立刻开工,其余来源在后台收尾;精度探测同理 (主精度可用就不等 fp32)。早期版本把剩余探测全 await 了,结果每次启动都被连不通的 huggingface.co 拖到超时才继续,白白多等 8 秒;
  • scripts/fetch-models.mjs:构建时把 Tiny / Base 下载到 public/models/ 作为同源兜底 (CI 里跑,带缓存,失败不阻断部署;public/models/ 不入库);
  • 推理设备默认 auto,会真实请求一次 WebGPU 适配器再决定用 WebGPU 还是 CPU, 避免在拿不到适配器的环境里白等一轮。

另外诊断过的一个隐形坑:不要手动覆盖 env.backends.onnx.wasm.wasmPaths。 transformers.js 的 dist 里自带匹配版本的 onnxruntime wasm;一旦指向别的版本(例如 onnxruntime-web@1.20.1), 会因文件名不匹配(如缺少 .jsep.wasm)而 404,表现同样是"卡在 0%"。


❓ 常见问题

支持多大的视频?

主要受内存限制,一般 1~2 小时的视频没问题。时长只影响识别耗时(大致是 音频时长 × 0.1~1,取决于模型与是否用上 WebGPU)。 浏览器解码超大文件时可能内存告急,建议先用工具压缩。

提示"无法解码音轨"怎么办?

说明浏览器自带的解码器不认这个文件的音轨编码。常见于 MKV 封装、HEVC/H.265、AC-3/EAC-3 音轨、AVI 老格式。 解决办法:先用格式转换工具转成 MP4(H.264 + AAC),或直接提取成 MP3 / WAV / M4A 再上传。

识别一直停在 0% 不动 / 模型加载失败?

先看界面上的进度文案:它会明确告诉你正在连接哪个来源、已经等了多久、以及是否准备换源, 不会像以前那样一动不动。然后按提示排查:

  1. 把「模型来源」设为「自动」:会按 魔搭 ModelScope → 本站同源 → 官方 → 镜像 依次探测, 并告诉你最终用了哪个、响应多快;模型建议先用 Tiny 或 Base;
  2. 检查「推理设备」:拿不到 WebGPU 适配器的环境(无头浏览器、禁用 GPU、老显卡、远程桌面) 会在日志里提示,可在「识别设置 → 推理设备」里手动选 CPU(WASM);
  3. 要换别的模型(Small / Large-v3-Turbo 等):只要魔搭上有该模型的镜像就能直接下载; 也可以自建一个放 ONNX 模型的静态目录,在「识别设置 → 模型来源」里选 自定义地址 填进去;
  4. 构建自己的站点:同源兜底模型由 npm run fetch:models 下载到 public/models/(不入库), 部署工作流 .github/workflows/deploy-pages.yml 里已经接好这一步;本地想手动跑也可以直接执行该命令。

想内置更多模型?改 scripts/fetch-models.mjs 里的 MODELS,同时把新模型 id 加进 src/lib/constants.ts 的 BUILTIN_MODELS。

模型下载很慢 / 一直等在下载上?

模型默认从魔搭 ModelScope(国内直连)取,实测 811MB/s:Tiny(约 39MB 权重)35 秒、 Base(约 73MB)约 8 秒。界面上会实时显示「已下载 / 总量 · 实时速度 · 预计剩余」, 不会再出现"百分比不动、也不知道还要等多久"的情况。

如果还是很慢:

  1. 把「识别设置 → 模型来源」设为 自动,让它按 魔搭 → 本站同源 → 官方 → 镜像 的顺序挑可用的;
  2. 换 Tiny(权重体积只有 Base 的一半);
  3. 自己有更快的托管(对象存储 / 自建静态目录)时,选「自定义地址」填进去, 路径需兼容 {模型id}/resolve/main/{文件名};
  4. 下载一次后由浏览器 Cache Storage 缓存,之后再用不会重复下载。

下载完之后还有一步「初始化推理引擎」(界面显示为动态条纹 + 编译中…), 那是在本机编译 WASM 推理引擎,CPU 上首次需要几十秒,与网络无关;换 Tiny 会明显更快。

识别很慢?
  • 优先用 Chrome / Edge 113+,在识别设置里把「计算设备」设为 WebGPU;
  • 选更小的模型(Tiny/Base)与 q8 精度;
  • 关掉其他占用显卡的页面;
  • 老电脑上 CPU 推理本来就慢,1 小时视频跑十几分钟是正常的。
识别结果不准?
  1. 明确指定视频语言,别用自动检测;
  2. 换更大的模型(Small / Large-v3-Turbo);
  3. 音质差、多人抢话、背景音乐大的素材,任何 ASR 都会掉点;
  4. 识别完可以直接在导出前用搜索定位,手动修正后再复制。
会上传我的视频吗?

不会。视频文件只在你本机被 decodeAudioData 解码成 PCM,识别也在本机完成,没有任何上传逻辑。 你可以打开浏览器开发者工具的 Network 面板自己验证:只有模型文件和(可选的)AI 摘要请求。


🗺️ Roadmap

  • 说话人分离(Speaker Diarization)
  • 导出 Word (.docx) / PDF
  • 字幕双语对照与翻译
  • 支持直接粘贴视频链接(需要后端或 yt-dlp,欢迎 PR)
  • 浏览器扩展版(在任意视频页面一键提取)

📄 License

MIT © 2026 Polaris929-cloud

语音识别模型来自 OpenAI Whisper,经 transformers.js 在浏览器内运行; 模型权重遵循其各自的开源许可。

About

🎬 浏览器内本地 Whisper 语音识别提取视频字幕,并自动总结内容导出文本文档(纯前端,视频不上传)

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages