@vapi-ai/web 1.0.105 → 1.0.107

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.
@@ -0,0 +1,58 @@
1
+ import async, { QueueObject } from "async";
2
+
3
+ class AtomicMediaSource {
4
+ mediaSource: MediaSource;
5
+ private sourceBuffer: SourceBuffer | null = null;
6
+ private operationsQueue: QueueObject<() => Promise<void>>;
7
+
8
+ constructor() {
9
+ this.mediaSource = new MediaSource();
10
+ this.operationsQueue = this.createOperationQueue();
11
+
12
+ this.mediaSource.addEventListener("sourceopen", () => {
13
+ this.mediaSource.duration = Number.POSITIVE_INFINITY;
14
+ this.sourceBuffer = this.mediaSource.addSourceBuffer(
15
+ 'audio/webm; codecs="opus"'
16
+ );
17
+ this.sourceBuffer.mode = "sequence";
18
+ });
19
+ }
20
+
21
+ public appendBuffer(buffer: ArrayBuffer) {
22
+ this.operationsQueue.push(() => {
23
+ return new Promise((resolve, reject) => {
24
+ if (this.sourceBuffer) {
25
+ this.sourceBuffer.addEventListener("updateend", () => {
26
+ resolve();
27
+ });
28
+ this.sourceBuffer.appendBuffer(buffer);
29
+ } else {
30
+ reject();
31
+ }
32
+ });
33
+ });
34
+ }
35
+
36
+ public clearBuffer() {
37
+ this.operationsQueue.push(() => {
38
+ return new Promise((resolve, reject) => {
39
+ if (this.sourceBuffer) {
40
+ this.sourceBuffer.addEventListener("updateend", () => {
41
+ resolve();
42
+ });
43
+ this.sourceBuffer.remove(0, this.mediaSource.duration);
44
+ } else {
45
+ reject();
46
+ }
47
+ });
48
+ });
49
+ }
50
+
51
+ private createOperationQueue() {
52
+ return async.queue<() => Promise<void>>((task, callback) => {
53
+ task().then(() => callback());
54
+ }, 1);
55
+ }
56
+ }
57
+
58
+ export default AtomicMediaSource;
@@ -0,0 +1,10 @@
1
+ declare class AtomicMediaSource {
2
+ mediaSource: MediaSource;
3
+ private sourceBuffer;
4
+ private operationsQueue;
5
+ constructor();
6
+ appendBuffer(buffer: ArrayBuffer): void;
7
+ clearBuffer(): void;
8
+ private createOperationQueue;
9
+ }
10
+ export default AtomicMediaSource;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const async_1 = __importDefault(require("async"));
7
+ class AtomicMediaSource {
8
+ constructor() {
9
+ this.sourceBuffer = null;
10
+ this.mediaSource = new MediaSource();
11
+ this.operationsQueue = this.createOperationQueue();
12
+ this.mediaSource.addEventListener("sourceopen", () => {
13
+ this.mediaSource.duration = Number.POSITIVE_INFINITY;
14
+ this.sourceBuffer = this.mediaSource.addSourceBuffer('audio/webm; codecs="opus"');
15
+ this.sourceBuffer.mode = "sequence";
16
+ });
17
+ }
18
+ appendBuffer(buffer) {
19
+ this.operationsQueue.push(() => {
20
+ return new Promise((resolve, reject) => {
21
+ if (this.sourceBuffer) {
22
+ this.sourceBuffer.addEventListener("updateend", () => {
23
+ resolve();
24
+ });
25
+ this.sourceBuffer.appendBuffer(buffer);
26
+ }
27
+ else {
28
+ reject();
29
+ }
30
+ });
31
+ });
32
+ }
33
+ clearBuffer() {
34
+ this.operationsQueue.push(() => {
35
+ return new Promise((resolve, reject) => {
36
+ if (this.sourceBuffer) {
37
+ this.sourceBuffer.addEventListener("updateend", () => {
38
+ resolve();
39
+ });
40
+ this.sourceBuffer.remove(0, this.mediaSource.duration);
41
+ }
42
+ else {
43
+ reject();
44
+ }
45
+ });
46
+ });
47
+ }
48
+ createOperationQueue() {
49
+ return async_1.default.queue((task, callback) => {
50
+ task().then(() => callback());
51
+ }, 1);
52
+ }
53
+ }
54
+ exports.default = AtomicMediaSource;
package/dist/player.d.ts CHANGED
@@ -1,18 +1,10 @@
1
1
  /// <reference types="node" />
2
- import async from "async";
3
2
  import EventEmitter from "events";
4
3
  export declare class ContinuousPlayer extends EventEmitter {
5
- private mediaSource;
6
- private sourceBuffer;
4
+ private atomic;
7
5
  private audio;
8
- private operationsQueue;
9
6
  constructor();
10
- createOperationQueue(): async.QueueObject<() => Promise<void>>;
11
7
  start(): void;
12
8
  playChunk(audioData: ArrayBuffer): void;
13
- private performBufferOperation;
14
- private appendNextChunk;
15
- private removeBuffer;
16
- private resetTimestampOffset;
17
9
  clear(): void;
18
10
  }
package/dist/player.js CHANGED
@@ -4,88 +4,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ContinuousPlayer = void 0;
7
- const async_1 = __importDefault(require("async"));
7
+ const AtomicMediaSource_1 = __importDefault(require("./AtomicMediaSource"));
8
8
  const events_1 = __importDefault(require("events"));
9
9
  class ContinuousPlayer extends events_1.default {
10
10
  constructor() {
11
11
  super();
12
- this.sourceBuffer = null;
13
- this.mediaSource = new MediaSource();
12
+ this.atomic = new AtomicMediaSource_1.default();
14
13
  this.audio = document.createElement("audio");
15
14
  this.audio.addEventListener("playing", () => this.emit("speech-start"));
16
15
  this.audio.addEventListener("play", () => this.emit("speech-start"));
17
16
  this.audio.addEventListener("waiting", () => this.emit("speech-end"));
18
- this.audio.src = URL.createObjectURL(this.mediaSource);
17
+ this.audio.src = URL.createObjectURL(this.atomic.mediaSource);
19
18
  document.body.appendChild(this.audio);
20
- this.operationsQueue = this.createOperationQueue();
21
- this.mediaSource.addEventListener("sourceopen", () => {
22
- this.mediaSource.duration = Number.POSITIVE_INFINITY;
23
- this.sourceBuffer = this.mediaSource.addSourceBuffer('audio/webm; codecs="opus"');
24
- this.sourceBuffer.mode = "sequence";
25
- });
26
- }
27
- createOperationQueue() {
28
- return async_1.default.queue((task, callback) => {
29
- task().then(() => callback());
30
- }, 1);
31
19
  }
32
20
  start() {
33
21
  this.audio.play();
34
22
  }
35
23
  playChunk(audioData) {
36
- console.log("playChunk");
37
- this.operationsQueue.push(() => this.appendNextChunk(audioData));
38
- }
39
- performBufferOperation(operation) {
40
- return new Promise((resolve) => {
41
- if (!this.sourceBuffer) {
42
- resolve();
43
- return;
44
- }
45
- if (this.sourceBuffer.updating) {
46
- this.sourceBuffer.addEventListener("updateend", () => {
47
- operation();
48
- resolve();
49
- }, {
50
- once: true,
51
- });
52
- return;
53
- }
54
- operation();
55
- resolve();
56
- });
57
- }
58
- appendNextChunk(chunk) {
59
- return this.performBufferOperation(() => {
60
- if (!this.sourceBuffer)
61
- return;
62
- console.log("appending");
63
- this.sourceBuffer.appendBuffer(chunk);
64
- });
65
- }
66
- removeBuffer() {
67
- return this.performBufferOperation(() => {
68
- if (!this.sourceBuffer)
69
- return;
70
- if (this.sourceBuffer.buffered.length > 0) {
71
- this.sourceBuffer.remove(0, this.sourceBuffer.buffered.end(this.sourceBuffer.buffered.length - 1));
72
- }
73
- });
74
- }
75
- resetTimestampOffset() {
76
- return this.performBufferOperation(() => {
77
- if (!this.sourceBuffer)
78
- return;
79
- this.sourceBuffer.timestampOffset = 0;
80
- });
24
+ this.atomic.appendBuffer(audioData);
81
25
  }
82
26
  clear() {
83
- // if (!this.sourceBuffer) return;
84
- // this.sourceBuffer.abort();
85
- // this.operationsQueue.kill();
86
- // this.operationsQueue = this.createOperationQueue();
87
- // this.operationsQueue.push(() => this.removeBuffer());
88
- // this.operationsQueue.push(() => this.resetTimestampOffset());
27
+ this.atomic.clearBuffer();
89
28
  }
90
29
  }
91
30
  exports.ContinuousPlayer = ContinuousPlayer;
package/dist/vapi.js CHANGED
@@ -35,8 +35,6 @@ class Vapi extends events_1.default {
35
35
  this.ws = socket;
36
36
  socket.onopen = () => {
37
37
  socket.send(JSON.stringify({ event: "start", callId }));
38
- this.startRecording();
39
- this.emit("started");
40
38
  };
41
39
  socket.onmessage = (event) => {
42
40
  if (!socket)
@@ -68,12 +66,17 @@ class Vapi extends events_1.default {
68
66
  }
69
67
  onMessage(ws, event) {
70
68
  const data = JSON.parse(event.data);
71
- if (data.event === "media") {
72
- const audioData = (0, base64_arraybuffer_1.decode)(data.media.payload);
73
- this.player.playChunk(audioData);
74
- }
75
- if (data.event === "clear") {
76
- this.clear();
69
+ switch (data.event) {
70
+ case "connected":
71
+ this.startRecording();
72
+ this.emit("started");
73
+ break;
74
+ case "clear":
75
+ this.clear();
76
+ break;
77
+ case "media":
78
+ const audioData = (0, base64_arraybuffer_1.decode)(data.media.payload);
79
+ this.player.playChunk(audioData);
77
80
  }
78
81
  }
79
82
  clear() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vapi-ai/web",
3
- "version": "1.0.105",
3
+ "version": "1.0.107",
4
4
  "description": "",
5
5
  "main": "dist/vapi.js",
6
6
  "types": "dist/vapi.d.ts",
package/player.ts CHANGED
@@ -1,42 +1,22 @@
1
- import async, { QueueObject } from "async";
2
-
1
+ import AtomicMediaSource from "./AtomicMediaSource";
3
2
  import EventEmitter from "events";
4
3
 
5
4
  export class ContinuousPlayer extends EventEmitter {
6
- private mediaSource: MediaSource;
7
- private sourceBuffer: SourceBuffer | null = null;
5
+ private atomic: AtomicMediaSource;
8
6
  private audio: HTMLAudioElement;
9
- private operationsQueue: QueueObject<() => Promise<void>>;
10
7
 
11
8
  constructor() {
12
9
  super();
13
- this.mediaSource = new MediaSource();
10
+ this.atomic = new AtomicMediaSource();
14
11
 
15
12
  this.audio = document.createElement("audio");
16
13
 
17
14
  this.audio.addEventListener("playing", () => this.emit("speech-start"));
18
15
  this.audio.addEventListener("play", () => this.emit("speech-start"));
19
-
20
16
  this.audio.addEventListener("waiting", () => this.emit("speech-end"));
21
17
 
22
- this.audio.src = URL.createObjectURL(this.mediaSource);
18
+ this.audio.src = URL.createObjectURL(this.atomic.mediaSource);
23
19
  document.body.appendChild(this.audio);
24
-
25
- this.operationsQueue = this.createOperationQueue();
26
-
27
- this.mediaSource.addEventListener("sourceopen", () => {
28
- this.mediaSource.duration = Number.POSITIVE_INFINITY;
29
- this.sourceBuffer = this.mediaSource.addSourceBuffer(
30
- 'audio/webm; codecs="opus"'
31
- );
32
- this.sourceBuffer.mode = "sequence";
33
- });
34
- }
35
-
36
- createOperationQueue() {
37
- return async.queue<() => Promise<void>>((task, callback) => {
38
- task().then(() => callback());
39
- }, 1);
40
20
  }
41
21
 
42
22
  start() {
@@ -44,71 +24,10 @@ export class ContinuousPlayer extends EventEmitter {
44
24
  }
45
25
 
46
26
  playChunk(audioData: ArrayBuffer) {
47
- console.log("playChunk");
48
- this.operationsQueue.push(() => this.appendNextChunk(audioData));
49
- }
50
-
51
- private performBufferOperation(operation: () => void) {
52
- return new Promise<void>((resolve) => {
53
- if (!this.sourceBuffer) {
54
- resolve();
55
- return;
56
- }
57
-
58
- if (this.sourceBuffer.updating) {
59
- this.sourceBuffer.addEventListener(
60
- "updateend",
61
- () => {
62
- operation();
63
- resolve();
64
- },
65
- {
66
- once: true,
67
- }
68
- );
69
- return;
70
- }
71
-
72
- operation();
73
- resolve();
74
- });
75
- }
76
-
77
- private appendNextChunk(chunk: ArrayBuffer) {
78
- return this.performBufferOperation(() => {
79
- if (!this.sourceBuffer) return;
80
- console.log("appending");
81
-
82
- this.sourceBuffer.appendBuffer(chunk);
83
- });
84
- }
85
-
86
- private removeBuffer() {
87
- return this.performBufferOperation(() => {
88
- if (!this.sourceBuffer) return;
89
-
90
- if (this.sourceBuffer.buffered.length > 0) {
91
- this.sourceBuffer.remove(
92
- 0,
93
- this.sourceBuffer.buffered.end(this.sourceBuffer.buffered.length - 1)
94
- );
95
- }
96
- });
97
- }
98
-
99
- private resetTimestampOffset() {
100
- return this.performBufferOperation(() => {
101
- if (!this.sourceBuffer) return;
102
- this.sourceBuffer.timestampOffset = 0;
103
- });
27
+ this.atomic.appendBuffer(audioData);
104
28
  }
105
29
 
106
30
  clear() {
107
- // if (!this.sourceBuffer) return;
108
- // this.sourceBuffer.abort();
109
- // this.operationsQueue.kill();
110
- // this.operationsQueue = this.createOperationQueue();
111
- // this.operationsQueue.push(() => this.removeBuffer());
112
- // this.operationsQueue.push(() => this.resetTimestampOffset());
31
+ this.atomic.clearBuffer();
113
32
  }
114
33
  }
package/vapi.ts CHANGED
@@ -40,8 +40,6 @@ export default class Vapi extends EventEmitter {
40
40
 
41
41
  socket.onopen = () => {
42
42
  socket.send(JSON.stringify({ event: "start", callId }));
43
- this.startRecording();
44
- this.emit("started");
45
43
  };
46
44
  socket.onmessage = (event) => {
47
45
  if (!socket) return;
@@ -74,12 +72,18 @@ export default class Vapi extends EventEmitter {
74
72
 
75
73
  private onMessage(ws: WebSocket, event: MessageEvent): void {
76
74
  const data = JSON.parse(event.data);
77
- if (data.event === "media") {
78
- const audioData = decode(data.media.payload);
79
- this.player.playChunk(audioData);
80
- }
81
- if (data.event === "clear") {
82
- this.clear();
75
+
76
+ switch (data.event) {
77
+ case "connected":
78
+ this.startRecording();
79
+ this.emit("started");
80
+ break;
81
+ case "clear":
82
+ this.clear();
83
+ break;
84
+ case "media":
85
+ const audioData = decode(data.media.payload);
86
+ this.player.playChunk(audioData);
83
87
  }
84
88
  }
85
89