@vivix-ai/ivi-frontend-sdk 0.3.7 → 0.3.9
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/README.md +194 -1
- package/dist/index.cjs +708 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +152 -2
- package/dist/index.d.ts +152 -2
- package/dist/index.js +702 -47
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ReceiveConversationItemAddedEvent, ReceiveConversationItemDoneEvent, ReceiveResponseCreatedEvent, IviClient, ReceiveSessionCreatedEvent, ReceiveSessionEndedEvent, ReceiveSessionStageGetResponseEvent, ReceiveSessionStageUpdatedEvent, ReceiveSessionTrackCreatedEvent, ReceiveSessionTrackDeletedEvent, ReceiveSessionTrackTookEvent, ReceiveSessionTrackCuedEvent, ReceiveSessionTrackNextSetEvent, ReceiveSessionTracksListResponseEvent, ReceiveSessionSourceCreatedEvent, ReceiveSessionSourceReadyEvent, ReceiveSessionSourceFailedEvent, ReceiveSessionSourceDeletedEvent, ReceiveSessionSourcePreloadEvent, ReceiveSessionSourceClearPreloadEvent, ReceiveSessionSourcesListResponseEvent, ReceiveSessionSourcePlaybackCompletedEvent, ReceiveSessionStreamCreatedEvent, ReceiveSessionStreamStartedEvent, ReceiveSessionStreamEndedEvent, ReceiveSessionStreamFailedEvent, ReceiveSessionStreamDeletedEvent, ReceiveSessionStreamsListResponseEvent, ReceiveConversationListResponseEvent, ReceiveResponseOutputTextDeltaEvent, ReceiveResponseOutputTextDoneEvent, ReceiveResponseOutputAudioTranscriptDeltaEvent, ReceiveResponseOutputAudioTranscriptDoneEvent, ReceiveResponseDoneEvent } from '@vivix-ai/ivi-sdk-ts';
|
|
2
|
+
import { WebSocketTransport } from '@vivix-ai/ivi-sdk-ts/transports/websocket';
|
|
2
3
|
import { createContext, useState, useEffect, useMemo, useRef, useContext, useCallback, useId } from 'react';
|
|
3
4
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
4
5
|
|
|
@@ -620,7 +621,9 @@ var SourceManager = class {
|
|
|
620
621
|
durationMs: previous?.durationMs,
|
|
621
622
|
hasAudio: previous?.hasAudio,
|
|
622
623
|
error: previous?.status === "failed" ? previous.error : void 0,
|
|
623
|
-
preload: previous?.preload
|
|
624
|
+
preload: previous?.preload,
|
|
625
|
+
origin: previous?.origin,
|
|
626
|
+
provisional: previous?.provisional ? false : previous?.provisional
|
|
624
627
|
});
|
|
625
628
|
this.sources = next;
|
|
626
629
|
}
|
|
@@ -635,7 +638,38 @@ var SourceManager = class {
|
|
|
635
638
|
height: payload.height,
|
|
636
639
|
durationMs: payload.durationMs,
|
|
637
640
|
hasAudio: payload.hasAudio,
|
|
638
|
-
preload: previous?.preload
|
|
641
|
+
preload: previous?.preload,
|
|
642
|
+
origin: previous?.origin,
|
|
643
|
+
provisional: previous?.provisional ? false : previous?.provisional
|
|
644
|
+
});
|
|
645
|
+
this.sources = next;
|
|
646
|
+
}
|
|
647
|
+
upsertBootstrapReady(payload) {
|
|
648
|
+
const previous = this.sources.get(payload.sourceId);
|
|
649
|
+
const next = new Map(this.sources);
|
|
650
|
+
const source = {
|
|
651
|
+
source_id: payload.sourceId,
|
|
652
|
+
kind: payload.source?.kind ?? (payload.streamId ? "generation_stream" : "stream"),
|
|
653
|
+
stream_id: payload.source?.stream_id ?? payload.streamId,
|
|
654
|
+
asset_type: payload.source?.asset_type ?? "video",
|
|
655
|
+
url: payload.source?.url,
|
|
656
|
+
generation: payload.source?.generation,
|
|
657
|
+
metadata: {
|
|
658
|
+
...payload.source?.metadata ?? {},
|
|
659
|
+
label: payload.source?.metadata?.label ?? "prebuilt"
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
next.set(payload.sourceId, {
|
|
663
|
+
source,
|
|
664
|
+
status: "ready",
|
|
665
|
+
playback: previous?.playback ?? payload.playback,
|
|
666
|
+
width: previous?.width ?? payload.width,
|
|
667
|
+
height: previous?.height ?? payload.height,
|
|
668
|
+
durationMs: previous?.durationMs ?? payload.durationMs,
|
|
669
|
+
hasAudio: previous?.hasAudio ?? payload.hasAudio,
|
|
670
|
+
preload: previous?.preload ?? { autoclearAfterPlay: true },
|
|
671
|
+
origin: previous?.origin ?? "prebuilt-session-response",
|
|
672
|
+
provisional: previous?.provisional ?? true
|
|
639
673
|
});
|
|
640
674
|
this.sources = next;
|
|
641
675
|
}
|
|
@@ -669,11 +703,14 @@ var SourceManager = class {
|
|
|
669
703
|
const next = /* @__PURE__ */ new Map();
|
|
670
704
|
sources.forEach((source) => {
|
|
671
705
|
const previous = this.sources.get(source.source_id);
|
|
706
|
+
const playback = source.playback ?? previous?.playback;
|
|
672
707
|
next.set(source.source_id, {
|
|
673
708
|
source,
|
|
674
|
-
status:
|
|
675
|
-
playback
|
|
676
|
-
preload: previous?.preload
|
|
709
|
+
status: playback ? "ready" : "created",
|
|
710
|
+
playback,
|
|
711
|
+
preload: previous?.preload,
|
|
712
|
+
origin: previous?.origin,
|
|
713
|
+
provisional: source.playback && previous?.provisional ? false : previous?.provisional
|
|
677
714
|
});
|
|
678
715
|
});
|
|
679
716
|
this.sources = next;
|
|
@@ -1105,6 +1142,8 @@ var ConversationManager = class {
|
|
|
1105
1142
|
|
|
1106
1143
|
// src/runtime/managers/trtc-source-manager.ts
|
|
1107
1144
|
var TAG = "[IVI-TRTC]";
|
|
1145
|
+
var TRTC_VIEW_ATTR = "data-ivi-trtc-view";
|
|
1146
|
+
var TRTC_MEDIA_STYLE_ID = "ivi-trtc-media-style";
|
|
1108
1147
|
var DEFAULT_DENOISER_OPTIONS = {
|
|
1109
1148
|
enabled: true,
|
|
1110
1149
|
mode: "normal"
|
|
@@ -1266,6 +1305,8 @@ var TrtcSourceManager = class {
|
|
|
1266
1305
|
if (!session) {
|
|
1267
1306
|
throw new Error(`TRTC source session not found: ${sourceId}`);
|
|
1268
1307
|
}
|
|
1308
|
+
ensureTrtcMediaStyleSheet();
|
|
1309
|
+
container.setAttribute(TRTC_VIEW_ATTR, "");
|
|
1269
1310
|
session.views.set(viewId, {
|
|
1270
1311
|
sourceId,
|
|
1271
1312
|
container,
|
|
@@ -1273,6 +1314,7 @@ var TrtcSourceManager = class {
|
|
|
1273
1314
|
startedVideoKeys: /* @__PURE__ */ new Set(),
|
|
1274
1315
|
startingVideoKeys: /* @__PURE__ */ new Set()
|
|
1275
1316
|
});
|
|
1317
|
+
this.log("info", `\u7ED1\u5B9A\u89C6\u56FE source=${sourceId} view=${viewId} views=${session.views.size}`);
|
|
1276
1318
|
await this.ensureConnected(session);
|
|
1277
1319
|
const binding = session.views.get(viewId);
|
|
1278
1320
|
if (!binding) {
|
|
@@ -1288,6 +1330,8 @@ var TrtcSourceManager = class {
|
|
|
1288
1330
|
}
|
|
1289
1331
|
const binding = session.views.get(viewId);
|
|
1290
1332
|
if (binding?.sourceId === sourceId) {
|
|
1333
|
+
this.log("info", `\u89E3\u7ED1\u89C6\u56FE source=${sourceId} view=${viewId}`);
|
|
1334
|
+
binding.container.removeAttribute(TRTC_VIEW_ATTR);
|
|
1291
1335
|
session.views.delete(viewId);
|
|
1292
1336
|
}
|
|
1293
1337
|
void this.applyAudioPolicy(session);
|
|
@@ -1304,11 +1348,30 @@ var TrtcSourceManager = class {
|
|
|
1304
1348
|
binding.muted = muted;
|
|
1305
1349
|
void this.applyAudioPolicy(session);
|
|
1306
1350
|
}
|
|
1351
|
+
reassignView(fromSourceId, toSourceId, viewId) {
|
|
1352
|
+
if (fromSourceId === toSourceId) {
|
|
1353
|
+
return true;
|
|
1354
|
+
}
|
|
1355
|
+
const fromSession = this.getSession(fromSourceId);
|
|
1356
|
+
const toSession = this.getSession(toSourceId);
|
|
1357
|
+
if (!fromSession || !toSession || fromSession !== toSession) {
|
|
1358
|
+
return false;
|
|
1359
|
+
}
|
|
1360
|
+
const binding = fromSession.views.get(viewId);
|
|
1361
|
+
if (!binding || binding.sourceId !== fromSourceId) {
|
|
1362
|
+
return false;
|
|
1363
|
+
}
|
|
1364
|
+
this.log("info", `\u8FC1\u79FB\u89C6\u56FE source=${fromSourceId}->${toSourceId} view=${viewId}`);
|
|
1365
|
+
binding.sourceId = toSourceId;
|
|
1366
|
+
void this.applyAudioPolicy(fromSession);
|
|
1367
|
+
return true;
|
|
1368
|
+
}
|
|
1307
1369
|
unlinkSourceFromSession(sourceId, session) {
|
|
1308
1370
|
session.sourceIds.delete(sourceId);
|
|
1309
1371
|
this.sourceSessionKeys.delete(sourceId);
|
|
1310
1372
|
session.views.forEach((binding, viewId) => {
|
|
1311
1373
|
if (binding.sourceId === sourceId) {
|
|
1374
|
+
binding.container.removeAttribute(TRTC_VIEW_ATTR);
|
|
1312
1375
|
session.views.delete(viewId);
|
|
1313
1376
|
}
|
|
1314
1377
|
});
|
|
@@ -1477,6 +1540,7 @@ var TrtcSourceManager = class {
|
|
|
1477
1540
|
if (!parsed) {
|
|
1478
1541
|
continue;
|
|
1479
1542
|
}
|
|
1543
|
+
this.log("info", `\u56DE\u653E\u5DF2\u77E5\u8FDC\u7AEF\u89C6\u9891 source=${binding.sourceId} userId=${parsed.userId} streamType=${parsed.streamType}`);
|
|
1480
1544
|
await this.startRemoteVideoForBinding(
|
|
1481
1545
|
client,
|
|
1482
1546
|
parsed.userId,
|
|
@@ -1492,12 +1556,14 @@ var TrtcSourceManager = class {
|
|
|
1492
1556
|
}
|
|
1493
1557
|
binding.startingVideoKeys.add(remoteVideoKey);
|
|
1494
1558
|
try {
|
|
1559
|
+
this.log("info", `\u5F00\u59CB\u6E32\u67D3\u8FDC\u7AEF\u89C6\u9891 source=${binding.sourceId} view=${remoteVideoKey} userId=${userId} streamType=${streamType}`);
|
|
1495
1560
|
await client.startRemoteVideo({
|
|
1496
1561
|
userId,
|
|
1497
1562
|
streamType,
|
|
1498
1563
|
view: binding.container,
|
|
1499
1564
|
option: { fillMode: "contain" }
|
|
1500
1565
|
});
|
|
1566
|
+
this.log("info", `\u8FDC\u7AEF\u89C6\u9891\u6E32\u67D3\u5B8C\u6210 source=${binding.sourceId} userId=${userId} streamType=${streamType}`);
|
|
1501
1567
|
binding.startedVideoKeys.add(remoteVideoKey);
|
|
1502
1568
|
enforceContainMedia(binding.container);
|
|
1503
1569
|
} catch (error) {
|
|
@@ -1628,11 +1694,43 @@ var TrtcSourceManager = class {
|
|
|
1628
1694
|
this.onTrtcEvent?.(event);
|
|
1629
1695
|
}
|
|
1630
1696
|
};
|
|
1697
|
+
function ensureTrtcMediaStyleSheet() {
|
|
1698
|
+
if (typeof document === "undefined") {
|
|
1699
|
+
return;
|
|
1700
|
+
}
|
|
1701
|
+
if (document.getElementById(TRTC_MEDIA_STYLE_ID)) {
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
const style = document.createElement("style");
|
|
1705
|
+
style.id = TRTC_MEDIA_STYLE_ID;
|
|
1706
|
+
style.textContent = `
|
|
1707
|
+
[${TRTC_VIEW_ATTR}] [id^="player_"],
|
|
1708
|
+
[${TRTC_VIEW_ATTR}] [id^="video_"],
|
|
1709
|
+
[${TRTC_VIEW_ATTR}] [id^="audio_"],
|
|
1710
|
+
[${TRTC_VIEW_ATTR}] video,
|
|
1711
|
+
[${TRTC_VIEW_ATTR}] canvas {
|
|
1712
|
+
width: 100% !important;
|
|
1713
|
+
height: 100% !important;
|
|
1714
|
+
max-width: 100% !important;
|
|
1715
|
+
max-height: 100% !important;
|
|
1716
|
+
background: transparent !important;
|
|
1717
|
+
background-color: transparent !important;
|
|
1718
|
+
background-image: none !important;
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
[${TRTC_VIEW_ATTR}] video,
|
|
1722
|
+
[${TRTC_VIEW_ATTR}] canvas {
|
|
1723
|
+
object-fit: contain !important;
|
|
1724
|
+
display: block !important;
|
|
1725
|
+
}
|
|
1726
|
+
`;
|
|
1727
|
+
document.head.appendChild(style);
|
|
1728
|
+
}
|
|
1631
1729
|
function isRuntimeTrtcSource(source) {
|
|
1632
1730
|
return source.status === "ready" && source.playback?.type === "trtc" && typeof source.playback.trtc === "object" && source.playback.trtc !== null;
|
|
1633
1731
|
}
|
|
1634
1732
|
function isSameTrtcConfig(a, b) {
|
|
1635
|
-
return a.app_id === b.app_id && a.user_id === b.user_id && a.
|
|
1733
|
+
return a.app_id === b.app_id && a.user_id === b.user_id && a.room_id === b.room_id;
|
|
1636
1734
|
}
|
|
1637
1735
|
function buildTrtcSessionKey(trtc, aiDenoiser) {
|
|
1638
1736
|
const denoiser = resolveAIDenoiserOptions(aiDenoiser);
|
|
@@ -1640,7 +1738,6 @@ function buildTrtcSessionKey(trtc, aiDenoiser) {
|
|
|
1640
1738
|
appId: trtc.app_id,
|
|
1641
1739
|
roomId: trtc.room_id,
|
|
1642
1740
|
userId: trtc.user_id,
|
|
1643
|
-
userSig: trtc.user_sig,
|
|
1644
1741
|
aiDenoiser: {
|
|
1645
1742
|
enabled: denoiser.enabled,
|
|
1646
1743
|
mode: denoiser.mode,
|
|
@@ -1719,6 +1816,9 @@ function enforceContainMedia(container) {
|
|
|
1719
1816
|
element.style.setProperty("max-height", "100%", "important");
|
|
1720
1817
|
element.style.setProperty("object-fit", "contain", "important");
|
|
1721
1818
|
element.style.setProperty("display", "block", "important");
|
|
1819
|
+
element.style.setProperty("background", "transparent", "important");
|
|
1820
|
+
element.style.setProperty("background-color", "transparent", "important");
|
|
1821
|
+
element.style.setProperty("background-image", "none", "important");
|
|
1722
1822
|
});
|
|
1723
1823
|
}
|
|
1724
1824
|
|
|
@@ -1746,8 +1846,9 @@ function describeLivekitRoom(livekit) {
|
|
|
1746
1846
|
// src/runtime/managers/livekit-source-manager.ts
|
|
1747
1847
|
var TAG2 = "[IVI-LIVEKIT]";
|
|
1748
1848
|
var LivekitSourceManager = class {
|
|
1749
|
-
constructor(onLog) {
|
|
1849
|
+
constructor(onLog, onRemoteVideoAvailable) {
|
|
1750
1850
|
this.onLog = onLog;
|
|
1851
|
+
this.onRemoteVideoAvailable = onRemoteVideoAvailable;
|
|
1751
1852
|
this.sessions = /* @__PURE__ */ new Map();
|
|
1752
1853
|
this.listeners = /* @__PURE__ */ new Map();
|
|
1753
1854
|
}
|
|
@@ -1974,6 +2075,7 @@ var LivekitSourceManager = class {
|
|
|
1974
2075
|
waiter(true);
|
|
1975
2076
|
}
|
|
1976
2077
|
session.remoteVideoWaiters.length = 0;
|
|
2078
|
+
this.onRemoteVideoAvailable?.(session.sourceId);
|
|
1977
2079
|
}
|
|
1978
2080
|
session.views.forEach((binding) => {
|
|
1979
2081
|
this.attachTrackToView(track, kind, trackKey, binding);
|
|
@@ -2169,6 +2271,7 @@ var IviRuntimeCoordinator = class {
|
|
|
2169
2271
|
this.userTextFlowCounter = 0;
|
|
2170
2272
|
this.waitingTracksListValidation = false;
|
|
2171
2273
|
this.waitingSourcesListValidation = false;
|
|
2274
|
+
this.bootstrapSource = null;
|
|
2172
2275
|
this.state = {
|
|
2173
2276
|
status: "idle",
|
|
2174
2277
|
session: null,
|
|
@@ -2177,7 +2280,8 @@ var IviRuntimeCoordinator = class {
|
|
|
2177
2280
|
sources: /* @__PURE__ */ new Map(),
|
|
2178
2281
|
streams: /* @__PURE__ */ new Map(),
|
|
2179
2282
|
conversationItems: /* @__PURE__ */ new Map(),
|
|
2180
|
-
conversations: []
|
|
2283
|
+
conversations: [],
|
|
2284
|
+
bootstrap: null
|
|
2181
2285
|
};
|
|
2182
2286
|
this.client = client;
|
|
2183
2287
|
this.config = {
|
|
@@ -2186,10 +2290,12 @@ var IviRuntimeCoordinator = class {
|
|
|
2186
2290
|
};
|
|
2187
2291
|
this.trtcSourceManager = new TrtcSourceManager(
|
|
2188
2292
|
this.config.onLog,
|
|
2189
|
-
(event) => this.
|
|
2293
|
+
(event) => this.onTrtcManagerEvent(event),
|
|
2190
2294
|
this.config.trtcAIDenoiser
|
|
2191
2295
|
);
|
|
2192
|
-
this.livekitSourceManager = new LivekitSourceManager(this.config.onLog)
|
|
2296
|
+
this.livekitSourceManager = new LivekitSourceManager(this.config.onLog, () => {
|
|
2297
|
+
this.recomposeBootstrapState();
|
|
2298
|
+
});
|
|
2193
2299
|
this.sessionHandler = new SessionEventHandler(this.sessionManager, {
|
|
2194
2300
|
onSessionCreated: (event) => this.onSessionCreated(event),
|
|
2195
2301
|
onSessionEnded: (event) => this.onSessionEnded(event)
|
|
@@ -2270,6 +2376,22 @@ var IviRuntimeCoordinator = class {
|
|
|
2270
2376
|
emitLog(entry) {
|
|
2271
2377
|
this.config.onLog?.(entry);
|
|
2272
2378
|
}
|
|
2379
|
+
setBootstrapSource(source) {
|
|
2380
|
+
const previous = this.bootstrapSource;
|
|
2381
|
+
if (previous && (!source || previous.sourceId !== source.sourceId)) {
|
|
2382
|
+
const existing = this.sourceManager.get(previous.sourceId);
|
|
2383
|
+
if (existing?.origin === "prebuilt-session-response" && existing.provisional) {
|
|
2384
|
+
this.sourceManager.remove(previous.sourceId);
|
|
2385
|
+
}
|
|
2386
|
+
}
|
|
2387
|
+
this.bootstrapSource = source;
|
|
2388
|
+
this.ensureBootstrapSourceRegistered();
|
|
2389
|
+
this.syncPlaybackManagers();
|
|
2390
|
+
this.setState({
|
|
2391
|
+
...this.state,
|
|
2392
|
+
sources: this.sourceManager.getAll()
|
|
2393
|
+
});
|
|
2394
|
+
}
|
|
2273
2395
|
/**
|
|
2274
2396
|
* 编排一次"用户文本输入 -> 触发模型回复"链路。
|
|
2275
2397
|
*
|
|
@@ -2390,10 +2512,10 @@ var IviRuntimeCoordinator = class {
|
|
|
2390
2512
|
this.trackManager.reset();
|
|
2391
2513
|
this.sourceManager.reset();
|
|
2392
2514
|
this.streamManager.reset();
|
|
2393
|
-
this.trtcSourceManager.reset();
|
|
2394
|
-
this.livekitSourceManager.reset();
|
|
2395
2515
|
this.conversationManager.reset();
|
|
2396
2516
|
this.reset();
|
|
2517
|
+
this.ensureBootstrapSourceRegistered();
|
|
2518
|
+
this.syncPlaybackManagers();
|
|
2397
2519
|
const nextStatus = this.config.syncStageOnSessionCreated !== false ? "syncing" : "running";
|
|
2398
2520
|
this.setState({
|
|
2399
2521
|
status: nextStatus,
|
|
@@ -2431,8 +2553,8 @@ var IviRuntimeCoordinator = class {
|
|
|
2431
2553
|
onTracksChanged(listRefreshed) {
|
|
2432
2554
|
const nextStage = this.stageManager.getStage();
|
|
2433
2555
|
this.sourceManager.syncWithTracks(this.trackManager.getAll());
|
|
2434
|
-
this.
|
|
2435
|
-
this.
|
|
2556
|
+
this.ensureBootstrapSourceRegistered();
|
|
2557
|
+
this.syncPlaybackManagers();
|
|
2436
2558
|
this.setState({
|
|
2437
2559
|
...this.state,
|
|
2438
2560
|
tracks: this.trackManager.getAll(),
|
|
@@ -2446,8 +2568,8 @@ var IviRuntimeCoordinator = class {
|
|
|
2446
2568
|
applyLocalTrackTake(trackId) {
|
|
2447
2569
|
this.trackManager.applyTrackTook(trackId);
|
|
2448
2570
|
this.sourceManager.syncWithTracks(this.trackManager.getAll());
|
|
2449
|
-
this.
|
|
2450
|
-
this.
|
|
2571
|
+
this.ensureBootstrapSourceRegistered();
|
|
2572
|
+
this.syncPlaybackManagers();
|
|
2451
2573
|
this.setState({
|
|
2452
2574
|
...this.state,
|
|
2453
2575
|
tracks: this.trackManager.getAll(),
|
|
@@ -2487,8 +2609,8 @@ var IviRuntimeCoordinator = class {
|
|
|
2487
2609
|
}
|
|
2488
2610
|
onSourcesChanged(listRefreshed) {
|
|
2489
2611
|
this.sourceManager.syncWithTracks(this.trackManager.getAll());
|
|
2490
|
-
this.
|
|
2491
|
-
this.
|
|
2612
|
+
this.ensureBootstrapSourceRegistered();
|
|
2613
|
+
this.syncPlaybackManagers();
|
|
2492
2614
|
this.setState({
|
|
2493
2615
|
...this.state,
|
|
2494
2616
|
sources: this.sourceManager.getAll()
|
|
@@ -2511,12 +2633,101 @@ var IviRuntimeCoordinator = class {
|
|
|
2511
2633
|
});
|
|
2512
2634
|
}
|
|
2513
2635
|
setState(nextState) {
|
|
2514
|
-
|
|
2636
|
+
let resolvedNextState = nextState;
|
|
2637
|
+
if (this.bootstrapSource && nextState.status !== "stopped" && !nextState.sources.has(this.bootstrapSource.sourceId)) {
|
|
2638
|
+
this.ensureBootstrapSourceRegistered();
|
|
2639
|
+
resolvedNextState = {
|
|
2640
|
+
...nextState,
|
|
2641
|
+
sources: this.sourceManager.getAll()
|
|
2642
|
+
};
|
|
2643
|
+
}
|
|
2644
|
+
const composedNextState = this.composeBootstrapState(resolvedNextState);
|
|
2645
|
+
if (this.state.status === composedNextState.status && this.state.session === composedNextState.session && this.state.stage === composedNextState.stage && this.state.tracks === composedNextState.tracks && this.state.sources === composedNextState.sources && this.state.streams === composedNextState.streams && this.state.conversationItems === composedNextState.conversationItems && this.state.conversations === composedNextState.conversations && isSameBootstrapState(this.state.bootstrap, composedNextState.bootstrap)) {
|
|
2515
2646
|
return;
|
|
2516
2647
|
}
|
|
2517
|
-
this.state =
|
|
2648
|
+
this.state = composedNextState;
|
|
2518
2649
|
this.stateListeners.forEach((listener) => listener(this.state));
|
|
2519
2650
|
}
|
|
2651
|
+
composeBootstrapState(nextState) {
|
|
2652
|
+
const bootstrap = this.bootstrapSource;
|
|
2653
|
+
if (!bootstrap) {
|
|
2654
|
+
return nextState.bootstrap === null || nextState.bootstrap === void 0 ? nextState : { ...nextState, bootstrap: null };
|
|
2655
|
+
}
|
|
2656
|
+
const shouldExpose = this.shouldExposeBootstrap(nextState, bootstrap);
|
|
2657
|
+
const bootstrapState = {
|
|
2658
|
+
active: shouldExpose,
|
|
2659
|
+
slot: bootstrap.slot,
|
|
2660
|
+
trackId: bootstrap.trackId,
|
|
2661
|
+
sourceId: bootstrap.sourceId,
|
|
2662
|
+
streamId: bootstrap.streamId
|
|
2663
|
+
};
|
|
2664
|
+
if (!shouldExpose) {
|
|
2665
|
+
return {
|
|
2666
|
+
...nextState,
|
|
2667
|
+
bootstrap: bootstrapState
|
|
2668
|
+
};
|
|
2669
|
+
}
|
|
2670
|
+
const tracks = new Map(nextState.tracks);
|
|
2671
|
+
tracks.set(bootstrap.trackId, {
|
|
2672
|
+
track_id: bootstrap.trackId,
|
|
2673
|
+
active_source_id: bootstrap.sourceId
|
|
2674
|
+
});
|
|
2675
|
+
return {
|
|
2676
|
+
...nextState,
|
|
2677
|
+
tracks,
|
|
2678
|
+
bootstrap: bootstrapState
|
|
2679
|
+
};
|
|
2680
|
+
}
|
|
2681
|
+
shouldExposeBootstrap(state, bootstrap) {
|
|
2682
|
+
if (state.status === "stopped") {
|
|
2683
|
+
return false;
|
|
2684
|
+
}
|
|
2685
|
+
const bootstrapRuntimeSource = state.sources.get(bootstrap.sourceId);
|
|
2686
|
+
if (!isReadyRenderableSource(bootstrapRuntimeSource)) {
|
|
2687
|
+
return false;
|
|
2688
|
+
}
|
|
2689
|
+
const layer = (state.stage?.composition ?? []).find((item) => item.slot === bootstrap.slot);
|
|
2690
|
+
if (!layer) {
|
|
2691
|
+
return true;
|
|
2692
|
+
}
|
|
2693
|
+
const track = state.tracks.get(layer.track_id);
|
|
2694
|
+
const activeSourceId = track?.active_source_id;
|
|
2695
|
+
if (!activeSourceId || activeSourceId === bootstrap.sourceId) {
|
|
2696
|
+
return true;
|
|
2697
|
+
}
|
|
2698
|
+
return !this.isReadyForBootstrapHandoff(activeSourceId, state.sources.get(activeSourceId));
|
|
2699
|
+
}
|
|
2700
|
+
isReadyForBootstrapHandoff(sourceId, source) {
|
|
2701
|
+
if (!isReadyRenderableSource(source)) {
|
|
2702
|
+
return false;
|
|
2703
|
+
}
|
|
2704
|
+
if (isTrtcPlaybackSource(source)) {
|
|
2705
|
+
return this.trtcSourceManager.hasRemoteVideoAvailable(sourceId);
|
|
2706
|
+
}
|
|
2707
|
+
if (isLivekitPlaybackSource(source)) {
|
|
2708
|
+
return this.livekitSourceManager.hasRemoteVideoAvailable(sourceId);
|
|
2709
|
+
}
|
|
2710
|
+
return true;
|
|
2711
|
+
}
|
|
2712
|
+
recomposeBootstrapState() {
|
|
2713
|
+
if (!this.bootstrapSource) {
|
|
2714
|
+
return;
|
|
2715
|
+
}
|
|
2716
|
+
this.setState({ ...this.state });
|
|
2717
|
+
}
|
|
2718
|
+
ensureBootstrapSourceRegistered() {
|
|
2719
|
+
if (!this.bootstrapSource) {
|
|
2720
|
+
return;
|
|
2721
|
+
}
|
|
2722
|
+
if (this.sourceManager.has(this.bootstrapSource.sourceId)) {
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
this.sourceManager.upsertBootstrapReady(this.bootstrapSource);
|
|
2726
|
+
}
|
|
2727
|
+
syncPlaybackManagers() {
|
|
2728
|
+
this.trtcSourceManager.syncRuntimeSources(this.sourceManager.getAll());
|
|
2729
|
+
this.livekitSourceManager.syncRuntimeSources(this.sourceManager.getAll());
|
|
2730
|
+
}
|
|
2520
2731
|
emitEvent(event) {
|
|
2521
2732
|
this.progressUserTextToResponseFlows(event);
|
|
2522
2733
|
this.eventListeners.forEach((listener) => listener(event, this.state));
|
|
@@ -2525,6 +2736,12 @@ var IviRuntimeCoordinator = class {
|
|
|
2525
2736
|
this.config.onTrtcEvent?.(event);
|
|
2526
2737
|
this.trtcEventListeners.forEach((listener) => listener(event));
|
|
2527
2738
|
}
|
|
2739
|
+
onTrtcManagerEvent(event) {
|
|
2740
|
+
this.emitTrtcEvent(event);
|
|
2741
|
+
if (event.type === "remote_video_available") {
|
|
2742
|
+
this.recomposeBootstrapState();
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2528
2745
|
resetStoppedState() {
|
|
2529
2746
|
this.rejectPendingUserTextToResponseFlows(
|
|
2530
2747
|
new Error("Runtime stopped before user text to response flow completed.")
|
|
@@ -2689,7 +2906,331 @@ function isLivekitPlaybackSource(source) {
|
|
|
2689
2906
|
if (!source || !source.playback) return false;
|
|
2690
2907
|
return isLivekitSourcePlayback(source.playback);
|
|
2691
2908
|
}
|
|
2909
|
+
function isReadyRenderableSource(source) {
|
|
2910
|
+
return Boolean(source && source.status === "ready" && source.playback);
|
|
2911
|
+
}
|
|
2912
|
+
function isSameBootstrapState(a, b) {
|
|
2913
|
+
if (!a && !b) return true;
|
|
2914
|
+
if (!a || !b) return false;
|
|
2915
|
+
return a.active === b.active && a.slot === b.slot && a.trackId === b.trackId && a.sourceId === b.sourceId && a.streamId === b.streamId;
|
|
2916
|
+
}
|
|
2917
|
+
var IviCreateIVISessionError = class extends Error {
|
|
2918
|
+
constructor(response, body) {
|
|
2919
|
+
super(`CreateIVISession failed with ${response.status} ${response.statusText}`);
|
|
2920
|
+
this.name = "IviCreateIVISessionError";
|
|
2921
|
+
this.status = response.status;
|
|
2922
|
+
this.statusText = response.statusText;
|
|
2923
|
+
this.body = body;
|
|
2924
|
+
}
|
|
2925
|
+
};
|
|
2926
|
+
async function createIVISession(request, options) {
|
|
2927
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
2928
|
+
if (!fetchImpl) {
|
|
2929
|
+
throw new Error("fetch is not available. Pass options.fetch to createIVISession.");
|
|
2930
|
+
}
|
|
2931
|
+
const requestBody = toCpCreateIVISessionRequestBody(request);
|
|
2932
|
+
const response = await fetchImpl(resolveCreateIVISessionUrl(options), {
|
|
2933
|
+
method: "POST",
|
|
2934
|
+
headers: await buildHeaders(options.headers),
|
|
2935
|
+
body: JSON.stringify(requestBody),
|
|
2936
|
+
credentials: options.credentials,
|
|
2937
|
+
signal: options.signal
|
|
2938
|
+
});
|
|
2939
|
+
const responseBody = await readJsonResponse(response);
|
|
2940
|
+
if (!response.ok) {
|
|
2941
|
+
throw new IviCreateIVISessionError(response, responseBody);
|
|
2942
|
+
}
|
|
2943
|
+
return normalizeCreateIVISessionResponse(responseBody, request, requestBody);
|
|
2944
|
+
}
|
|
2945
|
+
function createRuntimeFromSession(session, options = {}) {
|
|
2946
|
+
const websocketUrl = buildWebSocketUrl(session.endpoint, options.websocket);
|
|
2947
|
+
const client = new IviClient({
|
|
2948
|
+
...options.clientConfig ?? {},
|
|
2949
|
+
transport: new WebSocketTransport({
|
|
2950
|
+
url: websocketUrl,
|
|
2951
|
+
sessionId: session.iviSessionId,
|
|
2952
|
+
protocols: options.websocket?.protocols,
|
|
2953
|
+
socketFactory: options.websocket?.socketFactory
|
|
2954
|
+
})
|
|
2955
|
+
});
|
|
2956
|
+
const runtime = new IviRuntimeCoordinator(client, options.runtimeConfig);
|
|
2957
|
+
if (options.fastStart !== false && session.prebuiltPlayback) {
|
|
2958
|
+
runtime.setBootstrapSource(toBootstrapSource(session.prebuiltPlayback, options));
|
|
2959
|
+
}
|
|
2960
|
+
return {
|
|
2961
|
+
session,
|
|
2962
|
+
client,
|
|
2963
|
+
runtime
|
|
2964
|
+
};
|
|
2965
|
+
}
|
|
2966
|
+
async function createIVISessionRuntime(request, options) {
|
|
2967
|
+
const session = await createIVISession(request, options.http);
|
|
2968
|
+
return createRuntimeFromSession(session, options);
|
|
2969
|
+
}
|
|
2970
|
+
function toCpCreateIVISessionRequestBody(request) {
|
|
2971
|
+
const body = { ...request };
|
|
2972
|
+
delete body.idempotencyKey;
|
|
2973
|
+
delete body.streamType;
|
|
2974
|
+
delete body.iviVersion;
|
|
2975
|
+
delete body.sessionParameters;
|
|
2976
|
+
delete body.enableDecoderPublish;
|
|
2977
|
+
delete body.sessionRecording;
|
|
2978
|
+
delete body.prebuiltCharacters;
|
|
2979
|
+
delete body.prebuiltStream;
|
|
2980
|
+
if (request.idempotencyKey !== void 0) body.idempotency_key = request.idempotencyKey;
|
|
2981
|
+
if (request.streamType !== void 0) body.stream_type = request.streamType;
|
|
2982
|
+
if (request.iviVersion !== void 0) body.ivi_version = request.iviVersion;
|
|
2983
|
+
if (request.sessionParameters !== void 0) body.session_parameters = request.sessionParameters;
|
|
2984
|
+
if (request.enableDecoderPublish !== void 0) body.enable_decoder_publish = request.enableDecoderPublish;
|
|
2985
|
+
if (request.sessionRecording !== void 0) {
|
|
2986
|
+
body.session_recording = {
|
|
2987
|
+
enabled: request.sessionRecording.enabled,
|
|
2988
|
+
aspect_ratio: request.sessionRecording.aspectRatio
|
|
2989
|
+
};
|
|
2990
|
+
}
|
|
2991
|
+
if (request.prebuiltCharacters !== void 0) {
|
|
2992
|
+
body.prebuilt_characters = request.prebuiltCharacters.map((character) => ({
|
|
2993
|
+
character_id: character.characterId,
|
|
2994
|
+
character_payload: serializePrebuiltPayload(character.characterPayload)
|
|
2995
|
+
}));
|
|
2996
|
+
}
|
|
2997
|
+
if (request.prebuiltStream !== void 0) {
|
|
2998
|
+
body.prebuilt_stream = {
|
|
2999
|
+
stream_id: request.prebuiltStream.streamId,
|
|
3000
|
+
mode: request.prebuiltStream.mode,
|
|
3001
|
+
stream_config: serializePrebuiltPayload(request.prebuiltStream.streamConfig)
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
return body;
|
|
3005
|
+
}
|
|
3006
|
+
function normalizeCreateIVISessionResponse(responseBody, request = {}, requestBody = toCpCreateIVISessionRequestBody(request)) {
|
|
3007
|
+
const payload = unwrapResponsePayload(responseBody);
|
|
3008
|
+
const iviSessionId = getString(payload, "ivi_session_id") ?? getString(payload, "iviSessionId");
|
|
3009
|
+
const endpoint = getString(payload, "endpoint");
|
|
3010
|
+
if (!iviSessionId) {
|
|
3011
|
+
throw new Error("CreateIVISession response missing ivi_session_id.");
|
|
3012
|
+
}
|
|
3013
|
+
if (!endpoint) {
|
|
3014
|
+
throw new Error("CreateIVISession response missing endpoint.");
|
|
3015
|
+
}
|
|
3016
|
+
const trtcInfo = normalizeTrtcInfo(getObjectValue(payload, "trtc_info") ?? getObjectValue(payload, "trtcInfo"));
|
|
3017
|
+
const livekitInfo = normalizeLivekitInfo(getObjectValue(payload, "livekit_info") ?? getObjectValue(payload, "livekitInfo"));
|
|
3018
|
+
const prebuildInfo = getObjectValue(payload, "prebuild_trtc_info") ?? getObjectValue(payload, "prebuildTrtcInfo");
|
|
3019
|
+
const prebuildTrtcInfo = normalizeTrtcInfo(
|
|
3020
|
+
getObjectValue(prebuildInfo, "trtc_info") ?? getObjectValue(prebuildInfo, "trtcInfo")
|
|
3021
|
+
);
|
|
3022
|
+
const prebuildLivekitInfo = normalizeLivekitInfo(
|
|
3023
|
+
getObjectValue(prebuildInfo, "livekit_info") ?? getObjectValue(prebuildInfo, "livekitInfo")
|
|
3024
|
+
);
|
|
3025
|
+
const prebuiltPlayback = buildPrebuiltPlayback(
|
|
3026
|
+
request,
|
|
3027
|
+
requestBody,
|
|
3028
|
+
prebuildTrtcInfo,
|
|
3029
|
+
prebuildLivekitInfo
|
|
3030
|
+
);
|
|
3031
|
+
return {
|
|
3032
|
+
iviSessionId,
|
|
3033
|
+
endpoint,
|
|
3034
|
+
tokenInfo: getObjectValue(payload, "token_info") ?? getObjectValue(payload, "tokenInfo"),
|
|
3035
|
+
trtcInfo,
|
|
3036
|
+
livekitInfo,
|
|
3037
|
+
prebuildTrtcInfo: prebuildTrtcInfo || prebuildLivekitInfo ? {
|
|
3038
|
+
trtcInfo: prebuildTrtcInfo,
|
|
3039
|
+
livekitInfo: prebuildLivekitInfo
|
|
3040
|
+
} : void 0,
|
|
3041
|
+
prebuiltPlayback,
|
|
3042
|
+
raw: responseBody,
|
|
3043
|
+
requestBody
|
|
3044
|
+
};
|
|
3045
|
+
}
|
|
3046
|
+
function resolveCreateIVISessionUrl(options) {
|
|
3047
|
+
if (options.url) return options.url;
|
|
3048
|
+
if (!options.baseUrl) {
|
|
3049
|
+
throw new Error("CreateIVISession requires options.baseUrl or options.url.");
|
|
3050
|
+
}
|
|
3051
|
+
const path = options.path ?? "/v1/ivi/sessions";
|
|
3052
|
+
return new URL(path, ensureTrailingSlash(options.baseUrl)).toString();
|
|
3053
|
+
}
|
|
3054
|
+
async function buildHeaders(headers) {
|
|
3055
|
+
const resolvedHeaders = typeof headers === "function" ? await headers() : headers;
|
|
3056
|
+
const next = new Headers(resolvedHeaders);
|
|
3057
|
+
if (!next.has("content-type")) {
|
|
3058
|
+
next.set("content-type", "application/json");
|
|
3059
|
+
}
|
|
3060
|
+
return next;
|
|
3061
|
+
}
|
|
3062
|
+
async function readJsonResponse(response) {
|
|
3063
|
+
const text = await response.text();
|
|
3064
|
+
if (!text) {
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
3067
|
+
try {
|
|
3068
|
+
return JSON.parse(text);
|
|
3069
|
+
} catch {
|
|
3070
|
+
return text;
|
|
3071
|
+
}
|
|
3072
|
+
}
|
|
3073
|
+
function buildWebSocketUrl(endpoint, options) {
|
|
3074
|
+
const rawUrl = options?.url ?? endpoint;
|
|
3075
|
+
if (!options?.query) {
|
|
3076
|
+
return rawUrl;
|
|
3077
|
+
}
|
|
3078
|
+
const url = new URL(rawUrl);
|
|
3079
|
+
Object.entries(options.query).forEach(([key, value]) => {
|
|
3080
|
+
if (value === void 0 || value === null) return;
|
|
3081
|
+
url.searchParams.set(key, value);
|
|
3082
|
+
});
|
|
3083
|
+
return url.toString();
|
|
3084
|
+
}
|
|
3085
|
+
function toBootstrapSource(prebuiltPlayback, options) {
|
|
3086
|
+
return {
|
|
3087
|
+
sourceId: prebuiltPlayback.sourceId,
|
|
3088
|
+
streamId: prebuiltPlayback.streamId,
|
|
3089
|
+
slot: options.bootstrapSlot ?? "main",
|
|
3090
|
+
trackId: options.bootstrapTrackId ?? "__ivi_bootstrap_track",
|
|
3091
|
+
playback: prebuiltPlayback.playback,
|
|
3092
|
+
source: {
|
|
3093
|
+
source_id: prebuiltPlayback.sourceId,
|
|
3094
|
+
kind: prebuiltPlayback.streamId ? "generation_stream" : "stream",
|
|
3095
|
+
stream_id: prebuiltPlayback.streamId,
|
|
3096
|
+
asset_type: "video",
|
|
3097
|
+
metadata: {
|
|
3098
|
+
label: "prebuilt"
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
};
|
|
3102
|
+
}
|
|
3103
|
+
function buildPrebuiltPlayback(request, requestBody, trtcInfo, livekitInfo) {
|
|
3104
|
+
if (!trtcInfo && !livekitInfo) {
|
|
3105
|
+
return void 0;
|
|
3106
|
+
}
|
|
3107
|
+
const streamId = resolvePrebuiltStreamId(request, requestBody, trtcInfo, livekitInfo);
|
|
3108
|
+
const sourceId = request.prebuiltStream?.sourceId ?? streamId;
|
|
3109
|
+
if (!sourceId) {
|
|
3110
|
+
return void 0;
|
|
3111
|
+
}
|
|
3112
|
+
if (trtcInfo) {
|
|
3113
|
+
return {
|
|
3114
|
+
sourceId,
|
|
3115
|
+
streamId,
|
|
3116
|
+
kind: "trtc",
|
|
3117
|
+
playback: {
|
|
3118
|
+
type: "trtc",
|
|
3119
|
+
trtc: trtcInfo
|
|
3120
|
+
},
|
|
3121
|
+
provisional: true
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
if (livekitInfo) {
|
|
3125
|
+
return {
|
|
3126
|
+
sourceId,
|
|
3127
|
+
streamId,
|
|
3128
|
+
kind: "livekit",
|
|
3129
|
+
playback: {
|
|
3130
|
+
type: "livekit",
|
|
3131
|
+
livekit: livekitInfo
|
|
3132
|
+
},
|
|
3133
|
+
provisional: true
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
3136
|
+
return void 0;
|
|
3137
|
+
}
|
|
3138
|
+
function resolvePrebuiltStreamId(request, requestBody, trtcInfo, livekitInfo) {
|
|
3139
|
+
const prebuiltStream = getObjectValue(requestBody, "prebuilt_stream");
|
|
3140
|
+
return request.prebuiltStream?.streamId ?? getString(prebuiltStream, "stream_id") ?? getString(prebuiltStream, "streamId") ?? trtcInfo?.room_id ?? livekitInfo?.room;
|
|
3141
|
+
}
|
|
3142
|
+
function normalizeTrtcInfo(value) {
|
|
3143
|
+
if (!isRecord(value)) {
|
|
3144
|
+
return void 0;
|
|
3145
|
+
}
|
|
3146
|
+
const appId = getString(value, "app_id") ?? getString(value, "appId");
|
|
3147
|
+
const roomId = getString(value, "room_id") ?? getString(value, "roomId");
|
|
3148
|
+
const userId = getString(value, "user_id") ?? getString(value, "userId");
|
|
3149
|
+
const userSig = getString(value, "user_sig") ?? getString(value, "userSig");
|
|
3150
|
+
if (!appId || !roomId || !userId || !userSig) {
|
|
3151
|
+
return void 0;
|
|
3152
|
+
}
|
|
3153
|
+
const normalized = {
|
|
3154
|
+
...value,
|
|
3155
|
+
app_id: appId,
|
|
3156
|
+
room_id: roomId,
|
|
3157
|
+
user_id: userId,
|
|
3158
|
+
user_sig: userSig
|
|
3159
|
+
};
|
|
3160
|
+
delete normalized.secret_key;
|
|
3161
|
+
delete normalized.secretKey;
|
|
3162
|
+
return normalized;
|
|
3163
|
+
}
|
|
3164
|
+
function normalizeLivekitInfo(value) {
|
|
3165
|
+
if (!isRecord(value)) {
|
|
3166
|
+
return void 0;
|
|
3167
|
+
}
|
|
3168
|
+
const wsUrl = getString(value, "ws_url") ?? getString(value, "url") ?? getString(value, "wsUrl");
|
|
3169
|
+
const token = getString(value, "token") ?? getString(value, "access_token") ?? getString(value, "accessToken");
|
|
3170
|
+
if (!wsUrl || !token) {
|
|
3171
|
+
return void 0;
|
|
3172
|
+
}
|
|
3173
|
+
return {
|
|
3174
|
+
ws_url: wsUrl,
|
|
3175
|
+
token,
|
|
3176
|
+
room: getString(value, "room") ?? getString(value, "room_name") ?? getString(value, "roomName"),
|
|
3177
|
+
identity: getString(value, "identity")
|
|
3178
|
+
};
|
|
3179
|
+
}
|
|
3180
|
+
function serializePrebuiltPayload(value) {
|
|
3181
|
+
if (typeof value === "string") {
|
|
3182
|
+
return value;
|
|
3183
|
+
}
|
|
3184
|
+
return encodeJsonBase64(value);
|
|
3185
|
+
}
|
|
3186
|
+
function encodeJsonBase64(value) {
|
|
3187
|
+
const bytes = new TextEncoder().encode(JSON.stringify(value));
|
|
3188
|
+
let binary = "";
|
|
3189
|
+
bytes.forEach((byte) => {
|
|
3190
|
+
binary += String.fromCharCode(byte);
|
|
3191
|
+
});
|
|
3192
|
+
if (typeof btoa === "function") {
|
|
3193
|
+
return btoa(binary);
|
|
3194
|
+
}
|
|
3195
|
+
const maybeBuffer = globalThis.Buffer;
|
|
3196
|
+
if (maybeBuffer) {
|
|
3197
|
+
return maybeBuffer.from(bytes).toString("base64");
|
|
3198
|
+
}
|
|
3199
|
+
throw new Error("Base64 encoder is not available in this runtime.");
|
|
3200
|
+
}
|
|
3201
|
+
function unwrapResponsePayload(value) {
|
|
3202
|
+
if (!isRecord(value)) {
|
|
3203
|
+
return {};
|
|
3204
|
+
}
|
|
3205
|
+
const data = getObjectValue(value, "data");
|
|
3206
|
+
return data ?? value;
|
|
3207
|
+
}
|
|
3208
|
+
function getObjectValue(value, key) {
|
|
3209
|
+
if (!isRecord(value)) {
|
|
3210
|
+
return void 0;
|
|
3211
|
+
}
|
|
3212
|
+
const nested = value[key];
|
|
3213
|
+
return isRecord(nested) ? nested : void 0;
|
|
3214
|
+
}
|
|
3215
|
+
function getString(value, key) {
|
|
3216
|
+
if (!isRecord(value)) {
|
|
3217
|
+
return void 0;
|
|
3218
|
+
}
|
|
3219
|
+
const nested = value[key];
|
|
3220
|
+
return typeof nested === "string" && nested.trim().length > 0 ? nested.trim() : void 0;
|
|
3221
|
+
}
|
|
3222
|
+
function isRecord(value) {
|
|
3223
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3224
|
+
}
|
|
3225
|
+
function ensureTrailingSlash(value) {
|
|
3226
|
+
return value.endsWith("/") ? value : `${value}/`;
|
|
3227
|
+
}
|
|
3228
|
+
|
|
3229
|
+
// src/sdk.ts
|
|
2692
3230
|
var IviFrontendSdk = class {
|
|
3231
|
+
constructor(config = {}) {
|
|
3232
|
+
this.config = config;
|
|
3233
|
+
}
|
|
2693
3234
|
createClient(config) {
|
|
2694
3235
|
return new IviClient(config);
|
|
2695
3236
|
}
|
|
@@ -2697,6 +3238,25 @@ var IviFrontendSdk = class {
|
|
|
2697
3238
|
const client = this.createClient(clientConfig);
|
|
2698
3239
|
return new IviRuntimeCoordinator(client, runtimeConfig);
|
|
2699
3240
|
}
|
|
3241
|
+
createIVISession(request, options) {
|
|
3242
|
+
return createIVISession(request, {
|
|
3243
|
+
...this.config.http ?? {},
|
|
3244
|
+
...options ?? {}
|
|
3245
|
+
});
|
|
3246
|
+
}
|
|
3247
|
+
createRuntimeFromSession(session, options) {
|
|
3248
|
+
return createRuntimeFromSession(session, options);
|
|
3249
|
+
}
|
|
3250
|
+
createIVISessionRuntime(request, options) {
|
|
3251
|
+
const http = {
|
|
3252
|
+
...this.config.http ?? {},
|
|
3253
|
+
...options?.http ?? {}
|
|
3254
|
+
};
|
|
3255
|
+
return createIVISessionRuntime(request, {
|
|
3256
|
+
...options ?? {},
|
|
3257
|
+
http
|
|
3258
|
+
});
|
|
3259
|
+
}
|
|
2700
3260
|
};
|
|
2701
3261
|
var IviStageViewContext = createContext(null);
|
|
2702
3262
|
var EMPTY_RUNTIME_STATE = {
|
|
@@ -2707,7 +3267,8 @@ var EMPTY_RUNTIME_STATE = {
|
|
|
2707
3267
|
sources: /* @__PURE__ */ new Map(),
|
|
2708
3268
|
streams: /* @__PURE__ */ new Map(),
|
|
2709
3269
|
conversationItems: /* @__PURE__ */ new Map(),
|
|
2710
|
-
conversations: []
|
|
3270
|
+
conversations: [],
|
|
3271
|
+
bootstrap: null
|
|
2711
3272
|
};
|
|
2712
3273
|
function useRuntimeState(runtime) {
|
|
2713
3274
|
const [state, setState] = useState(() => runtime?.getState() ?? EMPTY_RUNTIME_STATE);
|
|
@@ -2735,10 +3296,10 @@ function IVIStageView(props) {
|
|
|
2735
3296
|
const state = useRuntimeState(runtime);
|
|
2736
3297
|
const slotTrackMap = useMemo(() => {
|
|
2737
3298
|
return buildSlotTrackMapFromState(state);
|
|
2738
|
-
}, [state.stage]);
|
|
3299
|
+
}, [state.stage, state.bootstrap]);
|
|
2739
3300
|
const slotBindings = useMemo(() => {
|
|
2740
3301
|
return buildSlotBindingsFromState(state);
|
|
2741
|
-
}, [state.stage, state.tracks, state.sources]);
|
|
3302
|
+
}, [state.stage, state.tracks, state.sources, state.bootstrap]);
|
|
2742
3303
|
useEffect(() => {
|
|
2743
3304
|
onBindingsChange?.(slotBindings);
|
|
2744
3305
|
}, [slotBindings, onBindingsChange]);
|
|
@@ -2778,15 +3339,18 @@ function buildSlotTrackMapFromState(state) {
|
|
|
2778
3339
|
(state.stage?.composition ?? []).forEach((item) => {
|
|
2779
3340
|
map.set(item.slot, item.track_id);
|
|
2780
3341
|
});
|
|
3342
|
+
if (state.bootstrap?.active) {
|
|
3343
|
+
map.set(state.bootstrap.slot, state.bootstrap.trackId);
|
|
3344
|
+
}
|
|
2781
3345
|
return map;
|
|
2782
3346
|
}
|
|
2783
3347
|
function buildSlotBindingsFromState(state) {
|
|
2784
|
-
const
|
|
3348
|
+
const bindingsBySlot = /* @__PURE__ */ new Map();
|
|
2785
3349
|
(state.stage?.composition ?? []).forEach((item) => {
|
|
2786
3350
|
const track = state.tracks.get(item.track_id);
|
|
2787
3351
|
const sourceId = track?.active_source_id ?? null;
|
|
2788
3352
|
const source = sourceId ? state.sources.get(sourceId) : void 0;
|
|
2789
|
-
|
|
3353
|
+
bindingsBySlot.set(item.slot, {
|
|
2790
3354
|
slot: item.slot,
|
|
2791
3355
|
trackId: item.track_id,
|
|
2792
3356
|
track,
|
|
@@ -2794,7 +3358,18 @@ function buildSlotBindingsFromState(state) {
|
|
|
2794
3358
|
source
|
|
2795
3359
|
});
|
|
2796
3360
|
});
|
|
2797
|
-
|
|
3361
|
+
if (state.bootstrap?.active) {
|
|
3362
|
+
const track = state.tracks.get(state.bootstrap.trackId);
|
|
3363
|
+
const source = state.sources.get(state.bootstrap.sourceId);
|
|
3364
|
+
bindingsBySlot.set(state.bootstrap.slot, {
|
|
3365
|
+
slot: state.bootstrap.slot,
|
|
3366
|
+
trackId: state.bootstrap.trackId,
|
|
3367
|
+
track,
|
|
3368
|
+
sourceId: state.bootstrap.sourceId,
|
|
3369
|
+
source
|
|
3370
|
+
});
|
|
3371
|
+
}
|
|
3372
|
+
return Array.from(bindingsBySlot.values());
|
|
2798
3373
|
}
|
|
2799
3374
|
var VOLUME_STORAGE_KEY = "ivi-volume-preferences";
|
|
2800
3375
|
var DEFAULT_VOLUME = 80;
|
|
@@ -3562,23 +4137,25 @@ function IVITrtcPlayer(props) {
|
|
|
3562
4137
|
const [loading, setLoading] = useState(true);
|
|
3563
4138
|
const [error, setError] = useState(null);
|
|
3564
4139
|
const mutedRef = useRef(muted);
|
|
4140
|
+
const attachedSourceIdRef = useRef(null);
|
|
3565
4141
|
mutedRef.current = muted;
|
|
4142
|
+
useEffect(() => {
|
|
4143
|
+
const unsubscribe = manager.subscribe(resolvedSourceId, (snapshot) => {
|
|
4144
|
+
setLoading(snapshot.status === "idle" || snapshot.status === "connecting");
|
|
4145
|
+
setError(snapshot.status === "error" ? snapshot.error ?? "TRTC \u62C9\u6D41\u5931\u8D25" : null);
|
|
4146
|
+
});
|
|
4147
|
+
return unsubscribe;
|
|
4148
|
+
}, [manager, resolvedSourceId]);
|
|
3566
4149
|
useEffect(() => {
|
|
3567
4150
|
const container = containerRef.current;
|
|
3568
4151
|
if (!container) {
|
|
3569
4152
|
return;
|
|
3570
4153
|
}
|
|
3571
4154
|
let disposed = false;
|
|
4155
|
+
attachedSourceIdRef.current = resolvedSourceId;
|
|
3572
4156
|
if (shouldManageSourceLifecycle) {
|
|
3573
4157
|
manager.upsertSource(resolvedSourceId, trtc, trtcAIDenoiser);
|
|
3574
4158
|
}
|
|
3575
|
-
const unsubscribe = manager.subscribe(resolvedSourceId, (snapshot) => {
|
|
3576
|
-
if (disposed) {
|
|
3577
|
-
return;
|
|
3578
|
-
}
|
|
3579
|
-
setLoading(snapshot.status === "idle" || snapshot.status === "connecting");
|
|
3580
|
-
setError(snapshot.status === "error" ? snapshot.error ?? "TRTC \u62C9\u6D41\u5931\u8D25" : null);
|
|
3581
|
-
});
|
|
3582
4159
|
void manager.attachView(resolvedSourceId, viewIdRef.current, container, mutedRef.current).catch((caughtError) => {
|
|
3583
4160
|
if (disposed) {
|
|
3584
4161
|
return;
|
|
@@ -3588,15 +4165,15 @@ function IVITrtcPlayer(props) {
|
|
|
3588
4165
|
});
|
|
3589
4166
|
return () => {
|
|
3590
4167
|
disposed = true;
|
|
3591
|
-
|
|
3592
|
-
manager.detachView(
|
|
4168
|
+
const attachedSourceId = attachedSourceIdRef.current ?? resolvedSourceId;
|
|
4169
|
+
manager.detachView(attachedSourceId, viewIdRef.current);
|
|
3593
4170
|
if (shouldManageSourceLifecycle) {
|
|
3594
|
-
manager.removeSource(
|
|
4171
|
+
manager.removeSource(attachedSourceId);
|
|
3595
4172
|
}
|
|
4173
|
+
attachedSourceIdRef.current = null;
|
|
3596
4174
|
};
|
|
3597
4175
|
}, [
|
|
3598
4176
|
manager,
|
|
3599
|
-
resolvedSourceId,
|
|
3600
4177
|
shouldManageSourceLifecycle,
|
|
3601
4178
|
trtc.app_id,
|
|
3602
4179
|
trtc.room_id,
|
|
@@ -3604,6 +4181,15 @@ function IVITrtcPlayer(props) {
|
|
|
3604
4181
|
trtc.user_sig,
|
|
3605
4182
|
trtcAIDenoiser
|
|
3606
4183
|
]);
|
|
4184
|
+
useEffect(() => {
|
|
4185
|
+
const previousSourceId = attachedSourceIdRef.current;
|
|
4186
|
+
if (!previousSourceId || previousSourceId === resolvedSourceId) {
|
|
4187
|
+
return;
|
|
4188
|
+
}
|
|
4189
|
+
if (manager.reassignView(previousSourceId, resolvedSourceId, viewIdRef.current)) {
|
|
4190
|
+
attachedSourceIdRef.current = resolvedSourceId;
|
|
4191
|
+
}
|
|
4192
|
+
}, [manager, resolvedSourceId]);
|
|
3607
4193
|
useEffect(() => {
|
|
3608
4194
|
manager.updateViewMuted(resolvedSourceId, viewIdRef.current, muted);
|
|
3609
4195
|
}, [manager, muted, resolvedSourceId]);
|
|
@@ -3616,7 +4202,6 @@ function IVITrtcPlayer(props) {
|
|
|
3616
4202
|
height: "100%",
|
|
3617
4203
|
minWidth: 0,
|
|
3618
4204
|
minHeight: 0,
|
|
3619
|
-
backgroundColor: "#000",
|
|
3620
4205
|
position: "relative",
|
|
3621
4206
|
...style
|
|
3622
4207
|
},
|
|
@@ -4112,13 +4697,7 @@ function getSourceRenderKey(sourceId, source) {
|
|
|
4112
4697
|
if (!trtc) {
|
|
4113
4698
|
return sourceId;
|
|
4114
4699
|
}
|
|
4115
|
-
return [
|
|
4116
|
-
"trtc",
|
|
4117
|
-
trtc.app_id ?? "",
|
|
4118
|
-
trtc.room_id ?? "",
|
|
4119
|
-
trtc.user_id ?? "",
|
|
4120
|
-
trtc.user_sig ?? ""
|
|
4121
|
-
].join(":");
|
|
4700
|
+
return ["trtc", trtc.app_id ?? "", trtc.room_id ?? ""].join(":");
|
|
4122
4701
|
}
|
|
4123
4702
|
function TrackSlotMediaContent(props) {
|
|
4124
4703
|
const {
|
|
@@ -4574,7 +5153,83 @@ function getClientLogTag(category) {
|
|
|
4574
5153
|
if (category === "reconnect") return "[IVI-RECONNECT]";
|
|
4575
5154
|
return "[IVI-CLIENT]";
|
|
4576
5155
|
}
|
|
5156
|
+
function useIviSessionRuntime(config) {
|
|
5157
|
+
const [status, setStatus] = useState("idle");
|
|
5158
|
+
const [session, setSession] = useState(null);
|
|
5159
|
+
const [runtime, setRuntime] = useState(null);
|
|
5160
|
+
const [client, setClient] = useState(null);
|
|
5161
|
+
const [error, setError] = useState(null);
|
|
5162
|
+
useEffect(() => {
|
|
5163
|
+
const {
|
|
5164
|
+
request,
|
|
5165
|
+
enabled = true,
|
|
5166
|
+
autoStart = true,
|
|
5167
|
+
onCreated,
|
|
5168
|
+
onRuntimeReady,
|
|
5169
|
+
onError,
|
|
5170
|
+
...options
|
|
5171
|
+
} = config;
|
|
5172
|
+
if (!enabled || !request) {
|
|
5173
|
+
setStatus("idle");
|
|
5174
|
+
setSession(null);
|
|
5175
|
+
setRuntime(null);
|
|
5176
|
+
setClient(null);
|
|
5177
|
+
setError(null);
|
|
5178
|
+
return;
|
|
5179
|
+
}
|
|
5180
|
+
let disposed = false;
|
|
5181
|
+
let activeRuntime = null;
|
|
5182
|
+
setStatus("creating");
|
|
5183
|
+
setSession(null);
|
|
5184
|
+
setRuntime(null);
|
|
5185
|
+
setClient(null);
|
|
5186
|
+
setError(null);
|
|
5187
|
+
void createIVISessionRuntime(request, options).then(async (created) => {
|
|
5188
|
+
if (disposed) {
|
|
5189
|
+
created.runtime.stop();
|
|
5190
|
+
return;
|
|
5191
|
+
}
|
|
5192
|
+
activeRuntime = created.runtime;
|
|
5193
|
+
setSession(created.session);
|
|
5194
|
+
setRuntime(created.runtime);
|
|
5195
|
+
setClient(created.client);
|
|
5196
|
+
onCreated?.(created.session);
|
|
5197
|
+
onRuntimeReady?.(created.runtime, created.client);
|
|
5198
|
+
if (!autoStart) {
|
|
5199
|
+
setStatus("idle");
|
|
5200
|
+
return;
|
|
5201
|
+
}
|
|
5202
|
+
setStatus("connecting");
|
|
5203
|
+
await created.runtime.start();
|
|
5204
|
+
if (disposed) {
|
|
5205
|
+
created.runtime.stop();
|
|
5206
|
+
return;
|
|
5207
|
+
}
|
|
5208
|
+
setStatus("running");
|
|
5209
|
+
}).catch((caughtError) => {
|
|
5210
|
+
if (disposed) {
|
|
5211
|
+
return;
|
|
5212
|
+
}
|
|
5213
|
+
const normalizedError = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
|
|
5214
|
+
setError(normalizedError);
|
|
5215
|
+
setStatus("error");
|
|
5216
|
+
onError?.(caughtError);
|
|
5217
|
+
});
|
|
5218
|
+
return () => {
|
|
5219
|
+
disposed = true;
|
|
5220
|
+
activeRuntime?.stop();
|
|
5221
|
+
setStatus("stopped");
|
|
5222
|
+
};
|
|
5223
|
+
}, [config]);
|
|
5224
|
+
return {
|
|
5225
|
+
status,
|
|
5226
|
+
session,
|
|
5227
|
+
runtime,
|
|
5228
|
+
client,
|
|
5229
|
+
error
|
|
5230
|
+
};
|
|
5231
|
+
}
|
|
4577
5232
|
|
|
4578
|
-
export { DEFAULT_HIDE_COMPLETED_SUBTITLE_AFTER_MS, EMPTY_RUNTIME_STATE, IVILivekitPlayer, IVIStageView, IVISubtitleOverlay, IVITrackSlot, IVITrtcPlayer, IviFrontendSdk, IviRuntimeCoordinator, IviRuntimeDispatcher, LivekitSourceManager, TrtcSourceManager, isLivekitSourcePlayback, isReadyLivekitRuntimeSource, isSameLivekitConfig, useIviStageView, useIviSubtitles, useManagedIviRuntime, useRuntimeState };
|
|
5233
|
+
export { DEFAULT_HIDE_COMPLETED_SUBTITLE_AFTER_MS, EMPTY_RUNTIME_STATE, IVILivekitPlayer, IVIStageView, IVISubtitleOverlay, IVITrackSlot, IVITrtcPlayer, IviCreateIVISessionError, IviFrontendSdk, IviRuntimeCoordinator, IviRuntimeDispatcher, LivekitSourceManager, TrtcSourceManager, createIVISession, createIVISessionRuntime, createRuntimeFromSession, isLivekitSourcePlayback, isReadyLivekitRuntimeSource, isSameLivekitConfig, normalizeCreateIVISessionResponse, toCpCreateIVISessionRequestBody, useIviSessionRuntime, useIviStageView, useIviSubtitles, useManagedIviRuntime, useRuntimeState };
|
|
4579
5234
|
//# sourceMappingURL=index.js.map
|
|
4580
5235
|
//# sourceMappingURL=index.js.map
|