@junwan666/remotion-install-whisper-cpp 4.0.448-zh.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/convert-to-captions.d.ts +11 -0
- package/dist/convert-to-captions.js +51 -0
- package/dist/download-whisper-model.d.ts +14 -0
- package/dist/download-whisper-model.js +108 -0
- package/dist/download.d.ts +8 -0
- package/dist/download.js +51 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +18 -0
- package/dist/install-whisper-cpp.d.ts +9 -0
- package/dist/install-whisper-cpp.js +168 -0
- package/dist/languages.d.ts +5 -0
- package/dist/languages.js +6 -0
- package/dist/to-captions.d.ts +10 -0
- package/dist/to-captions.js +21 -0
- package/dist/transcribe.d.ts +80 -0
- package/dist/transcribe.js +189 -0
- package/dist/utils.d.ts +9 -0
- package/dist/utils.js +38 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# @remotion/install-whisper-cpp
|
|
2
|
+
|
|
3
|
+
Helpers for installing and using Whisper.cpp
|
|
4
|
+
|
|
5
|
+
[](https://npmcharts.com/compare/@remotion/install-whisper-cpp?minimal=true)
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @remotion/install-whisper-cpp --save-exact
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
When installing a Remotion package, make sure to align the version of all `remotion` and `@remotion/*` packages to the same version.
|
|
14
|
+
Remove the `^` character from the version number to use the exact version.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
See the [documentation](https://www.remotion.dev/docs/install-whisper-cpp) for more information.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TranscriptionJson } from './transcribe';
|
|
2
|
+
export type ConvertToCaptionCaption = {
|
|
3
|
+
text: string;
|
|
4
|
+
startInSeconds: number;
|
|
5
|
+
};
|
|
6
|
+
export declare function convertToCaptions({ transcription, combineTokensWithinMilliseconds }: {
|
|
7
|
+
transcription: TranscriptionJson<true>['transcription'];
|
|
8
|
+
combineTokensWithinMilliseconds: number;
|
|
9
|
+
}): {
|
|
10
|
+
captions: ConvertToCaptionCaption[];
|
|
11
|
+
};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertToCaptions = convertToCaptions;
|
|
4
|
+
/*
|
|
5
|
+
* @description Converts the JSON transcription data into captions with start times based on the defined millisecond threshold.
|
|
6
|
+
* @see [Documentation](https://remotion.dev/docs/install-whisper-cpp/convert-to-captions)
|
|
7
|
+
*/
|
|
8
|
+
function convertToCaptions({ transcription, combineTokensWithinMilliseconds, }) {
|
|
9
|
+
const merged = [];
|
|
10
|
+
let currentText = '';
|
|
11
|
+
let currentFrom = 0;
|
|
12
|
+
let currentTo = 0;
|
|
13
|
+
let currentTokenLevelTimestamp = 0;
|
|
14
|
+
transcription.forEach((item, index) => {
|
|
15
|
+
const { text } = item;
|
|
16
|
+
// If text starts with a space, push the currentText (if it exists) and start a new one
|
|
17
|
+
if (text.startsWith(' ') &&
|
|
18
|
+
currentTo - currentFrom > combineTokensWithinMilliseconds) {
|
|
19
|
+
if (currentText !== '') {
|
|
20
|
+
merged.push({
|
|
21
|
+
text: currentText,
|
|
22
|
+
startInSeconds: currentTokenLevelTimestamp / 100,
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
// Start a new sentence
|
|
26
|
+
currentText = text.trimStart();
|
|
27
|
+
currentFrom = item.offsets.from;
|
|
28
|
+
currentTo = item.offsets.to;
|
|
29
|
+
currentTokenLevelTimestamp = item.tokens[0].t_dtw;
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Continuation or start of a new sentence without leading space
|
|
33
|
+
if (currentText === '') {
|
|
34
|
+
// It's the start of the document or after a sentence that started with a space
|
|
35
|
+
currentFrom = item.offsets.from;
|
|
36
|
+
currentTokenLevelTimestamp = item.tokens[0].t_dtw;
|
|
37
|
+
}
|
|
38
|
+
currentText += text;
|
|
39
|
+
currentText = currentText.trimStart();
|
|
40
|
+
currentTo = item.offsets.to;
|
|
41
|
+
}
|
|
42
|
+
// Ensure the last sentence is added
|
|
43
|
+
if (index === transcription.length - 1 && currentText !== '') {
|
|
44
|
+
merged.push({
|
|
45
|
+
text: currentText,
|
|
46
|
+
startInSeconds: currentTokenLevelTimestamp / 100,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return { captions: merged };
|
|
51
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type OnProgress } from './download';
|
|
2
|
+
declare const models: readonly ["tiny", "tiny.en", "base", "base.en", "small", "small.en", "medium", "medium.en", "large-v1", "large-v2", "large-v3", "large-v3-turbo"];
|
|
3
|
+
export type WhisperModel = (typeof models)[number];
|
|
4
|
+
export declare const getModelPath: (folder: string, model: "base" | "base.en" | "large-v1" | "large-v2" | "large-v3" | "large-v3-turbo" | "medium" | "medium.en" | "small" | "small.en" | "tiny" | "tiny.en") => string;
|
|
5
|
+
export declare const downloadWhisperModel: ({ model, folder, printOutput, onProgress, signal, }: {
|
|
6
|
+
model: "base" | "base.en" | "large-v1" | "large-v2" | "large-v3" | "large-v3-turbo" | "medium" | "medium.en" | "small" | "small.en" | "tiny" | "tiny.en";
|
|
7
|
+
folder: string;
|
|
8
|
+
signal?: AbortSignal | undefined;
|
|
9
|
+
printOutput?: boolean | undefined;
|
|
10
|
+
onProgress?: OnProgress | undefined;
|
|
11
|
+
}) => Promise<{
|
|
12
|
+
alreadyExisted: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.downloadWhisperModel = exports.getModelPath = void 0;
|
|
40
|
+
/* eslint-disable no-console */
|
|
41
|
+
const fs_1 = __importStar(require("fs"));
|
|
42
|
+
const path_1 = __importDefault(require("path"));
|
|
43
|
+
const download_1 = require("./download");
|
|
44
|
+
const models = [
|
|
45
|
+
'tiny',
|
|
46
|
+
'tiny.en',
|
|
47
|
+
'base',
|
|
48
|
+
'base.en',
|
|
49
|
+
'small',
|
|
50
|
+
'small.en',
|
|
51
|
+
'medium',
|
|
52
|
+
'medium.en',
|
|
53
|
+
'large-v1',
|
|
54
|
+
'large-v2',
|
|
55
|
+
'large-v3',
|
|
56
|
+
'large-v3-turbo',
|
|
57
|
+
];
|
|
58
|
+
const modelSizes = {
|
|
59
|
+
'medium.en': 1533774781,
|
|
60
|
+
'base.en': 147964211,
|
|
61
|
+
'large-v1': 3094623691,
|
|
62
|
+
'large-v2': 3094623691,
|
|
63
|
+
'large-v3': 3095033483,
|
|
64
|
+
'large-v3-turbo': 1624555275,
|
|
65
|
+
small: 487601967,
|
|
66
|
+
tiny: 77691713,
|
|
67
|
+
'small.en': 487614201,
|
|
68
|
+
'tiny.en': 77704715,
|
|
69
|
+
base: 147951465,
|
|
70
|
+
medium: 1533763059,
|
|
71
|
+
};
|
|
72
|
+
const getModelPath = (folder, model) => {
|
|
73
|
+
return path_1.default.join(path_1.default.resolve(process.cwd(), folder), `ggml-${model}.bin`);
|
|
74
|
+
};
|
|
75
|
+
exports.getModelPath = getModelPath;
|
|
76
|
+
const downloadWhisperModel = async ({ model, folder, printOutput = true, onProgress, signal, }) => {
|
|
77
|
+
if (!models.includes(model)) {
|
|
78
|
+
throw new Error(`Invalid whisper model ${model}. Available: ${models.join(', ')}`);
|
|
79
|
+
}
|
|
80
|
+
const filePath = (0, exports.getModelPath)(folder, model);
|
|
81
|
+
if ((0, fs_1.existsSync)(filePath)) {
|
|
82
|
+
const { size } = fs_1.default.statSync(filePath);
|
|
83
|
+
if (size === modelSizes[model]) {
|
|
84
|
+
if (printOutput) {
|
|
85
|
+
console.log(`Model already exists at ${filePath}`);
|
|
86
|
+
}
|
|
87
|
+
return Promise.resolve({ alreadyExisted: true });
|
|
88
|
+
}
|
|
89
|
+
if (printOutput) {
|
|
90
|
+
throw new Error(`Model ${model} already exists at ${filePath}, but the size is ${size} bytes (expected ${modelSizes[model]} bytes). Delete ${filePath} and try again.`);
|
|
91
|
+
}
|
|
92
|
+
return Promise.resolve({ alreadyExisted: false });
|
|
93
|
+
}
|
|
94
|
+
const baseModelUrl = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${model}.bin`;
|
|
95
|
+
if (printOutput) {
|
|
96
|
+
console.log(`Downloading whisper model ${model} from ${baseModelUrl}`);
|
|
97
|
+
}
|
|
98
|
+
const fileStream = fs_1.default.createWriteStream(filePath);
|
|
99
|
+
await (0, download_1.downloadFile)({
|
|
100
|
+
fileStream,
|
|
101
|
+
url: baseModelUrl,
|
|
102
|
+
printOutput,
|
|
103
|
+
onProgress,
|
|
104
|
+
signal: signal !== null && signal !== void 0 ? signal : null,
|
|
105
|
+
});
|
|
106
|
+
return { alreadyExisted: false };
|
|
107
|
+
};
|
|
108
|
+
exports.downloadWhisperModel = downloadWhisperModel;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type OnProgress = (downloadedBytes: number, totalBytes: number) => void;
|
|
2
|
+
export declare const downloadFile: ({ fileStream, url, printOutput, onProgress, signal, }: {
|
|
3
|
+
fileStream: NodeJS.WritableStream;
|
|
4
|
+
url: string;
|
|
5
|
+
printOutput: boolean;
|
|
6
|
+
onProgress: OnProgress | undefined;
|
|
7
|
+
signal: AbortSignal | null;
|
|
8
|
+
}) => Promise<void>;
|
package/dist/download.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.downloadFile = void 0;
|
|
4
|
+
const downloadFile = async ({ fileStream, url, printOutput, onProgress, signal, }) => {
|
|
5
|
+
const { body, headers } = await fetch(url, {
|
|
6
|
+
signal,
|
|
7
|
+
});
|
|
8
|
+
const contentLength = headers.get('content-length');
|
|
9
|
+
if (body === null) {
|
|
10
|
+
throw new Error('Failed to download whisper model');
|
|
11
|
+
}
|
|
12
|
+
if (contentLength === null) {
|
|
13
|
+
throw new Error('Content-Length header not found');
|
|
14
|
+
}
|
|
15
|
+
let downloaded = 0;
|
|
16
|
+
let lastPrinted = 0;
|
|
17
|
+
const totalFileSize = parseInt(contentLength, 10);
|
|
18
|
+
const reader = body.getReader();
|
|
19
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
20
|
+
await new Promise(async (resolve, reject) => {
|
|
21
|
+
try {
|
|
22
|
+
while (true) {
|
|
23
|
+
const { done, value } = await reader.read();
|
|
24
|
+
if (value) {
|
|
25
|
+
downloaded += value.length;
|
|
26
|
+
if (printOutput &&
|
|
27
|
+
(downloaded - lastPrinted > 1024 * 1024 * 10 ||
|
|
28
|
+
downloaded === totalFileSize)) {
|
|
29
|
+
console.log(`Downloaded ${downloaded} of ${contentLength} bytes (${((downloaded / Number(contentLength)) *
|
|
30
|
+
100).toFixed(2)}%)`);
|
|
31
|
+
lastPrinted = downloaded;
|
|
32
|
+
}
|
|
33
|
+
fileStream.write(value, () => {
|
|
34
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(downloaded, totalFileSize);
|
|
35
|
+
if (downloaded === totalFileSize) {
|
|
36
|
+
fileStream.end();
|
|
37
|
+
resolve();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
if (done) {
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
reject(e);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
exports.downloadFile = downloadFile;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { ConvertToCaptionCaption } from './convert-to-captions';
|
|
2
|
+
export type { OnProgress } from './download';
|
|
3
|
+
export { downloadWhisperModel, WhisperModel } from './download-whisper-model';
|
|
4
|
+
export { installWhisperCpp } from './install-whisper-cpp';
|
|
5
|
+
export type { Language } from './languages';
|
|
6
|
+
export { toCaptions } from './to-captions';
|
|
7
|
+
export { transcribe, TranscribeOnProgress, TranscriptionJson, } from './transcribe';
|
|
8
|
+
import { convertToCaptions as deprecatedConvertToCaptions } from './convert-to-captions';
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @deprecated convertToCaptions() has been deprecated as of Remotion v4.0.216.
|
|
12
|
+
* Use the toCaptions() function instead.
|
|
13
|
+
*/
|
|
14
|
+
export declare const convertToCaptions: typeof deprecatedConvertToCaptions;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertToCaptions = exports.transcribe = exports.toCaptions = exports.installWhisperCpp = exports.downloadWhisperModel = void 0;
|
|
4
|
+
const download_whisper_model_1 = require("./download-whisper-model");
|
|
5
|
+
Object.defineProperty(exports, "downloadWhisperModel", { enumerable: true, get: function () { return download_whisper_model_1.downloadWhisperModel; } });
|
|
6
|
+
const install_whisper_cpp_1 = require("./install-whisper-cpp");
|
|
7
|
+
Object.defineProperty(exports, "installWhisperCpp", { enumerable: true, get: function () { return install_whisper_cpp_1.installWhisperCpp; } });
|
|
8
|
+
const to_captions_1 = require("./to-captions");
|
|
9
|
+
Object.defineProperty(exports, "toCaptions", { enumerable: true, get: function () { return to_captions_1.toCaptions; } });
|
|
10
|
+
const transcribe_1 = require("./transcribe");
|
|
11
|
+
Object.defineProperty(exports, "transcribe", { enumerable: true, get: function () { return transcribe_1.transcribe; } });
|
|
12
|
+
const convert_to_captions_1 = require("./convert-to-captions");
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @deprecated convertToCaptions() has been deprecated as of Remotion v4.0.216.
|
|
16
|
+
* Use the toCaptions() function instead.
|
|
17
|
+
*/
|
|
18
|
+
exports.convertToCaptions = convert_to_captions_1.convertToCaptions;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const getWhisperExecutablePath: (whisperPath: string, whisperCppVersion: string) => string;
|
|
2
|
+
export declare const installWhisperCpp: ({ version, to, printOutput, signal, }: {
|
|
3
|
+
version: string;
|
|
4
|
+
to: string;
|
|
5
|
+
signal?: AbortSignal | null | undefined;
|
|
6
|
+
printOutput?: boolean | undefined;
|
|
7
|
+
}) => Promise<{
|
|
8
|
+
alreadyExisted: boolean;
|
|
9
|
+
}>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.installWhisperCpp = exports.getWhisperExecutablePath = void 0;
|
|
40
|
+
/* eslint-disable no-console */
|
|
41
|
+
const child_process_1 = require("child_process");
|
|
42
|
+
const fs_1 = __importStar(require("fs"));
|
|
43
|
+
const os_1 = __importDefault(require("os"));
|
|
44
|
+
const path_1 = __importDefault(require("path"));
|
|
45
|
+
const download_1 = require("./download");
|
|
46
|
+
const utils_1 = require("./utils");
|
|
47
|
+
const getIsSemVer = (str) => {
|
|
48
|
+
return /^[\d]{1}\.[\d]{1,2}\.+/.test(str);
|
|
49
|
+
};
|
|
50
|
+
const execute = ({ printOutput, signal, cwd, shell, args, bin, }) => {
|
|
51
|
+
const stdio = printOutput ? 'inherit' : 'ignore';
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const child = (0, child_process_1.spawn)(bin, args, {
|
|
54
|
+
stdio,
|
|
55
|
+
signal: signal !== null && signal !== void 0 ? signal : undefined,
|
|
56
|
+
cwd: cwd !== null && cwd !== void 0 ? cwd : undefined,
|
|
57
|
+
shell: shell !== null && shell !== void 0 ? shell : undefined,
|
|
58
|
+
});
|
|
59
|
+
child.on('exit', (code, exitSignal) => {
|
|
60
|
+
if (code !== 0) {
|
|
61
|
+
reject(new Error(`Error while executing ${bin} ${args.join(' ')}. Exit code: ${code}, signal: ${exitSignal}`));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
resolve();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const installForWindows = async ({ version, to, printOutput, signal, }) => {
|
|
69
|
+
if (!getIsSemVer(version)) {
|
|
70
|
+
throw new Error(`Non-semantic version provided. Only releases of Whisper.cpp are supported on Windows (e.g., 1.5.4). Provided version:
|
|
71
|
+
${version}. See https://www.remotion.dev/docs/install-whisper-cpp/install-whisper-cpp#version for more information.`);
|
|
72
|
+
}
|
|
73
|
+
const url = version === '1.5.5'
|
|
74
|
+
? 'https://remotion-ffmpeg-binaries.s3.eu-central-1.amazonaws.com/whisper-bin-x64-1-5-5.zip'
|
|
75
|
+
: `https://github.com/ggerganov/whisper.cpp/releases/download/v${version}/whisper-bin-x64.zip`;
|
|
76
|
+
const filePath = path_1.default.join(process.cwd(), 'whisper-bin-x64.zip');
|
|
77
|
+
const fileStream = fs_1.default.createWriteStream(filePath);
|
|
78
|
+
await (0, download_1.downloadFile)({
|
|
79
|
+
fileStream,
|
|
80
|
+
printOutput,
|
|
81
|
+
url,
|
|
82
|
+
onProgress: undefined,
|
|
83
|
+
signal,
|
|
84
|
+
});
|
|
85
|
+
await execute({
|
|
86
|
+
shell: 'powershell',
|
|
87
|
+
printOutput,
|
|
88
|
+
signal,
|
|
89
|
+
cwd: null,
|
|
90
|
+
bin: 'Expand-Archive',
|
|
91
|
+
args: ['-Force', filePath, to],
|
|
92
|
+
});
|
|
93
|
+
(0, fs_1.rmSync)(filePath);
|
|
94
|
+
};
|
|
95
|
+
const installWhisperForUnix = async ({ version, to, printOutput, signal, }) => {
|
|
96
|
+
await execute({
|
|
97
|
+
bin: 'git',
|
|
98
|
+
args: ['clone', 'https://github.com/ggerganov/whisper.cpp.git', to],
|
|
99
|
+
printOutput,
|
|
100
|
+
signal,
|
|
101
|
+
cwd: null,
|
|
102
|
+
shell: null,
|
|
103
|
+
});
|
|
104
|
+
const ref = getIsSemVer(version) ? `v${version}` : version;
|
|
105
|
+
await execute({
|
|
106
|
+
bin: 'git',
|
|
107
|
+
args: ['checkout', ref],
|
|
108
|
+
printOutput,
|
|
109
|
+
cwd: to,
|
|
110
|
+
signal,
|
|
111
|
+
shell: null,
|
|
112
|
+
});
|
|
113
|
+
await execute({
|
|
114
|
+
args: [],
|
|
115
|
+
bin: 'make',
|
|
116
|
+
cwd: to,
|
|
117
|
+
signal,
|
|
118
|
+
printOutput,
|
|
119
|
+
shell: null,
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
const getWhisperExecutablePath = (whisperPath, whisperCppVersion) => {
|
|
123
|
+
// INFO: 'main.exe' is deprecated.
|
|
124
|
+
let cppBin = ['main'];
|
|
125
|
+
let cppFolder = [];
|
|
126
|
+
if ((0, utils_1.compareVersions)(whisperCppVersion, '1.7.4') >= 0) {
|
|
127
|
+
cppBin = ['whisper-cli'];
|
|
128
|
+
cppFolder = ['build', 'bin'];
|
|
129
|
+
}
|
|
130
|
+
return os_1.default.platform() === 'win32'
|
|
131
|
+
? path_1.default.join(path_1.default.resolve(process.cwd(), whisperPath), ...cppFolder, `${cppBin}.exe`)
|
|
132
|
+
: path_1.default.join(path_1.default.resolve(process.cwd(), whisperPath), ...cppFolder, `./${cppBin}`);
|
|
133
|
+
};
|
|
134
|
+
exports.getWhisperExecutablePath = getWhisperExecutablePath;
|
|
135
|
+
const installWhisperCpp = async ({ version, to, printOutput = true, signal, }) => {
|
|
136
|
+
if ((0, fs_1.existsSync)(to)) {
|
|
137
|
+
if (!(0, fs_1.existsSync)((0, exports.getWhisperExecutablePath)(to, version))) {
|
|
138
|
+
if (printOutput) {
|
|
139
|
+
throw new Error(`Whisper folder ${to} exists but the executable (${(0, exports.getWhisperExecutablePath)(to, version)}) is missing. Delete ${to} and try again.`);
|
|
140
|
+
}
|
|
141
|
+
return Promise.resolve({ alreadyExisted: false });
|
|
142
|
+
}
|
|
143
|
+
if (printOutput) {
|
|
144
|
+
console.log(`Whisper already exists at ${to}`);
|
|
145
|
+
}
|
|
146
|
+
return Promise.resolve({ alreadyExisted: true });
|
|
147
|
+
}
|
|
148
|
+
if (process.platform === 'darwin' || process.platform === 'linux') {
|
|
149
|
+
await installWhisperForUnix({
|
|
150
|
+
version,
|
|
151
|
+
to,
|
|
152
|
+
printOutput,
|
|
153
|
+
signal: signal !== null && signal !== void 0 ? signal : null,
|
|
154
|
+
});
|
|
155
|
+
return Promise.resolve({ alreadyExisted: false });
|
|
156
|
+
}
|
|
157
|
+
if (process.platform === 'win32') {
|
|
158
|
+
await installForWindows({
|
|
159
|
+
version,
|
|
160
|
+
to,
|
|
161
|
+
printOutput,
|
|
162
|
+
signal: signal !== null && signal !== void 0 ? signal : null,
|
|
163
|
+
});
|
|
164
|
+
return Promise.resolve({ alreadyExisted: false });
|
|
165
|
+
}
|
|
166
|
+
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
167
|
+
};
|
|
168
|
+
exports.installWhisperCpp = installWhisperCpp;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* List of supported languages of Whispher.
|
|
3
|
+
* https://github.com/ggerganov/whisper.cpp/blob/8f253ef3af1c62c04316ba4afa7145fc4d701a8c/whisper.cpp#L424
|
|
4
|
+
*/
|
|
5
|
+
export type Language = 'Afrikaans' | 'Albanian' | 'Amharic' | 'Arabic' | 'Armenian' | 'Assamese' | 'Azerbaijani' | 'Bashkir' | 'Basque' | 'Belarusian' | 'Bengali' | 'Bosnian' | 'Breton' | 'Bulgarian' | 'Burmese' | 'Castilian' | 'Catalan' | 'Chinese' | 'Croatian' | 'Czech' | 'Danish' | 'Dutch' | 'English' | 'Estonian' | 'Faroese' | 'Finnish' | 'Flemish' | 'French' | 'Galician' | 'Georgian' | 'German' | 'Greek' | 'Gujarati' | 'Haitian' | 'Haitian Creole' | 'Hausa' | 'Hawaiian' | 'Hebrew' | 'Hindi' | 'Hungarian' | 'Icelandic' | 'Indonesian' | 'Italian' | 'Japanese' | 'Javanese' | 'Kannada' | 'Kazakh' | 'Khmer' | 'Korean' | 'Lao' | 'Latin' | 'Latvian' | 'Letzeburgesch' | 'Lingala' | 'Lithuanian' | 'Luxembourgish' | 'Macedonian' | 'Malagasy' | 'Malay' | 'Malayalam' | 'Maltese' | 'Maori' | 'Marathi' | 'Moldavian' | 'Moldovan' | 'Mongolian' | 'Myanmar' | 'Nepali' | 'Norwegian' | 'Nynorsk' | 'Occitan' | 'Panjabi' | 'Pashto' | 'Persian' | 'Polish' | 'Portuguese' | 'Punjabi' | 'Pushto' | 'Romanian' | 'Russian' | 'Sanskrit' | 'Serbian' | 'Shona' | 'Sindhi' | 'Sinhala' | 'Sinhalese' | 'Slovak' | 'Slovenian' | 'Somali' | 'Spanish' | 'Sundanese' | 'Swahili' | 'Swedish' | 'Tagalog' | 'Tajik' | 'Tamil' | 'Tatar' | 'Telugu' | 'Thai' | 'Tibetan' | 'Turkish' | 'Turkmen' | 'Ukrainian' | 'Urdu' | 'Uzbek' | 'Valencian' | 'Vietnamese' | 'Welsh' | 'Yiddish' | 'Yoruba' | 'Zulu' | 'auto' | 'af' | 'am' | 'ar' | 'as' | 'az' | 'ba' | 'be' | 'bg' | 'bn' | 'bo' | 'br' | 'bs' | 'ca' | 'cs' | 'cy' | 'da' | 'de' | 'el' | 'en' | 'es' | 'et' | 'eu' | 'fa' | 'fi' | 'fo' | 'fr' | 'gl' | 'gu' | 'ha' | 'haw' | 'he' | 'hi' | 'hr' | 'ht' | 'hu' | 'hy' | 'id' | 'is' | 'it' | 'ja' | 'jw' | 'ka' | 'kk' | 'km' | 'kn' | 'ko' | 'la' | 'lb' | 'ln' | 'lo' | 'lt' | 'lv' | 'mg' | 'mi' | 'mk' | 'ml' | 'mn' | 'mr' | 'ms' | 'mt' | 'my' | 'ne' | 'nl' | 'nn' | 'no' | 'oc' | 'pa' | 'pl' | 'ps' | 'pt' | 'ro' | 'ru' | 'sa' | 'sd' | 'si' | 'sk' | 'sl' | 'sn' | 'so' | 'sq' | 'sr' | 'su' | 'sv' | 'sw' | 'ta' | 'te' | 'tg' | 'th' | 'tk' | 'tl' | 'tr' | 'tt' | 'uk' | 'ur' | 'uz' | 'vi' | 'yi' | 'yo' | 'zh';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Caption } from '@remotion/captions';
|
|
2
|
+
import type { TranscriptionJson } from './transcribe';
|
|
3
|
+
type ToCaptionsInput = {
|
|
4
|
+
whisperCppOutput: TranscriptionJson<true>;
|
|
5
|
+
};
|
|
6
|
+
type ToCaptionsOutput = {
|
|
7
|
+
captions: Caption[];
|
|
8
|
+
};
|
|
9
|
+
export declare const toCaptions: (input: ToCaptionsInput) => ToCaptionsOutput;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toCaptions = void 0;
|
|
4
|
+
const toCaptions = (input) => {
|
|
5
|
+
const { transcription } = input.whisperCppOutput;
|
|
6
|
+
const captions = [];
|
|
7
|
+
for (const item of transcription) {
|
|
8
|
+
if (item.text === '') {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
captions.push({
|
|
12
|
+
text: captions.length === 0 ? item.text.trimStart() : item.text,
|
|
13
|
+
startMs: item.offsets.from,
|
|
14
|
+
endMs: item.offsets.to,
|
|
15
|
+
timestampMs: item.tokens[0].t_dtw === -1 ? null : item.tokens[0].t_dtw * 10,
|
|
16
|
+
confidence: item.tokens[0].p,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return { captions };
|
|
20
|
+
};
|
|
21
|
+
exports.toCaptions = toCaptions;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Language } from './languages';
|
|
2
|
+
type Timestamps = {
|
|
3
|
+
from: string;
|
|
4
|
+
to: string;
|
|
5
|
+
};
|
|
6
|
+
type Offsets = {
|
|
7
|
+
from: number;
|
|
8
|
+
to: number;
|
|
9
|
+
};
|
|
10
|
+
type WordLevelToken = {
|
|
11
|
+
t_dtw: number;
|
|
12
|
+
text: string;
|
|
13
|
+
timestamps: Timestamps;
|
|
14
|
+
offsets: Offsets;
|
|
15
|
+
id: number;
|
|
16
|
+
p: number;
|
|
17
|
+
};
|
|
18
|
+
type TranscriptionItem = {
|
|
19
|
+
timestamps: Timestamps;
|
|
20
|
+
offsets: Offsets;
|
|
21
|
+
text: string;
|
|
22
|
+
};
|
|
23
|
+
type TranscriptionItemWithTimestamp = TranscriptionItem & {
|
|
24
|
+
tokens: WordLevelToken[];
|
|
25
|
+
};
|
|
26
|
+
type Model = {
|
|
27
|
+
type: string;
|
|
28
|
+
multilingual: boolean;
|
|
29
|
+
vocab: number;
|
|
30
|
+
audio: {
|
|
31
|
+
ctx: number;
|
|
32
|
+
state: number;
|
|
33
|
+
head: number;
|
|
34
|
+
layer: number;
|
|
35
|
+
};
|
|
36
|
+
text: {
|
|
37
|
+
ctx: number;
|
|
38
|
+
state: number;
|
|
39
|
+
head: number;
|
|
40
|
+
layer: number;
|
|
41
|
+
};
|
|
42
|
+
mels: number;
|
|
43
|
+
ftype: number;
|
|
44
|
+
};
|
|
45
|
+
type Params = {
|
|
46
|
+
model: string;
|
|
47
|
+
language: string;
|
|
48
|
+
translate: boolean;
|
|
49
|
+
};
|
|
50
|
+
type Result = {
|
|
51
|
+
language: string;
|
|
52
|
+
};
|
|
53
|
+
type AdditionalArgs = string[] | [string, string][];
|
|
54
|
+
export type TranscriptionJson<WithTokenLevelTimestamp extends boolean> = {
|
|
55
|
+
systeminfo: string;
|
|
56
|
+
model: Model;
|
|
57
|
+
params: Params;
|
|
58
|
+
result: Result;
|
|
59
|
+
transcription: true extends WithTokenLevelTimestamp ? TranscriptionItemWithTimestamp[] : TranscriptionItem[];
|
|
60
|
+
};
|
|
61
|
+
export type TranscribeOnProgress = (progress: number) => void;
|
|
62
|
+
export declare const modelToDtw: (model: "base" | "base.en" | "large-v1" | "large-v2" | "large-v3" | "large-v3-turbo" | "medium" | "medium.en" | "small" | "small.en" | "tiny" | "tiny.en") => string;
|
|
63
|
+
export declare const transcribe: <HasTokenLevelTimestamps extends boolean>({ inputPath, whisperPath, whisperCppVersion, model, modelFolder, translateToEnglish, tokenLevelTimestamps, printOutput, tokensPerItem, language, splitOnWord, signal, onProgress, flashAttention, additionalArgs, }: {
|
|
64
|
+
inputPath: string;
|
|
65
|
+
whisperPath: string;
|
|
66
|
+
whisperCppVersion: string;
|
|
67
|
+
model: "base" | "base.en" | "large-v1" | "large-v2" | "large-v3" | "large-v3-turbo" | "medium" | "medium.en" | "small" | "small.en" | "tiny" | "tiny.en";
|
|
68
|
+
tokenLevelTimestamps: HasTokenLevelTimestamps;
|
|
69
|
+
modelFolder?: string | undefined;
|
|
70
|
+
translateToEnglish?: boolean | undefined;
|
|
71
|
+
printOutput?: boolean | undefined;
|
|
72
|
+
tokensPerItem?: (true extends HasTokenLevelTimestamps ? never : number | null) | undefined;
|
|
73
|
+
language?: Language | null | undefined;
|
|
74
|
+
splitOnWord?: boolean | undefined;
|
|
75
|
+
signal?: AbortSignal | undefined;
|
|
76
|
+
onProgress?: TranscribeOnProgress | undefined;
|
|
77
|
+
flashAttention?: boolean | undefined;
|
|
78
|
+
additionalArgs?: AdditionalArgs | undefined;
|
|
79
|
+
}) => Promise<TranscriptionJson<HasTokenLevelTimestamps>>;
|
|
80
|
+
export {};
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.transcribe = exports.modelToDtw = void 0;
|
|
40
|
+
const node_child_process_1 = require("node:child_process");
|
|
41
|
+
const node_fs_1 = __importStar(require("node:fs"));
|
|
42
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
43
|
+
const download_whisper_model_1 = require("./download-whisper-model");
|
|
44
|
+
const install_whisper_cpp_1 = require("./install-whisper-cpp");
|
|
45
|
+
const isWavFile = (inputPath) => {
|
|
46
|
+
const splitted = inputPath.split('.');
|
|
47
|
+
if (!splitted) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
return splitted[splitted.length - 1] === 'wav';
|
|
51
|
+
};
|
|
52
|
+
const readJson = async (jsonPath) => {
|
|
53
|
+
const data = await node_fs_1.default.promises.readFile(jsonPath, 'utf8');
|
|
54
|
+
return JSON.parse(data);
|
|
55
|
+
};
|
|
56
|
+
// https://github.com/ggerganov/whisper.cpp/blob/fe36c909715e6751277ddb020e7892c7670b61d4/examples/main/main.cpp#L989-L999
|
|
57
|
+
// https://github.com/remotion-dev/remotion/issues/4168
|
|
58
|
+
const modelToDtw = (model) => {
|
|
59
|
+
if (model === 'large-v3-turbo') {
|
|
60
|
+
return 'large.v3.turbo';
|
|
61
|
+
}
|
|
62
|
+
if (model === 'large-v3') {
|
|
63
|
+
return 'large.v3';
|
|
64
|
+
}
|
|
65
|
+
if (model === 'large-v2') {
|
|
66
|
+
return 'large.v2';
|
|
67
|
+
}
|
|
68
|
+
if (model === 'large-v1') {
|
|
69
|
+
return 'large.v1';
|
|
70
|
+
}
|
|
71
|
+
return model;
|
|
72
|
+
};
|
|
73
|
+
exports.modelToDtw = modelToDtw;
|
|
74
|
+
const transcribeToTemporaryFile = async ({ fileToTranscribe, whisperPath, whisperCppVersion, model, tmpJSONPath, modelFolder, translate, tokenLevelTimestamps, printOutput, tokensPerItem, language, splitOnWord, signal, onProgress, flashAttention, additionalArgs, }) => {
|
|
75
|
+
const modelPath = (0, download_whisper_model_1.getModelPath)(modelFolder !== null && modelFolder !== void 0 ? modelFolder : whisperPath, model);
|
|
76
|
+
if (!node_fs_1.default.existsSync(modelPath)) {
|
|
77
|
+
throw new Error(`Error: Model ${model} does not exist at ${modelFolder ? modelFolder : modelPath}. Check out the downloadWhisperModel() API at https://www.remotion.dev/docs/install-whisper-cpp/download-whisper-model to see how to install whisper models`);
|
|
78
|
+
}
|
|
79
|
+
const executable = (0, install_whisper_cpp_1.getWhisperExecutablePath)(whisperPath, whisperCppVersion);
|
|
80
|
+
const args = [
|
|
81
|
+
'-f',
|
|
82
|
+
fileToTranscribe,
|
|
83
|
+
'--output-file',
|
|
84
|
+
tmpJSONPath,
|
|
85
|
+
'--output-json',
|
|
86
|
+
tokensPerItem ? ['--max-len', tokensPerItem] : null,
|
|
87
|
+
'-ojf', // Output full JSON
|
|
88
|
+
tokenLevelTimestamps ? ['--dtw', (0, exports.modelToDtw)(model)] : null,
|
|
89
|
+
model ? [`-m`, `${modelPath}`] : null,
|
|
90
|
+
['-pp'], // print progress
|
|
91
|
+
translate ? '-tr' : null,
|
|
92
|
+
language ? ['-l', language.toLowerCase()] : null,
|
|
93
|
+
splitOnWord ? ['--split-on-word', splitOnWord] : null,
|
|
94
|
+
flashAttention ? ['--flash-attn', 'true'] : null,
|
|
95
|
+
...(additionalArgs !== null && additionalArgs !== void 0 ? additionalArgs : []),
|
|
96
|
+
]
|
|
97
|
+
.flat(1)
|
|
98
|
+
.filter(Boolean);
|
|
99
|
+
const outputPath = await new Promise((resolve, reject) => {
|
|
100
|
+
const task = (0, node_child_process_1.spawn)(executable, args, {
|
|
101
|
+
cwd: node_path_1.default.resolve(process.cwd(), whisperPath),
|
|
102
|
+
signal: signal !== null && signal !== void 0 ? signal : undefined,
|
|
103
|
+
});
|
|
104
|
+
const predictedPath = `${tmpJSONPath}.json`;
|
|
105
|
+
let output = '';
|
|
106
|
+
const onData = (data) => {
|
|
107
|
+
const str = data.toString('utf-8');
|
|
108
|
+
const hasProgress = str.includes('progress =');
|
|
109
|
+
if (hasProgress) {
|
|
110
|
+
const progress = parseFloat(str.split('progress =')[1].trim());
|
|
111
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress / 100);
|
|
112
|
+
}
|
|
113
|
+
output += str;
|
|
114
|
+
// Sometimes it hangs here
|
|
115
|
+
if (str.includes('ggml_metal_free: deallocating')) {
|
|
116
|
+
task.kill();
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
let stderr = '';
|
|
120
|
+
const onStderr = (data) => {
|
|
121
|
+
onData(data);
|
|
122
|
+
const utf8 = data.toString('utf-8');
|
|
123
|
+
stderr += utf8;
|
|
124
|
+
if (printOutput) {
|
|
125
|
+
process.stderr.write(utf8);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
const onStdout = (data) => {
|
|
129
|
+
onData(data);
|
|
130
|
+
if (printOutput) {
|
|
131
|
+
process.stdout.write(data.toString('utf-8'));
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
task.stdout.on('data', onStdout);
|
|
135
|
+
task.stderr.on('data', onStderr);
|
|
136
|
+
task.on('exit', (code, exitSignal) => {
|
|
137
|
+
// Whisper sometimes files also with error code 0
|
|
138
|
+
// https://github.com/ggerganov/whisper.cpp/pull/1952/files
|
|
139
|
+
if ((0, node_fs_1.existsSync)(predictedPath)) {
|
|
140
|
+
resolve(predictedPath);
|
|
141
|
+
onProgress === null || onProgress === void 0 ? void 0 : onProgress(1);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (exitSignal) {
|
|
145
|
+
reject(new Error(`Process was killed with signal ${exitSignal}: ${output}`));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (stderr.includes('must be 16 kHz')) {
|
|
149
|
+
reject(new Error('wav file must be 16 kHz - See https://www.remotion.dev/docs/webcodecs/resample-audio-16khz#on-the-server on how to convert your audio to a 16-bit, 16KHz, WAVE file'));
|
|
150
|
+
}
|
|
151
|
+
reject(new Error(`No transcription was created (process exited with code ${code}): ${output}`));
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
return { outputPath };
|
|
155
|
+
};
|
|
156
|
+
const transcribe = async ({ inputPath, whisperPath, whisperCppVersion, model, modelFolder, translateToEnglish = false, tokenLevelTimestamps, printOutput = true, tokensPerItem, language, splitOnWord, signal, onProgress, flashAttention, additionalArgs, }) => {
|
|
157
|
+
if (!(0, node_fs_1.existsSync)(whisperPath)) {
|
|
158
|
+
throw new Error(`Whisper does not exist at ${whisperPath}. Double-check the passed whisperPath. If you havent installed whisper, check out the installWhisperCpp() API at https://www.remotion.dev/docs/install-whisper-cpp/install-whisper-cpp to see how to install whisper programatically.`);
|
|
159
|
+
}
|
|
160
|
+
if (!(0, node_fs_1.existsSync)(inputPath)) {
|
|
161
|
+
throw new Error(`Input file does not exist at ${inputPath}`);
|
|
162
|
+
}
|
|
163
|
+
if (!isWavFile(inputPath)) {
|
|
164
|
+
throw new Error('Invalid inputFile type. The provided file is not a wav file! Convert the file to a 16KHz wav file first: "ffmpeg -i input.mp4 -ar 16000 output.wav -y"');
|
|
165
|
+
}
|
|
166
|
+
const tmpJSONDir = node_path_1.default.join(process.cwd(), 'tmp');
|
|
167
|
+
const { outputPath: tmpJSONPath } = await transcribeToTemporaryFile({
|
|
168
|
+
fileToTranscribe: inputPath,
|
|
169
|
+
whisperPath,
|
|
170
|
+
whisperCppVersion,
|
|
171
|
+
model,
|
|
172
|
+
tmpJSONPath: tmpJSONDir,
|
|
173
|
+
modelFolder: modelFolder !== null && modelFolder !== void 0 ? modelFolder : null,
|
|
174
|
+
translate: translateToEnglish,
|
|
175
|
+
tokenLevelTimestamps,
|
|
176
|
+
printOutput,
|
|
177
|
+
tokensPerItem: tokenLevelTimestamps ? 1 : (tokensPerItem !== null && tokensPerItem !== void 0 ? tokensPerItem : 1),
|
|
178
|
+
language: language !== null && language !== void 0 ? language : null,
|
|
179
|
+
signal: signal !== null && signal !== void 0 ? signal : null,
|
|
180
|
+
splitOnWord: splitOnWord !== null && splitOnWord !== void 0 ? splitOnWord : null,
|
|
181
|
+
onProgress: onProgress !== null && onProgress !== void 0 ? onProgress : null,
|
|
182
|
+
flashAttention,
|
|
183
|
+
additionalArgs,
|
|
184
|
+
});
|
|
185
|
+
const json = (await readJson(tmpJSONPath));
|
|
186
|
+
node_fs_1.default.unlinkSync(tmpJSONPath);
|
|
187
|
+
return json;
|
|
188
|
+
};
|
|
189
|
+
exports.transcribe = transcribe;
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compares two version strings.
|
|
3
|
+
*
|
|
4
|
+
* @param version1 - The first version string.
|
|
5
|
+
* @param version2 - The second version string.
|
|
6
|
+
* @returns 1 if version1 > version2, -1 if version1 < version2, 0 if they are equal.
|
|
7
|
+
* @throws {Error} If either version is not a valid string or has an invalid format.
|
|
8
|
+
*/
|
|
9
|
+
export declare function compareVersions(version1: string, version2: string): number;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compareVersions = compareVersions;
|
|
4
|
+
/**
|
|
5
|
+
* Compares two version strings.
|
|
6
|
+
*
|
|
7
|
+
* @param version1 - The first version string.
|
|
8
|
+
* @param version2 - The second version string.
|
|
9
|
+
* @returns 1 if version1 > version2, -1 if version1 < version2, 0 if they are equal.
|
|
10
|
+
* @throws {Error} If either version is not a valid string or has an invalid format.
|
|
11
|
+
*/
|
|
12
|
+
function compareVersions(version1, version2) {
|
|
13
|
+
const parseVersion = (version) => {
|
|
14
|
+
const parts = version.split('.').map((part) => Number(part));
|
|
15
|
+
if (parts.some(isNaN)) {
|
|
16
|
+
throw new Error('Invalid version format. Expected x.x.x');
|
|
17
|
+
}
|
|
18
|
+
return parts;
|
|
19
|
+
};
|
|
20
|
+
if (typeof version1 !== 'string' || typeof version2 !== 'string') {
|
|
21
|
+
throw new Error('Both inputs should be strings. Expected x.x.x');
|
|
22
|
+
}
|
|
23
|
+
const v1Parts = parseVersion(version1);
|
|
24
|
+
const v2Parts = parseVersion(version2);
|
|
25
|
+
// Ensure both versions have the same number of parts
|
|
26
|
+
const maxLength = Math.max(v1Parts.length, v2Parts.length);
|
|
27
|
+
while (v1Parts.length < maxLength)
|
|
28
|
+
v1Parts.push(0);
|
|
29
|
+
while (v2Parts.length < maxLength)
|
|
30
|
+
v2Parts.push(0);
|
|
31
|
+
for (let i = 0; i < maxLength; i++) {
|
|
32
|
+
if (v1Parts[i] > v2Parts[i])
|
|
33
|
+
return 1; // Version1 is greater
|
|
34
|
+
if (v1Parts[i] < v2Parts[i])
|
|
35
|
+
return -1; // Version2 is greater
|
|
36
|
+
}
|
|
37
|
+
return 0; // Versions are equal
|
|
38
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"repository": {
|
|
3
|
+
"url": "https://github.com/JunWan666/remotion-zh/tree/main/packages/install-whisper-cpp"
|
|
4
|
+
},
|
|
5
|
+
"name": "@junwan666/remotion-install-whisper-cpp",
|
|
6
|
+
"version": "4.0.448-zh.0",
|
|
7
|
+
"description": "Helpers for installing and using Whisper.cpp",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"sideEffects": false,
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/remotion-dev/remotion/issues"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"formatting": "oxfmt src --check",
|
|
15
|
+
"format": "oxfmt src",
|
|
16
|
+
"lint": "eslint src",
|
|
17
|
+
"test": "bun test src",
|
|
18
|
+
"make": "tsgo -d"
|
|
19
|
+
},
|
|
20
|
+
"author": "Jonny Burger <jonny@remotion.dev>",
|
|
21
|
+
"license": "SEE LICENSE IN LICENSE.md",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@remotion/captions": "npm:@junwan666/remotion-captions@4.0.448-zh.0"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@remotion/eslint-config-internal": "npm:@junwan666/remotion-eslint-config-internal@4.0.448-zh.0",
|
|
28
|
+
"eslint": "9.19.0",
|
|
29
|
+
"@typescript/native-preview": "7.0.0-dev.20260217.1"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"remotion",
|
|
33
|
+
"openai",
|
|
34
|
+
"whisper"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"homepage": "https://www.remotion.dev/docs/install-whisper-cpp"
|
|
40
|
+
}
|