import PlayerData from './PlayerData'; import Resizer from '../video-transform/Resizer'; import * as _ from 'lodash'; import BrowserDetect from '../../conf/BrowserDetect'; import Settings from '../Settings'; import PageInfo from './PageInfo'; import { sleep } from '../../../common/js/utils'; import { hasDrm } from '../ar-detect/DrmDetecor'; import EventBus from '../EventBus'; import { SiteSettings } from '../settings/SiteSettings'; import { Ar } from '../../../common/interfaces/ArInterface'; import { ExtensionStatus } from './ExtensionStatus'; import { RunLevel } from '../../enum/run-level.enum'; import { Aard } from '../aard/Aard'; import { Stretch } from '../../../common/interfaces/StretchInterface'; import ExtensionMode from '../../../common/enums/ExtensionMode.enum'; import { ExtensionEnvironment } from '../../../common/interfaces/SettingsInterface'; import { LogAggregator } from '../logging/LogAggregator'; import { ComponentLogger } from '../logging/ComponentLogger'; /** * VideoData — handles CSS for the video element. * * To quickly disable or revert all modifications extension has made to the * video element, you can call disable() function. Calling disable() also * toggles autodetection off. */ class VideoData { private baseCssName: string = 'uw-ultrawidify-base-wide-screen'; private baseVideoCss = `.uw-ultrawidify-base-wide-screen { margin: 0px 0px 0px 0px !important; width: initial !important; align-self: start !important; justify-self: start !important; max-height: initial !important; max-width: initial !important; }`; //#region flags arSetupComplete: boolean = false; enabled: boolean; runLevel: RunLevel = RunLevel.Off; destroyed: boolean = false; invalid: boolean = false; videoStatusOk: boolean = false; videoLoaded: boolean = false; videoDimensionsLoaded: boolean = false; active: boolean = false; //#endregion //#region misc stuff vdid: string; video: any; observer: ResizeObserver; mutationObserver: MutationObserver; mutationObserverConf: MutationObserverInit = { attributes: true, attributeFilter: ['class', 'style'], attributeOldValue: true, }; userCssClassName: string; validationId: number; dimensions: any; hasDrm: boolean; //#endregion //#region helper objects logger: ComponentLogger; logAggregator: LogAggregator settings: Settings; // AARD needs it siteSettings: SiteSettings; pageInfo: PageInfo; player: PlayerData; resizer: Resizer; aard: Aard; eventBus: EventBus; extensionStatus: ExtensionStatus; private currentEnvironment: ExtensionEnvironment; //#endregion get aspectRatio() { try { return this.video.videoWidth / this.video.videoHeight; } catch (e) { console.error('cannot determine stream aspect ratio!', e); return 1; } } private eventBusCommands = { 'get-drm-status': { function: () => { this.hasDrm = hasDrm(this.video); this.eventBus.send('uw-config-broadcast', {type: 'drm-status', hasDrm: this.hasDrm}); } }, 'set-run-level': { function: (runLevel: RunLevel) => this.setRunLevel(runLevel) }, 'uw-environment-change': { function: () => { this.onEnvironmentChanged(); } } } /** * Creates new VideoData object * @param video * @param settings NEEDED FOR AARD * @param siteSettings * @param pageInfo */ constructor(video, settings: Settings, siteSettings: SiteSettings, pageInfo: PageInfo){ this.logAggregator = pageInfo.logAggregator; this.logger = new ComponentLogger(this.logAggregator, 'VideoData', {}); this.arSetupComplete = false; this.video = video; this.destroyed = false; this.settings = settings; this.siteSettings = siteSettings; this.pageInfo = pageInfo; this.videoStatusOk = false; this.userCssClassName = `uw-fuck-you-and-do-what-i-tell-you_${this.vdid}`; this.videoLoaded = false; this.videoDimensionsLoaded = true; this.validationId = null; this.dimensions = { width: this.video.offsetWidth, height: this.video.offsetHeight, }; if (!pageInfo.eventBus) { this.eventBus = new EventBus(); } else { this.eventBus = pageInfo.eventBus; } this.extensionStatus = new ExtensionStatus(siteSettings, pageInfo.eventBus, pageInfo.fsStatus); this.eventBus.subscribeMulti( this.eventBusCommands, this ); this.setupEventListeners(); } async onVideoLoaded() { if (!this.videoLoaded) { /** * video.readyState 101: * 0 — no info. Can't play. * 1 — we have metadata but nothing else * 2 — we have data for current playback position, but not future <--- meaning current frame, meaning Aard can work here or higher * 3 — we have a lil bit for the future * 4 — we'll survive to the end */ if (!this.video?.videoWidth || !this.video?.videoHeight || this.video.readyState < 2) { return; // onVideoLoaded is a lie in this case } this.logger.info('onVideoLoaded', '%c ——————————— Initiating phase two of videoData setup ———————————', 'color: #0f9'); this.hasDrm = hasDrm(this.video); this.eventBus.send( 'uw-config-broadcast', { type: 'drm-status', hasDrm: this.hasDrm }); this.videoLoaded = true; this.videoDimensionsLoaded = true; try { await this.setupStageTwo(); this.logger.info('onVideoLoaded', '%c——————————— videoData setup stage two complete ———————————', 'color: #0f9'); } catch (e) { this.logger.error('onVideoLoaded', '%c ——————————— Setup stage two failed. ———————————\n', 'color: #f00', e); } } else if (!this.videoDimensionsLoaded) { this.logger.debug('onVideoLoaded', "%cRecovering from illegal video dimensions. Resetting aspect ratio.", "background: #afd, color: #132"); this.restoreCrop(); this.videoDimensionsLoaded = true; } } videoUnloaded() { this.videoLoaded = false; } async injectBaseCss() { try { if (!this.mutationObserver) { this.setupMutationObserver(); } // this.eventBus.send('inject-css', this.baseVideoCss); } catch (e) { console.error('Failed to inject base css!', e); } } unsetBaseClass() { this.mutationObserver.disconnect(); this.mutationObserver = undefined; this.video.classList.remove('uw-ultrawidify-base-wide-screen'); } //#region