@remotion/install-whisper-cpp 4.0.155 → 4.0.156

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.
@@ -2,9 +2,10 @@ import { type OnProgress } from './download';
2
2
  declare const models: readonly ["tiny", "tiny.en", "base", "base.en", "small", "small.en", "medium", "medium.en", "large-v1", "large-v2", "large-v3"];
3
3
  export type WhisperModel = (typeof models)[number];
4
4
  export declare const getModelPath: (folder: string, model: WhisperModel) => string;
5
- export declare const downloadWhisperModel: ({ model, folder, printOutput, onProgress, }: {
5
+ export declare const downloadWhisperModel: ({ model, folder, printOutput, onProgress, signal, }: {
6
6
  model: WhisperModel;
7
7
  folder: string;
8
+ signal?: AbortSignal | undefined;
8
9
  printOutput?: boolean | undefined;
9
10
  onProgress?: OnProgress | undefined;
10
11
  }) => Promise<{
@@ -43,20 +43,40 @@ const models = [
43
43
  'large-v2',
44
44
  'large-v3',
45
45
  ];
46
+ const modelSizes = {
47
+ 'medium.en': 1533774781,
48
+ 'base.en': 147964211,
49
+ 'large-v1': 3094623691,
50
+ 'large-v2': 3094623691,
51
+ 'large-v3': 3095033483,
52
+ small: 487601967,
53
+ tiny: 77691713,
54
+ 'small.en': 487614201,
55
+ 'tiny.en': 77704715,
56
+ base: 147951465,
57
+ medium: 1533774781,
58
+ };
46
59
  const getModelPath = (folder, model) => {
47
60
  return path_1.default.join(folder, `ggml-${model}.bin`);
48
61
  };
49
62
  exports.getModelPath = getModelPath;
50
- const downloadWhisperModel = async ({ model, folder, printOutput = true, onProgress, }) => {
63
+ const downloadWhisperModel = async ({ model, folder, printOutput = true, onProgress, signal, }) => {
51
64
  if (!models.includes(model)) {
52
65
  throw new Error(`Invalid whisper model ${model}. Available: ${models.join(', ')}`);
53
66
  }
54
67
  const filePath = (0, exports.getModelPath)(folder, model);
55
68
  if ((0, fs_1.existsSync)(filePath)) {
69
+ const { size } = fs_1.default.statSync(filePath);
70
+ if (size === modelSizes[model]) {
71
+ if (printOutput) {
72
+ console.log(`Model already exists at ${filePath}`);
73
+ }
74
+ return Promise.resolve({ alreadyExisted: true });
75
+ }
56
76
  if (printOutput) {
57
- console.log(`Model already exists at ${filePath}`);
77
+ throw new Error(`Model ${model} already exists at ${filePath}, but the size is ${size} bytes (expected ${modelSizes[model]} bytes). Delete ${filePath} and try again.`);
58
78
  }
59
- return Promise.resolve({ alreadyExisted: true });
79
+ return Promise.resolve({ alreadyExisted: false });
60
80
  }
61
81
  const baseModelUrl = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-${model}.bin`;
62
82
  if (printOutput) {
@@ -68,6 +88,7 @@ const downloadWhisperModel = async ({ model, folder, printOutput = true, onProgr
68
88
  url: baseModelUrl,
69
89
  printOutput,
70
90
  onProgress,
91
+ signal: signal !== null && signal !== void 0 ? signal : null,
71
92
  });
72
93
  return { alreadyExisted: false };
73
94
  };
@@ -6,9 +6,10 @@
6
6
  /// <reference types="bun-types" />
7
7
  /// <reference types="bun-types" />
8
8
  export type OnProgress = (downloadedBytes: number, totalBytes: number) => void;
9
- export declare const downloadFile: ({ fileStream, url, printOutput, onProgress, }: {
9
+ export declare const downloadFile: ({ fileStream, url, printOutput, onProgress, signal, }: {
10
10
  fileStream: NodeJS.WritableStream;
11
11
  url: string;
12
12
  printOutput: boolean;
13
13
  onProgress: OnProgress | undefined;
14
+ signal: AbortSignal | null;
14
15
  }) => Promise<void>;
package/dist/download.js CHANGED
@@ -1,8 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.downloadFile = void 0;
4
- const downloadFile = async ({ fileStream, url, printOutput, onProgress, }) => {
5
- const { body, headers } = await fetch(url);
4
+ const downloadFile = async ({ fileStream, url, printOutput, onProgress, signal, }) => {
5
+ const { body, headers } = await fetch(url, {
6
+ signal,
7
+ });
6
8
  const contentLength = headers.get('content-length');
7
9
  if (body === null) {
8
10
  throw new Error('Failed to download whisper model');
package/dist/index.d.ts CHANGED
@@ -3,4 +3,4 @@ export type { OnProgress } from './download';
3
3
  export { WhisperModel, downloadWhisperModel } from './download-whisper-model';
4
4
  export { installWhisperCpp } from './install-whisper-cpp';
5
5
  export type { Language } from './languages';
6
- export { TranscriptionJson, transcribe } from './transcribe';
6
+ export { TranscribeOnProgress, TranscriptionJson, transcribe, } from './transcribe';
@@ -1,7 +1,8 @@
1
1
  export declare const getWhisperExecutablePath: (whisperPath: string) => string;
2
- export declare const installWhisperCpp: ({ version, to, printOutput, }: {
2
+ export declare const installWhisperCpp: ({ version, to, printOutput, signal, }: {
3
3
  version: string;
4
4
  to: string;
5
+ signal?: AbortSignal | null | undefined;
5
6
  printOutput?: boolean | undefined;
6
7
  }) => Promise<{
7
8
  alreadyExisted: boolean;
@@ -27,15 +27,34 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.installWhisperCpp = exports.getWhisperExecutablePath = void 0;
30
+ const child_process_1 = require("child_process");
30
31
  const fs_1 = __importStar(require("fs"));
31
- const node_child_process_1 = require("node:child_process");
32
32
  const os_1 = __importDefault(require("os"));
33
33
  const path_1 = __importDefault(require("path"));
34
34
  const download_1 = require("./download");
35
35
  const getIsSemVer = (str) => {
36
36
  return /^[\d]{1}\.[\d]{1,2}\.+/.test(str);
37
37
  };
38
- const installForWindows = async ({ version, to, printOutput, }) => {
38
+ const execute = ({ command, printOutput, signal, cwd, shell, }) => {
39
+ const stdio = printOutput ? 'inherit' : 'ignore';
40
+ return new Promise((resolve, reject) => {
41
+ const [bin, ...args] = command.split(' ');
42
+ const child = (0, child_process_1.spawn)(bin, args, {
43
+ stdio,
44
+ signal: signal !== null && signal !== void 0 ? signal : undefined,
45
+ cwd: cwd !== null && cwd !== void 0 ? cwd : undefined,
46
+ shell: shell !== null && shell !== void 0 ? shell : undefined,
47
+ });
48
+ child.on('exit', (code, exitSignal) => {
49
+ if (code !== 0) {
50
+ reject(new Error(`Error while executing ${command}. Exit code: ${code}, signal: ${exitSignal}`));
51
+ return;
52
+ }
53
+ resolve();
54
+ });
55
+ });
56
+ };
57
+ const installForWindows = async ({ version, to, printOutput, signal, }) => {
39
58
  if (!getIsSemVer(version)) {
40
59
  throw new Error(`Non-semantic version provided. Only releases of Whisper.cpp are supported on Windows (e.g., 1.5.4). Provided version:
41
60
  ${version}. See https://www.remotion.dev/docs/install-whisper-cpp/install-whisper-cpp#version for more information.`);
@@ -50,26 +69,39 @@ const installForWindows = async ({ version, to, printOutput, }) => {
50
69
  printOutput,
51
70
  url,
52
71
  onProgress: undefined,
72
+ signal,
53
73
  });
54
- (0, node_child_process_1.execSync)(`Expand-Archive -Force ${filePath} ${to}`, {
74
+ execute({
75
+ command: `Expand-Archive -Force ${filePath} ${to}`,
55
76
  shell: 'powershell',
56
- stdio: printOutput ? 'inherit' : 'ignore',
77
+ printOutput,
78
+ signal,
79
+ cwd: null,
57
80
  });
58
81
  (0, fs_1.rmSync)(filePath);
59
82
  };
60
- const installWhisperForUnix = ({ version, to, printOutput, }) => {
61
- const stdio = printOutput ? 'inherit' : 'ignore';
62
- (0, node_child_process_1.execSync)(`git clone https://github.com/ggerganov/whisper.cpp.git ${to}`, {
63
- stdio,
83
+ const installWhisperForUnix = async ({ version, to, printOutput, signal, }) => {
84
+ await execute({
85
+ command: `git clone https://github.com/ggerganov/whisper.cpp.git ${to}`,
86
+ printOutput,
87
+ signal,
88
+ cwd: null,
89
+ shell: null,
64
90
  });
65
91
  const ref = getIsSemVer(version) ? `v${version}` : version;
66
- (0, node_child_process_1.execSync)(`git checkout ${ref}`, {
67
- stdio,
92
+ await execute({
93
+ command: `git checkout ${ref}`,
94
+ printOutput,
68
95
  cwd: to,
96
+ signal,
97
+ shell: null,
69
98
  });
70
- (0, node_child_process_1.execSync)(`make`, {
99
+ await execute({
100
+ command: 'make',
71
101
  cwd: to,
72
- stdio,
102
+ signal,
103
+ printOutput,
104
+ shell: null,
73
105
  });
74
106
  };
75
107
  const getWhisperExecutablePath = (whisperPath) => {
@@ -78,11 +110,11 @@ const getWhisperExecutablePath = (whisperPath) => {
78
110
  : path_1.default.join(whisperPath, './main');
79
111
  };
80
112
  exports.getWhisperExecutablePath = getWhisperExecutablePath;
81
- const installWhisperCpp = async ({ version, to, printOutput = true, }) => {
113
+ const installWhisperCpp = async ({ version, to, printOutput = true, signal, }) => {
82
114
  if ((0, fs_1.existsSync)(to)) {
83
115
  if (!(0, fs_1.existsSync)((0, exports.getWhisperExecutablePath)(to))) {
84
116
  if (printOutput) {
85
- console.log(`Whisper folder exists but the executable (${to}) is missing. Delete ${to} and try again.`);
117
+ throw new Error(`Whisper folder ${to} exists but the executable (${(0, exports.getWhisperExecutablePath)(to)}) is missing. Delete ${to} and try again.`);
86
118
  }
87
119
  return Promise.resolve({ alreadyExisted: false });
88
120
  }
@@ -92,11 +124,16 @@ const installWhisperCpp = async ({ version, to, printOutput = true, }) => {
92
124
  return Promise.resolve({ alreadyExisted: true });
93
125
  }
94
126
  if (process.platform === 'darwin' || process.platform === 'linux') {
95
- installWhisperForUnix({ version, to, printOutput });
127
+ await installWhisperForUnix({
128
+ version,
129
+ to,
130
+ printOutput,
131
+ signal: signal !== null && signal !== void 0 ? signal : null,
132
+ });
96
133
  return Promise.resolve({ alreadyExisted: false });
97
134
  }
98
135
  if (process.platform === 'win32') {
99
- await installForWindows({ version, to, printOutput });
136
+ await installForWindows({ version, to, printOutput, signal: signal !== null && signal !== void 0 ? signal : null });
100
137
  return Promise.resolve({ alreadyExisted: false });
101
138
  }
102
139
  throw new Error(`Unsupported platform: ${process.platform}`);
@@ -58,7 +58,8 @@ export type TranscriptionJson<WithTokenLevelTimestamp extends boolean> = {
58
58
  result: Result;
59
59
  transcription: true extends WithTokenLevelTimestamp ? TranscriptionItemWithTimestamp[] : TranscriptionItem[];
60
60
  };
61
- export declare const transcribe: <HasTokenLevelTimestamps extends boolean>({ inputPath, whisperPath, model, modelFolder, translateToEnglish, tokenLevelTimestamps, printOutput, tokensPerItem, language, }: {
61
+ export type TranscribeOnProgress = (progress: number) => void;
62
+ export declare const transcribe: <HasTokenLevelTimestamps extends boolean>({ inputPath, whisperPath, model, modelFolder, translateToEnglish, tokenLevelTimestamps, printOutput, tokensPerItem, language, signal, onProgress, }: {
62
63
  inputPath: string;
63
64
  whisperPath: string;
64
65
  model: WhisperModel;
@@ -68,5 +69,7 @@ export declare const transcribe: <HasTokenLevelTimestamps extends boolean>({ inp
68
69
  printOutput?: boolean | undefined;
69
70
  tokensPerItem?: (true extends HasTokenLevelTimestamps ? never : number | null) | undefined;
70
71
  language?: Language | null | undefined;
72
+ signal?: AbortSignal | undefined;
73
+ onProgress?: TranscribeOnProgress | undefined;
71
74
  }) => Promise<TranscriptionJson<HasTokenLevelTimestamps>>;
72
75
  export {};
@@ -43,7 +43,7 @@ const readJson = async (jsonPath) => {
43
43
  const data = await node_fs_1.default.promises.readFile(jsonPath, 'utf8');
44
44
  return JSON.parse(data);
45
45
  };
46
- const transcribeToTempJSON = async ({ fileToTranscribe, whisperPath, model, tmpJSONPath, modelFolder, translate, tokenLevelTimestamps, printOutput, tokensPerItem, language, }) => {
46
+ const transcribeToTemporaryFile = async ({ fileToTranscribe, whisperPath, model, tmpJSONPath, modelFolder, translate, tokenLevelTimestamps, printOutput, tokensPerItem, language, signal, onProgress, }) => {
47
47
  const modelPath = (0, download_whisper_model_1.getModelPath)(modelFolder !== null && modelFolder !== void 0 ? modelFolder : whisperPath, model);
48
48
  if (!node_fs_1.default.existsSync(modelPath)) {
49
49
  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`);
@@ -59,6 +59,7 @@ const transcribeToTempJSON = async ({ fileToTranscribe, whisperPath, model, tmpJ
59
59
  '-ojf', // Output full JSON
60
60
  tokenLevelTimestamps ? ['--dtw', model] : null,
61
61
  model ? [`-m`, `${modelPath}`] : null,
62
+ ['-pp'], // print progress
62
63
  translate ? '-tr' : null,
63
64
  language ? ['-l', language.toLowerCase()] : null,
64
65
  ]
@@ -67,11 +68,17 @@ const transcribeToTempJSON = async ({ fileToTranscribe, whisperPath, model, tmpJ
67
68
  const outputPath = await new Promise((resolve, reject) => {
68
69
  const task = (0, node_child_process_1.spawn)(executable, args, {
69
70
  cwd: whisperPath,
71
+ signal: signal !== null && signal !== void 0 ? signal : undefined,
70
72
  });
71
73
  const predictedPath = `${tmpJSONPath}.json`;
72
74
  let output = '';
73
75
  const onData = (data) => {
74
76
  const str = data.toString('utf-8');
77
+ const hasProgress = str.includes('progress =');
78
+ if (hasProgress) {
79
+ const progress = parseFloat(str.split('progress =')[1].trim());
80
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(progress / 100);
81
+ }
75
82
  output += str;
76
83
  // Sometimes it hangs here
77
84
  if (str.includes('ggml_metal_free: deallocating')) {
@@ -92,15 +99,16 @@ const transcribeToTempJSON = async ({ fileToTranscribe, whisperPath, model, tmpJ
92
99
  };
93
100
  task.stdout.on('data', onStdout);
94
101
  task.stderr.on('data', onStderr);
95
- task.on('exit', (code, signal) => {
102
+ task.on('exit', (code, exitSignal) => {
96
103
  // Whisper sometimes files also with error code 0
97
104
  // https://github.com/ggerganov/whisper.cpp/pull/1952/files
98
105
  if ((0, node_fs_1.existsSync)(predictedPath)) {
99
106
  resolve(predictedPath);
107
+ onProgress === null || onProgress === void 0 ? void 0 : onProgress(1);
100
108
  return;
101
109
  }
102
- if (signal) {
103
- reject(new Error(`Process was killed with signal ${signal}: ${output}`));
110
+ if (exitSignal) {
111
+ reject(new Error(`Process was killed with signal ${exitSignal}: ${output}`));
104
112
  return;
105
113
  }
106
114
  reject(new Error(`No transcription was created (process exited with code ${code}): ${output}`));
@@ -108,7 +116,7 @@ const transcribeToTempJSON = async ({ fileToTranscribe, whisperPath, model, tmpJ
108
116
  });
109
117
  return { outputPath };
110
118
  };
111
- const transcribe = async ({ inputPath, whisperPath, model, modelFolder, translateToEnglish = false, tokenLevelTimestamps, printOutput = true, tokensPerItem, language, }) => {
119
+ const transcribe = async ({ inputPath, whisperPath, model, modelFolder, translateToEnglish = false, tokenLevelTimestamps, printOutput = true, tokensPerItem, language, signal, onProgress, }) => {
112
120
  if (!(0, node_fs_1.existsSync)(whisperPath)) {
113
121
  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.`);
114
122
  }
@@ -119,7 +127,7 @@ const transcribe = async ({ inputPath, whisperPath, model, modelFolder, translat
119
127
  throw new Error('Invalid inputFile type. The provided file is not a wav file!');
120
128
  }
121
129
  const tmpJSONDir = node_path_1.default.join(process.cwd(), 'tmp');
122
- const { outputPath: tmpJSONPath } = await transcribeToTempJSON({
130
+ const { outputPath: tmpJSONPath } = await transcribeToTemporaryFile({
123
131
  fileToTranscribe: inputPath,
124
132
  whisperPath,
125
133
  model,
@@ -129,7 +137,9 @@ const transcribe = async ({ inputPath, whisperPath, model, modelFolder, translat
129
137
  tokenLevelTimestamps,
130
138
  printOutput,
131
139
  tokensPerItem: tokenLevelTimestamps ? 1 : tokensPerItem !== null && tokensPerItem !== void 0 ? tokensPerItem : 1,
132
- language,
140
+ language: language !== null && language !== void 0 ? language : null,
141
+ signal: signal !== null && signal !== void 0 ? signal : null,
142
+ onProgress: onProgress !== null && onProgress !== void 0 ? onProgress : null,
133
143
  });
134
144
  const json = (await readJson(tmpJSONPath));
135
145
  node_fs_1.default.unlinkSync(tmpJSONPath);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remotion/install-whisper-cpp",
3
- "version": "4.0.155",
3
+ "version": "4.0.156",
4
4
  "description": "Install helper for Whisper.cpp",
5
5
  "main": "dist/index.js",
6
6
  "sideEffects": false,