easyproctor 2.5.2 → 2.5.4
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 +10 -0
- package/esm/index.js +327 -97
- package/index.js +327 -97
- package/interfaces/ParamsConfig.d.ts +1 -2
- package/new-flow/backend/BackendService.d.ts +1 -1
- package/new-flow/proctoring/ProctoringSession.d.ts +4 -1
- package/new-flow/recorders/AlertRecorder.d.ts +19 -2
- package/new-flow/recorders/CameraRecorder.d.ts +1 -1
- package/package.json +1 -1
- package/proctoring/proctoring.d.ts +2 -0
- package/proctoring/useProctoring.d.ts +1 -1
- package/unpkg/easyproctor.min.js +39 -39
package/index.js
CHANGED
|
@@ -26076,6 +26076,14 @@ var BaseDetection = class {
|
|
|
26076
26076
|
return "Face n\xE3o centralizada, mova-se para cima";
|
|
26077
26077
|
case "wrong_face_position_move_bottom_detected":
|
|
26078
26078
|
return "Face n\xE3o centralizada, mova-se para baixo";
|
|
26079
|
+
case "face_turned_left_detected":
|
|
26080
|
+
return "Face virada para a esquerda, centralize-a";
|
|
26081
|
+
case "face_turned_right_detected":
|
|
26082
|
+
return "Face virada para a direita, centralize-a";
|
|
26083
|
+
case "face_turned_up_detected":
|
|
26084
|
+
return "Face virada para cima, centralize-a";
|
|
26085
|
+
case "face_turned_down_detected":
|
|
26086
|
+
return "Face virada para baixo, centralize-a";
|
|
26079
26087
|
default:
|
|
26080
26088
|
return description;
|
|
26081
26089
|
}
|
|
@@ -26106,7 +26114,14 @@ function buildVideoPreview() {
|
|
|
26106
26114
|
// src/modules/faceDetection.ts
|
|
26107
26115
|
var FaceDetection = class extends BaseDetection {
|
|
26108
26116
|
constructor(options, paramsConfig, classVideo = "videoPreviewFrameDetection", classDiv = "liveViewFrameDetection") {
|
|
26109
|
-
super(
|
|
26117
|
+
super(
|
|
26118
|
+
"FaceDetector",
|
|
26119
|
+
`https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite`,
|
|
26120
|
+
options,
|
|
26121
|
+
paramsConfig,
|
|
26122
|
+
classVideo,
|
|
26123
|
+
classDiv
|
|
26124
|
+
);
|
|
26110
26125
|
this.emmitedPositionAlert = false;
|
|
26111
26126
|
this.emmitedFaceAlert = false;
|
|
26112
26127
|
}
|
|
@@ -26252,8 +26267,8 @@ var FaceDetection = class extends BaseDetection {
|
|
|
26252
26267
|
for (const keypoint of detection.keypoints) {
|
|
26253
26268
|
const keypointEl = document.createElement("span");
|
|
26254
26269
|
keypointEl.className = "key-point";
|
|
26255
|
-
|
|
26256
|
-
|
|
26270
|
+
const kpX = keypoint.x < 2 ? keypoint.x * videoElement.videoWidth : keypoint.x;
|
|
26271
|
+
const kpY = keypoint.y < 2 ? keypoint.y * videoElement.videoHeight : keypoint.y;
|
|
26257
26272
|
const mirroredKpX = videoElement.videoWidth - kpX;
|
|
26258
26273
|
const screenX = mirroredKpX * scaleX;
|
|
26259
26274
|
const screenY = kpY * scaleY;
|
|
@@ -26292,55 +26307,87 @@ var FaceDetection = class extends BaseDetection {
|
|
|
26292
26307
|
}
|
|
26293
26308
|
}
|
|
26294
26309
|
if (result.detections.length === 0) return;
|
|
26295
|
-
let
|
|
26296
|
-
|
|
26310
|
+
let failedFacePosition = false;
|
|
26311
|
+
const video = document.getElementById(this.classVideo);
|
|
26297
26312
|
const imageWidth = video.videoWidth;
|
|
26298
26313
|
const imageHeight = video.videoHeight;
|
|
26299
|
-
|
|
26314
|
+
const detection = result.detections[0];
|
|
26315
|
+
const face = detection.boundingBox;
|
|
26316
|
+
const keypoints = detection.keypoints;
|
|
26317
|
+
const rightEye = keypoints[0];
|
|
26318
|
+
const leftEye = keypoints[1];
|
|
26319
|
+
const mouth = keypoints[3];
|
|
26320
|
+
const rightEar = keypoints[4];
|
|
26321
|
+
const leftEar = keypoints[5];
|
|
26322
|
+
const nose = keypoints[2];
|
|
26300
26323
|
if (imageWidth > imageHeight) {
|
|
26301
26324
|
if (face.height / imageHeight > 0.7) {
|
|
26302
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
26325
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
26326
|
+
"wrong_face_size_detected",
|
|
26327
|
+
"position_detection_on_stream"
|
|
26328
|
+
);
|
|
26303
26329
|
failedFacePosition = true;
|
|
26304
26330
|
this.emmitedPositionAlert = true;
|
|
26305
26331
|
} else if (face.width / imageWidth > 0.7) {
|
|
26306
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
26332
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
26333
|
+
"wrong_face_size_detected",
|
|
26334
|
+
"position_detection_on_stream"
|
|
26335
|
+
);
|
|
26307
26336
|
this.emmitedPositionAlert = true;
|
|
26308
26337
|
failedFacePosition = true;
|
|
26309
26338
|
}
|
|
26310
26339
|
}
|
|
26311
|
-
|
|
26312
|
-
|
|
26340
|
+
const start = [face.originX, face.originY];
|
|
26341
|
+
const end = [face.originX + face.width, face.originY + face.height];
|
|
26313
26342
|
if (start[0] < 0.1 * face.width || start[1] < 0.2 * face.height || end[0] > imageWidth - 0.1 * face.width || end[1] > imageHeight - 5) {
|
|
26314
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
26343
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
26344
|
+
"wrong_face_position_edge_detected",
|
|
26345
|
+
"position_detection_on_stream"
|
|
26346
|
+
);
|
|
26315
26347
|
this.emmitedPositionAlert = true;
|
|
26316
26348
|
failedFacePosition = true;
|
|
26317
26349
|
}
|
|
26318
|
-
|
|
26319
|
-
|
|
26320
|
-
|
|
26321
|
-
|
|
26350
|
+
const leftGap = start[0];
|
|
26351
|
+
const rightGap = imageWidth - end[0];
|
|
26352
|
+
const topGap = start[1];
|
|
26353
|
+
const bottomGap = imageHeight - end[1];
|
|
26322
26354
|
if (leftGap > 2 * rightGap) {
|
|
26323
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
26355
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
26356
|
+
"wrong_face_position_move_right_detected",
|
|
26357
|
+
"position_detection_on_stream"
|
|
26358
|
+
);
|
|
26324
26359
|
this.emmitedPositionAlert = true;
|
|
26325
26360
|
failedFacePosition = true;
|
|
26326
26361
|
}
|
|
26327
26362
|
if (topGap > 4 * bottomGap) {
|
|
26328
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
26363
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
26364
|
+
"wrong_face_position_move_top_detected",
|
|
26365
|
+
"position_detection_on_stream"
|
|
26366
|
+
);
|
|
26329
26367
|
this.emmitedPositionAlert = true;
|
|
26330
26368
|
failedFacePosition = true;
|
|
26331
26369
|
}
|
|
26332
26370
|
if (rightGap > 2 * leftGap) {
|
|
26333
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
26371
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
26372
|
+
"wrong_face_position_move_left_detected",
|
|
26373
|
+
"position_detection_on_stream"
|
|
26374
|
+
);
|
|
26334
26375
|
this.emmitedPositionAlert = true;
|
|
26335
26376
|
failedFacePosition = true;
|
|
26336
26377
|
}
|
|
26337
26378
|
if (bottomGap > 3 * topGap) {
|
|
26338
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
26379
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
26380
|
+
"wrong_face_position_move_bottom_detected",
|
|
26381
|
+
"position_detection_on_stream"
|
|
26382
|
+
);
|
|
26339
26383
|
this.emmitedPositionAlert = true;
|
|
26340
26384
|
failedFacePosition = true;
|
|
26341
26385
|
}
|
|
26342
26386
|
if (failedFacePosition == false) {
|
|
26343
|
-
this.emmitedPositionAlert && this.handleOk(
|
|
26387
|
+
this.emmitedPositionAlert && this.handleOk(
|
|
26388
|
+
"ok_position_face_detected",
|
|
26389
|
+
"position_detection_on_stream"
|
|
26390
|
+
);
|
|
26344
26391
|
this.emmitedPositionAlert = false;
|
|
26345
26392
|
}
|
|
26346
26393
|
}
|
|
@@ -29869,12 +29916,12 @@ var BackendService = class {
|
|
|
29869
29916
|
});
|
|
29870
29917
|
return result.data;
|
|
29871
29918
|
}
|
|
29872
|
-
async verifyFace(proctoringId2, faceImage) {
|
|
29919
|
+
async verifyFace(proctoringId2, faceImage, retry) {
|
|
29873
29920
|
const result = await this.makeRequestAxios({
|
|
29874
29921
|
path: `/Realtime/verify-face`,
|
|
29875
29922
|
method: "POST",
|
|
29876
29923
|
jwt: this.token,
|
|
29877
|
-
body: { "proctoringId": proctoringId2, "base64": faceImage }
|
|
29924
|
+
body: { "proctoringId": proctoringId2, "base64": faceImage, "retry": retry }
|
|
29878
29925
|
});
|
|
29879
29926
|
return result.data;
|
|
29880
29927
|
}
|
|
@@ -30750,7 +30797,6 @@ var CameraRecorder = class {
|
|
|
30750
30797
|
noiseLimit: 40
|
|
30751
30798
|
},
|
|
30752
30799
|
imageBehaviourParameters: {
|
|
30753
|
-
frames: 40,
|
|
30754
30800
|
useUploadImage: true,
|
|
30755
30801
|
uploadInterval: 20,
|
|
30756
30802
|
saveVideo: true
|
|
@@ -30762,7 +30808,6 @@ var CameraRecorder = class {
|
|
|
30762
30808
|
}
|
|
30763
30809
|
};
|
|
30764
30810
|
// private imageParams: ImageParameters = {
|
|
30765
|
-
// frames: 40,
|
|
30766
30811
|
// useUploadImage: true,
|
|
30767
30812
|
// uploadInterval: 20,
|
|
30768
30813
|
// saveVideo: true
|
|
@@ -30854,7 +30899,7 @@ var CameraRecorder = class {
|
|
|
30854
30899
|
}
|
|
30855
30900
|
}
|
|
30856
30901
|
async startRecording(options) {
|
|
30857
|
-
var _a2, _b, _c2, _d, _e3, _f, _g, _h
|
|
30902
|
+
var _a2, _b, _c2, _d, _e3, _f, _g, _h;
|
|
30858
30903
|
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)) {
|
|
30859
30904
|
await this.initializeDetectors();
|
|
30860
30905
|
}
|
|
@@ -30934,7 +30979,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
30934
30979
|
this.filesToUpload = [];
|
|
30935
30980
|
if (this.options.proctoringType == "REALTIME") {
|
|
30936
30981
|
this.captureFrame();
|
|
30937
|
-
this.sendFrameInterval = setInterval(() => this.captureFrame(), 1e3 * ((_i3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _i3.realtimeUploadInterval));
|
|
30938
30982
|
}
|
|
30939
30983
|
this.packageCount = 0;
|
|
30940
30984
|
}
|
|
@@ -30965,7 +31009,8 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
30965
31009
|
clearInterval(this.imageInterval);
|
|
30966
31010
|
clearInterval(this.sendFrameInterval);
|
|
30967
31011
|
if (this.options.proctoringType == "REALTIME" && this.upload && this.backendToken) {
|
|
30968
|
-
await this.sendPackage();
|
|
31012
|
+
await this.sendPackage(this.filesToUpload);
|
|
31013
|
+
await this.filesToUpload.splice(0, this.filesToUpload.length);
|
|
30969
31014
|
}
|
|
30970
31015
|
this.volumeMeter && this.volumeMeter.stop();
|
|
30971
31016
|
this.intervalNoiseDetection && clearInterval(this.intervalNoiseDetection);
|
|
@@ -31035,23 +31080,29 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
31035
31080
|
this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
|
|
31036
31081
|
return this.canvas.toDataURL("image/jpeg");
|
|
31037
31082
|
}
|
|
31038
|
-
//
|
|
31083
|
+
// captura um frame a cada intervalo de tempo definido no paramsConfig.videoBehaviourParameters?.realtimeCaptureInterval!
|
|
31039
31084
|
captureFrame() {
|
|
31040
|
-
var _a2;
|
|
31085
|
+
var _a2, _b;
|
|
31041
31086
|
let imageFile;
|
|
31042
31087
|
this.configImageCapture();
|
|
31088
|
+
let newCanvasWidth = this.videoOptions.width / 2;
|
|
31089
|
+
let newCanvasHeight = this.videoOptions.height / 2;
|
|
31090
|
+
if (newCanvasWidth < 320) newCanvasWidth = 320;
|
|
31091
|
+
if (newCanvasHeight < 180) newCanvasHeight = 180;
|
|
31092
|
+
this.canvas.width = newCanvasWidth;
|
|
31093
|
+
this.canvas.height = newCanvasHeight;
|
|
31043
31094
|
const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
|
|
31044
|
-
|
|
31045
|
-
this.imageCount++;
|
|
31095
|
+
this.imageCount = 0;
|
|
31046
31096
|
this.imageInterval = setInterval(async () => {
|
|
31097
|
+
console.log("capturando frame " + this.imageCount);
|
|
31047
31098
|
this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
|
|
31048
31099
|
const image_data_url = this.canvas.toDataURL("image/jpeg");
|
|
31049
31100
|
if (this.proctoringId == void 0) return;
|
|
31050
|
-
if (
|
|
31051
|
-
initSend = true;
|
|
31101
|
+
if (packSize == this.imageCount) {
|
|
31052
31102
|
this.imageCount = 0;
|
|
31053
|
-
|
|
31054
|
-
this.sendPackage();
|
|
31103
|
+
const framesToSend = [...this.filesToUpload];
|
|
31104
|
+
this.sendPackage(framesToSend);
|
|
31105
|
+
await this.filesToUpload.splice(0, this.filesToUpload.length);
|
|
31055
31106
|
}
|
|
31056
31107
|
let imageName = `${this.proctoringId}_${this.imageCount + 1}.jpg`;
|
|
31057
31108
|
imageFile = await this.getFile(image_data_url, imageName, "image/jpeg");
|
|
@@ -31059,22 +31110,24 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
31059
31110
|
this.filesToUpload.push(imageFile);
|
|
31060
31111
|
this.imageCount++;
|
|
31061
31112
|
}
|
|
31062
|
-
}, 1e3);
|
|
31113
|
+
}, ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) * 1e3);
|
|
31063
31114
|
}
|
|
31064
31115
|
// envia pacote de imagens
|
|
31065
|
-
async sendPackage() {
|
|
31116
|
+
async sendPackage(framesToSend) {
|
|
31117
|
+
var _a2, _b;
|
|
31066
31118
|
let pending = false;
|
|
31067
31119
|
let undeliveredPackagesCount = 0;
|
|
31120
|
+
const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
|
|
31121
|
+
const packCaptureInterval = ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) + 1;
|
|
31068
31122
|
if (this.upload && this.backendToken && !pending && this.filesToUpload.length > 0) {
|
|
31069
31123
|
undeliveredPackagesCount = 0;
|
|
31070
31124
|
pending = true;
|
|
31071
31125
|
const zip = new import_jszip_min.default();
|
|
31072
|
-
const
|
|
31073
|
-
for (const file of files) {
|
|
31126
|
+
for (const file of framesToSend) {
|
|
31074
31127
|
zip.file(file.name, file);
|
|
31075
31128
|
}
|
|
31076
31129
|
const blob = await zip.generateAsync({ type: "blob" });
|
|
31077
|
-
let packageName = "realtime_package_" +
|
|
31130
|
+
let packageName = "realtime_package_" + packSize * packCaptureInterval * this.packageCount + ".zip";
|
|
31078
31131
|
const myPackage = new File(
|
|
31079
31132
|
[blob],
|
|
31080
31133
|
packageName,
|
|
@@ -31087,7 +31140,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
31087
31140
|
this.backendToken
|
|
31088
31141
|
);
|
|
31089
31142
|
if (uploadResult.uploaded == true) {
|
|
31090
|
-
await this.filesToUpload.splice(0, this.filesToUpload.length);
|
|
31091
31143
|
this.packageCount++;
|
|
31092
31144
|
} else {
|
|
31093
31145
|
console.log("erro no upload do pacote");
|
|
@@ -31671,8 +31723,20 @@ var DeviceCheckerUI = class {
|
|
|
31671
31723
|
);
|
|
31672
31724
|
checkmark_FacePosition.appendChild(checkmark_stem_FacePosition);
|
|
31673
31725
|
checkmark_FacePosition.appendChild(checkmark_kick_FacePosition);
|
|
31726
|
+
const info_FacePosition = document.createElement("span");
|
|
31727
|
+
const info_stem_FacePosition = document.createElement("div");
|
|
31728
|
+
const info_kick_FacePosition = document.createElement("div");
|
|
31729
|
+
info_FacePosition.style.marginLeft = "10px";
|
|
31730
|
+
info_FacePosition.setAttribute("class", "info");
|
|
31731
|
+
info_FacePosition.setAttribute("id", "info_FacePosition");
|
|
31732
|
+
info_FacePosition.style.display = "none";
|
|
31733
|
+
info_stem_FacePosition.setAttribute("class", "info_stem");
|
|
31734
|
+
info_kick_FacePosition.setAttribute("class", "info_kick");
|
|
31735
|
+
info_FacePosition.appendChild(info_stem_FacePosition);
|
|
31736
|
+
info_FacePosition.appendChild(info_kick_FacePosition);
|
|
31674
31737
|
alertDivFacePosition.appendChild(checkmark_FacePosition);
|
|
31675
31738
|
alertDivFacePosition.appendChild(facePositionAlert);
|
|
31739
|
+
alertDivFacePosition.appendChild(info_FacePosition);
|
|
31676
31740
|
const alertDivAmbientVerify = document.createElement("div");
|
|
31677
31741
|
alertDivAmbientVerify.setAttribute("class", "alert-div");
|
|
31678
31742
|
alertDivAmbientVerify.setAttribute("id", "alertDivAmbientVerify");
|
|
@@ -32003,6 +32067,8 @@ var DeviceCheckerUI = class {
|
|
|
32003
32067
|
realtimeAlertsUI(response) {
|
|
32004
32068
|
const facePositionAlert = document.getElementById("facePositionAlert");
|
|
32005
32069
|
const ambientVerifyAlert = document.getElementById("ambientVerifyAlert");
|
|
32070
|
+
const alertDivFacePosition = document.getElementById("alertDivFacePosition");
|
|
32071
|
+
const info_FacePosition = document.getElementById("info_FacePosition");
|
|
32006
32072
|
const checkmark_FacePosition = document.getElementById("checkmark_FacePosition");
|
|
32007
32073
|
const checkmark_stem_FacePosition = document.getElementById("checkmark_stem_FacePosition");
|
|
32008
32074
|
const checkmark_kick_FacePosition = document.getElementById("checkmark_kick_FacePosition");
|
|
@@ -32013,6 +32079,8 @@ var DeviceCheckerUI = class {
|
|
|
32013
32079
|
if (response.status === "OK") {
|
|
32014
32080
|
facePositionAlert && (facePositionAlert.style.color = "#16A34A");
|
|
32015
32081
|
ambientVerifyAlert && (ambientVerifyAlert.style.color = "#16A34A");
|
|
32082
|
+
alertDivFacePosition && alertDivFacePosition.setAttribute("title", ``);
|
|
32083
|
+
info_FacePosition && (info_FacePosition.style.display = "none");
|
|
32016
32084
|
alertDivAmbientVerify && alertDivAmbientVerify.setAttribute("title", ``);
|
|
32017
32085
|
if (checkmark_FacePosition) {
|
|
32018
32086
|
checkmark_FacePosition.setAttribute("class", "checkmark");
|
|
@@ -32027,6 +32095,8 @@ var DeviceCheckerUI = class {
|
|
|
32027
32095
|
} else {
|
|
32028
32096
|
if (checkmark_FacePosition && (response.type === "position_detection_on_stream" || response.type === "face_detection_on_stream")) {
|
|
32029
32097
|
facePositionAlert && (facePositionAlert.style.color = "#FF0000");
|
|
32098
|
+
info_FacePosition && (info_FacePosition.style.display = "block");
|
|
32099
|
+
alertDivFacePosition && alertDivFacePosition.setAttribute("title", response.description);
|
|
32030
32100
|
checkmark_FacePosition.setAttribute("class", "checkmark_error");
|
|
32031
32101
|
checkmark_stem_FacePosition.setAttribute("class", "checkmark_stem_error");
|
|
32032
32102
|
checkmark_kick_FacePosition.setAttribute("class", "checkmark_kick_error");
|
|
@@ -33137,64 +33207,191 @@ var ProctoringUploader = class {
|
|
|
33137
33207
|
var AlertRecorder = class {
|
|
33138
33208
|
constructor(options, optionsProctoring) {
|
|
33139
33209
|
this.alerts = [];
|
|
33210
|
+
// Variáveis para controle de inatividade
|
|
33211
|
+
this.lastActivityTime = Date.now();
|
|
33212
|
+
this.IDLE_THRESHOLD_MS = 3e4;
|
|
33213
|
+
// ==========================================
|
|
33214
|
+
// HANDLERS (Funções Arrow para preservar o 'this')
|
|
33215
|
+
// ==========================================
|
|
33216
|
+
// 1. LOST / RETURN FOCUS
|
|
33217
|
+
this.handleVisibilityChange = () => {
|
|
33218
|
+
if (document.visibilityState === "visible") {
|
|
33219
|
+
this.handleReturnFocus();
|
|
33220
|
+
} else {
|
|
33221
|
+
this.handleLostFocus();
|
|
33222
|
+
}
|
|
33223
|
+
};
|
|
33224
|
+
this.handleLostFocus = () => {
|
|
33225
|
+
if (this.getRelativeTime() > 1e4) {
|
|
33226
|
+
const alertPayload = {
|
|
33227
|
+
begin: this.getRelativeTime(),
|
|
33228
|
+
end: 0,
|
|
33229
|
+
alert: 25 /* FocusOff */,
|
|
33230
|
+
type: 3 /* Screen */
|
|
33231
|
+
};
|
|
33232
|
+
this.onLostFocusCallback(alertPayload);
|
|
33233
|
+
this.alerts.push(alertPayload);
|
|
33234
|
+
}
|
|
33235
|
+
};
|
|
33236
|
+
this.handleReturnFocus = () => {
|
|
33237
|
+
const lastAlert = this.alerts[this.alerts.length - 1];
|
|
33238
|
+
if (lastAlert) {
|
|
33239
|
+
this.onFocusCallback({
|
|
33240
|
+
begin: lastAlert.begin,
|
|
33241
|
+
end: this.getRelativeTime(),
|
|
33242
|
+
alert: 25 /* FocusOff */,
|
|
33243
|
+
type: 3 /* Screen */
|
|
33244
|
+
});
|
|
33245
|
+
lastAlert.end = this.getRelativeTime();
|
|
33246
|
+
}
|
|
33247
|
+
};
|
|
33248
|
+
// 2. SPLIT SCREEN DETECTION
|
|
33249
|
+
this.handleResize = () => {
|
|
33250
|
+
clearTimeout(this.resizeTimeout);
|
|
33251
|
+
this.resizeTimeout = setTimeout(() => {
|
|
33252
|
+
const screenWidth = window.screen.availWidth;
|
|
33253
|
+
const windowWidth = window.outerWidth;
|
|
33254
|
+
if (windowWidth < screenWidth * 0.85) {
|
|
33255
|
+
const msg = `Split Screen Detectado: Janela ocupa ${(windowWidth / screenWidth * 100).toFixed(0)}% da tela`;
|
|
33256
|
+
this.createAlert(43 /* SplitScreen */, 3 /* Screen */, msg);
|
|
33257
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
33258
|
+
status: "ALERT",
|
|
33259
|
+
description: msg,
|
|
33260
|
+
type: "split_screen"
|
|
33261
|
+
});
|
|
33262
|
+
}
|
|
33263
|
+
}, 1e3);
|
|
33264
|
+
};
|
|
33265
|
+
// 3. DETECÇÃO DE INATIVIDADE (NO INPUT)
|
|
33266
|
+
this.handleUserActivity = (e3) => {
|
|
33267
|
+
this.lastActivityTime = Date.now();
|
|
33268
|
+
console.log("\u{1F449} handleUserActivity", e3);
|
|
33269
|
+
};
|
|
33270
|
+
// 5. CLIPBOARD HANDLERS
|
|
33271
|
+
this.handleCopy = (e3) => {
|
|
33272
|
+
e3.preventDefault();
|
|
33273
|
+
const msg = "Tentativa de Copiar (Clipboard)";
|
|
33274
|
+
this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
|
|
33275
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
33276
|
+
status: "ALERT",
|
|
33277
|
+
description: msg,
|
|
33278
|
+
type: "clipboard_copy"
|
|
33279
|
+
});
|
|
33280
|
+
};
|
|
33281
|
+
this.handleCut = (e3) => {
|
|
33282
|
+
e3.preventDefault();
|
|
33283
|
+
const msg = "Tentativa de Recortar (Clipboard)";
|
|
33284
|
+
this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
|
|
33285
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
33286
|
+
status: "ALERT",
|
|
33287
|
+
description: msg,
|
|
33288
|
+
type: "clipboard_cut"
|
|
33289
|
+
});
|
|
33290
|
+
};
|
|
33291
|
+
this.handlePaste = (e3) => {
|
|
33292
|
+
e3.preventDefault();
|
|
33293
|
+
const msg = "Tentativa de Colar (Clipboard)";
|
|
33294
|
+
this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
|
|
33295
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
33296
|
+
status: "ALERT",
|
|
33297
|
+
description: msg,
|
|
33298
|
+
type: "clipboard_paste"
|
|
33299
|
+
});
|
|
33300
|
+
};
|
|
33140
33301
|
this.onLostFocusCallback = options.onLostFocusCallback;
|
|
33141
33302
|
this.onFocusCallback = options.onFocusCallback;
|
|
33303
|
+
this.onRealtimeAlertCallback = options.onRealtimeAlertCallback;
|
|
33142
33304
|
this.optionsProctoring = optionsProctoring;
|
|
33143
33305
|
}
|
|
33144
33306
|
async startRecording() {
|
|
33145
33307
|
this.startTime = new Date(Date.now());
|
|
33146
|
-
|
|
33147
|
-
|
|
33148
|
-
window.addEventListener("focus", () => this.onReturnFocus());
|
|
33149
|
-
}
|
|
33308
|
+
this.alerts = [];
|
|
33309
|
+
this.attachListeners();
|
|
33150
33310
|
}
|
|
33151
33311
|
async pauseRecording() {
|
|
33152
|
-
|
|
33153
|
-
window.removeEventListener("focus", () => this.onReturnFocus());
|
|
33312
|
+
this.detachListeners();
|
|
33154
33313
|
}
|
|
33155
33314
|
async resumeRecording() {
|
|
33156
|
-
|
|
33157
|
-
window.addEventListener("blur", () => this.onLostFocus());
|
|
33158
|
-
window.addEventListener("focus", () => this.onReturnFocus());
|
|
33159
|
-
}
|
|
33315
|
+
this.attachListeners();
|
|
33160
33316
|
}
|
|
33161
33317
|
async stopRecording() {
|
|
33162
|
-
|
|
33163
|
-
window.removeEventListener("focus", () => this.onReturnFocus());
|
|
33318
|
+
this.detachListeners();
|
|
33164
33319
|
}
|
|
33165
33320
|
async saveOnSession(session) {
|
|
33166
33321
|
this.alerts.forEach((alert) => {
|
|
33167
33322
|
session.addAlert(alert);
|
|
33168
33323
|
});
|
|
33169
33324
|
}
|
|
33170
|
-
|
|
33171
|
-
|
|
33172
|
-
|
|
33173
|
-
|
|
33174
|
-
|
|
33175
|
-
|
|
33176
|
-
|
|
33177
|
-
|
|
33178
|
-
|
|
33179
|
-
|
|
33180
|
-
|
|
33181
|
-
|
|
33182
|
-
|
|
33183
|
-
|
|
33184
|
-
|
|
33185
|
-
|
|
33325
|
+
// ==========================================
|
|
33326
|
+
// GERENCIAMENTO DE LISTENERS
|
|
33327
|
+
// ==========================================
|
|
33328
|
+
attachListeners() {
|
|
33329
|
+
if (this.optionsProctoring.captureScreen) {
|
|
33330
|
+
window.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
33331
|
+
window.addEventListener("resize", this.handleResize);
|
|
33332
|
+
window.document.addEventListener("copy", this.handleCopy);
|
|
33333
|
+
window.document.addEventListener("cut", this.handleCut);
|
|
33334
|
+
window.document.addEventListener("paste", this.handlePaste);
|
|
33335
|
+
}
|
|
33336
|
+
}
|
|
33337
|
+
detachListeners() {
|
|
33338
|
+
window.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
33339
|
+
window.removeEventListener("resize", this.handleResize);
|
|
33340
|
+
window.document.removeEventListener("copy", this.handleCopy);
|
|
33341
|
+
window.document.removeEventListener("cut", this.handleCut);
|
|
33342
|
+
window.document.removeEventListener("paste", this.handlePaste);
|
|
33343
|
+
}
|
|
33344
|
+
// private startIdleChecker() {
|
|
33345
|
+
// this.stopIdleChecker(); // Garante que não tenha duplicação
|
|
33346
|
+
// this.idleCheckInterval = setInterval(() => {
|
|
33347
|
+
// const idleTime = Date.now() - this.lastActivityTime;
|
|
33348
|
+
// if (idleTime > this.IDLE_THRESHOLD_MS) {
|
|
33349
|
+
// // Verifica se já não mandamos esse alerta recentemente para não floodar
|
|
33350
|
+
// const lastAlert = this.alerts[this.alerts.length - 1];
|
|
33351
|
+
// // Se o último alerta não foi de inatividade ou foi há muito tempo, cria novo
|
|
33352
|
+
// if (!lastAlert || lastAlert.alert !== AlertCategory.ForbiddenAction || (this.getRelativeTime() - lastAlert.begin > 5000)) {
|
|
33353
|
+
// const msg = "Usuário inativo por muito tempo";
|
|
33354
|
+
// // Assumindo ForbiddenAction ou outra categoria adequada
|
|
33355
|
+
// this.createAlert(AlertCategory.ForbiddenAction, AlertType.System, msg);
|
|
33356
|
+
// this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
33357
|
+
// status: 'ALERT',
|
|
33358
|
+
// description: msg,
|
|
33359
|
+
// type: 'user_idle'
|
|
33360
|
+
// });
|
|
33361
|
+
// }
|
|
33362
|
+
// }
|
|
33363
|
+
// }, 5000); // Checa a cada 5 segundos
|
|
33364
|
+
// }
|
|
33365
|
+
// private stopIdleChecker() {
|
|
33366
|
+
// if (this.idleCheckInterval) {
|
|
33367
|
+
// clearInterval(this.idleCheckInterval);
|
|
33368
|
+
// this.idleCheckInterval = null;
|
|
33369
|
+
// }
|
|
33370
|
+
// }
|
|
33371
|
+
// 4. BACKGROUND EVENTS (Eventos disparados manualmente)
|
|
33372
|
+
addBackgroundEvent(description, category = 200 /* System */) {
|
|
33373
|
+
this.createAlert(category, 3 /* Screen */, description);
|
|
33374
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
33375
|
+
status: "ALERT",
|
|
33376
|
+
description,
|
|
33377
|
+
type: "background_event"
|
|
33378
|
+
});
|
|
33186
33379
|
}
|
|
33187
|
-
|
|
33188
|
-
|
|
33189
|
-
|
|
33190
|
-
|
|
33191
|
-
|
|
33192
|
-
|
|
33193
|
-
|
|
33194
|
-
|
|
33195
|
-
|
|
33196
|
-
|
|
33197
|
-
|
|
33380
|
+
// ==========================================
|
|
33381
|
+
// HELPERS
|
|
33382
|
+
// ==========================================
|
|
33383
|
+
getRelativeTime() {
|
|
33384
|
+
return Date.now() - this.startTime.getTime();
|
|
33385
|
+
}
|
|
33386
|
+
createAlert(category, type, description) {
|
|
33387
|
+
this.alerts.push({
|
|
33388
|
+
begin: this.getRelativeTime(),
|
|
33389
|
+
end: this.getRelativeTime(),
|
|
33390
|
+
// Eventos pontuais começam e terminam no mesmo ms (exceto focus)
|
|
33391
|
+
alert: category,
|
|
33392
|
+
type
|
|
33393
|
+
// description: description // Se sua interface Alert suportar descrição, descomente
|
|
33394
|
+
});
|
|
33198
33395
|
}
|
|
33199
33396
|
addAlert({ alert, type }) {
|
|
33200
33397
|
this.alerts.push({
|
|
@@ -36563,7 +36760,8 @@ var _ExternalCameraChecker = class _ExternalCameraChecker {
|
|
|
36563
36760
|
this.connection = new import_signalr.HubConnectionBuilder().withUrl(hubUrl, {
|
|
36564
36761
|
accessTokenFactory: () => this.context.token,
|
|
36565
36762
|
transport: import_signalr.HttpTransportType.WebSockets,
|
|
36566
|
-
withCredentials: false
|
|
36763
|
+
withCredentials: false,
|
|
36764
|
+
skipNegotiation: true
|
|
36567
36765
|
}).withAutomaticReconnect().configureLogging(import_signalr.LogLevel.Information).build();
|
|
36568
36766
|
this.connection.on(
|
|
36569
36767
|
"ReceiveMessage",
|
|
@@ -36769,7 +36967,6 @@ var Proctoring = class {
|
|
|
36769
36967
|
noiseLimit: 40
|
|
36770
36968
|
},
|
|
36771
36969
|
imageBehaviourParameters: {
|
|
36772
|
-
frames: 40,
|
|
36773
36970
|
useUploadImage: true,
|
|
36774
36971
|
uploadInterval: 20,
|
|
36775
36972
|
saveVideo: true
|
|
@@ -36780,8 +36977,8 @@ var Proctoring = class {
|
|
|
36780
36977
|
detectCellPhone: false,
|
|
36781
36978
|
detectNoise: false,
|
|
36782
36979
|
detectSpeech: false,
|
|
36783
|
-
realtimePackageSize:
|
|
36784
|
-
|
|
36980
|
+
realtimePackageSize: 10,
|
|
36981
|
+
realtimeCaptureInterval: 2
|
|
36785
36982
|
}
|
|
36786
36983
|
};
|
|
36787
36984
|
this.proctoringId = "";
|
|
@@ -36917,6 +37114,29 @@ var Proctoring = class {
|
|
|
36917
37114
|
return null;
|
|
36918
37115
|
}
|
|
36919
37116
|
}
|
|
37117
|
+
// metodo para fechar o alerta realtime e fica tentando verificar a face até 5 vezes
|
|
37118
|
+
async stopRealtimeAlert(alert) {
|
|
37119
|
+
const verifyMaxRetries = 3;
|
|
37120
|
+
const verifyFace = async (verifyCount) => {
|
|
37121
|
+
if (verifyCount > verifyMaxRetries) return;
|
|
37122
|
+
try {
|
|
37123
|
+
var response = await this.backend.stopRealtimeAlert({
|
|
37124
|
+
proctoringId: this.proctoringId,
|
|
37125
|
+
begin: alert.begin,
|
|
37126
|
+
end: alert.end,
|
|
37127
|
+
warningCategoryEnum: this.convertRealtimeTypeToWarningType(alert.type),
|
|
37128
|
+
alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64(),
|
|
37129
|
+
retry: verifyCount < verifyMaxRetries - 1 ? true : false
|
|
37130
|
+
});
|
|
37131
|
+
console.log("response stopRealtimeAlert", response);
|
|
37132
|
+
return response;
|
|
37133
|
+
} catch (error) {
|
|
37134
|
+
console.log("error stopRealtimeAlert", error);
|
|
37135
|
+
return verifyFace(verifyCount + 1);
|
|
37136
|
+
}
|
|
37137
|
+
};
|
|
37138
|
+
await verifyFace(1);
|
|
37139
|
+
}
|
|
36920
37140
|
async onRealtimeAlerts(options = {}) {
|
|
36921
37141
|
this.setOnLostFocusAlertRecorderCallback();
|
|
36922
37142
|
this.setOnFocusAlertRecorderCallback();
|
|
@@ -36931,13 +37151,7 @@ var Proctoring = class {
|
|
|
36931
37151
|
alert: this.convertRealtimeCategoryToAlertCategory(response.category)
|
|
36932
37152
|
});
|
|
36933
37153
|
} else if (response.status === "OK") {
|
|
36934
|
-
await this.
|
|
36935
|
-
proctoringId: this.proctoringId,
|
|
36936
|
-
begin: response.begin,
|
|
36937
|
-
end: response.end,
|
|
36938
|
-
warningCategoryEnum: this.convertRealtimeTypeToWarningType(response.type),
|
|
36939
|
-
alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64()
|
|
36940
|
-
});
|
|
37154
|
+
await this.stopRealtimeAlert(response);
|
|
36941
37155
|
}
|
|
36942
37156
|
}
|
|
36943
37157
|
};
|
|
@@ -36987,7 +37201,8 @@ var Proctoring = class {
|
|
|
36987
37201
|
const alertRecorder = new AlertRecorder(
|
|
36988
37202
|
{
|
|
36989
37203
|
onFocusCallback: (response) => this.onFocusAlertRecorderCallback(response),
|
|
36990
|
-
onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response)
|
|
37204
|
+
onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response),
|
|
37205
|
+
onRealtimeAlertCallback: (response) => this.onRealtimeAlertsCallback(response)
|
|
36991
37206
|
},
|
|
36992
37207
|
options
|
|
36993
37208
|
);
|
|
@@ -37095,11 +37310,23 @@ Navigator: ${JSON.stringify(_navigator2)}`
|
|
|
37095
37310
|
startResponse.screenStream = this.allRecorders.screenRecorder.screenStream;
|
|
37096
37311
|
}
|
|
37097
37312
|
this.state = "Recording" /* Recording */;
|
|
37098
|
-
|
|
37313
|
+
let verifyFirstFaceIntervalCount = 0;
|
|
37314
|
+
let verifyingFace = false;
|
|
37315
|
+
this.verifyFirstFaceInterval = setInterval(async () => {
|
|
37099
37316
|
if (this.sessionOptions.proctoringType === "REALTIME") {
|
|
37100
|
-
|
|
37317
|
+
if (verifyingFace) return;
|
|
37318
|
+
verifyingFace = true;
|
|
37319
|
+
verifyFirstFaceIntervalCount++;
|
|
37320
|
+
try {
|
|
37321
|
+
var response = await this.backend.verifyFace(this.proctoringId, await this.allRecorders.cameraRecorder.getCurrentImageBase64(), verifyFirstFaceIntervalCount > 5 ? false : true);
|
|
37322
|
+
verifyingFace = false;
|
|
37323
|
+
clearInterval(this.verifyFirstFaceInterval);
|
|
37324
|
+
} catch (error) {
|
|
37325
|
+
verifyingFace = false;
|
|
37326
|
+
return;
|
|
37327
|
+
}
|
|
37101
37328
|
}
|
|
37102
|
-
},
|
|
37329
|
+
}, 1500);
|
|
37103
37330
|
return startResponse;
|
|
37104
37331
|
} catch (error) {
|
|
37105
37332
|
console.log(error);
|
|
@@ -37236,6 +37463,9 @@ Upload Services: ${uploaderServices}`,
|
|
|
37236
37463
|
await this.backend.externalCameraFinish(externalSessionId);
|
|
37237
37464
|
}
|
|
37238
37465
|
}
|
|
37466
|
+
if (this.verifyFirstFaceInterval) {
|
|
37467
|
+
clearInterval(this.verifyFirstFaceInterval);
|
|
37468
|
+
}
|
|
37239
37469
|
this.state = "Stop" /* Stop */;
|
|
37240
37470
|
} catch (error) {
|
|
37241
37471
|
await this.cancel();
|
|
@@ -37570,12 +37800,12 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
|
|
|
37570
37800
|
const photo = new CapturePhoto();
|
|
37571
37801
|
const login = proctoring.login.bind(proctoring);
|
|
37572
37802
|
const originalStart = proctoring.start.bind(proctoring);
|
|
37573
|
-
const start = async (parameters2) => {
|
|
37803
|
+
const start = async (parameters2, videoOptions) => {
|
|
37574
37804
|
const deviceResult = checker.getDeviceCheckResult();
|
|
37575
37805
|
if (deviceResult) {
|
|
37576
37806
|
proctoring.setDeviceCheckData(deviceResult);
|
|
37577
37807
|
}
|
|
37578
|
-
return originalStart(parameters2);
|
|
37808
|
+
return originalStart(parameters2, videoOptions);
|
|
37579
37809
|
};
|
|
37580
37810
|
const finish = proctoring.finish.bind(proctoring);
|
|
37581
37811
|
const pause = proctoring.pause.bind(proctoring);
|