From 5ad8289e8ca3b6ff8304ac4fa3c2d3adaa9abf6d Mon Sep 17 00:00:00 2001 From: bobzel Date: Thu, 1 Sep 2022 14:52:04 -0400 Subject: afixed loadingBox to show error message for unsupported videos. fixed video uploading to generate errors for unsupported (Chrome) codecs. fixed videos that don't have codec support to still play audio and show duration. fixed dragging rotated documents to show and place the rotted document WYSIWYG --- src/client/Network.ts | 97 +++++++++++++++++++++++++-------------------------- 1 file changed, 47 insertions(+), 50 deletions(-) (limited to 'src/client/Network.ts') diff --git a/src/client/Network.ts b/src/client/Network.ts index c781d4b6b..a222b320f 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -1,57 +1,54 @@ -import { Utils } from "../Utils"; +import { Utils } from '../Utils'; import requestPromise = require('request-promise'); -import { Upload } from "../server/SharedMediaTypes"; +import { Upload } from '../server/SharedMediaTypes'; export namespace Networking { + export async function FetchFromServer(relativeRoute: string) { + return (await fetch(relativeRoute)).text(); + } - export async function FetchFromServer(relativeRoute: string) { - return (await fetch(relativeRoute)).text(); - } + export async function PostToServer(relativeRoute: string, body?: any) { + const options = { + uri: Utils.prepend(relativeRoute), + method: 'POST', + body, + json: true, + }; + return requestPromise.post(options); + } - export async function PostToServer(relativeRoute: string, body?: any) { - const options = { - uri: Utils.prepend(relativeRoute), - method: "POST", - body, - json: true - }; - return requestPromise.post(options); - } - - /** - * Handles uploading basic file types to server and makes the API call to "/uploadFormData" endpoint - * with the mapping of GUID to filem as parameters. - * - * @param files the files to be uploaded to the server - * @returns the response as a json from the server - */ - export async function UploadFilesToServer(files: File | File[]): Promise[]> { - const formData = new FormData(); - if (Array.isArray(files)) { - if (!files.length) { - return []; - } - files.forEach(file => formData.append(Utils.GenerateGuid(), file)); - } else { - formData.append(Utils.GenerateGuid(), files); + /** + * Handles uploading basic file types to server and makes the API call to "/uploadFormData" endpoint + * with the mapping of GUID to filem as parameters. + * + * @param files the files to be uploaded to the server + * @returns the response as a json from the server + */ + export async function UploadFilesToServer(files: File | File[]): Promise[]> { + const formData = new FormData(); + if (Array.isArray(files)) { + if (!files.length) { + return []; } - const parameters = { - method: 'POST', - body: formData - }; - const response = await fetch("/uploadFormData", parameters); - return response.json(); - } - - export async function UploadYoutubeToServer(videoId: string): Promise[]> { - const parameters = { - method: 'POST', - body: JSON.stringify({ videoId }), - json: true - }; - const response = await fetch("/uploadYoutubeVideo", parameters); - return response.json(); - } - + files.forEach(file => formData.append(Utils.GenerateGuid(), file)); + } else { + formData.append(Utils.GenerateGuid(), files); + } + const parameters = { + method: 'POST', + body: formData, + }; + const response = await fetch('/uploadFormData', parameters); + return response.json(); + } -} \ No newline at end of file + export async function UploadYoutubeToServer(videoId: string): Promise[]> { + const parameters = { + method: 'POST', + body: JSON.stringify({ videoId }), + json: true, + }; + const response = await fetch('/uploadYoutubeVideo', parameters); + return response.json(); + } +} -- cgit v1.2.3-70-g09d2 From dc5d5d318ad4ebc99e7c1302057e1b6e132f5017 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 21 Sep 2022 21:55:27 -0400 Subject: imposed 50MB max file size for uploads --- src/client/Network.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src/client/Network.ts') diff --git a/src/client/Network.ts b/src/client/Network.ts index a222b320f..996eb35d8 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -30,6 +30,17 @@ export namespace Networking { if (!files.length) { return []; } + const maxFileSize = 50000000; + if (files.some(f => f.size > maxFileSize)) { + return new Promise(res => + res([ + { + source: { name: '', type: '', size: 0, toJson: () => ({ name: '', type: '' }) }, + result: { name: '', message: `max file size (${maxFileSize / 1000000}MB) exceeded` }, + }, + ]) + ); + } files.forEach(file => formData.append(Utils.GenerateGuid(), file)); } else { formData.append(Utils.GenerateGuid(), files); -- cgit v1.2.3-70-g09d2 From 357247845341ef5e92d74883aeea093351d18490 Mon Sep 17 00:00:00 2001 From: bobzel Date: Fri, 23 Sep 2022 15:29:22 -0400 Subject: added progress messages to youtube uploads --- src/client/Network.ts | 9 +++++++++ src/client/views/nodes/LoadingBox.tsx | 16 ++++++++++++++- src/server/ApiManagers/UploadManager.ts | 15 ++++++++++++++ src/server/DashUploadUtils.ts | 35 +++++++++------------------------ 4 files changed, 48 insertions(+), 27 deletions(-) (limited to 'src/client/Network.ts') diff --git a/src/client/Network.ts b/src/client/Network.ts index 996eb35d8..19eff3b3b 100644 --- a/src/client/Network.ts +++ b/src/client/Network.ts @@ -62,4 +62,13 @@ export namespace Networking { const response = await fetch('/uploadYoutubeVideo', parameters); return response.json(); } + export async function QueryYoutubeProgress(videoId: string): Promise<{ progress: string }> { + const parameters = { + method: 'POST', + body: JSON.stringify({ videoId }), + json: true, + }; + const response = await fetch('/queryYoutubeProgress', parameters); + return response.json(); + } } diff --git a/src/client/views/nodes/LoadingBox.tsx b/src/client/views/nodes/LoadingBox.tsx index 53390328f..8c5255f80 100644 --- a/src/client/views/nodes/LoadingBox.tsx +++ b/src/client/views/nodes/LoadingBox.tsx @@ -1,8 +1,10 @@ +import { action, observable, runInAction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import ReactLoading from 'react-loading'; import { Doc } from '../../../fields/Doc'; import { StrCast } from '../../../fields/Types'; +import { Networking } from '../../Network'; import { ViewBoxAnnotatableComponent } from '../DocComponent'; import { FieldView, FieldViewProps } from './FieldView'; import './LoadingBox.scss'; @@ -34,17 +36,29 @@ export class LoadingBox extends ViewBoxAnnotatableComponent() { return FieldView.LayoutString(LoadingBox, fieldKey); } + _timer: any; + @observable progress = ''; componentDidMount() { if (!Doc.CurrentlyLoading?.includes(this.rootDoc)) { this.rootDoc.loadingError = 'Upload interrupted, please try again'; + } else { + const updateFunc = async () => { + const result = await Networking.QueryYoutubeProgress(StrCast(this.rootDoc.title)); + runInAction(() => (this.progress = result.progress)); + this._timer = setTimeout(updateFunc, 1000); + }; + this._timer = setTimeout(updateFunc, 1000); } } + componentWillUnmount() { + clearTimeout(this._timer); + } render() { return (
-

{StrCast(this.rootDoc.loadingError, 'Loading (can take several minutes):')}

+

{StrCast(this.rootDoc.loadingError, 'Loading ' + (this.progress.replace('[download]', '') || '(can take several minutes)'))}

{StrCast(this.rootDoc.title)} {this.rootDoc.loadingError ? null : }
diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 76cf36d44..fe4c475c9 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -106,6 +106,21 @@ export default class UploadManager extends ApiManager { }, }); + register({ + method: Method.POST, + subscription: '/queryYoutubeProgress', + secureHandler: async ({ req, res }) => { + return new Promise(async resolve => { + req.addListener('data', args => { + const payload = String.fromCharCode.apply(String, args); + const videoId = JSON.parse(payload).videoId; + _success(res, { progress: DashUploadUtils.QueryYoutubeProgress(videoId) }); + resolve(); + }); + }); + }, + }); + register({ method: Method.POST, subscription: new RouteSubscriber('youtubeScreenshot'), diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index 2c549cc9f..45e88d8cc 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -117,6 +117,12 @@ export namespace DashUploadUtils { }; } + export function QueryYoutubeProgress(videoId: string) { + return uploadProgress.get(videoId) ?? 'failed'; + } + + let uploadProgress = new Map(); + export function uploadYoutube(videoId: string): Promise { return new Promise>((res, rej) => { console.log('Uploading YouTube video: ' + videoId); @@ -124,6 +130,7 @@ export namespace DashUploadUtils { const path = name.replace(/^-/, '__') + '.mp4'; const finalPath = serverPathToFile(Directory.videos, path); if (existsSync(finalPath)) { + uploadProgress.set(videoId, 'computing duration'); exec(`yt-dlp -o ${finalPath} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { const time = Array.from(stdout.trim().split(':')).reverse(); const duration = (time.length > 2 ? Number(time[2]) * 1000 * 60 : 0) + (time.length > 1 ? Number(time[1]) * 60 : 0) + (time.length > 0 ? Number(time[0]) : 0); @@ -132,9 +139,7 @@ export namespace DashUploadUtils { } else { const ytdlp = spawn(`yt-dlp`, ['-o', path, videoId, '-f', 'mp4']); - ytdlp.stdout.on('data', function (data: any) { - console.log('stdout: ' + data.toString()); // bcz: somehow channel this back to the client... - }); + ytdlp.stdout.on('data', (data: any) => uploadProgress.set(videoId, data.toString())); let errors = ''; ytdlp.stderr.on('data', (data: any) => (errors = data.toString())); @@ -153,6 +158,7 @@ export namespace DashUploadUtils { result: { name: 'failed youtube query', message: `Could not upload video. ${errors}` }, }); } else { + uploadProgress.set(videoId, 'computing duration'); exec(`yt-dlp-o ${path} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { const time = Array.from(stdout.trim().split(':')).reverse(); const duration = (time.length > 2 ? Number(time[2]) * 1000 * 60 : 0) + (time.length > 1 ? Number(time[1]) * 60 : 0) + (time.length > 0 ? Number(time[0]) : 0); @@ -162,29 +168,6 @@ export namespace DashUploadUtils { }); } }); - // exec(`yt-dlp -o ${path} "https://www.youtube.com/watch?v=${videoId}" -f "mp4"`, (error: any, stdout: any, stderr: any) => { - // if (error) { - // console.log(`error: Error: ${error.message}`); - // res({ - // source: { - // size: 0, - // path, - // name, - // type: '', - // toJSON: () => ({ name, path }), - // }, - // result: { name: 'failed youtube query', message: `Could not upload YouTube video (${videoId}). Error: ${error.message}` }, - // }); - // } else { - // exec(`yt-dlp-o ${path} "https://www.youtube.com/watch?v=${videoId}" --get-duration`, (error: any, stdout: any, stderr: any) => { - // const time = Array.from(stdout.trim().split(':')).reverse(); - // const duration = (time.length > 2 ? Number(time[2]) * 1000 * 60 : 0) + (time.length > 1 ? Number(time[1]) * 60 : 0) + (time.length > 0 ? Number(time[0]) : 0); - // const data = { size: 0, path, name, type: 'video/mp4' }; - // const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ''), mtime: duration.toString(), mime: '', toJson: () => undefined as any }) }; - // res(MoveParsedFile(file, Directory.videos)); - // }); - // } - // }); } }); } -- cgit v1.2.3-70-g09d2