MOBILE-4239 mediaplugin: Lazy load videojs
This commit is contained in:
		
							parent
							
								
									4cb9a6640c
								
							
						
					
					
						commit
						8ee614a60a
					
				| @ -16,7 +16,7 @@ import { CorePlatform } from '@services/platform'; | ||||
| import { OGVPlayer, OGVCompat, OGVLoader } from 'ogv'; | ||||
| import videojs, { PreloadOption, TechSourceObject, VideoJSOptions } from 'video.js'; | ||||
| 
 | ||||
| export const Tech = videojs.getComponent('Tech'); | ||||
| const Tech = videojs.getComponent('Tech'); | ||||
| 
 | ||||
| /** | ||||
|  * Object.defineProperty but "lazy", which means that the value is only set after | ||||
| @ -728,13 +728,6 @@ export class VideoJSOgvJS extends Tech { | ||||
| ].forEach(([key, fn]) => { | ||||
|     defineLazyProperty(VideoJSOgvJS.prototype, key, () => VideoJSOgvJS[fn](), true); | ||||
| }); | ||||
| /** | ||||
|  * Initialize the controller. | ||||
|  */ | ||||
| export const initializeVideoJSOgvJS = (): void => { | ||||
|     OGVLoader.base = 'assets/lib/ogv'; | ||||
|     Tech.registerTech('OgvJS', VideoJSOgvJS); | ||||
| }; | ||||
| 
 | ||||
| type OGVPlayerEl = (HTMLAudioElement | HTMLVideoElement) & { | ||||
|     stop: () => void; | ||||
|  | ||||
| @ -15,7 +15,6 @@ | ||||
| import { APP_INITIALIZER, NgModule } from '@angular/core'; | ||||
| 
 | ||||
| import { CoreFilterDelegate } from '@features/filter/services/filter-delegate'; | ||||
| import { initializeVideoJSOgvJS } from './classes/videojs-ogvjs'; | ||||
| import { AddonFilterMediaPluginHandler } from './services/handlers/mediaplugin'; | ||||
| 
 | ||||
| @NgModule({ | ||||
| @ -29,8 +28,6 @@ import { AddonFilterMediaPluginHandler } from './services/handlers/mediaplugin'; | ||||
|             multi: true, | ||||
|             useValue: () => { | ||||
|                 CoreFilterDelegate.registerHandler(AddonFilterMediaPluginHandler.instance); | ||||
| 
 | ||||
|                 initializeVideoJSOgvJS(); | ||||
|             }, | ||||
|         }, | ||||
|     ], | ||||
|  | ||||
| @ -12,18 +12,12 @@ | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { AddonFilterMediaPluginVideoJS } from '@addons/filter/mediaplugin/services/videojs'; | ||||
| import { Injectable } from '@angular/core'; | ||||
| import { CoreExternalContentDirective } from '@directives/external-content'; | ||||
| 
 | ||||
| import { CoreFilterDefaultHandler } from '@features/filter/services/handlers/default-filter'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreUrlUtils } from '@services/utils/url'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { CoreDirectivesRegistry } from '@singletons/directives-registry'; | ||||
| import { CoreEvents } from '@singletons/events'; | ||||
| import { CoreMedia } from '@singletons/media'; | ||||
| import videojs, { VideoJSOptions, VideoJSPlayer } from 'video.js'; | ||||
| 
 | ||||
| /** | ||||
|  * Handler to support the Multimedia filter. | ||||
| @ -39,15 +33,13 @@ export class AddonFilterMediaPluginHandlerService extends CoreFilterDefaultHandl | ||||
|     /** | ||||
|      * @inheritdoc | ||||
|      */ | ||||
|     filter( | ||||
|         text: string, | ||||
|     ): string | Promise<string> { | ||||
|     filter(text: string): string | Promise<string> { | ||||
|         this.template.innerHTML = text; | ||||
| 
 | ||||
|         const videos = Array.from(this.template.content.querySelectorAll('video')); | ||||
| 
 | ||||
|         videos.forEach((video) => { | ||||
|             this.treatYoutubeVideos(video); | ||||
|             AddonFilterMediaPluginVideoJS.treatYoutubeVideos(video); | ||||
|         }); | ||||
| 
 | ||||
|         return this.template.innerHTML; | ||||
| @ -61,104 +53,18 @@ export class AddonFilterMediaPluginHandlerService extends CoreFilterDefaultHandl | ||||
| 
 | ||||
|         mediaElements.forEach((mediaElement) => { | ||||
|             if (CoreMedia.mediaUsesJavascriptPlayer(mediaElement)) { | ||||
|                 this.useVideoJS(mediaElement); | ||||
|             } else { | ||||
|                 // Remove the VideoJS classes and data if present.
 | ||||
|                 mediaElement.classList.remove('video-js'); | ||||
|                 mediaElement.removeAttribute('data-setup'); | ||||
|                 mediaElement.removeAttribute('data-setup-lazy'); | ||||
|                 AddonFilterMediaPluginVideoJS.createPlayer(mediaElement); | ||||
| 
 | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             // Remove the VideoJS classes and data if present.
 | ||||
|             mediaElement.classList.remove('video-js'); | ||||
|             mediaElement.removeAttribute('data-setup'); | ||||
|             mediaElement.removeAttribute('data-setup-lazy'); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Use video JS in a certain video or audio. | ||||
|      * | ||||
|      * @param mediaElement Media element. | ||||
|      */ | ||||
|     protected async useVideoJS(mediaElement: HTMLVideoElement | HTMLAudioElement): Promise<void> { | ||||
|         const lang = await CoreLang.getCurrentLanguage(); | ||||
| 
 | ||||
|         // Wait for external-content to finish in the element and its sources.
 | ||||
|         await Promise.all([ | ||||
|             CoreDirectivesRegistry.waitDirectivesReady(mediaElement, undefined, CoreExternalContentDirective), | ||||
|             CoreDirectivesRegistry.waitDirectivesReady(mediaElement, 'source', CoreExternalContentDirective), | ||||
|         ]); | ||||
| 
 | ||||
|         const dataSetupString = mediaElement.getAttribute('data-setup') || mediaElement.getAttribute('data-setup-lazy') || '{}'; | ||||
|         const data = CoreTextUtils.parseJSON<VideoJSOptions>(dataSetupString, {}); | ||||
| 
 | ||||
|         const player = videojs(mediaElement, { | ||||
|             controls: true, | ||||
|             techOrder: ['OgvJS'], | ||||
|             language: lang, | ||||
|             controlBar: { | ||||
|                 pictureInPictureToggle: false, | ||||
|             }, | ||||
|             aspectRatio: data.aspectRatio, | ||||
|         }, () => { | ||||
|             if (mediaElement.tagName === 'VIDEO') { | ||||
|                 this.fixVideoJSPlayerSize(player); | ||||
|             } | ||||
|         }); | ||||
| 
 | ||||
|         CoreEvents.trigger(CoreEvents.JS_PLAYER_CREATED, { | ||||
|             id: mediaElement.id, | ||||
|             element: mediaElement, | ||||
|             player, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fix VideoJS player size. | ||||
|      * If video width is wider than available width, video is cut off. Fix the dimensions in this case. | ||||
|      * | ||||
|      * @param player Player instance. | ||||
|      */ | ||||
|     protected fixVideoJSPlayerSize(player: VideoJSPlayer): void { | ||||
|         const videoWidth = player.videoWidth(); | ||||
|         const videoHeight = player.videoHeight(); | ||||
|         const playerDimensions = player.currentDimensions(); | ||||
|         if (!videoWidth || !videoHeight || !playerDimensions.width || videoWidth === playerDimensions.width) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const candidateHeight = playerDimensions.width * videoHeight / videoWidth; | ||||
|         if (!playerDimensions.height || Math.abs(candidateHeight - playerDimensions.height) > 1) { | ||||
|             player.dimension('height', candidateHeight); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Treat Video JS Youtube video links and translate them to iframes. | ||||
|      * | ||||
|      * @param video Video element. | ||||
|      */ | ||||
|     protected treatYoutubeVideos(video: HTMLElement): void { | ||||
|         if (!video.classList.contains('video-js')) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const dataSetupString = video.getAttribute('data-setup') || video.getAttribute('data-setup-lazy') || '{}'; | ||||
|         const data = CoreTextUtils.parseJSON<VideoJSOptions>(dataSetupString, {}); | ||||
|         const youtubeUrl = data.techOrder?.[0] == 'youtube' && CoreUrlUtils.getYoutubeEmbedUrl(data.sources?.[0]?.src); | ||||
| 
 | ||||
|         if (!youtubeUrl) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const iframe = document.createElement('iframe'); | ||||
|         iframe.id = video.id; | ||||
|         iframe.src = youtubeUrl; | ||||
|         iframe.setAttribute('frameborder', '0'); | ||||
|         iframe.setAttribute('allowfullscreen', '1'); | ||||
|         iframe.width = '100%'; | ||||
|         iframe.height = '300'; | ||||
| 
 | ||||
|         // Replace video tag by the iframe.
 | ||||
|         video.parentNode?.replaceChild(iframe, video); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export const AddonFilterMediaPluginHandler = makeSingleton(AddonFilterMediaPluginHandlerService); | ||||
|  | ||||
							
								
								
									
										188
									
								
								src/addons/filter/mediaplugin/services/videojs.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								src/addons/filter/mediaplugin/services/videojs.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,188 @@ | ||||
| // (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 { Injectable } from '@angular/core'; | ||||
| import { CorePromisedValue } from '@classes/promised-value'; | ||||
| import { CoreExternalContentDirective } from '@directives/external-content'; | ||||
| import { CoreLang } from '@services/lang'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreUrlUtils } from '@services/utils/url'; | ||||
| import { makeSingleton } from '@singletons'; | ||||
| import { CoreDirectivesRegistry } from '@singletons/directives-registry'; | ||||
| import { CoreEvents } from '@singletons/events'; | ||||
| import type videojs from 'video.js'; | ||||
| 
 | ||||
| // eslint-disable-next-line no-duplicate-imports
 | ||||
| import type { VideoJSOptions, VideoJSPlayer } from 'video.js'; | ||||
| 
 | ||||
| declare module '@singletons/events' { | ||||
| 
 | ||||
|     /** | ||||
|      * Augment CoreEventsData interface with events specific to this service. | ||||
|      * | ||||
|      * @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
 | ||||
|      */ | ||||
|     export interface CoreEventsData { | ||||
|         [VIDEO_JS_PLAYER_CREATED]: CoreEventJSVideoPlayerCreated; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export const VIDEO_JS_PLAYER_CREATED = 'video_js_player_created'; | ||||
| 
 | ||||
| /** | ||||
|  * Wrapper encapsulating videojs functionality. | ||||
|  */ | ||||
| @Injectable({ providedIn: 'root' }) | ||||
| export class AddonFilterMediaPluginVideoJSService { | ||||
| 
 | ||||
|     protected videojs?: CorePromisedValue<typeof videojs>; | ||||
| 
 | ||||
|     /** | ||||
|      * Create a VideoJS player. | ||||
|      * | ||||
|      * @param element Media element. | ||||
|      */ | ||||
|     async createPlayer(element: HTMLVideoElement | HTMLAudioElement): Promise<void> { | ||||
|         // Wait for external-content to finish in the element and its sources.
 | ||||
|         await Promise.all([ | ||||
|             CoreDirectivesRegistry.waitDirectivesReady(element, undefined, CoreExternalContentDirective), | ||||
|             CoreDirectivesRegistry.waitDirectivesReady(element, 'source', CoreExternalContentDirective), | ||||
|         ]); | ||||
| 
 | ||||
|         // Create player.
 | ||||
|         const videojs = await this.getVideoJS(); | ||||
|         const dataSetupString = element.getAttribute('data-setup') || element.getAttribute('data-setup-lazy') || '{}'; | ||||
|         const data = CoreTextUtils.parseJSON<VideoJSOptions>(dataSetupString, {}); | ||||
|         const player = videojs( | ||||
|             element, | ||||
|             { | ||||
|                 controls: true, | ||||
|                 techOrder: ['OgvJS'], | ||||
|                 language: await CoreLang.getCurrentLanguage(), | ||||
|                 controlBar: { pictureInPictureToggle: false }, | ||||
|                 aspectRatio: data.aspectRatio, | ||||
|             }, | ||||
|             () => element.tagName === 'VIDEO' && this.fixVideoJSPlayerSize(player), | ||||
|         ); | ||||
| 
 | ||||
|         CoreEvents.trigger(VIDEO_JS_PLAYER_CREATED, { | ||||
|             element, | ||||
|             player, | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Find a VideoJS player by id. | ||||
|      * | ||||
|      * @param id Element id. | ||||
|      * @returns VideoJS player. | ||||
|      */ | ||||
|     async findPlayer(id: string): Promise<VideoJSPlayer | null> { | ||||
|         const videojs = await this.getVideoJS(); | ||||
| 
 | ||||
|         return videojs.getPlayer(id); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Treat Video JS Youtube video links and translate them to iframes. | ||||
|      * | ||||
|      * @param video Video element. | ||||
|      */ | ||||
|     treatYoutubeVideos(video: HTMLElement): void { | ||||
|         if (!video.classList.contains('video-js')) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const dataSetupString = video.getAttribute('data-setup') || video.getAttribute('data-setup-lazy') || '{}'; | ||||
|         const data = CoreTextUtils.parseJSON<VideoJSOptions>(dataSetupString, {}); | ||||
|         const youtubeUrl = data.techOrder?.[0] == 'youtube' && CoreUrlUtils.getYoutubeEmbedUrl(data.sources?.[0]?.src); | ||||
| 
 | ||||
|         if (!youtubeUrl) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const iframe = document.createElement('iframe'); | ||||
|         iframe.id = video.id; | ||||
|         iframe.src = youtubeUrl; | ||||
|         iframe.setAttribute('frameborder', '0'); | ||||
|         iframe.setAttribute('allowfullscreen', '1'); | ||||
|         iframe.width = '100%'; | ||||
|         iframe.height = '300'; | ||||
| 
 | ||||
|         // Replace video tag by the iframe.
 | ||||
|         video.parentNode?.replaceChild(iframe, video); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets videojs instance. | ||||
|      * | ||||
|      * @returns VideoJS. | ||||
|      */ | ||||
|     protected async getVideoJS(): Promise<typeof videojs> { | ||||
|         if (!this.videojs) { | ||||
|             this.videojs = new CorePromisedValue(); | ||||
| 
 | ||||
|             // Inject CSS.
 | ||||
|             const link = document.createElement('link'); | ||||
| 
 | ||||
|             link.rel = 'stylesheet'; | ||||
|             link.href = 'assets/lib/video.js/video-js.min.css'; | ||||
| 
 | ||||
|             document.head.appendChild(link); | ||||
| 
 | ||||
|             // Load library.
 | ||||
|             return import('@addons/filter/mediaplugin/utils/videojs').then(({ initializeVideoJSOgvJS, videojs }) => { | ||||
|                 initializeVideoJSOgvJS(); | ||||
| 
 | ||||
|                 this.videojs?.resolve(videojs); | ||||
| 
 | ||||
|                 return videojs; | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         return this.videojs; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fix VideoJS player size. | ||||
|      * If video width is wider than available width, video is cut off. Fix the dimensions in this case. | ||||
|      * | ||||
|      * @param player Player instance. | ||||
|      */ | ||||
|     protected fixVideoJSPlayerSize(player: VideoJSPlayer): void { | ||||
|         const videoWidth = player.videoWidth(); | ||||
|         const videoHeight = player.videoHeight(); | ||||
|         const playerDimensions = player.currentDimensions(); | ||||
|         if (!videoWidth || !videoHeight || !playerDimensions.width || videoWidth === playerDimensions.width) { | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const candidateHeight = playerDimensions.width * videoHeight / videoWidth; | ||||
|         if (!playerDimensions.height || Math.abs(candidateHeight - playerDimensions.height) > 1) { | ||||
|             player.dimension('height', candidateHeight); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| export const AddonFilterMediaPluginVideoJS = makeSingleton(AddonFilterMediaPluginVideoJSService); | ||||
| 
 | ||||
| /** | ||||
|  * Data passed to VIDEO_JS_PLAYER_CREATED event. | ||||
|  */ | ||||
| export type CoreEventJSVideoPlayerCreated = { | ||||
|     element: HTMLAudioElement | HTMLVideoElement; | ||||
|     player: VideoJSPlayer; | ||||
| }; | ||||
							
								
								
									
										28
									
								
								src/addons/filter/mediaplugin/utils/videojs.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/addons/filter/mediaplugin/utils/videojs.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| // (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 { VideoJSOgvJS } from '@addons/filter/mediaplugin/classes/videojs-ogvjs'; | ||||
| import { OGVLoader } from 'ogv'; | ||||
| import videojs from 'video.js'; | ||||
| 
 | ||||
| export { videojs }; | ||||
| 
 | ||||
| /** | ||||
|  * Initialize the controller. | ||||
|  */ | ||||
| export function initializeVideoJSOgvJS(): void { | ||||
|     OGVLoader.base = 'assets/lib/ogv'; | ||||
| 
 | ||||
|     videojs.getComponent('Tech').registerTech('OgvJS', VideoJSOgvJS); | ||||
| } | ||||
| @ -14,10 +14,11 @@ | ||||
| 
 | ||||
| import { CoreUtils } from '@services/utils/utils'; | ||||
| import { ElementController } from './ElementController'; | ||||
| import videojs, { VideoJSPlayer } from 'video.js'; | ||||
| 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. | ||||
| @ -42,21 +43,7 @@ export class MediaElementController extends ElementController { | ||||
| 
 | ||||
|         media.autoplay = false; | ||||
| 
 | ||||
|         if (CoreMedia.mediaUsesJavascriptPlayer(media)) { | ||||
|             const player = this.searchJSPlayer(); | ||||
|             if (player) { | ||||
|                 this.jsPlayer.resolve(player); | ||||
|             } else { | ||||
|                 this.jsPlayerListener = CoreEvents.on(CoreEvents.JS_PLAYER_CREATED, data => { | ||||
|                     if (data.element === media) { | ||||
|                         this.jsPlayerListener?.off(); | ||||
|                         this.jsPlayer.resolve(data.player); | ||||
|                     } | ||||
|                 }); | ||||
|             } | ||||
|         } else { | ||||
|             this.jsPlayer.resolve(null); | ||||
|         } | ||||
|         this.initJSPlayer(media); | ||||
| 
 | ||||
|         enabled && this.onEnabled(); | ||||
|     } | ||||
| @ -115,6 +102,36 @@ export class MediaElementController extends ElementController { | ||||
|         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. | ||||
|      * | ||||
| @ -153,8 +170,9 @@ export class MediaElementController extends ElementController { | ||||
|      * | ||||
|      * @returns Player instance if found. | ||||
|      */ | ||||
|     private searchJSPlayer(): VideoJSPlayer | null { | ||||
|         return videojs.getPlayer(this.media.id) || videojs.getPlayer(this.media.id.replace('_html5_api', '')); | ||||
|     private async searchJSPlayer(): Promise<VideoJSPlayer | null> { | ||||
|         return AddonFilterMediaPluginVideoJS.findPlayer(this.media.id) | ||||
|             ?? AddonFilterMediaPluginVideoJS.findPlayer(this.media.id.replace('_html5_api', '')); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -20,7 +20,6 @@ import { CoreFilepoolComponentFileEventData } from '@services/filepool'; | ||||
| import { CoreRedirectPayload } from '@services/navigator'; | ||||
| import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper'; | ||||
| import { CoreScreenOrientation } from '@services/screen'; | ||||
| import { VideoJSPlayer } from 'video.js'; | ||||
| 
 | ||||
| /** | ||||
|  * Observer instance to stop listening to an event. | ||||
| @ -65,7 +64,6 @@ export interface CoreEventsData { | ||||
|     [CoreEvents.ORIENTATION_CHANGE]: CoreEventOrientationData; | ||||
|     [CoreEvents.COURSE_MODULE_VIEWED]: CoreEventCourseModuleViewed; | ||||
|     [CoreEvents.COMPLETE_REQUIRED_PROFILE_DATA_FINISHED]: CoreEventCompleteRequiredProfileDataFinished; | ||||
|     [CoreEvents.JS_PLAYER_CREATED]: CoreEventJSVideoPlayerCreated; | ||||
| } | ||||
| 
 | ||||
| /* | ||||
| @ -125,7 +123,6 @@ export class CoreEvents { | ||||
|     static readonly COMPLETE_REQUIRED_PROFILE_DATA_FINISHED = 'complete_required_profile_data_finished'; | ||||
|     static readonly MAIN_HOME_LOADED = 'main_home_loaded'; | ||||
|     static readonly FULL_SCREEN_CHANGED = 'full_screen_changed'; | ||||
|     static readonly JS_PLAYER_CREATED = 'js_player_created'; | ||||
| 
 | ||||
|     protected static logger = CoreLogger.getInstance('CoreEvents'); | ||||
|     protected static observables: { [eventName: string]: Subject<unknown> } = {}; | ||||
| @ -493,12 +490,3 @@ export type CoreEventCourseModuleViewed = { | ||||
| export type CoreEventCompleteRequiredProfileDataFinished = { | ||||
|     path: string; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Data passed to JS_PLAYER_CREATED event. | ||||
|  */ | ||||
| export type CoreEventJSVideoPlayerCreated = { | ||||
|     id: string; | ||||
|     element: HTMLAudioElement | HTMLVideoElement; | ||||
|     player: VideoJSPlayer; | ||||
| }; | ||||
|  | ||||
| @ -16,19 +16,12 @@ | ||||
|     <meta name="msapplication-tap-highlight" content="no" /> | ||||
| 
 | ||||
|     <link rel="icon" type="image/png" href="assets/icon/favicon.png" /> | ||||
|     <link rel="stylesheet" href="assets/lib/video.js/video-js.min.css"> | ||||
| 
 | ||||
|     <!-- add to homescreen for ios --> | ||||
|     <meta name="apple-mobile-web-app-capable" content="yes" /> | ||||
|     <meta name="apple-mobile-web-app-status-bar-style" content="black" /> | ||||
| 
 | ||||
|     <script src="assets/lib/mathjax/MathJax.js?delayStartupUntil=configured"></script> | ||||
|     <script type="text/javascript"> | ||||
|         if (globalThis === undefined) { | ||||
|             // Define globalThis in environments where it's not supported to avoid errors with ogv.js. | ||||
|             var globalThis = window; | ||||
|         } | ||||
|     </script> | ||||
| </head> | ||||
| 
 | ||||
| <body> | ||||
|  | ||||
| @ -20,6 +20,7 @@ import 'zone.js/dist/zone'; | ||||
| 
 | ||||
| // Platform polyfills
 | ||||
| import 'core-js/es/array/includes'; | ||||
| import 'core-js/es/global-this'; | ||||
| import 'core-js/es/promise/finally'; | ||||
| import 'core-js/es/string/match-all'; | ||||
| import 'core-js/es/string/trim-right'; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user