Merge branch 'master' into stable
This commit is contained in:
commit
b6ff740b84
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -5,10 +5,12 @@
|
||||
"blackbar",
|
||||
"blackframe",
|
||||
"canvas",
|
||||
"comms",
|
||||
"equalish",
|
||||
"insta",
|
||||
"recursing",
|
||||
"reddit",
|
||||
"rescan",
|
||||
"resizer",
|
||||
"textbox",
|
||||
"videodata",
|
||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -12,7 +12,16 @@ QoL improvements for me:
|
||||
|
||||
* 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)
|
||||
* Fixed bug that prevented settings page from opening
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"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.",
|
||||
"author": "Tamius Han <tamius.han@gmail.com>",
|
||||
"scripts": {
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
</div>
|
||||
<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>
|
||||
<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>
|
||||
<span class="icon" @click="editAction()">🖉</span>
|
||||
{{action.name}}
|
||||
|
9
src/common/enums/crop-mode-persistence.enum.js
Normal file
9
src/common/enums/crop-mode-persistence.enum.js
Normal file
@ -0,0 +1,9 @@
|
||||
var CropModePersistence = Object.freeze({
|
||||
Default: -1,
|
||||
Disabled: 0,
|
||||
UntilPageReload: 1,
|
||||
CurrentSession: 2,
|
||||
Forever: 3,
|
||||
});
|
||||
|
||||
export default CropModePersistence;
|
@ -1,6 +1,6 @@
|
||||
class KeyboardShortcutParser {
|
||||
static parseShortcut(keypress) {
|
||||
var shortcutCombo = '';
|
||||
let shortcutCombo = '';
|
||||
|
||||
if (keypress.ctrlKey) {
|
||||
shortcutCombo += 'Ctrl + ';
|
||||
|
@ -1,7 +1,13 @@
|
||||
export default {
|
||||
computed: {
|
||||
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(){
|
||||
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-extension-mode') || [];
|
||||
@ -12,6 +18,9 @@ export default {
|
||||
aspectRatioActions: function(){
|
||||
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(){
|
||||
return this.scopeActions.filter(x => x.cmd.length === 1 && x.cmd[0].action === 'set-stretch') || [];
|
||||
},
|
||||
|
@ -2,6 +2,7 @@ import VideoAlignment from '../../common/enums/video-alignment.enum';
|
||||
import Stretch from '../../common/enums/stretch.enum';
|
||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
||||
import AspectRatio from '../../common/enums/aspect-ratio.enum';
|
||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
||||
|
||||
var ActionList = {
|
||||
'set-ar': {
|
||||
@ -30,6 +31,33 @@ var ActionList = {
|
||||
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': {
|
||||
name: 'Set stretch',
|
||||
args: [{
|
||||
|
@ -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;
|
@ -5,6 +5,7 @@ import Stretch from '../../common/enums/stretch.enum';
|
||||
import ExtensionMode from '../../common/enums/extension-mode.enum';
|
||||
import AntiGradientMode from '../../common/enums/anti-gradient-mode.enum';
|
||||
import AspectRatio from '../../common/enums/aspect-ratio.enum';
|
||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
||||
|
||||
if(Debug.debug)
|
||||
console.log("Loading: ExtensionConf.js");
|
||||
@ -191,6 +192,7 @@ var ExtensionConf = {
|
||||
label: 'Automatic', // example override, takes precedence over default label
|
||||
shortcut: [{
|
||||
key: 'a',
|
||||
code: 'KeyA',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -204,7 +206,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'crop',
|
||||
},
|
||||
},{
|
||||
}, {
|
||||
name: 'Reset to default',
|
||||
label: 'Reset',
|
||||
cmd: [{
|
||||
@ -216,6 +218,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
shortcut: [{
|
||||
key: 'r',
|
||||
code: 'KeyR',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -229,7 +232,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'crop'
|
||||
},
|
||||
},{
|
||||
}, {
|
||||
name: 'Fit to width',
|
||||
label: 'Fit width',
|
||||
cmd: [{
|
||||
@ -241,6 +244,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
shortcut: [{
|
||||
key: 'w',
|
||||
code: 'KeyW',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -254,7 +258,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'crop'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Fit to height',
|
||||
label: 'Fit height',
|
||||
cmd: [{
|
||||
@ -266,6 +270,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
shortcut: [{
|
||||
key: 'e',
|
||||
code: 'KeyE',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -279,7 +284,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'crop'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Set aspect ratio to 16:9',
|
||||
label: '16:9',
|
||||
cmd: [{
|
||||
@ -292,6 +297,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
shortcut: [{
|
||||
key: 's',
|
||||
code: 'KeyS',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -305,7 +311,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'crop'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Set aspect ratio to 21:9 (2.39:1)',
|
||||
label: '21:9',
|
||||
cmd: [{
|
||||
@ -318,6 +324,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
shortcut: [{
|
||||
key: 'd',
|
||||
code: 'KeyD',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -331,7 +338,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'crop'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Set aspect ratio to 18:9',
|
||||
label: '18:9',
|
||||
cmd: [{
|
||||
@ -344,6 +351,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
shortcut: [{
|
||||
key: 'x',
|
||||
code: 'KeyX',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -357,7 +365,94 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
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',
|
||||
label: 'Zoom',
|
||||
cmd: [{
|
||||
@ -369,6 +464,7 @@ var ExtensionConf = {
|
||||
show: false,
|
||||
shortcut: [{
|
||||
key: 'z',
|
||||
code: 'KeyY',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -381,7 +477,7 @@ var ExtensionConf = {
|
||||
playerUi: {
|
||||
show: false,
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Zoom out',
|
||||
label: 'Unzoom',
|
||||
cmd: [{
|
||||
@ -393,6 +489,7 @@ var ExtensionConf = {
|
||||
show: false,
|
||||
shortcut: [{
|
||||
key: 'u',
|
||||
code: 'KeyU',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
@ -405,32 +502,20 @@ var ExtensionConf = {
|
||||
playerUi: {
|
||||
show: false
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Toggle panning mode',
|
||||
label: 'Toggle pan',
|
||||
cmd: [{
|
||||
action: 'toggle-pan',
|
||||
arg: 'toggle'
|
||||
}],
|
||||
scopes: {
|
||||
page: {
|
||||
show: true,
|
||||
shortcut: [{
|
||||
key: 'p',
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
altKey: false,
|
||||
shiftKey: false,
|
||||
onKeyUp: true,
|
||||
onKeyDown: false,
|
||||
}]
|
||||
}
|
||||
},
|
||||
playerUi: {
|
||||
show: true,
|
||||
path: 'zoom'
|
||||
},
|
||||
scopes: {
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Hold to pan',
|
||||
cmd: [{
|
||||
action: 'pan',
|
||||
@ -479,7 +564,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'stretch'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Set stretch to "basic"',
|
||||
label: 'Basic stretch',
|
||||
cmd: [{
|
||||
@ -504,7 +589,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'stretch'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Set stretch to "hybrid"',
|
||||
label: 'Hybrid stretch',
|
||||
cmd: [{
|
||||
@ -529,7 +614,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'stretch'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Stretch only to hide thin borders',
|
||||
label: 'Thin borders only',
|
||||
cmd: [{
|
||||
@ -554,7 +639,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'stretch'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Set stretch to default value',
|
||||
label: 'Default',
|
||||
cmd: [{
|
||||
@ -592,7 +677,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'align'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Align video to center',
|
||||
label: 'Center',
|
||||
cmd: [{
|
||||
@ -614,7 +699,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'align'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Align video to the right',
|
||||
label: 'Right',
|
||||
cmd: [{
|
||||
@ -636,7 +721,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
path: 'align'
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Use default alignment',
|
||||
label: 'Default',
|
||||
cmd: [{
|
||||
@ -669,7 +754,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Enable extension on whitelisted sites only',
|
||||
label: 'On whitelist only',
|
||||
cmd: [{
|
||||
@ -682,7 +767,7 @@ var ExtensionConf = {
|
||||
show: true
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Extension mode: use default settings',
|
||||
label: 'Default',
|
||||
cmd: [{
|
||||
@ -695,7 +780,7 @@ var ExtensionConf = {
|
||||
show: true
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Disable extension',
|
||||
label: 'Disable',
|
||||
cmd: [{
|
||||
@ -711,7 +796,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Enable automatic aspect ratio detection',
|
||||
label: 'Enable',
|
||||
cmd: [{
|
||||
@ -727,7 +812,7 @@ var ExtensionConf = {
|
||||
show: true
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Enable automatic aspect ratio detection on whitelisted sites only',
|
||||
label: 'On whitelist only',
|
||||
cmd: [{
|
||||
@ -740,7 +825,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Use default settings for automatic aspect ratio detection',
|
||||
label: 'Default',
|
||||
cmd: [{
|
||||
@ -753,7 +838,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Disable automatic aspect ratio detection',
|
||||
label: 'Disable',
|
||||
cmd: [{
|
||||
@ -792,7 +877,7 @@ var ExtensionConf = {
|
||||
show: true,
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Enable keyboard shortcuts on whitelisted sites only',
|
||||
label: 'On whitelist only',
|
||||
cmd: [{
|
||||
@ -804,7 +889,7 @@ var ExtensionConf = {
|
||||
show: true
|
||||
},
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Keyboard shortcuts mode: use default settings',
|
||||
label: 'Default',
|
||||
cmd: [{
|
||||
@ -816,7 +901,7 @@ var ExtensionConf = {
|
||||
show: true
|
||||
}
|
||||
}
|
||||
},{
|
||||
}, {
|
||||
name: 'Disable keyboard shortcuts',
|
||||
label: 'Disable',
|
||||
cmd: [{
|
||||
|
@ -133,28 +133,31 @@ class ActionHandler {
|
||||
// don't do shit on invalid value of state
|
||||
}
|
||||
|
||||
preventAction() {
|
||||
preventAction(event) {
|
||||
var activeElement = document.activeElement;
|
||||
|
||||
if(this.logger.canLog('keyboard')) {
|
||||
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" +
|
||||
"is full screen? (yes->allow):", PlayerData.isFullScreen(),
|
||||
"\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 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),
|
||||
"\nwill the action be prevented? (yes -> prevent)", this.preventAction(),
|
||||
"\nwill the action be prevented? (yes -> prevent)", preventAction,
|
||||
"\n-----------------{ extra debug info }-------------------",
|
||||
"\ntag name? (lowercase):", activeElement.tagName, activeElement.tagName.toLocaleLowerCase(),
|
||||
"\nrole:", activeElement.getAttribute('role'),
|
||||
"\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
|
||||
@ -175,25 +178,49 @@ class ActionHandler {
|
||||
if (activeElement.getAttribute("role") === "textbox") {
|
||||
return true;
|
||||
}
|
||||
if (event.target.isContentEditable) {
|
||||
return true;
|
||||
}
|
||||
if (activeElement.getAttribute("type") === "text") {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
isActionMatch(shortcut, event) {
|
||||
isLatin(key) {
|
||||
return 'abcdefghijklmnopqrstuvwxyz,.-+1234567890'.indexOf(key.toLocaleLowerCase()) !== -1;
|
||||
}
|
||||
|
||||
isActionMatchStandard(shortcut, event) {
|
||||
return shortcut.key === event.key &&
|
||||
shortcut.ctrlKey === event.ctrlKey &&
|
||||
shortcut.metaKey === event.metaKey &&
|
||||
shortcut.altKey === event.altKey &&
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
|
||||
for (var cmd of action.cmd) {
|
||||
@ -228,10 +255,17 @@ class ActionHandler {
|
||||
this.settings.active.sites[site].arStatus = 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.pageInfo.setArPersistence(cmd.arg);
|
||||
this.settings.saveWithoutReload();
|
||||
}
|
||||
|
||||
if (cmd.action !== 'set-ar-persistence') {
|
||||
this.settings.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// če smo našli dejanje za to tipko, potem ne preiskujemo naprej
|
||||
// if we found an action for this key, we stop searching for a match
|
||||
@ -244,7 +278,7 @@ class ActionHandler {
|
||||
handleKeyup(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.");
|
||||
return;
|
||||
}
|
||||
@ -255,7 +289,7 @@ class ActionHandler {
|
||||
handleKeydown(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.");
|
||||
return;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import ObjectCopy from '../lib/ObjectCopy';
|
||||
import Stretch from '../../common/enums/stretch.enum';
|
||||
import VideoAlignment from '../../common/enums/video-alignment.enum';
|
||||
import ExtensionConfPatch from '../conf/ExtConfPatches';
|
||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
||||
|
||||
|
||||
|
||||
@ -26,43 +27,30 @@ class Settings {
|
||||
|
||||
const ths = this;
|
||||
|
||||
if(currentBrowser.firefox) {
|
||||
browser.storage.onChanged.addListener( (changes, area) => {
|
||||
if (currentBrowser.firefox) {
|
||||
browser.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
|
||||
} else if (currentBrowser.chrome) {
|
||||
chrome.storage.onChanged.addListener((changes, area) => {this.storageChangeListener(changes, area)});
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
const parsedSettings = JSON.parse(changes.uwSettings.newValue);
|
||||
if(changes['uwSettings'] && changes['uwSettings'].newValue) {
|
||||
ths.setActive(JSON.parse(changes.uwSettings.newValue));
|
||||
this.setActive(parsedSettings);
|
||||
}
|
||||
|
||||
if(this.updateCallback) {
|
||||
if(!parsedSettings.preventReload && this.updateCallback) {
|
||||
try {
|
||||
updateCallback(ths);
|
||||
} catch (e) {
|
||||
this.logger.log('error', 'settings', "[Settings] CALLING UPDATE CALLBACK FAILED.")
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (currentBrowser.chrome) {
|
||||
chrome.storage.onChanged.addListener( (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.")
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getExtensionVersion() {
|
||||
@ -156,8 +144,23 @@ class Settings {
|
||||
// apply all remaining patches
|
||||
this.logger.log('info', 'settings', `[Settings::applySettingsPatches] There are ${patches.length - index} settings patches to apply`);
|
||||
while (index < patches.length) {
|
||||
const updateFn = patches[index].updateFn;
|
||||
delete patches[index].forVersion;
|
||||
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++;
|
||||
}
|
||||
}
|
||||
@ -171,11 +174,12 @@ class Settings {
|
||||
// | needed. In this case, we assume we're on the current 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,
|
||||
"\nlast saved with:", settings.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");
|
||||
@ -185,7 +189,6 @@ class Settings {
|
||||
// this.set(this.active);
|
||||
// return this.active;
|
||||
// }
|
||||
}
|
||||
|
||||
// if there's no settings saved, return default settings.
|
||||
if(! settings || (Object.keys(settings).length === 0 && settings.constructor === Object)) {
|
||||
@ -279,8 +282,10 @@ class Settings {
|
||||
}
|
||||
}
|
||||
|
||||
async set(extensionConf) {
|
||||
async set(extensionConf, options) {
|
||||
if (!options || !options.forcePreserveVersion) {
|
||||
extensionConf.version = this.version;
|
||||
}
|
||||
|
||||
this.logger.log('info', 'settings', "[Settings::set] setting new settings:", extensionConf)
|
||||
|
||||
@ -299,11 +304,17 @@ class Settings {
|
||||
this.active[prop] = value;
|
||||
}
|
||||
|
||||
async save() {
|
||||
async save(options) {
|
||||
if (Debug.debug && Debug.storage) {
|
||||
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);
|
||||
}
|
||||
|
||||
@ -522,6 +533,15 @@ class Settings {
|
||||
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) {
|
||||
if (site && this.active.sites[site] && this.active.sites[site].videoAlignment !== VideoAlignment.Default) {
|
||||
return this.active.sites[site].videoAlignment;
|
||||
|
@ -492,6 +492,13 @@ class ArDetector {
|
||||
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(){
|
||||
if(! this.video){
|
||||
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.clearImageData(imageData);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -598,6 +606,7 @@ class ArDetector {
|
||||
// if both succeed, then aspect ratio hasn't changed.
|
||||
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.clearImageData(imageData);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -617,6 +626,7 @@ class ArDetector {
|
||||
this.guardLine.reset();
|
||||
this.noLetterboxCanvasReset = true;
|
||||
|
||||
this.clearImageData(imageData);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -643,6 +653,7 @@ class ArDetector {
|
||||
|
||||
triggerTimeout = this.getTimeout(baseTimeout, startTime);
|
||||
this.scheduleFrameCheck(triggerTimeout);
|
||||
this.clearImageData(imageData);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -664,6 +675,8 @@ class ArDetector {
|
||||
// rob ni bil zaznan, zato ne naredimo ničesar.
|
||||
// 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.clearImageData(imageData);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -696,14 +709,16 @@ class ArDetector {
|
||||
} catch (e) {
|
||||
// edges weren't gucci, so we'll just reset
|
||||
// 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) {
|
||||
// guardline wasn't gucci either, but we'll just ignore
|
||||
// that and reset the aspect ratio anyway
|
||||
}
|
||||
this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
|
||||
// WE DO NOT RESET ASPECT RATIO HERE IN CASE OF PROBLEMS, CAUSES UNWARRANTED RESETS:
|
||||
// (eg. here: https://www.youtube.com/watch?v=nw5Z93Yt-UQ&t=410)
|
||||
//
|
||||
// this.conf.resizer.setAr({type: AspectRatio.Automatic, ratio: this.getDefaultAr()});
|
||||
}
|
||||
|
||||
this.clearImageData(imageData);
|
||||
}
|
||||
|
||||
resetBlackLevel(){
|
||||
|
@ -34,7 +34,7 @@ class GuardLine {
|
||||
// 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
|
||||
if (bbTop < 0 || bbBottom >= this.conf.canvas.height ){
|
||||
throw "INVALID_SETTINGS_IN_GUARDLINE"
|
||||
throw {error: "INVALID_SETTINGS_IN_GUARDLINE", bbTop, bbBottom}
|
||||
}
|
||||
|
||||
this.blackbar = {
|
||||
|
@ -92,6 +92,8 @@ class CommsClient {
|
||||
this.pageInfo.setManualTick(message.arg);
|
||||
} else if (message.cmd === 'autoar-tick') {
|
||||
this.pageInfo.tick();
|
||||
} else if (message.cmd === 'set-ar-persistence') {
|
||||
this.pageInfo.setArPersistence(message.arg);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ import Debug from '../../conf/Debug';
|
||||
import VideoData from './VideoData';
|
||||
import RescanReason from './enums/RescanReason';
|
||||
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
||||
import CropModePersistence from '../../../common/enums/crop-mode-persistence.enum';
|
||||
|
||||
if(Debug.debug)
|
||||
console.log("Loading: PageInfo.js");
|
||||
@ -20,23 +21,38 @@ class PageInfo {
|
||||
this.lastUrl = window.location.href;
|
||||
this.extensionMode = extensionMode;
|
||||
this.readOnly = readOnly;
|
||||
|
||||
this.defaultCrop = undefined;
|
||||
this.currentCrop = undefined;
|
||||
|
||||
if (comms){
|
||||
this.comms = comms;
|
||||
}
|
||||
|
||||
// request inject css immediately
|
||||
try {
|
||||
// request inject css immediately
|
||||
const playerStyleString = this.settings.active.sites[window.location.host].css.replace('\\n', '');
|
||||
this.comms.sendMessage({
|
||||
cmd: 'inject-css',
|
||||
cssString: playerStyleString
|
||||
});
|
||||
} 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.scheduleUrlCheck();
|
||||
|
||||
@ -197,11 +213,17 @@ class PageInfo {
|
||||
|
||||
try {
|
||||
v = new VideoData(video, this.settings, this);
|
||||
|
||||
if (!this.defaultCrop) {
|
||||
if (!v.invalid) {
|
||||
v.initArDetection();
|
||||
} else {
|
||||
this.logger.log('error', 'debug', 'Video is invalid. Aard not started.', video);
|
||||
}
|
||||
} else {
|
||||
this.logger.log('info', 'debug', 'Default crop is specified for this site. Not starting aard.');
|
||||
}
|
||||
|
||||
this.videos.push(v);
|
||||
} catch (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 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 (this.comms) {
|
||||
if (this.videos.length != oldVideoCount) { // only if number of videos changed, tho
|
||||
@ -551,6 +573,53 @@ class PageInfo {
|
||||
setKeyboardShortcutsEnabled(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;
|
||||
|
@ -44,6 +44,7 @@ class VideoData {
|
||||
};
|
||||
|
||||
this.resizer = new Resizer(this);
|
||||
|
||||
this.arDetector = new ArDetector(this); // this starts Ar detection. needs optional parameter that prevets ardetdctor from starting
|
||||
// player dimensions need to be in:
|
||||
// this.player.dimensions
|
||||
@ -59,6 +60,11 @@ class VideoData {
|
||||
|
||||
// start fallback video/player size detection
|
||||
this.fallbackChangeDetection();
|
||||
|
||||
// force reload last aspect ratio (if default crop ratio exists)
|
||||
if (this.pageInfo.defaultCrop) {
|
||||
this.resizer.setAr(this.pageInfo.defaultCrop);
|
||||
}
|
||||
}
|
||||
|
||||
async fallbackChangeDetection() {
|
||||
@ -80,19 +86,31 @@ class VideoData {
|
||||
}
|
||||
return;
|
||||
}
|
||||
let confirmAspectRatioRestore = false;
|
||||
|
||||
for (let mutation of mutationList) {
|
||||
if (mutation.type === 'attributes'
|
||||
&& mutation.attributeName === 'class'
|
||||
&& !context.video.classList.contains(this.userCssClassName) ) {
|
||||
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);
|
||||
break;
|
||||
}
|
||||
// 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
|
||||
@ -131,8 +149,9 @@ class VideoData {
|
||||
const pw = +(pcs.width.split('px')[0]);
|
||||
|
||||
// TODO: check & account for panning and alignment
|
||||
if (this.isWithin(vh, (ph - (translateY / 2)), 2)
|
||||
&& this.isWithin(vw, (pw - (translateX / 2)), 2)) {
|
||||
if (transformMatrix[0] !== 'none'
|
||||
&& this.isWithin(vh, (ph - (translateY * 2)), 2)
|
||||
&& this.isWithin(vw, (pw - (translateX * 2)), 2)) {
|
||||
} else {
|
||||
this.player.forceRefreshPlayerElement();
|
||||
this.restoreAr();
|
||||
@ -162,7 +181,7 @@ class VideoData {
|
||||
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
|
||||
return;
|
||||
}
|
||||
if(this.arDetector){
|
||||
if (this.arDetector){
|
||||
this.arDetector.init();
|
||||
}
|
||||
else{
|
||||
@ -177,8 +196,8 @@ class VideoData {
|
||||
// throw {error: 'VIDEO_DATA_DESTROYED', data: {videoData: this}};
|
||||
return;
|
||||
}
|
||||
if(!this.arDetector) {
|
||||
this.arDetector.init();
|
||||
if (!this.arDetector) {
|
||||
this.initArDetection();
|
||||
}
|
||||
this.arDetector.start();
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import ExtensionMode from '../../../common/enums/extension-mode.enum';
|
||||
import Stretch from '../../../common/enums/stretch.enum';
|
||||
import VideoAlignment from '../../../common/enums/video-alignment.enum';
|
||||
import AspectRatio from '../../../common/enums/aspect-ratio.enum';
|
||||
import CropModePersistance from '../../../common/enums/crop-mode-persistence.enum';
|
||||
|
||||
if(Debug.debug) {
|
||||
console.log("Loading: Resizer.js");
|
||||
@ -119,7 +120,7 @@ class Resizer {
|
||||
}
|
||||
|
||||
|
||||
setAr(ar, lastAr){
|
||||
setAr(ar, lastAr) {
|
||||
if (this.destroyed) {
|
||||
return;
|
||||
}
|
||||
@ -130,12 +131,36 @@ class Resizer {
|
||||
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 ||
|
||||
ar.type === AspectRatio.Reset && this.lastAr.type === AspectRatio.Initial) {
|
||||
// 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
|
||||
|
||||
const siteSettings = this.settings.active.sites[window.location.host];
|
||||
if (siteSettings && siteSettings.autoarPreventConditions) {
|
||||
if (siteSettings.autoarPreventConditions.videoStyleString) {
|
||||
const styleString = (this.video.getAttribute('style') || '').split(';');
|
||||
@ -170,6 +195,8 @@ class Resizer {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (lastAr) {
|
||||
this.lastAr = this.calculateRatioForLegacyOptions(lastAr);
|
||||
ar = this.calculateRatioForLegacyOptions(ar);
|
||||
@ -247,6 +274,10 @@ class Resizer {
|
||||
|
||||
}
|
||||
|
||||
toFixedAr() {
|
||||
this.lastAr.type = AspectRatio.Fixed;
|
||||
}
|
||||
|
||||
resetLastAr() {
|
||||
this.lastAr = {type: AspectRatio.Initial};
|
||||
}
|
||||
@ -272,6 +303,9 @@ class Resizer {
|
||||
// dont allow weird floats
|
||||
this.videoAlignment = VideoAlignment.Center;
|
||||
|
||||
// because non-fixed aspect ratios reset panning:
|
||||
this.toFixedAr();
|
||||
|
||||
const player = this.conf.player.element;
|
||||
|
||||
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){
|
||||
// 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
|
||||
|
@ -120,14 +120,31 @@ class Scaler {
|
||||
actualHeight: 0, // height of the video (excluding letterbox) when <video> tag height is equal to height
|
||||
}
|
||||
|
||||
if (fileAr < playerAr) {
|
||||
if (fileAr < ar.ratio){
|
||||
// imamo letterbox zgoraj in spodaj -> spremenimo velikost videa (a nikoli širše od ekrana)
|
||||
// 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.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 {
|
||||
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.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);
|
||||
|
@ -108,7 +108,7 @@ class Stretcher {
|
||||
yFactor: 1
|
||||
};
|
||||
|
||||
if(playerAr >= videoAr){
|
||||
if (playerAr >= videoAr){
|
||||
// player adds PILLARBOX
|
||||
|
||||
if(actualAr >= playerAr){
|
||||
|
@ -18,6 +18,7 @@ class Zoom {
|
||||
|
||||
reset(){
|
||||
this.scale = 1;
|
||||
this.logScale = 0;
|
||||
}
|
||||
|
||||
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.conf.resizer.toFixedAr();
|
||||
|
||||
this.conf.restoreAr();
|
||||
this.conf.announceZoom(this.scale);
|
||||
}
|
||||
|
@ -44,26 +44,26 @@ class UW {
|
||||
if (!this.logger) {
|
||||
const loggingOptions = {
|
||||
logToFile: false,
|
||||
logToConsole: false,
|
||||
logToConsole: true,
|
||||
fileOptions: {
|
||||
// really the same stuff as consoleOptions
|
||||
},
|
||||
consoleOptions: {
|
||||
enabled: true, // if logging is enabled at all
|
||||
'debug': true,
|
||||
'init': true,
|
||||
'settings': true,
|
||||
'keyboard': false,
|
||||
'mousemove': false,
|
||||
'actionHandler': false,
|
||||
'comms': false,
|
||||
'playerDetect': false,
|
||||
// 'debug': true,
|
||||
// 'init': true,
|
||||
// 'settings': true,
|
||||
// 'keyboard': true,
|
||||
// 'mousemove': false,
|
||||
// 'actionHandler': false,
|
||||
// 'comms': false,
|
||||
// 'playerDetect': false,
|
||||
// 'resizer': true,
|
||||
// 'scaler': true,
|
||||
// 'stretcher': true,
|
||||
'videoRescan': false,
|
||||
'arDetect': false,
|
||||
'arDetect_verbose': false,
|
||||
// 'videoRescan': false,
|
||||
// 'arDetect': true,
|
||||
// 'arDetect_verbose': true,
|
||||
}
|
||||
};
|
||||
this.logger = new Logger(loggingOptions);
|
||||
|
@ -2,7 +2,7 @@
|
||||
"manifest_version": 2,
|
||||
"name": "Ultrawidify",
|
||||
"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": {
|
||||
"gecko": {
|
||||
"id": "{cf02b1a7-a01a-4e37-a609-516a283f1ed3}"
|
||||
|
@ -70,7 +70,7 @@ export default {
|
||||
if (this.hasError) {
|
||||
return;
|
||||
}
|
||||
this.settings.save();
|
||||
this.settings.save({forcePreserveVersion: true});
|
||||
// this.parsedSettings = JSON.stringify(this.settings.active, null, 2);
|
||||
// this.lastSettings = JSON.parse(JSON.stringify(this.settings.active));
|
||||
const ths = this;
|
||||
|
@ -39,12 +39,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SetShortcutButton from './SetShortcutButton.vue';
|
||||
|
||||
import SetShortcutButton from './SetShortcutButton';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
SetShortcutButton
|
||||
SetShortcutButton,
|
||||
},
|
||||
props: {
|
||||
scopeOptions: Object,
|
||||
|
@ -7,7 +7,7 @@
|
||||
@focus="initiateKeypress"
|
||||
@keyup="processKeyup"
|
||||
/>
|
||||
<span v-if="shortcut" @click="$emit('set-shortcut')">(Clear shortcut)</span>
|
||||
<span v-if="shortcut" @click="clearShortcut()">(Clear shortcut)</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -38,6 +38,7 @@ export default {
|
||||
if (this.waitingForPress) {
|
||||
const shortcut = {
|
||||
key: event.key,
|
||||
code: event.code,
|
||||
ctrlKey: event.ctrlKey,
|
||||
metaKey: event.metaKey,
|
||||
altKey: event.altKey,
|
||||
@ -50,6 +51,11 @@ export default {
|
||||
this.shortcutText = KeyboardShortcutParser.parseShortcut(shortcut);
|
||||
}
|
||||
this.waitingForPress = false;
|
||||
},
|
||||
clearShortcut() {
|
||||
this.shortcutText = '[click to add shortcut]';
|
||||
this.shortcut = undefined;
|
||||
this.$emit('set-shortcut');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ class ExecAction {
|
||||
this.site = site;
|
||||
}
|
||||
|
||||
exec(action, scope, frame) {
|
||||
async exec(action, scope, frame) {
|
||||
for (var cmd of action.cmd) {
|
||||
if (scope === 'page') {
|
||||
const message = {
|
||||
@ -27,6 +27,23 @@ class ExecAction {
|
||||
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
|
||||
await Comms.sendMessage(message);
|
||||
}
|
||||
|
||||
let site = this.site;
|
||||
if (scope === 'global') {
|
||||
site = '@global';
|
||||
@ -48,11 +65,17 @@ class ExecAction {
|
||||
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;
|
||||
|
@ -42,9 +42,27 @@
|
||||
</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 -->
|
||||
<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">
|
||||
<ShortcutButton v-for="(action, index) of stretchActions"
|
||||
class="flex b3 flex-grow button"
|
||||
|
@ -5,6 +5,13 @@
|
||||
</div>
|
||||
<div v-if="aspectRatioActions.length">
|
||||
<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">
|
||||
<ShortcutButton v-for="(action, index) of aspectRatioActions"
|
||||
class="flex b3 flex-grow button"
|
||||
@ -117,11 +124,13 @@ import ExecAction from '../js/ExecAction';
|
||||
import KeyboardShortcutParser from '../../common/js/KeyboardShortcutParser';
|
||||
import ShortcutButton from '../../common/components/ShortcutButton';
|
||||
import ComputeActionsMixin from '../../common/mixins/ComputeActionsMixin';
|
||||
import CropModePersistence from '../../common/enums/crop-mode-persistence.enum';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
scope: 'page',
|
||||
CropModePersistence: CropModePersistence,
|
||||
}
|
||||
},
|
||||
mixins: [
|
||||
@ -131,7 +140,8 @@ export default {
|
||||
'settings',
|
||||
'frame',
|
||||
'zoom',
|
||||
'someSitesDisabledWarning'
|
||||
'someSitesDisabledWarning',
|
||||
'cropModePersistence',
|
||||
],
|
||||
created() {
|
||||
this.exec = new ExecAction(this.settings);
|
||||
|
@ -2,11 +2,17 @@
|
||||
<div>
|
||||
<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 class="label">4.3.1.1</p>
|
||||
<p class="label">4.4.0</p>
|
||||
<ul>
|
||||
<li><b>[4.3.3.1]</b> Patched twitch.tv</li>
|
||||
<li>Minor rework of settings page (actions & shortcuts section)</li>
|
||||
<li>Fixed bug that prevented settings page from opening</li>
|
||||
<li>Russian users (and users of other non-latin keyboard layouts) can now use keyboard shortcuts by default,
|
||||
without having to rebind them manually. <b>NOTE: this change will only be applied to users who have <i>NOT</i>
|
||||
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>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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:
|
||||
|
||||
@ -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=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
|
||||
|
||||
https://www.youtube.com/watch?v=tXTFdDrd7pA
|
||||
|
||||
|
||||
### HARD MODE
|
||||
|
||||
For situations that would be _really_ hard to fix (if fix is even possible)
|
||||
|
Loading…
Reference in New Issue
Block a user