@timur00kh/whisper.wasm 0.0.1
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/LICENSE +20 -0
- package/README.md +251 -0
- package/dist/index.cjs.js +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.es.js +576 -0
- package/dist/index.umd.js +13 -0
- package/dist/libmain-D50HCaHR.js +13 -0
- package/dist/libmain-DyRJqz-4.mjs +2198 -0
- package/dist/utils/Logger.d.ts +19 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/sleep.d.ts +2 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/whisper/ModelConfig.d.ts +18 -0
- package/dist/whisper/ModelConfig.d.ts.map +1 -0
- package/dist/whisper/ModelManager.d.ts +76 -0
- package/dist/whisper/ModelManager.d.ts.map +1 -0
- package/dist/whisper/TranscriptionSession.d.ts +17 -0
- package/dist/whisper/TranscriptionSession.d.ts.map +1 -0
- package/dist/whisper/WhisperWasmService.d.ts +34 -0
- package/dist/whisper/WhisperWasmService.d.ts.map +1 -0
- package/dist/whisper/parseCueLine.d.ts +13 -0
- package/dist/whisper/parseCueLine.d.ts.map +1 -0
- package/dist/whisper/types.d.ts +31 -0
- package/dist/whisper/types.d.ts.map +1 -0
- package/package.json +99 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 whisper.wasm
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
10
|
+
subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Whisper.wasm
|
|
2
|
+
|
|
3
|
+
A TypeScript wrapper for [whisper.cpp](https://github.com/ggerganov/whisper.cpp) that brings OpenAI's Whisper speech recognition to the browser and Node.js using WebAssembly.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🎤 **High-quality speech recognition** using OpenAI Whisper models
|
|
8
|
+
- ⚡ **WebAssembly performance** - runs directly in the browser
|
|
9
|
+
- 🌍 **Multi-language support** with automatic language detection
|
|
10
|
+
- 🔄 **Translation capabilities** - translate speech to English
|
|
11
|
+
- 📱 **Cross-platform** - works in browsers and Node.js
|
|
12
|
+
- 🧠 **Multiple model sizes** - from tiny to large models
|
|
13
|
+
- 🎯 **Streaming transcription** - real-time audio processing
|
|
14
|
+
- 📦 **Zero dependencies** - no external libraries required
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install whisper.wasm
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
### Basic Usage
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
import { WhisperWasmService, ModelManager } from 'whisper.wasm';
|
|
28
|
+
|
|
29
|
+
// Initialize the service
|
|
30
|
+
const whisper = new WhisperWasmService({ logLevel: 1 });
|
|
31
|
+
const modelManager = new ModelManager();
|
|
32
|
+
|
|
33
|
+
// Check WASM support
|
|
34
|
+
const isSupported = await whisper.checkWasmSupport();
|
|
35
|
+
if (!isSupported) {
|
|
36
|
+
throw new Error('WebAssembly is not supported');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Load a model
|
|
40
|
+
const modelData = await modelManager.loadModel('base'); // or 'tiny', 'small', 'medium', 'large'
|
|
41
|
+
await whisper.loadWasmModule(modelData);
|
|
42
|
+
|
|
43
|
+
// Create a transcription session for streaming
|
|
44
|
+
const session = whisper.createSession();
|
|
45
|
+
|
|
46
|
+
// Process audio in chunks
|
|
47
|
+
const stream = session.streamimg(audioData, {
|
|
48
|
+
language: 'en',
|
|
49
|
+
threads: 4,
|
|
50
|
+
translate: false,
|
|
51
|
+
sleepMsBetweenChunks: 100,
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
for await (const segment of stream) {
|
|
55
|
+
console.log(`[${segment.timeStart}ms - ${segment.timeEnd}ms]: ${segment.text}`);
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Model Management
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { ModelManager, getAllModels } from 'whisper.wasm';
|
|
63
|
+
|
|
64
|
+
const modelManager = new ModelManager();
|
|
65
|
+
|
|
66
|
+
// Get available models
|
|
67
|
+
const availableModels = await modelManager.getAvailableModels();
|
|
68
|
+
console.log(availableModels);
|
|
69
|
+
|
|
70
|
+
// Or use the synchronous version
|
|
71
|
+
const allModels = getAllModels();
|
|
72
|
+
|
|
73
|
+
// Load a specific model
|
|
74
|
+
const modelData = await modelManager.loadModel('base', true, (progress) => {
|
|
75
|
+
console.log(`Loading progress: ${progress}%`);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Clear cache
|
|
79
|
+
await modelManager.clearCache();
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## API Reference
|
|
83
|
+
|
|
84
|
+
### WhisperWasmService
|
|
85
|
+
|
|
86
|
+
Main service class for speech recognition.
|
|
87
|
+
|
|
88
|
+
#### Constructor
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
new WhisperWasmService(options?: {
|
|
92
|
+
logLevel?: LoggerLevelsType;
|
|
93
|
+
init?: boolean;
|
|
94
|
+
})
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
#### Methods
|
|
98
|
+
|
|
99
|
+
##### `checkWasmSupport(): Promise<boolean>`
|
|
100
|
+
|
|
101
|
+
Checks if WebAssembly is supported in the current environment.
|
|
102
|
+
|
|
103
|
+
##### `loadWasmModule(model: Uint8Array): Promise<void>`
|
|
104
|
+
|
|
105
|
+
Loads a Whisper model from binary data.
|
|
106
|
+
|
|
107
|
+
**Parameters:**
|
|
108
|
+
|
|
109
|
+
- `model`: Model data as Uint8Array
|
|
110
|
+
|
|
111
|
+
##### `transcribe(audioData: Float32Array, callback?: WhisperWasmServiceCallback, options?: WhisperWasmTranscriptionOptions): Promise<TranscriptionResult>`
|
|
112
|
+
|
|
113
|
+
Transcribes audio data to text.
|
|
114
|
+
|
|
115
|
+
**Parameters:**
|
|
116
|
+
|
|
117
|
+
- `audioData`: Audio data as Float32Array (16kHz sample rate)
|
|
118
|
+
- `callback`: Optional callback function called for each transcribed segment
|
|
119
|
+
- `options`: Transcription options (optional)
|
|
120
|
+
|
|
121
|
+
**Returns:**
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
{
|
|
125
|
+
segments: WhisperWasmServiceCallbackParams[];
|
|
126
|
+
transcribeDurationMs: number;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
##### `createSession(): TranscriptionSession`
|
|
131
|
+
|
|
132
|
+
Creates a new transcription session for streaming audio.
|
|
133
|
+
|
|
134
|
+
### ModelManager
|
|
135
|
+
|
|
136
|
+
Manages Whisper model loading and caching.
|
|
137
|
+
|
|
138
|
+
#### Methods
|
|
139
|
+
|
|
140
|
+
##### `getAvailableModels(): Promise<ModelInfo[]>`
|
|
141
|
+
|
|
142
|
+
Returns information about available models.
|
|
143
|
+
|
|
144
|
+
##### `loadModel(modelId: string, useCache?: boolean, onProgress?: (progress: number) => void): Promise<Uint8Array>`
|
|
145
|
+
|
|
146
|
+
Loads a model by ID.
|
|
147
|
+
|
|
148
|
+
**Parameters:**
|
|
149
|
+
|
|
150
|
+
- `modelId`: Model identifier ('tiny', 'base', 'small', 'medium', 'large')
|
|
151
|
+
- `useCache`: Whether to use cached model if available
|
|
152
|
+
- `onProgress`: Progress callback function
|
|
153
|
+
|
|
154
|
+
##### `clearCache(): Promise<void>`
|
|
155
|
+
|
|
156
|
+
Clears the model cache.
|
|
157
|
+
|
|
158
|
+
### TranscriptionSession
|
|
159
|
+
|
|
160
|
+
Handles streaming audio transcription.
|
|
161
|
+
|
|
162
|
+
#### Methods
|
|
163
|
+
|
|
164
|
+
##### `streamimg(audioData: Float32Array, options?: ITranscriptionSessionOptions): AsyncIterableIterator<WhisperWasmServiceCallbackParams>`
|
|
165
|
+
|
|
166
|
+
Processes audio data in streaming fashion.
|
|
167
|
+
|
|
168
|
+
## Supported Models
|
|
169
|
+
|
|
170
|
+
| Model | Size | Memory | Speed | Quality |
|
|
171
|
+
| ------ | -------- | ------ | ----- | ------- |
|
|
172
|
+
| tiny | ~39 MB | ~1 GB | ~32x | ~99% |
|
|
173
|
+
| base | ~74 MB | ~1 GB | ~16x | ~99% |
|
|
174
|
+
| small | ~244 MB | ~2 GB | ~6x | ~99% |
|
|
175
|
+
| medium | ~769 MB | ~5 GB | ~2x | ~99% |
|
|
176
|
+
| large | ~1550 MB | ~10 GB | ~1x | ~99% |
|
|
177
|
+
|
|
178
|
+
## Browser Support
|
|
179
|
+
|
|
180
|
+
- **Chrome**: 57+
|
|
181
|
+
- **Firefox**: 52+
|
|
182
|
+
- **Safari**: 11+
|
|
183
|
+
- **Edge**: 16+
|
|
184
|
+
|
|
185
|
+
## Demo
|
|
186
|
+
|
|
187
|
+
Try the interactive demo:
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
npm run dev:demo
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
The demo includes:
|
|
194
|
+
|
|
195
|
+
- Audio file upload and processing
|
|
196
|
+
- Model selection and loading
|
|
197
|
+
- Real-time transcription with progress
|
|
198
|
+
- Language detection and translation
|
|
199
|
+
- Streaming audio support
|
|
200
|
+
|
|
201
|
+
## Development
|
|
202
|
+
|
|
203
|
+
### Prerequisites
|
|
204
|
+
|
|
205
|
+
- Node.js 18+
|
|
206
|
+
- npm or yarn
|
|
207
|
+
|
|
208
|
+
### Setup
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
git clone https://github.com/timur00kh/whisper.wasm.git
|
|
212
|
+
cd whisper.wasm
|
|
213
|
+
npm install
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Build
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
# Build the library
|
|
220
|
+
npm run build:lib
|
|
221
|
+
|
|
222
|
+
# Build the demo
|
|
223
|
+
npm run build:demo
|
|
224
|
+
|
|
225
|
+
# Run development server
|
|
226
|
+
npm run dev:demo
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Testing
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
npm test
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
MIT
|
|
238
|
+
|
|
239
|
+
## Acknowledgments
|
|
240
|
+
|
|
241
|
+
- [whisper.cpp](https://github.com/ggerganov/whisper.cpp) - High-performance C++ implementation of Whisper
|
|
242
|
+
- [OpenAI Whisper](https://github.com/openai/whisper) - The original speech recognition model
|
|
243
|
+
- [Emscripten](https://emscripten.org/) - WebAssembly compilation toolkit
|
|
244
|
+
|
|
245
|
+
## Contributing
|
|
246
|
+
|
|
247
|
+
1. Fork the repository
|
|
248
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
249
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
250
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
251
|
+
5. Open a Pull Request
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const p=class p{constructor(e=p.levels.INFO,t=""){this.level=e,this.prefix=t}debug(...e){this.level<=p.levels.DEBUG&&console.debug(`[${this.prefix}] [DEBUG]`,...e)}info(...e){this.level<=p.levels.INFO&&console.info(`[${this.prefix}] [INFO]`,...e)}warn(...e){this.level<=p.levels.WARN&&console.warn(`[${this.prefix}] [WARN]`,...e)}error(...e){this.level<=p.levels.ERROR&&console.error(`[${this.prefix}] [ERROR]`,...e)}setLevel(e){this.level=e}getLevel(){return this.level}};p.levels={DEBUG:0,INFO:1,WARN:2,ERROR:3};let b=p;const B=async()=>WebAssembly.validate(new Uint8Array([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,10,10,1,8,0,65,0,253,15,253,98,11])),T={language:"auto",threads:4,translate:!1};function M(l){const e=String(l).trim().replace(",","."),t=e.split(":").map(Number);if(t.some(Number.isNaN))throw new Error(`Bad time: "${l}"`);let r=0,a=0,i=0;if(t.length===3)[r,a]=t,i=parseFloat(e.split(":").pop()||"0");else if(t.length===2)[a]=t,i=parseFloat(e.split(":").pop()||"0");else throw new Error(`Bad time format: "${l}"`);return Math.floor(((r*60+a)*60+i)*1e3)}function R(l){const t=/^\s*\[?\s*([0-9]{1,2}:[0-9]{2}:(?:[0-9]{2}[.,][0-9]{1,3})|[0-9]{1,2}:[0-9]{2}[.,][0-9]{1,3})\s*-->\s*([0-9]{1,2}:[0-9]{2}:(?:[0-9]{2}[.,][0-9]{1,3})|[0-9]{1,2}:[0-9]{2}[.,][0-9]{1,3})\s*\]?\s*(.*)\s*$/.exec(l);if(!t)throw new Error("Line does not match VTT-like pattern: "+l);const r=t[1],a=t[2],i=t[3]||"",n=M(r),s=M(a);if(s<n)throw new Error("End time is before start time");return{startMs:n,endMs:s,start:r,end:a,text:i}}function q(l){return new Promise(e=>setTimeout(e,l))}function W(l,e=16e3*100){const t=[];for(let r=0;r<l.length;r+=e)t.push(l.subarray(r,r+e));return t}class L{constructor(e,t){this.whisperService=e,this.logger=new b((t==null?void 0:t.logLevel)||b.levels.ERROR,"TranscriptionSession")}async*streamimg(e,t={}){const r=W(e);let a=0;for await(const i of r){const n=[];let s=null,o=!1,h,d=0;for(this.whisperService.transcribe(i,c=>{d=c.timeEnd,c.timeStart+=a,c.timeEnd+=a,s?(s(c),s=null):n.push(c)},t).then(()=>{o=!0,a+=d,s==null||s(void 0)}).catch(c=>{h=c});;){if(h)throw h;if(o)break;if(n.length)yield n.shift();else{const c=await new Promise(f=>s=f);c&&(yield c)}}t.sleepMsBetweenChunks&&await q(t.sleepMsBetweenChunks)}}}class x extends EventTarget{on(e,t){return this.addEventListener(e,t),()=>this.removeEventListener(e,t)}emit(e,t){this.dispatchEvent(new CustomEvent(e,{detail:t}))}}class A{constructor(e){this.wasmModule=null,this.instance=null,this.modelFileName="whisper.bin",this.isTranscribing=!1,this.bus=new x,this.logger=new b((e==null?void 0:e.logLevel)??b.levels.ERROR,"WhisperWasmService"),e!=null&&e.init&&this.loadWasmScript()}async checkWasmSupport(){return await B()}async loadWasmScript(){this.wasmModule=await(await Promise.resolve().then(()=>require("./libmain-D50HCaHR.js"))).default({print:(e,...t)=>{this.logger.debug(t),e.startsWith("[")?(this.logger.info(e),this.bus.emit("transcribe",e)):(this.logger.debug(e),this.bus.emit("system_info",e))},printErr:(e,...t)=>{this.logger.debug(t),this.logger.warn(e),this.bus.emit("transcribeError",e)}})}async loadWasmModule(e){if(!await this.checkWasmSupport())throw new Error("WASM is not supported");return this.wasmModule&&(this.wasmModule.FS_unlink(this.modelFileName),this.wasmModule.free()),await this.loadWasmScript(),await q(100),this.storeFS(this.modelFileName,e),this.instance=this.wasmModule.init(this.modelFileName),Promise.resolve()}storeFS(e,t){if(!this.wasmModule)throw new Error("WASM module not loaded");try{this.wasmModule.FS_unlink(e)}catch{}this.wasmModule.FS_createDataFile("/",e,t,!0,!0,!0)}async transcribe(e,t,r={}){if(this.isTranscribing)throw new Error("Already transcribing");if(!this.wasmModule)throw new Error("WASM module not loaded");if(!this.instance)throw new Error("WASM instance not loaded");const a=120;e.length>16e3*a&&this.logger.warn("It's not recommended to transcribe audio data that is longer than 120 seconds"),this.isTranscribing=!0;const{language:i,threads:n,translate:s}={...T,...r},o=[],h=Date.now();return this.wasmModule.full_default(this.instance,e,i,n,s),await new Promise((d,c)=>{const f=this.bus.on("transcribe",g=>{const{startMs:w,endMs:y,text:_}=R(g.detail),v={timeStart:w,timeEnd:y,text:_,raw:g.detail};o.push(v),t==null||t(v)}),u=setTimeout(()=>{this.isTranscribing=!1,f(),m(),this.logger.error("Transcribe timeout"),c(new Error("Transcribe timeout"))},a*2*1e3),m=this.bus.on("transcribeError",g=>{this.isTranscribing=!1,f(),m(),clearTimeout(u),this.logger.debug("Transcribe error",g.detail),d({segments:o,transcribeDurationMs:Date.now()-h})})})}createSession(){return new L(this,{logLevel:this.logger.getLevel()})}}const S={"tiny.en":{id:"tiny.en",name:"Tiny English",size:75,language:"en",quantized:!1,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.en.bin"},tiny:{id:"tiny",name:"Tiny Multilingual",size:75,language:"multilingual",quantized:!1,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.bin"},"base.en":{id:"base.en",name:"Base English",size:142,language:"en",quantized:!1,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin"},base:{id:"base",name:"Base Multilingual",size:142,language:"multilingual",quantized:!1,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin"},"small.en":{id:"small.en",name:"Small English",size:466,language:"en",quantized:!1,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin"},small:{id:"small",name:"Small Multilingual",size:466,language:"multilingual",quantized:!1,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin"},"tiny.en-q5_1":{id:"tiny.en-q5_1",name:"Tiny English (Q5_1)",size:31,language:"en",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.en-q5_1.bin"},"tiny-q5_1":{id:"tiny-q5_1",name:"Tiny Multilingual (Q5_1)",size:31,language:"multilingual",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny-q5_1.bin"},"base.en-q5_1":{id:"base.en-q5_1",name:"Base English (Q5_1)",size:57,language:"en",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en-q5_1.bin"},"base-q5_1":{id:"base-q5_1",name:"Base Multilingual (Q5_1)",size:57,language:"multilingual",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base-q5_1.bin"},"small.en-q5_1":{id:"small.en-q5_1",name:"Small English (Q5_1)",size:182,language:"en",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en-q5_1.bin"},"small-q5_1":{id:"small-q5_1",name:"Small Multilingual (Q5_1)",size:182,language:"multilingual",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small-q5_1.bin"},"medium.en-q5_0":{id:"medium.en-q5_0",name:"Medium English (Q5_0)",size:515,language:"en",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.en-q5_0.bin"},"medium-q5_0":{id:"medium-q5_0",name:"Medium Multilingual (Q5_0)",size:515,language:"multilingual",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium-q5_0.bin"},"large-q5_0":{id:"large-q5_0",name:"Large Multilingual (Q5_0)",size:1030,language:"multilingual",quantized:!0,url:"https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-large-q5_0.bin"}};function z(){return Object.values(S).map(({url:l,...e})=>e)}function E(l){return S[l]}class F{constructor(e={logLevel:b.levels.ERROR}){this.cacheEnabled=!0,this.models=z(),this.logger=new b(e.logLevel,"ModelManager")}async loadModel(e,t=!0,r){var m;const a=E(e);if(!a)throw new Error(`Model ${e} not found in config`);if(this.cacheEnabled&&t){const g=await this.getCachedModel(e);if(g)return this.logger.info(`Model ${e} loaded from cache`),r&&r(100),g}this.logger.info(`Loading model ${e} from ${a.url}`);const i=await fetch(a.url);if(!i.ok)throw new Error(`Failed to load model: ${i.statusText}`);const n=i.headers.get("content-length"),s=n?parseInt(n,10):0;let o=0;const h=(m=i.body)==null?void 0:m.getReader();if(!h)throw new Error("Response body is not readable");const d=[];try{let g=!1;for(;!g;){const w=await h.read();if(g=w.done,!g&&w.value&&(d.push(w.value),o+=w.value.length,r&&s>0)){const y=Math.round(o/s*100);r(y)}}}finally{h.releaseLock()}const c=d.reduce((g,w)=>g+w.length,0),f=new Uint8Array(c);let u=0;for(const g of d)f.set(g,u),u+=g.length;return this.cacheEnabled&&t&&await this.saveModelToCache(e,f),r&&r(100),f}async loadModelByUrl(e,t){var r;try{if(this.cacheEnabled){const u=await this.getCachedModelByUrl(e);if(u)return this.logger.info(`WASM module loaded from cache by URL: ${e}`),t&&t(100),u}this.logger.info(`Loading WASM module from URL: ${e}`);const a=await fetch(e);if(!a.ok)throw new Error(`Failed to load WASM module: ${a.statusText}`);const i=a.headers.get("content-length"),n=i?parseInt(i,10):0;let s=0;const o=(r=a.body)==null?void 0:r.getReader();if(!o)throw new Error("Response body is not readable");const h=[];try{let u=!1;for(;!u;){const m=await o.read();if(u=m.done,!u&&m.value&&(h.push(m.value),s+=m.value.length,t&&n>0)){const g=Math.round(s/n*100);t(g)}}}finally{o.releaseLock()}const d=h.reduce((u,m)=>u+m.length,0),c=new Uint8Array(d);let f=0;for(const u of h)c.set(u,f),f+=u.length;return this.cacheEnabled&&await this.saveModelToCacheByUrl(e,c),t&&t(100),c}catch(a){throw this.logger.error(a),new Error("Failed to load WASM module")}}async getCachedModelByUrl(e){try{const a=(await this.openIndexedDB()).transaction(["modelsByUrl"],"readonly").objectStore("modelsByUrl");return new Promise((i,n)=>{const s=a.get(e);s.onsuccess=()=>{const o=s.result;o&&o.data?i(o.data):i(null)},s.onerror=()=>n(s.error)})}catch(t){return this.logger.error("Error reading model from cache by URL:",t),null}}async saveModelToCacheByUrl(e,t){try{const i=(await this.openIndexedDB()).transaction(["modelsByUrl"],"readwrite").objectStore("modelsByUrl");await new Promise((n,s)=>{const o=i.put({url:e,data:t,timestamp:Date.now(),size:t.length});o.onsuccess=()=>n(),o.onerror=()=>s(o.error)}),this.logger.info(`Model saved to cache by URL: ${e}`)}catch(r){this.logger.error("Error saving model to cache by URL:",r)}}async getAvailableModels(){const e=[...this.models];if(!this.cacheEnabled)return e;try{const t=await this.getCachedModelNames();return e.map(r=>({...r,cached:t.includes(r.id)}))}catch(t){return this.logger.error("Error checking cache status:",t),e}}getAvailableModelsSync(){return[...this.models]}getModelConfig(e){return E(e)}async saveModelToCache(e,t){try{const i=(await this.openIndexedDB()).transaction(["models"],"readwrite").objectStore("models");await new Promise((n,s)=>{const o=i.put({name:e,data:t,timestamp:Date.now(),size:t.length});o.onsuccess=()=>n(),o.onerror=()=>s(o.error)}),this.logger.info(`Model ${e} saved to cache`)}catch(r){this.logger.error("Error saving model to cache:",r)}}async getCachedModel(e){try{const a=(await this.openIndexedDB()).transaction(["models"],"readonly").objectStore("models");return new Promise((i,n)=>{const s=a.get(e);s.onsuccess=()=>{const o=s.result;o&&o.data?i(o.data):i(null)},s.onerror=()=>n(s.error)})}catch(t){return this.logger.error("Error getting cached model:",t),null}}async getCachedModelNames(){try{const r=(await this.openIndexedDB()).transaction(["models"],"readonly").objectStore("models");return new Promise((a,i)=>{const n=r.getAllKeys();n.onsuccess=()=>{const s=n.result;a(s)},n.onerror=()=>i(n.error)})}catch(e){return this.logger.error("Error getting cached model names:",e),[]}}async openIndexedDB(){return new Promise((e,t)=>{const r=indexedDB.open("WhisperModels",2);r.onerror=()=>t(r.error),r.onsuccess=()=>e(r.result),r.onupgradeneeded=a=>{const i=a.target.result;if(!i.objectStoreNames.contains("models")){const n=i.createObjectStore("models",{keyPath:"name"});n.createIndex("timestamp","timestamp",{unique:!1}),n.createIndex("size","size",{unique:!1})}if(!i.objectStoreNames.contains("modelsByUrl")){const n=i.createObjectStore("modelsByUrl",{keyPath:"url"});n.createIndex("timestamp","timestamp",{unique:!1}),n.createIndex("size","size",{unique:!1})}}})}async clearCache(){try{const t=(await this.openIndexedDB()).transaction(["models","modelsByUrl"],"readwrite"),r=t.objectStore("models");await new Promise((i,n)=>{const s=r.clear();s.onsuccess=()=>i(),s.onerror=()=>n(s.error)});const a=t.objectStore("modelsByUrl");await new Promise((i,n)=>{const s=a.clear();s.onsuccess=()=>i(),s.onerror=()=>n(s.error)}),this.logger.info("Model cache cleared")}catch(e){this.logger.error("Error clearing cache:",e)}}async getCacheInfo(){try{const r=(await this.openIndexedDB()).transaction(["models"],"readonly").objectStore("models");return new Promise((a,i)=>{const n=r.getAll();n.onsuccess=()=>{const s=n.result,o=s.reduce((h,d)=>h+(d.size||0),0);a({count:s.length,totalSize:o})},n.onerror=()=>i(n.error)})}catch(e){return this.logger.error("Error getting cache info:",e),{count:0,totalSize:0}}}}exports.ModelManager=F;exports.WhisperWasmService=A;exports.getAllModels=z;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { WhisperWasmService } from './whisper/WhisperWasmService';
|
|
2
|
+
export type { WhisperWasmModule } from './whisper/types';
|
|
3
|
+
export { ModelManager } from './whisper/ModelManager';
|
|
4
|
+
export { getAllModels } from './whisper/ModelConfig';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAClE,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC"}
|