@defcomm/meetingsdk 0.6.0 → 0.6.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/index.js CHANGED
@@ -24,7 +24,6 @@ __export(index_exports, {
24
24
  MeetingProvider: () => MeetingProvider,
25
25
  MeetingState: () => MeetingState,
26
26
  VideoSDKCore: () => VideoSDKCore,
27
- useLiveTranscript: () => useLiveTranscript,
28
27
  useLocalParticipant: () => useLocalParticipant,
29
28
  useMeeting: () => useMeeting,
30
29
  useMeetingContext: () => useMeetingContext,
@@ -229,7 +228,6 @@ var MeetingState = class {
229
228
  this.presenterId = null;
230
229
  this.activeSpeakerIds = /* @__PURE__ */ new Set();
231
230
  this.recording = null;
232
- this.liveTranscripts = [];
233
231
  this.networkQuality = null;
234
232
  this.listeners = /* @__PURE__ */ new Map();
235
233
  }
@@ -268,17 +266,6 @@ var MeetingState = class {
268
266
  this.recording = null;
269
267
  this.notify("recording");
270
268
  }
271
- addLiveTranscript(transcript) {
272
- this.liveTranscripts = [...this.liveTranscripts.slice(-499), transcript];
273
- this.notify("transcript");
274
- }
275
- getLiveTranscripts() {
276
- return this.liveTranscripts;
277
- }
278
- clearLiveTranscripts() {
279
- this.liveTranscripts = [];
280
- this.notify("transcript");
281
- }
282
269
  setNetworkQuality(quality) {
283
270
  this.networkQuality = quality;
284
271
  this.notify("networkQuality");
@@ -403,13 +390,11 @@ var MeetingState = class {
403
390
  this.chatMessages.clear();
404
391
  this.presenterId = null;
405
392
  this.activeSpeakerIds = /* @__PURE__ */ new Set();
406
- this.liveTranscripts = [];
407
393
  this.networkQuality = null;
408
394
  this.notify("participants");
409
395
  this.notify("chat");
410
396
  this.notify("presenter");
411
397
  this.notify("activeSpeakers");
412
- this.notify("transcript");
413
398
  this.notify("networkQuality");
414
399
  }
415
400
  };
@@ -472,7 +457,6 @@ var SFUClient = class {
472
457
  this.trackToSource = /* @__PURE__ */ new Map();
473
458
  this.localTrackIds = /* @__PURE__ */ new Map();
474
459
  this.subscriberStatsTimer = null;
475
- this.liveTranscriptionSeen = false;
476
460
  this.networkProfile = null;
477
461
  this.heartbeatTimer = null;
478
462
  this.apiBase = options.apiBase || "https://api.example.com/api";
@@ -530,8 +514,6 @@ var SFUClient = class {
530
514
  });
531
515
  this.onRoomUpdate = options.onRoomUpdate || (() => {
532
516
  });
533
- this.onLiveTranscript = options.onLiveTranscript || (() => {
534
- });
535
517
  this.onNetworkQuality = options.onNetworkQuality || (() => {
536
518
  });
537
519
  this.onParticipantMediaState = options.onParticipantMediaState || (() => {
@@ -1128,24 +1110,6 @@ var SFUClient = class {
1128
1110
  if (reaction?.id && reaction?.emoji) this.onReaction(reaction);
1129
1111
  break;
1130
1112
  }
1131
- case "live_transcript": {
1132
- const transcript = {
1133
- transcript_id: String(payload.transcript_id),
1134
- participant_id: String(payload.participant_id),
1135
- speaker_name: payload.speaker_name ?? null,
1136
- sequence: Number(payload.sequence ?? 0),
1137
- text: String(payload.text ?? ""),
1138
- offset_ms: payload.offset_ms == null ? null : Number(payload.offset_ms),
1139
- start_ms: payload.start_ms == null ? null : Number(payload.start_ms),
1140
- end_ms: payload.end_ms == null ? null : Number(payload.end_ms),
1141
- timestamp: String(payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString())
1142
- };
1143
- if (transcript.text.trim()) {
1144
- this.liveTranscriptionSeen = true;
1145
- this.onLiveTranscript(transcript);
1146
- }
1147
- break;
1148
- }
1149
1113
  case "network_quality": {
1150
1114
  const raw = payload.quality || payload;
1151
1115
  const quality = {
@@ -1603,6 +1567,78 @@ var SFUClient = class {
1603
1567
  this.subscriptionRunning = false;
1604
1568
  }
1605
1569
  }
1570
+ /**
1571
+ * Enumerate media inputs and outputs exposed by the browser/OS.
1572
+ * Labels may be empty until the user has granted media permission.
1573
+ */
1574
+ async getMediaDevices() {
1575
+ if (!navigator.mediaDevices?.enumerateDevices) {
1576
+ throw new Error("Media device enumeration is not supported on this device");
1577
+ }
1578
+ return navigator.mediaDevices.enumerateDevices();
1579
+ }
1580
+ /**
1581
+ * Replace the published camera without leaving the meeting.
1582
+ * The browser/OS must expose the target as a videoinput device.
1583
+ */
1584
+ async selectCamera(deviceId) {
1585
+ if (!deviceId) throw new Error("Camera deviceId is required");
1586
+ if (!navigator.mediaDevices?.getUserMedia) {
1587
+ throw new Error("Camera access is not supported on this device");
1588
+ }
1589
+ const stream = await navigator.mediaDevices.getUserMedia({
1590
+ video: { deviceId: { exact: deviceId } },
1591
+ audio: false
1592
+ });
1593
+ const nextTrack = stream.getVideoTracks()[0];
1594
+ if (!nextTrack) {
1595
+ stream.getTracks().forEach((track) => track.stop());
1596
+ throw new Error("Selected camera returned no video track");
1597
+ }
1598
+ const currentTrack = this.localStream?.getVideoTracks()[0] ?? null;
1599
+ nextTrack.enabled = currentTrack?.enabled ?? true;
1600
+ if (!this.localStream) this.localStream = new MediaStream();
1601
+ if (currentTrack) this.localStream.removeTrack(currentTrack);
1602
+ this.localStream.addTrack(nextTrack);
1603
+ if (this.videoSender) {
1604
+ await this.videoSender.replaceTrack(nextTrack);
1605
+ }
1606
+ currentTrack?.stop();
1607
+ return nextTrack;
1608
+ }
1609
+ /**
1610
+ * Replace the published microphone without leaving the meeting.
1611
+ */
1612
+ async selectMicrophone(deviceId) {
1613
+ if (!deviceId) throw new Error("Microphone deviceId is required");
1614
+ if (!navigator.mediaDevices?.getUserMedia) {
1615
+ throw new Error("Microphone access is not supported on this device");
1616
+ }
1617
+ const stream = await navigator.mediaDevices.getUserMedia({
1618
+ video: false,
1619
+ audio: {
1620
+ deviceId: { exact: deviceId },
1621
+ echoCancellation: true,
1622
+ noiseSuppression: true,
1623
+ autoGainControl: true
1624
+ }
1625
+ });
1626
+ const nextTrack = stream.getAudioTracks()[0];
1627
+ if (!nextTrack) {
1628
+ stream.getTracks().forEach((track) => track.stop());
1629
+ throw new Error("Selected microphone returned no audio track");
1630
+ }
1631
+ const currentTrack = this.localStream?.getAudioTracks()[0] ?? null;
1632
+ nextTrack.enabled = currentTrack?.enabled ?? true;
1633
+ if (!this.localStream) this.localStream = new MediaStream();
1634
+ if (currentTrack) this.localStream.removeTrack(currentTrack);
1635
+ this.localStream.addTrack(nextTrack);
1636
+ if (this.audioSender) {
1637
+ await this.audioSender.replaceTrack(nextTrack);
1638
+ }
1639
+ currentTrack?.stop();
1640
+ return nextTrack;
1641
+ }
1606
1642
  async publish({ audio = true, video = true } = {}) {
1607
1643
  if (!this.publisher)
1608
1644
  throw new Error("Publisher PeerConnection is not initialized");
@@ -1824,27 +1860,6 @@ var SFUClient = class {
1824
1860
  }
1825
1861
  });
1826
1862
  }
1827
- /**
1828
- * Transcription is server-authoritative. The SFU reads the already-published
1829
- * microphone RTP stream, sends audio to the configured transcription service,
1830
- * persists final segments, and broadcasts LiveTranscript events.
1831
- *
1832
- * These methods remain as compatibility shims for applications that used the
1833
- * previous browser MediaRecorder-based API. They intentionally do not capture
1834
- * or upload microphone audio from the browser.
1835
- */
1836
- async startLiveTranscription() {
1837
- if (!this.room) {
1838
- throw new Error("Cannot start live transcription without a room");
1839
- }
1840
- console.info("[Transcript] server-authoritative live transcription is managed by the SFU");
1841
- }
1842
- stopLiveTranscription() {
1843
- console.info("[Transcript] server-authoritative live transcription remains managed by the SFU");
1844
- }
1845
- isLiveTranscriptionActive() {
1846
- return this.liveTranscriptionSeen;
1847
- }
1848
1863
  async getTranscript(roomCode, sessionId) {
1849
1864
  if (!this.authToken) throw new Error("Authentication token is required");
1850
1865
  const query = sessionId ? `?session_id=${encodeURIComponent(sessionId)}` : "";
@@ -2041,7 +2056,6 @@ var SFUClient = class {
2041
2056
  }
2042
2057
  }
2043
2058
  async leave() {
2044
- this.stopLiveTranscription();
2045
2059
  this.stopHeartbeat();
2046
2060
  this.intentionalClose = true;
2047
2061
  this.setState("leaving");
@@ -2331,7 +2345,14 @@ var VideoSDKCore = class {
2331
2345
  }
2332
2346
  // JOIN
2333
2347
  async joinMeeting(config) {
2334
- const { roomId, name, audioMuted = false, videoMuted = false } = config;
2348
+ const {
2349
+ roomId,
2350
+ name,
2351
+ audioMuted = false,
2352
+ videoMuted = false,
2353
+ cameraDeviceId,
2354
+ microphoneDeviceId
2355
+ } = config;
2335
2356
  if (!roomId || !name) {
2336
2357
  throw new Error("roomId and name are required to join meeting");
2337
2358
  }
@@ -2366,8 +2387,13 @@ var VideoSDKCore = class {
2366
2387
  this.room.name = name;
2367
2388
  if (!this.localStream) {
2368
2389
  this.localStream = await navigator.mediaDevices.getUserMedia({
2369
- video: true,
2370
- audio: true
2390
+ video: cameraDeviceId ? { deviceId: { exact: cameraDeviceId } } : true,
2391
+ audio: microphoneDeviceId ? {
2392
+ deviceId: { exact: microphoneDeviceId },
2393
+ echoCancellation: true,
2394
+ noiseSuppression: true,
2395
+ autoGainControl: true
2396
+ } : true
2371
2397
  });
2372
2398
  const localAudioTrack = this.localStream.getAudioTracks()[0];
2373
2399
  if (localAudioTrack) {
@@ -2456,10 +2482,6 @@ var VideoSDKCore = class {
2456
2482
  console.warn(`[SFU] Reconnecting, attempt ${attempt}`);
2457
2483
  },
2458
2484
  // ADMISSION CONTROL
2459
- onLiveTranscript: (transcript) => {
2460
- this.state.addLiveTranscript(transcript);
2461
- this.events.onLiveTranscript?.(transcript);
2462
- },
2463
2485
  onNetworkQuality: (quality) => {
2464
2486
  this.state.setNetworkQuality(quality);
2465
2487
  this.events.onNetworkQuality?.(quality);
@@ -2959,6 +2981,39 @@ var VideoSDKCore = class {
2959
2981
  return this.sfuClient?.removeModerator(roomCode, userId);
2960
2982
  }
2961
2983
  // MEDIA CONTROL
2984
+ async getMediaDevices() {
2985
+ if (!this.sfuClient) {
2986
+ throw new Error("SFU client is not initialized");
2987
+ }
2988
+ return this.sfuClient.getMediaDevices();
2989
+ }
2990
+ async selectCamera(deviceId) {
2991
+ if (!this.sfuClient) {
2992
+ throw new Error("SFU client is not initialized");
2993
+ }
2994
+ const track = await this.sfuClient.selectCamera(deviceId);
2995
+ if (this.localStream) {
2996
+ this.state.localStream = this.localStream;
2997
+ this.updateLocalMedia();
2998
+ }
2999
+ return track;
3000
+ }
3001
+ async selectMicrophone(deviceId) {
3002
+ if (!this.sfuClient) {
3003
+ throw new Error("SFU client is not initialized");
3004
+ }
3005
+ const track = await this.sfuClient.selectMicrophone(deviceId);
3006
+ if (this.localStream) {
3007
+ const mic = this.localStream.getAudioTracks()[0];
3008
+ if (mic) {
3009
+ this.activeSpeakerDetector.removeParticipant(this.myId);
3010
+ this.activeSpeakerDetector.addTrack(this.myId, mic);
3011
+ }
3012
+ this.state.localStream = this.localStream;
3013
+ this.updateLocalMedia();
3014
+ }
3015
+ return track;
3016
+ }
2962
3017
  toggleMic() {
2963
3018
  const mediaState = this.state.localParticipant?.media;
2964
3019
  if (!mediaState || !this.localStream) {
@@ -3108,22 +3163,6 @@ var VideoSDKCore = class {
3108
3163
  }
3109
3164
  }
3110
3165
  }
3111
- async startLiveTranscription() {
3112
- if (!this.sfuClient) {
3113
- throw new Error("SFU client is not connected");
3114
- }
3115
- const audioTrack = this.localStream?.getAudioTracks()[0];
3116
- if (!audioTrack || audioTrack.readyState !== "live") {
3117
- throw new Error("Microphone audio track is not available");
3118
- }
3119
- await this.sfuClient.startLiveTranscription();
3120
- }
3121
- stopLiveTranscription() {
3122
- this.sfuClient?.stopLiveTranscription();
3123
- }
3124
- isLiveTranscriptionActive() {
3125
- return this.sfuClient?.isLiveTranscriptionActive() ?? false;
3126
- }
3127
3166
  getNetworkQuality() {
3128
3167
  return this.state.networkQuality;
3129
3168
  }
@@ -3175,7 +3214,6 @@ var VideoSDKCore = class {
3175
3214
  this.room.id = null;
3176
3215
  this.room.name = null;
3177
3216
  this.state.localParticipant = null;
3178
- this.state.clearLiveTranscripts();
3179
3217
  this.state.setNetworkQuality(null);
3180
3218
  this.state.notify("localParticipant");
3181
3219
  this.state.resetRemoteState();
@@ -3230,9 +3268,6 @@ var MeetingProvider = ({
3230
3268
  const activeSpeakerListeners = (0, import_react2.useRef)(
3231
3269
  /* @__PURE__ */ new Set()
3232
3270
  );
3233
- const liveTranscriptListeners = (0, import_react2.useRef)(
3234
- /* @__PURE__ */ new Set()
3235
- );
3236
3271
  const networkQualityListeners = (0, import_react2.useRef)(
3237
3272
  /* @__PURE__ */ new Set()
3238
3273
  );
@@ -3319,9 +3354,6 @@ var MeetingProvider = ({
3319
3354
  );
3320
3355
  pubSubListeners.current.get("REACTION")?.forEach((fn) => fn(message));
3321
3356
  },
3322
- onLiveTranscript: (transcript) => {
3323
- liveTranscriptListeners.current.forEach((fn) => fn(transcript));
3324
- },
3325
3357
  onNetworkQuality: (quality) => {
3326
3358
  networkQualityListeners.current.forEach((fn) => fn(quality));
3327
3359
  },
@@ -3360,11 +3392,6 @@ var MeetingProvider = ({
3360
3392
  "chat",
3361
3393
  (s) => s.getChatMessages()
3362
3394
  );
3363
- const liveTranscripts = useMeetingStore(
3364
- sdk.state,
3365
- "transcript",
3366
- (s) => s.getLiveTranscripts()
3367
- );
3368
3395
  const networkQuality = useMeetingStore(
3369
3396
  sdk.state,
3370
3397
  "networkQuality",
@@ -3382,6 +3409,9 @@ var MeetingProvider = ({
3382
3409
  createRoom: sdk.createRoom.bind(sdk),
3383
3410
  getRoom: sdk.getRoom.bind(sdk),
3384
3411
  deleteRoom: sdk.deleteRoom.bind(sdk),
3412
+ getMediaDevices: sdk.getMediaDevices.bind(sdk),
3413
+ selectCamera: sdk.selectCamera.bind(sdk),
3414
+ selectMicrophone: sdk.selectMicrophone.bind(sdk),
3385
3415
  getTranscript: sdk.getTranscript.bind(sdk),
3386
3416
  getMeetingSummary: sdk.getMeetingSummary.bind(sdk),
3387
3417
  toggleMic: sdk.toggleMic.bind(sdk),
@@ -3396,11 +3426,7 @@ var MeetingProvider = ({
3396
3426
  messages,
3397
3427
  presenterId,
3398
3428
  activeSpeakerIds,
3399
- liveTranscripts,
3400
3429
  networkQuality,
3401
- startLiveTranscription: sdk.startLiveTranscription.bind(sdk),
3402
- stopLiveTranscription: sdk.stopLiveTranscription.bind(sdk),
3403
- isLiveTranscriptionActive: sdk.isLiveTranscriptionActive.bind(sdk),
3404
3430
  recording,
3405
3431
  usePubSub: (topic) => {
3406
3432
  if (topic === "SECURE_CHAT") {
@@ -3519,10 +3545,6 @@ var MeetingProvider = ({
3519
3545
  reactionListeners.current.add(cb);
3520
3546
  return () => reactionListeners.current.delete(cb);
3521
3547
  },
3522
- onLiveTranscript: (cb) => {
3523
- liveTranscriptListeners.current.add(cb);
3524
- return () => liveTranscriptListeners.current.delete(cb);
3525
- },
3526
3548
  onNetworkQuality: (cb) => {
3527
3549
  networkQualityListeners.current.add(cb);
3528
3550
  return () => networkQualityListeners.current.delete(cb);
@@ -3549,7 +3571,6 @@ var MeetingProvider = ({
3549
3571
  presenterId,
3550
3572
  recording,
3551
3573
  activeSpeakerIds,
3552
- liveTranscripts,
3553
3574
  networkQuality,
3554
3575
  reactionPubSubMessages
3555
3576
  ]);
@@ -3659,10 +3680,6 @@ var useMeeting = (handlers) => {
3659
3680
  if (!handlers?.onConnectionStateChanged) return;
3660
3681
  return ctx.onConnectionStateChanged(handlers.onConnectionStateChanged);
3661
3682
  }, [handlers?.onConnectionStateChanged]);
3662
- (0, import_react4.useEffect)(() => {
3663
- if (!handlers?.onLiveTranscript) return;
3664
- return ctx.onLiveTranscript(handlers.onLiveTranscript);
3665
- }, [handlers?.onLiveTranscript]);
3666
3683
  (0, import_react4.useEffect)(() => {
3667
3684
  if (!handlers?.onNetworkQuality) return;
3668
3685
  return ctx.onNetworkQuality(handlers.onNetworkQuality);
@@ -3804,16 +3821,6 @@ function useMeetingPreview(roomId, participantId) {
3804
3821
  };
3805
3822
  }
3806
3823
 
3807
- // src/react/useLiveTranscript.ts
3808
- var useLiveTranscript = () => {
3809
- const { sdk } = useMeetingContext();
3810
- return useMeetingStore(
3811
- sdk.state,
3812
- "transcript",
3813
- (state) => state.getLiveTranscripts()
3814
- );
3815
- };
3816
-
3817
3824
  // src/react/useNetworkQuality.ts
3818
3825
  var useNetworkQuality = () => {
3819
3826
  const { sdk } = useMeetingContext();
@@ -3843,7 +3850,6 @@ function usePubSub(topic, options) {
3843
3850
  MeetingProvider,
3844
3851
  MeetingState,
3845
3852
  VideoSDKCore,
3846
- useLiveTranscript,
3847
3853
  useLocalParticipant,
3848
3854
  useMeeting,
3849
3855
  useMeetingContext,