id-scanner-lib 1.6.3 → 1.6.5
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/LICENSE +1 -1
- package/dist/id-scanner-lib.esm.js +153 -52
- package/dist/id-scanner-lib.esm.js.map +1 -1
- package/dist/id-scanner-lib.js +153 -52
- package/dist/id-scanner-lib.js.map +1 -1
- package/package.json +1 -1
- package/src/core/config.ts +29 -11
- package/src/core/logger.ts +27 -3
- package/src/core/module-manager.ts +9 -0
- package/src/index.ts +1 -0
- package/src/modules/face/face-detector.ts +1 -0
- package/src/modules/face/index.ts +1 -0
- package/src/modules/face/liveness-detector.ts +1 -0
- package/src/modules/id-card/id-card-detector.ts +98 -35
- package/src/modules/id-card/index.ts +1 -0
- package/src/modules/id-card/ocr-processor.ts +3 -3
- package/src/modules/qrcode/index.ts +1 -0
- package/src/utils/camera.ts +16 -2
- package/src/utils/resource-manager.ts +2 -2
- package/src/utils/worker.ts +4 -4
package/package.json
CHANGED
package/src/core/config.ts
CHANGED
|
@@ -18,11 +18,38 @@ export class ConfigManager {
|
|
|
18
18
|
/** 配置变更回调 */
|
|
19
19
|
private changeCallbacks: Map<string, Array<(value: any, oldValue: any) => void>> = new Map();
|
|
20
20
|
|
|
21
|
+
/** 初始化状态 */
|
|
22
|
+
private initialized = false;
|
|
23
|
+
|
|
21
24
|
/**
|
|
22
25
|
* 私有构造函数
|
|
23
26
|
*/
|
|
24
27
|
private constructor() {
|
|
25
28
|
// 设置默认配置
|
|
29
|
+
this._resetDefaults();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 获取单例实例
|
|
34
|
+
*/
|
|
35
|
+
public static getInstance(): ConfigManager {
|
|
36
|
+
if (!ConfigManager.instance) {
|
|
37
|
+
ConfigManager.instance = new ConfigManager();
|
|
38
|
+
}
|
|
39
|
+
return ConfigManager.instance;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 重置单例实例(主要用于测试)
|
|
44
|
+
*/
|
|
45
|
+
public static resetInstance(): void {
|
|
46
|
+
ConfigManager.instance = undefined as any;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* 重置为默认配置
|
|
51
|
+
*/
|
|
52
|
+
private _resetDefaults(): void {
|
|
26
53
|
this.config = {
|
|
27
54
|
debug: false,
|
|
28
55
|
logLevel: 'info',
|
|
@@ -38,16 +65,7 @@ export class ConfigManager {
|
|
|
38
65
|
useCache: true
|
|
39
66
|
}
|
|
40
67
|
};
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 获取单例实例
|
|
45
|
-
*/
|
|
46
|
-
public static getInstance(): ConfigManager {
|
|
47
|
-
if (!ConfigManager.instance) {
|
|
48
|
-
ConfigManager.instance = new ConfigManager();
|
|
49
|
-
}
|
|
50
|
-
return ConfigManager.instance;
|
|
68
|
+
this.initialized = true;
|
|
51
69
|
}
|
|
52
70
|
|
|
53
71
|
/**
|
|
@@ -95,7 +113,7 @@ export class ConfigManager {
|
|
|
95
113
|
reset(): void {
|
|
96
114
|
const oldConfig = { ...this.config };
|
|
97
115
|
|
|
98
|
-
//
|
|
116
|
+
// 使用私有 reset 方法重建默认配置
|
|
99
117
|
this.config = {
|
|
100
118
|
debug: false,
|
|
101
119
|
logLevel: 'info',
|
package/src/core/logger.ts
CHANGED
|
@@ -200,6 +200,18 @@ export class RemoteLogHandler implements LogHandler {
|
|
|
200
200
|
const entriesToSend = [...this.queue];
|
|
201
201
|
this.queue = [];
|
|
202
202
|
|
|
203
|
+
// 防止在 fetch 失败时无限重试
|
|
204
|
+
const sendCount = (this as any)._sendCount || 0;
|
|
205
|
+
(this as any)._sendCount = sendCount + 1;
|
|
206
|
+
|
|
207
|
+
// 如果发送次数过多,停止发送以防止无限循环
|
|
208
|
+
if (sendCount > 10) {
|
|
209
|
+
console.warn('RemoteLogHandler: Too many failed sends, stopping. Clear queue.');
|
|
210
|
+
this.queue = [];
|
|
211
|
+
(this as any)._sendCount = 0;
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
203
215
|
try {
|
|
204
216
|
fetch(this.endpoint, {
|
|
205
217
|
method: 'POST',
|
|
@@ -207,14 +219,26 @@ export class RemoteLogHandler implements LogHandler {
|
|
|
207
219
|
'Content-Type': 'application/json'
|
|
208
220
|
},
|
|
209
221
|
body: JSON.stringify(entriesToSend),
|
|
210
|
-
// 不等待响应,避免阻塞
|
|
211
222
|
keepalive: true
|
|
212
|
-
}).catch(err => {
|
|
223
|
+
}).catch((err: Error) => {
|
|
213
224
|
console.error('Failed to send logs to remote server:', err);
|
|
225
|
+
|
|
226
|
+
// 防止无限重试 - 如果失败次数过多,丢弃日志
|
|
227
|
+
if ((this as any)._sendCount > 10) {
|
|
228
|
+
console.warn('RemoteLogHandler: Max retry exceeded, discarding logs');
|
|
229
|
+
this.queue = []; // 清空队列,避免内存泄漏
|
|
230
|
+
(this as any)._sendCount = 0;
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
|
|
214
234
|
// 失败时把日志放回队列,但防止无限增长
|
|
215
235
|
if (this.queue.length < this.maxQueueSize) {
|
|
216
|
-
|
|
236
|
+
const maxReturn = Math.min(entriesToSend.length, this.maxQueueSize - this.queue.length);
|
|
237
|
+
const returnedEntries = entriesToSend.slice(0, maxReturn);
|
|
238
|
+
this.queue = [...returnedEntries, ...this.queue];
|
|
217
239
|
}
|
|
240
|
+
|
|
241
|
+
(this as any)._sendCount = 0;
|
|
218
242
|
});
|
|
219
243
|
} catch (error) {
|
|
220
244
|
console.error('Error sending logs:', error);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
1
2
|
/**
|
|
2
3
|
* @file 模块管理器
|
|
3
4
|
* @description 统一管理库的各功能模块,提供模块的注册、初始化和卸载功能
|
|
@@ -49,6 +50,7 @@ export class ModuleManager extends EventEmitter {
|
|
|
49
50
|
private modules: Map<string, Module> = new Map();
|
|
50
51
|
private logger: Logger;
|
|
51
52
|
private initialized = false;
|
|
53
|
+
private initPromise: Promise<void> | null = null;
|
|
52
54
|
|
|
53
55
|
/**
|
|
54
56
|
* 获取模块管理器单例
|
|
@@ -59,6 +61,13 @@ export class ModuleManager extends EventEmitter {
|
|
|
59
61
|
}
|
|
60
62
|
return ModuleManager.instance;
|
|
61
63
|
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 重置单例实例(主要用于测试)
|
|
67
|
+
*/
|
|
68
|
+
public static resetInstance(): void {
|
|
69
|
+
ModuleManager.instance = undefined as any;
|
|
70
|
+
}
|
|
62
71
|
|
|
63
72
|
/**
|
|
64
73
|
* 私有构造函数,确保单例模式
|
package/src/index.ts
CHANGED
|
@@ -63,6 +63,10 @@ export class IDCardDetector extends EventEmitter {
|
|
|
63
63
|
ocr?: any;
|
|
64
64
|
antiFake?: any;
|
|
65
65
|
} = {};
|
|
66
|
+
|
|
67
|
+
/** 重用的 Canvas 元素,用于减少内存分配 */
|
|
68
|
+
private reusableCanvas: HTMLCanvasElement | null = null;
|
|
69
|
+
private reusableContext: CanvasRenderingContext2D | null = null;
|
|
66
70
|
|
|
67
71
|
/**
|
|
68
72
|
* 构造函数
|
|
@@ -187,6 +191,30 @@ export class IDCardDetector extends EventEmitter {
|
|
|
187
191
|
return Result.failure(new Error('身份证检测器未初始化'));
|
|
188
192
|
}
|
|
189
193
|
|
|
194
|
+
// 输入验证
|
|
195
|
+
if (!image) {
|
|
196
|
+
return Result.failure(new Error('图像源不能为空'));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// 验证 HTMLImageElement 是否已加载
|
|
200
|
+
if (image instanceof HTMLImageElement && !image.complete) {
|
|
201
|
+
return Result.failure(new Error('图像尚未加载完成'));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 验证 ImageData 尺寸
|
|
205
|
+
if (image instanceof ImageData) {
|
|
206
|
+
if (image.width === 0 || image.height === 0) {
|
|
207
|
+
return Result.failure(new Error('图像尺寸无效'));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// 验证 Canvas 尺寸
|
|
212
|
+
if (image instanceof HTMLCanvasElement) {
|
|
213
|
+
if (image.width === 0 || image.height === 0) {
|
|
214
|
+
return Result.failure(new Error('Canvas尺寸无效'));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
190
218
|
try {
|
|
191
219
|
this.logger.debug('IDCardDetector', '开始处理图像');
|
|
192
220
|
|
|
@@ -240,9 +268,7 @@ export class IDCardDetector extends EventEmitter {
|
|
|
240
268
|
idCardInfo.image = context.getImageData(0, 0, image.width, image.height);
|
|
241
269
|
}
|
|
242
270
|
} else if (image instanceof HTMLImageElement && image.complete) {
|
|
243
|
-
const canvas =
|
|
244
|
-
canvas.width = image.naturalWidth;
|
|
245
|
-
canvas.height = image.naturalHeight;
|
|
271
|
+
const canvas = this.getReusableCanvas(image.naturalWidth, image.naturalHeight);
|
|
246
272
|
const context = canvas.getContext('2d');
|
|
247
273
|
if (context) {
|
|
248
274
|
context.drawImage(image, 0, 0);
|
|
@@ -261,6 +287,29 @@ export class IDCardDetector extends EventEmitter {
|
|
|
261
287
|
}
|
|
262
288
|
}
|
|
263
289
|
|
|
290
|
+
/**
|
|
291
|
+
* 获取可重用的 Canvas 元素
|
|
292
|
+
* @param width 宽度
|
|
293
|
+
* @param height 高度
|
|
294
|
+
* @returns CanvasRenderingContext2D
|
|
295
|
+
*/
|
|
296
|
+
private getReusableCanvas(width: number, height: number): HTMLCanvasElement {
|
|
297
|
+
// 如果存在可重用的 canvas 且尺寸匹配,直接返回
|
|
298
|
+
if (this.reusableCanvas &&
|
|
299
|
+
this.reusableCanvas.width === width &&
|
|
300
|
+
this.reusableCanvas.height === height) {
|
|
301
|
+
return this.reusableCanvas;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// 创建新的 canvas
|
|
305
|
+
this.reusableCanvas = document.createElement('canvas');
|
|
306
|
+
this.reusableCanvas.width = width;
|
|
307
|
+
this.reusableCanvas.height = height;
|
|
308
|
+
this.reusableContext = this.reusableCanvas.getContext('2d');
|
|
309
|
+
|
|
310
|
+
return this.reusableCanvas;
|
|
311
|
+
}
|
|
312
|
+
|
|
264
313
|
/**
|
|
265
314
|
* 预处理图像
|
|
266
315
|
* @param image 图像源
|
|
@@ -272,7 +321,6 @@ export class IDCardDetector extends EventEmitter {
|
|
|
272
321
|
image: ImageData | HTMLImageElement | HTMLCanvasElement,
|
|
273
322
|
options: ImageProcessOptions
|
|
274
323
|
): Promise<ImageData> {
|
|
275
|
-
// 实际项目中,这里应该对图像进行预处理
|
|
276
324
|
this.logger.debug('IDCardDetector', '预处理图像');
|
|
277
325
|
|
|
278
326
|
// 创建ImageData对象
|
|
@@ -281,13 +329,10 @@ export class IDCardDetector extends EventEmitter {
|
|
|
281
329
|
if (image instanceof ImageData) {
|
|
282
330
|
imageData = image;
|
|
283
331
|
} else {
|
|
284
|
-
const canvas = document.createElement('canvas');
|
|
285
332
|
const width = image instanceof HTMLImageElement ? image.naturalWidth : image.width;
|
|
286
333
|
const height = image instanceof HTMLImageElement ? image.naturalHeight : image.height;
|
|
287
334
|
|
|
288
|
-
canvas
|
|
289
|
-
canvas.height = height;
|
|
290
|
-
|
|
335
|
+
const canvas = this.getReusableCanvas(width, height);
|
|
291
336
|
const context = canvas.getContext('2d');
|
|
292
337
|
if (!context) {
|
|
293
338
|
throw new Error('无法获取Canvas上下文');
|
|
@@ -344,21 +389,19 @@ export class IDCardDetector extends EventEmitter {
|
|
|
344
389
|
* @private
|
|
345
390
|
*/
|
|
346
391
|
private async cropAndAlign(image: ImageData, edge: IDCardEdge): Promise<ImageData> {
|
|
347
|
-
// 实际项目中,这里应该进行透视变换以校正图像
|
|
348
392
|
this.logger.debug('IDCardDetector', '裁剪并校正图像');
|
|
349
393
|
|
|
350
|
-
// 创建Canvas
|
|
351
|
-
const canvas = document.createElement('canvas');
|
|
352
394
|
// 设置标准身份证尺寸比例
|
|
353
|
-
|
|
354
|
-
|
|
395
|
+
const standardWidth = 428;
|
|
396
|
+
const standardHeight = 270;
|
|
355
397
|
|
|
398
|
+
const canvas = this.getReusableCanvas(standardWidth, standardHeight);
|
|
356
399
|
const context = canvas.getContext('2d');
|
|
357
400
|
if (!context) {
|
|
358
401
|
throw new Error('无法获取Canvas上下文');
|
|
359
402
|
}
|
|
360
403
|
|
|
361
|
-
// 创建临时Canvas
|
|
404
|
+
// 创建临时Canvas用于源图像
|
|
362
405
|
const tempCanvas = document.createElement('canvas');
|
|
363
406
|
tempCanvas.width = image.width;
|
|
364
407
|
tempCanvas.height = image.height;
|
|
@@ -383,34 +426,42 @@ export class IDCardDetector extends EventEmitter {
|
|
|
383
426
|
edge.bottomLeft.y - edge.topLeft.y,
|
|
384
427
|
0,
|
|
385
428
|
0,
|
|
386
|
-
|
|
387
|
-
|
|
429
|
+
standardWidth,
|
|
430
|
+
standardHeight
|
|
388
431
|
);
|
|
389
432
|
|
|
390
|
-
return context.getImageData(0, 0,
|
|
433
|
+
return context.getImageData(0, 0, standardWidth, standardHeight);
|
|
391
434
|
}
|
|
392
435
|
|
|
393
436
|
/**
|
|
394
437
|
* 识别文字
|
|
438
|
+
*
|
|
439
|
+
* @note 此方法返回模拟数据,用于框架开发和测试
|
|
440
|
+
* 实际使用时需要替换为真实的 OCR 模型集成
|
|
441
|
+
*
|
|
395
442
|
* @param image 图像数据
|
|
396
443
|
* @param type 身份证类型
|
|
397
444
|
* @returns 识别结果
|
|
398
445
|
* @private
|
|
399
446
|
*/
|
|
400
447
|
private async recognizeText(image: ImageData, type: IDCardType): Promise<Partial<IDCardInfo>> {
|
|
401
|
-
// 实际项目中,这里应该调用OCR模型进行文字识别
|
|
402
448
|
this.logger.debug('IDCardDetector', '识别文字');
|
|
403
449
|
|
|
404
450
|
// 模拟OCR结果
|
|
405
|
-
//
|
|
451
|
+
// 注意:这是框架的占位实现,真实场景需要接入实际的 OCR 服务
|
|
452
|
+
// 可选的方案包括:
|
|
453
|
+
// - TensorFlow.js + 自定义 OCR 模型
|
|
454
|
+
// - 第三方 OCR API (如百度OCR、腾讯OCR)
|
|
455
|
+
// - Tesseract.js WASM 版本
|
|
456
|
+
//
|
|
406
457
|
if (type === IDCardType.FRONT) {
|
|
407
458
|
return {
|
|
408
|
-
name: '张三',
|
|
409
|
-
gender: '男',
|
|
410
|
-
ethnicity: '汉',
|
|
411
|
-
birthDate: '1990-01-01',
|
|
412
|
-
address: '北京市朝阳区某某街道某某社区1号楼1单元101',
|
|
413
|
-
idNumber: '110101199001010001',
|
|
459
|
+
name: '张三', // TODO: 替换为真实OCR结果
|
|
460
|
+
gender: '男', // TODO: 替换为真实OCR结果
|
|
461
|
+
ethnicity: '汉', // TODO: 替换为真实OCR结果
|
|
462
|
+
birthDate: '1990-01-01', // TODO: 替换为真实OCR结果
|
|
463
|
+
address: '北京市朝阳区某某街道某某社区1号楼1单元101', // TODO: 替换为真实OCR结果
|
|
464
|
+
idNumber: '110101199001010001', // TODO: 替换为真实OCR结果
|
|
414
465
|
photoRegion: {
|
|
415
466
|
x: 300,
|
|
416
467
|
y: 40,
|
|
@@ -420,9 +471,9 @@ export class IDCardDetector extends EventEmitter {
|
|
|
420
471
|
};
|
|
421
472
|
} else if (type === IDCardType.BACK) {
|
|
422
473
|
return {
|
|
423
|
-
issueAuthority: '北京市公安局朝阳分局',
|
|
424
|
-
validFrom: '2015-01-01',
|
|
425
|
-
validTo: '2035-01-01'
|
|
474
|
+
issueAuthority: '北京市公安局朝阳分局', // TODO: 替换为真实OCR结果
|
|
475
|
+
validFrom: '2015-01-01', // TODO: 替换为真实OCR结果
|
|
476
|
+
validTo: '2035-01-01' // TODO: 替换为真实OCR结果
|
|
426
477
|
};
|
|
427
478
|
}
|
|
428
479
|
|
|
@@ -431,6 +482,10 @@ export class IDCardDetector extends EventEmitter {
|
|
|
431
482
|
|
|
432
483
|
/**
|
|
433
484
|
* 检测防伪特征
|
|
485
|
+
*
|
|
486
|
+
* @note 此方法返回模拟数据,用于框架开发和测试
|
|
487
|
+
* 实际使用时需要替换为真实的防伪检测模型
|
|
488
|
+
*
|
|
434
489
|
* @param image 图像数据
|
|
435
490
|
* @param detectionResult 检测结果
|
|
436
491
|
* @returns 防伪检测结果
|
|
@@ -440,20 +495,24 @@ export class IDCardDetector extends EventEmitter {
|
|
|
440
495
|
image: ImageData,
|
|
441
496
|
detectionResult: { type: IDCardType; edge: IDCardEdge; confidence: number }
|
|
442
497
|
): Promise<IDCardInfo['antiFake']> {
|
|
443
|
-
// 实际项目中,这里应该调用防伪模型进行特征检测
|
|
444
498
|
this.logger.debug('IDCardDetector', '检测防伪特征');
|
|
445
499
|
|
|
446
500
|
// 模拟防伪检测结果
|
|
447
|
-
//
|
|
501
|
+
// 注意:这是框架的占位实现,真实场景需要接入实际的防伪检测模型
|
|
502
|
+
// 可选的方案包括:
|
|
503
|
+
// - 紫外光特征检测
|
|
504
|
+
// - 红外光特征检测
|
|
505
|
+
// - 微缩文字检测
|
|
506
|
+
// - 光学变色特征检测
|
|
448
507
|
return {
|
|
449
508
|
passed: true,
|
|
450
509
|
score: 0.92,
|
|
451
510
|
features: {
|
|
452
|
-
fluorescent: true,
|
|
453
|
-
microtext: true,
|
|
454
|
-
opticalVariable: true,
|
|
455
|
-
texture: true,
|
|
456
|
-
watermark: true
|
|
511
|
+
fluorescent: true, // TODO: 替换为真实检测结果
|
|
512
|
+
microtext: true, // TODO: 替换为真实检测结果
|
|
513
|
+
opticalVariable: true, // TODO: 替换为真实检测结果
|
|
514
|
+
texture: true, // TODO: 替换为真实检测结果
|
|
515
|
+
watermark: true // TODO: 替换为真实检测结果
|
|
457
516
|
}
|
|
458
517
|
};
|
|
459
518
|
}
|
|
@@ -468,6 +527,10 @@ export class IDCardDetector extends EventEmitter {
|
|
|
468
527
|
this.models = {};
|
|
469
528
|
this.initialized = false;
|
|
470
529
|
|
|
530
|
+
// 清理可重用的 Canvas
|
|
531
|
+
this.reusableCanvas = null;
|
|
532
|
+
this.reusableContext = null;
|
|
533
|
+
|
|
471
534
|
// 清理事件监听
|
|
472
535
|
this.removeAllListeners();
|
|
473
536
|
}
|
|
@@ -140,7 +140,7 @@ export class OCRProcessor implements Disposable {
|
|
|
140
140
|
* @param imageData 要处理的身份证图像数据
|
|
141
141
|
* @returns 提取的身份证信息
|
|
142
142
|
*/
|
|
143
|
-
async processIDCard(imageData: ImageData): Promise<IDCardInfo> {
|
|
143
|
+
async processIDCard(imageData: ImageData): Promise<IDCardInfo | null> {
|
|
144
144
|
if (!this.initialized) {
|
|
145
145
|
await this.initialize()
|
|
146
146
|
}
|
|
@@ -249,8 +249,8 @@ export class OCRProcessor implements Disposable {
|
|
|
249
249
|
|
|
250
250
|
this.options.logger?.(`OCR识别错误: ${errorMessage}`);
|
|
251
251
|
|
|
252
|
-
//
|
|
253
|
-
return
|
|
252
|
+
// 返回 null,让调用方知道识别失败
|
|
253
|
+
return null;
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
package/src/utils/camera.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
1
2
|
/**
|
|
2
3
|
* @file 相机工具类
|
|
3
4
|
* @description 提供访问和控制设备摄像头的功能
|
|
@@ -104,15 +105,28 @@ export class Camera {
|
|
|
104
105
|
// 绑定到视频元素
|
|
105
106
|
if (this.videoElement) {
|
|
106
107
|
this.videoElement.srcObject = this.stream;
|
|
107
|
-
|
|
108
|
+
|
|
109
|
+
// 添加超时保护,防止永久挂起
|
|
110
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
111
|
+
setTimeout(() => reject(new Error('摄像头初始化超时')), 10000); // 10秒超时
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const playPromise = new Promise<void>((resolve, reject) => {
|
|
108
115
|
if (this.videoElement) {
|
|
109
116
|
this.videoElement.onloadedmetadata = () => {
|
|
110
117
|
if (this.videoElement) {
|
|
111
|
-
this.videoElement.play()
|
|
118
|
+
this.videoElement.play()
|
|
119
|
+
.then(() => resolve())
|
|
120
|
+
.catch(err => reject(err));
|
|
112
121
|
}
|
|
113
122
|
};
|
|
123
|
+
this.videoElement.onerror = () => reject(new Error('视频加载失败'));
|
|
124
|
+
} else {
|
|
125
|
+
reject(new Error('视频元素未初始化'));
|
|
114
126
|
}
|
|
115
127
|
});
|
|
128
|
+
|
|
129
|
+
await Promise.race([playPromise, timeoutPromise]);
|
|
116
130
|
}
|
|
117
131
|
} catch (error) {
|
|
118
132
|
const logger = Logger.getInstance();
|
|
@@ -86,7 +86,7 @@ export class ResourceManager {
|
|
|
86
86
|
*/
|
|
87
87
|
export function createDisposable<T>(
|
|
88
88
|
factory: () => T,
|
|
89
|
-
disposeCallback: (
|
|
89
|
+
disposeCallback: (_resource: T) => Promise<void> | void
|
|
90
90
|
): T & Disposable {
|
|
91
91
|
const resource = factory();
|
|
92
92
|
|
|
@@ -106,7 +106,7 @@ export function createDisposable<T>(
|
|
|
106
106
|
*/
|
|
107
107
|
export async function using<T extends Disposable, R>(
|
|
108
108
|
resource: T,
|
|
109
|
-
callback: (
|
|
109
|
+
callback: (_resource: T) => Promise<R> | R
|
|
110
110
|
): Promise<R> {
|
|
111
111
|
try {
|
|
112
112
|
const result = await callback(resource);
|
package/src/utils/worker.ts
CHANGED
|
@@ -75,7 +75,7 @@ export function createWorker<TInput, TOutput>(
|
|
|
75
75
|
|
|
76
76
|
// 监听Worker消息
|
|
77
77
|
worker.addEventListener('message', (event) => {
|
|
78
|
-
const { messageId, success, result, error } = event.data;
|
|
78
|
+
const { messageId, success, result, error: _error } = event.data;
|
|
79
79
|
|
|
80
80
|
// 查找对应的Promise
|
|
81
81
|
const promiseHandlers = promiseMap.get(messageId);
|
|
@@ -83,7 +83,7 @@ export function createWorker<TInput, TOutput>(
|
|
|
83
83
|
if (success) {
|
|
84
84
|
promiseHandlers.resolve(result);
|
|
85
85
|
} else {
|
|
86
|
-
promiseHandlers.reject(new Error(
|
|
86
|
+
promiseHandlers.reject(new Error(_error));
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// 删除Promise映射
|
|
@@ -113,8 +113,8 @@ export function createWorker<TInput, TOutput>(
|
|
|
113
113
|
URL.revokeObjectURL(url);
|
|
114
114
|
|
|
115
115
|
// 拒绝所有未完成的Promise
|
|
116
|
-
for (const [, { reject }] of promiseMap) {
|
|
117
|
-
|
|
116
|
+
for (const [, { reject: _reject }] of promiseMap) {
|
|
117
|
+
_reject(new Error('Worker已终止'));
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
// 清空Promise映射
|