@timur00kh/whisper.wasm 0.0.6 → 0.0.7

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 CHANGED
@@ -16,7 +16,7 @@ A TypeScript wrapper for [whisper.cpp](https://github.com/ggerganov/whisper.cpp)
16
16
  ## Installation
17
17
 
18
18
  ```bash
19
- npm install @timur00kh/whisper.wasm
19
+ npm install @timur00kh/whisper.wasm@canary
20
20
  ```
21
21
 
22
22
  ## Quick Start
@@ -24,7 +24,7 @@ npm install @timur00kh/whisper.wasm
24
24
  ### Basic Usage
25
25
 
26
26
  ```typescript
27
- import { WhisperWasmService, ModelManager } from 'whisper.wasm';
27
+ import { WhisperWasmService, ModelManager } from '@timur00kh/whisper.wasm';
28
28
 
29
29
  // Initialize the service
30
30
  const whisper = new WhisperWasmService({ logLevel: 1 });
@@ -59,7 +59,7 @@ for await (const segment of stream) {
59
59
  ### Model Management
60
60
 
61
61
  ```typescript
62
- import { ModelManager, getAllModels } from 'whisper.wasm';
62
+ import { ModelManager, getAllModels } from '@timur00kh/whisper.wasm';
63
63
 
64
64
  const modelManager = new ModelManager();
65
65
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../src/__tests__/setup.ts"],"names":[],"mappings":""}
package/dist/index.cjs.js CHANGED
@@ -1 +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;
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="auto",threads:n=4,translate:s=!1}={...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.es.js CHANGED
@@ -49,12 +49,12 @@ function T(l) {
49
49
  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);
50
50
  if (!t)
51
51
  throw new Error("Line does not match VTT-like pattern: " + l);
52
- const r = t[1], a = t[2], i = t[3] || "", s = M(r), n = M(a);
53
- if (n < s)
52
+ const r = t[1], a = t[2], i = t[3] || "", n = M(r), s = M(a);
53
+ if (s < n)
54
54
  throw new Error("End time is before start time");
55
55
  return {
56
- startMs: s,
57
- endMs: n,
56
+ startMs: n,
57
+ endMs: s,
58
58
  start: r,
59
59
  end: a,
60
60
  text: i
@@ -77,25 +77,25 @@ class x {
77
77
  const r = R(e);
78
78
  let a = 0;
79
79
  for await (const i of r) {
80
- const s = [];
81
- let n = null, o = !1, h, d = 0;
80
+ const n = [];
81
+ let s = null, o = !1, h, d = 0;
82
82
  for (this.whisperService.transcribe(
83
83
  i,
84
84
  (c) => {
85
- d = c.timeEnd, c.timeStart += a, c.timeEnd += a, n ? (n(c), n = null) : s.push(c);
85
+ d = c.timeEnd, c.timeStart += a, c.timeEnd += a, s ? (s(c), s = null) : n.push(c);
86
86
  },
87
87
  t
88
88
  ).then(() => {
89
- o = !0, a += d, n == null || n(void 0);
89
+ o = !0, a += d, s == null || s(void 0);
90
90
  }).catch((c) => {
91
91
  h = c;
92
92
  }); ; ) {
93
93
  if (h) throw h;
94
94
  if (o) break;
95
- if (s.length)
96
- yield s.shift();
95
+ if (n.length)
96
+ yield n.shift();
97
97
  else {
98
- const c = await new Promise((f) => n = f);
98
+ const c = await new Promise((f) => s = f);
99
99
  c && (yield c);
100
100
  }
101
101
  }
@@ -153,11 +153,15 @@ class A {
153
153
  e.length > 16e3 * a && this.logger.warn(
154
154
  "It's not recommended to transcribe audio data that is longer than 120 seconds"
155
155
  ), this.isTranscribing = !0;
156
- const { language: i, threads: s, translate: n } = {
156
+ const {
157
+ language: i = "auto",
158
+ threads: n = 4,
159
+ translate: s = !1
160
+ } = {
157
161
  ...B,
158
162
  ...r
159
163
  }, o = [], h = Date.now();
160
- return this.wasmModule.full_default(this.instance, e, i, s, n), await new Promise((d, c) => {
164
+ return this.wasmModule.full_default(this.instance, e, i, n, s), await new Promise((d, c) => {
161
165
  const f = this.bus.on("transcribe", (g) => {
162
166
  const { startMs: w, endMs: y, text: z } = T(g.detail), v = {
163
167
  timeStart: w,
@@ -329,7 +333,7 @@ class F {
329
333
  const i = await fetch(a.url);
330
334
  if (!i.ok)
331
335
  throw new Error(`Failed to load model: ${i.statusText}`);
332
- const s = i.headers.get("content-length"), n = s ? parseInt(s, 10) : 0;
336
+ const n = i.headers.get("content-length"), s = n ? parseInt(n, 10) : 0;
333
337
  let o = 0;
334
338
  const h = (m = i.body) == null ? void 0 : m.getReader();
335
339
  if (!h)
@@ -339,8 +343,8 @@ class F {
339
343
  let g = !1;
340
344
  for (; !g; ) {
341
345
  const w = await h.read();
342
- if (g = w.done, !g && w.value && (d.push(w.value), o += w.value.length, r && n > 0)) {
343
- const y = Math.round(o / n * 100);
346
+ if (g = w.done, !g && w.value && (d.push(w.value), o += w.value.length, r && s > 0)) {
347
+ const y = Math.round(o / s * 100);
344
348
  r(y);
345
349
  }
346
350
  }
@@ -368,8 +372,8 @@ class F {
368
372
  const a = await fetch(e);
369
373
  if (!a.ok)
370
374
  throw new Error(`Failed to load WASM module: ${a.statusText}`);
371
- const i = a.headers.get("content-length"), s = i ? parseInt(i, 10) : 0;
372
- let n = 0;
375
+ const i = a.headers.get("content-length"), n = i ? parseInt(i, 10) : 0;
376
+ let s = 0;
373
377
  const o = (r = a.body) == null ? void 0 : r.getReader();
374
378
  if (!o)
375
379
  throw new Error("Response body is not readable");
@@ -378,8 +382,8 @@ class F {
378
382
  let u = !1;
379
383
  for (; !u; ) {
380
384
  const m = await o.read();
381
- if (u = m.done, !u && m.value && (h.push(m.value), n += m.value.length, t && s > 0)) {
382
- const g = Math.round(n / s * 100);
385
+ if (u = m.done, !u && m.value && (h.push(m.value), s += m.value.length, t && n > 0)) {
386
+ const g = Math.round(s / n * 100);
383
387
  t(g);
384
388
  }
385
389
  }
@@ -401,12 +405,12 @@ class F {
401
405
  async getCachedModelByUrl(e) {
402
406
  try {
403
407
  const a = (await this.openIndexedDB()).transaction(["modelsByUrl"], "readonly").objectStore("modelsByUrl");
404
- return new Promise((i, s) => {
405
- const n = a.get(e);
406
- n.onsuccess = () => {
407
- const o = n.result;
408
+ return new Promise((i, n) => {
409
+ const s = a.get(e);
410
+ s.onsuccess = () => {
411
+ const o = s.result;
408
412
  o && o.data ? i(o.data) : i(null);
409
- }, n.onerror = () => s(n.error);
413
+ }, s.onerror = () => n(s.error);
410
414
  });
411
415
  } catch (t) {
412
416
  return this.logger.error("Error reading model from cache by URL:", t), null;
@@ -418,14 +422,14 @@ class F {
418
422
  async saveModelToCacheByUrl(e, t) {
419
423
  try {
420
424
  const i = (await this.openIndexedDB()).transaction(["modelsByUrl"], "readwrite").objectStore("modelsByUrl");
421
- await new Promise((s, n) => {
425
+ await new Promise((n, s) => {
422
426
  const o = i.put({
423
427
  url: e,
424
428
  data: t,
425
429
  timestamp: Date.now(),
426
430
  size: t.length
427
431
  });
428
- o.onsuccess = () => s(), o.onerror = () => n(o.error);
432
+ o.onsuccess = () => n(), o.onerror = () => s(o.error);
429
433
  }), this.logger.info(`Model saved to cache by URL: ${e}`);
430
434
  } catch (r) {
431
435
  this.logger.error("Error saving model to cache by URL:", r);
@@ -466,14 +470,14 @@ class F {
466
470
  async saveModelToCache(e, t) {
467
471
  try {
468
472
  const i = (await this.openIndexedDB()).transaction(["models"], "readwrite").objectStore("models");
469
- await new Promise((s, n) => {
473
+ await new Promise((n, s) => {
470
474
  const o = i.put({
471
475
  name: e,
472
476
  data: t,
473
477
  timestamp: Date.now(),
474
478
  size: t.length
475
479
  });
476
- o.onsuccess = () => s(), o.onerror = () => n(o.error);
480
+ o.onsuccess = () => n(), o.onerror = () => s(o.error);
477
481
  }), this.logger.info(`Model ${e} saved to cache`);
478
482
  } catch (r) {
479
483
  this.logger.error("Error saving model to cache:", r);
@@ -485,12 +489,12 @@ class F {
485
489
  async getCachedModel(e) {
486
490
  try {
487
491
  const a = (await this.openIndexedDB()).transaction(["models"], "readonly").objectStore("models");
488
- return new Promise((i, s) => {
489
- const n = a.get(e);
490
- n.onsuccess = () => {
491
- const o = n.result;
492
+ return new Promise((i, n) => {
493
+ const s = a.get(e);
494
+ s.onsuccess = () => {
495
+ const o = s.result;
492
496
  o && o.data ? i(o.data) : i(null);
493
- }, n.onerror = () => s(n.error);
497
+ }, s.onerror = () => n(s.error);
494
498
  });
495
499
  } catch (t) {
496
500
  return this.logger.error("Error getting cached model:", t), null;
@@ -503,11 +507,11 @@ class F {
503
507
  try {
504
508
  const r = (await this.openIndexedDB()).transaction(["models"], "readonly").objectStore("models");
505
509
  return new Promise((a, i) => {
506
- const s = r.getAllKeys();
507
- s.onsuccess = () => {
508
- const n = s.result;
509
- a(n);
510
- }, s.onerror = () => i(s.error);
510
+ const n = r.getAllKeys();
511
+ n.onsuccess = () => {
512
+ const s = n.result;
513
+ a(s);
514
+ }, n.onerror = () => i(n.error);
511
515
  });
512
516
  } catch (e) {
513
517
  return this.logger.error("Error getting cached model names:", e), [];
@@ -522,12 +526,12 @@ class F {
522
526
  r.onerror = () => t(r.error), r.onsuccess = () => e(r.result), r.onupgradeneeded = (a) => {
523
527
  const i = a.target.result;
524
528
  if (!i.objectStoreNames.contains("models")) {
525
- const s = i.createObjectStore("models", { keyPath: "name" });
526
- s.createIndex("timestamp", "timestamp", { unique: !1 }), s.createIndex("size", "size", { unique: !1 });
529
+ const n = i.createObjectStore("models", { keyPath: "name" });
530
+ n.createIndex("timestamp", "timestamp", { unique: !1 }), n.createIndex("size", "size", { unique: !1 });
527
531
  }
528
532
  if (!i.objectStoreNames.contains("modelsByUrl")) {
529
- const s = i.createObjectStore("modelsByUrl", { keyPath: "url" });
530
- s.createIndex("timestamp", "timestamp", { unique: !1 }), s.createIndex("size", "size", { unique: !1 });
533
+ const n = i.createObjectStore("modelsByUrl", { keyPath: "url" });
534
+ n.createIndex("timestamp", "timestamp", { unique: !1 }), n.createIndex("size", "size", { unique: !1 });
531
535
  }
532
536
  };
533
537
  });
@@ -538,14 +542,14 @@ class F {
538
542
  async clearCache() {
539
543
  try {
540
544
  const t = (await this.openIndexedDB()).transaction(["models", "modelsByUrl"], "readwrite"), r = t.objectStore("models");
541
- await new Promise((i, s) => {
542
- const n = r.clear();
543
- n.onsuccess = () => i(), n.onerror = () => s(n.error);
545
+ await new Promise((i, n) => {
546
+ const s = r.clear();
547
+ s.onsuccess = () => i(), s.onerror = () => n(s.error);
544
548
  });
545
549
  const a = t.objectStore("modelsByUrl");
546
- await new Promise((i, s) => {
547
- const n = a.clear();
548
- n.onsuccess = () => i(), n.onerror = () => s(n.error);
550
+ await new Promise((i, n) => {
551
+ const s = a.clear();
552
+ s.onsuccess = () => i(), s.onerror = () => n(s.error);
549
553
  }), this.logger.info("Model cache cleared");
550
554
  } catch (e) {
551
555
  this.logger.error("Error clearing cache:", e);
@@ -558,11 +562,11 @@ class F {
558
562
  try {
559
563
  const r = (await this.openIndexedDB()).transaction(["models"], "readonly").objectStore("models");
560
564
  return new Promise((a, i) => {
561
- const s = r.getAll();
562
- s.onsuccess = () => {
563
- const n = s.result, o = n.reduce((h, d) => h + (d.size || 0), 0);
564
- a({ count: n.length, totalSize: o });
565
- }, s.onerror = () => i(s.error);
565
+ const n = r.getAll();
566
+ n.onsuccess = () => {
567
+ const s = n.result, o = s.reduce((h, d) => h + (d.size || 0), 0);
568
+ a({ count: s.length, totalSize: o });
569
+ }, n.onerror = () => i(n.error);
566
570
  });
567
571
  } catch (e) {
568
572
  return this.logger.error("Error getting cache info:", e), { count: 0, totalSize: 0 };