@jaak.ai/stamps 2.1.0-dev.13 → 2.1.0-dev.15

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.
@@ -206,7 +206,21 @@ class CameraService {
206
206
  tempStream.getTracks().forEach(track => track.stop());
207
207
  }
208
208
  const devices = await navigator.mediaDevices.enumerateDevices();
209
- this.availableCameras = devices.filter(device => device.kind === 'videoinput');
209
+ const allCameras = devices.filter(device => device.kind === 'videoinput');
210
+ // Filter out virtual cameras
211
+ this.availableCameras = allCameras.filter(camera => {
212
+ const isVirtual = this.isVirtualCamera(camera);
213
+ if (isVirtual) {
214
+ console.log(`Virtual camera detected and filtered: ${camera.label}`);
215
+ }
216
+ return !isVirtual;
217
+ });
218
+ // Check if there are physical cameras available
219
+ if (this.availableCameras.length === 0) {
220
+ console.log('No physical cameras available');
221
+ this.eventBus.emit('error', new Error('Cámaras virtuales no están permitidas. Use una cámara física.'));
222
+ return [];
223
+ }
210
224
  // Cache the results for optimization
211
225
  CameraService.deviceEnumerationCache = {
212
226
  devices: this.availableCameras,
@@ -256,6 +270,21 @@ class CameraService {
256
270
  });
257
271
  this.lastStreamConstraints = finalConstraints;
258
272
  const stream = await this.currentStreamPromise;
273
+ // Validate that active stream is not from a virtual camera
274
+ const videoTrack = stream.getVideoTracks()[0];
275
+ if (videoTrack) {
276
+ const settings = videoTrack.getSettings();
277
+ const deviceId = settings.deviceId;
278
+ if (deviceId) {
279
+ const devices = await navigator.mediaDevices.enumerateDevices();
280
+ const currentDevice = devices.find(device => device.deviceId === deviceId);
281
+ if (currentDevice && this.isVirtualCamera(currentDevice)) {
282
+ console.log(`Virtual camera detected in active stream: ${currentDevice.label}`);
283
+ stream.getTracks().forEach(track => track.stop());
284
+ throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');
285
+ }
286
+ }
287
+ }
259
288
  return stream;
260
289
  }
261
290
  catch (error) {
@@ -273,7 +302,23 @@ class CameraService {
273
302
  audio: false
274
303
  });
275
304
  this.lastStreamConstraints = basicConstraints;
276
- return await this.currentStreamPromise;
305
+ const stream = await this.currentStreamPromise;
306
+ // Validate fallback stream as well
307
+ const videoTrack = stream.getVideoTracks()[0];
308
+ if (videoTrack) {
309
+ const settings = videoTrack.getSettings();
310
+ const deviceId = settings.deviceId;
311
+ if (deviceId) {
312
+ const devices = await navigator.mediaDevices.enumerateDevices();
313
+ const currentDevice = devices.find(device => device.deviceId === deviceId);
314
+ if (currentDevice && this.isVirtualCamera(currentDevice)) {
315
+ console.log(`Virtual camera detected in fallback stream: ${currentDevice.label}`);
316
+ stream.getTracks().forEach(track => track.stop());
317
+ throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');
318
+ }
319
+ }
320
+ }
321
+ return stream;
277
322
  }
278
323
  }
279
324
  async switchCamera(cameraId) {
@@ -282,6 +327,12 @@ class CameraService {
282
327
  await this.enumerateDevices();
283
328
  return;
284
329
  }
330
+ // Validate that the target camera is not virtual
331
+ if (this.isVirtualCamera(selectedCamera)) {
332
+ console.log(`Attempted to switch to virtual camera: ${selectedCamera.label}`);
333
+ this.eventBus.emit('error', new Error('No se puede cambiar a cámara virtual'));
334
+ return;
335
+ }
285
336
  await this.setSelectedCamera(cameraId);
286
337
  }
287
338
  isRearCamera(stream) {
@@ -553,6 +604,37 @@ class CameraService {
553
604
  invalidateDeviceCache() {
554
605
  CameraService.deviceEnumerationCache = null;
555
606
  }
607
+ isVirtualCamera(device) {
608
+ const label = device.label.toLowerCase();
609
+ const virtualCameraIndicators = [
610
+ 'obs',
611
+ 'virtual',
612
+ 'snap camera',
613
+ 'manycam',
614
+ 'xsplit',
615
+ 'camtwist',
616
+ 'ecamm',
617
+ 'nvidia broadcast',
618
+ 'droidcam',
619
+ 'iriun',
620
+ 'elgato',
621
+ 'streamlabs',
622
+ 'wirecast',
623
+ 'zoom virtual',
624
+ 'teams virtual',
625
+ 'mmhmm',
626
+ 'camo',
627
+ 'reincubate camo',
628
+ 'webcamoid',
629
+ 'splitcam',
630
+ 'cheese',
631
+ 'guvcview',
632
+ 'yawcam',
633
+ 'webcam 7',
634
+ 'altserver'
635
+ ];
636
+ return virtualCameraIndicators.some(indicator => label.includes(indicator));
637
+ }
556
638
  }
557
639
 
558
640
  class LowMemoryDeviceStrategy {
@@ -2556,10 +2638,13 @@ const JaakStamps = class {
2556
2638
  this.useDocumentDetector = false; // Desactivar detector automático
2557
2639
  // Actualizar el estado con mensaje de error
2558
2640
  this.updateStatus('Modo manual activado', errorMessage, 'error');
2559
- // Ocultar mensaje después de 5 segundos
2560
- setTimeout(() => {
2561
- this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
2562
- }, 5000);
2641
+ // Continuar con la configuración de cámara para mostrar el video
2642
+ this.continueWithCameraSetup().catch(() => {
2643
+ // Si falla la configuración de cámara, mantener el mensaje de error pero cambiar el estado
2644
+ setTimeout(() => {
2645
+ this.updateStatus('Captura manual', 'Use el botón para capturar', 'ready');
2646
+ }, 5000);
2647
+ });
2563
2648
  }
2564
2649
  stopPerformanceMonitoring() {
2565
2650
  // Nota: No necesitamos limpiar más variables porque ya no se usarán