From 8ea5c3a283b3ef05d6b87c6d4b5d3045aac900b6 Mon Sep 17 00:00:00 2001 From: morethanwords Date: Wed, 28 Oct 2020 20:20:01 +0200 Subject: [PATCH] Video autoplay --- src/components/wrappers.ts | 62 ++++++++++++++---------- src/lib/mediaPlayer.ts | 76 +++++++++++++----------------- src/scss/partials/_chatBubble.scss | 23 ++++++++- 3 files changed, 91 insertions(+), 70 deletions(-) diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 29993529..f6cc20d9 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -22,6 +22,8 @@ import { renderImageFromUrl } from './misc'; import PollElement from './poll'; import ProgressivePreloader from './preloader'; +const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB + export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTail, isOut, middleware, lazyLoadQueue, noInfo, group}: { doc: MyDocument, container?: HTMLDivElement, @@ -35,22 +37,28 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai noInfo?: true, group?: string, }) { + const isAlbumItem = !(boxWidth && boxHeight); + const canAutoplay = doc.type != 'video' || (doc.size <= MAX_VIDEO_AUTOPLAY_SIZE && !isAlbumItem); + let spanTime: HTMLElement; + if(!noInfo) { if(doc.type != 'round') { - let span: HTMLSpanElement, spanPlay: HTMLSpanElement; - - span = document.createElement('span'); - span.classList.add('video-time'); - container.append(span); + spanTime = document.createElement('span'); + spanTime.classList.add('video-time'); + container.append(spanTime); if(doc.type != 'gif') { - span.innerText = (doc.duration + '').toHHMMSS(false); - - spanPlay = document.createElement('span'); - spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center'); - container.append(spanPlay); + spanTime.innerText = (doc.duration + '').toHHMMSS(false); + + if(canAutoplay) { + spanTime.classList.add('tgico', 'can-autoplay'); + } else { + const spanPlay = document.createElement('span'); + spanPlay.classList.add('video-play', 'tgico-largeplay', 'btn-circle', 'position-center'); + container.append(spanPlay); + } } else { - span.innerText = 'GIF'; + spanTime.innerText = 'GIF'; } } } @@ -132,7 +140,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai let img: HTMLImageElement; if(message) { - if(doc.type == 'video' && doc.thumbs?.length) { + if(!canAutoplay && doc.thumbs?.length) { return wrapPhoto(doc, message, container, boxWidth, boxHeight, withTail, isOut, lazyLoadQueue, middleware); } @@ -199,18 +207,18 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai }, {once: true}); */ await promise; + + if(middleware && !middleware()) { + return; + } } else if(doc.supportsStreaming) { preloader = new ProgressivePreloader(null, false, false, 'prepend'); preloader.attach(container, false, null); - video.addEventListener('canplay', () => { + video.addEventListener(isSafari ? 'timeupdate' : 'canplay', () => { preloader.detach(); }, {once: true}); } - - if(middleware && !middleware()) { - return; - } - + //console.log('loaded doc:', doc, doc.url, container); const deferred = deferredPromise(); @@ -224,7 +232,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai /* if(!video.paused) { video.pause(); } */ - if(doc.type == 'gif' && group) { + if(doc.type != 'round' && group) { animationIntersector.addAnimation(video, group); } @@ -235,6 +243,12 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai }, {once: true}); //} + if(doc.type == 'video') { + video.addEventListener('timeupdate', () => { + spanTime.innerText = (video.duration - video.currentTime + '').toHHMMSS(false); + }); + } + video.addEventListener('error', (e) => { deferred.resolve(); /* console.error('video error', e, video.src); @@ -253,15 +267,15 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai container.append(video); } */ - if(doc.type == 'gif'/* || true */) { + if(doc.type == 'round') { + video.dataset.ckin = 'circle'; + video.dataset.overlay = '1'; + new VideoPlayer(video); + } else { video.muted = true; video.loop = true; //video.play(); video.autoplay = true; - } else if(doc.type == 'round') { - video.dataset.ckin = 'circle'; - video.dataset.overlay = '1'; - new VideoPlayer(video); } return deferred; diff --git a/src/lib/mediaPlayer.ts b/src/lib/mediaPlayer.ts index 1b562431..007e9698 100644 --- a/src/lib/mediaPlayer.ts +++ b/src/lib/mediaPlayer.ts @@ -318,15 +318,15 @@ export default class VideoPlayer { const html = this.buildControls(); player.insertAdjacentHTML('beforeend', html); - let updateInterval = 0; let elapsed = 0; let prevTime = 0; + let timeDuration: HTMLElement; if(skin === 'default') { const toggle = player.querySelectorAll('.toggle') as NodeListOf; const fullScreenButton = player.querySelector('.fullscreen') as HTMLElement; - var timeElapsed = player.querySelector('#time-elapsed'); - var timeDuration = player.querySelector('#time-duration') as HTMLElement; + const timeElapsed = player.querySelector('#time-elapsed'); + timeDuration = player.querySelector('#time-duration') as HTMLElement; timeDuration.innerHTML = String(video.duration | 0).toHHMMSS(); const volumeDiv = document.createElement('div'); @@ -451,10 +451,6 @@ export default class VideoPlayer { /* video.addEventListener('play', () => { }); */ - video.addEventListener('pause', () => { - clearInterval(updateInterval); - }); - video.addEventListener('dblclick', () => { if(isTouchSupported) { return; @@ -470,16 +466,20 @@ export default class VideoPlayer { 'webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange'.split(' ').forEach(eventName => { player.addEventListener(eventName, this.onFullScreen, false); }); + + video.addEventListener('timeupdate', () => { + timeElapsed.innerHTML = String(video.currentTime | 0).toHHMMSS(); + }); } else if(skin === 'circle') { const wrapper = document.createElement('div'); wrapper.classList.add('circle-time-left'); video.parentNode.insertBefore(wrapper, video); wrapper.innerHTML = '
'; - var circle = player.querySelector('.progress-ring__circle') as SVGCircleElement; + const circle = player.querySelector('.progress-ring__circle') as SVGCircleElement; const radius = circle.r.baseVal.value; - var circumference = 2 * Math.PI * radius; - var timeDuration = player.querySelector('.circle-time') as HTMLElement; + const circumference = 2 * Math.PI * radius; + timeDuration = player.querySelector('.circle-time') as HTMLElement; const iconVolume = player.querySelector('.iconVolume') as HTMLDivElement; circle.style.strokeDasharray = circumference + ' ' + circumference; circle.style.strokeDashoffset = '' + circumference; @@ -505,6 +505,28 @@ export default class VideoPlayer { video.addEventListener('pause', () => { iconVolume.style.display = ''; }); + + let updateInterval = 0; + video.addEventListener('timeupdate', () => { + clearInterval(updateInterval); + + let elapsed = 0; + let prevTime = 0; + + updateInterval = window.setInterval(() => { + if(video.currentTime != prevTime) { + elapsed = video.currentTime; // Update if getCurrentTime was changed + prevTime = video.currentTime; + } + + const offset = circumference - elapsed / video.duration * circumference; + circle.style.strokeDashoffset = '' + offset; + if(video.paused) clearInterval(updateInterval); + }, 20); + + const timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS(); + if(timeLeft != '0') timeDuration.innerHTML = timeLeft; + }); } video.addEventListener('play', () => { @@ -522,14 +544,6 @@ export default class VideoPlayer { timeDuration.innerHTML = String(Math.round(video.duration)).toHHMMSS(); }); } - - video.addEventListener('timeupdate', () => { - if(skin == 'default') { - timeElapsed.innerHTML = String(video.currentTime | 0).toHHMMSS(); - } - - updateInterval = this.handleProgress(timeDuration, circumference, circle, updateInterval); - }); } public togglePlay(stop?: boolean) { @@ -547,32 +561,6 @@ export default class VideoPlayer { //this.wrapper.classList.toggle('is-playing', !this.video.paused); } - private handleProgress(timeDuration: HTMLElement, circumference: number, circle: SVGCircleElement, updateInterval: number) { - const {video, skin} = this; - - clearInterval(updateInterval); - let elapsed = 0; - let prevTime = 0; - - if(skin === 'circle') { - updateInterval = window.setInterval(() => { - if(video.currentTime != prevTime) { - elapsed = video.currentTime; // Update if getCurrentTime was changed - prevTime = video.currentTime; - } - - const offset = circumference - elapsed / video.duration * circumference; - circle.style.strokeDashoffset = '' + offset; - if(video.paused) clearInterval(updateInterval); - }, 20); - - const timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS(); - if(timeLeft != '0') timeDuration.innerHTML = timeLeft; - - return updateInterval; - } - } - private buildControls() { const skin = this.skin; if(skin === 'default') { diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 86ed5030..29abd65d 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -519,6 +519,10 @@ $bubble-margin: .25rem; display: none; } } + + .preloader-container { + z-index: 1; + } } &:not(.sticker) { @@ -537,6 +541,12 @@ $bubble-margin: .25rem; height: 100%; } + html.is-safari &:not(.round) { + img:not(.emoji), video { + border-radius: inherit; + } + } + &.is-album { .attachment { max-width: unquote('min(451px, 100%)'); @@ -1000,7 +1010,7 @@ $bubble-margin: .25rem; } } - span.video-time { + .video-time { position: absolute; top: 3px; left: 3px; @@ -1014,9 +1024,18 @@ $bubble-margin: .25rem; align-items: center; cursor: pointer; user-select: none; + + &.can-autoplay:after { + content: $tgico-nosound; + + // * same as .iconVolume + padding: 0 1px 0 3px; + font-size: 1.25rem; + color: #fff; + } } - span.video-play { + .video-play { background-color: var(--message-time-background); color: #fff; text-align: center;