Merge branch 'master' into stable

This commit is contained in:
Tamius Han 2019-11-02 02:47:59 +01:00
commit b6ff740b84
31 changed files with 723 additions and 174 deletions

View File

@ -5,10 +5,12 @@
"blackbar", "blackbar",
"blackframe", "blackframe",
"canvas", "canvas",
"comms",
"equalish", "equalish",
"insta", "insta",
"recursing", "recursing",
"reddit", "reddit",
"rescan",
"resizer", "resizer",
"textbox", "textbox",
"videodata", "videodata",

View File

@ -12,7 +12,16 @@ QoL improvements for me:
* logging: allow to enable logging at will and export said logs to a file * logging: allow to enable logging at will and export said logs to a file
### v4.3.1 (current) ### v.4.4.0 (current)
* Russian users (and users of other non-latin keyboard layouts) can now use keyboard shortcuts by default, without having to rebind them manually. (NOTE: if you've changed keyboard shortcuts manually, this change will ***NOT*** be applied to your configuration.)
* NOTE: when using non-latin layouts, 'zoom' shortcut (`z` by default) uses the position of 'Y' on QWERTY layout.
* Ability to preserve aspect ratio between different videos (applies to current page and doesn't survive proper page reloads)
* Changing aspect ratio now resets zooming and panning.
* Fixed bug where keyboard shortcuts would work while typing in certain text fields
* Fixed minor bug with autodetection
### v4.3.1
* Minor rework of settings page (actions & shortcuts section) * Minor rework of settings page (actions & shortcuts section)
* Fixed bug that prevented settings page from opening * Fixed bug that prevented settings page from opening

View File

@ -1,6 +1,6 @@
{ {
"name": "ultravidify", "name": "ultravidify",
"version": "4.3.1", "version": "4.4.0",
"description": "Aspect ratio fixer for youtube that works around some people's disability to properly encode 21:9 (and sometimes, 16:9) videos.", "description": "Aspect ratio fixer for youtube that works around some people's disability to properly encode 21:9 (and sometimes, 16:9) videos.",
"author": "Tamius Han <tamius.han@gmail.com>", "author": "Tamius Han <tamius.han@gmail.com>",
"scripts": { "scripts": {

View File

@ -5,7 +5,7 @@
</div> </div>
<div class="flex action-name"> <div class="flex action-name">
<span v-if="action.cmd && action.cmd.length > 1 || action.cmd[0].action === 'set-ar' && action.cmd[0].arg === AspectRatio.Fixed" class="icon red" @click="removeAction()">🗙</span> &nbsp; &nbsp; <span v-if="action.cmd && action.cmd.length > 1 || action.cmd[0].action === 'set-ar' && action.cmd[0].arg === AspectRatio.Fixed" class="icon red" @click="removeAction()">🗙</span>
<span v-else class="icon transparent">🗙</span> &nbsp; &nbsp; <span v-else class="icon transparent">🗙</span> &nbsp; &nbsp;
<span class="icon" @click="editAction()">🖉</span> &nbsp; &nbsp; <span class="icon" @click="editAction()">🖉</span> &nbsp; &nbsp;
{{action.name}} {{action.name}}

View File

@ -0,0 +1,9 @@
var CropModePersistence = Object.freeze({
Default: -1,
Disabled: 0,
UntilPageReload: 1,
CurrentSession: 2,
Forever: 3,
});
export default CropModePersistence;

View File

@ -1,6 +1,6 @@
class KeyboardShortcutParser { class KeyboardShortcutParser {
static parseShortcut(keypress) { static parseShortcut(keypress) {
var shortcutCombo = ''; let shortcutCombo = '';
if (keypress.ctrlKey) { if (keypress.ctrlKey) {
shortcutCombo += 'Ctrl + '; shortcutCombo += 'Ctrl + ';

View File

@ -1,7 +1,13 @@
export default { export default {
computed: { computed: {
scopeActions: function() { scopeActions: function() {
return this.settings.active.actions.filter(x => x.scopes[this.scope] && x.scopes[this.scope].show) || []; return this.settings.active.actions.filter(x => {
if (! x.scopes) {
console.error('This action does not have a scope.', x);
return false;
}
return x.scopes[this.scope] && x.scopes[this.scope].show
}) || [];
}, },
extensionActions: function(){ extensionActions: function(){
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-extension-mode') || []; return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-extension-mode') || [];
@ -12,6 +18,9 @@ export default {
aspectRatioActions: function(){ aspectRatioActions: function(){
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-ar') || []; return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-ar') || [];
}, },
cropModePersistenceActions: function() {
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-ar-persistence') || [];
},
stretchActions: function(){ stretchActions: function(){
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-stretch') || []; return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-stretch') || [];
}, },

View File

@ -2,6 +2,7 @@ import VideoAlignment from '../../common/enums/video-alignment.enum';
import Stretch from '../../common/enums/stretch.enum'; import Stretch from '../../common/enums/stretch.enum';
import ExtensionMode from '../../common/enums/extension-mode.enum'; import ExtensionMode from '../../common/enums/extension-mode.enum';
import AspectRatio from '../../common/enums/aspect-ratio.enum'; import AspectRatio from '../../common/enums/aspect-ratio.enum';
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
var ActionList = { var ActionList = {
'set-ar': { 'set-ar': {
@ -30,6 +31,33 @@ var ActionList = {
page: true, page: true,
} }
}, },
'set-ar-persistence': {
name: 'Set crop mode persistence',
args: [{
name: 'Never persist',
arg: CropModePersistence.Disabled,
},{
name: 'While on page',
arg: CropModePersistence.UntilPageReload,
},{
name: 'Current session',
arg: CropModePersistence.CurrentSession,
},{
name: 'Always persist',
arg: CropModePersistence.Forever,
}, {
name: 'Default',
arg: CropModePersistence.Default,
scopes: {
site: true,
}
}],
scopes: {
global: true,
site: true,
page: false,
}
},
'set-stretch': { 'set-stretch': {
name: 'Set stretch', name: 'Set stretch',
args: [{ args: [{

View File

@ -117,7 +117,132 @@ const ExtensionConfPatch = [
} }
} }
} }
}, {
forVersion: '4.4.0',
updateFn: (userOptions, defaultOptions) => {
// remove 'press P to toggle panning mode' thing
const togglePan = userOptions.actions.find(x => x.cmd && x.cmd.length === 1 && x.cmd[0].action === 'toggle-pan');
if (togglePan) {
togglePan.scopes = {};
}
// add new actions
userOptions.actions.push({
name: 'Don\'t persist crop',
label: 'Never persist',
cmd: [{
action: 'set-ar-persistence',
arg: 0,
}],
scopes: {
site: {
show: true,
},
global: {
show: true,
}
},
playerUi: {
show: true,
}
}, {
name: 'Persist crop while on page',
label: 'Until page load',
cmd: [{
action: 'set-ar-persistence',
arg: 1,
}],
scopes: {
site: {
show: true,
},
global: {
show: true,
}
},
playerUi: {
show: true,
}
}, {
name: 'Persist crop for current session',
label: 'Current session',
cmd: [{
action: 'set-ar-persistence',
arg: 2,
}],
scopes: {
site: {
show: true,
},
global: {
show: true,
}
},
playerUi: {
show: true,
}
}, {
name: 'Persist until manually reset',
label: 'Always persist',
cmd: [{
action: 'set-ar-persistence',
arg: 3,
}],
scopes: {
site: {
show: true,
},
global: {
show: true,
}
},
playerUi: {
show: true,
}
}, {
name: 'Default crop persistence',
label: 'Default',
cmd: [{
action: 'set-ar-persistence',
arg: -1,
}],
scopes: {
site: {
show: true,
},
},
playerUi: {
show: true,
}
});
// patch shortcuts for non-latin layouts, but only if the user hasn't changed default keys
for (const action of userOptions.actions) {
if (!action.cmd || action.cmd.length !== 1) {
continue;
}
try {
// if this fails, then action doesn't have keyboard shortcut associated with it, so we skip it
const actionDefaults = defaultOptions.actions.find(x => x.cmd && x.cmd.length === 1 // (redundant, default actions have exactly 1 cmd in array)
&& x.cmd[0].action === action.cmd[0].action
&& x.scopes.page
&& x.scopes.page.shortcut
&& x.scopes.page.shortcut.length === 1
&& x.scopes.page.shortcut[0].key === action.scopes.page.shortcut[0].key // this can throw exception, and it's okay
);
if (actionDefaults === undefined) {
continue;
}
// update 'code' property for shortcut
action.scopes.page.shortcut[0]['code'] = actionDefaults.scopes.page.shortcut[0].code;
} catch (e) {
continue;
}
}
}
} }
]; ];
export default ExtensionConfPatch; export default ExtensionConfPatch;

View File

@ -5,6 +5,7 @@ import Stretch from '../../common/enums/stretch.enum';
import ExtensionMode from '../../common/enums/extension-mode.enum'; import ExtensionMode from '../../common/enums/extension-mode.enum';
import AntiGradientMode from '../../common/enums/anti-gradient-mode.enum'; import AntiGradientMode from '../../common/enums/anti-gradient-mode.enum';
import AspectRatio from '../../common/enums/aspect-ratio.enum'; import AspectRatio from '../../common/enums/aspect-ratio.enum';
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
if(Debug.debug) if(Debug.debug)
console.log("Loading: ExtensionConf.js"); console.log("Loading: ExtensionConf.js");
@ -191,6 +192,7 @@ var ExtensionConf = {
label: 'Automatic', // example override, takes precedence over default label label: 'Automatic', // example override, takes precedence over default label
shortcut: [{ shortcut: [{
key: 'a', key: 'a',
code: 'KeyA',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -204,7 +206,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'crop', path: 'crop',
}, },
},{ }, {
name: 'Reset to default', name: 'Reset to default',
label: 'Reset', label: 'Reset',
cmd: [{ cmd: [{
@ -216,6 +218,7 @@ var ExtensionConf = {
show: true, show: true,
shortcut: [{ shortcut: [{
key: 'r', key: 'r',
code: 'KeyR',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -229,7 +232,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'crop' path: 'crop'
}, },
},{ }, {
name: 'Fit to width', name: 'Fit to width',
label: 'Fit width', label: 'Fit width',
cmd: [{ cmd: [{
@ -241,6 +244,7 @@ var ExtensionConf = {
show: true, show: true,
shortcut: [{ shortcut: [{
key: 'w', key: 'w',
code: 'KeyW',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -254,7 +258,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'crop' path: 'crop'
} }
},{ }, {
name: 'Fit to height', name: 'Fit to height',
label: 'Fit height', label: 'Fit height',
cmd: [{ cmd: [{
@ -266,6 +270,7 @@ var ExtensionConf = {
show: true, show: true,
shortcut: [{ shortcut: [{
key: 'e', key: 'e',
code: 'KeyE',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -279,7 +284,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'crop' path: 'crop'
} }
},{ }, {
name: 'Set aspect ratio to 16:9', name: 'Set aspect ratio to 16:9',
label: '16:9', label: '16:9',
cmd: [{ cmd: [{
@ -292,6 +297,7 @@ var ExtensionConf = {
show: true, show: true,
shortcut: [{ shortcut: [{
key: 's', key: 's',
code: 'KeyS',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -305,7 +311,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'crop' path: 'crop'
} }
},{ }, {
name: 'Set aspect ratio to 21:9 (2.39:1)', name: 'Set aspect ratio to 21:9 (2.39:1)',
label: '21:9', label: '21:9',
cmd: [{ cmd: [{
@ -318,6 +324,7 @@ var ExtensionConf = {
show: true, show: true,
shortcut: [{ shortcut: [{
key: 'd', key: 'd',
code: 'KeyD',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -331,7 +338,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'crop' path: 'crop'
} }
},{ }, {
name: 'Set aspect ratio to 18:9', name: 'Set aspect ratio to 18:9',
label: '18:9', label: '18:9',
cmd: [{ cmd: [{
@ -344,6 +351,7 @@ var ExtensionConf = {
show: true, show: true,
shortcut: [{ shortcut: [{
key: 'x', key: 'x',
code: 'KeyX',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -357,7 +365,94 @@ var ExtensionConf = {
show: true, show: true,
path: 'crop', path: 'crop',
} }
},{ }, {
name: 'Don\'t persist crop',
label: 'Never persist',
cmd: [{
action: 'set-ar-persistence',
arg: CropModePersistence.Never,
}],
scopes: {
site: {
show: true,
},
global: {
show: true,
}
},
playerUi: {
show: true,
}
}, {
name: 'Persist crop while on page',
label: 'Until page load',
cmd: [{
action: 'set-ar-persistence',
arg: CropModePersistence.UntilPageReload,
}],
scopes: {
site: {
show: true,
},
global: {
show: true,
}
},
playerUi: {
show: true,
}
}, {
name: 'Persist crop for current session',
label: 'Current session',
cmd: [{
action: 'set-ar-persistence',
arg: CropModePersistence.CurrentSession,
}],
scopes: {
site: {
show: true,
},
global: {
show: true,
}
},
playerUi: {
show: true,
}
}, {
name: 'Persist until manually reset',
label: 'Always persist',
cmd: [{
action: 'set-ar-persistence',
arg: CropModePersistence.Forever,
}],
scopes: {
site: {
show: true,
},
global: {
show: true,
}
},
playerUi: {
show: true,
}
}, {
name: 'Default crop persistence',
label: 'Default',
cmd: [{
action: 'set-ar-persistence',
arg: CropModePersistence.Default,
}],
scopes: {
site: {
show: true,
},
},
playerUi: {
show: true,
}
}, {
name: 'Zoom in', name: 'Zoom in',
label: 'Zoom', label: 'Zoom',
cmd: [{ cmd: [{
@ -369,6 +464,7 @@ var ExtensionConf = {
show: false, show: false,
shortcut: [{ shortcut: [{
key: 'z', key: 'z',
code: 'KeyY',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -381,7 +477,7 @@ var ExtensionConf = {
playerUi: { playerUi: {
show: false, show: false,
} }
},{ }, {
name: 'Zoom out', name: 'Zoom out',
label: 'Unzoom', label: 'Unzoom',
cmd: [{ cmd: [{
@ -393,6 +489,7 @@ var ExtensionConf = {
show: false, show: false,
shortcut: [{ shortcut: [{
key: 'u', key: 'u',
code: 'KeyU',
ctrlKey: false, ctrlKey: false,
metaKey: false, metaKey: false,
altKey: false, altKey: false,
@ -405,32 +502,20 @@ var ExtensionConf = {
playerUi: { playerUi: {
show: false show: false
} }
},{ }, {
name: 'Toggle panning mode', name: 'Toggle panning mode',
label: 'Toggle pan', label: 'Toggle pan',
cmd: [{ cmd: [{
action: 'toggle-pan', action: 'toggle-pan',
arg: 'toggle' arg: 'toggle'
}], }],
scopes: {
page: {
show: true,
shortcut: [{
key: 'p',
ctrlKey: false,
metaKey: false,
altKey: false,
shiftKey: false,
onKeyUp: true,
onKeyDown: false,
}]
}
},
playerUi: { playerUi: {
show: true, show: true,
path: 'zoom' path: 'zoom'
},
scopes: {
} }
},{ }, {
name: 'Hold to pan', name: 'Hold to pan',
cmd: [{ cmd: [{
action: 'pan', action: 'pan',
@ -479,7 +564,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'stretch' path: 'stretch'
} }
},{ }, {
name: 'Set stretch to "basic"', name: 'Set stretch to "basic"',
label: 'Basic stretch', label: 'Basic stretch',
cmd: [{ cmd: [{
@ -504,7 +589,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'stretch' path: 'stretch'
} }
},{ }, {
name: 'Set stretch to "hybrid"', name: 'Set stretch to "hybrid"',
label: 'Hybrid stretch', label: 'Hybrid stretch',
cmd: [{ cmd: [{
@ -529,7 +614,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'stretch' path: 'stretch'
} }
},{ }, {
name: 'Stretch only to hide thin borders', name: 'Stretch only to hide thin borders',
label: 'Thin borders only', label: 'Thin borders only',
cmd: [{ cmd: [{
@ -554,7 +639,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'stretch' path: 'stretch'
} }
},{ }, {
name: 'Set stretch to default value', name: 'Set stretch to default value',
label: 'Default', label: 'Default',
cmd: [{ cmd: [{
@ -592,7 +677,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'align' path: 'align'
} }
},{ }, {
name: 'Align video to center', name: 'Align video to center',
label: 'Center', label: 'Center',
cmd: [{ cmd: [{
@ -614,7 +699,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'align' path: 'align'
} }
},{ }, {
name: 'Align video to the right', name: 'Align video to the right',
label: 'Right', label: 'Right',
cmd: [{ cmd: [{
@ -636,7 +721,7 @@ var ExtensionConf = {
show: true, show: true,
path: 'align' path: 'align'
} }
},{ }, {
name: 'Use default alignment', name: 'Use default alignment',
label: 'Default', label: 'Default',
cmd: [{ cmd: [{
@ -669,7 +754,7 @@ var ExtensionConf = {
show: true, show: true,
} }
} }
},{ }, {
name: 'Enable extension on whitelisted sites only', name: 'Enable extension on whitelisted sites only',
label: 'On whitelist only', label: 'On whitelist only',
cmd: [{ cmd: [{
@ -682,7 +767,7 @@ var ExtensionConf = {
show: true show: true
} }
} }
},{ }, {
name: 'Extension mode: use default settings', name: 'Extension mode: use default settings',
label: 'Default', label: 'Default',
cmd: [{ cmd: [{
@ -695,7 +780,7 @@ var ExtensionConf = {
show: true show: true
} }
} }
},{ }, {
name: 'Disable extension', name: 'Disable extension',
label: 'Disable', label: 'Disable',
cmd: [{ cmd: [{
@ -711,7 +796,7 @@ var ExtensionConf = {
show: true, show: true,
} }
} }
},{ }, {
name: 'Enable automatic aspect ratio detection', name: 'Enable automatic aspect ratio detection',
label: 'Enable', label: 'Enable',
cmd: [{ cmd: [{
@ -727,7 +812,7 @@ var ExtensionConf = {
show: true show: true
} }
} }
},{ }, {
name: 'Enable automatic aspect ratio detection on whitelisted sites only', name: 'Enable automatic aspect ratio detection on whitelisted sites only',
label: 'On whitelist only', label: 'On whitelist only',
cmd: [{ cmd: [{
@ -740,7 +825,7 @@ var ExtensionConf = {
show: true, show: true,
} }
} }
},{ }, {
name: 'Use default settings for automatic aspect ratio detection', name: 'Use default settings for automatic aspect ratio detection',
label: 'Default', label: 'Default',
cmd: [{ cmd: [{
@ -753,7 +838,7 @@ var ExtensionConf = {
show: true, show: true,
} }
} }
},{ }, {
name: 'Disable automatic aspect ratio detection', name: 'Disable automatic aspect ratio detection',
label: 'Disable', label: 'Disable',
cmd: [{ cmd: [{
@ -792,7 +877,7 @@ var ExtensionConf = {
show: true, show: true,
} }
} }
},{ }, {
name: 'Enable keyboard shortcuts on whitelisted sites only', name: 'Enable keyboard shortcuts on whitelisted sites only',
label: 'On whitelist only', label: 'On whitelist only',
cmd: [{ cmd: [{
@ -804,7 +889,7 @@ var ExtensionConf = {
show: true show: true
}, },
} }
},{ }, {
name: 'Keyboard shortcuts mode: use default settings', name: 'Keyboard shortcuts mode: use default settings',
label: 'Default', label: 'Default',
cmd: [{ cmd: [{
@ -816,7 +901,7 @@ var ExtensionConf = {
show: true show: true
} }
} }
},{ }, {
name: 'Disable keyboard shortcuts', name: 'Disable keyboard shortcuts',
label: 'Disable', label: 'Disable',
cmd: [{ cmd: [{

View File

@ -133,28 +133,31 @@ class ActionHandler {
// don't do shit on invalid value of state // don't do shit on invalid value of state
} }
preventAction() { preventAction(event) {
var activeElement = document.activeElement; var activeElement = document.activeElement;
if(this.logger.canLog('keyboard')) { if(this.logger.canLog('keyboard')) {
this.logger.pause(); // temp disable to avoid recursing; this.logger.pause(); // temp disable to avoid recursing;
const preventAction = this.preventAction();
this.logger.resume(); // undisable
this.logger.log('info', 'keyboard', "[ActionHandler::preventAction] Testing whether we're in a textbox or something. Detailed rundown of conditions:\n" + this.logger.log('info', 'keyboard', "[ActionHandler::preventAction] Testing whether we're in a textbox or something. Detailed rundown of conditions:\n" +
"is full screen? (yes->allow):", PlayerData.isFullScreen(), "is full screen? (yes->allow):", PlayerData.isFullScreen(),
"\nis tag one of defined inputs? (yes->prevent):", this.inputs.indexOf(activeElement.tagName.toLocaleLowerCase()) !== -1, "\nis tag one of defined inputs? (yes->prevent):", this.inputs.indexOf(activeElement.tagName.toLocaleLowerCase()) !== -1,
"\nis role = textbox? (yes -> prevent):", activeElement.getAttribute("role") === "textbox", "\nis role = textbox? (yes -> prevent):", activeElement.getAttribute("role") === "textbox",
"\nis type === 'text'? (yes -> prevent):", activeElement.getAttribute("type") === "text", "\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 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)", this.preventAction(), "\nwill the action be prevented? (yes -> prevent)", preventAction,
"\n-----------------{ extra debug info }-------------------", "\n-----------------{ extra debug info }-------------------",
"\ntag name? (lowercase):", activeElement.tagName, activeElement.tagName.toLocaleLowerCase(), "\ntag name? (lowercase):", activeElement.tagName, activeElement.tagName.toLocaleLowerCase(),
"\nrole:", activeElement.getAttribute('role'), "\nrole:", activeElement.getAttribute('role'),
"\ntype:", activeElement.getAttribute('type'), "\ntype:", activeElement.getAttribute('type'),
"insta-fail inputs:", this.inputs "\ninsta-fail inputs:", this.inputs,
"\nevent:", event,
"\nevent.target:", event.target
); );
this.logger.resume(); // undisable
} }
// lately youtube has allowed you to read and write comments while watching video in // lately youtube has allowed you to read and write comments while watching video in
@ -175,25 +178,49 @@ class ActionHandler {
if (activeElement.getAttribute("role") === "textbox") { if (activeElement.getAttribute("role") === "textbox") {
return true; return true;
} }
if (event.target.isContentEditable) {
return true;
}
if (activeElement.getAttribute("type") === "text") { if (activeElement.getAttribute("type") === "text") {
return true; return true;
} }
return false; return false;
} }
isActionMatch(shortcut, event) { isLatin(key) {
return 'abcdefghijklmnopqrstuvwxyz,.-+1234567890'.indexOf(key.toLocaleLowerCase()) !== -1;
}
isActionMatchStandard(shortcut, event) {
return shortcut.key === event.key && return shortcut.key === event.key &&
shortcut.ctrlKey === event.ctrlKey && shortcut.ctrlKey === event.ctrlKey &&
shortcut.metaKey === event.metaKey && shortcut.metaKey === event.metaKey &&
shortcut.altKey === event.altKey && shortcut.altKey === event.altKey &&
shortcut.shiftKey === event.shiftKey shortcut.shiftKey === event.shiftKey
} }
isActionMatchKeyCode(shortcut, event) {
return shortcut.code === event.code &&
shortcut.ctrlKey === event.ctrlKey &&
shortcut.metaKey === event.metaKey &&
shortcut.altKey === event.altKey &&
shortcut.shiftKey === event.shiftKey
}
isActionMatch(shortcut, event, isLatin = true) {
// ASCII and symbols fall back to key code matching, because we don't know for sure that
// regular matching by key is going to work
return isLatin ?
this.isActionMatchStandard(shortcut, event) :
this.isActionMatchStandard(shortcut, event) || this.isActionMatchKeyCode(shortcut, event);
}
execAction(actions, event, videoData) { execAction(actions, event, videoData) {
this.logger.log('info', 'keyboard', "%c[ActionHandler::execAction] Trying to find and execute action for event. Actions/event: ", "color: #ff0", actions, event); this.logger.log('info', 'keyboard', "%c[ActionHandler::execAction] Trying to find and execute action for event. Actions/event: ", "color: #ff0", actions, event);
const isLatin = event.key ? this.isLatin(event.key) : true;
for (var action of actions) { for (var action of actions) {
if (this.isActionMatch(action.shortcut, event)) { if (this.isActionMatch(action.shortcut, event, isLatin)) {
this.logger.log('info', 'keyboard', "%c[ActionHandler::execAction] found an action associated with keypress/event: ", "color: #ff0", action); this.logger.log('info', 'keyboard', "%c[ActionHandler::execAction] found an action associated with keypress/event: ", "color: #ff0", action);
for (var cmd of action.cmd) { for (var cmd of action.cmd) {
@ -228,8 +255,15 @@ class ActionHandler {
this.settings.active.sites[site].arStatus = cmd.arg; this.settings.active.sites[site].arStatus = cmd.arg;
} else if (cmd.action === 'set-keyboard') { } else if (cmd.action === 'set-keyboard') {
this.settings.active.sites[site].keyboardShortcutsEnabled = cmd.arg; this.settings.active.sites[site].keyboardShortcutsEnabled = cmd.arg;
} else if (cmd.action === 'set-ar-persistence') {
this.settings.active.sites[site]['cropModePersistence'] = cmd.arg;
this.pageInfo.setArPersistence(cmd.arg);
this.settings.saveWithoutReload();
}
if (cmd.action !== 'set-ar-persistence') {
this.settings.save();
} }
this.settings.save();
} }
} }
@ -244,7 +278,7 @@ class ActionHandler {
handleKeyup(event) { handleKeyup(event) {
this.logger.log('info', 'keyboard', "%c[ActionHandler::handleKeyup] we pressed a key: ", "color: #ff0", event.key , " | keyup: ", event.keyup, "event:", event); this.logger.log('info', 'keyboard', "%c[ActionHandler::handleKeyup] we pressed a key: ", "color: #ff0", event.key , " | keyup: ", event.keyup, "event:", event);
if (this.preventAction()) { if (this.preventAction(event)) {
this.logger.log('info', 'keyboard', "[ActionHandler::handleKeyup] we are in a text box or something. Doing nothing."); this.logger.log('info', 'keyboard', "[ActionHandler::handleKeyup] we are in a text box or something. Doing nothing.");
return; return;
} }
@ -255,7 +289,7 @@ class ActionHandler {
handleKeydown(event) { handleKeydown(event) {
this.logger.log('info', 'keyboard', "%c[ActionHandler::handleKeydown] we pressed a key: ", "color: #ff0", event.key , " | keydown: ", event.keydown, "event:", event) this.logger.log('info', 'keyboard', "%c[ActionHandler::handleKeydown] we pressed a key: ", "color: #ff0", event.key , " | keydown: ", event.keydown, "event:", event)
if (this.preventAction()) { if (this.preventAction(event)) {
this.logger.log('info', 'keyboard', "[ActionHandler::handleKeydown] we are in a text box or something. Doing nothing."); this.logger.log('info', 'keyboard', "[ActionHandler::handleKeydown] we are in a text box or something. Doing nothing.");
return; return;
} }

View File

@ -6,6 +6,7 @@ import ObjectCopy from '../lib/ObjectCopy';
import Stretch from '../../common/enums/stretch.enum'; import Stretch from '../../common/enums/stretch.enum';
import VideoAlignment from '../../common/enums/video-alignment.enum'; import VideoAlignment from '../../common/enums/video-alignment.enum';
import ExtensionConfPatch from '../conf/ExtConfPatches'; import ExtensionConfPatch from '../conf/ExtConfPatches';
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
@ -26,42 +27,29 @@ class Settings {
const ths = this; const ths = this;
if(currentBrowser.firefox) { if (currentBrowser.firefox) {
browser.storage.onChanged.addListener( (changes, area) => { browser.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
this.logger.log('info', 'settings', "[Settings::<storage/on change>] Settings have been changed outside of here. Updating active settings. Changes:", changes, "storage area:", area);
if (changes['uwSettings'] && changes['uwSettings'].newValue) {
this.logger.log('info', 'settings',"[Settings::<storage/on change>] new settings object:", JSON.parse(changes.uwSettings.newValue));
}
if(changes['uwSettings'] && changes['uwSettings'].newValue) {
ths.setActive(JSON.parse(changes.uwSettings.newValue));
}
if(this.updateCallback) {
try {
updateCallback(ths);
} catch (e) {
this.logger.log('error', 'settings', "[Settings] CALLING UPDATE CALLBACK FAILED.")
}
}
});
} else if (currentBrowser.chrome) { } else if (currentBrowser.chrome) {
chrome.storage.onChanged.addListener( (changes, area) => { chrome.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
this.logger.log('info', 'settings', "[Settings::<storage/on change>] Settings have been changed outside of here. Updating active settings. Changes:", changes, "storage area:", area); }
if (changes['uwSettings'] && changes['uwSettings'].newValue) { }
this.logger.log('info', 'settings',"[Settings::<storage/on change>] new settings object:", JSON.parse(changes.uwSettings.newValue));
}
if(changes['uwSettings'] && changes['uwSettings'].newValue) {
ths.setActive(JSON.parse(changes.uwSettings.newValue));
}
if(this.updateCallback) { storageChangeListener(changes, area) {
try { this.logger.log('info', 'settings', "[Settings::<storage/on change>] Settings have been changed outside of here. Updating active settings. Changes:", changes, "storage area:", area);
updateCallback(ths); if (changes['uwSettings'] && changes['uwSettings'].newValue) {
} catch (e) { this.logger.log('info', 'settings',"[Settings::<storage/on change>] new settings object:", JSON.parse(changes.uwSettings.newValue));
this.logger.log('error', 'settings',"[Settings] CALLING UPDATE CALLBACK FAILED.") }
} const parsedSettings = JSON.parse(changes.uwSettings.newValue);
} if(changes['uwSettings'] && changes['uwSettings'].newValue) {
}); this.setActive(parsedSettings);
}
if(!parsedSettings.preventReload && this.updateCallback) {
try {
updateCallback(ths);
} catch (e) {
this.logger.log('error', 'settings', "[Settings] CALLING UPDATE CALLBACK FAILED.")
}
} }
} }
@ -156,8 +144,23 @@ class Settings {
// apply all remaining patches // apply all remaining patches
this.logger.log('info', 'settings', `[Settings::applySettingsPatches] There are ${patches.length - index} settings patches to apply`); this.logger.log('info', 'settings', `[Settings::applySettingsPatches] There are ${patches.length - index} settings patches to apply`);
while (index < patches.length) { while (index < patches.length) {
const updateFn = patches[index].updateFn;
delete patches[index].forVersion; delete patches[index].forVersion;
ObjectCopy.overwrite(this.active, patches[index]); delete patches[index].updateFn;
if (Object.keys(patches[index]).length > 0) {
ObjectCopy.overwrite(this.active, patches[index]);
}
if (updateFn) {
try {
updateFn(this.active, this.getDefaultSettings());
} catch (e) {
console.log("!!!!", e)
this.logger.log('error', 'settings', '[Settings::applySettingsPatches] Failed to execute update function. Keeping settings object as-is. Error:', e);
}
}
index++; index++;
} }
} }
@ -171,22 +174,22 @@ class Settings {
// | needed. In this case, we assume we're on the current version // | needed. In this case, we assume we're on the current version
const oldVersion = (settings && settings.version) || this.version; const oldVersion = (settings && settings.version) || this.version;
if(Debug.debug) { if (settings) {
this.logger.log('info', 'settings', "[Settings::init] Configuration fetched from storage:", settings, this.logger.log('info', 'settings', "[Settings::init] Configuration fetched from storage:", settings,
"\nlast saved with:", settings.version, "\nlast saved with:", settings.version,
"\ncurrent version:", this.version "\ncurrent version:", this.version
); );
// if (Debug.flushStoredSettings) {
// this.logger.log('info', 'settings', "%c[Settings::init] Debug.flushStoredSettings is true. Using default settings", "background: #d00; color: #ffd");
// Debug.flushStoredSettings = false; // don't do it again this session
// this.active = this.getDefaultSettings();
// this.active.version = this.version;
// this.set(this.active);
// return this.active;
// }
} }
// if (Debug.flushStoredSettings) {
// this.logger.log('info', 'settings', "%c[Settings::init] Debug.flushStoredSettings is true. Using default settings", "background: #d00; color: #ffd");
// Debug.flushStoredSettings = false; // don't do it again this session
// this.active = this.getDefaultSettings();
// this.active.version = this.version;
// this.set(this.active);
// return this.active;
// }
// if there's no settings saved, return default settings. // if there's no settings saved, return default settings.
if(! settings || (Object.keys(settings).length === 0 && settings.constructor === Object)) { if(! settings || (Object.keys(settings).length === 0 && settings.constructor === Object)) {
this.logger.log( this.logger.log(
@ -279,8 +282,10 @@ class Settings {
} }
} }
async set(extensionConf) { async set(extensionConf, options) {
extensionConf.version = this.version; if (!options || !options.forcePreserveVersion) {
extensionConf.version = this.version;
}
this.logger.log('info', 'settings', "[Settings::set] setting new settings:", extensionConf) this.logger.log('info', 'settings', "[Settings::set] setting new settings:", extensionConf)
@ -299,11 +304,17 @@ class Settings {
this.active[prop] = value; this.active[prop] = value;
} }
async save() { async save(options) {
if (Debug.debug && Debug.storage) { if (Debug.debug && Debug.storage) {
console.log("[Settings::save] Saving active settings:", this.active); console.log("[Settings::save] Saving active settings:", this.active);
} }
this.active.preventReload = undefined;
await this.set(this.active, options);
}
async saveWithoutReload() {
this.active.preventReload = true;
await this.set(this.active); await this.set(this.active);
} }
@ -522,6 +533,15 @@ class Settings {
return this.active.sites['@global'].stretch; return this.active.sites['@global'].stretch;
} }
getDefaultCropPersistenceMode(site) {
if (site && this.active.sites[site] && this.active.sites[site].cropModePersistence !== Stretch.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) { getDefaultVideoAlignment(site) {
if (site && this.active.sites[site] && this.active.sites[site].videoAlignment !== VideoAlignment.Default) { if (site && this.active.sites[site] && this.active.sites[site].videoAlignment !== VideoAlignment.Default) {
return this.active.sites[site].videoAlignment; return this.active.sites[site].videoAlignment;

View File

@ -492,6 +492,13 @@ class ArDetector {
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: trueAr}, {type: AspectRatio.Automatic, ratio: trueAr}); this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: trueAr}, {type: AspectRatio.Automatic, ratio: trueAr});
} }
clearImageData(id) {
if (ArrayBuffer.transfer) {
ArrayBuffer.transfer(id, 0);
}
id = undefined;
}
frameCheck(){ frameCheck(){
if(! this.video){ if(! this.video){
this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`); this.logger.log('error', 'debug', `%c[ArDetect::frameCheck] <@${this.arid}> Video went missing. Destroying current instance of videoData.`);
@ -581,6 +588,7 @@ class ArDetector {
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Letterbox not detected in fast test. Letterbox is either gone or we manually corrected aspect ratio. Nothing will be done.`, "color: #fa3"); this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Letterbox not detected in fast test. Letterbox is either gone or we manually corrected aspect ratio. Nothing will be done.`, "color: #fa3");
this.clearImageData(imageData);
return; return;
} }
@ -598,6 +606,7 @@ class ArDetector {
// if both succeed, then aspect ratio hasn't changed. // if both succeed, then aspect ratio hasn't changed.
if (!guardLineOut.imageFail && !guardLineOut.blackbarFail) { if (!guardLineOut.imageFail && !guardLineOut.blackbarFail) {
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] guardLine tests were successful. (no imagefail and no blackbarfail)\n`, "color: #afa", guardLineOut); this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] guardLine tests were successful. (no imagefail and no blackbarfail)\n`, "color: #afa", guardLineOut);
this.clearImageData(imageData);
return; return;
} }
@ -617,6 +626,7 @@ class ArDetector {
this.guardLine.reset(); this.guardLine.reset();
this.noLetterboxCanvasReset = true; this.noLetterboxCanvasReset = true;
this.clearImageData(imageData);
return; return;
} }
@ -643,6 +653,7 @@ class ArDetector {
triggerTimeout = this.getTimeout(baseTimeout, startTime); triggerTimeout = this.getTimeout(baseTimeout, startTime);
this.scheduleFrameCheck(triggerTimeout); this.scheduleFrameCheck(triggerTimeout);
this.clearImageData(imageData);
return; return;
} }
} }
@ -664,6 +675,8 @@ class ArDetector {
// rob ni bil zaznan, zato ne naredimo ničesar. // rob ni bil zaznan, zato ne naredimo ničesar.
// no edge was detected. Let's leave things as they were // no edge was detected. Let's leave things as they were
this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Edge wasn't detected with findBars`, "color: #fa3", edgePost, "EdgeStatus.AR_KNOWN:", EdgeStatus.AR_KNOWN); this.logger.log('info', 'arDetect_verbose', `%c[ArDetect::frameCheck] Edge wasn't detected with findBars`, "color: #fa3", edgePost, "EdgeStatus.AR_KNOWN:", EdgeStatus.AR_KNOWN);
this.clearImageData(imageData);
return; return;
} }
@ -696,14 +709,16 @@ class ArDetector {
} catch (e) { } catch (e) {
// edges weren't gucci, so we'll just reset // edges weren't gucci, so we'll just reset
// the aspect ratio to defaults // the aspect ratio to defaults
try { this.logger.log('error', 'arDetect', `%c[ArDetect::frameCheck] There was a problem setting blackbar. Doing nothing. Error:`, e);
this.guardline.reset();
} catch (e) { this.guardline.reset();
// guardline wasn't gucci either, but we'll just ignore // WE DO NOT RESET ASPECT RATIO HERE IN CASE OF PROBLEMS, CAUSES UNWARRANTED RESETS:
// that and reset the aspect ratio anyway // (eg. here: https://www.youtube.com/watch?v=nw5Z93Yt-UQ&t=410)
} //
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()}); // this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
} }
this.clearImageData(imageData);
} }
resetBlackLevel(){ resetBlackLevel(){

View File

@ -34,7 +34,7 @@ class GuardLine {
// to odstrani vse neveljavne nastavitve in vse možnosti, ki niso smiselne // to odstrani vse neveljavne nastavitve in vse možnosti, ki niso smiselne
// this removes any configs with invalid values or values that dont make sense // this removes any configs with invalid values or values that dont make sense
if (bbTop < 0 || bbBottom >= this.conf.canvas.height ){ if (bbTop < 0 || bbBottom >= this.conf.canvas.height ){
throw "INVALID_SETTINGS_IN_GUARDLINE" throw {error: "INVALID_SETTINGS_IN_GUARDLINE", bbTop, bbBottom}
} }
this.blackbar = { this.blackbar = {

View File

@ -92,6 +92,8 @@ class CommsClient {
this.pageInfo.setManualTick(message.arg); this.pageInfo.setManualTick(message.arg);
} else if (message.cmd === 'autoar-tick') { } else if (message.cmd === 'autoar-tick') {
this.pageInfo.tick(); this.pageInfo.tick();
} else if (message.cmd === 'set-ar-persistence') {
this.pageInfo.setArPersistence(message.arg);
} }
} }

View File

@ -2,6 +2,7 @@ import Debug from '../../conf/Debug';
import VideoData from './VideoData'; import VideoData from './VideoData';
import RescanReason from './enums/RescanReason'; import RescanReason from './enums/RescanReason';
import AspectRatio from '../../../common/enums/aspect-ratio.enum'; import AspectRatio from '../../../common/enums/aspect-ratio.enum';
import CropModePersistence from '../../../common/enums/crop-mode-persistence.enum';
if(Debug.debug) if(Debug.debug)
console.log("Loading: PageInfo.js"); console.log("Loading: PageInfo.js");
@ -20,23 +21,38 @@ class PageInfo {
this.lastUrl = window.location.href; this.lastUrl = window.location.href;
this.extensionMode = extensionMode; this.extensionMode = extensionMode;
this.readOnly = readOnly; this.readOnly = readOnly;
this.defaultCrop = undefined;
this.currentCrop = undefined;
if (comms){ if (comms){
this.comms = comms; this.comms = comms;
} }
// request inject css immediately
try { try {
// request inject css immediately
const playerStyleString = this.settings.active.sites[window.location.host].css.replace('\\n', ''); const playerStyleString = this.settings.active.sites[window.location.host].css.replace('\\n', '');
this.comms.sendMessage({ this.comms.sendMessage({
cmd: 'inject-css', cmd: 'inject-css',
cssString: playerStyleString cssString: playerStyleString
}); });
} catch (e) { } catch (e) {
// do nothing. It's ok if there's no special settings for the player element // do nothing. It's ok if there's no special settings for the player element or crop persistence
} }
// try getting default crop immediately.
const cropModePersistence = this.settings.getDefaultCropPersistenceMode(window.location.host);
// try {
// if (cropModePersistence === CropModePersistence.Forever) {
// this.defaultCrop = this.settings.active.sites[window.location.host].defaultCrop;
// } else if (cropModePersistence === CropModePersistence.CurrentSession) {
// this.defaultCrop = JSON.parse(sessionStorage.getItem('uw-crop-mode-session-persistence'));
// }
// } catch (e) {
// // do nothing. It's ok if there's no special settings for the player element or crop persistence
// }
this.currentCrop = this.defaultCrop;
this.rescan(RescanReason.PERIODIC); this.rescan(RescanReason.PERIODIC);
this.scheduleUrlCheck(); this.scheduleUrlCheck();
@ -197,11 +213,17 @@ class PageInfo {
try { try {
v = new VideoData(video, this.settings, this); v = new VideoData(video, this.settings, this);
if (!v.invalid) {
v.initArDetection(); if (!this.defaultCrop) {
if (!v.invalid) {
v.initArDetection();
} else {
this.logger.log('error', 'debug', 'Video is invalid. Aard not started.', video);
}
} else { } else {
this.logger.log('error', 'debug', 'Video is invalid. Aard not started.', video); this.logger.log('info', 'debug', 'Default crop is specified for this site. Not starting aard.');
} }
this.videos.push(v); this.videos.push(v);
} catch (e) { } catch (e) {
this.logger.log('error', 'debug', "rescan error: failed to initialize videoData. Skipping this video.",e); this.logger.log('error', 'debug', "rescan error: failed to initialize videoData. Skipping this video.",e);
@ -216,7 +238,7 @@ class PageInfo {
// če smo ostali brez videev, potem odregistriraj stran. // če smo ostali brez videev, potem odregistriraj stran.
// če nismo ostali brez videev, potem registriraj stran. // če nismo ostali brez videev, potem registriraj stran.
// //
// if we're left withotu videos on the current page, we unregister the page. // if we're left without videos on the current page, we unregister the page.
// if we have videos, we call register. // if we have videos, we call register.
if (this.comms) { if (this.comms) {
if (this.videos.length != oldVideoCount) { // only if number of videos changed, tho if (this.videos.length != oldVideoCount) { // only if number of videos changed, tho
@ -551,6 +573,53 @@ class PageInfo {
setKeyboardShortcutsEnabled(state) { setKeyboardShortcutsEnabled(state) {
this.actionHandler.setKeybordLocal(state); this.actionHandler.setKeybordLocal(state);
} }
setArPersistence(persistenceMode) {
// name of this function is mildly misleading — we don't really _set_ ar persistence. (Ar persistence
// mode is set and saved via popup or keyboard shortcuts, if user defined them) We just save the current
// aspect ratio whenever aspect ratio persistence mode changes.
if (persistenceMode === CropModePersistence.CurrentSession) {
sessionStorage.setItem('uw-crop-mode-session-persistence', JSON.stringify(this.currentCrop));
} else if (persistenceMode === CropModePersistence.Forever) {
if (this.settings.active.sites[window.location.host]) {
// | key may be missing, so we do this
this.settings.active.sites[window.location.host]['defaultAr'] = this.currentCrop;
} else {
this.settings.active.sites[window.location.host] = this.settings.getDefaultOption();
this.settings.active.sites[window.location.host]['defaultAr'] = this.currentCrop;
}
this.settings.saveWithoutReload();
}
}
updateCurrentCrop(ar) {
this.currentCrop = ar;
// This means crop persistance is disabled. If crop persistance is enabled, then settings for current
// site MUST exist (crop persistence mode is disabled by default)
const cropModePersistence = this.settings.getDefaultCropPersistenceMode(window.location.host);
if (cropModePersistence === CropModePersistence.Disabled) {
return;
}
this.defaultCrop = ar;
if (cropModePersistence === CropModePersistence.CurrentSession) {
sessionStorage.setItem('uw-crop-mode-session-persistence', JSON.stringify(ar));
} else if (cropModePersistence === CropModePersistence.Forever) {
if (this.settings.active.sites[window.location.host]) {
// | key may be missing, so we do this
this.settings.active.sites[window.location.host]['defaultAr'] = ar;
} else {
this.settings.active.sites[window.location.host] = this.settings.getDefaultOption();
this.settings.active.sites[window.location.host]['defaultAr'] = ar;
}
this.settings.saveWithoutReload();
}
}
} }
export default PageInfo; export default PageInfo;

View File

@ -44,6 +44,7 @@ class VideoData {
}; };
this.resizer = new Resizer(this); this.resizer = new Resizer(this);
this.arDetector = new ArDetector(this); // this starts Ar detection. needs optional parameter that prevets ardetdctor from starting this.arDetector = new ArDetector(this); // this starts Ar detection. needs optional parameter that prevets ardetdctor from starting
// player dimensions need to be in: // player dimensions need to be in:
// this.player.dimensions // this.player.dimensions
@ -59,6 +60,11 @@ class VideoData {
// start fallback video/player size detection // start fallback video/player size detection
this.fallbackChangeDetection(); this.fallbackChangeDetection();
// force reload last aspect ratio (if default crop ratio exists)
if (this.pageInfo.defaultCrop) {
this.resizer.setAr(this.pageInfo.defaultCrop);
}
} }
async fallbackChangeDetection() { async fallbackChangeDetection() {
@ -80,21 +86,33 @@ class VideoData {
} }
return; return;
} }
for (let mutation of mutationList) { let confirmAspectRatioRestore = false;
if (mutation.type === 'attributes'
&& mutation.attributeName === 'class'
&& !context.video.classList.contains(this.userCssClassName) ) {
// force the page to include our class in classlist, if the classlist has been removed
// while classList.add() doesn't duplicate classes (does nothing if class is already added),
// we still only need to make sure we're only adding our class to classlist if it has been
// removed. classList.add() will _still_ trigger mutation (even if classlist wouldn't change).
// This is a problem because INFINITE RECURSION TIME, and we _really_ don't want that.
context.video.classList.add(this.userCssClassName); for (let mutation of mutationList) {
break; if (mutation.type === 'attributes') {
if (mutation.attributeName === 'class') {
if(!context.video.classList.contains(this.userCssClassName) ) {
// force the page to include our class in classlist, if the classlist has been removed
// while classList.add() doesn't duplicate classes (does nothing if class is already added),
// we still only need to make sure we're only adding our class to classlist if it has been
// removed. classList.add() will _still_ trigger mutation (even if classlist wouldn't change).
// This is a problem because INFINITE RECURSION TIME, and we _really_ don't want that.
context.video.classList.add(this.userCssClassName);
}
// always trigger refresh on class changes, since change of classname might trigger change
// of the player size as well.
confirmAspectRatioRestore = true;
}
if (mutation.attributeName === 'style') {
confirmAspectRatioRestore = true;
}
} }
} }
if (!confirmAspectRatioRestore) {
return;
}
// adding player observer taught us that if element size gets triggered by a class, then // adding player observer taught us that if element size gets triggered by a class, then
// the 'style' attributes don't necessarily trigger. This means we also need to trigger // the 'style' attributes don't necessarily trigger. This means we also need to trigger
// restoreAr here, in case video size was changed this way // restoreAr here, in case video size was changed this way
@ -131,8 +149,9 @@ class VideoData {
const pw = +(pcs.width.split('px')[0]); const pw = +(pcs.width.split('px')[0]);
// TODO: check & account for panning and alignment // TODO: check & account for panning and alignment
if (this.isWithin(vh, (ph - (translateY / 2)), 2) if (transformMatrix[0] !== 'none'
&& this.isWithin(vw, (pw - (translateX / 2)), 2)) { && this.isWithin(vh, (ph - (translateY * 2)), 2)
&& this.isWithin(vw, (pw - (translateX * 2)), 2)) {
} else { } else {
this.player.forceRefreshPlayerElement(); this.player.forceRefreshPlayerElement();
this.restoreAr(); this.restoreAr();
@ -162,7 +181,7 @@ class VideoData {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}}; // throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return; return;
} }
if(this.arDetector){ if (this.arDetector){
this.arDetector.init(); this.arDetector.init();
} }
else{ else{
@ -177,8 +196,8 @@ class VideoData {
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}}; // throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
return; return;
} }
if(!this.arDetector) { if (!this.arDetector) {
this.arDetector.init(); this.initArDetection();
} }
this.arDetector.start(); this.arDetector.start();
} }

View File

@ -7,6 +7,7 @@ import ExtensionMode from '../../../common/enums/extension-mode.enum';
import Stretch from '../../../common/enums/stretch.enum'; import Stretch from '../../../common/enums/stretch.enum';
import VideoAlignment from '../../../common/enums/video-alignment.enum'; import VideoAlignment from '../../../common/enums/video-alignment.enum';
import AspectRatio from '../../../common/enums/aspect-ratio.enum'; import AspectRatio from '../../../common/enums/aspect-ratio.enum';
import CropModePersistance from '../../../common/enums/crop-mode-persistence.enum';
if(Debug.debug) { if(Debug.debug) {
console.log("Loading: Resizer.js"); console.log("Loading: Resizer.js");
@ -119,7 +120,7 @@ class Resizer {
} }
setAr(ar, lastAr){ setAr(ar, lastAr) {
if (this.destroyed) { if (this.destroyed) {
return; return;
} }
@ -130,12 +131,36 @@ class Resizer {
return; return;
} }
const siteSettings = this.settings.active.sites[window.location.host];
// reset zoom, but only on aspect ratio switch. We also know that aspect ratio gets converted to
// AspectRatio.Fixed when zooming, so let's keep that in mind
if (ar.type !== AspectRatio.Fixed) {
this.zoom.reset();
this.resetPan();
} else if (ar.ratio !== this.lastAr.ratio) {
// we must check against this.lastAR.ratio because some calls provide same value for ar and lastAr
this.zoom.reset();
this.resetPan();
}
// most everything that could go wrong went wrong by this stage, and returns can happen afterwards
// 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 === AspectRatio.Automatic ||
ar.type === AspectRatio.Reset ||
ar.type === AspectRatio.Initial ) {
// reset/undo default
this.conf.pageInfo.updateCurrentCrop(undefined);
} else {
this.conf.pageInfo.updateCurrentCrop(ar);
}
if (ar.type === AspectRatio.Automatic || if (ar.type === AspectRatio.Automatic ||
ar.type === AspectRatio.Reset && this.lastAr.type === AspectRatio.Initial) { ar.type === AspectRatio.Reset && this.lastAr.type === AspectRatio.Initial) {
// some sites do things that interfere with our site (and aspect ratio setting in general) // some sites do things that interfere with our site (and aspect ratio setting in general)
// first, we check whether video contains anything we don't like // first, we check whether video contains anything we don't like
const siteSettings = this.settings.active.sites[window.location.host];
if (siteSettings && siteSettings.autoarPreventConditions) { if (siteSettings && siteSettings.autoarPreventConditions) {
if (siteSettings.autoarPreventConditions.videoStyleString) { if (siteSettings.autoarPreventConditions.videoStyleString) {
const styleString = (this.video.getAttribute('style') || '').split(';'); const styleString = (this.video.getAttribute('style') || '').split(';');
@ -170,6 +195,8 @@ class Resizer {
} }
} }
if (lastAr) { if (lastAr) {
this.lastAr = this.calculateRatioForLegacyOptions(lastAr); this.lastAr = this.calculateRatioForLegacyOptions(lastAr);
ar = this.calculateRatioForLegacyOptions(ar); ar = this.calculateRatioForLegacyOptions(ar);
@ -247,6 +274,10 @@ class Resizer {
} }
toFixedAr() {
this.lastAr.type = AspectRatio.Fixed;
}
resetLastAr() { resetLastAr() {
this.lastAr = {type: AspectRatio.Initial}; this.lastAr = {type: AspectRatio.Initial};
} }
@ -272,6 +303,9 @@ class Resizer {
// dont allow weird floats // dont allow weird floats
this.videoAlignment = VideoAlignment.Center; this.videoAlignment = VideoAlignment.Center;
// because non-fixed aspect ratios reset panning:
this.toFixedAr();
const player = this.conf.player.element; const player = this.conf.player.element;
const relativeX = (event.pageX - player.offsetLeft) / player.offsetWidth; const relativeX = (event.pageX - player.offsetLeft) / player.offsetWidth;
@ -283,6 +317,11 @@ class Resizer {
} }
} }
resetPan() {
this.pan = {};
this.videoAlignment = this.settings.getDefaultVideoAlignment(window.location.host);
}
setPan(relativeMousePosX, relativeMousePosY){ setPan(relativeMousePosX, relativeMousePosY){
// relativeMousePos[X|Y] - on scale from 0 to 1, how close is the mouse to player edges. // relativeMousePos[X|Y] - on scale from 0 to 1, how close is the mouse to player edges.
// use these values: top, left: 0, bottom, right: 1 // use these values: top, left: 0, bottom, right: 1

View File

@ -120,14 +120,31 @@ class Scaler {
actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height
} }
if (fileAr < ar.ratio){ if (fileAr < playerAr) {
// imamo letterbox zgoraj in spodaj -> spremenimo velikost videa (a nikoli širše od ekrana) if (fileAr < ar.ratio){
// letterbox -> change video size (but never to wider than monitor width) // in this situation we have to crop letterbox on top/bottom of the player
// we cut it, but never more than the player
videoDimensions.xFactor = Math.min(ar.ratio, playerAr) / fileAr; videoDimensions.xFactor = Math.min(ar.ratio, playerAr) / fileAr;
videoDimensions.yFactor = videoDimensions.xFactor; videoDimensions.yFactor = videoDimensions.xFactor;
} else {
// in this situation, we would be cutting pillarbox. Inside horizontal player.
// I don't think so. Except exceptions, we'll wait for bug reports.
videoDimensions.xFactor = 1;
videoDimensions.yFactor = 1;
}
} else { } else {
if (fileAr < ar.ratio || playerAr < ar.ratio){
// in this situation, we need to add extra letterbox on top of our letterbox
// this means we simply don't crop anything _at all_
videoDimensions.xFactor = 1;
videoDimensions.yFactor = 1;
} else {
// meant for handling pillarbox crop. not quite implemented.
videoDimensions.xFactor = fileAr / Math.min(ar.ratio, playerAr); videoDimensions.xFactor = fileAr / Math.min(ar.ratio, playerAr);
videoDimensions.yFactor = videoDimensions.xFactor; videoDimensions.yFactor = videoDimensions.xFactor;
// videoDimensions.xFactor = Math.max(ar.ratio, playerAr) * fileAr;
// videoDimensions.yFactor = videoDimensions.xFactor;
}
} }
this.logger.log('info', 'scaler', "[Scaler::calculateCrop] Crop factor calculated — ", videoDimensions.xFactor); this.logger.log('info', 'scaler', "[Scaler::calculateCrop] Crop factor calculated — ", videoDimensions.xFactor);

View File

@ -108,7 +108,7 @@ class Stretcher {
yFactor: 1 yFactor: 1
}; };
if(playerAr >= videoAr){ if (playerAr >= videoAr){
// player adds PILLARBOX // player adds PILLARBOX
if(actualAr >= playerAr){ if(actualAr >= playerAr){

View File

@ -18,6 +18,7 @@ class Zoom {
reset(){ reset(){
this.scale = 1; this.scale = 1;
this.logScale = 0;
} }
zoomStep(amount){ zoomStep(amount){
@ -34,6 +35,8 @@ class Zoom {
this.logger.log('info', 'debug', "[Zoom::zoomStep] changing zoom by", amount, ". New zoom level:", this.scale); this.logger.log('info', 'debug', "[Zoom::zoomStep] changing zoom by", amount, ". New zoom level:", this.scale);
this.conf.resizer.toFixedAr();
this.conf.restoreAr(); this.conf.restoreAr();
this.conf.announceZoom(this.scale); this.conf.announceZoom(this.scale);
} }

View File

@ -44,26 +44,26 @@ class UW {
if (!this.logger) { if (!this.logger) {
const loggingOptions = { const loggingOptions = {
logToFile: false, logToFile: false,
logToConsole: false, logToConsole: true,
fileOptions: { fileOptions: {
// really the same stuff as consoleOptions // really the same stuff as consoleOptions
}, },
consoleOptions: { consoleOptions: {
enabled: true, // if logging is enabled at all enabled: true, // if logging is enabled at all
'debug': true, // 'debug': true,
'init': true, // 'init': true,
'settings': true, // 'settings': true,
'keyboard': false, // 'keyboard': true,
'mousemove': false, // 'mousemove': false,
'actionHandler': false, // 'actionHandler': false,
'comms': false, // 'comms': false,
'playerDetect': false, // 'playerDetect': false,
// 'resizer': true, // 'resizer': true,
// 'scaler': true, // 'scaler': true,
// 'stretcher': true, // 'stretcher': true,
'videoRescan': false, // 'videoRescan': false,
'arDetect': false, // 'arDetect': true,
'arDetect_verbose': false, // 'arDetect_verbose': true,
} }
}; };
this.logger = new Logger(loggingOptions); this.logger = new Logger(loggingOptions);

View File

@ -2,7 +2,7 @@
"manifest_version": 2, "manifest_version": 2,
"name": "Ultrawidify", "name": "Ultrawidify",
"description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.", "description": "Removes black bars on ultrawide videos and offers advanced options to fix aspect ratio.",
"version": "4.3.1.1", "version": "4.4.0",
"applications": { "applications": {
"gecko": { "gecko": {
"id": "{cf02b1a7-a01a-4e37-a609-516a283f1ed3}" "id": "{cf02b1a7-a01a-4e37-a609-516a283f1ed3}"

View File

@ -70,7 +70,7 @@ export default {
if (this.hasError) { if (this.hasError) {
return; return;
} }
this.settings.save(); this.settings.save({forcePreserveVersion: true});
// this.parsedSettings = JSON.stringify(this.settings.active, null, 2); // this.parsedSettings = JSON.stringify(this.settings.active, null, 2);
// this.lastSettings = JSON.parse(JSON.stringify(this.settings.active)); // this.lastSettings = JSON.parse(JSON.stringify(this.settings.active));
const ths = this; const ths = this;

View File

@ -39,12 +39,11 @@
</template> </template>
<script> <script>
import SetShortcutButton from './SetShortcutButton.vue'; import SetShortcutButton from './SetShortcutButton';
export default { export default {
components: { components: {
SetShortcutButton SetShortcutButton,
}, },
props: { props: {
scopeOptions: Object, scopeOptions: Object,

View File

@ -7,7 +7,7 @@
@focus="initiateKeypress" @focus="initiateKeypress"
@keyup="processKeyup" @keyup="processKeyup"
/> />
<span v-if="shortcut" @click="$emit('set-shortcut')">(Clear shortcut)</span> <span v-if="shortcut" @click="clearShortcut()">(Clear shortcut)</span>
</div> </div>
</template> </template>
@ -38,6 +38,7 @@ export default {
if (this.waitingForPress) { if (this.waitingForPress) {
const shortcut = { const shortcut = {
key: event.key, key: event.key,
code: event.code,
ctrlKey: event.ctrlKey, ctrlKey: event.ctrlKey,
metaKey: event.metaKey, metaKey: event.metaKey,
altKey: event.altKey, altKey: event.altKey,
@ -50,6 +51,11 @@ export default {
this.shortcutText = KeyboardShortcutParser.parseShortcut(shortcut); this.shortcutText = KeyboardShortcutParser.parseShortcut(shortcut);
} }
this.waitingForPress = false; this.waitingForPress = false;
},
clearShortcut() {
this.shortcutText = '[click to add shortcut]';
this.shortcut = undefined;
this.$emit('set-shortcut');
} }
} }
} }

View File

@ -13,7 +13,7 @@ class ExecAction {
this.site = site; this.site = site;
} }
exec(action, scope, frame) { async exec(action, scope, frame) {
for (var cmd of action.cmd) { for (var cmd of action.cmd) {
if (scope === 'page') { if (scope === 'page') {
const message = { const message = {
@ -27,6 +27,23 @@ class ExecAction {
Comms.sendMessage(message); Comms.sendMessage(message);
} else { } 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
await Comms.sendMessage(message);
}
let site = this.site; let site = this.site;
if (scope === 'global') { if (scope === 'global') {
site = '@global'; site = '@global';
@ -48,8 +65,14 @@ class ExecAction {
this.settings.active.sites[site].autoar = cmd.arg; this.settings.active.sites[site].autoar = cmd.arg;
} else if (cmd.action === 'set-keyboard') { } else if (cmd.action === 'set-keyboard') {
this.settings.active.sites[site].keyboardShortcutsEnabled = cmd.arg; 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();
} }
this.settings.save();
} }
} }
} }

View File

@ -42,9 +42,27 @@
</div> </div>
</div> </div>
<!-- CROP MODE PERSISTENCE -->
<div v-if="cropModePersistenceActions.length"
class="w100"
>
<div class="label">Persists crop mode <template v-if="scope === 'site'">for {{site}}</template>:</div>
<div class="flex flex-row flex-wrap">
<ShortcutButton v-for="(action, index) of cropModePersistenceActions"
class="flex flex-grow button b3"
:class="{'setting-selected': getCurrent('cropModePersistence') === action.cmd[0].arg}"
:key="index"
:label="(action.scopes[scope] && action.scopes[scope].label) ? action.scopes[scope].label : action.label"
:shortcut="parseShortcut(action)"
@click.native="execAction(action)"
>
</ShortcutButton>
</div>
</div>
<!-- DEFAULT SETTINGS --> <!-- DEFAULT SETTINGS -->
<div v-if="stretchActions.length"> <div v-if="stretchActions.length">
<div class="label">Default stretching mode:</div> <div class="label experimental">Default stretching mode:</div>
<div class="flex flex-row flex-wrap"> <div class="flex flex-row flex-wrap">
<ShortcutButton v-for="(action, index) of stretchActions" <ShortcutButton v-for="(action, index) of stretchActions"
class="flex b3 flex-grow button" class="flex b3 flex-grow button"

View File

@ -5,6 +5,13 @@
</div> </div>
<div v-if="aspectRatioActions.length"> <div v-if="aspectRatioActions.length">
<div class="label">Cropping mode:</div> <div class="label">Cropping mode:</div>
<div v-if="cropModePersistence && cropModePersistence > CropModePersistence.Disabled" class="info">
Cropping mode will persist
<template v-if="cropModePersistence === CropModePersistence.UntilPageReload">until page reload (this includes page navigation!).</template>
<template v-if="cropModePersistence === CropModePersistence.CurrentSession">for current session.</template>
<template v-if="cropModePersistence === CropModePersistence.Forever">forever (or at least until you change aspect ratio manually).</template>
This can be changed in the 'site settings' or 'extension settings' tab.
</div>
<div class="flex flex-row flex-wrap"> <div class="flex flex-row flex-wrap">
<ShortcutButton v-for="(action, index) of aspectRatioActions" <ShortcutButton v-for="(action, index) of aspectRatioActions"
class="flex b3 flex-grow button" class="flex b3 flex-grow button"
@ -12,7 +19,7 @@
:label="(action.scopes.page && action.scopes.page.label) ? action.scopes.page.label : action.label" :label="(action.scopes.page && action.scopes.page.label) ? action.scopes.page.label : action.label"
:shortcut="parseShortcut(action)" :shortcut="parseShortcut(action)"
@click.native="execAction(action)" @click.native="execAction(action)"
> >
</ShortcutButton> </ShortcutButton>
</div> </div>
</div> </div>
@ -117,11 +124,13 @@ import ExecAction from '../js/ExecAction';
import KeyboardShortcutParser from '../../common/js/KeyboardShortcutParser'; import KeyboardShortcutParser from '../../common/js/KeyboardShortcutParser';
import ShortcutButton from '../../common/components/ShortcutButton'; import ShortcutButton from '../../common/components/ShortcutButton';
import ComputeActionsMixin from '../../common/mixins/ComputeActionsMixin'; import ComputeActionsMixin from '../../common/mixins/ComputeActionsMixin';
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
export default { export default {
data() { data() {
return { return {
scope: 'page', scope: 'page',
CropModePersistence: CropModePersistence,
} }
}, },
mixins: [ mixins: [
@ -131,7 +140,8 @@ export default {
'settings', 'settings',
'frame', 'frame',
'zoom', 'zoom',
'someSitesDisabledWarning' 'someSitesDisabledWarning',
'cropModePersistence',
], ],
created() { created() {
this.exec = new ExecAction(this.settings); this.exec = new ExecAction(this.settings);

View File

@ -2,11 +2,17 @@
<div> <div>
<h2>What's new</h2> <h2>What's new</h2>
<p>Full changelog for older versions <a href="https://github.com/xternal7/ultrawidify/blob/master/CHANGELOG.md">is available here</a>.</p> <p>Full changelog for older versions <a href="https://github.com/xternal7/ultrawidify/blob/master/CHANGELOG.md">is available here</a>.</p>
<p class="label">4.3.1.1</p> <p class="label">4.4.0</p>
<ul> <ul>
<li><b>[4.3.3.1]</b> Patched twitch.tv</li> <li>Russian users (and users of other non-latin keyboard layouts) can now use keyboard shortcuts by default,
<li>Minor rework of settings page (actions & shortcuts section)</li> without having to rebind them manually. <b>NOTE: this change will only be applied to users who have <i>NOT</i>
<li>Fixed bug that prevented settings page from opening</li> modified their keyboard shortcuts.</b></li>
<li>NOTE: when using non-latin layouts, 'zoom' shortcut (`z` by default) uses the position of 'Y' on QWERTY layout.</li>
<li>Ability to preserve aspect ratio between different videos (applies to current page and doesn't survive proper
page reloads)</li>
<li>Changing aspect ratio now resets zooming and panning.</li>
<li>Fixed bug where keyboard shortcuts would work while typing in certain text fields</li>
<li>Fixed a minor bug with autodetection</li>
</ul> </ul>
</div> </div>
</template> </template>

View File

@ -1,6 +1,6 @@
# List of test/sample videos # List of test/sample videos
A quick list of videos where letterbox is encoded in the file. A quick list of videos where letterbox is encoded in the file. Inclusion of a video on this list does not imply endorsement of the content expressed by the video or the views expressed by its creator.
## Vimeo: ## Vimeo:
@ -34,11 +34,13 @@ https://www.youtube.com/watch?v=L_u97PqWX6g (also dark at the start)
https://www.youtube.com/watch?v=Xr9Oubxw1gA (triggers autocorrection every now and then) https://www.youtube.com/watch?v=Xr9Oubxw1gA (triggers autocorrection every now and then)
https://www.youtube.com/watch?v=NaTGwlfRB_c (dark, triggers minor corrections) https://www.youtube.com/watch?v=NaTGwlfRB_c (dark, triggers minor corrections)
https://www.youtube.com/watch?v=nw5Z93Yt-UQ&t=410 (@ts, dark, resets aspect ratio for no reason within 15s)
### Watermark stopping AR ### Watermark stopping AR
https://www.youtube.com/watch?v=tXTFdDrd7pA https://www.youtube.com/watch?v=tXTFdDrd7pA
### HARD MODE ### HARD MODE
For situations that would be _really_ hard to fix (if fix is even possible) For situations that would be _really_ hard to fix (if fix is even possible)