Player selection should be functional enough
This commit is contained in:
parent
dbdcb4e367
commit
f57163f2bb
@ -92,9 +92,11 @@ export default class EventBus {
|
|||||||
|
|
||||||
send(command: string, commandData: any, context: EventBusContext = {}) {
|
send(command: string, commandData: any, context: EventBusContext = {}) {
|
||||||
if (context.visitedBusses?.includes(this.uuid)) {
|
if (context.visitedBusses?.includes(this.uuid)) {
|
||||||
|
console.warn('this bus was already visited before. Doing nothing.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (context.commandId && this.lastExecutedCommandIds.includes(context.commandId)) {
|
if (context.commandId && this.lastExecutedCommandIds.includes(context.commandId)) {
|
||||||
|
console.warn('this command was already sent');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.visitedBusses = [...context.visitedBusses ?? [], this.uuid];
|
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.');
|
console.error('Visited busses is missing from context. This is illegal.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.visitedBusses = [...context.visitedBusses ?? [], this.uuid];
|
|
||||||
|
|
||||||
if (!this.disableTunnel && typeof window !== 'undefined') {
|
if (!this.disableTunnel && typeof window !== 'undefined') {
|
||||||
window.parent.postMessage(
|
window.parent.postMessage(
|
||||||
|
|||||||
@ -117,32 +117,41 @@ class UI {
|
|||||||
this.initMessaging();
|
this.initMessaging();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private messageListener?: (event?: MessageEvent) => void;
|
||||||
|
|
||||||
private initMessaging() {
|
private initMessaging() {
|
||||||
window.addEventListener('message', (event: MessageEvent) => {
|
if (this.messageListener) {
|
||||||
const data = event.data;
|
window.removeEventListener('message', this.messageListener);
|
||||||
|
} else {
|
||||||
|
this.messageListener = (event: MessageEvent) => {
|
||||||
|
const data = event.data;
|
||||||
|
|
||||||
if (data?.action !== 'uw-bus-tunnel') {
|
if (data?.action !== 'uw-bus-tunnel') {
|
||||||
return;
|
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,
|
|
||||||
},
|
|
||||||
'*'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
|
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) {
|
executeCommand(x: CommandInterface) {
|
||||||
@ -464,6 +473,18 @@ class UI {
|
|||||||
createSettingsWindow(path?: string) {
|
createSettingsWindow(path?: string) {
|
||||||
const iframe = document.createElement('iframe');
|
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.src = chrome.runtime.getURL(`ui/pages/settings/index.html#ui${path ? `/${path}` : ''}`);
|
||||||
iframe.setAttribute('allowtransparency', 'true');
|
iframe.setAttribute('allowtransparency', 'true');
|
||||||
Object.assign(iframe.style, {
|
Object.assign(iframe.style, {
|
||||||
@ -475,10 +496,7 @@ class UI {
|
|||||||
|
|
||||||
new UwuiWindow({
|
new UwuiWindow({
|
||||||
title: `Ultrawidify settings (${window.location.host})`,
|
title: `Ultrawidify settings (${window.location.host})`,
|
||||||
width: 1200,
|
...params,
|
||||||
height: 800,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
content: iframe,
|
content: iframe,
|
||||||
onClose: () => {
|
onClose: () => {
|
||||||
this.eventBus.cancelIframeForwarding(iframe)
|
this.eventBus.cancelIframeForwarding(iframe)
|
||||||
|
|||||||
@ -103,6 +103,7 @@ class PlayerData {
|
|||||||
|
|
||||||
private trackChangesTimeout: any;
|
private trackChangesTimeout: any;
|
||||||
private markedElement: HTMLElement;
|
private markedElement: HTMLElement;
|
||||||
|
private markedElementIndex: number | undefined;
|
||||||
|
|
||||||
private ui: UI;
|
private ui: UI;
|
||||||
|
|
||||||
@ -114,7 +115,7 @@ class PlayerData {
|
|||||||
//#region event bus configuration
|
//#region event bus configuration
|
||||||
private eventBusCommands = {
|
private eventBusCommands = {
|
||||||
'get-player-tree': [{
|
'get-player-tree': [{
|
||||||
function: () => this.handlePlayerTreeRequest()
|
function: (data) => this.handlePlayerTreeRequest(data)
|
||||||
}],
|
}],
|
||||||
'get-player-dimensions': [{
|
'get-player-dimensions': [{
|
||||||
function: () => {
|
function: () => {
|
||||||
@ -530,6 +531,7 @@ class PlayerData {
|
|||||||
|
|
||||||
private getElementStack(): ElementStack {
|
private getElementStack(): ElementStack {
|
||||||
const elementStack: ElementStack = [{
|
const elementStack: ElementStack = [{
|
||||||
|
index: 0,
|
||||||
element: this.videoElement,
|
element: this.videoElement,
|
||||||
type: 'video',
|
type: 'video',
|
||||||
tagName: 'video',
|
tagName: 'video',
|
||||||
@ -540,8 +542,10 @@ class PlayerData {
|
|||||||
let element = this.videoElement.parentNode as HTMLElement;
|
let element = this.videoElement.parentNode as HTMLElement;
|
||||||
|
|
||||||
// first pass to generate the element stack and translate it into array
|
// first pass to generate the element stack and translate it into array
|
||||||
|
let i = 1;
|
||||||
while (element) {
|
while (element) {
|
||||||
elementStack.push({
|
elementStack.push({
|
||||||
|
index: i,
|
||||||
element,
|
element,
|
||||||
type: '',
|
type: '',
|
||||||
tagName: element.tagName,
|
tagName: element.tagName,
|
||||||
@ -552,6 +556,7 @@ class PlayerData {
|
|||||||
heuristics: {},
|
heuristics: {},
|
||||||
});
|
});
|
||||||
element = element.parentElement;
|
element = element.parentElement;
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.elementStack = elementStack;
|
this.elementStack = elementStack;
|
||||||
@ -610,6 +615,7 @@ class PlayerData {
|
|||||||
|
|
||||||
// on verbose, get both qs and index player
|
// on verbose, get both qs and index player
|
||||||
if (options?.verbose) {
|
if (options?.verbose) {
|
||||||
|
this.getPlayerAuto(elementStack, videoHeight, videoHeight, {listOnly: true});
|
||||||
if (playerIndex) {
|
if (playerIndex) {
|
||||||
playerCandidate = elementStack[playerIndex];
|
playerCandidate = elementStack[playerIndex];
|
||||||
playerCandidate.heuristics['manualElementByParentIndex'] = true;
|
playerCandidate.heuristics['manualElementByParentIndex'] = true;
|
||||||
@ -622,6 +628,7 @@ class PlayerData {
|
|||||||
if (detectionMode === PlayerDetectionMode.AncestorIndex) {
|
if (detectionMode === PlayerDetectionMode.AncestorIndex) {
|
||||||
playerCandidate = elementStack[playerIndex];
|
playerCandidate = elementStack[playerIndex];
|
||||||
playerCandidate.heuristics['manualElementByParentIndex'] = true;
|
playerCandidate.heuristics['manualElementByParentIndex'] = true;
|
||||||
|
playerCandidate.heuristics['activePlayer'] = true;
|
||||||
} else if (detectionMode === PlayerDetectionMode.QuerySelectors) {
|
} else if (detectionMode === PlayerDetectionMode.QuerySelectors) {
|
||||||
playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight);
|
playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight);
|
||||||
}
|
}
|
||||||
@ -677,7 +684,7 @@ class PlayerData {
|
|||||||
* @param videoHeight
|
* @param videoHeight
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
private getPlayerAuto(elementStack: ElementStack, videoWidth, videoHeight) {
|
private getPlayerAuto(elementStack: ElementStack, videoWidth, videoHeight, options?: {listOnly?: boolean}) {
|
||||||
let penaltyMultiplier = 2;
|
let penaltyMultiplier = 2;
|
||||||
const sizePenaltyMultiplier = 0.1;
|
const sizePenaltyMultiplier = 0.1;
|
||||||
const perLevelScorePenalty = 10;
|
const perLevelScorePenalty = 10;
|
||||||
@ -791,7 +798,7 @@ class PlayerData {
|
|||||||
// Some sites (youtube) can re-parent elements, causing current player element to vanish from DOM
|
// 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.
|
// 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
|
// TODO: Ideally, observer should request a tick
|
||||||
if (bestCandidate) {
|
if (bestCandidate && !options?.listOnly) {
|
||||||
const observer = new MutationObserver(
|
const observer = new MutationObserver(
|
||||||
(mutations) => {
|
(mutations) => {
|
||||||
mutations.forEach((mutation) => {
|
mutations.forEach((mutation) => {
|
||||||
@ -868,30 +875,65 @@ class PlayerData {
|
|||||||
bestCandidate.heuristics['qsMatch'] = true;
|
bestCandidate.heuristics['qsMatch'] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bestCandidate.heuristics['activePlayer'] = true;
|
||||||
return bestCandidate;
|
return bestCandidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists elements between video and DOM root for display in player selector (UI)
|
* 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 populates this.elementStack fully
|
||||||
// this.updatePlayer({verbose: true});
|
// 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}) {
|
private markElement(data: {parentIndex: number, enable: boolean}) {
|
||||||
if (data.enable === false) {
|
if (data.enable === false) {
|
||||||
this.markedElement.remove();
|
this.markedElement.remove();
|
||||||
|
this.elementStack[this.markedElementIndex]?.element.classList.remove('uw-mark-element');
|
||||||
|
this.markedElementIndex = undefined;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (this.markedElement) {
|
if (this.markedElement) {
|
||||||
this.markedElement.remove();
|
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);
|
// console.log('element bounding box:', elementBB);
|
||||||
|
|
||||||
@ -910,6 +952,8 @@ class PlayerData {
|
|||||||
document.body.insertBefore(div, document.body.firstChild);
|
document.body.insertBefore(div, document.body.firstChild);
|
||||||
this.markedElement = div;
|
this.markedElement = div;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this.elementStack[data.parentIndex].element.style.outline = data.enable ? '5px dashed #fa6' : null;
|
// 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;
|
// this.elementStack[data.parentIndex].element.style.filter = data.enable ? 'sepia(1) brightness(2) contrast(0.5)' : null;
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 43 KiB |
@ -167,12 +167,7 @@
|
|||||||
:eventBus="eventBus"
|
:eventBus="eventBus"
|
||||||
>
|
>
|
||||||
</PlayerElementSettings>
|
</PlayerElementSettings>
|
||||||
|
-->
|
||||||
<PlayerElementWindow
|
|
||||||
v-if="selectedTab === 'window.player-element-settings'"
|
|
||||||
:settings="settings"
|
|
||||||
:eventBus="eventBus"
|
|
||||||
></PlayerElementWindow> -->
|
|
||||||
|
|
||||||
<template v-if="selectedTab === 'window.player-element-advanced-settings'">
|
<template v-if="selectedTab === 'window.player-element-advanced-settings'">
|
||||||
<PlayerSelectorAdvancedForm
|
<PlayerSelectorAdvancedForm
|
||||||
@ -261,7 +256,6 @@ import { defineComponent } from 'vue';
|
|||||||
import VideoSettings from '@components/segments/VideoSettings/VideoSettings.vue';
|
import VideoSettings from '@components/segments/VideoSettings/VideoSettings.vue';
|
||||||
import OtherSiteSettings from '@components/segments/ExtensionSettings/Panels/OtherSiteSettings.vue';
|
import OtherSiteSettings from '@components/segments/ExtensionSettings/Panels/OtherSiteSettings.vue';
|
||||||
import PlayerElementSettings from '@components/segments/PlayerElementSelection/PlayerElementSettings.vue';
|
import PlayerElementSettings from '@components/segments/PlayerElementSelection/PlayerElementSettings.vue';
|
||||||
import PlayerElementWindow from '@components/segments/PlayerElementSelection/PlayerElementWindow.vue';
|
|
||||||
import AutodetectionSettings from '@components/segments/AutodetectionSettings/AutodetectionSettings.vue';
|
import AutodetectionSettings from '@components/segments/AutodetectionSettings/AutodetectionSettings.vue';
|
||||||
import UISettings from '@components/segments/UISettings/UISettings.vue';
|
import UISettings from '@components/segments/UISettings/UISettings.vue';
|
||||||
import KeyboardShortcutSettings from '@components/segments/KeyboardShortcuts/KeyboardShortcutSettings.vue';
|
import KeyboardShortcutSettings from '@components/segments/KeyboardShortcuts/KeyboardShortcutSettings.vue';
|
||||||
@ -379,7 +373,6 @@ export default defineComponent({
|
|||||||
VideoSettings,
|
VideoSettings,
|
||||||
OtherSiteSettings,
|
OtherSiteSettings,
|
||||||
PlayerElementSettings,
|
PlayerElementSettings,
|
||||||
PlayerElementWindow,
|
|
||||||
PlayerSelectorAdvancedForm,
|
PlayerSelectorAdvancedForm,
|
||||||
PlayerSelectorSimple,
|
PlayerSelectorSimple,
|
||||||
AutodetectionSettings,
|
AutodetectionSettings,
|
||||||
|
|||||||
@ -1,82 +1,157 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="w-full flex flex-col" style="margin-top: 1rem;">
|
<div class="w-full flex flex-col" style="margin-top: 1rem;">
|
||||||
|
|
||||||
<h2 class="text-[1.75em]">Simple video player picker</h2>
|
<h2 class="text-[1.75em]">Simple video player picker</h2>
|
||||||
<div class="text text-sm text-stone-500">
|
|
||||||
<p>If your video is not aligned correctly, video player was not detected correctly.</p>
|
|
||||||
<p>You need to help by selecting the video player. Hover over boxes below. This will highlight part of the screen.</p>
|
|
||||||
<p>Select the first box that highlights the video player on the page.</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- <div class="">
|
<template v-if="tutorialStep">
|
||||||
<div class="flex flex-row gap-4">
|
<div v-if="tutorialStep == 1" class="flex flex-col w-full justify-center items-center tutorial-step">
|
||||||
<button>How to use</button>
|
<h3 class="mb-4">1. Start hovering over elements on this list</h3>
|
||||||
</div>
|
<div class="flex flex-row w-full flex-wrap justify-center">
|
||||||
</div> -->
|
<div class="bg-black/50 rounded flex flex-col justify-center items-center p-4">
|
||||||
|
<div class="">
|
||||||
<!-- PLAYER ELEMENT SELECTOR FOR DUMMIES -->
|
<p>This list contains all the elements on the webpage that could be a video player.</p>
|
||||||
<div class="flex flex-col-reverse gap-2">
|
<p>Icons to the left of the list indicate which element is currently selected as the player element, which element the extension thinks to be the player element, and which element should be the player according to manual settings.</p>
|
||||||
<div
|
<p>Move your mouse over the first element on the list, but do not click it.</p>
|
||||||
v-for="(element, index) of elementStack"
|
<p>Hovering over elements on the list will highlight parts of the page.</p>
|
||||||
:key="index"
|
</div>
|
||||||
class="py-2 px-4 border border-stone-500 flex flex-col gap-2 text-sm cursor-pointer"
|
<img src="/res/img/player-select-demo/uw_player___element-list-hover.webp" class="w-1/2 min-w-[420px]" />
|
||||||
:class="{
|
|
||||||
'!border-blue-500': element.heuristics?.autoMatch,
|
|
||||||
'!border-teal-800': element.heuristics?.manualElementByParentIndex,
|
|
||||||
'!border-emerald-500': element.heuristics?.qsMatch,
|
|
||||||
'!border-red-700 bg-red-950/50': element.heuristics?.invalidSize,
|
|
||||||
'!border-primary-300': element.heuristics?.activePlayer,
|
|
||||||
'!pointer-events-none opacity-50': element.tagName == 'video',
|
|
||||||
}"
|
|
||||||
|
|
||||||
@mouseover="markElement(elementStack.length - index - 1, true)"
|
|
||||||
@mouseleave="markElement(elementStack.length - index - 1, false)"
|
|
||||||
@click="setPlayer(element, elementStack.length - index - 1)"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.autoMatch"
|
|
||||||
class="text-primary-300 flex flex-row gap-2"
|
|
||||||
>
|
|
||||||
<mdicon name="check-circle" /> This element is currently treated as player element.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<div class="tag">
|
|
||||||
<b><{{element.tagName}}></b> <i class="text-red-800">{{element.id ? `#`:''}}{{element.id}}</i> @ <span class="text-blue-500">{{element.width}}</span>x<span class="text-blue-500">{{element.height}}</span>
|
|
||||||
</div>
|
|
||||||
<div v-if="element.classList" class="text-nowrap overflow-hidden text-ellipsis text-stone-500/50">
|
|
||||||
<span v-for="cls of element.classList" :key="cls">.{{cls}} </span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="p-4 flex flex-row w-full gap-2 item-center justify-center">
|
||||||
|
<button @click="tutorialStep = 2">Next</button>
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.qsMatch"
|
|
||||||
class="text-emerald-600 flex flex-row gap-2"
|
|
||||||
>
|
|
||||||
<mdicon name="crosshairs" /> This element matches query string (advanced settings)
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.manualElementByParentIndex"
|
|
||||||
class="text-teal-800 flex flex-row gap-2"
|
|
||||||
>
|
|
||||||
<mdicon name="bookmark" /> This element has been manually selected as player element.
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.autoMatch"
|
|
||||||
class="text-blue-500 flex flex-row gap-2"
|
|
||||||
>
|
|
||||||
<mdicon name="refresh-auto" /> Automatic detections thinks this is the player.
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="element.heuristics?.invalidSize"
|
|
||||||
class="text-red-700 flex flex-row gap-2"
|
|
||||||
>
|
|
||||||
<mdicon name="alert-remove" /> This element has invalid dimensions
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
<div v-if="tutorialStep == 2" class="flex flex-col w-full justify-center items-center tutorial-step">
|
||||||
|
<h3 class="mb-4">2. Observe highlight</h3>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-2 tutorial-list">
|
||||||
|
<div class="bg-black/50 rounded flex flex-col justify-end items-center p-4">
|
||||||
|
<div class="card-text">
|
||||||
|
<p>Hovering over the elements will highlight part of the page. Highlighted area should cover the player area.</p>
|
||||||
|
<p>If the highlighted area covers the player, click the item on the list to select it and reload the page.</p>
|
||||||
|
<p>If more than one element covers the player area, select the first (topmost) one on the list.</p>
|
||||||
|
</div>
|
||||||
|
<img src="/res/img/player-select-demo/uw_player_select___just-right.webp" />
|
||||||
|
|
||||||
|
<div class="icon text-teal-500">
|
||||||
|
<mdicon name="check-circle" size="64"></mdicon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-black/50 rounded flex flex-col justify-end items-center p-4">
|
||||||
|
<div class="card-text">
|
||||||
|
<p>If highlight covers more than just the player area, that usually means the correct element is further down the list.</p>
|
||||||
|
<p>Move the mouse cursor down the list of elements, until you encounter the first element that covers the player area.</p>
|
||||||
|
</div>
|
||||||
|
<img src="/res/img/player-select-demo/uw_player_select___too_much.webp" />
|
||||||
|
<div class="icon text-red-700">
|
||||||
|
<mdicon name="close-circle" size="64"></mdicon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bg-black/50 rounded flex flex-col justify-end items-center p-4">
|
||||||
|
<div class="card-text">
|
||||||
|
<p>If highlight doesn't cover the whole player area, that usually means the correct element is further down the list.</p>
|
||||||
|
<p>Move your cursor up the list of elements, until you encounter something that highlights the entire player area.</p>
|
||||||
|
<p>If more than one element covers the player area, select the first (topmost) one on the list.</p>
|
||||||
|
</div>
|
||||||
|
<img src="/res/img/player-select-demo/uw_player_select___too_little.webp" />
|
||||||
|
<div class="icon text-red-700">
|
||||||
|
<mdicon name="close-circle" size="64"></mdicon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-4 flex flex-row w-full gap-2 item-center justify-center">
|
||||||
|
<button @click="tutorialStep = 1">Previous</button>
|
||||||
|
<button @click="tutorialStep = 0">Done</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template v-else>
|
||||||
|
<div class="text text-sm text-stone-500">
|
||||||
|
<p>If your video is not aligned correctly, video player was not detected correctly.</p>
|
||||||
|
<p>You need to help by selecting the video player. Hover over boxes below. This will highlight part of the screen.</p>
|
||||||
|
<p>Select the first box that highlights the video player on the page.</p>
|
||||||
|
|
||||||
|
<div class="flex flex-row justify-between">
|
||||||
|
<a @click="tutorialStep = 1">Click here for more details.</a>
|
||||||
|
<a @click="resetSettings()">Reset settings</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- <div class="">
|
||||||
|
<div class="flex flex-row gap-4">
|
||||||
|
<button>How to use</button>
|
||||||
|
</div>
|
||||||
|
</div> -->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- PLAYER ELEMENT SELECTOR FOR DUMMIES -->
|
||||||
|
<div class="flex flex-col-reverse gap-2">
|
||||||
|
<div
|
||||||
|
v-for="(element, index) of elementStack"
|
||||||
|
:key="index"
|
||||||
|
class="py-2 px-4 border border-stone-500 flex flex-col gap-2 text-sm cursor-pointer hover:bg-stone-800/50"
|
||||||
|
:class="{
|
||||||
|
'!border-blue-500 !hover:bg-blue-500/50': element.heuristics?.autoMatch,
|
||||||
|
'!border-teal-800 !hover:bg-teal-800/50': element.heuristics?.manualElementByParentIndex,
|
||||||
|
'!border-emerald-500 !hover:bg-emerald-500/50': element.heuristics?.qsMatch,
|
||||||
|
'!border-red-700 !bg-red-950/50': element.heuristics?.invalidSize,
|
||||||
|
'!border-primary-300 !hover:bg-primary-800/50': element.heuristics?.activePlayer,
|
||||||
|
'!pointer-events-none opacity-50': ['html', 'body', 'video'].includes(element.tagName.toLowerCase()),
|
||||||
|
}"
|
||||||
|
|
||||||
|
@mouseover="markElement(element.index, true)"
|
||||||
|
@mouseleave="markElement(element.index, false)"
|
||||||
|
@click="setPlayer(element, element.index)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="element.heuristics?.activePlayer"
|
||||||
|
class="text-primary-300 flex flex-row gap-2"
|
||||||
|
>
|
||||||
|
<mdicon name="check-circle" /> This element is currently treated as player element.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="tag">
|
||||||
|
<span class="text-monospace w-16">[{{element.index}}]</span> <b><{{element.tagName}}></b> <i class="text-red-800">{{element.id ? `#`:''}}{{element.id}}</i> @ <span class="text-blue-500">{{element.width}}</span>x<span class="text-blue-500">{{element.height}}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="element.classList" class="text-nowrap overflow-hidden text-ellipsis text-stone-500/50">
|
||||||
|
<span v-for="cls of element.classList" :key="cls">.{{cls}} </span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="element.heuristics?.qsMatch"
|
||||||
|
class="text-emerald-600 flex flex-row gap-2"
|
||||||
|
>
|
||||||
|
<mdicon name="crosshairs" /> This element matches query string (advanced settings)
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="element.heuristics?.manualElementByParentIndex"
|
||||||
|
class="text-teal-600 flex flex-row gap-2"
|
||||||
|
>
|
||||||
|
<mdicon name="bookmark" /> This element has been manually selected as player element.
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="element.heuristics?.autoMatch"
|
||||||
|
class="text-blue-500 flex flex-row gap-2"
|
||||||
|
>
|
||||||
|
<mdicon name="refresh-auto" /> Automatic detections thinks this is the player.
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-if="element.heuristics?.invalidSize"
|
||||||
|
class="text-red-700 flex flex-row gap-2"
|
||||||
|
>
|
||||||
|
<mdicon name="alert-remove" /> This element has invalid dimensions
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -94,11 +169,16 @@ export default defineComponent({
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
elementStack: [],
|
elementStack: [],
|
||||||
|
elementStacks: {
|
||||||
|
requestId: undefined,
|
||||||
|
stacks: []
|
||||||
|
},
|
||||||
cssStack: [],
|
cssStack: [],
|
||||||
showLegend: false,
|
showLegend: false,
|
||||||
showAdvancedOptions: false,
|
showAdvancedOptions: false,
|
||||||
tutorialVisible: false,
|
tutorialVisible: false,
|
||||||
tutorialStep: 0
|
tutorialStep: 0,
|
||||||
|
lastTreeId: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -130,17 +210,47 @@ export default defineComponent({
|
|||||||
this.tutorialStep = 0;
|
this.tutorialStep = 0;
|
||||||
},
|
},
|
||||||
getPlayerTree() {
|
getPlayerTree() {
|
||||||
this.eventBus.send('get-player-tree');
|
this.lastTreeId = crypto.randomUUID()
|
||||||
|
this.eventBus.send('get-player-tree', {requestId: this.lastTreeId});
|
||||||
},
|
},
|
||||||
handleElementStack(configBroadcast) {
|
handleElementStack(configBroadcast) {
|
||||||
if (configBroadcast.type === 'player-tree') {
|
if (configBroadcast.type === 'player-tree') {
|
||||||
this.elementStack = configBroadcast.config.reverse();
|
// reset tree if lastTreeId has changed
|
||||||
this.$nextTick( () => this.$forceUpdate() );
|
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) {
|
markElement(parentIndex, enable) {
|
||||||
this.eventBus.send('set-mark-element', {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
|
* Designates new element as player element. Currently, we only need
|
||||||
* 'index', however at some point we might also set mode according
|
* 'index', however at some point we might also set mode according
|
||||||
@ -170,6 +280,8 @@ export default defineComponent({
|
|||||||
await this.siteSettings.set('activeDOMConfig', domConfigName);
|
await this.siteSettings.set('activeDOMConfig', domConfigName);
|
||||||
|
|
||||||
this.getPlayerTree();
|
this.getPlayerTree();
|
||||||
|
setTimeout(() => this.getPlayerTree(), 500);
|
||||||
|
setTimeout(() => this.getPlayerTree(), 1000);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Toggles active CSS for element of certain parent index.
|
* Toggles active CSS for element of certain parent index.
|
||||||
|
|||||||
@ -1,216 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="flex flex-col w-full">
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<h1>Video player options</h1>
|
|
||||||
</div>
|
|
||||||
<div class="w-full">
|
|
||||||
<div v-if="tutorialVisible" class="w-full">
|
|
||||||
<button
|
|
||||||
class="info-button"
|
|
||||||
@click="tutorialVisible = false"
|
|
||||||
>
|
|
||||||
<mdicon name="arrow-left"></mdicon>
|
|
||||||
Back
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div v-if="tutorialStep == 0" class="flex flex-col w-full justify-center items-center tutorial-step">
|
|
||||||
<h3>1. Start hovering over elements on this list</h3>
|
|
||||||
<div class="flex flex-row w-full flex-wrap tutorial-list">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-text">
|
|
||||||
<p>This list contains all the elements on the webpage that could be a video player.</p>
|
|
||||||
<p>Icons to the left of the list indicate which element is currently selected as the player element, which element the extension thinks to be the player element, and which element should be the player according to manual settings.</p>
|
|
||||||
</div>
|
|
||||||
<img src="/res/img/player-select-demo/uw_player___element-list-1.webp" />
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-text">
|
|
||||||
<p>Move your mouse over the first element on the list, but do not click it.</p>
|
|
||||||
<p>Hovering over elements on the list will highlight parts of the page.</p>
|
|
||||||
</div>
|
|
||||||
<img src="/res/img/player-select-demo/uw_player___element-list-hover.webp" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button @click="tutorialStep = 1">Next</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="tutorialStep == 1" class="flex flex-col w-full justify-center items-center tutorial-step">
|
|
||||||
<h3>2. Observe highlight</h3>
|
|
||||||
|
|
||||||
<div class="flex flex-row w-full flex-wrap tutorial-list">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-text">
|
|
||||||
<p>Hovering over the elements will highlight part of the page. Highlighted area should cover the player area.</p>
|
|
||||||
<p>If the highlighted area covers the player, click the item on the list to select it and reload the page.</p>
|
|
||||||
<p>If more than one element covers the player area, select the first (topmost) one on the list.</p>
|
|
||||||
</div>
|
|
||||||
<img src="/res/img/player-select-demo/uw_player_select___just-right.webp" />
|
|
||||||
|
|
||||||
<div class="icon correct">
|
|
||||||
<mdicon name="check-circle" size="96"></mdicon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-text">
|
|
||||||
<p>If highlight covers more than just the player area, that usually means the correct element is further down the list.</p>
|
|
||||||
<p>Move the mouse cursor down the list of elements, until you encounter the first element that covers the player area.</p>
|
|
||||||
</div>
|
|
||||||
<img src="/res/img/player-select-demo/uw_player_select___too_much.webp" />
|
|
||||||
<div class="icon wrong">
|
|
||||||
<mdicon name="close-circle" size="96"></mdicon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-text">
|
|
||||||
<p>If highlight doesn't cover the whole player area, that usually means the correct element is further down the list.</p>
|
|
||||||
<p>Move your cursor up the list of elements, until you encounter something that highlights the entire player area.</p>
|
|
||||||
<p>If more than one element covers the player area, select the first (topmost) one on the list.</p>
|
|
||||||
</div>
|
|
||||||
<img src="/res/img/player-select-demo/uw_player_select___too_little.webp" />
|
|
||||||
<div class="icon wrong">
|
|
||||||
<mdicon name="close-circle" size="96"></mdicon>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<button @click="tutorialStep = 0">Previous</button>
|
|
||||||
<button @click="tutorialVisible = false">Done</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<div v-else class="w-full">
|
|
||||||
<button
|
|
||||||
class="info-button"
|
|
||||||
@click="showTutorial()"
|
|
||||||
>
|
|
||||||
<mdicon name="help"></mdicon>
|
|
||||||
How do I use this?
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<script lang="ts">
|
|
||||||
|
|
||||||
export default({
|
|
||||||
components: {
|
|
||||||
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
elementStack: [],
|
|
||||||
cssStack: [],
|
|
||||||
showLegend: false,
|
|
||||||
showAdvancedOptions: false,
|
|
||||||
tutorialVisible: false,
|
|
||||||
tutorialStep: 0
|
|
||||||
};
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
},
|
|
||||||
mixins: [],
|
|
||||||
props: [
|
|
||||||
'siteSettings',
|
|
||||||
'frame',
|
|
||||||
'eventBus',
|
|
||||||
'site',
|
|
||||||
'isPopup'
|
|
||||||
],
|
|
||||||
created() {
|
|
||||||
this.eventBus.subscribe(
|
|
||||||
'uw-config-broadcast',
|
|
||||||
{
|
|
||||||
source: this,
|
|
||||||
function: (config) => this.handleElementStack(config)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.getPlayerTree();
|
|
||||||
},
|
|
||||||
destroyed() {
|
|
||||||
this.eventBus.unsubscribeAll(this);
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
showTutorial() {
|
|
||||||
this.tutorialVisible = true;
|
|
||||||
this.tutorialStep = 0;
|
|
||||||
},
|
|
||||||
getPlayerTree() {
|
|
||||||
this.eventBus.send('get-player-tree');
|
|
||||||
},
|
|
||||||
handleElementStack(configBroadcast) {
|
|
||||||
if (configBroadcast.type === 'player-tree') {
|
|
||||||
this.elementStack = configBroadcast.config.reverse();
|
|
||||||
this.$nextTick( () => this.$forceUpdate() );
|
|
||||||
}
|
|
||||||
},
|
|
||||||
markElement(parentIndex, enable) {
|
|
||||||
this.eventBus.send('set-mark-element', {parentIndex, enable});
|
|
||||||
},
|
|
||||||
async setPlayer(index) {
|
|
||||||
// yup.
|
|
||||||
// this.siteSettings.getDOMConfig('modified', 'original');
|
|
||||||
// await this.siteSettings.setUpdateFlags(['PlayerData']);
|
|
||||||
// await this.siteSettings.set('DOMConfig.modified.type', 'modified', {noSave: true});
|
|
||||||
// await this.siteSettings.set('activeDOMConfig', 'modified', {noSave: true});
|
|
||||||
|
|
||||||
// // if user agrees with ultrawidify on what element player should be,
|
|
||||||
// // we just unset our settings for this site
|
|
||||||
// if (this.elementStack[index].heuristics?.autoMatch) {
|
|
||||||
// await this.siteSettings.set('DOMConfig.modified.elements.player.manual', false);
|
|
||||||
// this.getPlayerTree();
|
|
||||||
// } else {
|
|
||||||
// // ensure settings exist:
|
|
||||||
// await this.siteSettings.set('DOMConfig.modified.elements.player.manual', true, {noSave: true});
|
|
||||||
// await this.siteSettings.set('DOMConfig.modified.elements.player.mode', 'index', {noSave: true});
|
|
||||||
// await this.siteSettings.set('DOMConfig.modified.elements.player.index', index, true);
|
|
||||||
// this.getPlayerTree();
|
|
||||||
// }
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Toggles active CSS for element of certain parent index.
|
|
||||||
* cssValue is optional and can be included in cssRule argument
|
|
||||||
*/
|
|
||||||
toggleCssForElement(index, cssRule, cssValue) {
|
|
||||||
// we will handle elements that put cssValue as a separate argument elsewhere
|
|
||||||
if (cssValue) {
|
|
||||||
return this.toggleCssForElement_3arg(index,cssRule, cssValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
// this rule applies to current element — remove it!
|
|
||||||
if (this.cssStack[index]?.includes(cssRule)) {
|
|
||||||
this.cssStack[index] = this.cssStack[index].filter(x => ! x.includes(cssRule));
|
|
||||||
} else {
|
|
||||||
if (!this.cssStack[index]) {
|
|
||||||
this.cssStack[index] = [];
|
|
||||||
}
|
|
||||||
this.cssStack[index].push(cssRule)
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: update settings!
|
|
||||||
},
|
|
||||||
toggleCssForElement_3arg(index, cssRule, cssValue) {
|
|
||||||
const matching = this.cssStack[index]?.find(x => x.includes(cssRule))
|
|
||||||
if (matching) {
|
|
||||||
this.cssStack[index] = this.cssStack[index].filter(x => ! x.includes(cssRule));
|
|
||||||
if (!matching.includes(cssValue)) {
|
|
||||||
this.cssStack[index].push(`${cssRule}: ${cssValue};`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!this.cssStack[index]) {
|
|
||||||
this.cssStack[index] = [];
|
|
||||||
}
|
|
||||||
this.cssStack[index].push(`${cssRule}: ${cssValue};`);
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: update settings!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
Loading…
Reference in New Issue
Block a user