@nuancelabs/js-sdk 0.2.0 → 0.2.2
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/index.js +83 -6
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -4,7 +4,7 @@ function isRecord(value) {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
// src/core/sdk-version.ts
|
|
7
|
-
var SDK_HEADER = `js/${"0.2.
|
|
7
|
+
var SDK_HEADER = `js/${"0.2.2"}`;
|
|
8
8
|
|
|
9
9
|
// src/utils.ts
|
|
10
10
|
function normalizeHost(host) {
|
|
@@ -1167,6 +1167,15 @@ function microphonePublishOptions(resilient) {
|
|
|
1167
1167
|
return;
|
|
1168
1168
|
return { dtx: false, audioPreset: AudioPresets.speech };
|
|
1169
1169
|
}
|
|
1170
|
+
var SCREEN_CAPTURE = {
|
|
1171
|
+
audio: false,
|
|
1172
|
+
resolution: { width: 1920, height: 1080, frameRate: 2 },
|
|
1173
|
+
contentHint: "detail"
|
|
1174
|
+
};
|
|
1175
|
+
var SCREEN_PUBLISH = {
|
|
1176
|
+
screenShareEncoding: { maxBitrate: 1500000, maxFramerate: 2 },
|
|
1177
|
+
simulcast: false
|
|
1178
|
+
};
|
|
1170
1179
|
|
|
1171
1180
|
class LiveKitUserParticipant {
|
|
1172
1181
|
room;
|
|
@@ -1174,6 +1183,8 @@ class LiveKitUserParticipant {
|
|
|
1174
1183
|
onSpeakingChanged;
|
|
1175
1184
|
onMicrophoneChanged;
|
|
1176
1185
|
onCameraChanged;
|
|
1186
|
+
onScreenShareChanged;
|
|
1187
|
+
screenTrack;
|
|
1177
1188
|
inputs = {
|
|
1178
1189
|
audioinput: { changing: false, unavailable: false },
|
|
1179
1190
|
videoinput: { changing: false, unavailable: false }
|
|
@@ -1188,7 +1199,7 @@ class LiveKitUserParticipant {
|
|
|
1188
1199
|
lastAudioSource = null;
|
|
1189
1200
|
_isSpeaking = false;
|
|
1190
1201
|
speakingHandler = null;
|
|
1191
|
-
constructor(room, onTracksChanged, onSpeakingChanged, onMicrophoneChanged, onCameraChanged = () => {}, initialCameraFacing, resilient = false) {
|
|
1202
|
+
constructor(room, onTracksChanged, onSpeakingChanged, onMicrophoneChanged, onCameraChanged = () => {}, initialCameraFacing, resilient = false, onScreenShareChanged = () => {}) {
|
|
1192
1203
|
this.room = room;
|
|
1193
1204
|
this.onTracksChanged = onTracksChanged;
|
|
1194
1205
|
this.onSpeakingChanged = onSpeakingChanged;
|
|
@@ -1196,7 +1207,9 @@ class LiveKitUserParticipant {
|
|
|
1196
1207
|
this.onCameraChanged = onCameraChanged;
|
|
1197
1208
|
this.initialCameraFacing = initialCameraFacing;
|
|
1198
1209
|
this.microphoneOptions = microphonePublishOptions(resilient);
|
|
1210
|
+
this.onScreenShareChanged = onScreenShareChanged;
|
|
1199
1211
|
this.room.on(RoomEvent2.ActiveDeviceChanged, this.handleDeviceChanged);
|
|
1212
|
+
this.room.localParticipant.on(ParticipantEvent2.LocalTrackUnpublished, this.handleTrackUnpublished);
|
|
1200
1213
|
}
|
|
1201
1214
|
updateTracks() {
|
|
1202
1215
|
let changed = false;
|
|
@@ -1357,9 +1370,14 @@ class LiveKitUserParticipant {
|
|
|
1357
1370
|
this.notifyInput(kind);
|
|
1358
1371
|
}
|
|
1359
1372
|
};
|
|
1373
|
+
handleTrackUnpublished = (publication) => {
|
|
1374
|
+
if (!this.disposed && publication.source === Track5.Source.ScreenShare)
|
|
1375
|
+
this.onScreenShareChanged();
|
|
1376
|
+
};
|
|
1360
1377
|
cleanup() {
|
|
1361
1378
|
this.disposed = true;
|
|
1362
1379
|
this.room.off(RoomEvent2.ActiveDeviceChanged, this.handleDeviceChanged);
|
|
1380
|
+
this.room.localParticipant.off(ParticipantEvent2.LocalTrackUnpublished, this.handleTrackUnpublished);
|
|
1363
1381
|
if (this.inputs.audioinput.changing)
|
|
1364
1382
|
this.inputTrack("audioinput")?.stop();
|
|
1365
1383
|
if (this.inputs.videoinput.changing)
|
|
@@ -1437,10 +1455,44 @@ class LiveKitUserParticipant {
|
|
|
1437
1455
|
get isVideoEnabled() {
|
|
1438
1456
|
return this.room.localParticipant.isCameraEnabled;
|
|
1439
1457
|
}
|
|
1458
|
+
async startScreenShare() {
|
|
1459
|
+
await this.setScreenShare(true);
|
|
1460
|
+
}
|
|
1461
|
+
async stopScreenShare() {
|
|
1462
|
+
const track = this.screenTrack;
|
|
1463
|
+
this.screenTrack = undefined;
|
|
1464
|
+
try {
|
|
1465
|
+
if (!this.disposed)
|
|
1466
|
+
await this.setScreenShare(false);
|
|
1467
|
+
} finally {
|
|
1468
|
+
track?.stop();
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
get isScreenSharing() {
|
|
1472
|
+
return this.room.localParticipant.isScreenShareEnabled;
|
|
1473
|
+
}
|
|
1474
|
+
async setScreenShare(enabled) {
|
|
1475
|
+
if (this.disposed)
|
|
1476
|
+
throw new Error("Participant is disconnected");
|
|
1477
|
+
try {
|
|
1478
|
+
const publication = await this.room.localParticipant.setScreenShareEnabled(enabled, SCREEN_CAPTURE, SCREEN_PUBLISH);
|
|
1479
|
+
if (this.disposed)
|
|
1480
|
+
publication?.track?.stop();
|
|
1481
|
+
else if (enabled)
|
|
1482
|
+
this.screenTrack = publication?.track;
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
throw new MediaAccessError(error);
|
|
1485
|
+
} finally {
|
|
1486
|
+
if (!this.disposed)
|
|
1487
|
+
this.onScreenShareChanged();
|
|
1488
|
+
}
|
|
1489
|
+
}
|
|
1440
1490
|
releaseDevices() {
|
|
1441
1491
|
for (const pub of this.room.localParticipant.trackPublications.values()) {
|
|
1442
1492
|
pub.track?.stop();
|
|
1443
1493
|
}
|
|
1494
|
+
this.screenTrack?.stop();
|
|
1495
|
+
this.screenTrack = undefined;
|
|
1444
1496
|
}
|
|
1445
1497
|
startSpeakingDetection() {
|
|
1446
1498
|
this.stopSpeakingDetection();
|
|
@@ -1601,7 +1653,7 @@ class LiveKitConversation {
|
|
|
1601
1653
|
type: "speaking-changed",
|
|
1602
1654
|
participant: "user",
|
|
1603
1655
|
isSpeaking
|
|
1604
|
-
}), () => this.emit({ type: "microphone-changed" }), () => this.emit({ type: "camera-changed" }), this.cameraFacing, this.resilient);
|
|
1656
|
+
}), () => this.emit({ type: "microphone-changed" }), () => this.emit({ type: "camera-changed" }), this.cameraFacing, this.resilient, () => this.emit({ type: "screen-share-changed" }));
|
|
1605
1657
|
this._user.startSpeakingDetection();
|
|
1606
1658
|
try {
|
|
1607
1659
|
await this._user.unmuteAudio();
|
|
@@ -1677,7 +1729,7 @@ class LiveKitConversation {
|
|
|
1677
1729
|
}
|
|
1678
1730
|
onChange(handler) {
|
|
1679
1731
|
return this.onEvent((event) => {
|
|
1680
|
-
if (event.type !== "data-event")
|
|
1732
|
+
if (event.type !== "data-event" && event.type !== "screen-share-changed")
|
|
1681
1733
|
handler(event);
|
|
1682
1734
|
});
|
|
1683
1735
|
}
|
|
@@ -2086,6 +2138,8 @@ var DEFAULT_HOST = "https://api.nuancelabs.ai";
|
|
|
2086
2138
|
var QUEUE_HEARTBEAT_INTERVAL_MS = 5000;
|
|
2087
2139
|
var HEARTBEAT_INTERVAL_MS = 5000;
|
|
2088
2140
|
var ADMISSION_FAILED_BEATS = 6;
|
|
2141
|
+
var BUSY_RETRY_LIMIT_MS = 60000;
|
|
2142
|
+
var BUSY_RETRY_DEFAULT_MS = 3000;
|
|
2089
2143
|
var LOST_WINDOW_MS = 30000;
|
|
2090
2144
|
function createNuanceConversation(options = {}) {
|
|
2091
2145
|
return new NuanceConversation(options);
|
|
@@ -2115,6 +2169,9 @@ function isLegalRequiredBody(body) {
|
|
|
2115
2169
|
function isMaintenanceBody(body) {
|
|
2116
2170
|
return isRecord(body) && body.code === "maintenance";
|
|
2117
2171
|
}
|
|
2172
|
+
function isBusyBody(body) {
|
|
2173
|
+
return isRecord(body) && body.code === "busy";
|
|
2174
|
+
}
|
|
2118
2175
|
function isCurfewBody(body) {
|
|
2119
2176
|
return isRecord(body) && body.code === "policy_curfew";
|
|
2120
2177
|
}
|
|
@@ -2212,7 +2269,7 @@ class NuanceConversation {
|
|
|
2212
2269
|
request.personality = this.personality;
|
|
2213
2270
|
if (this.variant)
|
|
2214
2271
|
request.variant = this.variant;
|
|
2215
|
-
conversation = await this.
|
|
2272
|
+
conversation = await this.createWhenNotBusy(request, attempt);
|
|
2216
2273
|
} catch (error) {
|
|
2217
2274
|
if (error instanceof ApiError && error.status === 409) {
|
|
2218
2275
|
throw new ActiveSessionError;
|
|
@@ -2316,7 +2373,7 @@ class NuanceConversation {
|
|
|
2316
2373
|
}
|
|
2317
2374
|
onChange(handler) {
|
|
2318
2375
|
return this.onEvent((event) => {
|
|
2319
|
-
if (event.type !== "data-event")
|
|
2376
|
+
if (event.type !== "data-event" && event.type !== "screen-share-changed")
|
|
2320
2377
|
handler(event);
|
|
2321
2378
|
});
|
|
2322
2379
|
}
|
|
@@ -2354,6 +2411,26 @@ class NuanceConversation {
|
|
|
2354
2411
|
resilientReconnect: this.resilient
|
|
2355
2412
|
});
|
|
2356
2413
|
}
|
|
2414
|
+
async createWhenNotBusy(request, attempt) {
|
|
2415
|
+
const giveUpAt = Date.now() + BUSY_RETRY_LIMIT_MS;
|
|
2416
|
+
for (;; ) {
|
|
2417
|
+
try {
|
|
2418
|
+
return await this.api.createConversation(request);
|
|
2419
|
+
} catch (error) {
|
|
2420
|
+
if (!(error instanceof ApiError) || error.status !== 503 || !isBusyBody(error.body) || attempt !== this.attempt) {
|
|
2421
|
+
throw error;
|
|
2422
|
+
}
|
|
2423
|
+
const waitMs = typeof error.body.retry_after_seconds === "number" ? error.body.retry_after_seconds * 1000 : BUSY_RETRY_DEFAULT_MS;
|
|
2424
|
+
if (Date.now() + waitMs > giveUpAt)
|
|
2425
|
+
throw error;
|
|
2426
|
+
if (this._state !== "queued")
|
|
2427
|
+
this.setState("queued");
|
|
2428
|
+
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
2429
|
+
if (attempt !== this.attempt)
|
|
2430
|
+
throw error;
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
}
|
|
2357
2434
|
startHeartbeat(conversationId, intervalMs) {
|
|
2358
2435
|
this.stopHeartbeat();
|
|
2359
2436
|
this.failedBeats = 0;
|