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 +11 -0
- package/esm/index.js +69 -77
- package/index.js +69 -77
- package/interfaces/ParamsConfig.d.ts +1 -1
- package/new-flow/proctoring/ProctoringSession.d.ts +3 -3
- package/new-flow/recorders/AlertRecorder.d.ts +1 -0
- package/new-flow/recorders/CameraRecorder.d.ts +1 -2
- package/package.json +1 -1
- package/proctoring/proctoring.d.ts +1 -0
- package/unpkg/easyproctor.min.js +17 -17
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
|
-
|
|
12786
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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 (
|
|
12999
|
-
initSend = true;
|
|
13005
|
+
if (packSize == this.imageCount) {
|
|
13000
13006
|
this.imageCount = 0;
|
|
13001
|
-
|
|
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
|
|
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_" +
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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("
|
|
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("
|
|
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,
|
|
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:
|
|
21719
|
-
|
|
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.
|
|
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
|
-
|
|
30883
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
-
|
|
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 (
|
|
31096
|
-
initSend = true;
|
|
31102
|
+
if (packSize == this.imageCount) {
|
|
31097
31103
|
this.imageCount = 0;
|
|
31098
|
-
|
|
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
|
|
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_" +
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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("
|
|
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("
|
|
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,
|
|
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:
|
|
36968
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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;
|