@lookit/record 0.0.4 → 2.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 +87 -85
- package/dist/consentVideo.d.ts +10 -8
- package/dist/index.browser.js +86 -354
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +2 -2
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +85 -353
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +85 -353
- package/dist/index.js.map +1 -1
- package/dist/stop.d.ts +15 -4
- package/dist/{video_config.d.ts → videoConfig.d.ts} +36 -20
- package/package.json +3 -8
- package/src/consentVideo.ts +5 -4
- package/src/index.spec.ts +6 -2
- package/src/index.ts +1 -1
- package/src/stop.ts +11 -6
- package/src/{video_config.spec.ts → videoConfig.spec.ts} +70 -85
- package/src/{video_config.ts → videoConfig.ts} +70 -72
- package/src/{video_config_mic_check.spec.ts → videoConfig_mic_check.spec.ts} +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import chsTemplates from "@lookit/templates";
|
|
2
2
|
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
|
|
3
|
-
import
|
|
3
|
+
import checkmarkIcon from "../img/checkmark-icon.png";
|
|
4
4
|
import chromeInitialPrompt from "../img/chrome_initialprompt.png";
|
|
5
5
|
import chromeAlwaysAllow from "../img/chrome_step1_alwaysallow.png";
|
|
6
6
|
import chromePermissions from "../img/chrome_step1_permissions.png";
|
|
@@ -16,6 +16,7 @@ const info = <const>{
|
|
|
16
16
|
name: "video-config-plugin",
|
|
17
17
|
version: version,
|
|
18
18
|
parameters: {
|
|
19
|
+
locale: { type: ParameterType.STRING, default: "en-us" },
|
|
19
20
|
troubleshooting_intro: {
|
|
20
21
|
/**
|
|
21
22
|
* Optional string to appear at the start of the "Setup tips and
|
|
@@ -50,6 +51,31 @@ interface MediaDeviceInfo {
|
|
|
50
51
|
groupId: string;
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
export const html_params = {
|
|
55
|
+
webcam_container_id: "lookit-jspsych-webcam-container",
|
|
56
|
+
reload_button_id_text: "lookit-jspsych-reload-webcam",
|
|
57
|
+
reload_button_id_cam: "lookit-jspsych-reload-cam-mic",
|
|
58
|
+
camera_selection_id: "lookit-jspsych-which-webcam",
|
|
59
|
+
mic_selection_id: "lookit-jspsych-which-mic",
|
|
60
|
+
next_button_id: "lookit-jspsych-next",
|
|
61
|
+
error_msg_div_id: "lookit-jspsych-video-config-errors",
|
|
62
|
+
step1_id: "lookit-jspsych-step1",
|
|
63
|
+
step2_id: "lookit-jspsych-step2",
|
|
64
|
+
step3_id: "lookit-jspsych-step3",
|
|
65
|
+
step_complete_class: "lookit-jspsych-step-complete",
|
|
66
|
+
waiting_for_access_msg_id: "lookit-jspsych-waiting-for-access",
|
|
67
|
+
checking_mic_msg_id: "lookit-jspsych-checking-mic",
|
|
68
|
+
access_problem_msg_id: "lookit-jspsych-access-problem",
|
|
69
|
+
setup_problem_msg_id: "lookit-jspsych-setup-problem",
|
|
70
|
+
chromeInitialPrompt,
|
|
71
|
+
chromeAlwaysAllow,
|
|
72
|
+
chromePermissions,
|
|
73
|
+
firefoxInitialPrompt,
|
|
74
|
+
firefoxChooseDevice,
|
|
75
|
+
firefoxDevicesBlocked,
|
|
76
|
+
checkmarkIcon,
|
|
77
|
+
};
|
|
78
|
+
|
|
53
79
|
/**
|
|
54
80
|
* **Video Config**.
|
|
55
81
|
*
|
|
@@ -73,26 +99,6 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
73
99
|
private minVolume: number = 0.1;
|
|
74
100
|
private micChecked: boolean = false;
|
|
75
101
|
private processorNode: AudioWorkletNode | null = null;
|
|
76
|
-
// HTML IDs and classes
|
|
77
|
-
private webcam_container_id: string = "lookit-jspsych-webcam-container";
|
|
78
|
-
private reload_button_id_text: string = "lookit-jspsych-reload-webcam";
|
|
79
|
-
private reload_button_id_cam: string = "lookit-jspsych-reload-cam-mic";
|
|
80
|
-
private camera_selection_id: string = "lookit-jspsych-which-webcam";
|
|
81
|
-
private mic_selection_id: string = "lookit-jspsych-which-mic";
|
|
82
|
-
private next_button_id: string = "lookit-jspsych-next";
|
|
83
|
-
private error_msg_div_id: string = "lookit-jspsych-video-config-errors";
|
|
84
|
-
private step1_id: string = "lookit-jspsych-step1";
|
|
85
|
-
private step2_id: string = "lookit-jspsych-step2";
|
|
86
|
-
private step3_id: string = "lookit-jspsych-step3";
|
|
87
|
-
private step_complete_class: string = "lookit-jspsych-step-complete";
|
|
88
|
-
// info/error messages
|
|
89
|
-
private step_complete_text: string = "Done!";
|
|
90
|
-
private waiting_for_access_msg: string = "Waiting for camera/mic access...";
|
|
91
|
-
private checking_mic_msg: string = "Checking mic input...";
|
|
92
|
-
private access_problem_msg: string =
|
|
93
|
-
"There was a problem accessing your media devices.";
|
|
94
|
-
private setup_problem_msg: string =
|
|
95
|
-
"There was a problem setting up your camera and mic.";
|
|
96
102
|
|
|
97
103
|
/**
|
|
98
104
|
* Constructor for video config plugin.
|
|
@@ -112,7 +118,7 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
112
118
|
// Set up the event listener for device changes.
|
|
113
119
|
navigator.mediaDevices.ondevicechange = this.onDeviceChange;
|
|
114
120
|
// Add page content.
|
|
115
|
-
this.addHtmlContent(trial
|
|
121
|
+
this.addHtmlContent(trial);
|
|
116
122
|
// Add event listeners after elements have been added to the page.
|
|
117
123
|
this.addEventListeners();
|
|
118
124
|
// Begin the initial recorder setup steps.
|
|
@@ -124,44 +130,23 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
124
130
|
/**
|
|
125
131
|
* Add HTML content to the page.
|
|
126
132
|
*
|
|
127
|
-
* @param
|
|
128
|
-
* Trial object.
|
|
133
|
+
* @param trial - Trial object.
|
|
129
134
|
*/
|
|
130
|
-
private addHtmlContent = (
|
|
131
|
-
|
|
132
|
-
webcam_container_id: this.webcam_container_id,
|
|
133
|
-
reload_button_id_cam: this.reload_button_id_cam,
|
|
134
|
-
camera_selection_id: this.camera_selection_id,
|
|
135
|
-
mic_selection_id: this.mic_selection_id,
|
|
136
|
-
step1_id: this.step1_id,
|
|
137
|
-
step2_id: this.step2_id,
|
|
138
|
-
step3_id: this.step3_id,
|
|
139
|
-
step_complete_class: this.step_complete_class,
|
|
140
|
-
step_complete_text: this.step_complete_text,
|
|
141
|
-
reload_button_id_text: this.reload_button_id_text,
|
|
142
|
-
next_button_id: this.next_button_id,
|
|
143
|
-
chromeInitialPrompt,
|
|
144
|
-
chromeAlwaysAllow,
|
|
145
|
-
chromePermissions,
|
|
146
|
-
firefoxInitialPrompt,
|
|
147
|
-
firefoxChooseDevice,
|
|
148
|
-
firefoxDevicesBlocked,
|
|
149
|
-
troubleshooting_intro,
|
|
150
|
-
};
|
|
151
|
-
this.display_el!.innerHTML = Handlebars.compile(video_config)(html_params);
|
|
135
|
+
private addHtmlContent = (trial: VideoConsentTrialType) => {
|
|
136
|
+
this.display_el!.innerHTML = chsTemplates.videoConfig(trial, html_params);
|
|
152
137
|
};
|
|
153
138
|
|
|
154
139
|
/** Add event listeners to elements after they've been added to the page. */
|
|
155
140
|
private addEventListeners = () => {
|
|
156
141
|
// Next button.
|
|
157
142
|
const next_button_el = this.display_el?.querySelector(
|
|
158
|
-
`#${
|
|
143
|
+
`#${html_params.next_button_id}`,
|
|
159
144
|
) as HTMLButtonElement;
|
|
160
145
|
next_button_el.addEventListener("click", this.nextButtonClick);
|
|
161
146
|
// Reload buttons.
|
|
162
147
|
(
|
|
163
148
|
this.display_el?.querySelectorAll(
|
|
164
|
-
`#${
|
|
149
|
+
`#${html_params.reload_button_id_cam}, #${html_params.reload_button_id_text}`,
|
|
165
150
|
) as NodeListOf<HTMLButtonElement>
|
|
166
151
|
).forEach((el) => el.addEventListener("click", this.reloadButtonClick));
|
|
167
152
|
// Camera/mic selection elements.
|
|
@@ -211,9 +196,9 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
211
196
|
// Don't reset step 2 (reload) because that should persist after being checked once with any Recorder/devices.
|
|
212
197
|
this.updateInstructions(1, false);
|
|
213
198
|
this.updateInstructions(3, false);
|
|
214
|
-
this.updateErrors(
|
|
199
|
+
this.updateErrors(html_params.waiting_for_access_msg_id);
|
|
215
200
|
await this.requestPermission({ video: true, audio: true });
|
|
216
|
-
this.updateErrors(
|
|
201
|
+
this.updateErrors();
|
|
217
202
|
await this.onDeviceChange();
|
|
218
203
|
await this.setDevices();
|
|
219
204
|
await this.runStreamChecks();
|
|
@@ -259,11 +244,11 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
259
244
|
}) => {
|
|
260
245
|
// Clear any existing options in select elements
|
|
261
246
|
const cam_selection_el = this.display_el?.querySelector(
|
|
262
|
-
`#${
|
|
247
|
+
`#${html_params.camera_selection_id}`,
|
|
263
248
|
) as HTMLSelectElement;
|
|
264
249
|
cam_selection_el.innerHTML = "";
|
|
265
250
|
const mic_selection_el = this.display_el?.querySelector(
|
|
266
|
-
`#${
|
|
251
|
+
`#${html_params.mic_selection_id}`,
|
|
267
252
|
) as HTMLSelectElement;
|
|
268
253
|
mic_selection_el.innerHTML = "";
|
|
269
254
|
// Populate select elements with current device options.
|
|
@@ -299,13 +284,13 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
299
284
|
// Get the devices selected from the drop-down element.
|
|
300
285
|
const selected_cam: string = (
|
|
301
286
|
this.display_el?.querySelector(
|
|
302
|
-
`#${
|
|
287
|
+
`#${html_params.camera_selection_id}`,
|
|
303
288
|
) as HTMLSelectElement
|
|
304
289
|
).value;
|
|
305
290
|
this.camId = selected_cam;
|
|
306
291
|
const selected_mic: string = (
|
|
307
292
|
this.display_el?.querySelector(
|
|
308
|
-
`#${
|
|
293
|
+
`#${html_params.mic_selection_id}`,
|
|
309
294
|
) as HTMLSelectElement
|
|
310
295
|
).value;
|
|
311
296
|
this.micId = selected_mic;
|
|
@@ -332,14 +317,14 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
332
317
|
if (this.jsPsych.pluginAPI.getCameraRecorder() && this.recorder) {
|
|
333
318
|
this.recorder.insertWebcamFeed(
|
|
334
319
|
this.display_el?.querySelector(
|
|
335
|
-
`#${
|
|
320
|
+
`#${html_params.webcam_container_id}`,
|
|
336
321
|
) as HTMLDivElement,
|
|
337
322
|
);
|
|
338
323
|
this.updateInstructions(1, true);
|
|
339
|
-
this.updateErrors(
|
|
324
|
+
this.updateErrors(html_params.checking_mic_msg_id);
|
|
340
325
|
try {
|
|
341
326
|
await this.checkMic();
|
|
342
|
-
this.updateErrors(
|
|
327
|
+
this.updateErrors();
|
|
343
328
|
this.updateInstructions(3, true);
|
|
344
329
|
// Allow user to continue (end trial) when all checks have passed.
|
|
345
330
|
if (this.hasReloaded) {
|
|
@@ -347,11 +332,11 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
347
332
|
}
|
|
348
333
|
} catch (e) {
|
|
349
334
|
console.warn(`${e}`);
|
|
350
|
-
this.updateErrors(
|
|
335
|
+
this.updateErrors(html_params.setup_problem_msg_id);
|
|
351
336
|
throw new Error(`${e}`);
|
|
352
337
|
}
|
|
353
338
|
} else {
|
|
354
|
-
this.updateErrors(
|
|
339
|
+
this.updateErrors(html_params.access_problem_msg_id);
|
|
355
340
|
throw new NoStreamError();
|
|
356
341
|
}
|
|
357
342
|
};
|
|
@@ -553,15 +538,15 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
553
538
|
let step_id = null;
|
|
554
539
|
switch (step) {
|
|
555
540
|
case 1: {
|
|
556
|
-
step_id =
|
|
541
|
+
step_id = html_params.step1_id;
|
|
557
542
|
break;
|
|
558
543
|
}
|
|
559
544
|
case 2: {
|
|
560
|
-
step_id =
|
|
545
|
+
step_id = html_params.step2_id;
|
|
561
546
|
break;
|
|
562
547
|
}
|
|
563
548
|
case 3: {
|
|
564
|
-
step_id =
|
|
549
|
+
step_id = html_params.step3_id;
|
|
565
550
|
break;
|
|
566
551
|
}
|
|
567
552
|
}
|
|
@@ -586,14 +571,27 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
586
571
|
* Update the errors/messages div with information for the user about the
|
|
587
572
|
* camera/mic checks.
|
|
588
573
|
*
|
|
589
|
-
* @param
|
|
590
|
-
*
|
|
574
|
+
* @param errorMsgId - Span element ID containing the message to display in
|
|
575
|
+
* the error message div. Call the function without an errorMsgId to clear
|
|
576
|
+
* the errors.
|
|
591
577
|
*/
|
|
592
|
-
private updateErrors = (
|
|
593
|
-
const
|
|
594
|
-
`#${
|
|
578
|
+
private updateErrors = (errorMsgId?: string) => {
|
|
579
|
+
const error_msg_container = this.display_el?.querySelector(
|
|
580
|
+
`#${html_params.error_msg_div_id}`,
|
|
595
581
|
) as HTMLDivElement;
|
|
596
|
-
|
|
582
|
+
(
|
|
583
|
+
error_msg_container.querySelectorAll(
|
|
584
|
+
"span.error_msg",
|
|
585
|
+
) as NodeListOf<HTMLSpanElement>
|
|
586
|
+
).forEach((span) => {
|
|
587
|
+
span.style.display = "none";
|
|
588
|
+
});
|
|
589
|
+
if (errorMsgId) {
|
|
590
|
+
const error_msg_el = this.display_el?.querySelector(
|
|
591
|
+
`#${errorMsgId}`,
|
|
592
|
+
) as HTMLSpanElement;
|
|
593
|
+
error_msg_el.style.display = "block";
|
|
594
|
+
}
|
|
597
595
|
};
|
|
598
596
|
|
|
599
597
|
/**
|
|
@@ -652,14 +650,14 @@ export default class VideoConfigPlugin implements JsPsychPlugin<Info> {
|
|
|
652
650
|
*/
|
|
653
651
|
private enableNext = (enable: boolean) => {
|
|
654
652
|
const next_button_el = this.display_el?.querySelector(
|
|
655
|
-
`#${
|
|
653
|
+
`#${html_params.next_button_id}`,
|
|
656
654
|
) as HTMLButtonElement;
|
|
657
655
|
if (enable) {
|
|
658
656
|
next_button_el.disabled = false;
|
|
659
|
-
next_button_el.classList.add(`${
|
|
657
|
+
next_button_el.classList.add(`${html_params.step_complete_class}`);
|
|
660
658
|
} else {
|
|
661
659
|
next_button_el.disabled = true;
|
|
662
|
-
next_button_el.classList.remove(`${
|
|
660
|
+
next_button_el.classList.remove(`${html_params.step_complete_class}`);
|
|
663
661
|
}
|
|
664
662
|
};
|
|
665
663
|
}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
AudioWorkletNodeMock,
|
|
6
6
|
} from "../fixtures/MockWebAudioAPI";
|
|
7
7
|
import { MicCheckError, NoStreamError } from "./errors";
|
|
8
|
-
import VideoConfigPlugin from "./
|
|
8
|
+
import VideoConfigPlugin from "./videoConfig";
|
|
9
9
|
|
|
10
10
|
// The video config mic check relies on the WebAudio API, which is not available in Node/Jest/jsdom, so we'll mock it here.
|
|
11
11
|
// This is in a separate file to avoid polluting the other test environments with the WebAudio API mocks.
|