Compare commits

...

5 Commits

27 changed files with 328 additions and 223 deletions

View File

@ -17,11 +17,14 @@ export interface EventBusContext {
stopPropagation?: boolean; stopPropagation?: boolean;
origin?: CommsOrigin; origin?: CommsOrigin;
frameUrl?: string;
// tab?: number; // tab?: number;
// frame?: number; // frame?: number;
// port?: string; // port?: string;
visitedBusses?: string[];
comms?: { comms?: {
forwardTo?: 'all' | 'active' | 'popup' | 'contentScript' | 'all-frames'; forwardTo?: 'all' | 'active' | 'popup' | 'contentScript' | 'all-frames';
sender?: Runtime.MessageSender; sender?: Runtime.MessageSender;

View File

@ -0,0 +1,8 @@
import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
export interface HostInfo {
host: string,
hasVideo: boolean,
minEnvironment: ExtensionEnvironment,
maxEnvironment: ExtensionEnvironment
};

View File

@ -0,0 +1,6 @@
import { EventBusMessage } from '@src/common/interfaces/EventBusMessage.interface';
export interface IframeTunnelPayload {
action: string,
payload: EventBusMessage,
}

View File

@ -1,4 +1,4 @@
import Debug from '../conf/Debug'; import Debug from '../../ext/conf/Debug';
class ObjectCopy { class ObjectCopy {
static addNew(current, newValues){ static addNew(current, newValues){

View File

@ -1,7 +1,3 @@
export async function sleep(timeout) {
return new Promise<void>( (resolve, reject) => setTimeout(() => resolve(), timeout));
}
/** /**
* Creates deep copy of an object * Creates deep copy of an object
* @param obj * @param obj

View File

@ -8,7 +8,7 @@ import Settings from '@src/ext/module/settings/Settings';
import { SiteSettings } from '@src/ext/module/settings/SiteSettings'; import { SiteSettings } from '@src/ext/module/settings/SiteSettings';
import UI from '@src/ext/module/uwui/UI'; import UI from '@src/ext/module/uwui/UI';
import PageInfo from '@src/ext/module/video-data/PageInfo'; import PageInfo from '@src/ext/module/video-data/PageInfo';
import { getIframeParentHost, setupHostnameReporting } from '@src/ext/util/getHost'; import { getIframeParentHost, setupHostnameReporting } from '@src/common/utils/getHost';
export default class UWContent { export default class UWContent {
pageInfo: PageInfo; pageInfo: PageInfo;

View File

@ -4,6 +4,9 @@ import Settings from '@src/ext/module/settings/Settings';
import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger'; import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger';
import { BLANK_LOGGER_CONFIG, LogAggregator } from '@src/ext/module/logging/LogAggregator'; import { BLANK_LOGGER_CONFIG, LogAggregator } from '@src/ext/module/logging/LogAggregator';
import CommsServer from '@src/ext/module/comms/CommsServer'; import CommsServer from '@src/ext/module/comms/CommsServer';
import BrowserDetect from '@src/ext/conf/BrowserDetect';
import { HostInfo } from '@src/common/interfaces/HostData.interface';
import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
const BASE_LOGGING_STYLES = { const BASE_LOGGING_STYLES = {
@ -263,6 +266,7 @@ export default class UWServer {
const tabHostname = await this.getCurrentTabHostname(); const tabHostname = await this.getCurrentTabHostname();
this.logger.info('getCurrentSite', 'Returning data:', {site, tabHostname}); this.logger.info('getCurrentSite', 'Returning data:', {site, tabHostname});
console.info('get-current-site : returning data:', {site, tabHostname});
this.eventBus.send( this.eventBus.send(
'set-current-site', 'set-current-site',
@ -283,6 +287,36 @@ export default class UWServer {
} }
private populateFrameVideoStatus(tabId: number, hostnames: string[]) {
const out: HostInfo[] = [];
for (const host of hostnames) {
let video = {hasVideo: false, minEnvironment: ExtensionEnvironment.Normal, maxEnvironment: ExtensionEnvironment.Fullscreen};
for (const frameKey in this.videoTabs[tabId].frames) {
const frame = this.videoTabs[tabId].frames[frameKey];
if (frame.host === host) {
video.hasVideo = true;
if (frame.environment > video.maxEnvironment) {
video.maxEnvironment = frame.environment;
}
if (frame.environment < video.minEnvironment) {
video.minEnvironment = frame.environment;
}
}
}
out.push({
host,
...video,
})
}
return out;
}
private _lastVideoTabData: any | undefined;
async getVideoTab() { async getVideoTab() {
// friendly reminder: if current tab doesn't have a video, // friendly reminder: if current tab doesn't have a video,
// there won't be anything in this.videoTabs[this.currentTabId] // there won't be anything in this.videoTabs[this.currentTabId]
@ -299,7 +333,15 @@ export default class UWServer {
const hostnames = await this.comms.listUniqueFrameHosts(); const hostnames = await this.comms.listUniqueFrameHosts();
if (this.videoTabs[ctab.id]) { // this probably means we're inside a problematic page
if (!this.videoTabs[ctab.id]) {
return this._lastVideoTabData ?? {
host: 'INVALID SITE',
frames: [],
hostnames: [],
}
}
// if video is older than PageInfo's video rescan period (+ 4000ms of grace), // if video is older than PageInfo's video rescan period (+ 4000ms of grace),
// we clean it up from videoTabs[tabId].frames array. // we clean it up from videoTabs[tabId].frames array.
const ageLimit = Date.now() - this.settings.active.pageInfo.timeouts.rescan - 4000; const ageLimit = Date.now() - this.settings.active.pageInfo.timeouts.rescan - 4000;
@ -311,29 +353,32 @@ export default class UWServer {
} }
} catch (e) { } catch (e) {
// something went wrong. There's prolly no frames. // something went wrong. There's prolly no frames.
// this probably shouldn't ever run.
return { return {
host: this.extractHostname(ctab.url), host: this.extractHostname(ctab.url),
hostnames: [],
frames: [], frames: [],
selected: this.selectedSubitem selected: this.selectedSubitem
} }
} }
return { // Ensure hostnames with videos come before hostnames without videos
...this.videoTabs[ctab.id], // Also ensure hostnames are sorted alphabetically
const populatedHostnames = this.populateFrameVideoStatus(ctab.id, hostnames);
populatedHostnames.sort((a: HostInfo, b: HostInfo) => {
return a.hasVideo === b.hasVideo
? a.host === b.host ? 0 : a.host < b.host ? -1 : 1
: a.hasVideo < b.hasVideo ? 1 : -1;
});
this._lastVideoTabData = {
host: this.extractHostname(ctab.url), host: this.extractHostname(ctab.url),
hostnames, hostnames: populatedHostnames.map(x => x.host), // todo: try eliminating this
populatedHostnames: populatedHostnames,
selected: this.selectedSubitem selected: this.selectedSubitem
}; };
}
// return something more or less empty if this tab doesn't have return this._lastVideoTabData;
// a video registered for it
return {
host: this.extractHostname(ctab.url),
frames: [],
hostnames,
selected: this.selectedSubitem,
}
} }
async getCurrentTabHostname() { async getCurrentTabHostname() {

View File

@ -1,34 +1,19 @@
import { IframeData } from './video-data/IframeManager'; import { EventBusCommand, EventBusContext, EventBusMessage } from '@src/common/interfaces/EventBusMessage.interface';
import CommsClient, { CommsOrigin } from './comms/CommsClient'; import { IframeTunnelPayload } from '@src/common/interfaces/IframeTunnelPayload.interface';
import CommsServer from './comms/CommsServer'; import Comms from '@src/ext/module/comms/Comms';
import { EventBusCommand, EventBusContext } from '@src/common/interfaces/EventBusMessage.interface'; import CommsClient, { CommsOrigin } from '@src/ext/module/comms/CommsClient';
import CommsServer from '@src/ext/module/comms/CommsServer';
// export interface EventBusContext {
// stopPropagation?: boolean,
// // Context stuff added by Comms
// origin?: CommsOrigin,
// comms?: {
// sender?: chrome.runtime.MessageSender,
// port?: chrome.runtime.Port,
// frame?: any,
// sourceFrame?: IframeData
// forwardTo?: 'all' | 'active' | 'contentScript' | 'server' | 'sameOrigin' | 'popup' | 'all-frames',
// };
// borderCrossings?: {
// commsServer?: boolean,
// iframe?: boolean,
// }
// }
export default class EventBus { export default class EventBus {
private name: string; private name: string;
private uuid = crypto.randomUUID();
private commands: { [x: string]: EventBusCommand[]} = {}; private commands: { [x: string]: EventBusCommand[]} = {};
private comms?: CommsClient | CommsServer; private comms?: CommsClient | CommsServer;
private commsOrigin: CommsOrigin;
private disableTunnel: boolean = false; private disableTunnel: boolean = false;
private popupContext: any = {}; private popupContext: any = {};
@ -37,11 +22,12 @@ export default class EventBus {
// private uiUri = window.location.href; // private uiUri = window.location.href;
constructor(options?: {isUWServer?: boolean, name?: string}) { constructor(options?: {isUWServer?: boolean, name?: string, commsOrigin?: CommsOrigin}) {
if (!options?.isUWServer) { if (!options?.isUWServer) {
this.setupIframeTunnelling(); this.setupIframeTunnelling();
} }
this.name = options?.name; this.name = options?.name ?? '(unnamed EventBus)';
this.commsOrigin = options?.commsOrigin;
} }
setupPopupTunnelWorkaround(context: EventBusContext): void { setupPopupTunnelWorkaround(context: EventBusContext): void {
@ -102,8 +88,7 @@ export default class EventBus {
} }
} }
send(command: string, commandData: any, context?: EventBusContext) { send(command: string, commandData: any, context: EventBusContext = {}) {
console.info('sending eventBus command:', this.name, 'command:', {command, commandData, context});
// execute commands we have subscriptions for // execute commands we have subscriptions for
if (this.commands?.[command]) { if (this.commands?.[command]) {
@ -146,7 +131,9 @@ export default class EventBus {
} }
); );
} }
this.sendToTunnel(command, commandData); this.sendToTunnel(command, commandData, context);
} else {
console.warn('message was already sent to iframe, doing nothing ...')
} }
if (context?.stopPropagation) { if (context?.stopPropagation) {
@ -160,12 +147,14 @@ export default class EventBus {
* @param command * @param command
* @param config * @param config
*/ */
sendToTunnel(command: string, config: any) { sendToTunnel(command: string, config: any, context: EventBusContext = {}) {
if (!this.disableTunnel) { context.visitedBusses = [...context.visitedBusses ?? [], this.uuid];
if (!this.disableTunnel && typeof window !== 'undefined') {
window.parent.postMessage( window.parent.postMessage(
{ {
action: 'uw-bus-tunnel', action: 'uw-bus-tunnel',
payload: {action: command, config} payload: {command, config, context} as EventBusMessage
}, },
'*' '*'
); );
@ -175,10 +164,20 @@ export default class EventBus {
// in the popup // in the popup
if (this.comms) { if (this.comms) {
try { try {
this.comms.sendMessage({command, config, context: this.popupContext}, this.popupContext); this.comms.sendMessage(
{
command,
config,
context: {
...this.popupContext,
...context
}
},
this.popupContext
);
} catch (e) { } catch (e) {
if (command !== 'reload-required') { if (command !== 'reload-required') {
this.send('reload-required', {}); this.send('reload-required', {}, context);
} }
} }
} }
@ -188,17 +187,34 @@ export default class EventBus {
//#region iframe tunnelling //#region iframe tunnelling
private setupIframeTunnelling() { private setupIframeTunnelling() {
// forward messages coming from iframe tunnels // forward messages coming from iframe tunnels
window.addEventListener('message', this.handleIframeMessage); window.addEventListener('message', this);
} }
private destroyIframeTunnelling() { private destroyIframeTunnelling() {
window.removeEventListener('message', this.handleIframeMessage); window.removeEventListener('message', this);
} }
private handleIframeMessage(event: any) { /**
* Handles 'message' events (formerly handleIframeMessage)
* @param event
* @returns
*/
handleEvent(event: any) {
if (event.data?.action !== 'uw-bus-tunnel') { if (event.data?.action !== 'uw-bus-tunnel') {
return; return;
} }
console.info(this.name, 'received message from iframe. command:', event.data.payload);
this.send(event.data.payload.command, event.data.payload.config); const payload = event.data.payload as EventBusMessage;
console.info(this.name, 'received message from iframe. command:', payload);
if (!payload.context) {
console.warn('Received iframe message without context. Doing nothing in order to avoid infinite loop. Event:', event);
return;
}
if (payload.context?.visitedBusses?.includes(this.uuid)) {
return;
}
this.send(payload.command, payload.config, payload.context);
} }
//#endregion //#endregion

View File

@ -1,12 +0,0 @@
class Interface {
constructor(videoData) {
this.conf = videoData;
this.player = videoData.player;
}
injectUi() {
this.detectorDiv = document.createElement('div');
this.uiRoot = document.createElement('div');
this.detectorDiv.appendChild(this.uiRoot);
}
}

View File

@ -65,7 +65,8 @@ if (process.env.CHANNEL !== 'stable'){
export enum CommsOrigin { export enum CommsOrigin {
ContentScript = 1, ContentScript = 1,
Popup = 2, Popup = 2,
Server = 3 Server = 3,
Ui = 4, // in-player UI
} }
class CommsClient { class CommsClient {
@ -148,7 +149,7 @@ class CommsClient {
// send to server // send to server
if (!context?.borderCrossings?.commsServer) { if (!context?.borderCrossings?.commsServer) {
return chrome.runtime.sendMessage(null, message, null); return chrome?.runtime?.sendMessage(null, message, null);
} }
} }

View File

@ -1,8 +1,10 @@
import { ExtensionEnvironment } from './../../../common/interfaces/SettingsInterface';
import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger'; import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger';
import Settings from '@src/ext/module/settings/Settings'; import Settings from '@src/ext/module/settings/Settings';
import EventBus from '@src/ext/module/EventBus'; import EventBus from '@src/ext/module/EventBus';
import { CommsOrigin } from './CommsClient'; import { CommsOrigin } from './CommsClient';
import UWServer from '@src/ext/UWServer'; import UWServer from '@src/ext/UWServer';
import { HostInfo } from '@src/common/interfaces/HostData.interface';
const BASE_LOGGING_STYLES = { const BASE_LOGGING_STYLES = {
log: "background-color: #11D; color: #aad", log: "background-color: #11D; color: #aad",
@ -151,6 +153,15 @@ class CommsServer {
return hosts; return hosts;
} }
async getUniqueFrameHosts() {
const aTab = await this.activeTab;
const tabPort = this.ports[aTab.id];
const hosts: HostInfo[] = [];
}
sendMessage(message, context?) { sendMessage(message, context?) {
this.logger.debug('sendMessage', `preparing to send message ${message.command ?? ''} ...`, {message, context}); this.logger.debug('sendMessage', `preparing to send message ${message.command ?? ''} ...`, {message, context});
// stop messages from returning where they came from, and prevent // stop messages from returning where they came from, and prevent

View File

@ -1,14 +1,13 @@
import Debug from '../../conf/Debug'; import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
import ExtensionConf from '../../conf/ExtensionConf'; import StretchType from '@src/common/enums/StretchType.enum';
import ObjectCopy from '../ObjectCopy'; import SettingsInterface from '@src/common/interfaces/SettingsInterface';
import StretchType from '../../../common/enums/StretchType.enum'; import Debug from '@src/ext/conf/Debug';
import ExtensionConfPatch from '../../conf/ExtConfPatches'; import ExtensionConfPatch from '@src/ext/conf/ExtConfPatches';
import SettingsInterface from '../../../common/interfaces/SettingsInterface'; import ExtensionConf from '@src/ext/conf/ExtensionConf';
import AspectRatioType from '../../../common/enums/AspectRatioType.enum'; import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger';
import { GetSiteSettingsOptions, SiteSettings } from './SiteSettings'; import { LogAggregator } from '@src/ext/module/logging/LogAggregator';
import { SettingsSnapshot, SettingsSnapshotManager } from './SettingsSnapshotManager'; import { SettingsSnapshot, SettingsSnapshotManager } from '@src/ext/module/settings/SettingsSnapshotManager';
import { ComponentLogger } from '../logging/ComponentLogger'; import { GetSiteSettingsOptions, SiteSettings } from '@src/ext/module/settings/SiteSettings';
import { LogAggregator } from '../logging/LogAggregator';
import { getDiff } from 'json-difference' import { getDiff } from 'json-difference'
if(process.env.CHANNEL !== 'stable'){ if(process.env.CHANNEL !== 'stable'){

View File

@ -1,12 +1,13 @@
import CropModePersistence from '../../../common/enums/CropModePersistence.enum'; import CropModePersistence from '@src/common/enums/CropModePersistence.enum';
import ExtensionMode from '../../../common/enums/ExtensionMode.enum'; import EmbeddedContentSettingsOverridePolicy from '@src/common/enums/EmbeddedContentSettingsOverridePolicy.enum';
import { ExtensionEnvironment, SettingsReloadComponent, SettingsReloadFlags, SiteSettingsInterface } from '../../../common/interfaces/SettingsInterface'; import ExtensionMode from '@src/common/enums/ExtensionMode.enum';
import { _cp } from '../../../common/js/utils'; import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum';
import Settings from './Settings'; import { SiteSupportLevel } from '@src/common/enums/SiteSupportLevel.enum';
import StretchType from '../../../common/enums/StretchType.enum'; import StretchType from '@src/common/enums/StretchType.enum';
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum'; import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum';
import EmbeddedContentSettingsOverridePolicy from '../../../common/enums/EmbeddedContentSettingsOverridePolicy.enum'; import { ExtensionEnvironment, SettingsReloadComponent, SettingsReloadFlags, SiteSettingsInterface } from '@src/common/interfaces/SettingsInterface';
import { SiteSupportLevel } from '../../../common/enums/SiteSupportLevel.enum'; import { _cp } from '@src/common/utils/_cp';
import Settings from '@src/ext/module/settings/Settings';
export interface GetSiteSettingsOptions { export interface GetSiteSettingsOptions {
@ -322,7 +323,10 @@ export class SiteSettings {
* @returns querySelector if possible, undefined otherwise * @returns querySelector if possible, undefined otherwise
*/ */
getCustomDOMQuerySelector(element: 'video' | 'player'): string | undefined { getCustomDOMQuerySelector(element: 'video' | 'player'): string | undefined {
return this.data.currentDOMConfig?.elements?.[element]?.manual && this.data.currentDOMConfig?.elements?.[element]?.querySelectors || undefined; return (
this.data.currentDOMConfig?.elements?.[element]?.detectionMode === PlayerDetectionMode.QuerySelectors
&& this.data.currentDOMConfig?.elements?.[element]?.querySelectors
) ?? undefined;
} }
/** /**
@ -331,8 +335,8 @@ export class SiteSettings {
* @returns parent element index if possible, undefined otherwise * @returns parent element index if possible, undefined otherwise
*/ */
getPlayerIndex(): number | undefined { getPlayerIndex(): number | undefined {
return this.data.currentDOMConfig?.elements?.player?.manual && this.data.currentDOMConfig?.elements?.player?.index return ( this.data.currentDOMConfig?.elements?.player?.detectionMode === PlayerDetectionMode.AncestorIndex && this.data.currentDOMConfig?.elements?.player?.ancestorIndex )
|| this.data.playerAutoConfig?.modified && this.data.playerAutoConfig?.currentIndex || ( this.data.playerAutoConfig?.modified && this.data.playerAutoConfig?.currentIndex )
|| undefined; || undefined;
} }

View File

@ -97,10 +97,13 @@ class UI {
window.addEventListener('message', (event: MessageEvent) => { window.addEventListener('message', (event: MessageEvent) => {
const data = event.data; const data = event.data;
if (data?.action !== 'uw-bus-tunnel') return; if (data?.action !== 'uw-bus-tunnel') {
return;
}
const command = data.payload.command; const payload = data.payload;
const config = data.payload.config;
console.log('forwarding from tunnel to event bus. payload', payload);
// Forward to all iframes except the source // Forward to all iframes except the source
(UwuiWindow as any).instances?.forEach(win => { (UwuiWindow as any).instances?.forEach(win => {
@ -109,7 +112,7 @@ class UI {
iframe.contentWindow?.postMessage( iframe.contentWindow?.postMessage(
{ {
action: 'uw-bus-tunnel', action: 'uw-bus-tunnel',
payload: { command, config } payload,
}, },
'*' '*'
); );
@ -454,9 +457,9 @@ class UI {
} }
}); });
this.eventBus.forwardToIframe(iframe, (cmd, payload, context) => { this.eventBus.forwardToIframe(iframe, (command, config, context) => {
iframe.contentWindow?.postMessage( iframe.contentWindow?.postMessage(
{ action: 'uw-bus-tunnel', payload: { command: cmd, config: payload } }, { action: 'uw-bus-tunnel', payload: { command, config, context } },
'*' '*'
); );
}); });

View File

@ -50,7 +50,11 @@ export class UwuiWindow {
this.background = options.background ?? 'blur'; this.background = options.background ?? 'blur';
this.extraStyles = options.extraStyles; this.extraStyles = options.extraStyles;
if (document.fullscreenElement) {
document.fullscreenElement.appendChild(this.host);
} else {
document.body.appendChild(this.host); document.body.appendChild(this.host);
}
this.shadow = this.host.attachShadow({ mode: 'closed' }); this.shadow = this.host.attachShadow({ mode: 'closed' });
this.shadow.innerHTML = this.template(options); this.shadow.innerHTML = this.template(options);

View File

@ -255,7 +255,13 @@ class PageInfo {
// //
// no but honestly fuck Chrome. // no but honestly fuck Chrome.
if (videosDetected || this.hasVideo()) { if (videosDetected || this.hasVideo()) {
this.eventBus.send('has-video', null); this.eventBus.send(
'has-video',
{
site: window.location.host,
isIframe: window.self !== window.top,
}
);
} else { } else {
this.eventBus.send('noVideo', null); this.eventBus.send('noVideo', null);
} }

View File

@ -1,17 +1,15 @@
import Debug from '../../conf/Debug'; import ExtensionMode from '@src/common/enums/ExtensionMode.enum';
import ExtensionMode from '../../../common/enums/ExtensionMode.enum' import { PlayerDetectionMode } from '@src/common/enums/PlayerDetectionMode.enum';
import PlayerNotificationUi from '../uwui/PlayerNotificationUI'; import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
import { collectionHas, equalish } from '@src/common/utils/comparators';
import { RunLevel } from '@src/ext/enum/run-level.enum';
import EventBus from '@src/ext/module/EventBus';
import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger';
import { SiteSettings } from '@src/ext/module/settings/SiteSettings';
import UI from '@src/ext/module/uwui/UI';
import PageInfo from '@src/ext/module/video-data/PageInfo';
import VideoData from '@src/ext/module/video-data/VideoData';
import * as _ from 'lodash'; import * as _ from 'lodash';
import { sleep } from '../../../common/js/utils';
import VideoData from './VideoData';
import EventBus from '../EventBus';
import UI from '../uwui/UI';
import { SiteSettings } from '../settings/SiteSettings';
import PageInfo from './PageInfo';
import { RunLevel } from '../../enum/run-level.enum';
import { ExtensionEnvironment } from '../../../common/interfaces/SettingsInterface';
import { ComponentLogger } from '../logging/ComponentLogger';
import { collectionHas, equalish } from '../../util/comparators';
if (process.env.CHANNEL !== 'stable'){ if (process.env.CHANNEL !== 'stable'){
console.info("Loading: PlayerData.js"); console.info("Loading: PlayerData.js");
@ -77,7 +75,7 @@ class PlayerData {
videoData: VideoData; videoData: VideoData;
pageInfo: PageInfo; pageInfo: PageInfo;
siteSettings: SiteSettings; siteSettings: SiteSettings;
notificationService: PlayerNotificationUi; notificationService: any; // TODO: implement this
eventBus: EventBus; eventBus: EventBus;
//#endregion //#endregion
@ -600,6 +598,8 @@ class PlayerData {
* Finds and returns HTML element of the player * Finds and returns HTML element of the player
*/ */
private getPlayer(options?: {verbose?: boolean}): HTMLElement { private getPlayer(options?: {verbose?: boolean}): HTMLElement {
const detectionMode = this.siteSettings.data.currentDOMConfig?.elements?.player?.detectionMode;
const videoWidth = this.videoElement.offsetWidth; const videoWidth = this.videoElement.offsetWidth;
const videoHeight = this.videoElement.offsetHeight; const videoHeight = this.videoElement.offsetHeight;
let playerCandidate; let playerCandidate;
@ -619,31 +619,16 @@ class PlayerData {
} }
} }
// if mode is given, we follow the preference if (detectionMode === PlayerDetectionMode.AncestorIndex) {
if (this.siteSettings.data.currentDOMConfig?.elements?.player?.manual && this.siteSettings.data.currentDOMConfig?.elements?.player?.mode) {
if (this.siteSettings.data.currentDOMConfig?.elements?.player?.mode === 'qs') {
playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight);
} else {
playerCandidate = elementStack[playerIndex]; playerCandidate = elementStack[playerIndex];
playerCandidate.heuristics['manualElementByParentIndex'] = true; playerCandidate.heuristics['manualElementByParentIndex'] = true;
} } else if (detectionMode === PlayerDetectionMode.QuerySelectors) {
} else {
// try to figure it out based on what we have, with playerQs taking priority
if (playerQs) {
playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight); playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight);
} else if (playerIndex) { // btw 0 is not a valid index for player
playerCandidate = elementStack[playerIndex];
playerCandidate.heuristics['manualElementByParentIndex'] = true;
}
} }
if (playerCandidate) { // we fall back to automatic player detection even if detectionMode is
if (options?.verbose) { // not set to auto.
this.getPlayerAuto(elementStack, videoWidth, videoHeight); if (detectionMode === PlayerDetectionMode.Auto || !playerCandidate) {
playerCandidate.heuristics['activePlayer'] = true;
}
return playerCandidate.element;
} else {
const playerCandidate = this.getPlayerAuto(elementStack, videoWidth, videoHeight); const playerCandidate = this.getPlayerAuto(elementStack, videoWidth, videoHeight);
if (playerCandidate === null) { if (playerCandidate === null) {
console.warn('[uw::getPlayer] getPlayerAuto returned null — no player detected?'); console.warn('[uw::getPlayer] getPlayerAuto returned null — no player detected?');
@ -651,6 +636,12 @@ class PlayerData {
playerCandidate.heuristics['activePlayer'] = true; playerCandidate.heuristics['activePlayer'] = true;
return playerCandidate.element; return playerCandidate.element;
} }
} else {
if (options?.verbose) {
this.getPlayerAuto(elementStack, videoWidth, videoHeight);
playerCandidate.heuristics['activePlayer'] = true;
}
return playerCandidate.element;
} }
} }
@ -703,6 +694,15 @@ class PlayerData {
continue; continue;
} }
// when in full screen, we need to worry about top-layer shenanigans
if (!!fullscreenElement) {
console.log('we are in full screen — checking for top layer shenanigans. Is this element visible from top layer?', fullscreenElement.contains(elementData.element))
if (!fullscreenElement.contains(elementData.element)) {
elementData.heuristics['notVisibleInTopLayer'] = true;
continue;
}
}
if (elementData.element === fullscreenElement) { if (elementData.element === fullscreenElement) {
// reset penalty multiplier if current element is full screen element // reset penalty multiplier if current element is full screen element
// penaltyMultiplier here must be smaller than initial penalty multiplier, // penaltyMultiplier here must be smaller than initial penalty multiplier,

View File

@ -1,23 +1,22 @@
import PlayerData from './PlayerData'; import { Ar } from '@src/common/interfaces/ArInterface';
import Resizer from '../video-transform/Resizer'; import { ExtensionEnvironment } from '@src/common/interfaces/SettingsInterface';
import * as _ from 'lodash'; import { Stretch } from '@src/common/interfaces/StretchInterface';
import BrowserDetect from '../../conf/BrowserDetect'; import { sleep } from '@src/common/utils/sleep';
import Settings from '../settings/Settings'; import BrowserDetect from '@src/ext/conf/BrowserDetect';
import PageInfo from './PageInfo'; import { RunLevel } from '@src/ext/enum/run-level.enum';
import { sleep } from '../../../common/js/utils'; import { Aard } from '@src/ext/module/aard/Aard';
import EventBus from '../EventBus'; import { AardLegacy } from '@src/ext/module/aard/AardLegacy';
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';
import { AardLegacy } from '../aard/AardLegacy';
import { hasDrm } from '@src/ext/module/ar-detect/DrmDetector'; import { hasDrm } from '@src/ext/module/ar-detect/DrmDetector';
import EventBus from '@src/ext/module/EventBus';
import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger';
import { LogAggregator } from '@src/ext/module/logging/LogAggregator';
import Settings from '@src/ext/module/settings/Settings';
import { SiteSettings } from '@src/ext/module/settings/SiteSettings';
import { ExtensionStatus } from '@src/ext/module/video-data/ExtensionStatus';
import PageInfo from '@src/ext/module/video-data/PageInfo';
import PlayerData from '@src/ext/module/video-data/PlayerData';
import Resizer from '@src/ext/module/video-transform/Resizer';
import * as _ from 'lodash';
/** /**
* VideoData handles CSS for the video element. * VideoData handles CSS for the video element.
@ -106,6 +105,11 @@ class VideoData {
function: () => { function: () => {
this.onEnvironmentChanged(); this.onEnvironmentChanged();
} }
},
'get-current-site': {
function: () => {
console.warn('received get current site!');
}
} }
} }

View File

@ -1,21 +1,20 @@
import { SiteSettings } from '../settings/SiteSettings'; import AspectRatioType from '@src/common/enums/AspectRatioType.enum';
import Debug from '../../conf/Debug'; import StretchType from '@src/common/enums/StretchType.enum';
import Scaler, { CropStrategy, VideoDimensions } from './Scaler'; import VideoAlignmentType from '@src/common/enums/VideoAlignmentType.enum';
import Stretcher from './Stretcher'; import { Ar, ArVariant } from '@src/common/interfaces/ArInterface';
import Zoom from './Zoom'; import { Stretch } from '@src/common/interfaces/StretchInterface';
import StretchType from '../../../common/enums/StretchType.enum'; import getElementStyles from '@src/common/utils/getElementStyles';
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum'; import Debug from '@src/ext/conf/Debug';
import AspectRatioType from '../../../common/enums/AspectRatioType.enum'; import { RunLevel } from '@src/ext/enum/run-level.enum';
import VideoData from '../video-data/VideoData'; import EventBus from '@src/ext/module/EventBus';
import EventBus from '../EventBus'; import { ComponentLogger } from '@src/ext/module/logging/ComponentLogger';
import { _cp } from '../../../common/js/utils'; import Settings from '@src/ext/module/settings/Settings';
import Settings from '../settings/Settings'; import { SiteSettings } from '@src/ext/module/settings/SiteSettings';
import { Ar, ArVariant } from '../../../common/interfaces/ArInterface'; import VideoData from '@src/ext/module/video-data/VideoData';
import { RunLevel } from '../../enum/run-level.enum'; import Scaler, { VideoDimensions } from '@src/ext/module/video-transform/Scaler';
import Stretcher from '@src/ext/module/video-transform/Stretcher';
import Zoom from '@src/ext/module/video-transform/Zoom';
import * as _ from 'lodash'; import * as _ from 'lodash';
import getElementStyles from '../../util/getElementStyles';
import { Stretch } from '../../../common/interfaces/StretchInterface';
import { ComponentLogger } from '../logging/ComponentLogger';
if(Debug.debug) { if(Debug.debug) {
console.log("Loading: Resizer.js"); console.log("Loading: Resizer.js");

View File

@ -201,7 +201,7 @@ import UploadJsonFileButton from '@components/common/UploadJsonFileButton.vue';
import { LogAggregator, BLANK_LOGGER_CONFIG } from '@src/ext/module/logging/LogAggregator'; import { LogAggregator, BLANK_LOGGER_CONFIG } from '@src/ext/module/logging/LogAggregator';
import { SettingsSnapshot } from '@src/ext/module/settings/SettingsSnapshotManager'; import { SettingsSnapshot } from '@src/ext/module/settings/SettingsSnapshotManager';
import SettingsInterface from '@src/common/interfaces/SettingsInterface'; import SettingsInterface from '@src/common/interfaces/SettingsInterface';
import { _cp } from '@src/common/js/utils'; import { _cp } from '@src/common/utils/_cp';
export default defineComponent({ export default defineComponent({
components: { components: {

View File

@ -305,7 +305,7 @@ import SupportLevelIndicator from '@components/common/SupportLevelIndicator.vue'
import ConfirmButton from '@components/common/ConfirmButton.vue'; import ConfirmButton from '@components/common/ConfirmButton.vue';
import Popup from '@components/common/Popup.vue'; import Popup from '@components/common/Popup.vue';
import { _cp } from '@src/common/js/utils'; import { _cp } from '@src/common/utils/_cp';
import { SiteDOMSettingsInterface } from '@src/common/interfaces/SettingsInterface'; import { SiteDOMSettingsInterface } from '@src/common/interfaces/SettingsInterface';
import { SiteSupportLevel } from '../../../../../common/enums/SiteSupportLevel.enum'; import { SiteSupportLevel } from '../../../../../common/enums/SiteSupportLevel.enum';

View File

@ -263,36 +263,12 @@ export default defineComponent({
this.selectedTab = this.defaultTab; this.selectedTab = this.defaultTab;
} }
console.log('does event bus exist yet?', this.eventBus);
if (!this.eventBus) { if (!this.eventBus) {
// inter-frame communication should be set up by eventBus for free
this.eventBus = new EventBus({name: 'ui-window'}); this.eventBus = new EventBus({name: 'ui-window'});
} }
/**
* SETUP CROSS-FRAME COMMUNICATION
*
* 1. We need to allow eventBus to send messages to the parent page
* 2. We need to plug messages we receive from parent page into the event bus
*/
this.eventBus.sendToTunnel = (command: string, config: any) => {
window.parent.postMessage(
{
action: 'uw-bus-tunnel',
payload: { command, config }
},
'*'
);
};
window.addEventListener('message', (event: MessageEvent) => {
const data = event.data;
if (data?.action === 'uw-bus-tunnel') {
// prevent double-crossing by marking borderCrossings
this.eventBus.send(data.payload.command, data.payload.config, {
borderCrossings: { iframe: true }
});
}
});
/** /**
* Subscribe to event bus commands. * Subscribe to event bus commands.
* Note that showing and hiding of the settings window is no longer handled by iframe * Note that showing and hiding of the settings window is no longer handled by iframe
@ -336,8 +312,34 @@ export default defineComponent({
this.loadFrames(); this.loadFrames();
} }
}, },
'has-video': {
function: (config, context) => {
console.log('has video received.', config, context);
console.log('———————— building site:', !config.isIFrame, this.site?.host, config.site, this.site?.host !== config.site);
// update site data.
// If we're here, then the settings page is being opened from the in-player popup.
// This means that config.isIframe can generally be ignored.
// However, we absolutely cannot accept iframes as source for this.site when we
// open settings window from the extension popup. This needs to be TODO:
if (this.site?.host !== config.site) {
this.site = {
host: config.site,
frames: [],
hostnames: [],
};
this.siteSettings = this.settings.getSiteSettings({site: this.site.host});
this.loadHostnames();
this.loadFrames();
}
}
},
}); });
console.log('—————————————————————— polling from iframe window!!!!')
this.startSitePolling(); this.startSitePolling();
// this.eventBus.subscribe( // this.eventBus.subscribe(
// 'uw-show-ui', // 'uw-show-ui',

View File

@ -167,3 +167,13 @@
} }
} }
} }
.warning-box, .info-box {
@apply px-4 py-2 flex flex-row gap-4 border;
}
.warning-box {
@apply border-amber-500 text-amber-300 bg-amber-950/75;
}
.info-box {
@apply border-blue-500 text-blue-300 bg-blue-950/75;
}