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/esm/index.js
CHANGED
|
@@ -8791,6 +8791,14 @@ var BaseDetection = class {
|
|
|
8791
8791
|
return "Face n\xE3o centralizada, mova-se para cima";
|
|
8792
8792
|
case "wrong_face_position_move_bottom_detected":
|
|
8793
8793
|
return "Face n\xE3o centralizada, mova-se para baixo";
|
|
8794
|
+
case "face_turned_left_detected":
|
|
8795
|
+
return "Face virada para a esquerda, centralize-a";
|
|
8796
|
+
case "face_turned_right_detected":
|
|
8797
|
+
return "Face virada para a direita, centralize-a";
|
|
8798
|
+
case "face_turned_up_detected":
|
|
8799
|
+
return "Face virada para cima, centralize-a";
|
|
8800
|
+
case "face_turned_down_detected":
|
|
8801
|
+
return "Face virada para baixo, centralize-a";
|
|
8794
8802
|
default:
|
|
8795
8803
|
return description;
|
|
8796
8804
|
}
|
|
@@ -8821,7 +8829,14 @@ function buildVideoPreview() {
|
|
|
8821
8829
|
// src/modules/faceDetection.ts
|
|
8822
8830
|
var FaceDetection = class extends BaseDetection {
|
|
8823
8831
|
constructor(options, paramsConfig, classVideo = "videoPreviewFrameDetection", classDiv = "liveViewFrameDetection") {
|
|
8824
|
-
super(
|
|
8832
|
+
super(
|
|
8833
|
+
"FaceDetector",
|
|
8834
|
+
`https://storage.googleapis.com/mediapipe-models/face_detector/blaze_face_short_range/float16/1/blaze_face_short_range.tflite`,
|
|
8835
|
+
options,
|
|
8836
|
+
paramsConfig,
|
|
8837
|
+
classVideo,
|
|
8838
|
+
classDiv
|
|
8839
|
+
);
|
|
8825
8840
|
this.emmitedPositionAlert = false;
|
|
8826
8841
|
this.emmitedFaceAlert = false;
|
|
8827
8842
|
}
|
|
@@ -8967,8 +8982,8 @@ var FaceDetection = class extends BaseDetection {
|
|
|
8967
8982
|
for (const keypoint of detection.keypoints) {
|
|
8968
8983
|
const keypointEl = document.createElement("span");
|
|
8969
8984
|
keypointEl.className = "key-point";
|
|
8970
|
-
|
|
8971
|
-
|
|
8985
|
+
const kpX = keypoint.x < 2 ? keypoint.x * videoElement.videoWidth : keypoint.x;
|
|
8986
|
+
const kpY = keypoint.y < 2 ? keypoint.y * videoElement.videoHeight : keypoint.y;
|
|
8972
8987
|
const mirroredKpX = videoElement.videoWidth - kpX;
|
|
8973
8988
|
const screenX = mirroredKpX * scaleX;
|
|
8974
8989
|
const screenY = kpY * scaleY;
|
|
@@ -9007,55 +9022,87 @@ var FaceDetection = class extends BaseDetection {
|
|
|
9007
9022
|
}
|
|
9008
9023
|
}
|
|
9009
9024
|
if (result.detections.length === 0) return;
|
|
9010
|
-
let
|
|
9011
|
-
|
|
9025
|
+
let failedFacePosition = false;
|
|
9026
|
+
const video = document.getElementById(this.classVideo);
|
|
9012
9027
|
const imageWidth = video.videoWidth;
|
|
9013
9028
|
const imageHeight = video.videoHeight;
|
|
9014
|
-
|
|
9029
|
+
const detection = result.detections[0];
|
|
9030
|
+
const face = detection.boundingBox;
|
|
9031
|
+
const keypoints = detection.keypoints;
|
|
9032
|
+
const rightEye = keypoints[0];
|
|
9033
|
+
const leftEye = keypoints[1];
|
|
9034
|
+
const mouth = keypoints[3];
|
|
9035
|
+
const rightEar = keypoints[4];
|
|
9036
|
+
const leftEar = keypoints[5];
|
|
9037
|
+
const nose = keypoints[2];
|
|
9015
9038
|
if (imageWidth > imageHeight) {
|
|
9016
9039
|
if (face.height / imageHeight > 0.7) {
|
|
9017
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
9040
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
9041
|
+
"wrong_face_size_detected",
|
|
9042
|
+
"position_detection_on_stream"
|
|
9043
|
+
);
|
|
9018
9044
|
failedFacePosition = true;
|
|
9019
9045
|
this.emmitedPositionAlert = true;
|
|
9020
9046
|
} else if (face.width / imageWidth > 0.7) {
|
|
9021
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
9047
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
9048
|
+
"wrong_face_size_detected",
|
|
9049
|
+
"position_detection_on_stream"
|
|
9050
|
+
);
|
|
9022
9051
|
this.emmitedPositionAlert = true;
|
|
9023
9052
|
failedFacePosition = true;
|
|
9024
9053
|
}
|
|
9025
9054
|
}
|
|
9026
|
-
|
|
9027
|
-
|
|
9055
|
+
const start = [face.originX, face.originY];
|
|
9056
|
+
const end = [face.originX + face.width, face.originY + face.height];
|
|
9028
9057
|
if (start[0] < 0.1 * face.width || start[1] < 0.2 * face.height || end[0] > imageWidth - 0.1 * face.width || end[1] > imageHeight - 5) {
|
|
9029
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
9058
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
9059
|
+
"wrong_face_position_edge_detected",
|
|
9060
|
+
"position_detection_on_stream"
|
|
9061
|
+
);
|
|
9030
9062
|
this.emmitedPositionAlert = true;
|
|
9031
9063
|
failedFacePosition = true;
|
|
9032
9064
|
}
|
|
9033
|
-
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
9065
|
+
const leftGap = start[0];
|
|
9066
|
+
const rightGap = imageWidth - end[0];
|
|
9067
|
+
const topGap = start[1];
|
|
9068
|
+
const bottomGap = imageHeight - end[1];
|
|
9037
9069
|
if (leftGap > 2 * rightGap) {
|
|
9038
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
9070
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
9071
|
+
"wrong_face_position_move_right_detected",
|
|
9072
|
+
"position_detection_on_stream"
|
|
9073
|
+
);
|
|
9039
9074
|
this.emmitedPositionAlert = true;
|
|
9040
9075
|
failedFacePosition = true;
|
|
9041
9076
|
}
|
|
9042
9077
|
if (topGap > 4 * bottomGap) {
|
|
9043
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
9078
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
9079
|
+
"wrong_face_position_move_top_detected",
|
|
9080
|
+
"position_detection_on_stream"
|
|
9081
|
+
);
|
|
9044
9082
|
this.emmitedPositionAlert = true;
|
|
9045
9083
|
failedFacePosition = true;
|
|
9046
9084
|
}
|
|
9047
9085
|
if (rightGap > 2 * leftGap) {
|
|
9048
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
9086
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
9087
|
+
"wrong_face_position_move_left_detected",
|
|
9088
|
+
"position_detection_on_stream"
|
|
9089
|
+
);
|
|
9049
9090
|
this.emmitedPositionAlert = true;
|
|
9050
9091
|
failedFacePosition = true;
|
|
9051
9092
|
}
|
|
9052
9093
|
if (bottomGap > 3 * topGap) {
|
|
9053
|
-
!this.emmitedPositionAlert && this.handleAlert(
|
|
9094
|
+
!this.emmitedPositionAlert && this.handleAlert(
|
|
9095
|
+
"wrong_face_position_move_bottom_detected",
|
|
9096
|
+
"position_detection_on_stream"
|
|
9097
|
+
);
|
|
9054
9098
|
this.emmitedPositionAlert = true;
|
|
9055
9099
|
failedFacePosition = true;
|
|
9056
9100
|
}
|
|
9057
9101
|
if (failedFacePosition == false) {
|
|
9058
|
-
this.emmitedPositionAlert && this.handleOk(
|
|
9102
|
+
this.emmitedPositionAlert && this.handleOk(
|
|
9103
|
+
"ok_position_face_detected",
|
|
9104
|
+
"position_detection_on_stream"
|
|
9105
|
+
);
|
|
9059
9106
|
this.emmitedPositionAlert = false;
|
|
9060
9107
|
}
|
|
9061
9108
|
}
|
|
@@ -11772,12 +11819,12 @@ var BackendService = class {
|
|
|
11772
11819
|
});
|
|
11773
11820
|
return result.data;
|
|
11774
11821
|
}
|
|
11775
|
-
async verifyFace(proctoringId2, faceImage) {
|
|
11822
|
+
async verifyFace(proctoringId2, faceImage, retry) {
|
|
11776
11823
|
const result = await this.makeRequestAxios({
|
|
11777
11824
|
path: `/Realtime/verify-face`,
|
|
11778
11825
|
method: "POST",
|
|
11779
11826
|
jwt: this.token,
|
|
11780
|
-
body: { "proctoringId": proctoringId2, "base64": faceImage }
|
|
11827
|
+
body: { "proctoringId": proctoringId2, "base64": faceImage, "retry": retry }
|
|
11781
11828
|
});
|
|
11782
11829
|
return result.data;
|
|
11783
11830
|
}
|
|
@@ -12653,7 +12700,6 @@ var CameraRecorder = class {
|
|
|
12653
12700
|
noiseLimit: 40
|
|
12654
12701
|
},
|
|
12655
12702
|
imageBehaviourParameters: {
|
|
12656
|
-
frames: 40,
|
|
12657
12703
|
useUploadImage: true,
|
|
12658
12704
|
uploadInterval: 20,
|
|
12659
12705
|
saveVideo: true
|
|
@@ -12665,7 +12711,6 @@ var CameraRecorder = class {
|
|
|
12665
12711
|
}
|
|
12666
12712
|
};
|
|
12667
12713
|
// private imageParams: ImageParameters = {
|
|
12668
|
-
// frames: 40,
|
|
12669
12714
|
// useUploadImage: true,
|
|
12670
12715
|
// uploadInterval: 20,
|
|
12671
12716
|
// saveVideo: true
|
|
@@ -12757,7 +12802,7 @@ var CameraRecorder = class {
|
|
|
12757
12802
|
}
|
|
12758
12803
|
}
|
|
12759
12804
|
async startRecording(options) {
|
|
12760
|
-
var _a2, _b, _c2, _d, _e3, _f, _g, _h
|
|
12805
|
+
var _a2, _b, _c2, _d, _e3, _f, _g, _h;
|
|
12761
12806
|
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)) {
|
|
12762
12807
|
await this.initializeDetectors();
|
|
12763
12808
|
}
|
|
@@ -12837,7 +12882,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
12837
12882
|
this.filesToUpload = [];
|
|
12838
12883
|
if (this.options.proctoringType == "REALTIME") {
|
|
12839
12884
|
this.captureFrame();
|
|
12840
|
-
this.sendFrameInterval = setInterval(() => this.captureFrame(), 1e3 * ((_i3 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _i3.realtimeUploadInterval));
|
|
12841
12885
|
}
|
|
12842
12886
|
this.packageCount = 0;
|
|
12843
12887
|
}
|
|
@@ -12868,7 +12912,8 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
12868
12912
|
clearInterval(this.imageInterval);
|
|
12869
12913
|
clearInterval(this.sendFrameInterval);
|
|
12870
12914
|
if (this.options.proctoringType == "REALTIME" && this.upload && this.backendToken) {
|
|
12871
|
-
await this.sendPackage();
|
|
12915
|
+
await this.sendPackage(this.filesToUpload);
|
|
12916
|
+
await this.filesToUpload.splice(0, this.filesToUpload.length);
|
|
12872
12917
|
}
|
|
12873
12918
|
this.volumeMeter && this.volumeMeter.stop();
|
|
12874
12919
|
this.intervalNoiseDetection && clearInterval(this.intervalNoiseDetection);
|
|
@@ -12938,23 +12983,29 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
12938
12983
|
this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
|
|
12939
12984
|
return this.canvas.toDataURL("image/jpeg");
|
|
12940
12985
|
}
|
|
12941
|
-
//
|
|
12986
|
+
// captura um frame a cada intervalo de tempo definido no paramsConfig.videoBehaviourParameters?.realtimeCaptureInterval!
|
|
12942
12987
|
captureFrame() {
|
|
12943
|
-
var _a2;
|
|
12988
|
+
var _a2, _b;
|
|
12944
12989
|
let imageFile;
|
|
12945
12990
|
this.configImageCapture();
|
|
12991
|
+
let newCanvasWidth = this.videoOptions.width / 2;
|
|
12992
|
+
let newCanvasHeight = this.videoOptions.height / 2;
|
|
12993
|
+
if (newCanvasWidth < 320) newCanvasWidth = 320;
|
|
12994
|
+
if (newCanvasHeight < 180) newCanvasHeight = 180;
|
|
12995
|
+
this.canvas.width = newCanvasWidth;
|
|
12996
|
+
this.canvas.height = newCanvasHeight;
|
|
12946
12997
|
const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
|
|
12947
|
-
|
|
12948
|
-
this.imageCount++;
|
|
12998
|
+
this.imageCount = 0;
|
|
12949
12999
|
this.imageInterval = setInterval(async () => {
|
|
13000
|
+
console.log("capturando frame " + this.imageCount);
|
|
12950
13001
|
this.canvas.getContext("2d").drawImage(this.video, 0, 0, this.canvas.width, this.canvas.height);
|
|
12951
13002
|
const image_data_url = this.canvas.toDataURL("image/jpeg");
|
|
12952
13003
|
if (this.proctoringId == void 0) return;
|
|
12953
|
-
if (
|
|
12954
|
-
initSend = true;
|
|
13004
|
+
if (packSize == this.imageCount) {
|
|
12955
13005
|
this.imageCount = 0;
|
|
12956
|
-
|
|
12957
|
-
this.sendPackage();
|
|
13006
|
+
const framesToSend = [...this.filesToUpload];
|
|
13007
|
+
this.sendPackage(framesToSend);
|
|
13008
|
+
await this.filesToUpload.splice(0, this.filesToUpload.length);
|
|
12958
13009
|
}
|
|
12959
13010
|
let imageName = `${this.proctoringId}_${this.imageCount + 1}.jpg`;
|
|
12960
13011
|
imageFile = await this.getFile(image_data_url, imageName, "image/jpeg");
|
|
@@ -12962,22 +13013,24 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
12962
13013
|
this.filesToUpload.push(imageFile);
|
|
12963
13014
|
this.imageCount++;
|
|
12964
13015
|
}
|
|
12965
|
-
}, 1e3);
|
|
13016
|
+
}, ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) * 1e3);
|
|
12966
13017
|
}
|
|
12967
13018
|
// envia pacote de imagens
|
|
12968
|
-
async sendPackage() {
|
|
13019
|
+
async sendPackage(framesToSend) {
|
|
13020
|
+
var _a2, _b;
|
|
12969
13021
|
let pending = false;
|
|
12970
13022
|
let undeliveredPackagesCount = 0;
|
|
13023
|
+
const packSize = (_a2 = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _a2.realtimePackageSize;
|
|
13024
|
+
const packCaptureInterval = ((_b = this.paramsConfig.videoBehaviourParameters) == null ? void 0 : _b.realtimeCaptureInterval) + 1;
|
|
12971
13025
|
if (this.upload && this.backendToken && !pending && this.filesToUpload.length > 0) {
|
|
12972
13026
|
undeliveredPackagesCount = 0;
|
|
12973
13027
|
pending = true;
|
|
12974
13028
|
const zip = new import_jszip_min.default();
|
|
12975
|
-
const
|
|
12976
|
-
for (const file of files) {
|
|
13029
|
+
for (const file of framesToSend) {
|
|
12977
13030
|
zip.file(file.name, file);
|
|
12978
13031
|
}
|
|
12979
13032
|
const blob = await zip.generateAsync({ type: "blob" });
|
|
12980
|
-
let packageName = "realtime_package_" +
|
|
13033
|
+
let packageName = "realtime_package_" + packSize * packCaptureInterval * this.packageCount + ".zip";
|
|
12981
13034
|
const myPackage = new File(
|
|
12982
13035
|
[blob],
|
|
12983
13036
|
packageName,
|
|
@@ -12990,7 +13043,6 @@ Setting: ${JSON.stringify(settings, null, 2)}`
|
|
|
12990
13043
|
this.backendToken
|
|
12991
13044
|
);
|
|
12992
13045
|
if (uploadResult.uploaded == true) {
|
|
12993
|
-
await this.filesToUpload.splice(0, this.filesToUpload.length);
|
|
12994
13046
|
this.packageCount++;
|
|
12995
13047
|
} else {
|
|
12996
13048
|
console.log("erro no upload do pacote");
|
|
@@ -13574,8 +13626,20 @@ var DeviceCheckerUI = class {
|
|
|
13574
13626
|
);
|
|
13575
13627
|
checkmark_FacePosition.appendChild(checkmark_stem_FacePosition);
|
|
13576
13628
|
checkmark_FacePosition.appendChild(checkmark_kick_FacePosition);
|
|
13629
|
+
const info_FacePosition = document.createElement("span");
|
|
13630
|
+
const info_stem_FacePosition = document.createElement("div");
|
|
13631
|
+
const info_kick_FacePosition = document.createElement("div");
|
|
13632
|
+
info_FacePosition.style.marginLeft = "10px";
|
|
13633
|
+
info_FacePosition.setAttribute("class", "info");
|
|
13634
|
+
info_FacePosition.setAttribute("id", "info_FacePosition");
|
|
13635
|
+
info_FacePosition.style.display = "none";
|
|
13636
|
+
info_stem_FacePosition.setAttribute("class", "info_stem");
|
|
13637
|
+
info_kick_FacePosition.setAttribute("class", "info_kick");
|
|
13638
|
+
info_FacePosition.appendChild(info_stem_FacePosition);
|
|
13639
|
+
info_FacePosition.appendChild(info_kick_FacePosition);
|
|
13577
13640
|
alertDivFacePosition.appendChild(checkmark_FacePosition);
|
|
13578
13641
|
alertDivFacePosition.appendChild(facePositionAlert);
|
|
13642
|
+
alertDivFacePosition.appendChild(info_FacePosition);
|
|
13579
13643
|
const alertDivAmbientVerify = document.createElement("div");
|
|
13580
13644
|
alertDivAmbientVerify.setAttribute("class", "alert-div");
|
|
13581
13645
|
alertDivAmbientVerify.setAttribute("id", "alertDivAmbientVerify");
|
|
@@ -13906,6 +13970,8 @@ var DeviceCheckerUI = class {
|
|
|
13906
13970
|
realtimeAlertsUI(response) {
|
|
13907
13971
|
const facePositionAlert = document.getElementById("facePositionAlert");
|
|
13908
13972
|
const ambientVerifyAlert = document.getElementById("ambientVerifyAlert");
|
|
13973
|
+
const alertDivFacePosition = document.getElementById("alertDivFacePosition");
|
|
13974
|
+
const info_FacePosition = document.getElementById("info_FacePosition");
|
|
13909
13975
|
const checkmark_FacePosition = document.getElementById("checkmark_FacePosition");
|
|
13910
13976
|
const checkmark_stem_FacePosition = document.getElementById("checkmark_stem_FacePosition");
|
|
13911
13977
|
const checkmark_kick_FacePosition = document.getElementById("checkmark_kick_FacePosition");
|
|
@@ -13916,6 +13982,8 @@ var DeviceCheckerUI = class {
|
|
|
13916
13982
|
if (response.status === "OK") {
|
|
13917
13983
|
facePositionAlert && (facePositionAlert.style.color = "#16A34A");
|
|
13918
13984
|
ambientVerifyAlert && (ambientVerifyAlert.style.color = "#16A34A");
|
|
13985
|
+
alertDivFacePosition && alertDivFacePosition.setAttribute("title", ``);
|
|
13986
|
+
info_FacePosition && (info_FacePosition.style.display = "none");
|
|
13919
13987
|
alertDivAmbientVerify && alertDivAmbientVerify.setAttribute("title", ``);
|
|
13920
13988
|
if (checkmark_FacePosition) {
|
|
13921
13989
|
checkmark_FacePosition.setAttribute("class", "checkmark");
|
|
@@ -13930,6 +13998,8 @@ var DeviceCheckerUI = class {
|
|
|
13930
13998
|
} else {
|
|
13931
13999
|
if (checkmark_FacePosition && (response.type === "position_detection_on_stream" || response.type === "face_detection_on_stream")) {
|
|
13932
14000
|
facePositionAlert && (facePositionAlert.style.color = "#FF0000");
|
|
14001
|
+
info_FacePosition && (info_FacePosition.style.display = "block");
|
|
14002
|
+
alertDivFacePosition && alertDivFacePosition.setAttribute("title", response.description);
|
|
13933
14003
|
checkmark_FacePosition.setAttribute("class", "checkmark_error");
|
|
13934
14004
|
checkmark_stem_FacePosition.setAttribute("class", "checkmark_stem_error");
|
|
13935
14005
|
checkmark_kick_FacePosition.setAttribute("class", "checkmark_kick_error");
|
|
@@ -15040,64 +15110,191 @@ var ProctoringUploader = class {
|
|
|
15040
15110
|
var AlertRecorder = class {
|
|
15041
15111
|
constructor(options, optionsProctoring) {
|
|
15042
15112
|
this.alerts = [];
|
|
15113
|
+
// Variáveis para controle de inatividade
|
|
15114
|
+
this.lastActivityTime = Date.now();
|
|
15115
|
+
this.IDLE_THRESHOLD_MS = 3e4;
|
|
15116
|
+
// ==========================================
|
|
15117
|
+
// HANDLERS (Funções Arrow para preservar o 'this')
|
|
15118
|
+
// ==========================================
|
|
15119
|
+
// 1. LOST / RETURN FOCUS
|
|
15120
|
+
this.handleVisibilityChange = () => {
|
|
15121
|
+
if (document.visibilityState === "visible") {
|
|
15122
|
+
this.handleReturnFocus();
|
|
15123
|
+
} else {
|
|
15124
|
+
this.handleLostFocus();
|
|
15125
|
+
}
|
|
15126
|
+
};
|
|
15127
|
+
this.handleLostFocus = () => {
|
|
15128
|
+
if (this.getRelativeTime() > 1e4) {
|
|
15129
|
+
const alertPayload = {
|
|
15130
|
+
begin: this.getRelativeTime(),
|
|
15131
|
+
end: 0,
|
|
15132
|
+
alert: 25 /* FocusOff */,
|
|
15133
|
+
type: 3 /* Screen */
|
|
15134
|
+
};
|
|
15135
|
+
this.onLostFocusCallback(alertPayload);
|
|
15136
|
+
this.alerts.push(alertPayload);
|
|
15137
|
+
}
|
|
15138
|
+
};
|
|
15139
|
+
this.handleReturnFocus = () => {
|
|
15140
|
+
const lastAlert = this.alerts[this.alerts.length - 1];
|
|
15141
|
+
if (lastAlert) {
|
|
15142
|
+
this.onFocusCallback({
|
|
15143
|
+
begin: lastAlert.begin,
|
|
15144
|
+
end: this.getRelativeTime(),
|
|
15145
|
+
alert: 25 /* FocusOff */,
|
|
15146
|
+
type: 3 /* Screen */
|
|
15147
|
+
});
|
|
15148
|
+
lastAlert.end = this.getRelativeTime();
|
|
15149
|
+
}
|
|
15150
|
+
};
|
|
15151
|
+
// 2. SPLIT SCREEN DETECTION
|
|
15152
|
+
this.handleResize = () => {
|
|
15153
|
+
clearTimeout(this.resizeTimeout);
|
|
15154
|
+
this.resizeTimeout = setTimeout(() => {
|
|
15155
|
+
const screenWidth = window.screen.availWidth;
|
|
15156
|
+
const windowWidth = window.outerWidth;
|
|
15157
|
+
if (windowWidth < screenWidth * 0.85) {
|
|
15158
|
+
const msg = `Split Screen Detectado: Janela ocupa ${(windowWidth / screenWidth * 100).toFixed(0)}% da tela`;
|
|
15159
|
+
this.createAlert(43 /* SplitScreen */, 3 /* Screen */, msg);
|
|
15160
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
15161
|
+
status: "ALERT",
|
|
15162
|
+
description: msg,
|
|
15163
|
+
type: "split_screen"
|
|
15164
|
+
});
|
|
15165
|
+
}
|
|
15166
|
+
}, 1e3);
|
|
15167
|
+
};
|
|
15168
|
+
// 3. DETECÇÃO DE INATIVIDADE (NO INPUT)
|
|
15169
|
+
this.handleUserActivity = (e3) => {
|
|
15170
|
+
this.lastActivityTime = Date.now();
|
|
15171
|
+
console.log("\u{1F449} handleUserActivity", e3);
|
|
15172
|
+
};
|
|
15173
|
+
// 5. CLIPBOARD HANDLERS
|
|
15174
|
+
this.handleCopy = (e3) => {
|
|
15175
|
+
e3.preventDefault();
|
|
15176
|
+
const msg = "Tentativa de Copiar (Clipboard)";
|
|
15177
|
+
this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
|
|
15178
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
15179
|
+
status: "ALERT",
|
|
15180
|
+
description: msg,
|
|
15181
|
+
type: "clipboard_copy"
|
|
15182
|
+
});
|
|
15183
|
+
};
|
|
15184
|
+
this.handleCut = (e3) => {
|
|
15185
|
+
e3.preventDefault();
|
|
15186
|
+
const msg = "Tentativa de Recortar (Clipboard)";
|
|
15187
|
+
this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
|
|
15188
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
15189
|
+
status: "ALERT",
|
|
15190
|
+
description: msg,
|
|
15191
|
+
type: "clipboard_cut"
|
|
15192
|
+
});
|
|
15193
|
+
};
|
|
15194
|
+
this.handlePaste = (e3) => {
|
|
15195
|
+
e3.preventDefault();
|
|
15196
|
+
const msg = "Tentativa de Colar (Clipboard)";
|
|
15197
|
+
this.createAlert(42 /* ClipBoardUse */, 3 /* Screen */, msg);
|
|
15198
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
15199
|
+
status: "ALERT",
|
|
15200
|
+
description: msg,
|
|
15201
|
+
type: "clipboard_paste"
|
|
15202
|
+
});
|
|
15203
|
+
};
|
|
15043
15204
|
this.onLostFocusCallback = options.onLostFocusCallback;
|
|
15044
15205
|
this.onFocusCallback = options.onFocusCallback;
|
|
15206
|
+
this.onRealtimeAlertCallback = options.onRealtimeAlertCallback;
|
|
15045
15207
|
this.optionsProctoring = optionsProctoring;
|
|
15046
15208
|
}
|
|
15047
15209
|
async startRecording() {
|
|
15048
15210
|
this.startTime = new Date(Date.now());
|
|
15049
|
-
|
|
15050
|
-
|
|
15051
|
-
window.addEventListener("focus", () => this.onReturnFocus());
|
|
15052
|
-
}
|
|
15211
|
+
this.alerts = [];
|
|
15212
|
+
this.attachListeners();
|
|
15053
15213
|
}
|
|
15054
15214
|
async pauseRecording() {
|
|
15055
|
-
|
|
15056
|
-
window.removeEventListener("focus", () => this.onReturnFocus());
|
|
15215
|
+
this.detachListeners();
|
|
15057
15216
|
}
|
|
15058
15217
|
async resumeRecording() {
|
|
15059
|
-
|
|
15060
|
-
window.addEventListener("blur", () => this.onLostFocus());
|
|
15061
|
-
window.addEventListener("focus", () => this.onReturnFocus());
|
|
15062
|
-
}
|
|
15218
|
+
this.attachListeners();
|
|
15063
15219
|
}
|
|
15064
15220
|
async stopRecording() {
|
|
15065
|
-
|
|
15066
|
-
window.removeEventListener("focus", () => this.onReturnFocus());
|
|
15221
|
+
this.detachListeners();
|
|
15067
15222
|
}
|
|
15068
15223
|
async saveOnSession(session) {
|
|
15069
15224
|
this.alerts.forEach((alert) => {
|
|
15070
15225
|
session.addAlert(alert);
|
|
15071
15226
|
});
|
|
15072
15227
|
}
|
|
15073
|
-
|
|
15074
|
-
|
|
15075
|
-
|
|
15076
|
-
|
|
15077
|
-
|
|
15078
|
-
|
|
15079
|
-
|
|
15080
|
-
|
|
15081
|
-
|
|
15082
|
-
|
|
15083
|
-
|
|
15084
|
-
|
|
15085
|
-
|
|
15086
|
-
|
|
15087
|
-
|
|
15088
|
-
|
|
15228
|
+
// ==========================================
|
|
15229
|
+
// GERENCIAMENTO DE LISTENERS
|
|
15230
|
+
// ==========================================
|
|
15231
|
+
attachListeners() {
|
|
15232
|
+
if (this.optionsProctoring.captureScreen) {
|
|
15233
|
+
window.addEventListener("visibilitychange", this.handleVisibilityChange);
|
|
15234
|
+
window.addEventListener("resize", this.handleResize);
|
|
15235
|
+
window.document.addEventListener("copy", this.handleCopy);
|
|
15236
|
+
window.document.addEventListener("cut", this.handleCut);
|
|
15237
|
+
window.document.addEventListener("paste", this.handlePaste);
|
|
15238
|
+
}
|
|
15239
|
+
}
|
|
15240
|
+
detachListeners() {
|
|
15241
|
+
window.removeEventListener("visibilitychange", this.handleVisibilityChange);
|
|
15242
|
+
window.removeEventListener("resize", this.handleResize);
|
|
15243
|
+
window.document.removeEventListener("copy", this.handleCopy);
|
|
15244
|
+
window.document.removeEventListener("cut", this.handleCut);
|
|
15245
|
+
window.document.removeEventListener("paste", this.handlePaste);
|
|
15246
|
+
}
|
|
15247
|
+
// private startIdleChecker() {
|
|
15248
|
+
// this.stopIdleChecker(); // Garante que não tenha duplicação
|
|
15249
|
+
// this.idleCheckInterval = setInterval(() => {
|
|
15250
|
+
// const idleTime = Date.now() - this.lastActivityTime;
|
|
15251
|
+
// if (idleTime > this.IDLE_THRESHOLD_MS) {
|
|
15252
|
+
// // Verifica se já não mandamos esse alerta recentemente para não floodar
|
|
15253
|
+
// const lastAlert = this.alerts[this.alerts.length - 1];
|
|
15254
|
+
// // Se o último alerta não foi de inatividade ou foi há muito tempo, cria novo
|
|
15255
|
+
// if (!lastAlert || lastAlert.alert !== AlertCategory.ForbiddenAction || (this.getRelativeTime() - lastAlert.begin > 5000)) {
|
|
15256
|
+
// const msg = "Usuário inativo por muito tempo";
|
|
15257
|
+
// // Assumindo ForbiddenAction ou outra categoria adequada
|
|
15258
|
+
// this.createAlert(AlertCategory.ForbiddenAction, AlertType.System, msg);
|
|
15259
|
+
// this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
15260
|
+
// status: 'ALERT',
|
|
15261
|
+
// description: msg,
|
|
15262
|
+
// type: 'user_idle'
|
|
15263
|
+
// });
|
|
15264
|
+
// }
|
|
15265
|
+
// }
|
|
15266
|
+
// }, 5000); // Checa a cada 5 segundos
|
|
15267
|
+
// }
|
|
15268
|
+
// private stopIdleChecker() {
|
|
15269
|
+
// if (this.idleCheckInterval) {
|
|
15270
|
+
// clearInterval(this.idleCheckInterval);
|
|
15271
|
+
// this.idleCheckInterval = null;
|
|
15272
|
+
// }
|
|
15273
|
+
// }
|
|
15274
|
+
// 4. BACKGROUND EVENTS (Eventos disparados manualmente)
|
|
15275
|
+
addBackgroundEvent(description, category = 200 /* System */) {
|
|
15276
|
+
this.createAlert(category, 3 /* Screen */, description);
|
|
15277
|
+
this.onRealtimeAlertCallback && this.onRealtimeAlertCallback({
|
|
15278
|
+
status: "ALERT",
|
|
15279
|
+
description,
|
|
15280
|
+
type: "background_event"
|
|
15281
|
+
});
|
|
15089
15282
|
}
|
|
15090
|
-
|
|
15091
|
-
|
|
15092
|
-
|
|
15093
|
-
|
|
15094
|
-
|
|
15095
|
-
|
|
15096
|
-
|
|
15097
|
-
|
|
15098
|
-
|
|
15099
|
-
|
|
15100
|
-
|
|
15283
|
+
// ==========================================
|
|
15284
|
+
// HELPERS
|
|
15285
|
+
// ==========================================
|
|
15286
|
+
getRelativeTime() {
|
|
15287
|
+
return Date.now() - this.startTime.getTime();
|
|
15288
|
+
}
|
|
15289
|
+
createAlert(category, type, description) {
|
|
15290
|
+
this.alerts.push({
|
|
15291
|
+
begin: this.getRelativeTime(),
|
|
15292
|
+
end: this.getRelativeTime(),
|
|
15293
|
+
// Eventos pontuais começam e terminam no mesmo ms (exceto focus)
|
|
15294
|
+
alert: category,
|
|
15295
|
+
type
|
|
15296
|
+
// description: description // Se sua interface Alert suportar descrição, descomente
|
|
15297
|
+
});
|
|
15101
15298
|
}
|
|
15102
15299
|
addAlert({ alert, type }) {
|
|
15103
15300
|
this.alerts.push({
|
|
@@ -21314,7 +21511,8 @@ var _ExternalCameraChecker = class _ExternalCameraChecker {
|
|
|
21314
21511
|
this.connection = new HubConnectionBuilder().withUrl(hubUrl, {
|
|
21315
21512
|
accessTokenFactory: () => this.context.token,
|
|
21316
21513
|
transport: HttpTransportType.WebSockets,
|
|
21317
|
-
withCredentials: false
|
|
21514
|
+
withCredentials: false,
|
|
21515
|
+
skipNegotiation: true
|
|
21318
21516
|
}).withAutomaticReconnect().configureLogging(LogLevel.Information).build();
|
|
21319
21517
|
this.connection.on(
|
|
21320
21518
|
"ReceiveMessage",
|
|
@@ -21520,7 +21718,6 @@ var Proctoring = class {
|
|
|
21520
21718
|
noiseLimit: 40
|
|
21521
21719
|
},
|
|
21522
21720
|
imageBehaviourParameters: {
|
|
21523
|
-
frames: 40,
|
|
21524
21721
|
useUploadImage: true,
|
|
21525
21722
|
uploadInterval: 20,
|
|
21526
21723
|
saveVideo: true
|
|
@@ -21531,8 +21728,8 @@ var Proctoring = class {
|
|
|
21531
21728
|
detectCellPhone: false,
|
|
21532
21729
|
detectNoise: false,
|
|
21533
21730
|
detectSpeech: false,
|
|
21534
|
-
realtimePackageSize:
|
|
21535
|
-
|
|
21731
|
+
realtimePackageSize: 10,
|
|
21732
|
+
realtimeCaptureInterval: 2
|
|
21536
21733
|
}
|
|
21537
21734
|
};
|
|
21538
21735
|
this.proctoringId = "";
|
|
@@ -21668,6 +21865,29 @@ var Proctoring = class {
|
|
|
21668
21865
|
return null;
|
|
21669
21866
|
}
|
|
21670
21867
|
}
|
|
21868
|
+
// metodo para fechar o alerta realtime e fica tentando verificar a face até 5 vezes
|
|
21869
|
+
async stopRealtimeAlert(alert) {
|
|
21870
|
+
const verifyMaxRetries = 3;
|
|
21871
|
+
const verifyFace = async (verifyCount) => {
|
|
21872
|
+
if (verifyCount > verifyMaxRetries) return;
|
|
21873
|
+
try {
|
|
21874
|
+
var response = await this.backend.stopRealtimeAlert({
|
|
21875
|
+
proctoringId: this.proctoringId,
|
|
21876
|
+
begin: alert.begin,
|
|
21877
|
+
end: alert.end,
|
|
21878
|
+
warningCategoryEnum: this.convertRealtimeTypeToWarningType(alert.type),
|
|
21879
|
+
alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64(),
|
|
21880
|
+
retry: verifyCount < verifyMaxRetries - 1 ? true : false
|
|
21881
|
+
});
|
|
21882
|
+
console.log("response stopRealtimeAlert", response);
|
|
21883
|
+
return response;
|
|
21884
|
+
} catch (error) {
|
|
21885
|
+
console.log("error stopRealtimeAlert", error);
|
|
21886
|
+
return verifyFace(verifyCount + 1);
|
|
21887
|
+
}
|
|
21888
|
+
};
|
|
21889
|
+
await verifyFace(1);
|
|
21890
|
+
}
|
|
21671
21891
|
async onRealtimeAlerts(options = {}) {
|
|
21672
21892
|
this.setOnLostFocusAlertRecorderCallback();
|
|
21673
21893
|
this.setOnFocusAlertRecorderCallback();
|
|
@@ -21682,13 +21902,7 @@ var Proctoring = class {
|
|
|
21682
21902
|
alert: this.convertRealtimeCategoryToAlertCategory(response.category)
|
|
21683
21903
|
});
|
|
21684
21904
|
} else if (response.status === "OK") {
|
|
21685
|
-
await this.
|
|
21686
|
-
proctoringId: this.proctoringId,
|
|
21687
|
-
begin: response.begin,
|
|
21688
|
-
end: response.end,
|
|
21689
|
-
warningCategoryEnum: this.convertRealtimeTypeToWarningType(response.type),
|
|
21690
|
-
alertImageBase64: await this.allRecorders.cameraRecorder.getCurrentImageBase64()
|
|
21691
|
-
});
|
|
21905
|
+
await this.stopRealtimeAlert(response);
|
|
21692
21906
|
}
|
|
21693
21907
|
}
|
|
21694
21908
|
};
|
|
@@ -21738,7 +21952,8 @@ var Proctoring = class {
|
|
|
21738
21952
|
const alertRecorder = new AlertRecorder(
|
|
21739
21953
|
{
|
|
21740
21954
|
onFocusCallback: (response) => this.onFocusAlertRecorderCallback(response),
|
|
21741
|
-
onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response)
|
|
21955
|
+
onLostFocusCallback: (response) => this.onLostFocusAlertRecorderCallback(response),
|
|
21956
|
+
onRealtimeAlertCallback: (response) => this.onRealtimeAlertsCallback(response)
|
|
21742
21957
|
},
|
|
21743
21958
|
options
|
|
21744
21959
|
);
|
|
@@ -21846,11 +22061,23 @@ Navigator: ${JSON.stringify(_navigator2)}`
|
|
|
21846
22061
|
startResponse.screenStream = this.allRecorders.screenRecorder.screenStream;
|
|
21847
22062
|
}
|
|
21848
22063
|
this.state = "Recording" /* Recording */;
|
|
21849
|
-
|
|
22064
|
+
let verifyFirstFaceIntervalCount = 0;
|
|
22065
|
+
let verifyingFace = false;
|
|
22066
|
+
this.verifyFirstFaceInterval = setInterval(async () => {
|
|
21850
22067
|
if (this.sessionOptions.proctoringType === "REALTIME") {
|
|
21851
|
-
|
|
22068
|
+
if (verifyingFace) return;
|
|
22069
|
+
verifyingFace = true;
|
|
22070
|
+
verifyFirstFaceIntervalCount++;
|
|
22071
|
+
try {
|
|
22072
|
+
var response = await this.backend.verifyFace(this.proctoringId, await this.allRecorders.cameraRecorder.getCurrentImageBase64(), verifyFirstFaceIntervalCount > 5 ? false : true);
|
|
22073
|
+
verifyingFace = false;
|
|
22074
|
+
clearInterval(this.verifyFirstFaceInterval);
|
|
22075
|
+
} catch (error) {
|
|
22076
|
+
verifyingFace = false;
|
|
22077
|
+
return;
|
|
22078
|
+
}
|
|
21852
22079
|
}
|
|
21853
|
-
},
|
|
22080
|
+
}, 1500);
|
|
21854
22081
|
return startResponse;
|
|
21855
22082
|
} catch (error) {
|
|
21856
22083
|
console.log(error);
|
|
@@ -21987,6 +22214,9 @@ Upload Services: ${uploaderServices}`,
|
|
|
21987
22214
|
await this.backend.externalCameraFinish(externalSessionId);
|
|
21988
22215
|
}
|
|
21989
22216
|
}
|
|
22217
|
+
if (this.verifyFirstFaceInterval) {
|
|
22218
|
+
clearInterval(this.verifyFirstFaceInterval);
|
|
22219
|
+
}
|
|
21990
22220
|
this.state = "Stop" /* Stop */;
|
|
21991
22221
|
} catch (error) {
|
|
21992
22222
|
await this.cancel();
|
|
@@ -22321,12 +22551,12 @@ function useProctoring(proctoringOptions, enviromentConfig = "prod") {
|
|
|
22321
22551
|
const photo = new CapturePhoto();
|
|
22322
22552
|
const login = proctoring.login.bind(proctoring);
|
|
22323
22553
|
const originalStart = proctoring.start.bind(proctoring);
|
|
22324
|
-
const start = async (parameters2) => {
|
|
22554
|
+
const start = async (parameters2, videoOptions) => {
|
|
22325
22555
|
const deviceResult = checker.getDeviceCheckResult();
|
|
22326
22556
|
if (deviceResult) {
|
|
22327
22557
|
proctoring.setDeviceCheckData(deviceResult);
|
|
22328
22558
|
}
|
|
22329
|
-
return originalStart(parameters2);
|
|
22559
|
+
return originalStart(parameters2, videoOptions);
|
|
22330
22560
|
};
|
|
22331
22561
|
const finish = proctoring.finish.bind(proctoring);
|
|
22332
22562
|
const pause = proctoring.pause.bind(proctoring);
|