migration to site settings mostly done. TODO: check if things still work

This commit is contained in:
Tamius Han 2023-01-07 18:57:47 +01:00
parent a58edad8ea
commit 9503003a4a
27 changed files with 258 additions and 862 deletions

View File

@ -0,0 +1,6 @@
import AspectRatioType from '../enums/AspectRatioType.enum';
export interface Ar {
type: AspectRatioType,
ratio?: number
}

View File

@ -366,7 +366,7 @@ export interface SiteSettingsInterface {
defaults?: { // must be defined in @global and @empty
crop?: {type: AspectRatioType, [x: string]: any},
stretch?: StretchType,
alignment?: any,
alignment?: {x: VideoAlignmentType, y: VideoAlignmentType},
}
cropModePersistence?: CropModePersistence;

View File

@ -39,11 +39,9 @@ import DebugPanel from './src/PlayerUiPanels/DebugPanel.vue'
import VideoSettings from './src/PlayerUiPanels/VideoSettings.vue'
import AutodetectionSettingsPanel from './src/PlayerUiPanels/AutodetectionSettingsPanel.vue'
import PlayerDetectionPanel from './src/PlayerUiPanels/PlayerDetectionPanel.vue'
import { mapState } from 'vuex';
// import Icon from '../common/components/Icon';
import ResizerDebugPanel from './src/PlayerUiPanels/ResizerDebugPanelComponent';
import BrowserDetect from '../ext/conf/BrowserDetect';
import ExecAction from './src/ui-libs/ExecAction';
import Logger from '../ext/lib/Logger';
import Settings from '../ext/lib/Settings';
import EventBus from '../ext/lib/EventBus';
@ -85,7 +83,6 @@ export default {
settings: {},
BrowserDetect: BrowserDetect,
settingsInitialized: false,
execAction: new ExecAction(),
eventBus: new EventBus(),
logger: null,

View File

@ -154,6 +154,7 @@ export default {
selectedTab: 'videoSettings',
BrowserDetect: BrowserDetect,
preventClose: false,
siteSettings: null,
}
},
props: [
@ -168,9 +169,12 @@ export default {
// IS SUPER HARAM
// THINGS WILL NOT WORK IF YOU USE ARROWS
siteSupportLevel() {
return (this.site && this.settings?.active) ? this.settings.active.sites[this.site]?.type || 'no-support' : 'waiting';
return (this.site && this.siteSettings) ? this.siteSettings.data.type || 'no-support' : 'waiting';
}
},
created() {
this.siteSettings = this.settings.getSiteSettings(this.site);
},
methods: {
/**
* Gets URL of the browser settings page (i think?)

View File

@ -262,7 +262,6 @@ import Button from '../../../common/components/Button.vue'
import KeyboardShortcutParser from '../../../common/js/KeyboardShortcutParser';
import ShortcutButton from '../../../common/components/ShortcutButton';
import EditShortcutButton from '../../../common/components/EditShortcutButton';
import ExecAction from '../ui-libs/ExecAction';
import BrowserDetect from '../../../ext/conf/BrowserDetect';
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import StretchType from '../../../common/enums/StretchType.enum';
@ -286,7 +285,6 @@ export default {
'site'
],
created() {
this.exec = new ExecAction(this.settings, window.location.hostname);
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)});
},
mounted() {

View File

@ -191,26 +191,7 @@ export default {
commandArguments = $event.target.value;
}
if (!this.settings.active.sites[this.site]) {
this.settings.active.sites[this.site] = this.settings.getDefaultSiteConfiguration();
}
const optionPath = option.split('.');
if (optionPath.length < 2) {
this.settings.active.sites[this.site][option] = commandArguments;
} else {
let currentOptionObject = this.settings.active.sites[this.site][optionPath[0]];
let i;
for (i = 1; i < optionPath.length - 1; i++) {
if (currentOptionObject[optionPath[i]] === undefined) {
currentOptionObject[optionPath[i]] = {};
}
currentOptionObject = currentOptionObject[optionPath[i]];
}
currentOptionObject[optionPath[optionPath.length - 1]] = commandArguments;
}
this.settings.saveWithoutReload();
this.siteSettings.set(option, commandArguments);
}
}

View File

@ -132,23 +132,6 @@
</select>
</div>
</div>
<div class="field">
<div class="label">Extension default</div>
<div class="select">
<select
:value="extensionDefaultCrop"
@click="setDefaultCrop($event, 'global')"
>
<option
v-for="(command, index) of settings?.active.commands.crop"
:key="index"
:value="JSON.stringify(command.arguments)"
>
{{command.label}}
</option>
</select>
</div>
</div>
</div>
</template>
@ -220,23 +203,7 @@ export default {
setDefaultCrop($event, scope) {
const commandArguments = JSON.parse($event.target.value);
if (scope === 'site') {
if (!this.settings.active.sites[this.site]) {
this.settings.active.sites[this.site] = this.settings.getDefaultSiteConfiguration();
}
this.settings.active.sites[this.site].defaultCrop = commandArguments;
} else {
// eventually, this 'if' will be safe to remove (and we'll be able to only
// get away with the 'else' section) Maybe in 6 months or so.
if (!this.settings.active.crop) {
this.settings.active['crop'] = {
default: commandArguments
}
} else {
this.settings.active.crop.default = commandArguments;
}
}
this.siteSettings.set('defaults.crop', commandArguments);
this.settings.saveWithoutReload();
},

View File

@ -235,16 +235,7 @@ export default {
*/
setDefaultStretchingMode($event, globalOrSite) {
const commandArguments = JSON.parse($event.target.value);
if (globalOrSite === 'site') {
if (!this.settings.active.sites[this.site]) {
this.settings.active.sites[this.site] = this.settings.getDefaultSiteConfiguration();
}
this.settings.active.sites[this.site].defaultStretch = commandArguments;
} else {
this.settings.active.stretch.default = commandArguments;
}
this.settings.saveWithoutReload();
this.siteSettings.set('defaults.stretch', commandArguments);
},
/**

View File

@ -117,7 +117,6 @@ import Button from '../../../common/components/Button.vue'
import ShortcutButton from '../../../common/components/ShortcutButton';
import EditShortcutButton from '../../../common/components/EditShortcutButton';
import ComputeActionsMixin from '../../../common/mixins/ComputeActionsMixin';
import ExecAction from '../ui-libs/ExecAction';
import BrowserDetect from '../../../ext/conf/BrowserDetect';
import AlignmentOptionsControlComponent from './AlignmentOptionsControlComponent.vue';
import CommsMixin from '../utils/CommsMixin';
@ -149,7 +148,6 @@ export default {
'site'
],
created() {
this.exec = new ExecAction(this.settings, window.location.hostname);
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)});
},
mounted() {

View File

@ -1,94 +0,0 @@
import Comms from '../../../ext/lib/comms/Comms';
class ExecAction {
constructor(settings, site) {
this.settings = settings;
this.site = site;
}
setSettings(settings) {
this.settings = settings;
}
setSite(site) {
this.site = site;
}
async exec(action, scope, frame, useBus) {
for (var cmd of action.cmd) {
if (!scope || scope === 'page') {
const message = {
forwardToContentScript: true,
targetFrame: frame,
frame: frame,
cmd: cmd.action,
arg: cmd.arg,
customArg: cmd.customArg
}
if (useBus) {
// todo: postMessage out of the iframe!
// window.ultrawidify.bus.sendMessage(message.cmd, message);
window.parent.sendMessage(message.cmd, message);
} else {
Comms.sendMessage(message);
}
} else {
// set-ar-persistence sends stuff to content scripts as well (!)
// it's important to do that BEFORE the save step
if (cmd.action === 'set-ar-persistence') {
// even when setting global defaults, we only send message to the current tab in
// order to avoid problems related to
const message = {
forwardToActive: true,
targetFrame: frame,
frame: frame,
cmd: cmd.action,
arg: cmd.arg,
}
// this hopefully delays settings.save() until current crops are saved on the site
// and thus avoid any fucky-wuckies
if (useBus) {
// todo: postMessage out of the iframe!
// window.ultrawidify.bus.sendMessage(message.cmd, message);
window.parent.sendMessage(message.cmd, message);
} else {
await Comms.sendMessage(message);
}
}
let site = this.site;
if (scope === 'global') {
site = '@global';
} else if (!this.site) {
site = window.location.hostname;
}
if (scope === 'site' && !this.settings.active.sites[site]) {
this.settings.active.sites[site] = this.settings.getDefaultOption();
}
if (cmd.action === "set-stretch") {
this.settings.active.sites[site].stretch = cmd.arg;
} else if (cmd.action === "set-alignment") {
this.settings.active.sites[site].videoAlignment = cmd.arg;
} else if (cmd.action === "set-extension-mode") {
this.settings.active.sites[site].mode = cmd.arg;
} else if (cmd.action === "set-autoar-mode") {
this.settings.active.sites[site].autoar = cmd.arg;
} else if (cmd.action === 'set-keyboard') {
this.settings.active.sites[site].keyboardShortcutsEnabled = cmd.arg;
} else if (cmd.action === 'set-ar-persistence') {
this.settings.active.sites[site]['cropModePersistence'] = cmd.arg;
this.settings.saveWithoutReload();
}
if (cmd.action !== 'set-ar-persistence') {
this.settings.save();
}
}
}
}
}
export default ExecAction;

View File

@ -8,11 +8,13 @@ import Logger, { baseLoggingOptions } from './lib/Logger';
import UWGlobals from './lib/UWGlobals';
import EventBus from './lib/EventBus';
import KeyboardHandler from './lib/kbm/KeyboardHandler';
import { SiteSettings } from './lib/settings/SiteSettings';
export default class UWContent {
pageInfo: PageInfo;
comms: CommsClient;
settings: Settings;
siteSettings: SiteSettings;
keyboardHandler: KeyboardHandler;
logger: Logger;
eventBus: EventBus;
@ -90,6 +92,7 @@ export default class UWContent {
logger: this.logger
});
await this.settings.init();
this.siteSettings = this.settings.getSiteSettings();
}
this.eventBus = new EventBus();
@ -109,42 +112,25 @@ export default class UWContent {
}
}
// we always initialize extension, even if it's disabled.
initPhase2() {
// If extension is soft-disabled, don't do shit
var extensionMode = this.settings.getExtensionMode();
this.logger.log('info', 'debug', "[uw::init] Extension mode:" + (extensionMode < 0 ? "disabled" : extensionMode == '1' ? 'basic' : 'full'));
const isSiteDisabled = extensionMode === ExtensionMode.Disabled
if (isSiteDisabled) {
this.destroy();
if (this.settings.getExtensionMode('@global') === ExtensionMode.Disabled) {
this.logger.log('info', 'debug', "[uw::init] EXTENSION DISABLED, THEREFORE WONT BE STARTED")
return;
}
}
try {
if (this.pageInfo) {
this.logger.log('info', 'debug', '[uw.js::setup] An instance of pageInfo already exists and will be destroyed.');
this.pageInfo.destroy();
}
this.pageInfo = new PageInfo(this.eventBus, this.settings, this.logger, extensionMode, isSiteDisabled);
this.pageInfo = new PageInfo(this.eventBus, this.siteSettings, this.settings, this.logger);
this.logger.log('info', 'debug', "[uw.js::setup] pageInfo initialized.");
this.logger.log('info', 'debug', "[uw.js::setup] will try to initate KeyboardHandler.");
// start action handler only if extension is enabled for this site
if (!isSiteDisabled) {
if (this.keyboardHandler) {
this.keyboardHandler.destroy();
}
this.keyboardHandler = new KeyboardHandler(this.eventBus, this.settings, this.logger);
this.keyboardHandler.init();
this.logger.log('info', 'debug', "[uw.js::setup] KeyboardHandler initiated.");
if (this.keyboardHandler) {
this.keyboardHandler.destroy();
}
this.keyboardHandler = new KeyboardHandler(this.eventBus, this.siteSettings, this.settings, this.logger);
this.keyboardHandler.init();
this.logger.log('info', 'debug', "[uw.js::setup] KeyboardHandler initiated.");
} catch (e) {
console.error('Ultrawidify: failed to start extension. Error:', e)

View File

@ -12,6 +12,7 @@ import Logger from './Logger';
import SettingsInterface from '../../common/interfaces/SettingsInterface';
import { browser } from 'webextension-polyfill-ts';
import AspectRatioType from '../../common/enums/AspectRatioType.enum';
import { SiteSettings } from './settings/SiteSettings';
if(process.env.CHANNEL !== 'stable'){
console.info("Loading Settings");
@ -348,189 +349,6 @@ class Settings {
return JSON.parse(JSON.stringify(this.default));
}
// -----------------------------------------
// Config for a given page:
//
// <hostname> : {
// status: <option> // should extension work on this site?
// arStatus: <option> // should we do autodetection on this site?
// statusEmbedded: <option> // reserved for future... maybe
// }
//
// Valid values for options:
//
// status, arStatus, statusEmbedded:
//
// * enabled — always allow
// * basic — only allow fullscreen
// * default — allow if default is to allow, block if default is to block
// * disabled — never allow
getActionsForSite(site) {
if (!site) {
return this.active.actions;
}
if (this.active.sites[site] && this.active.sites[site].actions && this.active.sites[site].actions.length > 0) {
return this.active.sites[site].actions;
}
return this.active.actions;
}
getSettingsForSite(site?) {
if (!site) {
site = window.location.hostname;
}
return this.active.sites[site];
}
getExtensionMode(site?: string) {
if (!site) {
site = window.location.hostname;
if (!site) {
this.logger?.log('info', 'settings', `[Settings::canStartExtension] window.location.hostname is null or undefined: ${window.location.hostname} \nactive settings:`, this.active);
return ExtensionMode.Disabled;
}
}
try {
// if site-specific settings don't exist for the site, we use default mode:
if (! this.active.sites[site]) {
return this.getExtensionMode('@global');
}
if (this.active.sites[site].mode === ExtensionMode.Enabled) {
return ExtensionMode.Enabled;
} else if (this.active.sites[site].mode === ExtensionMode.Basic) {
return ExtensionMode.Basic;
} else if (this.active.sites[site].mode === ExtensionMode.Disabled) {
return ExtensionMode.Disabled;
} else {
if (site !== '@global') {
return this.getExtensionMode('@global');
} else {
return ExtensionMode.Disabled;
}
}
} catch(e){
this.logger?.log('error', 'settings', "[Settings.js::canStartExtension] Something went wrong — are settings defined/has init() been called?\n\nerror:", e, "\n\nSettings object:", this)
return ExtensionMode.Disabled;
}
}
/**
* Returns whether extension can start on a given site or not.
* @param site default value fof this argument is window.location.hostname
* @returns true if extension can run on this site, false otherwise
*/
isEnabledForSite(site = window.location.hostname) {
if (!site) {
this.logger?.log('info', 'settings', `[Settings::canStartExtension] window.location.hostname is null or undefined: ${window.location.hostname} \nactive settings:`, this.active);
return false;
}
// if (Debug.debug) {
// // let's just temporarily disable debugging while recursively calling
// // this function to get extension status on current site without duplo
// // console logs (and without endless recursion)
// Debug.debug = false;
// const cse = this.canStartExtension(site);
// Debug.debug = true;
// }
try{
// if site is not defined, we use default mode:
if (! this.active.sites[site] || this.active.sites[site].mode === ExtensionMode.Default) {
return this.active.sites['@global'].mode === ExtensionMode.Enabled;
}
if (this.active.sites['@global'].mode === ExtensionMode.Enabled) {
return this.active.sites[site].mode !== ExtensionMode.Disabled;
} else if (this.active.sites['@global'].mode === ExtensionMode.Whitelist) {
return this.active.sites[site].mode === ExtensionMode.Enabled;
} else {
return false;
}
} catch(e) {
this.logger?.log('error', 'settings', "[Settings.js::canStartExtension] Something went wrong — are settings defined/has init() been called?\nSettings object:", this);
return false;
}
}
keyboardShortcutsEnabled(site) {
if (!site) {
site = window.location.hostname;
}
if (!site) {
return false;
}
try {
if (!this.active.sites[site]
|| this.active.sites[site].keyboardShortcutsEnabled === undefined
|| this.active.sites[site].keyboardShortcutsEnabled === ExtensionMode.Default) {
return this.keyboardShortcutsEnabled('@global');
} else {
return this.active.sites[site].keyboardShortcutsEnabled === ExtensionMode.Enabled;
}
} catch (e) {
this.logger?.log('info', 'settings',"[Settings.js::keyboardDisabled] something went wrong:", e);
return false;
}
}
extensionEnabled(){
return this.active.sites['@global'].mode !== ExtensionMode.Disabled
}
canStartAutoAr(site?: string) {
// 'site' argument is only ever used when calling this function recursively for debugging
if (!site) {
site = window.location.hostname;
if (!site) {
this.logger?.log('warn', ['settings', 'init', 'debug'], `[Settings::canStartAutoAr] No site — even window.location.hostname returned nothing!: ${window.location.hostname}`);
return false;
}
}
// if (Debug.debug) {
// let's just temporarily disable debugging while recursively calling
// this function to get extension status on current site without duplo
// console logs (and without endless recursion)
// Debug.debug = false;
// const csar = this.canStartAutoAr(site);
// Debug.debug = true;
this.logger?.log('info', ['settings', 'init', 'debug'], "[Settings::canStartAutoAr] ----------------\nCAN WE START AUTOAR ON SITE", site,
"?\n\nsettings.active.sites[site]=", this.active.sites[site], "settings.active.sites[@global]=", this.active.sites['@global'],
"\nAutoar mode (global)?", this.active.sites['@global'].autoar,
`\nAutoar mode (${site})`, this.active.sites[site] ? this.active.sites[site].autoar : '<not defined>',
// "\nCan autoar be started?", csar
);
// }
// if site is not defined, we use default mode:
if (! this.active.sites[site]) {
this.logger?.log('info', ['settings', 'aard', 'init', 'debug'], "[Settings::canStartAutoAr] Settings not defined for this site, returning defaults.", site, this.active.sites[site], this.active.sites);
return this.active.sites['@global'].autoar === ExtensionMode.Enabled;
}
if (this.active.sites['@global'].autoar === ExtensionMode.Enabled) {
this.logger?.log('info', ['settings', 'aard', 'init', 'debug'], `[Settings::canStartAutoAr] Aard is enabled by default. Extension can run unless disabled for this site.`, this.active.sites[site].autoar);
return this.active.sites[site].autoar !== ExtensionMode.Disabled;
} else if (this.active.sites['@global'].autoar === ExtensionMode.Whitelist) {
this.logger?.log('info', ['settings', 'init', 'debug'], "canStartAutoAr — can(not) start aard because extension is in whitelist mode, and this site is (not) equal to", ExtensionMode.Enabled)
return this.active.sites[site].autoar === ExtensionMode.Enabled;
} else {
this.logger?.log('info', ['settings', 'init', 'debug'], "canStartAutoAr — cannot start aard because extension is globally disabled")
return false;
}
}
getDefaultOption(option?) {
const allDefault = {
mode: ExtensionMode.Default,
@ -547,40 +365,6 @@ class Settings {
return allDefault[option];
}
getDefaultAr(site) {
// site = this.getSiteSettings(site);
// if (site.defaultAr) {
// return site.defaultAr;
// }
return this.active.miscSettings.defaultAr;
}
getDefaultStretchMode_legacy(site) {
if (site && (this.active.sites[site]?.stretch ?? StretchType.Default) !== StretchType.Default) {
return this.active.sites[site].stretch;
}
return this.active.sites['@global'].stretch;
}
getDefaultCropPersistenceMode(site) {
if (site && (this.active.sites[site]?.cropModePersistence ?? StretchType.Default) !== StretchType.Default) {
return this.active.sites[site].cropModePersistence;
}
// persistence mode thing is missing from settings by default
return this.active.sites['@global'].cropModePersistence || CropModePersistence.Disabled;
}
getDefaultVideoAlignment(site) {
if ( (this.active.sites[site]?.videoAlignment ?? VideoAlignmentType.Default) !== VideoAlignmentType.Default) {
return this.active.sites[site].videoAlignment;
}
return this.active.sites['@global'].videoAlignment;
}
/**
* Gets default site configuration. Only returns essential settings.
* @returns
@ -597,48 +381,8 @@ class Settings {
}
}
/**
* Gets default cropping mode for extension.
* Returns site default if defined, otherwise it returns extension default.
* If extension default is not defined because extension updated but the
* settings didn't port over, we return automatic.
*/
getDefaultCrop(site?: string) {
return this.active.sites[site ?? window.location.hostname]?.defaultCrop ?? this.active.crop?.default ?? {type: AspectRatioType.Automatic};
}
/**
* Gets default stretching mode for extension.
* Returns site default if defined, otherwise it returns extension default.
* If extension default is not defined because extension updated but the
* settings didn't port over, we return automatic.
*/
getDefaultStretchMode(site?: string) {
return this.active.sites[site ?? window.location.hostname]?.defaultStretch ?? this.active.stretch.default ?? {type: StretchType.NoStretch};
}
/**
* Sets a site option and initializes values if empty.
* Does not save settings.
* @param path
* @param value
* @param site
*/
setSiteOption(path: string[], value: any, site?: string) {
site = site ?? window.location.hostname;
if (!this.active.sites[site]) {
this.active.sites[site] = {};
}
let object = this.active.sites[site];
for (let i = 0; i < path.length - 1; i++) {
if (!object[path[i]]) {
object[path[i]] = {};
}
object = object[path[i]];
}
object[path[path.length - 1]] = value;
getSiteSettings(site: string = window.location.hostname): SiteSettings {
return new SiteSettings(this, site);
}
}

View File

@ -252,7 +252,7 @@ class ArDetector {
this.resetBlackLevel();
// if we're restarting ArDetect, we need to do this in order to force-recalculate aspect ratio
this.conf.resizer.setLastAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr});
this.conf.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr};
this.canvasImageDataRowLength = cwidth << 2;
@ -277,7 +277,7 @@ class ArDetector {
start() {
if (this.conf.resizer.lastAr.type === AspectRatioType.AutomaticUpdate) {
// ensure first autodetection will run in any case
this.conf.resizer.setLastAr({type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr});
this.conf.resizer.lastAr = {type: AspectRatioType.AutomaticUpdate, ratio: this.defaultAr};
}
// start autodetection
@ -781,7 +781,7 @@ class ArDetector {
}
// check if aspect ratio is changed:
let lastAr = this.conf.resizer.getLastAr();
let lastAr = this.conf.resizer.lastAr;
if (lastAr.type === AspectRatioType.AutomaticUpdate && lastAr.ratio !== null && lastAr.ratio !== undefined){
// we can only deny aspect ratio changes if we use automatic mode and if aspect ratio was set from here.

View File

@ -1,11 +1,13 @@
import EventBus, { EventBusCommand } from '../EventBus';
import Logger from '../Logger';
import Settings from '../Settings';
import { SiteSettings } from '../settings/SiteSettings';
export class KbmBase {
listenFor: string[] = [];
logger: Logger;
settings: Settings;
siteSettings: SiteSettings;
eventBus: EventBus;
eventBusCommands: { [x: string]: EventBusCommand } = {
@ -26,7 +28,7 @@ export class KbmBase {
},
}
constructor(eventBus: EventBus, settings: Settings, logger: Logger) {
constructor(eventBus: EventBus, siteSettings: SiteSettings, settings: Settings, logger: Logger) {
this.logger = logger;
this.settings = settings;
this.eventBus = eventBus;
@ -90,9 +92,10 @@ export class KbmBase {
}
load() {
if (! (this.settings.isEnabledForSite() && this.settings.active.kbm.enabled)) {
return;
}
// if (! (this.settings.isEnabledForSite() && this.settings.active.kbm.enabled)) {
// return;
// }
// todo: detect if this is enabled or not
this.addListener();
}

View File

@ -7,6 +7,7 @@ import Settings from '../Settings';
import VideoData from '../video-data/VideoData';
import EventBus, { EventBusCommand } from '../EventBus';
import KbmBase from './KbmBase';
import { SiteSettings } from '../settings/SiteSettings';
if(process.env.CHANNEL !== 'stable'){
console.info("Loading KeyboardHandler");
@ -24,6 +25,7 @@ export class KeyboardHandler extends KbmBase {
listenFor: string[] = ['keyup'];
logger: Logger;
settings: Settings;
siteSettings: SiteSettings;
eventBus: EventBus;
playerElements: HTMLElement[] = [];
@ -43,8 +45,8 @@ export class KeyboardHandler extends KbmBase {
}
//#region lifecycle
constructor(eventBus: EventBus, settings: Settings, logger: Logger) {
super(eventBus, settings, logger);
constructor(eventBus: EventBus, siteSettings: SiteSettings, settings: Settings, logger: Logger) {
super(eventBus, siteSettings, settings, logger);
this.init();
}
@ -124,7 +126,7 @@ export class KeyboardHandler extends KbmBase {
"\nis type === 'text'? (yes -> prevent):", activeElement.getAttribute("type") === "text",
"\nevent.target.isContentEditable? (yes -> prevent):", event.target.isContentEditable,
"\nis keyboard local disabled? (yes -> prevent):", this.keyboardLocalDisabled,
"\nis keyboard enabled in settings? (no -> prevent)", this.settings.keyboardShortcutsEnabled(window.location.hostname),
// "\nis keyboard enabled in settings? (no -> prevent)", this.settings.keyboardShortcutsEnabled(window.location.hostname),
"\nwill the action be prevented? (yes -> prevent)", preventAction,
"\n-----------------{ extra debug info }-------------------",
"\ntag name? (lowercase):", activeElement.tagName, activeElement.tagName.toLocaleLowerCase(),
@ -139,9 +141,9 @@ export class KeyboardHandler extends KbmBase {
if (this.keyboardLocalDisabled) {
return true;
}
if (!this.settings.keyboardShortcutsEnabled(window.location.hostname)) {
return true;
}
// if (!this.settings.keyboardShortcutsEnabled(window.location.hostname)) {
// return true;
// }
if (this.inputs.indexOf(activeElement.tagName.toLocaleLowerCase()) !== -1) {
return true;
}

View File

@ -1,6 +1,7 @@
import EventBus, { EventBusCommand } from '../EventBus';
import Logger from '../Logger';
import Settings from '../Settings';
import { SiteSettings } from '../settings/SiteSettings';
import KbmBase from './KbmBase';
if(process.env.CHANNEL !== 'stable'){
@ -35,11 +36,12 @@ export class MouseHandler extends KbmBase {
}
//#region lifecycle
constructor(playerElement: HTMLElement, eventBus: EventBus, settings: Settings, logger: Logger) {
super(eventBus, settings, logger);
constructor(playerElement: HTMLElement, eventBus: EventBus, siteSettings: SiteSettings, settings: Settings, logger: Logger) {
super(eventBus, siteSettings, settings, logger);
this.logger = logger;
this.settings = settings;
this.siteSettings = siteSettings;
this.eventBus = eventBus;
this.playerElement = playerElement;
@ -52,10 +54,7 @@ export class MouseHandler extends KbmBase {
}
load() {
if (!this.settings.isEnabledForSite() || this.settings.active.kbm.enabled) {
return;
}
// todo: process whether mouse movement should be enabled or disabled
this.addListener();
}

View File

@ -4,10 +4,12 @@ import { SiteSettingsInterface } from '../../../common/interfaces/SettingsInterf
import { _cp } from '../../../common/js/utils';
import Settings from '../Settings';
import { browser } from 'webextension-polyfill-ts';
import StretchType from '../../../common/enums/StretchType.enum';
import VideoAlignmentType from '../../../common/enums/VideoAlignmentType.enum';
export class SiteSettings {
private settings: Settings;
private site: string;;
private site: string;
data: SiteSettingsInterface;
temporaryData: SiteSettingsInterface;
@ -45,8 +47,24 @@ export class SiteSettings {
return;
}
this.data.defaultCrop = this.data.defaultCrop ?? _cp(this.defaultSettings.defaultCrop);
this.data.defaultStretch = this.data.defaultStretch ?? _cp(this.defaultSettings.defaultStretch);
// 'undefined' default here means use default
this.data.defaults.crop = this.data.defaults.crop ?? _cp(this.defaultSettings.defaults.crop);
// these can contain default options, but can also be undefined
if (this.data.defaults?.stretch === StretchType.Default || this.data.defaults?.stretch === undefined) {
this.data.defaults.stretch = _cp(this.defaultSettings.defaults.stretch);
}
if (this.data.defaults?.alignment === undefined) { // distinguish between undefined and 0!
this.data.defaults.alignment = _cp(this.defaultSettings.defaults.alignment);
} else {
if (this.data.defaults?.alignment.x === VideoAlignmentType.Default) {
this.data.defaults.alignment.x = _cp(this.defaultSettings.defaults.alignment.x);
}
if (this.data.defaults.alignment.y === VideoAlignmentType.Default) {
this.data.defaults.alignment.y = _cp(this.defaultSettings.defaults.alignment.y);
}
}
for (const enableSegment of ['enable', 'enableAard', 'enableKeyboard']) {
@ -97,8 +115,9 @@ export class SiteSettings {
}
//#endregion
//#region get shit
/**
* Gets custom query selector for player or video, if element exists, is manually defined, and has querySelectors property.
* Gets custom query selector for player or video, if configuration for it exists, is manually defined, and has querySelectors property.
* @param element player or video
* @returns querySelector if possible, undefined otherwise
*/
@ -106,6 +125,80 @@ export class SiteSettings {
return this.data.currentDOMConfig?.elements?.[element]?.manual && this.data.currentDOMConfig?.elements?.[element]?.querySelectors || undefined;
}
/**
* Gets custom element index for player, if configuration for player exists, is manually defined, and has index property defined.
* NOTE: while querySelector should take priority over index, this function does NOT take that into account.
* @returns parent element index if possible, undefined otherwise
*/
getPlayerIndex(): number | undefined {
return this.data.currentDOMConfig?.elements?.player?.manual && this.data.currentDOMConfig?.elements?.player?.index || undefined;
}
/**
* Gets default crop mode for extension, while taking persistence settings into account
*/
getDefaultOption(option: 'crop' | 'stretch' | 'alignment') {
const persistenceLevel = this.data.persistOption[option];
switch (persistenceLevel) {
case CropModePersistence.UntilPageReload:
return this.temporaryData.defaults[option];
case CropModePersistence.CurrentSession:
return this.sessionData.defaults[option];
case CropModePersistence.Disabled:
case CropModePersistence.Default:
case CropModePersistence.Forever:
default:
return this.data.defaults[option];
}
}
private _getEnvironment(isTheater: boolean, isFullscreen: boolean): 'fullscreen' | 'theater' | 'normal' {
if (isFullscreen) {
return 'fullscreen';
}
if (isTheater) {
return 'theater';
}
return 'normal';
}
/**
* Is extension allowed to run in current environment
* @param isTheater
* @param isFullscreen
* @returns
*/
isEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean) {
const env = this._getEnvironment(isTheater, isFullscreen);
return this.data.enable[env];
}
/**
* Is autodetection allowed to run, given current environment
* @param isTheater
* @param isFullscreen
* @returns
*/
isAardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean) {
const env = this._getEnvironment(isTheater, isFullscreen);
return this.data.enableAard[env];
}
/**
* Returns whether keyboard interactions are enabled in current environment
* @param isTheater
* @param isFullscreen
* @returns
*/
isKeyboardEnabledForEnvironment(isTheater: boolean, isFullscreen: boolean) {
const env = this._getEnvironment(isTheater, isFullscreen);
return this.data.enableKeyboard[env];
}
//#endregion
//#region set shit
/**
* Sets option value.
* @param optionPath path to value in object notation (dot separated)
@ -123,6 +216,9 @@ export class SiteSettings {
let iterator = this.settings.active.sites[this.site];
let i;
for (i = 0; i < pathParts.length - 1; i++) {
if (!iterator[pathParts[i]]) { // some optional paths may still be undefined, even after cloning empty object
iterator[pathParts[i]] = {};
}
iterator = iterator[pathParts[i]];
}
iterator[pathParts[i]] = optionValue;
@ -168,25 +264,6 @@ export class SiteSettings {
sessionStorage.setItem('uw-session-defaults', JSON.stringify(this.sessionData));
}
/**
* Gets default crop mode for extension, while taking persistence settings into account
*/
getDefaultOption(option: 'crop' | 'stretch' | 'alignment') {
const persistenceLevel = this.data.persistOption[option];
switch (persistenceLevel) {
case CropModePersistence.UntilPageReload:
return this.temporaryData.defaults[option];
case CropModePersistence.CurrentSession:
return this.sessionData.defaults[option];
case CropModePersistence.Disabled:
case CropModePersistence.Default:
case CropModePersistence.Forever:
default:
return this.data.defaults[option];
}
}
/**
* Updates options while accounting for persistence settings
* @param option
@ -209,5 +286,6 @@ export class SiteSettings {
return;
}
}
//#endregion
}

View File

@ -61,7 +61,6 @@ class PageInfo {
//#region misc stuff
lastUrl: string;
extensionMode: ExtensionMode;
defaultCrop: any;
currentCrop: any;
keyboardHandlerInitQueue: any[] = [];
@ -70,14 +69,12 @@ class PageInfo {
keyboardHandler: any;
//#endregion
constructor(eventBus: EventBus, settings: Settings, logger: Logger, extensionMode, readOnly = false){
constructor(eventBus: EventBus, siteSettings: SiteSettings, settings: Settings, logger: Logger, readOnly = false){
this.logger = logger;
this.settings = settings;
this.siteSettings = new SiteSettings(settings, window.location.hostname);
this.siteSettings = siteSettings;
this.lastUrl = window.location.href;
this.extensionMode = extensionMode;
this.readOnly = readOnly;
this.isFullscreen = !!document.fullscreenElement;

View File

@ -10,6 +10,7 @@ import Settings from '../Settings';
import Logger from '../Logger';
import EventBus from '../EventBus';
import UI from '../uwui/UI';
import { SiteSettings } from '../settings/SiteSettings';
if (process.env.CHANNEL !== 'stable'){
console.info("Loading: PlayerData.js");
@ -41,7 +42,7 @@ class PlayerData {
//#region helper objects
logger: Logger;
videoData: VideoData;
settings: Settings;
siteSettings: SiteSettings;
notificationService: PlayerNotificationUi;
eventBus: EventBus;
//#endregion
@ -90,7 +91,7 @@ class PlayerData {
*/
get aspectRatio() {
try {
if (this.isFullscreen && !this.settings.getSettingsForSite()?.usePlayerArInFullscreen) {
if (this.isFullscreen) {
return window.innerWidth / window.innerHeight;
}
@ -106,7 +107,7 @@ class PlayerData {
this.logger = videoData.logger;
this.videoData = videoData;
this.video = videoData.video;
this.settings = videoData.settings;
this.siteSettings = videoData.siteSettings;
this.eventBus = videoData.eventBus;
this.extensionMode = videoData.extensionMode;
this.invalid = false;
@ -122,7 +123,7 @@ class PlayerData {
this.periodicallyRefreshPlayerElement = false;
try {
this.periodicallyRefreshPlayerElement = this.settings.active.sites[window.location.hostname].DOM.player.periodicallyRefreshPlayerElement;
this.periodicallyRefreshPlayerElement = this.siteSettings.data.currentDOMConfig.periodicallyRefreshPlayerElement;
} catch (e) {
// no biggie — that means we don't have any special settings for this site.
}
@ -225,30 +226,28 @@ class PlayerData {
private handleSizeConstraints(currentPlayerDimensions: PlayerDimensions) {
// never disable ultrawidify in full screen
if (this.isFullscreen) {
this.enable();
return;
}
const restrictions = this.settings.getSettingsForSite()?.restrictions ?? this.settings.active?.restrictions;
// if (this.isFullscreen) {
// this.enable();
// return;
// }
// if 'disable on small players' option is not enabled, the extension will run in any case
if (!restrictions?.disableOnSmallPlayers) {
this.enable();
return;
}
// if (!restrictions?.disableOnSmallPlayers) {
// this.enable();
// return;
// }
// If we only allow ultrawidify in full screen, we disable it when not in full screen
if (restrictions.onlyAllowInFullscreen && !currentPlayerDimensions.fullscreen) {
this.disable();
return;
}
// if (restrictions.onlyAllowInFullscreen && !currentPlayerDimensions.fullscreen) {
// this.disable();
// return;
// }
// if current width or height are smaller than the minimum, the extension will not run
if (restrictions.minAllowedHeight > currentPlayerDimensions?.height || restrictions.minAllowedWidth > currentPlayerDimensions?.width) {
this.disable();
return;
}
// if (restrictions.minAllowedHeight > currentPlayerDimensions?.height || restrictions.minAllowedWidth > currentPlayerDimensions?.width) {
// this.disable();
// return;
// }
// in this case, the player is big enough to warrant enabling Ultrawidify
this.enable();
@ -483,26 +482,20 @@ class PlayerData {
}
this.elementStack = elementStack;
if (this.settings.active.sites[host]?.DOM?.player?.manual) {
if (this.settings.active.sites[host]?.DOM?.player?.useRelativeAncestor
&& this.settings.active.sites[host]?.DOM?.player?.videoAncestor) {
playerCandidate = this.getPlayerParentIndex(elementStack);
} else if (this.settings.active.sites[host]?.DOM?.player?.querySelectors) {
playerCandidate = this.getPlayerQs(elementStack, videoWidth, videoHeight);
}
const playerQs = this.siteSettings.getCustomDOMQuerySelector('player');
const playerIndex = this.siteSettings.getPlayerIndex();
// if 'verbose' option is passed, we also populate the elementStack
// with heuristics data for auto player detection.
if (playerCandidate && !options?.verbose) {
return playerCandidate.element;
}
if (playerQs) {
playerCandidate = this.getPlayerQs(playerQs, elementStack, videoWidth, videoHeight);
} else if (playerIndex) { // btw 0 is not a valid index for player
playerCandidate = elementStack[playerIndex];
}
if (options?.verbose && playerCandidate) {
// remember — we're only populating elementStack. If we found a player
// element using manual methods, we will still return that element.
this.getPlayerAuto(elementStack, videoWidth, videoHeight);
playerCandidate.heuristics['activePlayer'] = true;
if (playerCandidate) {
if (options?.verbose) {
this.getPlayerAuto(elementStack, videoWidth, videoHeight);
playerCandidate.heuristics['activePlayer'] = true;
}
return playerCandidate.element;
} else {
const playerCandidate = this.getPlayerAuto(elementStack, videoWidth, videoHeight);
@ -511,6 +504,13 @@ class PlayerData {
}
}
/**
* Gets player based on some assumptions, without us defining shit.
* @param elementStack
* @param videoWidth
* @param videoHeight
* @returns
*/
private getPlayerAuto(elementStack: any[], videoWidth, videoHeight) {
let penaltyMultiplier = 1;
const sizePenaltyMultiplier = 0.1;
@ -592,12 +592,29 @@ class PlayerData {
return bestCandidate;
}
private getPlayerQs(elementStack: any[], videoWidth, videoHeight) {
const host = window.location.hostname;
/**
* Gets player element based on a query string.
*
* Since query string does not necessarily uniquely identify an element, this function also
* tries to evaluate which candidate of element that match the query selector is the most
* likely the one element we're looking for.
*
* Function prefers elements that are:
* 1. closer to the video
* 2. about the same size as the video
* 3. they must appear between video and root of the DOM hierarchy
*
* @param queryString query string for player element
* @param elementStack branch of DOM hierarchy that ends with a video
* @param videoWidth width of the video
* @param videoHeight height of the video
* @returns best candidate or null, if nothing in elementStack matches our query selector
*/
private getPlayerQs(queryString: string, elementStack: any[], videoWidth, videoHeight) {
const perLevelScorePenalty = 10;
let penaltyMultiplier = 0;
const allSelectors = document.querySelectorAll(this.settings.active.sites[host].DOM.player.querySelectors);
const allSelectors = document.querySelectorAll(queryString);
for (const element of elementStack) {
if (this.collectionHas(allSelectors, element.element)) {
@ -634,17 +651,12 @@ class PlayerData {
return bestCandidate;
}
private getPlayerParentIndex(elementStack: any[]) {
const host = window.location.hostname;
elementStack[this.settings.active.sites[host].DOM.player.videoAncestor].heuristics['manualElementByParentIndex'] = true;
return elementStack[this.settings.active.sites[host].DOM.player.videoAncestor];
}
/**
* Lists elements between video and DOM root for display in player selector (UI)
*/
private handlePlayerTreeRequest() {
// this populates this.elementStack fully
this.getPlayer({verbose: true});
console.info('player-tree: emitting stack:', this.elementStack);
this.eventBus.send('uw-config-broadcast', {type: 'player-tree', config: JSON.parse(JSON.stringify(this.elementStack))});
}

View File

@ -13,6 +13,7 @@ import { sleep } from '../../../common/js/utils';
import { hasDrm } from '../ar-detect/DrmDetecor';
import EventBus from '../EventBus';
import { SiteSettings } from '../settings/SiteSettings';
import { Ar } from '../../../common/interfaces/ArInterface';
/**
* VideoData handles CSS for the video element.
@ -226,7 +227,7 @@ class VideoData {
// after we receive a "please crop" or "please stretch".
// Time to apply any crop from address of crop mode persistence
const defaultCrop = this.siteSettings.getDefaultOption('crop');
const defaultCrop = this.siteSettings.getDefaultOption('crop') as Ar;
const defaultStretch = this.siteSettings.getDefaultOption('stretch');
this.resizer.setAr(defaultCrop);

View File

@ -1,3 +1,4 @@
import { SiteSettings } from './../settings/SiteSettings';
import Debug from '../../conf/Debug';
import Scaler, { CropStrategy, VideoDimensions } from './Scaler';
import Stretcher from './Stretcher';
@ -10,9 +11,12 @@ import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
import CropModePersistance from '../../../common/enums/CropModePersistence.enum';
import { sleep } from '../Util';
import Logger from '../Logger';
import Settings from '../Settings';
import siteSettings from '../Settings';
import VideoData from '../video-data/VideoData';
import EventBus from '../EventBus';
import { _cp } from '../../../common/js/utils';
import Settings from '../Settings';
import { Ar } from '../../../common/interfaces/ArInterface';
if(Debug.debug) {
console.log("Loading: Resizer.js");
@ -28,6 +32,7 @@ class Resizer {
//#region helper objects
logger: Logger;
settings: Settings;
siteSettings: SiteSettings;
scaler: Scaler;
stretcher: Stretcher;
zoom: Zoom;
@ -47,8 +52,8 @@ class Resizer {
currentCssValidFor: any;
currentVideoSettings: any;
_lastAr: {type: any, ratio?: number} = {type: AspectRatioType.Initial};
set lastAr(x: {type: any, ratio?: number}) {
_lastAr: Ar = {type: AspectRatioType.Initial};
set lastAr(x: Ar) {
this._lastAr = x;
// emit updates for UI when setting lastAr
this.eventBus.send('uw-config-broadcast', {type: 'ar', config: x})
@ -101,6 +106,7 @@ class Resizer {
this.logger = videoData.logger;
this.video = videoData.video;
this.settings = videoData.settings;
this.siteSettings = videoData.siteSettings;
this.eventBus = videoData.eventBus;
this.initEventBus();
@ -108,18 +114,15 @@ class Resizer {
this.stretcher = new Stretcher(this.conf);
this.zoom = new Zoom(this.conf);
this.videoAlignment = {
x: this.settings.getDefaultVideoAlignment(window.location.hostname),
y: VideoAlignmentType.Center
}; // this is initial video alignment
this.videoAlignment = this.siteSettings.getDefaultOption('alignment') as {x: VideoAlignmentType, y: VideoAlignmentType} // this is initial video alignment
this.destroyed = false;
if (this.settings.active.pan) {
this.canPan = this.settings.active.miscSettings.mousePan.enabled;
} else {
this.canPan = false;
}
// if (this.siteSettings.active.pan) {
// this.canPan = this.siteSettings.active.miscSettings.mousePan.enabled;
// } else {
// this.canPan = false;
// }
this.userCssClassName = videoData.userCssClassName;
}
@ -215,7 +218,7 @@ class Resizer {
}
}
async setAr(ar: {type: any, ratio?: number}, lastAr?: {type: any, ratio?: number}) {
async setAr(ar: Ar, lastAr?: Ar) {
if (this.destroyed) {
return;
}
@ -253,7 +256,6 @@ class Resizer {
return;
}
const siteSettings = this.settings.active.sites[window.location.hostname];
let stretchFactors: {xFactor: number, yFactor: number, arCorrectionFactor?: number, ratio?: number} | any;
// reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to
@ -271,8 +273,7 @@ class Resizer {
// this means here's the optimal place to set or forget aspect ratio. Saving of current crop ratio
// is handled in pageInfo.updateCurrentCrop(), which also makes sure to persist aspect ratio if ar
// is set to persist between videos / through current session / until manual reset.
if (ar.type === AspectRatioType.Automatic ||
ar.type === AspectRatioType.Reset ||
if (ar.type === AspectRatioType.Reset ||
ar.type === AspectRatioType.Initial ) {
// reset/undo default
this.conf.pageInfo.updateCurrentCrop(undefined);
@ -391,13 +392,6 @@ class Resizer {
this.lastAr = {type: AspectRatioType.Initial};
}
setLastAr(override){
this.lastAr = override;
}
getLastAr(){
return this.lastAr;
}
setStretchMode(stretchMode, fixedStretchRatio?){
this.stretcher.setStretchMode(stretchMode, fixedStretchRatio);
@ -481,7 +475,7 @@ class Resizer {
}
reset(){
this.setStretchMode(this.settings.active.sites[window.location.hostname]?.stretch ?? this.settings.active.sites['@global'].stretch);
this.setStretchMode(this.siteSettings.getDefaultOption('stretch'));
this.zoom.setZoom(1);
this.resetPan();
this.setAr({type: AspectRatioType.Reset});
@ -647,7 +641,7 @@ class Resizer {
private _computeOffsetsRecursionGuard: boolean = false;
computeOffsets(stretchFactors: VideoDimensions){
this.logger.log('info', 'debug', "[Resizer::computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.settings.active.sites['@global'].videoAlignment);
this.logger.log('info', 'debug', "[Resizer::computeOffsets] <rid:"+this.resizerId+"> video will be aligned to ", this.videoAlignment);
const {realVideoWidth, realVideoHeight, marginX, marginY} = this.computeVideoDisplayedDimensions();
@ -831,7 +825,7 @@ class Resizer {
let extraStyleString;
try {
extraStyleString = this.settings.active.sites[window.location.hostname].DOM.video.additionalCss;
extraStyleString = this.siteSettings.data.currentDOMConfig.customCss;
} catch (e) {
// do nothing. It's ok if no special settings are defined for this site, we'll just do defaults
}

View File

@ -1,3 +1,4 @@
import { SiteSettings } from './../settings/SiteSettings';
import StretchType from '../../../common/enums/StretchType.enum';
import BrowserDetect from '../../conf/BrowserDetect';
import AspectRatioType from '../../../common/enums/AspectRatioType.enum';
@ -19,6 +20,7 @@ class Stretcher {
conf: VideoData;
logger: Logger;
settings: Settings;
siteSettings: SiteSettings;
//#endregion
//#region misc data
@ -30,14 +32,15 @@ class Stretcher {
constructor(videoData) {
this.conf = videoData;
this.logger = videoData.logger;
this.siteSettings = videoData.siteSettings;
this.settings = videoData.settings;
this.mode = this.settings.getDefaultStretchMode_legacy(window.location.hostname);
this.mode = this.siteSettings.data.defaults.stretch;
this.fixedStretchRatio = undefined;
}
setStretchMode(stretchMode, fixedStretchRatio?) {
if (stretchMode === StretchType.Default) {
this.mode = this.settings.getDefaultStretchMode_legacy(window.location.hostname);
this.mode = this.siteSettings.data.defaults.stretch;
} else {
if (stretchMode === StretchType.Fixed || stretchMode == StretchType.FixedSource) {
this.fixedStretchRatio = fixedStretchRatio;

View File

@ -32,7 +32,7 @@
Never
</div>
</div>
<h1>Do you want to see update notes when extension receives updates?</h1>
<p>Update notes will open a new tab, just like this one.</p>
<div class="flex flex-row">
@ -72,7 +72,6 @@ export default {
performance: {},
site: null,
currentZoom: 1,
execAction: new ExecAction(),
settings: {},
settingsInitialized: false,
logger: {},

View File

@ -33,7 +33,7 @@
Never
</div>
</div>
<h1>Do you want to see update notes when extension receives updates?</h1>
<p>Update notes will open a new tab, just like this one.</p>
<div class="flex flex-row">
@ -73,7 +73,6 @@ export default {
performance: {},
site: null,
currentZoom: 1,
execAction: new ExecAction(),
settings: {},
settingsInitialized: false,
logger: {},

View File

@ -1,152 +1,6 @@
<template>
<div>
<div class="label">
Enable this extension:
</div>
<div class="flex flex-row button-box">
<Button label="Always"
:selected="settings.active.sites['@global'].mode === ExtensionMode.Enabled"
@click.native="setDefaultExtensionMode(ExtensionMode.Enabled)"
>
</Button>
<Button label="On whitelisted sites"
:selected="settings.active.sites['@global'].mode === ExtensionMode.Whitelist"
@click.native="setDefaultExtensionMode(ExtensionMode.Whitelist)"
>
</Button>
<Button label="Never"
:selected="settings.active.sites['@global'].mode === ExtensionMode.Disabled"
@click.native="setDefaultExtensionMode(ExtensionMode.Disabled)"
>
</Button>
</div>
<div class="description">
<b>Always</b> enables this extension on every site you visit that you didn't blacklist.<br/>
<b>On whitelisted sites</b> enables this extension only on sites you explicitly whitelisted.<br/>
<b>Never</b> disables extension on all sites, even on those you whitelisted.
</div>
<div class="label">
Enable autodetection:
</div>
<div class="flex flex-row button-box">
<Button label="Always"
:selected="settings.active.sites['@global'].autoar === ExtensionMode.Enabled"
@click.native="setDefaultAutodetectionMode(ExtensionMode.Enabled)">
</Button>
<Button label="On whitelisted sites"
:selected="settings.active.sites['@global'].autoar === ExtensionMode.Whitelist"
@click.native="setDefaultAutodetectionMode(ExtensionMode.Whitelist)">
</Button>
<Button label="Never"
:selected="settings.active.sites['@global'].autoar === ExtensionMode.Disabled"
@click.native="setDefaultAutodetectionMode(ExtensionMode.Disabled)">
</Button>
</div>
<div class="description">
<b>Always</b> enables autodetection on every site this extension is enabled for, unless blacklisted.<br/>
<b>On whitelisted sites</b> enables autodetection only for sites that you explicitly enabled.<br/>
<b>Never</b> disables autodetection on all sites, even on those you whitelisted.<br/>
<!-- <br/> -->
<!-- For more settings related to autodetection, please check the 'Autodetection' tab. -->
</div>
<div class="label">
Default video alignment:
</div>
<div class="flex flex-row button-box">
<Button label="Left"
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Left"
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Left)">
</Button>
<Button label="Center"
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Center"
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Center)">
</Button>
<Button label="Right"
:selected="settings.active.sites['@global'].videoAlignment === VideoAlignmentType.Right"
@click.native="setDefaultvideoAlignment(VideoAlignmentType.Right)">
</Button>
</div>
<div class="label">
Default stretch mode:
</div>
<div class="flex flex-row button-box">
<Button label="Don't stretch"
:selected="settings.active.sites['@global'].stretch === StretchType.NoStretch"
@click.native="setDefaultStretchingMode(StretchType.NoStretch)">
</Button>
<Button label="Basic stretch"
:selected="settings.active.sites['@global'].stretch === StretchType.Basic"
@click.native="setDefaultStretchingMode(StretchType.Basic)">
</Button>
<Button label="Hybrid stretch"
:selected="settings.active.sites['@global'].stretch === StretchType.Hybrid"
@click.native="setDefaultStretchingMode(StretchType.Hybrid)">
</Button>
<Button label="Thin borders only"
:selected="settings.active.sites['@global'].stretch === StretchType.Conditional"
@click.native="setDefaultStretchingMode(StretchType.Conditional)"
>
</Button>
</div>
<div class="description">
<b>None:</b> do not stretch the video at all. This is the default option, for men of culture.<br/>
<b>Basic:</b> stretches video to fit the player or screen unconditionally. If video has letterbox encoded, this option <i>will not</i> try to remove letterbox before stretching. You probably shouldn't be using this option.<br/>
<b>Hybrid:</b> stretches the video to fit the player, but only if cropping didn't completely remove the black bars.<br/>
<b>Thin borders:</b> stretches only if the width of black borders after cropping is thin.
<br/>
Threshold for thin borders can be defined below.
</div>
<div class="indent">
<div class="flex flex-row row-padding">
<div class="flex label-secondary">
Thin border threshold:
</div>
<div class="flex flex-input">
<input type="number"
step="any"
:value="settings.active.stretch.conditionalDifferencePercent"
@input="updateStretchThreshold($event.target.value)"
>
</div>
</div>
</div>
<div class="label">
Import, export, reset settings
</div>
<div class="flex flex-column">
<div v-if="downloadPermissionError"
class="w100 center-text warning-lite"
>
Exporting settings requires the 'downloads' permission. (If you want to export settings without granting 'downloads' permission, you can copy-paste settings from 'Super advanced settings' tab)
</div>
<div v-if="corruptedSettingsError"
class="w100 center-text warning-lite"
>
Settings import failed. The settings file is probably corrupted.
</div>
<div class="flex flex-row button-box">
<div class="button center-text flex flex-auto">
<label for="file-upload" class="w100 h100 block">
Import settings
</label>
<input id="file-upload"
type="file"
@input="importSettings"
/>
</div>
<Button label="Export settings"
@click.native="exportSettings()"
/>
<Button label="Reset settings"
@click.native="resetSettings()"
/>
</div>
</div>
this component is deprecated.
</div>
</template>
@ -166,110 +20,13 @@ export default {
},
data () {
return {
StretchType: StretchType,
ExtensionMode: ExtensionMode,
VideoAlignmentType: VideoAlignmentType,
stretchThreshold: 0,
corruptedSettingsError: false,
downloadPermissionError: false,
}
},
created () {
},
methods: {
setDefaultAutodetectionMode(mode) {
this.settings.active.sites['@global'].autoar = mode;
this.settings.save();
},
setDefaultExtensionMode(mode) {
this.settings.active.sites['@global'].mode = mode;
this.settings.save();
},
setDefaultvideoAlignment(mode) {
this.settings.active.sites['@global'].videoAlignment = mode;
this.settings.save();
},
setDefaultStretchingMode(mode) {
this.settings.active.sites['@global'].stretch = mode;
this.settings.save();
},
updateStretchThreshold(newThreshold) {
if (!newThreshold || isNaN(newThreshold)) {
return;
}
this.settings.active.stretch.conditionalDifferencePercent = newThreshold;
this.settings.save();
},
resetSettings() {
this.settings.active = JSON.parse(JSON.stringify(this.settings.default));
this.settings.save();
},
async exportSettings() {
this.downloadPermissionError = false;
const blob = new Blob([JSON.stringify(this.settings.active)], {type: 'application/json'});
const fileUrl = URL.createObjectURL(blob);
try {
if (BrowserDetect.firefox) {
// reminder webextension-polyfill doesn't seem to work in vue!
await browser.permissions.request({permissions: ['downloads']});
browser.downloads.download({saveAs: true, filename: 'ultrawidify-settings.json', url: fileUrl});
} else if (BrowserDetect.anyChromium) {
const ths = this;
chrome.permissions.request(
{permissions: ['downloads']},
(granted) => {
if (granted) {
ths.exportSettingsChrome(fileUrl);
} else {
ths.downloadPermissionError = true
}
}
)
}
} catch (e) {
this.downloadPermissionError = true;
}
},
exportSettingsChrome(fileUrl){
chrome.downloads.download({saveAs: true, filename: 'ultrawidify-settings.json', url: fileUrl});
},
async importSettings($event) {
let file, text, settingsObj;
this.corruptedSettingsError = false;
try {
file = $event.target.files[0];
} catch (e) {
console.error("error grabbing a file!");
this.corruptedSettingsError = true;
return;
}
try {
text = await file.text();
settingsObj = JSON.parse(text);
} catch (e) {
console.error("error parsing file to json");
this.corruptedSettingsError = true;
return;
}
// validate settings
for (const key in this.settings.default) {
if (!settingsObj[key]) {
console.error("corrupted settings!")
this.corruptedSettingsError = true;
return;
}
}
this.settings.active = settingsObj;
this.settings.save();
}
}
}
</script>

View File

@ -67,7 +67,6 @@
import ZoomOptionsPanel from '../../csui/src/PlayerUiPanels/PanelComponents/VideoSettings/ZoomOptionsPanel.vue'
import StretchOptionsPanel from '../../csui/src/PlayerUiPanels/PanelComponents/VideoSettings/StretchOptionsPanel.vue'
import CropOptionsPanel from '../../csui/src/PlayerUiPanels/PanelComponents/VideoSettings/CropOptionsPanel.vue'
import ExecAction from '../../csui/src/ui-libs/ExecAction';
export default {
data() {
@ -88,7 +87,6 @@ export default {
CropOptionsPanel, StretchOptionsPanel, ZoomOptionsPanel
},
created() {
this.exec = new ExecAction(this.settings, window.location.hostname);
this.eventBus.subscribe('uw-config-broadcast', {function: (config) => this.handleConfigBroadcast(config)});
},
mounted() {

View File

@ -250,23 +250,23 @@ export default {
},
props: {
site: String,
settings: Object,
siteSettings: Object,
},
created() {
try {
this.videoManualQs = this.settings.active.sites[this.site]?.DOM?.video?.manual ?? this.videoManualQs;
this.videoQs = this.settings.active.sites[this.site]?.DOM?.video?.querySelectors;
this.videoCss = this.settings.active.sites[this.site]?.DOM?.video?.additionalCss;
this.videoManualQs = this.siteSettings.data.currentDOMConfig?.elements?.video?.manual ?? this.videoManualQs;
this.videoQs = this.siteSettings.data.currentDOMConfig?.elements?.video?.querySelectors;
this.videoCss = this.siteSettings.data.currentDOMConfig?.elements?.video?.nodeCss;
} catch (e) {
// that's here just in case relevant settings for this site don't exist yet
}
try {
this.playerManualQs = this.settings.active.sites[this.site]?.DOM?.player?.manual ?? this.playerManualQs;
this.playerQs = this.settings.active.sites[this.site]?.DOM?.player?.querySelectors;
this.playerByNodeIndex = this.settings.active.sites[this.site]?.DOM?.player?.useRelativeAncestor ?? this.playerByNodeIndex;
this.playerParentNodeIndex = this.settings.active.sites[this.site]?.DOM?.player?.videoAncestor;
this.usePlayerAr = this.settings.active.sites[this.site]?.usePlayerArInFullscreen;
this.playerManualQs = this.siteSettings.data.currentDOMConfig?.elements?.player?.manual ?? this.playerManualQs;
this.playerQs = this.siteSettings.data.currentDOMConfig?.elements?.player?.querySelectors;
this.playerByNodeIndex = !this.siteSettings.data.currentDOMConfig?.elements?.player?.querySelectors || this.playerByNodeIndex;
this.playerParentNodeIndex = this.siteSettings.data.currentDOMConfig?.elements?.player?.index;
// this.usePlayerAr = this.settings.active.sites[this.site]?.usePlayerArInFullscreen;
} catch (e) {
// that's here just in case relevant settings for this site don't exist yet
}
@ -295,32 +295,8 @@ export default {
observer.observe(saveButtonBait);
},
ensureSettings(scope) {
if (! this.settings.active.sites[this.site]) {
this.settings.active.sites[this.site] = {
mode: ExtensionMode.Default,
autoar: ExtensionMode.Default,
type: 'user-added',
stretch: StretchType.Default,
videoAlignment: VideoAlignmentType.Default,
keyboardShortcutsEnabled: ExtensionMode.Default,
}
}
if (! this.settings.active.sites[this.site].DOM) {
this.settings.active.sites[this.site].DOM = {};
}
if (! this.settings.active.sites[this.site].DOM[scope]) {
this.settings.active.sites[this.site].DOM[scope] = {
manual: false,
querySelectors: '',
additionalCss: '',
useRelativeAncestor: scope === 'player' ? false : undefined,
videoAncestor: undefined,
playerNodeCss: scope === 'player' ? '' : undefined,
}
}
},
updateVideoQuerySelector() {
this.siteSettings.set('currentDOMConfig')
this.ensureSettings('video');
this.settings.active.sites[this.site].DOM.video.querySelectors = this.videoQs;
this.settings.save();