easyproctor 2.5.3 → 2.5.5

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/README.md CHANGED
@@ -263,6 +263,17 @@ const {
263
263
  token: "...",
264
264
  });
265
265
  ```
266
+
267
+ ## Release Note V 2.5.4
268
+ - Fix: Correção sobre as imagens do realtime
269
+
270
+ ## Release Note V 2.5.4
271
+ - Novos alertas de tela (Clipboard, SplitScreen)
272
+ - Ajustes na detecção de foco da tela
273
+ - Melhorias na analise biométrica
274
+ - Melhorias na analise do uso de dispositivos
275
+ - Correção do uso de câmera externa no ambiente de produção
276
+
266
277
  ## Release Note V 2.5.3
267
278
  - Fix: Resolução do video
268
279
  - Melhorias no proctoring do tipo REALTIME
package/esm/index.js CHANGED
@@ -12777,13 +12777,21 @@ var CameraRecorder = class {
12777
12777
  }
12778
12778
  }
12779
12779
  configImageCapture() {
12780
+ var _a2;
12780
12781
  this.video = document.createElement("video");
12781
12782
  this.canvas = document.createElement("canvas");
12782
12783
  this.video.srcObject = this.cameraStream;
12783
12784
  this.video.play();
12784
12785
  this.video.muted = true;
12785
- this.canvas.width = this.videoOptions.width;
12786
- this.canvas.height = this.videoOptions.height;
12786
+ const isPortrait = (_a2 = screen.orientation) == null ? void 0 : _a2.type.includes("portrait");
12787
+ if (isPortrait && isMobileDevice()) {
12788
+ console.log("configurando canvas em portrait");
12789
+ this.canvas.width = this.videoOptions.height / 2;
12790
+ this.canvas.height = this.videoOptions.width / 2;
12791
+ } else {
12792
+ this.canvas.width = this.videoOptions.width / 2;
12793
+ this.canvas.height = this.videoOptions.height / 2;
12794
+ }
12787
12795
  }
12788
12796
  async bufferError(e3) {
12789
12797
  var _a2, _b;
@@ -12802,7 +12810,7 @@ var CameraRecorder = class {
12802
12810
  }
12803
12811
  }
12804
12812
  async startRecording(options) {
12805
- var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
12813
+ var _a2, _b, _c2, _d, _e3, _f, _g, _h;
12806
12814
  if ((((_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.detectPerson) || ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.detectCellPhone) || ((_c2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _c2.detectFace)) && !(options == null ? void 0 : options.retry)) {
12807
12815
  await this.initializeDetectors();
12808
12816
  }
@@ -12882,7 +12890,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
12882
12890
  this.filesToUpload = [];
12883
12891
  if (this.options.proctoringType == "REALTIME") {
12884
12892
  this.captureFrame();
12885
- this.sendFrameInterval = setInterval(() => this.captureFrame(), 1e3 * ((_i3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _i3.realtimeUploadInterval));
12886
12893
  }
12887
12894
  this.packageCount = 0;
12888
12895
  }
@@ -12913,7 +12920,8 @@ Setting: ${JSON.stringify(settings, null, 2)}`
12913
12920
  clearInterval(this.imageInterval);
12914
12921
  clearInterval(this.sendFrameInterval);
12915
12922
  if (this.options.proctoringType == "REALTIME" && this.upload && this.backendToken) {
12916
- await this.sendPackage();
12923
+ await this.sendPackage(this.filesToUpload);
12924
+ await this.filesToUpload.splice(0, this.filesToUpload.length);
12917
12925
  }
12918
12926
  this.volumeMeter && this.volumeMeter.stop();
12919
12927
  this.intervalNoiseDetection && clearInterval(this.intervalNoiseDetection);
@@ -12983,23 +12991,22 @@ Setting: ${JSON.stringify(settings, null, 2)}`
12983
12991
  this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
12984
12992
  return this.canvas.toDataURL("image/jpeg");
12985
12993
  }
12986
- // De um em um segundo captura um frame ligado de 30 em 30 segundos)
12994
+ // captura um frame a cada intervalo de tempo definido no paramsConfig.videoBehaviourParameters?.realtimeCaptureInterval!
12987
12995
  captureFrame() {
12988
- var _a2;
12996
+ var _a2, _b;
12989
12997
  let imageFile;
12990
12998
  this.configImageCapture();
12991
12999
  const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
12992
- let initSend = false;
12993
- this.imageCount++;
13000
+ this.imageCount = 0;
12994
13001
  this.imageInterval = setInterval(async () => {
12995
13002
  this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
12996
13003
  const image_data_url = this.canvas.toDataURL("image/jpeg");
12997
13004
  if (this.proctoringId == void 0) return;
12998
- if (initSend == false && packSize == this.imageCount) {
12999
- initSend = true;
13005
+ if (packSize == this.imageCount) {
13000
13006
  this.imageCount = 0;
13001
- clearInterval(this.imageInterval);
13002
- this.sendPackage();
13007
+ const framesToSend = [...this.filesToUpload];
13008
+ this.sendPackage(framesToSend);
13009
+ await this.filesToUpload.splice(0, this.filesToUpload.length);
13003
13010
  }
13004
13011
  let imageName = `${this.proctoringId}_${this.imageCount + 1}.jpg`;
13005
13012
  imageFile = await this.getFile(image_data_url, imageName, "image/jpeg");
@@ -13007,22 +13014,24 @@ Setting: ${JSON.stringify(settings, null, 2)}`
13007
13014
  this.filesToUpload.push(imageFile);
13008
13015
  this.imageCount++;
13009
13016
  }
13010
- }, 1e3);
13017
+ }, ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) * 1e3);
13011
13018
  }
13012
13019
  // envia pacote de imagens
13013
- async sendPackage() {
13020
+ async sendPackage(framesToSend) {
13021
+ var _a2, _b;
13014
13022
  let pending = false;
13015
13023
  let undeliveredPackagesCount = 0;
13024
+ const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
13025
+ const packCaptureInterval = ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) + 1;
13016
13026
  if (this.upload && this.backendToken && !pending && this.filesToUpload.length > 0) {
13017
13027
  undeliveredPackagesCount = 0;
13018
13028
  pending = true;
13019
13029
  const zip = new import_jszip_min.default();
13020
- const files = this.filesToUpload;
13021
- for (const file of files) {
13030
+ for (const file of framesToSend) {
13022
13031
  zip.file(file.name, file);
13023
13032
  }
13024
13033
  const blob = await zip.generateAsync({ type: "blob" });
13025
- let packageName = "realtime_package_" + 30 * this.packageCount + ".zip";
13034
+ let packageName = "realtime_package_" + packSize * packCaptureInterval * this.packageCount + ".zip";
13026
13035
  const myPackage = new File(
13027
13036
  [blob],
13028
13037
  packageName,
@@ -13035,7 +13044,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
13035
13044
  this.backendToken
13036
13045
  );
13037
13046
  if (uploadResult.uploaded == true) {
13038
- await this.filesToUpload.splice(0, this.filesToUpload.length);
13039
13047
  this.packageCount++;
13040
13048
  } else {
13041
13049
  console.log("erro no upload do pacote");
@@ -13122,45 +13130,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
13122
13130
  }
13123
13131
  this.noiseWait++;
13124
13132
  }
13125
- /**
13126
- * Cria um stream processado onde os frames são rotacionados via Canvas.
13127
- * Isso corrige o problema de gravação deitada em iOS/Mobile.
13128
- */
13129
- createRotatedStream(originalStream) {
13130
- this.internalClonedStream = originalStream.clone();
13131
- const video = document.createElement("video");
13132
- video.srcObject = this.internalClonedStream;
13133
- video.muted = true;
13134
- video.play();
13135
- const canvas = document.createElement("canvas");
13136
- const ctx = canvas.getContext("2d");
13137
- const track = originalStream.getVideoTracks()[0];
13138
- const settings = track.getSettings();
13139
- const width = settings.width || 640;
13140
- const height = settings.height || 480;
13141
- canvas.width = height;
13142
- canvas.height = width;
13143
- const draw = () => {
13144
- if (video.paused || video.ended) return;
13145
- if (!this.isCanvasLoopActive) return;
13146
- if (ctx) {
13147
- ctx.clearRect(0, 0, canvas.width, canvas.height);
13148
- ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
13149
- }
13150
- if (this.isCanvasLoopActive) {
13151
- this.animationFrameId = requestAnimationFrame(draw);
13152
- }
13153
- };
13154
- video.onplaying = () => {
13155
- this.isCanvasLoopActive = true;
13156
- draw();
13157
- };
13158
- const canvasStream = canvas.captureStream(30);
13159
- originalStream.getAudioTracks().forEach((track2) => {
13160
- canvasStream.addTrack(track2);
13161
- });
13162
- return canvasStream;
13163
- }
13164
13133
  };
13165
13134
 
13166
13135
  // src/new-flow/checkers/DeviceCheckerUI.ts
@@ -15110,6 +15079,13 @@ var AlertRecorder = class {
15110
15079
  // HANDLERS (Funções Arrow para preservar o 'this')
15111
15080
  // ==========================================
15112
15081
  // 1. LOST / RETURN FOCUS
15082
+ this.handleVisibilityChange = () => {
15083
+ if (document.visibilityState === "visible") {
15084
+ this.handleReturnFocus();
15085
+ } else {
15086
+ this.handleLostFocus();
15087
+ }
15088
+ };
15113
15089
  this.handleLostFocus = () => {
15114
15090
  if (this.getRelativeTime() > 1e4) {
15115
15091
  const alertPayload = {
@@ -15142,7 +15118,7 @@ var AlertRecorder = class {
15142
15118
  const windowWidth = window.outerWidth;
15143
15119
  if (windowWidth < screenWidth * 0.85) {
15144
15120
  const msg = `Split Screen Detectado: Janela ocupa ${(windowWidth / screenWidth * 100).toFixed(0)}% da tela`;
15145
- this.createAlert(200 /* System */, 3 /* Screen */, msg);
15121
+ this.createAlert(43 /* SplitScreen */, 3 /* Screen */, msg);
15146
15122
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15147
15123
  status: "ALERT",
15148
15124
  description: msg,
@@ -15160,7 +15136,7 @@ var AlertRecorder = class {
15160
15136
  this.handleCopy = (e3) => {
15161
15137
  e3.preventDefault();
15162
15138
  const msg = "Tentativa de Copiar (Clipboard)";
15163
- this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
15139
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
15164
15140
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15165
15141
  status: "ALERT",
15166
15142
  description: msg,
@@ -15170,7 +15146,7 @@ var AlertRecorder = class {
15170
15146
  this.handleCut = (e3) => {
15171
15147
  e3.preventDefault();
15172
15148
  const msg = "Tentativa de Recortar (Clipboard)";
15173
- this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
15149
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
15174
15150
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15175
15151
  status: "ALERT",
15176
15152
  description: msg,
@@ -15180,7 +15156,7 @@ var AlertRecorder = class {
15180
15156
  this.handlePaste = (e3) => {
15181
15157
  e3.preventDefault();
15182
15158
  const msg = "Tentativa de Colar (Clipboard)";
15183
- this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
15159
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
15184
15160
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15185
15161
  status: "ALERT",
15186
15162
  description: msg,
@@ -15216,8 +15192,7 @@ var AlertRecorder = class {
15216
15192
  // ==========================================
15217
15193
  attachListeners() {
15218
15194
  if (this.optionsProctoring.captureScreen) {
15219
- window.addEventListener("blur", this.handleLostFocus);
15220
- window.addEventListener("focus", this.handleReturnFocus);
15195
+ window.addEventListener("visibilitychange", this.handleVisibilityChange);
15221
15196
  window.addEventListener("resize", this.handleResize);
15222
15197
  window.document.addEventListener("copy", this.handleCopy);
15223
15198
  window.document.addEventListener("cut", this.handleCut);
@@ -15225,8 +15200,7 @@ var AlertRecorder = class {
15225
15200
  }
15226
15201
  }
15227
15202
  detachListeners() {
15228
- window.removeEventListener("blur", this.handleLostFocus);
15229
- window.removeEventListener("focus", this.handleReturnFocus);
15203
+ window.removeEventListener("visibilitychange", this.handleVisibilityChange);
15230
15204
  window.removeEventListener("resize", this.handleResize);
15231
15205
  window.document.removeEventListener("copy", this.handleCopy);
15232
15206
  window.document.removeEventListener("cut", this.handleCut);
@@ -15261,7 +15235,7 @@ var AlertRecorder = class {
15261
15235
  // }
15262
15236
  // 4. BACKGROUND EVENTS (Eventos disparados manualmente)
15263
15237
  addBackgroundEvent(description, category = 200 /* System */) {
15264
- this.createAlert(category, 6 /* System */, description);
15238
+ this.createAlert(category, 3 /* Screen */, description);
15265
15239
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
15266
15240
  status: "ALERT",
15267
15241
  description,
@@ -21499,7 +21473,8 @@ var _ExternalCameraChecker = class _ExternalCameraChecker {
21499
21473
  this.connection = new HubConnectionBuilder().withUrl(hubUrl, {
21500
21474
  accessTokenFactory: () => this.context.token,
21501
21475
  transport: HttpTransportType.WebSockets,
21502
- withCredentials: false
21476
+ withCredentials: false,
21477
+ skipNegotiation: true
21503
21478
  }).withAutomaticReconnect().configureLogging(LogLevel.Information).build();
21504
21479
  this.connection.on(
21505
21480
  "ReceiveMessage",
@@ -21715,8 +21690,8 @@ var Proctoring = class {
21715
21690
  detectCellPhone: false,
21716
21691
  detectNoise: false,
21717
21692
  detectSpeech: false,
21718
- realtimePackageSize: 20,
21719
- realtimeUploadInterval: 30
21693
+ realtimePackageSize: 10,
21694
+ realtimeCaptureInterval: 2
21720
21695
  }
21721
21696
  };
21722
21697
  this.proctoringId = "";
@@ -21852,6 +21827,29 @@ var Proctoring = class {
21852
21827
  return null;
21853
21828
  }
21854
21829
  }
21830
+ // metodo para fechar o alerta realtime e fica tentando verificar a face até 5 vezes
21831
+ async stopRealtimeAlert(alert) {
21832
+ const verifyMaxRetries = 3;
21833
+ const verifyFace = async (verifyCount) => {
21834
+ if (verifyCount > verifyMaxRetries) return;
21835
+ try {
21836
+ var response = await this.backend.stopRealtimeAlert({
21837
+ proctoringId: this.proctoringId,
21838
+ begin: alert.begin,
21839
+ end: alert.end,
21840
+ warningCategoryEnum: this.convertRealtimeTypeToWarningType(alert.type),
21841
+ alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64(),
21842
+ retry: verifyCount < verifyMaxRetries - 1 ? true : false
21843
+ });
21844
+ console.log("response stopRealtimeAlert", response);
21845
+ return response;
21846
+ } catch (error) {
21847
+ console.log("error stopRealtimeAlert", error);
21848
+ return verifyFace(verifyCount + 1);
21849
+ }
21850
+ };
21851
+ await verifyFace(1);
21852
+ }
21855
21853
  async onRealtimeAlerts(options = {}) {
21856
21854
  this.setOnLostFocusAlertRecorderCallback();
21857
21855
  this.setOnFocusAlertRecorderCallback();
@@ -21866,13 +21864,7 @@ var Proctoring = class {
21866
21864
  alert: this.convertRealtimeCategoryToAlertCategory(response.category)
21867
21865
  });
21868
21866
  } else if (response.status === "OK") {
21869
- await this.backend.stopRealtimeAlert({
21870
- proctoringId: this.proctoringId,
21871
- begin: response.begin,
21872
- end: response.end,
21873
- warningCategoryEnum: this.convertRealtimeTypeToWarningType(response.type),
21874
- alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64()
21875
- });
21867
+ await this.stopRealtimeAlert(response);
21876
21868
  }
21877
21869
  }
21878
21870
  };
package/index.js CHANGED
@@ -30874,13 +30874,21 @@ var CameraRecorder = class {
30874
30874
  }
30875
30875
  }
30876
30876
  configImageCapture() {
30877
+ var _a2;
30877
30878
  this.video = document.createElement("video");
30878
30879
  this.canvas = document.createElement("canvas");
30879
30880
  this.video.srcObject = this.cameraStream;
30880
30881
  this.video.play();
30881
30882
  this.video.muted = true;
30882
- this.canvas.width = this.videoOptions.width;
30883
- this.canvas.height = this.videoOptions.height;
30883
+ const isPortrait = (_a2 = screen.orientation) == null ? void 0 : _a2.type.includes("portrait");
30884
+ if (isPortrait && isMobileDevice()) {
30885
+ console.log("configurando canvas em portrait");
30886
+ this.canvas.width = this.videoOptions.height / 2;
30887
+ this.canvas.height = this.videoOptions.width / 2;
30888
+ } else {
30889
+ this.canvas.width = this.videoOptions.width / 2;
30890
+ this.canvas.height = this.videoOptions.height / 2;
30891
+ }
30884
30892
  }
30885
30893
  async bufferError(e3) {
30886
30894
  var _a2, _b;
@@ -30899,7 +30907,7 @@ var CameraRecorder = class {
30899
30907
  }
30900
30908
  }
30901
30909
  async startRecording(options) {
30902
- var _a2, _b, _c2, _d, _e3, _f, _g, _h, _i3;
30910
+ var _a2, _b, _c2, _d, _e3, _f, _g, _h;
30903
30911
  if ((((_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.detectPerson) || ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.detectCellPhone) || ((_c2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _c2.detectFace)) && !(options == null ? void 0 : options.retry)) {
30904
30912
  await this.initializeDetectors();
30905
30913
  }
@@ -30979,7 +30987,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
30979
30987
  this.filesToUpload = [];
30980
30988
  if (this.options.proctoringType == "REALTIME") {
30981
30989
  this.captureFrame();
30982
- this.sendFrameInterval = setInterval(() => this.captureFrame(), 1e3 * ((_i3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _i3.realtimeUploadInterval));
30983
30990
  }
30984
30991
  this.packageCount = 0;
30985
30992
  }
@@ -31010,7 +31017,8 @@ Setting: ${JSON.stringify(settings, null, 2)}`
31010
31017
  clearInterval(this.imageInterval);
31011
31018
  clearInterval(this.sendFrameInterval);
31012
31019
  if (this.options.proctoringType == "REALTIME" && this.upload && this.backendToken) {
31013
- await this.sendPackage();
31020
+ await this.sendPackage(this.filesToUpload);
31021
+ await this.filesToUpload.splice(0, this.filesToUpload.length);
31014
31022
  }
31015
31023
  this.volumeMeter && this.volumeMeter.stop();
31016
31024
  this.intervalNoiseDetection && clearInterval(this.intervalNoiseDetection);
@@ -31080,23 +31088,22 @@ Setting: ${JSON.stringify(settings, null, 2)}`
31080
31088
  this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
31081
31089
  return this.canvas.toDataURL("image/jpeg");
31082
31090
  }
31083
- // De um em um segundo captura um frame ligado de 30 em 30 segundos)
31091
+ // captura um frame a cada intervalo de tempo definido no paramsConfig.videoBehaviourParameters?.realtimeCaptureInterval!
31084
31092
  captureFrame() {
31085
- var _a2;
31093
+ var _a2, _b;
31086
31094
  let imageFile;
31087
31095
  this.configImageCapture();
31088
31096
  const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
31089
- let initSend = false;
31090
- this.imageCount++;
31097
+ this.imageCount = 0;
31091
31098
  this.imageInterval = setInterval(async () => {
31092
31099
  this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
31093
31100
  const image_data_url = this.canvas.toDataURL("image/jpeg");
31094
31101
  if (this.proctoringId == void 0) return;
31095
- if (initSend == false && packSize == this.imageCount) {
31096
- initSend = true;
31102
+ if (packSize == this.imageCount) {
31097
31103
  this.imageCount = 0;
31098
- clearInterval(this.imageInterval);
31099
- this.sendPackage();
31104
+ const framesToSend = [...this.filesToUpload];
31105
+ this.sendPackage(framesToSend);
31106
+ await this.filesToUpload.splice(0, this.filesToUpload.length);
31100
31107
  }
31101
31108
  let imageName = `${this.proctoringId}_${this.imageCount + 1}.jpg`;
31102
31109
  imageFile = await this.getFile(image_data_url, imageName, "image/jpeg");
@@ -31104,22 +31111,24 @@ Setting: ${JSON.stringify(settings, null, 2)}`
31104
31111
  this.filesToUpload.push(imageFile);
31105
31112
  this.imageCount++;
31106
31113
  }
31107
- }, 1e3);
31114
+ }, ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) * 1e3);
31108
31115
  }
31109
31116
  // envia pacote de imagens
31110
- async sendPackage() {
31117
+ async sendPackage(framesToSend) {
31118
+ var _a2, _b;
31111
31119
  let pending = false;
31112
31120
  let undeliveredPackagesCount = 0;
31121
+ const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
31122
+ const packCaptureInterval = ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) + 1;
31113
31123
  if (this.upload && this.backendToken && !pending && this.filesToUpload.length > 0) {
31114
31124
  undeliveredPackagesCount = 0;
31115
31125
  pending = true;
31116
31126
  const zip = new import_jszip_min.default();
31117
- const files = this.filesToUpload;
31118
- for (const file of files) {
31127
+ for (const file of framesToSend) {
31119
31128
  zip.file(file.name, file);
31120
31129
  }
31121
31130
  const blob = await zip.generateAsync({ type: "blob" });
31122
- let packageName = "realtime_package_" + 30 * this.packageCount + ".zip";
31131
+ let packageName = "realtime_package_" + packSize * packCaptureInterval * this.packageCount + ".zip";
31123
31132
  const myPackage = new File(
31124
31133
  [blob],
31125
31134
  packageName,
@@ -31132,7 +31141,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
31132
31141
  this.backendToken
31133
31142
  );
31134
31143
  if (uploadResult.uploaded == true) {
31135
- await this.filesToUpload.splice(0, this.filesToUpload.length);
31136
31144
  this.packageCount++;
31137
31145
  } else {
31138
31146
  console.log("erro no upload do pacote");
@@ -31219,45 +31227,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
31219
31227
  }
31220
31228
  this.noiseWait++;
31221
31229
  }
31222
- /**
31223
- * Cria um stream processado onde os frames são rotacionados via Canvas.
31224
- * Isso corrige o problema de gravação deitada em iOS/Mobile.
31225
- */
31226
- createRotatedStream(originalStream) {
31227
- this.internalClonedStream = originalStream.clone();
31228
- const video = document.createElement("video");
31229
- video.srcObject = this.internalClonedStream;
31230
- video.muted = true;
31231
- video.play();
31232
- const canvas = document.createElement("canvas");
31233
- const ctx = canvas.getContext("2d");
31234
- const track = originalStream.getVideoTracks()[0];
31235
- const settings = track.getSettings();
31236
- const width = settings.width || 640;
31237
- const height = settings.height || 480;
31238
- canvas.width = height;
31239
- canvas.height = width;
31240
- const draw = () => {
31241
- if (video.paused || video.ended) return;
31242
- if (!this.isCanvasLoopActive) return;
31243
- if (ctx) {
31244
- ctx.clearRect(0, 0, canvas.width, canvas.height);
31245
- ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
31246
- }
31247
- if (this.isCanvasLoopActive) {
31248
- this.animationFrameId = requestAnimationFrame(draw);
31249
- }
31250
- };
31251
- video.onplaying = () => {
31252
- this.isCanvasLoopActive = true;
31253
- draw();
31254
- };
31255
- const canvasStream = canvas.captureStream(30);
31256
- originalStream.getAudioTracks().forEach((track2) => {
31257
- canvasStream.addTrack(track2);
31258
- });
31259
- return canvasStream;
31260
- }
31261
31230
  };
31262
31231
 
31263
31232
  // src/new-flow/checkers/DeviceCheckerUI.ts
@@ -33207,6 +33176,13 @@ var AlertRecorder = class {
33207
33176
  // HANDLERS (Funções Arrow para preservar o 'this')
33208
33177
  // ==========================================
33209
33178
  // 1. LOST / RETURN FOCUS
33179
+ this.handleVisibilityChange = () => {
33180
+ if (document.visibilityState === "visible") {
33181
+ this.handleReturnFocus();
33182
+ } else {
33183
+ this.handleLostFocus();
33184
+ }
33185
+ };
33210
33186
  this.handleLostFocus = () => {
33211
33187
  if (this.getRelativeTime() > 1e4) {
33212
33188
  const alertPayload = {
@@ -33239,7 +33215,7 @@ var AlertRecorder = class {
33239
33215
  const windowWidth = window.outerWidth;
33240
33216
  if (windowWidth < screenWidth * 0.85) {
33241
33217
  const msg = `Split Screen Detectado: Janela ocupa ${(windowWidth / screenWidth * 100).toFixed(0)}% da tela`;
33242
- this.createAlert(200 /* System */, 3 /* Screen */, msg);
33218
+ this.createAlert(43 /* SplitScreen */, 3 /* Screen */, msg);
33243
33219
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33244
33220
  status: "ALERT",
33245
33221
  description: msg,
@@ -33257,7 +33233,7 @@ var AlertRecorder = class {
33257
33233
  this.handleCopy = (e3) => {
33258
33234
  e3.preventDefault();
33259
33235
  const msg = "Tentativa de Copiar (Clipboard)";
33260
- this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
33236
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
33261
33237
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33262
33238
  status: "ALERT",
33263
33239
  description: msg,
@@ -33267,7 +33243,7 @@ var AlertRecorder = class {
33267
33243
  this.handleCut = (e3) => {
33268
33244
  e3.preventDefault();
33269
33245
  const msg = "Tentativa de Recortar (Clipboard)";
33270
- this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
33246
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
33271
33247
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33272
33248
  status: "ALERT",
33273
33249
  description: msg,
@@ -33277,7 +33253,7 @@ var AlertRecorder = class {
33277
33253
  this.handlePaste = (e3) => {
33278
33254
  e3.preventDefault();
33279
33255
  const msg = "Tentativa de Colar (Clipboard)";
33280
- this.createAlert(100 /* ForbiddenAction */, 6 /* System */, msg);
33256
+ this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
33281
33257
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33282
33258
  status: "ALERT",
33283
33259
  description: msg,
@@ -33313,8 +33289,7 @@ var AlertRecorder = class {
33313
33289
  // ==========================================
33314
33290
  attachListeners() {
33315
33291
  if (this.optionsProctoring.captureScreen) {
33316
- window.addEventListener("blur", this.handleLostFocus);
33317
- window.addEventListener("focus", this.handleReturnFocus);
33292
+ window.addEventListener("visibilitychange", this.handleVisibilityChange);
33318
33293
  window.addEventListener("resize", this.handleResize);
33319
33294
  window.document.addEventListener("copy", this.handleCopy);
33320
33295
  window.document.addEventListener("cut", this.handleCut);
@@ -33322,8 +33297,7 @@ var AlertRecorder = class {
33322
33297
  }
33323
33298
  }
33324
33299
  detachListeners() {
33325
- window.removeEventListener("blur", this.handleLostFocus);
33326
- window.removeEventListener("focus", this.handleReturnFocus);
33300
+ window.removeEventListener("visibilitychange", this.handleVisibilityChange);
33327
33301
  window.removeEventListener("resize", this.handleResize);
33328
33302
  window.document.removeEventListener("copy", this.handleCopy);
33329
33303
  window.document.removeEventListener("cut", this.handleCut);
@@ -33358,7 +33332,7 @@ var AlertRecorder = class {
33358
33332
  // }
33359
33333
  // 4. BACKGROUND EVENTS (Eventos disparados manualmente)
33360
33334
  addBackgroundEvent(description, category = 200 /* System */) {
33361
- this.createAlert(category, 6 /* System */, description);
33335
+ this.createAlert(category, 3 /* Screen */, description);
33362
33336
  this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
33363
33337
  status: "ALERT",
33364
33338
  description,
@@ -36748,7 +36722,8 @@ var _ExternalCameraChecker = class _ExternalCameraChecker {
36748
36722
  this.connection = new import_signalr.HubConnectionBuilder().withUrl(hubUrl, {
36749
36723
  accessTokenFactory: () => this.context.token,
36750
36724
  transport: import_signalr.HttpTransportType.WebSockets,
36751
- withCredentials: false
36725
+ withCredentials: false,
36726
+ skipNegotiation: true
36752
36727
  }).withAutomaticReconnect().configureLogging(import_signalr.LogLevel.Information).build();
36753
36728
  this.connection.on(
36754
36729
  "ReceiveMessage",
@@ -36964,8 +36939,8 @@ var Proctoring = class {
36964
36939
  detectCellPhone: false,
36965
36940
  detectNoise: false,
36966
36941
  detectSpeech: false,
36967
- realtimePackageSize: 20,
36968
- realtimeUploadInterval: 30
36942
+ realtimePackageSize: 10,
36943
+ realtimeCaptureInterval: 2
36969
36944
  }
36970
36945
  };
36971
36946
  this.proctoringId = "";
@@ -37101,6 +37076,29 @@ var Proctoring = class {
37101
37076
  return null;
37102
37077
  }
37103
37078
  }
37079
+ // metodo para fechar o alerta realtime e fica tentando verificar a face até 5 vezes
37080
+ async stopRealtimeAlert(alert) {
37081
+ const verifyMaxRetries = 3;
37082
+ const verifyFace = async (verifyCount) => {
37083
+ if (verifyCount > verifyMaxRetries) return;
37084
+ try {
37085
+ var response = await this.backend.stopRealtimeAlert({
37086
+ proctoringId: this.proctoringId,
37087
+ begin: alert.begin,
37088
+ end: alert.end,
37089
+ warningCategoryEnum: this.convertRealtimeTypeToWarningType(alert.type),
37090
+ alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64(),
37091
+ retry: verifyCount < verifyMaxRetries - 1 ? true : false
37092
+ });
37093
+ console.log("response stopRealtimeAlert", response);
37094
+ return response;
37095
+ } catch (error) {
37096
+ console.log("error stopRealtimeAlert", error);
37097
+ return verifyFace(verifyCount + 1);
37098
+ }
37099
+ };
37100
+ await verifyFace(1);
37101
+ }
37104
37102
  async onRealtimeAlerts(options = {}) {
37105
37103
  this.setOnLostFocusAlertRecorderCallback();
37106
37104
  this.setOnFocusAlertRecorderCallback();
@@ -37115,13 +37113,7 @@ var Proctoring = class {
37115
37113
  alert: this.convertRealtimeCategoryToAlertCategory(response.category)
37116
37114
  });
37117
37115
  } else if (response.status === "OK") {
37118
- await this.backend.stopRealtimeAlert({
37119
- proctoringId: this.proctoringId,
37120
- begin: response.begin,
37121
- end: response.end,
37122
- warningCategoryEnum: this.convertRealtimeTypeToWarningType(response.type),
37123
- alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64()
37124
- });
37116
+ await this.stopRealtimeAlert(response);
37125
37117
  }
37126
37118
  }
37127
37119
  };
@@ -25,7 +25,7 @@ export type VideoBehaviourParameters = {
25
25
  retryEnabled?: boolean;
26
26
  maxRetries?: number;
27
27
  realtimePackageSize?: number;
28
- realtimeUploadInterval?: number;
28
+ realtimeCaptureInterval?: number;
29
29
  };
30
30
  export default interface IParamsConfig {
31
31
  audioBehaviourParameters?: AudioBehaviourParameters;
@@ -25,7 +25,8 @@ export declare enum AlertCategory {
25
25
  SpyDeviceDisconnected = 33,
26
26
  StopSharingScreen = 34,
27
27
  ChangeDevices = 39,
28
- ForbiddenAction = 100,
28
+ ClipBoardUse = 42,
29
+ SplitScreen = 43,
29
30
  System = 200
30
31
  }
31
32
  export declare enum FinishRealtimeWarningCategory {
@@ -38,8 +39,7 @@ export declare enum AlertType {
38
39
  Video = 2,
39
40
  Screen = 3,
40
41
  Image = 4,
41
- Environment = 5,
42
- System = 6
42
+ Environment = 5
43
43
  }
44
44
  export interface Alert {
45
45
  begin: number;
@@ -24,6 +24,7 @@ export declare class AlertRecorder implements IRecorder {
24
24
  saveOnSession(session: ProctoringSession): Promise<void>;
25
25
  private attachListeners;
26
26
  private detachListeners;
27
+ private handleVisibilityChange;
27
28
  private handleLostFocus;
28
29
  private handleReturnFocus;
29
30
  private handleResize;