@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.mjs CHANGED
@@ -197,7 +197,6 @@ var MeetingState = class {
197
197
  this.presenterId = null;
198
198
  this.activeSpeakerIds = /* @__PURE__ */ new Set();
199
199
  this.recording = null;
200
- this.liveTranscripts = [];
201
200
  this.networkQuality = null;
202
201
  this.listeners = /* @__PURE__ */ new Map();
203
202
  }
@@ -236,17 +235,6 @@ var MeetingState = class {
236
235
  this.recording = null;
237
236
  this.notify("recording");
238
237
  }
239
- addLiveTranscript(transcript) {
240
- this.liveTranscripts = [...this.liveTranscripts.slice(-499), transcript];
241
- this.notify("transcript");
242
- }
243
- getLiveTranscripts() {
244
- return this.liveTranscripts;
245
- }
246
- clearLiveTranscripts() {
247
- this.liveTranscripts = [];
248
- this.notify("transcript");
249
- }
250
238
  setNetworkQuality(quality) {
251
239
  this.networkQuality = quality;
252
240
  this.notify("networkQuality");
@@ -371,13 +359,11 @@ var MeetingState = class {
371
359
  this.chatMessages.clear();
372
360
  this.presenterId = null;
373
361
  this.activeSpeakerIds = /* @__PURE__ */ new Set();
374
- this.liveTranscripts = [];
375
362
  this.networkQuality = null;
376
363
  this.notify("participants");
377
364
  this.notify("chat");
378
365
  this.notify("presenter");
379
366
  this.notify("activeSpeakers");
380
- this.notify("transcript");
381
367
  this.notify("networkQuality");
382
368
  }
383
369
  };
@@ -440,7 +426,6 @@ var SFUClient = class {
440
426
  this.trackToSource = /* @__PURE__ */ new Map();
441
427
  this.localTrackIds = /* @__PURE__ */ new Map();
442
428
  this.subscriberStatsTimer = null;
443
- this.liveTranscriptionSeen = false;
444
429
  this.networkProfile = null;
445
430
  this.heartbeatTimer = null;
446
431
  this.apiBase = options.apiBase || "https://api.example.com/api";
@@ -498,8 +483,6 @@ var SFUClient = class {
498
483
  });
499
484
  this.onRoomUpdate = options.onRoomUpdate || (() => {
500
485
  });
501
- this.onLiveTranscript = options.onLiveTranscript || (() => {
502
- });
503
486
  this.onNetworkQuality = options.onNetworkQuality || (() => {
504
487
  });
505
488
  this.onParticipantMediaState = options.onParticipantMediaState || (() => {
@@ -1096,24 +1079,6 @@ var SFUClient = class {
1096
1079
  if (reaction?.id && reaction?.emoji) this.onReaction(reaction);
1097
1080
  break;
1098
1081
  }
1099
- case "live_transcript": {
1100
- const transcript = {
1101
- transcript_id: String(payload.transcript_id),
1102
- participant_id: String(payload.participant_id),
1103
- speaker_name: payload.speaker_name ?? null,
1104
- sequence: Number(payload.sequence ?? 0),
1105
- text: String(payload.text ?? ""),
1106
- offset_ms: payload.offset_ms == null ? null : Number(payload.offset_ms),
1107
- start_ms: payload.start_ms == null ? null : Number(payload.start_ms),
1108
- end_ms: payload.end_ms == null ? null : Number(payload.end_ms),
1109
- timestamp: String(payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString())
1110
- };
1111
- if (transcript.text.trim()) {
1112
- this.liveTranscriptionSeen = true;
1113
- this.onLiveTranscript(transcript);
1114
- }
1115
- break;
1116
- }
1117
1082
  case "network_quality": {
1118
1083
  const raw = payload.quality || payload;
1119
1084
  const quality = {
@@ -1571,6 +1536,78 @@ var SFUClient = class {
1571
1536
  this.subscriptionRunning = false;
1572
1537
  }
1573
1538
  }
1539
+ /**
1540
+ * Enumerate media inputs and outputs exposed by the browser/OS.
1541
+ * Labels may be empty until the user has granted media permission.
1542
+ */
1543
+ async getMediaDevices() {
1544
+ if (!navigator.mediaDevices?.enumerateDevices) {
1545
+ throw new Error("Media device enumeration is not supported on this device");
1546
+ }
1547
+ return navigator.mediaDevices.enumerateDevices();
1548
+ }
1549
+ /**
1550
+ * Replace the published camera without leaving the meeting.
1551
+ * The browser/OS must expose the target as a videoinput device.
1552
+ */
1553
+ async selectCamera(deviceId) {
1554
+ if (!deviceId) throw new Error("Camera deviceId is required");
1555
+ if (!navigator.mediaDevices?.getUserMedia) {
1556
+ throw new Error("Camera access is not supported on this device");
1557
+ }
1558
+ const stream = await navigator.mediaDevices.getUserMedia({
1559
+ video: { deviceId: { exact: deviceId } },
1560
+ audio: false
1561
+ });
1562
+ const nextTrack = stream.getVideoTracks()[0];
1563
+ if (!nextTrack) {
1564
+ stream.getTracks().forEach((track) => track.stop());
1565
+ throw new Error("Selected camera returned no video track");
1566
+ }
1567
+ const currentTrack = this.localStream?.getVideoTracks()[0] ?? null;
1568
+ nextTrack.enabled = currentTrack?.enabled ?? true;
1569
+ if (!this.localStream) this.localStream = new MediaStream();
1570
+ if (currentTrack) this.localStream.removeTrack(currentTrack);
1571
+ this.localStream.addTrack(nextTrack);
1572
+ if (this.videoSender) {
1573
+ await this.videoSender.replaceTrack(nextTrack);
1574
+ }
1575
+ currentTrack?.stop();
1576
+ return nextTrack;
1577
+ }
1578
+ /**
1579
+ * Replace the published microphone without leaving the meeting.
1580
+ */
1581
+ async selectMicrophone(deviceId) {
1582
+ if (!deviceId) throw new Error("Microphone deviceId is required");
1583
+ if (!navigator.mediaDevices?.getUserMedia) {
1584
+ throw new Error("Microphone access is not supported on this device");
1585
+ }
1586
+ const stream = await navigator.mediaDevices.getUserMedia({
1587
+ video: false,
1588
+ audio: {
1589
+ deviceId: { exact: deviceId },
1590
+ echoCancellation: true,
1591
+ noiseSuppression: true,
1592
+ autoGainControl: true
1593
+ }
1594
+ });
1595
+ const nextTrack = stream.getAudioTracks()[0];
1596
+ if (!nextTrack) {
1597
+ stream.getTracks().forEach((track) => track.stop());
1598
+ throw new Error("Selected microphone returned no audio track");
1599
+ }
1600
+ const currentTrack = this.localStream?.getAudioTracks()[0] ?? null;
1601
+ nextTrack.enabled = currentTrack?.enabled ?? true;
1602
+ if (!this.localStream) this.localStream = new MediaStream();
1603
+ if (currentTrack) this.localStream.removeTrack(currentTrack);
1604
+ this.localStream.addTrack(nextTrack);
1605
+ if (this.audioSender) {
1606
+ await this.audioSender.replaceTrack(nextTrack);
1607
+ }
1608
+ currentTrack?.stop();
1609
+ return nextTrack;
1610
+ }
1574
1611
  async publish({ audio = true, video = true } = {}) {
1575
1612
  if (!this.publisher)
1576
1613
  throw new Error("Publisher PeerConnection is not initialized");
@@ -1792,27 +1829,6 @@ var SFUClient = class {
1792
1829
  }
1793
1830
  });
1794
1831
  }
1795
- /**
1796
- * Transcription is server-authoritative. The SFU reads the already-published
1797
- * microphone RTP stream, sends audio to the configured transcription service,
1798
- * persists final segments, and broadcasts LiveTranscript events.
1799
- *
1800
- * These methods remain as compatibility shims for applications that used the
1801
- * previous browser MediaRecorder-based API. They intentionally do not capture
1802
- * or upload microphone audio from the browser.
1803
- */
1804
- async startLiveTranscription() {
1805
- if (!this.room) {
1806
- throw new Error("Cannot start live transcription without a room");
1807
- }
1808
- console.info("[Transcript] server-authoritative live transcription is managed by the SFU");
1809
- }
1810
- stopLiveTranscription() {
1811
- console.info("[Transcript] server-authoritative live transcription remains managed by the SFU");
1812
- }
1813
- isLiveTranscriptionActive() {
1814
- return this.liveTranscriptionSeen;
1815
- }
1816
1832
  async getTranscript(roomCode, sessionId) {
1817
1833
  if (!this.authToken) throw new Error("Authentication token is required");
1818
1834
  const query = sessionId ? `?session_id=${encodeURIComponent(sessionId)}` : "";
@@ -2009,7 +2025,6 @@ var SFUClient = class {
2009
2025
  }
2010
2026
  }
2011
2027
  async leave() {
2012
- this.stopLiveTranscription();
2013
2028
  this.stopHeartbeat();
2014
2029
  this.intentionalClose = true;
2015
2030
  this.setState("leaving");
@@ -2299,7 +2314,14 @@ var VideoSDKCore = class {
2299
2314
  }
2300
2315
  // JOIN
2301
2316
  async joinMeeting(config) {
2302
- const { roomId, name, audioMuted = false, videoMuted = false } = config;
2317
+ const {
2318
+ roomId,
2319
+ name,
2320
+ audioMuted = false,
2321
+ videoMuted = false,
2322
+ cameraDeviceId,
2323
+ microphoneDeviceId
2324
+ } = config;
2303
2325
  if (!roomId || !name) {
2304
2326
  throw new Error("roomId and name are required to join meeting");
2305
2327
  }
@@ -2334,8 +2356,13 @@ var VideoSDKCore = class {
2334
2356
  this.room.name = name;
2335
2357
  if (!this.localStream) {
2336
2358
  this.localStream = await navigator.mediaDevices.getUserMedia({
2337
- video: true,
2338
- audio: true
2359
+ video: cameraDeviceId ? { deviceId: { exact: cameraDeviceId } } : true,
2360
+ audio: microphoneDeviceId ? {
2361
+ deviceId: { exact: microphoneDeviceId },
2362
+ echoCancellation: true,
2363
+ noiseSuppression: true,
2364
+ autoGainControl: true
2365
+ } : true
2339
2366
  });
2340
2367
  const localAudioTrack = this.localStream.getAudioTracks()[0];
2341
2368
  if (localAudioTrack) {
@@ -2424,10 +2451,6 @@ var VideoSDKCore = class {
2424
2451
  console.warn(`[SFU] Reconnecting, attempt ${attempt}`);
2425
2452
  },
2426
2453
  // ADMISSION CONTROL
2427
- onLiveTranscript: (transcript) => {
2428
- this.state.addLiveTranscript(transcript);
2429
- this.events.onLiveTranscript?.(transcript);
2430
- },
2431
2454
  onNetworkQuality: (quality) => {
2432
2455
  this.state.setNetworkQuality(quality);
2433
2456
  this.events.onNetworkQuality?.(quality);
@@ -2927,6 +2950,39 @@ var VideoSDKCore = class {
2927
2950
  return this.sfuClient?.removeModerator(roomCode, userId);
2928
2951
  }
2929
2952
  // MEDIA CONTROL
2953
+ async getMediaDevices() {
2954
+ if (!this.sfuClient) {
2955
+ throw new Error("SFU client is not initialized");
2956
+ }
2957
+ return this.sfuClient.getMediaDevices();
2958
+ }
2959
+ async selectCamera(deviceId) {
2960
+ if (!this.sfuClient) {
2961
+ throw new Error("SFU client is not initialized");
2962
+ }
2963
+ const track = await this.sfuClient.selectCamera(deviceId);
2964
+ if (this.localStream) {
2965
+ this.state.localStream = this.localStream;
2966
+ this.updateLocalMedia();
2967
+ }
2968
+ return track;
2969
+ }
2970
+ async selectMicrophone(deviceId) {
2971
+ if (!this.sfuClient) {
2972
+ throw new Error("SFU client is not initialized");
2973
+ }
2974
+ const track = await this.sfuClient.selectMicrophone(deviceId);
2975
+ if (this.localStream) {
2976
+ const mic = this.localStream.getAudioTracks()[0];
2977
+ if (mic) {
2978
+ this.activeSpeakerDetector.removeParticipant(this.myId);
2979
+ this.activeSpeakerDetector.addTrack(this.myId, mic);
2980
+ }
2981
+ this.state.localStream = this.localStream;
2982
+ this.updateLocalMedia();
2983
+ }
2984
+ return track;
2985
+ }
2930
2986
  toggleMic() {
2931
2987
  const mediaState = this.state.localParticipant?.media;
2932
2988
  if (!mediaState || !this.localStream) {
@@ -3076,22 +3132,6 @@ var VideoSDKCore = class {
3076
3132
  }
3077
3133
  }
3078
3134
  }
3079
- async startLiveTranscription() {
3080
- if (!this.sfuClient) {
3081
- throw new Error("SFU client is not connected");
3082
- }
3083
- const audioTrack = this.localStream?.getAudioTracks()[0];
3084
- if (!audioTrack || audioTrack.readyState !== "live") {
3085
- throw new Error("Microphone audio track is not available");
3086
- }
3087
- await this.sfuClient.startLiveTranscription();
3088
- }
3089
- stopLiveTranscription() {
3090
- this.sfuClient?.stopLiveTranscription();
3091
- }
3092
- isLiveTranscriptionActive() {
3093
- return this.sfuClient?.isLiveTranscriptionActive() ?? false;
3094
- }
3095
3135
  getNetworkQuality() {
3096
3136
  return this.state.networkQuality;
3097
3137
  }
@@ -3143,7 +3183,6 @@ var VideoSDKCore = class {
3143
3183
  this.room.id = null;
3144
3184
  this.room.name = null;
3145
3185
  this.state.localParticipant = null;
3146
- this.state.clearLiveTranscripts();
3147
3186
  this.state.setNetworkQuality(null);
3148
3187
  this.state.notify("localParticipant");
3149
3188
  this.state.resetRemoteState();
@@ -3198,9 +3237,6 @@ var MeetingProvider = ({
3198
3237
  const activeSpeakerListeners = useRef(
3199
3238
  /* @__PURE__ */ new Set()
3200
3239
  );
3201
- const liveTranscriptListeners = useRef(
3202
- /* @__PURE__ */ new Set()
3203
- );
3204
3240
  const networkQualityListeners = useRef(
3205
3241
  /* @__PURE__ */ new Set()
3206
3242
  );
@@ -3287,9 +3323,6 @@ var MeetingProvider = ({
3287
3323
  );
3288
3324
  pubSubListeners.current.get("REACTION")?.forEach((fn) => fn(message));
3289
3325
  },
3290
- onLiveTranscript: (transcript) => {
3291
- liveTranscriptListeners.current.forEach((fn) => fn(transcript));
3292
- },
3293
3326
  onNetworkQuality: (quality) => {
3294
3327
  networkQualityListeners.current.forEach((fn) => fn(quality));
3295
3328
  },
@@ -3328,11 +3361,6 @@ var MeetingProvider = ({
3328
3361
  "chat",
3329
3362
  (s) => s.getChatMessages()
3330
3363
  );
3331
- const liveTranscripts = useMeetingStore(
3332
- sdk.state,
3333
- "transcript",
3334
- (s) => s.getLiveTranscripts()
3335
- );
3336
3364
  const networkQuality = useMeetingStore(
3337
3365
  sdk.state,
3338
3366
  "networkQuality",
@@ -3350,6 +3378,9 @@ var MeetingProvider = ({
3350
3378
  createRoom: sdk.createRoom.bind(sdk),
3351
3379
  getRoom: sdk.getRoom.bind(sdk),
3352
3380
  deleteRoom: sdk.deleteRoom.bind(sdk),
3381
+ getMediaDevices: sdk.getMediaDevices.bind(sdk),
3382
+ selectCamera: sdk.selectCamera.bind(sdk),
3383
+ selectMicrophone: sdk.selectMicrophone.bind(sdk),
3353
3384
  getTranscript: sdk.getTranscript.bind(sdk),
3354
3385
  getMeetingSummary: sdk.getMeetingSummary.bind(sdk),
3355
3386
  toggleMic: sdk.toggleMic.bind(sdk),
@@ -3364,11 +3395,7 @@ var MeetingProvider = ({
3364
3395
  messages,
3365
3396
  presenterId,
3366
3397
  activeSpeakerIds,
3367
- liveTranscripts,
3368
3398
  networkQuality,
3369
- startLiveTranscription: sdk.startLiveTranscription.bind(sdk),
3370
- stopLiveTranscription: sdk.stopLiveTranscription.bind(sdk),
3371
- isLiveTranscriptionActive: sdk.isLiveTranscriptionActive.bind(sdk),
3372
3399
  recording,
3373
3400
  usePubSub: (topic) => {
3374
3401
  if (topic === "SECURE_CHAT") {
@@ -3487,10 +3514,6 @@ var MeetingProvider = ({
3487
3514
  reactionListeners.current.add(cb);
3488
3515
  return () => reactionListeners.current.delete(cb);
3489
3516
  },
3490
- onLiveTranscript: (cb) => {
3491
- liveTranscriptListeners.current.add(cb);
3492
- return () => liveTranscriptListeners.current.delete(cb);
3493
- },
3494
3517
  onNetworkQuality: (cb) => {
3495
3518
  networkQualityListeners.current.add(cb);
3496
3519
  return () => networkQualityListeners.current.delete(cb);
@@ -3517,7 +3540,6 @@ var MeetingProvider = ({
3517
3540
  presenterId,
3518
3541
  recording,
3519
3542
  activeSpeakerIds,
3520
- liveTranscripts,
3521
3543
  networkQuality,
3522
3544
  reactionPubSubMessages
3523
3545
  ]);
@@ -3627,10 +3649,6 @@ var useMeeting = (handlers) => {
3627
3649
  if (!handlers?.onConnectionStateChanged) return;
3628
3650
  return ctx.onConnectionStateChanged(handlers.onConnectionStateChanged);
3629
3651
  }, [handlers?.onConnectionStateChanged]);
3630
- useEffect3(() => {
3631
- if (!handlers?.onLiveTranscript) return;
3632
- return ctx.onLiveTranscript(handlers.onLiveTranscript);
3633
- }, [handlers?.onLiveTranscript]);
3634
3652
  useEffect3(() => {
3635
3653
  if (!handlers?.onNetworkQuality) return;
3636
3654
  return ctx.onNetworkQuality(handlers.onNetworkQuality);
@@ -3772,16 +3790,6 @@ function useMeetingPreview(roomId, participantId) {
3772
3790
  };
3773
3791
  }
3774
3792
 
3775
- // src/react/useLiveTranscript.ts
3776
- var useLiveTranscript = () => {
3777
- const { sdk } = useMeetingContext();
3778
- return useMeetingStore(
3779
- sdk.state,
3780
- "transcript",
3781
- (state) => state.getLiveTranscripts()
3782
- );
3783
- };
3784
-
3785
3793
  // src/react/useNetworkQuality.ts
3786
3794
  var useNetworkQuality = () => {
3787
3795
  const { sdk } = useMeetingContext();
@@ -3810,7 +3818,6 @@ export {
3810
3818
  MeetingProvider,
3811
3819
  MeetingState,
3812
3820
  VideoSDKCore,
3813
- useLiveTranscript,
3814
3821
  useLocalParticipant,
3815
3822
  useMeeting,
3816
3823
  useMeetingContext,