@bbc/react-transcript-editor
Version:
A React component to make transcribing audio and video easier and faster.
304 lines (266 loc) • 11.8 kB
JavaScript
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
import React from 'react';
import styles from './index.module.css';
import returnHotKeys from './defaultHotKeys.js'; // https://www.npmjs.com/package/react-keyboard-shortcuts
import { hotkeys } from 'react-keyboard-shortcuts';
import { secondsToTimecode, timecodeToSeconds } from '../../Util/timecode-converter/index.js'; // inspired by https://github.com/bbc/nm2/blob/master/src/components/chapter/video/Video.jsx
class MediaPlayer extends React.Component {
constructor(props) {
super(props);
_defineProperty(this, "hot_keys", returnHotKeys(this));
_defineProperty(this, "setCurrentTime", newCurrentTime => {
if (newCurrentTime !== '' && newCurrentTime !== null) {
// hh:mm:ss:ff - mm:ss - m:ss - ss - seconds number or string and hh:mm:ss
const newCurrentTimeInSeconds = timecodeToSeconds(newCurrentTime);
console.log('setCurrentTime', newCurrentTimeInSeconds, newCurrentTime);
if (this.videoRef.current !== null) {
const videoRef = this.videoRef.current; // videoRef.load();
if (videoRef.readyState === 4) {
// it's loaded
videoRef.currentTime = newCurrentTimeInSeconds;
videoRef.play();
}
}
}
});
_defineProperty(this, "promptSetCurrentTime", () => {
this.setCurrentTime(prompt('Jump to time - hh:mm:ss:ff hh:mm:ss mm:ss m:ss m.ss seconds'));
});
_defineProperty(this, "setTimeCodeOffset", newTimeCodeOffSet => {
if (newTimeCodeOffSet !== '' && newTimeCodeOffSet !== null) {
// use similar helper function from above to convert
let newCurrentTimeInSeconds = newTimeCodeOffSet;
if (newTimeCodeOffSet.includes(':')) {
newCurrentTimeInSeconds = timecodeToSeconds(newTimeCodeOffSet);
this.setState({
timecodeOffset: newCurrentTimeInSeconds
});
}
}
});
_defineProperty(this, "rollBack", () => {
if (this.videoRef.current !== null) {
// get video duration
const videoElem = this.videoRef.current;
const tmpDesiredCurrentTime = videoElem.currentTime - this.state.rollBackValueInSeconds; // > 0 < duration of video
this.setCurrentTime(tmpDesiredCurrentTime);
}
});
_defineProperty(this, "handleTimeUpdate", e => {
// eslint-disable-next-line react/prop-types
this.props.hookOnTimeUpdate(e.target.currentTime);
});
_defineProperty(this, "handlePlayBackRateChange", e => {
this.setPlayBackRate(e.target.value);
});
_defineProperty(this, "increasePlaybackRate", () => {
// if(this.videoRef.current!== null){
// this.playbackRateInputRange.current.stepUp(1)
// }
const currentPlaybackRate = this.getCurrentPlaybackRate();
let newPlaybackRate = currentPlaybackRate + 0.1; // rounding up eg 0.8-0.1 = 0.7000000000000001 => 0.7
newPlaybackRate = Number(newPlaybackRate.toFixed(1));
this.setPlayBackRate(newPlaybackRate);
});
_defineProperty(this, "decreasePlaybackRate", () => {
// if(this.videoRef.current!== null){
// this.playbackRateInputRange.current.stepDown(1)
// }
const currentPlaybackRate = this.getCurrentPlaybackRate();
let newPlaybackRate = currentPlaybackRate - 0.1; // rounding up eg 0.8-0.1 = 0.7000000000000001 => 0.7
newPlaybackRate = Number(newPlaybackRate.toFixed(1));
this.setPlayBackRate(newPlaybackRate);
});
_defineProperty(this, "getCurrentPlaybackRate", () => {
if (this.videoRef.current !== null) {
return this.videoRef.current.playbackRate;
}
});
_defineProperty(this, "setPlayBackRate", speedValue => {
// value between 0.2 and 3.5
if (this.videoRef.current !== null) {
if (speedValue >= 0.2 && speedValue <= 3.5) {
this.setState({
playBackRate: speedValue
}, () => {
this.videoRef.current.playbackRate = speedValue;
});
}
}
});
_defineProperty(this, "handleChangeReplayRollbackValue", e => {
if (this.videoRef.current !== null) {
this.setState({
rollBackValueInSeconds: e.target.value
});
}
});
_defineProperty(this, "handleMuteVolume", e => {
// https://www.w3schools.com/tags/av_prop_volume.asp
if (this.videoRef.current !== null) {
if (this.videoRef.current.volume > 0) {
this.videoRef.current.volume = 0;
} else {
this.videoRef.current.volume = 1;
}
}
});
_defineProperty(this, "isPlaying", e => {
if (this.videoRef.current !== null) {
if (this.videoRef.current.paused) {
return false;
} else {
return true;
}
}
});
_defineProperty(this, "playMedia", () => {
if (this.videoRef.current !== null) {
if (this.videoRef.current.paused) {
this.videoRef.current.play();
} else {
this.videoRef.current.pause();
}
}
});
_defineProperty(this, "skipForward", () => {
if (this.videoRef.current !== null) {
const currentTime = this.videoRef.current.currentTime;
let newCurrentTime = currentTime + 5;
newCurrentTime = Number(newCurrentTime.toFixed(1));
this.setCurrentTime(newCurrentTime);
}
});
_defineProperty(this, "skipBackward", () => {
if (this.videoRef.current !== null) {
const currentTime = this.videoRef.current.currentTime;
let newCurrentTime = currentTime - 5;
newCurrentTime = Number(newCurrentTime.toFixed(1));
this.setCurrentTime(newCurrentTime);
}
});
_defineProperty(this, "handleProgressBarClick", e => {
if (this.videoRef.current !== null) {
// length of the bar
const lengthOfBar = e.target.offsetWidth; // distance of the position of the lick from the start of the progress bar element
// location of click - start point of the bar
const clickLength = e.clientX - e.target.offsetLeft;
const positionPercentage = clickLength / lengthOfBar; // total time
const totalTime = e.target.max;
const resultInSeconds = totalTime * positionPercentage; // rounding up
const roundNewCurrentTime = parseFloat(resultInSeconds.toFixed(2));
this.setCurrentTime(roundNewCurrentTime);
}
});
this.videoRef = React.createRef();
this.playbackRateInputRange = React.createRef();
this.state = {
playBackRate: 1,
rollBackValueInSeconds: 15,
timecodeOffset: 0,
hotKeys: returnHotKeys(this)
};
}
componentDidMount() {
this.props.hookSeek(this.setCurrentTime);
}
render() {
// conditional, if media player not defined then don't show
let mediaPlayerEl;
if (this.props.mediaUrl !== null) {
mediaPlayerEl = React.createElement("video", {
id: "video",
playsInline: true // autoPlay
// controls
,
src: this.props.mediaUrl,
onTimeUpdate: this.handleTimeUpdate // TODO: video type
,
type: "video/mp4",
"data-testid": "media-player-id",
onClick: this.playMedia,
ref: this.videoRef
});
}
const playerControlsSection = React.createElement("section", null, React.createElement("progress", {
className: styles.progressBar,
max: this.videoRef.current !== null ? parseInt(this.videoRef.current.duration) : '100',
value: this.videoRef.current !== null ? parseInt(this.videoRef.current.currentTime) : '0',
onClick: e => {
this.handleProgressBarClick(e);
}
}), React.createElement("br", null), this.props.mediaUrl !== null ? React.createElement("button", {
onClick: () => {
this.playMedia();
}
}, " ", this.isPlaying() ? '❚❚' : '▶', " ") : '', this.props.mediaUrl !== null ? React.createElement("button", {
onClick: () => {
this.skipBackward();
}
}, " ", '◀◀', " ") : '', this.props.mediaUrl !== null ? React.createElement("button", {
onClick: () => {
this.skipForward();
}
}, " ", '▶▶', " ") : '', "\uFE0F", React.createElement("br", null), React.createElement("code", null, this.videoRef.current !== null ? secondsToTimecode(this.videoRef.current.currentTime + this.state.timecodeOffset) : '00:00:00:00'), "/", React.createElement("code", null, this.videoRef.current !== null ? secondsToTimecode(this.videoRef.current.duration + this.state.timecodeOffset) : '00:00:00:00'), React.createElement("br", null), React.createElement("button", {
type: "button",
onClick: this.promptSetCurrentTime
}, "Jump To Time \u23F1"), " ", React.createElement("br", null), React.createElement("button", {
type: "button",
onClick: () => {
this.setTimeCodeOffset(prompt('Add a timecode offset hh:mm:ss:ff'));
}
}, "Set Timecode Offset \u23F1"), React.createElement("output", null, React.createElement("code", null, secondsToTimecode(this.state.timecodeOffset))), React.createElement("br", null), React.createElement("p", {
className: styles.helpText
}, "Volume"), React.createElement("label", {
className: styles.switch
}, React.createElement("input", {
type: "checkbox",
defaultChecked: "true",
onChange: this.handleMuteVolume
}), React.createElement("span", {
className: styles.slider
})), React.createElement("p", {
className: styles.helpText
}, "Playback Rate", React.createElement("b", null, " ", React.createElement("output", null, `x${this.state.playBackRate}`), " ")), React.createElement("input", {
type: "range",
min: "0.2",
value: this.state.playBackRate,
max: "3.5",
step: "0.1" // list="tickmarks"
,
onChange: this.handlePlayBackRateChange,
ref: this.playbackRateInputRange
}), React.createElement("br", null), React.createElement("button", {
type: "button",
onClick: () => {
this.setPlayBackRate(1);
}
}, "Reset"), React.createElement("p", {
className: styles.helpText
}, "Rollback", React.createElement("b", null, " ", React.createElement("output", null, `x${this.state.rollBackValueInSeconds}`)), " Seconds"), React.createElement("input", {
type: "range",
min: "1",
max: "60",
step: "1",
value: this.state.rollBackValueInSeconds,
onChange: this.handleChangeReplayRollbackValue
}), React.createElement("br", null), React.createElement("button", {
type: "button",
onClick: () => {
this.rollBack();
}
}, "\u21BA"));
const keyboardShortcutsHelp = Object.keys(this.state.hotKeys).map((shortcutKey, index) => {
return React.createElement("p", {
className: styles.helpText,
key: shortcutKey
}, React.createElement("code", null, shortcutKey), " ", React.createElement("small", null, React.createElement("b", null, this.state.hotKeys[shortcutKey].helperText)));
});
return React.createElement("section", {
className: styles.videoSection
}, mediaPlayerEl, this.props.mediaUrl !== null ? playerControlsSection : '', React.createElement("section", {
className: styles.hideInMobile
}, React.createElement("label", null, this.props.mediaUrl !== null ? 'Keyboard Shortcuts' : ''), this.props.mediaUrl !== null ? keyboardShortcutsHelp : ''));
}
} // export default mouseTrap(MediaPlayer);
export default hotkeys(MediaPlayer); // export default MediaPlayer;