@defcomm/meetingsdk 0.5.5 → 0.5.7
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 +60 -2
- package/dist/index.d.ts +60 -2
- package/dist/index.js +384 -6
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +382 -6
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -197,6 +197,8 @@ var MeetingState = class {
|
|
|
197
197
|
this.presenterId = null;
|
|
198
198
|
this.activeSpeakerIds = /* @__PURE__ */ new Set();
|
|
199
199
|
this.recording = null;
|
|
200
|
+
this.liveTranscripts = [];
|
|
201
|
+
this.networkQuality = null;
|
|
200
202
|
this.listeners = /* @__PURE__ */ new Map();
|
|
201
203
|
}
|
|
202
204
|
// ---- reactive system ----
|
|
@@ -234,6 +236,21 @@ var MeetingState = class {
|
|
|
234
236
|
this.recording = null;
|
|
235
237
|
this.notify("recording");
|
|
236
238
|
}
|
|
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
|
+
setNetworkQuality(quality) {
|
|
251
|
+
this.networkQuality = quality;
|
|
252
|
+
this.notify("networkQuality");
|
|
253
|
+
}
|
|
237
254
|
// ---- participants ----
|
|
238
255
|
addParticipant(p) {
|
|
239
256
|
if (this.participants.has(p.id)) return false;
|
|
@@ -354,10 +371,14 @@ var MeetingState = class {
|
|
|
354
371
|
this.chatMessages.clear();
|
|
355
372
|
this.presenterId = null;
|
|
356
373
|
this.activeSpeakerIds = /* @__PURE__ */ new Set();
|
|
374
|
+
this.liveTranscripts = [];
|
|
375
|
+
this.networkQuality = null;
|
|
357
376
|
this.notify("participants");
|
|
358
377
|
this.notify("chat");
|
|
359
378
|
this.notify("presenter");
|
|
360
379
|
this.notify("activeSpeakers");
|
|
380
|
+
this.notify("transcript");
|
|
381
|
+
this.notify("networkQuality");
|
|
361
382
|
}
|
|
362
383
|
};
|
|
363
384
|
|
|
@@ -419,6 +440,12 @@ var SFUClient = class {
|
|
|
419
440
|
this.trackToSource = /* @__PURE__ */ new Map();
|
|
420
441
|
this.localTrackIds = /* @__PURE__ */ new Map();
|
|
421
442
|
this.subscriberStatsTimer = null;
|
|
443
|
+
this.liveTranscriptionActive = false;
|
|
444
|
+
this.liveTranscriptionSequence = 0;
|
|
445
|
+
this.liveTranscriptionStartedAt = 0;
|
|
446
|
+
this.liveTranscriptionRecorder = null;
|
|
447
|
+
this.liveTranscriptionLoopPromise = null;
|
|
448
|
+
this.networkProfile = null;
|
|
422
449
|
this.heartbeatTimer = null;
|
|
423
450
|
this.apiBase = options.apiBase || "https://api.example.com/api";
|
|
424
451
|
this.wsBase = options.wsBase || "wss://api.example.com/ws";
|
|
@@ -475,6 +502,10 @@ var SFUClient = class {
|
|
|
475
502
|
});
|
|
476
503
|
this.onRoomUpdate = options.onRoomUpdate || (() => {
|
|
477
504
|
});
|
|
505
|
+
this.onLiveTranscript = options.onLiveTranscript || (() => {
|
|
506
|
+
});
|
|
507
|
+
this.onNetworkQuality = options.onNetworkQuality || (() => {
|
|
508
|
+
});
|
|
478
509
|
this.onParticipantMediaState = options.onParticipantMediaState || (() => {
|
|
479
510
|
});
|
|
480
511
|
this.autoSubscribe = options.autoSubscribe !== false;
|
|
@@ -1069,6 +1100,40 @@ var SFUClient = class {
|
|
|
1069
1100
|
if (reaction?.id && reaction?.emoji) this.onReaction(reaction);
|
|
1070
1101
|
break;
|
|
1071
1102
|
}
|
|
1103
|
+
case "live_transcript": {
|
|
1104
|
+
const transcript = {
|
|
1105
|
+
transcript_id: String(payload.transcript_id),
|
|
1106
|
+
participant_id: String(payload.participant_id),
|
|
1107
|
+
speaker_name: payload.speaker_name ?? null,
|
|
1108
|
+
sequence: Number(payload.sequence ?? 0),
|
|
1109
|
+
text: String(payload.text ?? ""),
|
|
1110
|
+
offset_ms: payload.offset_ms == null ? null : Number(payload.offset_ms),
|
|
1111
|
+
start_ms: payload.start_ms == null ? null : Number(payload.start_ms),
|
|
1112
|
+
end_ms: payload.end_ms == null ? null : Number(payload.end_ms),
|
|
1113
|
+
timestamp: String(payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString())
|
|
1114
|
+
};
|
|
1115
|
+
if (transcript.text.trim()) this.onLiveTranscript(transcript);
|
|
1116
|
+
break;
|
|
1117
|
+
}
|
|
1118
|
+
case "network_quality": {
|
|
1119
|
+
const raw = payload.quality || payload;
|
|
1120
|
+
const quality = {
|
|
1121
|
+
participant_id: String(raw.participant_id ?? this.currentParticipantId ?? this.participantId),
|
|
1122
|
+
quality: String(raw.quality ?? "unknown"),
|
|
1123
|
+
score: Number(raw.score ?? 0),
|
|
1124
|
+
recommended_action: String(raw.recommended_action ?? "maintain"),
|
|
1125
|
+
packet_loss_percent: Number(raw.packet_loss_percent ?? 0),
|
|
1126
|
+
upload_kbps: Number(raw.upload_kbps ?? 0),
|
|
1127
|
+
forwarded_drop_percent: Number(raw.forwarded_drop_percent ?? 0),
|
|
1128
|
+
queued_packets: Number(raw.queued_packets ?? 0),
|
|
1129
|
+
packets_received: Number(raw.packets_received ?? 0),
|
|
1130
|
+
packets_lost: Number(raw.packets_lost ?? 0),
|
|
1131
|
+
observed_at: String(raw.observed_at ?? (/* @__PURE__ */ new Date()).toISOString())
|
|
1132
|
+
};
|
|
1133
|
+
this.onNetworkQuality(quality);
|
|
1134
|
+
void this.applyNetworkRecommendation(quality.recommended_action);
|
|
1135
|
+
break;
|
|
1136
|
+
}
|
|
1072
1137
|
case "error": {
|
|
1073
1138
|
const message2 = payload.message || "SFU signaling error";
|
|
1074
1139
|
if (this.state === "reconnecting" && message2.includes("participant session is stale")) {
|
|
@@ -1728,6 +1793,208 @@ var SFUClient = class {
|
|
|
1728
1793
|
}
|
|
1729
1794
|
});
|
|
1730
1795
|
}
|
|
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
|
+
}
|
|
1809
|
+
async startLiveTranscription() {
|
|
1810
|
+
if (this.liveTranscriptionActive) {
|
|
1811
|
+
return;
|
|
1812
|
+
}
|
|
1813
|
+
if (!this.room) {
|
|
1814
|
+
throw new Error("Cannot start live transcription without a room");
|
|
1815
|
+
}
|
|
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
|
+
});
|
|
1880
|
+
}
|
|
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;
|
|
1890
|
+
}
|
|
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
|
+
}
|
|
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
|
+
);
|
|
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
|
+
});
|
|
1947
|
+
if (!response.ok) {
|
|
1948
|
+
throw new Error(
|
|
1949
|
+
`Live transcription request failed: ${response.status} ${responseText}`
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
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();
|
|
1959
|
+
}
|
|
1960
|
+
} catch (error) {
|
|
1961
|
+
console.warn(
|
|
1962
|
+
"[Transcript] recorder shutdown failed",
|
|
1963
|
+
error
|
|
1964
|
+
);
|
|
1965
|
+
}
|
|
1966
|
+
this.liveTranscriptionRecorder = null;
|
|
1967
|
+
}
|
|
1968
|
+
isLiveTranscriptionActive() {
|
|
1969
|
+
return this.liveTranscriptionActive;
|
|
1970
|
+
}
|
|
1971
|
+
async applyNetworkRecommendation(action) {
|
|
1972
|
+
if (!this.videoSender || this.networkProfile === action) return;
|
|
1973
|
+
const params = this.videoSender.getParameters();
|
|
1974
|
+
const encodings = params.encodings?.length ? params.encodings : [{}];
|
|
1975
|
+
const encoding = encodings[0];
|
|
1976
|
+
switch (action) {
|
|
1977
|
+
case "reduce_video":
|
|
1978
|
+
encoding.maxBitrate = 7e5;
|
|
1979
|
+
encoding.maxFramerate = 20;
|
|
1980
|
+
break;
|
|
1981
|
+
case "audio_priority":
|
|
1982
|
+
encoding.maxBitrate = 35e4;
|
|
1983
|
+
encoding.maxFramerate = 10;
|
|
1984
|
+
break;
|
|
1985
|
+
default:
|
|
1986
|
+
encoding.maxBitrate = 15e5;
|
|
1987
|
+
encoding.maxFramerate = 30;
|
|
1988
|
+
break;
|
|
1989
|
+
}
|
|
1990
|
+
params.encodings = encodings;
|
|
1991
|
+
try {
|
|
1992
|
+
await this.videoSender.setParameters(params);
|
|
1993
|
+
this.networkProfile = action;
|
|
1994
|
+
} catch (error) {
|
|
1995
|
+
this.onError(error);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1731
1998
|
async startRecording() {
|
|
1732
1999
|
if (!this.send({ type: "start_recording" })) {
|
|
1733
2000
|
throw new Error("SFU signaling is not connected");
|
|
@@ -1860,6 +2127,7 @@ var SFUClient = class {
|
|
|
1860
2127
|
}
|
|
1861
2128
|
}
|
|
1862
2129
|
async leave() {
|
|
2130
|
+
this.stopLiveTranscription();
|
|
1863
2131
|
this.stopHeartbeat();
|
|
1864
2132
|
this.intentionalClose = true;
|
|
1865
2133
|
this.setState("leaving");
|
|
@@ -2268,6 +2536,14 @@ var VideoSDKCore = class {
|
|
|
2268
2536
|
console.warn(`[SFU] Reconnecting, attempt ${attempt}`);
|
|
2269
2537
|
},
|
|
2270
2538
|
// ADMISSION CONTROL
|
|
2539
|
+
onLiveTranscript: (transcript) => {
|
|
2540
|
+
this.state.addLiveTranscript(transcript);
|
|
2541
|
+
this.events.onLiveTranscript?.(transcript);
|
|
2542
|
+
},
|
|
2543
|
+
onNetworkQuality: (quality) => {
|
|
2544
|
+
this.state.setNetworkQuality(quality);
|
|
2545
|
+
this.events.onNetworkQuality?.(quality);
|
|
2546
|
+
},
|
|
2271
2547
|
onRoomUpdate: (payload) => {
|
|
2272
2548
|
const participants = payload?.participants || [];
|
|
2273
2549
|
const local = participants.find(
|
|
@@ -2912,6 +3188,25 @@ var VideoSDKCore = class {
|
|
|
2912
3188
|
}
|
|
2913
3189
|
}
|
|
2914
3190
|
}
|
|
3191
|
+
async startLiveTranscription() {
|
|
3192
|
+
if (!this.sfuClient) {
|
|
3193
|
+
throw new Error("SFU client is not connected");
|
|
3194
|
+
}
|
|
3195
|
+
const audioTrack = this.localStream?.getAudioTracks()[0];
|
|
3196
|
+
if (!audioTrack || audioTrack.readyState !== "live") {
|
|
3197
|
+
throw new Error("Microphone audio track is not available");
|
|
3198
|
+
}
|
|
3199
|
+
await this.sfuClient.startLiveTranscription();
|
|
3200
|
+
}
|
|
3201
|
+
stopLiveTranscription() {
|
|
3202
|
+
this.sfuClient?.stopLiveTranscription();
|
|
3203
|
+
}
|
|
3204
|
+
isLiveTranscriptionActive() {
|
|
3205
|
+
return this.sfuClient?.isLiveTranscriptionActive() ?? false;
|
|
3206
|
+
}
|
|
3207
|
+
getNetworkQuality() {
|
|
3208
|
+
return this.state.networkQuality;
|
|
3209
|
+
}
|
|
2915
3210
|
// CLEANUP
|
|
2916
3211
|
cleanupRemoteParticipant(participantId) {
|
|
2917
3212
|
this.activeSpeakerDetector.removeParticipant(participantId);
|
|
@@ -2960,6 +3255,8 @@ var VideoSDKCore = class {
|
|
|
2960
3255
|
this.room.id = null;
|
|
2961
3256
|
this.room.name = null;
|
|
2962
3257
|
this.state.localParticipant = null;
|
|
3258
|
+
this.state.clearLiveTranscripts();
|
|
3259
|
+
this.state.setNetworkQuality(null);
|
|
2963
3260
|
this.state.notify("localParticipant");
|
|
2964
3261
|
this.state.resetRemoteState();
|
|
2965
3262
|
this.events.onMeetingLeft?.();
|
|
@@ -2992,9 +3289,15 @@ var MeetingProvider = ({
|
|
|
2992
3289
|
const entryResponseListeners = useRef(
|
|
2993
3290
|
/* @__PURE__ */ new Set()
|
|
2994
3291
|
);
|
|
2995
|
-
const joinRequestCanceledListeners = useRef(
|
|
2996
|
-
|
|
2997
|
-
|
|
3292
|
+
const joinRequestCanceledListeners = useRef(
|
|
3293
|
+
/* @__PURE__ */ new Set()
|
|
3294
|
+
);
|
|
3295
|
+
const participantRemovedListeners = useRef(
|
|
3296
|
+
/* @__PURE__ */ new Set()
|
|
3297
|
+
);
|
|
3298
|
+
const participantPermissionsChangedListeners = useRef(
|
|
3299
|
+
/* @__PURE__ */ new Set()
|
|
3300
|
+
);
|
|
2998
3301
|
const meetingLeftListeners = useRef(/* @__PURE__ */ new Set());
|
|
2999
3302
|
const trackStateListeners = useRef(/* @__PURE__ */ new Set());
|
|
3000
3303
|
const recordingStartedListeners = useRef(/* @__PURE__ */ new Set());
|
|
@@ -3007,6 +3310,12 @@ var MeetingProvider = ({
|
|
|
3007
3310
|
const activeSpeakerListeners = useRef(
|
|
3008
3311
|
/* @__PURE__ */ new Set()
|
|
3009
3312
|
);
|
|
3313
|
+
const liveTranscriptListeners = useRef(
|
|
3314
|
+
/* @__PURE__ */ new Set()
|
|
3315
|
+
);
|
|
3316
|
+
const networkQualityListeners = useRef(
|
|
3317
|
+
/* @__PURE__ */ new Set()
|
|
3318
|
+
);
|
|
3010
3319
|
const connectionStateListeners = useRef(
|
|
3011
3320
|
/* @__PURE__ */ new Set()
|
|
3012
3321
|
);
|
|
@@ -3030,10 +3339,14 @@ var MeetingProvider = ({
|
|
|
3030
3339
|
joinRequestCanceledListeners.current.forEach((fn) => fn(requestId));
|
|
3031
3340
|
},
|
|
3032
3341
|
onParticipantRemoved: (participantId, removedBy) => {
|
|
3033
|
-
participantRemovedListeners.current.forEach(
|
|
3342
|
+
participantRemovedListeners.current.forEach(
|
|
3343
|
+
(fn) => fn(participantId, removedBy)
|
|
3344
|
+
);
|
|
3034
3345
|
},
|
|
3035
3346
|
onParticipantPermissionsChanged: (participantId, permissions) => {
|
|
3036
|
-
participantPermissionsChangedListeners.current.forEach(
|
|
3347
|
+
participantPermissionsChangedListeners.current.forEach(
|
|
3348
|
+
(fn) => fn(participantId, permissions)
|
|
3349
|
+
);
|
|
3037
3350
|
},
|
|
3038
3351
|
onMeetingLeft: () => {
|
|
3039
3352
|
meetingLeftListeners.current.forEach((fn) => fn());
|
|
@@ -3086,6 +3399,12 @@ var MeetingProvider = ({
|
|
|
3086
3399
|
);
|
|
3087
3400
|
pubSubListeners.current.get("REACTION")?.forEach((fn) => fn(message));
|
|
3088
3401
|
},
|
|
3402
|
+
onLiveTranscript: (transcript) => {
|
|
3403
|
+
liveTranscriptListeners.current.forEach((fn) => fn(transcript));
|
|
3404
|
+
},
|
|
3405
|
+
onNetworkQuality: (quality) => {
|
|
3406
|
+
networkQualityListeners.current.forEach((fn) => fn(quality));
|
|
3407
|
+
},
|
|
3089
3408
|
onConnectionStateChanged: (state) => {
|
|
3090
3409
|
connectionStateListeners.current.forEach(
|
|
3091
3410
|
(fn) => fn(state)
|
|
@@ -3121,6 +3440,16 @@ var MeetingProvider = ({
|
|
|
3121
3440
|
"chat",
|
|
3122
3441
|
(s) => s.getChatMessages()
|
|
3123
3442
|
);
|
|
3443
|
+
const liveTranscripts = useMeetingStore(
|
|
3444
|
+
sdk.state,
|
|
3445
|
+
"transcript",
|
|
3446
|
+
(s) => s.getLiveTranscripts()
|
|
3447
|
+
);
|
|
3448
|
+
const networkQuality = useMeetingStore(
|
|
3449
|
+
sdk.state,
|
|
3450
|
+
"networkQuality",
|
|
3451
|
+
(s) => s.networkQuality
|
|
3452
|
+
);
|
|
3124
3453
|
const value = useMemo(() => {
|
|
3125
3454
|
return {
|
|
3126
3455
|
sdk,
|
|
@@ -3145,6 +3474,11 @@ var MeetingProvider = ({
|
|
|
3145
3474
|
messages,
|
|
3146
3475
|
presenterId,
|
|
3147
3476
|
activeSpeakerIds,
|
|
3477
|
+
liveTranscripts,
|
|
3478
|
+
networkQuality,
|
|
3479
|
+
startLiveTranscription: sdk.startLiveTranscription.bind(sdk),
|
|
3480
|
+
stopLiveTranscription: sdk.stopLiveTranscription.bind(sdk),
|
|
3481
|
+
isLiveTranscriptionActive: sdk.isLiveTranscriptionActive.bind(sdk),
|
|
3148
3482
|
recording,
|
|
3149
3483
|
usePubSub: (topic) => {
|
|
3150
3484
|
if (topic === "SECURE_CHAT") {
|
|
@@ -3263,6 +3597,14 @@ var MeetingProvider = ({
|
|
|
3263
3597
|
reactionListeners.current.add(cb);
|
|
3264
3598
|
return () => reactionListeners.current.delete(cb);
|
|
3265
3599
|
},
|
|
3600
|
+
onLiveTranscript: (cb) => {
|
|
3601
|
+
liveTranscriptListeners.current.add(cb);
|
|
3602
|
+
return () => liveTranscriptListeners.current.delete(cb);
|
|
3603
|
+
},
|
|
3604
|
+
onNetworkQuality: (cb) => {
|
|
3605
|
+
networkQualityListeners.current.add(cb);
|
|
3606
|
+
return () => networkQualityListeners.current.delete(cb);
|
|
3607
|
+
},
|
|
3266
3608
|
onConnectionStateChanged: (cb) => {
|
|
3267
3609
|
connectionStateListeners.current.add(cb);
|
|
3268
3610
|
return () => {
|
|
@@ -3285,6 +3627,8 @@ var MeetingProvider = ({
|
|
|
3285
3627
|
presenterId,
|
|
3286
3628
|
recording,
|
|
3287
3629
|
activeSpeakerIds,
|
|
3630
|
+
liveTranscripts,
|
|
3631
|
+
networkQuality,
|
|
3288
3632
|
reactionPubSubMessages
|
|
3289
3633
|
]);
|
|
3290
3634
|
return /* @__PURE__ */ jsx(MeetingContext.Provider, { value, children });
|
|
@@ -3369,7 +3713,9 @@ var useMeeting = (handlers) => {
|
|
|
3369
3713
|
}, [handlers?.onParticipantRemoved]);
|
|
3370
3714
|
useEffect3(() => {
|
|
3371
3715
|
if (!handlers?.onParticipantPermissionsChanged) return;
|
|
3372
|
-
return ctx.onParticipantPermissionsChanged(
|
|
3716
|
+
return ctx.onParticipantPermissionsChanged(
|
|
3717
|
+
handlers.onParticipantPermissionsChanged
|
|
3718
|
+
);
|
|
3373
3719
|
}, [handlers?.onParticipantPermissionsChanged]);
|
|
3374
3720
|
useEffect3(() => {
|
|
3375
3721
|
if (!handlers?.onMeetingLeft) return;
|
|
@@ -3391,6 +3737,14 @@ var useMeeting = (handlers) => {
|
|
|
3391
3737
|
if (!handlers?.onConnectionStateChanged) return;
|
|
3392
3738
|
return ctx.onConnectionStateChanged(handlers.onConnectionStateChanged);
|
|
3393
3739
|
}, [handlers?.onConnectionStateChanged]);
|
|
3740
|
+
useEffect3(() => {
|
|
3741
|
+
if (!handlers?.onLiveTranscript) return;
|
|
3742
|
+
return ctx.onLiveTranscript(handlers.onLiveTranscript);
|
|
3743
|
+
}, [handlers?.onLiveTranscript]);
|
|
3744
|
+
useEffect3(() => {
|
|
3745
|
+
if (!handlers?.onNetworkQuality) return;
|
|
3746
|
+
return ctx.onNetworkQuality(handlers.onNetworkQuality);
|
|
3747
|
+
}, [handlers?.onNetworkQuality]);
|
|
3394
3748
|
useEffect3(() => {
|
|
3395
3749
|
if (!handlers?.onReaction) return;
|
|
3396
3750
|
return ctx.onReaction(handlers.onReaction);
|
|
@@ -3528,6 +3882,26 @@ function useMeetingPreview(roomId, participantId) {
|
|
|
3528
3882
|
};
|
|
3529
3883
|
}
|
|
3530
3884
|
|
|
3885
|
+
// src/react/useLiveTranscript.ts
|
|
3886
|
+
var useLiveTranscript = () => {
|
|
3887
|
+
const { sdk } = useMeetingContext();
|
|
3888
|
+
return useMeetingStore(
|
|
3889
|
+
sdk.state,
|
|
3890
|
+
"transcript",
|
|
3891
|
+
(state) => state.getLiveTranscripts()
|
|
3892
|
+
);
|
|
3893
|
+
};
|
|
3894
|
+
|
|
3895
|
+
// src/react/useNetworkQuality.ts
|
|
3896
|
+
var useNetworkQuality = () => {
|
|
3897
|
+
const { sdk } = useMeetingContext();
|
|
3898
|
+
return useMeetingStore(
|
|
3899
|
+
sdk.state,
|
|
3900
|
+
"networkQuality",
|
|
3901
|
+
(state) => state.networkQuality
|
|
3902
|
+
);
|
|
3903
|
+
};
|
|
3904
|
+
|
|
3531
3905
|
// src/react/usePubSub.ts
|
|
3532
3906
|
import { useEffect as useEffect7 } from "react";
|
|
3533
3907
|
function usePubSub(topic, options) {
|
|
@@ -3546,10 +3920,12 @@ export {
|
|
|
3546
3920
|
MeetingProvider,
|
|
3547
3921
|
MeetingState,
|
|
3548
3922
|
VideoSDKCore,
|
|
3923
|
+
useLiveTranscript,
|
|
3549
3924
|
useLocalParticipant,
|
|
3550
3925
|
useMeeting,
|
|
3551
3926
|
useMeetingContext,
|
|
3552
3927
|
useMeetingPreview,
|
|
3928
|
+
useNetworkQuality,
|
|
3553
3929
|
useParticipants,
|
|
3554
3930
|
usePubSub,
|
|
3555
3931
|
useRemoteMedia
|