From c7e6a4a53afeffc680a5a1c430b647c0eb66a404 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Sat, 28 Dec 2024 23:55:01 +0100 Subject: [PATCH 01/12] [playlists] Include pictures to the main progress bar --- .../pages/playlists/PlaylistPlayer.vue | 18 ++-- src/components/previews/VideoProgress.vue | 97 +++++++++++++++---- 2 files changed, 90 insertions(+), 25 deletions(-) diff --git a/src/components/pages/playlists/PlaylistPlayer.vue b/src/components/pages/playlists/PlaylistPlayer.vue index d3d3068b50..a7e983322f 100644 --- a/src/components/pages/playlists/PlaylistPlayer.vue +++ b/src/components/pages/playlists/PlaylistPlayer.vue @@ -316,6 +316,7 @@ ref="video-progress" class="video-progress pull-bottom" :annotations="annotations" + :entity-list="entityList" :fps="fps" :frame-duration="frameDuration" :is-playlist="true" @@ -329,14 +330,13 @@ :playlist-duration="playlistDuration" :playlist-progress="playlistProgress" :playlist-shot-position="playlistShotPosition" - :entity-list="entityList" @start-scrub="onScrubStart" @end-scrub="onScrubEnd" @progress-changed="onProgressChanged" @progress-playlist-changed="onProgressPlaylistChanged" @handle-in-changed="onHandleInChanged" @handle-out-changed="onHandleOutChanged" - v-show="isCurrentPreviewMovie && playlist.id && !isAddingEntity" + v-show="playlist.id && !isAddingEntity" />
/ {{ (nbFrames + '').padStart(3, '0') }} - - ) + )
@@ -1468,6 +1467,7 @@ export default { if (this.isCurrentPreviewPicture) { this.framesSeenOfPicture = 0 this.playPicture() + this.updateProgressBar() } } }, @@ -2002,6 +2002,7 @@ export default { this.progress.updateProgressBar(this.frameNumber + 1) } if (this.playlistDuration && !this.isFullMode && this.currentEntity) { + console.log('updateProgressBar') this.playlistProgress = this.currentEntity.start_duration + this.frameNumber / this.fps } @@ -2020,17 +2021,22 @@ export default { let playlistDuration = 0 let currentFrame = 0 this.entityList.forEach((entity, index) => { - this.framesPerImage[index] = + const defaultNbFrames = entity.preview_nb_frames || DEFAULT_NB_FRAMES_PICTURE + this.framesPerImage[index] = defaultNbFrames const nbFrames = Math.round( (entity.preview_file_duration || 0) * this.fps - ) + ) || defaultNbFrames + console.log(entity.name, nbFrames) entity.start_duration = (currentFrame + 1) / this.fps for (let i = 0; i < nbFrames; i++) { this.playlistShotPosition[currentFrame + i] = { index, name: entity.name, + extension: entity.preview_file_extension, start: entity.start_duration, + width: entity.preview_file_width, + height: entity.preview_file_height, id: entity.preview_file_id } } diff --git a/src/components/previews/VideoProgress.vue b/src/components/previews/VideoProgress.vue index 29c14a40b4..ba70b6d3df 100644 --- a/src/components/previews/VideoProgress.vue +++ b/src/components/previews/VideoProgress.vue @@ -179,6 +179,10 @@ export default { default: () => [], type: Array }, + entityList: { + default: () => [], + type: Array + }, fps: { default: 0, type: Number @@ -230,10 +234,6 @@ export default { playlistShotPosition: { default: () => {}, type: Object - }, - entityList: { - default: () => [], - type: Array } }, @@ -313,14 +313,30 @@ export default { frameNumberStyle() { const frameHeight = 100 const height = frameHeight + 30 - const frameWidth = Math.ceil(frameHeight * this.videoRatio) + let frameWidth = 150 + console.log('ok', this.isPlaylistHover) + if (this.isPlaylistHover) { + const preview = this.playlistShotPosition[this.hoverFrame] + if (preview.extension === 'mp4') { + const ratio = preview.width / preview.height + frameWidth = Math.ceil(frameHeight * ratio) + console.log('cool', ratio, preview) + } else if ( + this.isPlaylistHover && + preview.extension === 'png' + ) { + frameWidth = 150 + } + } else { + frameWidth = Math.ceil(frameHeight * this.videoRatio) + } const width = frameWidth + 10 const left = Math.min( Math.max(this.frameNumberLeftPosition - frameWidth / 2, 0), this.width - frameWidth - 10 ) const top = this.isFullScreen - ? `-${height + 30}px` + ? `-${height + 10}px` : this.isPlaylist ? '16px' : '0px' @@ -535,8 +551,10 @@ export default { getFrameBackgroundStyle(frame) { if (!frame) return {} let previewId = this.previewId + let extension = null if (this.isPlaylistHover) { previewId = this.playlistShotPosition[frame].id + extension = this.playlistShotPosition[frame].extension frame = frame - this.playlistShotPosition[frame].start * this.fps } else { frame = frame - 1 @@ -548,13 +566,42 @@ export default { const frameY = Math.floor(frame / 8) const frameHeight = 100 const frameWidth = Math.ceil(frameHeight * this.videoRatio) - const tilePath = `/api/movies/tiles/preview-files/${previewId}.png` - return { - background: `url(${tilePath})`, - 'background-position': `-${frameX * frameWidth}px -${ - frameY * frameHeight - }px`, - width: `${frameWidth}px` + + if (!this.isPlaylistHover) { + console.log('not playlist') + const tilePath = `/api/movies/tiles/preview-files/${previewId}.png` + return { + background: `url(${tilePath})`, + 'background-position': `-${frameX * frameWidth}px -${ + frameY * frameHeight + }px`, + width: `${frameWidth}px` + } + } else { + if (extension === 'png') { + const tilePath = `/api/pictures/thumbnails/preview-files/${previewId}.png` + console.log('png') + return { + background: `url(${tilePath})`, + 'background-position': '0 0', + width: '150px' + } + } else if (extension === 'mp4') { + console.log('mp4', this.extension) + const tilePath = `/api/movies/tiles/preview-files/${previewId}.png` + return { + background: `url(${tilePath})`, + 'background-position': `-${frameX * frameWidth}px -${ + frameY * frameHeight + }px`, + width: `${frameWidth}px` + } + } else { + console.log(extension) + return { + background: 'transparent' + } + } } }, @@ -593,7 +640,14 @@ export default { }, getEntityWidth(entity) { - const ratio = entity.preview_file_duration / this.playlistDuration + let ratio = 0 + // console.log(this.playlistDuration, entity.preview_file_duration) + if (entity.preview_file_extension === 'mp4') { + ratio = entity.preview_file_duration / this.playlistDuration + } else { + // console.log(this.frameDuration, this.playlistDuration) + ratio = 2 * this.fps * this.frameDuration / this.playlistDuration + } return ratio * 100 }, @@ -618,11 +672,14 @@ export default { immediate: true, handler() { if (this.previewId) { - this.isTileLoading = true - const img = new Image() - img.src = this.tilePath - img.onload = () => { - this.isTileLoading = false + const preview = this.playlistShotPosition[this.hoverFrame] + if (preview.extension === 'mp4') { + this.isTileLoading = true + const img = new Image() + img.src = this.tilePath + img.onload = () => { + this.isTileLoading = false + } } } } @@ -698,6 +755,8 @@ progress { top: -300px; width: 110px; z-index: 800; + display: flex; + flex-direction: column; .frame-tile { display: inline-block; From a04fdc6fe5b3e9a971abc2804ae27e52d3e9d38b Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Thu, 2 Jan 2025 15:54:31 +0100 Subject: [PATCH 02/12] [filters] Show filter (or group) creator in edition modal --- src/components/modals/EditSearchFilterGroupModal.vue | 9 ++++++++- src/components/modals/EditSearchFilterModal.vue | 9 ++++++++- src/locales/en.js | 1 + 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/modals/EditSearchFilterGroupModal.vue b/src/components/modals/EditSearchFilterGroupModal.vue index c65df88843..0854086568 100644 --- a/src/components/modals/EditSearchFilterGroupModal.vue +++ b/src/components/modals/EditSearchFilterGroupModal.vue @@ -42,6 +42,11 @@ /> +
+ $t('main.created_by'): + +
+ +
+ $t('main.created_by'): + +
+ Date: Thu, 2 Jan 2025 16:02:09 +0100 Subject: [PATCH 03/12] [previews] Separate playlist progress from video progress --- .../pages/playlists/PlaylistPlayer.vue | 55 +- src/components/previews/PlaylistProgress.vue | 508 ++++++++++++++++++ src/components/previews/VideoProgress.vue | 418 +++----------- 3 files changed, 615 insertions(+), 366 deletions(-) create mode 100644 src/components/previews/PlaylistProgress.vue diff --git a/src/components/pages/playlists/PlaylistPlayer.vue b/src/components/pages/playlists/PlaylistPlayer.vue index a7e983322f..b2e38ac82f 100644 --- a/src/components/pages/playlists/PlaylistPlayer.vue +++ b/src/components/pages/playlists/PlaylistPlayer.vue @@ -316,24 +316,20 @@ ref="video-progress" class="video-progress pull-bottom" :annotations="annotations" - :entity-list="entityList" + :entity-ist="entityList" + :empty="!isCurrentPreviewMovie" :fps="fps" :frame-duration="frameDuration" - :is-playlist="true" :is-full-mode="isFullMode" :is-full-screen="fullScreen || isEntitiesHidden" :movie-dimensions="movieDimensions" - :nb-frames="nbFrames" + :nb-frames="isCurrentPreviewMovie ? nbFrames : 48" :handle-in="playlist.for_entity === 'shot' ? handleIn : -1" :handle-out="playlist.for_entity === 'shot' ? handleOut : -1" :preview-id="currentPreview ? currentPreview.id : ''" - :playlist-duration="playlistDuration" - :playlist-progress="playlistProgress" - :playlist-shot-position="playlistShotPosition" @start-scrub="onScrubStart" @end-scrub="onScrubEnd" @progress-changed="onProgressChanged" - @progress-playlist-changed="onProgressPlaylistChanged" @handle-in-changed="onHandleInChanged" @handle-out-changed="onHandleOutChanged" v-show="playlist.id && !isAddingEntity" @@ -873,6 +869,24 @@ /> + +
+
+ + {{ hoverFrame }} + + + +
+
+
+ + {{ getFullEntityName(entity) }} + +
+ + +
+
+ + + + + diff --git a/src/components/previews/VideoProgress.vue b/src/components/previews/VideoProgress.vue index ba70b6d3df..b379fce17e 100644 --- a/src/components/previews/VideoProgress.vue +++ b/src/components/previews/VideoProgress.vue @@ -19,7 +19,7 @@ }" @mousedown="startHandleInDrag" @touchstart="startHandleInDrag" - v-if="handleIn >= 0 && !isFullMode" + v-if="handleIn >= 0 && !isFullMode && !empty" > {{ handleIn !== 0 ? handleIn + 1 : '' }} @@ -31,7 +31,7 @@ }" @mousedown="startHandleOutDrag" @touchstart="startHandleOutDrag" - v-if="handleOut >= 0 && !isFullMode" + v-if="handleOut >= 0 && !isFullMode && !empty" > {{ handleOut + 1 }} @@ -44,39 +44,43 @@ @mousedown="startProgressDrag" @touchstart="startProgressDrag" > - - - - + + +
{{ hoverFrame }} @@ -97,62 +101,6 @@
-
-
- - {{ getFullEntityName(entity) }} - -
- - -
@@ -179,6 +127,10 @@ export default { default: () => [], type: Array }, + empty: { + default: false, + type: Boolean + }, entityList: { default: () => [], type: Array @@ -218,22 +170,6 @@ export default { previewId: { default: '', type: String - }, - isPlaylist: { - default: false, - type: Boolean - }, - playlistDuration: { - default: 0, - type: Number - }, - playlistProgress: { - default: 0, - type: Number - }, - playlistShotPosition: { - default: () => {}, - type: Object } }, @@ -242,7 +178,6 @@ export default { 'handle-in-changed', 'handle-out-changed', 'progress-changed', - 'progress-playlist-changed', 'start-scrub' ], @@ -254,9 +189,7 @@ export default { isFrameNumberVisible: false, isTileLoading: false, hoverFrame: 0, - isPlaylistHover: false, progressDragging: false, - playlistProgressDragging: false, width: 0, domEvents: [ ['mousemove', this.doProgressDrag], @@ -265,10 +198,6 @@ export default { ['mouseleave', this.stopProgressDrag], ['touchend', this.stopProgressDrag], ['touchcancel', this.stopProgressDrag], - ['mouseup', this.stopPlaylistProgressDrag], - ['mouseleave', this.stopPlaylistProgressDrag], - ['touchend', this.stopPlaylistProgressDrag], - ['touchcancel', this.stopPlaylistProgressDrag], ['mouseup', this.stopHandleInDrag], ['mouseleave', this.stopHandleInDrag], ['touchend', this.stopHandleInDrag], @@ -314,32 +243,15 @@ export default { const frameHeight = 100 const height = frameHeight + 30 let frameWidth = 150 - console.log('ok', this.isPlaylistHover) - if (this.isPlaylistHover) { - const preview = this.playlistShotPosition[this.hoverFrame] - if (preview.extension === 'mp4') { - const ratio = preview.width / preview.height - frameWidth = Math.ceil(frameHeight * ratio) - console.log('cool', ratio, preview) - } else if ( - this.isPlaylistHover && - preview.extension === 'png' - ) { - frameWidth = 150 - } - } else { - frameWidth = Math.ceil(frameHeight * this.videoRatio) - } + frameWidth = Math.ceil(frameHeight * this.videoRatio) const width = frameWidth + 10 const left = Math.min( Math.max(this.frameNumberLeftPosition - frameWidth / 2, 0), this.width - frameWidth - 10 ) const top = this.isFullScreen - ? `-${height + 10}px` - : this.isPlaylist - ? '16px' - : '0px' + ? `-${height + 30}px` + : '0px' return { height: `${height}px`, @@ -369,10 +281,6 @@ export default { handleInWidth() { return Math.max(this.frameSize * this.handleIn, 0) + 'px' - }, - - playlistProgressWidget() { - return this.$refs['playlist-progress'] } }, @@ -390,14 +298,13 @@ export default { return frameNumber * this.frameSize }, - updatePlaylistProgressBar(time) {}, - updateProgressBar(frameNumber) { - this.progress.value = (frameNumber + 1) * this.frameDuration + this.progress.value = this.empty + ? frameNumber * this.frameDuration + : (frameNumber + 1) * this.frameDuration }, startProgressDrag(event) { - if (this.playlistProgressDragging) return this.progressDragging = true this.$emit('start-scrub') }, @@ -407,17 +314,6 @@ export default { this.$emit('end-scrub') }, - startPlaylistProgressDrag(event) { - if (this.progressDragging) return - this.playlistProgressDragging = true - this.$emit('start-scrub') - }, - - stopPlaylistProgressDrag(event) { - this.playlistProgressDragging = false - this.$emit('end-scrub') - }, - startHandleInDrag(event) { this.handleInDragging = true }, @@ -446,39 +342,17 @@ export default { doProgressDrag(event) { if ( this.progressDragging || - this.playlistProgressDragging || this.handleInDragging || this.handleOutDragging || this.isFrameNumberVisible ) { - if ( - this.playlistProgressDragging || - (!this.progressDragging && - event.target.classList && - (event.target.classList.contains('playlilst-progress') || - event.target.classList.contains('entity-status') || - event.target.classList.contains('playlist-progress-position'))) - ) { - this.currentMouseFrame = this._getPlaylistMouseFrame(event) - const { frameNumber } = this.currentMouseFrame - this.hoverFrame = frameNumber + 1 - const allDuration = Math.round(this.playlistDuration * this.fps) - this.frameNumberLeftPosition = - (this.width / allDuration) * frameNumber - this.isPlaylistHover = true - } else { - this.currentMouseFrame = this._getMouseFrame(event) - const { frameNumber } = this.currentMouseFrame - this.hoverFrame = frameNumber + 1 - this.frameNumberLeftPosition = - (this.width / this.nbFrames) * frameNumber - this.isPlaylistHover = false - } + this.currentMouseFrame = this._getMouseFrame(event) const { frameNumber } = this.currentMouseFrame + this.hoverFrame = frameNumber + 1 + this.frameNumberLeftPosition = + (this.width / this.nbFrames) * frameNumber if (this.progressDragging) { this.$emit('progress-changed', frameNumber) - } else if (this.playlistProgressDragging) { - this.$emit('progress-playlist-changed', frameNumber) } else if (this.handleInDragging) { this.$emit('handle-in-changed', { frameNumber, save: false }) } else if (this.handleOutDragging) { @@ -489,11 +363,6 @@ export default { } }, - onPlaylistProgressClicked(event) { - const { frameNumber } = this._getPlaylistMouseFrame(event) - this.$emit('progress-playlist-changed', frameNumber) - }, - onProgressClicked(event) { this._emitProgressEvent(event) }, @@ -526,19 +395,9 @@ export default { return { frameNumber, position } }, - _getPlaylistMouseFrame(event) { - const left = this.playlistProgressWidget.getBoundingClientRect().left - let position = this.getClientX(event) - left - if (position > this.width) position = this.width - 1 - const ratio = position / this.width - let duration = this.playlistDuration * ratio - if (duration < 0) duration = 0 - const frameNumber = Math.floor(duration / this.frameDuration) - return { frameNumber, position } - }, - _emitProgressEvent(event, annotation) { const { frameNumber } = this._getMouseFrame(event, annotation) + if (frameNumber < 0) return this.$emit('progress-changed', frameNumber) }, @@ -552,13 +411,7 @@ export default { if (!frame) return {} let previewId = this.previewId let extension = null - if (this.isPlaylistHover) { - previewId = this.playlistShotPosition[frame].id - extension = this.playlistShotPosition[frame].extension - frame = frame - this.playlistShotPosition[frame].start * this.fps - } else { - frame = frame - 1 - } + frame = frame - 1 if (this.nbFrames >= 3840) { frame = Math.ceil(frame / Math.ceil(this.nbFrames / 3840)) } @@ -566,42 +419,13 @@ export default { const frameY = Math.floor(frame / 8) const frameHeight = 100 const frameWidth = Math.ceil(frameHeight * this.videoRatio) - - if (!this.isPlaylistHover) { - console.log('not playlist') - const tilePath = `/api/movies/tiles/preview-files/${previewId}.png` - return { - background: `url(${tilePath})`, - 'background-position': `-${frameX * frameWidth}px -${ - frameY * frameHeight - }px`, - width: `${frameWidth}px` - } - } else { - if (extension === 'png') { - const tilePath = `/api/pictures/thumbnails/preview-files/${previewId}.png` - console.log('png') - return { - background: `url(${tilePath})`, - 'background-position': '0 0', - width: '150px' - } - } else if (extension === 'mp4') { - console.log('mp4', this.extension) - const tilePath = `/api/movies/tiles/preview-files/${previewId}.png` - return { - background: `url(${tilePath})`, - 'background-position': `-${frameX * frameWidth}px -${ - frameY * frameHeight - }px`, - width: `${frameWidth}px` - } - } else { - console.log(extension) - return { - background: 'transparent' - } - } + const tilePath = `/api/movies/tiles/preview-files/${previewId}.png` + return { + background: `url(${tilePath})`, + 'background-position': `-${frameX * frameWidth}px -${ + frameY * frameHeight + }px`, + width: `${frameWidth}px` } }, @@ -618,12 +442,8 @@ export default { this.width - frameWidth - 10 ) const top = this.isFullScreen - ? this.isPlaylist - ? `-${height + 32}px` - : `-${height}px` - : this.isPlaylist - ? '42px' - : '30px' + ? `-${height}px` + : '30px' return { height: `${height}px`, @@ -631,32 +451,6 @@ export default { top, left: `${left}px` } - }, - - getEntityPosition(entity) { - const ratio = - (entity.start_duration - this.frameDuration) / this.playlistDuration - return ratio * 100 - }, - - getEntityWidth(entity) { - let ratio = 0 - // console.log(this.playlistDuration, entity.preview_file_duration) - if (entity.preview_file_extension === 'mp4') { - ratio = entity.preview_file_duration / this.playlistDuration - } else { - // console.log(this.frameDuration, this.playlistDuration) - ratio = 2 * this.fps * this.frameDuration / this.playlistDuration - } - return ratio * 100 - }, - - getEntityColor(entity) { - return entity.task_status_color - }, - - getFullEntityName(entity) { - return `${entity.parent_name} / ${entity.name}`.replaceAll(' ', ' ') } }, @@ -666,27 +460,6 @@ export default { this.width = progressCoordinates.width this.progress.setAttribute('max', this.videoDuration) this.updateProgressBar(0) - }, - - previewId: { - immediate: true, - handler() { - if (this.previewId) { - const preview = this.playlistShotPosition[this.hoverFrame] - if (preview.extension === 'mp4') { - this.isTileLoading = true - const img = new Image() - img.src = this.tilePath - img.onload = () => { - this.isTileLoading = false - } - } - } - } - }, - - playlistProgress() { - this.updatePlaylistProgressBar(this.playlistProgress) } } } @@ -765,6 +538,10 @@ progress { } } +.frame-number-rail { + position: relative; +} + .handle-in { background: $black; color: $grey; @@ -817,61 +594,4 @@ progress { width: 5px; z-index: 120; } - -.playlist-progress { - background: $dark-grey; - border-bottom: 1px solid $dark-grey-light; - border-top: 1px solid $dark-grey-light; - cursor: pointer; - height: 18px; - width: 100%; - position: relative; /* Relative positioning for pseudo-element placement */ - overflow: visible; - transition: height 0.2s ease-in-out; - - &:hover { - height: 18px; - } -} - -.playlist-progress-position { - border-left: 5px solid $green; - position: absolute; - height: 6px; - border-radius: 50%; - z-index: 3; - top: -2px; -} - -.frame-number-rail { - position: relative; -} - -.entity-status { - border-left: 0; - border-right: 3px solid $dark-grey; - position: absolute; - bottom: 0; - transition: height 0.3s ease-in-out; - height: 16px; - z-index: 2; - opacity: 0.4; - - span { - background: $dark-grey; - border-radius: 5px; - color: $white; - display: none; - padding: 0.2em 0.5em; - position: absolute; - top: -30px; - } - - &:hover { - opacity: 1; - span { - display: block; - } - } -} From a1dba92bbe905bfb44c2b1eca8bbe71c7ad5eefd Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 3 Jan 2025 16:28:57 +0100 Subject: [PATCH 04/12] =?UTF-8?q?[prev=C3=AEews]=20Allow=20to=20select,=20?= =?UTF-8?q?start=20and=20stop=20animations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/mixins/player.js | 14 ++++ .../pages/playlists/PlaylistPlayer.vue | 57 +++++++++++++- src/components/previews/ObjectViewer.vue | 21 +++++- src/components/previews/PreviewPlayer.vue | 75 +++++++++++++++---- src/components/previews/PreviewViewer.vue | 18 +++++ vite.config.js | 5 ++ 6 files changed, 168 insertions(+), 22 deletions(-) diff --git a/src/components/mixins/player.js b/src/components/mixins/player.js index 1f71ad8c09..f07f537b26 100644 --- a/src/components/mixins/player.js +++ b/src/components/mixins/player.js @@ -253,6 +253,10 @@ export const playerMixin = { return this.$refs['sound-player'] }, + modelPlayer() { + return this.$refs['object-player'] + }, + canvas() { return this.$refs['canvas-wrapper'] }, @@ -425,6 +429,8 @@ export const playerMixin = { this.playPicture() } else if (this.isCurrentPreviewSound) { this.playSound() + } else if (this.isCurrentPreviewModel) { + this.playModel() } else { this._setCurrentTimeOnHandleIn() this.rawPlayer.play() @@ -490,6 +496,8 @@ export const playerMixin = { } } else if (this.isCurrentPreviewSound) { this.soundPlayer?.pause() + } else if (this.isCurrentPreviewModel) { + this.modelPlayer?.pause() } this.isPlaying = false }, @@ -1069,6 +1077,12 @@ export const playerMixin = { } }, + playModel() { + if (this.playingPictureTimeout) clearTimeout(this.playingPictureTimeout) + this.isPlaying = true + this.modelPlayer?.play(this.objectModel.currentAnimation) + }, + resetCanvasSize() { return this.$nextTick().then(() => { if (this.isCurrentPreviewMovie && this.isAnnotationCanvas()) { diff --git a/src/components/pages/playlists/PlaylistPlayer.vue b/src/components/pages/playlists/PlaylistPlayer.vue index b2e38ac82f..38015615da 100644 --- a/src/components/pages/playlists/PlaylistPlayer.vue +++ b/src/components/pages/playlists/PlaylistPlayer.vue @@ -214,7 +214,8 @@ position: isComparisonOverlay ? 'absolute' : 'static', opacity: overlayOpacity }" - v-if="isCurrentPreviewModel && !isLoading" + @model-loaded="onModelLoaded" + v-show="isCurrentPreviewModel && !isLoading" /> - + + +
0 + if (this.objectModel.isAnimation) { + console.log(animations) + this.objectModel.availableAnimations = animations + .map(animation => ({ + label: animation, + value: animation + })) + this.objectModel.currentAnimation = this.availableAnimations[0].value + this.$nextTick(() => { + this.playModel() + }) + } else { + this.objectModel.availableAnimations = [] + this.objectModel.currentAnimation = null + } + }, + onPreviewChanged(entity, previewFile) { this.pause() const localEntity = this.entityList.find(s => s.id === entity.id) @@ -2086,6 +2129,12 @@ export default { }, watch: { + 'objectModel.currentAnimation'() { + if (this.isCurrentPreviewModel && this.objectModel.isAnimation) { + this.playModel() + } + }, + isLoading() { if (!this.isLoading) { this.resetHeight() diff --git a/src/components/previews/ObjectViewer.vue b/src/components/previews/ObjectViewer.vue index f275820bee..f74f6e5c51 100644 --- a/src/components/previews/ObjectViewer.vue +++ b/src/components/previews/ObjectViewer.vue @@ -10,15 +10,16 @@ }" >
@@ -60,6 +61,7 @@ export default { }, methods: { + /** * Create a wireframe variant of each material of a 3D model * @param {Model} model - model from model-viewer component @@ -89,7 +91,20 @@ export default { material.envMapIntensity = 0 }) } - } + }, + + getAnimations() { + return this.$refs['model-viewer'].availableAnimations + }, + + play(animationName) { + this.$refs['model-viewer'].animationName = animationName + this.$refs['model-viewer'].play() + }, + + pause() { + this.$refs['model-viewer'].pause() + }, } } diff --git a/src/components/previews/PreviewPlayer.vue b/src/components/previews/PreviewPlayer.vue index 072fe87011..03f5242fc2 100644 --- a/src/components/previews/PreviewPlayer.vue +++ b/src/components/previews/PreviewPlayer.vue @@ -60,6 +60,7 @@ }" @duration-changed="changeMaxDuration" @frame-update="setVideoFrameContext" + @model-loaded="onModelLoaded" @play-ended="pause" @size-changed="fixCanvasSize" @video-end="onVideoEnd" @@ -129,7 +130,7 @@ />
-
+
+ +
@@ -650,6 +660,8 @@ export default { data() { return { annotations: [], + availableAnimations: [], + current3DAnimation: null, currentFrame: 0, currentIndex: 1, fullScreen: false, @@ -657,6 +669,7 @@ export default { currentBackground: null, currentTime: '00:00:00:00', currentTimeRaw: 0, + is3DAnimation: false, isObjectBackground: false, isAnnotationsDisplayed: true, isEnvironmentSkybox: false, @@ -1137,14 +1150,18 @@ export default { this.isPlaying = true this.isDrawing = false if (this.previewViewer) { - this.clearCanvas() - if (this.currentFrame >= this.nbFrames - 1) { - this.previewViewer.setCurrentFrame(0) - this.comparisonViewer.setCurrentFrame(0) - } - this.previewViewer.play() - if (this.comparisonViewer && this.isComparing) { - this.comparisonViewer.play() + if (this.is3DModel) { + this.previewViewer.playModelAnimation(this.current3DAnimation) + } else { + this.clearCanvas() + if (this.currentFrame >= this.nbFrames - 1) { + this.previewViewer.setCurrentFrame(0) + this.comparisonViewer.setCurrentFrame(0) + } + this.previewViewer.play() + if (this.comparisonViewer && this.isComparing) { + this.comparisonViewer.play() + } } } }, @@ -1152,11 +1169,16 @@ export default { pause() { if (this.isPlaying) { this.isPlaying = false - if (this.previewViewer) this.previewViewer.pause() - if (this.comparisonViewer) this.comparisonViewer.pause() - this.$nextTick(() => { - this.syncComparisonViewer() - }) + + if (this.is3DModel) { + this.previewViewer.pauseModelAnimation() + } else { + if (this.previewViewer) this.previewViewer.pause() + if (this.comparisonViewer) this.comparisonViewer.pause() + this.$nextTick(() => { + this.syncComparisonViewer() + }) + } } }, @@ -1788,6 +1810,20 @@ export default { }) }, + onModelLoaded() { + this.is3DAnimation = this.previewViewer.get3DAnimations().length > 0 + if (this.is3DAnimation) { + this.available3DAnimations = this.previewViewer.get3DAnimations() + .map(animation => ({ + label: animation, + value: animation + })) + this.current3DAnimation = this.available3DAnimations[0].value + this.isPlaying = true + this.previewViewer.playModelAnimation(this.current3DAnimation) + } + }, + onVideoLoaded() { if (this.isMovie) { this.movieDimensions = { @@ -1969,6 +2005,12 @@ export default { }, watch: { + current3DAnimation() { + if (this.is3DModel) { + this.previewViewer.playModelAnimation(this.current3DAnimation) + } + }, + currentPreview() { this.endAnnotationSaving() this.reloadAnnotations() @@ -1993,7 +2035,10 @@ export default { this.previewViewer.resize() this.comparisonViewer.resize() }, 500) - } else if (this.isSound || this.isFile || this.is3DModel) { + } else if (this.is3DModel) { + this.fixCanvasSize({ width: 0, height: 0, left: 0, top: 0 }) + this.previewViewer.resize() + } else if (this.isSound || this.isFile) { // hide canvas this.fixCanvasSize({ width: 0, height: 0, left: 0, top: 0 }) } diff --git a/src/components/previews/PreviewViewer.vue b/src/components/previews/PreviewViewer.vue index 7dd1272ada..dfdc3706b0 100644 --- a/src/components/previews/PreviewViewer.vue +++ b/src/components/previews/PreviewViewer.vue @@ -64,6 +64,7 @@ /> @@ -230,6 +232,10 @@ export default { return this.$refs['sound-viewer'] }, + objectViewer() { + return this.$refs['object-viewer'] + }, + // Utils backgroundUrl() { @@ -359,6 +365,14 @@ export default { } }, + playModelAnimation(animationName) { + this.objectViewer.play(animationName) + }, + + pauseModelAnimation() { + this.objectViewer.pause() + }, + goPreviousFrame() { return this.videoViewer.goPreviousFrame() }, @@ -375,6 +389,10 @@ export default { } }, + get3DAnimations() { + return this.$refs['object-viewer'].getAnimations() + }, + // Sizing getNaturalDimensions() { diff --git a/vite.config.js b/vite.config.js index 0a05073802..5cd67fbd99 100644 --- a/vite.config.js +++ b/vite.config.js @@ -16,6 +16,11 @@ export default defineConfig({ build: { sourcemap: true }, + template: { + compilerOptions: { + isCustomElement: (tag) => ['model-viewer'].includes(tag), + } + }, resolve: { extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], alias: { From e9f717032ebf5c811a84d75df224118857a71360 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Fri, 3 Jan 2025 16:30:21 +0100 Subject: [PATCH 05/12] [playlists] Show timeline also for pictures --- src/components/mixins/player.js | 28 ++++++++---- .../pages/playlists/PlaylistPlayer.vue | 45 +++++++++++-------- 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/src/components/mixins/player.js b/src/components/mixins/player.js index f07f537b26..f5c63953fd 100644 --- a/src/components/mixins/player.js +++ b/src/components/mixins/player.js @@ -20,7 +20,7 @@ export const playerMixin = { entityList: [], entityListToCompare: [], framesPerImage: [], - framesSeenOfPicture: 0, + framesSeenOfPicture: 1, fullScreen: false, isCommentsHidden: true, isComparing: false, @@ -203,6 +203,9 @@ export const playerMixin = { }, frameNumber() { + if (this.isCurrentPreviewPicture) { + return this.framesSeenOfPicture - 1 + } let frameNumber = this.currentTimeRaw / this.frameDuration if (frameNumber >= this.nbFrames) { frameNumber = this.nbFrames @@ -414,6 +417,7 @@ export const playerMixin = { }, play() { + if (this.playingPictureTimeout) clearTimeout(this.playingPictureTimeout) if (this.isFullMode) { if ( this.fullPlayer.currentTime >= @@ -506,7 +510,7 @@ export const playerMixin = { const entity = this.entityList[entityIndex] const wasDrawing = this.isDrawing === true this.clearCanvas() - this.framesSeenOfPicture = 0 + this.framesSeenOfPicture = 1 this.playingEntityIndex = entityIndex if (entity && this.isMovie(entity.preview_file_extension)) { this.$nextTick(() => { @@ -684,21 +688,25 @@ export const playerMixin = { onProgressChanged(frameNumber, updatePlaylistProgress = true) { this.clearCanvas() - this.rawPlayer.setCurrentFrame(frameNumber) - this.syncComparisonPlayer() + + if (this.isCurrentPreviewPicture) { + this.framesSeenOfPicture = frameNumber + 1 + } else { + this.rawPlayer.setCurrentFrame(frameNumber) + this.syncComparisonPlayer() + } + const annotation = this.getAnnotation(frameNumber * this.frameDuration) if (annotation) this.loadAnnotation(annotation) + this.sendUpdatePlayingStatus() this.onFrameUpdate(frameNumber) + if (this.isFullMode && updatePlaylistProgress) { const start = this.currentEntity.start_duration const time = (frameNumber - 1) / this.fps + start this.fullPlayer.currentTime = time this.playlistProgress = time - } else { - setTimeout(() => { - this.updateProgressBar() - }, 200) } }, @@ -1060,7 +1068,8 @@ export const playerMixin = { }, playPicture() { - if (this.isPlaying) clearTimeout(this.playingPictureTimeout) + if (this.playingPictureTimeout) clearTimeout(this.playingPictureTimeout) + this.framesSeenOfPicture = 1 this.isPlaying = true this.playingPictureTimeout = setTimeout(() => { this.continuePlayingPlaylist( @@ -1071,6 +1080,7 @@ export const playerMixin = { }, playSound() { + if (this.playingPictureTimeout) clearTimeout(this.playingPictureTimeout) this.isPlaying = true if (this.isCurrentPreviewSound) { this.soundPlayer?.play() diff --git a/src/components/pages/playlists/PlaylistPlayer.vue b/src/components/pages/playlists/PlaylistPlayer.vue index 38015615da..f7437c2b18 100644 --- a/src/components/pages/playlists/PlaylistPlayer.vue +++ b/src/components/pages/playlists/PlaylistPlayer.vue @@ -456,14 +456,7 @@
@@ -1092,6 +1085,7 @@ export default { isEnvironmentSkybox: false, isFullMode: false, isLaserModeOn: false, + isMounted: false, isObjectBackground: false, isShowingPalette: false, isShowingPencilPalette: false, @@ -1139,6 +1133,7 @@ export default { }, mounted() { + if (this.isMounted) return this.$options.scrubbing = false this.isHd = Boolean(this.organisation.hd_by_default) if (this.entities) { @@ -1161,6 +1156,7 @@ export default { this.currentBackground = this.productionBackgrounds.find(this.isDefaultBackground) || null this.onObjectBackgroundSelected() + this.isMounted = true }, computed: { @@ -1505,7 +1501,7 @@ export default { } else { this.onPlayNextEntityClicked() if (this.isCurrentPreviewPicture) { - this.framesSeenOfPicture = 0 + this.framesSeenOfPicture = 1 this.playPicture() this.updateProgressBar() } @@ -1533,11 +1529,12 @@ export default { const framesPerImage = this.framesPerImage[entityIndex] const durationToWaitMs = (framesPerImage * 1000) / this.fps const durationWaited = Date.now() - startMs + console.log('continuePlayingPlaylist', durationWaited, durationToWaitMs) if (!this.isPlaying) return else if (durationWaited < durationToWaitMs) { - this.framesSeenOfPicture = Math.floor( + this.framesSeenOfPicture = Math.max(Math.floor( (durationWaited / 1000) * this.fps - ) + ), 1) this.playingPictureTimeout = setTimeout(() => { this.continuePlayingPlaylist(entityIndex, startMs) }, 100) @@ -1545,7 +1542,7 @@ export default { } // we've seen all the frames the picture should be visible - this.framesSeenOfPicture = 0 + this.framesSeenOfPicture = 1 const previews = this.currentEntity.preview_file_previews if (previews.length === this.currentPreviewIndex) { this.$nextTick(() => { @@ -2014,7 +2011,8 @@ export default { } }, - onProgressPlaylistChanged(frameNumber) { + onProgressPlaylistChanged(frameNumber) { + console.log('onProgressPlaylistChanged', frameNumber) if (this.isFullMode) { const time = frameNumber / this.fps this.fullPlayer.currentTime = time @@ -2028,7 +2026,11 @@ export default { this.onFrameUpdate(frame) }) } else { - this.setCurrentTimeRaw(frame / this.fps) + if (this.isPlayingPicture) { + this.framesSeenOfPicture = frame + 1 + } else { + this.setCurrentTimeRaw(frame / this.fps) + } } }, @@ -2061,14 +2063,15 @@ export default { } }, - updateProgressBar() { + updateProgressBar(frameNumber) { + const frame = frameNumber || this.frameNumber if (this.progress) { - this.progress.updateProgressBar(this.frameNumber + 1) + this.progress.updateProgressBar(frame + 1) } + // console.error('updateProgressBar', frame) if (this.playlistDuration && !this.isFullMode && this.currentEntity) { - console.log('updateProgressBar') this.playlistProgress = - this.currentEntity.start_duration + this.frameNumber / this.fps + this.currentEntity.start_duration + frame / this.fps } }, @@ -2135,6 +2138,12 @@ export default { } }, + framesSeenOfPicture() { + if (this.isCurrentPreviewPicture) { + this.updateProgressBar(this.framesSeenOfPicture - 1) + } + }, + isLoading() { if (!this.isLoading) { this.resetHeight() From eada8a5c78a06dd28a01ff9446aa705a9186eec1 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 6 Jan 2025 10:35:21 +0100 Subject: [PATCH 06/12] [previews] Reorganise player mixin --- src/components/mixins/player.js | 115 ++++++++++++++++---------------- 1 file changed, 58 insertions(+), 57 deletions(-) diff --git a/src/components/mixins/player.js b/src/components/mixins/player.js index f5c63953fd..3d3955bc95 100644 --- a/src/components/mixins/player.js +++ b/src/components/mixins/player.js @@ -59,12 +59,49 @@ export const playerMixin = { 'user' ]), + // Elements + + container() { + return this.$refs.container + }, + + rawPlayer() { + return this.$refs['raw-player'] + }, + + rawPlayerComparison() { + return this.$refs['raw-player-comparison'] + }, + + picturePlayer() { + return this.$refs['picture-player'] + }, + + soundPlayer() { + return this.$refs['sound-player'] + }, + + modelPlayer() { + return this.$refs['object-player'] + }, + + canvas() { + return this.$refs['canvas-wrapper'] + }, + + progress() { + return this.$refs['video-progress'] + }, + + video() { + return this.$refs.movie + }, + + // File type + extension() { - if (!this.currentPreview) return '' - if (this.currentPreview.extension) { - return this.currentPreview.extension - } - return '' + if (!this.currentPreview || !this.currentPreview.extension) return '' + return this.currentPreview.extension }, isCurrentPreviewMovie() { @@ -202,6 +239,16 @@ export const playerMixin = { ) }, + // Frames + + frameDuration() { + return Math.round((1 / this.fps) * 10000) / 10000 + }, + + fps() { + return parseFloat(this.currentProduction?.fps) || 25 + }, + frameNumber() { if (this.isCurrentPreviewPicture) { return this.framesSeenOfPicture - 1 @@ -226,52 +273,6 @@ export const playerMixin = { return 0 }, - frameDuration() { - return Math.round((1 / this.fps) * 10000) / 10000 - }, - - fps() { - return parseFloat(this.currentProduction?.fps) || 25 - }, - - // Elements - - container() { - return this.$refs.container - }, - - rawPlayer() { - return this.$refs['raw-player'] - }, - - rawPlayerComparison() { - return this.$refs['raw-player-comparison'] - }, - - picturePlayer() { - return this.$refs['picture-player'] - }, - - soundPlayer() { - return this.$refs['sound-player'] - }, - - modelPlayer() { - return this.$refs['object-player'] - }, - - canvas() { - return this.$refs['canvas-wrapper'] - }, - - progress() { - return this.$refs['video-progress'] - }, - - video() { - return this.$refs.movie - }, - nbFrames() { const isChromium = !!window.chrome const change = isChromium ? this.frameDuration : 0 @@ -390,12 +391,6 @@ export const playerMixin = { this.$refs['video-progress'].$el.style.opacity = 0 }, - updateProgressBar() { - if (this.progress) { - this.progress.updateProgressBar(this.frameNumber) - } - }, - updateTaskPanel() { if (this.entityList.length > 0) { const entity = this.entityList[this.playingEntityIndex] @@ -406,6 +401,12 @@ export const playerMixin = { } }, + updateProgressBar() { + if (this.progress) { + this.progress.updateProgressBar(this.frameNumber) + } + }, + playClicked() { this.play() this.updateRoomStatus() From 9abe4edd9805267b8b54c9ac24a7fb76e0147a54 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 6 Jan 2025 10:37:15 +0100 Subject: [PATCH 07/12] [playlists] Fix model viewer when empty --- src/components/pages/playlists/PlaylistPlayer.vue | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/components/pages/playlists/PlaylistPlayer.vue b/src/components/pages/playlists/PlaylistPlayer.vue index f7437c2b18..f83a6982f3 100644 --- a/src/components/pages/playlists/PlaylistPlayer.vue +++ b/src/components/pages/playlists/PlaylistPlayer.vue @@ -209,7 +209,7 @@ :full-screen="fullScreen" :is-environment-skybox="isEnvironmentSkybox" :is-wireframe="isWireframe" - :preview-url="currentPreviewDlPath" + :preview-url="isCurrentPreviewModel ? currentPreviewDlPath : null" :style="{ position: isComparisonOverlay ? 'absolute' : 'static', opacity: overlayOpacity @@ -1529,7 +1529,6 @@ export default { const framesPerImage = this.framesPerImage[entityIndex] const durationToWaitMs = (framesPerImage * 1000) / this.fps const durationWaited = Date.now() - startMs - console.log('continuePlayingPlaylist', durationWaited, durationToWaitMs) if (!this.isPlaying) return else if (durationWaited < durationToWaitMs) { this.framesSeenOfPicture = Math.max(Math.floor( @@ -1569,16 +1568,14 @@ export default { onModelLoaded() { const animations = this.modelPlayer?.getAnimations() || [] - console.log('model loaded', animations) this.objectModel.isAnimation = animations.length > 0 if (this.objectModel.isAnimation) { - console.log(animations) this.objectModel.availableAnimations = animations .map(animation => ({ label: animation, value: animation })) - this.objectModel.currentAnimation = this.availableAnimations[0].value + this.objectModel.currentAnimation = animations[0] this.$nextTick(() => { this.playModel() }) @@ -2012,7 +2009,6 @@ export default { }, onProgressPlaylistChanged(frameNumber) { - console.log('onProgressPlaylistChanged', frameNumber) if (this.isFullMode) { const time = frameNumber / this.fps this.fullPlayer.currentTime = time From 0be1fa6a112ed89b4e67577304fde14b4bd2abc3 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 6 Jan 2025 14:53:39 +0100 Subject: [PATCH 08/12] [playlists] Fix comparison playing --- src/components/pages/playlists/PlaylistPlayer.vue | 2 +- src/components/pages/playlists/RawVideoPlayer.vue | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/components/pages/playlists/PlaylistPlayer.vue b/src/components/pages/playlists/PlaylistPlayer.vue index f83a6982f3..6261230548 100644 --- a/src/components/pages/playlists/PlaylistPlayer.vue +++ b/src/components/pages/playlists/PlaylistPlayer.vue @@ -111,6 +111,7 @@ -
+
({{ currentFrame }} - / {{ (nbFrames + '').padStart(3, '0') }} - ) + / {{ (nbFrames + '').padStart(3, '0') }} )
@@ -1531,9 +1528,10 @@ export default { const durationWaited = Date.now() - startMs if (!this.isPlaying) return else if (durationWaited < durationToWaitMs) { - this.framesSeenOfPicture = Math.max(Math.floor( - (durationWaited / 1000) * this.fps - ), 1) + this.framesSeenOfPicture = Math.max( + Math.floor((durationWaited / 1000) * this.fps), + 1 + ) this.playingPictureTimeout = setTimeout(() => { this.continuePlayingPlaylist(entityIndex, startMs) }, 100) @@ -1570,11 +1568,10 @@ export default { const animations = this.modelPlayer?.getAnimations() || [] this.objectModel.isAnimation = animations.length > 0 if (this.objectModel.isAnimation) { - this.objectModel.availableAnimations = animations - .map(animation => ({ - label: animation, - value: animation - })) + this.objectModel.availableAnimations = animations.map(animation => ({ + label: animation, + value: animation + })) this.objectModel.currentAnimation = animations[0] this.$nextTick(() => { this.playModel() @@ -2008,7 +2005,7 @@ export default { } }, - onProgressPlaylistChanged(frameNumber) { + onProgressPlaylistChanged(frameNumber) { if (this.isFullMode) { const time = frameNumber / this.fps this.fullPlayer.currentTime = time @@ -2087,9 +2084,9 @@ export default { const defaultNbFrames = entity.preview_nb_frames || DEFAULT_NB_FRAMES_PICTURE this.framesPerImage[index] = defaultNbFrames - const nbFrames = Math.round( - (entity.preview_file_duration || 0) * this.fps - ) || defaultNbFrames + const nbFrames = + Math.round((entity.preview_file_duration || 0) * this.fps) || + defaultNbFrames entity.start_duration = (currentFrame + 1) / this.fps for (let i = 0; i < nbFrames; i++) { this.playlistShotPosition[currentFrame + i] = { diff --git a/src/components/previews/ObjectViewer.vue b/src/components/previews/ObjectViewer.vue index f74f6e5c51..a8a01283eb 100644 --- a/src/components/previews/ObjectViewer.vue +++ b/src/components/previews/ObjectViewer.vue @@ -60,8 +60,9 @@ export default { } }, - methods: { + emits: ['model-loaded'], + methods: { /** * Create a wireframe variant of each material of a 3D model * @param {Model} model - model from model-viewer component @@ -104,7 +105,7 @@ export default { pause() { this.$refs['model-viewer'].pause() - }, + } } } diff --git a/src/components/previews/PlaylistProgress.vue b/src/components/previews/PlaylistProgress.vue index 1e3a17ec00..9df5e4eaa1 100644 --- a/src/components/previews/PlaylistProgress.vue +++ b/src/components/previews/PlaylistProgress.vue @@ -140,11 +140,7 @@ export default { } }, - emits: [ - 'end-scrub', - 'progress-playlist-changed', - 'start-scrub' - ], + emits: ['end-scrub', 'progress-playlist-changed', 'start-scrub'], data() { return { @@ -167,7 +163,7 @@ export default { ['mouseup', this.stopPlaylistProgressDrag], ['mouseleave', this.stopPlaylistProgressDrag], ['touchend', this.stopPlaylistProgressDrag], - ['touchcancel', this.stopPlaylistProgressDrag], + ['touchcancel', this.stopPlaylistProgressDrag] ] } }, @@ -212,9 +208,7 @@ export default { Math.max(this.frameNumberLeftPosition - frameWidth / 2, 0), this.width - frameWidth - 10 ) - const top = this.isFullScreen - ? `-${height + 2}px` - : '16px' + const top = this.isFullScreen ? `-${height + 2}px` : '16px' return { height: `${height}px`, @@ -244,7 +238,8 @@ export default { methods: { resetWidth() { if (this.playlistProgressWidget) { - const progressCoordinates = this.playlistProgressWidget.getBoundingClientRect() + const progressCoordinates = + this.playlistProgressWidget.getBoundingClientRect() this.width = progressCoordinates.width setTimeout(() => { this.width = progressCoordinates.width @@ -269,26 +264,25 @@ export default { }, doProgressDrag(event) { - if ( - this.playlistProgressDragging || - this.isFrameNumberVisible || - (!this.progressDragging && - event.target.classList && - (event.target.classList.contains('playlilst-progress') || - event.target.classList.contains('entity-status') || - event.target.classList.contains('playlist-progress-position'))) - ) { - this.currentMouseFrame = this._getPlaylistMouseFrame(event) - const { frameNumber } = this.currentMouseFrame - this.hoverFrame = frameNumber + 1 - const allDuration = Math.round(this.playlistDuration * this.fps) - this.frameNumberLeftPosition = - (this.width / allDuration) * frameNumber - if (this.playlistProgressDragging) { - this.$emit('progress-playlist-changed', frameNumber) - } - } - }, + if ( + this.playlistProgressDragging || + this.isFrameNumberVisible || + (!this.progressDragging && + event.target.classList && + (event.target.classList.contains('playlilst-progress') || + event.target.classList.contains('entity-status') || + event.target.classList.contains('playlist-progress-position'))) + ) { + this.currentMouseFrame = this._getPlaylistMouseFrame(event) + const { frameNumber } = this.currentMouseFrame + this.hoverFrame = frameNumber + 1 + const allDuration = Math.round(this.playlistDuration * this.fps) + this.frameNumberLeftPosition = (this.width / allDuration) * frameNumber + if (this.playlistProgressDragging) { + this.$emit('progress-playlist-changed', frameNumber) + } + } + }, onPlaylistProgressClicked(event) { const { frameNumber } = this._getPlaylistMouseFrame(event) @@ -362,9 +356,7 @@ export default { Math.max(this.frameNumberLeftPosition - frameWidth / 2, 0), this.width - frameWidth - 10 ) - const top = this.isFullScreen - ? `-${height + 32}px` - : '42px' + const top = this.isFullScreen ? `-${height + 32}px` : '42px' return { height: `${height}px`, @@ -385,7 +377,7 @@ export default { if (entity.preview_file_extension === 'mp4') { ratio = entity.preview_file_duration / this.playlistDuration } else { - ratio = 2 * this.fps * this.frameDuration / this.playlistDuration + ratio = (2 * this.fps * this.frameDuration) / this.playlistDuration } return ratio * 100 }, diff --git a/src/components/previews/PreviewPlayer.vue b/src/components/previews/PreviewPlayer.vue index 03f5242fc2..1eddbf152a 100644 --- a/src/components/previews/PreviewPlayer.vue +++ b/src/components/previews/PreviewPlayer.vue @@ -1813,7 +1813,8 @@ export default { onModelLoaded() { this.is3DAnimation = this.previewViewer.get3DAnimations().length > 0 if (this.is3DAnimation) { - this.available3DAnimations = this.previewViewer.get3DAnimations() + this.available3DAnimations = this.previewViewer + .get3DAnimations() .map(animation => ({ label: animation, value: animation diff --git a/src/components/previews/PreviewViewer.vue b/src/components/previews/PreviewViewer.vue index dfdc3706b0..85e2ca4df2 100644 --- a/src/components/previews/PreviewViewer.vue +++ b/src/components/previews/PreviewViewer.vue @@ -207,6 +207,7 @@ export default { emits: [ 'duration-changed', 'frame-update', + 'model-loaded', 'play-ended', 'size-changed', 'video-end', diff --git a/src/components/previews/VideoProgress.vue b/src/components/previews/VideoProgress.vue index b379fce17e..41cc9bca74 100644 --- a/src/components/previews/VideoProgress.vue +++ b/src/components/previews/VideoProgress.vue @@ -86,10 +86,7 @@ class="frame-number" :style="frameNumberStyle" v-show=" - isFrameNumberVisible && - hoverFrame > 0 && - !empty && - !progressDragging + isFrameNumberVisible && hoverFrame > 0 && !empty && !progressDragging " > {{ hoverFrame }} @@ -249,9 +246,7 @@ export default { Math.max(this.frameNumberLeftPosition - frameWidth / 2, 0), this.width - frameWidth - 10 ) - const top = this.isFullScreen - ? `-${height + 30}px` - : '0px' + const top = this.isFullScreen ? `-${height + 30}px` : '0px' return { height: `${height}px`, @@ -409,8 +404,7 @@ export default { */ getFrameBackgroundStyle(frame) { if (!frame) return {} - let previewId = this.previewId - let extension = null + const previewId = this.previewId frame = frame - 1 if (this.nbFrames >= 3840) { frame = Math.ceil(frame / Math.ceil(this.nbFrames / 3840)) @@ -441,9 +435,7 @@ export default { Math.max(this.frameNumberLeftPosition - frameWidth / 2, 0), this.width - frameWidth - 10 ) - const top = this.isFullScreen - ? `-${height}px` - : '30px' + const top = this.isFullScreen ? `-${height}px` : '30px' return { height: `${height}px`, From 85ac16d863ac23f93958a8346e85a399b9dc6dd2 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 6 Jan 2025 16:36:04 +0100 Subject: [PATCH 10/12] [qa] Remove template duplicate in vite conf --- vite.config.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/vite.config.js b/vite.config.js index 5cd67fbd99..0a05073802 100644 --- a/vite.config.js +++ b/vite.config.js @@ -16,11 +16,6 @@ export default defineConfig({ build: { sourcemap: true }, - template: { - compilerOptions: { - isCustomElement: (tag) => ['model-viewer'].includes(tag), - } - }, resolve: { extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue'], alias: { From 8ba33cb1e24d120b5be3777a444287500b9a3335 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 6 Jan 2025 17:17:05 +0100 Subject: [PATCH 11/12] [previews] Keep pencil configuration over time --- src/components/mixins/annotation.js | 69 ++++++++++++++----- .../pages/playlists/PlaylistPlayer.vue | 16 +++-- src/components/previews/PreviewPlayer.vue | 22 +++--- 3 files changed, 69 insertions(+), 38 deletions(-) diff --git a/src/components/mixins/annotation.js b/src/components/mixins/annotation.js index 7330f7d5dd..3648e88f30 100644 --- a/src/components/mixins/annotation.js +++ b/src/components/mixins/annotation.js @@ -9,6 +9,7 @@ import { markRaw } from 'vue' import clipboard from '@/lib/clipboard' import { formatFullDate } from '@/lib/time' +import localPreferences from '@/lib/preferences' /* Monkey patch needed to have text background including the padding. */ if (fabric) { @@ -41,7 +42,10 @@ export const annotationMixin = { updates: [], isShowingPalette: false, isShowingPencilPalette: false, - notSave: false + notSave: false, + pencilColor: '#ff3860', + pencilWidth: 'big', + textColor: '#ff3860', } }, @@ -612,57 +616,88 @@ export const annotationMixin = { /* * Enable / disabl showing pencil palette flag. */ - onPickPencil() { + onPickPencilWidth() { this.isShowingPencilPalette = !this.isShowingPencilPalette }, /* - * Enable / disabl showing color palette flag. + * Enable / disable showing color palette flag. */ - onPickColor() { + onPickPencilColor() { this.isShowingPalette = !this.isShowingPalette }, /* - * When a drawing color is changed, store it in local state. + * Enable / disable showing color palette flag. */ - onChangeColor(color) { - this.color = color + onPickTextColor() { + this.isShowingPalette = !this.isShowingPalette + }, + + /* + * When a drawing color is changed, change fabric configuration and save + * the new color in the local preferences. + */ + onChangePencilColor(color) { + this.pencilColor = color this._resetColor() this.isShowingPalette = false + localPreferences.setPreference('player:pencil-color', this.pencilColor) }, /* - * When a text color is changed, store it in local state. + * When a pencil width is changed, change fabric configuration and save + * the new width in the local preferences. */ - onChangeTextColor(color) { - this.textColor = color + onChangePencilWidth(pencil) { + this.pencilWidth = pencil + this._resetPencil() this.isShowingPalette = false + localPreferences.setPreference('player:pencil-width', this.pencilWidth) }, /* - * When a pencil is changed, store it in local state. + * When a text color is changed, change fabric configuration and save + * the new color in the local preferences. */ - onChangePencil(pencil) { - this.pencil = pencil - this._resetPencil() + onChangeTextColor(newValue) { + this.textColor = newValue this.isShowingPalette = false + localPreferences.setPreference('player:text-color', this.textColor) }, _resetColor() { - this.fabricCanvas.freeDrawingBrush.color = this.color + if (!this.fabricCanvas) return + this.fabricCanvas.freeDrawingBrush.color = this.pencilColor }, _resetPencil() { + if (!this.fabricCanvas) return const converter = { big: 4, medium: 2, small: 1 } - const strokeWidth = converter[this.pencil] + const strokeWidth = converter[this.pencilWidth] this.fabricCanvas.freeDrawingBrush.width = strokeWidth }, + + /* + * Reset pencil configuration to the last saved preferences. + */ + resetPencilConfiguration() { + this.pencilColor = + localPreferences.getPreference('player:pencil-color') || '#ff3860' + this.textColor = + localPreferences.getPreference('player:text-color') || '#ff3860' + this.pencilWidth = + localPreferences.getPreference('player:pencil-width') || 'big' + + this._resetColor() + this._resetPencil() + }, + /* * Enable / disable the drawing mode. Differentiate text from path drawing. */ @@ -987,7 +1022,7 @@ export const annotationMixin = { this.fabricCanvas.on('mouse:move', this.onCanvasMouseMoved) this.fabricCanvas.on('mouse:down', this.onCanvasClicked) this.fabricCanvas.on('mouse:up', this.onCanvasReleased) - this.fabricCanvas.freeDrawingBrush.color = this.color + this.fabricCanvas.freeDrawingBrush.color = this.pencilColor this.fabricCanvas.freeDrawingBrush.width = 4 fabric.Group.prototype._controlsVisibility = { diff --git a/src/components/pages/playlists/PlaylistPlayer.vue b/src/components/pages/playlists/PlaylistPlayer.vue index 5166fdf2ca..cde8eb52ac 100644 --- a/src/components/pages/playlists/PlaylistPlayer.vue +++ b/src/components/pages/playlists/PlaylistPlayer.vue @@ -688,7 +688,7 @@
@@ -704,16 +704,16 @@
@@ -1154,6 +1154,8 @@ export default { this.productionBackgrounds.find(this.isDefaultBackground) || null this.onObjectBackgroundSelected() this.isMounted = true + + this.resetPencilConfiguration() }, computed: { diff --git a/src/components/previews/PreviewPlayer.vue b/src/components/previews/PreviewPlayer.vue index 1eddbf152a..a6f4b6a9bc 100644 --- a/src/components/previews/PreviewPlayer.vue +++ b/src/components/previews/PreviewPlayer.vue @@ -294,7 +294,7 @@ >
@@ -315,15 +315,15 @@ v-show="isDrawing && (!light || fullScreen)" >
@@ -665,7 +665,6 @@ export default { currentFrame: 0, currentIndex: 1, fullScreen: false, - color: '#ff3860', currentBackground: null, currentTime: '00:00:00:00', currentTimeRaw: 0, @@ -749,6 +748,7 @@ export default { this.productionBackgrounds.find(this.isDefaultBackground) || null this.onObjectBackgroundSelected() } + this.resetPencilConfiguration() }, beforeUnmount() { @@ -1484,12 +1484,6 @@ export default { this.deleteSelection() }, - onChangeColor(newValue) { - this.color = newValue - this.fabricCanvas.freeDrawingBrush.color = this.color - this.isShowingPalette = false - }, - onPencilAnnotateClicked() { this.clearFocus() if (this.isDrawing) { From bdd560b30901cf7528398dcfc85ab88eaa93de48 Mon Sep 17 00:00:00 2001 From: Frank Rousseau Date: Mon, 6 Jan 2025 17:45:29 +0100 Subject: [PATCH 12/12] [qa] code scrub --- src/components/mixins/annotation.js | 3 +-- src/components/previews/VideoProgress.vue | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/mixins/annotation.js b/src/components/mixins/annotation.js index 3648e88f30..82b0824f42 100644 --- a/src/components/mixins/annotation.js +++ b/src/components/mixins/annotation.js @@ -45,7 +45,7 @@ export const annotationMixin = { notSave: false, pencilColor: '#ff3860', pencilWidth: 'big', - textColor: '#ff3860', + textColor: '#ff3860' } }, @@ -682,7 +682,6 @@ export const annotationMixin = { this.fabricCanvas.freeDrawingBrush.width = strokeWidth }, - /* * Reset pencil configuration to the last saved preferences. */ diff --git a/src/components/previews/VideoProgress.vue b/src/components/previews/VideoProgress.vue index 41cc9bca74..30da632f91 100644 --- a/src/components/previews/VideoProgress.vue +++ b/src/components/previews/VideoProgress.vue @@ -239,8 +239,7 @@ export default { frameNumberStyle() { const frameHeight = 100 const height = frameHeight + 30 - let frameWidth = 150 - frameWidth = Math.ceil(frameHeight * this.videoRatio) + const frameWidth = Math.ceil(frameHeight * this.videoRatio) const width = frameWidth + 10 const left = Math.min( Math.max(this.frameNumberLeftPosition - frameWidth / 2, 0),