diff options
-rw-r--r-- | src/client/views/AudioWaveform.scss | 2 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackedTimeline.scss | 13 | ||||
-rw-r--r-- | src/client/views/collections/CollectionStackedTimeline.tsx | 19 | ||||
-rw-r--r-- | src/client/views/nodes/AudioBox.tsx | 6 | ||||
-rw-r--r-- | src/client/views/nodes/VideoBox.tsx | 40 |
5 files changed, 38 insertions, 42 deletions
diff --git a/src/client/views/AudioWaveform.scss b/src/client/views/AudioWaveform.scss index e20434a25..6cbd1759a 100644 --- a/src/client/views/AudioWaveform.scss +++ b/src/client/views/AudioWaveform.scss @@ -1,7 +1,7 @@ .audioWaveform { position: relative; width: 100%; - height: 100%; + height: 200%; overflow: hidden; z-index: -1000; bottom: 0; diff --git a/src/client/views/collections/CollectionStackedTimeline.scss b/src/client/views/collections/CollectionStackedTimeline.scss index 580cbccda..f9cf5cd4e 100644 --- a/src/client/views/collections/CollectionStackedTimeline.scss +++ b/src/client/views/collections/CollectionStackedTimeline.scss @@ -6,7 +6,6 @@ overflow-y: hidden; border: none; background-color: $white; - border: 2px solid $dark-gray; border-width: 0 2px 0 2px; &:hover { @@ -16,7 +15,7 @@ } } -.timeline-container:hover + .videoBox-thumbnail { +.timeline-container:hover + .timeline-hoverUI { display: block; } @@ -29,6 +28,7 @@ background: $off-white; z-index: 1000; height: 100%; + overflow: hidden; .collectionStackedTimeline-trim-shade { position: absolute; @@ -127,10 +127,17 @@ } } -.videoBox-thumbnail { +.timeline-hoverUI { position: absolute; z-index: 10000; transform: translate(-50%, 100%); height: 100%; display: none; + + .hoverTime { + color: $dark-gray; + text-align: center; + transform: translate(0, -17px); + font-weight: bold; + } }
\ No newline at end of file diff --git a/src/client/views/collections/CollectionStackedTimeline.tsx b/src/client/views/collections/CollectionStackedTimeline.tsx index 2b78f5764..850aa5dbe 100644 --- a/src/client/views/collections/CollectionStackedTimeline.tsx +++ b/src/client/views/collections/CollectionStackedTimeline.tsx @@ -326,13 +326,13 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack const rect = this._timeline?.getBoundingClientRect(); const clientX = e.clientX; if (rect) { - this._hoverTime = Math.min(this.toTimeline(clientX - rect.x, rect.width), this.clipEnd); + this._hoverTime = this.toTimeline(clientX - rect.x, rect.width); if (this.dataDoc.thumbnails) { const nearest = Math.floor(this._hoverTime / this.props.rawDuration * VideoBox.numThumbnails); const thumbnails = Cast(this.dataDoc.thumbnails, listSpec("string"), []); - const src = new ImageField(thumbnails[nearest]).url.href.replace(".png", "_s.png"); - this._thumbnail = src; - console.log(src); + const imgField = thumbnails && thumbnails.length > 0 ? new ImageField(thumbnails[nearest]) : new ImageField(""); + const src = imgField && imgField.url.href ? imgField.url.href.replace(".png", "_s.png") : ""; + this._thumbnail = src ? src : undefined; } } } @@ -576,7 +576,7 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack dictationHeight = () => (this.props.PanelHeight() * (100 - this.dictationHeightPercent)) / 100; @computed get timelineContentHeight() { return this.props.PanelHeight() * this.dictationHeightPercent / 100; } - @computed get timelineContentWidth() { return this.props.PanelWidth() * this.zoomFactor - 4; } // subtract size of container border + @computed get timelineContentWidth() { return this.props.PanelWidth() * this.zoomFactor; } // subtract size of container border dictationScreenToLocalTransform = () => this.props.ScreenToLocalTransform().translate(0, -this.timelineContentHeight); @@ -723,12 +723,12 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack /> {/* {this.renderDictation} */} - <div + { /* check time to prevent weird div overflow */ this._hoverTime < this.clipDuration && <div className="collectionStackedTimeline-hover" style={{ left: `${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%`, }} - /> + />} <div className="collectionStackedTimeline-current" @@ -772,7 +772,10 @@ export class CollectionStackedTimeline extends CollectionSubView<CollectionStack )} </div> </div> - {this._thumbnail && <img className="videoBox-thumbnail" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }} src={this._thumbnail} />} + <div className="timeline-hoverUI" style={{ left: `calc(${((this._hoverTime - this.clipStart) / this.clipDuration) * 100}%` }}> + {this._thumbnail && <img className="videoBox-thumbnail" src={this._thumbnail} />} + <div className="hoverTime">{formatTime(this._hoverTime)}</div> + </div> </div >); } } diff --git a/src/client/views/nodes/AudioBox.tsx b/src/client/views/nodes/AudioBox.tsx index 59c37753a..1d06f368f 100644 --- a/src/client/views/nodes/AudioBox.tsx +++ b/src/client/views/nodes/AudioBox.tsx @@ -581,7 +581,9 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp <div className="timecode-current"> {this.timeline && formatTime(Math.round(NumCast(this.layoutDoc._currentTimecode) - NumCast(this.timeline.clipStart)))} </div> - {!this.miniPlayer && + {this.miniPlayer ? + <div>/</div> + : <div className="bottom-controls-middle"> <FontAwesomeIcon icon="search-plus" /> <input type="range" step="0.1" min="1" max="5" value={this.timeline?._zoomFactor} @@ -591,8 +593,6 @@ export class AudioBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp /> </div>} - {this.miniPlayer && <div>/</div>} - <div className="timecode-duration"> {this.timeline && formatTime(Math.round(this.timeline.clipDuration))} </div> diff --git a/src/client/views/nodes/VideoBox.tsx b/src/client/views/nodes/VideoBox.tsx index 80ff19519..ef3b0d105 100644 --- a/src/client/views/nodes/VideoBox.tsx +++ b/src/client/views/nodes/VideoBox.tsx @@ -32,18 +32,6 @@ import { Image } from "wikijs"; import { List } from "../../../fields/List"; const path = require('path'); - -//TODO mj: one option for doing thumbnail previews of video in timeline? -/** - * 1. set vref in videobox - * 2. when vref is set immediately process video to extract ~50 thumbnails - * (^^ would make more sense to do this when video is initially uploaded) - * 3. upload each file to server using convertDataURI and save list of URLs as field on doc - * 4. in CST onHover, set hover time - * 5. use hover time to figure out index of nearest thumbnail - * 6. get URL of image and use source to paint canvas accordingly - */ - /** * VideoBox * Main component: VideoBox.tsx @@ -375,12 +363,13 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp // extracts video thumbnails and saves them as field of doc getVideoThumbnails = () => { - this.layoutDoc.cloneO const video = document.createElement('video'); - const thumbnails: string[] = []; + const thumbnailPromises: Promise<any>[] = []; + video.onloadedmetadata = () => { video.currentTime = 0; }; + video.onseeked = () => { const canvas = document.createElement('canvas'); canvas.height = video.videoHeight; @@ -391,18 +380,15 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp const retitled = StrCast(this.rootDoc.title).replace(/[ -\.:]/g, ""); const encodedFilename = encodeURIComponent("thumbnail" + retitled + "_" + video.currentTime.toString().replace(/\./, "_")); const filename = basename(encodedFilename); - VideoBox.convertDataUri(imgUrl, filename).then((returnedFilename: string) => { - returnedFilename && thumbnails.push(returnedFilename); - const newTime = video.currentTime + video.duration / VideoBox.numThumbnails; - if (newTime < video.duration) { - video.currentTime = newTime; - console.log(thumbnails.length); - } - else { - this.dataDoc.thumbnails = new List<string>(thumbnails); - } - }); - }; + thumbnailPromises.push(VideoBox.convertDataUri(imgUrl, filename)); + const newTime = video.currentTime + video.duration / (VideoBox.numThumbnails - 1); + if (newTime < video.duration) { + video.currentTime = newTime; + } + else { + Promise.all(thumbnailPromises).then(thumbnails => { this.dataDoc.thumbnails = new List<string>(thumbnails); }); + } + } const field = Cast(this.dataDoc[this.fieldKey], VideoField); field && (video.src = field.url.href); @@ -421,7 +407,7 @@ export class VideoBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProp this._disposers.reactionDisposer = reaction(() => NumCast(this.layoutDoc._currentTimecode), time => !this._playing && (vref.currentTime = time), { fireImmediately: true }); - !this.dataDoc.thumbnails && this.getVideoThumbnails(); + (!this.dataDoc.thumbnails || this.dataDoc.thumbnails.length != VideoBox.numThumbnails) && this.getVideoThumbnails(); } } |