diff --git a/src/ext/module/EventBus.ts b/src/ext/module/EventBus.ts index c297caf..de5b253 100644 --- a/src/ext/module/EventBus.ts +++ b/src/ext/module/EventBus.ts @@ -92,9 +92,11 @@ export default class EventBus { send(command: string, commandData: any, context: EventBusContext = {}) { if (context.visitedBusses?.includes(this.uuid)) { + console.warn('this bus was already visited before. Doing nothing.'); return; } if (context.commandId && this.lastExecutedCommandIds.includes(context.commandId)) { + console.warn('this command was already sent'); return; } context.visitedBusses = [...context.visitedBusses ?? [], this.uuid]; @@ -165,7 +167,6 @@ export default class EventBus { console.error('Visited busses is missing from context. This is illegal.'); return; } - context.visitedBusses = [...context.visitedBusses ?? [], this.uuid]; if (!this.disableTunnel && typeof window !== 'undefined') { window.parent.postMessage( diff --git a/src/ext/module/uwui/UI.ts b/src/ext/module/uwui/UI.ts index dec7e35..863a2e7 100644 --- a/src/ext/module/uwui/UI.ts +++ b/src/ext/module/uwui/UI.ts @@ -117,32 +117,41 @@ class UI { this.initMessaging(); } + private messageListener?: (event?: MessageEvent) => void; + private initMessaging() { - window.addEventListener('message', (event: MessageEvent) => { - const data = event.data; + if (this.messageListener) { + window.removeEventListener('message', this.messageListener); + } else { + this.messageListener = (event: MessageEvent) => { + const data = event.data; - if (data?.action !== 'uw-bus-tunnel') { - return; - } - - const payload = data.payload; - - console.log('forwarding from tunnel to event bus. payload', payload); - - // Forward to all iframes except the source - (UwuiWindow as any).instances?.forEach(win => { - const iframe = win.content as HTMLIFrameElement; - if (iframe && event.source !== iframe.contentWindow) { - iframe.contentWindow?.postMessage( - { - action: 'uw-bus-tunnel', - payload, - }, - '*' - ); + if (data?.action !== 'uw-bus-tunnel') { + return; } - }); - }); + + + const payload = data.payload; + + console.log('forwarding from tunnel to event bus. payload', payload); + + // Forward to all iframes except the source + (UwuiWindow as any).instances?.forEach(win => { + const iframe = win.content as HTMLIFrameElement; + if (iframe && event.source !== iframe.contentWindow) { + iframe.contentWindow?.postMessage( + { + action: 'uw-bus-tunnel', + payload, + }, + '*' + ); + } + }); + }; + } + + window.addEventListener('message', this.messageListener); } executeCommand(x: CommandInterface) { @@ -464,6 +473,18 @@ class UI { createSettingsWindow(path?: string) { const iframe = document.createElement('iframe'); + // we don't enforce minimum margin on small screens + const margin = (window.innerWidth < 1024 || window.innerHeight < 720) ? 0 : 64; + + const params = { + width: Math.min(1600, window.innerWidth - margin), + height: Math.min(920, window.innerHeight - margin), + x: 0, + y: 0 + }; + params.x = Math.floor((window.innerWidth - params.width) / 2); + params.y = Math.floor((window.innerHeight - params.height) / 2); + iframe.src = chrome.runtime.getURL(`ui/pages/settings/index.html#ui${path ? `/${path}` : ''}`); iframe.setAttribute('allowtransparency', 'true'); Object.assign(iframe.style, { @@ -475,10 +496,7 @@ class UI { new UwuiWindow({ title: `Ultrawidify settings (${window.location.host})`, - width: 1200, - height: 800, - x: 0, - y: 0, + ...params, content: iframe, onClose: () => { this.eventBus.cancelIframeForwarding(iframe) diff --git a/src/ext/module/video-data/PlayerData.ts b/src/ext/module/video-data/PlayerData.ts index eeef6d9..142ea84 100644 --- a/src/ext/module/video-data/PlayerData.ts +++ b/src/ext/module/video-data/PlayerData.ts @@ -103,6 +103,7 @@ class PlayerData { private trackChangesTimeout: any; private markedElement: HTMLElement; + private markedElementIndex: number | undefined; private ui: UI; @@ -114,7 +115,7 @@ class PlayerData { //#region event bus configuration private eventBusCommands = { 'get-player-tree': [{ - function: () => this.handlePlayerTreeRequest() + function: (data) => this.handlePlayerTreeRequest(data) }], 'get-player-dimensions': [{ function: () => { @@ -530,6 +531,7 @@ class PlayerData { private getElementStack(): ElementStack { const elementStack: ElementStack = [{ + index: 0, element: this.videoElement, type: 'video', tagName: 'video', @@ -540,8 +542,10 @@ class PlayerData { let element = this.videoElement.parentNode as HTMLElement; // first pass to generate the element stack and translate it into array + let i = 1; while (element) { elementStack.push({ + index: i, element, type: '', tagName: element.tagName, @@ -552,6 +556,7 @@ class PlayerData { heuristics: {}, }); element = element.parentElement; + i++; } this.elementStack = elementStack; @@ -610,6 +615,7 @@ class PlayerData { // on verbose, get both qs and index player if (options?.verbose) { + this.getPlayerAuto(elementStack, videoHeight, videoHeight, {listOnly: true}); if (playerIndex) { playerCandidate = elementStack[playerIndex]; playerCandidate.heuristics['manualElementByParentIndex'] = true; @@ -622,6 +628,7 @@ class PlayerData { if (detectionMode === PlayerDetectionMode.AncestorIndex) { playerCandidate = elementStack[playerIndex]; playerCandidate.heuristics['manualElementByParentIndex'] = true; + playerCandidate.heuristics['activePlayer'] = true; } else if (detectionMode === PlayerDetectionMode.QuerySelectors) { playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight); } @@ -677,7 +684,7 @@ class PlayerData { * @param videoHeight * @returns */ - private getPlayerAuto(elementStack: ElementStack, videoWidth, videoHeight) { + private getPlayerAuto(elementStack: ElementStack, videoWidth, videoHeight, options?: {listOnly?: boolean}) { let penaltyMultiplier = 2; const sizePenaltyMultiplier = 0.1; const perLevelScorePenalty = 10; @@ -791,7 +798,7 @@ class PlayerData { // Some sites (youtube) can re-parent elements, causing current player element to vanish from DOM // Which means we need to set up an observer that will re-acquire the player when that happens. // TODO: Ideally, observer should request a tick - if (bestCandidate) { + if (bestCandidate && !options?.listOnly) { const observer = new MutationObserver( (mutations) => { mutations.forEach((mutation) => { @@ -868,30 +875,65 @@ class PlayerData { bestCandidate.heuristics['qsMatch'] = true; } + bestCandidate.heuristics['activePlayer'] = true; return bestCandidate; } /** * Lists elements between video and DOM root for display in player selector (UI) */ - private handlePlayerTreeRequest() { + private handlePlayerTreeRequest(data: {requestId: string}) { // this populates this.elementStack fully // this.updatePlayer({verbose: true}); - this.eventBus.send('uw-config-broadcast', {type: 'player-tree', config: JSON.parse(JSON.stringify(this.elementStack))}); + this.eventBus.send( + 'uw-config-broadcast', + { + type: 'player-tree', + requestId: data.requestId, + elementStack: JSON.parse(JSON.stringify(this.elementStack)) + } + ); } private markElement(data: {parentIndex: number, enable: boolean}) { if (data.enable === false) { this.markedElement.remove(); + this.elementStack[this.markedElementIndex]?.element.classList.remove('uw-mark-element'); + this.markedElementIndex = undefined; return; } - if (this.markedElement) { this.markedElement.remove(); } - const elementBB = this.elementStack[data.parentIndex].element.getBoundingClientRect();; + if (this.markedElementIndex !== undefined) { + this.elementStack[this.markedElementIndex]?.element.classList.remove('uw-mark-element'); + } else { + this.eventBus.send( + 'inject-css', + { + cssString: ` + .uw-mark-element { + border: 5px solid #fa6 !important; + box-sizing: border-box !important; + filter: + sepia(1) + saturate(5) + hue-rotate(-20deg) + brightness(1.15); + } + ` + } + ); + } + + this.markedElementIndex = data.parentIndex; + this.elementStack[this.markedElementIndex]?.element.classList.add('uw-mark-element'); + + console.log('———— setting element as active:', data.parentIndex, this.elementStack.length, this.elementStack, this.elementStack[data.parentIndex]) + + const elementBB = this.elementStack[data.parentIndex].element.getBoundingClientRect(); // console.log('element bounding box:', elementBB); @@ -910,6 +952,8 @@ class PlayerData { document.body.insertBefore(div, document.body.firstChild); this.markedElement = div; + + // this.elementStack[data.parentIndex].element.style.outline = data.enable ? '5px dashed #fa6' : null; // this.elementStack[data.parentIndex].element.style.filter = data.enable ? 'sepia(1) brightness(2) contrast(0.5)' : null; } diff --git a/src/res/img/player-select-demo/uw_player___element-list-hover.webp b/src/res/img/player-select-demo/uw_player___element-list-hover.webp index d0fe97d..bb22bc4 100644 Binary files a/src/res/img/player-select-demo/uw_player___element-list-hover.webp and b/src/res/img/player-select-demo/uw_player___element-list-hover.webp differ diff --git a/src/ui/components/SettingsWindowContent.vue b/src/ui/components/SettingsWindowContent.vue index 8c0f532..63672d0 100644 --- a/src/ui/components/SettingsWindowContent.vue +++ b/src/ui/components/SettingsWindowContent.vue @@ -167,12 +167,7 @@ :eventBus="eventBus" > - - --> + --> @@ -94,11 +169,16 @@ export default defineComponent({ data() { return { elementStack: [], + elementStacks: { + requestId: undefined, + stacks: [] + }, cssStack: [], showLegend: false, showAdvancedOptions: false, tutorialVisible: false, - tutorialStep: 0 + tutorialStep: 0, + lastTreeId: undefined, }; }, computed: { @@ -130,17 +210,47 @@ export default defineComponent({ this.tutorialStep = 0; }, getPlayerTree() { - this.eventBus.send('get-player-tree'); + this.lastTreeId = crypto.randomUUID() + this.eventBus.send('get-player-tree', {requestId: this.lastTreeId}); }, handleElementStack(configBroadcast) { if (configBroadcast.type === 'player-tree') { - this.elementStack = configBroadcast.config.reverse(); - this.$nextTick( () => this.$forceUpdate() ); + // reset tree if lastTreeId has changed + if (this.elementStacks.requestId !== this.lastTreeId) { + this.elementStacks = { + requestId: this.lastTreeId, + stacks: [] + }; + } + + if (configBroadcast.requestId === this.lastTreeId) { + const stack = configBroadcast.elementStack.sort((a, b) => a.index - b.index); + this.elementStacks.stacks.push({ + elementStack: stack + }); + this.elementStack = stack; + + this.$nextTick( () => this.$forceUpdate() ); + } } }, markElement(parentIndex, enable) { this.eventBus.send('set-mark-element', {parentIndex, enable}); }, + async resetSettings() { + if (!this.siteSettings.raw?.activeDOMConfig) { + console.warn('') + return; + } + + await this.siteSettings.setUpdateFlags(['PlayerData']); + await this.siteSettings.set(`DOMConfig.${this.siteSettings.data.activeDOMConfig}.player.detectionMode`, PlayerDetectionMode.Auto, {noSave: true}); + await this.siteSettings.set(`DOMConfig.${this.siteSettings.data.activeDOMConfig}.player.ancestorIndex`, undefined); + + this.getPlayerTree(); + setTimeout( () => this.getPlayerTree(), 500); + setTimeout( () => this.getPlayerTree(), 1000); + }, /** * Designates new element as player element. Currently, we only need * 'index', however at some point we might also set mode according @@ -170,6 +280,8 @@ export default defineComponent({ await this.siteSettings.set('activeDOMConfig', domConfigName); this.getPlayerTree(); + setTimeout(() => this.getPlayerTree(), 500); + setTimeout(() => this.getPlayerTree(), 1000); }, /** * Toggles active CSS for element of certain parent index. diff --git a/src/ui/components/segments/PlayerElementSelection/PlayerElementWindow.vue b/src/ui/components/segments/PlayerElementSelection/PlayerElementWindow.vue deleted file mode 100644 index bc9f1e3..0000000 --- a/src/ui/components/segments/PlayerElementSelection/PlayerElementWindow.vue +++ /dev/null @@ -1,216 +0,0 @@ - - -