easyproctor 2.3.5 → 2.5.0

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/esm/index.js CHANGED
@@ -6278,6 +6278,7 @@ var BaseDetection = class {
6278
6278
  this.modelAssetPath = modelAssetPath;
6279
6279
  this.classVideo = classVideo;
6280
6280
  this.classDiv = classDiv;
6281
+ this.startTime = new Date(Date.now());
6281
6282
  paramsConfig && (this.paramsConfig = paramsConfig);
6282
6283
  options && (this.options = options);
6283
6284
  }
@@ -6367,15 +6368,22 @@ var BaseDetection = class {
6367
6368
  this.options.onRealtimeAlertsCallback && this.options.onRealtimeAlertsCallback({
6368
6369
  status: "ALERT",
6369
6370
  description: this.alertTranslate(description),
6370
- type
6371
+ type,
6372
+ category: description,
6373
+ begin: Date.now() - this.startTime.getTime(),
6374
+ end: Date.now() - this.startTime.getTime()
6371
6375
  });
6372
- this.error && (this.error.innerText = description);
6376
+ if (this.options.onRealtimeAlertsCallback == null)
6377
+ this.error && (this.error.innerText = description);
6373
6378
  }
6374
6379
  handleOk(description, type) {
6375
6380
  this.options.onRealtimeAlertsCallback && this.options.onRealtimeAlertsCallback({
6376
6381
  status: "OK",
6377
6382
  description: this.alertTranslate(description),
6378
- type
6383
+ type,
6384
+ category: description,
6385
+ begin: Date.now() - this.startTime.getTime(),
6386
+ end: Date.now() - this.startTime.getTime()
6379
6387
  });
6380
6388
  this.error && (this.error.innerText = "");
6381
6389
  }
@@ -6405,6 +6413,18 @@ var BaseDetection = class {
6405
6413
  return "Face detectada";
6406
6414
  case "ok_position_face_detected":
6407
6415
  return "Face na posi\xE7\xE3o correta";
6416
+ case "wrong_face_size_detected":
6417
+ return "Face muito perto da c\xE2mera, afaste-se um pouco mais";
6418
+ case "wrong_face_position_edge_detected":
6419
+ return "Face muito pr\xF3xima da borda, mova-se para o centro da tela";
6420
+ case "wrong_face_position_move_right_detected":
6421
+ return "Face n\xE3o centralizada, mova-se para a direita";
6422
+ case "wrong_face_position_move_left_detected":
6423
+ return "Face n\xE3o centralizada, mova-se para a esquerda";
6424
+ case "wrong_face_position_move_top_detected":
6425
+ return "Face n\xE3o centralizada, mova-se para cima";
6426
+ case "wrong_face_position_move_bottom_detected":
6427
+ return "Face n\xE3o centralizada, mova-se para baixo";
6408
6428
  default:
6409
6429
  return description;
6410
6430
  }
@@ -6437,6 +6457,13 @@ var FaceDetection = class extends BaseDetection {
6437
6457
  constructor(options, paramsConfig, classVideo = "videoPreviewFrameDetection", classDiv = "liveViewFrameDetection") {
6438
6458
  super("FaceDetector", `https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite`, options, paramsConfig, classVideo, classDiv);
6439
6459
  this.emmitedPositionAlert = false;
6460
+ this.emmitedFaceAlert = false;
6461
+ }
6462
+ stopDetection() {
6463
+ super.stopDetection();
6464
+ if (this.emmitedFaceAlert) {
6465
+ this.handleOk("face_ok", "face_detection_on_stream");
6466
+ }
6440
6467
  }
6441
6468
  displayVideoDetections(result) {
6442
6469
  for (const child of this.children) {
@@ -6499,31 +6526,71 @@ var FaceDetection = class extends BaseDetection {
6499
6526
  }
6500
6527
  }
6501
6528
  verify(result) {
6502
- var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
6529
+ var _a2;
6503
6530
  if (((_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.detectFace) && result.detections.length !== this.numFacesSent) {
6504
6531
  this.numFacesSent = result.detections.length;
6505
6532
  if (result.detections.length === 0) {
6506
6533
  this.handleAlert("no_face_detected", "face_detection_on_stream");
6534
+ this.emmitedFaceAlert = true;
6535
+ return;
6507
6536
  } else if (result.detections.length > 1) {
6508
6537
  this.handleAlert("multiple_faces_detected", "face_detection_on_stream");
6538
+ this.emmitedFaceAlert = true;
6539
+ return;
6509
6540
  } else {
6510
6541
  this.handleOk("face_ok", "face_detection_on_stream");
6511
- }
6512
- }
6513
- const faceSize = ((_b = result.detections[0]) == null ? void 0 : _b.keypoints[3].y) - ((_c2 = result.detections[0]) == null ? void 0 : _c2.keypoints[0].y);
6514
- if (((_d = result.detections[0]) == null ? void 0 : _d.keypoints[4].x) < 0.2 || ((_e3 = result.detections[0]) == null ? void 0 : _e3.keypoints[5].x) > 0.8) {
6515
- !this.emmitedPositionAlert && this.handleAlert("wrong_position_face_detected", "position_detection_on_stream");
6542
+ this.emmitedFaceAlert = false;
6543
+ }
6544
+ }
6545
+ if (result.detections.length === 0) return;
6546
+ let face = result.detections[0].boundingBox;
6547
+ let video = document.getElementById(this.classVideo);
6548
+ const imageWidth = video.videoWidth;
6549
+ const imageHeight = video.videoHeight;
6550
+ let failedFacePosition = false;
6551
+ if (imageWidth > imageHeight) {
6552
+ if (face.height / imageHeight > 0.7) {
6553
+ !this.emmitedPositionAlert && this.handleAlert("wrong_face_size_detected", "position_detection_on_stream");
6554
+ failedFacePosition = true;
6555
+ this.emmitedPositionAlert = true;
6556
+ } else if (face.width / imageWidth > 0.7) {
6557
+ !this.emmitedPositionAlert && this.handleAlert("wrong_face_size_detected", "position_detection_on_stream");
6558
+ this.emmitedPositionAlert = true;
6559
+ failedFacePosition = true;
6560
+ }
6561
+ }
6562
+ let start = [face.originX, face.originY];
6563
+ let end = [face.originX + face.width, face.originY + face.height];
6564
+ if (start[0] < 0.1 * face.width || start[1] < 0.2 * face.height || end[0] > imageWidth - 0.1 * face.width || end[1] > imageHeight - 5) {
6565
+ !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_edge_detected", "position_detection_on_stream");
6516
6566
  this.emmitedPositionAlert = true;
6517
- } else if (((_f = result.detections[0]) == null ? void 0 : _f.keypoints[0].y) < 0.2 || ((_g = result.detections[0]) == null ? void 0 : _g.keypoints[0].y) > 0.8) {
6518
- !this.emmitedPositionAlert && this.handleAlert("wrong_position_face_detected", "position_detection_on_stream");
6567
+ failedFacePosition = true;
6568
+ }
6569
+ let leftGap = start[0];
6570
+ let rightGap = imageWidth - end[0];
6571
+ let topGap = start[1];
6572
+ let bottomGap = imageHeight - end[1];
6573
+ if (leftGap > 2 * rightGap) {
6574
+ !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_right_detected", "position_detection_on_stream");
6519
6575
  this.emmitedPositionAlert = true;
6520
- } else if (((_h = result.detections[0]) == null ? void 0 : _h.keypoints[3].y) < 0.4 || ((_i3 = result.detections[0]) == null ? void 0 : _i3.keypoints[3].y) > 0.9) {
6521
- !this.emmitedPositionAlert && this.handleAlert("wrong_position_face_detected", "position_detection_on_stream");
6576
+ failedFacePosition = true;
6577
+ }
6578
+ if (topGap > 4 * bottomGap) {
6579
+ !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_top_detected", "position_detection_on_stream");
6522
6580
  this.emmitedPositionAlert = true;
6523
- } else if (faceSize > 0.35) {
6524
- !this.emmitedPositionAlert && this.handleAlert("wrong_position_face_detected", "position_detection_on_stream");
6581
+ failedFacePosition = true;
6582
+ }
6583
+ if (rightGap > 2 * leftGap) {
6584
+ !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_left_detected", "position_detection_on_stream");
6525
6585
  this.emmitedPositionAlert = true;
6526
- } else {
6586
+ failedFacePosition = true;
6587
+ }
6588
+ if (bottomGap > 3 * topGap) {
6589
+ !this.emmitedPositionAlert && this.handleAlert("wrong_face_position_move_bottom_detected", "position_detection_on_stream");
6590
+ this.emmitedPositionAlert = true;
6591
+ failedFacePosition = true;
6592
+ }
6593
+ if (failedFacePosition == false) {
6527
6594
  this.emmitedPositionAlert && this.handleOk("ok_position_face_detected", "position_detection_on_stream");
6528
6595
  this.emmitedPositionAlert = false;
6529
6596
  }
@@ -9086,6 +9153,14 @@ var BackendService = class {
9086
9153
  jwt: this.token
9087
9154
  });
9088
9155
  }
9156
+ async goToExternalCameraPositionStep(externalSessionId) {
9157
+ return await this.makeRequest({
9158
+ path: `/ExternalCamera/go-to-position-step/${externalSessionId}`,
9159
+ method: "POST",
9160
+ body: {},
9161
+ jwt: this.token
9162
+ });
9163
+ }
9089
9164
  async externalCameraFinish(externalSessionId) {
9090
9165
  return await this.makeRequest({
9091
9166
  path: `/ExternalCamera/finish/${externalSessionId}`,
@@ -9094,15 +9169,16 @@ var BackendService = class {
9094
9169
  jwt: this.token
9095
9170
  });
9096
9171
  }
9097
- async confirmStart(proctoringOptions, proctoringType, latitude, longitude) {
9172
+ async confirmStart(proctoringOptions, sessionOptions, latitude, longitude) {
9098
9173
  return await this.makeRequest({
9099
9174
  path: `/proctoring/start/${proctoringOptions.examId}`,
9100
9175
  method: "POST",
9101
9176
  body: {
9102
9177
  clientId: proctoringOptions.clientId,
9103
- proctoringType,
9178
+ proctoringType: sessionOptions.proctoringType,
9104
9179
  latitude,
9105
- longitude
9180
+ longitude,
9181
+ captureScreen: sessionOptions.captureScreen
9106
9182
  },
9107
9183
  jwt: proctoringOptions.token
9108
9184
  });
@@ -9149,8 +9225,8 @@ var BackendService = class {
9149
9225
  }
9150
9226
  });
9151
9227
  }
9152
- async finishAndSendUrls(proctoringOptions, proctoringSession) {
9153
- await this.makeRequest({
9228
+ async finishAndSendUrls(proctoringOptions) {
9229
+ return await this.makeRequest({
9154
9230
  path: `/proctoring/finish/${proctoringOptions.examId}`,
9155
9231
  method: "POST",
9156
9232
  body: {
@@ -9196,6 +9272,51 @@ var BackendService = class {
9196
9272
  });
9197
9273
  return result.data;
9198
9274
  }
9275
+ async startChallenge(body) {
9276
+ const result = await this.makeRequestAxios({
9277
+ path: `/Challenge/start`,
9278
+ method: "POST",
9279
+ jwt: this.token,
9280
+ body
9281
+ });
9282
+ return result.data;
9283
+ }
9284
+ async stopChallenge(challengeId, body) {
9285
+ const result = await this.makeRequestAxios({
9286
+ path: `/Challenge/stop/${challengeId}`,
9287
+ method: "POST",
9288
+ jwt: this.token,
9289
+ body
9290
+ });
9291
+ return result.data;
9292
+ }
9293
+ async startRealtimeAlert(body) {
9294
+ const result = await this.makeRequestAxios({
9295
+ path: `/Realtime/start-warning`,
9296
+ method: "POST",
9297
+ jwt: this.token,
9298
+ body
9299
+ });
9300
+ return result.data;
9301
+ }
9302
+ async stopRealtimeAlert(body) {
9303
+ const result = await this.makeRequestAxios({
9304
+ path: `/Realtime/stop-warning`,
9305
+ method: "POST",
9306
+ jwt: this.token,
9307
+ body
9308
+ });
9309
+ return result.data;
9310
+ }
9311
+ async verifyFace(proctoringId2, faceImage) {
9312
+ const result = await this.makeRequestAxios({
9313
+ path: `/Realtime/verify-face`,
9314
+ method: "POST",
9315
+ jwt: this.token,
9316
+ body: { "proctoringId": proctoringId2, "base64": faceImage }
9317
+ });
9318
+ return result.data;
9319
+ }
9199
9320
  async getServerHour(token) {
9200
9321
  return await this.makeRequest({
9201
9322
  path: `/Proctoring/server-hour`,
@@ -9523,7 +9644,9 @@ var getDefaultProctoringOptions = {
9523
9644
  onBufferSizeError: false,
9524
9645
  useGeolocation: false,
9525
9646
  useSpyScan: false,
9526
- useExternalCamera: false
9647
+ useExternalCamera: false,
9648
+ useChallenge: false,
9649
+ screenRecorderOptions: { width: 1280, height: 720 }
9527
9650
  };
9528
9651
 
9529
9652
  // src/proctoring/options/ProctoringVideoOptions.ts
@@ -9546,6 +9669,35 @@ var getDefaultProctoringVideoOptions = {
9546
9669
  minHeight: 480
9547
9670
  };
9548
9671
 
9672
+ // src/utils/browserInformations.ts
9673
+ function fnBrowserDetect() {
9674
+ const userAgent = navigator.userAgent;
9675
+ let browserName;
9676
+ if (userAgent.match(/chrome|chromium|crios/i)) {
9677
+ browserName = "chrome";
9678
+ } else if (userAgent.match(/firefox|fxios/i)) {
9679
+ browserName = "firefox";
9680
+ } else if (userAgent.match(/safari/i)) {
9681
+ browserName = "safari";
9682
+ } else if (userAgent.match(/opr\//i)) {
9683
+ browserName = "opera";
9684
+ } else if (userAgent.match(/edg/i)) {
9685
+ browserName = "edge";
9686
+ } else {
9687
+ browserName = "No browser detection";
9688
+ }
9689
+ return browserName;
9690
+ }
9691
+ function isMobileDevice() {
9692
+ if ("userAgentData" in navigator) {
9693
+ const navUAData = navigator.userAgentData;
9694
+ const mobile = navUAData.mobile || false;
9695
+ const platform = navUAData.platform || "";
9696
+ return mobile || /Android|iOS/i.test(platform);
9697
+ }
9698
+ return /Android|iPhone|iPad|iPod/i.test(navigator.userAgent);
9699
+ }
9700
+
9549
9701
  // src/plugins/insights.ts
9550
9702
  var backendService;
9551
9703
  var init = (backend) => {
@@ -9553,6 +9705,7 @@ var init = (backend) => {
9553
9705
  return backendService;
9554
9706
  };
9555
9707
  var eventNames = {
9708
+ DEVICES_CHECKED: "devices_checked",
9556
9709
  START: "start",
9557
9710
  FINISH: "finish",
9558
9711
  ERROR: "error",
@@ -9569,6 +9722,7 @@ var eventNames = {
9569
9722
  };
9570
9723
  var log = (eventName, properties) => backendService && backendService.log(eventName, properties);
9571
9724
  var trackers = {
9725
+ registerDevicesChecked: (proctoringId2, success, description) => log(eventNames.DEVICES_CHECKED, { proctoringId: proctoringId2, success, description }),
9572
9726
  registerStart: (proctoringId2, success, description) => log(eventNames.START, { proctoringId: proctoringId2, success, description }),
9573
9727
  registerFinish: (proctoringId2, success, description) => log(eventNames.FINISH, { proctoringId: proctoringId2, success, description }),
9574
9728
  registerError: (proctoringId2, description) => log(eventNames.ERROR, { proctoringId: proctoringId2, description }),
@@ -9859,6 +10013,12 @@ var ObjectDetection = class extends BaseDetection {
9859
10013
  constructor(options, paramsConfig, classVideo = "videoPreviewFrameDetection", classDiv = "liveViewFrameDetection") {
9860
10014
  super("ObjectDetector", `https://storage.googleapis.com/mediapipe-models/object_detector/efficientdet_lite0/float16/1/efficientdet_lite0.tflite`, options, paramsConfig, classVideo, classDiv);
9861
10015
  }
10016
+ stopDetection() {
10017
+ super.stopDetection();
10018
+ if (this.numPersonsSent > 0) {
10019
+ this.handleOk("person_ok", "person_detection_on_stream");
10020
+ }
10021
+ }
9862
10022
  displayVideoDetections(result) {
9863
10023
  for (const child of this.children) {
9864
10024
  this.liveView.removeChild(child);
@@ -10033,6 +10193,10 @@ var CameraRecorder = class {
10033
10193
  this.blobsRTC = [];
10034
10194
  this.imageCount = 0;
10035
10195
  this.filesToUpload = [];
10196
+ this.animationFrameId = null;
10197
+ this.isCanvasLoopActive = false;
10198
+ this.hardwareStream = null;
10199
+ this.internalClonedStream = null;
10036
10200
  this.currentRetries = 0;
10037
10201
  this.noiseWait = 20;
10038
10202
  this.options = options;
@@ -10109,7 +10273,7 @@ var CameraRecorder = class {
10109
10273
  }
10110
10274
  };
10111
10275
  try {
10112
- this.cameraStream = await navigator.mediaDevices.getUserMedia(
10276
+ this.hardwareStream = await navigator.mediaDevices.getUserMedia(
10113
10277
  constraints
10114
10278
  );
10115
10279
  } catch (error) {
@@ -10117,6 +10281,16 @@ var CameraRecorder = class {
10117
10281
  throw "N\xE3o foi poss\xEDvel conectar a camera, ela pode estar sendo utilizada por outro programa";
10118
10282
  throw error;
10119
10283
  }
10284
+ this.cameraStream = this.hardwareStream;
10285
+ const track = this.cameraStream.getVideoTracks()[0];
10286
+ const settings = track.getSettings();
10287
+ const { width = 0, height = 0 } = settings;
10288
+ const needsRotationFix = isMobileDevice() && width > height;
10289
+ this.isCanvasLoopActive = true;
10290
+ if (needsRotationFix) {
10291
+ console.log("Aplicando corre\xE7\xE3o e substituindo stream p\xFAblico...");
10292
+ this.cameraStream = this.createRotatedStream(this.hardwareStream);
10293
+ }
10120
10294
  const {
10121
10295
  startRecording,
10122
10296
  stopRecording,
@@ -10126,6 +10300,7 @@ var CameraRecorder = class {
10126
10300
  getBufferSize
10127
10301
  } = recorder(
10128
10302
  this.cameraStream,
10303
+ // streamToRecord,
10129
10304
  this.blobs,
10130
10305
  this.options.onBufferSizeError,
10131
10306
  (e3) => this.bufferError(e3),
@@ -10138,11 +10313,9 @@ var CameraRecorder = class {
10138
10313
  this.recorderOptions = recorderOptions;
10139
10314
  this.getBufferSize = getBufferSize;
10140
10315
  this.recordingStart();
10141
- const tracks = this.cameraStream.getVideoTracks();
10142
- const settings = tracks[0].getSettings();
10143
- if (this.videoOptions.minWidth > settings.width || this.videoOptions.minHeight > settings.height) {
10316
+ if (this.videoOptions.minWidth > width || this.videoOptions.minHeight > height) {
10144
10317
  throw STREAM_UNDER_MINIMUM_PERMITTED;
10145
- } else if (this.videoOptions.width !== settings.width || this.videoOptions.height !== settings.height) {
10318
+ } else if (this.videoOptions.width !== width || this.videoOptions.height !== height) {
10146
10319
  trackers.registerAnotherStream(
10147
10320
  this.proctoringId,
10148
10321
  `Maybe have another stream active
@@ -10163,10 +10336,29 @@ Setting: ${JSON.stringify(settings, null, 2)}`
10163
10336
  await this.objectDetection.enableCam(this.cameraStream);
10164
10337
  }
10165
10338
  this.filesToUpload = [];
10166
- this.options.proctoringType == "REALTIME" && this.captureFrame();
10167
10339
  }
10168
10340
  async stopRecording() {
10341
+ this.isCanvasLoopActive = false;
10169
10342
  this.recordingStop && await this.recordingStop();
10343
+ try {
10344
+ if (this.animationFrameId) {
10345
+ cancelAnimationFrame(this.animationFrameId);
10346
+ this.animationFrameId = null;
10347
+ }
10348
+ if (this.cameraStream) {
10349
+ this.cameraStream.getTracks().forEach((track) => track.stop());
10350
+ }
10351
+ if (this.internalClonedStream) {
10352
+ this.internalClonedStream.getTracks().forEach((track) => track.stop());
10353
+ this.internalClonedStream = null;
10354
+ }
10355
+ if (this.hardwareStream) {
10356
+ this.hardwareStream.getTracks().forEach((track) => track.stop());
10357
+ this.hardwareStream = null;
10358
+ }
10359
+ } catch (e3) {
10360
+ console.error("Erro ao parar os streams de m\xEDdia.");
10361
+ }
10170
10362
  this.faceDetection && this.faceDetection.detecting && this.faceDetection.stopDetection();
10171
10363
  this.objectDetection && this.objectDetection.detecting && this.objectDetection.stopDetection();
10172
10364
  clearInterval(this.imageInterval);
@@ -10233,6 +10425,36 @@ Setting: ${JSON.stringify(settings, null, 2)}`
10233
10425
  }
10234
10426
  }, this.paramsConfig.imageBehaviourParameters.uploadInterval * 1e3);
10235
10427
  }
10428
+ async getCurrentImageBase64() {
10429
+ if (!this.video || !this.canvas) {
10430
+ this.configImageCapture();
10431
+ } else {
10432
+ if (this.video.srcObject !== this.cameraStream) {
10433
+ this.video.srcObject = this.cameraStream;
10434
+ await this.video.play();
10435
+ }
10436
+ }
10437
+ if (this.video.paused) {
10438
+ await this.video.play();
10439
+ }
10440
+ await new Promise((resolve) => {
10441
+ if (this.video.readyState >= 2) {
10442
+ resolve();
10443
+ return;
10444
+ }
10445
+ const onLoadedMetadata = () => {
10446
+ this.video.removeEventListener("loadedmetadata", onLoadedMetadata);
10447
+ setTimeout(() => resolve(), 50);
10448
+ };
10449
+ this.video.addEventListener("loadedmetadata", onLoadedMetadata);
10450
+ setTimeout(() => {
10451
+ this.video.removeEventListener("loadedmetadata", onLoadedMetadata);
10452
+ resolve();
10453
+ }, 500);
10454
+ });
10455
+ this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
10456
+ return this.canvas.toDataURL("image/jpeg");
10457
+ }
10236
10458
  // De um em um segundo captura um frame
10237
10459
  captureFrame() {
10238
10460
  let imageFile;
@@ -10367,6 +10589,46 @@ Setting: ${JSON.stringify(settings, null, 2)}`
10367
10589
  }
10368
10590
  this.noiseWait++;
10369
10591
  }
10592
+ /**
10593
+ * Cria um stream processado onde os frames são rotacionados via Canvas.
10594
+ * Isso corrige o problema de gravação deitada em iOS/Mobile.
10595
+ */
10596
+ createRotatedStream(originalStream) {
10597
+ this.internalClonedStream = originalStream.clone();
10598
+ const video = document.createElement("video");
10599
+ video.srcObject = this.internalClonedStream;
10600
+ video.muted = true;
10601
+ video.playsInline = true;
10602
+ video.play();
10603
+ const canvas = document.createElement("canvas");
10604
+ const ctx = canvas.getContext("2d");
10605
+ const track = originalStream.getVideoTracks()[0];
10606
+ const settings = track.getSettings();
10607
+ const width = settings.width || 640;
10608
+ const height = settings.height || 480;
10609
+ canvas.width = height;
10610
+ canvas.height = width;
10611
+ const draw = () => {
10612
+ if (video.paused || video.ended) return;
10613
+ if (!this.isCanvasLoopActive) return;
10614
+ if (ctx) {
10615
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
10616
+ ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
10617
+ }
10618
+ if (this.isCanvasLoopActive) {
10619
+ this.animationFrameId = requestAnimationFrame(draw);
10620
+ }
10621
+ };
10622
+ video.onplaying = () => {
10623
+ this.isCanvasLoopActive = true;
10624
+ draw();
10625
+ };
10626
+ const canvasStream = canvas.captureStream(30);
10627
+ originalStream.getAudioTracks().forEach((track2) => {
10628
+ canvasStream.addTrack(track2);
10629
+ });
10630
+ return canvasStream;
10631
+ }
10370
10632
  };
10371
10633
 
10372
10634
  // src/new-flow/checkers/DeviceCheckerUI.ts
@@ -10397,11 +10659,17 @@ var DeviceCheckerUI = class {
10397
10659
  display: inline-block;
10398
10660
  width: 22px;
10399
10661
  height: 22px;
10662
+ min-width: 22px;
10663
+ max-width: 22px;
10664
+ min-height: 22px;
10665
+ max-height: 22px;
10666
+ flex-shrink: 0;
10400
10667
  -ms-transform: rotate(45deg); /* IE 9 */
10401
10668
  -webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
10402
10669
  transform: rotate(45deg);
10403
10670
  border: 1px solid #16A34A;
10404
10671
  border-radius: 22px;
10672
+ box-sizing: border-box;
10405
10673
  }
10406
10674
 
10407
10675
  .checkmark_stem {
@@ -10426,11 +10694,17 @@ var DeviceCheckerUI = class {
10426
10694
  display: inline-block;
10427
10695
  width: 22px;
10428
10696
  height: 22px;
10697
+ min-width: 22px;
10698
+ max-width: 22px;
10699
+ min-height: 22px;
10700
+ max-height: 22px;
10701
+ flex-shrink: 0;
10429
10702
  -ms-transform: rotate(45deg); /* IE 9 */
10430
10703
  -webkit-transform: rotate(45deg); /* Chrome, Safari, Opera */
10431
10704
  transform: rotate(45deg);
10432
10705
  border: 1px solid #FF0000;
10433
10706
  border-radius: 22px;
10707
+ box-sizing: border-box;
10434
10708
  }
10435
10709
 
10436
10710
  .checkmark_stem_error {
@@ -10481,6 +10755,179 @@ var DeviceCheckerUI = class {
10481
10755
  left: 5px;
10482
10756
  top: 2px;
10483
10757
  }
10758
+
10759
+ /* Responsive styles */
10760
+ #checkDevices .modal-responsive {
10761
+ box-sizing: border-box !important;
10762
+ }
10763
+
10764
+ #checkDevices .modal-responsive * {
10765
+ box-sizing: border-box;
10766
+ }
10767
+
10768
+ /* Desktop screens - maintain gap between checks and camera */
10769
+ @media (min-width: 769px) {
10770
+ #checkDevices .modal-responsive .camera-container-responsive {
10771
+ gap: 20px !important;
10772
+ }
10773
+ }
10774
+
10775
+ @media (max-width: 768px) {
10776
+ #checkDevices .modal-responsive {
10777
+ width: 95% !important;
10778
+ max-width: 95% !important;
10779
+ margin: 10px !important;
10780
+ }
10781
+
10782
+ #checkDevices .modal-responsive h3 {
10783
+ font-size: 18px !important;
10784
+ padding: 15px 0px !important;
10785
+ }
10786
+
10787
+ #checkDevices .modal-responsive .camera-header-responsive,
10788
+ #checkDevices .modal-responsive .mic-header-responsive {
10789
+ flex-direction: column !important;
10790
+ gap: 10px !important;
10791
+ align-items: flex-start !important;
10792
+ }
10793
+
10794
+ #checkDevices .modal-responsive .camera-header-responsive select,
10795
+ #checkDevices .modal-responsive .mic-header-responsive select {
10796
+ max-width: 100% !important;
10797
+ width: 100% !important;
10798
+ }
10799
+
10800
+ #checkDevices .modal-responsive .camera-container-responsive {
10801
+ flex-direction: row !important;
10802
+ align-items: flex-start !important;
10803
+ gap: 15px !important;
10804
+ padding-left: 10px !important;
10805
+ padding-right: 10px !important;
10806
+ }
10807
+
10808
+ #checkDevices .modal-responsive .video-responsive {
10809
+ width: auto !important;
10810
+ max-width: 320px !important;
10811
+ flex-shrink: 0 !important;
10812
+ max-height: 240px !important;
10813
+ }
10814
+
10815
+ #checkDevices .modal-responsive .video-responsive video {
10816
+ width: 100% !important;
10817
+ max-width: 320px !important;
10818
+ max-height: 200px !important;
10819
+ height: auto !important;
10820
+ object-fit: contain !important;
10821
+ }
10822
+
10823
+ #checkDevices .modal-responsive .mic-container-responsive {
10824
+ flex-direction: column !important;
10825
+ padding: 0px 20px !important;
10826
+ }
10827
+
10828
+ #checkDevices .modal-responsive .mic-meter-responsive {
10829
+ width: 100% !important;
10830
+ }
10831
+
10832
+ #checkDevices .modal-responsive .range-responsive {
10833
+ width: 100% !important;
10834
+ }
10835
+
10836
+ #checkDevices .modal-responsive .pronounce-responsive {
10837
+ margin-left: 10px !important;
10838
+ margin-right: 10px !important;
10839
+ width: calc(100% - 20px) !important;
10840
+ }
10841
+
10842
+ #checkDevices .modal-responsive .button-responsive {
10843
+ height: 60px !important;
10844
+ font-size: 14px !important;
10845
+ }
10846
+
10847
+ #checkDevices .modal-responsive .divider-responsive {
10848
+ height: 60px !important;
10849
+ }
10850
+ }
10851
+
10852
+ @media (max-width: 480px) {
10853
+ #checkDevices .modal-responsive {
10854
+ width: 98% !important;
10855
+ max-width: 98% !important;
10856
+ border-radius: 8px !important;
10857
+ margin: 5px !important;
10858
+ }
10859
+
10860
+ #checkDevices .modal-responsive .camera-header-responsive,
10861
+ #checkDevices .modal-responsive .mic-header-responsive {
10862
+ gap: 0px !important;
10863
+ }
10864
+
10865
+ #checkDevices .modal-responsive .camera-header-responsive h3,
10866
+ #checkDevices .modal-responsive .mic-header-responsive h3 {
10867
+ margin: 0 !important;
10868
+ margin-top: 0 !important;
10869
+ margin-bottom: 0 !important;
10870
+ margin-left: 0 !important;
10871
+ margin-right: 0 !important;
10872
+ padding: 0 !important;
10873
+ padding-top: 0 !important;
10874
+ padding-bottom: 0 !important;
10875
+ }
10876
+
10877
+ #checkDevices .modal-responsive .camera-header-responsive select,
10878
+ #checkDevices .modal-responsive .mic-header-responsive select {
10879
+ margin: 0 !important;
10880
+ margin-top: 0 !important;
10881
+ margin-bottom: 0 !important;
10882
+ margin-left: 0 !important;
10883
+ margin-right: 0 !important;
10884
+ padding-top: 0 !important;
10885
+ }
10886
+
10887
+ #checkDevices .modal-responsive .camera-container-responsive {
10888
+ flex-direction: row !important;
10889
+ gap: 10px !important;
10890
+ padding-left: 5px !important;
10891
+ padding-right: 5px !important;
10892
+ }
10893
+
10894
+ #checkDevices .modal-responsive .video-responsive {
10895
+ max-width: 200px !important;
10896
+ max-height: 150px !important;
10897
+ }
10898
+
10899
+ #checkDevices .modal-responsive .video-responsive video {
10900
+ max-width: 200px !important;
10901
+ max-height: 150px !important;
10902
+ }
10903
+
10904
+ #checkDevices .modal-responsive .pronounce-responsive {
10905
+ margin-left: 5px !important;
10906
+ margin-right: 5px !important;
10907
+ width: calc(100% - 10px) !important;
10908
+ padding: 0 5px !important;
10909
+ }
10910
+
10911
+ #checkDevices .modal-responsive h3 {
10912
+ font-size: 16px !important;
10913
+ padding: 12px 0px !important;
10914
+ margin-bottom: 10px !important;
10915
+ }
10916
+
10917
+ #checkDevices .modal-responsive .camera-header-responsive h3,
10918
+ #checkDevices .modal-responsive .mic-header-responsive h3 {
10919
+ font-size: 14px !important;
10920
+ }
10921
+
10922
+ #checkDevices .modal-responsive .button-responsive {
10923
+ height: 50px !important;
10924
+ font-size: 13px !important;
10925
+ }
10926
+
10927
+ #checkDevices .modal-responsive .divider-responsive {
10928
+ height: 50px !important;
10929
+ }
10930
+ }
10484
10931
  `;
10485
10932
  document.getElementsByTagName("head")[0].appendChild(style);
10486
10933
  const fullBg = document.createElement("div");
@@ -10493,24 +10940,31 @@ var DeviceCheckerUI = class {
10493
10940
  left: "0",
10494
10941
  height: "100vh",
10495
10942
  width: "100%",
10943
+ maxWidth: "100vw",
10496
10944
  display: "flex",
10497
10945
  alignItems: "center",
10498
- justifyContent: "center"
10946
+ justifyContent: "center",
10947
+ overflow: "auto",
10948
+ boxSizing: "border-box"
10499
10949
  };
10500
10950
  this.applyStyles(fullBg, fullBgStyles);
10501
10951
  const modal = document.createElement("div");
10952
+ modal.setAttribute("class", "modal-responsive");
10502
10953
  const modalStyles = {
10503
10954
  backgroundColor: "#fff",
10504
10955
  zIndex: "1001",
10505
- width: "600px",
10956
+ width: "90%",
10957
+ maxWidth: "600px",
10506
10958
  borderRadius: "10px",
10507
10959
  display: "flex",
10508
10960
  flexDirection: "column",
10509
- alignItems: "center"
10961
+ alignItems: "center",
10962
+ boxSizing: "border-box",
10963
+ overflow: "hidden"
10510
10964
  };
10511
10965
  this.applyStyles(modal, modalStyles);
10512
10966
  const h3 = document.createElement("h3");
10513
- h3.innerText = "Para iniciar configure a c\xE2mera e o microfone";
10967
+ h3.innerText = "Checagem de dispositivos";
10514
10968
  const h3Styles = {
10515
10969
  color: "rgba(0, 0, 0, .7)",
10516
10970
  fontWeight: "bold",
@@ -10524,6 +10978,7 @@ var DeviceCheckerUI = class {
10524
10978
  this.applyStyles(h3, h3Styles);
10525
10979
  modal.appendChild(h3);
10526
10980
  const divCameraHeader = document.createElement("div");
10981
+ divCameraHeader.setAttribute("class", "camera-header-responsive");
10527
10982
  const h3Camera = document.createElement("h3");
10528
10983
  const selectCamera = document.createElement("select");
10529
10984
  selectCamera.setAttribute("id", "cameraSelect");
@@ -10543,33 +10998,49 @@ var DeviceCheckerUI = class {
10543
10998
  };
10544
10999
  this.applyStyles(h3Camera, h3CameraStyles);
10545
11000
  selectCamera.style.maxWidth = "400px";
11001
+ selectCamera.style.minWidth = "150px";
11002
+ selectCamera.style.width = "100%";
11003
+ selectCamera.style.boxSizing = "border-box";
10546
11004
  divCameraHeader.appendChild(h3Camera);
10547
11005
  divCameraHeader.appendChild(selectCamera);
10548
11006
  modal.appendChild(divCameraHeader);
10549
11007
  const divCamera = document.createElement("div");
10550
11008
  divCamera.setAttribute("id", "liveCheckDevices");
11009
+ divCamera.setAttribute("class", "camera-container-responsive");
10551
11010
  const videoDiv = document.createElement("div");
11011
+ videoDiv.setAttribute("class", "video-responsive");
10552
11012
  const video = document.createElement("video");
10553
11013
  const center = document.createElement("div");
10554
11014
  video.setAttribute("id", "cameraStream");
10555
11015
  video.muted = true;
11016
+ video.setAttribute("playsinline", "true");
11017
+ video.setAttribute("webkit-playsinline", "true");
11018
+ video.autoplay = true;
10556
11019
  const divCameraStyles = {
10557
- width: "calc(100% - 40px)",
11020
+ width: "100%",
10558
11021
  display: "flex",
10559
11022
  justifyContent: "space-between",
11023
+ gap: "20px",
10560
11024
  borderBottom: "2px solid rgba(0, 0, 0, .1)",
10561
11025
  paddingBottom: "15px",
11026
+ paddingLeft: "20px",
11027
+ paddingRight: "20px",
11028
+ boxSizing: "border-box",
10562
11029
  transform: "rotateY(180deg)"
10563
11030
  };
10564
11031
  this.applyStyles(divCamera, divCameraStyles);
10565
11032
  const videoStyles = {
10566
- width: "20rem",
11033
+ width: "100%",
11034
+ maxWidth: "320px",
11035
+ height: "auto",
11036
+ maxHeight: "240px",
11037
+ objectFit: "contain",
10567
11038
  backgroundColor: "#000",
10568
11039
  borderRadius: "10px",
10569
11040
  marginBottom: "15px"
10570
11041
  };
10571
11042
  this.applyStyles(video, videoStyles);
10572
- this.applyStyles(videoDiv, { position: "relative" });
11043
+ this.applyStyles(videoDiv, { position: "relative", width: "100%", maxWidth: "320px", maxHeight: "240px", boxSizing: "border-box" });
10573
11044
  const alertDivResolution = document.createElement("div");
10574
11045
  alertDivResolution.setAttribute("class", "alert-div");
10575
11046
  alertDivResolution.setAttribute("id", "alertDivResolution");
@@ -10663,12 +11134,17 @@ var DeviceCheckerUI = class {
10663
11134
  alertDivSpyCam.appendChild(checkmark_SpyCam);
10664
11135
  alertDivSpyCam.appendChild(SpyCamAlert);
10665
11136
  center.style.transform = "rotateY(180deg)";
11137
+ center.style.display = "flex";
11138
+ center.style.flexDirection = "column";
11139
+ center.style.flex = "1";
11140
+ center.style.minWidth = "0";
10666
11141
  center.appendChild(alertDivResolution);
10667
11142
  center.appendChild(alertDivFacePosition);
10668
11143
  center.appendChild(alertDivAmbientVerify);
10669
11144
  center.appendChild(alertDivSpyCam);
10670
11145
  divCamera.appendChild(center);
10671
11146
  videoDiv.appendChild(video);
11147
+ videoDiv.style.flexShrink = "0";
10672
11148
  divCamera.appendChild(videoDiv);
10673
11149
  const mask = document.createElement("div");
10674
11150
  mask.setAttribute("class", "facial-biometry__mask");
@@ -10691,6 +11167,7 @@ var DeviceCheckerUI = class {
10691
11167
  videoDiv.appendChild(frame);
10692
11168
  modal.appendChild(divCamera);
10693
11169
  const divMicHeader = document.createElement("div");
11170
+ divMicHeader.setAttribute("class", "mic-header-responsive");
10694
11171
  const h3Mic = document.createElement("h3");
10695
11172
  const selectMic = document.createElement("select");
10696
11173
  selectMic.setAttribute("id", "micSelect");
@@ -10710,18 +11187,24 @@ var DeviceCheckerUI = class {
10710
11187
  };
10711
11188
  this.applyStyles(h3Mic, h3MicStyles);
10712
11189
  selectMic.style.maxWidth = "400px";
11190
+ selectMic.style.minWidth = "150px";
11191
+ selectMic.style.width = "100%";
11192
+ selectMic.style.boxSizing = "border-box";
10713
11193
  divMicHeader.appendChild(h3Mic);
10714
11194
  divMicHeader.appendChild(selectMic);
10715
11195
  modal.appendChild(divMicHeader);
10716
11196
  const divMic = document.createElement("div");
11197
+ divMic.setAttribute("class", "mic-container-responsive");
10717
11198
  const divMicStyles = {
10718
11199
  width: "100%",
10719
- padding: "0px 40px",
10720
- display: "flex"
11200
+ padding: "0px 20px",
11201
+ display: "flex",
11202
+ boxSizing: "border-box"
10721
11203
  // justifyContent: "space-between",
10722
11204
  };
10723
11205
  this.applyStyles(divMic, divMicStyles);
10724
11206
  const divMicMeter = document.createElement("div");
11207
+ divMicMeter.setAttribute("class", "mic-meter-responsive");
10725
11208
  const divMicMeterStyles = {
10726
11209
  // width: "calc(100% - 40px)",
10727
11210
  display: "flex",
@@ -10750,13 +11233,16 @@ var DeviceCheckerUI = class {
10750
11233
  divMeter.appendChild(pill);
10751
11234
  }
10752
11235
  const divRange = document.createElement("div");
11236
+ divRange.setAttribute("class", "range-responsive");
10753
11237
  const pRange0 = document.createElement("p");
10754
11238
  const pRange50 = document.createElement("p");
10755
11239
  const pRange100 = document.createElement("p");
10756
11240
  const divRangeStyles = {
10757
- width: "250px",
11241
+ width: "100%",
11242
+ maxWidth: "250px",
10758
11243
  display: "flex",
10759
- justifyContent: "space-between"
11244
+ justifyContent: "space-between",
11245
+ boxSizing: "border-box"
10760
11246
  };
10761
11247
  this.applyStyles(divRange, divRangeStyles);
10762
11248
  pRange0.innerText = "0";
@@ -10769,10 +11255,10 @@ var DeviceCheckerUI = class {
10769
11255
  alertDivMicrophone.setAttribute("class", "alert-div");
10770
11256
  alertDivMicrophone.setAttribute("id", "alertDivMicrophone");
10771
11257
  const alertDivMicrophoneStyles = {
10772
- // display: "flex",
10773
- alignItems: "start",
10774
- justifyContent: "end",
10775
- width: "inherit"
11258
+ display: "flex",
11259
+ alignItems: "center",
11260
+ justifyContent: "center",
11261
+ width: "100%"
10776
11262
  };
10777
11263
  this.applyStyles(alertDivMicrophone, alertDivMicrophoneStyles);
10778
11264
  const microphoneAlert = document.createElement("error");
@@ -10792,12 +11278,16 @@ var DeviceCheckerUI = class {
10792
11278
  divMicMeter.appendChild(divMeter);
10793
11279
  divMicMeter.appendChild(divRange);
10794
11280
  const pronunceMic = document.createElement("p");
10795
- pronunceMic.innerText = 'Pronuncie em voz alta: "um, dois, tr\xEAs, quatro".';
11281
+ pronunceMic.setAttribute("class", "pronounce-responsive");
11282
+ pronunceMic.innerText = 'Pronuncie em voz alta: "Testando..."';
10796
11283
  pronunceMic.style.textAlign = "start";
10797
11284
  const pronunceMicStyles = {
10798
- width: "100%",
10799
- marginLeft: "40px",
10800
- marginBottom: "15px"
11285
+ width: "calc(100% - 40px)",
11286
+ marginLeft: "20px",
11287
+ marginRight: "20px",
11288
+ marginBottom: "15px",
11289
+ boxSizing: "border-box",
11290
+ padding: "0 10px"
10801
11291
  };
10802
11292
  this.applyStyles(pronunceMic, pronunceMicStyles);
10803
11293
  divMic.appendChild(divMicMeter);
@@ -10810,11 +11300,13 @@ var DeviceCheckerUI = class {
10810
11300
  display: "flex",
10811
11301
  alignItems: "center",
10812
11302
  justifyContent: "center",
10813
- borderTop: "2px solid rgba(0, 0, 0, .1)"
11303
+ borderTop: "2px solid rgba(0, 0, 0, .1)",
11304
+ boxSizing: "border-box"
10814
11305
  };
10815
11306
  this.applyStyles(divBtn, divBtnStyles);
10816
11307
  const buttonCancel = document.createElement("button");
10817
11308
  buttonCancel.setAttribute("id", "cancelBtn");
11309
+ buttonCancel.setAttribute("class", "button-responsive");
10818
11310
  buttonCancel.innerText = "Cancelar";
10819
11311
  const buttonCancelStyles = {
10820
11312
  width: "100%",
@@ -10829,6 +11321,7 @@ var DeviceCheckerUI = class {
10829
11321
  };
10830
11322
  this.applyStyles(buttonCancel, buttonCancelStyles);
10831
11323
  const divider = document.createElement("span");
11324
+ divider.setAttribute("class", "divider-responsive");
10832
11325
  const dividerStyles = {
10833
11326
  width: "3px",
10834
11327
  height: "70px",
@@ -10839,6 +11332,7 @@ var DeviceCheckerUI = class {
10839
11332
  const button = document.createElement("button");
10840
11333
  button.innerText = "Continuar";
10841
11334
  button.setAttribute("id", "confirmBtn");
11335
+ button.setAttribute("class", "button-responsive");
10842
11336
  const buttonStyles = {
10843
11337
  width: "100%",
10844
11338
  height: "70px",
@@ -10950,7 +11444,7 @@ var DeviceCheckerUI = class {
10950
11444
  checkmark_kick_AmbientVerify.setAttribute("class", "checkmark_kick");
10951
11445
  }
10952
11446
  } else {
10953
- if (checkmark_FacePosition && (response.description === "Nenhuma face encontrada" || response.description === "Face na posi\xE7\xE3o errada")) {
11447
+ if (checkmark_FacePosition && (response.type === "position_detection_on_stream" || response.type === "face_detection_on_stream")) {
10954
11448
  facePositionAlert && (facePositionAlert.style.color = "#FF0000");
10955
11449
  checkmark_FacePosition.setAttribute("class", "checkmark_error");
10956
11450
  checkmark_stem_FacePosition.setAttribute("class", "checkmark_stem_error");
@@ -11055,7 +11549,9 @@ Para iniciar um exame utilize uma outra c\xE2mera.`);
11055
11549
  const cameraStream = document.querySelector("#cameraStream");
11056
11550
  if (cameraStream) {
11057
11551
  cameraStream.srcObject = stream;
11058
- cameraStream.play();
11552
+ cameraStream.play().catch((e3) => {
11553
+ console.warn("Erro ao iniciar preview de v\xEDdeo:", e3);
11554
+ });
11059
11555
  }
11060
11556
  }
11061
11557
  audioDeviceInterfaceUIAllowedAmbient(allowedAmbient) {
@@ -11163,6 +11659,7 @@ Para iniciar um exame utilize uma outra c\xE2mera.`);
11163
11659
  // src/new-flow/checkers/DeviceCheckerService.ts
11164
11660
  var _DeviceCheckerService = class _DeviceCheckerService {
11165
11661
  constructor(context) {
11662
+ this.deviceCheckResult = null;
11166
11663
  this.videoOptions = {
11167
11664
  width: 1080,
11168
11665
  height: 720,
@@ -11193,6 +11690,9 @@ var _DeviceCheckerService = class _DeviceCheckerService {
11193
11690
  token: context.token
11194
11691
  });
11195
11692
  }
11693
+ getDeviceCheckResult() {
11694
+ return this.deviceCheckResult;
11695
+ }
11196
11696
  async runCheckDevicesFlow(options, _videoOptions, onModalConfirm, onModalCancel, onUpdate) {
11197
11697
  if (_DeviceCheckerService.isModalOpen) {
11198
11698
  return Promise.reject("Modal j\xE1 est\xE1 aberto");
@@ -11241,6 +11741,7 @@ var _DeviceCheckerService = class _DeviceCheckerService {
11241
11741
  });
11242
11742
  }
11243
11743
  );
11744
+ this.deviceCheckResult = { ...returnData, result: resultPromise };
11244
11745
  return { ...returnData, result: resultPromise };
11245
11746
  } catch (error) {
11246
11747
  this.closeCheckDevices();
@@ -11286,7 +11787,7 @@ var _DeviceCheckerService = class _DeviceCheckerService {
11286
11787
  await this.checkSpyScan();
11287
11788
  const { cameraId, microphoneId, result } = await this.DeviceCheckerUI.modalActions(() => this.closeCheckDevices());
11288
11789
  return new Promise((resolve) => {
11289
- resolve({
11790
+ const response = {
11290
11791
  result,
11291
11792
  cameraId,
11292
11793
  microphoneId,
@@ -11297,7 +11798,9 @@ var _DeviceCheckerService = class _DeviceCheckerService {
11297
11798
  allowedMicrophone: this.allowedMicrophone,
11298
11799
  allowedSpyScan: this.allowedSpyScan,
11299
11800
  faceDetectionAlerts: this.faceDetectionAlerts
11300
- });
11801
+ };
11802
+ this.deviceCheckResult = response;
11803
+ resolve(response);
11301
11804
  });
11302
11805
  } catch (error) {
11303
11806
  this.closeCheckDevices();
@@ -11306,8 +11809,16 @@ var _DeviceCheckerService = class _DeviceCheckerService {
11306
11809
  }
11307
11810
  }
11308
11811
  isUnderResolution() {
11812
+ var _a2;
11309
11813
  const settings = this.cameraRecorder.cameraStream.getVideoTracks()[0].getSettings();
11310
- if (this.videoOptions.minWidth > settings.width || this.videoOptions.minHeight > settings.height) {
11814
+ let { width = 0, height = 0 } = settings;
11815
+ const isPortrait = (_a2 = screen.orientation) == null ? void 0 : _a2.type.includes("portrait");
11816
+ if (isPortrait && isMobileDevice()) {
11817
+ if (this.videoOptions.width == height && this.videoOptions.height == width) {
11818
+ [width, height] = [height, width];
11819
+ }
11820
+ }
11821
+ if (this.videoOptions.minWidth > width || this.videoOptions.minHeight > height) {
11311
11822
  this.allowedResolution = false;
11312
11823
  } else {
11313
11824
  this.allowedResolution = true;
@@ -11826,9 +12337,11 @@ var Extension = class {
11826
12337
 
11827
12338
  // src/modules/onChangeDevices.ts
11828
12339
  var onChangeDevices = class {
11829
- constructor(repositoryDevices, proctoringId2) {
12340
+ constructor(repositoryDevices, proctoringId2, sessionOptions, allRecorders) {
11830
12341
  this.repositoryDevices = repositoryDevices;
11831
12342
  this.proctoringId = proctoringId2;
12343
+ this.sessionOptions = sessionOptions;
12344
+ this.allRecorders = allRecorders;
11832
12345
  }
11833
12346
  startRecording(options) {
11834
12347
  navigator.mediaDevices.ondevicechange = () => {
@@ -11842,24 +12355,49 @@ var onChangeDevices = class {
11842
12355
  const response = await this.repositoryDevices.getDevices("devices");
11843
12356
  const defaultDevice = { label: "", id: "" };
11844
12357
  const copy = { cameras: (response == null ? void 0 : response.cameras) || [defaultDevice], microphones: (response == null ? void 0 : response.microphones) || [defaultDevice] };
11845
- let resultCameras;
11846
- let resultMicrophones;
12358
+ const status = devices.cameras.length > (copy == null ? void 0 : copy.cameras.length) || devices.microphones.length > (copy == null ? void 0 : copy.microphones.length) ? "in" : "out";
12359
+ let resultCameras = [];
12360
+ let resultMicrophones = [];
12361
+ let isActiveDeviceRemoved = false;
11847
12362
  if (devices.cameras.length != (copy == null ? void 0 : copy.cameras.length)) {
11848
- resultCameras = devices.cameras.length > (copy == null ? void 0 : copy.cameras.length) ? onlyInLeft(devices.cameras, copy == null ? void 0 : copy.cameras, isSameDevice) : onlyInLeft(copy == null ? void 0 : copy.cameras, devices.cameras, isSameDevice);
12363
+ const removedCameras = status === "out" ? onlyInLeft(copy == null ? void 0 : copy.cameras, devices.cameras, isSameDevice) : [];
12364
+ const addedCameras = status === "in" ? onlyInLeft(devices.cameras, copy == null ? void 0 : copy.cameras, isSameDevice) : [];
12365
+ resultCameras = status === "out" ? removedCameras : addedCameras;
11849
12366
  resultCameras = resultCameras.filter((item) => item.id != "default");
12367
+ console.log(removedCameras);
12368
+ console.log(this.sessionOptions.cameraId);
12369
+ if (status === "out") {
12370
+ isActiveDeviceRemoved = removedCameras.some(
12371
+ (device) => device.id === this.sessionOptions.cameraId
12372
+ );
12373
+ }
11850
12374
  }
11851
12375
  if (devices.microphones.length != (copy == null ? void 0 : copy.microphones.length)) {
11852
- resultMicrophones = devices.microphones.length > (copy == null ? void 0 : copy.microphones.length) ? onlyInLeft(devices.microphones, copy == null ? void 0 : copy.microphones, isSameDevice) : onlyInLeft(copy == null ? void 0 : copy.microphones, devices.microphones, isSameDevice);
12376
+ const removedMicrophones = status === "out" ? onlyInLeft(copy == null ? void 0 : copy.microphones, devices.microphones, isSameDevice) : [];
12377
+ const addedMicrophones = status === "in" ? onlyInLeft(devices.microphones, copy == null ? void 0 : copy.microphones, isSameDevice) : [];
12378
+ resultMicrophones = status === "out" ? removedMicrophones : addedMicrophones;
11853
12379
  resultMicrophones = resultMicrophones.filter((item) => item.id != "default" && item.id != "communications");
12380
+ if (status === "out" && !isActiveDeviceRemoved) {
12381
+ isActiveDeviceRemoved = removedMicrophones.some(
12382
+ (device) => device.id === this.sessionOptions.microphoneId
12383
+ );
12384
+ }
11854
12385
  }
11855
12386
  const devicesChanged = {
11856
12387
  cameras: resultCameras || [],
11857
12388
  microphones: resultMicrophones || [],
11858
- status: devices.cameras.length > (copy == null ? void 0 : copy.cameras.length) || devices.microphones.length > (copy == null ? void 0 : copy.microphones.length) ? "in" : "out"
12389
+ status,
12390
+ isActiveDevice: isActiveDeviceRemoved
11859
12391
  };
11860
- await this.repositoryDevices.save({ ...devices, id: "devices", status: devices.cameras.length > (copy == null ? void 0 : copy.cameras.length) || devices.microphones.length > (copy == null ? void 0 : copy.microphones.length) ? "in" : "out" });
12392
+ await this.repositoryDevices.save({ ...devices, id: "devices", status });
11861
12393
  if (options.status && (devicesChanged.cameras.length != 0 || devicesChanged.microphones.length != 0)) {
11862
12394
  trackers.registerChangeDevice(this.proctoringId, devicesChanged.status, JSON.stringify(devicesChanged, null, 2));
12395
+ if (devicesChanged.isActiveDevice) {
12396
+ this.allRecorders.alertRecorder.addAlert({
12397
+ alert: 39 /* ChangeDevices */,
12398
+ type: 2 /* Video */
12399
+ });
12400
+ }
11863
12401
  options.status(devicesChanged);
11864
12402
  }
11865
12403
  }
@@ -12067,6 +12605,14 @@ var AlertRecorder = class {
12067
12605
  lastAlert.end = Date.now() - this.startTime.getTime();
12068
12606
  }
12069
12607
  }
12608
+ addAlert({ alert, type }) {
12609
+ this.alerts.push({
12610
+ begin: Date.now() - this.startTime.getTime(),
12611
+ end: Date.now() - this.startTime.getTime(),
12612
+ alert,
12613
+ type
12614
+ });
12615
+ }
12070
12616
  };
12071
12617
 
12072
12618
  // src/new-flow/recorders/AudioRecorder.ts
@@ -14555,19 +15101,38 @@ var ScreenRecorder = class {
14555
15101
  }
14556
15102
  async startRecording() {
14557
15103
  this.startTime = new Date(Date.now());
15104
+ if (isMobileDevice()) return;
14558
15105
  const { allowOnlyFirstMonitor, allowMultipleMonitors, onStopSharingScreenCallback, onBufferSizeErrorCallback } = this.options;
14559
- const displayMediaStreamConstraints = {
15106
+ const complexConstraints = {
14560
15107
  video: {
14561
15108
  cursor: "always",
14562
- width: 854,
14563
- height: 480,
15109
+ width: this.options.screenRecorderOptions.width,
15110
+ height: this.options.screenRecorderOptions.height,
14564
15111
  displaySurface: "monitor"
14565
15112
  },
14566
15113
  audio: false
14567
15114
  };
14568
- this.screenStream = await navigator.mediaDevices.getDisplayMedia(
14569
- displayMediaStreamConstraints
14570
- );
15115
+ const simpleConstraints = {
15116
+ video: {
15117
+ cursor: "always",
15118
+ width: 1280,
15119
+ height: 720,
15120
+ displaySurface: "monitor"
15121
+ },
15122
+ audio: false
15123
+ };
15124
+ let stream;
15125
+ try {
15126
+ stream = await navigator.mediaDevices.getDisplayMedia(complexConstraints);
15127
+ } catch (e3) {
15128
+ if (e3 instanceof TypeError && e3.message.includes("min constraints are not supported")) {
15129
+ console.warn("Navegador n\xE3o suporta restri\xE7\xF5es 'min/max/ideal'. Tentando com 720p fixo.");
15130
+ stream = await navigator.mediaDevices.getDisplayMedia(simpleConstraints);
15131
+ } else {
15132
+ throw e3;
15133
+ }
15134
+ }
15135
+ this.screenStream = stream;
14571
15136
  const tracks = this.screenStream.getVideoTracks();
14572
15137
  tracks[0].onended = onStopSharingScreenCallback;
14573
15138
  const isFirefox = navigator.userAgent.indexOf("Firefox") > -1;
@@ -14706,26 +15271,6 @@ var IndexDbSessionRepository = class {
14706
15271
  }
14707
15272
  };
14708
15273
 
14709
- // src/utils/browserInformations.ts
14710
- function fnBrowserDetect() {
14711
- const userAgent = navigator.userAgent;
14712
- let browserName;
14713
- if (userAgent.match(/chrome|chromium|crios/i)) {
14714
- browserName = "chrome";
14715
- } else if (userAgent.match(/firefox|fxios/i)) {
14716
- browserName = "firefox";
14717
- } else if (userAgent.match(/safari/i)) {
14718
- browserName = "safari";
14719
- } else if (userAgent.match(/opr\//i)) {
14720
- browserName = "opera";
14721
- } else if (userAgent.match(/edg/i)) {
14722
- browserName = "edge";
14723
- } else {
14724
- browserName = "No browser detection";
14725
- }
14726
- return browserName;
14727
- }
14728
-
14729
15274
  // src/utils/geolocation.ts
14730
15275
  function getGeolocation() {
14731
15276
  return new Promise((resolve, reject) => {
@@ -17727,15 +18272,12 @@ var _ExternalCameraChecker = class _ExternalCameraChecker {
17727
18272
  }
17728
18273
  }
17729
18274
  async goToPositionGuide() {
17730
- if (this.connection) {
17731
- const actionMessage = new ActionMessage();
17732
- actionMessage.command = "Position_Guide";
17733
- console.log("Enviando comando 'Position_Guide' para o aplicativo...");
17734
- this.connection.invoke(
17735
- "SendAction",
17736
- this.externalSessionId,
17737
- actionMessage
17738
- );
18275
+ try {
18276
+ const response = await this.backend.goToExternalCameraPositionStep("" + this.externalSessionId);
18277
+ console.log(response);
18278
+ } catch (error) {
18279
+ console.error("Erro ao enviar comando de Position:", error);
18280
+ throw new Error("N\xE3o foi poss\xEDvel enviar comando de Position Guide.");
17739
18281
  }
17740
18282
  }
17741
18283
  async reset() {
@@ -18414,13 +18956,9 @@ var ExternalCameraChecker = _ExternalCameraChecker;
18414
18956
 
18415
18957
  // src/proctoring/proctoring.ts
18416
18958
  var Proctoring = class {
18417
- // private onProgress = (percentage: number) => {};
18418
- // public setOnProgress(cb: (percentage: number) => void) {
18419
- // console.log("proctoring.setOnProgress");
18420
- // this.onProgress = (percentage) => cb(percentage);
18421
- // }
18422
18959
  constructor(context) {
18423
18960
  this.context = context;
18961
+ this.deviceData = null;
18424
18962
  this.paramsConfig = {
18425
18963
  audioBehaviourParameters: {
18426
18964
  recordingBitrate: 128,
@@ -18473,11 +19011,17 @@ var Proctoring = class {
18473
19011
  this.appChecker = new ExternalCameraChecker(this.context, (response) => this.onRealtimeAlertsCallback(response));
18474
19012
  }
18475
19013
  setOnStopSharingScreenCallback(cb) {
18476
- this.onStopSharingScreenCallback = () => {
19014
+ this.onStopSharingScreenCallback = async () => {
19015
+ var _a2, _b, _c2, _d;
18477
19016
  trackers.registerStopSharingScreen(
18478
19017
  this.proctoringId,
18479
19018
  "Stop sharing screen"
18480
19019
  );
19020
+ (_b = (_a2 = this.allRecorders) == null ? void 0 : _a2.alertRecorder) == null ? void 0 : _b.addAlert({
19021
+ alert: 34 /* StopSharingScreen */,
19022
+ type: 3 /* Screen */
19023
+ });
19024
+ (_d = (_c2 = this.allRecorders) == null ? void 0 : _c2.screenRecorder) == null ? void 0 : _d.stopRecording();
18481
19025
  cb();
18482
19026
  };
18483
19027
  }
@@ -18490,17 +19034,71 @@ var Proctoring = class {
18490
19034
  async onChangeDevices(options = {}) {
18491
19035
  const onChange = new onChangeDevices(
18492
19036
  this.repositoryDevices,
18493
- this.proctoringId
19037
+ this.proctoringId,
19038
+ this.sessionOptions,
19039
+ this.allRecorders
18494
19040
  );
18495
19041
  onChange.startRecording(options);
18496
19042
  this.onChangeDevicesCallback = (devices) => options.status && options.status(devices);
18497
19043
  }
19044
+ convertRealtimeCategoryToAlertCategory(category) {
19045
+ switch (category) {
19046
+ case "no_face_detected":
19047
+ return 1 /* NoFace */;
19048
+ case "multiple_faces_detected":
19049
+ return 2 /* MultipleFaces */;
19050
+ case "multiple_persons_detected":
19051
+ return 28 /* EnvironmentMultiplePeople */;
19052
+ case "no_person_detected":
19053
+ return 29 /* EnvironmentNoPeople */;
19054
+ default:
19055
+ return null;
19056
+ }
19057
+ }
19058
+ convertRealtimeTypeToWarningType(category) {
19059
+ switch (category) {
19060
+ case "face_detection_on_stream":
19061
+ return 0 /* Face */;
19062
+ case "person_detection_on_stream":
19063
+ return 1 /* People */;
19064
+ default:
19065
+ return null;
19066
+ }
19067
+ }
18498
19068
  async onRealtimeAlerts(options = {}) {
18499
- this.onRealtimeAlertsCallback = (response) => options.data && options.data(response);
19069
+ this.onRealtimeAlertsCallback = async (response) => {
19070
+ options.data && options.data(response);
19071
+ if (this.sessionOptions.proctoringType === "REALTIME" && (response.type === "face_detection_on_stream" || response.type === "person_detection_on_stream")) {
19072
+ if (response.status === "ALERT") {
19073
+ await this.backend.startRealtimeAlert({
19074
+ proctoringId: this.proctoringId,
19075
+ begin: response.begin,
19076
+ end: response.end,
19077
+ alert: this.convertRealtimeCategoryToAlertCategory(response.category)
19078
+ });
19079
+ } else if (response.status === "OK") {
19080
+ await this.backend.stopRealtimeAlert({
19081
+ proctoringId: this.proctoringId,
19082
+ begin: response.begin,
19083
+ end: response.end,
19084
+ warningCategoryEnum: this.convertRealtimeTypeToWarningType(response.type),
19085
+ alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64()
19086
+ });
19087
+ }
19088
+ }
19089
+ };
18500
19090
  }
18501
19091
  setOnBufferSizeErrorCallback(cb) {
18502
19092
  this.onBufferSizeErrorCallback = (cameraStream) => cb(cameraStream);
18503
19093
  }
19094
+ // private onProgress = (percentage: number) => {};
19095
+ // public setOnProgress(cb: (percentage: number) => void) {
19096
+ // console.log("proctoring.setOnProgress");
19097
+ // this.onProgress = (percentage) => cb(percentage);
19098
+ // }
19099
+ setDeviceCheckData(data) {
19100
+ this.deviceData = data;
19101
+ }
18504
19102
  createRecorders(options = getDefaultProctoringOptions) {
18505
19103
  var _a2, _b;
18506
19104
  this.onChangeDevices();
@@ -18527,6 +19125,7 @@ var Proctoring = class {
18527
19125
  const screenRecorder = this.sessionOptions.captureScreen ? new ScreenRecorder({
18528
19126
  allowOnlyFirstMonitor: (_a2 = this.sessionOptions.allowOnlyFirstMonitor) != null ? _a2 : true,
18529
19127
  allowMultipleMonitors: (_b = this.sessionOptions.allowMultipleMonitors) != null ? _b : true,
19128
+ screenRecorderOptions: this.sessionOptions.screenRecorderOptions,
18530
19129
  onStopSharingScreenCallback: () => this.onStopSharingScreenCallback(),
18531
19130
  onBufferSizeError: this.sessionOptions.onBufferSizeError,
18532
19131
  onBufferSizeErrorCallback: () => this.onBufferSizeErrorCallback()
@@ -18599,11 +19198,16 @@ var Proctoring = class {
18599
19198
  examId: this.context.examId,
18600
19199
  token: this.context.token
18601
19200
  },
18602
- this.sessionOptions.proctoringType,
19201
+ this.sessionOptions,
18603
19202
  this.geolocation ? this.geolocation.coords.latitude : void 0,
18604
19203
  this.geolocation ? this.geolocation.coords.longitude : void 0
18605
19204
  );
18606
19205
  this.proctoringId = startResponse.id;
19206
+ trackers.registerDevicesChecked(
19207
+ this.proctoringId,
19208
+ !!this.deviceData,
19209
+ `Devices checked: ${JSON.stringify(this.deviceData)} | Devices List: ${JSON.stringify(devices)}`
19210
+ );
18607
19211
  try {
18608
19212
  if (options == null ? void 0 : options.useExternalCamera) {
18609
19213
  await this.appChecker.startTransmission(this.proctoringId);
@@ -18637,6 +19241,11 @@ Navigator: ${JSON.stringify(_navigator2)}`
18637
19241
  startResponse.screenStream = this.allRecorders.screenRecorder.screenStream;
18638
19242
  }
18639
19243
  this.state = "Recording" /* Recording */;
19244
+ setTimeout(async () => {
19245
+ if (this.sessionOptions.proctoringType === "REALTIME") {
19246
+ await this.backend.verifyFace(this.proctoringId, await this.allRecorders.cameraRecorder.getCurrentImageBase64());
19247
+ }
19248
+ }, 1e3);
18640
19249
  return startResponse;
18641
19250
  } catch (error) {
18642
19251
  console.log(error);
@@ -18755,8 +19364,11 @@ Upload Services: ${uploaderServices}`,
18755
19364
  );
18756
19365
  });
18757
19366
  }
18758
- await this.backend.finishAndSendUrls(this.context, this.proctoringSession).then(() => {
19367
+ await this.backend.finishAndSendUrls(this.context).then((finishResponse) => {
19368
+ var _a2, _b;
18759
19369
  trackers.registerFinish(this.proctoringSession.id, true, "");
19370
+ console.log("finishResponse: ", finishResponse);
19371
+ options.onFinish && options.onFinish((_a2 = finishResponse == null ? void 0 : finishResponse.score) != null ? _a2 : 100, (_b = finishResponse == null ? void 0 : finishResponse.approved) != null ? _b : true);
18760
19372
  }).catch((error) => {
18761
19373
  trackers.registerFinish(
18762
19374
  this.proctoringSession.id,
@@ -18797,7 +19409,7 @@ Error: ` + error
18797
19409
  }
18798
19410
  async verifyBrowser() {
18799
19411
  const browserName = await fnBrowserDetect();
18800
- if (browserName !== "chrome" && browserName !== "firefox") {
19412
+ if (browserName !== "chrome" && browserName !== "firefox" && !isMobileDevice()) {
18801
19413
  trackers.registerBrowserNotSupported(
18802
19414
  this.proctoringId,
18803
19415
  `Browser n\xE3o suportado:
@@ -19103,7 +19715,14 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
19103
19715
  const signTerm = new SignTerm(parameters);
19104
19716
  const photo = new CapturePhoto();
19105
19717
  const login = proctoring.login.bind(proctoring);
19106
- const start = proctoring.start.bind(proctoring);
19718
+ const originalStart = proctoring.start.bind(proctoring);
19719
+ const start = async (parameters2) => {
19720
+ const deviceResult = checker.getDeviceCheckResult();
19721
+ if (deviceResult) {
19722
+ proctoring.setDeviceCheckData(deviceResult);
19723
+ }
19724
+ return originalStart(parameters2);
19725
+ };
19107
19726
  const finish = proctoring.finish.bind(proctoring);
19108
19727
  const pause = proctoring.pause.bind(proctoring);
19109
19728
  const resume = proctoring.resume.bind(proctoring);