diff --git a/CHANGELOG.md b/CHANGELOG.md index d3026e7..089460a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## [2.0.5] - + +### Changed + +- `FileTranscriber.transcribe()` now only runs if wasm module _and_ model file are loaded successfully + +### Fixed + +- prevent multiple calls to `createModule` if model file was not found - fixes possible memory issue [#10](https://github.com/TranscribeJs/transcribe.js/issues/10) + ## [2.0.4] - 2024-11-27 ### Add diff --git a/examples/index.html b/examples/index.html index 5097e1c..957d9ac 100644 --- a/examples/index.html +++ b/examples/index.html @@ -90,6 +90,8 @@ segment.text + "\n"; }); } catch (error) { + printConsole(error.message); + document.querySelector(".result").innerHTML = "Error"; console.error("Error transcribing", error); } } diff --git a/src/FileTranscriber.js b/src/FileTranscriber.js index 1d33cd4..a1981e1 100644 --- a/src/FileTranscriber.js +++ b/src/FileTranscriber.js @@ -157,8 +157,8 @@ export class FileTranscriber extends Transcriber { token_timestamps = true, } = {} ) { - if (!this.isRuntimeInitialized) { - throw new Error("transcriber not initialized."); + if (!this.isReady) { + throw new Error("FileTranscriber not initialized."); } if (threads > this.maxThreads) { diff --git a/src/Transcriber.js b/src/Transcriber.js index daca65b..a7caa48 100644 --- a/src/Transcriber.js +++ b/src/Transcriber.js @@ -33,6 +33,14 @@ export class Transcriber { */ _isRuntimeInitialized = false; + /** + * Is model file loaded. + * + * @protected + * @type {boolean} + */ + _isModelFileLoaded = false; + /** * Is everything initialized and ready to transcribe. * @@ -123,6 +131,15 @@ export class Transcriber { return this._isRuntimeInitialized; } + /** + * Is model file loaded. + * + * @type {boolean} + */ + get isModelFileLoaded() { + return this._isModelFileLoaded; + } + /** * True when ready to transcribe. * @@ -136,13 +153,18 @@ export class Transcriber { * Load model and create a new shout instance. */ async init() { - if (this.isRuntimeInitialized) { - console.log("shout already initialized."); + if (this.isRuntimeInitialized && this.isModelFileLoaded) { + console.log("Shout already initialized."); return; } - this.Module = await this._createModule(this.Module); - await this._loadModel(); + if (!this.isRuntimeInitialized) { + this.Module = await this._createModule(this.Module); + } + + if (!this.isModelFileLoaded) { + await this._loadModel(); + } } /** @@ -247,5 +269,7 @@ export class Transcriber { true, true ); + + this._isModelFileLoaded = true; } } diff --git a/tests/FileTranscriber.spec.js b/tests/FileTranscriber.spec.js index 6cbdf2c..f9a0cc8 100644 --- a/tests/FileTranscriber.spec.js +++ b/tests/FileTranscriber.spec.js @@ -99,7 +99,7 @@ describe("FileTranscriber", () => { const transcriber = new FileTranscriber({ createModule, model }); // Act & Assert await expect(transcriber.transcribe(new Float32Array())).rejects.toThrow( - "transcriber not initialized." + "FileTranscriber not initialized." ); }); diff --git a/tests/Transcriber.spec.js b/tests/Transcriber.spec.js index 443383d..e245c84 100644 --- a/tests/Transcriber.spec.js +++ b/tests/Transcriber.spec.js @@ -32,6 +32,7 @@ describe("Transcriber", () => { }); afterEach(() => { + createModule.mockClear(); transcriber.destroy(); vi.unstubAllGlobals(); }); @@ -133,6 +134,23 @@ describe("Transcriber", () => { }); }); + it("should call createModule (create wasm runtime) only once", async () => { + await transcriber.init(); + transcriber.Module.onRuntimeInitialized(); // fake call + transcriber._isModelFileLoaded = false; // fake + + await transcriber.init(); + expect(createModule).toHaveBeenCalledOnce(); + }); + + it("should load model file only once", async () => { + await transcriber.init(); + transcriber._isModelFileLoaded = true; // fake + await transcriber.init(); + + expect(window.fetch).toHaveBeenCalledOnce(); + }); + it("should fetch model if model is a string", async () => { await transcriber.init(); expect(window.fetch).toHaveBeenCalledWith("path/to/my-model.bin"); @@ -170,6 +188,18 @@ describe("Transcriber", () => { true ); }); + + it("should set isModelFileLoaded to true", async () => { + expect(transcriber.isModelFileLoaded).toBe(false); + await transcriber.init(); + expect(transcriber.isModelFileLoaded).toBe(true); + }); + + it("should set isModelFileLoaded to false if model file is not loaded", async () => { + window.fetch = vi.fn(() => Promise.reject()); + await expect(transcriber.init()).rejects.toThrowError(); + expect(transcriber.isModelFileLoaded).toBe(false); + }); }); describe("destroy", () => {