@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 +56 -1
- package/dist/index.d.ts +56 -1
- package/dist/index.js +57 -167
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +57 -167
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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.
|
|
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())
|
|
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
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
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
|
|
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
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
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
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
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
|
-
|
|
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),
|