@zeewain/3d-avatar-sdk 2.1.5 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/Build/webgl.data.unityweb +0 -0
- package/dist/assets/Build/webgl.framework.js.unityweb +0 -0
- package/dist/assets/Build/webgl.wasm.unityweb +0 -0
- package/dist/examples/test-vue2/package.json +1 -1
- package/dist/examples/test-vue3/package.json +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.es5.js +1 -14877
- package/dist/index.es5.umd.js +1 -14878
- package/dist/index.esm.js +1 -2850
- package/dist/index.umd.cjs +1 -2865
- package/package.json +2 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,2850 +1 @@
|
|
|
1
|
-
/******************************************************************************
|
|
2
|
-
Copyright (c) Microsoft Corporation.
|
|
3
|
-
|
|
4
|
-
Permission to use, copy, modify, and/or distribute this software for any
|
|
5
|
-
purpose with or without fee is hereby granted.
|
|
6
|
-
|
|
7
|
-
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8
|
-
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
9
|
-
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
10
|
-
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
11
|
-
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
12
|
-
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
13
|
-
PERFORMANCE OF THIS SOFTWARE.
|
|
14
|
-
***************************************************************************** */
|
|
15
|
-
/* global Reflect, Promise, SuppressedError, Symbol, Iterator */
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
function __awaiter(thisArg, _arguments, P, generator) {
|
|
19
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
20
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
21
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
22
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
23
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
24
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
29
|
-
var e = new Error(message);
|
|
30
|
-
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* @fileoverview SDK简化错误码定义
|
|
35
|
-
* @description 定义SDK的基本错误类型和错误码,保持简单实用
|
|
36
|
-
*/
|
|
37
|
-
/**
|
|
38
|
-
* 错误类别枚举
|
|
39
|
-
* @enum {string}
|
|
40
|
-
* @description 错误的主要分类
|
|
41
|
-
*/
|
|
42
|
-
var ErrorCategory;
|
|
43
|
-
(function (ErrorCategory) {
|
|
44
|
-
/** 网络通信错误 */
|
|
45
|
-
ErrorCategory["NETWORK"] = "NETWORK";
|
|
46
|
-
/** 操作执行错误 */
|
|
47
|
-
ErrorCategory["OPERATION"] = "OPERATION";
|
|
48
|
-
/** 资源加载错误 */
|
|
49
|
-
ErrorCategory["RESOURCE"] = "RESOURCE";
|
|
50
|
-
/** 系统错误 */
|
|
51
|
-
ErrorCategory["SYSTEM"] = "SYSTEM";
|
|
52
|
-
/** 配置错误 */
|
|
53
|
-
ErrorCategory["CONFIG"] = "CONFIG";
|
|
54
|
-
})(ErrorCategory || (ErrorCategory = {}));
|
|
55
|
-
/**
|
|
56
|
-
* 网络错误码 (1xxx)
|
|
57
|
-
* @enum {number}
|
|
58
|
-
* @description 网络相关的错误码
|
|
59
|
-
*/
|
|
60
|
-
var NetworkErrorCode;
|
|
61
|
-
(function (NetworkErrorCode) {
|
|
62
|
-
/** 网络连接失败 */
|
|
63
|
-
NetworkErrorCode[NetworkErrorCode["CONNECTION_FAILED"] = 1001] = "CONNECTION_FAILED";
|
|
64
|
-
/** 请求超时 */
|
|
65
|
-
NetworkErrorCode[NetworkErrorCode["REQUEST_TIMEOUT"] = 1002] = "REQUEST_TIMEOUT";
|
|
66
|
-
/** 服务错误 */
|
|
67
|
-
NetworkErrorCode[NetworkErrorCode["SERVER_ERROR"] = 1003] = "SERVER_ERROR";
|
|
68
|
-
/** 未授权访问 */
|
|
69
|
-
NetworkErrorCode[NetworkErrorCode["UNAUTHORIZED"] = 1004] = "UNAUTHORIZED";
|
|
70
|
-
})(NetworkErrorCode || (NetworkErrorCode = {}));
|
|
71
|
-
/**
|
|
72
|
-
* 操作错误码 (2xxx)
|
|
73
|
-
* @enum {number}
|
|
74
|
-
* @description 数字人操作相关的错误码
|
|
75
|
-
*/
|
|
76
|
-
var OperationErrorCode;
|
|
77
|
-
(function (OperationErrorCode) {
|
|
78
|
-
/** Unity实例未初始化 */
|
|
79
|
-
OperationErrorCode[OperationErrorCode["UNITY_NOT_INITIALIZED"] = 2001] = "UNITY_NOT_INITIALIZED";
|
|
80
|
-
/** 数字人未加载 */
|
|
81
|
-
OperationErrorCode[OperationErrorCode["AVATAR_NOT_LOADED"] = 2002] = "AVATAR_NOT_LOADED";
|
|
82
|
-
/** 操作执行失败 */
|
|
83
|
-
OperationErrorCode[OperationErrorCode["OPERATION_FAILED"] = 2003] = "OPERATION_FAILED";
|
|
84
|
-
/** 操作超时 */
|
|
85
|
-
OperationErrorCode[OperationErrorCode["OPERATION_TIMEOUT"] = 2004] = "OPERATION_TIMEOUT";
|
|
86
|
-
/** 操作被取消 */
|
|
87
|
-
OperationErrorCode[OperationErrorCode["OPERATION_CANCELLED"] = 2005] = "OPERATION_CANCELLED";
|
|
88
|
-
/** 用户权益额度不存在 */
|
|
89
|
-
OperationErrorCode[OperationErrorCode["BROADCAST_EQUITY_NOT_EXIST"] = 2006] = "BROADCAST_EQUITY_NOT_EXIST";
|
|
90
|
-
/** 用户权益额度不足 */
|
|
91
|
-
OperationErrorCode[OperationErrorCode["BROADCAST_EQUITY_NOT_ENOUGH"] = 2007] = "BROADCAST_EQUITY_NOT_ENOUGH";
|
|
92
|
-
/** 用户权益额度冻结失败 */
|
|
93
|
-
OperationErrorCode[OperationErrorCode["BROADCAST_EQUITY_FREEZE_FAILED"] = 2008] = "BROADCAST_EQUITY_FREEZE_FAILED";
|
|
94
|
-
})(OperationErrorCode || (OperationErrorCode = {}));
|
|
95
|
-
/**
|
|
96
|
-
* 资源错误码 (3xxx)
|
|
97
|
-
* @enum {number}
|
|
98
|
-
* @description 资源加载相关的错误码
|
|
99
|
-
*/
|
|
100
|
-
var ResourceErrorCode;
|
|
101
|
-
(function (ResourceErrorCode) {
|
|
102
|
-
/** 资源加载失败 */
|
|
103
|
-
ResourceErrorCode[ResourceErrorCode["LOAD_FAILED"] = 3001] = "LOAD_FAILED";
|
|
104
|
-
/** 资源文件损坏 */
|
|
105
|
-
ResourceErrorCode[ResourceErrorCode["FILE_CORRUPTED"] = 3002] = "FILE_CORRUPTED";
|
|
106
|
-
/** 资源不存在 */
|
|
107
|
-
ResourceErrorCode[ResourceErrorCode["NOT_FOUND"] = 3003] = "NOT_FOUND";
|
|
108
|
-
/** 资源格式不支持 */
|
|
109
|
-
ResourceErrorCode[ResourceErrorCode["UNSUPPORTED_FORMAT"] = 3004] = "UNSUPPORTED_FORMAT";
|
|
110
|
-
/** 资源版本不兼容 */
|
|
111
|
-
ResourceErrorCode[ResourceErrorCode["VERSION_INCOMPATIBLE"] = 3005] = "VERSION_INCOMPATIBLE";
|
|
112
|
-
})(ResourceErrorCode || (ResourceErrorCode = {}));
|
|
113
|
-
/**
|
|
114
|
-
* 系统错误码 (4xxx)
|
|
115
|
-
* @enum {number}
|
|
116
|
-
* @description 系统资源相关的错误码
|
|
117
|
-
*/
|
|
118
|
-
var SystemErrorCode;
|
|
119
|
-
(function (SystemErrorCode) {
|
|
120
|
-
/** 内存不足 */
|
|
121
|
-
SystemErrorCode[SystemErrorCode["OUT_OF_MEMORY"] = 4001] = "OUT_OF_MEMORY";
|
|
122
|
-
/** GPU不支持 */
|
|
123
|
-
SystemErrorCode[SystemErrorCode["GPU_NOT_SUPPORTED"] = 4002] = "GPU_NOT_SUPPORTED";
|
|
124
|
-
/** WebGL不支持 */
|
|
125
|
-
SystemErrorCode[SystemErrorCode["WEBGL_NOT_SUPPORTED"] = 4003] = "WEBGL_NOT_SUPPORTED";
|
|
126
|
-
/** 浏览器不兼容 */
|
|
127
|
-
SystemErrorCode[SystemErrorCode["BROWSER_NOT_COMPATIBLE"] = 4004] = "BROWSER_NOT_COMPATIBLE";
|
|
128
|
-
})(SystemErrorCode || (SystemErrorCode = {}));
|
|
129
|
-
/**
|
|
130
|
-
* 配置错误码 (5xxx)
|
|
131
|
-
* @enum {number}
|
|
132
|
-
* @description 配置相关的错误码
|
|
133
|
-
*/
|
|
134
|
-
var ConfigErrorCode;
|
|
135
|
-
(function (ConfigErrorCode) {
|
|
136
|
-
/** 配置参数无效 */
|
|
137
|
-
ConfigErrorCode[ConfigErrorCode["INVALID_CONFIG"] = 5001] = "INVALID_CONFIG";
|
|
138
|
-
/** 必需参数缺失 */
|
|
139
|
-
ConfigErrorCode[ConfigErrorCode["MISSING_REQUIRED_PARAM"] = 5002] = "MISSING_REQUIRED_PARAM";
|
|
140
|
-
/** 参数值超出范围 */
|
|
141
|
-
ConfigErrorCode[ConfigErrorCode["PARAM_OUT_OF_RANGE"] = 5003] = "PARAM_OUT_OF_RANGE";
|
|
142
|
-
/** JSON格式错误 */
|
|
143
|
-
ConfigErrorCode[ConfigErrorCode["INVALID_JSON_FORMAT"] = 5004] = "INVALID_JSON_FORMAT";
|
|
144
|
-
})(ConfigErrorCode || (ConfigErrorCode = {}));
|
|
145
|
-
/**
|
|
146
|
-
* 错误码信息映射表
|
|
147
|
-
* @const {Record<SDKErrorCode, { category: ErrorCategory; message: string }>}
|
|
148
|
-
* @description 错误码到错误信息的映射表
|
|
149
|
-
*/
|
|
150
|
-
const ERROR_CODE_MAP = {
|
|
151
|
-
// 网络错误
|
|
152
|
-
[NetworkErrorCode.CONNECTION_FAILED]: {
|
|
153
|
-
category: ErrorCategory.NETWORK,
|
|
154
|
-
message: '网络连接失败'
|
|
155
|
-
},
|
|
156
|
-
[NetworkErrorCode.REQUEST_TIMEOUT]: {
|
|
157
|
-
category: ErrorCategory.NETWORK,
|
|
158
|
-
message: '请求超时'
|
|
159
|
-
},
|
|
160
|
-
[NetworkErrorCode.SERVER_ERROR]: {
|
|
161
|
-
category: ErrorCategory.NETWORK,
|
|
162
|
-
message: '服务器错误'
|
|
163
|
-
},
|
|
164
|
-
[NetworkErrorCode.UNAUTHORIZED]: {
|
|
165
|
-
category: ErrorCategory.NETWORK,
|
|
166
|
-
message: '未授权访问'
|
|
167
|
-
},
|
|
168
|
-
// 操作错误
|
|
169
|
-
[OperationErrorCode.UNITY_NOT_INITIALIZED]: {
|
|
170
|
-
category: ErrorCategory.OPERATION,
|
|
171
|
-
message: 'Unity实例未初始化'
|
|
172
|
-
},
|
|
173
|
-
[OperationErrorCode.AVATAR_NOT_LOADED]: {
|
|
174
|
-
category: ErrorCategory.OPERATION,
|
|
175
|
-
message: '数字人未加载'
|
|
176
|
-
},
|
|
177
|
-
[OperationErrorCode.OPERATION_FAILED]: {
|
|
178
|
-
category: ErrorCategory.OPERATION,
|
|
179
|
-
message: '操作执行失败'
|
|
180
|
-
},
|
|
181
|
-
[OperationErrorCode.OPERATION_TIMEOUT]: {
|
|
182
|
-
category: ErrorCategory.OPERATION,
|
|
183
|
-
message: '操作超时'
|
|
184
|
-
},
|
|
185
|
-
[OperationErrorCode.OPERATION_CANCELLED]: {
|
|
186
|
-
category: ErrorCategory.OPERATION,
|
|
187
|
-
message: '操作被取消'
|
|
188
|
-
},
|
|
189
|
-
[OperationErrorCode.BROADCAST_EQUITY_NOT_EXIST]: {
|
|
190
|
-
category: ErrorCategory.OPERATION,
|
|
191
|
-
message: '用户权益额度不存在'
|
|
192
|
-
},
|
|
193
|
-
[OperationErrorCode.BROADCAST_EQUITY_NOT_ENOUGH]: {
|
|
194
|
-
category: ErrorCategory.OPERATION,
|
|
195
|
-
message: '用户权益额度不足'
|
|
196
|
-
},
|
|
197
|
-
[OperationErrorCode.BROADCAST_EQUITY_FREEZE_FAILED]: {
|
|
198
|
-
category: ErrorCategory.OPERATION,
|
|
199
|
-
message: '用户权益额度冻结失败'
|
|
200
|
-
},
|
|
201
|
-
// 资源错误
|
|
202
|
-
[ResourceErrorCode.LOAD_FAILED]: {
|
|
203
|
-
category: ErrorCategory.RESOURCE,
|
|
204
|
-
message: '资源加载失败'
|
|
205
|
-
},
|
|
206
|
-
[ResourceErrorCode.FILE_CORRUPTED]: {
|
|
207
|
-
category: ErrorCategory.RESOURCE,
|
|
208
|
-
message: '资源文件损坏'
|
|
209
|
-
},
|
|
210
|
-
[ResourceErrorCode.NOT_FOUND]: {
|
|
211
|
-
category: ErrorCategory.RESOURCE,
|
|
212
|
-
message: '资源不存在'
|
|
213
|
-
},
|
|
214
|
-
[ResourceErrorCode.UNSUPPORTED_FORMAT]: {
|
|
215
|
-
category: ErrorCategory.RESOURCE,
|
|
216
|
-
message: '资源格式不支持'
|
|
217
|
-
},
|
|
218
|
-
[ResourceErrorCode.VERSION_INCOMPATIBLE]: {
|
|
219
|
-
category: ErrorCategory.RESOURCE,
|
|
220
|
-
message: '资源版本不兼容'
|
|
221
|
-
},
|
|
222
|
-
// 系统错误
|
|
223
|
-
[SystemErrorCode.OUT_OF_MEMORY]: {
|
|
224
|
-
category: ErrorCategory.SYSTEM,
|
|
225
|
-
message: '内存不足'
|
|
226
|
-
},
|
|
227
|
-
[SystemErrorCode.GPU_NOT_SUPPORTED]: {
|
|
228
|
-
category: ErrorCategory.SYSTEM,
|
|
229
|
-
message: 'GPU不支持'
|
|
230
|
-
},
|
|
231
|
-
[SystemErrorCode.WEBGL_NOT_SUPPORTED]: {
|
|
232
|
-
category: ErrorCategory.SYSTEM,
|
|
233
|
-
message: 'WebGL不支持'
|
|
234
|
-
},
|
|
235
|
-
[SystemErrorCode.BROWSER_NOT_COMPATIBLE]: {
|
|
236
|
-
category: ErrorCategory.SYSTEM,
|
|
237
|
-
message: '浏览器不兼容'
|
|
238
|
-
},
|
|
239
|
-
// 配置错误
|
|
240
|
-
[ConfigErrorCode.INVALID_CONFIG]: {
|
|
241
|
-
category: ErrorCategory.CONFIG,
|
|
242
|
-
message: '配置参数无效'
|
|
243
|
-
},
|
|
244
|
-
[ConfigErrorCode.MISSING_REQUIRED_PARAM]: {
|
|
245
|
-
category: ErrorCategory.CONFIG,
|
|
246
|
-
message: '必需参数缺失'
|
|
247
|
-
},
|
|
248
|
-
[ConfigErrorCode.PARAM_OUT_OF_RANGE]: {
|
|
249
|
-
category: ErrorCategory.CONFIG,
|
|
250
|
-
message: '参数值超出范围'
|
|
251
|
-
},
|
|
252
|
-
[ConfigErrorCode.INVALID_JSON_FORMAT]: {
|
|
253
|
-
category: ErrorCategory.CONFIG,
|
|
254
|
-
message: 'JSON格式错误'
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
/**
|
|
258
|
-
* 根据错误码获取错误信息
|
|
259
|
-
* @param code - 错误码
|
|
260
|
-
* @returns 错误信息对象,包含分类和消息
|
|
261
|
-
*/
|
|
262
|
-
function getErrorInfo(code) {
|
|
263
|
-
return ERROR_CODE_MAP[code] || {
|
|
264
|
-
category: ErrorCategory.SYSTEM,
|
|
265
|
-
message: '未知错误'
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* @fileoverview SDK错误类
|
|
271
|
-
* @description 简单的SDK错误类,用户可以通过try-catch捕获错误码
|
|
272
|
-
*/
|
|
273
|
-
/**
|
|
274
|
-
* SDK错误类
|
|
275
|
-
* @class SDKError
|
|
276
|
-
* @extends {Error}
|
|
277
|
-
* @description SDK的统一错误类,包含错误码和错误类别
|
|
278
|
-
*/
|
|
279
|
-
class SDKError extends Error {
|
|
280
|
-
/**
|
|
281
|
-
* 构造函数
|
|
282
|
-
* @param code - 错误码
|
|
283
|
-
* @param message - 自定义错误消息(可选,默认使用错误码对应的消息)
|
|
284
|
-
* @param originalError - 原始错误对象(可选)
|
|
285
|
-
*/
|
|
286
|
-
constructor(code, message, originalError) {
|
|
287
|
-
const errorInfo = getErrorInfo(code);
|
|
288
|
-
const finalMessage = message || errorInfo.message;
|
|
289
|
-
super(finalMessage);
|
|
290
|
-
this.name = 'SDKError';
|
|
291
|
-
this.code = code;
|
|
292
|
-
this.category = errorInfo.category;
|
|
293
|
-
this.message = finalMessage;
|
|
294
|
-
this.timestamp = Date.now();
|
|
295
|
-
// 保留原始错误的堆栈信息
|
|
296
|
-
if (originalError && originalError.stack) {
|
|
297
|
-
this.stack = originalError.stack;
|
|
298
|
-
}
|
|
299
|
-
else if (Error.captureStackTrace) {
|
|
300
|
-
Error.captureStackTrace(this, SDKError);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* 获取错误的JSON表示
|
|
305
|
-
* @returns 错误信息对象
|
|
306
|
-
*/
|
|
307
|
-
toJSON() {
|
|
308
|
-
return {
|
|
309
|
-
name: this.name,
|
|
310
|
-
code: this.code,
|
|
311
|
-
category: this.category,
|
|
312
|
-
message: this.message,
|
|
313
|
-
timestamp: this.timestamp
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* 创建网络错误
|
|
318
|
-
* @param code - 网络错误码
|
|
319
|
-
* @param message - 自定义错误消息
|
|
320
|
-
* @param originalError - 原始错误
|
|
321
|
-
* @returns SDKError实例
|
|
322
|
-
* @static
|
|
323
|
-
*/
|
|
324
|
-
static createNetworkError(code, message, originalError) {
|
|
325
|
-
return new SDKError(code, message, originalError);
|
|
326
|
-
}
|
|
327
|
-
/**
|
|
328
|
-
* 创建操作错误
|
|
329
|
-
* @param code - 操作错误码
|
|
330
|
-
* @param message - 自定义错误消息
|
|
331
|
-
* @param originalError - 原始错误
|
|
332
|
-
* @returns SDKError实例
|
|
333
|
-
* @static
|
|
334
|
-
*/
|
|
335
|
-
static createOperationError(code, message, originalError) {
|
|
336
|
-
return new SDKError(code, message, originalError);
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* 创建资源错误
|
|
340
|
-
* @param code - 资源错误码
|
|
341
|
-
* @param message - 自定义错误消息
|
|
342
|
-
* @param originalError - 原始错误
|
|
343
|
-
* @returns SDKError实例
|
|
344
|
-
* @static
|
|
345
|
-
*/
|
|
346
|
-
static createResourceError(code, message, originalError) {
|
|
347
|
-
return new SDKError(code, message, originalError);
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* 创建系统错误
|
|
351
|
-
* @param code - 系统错误码
|
|
352
|
-
* @param message - 自定义错误消息
|
|
353
|
-
* @param originalError - 原始错误
|
|
354
|
-
* @returns SDKError实例
|
|
355
|
-
* @static
|
|
356
|
-
*/
|
|
357
|
-
static createSystemError(code, message, originalError) {
|
|
358
|
-
return new SDKError(code, message, originalError);
|
|
359
|
-
}
|
|
360
|
-
/**
|
|
361
|
-
* 创建配置错误
|
|
362
|
-
* @param code - 配置错误码
|
|
363
|
-
* @param message - 自定义错误消息
|
|
364
|
-
* @param originalError - 原始错误
|
|
365
|
-
* @returns SDKError实例
|
|
366
|
-
* @static
|
|
367
|
-
*/
|
|
368
|
-
static createConfigError(code, message, originalError) {
|
|
369
|
-
return new SDKError(code, message, originalError);
|
|
370
|
-
}
|
|
371
|
-
/**
|
|
372
|
-
* 根据Unity错误码创建错误
|
|
373
|
-
* @param unityCode - Unity错误码
|
|
374
|
-
* @param message - 错误消息
|
|
375
|
-
* @returns SDKError实例
|
|
376
|
-
* @static
|
|
377
|
-
*/
|
|
378
|
-
static createFromUnityError(unityCode, message) {
|
|
379
|
-
/** 错误类型编码 错误名称 描述说明 常见发生场景
|
|
380
|
-
ERR - 100 资源加载失败 资源文件加载失败 AssetBundle加载失败、纹理 / 音频加载失败
|
|
381
|
-
ERR - 200 网络通信错误 网络请求相关错误 下载超时、连接中断、服务器错误
|
|
382
|
-
ERR - 300 配置参数错误 配置数据错误或缺失 JSON解析失败、参数缺失、路径错误
|
|
383
|
-
ERR - 400 操作执行失败 特定操作执行失败 动画播放失败、装扮应用失败、合并失败
|
|
384
|
-
ERR - 500 系统资源不足 系统资源限制问题 内存不足、GPU资源不足
|
|
385
|
-
ERR - 600 状态冲突错误 当前状态不允许执行此操作 在非准备状态下执行操作
|
|
386
|
-
ERR - 700 数据验证失败 数据验证或格式错误 许可证无效、数据校验失败
|
|
387
|
-
ERR - 800 第三方依赖错误 外部依赖或服务问题 云服务不可用、SDK错误
|
|
388
|
-
*/
|
|
389
|
-
switch (unityCode) {
|
|
390
|
-
case 100:
|
|
391
|
-
return new SDKError(ResourceErrorCode.LOAD_FAILED, message);
|
|
392
|
-
case 200:
|
|
393
|
-
return new SDKError(NetworkErrorCode.CONNECTION_FAILED, message);
|
|
394
|
-
case 300:
|
|
395
|
-
return new SDKError(ConfigErrorCode.INVALID_CONFIG, message);
|
|
396
|
-
case 400:
|
|
397
|
-
return new SDKError(OperationErrorCode.OPERATION_FAILED, message);
|
|
398
|
-
case 500:
|
|
399
|
-
return new SDKError(SystemErrorCode.OUT_OF_MEMORY, message);
|
|
400
|
-
case 600:
|
|
401
|
-
return new SDKError(OperationErrorCode.OPERATION_CANCELLED, message);
|
|
402
|
-
case 700:
|
|
403
|
-
return new SDKError(ConfigErrorCode.INVALID_CONFIG, message);
|
|
404
|
-
default:
|
|
405
|
-
return new SDKError(3001, message || `Unity错误 (错误码: ${unityCode})`);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Unity服务日志级别枚举
|
|
412
|
-
* @enum {string}
|
|
413
|
-
* @description 定义日志记录的级别
|
|
414
|
-
*/
|
|
415
|
-
var LogLevel;
|
|
416
|
-
(function (LogLevel) {
|
|
417
|
-
LogLevel["DEBUG"] = "debug";
|
|
418
|
-
LogLevel["INFO"] = "info";
|
|
419
|
-
LogLevel["WARN"] = "warn";
|
|
420
|
-
LogLevel["ERROR"] = "error";
|
|
421
|
-
})(LogLevel || (LogLevel = {}));
|
|
422
|
-
/**
|
|
423
|
-
* 简单日志记录器实现
|
|
424
|
-
* @class SimpleLogger
|
|
425
|
-
* @implements {ISimpleLogger}
|
|
426
|
-
* @description 提供基础的日志记录功能
|
|
427
|
-
*/
|
|
428
|
-
class SimpleLogger {
|
|
429
|
-
constructor(enableDebug = false) {
|
|
430
|
-
this.enableDebug = enableDebug;
|
|
431
|
-
}
|
|
432
|
-
pad(num, len) {
|
|
433
|
-
let s = String(num);
|
|
434
|
-
while (s.length < len) {
|
|
435
|
-
s = `0${s}`;
|
|
436
|
-
}
|
|
437
|
-
return s;
|
|
438
|
-
}
|
|
439
|
-
getTimestamp() {
|
|
440
|
-
const now = new Date();
|
|
441
|
-
const hours = this.pad(now.getHours(), 2);
|
|
442
|
-
const minutes = this.pad(now.getMinutes(), 2);
|
|
443
|
-
const seconds = this.pad(now.getSeconds(), 2);
|
|
444
|
-
const milliseconds = this.pad(now.getMilliseconds(), 3).slice(0, 2);
|
|
445
|
-
return `[${hours}:${minutes}:${seconds}.${milliseconds}]`;
|
|
446
|
-
}
|
|
447
|
-
debug(message, data) {
|
|
448
|
-
if (this.enableDebug) {
|
|
449
|
-
console.debug(`[SDK DEBUG]${this.getTimestamp()} ${message}`, data);
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
info(message, data) {
|
|
453
|
-
console.info(`[SDK INFO]${this.getTimestamp()} ${message}`, data);
|
|
454
|
-
}
|
|
455
|
-
warn(message, data) {
|
|
456
|
-
console.warn(`[SDK WARN]${this.getTimestamp()} ${message}`, data);
|
|
457
|
-
}
|
|
458
|
-
error(message, error, data) {
|
|
459
|
-
console.error(`[SDK ERROR]${this.getTimestamp()} ${message}`, error, error instanceof SDKError ? error.code : null, data);
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* @fileoverview Unity服务基础类型定义
|
|
465
|
-
* @description 定义Unity服务的通用接口和类型,为所有Unity服务提供统一的基础类型
|
|
466
|
-
*/
|
|
467
|
-
/**
|
|
468
|
-
* Unity操作结果状态枚举
|
|
469
|
-
* @enum {number}
|
|
470
|
-
* @description 定义Unity操作的标准状态码
|
|
471
|
-
*/
|
|
472
|
-
var UnityOperationStatus;
|
|
473
|
-
(function (UnityOperationStatus) {
|
|
474
|
-
/** 操作成功 */
|
|
475
|
-
UnityOperationStatus[UnityOperationStatus["SUCCESS"] = 0] = "SUCCESS";
|
|
476
|
-
/** 操作失败 */
|
|
477
|
-
UnityOperationStatus[UnityOperationStatus["FAILURE"] = 1] = "FAILURE";
|
|
478
|
-
/** 操作超时 */
|
|
479
|
-
UnityOperationStatus[UnityOperationStatus["TIMEOUT"] = 2] = "TIMEOUT";
|
|
480
|
-
/** 操作取消 */
|
|
481
|
-
UnityOperationStatus[UnityOperationStatus["CANCELLED"] = 3] = "CANCELLED";
|
|
482
|
-
})(UnityOperationStatus || (UnityOperationStatus = {}));
|
|
483
|
-
|
|
484
|
-
/**
|
|
485
|
-
* @fileoverview Unity服务抽象基类
|
|
486
|
-
* @description 提供Unity服务的通用基础实现,包括回调管理、消息发送、错误处理等功能
|
|
487
|
-
*/
|
|
488
|
-
/**
|
|
489
|
-
* 默认Unity服务配置
|
|
490
|
-
* @const {Partial<IUnityServiceConfig>}
|
|
491
|
-
*/
|
|
492
|
-
const DEFAULT_CONFIG = {
|
|
493
|
-
targetObjectName: 'AvatarSDK',
|
|
494
|
-
timeout: 30000,
|
|
495
|
-
enableDebugLog: false,
|
|
496
|
-
maxRetries: 0
|
|
497
|
-
};
|
|
498
|
-
/**
|
|
499
|
-
* Unity服务抽象基类
|
|
500
|
-
* @abstract
|
|
501
|
-
* @class UnityBaseService
|
|
502
|
-
* @implements {IUnityCallbackManager<T>, IUnityMessageSender}
|
|
503
|
-
* @description 为所有Unity服务提供通用的基础功能
|
|
504
|
-
*/
|
|
505
|
-
class UnityBaseService {
|
|
506
|
-
get uniqueCallbackName() {
|
|
507
|
-
return `${this.callbackFunctionName}_${this.instanceId}`;
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* 构造函数
|
|
511
|
-
* @param config - Unity服务配置
|
|
512
|
-
* @description 初始化基础服务,设置配置和注册全局回调
|
|
513
|
-
*/
|
|
514
|
-
constructor(config) {
|
|
515
|
-
/** 存储待处理的Promise解析函数 */
|
|
516
|
-
this.pendingCallbacks = new Map();
|
|
517
|
-
const finalConfig = Object.assign(Object.assign({}, DEFAULT_CONFIG), config);
|
|
518
|
-
this.unityInstance = finalConfig.unityInstance;
|
|
519
|
-
this.targetObjectName = finalConfig.targetObjectName;
|
|
520
|
-
this.timeout = finalConfig.timeout || DEFAULT_CONFIG.timeout;
|
|
521
|
-
this.maxRetries = finalConfig.maxRetries;
|
|
522
|
-
this.instanceId = finalConfig.instanceId || 'default';
|
|
523
|
-
this.logger = new SimpleLogger(finalConfig.enableDebugLog);
|
|
524
|
-
this.initializeGlobalCallback();
|
|
525
|
-
this.logger.debug('Unity service initialized', { config: finalConfig });
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* 处理Unity回调
|
|
529
|
-
* @param operation - 操作类型
|
|
530
|
-
* @param code - 响应代码
|
|
531
|
-
* @param message - 响应消息
|
|
532
|
-
* @param data - 额外数据,支持可变数量的参数
|
|
533
|
-
* @description 统一处理Unity的回调,管理Promise的解析和拒绝
|
|
534
|
-
* @example
|
|
535
|
-
* // Unity可能传递的不同参数结构:
|
|
536
|
-
* // handleCallback('INIT', 0, 'Success', true, 'extra_data')
|
|
537
|
-
* // handleCallback('BROADCAST', 0, 'Success', { isBroadcastCompleted: true })
|
|
538
|
-
* // handleCallback('CAMERA', 0, 'Success', cameraData, screenCapture)
|
|
539
|
-
*/
|
|
540
|
-
handleCallback(operation, code, message, data) {
|
|
541
|
-
const dataObj = data ? JSON.parse(data) : undefined;
|
|
542
|
-
this.logger.warn('[ Received Unity callback ]', { operation, code, message, data: dataObj, originalData: data });
|
|
543
|
-
const callback = this.pendingCallbacks.get(operation);
|
|
544
|
-
if (!callback) {
|
|
545
|
-
this.logger.warn(`No pending callback for operation: ${operation}`);
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
// 清理超时定时器
|
|
549
|
-
if (callback.timer) {
|
|
550
|
-
clearTimeout(callback.timer);
|
|
551
|
-
}
|
|
552
|
-
// 构建响应对象,将data参数安全地合并到响应中
|
|
553
|
-
const response = {
|
|
554
|
-
success: code === UnityOperationStatus.SUCCESS,
|
|
555
|
-
message,
|
|
556
|
-
errorCode: code,
|
|
557
|
-
data: dataObj
|
|
558
|
-
};
|
|
559
|
-
try {
|
|
560
|
-
if (code === UnityOperationStatus.SUCCESS) {
|
|
561
|
-
callback.resolve(response);
|
|
562
|
-
this.logger.debug(`Operation '${operation}' completed successfully`);
|
|
563
|
-
}
|
|
564
|
-
else {
|
|
565
|
-
const error = SDKError.createFromUnityError(code, `Unity operation '${operation}' failed: ${message}`);
|
|
566
|
-
callback.reject(error);
|
|
567
|
-
this.logger.error(`Operation '${operation}' failed`, error);
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
catch (error) {
|
|
571
|
-
this.logger.error(`Error handling callback for operation '${operation}'`, error);
|
|
572
|
-
}
|
|
573
|
-
finally {
|
|
574
|
-
this.pendingCallbacks.delete(operation);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
/**
|
|
578
|
-
* 设置回调Promise
|
|
579
|
-
* @param operation - 操作类型
|
|
580
|
-
* @returns Promise<IUnityCallbackResponse> 返回回调Promise
|
|
581
|
-
* @description 创建Promise并设置超时处理
|
|
582
|
-
*/
|
|
583
|
-
setupCallback(operation) {
|
|
584
|
-
// 如果已存在相同操作的回调,先清理
|
|
585
|
-
this.clearCallback(operation);
|
|
586
|
-
return new Promise((resolve, reject) => {
|
|
587
|
-
// 设置超时定时器
|
|
588
|
-
const timer = setTimeout(() => {
|
|
589
|
-
this.pendingCallbacks.delete(operation);
|
|
590
|
-
const timeoutError = new SDKError(OperationErrorCode.OPERATION_TIMEOUT, `Unity operation '${operation}' timed out after ${this.timeout}ms`);
|
|
591
|
-
this.logger.error(`Operation '${operation}' timed out`, timeoutError);
|
|
592
|
-
reject(timeoutError);
|
|
593
|
-
}, this.timeout);
|
|
594
|
-
this.pendingCallbacks.set(operation, {
|
|
595
|
-
resolve,
|
|
596
|
-
reject,
|
|
597
|
-
timer,
|
|
598
|
-
retries: 0
|
|
599
|
-
});
|
|
600
|
-
this.logger.debug(`Callback setup for operation '${operation}' with timeout ${this.timeout}ms`);
|
|
601
|
-
});
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* 清理指定操作的回调
|
|
605
|
-
* @param operation - 操作类型
|
|
606
|
-
* @description 清理指定操作的回调和定时器
|
|
607
|
-
*/
|
|
608
|
-
clearCallback(operation) {
|
|
609
|
-
const callback = this.pendingCallbacks.get(operation);
|
|
610
|
-
if (callback) {
|
|
611
|
-
if (callback.timer) {
|
|
612
|
-
clearTimeout(callback.timer);
|
|
613
|
-
}
|
|
614
|
-
this.pendingCallbacks.delete(operation);
|
|
615
|
-
this.logger.debug(`Cleared callback for operation '${operation}'`);
|
|
616
|
-
}
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* 清理所有待处理的回调
|
|
620
|
-
* @description 清理所有待处理的回调和定时器,通常在服务销毁时调用
|
|
621
|
-
*/
|
|
622
|
-
clearAllCallbacks() {
|
|
623
|
-
// 使用 forEach 迭代 Map,避免 TS2549 错误
|
|
624
|
-
this.pendingCallbacks.forEach((callback) => {
|
|
625
|
-
if (callback.timer) {
|
|
626
|
-
clearTimeout(callback.timer);
|
|
627
|
-
}
|
|
628
|
-
});
|
|
629
|
-
this.pendingCallbacks.clear();
|
|
630
|
-
this.logger.debug('Cleared all pending callbacks');
|
|
631
|
-
}
|
|
632
|
-
/**
|
|
633
|
-
* 发送消息到Unity(同步)
|
|
634
|
-
* @param methodName - Unity方法名
|
|
635
|
-
* @param parameter - 参数对象,可选
|
|
636
|
-
* @description 向Unity发送消息,自动处理参数序列化和错误处理
|
|
637
|
-
* @throws {SDKError} 当Unity实例未初始化或发送失败时抛出错误
|
|
638
|
-
*/
|
|
639
|
-
sendMessage(methodName, parameter) {
|
|
640
|
-
if (!this.isUnityAvailable()) {
|
|
641
|
-
throw new SDKError(OperationErrorCode.UNITY_NOT_INITIALIZED, 'Unity实例未初始化');
|
|
642
|
-
}
|
|
643
|
-
try {
|
|
644
|
-
this.logger.warn(`[ Sending message ]: ${this.targetObjectName}.${methodName}`, parameter);
|
|
645
|
-
const paramString = parameter ? JSON.stringify(parameter) : '';
|
|
646
|
-
this.unityInstance.SendMessage(this.targetObjectName, methodName, paramString);
|
|
647
|
-
}
|
|
648
|
-
catch (error) {
|
|
649
|
-
const serviceError = new SDKError(OperationErrorCode.OPERATION_FAILED, `Failed to send Unity message: ${methodName}`, error);
|
|
650
|
-
this.logger.error(`Failed to send Unity message: ${methodName}`, serviceError);
|
|
651
|
-
throw serviceError;
|
|
652
|
-
}
|
|
653
|
-
}
|
|
654
|
-
/**
|
|
655
|
-
* 发送异步消息到Unity并等待回调
|
|
656
|
-
* @param methodName - Unity方法名
|
|
657
|
-
* @param operation - 操作类型(用于回调标识)
|
|
658
|
-
* @param parameter - 参数对象,可选
|
|
659
|
-
* @returns Promise<IUnityCallbackResponse> 返回异步操作的Promise
|
|
660
|
-
* @description 向Unity发送异步消息并等待回调响应
|
|
661
|
-
* @throws {Error} 当Unity实例不可用或操作失败时抛出错误
|
|
662
|
-
*/
|
|
663
|
-
sendAsyncMessage(methodName, operation, parameter) {
|
|
664
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
665
|
-
if (!this.isUnityAvailable()) {
|
|
666
|
-
throw new SDKError(OperationErrorCode.UNITY_NOT_INITIALIZED, 'Unity实例未初始化');
|
|
667
|
-
}
|
|
668
|
-
try {
|
|
669
|
-
// 注册异步回调
|
|
670
|
-
const callbackPromise = this.setupCallback(operation);
|
|
671
|
-
// 添加回调相关信息到参数
|
|
672
|
-
const fullParameter = Object.assign(Object.assign({}, parameter), { callbackFun: this.uniqueCallbackName, operationType: operation });
|
|
673
|
-
// 发送消息到Unity
|
|
674
|
-
this.sendMessage(methodName, fullParameter);
|
|
675
|
-
// 等待回调或超时
|
|
676
|
-
return yield callbackPromise;
|
|
677
|
-
}
|
|
678
|
-
catch (error) {
|
|
679
|
-
// 清理回调
|
|
680
|
-
this.clearCallback(operation);
|
|
681
|
-
// 如果是需要重试的错误且还有重试次数
|
|
682
|
-
if (this.maxRetries > 0 && error instanceof SDKError && error.code === OperationErrorCode.OPERATION_TIMEOUT) {
|
|
683
|
-
return yield this.retryOperation(operation, methodName, parameter);
|
|
684
|
-
}
|
|
685
|
-
throw error;
|
|
686
|
-
}
|
|
687
|
-
});
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* 销毁服务
|
|
691
|
-
* @description 清理所有资源和回调
|
|
692
|
-
*/
|
|
693
|
-
destroy() {
|
|
694
|
-
this.clearAllCallbacks();
|
|
695
|
-
// 清理全局回调函数
|
|
696
|
-
if (window[this.uniqueCallbackName]) {
|
|
697
|
-
delete window[this.uniqueCallbackName];
|
|
698
|
-
this.logger.debug(`Global callback unregistered: ${this.uniqueCallbackName}`);
|
|
699
|
-
}
|
|
700
|
-
this.logger.info('Unity service destroyed');
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* 检查Unity实例是否可用
|
|
704
|
-
* @returns boolean Unity实例是否可用
|
|
705
|
-
* @description 检查Unity实例是否已初始化且可用
|
|
706
|
-
*/
|
|
707
|
-
isUnityAvailable() {
|
|
708
|
-
return this.unityInstance !== null && typeof this.unityInstance.SendMessage === 'function';
|
|
709
|
-
}
|
|
710
|
-
/**
|
|
711
|
-
* 获取待处理回调数量
|
|
712
|
-
* @returns number 待处理回调数量
|
|
713
|
-
* @description 获取当前待处理的回调Promise数量
|
|
714
|
-
*/
|
|
715
|
-
getPendingCallbackCount() {
|
|
716
|
-
return this.pendingCallbacks.size;
|
|
717
|
-
}
|
|
718
|
-
/**
|
|
719
|
-
* 初始化全局回调函数
|
|
720
|
-
* @description 注册全局回调函数到window对象
|
|
721
|
-
* @protected
|
|
722
|
-
*/
|
|
723
|
-
initializeGlobalCallback() {
|
|
724
|
-
window[this.uniqueCallbackName] = (operation, code, message, data) => {
|
|
725
|
-
this.handleCallback(operation, code, message, data);
|
|
726
|
-
};
|
|
727
|
-
this.logger.warn(`Global callback registered: ${this.uniqueCallbackName}`);
|
|
728
|
-
}
|
|
729
|
-
/**
|
|
730
|
-
* 重试操作
|
|
731
|
-
* @param operation - 操作类型
|
|
732
|
-
* @param methodName - Unity方法名
|
|
733
|
-
* @param parameter - 参数
|
|
734
|
-
* @returns Promise<IUnityCallbackResponse> 返回操作结果
|
|
735
|
-
* @description 支持重试的异步操作
|
|
736
|
-
* @protected
|
|
737
|
-
*/
|
|
738
|
-
retryOperation(operation, methodName, parameter) {
|
|
739
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
740
|
-
let lastError = null;
|
|
741
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
742
|
-
try {
|
|
743
|
-
if (attempt > 0) {
|
|
744
|
-
this.logger.debug(`Retrying operation '${operation}', attempt ${attempt}/${this.maxRetries}`);
|
|
745
|
-
// 简单的延迟重试策略
|
|
746
|
-
yield new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
|
|
747
|
-
}
|
|
748
|
-
return yield this.sendAsyncMessage(methodName, operation, parameter);
|
|
749
|
-
}
|
|
750
|
-
catch (error) {
|
|
751
|
-
lastError = error;
|
|
752
|
-
this.logger.warn(`Operation '${operation}' failed, attempt ${attempt + 1}/${this.maxRetries + 1}`, error);
|
|
753
|
-
// 如果是超时错误且还有重试次数,继续重试
|
|
754
|
-
if (error instanceof SDKError && error.code === OperationErrorCode.OPERATION_TIMEOUT && attempt < this.maxRetries) {
|
|
755
|
-
continue;
|
|
756
|
-
}
|
|
757
|
-
// 其他类型的错误或没有重试次数了,直接抛出
|
|
758
|
-
if (!(error instanceof SDKError && error.code === OperationErrorCode.OPERATION_TIMEOUT) || attempt >= this.maxRetries) {
|
|
759
|
-
throw error;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
throw lastError || new SDKError(OperationErrorCode.OPERATION_TIMEOUT, `All retry attempts failed for operation: ${operation}`);
|
|
764
|
-
});
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
/**
|
|
769
|
-
* @fileoverview Avatar API接口定义
|
|
770
|
-
* @description 定义数字人控制API的核心接口、类型和枚举
|
|
771
|
-
*/
|
|
772
|
-
/**
|
|
773
|
-
* Avatar操作类型枚举
|
|
774
|
-
* @enum {string}
|
|
775
|
-
* @description 定义Avatar服务支持的操作类型
|
|
776
|
-
*/
|
|
777
|
-
var AvatarOperationType;
|
|
778
|
-
(function (AvatarOperationType) {
|
|
779
|
-
AvatarOperationType["INITIALIZE_AVATAR"] = "initializeAvatar";
|
|
780
|
-
AvatarOperationType["PLAY_MOTION"] = "playMotion";
|
|
781
|
-
AvatarOperationType["GET_CURRENT_MOTION"] = "getCurrentMotion";
|
|
782
|
-
AvatarOperationType["UNLOAD_AVATAR"] = "unloadAvatar";
|
|
783
|
-
AvatarOperationType["SET_CAMERA"] = "setCamera";
|
|
784
|
-
})(AvatarOperationType || (AvatarOperationType = {}));
|
|
785
|
-
/**
|
|
786
|
-
* Avatar摄像机类型枚举
|
|
787
|
-
* @enum {string}
|
|
788
|
-
* @description 定义Avatar支持的摄像机类型
|
|
789
|
-
*/
|
|
790
|
-
var AvatarCameraType;
|
|
791
|
-
(function (AvatarCameraType) {
|
|
792
|
-
AvatarCameraType["WHOLE"] = "whole";
|
|
793
|
-
AvatarCameraType["HALF"] = "half";
|
|
794
|
-
AvatarCameraType["FACE"] = "face";
|
|
795
|
-
})(AvatarCameraType || (AvatarCameraType = {}));
|
|
796
|
-
|
|
797
|
-
/**
|
|
798
|
-
* @fileoverview Avatar API重构实现模块
|
|
799
|
-
* @description 提供数字人控制API的重构实现,继承Unity基础服务,包括数字人初始化、动作控制、摄像机设置等功能
|
|
800
|
-
*/
|
|
801
|
-
/**
|
|
802
|
-
* Avatar API实现类
|
|
803
|
-
* @class AvatarService
|
|
804
|
-
* @extends {UnityBaseService<AvatarOperationType>}
|
|
805
|
-
* @implements {IAvatarAPI}
|
|
806
|
-
* @description 数字人控制API的重构实现,继承Unity基础服务,负责与Unity引擎进行通信
|
|
807
|
-
*/
|
|
808
|
-
class AvatarService extends UnityBaseService {
|
|
809
|
-
/** 全局回调函数名称 */
|
|
810
|
-
get callbackFunctionName() {
|
|
811
|
-
return 'uniAvatarCallback';
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* 构造函数
|
|
815
|
-
* @param config - 可选的服务配置,用于覆盖默认配置
|
|
816
|
-
* @description 初始化Avatar API,设置Unity实例和配置
|
|
817
|
-
* @example
|
|
818
|
-
* ```typescript
|
|
819
|
-
* const avatarAPI = new AvatarAPIRefactored(unityInstance, {
|
|
820
|
-
* timeout: 15000,
|
|
821
|
-
* enableDebugLog: true,
|
|
822
|
-
* maxRetries: 2
|
|
823
|
-
* });
|
|
824
|
-
* ```
|
|
825
|
-
*/
|
|
826
|
-
constructor(config) {
|
|
827
|
-
super(config);
|
|
828
|
-
this.logger.info('Avatar API service initialized');
|
|
829
|
-
}
|
|
830
|
-
/**
|
|
831
|
-
* 初始化数字人
|
|
832
|
-
* @param avatarCode - 数字人编码
|
|
833
|
-
* @param cameraType - 摄像机类型,默认为'whole'
|
|
834
|
-
* @returns Promise<IAvatarCallbackResponse> 初始化操作的Promise
|
|
835
|
-
* @description 通过数字人编码加载数字人模型,支持设置摄像机类型
|
|
836
|
-
* @example
|
|
837
|
-
* ```typescript
|
|
838
|
-
* const result = await avatarAPI.initializeAvatar('avatar001', 'whole');
|
|
839
|
-
* if (result.success) {
|
|
840
|
-
* console.log('Avatar initialized successfully');
|
|
841
|
-
* }
|
|
842
|
-
* ```
|
|
843
|
-
*/
|
|
844
|
-
initializeAvatar(avatarCode_1) {
|
|
845
|
-
return __awaiter(this, arguments, void 0, function* (avatarCode, cameraType = AvatarCameraType.WHOLE) {
|
|
846
|
-
this.logger.info(`Initializing avatar: ${avatarCode} with camera type: ${cameraType}`);
|
|
847
|
-
try {
|
|
848
|
-
const result = yield this.sendAsyncMessage('InitializeAvatar', AvatarOperationType.INITIALIZE_AVATAR, {
|
|
849
|
-
avatarCode,
|
|
850
|
-
cameraType
|
|
851
|
-
});
|
|
852
|
-
this.logger.info(`Avatar initialization ${result.success ? 'succeeded' : 'failed'}`);
|
|
853
|
-
return result;
|
|
854
|
-
}
|
|
855
|
-
catch (error) {
|
|
856
|
-
this.logger.error('Failed to initialize avatar', error);
|
|
857
|
-
throw error;
|
|
858
|
-
}
|
|
859
|
-
});
|
|
860
|
-
}
|
|
861
|
-
handleCallback(operation, code, message, data) {
|
|
862
|
-
super.handleCallback(operation, code, message, data);
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* 播放数字人动作
|
|
866
|
-
* @param clipCode - 动作编码
|
|
867
|
-
* @returns Promise<IAvatarCallbackResponse> 播放操作的Promise
|
|
868
|
-
* @description 播放指定的数字人动作
|
|
869
|
-
* @example
|
|
870
|
-
* ```typescript
|
|
871
|
-
* const result = await avatarAPI.playMotion('motion001');
|
|
872
|
-
* if (result.success) {
|
|
873
|
-
* console.log(`Motion started`);
|
|
874
|
-
* }
|
|
875
|
-
* ```
|
|
876
|
-
*/
|
|
877
|
-
playMotion(clipCode) {
|
|
878
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
879
|
-
this.logger.info(`Playing motion: ${clipCode}`);
|
|
880
|
-
try {
|
|
881
|
-
const result = yield this.sendAsyncMessage('PlayMotion', AvatarOperationType.PLAY_MOTION, {
|
|
882
|
-
clipCode
|
|
883
|
-
});
|
|
884
|
-
this.logger.info(`Motion play ${result.success ? 'succeeded' : 'failed'}`);
|
|
885
|
-
return result;
|
|
886
|
-
}
|
|
887
|
-
catch (error) {
|
|
888
|
-
this.logger.error('Failed to play motion', error);
|
|
889
|
-
throw error;
|
|
890
|
-
}
|
|
891
|
-
});
|
|
892
|
-
}
|
|
893
|
-
/**
|
|
894
|
-
* 获取当前播放的动作信息
|
|
895
|
-
* @param getRemainingTime - 是否获取剩余时间
|
|
896
|
-
* @returns Promise<IAvatarCallbackResponse> 获取操作的Promise
|
|
897
|
-
* @description 获取当前数字人播放的动作编码和剩余时间
|
|
898
|
-
* @example
|
|
899
|
-
* ```typescript
|
|
900
|
-
* const result = await avatarAPI.getCurrentMotion(true);
|
|
901
|
-
* if (result.success) {
|
|
902
|
-
* console.log(`Current motion: ${result.motionId}, remaining: ${result.motionRemainingTime}ms`);
|
|
903
|
-
* }
|
|
904
|
-
* ```
|
|
905
|
-
*/
|
|
906
|
-
getCurrentMotion(getRemainingTime) {
|
|
907
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
908
|
-
var _a, _b;
|
|
909
|
-
this.logger.info(`Getting current motion info, includeRemainingTime: ${getRemainingTime}`);
|
|
910
|
-
try {
|
|
911
|
-
const result = yield this.sendAsyncMessage('GetCurrentMotion', AvatarOperationType.GET_CURRENT_MOTION, {
|
|
912
|
-
getRemainingTime
|
|
913
|
-
});
|
|
914
|
-
this.logger.info('Current motion info retrieved', {
|
|
915
|
-
motionId: (_a = result.data) === null || _a === void 0 ? void 0 : _a.motionId,
|
|
916
|
-
remainingTime: (_b = result.data) === null || _b === void 0 ? void 0 : _b.motionRemainingTime
|
|
917
|
-
});
|
|
918
|
-
return result;
|
|
919
|
-
}
|
|
920
|
-
catch (error) {
|
|
921
|
-
this.logger.error('Failed to get current motion info', error);
|
|
922
|
-
throw error;
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
|
-
}
|
|
926
|
-
/**
|
|
927
|
-
* 卸载数字人
|
|
928
|
-
* @returns Promise<IAvatarCallbackResponse> 卸载操作的Promise
|
|
929
|
-
* @description 卸载当前加载的数字人模型,释放相关资源
|
|
930
|
-
* @example
|
|
931
|
-
* ```typescript
|
|
932
|
-
* const result = await avatarAPI.unloadAvatar();
|
|
933
|
-
* if (result.success) {
|
|
934
|
-
* console.log('Avatar unloaded successfully');
|
|
935
|
-
* }
|
|
936
|
-
* ```
|
|
937
|
-
*/
|
|
938
|
-
unloadAvatar() {
|
|
939
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
940
|
-
this.logger.info('Unloading avatar');
|
|
941
|
-
try {
|
|
942
|
-
const result = yield this.sendAsyncMessage('UnloadAvatar', AvatarOperationType.UNLOAD_AVATAR);
|
|
943
|
-
this.logger.info(`Avatar unload ${result.success ? 'succeeded' : 'failed'}`);
|
|
944
|
-
return result;
|
|
945
|
-
}
|
|
946
|
-
catch (error) {
|
|
947
|
-
this.logger.error('Failed to unload avatar', error);
|
|
948
|
-
throw error;
|
|
949
|
-
}
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
/**
|
|
953
|
-
* 设置摄像机类型
|
|
954
|
-
* @param cameraType - 摄像机类型
|
|
955
|
-
* @returns Promise<IAvatarCallbackResponse> 设置操作的Promise
|
|
956
|
-
* @description 设置数字人的摄像机类型和视角
|
|
957
|
-
* @example
|
|
958
|
-
* ```typescript
|
|
959
|
-
* const result = await avatarAPI.setCamera('face');
|
|
960
|
-
* if (result.success) {
|
|
961
|
-
* console.log('Camera type set successfully');
|
|
962
|
-
* }
|
|
963
|
-
* ```
|
|
964
|
-
*/
|
|
965
|
-
setCamera(cameraType) {
|
|
966
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
967
|
-
this.logger.info(`Setting camera type: ${cameraType}`);
|
|
968
|
-
try {
|
|
969
|
-
const result = yield this.sendAsyncMessage('SetCamera', AvatarOperationType.SET_CAMERA, {
|
|
970
|
-
cameraType
|
|
971
|
-
});
|
|
972
|
-
this.logger.info(`Camera type setting ${result.success ? 'succeeded' : 'failed'}`);
|
|
973
|
-
return result;
|
|
974
|
-
}
|
|
975
|
-
catch (error) {
|
|
976
|
-
this.logger.error('Failed to set camera type', error);
|
|
977
|
-
throw error;
|
|
978
|
-
}
|
|
979
|
-
});
|
|
980
|
-
}
|
|
981
|
-
/**
|
|
982
|
-
* 批量执行Avatar操作
|
|
983
|
-
* @param operations - 操作数组
|
|
984
|
-
* @returns Promise<IAvatarCallbackResponse[]> 返回所有操作的结果
|
|
985
|
-
* @description 批量执行多个Avatar操作,支持串行或并行执行
|
|
986
|
-
* @example
|
|
987
|
-
* ```typescript
|
|
988
|
-
* const results = await avatarAPI.batchExecute([
|
|
989
|
-
* { method: 'playMotion', params: ['motion001'] },
|
|
990
|
-
* { method: 'setCamera', params: ['bust'] }
|
|
991
|
-
* ]);
|
|
992
|
-
* ```
|
|
993
|
-
*/
|
|
994
|
-
batchExecute(operations) {
|
|
995
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
996
|
-
this.logger.info(`Executing batch operations: ${operations.length} items`);
|
|
997
|
-
const results = [];
|
|
998
|
-
for (const operation of operations) {
|
|
999
|
-
try {
|
|
1000
|
-
// 使用类型断言来调用方法
|
|
1001
|
-
const method = this[operation.method];
|
|
1002
|
-
const result = yield method.apply(this, operation.params);
|
|
1003
|
-
results.push(result);
|
|
1004
|
-
}
|
|
1005
|
-
catch (error) {
|
|
1006
|
-
this.logger.error(`Batch operation failed: ${operation.method}`, error);
|
|
1007
|
-
// 继续执行其他操作,但记录错误
|
|
1008
|
-
results.push({
|
|
1009
|
-
success: false,
|
|
1010
|
-
message: `Batch operation failed: ${error.message}`,
|
|
1011
|
-
errorCode: 1
|
|
1012
|
-
});
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
this.logger.info(`Batch operations completed: ${results.filter(r => r.success).length}/${results.length} successful`);
|
|
1016
|
-
return results;
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
/**
|
|
1022
|
-
* @fileoverview 环境配置管理模块
|
|
1023
|
-
* @description 提供多环境配置管理和自动环境检测功能
|
|
1024
|
-
*/
|
|
1025
|
-
/**
|
|
1026
|
-
* 环境配置映射表
|
|
1027
|
-
* @constant {Record<Environment, IEnvConfig>}
|
|
1028
|
-
* @description 各环境的配置映射,包含不同环境的API地址
|
|
1029
|
-
*/
|
|
1030
|
-
const ENV_MAP = {
|
|
1031
|
-
/** 开发环境配置 */
|
|
1032
|
-
dev: {
|
|
1033
|
-
apiBaseUrl: 'https://dev.local.zeewain.com'
|
|
1034
|
-
},
|
|
1035
|
-
/** 测试环境配置 */
|
|
1036
|
-
test: {
|
|
1037
|
-
apiBaseUrl: 'https://test.local.zeewain.com'
|
|
1038
|
-
},
|
|
1039
|
-
/** 生产环境配置 */
|
|
1040
|
-
prod: {
|
|
1041
|
-
apiBaseUrl: 'https://ai.zeewain3d.com'
|
|
1042
|
-
}
|
|
1043
|
-
};
|
|
1044
|
-
/**
|
|
1045
|
-
* 获取指定环境的配置
|
|
1046
|
-
* @param env - 环境类型,默认为'dev'
|
|
1047
|
-
* @param withApiModule - 是否包含API模块路径
|
|
1048
|
-
* @returns 返回对应环境的配置对象
|
|
1049
|
-
* @example
|
|
1050
|
-
* ```typescript
|
|
1051
|
-
* const config = getEnvConfig('prod');
|
|
1052
|
-
* console.log(config.apiBaseUrl); // https://ai.zeewain3d.com/api/dh-talker
|
|
1053
|
-
* ```
|
|
1054
|
-
*/
|
|
1055
|
-
function getEnvConfig(env = 'dev', withApiModule = true) {
|
|
1056
|
-
const baseUrl = ENV_MAP[env];
|
|
1057
|
-
if (baseUrl) {
|
|
1058
|
-
return {
|
|
1059
|
-
apiBaseUrl: `${baseUrl.apiBaseUrl}${withApiModule ? '/api' : ''}`
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
return null;
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
/**
|
|
1066
|
-
* @fileoverview SDK配置管理器
|
|
1067
|
-
* @description 提供全局SDK配置管理,避免循环依赖问题
|
|
1068
|
-
*/
|
|
1069
|
-
/**
|
|
1070
|
-
* SDK配置管理器
|
|
1071
|
-
* @class ConfigManager
|
|
1072
|
-
* @description 管理SDK全局配置,提供配置访问接口
|
|
1073
|
-
*/
|
|
1074
|
-
class ConfigManager {
|
|
1075
|
-
/**
|
|
1076
|
-
* 私有构造函数(单例模式)
|
|
1077
|
-
*/
|
|
1078
|
-
constructor() {
|
|
1079
|
-
/** SDK配置对象 */
|
|
1080
|
-
this.config = null;
|
|
1081
|
-
}
|
|
1082
|
-
/**
|
|
1083
|
-
* 获取配置管理器实例
|
|
1084
|
-
* @returns ConfigManager 配置管理器实例
|
|
1085
|
-
* @description 单例模式获取配置管理器
|
|
1086
|
-
*/
|
|
1087
|
-
static getInstance() {
|
|
1088
|
-
if (!ConfigManager.instance) {
|
|
1089
|
-
ConfigManager.instance = new ConfigManager();
|
|
1090
|
-
}
|
|
1091
|
-
return ConfigManager.instance;
|
|
1092
|
-
}
|
|
1093
|
-
/**
|
|
1094
|
-
* 设置SDK配置
|
|
1095
|
-
* @param config - SDK配置对象
|
|
1096
|
-
* @description 设置全局SDK配置
|
|
1097
|
-
*/
|
|
1098
|
-
setConfig(config) {
|
|
1099
|
-
this.config = Object.assign(Object.assign({}, config), { env: config.env || 'prod', containerId: config.containerId || 'unity-container' });
|
|
1100
|
-
}
|
|
1101
|
-
/**
|
|
1102
|
-
* 更新SDK配置
|
|
1103
|
-
* @param config - SDK配置对象
|
|
1104
|
-
* @description 更新全局SDK配置
|
|
1105
|
-
*/
|
|
1106
|
-
updateConfig(config) {
|
|
1107
|
-
if (this.config) {
|
|
1108
|
-
this.config = Object.assign(Object.assign({}, this.config), config);
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
/**
|
|
1112
|
-
* 获取SDK配置
|
|
1113
|
-
* @returns IAvatarSDKConfig | null SDK配置对象或null
|
|
1114
|
-
* @description 获取当前SDK配置
|
|
1115
|
-
*/
|
|
1116
|
-
getConfig() {
|
|
1117
|
-
return this.config;
|
|
1118
|
-
}
|
|
1119
|
-
/**
|
|
1120
|
-
* 获取API基础URL
|
|
1121
|
-
* @param withApiModule 是否包含模块路径
|
|
1122
|
-
* @returns string API基础URL
|
|
1123
|
-
* @description 根据当前环境配置获取API基础URL
|
|
1124
|
-
*/
|
|
1125
|
-
getApiBaseUrl(withApiModule = true) {
|
|
1126
|
-
var _a, _b, _c, _d;
|
|
1127
|
-
if (((_a = this.config) === null || _a === void 0 ? void 0 : _a.env) === 'custom' && ((_b = this.config) === null || _b === void 0 ? void 0 : _b.apiUrl)) {
|
|
1128
|
-
// 如果环境为自定义,则直接返回配置的API,无需添加模块路径
|
|
1129
|
-
return `${this.config.apiUrl}${withApiModule ? '/api' : ''}`;
|
|
1130
|
-
}
|
|
1131
|
-
return ((_d = getEnvConfig(((_c = this.config) === null || _c === void 0 ? void 0 : _c.env) || 'prod', withApiModule)) === null || _d === void 0 ? void 0 : _d.apiBaseUrl) || '';
|
|
1132
|
-
}
|
|
1133
|
-
/**
|
|
1134
|
-
* 重置配置
|
|
1135
|
-
* @description 清空当前配置
|
|
1136
|
-
*/
|
|
1137
|
-
reset() {
|
|
1138
|
-
this.config = null;
|
|
1139
|
-
}
|
|
1140
|
-
}
|
|
1141
|
-
/** 全局配置实例 */
|
|
1142
|
-
ConfigManager.instance = null;
|
|
1143
|
-
|
|
1144
|
-
/**
|
|
1145
|
-
* @fileoverview Unity WebGL加载器模块
|
|
1146
|
-
* @description 提供Unity WebGL应用的加载和初始化功能
|
|
1147
|
-
*/
|
|
1148
|
-
/**
|
|
1149
|
-
* Unity WebGL加载器类
|
|
1150
|
-
* @class UnityLoader
|
|
1151
|
-
* @description 负责Unity WebGL应用的加载、初始化和容器管理
|
|
1152
|
-
*/
|
|
1153
|
-
class UnityLoader {
|
|
1154
|
-
/**
|
|
1155
|
-
* 构造函数
|
|
1156
|
-
* @param config - Unity配置对象
|
|
1157
|
-
* @description 初始化Unity加载器,设置默认容器ID
|
|
1158
|
-
*/
|
|
1159
|
-
constructor() {
|
|
1160
|
-
this.config = ConfigManager.getInstance().getConfig();
|
|
1161
|
-
}
|
|
1162
|
-
/**
|
|
1163
|
-
* 初始化Unity实例
|
|
1164
|
-
* @returns Promise<IUnityInstance> 返回Unity实例的Promise
|
|
1165
|
-
* @description 完整的Unity初始化流程,包括容器创建、脚本加载和实例创建
|
|
1166
|
-
* @throws {Error} 当Unity加载失败时抛出错误
|
|
1167
|
-
*/
|
|
1168
|
-
init() {
|
|
1169
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1170
|
-
// 创建容器
|
|
1171
|
-
const container = this.createContainer();
|
|
1172
|
-
// 加载UnityLoader脚本
|
|
1173
|
-
yield this.loadUnityLoader();
|
|
1174
|
-
// 创建并返回Unity实例
|
|
1175
|
-
return yield this.createUnityInstance(container);
|
|
1176
|
-
});
|
|
1177
|
-
}
|
|
1178
|
-
/**
|
|
1179
|
-
* 创建Unity容器元素
|
|
1180
|
-
* @returns HTMLElement 返回Unity容器元素
|
|
1181
|
-
* @description 创建或获取Unity容器,设置基本样式,清理提示元素
|
|
1182
|
-
* @private
|
|
1183
|
-
*/
|
|
1184
|
-
createContainer() {
|
|
1185
|
-
const container = document.getElementById(this.config.containerId);
|
|
1186
|
-
if (!container) {
|
|
1187
|
-
throw new TypeError(`Avatar container element with ID "${this.config.containerId}" not found`);
|
|
1188
|
-
}
|
|
1189
|
-
else if (!(container instanceof HTMLDivElement)) {
|
|
1190
|
-
throw new TypeError('Avatar container element must be a div element');
|
|
1191
|
-
}
|
|
1192
|
-
container.style.position = 'relative';
|
|
1193
|
-
return container;
|
|
1194
|
-
}
|
|
1195
|
-
/**
|
|
1196
|
-
* 加载Unity WebGL加载器脚本
|
|
1197
|
-
* @returns Promise<void> 加载操作的Promise
|
|
1198
|
-
* @description 动态加载Unity WebGL加载器脚本,如果已存在则跳过
|
|
1199
|
-
* @throws {Error} 当脚本加载失败或函数不存在时抛出错误
|
|
1200
|
-
* @private
|
|
1201
|
-
*/
|
|
1202
|
-
loadUnityLoader() {
|
|
1203
|
-
return new Promise((resolve, reject) => {
|
|
1204
|
-
// 检查是否已经加载了createUnityInstance函数
|
|
1205
|
-
if (window.createUnityInstance) {
|
|
1206
|
-
return resolve();
|
|
1207
|
-
}
|
|
1208
|
-
// 创建脚本元素
|
|
1209
|
-
const script = document.createElement('script');
|
|
1210
|
-
script.src = this.config.loaderUrl;
|
|
1211
|
-
script.async = true;
|
|
1212
|
-
script.crossOrigin = 'anonymous';
|
|
1213
|
-
// 脚本加载成功回调
|
|
1214
|
-
script.onload = () => {
|
|
1215
|
-
if (typeof window.createUnityInstance === 'function') {
|
|
1216
|
-
resolve();
|
|
1217
|
-
}
|
|
1218
|
-
else {
|
|
1219
|
-
reject(new Error('createUnityInstance function not found'));
|
|
1220
|
-
}
|
|
1221
|
-
};
|
|
1222
|
-
// 脚本加载失败回调
|
|
1223
|
-
script.onerror = (error) => {
|
|
1224
|
-
reject(new Error(`Failed to load UnityLoader: ${error}`));
|
|
1225
|
-
};
|
|
1226
|
-
// 将脚本添加到页面头部
|
|
1227
|
-
document.head.appendChild(script);
|
|
1228
|
-
});
|
|
1229
|
-
}
|
|
1230
|
-
/**
|
|
1231
|
-
* 创建Unity实例
|
|
1232
|
-
* @param container - Unity容器元素
|
|
1233
|
-
* @param resolve - 成功回调函数
|
|
1234
|
-
* @param reject - 失败回调函数
|
|
1235
|
-
* @description 创建Canvas元素,配置移动设备适配,并初始化Unity实例
|
|
1236
|
-
* @throws {Error} 当Unity实例创建失败时抛出错误
|
|
1237
|
-
* @private
|
|
1238
|
-
*/
|
|
1239
|
-
createUnityInstance(container) {
|
|
1240
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1241
|
-
let canvas = container.querySelector('#unity-canvas');
|
|
1242
|
-
// 避免重复创建
|
|
1243
|
-
if (!canvas) {
|
|
1244
|
-
canvas = document.createElement('canvas');
|
|
1245
|
-
canvas.id = 'unity-canvas';
|
|
1246
|
-
canvas.style.width = '100%';
|
|
1247
|
-
canvas.style.height = '100%';
|
|
1248
|
-
container.appendChild(canvas);
|
|
1249
|
-
}
|
|
1250
|
-
// 移动设备适配
|
|
1251
|
-
if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) {
|
|
1252
|
-
// 添加移动设备viewport元标签
|
|
1253
|
-
const meta = document.createElement('meta');
|
|
1254
|
-
meta.name = 'viewport';
|
|
1255
|
-
meta.content = 'width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes';
|
|
1256
|
-
document.head.appendChild(meta);
|
|
1257
|
-
// 设置移动设备Canvas样式
|
|
1258
|
-
canvas.style.width = '100%';
|
|
1259
|
-
canvas.style.height = '100%';
|
|
1260
|
-
canvas.style.position = 'fixed';
|
|
1261
|
-
}
|
|
1262
|
-
// 创建Unity实例
|
|
1263
|
-
if (typeof window.createUnityInstance === 'function') {
|
|
1264
|
-
const instance = yield window.createUnityInstance(canvas, {
|
|
1265
|
-
dataUrl: this.config.dataUrl,
|
|
1266
|
-
frameworkUrl: this.config.frameworkUrl,
|
|
1267
|
-
codeUrl: this.config.codeUrl,
|
|
1268
|
-
companyName: '广州紫为云科技有限公司',
|
|
1269
|
-
productName: '数字人SDK',
|
|
1270
|
-
productVersion: '1.0'
|
|
1271
|
-
}, (progress) => {
|
|
1272
|
-
// 调用进度回调
|
|
1273
|
-
if (this.config.onProgress) {
|
|
1274
|
-
this.config.onProgress(progress);
|
|
1275
|
-
}
|
|
1276
|
-
});
|
|
1277
|
-
return instance;
|
|
1278
|
-
}
|
|
1279
|
-
else {
|
|
1280
|
-
throw new TypeError('createUnityInstance is not defined on window.');
|
|
1281
|
-
}
|
|
1282
|
-
});
|
|
1283
|
-
}
|
|
1284
|
-
}
|
|
1285
|
-
|
|
1286
|
-
async function getBytes(stream, onChunk) {
|
|
1287
|
-
const reader = stream.getReader();
|
|
1288
|
-
let result;
|
|
1289
|
-
while (!(result = await reader.read()).done) {
|
|
1290
|
-
onChunk(result.value);
|
|
1291
|
-
}
|
|
1292
|
-
}
|
|
1293
|
-
function getLines(onLine) {
|
|
1294
|
-
let buffer;
|
|
1295
|
-
let position;
|
|
1296
|
-
let fieldLength;
|
|
1297
|
-
let discardTrailingNewline = false;
|
|
1298
|
-
return function onChunk(arr) {
|
|
1299
|
-
if (buffer === undefined) {
|
|
1300
|
-
buffer = arr;
|
|
1301
|
-
position = 0;
|
|
1302
|
-
fieldLength = -1;
|
|
1303
|
-
}
|
|
1304
|
-
else {
|
|
1305
|
-
buffer = concat(buffer, arr);
|
|
1306
|
-
}
|
|
1307
|
-
const bufLength = buffer.length;
|
|
1308
|
-
let lineStart = 0;
|
|
1309
|
-
while (position < bufLength) {
|
|
1310
|
-
if (discardTrailingNewline) {
|
|
1311
|
-
if (buffer[position] === 10) {
|
|
1312
|
-
lineStart = ++position;
|
|
1313
|
-
}
|
|
1314
|
-
discardTrailingNewline = false;
|
|
1315
|
-
}
|
|
1316
|
-
let lineEnd = -1;
|
|
1317
|
-
for (; position < bufLength && lineEnd === -1; ++position) {
|
|
1318
|
-
switch (buffer[position]) {
|
|
1319
|
-
case 58:
|
|
1320
|
-
if (fieldLength === -1) {
|
|
1321
|
-
fieldLength = position - lineStart;
|
|
1322
|
-
}
|
|
1323
|
-
break;
|
|
1324
|
-
case 13:
|
|
1325
|
-
discardTrailingNewline = true;
|
|
1326
|
-
case 10:
|
|
1327
|
-
lineEnd = position;
|
|
1328
|
-
break;
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
if (lineEnd === -1) {
|
|
1332
|
-
break;
|
|
1333
|
-
}
|
|
1334
|
-
onLine(buffer.subarray(lineStart, lineEnd), fieldLength);
|
|
1335
|
-
lineStart = position;
|
|
1336
|
-
fieldLength = -1;
|
|
1337
|
-
}
|
|
1338
|
-
if (lineStart === bufLength) {
|
|
1339
|
-
buffer = undefined;
|
|
1340
|
-
}
|
|
1341
|
-
else if (lineStart !== 0) {
|
|
1342
|
-
buffer = buffer.subarray(lineStart);
|
|
1343
|
-
position -= lineStart;
|
|
1344
|
-
}
|
|
1345
|
-
};
|
|
1346
|
-
}
|
|
1347
|
-
function getMessages(onId, onRetry, onMessage) {
|
|
1348
|
-
let message = newMessage();
|
|
1349
|
-
const decoder = new TextDecoder();
|
|
1350
|
-
return function onLine(line, fieldLength) {
|
|
1351
|
-
if (line.length === 0) {
|
|
1352
|
-
onMessage === null || onMessage === void 0 ? void 0 : onMessage(message);
|
|
1353
|
-
message = newMessage();
|
|
1354
|
-
}
|
|
1355
|
-
else if (fieldLength > 0) {
|
|
1356
|
-
const field = decoder.decode(line.subarray(0, fieldLength));
|
|
1357
|
-
const valueOffset = fieldLength + (line[fieldLength + 1] === 32 ? 2 : 1);
|
|
1358
|
-
const value = decoder.decode(line.subarray(valueOffset));
|
|
1359
|
-
switch (field) {
|
|
1360
|
-
case 'data':
|
|
1361
|
-
message.data = message.data
|
|
1362
|
-
? message.data + '\n' + value
|
|
1363
|
-
: value;
|
|
1364
|
-
break;
|
|
1365
|
-
case 'event':
|
|
1366
|
-
message.event = value;
|
|
1367
|
-
break;
|
|
1368
|
-
case 'id':
|
|
1369
|
-
onId(message.id = value);
|
|
1370
|
-
break;
|
|
1371
|
-
case 'retry':
|
|
1372
|
-
const retry = parseInt(value, 10);
|
|
1373
|
-
if (!isNaN(retry)) {
|
|
1374
|
-
onRetry(message.retry = retry);
|
|
1375
|
-
}
|
|
1376
|
-
break;
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1381
|
-
function concat(a, b) {
|
|
1382
|
-
const res = new Uint8Array(a.length + b.length);
|
|
1383
|
-
res.set(a);
|
|
1384
|
-
res.set(b, a.length);
|
|
1385
|
-
return res;
|
|
1386
|
-
}
|
|
1387
|
-
function newMessage() {
|
|
1388
|
-
return {
|
|
1389
|
-
data: '',
|
|
1390
|
-
event: '',
|
|
1391
|
-
id: '',
|
|
1392
|
-
retry: undefined,
|
|
1393
|
-
};
|
|
1394
|
-
}
|
|
1395
|
-
|
|
1396
|
-
var __rest = (undefined && undefined.__rest) || function (s, e) {
|
|
1397
|
-
var t = {};
|
|
1398
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
1399
|
-
t[p] = s[p];
|
|
1400
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
1401
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
1402
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
1403
|
-
t[p[i]] = s[p[i]];
|
|
1404
|
-
}
|
|
1405
|
-
return t;
|
|
1406
|
-
};
|
|
1407
|
-
const EventStreamContentType = 'text/event-stream';
|
|
1408
|
-
const DefaultRetryInterval = 1000;
|
|
1409
|
-
const LastEventId = 'last-event-id';
|
|
1410
|
-
function fetchEventSource(input, _a) {
|
|
1411
|
-
var { signal: inputSignal, headers: inputHeaders, onopen: inputOnOpen, onmessage, onclose, onerror, openWhenHidden, fetch: inputFetch } = _a, rest = __rest(_a, ["signal", "headers", "onopen", "onmessage", "onclose", "onerror", "openWhenHidden", "fetch"]);
|
|
1412
|
-
return new Promise((resolve, reject) => {
|
|
1413
|
-
const headers = Object.assign({}, inputHeaders);
|
|
1414
|
-
if (!headers.accept) {
|
|
1415
|
-
headers.accept = EventStreamContentType;
|
|
1416
|
-
}
|
|
1417
|
-
let curRequestController;
|
|
1418
|
-
function onVisibilityChange() {
|
|
1419
|
-
curRequestController.abort();
|
|
1420
|
-
if (!document.hidden) {
|
|
1421
|
-
create();
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
if (!openWhenHidden) {
|
|
1425
|
-
document.addEventListener('visibilitychange', onVisibilityChange);
|
|
1426
|
-
}
|
|
1427
|
-
let retryInterval = DefaultRetryInterval;
|
|
1428
|
-
let retryTimer = 0;
|
|
1429
|
-
function dispose() {
|
|
1430
|
-
document.removeEventListener('visibilitychange', onVisibilityChange);
|
|
1431
|
-
window.clearTimeout(retryTimer);
|
|
1432
|
-
curRequestController.abort();
|
|
1433
|
-
}
|
|
1434
|
-
inputSignal === null || inputSignal === void 0 ? void 0 : inputSignal.addEventListener('abort', () => {
|
|
1435
|
-
dispose();
|
|
1436
|
-
resolve();
|
|
1437
|
-
});
|
|
1438
|
-
const fetch = inputFetch !== null && inputFetch !== void 0 ? inputFetch : window.fetch;
|
|
1439
|
-
const onopen = inputOnOpen !== null && inputOnOpen !== void 0 ? inputOnOpen : defaultOnOpen;
|
|
1440
|
-
async function create() {
|
|
1441
|
-
var _a;
|
|
1442
|
-
curRequestController = new AbortController();
|
|
1443
|
-
try {
|
|
1444
|
-
const response = await fetch(input, Object.assign(Object.assign({}, rest), { headers, signal: curRequestController.signal }));
|
|
1445
|
-
await onopen(response);
|
|
1446
|
-
await getBytes(response.body, getLines(getMessages(id => {
|
|
1447
|
-
if (id) {
|
|
1448
|
-
headers[LastEventId] = id;
|
|
1449
|
-
}
|
|
1450
|
-
else {
|
|
1451
|
-
delete headers[LastEventId];
|
|
1452
|
-
}
|
|
1453
|
-
}, retry => {
|
|
1454
|
-
retryInterval = retry;
|
|
1455
|
-
}, onmessage)));
|
|
1456
|
-
onclose === null || onclose === void 0 ? void 0 : onclose();
|
|
1457
|
-
dispose();
|
|
1458
|
-
resolve();
|
|
1459
|
-
}
|
|
1460
|
-
catch (err) {
|
|
1461
|
-
if (!curRequestController.signal.aborted) {
|
|
1462
|
-
try {
|
|
1463
|
-
const interval = (_a = onerror === null || onerror === void 0 ? void 0 : onerror(err)) !== null && _a !== void 0 ? _a : retryInterval;
|
|
1464
|
-
window.clearTimeout(retryTimer);
|
|
1465
|
-
retryTimer = window.setTimeout(create, interval);
|
|
1466
|
-
}
|
|
1467
|
-
catch (innerErr) {
|
|
1468
|
-
dispose();
|
|
1469
|
-
reject(innerErr);
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
create();
|
|
1475
|
-
});
|
|
1476
|
-
}
|
|
1477
|
-
function defaultOnOpen(response) {
|
|
1478
|
-
const contentType = response.headers.get('content-type');
|
|
1479
|
-
if (!(contentType === null || contentType === void 0 ? void 0 : contentType.startsWith(EventStreamContentType))) {
|
|
1480
|
-
throw new Error(`Expected content-type to be ${EventStreamContentType}, Actual: ${contentType}`);
|
|
1481
|
-
}
|
|
1482
|
-
}
|
|
1483
|
-
|
|
1484
|
-
/**
|
|
1485
|
-
* @fileoverview 流式播报服务接口定义
|
|
1486
|
-
* @description 定义流式播报服务相关的类型、接口和枚举
|
|
1487
|
-
*/
|
|
1488
|
-
/**
|
|
1489
|
-
* 播报操作类型枚举
|
|
1490
|
-
* @enum {string}
|
|
1491
|
-
* @description 定义播报服务支持的操作类型
|
|
1492
|
-
*/
|
|
1493
|
-
var BroadcastOperationType;
|
|
1494
|
-
(function (BroadcastOperationType) {
|
|
1495
|
-
BroadcastOperationType["START_BROADCAST"] = "startBroadcast";
|
|
1496
|
-
BroadcastOperationType["PAUSE_BROADCAST"] = "pauseBroadcast";
|
|
1497
|
-
BroadcastOperationType["RESUME_BROADCAST"] = "resumeBroadcast";
|
|
1498
|
-
BroadcastOperationType["STOP_BROADCAST"] = "stopBroadcast";
|
|
1499
|
-
BroadcastOperationType["APPEND_BROADCAST"] = "appendBroadcast";
|
|
1500
|
-
})(BroadcastOperationType || (BroadcastOperationType = {}));
|
|
1501
|
-
/**
|
|
1502
|
-
* 播报类型枚举
|
|
1503
|
-
* @enum {string}
|
|
1504
|
-
* @description 定义播报的类型,支持文本转语音和自定义音频播报
|
|
1505
|
-
*/
|
|
1506
|
-
var BroadcastType;
|
|
1507
|
-
(function (BroadcastType) {
|
|
1508
|
-
/** 文本智能体播报 */
|
|
1509
|
-
BroadcastType["TEXT"] = "text";
|
|
1510
|
-
/** 自定义音频智能体播报 */
|
|
1511
|
-
BroadcastType["AUDIO"] = "audio";
|
|
1512
|
-
})(BroadcastType || (BroadcastType = {}));
|
|
1513
|
-
|
|
1514
|
-
/**
|
|
1515
|
-
* @fileoverview 流式播报服务重构实现模块
|
|
1516
|
-
* @description 提供流式播报服务的重构实现,继承Unity基础服务,支持文本转语音和自定义音频播报
|
|
1517
|
-
*/
|
|
1518
|
-
/**
|
|
1519
|
-
* 播报任务状态枚举
|
|
1520
|
-
*/
|
|
1521
|
-
var BroadcastTaskStatus;
|
|
1522
|
-
(function (BroadcastTaskStatus) {
|
|
1523
|
-
/** 等待中(尚未开始请求) */
|
|
1524
|
-
BroadcastTaskStatus["PENDING"] = "pending";
|
|
1525
|
-
/** 请求中 */
|
|
1526
|
-
BroadcastTaskStatus["REQUESTING"] = "requesting";
|
|
1527
|
-
/** 已完成 */
|
|
1528
|
-
BroadcastTaskStatus["COMPLETED"] = "completed";
|
|
1529
|
-
/** 失败 */
|
|
1530
|
-
BroadcastTaskStatus["FAILED"] = "failed";
|
|
1531
|
-
/** 已取消 */
|
|
1532
|
-
BroadcastTaskStatus["CANCELLED"] = "cancelled";
|
|
1533
|
-
})(BroadcastTaskStatus || (BroadcastTaskStatus = {}));
|
|
1534
|
-
/**
|
|
1535
|
-
* 流式播报服务重构类
|
|
1536
|
-
* @class BroadcastServiceRefactored
|
|
1537
|
-
* @extends {UnityBaseService<BroadcastOperationType>}
|
|
1538
|
-
* @description 提供流式播报服务的重构实现,继承Unity基础服务,支持文本转语音和自定义音频播报
|
|
1539
|
-
*/
|
|
1540
|
-
class BroadcastService extends UnityBaseService {
|
|
1541
|
-
/**
|
|
1542
|
-
* 构造函数
|
|
1543
|
-
* @param config - 播报服务配置
|
|
1544
|
-
* @description 初始化播报服务,设置环境配置和回调函数
|
|
1545
|
-
* @example
|
|
1546
|
-
* ```typescript
|
|
1547
|
-
* const broadcastService = new BroadcastServiceRefactored({
|
|
1548
|
-
* unityInstance,
|
|
1549
|
-
* callbacks: {
|
|
1550
|
-
* onStart: () => console.log('Broadcast started'),
|
|
1551
|
-
* onFinish: () => console.log('Broadcast finished'),
|
|
1552
|
-
* onError: (error) => console.error('Broadcast error:', error)
|
|
1553
|
-
* },
|
|
1554
|
-
* timeout: 15000,
|
|
1555
|
-
* enableDebugLog: true
|
|
1556
|
-
* });
|
|
1557
|
-
* ```
|
|
1558
|
-
*/
|
|
1559
|
-
constructor(config) {
|
|
1560
|
-
super(config);
|
|
1561
|
-
/** 事件回调函数集合 */
|
|
1562
|
-
this.callbacks = {};
|
|
1563
|
-
/** 播报任务队列 */
|
|
1564
|
-
this.taskQueue = [];
|
|
1565
|
-
/** 任务序号计数器 */
|
|
1566
|
-
this.taskSequence = 0;
|
|
1567
|
-
/** 当前发送任务的序号 */
|
|
1568
|
-
this.currentSendingSequence = 0;
|
|
1569
|
-
/** 是否正在播报音频 */
|
|
1570
|
-
this.isBroadcastingAudio = false;
|
|
1571
|
-
/** 是否已经收到音频 */
|
|
1572
|
-
this.hasReceivedAudio = false;
|
|
1573
|
-
/** 队列处理定时器 */
|
|
1574
|
-
this.queueProcessTimer = null;
|
|
1575
|
-
/** 播报完成次数 */
|
|
1576
|
-
this.broadcastCompletedCount = 0;
|
|
1577
|
-
this.callbacks = config.callbacks || {};
|
|
1578
|
-
this.logger.info('Broadcast service initialized', { config });
|
|
1579
|
-
}
|
|
1580
|
-
/**
|
|
1581
|
-
* 处理Unity回调
|
|
1582
|
-
* @param operation - 操作类型
|
|
1583
|
-
* @param code - 响应代码
|
|
1584
|
-
* @param message - 响应消息
|
|
1585
|
-
* @param data - 额外数据,支持可变数量的参数
|
|
1586
|
-
* @description 处理Unity的播报回调,并触发相应的事件回调
|
|
1587
|
-
* @override
|
|
1588
|
-
*/
|
|
1589
|
-
handleCallback(operation, code, message, data) {
|
|
1590
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
1591
|
-
// 提取 isBroadcastCompleted 参数
|
|
1592
|
-
const { isBroadcastCompleted } = JSON.parse(data || '{}');
|
|
1593
|
-
// 先调用基类处理逻辑
|
|
1594
|
-
super.handleCallback(operation, code, message, data);
|
|
1595
|
-
// 触发对应的事件回调(仅在成功时)
|
|
1596
|
-
if (code === 0) {
|
|
1597
|
-
switch (operation) {
|
|
1598
|
-
case BroadcastOperationType.START_BROADCAST:
|
|
1599
|
-
if (isBroadcastCompleted) {
|
|
1600
|
-
this.broadcastCompletedCount++;
|
|
1601
|
-
const status = this.getStatus();
|
|
1602
|
-
// 判断是否所有任务都已完成:
|
|
1603
|
-
// 1. 没有待请求的任务 (PENDING)
|
|
1604
|
-
// 2. 没有正在请求的任务 (REQUESTING)
|
|
1605
|
-
// 3. Unity播放完成次数等于已完成任务数
|
|
1606
|
-
const isAllTasksCompleted = ((_a = status.queueInfo) === null || _a === void 0 ? void 0 : _a.pendingTasks) === 0
|
|
1607
|
-
&& ((_b = status.queueInfo) === null || _b === void 0 ? void 0 : _b.requestingTasks) === 0
|
|
1608
|
-
&& ((_c = status.queueInfo) === null || _c === void 0 ? void 0 : _c.completedTasks) === this.broadcastCompletedCount;
|
|
1609
|
-
if (isAllTasksCompleted) {
|
|
1610
|
-
// 重置状态、计数
|
|
1611
|
-
this.isBroadcastingAudio = false;
|
|
1612
|
-
this.hasReceivedAudio = false;
|
|
1613
|
-
this.currentSendingSequence = 0;
|
|
1614
|
-
// 清理已完成的任务
|
|
1615
|
-
this.cleanupCompletedTasks();
|
|
1616
|
-
this.logger.warn('Broadcast all completed');
|
|
1617
|
-
}
|
|
1618
|
-
// this.logger.warn('AAAAAA', { status: this.getStatus(), broadcastCompletedCount: this.broadcastCompletedCount });
|
|
1619
|
-
(_e = (_d = this.callbacks).onFinish) === null || _e === void 0 ? void 0 : _e.call(_d);
|
|
1620
|
-
}
|
|
1621
|
-
break;
|
|
1622
|
-
case BroadcastOperationType.PAUSE_BROADCAST:
|
|
1623
|
-
(_g = (_f = this.callbacks).onPause) === null || _g === void 0 ? void 0 : _g.call(_f);
|
|
1624
|
-
this.logger.debug('Broadcast paused callback triggered');
|
|
1625
|
-
break;
|
|
1626
|
-
case BroadcastOperationType.RESUME_BROADCAST:
|
|
1627
|
-
(_j = (_h = this.callbacks).onResume) === null || _j === void 0 ? void 0 : _j.call(_h);
|
|
1628
|
-
this.logger.debug('Broadcast resumed callback triggered');
|
|
1629
|
-
break;
|
|
1630
|
-
case BroadcastOperationType.STOP_BROADCAST:
|
|
1631
|
-
(_l = (_k = this.callbacks).onStop) === null || _l === void 0 ? void 0 : _l.call(_k);
|
|
1632
|
-
this.logger.debug('Broadcast stopped callback triggered');
|
|
1633
|
-
break;
|
|
1634
|
-
}
|
|
1635
|
-
}
|
|
1636
|
-
else {
|
|
1637
|
-
const error = SDKError.createFromUnityError(code, `Unity operation '${operation}' failed: ${message}`);
|
|
1638
|
-
this.handleError(error);
|
|
1639
|
-
}
|
|
1640
|
-
}
|
|
1641
|
-
/**
|
|
1642
|
-
* 开始播报
|
|
1643
|
-
* @param params - 播报参数
|
|
1644
|
-
* @param isAppend - 是否追加播报
|
|
1645
|
-
* @returns Promise<void> 播报操作的Promise
|
|
1646
|
-
* @description 开始流式播报,支持文本转语音和自定义音频播报。使用队列机制确保音频按序播报
|
|
1647
|
-
* @throws {SDKError} 当参数验证失败或播报失败时抛出错误
|
|
1648
|
-
* @example
|
|
1649
|
-
* ```typescript
|
|
1650
|
-
* // 文本播报
|
|
1651
|
-
* await broadcastService.startBroadcast({
|
|
1652
|
-
* type: BroadcastType.TEXT,
|
|
1653
|
-
* humanCode: 'human001',
|
|
1654
|
-
* text: '你好,世界!',
|
|
1655
|
-
* voiceCode: 'voice001',
|
|
1656
|
-
* volume: 0.8,
|
|
1657
|
-
* isSubtitle: true
|
|
1658
|
-
* });
|
|
1659
|
-
*
|
|
1660
|
-
* // 追加播报(会进入队列按序播报)
|
|
1661
|
-
* await broadcastService.startBroadcast({
|
|
1662
|
-
* type: BroadcastType.TEXT,
|
|
1663
|
-
* humanCode: 'human001',
|
|
1664
|
-
* text: '这是第二段内容',
|
|
1665
|
-
* voiceCode: 'voice001',
|
|
1666
|
-
* volume: 0.8,
|
|
1667
|
-
* isSubtitle: true
|
|
1668
|
-
* }, true);
|
|
1669
|
-
* ```
|
|
1670
|
-
*/
|
|
1671
|
-
startBroadcast(params, isAppend) {
|
|
1672
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1673
|
-
var _a, _b;
|
|
1674
|
-
this.logger.info(`Starting broadcast: ${params.type}`, {
|
|
1675
|
-
humanCode: params.humanCode,
|
|
1676
|
-
text: params.text,
|
|
1677
|
-
audioUrl: params.audioUrl,
|
|
1678
|
-
isAppend,
|
|
1679
|
-
queueLength: this.taskQueue.length
|
|
1680
|
-
});
|
|
1681
|
-
// 验证参数
|
|
1682
|
-
this.validateBroadcastParams(params);
|
|
1683
|
-
// 非追加模式下需要初始化播报
|
|
1684
|
-
if (!isAppend) {
|
|
1685
|
-
// 先停止当前播报并清空队列
|
|
1686
|
-
yield this.stopBroadcast();
|
|
1687
|
-
// 重置序号计数器
|
|
1688
|
-
this.taskSequence = 0;
|
|
1689
|
-
this.currentSendingSequence = 0;
|
|
1690
|
-
// 通知Unity开始新任务(清空队列),这里不使用sendAsyncMessage,因为startBroadcast不需要等待完成,而是在任务播报完成后通过回调通知
|
|
1691
|
-
this.sendMessage('StartBroadcast', {
|
|
1692
|
-
callbackFun: this.uniqueCallbackName,
|
|
1693
|
-
operationType: BroadcastOperationType.START_BROADCAST,
|
|
1694
|
-
motionList: params.motionList,
|
|
1695
|
-
motionPlayMode: params.motionPlayMode
|
|
1696
|
-
});
|
|
1697
|
-
// 触发开始回调
|
|
1698
|
-
(_b = (_a = this.callbacks).onStart) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1699
|
-
this.isBroadcastingAudio = true;
|
|
1700
|
-
}
|
|
1701
|
-
// 创建新的播报任务
|
|
1702
|
-
const task = this.createBroadcastTask(params);
|
|
1703
|
-
// 添加任务到队列
|
|
1704
|
-
this.addTaskToQueue(task);
|
|
1705
|
-
this.logger.debug('Broadcast task created and queued', {
|
|
1706
|
-
taskId: task.id,
|
|
1707
|
-
sequence: task.sequence,
|
|
1708
|
-
isAppend
|
|
1709
|
-
});
|
|
1710
|
-
});
|
|
1711
|
-
}
|
|
1712
|
-
/**
|
|
1713
|
-
* 暂停播报
|
|
1714
|
-
* @param resetIdle - 是否重置空闲状态
|
|
1715
|
-
* @returns Promise<void> 暂停操作的Promise
|
|
1716
|
-
* @description 暂停当前正在播报的内容
|
|
1717
|
-
* @example
|
|
1718
|
-
* ```typescript
|
|
1719
|
-
* await broadcastService.pauseBroadcast();
|
|
1720
|
-
* ```
|
|
1721
|
-
*/
|
|
1722
|
-
pauseBroadcast(resetIdle) {
|
|
1723
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1724
|
-
this.logger.info('Pausing broadcast');
|
|
1725
|
-
try {
|
|
1726
|
-
yield this.sendAsyncMessage('PauseBroadcast', BroadcastOperationType.PAUSE_BROADCAST, {
|
|
1727
|
-
resetIdle
|
|
1728
|
-
});
|
|
1729
|
-
this.logger.info('Broadcast paused successfully');
|
|
1730
|
-
}
|
|
1731
|
-
catch (error) {
|
|
1732
|
-
this.logger.error('Failed to pause broadcast', error);
|
|
1733
|
-
throw error;
|
|
1734
|
-
}
|
|
1735
|
-
});
|
|
1736
|
-
}
|
|
1737
|
-
/**
|
|
1738
|
-
* 继续播报
|
|
1739
|
-
* @returns Promise<void> 继续操作的Promise
|
|
1740
|
-
* @description 继续播报之前暂停的内容
|
|
1741
|
-
* @example
|
|
1742
|
-
* ```typescript
|
|
1743
|
-
* await broadcastService.resumeBroadcast();
|
|
1744
|
-
* ```
|
|
1745
|
-
*/
|
|
1746
|
-
resumeBroadcast() {
|
|
1747
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1748
|
-
this.logger.info('Resuming broadcast');
|
|
1749
|
-
try {
|
|
1750
|
-
yield this.sendAsyncMessage('ResumeBroadcast', BroadcastOperationType.RESUME_BROADCAST, {});
|
|
1751
|
-
this.logger.info('Broadcast resumed successfully');
|
|
1752
|
-
}
|
|
1753
|
-
catch (error) {
|
|
1754
|
-
this.logger.error('Failed to resume broadcast', error);
|
|
1755
|
-
throw error;
|
|
1756
|
-
}
|
|
1757
|
-
});
|
|
1758
|
-
}
|
|
1759
|
-
/**
|
|
1760
|
-
* 停止播报
|
|
1761
|
-
* @returns Promise<void> 停止操作的Promise
|
|
1762
|
-
* @description 停止当前播报并清空播报队列
|
|
1763
|
-
* @example
|
|
1764
|
-
* ```typescript
|
|
1765
|
-
* await broadcastService.stopBroadcast();
|
|
1766
|
-
* ```
|
|
1767
|
-
*/
|
|
1768
|
-
stopBroadcast() {
|
|
1769
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1770
|
-
this.logger.info('Stopping broadcast and clearing queue', { queueLength: this.taskQueue.length });
|
|
1771
|
-
// 取消所有队列中的任务
|
|
1772
|
-
this.cancelAllTasks();
|
|
1773
|
-
// 重置状态
|
|
1774
|
-
this.isBroadcastingAudio = false;
|
|
1775
|
-
this.hasReceivedAudio = false;
|
|
1776
|
-
this.broadcastCompletedCount = 0;
|
|
1777
|
-
try {
|
|
1778
|
-
yield this.sendAsyncMessage('StopBroadcast', BroadcastOperationType.STOP_BROADCAST, {});
|
|
1779
|
-
this.logger.info('Broadcast stopped successfully');
|
|
1780
|
-
}
|
|
1781
|
-
catch (error) {
|
|
1782
|
-
this.logger.error('Failed to stop broadcast', error);
|
|
1783
|
-
throw error;
|
|
1784
|
-
}
|
|
1785
|
-
});
|
|
1786
|
-
}
|
|
1787
|
-
/**
|
|
1788
|
-
* 更新回调函数
|
|
1789
|
-
* @param callbacks - 新的回调函数配置
|
|
1790
|
-
* @description 更新播报事件的回调函数
|
|
1791
|
-
*/
|
|
1792
|
-
updateCallbacks(callbacks) {
|
|
1793
|
-
this.callbacks = Object.assign(Object.assign({}, this.callbacks), callbacks);
|
|
1794
|
-
this.logger.debug('Broadcast callbacks updated');
|
|
1795
|
-
}
|
|
1796
|
-
/**
|
|
1797
|
-
* 获取播报状态
|
|
1798
|
-
* @returns 播报状态信息
|
|
1799
|
-
* @description 获取当前播报服务的状态信息,包括队列状态
|
|
1800
|
-
*/
|
|
1801
|
-
getStatus() {
|
|
1802
|
-
const pendingTasks = this.taskQueue.filter((t) => t.status === BroadcastTaskStatus.PENDING).length;
|
|
1803
|
-
const completedTasks = this.taskQueue.filter((t) => t.status === BroadcastTaskStatus.COMPLETED).length;
|
|
1804
|
-
const requestingTasks = this.taskQueue.filter((t) => t.status === BroadcastTaskStatus.REQUESTING).length;
|
|
1805
|
-
const failedTasks = this.taskQueue.filter((t) => t.status === BroadcastTaskStatus.FAILED).length;
|
|
1806
|
-
const totalPendingResponses = this.taskQueue.reduce((sum, t) => sum + t.pendingResponses.length, 0);
|
|
1807
|
-
const currentSendingSequence = this.currentSendingSequence;
|
|
1808
|
-
const isGeneratingAudio = pendingTasks + requestingTasks > 0;
|
|
1809
|
-
return {
|
|
1810
|
-
isActive: this.isBroadcastingAudio || isGeneratingAudio, // 是否正在播报音频或正在生成音频
|
|
1811
|
-
isGeneratingAudio,
|
|
1812
|
-
hasReceivedAudio: this.hasReceivedAudio,
|
|
1813
|
-
queueInfo: {
|
|
1814
|
-
totalTasks: this.taskQueue.length,
|
|
1815
|
-
pendingTasks,
|
|
1816
|
-
requestingTasks,
|
|
1817
|
-
completedTasks,
|
|
1818
|
-
failedTasks,
|
|
1819
|
-
totalPendingResponses,
|
|
1820
|
-
currentSendingSequence
|
|
1821
|
-
}
|
|
1822
|
-
};
|
|
1823
|
-
}
|
|
1824
|
-
/**
|
|
1825
|
-
* 销毁播报服务
|
|
1826
|
-
* @description 清理所有资源和回调
|
|
1827
|
-
*/
|
|
1828
|
-
destroy() {
|
|
1829
|
-
// 清理队列处理定时器
|
|
1830
|
-
this.clearQueueProcessTimer();
|
|
1831
|
-
// 取消所有任务
|
|
1832
|
-
this.cancelAllTasks();
|
|
1833
|
-
// 调用基类销毁方法
|
|
1834
|
-
super.destroy();
|
|
1835
|
-
this.logger.info('Broadcast service destroyed');
|
|
1836
|
-
}
|
|
1837
|
-
/**
|
|
1838
|
-
* 创建播报任务
|
|
1839
|
-
* @param params - 播报参数
|
|
1840
|
-
* @returns IBroadcastTask 播报任务对象
|
|
1841
|
-
* @description 创建新的播报任务并分配唯一ID和序号
|
|
1842
|
-
* @private
|
|
1843
|
-
*/
|
|
1844
|
-
createBroadcastTask(params) {
|
|
1845
|
-
const typeText = params.type === BroadcastType.TEXT ? 'TEXT' : 'AUDIO';
|
|
1846
|
-
const typeInfo = params.type === BroadcastType.TEXT ? params.text : params.audioUrl;
|
|
1847
|
-
const task = {
|
|
1848
|
-
id: `[${typeText}]-(${typeInfo})_${Math.random().toString(36).substring(2, 9)}`,
|
|
1849
|
-
sequence: ++this.taskSequence,
|
|
1850
|
-
params,
|
|
1851
|
-
status: BroadcastTaskStatus.PENDING,
|
|
1852
|
-
controller: new AbortController(),
|
|
1853
|
-
pendingResponses: [],
|
|
1854
|
-
isGenerationComplete: false,
|
|
1855
|
-
createdAt: new Date()
|
|
1856
|
-
};
|
|
1857
|
-
this.logger.debug('Created broadcast task', { taskId: task.id, sequence: task.sequence });
|
|
1858
|
-
return task;
|
|
1859
|
-
}
|
|
1860
|
-
/**
|
|
1861
|
-
* 添加任务到队列
|
|
1862
|
-
* @param task - 播报任务
|
|
1863
|
-
* @description 将任务添加到队列,只有当没有正在请求的任务时才开始请求(串行请求模式)
|
|
1864
|
-
* @private
|
|
1865
|
-
*/
|
|
1866
|
-
addTaskToQueue(task) {
|
|
1867
|
-
this.taskQueue.push(task);
|
|
1868
|
-
this.logger.debug('Task added to queue', { taskId: task.id, params: task.params, queueLength: this.taskQueue.length });
|
|
1869
|
-
// 检查是否有正在请求的任务
|
|
1870
|
-
const hasRequestingTask = this.taskQueue.some((t) => t.status === BroadcastTaskStatus.REQUESTING);
|
|
1871
|
-
// 只有当没有正在请求的任务时,才开始当前任务的请求
|
|
1872
|
-
if (!hasRequestingTask) {
|
|
1873
|
-
this.startTaskRequest(task);
|
|
1874
|
-
}
|
|
1875
|
-
else {
|
|
1876
|
-
this.logger.debug('Task queued, waiting for previous task to complete', {
|
|
1877
|
-
taskId: task.id,
|
|
1878
|
-
pendingTasks: this.taskQueue.filter((t) => t.status === BroadcastTaskStatus.PENDING).length
|
|
1879
|
-
});
|
|
1880
|
-
}
|
|
1881
|
-
// 开始处理队列
|
|
1882
|
-
this.processTaskQueue();
|
|
1883
|
-
}
|
|
1884
|
-
/**
|
|
1885
|
-
* 处理队列
|
|
1886
|
-
* @description 处理队列中的任务响应,按序号发送给unity播放
|
|
1887
|
-
* @private
|
|
1888
|
-
*/
|
|
1889
|
-
processTaskQueue() {
|
|
1890
|
-
// 启动队列处理定时器(如果尚未启动)
|
|
1891
|
-
if (!this.queueProcessTimer) {
|
|
1892
|
-
this.queueProcessTimer = setInterval(() => {
|
|
1893
|
-
this.processTaskQueueStep();
|
|
1894
|
-
}, 100); // 每100ms检查一次队列状态
|
|
1895
|
-
}
|
|
1896
|
-
// 立即处理一次
|
|
1897
|
-
this.processTaskQueueStep();
|
|
1898
|
-
}
|
|
1899
|
-
/**
|
|
1900
|
-
* 队列处理步骤
|
|
1901
|
-
* @description 处理队列中的单个步骤,按序号发送给unity播放
|
|
1902
|
-
* @private
|
|
1903
|
-
*/
|
|
1904
|
-
processTaskQueueStep() {
|
|
1905
|
-
// 按序号找到下一个要处理的任务(只处理已开始请求的任务,排除 PENDING 状态)
|
|
1906
|
-
const nextTask = this.taskQueue.find((task) => task.sequence === this.currentSendingSequence + 1
|
|
1907
|
-
&& task.status !== BroadcastTaskStatus.PENDING
|
|
1908
|
-
&& task.pendingResponses.length > 0);
|
|
1909
|
-
if (nextTask) {
|
|
1910
|
-
this.sendNextResponse(nextTask);
|
|
1911
|
-
}
|
|
1912
|
-
else {
|
|
1913
|
-
this.logger.debug('No next task to process', { currentSendingSequence: this.currentSendingSequence, taskQueue: this.taskQueue });
|
|
1914
|
-
}
|
|
1915
|
-
// 如果队列中没有需要处理的任务(PENDING 或 REQUESTING 状态),则停止定时器
|
|
1916
|
-
const activeTasks = this.taskQueue.filter(task => task.status === BroadcastTaskStatus.PENDING
|
|
1917
|
-
|| task.status === BroadcastTaskStatus.REQUESTING);
|
|
1918
|
-
if (activeTasks.length === 0) {
|
|
1919
|
-
this.clearQueueProcessTimer();
|
|
1920
|
-
}
|
|
1921
|
-
}
|
|
1922
|
-
/**
|
|
1923
|
-
* 开始任务请求
|
|
1924
|
-
* @param task - 播报任务
|
|
1925
|
-
* @description 为任务发起流式请求生成音频
|
|
1926
|
-
* @private
|
|
1927
|
-
*/
|
|
1928
|
-
startTaskRequest(task) {
|
|
1929
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1930
|
-
var _a;
|
|
1931
|
-
task.status = BroadcastTaskStatus.REQUESTING;
|
|
1932
|
-
this.logger.debug('Starting task request', { taskId: task.id });
|
|
1933
|
-
try {
|
|
1934
|
-
const apiUrl = `${ConfigManager.getInstance().getApiBaseUrl(true)}${this.getBroadcastApiPath(task.params.type)}`;
|
|
1935
|
-
const requestBody = {
|
|
1936
|
-
humanCode: task.params.humanCode,
|
|
1937
|
-
speed: task.params.speed,
|
|
1938
|
-
volume: task.params.volume >= 0 ? task.params.volume * 100 : undefined,
|
|
1939
|
-
isSubtitle: task.params.isSubtitle,
|
|
1940
|
-
audioDrivenVersion: ConfigManager.getInstance().getConfig().audioDrivenVersion
|
|
1941
|
-
};
|
|
1942
|
-
// 根据播报类型设置特定参数
|
|
1943
|
-
if (task.params.type === BroadcastType.TEXT) {
|
|
1944
|
-
requestBody.text = task.params.text;
|
|
1945
|
-
requestBody.voiceCode = task.params.voiceCode;
|
|
1946
|
-
}
|
|
1947
|
-
else if (task.params.type === BroadcastType.AUDIO) {
|
|
1948
|
-
requestBody.text = task.params.text;
|
|
1949
|
-
requestBody.audioUrl = task.params.audioUrl;
|
|
1950
|
-
}
|
|
1951
|
-
// 发起流式请求
|
|
1952
|
-
fetchEventSource(apiUrl, {
|
|
1953
|
-
method: 'POST',
|
|
1954
|
-
headers: {
|
|
1955
|
-
'Content-Type': 'application/json',
|
|
1956
|
-
'x_auth_token': ((_a = ConfigManager.getInstance().getConfig()) === null || _a === void 0 ? void 0 : _a.token) || ''
|
|
1957
|
-
},
|
|
1958
|
-
body: JSON.stringify(requestBody),
|
|
1959
|
-
signal: task.controller.signal,
|
|
1960
|
-
openWhenHidden: true,
|
|
1961
|
-
/**
|
|
1962
|
-
* 连接建立时的回调,用于检查 HTTP 状态码
|
|
1963
|
-
* @param response - HTTP 响应对象
|
|
1964
|
-
* @throws {SDKError} 当 HTTP 状态码异常时抛出对应的 SDKError
|
|
1965
|
-
*/
|
|
1966
|
-
onopen: (response) => __awaiter(this, void 0, void 0, function* () {
|
|
1967
|
-
// 检查 HTTP 状态码,处理 401 token 过期等异常
|
|
1968
|
-
if (!response.ok) {
|
|
1969
|
-
const error = this.createHttpError(response.status, response.statusText, task.id);
|
|
1970
|
-
this.handleTaskError(task, error);
|
|
1971
|
-
throw error;
|
|
1972
|
-
}
|
|
1973
|
-
}),
|
|
1974
|
-
onmessage: (event) => {
|
|
1975
|
-
this.handleTaskResponse(task, event.data);
|
|
1976
|
-
},
|
|
1977
|
-
onclose: () => {
|
|
1978
|
-
this.handleTaskClose(task);
|
|
1979
|
-
},
|
|
1980
|
-
onerror: (error) => {
|
|
1981
|
-
// 如果是 SDKError,说明已经在 onopen 中处理过了(HTTP 状态码错误),直接抛出
|
|
1982
|
-
if (error instanceof SDKError) {
|
|
1983
|
-
throw error;
|
|
1984
|
-
}
|
|
1985
|
-
// 其他错误(网络错误等)转换为 SDKError
|
|
1986
|
-
this.logger.error('broadcast onerror', error);
|
|
1987
|
-
const sdkError = this.convertToSDKError(error, task.id);
|
|
1988
|
-
this.handleTaskError(task, sdkError);
|
|
1989
|
-
throw sdkError;
|
|
1990
|
-
}
|
|
1991
|
-
});
|
|
1992
|
-
}
|
|
1993
|
-
catch (error) {
|
|
1994
|
-
// 将所有异常统一转换为 SDKError
|
|
1995
|
-
const sdkError = this.convertToSDKError(error, task.id);
|
|
1996
|
-
this.handleTaskError(task, sdkError);
|
|
1997
|
-
}
|
|
1998
|
-
});
|
|
1999
|
-
}
|
|
2000
|
-
/**
|
|
2001
|
-
* 处理任务响应
|
|
2002
|
-
* @param task - 播报任务
|
|
2003
|
-
* @param data - 响应数据
|
|
2004
|
-
* @description 处理任务的流式响应数据
|
|
2005
|
-
* @private
|
|
2006
|
-
*/
|
|
2007
|
-
handleTaskResponse(task, data) {
|
|
2008
|
-
try {
|
|
2009
|
-
const response = JSON.parse(data);
|
|
2010
|
-
// 错误处理
|
|
2011
|
-
if (response.code !== 0) {
|
|
2012
|
-
const error = this.createBroadcastError(response.code, response.message);
|
|
2013
|
-
this.handleTaskError(task, error);
|
|
2014
|
-
return;
|
|
2015
|
-
}
|
|
2016
|
-
// 处理音频数据
|
|
2017
|
-
if (response.data) {
|
|
2018
|
-
this.hasReceivedAudio = true;
|
|
2019
|
-
// 自定义音频播报时,如果服务器未返回音频URL,使用传入的audioUrl
|
|
2020
|
-
if (task.params.type === BroadcastType.AUDIO && task.params.audioUrl && !response.data.voiceUrl) {
|
|
2021
|
-
response.data.voiceUrl = task.params.audioUrl;
|
|
2022
|
-
}
|
|
2023
|
-
// 如果音频和口播文件是相对路径,则需要拼接API基础URL
|
|
2024
|
-
const { voiceUrl, mouthShapeUrl } = response.data;
|
|
2025
|
-
if (voiceUrl && !voiceUrl.startsWith('http')) {
|
|
2026
|
-
response.data.voiceUrl = `${ConfigManager.getInstance().getApiBaseUrl(false)}${voiceUrl}`;
|
|
2027
|
-
}
|
|
2028
|
-
if (mouthShapeUrl && !mouthShapeUrl.startsWith('http')) {
|
|
2029
|
-
response.data.mouthShapeUrl = `${ConfigManager.getInstance().getApiBaseUrl(false)}${mouthShapeUrl}`;
|
|
2030
|
-
}
|
|
2031
|
-
// 添加处理后的响应对象到待发送队列
|
|
2032
|
-
task.pendingResponses.push(response);
|
|
2033
|
-
this.logger.debug('Response added to task', { taskId: task.id, response, pendingCount: task.pendingResponses.length });
|
|
2034
|
-
// 检查是否完成
|
|
2035
|
-
if (response.data.done) {
|
|
2036
|
-
task.isGenerationComplete = true;
|
|
2037
|
-
this.logger.debug('Task generation completed', { taskId: task.id, totalResponses: task.pendingResponses.length });
|
|
2038
|
-
}
|
|
2039
|
-
}
|
|
2040
|
-
}
|
|
2041
|
-
catch (error) {
|
|
2042
|
-
this.handleTaskError(task, new SDKError(OperationErrorCode.OPERATION_FAILED, typeof error === 'string' ? error : (error.message || '播报服务错误')));
|
|
2043
|
-
}
|
|
2044
|
-
}
|
|
2045
|
-
/**
|
|
2046
|
-
* 发送下一个响应给unity播放
|
|
2047
|
-
* @param task - 播报任务
|
|
2048
|
-
* @description 发送任务的响应数组中的第一个待发送响应到Unity(按顺序发送),发送后立即删除
|
|
2049
|
-
* @private
|
|
2050
|
-
*/
|
|
2051
|
-
sendNextResponse(task) {
|
|
2052
|
-
var _a;
|
|
2053
|
-
if (task.pendingResponses.length === 0) {
|
|
2054
|
-
return;
|
|
2055
|
-
}
|
|
2056
|
-
// 取出第一个待发送的响应
|
|
2057
|
-
const response = task.pendingResponses.shift();
|
|
2058
|
-
this.logger.debug('Sending response to Unity', {
|
|
2059
|
-
taskId: task.id,
|
|
2060
|
-
remainingResponses: task.pendingResponses.length,
|
|
2061
|
-
voiceUrl: (_a = response.data) === null || _a === void 0 ? void 0 : _a.voiceUrl
|
|
2062
|
-
});
|
|
2063
|
-
// 发送响应到Unity,这里不使用sendAsyncMessage,因为appendBroadcast不需要等待完成,而是在任务播报完成后通过startBroadcast回调通知
|
|
2064
|
-
this.sendMessage('AppendBroadcast', {
|
|
2065
|
-
response,
|
|
2066
|
-
callbackFun: this.uniqueCallbackName,
|
|
2067
|
-
operationType: BroadcastOperationType.APPEND_BROADCAST
|
|
2068
|
-
});
|
|
2069
|
-
// 如果任务已完成且没有更多待发送响应,标记任务完成并推进序号
|
|
2070
|
-
if (task.isGenerationComplete && task.pendingResponses.length === 0) {
|
|
2071
|
-
task.status = BroadcastTaskStatus.COMPLETED;
|
|
2072
|
-
this.currentSendingSequence = task.sequence;
|
|
2073
|
-
this.logger.debug('Task completed', { taskId: task.id });
|
|
2074
|
-
}
|
|
2075
|
-
}
|
|
2076
|
-
/**
|
|
2077
|
-
* 处理任务关闭
|
|
2078
|
-
* @param task - 播报任务
|
|
2079
|
-
* @description 处理任务的流式连接关闭,并启动下一个待处理任务的请求
|
|
2080
|
-
* @private
|
|
2081
|
-
*/
|
|
2082
|
-
handleTaskClose(task) {
|
|
2083
|
-
this.logger.debug('Task stream closed', { taskId: task.id });
|
|
2084
|
-
// 如果任务已经失败或取消,不再处理(已经在 handleTaskError 中处理过了)
|
|
2085
|
-
if (task.status === BroadcastTaskStatus.FAILED || task.status === BroadcastTaskStatus.CANCELLED) {
|
|
2086
|
-
return;
|
|
2087
|
-
}
|
|
2088
|
-
// 如果还没有标记为完成,则标记为完成
|
|
2089
|
-
if (!task.isGenerationComplete && task.status === BroadcastTaskStatus.REQUESTING) {
|
|
2090
|
-
task.isGenerationComplete = true;
|
|
2091
|
-
}
|
|
2092
|
-
// 当前任务请求完成后(流断开后),启动下一个待处理任务的请求
|
|
2093
|
-
this.startNextPendingTask();
|
|
2094
|
-
}
|
|
2095
|
-
/**
|
|
2096
|
-
* 处理任务错误
|
|
2097
|
-
* @param task - 播报任务
|
|
2098
|
-
* @param error - 错误对象
|
|
2099
|
-
* @description 处理任务执行过程中的错误,防止重复触发错误回调,并启动下一个待处理任务
|
|
2100
|
-
* @private
|
|
2101
|
-
*/
|
|
2102
|
-
handleTaskError(task, error) {
|
|
2103
|
-
var _a, _b;
|
|
2104
|
-
// 如果任务已经是失败或取消状态,不再重复处理,防止回调被多次触发
|
|
2105
|
-
if (task.status === BroadcastTaskStatus.FAILED || task.status === BroadcastTaskStatus.CANCELLED) {
|
|
2106
|
-
return;
|
|
2107
|
-
}
|
|
2108
|
-
task.status = BroadcastTaskStatus.FAILED;
|
|
2109
|
-
task.error = error;
|
|
2110
|
-
this.logger.error(`Task failed - ${task.id}`, error);
|
|
2111
|
-
// 触发错误回调(只触发一次)
|
|
2112
|
-
(_b = (_a = this.callbacks).onError) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
2113
|
-
// 流式播报场景:一个任务失败,取消并清空所有后续任务
|
|
2114
|
-
this.cancelAllTasks();
|
|
2115
|
-
// 重置播报状态
|
|
2116
|
-
this.isBroadcastingAudio = false;
|
|
2117
|
-
this.hasReceivedAudio = false;
|
|
2118
|
-
this.currentSendingSequence = 0;
|
|
2119
|
-
this.clearQueueProcessTimer();
|
|
2120
|
-
this.logger.debug('Task failed, all tasks cancelled and broadcast state reset');
|
|
2121
|
-
}
|
|
2122
|
-
/**
|
|
2123
|
-
* 清理已完成的任务
|
|
2124
|
-
* @description 从队列中移除已完成、失败或取消的任务
|
|
2125
|
-
* @private
|
|
2126
|
-
*/
|
|
2127
|
-
cleanupCompletedTasks() {
|
|
2128
|
-
const beforeLength = this.taskQueue.length;
|
|
2129
|
-
this.taskQueue = this.taskQueue.filter(task => task.status !== BroadcastTaskStatus.COMPLETED
|
|
2130
|
-
&& task.status !== BroadcastTaskStatus.FAILED
|
|
2131
|
-
&& task.status !== BroadcastTaskStatus.CANCELLED);
|
|
2132
|
-
const removedCount = beforeLength - this.taskQueue.length;
|
|
2133
|
-
if (removedCount > 0) {
|
|
2134
|
-
this.logger.debug('Cleaned up completed tasks', { removedCount, remainingTasks: this.taskQueue.length });
|
|
2135
|
-
}
|
|
2136
|
-
}
|
|
2137
|
-
/**
|
|
2138
|
-
* 取消所有任务
|
|
2139
|
-
* @description 取消队列中的所有任务
|
|
2140
|
-
* @private
|
|
2141
|
-
*/
|
|
2142
|
-
cancelAllTasks() {
|
|
2143
|
-
for (const task of this.taskQueue) {
|
|
2144
|
-
if (task.status !== BroadcastTaskStatus.COMPLETED && task.status !== BroadcastTaskStatus.FAILED) {
|
|
2145
|
-
task.controller.abort();
|
|
2146
|
-
task.status = BroadcastTaskStatus.CANCELLED;
|
|
2147
|
-
}
|
|
2148
|
-
}
|
|
2149
|
-
this.taskQueue = [];
|
|
2150
|
-
this.taskSequence = 0;
|
|
2151
|
-
this.currentSendingSequence = 0;
|
|
2152
|
-
this.logger.debug('All tasks cancelled and queue cleared');
|
|
2153
|
-
}
|
|
2154
|
-
/** 全局回调函数名称 */
|
|
2155
|
-
get callbackFunctionName() {
|
|
2156
|
-
return 'uniBroadcastCallback';
|
|
2157
|
-
}
|
|
2158
|
-
/**
|
|
2159
|
-
* 获取播报API路径
|
|
2160
|
-
* @param type - 播报类型
|
|
2161
|
-
* @returns string 返回对应的API路径
|
|
2162
|
-
* @description 根据播报类型获取对应的API路径
|
|
2163
|
-
* @throws {SDKError} 当播报类型未知时抛出错误
|
|
2164
|
-
* @private
|
|
2165
|
-
*/
|
|
2166
|
-
getBroadcastApiPath(type) {
|
|
2167
|
-
switch (type) {
|
|
2168
|
-
case BroadcastType.TEXT:
|
|
2169
|
-
return '/aiep-openapi/avatar-interaction/v1/broadcast/text';
|
|
2170
|
-
case BroadcastType.AUDIO:
|
|
2171
|
-
return '/aiep-openapi/avatar-interaction/v1/broadcast/audio';
|
|
2172
|
-
default:
|
|
2173
|
-
throw new SDKError(ConfigErrorCode.INVALID_CONFIG, `未知的播报类型: ${type}`);
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
/**
|
|
2177
|
-
* 验证播报参数
|
|
2178
|
-
* @param params - 播报参数
|
|
2179
|
-
* @description 验证播报参数的有效性
|
|
2180
|
-
* @throws {SDKError} 当参数验证失败时抛出错误
|
|
2181
|
-
* @private
|
|
2182
|
-
*/
|
|
2183
|
-
validateBroadcastParams(params) {
|
|
2184
|
-
if (params.type === BroadcastType.TEXT) {
|
|
2185
|
-
if (!params.text || !params.voiceCode) {
|
|
2186
|
-
throw new SDKError(ConfigErrorCode.MISSING_REQUIRED_PARAM, '文本播报需要提供text和voiceCode参数');
|
|
2187
|
-
}
|
|
2188
|
-
}
|
|
2189
|
-
else if (params.type === BroadcastType.AUDIO) {
|
|
2190
|
-
if (!params.audioUrl) {
|
|
2191
|
-
throw new SDKError(ConfigErrorCode.MISSING_REQUIRED_PARAM, '自定义音频播报需要提供audioUrl参数');
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
}
|
|
2195
|
-
/**
|
|
2196
|
-
* 处理错误
|
|
2197
|
-
* @param error - 错误对象
|
|
2198
|
-
* @description 统一处理播报过程中的错误
|
|
2199
|
-
* @private
|
|
2200
|
-
*/
|
|
2201
|
-
handleError(error) {
|
|
2202
|
-
var _a, _b;
|
|
2203
|
-
this.logger.error('Broadcast error occurred', error);
|
|
2204
|
-
// 触发错误回调
|
|
2205
|
-
(_b = (_a = this.callbacks).onError) === null || _b === void 0 ? void 0 : _b.call(_a, error);
|
|
2206
|
-
}
|
|
2207
|
-
/**
|
|
2208
|
-
* 创建播报请求错误对象
|
|
2209
|
-
* @param errorCode - 错误码
|
|
2210
|
-
* @param errorMessage - 错误消息
|
|
2211
|
-
* @returns SDKError 错误对象
|
|
2212
|
-
* @description 根据错误码创建对应的 SDKError 对象
|
|
2213
|
-
* @private
|
|
2214
|
-
*/
|
|
2215
|
-
createBroadcastError(errorCode, errorMessage) {
|
|
2216
|
-
// 用户权益额度不存在错误码 14001
|
|
2217
|
-
// 用户权益额度不足错误码 14002
|
|
2218
|
-
// 用户权益额度冻结失败 14003
|
|
2219
|
-
switch (errorCode) {
|
|
2220
|
-
case 14001:
|
|
2221
|
-
return new SDKError(OperationErrorCode.BROADCAST_EQUITY_NOT_EXIST, '用户权益额度不存在');
|
|
2222
|
-
case 14002:
|
|
2223
|
-
return new SDKError(OperationErrorCode.BROADCAST_EQUITY_NOT_ENOUGH, '用户权益额度不足');
|
|
2224
|
-
case 14003:
|
|
2225
|
-
return new SDKError(OperationErrorCode.BROADCAST_EQUITY_FREEZE_FAILED, '用户权益额度冻结失败');
|
|
2226
|
-
default:
|
|
2227
|
-
return new SDKError(OperationErrorCode.OPERATION_FAILED, `${errorMessage}(${errorCode})` || `播报服务错误(${errorCode})`);
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
/**
|
|
2231
|
-
* 启动下一个待处理任务的请求
|
|
2232
|
-
* @description 在当前任务请求完成或失败后,检查并启动下一个待处理任务
|
|
2233
|
-
* @private
|
|
2234
|
-
*/
|
|
2235
|
-
startNextPendingTask() {
|
|
2236
|
-
// 查找下一个待处理的任务(按序号排序,取第一个 PENDING 状态的任务)
|
|
2237
|
-
const nextPendingTask = this.taskQueue
|
|
2238
|
-
.filter((t) => t.status === BroadcastTaskStatus.PENDING)
|
|
2239
|
-
.sort((a, b) => a.sequence - b.sequence)[0];
|
|
2240
|
-
if (nextPendingTask) {
|
|
2241
|
-
this.logger.debug('Starting next pending task', {
|
|
2242
|
-
taskId: nextPendingTask.id,
|
|
2243
|
-
sequence: nextPendingTask.sequence
|
|
2244
|
-
});
|
|
2245
|
-
this.startTaskRequest(nextPendingTask);
|
|
2246
|
-
}
|
|
2247
|
-
else {
|
|
2248
|
-
this.logger.debug('No pending tasks to start');
|
|
2249
|
-
}
|
|
2250
|
-
}
|
|
2251
|
-
/**
|
|
2252
|
-
* 清理队列处理定时器
|
|
2253
|
-
* @description 清理队列处理定时器
|
|
2254
|
-
* @private
|
|
2255
|
-
*/
|
|
2256
|
-
clearQueueProcessTimer() {
|
|
2257
|
-
if (this.queueProcessTimer) {
|
|
2258
|
-
clearInterval(this.queueProcessTimer);
|
|
2259
|
-
this.queueProcessTimer = null;
|
|
2260
|
-
this.logger.debug('Queue process timer cleared');
|
|
2261
|
-
}
|
|
2262
|
-
}
|
|
2263
|
-
/**
|
|
2264
|
-
* 根据 HTTP 状态码创建对应的 SDKError
|
|
2265
|
-
* @param status - HTTP 状态码
|
|
2266
|
-
* @param statusText - HTTP 状态文本
|
|
2267
|
-
* @param taskId - 任务 ID(用于日志记录)
|
|
2268
|
-
* @returns SDKError 实例
|
|
2269
|
-
* @description 将 HTTP 错误状态码映射为对应的 SDKError
|
|
2270
|
-
* @private
|
|
2271
|
-
*/
|
|
2272
|
-
createHttpError(status, statusText, taskId) {
|
|
2273
|
-
this.logger.warn(`HTTP error occurred - Task: ${taskId}, Status: ${status}`, { status, statusText });
|
|
2274
|
-
switch (status) {
|
|
2275
|
-
case 401:
|
|
2276
|
-
// Token 过期或未授权
|
|
2277
|
-
return new SDKError(NetworkErrorCode.UNAUTHORIZED, `Token 已过期或无效,请重新授权 (HTTP ${status})`);
|
|
2278
|
-
case 403:
|
|
2279
|
-
// 禁止访问
|
|
2280
|
-
return new SDKError(NetworkErrorCode.UNAUTHORIZED, `无权限访问该资源 (HTTP ${status})`);
|
|
2281
|
-
case 404:
|
|
2282
|
-
// 资源不存在
|
|
2283
|
-
return new SDKError(NetworkErrorCode.SERVER_ERROR, `请求的资源不存在 (HTTP ${status})`);
|
|
2284
|
-
case 500:
|
|
2285
|
-
case 502:
|
|
2286
|
-
case 503:
|
|
2287
|
-
case 504:
|
|
2288
|
-
// 服务器错误
|
|
2289
|
-
return new SDKError(NetworkErrorCode.SERVER_ERROR, `服务器错误,请稍后重试 (HTTP ${status})`);
|
|
2290
|
-
default:
|
|
2291
|
-
// 其他 HTTP 错误
|
|
2292
|
-
return new SDKError(NetworkErrorCode.CONNECTION_FAILED, `网络请求失败: ${statusText || 'Unknown Error'} (HTTP ${status})`);
|
|
2293
|
-
}
|
|
2294
|
-
}
|
|
2295
|
-
/**
|
|
2296
|
-
* 将任意错误转换为 SDKError
|
|
2297
|
-
* @param error - 原始错误对象
|
|
2298
|
-
* @param taskId - 任务 ID(用于日志记录)
|
|
2299
|
-
* @returns SDKError 实例
|
|
2300
|
-
* @description 统一将各种类型的错误转换为 SDKError,便于上层统一处理
|
|
2301
|
-
* @private
|
|
2302
|
-
*/
|
|
2303
|
-
convertToSDKError(error, taskId) {
|
|
2304
|
-
// 如果已经是 SDKError,直接返回
|
|
2305
|
-
if (error instanceof SDKError) {
|
|
2306
|
-
return error;
|
|
2307
|
-
}
|
|
2308
|
-
// 如果是普通 Error 对象
|
|
2309
|
-
if (error instanceof Error) {
|
|
2310
|
-
// 检查是否是网络相关的错误
|
|
2311
|
-
const errorMessage = error.message.toLowerCase();
|
|
2312
|
-
if (errorMessage.includes('timeout') || errorMessage.includes('timed out')) {
|
|
2313
|
-
return new SDKError(NetworkErrorCode.REQUEST_TIMEOUT, `请求超时 - Task: ${taskId}`, error);
|
|
2314
|
-
}
|
|
2315
|
-
if (errorMessage.includes('network') || errorMessage.includes('fetch') || errorMessage.includes('connection')) {
|
|
2316
|
-
// "Failed to fetch" 通常是 CORS 问题或 Token 过期(服务端返回 401 但未设置 CORS 头)
|
|
2317
|
-
const hint = errorMessage.includes('failed to fetch')
|
|
2318
|
-
? '(可能是 Token 过期或 CORS 跨域问题,请检查 Token 是否有效)'
|
|
2319
|
-
: '';
|
|
2320
|
-
return new SDKError(NetworkErrorCode.CONNECTION_FAILED, `网络请求失败 - Task: ${taskId}: ${error.message}${hint}`, error);
|
|
2321
|
-
}
|
|
2322
|
-
if (errorMessage.includes('abort') || errorMessage.includes('cancel')) {
|
|
2323
|
-
return new SDKError(OperationErrorCode.OPERATION_CANCELLED, `操作已取消 - Task: ${taskId}`, error);
|
|
2324
|
-
}
|
|
2325
|
-
// 默认作为操作失败处理
|
|
2326
|
-
return new SDKError(OperationErrorCode.OPERATION_FAILED, `播报任务执行失败 - Task: ${taskId}: ${error.message}`, error);
|
|
2327
|
-
}
|
|
2328
|
-
// 如果是字符串或其他类型
|
|
2329
|
-
const errorMessage = String(error);
|
|
2330
|
-
return new SDKError(OperationErrorCode.OPERATION_FAILED, `播报任务执行失败 - Task: ${taskId}: ${errorMessage}`);
|
|
2331
|
-
}
|
|
2332
|
-
}
|
|
2333
|
-
|
|
2334
|
-
/**
|
|
2335
|
-
* @fileoverview 3D数字人SDK核心模块
|
|
2336
|
-
* @description 提供数字人SDK的核心功能,包括Unity实例管理、Avatar API封装等
|
|
2337
|
-
*/
|
|
2338
|
-
/**
|
|
2339
|
-
* 数字人加载器类
|
|
2340
|
-
* @class ZEEAvatarLoader
|
|
2341
|
-
* @description 数字人SDK的核心加载器,负责Unity实例的初始化和管理
|
|
2342
|
-
*/
|
|
2343
|
-
class ZEEAvatarLoader {
|
|
2344
|
-
/**
|
|
2345
|
-
* 构造函数
|
|
2346
|
-
* @param config - Unity配置对象
|
|
2347
|
-
* @description 初始化数字人加载器,创建Unity加载器实例
|
|
2348
|
-
*/
|
|
2349
|
-
constructor() {
|
|
2350
|
-
/** Avatar API实例 */
|
|
2351
|
-
this.apiService = null;
|
|
2352
|
-
/** Unity实例 */
|
|
2353
|
-
this.unityInstance = null;
|
|
2354
|
-
this.loader = new UnityLoader();
|
|
2355
|
-
}
|
|
2356
|
-
/**
|
|
2357
|
-
* 初始化数字人SDK
|
|
2358
|
-
* @returns Promise<IAvatarAPI> 返回Avatar API的Promise
|
|
2359
|
-
* @description 完整的SDK初始化流程,包括Unity实例创建和Avatar API初始化
|
|
2360
|
-
* @throws {Error} 当Unity初始化失败时抛出错误
|
|
2361
|
-
* @example
|
|
2362
|
-
* ```typescript
|
|
2363
|
-
* const loader = new ZEEAvatarLoader({
|
|
2364
|
-
* loaderUrl: 'path/to/webgl.loader.js',
|
|
2365
|
-
* dataUrl: 'path/to/webgl.data.unityweb',
|
|
2366
|
-
* frameworkUrl: 'path/to/webgl.framework.js.unityweb',
|
|
2367
|
-
* codeUrl: 'path/to/webgl.wasm.unityweb',
|
|
2368
|
-
* containerId: 'unity-container'
|
|
2369
|
-
* });
|
|
2370
|
-
*
|
|
2371
|
-
* const avatarAPI = await loader.init();
|
|
2372
|
-
* ```
|
|
2373
|
-
*/
|
|
2374
|
-
init() {
|
|
2375
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2376
|
-
this.unityInstance = yield this.loader.init();
|
|
2377
|
-
this.initGlobalConfig();
|
|
2378
|
-
// 向下兼容,后续版本不在使用该实例
|
|
2379
|
-
this.apiService = new AvatarService({
|
|
2380
|
-
unityInstance: this.unityInstance
|
|
2381
|
-
});
|
|
2382
|
-
return this.apiService;
|
|
2383
|
-
});
|
|
2384
|
-
}
|
|
2385
|
-
/**
|
|
2386
|
-
* 更新token
|
|
2387
|
-
* @param token - 新的token
|
|
2388
|
-
* @description 更新当前的token
|
|
2389
|
-
*/
|
|
2390
|
-
updateToken(token) {
|
|
2391
|
-
const currentConfig = ConfigManager.getInstance().getConfig();
|
|
2392
|
-
if (currentConfig) {
|
|
2393
|
-
ConfigManager.getInstance().setConfig(Object.assign(Object.assign({}, currentConfig), { token }));
|
|
2394
|
-
this.initGlobalConfig();
|
|
2395
|
-
}
|
|
2396
|
-
}
|
|
2397
|
-
/**
|
|
2398
|
-
* 获取Avatar API实例
|
|
2399
|
-
* @returns IAvatarAPI | null 返回Avatar API实例或null
|
|
2400
|
-
* @description 获取当前的Avatar API实例,如果未初始化则返回null
|
|
2401
|
-
*/
|
|
2402
|
-
getAPI() {
|
|
2403
|
-
return this.apiService;
|
|
2404
|
-
}
|
|
2405
|
-
/**
|
|
2406
|
-
* 获取Unity实例
|
|
2407
|
-
* @returns IUnityInstance | null 返回Unity实例或null
|
|
2408
|
-
* @description 获取当前的Unity实例,如果未初始化则返回null
|
|
2409
|
-
*/
|
|
2410
|
-
getInstance() {
|
|
2411
|
-
return this.unityInstance;
|
|
2412
|
-
}
|
|
2413
|
-
/**
|
|
2414
|
-
* 获取容器ID
|
|
2415
|
-
* @returns string 返回容器ID
|
|
2416
|
-
* @description 获取Unity容器的DOM元素ID
|
|
2417
|
-
*/
|
|
2418
|
-
getContainerId() {
|
|
2419
|
-
return ConfigManager.getInstance().getConfig().containerId;
|
|
2420
|
-
}
|
|
2421
|
-
/**
|
|
2422
|
-
* 销毁SDK实例
|
|
2423
|
-
* @description 清理所有资源,包括Unity实例和DOM元素
|
|
2424
|
-
* @example
|
|
2425
|
-
* ```typescript
|
|
2426
|
-
* loader.destroy();
|
|
2427
|
-
* ```
|
|
2428
|
-
*/
|
|
2429
|
-
destroy() {
|
|
2430
|
-
if (this.unityInstance) {
|
|
2431
|
-
// 移除DOM容器下的canvas元素
|
|
2432
|
-
const container = document.getElementById(this.getContainerId());
|
|
2433
|
-
if (container) {
|
|
2434
|
-
const canvas = container.querySelector('#unity-canvas');
|
|
2435
|
-
if (canvas) {
|
|
2436
|
-
canvas.remove();
|
|
2437
|
-
}
|
|
2438
|
-
}
|
|
2439
|
-
// 退出Unity实例
|
|
2440
|
-
if (typeof this.unityInstance.Quit === 'function') {
|
|
2441
|
-
this.unityInstance.Quit();
|
|
2442
|
-
}
|
|
2443
|
-
// 清理引用
|
|
2444
|
-
this.unityInstance = null;
|
|
2445
|
-
this.apiService = null;
|
|
2446
|
-
}
|
|
2447
|
-
// 重置配置管理器
|
|
2448
|
-
ConfigManager.getInstance().reset();
|
|
2449
|
-
}
|
|
2450
|
-
/**
|
|
2451
|
-
* 初始化全局配置
|
|
2452
|
-
* @description 向Unity发送全局配置参数
|
|
2453
|
-
* @protected
|
|
2454
|
-
*/
|
|
2455
|
-
initGlobalConfig() {
|
|
2456
|
-
const config = ConfigManager.getInstance().getConfig();
|
|
2457
|
-
const { assetsFrom } = config;
|
|
2458
|
-
const globalParams = {
|
|
2459
|
-
token: config === null || config === void 0 ? void 0 : config.token,
|
|
2460
|
-
apiBaseUrl: ConfigManager.getInstance().getApiBaseUrl(false),
|
|
2461
|
-
idleMotionList: config === null || config === void 0 ? void 0 : config.idleMotionList
|
|
2462
|
-
// 純AB包方案在SDK 2.1.0 中已弃用
|
|
2463
|
-
// assetsUrl: config?.assetsUrl
|
|
2464
|
-
};
|
|
2465
|
-
this.unityInstance.SendMessage('AvatarSDK', 'InitializeConfig', JSON.stringify(globalParams));
|
|
2466
|
-
console.warn('[ Send Unity message ]: AvatarSDK.InitializeConfig', globalParams);
|
|
2467
|
-
//
|
|
2468
|
-
if (assetsFrom !== 'cloud') {
|
|
2469
|
-
const assetModuleParams = {
|
|
2470
|
-
isZip: true,
|
|
2471
|
-
assetBundlePath: config === null || config === void 0 ? void 0 : config.assetsUrl
|
|
2472
|
-
};
|
|
2473
|
-
this.unityInstance.SendMessage('AvatarSDK', 'InitAssetBundleModule', JSON.stringify(assetModuleParams));
|
|
2474
|
-
console.warn('[ Send Unity message ]: AvatarSDK.InitAssetBundleModule', assetModuleParams);
|
|
2475
|
-
}
|
|
2476
|
-
}
|
|
2477
|
-
}
|
|
2478
|
-
|
|
2479
|
-
// 深拷贝
|
|
2480
|
-
/**
|
|
2481
|
-
* 比较两个版本号的前两位(major.minor)是否一致
|
|
2482
|
-
* @param version1 版本号1,格式:x.x.x
|
|
2483
|
-
* @param version2 版本号2,格式:x.x.x
|
|
2484
|
-
* @returns boolean 如果前两位一致返回 true,否则返回 false
|
|
2485
|
-
* @description 用于检查 SDK 版本与资源版本是否兼容
|
|
2486
|
-
* @example
|
|
2487
|
-
* compareVersionCompatibility('2.1.0', '2.1.5') // true
|
|
2488
|
-
* compareVersionCompatibility('2.1.0', '2.2.0') // false
|
|
2489
|
-
* compareVersionCompatibility('2.1.0', '3.0.0') // false
|
|
2490
|
-
*/
|
|
2491
|
-
function compareVersionCompatibility(version1, version2) {
|
|
2492
|
-
// 提取版本号的前两位(major.minor)
|
|
2493
|
-
const getMajorMinor = (version) => {
|
|
2494
|
-
const parts = version.split('.');
|
|
2495
|
-
if (parts.length < 2) {
|
|
2496
|
-
return version;
|
|
2497
|
-
}
|
|
2498
|
-
return `${parts[0]}.${parts[1]}`;
|
|
2499
|
-
};
|
|
2500
|
-
const v1MajorMinor = getMajorMinor(version1);
|
|
2501
|
-
const v2MajorMinor = getMajorMinor(version2);
|
|
2502
|
-
return v1MajorMinor === v2MajorMinor;
|
|
2503
|
-
}
|
|
2504
|
-
|
|
2505
|
-
/**
|
|
2506
|
-
* @fileoverview SDK 版本号常量
|
|
2507
|
-
* @description 此文件由构建脚本自动生成,请勿手动修改
|
|
2508
|
-
*/
|
|
2509
|
-
/**
|
|
2510
|
-
* SDK 版本号
|
|
2511
|
-
* @const {string} SDK_VERSION
|
|
2512
|
-
*/
|
|
2513
|
-
const SDK_VERSION = '2.1.5';
|
|
2514
|
-
|
|
2515
|
-
/**
|
|
2516
|
-
* @fileoverview 统一的3D数字人SDK入口类
|
|
2517
|
-
* @description 提供统一的SDK接口,内部通过组合模式调用各个服务模块
|
|
2518
|
-
*/
|
|
2519
|
-
/**
|
|
2520
|
-
* 生成唯一标识符
|
|
2521
|
-
* @returns string 唯一标识符
|
|
2522
|
-
*/
|
|
2523
|
-
function generateUniqueId() {
|
|
2524
|
-
return `${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
|
|
2525
|
-
}
|
|
2526
|
-
/**
|
|
2527
|
-
* 统一的3D数字人SDK类
|
|
2528
|
-
* @class ZEEAvatarSDK
|
|
2529
|
-
* @description 提供统一的SDK接口,简化用户使用体验,支持多实例
|
|
2530
|
-
*/
|
|
2531
|
-
class ZEEAvatarSDK {
|
|
2532
|
-
/**
|
|
2533
|
-
* 构造函数
|
|
2534
|
-
* @param config SDK配置对象
|
|
2535
|
-
*/
|
|
2536
|
-
constructor(config) {
|
|
2537
|
-
/** Unity加载器实例 */
|
|
2538
|
-
this.loader = null;
|
|
2539
|
-
/** Avatar服务实例 */
|
|
2540
|
-
this.avatarService = null;
|
|
2541
|
-
/** 播报服务实例 */
|
|
2542
|
-
this.broadcastService = null;
|
|
2543
|
-
/** Unity实例 */
|
|
2544
|
-
this.unityInstance = null;
|
|
2545
|
-
/** SDK初始化状态 */
|
|
2546
|
-
this.isInitialized = false;
|
|
2547
|
-
this.instanceId = generateUniqueId();
|
|
2548
|
-
// 设置全局配置
|
|
2549
|
-
ConfigManager.getInstance().setConfig(config);
|
|
2550
|
-
// 设置日志
|
|
2551
|
-
this.logger = new SimpleLogger(ConfigManager.getInstance().getConfig().enableDebugLog);
|
|
2552
|
-
this.logger.info('SDK版本', SDK_VERSION);
|
|
2553
|
-
}
|
|
2554
|
-
/**
|
|
2555
|
-
* 初始化数字人
|
|
2556
|
-
* @param avatarCode 数字人编码
|
|
2557
|
-
* @param cameraType 摄像机类型
|
|
2558
|
-
* @returns Promise<IAvatarCallbackResponse> 初始化结果
|
|
2559
|
-
* @description 统一的初始化方法,包含Unity实例初始化和数字人加载
|
|
2560
|
-
*/
|
|
2561
|
-
initializeAvatar(avatarCode_1) {
|
|
2562
|
-
return __awaiter(this, arguments, void 0, function* (avatarCode, cameraType = AvatarCameraType.WHOLE) {
|
|
2563
|
-
var _a;
|
|
2564
|
-
if (this.isInitialized) {
|
|
2565
|
-
throw new SDKError(OperationErrorCode.OPERATION_FAILED, 'SDK已经初始化,请勿重复初始化');
|
|
2566
|
-
}
|
|
2567
|
-
try {
|
|
2568
|
-
const config = ConfigManager.getInstance().getConfig();
|
|
2569
|
-
// 1. 创建Unity加载器
|
|
2570
|
-
this.loader = new ZEEAvatarLoader();
|
|
2571
|
-
// 2. 初始化Unity实例
|
|
2572
|
-
yield this.loader.init();
|
|
2573
|
-
// 3. 获取Unity实例
|
|
2574
|
-
this.unityInstance = this.loader.getInstance();
|
|
2575
|
-
// 4. 创建带有唯一标识符的Avatar服务
|
|
2576
|
-
this.avatarService = new AvatarService({
|
|
2577
|
-
unityInstance: this.unityInstance,
|
|
2578
|
-
instanceId: this.instanceId,
|
|
2579
|
-
enableDebugLog: config.enableDebugLog,
|
|
2580
|
-
timeout: config.operationTimeout
|
|
2581
|
-
});
|
|
2582
|
-
// 5. 创建带有唯一标识符的播报服务
|
|
2583
|
-
this.broadcastService = new BroadcastService({
|
|
2584
|
-
unityInstance: this.unityInstance,
|
|
2585
|
-
instanceId: this.instanceId,
|
|
2586
|
-
callbacks: config.broadcastCallbacks,
|
|
2587
|
-
enableDebugLog: config.enableDebugLog,
|
|
2588
|
-
timeout: config.operationTimeout
|
|
2589
|
-
});
|
|
2590
|
-
// 6. 初始化数字人
|
|
2591
|
-
const result = yield this.avatarService.initializeAvatar(avatarCode, cameraType);
|
|
2592
|
-
if (result.success) {
|
|
2593
|
-
const audioDrivenVersion = (_a = result.data) === null || _a === void 0 ? void 0 : _a.audioDrivenVersion;
|
|
2594
|
-
config.audioDrivenVersion = audioDrivenVersion;
|
|
2595
|
-
this.logger.info('AudioDrivenVersion', audioDrivenVersion);
|
|
2596
|
-
// 7. 检查资源版本兼容性
|
|
2597
|
-
if (audioDrivenVersion) {
|
|
2598
|
-
const isCompatible = compareVersionCompatibility(SDK_VERSION, audioDrivenVersion);
|
|
2599
|
-
if (!isCompatible) {
|
|
2600
|
-
// 销毁SDK实例
|
|
2601
|
-
this.destroy();
|
|
2602
|
-
throw new SDKError(ResourceErrorCode.VERSION_INCOMPATIBLE, `资源版本不兼容: SDK版本为 ${SDK_VERSION},WebGL资源版本为 ${audioDrivenVersion},前两位版本号必须一致`);
|
|
2603
|
-
}
|
|
2604
|
-
this.logger.info('版本兼容性检查通过', `SDK: ${SDK_VERSION}, 资源: ${audioDrivenVersion}`);
|
|
2605
|
-
}
|
|
2606
|
-
this.isInitialized = true;
|
|
2607
|
-
}
|
|
2608
|
-
return result;
|
|
2609
|
-
}
|
|
2610
|
-
catch (error) {
|
|
2611
|
-
throw new SDKError(OperationErrorCode.OPERATION_FAILED, `数字人初始化失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
2612
|
-
}
|
|
2613
|
-
});
|
|
2614
|
-
}
|
|
2615
|
-
/**
|
|
2616
|
-
* 更新Token
|
|
2617
|
-
* @param token 新的认证Token
|
|
2618
|
-
* @description 更新全局Token配置
|
|
2619
|
-
*/
|
|
2620
|
-
updateToken(token) {
|
|
2621
|
-
ConfigManager.getInstance().updateConfig({ token });
|
|
2622
|
-
// 如果loader已经初始化,也更新loader的token
|
|
2623
|
-
if (this.loader) {
|
|
2624
|
-
this.loader.updateToken(token);
|
|
2625
|
-
}
|
|
2626
|
-
}
|
|
2627
|
-
// ===========================================
|
|
2628
|
-
// Avatar API 方法
|
|
2629
|
-
// ===========================================
|
|
2630
|
-
/**
|
|
2631
|
-
* 播放数字人动作
|
|
2632
|
-
* @param clipCode 动作编码
|
|
2633
|
-
* @returns Promise<IAvatarCallbackResponse> 播放结果
|
|
2634
|
-
*/
|
|
2635
|
-
playMotion(clipCode) {
|
|
2636
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2637
|
-
this.ensureInitialized();
|
|
2638
|
-
return yield this.avatarService.playMotion(clipCode);
|
|
2639
|
-
});
|
|
2640
|
-
}
|
|
2641
|
-
/**
|
|
2642
|
-
* 获取当前播放的动作信息
|
|
2643
|
-
* @param getRemainingTime 是否获取剩余时间
|
|
2644
|
-
* @returns Promise<IAvatarCallbackResponse> 动作信息
|
|
2645
|
-
*/
|
|
2646
|
-
getCurrentMotion() {
|
|
2647
|
-
return __awaiter(this, arguments, void 0, function* (getRemainingTime = false) {
|
|
2648
|
-
this.ensureInitialized();
|
|
2649
|
-
return yield this.avatarService.getCurrentMotion(getRemainingTime);
|
|
2650
|
-
});
|
|
2651
|
-
}
|
|
2652
|
-
/**
|
|
2653
|
-
* 卸载数字人
|
|
2654
|
-
* @returns Promise<IAvatarCallbackResponse> 卸载结果
|
|
2655
|
-
*/
|
|
2656
|
-
unloadAvatar() {
|
|
2657
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2658
|
-
this.ensureInitialized();
|
|
2659
|
-
try {
|
|
2660
|
-
const result = yield this.avatarService.unloadAvatar();
|
|
2661
|
-
if (result.success) {
|
|
2662
|
-
this.isInitialized = false;
|
|
2663
|
-
}
|
|
2664
|
-
return result;
|
|
2665
|
-
}
|
|
2666
|
-
catch (error) {
|
|
2667
|
-
throw new SDKError(OperationErrorCode.OPERATION_FAILED, `数字人卸载失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
2668
|
-
}
|
|
2669
|
-
});
|
|
2670
|
-
}
|
|
2671
|
-
/**
|
|
2672
|
-
* 设置摄像机类型
|
|
2673
|
-
* @param cameraType 摄像机类型
|
|
2674
|
-
* @returns Promise<IAvatarCallbackResponse> 设置结果
|
|
2675
|
-
*/
|
|
2676
|
-
setCamera(cameraType) {
|
|
2677
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2678
|
-
this.ensureInitialized();
|
|
2679
|
-
return yield this.avatarService.setCamera(cameraType);
|
|
2680
|
-
});
|
|
2681
|
-
}
|
|
2682
|
-
// ===========================================
|
|
2683
|
-
// Broadcast API 方法
|
|
2684
|
-
// ===========================================
|
|
2685
|
-
/**
|
|
2686
|
-
* 开始播报(适用于普通播报场景)
|
|
2687
|
-
* @param params 播报参数
|
|
2688
|
-
* @param isAppend 是否追加播报
|
|
2689
|
-
* @returns Promise<void> 播报操作的Promise
|
|
2690
|
-
*/
|
|
2691
|
-
startBroadcast(params, isAppend) {
|
|
2692
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2693
|
-
this.ensureInitialized();
|
|
2694
|
-
return yield this.broadcastService.startBroadcast(params, isAppend);
|
|
2695
|
-
});
|
|
2696
|
-
}
|
|
2697
|
-
/**
|
|
2698
|
-
* 开始流式播报(适用于ChatGPT等对话场景)
|
|
2699
|
-
* @param params 播报参数
|
|
2700
|
-
* @param forceRestart 是否强制重新开始(停止当前播报并开始新的)
|
|
2701
|
-
* @returns Promise<void> 播报操作的Promise
|
|
2702
|
-
* @description SDK内部自动维护播报状态,首次调用或空闲时使用 isAppend=false,后续调用自动使用 isAppend=true
|
|
2703
|
-
*/
|
|
2704
|
-
startStreamBroadcast(params, forceRestart) {
|
|
2705
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2706
|
-
this.ensureInitialized();
|
|
2707
|
-
// 如果强制重新开始,先停止当前播报
|
|
2708
|
-
if (forceRestart) {
|
|
2709
|
-
const status = this.broadcastService.getStatus();
|
|
2710
|
-
if (status.isActive) {
|
|
2711
|
-
yield this.broadcastService.stopBroadcast();
|
|
2712
|
-
}
|
|
2713
|
-
// 强制重新开始时使用 isAppend=false
|
|
2714
|
-
return yield this.broadcastService.startBroadcast(params, false);
|
|
2715
|
-
}
|
|
2716
|
-
// 检查当前播报状态
|
|
2717
|
-
const status = this.broadcastService.getStatus();
|
|
2718
|
-
// 如果当前正在播报或生成音频,使用追加模式
|
|
2719
|
-
// 如果当前空闲,使用非追加模式开始新的播报
|
|
2720
|
-
const isAppend = status.isActive;
|
|
2721
|
-
this.logger.debug('startStreamBroadcast', { isActive: status.isActive, isAppend });
|
|
2722
|
-
return yield this.broadcastService.startBroadcast(params, isAppend);
|
|
2723
|
-
});
|
|
2724
|
-
}
|
|
2725
|
-
/**
|
|
2726
|
-
* 暂停播报
|
|
2727
|
-
* @param resetIdle 是否重置空闲状态
|
|
2728
|
-
* @returns Promise<void> 暂停操作的Promise
|
|
2729
|
-
*/
|
|
2730
|
-
pauseBroadcast(resetIdle) {
|
|
2731
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2732
|
-
this.ensureInitialized();
|
|
2733
|
-
return yield this.broadcastService.pauseBroadcast(resetIdle);
|
|
2734
|
-
});
|
|
2735
|
-
}
|
|
2736
|
-
/**
|
|
2737
|
-
* 恢复播报
|
|
2738
|
-
* @returns Promise<void> 恢复操作的Promise
|
|
2739
|
-
*/
|
|
2740
|
-
resumeBroadcast() {
|
|
2741
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2742
|
-
this.ensureInitialized();
|
|
2743
|
-
return yield this.broadcastService.resumeBroadcast();
|
|
2744
|
-
});
|
|
2745
|
-
}
|
|
2746
|
-
/**
|
|
2747
|
-
* 停止播报
|
|
2748
|
-
* @returns Promise<void> 停止操作的Promise
|
|
2749
|
-
*/
|
|
2750
|
-
stopBroadcast() {
|
|
2751
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
2752
|
-
this.ensureInitialized();
|
|
2753
|
-
return yield this.broadcastService.stopBroadcast();
|
|
2754
|
-
});
|
|
2755
|
-
}
|
|
2756
|
-
/**
|
|
2757
|
-
* 更新播报回调函数
|
|
2758
|
-
* @param callbacks 新的回调函数集合
|
|
2759
|
-
*/
|
|
2760
|
-
updateBroadcastCallbacks(callbacks) {
|
|
2761
|
-
this.ensureInitialized();
|
|
2762
|
-
this.broadcastService.updateCallbacks(callbacks);
|
|
2763
|
-
}
|
|
2764
|
-
/**
|
|
2765
|
-
* 获取播报状态
|
|
2766
|
-
* @returns 播报状态信息
|
|
2767
|
-
*/
|
|
2768
|
-
getBroadcastStatus() {
|
|
2769
|
-
this.ensureInitialized();
|
|
2770
|
-
return this.broadcastService.getStatus();
|
|
2771
|
-
}
|
|
2772
|
-
// ===========================================
|
|
2773
|
-
// 生命周期管理
|
|
2774
|
-
// ===========================================
|
|
2775
|
-
/**
|
|
2776
|
-
* 销毁SDK实例
|
|
2777
|
-
* @description 清理所有资源,包括Unity实例和服务
|
|
2778
|
-
*/
|
|
2779
|
-
destroy() {
|
|
2780
|
-
try {
|
|
2781
|
-
// 销毁播报服务
|
|
2782
|
-
if (this.broadcastService) {
|
|
2783
|
-
this.broadcastService.destroy();
|
|
2784
|
-
this.broadcastService = null;
|
|
2785
|
-
}
|
|
2786
|
-
// 销毁Avatar服务
|
|
2787
|
-
if (this.avatarService) {
|
|
2788
|
-
this.avatarService.destroy();
|
|
2789
|
-
this.avatarService = null;
|
|
2790
|
-
}
|
|
2791
|
-
// 销毁Unity加载器
|
|
2792
|
-
if (this.loader) {
|
|
2793
|
-
this.loader.destroy();
|
|
2794
|
-
this.loader = null;
|
|
2795
|
-
}
|
|
2796
|
-
// 清理状态
|
|
2797
|
-
this.unityInstance = null;
|
|
2798
|
-
this.isInitialized = false;
|
|
2799
|
-
}
|
|
2800
|
-
catch (error) {
|
|
2801
|
-
console.error('SDK销毁过程中发生错误:', error);
|
|
2802
|
-
}
|
|
2803
|
-
}
|
|
2804
|
-
// ===========================================
|
|
2805
|
-
// 工具方法
|
|
2806
|
-
// ===========================================
|
|
2807
|
-
/**
|
|
2808
|
-
* 获取SDK实例唯一标识符
|
|
2809
|
-
* @returns string 实例ID
|
|
2810
|
-
*/
|
|
2811
|
-
getInstanceId() {
|
|
2812
|
-
return this.instanceId;
|
|
2813
|
-
}
|
|
2814
|
-
/**
|
|
2815
|
-
* 获取SDK初始化状态
|
|
2816
|
-
* @returns boolean 是否已初始化
|
|
2817
|
-
*/
|
|
2818
|
-
isSDKInitialized() {
|
|
2819
|
-
return this.isInitialized;
|
|
2820
|
-
}
|
|
2821
|
-
/**
|
|
2822
|
-
* 获取Avatar加载状态
|
|
2823
|
-
* @returns boolean 是否已加载Avatar
|
|
2824
|
-
*/
|
|
2825
|
-
isAvatarInitialized() {
|
|
2826
|
-
return this.isInitialized;
|
|
2827
|
-
}
|
|
2828
|
-
/**
|
|
2829
|
-
* 获取SDK配置
|
|
2830
|
-
* @returns IAvatarSDKConfig 当前配置
|
|
2831
|
-
*/
|
|
2832
|
-
getConfig() {
|
|
2833
|
-
const config = ConfigManager.getInstance().getConfig();
|
|
2834
|
-
return config ? Object.assign({}, config) : null;
|
|
2835
|
-
}
|
|
2836
|
-
// ===========================================
|
|
2837
|
-
// 私有方法
|
|
2838
|
-
// ===========================================
|
|
2839
|
-
/**
|
|
2840
|
-
* 确保SDK已初始化
|
|
2841
|
-
* @private
|
|
2842
|
-
*/
|
|
2843
|
-
ensureInitialized() {
|
|
2844
|
-
if (!this.isInitialized) {
|
|
2845
|
-
throw new SDKError(OperationErrorCode.UNITY_NOT_INITIALIZED, 'Avatar尚未初始化,请先调用initializeAvatar()方法');
|
|
2846
|
-
}
|
|
2847
|
-
}
|
|
2848
|
-
}
|
|
2849
|
-
|
|
2850
|
-
export { AvatarCameraType, AvatarOperationType, AvatarService, BroadcastOperationType, BroadcastService, BroadcastType, ConfigErrorCode, ERROR_CODE_MAP, ErrorCategory, NetworkErrorCode, OperationErrorCode, ResourceErrorCode, SDKError, SDK_VERSION, SystemErrorCode, UnityOperationStatus, ZEEAvatarLoader, ZEEAvatarSDK, getErrorInfo };
|
|
1
|
+
function __awaiter(thisArg,_arguments,P,generator){return new(P||(P=Promise))(function(resolve,reject){function fulfilled(value){try{step(generator.next(value))}catch(e){reject(e)}}function rejected(value){try{step(generator.throw(value))}catch(e){reject(e)}}function step(result){result.done?resolve(result.value):function adopt(value){return value instanceof P?value:new P(function(resolve){resolve(value)})}(result.value).then(fulfilled,rejected)}step((generator=generator.apply(thisArg,_arguments||[])).next())})}var ErrorCategory,NetworkErrorCode,OperationErrorCode,ResourceErrorCode,SystemErrorCode,ConfigErrorCode;"function"==typeof SuppressedError&&SuppressedError,function(ErrorCategory){ErrorCategory.NETWORK="NETWORK",ErrorCategory.OPERATION="OPERATION",ErrorCategory.RESOURCE="RESOURCE",ErrorCategory.SYSTEM="SYSTEM",ErrorCategory.CONFIG="CONFIG"}(ErrorCategory||(ErrorCategory={})),function(NetworkErrorCode){NetworkErrorCode[NetworkErrorCode.CONNECTION_FAILED=1001]="CONNECTION_FAILED",NetworkErrorCode[NetworkErrorCode.REQUEST_TIMEOUT=1002]="REQUEST_TIMEOUT",NetworkErrorCode[NetworkErrorCode.SERVER_ERROR=1003]="SERVER_ERROR",NetworkErrorCode[NetworkErrorCode.UNAUTHORIZED=1004]="UNAUTHORIZED"}(NetworkErrorCode||(NetworkErrorCode={})),function(OperationErrorCode){OperationErrorCode[OperationErrorCode.UNITY_NOT_INITIALIZED=2001]="UNITY_NOT_INITIALIZED",OperationErrorCode[OperationErrorCode.AVATAR_NOT_LOADED=2002]="AVATAR_NOT_LOADED",OperationErrorCode[OperationErrorCode.OPERATION_FAILED=2003]="OPERATION_FAILED",OperationErrorCode[OperationErrorCode.OPERATION_TIMEOUT=2004]="OPERATION_TIMEOUT",OperationErrorCode[OperationErrorCode.OPERATION_CANCELLED=2005]="OPERATION_CANCELLED",OperationErrorCode[OperationErrorCode.BROADCAST_EQUITY_NOT_EXIST=2006]="BROADCAST_EQUITY_NOT_EXIST",OperationErrorCode[OperationErrorCode.BROADCAST_EQUITY_NOT_ENOUGH=2007]="BROADCAST_EQUITY_NOT_ENOUGH",OperationErrorCode[OperationErrorCode.BROADCAST_EQUITY_FREEZE_FAILED=2008]="BROADCAST_EQUITY_FREEZE_FAILED"}(OperationErrorCode||(OperationErrorCode={})),function(ResourceErrorCode){ResourceErrorCode[ResourceErrorCode.LOAD_FAILED=3001]="LOAD_FAILED",ResourceErrorCode[ResourceErrorCode.FILE_CORRUPTED=3002]="FILE_CORRUPTED",ResourceErrorCode[ResourceErrorCode.NOT_FOUND=3003]="NOT_FOUND",ResourceErrorCode[ResourceErrorCode.UNSUPPORTED_FORMAT=3004]="UNSUPPORTED_FORMAT",ResourceErrorCode[ResourceErrorCode.VERSION_INCOMPATIBLE=3005]="VERSION_INCOMPATIBLE"}(ResourceErrorCode||(ResourceErrorCode={})),function(SystemErrorCode){SystemErrorCode[SystemErrorCode.OUT_OF_MEMORY=4001]="OUT_OF_MEMORY",SystemErrorCode[SystemErrorCode.GPU_NOT_SUPPORTED=4002]="GPU_NOT_SUPPORTED",SystemErrorCode[SystemErrorCode.WEBGL_NOT_SUPPORTED=4003]="WEBGL_NOT_SUPPORTED",SystemErrorCode[SystemErrorCode.BROWSER_NOT_COMPATIBLE=4004]="BROWSER_NOT_COMPATIBLE"}(SystemErrorCode||(SystemErrorCode={})),function(ConfigErrorCode){ConfigErrorCode[ConfigErrorCode.INVALID_CONFIG=5001]="INVALID_CONFIG",ConfigErrorCode[ConfigErrorCode.MISSING_REQUIRED_PARAM=5002]="MISSING_REQUIRED_PARAM",ConfigErrorCode[ConfigErrorCode.PARAM_OUT_OF_RANGE=5003]="PARAM_OUT_OF_RANGE",ConfigErrorCode[ConfigErrorCode.INVALID_JSON_FORMAT=5004]="INVALID_JSON_FORMAT"}(ConfigErrorCode||(ConfigErrorCode={}));const ERROR_CODE_MAP={[NetworkErrorCode.CONNECTION_FAILED]:{category:ErrorCategory.NETWORK,message:"网络连接失败"},[NetworkErrorCode.REQUEST_TIMEOUT]:{category:ErrorCategory.NETWORK,message:"请求超时"},[NetworkErrorCode.SERVER_ERROR]:{category:ErrorCategory.NETWORK,message:"服务器错误"},[NetworkErrorCode.UNAUTHORIZED]:{category:ErrorCategory.NETWORK,message:"未授权访问"},[OperationErrorCode.UNITY_NOT_INITIALIZED]:{category:ErrorCategory.OPERATION,message:"Unity实例未初始化"},[OperationErrorCode.AVATAR_NOT_LOADED]:{category:ErrorCategory.OPERATION,message:"数字人未加载"},[OperationErrorCode.OPERATION_FAILED]:{category:ErrorCategory.OPERATION,message:"操作执行失败"},[OperationErrorCode.OPERATION_TIMEOUT]:{category:ErrorCategory.OPERATION,message:"操作超时"},[OperationErrorCode.OPERATION_CANCELLED]:{category:ErrorCategory.OPERATION,message:"操作被取消"},[OperationErrorCode.BROADCAST_EQUITY_NOT_EXIST]:{category:ErrorCategory.OPERATION,message:"用户权益额度不存在"},[OperationErrorCode.BROADCAST_EQUITY_NOT_ENOUGH]:{category:ErrorCategory.OPERATION,message:"用户权益额度不足"},[OperationErrorCode.BROADCAST_EQUITY_FREEZE_FAILED]:{category:ErrorCategory.OPERATION,message:"用户权益额度冻结失败"},[ResourceErrorCode.LOAD_FAILED]:{category:ErrorCategory.RESOURCE,message:"资源加载失败"},[ResourceErrorCode.FILE_CORRUPTED]:{category:ErrorCategory.RESOURCE,message:"资源文件损坏"},[ResourceErrorCode.NOT_FOUND]:{category:ErrorCategory.RESOURCE,message:"资源不存在"},[ResourceErrorCode.UNSUPPORTED_FORMAT]:{category:ErrorCategory.RESOURCE,message:"资源格式不支持"},[ResourceErrorCode.VERSION_INCOMPATIBLE]:{category:ErrorCategory.RESOURCE,message:"资源版本不兼容"},[SystemErrorCode.OUT_OF_MEMORY]:{category:ErrorCategory.SYSTEM,message:"内存不足"},[SystemErrorCode.GPU_NOT_SUPPORTED]:{category:ErrorCategory.SYSTEM,message:"GPU不支持"},[SystemErrorCode.WEBGL_NOT_SUPPORTED]:{category:ErrorCategory.SYSTEM,message:"WebGL不支持"},[SystemErrorCode.BROWSER_NOT_COMPATIBLE]:{category:ErrorCategory.SYSTEM,message:"浏览器不兼容"},[ConfigErrorCode.INVALID_CONFIG]:{category:ErrorCategory.CONFIG,message:"配置参数无效"},[ConfigErrorCode.MISSING_REQUIRED_PARAM]:{category:ErrorCategory.CONFIG,message:"必需参数缺失"},[ConfigErrorCode.PARAM_OUT_OF_RANGE]:{category:ErrorCategory.CONFIG,message:"参数值超出范围"},[ConfigErrorCode.INVALID_JSON_FORMAT]:{category:ErrorCategory.CONFIG,message:"JSON格式错误"}};function getErrorInfo(code){return ERROR_CODE_MAP[code]||{category:ErrorCategory.SYSTEM,message:"未知错误"}}class SDKError extends Error{constructor(code,message,originalError){const errorInfo=getErrorInfo(code),finalMessage=message||errorInfo.message;super(finalMessage),this.name="SDKError",this.code=code,this.category=errorInfo.category,this.message=finalMessage,this.timestamp=Date.now(),originalError&&originalError.stack?this.stack=originalError.stack:Error.captureStackTrace&&Error.captureStackTrace(this,SDKError)}toJSON(){return{name:this.name,code:this.code,category:this.category,message:this.message,timestamp:this.timestamp}}static createNetworkError(code,message,originalError){return new SDKError(code,message,originalError)}static createOperationError(code,message,originalError){return new SDKError(code,message,originalError)}static createResourceError(code,message,originalError){return new SDKError(code,message,originalError)}static createSystemError(code,message,originalError){return new SDKError(code,message,originalError)}static createConfigError(code,message,originalError){return new SDKError(code,message,originalError)}static createFromUnityError(unityCode,message){switch(unityCode){case 100:return new SDKError(ResourceErrorCode.LOAD_FAILED,message);case 200:return new SDKError(NetworkErrorCode.CONNECTION_FAILED,message);case 300:case 700:return new SDKError(ConfigErrorCode.INVALID_CONFIG,message);case 400:return new SDKError(OperationErrorCode.OPERATION_FAILED,message);case 500:return new SDKError(SystemErrorCode.OUT_OF_MEMORY,message);case 600:return new SDKError(OperationErrorCode.OPERATION_CANCELLED,message);default:return new SDKError(3001,message||`Unity错误 (错误码: ${unityCode})`)}}}var LogLevel,UnityOperationStatus;!function(LogLevel){LogLevel.DEBUG="debug",LogLevel.INFO="info",LogLevel.WARN="warn",LogLevel.ERROR="error"}(LogLevel||(LogLevel={}));class SimpleLogger{constructor(enableDebug=!1){this.enableDebug=enableDebug}pad(num,len){let s=String(num);for(;s.length<len;)s=`0${s}`;return s}getTimestamp(){const now=new Date;return`[${this.pad(now.getHours(),2)}:${this.pad(now.getMinutes(),2)}:${this.pad(now.getSeconds(),2)}.${this.pad(now.getMilliseconds(),3).slice(0,2)}]`}debug(message,data){this.enableDebug&&console.debug(`[SDK DEBUG]${this.getTimestamp()} ${message}`,data)}info(message,data){console.info(`[SDK INFO]${this.getTimestamp()} ${message}`,data)}warn(message,data){console.warn(`[SDK WARN]${this.getTimestamp()} ${message}`,data)}error(message,error,data){console.error(`[SDK ERROR]${this.getTimestamp()} ${message}`,error,error instanceof SDKError?error.code:null,data)}}!function(UnityOperationStatus){UnityOperationStatus[UnityOperationStatus.SUCCESS=0]="SUCCESS",UnityOperationStatus[UnityOperationStatus.FAILURE=1]="FAILURE",UnityOperationStatus[UnityOperationStatus.TIMEOUT=2]="TIMEOUT",UnityOperationStatus[UnityOperationStatus.CANCELLED=3]="CANCELLED"}(UnityOperationStatus||(UnityOperationStatus={}));const DEFAULT_CONFIG={targetObjectName:"AvatarSDK",timeout:3e4,enableDebugLog:!1,maxRetries:0};class UnityBaseService{get uniqueCallbackName(){return`${this.callbackFunctionName}_${this.instanceId}`}constructor(config){this.pendingCallbacks=new Map;const finalConfig=Object.assign(Object.assign({},DEFAULT_CONFIG),config);this.unityInstance=finalConfig.unityInstance,this.targetObjectName=finalConfig.targetObjectName,this.timeout=finalConfig.timeout||DEFAULT_CONFIG.timeout,this.maxRetries=finalConfig.maxRetries,this.instanceId=finalConfig.instanceId||"default",this.logger=new SimpleLogger(finalConfig.enableDebugLog),this.initializeGlobalCallback(),this.logger.debug("Unity service initialized",{config:finalConfig})}handleCallback(operation,code,message,data){const dataObj=data?JSON.parse(data):void 0;this.logger.warn("[ Received Unity callback ]",{operation:operation,code:code,message:message,data:dataObj,originalData:data});const callback=this.pendingCallbacks.get(operation);if(!callback)return void this.logger.warn(`No pending callback for operation: ${operation}`);callback.timer&&clearTimeout(callback.timer);const response={success:code===UnityOperationStatus.SUCCESS,message:message,errorCode:code,data:dataObj};try{if(code===UnityOperationStatus.SUCCESS)callback.resolve(response),this.logger.debug(`Operation '${operation}' completed successfully`);else{const error=SDKError.createFromUnityError(code,`Unity operation '${operation}' failed: ${message}`);callback.reject(error),this.logger.error(`Operation '${operation}' failed`,error)}}catch(error){this.logger.error(`Error handling callback for operation '${operation}'`,error)}finally{this.pendingCallbacks.delete(operation)}}setupCallback(operation){return this.clearCallback(operation),new Promise((resolve,reject)=>{const timer=setTimeout(()=>{this.pendingCallbacks.delete(operation);const timeoutError=new SDKError(OperationErrorCode.OPERATION_TIMEOUT,`Unity operation '${operation}' timed out after ${this.timeout}ms`);this.logger.error(`Operation '${operation}' timed out`,timeoutError),reject(timeoutError)},this.timeout);this.pendingCallbacks.set(operation,{resolve:resolve,reject:reject,timer:timer,retries:0}),this.logger.debug(`Callback setup for operation '${operation}' with timeout ${this.timeout}ms`)})}clearCallback(operation){const callback=this.pendingCallbacks.get(operation);callback&&(callback.timer&&clearTimeout(callback.timer),this.pendingCallbacks.delete(operation),this.logger.debug(`Cleared callback for operation '${operation}'`))}clearAllCallbacks(){this.pendingCallbacks.forEach(callback=>{callback.timer&&clearTimeout(callback.timer)}),this.pendingCallbacks.clear(),this.logger.debug("Cleared all pending callbacks")}sendMessage(methodName,parameter){if(!this.isUnityAvailable())throw new SDKError(OperationErrorCode.UNITY_NOT_INITIALIZED,"Unity实例未初始化");try{this.logger.warn(`[ Sending message ]: ${this.targetObjectName}.${methodName}`,parameter);const paramString=parameter?JSON.stringify(parameter):"";this.unityInstance.SendMessage(this.targetObjectName,methodName,paramString)}catch(error){const serviceError=new SDKError(OperationErrorCode.OPERATION_FAILED,`Failed to send Unity message: ${methodName}`,error);throw this.logger.error(`Failed to send Unity message: ${methodName}`,serviceError),serviceError}}sendAsyncMessage(methodName,operation,parameter){return __awaiter(this,void 0,void 0,function*(){if(!this.isUnityAvailable())throw new SDKError(OperationErrorCode.UNITY_NOT_INITIALIZED,"Unity实例未初始化");try{const callbackPromise=this.setupCallback(operation),fullParameter=Object.assign(Object.assign({},parameter),{callbackFun:this.uniqueCallbackName,operationType:operation});return this.sendMessage(methodName,fullParameter),yield callbackPromise}catch(error){if(this.clearCallback(operation),this.maxRetries>0&&error instanceof SDKError&&error.code===OperationErrorCode.OPERATION_TIMEOUT)return yield this.retryOperation(operation,methodName,parameter);throw error}})}destroy(){this.clearAllCallbacks(),window[this.uniqueCallbackName]&&(delete window[this.uniqueCallbackName],this.logger.debug(`Global callback unregistered: ${this.uniqueCallbackName}`)),this.logger.info("Unity service destroyed")}isUnityAvailable(){return null!==this.unityInstance&&"function"==typeof this.unityInstance.SendMessage}getPendingCallbackCount(){return this.pendingCallbacks.size}initializeGlobalCallback(){window[this.uniqueCallbackName]=(operation,code,message,data)=>{this.handleCallback(operation,code,message,data)},this.logger.warn(`Global callback registered: ${this.uniqueCallbackName}`)}retryOperation(operation,methodName,parameter){return __awaiter(this,void 0,void 0,function*(){let lastError=null;for(let attempt=0;attempt<=this.maxRetries;attempt++)try{return attempt>0&&(this.logger.debug(`Retrying operation '${operation}', attempt ${attempt}/${this.maxRetries}`),yield new Promise(resolve=>setTimeout(resolve,1e3*Math.pow(2,attempt)))),yield this.sendAsyncMessage(methodName,operation,parameter)}catch(error){if(lastError=error,this.logger.warn(`Operation '${operation}' failed, attempt ${attempt+1}/${this.maxRetries+1}`,error),error instanceof SDKError&&error.code===OperationErrorCode.OPERATION_TIMEOUT&&attempt<this.maxRetries)continue;if(!(error instanceof SDKError&&error.code===OperationErrorCode.OPERATION_TIMEOUT)||attempt>=this.maxRetries)throw error}throw lastError||new SDKError(OperationErrorCode.OPERATION_TIMEOUT,`All retry attempts failed for operation: ${operation}`)})}}var AvatarOperationType,AvatarCameraType;!function(AvatarOperationType){AvatarOperationType.INITIALIZE_AVATAR="initializeAvatar",AvatarOperationType.PLAY_MOTION="playMotion",AvatarOperationType.GET_CURRENT_MOTION="getCurrentMotion",AvatarOperationType.UNLOAD_AVATAR="unloadAvatar",AvatarOperationType.SET_CAMERA="setCamera"}(AvatarOperationType||(AvatarOperationType={})),function(AvatarCameraType){AvatarCameraType.WHOLE="whole",AvatarCameraType.HALF="half",AvatarCameraType.FACE="face"}(AvatarCameraType||(AvatarCameraType={}));class AvatarService extends UnityBaseService{get callbackFunctionName(){return"uniAvatarCallback"}constructor(config){super(config),this.logger.info("Avatar API service initialized")}initializeAvatar(avatarCode_1){return __awaiter(this,arguments,void 0,function*(avatarCode,cameraType=AvatarCameraType.WHOLE){this.logger.info(`Initializing avatar: ${avatarCode} with camera type: ${cameraType}`);try{const result=yield this.sendAsyncMessage("InitializeAvatar",AvatarOperationType.INITIALIZE_AVATAR,{avatarCode:avatarCode,cameraType:cameraType});return this.logger.info("Avatar initialization "+(result.success?"succeeded":"failed")),result}catch(error){throw this.logger.error("Failed to initialize avatar",error),error}})}handleCallback(operation,code,message,data){super.handleCallback(operation,code,message,data)}playMotion(clipCode){return __awaiter(this,void 0,void 0,function*(){this.logger.info(`Playing motion: ${clipCode}`);try{const result=yield this.sendAsyncMessage("PlayMotion",AvatarOperationType.PLAY_MOTION,{clipCode:clipCode});return this.logger.info("Motion play "+(result.success?"succeeded":"failed")),result}catch(error){throw this.logger.error("Failed to play motion",error),error}})}getCurrentMotion(getRemainingTime){return __awaiter(this,void 0,void 0,function*(){var _a,_b;this.logger.info(`Getting current motion info, includeRemainingTime: ${getRemainingTime}`);try{const result=yield this.sendAsyncMessage("GetCurrentMotion",AvatarOperationType.GET_CURRENT_MOTION,{getRemainingTime:getRemainingTime});return this.logger.info("Current motion info retrieved",{motionId:null===(_a=result.data)||void 0===_a?void 0:_a.motionId,remainingTime:null===(_b=result.data)||void 0===_b?void 0:_b.motionRemainingTime}),result}catch(error){throw this.logger.error("Failed to get current motion info",error),error}})}unloadAvatar(){return __awaiter(this,void 0,void 0,function*(){this.logger.info("Unloading avatar");try{const result=yield this.sendAsyncMessage("UnloadAvatar",AvatarOperationType.UNLOAD_AVATAR);return this.logger.info("Avatar unload "+(result.success?"succeeded":"failed")),result}catch(error){throw this.logger.error("Failed to unload avatar",error),error}})}setCamera(cameraType){return __awaiter(this,void 0,void 0,function*(){this.logger.info(`Setting camera type: ${cameraType}`);try{const result=yield this.sendAsyncMessage("SetCamera",AvatarOperationType.SET_CAMERA,{cameraType:cameraType});return this.logger.info("Camera type setting "+(result.success?"succeeded":"failed")),result}catch(error){throw this.logger.error("Failed to set camera type",error),error}})}batchExecute(operations){return __awaiter(this,void 0,void 0,function*(){this.logger.info(`Executing batch operations: ${operations.length} items`);const results=[];for(const operation of operations)try{const method=this[operation.method],result=yield method.apply(this,operation.params);results.push(result)}catch(error){this.logger.error(`Batch operation failed: ${operation.method}`,error),results.push({success:!1,message:`Batch operation failed: ${error.message}`,errorCode:1})}return this.logger.info(`Batch operations completed: ${results.filter(r=>r.success).length}/${results.length} successful`),results})}}const ENV_MAP={dev:{apiBaseUrl:"https://dev.local.zeewain.com"},test:{apiBaseUrl:"https://test.local.zeewain.com"},prod:{apiBaseUrl:"https://ai.zeewain3d.com"}};class ConfigManager{constructor(){this.config=null}static getInstance(){return ConfigManager.instance||(ConfigManager.instance=new ConfigManager),ConfigManager.instance}setConfig(config){this.config=Object.assign(Object.assign({},config),{env:config.env||"prod",containerId:config.containerId||"unity-container"})}updateConfig(config){this.config&&(this.config=Object.assign(Object.assign({},this.config),config))}getConfig(){return this.config}getApiBaseUrl(withApiModule=!0){var _a,_b,_c,_d;return"custom"===(null===(_a=this.config)||void 0===_a?void 0:_a.env)&&(null===(_b=this.config)||void 0===_b?void 0:_b.apiUrl)?`${this.config.apiUrl}${withApiModule?"/api":""}`:(null===(_d=function getEnvConfig(env="dev",withApiModule=!0){const baseUrl=ENV_MAP[env];return baseUrl?{apiBaseUrl:`${baseUrl.apiBaseUrl}${withApiModule?"/api":""}`}:null}((null===(_c=this.config)||void 0===_c?void 0:_c.env)||"prod",withApiModule))||void 0===_d?void 0:_d.apiBaseUrl)||""}reset(){this.config=null}}ConfigManager.instance=null;class UnityLoader{constructor(){this.config=ConfigManager.getInstance().getConfig()}init(){return __awaiter(this,void 0,void 0,function*(){const container=this.createContainer();return yield this.loadUnityLoader(),yield this.createUnityInstance(container)})}createContainer(){const container=document.getElementById(this.config.containerId);if(!container)throw new TypeError(`Avatar container element with ID "${this.config.containerId}" not found`);if(!(container instanceof HTMLDivElement))throw new TypeError("Avatar container element must be a div element");return container.style.position="relative",container}loadUnityLoader(){return new Promise((resolve,reject)=>{if(window.createUnityInstance)return resolve();const script=document.createElement("script");script.src=this.config.loaderUrl,script.async=!0,script.crossOrigin="anonymous",script.onload=()=>{"function"==typeof window.createUnityInstance?resolve():reject(new Error("createUnityInstance function not found"))},script.onerror=error=>{reject(new Error(`Failed to load UnityLoader: ${error}`))},document.head.appendChild(script)})}createUnityInstance(container){return __awaiter(this,void 0,void 0,function*(){let canvas=container.querySelector("#unity-canvas");if(canvas||(canvas=document.createElement("canvas"),canvas.id="unity-canvas",canvas.style.width="100%",canvas.style.height="100%",container.appendChild(canvas)),/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)){const meta=document.createElement("meta");meta.name="viewport",meta.content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=no, shrink-to-fit=yes",document.head.appendChild(meta),canvas.style.width="100%",canvas.style.height="100%",canvas.style.position="fixed"}if("function"==typeof window.createUnityInstance){return yield window.createUnityInstance(canvas,{dataUrl:this.config.dataUrl,frameworkUrl:this.config.frameworkUrl,codeUrl:this.config.codeUrl,companyName:"广州紫为云科技有限公司",productName:"数字人SDK",productVersion:"1.0"},progress=>{this.config.onProgress&&this.config.onProgress(progress)})}throw new TypeError("createUnityInstance is not defined on window.")})}}function getLines(onLine){let buffer,position,fieldLength,discardTrailingNewline=!1;return function onChunk(arr){void 0===buffer?(buffer=arr,position=0,fieldLength=-1):buffer=function concat(a,b){const res=new Uint8Array(a.length+b.length);return res.set(a),res.set(b,a.length),res}(buffer,arr);const bufLength=buffer.length;let lineStart=0;for(;position<bufLength;){discardTrailingNewline&&(10===buffer[position]&&(lineStart=++position),discardTrailingNewline=!1);let lineEnd=-1;for(;position<bufLength&&-1===lineEnd;++position)switch(buffer[position]){case 58:-1===fieldLength&&(fieldLength=position-lineStart);break;case 13:discardTrailingNewline=!0;case 10:lineEnd=position}if(-1===lineEnd)break;onLine(buffer.subarray(lineStart,lineEnd),fieldLength),lineStart=position,fieldLength=-1}lineStart===bufLength?buffer=void 0:0!==lineStart&&(buffer=buffer.subarray(lineStart),position-=lineStart)}}var __rest=function(s,e){var t={};for(var p in s)Object.prototype.hasOwnProperty.call(s,p)&&e.indexOf(p)<0&&(t[p]=s[p]);if(null!=s&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(p=Object.getOwnPropertySymbols(s);i<p.length;i++)e.indexOf(p[i])<0&&Object.prototype.propertyIsEnumerable.call(s,p[i])&&(t[p[i]]=s[p[i]])}return t};function fetchEventSource(input,_a){var{signal:inputSignal,headers:inputHeaders,onopen:inputOnOpen,onmessage:onmessage,onclose:onclose,onerror:onerror,openWhenHidden:openWhenHidden,fetch:inputFetch}=_a,rest=__rest(_a,["signal","headers","onopen","onmessage","onclose","onerror","openWhenHidden","fetch"]);return new Promise((resolve,reject)=>{const headers=Object.assign({},inputHeaders);let curRequestController;function onVisibilityChange(){curRequestController.abort(),document.hidden||create()}headers.accept||(headers.accept="text/event-stream"),openWhenHidden||document.addEventListener("visibilitychange",onVisibilityChange);let retryInterval=1e3,retryTimer=0;function dispose(){document.removeEventListener("visibilitychange",onVisibilityChange),window.clearTimeout(retryTimer),curRequestController.abort()}null==inputSignal||inputSignal.addEventListener("abort",()=>{dispose(),resolve()});const fetch=null!=inputFetch?inputFetch:window.fetch,onopen=null!=inputOnOpen?inputOnOpen:defaultOnOpen;async function create(){var _a;curRequestController=new AbortController;try{const response=await fetch(input,Object.assign(Object.assign({},rest),{headers:headers,signal:curRequestController.signal}));await onopen(response),await async function getBytes(stream,onChunk){const reader=stream.getReader();let result;for(;!(result=await reader.read()).done;)onChunk(result.value)}(response.body,getLines(function getMessages(onId,onRetry,onMessage){let message={data:"",event:"",id:"",retry:void 0};const decoder=new TextDecoder;return function onLine(line,fieldLength){if(0===line.length)null==onMessage||onMessage(message),message={data:"",event:"",id:"",retry:void 0};else if(fieldLength>0){const field=decoder.decode(line.subarray(0,fieldLength)),valueOffset=fieldLength+(32===line[fieldLength+1]?2:1),value=decoder.decode(line.subarray(valueOffset));switch(field){case"data":message.data=message.data?message.data+"\n"+value:value;break;case"event":message.event=value;break;case"id":onId(message.id=value);break;case"retry":const retry=parseInt(value,10);isNaN(retry)||onRetry(message.retry=retry)}}}}(id=>{id?headers["last-event-id"]=id:delete headers["last-event-id"]},retry=>{retryInterval=retry},onmessage))),null==onclose||onclose(),dispose(),resolve()}catch(err){if(!curRequestController.signal.aborted)try{const interval=null!==(_a=null==onerror?void 0:onerror(err))&&void 0!==_a?_a:retryInterval;window.clearTimeout(retryTimer),retryTimer=window.setTimeout(create,interval)}catch(innerErr){dispose(),reject(innerErr)}}}create()})}function defaultOnOpen(response){const contentType=response.headers.get("content-type");if(!(null==contentType?void 0:contentType.startsWith("text/event-stream")))throw new Error(`Expected content-type to be text/event-stream, Actual: ${contentType}`)}var BroadcastOperationType,BroadcastType,BroadcastTaskStatus;!function(BroadcastOperationType){BroadcastOperationType.START_BROADCAST="startBroadcast",BroadcastOperationType.PAUSE_BROADCAST="pauseBroadcast",BroadcastOperationType.RESUME_BROADCAST="resumeBroadcast",BroadcastOperationType.STOP_BROADCAST="stopBroadcast",BroadcastOperationType.APPEND_BROADCAST="appendBroadcast"}(BroadcastOperationType||(BroadcastOperationType={})),function(BroadcastType){BroadcastType.TEXT="text",BroadcastType.AUDIO="audio"}(BroadcastType||(BroadcastType={})),function(BroadcastTaskStatus){BroadcastTaskStatus.PENDING="pending",BroadcastTaskStatus.REQUESTING="requesting",BroadcastTaskStatus.COMPLETED="completed",BroadcastTaskStatus.FAILED="failed",BroadcastTaskStatus.CANCELLED="cancelled"}(BroadcastTaskStatus||(BroadcastTaskStatus={}));class BroadcastService extends UnityBaseService{constructor(config){super(config),this.callbacks={},this.taskQueue=[],this.taskSequence=0,this.currentSendingSequence=0,this.isBroadcastingAudio=!1,this.hasReceivedAudio=!1,this.queueProcessTimer=null,this.broadcastCompletedCount=0,this.callbacks=config.callbacks||{},this.logger.info("Broadcast service initialized",{config:config})}handleCallback(operation,code,message,data){var _a,_b,_c,_d,_e,_f,_g,_h,_j,_k,_l;const{isBroadcastCompleted:isBroadcastCompleted}=JSON.parse(data||"{}");if(super.handleCallback(operation,code,message,data),0===code)switch(operation){case BroadcastOperationType.START_BROADCAST:if(isBroadcastCompleted){this.broadcastCompletedCount++;const status=this.getStatus();0===(null===(_a=status.queueInfo)||void 0===_a?void 0:_a.pendingTasks)&&0===(null===(_b=status.queueInfo)||void 0===_b?void 0:_b.requestingTasks)&&(null===(_c=status.queueInfo)||void 0===_c?void 0:_c.completedTasks)===this.broadcastCompletedCount&&(this.isBroadcastingAudio=!1,this.hasReceivedAudio=!1,this.currentSendingSequence=0,this.cleanupCompletedTasks(),this.logger.warn("Broadcast all completed")),null===(_e=(_d=this.callbacks).onFinish)||void 0===_e||_e.call(_d)}break;case BroadcastOperationType.PAUSE_BROADCAST:null===(_g=(_f=this.callbacks).onPause)||void 0===_g||_g.call(_f),this.logger.debug("Broadcast paused callback triggered");break;case BroadcastOperationType.RESUME_BROADCAST:null===(_j=(_h=this.callbacks).onResume)||void 0===_j||_j.call(_h),this.logger.debug("Broadcast resumed callback triggered");break;case BroadcastOperationType.STOP_BROADCAST:null===(_l=(_k=this.callbacks).onStop)||void 0===_l||_l.call(_k),this.logger.debug("Broadcast stopped callback triggered")}else{const error=SDKError.createFromUnityError(code,`Unity operation '${operation}' failed: ${message}`);this.handleError(error)}}startBroadcast(params,isAppend){return __awaiter(this,void 0,void 0,function*(){var _a,_b;this.logger.info(`Starting broadcast: ${params.type}`,{humanCode:params.humanCode,text:params.text,audioUrl:params.audioUrl,isAppend:isAppend,queueLength:this.taskQueue.length}),this.validateBroadcastParams(params),isAppend||(yield this.stopBroadcast(),this.taskSequence=0,this.currentSendingSequence=0,this.sendMessage("StartBroadcast",{callbackFun:this.uniqueCallbackName,operationType:BroadcastOperationType.START_BROADCAST,motionList:params.motionList,motionPlayMode:params.motionPlayMode}),null===(_b=(_a=this.callbacks).onStart)||void 0===_b||_b.call(_a),this.isBroadcastingAudio=!0);const task=this.createBroadcastTask(params);this.addTaskToQueue(task),this.logger.debug("Broadcast task created and queued",{taskId:task.id,sequence:task.sequence,isAppend:isAppend})})}pauseBroadcast(resetIdle){return __awaiter(this,void 0,void 0,function*(){this.logger.info("Pausing broadcast");try{yield this.sendAsyncMessage("PauseBroadcast",BroadcastOperationType.PAUSE_BROADCAST,{resetIdle:resetIdle}),this.logger.info("Broadcast paused successfully")}catch(error){throw this.logger.error("Failed to pause broadcast",error),error}})}resumeBroadcast(){return __awaiter(this,void 0,void 0,function*(){this.logger.info("Resuming broadcast");try{yield this.sendAsyncMessage("ResumeBroadcast",BroadcastOperationType.RESUME_BROADCAST,{}),this.logger.info("Broadcast resumed successfully")}catch(error){throw this.logger.error("Failed to resume broadcast",error),error}})}stopBroadcast(){return __awaiter(this,void 0,void 0,function*(){this.logger.info("Stopping broadcast and clearing queue",{queueLength:this.taskQueue.length}),this.cancelAllTasks(),yield new Promise(resolve=>setTimeout(resolve,100)),this.isBroadcastingAudio=!1,this.hasReceivedAudio=!1,this.broadcastCompletedCount=0;try{yield this.sendAsyncMessage("StopBroadcast",BroadcastOperationType.STOP_BROADCAST,{}),this.logger.info("Broadcast stopped successfully")}catch(error){throw this.logger.error("Failed to stop broadcast",error),error}})}updateCallbacks(callbacks){this.callbacks=Object.assign(Object.assign({},this.callbacks),callbacks),this.logger.debug("Broadcast callbacks updated")}getStatus(){const pendingTasks=this.taskQueue.filter(t=>t.status===BroadcastTaskStatus.PENDING).length,requestingTasks=this.taskQueue.filter(t=>t.status===BroadcastTaskStatus.REQUESTING).length,completedTasks=this.taskQueue.filter(t=>t.status===BroadcastTaskStatus.COMPLETED).length,failedTasks=this.taskQueue.filter(t=>t.status===BroadcastTaskStatus.FAILED).length,totalPendingResponses=this.taskQueue.reduce((sum,t)=>sum+t.pendingResponses.length,0),currentSendingSequence=this.currentSendingSequence,isGeneratingAudio=pendingTasks+requestingTasks>0;return{isActive:this.isBroadcastingAudio||isGeneratingAudio,isGeneratingAudio:isGeneratingAudio,hasReceivedAudio:this.hasReceivedAudio,queueInfo:{totalTasks:this.taskQueue.length,pendingTasks:pendingTasks,requestingTasks:requestingTasks,completedTasks:completedTasks,failedTasks:failedTasks,totalPendingResponses:totalPendingResponses,currentSendingSequence:currentSendingSequence}}}destroy(){this.clearQueueProcessTimer(),this.cancelAllTasks(),super.destroy(),this.logger.info("Broadcast service destroyed")}createBroadcastTask(params){const task={id:`[${params.type===BroadcastType.TEXT?"TEXT":"AUDIO"}]-(${params.type===BroadcastType.TEXT?params.text:params.audioUrl})_${Math.random().toString(36).substring(2,9)}`,sequence:++this.taskSequence,params:params,status:BroadcastTaskStatus.PENDING,controller:new AbortController,pendingResponses:[],isGenerationComplete:!1,createdAt:new Date};return this.logger.debug("Created broadcast task",{taskId:task.id,sequence:task.sequence}),task}addTaskToQueue(task){this.taskQueue.push(task),this.logger.debug("Task added to queue",{taskId:task.id,params:task.params,queueLength:this.taskQueue.length});this.taskQueue.some(t=>t.status===BroadcastTaskStatus.REQUESTING)?this.logger.debug("Task queued, waiting for previous task to complete",{taskId:task.id,pendingTasks:this.taskQueue.filter(t=>t.status===BroadcastTaskStatus.PENDING).length}):this.startTaskRequest(task),this.processTaskQueue()}processTaskQueue(){this.queueProcessTimer||(this.queueProcessTimer=setInterval(()=>{this.processTaskQueueStep()},100)),this.processTaskQueueStep()}processTaskQueueStep(){const nextTask=this.taskQueue.find(task=>task.sequence===this.currentSendingSequence+1&&task.status!==BroadcastTaskStatus.PENDING&&task.pendingResponses.length>0);nextTask?this.sendNextResponse(nextTask):this.logger.debug("No next task to process",{currentSendingSequence:this.currentSendingSequence,taskQueue:this.taskQueue});0===this.taskQueue.filter(task=>task.status===BroadcastTaskStatus.PENDING||task.status===BroadcastTaskStatus.REQUESTING).length&&this.clearQueueProcessTimer()}startTaskRequest(task){return __awaiter(this,void 0,void 0,function*(){var _a;task.status=BroadcastTaskStatus.REQUESTING,this.logger.debug("Starting task request",{taskId:task.id});try{const apiUrl=`${ConfigManager.getInstance().getApiBaseUrl(!0)}${this.getBroadcastApiPath(task.params.type)}`,requestBody={humanCode:task.params.humanCode,speed:task.params.speed,volume:task.params.volume>=0?100*task.params.volume:void 0,isSubtitle:task.params.isSubtitle,audioDrivenVersion:ConfigManager.getInstance().getConfig().audioDrivenVersion};task.params.type===BroadcastType.TEXT?(requestBody.text=task.params.text,requestBody.voiceCode=task.params.voiceCode):task.params.type===BroadcastType.AUDIO&&(requestBody.text=task.params.text,requestBody.audioUrl=task.params.audioUrl),fetchEventSource(apiUrl,{method:"POST",headers:{"Content-Type":"application/json",x_auth_token:(null===(_a=ConfigManager.getInstance().getConfig())||void 0===_a?void 0:_a.token)||""},body:JSON.stringify(requestBody),signal:task.controller.signal,openWhenHidden:!0,onopen:response=>__awaiter(this,void 0,void 0,function*(){if(!response.ok){const error=this.createHttpError(response.status,response.statusText,task.id);throw this.handleTaskError(task,error),error}}),onmessage:event=>{this.handleTaskResponse(task,event.data)},onclose:()=>{this.handleTaskClose(task)},onerror:error=>{if(error instanceof SDKError)throw error;this.logger.error("broadcast onerror",error);const sdkError=this.convertToSDKError(error,task.id);throw this.handleTaskError(task,sdkError),sdkError}})}catch(error){const sdkError=this.convertToSDKError(error,task.id);this.handleTaskError(task,sdkError)}})}handleTaskResponse(task,data){try{const response=JSON.parse(data);if(0!==response.code){const error=this.createBroadcastError(response.code,response.message);return void this.handleTaskError(task,error)}if(response.data){this.hasReceivedAudio=!0,task.params.type===BroadcastType.AUDIO&&task.params.audioUrl&&!response.data.voiceUrl&&(response.data.voiceUrl=task.params.audioUrl);const{voiceUrl:voiceUrl,mouthShapeUrl:mouthShapeUrl}=response.data;voiceUrl&&!voiceUrl.startsWith("http")&&(response.data.voiceUrl=`${ConfigManager.getInstance().getApiBaseUrl(!1)}${voiceUrl}`),mouthShapeUrl&&!mouthShapeUrl.startsWith("http")&&(response.data.mouthShapeUrl=`${ConfigManager.getInstance().getApiBaseUrl(!1)}${mouthShapeUrl}`),task.pendingResponses.push(response),this.logger.debug("Response added to task",{taskId:task.id,response:response,pendingCount:task.pendingResponses.length}),response.data.done&&(task.isGenerationComplete=!0,this.logger.debug("Task generation completed",{taskId:task.id,totalResponses:task.pendingResponses.length}))}}catch(error){this.handleTaskError(task,new SDKError(OperationErrorCode.OPERATION_FAILED,"string"==typeof error?error:error.message||"播报服务错误"))}}sendNextResponse(task){var _a;if(0===task.pendingResponses.length)return;const response=task.pendingResponses.shift();this.logger.debug("Sending response to Unity",{taskId:task.id,remainingResponses:task.pendingResponses.length,voiceUrl:null===(_a=response.data)||void 0===_a?void 0:_a.voiceUrl}),this.sendMessage("AppendBroadcast",{response:response,callbackFun:this.uniqueCallbackName,operationType:BroadcastOperationType.APPEND_BROADCAST}),task.isGenerationComplete&&0===task.pendingResponses.length&&(task.status=BroadcastTaskStatus.COMPLETED,this.currentSendingSequence=task.sequence,this.logger.debug("Task completed",{taskId:task.id}))}handleTaskClose(task){return __awaiter(this,void 0,void 0,function*(){this.logger.debug("Task stream closed",{taskId:task.id}),task.status!==BroadcastTaskStatus.FAILED&&task.status!==BroadcastTaskStatus.CANCELLED&&(task.isGenerationComplete||task.status!==BroadcastTaskStatus.REQUESTING||(task.isGenerationComplete=!0),this.startNextPendingTask())})}handleTaskError(task,error){var _a,_b;task.status!==BroadcastTaskStatus.FAILED&&task.status!==BroadcastTaskStatus.CANCELLED&&(task.status=BroadcastTaskStatus.FAILED,task.error=error,this.logger.error(`Task failed - ${task.id}`,error),null===(_b=(_a=this.callbacks).onError)||void 0===_b||_b.call(_a,error),this.cancelAllTasks(),this.isBroadcastingAudio=!1,this.hasReceivedAudio=!1,this.currentSendingSequence=0,this.clearQueueProcessTimer(),this.logger.debug("Task failed, all tasks cancelled and broadcast state reset"))}cleanupCompletedTasks(){const beforeLength=this.taskQueue.length;this.taskQueue=this.taskQueue.filter(task=>task.status!==BroadcastTaskStatus.COMPLETED&&task.status!==BroadcastTaskStatus.FAILED&&task.status!==BroadcastTaskStatus.CANCELLED);const removedCount=beforeLength-this.taskQueue.length;removedCount>0&&this.logger.debug("Cleaned up completed tasks",{removedCount:removedCount,remainingTasks:this.taskQueue.length})}cancelAllTasks(){for(const task of this.taskQueue)task.status===BroadcastTaskStatus.REQUESTING?(task.controller.abort(),task.status=BroadcastTaskStatus.CANCELLED,this.logger.debug("Task aborted",{taskId:task.id,previousStatus:"requesting"})):task.status===BroadcastTaskStatus.PENDING&&(task.status=BroadcastTaskStatus.CANCELLED,this.logger.debug("Task cancelled",{taskId:task.id,previousStatus:"pending"}));this.taskQueue=[],this.taskSequence=0,this.currentSendingSequence=0,this.logger.debug("All tasks cancelled and queue cleared")}get callbackFunctionName(){return"uniBroadcastCallback"}getBroadcastApiPath(type){switch(type){case BroadcastType.TEXT:return"/aiep-openapi/avatar-interaction/v1/broadcast/text";case BroadcastType.AUDIO:return"/aiep-openapi/avatar-interaction/v1/broadcast/audio";default:throw new SDKError(ConfigErrorCode.INVALID_CONFIG,`未知的播报类型: ${type}`)}}validateBroadcastParams(params){if(params.type===BroadcastType.TEXT){if(!params.text||!params.voiceCode)throw new SDKError(ConfigErrorCode.MISSING_REQUIRED_PARAM,"文本播报需要提供text和voiceCode参数")}else if(params.type===BroadcastType.AUDIO&&!params.audioUrl)throw new SDKError(ConfigErrorCode.MISSING_REQUIRED_PARAM,"自定义音频播报需要提供audioUrl参数")}handleError(error){var _a,_b;this.logger.error("Broadcast error occurred",error),null===(_b=(_a=this.callbacks).onError)||void 0===_b||_b.call(_a,error)}createBroadcastError(errorCode,errorMessage){switch(errorCode){case 14001:return new SDKError(OperationErrorCode.BROADCAST_EQUITY_NOT_EXIST,"用户权益额度不存在");case 14002:return new SDKError(OperationErrorCode.BROADCAST_EQUITY_NOT_ENOUGH,"用户权益额度不足");case 14003:return new SDKError(OperationErrorCode.BROADCAST_EQUITY_FREEZE_FAILED,"用户权益额度冻结失败");default:return new SDKError(OperationErrorCode.OPERATION_FAILED,`${errorMessage}(${errorCode})`||`播报服务错误(${errorCode})`)}}startNextPendingTask(){const nextPendingTask=this.taskQueue.filter(t=>t.status===BroadcastTaskStatus.PENDING).sort((a,b)=>a.sequence-b.sequence)[0];nextPendingTask?(this.logger.debug("Starting next pending task",{taskId:nextPendingTask.id,sequence:nextPendingTask.sequence}),this.startTaskRequest(nextPendingTask)):this.logger.debug("No pending tasks to start")}clearQueueProcessTimer(){this.queueProcessTimer&&(clearInterval(this.queueProcessTimer),this.queueProcessTimer=null,this.logger.debug("Queue process timer cleared"))}createHttpError(status,statusText,taskId){switch(this.logger.warn(`HTTP error occurred - Task: ${taskId}, Status: ${status}`,{status:status,statusText:statusText}),status){case 401:return new SDKError(NetworkErrorCode.UNAUTHORIZED,`Token 已过期或无效,请重新授权 (HTTP ${status})`);case 403:return new SDKError(NetworkErrorCode.UNAUTHORIZED,`无权限访问该资源 (HTTP ${status})`);case 404:return new SDKError(NetworkErrorCode.SERVER_ERROR,`请求的资源不存在 (HTTP ${status})`);case 500:case 502:case 503:case 504:return new SDKError(NetworkErrorCode.SERVER_ERROR,`服务器错误,请稍后重试 (HTTP ${status})`);default:return new SDKError(NetworkErrorCode.CONNECTION_FAILED,`网络请求失败: ${statusText||"Unknown Error"} (HTTP ${status})`)}}convertToSDKError(error,taskId){if(error instanceof SDKError)return error;if(error instanceof Error){const errorMessage=error.message.toLowerCase();if(errorMessage.includes("timeout")||errorMessage.includes("timed out"))return new SDKError(NetworkErrorCode.REQUEST_TIMEOUT,`请求超时 - Task: ${taskId}`,error);if(errorMessage.includes("network")||errorMessage.includes("fetch")||errorMessage.includes("connection")){const hint=errorMessage.includes("failed to fetch")?"(可能是 Token 过期或 CORS 跨域问题,请检查 Token 是否有效)":"";return new SDKError(NetworkErrorCode.CONNECTION_FAILED,`网络请求失败 - Task: ${taskId}: ${error.message}${hint}`,error)}return errorMessage.includes("abort")||errorMessage.includes("cancel")?new SDKError(OperationErrorCode.OPERATION_CANCELLED,`操作已取消 - Task: ${taskId}`,error):new SDKError(OperationErrorCode.OPERATION_FAILED,`播报任务执行失败 - Task: ${taskId}: ${error.message}`,error)}const errorMessage=String(error);return new SDKError(OperationErrorCode.OPERATION_FAILED,`播报任务执行失败 - Task: ${taskId}: ${errorMessage}`)}}class ZEEAvatarLoader{constructor(){this.apiService=null,this.unityInstance=null,this.loader=new UnityLoader}init(){return __awaiter(this,void 0,void 0,function*(){return this.unityInstance=yield this.loader.init(),this.initGlobalConfig(),this.apiService=new AvatarService({unityInstance:this.unityInstance}),this.apiService})}updateToken(token){const currentConfig=ConfigManager.getInstance().getConfig();currentConfig&&(ConfigManager.getInstance().setConfig(Object.assign(Object.assign({},currentConfig),{token:token})),this.initGlobalConfig())}getAPI(){return this.apiService}getInstance(){return this.unityInstance}getContainerId(){return ConfigManager.getInstance().getConfig().containerId}destroy(){if(this.unityInstance){const container=document.getElementById(this.getContainerId());if(container){const canvas=container.querySelector("#unity-canvas");canvas&&canvas.remove()}"function"==typeof this.unityInstance.Quit&&this.unityInstance.Quit(),this.unityInstance=null,this.apiService=null}ConfigManager.getInstance().reset()}initGlobalConfig(){const config=ConfigManager.getInstance().getConfig(),{assetsFrom:assetsFrom}=config,globalParams={token:null==config?void 0:config.token,apiBaseUrl:ConfigManager.getInstance().getApiBaseUrl(!1),idleMotionList:null==config?void 0:config.idleMotionList};if(this.unityInstance.SendMessage("AvatarSDK","InitializeConfig",JSON.stringify(globalParams)),console.warn("[ Send Unity message ]: AvatarSDK.InitializeConfig",globalParams),"cloud"!==assetsFrom){const assetModuleParams={isZip:!0,assetBundlePath:null==config?void 0:config.assetsUrl};this.unityInstance.SendMessage("AvatarSDK","InitAssetBundleModule",JSON.stringify(assetModuleParams)),console.warn("[ Send Unity message ]: AvatarSDK.InitAssetBundleModule",assetModuleParams)}}}const SDK_VERSION="2.2.1";class ZEEAvatarSDK{constructor(config){var _a;this.loader=null,this.avatarService=null,this.broadcastService=null,this.unityInstance=null,this.isInitialized=!1,this.instanceId=function generateUniqueId(){return`${Date.now()}_${Math.random().toString(36).substring(2,9)}`}(),ConfigManager.getInstance().setConfig(config),this.logger=new SimpleLogger(null===(_a=ConfigManager.getInstance().getConfig().enableDebugLog)||void 0===_a||_a),this.logger.info("SDK版本","2.2.1")}initializeAvatar(avatarCode_1){return __awaiter(this,arguments,void 0,function*(avatarCode,cameraType=AvatarCameraType.WHOLE){var _a;if(this.isInitialized)throw new SDKError(OperationErrorCode.OPERATION_FAILED,"SDK已经初始化,请勿重复初始化");try{const config=ConfigManager.getInstance().getConfig();this.loader=new ZEEAvatarLoader,yield this.loader.init(),this.unityInstance=this.loader.getInstance(),this.avatarService=new AvatarService({unityInstance:this.unityInstance,instanceId:this.instanceId,enableDebugLog:config.enableDebugLog,timeout:config.operationTimeout}),this.broadcastService=new BroadcastService({unityInstance:this.unityInstance,instanceId:this.instanceId,callbacks:config.broadcastCallbacks,enableDebugLog:config.enableDebugLog,timeout:config.operationTimeout});const result=yield this.avatarService.initializeAvatar(avatarCode,cameraType);if(result.success){const audioDrivenVersion=null===(_a=result.data)||void 0===_a?void 0:_a.audioDrivenVersion;if(config.audioDrivenVersion=audioDrivenVersion,this.logger.info("AudioDrivenVersion",audioDrivenVersion),audioDrivenVersion){if(!function compareVersionCompatibility(version1,version2){const getMajorMinor=version=>{const parts=version.split(".");return parts.length<2?version:`${parts[0]}.${parts[1]}`};return getMajorMinor(version1)===getMajorMinor(version2)}("2.2.1",audioDrivenVersion))throw this.destroy(),new SDKError(ResourceErrorCode.VERSION_INCOMPATIBLE,`资源版本不兼容: SDK版本为 2.2.1,WebGL资源版本为 ${audioDrivenVersion},前两位版本号必须一致`);this.logger.info("版本兼容性检查通过",`SDK: 2.2.1, 资源: ${audioDrivenVersion}`)}this.isInitialized=!0}return result}catch(error){throw new SDKError(OperationErrorCode.OPERATION_FAILED,`数字人初始化失败: ${error instanceof Error?error.message:String(error)}`)}})}updateToken(token){ConfigManager.getInstance().updateConfig({token:token}),this.loader&&this.loader.updateToken(token)}playMotion(clipCode){return __awaiter(this,void 0,void 0,function*(){return this.ensureInitialized(),yield this.avatarService.playMotion(clipCode)})}getCurrentMotion(){return __awaiter(this,arguments,void 0,function*(getRemainingTime=!1){return this.ensureInitialized(),yield this.avatarService.getCurrentMotion(getRemainingTime)})}unloadAvatar(){return __awaiter(this,void 0,void 0,function*(){this.ensureInitialized();try{const result=yield this.avatarService.unloadAvatar();return result.success&&(this.isInitialized=!1),result}catch(error){throw new SDKError(OperationErrorCode.OPERATION_FAILED,`数字人卸载失败: ${error instanceof Error?error.message:String(error)}`)}})}setCamera(cameraType){return __awaiter(this,void 0,void 0,function*(){return this.ensureInitialized(),yield this.avatarService.setCamera(cameraType)})}startBroadcast(params,isAppend){return __awaiter(this,void 0,void 0,function*(){return this.ensureInitialized(),yield this.broadcastService.startBroadcast(params,isAppend)})}startStreamBroadcast(params,forceRestart){return __awaiter(this,void 0,void 0,function*(){this.ensureInitialized();const status=this.broadcastService.getStatus(),isAppend=status.isActive;return this.logger.debug("startStreamBroadcast",{isActive:status.isActive,isAppend:isAppend}),yield this.broadcastService.startBroadcast(params,!forceRestart&&isAppend)})}pauseBroadcast(resetIdle){return __awaiter(this,void 0,void 0,function*(){return this.ensureInitialized(),yield this.broadcastService.pauseBroadcast(resetIdle)})}resumeBroadcast(){return __awaiter(this,void 0,void 0,function*(){return this.ensureInitialized(),yield this.broadcastService.resumeBroadcast()})}stopBroadcast(){return __awaiter(this,void 0,void 0,function*(){return this.ensureInitialized(),yield this.broadcastService.stopBroadcast()})}updateBroadcastCallbacks(callbacks){this.ensureInitialized(),this.broadcastService.updateCallbacks(callbacks)}getBroadcastStatus(){return this.ensureInitialized(),this.broadcastService.getStatus()}destroy(){try{this.broadcastService&&(this.broadcastService.destroy(),this.broadcastService=null),this.avatarService&&(this.avatarService.destroy(),this.avatarService=null),this.loader&&(this.loader.destroy(),this.loader=null),this.unityInstance=null,this.isInitialized=!1}catch(error){console.error("SDK销毁过程中发生错误:",error)}}getInstanceId(){return this.instanceId}isSDKInitialized(){return this.isInitialized}isAvatarInitialized(){return this.isInitialized}getConfig(){const config=ConfigManager.getInstance().getConfig();return config?Object.assign({},config):null}ensureInitialized(){if(!this.isInitialized)throw new SDKError(OperationErrorCode.UNITY_NOT_INITIALIZED,"Avatar尚未初始化,请先调用initializeAvatar()方法")}}export{AvatarCameraType,AvatarOperationType,AvatarService,BroadcastOperationType,BroadcastService,BroadcastType,ConfigErrorCode,ERROR_CODE_MAP,ErrorCategory,NetworkErrorCode,OperationErrorCode,ResourceErrorCode,SDKError,SDK_VERSION,SystemErrorCode,UnityOperationStatus,ZEEAvatarLoader,ZEEAvatarSDK,getErrorInfo};
|