@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.mjs CHANGED
@@ -440,11 +440,7 @@ var SFUClient = class {
440
440
  this.trackToSource = /* @__PURE__ */ new Map();
441
441
  this.localTrackIds = /* @__PURE__ */ new Map();
442
442
  this.subscriberStatsTimer = null;
443
- this.liveTranscriptionActive = false;
444
- this.liveTranscriptionSequence = 0;
445
- this.liveTranscriptionStartedAt = 0;
446
- this.liveTranscriptionRecorder = null;
447
- this.liveTranscriptionLoopPromise = null;
443
+ this.liveTranscriptionSeen = false;
448
444
  this.networkProfile = null;
449
445
  this.heartbeatTimer = null;
450
446
  this.apiBase = options.apiBase || "https://api.example.com/api";
@@ -1112,7 +1108,10 @@ var SFUClient = class {
1112
1108
  end_ms: payload.end_ms == null ? null : Number(payload.end_ms),
1113
1109
  timestamp: String(payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString())
1114
1110
  };
1115
- if (transcript.text.trim()) this.onLiveTranscript(transcript);
1111
+ if (transcript.text.trim()) {
1112
+ this.liveTranscriptionSeen = true;
1113
+ this.onLiveTranscript(transcript);
1114
+ }
1116
1115
  break;
1117
1116
  }
1118
1117
  case "network_quality": {
@@ -1793,180 +1792,63 @@ var SFUClient = class {
1793
1792
  }
1794
1793
  });
1795
1794
  }
1796
- getLiveTranscriptionMimeType() {
1797
- if (typeof MediaRecorder === "undefined") {
1798
- throw new Error("MediaRecorder is not supported by this browser");
1799
- }
1800
- const candidates = [
1801
- "audio/webm;codecs=opus",
1802
- "audio/webm",
1803
- "audio/ogg;codecs=opus"
1804
- ];
1805
- return candidates.find(
1806
- (type) => MediaRecorder.isTypeSupported(type)
1807
- ) || "";
1808
- }
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
+ */
1809
1804
  async startLiveTranscription() {
1810
- if (this.liveTranscriptionActive) {
1811
- return;
1812
- }
1813
1805
  if (!this.room) {
1814
1806
  throw new Error("Cannot start live transcription without a room");
1815
1807
  }
1816
- if (!this.authToken) {
1817
- throw new Error(
1818
- "Cannot start live transcription without an authenticated token"
1819
- );
1820
- }
1821
- const audioTrack = this.localStream?.getAudioTracks()[0];
1822
- if (!audioTrack || audioTrack.readyState !== "live") {
1823
- throw new Error(
1824
- "Cannot start live transcription without a live microphone track"
1825
- );
1826
- }
1827
- const mimeType = this.getLiveTranscriptionMimeType();
1828
- const stream = new MediaStream([audioTrack]);
1829
- this.liveTranscriptionActive = true;
1830
- this.liveTranscriptionSequence = 0;
1831
- this.liveTranscriptionStartedAt = Date.now();
1832
- console.log("[Transcript] starting", {
1833
- room: this.room,
1834
- trackId: audioTrack.id,
1835
- readyState: audioTrack.readyState,
1836
- enabled: audioTrack.enabled,
1837
- mimeType
1838
- });
1839
- this.liveTranscriptionLoopPromise = this.runLiveTranscriptionLoop(stream, mimeType).finally(() => {
1840
- this.liveTranscriptionRecorder = null;
1841
- this.liveTranscriptionLoopPromise = null;
1842
- });
1843
- await Promise.resolve();
1844
- }
1845
- async captureLiveChunk(stream, mimeType) {
1846
- return new Promise((resolve, reject) => {
1847
- const chunks = [];
1848
- const recorder = mimeType ? new MediaRecorder(stream, { mimeType }) : new MediaRecorder(stream);
1849
- this.liveTranscriptionRecorder = recorder;
1850
- const timeout = window.setTimeout(() => {
1851
- if (recorder.state !== "inactive") {
1852
- recorder.stop();
1853
- }
1854
- }, 1500);
1855
- recorder.onstart = () => {
1856
- console.log("[Transcript] MediaRecorder started", {
1857
- mimeType: recorder.mimeType
1858
- });
1859
- };
1860
- recorder.ondataavailable = (event) => {
1861
- if (event.data.size > 0) {
1862
- chunks.push(event.data);
1863
- }
1864
- };
1865
- recorder.onerror = () => {
1866
- window.clearTimeout(timeout);
1867
- reject(new Error("Live transcription recorder failed"));
1868
- };
1869
- recorder.onstop = () => {
1870
- window.clearTimeout(timeout);
1871
- this.liveTranscriptionRecorder = null;
1872
- resolve(
1873
- chunks.length ? new Blob(chunks, {
1874
- type: recorder.mimeType || mimeType || "audio/webm"
1875
- }) : null
1876
- );
1877
- };
1878
- recorder.start();
1879
- });
1808
+ console.info("[Transcript] server-authoritative live transcription is managed by the SFU");
1880
1809
  }
1881
- async runLiveTranscriptionLoop(stream, mimeType) {
1882
- while (this.liveTranscriptionActive && stream.getAudioTracks()[0]?.readyState === "live") {
1883
- try {
1884
- const blob = await this.captureLiveChunk(stream, mimeType);
1885
- if (!this.liveTranscriptionActive) {
1886
- break;
1887
- }
1888
- if (!blob || blob.size === 0) {
1889
- continue;
1810
+ stopLiveTranscription() {
1811
+ console.info("[Transcript] server-authoritative live transcription remains managed by the SFU");
1812
+ }
1813
+ isLiveTranscriptionActive() {
1814
+ return this.liveTranscriptionSeen;
1815
+ }
1816
+ async getTranscript(roomCode, sessionId) {
1817
+ if (!this.authToken) throw new Error("Authentication token is required");
1818
+ const query = sessionId ? `?session_id=${encodeURIComponent(sessionId)}` : "";
1819
+ const response = await fetch(
1820
+ `${this.apiBase}/rooms/${encodeURIComponent(roomCode)}/transcript${query}`,
1821
+ {
1822
+ method: "GET",
1823
+ headers: {
1824
+ Authorization: `Bearer ${this.authToken}`,
1825
+ Accept: "application/json"
1890
1826
  }
1891
- const sequence = this.liveTranscriptionSequence++;
1892
- const offsetMs = Math.max(
1893
- 0,
1894
- Date.now() - this.liveTranscriptionStartedAt
1895
- );
1896
- await this.uploadLiveTranscriptionChunk(
1897
- blob,
1898
- sequence,
1899
- offsetMs,
1900
- blob.type || mimeType
1901
- );
1902
- } catch (error) {
1903
- console.error(
1904
- "[Transcript] chunk processing failed",
1905
- error
1906
- );
1907
- this.onError(error);
1908
- await new Promise(
1909
- (resolve) => setTimeout(resolve, 750)
1910
- );
1911
1827
  }
1912
- }
1913
- }
1914
- async uploadLiveTranscriptionChunk(blob, sequence, offsetMs, mimeType) {
1915
- if (!this.room || !this.authToken) {
1916
- return;
1917
- }
1918
- const form = new FormData();
1919
- form.append("sequence", String(sequence));
1920
- form.append("offset_ms", String(offsetMs));
1921
- const extension = mimeType.includes("ogg") || blob.type.includes("ogg") ? "ogg" : "webm";
1922
- form.append(
1923
- "audio",
1924
- blob,
1925
- `live-${sequence}.${extension}`
1926
1828
  );
1927
- const url = `${this.apiBase}/rooms/${encodeURIComponent(this.room)}/live-transcription`;
1928
- console.log("[Transcript] POST", {
1929
- url,
1930
- sequence,
1931
- size: blob.size,
1932
- mimeType
1933
- });
1934
- const response = await fetch(url, {
1935
- method: "POST",
1936
- headers: {
1937
- Authorization: `Bearer ${this.authToken}`,
1938
- Accept: "application/json"
1939
- },
1940
- body: form
1941
- });
1942
- const responseText = await response.text();
1943
- console.log("[Transcript] response", {
1944
- status: response.status,
1945
- body: responseText
1946
- });
1829
+ const text = await response.text();
1947
1830
  if (!response.ok) {
1948
- throw new Error(
1949
- `Live transcription request failed: ${response.status} ${responseText}`
1950
- );
1831
+ throw new Error(`Transcript request failed: ${response.status} ${text}`);
1951
1832
  }
1833
+ return JSON.parse(text);
1952
1834
  }
1953
- stopLiveTranscription() {
1954
- console.log("[Transcript] stopping");
1955
- this.liveTranscriptionActive = false;
1956
- try {
1957
- if (this.liveTranscriptionRecorder && this.liveTranscriptionRecorder.state !== "inactive") {
1958
- this.liveTranscriptionRecorder.stop();
1835
+ async getMeetingSummary(roomCode, sessionId) {
1836
+ if (!this.authToken) throw new Error("Authentication token is required");
1837
+ const response = await fetch(
1838
+ `${this.apiBase}/rooms/${encodeURIComponent(roomCode)}/transcript/${encodeURIComponent(sessionId)}/summary`,
1839
+ {
1840
+ method: "GET",
1841
+ headers: {
1842
+ Authorization: `Bearer ${this.authToken}`,
1843
+ Accept: "application/json"
1844
+ }
1959
1845
  }
1960
- } catch (error) {
1961
- console.warn(
1962
- "[Transcript] recorder shutdown failed",
1963
- error
1964
- );
1846
+ );
1847
+ const text = await response.text();
1848
+ if (!response.ok) {
1849
+ throw new Error(`Meeting summary request failed: ${response.status} ${text}`);
1965
1850
  }
1966
- this.liveTranscriptionRecorder = null;
1967
- }
1968
- isLiveTranscriptionActive() {
1969
- return this.liveTranscriptionActive;
1851
+ return JSON.parse(text);
1970
1852
  }
1971
1853
  async applyNetworkRecommendation(action) {
1972
1854
  if (!this.videoSender || this.networkProfile === action) return;
@@ -2409,6 +2291,12 @@ var VideoSDKCore = class {
2409
2291
  async deleteRoom(roomCode) {
2410
2292
  return this.sfuClient.deleteRoom(roomCode);
2411
2293
  }
2294
+ async getTranscript(roomCode, sessionId) {
2295
+ return this.sfuClient.getTranscript(roomCode, sessionId);
2296
+ }
2297
+ async getMeetingSummary(roomCode, sessionId) {
2298
+ return this.sfuClient.getMeetingSummary(roomCode, sessionId);
2299
+ }
2412
2300
  // JOIN
2413
2301
  async joinMeeting(config) {
2414
2302
  const { roomId, name, audioMuted = false, videoMuted = false } = config;
@@ -3462,6 +3350,8 @@ var MeetingProvider = ({
3462
3350
  createRoom: sdk.createRoom.bind(sdk),
3463
3351
  getRoom: sdk.getRoom.bind(sdk),
3464
3352
  deleteRoom: sdk.deleteRoom.bind(sdk),
3353
+ getTranscript: sdk.getTranscript.bind(sdk),
3354
+ getMeetingSummary: sdk.getMeetingSummary.bind(sdk),
3465
3355
  toggleMic: sdk.toggleMic.bind(sdk),
3466
3356
  toggleCam: sdk.toggleCam.bind(sdk),
3467
3357
  startScreenShare: sdk.startScreenShare.bind(sdk),