@vivix-ai/ivi-frontend-sdk 0.3.9 → 0.3.10
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 +43 -0
- package/dist/index.cjs +416 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -3
- package/dist/index.d.ts +35 -3
- package/dist/index.js +416 -53
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
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 { propagation, SpanKind, trace, SpanStatusCode, context } from '@opentelemetry/api';
|
|
2
3
|
import { WebSocketTransport } from '@vivix-ai/ivi-sdk-ts/transports/websocket';
|
|
3
4
|
import { createContext, useState, useEffect, useMemo, useRef, useContext, useCallback, useId } from 'react';
|
|
4
5
|
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
@@ -1139,6 +1140,94 @@ var ConversationManager = class {
|
|
|
1139
1140
|
];
|
|
1140
1141
|
}
|
|
1141
1142
|
};
|
|
1143
|
+
var DEFAULT_TRACER_NAME = "@vivix-ai/ivi-frontend-sdk";
|
|
1144
|
+
function getIviTelemetryTracer(options) {
|
|
1145
|
+
return options?.tracer ?? trace.getTracer(DEFAULT_TRACER_NAME);
|
|
1146
|
+
}
|
|
1147
|
+
function getIviTelemetryContext(options) {
|
|
1148
|
+
return options?.context ?? context.active();
|
|
1149
|
+
}
|
|
1150
|
+
function startIviTelemetrySpan(name, options, attributes) {
|
|
1151
|
+
const tracer = getIviTelemetryTracer(options);
|
|
1152
|
+
const parentContext = getIviTelemetryContext(options);
|
|
1153
|
+
const span = tracer.startSpan(
|
|
1154
|
+
name,
|
|
1155
|
+
{
|
|
1156
|
+
kind: SpanKind.INTERNAL,
|
|
1157
|
+
attributes: mergeIviTelemetryAttributes(options?.attributes, attributes)
|
|
1158
|
+
},
|
|
1159
|
+
parentContext
|
|
1160
|
+
);
|
|
1161
|
+
return {
|
|
1162
|
+
span,
|
|
1163
|
+
context: trace.setSpan(parentContext, span)
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
function endIviTelemetrySpan(span, attributes) {
|
|
1167
|
+
const cleaned = cleanIviTelemetryAttributes(attributes);
|
|
1168
|
+
if (cleaned) {
|
|
1169
|
+
span.setAttributes(cleaned);
|
|
1170
|
+
}
|
|
1171
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1172
|
+
span.end();
|
|
1173
|
+
}
|
|
1174
|
+
function failIviTelemetrySpan(span, error, attributes) {
|
|
1175
|
+
const cleaned = cleanIviTelemetryAttributes(attributes);
|
|
1176
|
+
if (cleaned) {
|
|
1177
|
+
span.setAttributes(cleaned);
|
|
1178
|
+
}
|
|
1179
|
+
recordIviTelemetryError(span, error);
|
|
1180
|
+
span.end();
|
|
1181
|
+
}
|
|
1182
|
+
function recordIviTelemetrySpan(name, options, attributes, error) {
|
|
1183
|
+
const { span } = startIviTelemetrySpan(name, options, attributes);
|
|
1184
|
+
if (error === void 0) {
|
|
1185
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1186
|
+
} else {
|
|
1187
|
+
recordIviTelemetryError(span, error);
|
|
1188
|
+
}
|
|
1189
|
+
span.end();
|
|
1190
|
+
}
|
|
1191
|
+
function withIviTelemetryContext(options, context, attributes) {
|
|
1192
|
+
return {
|
|
1193
|
+
...options,
|
|
1194
|
+
context,
|
|
1195
|
+
attributes: mergeIviTelemetryAttributes(options?.attributes, attributes)
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
function mergeIviTelemetryAttributes(base, next) {
|
|
1199
|
+
return cleanIviTelemetryAttributes({
|
|
1200
|
+
...base ?? {},
|
|
1201
|
+
...next ?? {}
|
|
1202
|
+
});
|
|
1203
|
+
}
|
|
1204
|
+
function cleanIviTelemetryAttributes(attributes) {
|
|
1205
|
+
const cleaned = {};
|
|
1206
|
+
Object.entries(attributes ?? {}).forEach(([key, value]) => {
|
|
1207
|
+
if (isIviTelemetryAttributeValue(value)) {
|
|
1208
|
+
cleaned[key] = value;
|
|
1209
|
+
}
|
|
1210
|
+
});
|
|
1211
|
+
return Object.keys(cleaned).length > 0 ? cleaned : void 0;
|
|
1212
|
+
}
|
|
1213
|
+
function recordIviTelemetryError(span, error) {
|
|
1214
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1215
|
+
span.recordException(error instanceof Error ? error : message);
|
|
1216
|
+
span.setStatus({
|
|
1217
|
+
code: SpanStatusCode.ERROR,
|
|
1218
|
+
message
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
function isIviTelemetryAttributeValue(value) {
|
|
1222
|
+
if (typeof value === "string" || typeof value === "boolean") return true;
|
|
1223
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1224
|
+
if (!Array.isArray(value)) return false;
|
|
1225
|
+
const nonNullValues = value.filter((item) => item !== null && item !== void 0);
|
|
1226
|
+
if (nonNullValues.length === 0) return true;
|
|
1227
|
+
const firstType = typeof nonNullValues[0];
|
|
1228
|
+
if (firstType !== "string" && firstType !== "number" && firstType !== "boolean") return false;
|
|
1229
|
+
return nonNullValues.every((item) => typeof item === firstType && (typeof item !== "number" || Number.isFinite(item)));
|
|
1230
|
+
}
|
|
1142
1231
|
|
|
1143
1232
|
// src/runtime/managers/trtc-source-manager.ts
|
|
1144
1233
|
var TAG = "[IVI-TRTC]";
|
|
@@ -1149,10 +1238,11 @@ var DEFAULT_DENOISER_OPTIONS = {
|
|
|
1149
1238
|
mode: "normal"
|
|
1150
1239
|
};
|
|
1151
1240
|
var TrtcSourceManager = class {
|
|
1152
|
-
constructor(onLog, onTrtcEvent, aiDenoiser) {
|
|
1241
|
+
constructor(onLog, onTrtcEvent, aiDenoiser, telemetry) {
|
|
1153
1242
|
this.onLog = onLog;
|
|
1154
1243
|
this.onTrtcEvent = onTrtcEvent;
|
|
1155
1244
|
this.aiDenoiser = aiDenoiser;
|
|
1245
|
+
this.telemetry = telemetry;
|
|
1156
1246
|
this.sessions = /* @__PURE__ */ new Map();
|
|
1157
1247
|
this.sourceSessionKeys = /* @__PURE__ */ new Map();
|
|
1158
1248
|
this.listeners = /* @__PURE__ */ new Map();
|
|
@@ -1448,6 +1538,11 @@ var TrtcSourceManager = class {
|
|
|
1448
1538
|
const client = TRTC.create();
|
|
1449
1539
|
const onRemoteVideoAvailable = (event) => {
|
|
1450
1540
|
this.log("info", `\u8FDC\u7AEF\u89C6\u9891\u53EF\u7528 source=${session.sourceId} userId=${event.userId} streamType=${event.streamType}`);
|
|
1541
|
+
recordIviTelemetrySpan(
|
|
1542
|
+
"ivi.media.trtc.remote_video_available",
|
|
1543
|
+
this.telemetry,
|
|
1544
|
+
buildTrtcMediaTelemetryAttributes(session.sourceId, event.userId, event.streamType)
|
|
1545
|
+
);
|
|
1451
1546
|
const remoteVideoKey = buildRemoteVideoKey(event.userId, String(event.streamType));
|
|
1452
1547
|
session.remoteVideoStreams.add(remoteVideoKey);
|
|
1453
1548
|
if (!session.hasEverReceivedRemoteVideo) {
|
|
@@ -1555,6 +1650,16 @@ var TrtcSourceManager = class {
|
|
|
1555
1650
|
return;
|
|
1556
1651
|
}
|
|
1557
1652
|
binding.startingVideoKeys.add(remoteVideoKey);
|
|
1653
|
+
const telemetryAttributes = buildTrtcMediaTelemetryAttributes(
|
|
1654
|
+
binding.sourceId,
|
|
1655
|
+
userId,
|
|
1656
|
+
streamType
|
|
1657
|
+
);
|
|
1658
|
+
const telemetrySpan = startIviTelemetrySpan(
|
|
1659
|
+
"ivi.media.trtc.start_remote_video",
|
|
1660
|
+
this.telemetry,
|
|
1661
|
+
telemetryAttributes
|
|
1662
|
+
);
|
|
1558
1663
|
try {
|
|
1559
1664
|
this.log("info", `\u5F00\u59CB\u6E32\u67D3\u8FDC\u7AEF\u89C6\u9891 source=${binding.sourceId} view=${remoteVideoKey} userId=${userId} streamType=${streamType}`);
|
|
1560
1665
|
await client.startRemoteVideo({
|
|
@@ -1563,14 +1668,24 @@ var TrtcSourceManager = class {
|
|
|
1563
1668
|
view: binding.container,
|
|
1564
1669
|
option: { fillMode: "contain" }
|
|
1565
1670
|
});
|
|
1671
|
+
endIviTelemetrySpan(telemetrySpan.span);
|
|
1672
|
+
recordIviTelemetrySpan(
|
|
1673
|
+
"ivi.media.trtc.remote_video_rendered",
|
|
1674
|
+
this.telemetry,
|
|
1675
|
+
telemetryAttributes
|
|
1676
|
+
);
|
|
1566
1677
|
this.log("info", `\u8FDC\u7AEF\u89C6\u9891\u6E32\u67D3\u5B8C\u6210 source=${binding.sourceId} userId=${userId} streamType=${streamType}`);
|
|
1567
1678
|
binding.startedVideoKeys.add(remoteVideoKey);
|
|
1568
1679
|
enforceContainMedia(binding.container);
|
|
1569
1680
|
} catch (error) {
|
|
1570
1681
|
if (error instanceof Error && /already started|OPERATION_ABORT/i.test(error.message)) {
|
|
1571
1682
|
binding.startedVideoKeys.add(remoteVideoKey);
|
|
1683
|
+
endIviTelemetrySpan(telemetrySpan.span, {
|
|
1684
|
+
"ivi.media.trtc.start_remote_video.result": "already_started"
|
|
1685
|
+
});
|
|
1572
1686
|
return;
|
|
1573
1687
|
}
|
|
1688
|
+
failIviTelemetrySpan(telemetrySpan.span, error);
|
|
1574
1689
|
this.log("warn", `\u8FDC\u7AEF\u89C6\u9891\u6E32\u67D3\u5931\u8D25 userId=${userId} streamType=${streamType}`, error);
|
|
1575
1690
|
} finally {
|
|
1576
1691
|
binding.startingVideoKeys.delete(remoteVideoKey);
|
|
@@ -1784,6 +1899,13 @@ function getAIDenoiserModeValue(mode) {
|
|
|
1784
1899
|
function buildRemoteVideoKey(userId, streamType) {
|
|
1785
1900
|
return `${userId}::${streamType}`;
|
|
1786
1901
|
}
|
|
1902
|
+
function buildTrtcMediaTelemetryAttributes(sourceId, userId, streamType) {
|
|
1903
|
+
return {
|
|
1904
|
+
"ivi.source.id": sourceId,
|
|
1905
|
+
"ivi.trtc.user_id": userId,
|
|
1906
|
+
"ivi.trtc.stream_type": String(streamType)
|
|
1907
|
+
};
|
|
1908
|
+
}
|
|
1787
1909
|
function parseRemoteVideoKey(key) {
|
|
1788
1910
|
const separatorIndex = key.indexOf("::");
|
|
1789
1911
|
if (separatorIndex < 0) {
|
|
@@ -1846,9 +1968,10 @@ function describeLivekitRoom(livekit) {
|
|
|
1846
1968
|
// src/runtime/managers/livekit-source-manager.ts
|
|
1847
1969
|
var TAG2 = "[IVI-LIVEKIT]";
|
|
1848
1970
|
var LivekitSourceManager = class {
|
|
1849
|
-
constructor(onLog, onRemoteVideoAvailable) {
|
|
1971
|
+
constructor(onLog, onRemoteVideoAvailable, telemetry) {
|
|
1850
1972
|
this.onLog = onLog;
|
|
1851
1973
|
this.onRemoteVideoAvailable = onRemoteVideoAvailable;
|
|
1974
|
+
this.telemetry = telemetry;
|
|
1852
1975
|
this.sessions = /* @__PURE__ */ new Map();
|
|
1853
1976
|
this.listeners = /* @__PURE__ */ new Map();
|
|
1854
1977
|
}
|
|
@@ -2070,6 +2193,15 @@ var LivekitSourceManager = class {
|
|
|
2070
2193
|
kind
|
|
2071
2194
|
});
|
|
2072
2195
|
if (kind === "video" && !session.hasEverReceivedRemoteVideo) {
|
|
2196
|
+
recordIviTelemetrySpan(
|
|
2197
|
+
"ivi.media.livekit.remote_video_available",
|
|
2198
|
+
this.telemetry,
|
|
2199
|
+
{
|
|
2200
|
+
"ivi.source.id": session.sourceId,
|
|
2201
|
+
"ivi.livekit.participant_identity": participant.identity,
|
|
2202
|
+
"ivi.livekit.track_sid": track.sid
|
|
2203
|
+
}
|
|
2204
|
+
);
|
|
2073
2205
|
session.hasEverReceivedRemoteVideo = true;
|
|
2074
2206
|
for (const waiter of session.remoteVideoWaiters) {
|
|
2075
2207
|
waiter(true);
|
|
@@ -2291,11 +2423,16 @@ var IviRuntimeCoordinator = class {
|
|
|
2291
2423
|
this.trtcSourceManager = new TrtcSourceManager(
|
|
2292
2424
|
this.config.onLog,
|
|
2293
2425
|
(event) => this.onTrtcManagerEvent(event),
|
|
2294
|
-
this.config.trtcAIDenoiser
|
|
2426
|
+
this.config.trtcAIDenoiser,
|
|
2427
|
+
this.config.telemetry
|
|
2428
|
+
);
|
|
2429
|
+
this.livekitSourceManager = new LivekitSourceManager(
|
|
2430
|
+
this.config.onLog,
|
|
2431
|
+
() => {
|
|
2432
|
+
this.recomposeBootstrapState();
|
|
2433
|
+
},
|
|
2434
|
+
this.config.telemetry
|
|
2295
2435
|
);
|
|
2296
|
-
this.livekitSourceManager = new LivekitSourceManager(this.config.onLog, () => {
|
|
2297
|
-
this.recomposeBootstrapState();
|
|
2298
|
-
});
|
|
2299
2436
|
this.sessionHandler = new SessionEventHandler(this.sessionManager, {
|
|
2300
2437
|
onSessionCreated: (event) => this.onSessionCreated(event),
|
|
2301
2438
|
onSessionEnded: (event) => this.onSessionEnded(event)
|
|
@@ -2332,15 +2469,27 @@ var IviRuntimeCoordinator = class {
|
|
|
2332
2469
|
* 启动后状态会从 idle/stopped 进入 connecting,连接成功后由事件驱动进入后续状态。
|
|
2333
2470
|
*/
|
|
2334
2471
|
async start() {
|
|
2335
|
-
this.
|
|
2336
|
-
|
|
2337
|
-
|
|
2472
|
+
const telemetrySpan = startIviTelemetrySpan("ivi.runtime.start", this.config.telemetry, {
|
|
2473
|
+
"ivi.runtime.status": this.state.status,
|
|
2474
|
+
"ivi.session.id": this.state.session?.id
|
|
2338
2475
|
});
|
|
2339
|
-
this.dispatcher.start();
|
|
2340
2476
|
try {
|
|
2477
|
+
this.setState({
|
|
2478
|
+
...this.state,
|
|
2479
|
+
status: "connecting"
|
|
2480
|
+
});
|
|
2481
|
+
this.dispatcher.start();
|
|
2341
2482
|
await this.client.connect();
|
|
2483
|
+
endIviTelemetrySpan(telemetrySpan.span, {
|
|
2484
|
+
"ivi.runtime.status": this.state.status,
|
|
2485
|
+
"ivi.session.id": this.state.session?.id
|
|
2486
|
+
});
|
|
2342
2487
|
} catch (error) {
|
|
2343
2488
|
this.stop();
|
|
2489
|
+
failIviTelemetrySpan(telemetrySpan.span, error, {
|
|
2490
|
+
"ivi.runtime.status": this.state.status,
|
|
2491
|
+
"ivi.session.id": this.state.session?.id
|
|
2492
|
+
});
|
|
2344
2493
|
throw error;
|
|
2345
2494
|
}
|
|
2346
2495
|
}
|
|
@@ -2348,13 +2497,31 @@ var IviRuntimeCoordinator = class {
|
|
|
2348
2497
|
* 停止协调器并断开实时连接,同时重置运行态数据为 stopped。
|
|
2349
2498
|
*/
|
|
2350
2499
|
stop() {
|
|
2351
|
-
this.
|
|
2352
|
-
|
|
2353
|
-
|
|
2500
|
+
const telemetrySpan = startIviTelemetrySpan("ivi.runtime.stop", this.config.telemetry, {
|
|
2501
|
+
"ivi.runtime.status": this.state.status,
|
|
2502
|
+
"ivi.session.id": this.state.session?.id
|
|
2503
|
+
});
|
|
2504
|
+
try {
|
|
2505
|
+
this.dispatcher.stop();
|
|
2506
|
+
this.client.disconnect();
|
|
2507
|
+
this.resetStoppedState();
|
|
2508
|
+
endIviTelemetrySpan(telemetrySpan.span, {
|
|
2509
|
+
"ivi.runtime.status": this.state.status
|
|
2510
|
+
});
|
|
2511
|
+
} catch (error) {
|
|
2512
|
+
failIviTelemetrySpan(telemetrySpan.span, error, {
|
|
2513
|
+
"ivi.runtime.status": this.state.status,
|
|
2514
|
+
"ivi.session.id": this.state.session?.id
|
|
2515
|
+
});
|
|
2516
|
+
throw error;
|
|
2517
|
+
}
|
|
2354
2518
|
}
|
|
2355
2519
|
getState() {
|
|
2356
2520
|
return this.state;
|
|
2357
2521
|
}
|
|
2522
|
+
getTelemetryOptions() {
|
|
2523
|
+
return this.config.telemetry;
|
|
2524
|
+
}
|
|
2358
2525
|
onStateChange(listener) {
|
|
2359
2526
|
this.stateListeners.add(listener);
|
|
2360
2527
|
return () => {
|
|
@@ -2645,7 +2812,16 @@ var IviRuntimeCoordinator = class {
|
|
|
2645
2812
|
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)) {
|
|
2646
2813
|
return;
|
|
2647
2814
|
}
|
|
2815
|
+
const previousState = this.state;
|
|
2648
2816
|
this.state = composedNextState;
|
|
2817
|
+
if (previousState.status !== composedNextState.status) {
|
|
2818
|
+
recordIviTelemetrySpan("ivi.runtime.state_change", this.config.telemetry, {
|
|
2819
|
+
"ivi.runtime.status.previous": previousState.status,
|
|
2820
|
+
"ivi.runtime.status.next": composedNextState.status,
|
|
2821
|
+
"ivi.runtime.state_change.reason": "state_update",
|
|
2822
|
+
"ivi.session.id": composedNextState.session?.id ?? previousState.session?.id
|
|
2823
|
+
});
|
|
2824
|
+
}
|
|
2649
2825
|
this.stateListeners.forEach((listener) => listener(this.state));
|
|
2650
2826
|
}
|
|
2651
2827
|
composeBootstrapState(nextState) {
|
|
@@ -2928,44 +3104,101 @@ async function createIVISession(request, options) {
|
|
|
2928
3104
|
if (!fetchImpl) {
|
|
2929
3105
|
throw new Error("fetch is not available. Pass options.fetch to createIVISession.");
|
|
2930
3106
|
}
|
|
2931
|
-
const
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
3107
|
+
const telemetrySpan = startIviTelemetrySpan(
|
|
3108
|
+
"ivi.session.create",
|
|
3109
|
+
options.telemetry,
|
|
3110
|
+
buildCreateSessionTelemetryAttributes(request)
|
|
3111
|
+
);
|
|
3112
|
+
try {
|
|
3113
|
+
const requestBody = toCpCreateIVISessionRequestBody(request);
|
|
3114
|
+
const headers = await buildHeaders(options.headers);
|
|
3115
|
+
injectIviTelemetryHeaders(headers, telemetrySpan.context);
|
|
3116
|
+
const response = await fetchImpl(resolveCreateIVISessionUrl(options), {
|
|
3117
|
+
method: "POST",
|
|
3118
|
+
headers,
|
|
3119
|
+
body: JSON.stringify(requestBody),
|
|
3120
|
+
credentials: options.credentials,
|
|
3121
|
+
signal: options.signal
|
|
3122
|
+
});
|
|
3123
|
+
const responseBody = await readJsonResponse(response);
|
|
3124
|
+
if (!response.ok) {
|
|
3125
|
+
const error = new IviCreateIVISessionError(response, responseBody);
|
|
3126
|
+
failIviTelemetrySpan(telemetrySpan.span, error, {
|
|
3127
|
+
"http.response.status_code": response.status,
|
|
3128
|
+
"http.response.status_text": response.statusText
|
|
3129
|
+
});
|
|
3130
|
+
throw error;
|
|
3131
|
+
}
|
|
3132
|
+
const result = normalizeCreateIVISessionResponse(responseBody, request, requestBody);
|
|
3133
|
+
endIviTelemetrySpan(telemetrySpan.span, {
|
|
3134
|
+
"http.response.status_code": response.status,
|
|
3135
|
+
"ivi.session.id": result.iviSessionId,
|
|
3136
|
+
"ivi.prebuild.playback.kind": result.prebuiltPlayback?.kind
|
|
3137
|
+
});
|
|
3138
|
+
return result;
|
|
3139
|
+
} catch (error) {
|
|
3140
|
+
if (!(error instanceof IviCreateIVISessionError)) {
|
|
3141
|
+
failIviTelemetrySpan(telemetrySpan.span, error);
|
|
3142
|
+
}
|
|
3143
|
+
throw error;
|
|
2942
3144
|
}
|
|
2943
|
-
return normalizeCreateIVISessionResponse(responseBody, request, requestBody);
|
|
2944
3145
|
}
|
|
2945
3146
|
function createRuntimeFromSession(session, options = {}) {
|
|
2946
|
-
const
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
transport: new WebSocketTransport({
|
|
2950
|
-
url: websocketUrl,
|
|
2951
|
-
sessionId: session.iviSessionId,
|
|
2952
|
-
protocols: options.websocket?.protocols,
|
|
2953
|
-
socketFactory: options.websocket?.socketFactory
|
|
2954
|
-
})
|
|
3147
|
+
const telemetrySpan = startIviTelemetrySpan("ivi.runtime.create", options.telemetry, {
|
|
3148
|
+
"ivi.session.id": session.iviSessionId,
|
|
3149
|
+
"ivi.prebuild.playback.kind": session.prebuiltPlayback?.kind
|
|
2955
3150
|
});
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3151
|
+
try {
|
|
3152
|
+
const websocketUrl = buildWebSocketUrl(session.endpoint, options.websocket);
|
|
3153
|
+
const childTelemetry = withIviTelemetryContext(options.telemetry, telemetrySpan.context, {
|
|
3154
|
+
"ivi.session.id": session.iviSessionId
|
|
3155
|
+
});
|
|
3156
|
+
const clientTelemetry = mergeIviTelemetryOptions(
|
|
3157
|
+
options.clientConfig?.telemetry,
|
|
3158
|
+
childTelemetry
|
|
3159
|
+
);
|
|
3160
|
+
const runtimeTelemetry = mergeIviTelemetryOptions(
|
|
3161
|
+
options.runtimeConfig?.telemetry,
|
|
3162
|
+
childTelemetry
|
|
3163
|
+
);
|
|
3164
|
+
const client = new IviClient({
|
|
3165
|
+
...options.clientConfig ?? {},
|
|
3166
|
+
telemetry: clientTelemetry,
|
|
3167
|
+
transport: new WebSocketTransport({
|
|
3168
|
+
url: websocketUrl,
|
|
3169
|
+
sessionId: session.iviSessionId,
|
|
3170
|
+
protocols: options.websocket?.protocols,
|
|
3171
|
+
socketFactory: options.websocket?.socketFactory
|
|
3172
|
+
})
|
|
3173
|
+
});
|
|
3174
|
+
const runtime = new IviRuntimeCoordinator(client, {
|
|
3175
|
+
...options.runtimeConfig ?? {},
|
|
3176
|
+
telemetry: runtimeTelemetry
|
|
3177
|
+
});
|
|
3178
|
+
if (options.fastStart !== false && session.prebuiltPlayback) {
|
|
3179
|
+
runtime.setBootstrapSource(toBootstrapSource(session.prebuiltPlayback, options));
|
|
3180
|
+
}
|
|
3181
|
+
endIviTelemetrySpan(telemetrySpan.span);
|
|
3182
|
+
return {
|
|
3183
|
+
session,
|
|
3184
|
+
client,
|
|
3185
|
+
runtime
|
|
3186
|
+
};
|
|
3187
|
+
} catch (error) {
|
|
3188
|
+
failIviTelemetrySpan(telemetrySpan.span, error);
|
|
3189
|
+
throw error;
|
|
2959
3190
|
}
|
|
2960
|
-
return {
|
|
2961
|
-
session,
|
|
2962
|
-
client,
|
|
2963
|
-
runtime
|
|
2964
|
-
};
|
|
2965
3191
|
}
|
|
2966
3192
|
async function createIVISessionRuntime(request, options) {
|
|
2967
|
-
const
|
|
2968
|
-
|
|
3193
|
+
const telemetry = mergeIviTelemetryOptions(options.telemetry, options.http.telemetry);
|
|
3194
|
+
const session = await createIVISession(request, {
|
|
3195
|
+
...options.http,
|
|
3196
|
+
telemetry
|
|
3197
|
+
});
|
|
3198
|
+
return createRuntimeFromSession(session, {
|
|
3199
|
+
...options,
|
|
3200
|
+
telemetry
|
|
3201
|
+
});
|
|
2969
3202
|
}
|
|
2970
3203
|
function toCpCreateIVISessionRequestBody(request) {
|
|
2971
3204
|
const body = { ...request };
|
|
@@ -3051,6 +3284,25 @@ function resolveCreateIVISessionUrl(options) {
|
|
|
3051
3284
|
const path = options.path ?? "/v1/ivi/sessions";
|
|
3052
3285
|
return new URL(path, ensureTrailingSlash(options.baseUrl)).toString();
|
|
3053
3286
|
}
|
|
3287
|
+
function buildCreateSessionTelemetryAttributes(request) {
|
|
3288
|
+
return {
|
|
3289
|
+
"ivi.version": request.iviVersion,
|
|
3290
|
+
"ivi.stream.type": request.streamType,
|
|
3291
|
+
"ivi.prebuild.enabled": Boolean(
|
|
3292
|
+
request.prebuiltStream || request.prebuiltCharacters && request.prebuiltCharacters.length > 0
|
|
3293
|
+
)
|
|
3294
|
+
};
|
|
3295
|
+
}
|
|
3296
|
+
function mergeIviTelemetryOptions(explicit, inherited) {
|
|
3297
|
+
if (!explicit && !inherited) {
|
|
3298
|
+
return void 0;
|
|
3299
|
+
}
|
|
3300
|
+
return {
|
|
3301
|
+
tracer: explicit?.tracer ?? inherited?.tracer,
|
|
3302
|
+
context: explicit?.context ?? inherited?.context,
|
|
3303
|
+
attributes: mergeIviTelemetryAttributes(inherited?.attributes, explicit?.attributes)
|
|
3304
|
+
};
|
|
3305
|
+
}
|
|
3054
3306
|
async function buildHeaders(headers) {
|
|
3055
3307
|
const resolvedHeaders = typeof headers === "function" ? await headers() : headers;
|
|
3056
3308
|
const next = new Headers(resolvedHeaders);
|
|
@@ -3059,6 +3311,14 @@ async function buildHeaders(headers) {
|
|
|
3059
3311
|
}
|
|
3060
3312
|
return next;
|
|
3061
3313
|
}
|
|
3314
|
+
var headersTextMapSetter = {
|
|
3315
|
+
set(carrier, key, value) {
|
|
3316
|
+
carrier.set(key, value);
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
function injectIviTelemetryHeaders(headers, context) {
|
|
3320
|
+
propagation.inject(context, headers, headersTextMapSetter);
|
|
3321
|
+
}
|
|
3062
3322
|
async function readJsonResponse(response) {
|
|
3063
3323
|
const text = await response.text();
|
|
3064
3324
|
if (!text) {
|
|
@@ -3232,32 +3492,71 @@ var IviFrontendSdk = class {
|
|
|
3232
3492
|
this.config = config;
|
|
3233
3493
|
}
|
|
3234
3494
|
createClient(config) {
|
|
3235
|
-
return new IviClient(
|
|
3495
|
+
return new IviClient({
|
|
3496
|
+
...config,
|
|
3497
|
+
telemetry: mergeSdkTelemetryOptions(config.telemetry, this.config.telemetry)
|
|
3498
|
+
});
|
|
3236
3499
|
}
|
|
3237
3500
|
createRuntimeCoordinator(clientConfig, runtimeConfig) {
|
|
3238
3501
|
const client = this.createClient(clientConfig);
|
|
3239
|
-
return new IviRuntimeCoordinator(client,
|
|
3502
|
+
return new IviRuntimeCoordinator(client, {
|
|
3503
|
+
...runtimeConfig ?? {},
|
|
3504
|
+
telemetry: mergeSdkTelemetryOptions(runtimeConfig?.telemetry, this.config.telemetry)
|
|
3505
|
+
});
|
|
3240
3506
|
}
|
|
3241
3507
|
createIVISession(request, options) {
|
|
3508
|
+
const telemetry = mergeSdkTelemetryChain(
|
|
3509
|
+
this.config.telemetry,
|
|
3510
|
+
this.config.http?.telemetry,
|
|
3511
|
+
options?.telemetry
|
|
3512
|
+
);
|
|
3242
3513
|
return createIVISession(request, {
|
|
3243
3514
|
...this.config.http ?? {},
|
|
3244
|
-
...options ?? {}
|
|
3515
|
+
...options ?? {},
|
|
3516
|
+
telemetry
|
|
3245
3517
|
});
|
|
3246
3518
|
}
|
|
3247
3519
|
createRuntimeFromSession(session, options) {
|
|
3248
|
-
return createRuntimeFromSession(session,
|
|
3520
|
+
return createRuntimeFromSession(session, {
|
|
3521
|
+
...options ?? {},
|
|
3522
|
+
telemetry: mergeSdkTelemetryChain(this.config.telemetry, options?.telemetry)
|
|
3523
|
+
});
|
|
3249
3524
|
}
|
|
3250
3525
|
createIVISessionRuntime(request, options) {
|
|
3526
|
+
const telemetry = mergeSdkTelemetryChain(
|
|
3527
|
+
this.config.telemetry,
|
|
3528
|
+
this.config.http?.telemetry,
|
|
3529
|
+
options?.http?.telemetry,
|
|
3530
|
+
options?.telemetry
|
|
3531
|
+
);
|
|
3251
3532
|
const http = {
|
|
3252
3533
|
...this.config.http ?? {},
|
|
3253
|
-
...options?.http ?? {}
|
|
3534
|
+
...options?.http ?? {},
|
|
3535
|
+
telemetry
|
|
3254
3536
|
};
|
|
3255
3537
|
return createIVISessionRuntime(request, {
|
|
3256
3538
|
...options ?? {},
|
|
3539
|
+
telemetry,
|
|
3257
3540
|
http
|
|
3258
3541
|
});
|
|
3259
3542
|
}
|
|
3260
3543
|
};
|
|
3544
|
+
function mergeSdkTelemetryChain(...items) {
|
|
3545
|
+
return items.reduce(
|
|
3546
|
+
(merged, item) => mergeSdkTelemetryOptions(item, merged),
|
|
3547
|
+
void 0
|
|
3548
|
+
);
|
|
3549
|
+
}
|
|
3550
|
+
function mergeSdkTelemetryOptions(explicit, inherited) {
|
|
3551
|
+
if (!explicit && !inherited) {
|
|
3552
|
+
return void 0;
|
|
3553
|
+
}
|
|
3554
|
+
return {
|
|
3555
|
+
tracer: explicit?.tracer ?? inherited?.tracer,
|
|
3556
|
+
context: explicit?.context ?? inherited?.context,
|
|
3557
|
+
attributes: mergeIviTelemetryAttributes(inherited?.attributes, explicit?.attributes)
|
|
3558
|
+
};
|
|
3559
|
+
}
|
|
3261
3560
|
var IviStageViewContext = createContext(null);
|
|
3262
3561
|
var EMPTY_RUNTIME_STATE = {
|
|
3263
3562
|
status: "idle",
|
|
@@ -4458,7 +4757,16 @@ function makeLoadPolicy(label, onLog) {
|
|
|
4458
4757
|
};
|
|
4459
4758
|
}
|
|
4460
4759
|
function IVIHlsVideo(props) {
|
|
4461
|
-
const {
|
|
4760
|
+
const {
|
|
4761
|
+
url,
|
|
4762
|
+
sourceId,
|
|
4763
|
+
videoProps,
|
|
4764
|
+
style,
|
|
4765
|
+
aggressivePreload = false,
|
|
4766
|
+
paused = false,
|
|
4767
|
+
onLog,
|
|
4768
|
+
telemetry
|
|
4769
|
+
} = props;
|
|
4462
4770
|
const videoRef = useRef(null);
|
|
4463
4771
|
const pausedRef = useRef(paused);
|
|
4464
4772
|
useEffect(() => {
|
|
@@ -4495,6 +4803,7 @@ function IVIHlsVideo(props) {
|
|
|
4495
4803
|
const onVideoError = () => {
|
|
4496
4804
|
const el = videoRef.current;
|
|
4497
4805
|
const mediaErr = el?.error;
|
|
4806
|
+
recordVideoTelemetry("ivi.media.video.error", telemetry, sourceId, "hls", mediaErr);
|
|
4498
4807
|
emitHlsLog(onLog, "warn", "<video> \u5143\u7D20\u62A5\u9519", {
|
|
4499
4808
|
code: mediaErr?.code,
|
|
4500
4809
|
message: mediaErr?.message,
|
|
@@ -4504,6 +4813,13 @@ function IVIHlsVideo(props) {
|
|
|
4504
4813
|
});
|
|
4505
4814
|
};
|
|
4506
4815
|
const onVideoStalled = () => {
|
|
4816
|
+
recordVideoTelemetry(
|
|
4817
|
+
"ivi.media.video.stalled",
|
|
4818
|
+
telemetry,
|
|
4819
|
+
sourceId,
|
|
4820
|
+
"hls",
|
|
4821
|
+
new Error("<video> stalled")
|
|
4822
|
+
);
|
|
4507
4823
|
emitHlsLog(onLog, "warn", "<video> stalled\uFF08\u7F13\u51B2\u505C\u6EDE\uFF09", {
|
|
4508
4824
|
currentTime: videoRef.current?.currentTime,
|
|
4509
4825
|
readyState: videoRef.current?.readyState
|
|
@@ -4646,7 +4962,7 @@ function IVIHlsVideo(props) {
|
|
|
4646
4962
|
hlsInstance?.destroy();
|
|
4647
4963
|
hlsInstance = null;
|
|
4648
4964
|
};
|
|
4649
|
-
}, [url, aggressivePreload, onLog]);
|
|
4965
|
+
}, [url, sourceId, aggressivePreload, onLog, telemetry]);
|
|
4650
4966
|
return /* @__PURE__ */ jsx(
|
|
4651
4967
|
"video",
|
|
4652
4968
|
{
|
|
@@ -4659,6 +4975,17 @@ function IVIHlsVideo(props) {
|
|
|
4659
4975
|
}
|
|
4660
4976
|
);
|
|
4661
4977
|
}
|
|
4978
|
+
function recordVideoTelemetry(name, telemetry, sourceId, kind, error) {
|
|
4979
|
+
recordIviTelemetrySpan(
|
|
4980
|
+
name,
|
|
4981
|
+
telemetry,
|
|
4982
|
+
{
|
|
4983
|
+
"ivi.source.id": sourceId,
|
|
4984
|
+
"ivi.media.kind": kind
|
|
4985
|
+
},
|
|
4986
|
+
error
|
|
4987
|
+
);
|
|
4988
|
+
}
|
|
4662
4989
|
function isM3u8Url(url) {
|
|
4663
4990
|
return /\.m3u8(?:$|[?#])/i.test(url);
|
|
4664
4991
|
}
|
|
@@ -4724,6 +5051,7 @@ function TrackSlotMediaContent(props) {
|
|
|
4724
5051
|
source,
|
|
4725
5052
|
isPreloading: !isActive
|
|
4726
5053
|
};
|
|
5054
|
+
const telemetry = runtime?.getTelemetryOptions();
|
|
4727
5055
|
const mediaStyle = buildAdaptiveMediaStyle(adaptToSourceSize, fitStrategy);
|
|
4728
5056
|
const shouldMute = !isActive;
|
|
4729
5057
|
if (renderMedia) return renderMedia(renderContext);
|
|
@@ -4795,9 +5123,11 @@ function TrackSlotMediaContent(props) {
|
|
|
4795
5123
|
IVIHlsVideo,
|
|
4796
5124
|
{
|
|
4797
5125
|
url: playbackUrl,
|
|
5126
|
+
sourceId: slotSourceId,
|
|
4798
5127
|
videoProps: mergedVideoProps,
|
|
4799
5128
|
style: videoStyle,
|
|
4800
|
-
paused: shouldPause
|
|
5129
|
+
paused: shouldPause,
|
|
5130
|
+
telemetry
|
|
4801
5131
|
}
|
|
4802
5132
|
);
|
|
4803
5133
|
}
|
|
@@ -4819,6 +5149,7 @@ function TrackSlotMediaContent(props) {
|
|
|
4819
5149
|
SlotVideo,
|
|
4820
5150
|
{
|
|
4821
5151
|
src: playbackUrl,
|
|
5152
|
+
sourceId: slotSourceId,
|
|
4822
5153
|
paused: shouldPause,
|
|
4823
5154
|
videoProps: {
|
|
4824
5155
|
...mergedVideoProps,
|
|
@@ -4826,7 +5157,8 @@ function TrackSlotMediaContent(props) {
|
|
|
4826
5157
|
playsInline: videoProps?.playsInline ?? true,
|
|
4827
5158
|
controls: isActive ? videoProps?.controls ?? true : false
|
|
4828
5159
|
},
|
|
4829
|
-
style: videoStyle
|
|
5160
|
+
style: videoStyle,
|
|
5161
|
+
telemetry
|
|
4830
5162
|
}
|
|
4831
5163
|
);
|
|
4832
5164
|
}
|
|
@@ -4876,17 +5208,48 @@ function SlotVideo(props) {
|
|
|
4876
5208
|
video.play().catch(() => void 0);
|
|
4877
5209
|
}
|
|
4878
5210
|
}, [props.paused]);
|
|
5211
|
+
const onError = (event) => {
|
|
5212
|
+
recordPlainVideoTelemetry(
|
|
5213
|
+
"ivi.media.video.error",
|
|
5214
|
+
props.telemetry,
|
|
5215
|
+
props.sourceId,
|
|
5216
|
+
event.currentTarget.error
|
|
5217
|
+
);
|
|
5218
|
+
props.videoProps.onError?.(event);
|
|
5219
|
+
};
|
|
5220
|
+
const onStalled = (event) => {
|
|
5221
|
+
recordPlainVideoTelemetry(
|
|
5222
|
+
"ivi.media.video.stalled",
|
|
5223
|
+
props.telemetry,
|
|
5224
|
+
props.sourceId,
|
|
5225
|
+
new Error("<video> stalled")
|
|
5226
|
+
);
|
|
5227
|
+
props.videoProps.onStalled?.(event);
|
|
5228
|
+
};
|
|
4879
5229
|
return /* @__PURE__ */ jsx(
|
|
4880
5230
|
"video",
|
|
4881
5231
|
{
|
|
4882
5232
|
ref: videoRef,
|
|
4883
5233
|
src: props.src,
|
|
4884
5234
|
...props.videoProps,
|
|
5235
|
+
onError,
|
|
5236
|
+
onStalled,
|
|
4885
5237
|
autoPlay: props.paused ? false : props.videoProps.autoPlay ?? true,
|
|
4886
5238
|
style: props.style
|
|
4887
5239
|
}
|
|
4888
5240
|
);
|
|
4889
5241
|
}
|
|
5242
|
+
function recordPlainVideoTelemetry(name, telemetry, sourceId, error) {
|
|
5243
|
+
recordIviTelemetrySpan(
|
|
5244
|
+
name,
|
|
5245
|
+
telemetry,
|
|
5246
|
+
{
|
|
5247
|
+
"ivi.source.id": sourceId,
|
|
5248
|
+
"ivi.media.kind": "video"
|
|
5249
|
+
},
|
|
5250
|
+
error
|
|
5251
|
+
);
|
|
5252
|
+
}
|
|
4890
5253
|
|
|
4891
5254
|
// src/react/internal/use-multi-preload-sources.ts
|
|
4892
5255
|
function useMultiPreloadSources(sources, activeSourceId) {
|