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