Compare commits

...

9 Commits

Author SHA1 Message Date
f7f046ea53 Propagate messages across server boundary 2025-06-16 21:58:35 +02:00
ec0896e17a Implement zoom in extension popup, in-player UI 2025-05-19 01:18:48 +02:00
a5f35248bd Event-bus: Auto-forward across iframe and commsServer 2025-05-19 01:17:09 +02:00
a05eccce9e fix zoom config 2025-05-19 01:15:05 +02:00
e118777636 Fix debounce 2025-05-19 01:14:19 +02:00
7998e8158e Fix log coloration 2025-05-07 00:45:08 +02:00
14f837d1f0 EventBus: prevent backflow of messages that originate from CommsServer back into CommsServer 2025-05-07 00:42:25 +02:00
cff1d3cf16 Cache active tab in comms server
Previously, if console window was active, this.activeTab would return nothing, and the popup would break. If that happens now, popup will pretend it's on the last active site.
2025-05-07 00:41:45 +02:00
b2cd96982e Further logging fixes 2025-05-04 21:00:15 +02:00
25 changed files with 453 additions and 252 deletions

View File

@ -2,6 +2,14 @@
## v6.0 (current major) ## v6.0 (current major)
### v6.3.0
* Added zoom segment to in-player UI and popup.
* Fixed keyboard zoom
* Added additional zoom options. If you wonder how zoom options differ from crop, mess around and find out.
* Subdomains now inherit same settings as their parent domain by default
* Extension detects embedded content (but not always)
* Added `www.youtube-nocookie.com` to "officially supported" list
### v6.2.5 ### v6.2.5
* Popup appearance changed — UI advertisement panel was moved to the popup header * Popup appearance changed — UI advertisement panel was moved to the popup header

View File

@ -44,13 +44,6 @@ export default {
}, },
async created() { async created() {
this.logger = new Logger();
// this prolly needs to be taken out
await this.logger.init({
allowLogging: true,
});
/** /**
* Setup the "companion" onMouseMove handler to the one in the content script. * Setup the "companion" onMouseMove handler to the one in the content script.
* We can handle events with the same function we use to handle events from * We can handle events with the same function we use to handle events from

View File

@ -64,7 +64,7 @@
</GhettoContextMenuOption> </GhettoContextMenuOption>
</slot> </slot>
</GhettoContextMenu> </GhettoContextMenu>
<!-- <GhettoContextMenu alignment="right"> <GhettoContextMenu alignment="right">
<template v-slot:activator> <template v-slot:activator>
Zoom Zoom
</template> </template>
@ -86,7 +86,7 @@
/> />
</GhettoContextMenuItem> </GhettoContextMenuItem>
</slot> </slot>
</GhettoContextMenu> --> </GhettoContextMenu>
<GhettoContextMenu alignment="right"> <GhettoContextMenu alignment="right">
<template v-slot:activator> <template v-slot:activator>
<div class="context-item"> <div class="context-item">
@ -213,6 +213,8 @@ import CommsMixin from '@csui/src/utils/CommsMixin';
import SupportLevelIndicator from '@csui/src/components/SupportLevelIndicator.vue'; import SupportLevelIndicator from '@csui/src/components/SupportLevelIndicator.vue';
import TriggerZoneEditor from '@csui/src/components/TriggerZoneEditor.vue'; import TriggerZoneEditor from '@csui/src/components/TriggerZoneEditor.vue';
import ZoomControl from '@csui/src/popup/player-menu/ZoomControl.vue'; import ZoomControl from '@csui/src/popup/player-menu/ZoomControl.vue';
import { LogAggregator } from '@src/ext/lib/logging/LogAggregator';
import { ComponentLogger } from '@src/ext/lib/logging/ComponentLogger';
export default { export default {
components: { components: {
@ -329,11 +331,6 @@ export default {
this.logAggregator = new LogAggregator('player-overlay'); this.logAggregator = new LogAggregator('player-overlay');
this.logger = new ComponentLogger(this.logAggregator, 'PlayerOverlay.vue'); this.logger = new ComponentLogger(this.logAggregator, 'PlayerOverlay.vue');
// this prolly needs to be taken out
await this.logger.init({
allowLogging: true,
});
this.settings = new Settings({afterSettingsSaved: this.updateConfig, logger: this.logger}); this.settings = new Settings({afterSettingsSaved: this.updateConfig, logger: this.logger});
this.settings.listenAfterChange(() => this.updateTriggerZones()); this.settings.listenAfterChange(() => this.updateTriggerZones());
@ -575,7 +572,17 @@ export default {
}, },
handleBusTunnelIn(payload) { handleBusTunnelIn(payload) {
this.eventBus.send(payload.action, payload.config, payload.routingData); this.eventBus.send(
payload.action,
payload.config,
{
...payload.context,
borderCrossings: {
...payload.context?.borderCrossings,
iframe: true
}
}
);
}, },
updateConfig() { updateConfig() {

View File

@ -176,11 +176,8 @@ export default {
try { try {
this.logAggregator = new LogAggregator('🔵ext-popup🔵'); this.logAggregator = new LogAggregator('🔵ext-popup🔵');
this.logger = new ComponentLogger(this.logAggregator, 'Popup'); this.logger = new ComponentLogger(this.logAggregator, 'Popup');
await this.logger.init({
allowLogging: true,
});
this.settings = new Settings({afterSettingsSaved: () => this.updateConfig(), logger: this.logger}); this.settings = new Settings({afterSettingsSaved: () => this.updateConfig(), logAggregator: this.logAggregator});
await this.settings.init(); await this.settings.init();
this.settingsInitialized = true; this.settingsInitialized = true;
@ -277,8 +274,14 @@ export default {
try { try {
this.logger.log('info','popup', '[popup::getSite] Requesting current site ...') this.logger.log('info','popup', '[popup::getSite] Requesting current site ...')
// CSM.port.postMessage({command: 'get-current-site'}); // CSM.port.postMessage({command: 'get-current-site'});
this.eventBus.send(
'probe-video',
{},
{ comms: {forwardTo: 'active'} }
);
this.eventBus.send( this.eventBus.send(
'get-current-site', 'get-current-site',
{},
{ {
comms: {forwardTo: 'active'} comms: {forwardTo: 'active'}
} }

View File

@ -5,13 +5,15 @@
<h1>What's new</h1> <h1>What's new</h1>
<!-- <p>Full changelog for older versions <a href="https://github.com/tamius-han/ultrawidify/blob/master/CHANGELOG.md" target="_blank">is available here</a>.</p> --> <!-- <p>Full changelog for older versions <a href="https://github.com/tamius-han/ultrawidify/blob/master/CHANGELOG.md" target="_blank">is available here</a>.</p> -->
<h2>6.2.6</h2> <h2>6.3.0</h2>
<ul> <ul>
<li>Automatic aspect ratio detection: do not apply negative aspect ratios</li> <li>Automatic aspect ratio detection: do not apply negative aspect ratios</li>
<li>Keyboard zoom now works</li> <li>Keyboard zoom now works</li>
<li><code>www.youtube-nocookie.com</code> has been added to the "officially supported" list</li> <li><code>www.youtube-nocookie.com</code> has been added to the "officially supported" list</li>
<li>Fixed the bug where UI would sometimes refuse to stay hidden</li> <li>Fixed the bug where UI would sometimes refuse to stay hidden</li>
<li>New experimental zoom options</li> <li>New experimental zoom options</li>
<li>Subdomains now inherit same settings as their parent domain by default</li>
<li>Extension attempts to detect embedded content.</li>
</ul> </ul>
</div> </div>
<div style="width: 1rem; height: 0px;"></div> <div style="width: 1rem; height: 0px;"></div>

View File

@ -27,7 +27,7 @@
</template> </template>
<script> <script>
import { LogAggregator } from '@src/ext/lib/logging/LogAggregator'; import { LogAggregator, BLANK_LOGGER_CONFIG } from '@src/ext/lib/logging/LogAggregator';
import JsonEditor from '@csui/src/components/JsonEditor'; import JsonEditor from '@csui/src/components/JsonEditor';
export default { export default {
@ -79,21 +79,23 @@ export default {
...this.loggerPanelRotation[Math.floor(Math.random() * this.loggerPanelRotation.length)], ...this.loggerPanelRotation[Math.floor(Math.random() * this.loggerPanelRotation.length)],
pasteConfMode: false, pasteConfMode: false,
}; };
this.loadDefaultConfig(); this.getLoggerSettings();
}, },
methods: { methods: {
loadDefaultConfig() { loadDefaultConfig() {
this.lastSettings = baseLoggingOptions; this.lastSettings = JSON.parse(JSON.stringify(BLANK_LOGGER_CONFIG));
}, },
async getLoggerSettings() { async getLoggerSettings() {
this.lastSettings = await LogAggregator.getConfig() || baseLoggingOptions; this.lastSettings = await LogAggregator.getConfig() || BLANK_LOGGER_CONFIG;
}, },
saveLoggerSettings() { async saveLoggerSettings() {
LogAggregator.saveConfig({...this.lastSettings}); console.log('Saving logger settings', this.lastSettings);
await LogAggregator.saveConfig({...this.lastSettings});
console.log('[ok] logger settings saved');
}, },
async startLogging(){ async startLogging(){
this.logStringified = undefined; this.logStringified = undefined;
await LogAggregator.saveConfig({...this.lastSettings, allowLogging: true}); await LogAggregator.saveConfig({...this.lastSettings});
window.location.reload(); window.location.reload();
}, },
} }

View File

@ -108,26 +108,6 @@
</div> </div>
</div> </div>
<!-- <div v-if="siteSettings && allowSettingSiteDefault" class="edit-action-area">
<div class="field">
<div class="label">Default for this site</div>
<div class="select">
<select
:value="siteDefaultZoom"
@change="setDefaultZoom($event, 'site')"
>
<option
v-for="(command, index) of settings?.active.commands.zoom"
:key="index"
:value="JSON.stringify(command.arguments)"
>
{{command.label}}
</option>
</select>
</div>
</div>
</div> -->
</template> </template>
<template v-else> <template v-else>
<!-- <!--
@ -228,6 +208,13 @@ export default {
KeyboardShortcutParserMixin, KeyboardShortcutParserMixin,
CommsMixin CommsMixin
], ],
props: [
'settings', // required for buttons and actions, which are global
'siteSettings',
'eventBus',
'isEditing',
'compact',
],
data() { data() {
return { return {
AspectRatioType, AspectRatioType,
@ -247,13 +234,6 @@ export default {
} }
} }
}, },
props: [
'settings', // required for buttons and actions, which are global
'siteSettings',
'eventBus',
'isEditing',
'compact',
],
created() { created() {
if (this.isEditing) { if (this.isEditing) {
this.enableEditMode(); this.enableEditMode();
@ -272,7 +252,6 @@ export default {
getZoomForDisplay(axis) { getZoomForDisplay(axis) {
// zoom is internally handled logarithmically, because we want to have x0.5, x1, x2, x4 ... magnifications // zoom is internally handled logarithmically, because we want to have x0.5, x1, x2, x4 ... magnifications
// spaced out at regular intervals. When displaying, we need to convert that to non-logarithmic values. // spaced out at regular intervals. When displaying, we need to convert that to non-logarithmic values.
return `${(Math.pow(2, this.zoom[axis]) * 100).toFixed()}%` return `${(Math.pow(2, this.zoom[axis]) * 100).toFixed()}%`
}, },
toggleZoomAr() { toggleZoomAr() {

View File

@ -6,11 +6,16 @@
current settings do not allow the extension to only be disabled while in full screen current settings do not allow the extension to only be disabled while in full screen
--> -->
<template v-if="siteSettings.isEnabledForEnvironment(false, true) === ExtensionMode.Disabled"> <template v-if="siteSettings.isEnabledForEnvironment(false, true) === ExtensionMode.Disabled">
<div class="h-full flex flex-col items-center justify-center">
<div class="info"> <div class="info">
Extension is not enabled for this site. Extension is not enabled for this site.
</div> </div>
<div>
Please enable extension for this site.
</div>
</div>
</template> </template>
<template v-else>
<div class="flex flex-row"> <div class="flex flex-row">
<mdicon name="crop" :size="16" />&nbsp;&nbsp; <mdicon name="crop" :size="16" />&nbsp;&nbsp;
<span>CROP</span> <span>CROP</span>
@ -73,7 +78,7 @@
:large="true" :large="true"
> </AlignmentOptionsControlComponent> > </AlignmentOptionsControlComponent>
</div> </div>
</template>
</div> </div>

View File

@ -1,42 +1,109 @@
<template> <template>
<div> <div class="flex flex-row" style="width: 250px;">
<div class="flex-grow">
Custom zoom Custom zoom
</div> </div>
<div class="flex flex-row">
<Button
v-if="zoomAspectRatioLocked"
icon="lock"
:iconSize="16"
:fixedWidth="true"
:noPad="true"
@click="toggleZoomAr()"
>
</Button>
<Button
v-else
icon="lock-open-variant"
:iconSize="16"
:fixedWidth="true"
:noPad="true"
@click="toggleZoomAr()"
>
</Button>
<Button
icon="restore"
:iconSize="16"
:noPad="true"
@click="resetZoom()"
></Button>
</div>
</div>
<div class="top-label">Zoom:</div> <template v-if="zoomAspectRatioLocked">
<div class="input range-input"> <div class="input range-input no-bg">
<input <input
type="range" type="range"
class="slider" class="slider"
min="0" min="-1"
max="3" max="3"
step="0.01" step="0.01"
:value="zoom.x"
@input="changeZoom($event.target.value)"
/> />
<input <input
class="disabled"
style="width: 2rem;"
:value="getZoomForDisplay('x')"
/> />
</div> </div>
<template v-if="true"> </template>
<div class="top-label">Vertical zoom:</div> <template v-else>
<div class="input range-input"> <div class="top-label">Horizontal zoom:</div>
<div class="input range-input no-bg">
<input <input
type="range" type="range"
class="slider" class="slider"
min="0" min="-1"
max="3" max="3"
step="0.01" step="0.01"
:value="zoom.x"
@input="changeZoom($event.target.value, 'x')"
/> />
<input <input
class="disabled"
style="width: 2rem;"
:value="getZoomForDisplay('x')"
/>
</div>
<div class="top-label">Vertical zoom:</div>
<div class="input range-input no-bg">
<input
type="range"
class="slider"
min="-1"
max="3"
step="0.01"
:value="zoom.y"
@input="changeZoom($event.target.value, 'y')"
/>
<input
class="disabled"
style="width: 2rem;"
:value="getZoomForDisplay('y')"
/> />
</div> </div>
</template> </template>
<div><input type="checkbox"/> Control vertical and horizontal zoom independently.</div>
</template> </template>
<script> <script>
import Button from '@csui/src/components/Button.vue';
import * as _ from 'lodash';
export default { export default {
components: {
Button,
},
mixins: [
],
props: [
'settings', // required for buttons and actions, which are global
'eventBus'
],
data() { data() {
return { return {
zoomAspectRatioLocked: true, zoomAspectRatioLocked: true,
@ -51,17 +118,42 @@ export default {
stretch: null, stretch: null,
zoom: null, zoom: null,
pan: null pan: null
},
pollingInterval: undefined,
debouncedGetEffectiveZoom: undefined,
}
},
created() {
this.eventBus?.subscribeMulti(
{
'announce-zoom': {
function: (data) => {
this.zoom = {
x: Math.log2(data.x),
y: Math.log2(data.y)
};
} }
} }
}, },
mixins: [ this
);
], this.debouncedGetEffectiveZoom = _.debounce(
props: [ () => {
'settings', // required for buttons and actions, which are global this.getEffectiveZoom();
'eventBus' },
], 250
),
this.getEffectiveZoom();
this.pollingInterval = setInterval(this.debouncedGetEffectiveZoom, 2000);
},
destroyed() {
this.eventBus.unsubscribe(this);
clearInterval(this.pollingInterval);
},
methods: { methods: {
getEffectiveZoom() {
this.eventBus?.sendToTunnel('get-effective-zoom', {});
},
getZoomForDisplay(axis) { getZoomForDisplay(axis) {
// zoom is internally handled logarithmically, because we want to have x0.5, x1, x2, x4 ... magnifications // zoom is internally handled logarithmically, because we want to have x0.5, x1, x2, x4 ... magnifications
// spaced out at regular intervals. When displaying, we need to convert that to non-logarithmic values. // spaced out at regular intervals. When displaying, we need to convert that to non-logarithmic values.
@ -81,25 +173,36 @@ export default {
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'y'}); // this.eventBus.send('set-zoom', {zoom: 1, axis: 'y'});
// this.eventBus.send('set-zoom', {zoom: 1, axis: 'x'}); // this.eventBus.send('set-zoom', {zoom: 1, axis: 'x'});
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1, axis: 'y'}); this.eventBus?.sendToTunnel('set-zoom', {zoom: 1});
this.eventBus?.sendToTunnel('set-zoom', {zoom: 1, axis: 'x'});
}, },
changeZoom(newZoom, axis) { changeZoom(newZoom, axis, isLinear) {
// we store zoom logarithmically on this compnent if (isNaN(+newZoom)) {
if (!axis) { return;
this.zoom.x = newZoom; }
let logZoom, linZoom;
if (isLinear) {
newZoom /= 100;
logZoom = Math.log2(newZoom);
linZoom = newZoom;
} else { } else {
this.zoom[axis] = newZoom; logZoom = newZoom;
linZoom = Math.pow(2, newZoom);
}
// we store zoom logarithmically on this component
if (!axis) {
this.zoom.x = logZoom;
} else {
this.zoom[axis] = logZoom;
} }
// we do not use logarithmic zoom elsewhere, therefore we need to convert // we do not use logarithmic zoom elsewhere, therefore we need to convert
newZoom = Math.pow(2, newZoom);
if (this.zoomAspectRatioLocked) { if (this.zoomAspectRatioLocked) {
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: 'y'}); this.eventBus?.sendToTunnel('set-zoom', {zoom: linZoom});
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: 'x'});
} else { } else {
this.eventBus?.sendToTunnel('set-zoom', {zoom: newZoom, axis: axis ?? 'x'}); this.eventBus?.sendToTunnel('set-zoom', {zoom: {[axis ?? 'x']: linZoom}});
} }
}, },
} }

View File

@ -89,6 +89,12 @@ button, .button {
border-bottom: 1px solid rgba($primary, 0.5); border-bottom: 1px solid rgba($primary, 0.5);
} }
&.no-bg {
background-color: transparent;
border-color: transparent;
}
input { input {
width: 100%; width: 100%;
outline: none; outline: none;

View File

@ -119,6 +119,9 @@ const ExtensionConfPatch = Object.freeze([
onKeyUp: true, onKeyUp: true,
onKeyDown: false, onKeyDown: false,
}, },
arguments: {
zoom: 1
},
internalOnly: true, internalOnly: true,
actionId: 'set-zoom-reset' actionId: 'set-zoom-reset'
}, { }, {

View File

@ -411,6 +411,9 @@ const ExtensionConf: SettingsInterface = {
onKeyUp: true, onKeyUp: true,
onKeyDown: false, onKeyDown: false,
}, },
arguments: {
zoom: 1,
},
internalOnly: true, internalOnly: true,
actionId: 'set-zoom-reset' actionId: 'set-zoom-reset'
}, { }, {

View File

@ -19,6 +19,10 @@ export interface EventBusContext {
frame?: any, frame?: any,
sourceFrame?: IframeData sourceFrame?: IframeData
forwardTo?: 'all' | 'active' | 'contentScript' | 'server' | 'sameOrigin' | 'popup' | 'all-frames', forwardTo?: 'all' | 'active' | 'contentScript' | 'server' | 'sameOrigin' | 'popup' | 'all-frames',
};
borderCrossings?: {
commsServer?: boolean,
iframe?: boolean,
} }
} }
@ -29,6 +33,9 @@ export default class EventBus {
private disableTunnel: boolean = false; private disableTunnel: boolean = false;
private popupContext: any = {}; private popupContext: any = {};
private iframeForwardingList: {iframe: any, fn: (action, payload, context?) => void}[] = [];
// private uiUri = window.location.href; // private uiUri = window.location.href;
constructor(options?: {isUWServer?: boolean}) { constructor(options?: {isUWServer?: boolean}) {
@ -83,6 +90,18 @@ export default class EventBus {
} }
} }
forwardToIframe(iframe: any, fn: (action: string, payload: any, context?: EventBusContext) => void) {
this.cancelIframeForwarding(iframe);
this.iframeForwardingList.push({iframe, fn});
}
cancelIframeForwarding(iframe: any) {
const existingForwarding = this.iframeForwardingList.findIndex((x: any) => x.iframe === iframe);
if (existingForwarding !== -1) {
this.iframeForwardingList.splice(existingForwarding, 1);
}
}
send(command: string, commandData: any, context?: EventBusContext) { send(command: string, commandData: any, context?: EventBusContext) {
// execute commands we have subscriptions for // execute commands we have subscriptions for
@ -94,8 +113,27 @@ export default class EventBus {
// preventing messages from flowing back to their original senders is // preventing messages from flowing back to their original senders is
// CommsServer's job. EventBus does not have enough data for this decision. // CommsServer's job. EventBus does not have enough data for this decision.
if (this.comms) { // We do, however, have enough data to prevent backflow of messages that
// crossed CommsServer once already.
if (this.comms && context?.origin !== CommsOrigin.Server && !context?.borderCrossings?.commsServer) {
this.comms.sendMessage({command, config: commandData, context}, context); this.comms.sendMessage({command, config: commandData, context}, context);
};
// call forwarding functions if they exist
if (!context?.borderCrossings?.iframe) {
for (const forwarding of this.iframeForwardingList) {
forwarding.fn(
command,
commandData,
{
...context,
borderCrossings: {
...context?.borderCrossings,
iframe: true
}
}
);
}
} }
if (context?.stopPropagation) { if (context?.stopPropagation) {

View File

@ -186,7 +186,9 @@ export class AardDebugUi {
<div style="flex-grow: 1">${this.generateMiniGraphBar(this.aard.timer.average)}</div> <div style="flex-grow: 1">${this.generateMiniGraphBar(this.aard.timer.average)}</div>
</div> </div>
<div style="display: flex; flex-direction: row"> <div style="display: flex; flex-direction: row">
<div style="width: 120px">Last chg.:</div><div style="flex-grow: 1">${this.generateMiniGraphBar(this.aard.timer.lastChange)}</div> <div style="width: 120px">Last chg.:</div>
<div></div>
<div style="flex-grow: 1">${this.generateMiniGraphBar(this.aard.timer.lastChange)}</div>
</div> </div>
`; `;

View File

@ -128,7 +128,7 @@ class CommsClient {
} }
//#endregion //#endregion
async sendMessage(message, context?: EventBusContext){ async sendMessage(message, context?: EventBusContext, borderCrossings?){
this.logger.info('sendMessage', ' <<< Sending message to background script:', message); this.logger.info('sendMessage', ' <<< Sending message to background script:', message);
message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead message = JSON.parse(JSON.stringify(message)); // vue quirk. We should really use vue store instead
@ -144,9 +144,12 @@ class CommsClient {
return port.postMessage(message); return port.postMessage(message);
} }
} }
// send to server // send to server
if (!context?.borderCrossings?.commsServer) {
return chrome.runtime.sendMessage(null, message, null); return chrome.runtime.sendMessage(null, message, null);
} }
}
/** /**
* Processes message we received from CommsServer, and forwards it to eventBus. * Processes message we received from CommsServer, and forwards it to eventBus.
@ -170,7 +173,10 @@ class CommsClient {
message.config, message.config,
{ {
comms, comms,
origin: CommsOrigin.Server origin: CommsOrigin.Server,
borderCrossings: {
commsServer: true
}
} }
); );
} }

View File

@ -52,9 +52,26 @@ class CommsServer {
} = {}; } = {};
popupPort: any; popupPort: any;
private _lastActiveTab: chrome.tabs.Tab | undefined;
//#region getters //#region getters
get activeTab() { get activeTab(): Promise<chrome.tabs.Tab | undefined> {
return chrome.tabs.query({currentWindow: true, active: true}); return new Promise((resolve, reject) => {
chrome.tabs
.query({currentWindow: true, active: true})
.then((tabs) => {
if (tabs.length === 0) {
this.logger.warn('<getter-activeTab>', 'no active tab found, returning last valid active tab instead ...', this._lastActiveTab);
resolve(this._lastActiveTab);
} else {
this.logger.log('<getter-activeTab>', 'getting active tab', tabs[0]);
this._lastActiveTab = tabs[0];
resolve(tabs[0]);
}
})
.catch((err) => {
this.logger.error('<getter-activeTab>', 'error while getting active tab — returned last valid active tab instead ...', err, this._lastActiveTab);
});
});
} }
//#endregion //#endregion
@ -110,6 +127,10 @@ class CommsServer {
// stop messages from returning where they came from, and prevent // stop messages from returning where they came from, and prevent
// cross-pollination between content scripts running in different // cross-pollination between content scripts running in different
// tabs. // tabs.
if (!context) {
this.logger.debug('sendMessage', 'context was not passed in as parameter - does message have context?', message.context);
context = message.context;
}
if (context?.origin !== CommsOrigin.ContentScript) { if (context?.origin !== CommsOrigin.ContentScript) {
if (context?.comms.forwardTo === 'all') { if (context?.comms.forwardTo === 'all') {
@ -236,13 +257,13 @@ class CommsServer {
private async sendToActive(message) { private async sendToActive(message) {
this.logger.info('sendToActive', ` <——— trying to send a message ${message.command ?? ''} to active tab. Message:`, message); this.logger.info('sendToActive', ` <——— trying to send a message ${message.command ?? ''} to active tab. Message:`, message);
const tabs = await this.activeTab; const tab = await this.activeTab;
this.logger.info('sendToActive', "currently active tab(s)?", tabs); this.logger.info('sendToActive', "currently active tab?", tab);
for (const frame in this.ports[tabs[0].id]) { for (const frame in this.ports[tab.id]) {
this.logger.info('sendToActive', "sending message to frame:", frame, this.ports[tabs[0].id][frame], '; message:', message); this.logger.info('sendToActive', "sending message to frame:", frame, this.ports[tab.id][frame], '; message:', message);
this.sendToFrameContentScripts(message, tabs[0].id, frame); this.sendToFrameContentScripts(message, tab.id, frame);
} }
} }
@ -269,7 +290,7 @@ class CommsServer {
} }
private processReceivedMessage_nonpersistent(message, sender){ private processReceivedMessage_nonpersistent(message, sender){
this.logger.info('processMessage_nonpersistent', ` ==> Received message from background script!`, "background-color: #11D; color: #aad", message, sender); this.logger.info('processMessage_nonpersistent', ` ==> Received message from background script!`, message, sender);
this.eventBus.send( this.eventBus.send(
message.command, message.command,

View File

@ -22,12 +22,13 @@ export class ComponentLogger {
constructor(logAggregator: LogAggregator, component: string, componentOptions?: ComponentLoggerOptions) { constructor(logAggregator: LogAggregator, component: string, componentOptions?: ComponentLoggerOptions) {
this.logAggregator = logAggregator; this.logAggregator = logAggregator;
this.component = component; this.component = component;
this.componentOptions = componentOptions;
} }
private handleLog(logLevel: LogLevel, sourceFunction: string | LogSourceOptions, ...message: any) { private handleLog(logLevel: LogLevel, sourceFunction: string | LogSourceOptions, ...message: any) {
let functionSource = typeof sourceFunction === 'string' ? sourceFunction : sourceFunction?.src; let functionSource = typeof sourceFunction === 'string' ? sourceFunction : sourceFunction?.src;
let consoleMessageString = `[${this.component}${functionSource ? `::${functionSource}` : ''}] `; let consoleMessageString = `[${this.component}${functionSource ? `::${functionSource}` : ''}]`;
const consoleMessageData = [] const consoleMessageData = []
for (const m of message) { for (const m of message) {
@ -35,10 +36,13 @@ export class ComponentLogger {
consoleMessageString = `${consoleMessageString} ${m}`; consoleMessageString = `${consoleMessageString} ${m}`;
} else if (typeof m === 'number') { } else if (typeof m === 'number') {
consoleMessageString = `${consoleMessageString} %f`; consoleMessageString = `${consoleMessageString} %f`;
} else if (m instanceof HTMLElement) { consoleMessageData.unshift(m);
} else if (typeof HTMLElement !== 'undefined' && m instanceof HTMLElement) { // HTMLElement does not exist in background script, but this class may
consoleMessageString = `${consoleMessageString} %o`; consoleMessageString = `${consoleMessageString} %o`;
consoleMessageData.unshift(m);
} else { } else {
consoleMessageString = `${consoleMessageString} %O`; consoleMessageString = `${consoleMessageString} %O`;
consoleMessageData.unshift(m);
} }
} }

View File

@ -4,11 +4,6 @@ export const BLANK_LOGGER_CONFIG: LogConfig = {
logToConsole: false, logToConsole: false,
logToFile: false, logToFile: false,
component: { component: {
aard: { enabled: false },
resizer: { enabled: false },
comms: { enabled: false },
settings: { enabled: false },
eventBus: { enabled: false },
}, },
origins: { origins: {
videoRescan: { disabled: true}, videoRescan: { disabled: true},
@ -36,6 +31,7 @@ export interface LogConfig {
} }
} }
const STORAGE_LOG_SETTINGS_KEY = 'uw-log-config';
export class LogAggregator { export class LogAggregator {
private segment: string; private segment: string;
@ -46,29 +42,34 @@ export class LogAggregator {
history: any[]; history: any[];
static async getConfig() { static async getConfig() {
let ret = await chrome.storage.local.get('uw-log-config'); let ret = await chrome.storage.local.get(STORAGE_LOG_SETTINGS_KEY);
if (process.env.CHANNEL === 'dev') { if (process.env.CHANNEL === 'dev') {
try { try {
console.info("[Logger::getSaved] Got settings:", JSON.parse(ret.uwLogger)); console.info("[LogAggregator::getSaved] Got settings:", JSON.parse(ret[STORAGE_LOG_SETTINGS_KEY]));
} catch (e) { } catch (e) {
console.info("[Logger::getSaved] No settings.") console.info("[LogAggregator::getSaved] No settings.", ret)
} }
} }
try { try {
return JSON.parse(ret['uw-log-config']); return JSON.parse(ret[STORAGE_LOG_SETTINGS_KEY]);
} catch (e) { } catch (e) {
return JSON.parse(JSON.stringify(BLANK_LOGGER_CONFIG)); return JSON.parse(JSON.stringify(BLANK_LOGGER_CONFIG));
} }
} }
static saveConfig(conf: LogConfig) { static async saveConfig(conf: LogConfig) {
try {
const confCp = JSON.parse(JSON.stringify(conf));
if (process.env.CHANNEL === 'dev') { if (process.env.CHANNEL === 'dev') {
console.info('Saving logger conf:', conf) console.info('Saving logger conf:', confCp)
} }
chrome.storage.local.set( {'uw-log-config': JSON.stringify(conf)}); await chrome.storage.local.set({[STORAGE_LOG_SETTINGS_KEY]: JSON.stringify(confCp)} );
} catch (e) {
console.warn('[LogAggregator::saveConfig] Error while trying to save logger config:', e);
}
} }
static syncConfig(callback: (x) => void) { static syncConfig(callback: (x) => void) {
@ -89,26 +90,35 @@ export class LogAggregator {
chrome.storage.onChanged.addListener((changes, area) => { chrome.storage.onChanged.addListener((changes, area) => {
this.storageChangeListener(changes, area) this.storageChangeListener(changes, area)
}); });
this.init();
} }
private canLog(component: string, sourceOptions?: LogSourceOptions): boolean { private canLog(component: string, sourceOptions?: LogSourceOptions): boolean {
// component is not in config, so we add a blank entry
if (this.config && !this.config.component[component]) {
this.config.component[component] = {enabled: false};
LogAggregator.saveConfig(this.config);
return false;
}
if (this.config?.component?.[component]?.enabled) { if (this.config?.component?.[component]?.enabled) {
if (sourceOptions?.origin && this.config?.origins?.[sourceOptions.origin]?.disabled) { if (sourceOptions?.origin && this.config?.origins?.[sourceOptions.origin]?.disabled) {
return false; return false;
} }
return true; return true;
} }
return false; return false;
} }
private storageChangeListener(changes, area) { private storageChangeListener(changes, area) {
if (!changes.uwLogger) { if (!changes[STORAGE_LOG_SETTINGS_KEY]) {
console.info('We dont have any logging settings, not processing frther'); console.info('We dont have any logging settings, not processing frther', changes);
return; return;
} }
try { try {
this.config = JSON.parse(changes['uw-log-config'].newValue); this.config = JSON.parse(changes[STORAGE_LOG_SETTINGS_KEY].newValue);
} catch (e) { } catch (e) {
console.warn('[uwLogger] Error while trying to parse new conf for logger:', e, '\nWe received the following changes:', changes, 'for area:', area); console.warn('[uwLogger] Error while trying to parse new conf for logger:', e, '\nWe received the following changes:', changes, 'for area:', area);
} }
@ -188,7 +198,11 @@ export class LogAggregator {
} }
async init(config: LogConfig) { async init(config?: LogConfig) {
if (!config) {
config = await LogAggregator.getConfig();
}
this.config = config; this.config = config;
this.startTime = performance.now(); this.startTime = performance.now();
@ -211,7 +225,7 @@ export class LogAggregator {
} }
if (this.config.logToConsole) { if (this.config.logToConsole){
console[logLevel](`[${this.segment}]>>${message}`, ...data, {stack: this.parseStack()}); console[logLevel](`[${this.segment}]>>${message}`, ...data, {stack: this.parseStack()});
} }
} }

View File

@ -205,10 +205,18 @@ class UI {
document.addEventListener('mousemove', fn, true); document.addEventListener('mousemove', fn, true);
} }
this.eventBus.forwardToIframe(
this.uiIframe,
(action, payload) => {
this.sendToIframe(action, payload, {})
}
);
this.rootDiv.appendChild(iframe); this.rootDiv.appendChild(iframe);
} }
unloadIframe() { unloadIframe() {
this.eventBus.cancelIframeForwarding(this.uiIframe);
window.removeEventListener('message', this.messageHandlerFn); window.removeEventListener('message', this.messageHandlerFn);
this.uiIframe?.remove(); this.uiIframe?.remove();
delete this.uiIframe; delete this.uiIframe;
@ -416,7 +424,17 @@ class UI {
break; break;
case 'uw-bus-tunnel': case 'uw-bus-tunnel':
const busCommand = event.data.payload; const busCommand = event.data.payload;
this.eventBus.send(busCommand.action, busCommand.config, busCommand.routingData); this.eventBus.send(
busCommand.action,
busCommand.config,
{
...busCommand?.context,
borderCrossings: {
...busCommand?.context?.borderCrossings,
iframe: true,
}
}
);
break; break;
case 'uwui-get-role': case 'uwui-get-role':
this.sendToIframeLowLevel('uwui-set-role', {role: this.isGlobal ? 'global' : 'player'}); this.sendToIframeLowLevel('uwui-set-role', {role: this.isGlobal ? 'global' : 'player'});

View File

@ -190,7 +190,7 @@ class PlayerData {
constructor(videoData) { constructor(videoData) {
try { try {
// set all our helper objects // set all our helper objects
this.logger = new ComponentLogger(videoData.logAggregator, 'PlayerData', {styles: Debug.getLoggingStyles()}); this.logger = new ComponentLogger(videoData.logAggregator, 'PlayerData', {styles: {}});
this.videoData = videoData; this.videoData = videoData;
this.videoElement = videoData.video; this.videoElement = videoData.video;
this.pageInfo = videoData.pageInfo; this.pageInfo = videoData.pageInfo;
@ -482,10 +482,17 @@ class PlayerData {
} }
} }
onPlayerDimensionsChanged(mutationList?, observer?) { onPlayerDimensionsChanged: ResizeObserverCallback = _.debounce(
(mutationList?, observer?) => {
this.trackDimensionChanges(); this.trackDimensionChanges();
this.trackEnvironmentChanges(); this.trackEnvironmentChanges();
},
250, // do it once per this many ms
{
leading: true, // do it when we call this fallback first
trailing: true // do it after the timeout if we call this callback few more times
} }
);
//#region player element change detection //#region player element change detection
@ -499,29 +506,19 @@ class PlayerData {
} }
try { try {
this.observer = new ResizeObserver( if (this.observer) {
_.debounce( // don't do this too much: this.observer.disconnect();
(m,o) => {
this.onPlayerDimensionsChanged(m,o)
},
250, // do it once per this many ms
{
leading: true, // do it when we call this fallback first
trailing: true // do it after the timeout if we call this callback few more times
} }
)
);
const observerConf = { this.observer = new ResizeObserver(
attributes: true, this.onPlayerDimensionsChanged
// attributeFilter: ['style', 'class'], );
attributeOldValue: true,
};
this.observer.observe(this.element); this.observer.observe(this.element);
} catch (e) { } catch (e) {
console.error("failed to set observer",e ) console.error("failed to set observer",e );
} }
// legacy mode still exists, but acts as a fallback for observers and is triggered less // legacy mode still exists, but acts as a fallback for observers and is triggered less
// frequently in order to avoid too many pointless checks // frequently in order to avoid too many pointless checks
this.legacyChangeDetection(); this.legacyChangeDetection();

View File

@ -63,6 +63,8 @@ class Resizer {
currentCssValidFor: any; currentCssValidFor: any;
currentVideoSettings: any; currentVideoSettings: any;
private effectiveZoom: {x: number, y: number} = {x: 1, y: 1};
_lastAr: Ar = {type: AspectRatioType.Initial}; _lastAr: Ar = {type: AspectRatioType.Initial};
set lastAr(x: Ar) { set lastAr(x: Ar) {
// emit updates for UI when setting lastAr, but only if AR really changed // emit updates for UI when setting lastAr, but only if AR really changed
@ -88,6 +90,11 @@ class Resizer {
//#region event bus configuration //#region event bus configuration
private eventBusCommands = { private eventBusCommands = {
'get-effective-zoom': [{
function: () => {
this.eventBus.send('announce-zoom', this.manualZoom ? {x: this.zoom.scale, y: this.zoom.scaleY} : this.zoom.effectiveZoom);
}
}],
'set-ar': [{ 'set-ar': [{
function: (config: any) => { function: (config: any) => {
this.manualZoom = false; // this only gets called from UI or keyboard shortcuts, making this action safe. this.manualZoom = false; // this only gets called from UI or keyboard shortcuts, making this action safe.
@ -136,7 +143,9 @@ class Resizer {
} }
}], }],
'set-zoom': [{ 'set-zoom': [{
function: (config: any) => this.setZoom(config.zoom) function: (config: any) => {
this.setZoom(config?.zoom ?? {zoom: 1});
}
}], }],
'change-zoom': [{ 'change-zoom': [{
function: (config: any) => this.zoomStep(config.zoom) function: (config: any) => this.zoomStep(config.zoom)
@ -449,12 +458,12 @@ class Resizer {
} }
applyScaling(stretchFactors: VideoDimensions, options?: {noAnnounce?: boolean, ar?: Ar}) { applyScaling(stretchFactors: VideoDimensions, options?: {noAnnounce?: boolean, ar?: Ar}) {
// this.stretcher.chromeBugMitigation(stretchFactors); this.zoom.effectiveZoom = {x: stretchFactors.xFactor, y: stretchFactors.yFactor};
// let the UI know // announcing zoom somehow keeps incorrectly resetting zoom sliders in UI — UI is now polling for effective zoom while visible
if(!options?.noAnnounce) { // if(!options?.noAnnounce) {
this.videoData.eventBus.send('announce-zoom', {x: stretchFactors.xFactor, y: stretchFactors.yFactor}); // this.videoData.eventBus.send('announce-zoom', this.manualZoom ? {x: this.zoom.scale, y: this.zoom.scaleY} : this.zoom.effectiveZoom);
} // }
let translate = this.computeOffsets(stretchFactors, options?.ar); let translate = this.computeOffsets(stretchFactors, options?.ar);
this.applyCss(stretchFactors, translate); this.applyCss(stretchFactors, translate);

View File

@ -30,6 +30,7 @@ class Zoom {
maxScale = 8; maxScale = 8;
//#endregion //#endregion
effectiveZoom: {x: number, y: number}; // we're setting this in Resizer based on Resizer data!
constructor(videoData) { constructor(videoData) {
this.conf = videoData; this.conf = videoData;
@ -50,7 +51,12 @@ class Zoom {
* @param axis leave undefined to apply zoom to both axes * @param axis leave undefined to apply zoom to both axes
*/ */
zoomStep(amount: number, axis?: 'x' | 'y') { zoomStep(amount: number, axis?: 'x' | 'y') {
let newLog = axis === 'y' ? this.logScaleY : this.logScale; const effectiveLog = {
x: Math.log2(this.effectiveZoom.x),
y: Math.log2(this.effectiveZoom.y)
};
let newLog = axis === 'y' ? effectiveLog.y : effectiveLog.x;
newLog += amount; newLog += amount;
newLog = Math.min(Math.max(newLog, LOG_MIN_SCALE), LOG_MAX_SCALE); newLog = Math.min(Math.max(newLog, LOG_MIN_SCALE), LOG_MAX_SCALE);
@ -65,7 +71,6 @@ class Zoom {
this.scale = Math.pow(2, this.logScale); this.scale = Math.pow(2, this.logScale);
this.scaleY = Math.pow(2, this.logScaleY); this.scaleY = Math.pow(2, this.logScaleY);
this.logger.info('zoomStep', "changing zoom by", amount, ". New zoom level:", this.scale);
this.processZoom(); this.processZoom();
} }
@ -94,18 +99,6 @@ class Zoom {
this.conf.resizer.toFixedAr(); this.conf.resizer.toFixedAr();
this.conf.resizer.applyScaling({xFactor: this.scale, yFactor: this.scaleY}, {noAnnounce: true}); this.conf.resizer.applyScaling({xFactor: this.scale, yFactor: this.scaleY}, {noAnnounce: true});
} }
applyZoom(stretchFactors){
if (!stretchFactors) {
return;
}
this.logger.info('setZoom', "Applying zoom. Stretch factors pre:", stretchFactors, " —> scale:", this.scale);
stretchFactors.xFactor *= this.scale;
stretchFactors.yFactor *= this.scale;
this.logger.info('setZoom', "Applying zoom. Stretch factors post:", stretchFactors);
}
} }
export default Zoom; export default Zoom;

View File

@ -85,11 +85,8 @@ export default {
async created() { async created() {
this.logAggregator = new LogAggregator(''); this.logAggregator = new LogAggregator('');
this.logger = new ComponentLogger(this.logAggregator, 'App.vue'); this.logger = new ComponentLogger(this.logAggregator, 'App.vue');
await this.logger.init({
allowLogging: true,
});
this.settings = new Settings({updateCallback: () => this.updateConfig(), logger: this.logger}); this.settings = new Settings({updateCallback: () => this.updateConfig(), logAggregator: this.logAggregator });
await this.settings.init(); await this.settings.init();
this.settingsInitialized = true; this.settingsInitialized = true;
}, },

View File

@ -87,11 +87,7 @@ export default {
this.logAggregator = new LogAggregator(''); this.logAggregator = new LogAggregator('');
this.logger = new ComponentLogger(this.logAggregator, 'App.vue'); this.logger = new ComponentLogger(this.logAggregator, 'App.vue');
await this.logger.init({ this.settings = new Settings({updateCallback: () => this.updateConfig(), logAggregator: this.logAggregator});
allowLogging: true,
});
this.settings = new Settings({updateCallback: () => this.updateConfig(), logger: this.logger});
await this.settings.init(); await this.settings.init();
this.settingsInitialized = true; this.settingsInitialized = true;
}, },

View File

@ -111,10 +111,6 @@
<script> <script>
import Donate from '../common/misc/Donate.vue'; import Donate from '../common/misc/Donate.vue';
import SuperAdvancedSettings from './SuperAdvancedSettings.vue'; import SuperAdvancedSettings from './SuperAdvancedSettings.vue';
import Debug from '../ext/conf/Debug';
import BrowserDetect from '../ext/conf/BrowserDetect';
import ExtensionConf from '../ext/conf/ExtensionConf';
import ObjectCopy from '../ext/lib/ObjectCopy';
import Settings from '../ext/lib/Settings'; import Settings from '../ext/lib/Settings';
import GeneralSettings from './GeneralSettings'; import GeneralSettings from './GeneralSettings';
import ControlsSettings from './controls-settings/ControlsSettings'; import ControlsSettings from './controls-settings/ControlsSettings';
@ -122,7 +118,6 @@ import AddEditActionPopup from './controls-settings/AddEditActionPopup';
import ConfirmPopup from './common/ConfirmationPopup'; import ConfirmPopup from './common/ConfirmationPopup';
import About from './about' import About from './about'
import AutodetectionSettings from './AutodetectionSettings'; import AutodetectionSettings from './AutodetectionSettings';
// import SuperAdvancedSettings from './'
import { LogAggregator } from '@src/ext/lib/logging/LogAggregator'; import { LogAggregator } from '@src/ext/lib/logging/LogAggregator';
import { ComponentLogger } from '@src/ext/lib/logging/ComponentLogger'; import { ComponentLogger } from '@src/ext/lib/logging/ComponentLogger';
@ -149,11 +144,8 @@ export default {
async created () { async created () {
this.logAggregator = new LogAggregator(''); this.logAggregator = new LogAggregator('');
this.logger = new ComponentLogger(this.logAggregator, 'App.vue'); this.logger = new ComponentLogger(this.logAggregator, 'App.vue');
await this.logger.init({
allowLogging: true,
});
this.settings = new Settings({updateCallback: this.updateSettings, logger: this.logger}); this.settings = new Settings({updateCallback: this.updateSettings, logAggregator: this.logAggregator});
await this.settings.init(); await this.settings.init();
this.settingsInitialized = true; this.settingsInitialized = true;