@lookit/record 4.1.0 → 5.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 +82 -10
- package/dist/index.browser.js +201 -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 +200 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +83 -7
- package/dist/index.js +200 -40
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/consentVideo.spec.ts +9 -0
- package/src/consentVideo.ts +5 -1
- 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/dist/index.d.ts
CHANGED
|
@@ -404,7 +404,30 @@ declare class VideoConsentPlugin implements JsPsychPlugin<Info$3> {
|
|
|
404
404
|
declare const info$2: {
|
|
405
405
|
readonly name: "start-record-plugin";
|
|
406
406
|
readonly version: string;
|
|
407
|
-
readonly parameters: {
|
|
407
|
+
readonly parameters: {
|
|
408
|
+
/**
|
|
409
|
+
* This string can contain HTML markup. Any content provided will be
|
|
410
|
+
* displayed while the video recording connection is established. If null
|
|
411
|
+
* (the default), then the default 'establishing video connection, please
|
|
412
|
+
* wait' (or appropriate translation based on 'locale') will be displayed.
|
|
413
|
+
* Use a blank string for no message/content.
|
|
414
|
+
*/
|
|
415
|
+
readonly wait_for_connection_message: {
|
|
416
|
+
readonly type: ParameterType.HTML_STRING;
|
|
417
|
+
readonly default: string | null;
|
|
418
|
+
};
|
|
419
|
+
/**
|
|
420
|
+
* Locale code used for translating the default 'establishing video
|
|
421
|
+
* connection, please wait' message. This code must be present in the
|
|
422
|
+
* translation files. If the code is not found then English will be used. If
|
|
423
|
+
* the 'wait_for_connection_message' parameter is specified then this value
|
|
424
|
+
* is ignored.
|
|
425
|
+
*/
|
|
426
|
+
readonly locale: {
|
|
427
|
+
readonly type: ParameterType.STRING;
|
|
428
|
+
readonly default: "en-us";
|
|
429
|
+
};
|
|
430
|
+
};
|
|
408
431
|
readonly data: {};
|
|
409
432
|
};
|
|
410
433
|
type Info$2 = typeof info$2;
|
|
@@ -414,7 +437,30 @@ declare class StartRecordPlugin implements JsPsychPlugin<Info$2> {
|
|
|
414
437
|
static readonly info: {
|
|
415
438
|
readonly name: "start-record-plugin";
|
|
416
439
|
readonly version: string;
|
|
417
|
-
readonly parameters: {
|
|
440
|
+
readonly parameters: {
|
|
441
|
+
/**
|
|
442
|
+
* This string can contain HTML markup. Any content provided will be
|
|
443
|
+
* displayed while the video recording connection is established. If null
|
|
444
|
+
* (the default), then the default 'establishing video connection, please
|
|
445
|
+
* wait' (or appropriate translation based on 'locale') will be displayed.
|
|
446
|
+
* Use a blank string for no message/content.
|
|
447
|
+
*/
|
|
448
|
+
readonly wait_for_connection_message: {
|
|
449
|
+
readonly type: ParameterType.HTML_STRING;
|
|
450
|
+
readonly default: string | null;
|
|
451
|
+
};
|
|
452
|
+
/**
|
|
453
|
+
* Locale code used for translating the default 'establishing video
|
|
454
|
+
* connection, please wait' message. This code must be present in the
|
|
455
|
+
* translation files. If the code is not found then English will be used. If
|
|
456
|
+
* the 'wait_for_connection_message' parameter is specified then this value
|
|
457
|
+
* is ignored.
|
|
458
|
+
*/
|
|
459
|
+
readonly locale: {
|
|
460
|
+
readonly type: ParameterType.STRING;
|
|
461
|
+
readonly default: "en-us";
|
|
462
|
+
};
|
|
463
|
+
};
|
|
418
464
|
readonly data: {};
|
|
419
465
|
};
|
|
420
466
|
private recorder;
|
|
@@ -424,8 +470,15 @@ declare class StartRecordPlugin implements JsPsychPlugin<Info$2> {
|
|
|
424
470
|
* @param jsPsych - Object provided by jsPsych.
|
|
425
471
|
*/
|
|
426
472
|
constructor(jsPsych: JsPsych);
|
|
427
|
-
/**
|
|
428
|
-
|
|
473
|
+
/**
|
|
474
|
+
* Trial function called by jsPsych.
|
|
475
|
+
*
|
|
476
|
+
* @param display_element - DOM element where jsPsych content is being
|
|
477
|
+
* rendered (set in initJsPsych and automatically made available to a
|
|
478
|
+
* plugin's trial method via jsPsych core).
|
|
479
|
+
* @param trial - Trial object with parameters/values.
|
|
480
|
+
*/
|
|
481
|
+
trial(display_element: HTMLElement, trial: TrialType<Info$2>): Promise<void>;
|
|
429
482
|
}
|
|
430
483
|
|
|
431
484
|
declare const info$1: {
|
|
@@ -454,6 +507,14 @@ declare const info$1: {
|
|
|
454
507
|
readonly type: ParameterType.STRING;
|
|
455
508
|
readonly default: "en-us";
|
|
456
509
|
};
|
|
510
|
+
/**
|
|
511
|
+
* Maximum duration (in seconds) to wait for the session recording to finish
|
|
512
|
+
* uploading before continuing with the experiment.
|
|
513
|
+
*/
|
|
514
|
+
readonly max_upload_seconds: {
|
|
515
|
+
readonly type: ParameterType.INT;
|
|
516
|
+
readonly default: 10;
|
|
517
|
+
};
|
|
457
518
|
};
|
|
458
519
|
readonly data: {};
|
|
459
520
|
};
|
|
@@ -487,6 +548,14 @@ declare class StopRecordPlugin implements JsPsychPlugin<Info$1> {
|
|
|
487
548
|
readonly type: ParameterType.STRING;
|
|
488
549
|
readonly default: "en-us";
|
|
489
550
|
};
|
|
551
|
+
/**
|
|
552
|
+
* Maximum duration (in seconds) to wait for the session recording to finish
|
|
553
|
+
* uploading before continuing with the experiment.
|
|
554
|
+
*/
|
|
555
|
+
readonly max_upload_seconds: {
|
|
556
|
+
readonly type: ParameterType.INT;
|
|
557
|
+
readonly default: 10;
|
|
558
|
+
};
|
|
490
559
|
};
|
|
491
560
|
readonly data: {};
|
|
492
561
|
};
|
|
@@ -505,7 +574,7 @@ declare class StopRecordPlugin implements JsPsychPlugin<Info$1> {
|
|
|
505
574
|
* plugin's trial method via jsPsych core).
|
|
506
575
|
* @param trial - Trial object with parameters/values.
|
|
507
576
|
*/
|
|
508
|
-
trial(display_element: HTMLElement, trial: TrialType<Info$1>): void
|
|
577
|
+
trial(display_element: HTMLElement, trial: TrialType<Info$1>): Promise<void>;
|
|
509
578
|
}
|
|
510
579
|
|
|
511
580
|
interface Parameters {
|
|
@@ -526,12 +595,18 @@ interface Parameters {
|
|
|
526
595
|
* Locale code used for translating the default 'uploading video, please
|
|
527
596
|
* wait...' message. This code must be present in the translation files. If
|
|
528
597
|
* the code is not found then English will be used. If the
|
|
529
|
-
* 'wait_for_upload_message' parameter is specified then this value
|
|
530
|
-
*
|
|
598
|
+
* 'wait_for_upload_message' parameter is specified then this value is
|
|
599
|
+
* ignored.
|
|
531
600
|
*
|
|
532
601
|
* @default "en-us"
|
|
533
602
|
*/
|
|
534
603
|
locale?: string;
|
|
604
|
+
/**
|
|
605
|
+
* Maximum duration (in seconds) to wait for the trial recording to finish
|
|
606
|
+
* uploading before continuing with the experiment. Default is 10 seconds (set
|
|
607
|
+
* during initialize).
|
|
608
|
+
*/
|
|
609
|
+
max_upload_seconds?: null | number;
|
|
535
610
|
}
|
|
536
611
|
/** This extension allows researchers to record webcam audio/video during trials. */
|
|
537
612
|
declare class TrialRecordExtension implements JsPsychExtension {
|
|
@@ -541,6 +616,7 @@ declare class TrialRecordExtension implements JsPsychExtension {
|
|
|
541
616
|
private pluginName;
|
|
542
617
|
private uploadMsg;
|
|
543
618
|
private locale;
|
|
619
|
+
private maxUploadSeconds;
|
|
544
620
|
/**
|
|
545
621
|
* Video recording extension.
|
|
546
622
|
*
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import Handlebars from 'handlebars';
|
|
|
6
6
|
|
|
7
7
|
var _package = {
|
|
8
8
|
name: "@lookit/record",
|
|
9
|
-
version: "
|
|
9
|
+
version: "5.0.0",
|
|
10
10
|
description: "Recording extensions and plugins for CHS studies.",
|
|
11
11
|
homepage: "https://github.com/lookit/lookit-jspsych#readme",
|
|
12
12
|
bugs: {
|
|
@@ -47,8 +47,8 @@ var _package = {
|
|
|
47
47
|
typescript: "^5.6.2"
|
|
48
48
|
},
|
|
49
49
|
peerDependencies: {
|
|
50
|
-
"@lookit/data": "^0.
|
|
51
|
-
"@lookit/templates": "^
|
|
50
|
+
"@lookit/data": "^0.3.0",
|
|
51
|
+
"@lookit/templates": "^3.0.0",
|
|
52
52
|
jspsych: "^8.0.3"
|
|
53
53
|
}
|
|
54
54
|
};
|
|
@@ -123,6 +123,12 @@ class S3UndefinedError extends Error {
|
|
|
123
123
|
this.name = "S3UndefinedError";
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
|
+
class NoFileNameError extends Error {
|
|
127
|
+
constructor() {
|
|
128
|
+
super("No filename found for recording.");
|
|
129
|
+
this.name = "NoFileNameError";
|
|
130
|
+
}
|
|
131
|
+
}
|
|
126
132
|
class StreamActiveOnResetError extends Error {
|
|
127
133
|
constructor() {
|
|
128
134
|
super("Won't reset recorder. Stream is still active.");
|
|
@@ -147,6 +153,12 @@ class ElementNotFoundError extends Error {
|
|
|
147
153
|
this.name = "ElementNotFoundError";
|
|
148
154
|
}
|
|
149
155
|
}
|
|
156
|
+
class TimeoutError extends Error {
|
|
157
|
+
constructor(msg) {
|
|
158
|
+
super(`${msg}`);
|
|
159
|
+
this.name = "TimeoutError";
|
|
160
|
+
}
|
|
161
|
+
}
|
|
150
162
|
|
|
151
163
|
var playbackFeed = "<video\n autoplay\n playsinline\n src=\"{{{src}}}\"\n class=\"webcam-feed\"\n id=\"{{webcam_element_id}}\"\n width=\"{{width}}\"\n height=\"{{height}}\"\n controls\n></video>";
|
|
152
164
|
|
|
@@ -158,6 +170,31 @@ var img$8 = "data:image/svg+xml,%3c%3fxml version='1.0' encoding='utf-8' %3f%3e%
|
|
|
158
170
|
|
|
159
171
|
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";
|
|
160
172
|
|
|
173
|
+
const promiseWithTimeout = (promise, promiseId, timeoutMs, onTimeoutCleanup) => {
|
|
174
|
+
let timeoutHandle;
|
|
175
|
+
const timeout = new Promise((resolve) => {
|
|
176
|
+
timeoutHandle = setTimeout(() => {
|
|
177
|
+
onTimeoutCleanup?.();
|
|
178
|
+
resolve("timeout");
|
|
179
|
+
}, timeoutMs);
|
|
180
|
+
});
|
|
181
|
+
return Promise.race([promise, timeout]).then(
|
|
182
|
+
(value) => {
|
|
183
|
+
if (value == "timeout") {
|
|
184
|
+
console.log(`Upload for ${promiseId} timed out.`);
|
|
185
|
+
} else {
|
|
186
|
+
console.log(`Upload for ${promiseId} completed.`);
|
|
187
|
+
clearTimeout(timeoutHandle);
|
|
188
|
+
}
|
|
189
|
+
return value;
|
|
190
|
+
},
|
|
191
|
+
(err) => {
|
|
192
|
+
clearTimeout(timeoutHandle);
|
|
193
|
+
throw err;
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
161
198
|
class Recorder {
|
|
162
199
|
constructor(jsPsych) {
|
|
163
200
|
this.jsPsych = jsPsych;
|
|
@@ -257,13 +294,73 @@ class Recorder {
|
|
|
257
294
|
this.recorder.stop();
|
|
258
295
|
this.stream.getTracks().map((t) => t.stop());
|
|
259
296
|
}
|
|
260
|
-
stop(
|
|
297
|
+
stop({
|
|
298
|
+
maintain_container_size = false,
|
|
299
|
+
stop_timeout_ms = null,
|
|
300
|
+
upload_timeout_ms = 1e4
|
|
301
|
+
} = {}) {
|
|
302
|
+
this.preStopCheck();
|
|
261
303
|
this.clearWebcamFeed(maintain_container_size);
|
|
262
304
|
this.stopTracks();
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
305
|
+
const snapshot = {
|
|
306
|
+
s3: !this.localDownload ? this.s3 : null,
|
|
307
|
+
filename: this.filename,
|
|
308
|
+
localDownload: this.localDownload,
|
|
309
|
+
url: "null"
|
|
310
|
+
};
|
|
311
|
+
const stopped = stop_timeout_ms ? promiseWithTimeout(
|
|
312
|
+
this.stopPromise,
|
|
313
|
+
`${snapshot.filename}-stopped`,
|
|
314
|
+
stop_timeout_ms,
|
|
315
|
+
this.createTimeoutHandler("stop", snapshot.filename)
|
|
316
|
+
) : this.stopPromise;
|
|
317
|
+
stopped.finally(() => {
|
|
318
|
+
try {
|
|
319
|
+
this.reset();
|
|
320
|
+
} catch (err) {
|
|
321
|
+
console.error("Error while resetting recorder after stop: ", err);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
const uploadPromise = (async () => {
|
|
325
|
+
let url;
|
|
326
|
+
try {
|
|
327
|
+
url = await stopped;
|
|
328
|
+
if (url == "timeout") {
|
|
329
|
+
throw new TimeoutError("Recorder stop timed out.");
|
|
330
|
+
}
|
|
331
|
+
} catch (err) {
|
|
332
|
+
console.warn("Upload failed because recorder stop timed out");
|
|
333
|
+
throw err;
|
|
334
|
+
}
|
|
335
|
+
snapshot.url = url;
|
|
336
|
+
if (snapshot.localDownload) {
|
|
337
|
+
try {
|
|
338
|
+
this.download(snapshot.filename, snapshot.url);
|
|
339
|
+
await Promise.resolve();
|
|
340
|
+
} catch (err) {
|
|
341
|
+
console.error("Local download failed: ", err);
|
|
342
|
+
throw err;
|
|
343
|
+
}
|
|
344
|
+
} else {
|
|
345
|
+
try {
|
|
346
|
+
await snapshot.s3.completeUpload();
|
|
347
|
+
} catch (err) {
|
|
348
|
+
console.error("Upload failed: ", err);
|
|
349
|
+
throw err;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
})();
|
|
353
|
+
const uploaded = upload_timeout_ms ? promiseWithTimeout(
|
|
354
|
+
uploadPromise,
|
|
355
|
+
`${snapshot.filename}-uploaded`,
|
|
356
|
+
upload_timeout_ms,
|
|
357
|
+
this.createTimeoutHandler("upload", snapshot.filename)
|
|
358
|
+
) : uploadPromise;
|
|
359
|
+
window.chs.pendingUploads.push({
|
|
360
|
+
promise: uploadPromise,
|
|
361
|
+
file: snapshot.filename
|
|
362
|
+
});
|
|
363
|
+
return { stopped, uploaded };
|
|
267
364
|
}
|
|
268
365
|
initializeCheck() {
|
|
269
366
|
if (!this.recorder) {
|
|
@@ -276,19 +373,27 @@ class Recorder {
|
|
|
276
373
|
throw new StreamDataInitializeError();
|
|
277
374
|
}
|
|
278
375
|
}
|
|
376
|
+
preStopCheck() {
|
|
377
|
+
if (!this.recorder) {
|
|
378
|
+
throw new RecorderInitializeError();
|
|
379
|
+
}
|
|
380
|
+
if (!this.stream.active) {
|
|
381
|
+
throw new StreamInactiveInitializeError();
|
|
382
|
+
}
|
|
383
|
+
if (!this.stopPromise) {
|
|
384
|
+
throw new NoStopPromiseError();
|
|
385
|
+
}
|
|
386
|
+
if (!this.filename) {
|
|
387
|
+
throw new NoFileNameError();
|
|
388
|
+
}
|
|
389
|
+
}
|
|
279
390
|
handleStop(resolve) {
|
|
280
|
-
return
|
|
391
|
+
return () => {
|
|
281
392
|
if (this.blobs.length === 0) {
|
|
282
393
|
throw new CreateURLError();
|
|
283
394
|
}
|
|
284
395
|
this.url = URL.createObjectURL(new Blob(this.blobs));
|
|
285
|
-
|
|
286
|
-
this.download();
|
|
287
|
-
} else {
|
|
288
|
-
await this.s3.completeUpload();
|
|
289
|
-
}
|
|
290
|
-
this.reset();
|
|
291
|
-
resolve();
|
|
396
|
+
resolve(this.url);
|
|
292
397
|
};
|
|
293
398
|
}
|
|
294
399
|
handleDataAvailable(event) {
|
|
@@ -297,11 +402,11 @@ class Recorder {
|
|
|
297
402
|
this.s3.onDataAvailable(event.data);
|
|
298
403
|
}
|
|
299
404
|
}
|
|
300
|
-
download() {
|
|
301
|
-
if (
|
|
405
|
+
download(filename, url) {
|
|
406
|
+
if (filename && url) {
|
|
302
407
|
const link = document.createElement("a");
|
|
303
|
-
link.href =
|
|
304
|
-
link.download =
|
|
408
|
+
link.href = url;
|
|
409
|
+
link.download = filename;
|
|
305
410
|
link.click();
|
|
306
411
|
}
|
|
307
412
|
}
|
|
@@ -330,6 +435,18 @@ class Recorder {
|
|
|
330
435
|
const rand_digits = Math.floor(Math.random() * 1e3);
|
|
331
436
|
return `${prefix}_${window.chs.study.id}_${trial_id}_${window.chs.response.id}_${new Date().getTime()}_${rand_digits}.webm`;
|
|
332
437
|
}
|
|
438
|
+
createTimeoutHandler(eventName, id) {
|
|
439
|
+
return () => {
|
|
440
|
+
console.warn(`Recorder ${eventName} timed out: ${id}`);
|
|
441
|
+
if (!this.stream.active) {
|
|
442
|
+
try {
|
|
443
|
+
this.reset();
|
|
444
|
+
} catch (err) {
|
|
445
|
+
console.error("Error while resetting recorder after timeout: ", err);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
}
|
|
333
450
|
}
|
|
334
451
|
|
|
335
452
|
const info$3 = {
|
|
@@ -514,7 +631,10 @@ const _VideoConsentPlugin = class {
|
|
|
514
631
|
stop.addEventListener("click", async () => {
|
|
515
632
|
stop.disabled = true;
|
|
516
633
|
this.addMessage(display, this.uploadingMsg);
|
|
517
|
-
|
|
634
|
+
const { stopped, uploaded } = this.recorder.stop({
|
|
635
|
+
maintain_container_size: true
|
|
636
|
+
});
|
|
637
|
+
await stopped;
|
|
518
638
|
this.recordFeed(display);
|
|
519
639
|
this.getImg(display, "record-icon").style.visibility = "hidden";
|
|
520
640
|
this.addMessage(display, this.notRecordingMsg);
|
|
@@ -542,7 +662,16 @@ VideoConsentPlugin.info = info$3;
|
|
|
542
662
|
const info$2 = {
|
|
543
663
|
name: "start-record-plugin",
|
|
544
664
|
version: _package.version,
|
|
545
|
-
parameters: {
|
|
665
|
+
parameters: {
|
|
666
|
+
wait_for_connection_message: {
|
|
667
|
+
type: ParameterType.HTML_STRING,
|
|
668
|
+
default: null
|
|
669
|
+
},
|
|
670
|
+
locale: {
|
|
671
|
+
type: ParameterType.STRING,
|
|
672
|
+
default: "en-us"
|
|
673
|
+
}
|
|
674
|
+
},
|
|
546
675
|
data: {}
|
|
547
676
|
};
|
|
548
677
|
const _StartRecordPlugin = class {
|
|
@@ -555,8 +684,14 @@ const _StartRecordPlugin = class {
|
|
|
555
684
|
throw new ExistingRecordingError();
|
|
556
685
|
}
|
|
557
686
|
}
|
|
558
|
-
trial() {
|
|
559
|
-
|
|
687
|
+
async trial(display_element, trial) {
|
|
688
|
+
if (trial.wait_for_connection_message == null) {
|
|
689
|
+
display_element.innerHTML = chsTemplates.establishingConnection(trial);
|
|
690
|
+
} else {
|
|
691
|
+
display_element.innerHTML = trial.wait_for_connection_message;
|
|
692
|
+
}
|
|
693
|
+
await this.recorder.start(false, `${_StartRecordPlugin.info.name}-multiframe`).then(() => {
|
|
694
|
+
display_element.innerHTML = "";
|
|
560
695
|
this.jsPsych.finishTrial();
|
|
561
696
|
});
|
|
562
697
|
}
|
|
@@ -575,6 +710,10 @@ const info$1 = {
|
|
|
575
710
|
locale: {
|
|
576
711
|
type: ParameterType.STRING,
|
|
577
712
|
default: "en-us"
|
|
713
|
+
},
|
|
714
|
+
max_upload_seconds: {
|
|
715
|
+
type: ParameterType.INT,
|
|
716
|
+
default: 10
|
|
578
717
|
}
|
|
579
718
|
},
|
|
580
719
|
data: {}
|
|
@@ -588,19 +727,25 @@ class StopRecordPlugin {
|
|
|
588
727
|
throw new NoSessionRecordingError();
|
|
589
728
|
}
|
|
590
729
|
}
|
|
591
|
-
trial(display_element, trial) {
|
|
730
|
+
async trial(display_element, trial) {
|
|
592
731
|
if (trial.wait_for_upload_message == null) {
|
|
593
732
|
display_element.innerHTML = chsTemplates.uploadingVideo(trial);
|
|
594
733
|
} else {
|
|
595
734
|
display_element.innerHTML = trial.wait_for_upload_message;
|
|
596
735
|
}
|
|
597
|
-
this.recorder.stop(
|
|
736
|
+
const { stopped, uploaded } = this.recorder.stop({
|
|
737
|
+
upload_timeout_ms: trial.max_upload_seconds !== null ? trial.max_upload_seconds * 1e3 : null
|
|
738
|
+
});
|
|
739
|
+
try {
|
|
740
|
+
await stopped;
|
|
741
|
+
await uploaded;
|
|
742
|
+
} catch (err) {
|
|
743
|
+
console.error("StopRecordPlugin: recorder stop/upload failed.", err);
|
|
744
|
+
} finally {
|
|
598
745
|
window.chs.sessionRecorder = null;
|
|
599
746
|
display_element.innerHTML = "";
|
|
600
747
|
this.jsPsych.finishTrial();
|
|
601
|
-
}
|
|
602
|
-
console.error("StopRecordPlugin: recorder stop/upload failed.", err);
|
|
603
|
-
});
|
|
748
|
+
}
|
|
604
749
|
}
|
|
605
750
|
}
|
|
606
751
|
StopRecordPlugin.info = info$1;
|
|
@@ -610,14 +755,14 @@ class TrialRecordExtension {
|
|
|
610
755
|
this.jsPsych = jsPsych;
|
|
611
756
|
this.uploadMsg = null;
|
|
612
757
|
this.locale = "en-us";
|
|
758
|
+
this.maxUploadSeconds = void 0;
|
|
613
759
|
autoBind(this);
|
|
614
760
|
}
|
|
615
761
|
async initialize(params) {
|
|
616
762
|
await new Promise((resolve) => {
|
|
617
763
|
this.uploadMsg = params?.wait_for_upload_message ? params.wait_for_upload_message : null;
|
|
618
764
|
this.locale = params?.locale ? params.locale : "en-us";
|
|
619
|
-
|
|
620
|
-
console.log(this.locale);
|
|
765
|
+
this.maxUploadSeconds = params?.max_upload_seconds === void 0 ? 10 : params.max_upload_seconds;
|
|
621
766
|
resolve();
|
|
622
767
|
});
|
|
623
768
|
}
|
|
@@ -628,13 +773,14 @@ class TrialRecordExtension {
|
|
|
628
773
|
if (startParams?.locale) {
|
|
629
774
|
this.locale = startParams.locale;
|
|
630
775
|
}
|
|
631
|
-
|
|
632
|
-
|
|
776
|
+
if (startParams?.max_upload_seconds !== void 0) {
|
|
777
|
+
this.maxUploadSeconds = startParams?.max_upload_seconds;
|
|
778
|
+
}
|
|
633
779
|
this.recorder = new Recorder(this.jsPsych);
|
|
780
|
+
this.pluginName = this.getCurrentPluginName();
|
|
781
|
+
this.recorder.start(false, `${this.pluginName}`);
|
|
634
782
|
}
|
|
635
783
|
on_load() {
|
|
636
|
-
this.pluginName = this.getCurrentPluginName();
|
|
637
|
-
this.recorder?.start(false, `${this.pluginName}`);
|
|
638
784
|
}
|
|
639
785
|
async on_finish() {
|
|
640
786
|
const displayEl = this.jsPsych.getDisplayElement();
|
|
@@ -646,13 +792,27 @@ class TrialRecordExtension {
|
|
|
646
792
|
} else {
|
|
647
793
|
displayEl.innerHTML = this.uploadMsg;
|
|
648
794
|
}
|
|
649
|
-
|
|
650
|
-
|
|
795
|
+
if (this.recorder) {
|
|
796
|
+
const { stopped, uploaded } = this.recorder.stop({
|
|
797
|
+
upload_timeout_ms: this.maxUploadSeconds !== null ? this.maxUploadSeconds * 1e3 : null
|
|
798
|
+
});
|
|
799
|
+
try {
|
|
800
|
+
await stopped;
|
|
801
|
+
await uploaded;
|
|
802
|
+
displayEl.innerHTML = "";
|
|
803
|
+
return {};
|
|
804
|
+
} catch (err) {
|
|
805
|
+
console.error(
|
|
806
|
+
"TrialRecordExtension: recorder stop/upload failed.",
|
|
807
|
+
err
|
|
808
|
+
);
|
|
809
|
+
displayEl.innerHTML = "";
|
|
810
|
+
return {};
|
|
811
|
+
}
|
|
812
|
+
} else {
|
|
651
813
|
displayEl.innerHTML = "";
|
|
652
|
-
|
|
653
|
-
console.error("TrialRecordExtension: recorder stop/upload failed.", err);
|
|
814
|
+
return {};
|
|
654
815
|
}
|
|
655
|
-
return {};
|
|
656
816
|
}
|
|
657
817
|
getCurrentPluginName() {
|
|
658
818
|
const current_plugin_class = this.jsPsych.getCurrentTrial().type;
|