aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/RecordingBox/RecordingView.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/RecordingBox/RecordingView.tsx')
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingView.tsx443
1 files changed, 215 insertions, 228 deletions
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index 701277cbd..3f54a97ce 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -13,298 +13,285 @@ import { DashUploadUtils } from '../../../../server/DashUploadUtils';
export interface MediaSegment {
videoChunks: any[],
- endTime: number,
- startTime: number
+ endTime: number,
+ startTime: number
}
interface IRecordingViewProps {
- setResult: (info: Upload.AccessPathInfo, trackScreen: boolean) => void
- setDuration: (seconds: number) => void
- id: string
+ setResult: (info: Upload.AccessPathInfo, trackScreen: boolean) => void
+ setDuration: (seconds: number) => void
+ id: string
}
const MAXTIME = 100000;
export function RecordingView(props: IRecordingViewProps) {
- const [recording, setRecording] = useState(false);
- const recordingTimerRef = useRef<number>(0);
- const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second
- const [playing, setPlaying] = useState(false);
+ const [recording, setRecording] = useState(false);
+ const recordingTimerRef = useRef<number>(0);
+ const [recordingTimer, setRecordingTimer] = useState(0); // unit is 0.01 second
+ const [playing, setPlaying] = useState(false);
const [progress, setProgress] = useState(0);
-
+
+ // acts as a "refresh state" to tell progressBar when to undo
const [doUndo, setDoUndo] = useState(false);
const [videos, setVideos] = useState<MediaSegment[]>([]);
const [orderVideos, setOrderVideos] = useState<boolean>(false);
- const videoRecorder = useRef<MediaRecorder | null>(null);
- const videoElementRef = useRef<HTMLVideoElement | null>(null);
+ const videoRecorder = useRef<MediaRecorder | null>(null);
+ const videoElementRef = useRef<HTMLVideoElement | null>(null);
- const [finished, setFinished] = useState<boolean>(false)
- const [trackScreen, setTrackScreen] = useState<boolean>(true)
+ const [finished, setFinished] = useState<boolean>(false)
+ const [trackScreen, setTrackScreen] = useState<boolean>(true)
- const DEFAULT_MEDIA_CONSTRAINTS = {
- video: {
- width: 1280,
- height: 720,
- },
- audio: {
- echoCancellation: true,
- noiseSuppression: true,
- sampleRate: 44100
- }
- }
+ const DEFAULT_MEDIA_CONSTRAINTS = {
+ video: {
+ width: 1280,
+ height: 720,
+ },
+ audio: {
+ echoCancellation: true,
+ noiseSuppression: true,
+ sampleRate: 44100
+ }
+ }
useEffect(() => {
-
- // console.log('in videos useEffect', finished)
-
- if (finished) {
- (async () => {
+ if (finished) {
+ (async () => {
const inputPaths: string[] = [];
const videoFiles: File[] = []
videos.forEach(async (vid, i) => {
const videoFile = new File(vid.videoChunks, `segvideo${i}.mkv`, { type: vid.videoChunks[0].type, lastModified: Date.now() });
videoFiles.push(videoFile);
-
+
const { name } = videoFile;
inputPaths.push(name)
- })
-
+ })
+
+ // upload the segments to the server and get their server access paths
const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles))
- .map(res => (res.result instanceof Error) ? '' : res.result.accessPaths.agnostic.server)
-
+ .map(res => (res.result instanceof Error) ? '' : res.result.accessPaths.agnostic.server)
+
+ // concat the segments together using post call
const result: Upload.AccessPathInfo | Error = await Networking.PostToServer('/concatVideos', serverPaths)
- if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
+ if (!(result instanceof Error)) {
props.setResult(result, trackScreen)
} else {
alert("video conversion failed");
}
-
- })();
-
- }
+ })();
+ }
+ }, [videos])
+ // this will call upon the progress bar to edit videos to be in the correct order
+ useEffect(() => {
+ if (finished) setOrderVideos(true);
+ }, [finished])
- }, [videos])
-
- useEffect(() => {
+ useEffect(() => {
+ // check if the browser supports media devices on first load
+ if (!navigator.mediaDevices) {
+ console.log('This browser does not support getUserMedia.')
+ }
+ // console.log('This device has the correct media devices.')
+ }, [])
- if (finished) {
- setOrderVideos(true);
- }
+ useEffect(() => {
+ // get access to the video element on every render
+ videoElementRef.current = document.getElementById(`video-${props.id}`) as HTMLVideoElement;
+ });
+ useEffect(() => {
+ let interval: any = null;
+ if (recording) {
+ interval = setInterval(() => {
+ setRecordingTimer(unit => unit + 1);
+ }, 10);
+ } else if (!recording && recordingTimer !== 0) {
+ clearInterval(interval);
+ }
+ return () => clearInterval(interval);
+ }, [recording])
- }, [finished])
+ useEffect(() => {
+ setVideoProgressHelper(recordingTimer)
+ recordingTimerRef.current = recordingTimer;
+ }, [recordingTimer])
- useEffect(() => {
- // check if the browser supports media devices on first load
- if (!navigator.mediaDevices) {
- console.log('This browser does not support getUserMedia.')
- }
- // console.log('This device has the correct media devices.')
- }, [])
-
- useEffect(() => {
- // get access to the video element on every render
- videoElementRef.current = document.getElementById(`video-${props.id}`) as HTMLVideoElement;
- })
-
- useEffect(() => {
- let interval: any = null;
- if (recording) {
- interval = setInterval(() => {
- setRecordingTimer(unit => unit + 1);
- }, 10);
- } else if (!recording && recordingTimer !== 0) {
- clearInterval(interval);
- }
- return () => clearInterval(interval);
- }, [recording])
-
- useEffect(() => {
- setVideoProgressHelper(recordingTimer)
- recordingTimerRef.current = recordingTimer;
- }, [recordingTimer])
-
- const setVideoProgressHelper = (progress: number) => {
- const newProgress = (progress / MAXTIME) * 100;
- setProgress(newProgress)
- }
- const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => {
- const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints)
-
- videoElementRef.current!.src = ""
- videoElementRef.current!.srcObject = stream
- videoElementRef.current!.muted = true
-
- return stream
- }
-
- const record = async () => {
- const stream = await startShowingStream();
- videoRecorder.current = new MediaRecorder(stream)
-
- // temporary chunks of video
- let videoChunks: any = []
-
- videoRecorder.current.ondataavailable = (event: any) => {
- if (event.data.size > 0) {
- videoChunks.push(event.data)
- }
- }
+ const setVideoProgressHelper = (progress: number) => {
+ const newProgress = (progress / MAXTIME) * 100;
+ setProgress(newProgress)
+ }
+ const startShowingStream = async (mediaConstraints = DEFAULT_MEDIA_CONSTRAINTS) => {
+ const stream = await navigator.mediaDevices.getUserMedia(mediaConstraints)
- videoRecorder.current.onstart = (event: any) => {
- setRecording(true);
- trackScreen && RecordingApi.Instance.start();
- }
+ videoElementRef.current!.src = ""
+ videoElementRef.current!.srcObject = stream
+ videoElementRef.current!.muted = true
- videoRecorder.current.onstop = () => {
- // if we have a last portion
- if (videoChunks.length > 1) {
- // append the current portion to the video pieces
- setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
- }
-
- // reset the temporary chunks
- videoChunks = []
- setRecording(false);
- trackScreen && RecordingApi.Instance.pause();
- }
+ return stream
+ }
- // recording paused
- videoRecorder.current.onpause = (event: any) => {
- // append the current portion to the video pieces
- setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
+ const record = async () => {
+ const stream = await startShowingStream();
+ videoRecorder.current = new MediaRecorder(stream)
- // reset the temporary chunks
- videoChunks = []
- setRecording(false);
- trackScreen && RecordingApi.Instance.pause();
- }
+ // temporary chunks of video
+ let videoChunks: any = []
- videoRecorder.current.onresume = async (event: any) => {
- await startShowingStream();
- setRecording(true);
- trackScreen && RecordingApi.Instance.resume();
+ videoRecorder.current.ondataavailable = (event: any) => {
+ if (event.data.size > 0) {
+ videoChunks.push(event.data)
+ }
+ }
+
+ videoRecorder.current.onstart = (event: any) => {
+ setRecording(true);
+ trackScreen && RecordingApi.Instance.start();
+ }
+
+ videoRecorder.current.onstop = () => {
+ // if we have a last portion
+ if (videoChunks.length > 1) {
+ // append the current portion to the video pieces
+ setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
}
- videoRecorder.current.start(200)
- }
+ // reset the temporary chunks
+ videoChunks = []
+ setRecording(false);
+ trackScreen && RecordingApi.Instance.pause();
+ }
+
+ // recording paused
+ videoRecorder.current.onpause = (event: any) => {
+ // append the current portion to the video pieces
+ setVideos(videos => [...videos, { videoChunks: videoChunks, endTime: recordingTimerRef.current, startTime: videos?.lastElement()?.endTime || 0 }])
+
+ // reset the temporary chunks
+ videoChunks = []
+ setRecording(false);
+ trackScreen && RecordingApi.Instance.pause();
+ }
+
+ videoRecorder.current.onresume = async (event: any) => {
+ await startShowingStream();
+ setRecording(true);
+ trackScreen && RecordingApi.Instance.resume();
+ }
+
+ videoRecorder.current.start(200)
+ }
- const stop = (e: React.MouseEvent) => {
+ const stop = (e: React.MouseEvent) => {
e.stopPropagation()
if (videoRecorder.current) {
- setFinished(true);
- if (videoRecorder.current.state !== "inactive") {
- videoRecorder.current.stop();
- // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
- }
+ setFinished(true);
+ if (videoRecorder.current.state !== "inactive") {
+ videoRecorder.current.stop();
+ // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
}
- }
+ }
+ }
- const pause = (e: React.MouseEvent) => {
+ const pause = (e: React.MouseEvent) => {
e.stopPropagation()
- if (videoRecorder.current) {
- if (videoRecorder.current.state === "recording") {
- videoRecorder.current.stop();
- }
+ if (videoRecorder.current) {
+ if (videoRecorder.current.state === "recording") {
+ videoRecorder.current.stop();
}
- }
+ }
+ }
- const startOrResume = (e: React.MouseEvent) => {
+ const startOrResume = (e: React.MouseEvent) => {
e.stopPropagation()
- if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
- record();
- } else if (videoRecorder.current.state === "paused") {
- videoRecorder.current.start();
- }
- }
+ if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
+ record();
+ } else if (videoRecorder.current.state === "paused") {
+ videoRecorder.current.start();
+ }
+ }
const undoPrevious = (e: React.MouseEvent) => {
e.stopPropagation();
setDoUndo(prev => !prev);
- // const numVideos = videos.length
- // setRecordingTimer(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
- // setVideoProgressHelper(numVideos == 1 ? 0 : videos[numVideos - 2].endTime)
- // setVideos(videos.filter((_, idx) => idx !== numVideos - 1));
-
- }
-
- const handleOnTimeUpdate = () => {
- if (playing) {
- setVideoProgressHelper(videoElementRef.current!.currentTime)
- }
- };
+ }
- const millisecondToMinuteSecond = (milliseconds: number) => {
- const toTwoDigit = (digit: number) => {
- return String(digit).length == 1 ? "0" + digit : digit
- }
- const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
- const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
- return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
- }
+ const handleOnTimeUpdate = () => {
+ if (playing) {
+ setVideoProgressHelper(videoElementRef.current!.currentTime)
+ }
+ };
+
+ const millisecondToMinuteSecond = (milliseconds: number) => {
+ const toTwoDigit = (digit: number) => {
+ return String(digit).length == 1 ? "0" + digit : digit
+ }
+ const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
+ const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
+ return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
+ }
// TODO: have the undo button only appear if there is something to undo
return (
- <div className="recording-container">
- <div className="video-wrapper">
- <video id={`video-${props.id}`}
- autoPlay
- muted
- onTimeUpdate={handleOnTimeUpdate}
- />
- <div className="recording-sign">
- <span className="dot" />
- <p className="timer">{millisecondToMinuteSecond(recordingTimer * 10)}</p>
+ <div className="recording-container">
+ <div className="video-wrapper">
+ <video id={`video-${props.id}`}
+ autoPlay
+ muted
+ onTimeUpdate={handleOnTimeUpdate}
+ />
+ <div className="recording-sign">
+ <span className="dot" />
+ <p className="timer">{millisecondToMinuteSecond(recordingTimer * 10)}</p>
+ </div>
+ <div className="controls">
+
+ <div className="controls-inner-container">
+ <div className="record-button-wrapper">
+ {recording ?
+ <button className="stop-button" onClick={pause} /> :
+ <button className="record-button" onClick={startOrResume} />
+ }
</div>
- <div className="controls">
-
- <div className="controls-inner-container">
- <div className="record-button-wrapper">
- {recording ?
- <button className="stop-button" onClick={pause} /> :
- <button className="record-button" onClick={startOrResume} />
- }
- </div>
-
- {!recording && (videos.length > 0 ?
-
- <div className="options-wrapper video-edit-wrapper">
- <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
- <MdBackspace onClick={undoPrevious} />
- </IconContext.Provider>
- <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
- <FaCheckCircle onClick={stop} />
- </IconContext.Provider>
- </div>
-
- : <div className="options-wrapper track-screen-wrapper">
- <label className="track-screen">
- <input type="checkbox" checked={trackScreen} onChange={(e) => { setTrackScreen(e.target.checked) }} />
- <span className="checkmark"></span>
- Track Screen
- </label>
- </div>)}
-
- </div>
-
- </div>
-
- <ProgressBar
- videos={videos}
- setVideos={setVideos}
- orderVideos={orderVideos}
- progress={progress}
- recording={recording}
- doUndo={doUndo}
- // playSegment={playSegment}
- />
- </div>
- </div>)
+
+ {!recording && (videos.length > 0 ?
+
+ <div className="options-wrapper video-edit-wrapper">
+ <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
+ <MdBackspace onClick={undoPrevious} />
+ </IconContext.Provider>
+ <IconContext.Provider value={{ color: "#cc1c08", className: "video-edit-buttons" }}>
+ <FaCheckCircle onClick={stop} />
+ </IconContext.Provider>
+ </div>
+
+ : <div className="options-wrapper track-screen-wrapper">
+ <label className="track-screen">
+ <input type="checkbox" checked={trackScreen} onChange={(e) => { setTrackScreen(e.target.checked) }} />
+ <span className="checkmark"></span>
+ Track Screen
+ </label>
+ </div>)}
+
+ </div>
+
+ </div>
+
+ <ProgressBar
+ videos={videos}
+ setVideos={setVideos}
+ orderVideos={orderVideos}
+ progress={progress}
+ recording={recording}
+ doUndo={doUndo}
+ // playSegment={playSegment}
+ />
+ </div>
+ </div>)
} \ No newline at end of file