aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/client/views/collections/CollectionStackedTimeline.tsx2
-rw-r--r--src/client/views/collections/collectionLinear/CollectionLinearView.tsx14
-rw-r--r--src/client/views/nodes/AudioBox.tsx29
-rw-r--r--src/client/views/nodes/VideoBox.scss65
-rw-r--r--src/client/views/nodes/VideoBox.tsx120
5 files changed, 134 insertions, 96 deletions
diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx
index dca5089f4..793e01822 100644
--- a/src/client/views/collections/CollectionStackedTimeline.tsx
+++ b/src/client/views/collections/CollectionStackedTimeline.tsx
@@ -72,6 +72,8 @@ export class CollectionStackedTimeline extends CollectionSubView<
CollectionStackedTimelineProps
>(PanZoomDocument) {
@observable static SelectingRegion: CollectionStackedTimeline | undefined;
+ @observable public static CurrentlyPlaying: Doc[];
+
static RangeScript: ScriptField;
static LabelScript: ScriptField;
static RangePlayScript: ScriptField;
diff --git a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
index 77eed8bfc..70c8c9436 100644
--- a/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
+++ b/src/client/views/collections/collectionLinear/CollectionLinearView.tsx
@@ -19,6 +19,7 @@ import { DocumentLinksButton } from '../../nodes/DocumentLinksButton';
import { DocumentView } from '../../nodes/DocumentView';
import { LinkDescriptionPopup } from '../../nodes/LinkDescriptionPopup';
import { StyleProp } from '../../StyleProvider';
+import { CollectionStackedTimeline } from '../CollectionStackedTimeline';
import { CollectionSubView } from '../CollectionSubView';
import { CollectionViewType } from '../CollectionView';
import "./CollectionLinearView.scss";
@@ -231,23 +232,16 @@ export class CollectionLinearView extends CollectionSubView(LinearDocument) {
</Tooltip>
</span> : null}
- {AudioBox.CurrentlyPlaying && AudioBox.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ? <span className="bottomPopup-background">
+ {CollectionStackedTimeline.CurrentlyPlaying && CollectionStackedTimeline.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ? <span className="bottomPopup-background">
<span className="bottomPopup-text">
- Currently listening to: {AudioBox.CurrentlyPlaying.map((clip, i) =>
+ Currently playing: {CollectionStackedTimeline.CurrentlyPlaying.map((clip, i) =>
<span className="audio-title" onPointerDown={() => {
DocumentManager.Instance.jumpToDocument(clip, true);
- }}>{clip.title + (i == AudioBox.CurrentlyPlaying.length - 1 ? "" : ",")} </span>
+ }}>{clip.title + (i == CollectionStackedTimeline.CurrentlyPlaying.length - 1 ? "" : ",")} </span>
)}
</span>
</span> : null}
- {/* THIS RENDERS AUDIOBOX FOR EACH CLIP */}
- {/* {AudioBox.CurrentlyPlaying && AudioBox.CurrentlyPlaying.length != 0 && StrCast(this.layoutDoc.title) === "docked buttons" ? <div>
- <div className="currently-playing">
- {AudioBox.CurrentlyPlaying.map((clip) => this.getDisplayDoc(clip, true))}
- </div>
- </div> : null} */}
-
</div>
</div>;
}
diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx
index b9046b61e..eb7b9a773 100644
--- a/src/client/views/nodes/AudioBox.tsx
+++ b/src/client/views/nodes/AudioBox.tsx
@@ -37,7 +37,6 @@ enum media_state {
}
@observer
export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps, AudioDocument>(AudioDocument) {
- @observable public static CurrentlyPlaying: Doc[];
public static LayoutString(fieldKey: string) { return FieldView.LayoutString(AudioBox, fieldKey); }
public static SetScrubTime = action((timeInMillisFrom1970: number) => {
@@ -47,7 +46,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
public static Enabled = false;
static topControlsHeight = 30; // width of playhead
static bottomControlsHeight = 20; // height of timeline in percent of height of audioBox.
- static zoomInterval = 0.1;
@observable static _scrubTime = 0;
_dropDisposer?: DragManager.DragDropDisposer;
@@ -182,19 +180,19 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
// removes from currently playing display
@action
removeCurrentlyPlaying = () => {
- if (AudioBox.CurrentlyPlaying) {
- const index = AudioBox.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc);
- index !== -1 && AudioBox.CurrentlyPlaying.splice(index, 1);
+ if (CollectionStackedTimeline.CurrentlyPlaying) {
+ const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc);
+ index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
}
}
@action
addCurrentlyPlaying = () => {
- if (!AudioBox.CurrentlyPlaying) {
- AudioBox.CurrentlyPlaying = [];
+ if (!CollectionStackedTimeline.CurrentlyPlaying) {
+ CollectionStackedTimeline.CurrentlyPlaying = [];
}
- if (AudioBox.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc) == -1) {
- AudioBox.CurrentlyPlaying.push(this.layoutDoc.doc as Doc);
+ if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc) == -1) {
+ CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc.doc as Doc);
}
}
@@ -421,17 +419,16 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action
setVolume = (volume: number) => {
if (this._ele) {
- if (this._muted) {
- this._muted = false;
- }
this._volume = volume;
this._ele.volume = volume;
+ if (this._muted) {
+ this.toggleMute();
+ }
}
}
@action
- toggleMute = (e: React.PointerEvent) => {
- e.stopPropagation();
+ toggleMute = () => {
if (this._ele) {
this._muted = !this._muted;
this._ele.muted = this._muted;
@@ -505,8 +502,8 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<div className="controls-right">
<div className="audiobox-button"
title={this._muted ? "unmute" : "mute"}
- onPointerDown={this.toggleMute}>
- <FontAwesomeIcon icon={this._muted || this._volume == 0 ? "volume-mute" : "volume-up"} />
+ 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}
className="toolbar-slider volume"
diff --git a/src/client/views/nodes/VideoBox.scss b/src/client/views/nodes/VideoBox.scss
index 35f5a7e65..3cf10a033 100644
--- a/src/client/views/nodes/VideoBox.scss
+++ b/src/client/views/nodes/VideoBox.scss
@@ -86,27 +86,42 @@
background-color: $dark-gray;
color: white;
border-radius: 100px;
+ top: calc(100% - 20px);
left: 50%;
- transform: translate(-50%, -150%);
- transition: top 0.5s, opacity 0.2s, visibility 0s;
- height: 5%;
- width: 80%;
+ transform: translate(-50%, -100%);
+
+ transition: top 0.5s, width 0.5s, opacity 0.2s, visibility 0s;
+ height: 120px;
+ padding: 0 20px;
.timecode-controls {
display: flex;
flex-direction: row;
+ align-items: center;
+ justify-content: center;
margin: 0 5px;
+ flex-grow: 2;
+ font-size: 32px;
.timecode {
margin: 0 5px;
}
+
+ .timeline-slider {
+ margin: 0 20px 0 20px;
+ flex-grow: 2;
+ }
+ }
+
+ .toolbar-slider.volume, .toolbar-slider.zoom {
+ width: 100px;
}
.videobox-button {
margin: 5px;
cursor: pointer;
- width: 30px;
- height: 30px;
+ width: 70px;
+ height: 70px;
border-radius: 50%;
background: $dark-gray;
display: flex;
@@ -118,10 +133,12 @@
}
svg {
- width: 20px;
+ width: 40px;
+ height: 40px;
}
}
}
+
.videoBox-time,
.videoBox-snapshot,
.videoBox-timelineButton,
@@ -146,6 +163,22 @@
}
}
+.videoBox-content-fullScreen, .videoBox-content-fullScreen-interactive {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ &:hover {
+ .videoBox-ui {
+ opacity: 0;
+ }
+ }
+
+ .videoBox-ui:hover {
+ opacity: 1;
+ }
+}
+
video::-webkit-media-controls {
display: none !important;
}
@@ -153,7 +186,7 @@ video::-webkit-media-controls {
input[type="range"] {
-webkit-appearance: none;
background: none;
- margin: 5px;
+ margin: 10px;
}
input[type="range"]:focus {
@@ -162,25 +195,21 @@ input[type="range"]:focus {
input[type="range"]::-webkit-slider-runnable-track {
width: 100%;
- height: 6px;
+ height: 20px;
cursor: pointer;
box-shadow: 0;
background: $light-gray;
- border-radius: 3px;
+ border-radius: 20px;
}
input[type="range"]::-webkit-slider-thumb {
box-shadow: 0;
border: 0;
- height: 10px;
- width: 10px;
- border-radius: 10px;
+ height: 26px;
+ width: 26px;
+ border-radius: 20px;
background: $medium-blue;
cursor: pointer;
-webkit-appearance: none;
- margin-top: -2px;
-}
-
-.toolbar-slider.volume {
- width: 50px;
+ margin-top: -3px;
} \ No newline at end of file
diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx
index bd9423d74..c8e0cdb66 100644
--- a/src/client/views/nodes/VideoBox.tsx
+++ b/src/client/views/nodes/VideoBox.tsx
@@ -117,6 +117,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
}
componentWillUnmount() {
+ this.removeCurrentlyPlaying();
this.Pause();
Object.keys(this._disposers).forEach(d => this._disposers[d]?.());
}
@@ -155,6 +156,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@action public Pause = (update: boolean = true) => {
this._playing = false;
+ this.removeCurrentlyPlaying();
try {
update && this.player?.pause();
update && this._audioPlayer?.pause();
@@ -178,6 +180,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
else {
this._fullScreen = true;
this.player && this._contentRef && this._contentRef.requestFullscreen();
+
}
try {
this._youtubePlayer && this.props.addDocTab(this.rootDoc, "add");
@@ -326,9 +329,9 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
const classname = "videoBox-content" + (this._fullScreen ? "-fullScreen" : "") + interactive;
return !field ? <div key="loading">Loading</div> :
<div className="videoBox-contentContainer" key="container" style={{ mixBlendMode: "multiply" }}>
- <div className={classname} ref={this.setContentRef}>
+ <div className={classname} ref={this.setContentRef} onPointerDown={(e) => this._fullScreen && e.stopPropagation()}>
{this.uIButtons}
- <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef}
+ <video key="video" autoPlay={this._screenCapture} ref={this.setVideoRef} style={this._fullScreen ? this.fullScreenSize() : {}}
onCanPlay={this.videoLoad}
controls={VideoBox._nativeControls}
onPlay={() => this.Play()}
@@ -436,6 +439,24 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
(e: PointerEvent) => this.layoutDoc._currentTimecode = 0);
}
+ @action
+ removeCurrentlyPlaying = () => {
+ if (CollectionStackedTimeline.CurrentlyPlaying) {
+ const index = CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc);
+ index !== -1 && CollectionStackedTimeline.CurrentlyPlaying.splice(index, 1);
+ }
+ }
+
+ @action
+ addCurrentlyPlaying = () => {
+ if (!CollectionStackedTimeline.CurrentlyPlaying) {
+ CollectionStackedTimeline.CurrentlyPlaying = [];
+ }
+ if (CollectionStackedTimeline.CurrentlyPlaying.indexOf(this.layoutDoc.doc as Doc) == -1) {
+ CollectionStackedTimeline.CurrentlyPlaying.push(this.layoutDoc.doc as Doc);
+ }
+ }
+
@computed get youtubeContent() {
this._youtubeIframeId = VideoBox._youtubeIframeCounter++;
this._youtubeContentCreated = this._forceCreateYouTubeIFrame ? true : true;
@@ -472,9 +493,11 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
this.player.play();
this._audioPlayer?.play();
this._playing = true;
+ this.addCurrentlyPlaying();
this._playRegionTimer = setTimeout(
() => {
if (fullPlay) this._finished = true;
+ else this.removeCurrentlyPlaying();
this.Pause();
}, this._playRegionDuration * 1000);
} else {
@@ -512,20 +535,33 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
if (this.player) {
this._volume = volume;
this.player.volume = volume;
+ if (this._muted) {
+ this.toggleMute();
+ }
}
}
@action
- toggleMute = (e: React.PointerEvent) => {
- e.stopPropagation();
- console.log("click");
+ toggleMute = () => {
if (this.player) {
- console.log("audio exists");
this._muted = !this._muted;
this.player.muted = this._muted;
}
}
+ fullScreenSize() {
+ if (this._videoRef && this._videoRef.videoHeight / this._videoRef.videoWidth > 1) {
+ return { height: "100%" }
+ }
+ else {
+ return { width: "100%" }
+ }
+ }
+
+ zoom = (zoom: number) => {
+ this.timeline?.setZoom(zoom);
+ }
+
playLink = (doc: Doc) => {
const startTime = Math.max(0, (this._stackedTimeline.current?.anchorStart(doc) || 0));
const endTime = this.timeline?.anchorEnd(doc);
@@ -571,42 +607,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
@computed get uIButtons() {
const curTime = (this.layoutDoc._currentTimecode || 0) - (this.timeline?.clipStart || 0);
- // const nonNativeControls = [
- // <Tooltip title={<div className="dash-tooltip">{"playback"}</div>} key="play" placement="bottom">
- // <div className="videoBox-play" onPointerDown={this.onPlayDown} >
- // <FontAwesomeIcon icon={this._playing ? "pause" : "play"} size="lg" />
- // </div>
- // </Tooltip>,
- // <Tooltip title={<div className="dash-tooltip">{"timecode"}</div>} key="time" placement="bottom">
- // <div className="videoBox-time" onPointerDown={this.onResetDown} >
- // <span>{formatTime(curTime)}</span>
- // <span style={{ fontSize: 8 }}>{" " + Math.floor((curTime - Math.trunc(curTime)) * 100).toString().padStart(2, "0")}</span>
- // </div>
- // </Tooltip>,
- // <Tooltip title={<div className="dash-tooltip">{"view full screen"}</div>} key="full" placement="bottom">
- // <div className="videoBox-full" onPointerDown={this.FullScreen}>
- // <FontAwesomeIcon icon="expand" size="lg" />
- // </div>
- // </Tooltip>];
- // return <div className="videoBox-ui">
- // {[...(VideoBox._nativeControls ? [] : nonNativeControls),
- // <Tooltip title={<div className="dash-tooltip">{"snapshot current frame"}</div>} key="snap" placement="bottom">
- // <div className="videoBox-snapshot" onPointerDown={this.onSnapshotDown} >
- // <FontAwesomeIcon icon="camera" size="lg" />
- // </div>
- // </Tooltip>,
- // <Tooltip title={<div className="dash-tooltip">{"show annotation timeline"}</div>} key="timeline" placement="bottom">
- // <div className="videoBox-timelineButton" onPointerDown={this.onTimelineHdlDown}>
- // <FontAwesomeIcon icon="eye" size="lg" />
- // </div>
- // </Tooltip>,
- // <Tooltip title={<div className="dash-tooltip">{this.timeline?.IsTrimming !== TrimScope.None ? "finish trimming" : "start trim"}</div>} key="trim" placement="bottom">
- // <div className="videoBox-timelineButton" onPointerDown={this.onClipPointerDown}>
- // <FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} size="lg" />
- // </div>
- // </Tooltip>,]}
- // </div>;
- return <div className="videoBox-ui" style={{ top: `calc(100% - 10px)` }}>
+ return <div className="videoBox-ui" style={this._fullScreen || this.heightPercent == 100 ? { fontSize: "40px", minWidth: "80%" } : {}}>
<div className="videobox-button"
title={this._playing ? "play" : "pause"}
onPointerDown={this.onPlayDown}>
@@ -618,13 +619,16 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
{formatTime(curTime)}
</div>
- <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)) }}
- />
- </div>
+ {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)) }}
+ />
+ </div>
+ :
+ <div>/</div>}
<div className="timecode-end">
{formatTime(this.timeline.clipDuration)}
@@ -649,16 +653,28 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp
<FontAwesomeIcon icon={this.timeline?.IsTrimming !== TrimScope.None ? "check" : "cut"} />
</div>}
- <div className="videobox-button"
+ <div className="videobox-button show-slider"
title={this._muted ? "unmute" : "mute"}
- onPointerDown={this.toggleMute}>
- <FontAwesomeIcon icon={this._muted || this._volume == 0 ? "volume-mute" : "volume-up"} />
+ 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}
className="toolbar-slider volume"
onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.setVolume(Number(e.target.value)) }}
/>
+
+ {!this._fullScreen && this.heightPercent != 100 &&
+ <>
+ <div className="videobox-button" title="zoom">
+ <FontAwesomeIcon icon="search-plus" />
+ </div>
+ <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor}
+ className="toolbar-slider zoom"
+ onPointerDown={(e: React.PointerEvent) => { e.stopPropagation(); }}
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) => { this.zoom(Number(e.target.value)); }}
+ />
+ </>}
</div>
}
@computed get renderTimeline() {