@vivix-ai/ivi-frontend-sdk 0.3.8 → 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 +667 -45
- 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 +661 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var iviSdkTs = require('@vivix-ai/ivi-sdk-ts');
|
|
4
|
+
var websocket = require('@vivix-ai/ivi-sdk-ts/transports/websocket');
|
|
4
5
|
var react = require('react');
|
|
5
6
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
7
|
|
|
@@ -622,7 +623,9 @@ var SourceManager = class {
|
|
|
622
623
|
durationMs: previous?.durationMs,
|
|
623
624
|
hasAudio: previous?.hasAudio,
|
|
624
625
|
error: previous?.status === "failed" ? previous.error : void 0,
|
|
625
|
-
preload: previous?.preload
|
|
626
|
+
preload: previous?.preload,
|
|
627
|
+
origin: previous?.origin,
|
|
628
|
+
provisional: previous?.provisional ? false : previous?.provisional
|
|
626
629
|
});
|
|
627
630
|
this.sources = next;
|
|
628
631
|
}
|
|
@@ -637,7 +640,38 @@ var SourceManager = class {
|
|
|
637
640
|
height: payload.height,
|
|
638
641
|
durationMs: payload.durationMs,
|
|
639
642
|
hasAudio: payload.hasAudio,
|
|
640
|
-
preload: previous?.preload
|
|
643
|
+
preload: previous?.preload,
|
|
644
|
+
origin: previous?.origin,
|
|
645
|
+
provisional: previous?.provisional ? false : previous?.provisional
|
|
646
|
+
});
|
|
647
|
+
this.sources = next;
|
|
648
|
+
}
|
|
649
|
+
upsertBootstrapReady(payload) {
|
|
650
|
+
const previous = this.sources.get(payload.sourceId);
|
|
651
|
+
const next = new Map(this.sources);
|
|
652
|
+
const source = {
|
|
653
|
+
source_id: payload.sourceId,
|
|
654
|
+
kind: payload.source?.kind ?? (payload.streamId ? "generation_stream" : "stream"),
|
|
655
|
+
stream_id: payload.source?.stream_id ?? payload.streamId,
|
|
656
|
+
asset_type: payload.source?.asset_type ?? "video",
|
|
657
|
+
url: payload.source?.url,
|
|
658
|
+
generation: payload.source?.generation,
|
|
659
|
+
metadata: {
|
|
660
|
+
...payload.source?.metadata ?? {},
|
|
661
|
+
label: payload.source?.metadata?.label ?? "prebuilt"
|
|
662
|
+
}
|
|
663
|
+
};
|
|
664
|
+
next.set(payload.sourceId, {
|
|
665
|
+
source,
|
|
666
|
+
status: "ready",
|
|
667
|
+
playback: previous?.playback ?? payload.playback,
|
|
668
|
+
width: previous?.width ?? payload.width,
|
|
669
|
+
height: previous?.height ?? payload.height,
|
|
670
|
+
durationMs: previous?.durationMs ?? payload.durationMs,
|
|
671
|
+
hasAudio: previous?.hasAudio ?? payload.hasAudio,
|
|
672
|
+
preload: previous?.preload ?? { autoclearAfterPlay: true },
|
|
673
|
+
origin: previous?.origin ?? "prebuilt-session-response",
|
|
674
|
+
provisional: previous?.provisional ?? true
|
|
641
675
|
});
|
|
642
676
|
this.sources = next;
|
|
643
677
|
}
|
|
@@ -671,11 +705,14 @@ var SourceManager = class {
|
|
|
671
705
|
const next = /* @__PURE__ */ new Map();
|
|
672
706
|
sources.forEach((source) => {
|
|
673
707
|
const previous = this.sources.get(source.source_id);
|
|
708
|
+
const playback = source.playback ?? previous?.playback;
|
|
674
709
|
next.set(source.source_id, {
|
|
675
710
|
source,
|
|
676
|
-
status:
|
|
677
|
-
playback
|
|
678
|
-
preload: previous?.preload
|
|
711
|
+
status: playback ? "ready" : "created",
|
|
712
|
+
playback,
|
|
713
|
+
preload: previous?.preload,
|
|
714
|
+
origin: previous?.origin,
|
|
715
|
+
provisional: source.playback && previous?.provisional ? false : previous?.provisional
|
|
679
716
|
});
|
|
680
717
|
});
|
|
681
718
|
this.sources = next;
|
|
@@ -1279,6 +1316,7 @@ var TrtcSourceManager = class {
|
|
|
1279
1316
|
startedVideoKeys: /* @__PURE__ */ new Set(),
|
|
1280
1317
|
startingVideoKeys: /* @__PURE__ */ new Set()
|
|
1281
1318
|
});
|
|
1319
|
+
this.log("info", `\u7ED1\u5B9A\u89C6\u56FE source=${sourceId} view=${viewId} views=${session.views.size}`);
|
|
1282
1320
|
await this.ensureConnected(session);
|
|
1283
1321
|
const binding = session.views.get(viewId);
|
|
1284
1322
|
if (!binding) {
|
|
@@ -1294,6 +1332,7 @@ var TrtcSourceManager = class {
|
|
|
1294
1332
|
}
|
|
1295
1333
|
const binding = session.views.get(viewId);
|
|
1296
1334
|
if (binding?.sourceId === sourceId) {
|
|
1335
|
+
this.log("info", `\u89E3\u7ED1\u89C6\u56FE source=${sourceId} view=${viewId}`);
|
|
1297
1336
|
binding.container.removeAttribute(TRTC_VIEW_ATTR);
|
|
1298
1337
|
session.views.delete(viewId);
|
|
1299
1338
|
}
|
|
@@ -1311,6 +1350,24 @@ var TrtcSourceManager = class {
|
|
|
1311
1350
|
binding.muted = muted;
|
|
1312
1351
|
void this.applyAudioPolicy(session);
|
|
1313
1352
|
}
|
|
1353
|
+
reassignView(fromSourceId, toSourceId, viewId) {
|
|
1354
|
+
if (fromSourceId === toSourceId) {
|
|
1355
|
+
return true;
|
|
1356
|
+
}
|
|
1357
|
+
const fromSession = this.getSession(fromSourceId);
|
|
1358
|
+
const toSession = this.getSession(toSourceId);
|
|
1359
|
+
if (!fromSession || !toSession || fromSession !== toSession) {
|
|
1360
|
+
return false;
|
|
1361
|
+
}
|
|
1362
|
+
const binding = fromSession.views.get(viewId);
|
|
1363
|
+
if (!binding || binding.sourceId !== fromSourceId) {
|
|
1364
|
+
return false;
|
|
1365
|
+
}
|
|
1366
|
+
this.log("info", `\u8FC1\u79FB\u89C6\u56FE source=${fromSourceId}->${toSourceId} view=${viewId}`);
|
|
1367
|
+
binding.sourceId = toSourceId;
|
|
1368
|
+
void this.applyAudioPolicy(fromSession);
|
|
1369
|
+
return true;
|
|
1370
|
+
}
|
|
1314
1371
|
unlinkSourceFromSession(sourceId, session) {
|
|
1315
1372
|
session.sourceIds.delete(sourceId);
|
|
1316
1373
|
this.sourceSessionKeys.delete(sourceId);
|
|
@@ -1485,6 +1542,7 @@ var TrtcSourceManager = class {
|
|
|
1485
1542
|
if (!parsed) {
|
|
1486
1543
|
continue;
|
|
1487
1544
|
}
|
|
1545
|
+
this.log("info", `\u56DE\u653E\u5DF2\u77E5\u8FDC\u7AEF\u89C6\u9891 source=${binding.sourceId} userId=${parsed.userId} streamType=${parsed.streamType}`);
|
|
1488
1546
|
await this.startRemoteVideoForBinding(
|
|
1489
1547
|
client,
|
|
1490
1548
|
parsed.userId,
|
|
@@ -1500,12 +1558,14 @@ var TrtcSourceManager = class {
|
|
|
1500
1558
|
}
|
|
1501
1559
|
binding.startingVideoKeys.add(remoteVideoKey);
|
|
1502
1560
|
try {
|
|
1561
|
+
this.log("info", `\u5F00\u59CB\u6E32\u67D3\u8FDC\u7AEF\u89C6\u9891 source=${binding.sourceId} view=${remoteVideoKey} userId=${userId} streamType=${streamType}`);
|
|
1503
1562
|
await client.startRemoteVideo({
|
|
1504
1563
|
userId,
|
|
1505
1564
|
streamType,
|
|
1506
1565
|
view: binding.container,
|
|
1507
1566
|
option: { fillMode: "contain" }
|
|
1508
1567
|
});
|
|
1568
|
+
this.log("info", `\u8FDC\u7AEF\u89C6\u9891\u6E32\u67D3\u5B8C\u6210 source=${binding.sourceId} userId=${userId} streamType=${streamType}`);
|
|
1509
1569
|
binding.startedVideoKeys.add(remoteVideoKey);
|
|
1510
1570
|
enforceContainMedia(binding.container);
|
|
1511
1571
|
} catch (error) {
|
|
@@ -1672,7 +1732,7 @@ function isRuntimeTrtcSource(source) {
|
|
|
1672
1732
|
return source.status === "ready" && source.playback?.type === "trtc" && typeof source.playback.trtc === "object" && source.playback.trtc !== null;
|
|
1673
1733
|
}
|
|
1674
1734
|
function isSameTrtcConfig(a, b) {
|
|
1675
|
-
return a.app_id === b.app_id && a.user_id === b.user_id && a.
|
|
1735
|
+
return a.app_id === b.app_id && a.user_id === b.user_id && a.room_id === b.room_id;
|
|
1676
1736
|
}
|
|
1677
1737
|
function buildTrtcSessionKey(trtc, aiDenoiser) {
|
|
1678
1738
|
const denoiser = resolveAIDenoiserOptions(aiDenoiser);
|
|
@@ -1680,7 +1740,6 @@ function buildTrtcSessionKey(trtc, aiDenoiser) {
|
|
|
1680
1740
|
appId: trtc.app_id,
|
|
1681
1741
|
roomId: trtc.room_id,
|
|
1682
1742
|
userId: trtc.user_id,
|
|
1683
|
-
userSig: trtc.user_sig,
|
|
1684
1743
|
aiDenoiser: {
|
|
1685
1744
|
enabled: denoiser.enabled,
|
|
1686
1745
|
mode: denoiser.mode,
|
|
@@ -1789,8 +1848,9 @@ function describeLivekitRoom(livekit) {
|
|
|
1789
1848
|
// src/runtime/managers/livekit-source-manager.ts
|
|
1790
1849
|
var TAG2 = "[IVI-LIVEKIT]";
|
|
1791
1850
|
var LivekitSourceManager = class {
|
|
1792
|
-
constructor(onLog) {
|
|
1851
|
+
constructor(onLog, onRemoteVideoAvailable) {
|
|
1793
1852
|
this.onLog = onLog;
|
|
1853
|
+
this.onRemoteVideoAvailable = onRemoteVideoAvailable;
|
|
1794
1854
|
this.sessions = /* @__PURE__ */ new Map();
|
|
1795
1855
|
this.listeners = /* @__PURE__ */ new Map();
|
|
1796
1856
|
}
|
|
@@ -2017,6 +2077,7 @@ var LivekitSourceManager = class {
|
|
|
2017
2077
|
waiter(true);
|
|
2018
2078
|
}
|
|
2019
2079
|
session.remoteVideoWaiters.length = 0;
|
|
2080
|
+
this.onRemoteVideoAvailable?.(session.sourceId);
|
|
2020
2081
|
}
|
|
2021
2082
|
session.views.forEach((binding) => {
|
|
2022
2083
|
this.attachTrackToView(track, kind, trackKey, binding);
|
|
@@ -2212,6 +2273,7 @@ var IviRuntimeCoordinator = class {
|
|
|
2212
2273
|
this.userTextFlowCounter = 0;
|
|
2213
2274
|
this.waitingTracksListValidation = false;
|
|
2214
2275
|
this.waitingSourcesListValidation = false;
|
|
2276
|
+
this.bootstrapSource = null;
|
|
2215
2277
|
this.state = {
|
|
2216
2278
|
status: "idle",
|
|
2217
2279
|
session: null,
|
|
@@ -2220,7 +2282,8 @@ var IviRuntimeCoordinator = class {
|
|
|
2220
2282
|
sources: /* @__PURE__ */ new Map(),
|
|
2221
2283
|
streams: /* @__PURE__ */ new Map(),
|
|
2222
2284
|
conversationItems: /* @__PURE__ */ new Map(),
|
|
2223
|
-
conversations: []
|
|
2285
|
+
conversations: [],
|
|
2286
|
+
bootstrap: null
|
|
2224
2287
|
};
|
|
2225
2288
|
this.client = client;
|
|
2226
2289
|
this.config = {
|
|
@@ -2229,10 +2292,12 @@ var IviRuntimeCoordinator = class {
|
|
|
2229
2292
|
};
|
|
2230
2293
|
this.trtcSourceManager = new TrtcSourceManager(
|
|
2231
2294
|
this.config.onLog,
|
|
2232
|
-
(event) => this.
|
|
2295
|
+
(event) => this.onTrtcManagerEvent(event),
|
|
2233
2296
|
this.config.trtcAIDenoiser
|
|
2234
2297
|
);
|
|
2235
|
-
this.livekitSourceManager = new LivekitSourceManager(this.config.onLog)
|
|
2298
|
+
this.livekitSourceManager = new LivekitSourceManager(this.config.onLog, () => {
|
|
2299
|
+
this.recomposeBootstrapState();
|
|
2300
|
+
});
|
|
2236
2301
|
this.sessionHandler = new SessionEventHandler(this.sessionManager, {
|
|
2237
2302
|
onSessionCreated: (event) => this.onSessionCreated(event),
|
|
2238
2303
|
onSessionEnded: (event) => this.onSessionEnded(event)
|
|
@@ -2313,6 +2378,22 @@ var IviRuntimeCoordinator = class {
|
|
|
2313
2378
|
emitLog(entry) {
|
|
2314
2379
|
this.config.onLog?.(entry);
|
|
2315
2380
|
}
|
|
2381
|
+
setBootstrapSource(source) {
|
|
2382
|
+
const previous = this.bootstrapSource;
|
|
2383
|
+
if (previous && (!source || previous.sourceId !== source.sourceId)) {
|
|
2384
|
+
const existing = this.sourceManager.get(previous.sourceId);
|
|
2385
|
+
if (existing?.origin === "prebuilt-session-response" && existing.provisional) {
|
|
2386
|
+
this.sourceManager.remove(previous.sourceId);
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
this.bootstrapSource = source;
|
|
2390
|
+
this.ensureBootstrapSourceRegistered();
|
|
2391
|
+
this.syncPlaybackManagers();
|
|
2392
|
+
this.setState({
|
|
2393
|
+
...this.state,
|
|
2394
|
+
sources: this.sourceManager.getAll()
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2316
2397
|
/**
|
|
2317
2398
|
* 编排一次"用户文本输入 -> 触发模型回复"链路。
|
|
2318
2399
|
*
|
|
@@ -2433,10 +2514,10 @@ var IviRuntimeCoordinator = class {
|
|
|
2433
2514
|
this.trackManager.reset();
|
|
2434
2515
|
this.sourceManager.reset();
|
|
2435
2516
|
this.streamManager.reset();
|
|
2436
|
-
this.trtcSourceManager.reset();
|
|
2437
|
-
this.livekitSourceManager.reset();
|
|
2438
2517
|
this.conversationManager.reset();
|
|
2439
2518
|
this.reset();
|
|
2519
|
+
this.ensureBootstrapSourceRegistered();
|
|
2520
|
+
this.syncPlaybackManagers();
|
|
2440
2521
|
const nextStatus = this.config.syncStageOnSessionCreated !== false ? "syncing" : "running";
|
|
2441
2522
|
this.setState({
|
|
2442
2523
|
status: nextStatus,
|
|
@@ -2474,8 +2555,8 @@ var IviRuntimeCoordinator = class {
|
|
|
2474
2555
|
onTracksChanged(listRefreshed) {
|
|
2475
2556
|
const nextStage = this.stageManager.getStage();
|
|
2476
2557
|
this.sourceManager.syncWithTracks(this.trackManager.getAll());
|
|
2477
|
-
this.
|
|
2478
|
-
this.
|
|
2558
|
+
this.ensureBootstrapSourceRegistered();
|
|
2559
|
+
this.syncPlaybackManagers();
|
|
2479
2560
|
this.setState({
|
|
2480
2561
|
...this.state,
|
|
2481
2562
|
tracks: this.trackManager.getAll(),
|
|
@@ -2489,8 +2570,8 @@ var IviRuntimeCoordinator = class {
|
|
|
2489
2570
|
applyLocalTrackTake(trackId) {
|
|
2490
2571
|
this.trackManager.applyTrackTook(trackId);
|
|
2491
2572
|
this.sourceManager.syncWithTracks(this.trackManager.getAll());
|
|
2492
|
-
this.
|
|
2493
|
-
this.
|
|
2573
|
+
this.ensureBootstrapSourceRegistered();
|
|
2574
|
+
this.syncPlaybackManagers();
|
|
2494
2575
|
this.setState({
|
|
2495
2576
|
...this.state,
|
|
2496
2577
|
tracks: this.trackManager.getAll(),
|
|
@@ -2530,8 +2611,8 @@ var IviRuntimeCoordinator = class {
|
|
|
2530
2611
|
}
|
|
2531
2612
|
onSourcesChanged(listRefreshed) {
|
|
2532
2613
|
this.sourceManager.syncWithTracks(this.trackManager.getAll());
|
|
2533
|
-
this.
|
|
2534
|
-
this.
|
|
2614
|
+
this.ensureBootstrapSourceRegistered();
|
|
2615
|
+
this.syncPlaybackManagers();
|
|
2535
2616
|
this.setState({
|
|
2536
2617
|
...this.state,
|
|
2537
2618
|
sources: this.sourceManager.getAll()
|
|
@@ -2554,12 +2635,101 @@ var IviRuntimeCoordinator = class {
|
|
|
2554
2635
|
});
|
|
2555
2636
|
}
|
|
2556
2637
|
setState(nextState) {
|
|
2557
|
-
|
|
2638
|
+
let resolvedNextState = nextState;
|
|
2639
|
+
if (this.bootstrapSource && nextState.status !== "stopped" && !nextState.sources.has(this.bootstrapSource.sourceId)) {
|
|
2640
|
+
this.ensureBootstrapSourceRegistered();
|
|
2641
|
+
resolvedNextState = {
|
|
2642
|
+
...nextState,
|
|
2643
|
+
sources: this.sourceManager.getAll()
|
|
2644
|
+
};
|
|
2645
|
+
}
|
|
2646
|
+
const composedNextState = this.composeBootstrapState(resolvedNextState);
|
|
2647
|
+
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)) {
|
|
2558
2648
|
return;
|
|
2559
2649
|
}
|
|
2560
|
-
this.state =
|
|
2650
|
+
this.state = composedNextState;
|
|
2561
2651
|
this.stateListeners.forEach((listener) => listener(this.state));
|
|
2562
2652
|
}
|
|
2653
|
+
composeBootstrapState(nextState) {
|
|
2654
|
+
const bootstrap = this.bootstrapSource;
|
|
2655
|
+
if (!bootstrap) {
|
|
2656
|
+
return nextState.bootstrap === null || nextState.bootstrap === void 0 ? nextState : { ...nextState, bootstrap: null };
|
|
2657
|
+
}
|
|
2658
|
+
const shouldExpose = this.shouldExposeBootstrap(nextState, bootstrap);
|
|
2659
|
+
const bootstrapState = {
|
|
2660
|
+
active: shouldExpose,
|
|
2661
|
+
slot: bootstrap.slot,
|
|
2662
|
+
trackId: bootstrap.trackId,
|
|
2663
|
+
sourceId: bootstrap.sourceId,
|
|
2664
|
+
streamId: bootstrap.streamId
|
|
2665
|
+
};
|
|
2666
|
+
if (!shouldExpose) {
|
|
2667
|
+
return {
|
|
2668
|
+
...nextState,
|
|
2669
|
+
bootstrap: bootstrapState
|
|
2670
|
+
};
|
|
2671
|
+
}
|
|
2672
|
+
const tracks = new Map(nextState.tracks);
|
|
2673
|
+
tracks.set(bootstrap.trackId, {
|
|
2674
|
+
track_id: bootstrap.trackId,
|
|
2675
|
+
active_source_id: bootstrap.sourceId
|
|
2676
|
+
});
|
|
2677
|
+
return {
|
|
2678
|
+
...nextState,
|
|
2679
|
+
tracks,
|
|
2680
|
+
bootstrap: bootstrapState
|
|
2681
|
+
};
|
|
2682
|
+
}
|
|
2683
|
+
shouldExposeBootstrap(state, bootstrap) {
|
|
2684
|
+
if (state.status === "stopped") {
|
|
2685
|
+
return false;
|
|
2686
|
+
}
|
|
2687
|
+
const bootstrapRuntimeSource = state.sources.get(bootstrap.sourceId);
|
|
2688
|
+
if (!isReadyRenderableSource(bootstrapRuntimeSource)) {
|
|
2689
|
+
return false;
|
|
2690
|
+
}
|
|
2691
|
+
const layer = (state.stage?.composition ?? []).find((item) => item.slot === bootstrap.slot);
|
|
2692
|
+
if (!layer) {
|
|
2693
|
+
return true;
|
|
2694
|
+
}
|
|
2695
|
+
const track = state.tracks.get(layer.track_id);
|
|
2696
|
+
const activeSourceId = track?.active_source_id;
|
|
2697
|
+
if (!activeSourceId || activeSourceId === bootstrap.sourceId) {
|
|
2698
|
+
return true;
|
|
2699
|
+
}
|
|
2700
|
+
return !this.isReadyForBootstrapHandoff(activeSourceId, state.sources.get(activeSourceId));
|
|
2701
|
+
}
|
|
2702
|
+
isReadyForBootstrapHandoff(sourceId, source) {
|
|
2703
|
+
if (!isReadyRenderableSource(source)) {
|
|
2704
|
+
return false;
|
|
2705
|
+
}
|
|
2706
|
+
if (isTrtcPlaybackSource(source)) {
|
|
2707
|
+
return this.trtcSourceManager.hasRemoteVideoAvailable(sourceId);
|
|
2708
|
+
}
|
|
2709
|
+
if (isLivekitPlaybackSource(source)) {
|
|
2710
|
+
return this.livekitSourceManager.hasRemoteVideoAvailable(sourceId);
|
|
2711
|
+
}
|
|
2712
|
+
return true;
|
|
2713
|
+
}
|
|
2714
|
+
recomposeBootstrapState() {
|
|
2715
|
+
if (!this.bootstrapSource) {
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
this.setState({ ...this.state });
|
|
2719
|
+
}
|
|
2720
|
+
ensureBootstrapSourceRegistered() {
|
|
2721
|
+
if (!this.bootstrapSource) {
|
|
2722
|
+
return;
|
|
2723
|
+
}
|
|
2724
|
+
if (this.sourceManager.has(this.bootstrapSource.sourceId)) {
|
|
2725
|
+
return;
|
|
2726
|
+
}
|
|
2727
|
+
this.sourceManager.upsertBootstrapReady(this.bootstrapSource);
|
|
2728
|
+
}
|
|
2729
|
+
syncPlaybackManagers() {
|
|
2730
|
+
this.trtcSourceManager.syncRuntimeSources(this.sourceManager.getAll());
|
|
2731
|
+
this.livekitSourceManager.syncRuntimeSources(this.sourceManager.getAll());
|
|
2732
|
+
}
|
|
2563
2733
|
emitEvent(event) {
|
|
2564
2734
|
this.progressUserTextToResponseFlows(event);
|
|
2565
2735
|
this.eventListeners.forEach((listener) => listener(event, this.state));
|
|
@@ -2568,6 +2738,12 @@ var IviRuntimeCoordinator = class {
|
|
|
2568
2738
|
this.config.onTrtcEvent?.(event);
|
|
2569
2739
|
this.trtcEventListeners.forEach((listener) => listener(event));
|
|
2570
2740
|
}
|
|
2741
|
+
onTrtcManagerEvent(event) {
|
|
2742
|
+
this.emitTrtcEvent(event);
|
|
2743
|
+
if (event.type === "remote_video_available") {
|
|
2744
|
+
this.recomposeBootstrapState();
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2571
2747
|
resetStoppedState() {
|
|
2572
2748
|
this.rejectPendingUserTextToResponseFlows(
|
|
2573
2749
|
new Error("Runtime stopped before user text to response flow completed.")
|
|
@@ -2732,7 +2908,331 @@ function isLivekitPlaybackSource(source) {
|
|
|
2732
2908
|
if (!source || !source.playback) return false;
|
|
2733
2909
|
return isLivekitSourcePlayback(source.playback);
|
|
2734
2910
|
}
|
|
2911
|
+
function isReadyRenderableSource(source) {
|
|
2912
|
+
return Boolean(source && source.status === "ready" && source.playback);
|
|
2913
|
+
}
|
|
2914
|
+
function isSameBootstrapState(a, b) {
|
|
2915
|
+
if (!a && !b) return true;
|
|
2916
|
+
if (!a || !b) return false;
|
|
2917
|
+
return a.active === b.active && a.slot === b.slot && a.trackId === b.trackId && a.sourceId === b.sourceId && a.streamId === b.streamId;
|
|
2918
|
+
}
|
|
2919
|
+
var IviCreateIVISessionError = class extends Error {
|
|
2920
|
+
constructor(response, body) {
|
|
2921
|
+
super(`CreateIVISession failed with ${response.status} ${response.statusText}`);
|
|
2922
|
+
this.name = "IviCreateIVISessionError";
|
|
2923
|
+
this.status = response.status;
|
|
2924
|
+
this.statusText = response.statusText;
|
|
2925
|
+
this.body = body;
|
|
2926
|
+
}
|
|
2927
|
+
};
|
|
2928
|
+
async function createIVISession(request, options) {
|
|
2929
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
2930
|
+
if (!fetchImpl) {
|
|
2931
|
+
throw new Error("fetch is not available. Pass options.fetch to createIVISession.");
|
|
2932
|
+
}
|
|
2933
|
+
const requestBody = toCpCreateIVISessionRequestBody(request);
|
|
2934
|
+
const response = await fetchImpl(resolveCreateIVISessionUrl(options), {
|
|
2935
|
+
method: "POST",
|
|
2936
|
+
headers: await buildHeaders(options.headers),
|
|
2937
|
+
body: JSON.stringify(requestBody),
|
|
2938
|
+
credentials: options.credentials,
|
|
2939
|
+
signal: options.signal
|
|
2940
|
+
});
|
|
2941
|
+
const responseBody = await readJsonResponse(response);
|
|
2942
|
+
if (!response.ok) {
|
|
2943
|
+
throw new IviCreateIVISessionError(response, responseBody);
|
|
2944
|
+
}
|
|
2945
|
+
return normalizeCreateIVISessionResponse(responseBody, request, requestBody);
|
|
2946
|
+
}
|
|
2947
|
+
function createRuntimeFromSession(session, options = {}) {
|
|
2948
|
+
const websocketUrl = buildWebSocketUrl(session.endpoint, options.websocket);
|
|
2949
|
+
const client = new iviSdkTs.IviClient({
|
|
2950
|
+
...options.clientConfig ?? {},
|
|
2951
|
+
transport: new websocket.WebSocketTransport({
|
|
2952
|
+
url: websocketUrl,
|
|
2953
|
+
sessionId: session.iviSessionId,
|
|
2954
|
+
protocols: options.websocket?.protocols,
|
|
2955
|
+
socketFactory: options.websocket?.socketFactory
|
|
2956
|
+
})
|
|
2957
|
+
});
|
|
2958
|
+
const runtime = new IviRuntimeCoordinator(client, options.runtimeConfig);
|
|
2959
|
+
if (options.fastStart !== false && session.prebuiltPlayback) {
|
|
2960
|
+
runtime.setBootstrapSource(toBootstrapSource(session.prebuiltPlayback, options));
|
|
2961
|
+
}
|
|
2962
|
+
return {
|
|
2963
|
+
session,
|
|
2964
|
+
client,
|
|
2965
|
+
runtime
|
|
2966
|
+
};
|
|
2967
|
+
}
|
|
2968
|
+
async function createIVISessionRuntime(request, options) {
|
|
2969
|
+
const session = await createIVISession(request, options.http);
|
|
2970
|
+
return createRuntimeFromSession(session, options);
|
|
2971
|
+
}
|
|
2972
|
+
function toCpCreateIVISessionRequestBody(request) {
|
|
2973
|
+
const body = { ...request };
|
|
2974
|
+
delete body.idempotencyKey;
|
|
2975
|
+
delete body.streamType;
|
|
2976
|
+
delete body.iviVersion;
|
|
2977
|
+
delete body.sessionParameters;
|
|
2978
|
+
delete body.enableDecoderPublish;
|
|
2979
|
+
delete body.sessionRecording;
|
|
2980
|
+
delete body.prebuiltCharacters;
|
|
2981
|
+
delete body.prebuiltStream;
|
|
2982
|
+
if (request.idempotencyKey !== void 0) body.idempotency_key = request.idempotencyKey;
|
|
2983
|
+
if (request.streamType !== void 0) body.stream_type = request.streamType;
|
|
2984
|
+
if (request.iviVersion !== void 0) body.ivi_version = request.iviVersion;
|
|
2985
|
+
if (request.sessionParameters !== void 0) body.session_parameters = request.sessionParameters;
|
|
2986
|
+
if (request.enableDecoderPublish !== void 0) body.enable_decoder_publish = request.enableDecoderPublish;
|
|
2987
|
+
if (request.sessionRecording !== void 0) {
|
|
2988
|
+
body.session_recording = {
|
|
2989
|
+
enabled: request.sessionRecording.enabled,
|
|
2990
|
+
aspect_ratio: request.sessionRecording.aspectRatio
|
|
2991
|
+
};
|
|
2992
|
+
}
|
|
2993
|
+
if (request.prebuiltCharacters !== void 0) {
|
|
2994
|
+
body.prebuilt_characters = request.prebuiltCharacters.map((character) => ({
|
|
2995
|
+
character_id: character.characterId,
|
|
2996
|
+
character_payload: serializePrebuiltPayload(character.characterPayload)
|
|
2997
|
+
}));
|
|
2998
|
+
}
|
|
2999
|
+
if (request.prebuiltStream !== void 0) {
|
|
3000
|
+
body.prebuilt_stream = {
|
|
3001
|
+
stream_id: request.prebuiltStream.streamId,
|
|
3002
|
+
mode: request.prebuiltStream.mode,
|
|
3003
|
+
stream_config: serializePrebuiltPayload(request.prebuiltStream.streamConfig)
|
|
3004
|
+
};
|
|
3005
|
+
}
|
|
3006
|
+
return body;
|
|
3007
|
+
}
|
|
3008
|
+
function normalizeCreateIVISessionResponse(responseBody, request = {}, requestBody = toCpCreateIVISessionRequestBody(request)) {
|
|
3009
|
+
const payload = unwrapResponsePayload(responseBody);
|
|
3010
|
+
const iviSessionId = getString(payload, "ivi_session_id") ?? getString(payload, "iviSessionId");
|
|
3011
|
+
const endpoint = getString(payload, "endpoint");
|
|
3012
|
+
if (!iviSessionId) {
|
|
3013
|
+
throw new Error("CreateIVISession response missing ivi_session_id.");
|
|
3014
|
+
}
|
|
3015
|
+
if (!endpoint) {
|
|
3016
|
+
throw new Error("CreateIVISession response missing endpoint.");
|
|
3017
|
+
}
|
|
3018
|
+
const trtcInfo = normalizeTrtcInfo(getObjectValue(payload, "trtc_info") ?? getObjectValue(payload, "trtcInfo"));
|
|
3019
|
+
const livekitInfo = normalizeLivekitInfo(getObjectValue(payload, "livekit_info") ?? getObjectValue(payload, "livekitInfo"));
|
|
3020
|
+
const prebuildInfo = getObjectValue(payload, "prebuild_trtc_info") ?? getObjectValue(payload, "prebuildTrtcInfo");
|
|
3021
|
+
const prebuildTrtcInfo = normalizeTrtcInfo(
|
|
3022
|
+
getObjectValue(prebuildInfo, "trtc_info") ?? getObjectValue(prebuildInfo, "trtcInfo")
|
|
3023
|
+
);
|
|
3024
|
+
const prebuildLivekitInfo = normalizeLivekitInfo(
|
|
3025
|
+
getObjectValue(prebuildInfo, "livekit_info") ?? getObjectValue(prebuildInfo, "livekitInfo")
|
|
3026
|
+
);
|
|
3027
|
+
const prebuiltPlayback = buildPrebuiltPlayback(
|
|
3028
|
+
request,
|
|
3029
|
+
requestBody,
|
|
3030
|
+
prebuildTrtcInfo,
|
|
3031
|
+
prebuildLivekitInfo
|
|
3032
|
+
);
|
|
3033
|
+
return {
|
|
3034
|
+
iviSessionId,
|
|
3035
|
+
endpoint,
|
|
3036
|
+
tokenInfo: getObjectValue(payload, "token_info") ?? getObjectValue(payload, "tokenInfo"),
|
|
3037
|
+
trtcInfo,
|
|
3038
|
+
livekitInfo,
|
|
3039
|
+
prebuildTrtcInfo: prebuildTrtcInfo || prebuildLivekitInfo ? {
|
|
3040
|
+
trtcInfo: prebuildTrtcInfo,
|
|
3041
|
+
livekitInfo: prebuildLivekitInfo
|
|
3042
|
+
} : void 0,
|
|
3043
|
+
prebuiltPlayback,
|
|
3044
|
+
raw: responseBody,
|
|
3045
|
+
requestBody
|
|
3046
|
+
};
|
|
3047
|
+
}
|
|
3048
|
+
function resolveCreateIVISessionUrl(options) {
|
|
3049
|
+
if (options.url) return options.url;
|
|
3050
|
+
if (!options.baseUrl) {
|
|
3051
|
+
throw new Error("CreateIVISession requires options.baseUrl or options.url.");
|
|
3052
|
+
}
|
|
3053
|
+
const path = options.path ?? "/v1/ivi/sessions";
|
|
3054
|
+
return new URL(path, ensureTrailingSlash(options.baseUrl)).toString();
|
|
3055
|
+
}
|
|
3056
|
+
async function buildHeaders(headers) {
|
|
3057
|
+
const resolvedHeaders = typeof headers === "function" ? await headers() : headers;
|
|
3058
|
+
const next = new Headers(resolvedHeaders);
|
|
3059
|
+
if (!next.has("content-type")) {
|
|
3060
|
+
next.set("content-type", "application/json");
|
|
3061
|
+
}
|
|
3062
|
+
return next;
|
|
3063
|
+
}
|
|
3064
|
+
async function readJsonResponse(response) {
|
|
3065
|
+
const text = await response.text();
|
|
3066
|
+
if (!text) {
|
|
3067
|
+
return null;
|
|
3068
|
+
}
|
|
3069
|
+
try {
|
|
3070
|
+
return JSON.parse(text);
|
|
3071
|
+
} catch {
|
|
3072
|
+
return text;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
function buildWebSocketUrl(endpoint, options) {
|
|
3076
|
+
const rawUrl = options?.url ?? endpoint;
|
|
3077
|
+
if (!options?.query) {
|
|
3078
|
+
return rawUrl;
|
|
3079
|
+
}
|
|
3080
|
+
const url = new URL(rawUrl);
|
|
3081
|
+
Object.entries(options.query).forEach(([key, value]) => {
|
|
3082
|
+
if (value === void 0 || value === null) return;
|
|
3083
|
+
url.searchParams.set(key, value);
|
|
3084
|
+
});
|
|
3085
|
+
return url.toString();
|
|
3086
|
+
}
|
|
3087
|
+
function toBootstrapSource(prebuiltPlayback, options) {
|
|
3088
|
+
return {
|
|
3089
|
+
sourceId: prebuiltPlayback.sourceId,
|
|
3090
|
+
streamId: prebuiltPlayback.streamId,
|
|
3091
|
+
slot: options.bootstrapSlot ?? "main",
|
|
3092
|
+
trackId: options.bootstrapTrackId ?? "__ivi_bootstrap_track",
|
|
3093
|
+
playback: prebuiltPlayback.playback,
|
|
3094
|
+
source: {
|
|
3095
|
+
source_id: prebuiltPlayback.sourceId,
|
|
3096
|
+
kind: prebuiltPlayback.streamId ? "generation_stream" : "stream",
|
|
3097
|
+
stream_id: prebuiltPlayback.streamId,
|
|
3098
|
+
asset_type: "video",
|
|
3099
|
+
metadata: {
|
|
3100
|
+
label: "prebuilt"
|
|
3101
|
+
}
|
|
3102
|
+
}
|
|
3103
|
+
};
|
|
3104
|
+
}
|
|
3105
|
+
function buildPrebuiltPlayback(request, requestBody, trtcInfo, livekitInfo) {
|
|
3106
|
+
if (!trtcInfo && !livekitInfo) {
|
|
3107
|
+
return void 0;
|
|
3108
|
+
}
|
|
3109
|
+
const streamId = resolvePrebuiltStreamId(request, requestBody, trtcInfo, livekitInfo);
|
|
3110
|
+
const sourceId = request.prebuiltStream?.sourceId ?? streamId;
|
|
3111
|
+
if (!sourceId) {
|
|
3112
|
+
return void 0;
|
|
3113
|
+
}
|
|
3114
|
+
if (trtcInfo) {
|
|
3115
|
+
return {
|
|
3116
|
+
sourceId,
|
|
3117
|
+
streamId,
|
|
3118
|
+
kind: "trtc",
|
|
3119
|
+
playback: {
|
|
3120
|
+
type: "trtc",
|
|
3121
|
+
trtc: trtcInfo
|
|
3122
|
+
},
|
|
3123
|
+
provisional: true
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
3126
|
+
if (livekitInfo) {
|
|
3127
|
+
return {
|
|
3128
|
+
sourceId,
|
|
3129
|
+
streamId,
|
|
3130
|
+
kind: "livekit",
|
|
3131
|
+
playback: {
|
|
3132
|
+
type: "livekit",
|
|
3133
|
+
livekit: livekitInfo
|
|
3134
|
+
},
|
|
3135
|
+
provisional: true
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
return void 0;
|
|
3139
|
+
}
|
|
3140
|
+
function resolvePrebuiltStreamId(request, requestBody, trtcInfo, livekitInfo) {
|
|
3141
|
+
const prebuiltStream = getObjectValue(requestBody, "prebuilt_stream");
|
|
3142
|
+
return request.prebuiltStream?.streamId ?? getString(prebuiltStream, "stream_id") ?? getString(prebuiltStream, "streamId") ?? trtcInfo?.room_id ?? livekitInfo?.room;
|
|
3143
|
+
}
|
|
3144
|
+
function normalizeTrtcInfo(value) {
|
|
3145
|
+
if (!isRecord(value)) {
|
|
3146
|
+
return void 0;
|
|
3147
|
+
}
|
|
3148
|
+
const appId = getString(value, "app_id") ?? getString(value, "appId");
|
|
3149
|
+
const roomId = getString(value, "room_id") ?? getString(value, "roomId");
|
|
3150
|
+
const userId = getString(value, "user_id") ?? getString(value, "userId");
|
|
3151
|
+
const userSig = getString(value, "user_sig") ?? getString(value, "userSig");
|
|
3152
|
+
if (!appId || !roomId || !userId || !userSig) {
|
|
3153
|
+
return void 0;
|
|
3154
|
+
}
|
|
3155
|
+
const normalized = {
|
|
3156
|
+
...value,
|
|
3157
|
+
app_id: appId,
|
|
3158
|
+
room_id: roomId,
|
|
3159
|
+
user_id: userId,
|
|
3160
|
+
user_sig: userSig
|
|
3161
|
+
};
|
|
3162
|
+
delete normalized.secret_key;
|
|
3163
|
+
delete normalized.secretKey;
|
|
3164
|
+
return normalized;
|
|
3165
|
+
}
|
|
3166
|
+
function normalizeLivekitInfo(value) {
|
|
3167
|
+
if (!isRecord(value)) {
|
|
3168
|
+
return void 0;
|
|
3169
|
+
}
|
|
3170
|
+
const wsUrl = getString(value, "ws_url") ?? getString(value, "url") ?? getString(value, "wsUrl");
|
|
3171
|
+
const token = getString(value, "token") ?? getString(value, "access_token") ?? getString(value, "accessToken");
|
|
3172
|
+
if (!wsUrl || !token) {
|
|
3173
|
+
return void 0;
|
|
3174
|
+
}
|
|
3175
|
+
return {
|
|
3176
|
+
ws_url: wsUrl,
|
|
3177
|
+
token,
|
|
3178
|
+
room: getString(value, "room") ?? getString(value, "room_name") ?? getString(value, "roomName"),
|
|
3179
|
+
identity: getString(value, "identity")
|
|
3180
|
+
};
|
|
3181
|
+
}
|
|
3182
|
+
function serializePrebuiltPayload(value) {
|
|
3183
|
+
if (typeof value === "string") {
|
|
3184
|
+
return value;
|
|
3185
|
+
}
|
|
3186
|
+
return encodeJsonBase64(value);
|
|
3187
|
+
}
|
|
3188
|
+
function encodeJsonBase64(value) {
|
|
3189
|
+
const bytes = new TextEncoder().encode(JSON.stringify(value));
|
|
3190
|
+
let binary = "";
|
|
3191
|
+
bytes.forEach((byte) => {
|
|
3192
|
+
binary += String.fromCharCode(byte);
|
|
3193
|
+
});
|
|
3194
|
+
if (typeof btoa === "function") {
|
|
3195
|
+
return btoa(binary);
|
|
3196
|
+
}
|
|
3197
|
+
const maybeBuffer = globalThis.Buffer;
|
|
3198
|
+
if (maybeBuffer) {
|
|
3199
|
+
return maybeBuffer.from(bytes).toString("base64");
|
|
3200
|
+
}
|
|
3201
|
+
throw new Error("Base64 encoder is not available in this runtime.");
|
|
3202
|
+
}
|
|
3203
|
+
function unwrapResponsePayload(value) {
|
|
3204
|
+
if (!isRecord(value)) {
|
|
3205
|
+
return {};
|
|
3206
|
+
}
|
|
3207
|
+
const data = getObjectValue(value, "data");
|
|
3208
|
+
return data ?? value;
|
|
3209
|
+
}
|
|
3210
|
+
function getObjectValue(value, key) {
|
|
3211
|
+
if (!isRecord(value)) {
|
|
3212
|
+
return void 0;
|
|
3213
|
+
}
|
|
3214
|
+
const nested = value[key];
|
|
3215
|
+
return isRecord(nested) ? nested : void 0;
|
|
3216
|
+
}
|
|
3217
|
+
function getString(value, key) {
|
|
3218
|
+
if (!isRecord(value)) {
|
|
3219
|
+
return void 0;
|
|
3220
|
+
}
|
|
3221
|
+
const nested = value[key];
|
|
3222
|
+
return typeof nested === "string" && nested.trim().length > 0 ? nested.trim() : void 0;
|
|
3223
|
+
}
|
|
3224
|
+
function isRecord(value) {
|
|
3225
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3226
|
+
}
|
|
3227
|
+
function ensureTrailingSlash(value) {
|
|
3228
|
+
return value.endsWith("/") ? value : `${value}/`;
|
|
3229
|
+
}
|
|
3230
|
+
|
|
3231
|
+
// src/sdk.ts
|
|
2735
3232
|
var IviFrontendSdk = class {
|
|
3233
|
+
constructor(config = {}) {
|
|
3234
|
+
this.config = config;
|
|
3235
|
+
}
|
|
2736
3236
|
createClient(config) {
|
|
2737
3237
|
return new iviSdkTs.IviClient(config);
|
|
2738
3238
|
}
|
|
@@ -2740,6 +3240,25 @@ var IviFrontendSdk = class {
|
|
|
2740
3240
|
const client = this.createClient(clientConfig);
|
|
2741
3241
|
return new IviRuntimeCoordinator(client, runtimeConfig);
|
|
2742
3242
|
}
|
|
3243
|
+
createIVISession(request, options) {
|
|
3244
|
+
return createIVISession(request, {
|
|
3245
|
+
...this.config.http ?? {},
|
|
3246
|
+
...options ?? {}
|
|
3247
|
+
});
|
|
3248
|
+
}
|
|
3249
|
+
createRuntimeFromSession(session, options) {
|
|
3250
|
+
return createRuntimeFromSession(session, options);
|
|
3251
|
+
}
|
|
3252
|
+
createIVISessionRuntime(request, options) {
|
|
3253
|
+
const http = {
|
|
3254
|
+
...this.config.http ?? {},
|
|
3255
|
+
...options?.http ?? {}
|
|
3256
|
+
};
|
|
3257
|
+
return createIVISessionRuntime(request, {
|
|
3258
|
+
...options ?? {},
|
|
3259
|
+
http
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
2743
3262
|
};
|
|
2744
3263
|
var IviStageViewContext = react.createContext(null);
|
|
2745
3264
|
var EMPTY_RUNTIME_STATE = {
|
|
@@ -2750,7 +3269,8 @@ var EMPTY_RUNTIME_STATE = {
|
|
|
2750
3269
|
sources: /* @__PURE__ */ new Map(),
|
|
2751
3270
|
streams: /* @__PURE__ */ new Map(),
|
|
2752
3271
|
conversationItems: /* @__PURE__ */ new Map(),
|
|
2753
|
-
conversations: []
|
|
3272
|
+
conversations: [],
|
|
3273
|
+
bootstrap: null
|
|
2754
3274
|
};
|
|
2755
3275
|
function useRuntimeState(runtime) {
|
|
2756
3276
|
const [state, setState] = react.useState(() => runtime?.getState() ?? EMPTY_RUNTIME_STATE);
|
|
@@ -2778,10 +3298,10 @@ function IVIStageView(props) {
|
|
|
2778
3298
|
const state = useRuntimeState(runtime);
|
|
2779
3299
|
const slotTrackMap = react.useMemo(() => {
|
|
2780
3300
|
return buildSlotTrackMapFromState(state);
|
|
2781
|
-
}, [state.stage]);
|
|
3301
|
+
}, [state.stage, state.bootstrap]);
|
|
2782
3302
|
const slotBindings = react.useMemo(() => {
|
|
2783
3303
|
return buildSlotBindingsFromState(state);
|
|
2784
|
-
}, [state.stage, state.tracks, state.sources]);
|
|
3304
|
+
}, [state.stage, state.tracks, state.sources, state.bootstrap]);
|
|
2785
3305
|
react.useEffect(() => {
|
|
2786
3306
|
onBindingsChange?.(slotBindings);
|
|
2787
3307
|
}, [slotBindings, onBindingsChange]);
|
|
@@ -2821,15 +3341,18 @@ function buildSlotTrackMapFromState(state) {
|
|
|
2821
3341
|
(state.stage?.composition ?? []).forEach((item) => {
|
|
2822
3342
|
map.set(item.slot, item.track_id);
|
|
2823
3343
|
});
|
|
3344
|
+
if (state.bootstrap?.active) {
|
|
3345
|
+
map.set(state.bootstrap.slot, state.bootstrap.trackId);
|
|
3346
|
+
}
|
|
2824
3347
|
return map;
|
|
2825
3348
|
}
|
|
2826
3349
|
function buildSlotBindingsFromState(state) {
|
|
2827
|
-
const
|
|
3350
|
+
const bindingsBySlot = /* @__PURE__ */ new Map();
|
|
2828
3351
|
(state.stage?.composition ?? []).forEach((item) => {
|
|
2829
3352
|
const track = state.tracks.get(item.track_id);
|
|
2830
3353
|
const sourceId = track?.active_source_id ?? null;
|
|
2831
3354
|
const source = sourceId ? state.sources.get(sourceId) : void 0;
|
|
2832
|
-
|
|
3355
|
+
bindingsBySlot.set(item.slot, {
|
|
2833
3356
|
slot: item.slot,
|
|
2834
3357
|
trackId: item.track_id,
|
|
2835
3358
|
track,
|
|
@@ -2837,7 +3360,18 @@ function buildSlotBindingsFromState(state) {
|
|
|
2837
3360
|
source
|
|
2838
3361
|
});
|
|
2839
3362
|
});
|
|
2840
|
-
|
|
3363
|
+
if (state.bootstrap?.active) {
|
|
3364
|
+
const track = state.tracks.get(state.bootstrap.trackId);
|
|
3365
|
+
const source = state.sources.get(state.bootstrap.sourceId);
|
|
3366
|
+
bindingsBySlot.set(state.bootstrap.slot, {
|
|
3367
|
+
slot: state.bootstrap.slot,
|
|
3368
|
+
trackId: state.bootstrap.trackId,
|
|
3369
|
+
track,
|
|
3370
|
+
sourceId: state.bootstrap.sourceId,
|
|
3371
|
+
source
|
|
3372
|
+
});
|
|
3373
|
+
}
|
|
3374
|
+
return Array.from(bindingsBySlot.values());
|
|
2841
3375
|
}
|
|
2842
3376
|
var VOLUME_STORAGE_KEY = "ivi-volume-preferences";
|
|
2843
3377
|
var DEFAULT_VOLUME = 80;
|
|
@@ -3605,23 +4139,25 @@ function IVITrtcPlayer(props) {
|
|
|
3605
4139
|
const [loading, setLoading] = react.useState(true);
|
|
3606
4140
|
const [error, setError] = react.useState(null);
|
|
3607
4141
|
const mutedRef = react.useRef(muted);
|
|
4142
|
+
const attachedSourceIdRef = react.useRef(null);
|
|
3608
4143
|
mutedRef.current = muted;
|
|
4144
|
+
react.useEffect(() => {
|
|
4145
|
+
const unsubscribe = manager.subscribe(resolvedSourceId, (snapshot) => {
|
|
4146
|
+
setLoading(snapshot.status === "idle" || snapshot.status === "connecting");
|
|
4147
|
+
setError(snapshot.status === "error" ? snapshot.error ?? "TRTC \u62C9\u6D41\u5931\u8D25" : null);
|
|
4148
|
+
});
|
|
4149
|
+
return unsubscribe;
|
|
4150
|
+
}, [manager, resolvedSourceId]);
|
|
3609
4151
|
react.useEffect(() => {
|
|
3610
4152
|
const container = containerRef.current;
|
|
3611
4153
|
if (!container) {
|
|
3612
4154
|
return;
|
|
3613
4155
|
}
|
|
3614
4156
|
let disposed = false;
|
|
4157
|
+
attachedSourceIdRef.current = resolvedSourceId;
|
|
3615
4158
|
if (shouldManageSourceLifecycle) {
|
|
3616
4159
|
manager.upsertSource(resolvedSourceId, trtc, trtcAIDenoiser);
|
|
3617
4160
|
}
|
|
3618
|
-
const unsubscribe = manager.subscribe(resolvedSourceId, (snapshot) => {
|
|
3619
|
-
if (disposed) {
|
|
3620
|
-
return;
|
|
3621
|
-
}
|
|
3622
|
-
setLoading(snapshot.status === "idle" || snapshot.status === "connecting");
|
|
3623
|
-
setError(snapshot.status === "error" ? snapshot.error ?? "TRTC \u62C9\u6D41\u5931\u8D25" : null);
|
|
3624
|
-
});
|
|
3625
4161
|
void manager.attachView(resolvedSourceId, viewIdRef.current, container, mutedRef.current).catch((caughtError) => {
|
|
3626
4162
|
if (disposed) {
|
|
3627
4163
|
return;
|
|
@@ -3631,15 +4167,15 @@ function IVITrtcPlayer(props) {
|
|
|
3631
4167
|
});
|
|
3632
4168
|
return () => {
|
|
3633
4169
|
disposed = true;
|
|
3634
|
-
|
|
3635
|
-
manager.detachView(
|
|
4170
|
+
const attachedSourceId = attachedSourceIdRef.current ?? resolvedSourceId;
|
|
4171
|
+
manager.detachView(attachedSourceId, viewIdRef.current);
|
|
3636
4172
|
if (shouldManageSourceLifecycle) {
|
|
3637
|
-
manager.removeSource(
|
|
4173
|
+
manager.removeSource(attachedSourceId);
|
|
3638
4174
|
}
|
|
4175
|
+
attachedSourceIdRef.current = null;
|
|
3639
4176
|
};
|
|
3640
4177
|
}, [
|
|
3641
4178
|
manager,
|
|
3642
|
-
resolvedSourceId,
|
|
3643
4179
|
shouldManageSourceLifecycle,
|
|
3644
4180
|
trtc.app_id,
|
|
3645
4181
|
trtc.room_id,
|
|
@@ -3647,6 +4183,15 @@ function IVITrtcPlayer(props) {
|
|
|
3647
4183
|
trtc.user_sig,
|
|
3648
4184
|
trtcAIDenoiser
|
|
3649
4185
|
]);
|
|
4186
|
+
react.useEffect(() => {
|
|
4187
|
+
const previousSourceId = attachedSourceIdRef.current;
|
|
4188
|
+
if (!previousSourceId || previousSourceId === resolvedSourceId) {
|
|
4189
|
+
return;
|
|
4190
|
+
}
|
|
4191
|
+
if (manager.reassignView(previousSourceId, resolvedSourceId, viewIdRef.current)) {
|
|
4192
|
+
attachedSourceIdRef.current = resolvedSourceId;
|
|
4193
|
+
}
|
|
4194
|
+
}, [manager, resolvedSourceId]);
|
|
3650
4195
|
react.useEffect(() => {
|
|
3651
4196
|
manager.updateViewMuted(resolvedSourceId, viewIdRef.current, muted);
|
|
3652
4197
|
}, [manager, muted, resolvedSourceId]);
|
|
@@ -4154,13 +4699,7 @@ function getSourceRenderKey(sourceId, source) {
|
|
|
4154
4699
|
if (!trtc) {
|
|
4155
4700
|
return sourceId;
|
|
4156
4701
|
}
|
|
4157
|
-
return [
|
|
4158
|
-
"trtc",
|
|
4159
|
-
trtc.app_id ?? "",
|
|
4160
|
-
trtc.room_id ?? "",
|
|
4161
|
-
trtc.user_id ?? "",
|
|
4162
|
-
trtc.user_sig ?? ""
|
|
4163
|
-
].join(":");
|
|
4702
|
+
return ["trtc", trtc.app_id ?? "", trtc.room_id ?? ""].join(":");
|
|
4164
4703
|
}
|
|
4165
4704
|
function TrackSlotMediaContent(props) {
|
|
4166
4705
|
const {
|
|
@@ -4616,6 +5155,82 @@ function getClientLogTag(category) {
|
|
|
4616
5155
|
if (category === "reconnect") return "[IVI-RECONNECT]";
|
|
4617
5156
|
return "[IVI-CLIENT]";
|
|
4618
5157
|
}
|
|
5158
|
+
function useIviSessionRuntime(config) {
|
|
5159
|
+
const [status, setStatus] = react.useState("idle");
|
|
5160
|
+
const [session, setSession] = react.useState(null);
|
|
5161
|
+
const [runtime, setRuntime] = react.useState(null);
|
|
5162
|
+
const [client, setClient] = react.useState(null);
|
|
5163
|
+
const [error, setError] = react.useState(null);
|
|
5164
|
+
react.useEffect(() => {
|
|
5165
|
+
const {
|
|
5166
|
+
request,
|
|
5167
|
+
enabled = true,
|
|
5168
|
+
autoStart = true,
|
|
5169
|
+
onCreated,
|
|
5170
|
+
onRuntimeReady,
|
|
5171
|
+
onError,
|
|
5172
|
+
...options
|
|
5173
|
+
} = config;
|
|
5174
|
+
if (!enabled || !request) {
|
|
5175
|
+
setStatus("idle");
|
|
5176
|
+
setSession(null);
|
|
5177
|
+
setRuntime(null);
|
|
5178
|
+
setClient(null);
|
|
5179
|
+
setError(null);
|
|
5180
|
+
return;
|
|
5181
|
+
}
|
|
5182
|
+
let disposed = false;
|
|
5183
|
+
let activeRuntime = null;
|
|
5184
|
+
setStatus("creating");
|
|
5185
|
+
setSession(null);
|
|
5186
|
+
setRuntime(null);
|
|
5187
|
+
setClient(null);
|
|
5188
|
+
setError(null);
|
|
5189
|
+
void createIVISessionRuntime(request, options).then(async (created) => {
|
|
5190
|
+
if (disposed) {
|
|
5191
|
+
created.runtime.stop();
|
|
5192
|
+
return;
|
|
5193
|
+
}
|
|
5194
|
+
activeRuntime = created.runtime;
|
|
5195
|
+
setSession(created.session);
|
|
5196
|
+
setRuntime(created.runtime);
|
|
5197
|
+
setClient(created.client);
|
|
5198
|
+
onCreated?.(created.session);
|
|
5199
|
+
onRuntimeReady?.(created.runtime, created.client);
|
|
5200
|
+
if (!autoStart) {
|
|
5201
|
+
setStatus("idle");
|
|
5202
|
+
return;
|
|
5203
|
+
}
|
|
5204
|
+
setStatus("connecting");
|
|
5205
|
+
await created.runtime.start();
|
|
5206
|
+
if (disposed) {
|
|
5207
|
+
created.runtime.stop();
|
|
5208
|
+
return;
|
|
5209
|
+
}
|
|
5210
|
+
setStatus("running");
|
|
5211
|
+
}).catch((caughtError) => {
|
|
5212
|
+
if (disposed) {
|
|
5213
|
+
return;
|
|
5214
|
+
}
|
|
5215
|
+
const normalizedError = caughtError instanceof Error ? caughtError : new Error(String(caughtError));
|
|
5216
|
+
setError(normalizedError);
|
|
5217
|
+
setStatus("error");
|
|
5218
|
+
onError?.(caughtError);
|
|
5219
|
+
});
|
|
5220
|
+
return () => {
|
|
5221
|
+
disposed = true;
|
|
5222
|
+
activeRuntime?.stop();
|
|
5223
|
+
setStatus("stopped");
|
|
5224
|
+
};
|
|
5225
|
+
}, [config]);
|
|
5226
|
+
return {
|
|
5227
|
+
status,
|
|
5228
|
+
session,
|
|
5229
|
+
runtime,
|
|
5230
|
+
client,
|
|
5231
|
+
error
|
|
5232
|
+
};
|
|
5233
|
+
}
|
|
4619
5234
|
|
|
4620
5235
|
exports.DEFAULT_HIDE_COMPLETED_SUBTITLE_AFTER_MS = DEFAULT_HIDE_COMPLETED_SUBTITLE_AFTER_MS;
|
|
4621
5236
|
exports.EMPTY_RUNTIME_STATE = EMPTY_RUNTIME_STATE;
|
|
@@ -4624,14 +5239,21 @@ exports.IVIStageView = IVIStageView;
|
|
|
4624
5239
|
exports.IVISubtitleOverlay = IVISubtitleOverlay;
|
|
4625
5240
|
exports.IVITrackSlot = IVITrackSlot;
|
|
4626
5241
|
exports.IVITrtcPlayer = IVITrtcPlayer;
|
|
5242
|
+
exports.IviCreateIVISessionError = IviCreateIVISessionError;
|
|
4627
5243
|
exports.IviFrontendSdk = IviFrontendSdk;
|
|
4628
5244
|
exports.IviRuntimeCoordinator = IviRuntimeCoordinator;
|
|
4629
5245
|
exports.IviRuntimeDispatcher = IviRuntimeDispatcher;
|
|
4630
5246
|
exports.LivekitSourceManager = LivekitSourceManager;
|
|
4631
5247
|
exports.TrtcSourceManager = TrtcSourceManager;
|
|
5248
|
+
exports.createIVISession = createIVISession;
|
|
5249
|
+
exports.createIVISessionRuntime = createIVISessionRuntime;
|
|
5250
|
+
exports.createRuntimeFromSession = createRuntimeFromSession;
|
|
4632
5251
|
exports.isLivekitSourcePlayback = isLivekitSourcePlayback;
|
|
4633
5252
|
exports.isReadyLivekitRuntimeSource = isReadyLivekitRuntimeSource;
|
|
4634
5253
|
exports.isSameLivekitConfig = isSameLivekitConfig;
|
|
5254
|
+
exports.normalizeCreateIVISessionResponse = normalizeCreateIVISessionResponse;
|
|
5255
|
+
exports.toCpCreateIVISessionRequestBody = toCpCreateIVISessionRequestBody;
|
|
5256
|
+
exports.useIviSessionRuntime = useIviSessionRuntime;
|
|
4635
5257
|
exports.useIviStageView = useIviStageView;
|
|
4636
5258
|
exports.useIviSubtitles = useIviSubtitles;
|
|
4637
5259
|
exports.useManagedIviRuntime = useManagedIviRuntime;
|