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/esm/index.js CHANGED
@@ -9340,6 +9340,9 @@ var FaceDetection = class extends BaseDetection {
9340
9340
  if (this.emmitedFaceAlert) {
9341
9341
  this.handleOk("face_stop", "face_detection_on_stream");
9342
9342
  }
9343
+ this.numFacesSent = -1;
9344
+ this.emmitedPositionAlert = false;
9345
+ this.emmitedFaceAlert = false;
9343
9346
  }
9344
9347
  // displayVideoDetections(result: { detections: any; }) {
9345
9348
  // // console.log(result);
@@ -12699,7 +12702,8 @@ var getDefaultProctoringOptions = {
12699
12702
  useSpyScan: false,
12700
12703
  useExternalCamera: false,
12701
12704
  useChallenge: false,
12702
- screenRecorderOptions: { width: 1280, height: 720 }
12705
+ screenRecorderOptions: { width: 1280, height: 720 },
12706
+ auto: false
12703
12707
  };
12704
12708
 
12705
12709
  // src/proctoring/options/ProctoringVideoOptions.ts
@@ -12767,7 +12771,7 @@ var proctoringId;
12767
12771
  function setRecorderProctoringId(id) {
12768
12772
  proctoringId = id;
12769
12773
  }
12770
- function recorder(stream, buffer, onBufferSizeError = false, onBufferSizeErrorCallback, audio = false, recorderOpts) {
12774
+ function recorder(stream, buffer, onBufferSizeError = false, onBufferSizeErrorCallback, audio = false) {
12771
12775
  let resolvePromise;
12772
12776
  let onBufferSizeInterval;
12773
12777
  let lastEvent;
@@ -12775,7 +12779,6 @@ function recorder(stream, buffer, onBufferSizeError = false, onBufferSizeErrorCa
12775
12779
  bufferSize = 0;
12776
12780
  let startTime;
12777
12781
  let duration = 0;
12778
- let chunkIndex = 0;
12779
12782
  let recorderOptions = {
12780
12783
  // eslint-disable-next-line no-useless-escape
12781
12784
  mimeType: "video/webm",
@@ -12810,10 +12813,6 @@ function recorder(stream, buffer, onBufferSizeError = false, onBufferSizeErrorCa
12810
12813
  mediaRecorder2.ondataavailable = (e3) => {
12811
12814
  bufferSize = bufferSize + e3.data.size;
12812
12815
  if (e3.data.size > 0) {
12813
- if (recorderOpts == null ? void 0 : recorderOpts.onChunkAvailable) {
12814
- recorderOpts.onChunkAvailable(e3.data, chunkIndex);
12815
- chunkIndex++;
12816
- }
12817
12816
  buffer.push(e3.data);
12818
12817
  }
12819
12818
  };
@@ -12845,13 +12844,7 @@ function recorder(stream, buffer, onBufferSizeError = false, onBufferSizeErrorCa
12845
12844
  };
12846
12845
  try {
12847
12846
  console.log("State antes do start:", recorder2.state);
12848
- chunkIndex = 0;
12849
- if ((recorderOpts == null ? void 0 : recorderOpts.timeslice) && (recorderOpts == null ? void 0 : recorderOpts.timeslice) > 0) {
12850
- recorder2.start(recorderOpts.timeslice);
12851
- } else {
12852
- recorder2.start(1e4);
12853
- }
12854
- bufferSize = 0;
12847
+ recorder2.start(1e4);
12855
12848
  startTime = new Date(Date.now());
12856
12849
  } catch (e3) {
12857
12850
  console.error("Recorder erro ao chamar start event:", e3);
@@ -12905,6 +12898,9 @@ function recorder(stream, buffer, onBufferSizeError = false, onBufferSizeErrorCa
12905
12898
  console.log("stopRecording Recorder n\xE3o est\xE1 em estado recording");
12906
12899
  resolve();
12907
12900
  }
12901
+ stream.getTracks().forEach((el) => {
12902
+ el.stop();
12903
+ });
12908
12904
  });
12909
12905
  }
12910
12906
  function pauseRecording() {
@@ -13262,55 +13258,8 @@ var ObjectDetection = class extends BaseDetection {
13262
13258
  }
13263
13259
  };
13264
13260
 
13265
- // src/new-flow/recorders/VolumeMeter.ts
13266
- var VolumeMeter = class {
13267
- constructor(stream) {
13268
- this.volume = null;
13269
- this.animationFrameId = null;
13270
- this.stream = stream;
13271
- }
13272
- async start(options = {}) {
13273
- return new Promise((resolve, reject) => {
13274
- try {
13275
- this.audioContext = new AudioContext();
13276
- this.analyser = this.audioContext.createAnalyser();
13277
- this.microphone = this.audioContext.createMediaStreamSource(this.stream);
13278
- this.analyser.smoothingTimeConstant = 0.8;
13279
- this.analyser.fftSize = 1024;
13280
- this.microphone.connect(this.analyser);
13281
- const processAudio = () => {
13282
- const array = new Uint8Array(this.analyser.frequencyBinCount);
13283
- this.analyser.getByteFrequencyData(array);
13284
- const arraySum = array.reduce((a3, value) => a3 + value, 0);
13285
- const average = arraySum / array.length;
13286
- this.setVolume(average);
13287
- options.setVolume && options.setVolume(average);
13288
- this.animationFrameId = requestAnimationFrame(processAudio);
13289
- };
13290
- this.animationFrameId = requestAnimationFrame(processAudio);
13291
- resolve(true);
13292
- } catch (error) {
13293
- this.stop();
13294
- reject(`Error: ${error}`);
13295
- }
13296
- });
13297
- }
13298
- stop() {
13299
- var _a2, _b, _c2;
13300
- if (this.animationFrameId !== null) {
13301
- cancelAnimationFrame(this.animationFrameId);
13302
- }
13303
- (_a2 = this.audioContext) == null ? void 0 : _a2.close();
13304
- (_b = this.microphone) == null ? void 0 : _b.disconnect();
13305
- (_c2 = this.analyser) == null ? void 0 : _c2.disconnect();
13306
- }
13307
- getVolume() {
13308
- return this.volume;
13309
- }
13310
- setVolume(value) {
13311
- this.volume = value;
13312
- }
13313
- };
13261
+ // src/new-flow/recorders/CameraRecorder.ts
13262
+ var import_jszip_min = __toESM(require_jszip_min());
13314
13263
 
13315
13264
  // src/new-flow/chunk/ChunkStorageService.ts
13316
13265
  var _ChunkStorageService = class _ChunkStorageService {
@@ -13328,13 +13277,7 @@ var _ChunkStorageService = class _ChunkStorageService {
13328
13277
  _ChunkStorageService.DB_VERSION
13329
13278
  );
13330
13279
  request.onerror = () => {
13331
- var _a2, _b;
13332
- console.error("IndexedDB error:", request.error);
13333
- reject(
13334
- new Error(
13335
- `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}`
13336
- )
13337
- );
13280
+ reject(new Error("N\xE3o foi poss\xEDvel conectar ao IndexedDB para chunks."));
13338
13281
  };
13339
13282
  request.onupgradeneeded = () => {
13340
13283
  const db = request.result;
@@ -13557,8 +13500,8 @@ var _ChunkStorageService = class _ChunkStorageService {
13557
13500
  }
13558
13501
  };
13559
13502
  _ChunkStorageService.DB_NAME = "EasyProctorChunksDb";
13560
- /** Incrementado para v2 para recriar índices com tipos numéricos em vez de boolean */
13561
- _ChunkStorageService.DB_VERSION = 2;
13503
+ /** v2: índices uploaded numéricos; v3: campo arrayBuffer em vez de blob */
13504
+ _ChunkStorageService.DB_VERSION = 3;
13562
13505
  _ChunkStorageService.STORE_NAME = "chunks";
13563
13506
  var ChunkStorageService = _ChunkStorageService;
13564
13507
 
@@ -13741,8 +13684,9 @@ var BackgroundUploadService = class _BackgroundUploadService {
13741
13684
  let virtualStart = this.totalBytesPurged;
13742
13685
  const chunksWithMeta = allChunks.map((c3) => {
13743
13686
  const start = virtualStart;
13744
- const end = start + c3.blob.size - 1;
13745
- virtualStart += c3.blob.size;
13687
+ const byteLength = c3.arrayBuffer.byteLength;
13688
+ const end = start + byteLength - 1;
13689
+ virtualStart += byteLength;
13746
13690
  return { chunk: c3, start, end };
13747
13691
  });
13748
13692
  let combinedBlobParts = [];
@@ -13752,8 +13696,8 @@ var BackgroundUploadService = class _BackgroundUploadService {
13752
13696
  for (const meta of chunksWithMeta) {
13753
13697
  if (this.currentOffset > meta.end) continue;
13754
13698
  const sliceStart = Math.max(0, this.currentOffset - meta.start);
13755
- const chunkSlice = meta.chunk.blob.slice(sliceStart);
13756
- combinedBlobParts.push(chunkSlice);
13699
+ const sliceBuf = meta.chunk.arrayBuffer.slice(sliceStart);
13700
+ combinedBlobParts.push(new Blob([sliceBuf]));
13757
13701
  lastProcessedChunkId = meta.chunk.id;
13758
13702
  finalChunkIndex = meta.chunk.chunkIndex;
13759
13703
  }
@@ -13787,7 +13731,10 @@ var BackgroundUploadService = class _BackgroundUploadService {
13787
13731
  }
13788
13732
  if (this.config.cleanAfterUpload) {
13789
13733
  const chunksToClear = chunksWithMeta.filter((meta) => meta.chunk.uploaded === 1 || meta.chunk.uploaded === 0 && meta.end < this.currentOffset);
13790
- const sizePurged = chunksToClear.reduce((acc, meta) => acc + meta.chunk.blob.size, 0);
13734
+ const sizePurged = chunksToClear.reduce(
13735
+ (acc, meta) => acc + meta.chunk.arrayBuffer.byteLength,
13736
+ 0
13737
+ );
13791
13738
  await this.chunkStorage.clearUploadedChunks(this.proctoringId);
13792
13739
  if (sizePurged > 0) {
13793
13740
  this.totalBytesPurged += sizePurged;
@@ -13925,7 +13872,6 @@ var BackgroundUploadService = class _BackgroundUploadService {
13925
13872
  };
13926
13873
 
13927
13874
  // src/new-flow/recorders/CameraRecorder.ts
13928
- var import_jszip_min = __toESM(require_jszip_min());
13929
13875
  var pkg = require_fix_webm_duration();
13930
13876
  var fixWebmDuration = pkg.default || pkg;
13931
13877
  var _CameraRecorder = class _CameraRecorder {
@@ -13991,7 +13937,6 @@ var _CameraRecorder = class _CameraRecorder {
13991
13937
  this.currentRetries = 0;
13992
13938
  this.packageCount = 0;
13993
13939
  this.failedUploads = 0;
13994
- this.noiseWait = 20;
13995
13940
  this.options = options;
13996
13941
  this.videoOptions = videoOptions;
13997
13942
  this.backend = backend;
@@ -13999,11 +13944,11 @@ var _CameraRecorder = class _CameraRecorder {
13999
13944
  paramsConfig && (this.paramsConfig = paramsConfig);
14000
13945
  }
14001
13946
  /**
14002
- * Determina se o fluxo de chunks e lifecycle deve estar ativo.
14003
- * Retorna true se:
14004
- * 1. O proctoringId já foi definido (ou seja, estamos em uma sessão real, NÃO no checkDevices)
14005
- * 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
14006
- */
13947
+ * Determina se o fluxo de chunks e lifecycle deve estar ativo.
13948
+ * Retorna true se:
13949
+ * 1. O proctoringId já foi definido (ou seja, estamos em uma sessão real, NÃO no checkDevices)
13950
+ * 2. E (`useChunkRecording` foi explicitamente setado como true OU o dispositivo é mobile)
13951
+ */
14007
13952
  get isChunkEnabled() {
14008
13953
  return !!this.proctoringId && this.options.proctoringType === "REALTIME" && !isSafeBrowser();
14009
13954
  }
@@ -14266,15 +14211,9 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14266
14211
  await new Promise((r3) => setTimeout(r3, 300));
14267
14212
  }
14268
14213
  async startRecording() {
14269
- var _a2, _b, _c2, _d, _e3, _f, _g, _h;
14214
+ var _a2, _b, _c2, _d, _e3, _f, _g;
14270
14215
  await this.startStream();
14271
14216
  await this.attachAndWarmup(this.cameraStream);
14272
- const recorderOpts = this.isChunkEnabled ? {
14273
- timeslice: _CameraRecorder.CHUNK_TIMESLICE_MS,
14274
- onChunkAvailable: (blob, idx) => {
14275
- this.handleNewChunk(blob, idx);
14276
- }
14277
- } : {};
14278
14217
  const {
14279
14218
  startRecording,
14280
14219
  stopRecording,
@@ -14290,8 +14229,7 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14290
14229
  this.blobs,
14291
14230
  this.options.onBufferSizeError,
14292
14231
  (e3) => this.bufferError(e3),
14293
- false,
14294
- recorderOpts
14232
+ false
14295
14233
  );
14296
14234
  this.recordingStart = startRecording;
14297
14235
  this.recordingStop = stopRecording;
@@ -14301,18 +14239,13 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14301
14239
  this.getBufferSize = getBufferSize;
14302
14240
  this.getStartTime = getStartTime;
14303
14241
  this.getDuration = getDuration;
14304
- this.chunkIndex = 0;
14305
- if (this.isChunkEnabled) {
14306
- (_a2 = this.backgroundUpload) == null ? void 0 : _a2.start();
14307
- this.setupLifecycleListeners();
14308
- }
14309
14242
  try {
14310
14243
  await new Promise((r3) => setTimeout(r3, 500));
14311
14244
  await this.recordingStart();
14312
14245
  } catch (error) {
14313
14246
  console.log("Camera Recorder error", error);
14314
14247
  this.stopRecording();
14315
- const maxRetries = ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.maxRetries) || 3;
14248
+ const maxRetries = ((_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.maxRetries) || 3;
14316
14249
  if (this.currentRetries < maxRetries) {
14317
14250
  console.log("Camera Recorder retry", this.currentRetries);
14318
14251
  this.currentRetries++;
@@ -14322,13 +14255,13 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14322
14255
  }
14323
14256
  }
14324
14257
  this.stopped = false;
14325
- 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)) {
14258
+ 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)) {
14326
14259
  await this.initializeDetectors();
14327
14260
  }
14328
- if ((_f = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _f.detectFace) {
14261
+ if ((_e3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _e3.detectFace) {
14329
14262
  await this.faceDetection.enableCam(this.cameraStream);
14330
14263
  }
14331
- if (((_g = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _g.detectPerson) || ((_h = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _h.detectCellPhone)) {
14264
+ if (((_f = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _f.detectPerson) || ((_g = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _g.detectCellPhone)) {
14332
14265
  await this.objectDetection.enableCam(this.cameraStream);
14333
14266
  }
14334
14267
  this.filesToUpload = [];
@@ -14413,10 +14346,11 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14413
14346
  const savePromise = (async () => {
14414
14347
  var _a2;
14415
14348
  try {
14349
+ const arrayBuffer = await blob.arrayBuffer();
14416
14350
  await this.chunkStorage.saveChunk({
14417
14351
  proctoringId: this.proctoringId,
14418
14352
  chunkIndex: this.chunkIndex,
14419
- blob,
14353
+ arrayBuffer,
14420
14354
  timestamp: Date.now(),
14421
14355
  uploaded: 0,
14422
14356
  mimeType: ((_a2 = this.recorderOptions) == null ? void 0 : _a2.mimeType) || "video/webm"
@@ -14566,40 +14500,41 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14566
14500
  if (this.blobs != null)
14567
14501
  trackers.registerSaveOnSession(
14568
14502
  this.proctoringId,
14569
- `Blobs Length: ${this.blobs.length} Buffer Size: ${this.getBufferSize()} ChunkEnabled: ${this.isChunkEnabled}`
14503
+ `Blobs Length: ${this.blobs.length} Buffer Size: ${this.getBufferSize()} `
14570
14504
  );
14571
14505
  const settings = this.cameraStream.getVideoTracks()[0].getSettings();
14572
14506
  const settingsAudio = this.cameraStream.getAudioTracks()[0].getSettings();
14573
14507
  if (this.options.proctoringType == "VIDEO" || this.options.proctoringType == "REALTIME" || this.options.proctoringType == "IMAGE" && ((_a2 = this.paramsConfig.imageBehaviourParameters) == null ? void 0 : _a2.saveVideo)) {
14508
+ let isUploaded = false;
14574
14509
  if (this.isChunkEnabled) {
14575
- const isStable = await this.checkInternetStability();
14576
- if (isStable) {
14577
- } else {
14578
- if (this.backend && this.backendToken && this.proctoringId) {
14579
- const fileName = `EP_${this.proctoringId}_camera_0.webm`;
14580
- const objectName = `${this.proctoringId}/${fileName}`;
14581
- const isUploaded = await this.backend.checkUpload(this.backendToken, objectName, "video/webm");
14582
- if (isUploaded) {
14583
- this.chunkStorage && await this.chunkStorage.clearAllChunks(session.id);
14584
- return;
14585
- }
14586
- }
14510
+ if (this.backend && this.backendToken && this.proctoringId) {
14511
+ const fileName = `EP_${this.proctoringId}_camera_0.webm`;
14512
+ const objectName = `${this.proctoringId}/${fileName}`;
14513
+ isUploaded = await this.backend.checkUpload(this.backendToken, objectName, "video/webm");
14514
+ this.chunkStorage && await this.chunkStorage.clearAllChunks(session.id);
14587
14515
  }
14588
14516
  }
14589
- const rawBlob = new Blob(this.blobs, {
14590
- type: ((_b = this.recorderOptions) == null ? void 0 : _b.mimeType) || "video/webm"
14591
- });
14592
- const fixedBlob = await fixWebmDuration(rawBlob, this.duration);
14593
- session.addRecording({
14594
- device: `Audio
14517
+ if (!isUploaded) {
14518
+ const rawBlob = new Blob(this.blobs, {
14519
+ type: ((_b = this.recorderOptions) == null ? void 0 : _b.mimeType) || "video/webm"
14520
+ });
14521
+ let finalBlob = rawBlob;
14522
+ if (typeof fixWebmDuration === "function") {
14523
+ finalBlob = await fixWebmDuration(rawBlob, this.duration);
14524
+ } else {
14525
+ console.warn("fixWebmDuration n\xE3o dispon\xEDvel");
14526
+ }
14527
+ session.addRecording({
14528
+ device: `Audio
14595
14529
  Sample Rate: ${settingsAudio.sampleRate}
14596
14530
  Sample Size: ${settingsAudio.sampleSize}
14597
14531
 
14598
14532
  Video:
14599
14533
  ${JSON.stringify(this.recorderOptions)}`,
14600
- arrayBuffer: await fixedBlob.arrayBuffer(),
14601
- origin: "Camera" /* Camera */
14602
- });
14534
+ arrayBuffer: await finalBlob.arrayBuffer(),
14535
+ origin: "Camera" /* Camera */
14536
+ });
14537
+ }
14603
14538
  }
14604
14539
  }
14605
14540
  async getFile(file, name, type) {
@@ -14612,51 +14547,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
14612
14547
  });
14613
14548
  });
14614
14549
  }
14615
- /**
14616
- * Verifica se a internet está estável para realizar o upload do vídeo na íntegra.
14617
- */
14618
- async checkInternetStability() {
14619
- var _a2;
14620
- if (!navigator.onLine) return false;
14621
- try {
14622
- const controller = new AbortController();
14623
- const timeoutId = setTimeout(() => controller.abort(), 5e3);
14624
- const baseUrl = (_a2 = this.backend) == null ? void 0 : _a2.getBaseUrl();
14625
- if (!baseUrl) return true;
14626
- const response = await fetch(`${baseUrl}/Client/health`, {
14627
- method: "GET",
14628
- signal: controller.signal
14629
- });
14630
- clearTimeout(timeoutId);
14631
- return response.status < 500;
14632
- } catch (e3) {
14633
- console.warn("[CameraRecorder] Internet inst\xE1vel ou lenta detectada para upload integral.");
14634
- return false;
14635
- }
14636
- }
14637
- onNoiseDetected() {
14638
- var _a2, _b, _c2;
14639
- if (this.options.proctoringType === "REALTIME") return;
14640
- if (!this.volumeMeter && this.cameraStream) {
14641
- this.volumeMeter = new VolumeMeter(this.cameraStream);
14642
- this.volumeMeter.start().catch((e3) => {
14643
- console.log(e3);
14644
- this.volumeMeter = void 0;
14645
- });
14646
- }
14647
- const volume = (_b = (_a2 = this.volumeMeter) == null ? void 0 : _a2.getVolume()) != null ? _b : 0;
14648
- if (volume >= (((_c2 = this.paramsConfig.audioBehaviourParameters) == null ? void 0 : _c2.noiseLimit) || 40)) {
14649
- if (this.noiseWait >= 20) {
14650
- this.options.onRealtimeAlertsCallback({
14651
- status: "ALERT",
14652
- description: "Barulho detectado",
14653
- type: "audio_detection_on_stream"
14654
- });
14655
- this.noiseWait = 0;
14656
- }
14657
- }
14658
- this.noiseWait++;
14659
- }
14660
14550
  };
14661
14551
  // ========================
14662
14552
  // Chunk & Lifecycle
@@ -14667,10 +14557,62 @@ _CameraRecorder.CHUNK_TIMESLICE_MS = 6e4;
14667
14557
  _CameraRecorder.LS_SESSION_KEY = "ep_proctoring_session";
14668
14558
  var CameraRecorder = _CameraRecorder;
14669
14559
 
14560
+ // src/new-flow/recorders/VolumeMeter.ts
14561
+ var VolumeMeter = class {
14562
+ constructor(stream) {
14563
+ this.volume = null;
14564
+ this.animationFrameId = null;
14565
+ this.stream = stream;
14566
+ }
14567
+ async start(options = {}) {
14568
+ return new Promise((resolve, reject) => {
14569
+ try {
14570
+ this.audioContext = new AudioContext();
14571
+ this.analyser = this.audioContext.createAnalyser();
14572
+ this.microphone = this.audioContext.createMediaStreamSource(this.stream);
14573
+ this.analyser.smoothingTimeConstant = 0.8;
14574
+ this.analyser.fftSize = 1024;
14575
+ this.microphone.connect(this.analyser);
14576
+ const processAudio = () => {
14577
+ const array = new Uint8Array(this.analyser.frequencyBinCount);
14578
+ this.analyser.getByteFrequencyData(array);
14579
+ const arraySum = array.reduce((a3, value) => a3 + value, 0);
14580
+ const average = arraySum / array.length;
14581
+ this.setVolume(average);
14582
+ options.setVolume && options.setVolume(average);
14583
+ this.animationFrameId = requestAnimationFrame(processAudio);
14584
+ };
14585
+ this.animationFrameId = requestAnimationFrame(processAudio);
14586
+ resolve(true);
14587
+ } catch (error) {
14588
+ this.stop();
14589
+ reject(`Error: ${error}`);
14590
+ }
14591
+ });
14592
+ }
14593
+ stop() {
14594
+ var _a2, _b, _c2;
14595
+ if (this.animationFrameId !== null) {
14596
+ cancelAnimationFrame(this.animationFrameId);
14597
+ }
14598
+ (_a2 = this.audioContext) == null ? void 0 : _a2.close();
14599
+ (_b = this.microphone) == null ? void 0 : _b.disconnect();
14600
+ (_c2 = this.analyser) == null ? void 0 : _c2.disconnect();
14601
+ }
14602
+ getVolume() {
14603
+ return this.volume;
14604
+ }
14605
+ setVolume(value) {
14606
+ this.volume = value;
14607
+ }
14608
+ };
14609
+
14670
14610
  // src/new-flow/checkers/DeviceCheckerUI.ts
14671
14611
  var DeviceCheckerUI = class {
14672
14612
  constructor(options = getDefaultProctoringOptions, _videoOptions) {
14673
14613
  this.videoOptions = { width: 1080, height: 720, minWidth: 0, minHeight: 0 };
14614
+ this.autoConfirmTimer = null;
14615
+ this.autoConfirmSeconds = 5;
14674
14616
  this.options = {
14675
14617
  ...getDefaultProctoringOptions,
14676
14618
  ...options,
@@ -15436,9 +15378,53 @@ var DeviceCheckerUI = class {
15436
15378
  }));
15437
15379
  }
15438
15380
  closeModal() {
15381
+ this.stopAutoConfirm();
15439
15382
  const checkDevices = document.querySelector("#checkDevices");
15440
15383
  checkDevices == null ? void 0 : checkDevices.remove();
15441
15384
  }
15385
+ verifyAutoConfirm(status) {
15386
+ var _a2;
15387
+ if (!((_a2 = this.options) == null ? void 0 : _a2.auto)) return;
15388
+ let isAllGreen = status.allowedResolution && status.allowedPositionFace && status.allowedAmbient && status.allowedMicrophone;
15389
+ if (this.options.useSpyScan) {
15390
+ isAllGreen = isAllGreen && status.allowedSpyScan === true;
15391
+ }
15392
+ if (isAllGreen) {
15393
+ if (!this.autoConfirmTimer) {
15394
+ this.startAutoConfirm();
15395
+ }
15396
+ } else {
15397
+ if (this.autoConfirmTimer) {
15398
+ this.stopAutoConfirm();
15399
+ }
15400
+ }
15401
+ }
15402
+ startAutoConfirm() {
15403
+ this.autoConfirmSeconds = 5;
15404
+ const btn = document.getElementById("confirmBtn");
15405
+ if (btn && !btn.disabled) {
15406
+ btn.innerText = `Continuando em ${this.autoConfirmSeconds}s...`;
15407
+ this.autoConfirmTimer = setInterval(() => {
15408
+ this.autoConfirmSeconds--;
15409
+ if (this.autoConfirmSeconds <= 0) {
15410
+ this.stopAutoConfirm();
15411
+ btn.click();
15412
+ } else {
15413
+ btn.innerText = `Continuando em ${this.autoConfirmSeconds}s...`;
15414
+ }
15415
+ }, 1e3);
15416
+ }
15417
+ }
15418
+ stopAutoConfirm() {
15419
+ if (this.autoConfirmTimer) {
15420
+ clearInterval(this.autoConfirmTimer);
15421
+ this.autoConfirmTimer = null;
15422
+ }
15423
+ const btn = document.getElementById("confirmBtn");
15424
+ if (btn) {
15425
+ btn.innerText = "Continuar";
15426
+ }
15427
+ }
15442
15428
  modalActions(closeCheckDevices) {
15443
15429
  const cancelBtn = document.getElementById("cancelBtn");
15444
15430
  const confirmBtn = document.getElementById("confirmBtn");
@@ -15805,6 +15791,7 @@ var _DeviceCheckerService = class _DeviceCheckerService {
15805
15791
  }
15806
15792
  }
15807
15793
  onUpdateCallback() {
15794
+ var _a2;
15808
15795
  if (typeof this.onUpdateCb === "function") {
15809
15796
  this.onUpdateCb({
15810
15797
  allowedResolution: this.allowedResolution,
@@ -15815,6 +15802,15 @@ var _DeviceCheckerService = class _DeviceCheckerService {
15815
15802
  faceDetectionAlerts: this.faceDetectionAlerts
15816
15803
  });
15817
15804
  }
15805
+ if (((_a2 = this.options) == null ? void 0 : _a2.auto) && this.DeviceCheckerUI) {
15806
+ this.DeviceCheckerUI.verifyAutoConfirm({
15807
+ allowedResolution: this.allowedResolution,
15808
+ allowedPositionFace: this.allowedPositionFace,
15809
+ allowedAmbient: this.allowedAmbient,
15810
+ allowedMicrophone: this.allowedMicrophone,
15811
+ allowedSpyScan: this.allowedSpyScan
15812
+ });
15813
+ }
15818
15814
  }
15819
15815
  async checkDevices(options = getDefaultProctoringOptions, _videoOptions = getDefaultProctoringVideoOptions) {
15820
15816
  var _a2;
@@ -16021,16 +16017,19 @@ var _DeviceCheckerService = class _DeviceCheckerService {
16021
16017
  }).finally(() => {
16022
16018
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
16023
16019
  this.DeviceCheckerUI && this.allowedSpyScan != null && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
16020
+ this.onUpdateCallback();
16024
16021
  });
16025
16022
  } else {
16026
16023
  this.allowedSpyScan = false;
16027
16024
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
16028
16025
  this.DeviceCheckerUI && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
16026
+ this.onUpdateCallback();
16029
16027
  }
16030
16028
  } catch (error) {
16031
16029
  this.allowedSpyScan = false;
16032
16030
  this.DeviceCheckerUI && this.DeviceCheckerUI.waitingSpyDevices(false);
16033
16031
  this.DeviceCheckerUI && this.DeviceCheckerUI.isSpyDevicesUI(this.allowedSpyScan);
16032
+ this.onUpdateCallback();
16034
16033
  console.log(error);
16035
16034
  }
16036
16035
  }
@@ -16350,8 +16349,8 @@ var CapturePhoto = class {
16350
16349
  }
16351
16350
  };
16352
16351
 
16353
- // src/extension/extension.ts
16354
- var Extension = class {
16352
+ // src/extension/extensionEasyProctor.ts
16353
+ var ExtensionEasyProctor = class {
16355
16354
  constructor() {
16356
16355
  this.hasExtension = false;
16357
16356
  this.tryes = 0;
@@ -16396,6 +16395,89 @@ var Extension = class {
16396
16395
  }
16397
16396
  };
16398
16397
 
16398
+ // src/extension/extensionEasyCatcher.ts
16399
+ var ExtensionEasyCatcher = class {
16400
+ constructor(options) {
16401
+ this.hasExtension = false;
16402
+ this.tryes = 0;
16403
+ this.responseStart = false;
16404
+ this.options = options || {};
16405
+ }
16406
+ /**
16407
+ * Verifica se a extensão está instalada e ativa.
16408
+ * Retorna o número da versão se encontrada, ou lança erro após timeout.
16409
+ */
16410
+ checkExtensionInstalled(timeoutMs = 2e3) {
16411
+ return new Promise((resolve, reject) => {
16412
+ let handled = false;
16413
+ const handler = (event) => {
16414
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "version") {
16415
+ handled = true;
16416
+ window.removeEventListener("message", handler);
16417
+ resolve(event.data.message);
16418
+ }
16419
+ };
16420
+ window.addEventListener("message", handler);
16421
+ window.postMessage({
16422
+ type: "easycatcher",
16423
+ func: "verifyExtensionEasycatcher"
16424
+ }, "*");
16425
+ setTimeout(() => {
16426
+ if (!handled) {
16427
+ window.removeEventListener("message", handler);
16428
+ reject(new Error("Extens\xE3o n\xE3o detectada ou n\xE3o respondeu."));
16429
+ }
16430
+ }, timeoutMs);
16431
+ });
16432
+ }
16433
+ /**
16434
+ * Solicita o JSON da sessão atual capturado pela extensão.
16435
+ */
16436
+ getSessionData(timeoutMs = 5e3) {
16437
+ return new Promise((resolve, reject) => {
16438
+ let handled = false;
16439
+ const handler = (event) => {
16440
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "data_response") {
16441
+ handled = true;
16442
+ window.removeEventListener("message", handler);
16443
+ resolve(event.data.payload);
16444
+ }
16445
+ };
16446
+ window.addEventListener("message", handler);
16447
+ window.postMessage({
16448
+ type: "easycatcher",
16449
+ func: "getDataExtensionEasycatcher"
16450
+ }, "*");
16451
+ setTimeout(() => {
16452
+ if (!handled) {
16453
+ window.removeEventListener("message", handler);
16454
+ reject(new Error("Timeout ao aguardar dados da extens\xE3o."));
16455
+ }
16456
+ }, timeoutMs);
16457
+ });
16458
+ }
16459
+ start() {
16460
+ return new Promise((resolve, reject) => {
16461
+ let handled = false;
16462
+ const handler = (event) => {
16463
+ if (event.source === window && event.data.sender === "easyproctor-extension" && event.data.message_name === "started_confirmed") {
16464
+ handled = true;
16465
+ window.removeEventListener("message", handler);
16466
+ resolve(true);
16467
+ }
16468
+ };
16469
+ window.addEventListener("message", handler);
16470
+ window.postMessage({ type: "easycatcher", func: "startExtensionEasycatcher" }, "*");
16471
+ setTimeout(() => {
16472
+ if (!handled) {
16473
+ window.removeEventListener("message", handler);
16474
+ reject(new Error("Timeout: Extens\xE3o n\xE3o confirmou o in\xEDcio."));
16475
+ }
16476
+ }, 3e3);
16477
+ });
16478
+ }
16479
+ };
16480
+
16399
16481
  // src/modules/onChangeDevices.ts
16400
16482
  var onChangeDevices = class {
16401
16483
  constructor(repositoryDevices, proctoringId2, sessionOptions, allRecorders) {
@@ -16584,9 +16666,19 @@ var ProctoringUploader = class {
16584
16666
  }
16585
16667
  toFile(rec) {
16586
16668
  const suffix = rec.origin === "Screen" /* Screen */ ? "screen" : rec.origin === "Camera" /* Camera */ ? "camera" : "audio";
16587
- const name = `EP_${this.session.id}_${suffix}_0.webm`;
16669
+ const name = `EP_${this.proctoringId}_${suffix}_0.webm`;
16588
16670
  return new File([rec.arrayBuffer], name, { type: "video/webm" });
16589
16671
  }
16672
+ getFileType(origin2) {
16673
+ switch (origin2) {
16674
+ case "Camera" /* Camera */:
16675
+ return "Camera";
16676
+ case "Screen" /* Screen */:
16677
+ return "Screen";
16678
+ case "Mic" /* Mic */:
16679
+ return "Audio";
16680
+ }
16681
+ }
16590
16682
  async uploadFile(rec, token, onProgress) {
16591
16683
  const file = this.toFile(rec);
16592
16684
  for await (const uploadService of this.uploadServices) {
@@ -16598,23 +16690,24 @@ var ProctoringUploader = class {
16598
16690
  }
16599
16691
  },
16600
16692
  token
16601
- ).catch(
16602
- async (e3) => {
16603
- console.log("Upload File Error", e3), trackers.registerError(this.proctoringId, `Upload File
16693
+ ).catch(async (e3) => {
16694
+ console.log("Upload File Error", e3), trackers.registerError(
16695
+ this.proctoringId,
16696
+ `Upload File
16604
16697
  Name: ${file.name}
16605
16698
  Error: ${e3.message}
16606
- Size: ${e3.error}`);
16607
- await uploadService.upload(
16608
- {
16609
- file,
16610
- onProgress: (progress) => {
16611
- if (onProgress) onProgress(progress);
16612
- }
16613
- },
16614
- token
16615
- );
16616
- }
16617
- );
16699
+ Size: ${e3.error}`
16700
+ );
16701
+ await uploadService.upload(
16702
+ {
16703
+ file,
16704
+ onProgress: (progress) => {
16705
+ if (onProgress) onProgress(progress);
16706
+ }
16707
+ },
16708
+ token
16709
+ );
16710
+ });
16618
16711
  if (result) {
16619
16712
  let fileType = "";
16620
16713
  if (rec.origin === "Camera" /* Camera */) {
@@ -19436,10 +19529,15 @@ var ScreenRecorder = class {
19436
19529
  const rawBlob = new Blob(this.blobs, {
19437
19530
  type: "video/webm"
19438
19531
  });
19439
- const fixedBlob = await fixWebmDuration2(rawBlob, this.duration);
19532
+ let finalBlob = rawBlob;
19533
+ if (typeof fixWebmDuration2 === "function") {
19534
+ finalBlob = await fixWebmDuration2(rawBlob, this.duration);
19535
+ } else {
19536
+ console.warn("fixWebmDuration n\xE3o dispon\xEDvel");
19537
+ }
19440
19538
  session.addRecording({
19441
19539
  device: "",
19442
- arrayBuffer: await fixedBlob.arrayBuffer(),
19540
+ arrayBuffer: await finalBlob.arrayBuffer(),
19443
19541
  origin: "Screen" /* Screen */
19444
19542
  });
19445
19543
  }
@@ -23571,7 +23669,10 @@ var Proctoring = class {
23571
23669
  if (this.context.token === void 0) {
23572
23670
  throw TOKEN_MISSING;
23573
23671
  }
23574
- this.extension = new Extension();
23672
+ if (options.useChallenge) {
23673
+ this.extensionEasycatcher = new ExtensionEasyCatcher();
23674
+ }
23675
+ this.extension = new ExtensionEasyProctor();
23575
23676
  this.extension.addEventListener();
23576
23677
  const baseURL = this.backend.selectBaseUrl(this.context.type);
23577
23678
  const devices = await enumarateDevices();
@@ -23639,20 +23740,6 @@ var Proctoring = class {
23639
23740
  } catch (error) {
23640
23741
  throw SAFE_BROWSER_API_NOT_FOUND;
23641
23742
  }
23642
- this.allRecorders.cameraRecorder.onVisibilityRestored = () => {
23643
- console.log("[Proctoring] Usu\xE1rio retornou ao browser.");
23644
- this.onVisibilityRestoredCallback();
23645
- };
23646
- if (this.sessionOptions.proctoringType === "REALTIME" && !isSafeBrowser()) {
23647
- try {
23648
- await BackgroundUploadService.recoverPendingUploads(
23649
- this.backend,
23650
- this.context.token
23651
- );
23652
- } catch (e3) {
23653
- console.warn("[Proctoring] Erro ao recuperar chunks de sess\xE3o anterior:", e3);
23654
- }
23655
- }
23656
23743
  try {
23657
23744
  console.log("Starting recorders");
23658
23745
  await this.recorder.startAll();
@@ -23733,7 +23820,9 @@ Error: ${error}`
23733
23820
  this.appChecker && await this.appChecker.disconnectWebSocket();
23734
23821
  await this.recorder.saveAllOnSession();
23735
23822
  await this.sendPendingRealtimeAlerts();
23823
+ trackers.registerError(this.proctoringId, `finish this.repository.save starting`);
23736
23824
  await this.repository.save(this.proctoringSession);
23825
+ trackers.registerError(this.proctoringId, `finish this.repository.save finished`);
23737
23826
  let uploader;
23738
23827
  let uploaderServices;
23739
23828
  if (versionVerify() !== "1.0.0.0") {
@@ -23955,6 +24044,61 @@ Error: ` + error
23955
24044
  _screenStream: (_a2 = this.allRecorders.screenRecorder) == null ? void 0 : _a2.screenStream
23956
24045
  };
23957
24046
  }
24047
+ async startChallenge(templateId) {
24048
+ var _a2;
24049
+ if (!this.sessionOptions.useChallenge) {
24050
+ throw new Error("useChallenge is set as false on start method");
24051
+ }
24052
+ await this.extensionEasycatcher.checkExtensionInstalled().catch((err) => {
24053
+ throw new Error("EasyCatcher Extension is not installed");
24054
+ });
24055
+ this.extensionEasycatcher.start();
24056
+ const start = Date.now() - ((_a2 = this.allRecorders.cameraRecorder.getStartTime()) == null ? void 0 : _a2.getTime()) || 0;
24057
+ await this.backend.startChallenge({
24058
+ proctoringId: this.proctoringId,
24059
+ templateId,
24060
+ start
24061
+ }).then((resp) => {
24062
+ console.log(resp);
24063
+ this.challengeId = resp.id;
24064
+ }).catch((reason) => {
24065
+ trackers.registerError(
24066
+ this.proctoringId,
24067
+ "N\xE3o foi poss\xEDvel iniciar desafio!"
24068
+ );
24069
+ throw reason;
24070
+ });
24071
+ this.isChallengeRunning = true;
24072
+ }
24073
+ async stopChallenge() {
24074
+ var _a2;
24075
+ if (!this.isChallengeRunning) {
24076
+ throw new Error("Challenge not started");
24077
+ }
24078
+ try {
24079
+ const sessionData = await this.extensionEasycatcher.getSessionData();
24080
+ const end = Date.now() - ((_a2 = this.allRecorders.cameraRecorder.getStartTime()) == null ? void 0 : _a2.getTime()) || 0;
24081
+ await this.backend.stopChallenge(
24082
+ this.challengeId,
24083
+ {
24084
+ end,
24085
+ data: sessionData
24086
+ }
24087
+ ).catch((reason) => {
24088
+ trackers.registerError(
24089
+ this.proctoringId,
24090
+ "N\xE3o foi poss\xEDvel finalizar o desafio no backend!"
24091
+ );
24092
+ return void 0;
24093
+ });
24094
+ this.isChallengeRunning = false;
24095
+ } catch (error) {
24096
+ trackers.registerError(
24097
+ this.proctoringId,
24098
+ "Erro ao recuperar dados da extens\xE3o: " + error.message
24099
+ );
24100
+ }
24101
+ }
23958
24102
  };
23959
24103
 
23960
24104
  // src/proctoring/SignTerm.ts
@@ -24183,6 +24327,8 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
24183
24327
  return originalStart(parameters2, videoOptions);
24184
24328
  };
24185
24329
  const finish = proctoring.finish.bind(proctoring);
24330
+ const startChallenge = proctoring.startChallenge.bind(proctoring);
24331
+ const stopChallenge = proctoring.stopChallenge.bind(proctoring);
24186
24332
  const pause = proctoring.pause.bind(proctoring);
24187
24333
  const resume = proctoring.resume.bind(proctoring);
24188
24334
  const onFocus = proctoring.setOnFocusCallback.bind(proctoring);
@@ -24207,6 +24353,8 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
24207
24353
  login,
24208
24354
  start,
24209
24355
  finish,
24356
+ startChallenge,
24357
+ stopChallenge,
24210
24358
  onFocus,
24211
24359
  onLostFocus,
24212
24360
  onChangeDevices: onChangeDevices2,