@jaak.ai/stamps 2.1.0-dev.14 → 2.1.0-dev.16

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.
@@ -204,7 +204,21 @@ class CameraService {
204
204
  tempStream.getTracks().forEach(track => track.stop());
205
205
  }
206
206
  const devices = await navigator.mediaDevices.enumerateDevices();
207
- this.availableCameras = devices.filter(device => device.kind === 'videoinput');
207
+ const allCameras = devices.filter(device => device.kind === 'videoinput');
208
+ // Filter out virtual cameras
209
+ this.availableCameras = allCameras.filter(camera => {
210
+ const isVirtual = this.isVirtualCamera(camera);
211
+ if (isVirtual) {
212
+ console.log(`Virtual camera detected and filtered: ${camera.label}`);
213
+ }
214
+ return !isVirtual;
215
+ });
216
+ // Check if there are physical cameras available
217
+ if (this.availableCameras.length === 0) {
218
+ console.log('No physical cameras available');
219
+ this.eventBus.emit('error', new Error('Cámaras virtuales no están permitidas. Use una cámara física.'));
220
+ return [];
221
+ }
208
222
  // Cache the results for optimization
209
223
  CameraService.deviceEnumerationCache = {
210
224
  devices: this.availableCameras,
@@ -254,6 +268,21 @@ class CameraService {
254
268
  });
255
269
  this.lastStreamConstraints = finalConstraints;
256
270
  const stream = await this.currentStreamPromise;
271
+ // Validate that active stream is not from a virtual camera
272
+ const videoTrack = stream.getVideoTracks()[0];
273
+ if (videoTrack) {
274
+ const settings = videoTrack.getSettings();
275
+ const deviceId = settings.deviceId;
276
+ if (deviceId) {
277
+ const devices = await navigator.mediaDevices.enumerateDevices();
278
+ const currentDevice = devices.find(device => device.deviceId === deviceId);
279
+ if (currentDevice && this.isVirtualCamera(currentDevice)) {
280
+ console.log(`Virtual camera detected in active stream: ${currentDevice.label}`);
281
+ stream.getTracks().forEach(track => track.stop());
282
+ throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');
283
+ }
284
+ }
285
+ }
257
286
  return stream;
258
287
  }
259
288
  catch (error) {
@@ -271,7 +300,23 @@ class CameraService {
271
300
  audio: false
272
301
  });
273
302
  this.lastStreamConstraints = basicConstraints;
274
- return await this.currentStreamPromise;
303
+ const stream = await this.currentStreamPromise;
304
+ // Validate fallback stream as well
305
+ const videoTrack = stream.getVideoTracks()[0];
306
+ if (videoTrack) {
307
+ const settings = videoTrack.getSettings();
308
+ const deviceId = settings.deviceId;
309
+ if (deviceId) {
310
+ const devices = await navigator.mediaDevices.enumerateDevices();
311
+ const currentDevice = devices.find(device => device.deviceId === deviceId);
312
+ if (currentDevice && this.isVirtualCamera(currentDevice)) {
313
+ console.log(`Virtual camera detected in fallback stream: ${currentDevice.label}`);
314
+ stream.getTracks().forEach(track => track.stop());
315
+ throw new Error('Cámaras virtuales no están permitidas. Use una cámara física.');
316
+ }
317
+ }
318
+ }
319
+ return stream;
275
320
  }
276
321
  }
277
322
  async switchCamera(cameraId) {
@@ -280,6 +325,12 @@ class CameraService {
280
325
  await this.enumerateDevices();
281
326
  return;
282
327
  }
328
+ // Validate that the target camera is not virtual
329
+ if (this.isVirtualCamera(selectedCamera)) {
330
+ console.log(`Attempted to switch to virtual camera: ${selectedCamera.label}`);
331
+ this.eventBus.emit('error', new Error('No se puede cambiar a cámara virtual'));
332
+ return;
333
+ }
283
334
  await this.setSelectedCamera(cameraId);
284
335
  }
285
336
  isRearCamera(stream) {
@@ -551,6 +602,37 @@ class CameraService {
551
602
  invalidateDeviceCache() {
552
603
  CameraService.deviceEnumerationCache = null;
553
604
  }
605
+ isVirtualCamera(device) {
606
+ const label = device.label.toLowerCase();
607
+ const virtualCameraIndicators = [
608
+ 'obs',
609
+ 'virtual',
610
+ 'snap camera',
611
+ 'manycam',
612
+ 'xsplit',
613
+ 'camtwist',
614
+ 'ecamm',
615
+ 'nvidia broadcast',
616
+ 'droidcam',
617
+ 'iriun',
618
+ 'elgato',
619
+ 'streamlabs',
620
+ 'wirecast',
621
+ 'zoom virtual',
622
+ 'teams virtual',
623
+ 'mmhmm',
624
+ 'camo',
625
+ 'reincubate camo',
626
+ 'webcamoid',
627
+ 'splitcam',
628
+ 'cheese',
629
+ 'guvcview',
630
+ 'yawcam',
631
+ 'webcam 7',
632
+ 'altserver'
633
+ ];
634
+ return virtualCameraIndicators.some(indicator => label.includes(indicator));
635
+ }
554
636
  }
555
637
 
556
638
  class LowMemoryDeviceStrategy {
@@ -2647,6 +2729,12 @@ const JaakStamps = class {
2647
2729
  try {
2648
2730
  // Close the selector immediately when user selects a camera
2649
2731
  this.showCameraSelector = false;
2732
+ // Check if user selected the same camera that's already active
2733
+ const currentCameraId = this.cameraService.getSelectedCameraId();
2734
+ if (currentCameraId === cameraId) {
2735
+ // Same camera selected, just close the selector without switching
2736
+ return;
2737
+ }
2650
2738
  this.isSwitchingCamera = true;
2651
2739
  // Stop current video stream
2652
2740
  if (this.videoStream) {