aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/RecordingBox
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/RecordingBox')
-rw-r--r--src/client/views/nodes/RecordingBox/ProgressBar.scss26
-rw-r--r--src/client/views/nodes/RecordingBox/ProgressBar.tsx45
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingBox.tsx61
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingView.scss207
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingView.tsx282
-rw-r--r--src/client/views/nodes/RecordingBox/index.ts2
6 files changed, 0 insertions, 623 deletions
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss
deleted file mode 100644
index a493b0b89..000000000
--- a/src/client/views/nodes/RecordingBox/ProgressBar.scss
+++ /dev/null
@@ -1,26 +0,0 @@
-
-.progressbar {
- position: absolute;
- display: flex;
- justify-content: flex-start;
- bottom: 10px;
- width: 80%;
- height: 5px;
- background-color: gray;
-
- &.done {
- top: 0;
- width: 0px;
- height: 5px;
- background-color: red;
- z-index: 2;
- }
-
- &.mark {
- top: 0;
- background-color: transparent;
- border-right: 2px solid white;
- z-index: 3;
- pointer-events: none;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
deleted file mode 100644
index 82d5e1f04..000000000
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import * as React from 'react';
-import { useEffect } from "react"
-import "./ProgressBar.scss"
-
-interface ProgressBarProps {
- progress: number,
- marks: number[],
-}
-
-export function ProgressBar(props: ProgressBarProps) {
-
- // const handleClick = (e: React.MouseEvent) => {
- // let progressbar = document.getElementById('progressbar')!
- // let bounds = progressbar!.getBoundingClientRect();
- // let x = e.clientX - bounds.left;
- // let percent = x / progressbar.clientWidth * 100
-
- // for (let i = 0; i < props.marks.length; i++) {
- // let start = i == 0 ? 0 : props.marks[i-1];
- // if (percent > start && percent < props.marks[i]) {
- // props.playSegment(i)
- // // console.log(i)
- // // console.log(percent)
- // // console.log(props.marks[i])
- // break
- // }
- // }
- // }
-
- return (
- <div className="progressbar" id="progressbar">
- <div
- className="progressbar done"
- style={{ width: `${props.progress}%` }}
- // onClick={handleClick}
- ></div>
- {props.marks.map((mark) => {
- return <div
- className="progressbar mark"
- style={{ width: `${mark}%` }}
- ></div>
- })}
- </div>
- )
-} \ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
deleted file mode 100644
index 10393624b..000000000
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ /dev/null
@@ -1,61 +0,0 @@
-import { action, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { VideoField } from "../../../../fields/URLField";
-import { Upload } from "../../../../server/SharedMediaTypes";
-import { ViewBoxBaseComponent } from "../../DocComponent";
-import { FieldView } from "../FieldView";
-import { VideoBox } from "../VideoBox";
-import { RecordingView } from './RecordingView';
-import { DocumentType } from "../../../documents/DocumentTypes";
-import { RecordingApi } from "../../../util/RecordingApi";
-import { Doc, FieldsSym } from "../../../../fields/Doc";
-import { Id } from "../../../../fields/FieldSymbols";
-
-
-@observer
-export class RecordingBox extends ViewBoxBaseComponent() {
-
- public static LayoutString(fieldKey: string) { return FieldView.LayoutString(RecordingBox, fieldKey); }
-
- private _ref: React.RefObject<HTMLDivElement> = React.createRef();
-
- constructor(props: any) {
- super(props);
- }
-
- componentDidMount() {
- console.log("set native width and height")
- Doc.SetNativeWidth(this.dataDoc, 1280);
- Doc.SetNativeHeight(this.dataDoc, 720);
- }
-
- @observable result: Upload.FileInformation | undefined = undefined
- @observable videoDuration: number | undefined = undefined
-
- @action
- setVideoDuration = (duration: number) => {
- this.videoDuration = duration
- }
-
- @action
- setResult = (info: Upload.FileInformation, trackScreen: boolean) => {
- this.result = info
- this.dataDoc.type = DocumentType.VID;
- this.dataDoc[this.fieldKey + "-duration"] = this.videoDuration;
-
- this.dataDoc.layout = VideoBox.LayoutString(this.fieldKey);
- this.dataDoc[this.props.fieldKey] = new VideoField(this.result.accessPaths.agnostic.client);
- this.dataDoc[this.fieldKey + "-recorded"] = true;
- // stringify the presenation and store it
- if (trackScreen) {
- this.dataDoc[this.fieldKey + "-presentation"] = JSON.stringify(RecordingApi.Instance.clear());
- }
- }
-
- render() {
- return <div className="recordingBox" ref={this._ref}>
- {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={Doc.GetProto(this.rootDoc)[Id]} />}
- </div>;
- }
-}
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.scss b/src/client/views/nodes/RecordingBox/RecordingView.scss
deleted file mode 100644
index 9b2f6d070..000000000
--- a/src/client/views/nodes/RecordingBox/RecordingView.scss
+++ /dev/null
@@ -1,207 +0,0 @@
-video {
- // flex: 100%;
- width: 100%;
- // min-height: 400px;
- //height: auto;
- height: 100%;
- //display: block;
- object-fit: cover;
- background-color: black;
-}
-
-button {
- margin: 0 .5rem
-}
-
-.recording-container {
- height: 100%;
- width: 100%;
- // display: flex;
- pointer-events: all;
- background-color: grey;
-}
-
-.video-wrapper {
- // max-width: 600px;
- // max-width: 700px;
- position: relative;
- display: flex;
- justify-content: center;
- // overflow: hidden;
- border-radius: 10px;
- margin: 0;
-}
-
-.video-wrapper:hover .controls {
- bottom: 30px;
- transform: translateY(0%);
- opacity: 100%;
-}
-
-.controls {
- display: flex;
- align-items: center;
- justify-content: space-evenly;
- position: absolute;
- padding: 14px;
- width: 100%;
- max-width: 500px;
- // max-height: 20%;
- flex-wrap: wrap;
- background: rgba(255, 255, 255, 0.25);
- box-shadow: 0 8px 32px 0 rgba(255, 255, 255, 0.1);
- backdrop-filter: blur(4px);
- border-radius: 10px;
- border: 1px solid rgba(255, 255, 255, 0.18);
- // transform: translateY(150%);
- transition: all 0.3s ease-in-out;
- // opacity: 0%;
- bottom: 30px;
- // bottom: -150px;
-}
-
-.actions button {
- background: none;
- border: none;
- outline: none;
- cursor: pointer;
-}
-
-.actions button i {
- background-color: none;
- color: white;
- font-size: 30px;
-}
-
-
-.velocity {
- appearance: none;
- background: none;
- color: white;
- outline: none;
- border: none;
- text-align: center;
- font-size: 16px;
-}
-
-.mute-btn {
- background: none;
- border: none;
- outline: none;
- cursor: pointer;
-}
-
-.mute-btn i {
- background-color: none;
- color: white;
- font-size: 20px;
-}
-
-.recording-sign {
- height: 20px;
- width: auto;
- display: flex;
- flex-direction: row;
- position: absolute;
- top: 10px;
- right: 15px;
- align-items: center;
- justify-content: center;
-
- .timer {
- font-size: 15px;
- color: white;
- margin: 0;
- }
-
- .dot {
- height: 15px;
- width: 15px;
- margin: 5px;
- background-color: red;
- border-radius: 50%;
- display: inline-block;
- }
-}
-
-.controls-inner-container {
- display: flex;
- flex-direction: row;
- justify-content: center;
- width: 100%;
-
-}
-
-.record-button-wrapper {
- width: 35px;
- height: 35px;
- font-size: 0;
- background-color: grey;
- border: 0px;
- border-radius: 35px;
- margin: 10px;
- display: flex;
- justify-content: center;
-
- .record-button {
- background-color: red;
- border: 0px;
- border-radius: 50%;
- height: 80%;
- width: 80%;
- align-self: center;
- margin: 0;
-
- &:hover {
- height: 85%;
- width: 85%;
- }
- }
-
- .stop-button {
- background-color: red;
- border: 0px;
- border-radius: 10%;
- height: 70%;
- width: 70%;
- align-self: center;
- margin: 0;
-
-
- // &:hover {
- // width: 40px;
- // height: 40px
- // }
- }
-
-}
-
-.options-wrapper {
- height: 100%;
- display: flex;
- flex-direction: row;
- align-items: center;
- position: absolute;
- top: 0;
- bottom: 0;
-
- &.video-edit-wrapper {
-
- right: 50% - 15;
-
- .track-screen {
- font-weight: 200;
- }
-
- }
-
- &.track-screen-wrapper {
-
- right: 50% - 30;
-
- .track-screen {
- font-weight: 200;
- }
-
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
deleted file mode 100644
index b95335792..000000000
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ /dev/null
@@ -1,282 +0,0 @@
-import * as React from 'react';
-import "./RecordingView.scss";
-import { ReactElement, useCallback, useEffect, useRef, useState } from "react";
-import { ProgressBar } from "./ProgressBar"
-import { MdBackspace } from 'react-icons/md';
-import { FaCheckCircle } from 'react-icons/fa';
-import { IconContext } from "react-icons";
-import { Networking } from '../../../Network';
-import { Upload } from '../../../../server/SharedMediaTypes';
-
-import { RecordingApi } from '../../../util/RecordingApi';
-import { emptyFunction, returnFalse, returnTrue, setupMoveUpEvents } from '../../../../Utils';
-
-interface MediaSegment {
- videoChunks: any[],
- endTime: number
-}
-
-interface IRecordingViewProps {
- setResult: (info: Upload.FileInformation, 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 [progress, setProgress] = useState(0);
-
- const [videos, setVideos] = useState<MediaSegment[]>([]);
- 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 DEFAULT_MEDIA_CONSTRAINTS = {
- video: {
- width: 1280,
- height: 720,
- },
- audio: {
- echoCancellation: true,
- noiseSuppression: true,
- sampleRate: 44100
- }
- }
-
- useEffect(() => {
-
- if (finished) {
- props.setDuration(recordingTimer * 100)
- let allVideoChunks: any = []
- videos.forEach((vid) => {
- console.log(vid.videoChunks)
- allVideoChunks = allVideoChunks.concat(vid.videoChunks)
- })
-
- const videoFile = new File(allVideoChunks, "video.mkv", { type: allVideoChunks[0].type, lastModified: Date.now() });
-
- Networking.UploadFilesToServer(videoFile)
- .then((data) => {
- const result = data[0].result
- if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
- props.setResult(result, trackScreen)
- } else {
- alert("video conversion failed");
- }
- })
-
- }
-
-
- }, [finished])
-
- 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)
- }
- }
-
- 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 }])
- }
-
- // reset the temporary chunks
- videoChunks = []
- setRecording(false);
- setFinished(true);
- 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 }])
-
- // 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 = () => {
- if (videoRecorder.current) {
- if (videoRecorder.current.state !== "inactive") {
- videoRecorder.current.stop();
- // recorder.current.stream.getTracks().forEach((track: any) => track.stop())
- }
- }
- }
-
- const pause = () => {
- if (videoRecorder.current) {
- if (videoRecorder.current.state === "recording") {
- videoRecorder.current.pause();
- }
- }
- }
-
- const startOrResume = (e: React.PointerEvent) => {
- // the code to start or resume does not get triggered if we start dragging the button
- setupMoveUpEvents({}, e, returnTrue, returnFalse, e => {
- if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
- record();
- } else if (videoRecorder.current.state === "paused") {
- videoRecorder.current.resume();
- }
- return true; // cancels propagation to documentView to avoid selecting it.
- }, false, false);
- }
-
- const clearPrevious = () => {
- 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);
- }
-
- 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>
- <div className="controls">
-
- <div className="controls-inner-container">
- <div className="record-button-wrapper">
- {recording ?
- <button className="stop-button" onPointerDown={pause} /> :
- <button className="record-button" onPointerDown={startOrResume} />
- }
- </div>
-
- {!recording && (videos.length > 0 ?
-
- <div className="options-wrapper video-edit-wrapper">
- {/* <IconContext.Provider value={{ color: "grey", className: "video-edit-buttons" }}>
- <MdBackspace onClick={clearPrevious} />
- </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>
-
- <ProgressBar
- progress={progress}
- marks={videos.map((elt) => elt.endTime / MAXTIME * 100)}
- // playSegment={playSegment}
- />
- </div>
- </div>
- </div>)
-} \ No newline at end of file
diff --git a/src/client/views/nodes/RecordingBox/index.ts b/src/client/views/nodes/RecordingBox/index.ts
deleted file mode 100644
index ff21eaed6..000000000
--- a/src/client/views/nodes/RecordingBox/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export * from './RecordingView'
-export * from './RecordingBox' \ No newline at end of file