Browse Source

Fix round videos

master
Eduard Kuzmenko 4 years ago
parent
commit
8dc136538a
  1. 8
      src/components/appMediaPlaybackController.ts
  2. 2
      src/components/preloader.ts
  3. 65
      src/components/wrappers.ts
  4. 21
      src/helpers/animation.ts
  5. 12
      src/helpers/files.ts
  6. 2
      src/lib/cacheStorage.ts
  7. 46
      src/lib/mediaPlayer.ts
  8. 4
      src/scss/partials/_avatar.scss
  9. 7
      src/scss/partials/_chatBubble.scss

8
src/components/appMediaPlaybackController.ts

@ -4,6 +4,7 @@ import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager"; @@ -4,6 +4,7 @@ import appDocsManager, {MyDocument} from "../lib/appManagers/appDocsManager";
import { CancellablePromise, deferredPromise } from "../helpers/cancellablePromise";
import { isSafari } from "../helpers/userAgent";
import { MOUNT_CLASS_TO } from "../lib/mtproto/mtproto_config";
import { isInDOM } from "../helpers/dom";
// TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда
@ -177,6 +178,13 @@ class AppMediaPlaybackController { @@ -177,6 +178,13 @@ class AppMediaPlaybackController {
}
onPause = (e: Event) => {
/* const target = e.target as HTMLMediaElement;
if(!isInDOM(target)) {
this.container.append(target);
target.play();
return;
} */
rootScope.broadcast('audio_pause');
};

2
src/components/preloader.ts

@ -237,6 +237,8 @@ export default class ProgressivePreloader { @@ -237,6 +237,8 @@ export default class ProgressivePreloader {
}
public detach() {
//return;
this.detached = true;
//return;

65
src/components/wrappers.ts

@ -1,3 +1,4 @@ @@ -1,3 +1,4 @@
import type Chat from './chat/chat';
import { getEmojiToneIndex } from '../emoji';
import { readBlobAsText } from '../helpers/blob';
import { deferredPromise } from '../helpers/cancellablePromise';
@ -7,7 +8,6 @@ import { formatBytes } from '../helpers/number'; @@ -7,7 +8,6 @@ import { formatBytes } from '../helpers/number';
import { isAppleMobile, isSafari } from '../helpers/userAgent';
import { PhotoSize } from '../layer';
import appDocsManager, { MyDocument } from "../lib/appManagers/appDocsManager";
import { DownloadBlob } from '../lib/appManagers/appDownloadManager';
import appMessagesManager from '../lib/appManagers/appMessagesManager';
import appPhotosManager, { MyPhoto } from '../lib/appManagers/appPhotosManager';
import LottieLoader from '../lib/lottieLoader';
@ -27,9 +27,9 @@ import './middleEllipsis'; @@ -27,9 +27,9 @@ import './middleEllipsis';
import { nextRandomInt } from '../helpers/random';
import RichTextProcessor from '../lib/richtextprocessor';
import appImManager from '../lib/appManagers/appImManager';
import Chat from './chat/chat';
import { SearchSuperContext } from './appSearchSuper.';
import rootScope from '../lib/rootScope';
import { onVideoLoad } from '../helpers/files';
const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB
@ -108,28 +108,28 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -108,28 +108,28 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
video.remove();
} */
const video = document.createElement('video');
const video = /* doc.type === 'round' ? appMediaPlaybackController.addMedia(message.peerId, doc, message.mid) as HTMLVideoElement : */document.createElement('video');
video.classList.add('media-video');
video.muted = true;
video.setAttribute('playsinline', 'true');
if(doc.type === 'round') {
//video.muted = true;
//video.classList.add('z-depth-1');
const globalVideo = appMediaPlaybackController.addMedia(message.peerId, doc, message.mid);
video.classList.add('z-depth-1');
video.addEventListener('canplay', () => {
if(globalVideo.currentTime > 0) {
onVideoLoad(video).then(() => {
if(globalVideo.currentTime !== globalVideo.duration) {
video.currentTime = globalVideo.currentTime;
}
if(!globalVideo.paused) {
// с закоментированными настройками - хром выключал видео при скролле, для этого нужно было включить видео - выйти из диалога, зайти заново и проскроллить вверх
/* video.autoplay = true;
video.loop = false; */
//video.autoplay = true;
//video.loop = false;
video.play();
}
}, {once: true});
});
const clear = () => {
//console.log('clearing video');
@ -160,14 +160,18 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -160,14 +160,18 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
const onVideoPlay = (e: Event) => {
//console.log('video play event', e);
globalVideo.currentTime = video.currentTime;
globalVideo.play();
if(globalVideo.paused) {
globalVideo.currentTime = video.currentTime;
globalVideo.play();
}
};
// * this will fire when video unmounts
const onVideoPause = (e: Event) => {
//console.trace('video pause event', e);
if(isInDOM(video)) {
globalVideo.pause();
globalVideo.currentTime = video.currentTime;
} else {
clear();
}
@ -229,22 +233,25 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -229,22 +233,25 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
return;
}
let loadPromise: Promise<any> = Promise.resolve();
let preloader: ProgressivePreloader;
if(message?.media?.preloader) { // means upload
preloader = message.media.preloader as ProgressivePreloader;
preloader.attach(container, false);
} else if(!doc.downloaded && !doc.supportsStreaming) {
const promise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId);
const promise = loadPromise = appDocsManager.downloadDoc(doc, lazyLoadQueue?.queueId);
preloader = new ProgressivePreloader({
attachMethod: 'prepend'
});
preloader.attach(container, true, promise);
await promise;
//if(doc.type !== 'round') {
await promise;
if(middleware && !middleware()) {
return;
}
if(middleware && !middleware()) {
return;
}
//}
} else if(doc.supportsStreaming) {
preloader = new ProgressivePreloader({
cancelable: false,
@ -255,13 +262,17 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -255,13 +262,17 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
preloader.detach();
}, {once: true});
}
/* if(doc.type === 'round') {
return;
} */
//console.log('loaded doc:', doc, doc.url, container);
const deferred = deferredPromise<void>();
//if(doc.type == 'gif'/* || true */) {
video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => {
onVideoLoad(video).then(() => {
/* if(!video.paused) {
video.pause();
} */
@ -273,7 +284,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -273,7 +284,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
//setTimeout(() => {
deferred.resolve();
//}, 5000);
}, {once: true});
});
//}
if(doc.type === 'video') {
@ -286,22 +297,24 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai @@ -286,22 +297,24 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai
deferred.resolve();
});
renderImageFromUrl(video, doc.url);
if(doc.type === 'round') {
video.dataset.ckin = 'circle';
video.dataset.overlay = '1';
new VideoPlayer(video);
} else {
if(doc.type !== 'round') {
video.muted = true;
video.loop = true;
//video.play();
video.autoplay = true;
}
renderImageFromUrl(video, doc.url);
return deferred;
return Promise.all([loadPromise, deferred]);
};
if(doc.type === 'round') {
video.dataset.ckin = 'circle';
video.dataset.overlay = '1';
new VideoPlayer(video, undefined, undefined, doc.duration);
}
/* if(doc.size >= 20e6 && !doc.downloaded) {
let downloadDiv = document.createElement('div');
downloadDiv.classList.add('download');

21
src/helpers/animation.ts

@ -11,6 +11,22 @@ interface AnimationInstance { @@ -11,6 +11,22 @@ interface AnimationInstance {
type AnimationInstanceKey = any;
const instances: Map<AnimationInstanceKey, AnimationInstance> = new Map();
export function createAnimationInstance(key: AnimationInstanceKey) {
cancelAnimationByKey(key);
const instance: AnimationInstance = {
isCancelled: false,
deferred: deferredPromise<void>()
};
instances.set(key, instance);
instance.deferred.then(() => {
instances.delete(key);
});
return instance;
}
export function getAnimationInstance(key: AnimationInstanceKey) {
return instances.get(key);
}
@ -20,15 +36,12 @@ export function cancelAnimationByKey(key: AnimationInstanceKey) { @@ -20,15 +36,12 @@ export function cancelAnimationByKey(key: AnimationInstanceKey) {
if(instance) {
instance.isCancelled = true;
instance.deferred.resolve();
instances.delete(key);
}
}
export function animateSingle(tick: Function, key: AnimationInstanceKey, instance?: AnimationInstance) {
if(!instance) {
cancelAnimationByKey(key);
instance = { isCancelled: false, deferred: deferredPromise<void>() };
instances.set(key, instance);
instance = createAnimationInstance(key);
}
fastRaf(() => {

12
src/helpers/files.ts

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
import { pause } from "./schedulers";
import { isAppleMobile } from "./userAgent";
export function preloadVideo(url: string): Promise<HTMLVideoElement> {
return new Promise((resolve, reject) => {
@ -35,4 +36,15 @@ export async function createPosterForVideo(url: string): Promise<Blob | undefine @@ -35,4 +36,15 @@ export async function createPosterForVideo(url: string): Promise<Blob | undefine
pause(2000) as Promise<undefined>,
createPosterFromVideo(video),
]);
}
export function onVideoLoad(video: HTMLVideoElement) {
return new Promise<void>((resolve) => {
if(video.readyState >= video.HAVE_METADATA) {
resolve();
return;
}
video.addEventListener(isAppleMobile ? 'loadeddata' : 'canplay', () => resolve(), {once: true});
});
}

2
src/lib/cacheStorage.ts

@ -82,7 +82,7 @@ export default class CacheStorageController { @@ -82,7 +82,7 @@ export default class CacheStorageController {
reject();
//console.warn('CACHESTORAGE TIMEOUT');
rejected = true;
}, 5e3);
}, 15e3);
try {
const cache = await this.openDatabase();

46
src/lib/mediaPlayer.ts

@ -1,8 +1,10 @@ @@ -1,8 +1,10 @@
import { cancelEvent } from "../helpers/dom";
import { attachClickEvent, cancelEvent } from "../helpers/dom";
import appMediaPlaybackController from "../components/appMediaPlaybackController";
import { isAppleMobile } from "../helpers/userAgent";
import { isTouchSupported } from "../helpers/touchSupport";
import RangeSelector from "../components/rangeSelector";
import { animateSingle } from "../helpers/animation";
import { onVideoLoad } from "../helpers/files";
type SUPEREVENT = MouseEvent | TouchEvent;
@ -167,7 +169,7 @@ export default class VideoPlayer { @@ -167,7 +169,7 @@ export default class VideoPlayer {
/* private videoParent: HTMLElement;
private videoWhichChild: number; */
constructor(public video: HTMLVideoElement, play = false, streamable = false) {
constructor(public video: HTMLVideoElement, play = false, streamable = false, duration?: number) {
this.wrapper = document.createElement('div');
this.wrapper.classList.add('ckin__player');
@ -176,7 +178,7 @@ export default class VideoPlayer { @@ -176,7 +178,7 @@ export default class VideoPlayer {
this.skin = video.dataset.ckin ?? 'default';
this.stylePlayer();
this.stylePlayer(duration);
if(this.skin === 'default') {
let controls = this.wrapper.querySelector('.default__controls.ckin__controls') as HTMLDivElement;
@ -199,7 +201,7 @@ export default class VideoPlayer { @@ -199,7 +201,7 @@ export default class VideoPlayer {
}
}
private stylePlayer() {
private stylePlayer(initDuration: number) {
const {wrapper: player, video, skin} = this;
player.classList.add(skin);
@ -365,11 +367,12 @@ export default class VideoPlayer { @@ -365,11 +367,12 @@ export default class VideoPlayer {
const circle = player.querySelector('.progress-ring__circle') as SVGCircleElement;
const radius = circle.r.baseVal.value;
const circumference = 2 * Math.PI * radius;
timeDuration = player.querySelector('.circle-time') as HTMLElement;
const iconVolume = player.querySelector('.iconVolume') as HTMLDivElement;
timeDuration = wrapper.firstElementChild as HTMLElement;
const iconVolume = wrapper.lastElementChild as HTMLElement;
circle.style.strokeDasharray = circumference + ' ' + circumference;
circle.style.strokeDashoffset = '' + circumference;
circle.addEventListener('click', () => {
attachClickEvent(circle as any, (e) => {
cancelEvent(e);
this.togglePlay();
});
@ -377,24 +380,33 @@ export default class VideoPlayer { @@ -377,24 +380,33 @@ export default class VideoPlayer {
const offset = circumference - video.currentTime / video.duration * circumference;
circle.style.strokeDashoffset = '' + offset;
if(video.paused) {
clearInterval(updateInterval);
}
return !video.paused;
};
const timeUpdate = () => {
const timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS();
if(timeLeft != '0') timeDuration.innerHTML = timeLeft;
};
video.addEventListener('play', () => {
iconVolume.style.display = 'none';
updateInterval = window.setInterval(update, 20);
animateSingle(update, circle);
update();
});
video.addEventListener('pause', () => {
iconVolume.style.display = '';
});
let updateInterval = 0;
video.addEventListener('timeupdate', () => {
const timeLeft = String((video.duration - video.currentTime) | 0).toHHMMSS();
if(timeLeft != '0') timeDuration.innerHTML = timeLeft;
timeUpdate();
});
onVideoLoad(video).then(() => {
if(!video.currentTime || video.currentTime === video.duration) return;
update();
timeUpdate();
});
}
@ -406,10 +418,10 @@ export default class VideoPlayer { @@ -406,10 +418,10 @@ export default class VideoPlayer {
this.wrapper.classList.remove('is-playing');
});
if(video.duration > 0) {
timeDuration.innerHTML = String(Math.round(video.duration)).toHHMMSS();
if(video.duration || initDuration) {
timeDuration.innerHTML = String(Math.round(video.duration || initDuration)).toHHMMSS();
} else {
video.addEventListener('loadeddata', () => {
onVideoLoad(video).then(() => {
timeDuration.innerHTML = String(Math.round(video.duration)).toHHMMSS();
});
}

4
src/scss/partials/_avatar.scss

@ -78,8 +78,8 @@ avatar-element { @@ -78,8 +78,8 @@ avatar-element {
}
&.emoji {
width: calc(1.125rem / var(--multiplier));
height: calc(1.125rem / var(--multiplier));
width: calc(1.125rem / var(--multiplier)) !important;
height: calc(1.125rem / var(--multiplier)) !important;
vertical-align: middle !important;
}
}

7
src/scss/partials/_chatBubble.scss

@ -667,13 +667,6 @@ $bubble-margin: .25rem; @@ -667,13 +667,6 @@ $bubble-margin: .25rem;
}
}
&.round {
.attachment {
max-height: 200px;
max-width: 200px;
}
}
.web {
padding-top: 1px;
margin: 4px 0 -5px 1px;

Loading…
Cancel
Save