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/util/DragManager.ts | 44 +++++++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 13 deletions(-) (limited to 'src/client/util/DragManager.ts') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index 6386c87a0..dfd916e92 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -344,8 +344,7 @@ export namespace DragManager { } Object.assign(dragDiv.style, { width: '', height: '', overflow: '' }); dragDiv.hidden = false; - const scaleXs: number[] = [], - scaleYs: number[] = [], + const scalings: number[] = [], xs: number[] = [], ys: number[] = []; @@ -355,8 +354,15 @@ export namespace DragManager { top: Number.MAX_SAFE_INTEGER, bottom: Number.MIN_SAFE_INTEGER, }; + let rot = 0; const docsToDrag = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : []; const dragElements = eles.map(ele => { + if (ele?.parentElement?.parentElement?.parentElement?.className === 'collectionFreeFormDocumentView-container') { + ele = ele.parentElement.parentElement.parentElement; + const rotStr = ele.style.transform.replace(/.*rotate\(([-0-9.]*)deg\).*/, '$1'); + if (rotStr) rot = Number(rotStr); + } + if (rot < 0) rot += 360; if (!ele.parentNode) dragDiv.appendChild(ele); const dragElement = ele.parentNode === dragDiv ? ele : (ele.cloneNode(true) as HTMLElement); const children = Array.from(dragElement.children); @@ -376,19 +382,29 @@ export namespace DragManager { } } const rect = ele.getBoundingClientRect(); - const scaleX = rect.width / (ele.offsetWidth || rect.width); - const scaleY = scaleX; //ele.offsetHeight ? rect.height / (ele.offsetHeight || rect.height) : scaleX; + const rotWidth = (rot > 45 && rot < 135) || (rot > 215 && rot < 305) ? rect.height : rect.width; //rect.width * Math.cos((rot * Math.PI) / 180) + rect.height * Math.sin((rot * Math.PI) / 180); + const scaling = rot ? rotWidth / ele.offsetWidth : rect.width / (ele.offsetWidth || rect.width); elesCont.left = Math.min(rect.left, elesCont.left); elesCont.top = Math.min(rect.top, elesCont.top); elesCont.right = Math.max(rect.right, elesCont.right); elesCont.bottom = Math.max(rect.bottom, elesCont.bottom); - xs.push(rect.left + (options?.offsetX || 0)); - ys.push(rect.top + (options?.offsetY || 0)); - scaleXs.push(scaleX); - scaleYs.push(scaleY); + const rotRad = (rot / 180) * Math.PI; + xs.push( + (rot > 90 && rot <= 270 ? rect.right : rect.left) + // + (rot > 270 ? -scaling * (ele.offsetHeight * Math.sin(rotRad)) : 0) + + (rot <= 90 || rot > 180 ? scaling * (ele.offsetHeight * Math.sin(rotRad)) : 0) + + (options?.offsetX || 0) + ); + ys.push( + rect.top + // + (rot > 180 ? -scaling * (ele.offsetWidth * Math.sin(rotRad)) : 0) + + (rot >= 90 && rot < 270 ? -scaling * (ele.offsetHeight * Math.cos(rotRad)) : 0) + + (options?.offsetY || 0) + ); + scalings.push(scaling); Object.assign(dragElement.style, { - opacity: '0.7', + opacity: '0', position: 'absolute', margin: '0', top: '0', @@ -399,9 +415,9 @@ export namespace DragManager { borderRadius: getComputedStyle(ele).borderRadius, zIndex: globalCssVariables.contextMenuZindex, transformOrigin: '0 0', - width: `${rect.width / scaleX}px`, - height: `${rect.height / scaleY}px`, - transform: `translate(${xs[0]}px, ${ys[0]}px) scale(${scaleX}, ${scaleY})`, + width: rot ? '' : `${rect.width / scaling}px`, + height: rot ? '' : `${rect.height / scaling}px`, + transform: `translate(${xs[0]}px, ${ys[0]}px) rotate(${rot}deg)`, }); dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; @@ -415,6 +431,8 @@ export namespace DragManager { [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => (ele as any).style && ((ele as any).style.pointerEvents = 'none')); dragDiv.appendChild(dragElement); + scalings[scalings.length - 1] = rect.width / dragElement.getBoundingClientRect().width; + setTimeout(() => (dragElement.style.opacity = '0.7')); if (dragElement !== ele) { const children = [Array.from(ele.children), Array.from(dragElement.children)]; while (children[0].length) { @@ -542,7 +560,7 @@ export namespace DragManager { const moveVec = { x: x - lastPt.x, y: y - lastPt.y }; lastPt = { x, y }; - dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x)}px, ${(ys[i] += moveVec.y)}px) scale(${scaleXs[i]}, ${scaleYs[i]})`)); + dragElements.map((dragElement, i) => (dragElement.style.transform = `translate(${(xs[i] += moveVec.x)}px, ${(ys[i] += moveVec.y)}px) rotate(${rot}deg) scale(${scalings[i]})`)); dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; }; const upHandler = (e: PointerEvent) => { -- cgit v1.2.3-70-g09d2 From e70360946815cdcde434e25eb592e1b919bb4105 Mon Sep 17 00:00:00 2001 From: bobzel Date: Tue, 6 Sep 2022 14:06:02 -0400 Subject: final cleanup of dragging rotated images. fixed loading of youtube videos and displaying errors when the upload fails --- src/client/documents/Documents.ts | 13 +- src/client/util/DragManager.ts | 39 ++-- src/client/views/PreviewCursor.tsx | 55 ++++-- .../collectionFreeForm/CollectionFreeFormView.tsx | 2 +- .../collections/collectionFreeForm/MarqueeView.tsx | 12 +- src/client/views/nodes/LoadingBox.tsx | 10 +- src/client/views/nodes/VideoBox.scss | 20 +- src/client/views/nodes/VideoBox.tsx | 212 ++++++++++----------- src/server/ApiManagers/UploadManager.ts | 2 +- src/server/DashUploadUtils.ts | 48 +++-- src/server/SharedMediaTypes.ts | 38 ++-- 11 files changed, 257 insertions(+), 194 deletions(-) (limited to 'src/client/util/DragManager.ts') diff --git a/src/client/documents/Documents.ts b/src/client/documents/Documents.ts index 8c3b91177..7111cb233 100644 --- a/src/client/documents/Documents.ts +++ b/src/client/documents/Documents.ts @@ -1,5 +1,4 @@ import { IconProp } from '@fortawesome/fontawesome-svg-core'; -import { files } from 'jszip'; import { action, runInAction } from 'mobx'; import { basename } from 'path'; import { DateField } from '../../fields/DateField'; @@ -1780,6 +1779,9 @@ export namespace DocUtils { proto.lng = ConvertDMSToDD(longitude[0], longitude[1], longitude[2], longitudeDirection); } } + if (Upload.isVideoInformation(result)) { + proto['data-duration'] = result.duration; + } generatedDocuments.push(doc); } } @@ -1814,7 +1816,7 @@ export namespace DocUtils { source: { name, type }, result, } of await Networking.UploadYoutubeToServer(videoId)) { - name && type && processFileupload(generatedDocuments, name, type, result, options); + name && processFileupload(generatedDocuments, name, type, result, options); } return generatedDocuments; } @@ -1826,7 +1828,12 @@ export namespace DocUtils { source: { name, type }, result, } = upfiles.lastElement(); - name && type && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); + if ((result as any).message) { + if (overwriteDoc) { + overwriteDoc.isLoading = false; + overwriteDoc.errorMessage = (result as any).message; + } + } else name && processFileupload(generatedDocuments, name, type, result, options, overwriteDoc); }); } diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index dfd916e92..cec158d23 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -382,29 +382,28 @@ export namespace DragManager { } } const rect = ele.getBoundingClientRect(); - const rotWidth = (rot > 45 && rot < 135) || (rot > 215 && rot < 305) ? rect.height : rect.width; //rect.width * Math.cos((rot * Math.PI) / 180) + rect.height * Math.sin((rot * Math.PI) / 180); - const scaling = rot ? rotWidth / ele.offsetWidth : rect.width / (ele.offsetWidth || rect.width); + const w = ele.offsetWidth || rect.width; + const h = ele.offsetHeight || rect.height; + const rotR = -(rot / 180) * Math.PI; + const tl = [0, 0]; + const tr = [Math.cos(rotR) * w, Math.sin(-rotR) * w]; + const bl = [Math.sin(rotR) * h, Math.cos(-rotR) * h]; + const br = [Math.cos(rotR) * w + Math.sin(rotR) * h, Math.cos(-rotR) * h - Math.sin(rotR) * w]; + const minx = Math.min(tl[0], tr[0], br[0], bl[0]); + const maxx = Math.max(tl[0], tr[0], br[0], bl[0]); + const miny = Math.min(tl[1], tr[1], br[1], bl[1]); + const maxy = Math.max(tl[1], tr[1], br[1], bl[1]); + const scaling = rect.width / (Math.abs(maxx - minx) || 1); elesCont.left = Math.min(rect.left, elesCont.left); elesCont.top = Math.min(rect.top, elesCont.top); elesCont.right = Math.max(rect.right, elesCont.right); elesCont.bottom = Math.max(rect.bottom, elesCont.bottom); - const rotRad = (rot / 180) * Math.PI; - xs.push( - (rot > 90 && rot <= 270 ? rect.right : rect.left) + // - (rot > 270 ? -scaling * (ele.offsetHeight * Math.sin(rotRad)) : 0) + - (rot <= 90 || rot > 180 ? scaling * (ele.offsetHeight * Math.sin(rotRad)) : 0) + - (options?.offsetX || 0) - ); - ys.push( - rect.top + // - (rot > 180 ? -scaling * (ele.offsetWidth * Math.sin(rotRad)) : 0) + - (rot >= 90 && rot < 270 ? -scaling * (ele.offsetHeight * Math.cos(rotRad)) : 0) + - (options?.offsetY || 0) - ); + xs.push(((0 - minx) / (maxx - minx)) * rect.width + rect.left); + ys.push(((0 - miny) / (maxy - miny)) * rect.height + rect.top); scalings.push(scaling); Object.assign(dragElement.style, { - opacity: '0', + opacity: '0.7', position: 'absolute', margin: '0', top: '0', @@ -415,9 +414,9 @@ export namespace DragManager { borderRadius: getComputedStyle(ele).borderRadius, zIndex: globalCssVariables.contextMenuZindex, transformOrigin: '0 0', - width: rot ? '' : `${rect.width / scaling}px`, - height: rot ? '' : `${rect.height / scaling}px`, - transform: `translate(${xs[0]}px, ${ys[0]}px) rotate(${rot}deg)`, + width: '', + height: '', + transform: `translate(${xs[0]}px, ${ys[0]}px) rotate(${rot}deg) scale(${scaling})`, }); dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; @@ -431,8 +430,6 @@ export namespace DragManager { [dragElement, ...Array.from(dragElement.getElementsByTagName('*'))].forEach(ele => (ele as any).style && ((ele as any).style.pointerEvents = 'none')); dragDiv.appendChild(dragElement); - scalings[scalings.length - 1] = rect.width / dragElement.getBoundingClientRect().width; - setTimeout(() => (dragElement.style.opacity = '0.7')); if (dragElement !== ele) { const children = [Array.from(ele.children), Array.from(dragElement.children)]; while (children[0].length) { diff --git a/src/client/views/PreviewCursor.tsx b/src/client/views/PreviewCursor.tsx index 68f5f072d..4c17d5a97 100644 --- a/src/client/views/PreviewCursor.tsx +++ b/src/client/views/PreviewCursor.tsx @@ -4,9 +4,9 @@ import 'normalize.css'; import * as React from 'react'; import { Doc } from '../../fields/Doc'; import { Cast, NumCast, StrCast } from '../../fields/Types'; -import { returnFalse } from '../../Utils'; +import { emptyFunction, returnFalse } from '../../Utils'; import { DocServer } from '../DocServer'; -import { Docs, DocUtils } from '../documents/Documents'; +import { Docs, DocumentOptions, DocUtils } from '../documents/Documents'; import { Transform } from '../util/Transform'; import { undoBatch, UndoManager } from '../util/UndoManager'; import { FormattedTextBox } from './nodes/formattedText/FormattedTextBox'; @@ -16,15 +16,25 @@ import './PreviewCursor.scss'; export class PreviewCursor extends React.Component<{}> { static _onKeyPress?: (e: KeyboardEvent) => void; static _getTransform: () => Transform; - static _addDocument: (doc: Doc | Doc[]) => void; + static _addDocument: (doc: Doc | Doc[]) => boolean; static _addLiveTextDoc: (doc: Doc) => void; static _nudge?: undefined | ((x: number, y: number) => boolean); + static _slowLoadDocuments?: ( + files: File[] | string, + options: DocumentOptions, + generatedDocuments: Doc[], + text: string, + completed: ((doc: Doc[]) => void) | undefined, + clientX: number, + clientY: number, + addDocument: (doc: Doc | Doc[]) => boolean + ) => Promise; @observable static _clickPoint = [0, 0]; @observable public static Visible = false; constructor(props: any) { super(props); document.addEventListener('keydown', this.onKeyPress); - document.addEventListener('paste', this.paste); + document.addEventListener('paste', this.paste, true); } paste = async (e: ClipboardEvent) => { @@ -38,20 +48,16 @@ export class PreviewCursor extends React.Component<{}> { if (plain) { // tests for youtube and makes video document if (plain.indexOf('www.youtube.com/watch') !== -1) { - const url = plain.replace('youtube.com/watch?v=', 'youtube.com/embed/'); - undoBatch(() => - PreviewCursor._addDocument( - Docs.Create.VideoDocument(url, { - title: url, - _width: 400, - _height: 315, - _nativeWidth: 600, - _nativeHeight: 472.5, - x: newPoint[0], - y: newPoint[1], - }) - ) - )(); + const batch = UndoManager.StartBatch('youtube upload'); + const generatedDocuments: Doc[] = []; + const options = { + title: plain, + _width: 400, + _height: 315, + x: newPoint[0], + y: newPoint[1], + }; + PreviewCursor._slowLoadDocuments?.(plain.split('v=')[1].split('&')[0], options, generatedDocuments, '', undefined, newPoint[0], newPoint[1], PreviewCursor._addDocument).then(batch.end); } else if (re.test(plain)) { const url = plain; undoBatch(() => @@ -184,7 +190,17 @@ export class PreviewCursor extends React.Component<{}> { addLiveText: (doc: Doc) => void, getTransform: () => Transform, addDocument: undefined | ((doc: Doc | Doc[]) => boolean), - nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean) + nudge: undefined | ((nudgeX: number, nudgeY: number) => boolean), + slowLoadDocuments: ( + files: File[] | string, + options: DocumentOptions, + generatedDocuments: Doc[], + text: string, + completed: ((doc: Doc[]) => void) | undefined, + clientX: number, + clientY: number, + addDocument: (doc: Doc | Doc[]) => boolean + ) => Promise ) { this._clickPoint = [x, y]; this._onKeyPress = onKeyPress; @@ -192,6 +208,7 @@ export class PreviewCursor extends React.Component<{}> { this._getTransform = getTransform; this._addDocument = addDocument || returnFalse; this._nudge = nudge; + this._slowLoadDocuments = slowLoadDocuments; this.Visible = true; } render() { diff --git a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx index 0c4de681a..947bd8aaa 100644 --- a/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx +++ b/src/client/views/collections/collectionFreeForm/CollectionFreeFormView.tsx @@ -52,7 +52,6 @@ import { CollectionFreeFormRemoteCursors } from './CollectionFreeFormRemoteCurso import './CollectionFreeFormView.scss'; import { MarqueeView } from './MarqueeView'; import React = require('react'); -import e = require('connect-flash'); export type collectionFreeformViewProps = { annotationLayerHostsContent?: boolean; // whether to force scaling of content (needed by ImageBox) @@ -1858,6 +1857,7 @@ export class CollectionFreeFormView extends CollectionSubView 0 ? undefined : this.nudge} addDocTab={this.addDocTab} + slowLoadDocuments={this.slowLoadDocuments} trySelectCluster={this.trySelectCluster} activeDocuments={this.getActiveDocuments} selectDocuments={this.selectDocuments} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx index 65a11cbcb..58a00bbac 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.tsx +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.tsx @@ -40,6 +40,16 @@ interface MarqueeViewProps { nudge?: (x: number, y: number, nudgeTime?: number) => boolean; ungroup?: () => void; setPreviewCursor?: (func: (x: number, y: number, drag: boolean, hide: boolean) => void) => void; + slowLoadDocuments: ( + files: File[] | string, + options: DocumentOptions, + generatedDocuments: Doc[], + text: string, + completed: ((doc: Doc[]) => void) | undefined, + clientX: number, + clientY: number, + addDocument: (doc: Doc | Doc[]) => boolean + ) => Promise; } export interface MarqueeViewBounds { @@ -330,7 +340,7 @@ export class MarqueeView extends React.Component() { return (
-

{this.rootDoc.isLoading ? 'Loading:' : StrCast(this.rootDoc.errorMessage, 'Error Loading File:')}

+

{this.rootDoc.isLoading ? 'Loading (can take several minutes):' : StrCast(this.rootDoc.errorMessage, 'Error Loading File:')}

{StrCast(this.rootDoc.title)} {!this.rootDoc.isLoading ? null : }
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss index aa51714da..c2aee7a1b 100644 --- a/src/client/views/nodes/VideoBox.scss +++ b/src/client/views/nodes/VideoBox.scss @@ -1,4 +1,4 @@ -@import "../global/globalCssVariables.scss"; +@import '../global/globalCssVariables.scss'; .mini-viewer { cursor: grab; @@ -97,7 +97,7 @@ height: 40px; padding: 0 10px 0 7px; transition: opacity 0.3s; - z-index: 100001; + z-index: 10001; .timecode-controls { display: flex; @@ -114,7 +114,8 @@ } } - .toolbar-slider.volume, .toolbar-slider.zoom { + .toolbar-slider.volume, + .toolbar-slider.zoom { width: 50px; } @@ -157,7 +158,8 @@ } } -.videoBox-content-fullScreen, .videoBox-content-fullScreen-interactive { +.videoBox-content-fullScreen, +.videoBox-content-fullScreen-interactive { display: flex; justify-content: center; align-items: flex-end; @@ -175,16 +177,16 @@ video::-webkit-media-controls { display: none !important; } -input[type="range"] { +input[type='range'] { -webkit-appearance: none; background: none; } -input[type="range"]:focus { +input[type='range']:focus { outline: none; } -input[type="range"]::-webkit-slider-runnable-track { +input[type='range']::-webkit-slider-runnable-track { width: 100%; height: 10px; cursor: pointer; @@ -193,7 +195,7 @@ input[type="range"]::-webkit-slider-runnable-track { border-radius: 10px; } -input[type="range"]::-webkit-slider-thumb { +input[type='range']::-webkit-slider-thumb { box-shadow: 0; border: 0; height: 12px; @@ -203,4 +205,4 @@ input[type="range"]::-webkit-slider-thumb { cursor: pointer; -webkit-appearance: none; margin-top: -1px; -} \ No newline at end of file +} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 6ff11258d..bfb8c1528 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -935,112 +935,6 @@ export class VideoBox extends ViewBoxAnnotatableComponent -
- -
- - {this.timeline && width > 150 && ( -
-
{formatTime(curTime)}
- - {this._fullScreen || (this.heightPercent === 100 && width > 200) ? ( -
- { - e.stopPropagation(); - this._scrubbing = true; - })} - onChange={(e: React.ChangeEvent) => this.setPlayheadTime(Number(e.target.value))} - onPointerUp={action((e: React.PointerEvent) => { - e.stopPropagation(); - this._scrubbing = false; - })} - /> -
- ) : ( -
/
- )} - -
{formatTime(this.timeline.clipDuration)}
-
- )} - -
- -
- - {!this._fullScreen && width > 300 && ( -
- -
- )} - - {!this._fullScreen && width > 300 && ( -
- -
- )} - -
{ - e.stopPropagation(); - this.toggleMute(); - }}> - -
- {width > 300 && ( - e.stopPropagation()} - onChange={(e: React.ChangeEvent) => this.setVolume(Number(e.target.value))} - /> - )} - - {!this._fullScreen && this.heightPercent !== 100 && width > 300 && ( - <> -
- -
- { - e.stopPropagation(); - }} - onChange={(e: React.ChangeEvent) => { - this.zoom(Number(e.target.value)); - }} - /> - - )} - - ); - } - // renders CollectionStackedTimeline @computed get renderTimeline() { return ( @@ -1149,6 +1043,112 @@ export class VideoBox extends ViewBoxAnnotatableComponent ); } + + @computed get UIButtons() { + const bounds = this.props.docViewPath().lastElement().getBounds(); + const width = (bounds?.right || 0) - (bounds?.left || 0); + const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0); + return ( + <> +
+ +
+ + {this.timeline && width > 150 && ( +
+
{formatTime(curTime)}
+ + {this._fullScreen || (this.heightPercent === 100 && width > 200) ? ( +
+ { + e.stopPropagation(); + this._scrubbing = true; + })} + onChange={(e: React.ChangeEvent) => this.setPlayheadTime(Number(e.target.value))} + onPointerUp={action((e: React.PointerEvent) => { + e.stopPropagation(); + this._scrubbing = false; + })} + /> +
+ ) : ( +
/
+ )} + +
{formatTime(this.timeline.clipDuration)}
+
+ )} + +
+ +
+ + {!this._fullScreen && width > 300 && ( +
+ +
+ )} + + {!this._fullScreen && width > 300 && ( +
+ +
+ )} + +
{ + e.stopPropagation(); + this.toggleMute(); + }}> + +
+ {width > 300 && ( + e.stopPropagation()} + onChange={(e: React.ChangeEvent) => this.setVolume(Number(e.target.value))} + /> + )} + + {!this._fullScreen && this.heightPercent !== 100 && width > 300 && ( + <> +
+ +
+ { + e.stopPropagation(); + }} + onChange={(e: React.ChangeEvent) => { + this.zoom(Number(e.target.value)); + }} + /> + + )} + + ); + } } VideoBox._nativeControls = false; diff --git a/src/server/ApiManagers/UploadManager.ts b/src/server/ApiManagers/UploadManager.ts index 787e331c5..0b6e18743 100644 --- a/src/server/ApiManagers/UploadManager.ts +++ b/src/server/ApiManagers/UploadManager.ts @@ -86,7 +86,7 @@ export default class UploadManager extends ApiManager { const videoId = JSON.parse(payload).videoId; const results: Upload.FileResponse[] = []; const result = await DashUploadUtils.uploadYoutube(videoId); - result && !(result.result instanceof Error) && results.push(result); + result && results.push(result); _success(res, results); resolve(); }); diff --git a/src/server/DashUploadUtils.ts b/src/server/DashUploadUtils.ts index ef7192ecc..28e26e51e 100644 --- a/src/server/DashUploadUtils.ts +++ b/src/server/DashUploadUtils.ts @@ -17,8 +17,6 @@ import { resolvedServerUrl } from './server_Initialization'; import { AcceptableMedia, Upload } from './SharedMediaTypes'; import request = require('request-promise'); import formidable = require('formidable'); -import { file } from 'jszip'; -import { csvParser } from './DataVizUtils'; const { exec } = require('child_process'); const parse = require('pdf-parse'); const ffmpeg = require('fluent-ffmpeg'); @@ -102,16 +100,41 @@ export namespace DashUploadUtils { } export function uploadYoutube(videoId: string): Promise { - console.log('UPLOAD ' + videoId); return new Promise>((res, rej) => { - exec('youtube-dl -o ' + (videoId + '.mp4') + ' https://www.youtube.com/watch?v=' + videoId + ' -f "best[filesize<50M]"', (error: any, stdout: any, stderr: any) => { - if (error) console.log(`error: ${error.message}`); - else if (stderr) console.log(`stderr: ${stderr}`); - else { - console.log(`stdout: ${stdout}`); - const data = { size: 0, path: videoId + '.mp4', name: videoId, type: 'video/mp4' }; - const file = { ...data, toJSON: () => ({ ...data, filename: data.path.replace(/.*\//, ''), mtime: null, length: 0, mime: '', toJson: () => undefined as any }) }; - res(MoveParsedFile(file, Directory.videos)); + console.log('Uploading YouTube video: ' + videoId); + exec('youtube-dl -o ' + (videoId + '.mp4') + ' ' + videoId + ' -f "bestvideo[filesize<5M]+bestaudio/bestvideo+bestaudio"', (error: any, stdout: any, stderr: any) => { + if (error) { + console.log(`error: Error: ${error.message}`); + res({ + source: { + size: 0, + path: videoId, + name: videoId, + type: '', + toJSON: () => ({ name: videoId, path: videoId }), + }, + result: { name: 'failed youtube query', message: `Could not upload YouTube video (${videoId}). Error: ${error.message}` }, + }); + } else if (stderr) { + console.log(`stderr: StdError: ${stderr}`); + res({ + source: { + size: 0, + path: videoId, + name: videoId, + type: '', + toJSON: () => ({ name: videoId, path: videoId }), + }, + result: { name: 'failed youtube query', message: `Could not upload YouTube video (${videoId}). Error: ${stderr}` }, + }); + } else { + exec('youtube-dl -o ' + (videoId + '.mp4') + ' ' + 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: videoId + '.mp4', name: videoId, 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)); + }); } }); }); @@ -352,7 +375,7 @@ export namespace DashUploadUtils { * @param suffix If the file doesn't have a suffix and you want to provide it one * to appear in the new location */ - export async function MoveParsedFile(file: formidable.File, destination: Directory, suffix: string | undefined = undefined, text?: string): Promise { + export async function MoveParsedFile(file: formidable.File, destination: Directory, suffix: string | undefined = undefined, text?: string, duration?: number): Promise { const { path: sourcePath } = file; let name = path.basename(sourcePath); suffix && (name += suffix); @@ -368,6 +391,7 @@ export namespace DashUploadUtils { agnostic: getAccessPaths(destination, name), }, rawText: text, + duration, }, }); }); diff --git a/src/server/SharedMediaTypes.ts b/src/server/SharedMediaTypes.ts index cde95526f..7db1c2dae 100644 --- a/src/server/SharedMediaTypes.ts +++ b/src/server/SharedMediaTypes.ts @@ -2,36 +2,45 @@ import { ExifData } from 'exif'; import { File } from 'formidable'; export namespace AcceptableMedia { - export const gifs = [".gif"]; - export const pngs = [".png"]; - export const jpgs = [".jpg", ".jpeg"]; - export const webps = [".webp"]; - export const tiffs = [".tiff"]; + export const gifs = ['.gif']; + export const pngs = ['.png']; + export const jpgs = ['.jpg', '.jpeg']; + export const webps = ['.webp']; + export const tiffs = ['.tiff']; export const imageFormats = [...pngs, ...jpgs, ...gifs, ...webps, ...tiffs]; - export const videoFormats = [".mov", ".mp4", ".quicktime", ".mkv", ".x-matroska;codecs=avc1"]; - export const applicationFormats = [".pdf"]; - export const audioFormats = [".wav", ".mp3", ".mpeg", ".flac", ".au", ".aiff", ".m4a", ".webm"]; + export const videoFormats = ['.mov', '.mp4', '.quicktime', '.mkv', '.x-matroska;codecs=avc1']; + export const applicationFormats = ['.pdf']; + export const audioFormats = ['.wav', '.mp3', '.mpeg', '.flac', '.au', '.aiff', '.m4a', '.webm']; } export namespace Upload { - export function isImageInformation(uploadResponse: Upload.FileInformation): uploadResponse is Upload.ImageInformation { - return "nativeWidth" in uploadResponse; + return 'nativeWidth' in uploadResponse; + } + + export function isVideoInformation(uploadResponse: Upload.FileInformation): uploadResponse is Upload.VideoInformation { + return 'duration' in uploadResponse; } export interface FileInformation { accessPaths: AccessPathInfo; rawText?: string; + duration?: number; } - export type FileResponse = { source: File, result: T | Error }; + export type FileResponse = { source: File; result: T | Error }; export type ImageInformation = FileInformation & InspectionResults; + export type VideoInformation = FileInformation & VideoResults; + export interface AccessPathInfo { - [suffix: string]: { client: string, server: string }; + [suffix: string]: { client: string; server: string }; } + export interface VideoResults { + duration: number; + } export interface InspectionResults { source: string; requestable: string; @@ -44,8 +53,7 @@ export namespace Upload { } export interface EnrichedExifData { - data: ExifData & ExifData["gps"]; + data: ExifData & ExifData['gps']; error?: string; } - -} \ No newline at end of file +} -- cgit v1.2.3-70-g09d2 From 2d8f763d763080fd52e940abb1a98b41e2da23fd Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 7 Sep 2022 14:28:44 -0400 Subject: fixed dragging stackedTimeline entries so that they appear during drag. fixed cursors for video/pdf/stackedTimeline to be a little more clear. fixed initial corner resize of pdfs. fixed videobox marquee drag when viewScale is undefined. --- src/client/util/DragManager.ts | 9 +++++++-- src/client/views/DocumentDecorations.tsx | 3 +++ src/client/views/MarqueeAnnotator.scss | 9 ++++----- src/client/views/MarqueeAnnotator.tsx | 3 +-- .../views/collections/CollectionStackedTimeline.scss | 4 +++- .../views/collections/CollectionStackedTimeline.tsx | 4 +++- .../collections/collectionFreeForm/MarqueeView.scss | 20 +++++++++----------- src/client/views/nodes/VideoBox.tsx | 4 ++-- 8 files changed, 32 insertions(+), 24 deletions(-) (limited to 'src/client/util/DragManager.ts') diff --git a/src/client/util/DragManager.ts b/src/client/util/DragManager.ts index cec158d23..664933de0 100644 --- a/src/client/util/DragManager.ts +++ b/src/client/util/DragManager.ts @@ -357,10 +357,13 @@ export namespace DragManager { let rot = 0; const docsToDrag = dragData instanceof DocumentDragData ? dragData.draggedDocuments : dragData instanceof AnchorAnnoDragData ? [dragData.dragDocument] : []; const dragElements = eles.map(ele => { + let useDim = false; if (ele?.parentElement?.parentElement?.parentElement?.className === 'collectionFreeFormDocumentView-container') { ele = ele.parentElement.parentElement.parentElement; const rotStr = ele.style.transform.replace(/.*rotate\(([-0-9.]*)deg\).*/, '$1'); if (rotStr) rot = Number(rotStr); + } else { + useDim = true; } if (rot < 0) rot += 360; if (!ele.parentNode) dragDiv.appendChild(ele); @@ -402,6 +405,8 @@ export namespace DragManager { xs.push(((0 - minx) / (maxx - minx)) * rect.width + rect.left); ys.push(((0 - miny) / (maxy - miny)) * rect.height + rect.top); scalings.push(scaling); + const width = useDim ? getComputedStyle(ele).width : ''; + const height = useDim ? getComputedStyle(ele).height : ''; Object.assign(dragElement.style, { opacity: '0.7', position: 'absolute', @@ -414,8 +419,8 @@ export namespace DragManager { borderRadius: getComputedStyle(ele).borderRadius, zIndex: globalCssVariables.contextMenuZindex, transformOrigin: '0 0', - width: '', - height: '', + width, + height, transform: `translate(${xs[0]}px, ${ys[0]}px) rotate(${rot}deg) scale(${scaling})`, }); dragLabel.style.transform = `translate(${xs[0]}px, ${ys[0] - 20}px)`; diff --git a/src/client/views/DocumentDecorations.tsx b/src/client/views/DocumentDecorations.tsx index 3589e014a..a79f727a7 100644 --- a/src/client/views/DocumentDecorations.tsx +++ b/src/client/views/DocumentDecorations.tsx @@ -476,6 +476,9 @@ export class DocumentDecorations extends React.Component<{ PanelWidth: number; P if (e.ctrlKey && !Doc.NativeHeight(docView.props.Document)) docView.toggleNativeDimensions(); if (dX !== 0 || dY !== 0 || dW !== 0 || dH !== 0) { const doc = Document(docView.rootDoc); + if (doc.nativeHeightUnfrozen && !NumCast(doc.nativeHeight)) { + doc._nativeHeight = (NumCast(doc._height) / NumCast(doc._width, 1)) * docView.nativeWidth; + } const nwidth = docView.nativeWidth; const nheight = docView.nativeHeight; let docheight = doc._height || 0; diff --git a/src/client/views/MarqueeAnnotator.scss b/src/client/views/MarqueeAnnotator.scss index c90d48a65..5c65f35e9 100644 --- a/src/client/views/MarqueeAnnotator.scss +++ b/src/client/views/MarqueeAnnotator.scss @@ -1,12 +1,11 @@ - .marqueeAnnotator-annotationBox { position: absolute; background-color: rgba(245, 230, 95, 0.616); } - .marqueeAnnotator-dragBox { - position:absolute; + position: absolute; background-color: transparent; - opacity: 0.1; -} \ No newline at end of file + opacity: 0.2; + cursor: default; +} diff --git a/src/client/views/MarqueeAnnotator.tsx b/src/client/views/MarqueeAnnotator.tsx index f90ad8bb5..d9a989309 100644 --- a/src/client/views/MarqueeAnnotator.tsx +++ b/src/client/views/MarqueeAnnotator.tsx @@ -271,8 +271,7 @@ export class MarqueeAnnotator extends React.Component { width: `${this._width}px`, height: `${this._height}px`, border: `${this._width === 0 ? '' : '2px dashed black'}`, - opacity: 0.2, - }}>
+ }}/> ); } } diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index aa8502c20..5a107d2ca 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -10,6 +10,7 @@ border-width: 0 2px 0 2px; &:hover { + cursor: default; .collectionStackedTimeline-hover { display: block; } @@ -109,14 +110,15 @@ height: 100%; width: 10px; pointer-events: all; - cursor: ew-resize; z-index: 100; } .collectionStackedTimeline-resizer { right: 0; + cursor: e-resize; } .collectionStackedTimeline-left-resizer { left: 0; + cursor: w-resize; } } diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 2543624d3..b29abf083 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -244,6 +244,7 @@ export class CollectionStackedTimeline extends CollectionSubView DocumentManager.Instance.getDocumentView(anchor)?.select(false)); } (!isClick || !wasSelecting) && (this._markerEnd = undefined); + this._timelineWrapper && (this._timelineWrapper.style.cursor = ''); }), (e, doubleTap) => { if (e.button !== 2) { @@ -561,7 +563,7 @@ export class CollectionStackedTimeline extends CollectionSubView
e.stopPropagation()} onScroll={this.setScroll} onMouseMove={e => this.isContentActive() && this.onHover(e)} diff --git a/src/client/views/collections/collectionFreeForm/MarqueeView.scss b/src/client/views/collections/collectionFreeForm/MarqueeView.scss index 41e4d6b6a..e0f5cbe5b 100644 --- a/src/client/views/collections/collectionFreeForm/MarqueeView.scss +++ b/src/client/views/collections/collectionFreeForm/MarqueeView.scss @@ -1,16 +1,14 @@ - .marqueeView { position: inherit; - top:0; - left:0; - width:100%; - height:100%; + top: 0; + left: 0; + width: 100%; + height: 100%; overflow: hidden; border-radius: inherit; user-select: none; } - .marqueeView:focus-within { overflow: hidden; } @@ -22,13 +20,13 @@ border-color: black; pointer-events: none; .marquee-legend { - bottom:-18px; - left:0; + bottom: -18px; + left: 0; position: absolute; font-size: 9; - white-space:nowrap; + white-space: nowrap; } .marquee-legend::after { - content: "Press for lasso" + content: 'Press for lasso'; } -} \ No newline at end of file +} diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index bfb8c1528..5a3594ffc 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -551,7 +551,7 @@ export class VideoBox extends ViewBoxAnnotatableComponentLoading
) : ( -
+
this._fullScreen && e.stopPropagation()}> {this._fullScreen && (
{ - if (!e.altKey && e.button === 0 && this.layoutDoc._viewScale === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) { + if (!e.altKey && e.button === 0 && NumCast(this.layoutDoc._viewScale, 1) === 1 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen].includes(Doc.ActiveTool)) { setupMoveUpEvents( this, e, -- cgit v1.2.3-70-g09d2