@defcomm/meetingsdk 0.5.7 → 0.5.8

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.d.mts CHANGED
@@ -143,6 +143,49 @@ type LiveTranscript = {
143
143
  end_ms?: number | null;
144
144
  timestamp: string;
145
145
  };
146
+ type TranscriptSegment = {
147
+ id: string;
148
+ transcript_session_id: string;
149
+ room_id: string;
150
+ participant_id: string;
151
+ speaker_name?: string | null;
152
+ sequence: number;
153
+ text: string;
154
+ offset_ms?: number | null;
155
+ start_ms?: number | null;
156
+ end_ms?: number | null;
157
+ created_at: string;
158
+ };
159
+ type TranscriptSession = {
160
+ id: string;
161
+ room_id: string;
162
+ status: string;
163
+ started_at: string;
164
+ finalized_at?: string | null;
165
+ };
166
+ type MeetingSummary = {
167
+ transcript_session_id: string;
168
+ room_id: string;
169
+ status: string;
170
+ overview?: string | null;
171
+ topics: string[];
172
+ decisions: string[];
173
+ action_items: string[];
174
+ important_moments: Array<{
175
+ timestamp_ms?: number | null;
176
+ title?: string | null;
177
+ description?: string | null;
178
+ }>;
179
+ error?: string | null;
180
+ created_at: string;
181
+ updated_at: string;
182
+ };
183
+ type TranscriptResponse = {
184
+ room_id: string;
185
+ session: TranscriptSession | null;
186
+ segments: TranscriptSegment[];
187
+ summary: MeetingSummary | null;
188
+ };
146
189
  type NetworkQuality = {
147
190
  participant_id: string;
148
191
  quality: "excellent" | "good" | "fair" | "poor" | string;
@@ -291,6 +334,10 @@ declare class VideoSDKCore {
291
334
  getRoom(roomCode: string): Promise<any>;
292
335
  getJoinState(roomCode: string): Promise<JoinState>;
293
336
  deleteRoom(roomCode: string): Promise<any>;
337
+ getTranscript(roomCode: string, sessionId?: string): Promise<TranscriptResponse>;
338
+ getMeetingSummary(roomCode: string, sessionId: string): Promise<{
339
+ summary: MeetingSummary | null;
340
+ }>;
294
341
  joinMeeting(config: Omit<MeetingConfig, "participantId">): Promise<void>;
295
342
  private connect;
296
343
  private createSFUClient;
@@ -368,6 +415,10 @@ type MeetingContextValue = {
368
415
  }) => Promise<any>;
369
416
  getRoom: (roomCode: string) => Promise<any>;
370
417
  deleteRoom: (roomCode: string) => Promise<any>;
418
+ getTranscript: (roomCode: string, sessionId?: string) => Promise<TranscriptResponse>;
419
+ getMeetingSummary: (roomCode: string, sessionId: string) => Promise<{
420
+ summary: MeetingSummary | null;
421
+ }>;
371
422
  toggleMic: () => void;
372
423
  toggleCam: () => void;
373
424
  startScreenShare: () => Promise<MediaStream | null>;
@@ -453,6 +504,10 @@ declare const useMeeting: (handlers?: {
453
504
  }) => Promise<any>;
454
505
  getRoom: (roomCode: string) => Promise<any>;
455
506
  deleteRoom: (roomCode: string) => Promise<any>;
507
+ getTranscript: (roomCode: string, sessionId?: string) => Promise<TranscriptResponse>;
508
+ getMeetingSummary: (roomCode: string, sessionId: string) => Promise<{
509
+ summary: MeetingSummary | null;
510
+ }>;
456
511
  toggleMic: () => void;
457
512
  toggleCam: () => void;
458
513
  startScreenShare: () => Promise<MediaStream | null>;
@@ -590,4 +645,4 @@ declare function usePubSub<T extends PubSubTopic>(topic: T, options?: {
590
645
  onMessageReceived?: (message: PubSubMessage<PubSubTopicConfig[T]["message"]>) => void;
591
646
  }): PubSubHandleForTopic<T>;
592
647
 
593
- export { ActiveSpeakerDetector, type ChatInput, type LiveTranscript, type MeetingConfig, MeetingProvider, MeetingState, type NetworkQuality, type Participant, type ParticipantInfo, type PubSubHandle, type PubSubHandleForTopic, type PubSubMessage, type PubSubTopic, type PubSubTopicConfig, type Reaction, type RecordingInfo, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLiveTranscript, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useNetworkQuality, useParticipants, usePubSub, useRemoteMedia };
648
+ export { ActiveSpeakerDetector, type ChatInput, type LiveTranscript, type MeetingConfig, MeetingProvider, MeetingState, type MeetingSummary, type NetworkQuality, type Participant, type ParticipantInfo, type PubSubHandle, type PubSubHandleForTopic, type PubSubMessage, type PubSubTopic, type PubSubTopicConfig, type Reaction, type RecordingInfo, type SFUConnectionState, type TrackInfo, type TranscriptResponse, type TranscriptSegment, type TranscriptSession, VideoSDKCore, useLiveTranscript, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useNetworkQuality, useParticipants, usePubSub, useRemoteMedia };
package/dist/index.d.ts CHANGED
@@ -143,6 +143,49 @@ type LiveTranscript = {
143
143
  end_ms?: number | null;
144
144
  timestamp: string;
145
145
  };
146
+ type TranscriptSegment = {
147
+ id: string;
148
+ transcript_session_id: string;
149
+ room_id: string;
150
+ participant_id: string;
151
+ speaker_name?: string | null;
152
+ sequence: number;
153
+ text: string;
154
+ offset_ms?: number | null;
155
+ start_ms?: number | null;
156
+ end_ms?: number | null;
157
+ created_at: string;
158
+ };
159
+ type TranscriptSession = {
160
+ id: string;
161
+ room_id: string;
162
+ status: string;
163
+ started_at: string;
164
+ finalized_at?: string | null;
165
+ };
166
+ type MeetingSummary = {
167
+ transcript_session_id: string;
168
+ room_id: string;
169
+ status: string;
170
+ overview?: string | null;
171
+ topics: string[];
172
+ decisions: string[];
173
+ action_items: string[];
174
+ important_moments: Array<{
175
+ timestamp_ms?: number | null;
176
+ title?: string | null;
177
+ description?: string | null;
178
+ }>;
179
+ error?: string | null;
180
+ created_at: string;
181
+ updated_at: string;
182
+ };
183
+ type TranscriptResponse = {
184
+ room_id: string;
185
+ session: TranscriptSession | null;
186
+ segments: TranscriptSegment[];
187
+ summary: MeetingSummary | null;
188
+ };
146
189
  type NetworkQuality = {
147
190
  participant_id: string;
148
191
  quality: "excellent" | "good" | "fair" | "poor" | string;
@@ -291,6 +334,10 @@ declare class VideoSDKCore {
291
334
  getRoom(roomCode: string): Promise<any>;
292
335
  getJoinState(roomCode: string): Promise<JoinState>;
293
336
  deleteRoom(roomCode: string): Promise<any>;
337
+ getTranscript(roomCode: string, sessionId?: string): Promise<TranscriptResponse>;
338
+ getMeetingSummary(roomCode: string, sessionId: string): Promise<{
339
+ summary: MeetingSummary | null;
340
+ }>;
294
341
  joinMeeting(config: Omit<MeetingConfig, "participantId">): Promise<void>;
295
342
  private connect;
296
343
  private createSFUClient;
@@ -368,6 +415,10 @@ type MeetingContextValue = {
368
415
  }) => Promise<any>;
369
416
  getRoom: (roomCode: string) => Promise<any>;
370
417
  deleteRoom: (roomCode: string) => Promise<any>;
418
+ getTranscript: (roomCode: string, sessionId?: string) => Promise<TranscriptResponse>;
419
+ getMeetingSummary: (roomCode: string, sessionId: string) => Promise<{
420
+ summary: MeetingSummary | null;
421
+ }>;
371
422
  toggleMic: () => void;
372
423
  toggleCam: () => void;
373
424
  startScreenShare: () => Promise<MediaStream | null>;
@@ -453,6 +504,10 @@ declare const useMeeting: (handlers?: {
453
504
  }) => Promise<any>;
454
505
  getRoom: (roomCode: string) => Promise<any>;
455
506
  deleteRoom: (roomCode: string) => Promise<any>;
507
+ getTranscript: (roomCode: string, sessionId?: string) => Promise<TranscriptResponse>;
508
+ getMeetingSummary: (roomCode: string, sessionId: string) => Promise<{
509
+ summary: MeetingSummary | null;
510
+ }>;
456
511
  toggleMic: () => void;
457
512
  toggleCam: () => void;
458
513
  startScreenShare: () => Promise<MediaStream | null>;
@@ -590,4 +645,4 @@ declare function usePubSub<T extends PubSubTopic>(topic: T, options?: {
590
645
  onMessageReceived?: (message: PubSubMessage<PubSubTopicConfig[T]["message"]>) => void;
591
646
  }): PubSubHandleForTopic<T>;
592
647
 
593
- export { ActiveSpeakerDetector, type ChatInput, type LiveTranscript, type MeetingConfig, MeetingProvider, MeetingState, type NetworkQuality, type Participant, type ParticipantInfo, type PubSubHandle, type PubSubHandleForTopic, type PubSubMessage, type PubSubTopic, type PubSubTopicConfig, type Reaction, type RecordingInfo, type SFUConnectionState, type TrackInfo, VideoSDKCore, useLiveTranscript, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useNetworkQuality, useParticipants, usePubSub, useRemoteMedia };
648
+ export { ActiveSpeakerDetector, type ChatInput, type LiveTranscript, type MeetingConfig, MeetingProvider, MeetingState, type MeetingSummary, type NetworkQuality, type Participant, type ParticipantInfo, type PubSubHandle, type PubSubHandleForTopic, type PubSubMessage, type PubSubTopic, type PubSubTopicConfig, type Reaction, type RecordingInfo, type SFUConnectionState, type TrackInfo, type TranscriptResponse, type TranscriptSegment, type TranscriptSession, VideoSDKCore, useLiveTranscript, useLocalParticipant, useMeeting, useMeetingContext, useMeetingPreview, useNetworkQuality, useParticipants, usePubSub, useRemoteMedia };
package/dist/index.js CHANGED
@@ -472,11 +472,7 @@ var SFUClient = class {
472
472
  this.trackToSource = /* @__PURE__ */ new Map();
473
473
  this.localTrackIds = /* @__PURE__ */ new Map();
474
474
  this.subscriberStatsTimer = null;
475
- this.liveTranscriptionActive = false;
476
- this.liveTranscriptionSequence = 0;
477
- this.liveTranscriptionStartedAt = 0;
478
- this.liveTranscriptionRecorder = null;
479
- this.liveTranscriptionLoopPromise = null;
475
+ this.liveTranscriptionSeen = false;
480
476
  this.networkProfile = null;
481
477
  this.heartbeatTimer = null;
482
478
  this.apiBase = options.apiBase || "https://api.example.com/api";
@@ -1144,7 +1140,10 @@ var SFUClient = class {
1144
1140
  end_ms: payload.end_ms == null ? null : Number(payload.end_ms),
1145
1141
  timestamp: String(payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString())
1146
1142
  };
1147
- if (transcript.text.trim()) this.onLiveTranscript(transcript);
1143
+ if (transcript.text.trim()) {
1144
+ this.liveTranscriptionSeen = true;
1145
+ this.onLiveTranscript(transcript);
1146
+ }
1148
1147
  break;
1149
1148
  }
1150
1149
  case "network_quality": {
@@ -1825,180 +1824,63 @@ var SFUClient = class {
1825
1824
  }
1826
1825
  });
1827
1826
  }
1828
- getLiveTranscriptionMimeType() {
1829
- if (typeof MediaRecorder === "undefined") {
1830
- throw new Error("MediaRecorder is not supported by this browser");
1831
- }
1832
- const candidates = [
1833
- "audio/webm;codecs=opus",
1834
- "audio/webm",
1835
- "audio/ogg;codecs=opus"
1836
- ];
1837
- return candidates.find(
1838
- (type) => MediaRecorder.isTypeSupported(type)
1839
- ) || "";
1840
- }
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
+ */
1841
1836
  async startLiveTranscription() {
1842
- if (this.liveTranscriptionActive) {
1843
- return;
1844
- }
1845
1837
  if (!this.room) {
1846
1838
  throw new Error("Cannot start live transcription without a room");
1847
1839
  }
1848
- if (!this.authToken) {
1849
- throw new Error(
1850
- "Cannot start live transcription without an authenticated token"
1851
- );
1852
- }
1853
- const audioTrack = this.localStream?.getAudioTracks()[0];
1854
- if (!audioTrack || audioTrack.readyState !== "live") {
1855
- throw new Error(
1856
- "Cannot start live transcription without a live microphone track"
1857
- );
1858
- }
1859
- const mimeType = this.getLiveTranscriptionMimeType();
1860
- const stream = new MediaStream([audioTrack]);
1861
- this.liveTranscriptionActive = true;
1862
- this.liveTranscriptionSequence = 0;
1863
- this.liveTranscriptionStartedAt = Date.now();
1864
- console.log("[Transcript] starting", {
1865
- room: this.room,
1866
- trackId: audioTrack.id,
1867
- readyState: audioTrack.readyState,
1868
- enabled: audioTrack.enabled,
1869
- mimeType
1870
- });
1871
- this.liveTranscriptionLoopPromise = this.runLiveTranscriptionLoop(stream, mimeType).finally(() => {
1872
- this.liveTranscriptionRecorder = null;
1873
- this.liveTranscriptionLoopPromise = null;
1874
- });
1875
- await Promise.resolve();
1876
- }
1877
- async captureLiveChunk(stream, mimeType) {
1878
- return new Promise((resolve, reject) => {
1879
- const chunks = [];
1880
- const recorder = mimeType ? new MediaRecorder(stream, { mimeType }) : new MediaRecorder(stream);
1881
- this.liveTranscriptionRecorder = recorder;
1882
- const timeout = window.setTimeout(() => {
1883
- if (recorder.state !== "inactive") {
1884
- recorder.stop();
1885
- }
1886
- }, 1500);
1887
- recorder.onstart = () => {
1888
- console.log("[Transcript] MediaRecorder started", {
1889
- mimeType: recorder.mimeType
1890
- });
1891
- };
1892
- recorder.ondataavailable = (event) => {
1893
- if (event.data.size > 0) {
1894
- chunks.push(event.data);
1895
- }
1896
- };
1897
- recorder.onerror = () => {
1898
- window.clearTimeout(timeout);
1899
- reject(new Error("Live transcription recorder failed"));
1900
- };
1901
- recorder.onstop = () => {
1902
- window.clearTimeout(timeout);
1903
- this.liveTranscriptionRecorder = null;
1904
- resolve(
1905
- chunks.length ? new Blob(chunks, {
1906
- type: recorder.mimeType || mimeType || "audio/webm"
1907
- }) : null
1908
- );
1909
- };
1910
- recorder.start();
1911
- });
1840
+ console.info("[Transcript] server-authoritative live transcription is managed by the SFU");
1912
1841
  }
1913
- async runLiveTranscriptionLoop(stream, mimeType) {
1914
- while (this.liveTranscriptionActive && stream.getAudioTracks()[0]?.readyState === "live") {
1915
- try {
1916
- const blob = await this.captureLiveChunk(stream, mimeType);
1917
- if (!this.liveTranscriptionActive) {
1918
- break;
1919
- }
1920
- if (!blob || blob.size === 0) {
1921
- continue;
1842
+ stopLiveTranscription() {
1843
+ console.info("[Transcript] server-authoritative live transcription remains managed by the SFU");
1844
+ }
1845
+ isLiveTranscriptionActive() {
1846
+ return this.liveTranscriptionSeen;
1847
+ }
1848
+ async getTranscript(roomCode, sessionId) {
1849
+ if (!this.authToken) throw new Error("Authentication token is required");
1850
+ const query = sessionId ? `?session_id=${encodeURIComponent(sessionId)}` : "";
1851
+ const response = await fetch(
1852
+ `${this.apiBase}/rooms/${encodeURIComponent(roomCode)}/transcript${query}`,
1853
+ {
1854
+ method: "GET",
1855
+ headers: {
1856
+ Authorization: `Bearer ${this.authToken}`,
1857
+ Accept: "application/json"
1922
1858
  }
1923
- const sequence = this.liveTranscriptionSequence++;
1924
- const offsetMs = Math.max(
1925
- 0,
1926
- Date.now() - this.liveTranscriptionStartedAt
1927
- );
1928
- await this.uploadLiveTranscriptionChunk(
1929
- blob,
1930
- sequence,
1931
- offsetMs,
1932
- blob.type || mimeType
1933
- );
1934
- } catch (error) {
1935
- console.error(
1936
- "[Transcript] chunk processing failed",
1937
- error
1938
- );
1939
- this.onError(error);
1940
- await new Promise(
1941
- (resolve) => setTimeout(resolve, 750)
1942
- );
1943
1859
  }
1944
- }
1945
- }
1946
- async uploadLiveTranscriptionChunk(blob, sequence, offsetMs, mimeType) {
1947
- if (!this.room || !this.authToken) {
1948
- return;
1949
- }
1950
- const form = new FormData();
1951
- form.append("sequence", String(sequence));
1952
- form.append("offset_ms", String(offsetMs));
1953
- const extension = mimeType.includes("ogg") || blob.type.includes("ogg") ? "ogg" : "webm";
1954
- form.append(
1955
- "audio",
1956
- blob,
1957
- `live-${sequence}.${extension}`
1958
1860
  );
1959
- const url = `${this.apiBase}/rooms/${encodeURIComponent(this.room)}/live-transcription`;
1960
- console.log("[Transcript] POST", {
1961
- url,
1962
- sequence,
1963
- size: blob.size,
1964
- mimeType
1965
- });
1966
- const response = await fetch(url, {
1967
- method: "POST",
1968
- headers: {
1969
- Authorization: `Bearer ${this.authToken}`,
1970
- Accept: "application/json"
1971
- },
1972
- body: form
1973
- });
1974
- const responseText = await response.text();
1975
- console.log("[Transcript] response", {
1976
- status: response.status,
1977
- body: responseText
1978
- });
1861
+ const text = await response.text();
1979
1862
  if (!response.ok) {
1980
- throw new Error(
1981
- `Live transcription request failed: ${response.status} ${responseText}`
1982
- );
1863
+ throw new Error(`Transcript request failed: ${response.status} ${text}`);
1983
1864
  }
1865
+ return JSON.parse(text);
1984
1866
  }
1985
- stopLiveTranscription() {
1986
- console.log("[Transcript] stopping");
1987
- this.liveTranscriptionActive = false;
1988
- try {
1989
- if (this.liveTranscriptionRecorder && this.liveTranscriptionRecorder.state !== "inactive") {
1990
- this.liveTranscriptionRecorder.stop();
1867
+ async getMeetingSummary(roomCode, sessionId) {
1868
+ if (!this.authToken) throw new Error("Authentication token is required");
1869
+ const response = await fetch(
1870
+ `${this.apiBase}/rooms/${encodeURIComponent(roomCode)}/transcript/${encodeURIComponent(sessionId)}/summary`,
1871
+ {
1872
+ method: "GET",
1873
+ headers: {
1874
+ Authorization: `Bearer ${this.authToken}`,
1875
+ Accept: "application/json"
1876
+ }
1991
1877
  }
1992
- } catch (error) {
1993
- console.warn(
1994
- "[Transcript] recorder shutdown failed",
1995
- error
1996
- );
1878
+ );
1879
+ const text = await response.text();
1880
+ if (!response.ok) {
1881
+ throw new Error(`Meeting summary request failed: ${response.status} ${text}`);
1997
1882
  }
1998
- this.liveTranscriptionRecorder = null;
1999
- }
2000
- isLiveTranscriptionActive() {
2001
- return this.liveTranscriptionActive;
1883
+ return JSON.parse(text);
2002
1884
  }
2003
1885
  async applyNetworkRecommendation(action) {
2004
1886
  if (!this.videoSender || this.networkProfile === action) return;
@@ -2441,6 +2323,12 @@ var VideoSDKCore = class {
2441
2323
  async deleteRoom(roomCode) {
2442
2324
  return this.sfuClient.deleteRoom(roomCode);
2443
2325
  }
2326
+ async getTranscript(roomCode, sessionId) {
2327
+ return this.sfuClient.getTranscript(roomCode, sessionId);
2328
+ }
2329
+ async getMeetingSummary(roomCode, sessionId) {
2330
+ return this.sfuClient.getMeetingSummary(roomCode, sessionId);
2331
+ }
2444
2332
  // JOIN
2445
2333
  async joinMeeting(config) {
2446
2334
  const { roomId, name, audioMuted = false, videoMuted = false } = config;
@@ -3494,6 +3382,8 @@ var MeetingProvider = ({
3494
3382
  createRoom: sdk.createRoom.bind(sdk),
3495
3383
  getRoom: sdk.getRoom.bind(sdk),
3496
3384
  deleteRoom: sdk.deleteRoom.bind(sdk),
3385
+ getTranscript: sdk.getTranscript.bind(sdk),
3386
+ getMeetingSummary: sdk.getMeetingSummary.bind(sdk),
3497
3387
  toggleMic: sdk.toggleMic.bind(sdk),
3498
3388
  toggleCam: sdk.toggleCam.bind(sdk),
3499
3389
  startScreenShare: sdk.startScreenShare.bind(sdk),