easyproctor-hml 0.0.41 → 0.0.43

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.
@@ -9,3 +9,4 @@ export declare const PROCTORING_NOT_STARTED = "proctoring_not_started";
9
9
  export declare const PROCTORING_RUNNING = "proctoring_running";
10
10
  export declare const NO_VIDEOS_RECORDED = "no_videos_recorded";
11
11
  export declare const PERMISSIONS_NOT_GRANTED = "permissions_not_granted";
12
+ export declare const ANOTHER_STREAM_ACTIVE = "another_stream_active";
package/esm/index.js CHANGED
@@ -22429,7 +22429,8 @@ var eventNames = {
22429
22429
  FINISH: "finish",
22430
22430
  ERROR: "error",
22431
22431
  UPLOAD_VIDEO: "upload_video",
22432
- BUFFER_SIZE: "buffer_size"
22432
+ BUFFER_SIZE: "buffer_size",
22433
+ ANOTHER_STREAM: "another_stream"
22433
22434
  };
22434
22435
  var trackError = (exception) => insights.trackException({ exception });
22435
22436
  var registerCustomEvent = (eventName, properties) => insights.trackEvent({ name: eventName, properties });
@@ -22445,7 +22446,8 @@ var trackers = {
22445
22446
  registerChangeDevice: (proctoringId, inOrOut, devicesChanged) => registerCustomEvent(eventNames.UPLOAD_VIDEO, { proctoringId, inOrOut, devicesChanged }),
22446
22447
  registerStopSharingScreen: (proctoringId, description) => registerCustomEvent(eventNames.UPLOAD_VIDEO, { proctoringId, description }),
22447
22448
  registerErrorRecorderRTC: (proctoringId, description) => registerCustomEvent(eventNames.UPLOAD_VIDEO, { proctoringId, description }),
22448
- registerOnBufferSizeError: (proctoringId, description) => registerCustomEvent(eventNames.BUFFER_SIZE, { proctoringId, description })
22449
+ registerOnBufferSizeError: (proctoringId, description) => registerCustomEvent(eventNames.BUFFER_SIZE, { proctoringId, description }),
22450
+ registerAnotherStream: (proctoringId, description) => registerCustomEvent(eventNames.ANOTHER_STREAM, { proctoringId, description })
22449
22451
  };
22450
22452
 
22451
22453
  // src/plugins/recorder.ts
@@ -24319,28 +24321,65 @@ var axios_default2 = axios_default;
24319
24321
  var AzureUploadService = class {
24320
24322
  constructor(proctoringId, backend) {
24321
24323
  this.backend = backend;
24324
+ this.imageUrlPackage = [];
24325
+ this.imageBatchNum = 0;
24326
+ this.contImages = 0;
24322
24327
  this.proctoringId = proctoringId;
24323
24328
  }
24324
- async upload(data, token) {
24329
+ async upload(data, token, image) {
24325
24330
  const { file, onProgress } = data;
24326
- console.log(file);
24327
24331
  try {
24328
24332
  const progressCallback = (e) => {
24329
24333
  const progress = e.loadedBytes / file.size * 100;
24330
24334
  onProgress && onProgress(Math.round(progress));
24331
24335
  };
24332
- const uploadUrl = await this.backend.getAzureSignedUrl(token, file, this.proctoringId);
24333
- const uploaded = await axios_default2.request({
24334
- url: uploadUrl,
24335
- method: "PUT",
24336
- headers: {
24337
- "x-ms-blob-type": "BlockBlob"
24338
- },
24339
- data: file,
24340
- onUploadProgress: (p) => {
24341
- progressCallback({ loadedBytes: p.loaded });
24336
+ let uploadUrl;
24337
+ if (image) {
24338
+ if (this.imageBatchNum === this.contImages) {
24339
+ const batchArray = [];
24340
+ for (let i = this.imageBatchNum; i < this.imageBatchNum + 20; i++) {
24341
+ batchArray.push({
24342
+ objectName: `${this.proctoringId}/${this.proctoringId}_${i + 1}.jpg`,
24343
+ contentType: "image/jpeg"
24344
+ });
24345
+ }
24346
+ const batchUrls = await this.backend.getAzureSignedUrlImage(token, batchArray);
24347
+ batchUrls.map((item) => {
24348
+ this.imageUrlPackage.push(item);
24349
+ });
24350
+ this.imageBatchNum += 20;
24342
24351
  }
24343
- }).then(() => true).catch(() => false);
24352
+ } else {
24353
+ uploadUrl = await this.backend.getAzureSignedUrl(token, file, this.proctoringId);
24354
+ }
24355
+ let uploaded = false;
24356
+ if (image) {
24357
+ uploaded = await axios_default2.request({
24358
+ url: this.imageUrlPackage[this.contImages],
24359
+ method: "PUT",
24360
+ headers: {
24361
+ "x-ms-blob-type": "BlockBlob"
24362
+ },
24363
+ data: file,
24364
+ onUploadProgress: (p) => {
24365
+ progressCallback({ loadedBytes: p.loaded });
24366
+ }
24367
+ }).then(() => true).catch(() => false).finally(() => {
24368
+ this.contImages++;
24369
+ });
24370
+ } else {
24371
+ uploaded = await axios_default2.request({
24372
+ url: uploadUrl,
24373
+ method: "PUT",
24374
+ headers: {
24375
+ "x-ms-blob-type": "BlockBlob"
24376
+ },
24377
+ data: file,
24378
+ onUploadProgress: (p) => {
24379
+ progressCallback({ loadedBytes: p.loaded });
24380
+ }
24381
+ }).then(() => true).catch(() => false);
24382
+ }
24344
24383
  return {
24345
24384
  storage: "AzureBlob" /* AzureBlob */,
24346
24385
  url: uploadUrl,
@@ -24356,10 +24395,23 @@ var AzureUploadService = class {
24356
24395
  }
24357
24396
  };
24358
24397
 
24398
+ // src/errors/errors.ts
24399
+ var NOT_SHARED_FIRST_SCREEN = "not_shared_first_screen";
24400
+ var NOT_SHARED_SCREEN = "not_shared_screen";
24401
+ var MULTIPLE_MONITORS_DETECTED = "multiple_monitors_detected";
24402
+ var PROCTORING_ALREADY_STARTED = "proctoring_already_started";
24403
+ var PROCTORING_NOT_STARTED = "proctoring_not_started";
24404
+ var ANOTHER_STREAM_ACTIVE = "another_stream_active";
24405
+
24359
24406
  // src/new-flow/recorders/CameraRecorder.ts
24360
24407
  var CameraRecorder = class {
24361
- constructor(options, videoOptions, backend, backendToken) {
24408
+ constructor(options, videoOptions, imageProctoring, backend, backendToken) {
24362
24409
  this.blobs = [];
24410
+ this.imageProctoring = {
24411
+ useUploadImage: false,
24412
+ uploadInterval: 20,
24413
+ saveVideo: true
24414
+ };
24363
24415
  this.options = {
24364
24416
  cameraId: void 0,
24365
24417
  microphoneId: void 0,
@@ -24377,11 +24429,21 @@ var CameraRecorder = class {
24377
24429
  this.videoOptions = videoOptions;
24378
24430
  this.backend = backend;
24379
24431
  this.backendToken = backendToken;
24432
+ imageProctoring && (this.imageProctoring = imageProctoring);
24380
24433
  }
24381
24434
  setProctoringId(proctoringId) {
24382
24435
  this.proctoringId = proctoringId;
24383
24436
  this.proctoringId && this.backend && (this.azureUpload = new AzureUploadService(this.proctoringId, this.backend));
24384
24437
  }
24438
+ configImageCapture() {
24439
+ this.video = document.createElement("video");
24440
+ this.canvas = document.createElement("canvas");
24441
+ this.video.srcObject = this.cameraStream;
24442
+ this.video.play();
24443
+ this.video.muted = true;
24444
+ this.canvas.width = 1280;
24445
+ this.canvas.height = 720;
24446
+ }
24385
24447
  async startRecording() {
24386
24448
  const { cameraId, microphoneId, onBufferSizeErrorCallback } = this.options;
24387
24449
  const constraints = {
@@ -24406,6 +24468,14 @@ var CameraRecorder = class {
24406
24468
  this.recordingPause = pauseRecording;
24407
24469
  this.recordingResume = resumeRecording;
24408
24470
  this.recordingStart();
24471
+ const settings = this.cameraStream.getVideoTracks()[0].getSettings();
24472
+ if (this.videoOptions.width !== settings.width || this.videoOptions.height !== settings.height) {
24473
+ trackers.registerAnotherStream(this.proctoringId, `Maybe have another stream active
24474
+ Video Options: ${this.videoOptions}
24475
+ Setting: ${settings}`);
24476
+ throw ANOTHER_STREAM_ACTIVE;
24477
+ }
24478
+ this.imageProctoring.useUploadImage && this.photoShotsCycle();
24409
24479
  }
24410
24480
  async stopRecording() {
24411
24481
  this.recordingStop && await this.recordingStop();
@@ -24417,17 +24487,44 @@ var CameraRecorder = class {
24417
24487
  async resumeRecording() {
24418
24488
  await this.recordingResume();
24419
24489
  }
24490
+ photoShotsCycle() {
24491
+ let imageFile;
24492
+ this.configImageCapture();
24493
+ this.imageInterval = setInterval(async () => {
24494
+ this.canvas.getContext("2d").drawImage(this.video, 0, 0, 1280, 720);
24495
+ const image_data_url = this.canvas.toDataURL("image/jpeg");
24496
+ imageFile = await this.getFile(image_data_url, `${this.proctoringId}_${this.imageCount + 1}.jpg`, "image/jpeg");
24497
+ if (imageFile && this.azureUpload && this.backendToken) {
24498
+ this.azureUpload.upload({
24499
+ file: imageFile
24500
+ }, this.backendToken, true);
24501
+ this.imageCount++;
24502
+ }
24503
+ }, this.imageProctoring.uploadInterval * 1e3);
24504
+ }
24420
24505
  async saveOnSession(session) {
24421
24506
  const settings = this.cameraStream.getVideoTracks()[0].getSettings();
24422
24507
  const settingsAudio = this.cameraStream.getAudioTracks()[0].getSettings();
24423
- session.addRecording({
24424
- device: `Audio
24508
+ if (this.imageProctoring.saveVideo) {
24509
+ session.addRecording({
24510
+ device: `Audio
24425
24511
  Sample Rate: ${settingsAudio.sampleRate}
24426
24512
  Sample Size: ${settingsAudio.sampleSize}`,
24427
- file: new File(this.blobs, `EP_${session.id}_camera_0.webm`, {
24428
- type: "video/webm"
24429
- }),
24430
- origin: "Camera" /* Camera */
24513
+ file: new File(this.blobs, `EP_${session.id}_camera_0.webm`, {
24514
+ type: "video/webm"
24515
+ }),
24516
+ origin: "Camera" /* Camera */
24517
+ });
24518
+ }
24519
+ }
24520
+ async getFile(file, name, type) {
24521
+ return new Promise((resolve, reject) => {
24522
+ file.length > 5 && fetch(file).then((res) => res.blob()).then((blob) => {
24523
+ const file2 = new File([blob], name, { type });
24524
+ resolve(file2);
24525
+ }).catch((e) => {
24526
+ reject(e);
24527
+ });
24431
24528
  });
24432
24529
  }
24433
24530
  };
@@ -24438,7 +24535,6 @@ var CapturePhoto = class {
24438
24535
  }
24439
24536
  async takePicture(title, subtitle, resolution = { width: 1080, height: 720 }) {
24440
24537
  this.resolution = resolution;
24441
- console.log(this.resolution);
24442
24538
  return await this.takePictureInterface(title, subtitle);
24443
24539
  }
24444
24540
  async startCapture(cameraId = "default") {
@@ -24463,7 +24559,6 @@ var CapturePhoto = class {
24463
24559
  cameraContainer.play();
24464
24560
  }
24465
24561
  shot() {
24466
- console.log("shot");
24467
24562
  const cameraContainer = document.querySelector("#cameraStream");
24468
24563
  const canvas = document.querySelector("#canvas");
24469
24564
  const picture = document.querySelector("#image");
@@ -25021,6 +25116,7 @@ var DeviceChecker = class {
25021
25116
  this.cameraRecorder = new CameraRecorder({
25022
25117
  cameraId: cameras == null ? void 0 : cameras.value,
25023
25118
  microphoneId: microphones == null ? void 0 : microphones.value,
25119
+ onBufferSizeError: false,
25024
25120
  onBufferSizeErrorCallback: () => {
25025
25121
  }
25026
25122
  }, {
@@ -25038,6 +25134,7 @@ var DeviceChecker = class {
25038
25134
  this.cameraRecorder = new CameraRecorder({
25039
25135
  cameraId: cameras == null ? void 0 : cameras.value,
25040
25136
  microphoneId: microphones == null ? void 0 : microphones.value,
25137
+ onBufferSizeError: false,
25041
25138
  onBufferSizeErrorCallback: () => {
25042
25139
  }
25043
25140
  }, {
@@ -25056,6 +25153,7 @@ var DeviceChecker = class {
25056
25153
  this.cameraRecorder = new CameraRecorder({
25057
25154
  cameraId: options.cameraId,
25058
25155
  microphoneId: options.microphoneId,
25156
+ onBufferSizeError: false,
25059
25157
  onBufferSizeErrorCallback: () => {
25060
25158
  }
25061
25159
  }, {
@@ -25081,12 +25179,6 @@ var DeviceChecker = class {
25081
25179
  }
25082
25180
  };
25083
25181
 
25084
- // src/errors/errors.ts
25085
- var NOT_SHARED_FIRST_SCREEN = "not_shared_first_screen";
25086
- var MULTIPLE_MONITORS_DETECTED = "multiple_monitors_detected";
25087
- var PROCTORING_ALREADY_STARTED = "proctoring_already_started";
25088
- var PROCTORING_NOT_STARTED = "proctoring_not_started";
25089
-
25090
25182
  // src/extension/extension.ts
25091
25183
  var Extension = class {
25092
25184
  constructor() {
@@ -25215,6 +25307,16 @@ var BackendService = class {
25215
25307
  });
25216
25308
  return insights2.data;
25217
25309
  }
25310
+ async getImageConfig(proctoringOptions) {
25311
+ const imageConfig = await this.makeRequestAxios({
25312
+ path: `/Client/image-config`,
25313
+ method: "GET",
25314
+ jwt: proctoringOptions.token
25315
+ }).catch((error) => {
25316
+ throw "N\xE3o foi poss\xEDvel realizar a requisi\xE7\xE3o, tente novamente mais tarde";
25317
+ });
25318
+ return imageConfig.data;
25319
+ }
25218
25320
  async getAzureBlobUrl(proctoringOptions) {
25219
25321
  const blobUrl = await this.makeRequestAxios({
25220
25322
  path: `/AzureKey/get-blob-url`,
@@ -25237,15 +25339,12 @@ var BackendService = class {
25237
25339
  });
25238
25340
  return urlAzure.data;
25239
25341
  }
25240
- async getAzureSignedUrlImage(token, file) {
25342
+ async getAzureSignedUrlImage(token, body) {
25241
25343
  const urlAzure = await this.makeRequestAxios({
25242
- path: `/AzureKey/signed-url`,
25344
+ path: `/AzureKey/signed-url-batch`,
25243
25345
  method: "POST",
25244
25346
  jwt: token,
25245
- body: {
25246
- objectName: file.name,
25247
- contentType: file.type
25248
- }
25347
+ body
25249
25348
  });
25250
25349
  return urlAzure.data;
25251
25350
  }
@@ -25481,9 +25580,9 @@ var ProctoringUploader = class {
25481
25580
  rec.upload.storage = result.storage;
25482
25581
  }
25483
25582
  if (result.storage === "AzureBlob" /* AzureBlob */) {
25484
- rec.upload.azureUrl = result.url;
25583
+ result.url && (rec.upload.azureUrl = result.url);
25485
25584
  } else {
25486
- rec.upload.awsUrl = result.url;
25585
+ result.url && (rec.upload.awsUrl = result.url);
25487
25586
  }
25488
25587
  }
25489
25588
  }
@@ -25658,7 +25757,7 @@ var ScreenRecorder = class {
25658
25757
  }
25659
25758
  async startRecording() {
25660
25759
  this.startTime = new Date(Date.now());
25661
- const { allowOnlyFirstMonitor, onStopSharingScreenCallback, onBufferSizeErrorCallback } = this.options;
25760
+ const { allowOnlyFirstMonitor, allowMultipleMonitors, onStopSharingScreenCallback, onBufferSizeErrorCallback } = this.options;
25662
25761
  const displayMediaStreamConstraints = {
25663
25762
  video: {
25664
25763
  cursor: "always",
@@ -25670,12 +25769,18 @@ var ScreenRecorder = class {
25670
25769
  this.screenStream = await navigator.mediaDevices.getDisplayMedia(displayMediaStreamConstraints);
25671
25770
  const tracks = this.screenStream.getVideoTracks();
25672
25771
  tracks[0].onended = onStopSharingScreenCallback;
25673
- console.log(tracks);
25772
+ const isFirefox = navigator.userAgent.indexOf("Firefox") > -1;
25773
+ if (isFirefox) {
25774
+ const hasMultipleMonitors = tracks.find((el) => el.label == "");
25775
+ if (hasMultipleMonitors && allowOnlyFirstMonitor || hasMultipleMonitors && !allowMultipleMonitors) {
25776
+ throw MULTIPLE_MONITORS_DETECTED;
25777
+ }
25778
+ }
25674
25779
  const sharedFirstScreen = tracks.find((el) => {
25675
25780
  return ["screen:0:0", "Primary Monitor"].includes(el.label);
25676
25781
  }) != null;
25677
25782
  const sharedScreen = tracks.find((el) => {
25678
- return ["screen:0:0", "Primary Monitor"].includes(el.label) || ["screen:"].some((substring) => el.label.includes(substring));
25783
+ return ["screen:0:0", "Primary Monitor", ""].includes(el.label) || ["screen:"].some((substring) => el.label.includes(substring));
25679
25784
  }) != null;
25680
25785
  const sharedNotAllowed = tracks.find((el) => {
25681
25786
  return ["web-contents-media-stream", "window"].some((substring) => el.label.includes(substring));
@@ -25686,6 +25791,10 @@ var ScreenRecorder = class {
25686
25791
  });
25687
25792
  throw NOT_SHARED_FIRST_SCREEN;
25688
25793
  } else if (!sharedScreen || sharedNotAllowed) {
25794
+ tracks.forEach((el) => {
25795
+ el.stop();
25796
+ });
25797
+ throw NOT_SHARED_SCREEN;
25689
25798
  }
25690
25799
  const { startRecording, stopRecording } = recorder(this.screenStream, this.blobs, this.proctoringId, this.options.onBufferSizeError, onBufferSizeErrorCallback);
25691
25800
  this.recordingStart = startRecording;
@@ -25843,13 +25952,16 @@ function versionVerify() {
25843
25952
  var Proctoring = class {
25844
25953
  constructor(context) {
25845
25954
  this.context = context;
25955
+ this.imageProctoring = {
25956
+ useUploadImage: false,
25957
+ uploadInterval: 20,
25958
+ saveVideo: true
25959
+ };
25846
25960
  this.proctoringId = "";
25847
25961
  this.insights = void 0;
25848
25962
  this.state = "Stop" /* Stop */;
25849
25963
  this.serviceType = "Upload" /* Upload */;
25850
25964
  this.onStopSharingScreenCallback = () => {
25851
- console.log("Stop sharing screen");
25852
- trackers.registerStopSharingScreen(this.proctoringId, "Stop sharing screen");
25853
25965
  };
25854
25966
  this.onLostFocusCallback = () => {
25855
25967
  };
@@ -25867,7 +25979,10 @@ var Proctoring = class {
25867
25979
  this.repositoryDevices = new IndexDbSessionRepository("EasyProctorDbDevices", "devices");
25868
25980
  }
25869
25981
  setOnStopSharingScreenCallback(cb) {
25870
- this.onStopSharingScreenCallback = () => cb();
25982
+ this.onStopSharingScreenCallback = () => {
25983
+ trackers.registerStopSharingScreen(this.proctoringId, "Stop sharing screen");
25984
+ cb();
25985
+ };
25871
25986
  }
25872
25987
  setOnLostFocusCallback(cb) {
25873
25988
  this.onLostFocusCallback = () => cb();
@@ -25886,7 +26001,7 @@ var Proctoring = class {
25886
26001
  this.onBufferSizeErrorCallback = () => cb();
25887
26002
  }
25888
26003
  createRecorders(options = getDefaultProctoringOptions) {
25889
- var _a2;
26004
+ var _a2, _b;
25890
26005
  const cameraRecorder = new CameraRecorder({
25891
26006
  cameraId: this.sessionOptions.cameraId,
25892
26007
  microphoneId: this.sessionOptions.microphoneId,
@@ -25895,10 +26010,11 @@ var Proctoring = class {
25895
26010
  }, {
25896
26011
  width: this.videoOptions.width,
25897
26012
  height: this.videoOptions.height
25898
- }, this.backend, this.context.token);
26013
+ }, this.imageProctoring, this.backend, this.context.token);
25899
26014
  const audioRecorder = new AudioRecorder();
25900
26015
  const screenRecorder = this.sessionOptions.captureScreen ? new ScreenRecorder({
25901
26016
  allowOnlyFirstMonitor: (_a2 = this.sessionOptions.allowOnlyFirstMonitor) != null ? _a2 : true,
26017
+ allowMultipleMonitors: (_b = this.sessionOptions.allowMultipleMonitors) != null ? _b : true,
25902
26018
  onStopSharingScreenCallback: () => this.onStopSharingScreenCallback(),
25903
26019
  onBufferSizeError: this.sessionOptions.onBufferSizeError,
25904
26020
  onBufferSizeErrorCallback: () => this.onBufferSizeErrorCallback()
@@ -25928,7 +26044,7 @@ var Proctoring = class {
25928
26044
  this.sessionOptions = { ...getDefaultProctoringOptions, ...options };
25929
26045
  this.videoOptions = validatePartialVideoOptions(_videoOptions);
25930
26046
  await this.initConfig();
25931
- await this.verifyMultipleMonitors(this.sessionOptions);
26047
+ this.sessionOptions.captureScreen && await this.verifyMultipleMonitors(this.sessionOptions);
25932
26048
  if (this.state != "Stop" /* Stop */) {
25933
26049
  throw PROCTORING_ALREADY_STARTED;
25934
26050
  }
@@ -25957,7 +26073,6 @@ Navigator: ${navigator}`);
25957
26073
  this.state = "Recording" /* Recording */;
25958
26074
  return startResponse;
25959
26075
  } catch (error) {
25960
- console.log(error);
25961
26076
  await this.cancel();
25962
26077
  trackers.registerStart(this.proctoringId, false, `Token: ${this.context.token}
25963
26078
  Version: ${versionVerify()}
@@ -26061,6 +26176,8 @@ Error: ` + error);
26061
26176
  async initConfig() {
26062
26177
  try {
26063
26178
  this.insights = await this.backend.getInsights(this.context);
26179
+ const imageConfig = await this.backend.getImageConfig(this.context);
26180
+ this.imageProctoring = { ...this.imageProctoring, ...imageConfig };
26064
26181
  this.insights ? init(this.insights) : () => {
26065
26182
  throw "Error on insights key";
26066
26183
  };
@@ -26094,7 +26211,6 @@ Error: ` + error);
26094
26211
  throw PROCTORING_NOT_STARTED;
26095
26212
  }
26096
26213
  await this.verifyMultipleMonitors(this.sessionOptions);
26097
- this.allRecorders = this.createRecorders(this.sessionOptions);
26098
26214
  await this.recorder.startAll();
26099
26215
  this.proctoringSession.recordings = [];
26100
26216
  await this.repository.save(this.proctoringSession);