@lookit/record 3.0.0 → 4.0.0
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 +125 -39
- package/dist/index.browser.js +96 -33
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +13 -13
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +95 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +42 -3
- package/dist/index.js +95 -32
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/consentVideo.spec.ts +38 -11
- package/src/consentVideo.ts +75 -18
- package/src/errors.ts +7 -27
- package/src/recorder.spec.ts +208 -1
- package/src/recorder.ts +36 -5
- package/src/videoConfig.spec.ts +115 -0
- package/src/videoConfig.ts +38 -1
package/dist/index.cjs
CHANGED
|
@@ -8,7 +8,7 @@ var Handlebars = require('handlebars');
|
|
|
8
8
|
|
|
9
9
|
var _package = {
|
|
10
10
|
name: "@lookit/record",
|
|
11
|
-
version: "
|
|
11
|
+
version: "4.0.0",
|
|
12
12
|
description: "Recording extensions and plugins for CHS studies.",
|
|
13
13
|
homepage: "https://github.com/lookit/lookit-jspsych#readme",
|
|
14
14
|
bugs: {
|
|
@@ -50,7 +50,7 @@ var _package = {
|
|
|
50
50
|
},
|
|
51
51
|
peerDependencies: {
|
|
52
52
|
"@lookit/data": "^0.2.0",
|
|
53
|
-
"@lookit/templates": "^2.
|
|
53
|
+
"@lookit/templates": "^2.1.0",
|
|
54
54
|
jspsych: "^8.0.3"
|
|
55
55
|
}
|
|
56
56
|
};
|
|
@@ -143,21 +143,10 @@ class CreateURLError extends Error {
|
|
|
143
143
|
this.name = "CreateURLError";
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
class
|
|
147
|
-
constructor() {
|
|
148
|
-
super("
|
|
149
|
-
this.name = "
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
class ButtonNotFoundError extends Error {
|
|
153
|
-
constructor(id) {
|
|
154
|
-
super(`"${id}" button not found.`);
|
|
155
|
-
this.name = "ButtonNotFoundError";
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
class ImageNotFoundError extends Error {
|
|
159
|
-
constructor(id) {
|
|
160
|
-
super(`"${id}" image not found.`);
|
|
146
|
+
class ElementNotFoundError extends Error {
|
|
147
|
+
constructor(id, tag) {
|
|
148
|
+
super(`"${id}" ${tag} not found.`);
|
|
149
|
+
this.name = "ElementNotFoundError";
|
|
161
150
|
}
|
|
162
151
|
}
|
|
163
152
|
|
|
@@ -177,8 +166,10 @@ class Recorder {
|
|
|
177
166
|
this.blobs = [];
|
|
178
167
|
this.localDownload = "false"?.toLowerCase() === "true";
|
|
179
168
|
this.webcam_element_id = "lookit-jspsych-webcam";
|
|
169
|
+
this.mimeType = "video/webm";
|
|
180
170
|
this.streamClone = this.stream.clone();
|
|
181
171
|
autoBind(this);
|
|
172
|
+
this.mimeType = this.recorder?.mimeType || this.mimeType;
|
|
182
173
|
}
|
|
183
174
|
get recorder() {
|
|
184
175
|
return this.jsPsych.pluginAPI.getCameraRecorder() || this.jsPsych.pluginAPI.getMicrophoneRecorder();
|
|
@@ -196,7 +187,11 @@ class Recorder {
|
|
|
196
187
|
this._s3 = value;
|
|
197
188
|
}
|
|
198
189
|
initializeRecorder(stream, opts) {
|
|
199
|
-
|
|
190
|
+
const recorder_options = {
|
|
191
|
+
...opts,
|
|
192
|
+
mimeType: this.mimeType
|
|
193
|
+
};
|
|
194
|
+
this.jsPsych.pluginAPI.initializeCameraRecorder(stream, recorder_options);
|
|
200
195
|
}
|
|
201
196
|
reset() {
|
|
202
197
|
if (this.stream.active) {
|
|
@@ -264,9 +259,9 @@ class Recorder {
|
|
|
264
259
|
this.recorder.stop();
|
|
265
260
|
this.stream.getTracks().map((t) => t.stop());
|
|
266
261
|
}
|
|
267
|
-
stop() {
|
|
262
|
+
stop(maintain_container_size = false) {
|
|
263
|
+
this.clearWebcamFeed(maintain_container_size);
|
|
268
264
|
this.stopTracks();
|
|
269
|
-
this.clearWebcamFeed();
|
|
270
265
|
if (!this.stopPromise) {
|
|
271
266
|
throw new NoStopPromiseError();
|
|
272
267
|
}
|
|
@@ -311,11 +306,20 @@ class Recorder {
|
|
|
311
306
|
link.click();
|
|
312
307
|
}
|
|
313
308
|
}
|
|
314
|
-
clearWebcamFeed() {
|
|
309
|
+
clearWebcamFeed(maintain_container_size) {
|
|
315
310
|
const webcam_feed_element = document.querySelector(
|
|
316
311
|
`#${this.webcam_element_id}`
|
|
317
312
|
);
|
|
318
313
|
if (webcam_feed_element) {
|
|
314
|
+
if (maintain_container_size) {
|
|
315
|
+
const parent_div = webcam_feed_element.parentElement;
|
|
316
|
+
if (parent_div) {
|
|
317
|
+
const width = webcam_feed_element.offsetWidth;
|
|
318
|
+
const height = webcam_feed_element.offsetHeight;
|
|
319
|
+
parent_div.style.height = `${height}px`;
|
|
320
|
+
parent_div.style.width = `${width}px`;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
319
323
|
webcam_feed_element.remove();
|
|
320
324
|
}
|
|
321
325
|
}
|
|
@@ -385,6 +389,11 @@ const _VideoConsentPlugin = class {
|
|
|
385
389
|
constructor(jsPsych) {
|
|
386
390
|
this.jsPsych = jsPsych;
|
|
387
391
|
this.video_container_id = "lookit-jspsych-video-container";
|
|
392
|
+
this.msg_container_id = "lookit-jspsych-video-msg-container";
|
|
393
|
+
this.uploadingMsg = null;
|
|
394
|
+
this.startingMsg = null;
|
|
395
|
+
this.recordingMsg = null;
|
|
396
|
+
this.notRecordingMsg = null;
|
|
388
397
|
this.jsPsych = jsPsych;
|
|
389
398
|
this.recorder = new Recorder(this.jsPsych);
|
|
390
399
|
}
|
|
@@ -396,13 +405,25 @@ const _VideoConsentPlugin = class {
|
|
|
396
405
|
this.stopButton(display);
|
|
397
406
|
this.playButton(display);
|
|
398
407
|
this.nextButton(display);
|
|
408
|
+
this.uploadingMsg = chsTemplates.translateString(
|
|
409
|
+
"exp-lookit-video-consent.Stopping-and-uploading"
|
|
410
|
+
);
|
|
411
|
+
this.startingMsg = chsTemplates.translateString(
|
|
412
|
+
"exp-lookit-video-consent.Starting-recorder"
|
|
413
|
+
);
|
|
414
|
+
this.recordingMsg = chsTemplates.translateString(
|
|
415
|
+
"exp-lookit-video-consent.Recording"
|
|
416
|
+
);
|
|
417
|
+
this.notRecordingMsg = chsTemplates.translateString(
|
|
418
|
+
"exp-lookit-video-consent.Not-recording"
|
|
419
|
+
);
|
|
399
420
|
}
|
|
400
421
|
getVideoContainer(display) {
|
|
401
422
|
const videoContainer = display.querySelector(
|
|
402
423
|
`div#${this.video_container_id}`
|
|
403
424
|
);
|
|
404
425
|
if (!videoContainer) {
|
|
405
|
-
throw new
|
|
426
|
+
throw new ElementNotFoundError(this.video_container_id, "div");
|
|
406
427
|
}
|
|
407
428
|
return videoContainer;
|
|
408
429
|
}
|
|
@@ -413,14 +434,31 @@ const _VideoConsentPlugin = class {
|
|
|
413
434
|
}
|
|
414
435
|
playbackFeed(display) {
|
|
415
436
|
const videoContainer = this.getVideoContainer(display);
|
|
416
|
-
this.recorder.insertPlaybackFeed(
|
|
437
|
+
this.recorder.insertPlaybackFeed(
|
|
438
|
+
videoContainer,
|
|
439
|
+
this.onPlaybackEnded(display)
|
|
440
|
+
);
|
|
417
441
|
}
|
|
418
|
-
|
|
442
|
+
getMessageContainer(display) {
|
|
443
|
+
const msgContainer = display.querySelector(
|
|
444
|
+
`div#${this.msg_container_id}`
|
|
445
|
+
);
|
|
446
|
+
if (!msgContainer) {
|
|
447
|
+
throw new ElementNotFoundError(this.msg_container_id, "div");
|
|
448
|
+
}
|
|
449
|
+
return msgContainer;
|
|
450
|
+
}
|
|
451
|
+
addMessage(display, message) {
|
|
452
|
+
const msgContainer = this.getMessageContainer(display);
|
|
453
|
+
msgContainer.innerHTML = message;
|
|
454
|
+
}
|
|
455
|
+
onPlaybackEnded(display) {
|
|
419
456
|
return () => {
|
|
420
457
|
const next = this.getButton(display, "next");
|
|
421
458
|
const play = this.getButton(display, "play");
|
|
422
459
|
const record = this.getButton(display, "record");
|
|
423
460
|
this.recordFeed(display);
|
|
461
|
+
this.addMessage(display, this.notRecordingMsg);
|
|
424
462
|
next.disabled = false;
|
|
425
463
|
play.disabled = false;
|
|
426
464
|
record.disabled = false;
|
|
@@ -429,14 +467,14 @@ const _VideoConsentPlugin = class {
|
|
|
429
467
|
getButton(display, id) {
|
|
430
468
|
const btn = display.querySelector(`button#${id}`);
|
|
431
469
|
if (!btn) {
|
|
432
|
-
throw new
|
|
470
|
+
throw new ElementNotFoundError(id, "button");
|
|
433
471
|
}
|
|
434
472
|
return btn;
|
|
435
473
|
}
|
|
436
474
|
getImg(display, id) {
|
|
437
475
|
const img = display.querySelector(`img#${id}`);
|
|
438
476
|
if (!img) {
|
|
439
|
-
throw new
|
|
477
|
+
throw new ElementNotFoundError(id, "img");
|
|
440
478
|
}
|
|
441
479
|
return img;
|
|
442
480
|
}
|
|
@@ -446,12 +484,14 @@ const _VideoConsentPlugin = class {
|
|
|
446
484
|
const play = this.getButton(display, "play");
|
|
447
485
|
const next = this.getButton(display, "next");
|
|
448
486
|
record.addEventListener("click", async () => {
|
|
487
|
+
this.addMessage(display, this.startingMsg);
|
|
449
488
|
record.disabled = true;
|
|
450
|
-
stop.disabled = false;
|
|
451
489
|
play.disabled = true;
|
|
452
490
|
next.disabled = true;
|
|
453
|
-
this.getImg(display, "record-icon").style.visibility = "visible";
|
|
454
491
|
await this.recorder.start(true, _VideoConsentPlugin.info.name);
|
|
492
|
+
this.getImg(display, "record-icon").style.visibility = "visible";
|
|
493
|
+
this.addMessage(display, this.recordingMsg);
|
|
494
|
+
stop.disabled = false;
|
|
455
495
|
});
|
|
456
496
|
}
|
|
457
497
|
playButton(display) {
|
|
@@ -469,11 +509,14 @@ const _VideoConsentPlugin = class {
|
|
|
469
509
|
const play = this.getButton(display, "play");
|
|
470
510
|
stop.addEventListener("click", async () => {
|
|
471
511
|
stop.disabled = true;
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
await this.recorder.stop();
|
|
512
|
+
this.addMessage(display, this.uploadingMsg);
|
|
513
|
+
await this.recorder.stop(true);
|
|
475
514
|
this.recorder.reset();
|
|
476
515
|
this.recordFeed(display);
|
|
516
|
+
this.getImg(display, "record-icon").style.visibility = "hidden";
|
|
517
|
+
this.addMessage(display, this.notRecordingMsg);
|
|
518
|
+
play.disabled = false;
|
|
519
|
+
record.disabled = false;
|
|
477
520
|
});
|
|
478
521
|
}
|
|
479
522
|
nextButton(display) {
|
|
@@ -640,6 +683,7 @@ class VideoConfigPlugin {
|
|
|
640
683
|
this.minVolume = 0.1;
|
|
641
684
|
this.micChecked = false;
|
|
642
685
|
this.processorNode = null;
|
|
686
|
+
this.mimeType = "video/webm";
|
|
643
687
|
this.addHtmlContent = (trial) => {
|
|
644
688
|
this.display_el.innerHTML = chsTemplates.videoConfig(trial, html_params);
|
|
645
689
|
};
|
|
@@ -782,7 +826,12 @@ class VideoConfigPlugin {
|
|
|
782
826
|
return { cameras: unique_cameras, mics: unique_mics };
|
|
783
827
|
};
|
|
784
828
|
this.initializeAndCreateRecorder = (stream, opts) => {
|
|
785
|
-
this.
|
|
829
|
+
this.mimeType = this.getCompatibleMimeType() || this.mimeType;
|
|
830
|
+
const recorder_options = {
|
|
831
|
+
...opts,
|
|
832
|
+
mimeType: this.mimeType
|
|
833
|
+
};
|
|
834
|
+
this.jsPsych.pluginAPI.initializeCameraRecorder(stream, recorder_options);
|
|
786
835
|
this.recorder = new Recorder(this.jsPsych);
|
|
787
836
|
};
|
|
788
837
|
this.checkMic = async (minVol = this.minVolume) => {
|
|
@@ -935,6 +984,20 @@ class VideoConfigPlugin {
|
|
|
935
984
|
};
|
|
936
985
|
});
|
|
937
986
|
}
|
|
987
|
+
getCompatibleMimeType() {
|
|
988
|
+
const mime_types = [
|
|
989
|
+
"video/webm;codecs=vp9,opus",
|
|
990
|
+
"video/webm;codecs=vp8,opus"
|
|
991
|
+
];
|
|
992
|
+
let mime_type_index = 0;
|
|
993
|
+
while (mime_type_index < mime_types.length) {
|
|
994
|
+
if (MediaRecorder.isTypeSupported(mime_types[mime_type_index])) {
|
|
995
|
+
return mime_types[mime_type_index];
|
|
996
|
+
}
|
|
997
|
+
mime_type_index++;
|
|
998
|
+
}
|
|
999
|
+
return null;
|
|
1000
|
+
}
|
|
938
1001
|
}
|
|
939
1002
|
VideoConfigPlugin.info = info;
|
|
940
1003
|
|