@lookit/record 4.1.0 → 6.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 +158 -15
- package/dist/index.browser.js +212 -43
- 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 +211 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +113 -9
- package/dist/index.js +211 -42
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/consentVideo.spec.ts +9 -0
- package/src/consentVideo.ts +24 -2
- package/src/errors.ts +28 -0
- package/src/index.spec.ts +497 -54
- package/src/recorder.spec.ts +669 -109
- package/src/recorder.ts +184 -36
- package/src/start.ts +45 -5
- package/src/stop.ts +29 -12
- package/src/trial.ts +42 -16
- package/src/types.ts +21 -0
- package/src/utils.spec.ts +129 -0
- package/src/utils.ts +45 -0
package/README.md
CHANGED
|
@@ -140,11 +140,23 @@ whether babies love cats because of their soft fur or their twitchy tails.”
|
|
|
140
140
|
|
|
141
141
|
#### Optional
|
|
142
142
|
|
|
143
|
-
**`template` [String | "consent-template-5"]**
|
|
143
|
+
**`template` [String option | "consent-template-5"]**
|
|
144
144
|
|
|
145
|
-
Which consent document template to use.
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
Which consent document template to use. Options: `consent-template-5`,
|
|
146
|
+
`consent-garden`, `consent-recording-only`.
|
|
147
|
+
|
|
148
|
+
Most studies should use `consent-template-5` (the default).
|
|
149
|
+
|
|
150
|
+
If you are running a study that ONLY uses webcam recording for the consent
|
|
151
|
+
statement (nothing else is recording during the session), then you should use
|
|
152
|
+
the `consent-recording-only` template. This version removes/modifies some text
|
|
153
|
+
that references additional webcam recordings during the session.
|
|
154
|
+
|
|
155
|
+
By default, the `consent-recording-only` plugin assumes that, in addition to a
|
|
156
|
+
consent trial, you are using CHS to collect other data/responses. If you are not
|
|
157
|
+
collecting any other data on CHS (for instance, because the rest of the study
|
|
158
|
+
takes place on a different platform), then you should change this using the
|
|
159
|
+
`only_consent_on_chs` parameter (see below).
|
|
148
160
|
|
|
149
161
|
**`additional_video_privacy_statement` [String | ""]**
|
|
150
162
|
|
|
@@ -220,6 +232,18 @@ Optional additional text for under header “Participation is voluntary”. E.g.
|
|
|
220
232
|
“There are two sessions in this study; you will be invited to complete another
|
|
221
233
|
session next month. It is okay not to do both sessions!”
|
|
222
234
|
|
|
235
|
+
**`only_consent_on_chs` [Boolean | false]**
|
|
236
|
+
|
|
237
|
+
This parameter is only relevant to the `consent-recording-only` template. If a
|
|
238
|
+
different template is used, this parameter will be ignored.
|
|
239
|
+
|
|
240
|
+
Whether or not the consent trial is the ONLY data being collected about the
|
|
241
|
+
participant on CHS (e.g. the study redirects to an external URL after the
|
|
242
|
+
consent trial). If `false` (the default), then the consent template will contain
|
|
243
|
+
information about how CHS handles data/responses. If `true`, then the template
|
|
244
|
+
will only reference the consent recording, and any statements about CHS access
|
|
245
|
+
to data/responses are omitted.
|
|
246
|
+
|
|
223
247
|
#### Additional customization available if REQUIRED by your IRB
|
|
224
248
|
|
|
225
249
|
To accommodate a variety of idiosyncratic IRB requirements, various other fields
|
|
@@ -297,7 +321,9 @@ Replace the default spoken consent statement with your custom text.
|
|
|
297
321
|
Whether to omit the phrase “or in the very unlikely event of a research-related
|
|
298
322
|
injury” from the contact section. (This was required by the Northwestern IRB.)
|
|
299
323
|
|
|
300
|
-
###
|
|
324
|
+
### Examples
|
|
325
|
+
|
|
326
|
+
**Standard use case**
|
|
301
327
|
|
|
302
328
|
```javascript
|
|
303
329
|
const videoConsent = {
|
|
@@ -332,6 +358,51 @@ const videoConsent = {
|
|
|
332
358
|
};
|
|
333
359
|
```
|
|
334
360
|
|
|
361
|
+
**Study only uses webcam recording for consent**
|
|
362
|
+
|
|
363
|
+
Use the `"consent-recording-only"` template when the study collects (some or
|
|
364
|
+
all) data/responses on CHS, but webcam recording is only used for the consent
|
|
365
|
+
statement (nothing else is recorded).
|
|
366
|
+
|
|
367
|
+
```javascript
|
|
368
|
+
const videoConsentRecOnly = {
|
|
369
|
+
type: chsRecord.VideoConsentPlugin,
|
|
370
|
+
template: "consent-recording-only",
|
|
371
|
+
PIName: "Jane Smith",
|
|
372
|
+
institution: "Science University",
|
|
373
|
+
PIContact: "Jane Smith at 123 456 7890",
|
|
374
|
+
purpose:
|
|
375
|
+
"Why do babies love cats? This study will help us find out whether babies love cats because of their soft fur or their twitchy tails.",
|
|
376
|
+
procedures:
|
|
377
|
+
"Your child will be shown pictures of lots of different cats, along with noises that cats make like meowing and purring. We are interested in which pictures and sounds make your child smile. We will ask you (the parent) to turn around to avoid influencing your child's responses.",
|
|
378
|
+
payment:
|
|
379
|
+
"After you finish the study, we will email you a $5 BabyStore gift card within approximately three days. To be eligible for the gift card your child must be in the age range for this study, you need to submit a valid consent statement, and we need to see that there is a child with you. But we will send a gift card even if you do not finish the whole study or we are not able to use your child's data! There are no other direct benefits to you or your child from participating, but we hope you will enjoy the experience.",
|
|
380
|
+
};
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
**CHS study is only used for a consent recording**
|
|
384
|
+
|
|
385
|
+
Use the `"consent-recording-only"` template with `only_consent_on_chs: true`
|
|
386
|
+
when webcam recording is only used for the consent statement and no other
|
|
387
|
+
data/responses are collected on CHS.
|
|
388
|
+
|
|
389
|
+
```javascript
|
|
390
|
+
const videoConsentRecOnly = {
|
|
391
|
+
type: chsRecord.VideoConsentPlugin,
|
|
392
|
+
template: "consent-recording-only",
|
|
393
|
+
only_consent_on_chs: true, // set this to true
|
|
394
|
+
PIName: "Jane Smith",
|
|
395
|
+
institution: "Science University",
|
|
396
|
+
PIContact: "Jane Smith at 123 456 7890",
|
|
397
|
+
purpose:
|
|
398
|
+
"Why do babies love cats? This study will help us find out whether babies love cats because of their soft fur or their twitchy tails.",
|
|
399
|
+
procedures:
|
|
400
|
+
"Your child will be shown pictures of lots of different cats, along with noises that cats make like meowing and purring. We are interested in which pictures and sounds make your child smile. We will ask you (the parent) to turn around to avoid influencing your child's responses.",
|
|
401
|
+
payment:
|
|
402
|
+
"After you finish the study, we will email you a $5 BabyStore gift card within approximately three days. To be eligible for the gift card your child must be in the age range for this study, you need to submit a valid consent statement, and we need to see that there is a child with you. But we will send a gift card even if you do not finish the whole study or we are not able to use your child's data! There are no other direct benefits to you or your child from participating, but we hope you will enjoy the experience.",
|
|
403
|
+
};
|
|
404
|
+
```
|
|
405
|
+
|
|
335
406
|
## Trial Recording Extension
|
|
336
407
|
|
|
337
408
|
Trial recording can be added to most jsPsych trials. This is a jsPsych extension
|
|
@@ -368,6 +439,13 @@ string, be sure to preload the media files with the `preload` plugin and
|
|
|
368
439
|
Use a blank string (`""`) for no message/content. If a value is provided then
|
|
369
440
|
the `locale` parameter will be ignored.
|
|
370
441
|
|
|
442
|
+
**`max_upload_seconds` [ Integer | 10 ]**
|
|
443
|
+
|
|
444
|
+
Maximum duration (in seconds) to wait for the trial recording to finish
|
|
445
|
+
uploading before continuing with the experiment. If the maximum upload duration
|
|
446
|
+
is reached and the upload has not finished, then the experiment will move on and
|
|
447
|
+
the trial recording upload will continue in the background.
|
|
448
|
+
|
|
371
449
|
### Examples
|
|
372
450
|
|
|
373
451
|
**Basic usage**
|
|
@@ -452,6 +530,27 @@ const trialRec = {
|
|
|
452
530
|
};
|
|
453
531
|
```
|
|
454
532
|
|
|
533
|
+
By default, the trial recording extension will wait up to 10 seconds for the
|
|
534
|
+
recording to finish uploading before moving on to the next trial. If the upload
|
|
535
|
+
does not finish in that time, it will continue to upload in the background. You
|
|
536
|
+
can adjust this duration with the `max_upload_seconds` parameter. You may want
|
|
537
|
+
to increase or decrease this duration depending on the duration of your recorded
|
|
538
|
+
trials, or you expect that some participants may have slow/unreliable internet
|
|
539
|
+
connections. This example decreases the maximum upload duration from 10 to 5
|
|
540
|
+
seconds:
|
|
541
|
+
|
|
542
|
+
```javascript
|
|
543
|
+
const trialRec = {
|
|
544
|
+
// ... Other trial parameters ...
|
|
545
|
+
extensions: [
|
|
546
|
+
{
|
|
547
|
+
type: chsRecord.TrialRecordExtension,
|
|
548
|
+
params: { max_upload_seconds: 5 },
|
|
549
|
+
},
|
|
550
|
+
],
|
|
551
|
+
};
|
|
552
|
+
```
|
|
553
|
+
|
|
455
554
|
## Session Recording
|
|
456
555
|
|
|
457
556
|
You might prefer to record across multiple trials in a study session. This can
|
|
@@ -480,8 +579,18 @@ const startRec = { type: chsRecord.StartRecordPlugin };
|
|
|
480
579
|
|
|
481
580
|
#### Parameters
|
|
482
581
|
|
|
483
|
-
|
|
484
|
-
|
|
582
|
+
**`wait_for_connection_message` [`null` or HTML string | `null` ]**
|
|
583
|
+
|
|
584
|
+
This parameter determines what content should be displayed while the session
|
|
585
|
+
recording is initializing. If `null` (the default), then the message
|
|
586
|
+
'establishing video connection, please wait' (or appropriate translation based
|
|
587
|
+
on `locale`) will be displayed. Otherwise this parameter can be set to a custom
|
|
588
|
+
string and can contain HTML markup. If you want to embed images/video/audio in
|
|
589
|
+
this HTML string, be sure to preload the media files with the `preload` plugin
|
|
590
|
+
and
|
|
591
|
+
[manual preloading](https://www.jspsych.org/latest/overview/media-preloading/#manual-preloading).
|
|
592
|
+
Use a blank string (`""`) for no message/content. If a value is provided then
|
|
593
|
+
the `locale` parameter will be ignored.
|
|
485
594
|
|
|
486
595
|
### Stop Recording Plugin
|
|
487
596
|
|
|
@@ -519,6 +628,13 @@ string, be sure to preload the media files with the `preload` plugin and
|
|
|
519
628
|
Use a blank string (`""`) for no message/content. If a value is provided then
|
|
520
629
|
the `locale` parameter will be ignored.
|
|
521
630
|
|
|
631
|
+
**`max_upload_seconds` [ Integer | 10 ]**
|
|
632
|
+
|
|
633
|
+
Maximum duration (in seconds) to wait for the session recording to finish
|
|
634
|
+
uploading before continuing with the experiment. If the maximum upload duration
|
|
635
|
+
is reached and the upload has not finished, then the experiment will move on and
|
|
636
|
+
the session recording upload will continue in the background.
|
|
637
|
+
|
|
522
638
|
### Examples
|
|
523
639
|
|
|
524
640
|
**Basic usage**
|
|
@@ -570,28 +686,55 @@ jsPsych.run([
|
|
|
570
686
|
|
|
571
687
|
**Setting parameters**
|
|
572
688
|
|
|
573
|
-
By default, the
|
|
574
|
-
please wait
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
689
|
+
By default, the start session recording plugin will display "establishing video
|
|
690
|
+
connection, please wait" while establishing the connection to our video storage,
|
|
691
|
+
and the stop session recording plugin will display "uploading video, please
|
|
692
|
+
wait..." while the session recording is uploading. You can set the `locale`
|
|
693
|
+
parameter value to translate these messages to another language. For example,
|
|
694
|
+
the trial below will display the Brazilian Portuguese translation of these
|
|
695
|
+
messages. If the locale string does not match any of the translation codes that
|
|
696
|
+
we support, then the message will be displayed in English.
|
|
579
697
|
|
|
580
698
|
```javascript
|
|
699
|
+
const startpRec = {
|
|
700
|
+
type: type: chsRecord.StartRecordPlugin,
|
|
701
|
+
locale: "pt-br",
|
|
702
|
+
};
|
|
581
703
|
const stopRec = {
|
|
582
704
|
type: chsRecord.StopRecordPlugin,
|
|
583
705
|
locale: "pt-br",
|
|
584
706
|
};
|
|
585
707
|
```
|
|
586
708
|
|
|
587
|
-
You can also set custom content to be displayed
|
|
588
|
-
|
|
709
|
+
You can also set custom content to be displayed at the start of the session
|
|
710
|
+
recording, while it is initializing, and/or at the end, when the file is
|
|
711
|
+
uploading. The value must be a string. It can include HTML-formatted content,
|
|
589
712
|
which means that you can embed audio, video, images etc. (be sure to preload any
|
|
590
713
|
media files!).
|
|
591
714
|
|
|
592
715
|
```javascript
|
|
716
|
+
const startpRec = {
|
|
717
|
+
type: type: chsRecord.StartRecordPlugin,
|
|
718
|
+
wait_for_connection_message: "<p style='color:green;>Please wait...</p>"
|
|
719
|
+
};
|
|
593
720
|
const stopRec = {
|
|
594
721
|
type: chsRecord.StopRecordPlugin,
|
|
595
722
|
wait_for_upload_message: "<p style='color:red;'>Hang on a sec!</p>",
|
|
596
723
|
};
|
|
597
724
|
```
|
|
725
|
+
|
|
726
|
+
By default, the stop session recording plugin will wait up to 10 seconds for the
|
|
727
|
+
session recording to finish uploading before moving on with the experiment. If
|
|
728
|
+
the upload does not finish in that time, it will continue to upload in the
|
|
729
|
+
background. You can adjust this duration with the `max_upload_seconds`
|
|
730
|
+
parameter. You may want to increase this duration if, for example, your
|
|
731
|
+
experiment creates a very long session recording, or you expect that some
|
|
732
|
+
participants may have slow/unreliable internet connections. This example
|
|
733
|
+
increases the maximum upload duration from 10 to 20 seconds:
|
|
734
|
+
|
|
735
|
+
```javascript
|
|
736
|
+
const stopRec = {
|
|
737
|
+
type: chsRecord.StopRecordPlugin,
|
|
738
|
+
max_upload_seconds: 20,
|
|
739
|
+
};
|
|
740
|
+
```
|
package/dist/index.browser.js
CHANGED
|
@@ -32,7 +32,7 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
32
32
|
|
|
33
33
|
var _package = {
|
|
34
34
|
name: "@lookit/record",
|
|
35
|
-
version: "
|
|
35
|
+
version: "6.0.0",
|
|
36
36
|
description: "Recording extensions and plugins for CHS studies.",
|
|
37
37
|
homepage: "https://github.com/lookit/lookit-jspsych#readme",
|
|
38
38
|
bugs: {
|
|
@@ -73,8 +73,8 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
73
73
|
typescript: "^5.6.2"
|
|
74
74
|
},
|
|
75
75
|
peerDependencies: {
|
|
76
|
-
"@lookit/data": "^0.
|
|
77
|
-
"@lookit/templates": "^
|
|
76
|
+
"@lookit/data": "^0.3.0",
|
|
77
|
+
"@lookit/templates": "^3.1.0",
|
|
78
78
|
jspsych: "^8.0.3"
|
|
79
79
|
}
|
|
80
80
|
};
|
|
@@ -149,6 +149,12 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
149
149
|
this.name = "S3UndefinedError";
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
+
class NoFileNameError extends Error {
|
|
153
|
+
constructor() {
|
|
154
|
+
super("No filename found for recording.");
|
|
155
|
+
this.name = "NoFileNameError";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
152
158
|
class StreamActiveOnResetError extends Error {
|
|
153
159
|
constructor() {
|
|
154
160
|
super("Won't reset recorder. Stream is still active.");
|
|
@@ -173,6 +179,12 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
173
179
|
this.name = "ElementNotFoundError";
|
|
174
180
|
}
|
|
175
181
|
}
|
|
182
|
+
class TimeoutError extends Error {
|
|
183
|
+
constructor(msg) {
|
|
184
|
+
super(`${msg}`);
|
|
185
|
+
this.name = "TimeoutError";
|
|
186
|
+
}
|
|
187
|
+
}
|
|
176
188
|
|
|
177
189
|
// Gets all non-builtin properties up the prototype chain.
|
|
178
190
|
const getAllProperties = object => {
|
|
@@ -8465,6 +8477,31 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8465
8477
|
|
|
8466
8478
|
var img$7 = "data:image/svg+xml,%3c%3fxml version='1.0' encoding='utf-8' %3f%3e%3csvg viewBox='-1 -1 18 18' xmlns='http://www.w3.org/2000/svg'%3e %3ccircle fill='red' stroke='black' stroke-width='0.5' cx='8' cy='8' r='8'%3e%3c/circle%3e%3c/svg%3e";
|
|
8467
8479
|
|
|
8480
|
+
const promiseWithTimeout = (promise, promiseId, timeoutMs, onTimeoutCleanup) => {
|
|
8481
|
+
let timeoutHandle;
|
|
8482
|
+
const timeout = new Promise((resolve) => {
|
|
8483
|
+
timeoutHandle = setTimeout(() => {
|
|
8484
|
+
onTimeoutCleanup?.();
|
|
8485
|
+
resolve("timeout");
|
|
8486
|
+
}, timeoutMs);
|
|
8487
|
+
});
|
|
8488
|
+
return Promise.race([promise, timeout]).then(
|
|
8489
|
+
(value) => {
|
|
8490
|
+
if (value == "timeout") {
|
|
8491
|
+
console.log(`Upload for ${promiseId} timed out.`);
|
|
8492
|
+
} else {
|
|
8493
|
+
console.log(`Upload for ${promiseId} completed.`);
|
|
8494
|
+
clearTimeout(timeoutHandle);
|
|
8495
|
+
}
|
|
8496
|
+
return value;
|
|
8497
|
+
},
|
|
8498
|
+
(err) => {
|
|
8499
|
+
clearTimeout(timeoutHandle);
|
|
8500
|
+
throw err;
|
|
8501
|
+
}
|
|
8502
|
+
);
|
|
8503
|
+
};
|
|
8504
|
+
|
|
8468
8505
|
class Recorder {
|
|
8469
8506
|
constructor(jsPsych) {
|
|
8470
8507
|
this.jsPsych = jsPsych;
|
|
@@ -8569,13 +8606,73 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8569
8606
|
this.recorder.stop();
|
|
8570
8607
|
this.stream.getTracks().map((t) => t.stop());
|
|
8571
8608
|
}
|
|
8572
|
-
stop(
|
|
8609
|
+
stop({
|
|
8610
|
+
maintain_container_size = false,
|
|
8611
|
+
stop_timeout_ms = null,
|
|
8612
|
+
upload_timeout_ms = 1e4
|
|
8613
|
+
} = {}) {
|
|
8614
|
+
this.preStopCheck();
|
|
8573
8615
|
this.clearWebcamFeed(maintain_container_size);
|
|
8574
8616
|
this.stopTracks();
|
|
8575
|
-
|
|
8576
|
-
|
|
8577
|
-
|
|
8578
|
-
|
|
8617
|
+
const snapshot = {
|
|
8618
|
+
s3: !this.localDownload ? this.s3 : null,
|
|
8619
|
+
filename: this.filename,
|
|
8620
|
+
localDownload: this.localDownload,
|
|
8621
|
+
url: "null"
|
|
8622
|
+
};
|
|
8623
|
+
const stopped = stop_timeout_ms ? promiseWithTimeout(
|
|
8624
|
+
this.stopPromise,
|
|
8625
|
+
`${snapshot.filename}-stopped`,
|
|
8626
|
+
stop_timeout_ms,
|
|
8627
|
+
this.createTimeoutHandler("stop", snapshot.filename)
|
|
8628
|
+
) : this.stopPromise;
|
|
8629
|
+
stopped.finally(() => {
|
|
8630
|
+
try {
|
|
8631
|
+
this.reset();
|
|
8632
|
+
} catch (err) {
|
|
8633
|
+
console.error("Error while resetting recorder after stop: ", err);
|
|
8634
|
+
}
|
|
8635
|
+
});
|
|
8636
|
+
const uploadPromise = (async () => {
|
|
8637
|
+
let url;
|
|
8638
|
+
try {
|
|
8639
|
+
url = await stopped;
|
|
8640
|
+
if (url == "timeout") {
|
|
8641
|
+
throw new TimeoutError("Recorder stop timed out.");
|
|
8642
|
+
}
|
|
8643
|
+
} catch (err) {
|
|
8644
|
+
console.warn("Upload failed because recorder stop timed out");
|
|
8645
|
+
throw err;
|
|
8646
|
+
}
|
|
8647
|
+
snapshot.url = url;
|
|
8648
|
+
if (snapshot.localDownload) {
|
|
8649
|
+
try {
|
|
8650
|
+
this.download(snapshot.filename, snapshot.url);
|
|
8651
|
+
await Promise.resolve();
|
|
8652
|
+
} catch (err) {
|
|
8653
|
+
console.error("Local download failed: ", err);
|
|
8654
|
+
throw err;
|
|
8655
|
+
}
|
|
8656
|
+
} else {
|
|
8657
|
+
try {
|
|
8658
|
+
await snapshot.s3.completeUpload();
|
|
8659
|
+
} catch (err) {
|
|
8660
|
+
console.error("Upload failed: ", err);
|
|
8661
|
+
throw err;
|
|
8662
|
+
}
|
|
8663
|
+
}
|
|
8664
|
+
})();
|
|
8665
|
+
const uploaded = upload_timeout_ms ? promiseWithTimeout(
|
|
8666
|
+
uploadPromise,
|
|
8667
|
+
`${snapshot.filename}-uploaded`,
|
|
8668
|
+
upload_timeout_ms,
|
|
8669
|
+
this.createTimeoutHandler("upload", snapshot.filename)
|
|
8670
|
+
) : uploadPromise;
|
|
8671
|
+
window.chs.pendingUploads.push({
|
|
8672
|
+
promise: uploadPromise,
|
|
8673
|
+
file: snapshot.filename
|
|
8674
|
+
});
|
|
8675
|
+
return { stopped, uploaded };
|
|
8579
8676
|
}
|
|
8580
8677
|
initializeCheck() {
|
|
8581
8678
|
if (!this.recorder) {
|
|
@@ -8588,19 +8685,27 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8588
8685
|
throw new StreamDataInitializeError();
|
|
8589
8686
|
}
|
|
8590
8687
|
}
|
|
8688
|
+
preStopCheck() {
|
|
8689
|
+
if (!this.recorder) {
|
|
8690
|
+
throw new RecorderInitializeError();
|
|
8691
|
+
}
|
|
8692
|
+
if (!this.stream.active) {
|
|
8693
|
+
throw new StreamInactiveInitializeError();
|
|
8694
|
+
}
|
|
8695
|
+
if (!this.stopPromise) {
|
|
8696
|
+
throw new NoStopPromiseError();
|
|
8697
|
+
}
|
|
8698
|
+
if (!this.filename) {
|
|
8699
|
+
throw new NoFileNameError();
|
|
8700
|
+
}
|
|
8701
|
+
}
|
|
8591
8702
|
handleStop(resolve) {
|
|
8592
|
-
return
|
|
8703
|
+
return () => {
|
|
8593
8704
|
if (this.blobs.length === 0) {
|
|
8594
8705
|
throw new CreateURLError();
|
|
8595
8706
|
}
|
|
8596
8707
|
this.url = URL.createObjectURL(new Blob(this.blobs));
|
|
8597
|
-
|
|
8598
|
-
this.download();
|
|
8599
|
-
} else {
|
|
8600
|
-
await this.s3.completeUpload();
|
|
8601
|
-
}
|
|
8602
|
-
this.reset();
|
|
8603
|
-
resolve();
|
|
8708
|
+
resolve(this.url);
|
|
8604
8709
|
};
|
|
8605
8710
|
}
|
|
8606
8711
|
handleDataAvailable(event) {
|
|
@@ -8609,11 +8714,11 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8609
8714
|
this.s3.onDataAvailable(event.data);
|
|
8610
8715
|
}
|
|
8611
8716
|
}
|
|
8612
|
-
download() {
|
|
8613
|
-
if (
|
|
8717
|
+
download(filename, url) {
|
|
8718
|
+
if (filename && url) {
|
|
8614
8719
|
const link = document.createElement("a");
|
|
8615
|
-
link.href =
|
|
8616
|
-
link.download =
|
|
8720
|
+
link.href = url;
|
|
8721
|
+
link.download = filename;
|
|
8617
8722
|
link.click();
|
|
8618
8723
|
}
|
|
8619
8724
|
}
|
|
@@ -8642,13 +8747,33 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8642
8747
|
const rand_digits = Math.floor(Math.random() * 1e3);
|
|
8643
8748
|
return `${prefix}_${window.chs.study.id}_${trial_id}_${window.chs.response.id}_${new Date().getTime()}_${rand_digits}.webm`;
|
|
8644
8749
|
}
|
|
8750
|
+
createTimeoutHandler(eventName, id) {
|
|
8751
|
+
return () => {
|
|
8752
|
+
console.warn(`Recorder ${eventName} timed out: ${id}`);
|
|
8753
|
+
if (!this.stream.active) {
|
|
8754
|
+
try {
|
|
8755
|
+
this.reset();
|
|
8756
|
+
} catch (err) {
|
|
8757
|
+
console.error("Error while resetting recorder after timeout: ", err);
|
|
8758
|
+
}
|
|
8759
|
+
}
|
|
8760
|
+
};
|
|
8761
|
+
}
|
|
8645
8762
|
}
|
|
8646
8763
|
|
|
8647
8764
|
const info$3 = {
|
|
8648
8765
|
name: "consent-video",
|
|
8649
8766
|
version: _package.version,
|
|
8650
8767
|
parameters: {
|
|
8651
|
-
template: {
|
|
8768
|
+
template: {
|
|
8769
|
+
type: jspsych.ParameterType.SELECT,
|
|
8770
|
+
options: [
|
|
8771
|
+
"consent-template-5",
|
|
8772
|
+
"consent-garden",
|
|
8773
|
+
"consent-recording-only"
|
|
8774
|
+
],
|
|
8775
|
+
default: "consent-template-5"
|
|
8776
|
+
},
|
|
8652
8777
|
locale: { type: jspsych.ParameterType.STRING, default: "en-us" },
|
|
8653
8778
|
additional_video_privacy_statement: {
|
|
8654
8779
|
type: jspsych.ParameterType.STRING,
|
|
@@ -8693,7 +8818,8 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8693
8818
|
prompt_all_adults: { type: jspsych.ParameterType.BOOL, default: false },
|
|
8694
8819
|
prompt_only_adults: { type: jspsych.ParameterType.BOOL, default: false },
|
|
8695
8820
|
consent_statement_text: { type: jspsych.ParameterType.STRING, default: "" },
|
|
8696
|
-
omit_injury_phrase: { type: jspsych.ParameterType.BOOL, default: false }
|
|
8821
|
+
omit_injury_phrase: { type: jspsych.ParameterType.BOOL, default: false },
|
|
8822
|
+
only_consent_on_chs: { type: jspsych.ParameterType.BOOL, default: false }
|
|
8697
8823
|
},
|
|
8698
8824
|
data: {
|
|
8699
8825
|
chs_type: {
|
|
@@ -8828,7 +8954,10 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8828
8954
|
stop.addEventListener("click", async () => {
|
|
8829
8955
|
stop.disabled = true;
|
|
8830
8956
|
this.addMessage(display, this.uploadingMsg);
|
|
8831
|
-
|
|
8957
|
+
const { stopped, uploaded } = this.recorder.stop({
|
|
8958
|
+
maintain_container_size: true
|
|
8959
|
+
});
|
|
8960
|
+
await stopped;
|
|
8832
8961
|
this.recordFeed(display);
|
|
8833
8962
|
this.getImg(display, "record-icon").style.visibility = "hidden";
|
|
8834
8963
|
this.addMessage(display, this.notRecordingMsg);
|
|
@@ -8854,7 +8983,16 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8854
8983
|
const info$2 = {
|
|
8855
8984
|
name: "start-record-plugin",
|
|
8856
8985
|
version: _package.version,
|
|
8857
|
-
parameters: {
|
|
8986
|
+
parameters: {
|
|
8987
|
+
wait_for_connection_message: {
|
|
8988
|
+
type: jspsych.ParameterType.HTML_STRING,
|
|
8989
|
+
default: null
|
|
8990
|
+
},
|
|
8991
|
+
locale: {
|
|
8992
|
+
type: jspsych.ParameterType.STRING,
|
|
8993
|
+
default: "en-us"
|
|
8994
|
+
}
|
|
8995
|
+
},
|
|
8858
8996
|
data: {}
|
|
8859
8997
|
};
|
|
8860
8998
|
class StartRecordPlugin {
|
|
@@ -8869,8 +9007,14 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8869
9007
|
}
|
|
8870
9008
|
static info = info$2;
|
|
8871
9009
|
recorder;
|
|
8872
|
-
trial() {
|
|
8873
|
-
|
|
9010
|
+
async trial(display_element, trial) {
|
|
9011
|
+
if (trial.wait_for_connection_message == null) {
|
|
9012
|
+
display_element.innerHTML = chsTemplates.establishingConnection(trial);
|
|
9013
|
+
} else {
|
|
9014
|
+
display_element.innerHTML = trial.wait_for_connection_message;
|
|
9015
|
+
}
|
|
9016
|
+
await this.recorder.start(false, `${StartRecordPlugin.info.name}-multiframe`).then(() => {
|
|
9017
|
+
display_element.innerHTML = "";
|
|
8874
9018
|
this.jsPsych.finishTrial();
|
|
8875
9019
|
});
|
|
8876
9020
|
}
|
|
@@ -8887,6 +9031,10 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8887
9031
|
locale: {
|
|
8888
9032
|
type: jspsych.ParameterType.STRING,
|
|
8889
9033
|
default: "en-us"
|
|
9034
|
+
},
|
|
9035
|
+
max_upload_seconds: {
|
|
9036
|
+
type: jspsych.ParameterType.INT,
|
|
9037
|
+
default: 10
|
|
8890
9038
|
}
|
|
8891
9039
|
},
|
|
8892
9040
|
data: {}
|
|
@@ -8902,19 +9050,25 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8902
9050
|
}
|
|
8903
9051
|
static info = info$1;
|
|
8904
9052
|
recorder;
|
|
8905
|
-
trial(display_element, trial) {
|
|
9053
|
+
async trial(display_element, trial) {
|
|
8906
9054
|
if (trial.wait_for_upload_message == null) {
|
|
8907
9055
|
display_element.innerHTML = chsTemplates.uploadingVideo(trial);
|
|
8908
9056
|
} else {
|
|
8909
9057
|
display_element.innerHTML = trial.wait_for_upload_message;
|
|
8910
9058
|
}
|
|
8911
|
-
this.recorder.stop(
|
|
9059
|
+
const { stopped, uploaded } = this.recorder.stop({
|
|
9060
|
+
upload_timeout_ms: trial.max_upload_seconds !== null ? trial.max_upload_seconds * 1e3 : null
|
|
9061
|
+
});
|
|
9062
|
+
try {
|
|
9063
|
+
await stopped;
|
|
9064
|
+
await uploaded;
|
|
9065
|
+
} catch (err) {
|
|
9066
|
+
console.error("StopRecordPlugin: recorder stop/upload failed.", err);
|
|
9067
|
+
} finally {
|
|
8912
9068
|
window.chs.sessionRecorder = null;
|
|
8913
9069
|
display_element.innerHTML = "";
|
|
8914
9070
|
this.jsPsych.finishTrial();
|
|
8915
|
-
}
|
|
8916
|
-
console.error("StopRecordPlugin: recorder stop/upload failed.", err);
|
|
8917
|
-
});
|
|
9071
|
+
}
|
|
8918
9072
|
}
|
|
8919
9073
|
}
|
|
8920
9074
|
|
|
@@ -8932,12 +9086,12 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8932
9086
|
pluginName;
|
|
8933
9087
|
uploadMsg = null;
|
|
8934
9088
|
locale = "en-us";
|
|
9089
|
+
maxUploadSeconds = void 0;
|
|
8935
9090
|
async initialize(params) {
|
|
8936
9091
|
await new Promise((resolve) => {
|
|
8937
9092
|
this.uploadMsg = params?.wait_for_upload_message ? params.wait_for_upload_message : null;
|
|
8938
9093
|
this.locale = params?.locale ? params.locale : "en-us";
|
|
8939
|
-
|
|
8940
|
-
console.log(this.locale);
|
|
9094
|
+
this.maxUploadSeconds = params?.max_upload_seconds === void 0 ? 10 : params.max_upload_seconds;
|
|
8941
9095
|
resolve();
|
|
8942
9096
|
});
|
|
8943
9097
|
}
|
|
@@ -8948,13 +9102,14 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8948
9102
|
if (startParams?.locale) {
|
|
8949
9103
|
this.locale = startParams.locale;
|
|
8950
9104
|
}
|
|
8951
|
-
|
|
8952
|
-
|
|
9105
|
+
if (startParams?.max_upload_seconds !== void 0) {
|
|
9106
|
+
this.maxUploadSeconds = startParams?.max_upload_seconds;
|
|
9107
|
+
}
|
|
8953
9108
|
this.recorder = new Recorder(this.jsPsych);
|
|
9109
|
+
this.pluginName = this.getCurrentPluginName();
|
|
9110
|
+
this.recorder.start(false, `${this.pluginName}`);
|
|
8954
9111
|
}
|
|
8955
9112
|
on_load() {
|
|
8956
|
-
this.pluginName = this.getCurrentPluginName();
|
|
8957
|
-
this.recorder?.start(false, `${this.pluginName}`);
|
|
8958
9113
|
}
|
|
8959
9114
|
async on_finish() {
|
|
8960
9115
|
const displayEl = this.jsPsych.getDisplayElement();
|
|
@@ -8966,13 +9121,27 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
8966
9121
|
} else {
|
|
8967
9122
|
displayEl.innerHTML = this.uploadMsg;
|
|
8968
9123
|
}
|
|
8969
|
-
|
|
8970
|
-
|
|
9124
|
+
if (this.recorder) {
|
|
9125
|
+
const { stopped, uploaded } = this.recorder.stop({
|
|
9126
|
+
upload_timeout_ms: this.maxUploadSeconds !== null ? this.maxUploadSeconds * 1e3 : null
|
|
9127
|
+
});
|
|
9128
|
+
try {
|
|
9129
|
+
await stopped;
|
|
9130
|
+
await uploaded;
|
|
9131
|
+
displayEl.innerHTML = "";
|
|
9132
|
+
return {};
|
|
9133
|
+
} catch (err) {
|
|
9134
|
+
console.error(
|
|
9135
|
+
"TrialRecordExtension: recorder stop/upload failed.",
|
|
9136
|
+
err
|
|
9137
|
+
);
|
|
9138
|
+
displayEl.innerHTML = "";
|
|
9139
|
+
return {};
|
|
9140
|
+
}
|
|
9141
|
+
} else {
|
|
8971
9142
|
displayEl.innerHTML = "";
|
|
8972
|
-
|
|
8973
|
-
console.error("TrialRecordExtension: recorder stop/upload failed.", err);
|
|
9143
|
+
return {};
|
|
8974
9144
|
}
|
|
8975
|
-
return {};
|
|
8976
9145
|
}
|
|
8977
9146
|
getCurrentPluginName() {
|
|
8978
9147
|
const current_plugin_class = this.jsPsych.getCurrentTrial().type;
|
|
@@ -9383,4 +9552,4 @@ var chsRecord = (function (Data, chsTemplates, jspsych) {
|
|
|
9383
9552
|
return index;
|
|
9384
9553
|
|
|
9385
9554
|
})(chsData, chsTemplates, jsPsychModule);
|
|
9386
|
-
//# sourceMappingURL=https://unpkg.com/@lookit/record@
|
|
9555
|
+
//# sourceMappingURL=https://unpkg.com/@lookit/record@6.0.0/dist/index.browser.js.map
|