Compare commits

...

10 Commits

19 changed files with 482 additions and 84 deletions

2
package-lock.json generated
View File

@ -1,6 +1,6 @@
{
"name": "ultrawidify",
"version": "6.2.5",
"version": "6.2.6",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@ -1,6 +1,6 @@
{
"name": "ultrawidify",
"version": "6.2.5",
"version": "6.2.6",
"description": "Aspect ratio fixer for youtube and other sites, with automatic aspect ratio detection. Supports ultrawide and other ratios.",
"author": "Tamius Han <tamius.han@gmail.com>",
"scripts": {

View File

@ -12,6 +12,13 @@ export enum ExtensionEnvironment {
Fullscreen = 'fullscreen',
}
export interface DevUiConfig {
aardDebugOverlay: {
showOnStartup: boolean,
showDetectionDetails: boolean,
}
}
export interface KeyboardShortcutInterface {
key?: string,
code?: string,
@ -161,11 +168,16 @@ export interface AardSettings {
}
}
interface DevSettings {
loadFromSnapshot: boolean,
}
interface SettingsInterface {
_updateFlags?: {
requireReload?: SettingsReloadFlags,
forSite?: string
}
dev: DevSettings,
arDetect: AardSettings,
@ -185,6 +197,7 @@ interface SettingsInterface {
},
},
devMode?: boolean,
dev: DevUiConfig,
}
restrictions?: RestrictionsSettings;

View File

@ -144,11 +144,13 @@
Site compatibility:
<SupportLevelIndicator
:siteSupportLevel="siteSupportLevel"
supportLevelStyle="font-size: 0.69rem !important;"
tooltipStyle="font-size: 0.8rem;"
>
</SupportLevelIndicator>
<div v-if="statusFlags.hasDrm" class="aard-blocked">
Autodetection potentially<br/>
unavailable due to <a style="color: #fff" href="https://en.wikipedia.org/wiki/Digital_rights_management" target="_blank">DRM</a>.
Autodetection blocked<br/>
by <a style="color: #fff" href="https://en.wikipedia.org/wiki/Digital_rights_management" target="_blank">DRM</a>.
</div>
<div v-else-if="statusFlags.aardErrors?.cors" class="aard-blocked">
Autodetection blocked<br/>
@ -156,7 +158,7 @@
</div>
<div v-else-if="statusFlags.aardErrors?.webglError" class="aard-blocked">
Autodetection unavailable<br/>
due to webgl error.
(webgl error)
</div>
</GhettoContextMenuItem>
</div>
@ -483,7 +485,8 @@ export default {
},
acknowledgeNewFeature(featureKey) {
delete this.settings.active.newFeatureTracker[featureKey];
this.settings.active.newFeatureTracker[featureKey].show = 0;
this.settings.active.newFeatureTracker[featureKey].acknowledged = true;
this.settings.saveWithoutReload();
},
newFeatureViewUpdate(featureKey) {
@ -721,6 +724,7 @@ export default {
}
.aard-blocked {
font-size: 0.8rem;
color: #fa6;
}

View File

@ -10,7 +10,7 @@
<div class="w-[1/2]" style="width: 50%">
<h2>Report a problem</h2>
<p>
You may report <strike>undocumented features</strike> bugs using one of the following options (in order of preference):
Please report <strike>undocumented features</strike> bugs using one of the following options (in order of preference):
</p>
<ul>
<li> <a target="_blank" href="https://github.com/tamius-han/ultrawidify/issues"><b>Github (preferred)</b></a><br/></li>

View File

@ -269,9 +269,28 @@
<p>
<b>Debug options</b>
</p>
<div class="flex flex-row">
<div>
<div>
<button @click="eventBus.sendToTunnel('aard-enable-debug', true)">Show debug overlay</button>
</div>
<div>
<div class="label">Show debug overlay on startup</div>
<input
type="checkbox"
v-model="settings.active.ui.dev.aardDebugOverlay.showOnStartup"
@change="settings.saveWithoutReload"
>
</div>
</div>
<div>
<JsonEditor
v-model="settingsJson"
>
</JsonEditor>
<button @click="saveDebugUiSettings">Save debug UI settings</button>
</div>
</div>
</div>
</div>
</div>
@ -289,14 +308,15 @@ import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import StretchType from '../../../common/enums/StretchType.enum';
import CropModePersistence from '../../../common/enums/CropModePersistence.enum';
import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue';
import JsonEditor from '@csui/src/components/JsonEditor';
export default {
data() {
return {
exec: null,
performanceData: {},
graphRefreshInterval: undefined,
}
components: {
ShortcutButton,
EditShortcutButton,
Button,
AlignmentOptionsControlComponent,
JsonEditor
},
mixins: [
],
@ -306,6 +326,16 @@ export default {
'eventBus',
'site'
],
data() {
return {
exec: null,
performanceData: {},
graphRefreshInterval: undefined,
settingsJson: {},
}
},
computed: {
},
created() {
this.eventBus.subscribe(
'uw-config-broadcast',
@ -315,24 +345,15 @@ export default {
}
);
},
destroyed() {
this.eventBus.unsubscribeAll(this);
},
mounted() {
this.eventBus.sendToTunnel('get-aard-timing');
this.graphRefreshInterval = setInterval(() => this.eventBus.sendToTunnel('get-aard-timing'), 500);
this.resetSettingsEditor();
},
destroyed() {
this.eventBus.unsubscribeAll(this);
clearInterval(this.graphRefreshInterval);
},
components: {
ShortcutButton,
EditShortcutButton,
Button,
AlignmentOptionsControlComponent
},
computed: {
},
methods: {
async openOptionsPage() {
BrowserDetect.runtime.openOptionsPage();
@ -349,7 +370,15 @@ export default {
this.$nextTick( () => this.$forceUpdate() );
}
},
resetSettingsEditor() {
this.settingsJson = JSON.parse(JSON.stringify(this.settings?.active.ui.dev.aardDebugOverlay ?? {}));
},
saveDebugUiSettings() {
this.settings.active.ui.dev.aardDebugOverlay = JSON.parse(JSON.stringify(this.settingsJson));
this.settings.saveWithoutReload();
}
},
}
</script>

View File

@ -113,7 +113,7 @@
class="danger"
:class="{'disabled': !allowSettingsEditing}"
:disabled="!allowSettingsEditing"
@click="saveSettingsChanges"
@click="() => saveSettingsChanges()"
>
Save
</button>
@ -129,6 +129,31 @@
>
</JsonEditor>
</div>
<h2>Settings migration report</h2>
<pre>
{{settings.migrationReport}}
</pre>
<h2>Settings snapshots</h2>
<div class="flex flex-col">
<div v-for="(snapshot, index) of settingsSnapshots" :key="snapshot.createdAt">
<small>{{snapshot.createdAt.toISOString()}}</small>
<div class="flex flex-row">
<div class="grow">
{{snapshot.name}}
</div>
<div v-if="settings.isAutomatic">(auto)</div>
<div v-if="settings.isAutomatic && settings.isProtected">(protected)</div>
<div v-if="settings.default">(default)</div>
</div>
<div>
<button @click="() => markDefaultSnapshot(index)"><template v-if="settings.isDefault">Revoke default</template><template v-else>Make default</template></button>
<button v-if="settings.isAutomatic" @click="() => toggleSnapshotProtection(index)">Toggle protection</button>
<button @click="() => deleteSnapshot(index)">Delete snapshot</button>
</div>
</div>
</div>
</div>
</div>
</template>
@ -151,6 +176,7 @@ export default {
allowSettingsEditing: false,
editorSaveFinished: false,
settingsJson: {},
settingsSnapshots: []
}
},
mixins: [
@ -236,10 +262,18 @@ export default {
this.resetSettingsEditor();
},
saveSettingsChanges() {
async saveSettingsChanges() {
if (this.allowSettingsEditing) {
const currentVersion = this.settings.active?.version;
this.settings.active = this.settingsJson;
this.settings.saveWithoutReload();
if (currentVersion !== this.settingsJson.version) {
await this.settings.save({forcePreserveVersion: true});
return;
} else {
await this.settings.saveWithoutReload();
}
this.resetSettingsEditor();
this.editorSaveFinished = true;
@ -251,10 +285,32 @@ export default {
resetSettingsEditor() {
this.settingsJson = JSON.parse(JSON.stringify(this.settings?.active ?? {}));
},
//#endregion
//#region settings snapshot management
async loadSettingsSnapshots() {
this.settingsSnapshots = await this.settings.snapshotManager.listSnapshots();
},
async markDefaultSnapshot(index) {
await this.settings.snapshotManager.setDefaultSnapshot(index, !this.settingsSnapshots[index].isDefault);
await this.loadSettingsSnapshots();
this.settings.active.dev.loadFromSnapshot = this.settingsSnapshots[index].isDefault;
this.saveSettingsChanges();
},
async toggleSnapshotProtection(index) {
await this.settings.snapshotManager.setSnapshotAsProtected(index, !this.settingsSnapshots[index].isProtected);
await this.loadSettingsSnapshots();
},
async deleteSnapshot(index) {
await this.settings.snapshotManager.deleteSnapshot(index);
await this.loadSettingsSnapshots();
},
}
//#endregion
}
}
</script>

View File

@ -1,23 +1,33 @@
<template>
<div class="flex flex-col w-full h-full gap-2">
<div class="flex flex-row gap-8 bg-black flex-wrap">
<div class="min-w-[400px] max-w-[520px] grow-1 shrink-1">
<div class="flex flex-row gap-8 bg-black flex-wrap w-full">
<div class="min-w-[400px] max-w-[520px] grow shrink">
<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> -->
<h2>6.2.5</h2>
<h2>6.2.6</h2>
<ul>
<li>'Show UI' button was moved to popup header. Extension popup now defaults to 'crop options' tab</li>
<li>Fixed the bug where current extension settings wouldn't be displayed correctly in the popup</li>
<li>Fixed the issue where extension options using the "Extension default" mode would always be disabled</li>
<li>Added the ability to import and export settings (ft. developer mode editor)</li>
<li>Added some toys not intended for general audience (Aard now has debug tools).</li>
<li>Fixed an issue where aspect ratio wouldn't get calculated correctly on youtube videos with native aspect ratios other than 16:9</li>
<li>Fixed an issue that would crash the extension if video element didn't have a player element associated with it</li>
<li>Fixed an issue where extension sometimes wouldn't work if video element was grafted/re-parented to a different element</li>
<li>Automatic aspect ratio detection: do not apply negative aspect ratios</li>
<li>Keyboard zoom now works</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>
</ul>
</div>
<div class="min-w-[400px] max-w-[520px] grow-1 shrink-1">
<div style="width: 1rem; height: 0px;"></div>
<div class="min-w-[400px] max-w-[520px] grow shrink">
<h2>Report a problem</h2>
<p>
Please report <strike>undocumented features</strike> bugs using one of the following options (in order of preference):
</p>
<ul>
<li> <a target="_blank" href="https://github.com/tamius-han/ultrawidify/issues"><b>Github (preferred)</b></a><br/></li>
<li>Email: <a target="_blank" :href="mailtoLink">tamius.han@gmail.com</a></li>
</ul>
<p>
When reporting bugs, please include extension version, whether you installed the extension from, and description of your problem.
</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h2>Thank you monies</h2>
<p>
If you think I deserve money for the work I did up to this point, you can bankroll my caffeine addiction.
@ -30,11 +40,23 @@
</div>
</template>
<script>
import BrowserDetect from '@src/ext/conf/BrowserDetect';
export default({
props: [
'settings'
],
data() {
return {
BrowserDetect: BrowserDetect,
// reminder webextension-polyfill doesn't seem to work in vue!
addonVersion: BrowserDetect.firefox ? chrome.runtime.getManifest().version : chrome.runtime.getManifest().version,
addonSource: BrowserDetect.processEnvVersion,
mailtoLink: '',
redditLink: '',
showEasterEgg: false,
}
},
mounted() {
this.settings.active.whatsNewChecked = true;
this.settings.saveWithoutReload();
@ -49,6 +71,10 @@ export default({
flex-direction: row;
}
.grow {
flex-grow: 1;
}
p, li {
font-size: 1rem;
}

View File

@ -1,44 +1,44 @@
<template>
<div v-if="siteSupportLevel === 'official'" class="site-support official">
<div v-if="siteSupportLevel === 'official'" class="site-support official" :style="supportLevelStyle">
<mdicon name="check-decagram" />
<div v-if="!small">Verified</div>
<div class="tooltip">
<div class="tooltip" :style="tooltipStyle">
<template v-if="small">Verified&nbsp;&nbsp;</template>
The extension is being tested and should work on this site.
</div>
</div>
<div v-if="siteSupportLevel === 'community'" class="site-support community">
<div v-if="siteSupportLevel === 'community'" class="site-support community" :style="supportLevelStyle">
<mdicon name="account-group" />
<div v-if="!small">Community</div>
<div class="tooltip">
<div class="tooltip" :style="tooltipStyle">
<template v-if="small">Community&nbsp;&nbsp;</template>
People say extension works on this site (or have provided help getting the extension to work if it didn't).<br/><br/>
Tamius (the dev) does not test the extension on this site, probably because it requires a subscription or
is geoblocked.
</div>
</div>
<div v-if="siteSupportLevel === 'no-support' || siteSupportLevel === 'unknown'" class="site-support no-support">
<div v-if="siteSupportLevel === 'no-support' || siteSupportLevel === 'unknown'" class="site-support no-support" :style="supportLevelStyle">
<mdicon name="help-circle-outline" />
<div v-if="!small">Unknown</div>
<div class="tooltip">
<div class="tooltip" :style="tooltipStyle">
<template v-if="small">Unknown&nbsp;&nbsp;</template>
Not officially supported. Extension will try to fix things, but no promises.<br/><br/>
Tamius (the dev) does not test the extension on this site for various reasons
(unaware, not using the site, language barrier, geoblocking, paid services Tam doesn't use).
</div>
</div>
<div v-if="siteSupportLevel === 'user-added' || siteSupportLevel === 'user-defined'" class="site-support user-added">
<div v-if="siteSupportLevel === 'user-added' || siteSupportLevel === 'user-defined'" class="site-support user-added" :style="supportLevelStyle">
<mdicon name="account" />
<div v-if="!small">Modified by you</div>
<div class="tooltip">
<div class="tooltip" :style="tooltipStyle">
<template v-if="small">Modified by you&nbsp;&nbsp;</template>
You have manually changed settings for this site. The extension is doing what you told it to do.
</div>
</div>
<div v-if="siteSupportLevel === 'officially-disabled'" class="site-support officially-disabled">
<div v-if="siteSupportLevel === 'officially-disabled'" class="site-support officially-disabled" :style="supportLevelStyle">
<mdicon class="site-support no-support" name="checkbox-marked-circle" />
<div v-if="!small">Not supported</div>
<div class="tooltip">
<div class="tooltip" :style="tooltipStyle">
<template v-if="small">Not supported&nbsp;&nbsp;</template>
Extension is known to not work with this site.
</div>
@ -50,6 +50,8 @@ export default {
props: {
siteSupportLevel: String,
small: Boolean,
supportLevelStyle: String,
tooltipStyle: String,
}
}
</script>

View File

@ -21,7 +21,6 @@ const ExtensionConfPatch = [
normal: ExtensionMode.Default,
}
}
const uiEnabled =
userOptions.sites['@global'].enableUI = {
fullscreen: userOptions.ui.inPlayer.enabled ? ExtensionMode.Enabled : ExtensionMode.Disabled,
theater: ExtensionMode.Enabled,
@ -83,6 +82,16 @@ const ExtensionConfPatch = [
}];
delete (userOptions as any).actions;
userOptions.dev = {
loadFromSnapshot: false
};
userOptions.ui.dev = {
aardDebugOverlay: {
showOnStartup: false,
showDetectionDetails: true
}
}
}
}
];

View File

@ -14,6 +14,10 @@ if(Debug.debug)
console.log("Loading: ExtensionConf.js");
const ExtensionConf: SettingsInterface = {
dev: {
loadFromSnapshot: false,
},
arDetect: {
aardType: 'auto',
@ -127,6 +131,12 @@ const ExtensionConf: SettingsInterface = {
offsetX: -50,
offsetY: 0
}
},
dev: {
aardDebugOverlay: {
showOnStartup: false,
showDetectionDetails: true
}
}
},
@ -722,6 +732,46 @@ const ExtensionConf: SettingsInterface = {
}
}
},
"www.youtube-nocookie.com": {
enable: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled,
},
enableAard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled,
},
enableKeyboard: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Enabled
},
enableUI: {
fullscreen: ExtensionMode.Enabled,
theater: ExtensionMode.Enabled,
normal: ExtensionMode.Disabled
},
override: false, // ignore value localStorage in favour of this
type: 'official', // is officially supported? (Alternatives are 'community' and 'user-defined')
defaultType: 'official', // if user mucks around with settings, type changes to 'user-defined'.
// We still want to know what the original type was, hence defaultType
activeDOMConfig: 'official',
DOMConfig: {
'official': {
type: 'official',
elements: {
player: {
manual: true,
querySelectors: "#movie_player, #player, #c4-player",
}
}
}
}
},
"www.netflix.com" : {
enable: {
fullscreen: ExtensionMode.Enabled,

View File

@ -12,11 +12,15 @@ import Logger from './Logger';
import SettingsInterface from '../../common/interfaces/SettingsInterface';
import AspectRatioType from '../../common/enums/AspectRatioType.enum';
import { SiteSettings } from './settings/SiteSettings';
import { SettingsSnapshotManager } from './settings/SettingsSnapshotManager';
if(process.env.CHANNEL !== 'stable'){
console.info("Loading Settings");
}
interface SetSettingsOptions {
forcePreserveVersion?: boolean,
}
class Settings {
//#region flags
@ -41,6 +45,17 @@ class Settings {
afterSettingsChangedCallbacks: (() => void)[] = [];
private sortedPatches: any[];
public snapshotManager: SettingsSnapshotManager;
private _migrationReport: string = '';
private set migrationReport(report: string) {
this._migrationReport = report;
}
public get migrationReport(): string {
return this._migrationReport;
}
//#endregion
constructor(options) {
@ -50,11 +65,14 @@ class Settings {
this.afterSettingsSaved = options?.afterSettingsSaved;
this.active = options?.activeSettings ?? undefined;
this.default = ExtensionConf;
this.snapshotManager = new SettingsSnapshotManager();
this.default['version'] = this.getExtensionVersion();
chrome.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
this.sortedPatches = this.sortConfPatches(ExtensionConfPatch);
}
private storageChangeListener(changes, area) {
@ -186,6 +204,18 @@ class Settings {
return;
}
// save current settings object
const currentSettings = this.active;
this.snapshotManager.createSnapshot(
JSON.parse(JSON.stringify(currentSettings)),
{
label: 'Pre-migration snapshot',
isAutomatic: true
}
);
// apply all remaining patches
this.logger?.log('info', 'settings', `[Settings::applySettingsPatches] There are ${this.sortedPatches.length - index} settings patches to apply`);
while (index < this.sortedPatches.length) {
@ -209,7 +239,16 @@ class Settings {
}
async init() {
const settings = await this.get();
let settings = await this.get();
if (settings?.dev?.loadFromSnapshot) {
this.logger?.log('info', 'settings', '[Settings::init] Dev mode is enabled, Loading settings from snapshot:', settings.dev.loadFromSnapshot);
const snapshot = await this.snapshotManager.getSnapshot();
if (snapshot) {
settings = snapshot.settings;
}
}
this.version = this.getExtensionVersion();
// |—> on first setup, settings is undefined & settings.version is haram
@ -282,7 +321,7 @@ class Settings {
return this.active;
}
async get() {
async get(): Promise<SettingsInterface | undefined> {
let ret;
ret = await chrome.storage.local.get('uwSettings');
@ -290,13 +329,13 @@ class Settings {
this.logger?.log('info', 'settings', 'Got settings:', ret && ret.uwSettings && JSON.parse(ret.uwSettings));
try {
return JSON.parse(ret.uwSettings);
return JSON.parse(ret.uwSettings) as SettingsInterface;
} catch(e) {
return undefined;
}
}
async set(extensionConf, options?) {
async set(extensionConf, options?: SetSettingsOptions) {
if (!options || !options.forcePreserveVersion) {
extensionConf.version = this.version;
}
@ -344,7 +383,7 @@ class Settings {
}
}
async save(options?) {
async save(options?: SetSettingsOptions) {
if (Debug.debug && Debug.storage) {
console.log("[Settings::save] Saving active settings:", this.active);
}
@ -354,10 +393,10 @@ class Settings {
}
async saveWithoutReload() {
async saveWithoutReload(options?: SetSettingsOptions) {
this.active.preventReload = true;
this.active.lastModified = new Date();
await this.set(this.active);
await this.set(this.active, options);
}
async rollback() {

View File

@ -219,7 +219,6 @@ import { AardTimers, initAardTimers } from './interfaces/aard-timers.interface';
*
*/
export class Aard {
//#region configuration parameters
private logger: Logger;
private videoData: VideoData;
@ -269,6 +268,7 @@ export class Aard {
private debugConfig: any = {};
private timer: AardTimer;
private lastAnimationFrameTime: number = Infinity;
//#endregion
//#region getters
@ -304,8 +304,7 @@ export class Aard {
// we can tick manually, for debugging
this.logger.log('info', 'init', `[ArDetector::ctor] creating new ArDetector. arid: ${this.arid}`);
this.timer = new AardTimer()
this.timer = new AardTimer();
this.init();
}
@ -337,6 +336,14 @@ export class Aard {
// console.error('FALIED TO CREATE DEBUGG CANVAS', e);
// }
try {
if (this.settings.active.ui.dev?.aardDebugOverlay?.showOnStartup) {
this.showDebugCanvas();
}
} catch (e) {
console.error(`[uw::aard] failed to create debug UI:`, e);
}
this.startCheck();
}
@ -407,6 +414,7 @@ export class Aard {
* Checks whether autodetection can run
*/
startCheck() {
console.log('aard - starting checks')
if (!this.videoData.player) {
console.warn('Player not detected!');
console.log('--- video data: ---\n', this.videoData);
@ -453,7 +461,6 @@ export class Aard {
this.verticalTestResults = initAardTestResults(this.settings.active.arDetect);
}
this.main();
}
@ -648,7 +655,7 @@ export class Aard {
do {
if (this.testResults.notLetterbox) {
// console.log('————not letterbox')
console.warn('DETECTED NOT LETTERBOX! (resetting)')
// console.warn('DETECTED NOT LETTERBOX! (resetting)')
this.timer.arChanged();
this.updateAspectRatio(this.defaultAr);
break;
@ -659,7 +666,7 @@ export class Aard {
// console.info('aspect ratio not certain:', this.testResults.aspectRatioUncertainReason);
// console.warn('check finished:', JSON.parse(JSON.stringify(this.testResults)), JSON.parse(JSON.stringify(this.canvasSamples)), '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n');
console.warn('ASPECT RATIO UNCERTAIN, GUARD LINE INVALIDATED (resetting)')
// console.warn('ASPECT RATIO UNCERTAIN, GUARD LINE INVALIDATED (resetting)')
this.timer.arChanged();
this.updateAspectRatio(this.defaultAr);
@ -669,18 +676,24 @@ export class Aard {
// TODO: emit debug values if debugging is enabled
this.testResults.isFinished = true;
console.warn(
`[${(+new Date() % 10000) / 100} | ${this.arid}]`,'check finished — aspect ratio updated:', this.testResults.aspectRatioUpdated,
'\ndetected ar:', this.testResults.activeAspectRatio, '->', this.getAr(),
'\nis video playing?', this.getVideoPlaybackState() === VideoPlaybackState.Playing,
'\n\n', JSON.parse(JSON.stringify(this.testResults)), JSON.parse(JSON.stringify(this.canvasSamples)), '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n');
// console.warn(
// `[${(+new Date() % 10000) / 100} | ${this.arid}]`,'check finished — aspect ratio updated:', this.testResults.aspectRatioUpdated,
// '\ndetected ar:', this.testResults.activeAspectRatio, '->', this.getAr(),
// '\nis video playing?', this.getVideoPlaybackState() === VideoPlaybackState.Playing,
// '\n\n', JSON.parse(JSON.stringify(this.testResults)), JSON.parse(JSON.stringify(this.canvasSamples)), '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n');
// if edge width changed, emit update event.
// except aspectRatioUpdated doesn't get set reliably, so we just call update every time, and update
// if detected aspect ratio is different from the current aspect ratio
// if (this.testResults.aspectRatioUpdated) {
// this.timer.arChanged();
this.updateAspectRatio();
const finalAr = this.getAr();
if (finalAr > 0) {
this.updateAspectRatio(finalAr);
} else {
this.testResults.aspectRatioInvalid = true;
this.testResults.aspectRatioInvalidReason = finalAr.toFixed(3);
}
// }
// if we got "no letterbox" OR aspectRatioUpdated

View File

@ -7,9 +7,15 @@ export class AardDebugUi {
uiAnchorElement: HTMLDivElement;
pauseOnArCheck: boolean = false;
uiVisibility: any = {};
constructor(aard: any) {
this.aard = aard;
this.uiVisibility = {
detectionDetails: aard.settings.active.ui.dev.aardDebugOverlay.showDetectionDetails
};
(window as any).ultrawidify_uw_aard_debug_tools = {
enableStopOnChange: () => this.changePauseOnCheck(true),
disableStopOnChange: () => this.changePauseOnCheck(false),
@ -26,7 +32,13 @@ export class AardDebugUi {
position: fixed; top: 0; left: 0; width: 100vw; height: 100dvh; display: flex; flex-direction: column; pointer-events: none; z-index: 9999; font-size: 16px; font-family: 'Overpass Mono', monospace;
">
<div style="width: 100%; display: flex; flex-direction: row; justify-content: space-between; backdrop-filter: blur(0.5rem) brightness(0.5);">
<div style="padding: 1rem; color: #fff"><h1>Aard debug overlay</h1></div>
<div style="padding: 1rem; color: #fff">
<h1>Aard debug overlay</h1>
</div>
<div style="pointer-events: all; display: flex; flex-direction: column; margin-right: 1rem;">
<button id="uw-aard-debug_show-detection-details">Show det. details</button>
<button id="uw-aard-debug_hide-detection-details">Hide det. details</button>
</div>
<style>
#uw-aard-debug_performance-container #uw-aard-debug_performance-popup {
@ -62,7 +74,9 @@ export class AardDebugUi {
<button id="uw-aard-debug-ui_close-overlay">Close overlay</button>
</div>
</div>
<div style="display: flex; flex-direction: row; width: 100%">
<div id="uw-aard-debug-ui_body" style="display: flex; flex-direction: row; width: 100%">
<div style="">
<div id="uw-aard-debug_aard-sample-canvas" style="min-width: 640px"></div>
<div style="background: black; color: #fff"; font-size: 24px;">AARD IN</div>
@ -129,6 +143,10 @@ export class AardDebugUi {
document.getElementById('uw-aard-debug-ui_enable-step').onclick = () => this.aard.step();
document.getElementById('uw-aard-debug-ui_enable-step-nocache').onclick = () => this.aard.step({noCache: true});
document.getElementById('uw-aard-debug-ui_close-overlay').onclick = () => (this.aard as any).hideDebugCanvas();
document.getElementById('uw-aard-debug_show-detection-details').onclick = () => {this.uiVisibility.detectionDetails = true; this.setOverlayVisibility();};
document.getElementById('uw-aard-debug_hide-detection-details').onclick = () => {this.uiVisibility.detectionDetails = false; this.setOverlayVisibility();};
this.setOverlayVisibility();
}
changePauseOnCheck(pauseOnChange: boolean) {
@ -352,7 +370,8 @@ export class AardDebugUi {
out = `${out}
-- UNCERTAIN FLAGS
AR: ${testResults.aspectRatioUncertain} (reason: ${testResults.aspectRatioUncertainReason ?? 'n/a'}); top row: ${testResults.topRowUncertain}; bottom row: ${testResults.bottomRowUncertain}
AR: ${testResults.aspectRatioUncertain} (reason: ${testResults.aspectRatioUncertainReason ?? 'n/a'}); top row: ${testResults.topRowUncertain}; bottom row: ${testResults.bottomRowUncertain}${
testResults.aspectRatioInvalid ? `\nINVALID_AR (reason: ${testResults.aspectRatioInvalidReason ?? 'n/a'})` : ''}
-- GUARD & IMAGE LINE
bottom guard: ${testResults.guardLine.bottom} image: ${testResults.guardLine.invalidated ? 'n/a' : testResults.imageLine.bottom}
@ -388,5 +407,10 @@ export class AardDebugUi {
resultsDiv.textContent = out;
}
private setOverlayVisibility() {
document.getElementById('uw-aard-debug-ui_body').style.display = this.uiVisibility.detectionDetails ? 'flex' : 'none';
document.getElementById('uw-aard-debug_hide-detection-details').style.display = this.uiVisibility.detectionDetails ? '' : 'none';
document.getElementById('uw-aard-debug_show-detection-details').style.display = this.uiVisibility.detectionDetails ? 'none' : '';
}
}

View File

@ -39,7 +39,9 @@ export interface AardTestResults {
letterboxWidth: number,
letterboxOffset: number,
logoDetected: [boolean, boolean, boolean, boolean]
aspectRatioInvalid: boolean
aspectRatioUncertainReason?: string
aspectRatioInvalidReason?: string
}
export function initAardTestResults(settings: AardSettings): AardTestResults {
@ -81,7 +83,8 @@ export function initAardTestResults(settings: AardSettings): AardTestResults {
activeAspectRatio: 0,
letterboxWidth: 0,
letterboxOffset: 0,
logoDetected: [false, false, false, false]
logoDetected: [false, false, false, false],
aspectRatioInvalid: false,
}
}
@ -120,4 +123,5 @@ export function resetAardTestResults(results: AardTestResults): void {
results.aspectRatioUncertainReason = null;
results.topRowUncertain = false;
results.bottomRowUncertain = false;
results.aspectRatioInvalid = false;
}

View File

@ -0,0 +1,106 @@
import { settings } from 'cluster'
import SettingsInterface from '@src/common/interfaces/SettingsInterface';
export interface SettingsSnapshot {
isAutomatic?: boolean;
isProtected?: boolean;
isDefault?: boolean;
forVersion: string;
label: string;
settings: SettingsInterface;
createdAt: Date;
}
export interface SettingsSnapshotOptions {
isAutomatic?: boolean,
isProtected?: boolean,
isDefault?: boolean,
label?: string,
forVersion?: string
}
export class SettingsSnapshotManager {
private MAX_AUTOMATIC_SNAPSHOTS = 5;
async getSnapshot(index?: number) {
const snapshots = await this.listSnapshots();
if (!index) {
return snapshots.find(x => x.isDefault);
} else {
if (index < 0 || index >= snapshots.length) {
throw new Error('Invalid index');
}
return snapshots[index];
}
}
async createSnapshot(settings: SettingsInterface, options?: SettingsSnapshotOptions) {
const snapshot = {
...options,
label: options.label ?? 'Automatic snapshot',
forVersion: options.forVersion || settings.version,
settings: JSON.parse(JSON.stringify(settings)),
createdAt: new Date(),
};
const snapshots = await this.listSnapshots();
const automaticSnapshots = snapshots.filter((s) => s.isAutomatic && !s.isProtected);
if (options.isAutomatic && automaticSnapshots.length >= this.MAX_AUTOMATIC_SNAPSHOTS) {
const firstAutomaticIndex = snapshots.findIndex((s) => s.isAutomatic && !s.isProtected);
snapshots.splice(firstAutomaticIndex, 1);
}
snapshots.push(snapshot);
this.set(snapshots);
}
async setDefaultSnapshot(index: number, isDefault: boolean) {
const snapshots = await this.listSnapshots();
if (index < 0 || index >= snapshots.length) {
throw new Error('Invalid index');
}
if (isDefault) {
for (const snapshot of snapshots) {
snapshot.isDefault = false;
}
}
snapshots[index].isDefault = isDefault;
this.set(snapshots);
}
async markSnapshotAsProtected(index: number, isProtected: boolean) {
const snapshots = await this.listSnapshots();
if (index < 0 || index >= snapshots.length) {
throw new Error('Invalid index');
}
snapshots[index].isProtected = isProtected;
this.set(snapshots);
}
async deleteSnapshot(index: number) {
const snapshots = await this.listSnapshots();
if (index < 0 || index >= snapshots.length) {
throw new Error('Invalid index');
}
snapshots.splice(index, 1);
this.set(snapshots);
}
async listSnapshots(): Promise<SettingsSnapshot[]> {
const ret = await chrome.storage.local.get('uwSettings-snapshots');
try {
JSON.parse(ret['uwSettings-snapshots']) as SettingsSnapshot[];
} catch (e) {
return [] as SettingsSnapshot[];
}
}
private async set(snapshots: SettingsSnapshot[]) {
await chrome.storage.local.set({
'uwSettings-snapshots': JSON.stringify(snapshots),
});
}
}

View File

@ -1,3 +1,4 @@
import ExtensionMode from '../../../common/enums/ExtensionMode.enum';
import { EventBusConnector } from '../EventBus';
if (process.env.CHANNEL !== 'stable'){
@ -29,6 +30,7 @@ class UI {
this.saveState = undefined;
this.playerData = uiConfig.playerData;
this.uiSettings = uiConfig.uiSettings;
this.siteSettings = uiConfig.siteSettings;
this.iframeErrorCount = 0;
this.iframeConfirmed = false;
@ -43,10 +45,28 @@ class UI {
this.extensionBase = chrome.runtime.getURL('').replace(/\/$/, "");
// UI will be initialized when setUiVisibility is called
console.log('ui config:', uiConfig);
this.init();
}
canRun() {
if (this.isGlobal) {
return true;
}
return this.siteSettings?.data.enableUI.fullscreen === ExtensionMode.Enabled
|| this.siteSettings?.data.enableUI.theater === ExtensionMode.Enabled
|| this.siteSettings?.data.enableUI.normal === ExtensionMode.Enabled;
}
async init() {
if (!this.canRun()) {
console.log('ui config: canRun returned false', this.siteSettings?.data.enableUI.fullscreen === ExtensionMode.Enabled, this.siteSettings?.data.enableUI.theater === ExtensionMode.Enabled, this.siteSettings?.data.enableUI.normal === ExtensionMode.Enabled)
return;
}
console.log('ui config: canRun returned truie', this.siteSettings?.data.enableUI.fullscreen === ExtensionMode.Enabled, this.siteSettings?.data.enableUI.theater === ExtensionMode.Enabled, this.siteSettings?.data.enableUI.normal === ExtensionMode.Enabled)
this.initUIContainer();
this.loadIframe();
this.initMessaging();
@ -315,6 +335,10 @@ class UI {
canShowUI: false,
}
if (this.playerData?.environment && this.siteSettings.data.enableUI[this.playerData?.environment] !== ExtensionMode.Enabled) {
return result;
}
if (this.playerData?.dimensions) {
result.playerDimensions = this.playerData.dimensions;
}

View File

@ -291,7 +291,8 @@ class PlayerData {
parentElement: this.element,
eventBus: this.eventBus,
playerData: this,
uiSettings: this.videoData.settings.active.ui
uiSettings: this.videoData.settings.active.ui,
siteSettings: this.siteSettings,
}
);
@ -615,7 +616,8 @@ class PlayerData {
parentElement: this.element,
eventBus: this.eventBus,
playerData: this,
uiSettings: this.videoData.settings.active.ui
uiSettings: this.videoData.settings.active.ui,
siteSettings: this.siteSettings,
}
);
@ -627,8 +629,6 @@ class PlayerData {
* Finds and returns HTML element of the player
*/
private getPlayer(options?: {verbose?: boolean}): HTMLElement {
const host = window.location.hostname;
let element = this.videoElement.parentNode;
const videoWidth = this.videoElement.offsetWidth;
const videoHeight = this.videoElement.offsetHeight;
let playerCandidate;
@ -649,7 +649,6 @@ class PlayerData {
}
// if mode is given, we follow the preference
if (this.siteSettings.data.currentDOMConfig?.elements?.player?.manual && this.siteSettings.data.currentDOMConfig?.elements?.player?.mode) {
if (this.siteSettings.data.currentDOMConfig?.elements?.player?.mode === 'qs') {
playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight);

View File

@ -2,7 +2,7 @@
"manifest_version": 3,
"name": "Ultrawidify",
"description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.",
"version": "6.2.5",
"version": "6.2.6",
"icons": {
"32":"res/icons/uw-32.png",
"64":"res/icons/uw-64.png"