@lookit/record 3.0.1 → 4.1.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 -6
- package/dist/index.browser.js +136 -41
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.min.js +16 -16
- package/dist/index.browser.min.js.map +1 -1
- package/dist/index.cjs +135 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +129 -12
- package/dist/index.js +135 -40
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/consentVideo.spec.ts +38 -12
- package/src/consentVideo.ts +80 -19
- package/src/errors.ts +7 -27
- package/src/index.spec.ts +387 -12
- package/src/recorder.spec.ts +170 -6
- package/src/recorder.ts +37 -5
- package/src/start.ts +7 -1
- package/src/stop.ts +41 -7
- package/src/trial.ts +97 -11
package/src/recorder.ts
CHANGED
|
@@ -109,7 +109,13 @@ export default class Recorder {
|
|
|
109
109
|
this.jsPsych.pluginAPI.initializeCameraRecorder(stream, recorder_options);
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
/**
|
|
112
|
+
/**
|
|
113
|
+
* Reset the recorder. This is used internally after stopping/uploading a
|
|
114
|
+
* recording, in order to create a new active stream that can be used by a new
|
|
115
|
+
* Recorder instance. This can also be used by the consuming plugin/extension
|
|
116
|
+
* when a recorder needs to be reset without the stop/upload events (e.g. in
|
|
117
|
+
* the video config plugin).
|
|
118
|
+
*/
|
|
113
119
|
public reset() {
|
|
114
120
|
if (this.stream.active) {
|
|
115
121
|
throw new StreamActiveOnResetError();
|
|
@@ -276,13 +282,19 @@ export default class Recorder {
|
|
|
276
282
|
* tracks, clear the webcam feed element (if there is one), and return the
|
|
277
283
|
* stop promise. This should only be called after recording has started.
|
|
278
284
|
*
|
|
285
|
+
* @param maintain_container_size - Optional boolean indicating whether or not
|
|
286
|
+
* to maintain the current size of the webcam feed container when removing
|
|
287
|
+
* the video element. Default is false. If true, the container will be
|
|
288
|
+
* resized to match the dimensions of the video element before it is
|
|
289
|
+
* removed. This is useful for avoiding layout jumps when the webcam
|
|
290
|
+
* container will be re-used during the trial.
|
|
279
291
|
* @returns Promise that resolves after the media recorder has stopped and
|
|
280
292
|
* final 'dataavailable' event has occurred, when the "stop" event-related
|
|
281
293
|
* callback function is called.
|
|
282
294
|
*/
|
|
283
|
-
public stop() {
|
|
295
|
+
public stop(maintain_container_size: boolean = false) {
|
|
296
|
+
this.clearWebcamFeed(maintain_container_size);
|
|
284
297
|
this.stopTracks();
|
|
285
|
-
this.clearWebcamFeed();
|
|
286
298
|
|
|
287
299
|
if (!this.stopPromise) {
|
|
288
300
|
throw new NoStopPromiseError();
|
|
@@ -326,6 +338,8 @@ export default class Recorder {
|
|
|
326
338
|
} else {
|
|
327
339
|
await this.s3.completeUpload();
|
|
328
340
|
}
|
|
341
|
+
// Reset the recorder. This is necessary to create another active media stream from the stream clone, because the current stream is fully stopped/inactive and cannot be used again.
|
|
342
|
+
this.reset();
|
|
329
343
|
|
|
330
344
|
resolve();
|
|
331
345
|
};
|
|
@@ -353,12 +367,30 @@ export default class Recorder {
|
|
|
353
367
|
}
|
|
354
368
|
}
|
|
355
369
|
|
|
356
|
-
/**
|
|
357
|
-
|
|
370
|
+
/**
|
|
371
|
+
* Private helper to clear the webcam feed, if there is one. If remove is
|
|
372
|
+
* false, the video element source attribute is cleared and the parent div
|
|
373
|
+
* will be set to the same dimensions. This is useful for avoiding layout
|
|
374
|
+
* jumps when the webcam container and video element will be re-used during
|
|
375
|
+
* the trial.
|
|
376
|
+
*
|
|
377
|
+
* @param maintain_container_size - Boolean indicating whether or not to set
|
|
378
|
+
* the webcam feed container size before removing the video element
|
|
379
|
+
*/
|
|
380
|
+
private clearWebcamFeed(maintain_container_size: boolean) {
|
|
358
381
|
const webcam_feed_element = document.querySelector(
|
|
359
382
|
`#${this.webcam_element_id}`,
|
|
360
383
|
) as HTMLVideoElement;
|
|
361
384
|
if (webcam_feed_element) {
|
|
385
|
+
if (maintain_container_size) {
|
|
386
|
+
const parent_div = webcam_feed_element.parentElement as HTMLDivElement;
|
|
387
|
+
if (parent_div) {
|
|
388
|
+
const width = webcam_feed_element.offsetWidth;
|
|
389
|
+
const height = webcam_feed_element.offsetHeight;
|
|
390
|
+
parent_div.style.height = `${height}px`;
|
|
391
|
+
parent_div.style.width = `${width}px`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
362
394
|
webcam_feed_element.remove();
|
|
363
395
|
}
|
|
364
396
|
}
|
package/src/start.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import { LookitWindow } from "@lookit/data/dist/types";
|
|
2
2
|
import { JsPsych, JsPsychPlugin } from "jspsych";
|
|
3
|
+
import { version } from "../package.json";
|
|
3
4
|
import { ExistingRecordingError } from "./errors";
|
|
4
5
|
import Recorder from "./recorder";
|
|
5
6
|
|
|
6
7
|
declare let window: LookitWindow;
|
|
7
8
|
|
|
8
|
-
const info = <const>{
|
|
9
|
+
const info = <const>{
|
|
10
|
+
name: "start-record-plugin",
|
|
11
|
+
version,
|
|
12
|
+
parameters: {},
|
|
13
|
+
data: {},
|
|
14
|
+
};
|
|
9
15
|
type Info = typeof info;
|
|
10
16
|
|
|
11
17
|
/** Start recording. Used by researchers who want to record across trials. */
|
package/src/stop.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LookitWindow } from "@lookit/data/dist/types";
|
|
2
2
|
import chsTemplates from "@lookit/templates";
|
|
3
3
|
import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych";
|
|
4
|
+
import { version } from "../package.json";
|
|
4
5
|
import { NoSessionRecordingError } from "./errors";
|
|
5
6
|
import Recorder from "./recorder";
|
|
6
7
|
|
|
@@ -8,9 +9,32 @@ declare let window: LookitWindow;
|
|
|
8
9
|
|
|
9
10
|
const info = <const>{
|
|
10
11
|
name: "stop-record-plugin",
|
|
12
|
+
version,
|
|
11
13
|
parameters: {
|
|
12
|
-
|
|
14
|
+
/**
|
|
15
|
+
* This string can contain HTML markup. Any content provided will be
|
|
16
|
+
* displayed while the recording is uploading. If null (the default), then
|
|
17
|
+
* the default 'uploading video, please wait' (or appropriate translation
|
|
18
|
+
* based on 'locale') will be displayed. Use a blank string for no
|
|
19
|
+
* message/content.
|
|
20
|
+
*/
|
|
21
|
+
wait_for_upload_message: {
|
|
22
|
+
type: ParameterType.HTML_STRING,
|
|
23
|
+
default: null as null | string,
|
|
24
|
+
},
|
|
25
|
+
/**
|
|
26
|
+
* Locale code used for translating the default 'uploading video, please
|
|
27
|
+
* wait' message. This code must be present in the translation files. If the
|
|
28
|
+
* code is not found then English will be used. If the
|
|
29
|
+
* 'wait_for_upload_message' parameter is specified then this value is
|
|
30
|
+
* ignored.
|
|
31
|
+
*/
|
|
32
|
+
locale: {
|
|
33
|
+
type: ParameterType.STRING,
|
|
34
|
+
default: "en-us",
|
|
35
|
+
},
|
|
13
36
|
},
|
|
37
|
+
data: {},
|
|
14
38
|
};
|
|
15
39
|
type Info = typeof info;
|
|
16
40
|
|
|
@@ -41,11 +65,21 @@ export default class StopRecordPlugin implements JsPsychPlugin<Info> {
|
|
|
41
65
|
* @param trial - Trial object with parameters/values.
|
|
42
66
|
*/
|
|
43
67
|
public trial(display_element: HTMLElement, trial: TrialType<Info>): void {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
display_element.innerHTML =
|
|
48
|
-
|
|
49
|
-
|
|
68
|
+
if (trial.wait_for_upload_message == null) {
|
|
69
|
+
display_element.innerHTML = chsTemplates.uploadingVideo(trial);
|
|
70
|
+
} else {
|
|
71
|
+
display_element.innerHTML = trial.wait_for_upload_message;
|
|
72
|
+
}
|
|
73
|
+
this.recorder
|
|
74
|
+
.stop()
|
|
75
|
+
.then(() => {
|
|
76
|
+
window.chs.sessionRecorder = null;
|
|
77
|
+
display_element.innerHTML = "";
|
|
78
|
+
this.jsPsych.finishTrial();
|
|
79
|
+
})
|
|
80
|
+
.catch((err) => {
|
|
81
|
+
console.error("StopRecordPlugin: recorder stop/upload failed.", err);
|
|
82
|
+
// TO DO: display translated error msg and/or researcher contact info
|
|
83
|
+
});
|
|
50
84
|
}
|
|
51
85
|
}
|
package/src/trial.ts
CHANGED
|
@@ -1,16 +1,55 @@
|
|
|
1
|
+
import chsTemplates from "@lookit/templates";
|
|
1
2
|
import autoBind from "auto-bind";
|
|
2
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
JsPsych,
|
|
5
|
+
JsPsychExtension,
|
|
6
|
+
JsPsychExtensionInfo,
|
|
7
|
+
PluginInfo,
|
|
8
|
+
TrialType,
|
|
9
|
+
} from "jspsych";
|
|
10
|
+
import { version } from "../package.json";
|
|
3
11
|
import Recorder from "./recorder";
|
|
4
12
|
import { jsPsychPluginWithInfo } from "./types";
|
|
5
13
|
|
|
6
|
-
|
|
14
|
+
// JsPsychExtensionInfo does not allow parameters, so we define them as interfaces and use these to type the arguments passed to extension initialize and on_start functions.
|
|
15
|
+
interface Parameters {
|
|
16
|
+
/**
|
|
17
|
+
* Content that should be displayed while the recording is uploading. If null
|
|
18
|
+
* (the default), then the default 'uploading video, please wait...' (or
|
|
19
|
+
* appropriate translation based on 'locale') will be displayed. Use a blank
|
|
20
|
+
* string for no message/content. Otherwise this parameter can be set to a
|
|
21
|
+
* custom string and can contain HTML markup. If you want to embed
|
|
22
|
+
* images/video/audio in this HTML string, be sure to preload the media files
|
|
23
|
+
* with the `preload` plugin and manual preloading. Use a blank string (`""`)
|
|
24
|
+
* for no message/content.
|
|
25
|
+
*
|
|
26
|
+
* @default null
|
|
27
|
+
*/
|
|
28
|
+
wait_for_upload_message?: null | string;
|
|
29
|
+
/**
|
|
30
|
+
* Locale code used for translating the default 'uploading video, please
|
|
31
|
+
* wait...' message. This code must be present in the translation files. If
|
|
32
|
+
* the code is not found then English will be used. If the
|
|
33
|
+
* 'wait_for_upload_message' parameter is specified then this value
|
|
34
|
+
* isignored.
|
|
35
|
+
*
|
|
36
|
+
* @default "en-us"
|
|
37
|
+
*/
|
|
38
|
+
locale?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** This extension allows researchers to record webcam audio/video during trials. */
|
|
7
42
|
export default class TrialRecordExtension implements JsPsychExtension {
|
|
8
43
|
public static readonly info: JsPsychExtensionInfo = {
|
|
9
44
|
name: "chs-trial-record-extension",
|
|
45
|
+
version,
|
|
46
|
+
data: {},
|
|
10
47
|
};
|
|
11
48
|
|
|
12
49
|
private recorder?: Recorder;
|
|
13
50
|
private pluginName: string | undefined;
|
|
51
|
+
private uploadMsg: null | string = null;
|
|
52
|
+
private locale: string = "en-us";
|
|
14
53
|
|
|
15
54
|
/**
|
|
16
55
|
* Video recording extension.
|
|
@@ -22,29 +61,76 @@ export default class TrialRecordExtension implements JsPsychExtension {
|
|
|
22
61
|
}
|
|
23
62
|
|
|
24
63
|
/**
|
|
25
|
-
*
|
|
64
|
+
* Runs on the initialize step for extensions, called when an instance of
|
|
26
65
|
* jsPsych is first initialized through initJsPsych().
|
|
66
|
+
*
|
|
67
|
+
* @param params - Parameters object
|
|
68
|
+
* @param params.wait_for_upload_message - Message to display while waiting
|
|
69
|
+
* for upload. String or null (default)
|
|
70
|
+
* @param params.locale - Message to display while waiting for upload. String
|
|
71
|
+
* or null (default).
|
|
27
72
|
*/
|
|
28
|
-
public async initialize() {
|
|
73
|
+
public async initialize(params?: Parameters) {
|
|
74
|
+
await new Promise<void>((resolve) => {
|
|
75
|
+
this.uploadMsg = params?.wait_for_upload_message
|
|
76
|
+
? params.wait_for_upload_message
|
|
77
|
+
: null;
|
|
78
|
+
this.locale = params?.locale ? params.locale : "en-us";
|
|
79
|
+
console.log(this.uploadMsg);
|
|
80
|
+
console.log(this.locale);
|
|
81
|
+
resolve();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
29
84
|
|
|
30
|
-
/**
|
|
31
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Runs at the start of a trial.
|
|
87
|
+
*
|
|
88
|
+
* @param startParams - Parameters object
|
|
89
|
+
* @param startParams.wait_for_upload_message - Message to display while
|
|
90
|
+
* waiting for upload. String or null (default). If given, this will
|
|
91
|
+
* overwrite the value used during initialization.
|
|
92
|
+
*/
|
|
93
|
+
public on_start(startParams?: Parameters) {
|
|
94
|
+
if (startParams?.wait_for_upload_message) {
|
|
95
|
+
this.uploadMsg = startParams.wait_for_upload_message;
|
|
96
|
+
}
|
|
97
|
+
if (startParams?.locale) {
|
|
98
|
+
this.locale = startParams.locale;
|
|
99
|
+
}
|
|
100
|
+
console.log(this.uploadMsg);
|
|
101
|
+
console.log(this.locale);
|
|
32
102
|
this.recorder = new Recorder(this.jsPsych);
|
|
33
103
|
}
|
|
34
104
|
|
|
35
|
-
/**
|
|
105
|
+
/** Runs when the trial has loaded. */
|
|
36
106
|
public on_load() {
|
|
37
107
|
this.pluginName = this.getCurrentPluginName();
|
|
38
108
|
this.recorder?.start(false, `${this.pluginName}`);
|
|
39
109
|
}
|
|
40
110
|
|
|
41
111
|
/**
|
|
42
|
-
*
|
|
112
|
+
* Runs when trial has finished.
|
|
43
113
|
*
|
|
44
|
-
* @returns
|
|
114
|
+
* @returns Any data from the trial extension that should be added to the rest
|
|
115
|
+
* of the trial data.
|
|
45
116
|
*/
|
|
46
|
-
public on_finish() {
|
|
47
|
-
this.
|
|
117
|
+
public async on_finish() {
|
|
118
|
+
const displayEl = this.jsPsych.getDisplayElement();
|
|
119
|
+
if (this.uploadMsg == null) {
|
|
120
|
+
displayEl.innerHTML = chsTemplates.uploadingVideo({
|
|
121
|
+
type: this.jsPsych.getCurrentTrial().type,
|
|
122
|
+
locale: this.locale,
|
|
123
|
+
} as TrialType<PluginInfo>);
|
|
124
|
+
} else {
|
|
125
|
+
displayEl.innerHTML = this.uploadMsg;
|
|
126
|
+
}
|
|
127
|
+
try {
|
|
128
|
+
await this.recorder?.stop();
|
|
129
|
+
displayEl.innerHTML = "";
|
|
130
|
+
} catch (err) {
|
|
131
|
+
console.error("TrialRecordExtension: recorder stop/upload failed.", err);
|
|
132
|
+
// TO DO: display translated error msg and/or researcher contact info
|
|
133
|
+
}
|
|
48
134
|
return {};
|
|
49
135
|
}
|
|
50
136
|
|