import Debug from '../../conf/Debug'; import VideoData from './VideoData'; import RescanReason from './enums/RescanReason.enum'; import AspectRatioType from '../../../common/enums/AspectRatioType.enum'; import CropModePersistence from '../../../common/enums/CropModePersistence.enum'; import Logger from '../Logger'; import Settings from '../Settings'; import ExtensionMode from '../../../common/enums/ExtensionMode.enum'; import CommsClient from '../comms/CommsClient'; import EventBus from '../EventBus'; if (process.env.CHANNEL !== 'stable'){ console.info("Loading PageInfo"); } /** * * The classes kinda go like this: * * PageInfo — one per page/frame. * | * +——— VideoData — child of PageInfo. There may be more than one of those * | +—— PlayerData — VideoData has exactly ONE (1) PlayerData * | +—— AspectRatioDetector — VideoData has 0-1 AARD things * | +—— Resizer * | +—— Scaler * | +—— Stretcher * | +—— Zoom * +——— VideoData * | +—— PlayerData * | : * : * * There is as many VideoData objects as there are videos. */ class PageInfo { //#region flags readOnly: boolean = false; hasVideos: boolean = false; siteDisabled: boolean = false; //#endregion //#region timers and timeouts rescanTimer: any; urlCheckTimer: any; announceZoomTimeout: any; //#endregion //#region helper objects logger: Logger; settings: Settings; comms: CommsClient; eventBus: EventBus; videos: {videoData: VideoData, element: HTMLVideoElement}[] = []; //#endregion //#region misc stuff lastUrl: string; extensionMode: ExtensionMode; defaultCrop: any; currentCrop: any; kbmHandlerInitQueue: any[] = []; currentZoomScale: number = 1; kbmHandler: any; //#endregion constructor(eventBus: EventBus, settings: Settings, logger: Logger, extensionMode, readOnly = false){ this.logger = logger; this.settings = settings; this.lastUrl = window.location.href; this.extensionMode = extensionMode; this.readOnly = readOnly; if (eventBus){ this.eventBus = eventBus; } try { // request inject css immediately const playerStyleString = this.settings.active.sites[window.location.hostname].css.replace('\\n', ''); this.eventBus.send('inject-css', {cssString: playerStyleString}); } catch (e) { // do nothing. It's ok if there's no special settings for the player element or crop persistence } this.currentCrop = this.defaultCrop; this.rescan(RescanReason.PERIODIC); this.scheduleUrlCheck(); } destroy() { this.logger.log('info', ['debug', 'init'], "[PageInfo::destroy] destroying all videos!") if(this.rescanTimer){ clearTimeout(this.rescanTimer); } for (let video of this.videos) { try { this.eventBus.send('noVideo', undefined); video.videoData.destroy(); } catch (e) { this.logger.log('error', ['debug', 'init'], '[PageInfo::destroy] unable to destroy video! Error:', e); } } try { const playerStyleString = this.settings.active.sites[window.location.hostname].css; this.eventBus.send('eject-css', {cssString: playerStyleString}); } catch (e) { // do nothing. It's ok if there's no special settings for the player element } } reset() { for(let video of this.videos) { video.videoData.destroy(); video.videoData = null; } this.videos = []; this.rescan(RescanReason.MANUAL); } initMouseActionHandler(videoData) { if (this.kbmHandler) { this.kbmHandler.registerHandleMouse(videoData); } else { this.kbmHandlerInitQueue.push(videoData); } } getVideos(host) { if (this.settings.active.sites[host]?.DOM?.video?.manual && this.settings.active.sites[host]?.DOM?.video?.querySelectors){ const videos = document.querySelectorAll(this.settings.active.sites[host].DOM.video.querySelectors) as NodeListOf; if (videos.length) { return videos; } } return document.getElementsByTagName('video'); } hasVideo() { return this.readOnly ? this.hasVideos : this.videos.length; } /** * Re-scans the page for videos. Removes any videos that no longer exist from our list * of videos. Destroys all videoData objects for all the videos that don't have their * own