Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
BAIDU_KEY=
BAIDU_SECRET=
VOLC_KEY=
VOLC_SECRET=

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}

# Camb AI — used for both transcription and translation when selected in config.js
CAMB_API_KEY=
4 changes: 3 additions & 1 deletion .env.local
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ VOLC_SECRET=

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}
OLLAMA_PROMPT=Please translate the following content from ${sourceLanguage} to ${targetLanguage}, only return the translation result can be. \n ${content}

CAMB_API_KEY=
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
- 支持百度翻译
- 支持 deeplx 翻译 (批量翻译容易存在被限流的情况)
- 支持 ollama 翻译
- 支持 Camb AI (转录 + 翻译)
- 自定义字幕文件名,方便兼容不同的播放器挂载字幕识别
- 自定义翻译后的字幕文件内容,纯翻译结果,原字幕+翻译结果
- 项目集成 `whisper.cpp`, 它对 apple silicon 进行了优化,有较快的生成速度
Expand Down Expand Up @@ -238,6 +239,34 @@ export const contentTemplateRule = contentTemplateRuleMap.onlyTranslate;

5️⃣ 配置好该文件之后,执行 `yarn start` 或者 `npm start`, 首次执行会下载 `whisper.cpp` 和配置的对应的模型文件,会比较慢一些。下次执行将会跳过该流程

## Camb AI (transcription + translation)

This project integrates [Camb AI](https://docs.camb.ai) via the official `@camb-ai/sdk` package. Camb AI can be used as a transcription provider (replacing the local `whisper.cpp` run) and/or as a translation provider.

1. Install the SDK:

```bash
npm install @camb-ai/sdk
```

2. Add your key to `.env.local` (or copy `.env.example`):

```
CAMB_API_KEY=your_key_here
```

3. Select Camb AI in `config.js`:

```js
// transcription (optional — defaults to local whisper.cpp)
export const transcriptionServiceProvider = supportedTranscriptionService.camb;

// translation
export const translateServiceProvider = supportedService.camb;
```

The provider implementation lives at `providers/camb.js` and exposes `transcribe(audioPath, opts)` and `translate(text, targetLang, opts)`. The translation path is also wired through `service/camb.js` to match the dynamic-import pattern used by the other providers.

如果在使用过程中遇到啥问题,可以提 Issue 或者通过 Discussions 进行讨论

[![Powered by DartNode](https://dartnode.com/branding/DN-Open-Source-sm.png)](https://dartnode.com "Powered by DartNode - Free VPS for Open Source")
12 changes: 12 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,23 @@ export const supportedService = {
volc: Symbol.for('volc'),
deeplx: Symbol.for('deeplx'),
ollama: Symbol.for('ollama'),
camb: Symbol.for('camb'),
};

// 当前使用的翻译服务商,如果不配置,则不执行翻译流程
export const translateServiceProvider = supportedService.ollama;

// 支持的转录(语音转文字)服务商
// whisper: 使用本地 whisper.cpp(默认)
// camb: 使用 Camb AI 云端转录
export const supportedTranscriptionService = {
whisper: Symbol.for('whisper'),
camb: Symbol.for('camb'),
};

// 当前使用的转录服务商
export const transcriptionServiceProvider = supportedTranscriptionService.whisper;

// 翻译结果字幕文件内容配置
export const contentTemplateRuleMap = {
onlyTranslate: Symbol.for('onlyTranslate'), // 只输出翻译内容
Expand Down
35 changes: 27 additions & 8 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ import path from 'path';
import { execSync } from 'child_process';
import { config } from 'dotenv';
import app from './translate.js';
import { sourceSrtSaveName, translateServiceProvider, videoDir, translateConfig ,whisperModel } from './config.js';
import {
sourceSrtSaveName,
translateServiceProvider,
videoDir,
translateConfig,
whisperModel,
transcriptionServiceProvider,
supportedTranscriptionService,
} from './config.js';
import { extractAudio, renderFilePath, installWhisper, isDarwin, isWin32 } from './utils.js';

config();
Expand All @@ -12,7 +20,9 @@ const { log, error } = console;

const SUPPORTED_VIDEO_FORMATS = ["mp4", "avi", "mov", "mkv", "flv", "wmv", "webm", "m4a"];

await installWhisper();
if (transcriptionServiceProvider !== supportedTranscriptionService.camb) {
await installWhisper();
}
fs.readdir(videoDir, async (err, files) => {
if (err) {
error(err);
Expand All @@ -30,13 +40,22 @@ fs.readdir(videoDir, async (err, files) => {
await extractAudio(`${videoDir}/${file}`, `${wavFile}`);
//execSync(`ffmpeg -v quiet -stats -i "${videoDir}/${file}" -ar 16000 -ac 1 -c:a pcm_s16le -y "${wavFile}"`);
log('完成音频文件提取, 准备生成字幕文件');
let mainPath = path.join('./', 'whisper.cpp/main');
if(isWin32()){
mainPath = path.join('./', 'whisper-bin-x64/main.exe');
if (transcriptionServiceProvider === supportedTranscriptionService.camb) {
const { transcribe } = await import('./providers/camb.js');
const srt = await transcribe(wavFile, {
sourceLanguage: translateConfig.sourceLanguage,
format: 'srt',
});
fs.writeFileSync(`${srtFile}.srt`, srt);
} else {
let mainPath = path.join('./', 'whisper.cpp/main');
if(isWin32()){
mainPath = path.join('./', 'whisper-bin-x64/main.exe');
}
execSync(
`${mainPath} -m ./whisper.cpp/models/ggml-${whisperModel}.bin -f "${wavFile}" -osrt -of "${srtFile}" -l ${translateConfig.sourceLanguage}`
)
}
execSync(
`${mainPath} -m ./whisper.cpp/models/ggml-${whisperModel}.bin -f "${wavFile}" -osrt -of "${srtFile}" -l ${translateConfig.sourceLanguage}`
)
log('完成字幕文件生成, 准备开始翻译');
if (translateServiceProvider) {
await app(videoDir, fileName, `${srtFile}.srt`);
Expand Down
Loading