assemblyai 4.12.1 → 4.13.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/dist/assemblyai.umd.js +210 -4
- package/dist/assemblyai.umd.min.js +1 -1
- package/dist/browser.mjs +207 -5
- package/dist/bun.mjs +207 -5
- package/dist/deno.mjs +207 -5
- package/dist/index.cjs +210 -4
- package/dist/index.mjs +210 -5
- package/dist/node.cjs +207 -4
- package/dist/node.mjs +207 -5
- package/dist/services/index.d.ts +6 -1
- package/dist/services/streaming/factory.d.ts +10 -0
- package/dist/services/streaming/index.d.ts +2 -0
- package/dist/services/streaming/service.d.ts +20 -0
- package/dist/types/index.d.ts +2 -1
- package/dist/types/openapi.generated.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -0
- package/dist/types/streaming/index.d.ts +71 -0
- package/dist/utils/errors/index.d.ts +1 -0
- package/dist/utils/errors/streaming.d.ts +23 -0
- package/dist/workerd.mjs +207 -5
- package/package.json +1 -1
- package/src/services/index.ts +14 -0
- package/src/services/streaming/factory.ts +39 -0
- package/src/services/streaming/index.ts +2 -0
- package/src/services/streaming/service.ts +219 -0
- package/src/types/index.ts +2 -1
- package/src/types/openapi.generated.ts +1 -1
- package/src/types/services/index.ts +1 -0
- package/src/types/streaming/index.ts +94 -0
- package/src/utils/errors/index.ts +6 -0
- package/src/utils/errors/streaming.ts +51 -0
package/dist/bun.mjs
CHANGED
|
@@ -17,7 +17,7 @@ if (typeof navigator !== "undefined" && navigator.userAgent) {
|
|
|
17
17
|
defaultUserAgentString += navigator.userAgent;
|
|
18
18
|
}
|
|
19
19
|
const defaultUserAgent = {
|
|
20
|
-
sdk: { name: "JavaScript", version: "4.
|
|
20
|
+
sdk: { name: "JavaScript", version: "4.13.0-beta.1" },
|
|
21
21
|
};
|
|
22
22
|
if (typeof process !== "undefined") {
|
|
23
23
|
if (process.versions.node && defaultUserAgentString.indexOf("Node") === -1) {
|
|
@@ -199,9 +199,48 @@ const RealtimeErrorMessages = {
|
|
|
199
199
|
class RealtimeError extends Error {
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
const StreamingErrorType = {
|
|
203
|
+
BadSampleRate: 4000,
|
|
204
|
+
AuthFailed: 4001,
|
|
205
|
+
InsufficientFunds: 4002,
|
|
206
|
+
FreeTierUser: 4003,
|
|
207
|
+
NonexistentSessionId: 4004,
|
|
208
|
+
SessionExpired: 4008,
|
|
209
|
+
ClosedSession: 4010,
|
|
210
|
+
RateLimited: 4029,
|
|
211
|
+
UniqueSessionViolation: 4030,
|
|
212
|
+
SessionTimeout: 4031,
|
|
213
|
+
AudioTooShort: 4032,
|
|
214
|
+
AudioTooLong: 4033,
|
|
215
|
+
AudioTooSmallToTranscode: 4034,
|
|
216
|
+
BadSchema: 4101,
|
|
217
|
+
TooManyStreams: 4102,
|
|
218
|
+
Reconnected: 4103,
|
|
219
|
+
};
|
|
220
|
+
const StreamingErrorMessages = {
|
|
221
|
+
[StreamingErrorType.BadSampleRate]: "Sample rate must be a positive integer",
|
|
222
|
+
[StreamingErrorType.AuthFailed]: "Not Authorized",
|
|
223
|
+
[StreamingErrorType.InsufficientFunds]: "Insufficient funds",
|
|
224
|
+
[StreamingErrorType.FreeTierUser]: "This feature is paid-only and requires you to add a credit card. Please visit https://app.assemblyai.com/ to add a credit card to your account.",
|
|
225
|
+
[StreamingErrorType.NonexistentSessionId]: "Session ID does not exist",
|
|
226
|
+
[StreamingErrorType.SessionExpired]: "Session has expired",
|
|
227
|
+
[StreamingErrorType.ClosedSession]: "Session is closed",
|
|
228
|
+
[StreamingErrorType.RateLimited]: "Rate limited",
|
|
229
|
+
[StreamingErrorType.UniqueSessionViolation]: "Unique session violation",
|
|
230
|
+
[StreamingErrorType.SessionTimeout]: "Session Timeout",
|
|
231
|
+
[StreamingErrorType.AudioTooShort]: "Audio too short",
|
|
232
|
+
[StreamingErrorType.AudioTooLong]: "Audio too long",
|
|
233
|
+
[StreamingErrorType.AudioTooSmallToTranscode]: "Audio too small to transcode",
|
|
234
|
+
[StreamingErrorType.BadSchema]: "Bad schema",
|
|
235
|
+
[StreamingErrorType.TooManyStreams]: "Too many streams",
|
|
236
|
+
[StreamingErrorType.Reconnected]: "This session has been reconnected. This WebSocket is no longer valid.",
|
|
237
|
+
};
|
|
238
|
+
class StreamingError extends Error {
|
|
239
|
+
}
|
|
240
|
+
|
|
202
241
|
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
203
242
|
const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
|
|
204
|
-
const terminateSessionMessage = `{"terminate_session":true}`;
|
|
243
|
+
const terminateSessionMessage$1 = `{"terminate_session":true}`;
|
|
205
244
|
/**
|
|
206
245
|
* RealtimeTranscriber connects to the Streaming Speech-to-Text API and lets you transcribe audio in real-time.
|
|
207
246
|
*/
|
|
@@ -390,11 +429,11 @@ class RealtimeTranscriber {
|
|
|
390
429
|
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
391
430
|
this.sessionTerminatedResolve = resolve;
|
|
392
431
|
});
|
|
393
|
-
this.socket.send(terminateSessionMessage);
|
|
432
|
+
this.socket.send(terminateSessionMessage$1);
|
|
394
433
|
await sessionTerminatedPromise;
|
|
395
434
|
}
|
|
396
435
|
else {
|
|
397
|
-
this.socket.send(terminateSessionMessage);
|
|
436
|
+
this.socket.send(terminateSessionMessage$1);
|
|
398
437
|
}
|
|
399
438
|
}
|
|
400
439
|
if (this.socket?.removeAllListeners)
|
|
@@ -727,7 +766,166 @@ function dataUrlToBlob(dataUrl) {
|
|
|
727
766
|
return new Blob([u8arr], { type: mime });
|
|
728
767
|
}
|
|
729
768
|
|
|
769
|
+
const defaultStreamingUrl$1 = "wss://streaming.assemblyai.com/v3/ws";
|
|
770
|
+
const terminateSessionMessage = `{"type":"Terminate"}`;
|
|
771
|
+
class StreamingTranscriber {
|
|
772
|
+
constructor(params) {
|
|
773
|
+
this.listeners = {};
|
|
774
|
+
this.params = {
|
|
775
|
+
...params,
|
|
776
|
+
websocketBaseUrl: params.websocketBaseUrl || defaultStreamingUrl$1,
|
|
777
|
+
};
|
|
778
|
+
if ("token" in params && params.token)
|
|
779
|
+
this.token = params.token;
|
|
780
|
+
if ("apiKey" in params && params.apiKey)
|
|
781
|
+
this.apiKey = params.apiKey;
|
|
782
|
+
if (!(this.token || this.apiKey)) {
|
|
783
|
+
throw new Error("API key or temporary token is required.");
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
connectionUrl() {
|
|
787
|
+
const url = new URL(this.params.websocketBaseUrl ?? "");
|
|
788
|
+
if (url.protocol !== "wss:") {
|
|
789
|
+
throw new Error("Invalid protocol, must be wss");
|
|
790
|
+
}
|
|
791
|
+
const searchParams = new URLSearchParams();
|
|
792
|
+
searchParams.set("sample_rate", this.params.sampleRate.toString());
|
|
793
|
+
if (this.params.wordFinalizationMaxWaitTime) {
|
|
794
|
+
searchParams.set("word_finalization_max_wait_time", this.params.wordFinalizationMaxWaitTime.toString());
|
|
795
|
+
}
|
|
796
|
+
if (this.params.endOfTurnConfidenceThreshold) {
|
|
797
|
+
searchParams.set("end_of_turn_confidence_threshold", this.params.endOfTurnConfidenceThreshold.toString());
|
|
798
|
+
}
|
|
799
|
+
if (this.params.minEndOfTurnSilenceWhenConfident) {
|
|
800
|
+
searchParams.set("min_end_of_turn_silence_when_confident", this.params.minEndOfTurnSilenceWhenConfident.toString());
|
|
801
|
+
}
|
|
802
|
+
if (this.params.maxTurnSilence) {
|
|
803
|
+
searchParams.set("max_turn_silence", this.params.maxTurnSilence.toString());
|
|
804
|
+
}
|
|
805
|
+
if (this.params.formattedFinals) {
|
|
806
|
+
searchParams.set("formatted_finals", this.params.formattedFinals.toString());
|
|
807
|
+
}
|
|
808
|
+
url.search = searchParams.toString();
|
|
809
|
+
return url;
|
|
810
|
+
}
|
|
811
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
812
|
+
on(event, listener) {
|
|
813
|
+
this.listeners[event] = listener;
|
|
814
|
+
}
|
|
815
|
+
connect() {
|
|
816
|
+
return new Promise((resolve) => {
|
|
817
|
+
if (this.socket) {
|
|
818
|
+
throw new Error("Already connected");
|
|
819
|
+
}
|
|
820
|
+
const url = this.connectionUrl();
|
|
821
|
+
this.socket = factory(url.toString(), {
|
|
822
|
+
headers: { Authorization: this.token || this.apiKey },
|
|
823
|
+
});
|
|
824
|
+
this.socket.binaryType = "arraybuffer";
|
|
825
|
+
this.socket.onopen = () => { };
|
|
826
|
+
this.socket.onclose = ({ code, reason }) => {
|
|
827
|
+
if (!reason) {
|
|
828
|
+
if (code in StreamingErrorMessages) {
|
|
829
|
+
reason = StreamingErrorMessages[code];
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
this.listeners.close?.(code, reason);
|
|
833
|
+
};
|
|
834
|
+
this.socket.onerror = (event) => {
|
|
835
|
+
if (event.error)
|
|
836
|
+
this.listeners.error?.(event.error);
|
|
837
|
+
else
|
|
838
|
+
this.listeners.error?.(new Error(event.message));
|
|
839
|
+
};
|
|
840
|
+
this.socket.onmessage = ({ data }) => {
|
|
841
|
+
const message = JSON.parse(data.toString());
|
|
842
|
+
if ("error" in message) {
|
|
843
|
+
this.listeners.error?.(new StreamingError(message.error));
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
switch (message.type) {
|
|
847
|
+
case "Begin": {
|
|
848
|
+
resolve(message);
|
|
849
|
+
this.listeners.open?.(message);
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
case "Turn": {
|
|
853
|
+
this.listeners.turn?.(message);
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
case "Termination": {
|
|
857
|
+
this.sessionTerminatedResolve?.();
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
stream() {
|
|
865
|
+
return new WritableStream({
|
|
866
|
+
write: (chunk) => {
|
|
867
|
+
this.sendAudio(chunk);
|
|
868
|
+
},
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
sendAudio(audio) {
|
|
872
|
+
this.send(audio);
|
|
873
|
+
}
|
|
874
|
+
send(data) {
|
|
875
|
+
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
|
|
876
|
+
throw new Error("Socket is not open for communication");
|
|
877
|
+
}
|
|
878
|
+
this.socket.send(data);
|
|
879
|
+
}
|
|
880
|
+
async close(waitForSessionTermination = true) {
|
|
881
|
+
if (this.socket) {
|
|
882
|
+
if (this.socket.readyState === this.socket.OPEN) {
|
|
883
|
+
if (waitForSessionTermination) {
|
|
884
|
+
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
885
|
+
this.sessionTerminatedResolve = resolve;
|
|
886
|
+
});
|
|
887
|
+
this.socket.send(terminateSessionMessage);
|
|
888
|
+
await sessionTerminatedPromise;
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
this.socket.send(terminateSessionMessage);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
if (this.socket?.removeAllListeners)
|
|
895
|
+
this.socket.removeAllListeners();
|
|
896
|
+
this.socket.close();
|
|
897
|
+
}
|
|
898
|
+
this.listeners = {};
|
|
899
|
+
this.socket = undefined;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
class StreamingTranscriberFactory extends BaseService {
|
|
904
|
+
constructor(params) {
|
|
905
|
+
super(params);
|
|
906
|
+
}
|
|
907
|
+
transcriber(params) {
|
|
908
|
+
return new StreamingTranscriber(params);
|
|
909
|
+
}
|
|
910
|
+
async createTemporaryToken(params) {
|
|
911
|
+
const searchParams = new URLSearchParams();
|
|
912
|
+
// Add each param to the search params
|
|
913
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
914
|
+
if (value !== undefined && value !== null) {
|
|
915
|
+
searchParams.append(key, String(value));
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
const queryString = searchParams.toString();
|
|
919
|
+
const url = queryString ? `/v3/token?${queryString}` : "/v3/token";
|
|
920
|
+
const data = await this.fetchJson(url, {
|
|
921
|
+
method: "GET",
|
|
922
|
+
});
|
|
923
|
+
return data.token;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
730
927
|
const defaultBaseUrl = "https://api.assemblyai.com";
|
|
928
|
+
const defaultStreamingUrl = "https://streaming.assemblyai.com";
|
|
731
929
|
class AssemblyAI {
|
|
732
930
|
/**
|
|
733
931
|
* Create a new AssemblyAI client.
|
|
@@ -742,7 +940,11 @@ class AssemblyAI {
|
|
|
742
940
|
this.transcripts = new TranscriptService(params, this.files);
|
|
743
941
|
this.lemur = new LemurService(params);
|
|
744
942
|
this.realtime = new RealtimeTranscriberFactory(params);
|
|
943
|
+
this.streaming = new StreamingTranscriberFactory({
|
|
944
|
+
...params,
|
|
945
|
+
baseUrl: params.streamingBaseUrl || defaultStreamingUrl,
|
|
946
|
+
});
|
|
745
947
|
}
|
|
746
948
|
}
|
|
747
949
|
|
|
748
|
-
export { AssemblyAI, FileService, LemurService, RealtimeService, RealtimeServiceFactory, RealtimeTranscriber, RealtimeTranscriberFactory, TranscriptService };
|
|
950
|
+
export { AssemblyAI, FileService, LemurService, RealtimeService, RealtimeServiceFactory, RealtimeTranscriber, RealtimeTranscriberFactory, StreamingTranscriber, TranscriptService };
|
package/dist/deno.mjs
CHANGED
|
@@ -17,7 +17,7 @@ if (typeof navigator !== "undefined" && navigator.userAgent) {
|
|
|
17
17
|
defaultUserAgentString += navigator.userAgent;
|
|
18
18
|
}
|
|
19
19
|
const defaultUserAgent = {
|
|
20
|
-
sdk: { name: "JavaScript", version: "4.
|
|
20
|
+
sdk: { name: "JavaScript", version: "4.13.0-beta.1" },
|
|
21
21
|
};
|
|
22
22
|
if (typeof process !== "undefined") {
|
|
23
23
|
if (process.versions.node && defaultUserAgentString.indexOf("Node") === -1) {
|
|
@@ -199,9 +199,48 @@ const RealtimeErrorMessages = {
|
|
|
199
199
|
class RealtimeError extends Error {
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
const StreamingErrorType = {
|
|
203
|
+
BadSampleRate: 4000,
|
|
204
|
+
AuthFailed: 4001,
|
|
205
|
+
InsufficientFunds: 4002,
|
|
206
|
+
FreeTierUser: 4003,
|
|
207
|
+
NonexistentSessionId: 4004,
|
|
208
|
+
SessionExpired: 4008,
|
|
209
|
+
ClosedSession: 4010,
|
|
210
|
+
RateLimited: 4029,
|
|
211
|
+
UniqueSessionViolation: 4030,
|
|
212
|
+
SessionTimeout: 4031,
|
|
213
|
+
AudioTooShort: 4032,
|
|
214
|
+
AudioTooLong: 4033,
|
|
215
|
+
AudioTooSmallToTranscode: 4034,
|
|
216
|
+
BadSchema: 4101,
|
|
217
|
+
TooManyStreams: 4102,
|
|
218
|
+
Reconnected: 4103,
|
|
219
|
+
};
|
|
220
|
+
const StreamingErrorMessages = {
|
|
221
|
+
[StreamingErrorType.BadSampleRate]: "Sample rate must be a positive integer",
|
|
222
|
+
[StreamingErrorType.AuthFailed]: "Not Authorized",
|
|
223
|
+
[StreamingErrorType.InsufficientFunds]: "Insufficient funds",
|
|
224
|
+
[StreamingErrorType.FreeTierUser]: "This feature is paid-only and requires you to add a credit card. Please visit https://app.assemblyai.com/ to add a credit card to your account.",
|
|
225
|
+
[StreamingErrorType.NonexistentSessionId]: "Session ID does not exist",
|
|
226
|
+
[StreamingErrorType.SessionExpired]: "Session has expired",
|
|
227
|
+
[StreamingErrorType.ClosedSession]: "Session is closed",
|
|
228
|
+
[StreamingErrorType.RateLimited]: "Rate limited",
|
|
229
|
+
[StreamingErrorType.UniqueSessionViolation]: "Unique session violation",
|
|
230
|
+
[StreamingErrorType.SessionTimeout]: "Session Timeout",
|
|
231
|
+
[StreamingErrorType.AudioTooShort]: "Audio too short",
|
|
232
|
+
[StreamingErrorType.AudioTooLong]: "Audio too long",
|
|
233
|
+
[StreamingErrorType.AudioTooSmallToTranscode]: "Audio too small to transcode",
|
|
234
|
+
[StreamingErrorType.BadSchema]: "Bad schema",
|
|
235
|
+
[StreamingErrorType.TooManyStreams]: "Too many streams",
|
|
236
|
+
[StreamingErrorType.Reconnected]: "This session has been reconnected. This WebSocket is no longer valid.",
|
|
237
|
+
};
|
|
238
|
+
class StreamingError extends Error {
|
|
239
|
+
}
|
|
240
|
+
|
|
202
241
|
const defaultRealtimeUrl = "wss://api.assemblyai.com/v2/realtime/ws";
|
|
203
242
|
const forceEndOfUtteranceMessage = `{"force_end_utterance":true}`;
|
|
204
|
-
const terminateSessionMessage = `{"terminate_session":true}`;
|
|
243
|
+
const terminateSessionMessage$1 = `{"terminate_session":true}`;
|
|
205
244
|
/**
|
|
206
245
|
* RealtimeTranscriber connects to the Streaming Speech-to-Text API and lets you transcribe audio in real-time.
|
|
207
246
|
*/
|
|
@@ -390,11 +429,11 @@ class RealtimeTranscriber {
|
|
|
390
429
|
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
391
430
|
this.sessionTerminatedResolve = resolve;
|
|
392
431
|
});
|
|
393
|
-
this.socket.send(terminateSessionMessage);
|
|
432
|
+
this.socket.send(terminateSessionMessage$1);
|
|
394
433
|
await sessionTerminatedPromise;
|
|
395
434
|
}
|
|
396
435
|
else {
|
|
397
|
-
this.socket.send(terminateSessionMessage);
|
|
436
|
+
this.socket.send(terminateSessionMessage$1);
|
|
398
437
|
}
|
|
399
438
|
}
|
|
400
439
|
if (this.socket?.removeAllListeners)
|
|
@@ -727,7 +766,166 @@ function dataUrlToBlob(dataUrl) {
|
|
|
727
766
|
return new Blob([u8arr], { type: mime });
|
|
728
767
|
}
|
|
729
768
|
|
|
769
|
+
const defaultStreamingUrl$1 = "wss://streaming.assemblyai.com/v3/ws";
|
|
770
|
+
const terminateSessionMessage = `{"type":"Terminate"}`;
|
|
771
|
+
class StreamingTranscriber {
|
|
772
|
+
constructor(params) {
|
|
773
|
+
this.listeners = {};
|
|
774
|
+
this.params = {
|
|
775
|
+
...params,
|
|
776
|
+
websocketBaseUrl: params.websocketBaseUrl || defaultStreamingUrl$1,
|
|
777
|
+
};
|
|
778
|
+
if ("token" in params && params.token)
|
|
779
|
+
this.token = params.token;
|
|
780
|
+
if ("apiKey" in params && params.apiKey)
|
|
781
|
+
this.apiKey = params.apiKey;
|
|
782
|
+
if (!(this.token || this.apiKey)) {
|
|
783
|
+
throw new Error("API key or temporary token is required.");
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
connectionUrl() {
|
|
787
|
+
const url = new URL(this.params.websocketBaseUrl ?? "");
|
|
788
|
+
if (url.protocol !== "wss:") {
|
|
789
|
+
throw new Error("Invalid protocol, must be wss");
|
|
790
|
+
}
|
|
791
|
+
const searchParams = new URLSearchParams();
|
|
792
|
+
searchParams.set("sample_rate", this.params.sampleRate.toString());
|
|
793
|
+
if (this.params.wordFinalizationMaxWaitTime) {
|
|
794
|
+
searchParams.set("word_finalization_max_wait_time", this.params.wordFinalizationMaxWaitTime.toString());
|
|
795
|
+
}
|
|
796
|
+
if (this.params.endOfTurnConfidenceThreshold) {
|
|
797
|
+
searchParams.set("end_of_turn_confidence_threshold", this.params.endOfTurnConfidenceThreshold.toString());
|
|
798
|
+
}
|
|
799
|
+
if (this.params.minEndOfTurnSilenceWhenConfident) {
|
|
800
|
+
searchParams.set("min_end_of_turn_silence_when_confident", this.params.minEndOfTurnSilenceWhenConfident.toString());
|
|
801
|
+
}
|
|
802
|
+
if (this.params.maxTurnSilence) {
|
|
803
|
+
searchParams.set("max_turn_silence", this.params.maxTurnSilence.toString());
|
|
804
|
+
}
|
|
805
|
+
if (this.params.formattedFinals) {
|
|
806
|
+
searchParams.set("formatted_finals", this.params.formattedFinals.toString());
|
|
807
|
+
}
|
|
808
|
+
url.search = searchParams.toString();
|
|
809
|
+
return url;
|
|
810
|
+
}
|
|
811
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
812
|
+
on(event, listener) {
|
|
813
|
+
this.listeners[event] = listener;
|
|
814
|
+
}
|
|
815
|
+
connect() {
|
|
816
|
+
return new Promise((resolve) => {
|
|
817
|
+
if (this.socket) {
|
|
818
|
+
throw new Error("Already connected");
|
|
819
|
+
}
|
|
820
|
+
const url = this.connectionUrl();
|
|
821
|
+
this.socket = factory(url.toString(), {
|
|
822
|
+
headers: { Authorization: this.token || this.apiKey },
|
|
823
|
+
});
|
|
824
|
+
this.socket.binaryType = "arraybuffer";
|
|
825
|
+
this.socket.onopen = () => { };
|
|
826
|
+
this.socket.onclose = ({ code, reason }) => {
|
|
827
|
+
if (!reason) {
|
|
828
|
+
if (code in StreamingErrorMessages) {
|
|
829
|
+
reason = StreamingErrorMessages[code];
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
this.listeners.close?.(code, reason);
|
|
833
|
+
};
|
|
834
|
+
this.socket.onerror = (event) => {
|
|
835
|
+
if (event.error)
|
|
836
|
+
this.listeners.error?.(event.error);
|
|
837
|
+
else
|
|
838
|
+
this.listeners.error?.(new Error(event.message));
|
|
839
|
+
};
|
|
840
|
+
this.socket.onmessage = ({ data }) => {
|
|
841
|
+
const message = JSON.parse(data.toString());
|
|
842
|
+
if ("error" in message) {
|
|
843
|
+
this.listeners.error?.(new StreamingError(message.error));
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
switch (message.type) {
|
|
847
|
+
case "Begin": {
|
|
848
|
+
resolve(message);
|
|
849
|
+
this.listeners.open?.(message);
|
|
850
|
+
break;
|
|
851
|
+
}
|
|
852
|
+
case "Turn": {
|
|
853
|
+
this.listeners.turn?.(message);
|
|
854
|
+
break;
|
|
855
|
+
}
|
|
856
|
+
case "Termination": {
|
|
857
|
+
this.sessionTerminatedResolve?.();
|
|
858
|
+
break;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
};
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
stream() {
|
|
865
|
+
return new WritableStream({
|
|
866
|
+
write: (chunk) => {
|
|
867
|
+
this.sendAudio(chunk);
|
|
868
|
+
},
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
sendAudio(audio) {
|
|
872
|
+
this.send(audio);
|
|
873
|
+
}
|
|
874
|
+
send(data) {
|
|
875
|
+
if (!this.socket || this.socket.readyState !== this.socket.OPEN) {
|
|
876
|
+
throw new Error("Socket is not open for communication");
|
|
877
|
+
}
|
|
878
|
+
this.socket.send(data);
|
|
879
|
+
}
|
|
880
|
+
async close(waitForSessionTermination = true) {
|
|
881
|
+
if (this.socket) {
|
|
882
|
+
if (this.socket.readyState === this.socket.OPEN) {
|
|
883
|
+
if (waitForSessionTermination) {
|
|
884
|
+
const sessionTerminatedPromise = new Promise((resolve) => {
|
|
885
|
+
this.sessionTerminatedResolve = resolve;
|
|
886
|
+
});
|
|
887
|
+
this.socket.send(terminateSessionMessage);
|
|
888
|
+
await sessionTerminatedPromise;
|
|
889
|
+
}
|
|
890
|
+
else {
|
|
891
|
+
this.socket.send(terminateSessionMessage);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
if (this.socket?.removeAllListeners)
|
|
895
|
+
this.socket.removeAllListeners();
|
|
896
|
+
this.socket.close();
|
|
897
|
+
}
|
|
898
|
+
this.listeners = {};
|
|
899
|
+
this.socket = undefined;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
class StreamingTranscriberFactory extends BaseService {
|
|
904
|
+
constructor(params) {
|
|
905
|
+
super(params);
|
|
906
|
+
}
|
|
907
|
+
transcriber(params) {
|
|
908
|
+
return new StreamingTranscriber(params);
|
|
909
|
+
}
|
|
910
|
+
async createTemporaryToken(params) {
|
|
911
|
+
const searchParams = new URLSearchParams();
|
|
912
|
+
// Add each param to the search params
|
|
913
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
914
|
+
if (value !== undefined && value !== null) {
|
|
915
|
+
searchParams.append(key, String(value));
|
|
916
|
+
}
|
|
917
|
+
});
|
|
918
|
+
const queryString = searchParams.toString();
|
|
919
|
+
const url = queryString ? `/v3/token?${queryString}` : "/v3/token";
|
|
920
|
+
const data = await this.fetchJson(url, {
|
|
921
|
+
method: "GET",
|
|
922
|
+
});
|
|
923
|
+
return data.token;
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
730
927
|
const defaultBaseUrl = "https://api.assemblyai.com";
|
|
928
|
+
const defaultStreamingUrl = "https://streaming.assemblyai.com";
|
|
731
929
|
class AssemblyAI {
|
|
732
930
|
/**
|
|
733
931
|
* Create a new AssemblyAI client.
|
|
@@ -742,7 +940,11 @@ class AssemblyAI {
|
|
|
742
940
|
this.transcripts = new TranscriptService(params, this.files);
|
|
743
941
|
this.lemur = new LemurService(params);
|
|
744
942
|
this.realtime = new RealtimeTranscriberFactory(params);
|
|
943
|
+
this.streaming = new StreamingTranscriberFactory({
|
|
944
|
+
...params,
|
|
945
|
+
baseUrl: params.streamingBaseUrl || defaultStreamingUrl,
|
|
946
|
+
});
|
|
745
947
|
}
|
|
746
948
|
}
|
|
747
949
|
|
|
748
|
-
export { AssemblyAI, FileService, LemurService, RealtimeService, RealtimeServiceFactory, RealtimeTranscriber, RealtimeTranscriberFactory, TranscriptService };
|
|
950
|
+
export { AssemblyAI, FileService, LemurService, RealtimeService, RealtimeServiceFactory, RealtimeTranscriber, RealtimeTranscriberFactory, StreamingTranscriber, TranscriptService };
|