@jaak.ai/stamps 2.0.0-dev.58 → 2.0.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/cjs/jaak-stamps-webcomponent.cjs.js +1 -1
- package/dist/cjs/jaak-stamps.cjs.entry.js +611 -546
- package/dist/cjs/jaak-stamps.cjs.entry.js.map +1 -1
- package/dist/cjs/jaak-stamps.entry.cjs.js.map +1 -1
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/collection/components/my-component/my-component.css +2 -14
- package/dist/collection/components/my-component/my-component.js +237 -346
- package/dist/collection/components/my-component/my-component.js.map +1 -1
- package/dist/collection/services/CameraService.js +344 -156
- package/dist/collection/services/CameraService.js.map +1 -1
- package/dist/collection/services/DetectionService.js +38 -53
- package/dist/collection/services/DetectionService.js.map +1 -1
- package/dist/collection/services/EventBusService.js +1 -1
- package/dist/collection/services/EventBusService.js.map +1 -1
- package/dist/collection/services/LoggerService.js +40 -0
- package/dist/collection/services/LoggerService.js.map +1 -0
- package/dist/collection/services/ServiceContainer.js +12 -2
- package/dist/collection/services/ServiceContainer.js.map +1 -1
- package/dist/collection/services/interfaces/ICameraService.js.map +1 -1
- package/dist/collection/services/interfaces/ILogger.js +2 -0
- package/dist/collection/services/interfaces/ILogger.js.map +1 -0
- package/dist/collection/types/component-types.js.map +1 -1
- package/dist/components/jaak-stamps.js +614 -548
- package/dist/components/jaak-stamps.js.map +1 -1
- package/dist/esm/jaak-stamps-webcomponent.js +1 -1
- package/dist/esm/jaak-stamps.entry.js +611 -546
- package/dist/esm/jaak-stamps.entry.js.map +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps-webcomponent.esm.js +1 -1
- package/dist/jaak-stamps-webcomponent/jaak-stamps.entry.esm.js.map +1 -1
- package/dist/jaak-stamps-webcomponent/p-2264b5b4.entry.js +2 -0
- package/dist/jaak-stamps-webcomponent/p-2264b5b4.entry.js.map +1 -0
- package/dist/types/components/my-component/my-component.d.ts +17 -62
- package/dist/types/components.d.ts +8 -6
- package/dist/types/services/CameraService.d.ts +14 -12
- package/dist/types/services/DetectionService.d.ts +3 -9
- package/dist/types/services/LoggerService.d.ts +12 -0
- package/dist/types/services/ServiceContainer.d.ts +2 -0
- package/dist/types/services/interfaces/ICameraService.d.ts +12 -3
- package/dist/types/services/interfaces/ILogger.d.ts +8 -0
- package/dist/types/types/component-types.d.ts +0 -3
- package/package.json +4 -4
- package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js +0 -2
- package/dist/jaak-stamps-webcomponent/p-39560f23.entry.js.map +0 -1
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
import { p as proxyCustomElement, H, c as createEvent, h } from './index.js';
|
|
2
2
|
|
|
3
|
+
class LoggerService {
|
|
4
|
+
debugMode;
|
|
5
|
+
constructor(debug = false) {
|
|
6
|
+
this.debugMode = debug;
|
|
7
|
+
}
|
|
8
|
+
setDebugMode(debug) {
|
|
9
|
+
this.debugMode = debug;
|
|
10
|
+
}
|
|
11
|
+
info(...args) {
|
|
12
|
+
if (this.debugMode) {
|
|
13
|
+
console.log(`[JAAK-STAMPS] [INFO] [${new Date().toLocaleTimeString()}]`, ...args);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
warn(...args) {
|
|
17
|
+
if (this.debugMode) {
|
|
18
|
+
console.warn(`[JAAK-STAMPS] [WARN] [${new Date().toLocaleTimeString()}]`, ...args);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
error(...args) {
|
|
22
|
+
if (this.debugMode) {
|
|
23
|
+
console.error(`[JAAK-STAMPS] [ERROR] [${new Date().toLocaleTimeString()}]`, ...args);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
debug(...args) {
|
|
27
|
+
if (this.debugMode) {
|
|
28
|
+
console.debug(`[JAAK-STAMPS] [DEBUG] [${new Date().toLocaleTimeString()}]`, ...args);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
state(state, data) {
|
|
32
|
+
if (this.debugMode) {
|
|
33
|
+
console.log(`[JAAK-STAMPS] [STATE] [${new Date().toLocaleTimeString()}] ${state}`, data || '');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
performance(operation, duration) {
|
|
37
|
+
if (this.debugMode) {
|
|
38
|
+
console.log(`[JAAK-STAMPS] [PERF] [${new Date().toLocaleTimeString()}] ${operation}: ${duration}ms`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
3
43
|
class EventBusService {
|
|
4
44
|
events = new Map();
|
|
5
45
|
on(event, callback) {
|
|
@@ -25,7 +65,7 @@ class EventBusService {
|
|
|
25
65
|
callback(data);
|
|
26
66
|
}
|
|
27
67
|
catch (error) {
|
|
28
|
-
|
|
68
|
+
console.error(`Error in event callback for ${event}:`, error);
|
|
29
69
|
}
|
|
30
70
|
});
|
|
31
71
|
}
|
|
@@ -152,20 +192,16 @@ class StateManagerService {
|
|
|
152
192
|
}
|
|
153
193
|
|
|
154
194
|
class CameraService {
|
|
195
|
+
logger;
|
|
155
196
|
eventBus;
|
|
156
197
|
availableCameras = [];
|
|
157
198
|
selectedCameraId = null;
|
|
158
199
|
deviceType = 'desktop';
|
|
159
200
|
preferredCameraFacing = null;
|
|
160
201
|
preferredCamera = 'auto';
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
static CACHE_DURATION = 30000; // 30 seconds
|
|
165
|
-
// Stream management for optimization
|
|
166
|
-
currentStreamPromise;
|
|
167
|
-
lastStreamConstraints;
|
|
168
|
-
constructor(eventBus, preferredCamera = 'auto') {
|
|
202
|
+
isManuallySelected = false;
|
|
203
|
+
constructor(logger, eventBus, preferredCamera = 'auto') {
|
|
204
|
+
this.logger = logger;
|
|
169
205
|
this.eventBus = eventBus;
|
|
170
206
|
this.preferredCamera = preferredCamera;
|
|
171
207
|
}
|
|
@@ -182,21 +218,18 @@ class CameraService {
|
|
|
182
218
|
else {
|
|
183
219
|
this.deviceType = 'desktop';
|
|
184
220
|
}
|
|
221
|
+
this.logger.state('DISPOSITIVO_DETECTADO', {
|
|
222
|
+
deviceType: this.deviceType,
|
|
223
|
+
userAgent: navigator.userAgent,
|
|
224
|
+
screenDimensions: { width: window.innerWidth, height: window.innerHeight }
|
|
225
|
+
});
|
|
185
226
|
return this.deviceType;
|
|
186
227
|
}
|
|
187
228
|
async enumerateDevices() {
|
|
188
229
|
try {
|
|
189
|
-
// Check cache first for optimization
|
|
190
|
-
const now = Date.now();
|
|
191
|
-
if (CameraService.deviceEnumerationCache &&
|
|
192
|
-
(now - CameraService.deviceEnumerationCache.timestamp) < CameraService.CACHE_DURATION) {
|
|
193
|
-
this.availableCameras = CameraService.deviceEnumerationCache.devices;
|
|
194
|
-
await this.setInitialCameraPreference();
|
|
195
|
-
this.eventBus.emit('camera-changed', this.selectedCameraId);
|
|
196
|
-
return this.availableCameras;
|
|
197
|
-
}
|
|
198
230
|
const permissionStatus = await this.checkCameraPermission();
|
|
199
231
|
if (permissionStatus === 'denied') {
|
|
232
|
+
this.logger.error('Permiso de cámara denegado por el usuario');
|
|
200
233
|
return [];
|
|
201
234
|
}
|
|
202
235
|
if (permissionStatus === 'prompt') {
|
|
@@ -205,16 +238,24 @@ class CameraService {
|
|
|
205
238
|
}
|
|
206
239
|
const devices = await navigator.mediaDevices.enumerateDevices();
|
|
207
240
|
this.availableCameras = devices.filter(device => device.kind === 'videoinput');
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
241
|
+
this.logger.state('CAMARAS_DETECTADAS', {
|
|
242
|
+
count: this.availableCameras.length,
|
|
243
|
+
cameras: this.availableCameras.map(cam => ({
|
|
244
|
+
id: cam.deviceId,
|
|
245
|
+
label: cam.label || 'Unknown Camera'
|
|
246
|
+
}))
|
|
247
|
+
});
|
|
248
|
+
this.setInitialCameraPreference();
|
|
249
|
+
this.logger.state('PREFERENCIA_INICIAL_APLICADA', {
|
|
250
|
+
selectedCameraId: this.selectedCameraId,
|
|
251
|
+
preferredCameraFacing: this.preferredCameraFacing,
|
|
252
|
+
preferredCamera: this.preferredCamera
|
|
253
|
+
});
|
|
214
254
|
this.eventBus.emit('camera-changed', this.selectedCameraId);
|
|
215
255
|
return this.availableCameras;
|
|
216
256
|
}
|
|
217
257
|
catch (error) {
|
|
258
|
+
this.logger.error('Error al enumerar cámaras disponibles:', error);
|
|
218
259
|
this.handleCameraPermissionError(error);
|
|
219
260
|
return [];
|
|
220
261
|
}
|
|
@@ -235,6 +276,8 @@ class CameraService {
|
|
|
235
276
|
}
|
|
236
277
|
this.selectedCameraId = cameraId;
|
|
237
278
|
this.updatePreferredFacing(camera);
|
|
279
|
+
this.isManuallySelected = true; // Mark as manually selected
|
|
280
|
+
this.savePreference();
|
|
238
281
|
this.eventBus.emit('camera-changed', cameraId);
|
|
239
282
|
}
|
|
240
283
|
getPreferredFacing() {
|
|
@@ -243,20 +286,14 @@ class CameraService {
|
|
|
243
286
|
async setupCamera(constraints) {
|
|
244
287
|
try {
|
|
245
288
|
const finalConstraints = constraints || await this.getMaxResolution();
|
|
246
|
-
|
|
247
|
-
if (this.currentStreamPromise && this.constraintsEqual(finalConstraints, this.lastStreamConstraints)) {
|
|
248
|
-
return await this.currentStreamPromise;
|
|
249
|
-
}
|
|
250
|
-
// Create new stream promise for lazy loading
|
|
251
|
-
this.currentStreamPromise = navigator.mediaDevices.getUserMedia({
|
|
289
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
252
290
|
video: finalConstraints,
|
|
253
291
|
audio: false
|
|
254
292
|
});
|
|
255
|
-
this.lastStreamConstraints = finalConstraints;
|
|
256
|
-
const stream = await this.currentStreamPromise;
|
|
257
293
|
return stream;
|
|
258
294
|
}
|
|
259
295
|
catch (error) {
|
|
296
|
+
this.logger.error('Error en setupCamera, intentando con restricciones básicas:', error);
|
|
260
297
|
// Fallback to basic constraints if getMaxResolution fails
|
|
261
298
|
const basicConstraints = {
|
|
262
299
|
width: { ideal: 1280 },
|
|
@@ -266,21 +303,65 @@ class CameraService {
|
|
|
266
303
|
if (this.deviceType !== 'desktop' && this.preferredCameraFacing) {
|
|
267
304
|
basicConstraints.facingMode = this.preferredCameraFacing;
|
|
268
305
|
}
|
|
269
|
-
this.
|
|
306
|
+
this.logger.state('USANDO_RESTRICCIONES_BASICAS', { constraints: basicConstraints });
|
|
307
|
+
return await navigator.mediaDevices.getUserMedia({
|
|
270
308
|
video: basicConstraints,
|
|
271
309
|
audio: false
|
|
272
310
|
});
|
|
273
|
-
this.lastStreamConstraints = basicConstraints;
|
|
274
|
-
return await this.currentStreamPromise;
|
|
275
311
|
}
|
|
276
312
|
}
|
|
277
313
|
async switchCamera(cameraId) {
|
|
278
314
|
const selectedCamera = this.availableCameras.find(cam => cam.deviceId === cameraId);
|
|
279
315
|
if (!selectedCamera) {
|
|
316
|
+
this.logger.warn('Cámara seleccionada no encontrada, re-enumerando dispositivos...');
|
|
280
317
|
await this.enumerateDevices();
|
|
281
318
|
return;
|
|
282
319
|
}
|
|
283
320
|
await this.setSelectedCamera(cameraId);
|
|
321
|
+
this.logger.state('CAMARA_CAMBIADA', {
|
|
322
|
+
label: selectedCamera.label,
|
|
323
|
+
deviceId: selectedCamera.deviceId,
|
|
324
|
+
isManuallySelected: this.isManuallySelected
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
async flipToNextCamera() {
|
|
328
|
+
if (!this.isMultipleCamerasAvailable())
|
|
329
|
+
return;
|
|
330
|
+
const currentIndex = this.availableCameras.findIndex(camera => camera.deviceId === this.selectedCameraId);
|
|
331
|
+
const nextIndex = (currentIndex + 1) % this.availableCameras.length;
|
|
332
|
+
const nextCamera = this.availableCameras[nextIndex];
|
|
333
|
+
await this.switchCamera(nextCamera.deviceId);
|
|
334
|
+
}
|
|
335
|
+
savePreference() {
|
|
336
|
+
try {
|
|
337
|
+
const preference = {
|
|
338
|
+
cameraId: this.selectedCameraId,
|
|
339
|
+
facing: this.preferredCameraFacing,
|
|
340
|
+
timestamp: Date.now()
|
|
341
|
+
};
|
|
342
|
+
localStorage.setItem('jaak-stamps-camera-preference', JSON.stringify(preference));
|
|
343
|
+
this.logger.state('PREFERENCIA_CAMARA_GUARDADA', preference);
|
|
344
|
+
}
|
|
345
|
+
catch (error) {
|
|
346
|
+
this.logger.warn('Error al guardar preferencia de cámara:', error);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
loadPreference() {
|
|
350
|
+
try {
|
|
351
|
+
const saved = localStorage.getItem('jaak-stamps-camera-preference');
|
|
352
|
+
if (saved) {
|
|
353
|
+
const preference = JSON.parse(saved);
|
|
354
|
+
const isStillAvailable = this.availableCameras.some(camera => camera.deviceId === preference.cameraId);
|
|
355
|
+
if (isStillAvailable) {
|
|
356
|
+
this.selectedCameraId = preference.cameraId;
|
|
357
|
+
this.preferredCameraFacing = preference.facing;
|
|
358
|
+
this.logger.state('PREFERENCIA_CAMARA_CARGADA', preference);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
this.logger.warn('Error al cargar preferencia de cámara:', error);
|
|
364
|
+
}
|
|
284
365
|
}
|
|
285
366
|
isRearCamera(stream) {
|
|
286
367
|
const videoTrack = stream.getVideoTracks()[0];
|
|
@@ -289,18 +370,13 @@ class CameraService {
|
|
|
289
370
|
const settings = videoTrack.getSettings();
|
|
290
371
|
return settings.facingMode === 'environment';
|
|
291
372
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
return {
|
|
373
|
+
getCameraInfo() {
|
|
374
|
+
return {
|
|
375
|
+
availableCameras: this.availableCameras.map(camera => ({
|
|
296
376
|
id: camera.deviceId,
|
|
297
377
|
label: camera.label || 'Unknown Camera',
|
|
298
|
-
selected: camera.deviceId === this.selectedCameraId
|
|
299
|
-
|
|
300
|
-
};
|
|
301
|
-
}));
|
|
302
|
-
return {
|
|
303
|
-
availableCameras: availableCamerasWithAutofocus,
|
|
378
|
+
selected: camera.deviceId === this.selectedCameraId
|
|
379
|
+
})),
|
|
304
380
|
selectedCameraId: this.selectedCameraId,
|
|
305
381
|
deviceType: this.deviceType,
|
|
306
382
|
isMultipleCamerasAvailable: this.isMultipleCamerasAvailable(),
|
|
@@ -316,6 +392,7 @@ class CameraService {
|
|
|
316
392
|
return permission.state;
|
|
317
393
|
}
|
|
318
394
|
catch (error) {
|
|
395
|
+
this.logger.warn('No se pudo verificar permisos de cámara:', error);
|
|
319
396
|
return 'prompt';
|
|
320
397
|
}
|
|
321
398
|
}
|
|
@@ -323,48 +400,35 @@ class CameraService {
|
|
|
323
400
|
this.availableCameras = [];
|
|
324
401
|
this.eventBus.emit('error', new Error(`Camera permission error: ${error.message}`));
|
|
325
402
|
}
|
|
326
|
-
|
|
403
|
+
setInitialCameraPreference() {
|
|
327
404
|
if (this.availableCameras.length === 0)
|
|
328
405
|
return;
|
|
406
|
+
// Clear any existing localStorage preferences to ensure fresh start
|
|
407
|
+
localStorage.removeItem('jaak-stamps-camera-preference');
|
|
408
|
+
this.logger.state('APLICANDO_PREFERENCIA_INICIAL', {
|
|
409
|
+
preferredCamera: this.preferredCamera,
|
|
410
|
+
availableCamerasCount: this.availableCameras.length
|
|
411
|
+
});
|
|
412
|
+
this.isManuallySelected = false; // Reset manual selection flag
|
|
329
413
|
if (this.preferredCamera === 'front') {
|
|
330
|
-
|
|
414
|
+
this.selectFrontCamera();
|
|
331
415
|
}
|
|
332
416
|
else if (this.preferredCamera === 'back') {
|
|
333
|
-
|
|
417
|
+
this.selectBackCamera();
|
|
334
418
|
}
|
|
335
419
|
else {
|
|
336
|
-
|
|
420
|
+
this.selectAutoCamera();
|
|
337
421
|
}
|
|
338
422
|
}
|
|
339
|
-
|
|
340
|
-
try {
|
|
341
|
-
// Check cache first for optimization
|
|
342
|
-
if (CameraService.cameraCapabilitiesCache.has(deviceId)) {
|
|
343
|
-
const cached = CameraService.cameraCapabilitiesCache.get(deviceId);
|
|
344
|
-
return typeof cached === 'boolean' ? cached : false;
|
|
345
|
-
}
|
|
346
|
-
const stream = await navigator.mediaDevices.getUserMedia({
|
|
347
|
-
video: { deviceId: { exact: deviceId } }
|
|
348
|
-
});
|
|
349
|
-
const videoTrack = stream.getVideoTracks()[0];
|
|
350
|
-
const capabilities = videoTrack.getCapabilities();
|
|
351
|
-
stream.getTracks().forEach(track => track.stop());
|
|
352
|
-
const hasFocusMode = capabilities.focusMode && Array.isArray(capabilities.focusMode) && capabilities.focusMode.length > 0;
|
|
353
|
-
const hasAutofocus = hasFocusMode && (capabilities.focusMode.includes('continuous') ||
|
|
354
|
-
capabilities.focusMode.includes('auto'));
|
|
355
|
-
// Cache the result for future use
|
|
356
|
-
CameraService.cameraCapabilitiesCache.set(deviceId, hasAutofocus);
|
|
357
|
-
return hasAutofocus;
|
|
358
|
-
}
|
|
359
|
-
catch (error) {
|
|
360
|
-
// Cache negative result to avoid repeated failures
|
|
361
|
-
CameraService.cameraCapabilitiesCache.set(deviceId, false);
|
|
362
|
-
return false;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
async selectFrontCamera() {
|
|
423
|
+
selectFrontCamera() {
|
|
366
424
|
this.preferredCameraFacing = 'user';
|
|
367
|
-
|
|
425
|
+
this.logger.state('BUSCANDO_CAMARA_FRONTAL', {
|
|
426
|
+
availableCameras: this.availableCameras.map(cam => ({
|
|
427
|
+
id: cam.deviceId,
|
|
428
|
+
label: cam.label
|
|
429
|
+
}))
|
|
430
|
+
});
|
|
431
|
+
const frontCamera = this.availableCameras.find(camera => {
|
|
368
432
|
const label = camera.label.toLowerCase();
|
|
369
433
|
return label.includes('front') ||
|
|
370
434
|
label.includes('user') ||
|
|
@@ -372,24 +436,24 @@ class CameraService {
|
|
|
372
436
|
label.includes('frontal') ||
|
|
373
437
|
(!label.includes('back') && !label.includes('rear') && !label.includes('environment'));
|
|
374
438
|
});
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
if (!selectedCamera) {
|
|
385
|
-
selectedCamera = frontCameras[0];
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
this.selectedCameraId = selectedCamera ? selectedCamera.deviceId : this.availableCameras[0].deviceId;
|
|
439
|
+
// If not found, use the first camera (usually front on mobile)
|
|
440
|
+
this.selectedCameraId = frontCamera ? frontCamera.deviceId : this.availableCameras[0].deviceId;
|
|
441
|
+
this.logger.state('CAMARA_FRONTAL_SELECCIONADA', {
|
|
442
|
+
found: !!frontCamera,
|
|
443
|
+
label: frontCamera?.label || this.availableCameras[0].label,
|
|
444
|
+
deviceId: this.selectedCameraId
|
|
445
|
+
});
|
|
389
446
|
}
|
|
390
|
-
|
|
447
|
+
selectBackCamera() {
|
|
391
448
|
this.preferredCameraFacing = 'environment';
|
|
392
|
-
|
|
449
|
+
this.logger.state('BUSCANDO_CAMARA_TRASERA', {
|
|
450
|
+
availableCameras: this.availableCameras.map(cam => ({
|
|
451
|
+
id: cam.deviceId,
|
|
452
|
+
label: cam.label
|
|
453
|
+
}))
|
|
454
|
+
});
|
|
455
|
+
// Try to find rear camera with multiple detection methods
|
|
456
|
+
let backCamera = this.availableCameras.find(camera => {
|
|
393
457
|
const label = camera.label.toLowerCase();
|
|
394
458
|
return label.includes('back') ||
|
|
395
459
|
label.includes('rear') ||
|
|
@@ -397,33 +461,34 @@ class CameraService {
|
|
|
397
461
|
label.includes('trasera') ||
|
|
398
462
|
label.includes('posterior');
|
|
399
463
|
});
|
|
400
|
-
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
if (!selectedCamera) {
|
|
410
|
-
selectedCamera = backCameras[0];
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
else if (this.availableCameras.length > 1) {
|
|
414
|
-
const lastCamera = this.availableCameras[this.availableCameras.length - 1];
|
|
415
|
-
const hasAutofocus = await this.checkCameraAutofocus(lastCamera.deviceId);
|
|
416
|
-
selectedCamera = hasAutofocus ? lastCamera : this.availableCameras[this.availableCameras.length - 1];
|
|
464
|
+
// If not found by label, try to use the last camera (usually rear on mobile)
|
|
465
|
+
if (!backCamera && this.availableCameras.length > 1) {
|
|
466
|
+
backCamera = this.availableCameras[this.availableCameras.length - 1];
|
|
467
|
+
this.logger.state('USANDO_ULTIMA_CAMARA_COMO_TRASERA', {
|
|
468
|
+
label: backCamera.label,
|
|
469
|
+
deviceId: backCamera.deviceId
|
|
470
|
+
});
|
|
417
471
|
}
|
|
418
|
-
this.selectedCameraId =
|
|
472
|
+
this.selectedCameraId = backCamera ? backCamera.deviceId : this.availableCameras[0].deviceId;
|
|
473
|
+
this.logger.state('CAMARA_TRASERA_SELECCIONADA', {
|
|
474
|
+
found: !!backCamera,
|
|
475
|
+
label: backCamera?.label || this.availableCameras[0].label,
|
|
476
|
+
deviceId: this.selectedCameraId
|
|
477
|
+
});
|
|
419
478
|
}
|
|
420
|
-
|
|
479
|
+
selectAutoCamera() {
|
|
421
480
|
if (this.deviceType === 'mobile' || this.deviceType === 'tablet') {
|
|
422
|
-
|
|
481
|
+
this.selectBackCamera();
|
|
482
|
+
this.logger.state('CAMARA_AUTO_SELECCIONADA_MOBILE', { type: 'rear' });
|
|
423
483
|
}
|
|
424
484
|
else {
|
|
425
485
|
// For desktop, prefer front camera (usually built-in webcam)
|
|
426
|
-
|
|
486
|
+
this.selectFrontCamera();
|
|
487
|
+
this.logger.state('CAMARA_AUTO_SELECCIONADA_DESKTOP', {
|
|
488
|
+
type: 'front',
|
|
489
|
+
label: this.availableCameras[0].label,
|
|
490
|
+
deviceId: this.selectedCameraId
|
|
491
|
+
});
|
|
427
492
|
}
|
|
428
493
|
}
|
|
429
494
|
updatePreferredFacing(camera) {
|
|
@@ -439,19 +504,40 @@ class CameraService {
|
|
|
439
504
|
await this.detectDeviceType();
|
|
440
505
|
}
|
|
441
506
|
const videoConstraints = {};
|
|
442
|
-
|
|
443
|
-
|
|
507
|
+
this.logger.state('CONFIGURANDO_CONSTRAINS_CAMARA', {
|
|
508
|
+
selectedCameraId: this.selectedCameraId,
|
|
509
|
+
preferredCameraFacing: this.preferredCameraFacing,
|
|
510
|
+
preferredCamera: this.preferredCamera,
|
|
511
|
+
deviceType: this.deviceType,
|
|
512
|
+
isManuallySelected: this.isManuallySelected
|
|
513
|
+
});
|
|
514
|
+
if (this.isManuallySelected && this.selectedCameraId) {
|
|
515
|
+
// When manually selected, use deviceId for exact camera selection
|
|
444
516
|
videoConstraints.deviceId = { exact: this.selectedCameraId };
|
|
517
|
+
this.logger.state('USANDO_CAMARA_MANUAL', {
|
|
518
|
+
deviceId: this.selectedCameraId
|
|
519
|
+
});
|
|
445
520
|
}
|
|
446
|
-
// Priority 2: Use facingMode as fallback when no specific camera is selected
|
|
447
521
|
else if (this.preferredCameraFacing) {
|
|
522
|
+
// Use facingMode for initial preference-based selection
|
|
523
|
+
// Use 'ideal' instead of 'exact' to avoid OverconstrainedError on desktop
|
|
448
524
|
const facingConstraint = this.deviceType === 'desktop'
|
|
449
525
|
? { ideal: this.preferredCameraFacing }
|
|
450
526
|
: { exact: this.preferredCameraFacing };
|
|
451
527
|
videoConstraints.facingMode = facingConstraint;
|
|
528
|
+
this.logger.state('USANDO_FACING_CONSTRAINT', {
|
|
529
|
+
facingMode: this.preferredCameraFacing,
|
|
530
|
+
constraintType: this.deviceType === 'desktop' ? 'ideal' : 'exact'
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
else if (this.selectedCameraId) {
|
|
534
|
+
videoConstraints.deviceId = { exact: this.selectedCameraId };
|
|
535
|
+
this.logger.state('USANDO_CAMARA_SELECCIONADA', {
|
|
536
|
+
deviceId: this.selectedCameraId
|
|
537
|
+
});
|
|
452
538
|
}
|
|
453
|
-
// Priority 3: Fallback to preferredCamera setting
|
|
454
539
|
else {
|
|
540
|
+
// Use preferredCamera setting to determine default facing mode
|
|
455
541
|
if (this.preferredCamera === 'front') {
|
|
456
542
|
videoConstraints.facingMode = "user";
|
|
457
543
|
}
|
|
@@ -459,25 +545,24 @@ class CameraService {
|
|
|
459
545
|
videoConstraints.facingMode = "environment";
|
|
460
546
|
}
|
|
461
547
|
else {
|
|
548
|
+
// Auto mode: use environment for mobile/tablet, user for desktop
|
|
462
549
|
videoConstraints.facingMode = (this.deviceType === 'mobile' || this.deviceType === 'tablet') ? "environment" : "user";
|
|
463
550
|
}
|
|
551
|
+
this.logger.state('USANDO_PREFERENCIA_CONFIGURADA', {
|
|
552
|
+
facingMode: videoConstraints.facingMode,
|
|
553
|
+
preferredCamera: this.preferredCamera,
|
|
554
|
+
deviceType: this.deviceType
|
|
555
|
+
});
|
|
464
556
|
}
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
const tempStream = await navigator.mediaDevices.getUserMedia({ video: videoConstraints });
|
|
472
|
-
const videoTrack = tempStream.getVideoTracks()[0];
|
|
473
|
-
capabilities = videoTrack.getCapabilities();
|
|
474
|
-
tempStream.getTracks().forEach(track => track.stop());
|
|
475
|
-
// Cache capabilities for future use
|
|
476
|
-
if (this.selectedCameraId) {
|
|
477
|
-
CameraService.cameraCapabilitiesCache.set(this.selectedCameraId + '_caps', JSON.stringify(capabilities));
|
|
478
|
-
}
|
|
479
|
-
}
|
|
557
|
+
const tempStream = await navigator.mediaDevices.getUserMedia({
|
|
558
|
+
video: videoConstraints
|
|
559
|
+
});
|
|
560
|
+
const videoTrack = tempStream.getVideoTracks()[0];
|
|
561
|
+
const capabilities = videoTrack.getCapabilities();
|
|
562
|
+
tempStream.getTracks().forEach(track => track.stop());
|
|
480
563
|
const constraints = { ...videoConstraints };
|
|
564
|
+
// Enhanced focus and image quality settings
|
|
565
|
+
this.applyAdvancedCameraSettings(constraints, capabilities);
|
|
481
566
|
if (capabilities.width && capabilities.height) {
|
|
482
567
|
const maxWidth = Math.min(capabilities.width.max, 1920);
|
|
483
568
|
const maxHeight = Math.min(capabilities.height.max, 1080);
|
|
@@ -491,20 +576,15 @@ class CameraService {
|
|
|
491
576
|
constraints.height = { ideal: maxHeight };
|
|
492
577
|
}
|
|
493
578
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
constraints.focusMode = 'continuous';
|
|
499
|
-
}
|
|
500
|
-
else if (capabilitiesAny.focusMode.includes('auto')) {
|
|
501
|
-
constraints.focusMode = 'auto';
|
|
502
|
-
}
|
|
503
|
-
}
|
|
579
|
+
this.logger.state('CONFIGURACION_CAMARA_OPTIMIZADA', {
|
|
580
|
+
constraints,
|
|
581
|
+
capabilities: this.getCapabilitiesSummary(capabilities)
|
|
582
|
+
});
|
|
504
583
|
return constraints;
|
|
505
584
|
}
|
|
506
585
|
catch (err) {
|
|
507
|
-
|
|
586
|
+
this.logger.warn('No se pudieron obtener capacidades de cámara, usando configuración de respaldo');
|
|
587
|
+
// Ensure device type is detected before determining constraints
|
|
508
588
|
if (!this.deviceType || this.deviceType === 'desktop') {
|
|
509
589
|
await this.detectDeviceType();
|
|
510
590
|
}
|
|
@@ -513,16 +593,25 @@ class CameraService {
|
|
|
513
593
|
width: { ideal: isTablet ? 1280 : 1920 },
|
|
514
594
|
height: { ideal: isTablet ? 720 : 1080 }
|
|
515
595
|
};
|
|
516
|
-
|
|
596
|
+
// Apply basic focus settings even for fallback
|
|
597
|
+
this.applyBasicFocusSettings(fallbackConstraints);
|
|
598
|
+
if (this.isManuallySelected && this.selectedCameraId) {
|
|
599
|
+
// When manually selected, use deviceId for exact camera selection
|
|
517
600
|
fallbackConstraints.deviceId = { exact: this.selectedCameraId };
|
|
518
601
|
}
|
|
519
602
|
else if (this.preferredCameraFacing) {
|
|
603
|
+
// Use facingMode for initial preference-based selection
|
|
604
|
+
// Use 'ideal' instead of 'exact' to avoid OverconstrainedError on desktop
|
|
520
605
|
const facingConstraint = this.deviceType === 'desktop'
|
|
521
606
|
? { ideal: this.preferredCameraFacing }
|
|
522
607
|
: { exact: this.preferredCameraFacing };
|
|
523
608
|
fallbackConstraints.facingMode = facingConstraint;
|
|
524
609
|
}
|
|
610
|
+
else if (this.selectedCameraId) {
|
|
611
|
+
fallbackConstraints.deviceId = { exact: this.selectedCameraId };
|
|
612
|
+
}
|
|
525
613
|
else {
|
|
614
|
+
// Use preferredCamera setting to determine default facing mode
|
|
526
615
|
if (this.preferredCamera === 'front') {
|
|
527
616
|
fallbackConstraints.facingMode = "user";
|
|
528
617
|
}
|
|
@@ -530,26 +619,165 @@ class CameraService {
|
|
|
530
619
|
fallbackConstraints.facingMode = "environment";
|
|
531
620
|
}
|
|
532
621
|
else {
|
|
622
|
+
// Auto mode: use environment for mobile/tablet, user for desktop
|
|
533
623
|
fallbackConstraints.facingMode = (this.deviceType === 'mobile' || this.deviceType === 'tablet') ? "environment" : "user";
|
|
534
624
|
}
|
|
535
625
|
}
|
|
536
626
|
return fallbackConstraints;
|
|
537
627
|
}
|
|
538
628
|
}
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
629
|
+
applyAdvancedCameraSettings(constraints, capabilities) {
|
|
630
|
+
// Apply standard camera settings that are widely supported
|
|
631
|
+
// Frame rate optimization
|
|
632
|
+
if (capabilities.frameRate) {
|
|
633
|
+
constraints.frameRate = {
|
|
634
|
+
ideal: 30,
|
|
635
|
+
min: 15,
|
|
636
|
+
max: 60
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
// Width and height constraints for optimal resolution
|
|
640
|
+
if (capabilities.width && capabilities.height) {
|
|
641
|
+
constraints.width = {
|
|
642
|
+
ideal: Math.min(1920, capabilities.width.max || 1920),
|
|
643
|
+
min: 640
|
|
644
|
+
};
|
|
645
|
+
constraints.height = {
|
|
646
|
+
ideal: Math.min(1080, capabilities.height.max || 1080),
|
|
647
|
+
min: 480
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
// Apply enhanced constraints through advanced array for better browser compatibility
|
|
651
|
+
const advancedConstraints = [];
|
|
652
|
+
// Try to set optimal settings through advanced constraints
|
|
653
|
+
const advanced = {};
|
|
654
|
+
// Focus mode - use 'any' type to bypass TypeScript limitations
|
|
655
|
+
if (capabilities.focusMode) {
|
|
656
|
+
if (capabilities.focusMode.includes('continuous')) {
|
|
657
|
+
advanced.focusMode = 'continuous';
|
|
658
|
+
}
|
|
659
|
+
else if (capabilities.focusMode.includes('single-shot')) {
|
|
660
|
+
advanced.focusMode = 'single-shot';
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
// Focus distance for close objects
|
|
664
|
+
if (capabilities.focusDistance) {
|
|
665
|
+
advanced.focusDistance = {
|
|
666
|
+
ideal: 0.4, // 40cm - optimal for document capture
|
|
667
|
+
min: 0.2,
|
|
668
|
+
max: 1.0
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
// Zoom control
|
|
672
|
+
if (capabilities.zoom) {
|
|
673
|
+
advanced.zoom = {
|
|
674
|
+
ideal: 1.0,
|
|
675
|
+
min: capabilities.zoom.min,
|
|
676
|
+
max: Math.min(capabilities.zoom.max, 3.0)
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
// Add advanced constraints if any were set
|
|
680
|
+
if (Object.keys(advanced).length > 0) {
|
|
681
|
+
advancedConstraints.push(advanced);
|
|
682
|
+
}
|
|
683
|
+
if (advancedConstraints.length > 0) {
|
|
684
|
+
constraints.advanced = advancedConstraints;
|
|
685
|
+
}
|
|
544
686
|
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
687
|
+
applyBasicFocusSettings(constraints) {
|
|
688
|
+
// Apply basic focus settings when capabilities are not available
|
|
689
|
+
// Use advanced constraints for non-standard properties
|
|
690
|
+
const advanced = {
|
|
691
|
+
focusMode: 'continuous',
|
|
692
|
+
exposureMode: 'continuous',
|
|
693
|
+
whiteBalanceMode: 'continuous'
|
|
694
|
+
};
|
|
695
|
+
if (!constraints.advanced) {
|
|
696
|
+
constraints.advanced = [];
|
|
697
|
+
}
|
|
698
|
+
constraints.advanced.push(advanced);
|
|
699
|
+
constraints.frameRate = { ideal: 30 };
|
|
549
700
|
}
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
701
|
+
getCapabilitiesSummary(capabilities) {
|
|
702
|
+
return {
|
|
703
|
+
hasAutoFocus: !!capabilities.focusMode,
|
|
704
|
+
hasFocusDistance: !!capabilities.focusDistance,
|
|
705
|
+
hasExposureControl: !!capabilities.exposureMode,
|
|
706
|
+
hasWhiteBalance: !!capabilities.whiteBalanceMode,
|
|
707
|
+
hasZoom: !!capabilities.zoom,
|
|
708
|
+
hasTorch: capabilities.torch !== undefined,
|
|
709
|
+
hasImageControls: !!(capabilities.brightness || capabilities.contrast || capabilities.saturation || capabilities.sharpness),
|
|
710
|
+
resolutionRange: capabilities.width && capabilities.height ?
|
|
711
|
+
`${capabilities.width.min}x${capabilities.height.min} - ${capabilities.width.max}x${capabilities.height.max}` : 'unknown'
|
|
712
|
+
};
|
|
713
|
+
}
|
|
714
|
+
// New method to enable torch/flash for better illumination
|
|
715
|
+
async setTorchEnabled(enabled, stream) {
|
|
716
|
+
try {
|
|
717
|
+
if (!stream) {
|
|
718
|
+
this.logger.warn('No hay stream disponible para controlar el flash');
|
|
719
|
+
return false;
|
|
720
|
+
}
|
|
721
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
722
|
+
if (!videoTrack) {
|
|
723
|
+
this.logger.warn('No hay track de video disponible');
|
|
724
|
+
return false;
|
|
725
|
+
}
|
|
726
|
+
const capabilities = videoTrack.getCapabilities();
|
|
727
|
+
if (capabilities.torch === undefined) {
|
|
728
|
+
this.logger.warn('El dispositivo no soporta control de flash');
|
|
729
|
+
return false;
|
|
730
|
+
}
|
|
731
|
+
await videoTrack.applyConstraints({
|
|
732
|
+
advanced: [{ torch: enabled }]
|
|
733
|
+
});
|
|
734
|
+
this.logger.state('FLASH_CONTROLADO', { enabled });
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
catch (error) {
|
|
738
|
+
this.logger.error('Error al controlar el flash:', error);
|
|
739
|
+
return false;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
// Method to manually trigger focus on a specific point
|
|
743
|
+
async focusAtPoint(x, y, stream) {
|
|
744
|
+
try {
|
|
745
|
+
if (!stream) {
|
|
746
|
+
this.logger.warn('No hay stream disponible para enfocar');
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
const videoTrack = stream.getVideoTracks()[0];
|
|
750
|
+
if (!videoTrack) {
|
|
751
|
+
this.logger.warn('No hay track de video disponible');
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
const capabilities = videoTrack.getCapabilities();
|
|
755
|
+
if (!capabilities.focusMode || !capabilities.focusMode.includes('single-shot')) {
|
|
756
|
+
this.logger.warn('El dispositivo no soporta enfoque manual');
|
|
757
|
+
return false;
|
|
758
|
+
}
|
|
759
|
+
// Trigger single-shot focus
|
|
760
|
+
await videoTrack.applyConstraints({
|
|
761
|
+
advanced: [{ focusMode: 'single-shot' }]
|
|
762
|
+
});
|
|
763
|
+
// Return to continuous focus after a short delay
|
|
764
|
+
setTimeout(async () => {
|
|
765
|
+
try {
|
|
766
|
+
await videoTrack.applyConstraints({
|
|
767
|
+
advanced: [{ focusMode: 'continuous' }]
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
catch (error) {
|
|
771
|
+
this.logger.warn('Error al volver a enfoque continuo:', error);
|
|
772
|
+
}
|
|
773
|
+
}, 1000);
|
|
774
|
+
this.logger.state('ENFOQUE_MANUAL_ACTIVADO', { x, y });
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
catch (error) {
|
|
778
|
+
this.logger.error('Error al enfocar manualmente:', error);
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
553
781
|
}
|
|
554
782
|
}
|
|
555
783
|
|
|
@@ -628,6 +856,7 @@ class DeviceStrategyFactory {
|
|
|
628
856
|
}
|
|
629
857
|
|
|
630
858
|
class DetectionService {
|
|
859
|
+
logger;
|
|
631
860
|
debug;
|
|
632
861
|
useDocumentClassification;
|
|
633
862
|
session;
|
|
@@ -644,10 +873,8 @@ class DetectionService {
|
|
|
644
873
|
preprocessCanvas;
|
|
645
874
|
preprocessCtx;
|
|
646
875
|
captureCanvas;
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
static MAX_POOL_SIZE = 5;
|
|
650
|
-
constructor(debug = false, useDocumentClassification = false) {
|
|
876
|
+
constructor(logger, debug = false, useDocumentClassification = false) {
|
|
877
|
+
this.logger = logger;
|
|
651
878
|
this.debug = debug;
|
|
652
879
|
this.useDocumentClassification = useDocumentClassification;
|
|
653
880
|
this.deviceStrategy = DeviceStrategyFactory.createStrategy();
|
|
@@ -655,15 +882,21 @@ class DetectionService {
|
|
|
655
882
|
}
|
|
656
883
|
async loadModel() {
|
|
657
884
|
if (this.modelLoaded || this.session) {
|
|
885
|
+
this.logger.state('MODELO_YA_PRECARGADO', {
|
|
886
|
+
sessionExists: !!this.session,
|
|
887
|
+
modelPreloaded: this.modelLoaded
|
|
888
|
+
});
|
|
658
889
|
return;
|
|
659
890
|
}
|
|
660
891
|
try {
|
|
892
|
+
this.logger.state('PRECARGANDO_MODELO_DETECCION', { modelPath: this.MODEL_PATH });
|
|
661
893
|
const sessionOptions = this.deviceStrategy.getSessionOptions(this.debug);
|
|
662
894
|
try {
|
|
663
895
|
this.session = await window.ort.InferenceSession.create(this.MODEL_PATH, sessionOptions);
|
|
664
896
|
}
|
|
665
897
|
catch (error) {
|
|
666
898
|
if (error.message.includes('failed to allocate a buffer')) {
|
|
899
|
+
this.logger.warn('Fallo en asignación de buffer durante precarga, intentando con configuración mínima');
|
|
667
900
|
const fallbackOptions = {
|
|
668
901
|
executionProviders: ['wasm'],
|
|
669
902
|
graphOptimizationLevel: 'disabled',
|
|
@@ -681,8 +914,10 @@ class DetectionService {
|
|
|
681
914
|
}
|
|
682
915
|
}
|
|
683
916
|
this.modelLoaded = true;
|
|
917
|
+
this.logger.state('MODELO_DETECCION_CARGADO_EXITOSAMENTE', { sessionCreated: !!this.session });
|
|
684
918
|
}
|
|
685
919
|
catch (error) {
|
|
920
|
+
this.logger.error('Error al precargar modelo de detección:', error);
|
|
686
921
|
throw error;
|
|
687
922
|
}
|
|
688
923
|
}
|
|
@@ -691,14 +926,19 @@ class DetectionService {
|
|
|
691
926
|
return;
|
|
692
927
|
}
|
|
693
928
|
try {
|
|
929
|
+
this.logger.state('CARGANDO_MODELO_MOBILENET', { path: this.MOBILENET_MODEL_PATH });
|
|
694
930
|
// Try to load class map (optional - SqueezeNet may not need it for basic classification)
|
|
695
931
|
try {
|
|
696
932
|
const classResponse = await fetch(this.MOBILENET_CLASSES_PATH);
|
|
697
933
|
if (classResponse.ok) {
|
|
698
934
|
this.mobileNetClassMap = await classResponse.json();
|
|
935
|
+
this.logger.state('CLASES_MOBILENET_CARGADAS', {
|
|
936
|
+
classCount: Object.keys(this.mobileNetClassMap).length
|
|
937
|
+
});
|
|
699
938
|
}
|
|
700
939
|
}
|
|
701
940
|
catch (error) {
|
|
941
|
+
this.logger.warn('No se pudo cargar el mapa de clases de MobileNet, usando clasificación básica');
|
|
702
942
|
// Create a basic class map for document types
|
|
703
943
|
this.mobileNetClassMap = {
|
|
704
944
|
"0": "document",
|
|
@@ -715,6 +955,7 @@ class DetectionService {
|
|
|
715
955
|
}
|
|
716
956
|
catch (error) {
|
|
717
957
|
if (error.message.includes('failed to allocate a buffer')) {
|
|
958
|
+
this.logger.warn('Fallo en asignación de buffer de MobileNet, intentando con configuración mínima');
|
|
718
959
|
const fallbackOptions = {
|
|
719
960
|
executionProviders: ['wasm'],
|
|
720
961
|
graphOptimizationLevel: 'disabled',
|
|
@@ -731,8 +972,12 @@ class DetectionService {
|
|
|
731
972
|
throw error;
|
|
732
973
|
}
|
|
733
974
|
}
|
|
975
|
+
this.logger.state('MODELO_MOBILENET_CARGADO_EXITOSAMENTE', {
|
|
976
|
+
sessionCreated: !!this.mobileNetSession
|
|
977
|
+
});
|
|
734
978
|
}
|
|
735
979
|
catch (error) {
|
|
980
|
+
this.logger.error('Error al cargar modelo MobileNet:', error);
|
|
736
981
|
throw error;
|
|
737
982
|
}
|
|
738
983
|
}
|
|
@@ -785,9 +1030,11 @@ class DetectionService {
|
|
|
785
1030
|
}
|
|
786
1031
|
async classifyDocument(canvas) {
|
|
787
1032
|
if (!this.mobileNetSession || !this.mobileNetClassMap) {
|
|
1033
|
+
this.logger.warn('Modelo MobileNet no está cargado, saltando clasificación');
|
|
788
1034
|
return null;
|
|
789
1035
|
}
|
|
790
1036
|
try {
|
|
1037
|
+
this.logger.state('CLASIFICANDO_DOCUMENTO', { timestamp: Date.now() });
|
|
791
1038
|
const inputTensor = this.preprocessMobileNet(canvas);
|
|
792
1039
|
const feeds = { [this.mobileNetSession.inputNames[0]]: inputTensor };
|
|
793
1040
|
const results = await this.mobileNetSession.run(feeds);
|
|
@@ -795,6 +1042,13 @@ class DetectionService {
|
|
|
795
1042
|
const maxIdx = output.reduce((bestIdx, val, idx, arr) => val > arr[bestIdx] ? idx : bestIdx, 0);
|
|
796
1043
|
const confidence = output[maxIdx];
|
|
797
1044
|
const className = this.mobileNetClassMap[maxIdx.toString()] || "unknown";
|
|
1045
|
+
this.logger.state('DOCUMENTO_CLASIFICADO', {
|
|
1046
|
+
class: className,
|
|
1047
|
+
confidence: confidence.toFixed(3),
|
|
1048
|
+
classIndex: maxIdx,
|
|
1049
|
+
timestamp: Date.now(),
|
|
1050
|
+
results
|
|
1051
|
+
});
|
|
798
1052
|
return {
|
|
799
1053
|
class: className,
|
|
800
1054
|
confidence: confidence,
|
|
@@ -802,6 +1056,7 @@ class DetectionService {
|
|
|
802
1056
|
};
|
|
803
1057
|
}
|
|
804
1058
|
catch (error) {
|
|
1059
|
+
this.logger.error('Error al clasificar documento:', error);
|
|
805
1060
|
return null;
|
|
806
1061
|
}
|
|
807
1062
|
}
|
|
@@ -914,6 +1169,7 @@ class DetectionService {
|
|
|
914
1169
|
}
|
|
915
1170
|
this.mobileNetClassMap = undefined;
|
|
916
1171
|
this.modelLoaded = false;
|
|
1172
|
+
this.logger.state('DETECCION_SERVICE_LIMPIADO', { timestamp: Date.now() });
|
|
917
1173
|
}
|
|
918
1174
|
initializeCanvasPool() {
|
|
919
1175
|
this.preprocessCanvas = document.createElement('canvas');
|
|
@@ -924,6 +1180,9 @@ class DetectionService {
|
|
|
924
1180
|
willReadFrequently: true
|
|
925
1181
|
});
|
|
926
1182
|
this.captureCanvas = document.createElement('canvas');
|
|
1183
|
+
this.logger.state('CANVAS_POOL_INICIALIZADO', {
|
|
1184
|
+
preprocessCanvasSize: this.INPUT_SIZE
|
|
1185
|
+
});
|
|
927
1186
|
}
|
|
928
1187
|
cleanupCanvasPool() {
|
|
929
1188
|
if (this.preprocessCanvas) {
|
|
@@ -959,11 +1218,10 @@ class DetectionService {
|
|
|
959
1218
|
return (sign << 15) | (newExp << 10) | (frac >> 13);
|
|
960
1219
|
}
|
|
961
1220
|
preprocessMobileNet(canvas) {
|
|
962
|
-
|
|
963
|
-
|
|
1221
|
+
const tempCanvas = document.createElement('canvas');
|
|
1222
|
+
tempCanvas.width = 224;
|
|
1223
|
+
tempCanvas.height = 224;
|
|
964
1224
|
const tempCtx = tempCanvas.getContext('2d');
|
|
965
|
-
// Clear canvas before use
|
|
966
|
-
tempCtx.clearRect(0, 0, 224, 224);
|
|
967
1225
|
// Resize image to 224x224 for SqueezeNet (using MobileNet interface)
|
|
968
1226
|
tempCtx.drawImage(canvas, 0, 0, 224, 224);
|
|
969
1227
|
const imageData = tempCtx.getImageData(0, 0, 224, 224);
|
|
@@ -979,53 +1237,8 @@ class DetectionService {
|
|
|
979
1237
|
// Blue channel
|
|
980
1238
|
arr[2 * hw + i] = data[i * 4 + 2] / 255.0;
|
|
981
1239
|
}
|
|
982
|
-
// Return canvas to pool after use
|
|
983
|
-
this.returnCanvas(tempCanvas);
|
|
984
1240
|
return new window.ort.Tensor('float32', arr, [1, 3, 224, 224]);
|
|
985
1241
|
}
|
|
986
|
-
// Canvas pool management methods for optimization
|
|
987
|
-
getCanvas(width, height) {
|
|
988
|
-
const key = `${width}x${height}`;
|
|
989
|
-
if (!DetectionService.canvasPool.has(key)) {
|
|
990
|
-
DetectionService.canvasPool.set(key, []);
|
|
991
|
-
}
|
|
992
|
-
const pool = DetectionService.canvasPool.get(key);
|
|
993
|
-
let canvas = pool.pop();
|
|
994
|
-
if (!canvas) {
|
|
995
|
-
canvas = document.createElement('canvas');
|
|
996
|
-
canvas.width = width;
|
|
997
|
-
canvas.height = height;
|
|
998
|
-
}
|
|
999
|
-
else {
|
|
1000
|
-
// Clear the canvas before reuse
|
|
1001
|
-
const ctx = canvas.getContext('2d');
|
|
1002
|
-
ctx.clearRect(0, 0, width, height);
|
|
1003
|
-
}
|
|
1004
|
-
return canvas;
|
|
1005
|
-
}
|
|
1006
|
-
returnCanvas(canvas) {
|
|
1007
|
-
const key = `${canvas.width}x${canvas.height}`;
|
|
1008
|
-
if (!DetectionService.canvasPool.has(key)) {
|
|
1009
|
-
DetectionService.canvasPool.set(key, []);
|
|
1010
|
-
}
|
|
1011
|
-
const pool = DetectionService.canvasPool.get(key);
|
|
1012
|
-
// Only return to pool if not at max capacity
|
|
1013
|
-
if (pool.length < DetectionService.MAX_POOL_SIZE) {
|
|
1014
|
-
pool.push(canvas);
|
|
1015
|
-
}
|
|
1016
|
-
}
|
|
1017
|
-
// Static method to clear all canvas pools (useful for memory management)
|
|
1018
|
-
static clearCanvasPools() {
|
|
1019
|
-
DetectionService.canvasPool.clear();
|
|
1020
|
-
}
|
|
1021
|
-
// Method to get pool statistics for debugging
|
|
1022
|
-
getPoolStats() {
|
|
1023
|
-
const stats = {};
|
|
1024
|
-
DetectionService.canvasPool.forEach((pool, key) => {
|
|
1025
|
-
stats[key] = pool.length;
|
|
1026
|
-
});
|
|
1027
|
-
return stats;
|
|
1028
|
-
}
|
|
1029
1242
|
}
|
|
1030
1243
|
|
|
1031
1244
|
class ServiceContainer {
|
|
@@ -1036,11 +1249,13 @@ class ServiceContainer {
|
|
|
1036
1249
|
initializeServices(config) {
|
|
1037
1250
|
// Initialize services in dependency order
|
|
1038
1251
|
const eventBus = new EventBusService();
|
|
1252
|
+
const logger = new LoggerService(config.debug);
|
|
1039
1253
|
const stateManager = new StateManagerService(eventBus);
|
|
1040
|
-
const cameraService = new CameraService(eventBus, config.preferredCamera);
|
|
1041
|
-
const detectionService = new DetectionService(config.debug, config.useDocumentClassification);
|
|
1254
|
+
const cameraService = new CameraService(logger, eventBus, config.preferredCamera);
|
|
1255
|
+
const detectionService = new DetectionService(logger, config.debug, config.useDocumentClassification);
|
|
1042
1256
|
// Register services
|
|
1043
1257
|
this.services.set('eventBus', eventBus);
|
|
1258
|
+
this.services.set('logger', logger);
|
|
1044
1259
|
this.services.set('stateManager', stateManager);
|
|
1045
1260
|
this.services.set('cameraService', cameraService);
|
|
1046
1261
|
this.services.set('detectionService', detectionService);
|
|
@@ -1052,6 +1267,9 @@ class ServiceContainer {
|
|
|
1052
1267
|
}
|
|
1053
1268
|
return service;
|
|
1054
1269
|
}
|
|
1270
|
+
getLogger() {
|
|
1271
|
+
return this.get('logger');
|
|
1272
|
+
}
|
|
1055
1273
|
getEventBus() {
|
|
1056
1274
|
return this.get('eventBus');
|
|
1057
1275
|
}
|
|
@@ -1065,6 +1283,10 @@ class ServiceContainer {
|
|
|
1065
1283
|
return this.get('detectionService');
|
|
1066
1284
|
}
|
|
1067
1285
|
updateConfig(config) {
|
|
1286
|
+
if (config.debug !== undefined) {
|
|
1287
|
+
const logger = this.services.get('logger');
|
|
1288
|
+
logger.setDebugMode(config.debug);
|
|
1289
|
+
}
|
|
1068
1290
|
}
|
|
1069
1291
|
cleanup() {
|
|
1070
1292
|
// Cleanup services in reverse order
|
|
@@ -1074,7 +1296,7 @@ class ServiceContainer {
|
|
|
1074
1296
|
}
|
|
1075
1297
|
}
|
|
1076
1298
|
|
|
1077
|
-
const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);color:#fff;font-size:14px;font-weight:600;text-align:center;white-space:normal;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);padding:12px 20px;border-radius:8px;max-width:300px;z-index:20;border:1px solid rgba(255, 255, 255, 0.1)}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(255, 255, 255, 0.3);border-top:2px solid #ffffff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:flex-start;align-items:center;gap:8px;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:0;font-weight:400;display:flex;align-items:center;gap:6px}.camera-label .autofocus-icon{font-size:12px;color:#ffffff !important;opacity:0.8;flex-shrink:0}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9;flex-shrink:0}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}";
|
|
1299
|
+
const myComponentCss = ":host{display:block;width:100%;height:100%;font-family:system-ui, -apple-system, sans-serif;color:#1a1a1a}.detector-container{display:flex;flex-direction:column;align-items:center;width:100%;height:100%}h1{font-size:24px;font-weight:500;color:#333;margin:0 0 24px 0}.video-container{position:relative;width:100%;height:100%;background:#333;border:1px solid #e0e0e0;border-radius:8px;overflow:hidden}video,.detection-overlay{position:absolute;width:100%;height:100%;border-radius:8px}video.mirror,.detection-overlay.mirror{transform:rotateY(180deg)}.detection-overlay{z-index:1;pointer-events:none}video{z-index:0}.detection-box{transition:opacity 0.2s ease}.status{margin-top:16px;font-size:12px;color:#666}.overlay-mask{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10;display:block;pointer-events:none}.card-outline{position:absolute;top:var(--mask-center-y, 50%);left:var(--mask-center-x, 50%);transform:translate(-50%, -50%);width:var(--mask-width, 88%);height:var(--mask-height, 55%);border:none;border-radius:4px;background:transparent;opacity:0.8}.side{position:absolute;background:#999;transition:background-color 0.3s ease;border-radius:1px}.side-top.aligned{border-left-color:#28a745;border-top-color:#28a745}.side-right.aligned{border-right-color:#28a745;border-top-color:#28a745}.side-bottom.aligned{border-left-color:#28a745;border-bottom-color:#28a745}.side-left.aligned{border-right-color:#28a745;border-bottom-color:#28a745}.side-top{top:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-right{top:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-top:6px solid #ffffff;background:transparent}.side-bottom{bottom:-3px;left:-3px;width:30px;height:30px;border-left:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.side-left{bottom:-3px;right:-3px;width:30px;height:30px;border-right:6px solid #ffffff;border-bottom:6px solid #ffffff;background:transparent}.guide-text{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);color:#fff;font-size:14px;font-weight:600;text-align:center;white-space:normal;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);padding:12px 20px;border-radius:8px;max-width:300px;z-index:20;border:1px solid rgba(255, 255, 255, 0.1)}.quality-indicator{position:absolute;top:20px;right:20px;display:flex;align-items:center;gap:8px;background:rgba(0, 0, 0, 0.8);padding:8px 12px;border-radius:20px;z-index:25;transition:all 0.3s ease}.quality-indicator.warning{background:rgba(255, 152, 0, 0.9)}.quality-indicator.good{background:rgba(76, 175, 80, 0.9)}.quality-score{color:#fff;font-size:12px;font-weight:600;min-width:30px;text-align:center}.quality-status{width:20px;height:20px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:bold;color:#fff;transition:all 0.3s ease}.quality-status.ready{background:#4CAF50}.quality-status.not-ready{background:#f44336}.card-outline.quality-warning{animation:qualityWarning 1s ease-in-out infinite alternate}@keyframes qualityWarning{0%{box-shadow:0 0 0 3px rgba(255, 152, 0, 0.6)}100%{box-shadow:0 0 0 6px rgba(255, 152, 0, 0.3)}}.capture-animation{position:absolute;top:0;left:0;width:100%;height:100%;background:#fff;opacity:0;z-index:30;pointer-events:none;animation:captureFlash 0.6s ease-out}@keyframes captureFlash{0%{opacity:0}15%{opacity:0.8}30%{opacity:0}45%{opacity:0.4}60%{opacity:0}100%{opacity:0}}.card-outline.capturing{animation:pulseGreen 0.6s ease-out}@keyframes pulseGreen{0%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}50%{border-color:#28a745;box-shadow:0 0 0 8px rgba(40, 167, 69, 0.6)}100%{border-color:#28a745;box-shadow:0 0 0 4px rgba(40, 167, 69, 0)}}.flip-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showFlipInstruction 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.id-card-icon{width:80px;height:50px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:8px;position:relative;animation:flipCard 2s ease-in-out infinite;box-shadow:0 4px 12px rgba(0, 0, 0, 0.3)}.id-card-icon::before{content:'';position:absolute;top:8px;left:8px;width:16px;height:12px;background:rgba(255, 255, 255, 0.9);border-radius:2px}.id-card-icon::after{content:'';position:absolute;top:25px;left:8px;width:64px;height:3px;background:rgba(255, 255, 255, 0.7);border-radius:1px;box-shadow:0 6px 0 rgba(255, 255, 255, 0.7), 0 12px 0 rgba(255, 255, 255, 0.7)}.flip-text{margin-top:12px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px}@keyframes showFlipInstruction{0%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.8)}}@keyframes flipCard{0%{transform:rotateY(0deg)}50%{transform:rotateY(180deg)}100%{transform:rotateY(360deg)}}.success-animation{position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);z-index:35;pointer-events:none;opacity:0;animation:showSuccessMessage 3s ease-in-out;display:flex;flex-direction:column;align-items:center;justify-content:center}.check-icon{width:80px;height:80px;background:linear-gradient(135deg, #6c757d 0%, #495057 100%);border-radius:50%;position:relative;animation:bounceSuccess 0.6s ease-out;box-shadow:0 4px 16px rgba(108, 117, 125, 0.4);display:flex;align-items:center;justify-content:center}.check-icon::before{content:'';position:absolute;width:20px;height:35px;border:4px solid #fff;border-top:none;border-left:none;transform:rotate(45deg);animation:drawCheck 0.4s ease-out 0.2s both}.success-text{margin-top:16px;color:#fff;font-size:14px;font-weight:600;text-align:center;background:rgba(128, 128, 128, 0.8);padding:12px 20px;border-radius:20px;max-width:300px;animation:fadeInUp 0.5s ease-out 0.4s both}@keyframes showSuccessMessage{0%{opacity:0;transform:translate(-50%, -50%) scale(0.5)}15%{opacity:1;transform:translate(-50%, -50%) scale(1)}85%{opacity:1;transform:translate(-50%, -50%) scale(1)}100%{opacity:0;transform:translate(-50%, -50%) scale(0.9)}}@keyframes bounceSuccess{0%{transform:scale(0)}50%{transform:scale(1.1)}100%{transform:scale(1)}}@keyframes drawCheck{0%{width:0;height:0}50%{width:20px;height:0}100%{width:20px;height:35px}}@keyframes fadeInUp{0%{opacity:0;transform:translateY(20px)}100%{opacity:1;transform:translateY(0)}}.skip-section{position:absolute;bottom:20px;left:50%;transform:translateX(-50%);z-index:25;display:flex;flex-direction:column;align-items:center;width:100%;max-width:300px}.skip-explanation{background:rgba(0, 0, 0, 0.5);color:#ffffff;padding:10px 16px;border-radius:8px;font-size:14px;font-weight:500;text-align:center;margin-bottom:12px;box-shadow:0 2px 8px rgba(0, 0, 0, 0.2);backdrop-filter:blur(12px);border:1px solid rgba(255, 255, 255, 0.1);animation:fadeIn 0.3s ease-in-out}@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}.skip-button{pointer-events:auto;background:#fff;color:#333;border:none;border-radius:25px;padding:12px 24px;font-size:14px;font-weight:600;cursor:pointer;transition:all 0.3s ease}.skip-button:hover{background:#f8f9fa}.skip-button:active{background:#e9ecef;transform:translateY(1px)}.camera-controls{position:absolute;top:16px;right:16px;z-index:25;display:flex;gap:8px;pointer-events:auto}.camera-selector-button{height:32px;padding:0 10px;border:none;border-radius:8px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(12px);color:#ffffff;font-size:12px;font-weight:500;cursor:pointer;transition:all 0.2s ease;display:flex;align-items:center;justify-content:center;border:1px solid rgba(255, 255, 255, 0.1);white-space:nowrap;min-width:fit-content}.camera-selector-button:hover{background:rgba(0, 0, 0, 0.8);border-color:rgba(255, 255, 255, 0.2);transform:translateY(-1px)}.camera-selector-button:active{transform:translateY(0);background:rgba(0, 0, 0, 0.9)}.camera-selector-button:disabled,.camera-selector-button.loading{opacity:0.7;cursor:not-allowed;pointer-events:none}.camera-selector-button:disabled:hover,.camera-selector-button.loading:hover{transform:none;background:rgba(0, 0, 0, 0.6);border-color:rgba(255, 255, 255, 0.1)}.button-spinner{width:16px;height:16px;border:2px solid rgba(255, 255, 255, 0.3);border-top:2px solid #ffffff;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.camera-selector-dropdown{position:absolute;top:56px;right:16px;z-index:30;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;min-width:260px;max-width:300px;border:1px solid rgba(255, 255, 255, 0.1);overflow:hidden;pointer-events:auto;animation:slideInFromTop 0.3s ease-out}@keyframes slideInFromTop{0%{opacity:0;transform:translateY(-8px) scale(0.95)}100%{opacity:1;transform:translateY(0) scale(1)}}.camera-selector-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;border-bottom:1px solid rgba(255, 255, 255, 0.1)}.camera-selector-header span{font-weight:500;color:#ffffff;font-size:13px;opacity:0.9}.close-selector{width:20px;height:20px;border:none;background:none;color:rgba(255, 255, 255, 0.7);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;border-radius:4px;transition:all 0.2s ease}.close-selector:hover{background:rgba(255, 255, 255, 0.1);color:#ffffff}.camera-list{padding:4px 0;max-height:240px;overflow-y:auto}.camera-option{width:100%;padding:8px 12px;border:none;background:none;text-align:left;cursor:pointer;transition:all 0.2s ease;display:flex;justify-content:space-between;align-items:center;color:rgba(255, 255, 255, 0.9)}.camera-option:hover{background:rgba(255, 255, 255, 0.08)}.camera-option.selected{background:rgba(255, 255, 255, 0.12);color:#ffffff}.camera-label{font-size:13px;flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-right:8px;font-weight:400}.selected-indicator{font-size:14px;color:#ffffff;opacity:0.9}.device-info{padding:6px 12px;border-top:1px solid rgba(255, 255, 255, 0.1);background:rgba(255, 255, 255, 0.05)}.device-info small{color:rgba(255, 255, 255, 0.6);font-size:11px;text-transform:capitalize;font-weight:400}@media (max-width: 480px){.camera-controls{top:12px;right:12px;gap:6px}.camera-selector-button{height:36px;padding:0 12px;font-size:11px;border-radius:6px}.camera-selector-dropdown{right:12px;top:48px;min-width:240px;max-width:calc(100vw - 24px)}.camera-selector-header{padding:6px 10px}.camera-option{padding:6px 10px}.device-info{padding:4px 10px}}.watermark{position:absolute;bottom:12px;right:12px;z-index:15;pointer-events:none;opacity:0.7}.watermark img{height:24px;width:auto;filter:drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))}.component-status{position:absolute;top:16px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);display:flex;align-items:center;gap:6px;padding:6px 10px;z-index:40;height:32px;width:fit-content;animation:slideInFromTop 0.3s ease-out}.status-spinner{width:16px;height:16px;border:1px solid rgba(255, 255, 255, 0.3);border-top:1px solid #ffffff;border-radius:50%;animation:statusSpin 1s linear infinite;flex-shrink:0}.status-content{display:flex;flex-direction:column;gap:1px}.status-message{color:#ffffff;font-size:12px;font-weight:500;margin:0}.status-description{color:rgba(255, 255, 255, 0.7);font-size:10px;line-height:1.2;margin:0}@keyframes statusSpin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}@media (max-width: 480px){.component-status{top:12px;left:12px;padding:4px 8px;gap:4px;height:28px;border-radius:8px}.status-spinner{width:14px;height:14px}.status-message{font-size:11px}.status-description{font-size:9px}}.loading-overlay{position:absolute;top:0;left:0;width:100%;height:100%;background:rgba(0, 0, 0, 0.85);backdrop-filter:blur(4px);display:flex;flex-direction:column;align-items:center;justify-content:center;z-index:30;border-radius:8px}.loading-spinner{width:50px;height:50px;border:3px solid rgba(255, 255, 255, 0.15);border-top:3px solid #28a745;border-radius:50%;animation:spin 1s ease-in-out infinite;margin-bottom:16px}.loading-text{color:#ffffff;font-size:14px;font-weight:500;text-align:center;opacity:0.9;margin-bottom:4px}.loading-description{color:rgba(255, 255, 255, 0.7);font-size:12px;text-align:center;opacity:0.8}@keyframes spin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}.performance-monitor{position:absolute;top:70px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}@keyframes slideInFromLeft{0%{opacity:0;transform:translateX(-20px) scale(0.95)}100%{opacity:1;transform:translateX(0) scale(1)}}.performance-expanded{padding:6px 10px}.metrics-row{display:flex;gap:8px;margin-bottom:4px}.metrics-row:last-child{margin-bottom:0}.metric-compact{display:flex;flex-direction:column;align-items:center;gap:2px;min-width:50px}.metric-label{font-size:9px;color:rgba(255, 255, 255, 0.6);font-weight:500;text-transform:uppercase;letter-spacing:0.3px}.metric-value{font-size:10px;font-weight:600;font-family:'Monaco', 'Menlo', 'Consolas', monospace;padding:2px 4px;border-radius:3px;text-align:center;white-space:nowrap}.metric-value.good{color:#4ade80;background:rgba(74, 222, 128, 0.1);border:1px solid rgba(74, 222, 128, 0.2)}.metric-value.warning{color:#fbbf24;background:rgba(251, 191, 36, 0.1);border:1px solid rgba(251, 191, 36, 0.2)}.metric-value.danger{color:#f87171;background:rgba(248, 113, 113, 0.1);border:1px solid rgba(248, 113, 113, 0.2)}@media (max-width: 480px){.performance-monitor{top:60px;left:12px;width:fit-content}.performance-expanded{padding:4px 8px}.metrics-row{gap:6px;margin-bottom:3px}.metric-compact{min-width:45px;gap:1px}.metric-label{font-size:8px}.metric-value{font-size:9px;padding:1px 3px}}.quality-monitor{position:absolute;top:200px;left:16px;background:rgba(0, 0, 0, 0.25);backdrop-filter:blur(20px);border-radius:12px;border:1px solid rgba(255, 255, 255, 0.1);z-index:35;width:fit-content;animation:slideInFromLeft 0.3s ease-out;transition:all 0.3s ease}.quality-text{padding:6px 10px;font-size:11px;color:white;font-family:'Monaco', 'Menlo', 'Consolas', monospace;line-height:1.4}.quality-fail{color:#ff9999}@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.7}}.metric-value.danger{animation:pulse 2s infinite}@media (max-width: 480px){.quality-monitor{top:160px;left:12px}.quality-text{padding:4px 8px;font-size:10px}}";
|
|
1078
1300
|
|
|
1079
1301
|
const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H {
|
|
1080
1302
|
constructor() {
|
|
@@ -1106,16 +1328,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1106
1328
|
showCameraSelector = false;
|
|
1107
1329
|
isSwitchingCamera = false;
|
|
1108
1330
|
hasDocumentDetected = false;
|
|
1109
|
-
cameraInfoWithAutofocus = {
|
|
1110
|
-
availableCameras: [],
|
|
1111
|
-
selectedCameraId: null,
|
|
1112
|
-
deviceType: 'desktop',
|
|
1113
|
-
isMultipleCamerasAvailable: false,
|
|
1114
|
-
preferredFacing: null
|
|
1115
|
-
};
|
|
1116
1331
|
currentStatus = {
|
|
1117
|
-
message: '
|
|
1118
|
-
description: 'Configurando
|
|
1332
|
+
message: 'Inicializando componente...',
|
|
1333
|
+
description: 'Configurando servicios y cargando recursos',
|
|
1119
1334
|
type: 'initializing',
|
|
1120
1335
|
isInitialized: false
|
|
1121
1336
|
};
|
|
@@ -1132,6 +1347,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1132
1347
|
backDocumentTimerRemaining = 0;
|
|
1133
1348
|
// Services
|
|
1134
1349
|
serviceContainer;
|
|
1350
|
+
logger;
|
|
1135
1351
|
eventBus;
|
|
1136
1352
|
stateManager;
|
|
1137
1353
|
cameraService;
|
|
@@ -1162,25 +1378,19 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1162
1378
|
lastUpdateTime: 0
|
|
1163
1379
|
};
|
|
1164
1380
|
performanceUpdateInterval;
|
|
1165
|
-
// Performance optimization
|
|
1381
|
+
// Performance optimization
|
|
1166
1382
|
frameSkipCounter = 0;
|
|
1167
|
-
|
|
1168
|
-
MAX_FRAME_SKIP = 8;
|
|
1383
|
+
FRAME_SKIP = 2;
|
|
1169
1384
|
consecutiveFailures = 0;
|
|
1170
1385
|
MAX_FAILURES = 30;
|
|
1171
1386
|
lastInferenceTime = 0;
|
|
1172
1387
|
MIN_INFERENCE_INTERVAL = 50;
|
|
1173
|
-
performanceHistory = [];
|
|
1174
|
-
PERFORMANCE_HISTORY_SIZE = 10;
|
|
1175
|
-
// Canvas pool for optimized screenshot capture
|
|
1176
|
-
canvasPool = [];
|
|
1177
|
-
MAX_CANVAS_POOL_SIZE = 3;
|
|
1178
1388
|
async componentDidLoad() {
|
|
1179
|
-
this.updateStatus('
|
|
1389
|
+
this.updateStatus('Iniciando servicios...', 'Configurando módulos internos', 'initializing');
|
|
1180
1390
|
await this.initializeServices();
|
|
1181
|
-
this.updateStatus('
|
|
1391
|
+
this.updateStatus('Configurando eventos...', 'Preparando comunicación entre servicios', 'initializing');
|
|
1182
1392
|
await this.setupEventListeners();
|
|
1183
|
-
this.updateStatus('
|
|
1393
|
+
this.updateStatus('Inicializando cámara...', 'Detectando dispositivos disponibles', 'initializing');
|
|
1184
1394
|
await this.initializeComponent();
|
|
1185
1395
|
if (this.debug) {
|
|
1186
1396
|
this.initializePerformanceMonitor();
|
|
@@ -1197,23 +1407,33 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1197
1407
|
captureDelay: this.captureDelay
|
|
1198
1408
|
};
|
|
1199
1409
|
this.serviceContainer = new ServiceContainer(config);
|
|
1410
|
+
this.logger = this.serviceContainer.getLogger();
|
|
1200
1411
|
this.eventBus = this.serviceContainer.getEventBus();
|
|
1201
1412
|
this.stateManager = this.serviceContainer.getStateManager();
|
|
1202
1413
|
this.cameraService = this.serviceContainer.getCameraService();
|
|
1203
1414
|
this.detectionService = this.serviceContainer.getDetectionService();
|
|
1415
|
+
this.logger.state('SERVICIOS_INICIALIZADOS', { timestamp: Date.now() });
|
|
1204
1416
|
}
|
|
1205
1417
|
async setupEventListeners() {
|
|
1206
1418
|
this.eventBus.on('state-changed', (data) => {
|
|
1207
1419
|
this.handleStateChange(data);
|
|
1208
1420
|
});
|
|
1209
|
-
this.eventBus.on('camera-changed', () => {
|
|
1210
|
-
|
|
1421
|
+
this.eventBus.on('camera-changed', (cameraId) => {
|
|
1422
|
+
this.logger.state('CAMARA_CAMBIADA_EVENT', { cameraId });
|
|
1211
1423
|
});
|
|
1212
|
-
this.eventBus.on('error', () => {
|
|
1213
|
-
|
|
1424
|
+
this.eventBus.on('error', (error) => {
|
|
1425
|
+
this.logger.error('Error en servicio:', error);
|
|
1214
1426
|
});
|
|
1215
1427
|
}
|
|
1216
1428
|
async initializeComponent() {
|
|
1429
|
+
this.logger.state('COMPONENTE_INICIALIZANDO', {
|
|
1430
|
+
debug: this.debug,
|
|
1431
|
+
maskSize: this.maskSize,
|
|
1432
|
+
cropMargin: this.cropMargin,
|
|
1433
|
+
useDocumentClassification: this.useDocumentClassification,
|
|
1434
|
+
preferredCamera: this.preferredCamera,
|
|
1435
|
+
captureDelay: this.captureDelay
|
|
1436
|
+
});
|
|
1217
1437
|
this.validateProps();
|
|
1218
1438
|
if (this.debug) {
|
|
1219
1439
|
this.stateManager.updateCaptureState({ isLoading: true });
|
|
@@ -1222,29 +1442,32 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1222
1442
|
this.updateStatus('Detectando cámaras...', 'Buscando dispositivos de captura', 'initializing');
|
|
1223
1443
|
await this.cameraService.detectDeviceType();
|
|
1224
1444
|
await this.cameraService.enumerateDevices();
|
|
1225
|
-
|
|
1226
|
-
this.updateStatus('Preparando reconocimiento...', 'Configurando detección de documentos', 'initializing');
|
|
1445
|
+
this.updateStatus('Cargando modelo IA...', 'Preparando reconocimiento de documentos', 'initializing');
|
|
1227
1446
|
await this.loadOnnxRuntime();
|
|
1228
1447
|
this.initializeResizeObserver();
|
|
1229
1448
|
}
|
|
1230
1449
|
validateProps() {
|
|
1231
1450
|
if (this.maskSize < 50 || this.maskSize > 100) {
|
|
1451
|
+
this.logger.warn(`Propiedad maskSize inválida. Valor: ${this.maskSize}, esperado: 50-100. Usando valor por defecto: 90`);
|
|
1232
1452
|
this.maskSize = 90;
|
|
1233
1453
|
}
|
|
1234
1454
|
if (this.cropMargin < 0 || this.cropMargin > 100) {
|
|
1455
|
+
this.logger.warn(`Propiedad cropMargin inválida. Valor: ${this.cropMargin}, esperado: 0-100. Usando valor por defecto: 0`);
|
|
1235
1456
|
this.cropMargin = 0;
|
|
1236
1457
|
}
|
|
1237
1458
|
const validOptions = ['auto', 'front', 'back'];
|
|
1238
1459
|
if (!validOptions.includes(this.preferredCamera)) {
|
|
1460
|
+
this.logger.warn(`Propiedad preferredCamera inválida. Valor: ${this.preferredCamera}, esperado: ${validOptions.join(', ')}. Usando valor por defecto: 'auto'`);
|
|
1239
1461
|
this.preferredCamera = 'auto';
|
|
1240
1462
|
}
|
|
1241
1463
|
if (this.captureDelay < 0 || this.captureDelay > 10000) {
|
|
1464
|
+
this.logger.warn(`Propiedad captureDelay inválida. Valor: ${this.captureDelay}, esperado: 0-10000. Usando valor por defecto: 1500`);
|
|
1242
1465
|
this.captureDelay = 1500;
|
|
1243
1466
|
}
|
|
1244
1467
|
}
|
|
1245
1468
|
async loadOnnxRuntime() {
|
|
1246
1469
|
if (!window.ort) {
|
|
1247
|
-
this.updateStatus('
|
|
1470
|
+
this.updateStatus('Descargando librerías...', 'Obteniendo recursos de reconocimiento', 'initializing');
|
|
1248
1471
|
const script = document.createElement('script');
|
|
1249
1472
|
script.src = 'https://cdn.jsdelivr.net/npm/onnxruntime-web/dist/ort.min.js';
|
|
1250
1473
|
document.head.appendChild(script);
|
|
@@ -1284,6 +1507,11 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1284
1507
|
emitReadyEvent() {
|
|
1285
1508
|
const isDocumentReady = !!window.ort && this.detectionService.isModelLoaded();
|
|
1286
1509
|
this.isReady.emit(isDocumentReady);
|
|
1510
|
+
this.logger.state('COMPONENTE_LISTO', {
|
|
1511
|
+
ortLibraryLoaded: !!window.ort,
|
|
1512
|
+
modelPreloaded: this.detectionService.isModelLoaded(),
|
|
1513
|
+
isReady: isDocumentReady
|
|
1514
|
+
});
|
|
1287
1515
|
}
|
|
1288
1516
|
handleStateChange(data) {
|
|
1289
1517
|
}
|
|
@@ -1300,6 +1528,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1300
1528
|
const container = this.detectionContainer.parentElement;
|
|
1301
1529
|
const rect = container.getBoundingClientRect();
|
|
1302
1530
|
this.updateMaskDimensions(rect);
|
|
1531
|
+
this.logger.debug('Container redimensionado:', { width: rect.width, height: rect.height });
|
|
1303
1532
|
}
|
|
1304
1533
|
}
|
|
1305
1534
|
updateMaskDimensions(containerRect) {
|
|
@@ -1353,38 +1582,16 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1353
1582
|
this.el.style.setProperty('--mask-center-x', `${videoCenterXPercent}%`);
|
|
1354
1583
|
this.el.style.setProperty('--mask-center-y', `${videoCenterYPercent}%`);
|
|
1355
1584
|
this.isMaskReady = true;
|
|
1585
|
+
this.logger.state('DIMENSIONES_MASCARA_ACTUALIZADAS', {
|
|
1586
|
+
video: { width: videoWidth, height: videoHeight },
|
|
1587
|
+
displayed: { width: displayedVideoWidth, height: displayedVideoHeight },
|
|
1588
|
+
mask: { widthPercent: maskWidthPercent, heightPercent: maskHeightPercent },
|
|
1589
|
+
center: { x: videoCenterXPercent, y: videoCenterYPercent },
|
|
1590
|
+
offset: { x: videoOffsetX, y: videoOffsetY }
|
|
1591
|
+
});
|
|
1356
1592
|
}
|
|
1357
1593
|
// PUBLIC METHODS
|
|
1358
|
-
// Helper method to check if component is ready for operations
|
|
1359
|
-
isComponentReady() {
|
|
1360
|
-
if (!this.currentStatus.isInitialized) {
|
|
1361
|
-
return {
|
|
1362
|
-
ready: false,
|
|
1363
|
-
message: 'El componente aún se está inicializando',
|
|
1364
|
-
status: 'initializing'
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
if (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') {
|
|
1368
|
-
return {
|
|
1369
|
-
ready: false,
|
|
1370
|
-
message: 'El componente está cargando recursos',
|
|
1371
|
-
status: 'loading'
|
|
1372
|
-
};
|
|
1373
|
-
}
|
|
1374
|
-
if (!this.serviceContainer || !this.stateManager || !this.cameraService || !this.detectionService) {
|
|
1375
|
-
return {
|
|
1376
|
-
ready: false,
|
|
1377
|
-
message: 'Los servicios del componente no están disponibles',
|
|
1378
|
-
status: 'error'
|
|
1379
|
-
};
|
|
1380
|
-
}
|
|
1381
|
-
return { ready: true };
|
|
1382
|
-
}
|
|
1383
1594
|
async getCapturedImages() {
|
|
1384
|
-
const readyCheck = this.isComponentReady();
|
|
1385
|
-
if (!readyCheck.ready) {
|
|
1386
|
-
throw new Error(readyCheck.message);
|
|
1387
|
-
}
|
|
1388
1595
|
const state = this.stateManager.getCaptureState();
|
|
1389
1596
|
if (state.step !== 'completed') {
|
|
1390
1597
|
throw new Error('El proceso de captura no ha sido completado');
|
|
@@ -1392,128 +1599,47 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1392
1599
|
return this.stateManager.getCapturedImages();
|
|
1393
1600
|
}
|
|
1394
1601
|
async isProcessCompleted() {
|
|
1395
|
-
const readyCheck = this.isComponentReady();
|
|
1396
|
-
if (!readyCheck.ready) {
|
|
1397
|
-
return false; // Return false instead of error for this method
|
|
1398
|
-
}
|
|
1399
1602
|
return this.stateManager.isProcessCompleted();
|
|
1400
1603
|
}
|
|
1401
1604
|
async startCapture() {
|
|
1402
|
-
|
|
1403
|
-
if (!readyCheck.ready) {
|
|
1404
|
-
this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
|
|
1405
|
-
return { success: false, error: readyCheck.message };
|
|
1406
|
-
}
|
|
1407
|
-
try {
|
|
1408
|
-
await this.startDetection();
|
|
1409
|
-
return { success: true };
|
|
1410
|
-
}
|
|
1411
|
-
catch (error) {
|
|
1412
|
-
this.updateStatus('Error al iniciar captura', error.message, 'error');
|
|
1413
|
-
return { success: false, error: error.message };
|
|
1414
|
-
}
|
|
1605
|
+
await this.startDetection();
|
|
1415
1606
|
}
|
|
1416
1607
|
async stopCapture() {
|
|
1417
|
-
|
|
1418
|
-
if (!readyCheck.ready) {
|
|
1419
|
-
return { success: false, error: readyCheck.message };
|
|
1420
|
-
}
|
|
1421
|
-
try {
|
|
1422
|
-
this.exitSession();
|
|
1423
|
-
return { success: true };
|
|
1424
|
-
}
|
|
1425
|
-
catch (error) {
|
|
1426
|
-
return { success: false, error: error.message };
|
|
1427
|
-
}
|
|
1608
|
+
this.exitSession();
|
|
1428
1609
|
}
|
|
1429
1610
|
async resetCapture() {
|
|
1430
|
-
|
|
1431
|
-
if (!readyCheck.ready) {
|
|
1432
|
-
return { success: false, error: readyCheck.message };
|
|
1433
|
-
}
|
|
1434
|
-
try {
|
|
1435
|
-
this.resetDetection();
|
|
1436
|
-
return { success: true };
|
|
1437
|
-
}
|
|
1438
|
-
catch (error) {
|
|
1439
|
-
return { success: false, error: error.message };
|
|
1440
|
-
}
|
|
1611
|
+
this.resetDetection();
|
|
1441
1612
|
}
|
|
1442
1613
|
async skipBackCapture() {
|
|
1443
|
-
const
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
try {
|
|
1448
|
-
const captureState = this.stateManager.getCaptureState();
|
|
1449
|
-
const capturedImages = this.stateManager.getCapturedImages();
|
|
1450
|
-
if (captureState.step === 'back' && capturedImages.front.fullFrame) {
|
|
1451
|
-
this.completeProcess(true);
|
|
1452
|
-
return { success: true };
|
|
1453
|
-
}
|
|
1454
|
-
else {
|
|
1455
|
-
return { success: false, error: 'No se puede saltar el reverso en el estado actual' };
|
|
1456
|
-
}
|
|
1457
|
-
}
|
|
1458
|
-
catch (error) {
|
|
1459
|
-
return { success: false, error: error.message };
|
|
1614
|
+
const captureState = this.stateManager.getCaptureState();
|
|
1615
|
+
const capturedImages = this.stateManager.getCapturedImages();
|
|
1616
|
+
if (captureState.step === 'back' && capturedImages.front.fullFrame) {
|
|
1617
|
+
this.completeProcess(true);
|
|
1460
1618
|
}
|
|
1461
1619
|
}
|
|
1462
1620
|
async getStatus() {
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
componentMessage: readyCheck.message
|
|
1473
|
-
};
|
|
1474
|
-
}
|
|
1475
|
-
try {
|
|
1476
|
-
const captureState = this.stateManager.getCaptureState();
|
|
1477
|
-
const capturedImages = this.stateManager.getCapturedImages();
|
|
1478
|
-
return {
|
|
1479
|
-
isVideoActive: captureState.isVideoActive,
|
|
1480
|
-
captureStep: captureState.step,
|
|
1481
|
-
hasImages: !!(capturedImages.front.fullFrame || capturedImages.back.fullFrame),
|
|
1482
|
-
isProcessCompleted: this.stateManager.isProcessCompleted(),
|
|
1483
|
-
isModelPreloaded: this.detectionService.isModelLoaded(),
|
|
1484
|
-
componentStatus: 'ready'
|
|
1485
|
-
};
|
|
1486
|
-
}
|
|
1487
|
-
catch (error) {
|
|
1488
|
-
return {
|
|
1489
|
-
isVideoActive: false,
|
|
1490
|
-
captureStep: 'front',
|
|
1491
|
-
hasImages: false,
|
|
1492
|
-
isProcessCompleted: false,
|
|
1493
|
-
isModelPreloaded: false,
|
|
1494
|
-
componentStatus: 'error',
|
|
1495
|
-
componentMessage: error.message
|
|
1496
|
-
};
|
|
1497
|
-
}
|
|
1621
|
+
const captureState = this.stateManager.getCaptureState();
|
|
1622
|
+
const capturedImages = this.stateManager.getCapturedImages();
|
|
1623
|
+
return {
|
|
1624
|
+
isVideoActive: captureState.isVideoActive,
|
|
1625
|
+
captureStep: captureState.step,
|
|
1626
|
+
hasImages: !!(capturedImages.front.fullFrame || capturedImages.back.fullFrame),
|
|
1627
|
+
isProcessCompleted: this.stateManager.isProcessCompleted(),
|
|
1628
|
+
isModelPreloaded: this.detectionService.isModelLoaded()
|
|
1629
|
+
};
|
|
1498
1630
|
}
|
|
1499
1631
|
async preloadModel() {
|
|
1500
|
-
const readyCheck = this.isComponentReady();
|
|
1501
|
-
if (!readyCheck.ready) {
|
|
1502
|
-
this.updateStatus(readyCheck.message, 'Espere a que termine la inicialización', readyCheck.status);
|
|
1503
|
-
return { success: false, error: readyCheck.message };
|
|
1504
|
-
}
|
|
1505
1632
|
if (this.detectionService.isModelLoaded()) {
|
|
1506
|
-
this.
|
|
1633
|
+
this.logger.state('MODELO_YA_PRECARGADO');
|
|
1634
|
+
this.updateStatus('Modelos ya cargados', '', 'ready');
|
|
1507
1635
|
return { success: true, message: 'Model already loaded' };
|
|
1508
1636
|
}
|
|
1509
1637
|
try {
|
|
1510
1638
|
const loadStartTime = performance.now();
|
|
1511
|
-
this.updateStatus('
|
|
1512
|
-
|
|
1513
|
-
this.stateManager.updateCaptureState({ isLoading: true });
|
|
1514
|
-
}
|
|
1639
|
+
this.updateStatus('Descargando modelo de detección...', 'Obteniendo red neuronal para reconocimiento de documentos', 'loading');
|
|
1640
|
+
this.stateManager.updateCaptureState({ isLoading: true });
|
|
1515
1641
|
await this.detectionService.loadModel();
|
|
1516
|
-
this.updateStatus('
|
|
1642
|
+
this.updateStatus('Cargando clasificador...', 'Preparando modelo de clasificación de tipos de documento', 'loading');
|
|
1517
1643
|
await this.detectionService.loadClassificationModel();
|
|
1518
1644
|
const loadEndTime = performance.now();
|
|
1519
1645
|
const loadTime = loadEndTime - loadStartTime;
|
|
@@ -1521,118 +1647,80 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1521
1647
|
if (this.debug) {
|
|
1522
1648
|
this.recordOnnxPerformance(loadTime, 0);
|
|
1523
1649
|
}
|
|
1524
|
-
this.updateStatus('
|
|
1650
|
+
this.updateStatus('Optimizando modelos...', 'Configurando parámetros de rendimiento', 'loading');
|
|
1525
1651
|
await new Promise(resolve => setTimeout(resolve, 300));
|
|
1526
|
-
this.updateStatus('
|
|
1527
|
-
|
|
1528
|
-
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1529
|
-
}
|
|
1652
|
+
this.updateStatus('Modelos precargados', '', 'ready');
|
|
1653
|
+
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1530
1654
|
this.emitReadyEvent();
|
|
1655
|
+
this.logger.state('MODELOS_PRECARGADOS_EXITOSAMENTE', { loadTime: Math.round(loadTime) });
|
|
1531
1656
|
return { success: true, message: 'Models preloaded successfully' };
|
|
1532
1657
|
}
|
|
1533
1658
|
catch (error) {
|
|
1534
|
-
this.
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
}
|
|
1659
|
+
this.logger.error('Error al precargar modelos:', error);
|
|
1660
|
+
this.updateStatus('Error al cargar modelos', 'No se pudieron descargar los recursos necesarios', 'error');
|
|
1661
|
+
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1538
1662
|
return { success: false, error: error.message };
|
|
1539
1663
|
}
|
|
1540
1664
|
}
|
|
1541
1665
|
async getCameraInfo() {
|
|
1542
|
-
|
|
1543
|
-
if (!readyCheck.ready) {
|
|
1544
|
-
return {
|
|
1545
|
-
availableCameras: [],
|
|
1546
|
-
selectedCameraId: null,
|
|
1547
|
-
deviceType: 'desktop',
|
|
1548
|
-
isMultipleCamerasAvailable: false,
|
|
1549
|
-
preferredFacing: null,
|
|
1550
|
-
error: readyCheck.message
|
|
1551
|
-
};
|
|
1552
|
-
}
|
|
1553
|
-
try {
|
|
1554
|
-
return await this.cameraService.getCameraInfo();
|
|
1555
|
-
}
|
|
1556
|
-
catch (error) {
|
|
1557
|
-
return {
|
|
1558
|
-
availableCameras: [],
|
|
1559
|
-
selectedCameraId: null,
|
|
1560
|
-
deviceType: 'desktop',
|
|
1561
|
-
isMultipleCamerasAvailable: false,
|
|
1562
|
-
preferredFacing: null,
|
|
1563
|
-
error: error.message
|
|
1564
|
-
};
|
|
1565
|
-
}
|
|
1666
|
+
return this.cameraService.getCameraInfo();
|
|
1566
1667
|
}
|
|
1567
1668
|
async setPreferredCamera(camera) {
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
}
|
|
1577
|
-
try {
|
|
1578
|
-
this.preferredCamera = camera;
|
|
1579
|
-
this.serviceContainer.updateConfig({ preferredCamera: camera });
|
|
1580
|
-
await this.cameraService.enumerateDevices();
|
|
1581
|
-
await this.updateCameraInfoWithAutofocus();
|
|
1582
|
-
const captureState = this.stateManager.getCaptureState();
|
|
1583
|
-
if (captureState.isVideoActive) {
|
|
1584
|
-
const selectedCameraId = this.cameraService.getSelectedCameraId();
|
|
1585
|
-
if (selectedCameraId) {
|
|
1586
|
-
await this.cameraService.switchCamera(selectedCameraId);
|
|
1587
|
-
}
|
|
1669
|
+
this.preferredCamera = camera;
|
|
1670
|
+
this.serviceContainer.updateConfig({ preferredCamera: camera });
|
|
1671
|
+
await this.cameraService.enumerateDevices();
|
|
1672
|
+
const captureState = this.stateManager.getCaptureState();
|
|
1673
|
+
if (captureState.isVideoActive) {
|
|
1674
|
+
const selectedCameraId = this.cameraService.getSelectedCameraId();
|
|
1675
|
+
if (selectedCameraId) {
|
|
1676
|
+
await this.cameraService.switchCamera(selectedCameraId);
|
|
1588
1677
|
}
|
|
1589
|
-
return {
|
|
1590
|
-
success: true,
|
|
1591
|
-
selectedCamera: this.cameraService.getSelectedCameraId(),
|
|
1592
|
-
availableCameras: this.cameraService.getAvailableCameras().length
|
|
1593
|
-
};
|
|
1594
|
-
}
|
|
1595
|
-
catch (error) {
|
|
1596
|
-
return {
|
|
1597
|
-
success: false,
|
|
1598
|
-
error: error.message,
|
|
1599
|
-
selectedCamera: null,
|
|
1600
|
-
availableCameras: 0
|
|
1601
|
-
};
|
|
1602
1678
|
}
|
|
1679
|
+
return {
|
|
1680
|
+
success: true,
|
|
1681
|
+
selectedCamera: this.cameraService.getSelectedCameraId(),
|
|
1682
|
+
availableCameras: this.cameraService.getAvailableCameras().length
|
|
1683
|
+
};
|
|
1603
1684
|
}
|
|
1604
1685
|
async setCaptureDelay(delay) {
|
|
1605
1686
|
if (delay < 0 || delay > 10000) {
|
|
1606
|
-
|
|
1607
|
-
success: false,
|
|
1608
|
-
error: 'Capture delay must be between 0 and 10000 milliseconds',
|
|
1609
|
-
captureDelay: this.captureDelay
|
|
1610
|
-
};
|
|
1687
|
+
throw new Error('Capture delay must be between 0 and 10000 milliseconds');
|
|
1611
1688
|
}
|
|
1612
|
-
// Allow setting capture delay even during initialization
|
|
1613
1689
|
this.captureDelay = delay;
|
|
1614
|
-
|
|
1615
|
-
this.serviceContainer.updateConfig({ captureDelay: delay });
|
|
1616
|
-
}
|
|
1690
|
+
this.serviceContainer.updateConfig({ captureDelay: delay });
|
|
1617
1691
|
return {
|
|
1618
1692
|
success: true,
|
|
1619
1693
|
captureDelay: this.captureDelay
|
|
1620
1694
|
};
|
|
1621
1695
|
}
|
|
1622
1696
|
async getCaptureDelay() {
|
|
1623
|
-
// Always allow getting capture delay, even during initialization
|
|
1624
1697
|
return this.captureDelay;
|
|
1625
1698
|
}
|
|
1699
|
+
async setTorchEnabled(enabled) {
|
|
1700
|
+
const torchEnabled = await this.cameraService.setTorchEnabled(enabled, this.videoStream);
|
|
1701
|
+
return {
|
|
1702
|
+
success: torchEnabled,
|
|
1703
|
+
enabled: torchEnabled ? enabled : false
|
|
1704
|
+
};
|
|
1705
|
+
}
|
|
1706
|
+
async focusAtPoint(x, y) {
|
|
1707
|
+
const focused = await this.cameraService.focusAtPoint(x, y, this.videoStream);
|
|
1708
|
+
return {
|
|
1709
|
+
success: focused,
|
|
1710
|
+
coordinates: { x, y }
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1626
1713
|
// DETECTION METHODS
|
|
1627
1714
|
async startDetection() {
|
|
1715
|
+
this.logger.state('INICIANDO_DETECCION');
|
|
1628
1716
|
try {
|
|
1629
1717
|
// Paso 1: Verificar y cargar modelos si es necesario
|
|
1630
1718
|
if (!this.detectionService.isModelLoaded()) {
|
|
1631
1719
|
const loadStartTime = performance.now();
|
|
1632
|
-
this.updateStatus('
|
|
1720
|
+
this.updateStatus('Cargando modelo de detección...', 'Descargando red neuronal para reconocimiento', 'loading');
|
|
1633
1721
|
this.stateManager.updateCaptureState({ isLoading: true });
|
|
1634
1722
|
await this.detectionService.loadModel();
|
|
1635
|
-
this.updateStatus('
|
|
1723
|
+
this.updateStatus('Cargando clasificador...', 'Preparando modelo de clasificación de documentos', 'loading');
|
|
1636
1724
|
await this.detectionService.loadClassificationModel();
|
|
1637
1725
|
const loadEndTime = performance.now();
|
|
1638
1726
|
const loadTime = loadEndTime - loadStartTime;
|
|
@@ -1640,13 +1728,13 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1640
1728
|
if (this.debug) {
|
|
1641
1729
|
this.recordOnnxPerformance(loadTime, 0);
|
|
1642
1730
|
}
|
|
1731
|
+
this.logger.state('MODELOS_CARGADOS_EN_DETECCION', { loadTime: Math.round(loadTime) });
|
|
1643
1732
|
}
|
|
1644
1733
|
// Paso 2: Detectar y configurar dispositivos
|
|
1645
|
-
this.updateStatus('
|
|
1734
|
+
this.updateStatus('Detectando cámaras...', 'Buscando dispositivos de captura disponibles', 'loading');
|
|
1646
1735
|
await this.cameraService.enumerateDevices();
|
|
1647
|
-
await this.updateCameraInfoWithAutofocus();
|
|
1648
1736
|
// Paso 3: Configurar cámara seleccionada
|
|
1649
|
-
this.updateStatus('
|
|
1737
|
+
this.updateStatus('Configurando cámara...', 'Estableciendo resolución y parámetros óptimos', 'loading');
|
|
1650
1738
|
const stream = await this.cameraService.setupCamera();
|
|
1651
1739
|
// Paso 4: Inicializar video y ocultar estado inmediatamente
|
|
1652
1740
|
this.updateStatus('Captura activa', 'Buscando documento en el marco de captura', 'active');
|
|
@@ -1663,7 +1751,8 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1663
1751
|
this.detectFrame();
|
|
1664
1752
|
}
|
|
1665
1753
|
catch (err) {
|
|
1666
|
-
this.
|
|
1754
|
+
this.logger.error('Error al inicializar detección:', err);
|
|
1755
|
+
this.updateStatus('Error al iniciar captura', 'No se pudo completar la inicialización', 'error');
|
|
1667
1756
|
this.stateManager.updateCaptureState({ isLoading: false });
|
|
1668
1757
|
}
|
|
1669
1758
|
}
|
|
@@ -1673,6 +1762,10 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1673
1762
|
this.videoStream = stream;
|
|
1674
1763
|
const isRear = this.cameraService.isRearCamera(stream);
|
|
1675
1764
|
this.shouldMirrorVideo = !isRear;
|
|
1765
|
+
this.logger.state('CAMARA_CONFIGURADA', {
|
|
1766
|
+
isRearCamera: isRear,
|
|
1767
|
+
shouldMirrorVideo: this.shouldMirrorVideo
|
|
1768
|
+
});
|
|
1676
1769
|
return new Promise((resolve) => {
|
|
1677
1770
|
this.videoRef.onloadedmetadata = async () => {
|
|
1678
1771
|
await this.videoRef.play();
|
|
@@ -1697,10 +1790,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1697
1790
|
}
|
|
1698
1791
|
return;
|
|
1699
1792
|
}
|
|
1700
|
-
//
|
|
1793
|
+
// Frame skipping for performance
|
|
1701
1794
|
this.frameSkipCounter++;
|
|
1702
|
-
|
|
1703
|
-
if (this.frameSkipCounter <= currentFrameSkip) {
|
|
1795
|
+
if (this.frameSkipCounter <= this.FRAME_SKIP) {
|
|
1704
1796
|
if (captureState.step !== 'completed') {
|
|
1705
1797
|
this.animationId = requestAnimationFrame(() => this.detectFrame());
|
|
1706
1798
|
}
|
|
@@ -1776,6 +1868,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1776
1868
|
}
|
|
1777
1869
|
}
|
|
1778
1870
|
catch (e) {
|
|
1871
|
+
this.logger.error('Error en inferencia de modelo:', e);
|
|
1779
1872
|
const captureState = this.stateManager.getCaptureState();
|
|
1780
1873
|
if (captureState.step !== 'completed') {
|
|
1781
1874
|
setTimeout(() => this.detectFrame(), 100);
|
|
@@ -1802,8 +1895,6 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1802
1895
|
clearInterval(this.performanceUpdateInterval);
|
|
1803
1896
|
this.performanceUpdateInterval = undefined;
|
|
1804
1897
|
}
|
|
1805
|
-
// Clear canvas pool
|
|
1806
|
-
this.canvasPool = [];
|
|
1807
1898
|
this.detectionBoxes = [];
|
|
1808
1899
|
this.alignmentStartTime = undefined;
|
|
1809
1900
|
this.hasDocumentDetected = false;
|
|
@@ -1817,8 +1908,11 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1817
1908
|
step: 'front',
|
|
1818
1909
|
isCapturing: false
|
|
1819
1910
|
};
|
|
1820
|
-
const cameraInfo = this.
|
|
1821
|
-
|
|
1911
|
+
const cameraInfo = this.cameraService?.getCameraInfo() || {
|
|
1912
|
+
availableCameras: [],
|
|
1913
|
+
selectedCameraId: null,
|
|
1914
|
+
deviceType: 'desktop'};
|
|
1915
|
+
return (h("div", { key: '9d4d77042218ab3d0bc4fb1ce879db09dc3e78b4', class: "detector-container" }, h("div", { key: '51ac8ccb1275637282b038ad8364d19f14ef61b1', class: "video-container" }, h("video", { key: 'a34f0481da20ec5b7b62b21aa275ec5f6dfec5d1', ref: el => this.videoRef = el, autoplay: true, muted: true, playsinline: true, class: this.shouldMirrorVideo ? 'mirror' : '', style: { display: captureState.isVideoActive ? 'block' : 'none' } }), h("div", { key: '43b71dfe3eaf20ffa66a4139e24b1af6a2074631', ref: el => this.detectionContainer = el, class: `detection-overlay ${this.shouldMirrorVideo ? 'mirror' : ''}` }, this.debug && this.detectionBoxes.map((box, index) => (h("div", { key: index, class: "detection-box", style: {
|
|
1822
1916
|
position: 'absolute',
|
|
1823
1917
|
left: `${box.x}px`,
|
|
1824
1918
|
top: `${box.y}px`,
|
|
@@ -1827,9 +1921,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1827
1921
|
border: '2px solid #32406C',
|
|
1828
1922
|
pointerEvents: 'none',
|
|
1829
1923
|
boxSizing: 'border-box'
|
|
1830
|
-
} })))), this.isMaskReady && (h("div", { key: '
|
|
1924
|
+
} })))), this.isMaskReady && (h("div", { key: 'c2596f8cabb227e3ea3b082495f52b141cb1690f', class: "overlay-mask" }, h("div", { key: 'ba1ea39b5df1cd312a9bd9410b7225a29cfb8a29', class: "card-outline" }, h("div", { key: '6ce4d4d969d5f3fbcf75a4eb285f61d434c5633b', class: "side side-top" }), h("div", { key: '36653ecc4fcda25dfc84bc4d1f4c4c5dfb71ced7', class: "side side-right" }), h("div", { key: '8b108fa8fb370b229a0d1a77cb1ff044319a8f75', class: "side side-bottom" }), h("div", { key: 'a7aa3b45c065a50445aafd5c3ccefb57b0c9c769', class: "side side-left" }), !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: 'b94b92273e12a019238e11ae57f21e140f13c3f1', class: "guide-text" }, "Alinee su identificaci\u00F3n con el marco"))), captureState.step === 'back' && !captureState.showFlipAnimation && !captureState.showSuccessAnimation && (h("div", { key: '144c3741003ec0cc148bb6ce92012d7b3cd110c8', class: "skip-section" }, h("div", { key: 'f0a984a6b75afa374c5d47da9d53e39f98071da0', class: "skip-explanation" }, "Si tu documento no tiene lado trasero, da clic en el bot\u00F3n para continuar con el proceso"), h("button", { key: '57ac30d776e30709aab6e0a621cef889da3cc677', class: "skip-button", onClick: () => this.skipBackCapture(), type: "button" }, this.enableBackDocumentTimer && this.backDocumentTimerRemaining > 0
|
|
1831
1925
|
? `Saltar reverso (${this.backDocumentTimerRemaining}s)`
|
|
1832
|
-
: 'Saltar reverso'))), captureState.isVideoActive && (h("div", { key: '
|
|
1926
|
+
: 'Saltar reverso'))), captureState.isVideoActive && (h("div", { key: 'bd45628707e0169317ed3dd4247e1056d7046104', class: "camera-controls" }, h("button", { key: 'e750fbf4d5d76d9e9596823bb2b86801ef19e485', class: `camera-selector-button ${this.isSwitchingCamera ? 'loading' : ''}`, onClick: () => this.toggleCameraSelector(), type: "button", title: "Seleccionar c\u00E1mara", disabled: this.isSwitchingCamera }, this.isSwitchingCamera ? (h("div", { class: "button-spinner" })) : ('Cámaras')))), this.showCameraSelector && cameraInfo.availableCameras.length > 0 && (h("div", { key: '40ada780a8ea13aeeaee9cb39bc6496f69f2679d', class: "camera-selector-dropdown" }, h("div", { key: '3a920bb02ba024f0359513f10544bf0528ecee6d', class: "camera-selector-header" }, h("span", { key: 'e5efd478a602b13821ba53cb9a7a59d1b4a71178' }, "Seleccionar C\u00E1mara"), h("button", { key: '2df6cc11e162be74862029e33815927e3fa8f2f8', class: "close-selector", onClick: () => this.toggleCameraSelector(), type: "button" }, "\u00D7")), h("div", { key: '06934642dff134c9e1a6872f935a5892a9c7b835', class: "camera-list" }, cameraInfo.availableCameras.map((camera) => (h("button", { key: camera.id, class: `camera-option ${cameraInfo.selectedCameraId === camera.id ? 'selected' : ''}`, onClick: () => this.handleCameraSwitch(camera.id), type: "button" }, h("span", { class: "camera-label" }, camera.label || `Cámara ${cameraInfo.availableCameras.indexOf(camera) + 1}`), cameraInfo.selectedCameraId === camera.id && (h("span", { class: "selected-indicator" }, "\u2713")))))), h("div", { key: '8d5ede287c2efb0ebe418bee8dc7fb310443c991', class: "device-info" }, h("small", { key: 'b7dd10565113694b09f01ab28ffa29262ae1d2bc' }, "Dispositivo: ", cameraInfo.deviceType)))))), captureState.isCapturing && (h("div", { key: '2f9e549e6c0c56d4db5835e58361fb17bba6cf90', class: "capture-animation" })), captureState.showFlipAnimation && (h("div", { key: 'a6ef0ed127f23e9d764896ed743ca8d8454851a6', class: "flip-animation" }, h("div", { key: '0b04411f88a634bb3243b00ca2d5a45fd6962dea', class: "id-card-icon" }), h("div", { key: '9327be58879b407cea87f455989406e58302e9d1', class: "flip-text" }, "\u00A1Voltea tu identificaci\u00F3n!"))), captureState.showSuccessAnimation && (h("div", { key: 'bd268578b5003e99256e3340b4a9a6fb7656be64', class: "success-animation" }, h("div", { key: '064e0a5b9851de14bc6829f028e7ea7161fd1633', class: "check-icon" }), h("div", { key: 'bdcd94a8806399e396e218a359c10b11d05f38d6', class: "success-text" }, "\u00A1Proceso completado!"))), h("div", { key: '3d9af617376c16e44e065873919c3d90ff46110a', class: `component-status status-${this.currentStatus.type}` }, (this.currentStatus.type === 'loading' || this.currentStatus.type === 'initializing') && (h("div", { key: 'be7b56decfd6f12dcca46b2a9456480e3d5ac00e', class: "status-spinner" })), h("div", { key: '5fe9850883cb749cebaa18c3e9c13ca21325e373', class: "status-content" }, h("div", { key: '43fdb50691e4b98e56126af1a129f625061128bf', class: "status-message" }, this.currentStatus.message), this.currentStatus.description && (h("div", { key: 'b76cbfa4a0349421fcd535d3fd8bf40c89ca3aef', class: "status-description" }, this.currentStatus.description)))), this.debug && (h("div", { key: 'eddefb464af0487ab9ab2a0f76dccbb6a2092e51', class: "performance-monitor" }, h("div", { key: '7b7d93b56cb4cd4fbb1bdde89c6150b282fa805e', class: "performance-expanded" }, h("div", { key: 'f302e8b9d985fb8cad53c4c4e0d37a3bf5a63261', class: "metrics-row" }, h("div", { key: 'e8d8f2420ee8643a7f454d38065c75b5be598f77', class: "metric-compact" }, h("span", { key: '6e52eb5bb06e0df57e61774de38c31349f45e22b', class: "metric-label" }, "FPS"), h("span", { key: '6636f9cea20c49b49c17db86188e2f6b214c45c3', class: `metric-value ${this.performanceData.fps < 15 ? 'warning' : this.performanceData.fps < 10 ? 'danger' : 'good'}` }, this.performanceData.fps)), h("div", { key: 'afa1d9dec05b14256a1b1fcec8638b8c21a56e37', class: "metric-compact" }, h("span", { key: '9c55e30f5d01582648bacc1fb38ca1990485a591', class: "metric-label" }, "MEM"), h("span", { key: 'ca263601e8715027f95ffb2d5413a643dc570cf2', class: `metric-value ${this.performanceData.memoryUsage > 100 ? 'warning' : this.performanceData.memoryUsage > 200 ? 'danger' : 'good'}` }, this.performanceData.memoryUsage, "MB"))), h("div", { key: '03caf19961fd01b7781484d49d8121b1e78c161f', class: "metrics-row" }, h("div", { key: 'bffb08f01f703411f7c8605e4f8d95b1c37b5c0f', class: "metric-compact" }, h("span", { key: '9b92279b3253cc669412733facb3751f4344d69f', class: "metric-label" }, "INF"), h("span", { key: '3234d4150e2e1479e3b552767af8123e69911d6c', class: `metric-value ${this.performanceData.inferenceTime > 100 ? 'warning' : this.performanceData.inferenceTime > 200 ? 'danger' : 'good'}` }, this.performanceData.inferenceTime, "ms")), h("div", { key: '77082d2669db53f19fb50b3bbfa1ebefb0b86ff4', class: "metric-compact" }, h("span", { key: 'cc6ef96c72c800ba36dab2a388f1de863b2dacd5', class: "metric-label" }, "FRAME"), h("span", { key: 'e2bb4b7351869a76e810f8bbf20d805154495e35', class: `metric-value ${this.performanceData.frameProcessingTime > 50 ? 'warning' : this.performanceData.frameProcessingTime > 100 ? 'danger' : 'good'}` }, this.performanceData.frameProcessingTime, "ms"))), h("div", { key: '5700d7ef839fa77686299d79445203090dc8079f', class: "metrics-row" }, h("div", { key: 'a2999a035fccb7f250c01b084075490678513c87', class: "metric-compact" }, h("span", { key: '3677d58665996fa5969507dbaff9e956b2ea54ee', class: "metric-label" }, "DET"), h("span", { key: 'c584312438b339c82a3bdc33f00fb985ff2a6f55', class: "metric-value good" }, this.performanceData.successfulDetections, "/", this.performanceData.totalDetections)), h("div", { key: '58866219f213bf8607083f30be2d5ca5d0515cea', class: "metric-compact" }, h("span", { key: 'd90b450051331347f219b6c0f197536d0dcee9b7', class: "metric-label" }, "RATE"), h("span", { key: 'f6e6cd7948e06014e5a0416e4e81394fa36ec61d', class: `metric-value ${this.performanceData.detectionRate < 30 ? 'danger' : this.performanceData.detectionRate < 60 ? 'warning' : 'good'}` }, this.performanceData.detectionRate, "%")))))), h("div", { key: 'c1d0194f181a0082cc1aa5452de382908da61b65', class: "watermark" }, h("img", { key: 'e04d35f3c79a2b4a3922ab5b0f102220171fa7c0', src: "https://storage.googleapis.com/jaak-static/commons/powered-by-jaak.png", alt: "Powered by Jaak" })))));
|
|
1833
1927
|
}
|
|
1834
1928
|
// Utility methods
|
|
1835
1929
|
updateDetectionBoxes(boxes) {
|
|
@@ -1910,18 +2004,35 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1910
2004
|
if (allSidesAligned && bestBox) {
|
|
1911
2005
|
cardOutline?.classList.add('perfect-match');
|
|
1912
2006
|
corners?.forEach(corner => corner.classList.add('perfect-match'));
|
|
2007
|
+
// Debug logging
|
|
2008
|
+
this.logger.state('CAPTURE_EVALUATION', {
|
|
2009
|
+
allSidesAligned: true,
|
|
2010
|
+
hasScreenshotTaken: this.hasScreenshotTaken,
|
|
2011
|
+
captureDelay: this.captureDelay,
|
|
2012
|
+
alignmentStartTime: this.alignmentStartTime
|
|
2013
|
+
});
|
|
1913
2014
|
if (!this.hasScreenshotTaken) {
|
|
1914
2015
|
const currentTime = Date.now();
|
|
1915
2016
|
// Initialize alignment start time if not set
|
|
1916
2017
|
if (!this.alignmentStartTime) {
|
|
1917
2018
|
this.alignmentStartTime = currentTime;
|
|
2019
|
+
this.logger.state('ALIGNMENT_TIMER_STARTED', { startTime: currentTime });
|
|
1918
2020
|
}
|
|
1919
2021
|
// Check if document has been aligned for the configured delay
|
|
1920
2022
|
const alignmentDuration = currentTime - this.alignmentStartTime;
|
|
2023
|
+
this.logger.state('ALIGNMENT_DURATION_CHECK', {
|
|
2024
|
+
alignmentDuration,
|
|
2025
|
+
captureDelay: this.captureDelay,
|
|
2026
|
+
readyToCapture: alignmentDuration >= this.captureDelay
|
|
2027
|
+
});
|
|
1921
2028
|
if (alignmentDuration >= this.captureDelay) {
|
|
2029
|
+
this.logger.state('TRIGGERING_CAPTURE', {
|
|
2030
|
+
alignmentDuration,
|
|
2031
|
+
captureDelay: this.captureDelay
|
|
2032
|
+
});
|
|
1922
2033
|
this.lastDetectedBox = bestBox;
|
|
1923
|
-
this.takeScreenshot().catch(
|
|
1924
|
-
|
|
2034
|
+
this.takeScreenshot().catch(error => {
|
|
2035
|
+
this.logger.error('Error al tomar captura de pantalla:', error);
|
|
1925
2036
|
});
|
|
1926
2037
|
this.hasScreenshotTaken = true;
|
|
1927
2038
|
this.alignmentStartTime = undefined;
|
|
@@ -1947,12 +2058,21 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1947
2058
|
async takeScreenshot() {
|
|
1948
2059
|
if (!this.videoRef || !this.lastDetectedBox)
|
|
1949
2060
|
return;
|
|
2061
|
+
this.logger.state('INICIANDO_CAPTURA', {
|
|
2062
|
+
captureStep: this.stateManager.getCaptureState().step,
|
|
2063
|
+
detectedBox: this.lastDetectedBox,
|
|
2064
|
+
videoResolution: {
|
|
2065
|
+
width: this.videoRef.videoWidth,
|
|
2066
|
+
height: this.videoRef.videoHeight
|
|
2067
|
+
}
|
|
2068
|
+
});
|
|
1950
2069
|
this.stateManager.updateCaptureState({ isCapturing: true });
|
|
1951
2070
|
this.triggerCaptureAnimation();
|
|
1952
|
-
//
|
|
1953
|
-
const captureCanvas =
|
|
2071
|
+
// Create capture canvas
|
|
2072
|
+
const captureCanvas = document.createElement('canvas');
|
|
2073
|
+
captureCanvas.width = this.videoRef.videoWidth;
|
|
2074
|
+
captureCanvas.height = this.videoRef.videoHeight;
|
|
1954
2075
|
const captureCtx = captureCanvas.getContext('2d', { alpha: false });
|
|
1955
|
-
captureCtx.clearRect(0, 0, captureCanvas.width, captureCanvas.height);
|
|
1956
2076
|
captureCtx.drawImage(this.videoRef, 0, 0, captureCanvas.width, captureCanvas.height);
|
|
1957
2077
|
// Calculate crop coordinates
|
|
1958
2078
|
const INPUT_SIZE = 320;
|
|
@@ -1962,10 +2082,11 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1962
2082
|
const cropY = Math.max(0, (this.lastDetectedBox.y * scaleY) - this.cropMargin);
|
|
1963
2083
|
const cropWidth = Math.min((this.lastDetectedBox.w * scaleX) + (2 * this.cropMargin), this.videoRef.videoWidth - cropX);
|
|
1964
2084
|
const cropHeight = Math.min((this.lastDetectedBox.h * scaleY) + (2 * this.cropMargin), this.videoRef.videoHeight - cropY);
|
|
1965
|
-
//
|
|
1966
|
-
const croppedCanvas =
|
|
2085
|
+
// Create cropped version
|
|
2086
|
+
const croppedCanvas = document.createElement('canvas');
|
|
2087
|
+
croppedCanvas.width = cropWidth;
|
|
2088
|
+
croppedCanvas.height = cropHeight;
|
|
1967
2089
|
const croppedCtx = croppedCanvas.getContext('2d', { alpha: false });
|
|
1968
|
-
croppedCtx.clearRect(0, 0, croppedCanvas.width, croppedCanvas.height);
|
|
1969
2090
|
croppedCtx.drawImage(this.videoRef, cropX, cropY, cropWidth, cropHeight, 0, 0, cropWidth, cropHeight);
|
|
1970
2091
|
const captureState = this.stateManager.getCaptureState();
|
|
1971
2092
|
if (captureState.step === 'front') {
|
|
@@ -1979,6 +2100,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
1979
2100
|
if (this.useDocumentClassification) {
|
|
1980
2101
|
const classification = await this.detectionService.classifyDocument(croppedCanvas);
|
|
1981
2102
|
if (classification && classification.class === 'passport') {
|
|
2103
|
+
this.logger.state('PASAPORTE_DETECTADO_SALTANDO_REVERSO', { classification: classification?.class });
|
|
1982
2104
|
this.completeProcess(true);
|
|
1983
2105
|
return;
|
|
1984
2106
|
}
|
|
@@ -2005,9 +2127,6 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2005
2127
|
});
|
|
2006
2128
|
this.completeProcess(false);
|
|
2007
2129
|
}
|
|
2008
|
-
// Return canvases to pool after use
|
|
2009
|
-
this.returnCanvasToPool(captureCanvas);
|
|
2010
|
-
this.returnCanvasToPool(croppedCanvas);
|
|
2011
2130
|
}
|
|
2012
2131
|
triggerCaptureAnimation() {
|
|
2013
2132
|
const cardOutline = this.el.shadowRoot?.querySelector('.card-outline');
|
|
@@ -2036,6 +2155,11 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2036
2155
|
setTimeout(() => {
|
|
2037
2156
|
this.stateManager.updateCaptureState({ showSuccessAnimation: false });
|
|
2038
2157
|
}, 3000);
|
|
2158
|
+
this.logger.state('PROCESO_COMPLETADO', {
|
|
2159
|
+
skippedBack,
|
|
2160
|
+
totalImages: capturedImages.metadata.totalImages,
|
|
2161
|
+
timestamp: new Date().toISOString()
|
|
2162
|
+
});
|
|
2039
2163
|
}
|
|
2040
2164
|
stopDetection() {
|
|
2041
2165
|
if (this.animationId) {
|
|
@@ -2044,6 +2168,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2044
2168
|
}
|
|
2045
2169
|
this.clearBackDocumentTimer();
|
|
2046
2170
|
this.detectionBoxes = [];
|
|
2171
|
+
this.logger.state('DETECTOR_DETENIDO', { timestamp: Date.now() });
|
|
2047
2172
|
}
|
|
2048
2173
|
startBackDocumentTimer() {
|
|
2049
2174
|
if (!this.enableBackDocumentTimer)
|
|
@@ -2072,15 +2197,6 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2072
2197
|
return; // Don't toggle if switching camera
|
|
2073
2198
|
this.showCameraSelector = !this.showCameraSelector;
|
|
2074
2199
|
}
|
|
2075
|
-
async updateCameraInfoWithAutofocus() {
|
|
2076
|
-
try {
|
|
2077
|
-
const cameraInfo = await this.cameraService.getCameraInfo();
|
|
2078
|
-
this.cameraInfoWithAutofocus = cameraInfo;
|
|
2079
|
-
}
|
|
2080
|
-
catch (error) {
|
|
2081
|
-
// Keep existing state if update fails
|
|
2082
|
-
}
|
|
2083
|
-
}
|
|
2084
2200
|
async handleCameraSwitch(cameraId) {
|
|
2085
2201
|
if (this.isSwitchingCamera)
|
|
2086
2202
|
return; // Prevent multiple simultaneous switches
|
|
@@ -2088,6 +2204,10 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2088
2204
|
// Close the selector immediately when user selects a camera
|
|
2089
2205
|
this.showCameraSelector = false;
|
|
2090
2206
|
this.isSwitchingCamera = true;
|
|
2207
|
+
this.logger.state('INICIANDO_CAMBIO_CAMARA', {
|
|
2208
|
+
from: this.cameraService.getSelectedCameraId(),
|
|
2209
|
+
to: cameraId
|
|
2210
|
+
});
|
|
2091
2211
|
// Stop current video stream
|
|
2092
2212
|
if (this.videoStream) {
|
|
2093
2213
|
this.videoStream.getTracks().forEach(track => track.stop());
|
|
@@ -2098,17 +2218,21 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2098
2218
|
const newStream = await this.cameraService.setupCamera();
|
|
2099
2219
|
// Update video element
|
|
2100
2220
|
await this.initializeVideoStream(newStream);
|
|
2101
|
-
|
|
2102
|
-
|
|
2221
|
+
this.logger.state('CAMBIO_CAMARA_EXITOSO', {
|
|
2222
|
+
newCameraId: cameraId,
|
|
2223
|
+
isRearCamera: this.cameraService.isRearCamera(newStream)
|
|
2224
|
+
});
|
|
2103
2225
|
}
|
|
2104
2226
|
catch (error) {
|
|
2227
|
+
this.logger.error('Error al cambiar cámara:', error);
|
|
2105
2228
|
// Try to restore previous camera if switch failed
|
|
2106
2229
|
try {
|
|
2107
2230
|
const fallbackStream = await this.cameraService.setupCamera();
|
|
2108
2231
|
await this.initializeVideoStream(fallbackStream);
|
|
2109
2232
|
}
|
|
2110
2233
|
catch (fallbackError) {
|
|
2111
|
-
this.
|
|
2234
|
+
this.logger.error('Error al restaurar cámara anterior:', fallbackError);
|
|
2235
|
+
this.updateStatus('Error al cambiar cámara', 'No se pudo completar el cambio de dispositivo', 'error');
|
|
2112
2236
|
}
|
|
2113
2237
|
}
|
|
2114
2238
|
finally {
|
|
@@ -2149,7 +2273,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2149
2273
|
this.stateManager.updateCaptureState({ isVideoActive: false, isLoading: false });
|
|
2150
2274
|
}
|
|
2151
2275
|
this.isMaskReady = false;
|
|
2152
|
-
this.updateStatus('
|
|
2276
|
+
this.updateStatus('Sesión finalizada', '', 'ready');
|
|
2153
2277
|
this.detectionBoxes = [];
|
|
2154
2278
|
this.cleanup();
|
|
2155
2279
|
}
|
|
@@ -2160,6 +2284,7 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2160
2284
|
this.performanceUpdateInterval = window.setInterval(() => {
|
|
2161
2285
|
this.updatePerformanceMetrics();
|
|
2162
2286
|
}, 500);
|
|
2287
|
+
this.logger.debug('Monitor de performance inicializado');
|
|
2163
2288
|
}
|
|
2164
2289
|
updatePerformanceMetrics() {
|
|
2165
2290
|
const currentTime = performance.now();
|
|
@@ -2202,66 +2327,6 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2202
2327
|
if (detectionsFound > 0) {
|
|
2203
2328
|
this.performanceMetrics.successfulDetections++;
|
|
2204
2329
|
}
|
|
2205
|
-
// Update performance history for adaptive frame skipping
|
|
2206
|
-
this.performanceHistory.push(processingTime);
|
|
2207
|
-
if (this.performanceHistory.length > this.PERFORMANCE_HISTORY_SIZE) {
|
|
2208
|
-
this.performanceHistory.shift();
|
|
2209
|
-
}
|
|
2210
|
-
}
|
|
2211
|
-
// Adaptive frame skipping based on performance
|
|
2212
|
-
getAdaptiveFrameSkip() {
|
|
2213
|
-
// Base conditions for high frame skip
|
|
2214
|
-
if (this.consecutiveFailures > 20)
|
|
2215
|
-
return this.MAX_FRAME_SKIP;
|
|
2216
|
-
if (this.performanceMetrics.inferenceTime > 200)
|
|
2217
|
-
return 6;
|
|
2218
|
-
if (this.performanceMetrics.memoryUsage > 200)
|
|
2219
|
-
return 5;
|
|
2220
|
-
// Calculate average processing time
|
|
2221
|
-
if (this.performanceHistory.length > 0) {
|
|
2222
|
-
const avgProcessingTime = this.performanceHistory.reduce((a, b) => a + b, 0) / this.performanceHistory.length;
|
|
2223
|
-
if (avgProcessingTime > 100)
|
|
2224
|
-
return 4;
|
|
2225
|
-
if (avgProcessingTime > 80)
|
|
2226
|
-
return 3;
|
|
2227
|
-
if (avgProcessingTime > 60)
|
|
2228
|
-
return this.BASE_FRAME_SKIP + 1;
|
|
2229
|
-
}
|
|
2230
|
-
// Low memory devices get higher frame skip
|
|
2231
|
-
if (this.performanceMetrics.memoryUsage > 150)
|
|
2232
|
-
return 4;
|
|
2233
|
-
// Performance is good, use base frame skip
|
|
2234
|
-
return this.BASE_FRAME_SKIP;
|
|
2235
|
-
}
|
|
2236
|
-
// Canvas pool management for screenshots
|
|
2237
|
-
getPooledCanvas(width, height) {
|
|
2238
|
-
// Try to find a canvas with matching dimensions
|
|
2239
|
-
const matchingIndex = this.canvasPool.findIndex(canvas => canvas.width === width && canvas.height === height);
|
|
2240
|
-
if (matchingIndex !== -1) {
|
|
2241
|
-
return this.canvasPool.splice(matchingIndex, 1)[0];
|
|
2242
|
-
}
|
|
2243
|
-
// If no matching canvas, try to reuse any canvas and resize
|
|
2244
|
-
if (this.canvasPool.length > 0) {
|
|
2245
|
-
const canvas = this.canvasPool.pop();
|
|
2246
|
-
canvas.width = width;
|
|
2247
|
-
canvas.height = height;
|
|
2248
|
-
return canvas;
|
|
2249
|
-
}
|
|
2250
|
-
// Create new canvas if pool is empty
|
|
2251
|
-
const canvas = document.createElement('canvas');
|
|
2252
|
-
canvas.width = width;
|
|
2253
|
-
canvas.height = height;
|
|
2254
|
-
return canvas;
|
|
2255
|
-
}
|
|
2256
|
-
returnCanvasToPool(canvas) {
|
|
2257
|
-
// Only return to pool if not at max capacity
|
|
2258
|
-
if (this.canvasPool.length < this.MAX_CANVAS_POOL_SIZE) {
|
|
2259
|
-
// Clear the canvas before returning to pool
|
|
2260
|
-
const ctx = canvas.getContext('2d');
|
|
2261
|
-
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
2262
|
-
this.canvasPool.push(canvas);
|
|
2263
|
-
}
|
|
2264
|
-
// If pool is full, let the canvas be garbage collected
|
|
2265
2330
|
}
|
|
2266
2331
|
static get style() { return myComponentCss; }
|
|
2267
2332
|
}, [1, "jaak-stamps", {
|
|
@@ -2281,7 +2346,6 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2281
2346
|
"showCameraSelector": [32],
|
|
2282
2347
|
"isSwitchingCamera": [32],
|
|
2283
2348
|
"hasDocumentDetected": [32],
|
|
2284
|
-
"cameraInfoWithAutofocus": [32],
|
|
2285
2349
|
"currentStatus": [32],
|
|
2286
2350
|
"performanceData": [32],
|
|
2287
2351
|
"backDocumentTimerRemaining": [32],
|
|
@@ -2296,7 +2360,9 @@ const JaakStamps$1 = /*@__PURE__*/ proxyCustomElement(class JaakStamps extends H
|
|
|
2296
2360
|
"getCameraInfo": [64],
|
|
2297
2361
|
"setPreferredCamera": [64],
|
|
2298
2362
|
"setCaptureDelay": [64],
|
|
2299
|
-
"getCaptureDelay": [64]
|
|
2363
|
+
"getCaptureDelay": [64],
|
|
2364
|
+
"setTorchEnabled": [64],
|
|
2365
|
+
"focusAtPoint": [64]
|
|
2300
2366
|
}]);
|
|
2301
2367
|
function defineCustomElement$1() {
|
|
2302
2368
|
if (typeof customElements === "undefined") {
|