import { MTDocument } from "../types"; import { $rootScope } from "../lib/utils"; import appMessagesManager from "../lib/appManagers/appMessagesManager"; import appDocsManager from "../lib/appManagers/appDocsManager"; import opusDecodeController from "../lib/opusDecodeController"; // TODO: если удалить сообщение, и при этом аудио будет играть - оно не остановится, и можно будет по нему перейти вникуда class AppAudio { private container: HTMLElement; private audios: {[mid: string]: HTMLAudioElement} = {}; private playingAudio: HTMLAudioElement; public willBePlayedAudio: HTMLAudioElement; private prevMid: number; private nextMid: number; constructor() { this.container = document.createElement('div'); //this.container.style.cssText = 'position: absolute; top: -10000px; left: -10000px;'; this.container.style.cssText = 'display: none;'; document.body.append(this.container); } public addAudio(doc: MTDocument, mid: number) { if(this.audios[mid]) return this.audios[mid]; const audio = document.createElement('audio'); const source = document.createElement('source'); source.type = doc.type == 'voice' && !opusDecodeController.isPlaySupported() ? 'audio/wav' : doc.mime_type; audio.autoplay = false; audio.volume = 1; audio.append(source); audio.addEventListener('playing', (e) => { if(this.playingAudio != audio) { if(this.playingAudio && !this.playingAudio.paused) { this.playingAudio.pause(); } this.playingAudio = audio; this.loadSiblingsAudio(doc.type as 'voice' | 'audio', mid); } // audio_pause не успеет сработать без таймаута setTimeout(() => { $rootScope.$broadcast('audio_play', {doc, mid}); }, 0); }); audio.addEventListener('pause', this.onPause); audio.addEventListener('ended', this.onEnded); const onError = (e: Event) => { if(this.nextMid == mid) { this.loadSiblingsAudio(doc.type as 'voice' | 'audio', mid).then(() => { if(this.nextMid && this.audios[this.nextMid]) { this.audios[this.nextMid].play(); } }) } }; audio.addEventListener('error', onError); const downloadPromise: Promise = !doc.supportsStreaming ? appDocsManager.downloadDocNew(doc.id) : Promise.resolve(); downloadPromise.then(() => { this.container.append(audio); source.src = doc.url; }, onError); return this.audios[mid] = audio; } onPause = (e: Event) => { $rootScope.$broadcast('audio_pause'); }; onEnded = (e: Event) => { this.onPause(e); if(this.nextMid) { this.audios[this.nextMid].play(); } }; private loadSiblingsAudio(type: 'voice' | 'audio', mid: number) { const audio = this.playingAudio; const message = appMessagesManager.getMessage(mid); this.prevMid = this.nextMid = 0; return appMessagesManager.getSearch(message.peerID, '', { _: type == 'audio' ? 'inputMessagesFilterMusic' : 'inputMessagesFilterVoice' }, mid, 3, 0, 2).then(value => { if(this.playingAudio != audio) { return; } for(let m of value.history) { if(m > mid) { this.nextMid = m; } else if(m < mid) { this.prevMid = m; break; } } [this.prevMid, this.nextMid].filter(Boolean).forEach(mid => { const message = appMessagesManager.getMessage(mid); this.addAudio(message.media.document, mid); }); //console.log('loadSiblingsAudio', audio, type, mid, value, this.prevMid, this.nextMid); }); } public toggle() { if(!this.playingAudio) return; if(this.playingAudio.paused) { this.playingAudio.play(); } else { this.playingAudio.pause(); } } public pause() { if(!this.playingAudio || this.playingAudio.paused) return; this.playingAudio.pause(); } public willBePlayed(audio: HTMLAudioElement) { this.willBePlayedAudio = audio; } public audioExists(mid: number) { return !!this.audios[mid]; } } const appAudio = new AppAudio(); // @ts-ignore if(process.env.NODE_ENV != 'production') { (window as any).appAudio = appAudio; } export default appAudio;