diff options
author | Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local> | 2022-06-08 00:05:34 -0400 |
---|---|---|
committer | Michael Foiani <sotech117@Michaels-MacBook-Pro-5.local> | 2022-06-08 00:05:34 -0400 |
commit | c656abad84ac56c3067a48ca6d34d567e3fcdd88 (patch) | |
tree | be0114c2a48ac621e210a294d719953091cee97b /src | |
parent | ac76c4836c61cc6564367f35e14014bb9489257b (diff) |
Took stab at ffmpeg in server. did some reworking of recordingview css. fix smoothness bug for the moving segment that follow cursor.
Diffstat (limited to 'src')
-rw-r--r-- | src/client/Network.ts | 15 | ||||
-rw-r--r-- | src/client/views/nodes/RecordingBox/ProgressBar.scss | 33 | ||||
-rw-r--r-- | src/client/views/nodes/RecordingBox/ProgressBar.tsx | 9 | ||||
-rw-r--r-- | src/client/views/nodes/RecordingBox/RecordingBox.tsx | 4 | ||||
-rw-r--r-- | src/client/views/nodes/RecordingBox/RecordingView.scss | 29 | ||||
-rw-r--r-- | src/client/views/nodes/RecordingBox/RecordingView.tsx | 23 | ||||
-rw-r--r-- | src/server/ApiManagers/UploadManager.ts | 28 | ||||
-rw-r--r-- | src/server/DashUploadUtils.ts | 40 |
8 files changed, 136 insertions, 45 deletions
diff --git a/src/client/Network.ts b/src/client/Network.ts index 1255e5ce0..2c6d9d711 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -19,7 +19,6 @@ export namespace Networking { } export async function UploadFilesToServer<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> { - console.log(files) const formData = new FormData(); if (Array.isArray(files)) { if (!files.length) { @@ -36,6 +35,19 @@ export namespace Networking { const response = await fetch("/uploadFormData", parameters); return response.json(); } + + export async function UploadSegmentsAndConcatenate<T extends Upload.FileInformation = Upload.FileInformation>(files: File | File[]): Promise<Upload.FileResponse<T>[]> { + console.log(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 = { @@ -46,5 +58,6 @@ export namespace Networking { const response = await fetch("/uploadYoutubeVideo", parameters); return response.json(); } + }
\ No newline at end of file diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.scss b/src/client/views/nodes/RecordingBox/ProgressBar.scss index 67f96033a..d387468c6 100644 --- a/src/client/views/nodes/RecordingBox/ProgressBar.scss +++ b/src/client/views/nodes/RecordingBox/ProgressBar.scss @@ -1,13 +1,17 @@ .progressbar { touch-action: none; + vertical-align: middle; + text-align: center; + + align-items: center; position: absolute; display: flex; justify-content: flex-start; - bottom: 10px; - width: 80%; + bottom: 2px; + width: 99%; height: 30px; background-color: gray; @@ -34,11 +38,24 @@ margin: 1px; padding: 0; cursor: pointer; - transition-duration: .25s; + transition-duration: .5s; + user-select: none; + vertical-align: middle; text-align: center; } +.segment-hide { + background-color: inherit; + text-align: center; + vertical-align: middle; + user-select: none; + // /* Hide the text. */ + // text-indent: 100%; + // white-space: nowrap; + // overflow: hidden; +} + .segment:first-child { margin-left: 2px; } @@ -47,9 +64,17 @@ } .segment:hover { + background-color: white; +} + +.segment:hover, .segment-selected { margin: 0px; border: 4px solid red; - background-color: black; min-width: 10px; border-radius: 2px; } + +.segment-selected { + border: 4px solid grey; + background-color: red; +} diff --git a/src/client/views/nodes/RecordingBox/ProgressBar.tsx b/src/client/views/nodes/RecordingBox/ProgressBar.tsx index b17d27d2e..34a771367 100644 --- a/src/client/views/nodes/RecordingBox/ProgressBar.tsx +++ b/src/client/views/nodes/RecordingBox/ProgressBar.tsx @@ -27,7 +27,7 @@ export function ProgressBar(props: ProgressBarProps) { useEffect(() => { const segmentsJSX = ordered.map((vid, i) => - <div id={`segment-${i.toString()}`} className='segment' style={{ width: `${((vid.endTime - vid.startTime) / totalTime()) * 100}%`, backgroundColor: clicked === i ? 'inherit' : 'red', }}>{vid.order}</div>); + <div id={`segment-${i.toString()}`} className={ clicked === i ? 'segment-hide' : 'segment'} style={{ width: `${((vid.endTime - vid.startTime) / totalTime()) * 100}%` }}>{vid.order}</div>); setSegments(segmentsJSX) }, [clicked, ordered]) @@ -98,7 +98,8 @@ export function ProgressBar(props: ProgressBarProps) { // clickedSegment.style.backgroundColor = `inherit`; const dot = document.createElement("div") - dot.classList.add("segment") + dot.classList.add("segment-selected") + dot.style.transitionDuration = '0s'; dot.style.position = 'absolute'; dot.style.zIndex = '999'; dot.style.width = `${rect.width}px`; @@ -106,8 +107,8 @@ export function ProgressBar(props: ProgressBarProps) { document.body.append(dot) const dragSegment = (event: PointerEvent): void => { // event.stopPropagation() - dot.style.left = `${event.clientX - rect.width/2}px`; - dot.style.top = `${event.clientY - rect.height/2}px`; + dot.style.left = `${event.clientX - rect.width/2}px`; + dot.style.top = `${event.clientY - rect.height/2}px`; } const updateSegmentOrder = (event: PointerEvent): void => { diff --git a/src/client/views/nodes/RecordingBox/RecordingBox.tsx b/src/client/views/nodes/RecordingBox/RecordingBox.tsx index 159271223..68f3b3ad4 100644 --- a/src/client/views/nodes/RecordingBox/RecordingBox.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingBox.tsx @@ -54,9 +54,9 @@ export class RecordingBox extends ViewBoxBaseComponent() { } render() { - // console.log("Proto[Is]: ", this.rootDoc.proto?.[Id]) + console.log("Proto[Is]: ", this.rootDoc.proto?.[Id]) return <div className="recordingBox" ref={this._ref}> - {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={this.rootDoc.proto?.[Id]} />} + {!this.result && <RecordingView setResult={this.setResult} setDuration={this.setVideoDuration} id={this.rootDoc.proto?.[Id] || ''} />} </div>; } } diff --git a/src/client/views/nodes/RecordingBox/RecordingView.scss b/src/client/views/nodes/RecordingBox/RecordingView.scss index 9b2f6d070..0b7b1918f 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.scss +++ b/src/client/views/nodes/RecordingBox/RecordingView.scss @@ -33,7 +33,7 @@ button { } .video-wrapper:hover .controls { - bottom: 30px; + bottom: 34.5px; transform: translateY(0%); opacity: 100%; } @@ -43,8 +43,8 @@ button { align-items: center; justify-content: space-evenly; position: absolute; - padding: 14px; - width: 100%; + // padding: 14px; + //width: 100%; max-width: 500px; // max-height: 20%; flex-wrap: wrap; @@ -56,7 +56,14 @@ button { // transform: translateY(150%); transition: all 0.3s ease-in-out; // opacity: 0%; - bottom: 30px; + bottom: 34.5px; + height: 60px; + right: 2px; + // bottom: -150px; +} + +.controls:active { + bottom: 40px; // bottom: -150px; } @@ -127,9 +134,8 @@ button { .controls-inner-container { display: flex; flex-direction: row; - justify-content: center; - width: 100%; - + align-content: center; + position: relative; } .record-button-wrapper { @@ -180,14 +186,14 @@ button { height: 100%; display: flex; flex-direction: row; - align-items: center; - position: absolute; + align-content: center; + position: relative; top: 0; bottom: 0; &.video-edit-wrapper { - right: 50% - 15; + // right: 50% - 15; .track-screen { font-weight: 200; @@ -197,10 +203,11 @@ button { &.track-screen-wrapper { - right: 50% - 30; + // right: 50% - 30; .track-screen { font-weight: 200; + color: aqua; } } diff --git a/src/client/views/nodes/RecordingBox/RecordingView.tsx b/src/client/views/nodes/RecordingBox/RecordingView.tsx index 640e4f5f2..1e9ce22f1 100644 --- a/src/client/views/nodes/RecordingBox/RecordingView.tsx +++ b/src/client/views/nodes/RecordingBox/RecordingView.tsx @@ -71,13 +71,21 @@ export function RecordingView(props: IRecordingViewProps) { inputPaths.push(name) }); - console.log(videoFiles) + console.log(videoFiles) + + 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 inputListName = 'order.txt'; // fs.writeFileSync(inputListName, inputPaths.join('\n')); - // var merge = ffmpeg(); // merge.input(inputListName) // .inputOptions(['-f concat', '-safe 0']) @@ -345,16 +353,17 @@ export function RecordingView(props: IRecordingViewProps) { <span className="checkmark"></span> Track Screen </label> - </div>)} - - </div> + </div>)} - <ProgressBar + </div> + + </div> + + <ProgressBar videos={videos} setVideos={setVideos} // playSegment={playSegment} /> - </div> </div> </div>) }
\ No newline at end of file diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 634548154..faf36c6e5 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -40,7 +40,31 @@ export function clientPathToFile(directory: Directory, filename: string) { export default class UploadManager extends ApiManager { - protected initialize(register: Registration): void { + protected initialize(register: Registration): void { + + 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 result: Upload.FileResponse[] = []; + for (const key in files) { + const f = files[key]; + if (Array.isArray(f)) { + const result = await DashUploadUtils.concatenateVideos(f); + console.log('concatenated', result); + result && !(result.result instanceof Error) && _success(res, result); + } + } + resolve(); + }); + }); + } + }); register({ method: Method.POST, @@ -50,7 +74,7 @@ export default class UploadManager extends ApiManager { form.keepExtensions = true; form.uploadDir = pathToDirectory(Directory.parsed_files); return new Promise<void>(resolve => { - form.parse(req, async (_err, _fields, files) => { + form.parse(req, async (_err, _fields, files) => { const results: Upload.FileResponse[] = []; for (const key in files) { const f = files[key]; diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 0c4f87905..6a7c8543d 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -1,9 +1,9 @@ import { green, red } from 'colors'; import { ExifImage } from 'exif'; +import * as exifr from 'exifr'; import { File } from 'formidable'; import { createWriteStream, existsSync, readFileSync, rename, unlinkSync, writeFile } from 'fs'; import * as path from 'path'; -import * as exifr from 'exifr'; import { basename } from "path"; import * as sharp from 'sharp'; import { Stream } from 'stream'; @@ -17,7 +17,6 @@ import { resolvedServerUrl } from "./server_Initialization"; import { AcceptableMedia, Upload } from './SharedMediaTypes'; import request = require('request-promise'); import formidable = require('formidable'); -import { output } from '../../webpack.config'; const { exec } = require("child_process"); const parse = require('pdf-parse'); const ffmpeg = require("fluent-ffmpeg"); @@ -63,19 +62,31 @@ export namespace DashUploadUtils { const { imageFormats, videoFormats, applicationFormats, audioFormats } = AcceptableMedia; //TODO:glr - export async function combineSegments(filePtr: File[], inputPaths: string[]): Promise<File> { - const inputListName = 'order.txt'; - - return new Promise<File>((resolve, reject) => { - fs.writeFileSync(inputListName, inputPaths.join('\n')); - ffmpeg(inputListName).inputOptions(['-f concat', '-safe 0']).outputOptions('-c copy').save('output.mp4') + export async function concatenateVideos(videoFiles: File[]): Promise<Upload.FileResponse> { + // 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, "concat.txt"); + writeFile(textFilePath, filePaths.join("\n"), (err) => console.log(err)); + + + // make output file name based on timestamp + const outputFileName = `output-${Utils.GenerateGuid()}.mp4`; + await new Promise((resolve, reject) => { + ffmpeg(inputListName).inputOptions(['-f concat', '-safe 0']).outputOptions('-c copy').save(outputFileName) .on("error", reject) - .on("end", () => { - fs.unlinkSync(inputListName); - filePtr[0].path = 'output.mp4'; - resolve(filePtr[0]); - }); - }); + .on("end", resolve); + }) + + // delete concat.txt from the file system + unlinkSync(textFilePath); + + // read the output file from the file system + const outputFile = fs.readFileSync(outputFileName); + console.log('outputFile', outputFile); + // move only the output file to the videos directory + return MoveParsedFile(outputFile, Directory.videos) } export function uploadYoutube(videoId: string): Promise<Upload.FileResponse> { @@ -237,6 +248,7 @@ export namespace DashUploadUtils { } let resolvedUrl: string; /** + * * At this point, we want to take whatever url we have and make sure it's requestable. * Anything that's hosted by some other website already is, but if the url is a local file url * (locates the file on this server machine), we have to resolve the client side url by cutting out the |