Skip to content

Commit

Permalink
Add replace webcam feed function with record feed function
Browse files Browse the repository at this point in the history
  • Loading branch information
okaycj committed Oct 7, 2024
1 parent dc3c911 commit f4c8ab5
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 72 deletions.
66 changes: 29 additions & 37 deletions packages/record/src/consentVideo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,32 +19,36 @@ const info = <const>{
version,
parameters: {
template: { type: ParameterType.STRING, default: "consent_005" },
additional_video_privacy_statement: { type: ParameterType.STRING },
datause: { type: ParameterType.STRING },
locale: { type: ParameterType.STRING, default: "en-us" },
additional_video_privacy_statement: {
type: ParameterType.STRING,
default: "",
},
datause: { type: ParameterType.STRING, default: "" },
gdpr: { type: ParameterType.BOOL, default: false },
gdpr_personal_data: { type: ParameterType.STRING },
gdpr_sensitive_data: { type: ParameterType.STRING },
PIName: { type: ParameterType.STRING },
gdpr_personal_data: { type: ParameterType.STRING, default: "" },
gdpr_sensitive_data: { type: ParameterType.STRING, default: "" },
PIName: { type: ParameterType.STRING, default: "" },
include_databrary: { type: ParameterType.BOOL, default: false },
institution: { type: ParameterType.STRING },
PIContact: { type: ParameterType.STRING },
payment: { type: ParameterType.STRING },
institution: { type: ParameterType.STRING, default: "" },
PIContact: { type: ParameterType.STRING, default: "" },
payment: { type: ParameterType.STRING, default: "" },
private_level_only: { type: ParameterType.BOOL, default: false },
procedures: { type: ParameterType.STRING },
procedures: { type: ParameterType.STRING, default: "" },
purpose: { type: ParameterType.STRING, default: "" },
research_rights_statement: { type: ParameterType.STRING },
risk_statement: { type: ParameterType.STRING },
voluntary_participation: { type: ParameterType.STRING },
research_rights_statement: { type: ParameterType.STRING, default: "" },
risk_statement: { type: ParameterType.STRING, default: "" },
voluntary_participation: { type: ParameterType.STRING, default: "" },
purpose_header: { type: ParameterType.STRING, default: "" },
procedures_header: { type: ParameterType.STRING, default: "" },
participation_header: { type: ParameterType.STRING, default: "" },
benefits_header: { type: ParameterType.STRING, default: "" },
risk_header: { type: ParameterType.STRING, default: "" },
summary_statement: { type: ParameterType.STRING },
summary_statement: { type: ParameterType.STRING, default: "" },
additional_segments: { type: ParameterType.COMPLEX },
prompt_all_adults: { type: ParameterType.BOOL, default: false },
prompt_only_adults: { type: ParameterType.BOOL, default: false },
consent_statement_text: { type: ParameterType.STRING },
consent_statement_text: { type: ParameterType.STRING, default: "" },
omit_injury_phrase: { type: ParameterType.BOOL, default: false },
},
};
Expand Down Expand Up @@ -99,7 +103,7 @@ export class VideoConsentPlugin implements JsPsychPlugin<Info> {
display.insertAdjacentHTML("afterbegin", consentVideoTrial);

// Video recording HTML
this.webcamFeed(display);
this.recordFeed(display);
this.recordButton(display);
this.stopButton(display);
this.playButton(display);
Expand Down Expand Up @@ -128,9 +132,9 @@ export class VideoConsentPlugin implements JsPsychPlugin<Info> {
*
* @param display - HTML element for experiment.
*/
private webcamFeed(display: HTMLElement) {
private recordFeed(display: HTMLElement) {
const videoContainer = this.getVideoContainer(display);
this.recorder.insertWebcamFeed(videoContainer);
this.recorder.insertRecordFeed(videoContainer);
this.getImg(display, "record-icon").style.visibility = "hidden";
}

Expand All @@ -155,9 +159,12 @@ export class VideoConsentPlugin implements JsPsychPlugin<Info> {
return () => {
const next = this.getButton(display, "next");
const play = this.getButton(display, "play");
this.webcamFeed(display);
const record = this.getButton(display, "record");

this.recordFeed(display);
next.disabled = false;
play.disabled = false;
record.disabled = false;
};
}

Expand Down Expand Up @@ -224,9 +231,11 @@ export class VideoConsentPlugin implements JsPsychPlugin<Info> {
*/
private playButton(display: HTMLElement) {
const play = this.getButton(display, "play");
const record = this.getButton(display, "record");

play.addEventListener("click", () => {
play.disabled = true;
record.disabled = true;
this.playbackFeed(display);
});
}
Expand All @@ -240,13 +249,14 @@ export class VideoConsentPlugin implements JsPsychPlugin<Info> {
const stop = this.getButton(display, "stop");
const record = this.getButton(display, "record");
const play = this.getButton(display, "play");

stop.addEventListener("click", async () => {
stop.disabled = true;
record.disabled = false;
play.disabled = false;
await this.recorder.stop();
this.recorder.reset();
this.webcamFeed(display);
this.recordFeed(display);
});
}
/**
Expand All @@ -259,21 +269,3 @@ export class VideoConsentPlugin implements JsPsychPlugin<Info> {
next.addEventListener("click", () => this.jsPsych.finishTrial());
}
}

// type Objectx ={
// type:string
// }

// interface ObjectA extends Objectx {
// type: "A";
// value1: string;
// value2: number;
// };

// interface ObjectB extends Objectx {
// type: "B";
// valueA: string;
// valueB: number;
// };

// type TypeA<I extends Objectx>
89 changes: 61 additions & 28 deletions packages/record/src/recorder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ import Mustache from "mustache";
import play_icon from "../img/play-icon.svg";
import record_icon from "../img/record-icon.svg";
import playbackFeed from "../templates/playback-feed.mustache";
import recordFeed from "../templates/record-feed.hbs";
import webcamFeed from "../templates/webcam-feed.mustache";
import {
CreateURLError,
NoPlayBackElementError,
NoStopPromiseError,
NoWebCamElementError,
RecorderInitializeError,
Expand All @@ -31,7 +31,6 @@ export default class Recorder {
private filename?: string;
private stopPromise?: Promise<void>;
private webcam_element_id = "lookit-jspsych-webcam";
private playback_element_id = "lookit-jspsych-playback";

private streamClone: MediaStream;

Expand Down Expand Up @@ -109,6 +108,39 @@ export default class Recorder {
this.blobs = [];
}

/**
* Insert a rendered template into an element.
*
* @param element - Element to have video inserted into.
* @param template - Template string
* @param insertStream - Should the stream be attributed to the webcam
* element.
* @returns Webcam element
*/
private insertVideoFeed(
element: HTMLDivElement,
template: string,
insertStream: boolean = true,
) {
const { webcam_element_id, stream } = this;

element.innerHTML = template;

const webcam = element.querySelector<HTMLVideoElement>(
`#${webcam_element_id}`,
);

if (!webcam) {
throw new NoWebCamElementError();
}

if (insertStream) {
webcam.srcObject = stream;
}

return webcam;
}

/**
* Insert a video element containing the webcam feed onto the page.
*
Expand All @@ -124,18 +156,9 @@ export default class Recorder {
width: CSSWidthHeight = "100%",
height: CSSWidthHeight = "auto",
) {
const { webcam_element_id, stream } = this;
const view = { height, width, webcam_element_id, record_icon };
element.innerHTML = Mustache.render(webcamFeed, view);
const webcam = element.querySelector<HTMLVideoElement>(
`#${webcam_element_id}`,
);

if (!webcam) {
throw new NoWebCamElementError();
}

webcam.srcObject = stream;
const { webcam_element_id } = this;
const view = { height, width, webcam_element_id };
this.insertVideoFeed(element, Mustache.render(webcamFeed, view));
}

/**
Expand All @@ -155,33 +178,43 @@ export default class Recorder {
width: CSSWidthHeight = "100%",
height: CSSWidthHeight = "auto",
) {
const { playback_element_id } = this;
const { webcam_element_id } = this;
const view = {
src: this.url,
width,
height,
playback_element_id,
webcam_element_id,
play_icon,
};

this.clearWebcamFeed();

element.insertAdjacentHTML(
"afterbegin",
const playbackElement = this.insertVideoFeed(
element,
Mustache.render(playbackFeed, view),
false,
);

const playbackElement = element.querySelector<HTMLVideoElement>(
`video#${this.playback_element_id}`,
);

if (!playbackElement) {
throw new NoPlayBackElementError();
}

playbackElement.addEventListener("ended", on_ended, { once: true });
}

/**
* Insert a feed to be used for recording into an element.
*
* @param element - Element to have record feed inserted into.
* @param width - The width of the video element containing the webcam feed,
* in CSS units (optional). Default is `'100%'`
* @param height - The height of the video element containing the webcam feed,
* in CSS units (optional). Default is `'auto'`
*/
public insertRecordFeed(
element: HTMLDivElement,
width: CSSWidthHeight = "100%",
height: CSSWidthHeight = "auto",
) {
const { webcam_element_id } = this;
const view = { height, width, webcam_element_id, record_icon };
this.insertVideoFeed(element, Mustache.render(recordFeed, view));
}

/**
* Start recording. Also, adds event listeners for handling data and checks
* for recorder initialization.
Expand Down
7 changes: 3 additions & 4 deletions packages/record/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,15 @@ export const expFormat = (text?: string | string[]) => {
* @param trial - Trial data including user supplied parameters.
*/
const initI18next = (trial: TrialType<PluginInfo>) => {
const { locale } = trial;
const translation = Yaml.load(en_us) as Record<string, string>;
const a2Code = locale.split("-")[0];
const debug = process.env.DEBUG === "true";
const { baseName, language } = new Intl.Locale(trial.locale);

i18next.use(ICU).init({
lng: locale,
lng: baseName,
debug,
resources: {
[a2Code]: {
[language]: {
translation,
},
},
Expand Down
4 changes: 2 additions & 2 deletions packages/record/templates/consent-document.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
<p>{{t "consent-template-5.video-privacy-withdraw"}}</p>
{{/if}}
{{#if include_databrary}}
<p>{{& t "consent-template-5.databrary"}}</p>
<p>{{{t "consent-template-5.databrary"}}}</p>
{{/if}}
{{#if additional_video_privacy_statement}}
<p>{{exp-format additional_video_privacy_statement}}</p>
Expand Down Expand Up @@ -147,7 +147,7 @@
{{/if}}

<h2>{{t "consent-template-5.research-subject-rights-header"}}</h2>
<p>{{& exp-format research_rights_statement}}</p>
<p>{{{exp-format research_rights_statement}}}</p>

{{#if gdpr}}
<h2>{{t "consent-template-5.gdpr-header"}}</h2>
Expand Down
2 changes: 1 addition & 1 deletion packages/record/templates/playback-feed.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
playsinline
muted
src="{{{src}}}"
id="{{playback_element_id}}"
id="{{webcam_element_id}}"
width="{{width}}"
height="{{height}}"
controls
Expand Down
10 changes: 10 additions & 0 deletions packages/record/templates/record-feed.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<video
autoplay
playsinline
muted
id="{{webcam_element_id}}"
width="{{width}}"
height="{{height}}"
style="display: inline-block"
></video>
<img id="record-icon" src="{{{record_icon}}}" />

0 comments on commit f4c8ab5

Please sign in to comment.