assemblyai 4.0.0-beta.0 → 4.0.0-beta.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/CHANGELOG.md +92 -0
- package/dist/assemblyai.umd.js +20 -26
- package/dist/assemblyai.umd.min.js +1 -1
- package/dist/index.cjs +11 -3
- package/dist/index.mjs +11 -3
- package/dist/index.node.cjs +618 -0
- package/dist/{index.browser.js → index.node.mjs} +10 -75
- package/docs/compat.md +59 -0
- package/package.json +30 -10
- package/src/{browser/fs.ts → polyfills/no-fs.ts} +1 -1
- package/src/services/realtime/service.ts +2 -2
- /package/dist/{browser/fs.d.ts → polyfills/no-fs.d.ts} +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [4.0.0]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add `browser` and `workerd` (Cloudflare Workers) exports to package.json. These exports are compatible versions of the SDK, with a few limitations. You can't use the file system and you have to use a temporary auth token with the real-time transcriber.
|
|
8
|
+
- Add `dist/assemblyai.umd.js` and `dist/assemblyai.umd.min.js`. You can reference these script files directly in the browser and the SDK will be available at the global `assemblyai` variable.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- `RealtimeService.sendAudio` accepts audio via type `ArrayBufferLike`.
|
|
13
|
+
- **Breaking**: `RealtimeService.stream` returns a [WHATWG Streams Standard stream](https://nodejs.org/api/webstreams.html), instead of a Node stream. In the browser, the native web standard stream will be used.
|
|
14
|
+
- `ws` is used as the WebSocket client as before, but in the browser, the native WebSocket client is used.
|
|
15
|
+
- Rename Node SDK to JavaScript SDK as the SDK is compatible with more runtimes now.
|
|
16
|
+
|
|
17
|
+
## [3.1.1] - 2023-11-21
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- Add `client.transcripts.transcribe` function to transcribe an audio file with polling until transcript status is `completed` or `error`. This function takes an `audio` option which can be an audio file URL, path, stream, or buffer.
|
|
22
|
+
- Add `client.transcripts.submit` function to queue a transcript. You can use `client.transcripts.waitUntilReady` to poll the transcript returned by `submit`. This function also takes an `audio` option which can be an audio file URL, path, stream, or buffer.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Deprecated `client.transcripts.create` in favor of `transcribe` and `submit`, to be more consistent with other AssemblyAI SDKs.
|
|
27
|
+
- Renamed types
|
|
28
|
+
- Renamed `Parameters` type suffix with `Params` type suffix
|
|
29
|
+
- Renamed `CreateTranscriptParameters` to `TranscriptParams`
|
|
30
|
+
- Renamed `CreateTranscriptOptionalParameters` to `TranscriptOptionalParams`.
|
|
31
|
+
- Added deprecated aliases for the forementioned types
|
|
32
|
+
- Improved type docs
|
|
33
|
+
|
|
34
|
+
## [3.1.0] - 2023-11-16
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Add `AssemblyAI.transcripts.waitUntilReady` function to wait until a transcript is ready, meaning `status` is `completed` or `error`.
|
|
39
|
+
- Add `chars_per_caption` parameter to `AssemblyAI.transcripts.subtitles` function.
|
|
40
|
+
- Add `input_text` property to LeMUR functions. Instead of using `transcript_ids`, you can use `input_text` to provide custom formatted transcripts as input to LeMUR.
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Change default timeout from 3 minutes to infinite (-1). Fixes [#17](https://github.com/AssemblyAI/assemblyai-node-sdk/issues/17)
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- Correctly serialize the keywords for `client.transcripts.wordSearch`.
|
|
49
|
+
- Use more widely compatible syntax for wildcard exporting types. Fixes [#18](https://github.com/AssemblyAI/assemblyai-node-sdk/issues/18).
|
|
50
|
+
|
|
51
|
+
## [3.0.1] - 2023-10-30
|
|
52
|
+
|
|
53
|
+
### Changed
|
|
54
|
+
|
|
55
|
+
- The SDK uses `fetch` instead of Axios. This removes the Axios dependency. Axios relies on XMLHttpRequest which isn't supported in Cloudflare Workers, Deno, Bun, etc. By using `fetch`, the SDK is now more compatible on the forementioned runtimes.
|
|
56
|
+
|
|
57
|
+
### Fixed
|
|
58
|
+
|
|
59
|
+
- The SDK uses relative imports instead of using path aliases, to make the library transpilable with tsc for consumers. Fixes [#14](https://github.com/AssemblyAI/assemblyai-node-sdk/issues/14).
|
|
60
|
+
- Added `speaker` property to the `TranscriptUtterance` type, and removed `channel` property.
|
|
61
|
+
|
|
62
|
+
## [3.0.0] - 2023-10-24
|
|
63
|
+
|
|
64
|
+
### Changed
|
|
65
|
+
|
|
66
|
+
- `AssemblyAI.files.upload` accepts streams and buffers, in addition to a string (path to file).
|
|
67
|
+
|
|
68
|
+
### Removed
|
|
69
|
+
|
|
70
|
+
- **Breaking**: The module does not have a default export anymore, because of inconsistent functionality across module systems. Instead, use `AssemblyAI` as a named import like this: `import { AssemblyAI } from 'assemblyai'`.
|
|
71
|
+
|
|
72
|
+
## [2.0.2] - 2023-10-13
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- `AssemblyAI.transcripts.wordSearch` searches for keywords in the transcript.
|
|
77
|
+
- `AssemblyAI.lemur.purgeRequestData` deletes data related to your LeMUR request.
|
|
78
|
+
- `RealtimeService.stream` creates a writable stream that you can write audio data to instead of using `RealtimeService.sendAudio``.
|
|
79
|
+
|
|
80
|
+
### Fixed
|
|
81
|
+
|
|
82
|
+
- The AssemblyAI class would be exported as default named export instead in certain module systems.
|
|
83
|
+
|
|
84
|
+
## [2.0.1] - 2023-10-10
|
|
85
|
+
|
|
86
|
+
Re-implement the Node SDK in TypeScript and add all AssemblyAI APIs.
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- Transcript API client
|
|
91
|
+
- LeMUR API client
|
|
92
|
+
- Real-time transcript client
|
package/dist/assemblyai.umd.js
CHANGED
|
@@ -146,32 +146,26 @@
|
|
|
146
146
|
ByteLengthQueuingStrategy,
|
|
147
147
|
CountQueuingStrategy,
|
|
148
148
|
TextEncoderStream,
|
|
149
|
-
TextDecoderStream
|
|
150
|
-
} = window;
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
if (done) return;
|
|
165
|
-
// Else yield the chunk
|
|
166
|
-
yield value;
|
|
149
|
+
TextDecoderStream
|
|
150
|
+
} = typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : globalThis;
|
|
151
|
+
(function(ReadableStream2) {
|
|
152
|
+
if (!ReadableStream2.prototype[Symbol.asyncIterator]) {
|
|
153
|
+
async function* streamAsyncIterator() {
|
|
154
|
+
const reader = this.getReader();
|
|
155
|
+
try {
|
|
156
|
+
while (true) {
|
|
157
|
+
const { done, value } = await reader.read();
|
|
158
|
+
if (done)
|
|
159
|
+
return;
|
|
160
|
+
yield value;
|
|
161
|
+
}
|
|
162
|
+
} finally {
|
|
163
|
+
reader.releaseLock();
|
|
167
164
|
}
|
|
168
|
-
} finally {
|
|
169
|
-
reader.releaseLock();
|
|
170
165
|
}
|
|
166
|
+
ReadableStream2.prototype[Symbol.asyncIterator] = streamAsyncIterator;
|
|
171
167
|
}
|
|
172
|
-
|
|
173
|
-
ReadableStream.prototype[Symbol.asyncIterator] = streamAsyncIterator;
|
|
174
|
-
}
|
|
168
|
+
})(ReadableStream);
|
|
175
169
|
|
|
176
170
|
// https://github.com/maxogden/websocket-stream/blob/48dc3ddf943e5ada668c31ccd94e9186f02fafbd/ws-fallback.js
|
|
177
171
|
|
|
@@ -240,9 +234,9 @@
|
|
|
240
234
|
this.realtimeUrl = (_a = params.realtimeUrl) !== null && _a !== void 0 ? _a : defaultRealtimeUrl;
|
|
241
235
|
this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
|
|
242
236
|
this.wordBoost = params.wordBoost;
|
|
243
|
-
if ("apiKey" in params)
|
|
237
|
+
if ("apiKey" in params && params.apiKey)
|
|
244
238
|
this.apiKey = params.apiKey;
|
|
245
|
-
if ("token" in params)
|
|
239
|
+
if ("token" in params && params.token)
|
|
246
240
|
this.token = params.token;
|
|
247
241
|
if (!(this.apiKey || this.token)) {
|
|
248
242
|
throw new Error("API key or temporary token is required.");
|
|
@@ -628,7 +622,7 @@
|
|
|
628
622
|
}
|
|
629
623
|
|
|
630
624
|
function throwError() {
|
|
631
|
-
throw new Error("
|
|
625
|
+
throw new Error("'fs' is not supported in this environment.");
|
|
632
626
|
}
|
|
633
627
|
|
|
634
628
|
const createReadStream = throwError;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).assemblyai={})}(this,(function(e){"use strict";function t(e,t,o,s){return new(o||(o=Promise))((function(r,i){function n(e){try{l(s.next(e))}catch(e){i(e)}}function a(e){try{l(s.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof o?t:new o((function(e){e(t)}))).then(n,a)}l((s=s.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class o{constructor(e){this.params=e}fetch(e,o){var s;return t(this,void 0,void 0,(function*(){(o=null!=o?o:{}).headers=null!==(s=o.headers)&&void 0!==s?s:{},o.headers=Object.assign({Authorization:this.params.apiKey,"Content-Type":"application/json"},o.headers),e.startsWith("http")||(e=this.params.baseUrl+e);const t=yield fetch(e,o);if(t.status>=400){let e;const o=yield t.text();if(o){try{e=JSON.parse(o)}catch(e){}if(null==e?void 0:e.error)throw new Error(e.error);throw new Error(o)}throw new Error(`HTTP Error: ${t.status} ${t.statusText}`)}return t}))}fetchJson(e,o){return t(this,void 0,void 0,(function*(){return(yield this.fetch(e,o)).json()}))}}class s extends o{summary(e){return this.fetchJson("/lemur/v3/generate/summary",{method:"POST",body:JSON.stringify(e)})}questionAnswer(e){return this.fetchJson("/lemur/v3/generate/question-answer",{method:"POST",body:JSON.stringify(e)})}actionItems(e){return this.fetchJson("/lemur/v3/generate/action-items",{method:"POST",body:JSON.stringify(e)})}task(e){return this.fetchJson("/lemur/v3/generate/task",{method:"POST",body:JSON.stringify(e)})}purgeRequestData(e){return this.fetchJson(`/lemur/v3/${e}`,{method:"DELETE"})}}const{ReadableStream:r,ReadableStreamDefaultReader:i,ReadableStreamBYOBReader:n,ReadableStreamBYOBRequest:a,ReadableByteStreamController:l,ReadableStreamDefaultController:c,TransformStream:d,TransformStreamDefaultController:u,WritableStream:h,WritableStreamDefaultWriter:f,WritableStreamDefaultController:p,ByteLengthQueuingStrategy:m,CountQueuingStrategy:y,TextEncoderStream:S,TextDecoderStream:v}=window;if(!r.prototype[Symbol.asyncIterator]){async function*P(){const e=this.getReader();try{for(;;){const{done:t,value:o}=await e.read();if(t)return;yield o}}finally{e.releaseLock()}}r.prototype[Symbol.asyncIterator]=P}var w=null;"undefined"!=typeof WebSocket?w=WebSocket:"undefined"!=typeof MozWebSocket?w=MozWebSocket:"undefined"!=typeof global?w=global.WebSocket||global.MozWebSocket:"undefined"!=typeof window?w=window.WebSocket||window.MozWebSocket:"undefined"!=typeof self&&(w=self.WebSocket||self.MozWebSocket);var b,g=w;!function(e){e[e.BadSampleRate=4e3]="BadSampleRate",e[e.AuthFailed=4001]="AuthFailed",e[e.InsufficientFundsOrFreeAccount=4002]="InsufficientFundsOrFreeAccount",e[e.NonexistentSessionId=4004]="NonexistentSessionId",e[e.SessionExpired=4008]="SessionExpired",e[e.ClosedSession=4010]="ClosedSession",e[e.RateLimited=4029]="RateLimited",e[e.UniqueSessionViolation=4030]="UniqueSessionViolation",e[e.SessionTimeout=4031]="SessionTimeout",e[e.AudioTooShort=4032]="AudioTooShort",e[e.AudioTooLong=4033]="AudioTooLong",e[e.BadJson=4100]="BadJson",e[e.BadSchema=4101]="BadSchema",e[e.TooManyStreams=4102]="TooManyStreams",e[e.Reconnected=4103]="Reconnected",e[e.ReconnectAttemptsExhausted=1013]="ReconnectAttemptsExhausted"}(b||(b={}));const k={[b.BadSampleRate]:"Sample rate must be a positive integer",[b.AuthFailed]:"Not Authorized",[b.InsufficientFundsOrFreeAccount]:"Insufficient funds or you are using a free account. This feature is paid-only and requires you to add a credit card. Please visit https://assemblyai.com/dashboard/ to add a credit card to your account.",[b.NonexistentSessionId]:"Session ID does not exist",[b.SessionExpired]:"Session has expired",[b.ClosedSession]:"Session is closed",[b.RateLimited]:"Rate limited",[b.UniqueSessionViolation]:"Unique session violation",[b.SessionTimeout]:"Session Timeout",[b.AudioTooShort]:"Audio too short",[b.AudioTooLong]:"Audio too long",[b.BadJson]:"Bad JSON",[b.BadSchema]:"Bad schema",[b.TooManyStreams]:"Too many streams",[b.Reconnected]:"Reconnected",[b.ReconnectAttemptsExhausted]:"Reconnect attempts exhausted"};class T extends Error{}class R{constructor(e){var t,o;if(this.listeners={},this.realtimeUrl=null!==(t=e.realtimeUrl)&&void 0!==t?t:"wss://api.assemblyai.com/v2/realtime/ws",this.sampleRate=null!==(o=e.sampleRate)&&void 0!==o?o:16e3,this.wordBoost=e.wordBoost,"apiKey"in e&&(this.apiKey=e.apiKey),"token"in e&&(this.token=e.token),!this.apiKey&&!this.token)throw new Error("API key or temporary token is required.")}connectionUrl(){const e=new URL(this.realtimeUrl);if("wss:"!==e.protocol)throw new Error("Invalid protocol, must be wss");const t=new URLSearchParams;return this.token&&t.set("token",this.token),t.set("sample_rate",this.sampleRate.toString()),this.wordBoost&&this.wordBoost.length>0&&t.set("word_boost",JSON.stringify(this.wordBoost)),e.search=t.toString(),e}on(e,t){this.listeners[e]=t}connect(){return new Promise((e=>{if(this.socket)throw new Error("Already connected");const t=this.connectionUrl();this.token?this.socket=new g(t.toString()):this.socket=new g(t.toString(),{headers:{Authorization:this.apiKey}}),this.socket.onclose=({code:e,reason:t})=>{var o,s;t||e in b&&(t=k[e]),null===(s=(o=this.listeners).close)||void 0===s||s.call(o,e,t)},this.socket.onerror=e=>{var t,o,s,r;e.error?null===(o=(t=this.listeners).error)||void 0===o||o.call(t,e.error):null===(r=(s=this.listeners).error)||void 0===r||r.call(s,new Error(e.message))},this.socket.onmessage=({data:t})=>{var o,s,r,i,n,a,l,c,d,u,h,f,p;const m=JSON.parse(t.toString());if("error"in m)null===(s=(o=this.listeners).error)||void 0===s||s.call(o,new T(m.error));else switch(m.message_type){case"SessionBegins":{const t={sessionId:m.session_id,expiresAt:new Date(m.expires_at)};e(t),null===(i=(r=this.listeners).open)||void 0===i||i.call(r,t);break}case"PartialTranscript":m.created=new Date(m.created),null===(a=(n=this.listeners).transcript)||void 0===a||a.call(n,m),null===(c=(l=this.listeners)["transcript.partial"])||void 0===c||c.call(l,m);break;case"FinalTranscript":m.created=new Date(m.created),null===(u=(d=this.listeners).transcript)||void 0===u||u.call(d,m),null===(f=(h=this.listeners)["transcript.final"])||void 0===f||f.call(h,m);break;case"SessionTerminated":null===(p=this.sessionTerminatedResolve)||void 0===p||p.call(this)}}}))}sendAudio(e){if(!this.socket||this.socket.readyState!==g.OPEN)throw new Error("Socket is not open for communication");let t;t="undefined"!=typeof Buffer?Buffer.from(e).toString("base64"):btoa(new Uint8Array(e).reduce(((e,t)=>e+String.fromCharCode(t)),""));const o={audio_data:t};this.socket.send(JSON.stringify(o))}stream(){return new h({write:e=>{this.sendAudio(e)}})}close(e=!0){return t(this,void 0,void 0,(function*(){if(this.socket){if(this.socket.readyState===g.OPEN){const t='{"terminate_session": true}';if(e){const e=new Promise((e=>{this.sessionTerminatedResolve=e}));this.socket.send(t),yield e}else this.socket.send(t)}"removeAllListeners"in this.socket&&this.socket.removeAllListeners(),this.socket.close()}this.listeners={},this.socket=void 0}))}}class O extends o{constructor(e){super(e),this.rtFactoryParams=e}createService(e){return e?"token"in e||e.apiKey||(e.apiKey=this.rtFactoryParams.apiKey):e={apiKey:this.rtFactoryParams.apiKey},new R(e)}createTemporaryToken(e){return t(this,void 0,void 0,(function*(){return(yield this.fetchJson("/v2/realtime/token",{method:"POST",body:JSON.stringify(e)})).token}))}}class x extends o{constructor(e,t){super(e),this.files=t}transcribe(e,o){return t(this,void 0,void 0,(function*(){const t=yield this.submit(e);return yield this.waitUntilReady(t.id,o)}))}submit(e){return t(this,void 0,void 0,(function*(){const{audio:t}=e,o=function(e,t){var o={};for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.indexOf(s)<0&&(o[s]=e[s]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var r=0;for(s=Object.getOwnPropertySymbols(e);r<s.length;r++)t.indexOf(s[r])<0&&Object.prototype.propertyIsEnumerable.call(e,s[r])&&(o[s[r]]=e[s[r]])}return o}(e,["audio"]);let s;if("string"==typeof t){const e=J(t);s=null!==e?yield this.files.upload(e):t}else s=yield this.files.upload(t);return yield this.fetchJson("/v2/transcript",{method:"POST",body:JSON.stringify(Object.assign(Object.assign({},o),{audio_url:s}))})}))}create(e,o){var s;return t(this,void 0,void 0,(function*(){const t=J(e.audio_url);if(null!==t){const o=yield this.files.upload(t);e.audio_url=o}const r=yield this.fetchJson("/v2/transcript",{method:"POST",body:JSON.stringify(e)});return null===(s=null==o?void 0:o.poll)||void 0===s||s?yield this.waitUntilReady(r.id,o):r}))}waitUntilReady(e,o){var s,r;return t(this,void 0,void 0,(function*(){const t=null!==(s=null==o?void 0:o.pollingInterval)&&void 0!==s?s:3e3,i=null!==(r=null==o?void 0:o.pollingTimeout)&&void 0!==r?r:-1,n=Date.now();for(;;){const o=yield this.get(e);if("completed"===o.status||"error"===o.status)return o;if(i>0&&Date.now()-n>i)throw new Error("Polling timeout");yield new Promise((e=>setTimeout(e,t)))}}))}get(e){return this.fetchJson(`/v2/transcript/${e}`)}list(e){return t(this,void 0,void 0,(function*(){let t="/v2/transcript";"string"==typeof e?t=e:e&&(t=`${t}?${new URLSearchParams(Object.keys(e).map((t=>{var o;return[t,(null===(o=e[t])||void 0===o?void 0:o.toString())||""]})))}`);const o=yield this.fetchJson(t);for(const e of o.transcripts)e.created=new Date(e.created),e.completed&&(e.completed=new Date(e.completed));return o}))}delete(e){return this.fetchJson(`/v2/transcript/${e}`,{method:"DELETE"})}wordSearch(e,t){const o=new URLSearchParams({words:t.join(",")});return this.fetchJson(`/v2/transcript/${e}/word-search?${o.toString()}`)}sentences(e){return this.fetchJson(`/v2/transcript/${e}/sentences`)}paragraphs(e){return this.fetchJson(`/v2/transcript/${e}/paragraphs`)}subtitles(e,o="srt",s){return t(this,void 0,void 0,(function*(){let t=`/v2/transcript/${e}/${o}`;if(s){const e=new URLSearchParams;e.set("chars_per_caption",s.toString()),t+=`?${e.toString()}`}const r=yield this.fetch(t);return yield r.text()}))}redactions(e){return this.fetchJson(`/v2/transcript/${e}/redacted-audio`)}}function J(e){let t;try{return t=new URL(e),"file:"===t.protocol?t.pathname:null}catch(t){return e}}var A={createReadStream:function(){throw new Error("Function is not supported in this environment.")}};class E extends o{upload(e){return t(this,void 0,void 0,(function*(){let t;t="string"==typeof e?A.createReadStream(e):e;return(yield this.fetchJson("/v2/upload",{method:"POST",body:t,headers:{"Content-Type":"application/octet-stream"},duplex:"half"})).upload_url}))}}e.AssemblyAI=class{constructor(e){e.baseUrl=e.baseUrl||"https://api.assemblyai.com",e.baseUrl&&e.baseUrl.endsWith("/")&&(e.baseUrl=e.baseUrl.slice(0,-1)),this.files=new E(e),this.transcripts=new x(e,this.files),this.lemur=new s(e),this.realtime=new O(e)}},e.FileService=E,e.LemurService=s,e.RealtimeService=R,e.RealtimeServiceFactory=O,e.TranscriptService=x}));
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).assemblyai={})}(this,(function(e){"use strict";function t(e,t,o,s){return new(o||(o=Promise))((function(r,i){function n(e){try{l(s.next(e))}catch(e){i(e)}}function a(e){try{l(s.throw(e))}catch(e){i(e)}}function l(e){var t;e.done?r(e.value):(t=e.value,t instanceof o?t:new o((function(e){e(t)}))).then(n,a)}l((s=s.apply(e,t||[])).next())}))}"function"==typeof SuppressedError&&SuppressedError;class o{constructor(e){this.params=e}fetch(e,o){var s;return t(this,void 0,void 0,(function*(){(o=null!=o?o:{}).headers=null!==(s=o.headers)&&void 0!==s?s:{},o.headers=Object.assign({Authorization:this.params.apiKey,"Content-Type":"application/json"},o.headers),e.startsWith("http")||(e=this.params.baseUrl+e);const t=yield fetch(e,o);if(t.status>=400){let e;const o=yield t.text();if(o){try{e=JSON.parse(o)}catch(e){}if(null==e?void 0:e.error)throw new Error(e.error);throw new Error(o)}throw new Error(`HTTP Error: ${t.status} ${t.statusText}`)}return t}))}fetchJson(e,o){return t(this,void 0,void 0,(function*(){return(yield this.fetch(e,o)).json()}))}}class s extends o{summary(e){return this.fetchJson("/lemur/v3/generate/summary",{method:"POST",body:JSON.stringify(e)})}questionAnswer(e){return this.fetchJson("/lemur/v3/generate/question-answer",{method:"POST",body:JSON.stringify(e)})}actionItems(e){return this.fetchJson("/lemur/v3/generate/action-items",{method:"POST",body:JSON.stringify(e)})}task(e){return this.fetchJson("/lemur/v3/generate/task",{method:"POST",body:JSON.stringify(e)})}purgeRequestData(e){return this.fetchJson(`/lemur/v3/${e}`,{method:"DELETE"})}}const{ReadableStream:r,ReadableStreamDefaultReader:i,ReadableStreamBYOBReader:n,ReadableStreamBYOBRequest:a,ReadableByteStreamController:l,ReadableStreamDefaultController:c,TransformStream:d,TransformStreamDefaultController:u,WritableStream:h,WritableStreamDefaultWriter:f,WritableStreamDefaultController:p,ByteLengthQueuingStrategy:m,CountQueuingStrategy:y,TextEncoderStream:S,TextDecoderStream:v}="undefined"!=typeof window?window:"undefined"!=typeof global?global:globalThis;!function(e){if(!e.prototype[Symbol.asyncIterator]){async function*t(){const e=this.getReader();try{for(;;){const{done:t,value:o}=await e.read();if(t)return;yield o}}finally{e.releaseLock()}}e.prototype[Symbol.asyncIterator]=t}}(r);var w=null;"undefined"!=typeof WebSocket?w=WebSocket:"undefined"!=typeof MozWebSocket?w=MozWebSocket:"undefined"!=typeof global?w=global.WebSocket||global.MozWebSocket:"undefined"!=typeof window?w=window.WebSocket||window.MozWebSocket:"undefined"!=typeof self&&(w=self.WebSocket||self.MozWebSocket);var b,g=w;!function(e){e[e.BadSampleRate=4e3]="BadSampleRate",e[e.AuthFailed=4001]="AuthFailed",e[e.InsufficientFundsOrFreeAccount=4002]="InsufficientFundsOrFreeAccount",e[e.NonexistentSessionId=4004]="NonexistentSessionId",e[e.SessionExpired=4008]="SessionExpired",e[e.ClosedSession=4010]="ClosedSession",e[e.RateLimited=4029]="RateLimited",e[e.UniqueSessionViolation=4030]="UniqueSessionViolation",e[e.SessionTimeout=4031]="SessionTimeout",e[e.AudioTooShort=4032]="AudioTooShort",e[e.AudioTooLong=4033]="AudioTooLong",e[e.BadJson=4100]="BadJson",e[e.BadSchema=4101]="BadSchema",e[e.TooManyStreams=4102]="TooManyStreams",e[e.Reconnected=4103]="Reconnected",e[e.ReconnectAttemptsExhausted=1013]="ReconnectAttemptsExhausted"}(b||(b={}));const k={[b.BadSampleRate]:"Sample rate must be a positive integer",[b.AuthFailed]:"Not Authorized",[b.InsufficientFundsOrFreeAccount]:"Insufficient funds or you are using a free account. This feature is paid-only and requires you to add a credit card. Please visit https://assemblyai.com/dashboard/ to add a credit card to your account.",[b.NonexistentSessionId]:"Session ID does not exist",[b.SessionExpired]:"Session has expired",[b.ClosedSession]:"Session is closed",[b.RateLimited]:"Rate limited",[b.UniqueSessionViolation]:"Unique session violation",[b.SessionTimeout]:"Session Timeout",[b.AudioTooShort]:"Audio too short",[b.AudioTooLong]:"Audio too long",[b.BadJson]:"Bad JSON",[b.BadSchema]:"Bad schema",[b.TooManyStreams]:"Too many streams",[b.Reconnected]:"Reconnected",[b.ReconnectAttemptsExhausted]:"Reconnect attempts exhausted"};class T extends Error{}class R{constructor(e){var t,o;if(this.listeners={},this.realtimeUrl=null!==(t=e.realtimeUrl)&&void 0!==t?t:"wss://api.assemblyai.com/v2/realtime/ws",this.sampleRate=null!==(o=e.sampleRate)&&void 0!==o?o:16e3,this.wordBoost=e.wordBoost,"apiKey"in e&&e.apiKey&&(this.apiKey=e.apiKey),"token"in e&&e.token&&(this.token=e.token),!this.apiKey&&!this.token)throw new Error("API key or temporary token is required.")}connectionUrl(){const e=new URL(this.realtimeUrl);if("wss:"!==e.protocol)throw new Error("Invalid protocol, must be wss");const t=new URLSearchParams;return this.token&&t.set("token",this.token),t.set("sample_rate",this.sampleRate.toString()),this.wordBoost&&this.wordBoost.length>0&&t.set("word_boost",JSON.stringify(this.wordBoost)),e.search=t.toString(),e}on(e,t){this.listeners[e]=t}connect(){return new Promise((e=>{if(this.socket)throw new Error("Already connected");const t=this.connectionUrl();this.token?this.socket=new g(t.toString()):this.socket=new g(t.toString(),{headers:{Authorization:this.apiKey}}),this.socket.onclose=({code:e,reason:t})=>{var o,s;t||e in b&&(t=k[e]),null===(s=(o=this.listeners).close)||void 0===s||s.call(o,e,t)},this.socket.onerror=e=>{var t,o,s,r;e.error?null===(o=(t=this.listeners).error)||void 0===o||o.call(t,e.error):null===(r=(s=this.listeners).error)||void 0===r||r.call(s,new Error(e.message))},this.socket.onmessage=({data:t})=>{var o,s,r,i,n,a,l,c,d,u,h,f,p;const m=JSON.parse(t.toString());if("error"in m)null===(s=(o=this.listeners).error)||void 0===s||s.call(o,new T(m.error));else switch(m.message_type){case"SessionBegins":{const t={sessionId:m.session_id,expiresAt:new Date(m.expires_at)};e(t),null===(i=(r=this.listeners).open)||void 0===i||i.call(r,t);break}case"PartialTranscript":m.created=new Date(m.created),null===(a=(n=this.listeners).transcript)||void 0===a||a.call(n,m),null===(c=(l=this.listeners)["transcript.partial"])||void 0===c||c.call(l,m);break;case"FinalTranscript":m.created=new Date(m.created),null===(u=(d=this.listeners).transcript)||void 0===u||u.call(d,m),null===(f=(h=this.listeners)["transcript.final"])||void 0===f||f.call(h,m);break;case"SessionTerminated":null===(p=this.sessionTerminatedResolve)||void 0===p||p.call(this)}}}))}sendAudio(e){if(!this.socket||this.socket.readyState!==g.OPEN)throw new Error("Socket is not open for communication");let t;t="undefined"!=typeof Buffer?Buffer.from(e).toString("base64"):btoa(new Uint8Array(e).reduce(((e,t)=>e+String.fromCharCode(t)),""));const o={audio_data:t};this.socket.send(JSON.stringify(o))}stream(){return new h({write:e=>{this.sendAudio(e)}})}close(e=!0){return t(this,void 0,void 0,(function*(){if(this.socket){if(this.socket.readyState===g.OPEN){const t='{"terminate_session": true}';if(e){const e=new Promise((e=>{this.sessionTerminatedResolve=e}));this.socket.send(t),yield e}else this.socket.send(t)}"removeAllListeners"in this.socket&&this.socket.removeAllListeners(),this.socket.close()}this.listeners={},this.socket=void 0}))}}class O extends o{constructor(e){super(e),this.rtFactoryParams=e}createService(e){return e?"token"in e||e.apiKey||(e.apiKey=this.rtFactoryParams.apiKey):e={apiKey:this.rtFactoryParams.apiKey},new R(e)}createTemporaryToken(e){return t(this,void 0,void 0,(function*(){return(yield this.fetchJson("/v2/realtime/token",{method:"POST",body:JSON.stringify(e)})).token}))}}class x extends o{constructor(e,t){super(e),this.files=t}transcribe(e,o){return t(this,void 0,void 0,(function*(){const t=yield this.submit(e);return yield this.waitUntilReady(t.id,o)}))}submit(e){return t(this,void 0,void 0,(function*(){const{audio:t}=e,o=function(e,t){var o={};for(var s in e)Object.prototype.hasOwnProperty.call(e,s)&&t.indexOf(s)<0&&(o[s]=e[s]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var r=0;for(s=Object.getOwnPropertySymbols(e);r<s.length;r++)t.indexOf(s[r])<0&&Object.prototype.propertyIsEnumerable.call(e,s[r])&&(o[s[r]]=e[s[r]])}return o}(e,["audio"]);let s;if("string"==typeof t){const e=J(t);s=null!==e?yield this.files.upload(e):t}else s=yield this.files.upload(t);return yield this.fetchJson("/v2/transcript",{method:"POST",body:JSON.stringify(Object.assign(Object.assign({},o),{audio_url:s}))})}))}create(e,o){var s;return t(this,void 0,void 0,(function*(){const t=J(e.audio_url);if(null!==t){const o=yield this.files.upload(t);e.audio_url=o}const r=yield this.fetchJson("/v2/transcript",{method:"POST",body:JSON.stringify(e)});return null===(s=null==o?void 0:o.poll)||void 0===s||s?yield this.waitUntilReady(r.id,o):r}))}waitUntilReady(e,o){var s,r;return t(this,void 0,void 0,(function*(){const t=null!==(s=null==o?void 0:o.pollingInterval)&&void 0!==s?s:3e3,i=null!==(r=null==o?void 0:o.pollingTimeout)&&void 0!==r?r:-1,n=Date.now();for(;;){const o=yield this.get(e);if("completed"===o.status||"error"===o.status)return o;if(i>0&&Date.now()-n>i)throw new Error("Polling timeout");yield new Promise((e=>setTimeout(e,t)))}}))}get(e){return this.fetchJson(`/v2/transcript/${e}`)}list(e){return t(this,void 0,void 0,(function*(){let t="/v2/transcript";"string"==typeof e?t=e:e&&(t=`${t}?${new URLSearchParams(Object.keys(e).map((t=>{var o;return[t,(null===(o=e[t])||void 0===o?void 0:o.toString())||""]})))}`);const o=yield this.fetchJson(t);for(const e of o.transcripts)e.created=new Date(e.created),e.completed&&(e.completed=new Date(e.completed));return o}))}delete(e){return this.fetchJson(`/v2/transcript/${e}`,{method:"DELETE"})}wordSearch(e,t){const o=new URLSearchParams({words:t.join(",")});return this.fetchJson(`/v2/transcript/${e}/word-search?${o.toString()}`)}sentences(e){return this.fetchJson(`/v2/transcript/${e}/sentences`)}paragraphs(e){return this.fetchJson(`/v2/transcript/${e}/paragraphs`)}subtitles(e,o="srt",s){return t(this,void 0,void 0,(function*(){let t=`/v2/transcript/${e}/${o}`;if(s){const e=new URLSearchParams;e.set("chars_per_caption",s.toString()),t+=`?${e.toString()}`}const r=yield this.fetch(t);return yield r.text()}))}redactions(e){return this.fetchJson(`/v2/transcript/${e}/redacted-audio`)}}function J(e){let t;try{return t=new URL(e),"file:"===t.protocol?t.pathname:null}catch(t){return e}}var A={createReadStream:function(){throw new Error("'fs' is not supported in this environment.")}};class E extends o{upload(e){return t(this,void 0,void 0,(function*(){let t;t="string"==typeof e?A.createReadStream(e):e;return(yield this.fetchJson("/v2/upload",{method:"POST",body:t,headers:{"Content-Type":"application/octet-stream"},duplex:"half"})).upload_url}))}}e.AssemblyAI=class{constructor(e){e.baseUrl=e.baseUrl||"https://api.assemblyai.com",e.baseUrl&&e.baseUrl.endsWith("/")&&(e.baseUrl=e.baseUrl.slice(0,-1)),this.files=new E(e),this.transcripts=new x(e,this.files),this.lemur=new s(e),this.realtime=new O(e)}},e.FileService=E,e.LemurService=s,e.RealtimeService=R,e.RealtimeServiceFactory=O,e.TranscriptService=x}));
|
package/dist/index.cjs
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
var isomorphicStreams = require('@swimburger/isomorphic-streams');
|
|
4
4
|
var WebSocket = require('isomorphic-ws');
|
|
5
|
-
var fs = require('fs');
|
|
6
5
|
|
|
7
6
|
/******************************************************************************
|
|
8
7
|
Copyright (c) Microsoft Corporation.
|
|
@@ -180,9 +179,9 @@ class RealtimeService {
|
|
|
180
179
|
this.realtimeUrl = (_a = params.realtimeUrl) !== null && _a !== void 0 ? _a : defaultRealtimeUrl;
|
|
181
180
|
this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
|
|
182
181
|
this.wordBoost = params.wordBoost;
|
|
183
|
-
if ("apiKey" in params)
|
|
182
|
+
if ("apiKey" in params && params.apiKey)
|
|
184
183
|
this.apiKey = params.apiKey;
|
|
185
|
-
if ("token" in params)
|
|
184
|
+
if ("token" in params && params.token)
|
|
186
185
|
this.token = params.token;
|
|
187
186
|
if (!(this.apiKey || this.token)) {
|
|
188
187
|
throw new Error("API key or temporary token is required.");
|
|
@@ -567,6 +566,15 @@ function getPath(path) {
|
|
|
567
566
|
}
|
|
568
567
|
}
|
|
569
568
|
|
|
569
|
+
function throwError() {
|
|
570
|
+
throw new Error("'fs' is not supported in this environment.");
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
const createReadStream = throwError;
|
|
574
|
+
var fs = {
|
|
575
|
+
createReadStream,
|
|
576
|
+
};
|
|
577
|
+
|
|
570
578
|
class FileService extends BaseService {
|
|
571
579
|
/**
|
|
572
580
|
* Upload a local file to AssemblyAI.
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { WritableStream } from '@swimburger/isomorphic-streams';
|
|
2
2
|
import WebSocket from 'isomorphic-ws';
|
|
3
|
-
import fs from 'fs';
|
|
4
3
|
|
|
5
4
|
/******************************************************************************
|
|
6
5
|
Copyright (c) Microsoft Corporation.
|
|
@@ -178,9 +177,9 @@ class RealtimeService {
|
|
|
178
177
|
this.realtimeUrl = (_a = params.realtimeUrl) !== null && _a !== void 0 ? _a : defaultRealtimeUrl;
|
|
179
178
|
this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
|
|
180
179
|
this.wordBoost = params.wordBoost;
|
|
181
|
-
if ("apiKey" in params)
|
|
180
|
+
if ("apiKey" in params && params.apiKey)
|
|
182
181
|
this.apiKey = params.apiKey;
|
|
183
|
-
if ("token" in params)
|
|
182
|
+
if ("token" in params && params.token)
|
|
184
183
|
this.token = params.token;
|
|
185
184
|
if (!(this.apiKey || this.token)) {
|
|
186
185
|
throw new Error("API key or temporary token is required.");
|
|
@@ -565,6 +564,15 @@ function getPath(path) {
|
|
|
565
564
|
}
|
|
566
565
|
}
|
|
567
566
|
|
|
567
|
+
function throwError() {
|
|
568
|
+
throw new Error("'fs' is not supported in this environment.");
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
const createReadStream = throwError;
|
|
572
|
+
var fs = {
|
|
573
|
+
createReadStream,
|
|
574
|
+
};
|
|
575
|
+
|
|
568
576
|
class FileService extends BaseService {
|
|
569
577
|
/**
|
|
570
578
|
* Upload a local file to AssemblyAI.
|
|
@@ -0,0 +1,618 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var isomorphicStreams = require('@swimburger/isomorphic-streams');
|
|
4
|
+
var WebSocket = require('isomorphic-ws');
|
|
5
|
+
var fs = require('fs');
|
|
6
|
+
|
|
7
|
+
/******************************************************************************
|
|
8
|
+
Copyright (c) Microsoft Corporation.
|
|
9
|
+
|
|
10
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
11
|
+
purpose with or without fee is hereby granted.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
14
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
15
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
16
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
17
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
18
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
19
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
20
|
+
***************************************************************************** */
|
|
21
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
function __rest(s, e) {
|
|
25
|
+
var t = {};
|
|
26
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
27
|
+
t[p] = s[p];
|
|
28
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
29
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
30
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
31
|
+
t[p[i]] = s[p[i]];
|
|
32
|
+
}
|
|
33
|
+
return t;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function __awaiter(thisArg, _arguments, P, generator) {
|
|
37
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
38
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
39
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
40
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
41
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
42
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
47
|
+
var e = new Error(message);
|
|
48
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Base class for services that communicate with the API.
|
|
53
|
+
*/
|
|
54
|
+
class BaseService {
|
|
55
|
+
/**
|
|
56
|
+
* Create a new service.
|
|
57
|
+
* @param params The parameters to use for the service.
|
|
58
|
+
*/
|
|
59
|
+
constructor(params) {
|
|
60
|
+
this.params = params;
|
|
61
|
+
}
|
|
62
|
+
fetch(input, init) {
|
|
63
|
+
var _a;
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
init = init !== null && init !== void 0 ? init : {};
|
|
66
|
+
init.headers = (_a = init.headers) !== null && _a !== void 0 ? _a : {};
|
|
67
|
+
init.headers = Object.assign({ Authorization: this.params.apiKey, "Content-Type": "application/json" }, init.headers);
|
|
68
|
+
if (!input.startsWith("http"))
|
|
69
|
+
input = this.params.baseUrl + input;
|
|
70
|
+
const response = yield fetch(input, init);
|
|
71
|
+
if (response.status >= 400) {
|
|
72
|
+
let json;
|
|
73
|
+
const text = yield response.text();
|
|
74
|
+
if (text) {
|
|
75
|
+
try {
|
|
76
|
+
json = JSON.parse(text);
|
|
77
|
+
}
|
|
78
|
+
catch (_b) {
|
|
79
|
+
/* empty */
|
|
80
|
+
}
|
|
81
|
+
if (json === null || json === void 0 ? void 0 : json.error)
|
|
82
|
+
throw new Error(json.error);
|
|
83
|
+
throw new Error(text);
|
|
84
|
+
}
|
|
85
|
+
throw new Error(`HTTP Error: ${response.status} ${response.statusText}`);
|
|
86
|
+
}
|
|
87
|
+
return response;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
fetchJson(input, init) {
|
|
91
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
92
|
+
const response = yield this.fetch(input, init);
|
|
93
|
+
return response.json();
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
class LemurService extends BaseService {
|
|
99
|
+
summary(params) {
|
|
100
|
+
return this.fetchJson("/lemur/v3/generate/summary", {
|
|
101
|
+
method: "POST",
|
|
102
|
+
body: JSON.stringify(params),
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
questionAnswer(params) {
|
|
106
|
+
return this.fetchJson("/lemur/v3/generate/question-answer", {
|
|
107
|
+
method: "POST",
|
|
108
|
+
body: JSON.stringify(params),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
actionItems(params) {
|
|
112
|
+
return this.fetchJson("/lemur/v3/generate/action-items", {
|
|
113
|
+
method: "POST",
|
|
114
|
+
body: JSON.stringify(params),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
task(params) {
|
|
118
|
+
return this.fetchJson("/lemur/v3/generate/task", {
|
|
119
|
+
method: "POST",
|
|
120
|
+
body: JSON.stringify(params),
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Delete the data for a previously submitted LeMUR request.
|
|
125
|
+
* @param id ID of the LeMUR request
|
|
126
|
+
*/
|
|
127
|
+
purgeRequestData(id) {
|
|
128
|
+
return this.fetchJson(`/lemur/v3/${id}`, {
|
|
129
|
+
method: "DELETE",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
var RealtimeErrorType;
|
|
135
|
+
(function (RealtimeErrorType) {
|
|
136
|
+
RealtimeErrorType[RealtimeErrorType["BadSampleRate"] = 4000] = "BadSampleRate";
|
|
137
|
+
RealtimeErrorType[RealtimeErrorType["AuthFailed"] = 4001] = "AuthFailed";
|
|
138
|
+
// Both InsufficientFunds and FreeAccount error use 4002
|
|
139
|
+
RealtimeErrorType[RealtimeErrorType["InsufficientFundsOrFreeAccount"] = 4002] = "InsufficientFundsOrFreeAccount";
|
|
140
|
+
RealtimeErrorType[RealtimeErrorType["NonexistentSessionId"] = 4004] = "NonexistentSessionId";
|
|
141
|
+
RealtimeErrorType[RealtimeErrorType["SessionExpired"] = 4008] = "SessionExpired";
|
|
142
|
+
RealtimeErrorType[RealtimeErrorType["ClosedSession"] = 4010] = "ClosedSession";
|
|
143
|
+
RealtimeErrorType[RealtimeErrorType["RateLimited"] = 4029] = "RateLimited";
|
|
144
|
+
RealtimeErrorType[RealtimeErrorType["UniqueSessionViolation"] = 4030] = "UniqueSessionViolation";
|
|
145
|
+
RealtimeErrorType[RealtimeErrorType["SessionTimeout"] = 4031] = "SessionTimeout";
|
|
146
|
+
RealtimeErrorType[RealtimeErrorType["AudioTooShort"] = 4032] = "AudioTooShort";
|
|
147
|
+
RealtimeErrorType[RealtimeErrorType["AudioTooLong"] = 4033] = "AudioTooLong";
|
|
148
|
+
RealtimeErrorType[RealtimeErrorType["BadJson"] = 4100] = "BadJson";
|
|
149
|
+
RealtimeErrorType[RealtimeErrorType["BadSchema"] = 4101] = "BadSchema";
|
|
150
|
+
RealtimeErrorType[RealtimeErrorType["TooManyStreams"] = 4102] = "TooManyStreams";
|
|
151
|
+
RealtimeErrorType[RealtimeErrorType["Reconnected"] = 4103] = "Reconnected";
|
|
152
|
+
RealtimeErrorType[RealtimeErrorType["ReconnectAttemptsExhausted"] = 1013] = "ReconnectAttemptsExhausted";
|
|
153
|
+
})(RealtimeErrorType || (RealtimeErrorType = {}));
|
|
154
|
+
const RealtimeErrorMessages = {
|
|
155
|
+
[RealtimeErrorType.BadSampleRate]: "Sample rate must be a positive integer",
|
|
156
|
+
[RealtimeErrorType.AuthFailed]: "Not Authorized",
|
|
157
|
+
[RealtimeErrorType.InsufficientFundsOrFreeAccount]: "Insufficient funds or you are using a free account. This feature is paid-only and requires you to add a credit card. Please visit https://assemblyai.com/dashboard/ to add a credit card to your account.",
|
|
158
|
+
[RealtimeErrorType.NonexistentSessionId]: "Session ID does not exist",
|
|
159
|
+
[RealtimeErrorType.SessionExpired]: "Session has expired",
|
|
160
|
+
[RealtimeErrorType.ClosedSession]: "Session is closed",
|
|
161
|
+
[RealtimeErrorType.RateLimited]: "Rate limited",
|
|
162
|
+
[RealtimeErrorType.UniqueSessionViolation]: "Unique session violation",
|
|
163
|
+
[RealtimeErrorType.SessionTimeout]: "Session Timeout",
|
|
164
|
+
[RealtimeErrorType.AudioTooShort]: "Audio too short",
|
|
165
|
+
[RealtimeErrorType.AudioTooLong]: "Audio too long",
|
|
166
|
+
[RealtimeErrorType.BadJson]: "Bad JSON",
|
|
167
|
+
[RealtimeErrorType.BadSchema]: "Bad schema",
|
|
168
|
+
[RealtimeErrorType.TooManyStreams]: "Too many streams",
|
|
169
|
+
[RealtimeErrorType.Reconnected]: "Reconnected",
|
|
170
|
+
[RealtimeErrorType.ReconnectAttemptsExhausted]: "Reconnect attempts exhausted",
|
|
171
|
+
};
|
|
172
|
+
class RealtimeError extends Error {
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
176
|
+
class RealtimeService {
|
|
177
|
+
constructor(params) {
|
|
178
|
+
var _a, _b;
|
|
179
|
+
this.listeners = {};
|
|
180
|
+
this.realtimeUrl = (_a = params.realtimeUrl) !== null && _a !== void 0 ? _a : defaultRealtimeUrl;
|
|
181
|
+
this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
|
|
182
|
+
this.wordBoost = params.wordBoost;
|
|
183
|
+
if ("apiKey" in params && params.apiKey)
|
|
184
|
+
this.apiKey = params.apiKey;
|
|
185
|
+
if ("token" in params && params.token)
|
|
186
|
+
this.token = params.token;
|
|
187
|
+
if (!(this.apiKey || this.token)) {
|
|
188
|
+
throw new Error("API key or temporary token is required.");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
connectionUrl() {
|
|
192
|
+
const url = new URL(this.realtimeUrl);
|
|
193
|
+
if (url.protocol !== "wss:") {
|
|
194
|
+
throw new Error("Invalid protocol, must be wss");
|
|
195
|
+
}
|
|
196
|
+
const searchParams = new URLSearchParams();
|
|
197
|
+
if (this.token) {
|
|
198
|
+
searchParams.set("token", this.token);
|
|
199
|
+
}
|
|
200
|
+
searchParams.set("sample_rate", this.sampleRate.toString());
|
|
201
|
+
if (this.wordBoost && this.wordBoost.length > 0) {
|
|
202
|
+
searchParams.set("word_boost", JSON.stringify(this.wordBoost));
|
|
203
|
+
}
|
|
204
|
+
url.search = searchParams.toString();
|
|
205
|
+
return url;
|
|
206
|
+
}
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
208
|
+
on(event, listener) {
|
|
209
|
+
this.listeners[event] = listener;
|
|
210
|
+
}
|
|
211
|
+
connect() {
|
|
212
|
+
return new Promise((resolve) => {
|
|
213
|
+
if (this.socket) {
|
|
214
|
+
throw new Error("Already connected");
|
|
215
|
+
}
|
|
216
|
+
const url = this.connectionUrl();
|
|
217
|
+
if (this.token) {
|
|
218
|
+
this.socket = new WebSocket(url.toString());
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
this.socket = new WebSocket(url.toString(), {
|
|
222
|
+
headers: { Authorization: this.apiKey },
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
this.socket.onclose = ({ code, reason }) => {
|
|
226
|
+
var _a, _b;
|
|
227
|
+
if (!reason) {
|
|
228
|
+
if (code in RealtimeErrorType) {
|
|
229
|
+
reason = RealtimeErrorMessages[code];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
(_b = (_a = this.listeners).close) === null || _b === void 0 ? void 0 : _b.call(_a, code, reason);
|
|
233
|
+
};
|
|
234
|
+
this.socket.onerror = (event) => {
|
|
235
|
+
var _a, _b, _c, _d;
|
|
236
|
+
if (event.error)
|
|
237
|
+
(_b = (_a = this.listeners).error) === null || _b === void 0 ? void 0 : _b.call(_a, event.error);
|
|
238
|
+
else
|
|
239
|
+
(_d = (_c = this.listeners).error) === null || _d === void 0 ? void 0 : _d.call(_c, new Error(event.message));
|
|
240
|
+
};
|
|
241
|
+
this.socket.onmessage = ({ data }) => {
|
|
242
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
243
|
+
const message = JSON.parse(data.toString());
|
|
244
|
+
if ("error" in message) {
|
|
245
|
+
(_b = (_a = this.listeners).error) === null || _b === void 0 ? void 0 : _b.call(_a, new RealtimeError(message.error));
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
switch (message.message_type) {
|
|
249
|
+
case "SessionBegins": {
|
|
250
|
+
const openObject = {
|
|
251
|
+
sessionId: message.session_id,
|
|
252
|
+
expiresAt: new Date(message.expires_at),
|
|
253
|
+
};
|
|
254
|
+
resolve(openObject);
|
|
255
|
+
(_d = (_c = this.listeners).open) === null || _d === void 0 ? void 0 : _d.call(_c, openObject);
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
case "PartialTranscript": {
|
|
259
|
+
// message.created is actually a string when coming from the socket
|
|
260
|
+
message.created = new Date(message.created);
|
|
261
|
+
(_f = (_e = this.listeners).transcript) === null || _f === void 0 ? void 0 : _f.call(_e, message);
|
|
262
|
+
(_h = (_g = this.listeners)["transcript.partial"]) === null || _h === void 0 ? void 0 : _h.call(_g, message);
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case "FinalTranscript": {
|
|
266
|
+
// message.created is actually a string when coming from the socket
|
|
267
|
+
message.created = new Date(message.created);
|
|
268
|
+
(_k = (_j = this.listeners).transcript) === null || _k === void 0 ? void 0 : _k.call(_j, message);
|
|
269
|
+
(_m = (_l = this.listeners)["transcript.final"]) === null || _m === void 0 ? void 0 : _m.call(_l, message);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
case "SessionTerminated": {
|
|
273
|
+
(_o = this.sessionTerminatedResolve) === null || _o === void 0 ? void 0 : _o.call(this);
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
sendAudio(audio) {
|
|
281
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
282
|
+
throw new Error("Socket is not open for communication");
|
|
283
|
+
}
|
|
284
|
+
let audioData;
|
|
285
|
+
if (typeof Buffer !== "undefined") {
|
|
286
|
+
audioData = Buffer.from(audio).toString("base64");
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
// Buffer is not available in the browser by default
|
|
290
|
+
// https://stackoverflow.com/a/42334410/2919731
|
|
291
|
+
audioData = btoa(new Uint8Array(audio).reduce((data, byte) => data + String.fromCharCode(byte), ""));
|
|
292
|
+
}
|
|
293
|
+
const payload = {
|
|
294
|
+
audio_data: audioData,
|
|
295
|
+
};
|
|
296
|
+
this.socket.send(JSON.stringify(payload));
|
|
297
|
+
}
|
|
298
|
+
stream() {
|
|
299
|
+
return new isomorphicStreams.WritableStream({
|
|
300
|
+
write: (chunk) => {
|
|
301
|
+
this.sendAudio(chunk);
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
close(waitForSessionTermination = true) {
|
|
306
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
307
|
+
if (this.socket) {
|
|
308
|
+
if (this.socket.readyState === WebSocket.OPEN) {
|
|
309
|
+
const terminateSessionMessage = `{"terminate_session": true}`;
|
|
310
|
+
if (waitForSessionTermination) {
|
|
311
|
+
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
312
|
+
this.sessionTerminatedResolve = resolve;
|
|
313
|
+
});
|
|
314
|
+
this.socket.send(terminateSessionMessage);
|
|
315
|
+
yield sessionTerminatedPromise;
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
this.socket.send(terminateSessionMessage);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
if ("removeAllListeners" in this.socket)
|
|
322
|
+
this.socket.removeAllListeners();
|
|
323
|
+
this.socket.close();
|
|
324
|
+
}
|
|
325
|
+
this.listeners = {};
|
|
326
|
+
this.socket = undefined;
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
class RealtimeServiceFactory extends BaseService {
|
|
332
|
+
constructor(params) {
|
|
333
|
+
super(params);
|
|
334
|
+
this.rtFactoryParams = params;
|
|
335
|
+
}
|
|
336
|
+
createService(params) {
|
|
337
|
+
if (!params)
|
|
338
|
+
params = { apiKey: this.rtFactoryParams.apiKey };
|
|
339
|
+
else if (!("token" in params) && !params.apiKey) {
|
|
340
|
+
params.apiKey = this.rtFactoryParams.apiKey;
|
|
341
|
+
}
|
|
342
|
+
return new RealtimeService(params);
|
|
343
|
+
}
|
|
344
|
+
createTemporaryToken(params) {
|
|
345
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
346
|
+
const data = yield this.fetchJson("/v2/realtime/token", {
|
|
347
|
+
method: "POST",
|
|
348
|
+
body: JSON.stringify(params),
|
|
349
|
+
});
|
|
350
|
+
return data.token;
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
class TranscriptService extends BaseService {
|
|
356
|
+
constructor(params, files) {
|
|
357
|
+
super(params);
|
|
358
|
+
this.files = files;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Transcribe an audio file. This will create a transcript and wait until the transcript status is "completed" or "error".
|
|
362
|
+
* @param params The parameters to transcribe an audio file.
|
|
363
|
+
* @param options The options to transcribe an audio file.
|
|
364
|
+
* @returns A promise that resolves to the transcript. The transcript status is "completed" or "error".
|
|
365
|
+
*/
|
|
366
|
+
transcribe(params, options) {
|
|
367
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
368
|
+
const transcript = yield this.submit(params);
|
|
369
|
+
return yield this.waitUntilReady(transcript.id, options);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Submits a transcription job for an audio file. This will not wait until the transcript status is "completed" or "error".
|
|
374
|
+
* @param params The parameters to start the transcription of an audio file.
|
|
375
|
+
* @returns A promise that resolves to the queued transcript.
|
|
376
|
+
*/
|
|
377
|
+
submit(params) {
|
|
378
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
379
|
+
const { audio } = params, createParams = __rest(params, ["audio"]);
|
|
380
|
+
let audioUrl;
|
|
381
|
+
if (typeof audio === "string") {
|
|
382
|
+
const path = getPath(audio);
|
|
383
|
+
if (path !== null) {
|
|
384
|
+
// audio is local path, upload local file
|
|
385
|
+
audioUrl = yield this.files.upload(path);
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// audio is not a local path, assume it's a URL
|
|
389
|
+
audioUrl = audio;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
// audio is of uploadable type
|
|
394
|
+
audioUrl = yield this.files.upload(audio);
|
|
395
|
+
}
|
|
396
|
+
const data = yield this.fetchJson("/v2/transcript", {
|
|
397
|
+
method: "POST",
|
|
398
|
+
body: JSON.stringify(Object.assign(Object.assign({}, createParams), { audio_url: audioUrl })),
|
|
399
|
+
});
|
|
400
|
+
return data;
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Create a transcript.
|
|
405
|
+
* @param params The parameters to create a transcript.
|
|
406
|
+
* @param options The options used for creating the new transcript.
|
|
407
|
+
* @returns A promise that resolves to the transcript.
|
|
408
|
+
* @deprecated Use `transcribe` instead to transcribe a audio file that includes polling, or `submit` to transcribe a audio file without polling.
|
|
409
|
+
*/
|
|
410
|
+
create(params, options) {
|
|
411
|
+
var _a;
|
|
412
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
413
|
+
const path = getPath(params.audio_url);
|
|
414
|
+
if (path !== null) {
|
|
415
|
+
const uploadUrl = yield this.files.upload(path);
|
|
416
|
+
params.audio_url = uploadUrl;
|
|
417
|
+
}
|
|
418
|
+
const data = yield this.fetchJson("/v2/transcript", {
|
|
419
|
+
method: "POST",
|
|
420
|
+
body: JSON.stringify(params),
|
|
421
|
+
});
|
|
422
|
+
if ((_a = options === null || options === void 0 ? void 0 : options.poll) !== null && _a !== void 0 ? _a : true) {
|
|
423
|
+
return yield this.waitUntilReady(data.id, options);
|
|
424
|
+
}
|
|
425
|
+
return data;
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Wait until the transcript ready, either the status is "completed" or "error".
|
|
430
|
+
* @param transcriptId The ID of the transcript.
|
|
431
|
+
* @param options The options to wait until the transcript is ready.
|
|
432
|
+
* @returns A promise that resolves to the transcript. The transcript status is "completed" or "error".
|
|
433
|
+
*/
|
|
434
|
+
waitUntilReady(transcriptId, options) {
|
|
435
|
+
var _a, _b;
|
|
436
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
437
|
+
const pollingInterval = (_a = options === null || options === void 0 ? void 0 : options.pollingInterval) !== null && _a !== void 0 ? _a : 3000;
|
|
438
|
+
const pollingTimeout = (_b = options === null || options === void 0 ? void 0 : options.pollingTimeout) !== null && _b !== void 0 ? _b : -1;
|
|
439
|
+
const startTime = Date.now();
|
|
440
|
+
// eslint-disable-next-line no-constant-condition
|
|
441
|
+
while (true) {
|
|
442
|
+
const transcript = yield this.get(transcriptId);
|
|
443
|
+
if (transcript.status === "completed" || transcript.status === "error") {
|
|
444
|
+
return transcript;
|
|
445
|
+
}
|
|
446
|
+
else if (pollingTimeout > 0 &&
|
|
447
|
+
Date.now() - startTime > pollingTimeout) {
|
|
448
|
+
throw new Error("Polling timeout");
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
yield new Promise((resolve) => setTimeout(resolve, pollingInterval));
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Retrieve a transcript.
|
|
458
|
+
* @param id The identifier of the transcript.
|
|
459
|
+
* @returns A promise that resolves to the transcript.
|
|
460
|
+
*/
|
|
461
|
+
get(id) {
|
|
462
|
+
return this.fetchJson(`/v2/transcript/${id}`);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Retrieves a page of transcript listings.
|
|
466
|
+
* @param parameters The parameters to filter the transcript list by, or the URL to retrieve the transcript list from.
|
|
467
|
+
*/
|
|
468
|
+
list(parameters) {
|
|
469
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
470
|
+
let url = "/v2/transcript";
|
|
471
|
+
if (typeof parameters === "string") {
|
|
472
|
+
url = parameters;
|
|
473
|
+
}
|
|
474
|
+
else if (parameters) {
|
|
475
|
+
url = `${url}?${new URLSearchParams(Object.keys(parameters).map((key) => {
|
|
476
|
+
var _a;
|
|
477
|
+
return [
|
|
478
|
+
key,
|
|
479
|
+
((_a = parameters[key]) === null || _a === void 0 ? void 0 : _a.toString()) || "",
|
|
480
|
+
];
|
|
481
|
+
}))}`;
|
|
482
|
+
}
|
|
483
|
+
const data = yield this.fetchJson(url);
|
|
484
|
+
for (const transcriptListItem of data.transcripts) {
|
|
485
|
+
transcriptListItem.created = new Date(transcriptListItem.created);
|
|
486
|
+
if (transcriptListItem.completed) {
|
|
487
|
+
transcriptListItem.completed = new Date(transcriptListItem.completed);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return data;
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Delete a transcript
|
|
495
|
+
* @param id The identifier of the transcript.
|
|
496
|
+
* @returns A promise that resolves to the transcript.
|
|
497
|
+
*/
|
|
498
|
+
delete(id) {
|
|
499
|
+
return this.fetchJson(`/v2/transcript/${id}`, { method: "DELETE" });
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Search through the transcript for a specific set of keywords.
|
|
503
|
+
* You can search for individual words, numbers, or phrases containing up to five words or numbers.
|
|
504
|
+
* @param id The identifier of the transcript.
|
|
505
|
+
* @param words Keywords to search for.
|
|
506
|
+
* @return A promise that resolves to the sentences.
|
|
507
|
+
*/
|
|
508
|
+
wordSearch(id, words) {
|
|
509
|
+
const params = new URLSearchParams({ words: words.join(",") });
|
|
510
|
+
return this.fetchJson(`/v2/transcript/${id}/word-search?${params.toString()}`);
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Retrieve all sentences of a transcript.
|
|
514
|
+
* @param id The identifier of the transcript.
|
|
515
|
+
* @return A promise that resolves to the sentences.
|
|
516
|
+
*/
|
|
517
|
+
sentences(id) {
|
|
518
|
+
return this.fetchJson(`/v2/transcript/${id}/sentences`);
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Retrieve all paragraphs of a transcript.
|
|
522
|
+
* @param id The identifier of the transcript.
|
|
523
|
+
* @return A promise that resolves to the paragraphs.
|
|
524
|
+
*/
|
|
525
|
+
paragraphs(id) {
|
|
526
|
+
return this.fetchJson(`/v2/transcript/${id}/paragraphs`);
|
|
527
|
+
}
|
|
528
|
+
/**
|
|
529
|
+
* Retrieve subtitles of a transcript.
|
|
530
|
+
* @param id The identifier of the transcript.
|
|
531
|
+
* @param format The format of the subtitles.
|
|
532
|
+
* @param chars_per_caption The maximum number of characters per caption.
|
|
533
|
+
* @return A promise that resolves to the subtitles text.
|
|
534
|
+
*/
|
|
535
|
+
subtitles(id, format = "srt", chars_per_caption) {
|
|
536
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
537
|
+
let url = `/v2/transcript/${id}/${format}`;
|
|
538
|
+
if (chars_per_caption) {
|
|
539
|
+
const params = new URLSearchParams();
|
|
540
|
+
params.set("chars_per_caption", chars_per_caption.toString());
|
|
541
|
+
url += `?${params.toString()}`;
|
|
542
|
+
}
|
|
543
|
+
const response = yield this.fetch(url);
|
|
544
|
+
return yield response.text();
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Retrieve redactions of a transcript.
|
|
549
|
+
* @param id The identifier of the transcript.
|
|
550
|
+
* @return A promise that resolves to the subtitles text.
|
|
551
|
+
*/
|
|
552
|
+
redactions(id) {
|
|
553
|
+
return this.fetchJson(`/v2/transcript/${id}/redacted-audio`);
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
function getPath(path) {
|
|
557
|
+
let url;
|
|
558
|
+
try {
|
|
559
|
+
url = new URL(path);
|
|
560
|
+
if (url.protocol === "file:")
|
|
561
|
+
return url.pathname;
|
|
562
|
+
else
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
catch (_a) {
|
|
566
|
+
return path;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
class FileService extends BaseService {
|
|
571
|
+
/**
|
|
572
|
+
* Upload a local file to AssemblyAI.
|
|
573
|
+
* @param input The local file path to upload, or a stream or buffer of the file to upload.
|
|
574
|
+
* @return A promise that resolves to the uploaded file URL.
|
|
575
|
+
*/
|
|
576
|
+
upload(input) {
|
|
577
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
578
|
+
let fileData;
|
|
579
|
+
if (typeof input === "string")
|
|
580
|
+
fileData = fs.createReadStream(input);
|
|
581
|
+
else
|
|
582
|
+
fileData = input;
|
|
583
|
+
const data = yield this.fetchJson("/v2/upload", {
|
|
584
|
+
method: "POST",
|
|
585
|
+
body: fileData,
|
|
586
|
+
headers: {
|
|
587
|
+
"Content-Type": "application/octet-stream",
|
|
588
|
+
},
|
|
589
|
+
duplex: "half",
|
|
590
|
+
});
|
|
591
|
+
return data.upload_url;
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const defaultBaseUrl = "https://api.assemblyai.com";
|
|
597
|
+
class AssemblyAI {
|
|
598
|
+
/**
|
|
599
|
+
* Create a new AssemblyAI client.
|
|
600
|
+
* @param params The parameters for the service, including the API key and base URL, if any.
|
|
601
|
+
*/
|
|
602
|
+
constructor(params) {
|
|
603
|
+
params.baseUrl = params.baseUrl || defaultBaseUrl;
|
|
604
|
+
if (params.baseUrl && params.baseUrl.endsWith("/"))
|
|
605
|
+
params.baseUrl = params.baseUrl.slice(0, -1);
|
|
606
|
+
this.files = new FileService(params);
|
|
607
|
+
this.transcripts = new TranscriptService(params, this.files);
|
|
608
|
+
this.lemur = new LemurService(params);
|
|
609
|
+
this.realtime = new RealtimeServiceFactory(params);
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
exports.AssemblyAI = AssemblyAI;
|
|
614
|
+
exports.FileService = FileService;
|
|
615
|
+
exports.LemurService = LemurService;
|
|
616
|
+
exports.RealtimeService = RealtimeService;
|
|
617
|
+
exports.RealtimeServiceFactory = RealtimeServiceFactory;
|
|
618
|
+
exports.TranscriptService = TranscriptService;
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { WritableStream } from '@swimburger/isomorphic-streams';
|
|
2
|
+
import WebSocket from 'isomorphic-ws';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
|
|
1
5
|
/******************************************************************************
|
|
2
6
|
Copyright (c) Microsoft Corporation.
|
|
3
7
|
|
|
@@ -125,66 +129,6 @@ class LemurService extends BaseService {
|
|
|
125
129
|
}
|
|
126
130
|
}
|
|
127
131
|
|
|
128
|
-
const {
|
|
129
|
-
ReadableStream,
|
|
130
|
-
ReadableStreamDefaultReader,
|
|
131
|
-
ReadableStreamBYOBReader,
|
|
132
|
-
ReadableStreamBYOBRequest,
|
|
133
|
-
ReadableByteStreamController,
|
|
134
|
-
ReadableStreamDefaultController,
|
|
135
|
-
TransformStream,
|
|
136
|
-
TransformStreamDefaultController,
|
|
137
|
-
WritableStream,
|
|
138
|
-
WritableStreamDefaultWriter,
|
|
139
|
-
WritableStreamDefaultController,
|
|
140
|
-
ByteLengthQueuingStrategy,
|
|
141
|
-
CountQueuingStrategy,
|
|
142
|
-
TextEncoderStream,
|
|
143
|
-
TextDecoderStream,
|
|
144
|
-
} = window;
|
|
145
|
-
|
|
146
|
-
// Polyfill to make ReadableStream async-iterable with for-await
|
|
147
|
-
// https://jakearchibald.com/2017/async-iterators-and-generators/#making-streams-iterate
|
|
148
|
-
if (!ReadableStream.prototype[Symbol.asyncIterator]) {
|
|
149
|
-
async function* streamAsyncIterator() {
|
|
150
|
-
// Get a lock on the stream
|
|
151
|
-
const reader = this.getReader();
|
|
152
|
-
|
|
153
|
-
try {
|
|
154
|
-
while (true) {
|
|
155
|
-
// Read from the stream
|
|
156
|
-
const { done, value } = await reader.read();
|
|
157
|
-
// Exit if we're done
|
|
158
|
-
if (done) return;
|
|
159
|
-
// Else yield the chunk
|
|
160
|
-
yield value;
|
|
161
|
-
}
|
|
162
|
-
} finally {
|
|
163
|
-
reader.releaseLock();
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
ReadableStream.prototype[Symbol.asyncIterator] = streamAsyncIterator;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// https://github.com/maxogden/websocket-stream/blob/48dc3ddf943e5ada668c31ccd94e9186f02fafbd/ws-fallback.js
|
|
171
|
-
|
|
172
|
-
var ws = null;
|
|
173
|
-
|
|
174
|
-
if (typeof WebSocket !== 'undefined') {
|
|
175
|
-
ws = WebSocket;
|
|
176
|
-
} else if (typeof MozWebSocket !== 'undefined') {
|
|
177
|
-
ws = MozWebSocket;
|
|
178
|
-
} else if (typeof global !== 'undefined') {
|
|
179
|
-
ws = global.WebSocket || global.MozWebSocket;
|
|
180
|
-
} else if (typeof window !== 'undefined') {
|
|
181
|
-
ws = window.WebSocket || window.MozWebSocket;
|
|
182
|
-
} else if (typeof self !== 'undefined') {
|
|
183
|
-
ws = self.WebSocket || self.MozWebSocket;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
var WebSocket$1 = ws;
|
|
187
|
-
|
|
188
132
|
var RealtimeErrorType;
|
|
189
133
|
(function (RealtimeErrorType) {
|
|
190
134
|
RealtimeErrorType[RealtimeErrorType["BadSampleRate"] = 4000] = "BadSampleRate";
|
|
@@ -234,9 +178,9 @@ class RealtimeService {
|
|
|
234
178
|
this.realtimeUrl = (_a = params.realtimeUrl) !== null && _a !== void 0 ? _a : defaultRealtimeUrl;
|
|
235
179
|
this.sampleRate = (_b = params.sampleRate) !== null && _b !== void 0 ? _b : 16000;
|
|
236
180
|
this.wordBoost = params.wordBoost;
|
|
237
|
-
if ("apiKey" in params)
|
|
181
|
+
if ("apiKey" in params && params.apiKey)
|
|
238
182
|
this.apiKey = params.apiKey;
|
|
239
|
-
if ("token" in params)
|
|
183
|
+
if ("token" in params && params.token)
|
|
240
184
|
this.token = params.token;
|
|
241
185
|
if (!(this.apiKey || this.token)) {
|
|
242
186
|
throw new Error("API key or temporary token is required.");
|
|
@@ -269,10 +213,10 @@ class RealtimeService {
|
|
|
269
213
|
}
|
|
270
214
|
const url = this.connectionUrl();
|
|
271
215
|
if (this.token) {
|
|
272
|
-
this.socket = new WebSocket
|
|
216
|
+
this.socket = new WebSocket(url.toString());
|
|
273
217
|
}
|
|
274
218
|
else {
|
|
275
|
-
this.socket = new WebSocket
|
|
219
|
+
this.socket = new WebSocket(url.toString(), {
|
|
276
220
|
headers: { Authorization: this.apiKey },
|
|
277
221
|
});
|
|
278
222
|
}
|
|
@@ -332,7 +276,7 @@ class RealtimeService {
|
|
|
332
276
|
});
|
|
333
277
|
}
|
|
334
278
|
sendAudio(audio) {
|
|
335
|
-
if (!this.socket || this.socket.readyState !== WebSocket
|
|
279
|
+
if (!this.socket || this.socket.readyState !== WebSocket.OPEN) {
|
|
336
280
|
throw new Error("Socket is not open for communication");
|
|
337
281
|
}
|
|
338
282
|
let audioData;
|
|
@@ -359,7 +303,7 @@ class RealtimeService {
|
|
|
359
303
|
close(waitForSessionTermination = true) {
|
|
360
304
|
return __awaiter(this, void 0, void 0, function* () {
|
|
361
305
|
if (this.socket) {
|
|
362
|
-
if (this.socket.readyState === WebSocket
|
|
306
|
+
if (this.socket.readyState === WebSocket.OPEN) {
|
|
363
307
|
const terminateSessionMessage = `{"terminate_session": true}`;
|
|
364
308
|
if (waitForSessionTermination) {
|
|
365
309
|
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
@@ -621,15 +565,6 @@ function getPath(path) {
|
|
|
621
565
|
}
|
|
622
566
|
}
|
|
623
567
|
|
|
624
|
-
function throwError() {
|
|
625
|
-
throw new Error("Function is not supported in this environment.");
|
|
626
|
-
}
|
|
627
|
-
|
|
628
|
-
const createReadStream = throwError;
|
|
629
|
-
var fs = {
|
|
630
|
-
createReadStream,
|
|
631
|
-
};
|
|
632
|
-
|
|
633
568
|
class FileService extends BaseService {
|
|
634
569
|
/**
|
|
635
570
|
* Upload a local file to AssemblyAI.
|
package/docs/compat.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# SDK Compatibility
|
|
2
|
+
|
|
3
|
+
The JavaScript SDK is developed for Node.js but is also compatible with other runtimes
|
|
4
|
+
such as the browser, Deno, Bun, Cloudflare Workers, etc.
|
|
5
|
+
|
|
6
|
+
## Browser compatibility
|
|
7
|
+
|
|
8
|
+
To make the SDK compatible with the browser, the SDK aims to use web standards as much as possible.
|
|
9
|
+
However, there are still incompatibilities between Node.js and the browser.
|
|
10
|
+
|
|
11
|
+
- `RealtimeService` doesn't support the AssemblyAI API key in the browser.
|
|
12
|
+
Instead, you have to generate a temporary auth token using `client.realtime.createTemporaryToken`, and pass in the resulting token to the real-time transcriber.
|
|
13
|
+
|
|
14
|
+
Generate a temporary auth token on the server.
|
|
15
|
+
|
|
16
|
+
```js
|
|
17
|
+
import { AssemblyAI } from "assemblyai"
|
|
18
|
+
// Ideally, to avoid embedding your API key client side,
|
|
19
|
+
// you generate this token on the server, and pass it to the client via an API.
|
|
20
|
+
const client = new AssemblyAI({ apiKey: "YOUR_API_KEY" });
|
|
21
|
+
const token = await client.realtime.createTemporaryToken({ expires_in = 480 });
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
> [!NOTE]
|
|
25
|
+
> We recommend generating the token on the server, so you don't embed your AssemblyAI API key in your client app.
|
|
26
|
+
> If you embed the API key on the client, everyone can see it and use it for themselves.
|
|
27
|
+
|
|
28
|
+
Then pass the token via an API to the client.
|
|
29
|
+
On the client, create an instance of `RealtimeService` using the token.
|
|
30
|
+
|
|
31
|
+
```js
|
|
32
|
+
import { RealtimeService } from "assemblyai";
|
|
33
|
+
// or the following if you're using UMD
|
|
34
|
+
// const { RealtimeService } = assemblyai;
|
|
35
|
+
|
|
36
|
+
const token = getToken(); // getToken is a function for you to implement
|
|
37
|
+
|
|
38
|
+
const rt = new RealtimeService({
|
|
39
|
+
token: token,
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
- You can't pass local audio file paths to `client.files.upload`, `client.transcripts.transcribe`, and `client.transcripts.submit`. If you do, you'll get the following error: "'fs' is not supported in this environment.".
|
|
44
|
+
If you want to transcribe audio files, you must use a public URL, a stream, or a buffer.
|
|
45
|
+
|
|
46
|
+
> [!WARNING]
|
|
47
|
+
> The SDK is usable from the browser, but we strongly recommend you don't embed the AssemblyAI API key into your client apps.
|
|
48
|
+
> If you embed the API key on the client, everyone can see it and use it for themselves.
|
|
49
|
+
> Instead, create use the SDK on the server and provide APIs for your client to call.
|
|
50
|
+
|
|
51
|
+
## Deno, Bun, Cloudflare Workers, etc.
|
|
52
|
+
|
|
53
|
+
Most server-side JavaScript runtimes include a compatibility layer with Node.js.
|
|
54
|
+
Our SDK is developed for Node.js, which makes it compatible with other runtimes through their compatibility layer.
|
|
55
|
+
The bugs in these compatibility layers may introduce issues in our SDK.
|
|
56
|
+
|
|
57
|
+
## Report issues
|
|
58
|
+
|
|
59
|
+
If you find any (undocumented) bugs when using the SDK, [submit a GitHub issue](https://github.com/AssemblyAI/assemblyai-node-sdk). We'll try to fix it or at least document the compatibility issue.
|
package/package.json
CHANGED
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "assemblyai",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.1",
|
|
4
4
|
"description": "The AssemblyAI JavaScript SDK provides an easy-to-use interface for interacting with the AssemblyAI API, which supports async and real-time transcription, as well as the latest LeMUR models.",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"node": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./dist/index.node.mjs",
|
|
11
|
+
"require": "./dist/index.node.cjs"
|
|
12
|
+
},
|
|
13
|
+
"bun": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.node.mjs",
|
|
16
|
+
"require": "./dist/index.node.cjs"
|
|
17
|
+
},
|
|
18
|
+
"deno": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.node.mjs",
|
|
21
|
+
"require": "./dist/index.node.cjs"
|
|
22
|
+
},
|
|
23
|
+
"workerd": "./dist/index.mjs",
|
|
24
|
+
"browser": "./dist/index.mjs",
|
|
8
25
|
"import": "./dist/index.mjs",
|
|
9
26
|
"require": "./dist/index.cjs",
|
|
10
|
-
"browser": "./dist/index.browser.js",
|
|
11
27
|
"default": "./dist/index.cjs"
|
|
12
28
|
},
|
|
13
29
|
"./package.json": "./package.json"
|
|
14
30
|
},
|
|
15
31
|
"type": "commonjs",
|
|
16
|
-
"main": "dist/index.cjs",
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
32
|
+
"main": "./dist/index.cjs",
|
|
33
|
+
"require": "./dist/index.cjs",
|
|
34
|
+
"module": "./dist/index.mjs",
|
|
35
|
+
"types": "./dist/index.d.ts",
|
|
36
|
+
"typings": "./dist/index.d.ts",
|
|
20
37
|
"repository": {
|
|
21
38
|
"type": "git",
|
|
22
39
|
"url": "git+https://github.com/AssemblyAI/assemblyai-node-sdk.git"
|
|
@@ -30,8 +47,7 @@
|
|
|
30
47
|
"build": "pnpm clean && pnpm rollup -c",
|
|
31
48
|
"clean": "rimraf dist",
|
|
32
49
|
"lint": "eslint -c .eslintrc.json '{src,tests}/**/*.{js,ts}' && publint",
|
|
33
|
-
"test": "
|
|
34
|
-
"test:unit": "jest --config jest.config.rollup.ts",
|
|
50
|
+
"test": "jest --config jest.config.rollup.ts",
|
|
35
51
|
"format": "prettier '**/*' --write",
|
|
36
52
|
"generate-types": "tsx ./scripts/generate-types.ts && pnpm format",
|
|
37
53
|
"copybara:dry-run": "./copybara.sh dry_run --init-history",
|
|
@@ -49,7 +65,11 @@
|
|
|
49
65
|
"homepage": "https://www.assemblyai.com/docs",
|
|
50
66
|
"files": [
|
|
51
67
|
"dist",
|
|
52
|
-
"src"
|
|
68
|
+
"src",
|
|
69
|
+
"package.json",
|
|
70
|
+
"README.md",
|
|
71
|
+
"CHANGELOG.md",
|
|
72
|
+
"docs"
|
|
53
73
|
],
|
|
54
74
|
"devDependencies": {
|
|
55
75
|
"@rollup/plugin-alias": "^5.0.1",
|
|
@@ -83,7 +103,7 @@
|
|
|
83
103
|
"typescript": "^5.2.2"
|
|
84
104
|
},
|
|
85
105
|
"dependencies": {
|
|
86
|
-
"@swimburger/isomorphic-streams": "^1.
|
|
106
|
+
"@swimburger/isomorphic-streams": "^1.1.1",
|
|
87
107
|
"isomorphic-ws": "^5.0.0",
|
|
88
108
|
"ws": "^8.13.0"
|
|
89
109
|
}
|
|
@@ -33,8 +33,8 @@ export class RealtimeService {
|
|
|
33
33
|
this.realtimeUrl = params.realtimeUrl ?? defaultRealtimeUrl;
|
|
34
34
|
this.sampleRate = params.sampleRate ?? 16_000;
|
|
35
35
|
this.wordBoost = params.wordBoost;
|
|
36
|
-
if ("apiKey" in params) this.apiKey = params.apiKey;
|
|
37
|
-
if ("token" in params) this.token = params.token;
|
|
36
|
+
if ("apiKey" in params && params.apiKey) this.apiKey = params.apiKey;
|
|
37
|
+
if ("token" in params && params.token) this.token = params.token;
|
|
38
38
|
|
|
39
39
|
if (!(this.apiKey || this.token)) {
|
|
40
40
|
throw new Error("API key or temporary token is required.");
|
|
File without changes
|