Vmeda.Online/src/core/classes/element-controllers/MediaElementController.ts
2023-02-22 15:12:25 +01:00

179 lines
5.3 KiB
TypeScript

// (C) Copyright 2015 Moodle Pty Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { CoreUtils } from '@services/utils/utils';
import { ElementController } from './ElementController';
import { CorePromisedValue } from '@classes/promised-value';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreMedia } from '@singletons/media';
import { AddonFilterMediaPluginVideoJS, VIDEO_JS_PLAYER_CREATED } from '@addons/filter/mediaplugin/services/videojs';
import type { VideoJSPlayer } from 'video.js';
/**
* Wrapper class to control the interactivity of a media element.
*/
export class MediaElementController extends ElementController {
private media: HTMLMediaElement;
private autoplay: boolean;
private playing?: boolean;
private playListener?: () => void;
private pauseListener?: () => void;
private jsPlayer = new CorePromisedValue<VideoJSPlayer | null>();
private jsPlayerListener?: CoreEventObserver;
private shouldEnable = false;
private shouldDisable = false;
constructor(media: HTMLMediaElement, enabled: boolean) {
super(enabled);
this.media = media;
this.autoplay = media.autoplay;
media.autoplay = false;
this.initJSPlayer(media);
enabled && this.onEnabled();
}
/**
* @inheritdoc
*/
async onEnabled(): Promise<void> {
this.shouldEnable = true;
this.shouldDisable = false;
const jsPlayer = await this.jsPlayer;
if (!this.shouldEnable || this.destroyed) {
return;
}
const ready = this.playing ?? this.autoplay
? (jsPlayer ?? this.media).play()
: Promise.resolve();
try {
await ready;
this.addPlaybackEventListeners(jsPlayer);
} catch (error) {
CoreUtils.logUnhandledError('Error enabling media element', error);
}
}
/**
* @inheritdoc
*/
async onDisabled(): Promise<void> {
this.shouldDisable = true;
this.shouldEnable = false;
const jsPlayer = await this.jsPlayer;
if (!this.shouldDisable || this.destroyed) {
return;
}
this.removePlaybackEventListeners(jsPlayer);
(jsPlayer ?? this.media).pause();
}
/**
* @inheritdoc
*/
async onDestroy(): Promise<void> {
const jsPlayer = await this.jsPlayer;
this.removePlaybackEventListeners(jsPlayer);
jsPlayer?.dispose();
}
/**
* Init JS Player instance.
*
* @param media Media element.
*/
private async initJSPlayer(media: HTMLMediaElement): Promise<void> {
if (!CoreMedia.mediaUsesJavascriptPlayer(media)) {
this.jsPlayer.resolve(null);
return;
}
const player = await this.searchJSPlayer();
if (!player) {
this.jsPlayerListener = CoreEvents.on(VIDEO_JS_PLAYER_CREATED, ({ element, player }) => {
if (element !== media) {
return;
}
this.jsPlayerListener?.off();
this.jsPlayer.resolve(player);
});
return;
}
this.jsPlayer.resolve(player);
}
/**
* Start listening playback events.
*
* @param jsPlayer Javascript player instance (if any).
*/
private addPlaybackEventListeners(jsPlayer: VideoJSPlayer | null): void {
if (jsPlayer) {
jsPlayer.on('play', this.playListener = () => this.playing = true);
jsPlayer.on('pause', this.pauseListener = () => this.playing = false);
} else {
this.media.addEventListener('play', this.playListener = () => this.playing = true);
this.media.addEventListener('pause', this.pauseListener = () => this.playing = false);
}
}
/**
* Stop listening playback events.
*
* @param jsPlayer Javascript player instance (if any).
*/
private removePlaybackEventListeners(jsPlayer: VideoJSPlayer | null): void {
if (jsPlayer) {
this.playListener && jsPlayer.off('play', this.playListener);
this.pauseListener && jsPlayer.off('pause', this.pauseListener);
} else {
this.playListener && this.media.removeEventListener('play', this.playListener);
this.pauseListener && this.media.removeEventListener('pause', this.pauseListener);
}
delete this.playListener;
delete this.pauseListener;
}
/**
* Search JS player instance.
*
* @returns Player instance if found.
*/
private async searchJSPlayer(): Promise<VideoJSPlayer | null> {
return AddonFilterMediaPluginVideoJS.findPlayer(this.media.id)
?? AddonFilterMediaPluginVideoJS.findPlayer(this.media.id.replace('_html5_api', ''));
}
}