aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/Network.ts15
-rw-r--r--src/client/views/nodes/RecordingBox/ProgressBar.tsx20
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingBox.tsx7
-rw-r--r--src/client/views/nodes/RecordingBox/RecordingView.tsx126
-rw-r--r--src/server/ApiManagers/UploadManager.ts23
-rw-r--r--src/server/DashUploadUtils.ts59
6 files changed, 60 insertions, 190 deletions
diff --git a/src/client/Network.ts b/src/client/Network.ts
index 8c1f31488..b26f2458d 100644
--- a/src/client/Network.ts
+++ b/src/client/Network.ts
@@ -35,20 +35,7 @@ export namespace Networking {
const response = await fetch("/uploadFormData", parameters);
return response.json();
}
-
- export async function UploadSegmentsAndConcatenate<T extends Upload.FileInformation = Upload.FileInformation>(files: File[]): Promise<Upload.FileResponse<T>[]> {
- console.log("network.ts : uploading segments and concatenating", files);
- const formData = new FormData();
- if (!Array.isArray(files) || !files.length) return [];
- files.forEach(file => formData.append(Utils.GenerateGuid(), file));
- const parameters = {
- method: 'POST',
- body: formData
- };
- const response = await fetch("/uploadVideosandConcatenate", parameters);
- return response.json();
- }
-
+
export async function UploadYoutubeToServer<T extends Upload.FileInformation = Upload.FileInformation>(videoId: string): Promise<Upload.FileResponse<T>[]> {
const parameters = {
method: 'POST',
diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
index a91656cbc..effc3d8a8 100644
--- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx
+++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx
@@ -53,11 +53,6 @@ export function ProgressBar(props: ProgressBarProps) {
return [...prevOrdered, { endTime, startTime , order }];
});
}
- // }props.videos.map((vid, order) => {
- // //const { endTime, startTime } = vid
- // // TODO: not tranfer the blobs around
- // return { ...vid, order };
- // }))
}, [props.videos]);
useEffect(() => {
@@ -96,7 +91,6 @@ export function ProgressBar(props: ProgressBarProps) {
}
const onPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
- console.log('pointer down')
// don't move the videobox element
e.stopPropagation()
@@ -107,6 +101,20 @@ export function ProgressBar(props: ProgressBarProps) {
// don't do anything if null
const progressBar = progressBarRef.current
if (progressBar == null || clickedSegment.id === progressBar.id) return
+
+ console.log('pointer down')
+ // if holding shift key, let's remove that segment
+ if (e.shiftKey) {
+ // TODO: fix this
+ const segId = parseInt(clickedSegment.id.split('-')[1])
+ const o = ordered[segId].order
+ setOrdered(prevOrdered => {
+ return prevOrdered.filter((seg, i) => i !== segId)
+ })
+
+ return
+ }
+
const ptrId = e.pointerId;
progressBar.setPointerCapture(ptrId)
diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
index 68f3b3ad4..bd4af0a75 100644
--- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx
@@ -30,7 +30,7 @@ export class RecordingBox extends ViewBoxBaseComponent() {
Doc.SetNativeHeight(this.dataDoc, 720);
}
- @observable result: Upload.FileInformation | undefined = undefined
+ @observable result: Upload.AccessPathInfo | undefined = undefined
@observable videoDuration: number | undefined = undefined
@action
@@ -39,13 +39,14 @@ export class RecordingBox extends ViewBoxBaseComponent() {
}
@action
- setResult = (info: Upload.FileInformation, trackScreen: boolean) => {
+ setResult = (info: Upload.AccessPathInfo, trackScreen: boolean) => {
+ if (info == null) return
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.props.fieldKey] = new VideoField(this.result.accessPaths.client);
this.dataDoc[this.fieldKey + "-recorded"] = true;
// stringify the presenation and store it
if (trackScreen) {
diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx
index a5c2dc85c..8c8728fc3 100644
--- a/src/client/views/nodes/RecordingBox/RecordingView.tsx
+++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx
@@ -18,7 +18,7 @@ export interface MediaSegment {
}
interface IRecordingViewProps {
- setResult: (info: Upload.FileInformation, trackScreen: boolean) => void
+ setResult: (info: Upload.AccessPathInfo, trackScreen: boolean) => void
setDuration: (seconds: number) => void
id: string
}
@@ -57,7 +57,7 @@ export function RecordingView(props: IRecordingViewProps) {
useEffect(() => {
- console.log('in videos useEffect')
+ // console.log('in videos useEffect', finished)
if (finished) {
(async () => {
@@ -71,89 +71,18 @@ export function RecordingView(props: IRecordingViewProps) {
const { name } = videoFile;
inputPaths.push(name)
})
-
- console.log(inputPaths)
- const data = await Networking.UploadSegmentsAndConcatenate(videoFiles)
- console.log('data', 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");
- }
-
-
+ const serverPaths: string[] = (await Networking.UploadFilesToServer(videoFiles))
+ .map(res => (res.result instanceof Error) ? '' : res.result.accessPaths.agnostic.server)
- // const inputListName = 'order.txt';
- // fs.writeFileSync(inputListName, inputPaths.join('\n'));
- // var merge = ffmpeg();
- // merge.input(inputListName)
- // .inputOptions(['-f concat', '-safe 0'])
- // .outputOptions('-c copy')
- // .save('output.mp4')
-
- // fs.unlinkSync(inputListName);
-
- // const combined = await DashUploadUtils.combineSegments(videoFiles, inputPaths)
- // console.log('combined', combined)
-
- // const outputFile = new File(['output.mp4'], 'output.mp4', { type: 'video/mp4', lastModified: Date.now() });
-
- // const data = await Networking.UploadFilesToServer(combined)
- // const result = data[0].result
- // if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
- // props.setResult(result, trackScreen)
- // } else {
- // alert("video conversion failed");
- // }
-
-
- // if (format.includes("x-matroska")) {
- // await new Promise(res => ffmpeg(file.path)
- // .videoCodec("copy") // this will copy the data instead of reencode it
- // .save(file.path.replace(".mkv", ".mp4"))
- // .on('end', res));
- // file.path = file.path.replace(".mkv", ".mp4");
- // format = ".mp4";
- // }
- // console.log('crossOriginIsolated', crossOriginIsolated)
- // props.setDuration(recordingTimer * 100)
-
- // console.log('Loading ffmpeg-core.js');
- // const ffmpeg = createFFmpeg({ log: true });
- // await ffmpeg.load();
- // console.log('ffmpeg-core.js loaded');
-
- // let allVideoChunks: any = [];
- // const inputPaths: string[] = [];
- // // write each segment into it's indexed file
- // videos.forEach(async (vid, i) => {
- // const vidName = `segvideo${i}.mkv`
- // inputPaths.push(vidName)
- // const videoFile = new File(vid.videoChunks, vidName, { type: allVideoChunks[0].type, lastModified: Date.now() });
- // ffmpeg.FS('writeFile', vidName, await fetchFile(videoFile));
- // // })
- // ffmpeg.FS('writeFile', 'order.txt', inputPaths.join('\n'));
-
- // console.log('concat')
- // await ffmpeg.run('-f', 'concat', '-safe', '0', '-i', 'order.txt', 'ouput.mp4');
-
- // const { buffer } = ffmpeg.FS('readFile', 'output.mp4');
- // const concatVideo = new File([buffer], 'concat.mp4', { type: "video/mp4" });
-
- // const data = await Networking.UploadFilesToServer(concatVideo)
- // const result = data[0].result
- // if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
- // props.setResult(result, trackScreen)
- // } else {
- // alert("video conversion failed");
- // }
-
- // // delete all files in MEMFS
- // inputPaths.forEach(path => ffmpeg.FS('unlink', path));
- // ffmpeg.FS('unlink', 'order.txt');
- // ffmpeg.FS('unlink', 'output.mp4');
+ const result: Upload.AccessPathInfo | Error = await Networking.PostToServer('/concatVideos', serverPaths)
+ console.log(result)
+ if (!(result instanceof Error)) { // convert this screenshotBox into normal videoBox
+ props.setResult(result, trackScreen)
+ } else {
+ alert("video conversion failed");
+ }
+
})();
}
@@ -162,8 +91,6 @@ export function RecordingView(props: IRecordingViewProps) {
}, [videos])
useEffect(() => {
-
- console.log('in finish useEffect')
if (finished) {
setOrderVideos(true);
@@ -177,7 +104,7 @@ export function RecordingView(props: IRecordingViewProps) {
if (!navigator.mediaDevices) {
console.log('This browser does not support getUserMedia.')
}
- console.log('This device has the correct media devices.')
+ // console.log('This device has the correct media devices.')
}, [])
useEffect(() => {
@@ -244,7 +171,6 @@ export function RecordingView(props: IRecordingViewProps) {
// reset the temporary chunks
videoChunks = []
setRecording(false);
- setFinished(true);
trackScreen && RecordingApi.Instance.pause();
}
@@ -269,8 +195,10 @@ export function RecordingView(props: IRecordingViewProps) {
}
- const stop = () => {
- if (videoRecorder.current) {
+ 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())
@@ -278,19 +206,21 @@ export function RecordingView(props: IRecordingViewProps) {
}
}
- const pause = () => {
+ const pause = (e: React.MouseEvent) => {
+ e.stopPropagation()
if (videoRecorder.current) {
if (videoRecorder.current.state === "recording") {
- videoRecorder.current.pause();
+ videoRecorder.current.stop();
}
}
}
- const startOrResume = () => {
+ const startOrResume = (e: React.MouseEvent) => {
+ e.stopPropagation()
if (!videoRecorder.current || videoRecorder.current.state === "inactive") {
record();
} else if (videoRecorder.current.state === "paused") {
- videoRecorder.current.resume();
+ videoRecorder.current.start();
}
}
@@ -315,16 +245,6 @@ export function RecordingView(props: IRecordingViewProps) {
const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
return toTwoDigit(minutes) + " : " + toTwoDigit(seconds);
}
-
- const doTranscode = async () => {
-
- // console.log('Start transcoding');
- // ffmpeg.FS('writeFile', 'test.avi', await fetchFile('/flame.avi'));
- // await ffmpeg.run('-i', 'test.avi', 'test.mp4');
- // console.log('Complete transcoding');
- // const data = ffmpeg.FS('readFile', 'test.mp4');
- // console.log(URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' })));
- };
return (
<div className="recording-container">
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts
index 398b007b5..0ee0a34df 100644
--- a/src/server/ApiManagers/UploadManager.ts
+++ b/src/server/ApiManagers/UploadManager.ts
@@ -44,25 +44,10 @@ export default class UploadManager extends ApiManager {
register({
method: Method.POST,
- subscription: "/uploadVideosandConcatenate",
- secureHandler: async ({ req, res }) => {
- const form = new formidable.IncomingForm();
- form.keepExtensions = true;
- form.uploadDir = pathToDirectory(Directory.parsed_files);
- return new Promise<void>(resolve => {
- form.parse(req, async (_err, _fields, files) => {
- const results: Upload.FileResponse[] = [];
-
- // create an array of all the file paths
- const filePaths: string[] = Object.keys(files).map(key => files[key].path);
- console.log("uploading files", Array.isArray(filePaths));
- const result = await DashUploadUtils.concatenateVideos(filePaths);
- console.log('concatenated', result);
- result && !(result.result instanceof Error) && results.push(result);
- _success(res, results)
- resolve();
- });
- });
+ subscription: "/concatVideos",
+ secureHandler: async ({ req, res }) => {
+ // req.body contains the array of server paths to the videos
+ _success(res, await DashUploadUtils.concatVideos(req.body));
}
});
diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts
index 148b0df65..be30c115d 100644
--- a/src/server/DashUploadUtils.ts
+++ b/src/server/DashUploadUtils.ts
@@ -63,21 +63,20 @@ export namespace DashUploadUtils {
const { imageFormats, videoFormats, applicationFormats, audioFormats } = AcceptableMedia; //TODO:glr
- export async function concatenateVideos(filePaths: string[]): Promise<Upload.FileResponse> {
+ export async function concatVideos(filePaths: string[]): Promise<Upload.AccessPathInfo> {
// make a list of paths to create the ordered text file for ffmpeg
- //const filePaths = videoFiles.map(file => file.path);
- // write the text file to the file system
const inputListName = 'concat.txt';
const textFilePath = path.join(filesDirectory, inputListName);
// make a list of paths to create the ordered text file for ffmpeg
- const filePathsText = filePaths.map(filePath => `file '${filePath}'`).join('\n');
+ const filePathsText = filePaths.map(filePath => `file '${filePath}'`).join('\n');
+ // write the text file to the file system
writeFile(textFilePath, filePathsText, (err) => console.log(err));
- console.log(filePathsText)
+ console.log('fileTextPaths', filePathsText)
// make output file name based on timestamp
- const outputFileName = `output-${Utils.GenerateGuid()}.mkv`;
- // create the output file path in the parsed_file directory
- const outputFilePath = path.join(filesDirectory, outputFileName);
+ const outputFileName = `output-${Utils.GenerateGuid()}.mp4`;
+ // create the output file path in the videos directory
+ const outputFilePath = path.join(pathToDirectory(Directory.videos), outputFileName);
// concatenate the videos
await new Promise((resolve, reject) => {
@@ -92,45 +91,15 @@ export namespace DashUploadUtils {
.on("end", resolve);
})
- // delete concat.txt from the file system
- unlinkSync(textFilePath);
-
- // read the output file from the file system
- // const outputFile = fs.readFileSync(outputFilePath);
-
- // make a new blob object with the output file buffer
- // const outputFileBlob = new Blob([outputFile.buffer], { type: 'x-matroska/mkv' });
-
- // TODO: make with toJSON()
+ // delete concat.txt from the file system
+ unlinkSync(textFilePath);
+ // delete the old segment videos from the server
+ filePaths.forEach(filePath => unlinkSync(filePath));
- // make a data object
- const outputFileObject: formidable.File = {
- size: 0,
- name: outputFileName,
- path: outputFilePath,
- // size: outputFileBlob.size,
- type: 'video/x-matroska;codecs=avc1,opus',
- lastModifiedDate: new Date(),
-
- toJSON: () => ({ ...outputFileObject, filename: outputFilePath.replace(/.*\//, ""), mtime: null, length: 0, mime: "", toJson: () => undefined as any })
+ // return the path(s) to the output file
+ return {
+ accessPaths: getAccessPaths(Directory.videos, outputFileName)
}
-
- // const file = { ...outputFileObject, toJSON: () => ({ ...outputFileObject, filename: outputFilePath.replace(/.*\//, ""), mtime: null, length: 0, mime: "", toJson: () => undefined as any }) };
-
- // this will convert it to mp4 and save it to the server
- //return await MoveParsedFile(outputFileObject, Directory.videos);
-
- return await upload(outputFileObject);
-
- // // return only the output (first) file to the videos directory
- // return {
- // source: file, result: {
- // accessPaths: {
- // agnostic: getAccessPaths(Directory.videos, outputFileName)
- // },
- // rawText: undefined
- // }
- // }
}
export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> {