easyproctor-hml 2.7.14 → 2.8.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/index.js CHANGED
@@ -26625,6 +26625,9 @@ var FaceDetection = class extends BaseDetection {
26625
26625
  if (this.emmitedFaceAlert) {
26626
26626
  this.handleOk("face_stop", "face_detection_on_stream");
26627
26627
  }
26628
+ this.numFacesSent = -1;
26629
+ this.emmitedPositionAlert = false;
26630
+ this.emmitedFaceAlert = false;
26628
26631
  }
26629
26632
  // displayVideoDetections(result: { detections: any; }) {
26630
26633
  // // console.log(result);
@@ -30796,7 +30799,8 @@ var getDefaultProctoringOptions = {
30796
30799
  useSpyScan: false,
30797
30800
  useExternalCamera: false,
30798
30801
  useChallenge: false,
30799
- screenRecorderOptions: { width: 1280, height: 720 }
30802
+ screenRecorderOptions: { width: 1280, height: 720 },
30803
+ auto: false
30800
30804
  };
30801
30805
 
30802
30806
  // src/proctoring/options/ProctoringVideoOptions.ts
@@ -30864,7 +30868,7 @@ var proctoringId;
30864
30868
  function setRecorderProctoringId(id) {
30865
30869
  proctoringId = id;
30866
30870
  }
30867
- function recorder(stream4, buffer, onBufferSizeError = false, onBufferSizeErrorCallback, audio = false, recorderOpts) {
30871
+ function recorder(stream4, buffer, onBufferSizeError = false, onBufferSizeErrorCallback, audio = false) {
30868
30872
  let resolvePromise;
30869
30873
  let onBufferSizeInterval;
30870
30874
  let lastEvent;
@@ -30872,7 +30876,6 @@ function recorder(stream4, buffer, onBufferSizeError = false, onBufferSizeErrorC
30872
30876
  bufferSize = 0;
30873
30877
  let startTime;
30874
30878
  let duration = 0;
30875
- let chunkIndex = 0;
30876
30879
  let recorderOptions = {
30877
30880
  // eslint-disable-next-line no-useless-escape
30878
30881
  mimeType: "video/webm",
@@ -30907,10 +30910,6 @@ function recorder(stream4, buffer, onBufferSizeError = false, onBufferSizeErrorC
30907
30910
  mediaRecorder2.ondataavailable = (e3) => {
30908
30911
  bufferSize = bufferSize + e3.data.size;
30909
30912
  if (e3.data.size > 0) {
30910
- if (recorderOpts == null ? void 0 : recorderOpts.onChunkAvailable) {
30911
- recorderOpts.onChunkAvailable(e3.data, chunkIndex);
30912
- chunkIndex++;
30913
- }
30914
30913
  buffer.push(e3.data);
30915
30914
  }
30916
30915
  };
@@ -30942,13 +30941,7 @@ function recorder(stream4, buffer, onBufferSizeError = false, onBufferSizeErrorC
30942
30941
  };
30943
30942
  try {
30944
30943
  console.log("State antes do start:", recorder2.state);
30945
- chunkIndex = 0;
30946
- if ((recorderOpts == null ? void 0 : recorderOpts.timeslice) && (recorderOpts == null ? void 0 : recorderOpts.timeslice) > 0) {
30947
- recorder2.start(recorderOpts.timeslice);
30948
- } else {
30949
- recorder2.start(1e4);
30950
- }
30951
- bufferSize = 0;
30944
+ recorder2.start(1e4);
30952
30945
  startTime = new Date(Date.now());
30953
30946
  } catch (e3) {
30954
30947
  console.error("Recorder erro ao chamar start event:", e3);
@@ -31002,6 +30995,9 @@ function recorder(stream4, buffer, onBufferSizeError = false, onBufferSizeErrorC
31002
30995
  console.log("stopRecording Recorder n\xE3o est\xE1 em estado recording");
31003
30996
  resolve();
31004
30997
  }
30998
+ stream4.getTracks().forEach((el) => {
30999
+ el.stop();
31000
+ });
31005
31001
  });
31006
31002
  }
31007
31003
  function pauseRecording() {
@@ -31359,55 +31355,8 @@ var ObjectDetection = class extends BaseDetection {
31359
31355
  }
31360
31356
  };
31361
31357
 
31362
- // src/new-flow/recorders/VolumeMeter.ts
31363
- var VolumeMeter = class {
31364
- constructor(stream4) {
31365
- this.volume = null;
31366
- this.animationFrameId = null;
31367
- this.stream = stream4;
31368
- }
31369
- async start(options = {}) {
31370
- return new Promise((resolve, reject) => {
31371
- try {
31372
- this.audioContext = new AudioContext();
31373
- this.analyser = this.audioContext.createAnalyser();
31374
- this.microphone = this.audioContext.createMediaStreamSource(this.stream);
31375
- this.analyser.smoothingTimeConstant = 0.8;
31376
- this.analyser.fftSize = 1024;
31377
- this.microphone.connect(this.analyser);
31378
- const processAudio = () => {
31379
- const array = new Uint8Array(this.analyser.frequencyBinCount);
31380
- this.analyser.getByteFrequencyData(array);
31381
- const arraySum = array.reduce((a3, value) => a3 + value, 0);
31382
- const average = arraySum / array.length;
31383
- this.setVolume(average);
31384
- options.setVolume && options.setVolume(average);
31385
- this.animationFrameId = requestAnimationFrame(processAudio);
31386
- };
31387
- this.animationFrameId = requestAnimationFrame(processAudio);
31388
- resolve(true);
31389
- } catch (error) {
31390
- this.stop();
31391
- reject(`Error: ${error}`);
31392
- }
31393
- });
31394
- }
31395
- stop() {
31396
- var _a2, _b, _c2;
31397
- if (this.animationFrameId !== null) {
31398
- cancelAnimationFrame(this.animationFrameId);
31399
- }
31400
- (_a2 = this.audioContext) == null ? void 0 : _a2.close();
31401
- (_b = this.microphone) == null ? void 0 : _b.disconnect();
31402
- (_c2 = this.analyser) == null ? void 0 : _c2.disconnect();
31403
- }
31404
- getVolume() {
31405
- return this.volume;
31406
- }
31407
- setVolume(value) {
31408
- this.volume = value;
31409
- }
31410
- };
31358
+ // src/new-flow/recorders/CameraRecorder.ts
31359
+ var import_jszip_min = __toESM(require_jszip_min());
31411
31360
 
31412
31361
  // src/new-flow/chunk/ChunkStorageService.ts
31413
31362
  var _ChunkStorageService = class _ChunkStorageService {
@@ -31425,13 +31374,7 @@ var _ChunkStorageService = class _ChunkStorageService {
31425
31374
  _ChunkStorageService.DB_VERSION
31426
31375
  );
31427
31376
  request.onerror = () => {
31428
- var _a2, _b;
31429
- console.error("IndexedDB error:", request.error);
31430
- reject(
31431
- new Error(
31432
- `N\xE3o foi poss\xEDvel conectar ao IndexedDB para chunks: ${(_a2 = request.error) == null ? void 0 : _a2.name} - ${(_b = request.error) == null ? void 0 : _b.message}`
31433
- )
31434
- );
31377
+ reject(new Error("N\xE3o foi poss\xEDvel conectar ao IndexedDB para chunks."));
31435
31378
  };
31436
31379
  request.onupgradeneeded = () => {
31437
31380
  const db = request.result;
@@ -31654,8 +31597,8 @@ var _ChunkStorageService = class _ChunkStorageService {
31654
31597
  }
31655
31598
  };
31656
31599
  _ChunkStorageService.DB_NAME = "EasyProctorChunksDb";
31657
- /** Incrementado para v2 para recriar índices com tipos numéricos em vez de boolean */
31658
- _ChunkStorageService.DB_VERSION = 2;
31600
+ /** v2: índices uploaded numéricos; v3: campo arrayBuffer em vez de blob */
31601
+ _ChunkStorageService.DB_VERSION = 3;
31659
31602
  _ChunkStorageService.STORE_NAME = "chunks";
31660
31603
  var ChunkStorageService = _ChunkStorageService;
31661
31604
 
@@ -31838,8 +31781,9 @@ var BackgroundUploadService = class _BackgroundUploadService {
31838
31781
  let virtualStart = this.totalBytesPurged;
31839
31782
  const chunksWithMeta = allChunks.map((c3) => {
31840
31783
  const start = virtualStart;
31841
- const end = start + c3.blob.size - 1;
31842
- virtualStart += c3.blob.size;
31784
+ const byteLength = c3.arrayBuffer.byteLength;
31785
+ const end = start + byteLength - 1;
31786
+ virtualStart += byteLength;
31843
31787
  return { chunk: c3, start, end };
31844
31788
  });
31845
31789
  let combinedBlobParts = [];
@@ -31849,8 +31793,8 @@ var BackgroundUploadService = class _BackgroundUploadService {
31849
31793
  for (const meta of chunksWithMeta) {
31850
31794
  if (this.currentOffset > meta.end) continue;
31851
31795
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
31852
- const chunkSlice = meta.chunk.blob.slice(sliceStart);
31853
- combinedBlobParts.push(chunkSlice);
31796
+ const sliceBuf = meta.chunk.arrayBuffer.slice(sliceStart);
31797
+ combinedBlobParts.push(new Blob([sliceBuf]));
31854
31798
  lastProcessedChunkId = meta.chunk.id;
31855
31799
  finalChunkIndex = meta.chunk.chunkIndex;
31856
31800
  }
@@ -31884,7 +31828,10 @@ var BackgroundUploadService = class _BackgroundUploadService {
31884
31828
  }
31885
31829
  if (this.config.cleanAfterUpload) {
31886
31830
  const chunksToClear = chunksWithMeta.filter((meta) => meta.chunk.uploaded === 1 || meta.chunk.uploaded === 0 && meta.end < this.currentOffset);
31887
- const sizePurged = chunksToClear.reduce((acc, meta) => acc + meta.chunk.blob.size, 0);
31831
+ const sizePurged = chunksToClear.reduce(
31832
+ (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
31833
+ 0
31834
+ );
31888
31835
  await this.chunkStorage.clearUploadedChunks(this.proctoringId);
31889
31836
  if (sizePurged > 0) {
31890
31837
  this.totalBytesPurged += sizePurged;
@@ -32022,7 +31969,6 @@ var BackgroundUploadService = class _BackgroundUploadService {
32022
31969
  };
32023
31970
 
32024
31971
  // src/new-flow/recorders/CameraRecorder.ts
32025
- var import_jszip_min = __toESM(require_jszip_min());
32026
31972
  var pkg = require_fix_webm_duration();
32027
31973
  var fixWebmDuration = pkg.default || pkg;
32028
31974
  var _CameraRecorder = class _CameraRecorder {
@@ -32088,7 +32034,6 @@ var _CameraRecorder = class _CameraRecorder {
32088
32034
  this.currentRetries = 0;
32089
32035
  this.packageCount = 0;
32090
32036
  this.failedUploads = 0;
32091
- this.noiseWait = 20;
32092
32037
  this.options = options;
32093
32038
  this.videoOptions = videoOptions;
32094
32039
  this.backend = backend;
@@ -32096,11 +32041,11 @@ var _CameraRecorder = class _CameraRecorder {
32096
32041
  paramsConfig && (this.paramsConfig = paramsConfig);
32097
32042
  }
32098
32043
  /**
32099
- * Determina se o fluxo de chunks e lifecycle deve estar ativo.
32100
- * Retorna true se:
32101
- * 1. O proctoringId já foi definido (ou seja, estamos em uma sessão real, NÃO no checkDevices)
32102
- * 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
32103
- */
32044
+ * Determina se o fluxo de chunks e lifecycle deve estar ativo.
32045
+ * Retorna true se:
32046
+ * 1. O proctoringId já foi definido (ou seja, estamos em uma sessão real, NÃO no checkDevices)
32047
+ * 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
32048
+ */
32104
32049
  get isChunkEnabled() {
32105
32050
  return !!this.proctoringId && this.options.proctoringType === "REALTIME" && !isSafeBrowser();
32106
32051
  }
@@ -32363,15 +32308,9 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32363
32308
  await new Promise((r3) => setTimeout(r3, 300));
32364
32309
  }
32365
32310
  async startRecording() {
32366
- var _a2, _b, _c2, _d, _e3, _f, _g, _h;
32311
+ var _a2, _b, _c2, _d, _e3, _f, _g;
32367
32312
  await this.startStream();
32368
32313
  await this.attachAndWarmup(this.cameraStream);
32369
- const recorderOpts = this.isChunkEnabled ? {
32370
- timeslice: _CameraRecorder.CHUNK_TIMESLICE_MS,
32371
- onChunkAvailable: (blob, idx) => {
32372
- this.handleNewChunk(blob, idx);
32373
- }
32374
- } : {};
32375
32314
  const {
32376
32315
  startRecording,
32377
32316
  stopRecording,
@@ -32387,8 +32326,7 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32387
32326
  this.blobs,
32388
32327
  this.options.onBufferSizeError,
32389
32328
  (e3) => this.bufferError(e3),
32390
- false,
32391
- recorderOpts
32329
+ false
32392
32330
  );
32393
32331
  this.recordingStart = startRecording;
32394
32332
  this.recordingStop = stopRecording;
@@ -32398,18 +32336,13 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32398
32336
  this.getBufferSize = getBufferSize;
32399
32337
  this.getStartTime = getStartTime;
32400
32338
  this.getDuration = getDuration;
32401
- this.chunkIndex = 0;
32402
- if (this.isChunkEnabled) {
32403
- (_a2 = this.backgroundUpload) == null ? void 0 : _a2.start();
32404
- this.setupLifecycleListeners();
32405
- }
32406
32339
  try {
32407
32340
  await new Promise((r3) => setTimeout(r3, 500));
32408
32341
  await this.recordingStart();
32409
32342
  } catch (error) {
32410
32343
  console.log("Camera Recorder error", error);
32411
32344
  this.stopRecording();
32412
- const maxRetries = ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.maxRetries) || 3;
32345
+ const maxRetries = ((_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.maxRetries) || 3;
32413
32346
  if (this.currentRetries < maxRetries) {
32414
32347
  console.log("Camera Recorder retry", this.currentRetries);
32415
32348
  this.currentRetries++;
@@ -32419,13 +32352,13 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32419
32352
  }
32420
32353
  }
32421
32354
  this.stopped = false;
32422
- if (((_c2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _c2.detectPerson) || ((_d = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _d.detectCellPhone) || ((_e3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _e3.detectFace)) {
32355
+ if (((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.detectPerson) || ((_c2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _c2.detectCellPhone) || ((_d = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _d.detectFace)) {
32423
32356
  await this.initializeDetectors();
32424
32357
  }
32425
- if ((_f = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _f.detectFace) {
32358
+ if ((_e3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _e3.detectFace) {
32426
32359
  await this.faceDetection.enableCam(this.cameraStream);
32427
32360
  }
32428
- if (((_g = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _g.detectPerson) || ((_h = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _h.detectCellPhone)) {
32361
+ if (((_f = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _f.detectPerson) || ((_g = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _g.detectCellPhone)) {
32429
32362
  await this.objectDetection.enableCam(this.cameraStream);
32430
32363
  }
32431
32364
  this.filesToUpload = [];
@@ -32510,10 +32443,11 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32510
32443
  const savePromise = (async () => {
32511
32444
  var _a2;
32512
32445
  try {
32446
+ const arrayBuffer = await blob.arrayBuffer();
32513
32447
  await this.chunkStorage.saveChunk({
32514
32448
  proctoringId: this.proctoringId,
32515
32449
  chunkIndex: this.chunkIndex,
32516
- blob,
32450
+ arrayBuffer,
32517
32451
  timestamp: Date.now(),
32518
32452
  uploaded: 0,
32519
32453
  mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"
@@ -32663,40 +32597,41 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32663
32597
  if (this.blobs != null)
32664
32598
  trackers.registerSaveOnSession(
32665
32599
  this.proctoringId,
32666
- `Blobs Length: ${this.blobs.length} Buffer Size: ${this.getBufferSize()} ChunkEnabled: ${this.isChunkEnabled}`
32600
+ `Blobs Length: ${this.blobs.length} Buffer Size: ${this.getBufferSize()} `
32667
32601
  );
32668
32602
  const settings = this.cameraStream.getVideoTracks()[0].getSettings();
32669
32603
  const settingsAudio = this.cameraStream.getAudioTracks()[0].getSettings();
32670
32604
  if (this.options.proctoringType == "VIDEO" || this.options.proctoringType == "REALTIME" || this.options.proctoringType == "IMAGE" && ((_a2 = this.paramsConfig.imageBehaviourParameters) == null ? void 0 : _a2.saveVideo)) {
32605
+ let isUploaded = false;
32671
32606
  if (this.isChunkEnabled) {
32672
- const isStable = await this.checkInternetStability();
32673
- if (isStable) {
32674
- } else {
32675
- if (this.backend && this.backendToken && this.proctoringId) {
32676
- const fileName = `EP_${this.proctoringId}_camera_0.webm`;
32677
- const objectName = `${this.proctoringId}/${fileName}`;
32678
- const isUploaded = await this.backend.checkUpload(this.backendToken, objectName, "video/webm");
32679
- if (isUploaded) {
32680
- this.chunkStorage && await this.chunkStorage.clearAllChunks(session.id);
32681
- return;
32682
- }
32683
- }
32607
+ if (this.backend && this.backendToken && this.proctoringId) {
32608
+ const fileName = `EP_${this.proctoringId}_camera_0.webm`;
32609
+ const objectName = `${this.proctoringId}/${fileName}`;
32610
+ isUploaded = await this.backend.checkUpload(this.backendToken, objectName, "video/webm");
32611
+ this.chunkStorage && await this.chunkStorage.clearAllChunks(session.id);
32684
32612
  }
32685
32613
  }
32686
- const rawBlob = new Blob(this.blobs, {
32687
- type: ((_b = this.recorderOptions) == null ? void 0 : _b.mimeType) || "video/webm"
32688
- });
32689
- const fixedBlob = await fixWebmDuration(rawBlob, this.duration);
32690
- session.addRecording({
32691
- device: `Audio
32614
+ if (!isUploaded) {
32615
+ const rawBlob = new Blob(this.blobs, {
32616
+ type: ((_b = this.recorderOptions) == null ? void 0 : _b.mimeType) || "video/webm"
32617
+ });
32618
+ let finalBlob = rawBlob;
32619
+ if (typeof fixWebmDuration === "function") {
32620
+ finalBlob = await fixWebmDuration(rawBlob, this.duration);
32621
+ } else {
32622
+ console.warn("fixWebmDuration n\xE3o dispon\xEDvel");
32623
+ }
32624
+ session.addRecording({
32625
+ device: `Audio
32692
32626
  Sample Rate: ${settingsAudio.sampleRate}
32693
32627
  Sample Size: ${settingsAudio.sampleSize}
32694
32628
 
32695
32629
  Video:
32696
32630
  ${JSON.stringify(this.recorderOptions)}`,
32697
- arrayBuffer: await fixedBlob.arrayBuffer(),
32698
- origin: "Camera" /* Camera */
32699
- });
32631
+ arrayBuffer: await finalBlob.arrayBuffer(),
32632
+ origin: "Camera" /* Camera */
32633
+ });
32634
+ }
32700
32635
  }
32701
32636
  }
32702
32637
  async getFile(file, name, type) {
@@ -32709,51 +32644,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
32709
32644
  });
32710
32645
  });
32711
32646
  }
32712
- /**
32713
- * Verifica se a internet está estável para realizar o upload do vídeo na íntegra.
32714
- */
32715
- async checkInternetStability() {
32716
- var _a2;
32717
- if (!navigator.onLine) return false;
32718
- try {
32719
- const controller = new AbortController();
32720
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
32721
- const baseUrl = (_a2 = this.backend) == null ? void 0 : _a2.getBaseUrl();
32722
- if (!baseUrl) return true;
32723
- const response = await fetch(`${baseUrl}/Client/health`, {
32724
- method: "GET",
32725
- signal: controller.signal
32726
- });
32727
- clearTimeout(timeoutId);
32728
- return response.status < 500;
32729
- } catch (e3) {
32730
- console.warn("[CameraRecorder] Internet inst\xE1vel ou lenta detectada para upload integral.");
32731
- return false;
32732
- }
32733
- }
32734
- onNoiseDetected() {
32735
- var _a2, _b, _c2;
32736
- if (this.options.proctoringType === "REALTIME") return;
32737
- if (!this.volumeMeter && this.cameraStream) {
32738
- this.volumeMeter = new VolumeMeter(this.cameraStream);
32739
- this.volumeMeter.start().catch((e3) => {
32740
- console.log(e3);
32741
- this.volumeMeter = void 0;
32742
- });
32743
- }
32744
- const volume = (_b = (_a2 = this.volumeMeter) == null ? void 0 : _a2.getVolume()) != null ? _b : 0;
32745
- if (volume >= (((_c2 = this.paramsConfig.audioBehaviourParameters) == null ? void 0 : _c2.noiseLimit) || 40)) {
32746
- if (this.noiseWait >= 20) {
32747
- this.options.onRealtimeAlertsCallback({
32748
- status: "ALERT",
32749
- description: "Barulho detectado",
32750
- type: "audio_detection_on_stream"
32751
- });
32752
- this.noiseWait = 0;
32753
- }
32754
- }
32755
- this.noiseWait++;
32756
- }
32757
32647
  };
32758
32648
  // ========================
32759
32649
  // Chunk & Lifecycle
@@ -32764,10 +32654,62 @@ _CameraRecorder.CHUNK_TIMESLICE_MS = 6e4;
32764
32654
  _CameraRecorder.LS_SESSION_KEY = "ep_proctoring_session";
32765
32655
  var CameraRecorder = _CameraRecorder;
32766
32656
 
32657
+ // src/new-flow/recorders/VolumeMeter.ts
32658
+ var VolumeMeter = class {
32659
+ constructor(stream4) {
32660
+ this.volume = null;
32661
+ this.animationFrameId = null;
32662
+ this.stream = stream4;
32663
+ }
32664
+ async start(options = {}) {
32665
+ return new Promise((resolve, reject) => {
32666
+ try {
32667
+ this.audioContext = new AudioContext();
32668
+ this.analyser = this.audioContext.createAnalyser();
32669
+ this.microphone = this.audioContext.createMediaStreamSource(this.stream);
32670
+ this.analyser.smoothingTimeConstant = 0.8;
32671
+ this.analyser.fftSize = 1024;
32672
+ this.microphone.connect(this.analyser);
32673
+ const processAudio = () => {
32674
+ const array = new Uint8Array(this.analyser.frequencyBinCount);
32675
+ this.analyser.getByteFrequencyData(array);
32676
+ const arraySum = array.reduce((a3, value) => a3 + value, 0);
32677
+ const average = arraySum / array.length;
32678
+ this.setVolume(average);
32679
+ options.setVolume && options.setVolume(average);
32680
+ this.animationFrameId = requestAnimationFrame(processAudio);
32681
+ };
32682
+ this.animationFrameId = requestAnimationFrame(processAudio);
32683
+ resolve(true);
32684
+ } catch (error) {
32685
+ this.stop();
32686
+ reject(`Error: ${error}`);
32687
+ }
32688
+ });
32689
+ }
32690
+ stop() {
32691
+ var _a2, _b, _c2;
32692
+ if (this.animationFrameId !== null) {
32693
+ cancelAnimationFrame(this.animationFrameId);
32694
+ }
32695
+ (_a2 = this.audioContext) == null ? void 0 : _a2.close();
32696
+ (_b = this.microphone) == null ? void 0 : _b.disconnect();
32697
+ (_c2 = this.analyser) == null ? void 0 : _c2.disconnect();
32698
+ }
32699
+ getVolume() {
32700
+ return this.volume;
32701
+ }
32702
+ setVolume(value) {
32703
+ this.volume = value;
32704
+ }
32705
+ };
32706
+
32767
32707
  // src/new-flow/checkers/DeviceCheckerUI.ts
32768
32708
  var DeviceCheckerUI = class {
32769
32709
  constructor(options = getDefaultProctoringOptions, _videoOptions) {
32770
32710
  this.videoOptions = { width: 1080, height: 720, minWidth: 0, minHeight: 0 };
32711
+ this.autoConfirmTimer = null;
32712
+ this.autoConfirmSeconds = 5;
32771
32713
  this.options = {
32772
32714
  ...getDefaultProctoringOptions,
32773
32715
  ...options,
@@ -33533,9 +33475,53 @@ var DeviceCheckerUI = class {
33533
33475
  }));
33534
33476
  }
33535
33477
  closeModal() {
33478
+ this.stopAutoConfirm();
33536
33479
  const checkDevices = document.querySelector("#checkDevices");
33537
33480
  checkDevices == null ? void 0 : checkDevices.remove();
33538
33481
  }
33482
+ verifyAutoConfirm(status) {
33483
+ var _a2;
33484
+ if (!((_a2 = this.options) == null ? void 0 : _a2.auto)) return;
33485
+ let isAllGreen = status.allowedResolution && status.allowedPositionFace && status.allowedAmbient && status.allowedMicrophone;
33486
+ if (this.options.useSpyScan) {
33487
+ isAllGreen = isAllGreen && status.allowedSpyScan === true;
33488
+ }
33489
+ if (isAllGreen) {
33490
+ if (!this.autoConfirmTimer) {
33491
+ this.startAutoConfirm();
33492
+ }
33493
+ } else {
33494
+ if (this.autoConfirmTimer) {
33495
+ this.stopAutoConfirm();
33496
+ }
33497
+ }
33498
+ }
33499
+ startAutoConfirm() {
33500
+ this.autoConfirmSeconds = 5;
33501
+ const btn = document.getElementById("confirmBtn");
33502
+ if (btn && !btn.disabled) {
33503
+ btn.innerText = `Continuando em ${this.autoConfirmSeconds}s...`;
33504
+ this.autoConfirmTimer = setInterval(() => {
33505
+ this.autoConfirmSeconds--;
33506
+ if (this.autoConfirmSeconds <= 0) {
33507
+ this.stopAutoConfirm();
33508
+ btn.click();
33509
+ } else {
33510
+ btn.innerText = `Continuando em ${this.autoConfirmSeconds}s...`;
33511
+ }
33512
+ }, 1e3);
33513
+ }
33514
+ }
33515
+ stopAutoConfirm() {
33516
+ if (this.autoConfirmTimer) {
33517
+ clearInterval(this.autoConfirmTimer);
33518
+ this.autoConfirmTimer = null;
33519
+ }
33520
+ const btn = document.getElementById("confirmBtn");
33521
+ if (btn) {
33522
+ btn.innerText = "Continuar";
33523
+ }
33524
+ }
33539
33525
  modalActions(closeCheckDevices) {
33540
33526
  const cancelBtn = document.getElementById("cancelBtn");
33541
33527
  const confirmBtn = document.getElementById("confirmBtn");
@@ -33902,6 +33888,7 @@ var _DeviceCheckerService = class _DeviceCheckerService {
33902
33888
  }
33903
33889
  }
33904
33890
  onUpdateCallback() {
33891
+ var _a2;
33905
33892
  if (typeof this.onUpdateCb === "function") {
33906
33893
  this.onUpdateCb({
33907
33894
  allowedResolution: this.allowedResolution,
@@ -33912,6 +33899,15 @@ var _DeviceCheckerService = class _DeviceCheckerService {
33912
33899
  faceDetectionAlerts: this.faceDetectionAlerts
33913
33900
  });
33914
33901
  }
33902
+ if (((_a2 = this.options) == null ? void 0 : _a2.auto) && this.DeviceCheckerUI) {
33903
+ this.DeviceCheckerUI.verifyAutoConfirm({
33904
+ allowedResolution: this.allowedResolution,
33905
+ allowedPositionFace: this.allowedPositionFace,
33906
+ allowedAmbient: this.allowedAmbient,
33907
+ allowedMicrophone: this.allowedMicrophone,
33908
+ allowedSpyScan: this.allowedSpyScan
33909
+ });
33910
+ }
33915
33911
  }
33916
33912
  async checkDevices(options = getDefaultProctoringOptions, _videoOptions = getDefaultProctoringVideoOptions) {
33917
33913
  var _a2;
@@ -34118,16 +34114,19 @@ var _DeviceCheckerService = class _DeviceCheckerService {
34118
34114
  }).finally(() => {
34119
34115
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
34120
34116
  this.DeviceCheckerUI && this.allowedSpyScan != null && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
34117
+ this.onUpdateCallback();
34121
34118
  });
34122
34119
  } else {
34123
34120
  this.allowedSpyScan = false;
34124
34121
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
34125
34122
  this.DeviceCheckerUI && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
34123
+ this.onUpdateCallback();
34126
34124
  }
34127
34125
  } catch (error) {
34128
34126
  this.allowedSpyScan = false;
34129
34127
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
34130
34128
  this.DeviceCheckerUI && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
34129
+ this.onUpdateCallback();
34131
34130
  console.log(error);
34132
34131
  }
34133
34132
  }
@@ -34447,8 +34446,8 @@ var CapturePhoto = class {
34447
34446
  }
34448
34447
  };
34449
34448
 
34450
- // src/extension/extension.ts
34451
- var Extension = class {
34449
+ // src/extension/extensionEasyProctor.ts
34450
+ var ExtensionEasyProctor = class {
34452
34451
  constructor() {
34453
34452
  this.hasExtension = false;
34454
34453
  this.tryes = 0;
@@ -34493,6 +34492,89 @@ var Extension = class {
34493
34492
  }
34494
34493
  };
34495
34494
 
34495
+ // src/extension/extensionEasyCatcher.ts
34496
+ var ExtensionEasyCatcher = class {
34497
+ constructor(options) {
34498
+ this.hasExtension = false;
34499
+ this.tryes = 0;
34500
+ this.responseStart = false;
34501
+ this.options = options || {};
34502
+ }
34503
+ /**
34504
+ * Verifica se a extensão está instalada e ativa.
34505
+ * Retorna o número da versão se encontrada, ou lança erro após timeout.
34506
+ */
34507
+ checkExtensionInstalled(timeoutMs = 2e3) {
34508
+ return new Promise((resolve, reject) => {
34509
+ let handled = false;
34510
+ const handler = (event) => {
34511
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "version") {
34512
+ handled = true;
34513
+ window.removeEventListener("message", handler);
34514
+ resolve(event.data.message);
34515
+ }
34516
+ };
34517
+ window.addEventListener("message", handler);
34518
+ window.postMessage({
34519
+ type: "easycatcher",
34520
+ func: "verifyExtensionEasycatcher"
34521
+ }, "*");
34522
+ setTimeout(() => {
34523
+ if (!handled) {
34524
+ window.removeEventListener("message", handler);
34525
+ reject(new Error("Extens\xE3o n\xE3o detectada ou n\xE3o respondeu."));
34526
+ }
34527
+ }, timeoutMs);
34528
+ });
34529
+ }
34530
+ /**
34531
+ * Solicita o JSON da sessão atual capturado pela extensão.
34532
+ */
34533
+ getSessionData(timeoutMs = 5e3) {
34534
+ return new Promise((resolve, reject) => {
34535
+ let handled = false;
34536
+ const handler = (event) => {
34537
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "data_response") {
34538
+ handled = true;
34539
+ window.removeEventListener("message", handler);
34540
+ resolve(event.data.payload);
34541
+ }
34542
+ };
34543
+ window.addEventListener("message", handler);
34544
+ window.postMessage({
34545
+ type: "easycatcher",
34546
+ func: "getDataExtensionEasycatcher"
34547
+ }, "*");
34548
+ setTimeout(() => {
34549
+ if (!handled) {
34550
+ window.removeEventListener("message", handler);
34551
+ reject(new Error("Timeout ao aguardar dados da extens\xE3o."));
34552
+ }
34553
+ }, timeoutMs);
34554
+ });
34555
+ }
34556
+ start() {
34557
+ return new Promise((resolve, reject) => {
34558
+ let handled = false;
34559
+ const handler = (event) => {
34560
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "started_confirmed") {
34561
+ handled = true;
34562
+ window.removeEventListener("message", handler);
34563
+ resolve(true);
34564
+ }
34565
+ };
34566
+ window.addEventListener("message", handler);
34567
+ window.postMessage({ type: "easycatcher", func: "startExtensionEasycatcher" }, "*");
34568
+ setTimeout(() => {
34569
+ if (!handled) {
34570
+ window.removeEventListener("message", handler);
34571
+ reject(new Error("Timeout: Extens\xE3o n\xE3o confirmou o in\xEDcio."));
34572
+ }
34573
+ }, 3e3);
34574
+ });
34575
+ }
34576
+ };
34577
+
34496
34578
  // src/modules/onChangeDevices.ts
34497
34579
  var onChangeDevices = class {
34498
34580
  constructor(repositoryDevices, proctoringId2, sessionOptions, allRecorders) {
@@ -34681,9 +34763,19 @@ var ProctoringUploader = class {
34681
34763
  }
34682
34764
  toFile(rec) {
34683
34765
  const suffix = rec.origin === "Screen" /* Screen */ ? "screen" : rec.origin === "Camera" /* Camera */ ? "camera" : "audio";
34684
- const name = `EP_${this.session.id}_${suffix}_0.webm`;
34766
+ const name = `EP_${this.proctoringId}_${suffix}_0.webm`;
34685
34767
  return new File([rec.arrayBuffer], name, { type: "video/webm" });
34686
34768
  }
34769
+ getFileType(origin2) {
34770
+ switch (origin2) {
34771
+ case "Camera" /* Camera */:
34772
+ return "Camera";
34773
+ case "Screen" /* Screen */:
34774
+ return "Screen";
34775
+ case "Mic" /* Mic */:
34776
+ return "Audio";
34777
+ }
34778
+ }
34687
34779
  async uploadFile(rec, token, onProgress) {
34688
34780
  const file = this.toFile(rec);
34689
34781
  for await (const uploadService of this.uploadServices) {
@@ -34695,23 +34787,24 @@ var ProctoringUploader = class {
34695
34787
  }
34696
34788
  },
34697
34789
  token
34698
- ).catch(
34699
- async (e3) => {
34700
- console.log("Upload File Error", e3), trackers.registerError(this.proctoringId, `Upload File
34790
+ ).catch(async (e3) => {
34791
+ console.log("Upload File Error", e3), trackers.registerError(
34792
+ this.proctoringId,
34793
+ `Upload File
34701
34794
  Name: ${file.name}
34702
34795
  Error: ${e3.message}
34703
- Size: ${e3.error}`);
34704
- await uploadService.upload(
34705
- {
34706
- file,
34707
- onProgress: (progress) => {
34708
- if (onProgress) onProgress(progress);
34709
- }
34710
- },
34711
- token
34712
- );
34713
- }
34714
- );
34796
+ Size: ${e3.error}`
34797
+ );
34798
+ await uploadService.upload(
34799
+ {
34800
+ file,
34801
+ onProgress: (progress) => {
34802
+ if (onProgress) onProgress(progress);
34803
+ }
34804
+ },
34805
+ token
34806
+ );
34807
+ });
34715
34808
  if (result) {
34716
34809
  let fileType = "";
34717
34810
  if (rec.origin === "Camera" /* Camera */) {
@@ -37533,10 +37626,15 @@ var ScreenRecorder = class {
37533
37626
  const rawBlob = new Blob(this.blobs, {
37534
37627
  type: "video/webm"
37535
37628
  });
37536
- const fixedBlob = await fixWebmDuration2(rawBlob, this.duration);
37629
+ let finalBlob = rawBlob;
37630
+ if (typeof fixWebmDuration2 === "function") {
37631
+ finalBlob = await fixWebmDuration2(rawBlob, this.duration);
37632
+ } else {
37633
+ console.warn("fixWebmDuration n\xE3o dispon\xEDvel");
37634
+ }
37537
37635
  session.addRecording({
37538
37636
  device: "",
37539
- arrayBuffer: await fixedBlob.arrayBuffer(),
37637
+ arrayBuffer: await finalBlob.arrayBuffer(),
37540
37638
  origin: "Screen" /* Screen */
37541
37639
  });
37542
37640
  }
@@ -38820,7 +38918,10 @@ var Proctoring = class {
38820
38918
  if (this.context.token === void 0) {
38821
38919
  throw TOKEN_MISSING;
38822
38920
  }
38823
- this.extension = new Extension();
38921
+ if (options.useChallenge) {
38922
+ this.extensionEasycatcher = new ExtensionEasyCatcher();
38923
+ }
38924
+ this.extension = new ExtensionEasyProctor();
38824
38925
  this.extension.addEventListener();
38825
38926
  const baseURL = this.backend.selectBaseUrl(this.context.type);
38826
38927
  const devices = await enumarateDevices();
@@ -38888,20 +38989,6 @@ var Proctoring = class {
38888
38989
  } catch (error) {
38889
38990
  throw SAFE_BROWSER_API_NOT_FOUND;
38890
38991
  }
38891
- this.allRecorders.cameraRecorder.onVisibilityRestored = () => {
38892
- console.log("[Proctoring] Usu\xE1rio retornou ao browser.");
38893
- this.onVisibilityRestoredCallback();
38894
- };
38895
- if (this.sessionOptions.proctoringType === "REALTIME" && !isSafeBrowser()) {
38896
- try {
38897
- await BackgroundUploadService.recoverPendingUploads(
38898
- this.backend,
38899
- this.context.token
38900
- );
38901
- } catch (e3) {
38902
- console.warn("[Proctoring] Erro ao recuperar chunks de sess\xE3o anterior:", e3);
38903
- }
38904
- }
38905
38992
  try {
38906
38993
  console.log("Starting recorders");
38907
38994
  await this.recorder.startAll();
@@ -38982,7 +39069,9 @@ Error: ${error}`
38982
39069
  this.appChecker && await this.appChecker.disconnectWebSocket();
38983
39070
  await this.recorder.saveAllOnSession();
38984
39071
  await this.sendPendingRealtimeAlerts();
39072
+ trackers.registerError(this.proctoringId, `finish this.repository.save starting`);
38985
39073
  await this.repository.save(this.proctoringSession);
39074
+ trackers.registerError(this.proctoringId, `finish this.repository.save finished`);
38986
39075
  let uploader;
38987
39076
  let uploaderServices;
38988
39077
  if (versionVerify() !== "1.0.0.0") {
@@ -39204,6 +39293,61 @@ Error: ` + error
39204
39293
  _screenStream: (_a2 = this.allRecorders.screenRecorder) == null ? void 0 : _a2.screenStream
39205
39294
  };
39206
39295
  }
39296
+ async startChallenge(templateId) {
39297
+ var _a2;
39298
+ if (!this.sessionOptions.useChallenge) {
39299
+ throw new Error("useChallenge is set as false on start method");
39300
+ }
39301
+ await this.extensionEasycatcher.checkExtensionInstalled().catch((err) => {
39302
+ throw new Error("EasyCatcher Extension is not installed");
39303
+ });
39304
+ this.extensionEasycatcher.start();
39305
+ const start = Date.now() - ((_a2 = this.allRecorders.cameraRecorder.getStartTime()) == null ? void 0 : _a2.getTime()) || 0;
39306
+ await this.backend.startChallenge({
39307
+ proctoringId: this.proctoringId,
39308
+ templateId,
39309
+ start
39310
+ }).then((resp) => {
39311
+ console.log(resp);
39312
+ this.challengeId = resp.id;
39313
+ }).catch((reason) => {
39314
+ trackers.registerError(
39315
+ this.proctoringId,
39316
+ "N\xE3o foi poss\xEDvel iniciar desafio!"
39317
+ );
39318
+ throw reason;
39319
+ });
39320
+ this.isChallengeRunning = true;
39321
+ }
39322
+ async stopChallenge() {
39323
+ var _a2;
39324
+ if (!this.isChallengeRunning) {
39325
+ throw new Error("Challenge not started");
39326
+ }
39327
+ try {
39328
+ const sessionData = await this.extensionEasycatcher.getSessionData();
39329
+ const end = Date.now() - ((_a2 = this.allRecorders.cameraRecorder.getStartTime()) == null ? void 0 : _a2.getTime()) || 0;
39330
+ await this.backend.stopChallenge(
39331
+ this.challengeId,
39332
+ {
39333
+ end,
39334
+ data: sessionData
39335
+ }
39336
+ ).catch((reason) => {
39337
+ trackers.registerError(
39338
+ this.proctoringId,
39339
+ "N\xE3o foi poss\xEDvel finalizar o desafio no backend!"
39340
+ );
39341
+ return void 0;
39342
+ });
39343
+ this.isChallengeRunning = false;
39344
+ } catch (error) {
39345
+ trackers.registerError(
39346
+ this.proctoringId,
39347
+ "Erro ao recuperar dados da extens\xE3o: " + error.message
39348
+ );
39349
+ }
39350
+ }
39207
39351
  };
39208
39352
 
39209
39353
  // src/proctoring/SignTerm.ts
@@ -39432,6 +39576,8 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
39432
39576
  return originalStart(parameters2, videoOptions);
39433
39577
  };
39434
39578
  const finish = proctoring.finish.bind(proctoring);
39579
+ const startChallenge = proctoring.startChallenge.bind(proctoring);
39580
+ const stopChallenge = proctoring.stopChallenge.bind(proctoring);
39435
39581
  const pause = proctoring.pause.bind(proctoring);
39436
39582
  const resume = proctoring.resume.bind(proctoring);
39437
39583
  const onFocus = proctoring.setOnFocusCallback.bind(proctoring);
@@ -39456,6 +39602,8 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
39456
39602
  login,
39457
39603
  start,
39458
39604
  finish,
39605
+ startChallenge,
39606
+ stopChallenge,
39459
39607
  onFocus,
39460
39608
  onLostFocus,
39461
39609
  onChangeDevices: onChangeDevices2,