aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/VideoBox.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/VideoBox.tsx')
-rw-r--r--src/client/views/nodes/VideoBox.tsx75
1 files changed, 48 insertions, 27 deletions
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index 913123cda..ac848fc2a 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -4,10 +4,10 @@ import { action, computed, IReactionDisposer, observable, ObservableMap, reactio
import { observer } from "mobx-react";
import { basename } from "path";
import * as rp from 'request-promise';
-import { Doc, DocListCast } from "../../../fields/Doc";
+import { Doc, DocListCast, HeightSym, WidthSym } from "../../../fields/Doc";
import { InkTool } from "../../../fields/InkField";
import { Cast, NumCast, StrCast } from "../../../fields/Types";
-import { AudioField, VideoField } from "../../../fields/URLField";
+import { AudioField, ImageField, VideoField } from "../../../fields/URLField";
import { emptyFunction, formatTime, OmitKeys, returnFalse, returnOne, setupMoveUpEvents, Utils } from "../../../Utils";
import { Docs, DocUtils } from "../../documents/Documents";
import { DocumentType } from "../../documents/DocumentTypes";
@@ -46,13 +46,22 @@ const path = require('path');
@observer
export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps>() {
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(VideoBox, fieldKey); }
- static async convertDataUri(imageUri: string, returnedFilename: string) {
+ /**
+ * Uploads an image buffer to the server and stores with specified filename. by default the image
+ * is stored at multiple resolutions each retrieved by using the filename appended with _o, _s, _m, _l (indicating original, small, medium, or large)
+ * @param imageUri the bytes of the image
+ * @param returnedFilename the base filename to store the image on the server
+ * @param nosuffix optionally suppress creating multiple resolution images
+ */
+ public static async convertDataUri(imageUri: string, returnedFilename: string, nosuffix = false, replaceRootFilename?: string) {
try {
const posting = Utils.prepend("/uploadURI");
const returnedUri = await rp.post(posting, {
body: {
uri: imageUri,
- name: returnedFilename
+ name: returnedFilename,
+ nosuffix,
+ replaceRootFilename
},
json: true,
});
@@ -138,6 +147,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// plays video
@action public Play = (update: boolean = true) => {
+ if (this._playRegionTimer) return;
this._playing = true;
const eleTime = this.player?.currentTime || 0;
if (this.timeline) {
@@ -194,7 +204,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// toggles video full screen
@action public FullScreen = () => {
- if (document.fullscreenElement == this._contentRef) {
+ if (document.fullscreenElement === this._contentRef) {
this._fullScreen = false;
this.player && this._contentRef && document.exitFullscreen();
}
@@ -212,16 +222,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// creates and links snapshot photo of current video frame
- @action public Snapshot(downX?: number, downY?: number) {
+ @action public Snapshot = (downX?: number, downY?: number, cb?: (filename: string, x: number | undefined, y: number | undefined) => void) => {
const width = NumCast(this.layoutDoc._width);
const canvas = document.createElement('canvas');
canvas.width = 640;
canvas.height = 640 * Doc.NativeHeight(this.layoutDoc) / (Doc.NativeWidth(this.layoutDoc) || 1);
const ctx = canvas.getContext('2d');//draw image to canvas. scale to target dimensions
if (ctx) {
- // ctx.rect(0, 0, canvas.width, canvas.height);
- // ctx.fillStyle = "blue";
- // ctx.fill();
this._videoRef && ctx.drawImage(this._videoRef, 0, 0, canvas.width, canvas.height);
}
@@ -251,10 +258,19 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const encodedFilename = encodeURIComponent("snapshot" + retitled + "_" + (this.layoutDoc._currentTimecode || 0).toString().replace(/\./, "_"));
const filename = basename(encodedFilename);
VideoBox.convertDataUri(dataUrl, filename).then((returnedFilename: string) =>
- returnedFilename && this.createRealSummaryLink(returnedFilename, downX, downY));
+ returnedFilename && (cb ?? this.createRealSummaryLink)(returnedFilename, downX, downY));
}
}
+ updateIcon = () => {
+ const makeIcon = (returnedfilename: string) => {
+ this.dataDoc.icon = new ImageField(returnedfilename);
+ this.dataDoc["icon-nativeWidth"] = this.layoutDoc[WidthSym]();
+ this.dataDoc["icon-nativeHeight"] = this.layoutDoc[HeightSym]();
+ };
+ this.Snapshot(undefined, undefined, makeIcon);
+ }
+
// creates link for snapshot
createRealSummaryLink = (imagePath: string, downX?: number, downY?: number) => {
const url = !imagePath.startsWith("/") ? Utils.CorsProxy(imagePath) : imagePath;
@@ -291,7 +307,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (Number.isFinite(this.player!.duration)) {
this.rawDuration = this.player!.duration;
}
- })
+ });
// updates video time
@@ -325,7 +341,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
setContentRef = (cref: HTMLDivElement | null) => {
this._contentRef = cref;
if (cref) {
- cref.onfullscreenchange = action((e) => this._fullScreen = (document.fullscreenElement == cref));
+ cref.onfullscreenchange = action((e) => this._fullScreen = (document.fullscreenElement === cref));
}
}
@@ -477,7 +493,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
removeCurrentlyPlaying = () => {
if (CollectionStackedTimeline.CurrentlyPlaying) {
- const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc);
+ const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc);
index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
}
}
@@ -488,8 +504,8 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (!CollectionStackedTimeline.CurrentlyPlaying) {
CollectionStackedTimeline.CurrentlyPlaying = [];
}
- if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc) == -1) {
- CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc.doc as Doc);
+ if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc) === -1) {
+ CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc);
}
}
@@ -543,6 +559,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// removes from currently playing if playback has reached end of range marker
else this.removeCurrentlyPlaying();
this.Pause();
+ this._playRegionTimer = undefined;
}, playRegionDuration * 1000);
} else {
this.Pause();
@@ -605,10 +622,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// stretches vertically or horizontally depending on video orientation so video fits full screen
fullScreenSize() {
if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
- return { height: "100%" }
+ return { height: "100%" };
}
else {
- return { width: "100%" }
+ return { width: "100%" };
}
}
@@ -679,7 +696,10 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// renders video controls
@computed get uIButtons() {
const curTime = NumCast(this.layoutDoc._currentTimecode) - (this.timeline?.clipStart || 0);
- return <div className="videoBox-ui" style={this._fullScreen || this.heightPercent == 100 ? { fontSize: "40px", minWidth: "80%" } : {}}>
+ const scaling = (this.props.scaling?.() || 1);
+ return <div className="videoBox-ui" style={{
+ transform: `scale(${1 / scaling})`, width: `${100 * scaling}%`, bottom: 20 / scaling
+ }}>
<div className="videobox-button"
title={this._playing ? "play" : "pause"}
onPointerDown={this.onPlayDown}>
@@ -691,12 +711,12 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{formatTime(curTime)}
</div>
- {this._fullScreen || this.heightPercent == 100 ?
+ {this._fullScreen || this.heightPercent === 100 ?
<div className="timeline-slider">
<input type="range" step="0.1" min={this.timeline.clipStart} max={this.timeline.clipEnd} value={curTime}
className="toolbar-slider time-progress"
onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.setPlayheadTime(Number(e.target.value)) }}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setPlayheadTime(Number(e.target.value))}
/>
</div>
:
@@ -730,13 +750,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
onPointerDown={(e) => { e.stopPropagation(); this.toggleMute(); }}>
<FontAwesomeIcon icon={this._muted ? "volume-mute" : "volume-up"} />
</div>
- <input type="range" step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
+ <input type="range" style={{ width: `min(25%, 50px)` }} step="0.1" min="0" max="1" value={this._muted ? 0 : this._volume}
className="toolbar-slider volume"
- onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
- onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.setVolume(Number(e.target.value)) }}
+ onPointerDown={(e: React.PointerEvent) => e.stopPropagation()}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => this.setVolume(Number(e.target.value))}
/>
- {!this._fullScreen && this.heightPercent != 100 &&
+ {!this._fullScreen && this.heightPercent !== 100 &&
<>
<div className="videobox-button" title="zoom">
<FontAwesomeIcon icon="search-plus" />
@@ -747,7 +767,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
/>
</>}
- </div>
+ </div>;
}
// renders CollectionStackedTimeline
@@ -785,12 +805,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
return <div className="videoBox-annotationLayer" style={{ transition: this.transition, height: `${this.heightPercent}%` }} ref={this._annotationLayer} />;
}
+ savedAnnotations = () => this._savedAnnotations;
render() {
const borderRad = this.props.styleProvider?.(this.layoutDoc, this.props, StyleProp.BorderRounding);
const borderRadius = borderRad?.includes("px") ? `${Number(borderRad.split("px")[0]) / this.scaling()}px` : borderRad;
return (<div className="videoBox" onContextMenu={this.specificContextMenu} ref={this._mainCont}
style={{
- pointerEvents: this.props.layerProvider?.(this.layoutDoc) === false ? "none" : undefined,
+ pointerEvents: this.layoutDoc._lockedPosition ? "none" : undefined,
borderRadius,
overflow: this.props.docViewPath?.().slice(-1)[0].fitWidth ? "auto" : undefined
}} onWheel={e => { e.stopPropagation(); e.preventDefault(); }}>
@@ -832,7 +853,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
containerOffset={this.marqueeOffset}
addDocument={this.addDocWithTimecode}
finishMarquee={this.finishMarquee}
- savedAnnotations={this._savedAnnotations}
+ savedAnnotations={this.savedAnnotations}
annotationLayer={this._annotationLayer.current}
mainCont={this._mainCont.current}
/>}