Important
🧨 💥 🎉 本项目是一个命令行工具,但非常荣幸得到了很多朋友的支持,也给我了很大的鼓舞,因此,我基于它制作了一款客户端工具,让大家能够更加方便地使用,也方便一部分不熟悉代码配置的朋友也能使用该工具。
欢迎大家移步 video-subtitle-master 以获得更加便捷的使用体验
做这个小工具的初衷:
自己有一大批外文视频,没有字幕,希望能够添加字幕文件,同时也能够将字幕文件翻译成中文, 同时希望能够通过批量处理的方式来减轻工作量。
类似需求,有一批厂商已经提供到了支持,比如 讯飞听见, 网易见外 等,但这些在线服务都涉及到视频的上传动作,效率相对比较低下。
希望能够找一个客户端工具,在本地来生成,试用了一些工具,依然不理想
- Buzz 非 Store 版本没有对 apple silicon 做优化,字幕生成速度比较慢,也不支持翻译
- MacWhisper 免费版本只支持单个生成,不支持批量,不支持翻译
- WhisperScript 可以批量生成,但字幕文件需要手动一个个地保存,不支持翻译
- memo.ac 做了 mac 下的性能优化,可以使用 GPU ,也支持翻译功能,非常棒的一款软件,但目前批量模式 bug 太多,无法正常使用
最后想了一下,本地语音转文字,通常的做法就是使用目前最强的 whisper 模型来生成。那我的需求就比较简单了:
- 通过 ffmpeg 从视频文件中提取出音频文件
- 通过 whisper 模型将音频生成原语言的字幕文件
- 调用翻译 API, 将原语言的字幕文件翻译成目标语言的字幕文件
基于以上简单的思路和流程,就可以简单写一个小工具来批量处理本地的视频了。
- 源语言字幕文件和目标语言字幕文件放在视频同目录下,方便播放时任意挂载字幕文件
- 批量处理目录下面的所有视频文件
- 可以只生成字幕,不翻译,方便批量为视频生成字幕
- 支持火山引擎翻译
- 支持百度翻译
- 支持 deeplx 翻译 (批量翻译容易存在被限流的情况)
- 支持 ollama 翻译
- 自定义字幕文件名,方便兼容不同的播放器挂载字幕识别
- 自定义翻译后的字幕文件内容,纯翻译结果,原字幕+翻译结果
- 项目集成
whisper.cpp
, 它对 apple silicon 进行了优化,有较快的生成速度 - 项目集成了
fluent-ffmpeg
, 无须安装ffmpeg
tiny.en
tiny
base.en
base
small.en
small
medium.en
medium
large-v1
large-v2
large-v3
本项目的翻译能力是基于 百度/火山/deeplx 的翻译API来实现的,这些 API 的使用需要申请对的 KEY 和 SECRET, 因此,如果你需要使用到翻译服务,需要先申请一个 API 。
具体的申请方法,可以参考 https://bobtranslate.com/service/ , 感谢 Bob 这款优秀的软件。
1️⃣ 克隆本项目在本地
git clone https://github.com/buxuku/VideoSubtitleGenerator.git
2️⃣ 在项目中执行 yarn install
或者 npm install
cd VideoSubtitleGenerator
yarn install
3️⃣ 如果需要翻译,复制 .env.local
为 .env
在项目根目录,访文件用于配置翻译相关的 KEY 和 SECRET, 例如
BAIDU_
开头的为百度翻译的配置
VOLC_
开头的为火山翻译的配置
OLLAMA_
开头的为 ollama 翻译的配置
BAIDU_KEY=2023120600190xxxx
BAIDU_SECRET=PIbyKjEr1y8u18RZxxxx
VOLC_KEY=AKLTMDUwZjY4MTZkNTFmN4M3ZjlkMzlmYzAzMTdlMDExxxx
VOLC_SECRET=T0dRMllUUmpPREUzWWpjNE5HVm2Zamt4TlRObU9EUm1ORFk0T1dGbExxxx==
OLLAMA_API_URL=http://localhost:11434
OLLAMA_MODEL_NAME=llama3
OLLAMA_PROMPT=Please translate the following content from ${sourceLanguage} to ${targetLanguage}, only return the translation result can be. \n ${content}
4️⃣ 其余的配置在 config.js
文件中进行配置,每条配置均的详细的注释
// 视频文件所在目录 如 /Users/demo/video
export const videoDir = './examples';
/*
whisper.cpp 模型 支持以下
tiny.en
tiny
base.en
base
small.en
small
medium.en
medium
large-v1
large-v2
large-v3
*/
export const whisperModel = 'base.en';
// 翻译配置,视频原语言与翻译后的目标语言
export const translateConfig = {
sourceLanguage: 'en',
targetLanguage: 'zh',
};
// 支持的翻译服务商
export const supportedService = {
baidu: Symbol.for('baidu'),
volc: Symbol.for('volc'),
deeplx: Symbol.for('deeplx'),
ollama: Symbol.for('ollama'),
};
// 当前使用的翻译服务商,如果不配置,则不执行翻译流程
export const translateServiceProvider = supportedService.volc;
// 翻译结果字幕文件内容配置
export const contentTemplateRuleMap = {
onlyTranslate: Symbol.for('onlyTranslate'), // 只输出翻译内容
sourceAndTranslate: Symbol.for('sourceAndTranslate'), // 输出原始字幕和翻译字幕, 原始字幕在上面
translateAndSource: Symbol.for('translateAndSource'), // 输出翻译后的字幕和原始字幕, 翻译字幕在上面
};
// 字幕文件内容模板 支持 ${sourceContent}, ${targetContent} 变量
export const contentTemplate = {
[contentTemplateRuleMap.onlyTranslate]: '${targetContent}\n\n',
[contentTemplateRuleMap.sourceAndTranslate]: '${sourceContent}\n${targetContent}\n\n',
[contentTemplateRuleMap.translateAndSource]: '${targetContent}\n${sourceContent}\n\n',
};
// 翻译内容输出模板规则,默认只输出翻译内容, 支持 contentTemplateRuleMap 内的规则
export const contentTemplateRule = contentTemplateRuleMap.onlyTranslate;
// 原始字幕文件保存命名规则 支持 fileName, sourceLanguage, targetLanguage 变量
// 如果为空,将不保存原始字幕文件
// eg: '${fileName}.${sourceLanguage}' -> 对于视频名为 text.mp4 的英文视频原始字幕文件名为 text.en.srt
export const sourceSrtSaveName = '${fileName}.${sourceLanguage}';
// 翻译后的字幕文件保存命名规则 支持 fileName, sourceLanguage, targetLanguage 变量
export const targetSrtSaveName = '${fileName}.${targetLanguage}';
这里面的字幕内容和字幕文件名可以自定义配置,同时会将里面的 ${xxx}
变量转为对应的字符
核心配置主要为以下几项
// 当前使用的翻译服务商,如果不配置,则不执行翻译流程
export const translateServiceProvider = supportedService.volc;
支持
- supportedService.volc 火山翻译
- supportedService.baidu 百度翻译
- supportedService.deeplx deeplx 翻译
- supportedService.ollama ollama 翻译
// 翻译内容输出模板规则,默认只输出翻译内容, 支持 contentTemplateRuleMap 内的规则
export const contentTemplateRule = contentTemplateRuleMap.onlyTranslate;
支持以下几种设置模式
只保留翻译后的结果内容,如
1
00:00:00,000 --> 00:00:09,360
我要和你们谈谈我这本书里的一些东西,我希望能
2
00:00:09,360 --> 00:00:13,680
和你们已经听到的东西产生共鸣,我会试着建立一些联系。
保留原字幕和翻译之后的字幕,且原字幕在上面
1
00:00:00,000 --> 00:00:09,360
I'm going to talk to you about some stuff that's in this book of mine that I hope will
我要和你们谈谈我这本书里的一些东西,我希望能
2
00:00:09,360 --> 00:00:13,680
resonate with other things that you've already heard and I'll try to make some connections
和你们已经听到的东西产生共鸣,我会试着建立一些联系。
保留原字幕和翻译之后的字幕,且翻译字幕在上面
1
00:00:00,000 --> 00:00:09,360
我要和你们谈谈我这本书里的一些东西,我希望能
I'm going to talk to you about some stuff that's in this book of mine that I hope will
2
00:00:09,360 --> 00:00:13,680
和你们已经听到的东西产生共鸣,我会试着建立一些联系。
resonate with other things that you've already heard and I'll try to make some connections
5️⃣ 配置好该文件之后,执行 yarn start
或者 npm start
, 首次执行会下载 whisper.cpp
和配置的对应的模型文件,会比较慢一些。下次执行将会跳过该流程
如果在使用过程中遇到啥问题,可以提 Issue 或者通过 Discussions 进行讨论